@elevasis/sdk 1.48.0 → 1.50.0

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 (56) hide show
  1. package/dist/chunk-MGZZ4HL4.js +4399 -0
  2. package/dist/chunk-VYWGWJRW.js +130 -0
  3. package/dist/chunk-YJDXRHNP.js +7901 -0
  4. package/dist/cli.cjs +949 -281
  5. package/dist/index.d.ts +1031 -48
  6. package/dist/index.js +2 -7597
  7. package/dist/node/index.d.ts +3 -3675
  8. package/dist/node/index.js +2 -124
  9. package/dist/test-utils/index.d.ts +2 -12051
  10. package/dist/test-utils/index.js +113 -27891
  11. package/dist/worker/index.d.ts +548 -12264
  12. package/dist/worker/index.js +3 -7400
  13. package/package.json +12 -4
  14. package/reference/_navigation.md +4 -4
  15. package/reference/_reference-manifest.json +1 -1
  16. package/reference/core/index.mdx +6 -4
  17. package/reference/index.mdx +11 -5
  18. package/reference/packages/core/src/README.md +46 -44
  19. package/reference/packages/core/src/content/README.md +16 -12
  20. package/reference/rules/agent-start-here.md +1 -1
  21. package/reference/rules/frontend.md +3 -1
  22. package/reference/rules/package-taxonomy.md +7 -5
  23. package/reference/rules/ui.md +31 -5
  24. package/reference/rules/vibe-intents.md +2 -2
  25. package/reference/rules/vibe.md +30 -10
  26. package/reference/scaffold/recipes/extend-content.md +82 -3
  27. package/reference/scaffold/recipes/gate-by-feature-or-admin.md +8 -6
  28. package/reference/scaffold/ui/feature-flags-and-gating.md +11 -1
  29. package/reference/sdk/cli-management.mdx +284 -139
  30. package/reference/sdk/cli.mdx +136 -88
  31. package/reference/sdk/define-builders.mdx +1 -1
  32. package/reference/sdk/deployment/command-center.mdx +2 -2
  33. package/reference/sdk/deployment/index.mdx +24 -7
  34. package/reference/sdk/exports.mdx +4 -4
  35. package/reference/sdk/framework/agent.mdx +4 -3
  36. package/reference/sdk/framework/index.mdx +1 -1
  37. package/reference/sdk/framework/project-structure.mdx +34 -23
  38. package/reference/sdk/framework/tutorial-system.mdx +1 -1
  39. package/reference/sdk/getting-started.mdx +25 -52
  40. package/reference/sdk/index.mdx +3 -3
  41. package/reference/sdk/platform-tools/adapters-integration.mdx +1 -1
  42. package/reference/sdk/platform-tools/adapters-platform.mdx +1 -1
  43. package/reference/sdk/platform-tools/type-safety.mdx +1 -1
  44. package/reference/sdk/resources/patterns.mdx +10 -11
  45. package/reference/sdk/resources/types.mdx +15 -9
  46. package/reference/sdk/templates/data-enrichment.mdx +1 -1
  47. package/reference/sdk/templates/email-sender.mdx +1 -1
  48. package/reference/sdk/templates/index.mdx +47 -47
  49. package/reference/sdk/templates/lead-scorer.mdx +1 -1
  50. package/reference/sdk/templates/pdf-generator.mdx +42 -24
  51. package/reference/sdk/templates/recurring-job.mdx +20 -15
  52. package/reference/sdk/templates/text-classifier.mdx +1 -1
  53. package/reference/sdk/templates/web-scraper.mdx +9 -5
  54. package/reference/sdk/troubleshooting.mdx +72 -1
  55. package/reference/ui/exports.mdx +1 -1
  56. package/reference/ui/index.mdx +2 -2
@@ -104,6 +104,70 @@ Then set `content.config.defaultPipelineId` if this should be the pipeline the U
104
104
 
