@player-tools/fluent 0.13.0--canary.221.5662

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 (121) hide show
  1. package/dist/cjs/index.cjs +2257 -0
  2. package/dist/cjs/index.cjs.map +1 -0
  3. package/dist/index.legacy-esm.js +2143 -0
  4. package/dist/index.mjs +2143 -0
  5. package/dist/index.mjs.map +1 -0
  6. package/package.json +36 -0
  7. package/src/core/base-builder/__tests__/fluent-builder-base.test.ts +2257 -0
  8. package/src/core/base-builder/__tests__/id-generator.test.ts +658 -0
  9. package/src/core/base-builder/__tests__/registry.test.ts +411 -0
  10. package/src/core/base-builder/__tests__/switch.test.ts +501 -0
  11. package/src/core/base-builder/__tests__/template.test.ts +449 -0
  12. package/src/core/base-builder/__tests__/value-extraction.test.ts +200 -0
  13. package/src/core/base-builder/conditional/index.ts +64 -0
  14. package/src/core/base-builder/context.ts +151 -0
  15. package/src/core/base-builder/fluent-builder-base.ts +261 -0
  16. package/src/core/base-builder/guards.ts +137 -0
  17. package/src/core/base-builder/id/generator.ts +286 -0
  18. package/src/core/base-builder/id/registry.ts +152 -0
  19. package/src/core/base-builder/index.ts +60 -0
  20. package/src/core/base-builder/resolution/path-resolver.ts +108 -0
  21. package/src/core/base-builder/resolution/pipeline.ts +96 -0
  22. package/src/core/base-builder/resolution/steps/asset-id.ts +77 -0
  23. package/src/core/base-builder/resolution/steps/asset-wrappers.ts +64 -0
  24. package/src/core/base-builder/resolution/steps/builders.ts +85 -0
  25. package/src/core/base-builder/resolution/steps/mixed-arrays.ts +117 -0
  26. package/src/core/base-builder/resolution/steps/static-values.ts +35 -0
  27. package/src/core/base-builder/resolution/steps/switches.ts +63 -0
  28. package/src/core/base-builder/resolution/steps/templates.ts +30 -0
  29. package/src/core/base-builder/resolution/value-resolver.ts +308 -0
  30. package/src/core/base-builder/storage/auxiliary-storage.ts +82 -0
  31. package/src/core/base-builder/storage/value-storage.ts +280 -0
  32. package/src/core/base-builder/types.ts +184 -0
  33. package/src/core/base-builder/utils.ts +10 -0
  34. package/src/core/flow/__tests__/index.test.ts +292 -0
  35. package/src/core/flow/index.ts +141 -0
  36. package/src/core/index.ts +8 -0
  37. package/src/core/mocks/generated/action.builder.ts +109 -0
  38. package/src/core/mocks/generated/choice.builder.ts +161 -0
  39. package/src/core/mocks/generated/choiceItem.builder.ts +133 -0
  40. package/src/core/mocks/generated/collection.builder.ts +117 -0
  41. package/src/core/mocks/generated/index.ts +7 -0
  42. package/src/core/mocks/generated/info.builder.ts +80 -0
  43. package/src/core/mocks/generated/input.builder.ts +75 -0
  44. package/src/core/mocks/generated/text.builder.ts +63 -0
  45. package/src/core/mocks/index.ts +1 -0
  46. package/src/core/mocks/types/action.ts +92 -0
  47. package/src/core/mocks/types/choice.ts +129 -0
  48. package/src/core/mocks/types/collection.ts +140 -0
  49. package/src/core/mocks/types/info.ts +7 -0
  50. package/src/core/mocks/types/input.ts +7 -0
  51. package/src/core/mocks/types/text.ts +5 -0
  52. package/src/core/schema/__tests__/index.test.ts +127 -0
  53. package/src/core/schema/index.ts +195 -0
  54. package/src/core/schema/types.ts +7 -0
  55. package/src/core/switch/__tests__/index.test.ts +156 -0
  56. package/src/core/switch/index.ts +76 -0
  57. package/src/core/tagged-template/README.md +448 -0
  58. package/src/core/tagged-template/__tests__/extract-bindings-from-schema.test.ts +207 -0
  59. package/src/core/tagged-template/__tests__/index.test.ts +190 -0
  60. package/src/core/tagged-template/__tests__/schema-std-integration.test.ts +580 -0
  61. package/src/core/tagged-template/binding.ts +95 -0
  62. package/src/core/tagged-template/expression.ts +92 -0
  63. package/src/core/tagged-template/extract-bindings-from-schema.ts +120 -0
  64. package/src/core/tagged-template/index.ts +5 -0
  65. package/src/core/tagged-template/std.ts +472 -0
  66. package/src/core/tagged-template/types.ts +123 -0
  67. package/src/core/template/__tests__/index.test.ts +380 -0
  68. package/src/core/template/index.ts +191 -0
  69. package/src/core/utils/index.ts +160 -0
  70. package/src/fp/README.md +411 -0
  71. package/src/fp/__tests__/index.test.ts +1178 -0
  72. package/src/fp/index.ts +386 -0
  73. package/src/gen/common.ts +2 -0
  74. package/src/gen/plugin.mjs +315 -0
  75. package/src/index.ts +5 -0
  76. package/src/types.ts +203 -0
  77. package/types/core/base-builder/conditional/index.d.ts +21 -0
  78. package/types/core/base-builder/context.d.ts +39 -0
  79. package/types/core/base-builder/fluent-builder-base.d.ts +132 -0
  80. package/types/core/base-builder/guards.d.ts +58 -0
  81. package/types/core/base-builder/id/generator.d.ts +69 -0
  82. package/types/core/base-builder/id/registry.d.ts +93 -0
  83. package/types/core/base-builder/index.d.ts +8 -0
  84. package/types/core/base-builder/resolution/path-resolver.d.ts +15 -0
  85. package/types/core/base-builder/resolution/pipeline.d.ts +25 -0
  86. package/types/core/base-builder/resolution/steps/asset-id.d.ts +14 -0
  87. package/types/core/base-builder/resolution/steps/asset-wrappers.d.ts +14 -0
  88. package/types/core/base-builder/resolution/steps/builders.d.ts +14 -0
  89. package/types/core/base-builder/resolution/steps/mixed-arrays.d.ts +14 -0
  90. package/types/core/base-builder/resolution/steps/static-values.d.ts +14 -0
  91. package/types/core/base-builder/resolution/steps/switches.d.ts +15 -0
  92. package/types/core/base-builder/resolution/steps/templates.d.ts +14 -0
  93. package/types/core/base-builder/resolution/value-resolver.d.ts +37 -0
  94. package/types/core/base-builder/storage/auxiliary-storage.d.ts +50 -0
  95. package/types/core/base-builder/storage/value-storage.d.ts +82 -0
  96. package/types/core/base-builder/types.d.ts +141 -0
  97. package/types/core/base-builder/utils.d.ts +2 -0
  98. package/types/core/flow/index.d.ts +23 -0
  99. package/types/core/index.d.ts +8 -0
  100. package/types/core/mocks/index.d.ts +2 -0
  101. package/types/core/mocks/types/action.d.ts +58 -0
  102. package/types/core/mocks/types/choice.d.ts +95 -0
  103. package/types/core/mocks/types/collection.d.ts +102 -0
  104. package/types/core/mocks/types/info.d.ts +7 -0
  105. package/types/core/mocks/types/input.d.ts +7 -0
  106. package/types/core/mocks/types/text.d.ts +5 -0
  107. package/types/core/schema/index.d.ts +34 -0
  108. package/types/core/schema/types.d.ts +5 -0
  109. package/types/core/switch/index.d.ts +21 -0
  110. package/types/core/tagged-template/binding.d.ts +19 -0
  111. package/types/core/tagged-template/expression.d.ts +11 -0
  112. package/types/core/tagged-template/extract-bindings-from-schema.d.ts +7 -0
  113. package/types/core/tagged-template/index.d.ts +6 -0
  114. package/types/core/tagged-template/std.d.ts +174 -0
  115. package/types/core/tagged-template/types.d.ts +69 -0
  116. package/types/core/template/index.d.ts +97 -0
  117. package/types/core/utils/index.d.ts +47 -0
  118. package/types/fp/index.d.ts +149 -0
  119. package/types/gen/common.d.ts +3 -0
  120. package/types/index.d.ts +3 -0
  121. package/types/types.d.ts +163 -0
