@process.co/element-types 0.0.8 → 0.0.10

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 (37) hide show
  1. package/dist/authoring-contract-types.d.ts +109 -0
  2. package/dist/authoring-contract-types.d.ts.map +1 -0
  3. package/dist/authoring-contract-types.js +13 -0
  4. package/dist/authoring-contract.d.ts +104 -0
  5. package/dist/authoring-contract.d.ts.map +1 -0
  6. package/dist/authoring-contract.js +115 -0
  7. package/dist/builtin-action-slots-registry.d.ts +31 -0
  8. package/dist/builtin-action-slots-registry.d.ts.map +1 -0
  9. package/dist/builtin-action-slots-registry.js +30 -0
  10. package/dist/index.d.ts +9 -28
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +10 -1
  13. package/dist/materialize-authoring-from-cli-output.d.ts +51 -0
  14. package/dist/materialize-authoring-from-cli-output.d.ts.map +1 -0
  15. package/dist/materialize-authoring-from-cli-output.js +99 -0
  16. package/dist/materialize-slot-definition.d.ts +32 -0
  17. package/dist/materialize-slot-definition.d.ts.map +1 -0
  18. package/dist/materialize-slot-definition.js +68 -0
  19. package/dist/platform-loader-type.d.ts +11 -0
  20. package/dist/platform-loader-type.d.ts.map +1 -0
  21. package/dist/platform-loader-type.js +18 -0
  22. package/dist/process-element-cli-output.d.ts +82 -0
  23. package/dist/process-element-cli-output.d.ts.map +1 -0
  24. package/dist/process-element-cli-output.js +3 -0
  25. package/dist/slot-definition.d.ts +30 -0
  26. package/dist/slot-definition.d.ts.map +1 -0
  27. package/dist/slot-definition.js +3 -0
  28. package/package.json +1 -1
  29. package/src/authoring-contract-types.ts +132 -0
  30. package/src/authoring-contract.ts +243 -0
  31. package/src/builtin-action-slots-registry.ts +36 -0
  32. package/src/index.ts +44 -29
  33. package/src/materialize-authoring-from-cli-output.ts +157 -0
  34. package/src/materialize-slot-definition.ts +88 -0
  35. package/src/platform-loader-type.ts +14 -0
  36. package/src/process-element-cli-output.ts +85 -0
  37. package/src/slot-definition.ts +31 -0