105
105
  **The step catalog name is load-bearing.** `content:catalog/{pipelineId}-steps` must match the pipeline record's `stepCatalog` exactly. Nothing type-checks that string.
106
106
 
107
+ ### Or declare it once with `defineContentPipeline`
108
+
109
+ The hand-written form above is fine and stays supported, but a pipeline is actually declared in **five** places, and missing one fails at runtime rather than at build:
110
+
111
+ 1. the `content:catalog/pipeline` entry
112
+ 2. the step catalog, whose id must match the string above exactly
113
+ 3. `content.config.defaultPipelineId`
114
+ 4. `apiInterface.readinessContract.requiredCatalogs`
115
+ 5. each participating resource's `ontology.usesCatalogs`
116
+
117
+ `defineContentPipeline` takes one declaration and derives all five.
118
+
119
+ <!-- doc-snippet:skip: illustrative excerpt -- references project-local descriptors -->
120
+
121
+ ```ts
122
+ import { buildContentPipelineCatalogs, defineContentPipeline } from '@elevasis/core/organization-model'
123
+ import { z } from 'zod'
124
+
125
+ export const podcastToClips = defineContentPipeline({
126
+ systemPath: 'content',
127
+ id: 'podcast-to-clips',
128
+ label: 'Podcast to Clips',
129
+ workspaceRoute: '/content/clips',
130
+ steps: [
131
+ {
132
+ key: 'transcript',
133
+ actor: 'workflow',
134
+ review: 'none',
135
+ advancesTo: 'clip-selection',
136
+ resource: transcribeEpisode,
137
+ payload: z.object({ transcriptPath: z.string() })
138
+ },
139
+ {
140
+ key: 'clip-selection',
141
+ actor: 'agent',
142
+ review: 'required',
143
+ advancesTo: 'publish',
144
+ resource: proposeClips,
145
+ payload: z.object({ candidateIds: z.array(z.string()) })
146
+ },
147
+ { key: 'publish', actor: 'workflow', review: 'none', resource: publishClip }
148
+ ]
149
+ })
150
+
151
+ // Inside the content System:
152
+ // ontology.catalogTypes: { ...buildContentPipelineCatalogs([podcastToClips]), ...yourOtherCatalogs }
153
+ // config: { defaultPipelineId: podcastToClips.defaultPipelineId }
154
+ // apiInterface: { resourceIds: podcastToClips.resourceIds,
155
+ // readinessContract: { requiredCatalogs: [...podcastToClips.requiredCatalogs,
156
+ // 'content:catalog/status'] } }
157
+ // And on each participating resource:
158
+ // ontology: { usesCatalogs: podcastToClips.usesCatalogs }
159
+ ```
160
+
161
+ Three failure modes stop being possible:
162
+
163
+ - **`resource` is a descriptor reference, not a string.** Deleting a workflow is a type error at the declaration site instead of a readiness failure at deploy.
164
+ - **`payloadFields` derives from `payload`.** The declared field list and the schema your producer emits cannot drift, because there is only one of them. Use `.meta({ label, contentFieldType })` on a field to override a derived label or force `'text'` — nothing in a Zod schema distinguishes a long body from a short string.
165
+ - **`workspaceRoute` gets a type**, and is validated for the leading slash at declaration time. The raw catalog entry type is open, which is how a route rename can point production at a dead path with every gate green.
166
+
167
+ `buildContentPipelineCatalogs` exists because the pipeline catalog is **shared**: every pipeline in a System is an entry in the same `content:catalog/pipeline` record, so spreading two definitions independently would have the second silently replace the first. Call it once per System with all of them.
168
+
169
+ Steps stay catalog data either way. Adding one remains an org-model edit plus a workflow, never a shared-UI or route change — the helper removes no expressiveness, it stops one fact being written in five files.
170
+
107
171
  ## 2. Produce a Step in a Workflow
108
172
 
