Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ docker-entrypoint.sh text eol=lf
Dockerfile text eol=lf
*.bash text eol=lf
Makefile text eol=lf

# Keep committed snapshots on LF
*.snap text eol=lf
2 changes: 1 addition & 1 deletion middleware/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"eval:adversarial": "node --import tsx test/adversarial/adversarialSuite.eval.ts",
"setup:tigris-lifecycle": "tsx scripts/setup-tigris-lifecycle.ts",
"pretest": "node scripts/check-node-version.mjs",
"test": "node --import tsx --test --test-timeout=120000 --test-concurrency=4 --test-reporter=spec --test-reporter-destination=stdout --test-reporter=./scripts/testFileDurations.reporter.mjs --test-reporter-destination=test-file-durations.json 'test/**/*.test.ts'",
"test": "node --import tsx --test --test-timeout=120000 --test-concurrency=4 --test-reporter=spec --test-reporter-destination=stdout --test-reporter=./scripts/testFileDurations.reporter.mjs --test-reporter-destination=test-file-durations.json 'test/**/*.test.ts' 'packages/plugin-api/test/**/*.test.ts'",
"test:filetimes": "node scripts/check-test-file-durations.mjs",
"test:pg": "node --import tsx --test --test-timeout=120000 --test-concurrency=1 --test-reporter=spec 'test/**/*.pg.test.ts'",
"test:updater": "node --test 'sidecars/updater/test/*.test.mjs'"
Expand Down
56 changes: 56 additions & 0 deletions middleware/packages/plugin-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# `@omadia/plugin-api`

The shared type contract between the kernel and every plugin. The kernel imports it to build
`PluginContext`; each plugin imports it to describe what it needs. Nothing in here has a runtime
of its own beyond a handful of pure helpers and fixtures.

The package is `private: true` and is not published. Consumers inside this repo resolve it
through the npm workspace; out-of-repo plugins consume it by `file:` link, a vendored `.d.ts`, or
a git tag.

## The API surface is machine-checked

`api-snapshot/plugin-api.d.ts.snap` is a golden snapshot of every declaration this package emits:
comments stripped, blank lines dropped, whitespace collapsed, files concatenated in sorted path
order. `test/apiSnapshot.test.ts` regenerates it from the current `src/` and fails on any
difference.

It exists because a breaking change here is invisible at the moment it is made. Every consumer
lives in this repo today and gets recompiled in the same commit, so `tsc` stays green while a
renamed method or a narrowed parameter quietly changes what a plugin must compile against. Once
plugins ship from their own repositories that silence becomes someone else's install-time
incident. The snapshot turns it into a diff in the PR that causes it.

```bash
npm run api:check -w packages/plugin-api # what CI runs
npm run api:update -w packages/plugin-api # accept the new surface
```

The declarations are compiled into a temporary directory, never into `dist/`, so the check always
measures the current source and never races the compiled output other suites import.

## What to do when the check fails

A red snapshot check is not a request to run `api:update` and move on. It is the one place the
change is visible, so read the diff first and decide what it means.

1. **Unintended?** Fix the source. That is the whole point of the gate.
2. **Intended?** Run `npm run api:update -w packages/plugin-api`, commit the regenerated snapshot
in the same commit as the source change, and bump `version` in `package.json`:

| Change in the diff | Bump |
| --- | --- |
| Symbol removed or renamed; parameter added; type narrowed; optional field made required | **MAJOR** — every consumer must be checked |
| Symbol added; required field made optional; type widened | **MINOR** — existing consumers keep compiling |
| Nothing (the diff is empty) | none |

SemVer is load-bearing here rather than decorative: after the split it is the only signal an
out-of-repo plugin gets about whether its pinned contract still holds.

## Layout

- `src/` — the contract. `index.ts` re-exports the modules that make up the public surface.
- `scripts/api-snapshot.mjs` — snapshot generator and checker.
- `api-snapshot/` — the committed golden snapshot. Generated; do not hand-edit.
- `test/` — the gate. Run standalone with `npm test -w packages/plugin-api`; CI runs it as part
of the middleware suite.
Loading
Loading