From 64211e9970b9cd7ca3dd5144f1407401964863ad Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 25 Apr 2026 11:11:12 +0000 Subject: [PATCH] Version Packages --- .changeset/atproto-record-shape.md | 28 ---- .changeset/backfill-all-rename.md | 31 ---- .changeset/contrail-dev-command.md | 19 --- .changeset/create-worker-lexicons-endpoint.md | 32 ----- .changeset/drop-queryable-autodetect.md | 11 -- .changeset/jiti-config-loader.md | 5 - .changeset/lexicons-publish-cmd.md | 13 -- .changeset/refresh-command.md | 25 ---- .changeset/spaces-owner-no-delete-bypass.md | 11 -- packages/contrail/CHANGELOG.md | 132 ++++++++++++++++++ packages/contrail/package.json | 2 +- packages/lexicons/CHANGELOG.md | 85 +++++++++++ packages/lexicons/package.json | 2 +- packages/sync/CHANGELOG.md | 28 ++++ packages/sync/package.json | 2 +- 15 files changed, 248 insertions(+), 178 deletions(-) delete mode 100644 .changeset/atproto-record-shape.md delete mode 100644 .changeset/backfill-all-rename.md delete mode 100644 .changeset/contrail-dev-command.md delete mode 100644 .changeset/create-worker-lexicons-endpoint.md delete mode 100644 .changeset/drop-queryable-autodetect.md delete mode 100644 .changeset/jiti-config-loader.md delete mode 100644 .changeset/lexicons-publish-cmd.md delete mode 100644 .changeset/refresh-command.md delete mode 100644 .changeset/spaces-owner-no-delete-bypass.md diff --git a/.changeset/atproto-record-shape.md b/.changeset/atproto-record-shape.md deleted file mode 100644 index e7495cc..0000000 --- a/.changeset/atproto-record-shape.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -"@atmo-dev/contrail": minor -"@atmo-dev/contrail-sync": minor -"@atmo-dev/contrail-lexicons": minor ---- - -align the `listRecords` / `getRecord` response envelope with atproto's `com.atproto.repo.*`. the field that carries the record value is now `value`, not `record`. - -**before** (contrail-specific): - -```jsonc -{ "records": [{ "uri", "did", "collection", "rkey", "cid", "record": {...}, "time_us" }] } -``` - -**after** (atproto-compatible plus extras): - -```jsonc -{ "records": [{ "uri", "cid", "value": {...}, "did", "collection", "rkey", "time_us" }] } -``` - -changes: -- `#record` def now requires `["uri", "cid", "value"]` (matches atproto's standard `com.atproto.repo.listRecords#record`). `did`/`collection`/`rkey`/`time_us` remain in the response but are optional. -- `getRecord` top-level output requires `["uri", "value"]` (matches atproto's `com.atproto.repo.getRecord`). -- profile entries in `?profiles=true` responses use `value` instead of `record` for the profile record body. -- realtime watch events (`record.created`, `snapshot.record`, `hydration.added`) — the inner record payload's body field is now `value`. -- `@atmo-dev/contrail-sync`: `WatchRecord.value` (was `record`); `addOptimistic({ value })` (was `record`). - -**breaking.** anywhere you read `r.record` from a contrail response, rename to `r.value`. anywhere you call `addOptimistic({ record: ... })`, switch to `addOptimistic({ value: ... })`. regenerate committed `lexicons/generated/*` in each deployment — the new shape will be advertised on next `contrail-lex generate` run. diff --git a/.changeset/backfill-all-rename.md b/.changeset/backfill-all-rename.md deleted file mode 100644 index 9718563..0000000 --- a/.changeset/backfill-all-rename.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -"@atmo-dev/contrail": minor ---- - -rename `contrail.sync()` → `contrail.backfillAll()` and emit progress via `config.logger` by default. - -the method previously returned `{ discovered, backfilled }` but emitted no output, so callers had to wire up their own `onProgress`. it now logs discovery + throttled backfill progress + final summary through `config.logger` (defaults to `console`). supplying `onProgress` still takes over, and passing a no-op logger silences it. - -also renames the internal `backfillAll` function (in `src/core/backfill.ts`) to `backfillPending` to reduce confusion with the new public method. not publicly exported, so no user-facing impact. - -adds a `contrail` CLI bin with a `backfill` subcommand so workers deploys don't need a local script file at all: - -```json -"scripts": { - "backfill": "contrail backfill --config src/config.ts", - "backfill:remote": "contrail backfill --config src/config.ts --remote" -} -``` - -auto-detects `contrail.config.ts`, `app/config.ts`, or `src/lib/contrail/config.ts`; loads TS configs via `jiti` (no tsx hook required). flags: `--config`, `--remote`, `--binding `, `--concurrency `. - -the underlying helper is also exported at `@atmo-dev/contrail/workers` for embedded use: - -```ts -import { backfillAll } from "@atmo-dev/contrail/workers"; -await backfillAll({ config, remote: true }); -``` - -`wrangler` is an optional peer dep — only imported at runtime when the cli/helper is called. - -breaking: `contrail.sync()` is gone; rename callsites to `contrail.backfillAll()`. signature and return shape unchanged. diff --git a/.changeset/contrail-dev-command.md b/.changeset/contrail-dev-command.md deleted file mode 100644 index 5e5e715..0000000 --- a/.changeset/contrail-dev-command.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -"@atmo-dev/contrail": minor ---- - -add `contrail dev` — local dev wrapper for cloudflare workers deployments. - -replaces `wrangler dev --test-scheduled` + a separate cron-trigger script with one command. on start it: - -1. connects to your local D1 via wrangler's `getPlatformProxy`, inspects state -2. prompts to run `backfillAll` if no completed backfills exist yet -3. prompts to run `refresh` if the ingest cursor is older than 60 minutes (configurable with `--stale-after`) -4. spawns `wrangler dev --test-scheduled` -5. fires `GET /__scheduled?cron=...` every 60 seconds so the cron actually runs in local dev (wrangler's scheduler only works in deployed production) - -flags: `--cron ` (default `"*/1 * * * *"`), `--stale-after ` (default 60), `--yes` to auto-accept prompts, plus the standard `--config` / `--root` / `--binding`. - -prompts are skipped in non-TTY environments (default-declined). - -also adds `--yes` to the CLI-wide arg parser. diff --git a/.changeset/create-worker-lexicons-endpoint.md b/.changeset/create-worker-lexicons-endpoint.md deleted file mode 100644 index 7ae8ac3..0000000 --- a/.changeset/create-worker-lexicons-endpoint.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -"@atmo-dev/contrail": minor -"@atmo-dev/contrail-lexicons": minor ---- - -two new DX pieces: - -**`@atmo-dev/contrail/worker`** exports `createWorker(config, options?)` — a prebuilt Cloudflare Workers entry that collapses the ~12-line `{ fetch, scheduled }` boilerplate to one line: - -```ts -import { createWorker } from "@atmo-dev/contrail/worker"; -import { config } from "./contrail.config"; -import { lexicons } from "../lexicons/generated"; - -export default createWorker(config, { lexicons }); -``` - -options: `binding` (D1 binding name, default `"DB"`), `lexicons` (see below), `onInit` (one-shot app-specific setup). - -**`/xrpc/.lexicons` endpoint + `contrail-lex pull-service`** lets consumer apps typegen against a deployed contrail over HTTP, no PDS or DNS required: - -- `contrail-lex generate` now emits a barrel `lexicons/generated/index.ts` that imports every lexicon the deployment speaks: generated + pulled + custom. The pulled lexicons are needed so consumer typegen can resolve `$ref`s out of the generated schemas. -- Pass `{ lexicons }` to `createWorker` (or `createHandler(contrail, { lexicons })`) and the service exposes them at `GET /xrpc/.lexicons`. -- From a consumer app: - ```bash - contrail-lex pull-service https://my-contrail.dev/xrpc/com.example.lexicons - # or - contrail-lex pull-service https://my-contrail.dev --namespace com.example - ``` - Fetches the manifest, writes each lexicon under `lexicons/pulled/`. Then `npx lex-cli generate` emits TS types. - -Path 1 of 4 of a set of DX improvements — path 2 (consumer typegen) works end-to-end but assumes the operator has regenerated. Paths 3 (one-command deploy) and 4 (fully vendored worker) are deferred. diff --git a/.changeset/drop-queryable-autodetect.md b/.changeset/drop-queryable-autodetect.md deleted file mode 100644 index 8e665f2..0000000 --- a/.changeset/drop-queryable-autodetect.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@atmo-dev/contrail-lexicons": patch ---- - -the lexicon generator previously auto-detected queryable fields by walking the pulled record schema (string → equality, datetime → range, etc.) and merged them with the user's explicit `queryable` before emitting `listRecords.json`. - -problem: the runtime does **not** auto-detect — it only honors fields explicitly declared in `colConfig.queryable`. So the generated lexicon advertised filter params (e.g. `?mode=online`, `?status=going`) that the server silently ignored. Clients would pass them and get unfiltered results back. - -fix: the generator now only emits what the user declared. the lexicon matches the runtime. one source of truth. - -if you were relying on the phantom params, add the fields explicitly to your config's `queryable` map. if you weren't, nothing changes except smaller, more honest `listRecords.json` files on the next `contrail-lex generate` run. diff --git a/.changeset/jiti-config-loader.md b/.changeset/jiti-config-loader.md deleted file mode 100644 index 494bd26..0000000 --- a/.changeset/jiti-config-loader.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@atmo-dev/contrail-lexicons": patch ---- - -fix `contrail-lex --config ` with plain TS files. previously broke with `ERR_UNKNOWN_FILE_EXTENSION` under plain node — bare `contrail-lex` invocation couldn't load TS configs. now uses `jiti` to handle TS + ESM + CJS transparently, so invocations like `contrail-lex all --config src/config.ts` work without needing tsx/ts-node preregistered. diff --git a/.changeset/lexicons-publish-cmd.md b/.changeset/lexicons-publish-cmd.md deleted file mode 100644 index 5efbcbf..0000000 --- a/.changeset/lexicons-publish-cmd.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -"@atmo-dev/contrail-lexicons": minor ---- - -add `contrail-lex publish` subcommand. wraps `publishLexicons` so you can push lexicon JSON to a PDS without writing a script: - -```bash -contrail-lex publish -# or via env: -LEXICON_ACCOUNT_IDENTIFIER=you.bsky.social LEXICON_ACCOUNT_PASSWORD=xxxx contrail-lex publish -``` - -supports `--generated-dir` (default `lexicons-generated`), `--skip-confirm` (for CI), and `--dry-run` (print what would be published + the DNS records needed, no writes, credentials not required). diff --git a/.changeset/refresh-command.md b/.changeset/refresh-command.md deleted file mode 100644 index a429382..0000000 --- a/.changeset/refresh-command.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -"@atmo-dev/contrail": minor ---- - -add `refresh` — a "catch-up" CLI + method that reconciles every known DID's PDS against the DB and reports what was missed. - -unlike `backfillAll`, it ignores the `backfills` state table and sweeps fresh. useful after jetstream outages or after leaving a dev deployment idle for days. - -each record in each configured collection is classified as: - -- **missing** — PDS has it, DB doesn't -- **stale update** — DB has it with a different CID, *and* the DB row was written before the ignore window (default 60s, configurable) -- **in sync** — same CID, or DB row is within the ignore window - -```bash -pnpm contrail refresh # totals -pnpm contrail refresh --by-collection # + per-nsid breakdown -pnpm contrail refresh --ignore-window 30 -``` - -programmatic: `contrail.refresh({ ignoreWindowMs, concurrency })` returns per-collection stats + totals. also exported from `@atmo-dev/contrail/workers` as `refresh()` for wrangler-backed deployments. - -safe to run repeatedly — each pass converges toward zero. not a replacement for `ingest` / `runPersistent` (walks every user's history, which is expensive); use for after-outage reconciliation or dev-idle catch-up. - -also extends `ExistingRecordInfo` with an `indexed_at: number | null` field so callers using `lookupExistingRecords` can inspect per-row freshness without a second query. diff --git a/.changeset/spaces-owner-no-delete-bypass.md b/.changeset/spaces-owner-no-delete-bypass.md deleted file mode 100644 index 32977fe..0000000 --- a/.changeset/spaces-owner-no-delete-bypass.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@atmo-dev/contrail": patch ---- - -tighten spaces ACL: owners no longer bypass the "own-record" rule on delete. everyone in the member list — owner included — can only delete records they authored. - -before: owner calling `space.deleteRecord` on someone else's record returned `200 { ok: true }` (ACL passed, but the adapter's SQL was already scoped to `did = caller`, so no rows were actually deleted — the response lied). - -after: that same call returns `403 { error: "Forbidden", reason: "not-own-record" }`. honest response; no behavior change at the storage layer. - -to wipe someone else's records in a space you own, delete the space itself. diff --git a/packages/contrail/CHANGELOG.md b/packages/contrail/CHANGELOG.md index 549a4ad..092800c 100644 --- a/packages/contrail/CHANGELOG.md +++ b/packages/contrail/CHANGELOG.md @@ -1,5 +1,137 @@ # @atmo-dev/contrail +## 0.3.0 + +### Minor Changes + +- f8fa672: align the `listRecords` / `getRecord` response envelope with atproto's `com.atproto.repo.*`. the field that carries the record value is now `value`, not `record`. + + **before** (contrail-specific): + + ```jsonc + { "records": [{ "uri", "did", "collection", "rkey", "cid", "record": {...}, "time_us" }] } + ``` + + **after** (atproto-compatible plus extras): + + ```jsonc + { "records": [{ "uri", "cid", "value": {...}, "did", "collection", "rkey", "time_us" }] } + ``` + + changes: + + - `#record` def now requires `["uri", "cid", "value"]` (matches atproto's standard `com.atproto.repo.listRecords#record`). `did`/`collection`/`rkey`/`time_us` remain in the response but are optional. + - `getRecord` top-level output requires `["uri", "value"]` (matches atproto's `com.atproto.repo.getRecord`). + - profile entries in `?profiles=true` responses use `value` instead of `record` for the profile record body. + - realtime watch events (`record.created`, `snapshot.record`, `hydration.added`) — the inner record payload's body field is now `value`. + - `@atmo-dev/contrail-sync`: `WatchRecord.value` (was `record`); `addOptimistic({ value })` (was `record`). + + **breaking.** anywhere you read `r.record` from a contrail response, rename to `r.value`. anywhere you call `addOptimistic({ record: ... })`, switch to `addOptimistic({ value: ... })`. regenerate committed `lexicons/generated/*` in each deployment — the new shape will be advertised on next `contrail-lex generate` run. + +- b81038c: rename `contrail.sync()` → `contrail.backfillAll()` and emit progress via `config.logger` by default. + + the method previously returned `{ discovered, backfilled }` but emitted no output, so callers had to wire up their own `onProgress`. it now logs discovery + throttled backfill progress + final summary through `config.logger` (defaults to `console`). supplying `onProgress` still takes over, and passing a no-op logger silences it. + + also renames the internal `backfillAll` function (in `src/core/backfill.ts`) to `backfillPending` to reduce confusion with the new public method. not publicly exported, so no user-facing impact. + + adds a `contrail` CLI bin with a `backfill` subcommand so workers deploys don't need a local script file at all: + + ```json + "scripts": { + "backfill": "contrail backfill --config src/config.ts", + "backfill:remote": "contrail backfill --config src/config.ts --remote" + } + ``` + + auto-detects `contrail.config.ts`, `app/config.ts`, or `src/lib/contrail/config.ts`; loads TS configs via `jiti` (no tsx hook required). flags: `--config`, `--remote`, `--binding `, `--concurrency `. + + the underlying helper is also exported at `@atmo-dev/contrail/workers` for embedded use: + + ```ts + import { backfillAll } from "@atmo-dev/contrail/workers"; + await backfillAll({ config, remote: true }); + ``` + + `wrangler` is an optional peer dep — only imported at runtime when the cli/helper is called. + + breaking: `contrail.sync()` is gone; rename callsites to `contrail.backfillAll()`. signature and return shape unchanged. + +- ad3a61d: add `contrail dev` — local dev wrapper for cloudflare workers deployments. + + replaces `wrangler dev --test-scheduled` + a separate cron-trigger script with one command. on start it: + + 1. connects to your local D1 via wrangler's `getPlatformProxy`, inspects state + 2. prompts to run `backfillAll` if no completed backfills exist yet + 3. prompts to run `refresh` if the ingest cursor is older than 60 minutes (configurable with `--stale-after`) + 4. spawns `wrangler dev --test-scheduled` + 5. fires `GET /__scheduled?cron=...` every 60 seconds so the cron actually runs in local dev (wrangler's scheduler only works in deployed production) + + flags: `--cron ` (default `"*/1 * * * *"`), `--stale-after ` (default 60), `--yes` to auto-accept prompts, plus the standard `--config` / `--root` / `--binding`. + + prompts are skipped in non-TTY environments (default-declined). + + also adds `--yes` to the CLI-wide arg parser. + +- ad3063a: two new DX pieces: + + **`@atmo-dev/contrail/worker`** exports `createWorker(config, options?)` — a prebuilt Cloudflare Workers entry that collapses the ~12-line `{ fetch, scheduled }` boilerplate to one line: + + ```ts + import { createWorker } from "@atmo-dev/contrail/worker"; + import { config } from "./contrail.config"; + import { lexicons } from "../lexicons/generated"; + + export default createWorker(config, { lexicons }); + ``` + + options: `binding` (D1 binding name, default `"DB"`), `lexicons` (see below), `onInit` (one-shot app-specific setup). + + **`/xrpc/.lexicons` endpoint + `contrail-lex pull-service`** lets consumer apps typegen against a deployed contrail over HTTP, no PDS or DNS required: + + - `contrail-lex generate` now emits a barrel `lexicons/generated/index.ts` that imports every lexicon the deployment speaks: generated + pulled + custom. The pulled lexicons are needed so consumer typegen can resolve `$ref`s out of the generated schemas. + - Pass `{ lexicons }` to `createWorker` (or `createHandler(contrail, { lexicons })`) and the service exposes them at `GET /xrpc/.lexicons`. + - From a consumer app: + ```bash + contrail-lex pull-service https://my-contrail.dev/xrpc/com.example.lexicons + # or + contrail-lex pull-service https://my-contrail.dev --namespace com.example + ``` + Fetches the manifest, writes each lexicon under `lexicons/pulled/`. Then `npx lex-cli generate` emits TS types. + + Path 1 of 4 of a set of DX improvements — path 2 (consumer typegen) works end-to-end but assumes the operator has regenerated. Paths 3 (one-command deploy) and 4 (fully vendored worker) are deferred. + +- b81038c: add `refresh` — a "catch-up" CLI + method that reconciles every known DID's PDS against the DB and reports what was missed. + + unlike `backfillAll`, it ignores the `backfills` state table and sweeps fresh. useful after jetstream outages or after leaving a dev deployment idle for days. + + each record in each configured collection is classified as: + + - **missing** — PDS has it, DB doesn't + - **stale update** — DB has it with a different CID, _and_ the DB row was written before the ignore window (default 60s, configurable) + - **in sync** — same CID, or DB row is within the ignore window + + ```bash + pnpm contrail refresh # totals + pnpm contrail refresh --by-collection # + per-nsid breakdown + pnpm contrail refresh --ignore-window 30 + ``` + + programmatic: `contrail.refresh({ ignoreWindowMs, concurrency })` returns per-collection stats + totals. also exported from `@atmo-dev/contrail/workers` as `refresh()` for wrangler-backed deployments. + + safe to run repeatedly — each pass converges toward zero. not a replacement for `ingest` / `runPersistent` (walks every user's history, which is expensive); use for after-outage reconciliation or dev-idle catch-up. + + also extends `ExistingRecordInfo` with an `indexed_at: number | null` field so callers using `lookupExistingRecords` can inspect per-row freshness without a second query. + +### Patch Changes + +- 3ee5ed4: tighten spaces ACL: owners no longer bypass the "own-record" rule on delete. everyone in the member list — owner included — can only delete records they authored. + + before: owner calling `space.deleteRecord` on someone else's record returned `200 { ok: true }` (ACL passed, but the adapter's SQL was already scoped to `did = caller`, so no rows were actually deleted — the response lied). + + after: that same call returns `403 { error: "Forbidden", reason: "not-own-record" }`. honest response; no behavior change at the storage layer. + + to wipe someone else's records in a space you own, delete the space itself. + ## 0.2.0 ### Minor Changes diff --git a/packages/contrail/package.json b/packages/contrail/package.json index 39493dd..b111360 100644 --- a/packages/contrail/package.json +++ b/packages/contrail/package.json @@ -1,6 +1,6 @@ { "name": "@atmo-dev/contrail", - "version": "0.2.0", + "version": "0.3.0", "description": "Index AT Protocol records with typed XRPC endpoints. Cloudflare Workers + D1, SvelteKit, Node.js.", "type": "module", "sideEffects": false, diff --git a/packages/lexicons/CHANGELOG.md b/packages/lexicons/CHANGELOG.md index 3682157..d0f0e74 100644 --- a/packages/lexicons/CHANGELOG.md +++ b/packages/lexicons/CHANGELOG.md @@ -1,5 +1,90 @@ # @atmo-dev/contrail-lexicons +## 0.3.0 + +### Minor Changes + +- f8fa672: align the `listRecords` / `getRecord` response envelope with atproto's `com.atproto.repo.*`. the field that carries the record value is now `value`, not `record`. + + **before** (contrail-specific): + + ```jsonc + { "records": [{ "uri", "did", "collection", "rkey", "cid", "record": {...}, "time_us" }] } + ``` + + **after** (atproto-compatible plus extras): + + ```jsonc + { "records": [{ "uri", "cid", "value": {...}, "did", "collection", "rkey", "time_us" }] } + ``` + + changes: + + - `#record` def now requires `["uri", "cid", "value"]` (matches atproto's standard `com.atproto.repo.listRecords#record`). `did`/`collection`/`rkey`/`time_us` remain in the response but are optional. + - `getRecord` top-level output requires `["uri", "value"]` (matches atproto's `com.atproto.repo.getRecord`). + - profile entries in `?profiles=true` responses use `value` instead of `record` for the profile record body. + - realtime watch events (`record.created`, `snapshot.record`, `hydration.added`) — the inner record payload's body field is now `value`. + - `@atmo-dev/contrail-sync`: `WatchRecord.value` (was `record`); `addOptimistic({ value })` (was `record`). + + **breaking.** anywhere you read `r.record` from a contrail response, rename to `r.value`. anywhere you call `addOptimistic({ record: ... })`, switch to `addOptimistic({ value: ... })`. regenerate committed `lexicons/generated/*` in each deployment — the new shape will be advertised on next `contrail-lex generate` run. + +- ad3063a: two new DX pieces: + + **`@atmo-dev/contrail/worker`** exports `createWorker(config, options?)` — a prebuilt Cloudflare Workers entry that collapses the ~12-line `{ fetch, scheduled }` boilerplate to one line: + + ```ts + import { createWorker } from "@atmo-dev/contrail/worker"; + import { config } from "./contrail.config"; + import { lexicons } from "../lexicons/generated"; + + export default createWorker(config, { lexicons }); + ``` + + options: `binding` (D1 binding name, default `"DB"`), `lexicons` (see below), `onInit` (one-shot app-specific setup). + + **`/xrpc/.lexicons` endpoint + `contrail-lex pull-service`** lets consumer apps typegen against a deployed contrail over HTTP, no PDS or DNS required: + + - `contrail-lex generate` now emits a barrel `lexicons/generated/index.ts` that imports every lexicon the deployment speaks: generated + pulled + custom. The pulled lexicons are needed so consumer typegen can resolve `$ref`s out of the generated schemas. + - Pass `{ lexicons }` to `createWorker` (or `createHandler(contrail, { lexicons })`) and the service exposes them at `GET /xrpc/.lexicons`. + - From a consumer app: + ```bash + contrail-lex pull-service https://my-contrail.dev/xrpc/com.example.lexicons + # or + contrail-lex pull-service https://my-contrail.dev --namespace com.example + ``` + Fetches the manifest, writes each lexicon under `lexicons/pulled/`. Then `npx lex-cli generate` emits TS types. + + Path 1 of 4 of a set of DX improvements — path 2 (consumer typegen) works end-to-end but assumes the operator has regenerated. Paths 3 (one-command deploy) and 4 (fully vendored worker) are deferred. + +- b81038c: add `contrail-lex publish` subcommand. wraps `publishLexicons` so you can push lexicon JSON to a PDS without writing a script: + + ```bash + contrail-lex publish + # or via env: + LEXICON_ACCOUNT_IDENTIFIER=you.bsky.social LEXICON_ACCOUNT_PASSWORD=xxxx contrail-lex publish + ``` + + supports `--generated-dir` (default `lexicons-generated`), `--skip-confirm` (for CI), and `--dry-run` (print what would be published + the DNS records needed, no writes, credentials not required). + +### Patch Changes + +- f8fa672: the lexicon generator previously auto-detected queryable fields by walking the pulled record schema (string → equality, datetime → range, etc.) and merged them with the user's explicit `queryable` before emitting `listRecords.json`. + + problem: the runtime does **not** auto-detect — it only honors fields explicitly declared in `colConfig.queryable`. So the generated lexicon advertised filter params (e.g. `?mode=online`, `?status=going`) that the server silently ignored. Clients would pass them and get unfiltered results back. + + fix: the generator now only emits what the user declared. the lexicon matches the runtime. one source of truth. + + if you were relying on the phantom params, add the fields explicitly to your config's `queryable` map. if you weren't, nothing changes except smaller, more honest `listRecords.json` files on the next `contrail-lex generate` run. + +- b81038c: fix `contrail-lex --config ` with plain TS files. previously broke with `ERR_UNKNOWN_FILE_EXTENSION` under plain node — bare `contrail-lex` invocation couldn't load TS configs. now uses `jiti` to handle TS + ESM + CJS transparently, so invocations like `contrail-lex all --config src/config.ts` work without needing tsx/ts-node preregistered. +- Updated dependencies [f8fa672] +- Updated dependencies [b81038c] +- Updated dependencies [ad3a61d] +- Updated dependencies [ad3063a] +- Updated dependencies [b81038c] +- Updated dependencies [3ee5ed4] + - @atmo-dev/contrail@0.3.0 + ## 0.2.0 ### Minor Changes diff --git a/packages/lexicons/package.json b/packages/lexicons/package.json index ab4f65f..6b5e03d 100644 --- a/packages/lexicons/package.json +++ b/packages/lexicons/package.json @@ -1,6 +1,6 @@ { "name": "@atmo-dev/contrail-lexicons", - "version": "0.2.0", + "version": "0.3.0", "description": "Generate atproto lexicon JSON (and optionally TypeScript types via @atcute/lex-cli) from a Contrail config.", "type": "module", "files": [ diff --git a/packages/sync/CHANGELOG.md b/packages/sync/CHANGELOG.md index c2100ca..cff3b46 100644 --- a/packages/sync/CHANGELOG.md +++ b/packages/sync/CHANGELOG.md @@ -1,5 +1,33 @@ # @atmo-dev/contrail-sync +## 0.3.0 + +### Minor Changes + +- f8fa672: align the `listRecords` / `getRecord` response envelope with atproto's `com.atproto.repo.*`. the field that carries the record value is now `value`, not `record`. + + **before** (contrail-specific): + + ```jsonc + { "records": [{ "uri", "did", "collection", "rkey", "cid", "record": {...}, "time_us" }] } + ``` + + **after** (atproto-compatible plus extras): + + ```jsonc + { "records": [{ "uri", "cid", "value": {...}, "did", "collection", "rkey", "time_us" }] } + ``` + + changes: + + - `#record` def now requires `["uri", "cid", "value"]` (matches atproto's standard `com.atproto.repo.listRecords#record`). `did`/`collection`/`rkey`/`time_us` remain in the response but are optional. + - `getRecord` top-level output requires `["uri", "value"]` (matches atproto's `com.atproto.repo.getRecord`). + - profile entries in `?profiles=true` responses use `value` instead of `record` for the profile record body. + - realtime watch events (`record.created`, `snapshot.record`, `hydration.added`) — the inner record payload's body field is now `value`. + - `@atmo-dev/contrail-sync`: `WatchRecord.value` (was `record`); `addOptimistic({ value })` (was `record`). + + **breaking.** anywhere you read `r.record` from a contrail response, rename to `r.value`. anywhere you call `addOptimistic({ record: ... })`, switch to `addOptimistic({ value: ... })`. regenerate committed `lexicons/generated/*` in each deployment — the new shape will be advertised on next `contrail-lex generate` run. + ## 0.2.0 ### Minor Changes diff --git a/packages/sync/package.json b/packages/sync/package.json index ec7f5a1..f23cde0 100644 --- a/packages/sync/package.json +++ b/packages/sync/package.json @@ -1,6 +1,6 @@ { "name": "@atmo-dev/contrail-sync", - "version": "0.2.0", + "version": "0.3.0", "description": "Client-side reactive watch-store over contrail's watchRecords endpoints. SSE + WebSocket transports, optimistic updates, optional IndexedDB cache.", "type": "module", "sideEffects": false, -- 2.51.2