@elytracms/studio 0.0.86 → 0.0.87

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.
Files changed (203) hide show
  1. package/convex/asset-revalidate.test.ts +186 -0
  2. package/convex/assets.ts +53 -19
  3. package/convex/cms.ts +22 -0
  4. package/convex/delivery.ts +33 -116
  5. package/convex/functions.ts +3 -3
  6. package/convex/rail-write-parity.test.ts +180 -0
  7. package/convex/redirects.ts +33 -11
  8. package/convex/revalidate.ts +7 -5
  9. package/convex/triggers.ts +53 -3
  10. package/dist/components/app-views/default-modules.d.ts +20 -20
  11. package/dist/components/app-views/default-modules.js +27 -48
  12. package/dist/components/app-views/default-modules.js.map +1 -1
  13. package/dist/components/app-views/studio-config.d.ts +5 -6
  14. package/dist/components/app-views/studio-config.js.map +1 -1
  15. package/dist/components/assets/asset-metadata.d.ts +28 -19
  16. package/dist/components/assets/asset-metadata.js +24 -31
  17. package/dist/components/assets/asset-metadata.js.map +1 -1
  18. package/dist/components/assets/asset-selection.d.ts +24 -5
  19. package/dist/components/assets/asset-selection.js +39 -9
  20. package/dist/components/assets/asset-selection.js.map +1 -1
  21. package/dist/components/assets/assets-collection-view-source.d.ts +17 -14
  22. package/dist/components/assets/assets-collection-view-source.js +25 -31
  23. package/dist/components/assets/assets-collection-view-source.js.map +1 -1
  24. package/dist/components/builder/blocks-panel.js +3 -3
  25. package/dist/components/builder/blocks-panel.js.map +1 -1
  26. package/dist/components/builder/builder-store.d.ts +25 -1
  27. package/dist/components/builder/builder-store.js +3 -2
  28. package/dist/components/builder/builder-store.js.map +1 -1
  29. package/dist/components/builder/canvas-chrome-css.js +0 -17
  30. package/dist/components/builder/canvas-chrome-css.js.map +1 -1
  31. package/dist/components/builder/canvas-chrome.d.ts +10 -12
  32. package/dist/components/builder/canvas-chrome.js +12 -13
  33. package/dist/components/builder/canvas-chrome.js.map +1 -1
  34. package/dist/components/builder/canvas-dnd.d.ts +8 -0
  35. package/dist/components/builder/canvas-dnd.js +5 -3
  36. package/dist/components/builder/canvas-dnd.js.map +1 -1
  37. package/dist/components/builder/canvas-render-context.d.ts +12 -24
  38. package/dist/components/builder/canvas-render-context.js +15 -44
  39. package/dist/components/builder/canvas-render-context.js.map +1 -1
  40. package/dist/components/builder/canvas.js +5 -11
  41. package/dist/components/builder/canvas.js.map +1 -1
  42. package/dist/components/builder/clipboard-actions.d.ts +14 -6
  43. package/dist/components/builder/clipboard-actions.js +29 -20
  44. package/dist/components/builder/clipboard-actions.js.map +1 -1
  45. package/dist/components/builder/composition-field-editor.d.ts +19 -26
  46. package/dist/components/builder/composition-field-editor.js +30 -52
  47. package/dist/components/builder/composition-field-editor.js.map +1 -1
  48. package/dist/components/builder/insert-context.d.ts +3 -1
  49. package/dist/components/builder/insert-context.js +15 -10
  50. package/dist/components/builder/insert-context.js.map +1 -1
  51. package/dist/components/builder/page-tree.d.ts +29 -0
  52. package/dist/components/builder/page-tree.js +56 -58
  53. package/dist/components/builder/page-tree.js.map +1 -1
  54. package/dist/components/builder/reuse.js +5 -2
  55. package/dist/components/builder/reuse.js.map +1 -1
  56. package/dist/components/cms/asset-field.js +20 -32
  57. package/dist/components/cms/asset-field.js.map +1 -1
  58. package/dist/components/cms/cms-composition-validators.js +11 -2
  59. package/dist/components/cms/cms-composition-validators.js.map +1 -1
  60. package/dist/components/cms/editable-cell.js +5 -2
  61. package/dist/components/cms/editable-cell.js.map +1 -1
  62. package/dist/components/cms/use-document-versions.js +1 -0
  63. package/dist/components/cms/use-document-versions.js.map +1 -1
  64. package/dist/components/cms/versions-model.d.ts +0 -12
  65. package/dist/components/cms/versions-model.js +3 -15
  66. package/dist/components/cms/versions-model.js.map +1 -1
  67. package/dist/components/components/component-detail.js +1 -1
  68. package/dist/components/components/component-detail.js.map +1 -1
  69. package/dist/components/components/transfer-dialog.js +2 -4
  70. package/dist/components/components/transfer-dialog.js.map +1 -1
  71. package/dist/components/components/transfer.d.ts +13 -10
  72. package/dist/components/components/transfer.js +21 -19
  73. package/dist/components/components/transfer.js.map +1 -1
  74. package/dist/components/project-session.d.ts +9 -0
  75. package/dist/components/project-session.js +1 -1
  76. package/dist/components/project-session.js.map +1 -1
  77. package/dist/components/redirects/redirects-collection-view-source.d.ts +17 -2
  78. package/dist/components/redirects/redirects-collection-view-source.js +20 -6
  79. package/dist/components/redirects/redirects-collection-view-source.js.map +1 -1
  80. package/dist/components/section-surface.js +3 -2
  81. package/dist/components/section-surface.js.map +1 -1
  82. package/dist/components/settings-form.js +1 -2
  83. package/dist/components/settings-form.js.map +1 -1
  84. package/dist/components/studio-context.js +1 -18
  85. package/dist/components/studio-context.js.map +1 -1
  86. package/dist/components/studio-project-screen.js +3 -5
  87. package/dist/components/studio-project-screen.js.map +1 -1
  88. package/dist/lib/ai/apply-content.js +73 -29
  89. package/dist/lib/ai/apply-content.js.map +1 -1
  90. package/dist/lib/ai/constraints.d.ts +35 -0
  91. package/dist/lib/ai/constraints.js +104 -4
  92. package/dist/lib/ai/constraints.js.map +1 -1
  93. package/dist/lib/ai/route.js +4 -1
  94. package/dist/lib/ai/route.js.map +1 -1
  95. package/dist/lib/ai/shape.d.ts +3 -2
  96. package/dist/lib/ai/shape.js +16 -24
  97. package/dist/lib/ai/shape.js.map +1 -1
  98. package/dist/lib/ai/types.d.ts +8 -0
  99. package/dist/lib/ai/types.js.map +1 -1
  100. package/dist/lib/ai/update-fields.js +33 -12
  101. package/dist/lib/ai/update-fields.js.map +1 -1
  102. package/dist/lib/assets/asset-placements.d.ts +35 -0
  103. package/dist/lib/assets/asset-placements.js +74 -0
  104. package/dist/lib/assets/asset-placements.js.map +1 -0
  105. package/dist/lib/assets/backfill-thumbs.d.ts +9 -8
  106. package/dist/lib/assets/backfill-thumbs.js +13 -25
  107. package/dist/lib/assets/backfill-thumbs.js.map +1 -1
  108. package/dist/lib/clipboard/paste-issues.d.ts +17 -15
  109. package/dist/lib/clipboard/paste-issues.js +8 -45
  110. package/dist/lib/clipboard/paste-issues.js.map +1 -1
  111. package/dist/lib/clipboard/paste-target.d.ts +25 -1
  112. package/dist/lib/clipboard/paste-target.js +13 -6
  113. package/dist/lib/clipboard/paste-target.js.map +1 -1
  114. package/dist/lib/cms/published-documents.js +1 -0
  115. package/dist/lib/cms/published-documents.js.map +1 -1
  116. package/dist/lib/cms-views/view-model.d.ts +3 -3
  117. package/dist/lib/data/index.d.ts +0 -3
  118. package/dist/lib/data/index.js +4 -6
  119. package/dist/lib/data/index.js.map +1 -1
  120. package/dist/lib/dnd/drop-rules.d.ts +45 -2
  121. package/dist/lib/dnd/drop-rules.js +64 -9
  122. package/dist/lib/dnd/drop-rules.js.map +1 -1
  123. package/dist/lib/engine-version.d.ts +1 -1
  124. package/dist/lib/engine-version.js +1 -1
  125. package/dist/lib/engine-version.js.map +1 -1
  126. package/dist/lib/i18n/messages.de.js +4 -56
  127. package/dist/lib/i18n/messages.de.js.map +1 -1
  128. package/dist/lib/i18n/messages.en.d.ts +4 -53
  129. package/dist/lib/i18n/messages.en.js +8 -57
  130. package/dist/lib/i18n/messages.en.js.map +1 -1
  131. package/dist/lib/locale/fallback.js +8 -2
  132. package/dist/lib/locale/fallback.js.map +1 -1
  133. package/dist/lib/locale/index.d.ts +0 -1
  134. package/dist/lib/locale/index.js +0 -1
  135. package/dist/lib/locale/index.js.map +1 -1
  136. package/dist/lib/locale/locale-store.d.ts +10 -3
  137. package/dist/lib/locale/locale-store.js +12 -5
  138. package/dist/lib/locale/locale-store.js.map +1 -1
  139. package/dist/lib/operations/session-hydration.d.ts +9 -5
  140. package/dist/lib/operations/session-hydration.js +10 -10
  141. package/dist/lib/operations/session-hydration.js.map +1 -1
  142. package/dist/lib/persistence/convex-adapter.js +63 -75
  143. package/dist/lib/persistence/convex-adapter.js.map +1 -1
  144. package/dist/lib/persona/labels.d.ts +0 -2
  145. package/dist/lib/persona/labels.js +1 -7
  146. package/dist/lib/persona/labels.js.map +1 -1
  147. package/dist/lib/registry/index.d.ts +4 -2
  148. package/dist/lib/registry/index.js +26 -21
  149. package/dist/lib/registry/index.js.map +1 -1
  150. package/dist/lib/registry/project-components.js +0 -3
  151. package/dist/lib/registry/project-components.js.map +1 -1
  152. package/dist/lib/routes/route-issues.js +4 -5
  153. package/dist/lib/routes/route-issues.js.map +1 -1
  154. package/dist/lib/theme.js +3 -2
  155. package/dist/lib/theme.js.map +1 -1
  156. package/dist/lib/webhooks/tags.js +11 -14
  157. package/dist/lib/webhooks/tags.js.map +1 -1
  158. package/dist/lib/workspace/index.d.ts +1 -1
  159. package/dist/lib/workspace/index.js +1 -1
  160. package/dist/lib/workspace/index.js.map +1 -1
  161. package/dist/previews/enumerate.d.ts +27 -9
  162. package/dist/previews/enumerate.js +27 -6
  163. package/dist/previews/enumerate.js.map +1 -1
  164. package/dist/previews/manifest-format.d.ts +9 -0
  165. package/dist/previews/manifest-format.js.map +1 -1
  166. package/dist/router/match.d.ts +0 -2
  167. package/dist/router/match.js +0 -2
  168. package/dist/router/match.js.map +1 -1
  169. package/dist/ui/badge.d.ts +1 -1
  170. package/package.json +2 -2
  171. package/src/styles/studio.css +5 -4
  172. package/src/styles/tokens.css +72 -55
  173. package/convex/content.ts +0 -291
  174. package/dist/components/collab/collab-context.d.ts +0 -27
  175. package/dist/components/collab/collab-context.js +0 -39
  176. package/dist/components/collab/collab-context.js.map +0 -1
  177. package/dist/components/collab/collab-seed.d.ts +0 -15
  178. package/dist/components/collab/collab-seed.js +0 -20
  179. package/dist/components/collab/collab-seed.js.map +0 -1
  180. package/dist/components/extensions/extensions-view.d.ts +0 -7
  181. package/dist/components/extensions/extensions-view.js +0 -127
  182. package/dist/components/extensions/extensions-view.js.map +0 -1
  183. package/dist/lib/bindings/section-bindings.d.ts +0 -24
  184. package/dist/lib/bindings/section-bindings.js +0 -9
  185. package/dist/lib/bindings/section-bindings.js.map +0 -1
  186. package/dist/lib/data/data-store.d.ts +0 -98
  187. package/dist/lib/data/data-store.js +0 -211
  188. package/dist/lib/data/data-store.js.map +0 -1
  189. package/dist/lib/data/providers.d.ts +0 -143
  190. package/dist/lib/data/providers.js +0 -255
  191. package/dist/lib/data/providers.js.map +0 -1
  192. package/dist/lib/data/sample-capture.d.ts +0 -30
  193. package/dist/lib/data/sample-capture.js +0 -70
  194. package/dist/lib/data/sample-capture.js.map +0 -1
  195. package/dist/lib/data/sources-store.d.ts +0 -42
  196. package/dist/lib/data/sources-store.js +0 -120
  197. package/dist/lib/data/sources-store.js.map +0 -1
  198. package/dist/lib/locale/binding-fallback.d.ts +0 -35
  199. package/dist/lib/locale/binding-fallback.js +0 -73
  200. package/dist/lib/locale/binding-fallback.js.map +0 -1
  201. package/dist/lib/starters/kit-session.d.ts +0 -5
  202. package/dist/lib/starters/kit-session.js +0 -17
  203. package/dist/lib/starters/kit-session.js.map +0 -1
