@synap-core/workspace-templates 0.9.1 → 0.9.3

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/index.d.ts CHANGED
@@ -15,12 +15,55 @@ interface TemplateAppRef {
15
15
  /** For kind='stored': the cell key registered in the cell registry */
16
16
  cellKey?: string;
17
17
  /** For kind='stored': default props passed to the cell */
18
- props?: Record<string, unknown>;
18
+ props?: Record<string, WorkspaceJsonValue>;
19
19
  /** For kind='url': external URL to open */
20
20
  url?: string;
21
21
  /** Human label */
22
22
  title?: string;
23
23
  }
24
+ type WorkspaceJsonValue = string | number | boolean | null | WorkspaceJsonValue[] | {
25
+ [key: string]: WorkspaceJsonValue;
26
+ };
27
+ type WorkspacePrimarySurface = {
28
+ kind: 'app';
29
+ appId: string;
30
+ rendererType: 'native';
31
+ title?: string;
32
+ props?: Record<string, WorkspaceJsonValue>;
33
+ } | {
34
+ kind: 'app';
35
+ appId: string;
36
+ rendererType: 'external';
37
+ url: string;
38
+ title?: string;
39
+ props?: Record<string, WorkspaceJsonValue>;
40
+ } | {
41
+ kind: 'cell';
42
+ cellKey: string;
43
+ title?: string;
44
+ props?: Record<string, WorkspaceJsonValue>;
45
+ } | {
46
+ kind: 'view';
47
+ viewId: string;
48
+ title?: string;
49
+ };
50
+ /**
51
+ * Authoring-time view reference. View IDs do not exist until provisioning, so
52
+ * templates and packages address a view by its stable authored name and/or
53
+ * slug. The pod resolves this to `WorkspacePrimarySurface` before persistence.
54
+ */
55
+ type WorkspacePrimarySurfaceViewReference = {
56
+ kind: 'view';
57
+ viewName: string;
58
+ viewSlug?: string;
59
+ title?: string;
60
+ } | {
61
+ kind: 'view';
62
+ viewName?: string;
63
+ viewSlug: string;
64
+ title?: string;
65
+ };
66
+ type TemplatePrimarySurface = WorkspacePrimarySurface | WorkspacePrimarySurfaceViewReference;
24
67
  interface TemplateProperty {
25
68
  slug: string;
26
69
  label: string;
@@ -41,8 +84,7 @@ interface TemplateProfile {
41
84
  * Visibility of the profile DEFINITION — who can USE this vocabulary.
42
85
  * `system` = pod-wide; `shared` = ONE row reachable from other workspaces via
43
86
  * a `profile_workspace_access` grant; omit or `workspace` = this workspace
44
- * only. Orthogonal to `entityScope` below — do not conflate the two axes.
45
- */
87
+ * only. Orthogonal to `entityScope` below — do not conflate the two axes. */
46
88
  scope?: string;
47
89
  /**
48
90
  * Placement of the profile's ENTITY ROWS — where the data LANDS.
@@ -64,7 +106,7 @@ interface TemplateProfile {
64
106
  /** Semantic slug for system profiles. */
65
107
  semanticSlug?: string;
66
108
  /** Kind vs role profile. Omit for default ('kind'). */
67
- profileKind?: "kind" | "role";
109
+ profileKind?: 'kind' | 'role';
68
110
  /** For profileKind='role': which base-kind profile slugs this role can attach to. */
69
111
  applicableKinds?: string[];
70
112
  properties: TemplateProperty[];
@@ -149,7 +191,14 @@ interface TemplateSidebarItem {
149
191
  interface TemplateLayoutConfig {
150
192
  pinnedApps?: string[];
151
193
  defaultView?: string;
152
- /** The first-screen app — auto-opened on workspace switch */
194
+ /**
195
+ * Canonical first screen for this workspace.
196
+ *
197
+ * `undefined` means the template has no opinion during reconciliation,
198
+ * `null` explicitly returns the workspace to its home surface, and a
199
+ * descriptor replaces the current primary surface. */
200
+ primarySurface?: TemplatePrimarySurface | null;
201
+ /** Compatibility input only. New templates should use `app:` or `primarySurface`. */
153
202
  defaultApp?: string | null;
154
203
  theme?: string;
155
204
  sidebarItems?: TemplateSidebarItem[];
@@ -1036,8 +1085,8 @@ declare function groupByDomain<T>(items: readonly T[], domainOf: (item: T) => st
1036
1085
  * • `scripts/check-converters.mjs` — injects the real converters, exits non-zero.
1037
1086
  * • CP / pod / CLI (from dist) — import `validateTemplate` + `SYSTEM_PROFILES`.
1038
1087
  *
1039
- * ZERO RUNTIME IMPORTS BY DESIGN. `build.mjs` loads this file directly under
1040
- * Node's type-stripping BEFORE `tsup` builds `dist/`. A static
1088
+ * NO RUNTIME IMPORT THAT FAILS AT STRIP-TIME. `build.mjs` loads this file
1089
+ * directly under Node's type-stripping BEFORE `tsup` builds `dist/`. A static
1041
1090
  * `import { toWorkspaceDefinition } from "./define.js"` would fail to resolve at
1042
1091
  * that moment (define → templates.js does not exist pre-build, and Node's
1043
1092
  * type-stripper does not remap a `.js` specifier to its `.ts` source). So the
@@ -1045,6 +1094,14 @@ declare function groupByDomain<T>(items: readonly T[], domainOf: (item: T) => st
1045
1094
  * YAML/bento rules run everywhere; the converter-parity rules run wherever the
1046
1095
  * caller can supply the compiled converters (check-converters.mjs, from dist).
1047
1096
  * `import type` lines are fully erased by the type-stripper, so they are safe.
1097
+ *
1098
+ * THE ONE RUNTIME IMPORT: `./validate-flow.ts` (the pure automation-flow
1099
+ * validator). It is imported with the `.ts` extension SPECIFICALLY so it resolves
1100
+ * under Node's type-stripper at build.mjs time (a `.js` specifier would NOT — see
1101
+ * above), and it has NO generated dependency (unlike `./define.js` → templates)
1102
+ * so it is always present. It is DB-free (only `import type` internally), so it
1103
+ * needs no injection: the flow-node contract check runs for every caller
1104
+ * (build / check-converters / CLI / CP) automatically.
1048
1105
  */
1049
1106
 
1050
1107
  interface ValidationError {
@@ -1159,6 +1216,103 @@ declare function collectBaseProfileSlugs(data: WorkspaceYaml, resolveDep: (slug:
1159
1216
  */
1160
1217
  declare function validateTemplate(tpl: WorkspaceYaml, opts?: ValidateOptions): ValidateResult;
1161
1218
 
1219
+ /**
1220
+ * Node-contract validation for automation flow definitions.
1221
+ *
1222
+ * The persist doors and the install/apply doors validate flow SHAPE only
1223
+ * (`{ nodes: [], edges: [] }`). A semantically-broken flow — a `channel_message`
1224
+ * output with a typo'd `channelType`, a `capability` node with no `verbId`, an
1225
+ * edge to a nonexistent node, a cycle, an unknown `node.type` — passes the shape
1226
+ * check and only fails at RUNTIME (automation-executor.ts throws mid-run). For a
1227
+ * template automation this is worse: it validates green at author time and then
1228
+ * SILENTLY never fires on a live pod.
1229
+ *
1230
+ * `validateFlowDefinition` closes that gap: a PURE, synchronous, DB-free function
1231
+ * that rejects malformed flows with actionable per-node errors. It is called by
1232
+ * `validateTemplate` (this package) so `synap market validate` / the build gate /
1233
+ * the CP publish door all run the same contract check, and by the pod's persist
1234
+ * doors.
1235
+ *
1236
+ * ── SSOT-IN-WAITING — temporary duplicate ────────────────────────────────────
1237
+ * This is a PURE copy of synap-backend's
1238
+ * `packages/api/src/services/automations/validate-flow.ts` (the current runtime
1239
+ * SSOT, which carries the `@synap/database` COMPILE-TIME drift guard). This copy
1240
+ * exists so `validateTemplate` — which runs in THIS published, backend-independent
1241
+ * package (CLI `synap market validate` / build gate / CP publish) — can catch
1242
+ * broken automation flows at AUTHOR time. It is intentionally free of any
1243
+ * `@synap/database` import (this package depends only on zod + js-yaml), so the
1244
+ * compile-time drift guard cannot live here; it stays in backend.
1245
+ *
1246
+ * The two copies are kept byte-identical (logic) by `validate-flow.drift.test.ts`
1247
+ * in this package, which reads BOTH files and fails if they diverge.
1248
+ *
1249
+ * CUTOVER (collapse to ONE SSOT): publish `@synap-core/workspace-templates`, bump
1250
+ * synap-backend (`^0.7.1`) and CP (`^0.9.2`) deps to that version, then replace
1251
+ * backend's validate-flow.ts body with a re-export FROM HERE (keeping backend's
1252
+ * `AssertExact` guard binding these arrays to the `@synap/database` unions).
1253
+ * Until then: keep the constant arrays below identical to `@synap/database`
1254
+ * `schema/automations.ts`.
1255
+ */
1256
+ /** The 21 node types — `AutomationNodeBase.type` union (schema/automations.ts). */
1257
+ declare const FLOW_NODE_TYPES: readonly ["trigger", "command", "condition", "delay", "output", "loop", "transform", "fetch", "query", "entity_read", "related_entities", "guard", "compute", "select", "claim", "messages_query", "switch", "skill", "capability", "sub_automation", "playbook_run"];
1258
+ /** The 11 output types — `OutputNodeDef.data.outputType` (schema/automations.ts). */
1259
+ declare const FLOW_OUTPUT_TYPES: readonly ["notification", "entity_create", "entity_update", "facet_attach", "facet_update", "facet_detach", "relation_create", "webhook", "channel_message", "session_update", "set_state"];
1260
+ /**
1261
+ * The known `config.channelType` values for a `channel_message` output. Mirrors
1262
+ * the executor's channel_message resolution (automation-executor.ts). The output
1263
+ * channel is CONTEXT-DERIVED — a channel_message is valid with `channelId`, OR
1264
+ * `channelEntityRef` (an entity-id expression → that entity's internal channel),
1265
+ * OR a `channelType` from this set, OR NOTHING (the executor defaults a targetless
1266
+ * channel_message to the automation's own run channel). A `channelType` that is
1267
+ * PRESENT but not one of these is still a typo → still an error.
1268
+ */
1269
+ declare const CHANNEL_MESSAGE_CHANNEL_TYPES: readonly ["personal_thread", "proactive", "subjectEntity"];
1270
+ interface FlowValidationError {
1271
+ /** Node the error is about (absent for graph-level / edge-level errors). */
1272
+ nodeId?: string;
1273
+ /** Edge the error is about (for dangling-reference / bad-handle errors). */
1274
+ edgeId?: string;
1275
+ /** Stable machine code — see the `code` literals below. */
1276
+ code: string;
1277
+ /** Human-actionable message naming what is wrong and how to fix it. */
1278
+ message: string;
1279
+ }
1280
+ interface FlowValidationResult {
1281
+ valid: boolean;
1282
+ errors: FlowValidationError[];
1283
+ }
1284
+ /**
1285
+ * Optional catalog resolvers. When provided, existence checks run; when omitted,
1286
+ * those checks are SKIPPED (structural + contract checks always run). Kept
1287
+ * synchronous so the whole function stays pure — the caller pre-loads whatever
1288
+ * it needs and passes predicates.
1289
+ */
1290
+ interface FlowValidationResolvers {
1291
+ /** Does this capability verb (skill name) exist in the workspace catalog? */
1292
+ verbExists?: (verbId: string) => boolean;
1293
+ /** Does the referenced skill exist (by id OR by name)? */
1294
+ skillExists?: (ref: {
1295
+ skillId?: string;
1296
+ skillName?: string;
1297
+ }) => boolean;
1298
+ /** Does the referenced playbook exist? */
1299
+ playbookExists?: (ref: {
1300
+ playbookId?: string;
1301
+ playbookName?: string;
1302
+ }) => boolean;
1303
+ }
1304
+ /**
1305
+ * Validate an automation flow definition against the node contract.
1306
+ * Pure and synchronous. `resolvers` gate the catalog-existence checks only.
1307
+ */
1308
+ declare function validateFlowDefinition(flow: unknown, resolvers?: FlowValidationResolvers): FlowValidationResult;
1309
+ /**
1310
+ * Convenience helper for the persist doors: run the validator and, if invalid,
1311
+ * return a single joined actionable message (else null). Keeps the door code a
1312
+ * one-liner and the message format consistent across create/update.
1313
+ */
1314
+ declare function flowValidationErrorMessage(flow: unknown, resolvers?: FlowValidationResolvers): string | null;
1315
+
1162
1316
  /**
1163
1317
  * @synap-core/workspace-templates — Definition adapter
1164
1318
  *
@@ -1207,6 +1361,14 @@ interface SplitBentoBlocks {
1207
1361
  bentoLayout: BentoWidgetBlockWire[];
1208
1362
  bentoViewBlocks: BentoViewBlockWire[];
1209
1363
  }
1364
+ /**
1365
+ * Produce the canonical persisted layout. A template may author either the
1366
+ * top-level `app:` shorthand or an explicit `layout.primarySurface`, never both.
1367
+ * Legacy `defaultApp` is the final compatibility fallback. Keeping the property
1368
+ * absent preserves a live workspace value during reconciliation; an authored
1369
+ * null is retained as an explicit clear.
1370
+ */
1371
+ declare function normalizeTemplateLayout(layout: TemplateLayoutConfig | undefined, app: TemplateAppRef | undefined): TemplateLayoutConfig | undefined;
1210
1372
  declare function splitBentoBlocks(blocks: TemplateBentoBlock[] | undefined): SplitBentoBlocks;
1211
1373
  /** The flat definition object accepted by createFromDefinition. */
1212
1374
  interface WorkspaceDefinitionInput {
@@ -1362,4 +1524,4 @@ interface PackageDefinitionOutput {
1362
1524
  */
1363
1525
  declare function toPackageDefinition(slugOrYaml: string | WorkspaceYaml): PackageDefinitionOutput;
1364
1526
 
1365
- export { type AssembleCatalogInput, type BentoViewBlockWire, type BentoWidgetBlockWire, type BundledCatalogEntry, type BundledEntry, type CatalogEntry, type CompositionEdge, type CompositionGraph, type CompositionInstalledEntry, type CompositionNode, type CompositionOverlay, type ConceptViolation, type CpPackageRow, DECLARED_DIVERGENCES, type DomainGroup, type InstalledTemplate, type LayerTemplateGraphInput, type MergeCatalogInput, type MergeCatalogResult, OTHER_DOMAIN_ID, OTHER_DOMAIN_LABEL, PACKAGE_DEPENDENCY_KINDS, type PackageDefinitionOutput, type PackageDependencyKind, REQUIRED_META_FIELDS, REQUIRED_WORKSPACE_FIELDS, type RemoteCatalogEntry, type RemoteEntry, type RemoteSourceStatus, SYSTEM_PROFILES, type SplitBentoBlocks, TEMPLATE_DOMAINS, type TemplateActionPlacement, type TemplateAppKind, type TemplateAppRef, type TemplateAutomation, type TemplateBentoBlock, type TemplateConverters, type TemplateDefaultSource, type TemplateDependency, type TemplateDisplayTemplate, type TemplateDomain, type TemplateEntityLink, type TemplateExtendsRef, type TemplateIntegration, type TemplateLayoutConfig, type TemplateMeta, type TemplateOnboarding, type TemplateOnboardingCollectTarget, type TemplatePlaybook, type TemplatePlaybookParam, type TemplateProfile, type TemplateProfileEntityBentoTemplates, type TemplateProperty, type TemplateSeedEntity, type TemplateSidebarItem, type TemplateSidebarSurface, type TemplateSourceRole, type TemplateSuggestedRelation, type TemplateUpdateCheck, type TemplateView, type ToDefinitionResult, type ValidateOptions, type ValidateResult, type ValidationError, WORKSPACE_TEMPLATES, type WorkspaceDefinitionInput, type WorkspaceTemplateSlug, type WorkspaceYaml, assembleCatalog, bundledEdgesOf, collectBaseProfileSlugs, computeTemplateUpdates, conceptTerm, domainLabel, findConceptViolations, formatConceptViolations, getWorkspaceTemplate, groupByDomain, isHashVersion, isKnownDomain, layerTemplateGraph, listPublicTemplates, listWorkspaceTemplates, mergeCatalog, rowToRemoteEntry, splitBentoBlocks, toPackageDefinition, toWorkspaceDefinition, validateTemplate };
1527
+ export { type AssembleCatalogInput, type BentoViewBlockWire, type BentoWidgetBlockWire, type BundledCatalogEntry, type BundledEntry, CHANNEL_MESSAGE_CHANNEL_TYPES, type CatalogEntry, type CompositionEdge, type CompositionGraph, type CompositionInstalledEntry, type CompositionNode, type CompositionOverlay, type ConceptViolation, type CpPackageRow, DECLARED_DIVERGENCES, type DomainGroup, FLOW_NODE_TYPES, FLOW_OUTPUT_TYPES, type FlowValidationError, type FlowValidationResolvers, type FlowValidationResult, type InstalledTemplate, type LayerTemplateGraphInput, type MergeCatalogInput, type MergeCatalogResult, OTHER_DOMAIN_ID, OTHER_DOMAIN_LABEL, PACKAGE_DEPENDENCY_KINDS, type PackageDefinitionOutput, type PackageDependencyKind, REQUIRED_META_FIELDS, REQUIRED_WORKSPACE_FIELDS, type RemoteCatalogEntry, type RemoteEntry, type RemoteSourceStatus, SYSTEM_PROFILES, type SplitBentoBlocks, TEMPLATE_DOMAINS, type TemplateActionPlacement, type TemplateAppKind, type TemplateAppRef, type TemplateAutomation, type TemplateBentoBlock, type TemplateConverters, type TemplateDefaultSource, type TemplateDependency, type TemplateDisplayTemplate, type TemplateDomain, type TemplateEntityLink, type TemplateExtendsRef, type TemplateIntegration, type TemplateLayoutConfig, type TemplateMeta, type TemplateOnboarding, type TemplateOnboardingCollectTarget, type TemplatePlaybook, type TemplatePlaybookParam, type TemplatePrimarySurface, type TemplateProfile, type TemplateProfileEntityBentoTemplates, type TemplateProperty, type TemplateSeedEntity, type TemplateSidebarItem, type TemplateSidebarSurface, type TemplateSourceRole, type TemplateSuggestedRelation, type TemplateUpdateCheck, type TemplateView, type ToDefinitionResult, type ValidateOptions, type ValidateResult, type ValidationError, WORKSPACE_TEMPLATES, type WorkspaceDefinitionInput, type WorkspaceJsonValue, type WorkspacePrimarySurface, type WorkspacePrimarySurfaceViewReference, type WorkspaceTemplateSlug, type WorkspaceYaml, assembleCatalog, bundledEdgesOf, collectBaseProfileSlugs, computeTemplateUpdates, conceptTerm, domainLabel, findConceptViolations, flowValidationErrorMessage, formatConceptViolations, getWorkspaceTemplate, groupByDomain, isHashVersion, isKnownDomain, layerTemplateGraph, listPublicTemplates, listWorkspaceTemplates, mergeCatalog, normalizeTemplateLayout, rowToRemoteEntry, splitBentoBlocks, toPackageDefinition, toWorkspaceDefinition, validateFlowDefinition, validateTemplate };