@elytracms/studio 0.0.85 → 0.0.86
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/delivery.ts +21 -0
- package/convex/engine-version.test.ts +31 -0
- package/dist/components/ai/ai-chat-panel.js +13 -2
- package/dist/components/ai/ai-chat-panel.js.map +1 -1
- package/dist/components/builder/blocks-model.d.ts +5 -4
- package/dist/components/builder/blocks-model.js +5 -4
- package/dist/components/builder/blocks-model.js.map +1 -1
- package/dist/components/builder/clipboard-actions.d.ts +6 -0
- package/dist/components/builder/clipboard-actions.js +15 -1
- package/dist/components/builder/clipboard-actions.js.map +1 -1
- package/dist/components/builder/composition-field-editor.d.ts +13 -0
- package/dist/components/builder/composition-field-editor.js +19 -4
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/builder/insert-context.js +6 -4
- package/dist/components/builder/insert-context.js.map +1 -1
- package/dist/components/cms/cell-edit.d.ts +7 -0
- package/dist/components/cms/cell-edit.js +9 -1
- package/dist/components/cms/cell-edit.js.map +1 -1
- package/dist/components/cms/collection-table.js +11 -0
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/document-url-path.d.ts +7 -4
- package/dist/components/cms/document-url-path.js +15 -1
- package/dist/components/cms/document-url-path.js.map +1 -1
- package/dist/components/cms/field-control.js +4 -0
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/inline-field-errors.d.ts +36 -0
- package/dist/components/cms/inline-field-errors.js +13 -5
- package/dist/components/cms/inline-field-errors.js.map +1 -1
- package/dist/components/cms/rich-text/insert.d.ts +6 -3
- package/dist/components/cms/rich-text/insert.js +9 -6
- package/dist/components/cms/rich-text/insert.js.map +1 -1
- package/dist/components/cms/use-live-url.d.ts +18 -6
- package/dist/components/cms/use-live-url.js +10 -0
- package/dist/components/cms/use-live-url.js.map +1 -1
- package/dist/components/components/transfer.d.ts +6 -5
- package/dist/components/components/transfer.js +7 -6
- package/dist/components/components/transfer.js.map +1 -1
- package/dist/components/deployment-setup-gate.js +9 -3
- package/dist/components/deployment-setup-gate.js.map +1 -1
- package/dist/lib/ai/constraints.js +54 -71
- package/dist/lib/ai/constraints.js.map +1 -1
- package/dist/lib/ai/index.d.ts +1 -0
- package/dist/lib/ai/index.js +1 -0
- package/dist/lib/ai/index.js.map +1 -1
- package/dist/lib/ai/route-fetch.d.ts +58 -0
- package/dist/lib/ai/route-fetch.js +98 -0
- package/dist/lib/ai/route-fetch.js.map +1 -0
- package/dist/lib/ai/route-provider.d.ts +5 -22
- package/dist/lib/ai/route-provider.js +23 -1
- package/dist/lib/ai/route-provider.js.map +1 -1
- package/dist/lib/clipboard/paste-target.d.ts +23 -1
- package/dist/lib/clipboard/paste-target.js +12 -11
- package/dist/lib/clipboard/paste-target.js.map +1 -1
- package/dist/lib/cms/document-gate.d.ts +19 -9
- package/dist/lib/cms/document-gate.js +25 -4
- package/dist/lib/cms/document-gate.js.map +1 -1
- package/dist/lib/engine-version.d.ts +16 -0
- package/dist/lib/engine-version.js +17 -0
- package/dist/lib/engine-version.js.map +1 -0
- 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 +4 -0
- package/dist/lib/i18n/messages.en.js +7 -0
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/operations/studio-operations.js +4 -2
- package/dist/lib/operations/studio-operations.js.map +1 -1
- package/dist/lib/persistence/deployment-readiness.d.ts +8 -1
- package/dist/lib/persistence/deployment-readiness.js +4 -0
- package/dist/lib/persistence/deployment-readiness.js.map +1 -1
- package/dist/previews/manifest-format.d.ts +7 -7
- package/dist/previews/manifest-format.js +0 -21
- package/dist/previews/manifest-format.js.map +1 -1
- package/package.json +2 -2
|
@@ -20,8 +20,10 @@ export function createStudioOperations(adapter, options = {}) {
|
|
|
20
20
|
// bag — the same one the detail editor, bulk publish, cell edit, import and
|
|
21
21
|
// every AI path use — instead of deciding its own (weaker, mode-dependent)
|
|
22
22
|
// strength off the adapter-served schema record. The seam reads a validator the
|
|
23
|
-
// mounted session published; with no session it
|
|
24
|
-
//
|
|
23
|
+
// mounted session published; with no session it answers with ONE blocking issue
|
|
24
|
+
// (EC-546) — a publish issued outside a mounted CMS session is refused, never
|
|
25
|
+
// waved through on weaker rules. The validator is required by the operations
|
|
26
|
+
// layer, so this bundle cannot be built without it.
|
|
25
27
|
const operations = createOperations({
|
|
26
28
|
adapter,
|
|
27
29
|
validateDocument: sessionValidateDocument,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"studio-operations.js","sourceRoot":"","sources":["../../../src/lib/operations/studio-operations.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PersistenceAdapter } from '@elytracms/core/persistence'\nimport { activeDeploymentKey, selectPersistenceAdapter } from '../persistence'\nimport { attachSessionExpiryReporting } from '../session-expiry'\nimport { attachConfiguredRevalidationWebhooks } from '../webhooks'\nimport { sessionValidateDocument } from '../cms/document-gate'\nimport {\n attachReferenceIndex,\n createChangesets,\n createOperations,\n createRoleAuthorization,\n} from './operations-package'\nimport type {\n ChangesetOperations,\n OperationsClient,\n Principal,\n ReferenceIndexAttachment,\n} from './operations-package'\n\n/**\n * The studio's single operations bootstrap (EC-141, vision AD-7).\n *\n * One `OperationsClient` + one `ChangesetOperations` instance, built over the\n * selected persistence adapter, shared by every store and surface in the app.\n * The UI never talks to a repository for writes — every mutation is a typed\n * operation command (validate → authorize → mutate → event), so the studio\n * dogfoods the same seam plugins, AI changesets, and the CLI use.\n *\n * The EC-155 reference index is attached to the shared event bus here, so any\n * document/graph/publishing command issued anywhere in the studio keeps the\n * index maintained.\n */\nexport interface StudioOperations {\n readonly operations: OperationsClient\n readonly changesets: ChangesetOperations\n /**\n * The raw adapter, exposed for *transport and read paths only*: blob byte\n * upload/download (`adapter.blobs`) and existing read stores. Studio code\n * must not call repository write methods on it — writes go through\n * `operations`.\n */\n readonly adapter: PersistenceAdapter\n /** EC-155 maintained-on-write index attachment (flushable in tests). */\n readonly references: ReferenceIndexAttachment\n}\n\nexport interface CreateStudioOperationsOptions {\n /**\n * The signed-in principal (EC-150). When present, the EC-150 role hook\n * (`createRoleAuthorization` over `adapter.members`) becomes the\n * authorization chokepoint for every command and query — viewers get\n * structured denials on mutations, editors on administration. Without an\n * actor the client runs pass-through (seeds, fixtures, tests that exercise\n * domain logic rather than permissions); the route guard keeps\n * unauthenticated users away from every operation surface.\n */\n actor?: Principal\n}\n\n/**\n * A no-op {@link ReferenceIndexAttachment} for self-maintaining adapters (EC-277):\n * nothing is subscribed, so `detach` and `flush` are trivially satisfied (the index\n * is kept current in the adapter's write path, not by a queued subscriber).\n */\nfunction noopReferenceIndexAttachment(): ReferenceIndexAttachment {\n return { detach: () => {}, flush: async () => {} }\n}\n\n/** Build a studio operations bundle over an adapter (exported for tests). */\nexport function createStudioOperations(\n adapter: PersistenceAdapter,\n options: CreateStudioOperationsOptions = {},\n): StudioOperations {\n const authorized = options.actor\n ? { actor: options.actor, authorize: createRoleAuthorization(adapter.members) }\n : {}\n // EC-497: the publish gate validates with the CMS session's canonical option\n // bag — the same one the detail editor, bulk publish, cell edit, import and\n // every AI path use — instead of deciding its own (weaker, mode-dependent)\n // strength off the adapter-served schema record. The seam reads a validator the\n // mounted session published; with no session it returns `undefined` and the gate\n // takes its documented fallback.\n const operations = createOperations({\n adapter,\n validateDocument: sessionValidateDocument,\n ...authorized,\n })\n // EC-277: when the adapter maintains the index in its own write path (AD-6 \"at the\n // source\" — the Convex provider does this server-side, so a raw `convex run` is\n // caught too), the client-side subscriber would only double the work. Attach it\n // ONLY for providers that don't self-maintain. Where it does run, extraction is a\n // schema-free, manifest-free walk over self-describing content, so it needs no\n // registry (compositions self-typed their relation/asset props on write).\n const references = adapter.maintainsReferenceIndex\n ? noopReferenceIndexAttachment()\n : attachReferenceIndex(operations.events, adapter)\n const changesets = createChangesets({\n adapter,\n events: operations.events,\n // EC-497: a changeset-driven publish is gated identically — same seam, same bag.\n validateDocument: sessionValidateDocument,\n ...authorized,\n })\n // EC-146 instant publish: when a host revalidate endpoint is configured via\n // env, every successful publish-shaped command (document upsert/delete/\n // setState, graph publish/unpublish) fires a signed revalidation webhook.\n // No env (dev/tests) → no-op.\n attachConfiguredRevalidationWebhooks(operations.events)\n // EC-403: session-expiry detection rides the SAME lifecycle bus — every failed\n // command/query/changeset operation is checked against the session-expired\n // predicate here, so expiry raises the ONE app-level blocking re-auth state no\n // matter which surface issued the operation (per-surface banners can't cover\n // surfaces that render no banner).\n attachSessionExpiryReporting(operations.events)\n return { operations, changesets, adapter, references }\n}\n\n// One adapter + one operations bundle per backend (EC-179): keyed by the active\n// deployment so switching to a project on another Convex deployment selects (or\n// lazily builds) that backend's client, rather than reusing the first one.\nconst adaptersByKey = new Map<string, Promise<PersistenceAdapter>>()\nconst instancesByKey = new Map<string, Promise<StudioOperations>>()\nlet currentActor: Principal | null = null\n\n/**\n * The active backend key, tolerant of an unconfigured env: the real app never\n * reaches `getStudioOperations` while misconfigured (the `DeploymentSetupGate` renders a\n * boot error first), but tests inject a bundle directly via the seam below — so\n * a missing backend resolves to one stable key rather than throwing.\n */\nfunction deploymentKey(): string {\n try {\n return activeDeploymentKey()\n } catch {\n return '__unconfigured__'\n }\n}\n\nfunction getAdapter(key: string): Promise<PersistenceAdapter> {\n let adapter = adaptersByKey.get(key)\n if (!adapter) {\n adapter = selectPersistenceAdapter()\n adaptersByKey.set(key, adapter)\n }\n return adapter\n}\n\n/**\n * The shared studio operations client for the active backend (lazily\n * bootstrapped, cached per deployment). In-memory mode this owns the one seeded\n * adapter the whole session mutates; against Convex it owns one client per\n * deployment — the active project (EC-179) selects which.\n */\nexport function getStudioOperations(): Promise<StudioOperations> {\n const key = deploymentKey()\n let instance = instancesByKey.get(key)\n if (!instance) {\n const actor = currentActor\n instance = getAdapter(key).then((adapter) =>\n createStudioOperations(adapter, actor ? { actor } : {}),\n )\n instancesByKey.set(key, instance)\n }\n return instance\n}\n\n/**\n * Bind (or clear) the session principal (EC-150). Called by the auth context\n * on sign-in/sign-out. Rebuilds the operations clients over the *same* adapters\n * — the persisted state (and, in-memory, the whole session's edits) survives;\n * only the authorization identity changes. All cached backends are rebuilt so a\n * later switch picks up the new actor too.\n */\nexport function setStudioSession(actor: Principal | null): void {\n if (currentActor?.id === actor?.id) return // same user (or both null) — keep the clients\n currentActor = actor\n instancesByKey.clear()\n}\n\n/** Test seam: replace (or clear) the singleton for the active backend. */\nexport function setStudioOperationsForTests(next: StudioOperations | null): void {\n if (next) {\n instancesByKey.set(deploymentKey(), Promise.resolve(next))\n return\n }\n instancesByKey.clear()\n adaptersByKey.clear()\n currentActor = null\n}\n"]}
|
|
1
|
+
{"version":3,"file":"studio-operations.js","sourceRoot":"","sources":["../../../src/lib/operations/studio-operations.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PersistenceAdapter } from '@elytracms/core/persistence'\nimport { activeDeploymentKey, selectPersistenceAdapter } from '../persistence'\nimport { attachSessionExpiryReporting } from '../session-expiry'\nimport { attachConfiguredRevalidationWebhooks } from '../webhooks'\nimport { sessionValidateDocument } from '../cms/document-gate'\nimport {\n attachReferenceIndex,\n createChangesets,\n createOperations,\n createRoleAuthorization,\n} from './operations-package'\nimport type {\n ChangesetOperations,\n OperationsClient,\n Principal,\n ReferenceIndexAttachment,\n} from './operations-package'\n\n/**\n * The studio's single operations bootstrap (EC-141, vision AD-7).\n *\n * One `OperationsClient` + one `ChangesetOperations` instance, built over the\n * selected persistence adapter, shared by every store and surface in the app.\n * The UI never talks to a repository for writes — every mutation is a typed\n * operation command (validate → authorize → mutate → event), so the studio\n * dogfoods the same seam plugins, AI changesets, and the CLI use.\n *\n * The EC-155 reference index is attached to the shared event bus here, so any\n * document/graph/publishing command issued anywhere in the studio keeps the\n * index maintained.\n */\nexport interface StudioOperations {\n readonly operations: OperationsClient\n readonly changesets: ChangesetOperations\n /**\n * The raw adapter, exposed for *transport and read paths only*: blob byte\n * upload/download (`adapter.blobs`) and existing read stores. Studio code\n * must not call repository write methods on it — writes go through\n * `operations`.\n */\n readonly adapter: PersistenceAdapter\n /** EC-155 maintained-on-write index attachment (flushable in tests). */\n readonly references: ReferenceIndexAttachment\n}\n\nexport interface CreateStudioOperationsOptions {\n /**\n * The signed-in principal (EC-150). When present, the EC-150 role hook\n * (`createRoleAuthorization` over `adapter.members`) becomes the\n * authorization chokepoint for every command and query — viewers get\n * structured denials on mutations, editors on administration. Without an\n * actor the client runs pass-through (seeds, fixtures, tests that exercise\n * domain logic rather than permissions); the route guard keeps\n * unauthenticated users away from every operation surface.\n */\n actor?: Principal\n}\n\n/**\n * A no-op {@link ReferenceIndexAttachment} for self-maintaining adapters (EC-277):\n * nothing is subscribed, so `detach` and `flush` are trivially satisfied (the index\n * is kept current in the adapter's write path, not by a queued subscriber).\n */\nfunction noopReferenceIndexAttachment(): ReferenceIndexAttachment {\n return { detach: () => {}, flush: async () => {} }\n}\n\n/** Build a studio operations bundle over an adapter (exported for tests). */\nexport function createStudioOperations(\n adapter: PersistenceAdapter,\n options: CreateStudioOperationsOptions = {},\n): StudioOperations {\n const authorized = options.actor\n ? { actor: options.actor, authorize: createRoleAuthorization(adapter.members) }\n : {}\n // EC-497: the publish gate validates with the CMS session's canonical option\n // bag — the same one the detail editor, bulk publish, cell edit, import and\n // every AI path use — instead of deciding its own (weaker, mode-dependent)\n // strength off the adapter-served schema record. The seam reads a validator the\n // mounted session published; with no session it answers with ONE blocking issue\n // (EC-546) — a publish issued outside a mounted CMS session is refused, never\n // waved through on weaker rules. The validator is required by the operations\n // layer, so this bundle cannot be built without it.\n const operations = createOperations({\n adapter,\n validateDocument: sessionValidateDocument,\n ...authorized,\n })\n // EC-277: when the adapter maintains the index in its own write path (AD-6 \"at the\n // source\" — the Convex provider does this server-side, so a raw `convex run` is\n // caught too), the client-side subscriber would only double the work. Attach it\n // ONLY for providers that don't self-maintain. Where it does run, extraction is a\n // schema-free, manifest-free walk over self-describing content, so it needs no\n // registry (compositions self-typed their relation/asset props on write).\n const references = adapter.maintainsReferenceIndex\n ? noopReferenceIndexAttachment()\n : attachReferenceIndex(operations.events, adapter)\n const changesets = createChangesets({\n adapter,\n events: operations.events,\n // EC-497: a changeset-driven publish is gated identically — same seam, same bag.\n validateDocument: sessionValidateDocument,\n ...authorized,\n })\n // EC-146 instant publish: when a host revalidate endpoint is configured via\n // env, every successful publish-shaped command (document upsert/delete/\n // setState, graph publish/unpublish) fires a signed revalidation webhook.\n // No env (dev/tests) → no-op.\n attachConfiguredRevalidationWebhooks(operations.events)\n // EC-403: session-expiry detection rides the SAME lifecycle bus — every failed\n // command/query/changeset operation is checked against the session-expired\n // predicate here, so expiry raises the ONE app-level blocking re-auth state no\n // matter which surface issued the operation (per-surface banners can't cover\n // surfaces that render no banner).\n attachSessionExpiryReporting(operations.events)\n return { operations, changesets, adapter, references }\n}\n\n// One adapter + one operations bundle per backend (EC-179): keyed by the active\n// deployment so switching to a project on another Convex deployment selects (or\n// lazily builds) that backend's client, rather than reusing the first one.\nconst adaptersByKey = new Map<string, Promise<PersistenceAdapter>>()\nconst instancesByKey = new Map<string, Promise<StudioOperations>>()\nlet currentActor: Principal | null = null\n\n/**\n * The active backend key, tolerant of an unconfigured env: the real app never\n * reaches `getStudioOperations` while misconfigured (the `DeploymentSetupGate` renders a\n * boot error first), but tests inject a bundle directly via the seam below — so\n * a missing backend resolves to one stable key rather than throwing.\n */\nfunction deploymentKey(): string {\n try {\n return activeDeploymentKey()\n } catch {\n return '__unconfigured__'\n }\n}\n\nfunction getAdapter(key: string): Promise<PersistenceAdapter> {\n let adapter = adaptersByKey.get(key)\n if (!adapter) {\n adapter = selectPersistenceAdapter()\n adaptersByKey.set(key, adapter)\n }\n return adapter\n}\n\n/**\n * The shared studio operations client for the active backend (lazily\n * bootstrapped, cached per deployment). In-memory mode this owns the one seeded\n * adapter the whole session mutates; against Convex it owns one client per\n * deployment — the active project (EC-179) selects which.\n */\nexport function getStudioOperations(): Promise<StudioOperations> {\n const key = deploymentKey()\n let instance = instancesByKey.get(key)\n if (!instance) {\n const actor = currentActor\n instance = getAdapter(key).then((adapter) =>\n createStudioOperations(adapter, actor ? { actor } : {}),\n )\n instancesByKey.set(key, instance)\n }\n return instance\n}\n\n/**\n * Bind (or clear) the session principal (EC-150). Called by the auth context\n * on sign-in/sign-out. Rebuilds the operations clients over the *same* adapters\n * — the persisted state (and, in-memory, the whole session's edits) survives;\n * only the authorization identity changes. All cached backends are rebuilt so a\n * later switch picks up the new actor too.\n */\nexport function setStudioSession(actor: Principal | null): void {\n if (currentActor?.id === actor?.id) return // same user (or both null) — keep the clients\n currentActor = actor\n instancesByKey.clear()\n}\n\n/** Test seam: replace (or clear) the singleton for the active backend. */\nexport function setStudioOperationsForTests(next: StudioOperations | null): void {\n if (next) {\n instancesByKey.set(deploymentKey(), Promise.resolve(next))\n return\n }\n instancesByKey.clear()\n adaptersByKey.clear()\n currentActor = null\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DeclaredEnvVar, DeploymentReadinessFacts, DeploymentReadinessReport } from '@elytracms/core/export-sync/deployment-ready';
|
|
1
|
+
import type { DeclaredEnvVar, DeploymentReadinessFacts, DeploymentReadinessReport, FunctionsVersionProbe } from '@elytracms/core/export-sync/deployment-ready';
|
|
2
2
|
import type { CollectionDef } from '@elytracms/core/cms-core';
|
|
3
3
|
import type { StudioBackendConfig } from './backend-mode.js';
|
|
4
4
|
import type { WorkspaceState } from '../workspace/index.js';
|
|
@@ -29,6 +29,13 @@ export interface StudioReadinessInputs {
|
|
|
29
29
|
available: boolean;
|
|
30
30
|
detail?: string;
|
|
31
31
|
};
|
|
32
|
+
/**
|
|
33
|
+
* Deployed-functions version probe outcome (`probeDeployedFunctionsVersion`, EC-552);
|
|
34
|
+
* omitted when not probed (playground / unconfigured). The `expected` half of the
|
|
35
|
+
* handshake fact is supplied HERE — this build's own {@link ENGINE_VERSION} — so
|
|
36
|
+
* callers only ever hand over what they measured.
|
|
37
|
+
*/
|
|
38
|
+
functionsVersion?: FunctionsVersionProbe;
|
|
32
39
|
/**
|
|
33
40
|
* Whether `defineStudio({ ai })` is configured and enabled (EC-399) — drives the
|
|
34
41
|
* AI provider-key expectation in the deployment-env checks.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { checkDeploymentReady, expectedDeploymentEnv } from '@elytracms/core/export-sync/deployment-ready';
|
|
2
|
+
import { ENGINE_VERSION } from '../engine-version.js';
|
|
2
3
|
/** Map the studio's resolved state onto the core readiness facts (pure). */
|
|
3
4
|
export function studioReadinessFacts(inputs) {
|
|
4
5
|
const { backend, workspace, collections, reachable, effectiveUrl } = inputs;
|
|
@@ -39,6 +40,9 @@ export function studioReadinessFacts(inputs) {
|
|
|
39
40
|
return {
|
|
40
41
|
backend: backendFact,
|
|
41
42
|
...(reachable ? { reachable } : {}),
|
|
43
|
+
...(inputs.functionsVersion && backendFact.kind === 'convex'
|
|
44
|
+
? { functionsVersion: { probe: inputs.functionsVersion, expected: ENGINE_VERSION } }
|
|
45
|
+
: {}),
|
|
42
46
|
collections,
|
|
43
47
|
config,
|
|
44
48
|
...(deploymentEnv ? { deploymentEnv } : {}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployment-readiness.js","sourceRoot":"","sources":["../../../src/lib/persistence/deployment-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAA;
|
|
1
|
+
{"version":3,"file":"deployment-readiness.js","sourceRoot":"","sources":["../../../src/lib/persistence/deployment-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,8CAA8C,CAAA;AAQ1G,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AA4ClD,4EAA4E;AAC5E,MAAM,UAAU,oBAAoB,CAAC,MAA6B;IAChE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,MAAM,CAAA;IAE3E,oFAAoF;IACpF,qFAAqF;IACrF,iFAAiF;IACjF,qFAAqF;IACrF,sFAAsF;IACtF,sFAAsF;IACtF,+EAA+E;IAC/E,sFAAsF;IACtF,kFAAkF;IAClF,kCAAkC;IAClC,MAAM,MAAM,GACV,SAAS,CAAC,IAAI,KAAK,SAAS;QAC1B,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE;QACjF,CAAC,CAAC,SAAS,CAAC,IAAI,KAAK,QAAQ;YAC3B,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE;YAC3C,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;IAEvB,MAAM,WAAW,GACf,OAAO,CAAC,IAAI,KAAK,QAAQ;QACvB,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,YAAY,IAAI,OAAO,CAAC,GAAG,EAAE;QACtD,CAAC,CAAC,OAAO,CAAC,IAAI,KAAK,YAAY;YAC7B,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE;YACxB,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAA;IAExD,6EAA6E;IAC7E,6EAA6E;IAC7E,+EAA+E;IAC/E,8EAA8E;IAC9E,kFAAkF;IAClF,MAAM,aAAa,GACjB,WAAW,CAAC,IAAI,KAAK,QAAQ;QAC3B,CAAC,CAAC;YACE,QAAQ,EAAE,qBAAqB,CAAC;gBAC9B,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,IAAI,KAAK;gBACpD,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAChE,CAAC;YACF,iBAAiB,EAAE,kFAAkF;SACtG;QACH,CAAC,CAAC,SAAS,CAAA;IAEf,OAAO;QACL,OAAO,EAAE,WAAW;QACpB,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnC,GAAG,CAAC,MAAM,CAAC,gBAAgB,IAAI,WAAW,CAAC,IAAI,KAAK,QAAQ;YAC1D,CAAC,CAAC,EAAE,gBAAgB,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,gBAAgB,EAAE,QAAQ,EAAE,cAAc,EAAE,EAAE;YACpF,CAAC,CAAC,EAAE,CAAC;QACP,WAAW;QACX,MAAM;QACN,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC5C,CAAA;AACH,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,yBAAyB,CAAC,MAA6B;IACrE,OAAO,oBAAoB,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAA;AAC3D,CAAC","sourcesContent":["import { checkDeploymentReady, expectedDeploymentEnv } from '@elytracms/core/export-sync/deployment-ready'\nimport type {\n DeclaredEnvVar,\n DeploymentReadinessFacts,\n DeploymentReadinessReport,\n FunctionsVersionProbe,\n} from '@elytracms/core/export-sync/deployment-ready'\nimport type { CollectionDef } from '@elytracms/core/cms-core'\nimport { ENGINE_VERSION } from '../engine-version'\nimport type { StudioBackendConfig } from './backend-mode'\nimport type { WorkspaceState } from '../workspace'\n\n/**\n * Studio-side adapter for the shared `checkDeploymentReady` contract (EC-258).\n * Maps the studio's resolved state — backend mode (`resolveBackendConfig`),\n * workspace config (`getWorkspaceState`), config-owned collections\n * (`resolveDeploymentCollections`), and an optional reachability probe outcome —\n * onto the core-owned {@link DeploymentReadinessFacts}, then composes the report.\n *\n * A PURE mapper: the async I/O (the `adapter.status()` reachability probe) lives in\n * the boot `DeploymentSetupGate`, which passes the outcome in. `authKeys` is\n * deliberately left unprobed here — the deployment's JWKS endpoint is CORS-blocked\n * from the browser, so the auth-signing-keys check resolves to `unknown` at boot\n * (EC-257's honest sign-in error is the browser's actionable surface; the\n * `elytra doctor` CLI probes it definitively from node). `session` is left unset —\n * this gate runs before auth, so the per-user bucket stays `unknown` (never a false\n * fail) until a signed-in surface supplies it.\n */\nexport interface StudioReadinessInputs {\n backend: StudioBackendConfig\n /** The active project's effective deployment URL (`resolveDeploymentUrl`); defaults to `backend.url`. */\n effectiveUrl?: string\n workspace: WorkspaceState\n collections: readonly CollectionDef[]\n /** Reachability probe outcome (`adapter.status()`); omitted when not probed (playground / unconfigured). */\n reachable?: { available: boolean; detail?: string }\n /**\n * Deployed-functions version probe outcome (`probeDeployedFunctionsVersion`, EC-552);\n * omitted when not probed (playground / unconfigured). The `expected` half of the\n * handshake fact is supplied HERE — this build's own {@link ENGINE_VERSION} — so\n * callers only ever hand over what they measured.\n */\n functionsVersion?: FunctionsVersionProbe\n /**\n * Whether `defineStudio({ ai })` is configured and enabled (EC-399) — drives the\n * AI provider-key expectation in the deployment-env checks.\n */\n aiRouteConfigured?: boolean\n /** Consumer-declared deployment-env expectations (EC-399) — merged into the engine contract. */\n declaredEnv?: readonly DeclaredEnvVar[]\n}\n\n/** Map the studio's resolved state onto the core readiness facts (pure). */\nexport function studioReadinessFacts(inputs: StudioReadinessInputs): DeploymentReadinessFacts {\n const { backend, workspace, collections, reachable, effectiveUrl } = inputs\n\n // The `config` fact carries `issueCount` only on the INVALID branch — deliberately,\n // not by omission (EC-506, decided 2026-08-01). `elytra doctor` sets it on the valid\n // branch too (`cli/src/doctor.js` `configFactsFromOutcome`), which is what makes\n // `deployment-ready.ts`'s `config-valid` `warn` branch reachable there and not here.\n // The studio cannot supply it: `WorkspaceState`'s `config` variant carries no issues,\n // deliberately (see `lib/workspace/workspace-source.ts` for the full argument and the\n // resurface trigger). The divergence is LATENT, not live — the resolver has no\n // warning-severity producer, so on a valid config `issues.length` is 0 and both tools\n // render the same `ok`. When the first config warning exists, close this with the\n // rest of EC-506, not on its own.\n const config: DeploymentReadinessFacts['config'] =\n workspace.kind === 'invalid'\n ? { valid: false, source: workspace.source, issueCount: workspace.issues.length }\n : workspace.kind === 'config'\n ? { valid: true, source: workspace.source }\n : { valid: true }\n\n const backendFact: DeploymentReadinessFacts['backend'] =\n backend.kind === 'convex'\n ? { kind: 'convex', url: effectiveUrl ?? backend.url }\n : backend.kind === 'playground'\n ? { kind: 'playground' }\n : { kind: 'unconfigured', detail: backend.detail }\n\n // Expected-env facts (EC-399): the studio surfaces the SAME contract the CLI\n // checks, but the browser can read neither the Convex deployment env nor the\n // studio host's server env (the AI key is deliberately never VITE_-prefixed) —\n // so no presence source is gathered and every env check surfaces as an honest\n // `unknown` pointing at `elytra doctor`, exactly the auth-signing-keys precedent.\n const deploymentEnv: DeploymentReadinessFacts['deploymentEnv'] =\n backendFact.kind === 'convex'\n ? {\n expected: expectedDeploymentEnv({\n aiRouteConfigured: inputs.aiRouteConfigured ?? false,\n ...(inputs.declaredEnv ? { declared: inputs.declaredEnv } : {}),\n }),\n unavailableDetail: 'Not readable from the browser — run `elytra doctor` to check it (presence only).',\n }\n : undefined\n\n return {\n backend: backendFact,\n ...(reachable ? { reachable } : {}),\n ...(inputs.functionsVersion && backendFact.kind === 'convex'\n ? { functionsVersion: { probe: inputs.functionsVersion, expected: ENGINE_VERSION } }\n : {}),\n collections,\n config,\n ...(deploymentEnv ? { deploymentEnv } : {}),\n }\n}\n\n/** Compose the studio's deployment-readiness report (pure). */\nexport function studioDeploymentReadiness(inputs: StudioReadinessInputs): DeploymentReadinessReport {\n return checkDeploymentReady(studioReadinessFacts(inputs))\n}\n"]}
|
|
@@ -12,14 +12,14 @@ import type { GeneratedPreviewLookup } from '../components/builder/blocks-model.
|
|
|
12
12
|
* deterministic — no timestamps — so an unchanged re-run rewrites nothing.
|
|
13
13
|
*/
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* The
|
|
15
|
+
* The manifest FORMAT VERSION is declared once, in the CLI (`@elytracms/cli`
|
|
16
|
+
* `src/previews.js` `PREVIEWS_MANIFEST_VERSION`) — the CLI is both its writer and
|
|
17
|
+
* its only reader (`elytra previews --check` reports a mismatch as itself and
|
|
18
|
+
* regenerates). EC-514 removed the mirror this module used to carry: nothing in the
|
|
19
|
+
* studio ever read it, and a hand-kept copy is a drift site with no reader to catch
|
|
20
|
+
* the drift. The reader below is version-agnostic by design — it only ever looks at
|
|
21
|
+
* `componentId`/`fixture`/`url`, so a manifest of any version resolves tiles.
|
|
21
22
|
*/
|
|
22
|
-
export declare const PREVIEWS_MANIFEST_VERSION = 2;
|
|
23
23
|
/** One generated preview artifact: a component fixture and its rendered image. */
|
|
24
24
|
export interface GeneratedPreviewsManifestEntry {
|
|
25
25
|
componentId: string;
|
|
@@ -1,24 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The previews-manifest format (EC-440) — the JSON contract between the
|
|
3
|
-
* `elytra previews` CLI (the writer) and a consumer's `studio.config.tsx`
|
|
4
|
-
* (the reader, via {@link generatedPreviewsFromManifest}).
|
|
5
|
-
*
|
|
6
|
-
* The CLI renders every canvas-eligible component's fixtures headlessly
|
|
7
|
-
* against the consumer theme, writes the images to the studio app's
|
|
8
|
-
* `public/elytra-previews/` (served at `/elytra-previews/...`), and writes
|
|
9
|
-
* this manifest next to `studio.config.tsx` (`studio/elytra-previews.json`)
|
|
10
|
-
* so it is importable as a plain JSON module. Everything in it is
|
|
11
|
-
* deterministic — no timestamps — so an unchanged re-run rewrites nothing.
|
|
12
|
-
*/
|
|
13
|
-
/**
|
|
14
|
-
* Format version the CLI writes; bump on incompatible schema/hash changes.
|
|
15
|
-
*
|
|
16
|
-
* `2` (EC-502): the entry hash no longer folds in the engine version, so v1 and v2
|
|
17
|
-
* hashes are not comparable. `elytra previews --check` reports a version mismatch
|
|
18
|
-
* as itself and regenerates, rather than printing one bogus `stale` line per entry.
|
|
19
|
-
* The READER below is unaffected — it only ever looks at componentId/fixture/url.
|
|
20
|
-
*/
|
|
21
|
-
export const PREVIEWS_MANIFEST_VERSION = 2;
|
|
22
1
|
/** Tolerant entry guard — a malformed entry is skipped, never a crash. */
|
|
23
2
|
function isManifestEntry(value) {
|
|
24
3
|
if (typeof value !== 'object' || value === null)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-format.js","sourceRoot":"","sources":["../../src/previews/manifest-format.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"manifest-format.js","sourceRoot":"","sources":["../../src/previews/manifest-format.ts"],"names":[],"mappings":"AAkDA,0EAA0E;AAC1E,SAAS,eAAe,CAAC,KAAc;IACrC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAA;IAC7D,MAAM,KAAK,GAAG,KAAgC,CAAA;IAC9C,OAAO,CACL,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ;QACrC,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;QACjC,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ,CAC9B,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,6BAA6B,CAAC,QAAiB;IAC7D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAA;IACvC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtD,MAAM,OAAO,GAAI,QAAkC,CAAC,OAAO,CAAA;QAC3D,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,IAAI,eAAe,CAAC,KAAK,CAAC;oBAAE,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;YAC/F,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,CAAC,WAAW,EAAE,WAAW,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,CAAA;AACrF,CAAC;AAED,SAAS,SAAS,CAAC,WAAmB,EAAE,OAAe;IACrD,wEAAwE;IACxE,OAAO,GAAG,WAAW,SAAS,OAAO,EAAE,CAAA;AACzC,CAAC","sourcesContent":["import type { GeneratedPreviewLookup } from '../components/builder/blocks-model'\n\n/**\n * The previews-manifest format (EC-440) — the JSON contract between the\n * `elytra previews` CLI (the writer) and a consumer's `studio.config.tsx`\n * (the reader, via {@link generatedPreviewsFromManifest}).\n *\n * The CLI renders every canvas-eligible component's fixtures headlessly\n * against the consumer theme, writes the images to the studio app's\n * `public/elytra-previews/` (served at `/elytra-previews/...`), and writes\n * this manifest next to `studio.config.tsx` (`studio/elytra-previews.json`)\n * so it is importable as a plain JSON module. Everything in it is\n * deterministic — no timestamps — so an unchanged re-run rewrites nothing.\n */\n\n/**\n * The manifest FORMAT VERSION is declared once, in the CLI (`@elytracms/cli`\n * `src/previews.js` `PREVIEWS_MANIFEST_VERSION`) — the CLI is both its writer and\n * its only reader (`elytra previews --check` reports a mismatch as itself and\n * regenerates). EC-514 removed the mirror this module used to carry: nothing in the\n * studio ever read it, and a hand-kept copy is a drift site with no reader to catch\n * the drift. The reader below is version-agnostic by design — it only ever looks at\n * `componentId`/`fixture`/`url`, so a manifest of any version resolves tiles.\n */\n\n/** One generated preview artifact: a component fixture and its rendered image. */\nexport interface GeneratedPreviewsManifestEntry {\n componentId: string\n /** The fixture name, or `'Default'` for a fixture-less component's implicit variant. */\n fixture: string\n /** Root-relative image URL (the file lives in the studio app's `public/`). */\n url: string\n /**\n * Input hash (sha-256 hex over the stable-serialized inputs: manifest prop +\n * slot shape, fixture props/slotChildren — NOT the engine version, EC-502).\n * `elytra previews` skips entries whose hash is unchanged; `elytra previews\n * --check` flags entries whose hash drifted as stale. Deliberately NOT part of\n * the lookup — the studio only reads `componentId`/`fixture`/`url`.\n */\n hash: string\n}\n\n/** The manifest document `elytra previews` writes (`studio/elytra-previews.json`). */\nexport interface GeneratedPreviewsManifest {\n version: number\n /** The `@elytracms/studio` version the images were rendered with. */\n engine: string\n entries: GeneratedPreviewsManifestEntry[]\n}\n\n/** Tolerant entry guard — a malformed entry is skipped, never a crash. */\nfunction isManifestEntry(value: unknown): value is GeneratedPreviewsManifestEntry {\n if (typeof value !== 'object' || value === null) return false\n const entry = value as Record<string, unknown>\n return (\n typeof entry.componentId === 'string' &&\n typeof entry.fixture === 'string' &&\n typeof entry.url === 'string'\n )\n}\n\n/**\n * Adapt a previews manifest (a plain JSON import) into the EC-439\n * {@link GeneratedPreviewLookup} seam. The whole consumer wiring is ~2 lines:\n *\n * ```tsx\n * // studio.config.tsx\n * import previewsManifest from './elytra-previews.json'\n * export default defineStudio({\n * previews: { generated: generatedPreviewsFromManifest(previewsManifest) },\n * })\n * ```\n *\n * Tolerant by design (the input is a generated file a human may hand-edit or\n * delete): a malformed document or entry yields lookup misses — the blocks\n * panel then falls back to the icon+name placeholder — never a crash. A\n * fixture-less component's single implicit variant is looked up (and written\n * by the CLI) under the fixture name `'Default'`.\n */\nexport function generatedPreviewsFromManifest(manifest: unknown): GeneratedPreviewLookup {\n const byKey = new Map<string, string>()\n if (typeof manifest === 'object' && manifest !== null) {\n const entries = (manifest as { entries?: unknown }).entries\n if (Array.isArray(entries)) {\n for (const entry of entries) {\n if (isManifestEntry(entry)) byKey.set(lookupKey(entry.componentId, entry.fixture), entry.url)\n }\n }\n }\n return (componentId, fixtureName) => byKey.get(lookupKey(componentId, fixtureName))\n}\n\nfunction lookupKey(componentId: string, fixture: string): string {\n // NUL (\\u0000) can appear in neither a component id nor a fixture name.\n return `${componentId}\\u0000${fixture}`\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.86",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22.18.0"
|
|
7
7
|
},
|
|
@@ -153,7 +153,7 @@
|
|
|
153
153
|
"lucide-react": "^1.23.0",
|
|
154
154
|
"tailwind-merge": "^3.6.0",
|
|
155
155
|
"zod": "^4.4.3",
|
|
156
|
-
"@elytracms/core": "^0.0.
|
|
156
|
+
"@elytracms/core": "^0.0.86"
|
|
157
157
|
},
|
|
158
158
|
"peerDependencies": {
|
|
159
159
|
"@tanstack/react-router": "^1.170.0",
|