@@ -1,255 +0,0 @@
1
- import { z } from 'zod';
2
- /**
3
- * Define a provider with the output type checked against the schema — the
4
- * provider-side mirror of `defineDataSourceAdapter` (EC-132) and component
5
- * `ComponentManifest` definitions.
6
- */
7
- export function defineDataProvider(provider) {
8
- return provider;
9
- }
10
- const PROVIDER_ID_PATTERN = /^[a-z][a-z0-9-]*$/;
11
- /**
12
- * Registry of data providers keyed by id. First registration wins; duplicates
13
- * and malformed ids are recorded as structured issues (component-registry
14
- * semantics).
15
- */
16
- export class DataProviderRegistry {
17
- byId = new Map();
18
- issues = [];
19
- constructor(providers = []) {
20
- for (const provider of providers)
21
- this.register(provider);
22
- }
23
- register(provider) {
24
- const id = provider.manifest.id;
25
- if (!PROVIDER_ID_PATTERN.test(id)) {
26
- this.issues.push({
27
- code: 'invalid-id',
28
- ref: id,
29
- message: `Provider id "${id}" must be lower-case and dash-separated (e.g. "pricing-plans").`,
30
- });
31
- return;
32
- }
33
- if (this.byId.has(id)) {
34
- this.issues.push({
35
- code: 'duplicate',
36
- ref: id,
37
- message: `Duplicate provider id "${id}" — keeping the first registration.`,
38
- });
39
- return;
40
- }
41
- this.byId.set(id, provider);
42
- }
43
- get(id) {
44
- return this.byId.get(id);
45
- }
46
- list() {
47
- return [...this.byId.values()];
48
- }
49
- get size() {
50
- return this.byId.size;
51
- }
52
- }
53
- /** Default args of a provider (its params' declared defaults). */
54
- export function defaultProviderArgs(manifest) {
55
- return Object.fromEntries((manifest.params ?? []).map((p) => [p.name, p.defaultValue]));
56
- }
57
- /**
58
- * Run a provider and validate the result against its output schema. A thrown
59
- * error or a schema mismatch comes back as a plain-language issue — the
60
- * binding layer renders a visible fallback, never a crash.
61
- */
62
- export function runProvider(provider, args) {
63
- const title = provider.manifest.title;
64
- let raw;
65
- try {
66
- raw = provider.fetch(args ?? defaultProviderArgs(provider.manifest));
67
- }
68
- catch {
69
- return {
70
- ok: false,
71
- message: `“${title}” couldn't load right now — the project code reported a problem. Anything showing it stays empty until it's fixed.`,
72
- };
73
- }
74
- const parsed = provider.manifest.output.safeParse(raw);
75
- if (!parsed.success) {
76
- return {
77
- ok: false,
78
- message: `“${title}” returned data in an unexpected shape, so it can't be shown. The project code and its declared output no longer agree.`,
79
- };
80
- }
81
- return { ok: true, value: parsed.data };
82
- }
83
- // ---------------------------------------------------------------------------
84
- // Bridging providers into the binding engine's source vocabulary
85
- // ---------------------------------------------------------------------------
86
- /** Shared project-wide provider sources: `provider.<providerId>`. */
87
- export const PROVIDER_SOURCE_PREFIX = 'provider.';
88
- /** The functionRef namespace marking a source as provider-backed. */
89
- const PROVIDER_FUNCTION_PREFIX = 'provider:';
90
- export function providerSourceIdFor(providerId) {
91
- return `${PROVIDER_SOURCE_PREFIX}${providerId}`;
92
- }
93
- /** The provider a source is backed by, or undefined for non-provider sources. */
94
- export function providerIdOfSource(source) {
95
- if (source === undefined || source.kind !== 'convex')
96
- return undefined;
97
- const ref = source.config.functionRef;
98
- return ref.startsWith(PROVIDER_FUNCTION_PREFIX)
99
- ? ref.slice(PROVIDER_FUNCTION_PREFIX.length)
100
- : undefined;
101
- }
102
- /**
103
- * The engine-shaped source addressing a provider. `args` carry the editor's
104
- * param choices; `id` defaults to the shared project-wide source.
105
- */
106
- export function providerSourceFor(provider, args, id, label) {
107
- return {
108
- kind: 'convex',
109
- id: id ?? providerSourceIdFor(provider.manifest.id),
110
- label: label ?? provider.manifest.title,
111
- config: {
112
- operation: 'query',
113
- functionRef: `${PROVIDER_FUNCTION_PREFIX}${provider.manifest.id}`,
114
- args: args ?? defaultProviderArgs(provider.manifest),
115
- },
116
- };
117
- }
118
- // ---------------------------------------------------------------------------
119
- // The project's registered providers (project-owned, code-first)
120
- // ---------------------------------------------------------------------------
121
- const pricingPlanSchema = z.object({
122
- id: z.string(),
123
- name: z.string(),
124
- monthlyPrice: z.number(),
125
- summary: z.string(),
126
- featured: z.boolean(),
127
- });
128
- /** Example provider: a static, code-owned pricing table (think Stripe prices). */
129
- export const pricingPlansProvider = defineDataProvider({
130
- manifest: {
131
- id: 'pricing-plans',
132
- title: 'Pricing plans',
133
- description: 'The pricing tiers exactly as the billing code defines them.',
134
- output: z.array(pricingPlanSchema),
135
- },
136
- fetch: () => [
137
- {
138
- id: 'starter',
139
- name: 'Starter',
140
- monthlyPrice: 19,
141
- summary: 'For solo builders shipping their first site.',
142
- featured: false,
143
- },
144
- {
145
- id: 'studio',
146
- name: 'Studio',
147
- monthlyPrice: 49,
148
- summary: 'For teams that publish every week.',
149
- featured: true,
150
- },
151
- {
152
- id: 'enterprise',
153
- name: 'Enterprise',
154
- monthlyPrice: 199,
155
- summary: 'For organizations with compliance needs.',
156
- featured: false,
157
- },
158
- ],
159
- });
160
- const jobListingSchema = z.object({
161
- id: z.string(),
162
- title: z.string(),
163
- department: z.string(),
164
- location: z.string(),
165
- remote: z.boolean(),
166
- });
167
- const JOB_LISTINGS = [
168
- {
169
- id: 'job-fe',
170
- title: 'Frontend Engineer',
171
- department: 'Engineering',
172
- location: 'Berlin',
173
- remote: true,
174
- },
175
- {
176
- id: 'job-be',
177
- title: 'Backend Engineer',
178
- department: 'Engineering',
179
- location: 'Amsterdam',
180
- remote: true,
181
- },
182
- {
183
- id: 'job-pd',
184
- title: 'Product Designer',
185
- department: 'Design',
186
- location: 'Berlin',
187
- remote: false,
188
- },
189
- {
190
- id: 'job-cm',
191
- title: 'Content Marketer',
192
- department: 'Marketing',
193
- location: 'Remote',
194
- remote: true,
195
- },
196
- ];
197
- /** Example provider with a param: job listings filterable by department. */
198
- export const jobListingsProvider = defineDataProvider({
199
- manifest: {
200
- id: 'job-listings',
201
- title: 'Job listings',
202
- description: 'Open roles from the hiring system, optionally narrowed to one department.',
203
- output: z.array(jobListingSchema),
204
- params: [
205
- {
206
- name: 'department',
207
- label: 'Department',
208
- kind: 'choice',
209
- choices: [
210
- { value: 'all', label: 'All departments' },
211
- { value: 'Engineering', label: 'Engineering' },
212
- { value: 'Design', label: 'Design' },
213
- { value: 'Marketing', label: 'Marketing' },
214
- ],
215
- defaultValue: 'all',
216
- },
217
- ],
218
- },
219
- fetch: ({ department }) => typeof department === 'string' && department !== 'all'
220
- ? JOB_LISTINGS.filter((job) => job.department === department)
221
- : [...JOB_LISTINGS],
222
- });
223
- /**
224
- * The project's provider registry. In a deployed studio this hydrates from
225
- * synced provider manifests (the component-manifest sync path); in the
226
- * playground the project code registers directly.
227
- */
228
- export const projectDataProviders = new DataProviderRegistry([
229
- pricingPlansProvider,
230
- jobListingsProvider,
231
- ]);
232
- export function listDataProviders() {
233
- return projectDataProviders.list();
234
- }
235
- export function getDataProvider(id) {
236
- return projectDataProviders.get(id);
237
- }
238
- /**
239
- * The payload a provider-backed source serves, or `null` when the provider is
240
- * unregistered or failing — a `null` sample makes every binding against it a
241
- * visible plain-language issue instead of a crash.
242
- */
243
- export function providerPayload(providerId, args) {
244
- const provider = getDataProvider(providerId);
245
- if (provider === undefined)
246
- return null;
247
- const run = runProvider(provider, args);
248
- return run.ok ? run.value : null;
249
- }
250
- /** A provider's canonical sample (its default-args run), or undefined when failing. */
251
- export function providerSample(provider) {
252
- const run = runProvider(provider);
253
- return run.ok ? run.value : undefined;
254
- }
255
- //# sourceMappingURL=providers.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"providers.js","sourceRoot":"","sources":["../../../src/lib/data/providers.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod'\nimport type { ConvexSource, DataSource } from '@elytracms/core/data-binding'\nimport type { JsonValue } from '@elytracms/core/project-graph'\n\n/**\n * Typed data providers (EC-167, vision AD-9). External data is a developer\n * concern expressed exactly like a component: a provider is a plain function\n * plus a manifest (id, title, description, output schema, optional params)\n * registered in code. Editors never see providers as anything technical — the\n * Content tab offers them next to CMS collections, labeled by their\n * plain-language titles, and provider results resolve through the unchanged\n * `@elytracms/core/data-binding` engine.\n *\n * Registration mirrors how component manifests register: project code defines\n * providers and registers them in-process here. In a host repository the same\n * definitions would be extracted into synced manifests (the EC-147 component\n * manifest sync path) with the function itself executed behind the\n * project-owned adapter — an honest gap today: this playground registry IS the\n * project code, so \"sync\" is a direct import.\n *\n * Bridging into the engine: a provider is addressed as a `convex`-kind data\n * source whose `functionRef` is `provider:<id>` — a provider is exactly a\n * typed server function, and the engine's source vocabulary already models\n * that. Resolution, sampling, inference, tokens, and broken-binding issues all\n * apply unchanged. (See NOTES: the engine could gain a first-class `provider`\n * source kind as a purely additive change.)\n */\n\n/** A scalar an editor can put into a provider param control. */\nexport type ProviderParamValue = string | number | boolean\n\n/** One choice of a `choice`-kind param. */\nexport interface ProviderParamChoice {\n value: string\n label: string\n}\n\n/**\n * A provider param, surfaced to editors as one plain control (no syntax, no\n * JSON): `choice` renders a select over {@link choices}; the scalar kinds\n * render a matching input.\n */\nexport interface ProviderParamSpec {\n name: string\n /** Plain-language control label, e.g. \"Department\". */\n label: string\n kind: 'text' | 'number' | 'boolean' | 'choice'\n /** For `choice` params: the closed set of offered values. */\n choices?: ProviderParamChoice[]\n defaultValue: ProviderParamValue\n}\n\n/** Arguments a provider runs with (param name → value). */\nexport type ProviderArgs = Record<string, JsonValue>\n\n/** The manifest half of a provider — everything the builder surfaces. */\nexport interface DataProviderManifest {\n /** Stable provider id, e.g. `pricing-plans`. */\n id: string\n /** Plain-language title editors pick, e.g. \"Pricing plans\". */\n title: string\n description: string\n /** Zod schema of the payload; every run is validated against it. */\n output: z.ZodType<JsonValue>\n params?: ProviderParamSpec[]\n}\n\n/** A data provider: a typed function plus its manifest. */\nexport interface DataProvider {\n manifest: DataProviderManifest\n /** Produce the payload. Deterministic here; a host repo may make this async behind its adapter. */\n fetch: (args: ProviderArgs) => JsonValue\n}\n\n/**\n * Define a provider with the output type checked against the schema — the\n * provider-side mirror of `defineDataSourceAdapter` (EC-132) and component\n * `ComponentManifest` definitions.\n */\nexport function defineDataProvider<T extends JsonValue>(provider: {\n manifest: Omit<DataProviderManifest, 'output'> & { output: z.ZodType<T> }\n fetch: (args: ProviderArgs) => T\n}): DataProvider {\n return provider as DataProvider\n}\n\n// ---------------------------------------------------------------------------\n// Registration (mirrors the component-registry contract)\n// ---------------------------------------------------------------------------\n\n/** A structured registration issue — duplicates are reported, never thrown. */\nexport interface ProviderRegistrationIssue {\n code: 'duplicate' | 'invalid-id'\n message: string\n ref: string\n}\n\nconst PROVIDER_ID_PATTERN = /^[a-z][a-z0-9-]*$/\n\n/**\n * Registry of data providers keyed by id. First registration wins; duplicates\n * and malformed ids are recorded as structured issues (component-registry\n * semantics).\n */\nexport class DataProviderRegistry {\n private readonly byId = new Map<string, DataProvider>()\n readonly issues: ProviderRegistrationIssue[] = []\n\n constructor(providers: Iterable<DataProvider> = []) {\n for (const provider of providers) this.register(provider)\n }\n\n register(provider: DataProvider): void {\n const id = provider.manifest.id\n if (!PROVIDER_ID_PATTERN.test(id)) {\n this.issues.push({\n code: 'invalid-id',\n ref: id,\n message: `Provider id \"${id}\" must be lower-case and dash-separated (e.g. \"pricing-plans\").`,\n })\n return\n }\n if (this.byId.has(id)) {\n this.issues.push({\n code: 'duplicate',\n ref: id,\n message: `Duplicate provider id \"${id}\" — keeping the first registration.`,\n })\n return\n }\n this.byId.set(id, provider)\n }\n\n get(id: string): DataProvider | undefined {\n return this.byId.get(id)\n }\n\n list(): DataProvider[] {\n return [...this.byId.values()]\n }\n\n get size(): number {\n return this.byId.size\n }\n}\n\n// ---------------------------------------------------------------------------\n// Running a provider (never throws, never crashes a surface)\n// ---------------------------------------------------------------------------\n\n/** The outcome of one provider run: a validated payload or a plain-language issue. */\nexport type ProviderRun =\n | { ok: true; value: JsonValue }\n | { ok: false; message: string }\n\n/** Default args of a provider (its params' declared defaults). */\nexport function defaultProviderArgs(manifest: DataProviderManifest): ProviderArgs {\n return Object.fromEntries((manifest.params ?? []).map((p) => [p.name, p.defaultValue]))\n}\n\n/**\n * Run a provider and validate the result against its output schema. A thrown\n * error or a schema mismatch comes back as a plain-language issue — the\n * binding layer renders a visible fallback, never a crash.\n */\nexport function runProvider(provider: DataProvider, args?: ProviderArgs): ProviderRun {\n const title = provider.manifest.title\n let raw: JsonValue\n try {\n raw = provider.fetch(args ?? defaultProviderArgs(provider.manifest))\n } catch {\n return {\n ok: false,\n message: `“${title}” couldn't load right now — the project code reported a problem. Anything showing it stays empty until it's fixed.`,\n }\n }\n const parsed = provider.manifest.output.safeParse(raw)\n if (!parsed.success) {\n return {\n ok: false,\n message: `“${title}” returned data in an unexpected shape, so it can't be shown. The project code and its declared output no longer agree.`,\n }\n }\n return { ok: true, value: parsed.data }\n}\n\n// ---------------------------------------------------------------------------\n// Bridging providers into the binding engine's source vocabulary\n// ---------------------------------------------------------------------------\n\n/** Shared project-wide provider sources: `provider.<providerId>`. */\nexport const PROVIDER_SOURCE_PREFIX = 'provider.'\n\n/** The functionRef namespace marking a source as provider-backed. */\nconst PROVIDER_FUNCTION_PREFIX = 'provider:'\n\nexport function providerSourceIdFor(providerId: string): string {\n return `${PROVIDER_SOURCE_PREFIX}${providerId}`\n}\n\n/** The provider a source is backed by, or undefined for non-provider sources. */\nexport function providerIdOfSource(source: DataSource | undefined): string | undefined {\n if (source === undefined || source.kind !== 'convex') return undefined\n const ref = source.config.functionRef\n return ref.startsWith(PROVIDER_FUNCTION_PREFIX)\n ? ref.slice(PROVIDER_FUNCTION_PREFIX.length)\n : undefined\n}\n\n/**\n * The engine-shaped source addressing a provider. `args` carry the editor's\n * param choices; `id` defaults to the shared project-wide source.\n */\nexport function providerSourceFor(\n provider: DataProvider,\n args?: ProviderArgs,\n id?: string,\n label?: string,\n): ConvexSource {\n return {\n kind: 'convex',\n id: id ?? providerSourceIdFor(provider.manifest.id),\n label: label ?? provider.manifest.title,\n config: {\n operation: 'query',\n functionRef: `${PROVIDER_FUNCTION_PREFIX}${provider.manifest.id}`,\n args: args ?? defaultProviderArgs(provider.manifest),\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// The project's registered providers (project-owned, code-first)\n// ---------------------------------------------------------------------------\n\nconst pricingPlanSchema = z.object({\n id: z.string(),\n name: z.string(),\n monthlyPrice: z.number(),\n summary: z.string(),\n featured: z.boolean(),\n})\n\n/** Example provider: a static, code-owned pricing table (think Stripe prices). */\nexport const pricingPlansProvider = defineDataProvider({\n manifest: {\n id: 'pricing-plans',\n title: 'Pricing plans',\n description: 'The pricing tiers exactly as the billing code defines them.',\n output: z.array(pricingPlanSchema),\n },\n fetch: () => [\n {\n id: 'starter',\n name: 'Starter',\n monthlyPrice: 19,\n summary: 'For solo builders shipping their first site.',\n featured: false,\n },\n {\n id: 'studio',\n name: 'Studio',\n monthlyPrice: 49,\n summary: 'For teams that publish every week.',\n featured: true,\n },\n {\n id: 'enterprise',\n name: 'Enterprise',\n monthlyPrice: 199,\n summary: 'For organizations with compliance needs.',\n featured: false,\n },\n ],\n})\n\nconst jobListingSchema = z.object({\n id: z.string(),\n title: z.string(),\n department: z.string(),\n location: z.string(),\n remote: z.boolean(),\n})\n\nconst JOB_LISTINGS = [\n {\n id: 'job-fe',\n title: 'Frontend Engineer',\n department: 'Engineering',\n location: 'Berlin',\n remote: true,\n },\n {\n id: 'job-be',\n title: 'Backend Engineer',\n department: 'Engineering',\n location: 'Amsterdam',\n remote: true,\n },\n {\n id: 'job-pd',\n title: 'Product Designer',\n department: 'Design',\n location: 'Berlin',\n remote: false,\n },\n {\n id: 'job-cm',\n title: 'Content Marketer',\n department: 'Marketing',\n location: 'Remote',\n remote: true,\n },\n] as const\n\n/** Example provider with a param: job listings filterable by department. */\nexport const jobListingsProvider = defineDataProvider({\n manifest: {\n id: 'job-listings',\n title: 'Job listings',\n description: 'Open roles from the hiring system, optionally narrowed to one department.',\n output: z.array(jobListingSchema),\n params: [\n {\n name: 'department',\n label: 'Department',\n kind: 'choice',\n choices: [\n { value: 'all', label: 'All departments' },\n { value: 'Engineering', label: 'Engineering' },\n { value: 'Design', label: 'Design' },\n { value: 'Marketing', label: 'Marketing' },\n ],\n defaultValue: 'all',\n },\n ],\n },\n fetch: ({ department }) =>\n typeof department === 'string' && department !== 'all'\n ? JOB_LISTINGS.filter((job) => job.department === department)\n : [...JOB_LISTINGS],\n})\n\n/**\n * The project's provider registry. In a deployed studio this hydrates from\n * synced provider manifests (the component-manifest sync path); in the\n * playground the project code registers directly.\n */\nexport const projectDataProviders = new DataProviderRegistry([\n pricingPlansProvider,\n jobListingsProvider,\n])\n\nexport function listDataProviders(): DataProvider[] {\n return projectDataProviders.list()\n}\n\nexport function getDataProvider(id: string): DataProvider | undefined {\n return projectDataProviders.get(id)\n}\n\n/**\n * The payload a provider-backed source serves, or `null` when the provider is\n * unregistered or failing — a `null` sample makes every binding against it a\n * visible plain-language issue instead of a crash.\n */\nexport function providerPayload(providerId: string, args?: ProviderArgs): JsonValue {\n const provider = getDataProvider(providerId)\n if (provider === undefined) return null\n const run = runProvider(provider, args)\n return run.ok ? run.value : null\n}\n\n/** A provider's canonical sample (its default-args run), or undefined when failing. */\nexport function providerSample(provider: DataProvider): JsonValue | undefined {\n const run = runProvider(provider)\n return run.ok ? run.value : undefined\n}\n"]}
@@ -1,30 +0,0 @@
1
- import type { DataSource } from '@elytracms/core/data-binding';
2
- import type { JsonValue } from '@elytracms/core/project-graph';
3
- import type { CollectionDef } from '@elytracms/core/cms-core';
4
- /**
5
- * Data-source sample capture (EC-083/EC-166/EC-167). The hosted builder is
6
- * separate from the site it exports: actually performing a REST fetch / external
7
- * query happens behind the project-owned adapters in the deployed site, NOT here.
8
- * So the studio captures a sample ONLY when it can do so honestly — it never
9
- * fabricates data (Oli, 2026-06-19; cf. backend-mode's "never silently swap in
10
- * in-memory storage", and the v1 "static props, bindings parked" direction):
11
- *
12
- * - a provider-backed source runs its registered provider (real project code);
13
- * - a CMS source replays its stored query over the live content session (Convex
14
- * mode — EC-177 gap 1), so section/template previews show what delivery serves;
15
- * - anything else — an external source with no in-studio data path — returns
16
- * `undefined`. The source then carries no sample, and the binding UI shows an
17
- * honest "no sample yet" state instead of inventing rows that aren't there.
18
- */
19
- export declare function captureSourceSample(source: DataSource): JsonValue | undefined;
20
- /**
21
- * A live CMS data source for one config-driven collection (EC-192). The binding
22
- * and condition pickers enumerate `data.sources`; deriving one source per
23
- * configured content collection — the same set the CMS uses
24
- * (`resolveDeploymentCollections`) — means what an editor can bind matches what
25
- * actually exists. The sample is captured by replaying the collection's list
26
- * query through the canonical content engine, so when a content session is
27
- * configured (Convex mode) the inferred field tokens are the collection's real
28
- * fields; with no session the source is listed but carries no sample.
29
- */
30
- export declare function cmsSourceForCollection(collection: CollectionDef): DataSource;
@@ -1,70 +0,0 @@
1
- import { captureSample } from '@elytracms/core/data-binding';
2
- import { humanizeName } from '../utils.js';
3
- import { listQueryOfConfig } from '../bindings/section-bindings.js';
4
- import { getCollectionDocument, hasContentSession, playgroundCollection, runCollectionListQuery, } from './collection-content.js';
5
- import { providerIdOfSource, providerPayload } from './providers.js';
6
- /**
7
- * Data-source sample capture (EC-083/EC-166/EC-167). The hosted builder is
8
- * separate from the site it exports: actually performing a REST fetch / external
9
- * query happens behind the project-owned adapters in the deployed site, NOT here.
10
- * So the studio captures a sample ONLY when it can do so honestly — it never
11
- * fabricates data (Oli, 2026-06-19; cf. backend-mode's "never silently swap in
12
- * in-memory storage", and the v1 "static props, bindings parked" direction):
13
- *
14
- * - a provider-backed source runs its registered provider (real project code);
15
- * - a CMS source replays its stored query over the live content session (Convex
16
- * mode — EC-177 gap 1), so section/template previews show what delivery serves;
17
- * - anything else — an external source with no in-studio data path — returns
18
- * `undefined`. The source then carries no sample, and the binding UI shows an
19
- * honest "no sample yet" state instead of inventing rows that aren't there.
20
- */
21
- export function captureSourceSample(source) {
22
- const providerId = providerIdOfSource(source);
23
- if (providerId !== undefined) {
24
- return providerPayload(providerId, source.kind === 'convex' ? source.config.args : undefined);
25
- }
26
- if (source.kind === 'cms') {
27
- return cmsDeliverySample(source.config);
28
- }
29
- return undefined;
30
- }
31
- /**
32
- * A live CMS data source for one config-driven collection (EC-192). The binding
33
- * and condition pickers enumerate `data.sources`; deriving one source per
34
- * configured content collection — the same set the CMS uses
35
- * (`resolveDeploymentCollections`) — means what an editor can bind matches what
36
- * actually exists. The sample is captured by replaying the collection's list
37
- * query through the canonical content engine, so when a content session is
38
- * configured (Convex mode) the inferred field tokens are the collection's real
39
- * fields; with no session the source is listed but carries no sample.
40
- */
41
- export function cmsSourceForCollection(collection) {
42
- const source = {
43
- kind: 'cms',
44
- id: `cms.${collection.id}`,
45
- label: humanizeName(collection.id),
46
- config: { collectionId: collection.id, filter: {}, limit: 20 },
47
- };
48
- const sample = captureSourceSample(source);
49
- return sample !== undefined ? captureSample(source, sample) : source;
50
- }
51
- /**
52
- * Replay a CMS source's stored query over the configured content session, in
53
- * delivery shape (EC-166). Returns `undefined` when no session serves the
54
- * collection (playground / no backend) — the caller then leaves the source
55
- * sample-less rather than fabricating one. A query the engine rejects — or one
56
- * matching nothing — yields an empty list / `null`, never a crash.
57
- */
58
- function cmsDeliverySample(config) {
59
- if (!hasContentSession())
60
- return undefined;
61
- if (playgroundCollection(config.collectionId) === undefined)
62
- return undefined;
63
- if (config.documentId !== undefined) {
64
- const result = getCollectionDocument(config.collectionId, config.documentId);
65
- return result.document ?? null;
66
- }
67
- const result = runCollectionListQuery(config.collectionId, listQueryOfConfig(config));
68
- return result.ok ? result.documents : [];
69
- }
70
- //# sourceMappingURL=sample-capture.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sample-capture.js","sourceRoot":"","sources":["../../../src/lib/data/sample-capture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAI5D,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAA;AAEhE,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAEjE;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAkB;IACpD,MAAM,UAAU,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAA;IAC7C,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,eAAe,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IAC/F,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC1B,OAAO,iBAAiB,CAAC,MAAM,CAAC,MAAwB,CAAC,CAAA;IAC3D,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,sBAAsB,CAAC,UAAyB;IAC9D,MAAM,MAAM,GAAe;QACzB,IAAI,EAAE,KAAK;QACX,EAAE,EAAE,OAAO,UAAU,CAAC,EAAE,EAAE;QAC1B,KAAK,EAAE,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC;QAClC,MAAM,EAAE,EAAE,YAAY,EAAE,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;KAC/D,CAAA;IACD,MAAM,MAAM,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAA;IAC1C,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;AACtE,CAAC;AAED;;;;;;GAMG;AACH,SAAS,iBAAiB,CAAC,MAAsB;IAC/C,IAAI,CAAC,iBAAiB,EAAE;QAAE,OAAO,SAAS,CAAA;IAC1C,IAAI,oBAAoB,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IAC7E,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACpC,MAAM,MAAM,GAAG,qBAAqB,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,UAAU,CAAC,CAAA;QAC5E,OAAQ,MAAM,CAAC,QAAiC,IAAI,IAAI,CAAA;IAC1D,CAAC;IACD,MAAM,MAAM,GAAG,sBAAsB,CAAC,MAAM,CAAC,YAAY,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAA;IACrF,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,CAAE,MAAM,CAAC,SAAkC,CAAC,CAAC,CAAC,EAAE,CAAA;AACpE,CAAC","sourcesContent":["import { captureSample } from '@elytracms/core/data-binding'\nimport type { DataSource } from '@elytracms/core/data-binding'\nimport type { JsonValue } from '@elytracms/core/project-graph'\nimport type { CollectionDef } from '@elytracms/core/cms-core'\nimport { humanizeName } from '../utils'\nimport { listQueryOfConfig } from '../bindings/section-bindings'\nimport type { QueryCmsConfig } from '../bindings/section-bindings'\nimport {\n getCollectionDocument,\n hasContentSession,\n playgroundCollection,\n runCollectionListQuery,\n} from './collection-content'\nimport { providerIdOfSource, providerPayload } from './providers'\n\n/**\n * Data-source sample capture (EC-083/EC-166/EC-167). The hosted builder is\n * separate from the site it exports: actually performing a REST fetch / external\n * query happens behind the project-owned adapters in the deployed site, NOT here.\n * So the studio captures a sample ONLY when it can do so honestly — it never\n * fabricates data (Oli, 2026-06-19; cf. backend-mode's \"never silently swap in\n * in-memory storage\", and the v1 \"static props, bindings parked\" direction):\n *\n * - a provider-backed source runs its registered provider (real project code);\n * - a CMS source replays its stored query over the live content session (Convex\n * mode — EC-177 gap 1), so section/template previews show what delivery serves;\n * - anything else — an external source with no in-studio data path — returns\n * `undefined`. The source then carries no sample, and the binding UI shows an\n * honest \"no sample yet\" state instead of inventing rows that aren't there.\n */\nexport function captureSourceSample(source: DataSource): JsonValue | undefined {\n const providerId = providerIdOfSource(source)\n if (providerId !== undefined) {\n return providerPayload(providerId, source.kind === 'convex' ? source.config.args : undefined)\n }\n if (source.kind === 'cms') {\n return cmsDeliverySample(source.config as QueryCmsConfig)\n }\n return undefined\n}\n\n/**\n * A live CMS data source for one config-driven collection (EC-192). The binding\n * and condition pickers enumerate `data.sources`; deriving one source per\n * configured content collection — the same set the CMS uses\n * (`resolveDeploymentCollections`) — means what an editor can bind matches what\n * actually exists. The sample is captured by replaying the collection's list\n * query through the canonical content engine, so when a content session is\n * configured (Convex mode) the inferred field tokens are the collection's real\n * fields; with no session the source is listed but carries no sample.\n */\nexport function cmsSourceForCollection(collection: CollectionDef): DataSource {\n const source: DataSource = {\n kind: 'cms',\n id: `cms.${collection.id}`,\n label: humanizeName(collection.id),\n config: { collectionId: collection.id, filter: {}, limit: 20 },\n }\n const sample = captureSourceSample(source)\n return sample !== undefined ? captureSample(source, sample) : source\n}\n\n/**\n * Replay a CMS source's stored query over the configured content session, in\n * delivery shape (EC-166). Returns `undefined` when no session serves the\n * collection (playground / no backend) — the caller then leaves the source\n * sample-less rather than fabricating one. A query the engine rejects — or one\n * matching nothing — yields an empty list / `null`, never a crash.\n */\nfunction cmsDeliverySample(config: QueryCmsConfig): JsonValue | undefined {\n if (!hasContentSession()) return undefined\n if (playgroundCollection(config.collectionId) === undefined) return undefined\n if (config.documentId !== undefined) {\n const result = getCollectionDocument(config.collectionId, config.documentId)\n return (result.document as unknown as JsonValue) ?? null\n }\n const result = runCollectionListQuery(config.collectionId, listQueryOfConfig(config))\n return result.ok ? (result.documents as unknown as JsonValue) : []\n}\n"]}
@@ -1,42 +0,0 @@
1
- import type { DataSource } from '@elytracms/core/data-binding';
2
- import type { CmsDocument, CollectionDef } from '@elytracms/core/cms-core';
3
- import type { OperationResult, OperationsClient } from '../operations/index.js';
4
- /**
5
- * Data-source persistence for the Builder's data layer (LIVE-mode gap fix).
6
- *
7
- * EC-166/EC-167 made the builder provision per-section (`cms.section.<nodeId>`)
8
- * and per-template (`cms.template.<pageId>`) CMS sources, but only as session
9
- * state — the live delivery chain (`convex/delivery.ts` →
10
- * `apps/example-site/lib/live-content.ts`) had nothing to carry. Following the
11
- * EC-170 routing-store / EC-169 views-store pattern, sources persist as
12
- * documents in ONE system collection, one document per source, and every
13
- * mutation goes through the *existing* `@elytracms/core/operations` commands
14
- * (`schema.upsertCollection`, `documents.upsert`, `documents.delete`) — no new
15
- * operations surface, no direct repository access.
16
- *
17
- * The record round-trips the FULL canonical `DataSource` shape (including the
18
- * captured `sample` + inferred `schema` envelope and the cms config's `sort`):
19
- * `@elytracms/core/data-binding`'s `dataSourceSchema` is the source of truth, applied
20
- * on both write and read, so a malformed stored record is dropped on load —
21
- * never a crash, never a half-parsed source.
22
- */
23
- export declare const DATA_SOURCES_COLLECTION = "cms-data-sources";
24
- /** System collection holding one document per persisted {@link DataSource}. */
25
- export declare const dataSourcesCollectionDef: CollectionDef;
26
- export declare function sourceToDocument(source: DataSource): CmsDocument;
27
- export declare function documentToSource(document: CmsDocument): DataSource | undefined;
28
- /** Load every persisted data source of a project (malformed records dropped). */
29
- export declare function loadDataSources(operations: OperationsClient, projectId: string): Promise<OperationResult<DataSource[]>>;
30
- /**
31
- * Make sure the system collection exists in the project's CMS schema so
32
- * `documents.upsert` accepts source documents. Idempotent.
33
- */
34
- export declare function ensureDataSourcesCollection(_operations: OperationsClient, _projectId: string): Promise<OperationResult<void>>;
35
- /** Create or update a persisted data source (operations command). */
36
- export declare function saveDataSource(operations: OperationsClient, projectId: string, source: DataSource): Promise<OperationResult<void>>;
37
- /**
38
- * Delete a persisted data source. A source that was never persisted (pure
39
- * session state, e.g. a seed) deletes as a no-op — `not-found` is success
40
- * here, since the goal state ("no persisted record") already holds.
41
- */
42
- export declare function deleteDataSource(operations: OperationsClient, projectId: string, sourceId: string): Promise<OperationResult<void>>;
@@ -1,120 +0,0 @@
1
- import { dataSourceSchema } from '@elytracms/core/data-binding';
2
- /**
3
- * Data-source persistence for the Builder's data layer (LIVE-mode gap fix).
4
- *
5
- * EC-166/EC-167 made the builder provision per-section (`cms.section.<nodeId>`)
6
- * and per-template (`cms.template.<pageId>`) CMS sources, but only as session
7
- * state — the live delivery chain (`convex/delivery.ts` →
8
- * `apps/example-site/lib/live-content.ts`) had nothing to carry. Following the
9
- * EC-170 routing-store / EC-169 views-store pattern, sources persist as
10
- * documents in ONE system collection, one document per source, and every
11
- * mutation goes through the *existing* `@elytracms/core/operations` commands
12
- * (`schema.upsertCollection`, `documents.upsert`, `documents.delete`) — no new
13
- * operations surface, no direct repository access.
14
- *
15
- * The record round-trips the FULL canonical `DataSource` shape (including the
16
- * captured `sample` + inferred `schema` envelope and the cms config's `sort`):
17
- * `@elytracms/core/data-binding`'s `dataSourceSchema` is the source of truth, applied
18
- * on both write and read, so a malformed stored record is dropped on load —
19
- * never a crash, never a half-parsed source.
20
- */
21
- export const DATA_SOURCES_COLLECTION = 'cms-data-sources';
22
- /** System collection holding one document per persisted {@link DataSource}. */
23
- export const dataSourcesCollectionDef = {
24
- id: DATA_SOURCES_COLLECTION,
25
- kind: 'document',
26
- titleField: 'label',
27
- form: {
28
- label: 'Data sources',
29
- labelSingular: 'Data source',
30
- description: "System collection managed by the Builder's data layer.",
31
- },
32
- fields: [
33
- { name: 'label', type: 'text', validation: { required: true } },
34
- /** The source kind (`rest` | `cms` | `convex`), denormalized for table views. */
35
- { name: 'kind', type: 'text', validation: { required: true } },
36
- /** The FULL canonical DataSource, JSON-serialized (validated by Zod on read). */
37
- { name: 'config', type: 'text', validation: { required: true } },
38
- ],
39
- };
40
- // ---- Source ⇄ document mapping ----
41
- export function sourceToDocument(source) {
42
- const parsed = dataSourceSchema.parse(source);
43
- return {
44
- collection: DATA_SOURCES_COLLECTION,
45
- id: parsed.id,
46
- values: {
47
- label: parsed.label,
48
- kind: parsed.kind,
49
- config: JSON.stringify(parsed),
50
- },
51
- };
52
- }
53
- export function documentToSource(document) {
54
- const raw = document.values.config;
55
- let value;
56
- try {
57
- value = typeof raw === 'string' ? JSON.parse(raw) : undefined;
58
- }
59
- catch {
60
- return undefined;
61
- }
62
- const parsed = dataSourceSchema.safeParse(value);
63
- if (!parsed.success)
64
- return undefined;
65
- // The document id is the source id — a record whose payload disagrees with
66
- // its own key is malformed and never loads.
67
- return parsed.data.id === document.id ? parsed.data : undefined;
68
- }
69
- // ---- Reads & writes through @elytracms/operations ----
70
- /** Load every persisted data source of a project (malformed records dropped). */
71
- export async function loadDataSources(operations, projectId) {
72
- const result = await operations.documents.list({
73
- projectId,
74
- collection: DATA_SOURCES_COLLECTION,
75
- });
76
- if (!result.ok)
77
- return result;
78
- return {
79
- ok: true,
80
- value: result.value
81
- .map((record) => documentToSource(record.document))
82
- .filter((source) => source !== undefined),
83
- };
84
- }
85
- /**
86
- * Make sure the system collection exists in the project's CMS schema so
87
- * `documents.upsert` accepts source documents. Idempotent.
88
- */
89
- export async function ensureDataSourcesCollection(_operations, _projectId) {
90
- // No-op (EC-183, AD-11): the data-sources system collection is config-owned and
91
- // the backend stores documents without a schema-membership check — nothing to
92
- // register. `dataSourcesCollectionDef` remains the canonical document shape.
93
- return { ok: true, value: undefined };
94
- }
95
- /** Create or update a persisted data source (operations command). */
96
- export async function saveDataSource(operations, projectId, source) {
97
- const ensured = await ensureDataSourcesCollection(operations, projectId);
98
- if (!ensured.ok)
99
- return ensured;
100
- const result = await operations.documents.upsert({
101
- projectId,
102
- document: sourceToDocument(source),
103
- });
104
- return result.ok ? { ok: true, value: undefined } : result;
105
- }
106
- /**
107
- * Delete a persisted data source. A source that was never persisted (pure
108
- * session state, e.g. a seed) deletes as a no-op — `not-found` is success
109
- * here, since the goal state ("no persisted record") already holds.
110
- */
111
- export async function deleteDataSource(operations, projectId, sourceId) {
112
- const result = await operations.documents.delete({
113
- projectId,
114
- ref: { collection: DATA_SOURCES_COLLECTION, id: sourceId },
115
- });
116
- if (!result.ok && result.error.code === 'not-found')
117
- return { ok: true, value: undefined };
118
- return result.ok ? { ok: true, value: undefined } : result;
119
- }
120
- //# sourceMappingURL=sources-store.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sources-store.js","sourceRoot":"","sources":["../../../src/lib/data/sources-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { dataSourceSchema } from '@elytracms/core/data-binding'\nimport type { DataSource } from '@elytracms/core/data-binding'\nimport type { CmsDocument, CollectionDef } from '@elytracms/core/cms-core'\nimport type { OperationResult, OperationsClient } from '../operations'\n\n/**\n * Data-source persistence for the Builder's data layer (LIVE-mode gap fix).\n *\n * EC-166/EC-167 made the builder provision per-section (`cms.section.<nodeId>`)\n * and per-template (`cms.template.<pageId>`) CMS sources, but only as session\n * state — the live delivery chain (`convex/delivery.ts` →\n * `apps/example-site/lib/live-content.ts`) had nothing to carry. Following the\n * EC-170 routing-store / EC-169 views-store pattern, sources persist as\n * documents in ONE system collection, one document per source, and every\n * mutation goes through the *existing* `@elytracms/core/operations` commands\n * (`schema.upsertCollection`, `documents.upsert`, `documents.delete`) — no new\n * operations surface, no direct repository access.\n *\n * The record round-trips the FULL canonical `DataSource` shape (including the\n * captured `sample` + inferred `schema` envelope and the cms config's `sort`):\n * `@elytracms/core/data-binding`'s `dataSourceSchema` is the source of truth, applied\n * on both write and read, so a malformed stored record is dropped on load —\n * never a crash, never a half-parsed source.\n */\n\nexport const DATA_SOURCES_COLLECTION = 'cms-data-sources'\n\n/** System collection holding one document per persisted {@link DataSource}. */\nexport const dataSourcesCollectionDef: CollectionDef = {\n id: DATA_SOURCES_COLLECTION,\n kind: 'document',\n titleField: 'label',\n form: {\n label: 'Data sources',\n labelSingular: 'Data source',\n description: \"System collection managed by the Builder's data layer.\",\n },\n fields: [\n { name: 'label', type: 'text', validation: { required: true } },\n /** The source kind (`rest` | `cms` | `convex`), denormalized for table views. */\n { name: 'kind', type: 'text', validation: { required: true } },\n /** The FULL canonical DataSource, JSON-serialized (validated by Zod on read). */\n { name: 'config', type: 'text', validation: { required: true } },\n ],\n}\n\n// ---- Source ⇄ document mapping ----\n\nexport function sourceToDocument(source: DataSource): CmsDocument {\n const parsed = dataSourceSchema.parse(source)\n return {\n collection: DATA_SOURCES_COLLECTION,\n id: parsed.id,\n values: {\n label: parsed.label,\n kind: parsed.kind,\n config: JSON.stringify(parsed),\n },\n }\n}\n\nexport function documentToSource(document: CmsDocument): DataSource | undefined {\n const raw = document.values.config\n let value: unknown\n try {\n value = typeof raw === 'string' ? JSON.parse(raw) : undefined\n } catch {\n return undefined\n }\n const parsed = dataSourceSchema.safeParse(value)\n if (!parsed.success) return undefined\n // The document id is the source id — a record whose payload disagrees with\n // its own key is malformed and never loads.\n return parsed.data.id === document.id ? parsed.data : undefined\n}\n\n// ---- Reads & writes through @elytracms/operations ----\n\n/** Load every persisted data source of a project (malformed records dropped). */\nexport async function loadDataSources(\n operations: OperationsClient,\n projectId: string,\n): Promise<OperationResult<DataSource[]>> {\n const result = await operations.documents.list({\n projectId,\n collection: DATA_SOURCES_COLLECTION,\n })\n if (!result.ok) return result\n return {\n ok: true,\n value: result.value\n .map((record) => documentToSource(record.document))\n .filter((source): source is DataSource => source !== undefined),\n }\n}\n\n/**\n * Make sure the system collection exists in the project's CMS schema so\n * `documents.upsert` accepts source documents. Idempotent.\n */\nexport async function ensureDataSourcesCollection(\n _operations: OperationsClient,\n _projectId: string,\n): Promise<OperationResult<void>> {\n // No-op (EC-183, AD-11): the data-sources system collection is config-owned and\n // the backend stores documents without a schema-membership check — nothing to\n // register. `dataSourcesCollectionDef` remains the canonical document shape.\n return { ok: true, value: undefined }\n}\n\n/** Create or update a persisted data source (operations command). */\nexport async function saveDataSource(\n operations: OperationsClient,\n projectId: string,\n source: DataSource,\n): Promise<OperationResult<void>> {\n const ensured = await ensureDataSourcesCollection(operations, projectId)\n if (!ensured.ok) return ensured\n const result = await operations.documents.upsert({\n projectId,\n document: sourceToDocument(source),\n })\n return result.ok ? { ok: true, value: undefined } : result\n}\n\n/**\n * Delete a persisted data source. A source that was never persisted (pure\n * session state, e.g. a seed) deletes as a no-op — `not-found` is success\n * here, since the goal state (\"no persisted record\") already holds.\n */\nexport async function deleteDataSource(\n operations: OperationsClient,\n projectId: string,\n sourceId: string,\n): Promise<OperationResult<void>> {\n const result = await operations.documents.delete({\n projectId,\n ref: { collection: DATA_SOURCES_COLLECTION, id: sourceId },\n })\n if (!result.ok && result.error.code === 'not-found') return { ok: true, value: undefined }\n return result.ok ? { ok: true, value: undefined } : result\n}\n"]}
@@ -1,35 +0,0 @@
1
- import type { Locale } from '@elytracms/core/cms-core';
2
- import type { BindingReference } from '@elytracms/core/project-graph';
3
- import type { DataSource } from '@elytracms/core/data-binding';
4
- import type { DisplayedFallback } from './fallback.js';
5
- /**
6
- * Fallback visibility for BOUND content (EC-176): given a stored content
7
- * binding and its CMS source, decide whether the value the canvas currently
8
- * previews for the active locale is served via EC-018 locale fallback. Used
9
- * by the inspector's Content tab — the surface that explains what a bound
10
- * field shows (the canvas overlay itself is out of this wave's reach; see
11
- * EC-176 notes).
12
- *
13
- * Deterministic over the playground delivery engine: the same
14
- * `@elytracms/core/content` resolution that produced the previewed value reports
15
- * which fields fell back (`ResolutionInfo.localeFallbacks`), so the label can
16
- * never disagree with the displayed value.
17
- */
18
- /** A previewed bound value that fell back, plus which field it came from. */
19
- export interface BoundValueFallback extends DisplayedFallback {
20
- field: string;
21
- }
22
- /**
23
- * Whether the bound field's previewed value (first entry for list/repeater
24
- * bindings, the bound document otherwise) is a locale fallback in `locale`.
25
- * `null` when the binding is not CMS-shaped, points at no field, or the value
26
- * is a real translation.
27
- */
28
- export declare function cmsBindingFallback(binding: BindingReference, source: DataSource | undefined,
29
- /**
30
- * The locale to judge fallback in. Optional (EC-479): the ambient studio locale
31
- * is `null` until a project's locale config is installed, and the content engine
32
- * then resolves in the session's own default — so a caller that has no locale
33
- * omits it rather than substituting one.
34
- */
35
- locale?: Locale): BoundValueFallback | null;