@@ -0,0 +1,184 @@
1
+ import { Asset, AssetWrapper } from "@player-ui/types";
2
+
3
+ /**
4
+ * Unique symbol to identify FluentBuilder instances
5
+ * Used for runtime type checking of builder objects
6
+ */
7
+ export const FLUENT_BUILDER_SYMBOL: unique symbol =
8
+ Symbol.for("fluent-builder");
9
+
10
+ /**
11
+ * Core interface for all fluent builders
12
+ * Provides build(), peek(), and has() methods for all builder types
13
+ */
14
+ export interface FluentBuilder<
15
+ T,
16
+ C extends BaseBuildContext = BaseBuildContext,
17
+ > {
18
+ readonly [FLUENT_BUILDER_SYMBOL]: true;
19
+ build(context?: C): T;
20
+ peek<K extends keyof T>(key: K): T[K] | undefined;
21
+ has<K extends keyof T>(key: K): boolean;
22
+ }
23
+
24
+ /**
25
+ * Type-erased asset builder interface for generic asset handling
26
+ */
27
+ export type AnyAssetBuilder<C extends BaseBuildContext = BaseBuildContext> = {
28
+ readonly [FLUENT_BUILDER_SYMBOL]: true;
29
+ build(context?: C): Asset;
30
+ peek(key: string): unknown;
31
+ has(key: string): boolean;
32
+ };
33
+
34
+ /**
35
+ * Parameters for creating nested build contexts
36
+ * Used by nested context generators to create child contexts
37
+ */
38
+ export interface NestedContextParams<C extends BaseBuildContext> {
39
+ readonly parentContext: C;
40
+ readonly parameterName: string;
41
+ readonly index?: number;
42
+ }
43
+
44
+ /**
45
+ * Function type for custom nested context generation
46
+ * Allows users to customize how child contexts are created
47
+ */
48
+ export type NestedContextGenerator<C extends BaseBuildContext> = (
49
+ params: NestedContextParams<C>,
50
+ ) => C;
51
+
52
+ /**
53
+ * Metadata about an asset used for ID generation and context tracking
54
+ */
55
+ export interface AssetMetadata {
56
+ readonly type?: string;
57
+ readonly binding?: string;
58
+ readonly value?: string;
59
+ }
60
+
61
+ /**
62
+ * Base build context interface containing common fields for all builders
63
+ * Extended by specific builder implementations for custom context needs
64
+ */
65
+ export interface BaseBuildContext {
66
+ readonly parentId?: string;
67
+ readonly parameterName?: string;
68
+ readonly index?: number;
69
+ readonly branch?: IdBranch;
70
+ readonly nestedContextGenerator?: NestedContextGenerator<BaseBuildContext>;
71
+ readonly assetMetadata?: AssetMetadata;
72
+ readonly [key: string]: unknown;
73
+ }
74
+
75
+ /**
76
+ * Slot branch for named properties (e.g., "label", "action")
77
+ * Creates IDs like: parent-label, parent-action
78
+ */
79
+ export interface SlotBranch {
80
+ type: "slot";
81
+ name: string;
82
+ }
83
+
84
+ /**
85
+ * Array item branch for indexed elements
86
+ * Creates IDs like: parent-0, parent-1
87
+ */
88
+ export interface ArrayItemBranch {
89
+ type: "array-item";
90
+ index: number;
91
+ }
92
+
93
+ /**
94
+ * Template branch for template placeholders
95
+ * Creates IDs like: parent-_index_, parent-_index1_
96
+ */
97
+ export interface TemplateBranch {
98
+ type: "template";
99
+ depth?: number;
100
+ }
101
+
102
+ /**
103
+ * Switch branch for conditional cases
104
+ * Creates IDs like: parent-staticSwitch-0, parent-dynamicSwitch-1
105
+ */
106
+ export interface SwitchBranch {
107
+ type: "switch";
108
+ index: number;
109
+ kind: "static" | "dynamic";
110
+ }
111
+
112
+ /**
113
+ * Custom branch for user-defined ID patterns
114
+ */
115
+ export interface CustomBranch {
116
+ type: "custom";
117
+ }
118
+
119
+ /**
120
+ * Union of all branch types for type-safe ID generation
121
+ */
122
+ export type IdBranch =
123
+ | SlotBranch
124
+ | ArrayItemBranch
125
+ | TemplateBranch
126
+ | SwitchBranch
127
+ | CustomBranch;
128
+
129
+ /**
130
+ * Metadata for arrays containing mixed static values and builders
131
+ * Tracks which indices contain builders for selective resolution
132
+ */
133
+ export interface MixedArrayMetadata {
134
+ readonly array: readonly unknown[];
135
+ readonly builderIndices: ReadonlySet<number>;
136
+ readonly objectIndices: ReadonlySet<number>;
137
+ }
138
+
139
+ /**
140
+ * Metadata for template storage in FluentBuilderBase
141
+ */
142
+ export interface TemplateMetadata {
143
+ readonly data: string;
144
+ readonly output: string;
145
+ readonly dynamic?: boolean;
146
+ }
147
+
148
+ /**
149
+ * Path type for targeting where to inject values in nested structures
150
+ * Example: ["actions", 0, "label"] targets actions[0].label
151
+ */
152
+ export type ValuePath = ReadonlyArray<string | number>;
153
+
154
+ /**
155
+ * Metadata for switch storage in FluentBuilderBase
156
+ */
157
+ export interface SwitchMetadata<C extends BaseBuildContext = BaseBuildContext> {
158
+ readonly path: ValuePath;
159
+ readonly switchFn: (context: C, globalCaseIndex?: number) => unknown;
160
+ }
161
+
162
+ /**
163
+ * Helper type for conditional property values in if/ifElse methods
164
+ * Allows passing unwrapped Asset builders to AssetWrapper properties
165
+ * Enables: .if(() => true, "label", text().withValue("..."))
166
+ * Instead of: .if(() => true, "label", { asset: text().withValue("...") })
167
+ */
168
+ export type ConditionalValue<T, C extends BaseBuildContext> = T extends
169
+ | AssetWrapper<infer A>
170
+ | undefined
171
+ ? // For AssetWrapper properties, allow unwrapped builders/assets
172
+ | T
173
+ | FluentBuilder<T, C>
174
+ | FluentBuilder<A, C>
175
+ | A
176
+ | Array<FluentBuilder<A, C> | A>
177
+ | (() =>
178
+ | T
179
+ | FluentBuilder<T, C>
180
+ | FluentBuilder<A, C>
181
+ | A
182
+ | Array<FluentBuilder<A, C> | A>)
183
+ : // For other properties, just allow the normal types
184
+ T | FluentBuilder<T, C> | (() => T | FluentBuilder<T, C>);
@@ -0,0 +1,10 @@
1
+ export function createInspectMethod(
2
+ builderName: string,
3
+ properties: Record<string, unknown>,
4
+ ): string {
5
+ return `${builderName} { properties: ${JSON.stringify(
6
+ properties,
7
+ null,
8
+ 2,
9
+ )} }`;
10
+ }
@@ -0,0 +1,292 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { flow } from "..";
3
+ import { Flow } from "@player-ui/types";
4
+ import { text } from "../../mocks";
5
+
6
+ describe("flow function", () => {
7
+ it("should create a flow with basic properties", () => {
8
+ const result = flow({
9
+ id: "test-flow",
10
+ topic: "test-flow",
11
+ views: [text({ value: "Hello World" })],
12
+ data: {
13
+ greeting: "Hello",
14
+ },
15
+ navigation: {
16
+ BEGIN: "FLOW_1",
17
+ FLOW_1: {
18
+ startState: "VIEW_1",
19
+ VIEW_1: {
20
+ state_type: "VIEW",
21
+ ref: "view-1",
22
+ transitions: {},
23
+ },
24
+ },
25
+ },
26
+ });
27
+
28
+ const expected: Flow = {
29
+ data: {
30
+ greeting: "Hello",
31
+ },
32
+ id: "test-flow",
33
+ topic: "test-flow",
34
+ navigation: {
35
+ BEGIN: "FLOW_1",
36
+ FLOW_1: {
37
+ VIEW_1: {
38
+ ref: "view-1",
39
+ state_type: "VIEW",
40
+ transitions: {},
41
+ },
42
+ startState: "VIEW_1",
43
+ },
44
+ },
45
+ views: [
46
+ {
47
+ id: "test-flow-views-0-text",
48
+ type: "text",
49
+ value: "Hello World",
50
+ },
51
+ ],
52
+ };
53
+
54
+ expect(result).toEqual(expected);
55
+ });
56
+
57
+ it('should use "root" as default flow ID', () => {
58
+ const result = flow({
59
+ views: [text({ value: "Hello World" })],
60
+ navigation: {
61
+ BEGIN: "FLOW_1",
62
+ FLOW_1: {
63
+ startState: "VIEW_1",
64
+ VIEW_1: {
65
+ state_type: "VIEW",
66
+ ref: "view-1",
67
+ transitions: {},
68
+ },
69
+ },
70
+ },
71
+ });
72
+
73
+ expect(result.id).toEqual("root");
74
+ });
75
+
76
+ it("should generate hierarchical IDs for views", () => {
77
+ const result = flow({
78
+ id: "my-flow",
79
+ views: [text().withValue("First View"), text().withValue("Second View")],
80
+ navigation: {
81
+ BEGIN: "FLOW_1",
82
+ FLOW_1: {
83
+ startState: "VIEW_1",
84
+ VIEW_1: {
85
+ state_type: "VIEW",
86
+ ref: "view-1",
87
+ transitions: {},
88
+ },
89
+ },
90
+ },
91
+ });
92
+
93
+ const expected: Flow["views"] = [
94
+ {
95
+ id: "my-flow-views-0-text",
96
+ type: "text",
97
+ value: "First View",
98
+ },
99
+ {
100
+ id: "my-flow-views-1-text",
101
+ type: "text",
102
+ value: "Second View",
103
+ },
104
+ ];
105
+
106
+ expect(result.views).toEqual(expected);
107
+ });
108
+
109
+ it("should preserve custom IDs in views", () => {
110
+ const result = flow({
111
+ id: "custom-flow",
112
+ views: [
113
+ text().withId("custom-view-id").withValue("Custom ID View"),
114
+ text().withValue("Auto ID View"),
115
+ ],
116
+ navigation: {
117
+ BEGIN: "FLOW_1",
118
+ FLOW_1: {
119
+ startState: "VIEW_1",
120
+ VIEW_1: {
121
+ state_type: "VIEW",
122
+ ref: "custom-view-id",
123
+ transitions: {},
124
+ },
125
+ },
126
+ },
127
+ });
128
+
129
+ const expected: Flow["views"] = [
130
+ {
131
+ id: "custom-view-id",
132
+ type: "text",
133
+ value: "Custom ID View",
134
+ },
135
+ {
136
+ id: "custom-flow-views-1-text",
137
+ type: "text",
138
+ value: "Auto ID View",
139
+ },
140
+ ];
141
+
142
+ expect(result.views).toEqual(expected);
143
+ });
144
+
145
+ it("should handle function-based view generators", () => {
146
+ const result = flow({
147
+ id: "functional-flow",
148
+ views: [
149
+ (ctx) => ({
150
+ id: ctx.parentId ?? "default-id",
151
+ type: "text" as const,
152
+ value: "Function Generated View",
153
+ }),
154
+ ],
155
+ navigation: {
156
+ BEGIN: "FLOW_1",
157
+ FLOW_1: {
158
+ startState: "VIEW_1",
159
+ VIEW_1: {
160
+ state_type: "VIEW",
161
+ ref: "view-func",
162
+ transitions: {},
163
+ },
164
+ },
165
+ },
166
+ });
167
+
168
+ const expected: Flow["views"] = [
169
+ {
170
+ id: "functional-flow-views",
171
+ type: "text",
172
+ value: "Function Generated View",
173
+ },
174
+ ];
175
+
176
+ expect(result.views).toEqual(expected);
177
+ });
178
+
179
+ it("should handle a complex flow with multiple properties", () => {
180
+ const result = flow({
181
+ id: "complex-flow",
182
+ views: [
183
+ text().withId("view-1").withValue("First View"),
184
+ text().withId("view-2").withValue("Second View"),
185
+ ],
186
+ data: {
187
+ user: {
188
+ name: "John Doe",
189
+ email: "john@example.com",
190
+ },
191
+ settings: {
192
+ theme: "dark",
193
+ },
194
+ },
195
+ schema: {
196
+ ROOT: {
197
+ user: {
198
+ type: "object",
199
+ },
200
+ settings: {
201
+ type: "object",
202
+ },
203
+ },
204
+ },
205
+ navigation: {
206
+ BEGIN: "MAIN_FLOW",
207
+ MAIN_FLOW: {
208
+ startState: "FIRST_VIEW",
209
+ FIRST_VIEW: {
210
+ state_type: "VIEW",
211
+ ref: "view-1",
212
+ transitions: {
213
+ NEXT: "SECOND_VIEW",
214
+ },
215
+ },
216
+ SECOND_VIEW: {
217
+ state_type: "VIEW",
218
+ ref: "view-2",
219
+ transitions: {
220
+ DONE: "END_STATE",
221
+ },
222
+ },
223
+ END_STATE: {
224
+ state_type: "END",
225
+ outcome: "completed",
226
+ },
227
+ },
228
+ },
229
+ });
230
+
231
+ const expected: Flow = {
232
+ data: {
233
+ settings: {
234
+ theme: "dark",
235
+ },
236
+ user: {
237
+ email: "john@example.com",
238
+ name: "John Doe",
239
+ },
240
+ },
241
+ id: "complex-flow",
242
+ navigation: {
243
+ BEGIN: "MAIN_FLOW",
244
+ MAIN_FLOW: {
245
+ END_STATE: {
246
+ outcome: "completed",
247
+ state_type: "END",
248
+ },
249
+ FIRST_VIEW: {
250
+ ref: "view-1",
251
+ state_type: "VIEW",
252
+ transitions: {
253
+ NEXT: "SECOND_VIEW",
254
+ },
255
+ },
256
+ SECOND_VIEW: {
257
+ ref: "view-2",
258
+ state_type: "VIEW",
259
+ transitions: {
260
+ DONE: "END_STATE",
261
+ },
262
+ },
263
+ startState: "FIRST_VIEW",
264
+ },
265
+ },
266
+ schema: {
267
+ ROOT: {
268
+ settings: {
269
+ type: "object",
270
+ },
271
+ user: {
272
+ type: "object",
273
+ },
274
+ },
275
+ },
276
+ views: [
277
+ {
278
+ id: "view-1",
279
+ type: "text",
280
+ value: "First View",
281
+ },
282
+ {
283
+ id: "view-2",
284
+ type: "text",
285
+ value: "Second View",
286
+ },
287
+ ],
288
+ };
289
+
290
+ expect(result).toEqual(expected);
291
+ });
292
+ });
@@ -0,0 +1,141 @@
1
+ import { Asset, Flow, DataModel, Navigation, Schema } from "@player-ui/types";
2
+ import { BaseBuildContext } from "../base-builder";
3
+
4
+ /**
5
+ * Core options for creating a Player-UI Flow
6
+ */
7
+ interface CoreFlowOptions<
8
+ T extends Asset = Asset,
9
+ C extends BaseBuildContext = BaseBuildContext,
10
+ > {
11
+ id?: string;
12
+ views: Array<T | { build(context?: C): T } | ((ctx: C) => T)>;
13
+ data?: DataModel;
14
+ schema?: Schema.Schema;
15
+ navigation: Navigation;
16
+ context?: C;
17
+ }
18
+
19
+ /**
20
+ * Options for creating a Player-UI Flow
21
+ * Allows additional properties to be passed through to the final Flow
22
+ */
23
+ export type FlowOptions<
24
+ T extends Asset = Asset,
25
+ C extends BaseBuildContext = BaseBuildContext,
26
+ > = CoreFlowOptions<T, C> &
27
+ Omit<Flow<T>, keyof CoreFlowOptions<T, C> | "views">;
28
+
29
+ /**
30
+ * Creates a Player-UI Flow from the given options
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * import { flow } from './flow';
35
+ * import { text, autogenText } from './assets';
36
+ *
37
+ * // Basic usage (no autogen)
38
+ * const basicFlow = flow({
39
+ * id: 'example-flow',
40
+ * views: [text('Some Text').binding('label').id('view-1')],
41
+ * navigation: { ... }
42
+ * });
43
+ *
44
+ * // Recommended autogen usage
45
+ * const autogenFlow = flow({
46
+ * id: 'autogen-flow',
47
+ * autogenRunner: runner,
48
+ * autogenInputData: inputData, // Rich NLS data, title, subtitle, etc.
49
+ * views: [autogenText({ type: 'header' })],
50
+ * navigation: { ... }
51
+ * });
52
+ *
53
+ * // Advanced autogen usage (full control)
54
+ * const advancedFlow = flow({
55
+ * id: 'advanced-flow',
56
+ * autogenRunner: runner,
57
+ * autogenContext: customContext,
58
+ * views: [autogenText({ type: 'header' })],
59
+ * navigation: { ... }
60
+ * });
61
+ * ```
62
+ */
63
+ function isBuilder<T>(
64
+ value: T | { build(context?: BaseBuildContext): T },
65
+ ): value is { build(context?: BaseBuildContext): T } {
66
+ return (
67
+ typeof value === "object" &&
68
+ value !== null &&
69
+ "build" in value &&
70
+ typeof value.build === "function"
71
+ );
72
+ }
73
+
74
+ function isBuilderFunction<T>(
75
+ value: T | ((ctx: BaseBuildContext) => T),
76
+ ): value is (ctx: BaseBuildContext) => T {
77
+ return typeof value === "function";
78
+ }
79
+
80
+ export function flow<T extends Asset = Asset>(
81
+ options: FlowOptions<T>,
82
+ ): Flow<T> {
83
+ const flowId = options.id || "root";
84
+
85
+ const viewsNamespace = `${flowId}-views`;
86
+
87
+ const processedViews = (() => {
88
+ const processViews = (): T[] => {
89
+ const results: T[] = [];
90
+
91
+ for (let index = 0; index < options.views.length; index++) {
92
+ const viewOrFn = options.views[index];
93
+ const ctx: BaseBuildContext = {
94
+ parentId: viewsNamespace,
95
+ branch: {
96
+ type: "array-item",
97
+ index,
98
+ },
99
+ ...(options.context ?? {}),
100
+ };
101
+
102
+ if (isBuilder(viewOrFn)) {
103
+ results.push(viewOrFn.build(ctx));
104
+ } else if (isBuilderFunction(viewOrFn)) {
105
+ results.push(viewOrFn(ctx));
106
+ } else {
107
+ results.push(viewOrFn);
108
+ }
109
+ }
110
+
111
+ return results;
112
+ };
113
+
114
+ // No autogen runner, process views normally
115
+ return processViews();
116
+ })();
117
+
118
+ // Extract core properties that need special handling
119
+ const {
120
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
121
+ views: _views,
122
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
123
+ context: _context,
124
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
125
+ autogenRunner: _autogenRunner,
126
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
127
+ autogenInputData: _autogenInputData,
128
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
129
+ autogenContext: _autogenContext,
130
+ ...restOptions
131
+ } = options;
132
+
133
+ return {
134
+ ...restOptions,
135
+ id: flowId,
136
+ views: processedViews,
137
+ ...(options.data && { data: options.data }),
138
+ ...(options.schema && { schema: options.schema }),
139
+ navigation: options.navigation,
140
+ };
141
+ }
@@ -0,0 +1,8 @@
1
+ // Core utilities - building blocks for DSL generation
2
+ export * from "./base-builder";
3
+ export * from "./tagged-template";
4
+ export * from "./template";
5
+ export * from "./switch";
6
+ export * from "./flow";
7
+ export * from "./schema";
8
+ export * from "./utils";