109
173
  One workflow per step. The workflow records an **attempt** against the item, tagged with the step it satisfies.
@@ -178,13 +242,17 @@ export const clipSelectionWorkflow: WorkflowDefinition = {
178
242
  }
179
243
  ```
180
244
 
181
- `ContentToolMap` is the full surface, 13 methods: `createItem`, `getItem`, `listItems`, `updateItem`, `createAttempt`, `listAttempts`, `updateAttempt`, `createSourceAsset`, `getSourceAsset`, `listSourceAssets`, `updateSourceAsset`, `createDistribution`, `updateDistribution`. `organizationId` is injected server-side — never pass it from workflow code.
245
+ `ContentToolMap` is the full surface, 19 methods: `createItem`, `getItem`, `listItems`, `updateItem`, `addItemSourceAsset`, `removeItemSourceAsset`, `reorderItemSourceAssets`, `updateItemSourceAsset`, `createAttempt`, `listAttempts`, `updateAttempt`, `createSourceAsset`, `getSourceAsset`, `listSourceAssets`, `updateSourceAsset`, `getDistribution`, `listDistributions`, `createDistribution`, `updateDistribution`. `organizationId` is injected server-side — never pass it from workflow code.
182
246
 
183
247
  **`reviewItem` is deliberately not on this list.** Clearing a `queued` review gate is operator work, done through `elevasis-sdk content:review` (see `apps/docs/content/docs/sdk/sdk/cli-management.mdx`), not something a producer workflow can call. A producer approving its own output collapses the gate the review step exists to provide.
184
248
 
185
249
  ### Source Assets
186
250
 
187
- `createSourceAsset`, `getSourceAsset`, `listSourceAssets`, and `updateSourceAsset` manage the raw material a producer works from -- an uploaded podcast episode, a transcript, a reference file -- independent of any content item. A producer lists or fetches source assets to pick material, then references the chosen one (`sourceAssetId`) when it calls `createItem`. Source assets have their own `kind`-keyed payload envelope, declared per-kind in the content System's OM catalog, the same pattern `createItem`'s payload uses.
251
+ `createSourceAsset`, `getSourceAsset`, `listSourceAssets`, and `updateSourceAsset` manage the raw material a producer works from -- an uploaded podcast episode, a transcript, a reference file -- independent of any content item. A producer lists or fetches source assets to pick material, then references the chosen one when it calls `createItem`. Source assets have their own `kind`-keyed payload envelope, declared per-kind in the content System's OM catalog, the same pattern `createItem`'s payload uses.
252
+
253
+ ### Item Membership (Which Assets Are In Which Item)
254
+
255
+ A content item's membership in `content_item_source_assets` -- which source assets it carries, in what slide order, with what per-slide crop and alt text -- is a separate concern from the source asset row itself, and it is owned entirely by four dedicated methods: `addItemSourceAsset`, `removeItemSourceAsset`, `reorderItemSourceAssets`, and `updateItemSourceAsset`. `createItem` also accepts an initial ordered `sourceAssets` list, so a carousel can be created with its full membership in one call; `updateItem` deliberately does not accept a `sourceAssets` field, and every membership change after creation goes through the four methods above.
188
256
 
189
257
  ## 3. Give Producers Their Instructions
190
258
 
@@ -299,12 +367,23 @@ video, then copy plus a schedule, is three screens, not one:
299
367
  <!-- doc-snippet:skip: illustrative excerpt, not a standalone compilable file -->
300
368
 
301
369
  ```ts
