@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.
- package/dist/chunk-MGZZ4HL4.js +4399 -0
- package/dist/chunk-VYWGWJRW.js +130 -0
- package/dist/chunk-YJDXRHNP.js +7901 -0
- package/dist/cli.cjs +949 -281
- package/dist/index.d.ts +1031 -48
- package/dist/index.js +2 -7597
- package/dist/node/index.d.ts +3 -3675
- package/dist/node/index.js +2 -124
- package/dist/test-utils/index.d.ts +2 -12051
- package/dist/test-utils/index.js +113 -27891
- package/dist/worker/index.d.ts +548 -12264
- package/dist/worker/index.js +3 -7400
- package/package.json +12 -4
- package/reference/_navigation.md +4 -4
- package/reference/_reference-manifest.json +1 -1
- package/reference/core/index.mdx +6 -4
- package/reference/index.mdx +11 -5
- package/reference/packages/core/src/README.md +46 -44
- package/reference/packages/core/src/content/README.md +16 -12
- package/reference/rules/agent-start-here.md +1 -1
- package/reference/rules/frontend.md +3 -1
- package/reference/rules/package-taxonomy.md +7 -5
- package/reference/rules/ui.md +31 -5
- package/reference/rules/vibe-intents.md +2 -2
- package/reference/rules/vibe.md +30 -10
- package/reference/scaffold/recipes/extend-content.md +82 -3
- package/reference/scaffold/recipes/gate-by-feature-or-admin.md +8 -6
- package/reference/scaffold/ui/feature-flags-and-gating.md +11 -1
- package/reference/sdk/cli-management.mdx +284 -139
- package/reference/sdk/cli.mdx +136 -88
- package/reference/sdk/define-builders.mdx +1 -1
- package/reference/sdk/deployment/command-center.mdx +2 -2
- package/reference/sdk/deployment/index.mdx +24 -7
- package/reference/sdk/exports.mdx +4 -4
- package/reference/sdk/framework/agent.mdx +4 -3
- package/reference/sdk/framework/index.mdx +1 -1
- package/reference/sdk/framework/project-structure.mdx +34 -23
- package/reference/sdk/framework/tutorial-system.mdx +1 -1
- package/reference/sdk/getting-started.mdx +25 -52
- package/reference/sdk/index.mdx +3 -3
- package/reference/sdk/platform-tools/adapters-integration.mdx +1 -1
- package/reference/sdk/platform-tools/adapters-platform.mdx +1 -1
- package/reference/sdk/platform-tools/type-safety.mdx +1 -1
- package/reference/sdk/resources/patterns.mdx +10 -11
- package/reference/sdk/resources/types.mdx +15 -9
- package/reference/sdk/templates/data-enrichment.mdx +1 -1
- package/reference/sdk/templates/email-sender.mdx +1 -1
- package/reference/sdk/templates/index.mdx +47 -47
- package/reference/sdk/templates/lead-scorer.mdx +1 -1
- package/reference/sdk/templates/pdf-generator.mdx +42 -24
- package/reference/sdk/templates/recurring-job.mdx +20 -15
- package/reference/sdk/templates/text-classifier.mdx +1 -1
- package/reference/sdk/templates/web-scraper.mdx +9 -5
- package/reference/sdk/troubleshooting.mdx +72 -1
- package/reference/ui/exports.mdx +1 -1
- 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,
|
|
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
|
|
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: '
|
|
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 {
|
|
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
|
-
<
|
|
64
|
-
<
|
|
65
|
-
|
|
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'
|