@elytracms/studio 0.0.74 → 0.0.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/convex/cms.ts +67 -3
- package/convex/schema.ts +9 -0
- package/dist/components/app-views/default-modules.js +1 -1
- package/dist/components/app-views/default-modules.js.map +1 -1
- package/dist/components/cms/cms-store.d.ts +19 -2
- package/dist/components/cms/cms-store.js +43 -2
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/collection-table.js +33 -17
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/column-width.d.ts +8 -0
- package/dist/components/cms/column-width.js +8 -0
- package/dist/components/cms/column-width.js.map +1 -1
- package/dist/components/cms/doc-state.d.ts +46 -0
- package/dist/components/cms/doc-state.js +47 -1
- package/dist/components/cms/doc-state.js.map +1 -1
- package/dist/components/collection-view/cms-collection-view-source.js +5 -4
- package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
- package/dist/lib/cms-views/view-model.d.ts +29 -11
- package/dist/lib/cms-views/view-model.js +43 -16
- package/dist/lib/cms-views/view-model.js.map +1 -1
- package/dist/lib/collection-view/collection-view-source.d.ts +6 -5
- package/dist/lib/collection-view/collection-view-source.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +4 -0
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +2 -0
- package/dist/lib/i18n/messages.en.js +7 -0
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/operations/session-hydration.d.ts +11 -0
- package/dist/lib/operations/session-hydration.js +6 -0
- package/dist/lib/operations/session-hydration.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.js +9 -1
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/ui/status-dot.d.ts +17 -6
- package/dist/ui/status-dot.js +9 -7
- package/dist/ui/status-dot.js.map +1 -1
- package/package.json +2 -2
|
@@ -59,6 +59,17 @@ export interface HydratedCms {
|
|
|
59
59
|
* session-start stamp for the seeded documents); `loadHydratedCms` always sets it.
|
|
60
60
|
*/
|
|
61
61
|
timestamps?: Record<string, string>;
|
|
62
|
+
/**
|
|
63
|
+
* Per-document FIRST-publish instants (EC-447), keyed by `documentKey`
|
|
64
|
+
* (`collection:id`) → the persisted `cmsDocuments.publishedAt` ISO string. Present
|
|
65
|
+
* only for documents that carry one (currently published, with a recorded publish
|
|
66
|
+
* instant); the session seeds its `documentPublishedAt` map from this. Backs the
|
|
67
|
+
* `$publishedAt` list column. UNLIKE {@link timestamps} there is no session-start
|
|
68
|
+
* fallback — an absent entry is a real "no publish date" (EmDash), and the store
|
|
69
|
+
* re-stamps/clears it authoritatively on publish/unpublish. Optional so pre-EC-447
|
|
70
|
+
* fixtures/callers stay valid; `loadHydratedCms` always sets it.
|
|
71
|
+
*/
|
|
72
|
+
publishTimestamps?: Record<string, string>;
|
|
62
73
|
/**
|
|
63
74
|
* The project's asset records (the `assets` table — NOT cms `asset`-collection
|
|
64
75
|
* documents). The preview content engine wires these into its lookup so listing/
|
|
@@ -86,6 +86,12 @@ export async function loadHydratedCms(operations, projectId) {
|
|
|
86
86
|
// EC-445: carry each record's `updatedAt` onto the session for the $updatedAt
|
|
87
87
|
// column — a per-document metadata accessor, exactly like `publishedKeys`.
|
|
88
88
|
timestamps: Object.fromEntries(contentRecords.map((record) => [documentKey(record.document), record.updatedAt])),
|
|
89
|
+
// EC-447: carry each PUBLISHED record's materialized `publishedAt` for the
|
|
90
|
+
// $publishedAt column. Only records that actually have one contribute (no
|
|
91
|
+
// session-start fallback) — an absent entry is a real "no publish date".
|
|
92
|
+
publishTimestamps: Object.fromEntries(contentRecords
|
|
93
|
+
.filter((record) => record.publishedAt !== undefined)
|
|
94
|
+
.map((record) => [documentKey(record.document), record.publishedAt])),
|
|
89
95
|
assets: assets.ok ? assets.value : [],
|
|
90
96
|
},
|
|
91
97
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-hydration.js","sourceRoot":"","sources":["../../../src/lib/operations/session-hydration.ts"],"names":[],"mappings":"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","sourcesContent":["import { documentKey } from '@elytracms/core/cms-core'\nimport type { CmsDocument, CollectionDef } from '@elytracms/core/cms-core'\nimport type { AssetRecord } from '@elytracms/core/persistence'\nimport { DATA_SOURCES_COLLECTION } from '../data/sources-store'\nimport type { OperationResult, OperationsClient } from './operations-package'\n\n/**\n * Legacy marker (EC-246b): editor-owned saved views are retired — list\n * presentation is config-as-code now (named views live in `cms/`). The id is kept\n * only so hydration keeps excluding any `cms-saved-views` documents a pre-EC-246b\n * deployment persisted, so they never surface as content.\n */\nexport const VIEWS_COLLECTION = 'cms-saved-views'\n\n/**\n * Persisted-session hydration (Milestone B sign-off fix, EC-177 gap 1).\n *\n * Against a persistent backend (Convex mode) a studio session must BOOT from\n * what is persisted — the saved CMS schema + documents (+ asset records) — and\n * fall back to the deterministic fixtures only when the project has nothing\n * persisted yet (first run). Before this, every session seeded from fixtures,\n * so reloads hid the editor's work.\n *\n * EC-287: the DB-resident project graph and its publish path are retired —\n * layouts/config/schema/components are CODE, the DB holds content only. A\n * session no longer hydrates a graph revision or graph-publishing state; CMS\n * content (with per-document `publishedVersion`, EC-224) is the only persisted\n * surface a session boots from.\n *\n * This module is the pure load half: read-only `@elytracms/core/operations` queries\n * composed into one hydration result. `not-found` is a *normal* answer (the\n * first-run fallback); every other failure propagates as the structured\n * operation error so the gate renders it — never a silent fixture fallback.\n * The React gate that applies it lives in\n * `components/studio/project-session.tsx`. EC-361: the playground calls the\n * SAME loader (over the seeded in-memory adapter, through operations — AD-17)\n * to hydrate the canvas/preview content session; only the render-gating is\n * Convex-specific.\n */\n\n/**\n * Collections owned by studio subsystems (data sources, saved views). They live\n * in the same persisted schema but are NOT content: the CMS session, the\n * sidebar, and the content preview engine must never offer them as authorable\n * collections. Routes/redirects are NO LONGER system collections (EC-207): they\n * are config-sourced and never persisted to the backend.\n */\nexport const STUDIO_SYSTEM_COLLECTION_IDS: ReadonlySet<string> = new Set([\n DATA_SOURCES_COLLECTION,\n VIEWS_COLLECTION,\n])\n\n/** The persisted CMS content of a project: schema + documents, system collections excluded. */\nexport interface HydratedCms {\n collections: CollectionDef[]\n documents: CmsDocument[]\n /**\n * Published document keys (EC-224), via the version pointer: a record is\n * published iff `publishedVersion` is set. The session boots its\n * `publishedKeys` from this — published-ness is the pointer, nothing else.\n */\n publishedKeys: string[]\n /**\n * Per-document last-write timestamps (EC-445), keyed by `documentKey`\n * (`collection:id`) → the persisted `cmsDocuments.updatedAt` ISO string. Backs the\n * `$updatedAt` list column; the session seeds its `documentUpdatedAt` map from this.\n * Optional so pre-EC-445 fixtures/callers stay valid (the session falls back to a\n * session-start stamp for the seeded documents); `loadHydratedCms` always sets it.\n */\n timestamps?: Record<string, string>\n /**\n * The project's asset records (the `assets` table — NOT cms `asset`-collection\n * documents). The preview content engine wires these into its lookup so listing/\n * relation results resolve their `asset`-typed FIELDS (e.g. a ProductGrid product's\n * `gallery`) to delivery shape — matching what the renderer's `resolveAsset` already\n * does for static `asset` props (EC-262). Without them, the studio canvas renders\n * such nested images blank even though the documents reference real assets.\n */\n assets: AssetRecord[]\n}\n\n/** Everything a Convex-mode session boots from. `null` fields mean \"nothing persisted\" (first run). */\nexport interface HydratedProjectSession {\n /** Persisted CMS schema + documents, or `null` when no content schema was ever saved. */\n cms: HydratedCms | null\n}\n\n/**\n * Load the persisted CMS content state. `null` when the project has no\n * persisted content schema (first run — the session seeds fixtures and the\n * persister's baseline will persist them on the first edit). A schema that\n * contains only studio system collections counts as \"no content persisted\":\n * system stores (saved views, routes) may create the schema record before any\n * content baseline ran.\n */\nexport async function loadHydratedCms(\n operations: OperationsClient,\n projectId: string,\n): Promise<OperationResult<HydratedCms | null>> {\n const schema = await operations.schema.get({ projectId })\n if (!schema.ok) {\n return schema.error.code === 'not-found' ? { ok: true, value: null } : schema\n }\n const collections = schema.value.collections.filter(\n (collection) => !STUDIO_SYSTEM_COLLECTION_IDS.has(collection.id),\n )\n if (collections.length === 0) return { ok: true, value: null }\n const documents = await operations.documents.list({ projectId })\n if (!documents.ok) return documents\n // The project's asset records (assets table), wired into the preview lookup so\n // listing/relation results resolve their nested `asset` fields (EC-262). Non-fatal:\n // a failed asset list degrades previews to no images, never blocks the session.\n const assets = await operations.assets.list({ projectId })\n const contentIds = new Set(collections.map((collection) => collection.id))\n const contentRecords = documents.value.filter((record) =>\n contentIds.has(record.document.collection),\n )\n // EC-224: a record is published when its version pointer is set — the same\n // gate delivery reads. There is no flat-state fallback (the field is gone).\n const isRecordPublished = (record: (typeof contentRecords)[number]) =>\n record.publishedVersion !== undefined\n return {\n ok: true,\n value: {\n collections,\n // The live row hydrates AS PERSISTED — a continuous draft (EC-224). We no\n // longer stamp a `state:'published'` mirror: published-ness is the version\n // pointer (`publishedKeys`), which every studio reader now consults (the\n // table badge/$state via `isPublished`, the preview engine via the pin).\n documents: contentRecords.map((record) => record.document),\n publishedKeys: contentRecords\n .filter(isRecordPublished)\n .map((record) => documentKey(record.document)),\n // EC-445: carry each record's `updatedAt` onto the session for the $updatedAt\n // column — a per-document metadata accessor, exactly like `publishedKeys`.\n timestamps: Object.fromEntries(\n contentRecords.map((record) => [documentKey(record.document), record.updatedAt]),\n ),\n assets: assets.ok ? assets.value : [],\n },\n }\n}\n\n/**\n * Load everything a project session hydrates from. Post-EC-287 that is just the\n * persisted CMS content (schema + documents + assets): the project graph and its\n * publish path are gone, so there is no graph revision or graph-publishing state\n * to load. Per-document `publishedVersion` (EC-224) is the only published surface.\n */\nexport async function loadProjectSession(\n operations: OperationsClient,\n projectId: string,\n): Promise<OperationResult<HydratedProjectSession>> {\n const cms = await loadHydratedCms(operations, projectId)\n if (!cms.ok) return cms\n return { ok: true, value: { cms: cms.value } }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session-hydration.js","sourceRoot":"","sources":["../../../src/lib/operations/session-hydration.ts"],"names":[],"mappings":"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","sourcesContent":["import { documentKey } from '@elytracms/core/cms-core'\nimport type { CmsDocument, CollectionDef } from '@elytracms/core/cms-core'\nimport type { AssetRecord } from '@elytracms/core/persistence'\nimport { DATA_SOURCES_COLLECTION } from '../data/sources-store'\nimport type { OperationResult, OperationsClient } from './operations-package'\n\n/**\n * Legacy marker (EC-246b): editor-owned saved views are retired — list\n * presentation is config-as-code now (named views live in `cms/`). The id is kept\n * only so hydration keeps excluding any `cms-saved-views` documents a pre-EC-246b\n * deployment persisted, so they never surface as content.\n */\nexport const VIEWS_COLLECTION = 'cms-saved-views'\n\n/**\n * Persisted-session hydration (Milestone B sign-off fix, EC-177 gap 1).\n *\n * Against a persistent backend (Convex mode) a studio session must BOOT from\n * what is persisted — the saved CMS schema + documents (+ asset records) — and\n * fall back to the deterministic fixtures only when the project has nothing\n * persisted yet (first run). Before this, every session seeded from fixtures,\n * so reloads hid the editor's work.\n *\n * EC-287: the DB-resident project graph and its publish path are retired —\n * layouts/config/schema/components are CODE, the DB holds content only. A\n * session no longer hydrates a graph revision or graph-publishing state; CMS\n * content (with per-document `publishedVersion`, EC-224) is the only persisted\n * surface a session boots from.\n *\n * This module is the pure load half: read-only `@elytracms/core/operations` queries\n * composed into one hydration result. `not-found` is a *normal* answer (the\n * first-run fallback); every other failure propagates as the structured\n * operation error so the gate renders it — never a silent fixture fallback.\n * The React gate that applies it lives in\n * `components/studio/project-session.tsx`. EC-361: the playground calls the\n * SAME loader (over the seeded in-memory adapter, through operations — AD-17)\n * to hydrate the canvas/preview content session; only the render-gating is\n * Convex-specific.\n */\n\n/**\n * Collections owned by studio subsystems (data sources, saved views). They live\n * in the same persisted schema but are NOT content: the CMS session, the\n * sidebar, and the content preview engine must never offer them as authorable\n * collections. Routes/redirects are NO LONGER system collections (EC-207): they\n * are config-sourced and never persisted to the backend.\n */\nexport const STUDIO_SYSTEM_COLLECTION_IDS: ReadonlySet<string> = new Set([\n DATA_SOURCES_COLLECTION,\n VIEWS_COLLECTION,\n])\n\n/** The persisted CMS content of a project: schema + documents, system collections excluded. */\nexport interface HydratedCms {\n collections: CollectionDef[]\n documents: CmsDocument[]\n /**\n * Published document keys (EC-224), via the version pointer: a record is\n * published iff `publishedVersion` is set. The session boots its\n * `publishedKeys` from this — published-ness is the pointer, nothing else.\n */\n publishedKeys: string[]\n /**\n * Per-document last-write timestamps (EC-445), keyed by `documentKey`\n * (`collection:id`) → the persisted `cmsDocuments.updatedAt` ISO string. Backs the\n * `$updatedAt` list column; the session seeds its `documentUpdatedAt` map from this.\n * Optional so pre-EC-445 fixtures/callers stay valid (the session falls back to a\n * session-start stamp for the seeded documents); `loadHydratedCms` always sets it.\n */\n timestamps?: Record<string, string>\n /**\n * Per-document FIRST-publish instants (EC-447), keyed by `documentKey`\n * (`collection:id`) → the persisted `cmsDocuments.publishedAt` ISO string. Present\n * only for documents that carry one (currently published, with a recorded publish\n * instant); the session seeds its `documentPublishedAt` map from this. Backs the\n * `$publishedAt` list column. UNLIKE {@link timestamps} there is no session-start\n * fallback — an absent entry is a real \"no publish date\" (EmDash), and the store\n * re-stamps/clears it authoritatively on publish/unpublish. Optional so pre-EC-447\n * fixtures/callers stay valid; `loadHydratedCms` always sets it.\n */\n publishTimestamps?: Record<string, string>\n /**\n * The project's asset records (the `assets` table — NOT cms `asset`-collection\n * documents). The preview content engine wires these into its lookup so listing/\n * relation results resolve their `asset`-typed FIELDS (e.g. a ProductGrid product's\n * `gallery`) to delivery shape — matching what the renderer's `resolveAsset` already\n * does for static `asset` props (EC-262). Without them, the studio canvas renders\n * such nested images blank even though the documents reference real assets.\n */\n assets: AssetRecord[]\n}\n\n/** Everything a Convex-mode session boots from. `null` fields mean \"nothing persisted\" (first run). */\nexport interface HydratedProjectSession {\n /** Persisted CMS schema + documents, or `null` when no content schema was ever saved. */\n cms: HydratedCms | null\n}\n\n/**\n * Load the persisted CMS content state. `null` when the project has no\n * persisted content schema (first run — the session seeds fixtures and the\n * persister's baseline will persist them on the first edit). A schema that\n * contains only studio system collections counts as \"no content persisted\":\n * system stores (saved views, routes) may create the schema record before any\n * content baseline ran.\n */\nexport async function loadHydratedCms(\n operations: OperationsClient,\n projectId: string,\n): Promise<OperationResult<HydratedCms | null>> {\n const schema = await operations.schema.get({ projectId })\n if (!schema.ok) {\n return schema.error.code === 'not-found' ? { ok: true, value: null } : schema\n }\n const collections = schema.value.collections.filter(\n (collection) => !STUDIO_SYSTEM_COLLECTION_IDS.has(collection.id),\n )\n if (collections.length === 0) return { ok: true, value: null }\n const documents = await operations.documents.list({ projectId })\n if (!documents.ok) return documents\n // The project's asset records (assets table), wired into the preview lookup so\n // listing/relation results resolve their nested `asset` fields (EC-262). Non-fatal:\n // a failed asset list degrades previews to no images, never blocks the session.\n const assets = await operations.assets.list({ projectId })\n const contentIds = new Set(collections.map((collection) => collection.id))\n const contentRecords = documents.value.filter((record) =>\n contentIds.has(record.document.collection),\n )\n // EC-224: a record is published when its version pointer is set — the same\n // gate delivery reads. There is no flat-state fallback (the field is gone).\n const isRecordPublished = (record: (typeof contentRecords)[number]) =>\n record.publishedVersion !== undefined\n return {\n ok: true,\n value: {\n collections,\n // The live row hydrates AS PERSISTED — a continuous draft (EC-224). We no\n // longer stamp a `state:'published'` mirror: published-ness is the version\n // pointer (`publishedKeys`), which every studio reader now consults (the\n // table badge/$state via `isPublished`, the preview engine via the pin).\n documents: contentRecords.map((record) => record.document),\n publishedKeys: contentRecords\n .filter(isRecordPublished)\n .map((record) => documentKey(record.document)),\n // EC-445: carry each record's `updatedAt` onto the session for the $updatedAt\n // column — a per-document metadata accessor, exactly like `publishedKeys`.\n timestamps: Object.fromEntries(\n contentRecords.map((record) => [documentKey(record.document), record.updatedAt]),\n ),\n // EC-447: carry each PUBLISHED record's materialized `publishedAt` for the\n // $publishedAt column. Only records that actually have one contribute (no\n // session-start fallback) — an absent entry is a real \"no publish date\".\n publishTimestamps: Object.fromEntries(\n contentRecords\n .filter((record) => record.publishedAt !== undefined)\n .map((record) => [documentKey(record.document), record.publishedAt as string]),\n ),\n assets: assets.ok ? assets.value : [],\n },\n }\n}\n\n/**\n * Load everything a project session hydrates from. Post-EC-287 that is just the\n * persisted CMS content (schema + documents + assets): the project graph and its\n * publish path are gone, so there is no graph revision or graph-publishing state\n * to load. Per-document `publishedVersion` (EC-224) is the only published surface.\n */\nexport async function loadProjectSession(\n operations: OperationsClient,\n projectId: string,\n): Promise<OperationResult<HydratedProjectSession>> {\n const cms = await loadHydratedCms(operations, projectId)\n if (!cms.ok) return cms\n return { ok: true, value: { cms: cms.value } }\n}\n"]}
|
|
@@ -22,6 +22,9 @@ function docRecordFromDoc(doc) {
|
|
|
22
22
|
projectId: doc.projectId,
|
|
23
23
|
document: doc.document,
|
|
24
24
|
...(doc.publishedVersion !== undefined ? { publishedVersion: doc.publishedVersion } : {}),
|
|
25
|
+
// EC-447: carry the materialized publish instant onto the contract record so the
|
|
26
|
+
// studio's $publishedAt column + its reconcile read the authoritative value.
|
|
27
|
+
...(doc.publishedAt !== undefined ? { publishedAt: doc.publishedAt } : {}),
|
|
25
28
|
createdAt: doc.createdAt,
|
|
26
29
|
updatedAt: doc.updatedAt,
|
|
27
30
|
};
|
|
@@ -179,11 +182,16 @@ class ConvexCmsRepository {
|
|
|
179
182
|
// the pinned version must both exist (each throws RecordNotFoundError).
|
|
180
183
|
await this.getDocument(projectId, ref);
|
|
181
184
|
await this.getDocumentVersion(projectId, ref, version);
|
|
185
|
+
// EC-447: pass a fresh publish-action instant as the SET-ONCE candidate (the
|
|
186
|
+
// handler writes it only when the row has no `publishedAt` yet). One `now` for
|
|
187
|
+
// both stamps — this IS the publish moment.
|
|
188
|
+
const now = this.clock();
|
|
182
189
|
const doc = (await this.client.mutation(mutationRef('cms:publishDocumentVersion'), {
|
|
183
190
|
collection: ref.collection,
|
|
184
191
|
docId: ref.id,
|
|
185
192
|
version,
|
|
186
|
-
updatedAt:
|
|
193
|
+
updatedAt: now,
|
|
194
|
+
publishedAt: now,
|
|
187
195
|
}));
|
|
188
196
|
return docRecordFromDoc(doc);
|
|
189
197
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convex-adapter.js","sourceRoot":"","sources":["../../../src/lib/persistence/convex-adapter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ConvexHttpClient } from 'convex/browser'\nimport { documentKey, documentSchema } from '@elytracms/core/cms-core'\nimport type { CmsDocument, CollectionDef, DocumentRef } from '@elytracms/core/cms-core'\nimport {\n RecordNotFoundError,\n PersistenceValidationError,\n compareReferenceEntries,\n highestRole,\n isKnownRole,\n validateForPersistence,\n} from '@elytracms/core/persistence'\nimport type {\n AssetRecord,\n AssetRepository,\n BackendStatus,\n BackendValidationInput,\n BackendValidationResult,\n BlobStorageAdapter,\n CliTokenRecord,\n CliTokenRepository,\n CmsRepository,\n DeliveryConfigStatus,\n DeliveryRepository,\n CmsSchemaRecord,\n CmsDocumentRecord,\n DocumentVersionRecord,\n SaveDocumentVersionOptions,\n MembersRepository,\n NewAssetInput,\n NewCliTokenInput,\n PersistenceAdapter,\n OutboundReference,\n ProjectMemberRecord,\n ProjectRole,\n ReferenceEntry,\n ReferenceEntryRecord,\n ReferenceIndexBuildMarker,\n ReferenceIndexRepository,\n ReferenceLookupOptions,\n ReferenceSource,\n ReferenceSourceType,\n ReferenceTarget,\n ReferenceVariant,\n RedirectEntry,\n RedirectRepository,\n SaveRedirectInput,\n SaveUserInput,\n UserRecord,\n UserRepository,\n InviteRecord,\n InviteRepository,\n SaveInviteInput,\n} from '@elytracms/core/persistence'\nimport { normalizeInviteEmail } from '@elytracms/core/persistence'\nimport { queryRef, mutationRef } from './convex-client'\nimport { ConvexBlobStorage } from './convex-blob-storage'\n\n/** Wall-clock for runtime writes (deterministic seeds use a fixed clock instead). */\ntype Clock = () => string\nconst realClock: Clock = () => new Date().toISOString()\n\nfunction newId(prefix: string): string {\n const rand =\n typeof globalThis.crypto?.randomUUID === 'function'\n ? globalThis.crypto.randomUUID()\n : Math.abs(Date.parse(new Date().toISOString())).toString(36)\n return `${prefix}_${rand}`\n}\n\n// --- CMS, Assets ----------------------------------------------------------\n\n/**\n * Deterministic, config-owned schema timestamps (EC-183). CMS collections live\n * in `elytra.config.ts`, not the backend, so the synthesized `CmsSchemaRecord`\n * carries a stable placeholder rather than DB-row timestamps.\n */\nconst CONFIG_SCHEMA_TIMESTAMP = '2026-01-01T00:00:00.000Z'\n\ninterface DocumentDoc {\n projectId: string\n document: CmsDocument\n publishedVersion?: number\n createdAt: string\n updatedAt: string\n}\nfunction docRecordFromDoc(doc: DocumentDoc): CmsDocumentRecord {\n return {\n projectId: doc.projectId,\n document: doc.document,\n ...(doc.publishedVersion !== undefined ? { publishedVersion: doc.publishedVersion } : {}),\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n }\n}\n\ninterface VersionDoc {\n collection: string\n docId: string\n version: number\n snapshot: CmsDocument\n label?: string\n origin?: string\n createdAt: string\n}\n/** The backend row carries no `projectId` (single-tenant); stamp it from the call. */\nfunction versionRecordFromDoc(doc: VersionDoc, projectId: string): DocumentVersionRecord {\n return {\n projectId,\n collection: doc.collection,\n docId: doc.docId,\n version: doc.version,\n snapshot: doc.snapshot,\n createdAt: doc.createdAt,\n ...(doc.label ? { label: doc.label } : {}),\n ...(doc.origin ? { origin: doc.origin } : {}),\n }\n}\n\nclass ConvexCmsRepository implements CmsRepository {\n /**\n * @param collections the active project's config-owned CMS collections\n * (EC-183, AD-11), injected from `elytra.config.ts`. The Convex backend has\n * no `cmsSchemas` table — `findSchema`/`getSchema` serve these instead.\n */\n constructor(\n private readonly client: ConvexHttpClient,\n private readonly collections: readonly CollectionDef[] = [],\n private readonly clock: Clock = realClock,\n ) {}\n\n async saveSchema(\n _projectId: string,\n _collections: readonly CollectionDef[],\n ): Promise<CmsSchemaRecord> {\n // Read-only (EC-183, AD-11): the CMS collection schema is owned by\n // `elytra.config.ts`; the backend stores documents only. A write here is a\n // visible, structured error rather than a silent no-op (the studio's CMS\n // schema surfaces are read-only; legitimate writers were removed in EC-183).\n throw new PersistenceValidationError(\n 'CMS collection schema is owned by elytra.config.ts and cannot be written to the backend.',\n [\n {\n code: 'schema-config-owned',\n message:\n 'Edit elytra.config.ts to change collections; the backend stores documents only.',\n path: [],\n domain: 'cms',\n },\n ],\n )\n }\n\n /** Config-owned schema (EC-183): the active project's collections from config. */\n private configSchema(projectId: string): CmsSchemaRecord {\n return {\n projectId,\n collections: [...this.collections],\n createdAt: CONFIG_SCHEMA_TIMESTAMP,\n updatedAt: CONFIG_SCHEMA_TIMESTAMP,\n }\n }\n\n async getSchema(projectId: string): Promise<CmsSchemaRecord> {\n return this.configSchema(projectId)\n }\n\n async findSchema(projectId: string): Promise<CmsSchemaRecord | undefined> {\n return this.configSchema(projectId)\n }\n\n async upsertDocument(projectId: string, document: CmsDocument): Promise<CmsDocumentRecord> {\n // The backend is a pure document store (EC-183, AD-11): collection-membership\n // validation lives in `@elytracms/core/cms-core` `validateDocument`, which the studio\n // runs pre-dispatch (cell edits, imports). We still parse the canonical\n // document shape so malformed blobs never reach storage.\n const parsedDoc = documentSchema.parse(document)\n const now = this.clock()\n const doc = (await this.client.mutation(mutationRef('cms:upsertDocument'), {\n collection: parsedDoc.collection,\n docId: parsedDoc.id,\n document: parsedDoc,\n createdAt: now,\n updatedAt: now,\n })) as DocumentDoc\n return docRecordFromDoc(doc)\n }\n\n async getDocument(projectId: string, ref: DocumentRef): Promise<CmsDocumentRecord> {\n const found = await this.findDocument(projectId, ref)\n if (!found)\n throw new RecordNotFoundError('cms-document', `${projectId}/${ref.collection}:${ref.id}`)\n return found\n }\n\n async findDocument(projectId: string, ref: DocumentRef): Promise<CmsDocumentRecord | undefined> {\n const doc = (await this.client.query(queryRef('cms:getDocument'), {\n collection: ref.collection,\n docId: ref.id,\n })) as DocumentDoc | null\n return doc ? docRecordFromDoc(doc) : undefined\n }\n\n async listDocuments(projectId: string, collection?: string): Promise<CmsDocumentRecord[]> {\n const docs = (await this.client.query(queryRef('cms:listDocuments'), {\n ...(collection ? { collection } : {}),\n })) as DocumentDoc[]\n // EC-153 parity: deterministic document-key ordering (Convex index order\n // within a project is insertion order, so we sort client-side).\n return docs\n .map(docRecordFromDoc)\n .sort((a, b) => (documentKey(a.document) < documentKey(b.document) ? -1 : 1))\n }\n\n async listPublishedDocuments(projectId: string, collection?: string): Promise<CmsDocument[]> {\n // EC-307: one bulk read for the published pool (collapses the per-document\n // `listVersions` fan-out). The handler resolves each pin to its snapshot.\n const snapshots = (await this.client.query(queryRef('cms:listPublishedDocuments'), {\n ...(collection ? { collection } : {}),\n })) as CmsDocument[]\n // Deterministic document-key order (parity with the in-memory adapter).\n return snapshots\n .map((doc) => documentSchema.parse(doc))\n .sort((a, b) => (documentKey(a) < documentKey(b) ? -1 : 1))\n }\n\n async deleteDocument(projectId: string, ref: DocumentRef): Promise<void> {\n // EC-153 parity: deleting a missing document is not-found, never a no-op.\n await this.getDocument(projectId, ref)\n await this.client.mutation(mutationRef('cms:deleteDocument'), {\n collection: ref.collection,\n docId: ref.id,\n })\n }\n\n async saveDocumentVersion(\n projectId: string,\n snapshot: CmsDocument,\n options: SaveDocumentVersionOptions = {},\n ): Promise<DocumentVersionRecord> {\n const parsed = documentSchema.parse(snapshot)\n const doc = (await this.client.mutation(mutationRef('cms:saveDocumentVersion'), {\n collection: parsed.collection,\n docId: parsed.id,\n snapshot: parsed,\n createdAt: this.clock(),\n ...(options.label ? { label: options.label } : {}),\n ...(options.origin ? { origin: options.origin } : {}),\n })) as VersionDoc\n return versionRecordFromDoc(doc, projectId)\n }\n\n async listDocumentVersions(\n projectId: string,\n ref: DocumentRef,\n ): Promise<DocumentVersionRecord[]> {\n const docs = (await this.client.query(queryRef('cms:listDocumentVersions'), {\n collection: ref.collection,\n docId: ref.id,\n })) as VersionDoc[]\n return docs.map((doc) => versionRecordFromDoc(doc, projectId))\n }\n\n async listAllDocumentVersions(projectId: string): Promise<DocumentVersionRecord[]> {\n const docs = (await this.client.query(\n queryRef('cms:listAllDocumentVersions'),\n {},\n )) as VersionDoc[]\n return docs.map((doc) => versionRecordFromDoc(doc, projectId))\n }\n\n async getDocumentVersion(\n projectId: string,\n ref: DocumentRef,\n version: number,\n ): Promise<DocumentVersionRecord> {\n const doc = (await this.client.query(queryRef('cms:getDocumentVersion'), {\n collection: ref.collection,\n docId: ref.id,\n version,\n })) as VersionDoc | null\n if (!doc) {\n throw new RecordNotFoundError(\n 'cms-document-version',\n `${projectId}/${ref.collection}:${ref.id}#${version}`,\n )\n }\n return versionRecordFromDoc(doc, projectId)\n }\n\n async publishDocumentVersion(\n projectId: string,\n ref: DocumentRef,\n version: number,\n ): Promise<CmsDocumentRecord> {\n // Taxonomy enforced adapter-side (the handler is lenient): the document and\n // the pinned version must both exist (each throws RecordNotFoundError).\n await this.getDocument(projectId, ref)\n await this.getDocumentVersion(projectId, ref, version)\n const doc = (await this.client.mutation(mutationRef('cms:publishDocumentVersion'), {\n collection: ref.collection,\n docId: ref.id,\n version,\n updatedAt: this.clock(),\n })) as DocumentDoc\n return docRecordFromDoc(doc)\n }\n\n async unpublishDocument(projectId: string, ref: DocumentRef): Promise<CmsDocumentRecord> {\n const existing = await this.getDocument(projectId, ref)\n if (existing.publishedVersion === undefined) {\n throw new PersistenceValidationError('Document is not currently published.', [\n {\n code: 'invalid-transition',\n message: 'Cannot unpublish: the document has no published version.',\n domain: 'cms',\n },\n ])\n }\n const doc = (await this.client.mutation(mutationRef('cms:unpublishDocument'), {\n collection: ref.collection,\n docId: ref.id,\n updatedAt: this.clock(),\n })) as DocumentDoc\n return docRecordFromDoc(doc)\n }\n}\n\ninterface AssetDoc {\n projectId: string\n assetId: string\n filename: string\n contentType: string\n byteSize: number\n width?: number\n height?: number\n alt?: string\n /** Editable display title (EC-152). */\n title?: string\n storageKey: string\n url?: string\n /** Normalized image focal point `{ x, y }` in 0–1 (EC-230). */\n focalPoint?: { x: number; y: number }\n /** Downscaled editorial preview variant (EC-400). */\n thumb?: { storageKey: string; url: string; width: number; height: number }\n /** Delivery LQIP blur-placeholder data URI (EC-420). */\n lqip?: string\n /** Schema-driven asset metadata bag (EC-301). */\n meta?: Record<string, unknown>\n createdAt: string\n updatedAt: string\n}\n\n/** Map a Convex asset doc onto the contract record (assetId → id, drop _id/_creationTime). */\nfunction assetRecordFromDoc(doc: AssetDoc): AssetRecord {\n return {\n id: doc.assetId,\n projectId: doc.projectId,\n filename: doc.filename,\n contentType: doc.contentType,\n byteSize: doc.byteSize,\n ...(doc.width !== undefined ? { width: doc.width } : {}),\n ...(doc.height !== undefined ? { height: doc.height } : {}),\n ...(doc.alt !== undefined ? { alt: doc.alt } : {}),\n ...(doc.title !== undefined ? { title: doc.title } : {}),\n ...(doc.url !== undefined ? { url: doc.url } : {}),\n ...(doc.focalPoint !== undefined ? { focalPoint: doc.focalPoint } : {}),\n ...(doc.thumb !== undefined ? { thumb: doc.thumb } : {}),\n ...(doc.lqip !== undefined ? { lqip: doc.lqip } : {}),\n ...(doc.meta !== undefined ? { meta: doc.meta } : {}),\n storageKey: doc.storageKey,\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n }\n}\n\nclass ConvexAssetRepository implements AssetRepository {\n constructor(\n private readonly client: ConvexHttpClient,\n private readonly clock: Clock = realClock,\n ) {}\n\n async upsertAsset(projectId: string, input: NewAssetInput): Promise<AssetRecord> {\n const now = this.clock()\n const id = input.id ?? newId('asset')\n const doc = (await this.client.mutation(mutationRef('assets:upsert'), {\n assetId: id,\n filename: input.filename,\n contentType: input.contentType,\n byteSize: input.byteSize,\n ...(input.width !== undefined ? { width: input.width } : {}),\n ...(input.height !== undefined ? { height: input.height } : {}),\n ...(input.alt !== undefined ? { alt: input.alt } : {}),\n // EC-153 parity: `title` (EC-152 metadata) must persist, not silently drop.\n ...(input.title !== undefined ? { title: input.title } : {}),\n ...(input.url !== undefined ? { url: input.url } : {}),\n ...(input.focalPoint !== undefined ? { focalPoint: input.focalPoint } : {}),\n ...(input.thumb !== undefined ? { thumb: input.thumb } : {}),\n ...(input.lqip !== undefined ? { lqip: input.lqip } : {}),\n ...(input.meta !== undefined ? { meta: input.meta } : {}),\n storageKey: input.storageKey ?? `${projectId}/${id}`,\n createdAt: now,\n updatedAt: now,\n })) as AssetDoc\n return assetRecordFromDoc(doc)\n }\n\n async getAsset(projectId: string, id: string): Promise<AssetRecord> {\n const found = await this.findAsset(projectId, id)\n if (!found) throw new RecordNotFoundError('asset', `${projectId}/${id}`)\n return found\n }\n\n async findAsset(projectId: string, id: string): Promise<AssetRecord | undefined> {\n const doc = (await this.client.query(queryRef('assets:get'), {\n assetId: id,\n })) as AssetDoc | null\n return doc ? assetRecordFromDoc(doc) : undefined\n }\n\n async listAssets(projectId: string): Promise<AssetRecord[]> {\n const docs = (await this.client.query(queryRef('assets:list'), {})) as AssetDoc[]\n return docs.map(assetRecordFromDoc)\n }\n\n async removeAsset(projectId: string, id: string): Promise<void> {\n // EC-153 parity: removing an unknown asset is not-found, never a no-op.\n await this.getAsset(projectId, id)\n await this.client.mutation(mutationRef('assets:remove'), { assetId: id })\n }\n\n async resolveMany(projectId: string, ids: readonly string[]): Promise<Map<string, AssetRecord>> {\n const all = await this.listAssets(projectId)\n const out = new Map<string, AssetRecord>()\n for (const asset of all) if (ids.includes(asset.id)) out.set(asset.id, asset)\n return out\n }\n}\n\n// --- Reference index (EC-155) ------------------------------------------------\n\n/** A Convex reference-entry doc (flattened source/target for index-backed lookups). */\ninterface ReferenceEntryDoc {\n projectId: string\n variant: ReferenceVariant\n sourceType: ReferenceSourceType\n sourceId: string\n path: string\n targetType: ReferenceTarget['type']\n targetId: string\n kind: ReferenceEntryRecord['kind']\n}\n\nfunction referenceRecordFromDoc(doc: ReferenceEntryDoc): ReferenceEntryRecord {\n return {\n projectId: doc.projectId,\n variant: doc.variant,\n source: { type: doc.sourceType, id: doc.sourceId },\n path: doc.path,\n target: { type: doc.targetType, id: doc.targetId },\n kind: doc.kind,\n }\n}\n\n/** Deterministic ordering is a repository contract; Convex docs sort client-side. */\nfunction sortedReferenceRecords(docs: ReferenceEntryDoc[]): ReferenceEntryRecord[] {\n return docs.map(referenceRecordFromDoc).sort(compareReferenceEntries)\n}\n\nclass ConvexReferenceIndexRepository implements ReferenceIndexRepository {\n constructor(private readonly client: ConvexHttpClient) {}\n\n async replaceForSource(\n projectId: string,\n variant: ReferenceVariant,\n source: ReferenceSource,\n references: readonly OutboundReference[],\n ): Promise<ReferenceEntryRecord[]> {\n await this.client.mutation(mutationRef('references:replaceForSource'), {\n variant,\n sourceType: source.type,\n sourceId: source.id,\n references: references.map((r) => ({\n path: r.path,\n targetType: r.target.type,\n targetId: r.target.id,\n kind: r.kind,\n })),\n })\n return this.outbound(projectId, source, { variant })\n }\n\n async replaceForSourceTypes(\n projectId: string,\n variant: ReferenceVariant,\n sourceTypes: readonly ReferenceSourceType[],\n entries: readonly ReferenceEntry[],\n ): Promise<ReferenceEntryRecord[]> {\n await this.client.mutation(mutationRef('references:replaceForSourceTypes'), {\n variant,\n sourceTypes: [...sourceTypes],\n entries: entries.map((e) => ({\n sourceType: e.source.type,\n sourceId: e.source.id,\n path: e.path,\n targetType: e.target.type,\n targetId: e.target.id,\n kind: e.kind,\n })),\n })\n const all = await this.listEntries(projectId, variant)\n const types = new Set(sourceTypes)\n return all.filter((record) => types.has(record.source.type))\n }\n\n async removeForSource(\n projectId: string,\n source: ReferenceSource,\n variant?: ReferenceVariant,\n ): Promise<void> {\n await this.client.mutation(mutationRef('references:removeForSource'), {\n sourceType: source.type,\n sourceId: source.id,\n ...(variant ? { variant } : {}),\n })\n }\n\n async outbound(\n projectId: string,\n source: ReferenceSource,\n options: ReferenceLookupOptions = {},\n ): Promise<ReferenceEntryRecord[]> {\n const docs = (await this.client.query(queryRef('references:outbound'), {\n sourceType: source.type,\n sourceId: source.id,\n ...(options.variant ? { variant: options.variant } : {}),\n })) as ReferenceEntryDoc[]\n return sortedReferenceRecords(docs)\n }\n\n async inbound(\n projectId: string,\n target: ReferenceTarget,\n options: ReferenceLookupOptions = {},\n ): Promise<ReferenceEntryRecord[]> {\n const docs = (await this.client.query(queryRef('references:inbound'), {\n targetType: target.type,\n targetId: target.id,\n ...(options.variant ? { variant: options.variant } : {}),\n })) as ReferenceEntryDoc[]\n return sortedReferenceRecords(docs)\n }\n\n async inboundMany(\n projectId: string,\n targets: readonly ReferenceTarget[],\n options: ReferenceLookupOptions = {},\n ): Promise<ReferenceEntryRecord[][]> {\n // EC-308: one round-trip for all targets (the handler loops the by_target\n // index server-side), replacing the client's per-target inbound fan-out.\n if (targets.length === 0) return []\n const groups = (await this.client.query(queryRef('references:inboundMany'), {\n targets: targets.map((target) => ({ targetType: target.type, targetId: target.id })),\n ...(options.variant ? { variant: options.variant } : {}),\n })) as ReferenceEntryDoc[][]\n return groups.map((docs) => sortedReferenceRecords(docs))\n }\n\n async listEntries(\n projectId: string,\n variant?: ReferenceVariant,\n ): Promise<ReferenceEntryRecord[]> {\n const docs = (await this.client.query(queryRef('references:list'), {\n ...(variant ? { variant } : {}),\n })) as ReferenceEntryDoc[]\n return sortedReferenceRecords(docs)\n }\n\n async getBuildMarker(projectId: string): Promise<ReferenceIndexBuildMarker | null> {\n return (await this.client.query(queryRef('references:buildMarker'), {})) as\n | ReferenceIndexBuildMarker\n | null\n }\n\n async setBuildMarker(projectId: string, marker: ReferenceIndexBuildMarker): Promise<void> {\n await this.client.mutation(mutationRef('references:setBuildMarker'), {\n entryCount: marker.entryCount,\n })\n }\n\n async clearProject(projectId: string): Promise<void> {\n await this.client.mutation(mutationRef('references:clearProject'), {})\n }\n}\n\n// --- Members + CLI tokens (EC-150) -------------------------------------------\n\ninterface MemberDoc {\n userId: string\n role: ProjectRole\n createdAt: string\n updatedAt: string\n}\n\n/**\n * Single-tenant (EC-179): membership is deployment-scoped, so the backend rows\n * carry no `projectId`. The contract record still does — this adapter is bound\n * to its deployment's project, so it stamps that slug onto every record.\n */\nfunction memberRecordFromDoc(doc: MemberDoc, projectId: string): ProjectMemberRecord {\n return {\n projectId,\n userId: doc.userId,\n role: doc.role,\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n }\n}\n\nclass ConvexMembersRepository implements MembersRepository {\n constructor(\n private readonly client: ConvexHttpClient,\n /** The slug of this deployment's one project — stamped onto records. */\n private readonly boundProjectId: string,\n private readonly clock: Clock = realClock,\n ) {}\n\n async setMember(\n projectId: string,\n userId: string,\n role: ProjectRole,\n ): Promise<ProjectMemberRecord> {\n const now = this.clock()\n const doc = (await this.client.mutation(mutationRef('members:set'), {\n userId,\n role,\n createdAt: now,\n updatedAt: now,\n })) as MemberDoc\n return memberRecordFromDoc(doc, projectId)\n }\n\n async findMember(projectId: string, userId: string): Promise<ProjectMemberRecord | undefined> {\n const doc = (await this.client.query(queryRef('members:get'), {\n userId,\n })) as MemberDoc | null\n return doc ? memberRecordFromDoc(doc, projectId) : undefined\n }\n\n async listMembers(projectId: string): Promise<ProjectMemberRecord[]> {\n const docs = (await this.client.query(queryRef('members:list'), {})) as MemberDoc[]\n return docs.map((d) => memberRecordFromDoc(d, projectId))\n }\n\n async listMembershipsForUser(userId: string): Promise<ProjectMemberRecord[]> {\n // `members:mine` is caller-scoped server-side and returns the caller's single\n // deployment membership (or null). The studio only ever asks about the\n // session user; records are stamped with this deployment's bound project.\n const doc = (await this.client.query(queryRef('members:mine'), {})) as MemberDoc | null\n return doc && doc.userId === userId ? [memberRecordFromDoc(doc, this.boundProjectId)] : []\n }\n\n async removeMember(projectId: string, userId: string): Promise<void> {\n try {\n await this.client.mutation(mutationRef('members:remove'), { userId })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n if (/not-found/i.test(message)) {\n throw new RecordNotFoundError('project-member', `${projectId}/${userId}`)\n }\n throw error\n }\n }\n}\n\n// --- Users rail (EC-240, 240a) -----------------------------------------------\n\n/** The wire shape returned by the `users:list`/`users:get` join handlers. */\ninterface UserDoc {\n id: string\n email: string\n name?: string\n avatar?: string\n role: ProjectRole\n meta?: Record<string, unknown>\n createdAt: string\n updatedAt: string\n}\n\n/** Map a joined user doc onto the contract record; stamp the bound project. */\nfunction userRecordFromDoc(doc: UserDoc, projectId: string): UserRecord {\n return {\n id: doc.id,\n projectId,\n email: doc.email,\n ...(doc.name !== undefined ? { name: doc.name } : {}),\n ...(doc.avatar !== undefined ? { avatar: doc.avatar } : {}),\n // 240a bridge: the single `members.role` column ⇒ the 240c-ready `roles[]`.\n roles: [doc.role],\n ...(doc.meta !== undefined ? { meta: doc.meta } : {}),\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n }\n}\n\n/**\n * Collapse the 240c-ready `roles[]` to the single `members.role` column by\n * applying the strongest role (the 240a→240c enforcement bridge). Unknown\n * strings are ignored; an empty/unknown set floors to `viewer`.\n */\nfunction strongestRole(roles: readonly string[]): ProjectRole {\n // Validate against the active role policy (EC-240c) rather than a hardcoded\n // enum, then take the canonical rank max; floor an empty/unknown set to `viewer`.\n const valid = roles.filter((r) => isKnownRole(r))\n return highestRole(valid) ?? 'viewer'\n}\n\n/**\n * Convex-backed {@link UserRepository} (EC-240, 240a). Reads are the admin-gated\n * `users:*` **join** handlers (members row + auth identity); the role write\n * reuses the member write path (`members:set`, extended with the `meta` bag) so\n * there is one source for role writes — 240c collapses enforcement onto it.\n */\nclass ConvexUserRepository implements UserRepository {\n constructor(\n private readonly client: ConvexHttpClient,\n private readonly clock: Clock = realClock,\n ) {}\n\n async listUsers(projectId: string): Promise<UserRecord[]> {\n const docs = (await this.client.query(queryRef('users:list'), {})) as UserDoc[]\n return docs.map((doc) => userRecordFromDoc(doc, projectId))\n }\n\n async findUser(projectId: string, id: string): Promise<UserRecord | undefined> {\n const doc = (await this.client.query(queryRef('users:get'), { userId: id })) as UserDoc | null\n return doc ? userRecordFromDoc(doc, projectId) : undefined\n }\n\n async getUser(projectId: string, id: string): Promise<UserRecord> {\n const found = await this.findUser(projectId, id)\n if (!found) throw new RecordNotFoundError('user', `${projectId}/${id}`)\n return found\n }\n\n async getSelf(projectId: string): Promise<UserRecord | null> {\n // Self-permitted (EC-240 H-tail item 1): `users:getSelf` derives the caller\n // from the JWT server-side and returns ONLY their own join — never widened to\n // the admin-only `users:get`. The `selfId` the contract passes is ignored here;\n // the trust boundary is the server.\n const doc = (await this.client.query(queryRef('users:getSelf'), {})) as UserDoc | null\n return doc ? userRecordFromDoc(doc, projectId) : null\n }\n\n async saveUser(projectId: string, input: SaveUserInput): Promise<UserRecord> {\n const now = this.clock()\n await this.client.mutation(mutationRef('members:set'), {\n userId: input.id,\n role: strongestRole(input.roles),\n ...(input.meta !== undefined ? { meta: input.meta } : {}),\n createdAt: now,\n updatedAt: now,\n })\n // Return the full joined projection (identity + role + custom fields).\n return this.getUser(projectId, input.id)\n }\n\n async removeUser(projectId: string, id: string): Promise<void> {\n try {\n await this.client.mutation(mutationRef('members:remove'), { userId: id })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n if (/not-found/i.test(message)) {\n throw new RecordNotFoundError('user', `${projectId}/${id}`)\n }\n throw error\n }\n }\n}\n\n// --- Invite allowlist rail (EC-324) ------------------------------------------\n\n/** The wire shape returned by the `invites:*` handlers. */\ninterface InviteDoc {\n email: string\n role: string\n invitedBy?: string\n createdAt: string\n updatedAt: string\n}\n\nfunction inviteRecordFromDoc(doc: InviteDoc): InviteRecord {\n return {\n email: doc.email,\n role: doc.role,\n ...(doc.invitedBy !== undefined ? { invitedBy: doc.invitedBy } : {}),\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n }\n}\n\n/**\n * Convex-backed {@link InviteRepository} (EC-324). Admin-gated `invites:*`\n * handlers; the repo supplies timestamps (mirroring `ConvexUserRepository`). The\n * Convex auth boundary reads the same table in `beforeSessionCreation` to gate\n * open sign-up + grant the invited role.\n */\nclass ConvexInviteRepository implements InviteRepository {\n constructor(\n private readonly client: ConvexHttpClient,\n private readonly clock: Clock = realClock,\n ) {}\n\n async listInvites(_projectId: string): Promise<InviteRecord[]> {\n const docs = (await this.client.query(queryRef('invites:list'), {})) as InviteDoc[]\n return docs.map(inviteRecordFromDoc)\n }\n\n async findInvite(projectId: string, email: string): Promise<InviteRecord | undefined> {\n const key = normalizeInviteEmail(email)\n const all = await this.listInvites(projectId)\n return all.find((invite) => invite.email === key)\n }\n\n async saveInvite(_projectId: string, input: SaveInviteInput): Promise<InviteRecord> {\n const now = this.clock()\n const doc = (await this.client.mutation(mutationRef('invites:create'), {\n email: normalizeInviteEmail(input.email),\n role: input.role,\n ...(input.invitedBy !== undefined ? { invitedBy: input.invitedBy } : {}),\n createdAt: now,\n updatedAt: now,\n })) as InviteDoc\n return inviteRecordFromDoc(doc)\n }\n\n async removeInvite(projectId: string, email: string): Promise<void> {\n try {\n await this.client.mutation(mutationRef('invites:revoke'), {\n email: normalizeInviteEmail(email),\n })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n if (/not-found/i.test(message)) {\n throw new RecordNotFoundError('invite', `${projectId}/${normalizeInviteEmail(email)}`)\n }\n throw error\n }\n }\n}\n\ninterface CliTokenDoc {\n /** Retired (EC-182): optional only because live rows may still carry it until the migration clears it. */\n projectId?: string\n tokenId: string\n label: string\n tokenHash: string\n createdAt: string\n revokedAt?: string\n}\n\nfunction cliTokenRecordFromDoc(doc: CliTokenDoc): CliTokenRecord {\n return {\n id: doc.tokenId,\n label: doc.label,\n tokenHash: doc.tokenHash,\n createdAt: doc.createdAt,\n ...(doc.revokedAt ? { revokedAt: doc.revokedAt } : {}),\n }\n}\n\nclass ConvexCliTokenRepository implements CliTokenRepository {\n constructor(\n private readonly client: ConvexHttpClient,\n private readonly clock: Clock = realClock,\n ) {}\n\n async createToken(input: NewCliTokenInput): Promise<CliTokenRecord> {\n const doc = (await this.client.mutation(mutationRef('cliTokens:create'), {\n tokenId: newId('tok'),\n label: input.label,\n tokenHash: input.tokenHash,\n createdAt: this.clock(),\n })) as CliTokenDoc\n return cliTokenRecordFromDoc(doc)\n }\n\n async listTokens(): Promise<CliTokenRecord[]> {\n const docs = (await this.client.query(queryRef('cliTokens:list'), {})) as CliTokenDoc[]\n return docs.map(cliTokenRecordFromDoc)\n }\n\n async revokeToken(tokenId: string): Promise<CliTokenRecord> {\n try {\n const doc = (await this.client.mutation(mutationRef('cliTokens:revoke'), {\n tokenId,\n revokedAt: this.clock(),\n })) as CliTokenDoc\n return cliTokenRecordFromDoc(doc)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n if (/not-found/i.test(message)) {\n throw new RecordNotFoundError('cli-token', tokenId)\n }\n throw error\n }\n }\n\n async findByHash(tokenHash: string): Promise<CliTokenRecord | undefined> {\n const doc = (await this.client.query(queryRef('cliTokens:byHash'), {\n tokenHash,\n })) as CliTokenDoc | null\n return doc ? cliTokenRecordFromDoc(doc) : undefined\n }\n}\n\n/** The `redirects` table row shape (EC-303). Single-tenant: no `projectId` column. */\ninterface RedirectDoc {\n redirectId: string\n from: string\n to: string\n permanent: boolean\n enabled: boolean\n /** Schema-driven additive-field bag (EC-311 Tier 2). */\n meta?: Record<string, unknown>\n createdAt: string\n updatedAt: string\n}\n\nfunction redirectEntryFromDoc(doc: RedirectDoc, projectId: string): RedirectEntry {\n return {\n id: doc.redirectId,\n projectId,\n from: doc.from,\n to: doc.to,\n permanent: doc.permanent,\n enabled: doc.enabled,\n ...(doc.meta !== undefined ? { meta: doc.meta } : {}),\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n }\n}\n\n/**\n * Convex-backed {@link RedirectRepository} (EC-303) — editorial redirects as data\n * (not config). Content-gated CRUD over the `redirects` table; the studio's\n * editorial surface + the delivery snapshot both read through here. The editorial\n * surface sends a full record on save, so create-defaults (permanent/enabled) also\n * cover edits.\n */\nclass ConvexRedirectRepository implements RedirectRepository {\n constructor(\n private readonly client: ConvexHttpClient,\n private readonly clock: Clock = realClock,\n ) {}\n\n async saveRedirect(projectId: string, input: SaveRedirectInput): Promise<RedirectEntry> {\n const now = this.clock()\n const id = input.id ?? newId('redirect')\n const doc = (await this.client.mutation(mutationRef('redirects:save'), {\n redirectId: id,\n from: input.from,\n to: input.to,\n permanent: input.permanent ?? true,\n enabled: input.enabled ?? true,\n ...(input.meta !== undefined ? { meta: input.meta } : {}),\n createdAt: now,\n updatedAt: now,\n })) as RedirectDoc\n return redirectEntryFromDoc(doc, projectId)\n }\n\n async getRedirect(projectId: string, id: string): Promise<RedirectEntry> {\n const found = await this.findRedirect(projectId, id)\n if (!found) throw new RecordNotFoundError('redirect', `${projectId}/${id}`)\n return found\n }\n\n async findRedirect(projectId: string, id: string): Promise<RedirectEntry | undefined> {\n const doc = (await this.client.query(queryRef('redirects:get'), {\n redirectId: id,\n })) as RedirectDoc | null\n return doc ? redirectEntryFromDoc(doc, projectId) : undefined\n }\n\n async listRedirects(projectId: string): Promise<RedirectEntry[]> {\n const docs = (await this.client.query(queryRef('redirects:list'), {})) as RedirectDoc[]\n return docs\n .map((doc) => redirectEntryFromDoc(doc, projectId))\n .sort((a, b) => (a.from < b.from ? -1 : a.from > b.from ? 1 : 0))\n }\n\n async removeRedirect(projectId: string, id: string): Promise<void> {\n await this.getRedirect(projectId, id) // not-found parity with the in-memory rail\n await this.client.mutation(mutationRef('redirects:remove'), { redirectId: id })\n }\n}\n\n/**\n * Convex-backed {@link DeliveryRepository} (EC-268/EC-329) — reads the\n * deployment's actual instant-publish env var status via the guarded\n * `delivery:deliveryConfig` query. Replaces the studio's former direct\n * `getConvexClient(...).query('delivery:deliveryConfig')` call in\n * `delivery-health-banner.tsx`, the only place that used to bypass both the\n * operations chokepoint and the shared `useOperationQuery` cache (AD-17).\n */\nclass ConvexDeliveryRepository implements DeliveryRepository {\n constructor(private readonly client: ConvexHttpClient) {}\n\n async getConfig(): Promise<DeliveryConfigStatus> {\n return (await this.client.query(\n queryRef('delivery:deliveryConfig'),\n {},\n )) as DeliveryConfigStatus\n }\n}\n\n/**\n * Convex-backed {@link PersistenceAdapter} (brief §10, EC-039). Every repository\n * is implemented against the Convex functions in `apps/builder/convex/*`, mapping\n * results and failures onto the provider-neutral contracts + error taxonomies.\n */\nexport class ConvexPersistenceAdapter implements PersistenceAdapter {\n readonly cms: CmsRepository\n readonly assets: AssetRepository\n /** Reference index — outbound/inbound entries per variant (EC-155, AD-6). */\n readonly references: ReferenceIndexRepository\n /** Per-project membership roles (EC-150). */\n readonly members: MembersRepository\n readonly invites: InviteRepository\n /** Studio users rail — role + custom fields joined to auth identity (EC-240). */\n readonly users: UserRepository\n /** Editorial redirects rail — data-backed 301/302 forwards (EC-303). */\n readonly redirects: RedirectRepository\n /** Revocable CLI sync tokens, stored hashed (EC-150). */\n readonly cliTokens: CliTokenRepository\n /** Convex file storage behind the EC-133 blob boundary (EC-151). */\n readonly blobs: BlobStorageAdapter\n /** Delivery-pipeline (instant-publish) health, read-only (EC-268/EC-329). */\n readonly delivery: DeliveryRepository\n /**\n * EC-277: the Convex mutations maintain the reference index in-transaction\n * (`convex/referenceMaintenance.ts`), so every writer — studio, CLI, webhook,\n * raw `convex run` — is caught at the source. The studio bootstrap therefore does\n * NOT attach the client-side `attachReferenceIndex` subscriber for this provider.\n */\n readonly maintainsReferenceIndex = true\n\n /**\n * @param client the Convex client bound to this deployment (one project).\n * @param projectId the slug of this deployment's one project (EC-179). Used to\n * stamp deployment-scoped member records onto the still-project-keyed contract.\n * @param collections the active project's config-owned CMS collections (EC-183,\n * AD-11), from `elytra.config.ts`. The backend has no `cmsSchemas` table, so\n * the CMS repository serves the schema from these.\n */\n constructor(\n private readonly client: ConvexHttpClient,\n projectId: string,\n collections: readonly CollectionDef[] = [],\n ) {\n this.cms = new ConvexCmsRepository(client, collections)\n this.assets = new ConvexAssetRepository(client)\n this.references = new ConvexReferenceIndexRepository(client)\n this.members = new ConvexMembersRepository(client, projectId)\n this.users = new ConvexUserRepository(client)\n this.invites = new ConvexInviteRepository(client)\n this.redirects = new ConvexRedirectRepository(client)\n this.cliTokens = new ConvexCliTokenRepository(client)\n this.blobs = new ConvexBlobStorage(client)\n this.delivery = new ConvexDeliveryRepository(client)\n }\n\n async status(): Promise<BackendStatus> {\n try {\n // Lightweight connectivity probe (EC-182: no `projects` table). `members:mine`\n // takes no args and is soft-guarded (EC-279): it answers `null` for an\n // unauthenticated caller rather than throwing, so the pre-sign-in boot probe\n // resolves cleanly — backend answered ⇒ `available: true` — with no scary\n // `Uncaught Error` in the Convex log.\n await this.client.query(queryRef('members:mine'), {})\n return { backend: 'convex', available: true }\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error)\n // EC-153: an `unauthorized` rejection *is* a healthy backend — the\n // deployment answered and enforced auth. Now that `members:mine` is\n // soft-guarded this branch is a defensive fallback (a protected probe could\n // still throw); only transport/server failures count as unavailable, so the\n // DeploymentSetupGate surfaces those visibly instead of falling back to in-memory.\n if (/unauthorized/i.test(detail)) return { backend: 'convex', available: true }\n return { backend: 'convex', available: false, detail }\n }\n }\n\n validate(input: BackendValidationInput): BackendValidationResult {\n return validateForPersistence(input)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"convex-adapter.js","sourceRoot":"","sources":["../../../src/lib/persistence/convex-adapter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ConvexHttpClient } from 'convex/browser'\nimport { documentKey, documentSchema } from '@elytracms/core/cms-core'\nimport type { CmsDocument, CollectionDef, DocumentRef } from '@elytracms/core/cms-core'\nimport {\n RecordNotFoundError,\n PersistenceValidationError,\n compareReferenceEntries,\n highestRole,\n isKnownRole,\n validateForPersistence,\n} from '@elytracms/core/persistence'\nimport type {\n AssetRecord,\n AssetRepository,\n BackendStatus,\n BackendValidationInput,\n BackendValidationResult,\n BlobStorageAdapter,\n CliTokenRecord,\n CliTokenRepository,\n CmsRepository,\n DeliveryConfigStatus,\n DeliveryRepository,\n CmsSchemaRecord,\n CmsDocumentRecord,\n DocumentVersionRecord,\n SaveDocumentVersionOptions,\n MembersRepository,\n NewAssetInput,\n NewCliTokenInput,\n PersistenceAdapter,\n OutboundReference,\n ProjectMemberRecord,\n ProjectRole,\n ReferenceEntry,\n ReferenceEntryRecord,\n ReferenceIndexBuildMarker,\n ReferenceIndexRepository,\n ReferenceLookupOptions,\n ReferenceSource,\n ReferenceSourceType,\n ReferenceTarget,\n ReferenceVariant,\n RedirectEntry,\n RedirectRepository,\n SaveRedirectInput,\n SaveUserInput,\n UserRecord,\n UserRepository,\n InviteRecord,\n InviteRepository,\n SaveInviteInput,\n} from '@elytracms/core/persistence'\nimport { normalizeInviteEmail } from '@elytracms/core/persistence'\nimport { queryRef, mutationRef } from './convex-client'\nimport { ConvexBlobStorage } from './convex-blob-storage'\n\n/** Wall-clock for runtime writes (deterministic seeds use a fixed clock instead). */\ntype Clock = () => string\nconst realClock: Clock = () => new Date().toISOString()\n\nfunction newId(prefix: string): string {\n const rand =\n typeof globalThis.crypto?.randomUUID === 'function'\n ? globalThis.crypto.randomUUID()\n : Math.abs(Date.parse(new Date().toISOString())).toString(36)\n return `${prefix}_${rand}`\n}\n\n// --- CMS, Assets ----------------------------------------------------------\n\n/**\n * Deterministic, config-owned schema timestamps (EC-183). CMS collections live\n * in `elytra.config.ts`, not the backend, so the synthesized `CmsSchemaRecord`\n * carries a stable placeholder rather than DB-row timestamps.\n */\nconst CONFIG_SCHEMA_TIMESTAMP = '2026-01-01T00:00:00.000Z'\n\ninterface DocumentDoc {\n projectId: string\n document: CmsDocument\n publishedVersion?: number\n /** EC-447: the first-publish instant (set-once on publish, cleared on unpublish). */\n publishedAt?: string\n createdAt: string\n updatedAt: string\n}\nfunction docRecordFromDoc(doc: DocumentDoc): CmsDocumentRecord {\n return {\n projectId: doc.projectId,\n document: doc.document,\n ...(doc.publishedVersion !== undefined ? { publishedVersion: doc.publishedVersion } : {}),\n // EC-447: carry the materialized publish instant onto the contract record so the\n // studio's $publishedAt column + its reconcile read the authoritative value.\n ...(doc.publishedAt !== undefined ? { publishedAt: doc.publishedAt } : {}),\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n }\n}\n\ninterface VersionDoc {\n collection: string\n docId: string\n version: number\n snapshot: CmsDocument\n label?: string\n origin?: string\n createdAt: string\n}\n/** The backend row carries no `projectId` (single-tenant); stamp it from the call. */\nfunction versionRecordFromDoc(doc: VersionDoc, projectId: string): DocumentVersionRecord {\n return {\n projectId,\n collection: doc.collection,\n docId: doc.docId,\n version: doc.version,\n snapshot: doc.snapshot,\n createdAt: doc.createdAt,\n ...(doc.label ? { label: doc.label } : {}),\n ...(doc.origin ? { origin: doc.origin } : {}),\n }\n}\n\nclass ConvexCmsRepository implements CmsRepository {\n /**\n * @param collections the active project's config-owned CMS collections\n * (EC-183, AD-11), injected from `elytra.config.ts`. The Convex backend has\n * no `cmsSchemas` table — `findSchema`/`getSchema` serve these instead.\n */\n constructor(\n private readonly client: ConvexHttpClient,\n private readonly collections: readonly CollectionDef[] = [],\n private readonly clock: Clock = realClock,\n ) {}\n\n async saveSchema(\n _projectId: string,\n _collections: readonly CollectionDef[],\n ): Promise<CmsSchemaRecord> {\n // Read-only (EC-183, AD-11): the CMS collection schema is owned by\n // `elytra.config.ts`; the backend stores documents only. A write here is a\n // visible, structured error rather than a silent no-op (the studio's CMS\n // schema surfaces are read-only; legitimate writers were removed in EC-183).\n throw new PersistenceValidationError(\n 'CMS collection schema is owned by elytra.config.ts and cannot be written to the backend.',\n [\n {\n code: 'schema-config-owned',\n message:\n 'Edit elytra.config.ts to change collections; the backend stores documents only.',\n path: [],\n domain: 'cms',\n },\n ],\n )\n }\n\n /** Config-owned schema (EC-183): the active project's collections from config. */\n private configSchema(projectId: string): CmsSchemaRecord {\n return {\n projectId,\n collections: [...this.collections],\n createdAt: CONFIG_SCHEMA_TIMESTAMP,\n updatedAt: CONFIG_SCHEMA_TIMESTAMP,\n }\n }\n\n async getSchema(projectId: string): Promise<CmsSchemaRecord> {\n return this.configSchema(projectId)\n }\n\n async findSchema(projectId: string): Promise<CmsSchemaRecord | undefined> {\n return this.configSchema(projectId)\n }\n\n async upsertDocument(projectId: string, document: CmsDocument): Promise<CmsDocumentRecord> {\n // The backend is a pure document store (EC-183, AD-11): collection-membership\n // validation lives in `@elytracms/core/cms-core` `validateDocument`, which the studio\n // runs pre-dispatch (cell edits, imports). We still parse the canonical\n // document shape so malformed blobs never reach storage.\n const parsedDoc = documentSchema.parse(document)\n const now = this.clock()\n const doc = (await this.client.mutation(mutationRef('cms:upsertDocument'), {\n collection: parsedDoc.collection,\n docId: parsedDoc.id,\n document: parsedDoc,\n createdAt: now,\n updatedAt: now,\n })) as DocumentDoc\n return docRecordFromDoc(doc)\n }\n\n async getDocument(projectId: string, ref: DocumentRef): Promise<CmsDocumentRecord> {\n const found = await this.findDocument(projectId, ref)\n if (!found)\n throw new RecordNotFoundError('cms-document', `${projectId}/${ref.collection}:${ref.id}`)\n return found\n }\n\n async findDocument(projectId: string, ref: DocumentRef): Promise<CmsDocumentRecord | undefined> {\n const doc = (await this.client.query(queryRef('cms:getDocument'), {\n collection: ref.collection,\n docId: ref.id,\n })) as DocumentDoc | null\n return doc ? docRecordFromDoc(doc) : undefined\n }\n\n async listDocuments(projectId: string, collection?: string): Promise<CmsDocumentRecord[]> {\n const docs = (await this.client.query(queryRef('cms:listDocuments'), {\n ...(collection ? { collection } : {}),\n })) as DocumentDoc[]\n // EC-153 parity: deterministic document-key ordering (Convex index order\n // within a project is insertion order, so we sort client-side).\n return docs\n .map(docRecordFromDoc)\n .sort((a, b) => (documentKey(a.document) < documentKey(b.document) ? -1 : 1))\n }\n\n async listPublishedDocuments(projectId: string, collection?: string): Promise<CmsDocument[]> {\n // EC-307: one bulk read for the published pool (collapses the per-document\n // `listVersions` fan-out). The handler resolves each pin to its snapshot.\n const snapshots = (await this.client.query(queryRef('cms:listPublishedDocuments'), {\n ...(collection ? { collection } : {}),\n })) as CmsDocument[]\n // Deterministic document-key order (parity with the in-memory adapter).\n return snapshots\n .map((doc) => documentSchema.parse(doc))\n .sort((a, b) => (documentKey(a) < documentKey(b) ? -1 : 1))\n }\n\n async deleteDocument(projectId: string, ref: DocumentRef): Promise<void> {\n // EC-153 parity: deleting a missing document is not-found, never a no-op.\n await this.getDocument(projectId, ref)\n await this.client.mutation(mutationRef('cms:deleteDocument'), {\n collection: ref.collection,\n docId: ref.id,\n })\n }\n\n async saveDocumentVersion(\n projectId: string,\n snapshot: CmsDocument,\n options: SaveDocumentVersionOptions = {},\n ): Promise<DocumentVersionRecord> {\n const parsed = documentSchema.parse(snapshot)\n const doc = (await this.client.mutation(mutationRef('cms:saveDocumentVersion'), {\n collection: parsed.collection,\n docId: parsed.id,\n snapshot: parsed,\n createdAt: this.clock(),\n ...(options.label ? { label: options.label } : {}),\n ...(options.origin ? { origin: options.origin } : {}),\n })) as VersionDoc\n return versionRecordFromDoc(doc, projectId)\n }\n\n async listDocumentVersions(\n projectId: string,\n ref: DocumentRef,\n ): Promise<DocumentVersionRecord[]> {\n const docs = (await this.client.query(queryRef('cms:listDocumentVersions'), {\n collection: ref.collection,\n docId: ref.id,\n })) as VersionDoc[]\n return docs.map((doc) => versionRecordFromDoc(doc, projectId))\n }\n\n async listAllDocumentVersions(projectId: string): Promise<DocumentVersionRecord[]> {\n const docs = (await this.client.query(\n queryRef('cms:listAllDocumentVersions'),\n {},\n )) as VersionDoc[]\n return docs.map((doc) => versionRecordFromDoc(doc, projectId))\n }\n\n async getDocumentVersion(\n projectId: string,\n ref: DocumentRef,\n version: number,\n ): Promise<DocumentVersionRecord> {\n const doc = (await this.client.query(queryRef('cms:getDocumentVersion'), {\n collection: ref.collection,\n docId: ref.id,\n version,\n })) as VersionDoc | null\n if (!doc) {\n throw new RecordNotFoundError(\n 'cms-document-version',\n `${projectId}/${ref.collection}:${ref.id}#${version}`,\n )\n }\n return versionRecordFromDoc(doc, projectId)\n }\n\n async publishDocumentVersion(\n projectId: string,\n ref: DocumentRef,\n version: number,\n ): Promise<CmsDocumentRecord> {\n // Taxonomy enforced adapter-side (the handler is lenient): the document and\n // the pinned version must both exist (each throws RecordNotFoundError).\n await this.getDocument(projectId, ref)\n await this.getDocumentVersion(projectId, ref, version)\n // EC-447: pass a fresh publish-action instant as the SET-ONCE candidate (the\n // handler writes it only when the row has no `publishedAt` yet). One `now` for\n // both stamps — this IS the publish moment.\n const now = this.clock()\n const doc = (await this.client.mutation(mutationRef('cms:publishDocumentVersion'), {\n collection: ref.collection,\n docId: ref.id,\n version,\n updatedAt: now,\n publishedAt: now,\n })) as DocumentDoc\n return docRecordFromDoc(doc)\n }\n\n async unpublishDocument(projectId: string, ref: DocumentRef): Promise<CmsDocumentRecord> {\n const existing = await this.getDocument(projectId, ref)\n if (existing.publishedVersion === undefined) {\n throw new PersistenceValidationError('Document is not currently published.', [\n {\n code: 'invalid-transition',\n message: 'Cannot unpublish: the document has no published version.',\n domain: 'cms',\n },\n ])\n }\n const doc = (await this.client.mutation(mutationRef('cms:unpublishDocument'), {\n collection: ref.collection,\n docId: ref.id,\n updatedAt: this.clock(),\n })) as DocumentDoc\n return docRecordFromDoc(doc)\n }\n}\n\ninterface AssetDoc {\n projectId: string\n assetId: string\n filename: string\n contentType: string\n byteSize: number\n width?: number\n height?: number\n alt?: string\n /** Editable display title (EC-152). */\n title?: string\n storageKey: string\n url?: string\n /** Normalized image focal point `{ x, y }` in 0–1 (EC-230). */\n focalPoint?: { x: number; y: number }\n /** Downscaled editorial preview variant (EC-400). */\n thumb?: { storageKey: string; url: string; width: number; height: number }\n /** Delivery LQIP blur-placeholder data URI (EC-420). */\n lqip?: string\n /** Schema-driven asset metadata bag (EC-301). */\n meta?: Record<string, unknown>\n createdAt: string\n updatedAt: string\n}\n\n/** Map a Convex asset doc onto the contract record (assetId → id, drop _id/_creationTime). */\nfunction assetRecordFromDoc(doc: AssetDoc): AssetRecord {\n return {\n id: doc.assetId,\n projectId: doc.projectId,\n filename: doc.filename,\n contentType: doc.contentType,\n byteSize: doc.byteSize,\n ...(doc.width !== undefined ? { width: doc.width } : {}),\n ...(doc.height !== undefined ? { height: doc.height } : {}),\n ...(doc.alt !== undefined ? { alt: doc.alt } : {}),\n ...(doc.title !== undefined ? { title: doc.title } : {}),\n ...(doc.url !== undefined ? { url: doc.url } : {}),\n ...(doc.focalPoint !== undefined ? { focalPoint: doc.focalPoint } : {}),\n ...(doc.thumb !== undefined ? { thumb: doc.thumb } : {}),\n ...(doc.lqip !== undefined ? { lqip: doc.lqip } : {}),\n ...(doc.meta !== undefined ? { meta: doc.meta } : {}),\n storageKey: doc.storageKey,\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n }\n}\n\nclass ConvexAssetRepository implements AssetRepository {\n constructor(\n private readonly client: ConvexHttpClient,\n private readonly clock: Clock = realClock,\n ) {}\n\n async upsertAsset(projectId: string, input: NewAssetInput): Promise<AssetRecord> {\n const now = this.clock()\n const id = input.id ?? newId('asset')\n const doc = (await this.client.mutation(mutationRef('assets:upsert'), {\n assetId: id,\n filename: input.filename,\n contentType: input.contentType,\n byteSize: input.byteSize,\n ...(input.width !== undefined ? { width: input.width } : {}),\n ...(input.height !== undefined ? { height: input.height } : {}),\n ...(input.alt !== undefined ? { alt: input.alt } : {}),\n // EC-153 parity: `title` (EC-152 metadata) must persist, not silently drop.\n ...(input.title !== undefined ? { title: input.title } : {}),\n ...(input.url !== undefined ? { url: input.url } : {}),\n ...(input.focalPoint !== undefined ? { focalPoint: input.focalPoint } : {}),\n ...(input.thumb !== undefined ? { thumb: input.thumb } : {}),\n ...(input.lqip !== undefined ? { lqip: input.lqip } : {}),\n ...(input.meta !== undefined ? { meta: input.meta } : {}),\n storageKey: input.storageKey ?? `${projectId}/${id}`,\n createdAt: now,\n updatedAt: now,\n })) as AssetDoc\n return assetRecordFromDoc(doc)\n }\n\n async getAsset(projectId: string, id: string): Promise<AssetRecord> {\n const found = await this.findAsset(projectId, id)\n if (!found) throw new RecordNotFoundError('asset', `${projectId}/${id}`)\n return found\n }\n\n async findAsset(projectId: string, id: string): Promise<AssetRecord | undefined> {\n const doc = (await this.client.query(queryRef('assets:get'), {\n assetId: id,\n })) as AssetDoc | null\n return doc ? assetRecordFromDoc(doc) : undefined\n }\n\n async listAssets(projectId: string): Promise<AssetRecord[]> {\n const docs = (await this.client.query(queryRef('assets:list'), {})) as AssetDoc[]\n return docs.map(assetRecordFromDoc)\n }\n\n async removeAsset(projectId: string, id: string): Promise<void> {\n // EC-153 parity: removing an unknown asset is not-found, never a no-op.\n await this.getAsset(projectId, id)\n await this.client.mutation(mutationRef('assets:remove'), { assetId: id })\n }\n\n async resolveMany(projectId: string, ids: readonly string[]): Promise<Map<string, AssetRecord>> {\n const all = await this.listAssets(projectId)\n const out = new Map<string, AssetRecord>()\n for (const asset of all) if (ids.includes(asset.id)) out.set(asset.id, asset)\n return out\n }\n}\n\n// --- Reference index (EC-155) ------------------------------------------------\n\n/** A Convex reference-entry doc (flattened source/target for index-backed lookups). */\ninterface ReferenceEntryDoc {\n projectId: string\n variant: ReferenceVariant\n sourceType: ReferenceSourceType\n sourceId: string\n path: string\n targetType: ReferenceTarget['type']\n targetId: string\n kind: ReferenceEntryRecord['kind']\n}\n\nfunction referenceRecordFromDoc(doc: ReferenceEntryDoc): ReferenceEntryRecord {\n return {\n projectId: doc.projectId,\n variant: doc.variant,\n source: { type: doc.sourceType, id: doc.sourceId },\n path: doc.path,\n target: { type: doc.targetType, id: doc.targetId },\n kind: doc.kind,\n }\n}\n\n/** Deterministic ordering is a repository contract; Convex docs sort client-side. */\nfunction sortedReferenceRecords(docs: ReferenceEntryDoc[]): ReferenceEntryRecord[] {\n return docs.map(referenceRecordFromDoc).sort(compareReferenceEntries)\n}\n\nclass ConvexReferenceIndexRepository implements ReferenceIndexRepository {\n constructor(private readonly client: ConvexHttpClient) {}\n\n async replaceForSource(\n projectId: string,\n variant: ReferenceVariant,\n source: ReferenceSource,\n references: readonly OutboundReference[],\n ): Promise<ReferenceEntryRecord[]> {\n await this.client.mutation(mutationRef('references:replaceForSource'), {\n variant,\n sourceType: source.type,\n sourceId: source.id,\n references: references.map((r) => ({\n path: r.path,\n targetType: r.target.type,\n targetId: r.target.id,\n kind: r.kind,\n })),\n })\n return this.outbound(projectId, source, { variant })\n }\n\n async replaceForSourceTypes(\n projectId: string,\n variant: ReferenceVariant,\n sourceTypes: readonly ReferenceSourceType[],\n entries: readonly ReferenceEntry[],\n ): Promise<ReferenceEntryRecord[]> {\n await this.client.mutation(mutationRef('references:replaceForSourceTypes'), {\n variant,\n sourceTypes: [...sourceTypes],\n entries: entries.map((e) => ({\n sourceType: e.source.type,\n sourceId: e.source.id,\n path: e.path,\n targetType: e.target.type,\n targetId: e.target.id,\n kind: e.kind,\n })),\n })\n const all = await this.listEntries(projectId, variant)\n const types = new Set(sourceTypes)\n return all.filter((record) => types.has(record.source.type))\n }\n\n async removeForSource(\n projectId: string,\n source: ReferenceSource,\n variant?: ReferenceVariant,\n ): Promise<void> {\n await this.client.mutation(mutationRef('references:removeForSource'), {\n sourceType: source.type,\n sourceId: source.id,\n ...(variant ? { variant } : {}),\n })\n }\n\n async outbound(\n projectId: string,\n source: ReferenceSource,\n options: ReferenceLookupOptions = {},\n ): Promise<ReferenceEntryRecord[]> {\n const docs = (await this.client.query(queryRef('references:outbound'), {\n sourceType: source.type,\n sourceId: source.id,\n ...(options.variant ? { variant: options.variant } : {}),\n })) as ReferenceEntryDoc[]\n return sortedReferenceRecords(docs)\n }\n\n async inbound(\n projectId: string,\n target: ReferenceTarget,\n options: ReferenceLookupOptions = {},\n ): Promise<ReferenceEntryRecord[]> {\n const docs = (await this.client.query(queryRef('references:inbound'), {\n targetType: target.type,\n targetId: target.id,\n ...(options.variant ? { variant: options.variant } : {}),\n })) as ReferenceEntryDoc[]\n return sortedReferenceRecords(docs)\n }\n\n async inboundMany(\n projectId: string,\n targets: readonly ReferenceTarget[],\n options: ReferenceLookupOptions = {},\n ): Promise<ReferenceEntryRecord[][]> {\n // EC-308: one round-trip for all targets (the handler loops the by_target\n // index server-side), replacing the client's per-target inbound fan-out.\n if (targets.length === 0) return []\n const groups = (await this.client.query(queryRef('references:inboundMany'), {\n targets: targets.map((target) => ({ targetType: target.type, targetId: target.id })),\n ...(options.variant ? { variant: options.variant } : {}),\n })) as ReferenceEntryDoc[][]\n return groups.map((docs) => sortedReferenceRecords(docs))\n }\n\n async listEntries(\n projectId: string,\n variant?: ReferenceVariant,\n ): Promise<ReferenceEntryRecord[]> {\n const docs = (await this.client.query(queryRef('references:list'), {\n ...(variant ? { variant } : {}),\n })) as ReferenceEntryDoc[]\n return sortedReferenceRecords(docs)\n }\n\n async getBuildMarker(projectId: string): Promise<ReferenceIndexBuildMarker | null> {\n return (await this.client.query(queryRef('references:buildMarker'), {})) as\n | ReferenceIndexBuildMarker\n | null\n }\n\n async setBuildMarker(projectId: string, marker: ReferenceIndexBuildMarker): Promise<void> {\n await this.client.mutation(mutationRef('references:setBuildMarker'), {\n entryCount: marker.entryCount,\n })\n }\n\n async clearProject(projectId: string): Promise<void> {\n await this.client.mutation(mutationRef('references:clearProject'), {})\n }\n}\n\n// --- Members + CLI tokens (EC-150) -------------------------------------------\n\ninterface MemberDoc {\n userId: string\n role: ProjectRole\n createdAt: string\n updatedAt: string\n}\n\n/**\n * Single-tenant (EC-179): membership is deployment-scoped, so the backend rows\n * carry no `projectId`. The contract record still does — this adapter is bound\n * to its deployment's project, so it stamps that slug onto every record.\n */\nfunction memberRecordFromDoc(doc: MemberDoc, projectId: string): ProjectMemberRecord {\n return {\n projectId,\n userId: doc.userId,\n role: doc.role,\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n }\n}\n\nclass ConvexMembersRepository implements MembersRepository {\n constructor(\n private readonly client: ConvexHttpClient,\n /** The slug of this deployment's one project — stamped onto records. */\n private readonly boundProjectId: string,\n private readonly clock: Clock = realClock,\n ) {}\n\n async setMember(\n projectId: string,\n userId: string,\n role: ProjectRole,\n ): Promise<ProjectMemberRecord> {\n const now = this.clock()\n const doc = (await this.client.mutation(mutationRef('members:set'), {\n userId,\n role,\n createdAt: now,\n updatedAt: now,\n })) as MemberDoc\n return memberRecordFromDoc(doc, projectId)\n }\n\n async findMember(projectId: string, userId: string): Promise<ProjectMemberRecord | undefined> {\n const doc = (await this.client.query(queryRef('members:get'), {\n userId,\n })) as MemberDoc | null\n return doc ? memberRecordFromDoc(doc, projectId) : undefined\n }\n\n async listMembers(projectId: string): Promise<ProjectMemberRecord[]> {\n const docs = (await this.client.query(queryRef('members:list'), {})) as MemberDoc[]\n return docs.map((d) => memberRecordFromDoc(d, projectId))\n }\n\n async listMembershipsForUser(userId: string): Promise<ProjectMemberRecord[]> {\n // `members:mine` is caller-scoped server-side and returns the caller's single\n // deployment membership (or null). The studio only ever asks about the\n // session user; records are stamped with this deployment's bound project.\n const doc = (await this.client.query(queryRef('members:mine'), {})) as MemberDoc | null\n return doc && doc.userId === userId ? [memberRecordFromDoc(doc, this.boundProjectId)] : []\n }\n\n async removeMember(projectId: string, userId: string): Promise<void> {\n try {\n await this.client.mutation(mutationRef('members:remove'), { userId })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n if (/not-found/i.test(message)) {\n throw new RecordNotFoundError('project-member', `${projectId}/${userId}`)\n }\n throw error\n }\n }\n}\n\n// --- Users rail (EC-240, 240a) -----------------------------------------------\n\n/** The wire shape returned by the `users:list`/`users:get` join handlers. */\ninterface UserDoc {\n id: string\n email: string\n name?: string\n avatar?: string\n role: ProjectRole\n meta?: Record<string, unknown>\n createdAt: string\n updatedAt: string\n}\n\n/** Map a joined user doc onto the contract record; stamp the bound project. */\nfunction userRecordFromDoc(doc: UserDoc, projectId: string): UserRecord {\n return {\n id: doc.id,\n projectId,\n email: doc.email,\n ...(doc.name !== undefined ? { name: doc.name } : {}),\n ...(doc.avatar !== undefined ? { avatar: doc.avatar } : {}),\n // 240a bridge: the single `members.role` column ⇒ the 240c-ready `roles[]`.\n roles: [doc.role],\n ...(doc.meta !== undefined ? { meta: doc.meta } : {}),\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n }\n}\n\n/**\n * Collapse the 240c-ready `roles[]` to the single `members.role` column by\n * applying the strongest role (the 240a→240c enforcement bridge). Unknown\n * strings are ignored; an empty/unknown set floors to `viewer`.\n */\nfunction strongestRole(roles: readonly string[]): ProjectRole {\n // Validate against the active role policy (EC-240c) rather than a hardcoded\n // enum, then take the canonical rank max; floor an empty/unknown set to `viewer`.\n const valid = roles.filter((r) => isKnownRole(r))\n return highestRole(valid) ?? 'viewer'\n}\n\n/**\n * Convex-backed {@link UserRepository} (EC-240, 240a). Reads are the admin-gated\n * `users:*` **join** handlers (members row + auth identity); the role write\n * reuses the member write path (`members:set`, extended with the `meta` bag) so\n * there is one source for role writes — 240c collapses enforcement onto it.\n */\nclass ConvexUserRepository implements UserRepository {\n constructor(\n private readonly client: ConvexHttpClient,\n private readonly clock: Clock = realClock,\n ) {}\n\n async listUsers(projectId: string): Promise<UserRecord[]> {\n const docs = (await this.client.query(queryRef('users:list'), {})) as UserDoc[]\n return docs.map((doc) => userRecordFromDoc(doc, projectId))\n }\n\n async findUser(projectId: string, id: string): Promise<UserRecord | undefined> {\n const doc = (await this.client.query(queryRef('users:get'), { userId: id })) as UserDoc | null\n return doc ? userRecordFromDoc(doc, projectId) : undefined\n }\n\n async getUser(projectId: string, id: string): Promise<UserRecord> {\n const found = await this.findUser(projectId, id)\n if (!found) throw new RecordNotFoundError('user', `${projectId}/${id}`)\n return found\n }\n\n async getSelf(projectId: string): Promise<UserRecord | null> {\n // Self-permitted (EC-240 H-tail item 1): `users:getSelf` derives the caller\n // from the JWT server-side and returns ONLY their own join — never widened to\n // the admin-only `users:get`. The `selfId` the contract passes is ignored here;\n // the trust boundary is the server.\n const doc = (await this.client.query(queryRef('users:getSelf'), {})) as UserDoc | null\n return doc ? userRecordFromDoc(doc, projectId) : null\n }\n\n async saveUser(projectId: string, input: SaveUserInput): Promise<UserRecord> {\n const now = this.clock()\n await this.client.mutation(mutationRef('members:set'), {\n userId: input.id,\n role: strongestRole(input.roles),\n ...(input.meta !== undefined ? { meta: input.meta } : {}),\n createdAt: now,\n updatedAt: now,\n })\n // Return the full joined projection (identity + role + custom fields).\n return this.getUser(projectId, input.id)\n }\n\n async removeUser(projectId: string, id: string): Promise<void> {\n try {\n await this.client.mutation(mutationRef('members:remove'), { userId: id })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n if (/not-found/i.test(message)) {\n throw new RecordNotFoundError('user', `${projectId}/${id}`)\n }\n throw error\n }\n }\n}\n\n// --- Invite allowlist rail (EC-324) ------------------------------------------\n\n/** The wire shape returned by the `invites:*` handlers. */\ninterface InviteDoc {\n email: string\n role: string\n invitedBy?: string\n createdAt: string\n updatedAt: string\n}\n\nfunction inviteRecordFromDoc(doc: InviteDoc): InviteRecord {\n return {\n email: doc.email,\n role: doc.role,\n ...(doc.invitedBy !== undefined ? { invitedBy: doc.invitedBy } : {}),\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n }\n}\n\n/**\n * Convex-backed {@link InviteRepository} (EC-324). Admin-gated `invites:*`\n * handlers; the repo supplies timestamps (mirroring `ConvexUserRepository`). The\n * Convex auth boundary reads the same table in `beforeSessionCreation` to gate\n * open sign-up + grant the invited role.\n */\nclass ConvexInviteRepository implements InviteRepository {\n constructor(\n private readonly client: ConvexHttpClient,\n private readonly clock: Clock = realClock,\n ) {}\n\n async listInvites(_projectId: string): Promise<InviteRecord[]> {\n const docs = (await this.client.query(queryRef('invites:list'), {})) as InviteDoc[]\n return docs.map(inviteRecordFromDoc)\n }\n\n async findInvite(projectId: string, email: string): Promise<InviteRecord | undefined> {\n const key = normalizeInviteEmail(email)\n const all = await this.listInvites(projectId)\n return all.find((invite) => invite.email === key)\n }\n\n async saveInvite(_projectId: string, input: SaveInviteInput): Promise<InviteRecord> {\n const now = this.clock()\n const doc = (await this.client.mutation(mutationRef('invites:create'), {\n email: normalizeInviteEmail(input.email),\n role: input.role,\n ...(input.invitedBy !== undefined ? { invitedBy: input.invitedBy } : {}),\n createdAt: now,\n updatedAt: now,\n })) as InviteDoc\n return inviteRecordFromDoc(doc)\n }\n\n async removeInvite(projectId: string, email: string): Promise<void> {\n try {\n await this.client.mutation(mutationRef('invites:revoke'), {\n email: normalizeInviteEmail(email),\n })\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n if (/not-found/i.test(message)) {\n throw new RecordNotFoundError('invite', `${projectId}/${normalizeInviteEmail(email)}`)\n }\n throw error\n }\n }\n}\n\ninterface CliTokenDoc {\n /** Retired (EC-182): optional only because live rows may still carry it until the migration clears it. */\n projectId?: string\n tokenId: string\n label: string\n tokenHash: string\n createdAt: string\n revokedAt?: string\n}\n\nfunction cliTokenRecordFromDoc(doc: CliTokenDoc): CliTokenRecord {\n return {\n id: doc.tokenId,\n label: doc.label,\n tokenHash: doc.tokenHash,\n createdAt: doc.createdAt,\n ...(doc.revokedAt ? { revokedAt: doc.revokedAt } : {}),\n }\n}\n\nclass ConvexCliTokenRepository implements CliTokenRepository {\n constructor(\n private readonly client: ConvexHttpClient,\n private readonly clock: Clock = realClock,\n ) {}\n\n async createToken(input: NewCliTokenInput): Promise<CliTokenRecord> {\n const doc = (await this.client.mutation(mutationRef('cliTokens:create'), {\n tokenId: newId('tok'),\n label: input.label,\n tokenHash: input.tokenHash,\n createdAt: this.clock(),\n })) as CliTokenDoc\n return cliTokenRecordFromDoc(doc)\n }\n\n async listTokens(): Promise<CliTokenRecord[]> {\n const docs = (await this.client.query(queryRef('cliTokens:list'), {})) as CliTokenDoc[]\n return docs.map(cliTokenRecordFromDoc)\n }\n\n async revokeToken(tokenId: string): Promise<CliTokenRecord> {\n try {\n const doc = (await this.client.mutation(mutationRef('cliTokens:revoke'), {\n tokenId,\n revokedAt: this.clock(),\n })) as CliTokenDoc\n return cliTokenRecordFromDoc(doc)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n if (/not-found/i.test(message)) {\n throw new RecordNotFoundError('cli-token', tokenId)\n }\n throw error\n }\n }\n\n async findByHash(tokenHash: string): Promise<CliTokenRecord | undefined> {\n const doc = (await this.client.query(queryRef('cliTokens:byHash'), {\n tokenHash,\n })) as CliTokenDoc | null\n return doc ? cliTokenRecordFromDoc(doc) : undefined\n }\n}\n\n/** The `redirects` table row shape (EC-303). Single-tenant: no `projectId` column. */\ninterface RedirectDoc {\n redirectId: string\n from: string\n to: string\n permanent: boolean\n enabled: boolean\n /** Schema-driven additive-field bag (EC-311 Tier 2). */\n meta?: Record<string, unknown>\n createdAt: string\n updatedAt: string\n}\n\nfunction redirectEntryFromDoc(doc: RedirectDoc, projectId: string): RedirectEntry {\n return {\n id: doc.redirectId,\n projectId,\n from: doc.from,\n to: doc.to,\n permanent: doc.permanent,\n enabled: doc.enabled,\n ...(doc.meta !== undefined ? { meta: doc.meta } : {}),\n createdAt: doc.createdAt,\n updatedAt: doc.updatedAt,\n }\n}\n\n/**\n * Convex-backed {@link RedirectRepository} (EC-303) — editorial redirects as data\n * (not config). Content-gated CRUD over the `redirects` table; the studio's\n * editorial surface + the delivery snapshot both read through here. The editorial\n * surface sends a full record on save, so create-defaults (permanent/enabled) also\n * cover edits.\n */\nclass ConvexRedirectRepository implements RedirectRepository {\n constructor(\n private readonly client: ConvexHttpClient,\n private readonly clock: Clock = realClock,\n ) {}\n\n async saveRedirect(projectId: string, input: SaveRedirectInput): Promise<RedirectEntry> {\n const now = this.clock()\n const id = input.id ?? newId('redirect')\n const doc = (await this.client.mutation(mutationRef('redirects:save'), {\n redirectId: id,\n from: input.from,\n to: input.to,\n permanent: input.permanent ?? true,\n enabled: input.enabled ?? true,\n ...(input.meta !== undefined ? { meta: input.meta } : {}),\n createdAt: now,\n updatedAt: now,\n })) as RedirectDoc\n return redirectEntryFromDoc(doc, projectId)\n }\n\n async getRedirect(projectId: string, id: string): Promise<RedirectEntry> {\n const found = await this.findRedirect(projectId, id)\n if (!found) throw new RecordNotFoundError('redirect', `${projectId}/${id}`)\n return found\n }\n\n async findRedirect(projectId: string, id: string): Promise<RedirectEntry | undefined> {\n const doc = (await this.client.query(queryRef('redirects:get'), {\n redirectId: id,\n })) as RedirectDoc | null\n return doc ? redirectEntryFromDoc(doc, projectId) : undefined\n }\n\n async listRedirects(projectId: string): Promise<RedirectEntry[]> {\n const docs = (await this.client.query(queryRef('redirects:list'), {})) as RedirectDoc[]\n return docs\n .map((doc) => redirectEntryFromDoc(doc, projectId))\n .sort((a, b) => (a.from < b.from ? -1 : a.from > b.from ? 1 : 0))\n }\n\n async removeRedirect(projectId: string, id: string): Promise<void> {\n await this.getRedirect(projectId, id) // not-found parity with the in-memory rail\n await this.client.mutation(mutationRef('redirects:remove'), { redirectId: id })\n }\n}\n\n/**\n * Convex-backed {@link DeliveryRepository} (EC-268/EC-329) — reads the\n * deployment's actual instant-publish env var status via the guarded\n * `delivery:deliveryConfig` query. Replaces the studio's former direct\n * `getConvexClient(...).query('delivery:deliveryConfig')` call in\n * `delivery-health-banner.tsx`, the only place that used to bypass both the\n * operations chokepoint and the shared `useOperationQuery` cache (AD-17).\n */\nclass ConvexDeliveryRepository implements DeliveryRepository {\n constructor(private readonly client: ConvexHttpClient) {}\n\n async getConfig(): Promise<DeliveryConfigStatus> {\n return (await this.client.query(\n queryRef('delivery:deliveryConfig'),\n {},\n )) as DeliveryConfigStatus\n }\n}\n\n/**\n * Convex-backed {@link PersistenceAdapter} (brief §10, EC-039). Every repository\n * is implemented against the Convex functions in `apps/builder/convex/*`, mapping\n * results and failures onto the provider-neutral contracts + error taxonomies.\n */\nexport class ConvexPersistenceAdapter implements PersistenceAdapter {\n readonly cms: CmsRepository\n readonly assets: AssetRepository\n /** Reference index — outbound/inbound entries per variant (EC-155, AD-6). */\n readonly references: ReferenceIndexRepository\n /** Per-project membership roles (EC-150). */\n readonly members: MembersRepository\n readonly invites: InviteRepository\n /** Studio users rail — role + custom fields joined to auth identity (EC-240). */\n readonly users: UserRepository\n /** Editorial redirects rail — data-backed 301/302 forwards (EC-303). */\n readonly redirects: RedirectRepository\n /** Revocable CLI sync tokens, stored hashed (EC-150). */\n readonly cliTokens: CliTokenRepository\n /** Convex file storage behind the EC-133 blob boundary (EC-151). */\n readonly blobs: BlobStorageAdapter\n /** Delivery-pipeline (instant-publish) health, read-only (EC-268/EC-329). */\n readonly delivery: DeliveryRepository\n /**\n * EC-277: the Convex mutations maintain the reference index in-transaction\n * (`convex/referenceMaintenance.ts`), so every writer — studio, CLI, webhook,\n * raw `convex run` — is caught at the source. The studio bootstrap therefore does\n * NOT attach the client-side `attachReferenceIndex` subscriber for this provider.\n */\n readonly maintainsReferenceIndex = true\n\n /**\n * @param client the Convex client bound to this deployment (one project).\n * @param projectId the slug of this deployment's one project (EC-179). Used to\n * stamp deployment-scoped member records onto the still-project-keyed contract.\n * @param collections the active project's config-owned CMS collections (EC-183,\n * AD-11), from `elytra.config.ts`. The backend has no `cmsSchemas` table, so\n * the CMS repository serves the schema from these.\n */\n constructor(\n private readonly client: ConvexHttpClient,\n projectId: string,\n collections: readonly CollectionDef[] = [],\n ) {\n this.cms = new ConvexCmsRepository(client, collections)\n this.assets = new ConvexAssetRepository(client)\n this.references = new ConvexReferenceIndexRepository(client)\n this.members = new ConvexMembersRepository(client, projectId)\n this.users = new ConvexUserRepository(client)\n this.invites = new ConvexInviteRepository(client)\n this.redirects = new ConvexRedirectRepository(client)\n this.cliTokens = new ConvexCliTokenRepository(client)\n this.blobs = new ConvexBlobStorage(client)\n this.delivery = new ConvexDeliveryRepository(client)\n }\n\n async status(): Promise<BackendStatus> {\n try {\n // Lightweight connectivity probe (EC-182: no `projects` table). `members:mine`\n // takes no args and is soft-guarded (EC-279): it answers `null` for an\n // unauthenticated caller rather than throwing, so the pre-sign-in boot probe\n // resolves cleanly — backend answered ⇒ `available: true` — with no scary\n // `Uncaught Error` in the Convex log.\n await this.client.query(queryRef('members:mine'), {})\n return { backend: 'convex', available: true }\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error)\n // EC-153: an `unauthorized` rejection *is* a healthy backend — the\n // deployment answered and enforced auth. Now that `members:mine` is\n // soft-guarded this branch is a defensive fallback (a protected probe could\n // still throw); only transport/server failures count as unavailable, so the\n // DeploymentSetupGate surfaces those visibly instead of falling back to in-memory.\n if (/unauthorized/i.test(detail)) return { backend: 'convex', available: true }\n return { backend: 'convex', available: false, detail }\n }\n }\n\n validate(input: BackendValidationInput): BackendValidationResult {\n return validateForPersistence(input)\n }\n}\n"]}
|
package/dist/ui/status-dot.d.ts
CHANGED
|
@@ -22,6 +22,15 @@ export interface StatusDotProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'c
|
|
|
22
22
|
* keyboard user (no mouse) can still read it.
|
|
23
23
|
*/
|
|
24
24
|
label: string;
|
|
25
|
+
/**
|
|
26
|
+
* Whether the dot is a keyboard tab stop (EC-448). **Default `true`** preserves the
|
|
27
|
+
* EC-446 focusable behavior (the versions pane — a few rows — so a keyboard user can
|
|
28
|
+
* focus a dot to reveal its pill). Pass `false` for a DENSE surface (the list
|
|
29
|
+
* `$state` column, EC-448): a long list must not add a tab stop per row. The label
|
|
30
|
+
* stays reachable via `aria-label` + native `title` (and the hover pill on mouse) —
|
|
31
|
+
* just not via focus. The focus-ring styling is likewise dropped when non-interactive.
|
|
32
|
+
*/
|
|
33
|
+
interactive?: boolean;
|
|
25
34
|
}
|
|
26
35
|
/**
|
|
27
36
|
* EC-446 — a compact, reusable status indicator: a small colored dot whose
|
|
@@ -30,10 +39,12 @@ export interface StatusDotProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'c
|
|
|
30
39
|
* list `$state` column). Pure-presentational and domain-agnostic: it knows a
|
|
31
40
|
* `tone` and a `label`, nothing about versions or documents.
|
|
32
41
|
*
|
|
33
|
-
* Accessibility: the wrapper is
|
|
34
|
-
*
|
|
35
|
-
* (
|
|
36
|
-
* the meaning is reachable without a mouse
|
|
37
|
-
*
|
|
42
|
+
* Accessibility: the wrapper is `role="img"` and carries the label as both
|
|
43
|
+
* `aria-label` (screen readers) and native `title` (mouse). When `interactive`
|
|
44
|
+
* (the default), it is also focusable (`tabIndex=0`) and a CSS pill reveals the
|
|
45
|
+
* label on `:focus-visible`, so the meaning is reachable without a mouse; a
|
|
46
|
+
* non-interactive dot (EC-448, dense lists) drops the tab stop + focus ring but
|
|
47
|
+
* keeps the name + hover pill. The dot glyph and the pill are `aria-hidden` — the
|
|
48
|
+
* wrapper's `aria-label` is the single announced name.
|
|
38
49
|
*/
|
|
39
|
-
export declare function StatusDot({ tone, label, className, ...props }: StatusDotProps): import("react").JSX.Element;
|
|
50
|
+
export declare function StatusDot({ tone, label, className, interactive, ...props }: StatusDotProps): import("react").JSX.Element;
|
package/dist/ui/status-dot.js
CHANGED
|
@@ -24,13 +24,15 @@ export function statusDotToneClass(tone) {
|
|
|
24
24
|
* list `$state` column). Pure-presentational and domain-agnostic: it knows a
|
|
25
25
|
* `tone` and a `label`, nothing about versions or documents.
|
|
26
26
|
*
|
|
27
|
-
* Accessibility: the wrapper is
|
|
28
|
-
*
|
|
29
|
-
* (
|
|
30
|
-
* the meaning is reachable without a mouse
|
|
31
|
-
*
|
|
27
|
+
* Accessibility: the wrapper is `role="img"` and carries the label as both
|
|
28
|
+
* `aria-label` (screen readers) and native `title` (mouse). When `interactive`
|
|
29
|
+
* (the default), it is also focusable (`tabIndex=0`) and a CSS pill reveals the
|
|
30
|
+
* label on `:focus-visible`, so the meaning is reachable without a mouse; a
|
|
31
|
+
* non-interactive dot (EC-448, dense lists) drops the tab stop + focus ring but
|
|
32
|
+
* keeps the name + hover pill. The dot glyph and the pill are `aria-hidden` — the
|
|
33
|
+
* wrapper's `aria-label` is the single announced name.
|
|
32
34
|
*/
|
|
33
|
-
export function StatusDot({ tone, label, className, ...props }) {
|
|
34
|
-
return (_jsxs("span", { role: "img", tabIndex: 0, "aria-label": label, title: label, className: cn('group/status-dot relative inline-flex items-center justify-center rounded-full outline-none', 'focus-visible:ring-2 focus-visible:ring-ring/60', className), ...props, children: [_jsx("span", { "aria-hidden": true, className: cn('size-2 shrink-0 rounded-full', statusDotToneClass(tone)) }), _jsx("span", { "aria-hidden": true, className: cn('pointer-events-none absolute left-1/2 top-full z-50 mt-1.5 -translate-x-1/2 whitespace-nowrap', 'rounded-md border border-border bg-popover px-2 py-1 text-xs font-medium text-popover-foreground shadow-md', 'opacity-0 transition-opacity duration-100', 'group-hover/status-dot:opacity-100 group-focus-visible/status-dot:opacity-100'), children: label })] }));
|
|
35
|
+
export function StatusDot({ tone, label, className, interactive = true, ...props }) {
|
|
36
|
+
return (_jsxs("span", { role: "img", ...(interactive ? { tabIndex: 0 } : {}), "aria-label": label, title: label, className: cn('group/status-dot relative inline-flex items-center justify-center rounded-full outline-none', interactive && 'focus-visible:ring-2 focus-visible:ring-ring/60', className), ...props, children: [_jsx("span", { "aria-hidden": true, className: cn('size-2 shrink-0 rounded-full', statusDotToneClass(tone)) }), _jsx("span", { "aria-hidden": true, className: cn('pointer-events-none absolute left-1/2 top-full z-50 mt-1.5 -translate-x-1/2 whitespace-nowrap', 'rounded-md border border-border bg-popover px-2 py-1 text-xs font-medium text-popover-foreground shadow-md', 'opacity-0 transition-opacity duration-100', 'group-hover/status-dot:opacity-100 group-focus-visible/status-dot:opacity-100'), children: label })] }));
|
|
35
37
|
}
|
|
36
38
|
//# sourceMappingURL=status-dot.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status-dot.js","sourceRoot":"","sources":["../../src/ui/status-dot.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAWjC;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAgB;IACjD,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,SAAS;YACZ,OAAO,YAAY,CAAA;QACrB,KAAK,SAAS;YACZ,OAAO,YAAY,CAAA;QACrB,KAAK,OAAO;YACV,OAAO,wBAAwB,CAAA;QACjC,KAAK,SAAS;YACZ,OAAO,qBAAqB,CAAA;IAChC,CAAC;AACH,CAAC;
|
|
1
|
+
{"version":3,"file":"status-dot.js","sourceRoot":"","sources":["../../src/ui/status-dot.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAWjC;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAgB;IACjD,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,SAAS;YACZ,OAAO,YAAY,CAAA;QACrB,KAAK,SAAS;YACZ,OAAO,YAAY,CAAA;QACrB,KAAK,OAAO;YACV,OAAO,wBAAwB,CAAA;QACjC,KAAK,SAAS;YACZ,OAAO,qBAAqB,CAAA;IAChC,CAAC;AACH,CAAC;AAsBD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,GAAG,IAAI,EAAE,GAAG,KAAK,EAAkB;IAChG,OAAO,CACL,gBACE,IAAI,EAAC,KAAK,KACN,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,gBAC5B,KAAK,EACjB,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,EAAE,CACX,6FAA6F,EAC7F,WAAW,IAAI,iDAAiD,EAChE,SAAS,CACV,KACG,KAAK,aAET,oCAAkB,SAAS,EAAE,EAAE,CAAC,8BAA8B,EAAE,kBAAkB,CAAC,IAAI,CAAC,CAAC,GAAI,EAC7F,oCAEE,SAAS,EAAE,EAAE,CACX,+FAA+F,EAC/F,4GAA4G,EAC5G,2CAA2C,EAC3C,+EAA+E,CAChF,YAEA,KAAK,GACD,IACF,CACR,CAAA;AACH,CAAC","sourcesContent":["import type { HTMLAttributes } from 'react'\nimport { cn } from '../lib/utils'\n\n/**\n * The status vocabulary a {@link StatusDot} can express. Kept deliberately\n * generic (not domain-specific) so the same primitive marks a published version\n * (`success`), a draft/pending state (`warning`), or a quiet/absent state\n * (`muted`/`neutral`) — the versions pane (EC-446) is its first consumer, the\n * list `$state` column (EC-445) its next.\n */\nexport type StatusTone = 'success' | 'warning' | 'muted' | 'neutral'\n\n/**\n * The dot's fill class per tone — a pure mapping onto the shared token palette\n * (the same `--success`/`--warning`/`--muted-foreground` tokens {@link Badge}\n * uses), extracted so it is unit-testable without a DOM.\n */\nexport function statusDotToneClass(tone: StatusTone): string {\n switch (tone) {\n case 'success':\n return 'bg-success'\n case 'warning':\n return 'bg-warning'\n case 'muted':\n return 'bg-muted-foreground/40'\n case 'neutral':\n return 'bg-muted-foreground'\n }\n}\n\nexport interface StatusDotProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'children' | 'aria-label'> {\n /** Which status the dot expresses — maps onto the shared token palette. */\n tone: StatusTone\n /**\n * The accessible + hover/focus-revealed text (e.g. \"Published\"). Exposed as the\n * `aria-label` and native `title`, and shown as a small pill on hover/focus so a\n * keyboard user (no mouse) can still read it.\n */\n label: string\n /**\n * Whether the dot is a keyboard tab stop (EC-448). **Default `true`** preserves the\n * EC-446 focusable behavior (the versions pane — a few rows — so a keyboard user can\n * focus a dot to reveal its pill). Pass `false` for a DENSE surface (the list\n * `$state` column, EC-448): a long list must not add a tab stop per row. The label\n * stays reachable via `aria-label` + native `title` (and the hover pill on mouse) —\n * just not via focus. The focus-ring styling is likewise dropped when non-interactive.\n */\n interactive?: boolean\n}\n\n/**\n * EC-446 — a compact, reusable status indicator: a small colored dot whose\n * meaning is revealed on hover/focus rather than spent as a full-width pill. The\n * primary space win over {@link Badge} for dense rows (the versions history, the\n * list `$state` column). Pure-presentational and domain-agnostic: it knows a\n * `tone` and a `label`, nothing about versions or documents.\n *\n * Accessibility: the wrapper is `role=\"img\"` and carries the label as both\n * `aria-label` (screen readers) and native `title` (mouse). When `interactive`\n * (the default), it is also focusable (`tabIndex=0`) and a CSS pill reveals the\n * label on `:focus-visible`, so the meaning is reachable without a mouse; a\n * non-interactive dot (EC-448, dense lists) drops the tab stop + focus ring but\n * keeps the name + hover pill. The dot glyph and the pill are `aria-hidden` — the\n * wrapper's `aria-label` is the single announced name.\n */\nexport function StatusDot({ tone, label, className, interactive = true, ...props }: StatusDotProps) {\n return (\n <span\n role=\"img\"\n {...(interactive ? { tabIndex: 0 } : {})}\n aria-label={label}\n title={label}\n className={cn(\n 'group/status-dot relative inline-flex items-center justify-center rounded-full outline-none',\n interactive && 'focus-visible:ring-2 focus-visible:ring-ring/60',\n className,\n )}\n {...props}\n >\n <span aria-hidden className={cn('size-2 shrink-0 rounded-full', statusDotToneClass(tone))} />\n <span\n aria-hidden\n className={cn(\n 'pointer-events-none absolute left-1/2 top-full z-50 mt-1.5 -translate-x-1/2 whitespace-nowrap',\n 'rounded-md border border-border bg-popover px-2 py-1 text-xs font-medium text-popover-foreground shadow-md',\n 'opacity-0 transition-opacity duration-100',\n 'group-hover/status-dot:opacity-100 group-focus-visible/status-dot:opacity-100',\n )}\n >\n {label}\n </span>\n </span>\n )\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elytracms/studio",
|
|
3
3
|
"author": "finaldream <hi@finaldream.de>",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.75",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "The Elytra studio engine (UI + lib + routes + data-only Convex schema) as an importable package. Extraction staged in EXTRACTION.md (EC-223); Stages 1-2 = UI primitives + router-free lib leaves.",
|
|
7
7
|
"license": "MIT",
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
"lucide-react": "^1.23.0",
|
|
151
151
|
"tailwind-merge": "^3.6.0",
|
|
152
152
|
"zod": "^4.4.3",
|
|
153
|
-
"@elytracms/core": "^0.0.
|
|
153
|
+
"@elytracms/core": "^0.0.75"
|
|
154
154
|
},
|
|
155
155
|
"peerDependencies": {
|
|
156
156
|
"@tanstack/react-router": "^1.170.0",
|