302
- 'render-video': { actor: 'workflow', reviewMode: 'queued', workspaceRoute: '/content/review-render' }
370
+ 'render-video': { actor: 'workflow', reviewMode: 'required', workspaceRoute: '/content/review-render' }
303
371
  ```
304
372
 
305
373
  Resolution is override-then-fallback — the step's route when it declares one, the pipeline's
306
374
  otherwise. Use `resolveContentWorkspaceRoute` rather than reading either field directly.
307
375
 
376
+ **`reviewMode` says whether, not where.** It has two values, `'required'` and `'none'`. Where a
377
+ required review happens is derived from the route above: a step whose route resolves is reviewed on
378
+ your screen, and one whose route does not is reviewed in the shared queue. The resolved answer is
379
+ `step.reviewVenue` (`'none' | 'queue' | 'workspace'`) — read that rather than recombining
380
+ `reviewMode` and `workspaceRoute` yourself.
381
+
382
+ The field used to take `'live' | 'queued' | 'none'`, where `'live'` and `'queued'` differed only in
383
+ location. They are **not** accepted as aliases: a step still declaring one fails validation and is
384
+ dropped from the pipeline. If you are upgrading, `'live'` and `'queued'` both become `'required'` —
385
+ declare a `workspaceRoute` wherever you previously meant `'live'`.
386
+
308
387
  **Declaring it is expected, and omitting it is supported.** A pipeline with no `workspaceRoute`
309
388
  keeps the shared review page's action bar, so you can clear gates before you have built anything.
310
389
  Once you declare a route, that page defers to it and links out instead. The route is an
@@ -52,23 +52,25 @@ Dotted IDs such as `analytics.reports` inherit lifecycle and shell placement fro
52
52
 
53
53
  ## Route-level system gate
54
54
 
55
+ `ProtectedSystemRoute` is the shape a System's top-level route uses. It composes `ProtectedRoute` + `AccessGuard` and supplies `SystemUnavailableState` as the fallback, so a denial renders the reason `checkAccess` computed instead of a blank screen:
56
+
55
57
  ```tsx
56
- import { AccessGuard, ProtectedRoute } from '@elevasis/ui/auth'
58
+ import { ProtectedSystemRoute } from '@elevasis/ui/features/auth'
57
59
  import { createFileRoute, Outlet } from '@tanstack/react-router'
58
60
 
59
61
  export const Route = createFileRoute('/analytics')({ component: AnalyticsLayout })
60
62
 
61
63
  function AnalyticsLayout() {
62
64
  return (
63
- <ProtectedRoute>
64
- <AccessGuard accessKey="analytics">
65
- <Outlet />
66
- </AccessGuard>
67
- </ProtectedRoute>
65
+ <ProtectedSystemRoute accessKey="analytics">
66
+ <Outlet />
67
+ </ProtectedSystemRoute>
68
68
  )
69
69
  }
70
70
  ```
71
71
 
72
+ Writing the pairing by hand is still correct and is exactly what the above expands to. Reach for it when the gate is not a whole System's route -- see the admin-only recipe below.
73
+
72
74
  The sidebar is derived from `OrganizationModel.systems`; hiding a node there is display behavior only. Keep route guards in place for direct URL access.
73
75
 
74
76
  ## Admin-only route
@@ -18,7 +18,17 @@ The shell derives visible sidebar entries from `shellModel.topLevel()` and `shel
18
18
 
19
19
  ## Route Guards
20
20
 
21
- Navigation visibility is cosmetic. Always guard routes directly:
21
+ Navigation visibility is cosmetic. Always guard routes directly. For a System's top-level route use `ProtectedSystemRoute`, which composes `ProtectedRoute` + `AccessGuard` and renders `SystemUnavailableState` on denial instead of a blank fallback:
22
+
23
+ ```tsx
24
+ import { ProtectedSystemRoute } from '@elevasis/ui/features/auth'
25
+
26
+ <ProtectedSystemRoute accessKey="sales.crm">
27
+ <Outlet />
28
+ </ProtectedSystemRoute>
29
+ ```
30
+
31
+ That expands to the pairing below, which stays correct for gating that is not a whole System's route:
22
32
 
23
33
  ```tsx
24
34
  import { AccessGuard } from '@elevasis/ui/auth'