@@ -0,0 +1,243 @@
1
+ import type {
2
+ MaterializedActionAuthoringEntry,
3
+ MaterializedAuthoringCatalog,
4
+ MaterializedSignalAuthoringEntry,
5
+ } from './materialize-authoring-from-cli-output';
6
+ import type { MaterializedSlotBranch, MaterializedSlotDefinition } from './materialize-slot-definition';
7
+ import type { ProcessElementCliOutputWire } from './process-element-cli-output';
8
+ import { materializeAuthoringCatalogFromCliOutput } from './materialize-authoring-from-cli-output';
9
+
10
+ /**
11
+ * Bump when this contract changes — regenerate workflow-sdk / codegen consumers.
12
+ * Raw CLI JSON does not carry this; the **contract** is the stable TS surface.
13
+ */
14
+ export const ELEMENT_AUTHORING_CONTRACT_VERSION = 1 as const;
15
+
16
+ /**
17
+ * Normalized prop kind for **`keyof` / conditional types** (not the raw loader string).
18
+ */
19
+ export type AuthoringPropWireKind =
20
+ | 'string'
21
+ | 'number'
22
+ | 'boolean'
23
+ | 'integer'
24
+ | 'object'
25
+ | 'array'
26
+ | 'unknown';
27
+
28
+ /** One action prop in the **locked** authoring shape. */
29
+ export interface AuthoringPropContract {
30
+ readonly key: string;
31
+ readonly label: string;
32
+ readonly wireKind: AuthoringPropWireKind;
33
+ readonly required: boolean;
34
+ }
35
+
36
+ /**
37
+ * One container branch. **`childStepsProperty`** is intentionally a **string literal type** so
38
+ * `defineGroup`-style APIs can use `B['childStepsProperty']` and get `'actions'`, not `string`.
39
+ */
40
+ export interface SlotBranchAuthoringContract {
41
+ readonly kind: 'static' | 'dynamic';
42
+ readonly slotRowId?: string;
43
+ readonly paths: Readonly<{
44
+ row?: string;
45
+ id?: string;
46
+ label?: string;
47
+ enabled?: string;
48
+ actions?: string;
49
+ exports?: string;
50
+ }>;
51
+ /**
52
+ * Where nested steps attach on each branch row, relative to that row’s object.
53
+ * Locked default **`actions`**; override only if element contract documents otherwise.
54
+ */
55
+ readonly childStepsProperty: 'actions';
56
+ /** Branch-level export callback property when present (e.g. switch **`export`**). */
57
+ readonly branchExportsProperty?: 'export' | 'exports';
58
+ }
59
+
60
+ export interface SlotsAuthoringContract {
61
+ readonly layout: Readonly<{
62
+ showBranchLabels?: boolean;
63
+ activeSlotId?: string;
64
+ activeSlotLabel?: string;
65
+ hideDisabled?: boolean;
66
+ hideDisabledPath?: string;
67
+ hideOnDisable?: boolean;
68
+ exportSchemaPath?: string;
69
+ }>;
70
+ readonly branches: readonly SlotBranchAuthoringContract[];
71
+ }
72
+
73
+ /** Locked per-action shape for FERN-keyed inference. */
74
+ export interface ActionAuthoringContract {
75
+ readonly fern: string;
76
+ readonly elementKey: string;
77
+ readonly name: string;
78
+ readonly returnsTypeName?: string;
79
+ readonly props: readonly AuthoringPropContract[];
80
+ readonly slots: SlotsAuthoringContract | null;
81
+ }
82
+
83
+ export interface SignalAuthoringContract {
84
+ readonly fern: string;
85
+ readonly elementKey: string;
86
+ readonly name: string;
87
+ readonly returnsTypeName?: string;
88
+ readonly props: readonly AuthoringPropContract[];
89
+ }
90
+
91
+ /**
92
+ * **Canonical object** for TypeScript to hang inference off — not the CLI blob.
93
+ *
94
+ * - Runtime: **`authoringCatalogContractFromCliOutput`** / **`toAuthoringCatalogContract`**.
95
+ * - Best inference: codegen emits **`export const X = { … } as const satisfies ElementAuthoringCatalogContract`**
96
+ * so **`keyof X['actions']`** is a **union of FERN literals**, not plain **`string`**.
97
+ */
98
+ export interface ElementAuthoringCatalogContract {
99
+ readonly version: typeof ELEMENT_AUTHORING_CONTRACT_VERSION;
100
+ readonly namespace: string;
101
+ readonly actions: Readonly<Record<string, ActionAuthoringContract>>;
102
+ readonly signals: Readonly<Record<string, SignalAuthoringContract>>;
103
+ }
104
+
105
+ // --- Type-level helpers (the “magic” entry points) -------------------------------------------
106
+
107
+ export type ChildStepsPropertyForBranch<B extends SlotBranchAuthoringContract> = B['childStepsProperty'];
108
+
109
+ export type ActionPropKeys<A extends ActionAuthoringContract> = A['props'][number]['key'];
110
+
111
+ export type ActionContractByFern<
112
+ C extends ElementAuthoringCatalogContract,
113
+ Fern extends keyof C['actions'] & string,
114
+ > = C['actions'][Fern];
115
+
116
+ // --- Normalization: materialized → contract ---------------------------------------------------
117
+
118
+ function normalizeWireKind(wireType: string): AuthoringPropWireKind {
119
+ const t = wireType.trim().toLowerCase();
120
+ if (t === 'string' || t === 'number' || t === 'boolean' || t === 'integer') {
121
+ return t as AuthoringPropWireKind;
122
+ }
123
+ if (t === 'object' || t.startsWith('object(')) {
124
+ return 'object';
125
+ }
126
+ if (t === 'array' || t.startsWith('array<')) {
127
+ return 'array';
128
+ }
129
+ return 'unknown';
130
+ }
131
+
132
+ function inferBranchExportsProperty(branch: MaterializedSlotBranch): 'export' | 'exports' | undefined {
133
+ const e = branch.exportsPath;
134
+ if (e === undefined) {
135
+ return undefined;
136
+ }
137
+ if (e.endsWith('.export') || e.includes('.export.')) {
138
+ return 'export';
139
+ }
140
+ if (e.endsWith('.exports') || e.includes('.exports.')) {
141
+ return 'exports';
142
+ }
143
+ return undefined;
144
+ }
145
+
146
+ function branchToContract(branch: MaterializedSlotBranch): SlotBranchAuthoringContract {
147
+ const exportsProp = inferBranchExportsProperty(branch);
148
+ return {
149
+ kind: branch.kind,
150
+ slotRowId: branch.id,
151
+ paths: {
152
+ row: branch.path,
153
+ id: branch.idPath,
154
+ label: branch.labelPath,
155
+ enabled: branch.enabledPath,
156
+ actions: branch.actionsPath,
157
+ exports: branch.exportsPath,
158
+ },
159
+ childStepsProperty: 'actions',
160
+ ...(exportsProp !== undefined ? { branchExportsProperty: exportsProp } : {}),
161
+ };
162
+ }
163
+
164
+ function slotsToContract(slots: MaterializedSlotDefinition | null): SlotsAuthoringContract | null {
165
+ if (slots === null) {
166
+ return null;
167
+ }
168
+ return {
169
+ layout: { ...slots.layout },
170
+ branches: slots.branches.map(branchToContract),
171
+ };
172
+ }
173
+
174
+ function propToContract(p: { key: string; label: string; wireType: string; required: boolean }): AuthoringPropContract {
175
+ return {
176
+ key: p.key,
177
+ label: p.label,
178
+ wireKind: normalizeWireKind(p.wireType),
179
+ required: p.required,
180
+ };
181
+ }
182
+
183
+ function actionToContract(a: MaterializedActionAuthoringEntry): ActionAuthoringContract {
184
+ return {
185
+ fern: a.fern,
186
+ elementKey: a.elementKey,
187
+ name: a.name,
188
+ returnsTypeName: a.returns,
189
+ props: a.props.map(propToContract),
190
+ slots: slotsToContract(a.slots),
191
+ };
192
+ }
193
+
194
+ function signalToContract(s: MaterializedSignalAuthoringEntry): SignalAuthoringContract {
195
+ return {
196
+ fern: s.fern,
197
+ elementKey: s.elementKey,
198
+ name: s.name,
199
+ returnsTypeName: s.returns,
200
+ props: s.props.map(propToContract),
201
+ };
202
+ }
203
+
204
+ /**
205
+ * Map **loose materialized catalog** (from CLI JSON) into the **locked `ElementAuthoringCatalogContract`**.
206
+ */
207
+ export function toAuthoringCatalogContract(cat: MaterializedAuthoringCatalog): ElementAuthoringCatalogContract {
208
+ const actions: Record<string, ActionAuthoringContract> = {};
209
+ const signals: Record<string, SignalAuthoringContract> = {};
210
+ for (const [fern, a] of Object.entries(cat.actionsByFern)) {
211
+ actions[fern] = actionToContract(a);
212
+ }
213
+ for (const [fern, s] of Object.entries(cat.signalsByFern)) {
214
+ signals[fern] = signalToContract(s);
215
+ }
216
+ return {
217
+ version: ELEMENT_AUTHORING_CONTRACT_VERSION,
218
+ namespace: cat.namespace,
219
+ actions,
220
+ signals,
221
+ };
222
+ }
223
+
224
+ /** One-shot: **`process-element` JSON shape** → locked contract. */
225
+ export function authoringCatalogContractFromCliOutput(
226
+ info: ProcessElementCliOutputWire,
227
+ namespace?: string,
228
+ ): ElementAuthoringCatalogContract {
229
+ return toAuthoringCatalogContract(materializeAuthoringCatalogFromCliOutput(info, namespace));
230
+ }
231
+
232
+ /**
233
+ * **Early** artifact: one file per element / build, written by **`@process.co/compatibility`**.
234
+ * **Late** step merges many shards into **`workflow-sdk`** `fern-authoring-registry.generated.ts`.
235
+ */
236
+ export type FernAuthoringShardFileV1 = {
237
+ readonly version: typeof ELEMENT_AUTHORING_CONTRACT_VERSION;
238
+ /** Source path or id (optional, for debugging). */
239
+ readonly source?: string;
240
+ readonly namespace: string;
241
+ readonly actions: Record<string, ActionAuthoringContract>;
242
+ readonly signals: Record<string, SignalAuthoringContract>;
243
+ };
@@ -0,0 +1,36 @@
1
+ import type { ISlotDefinition } from './slot-definition';
2
+
3
+ /**
4
+ * Optional bootstrap registry (FERN → **`ISlotDefinition`**).
5
+ * Prefer driving inference from **`@process.co/compatibility`** **`authoring-spec`** **`materializeAuthoringCatalogFromCliOutput`**
6
+ * on the full **`process-element` / `loadElementPointers`** JSON — that includes **props**, **returns**, and **slots**.
7
+ */
8
+ export const builtinActionSlotsRegistry = {
9
+ 'process-internal::action:switch': {
10
+ hideDisabled: true,
11
+ slots: [
12
+ {
13
+ type: 'static' as const,
14
+ id: '{{ID_GUID}}_default_case',
15
+ labelPath: '$.data.cases.defaultLabel',
16
+ enabledPath: '$.data.cases.defaultEnabled',
17
+ hideOnDisable: true,
18
+ },
19
+ {
20
+ path: '$.data.cases.cases[*]',
21
+ idPath: '$.data.cases.cases[*].id',
22
+ labelPath: '$.data.cases.cases[*].label',
23
+ enabledPath: '$.data.cases.cases[*].enabled',
24
+ hideOnDisable: true,
25
+ },
26
+ ],
27
+ } satisfies ISlotDefinition,
28
+ } as const;
29
+
30
+ export type BuiltinActionSlotsRegistry = typeof builtinActionSlotsRegistry;
31
+
32
+ export type BuiltinActionSlotsFern = keyof BuiltinActionSlotsRegistry;
33
+
34
+ export type InferBuiltinActionSlots<Fern extends string> = Fern extends BuiltinActionSlotsFern
35
+ ? BuiltinActionSlotsRegistry[Fern]
36
+ : undefined;
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import type { z } from 'zod';
2
3
 
3
4
  // Element types
@@ -15,35 +16,49 @@ export type ElementSignal<T> = { type: "signal"; icon?: ElementIcon; label?: str
15
16
 
16
17
  export type ElementIcon = { type: "FontAwesome" | "MaterialIcons" | "ProcessIcons" | "RemoteImage" | "image"; icon: string | ['far' | 'fas' | 'fab' | 'fal' | 'fad', string] } | string;
17
18
 
18
- export type ISlotInstanceDefinition = {
19
- id?: string;
20
- label?: string;
21
- enabled?: boolean;
22
- path?: string;
23
- idPath?: string;
24
- labelPath?: string;
25
- enabledPath?: string;
26
- labelPlaceholderTemplate?: string;
27
- labelPlaceholderValue?: string;
28
- branchValue?: string;
29
- hideOnDisable?: boolean;
30
- actionsPath?: string;
31
- exportsPath?: string;
32
- }
33
-
34
- export type ISlotStaticInstanceDefinition = ISlotInstanceDefinition & {
35
- type: "static";
36
- }
37
-
38
- export type ISlotDefinition = {
39
- showBranchLabels?: boolean;
40
- activeSlotId?: string;
41
- activeSlotLabel?: string;
42
- hideDisabled?: boolean;
43
- hideOnDisable?: boolean;
44
- exportSchemaPath?: string;
45
- slots?: (ISlotInstanceDefinition | ISlotStaticInstanceDefinition)[];
46
- }
19
+ import type {
20
+ ISlotInstanceDefinition,
21
+ ISlotStaticInstanceDefinition,
22
+ ISlotDefinition,
23
+ } from './slot-definition';
24
+
25
+ export type { ISlotInstanceDefinition, ISlotStaticInstanceDefinition, ISlotDefinition };
26
+
27
+ export {
28
+ builtinActionSlotsRegistry,
29
+ type BuiltinActionSlotsRegistry,
30
+ type BuiltinActionSlotsFern,
31
+ type InferBuiltinActionSlots,
32
+ } from './builtin-action-slots-registry';
33
+
34
+ /** Full **`process-element` CLI** JSON shape (types only; materialize in **`@process.co/compatibility`** **`authoring-spec`**). */
35
+ export type {
36
+ ProcessElementPropCliWire,
37
+ ProcessElementActionCliWire,
38
+ ProcessElementSignalCliWire,
39
+ ProcessElementCliOutputWire,
40
+ } from './process-element-cli-output';
41
+
42
+ /** Locked authoring catalog **types** + version (runtime materialize: **`@process.co/compatibility`** **`authoring-spec`**). */
43
+ export { ELEMENT_AUTHORING_CONTRACT_VERSION } from './authoring-contract-types';
44
+ export type {
45
+ AuthoringPropWireKind,
46
+ AuthoringPropContract,
47
+ SlotBranchAuthoringContract,
48
+ SlotsAuthoringContract,
49
+ ActionAuthoringContract,
50
+ SignalAuthoringContract,
51
+ ElementAuthoringCatalogContract,
52
+ ChildStepsPropertyForBranch,
53
+ ActionPropKeys,
54
+ ActionContractByFern,
55
+ FernAuthoringShardFileV1,
56
+ } from './authoring-contract-types';
57
+
58
+ export {
59
+ PLATFORM_BOUND_LOADER_TYPE_PREFIXES,
60
+ isPlatformBoundLoaderType,
61
+ } from './platform-loader-type';
47
62
 
48
63
  // Base types for module definitions
49
64
  export type ModuleDefinition = {
@@ -0,0 +1,157 @@
1
+ import { materializeSlotDefinition, type MaterializedSlotDefinition } from './materialize-slot-definition';
2
+ import type {
3
+ ProcessElementActionCliWire,
4
+ ProcessElementCliOutputWire,
5
+ ProcessElementPropCliWire,
6
+ ProcessElementSignalCliWire,
7
+ } from './process-element-cli-output';
8
+
9
+ export type MaterializedPropAuthoring = {
10
+ key: string;
11
+ label: string;
12
+ description: string;
13
+ /** String from loader **`type`** (e.g. **`string`**, **`object`**, or zodex-serialized object as JSON). */
14
+ wireType: string;
15
+ required: boolean;
16
+ hasJsonType: boolean;
17
+ isFunction: boolean;
18
+ };
19
+
20
+ export type MaterializedActionAuthoringEntry = {
21
+ kind: 'action';
22
+ fern: string;
23
+ /** Raw action key from the element (may include namespace prefix). */
24
+ elementKey: string;
25
+ name: string;
26
+ description?: string;
27
+ returns?: string;
28
+ props: MaterializedPropAuthoring[];
29
+ slots: MaterializedSlotDefinition | null;
30
+ };
31
+
32
+ export type MaterializedSignalAuthoringEntry = {
33
+ kind: 'signal';
34
+ fern: string;
35
+ elementKey: string;
36
+ name: string;
37
+ description?: string;
38
+ returns?: string;
39
+ props: MaterializedPropAuthoring[];
40
+ };
41
+
42
+ export type MaterializedAuthoringCatalog = {
43
+ namespace: string;
44
+ actionsByFern: Record<string, MaterializedActionAuthoringEntry>;
45
+ signalsByFern: Record<string, MaterializedSignalAuthoringEntry>;
46
+ };
47
+
48
+ function stripNamespacePrefixFromActionKey(namespace: string, key: string): string {
49
+ const prefix = `${namespace}-`;
50
+ return key.startsWith(prefix) ? key.slice(prefix.length) : key;
51
+ }
52
+
53
+ /**
54
+ * Build registry FERN for an action (**`namespace::action:slug`**), matching common API normalization.
55
+ */
56
+ export function buildProcessActionFern(namespace: string, actionKey: string): string {
57
+ const slug = stripNamespacePrefixFromActionKey(namespace, actionKey);
58
+ return `${namespace}::action:${slug}`;
59
+ }
60
+
61
+ /** Build registry FERN for a signal (**`namespace::signal:slug`**). */
62
+ export function buildProcessSignalFern(namespace: string, signalKey: string): string {
63
+ const slug = stripNamespacePrefixFromActionKey(namespace, signalKey);
64
+ return `${namespace}::signal:${slug}`;
65
+ }
66
+
67
+ function wireTypeLabel(type: unknown): string {
68
+ if (type === undefined || type === null) {
69
+ return 'unknown';
70
+ }
71
+ if (typeof type === 'string') {
72
+ return type;
73
+ }
74
+ try {
75
+ return JSON.stringify(type);
76
+ } catch {
77
+ return 'unknown';
78
+ }
79
+ }
80
+
81
+ function materializeProp(p: ProcessElementPropCliWire): MaterializedPropAuthoring {
82
+ return {
83
+ key: p.key,
84
+ label: p.label,
85
+ description: typeof p.description === 'string' ? p.description : '',
86
+ wireType: wireTypeLabel(p.type),
87
+ required: p.required === true,
88
+ hasJsonType: p.jsonType !== undefined && p.jsonType !== null,
89
+ isFunction: p.isFunction === true,
90
+ };
91
+ }
92
+
93
+ function materializeAction(
94
+ namespace: string,
95
+ a: ProcessElementActionCliWire,
96
+ ): MaterializedActionAuthoringEntry {
97
+ const fern = buildProcessActionFern(namespace, a.key);
98
+ return {
99
+ kind: 'action',
100
+ fern,
101
+ elementKey: a.key,
102
+ name: a.name,
103
+ description: typeof a.description === 'string' ? a.description : undefined,
104
+ returns: typeof a.returns === 'string' ? a.returns : undefined,
105
+ props: (Array.isArray(a.props) ? a.props : []).map(materializeProp),
106
+ slots: materializeSlotDefinition(a.slots),
107
+ };
108
+ }
109
+
110
+ function materializeSignal(
111
+ namespace: string,
112
+ s: ProcessElementSignalCliWire,
113
+ ): MaterializedSignalAuthoringEntry {
114
+ const fern = buildProcessSignalFern(namespace, s.key);
115
+ return {
116
+ kind: 'signal',
117
+ fern,
118
+ elementKey: s.key,
119
+ name: s.name,
120
+ description: typeof s.description === 'string' ? s.description : undefined,
121
+ returns: typeof s.returns === 'string' ? s.returns : undefined,
122
+ props: (Array.isArray(s.props) ? s.props : []).map(materializeProp),
123
+ };
124
+ }
125
+
126
+ /**
127
+ * Turn the **entire compatibility CLI / `loadElementPointers` JSON** into FERN-keyed authoring material
128
+ * (props + slot paths + returns hints) for codegen or merging into **`@process.co/workflow-sdk`**.
129
+ *
130
+ * @param namespace — FERN namespace segment (e.g. **`process-internal`**). Defaults to **`info.name`** (element app slug).
131
+ */
132
+ export function materializeAuthoringCatalogFromCliOutput(
133
+ info: ProcessElementCliOutputWire,
134
+ namespace?: string,
135
+ ): MaterializedAuthoringCatalog {
136
+ const ns = namespace ?? info.name;
137
+ const actionsByFern: Record<string, MaterializedActionAuthoringEntry> = {};
138
+ const signalsByFern: Record<string, MaterializedSignalAuthoringEntry> = {};
139
+
140
+ for (const a of info.actions) {
141
+ if (a?.type !== 'action' || typeof a.key !== 'string') {
142
+ continue;
143
+ }
144
+ const entry = materializeAction(ns, a as ProcessElementActionCliWire);
145
+ actionsByFern[entry.fern] = entry;
146
+ }
147
+
148
+ for (const s of info.signals) {
149
+ if (s?.type !== 'signal' || typeof s.key !== 'string') {
150
+ continue;
151
+ }
152
+ const entry = materializeSignal(ns, s as ProcessElementSignalCliWire);
153
+ signalsByFern[entry.fern] = entry;
154
+ }
155
+
156
+ return { namespace: ns, actionsByFern, signalsByFern };
157
+ }
@@ -0,0 +1,88 @@
1
+ import type {
2
+ ISlotDefinition,
3
+ ISlotInstanceDefinition,
4
+ ISlotStaticInstanceDefinition,
5
+ } from './slot-definition';
6
+
7
+ export type MaterializedSlotBranch = {
8
+ kind: 'static' | 'dynamic';
9
+ id?: string;
10
+ label?: string;
11
+ branchValue?: string;
12
+ path?: string;
13
+ idPath?: string;
14
+ labelPath?: string;
15
+ enabledPath?: string;
16
+ actionsPath?: string;
17
+ exportsPath?: string;
18
+ hideOnDisable?: boolean;
19
+ labelPlaceholderTemplate?: string;
20
+ labelPlaceholderValue?: string;
21
+ };
22
+
23
+ export type MaterializedSlotLayout = {
24
+ showBranchLabels?: boolean;
25
+ activeSlotId?: string;
26
+ activeSlotLabel?: string;
27
+ hideDisabled?: boolean;
28
+ hideDisabledPath?: string;
29
+ hideOnDisable?: boolean;
30
+ exportSchemaPath?: string;
31
+ };
32
+
33
+ export type MaterializedSlotDefinition = {
34
+ layout: MaterializedSlotLayout;
35
+ branches: MaterializedSlotBranch[];
36
+ };
37
+
38
+ function branchKind(row: ISlotInstanceDefinition | ISlotStaticInstanceDefinition): 'static' | 'dynamic' {
39
+ return 'type' in row && row.type === 'static' ? 'static' : 'dynamic';
40
+ }
41
+
42
+ function pickBranch(row: ISlotInstanceDefinition | ISlotStaticInstanceDefinition): MaterializedSlotBranch {
43
+ const b: MaterializedSlotBranch = { kind: branchKind(row) };
44
+ if (row.id !== undefined) b.id = row.id;
45
+ if (row.label !== undefined) b.label = row.label;
46
+ if (row.branchValue !== undefined) b.branchValue = row.branchValue;
47
+ if (row.path !== undefined) b.path = row.path;
48
+ if (row.idPath !== undefined) b.idPath = row.idPath;
49
+ if (row.labelPath !== undefined) b.labelPath = row.labelPath;
50
+ if (row.enabledPath !== undefined) b.enabledPath = row.enabledPath;
51
+ if (row.actionsPath !== undefined) b.actionsPath = row.actionsPath;
52
+ if (row.exportsPath !== undefined) b.exportsPath = row.exportsPath;
53
+ if (row.hideOnDisable !== undefined) b.hideOnDisable = row.hideOnDisable;
54
+ if (row.labelPlaceholderTemplate !== undefined) b.labelPlaceholderTemplate = row.labelPlaceholderTemplate;
55
+ if (row.labelPlaceholderValue !== undefined) b.labelPlaceholderValue = row.labelPlaceholderValue;
56
+ return b;
57
+ }
58
+
59
+ function pickLayout(def: ISlotDefinition): MaterializedSlotLayout {
60
+ const layout: MaterializedSlotLayout = {};
61
+ if (def.showBranchLabels !== undefined) layout.showBranchLabels = def.showBranchLabels;
62
+ if (def.activeSlotId !== undefined) layout.activeSlotId = def.activeSlotId;
63
+ if (def.activeSlotLabel !== undefined) layout.activeSlotLabel = def.activeSlotLabel;
64
+ if (def.hideDisabled !== undefined) layout.hideDisabled = def.hideDisabled;
65
+ if (def.hideDisabledPath !== undefined) layout.hideDisabledPath = def.hideDisabledPath;
66
+ if (def.hideOnDisable !== undefined) layout.hideOnDisable = def.hideOnDisable;
67
+ if (def.exportSchemaPath !== undefined) layout.exportSchemaPath = def.exportSchemaPath;
68
+ return layout;
69
+ }
70
+
71
+ function isEffectivelyEmpty(def: ISlotDefinition): boolean {
72
+ const rows = def.slots ?? [];
73
+ const layout = pickLayout(def);
74
+ return rows.length === 0 && Object.keys(layout).length === 0;
75
+ }
76
+
77
+ /** Normalize **`ISlotDefinition`** for codegen / workflow inference (JSON-serializable). */
78
+ export function materializeSlotDefinition(
79
+ def: ISlotDefinition | null | undefined,
80
+ ): MaterializedSlotDefinition | null {
81
+ if (def == null || isEffectivelyEmpty(def)) {
82
+ return null;
83
+ }
84
+ return {
85
+ layout: pickLayout(def),
86
+ branches: (def.slots ?? []).map(pickBranch),
87
+ };
88
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * **`prop.type`** string prefixes that bind to runtime platform implementations
3
+ * (**`$.interface.http`**, **`$.service.db`**, **`$.interface.timer`**, …).
4
+ *
5
+ * Keep in sync with:
6
+ * - **`@process.co/compatibility`** authoring registry (**`excludeFromAuthoringInstanceShape`**)
7
+ * - **`apps/web`** expression validation / console (skip misleading type-mismatch noise)
8
+ */
9
+ export const PLATFORM_BOUND_LOADER_TYPE_PREFIXES = ['$.interface.', '$.service.'] as const;
10
+
11
+ export function isPlatformBoundLoaderType(type: string): boolean {
12
+ const t = type.trim();
13
+ return PLATFORM_BOUND_LOADER_TYPE_PREFIXES.some((p) => t.startsWith(p));
14
+ }