@kaskad/schema 0.0.1 → 0.0.2

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 (136) hide show
  1. package/fesm2022/kaskad-schema.mjs +1368 -0
  2. package/fesm2022/kaskad-schema.mjs.map +1 -0
  3. package/package.json +9 -9
  4. package/types/kaskad-schema.d.ts +125 -0
  5. package/esm2022/index.js +0 -17
  6. package/esm2022/index.js.map +0 -1
  7. package/esm2022/kaskad-schema.js +0 -5
  8. package/esm2022/kaskad-schema.js.map +0 -1
  9. package/esm2022/lib/load/load-schema.js +0 -16
  10. package/esm2022/lib/load/load-schema.js.map +0 -1
  11. package/esm2022/lib/load/load-templates.js +0 -136
  12. package/esm2022/lib/load/load-templates.js.map +0 -1
  13. package/esm2022/lib/load/register-declared-type.js +0 -28
  14. package/esm2022/lib/load/register-declared-type.js.map +0 -1
  15. package/esm2022/lib/load/schema-component-extractor.js +0 -101
  16. package/esm2022/lib/load/schema-component-extractor.js.map +0 -1
  17. package/esm2022/lib/load/template-registry.js +0 -51
  18. package/esm2022/lib/load/template-registry.js.map +0 -1
  19. package/esm2022/lib/types/component-definition.js +0 -1
  20. package/esm2022/lib/types/component-definition.js.map +0 -1
  21. package/esm2022/lib/types/component-schema.js +0 -1
  22. package/esm2022/lib/types/component-schema.js.map +0 -1
  23. package/esm2022/lib/types/raw-schemas.js +0 -3
  24. package/esm2022/lib/types/raw-schemas.js.map +0 -1
  25. package/esm2022/lib/types/template-raw-definition.js +0 -1
  26. package/esm2022/lib/types/template-raw-definition.js.map +0 -1
  27. package/esm2022/lib/unfolding/computation/unfold-computation-schema.js +0 -70
  28. package/esm2022/lib/unfolding/computation/unfold-computation-schema.js.map +0 -1
  29. package/esm2022/lib/unfolding/computation/unfold-for-new-syntax.js +0 -147
  30. package/esm2022/lib/unfolding/computation/unfold-for-new-syntax.js.map +0 -1
  31. package/esm2022/lib/unfolding/computation/unfold-for.js +0 -45
  32. package/esm2022/lib/unfolding/computation/unfold-for.js.map +0 -1
  33. package/esm2022/lib/unfolding/computation/unfold-if.js +0 -18
  34. package/esm2022/lib/unfolding/computation/unfold-if.js.map +0 -1
  35. package/esm2022/lib/unfolding/computation/unfold-import.js +0 -26
  36. package/esm2022/lib/unfolding/computation/unfold-import.js.map +0 -1
  37. package/esm2022/lib/unfolding/computation/unfold-interpolation.js +0 -44
  38. package/esm2022/lib/unfolding/computation/unfold-interpolation.js.map +0 -1
  39. package/esm2022/lib/unfolding/parse-binding-shorthand.js +0 -40
  40. package/esm2022/lib/unfolding/parse-binding-shorthand.js.map +0 -1
  41. package/esm2022/lib/unfolding/parse-property-key-shorthand.js +0 -9
  42. package/esm2022/lib/unfolding/parse-property-key-shorthand.js.map +0 -1
  43. package/esm2022/lib/unfolding/parse-value-type.js +0 -115
  44. package/esm2022/lib/unfolding/parse-value-type.js.map +0 -1
  45. package/esm2022/lib/unfolding/to-full-notation.js +0 -124
  46. package/esm2022/lib/unfolding/to-full-notation.js.map +0 -1
  47. package/esm2022/lib/unfolding/unfold-flat-wrapper-notation.js +0 -72
  48. package/esm2022/lib/unfolding/unfold-flat-wrapper-notation.js.map +0 -1
  49. package/esm2022/lib/unfolding/unfold-node-schema.js +0 -35
  50. package/esm2022/lib/unfolding/unfold-node-schema.js.map +0 -1
  51. package/esm2022/lib/unfolding/unfold-property.js +0 -21
  52. package/esm2022/lib/unfolding/unfold-property.js.map +0 -1
  53. package/esm2022/lib/unfolding/value/handlers/index.js +0 -11
  54. package/esm2022/lib/unfolding/value/handlers/index.js.map +0 -1
  55. package/esm2022/lib/unfolding/value/handlers/unfold-array.js +0 -9
  56. package/esm2022/lib/unfolding/value/handlers/unfold-array.js.map +0 -1
  57. package/esm2022/lib/unfolding/value/handlers/unfold-boolean.js +0 -8
  58. package/esm2022/lib/unfolding/value/handlers/unfold-boolean.js.map +0 -1
  59. package/esm2022/lib/unfolding/value/handlers/unfold-command.js +0 -22
  60. package/esm2022/lib/unfolding/value/handlers/unfold-command.js.map +0 -1
  61. package/esm2022/lib/unfolding/value/handlers/unfold-component-schema.js +0 -14
  62. package/esm2022/lib/unfolding/value/handlers/unfold-component-schema.js.map +0 -1
  63. package/esm2022/lib/unfolding/value/handlers/unfold-component.js +0 -66
  64. package/esm2022/lib/unfolding/value/handlers/unfold-component.js.map +0 -1
  65. package/esm2022/lib/unfolding/value/handlers/unfold-map.js +0 -13
  66. package/esm2022/lib/unfolding/value/handlers/unfold-map.js.map +0 -1
  67. package/esm2022/lib/unfolding/value/handlers/unfold-number.js +0 -11
  68. package/esm2022/lib/unfolding/value/handlers/unfold-number.js.map +0 -1
  69. package/esm2022/lib/unfolding/value/handlers/unfold-object.js +0 -15
  70. package/esm2022/lib/unfolding/value/handlers/unfold-object.js.map +0 -1
  71. package/esm2022/lib/unfolding/value/handlers/unfold-set.js +0 -6
  72. package/esm2022/lib/unfolding/value/handlers/unfold-set.js.map +0 -1
  73. package/esm2022/lib/unfolding/value/handlers/unfold-shape.js +0 -13
  74. package/esm2022/lib/unfolding/value/handlers/unfold-shape.js.map +0 -1
  75. package/esm2022/lib/unfolding/value/handlers/unfold-string.js +0 -8
  76. package/esm2022/lib/unfolding/value/handlers/unfold-string.js.map +0 -1
  77. package/esm2022/lib/unfolding/value/handlers/unfold-unknown.js +0 -4
  78. package/esm2022/lib/unfolding/value/handlers/unfold-unknown.js.map +0 -1
  79. package/esm2022/lib/unfolding/value/handlers/unfold-variant-shape.js +0 -61
  80. package/esm2022/lib/unfolding/value/handlers/unfold-variant-shape.js.map +0 -1
  81. package/esm2022/lib/unfolding/value/handlers/validate-props.js +0 -20
  82. package/esm2022/lib/unfolding/value/handlers/validate-props.js.map +0 -1
  83. package/esm2022/lib/unfolding/value/throw-unfold-value-error.js +0 -11
  84. package/esm2022/lib/unfolding/value/throw-unfold-value-error.js.map +0 -1
  85. package/esm2022/lib/unfolding/value/unfold-node-value.js +0 -30
  86. package/esm2022/lib/unfolding/value/unfold-node-value.js.map +0 -1
  87. package/esm2022/lib/util/is-object.js +0 -4
  88. package/esm2022/lib/util/is-object.js.map +0 -1
  89. package/esm2022/lib/util/stringify-object.js +0 -4
  90. package/esm2022/lib/util/stringify-object.js.map +0 -1
  91. package/esm2022/lib/util/unfold-component-definitions.js +0 -21
  92. package/esm2022/lib/util/unfold-component-definitions.js.map +0 -1
  93. package/index.d.ts +0 -16
  94. package/kaskad-schema.d.ts +0 -5
  95. package/lib/load/load-schema.d.ts +0 -3
  96. package/lib/load/load-templates.d.ts +0 -2
  97. package/lib/load/register-declared-type.d.ts +0 -1
  98. package/lib/load/schema-component-extractor.d.ts +0 -2
  99. package/lib/load/template-registry.d.ts +0 -16
  100. package/lib/types/component-definition.d.ts +0 -5
  101. package/lib/types/component-schema.d.ts +0 -26
  102. package/lib/types/raw-schemas.d.ts +0 -25
  103. package/lib/types/template-raw-definition.d.ts +0 -7
  104. package/lib/unfolding/computation/unfold-computation-schema.d.ts +0 -2
  105. package/lib/unfolding/computation/unfold-for-new-syntax.d.ts +0 -6
  106. package/lib/unfolding/computation/unfold-for.d.ts +0 -2
  107. package/lib/unfolding/computation/unfold-if.d.ts +0 -3
  108. package/lib/unfolding/computation/unfold-import.d.ts +0 -2
  109. package/lib/unfolding/computation/unfold-interpolation.d.ts +0 -3
  110. package/lib/unfolding/parse-binding-shorthand.d.ts +0 -3
  111. package/lib/unfolding/parse-property-key-shorthand.d.ts +0 -2
  112. package/lib/unfolding/parse-value-type.d.ts +0 -2
  113. package/lib/unfolding/to-full-notation.d.ts +0 -8
  114. package/lib/unfolding/unfold-flat-wrapper-notation.d.ts +0 -13
  115. package/lib/unfolding/unfold-node-schema.d.ts +0 -3
  116. package/lib/unfolding/unfold-property.d.ts +0 -2
  117. package/lib/unfolding/value/handlers/index.d.ts +0 -10
  118. package/lib/unfolding/value/handlers/unfold-array.d.ts +0 -3
  119. package/lib/unfolding/value/handlers/unfold-boolean.d.ts +0 -2
  120. package/lib/unfolding/value/handlers/unfold-command.d.ts +0 -3
  121. package/lib/unfolding/value/handlers/unfold-component-schema.d.ts +0 -4
  122. package/lib/unfolding/value/handlers/unfold-component.d.ts +0 -3
  123. package/lib/unfolding/value/handlers/unfold-map.d.ts +0 -3
  124. package/lib/unfolding/value/handlers/unfold-number.d.ts +0 -2
  125. package/lib/unfolding/value/handlers/unfold-object.d.ts +0 -3
  126. package/lib/unfolding/value/handlers/unfold-set.d.ts +0 -3
  127. package/lib/unfolding/value/handlers/unfold-shape.d.ts +0 -3
  128. package/lib/unfolding/value/handlers/unfold-string.d.ts +0 -2
  129. package/lib/unfolding/value/handlers/unfold-unknown.d.ts +0 -2
  130. package/lib/unfolding/value/handlers/unfold-variant-shape.d.ts +0 -3
  131. package/lib/unfolding/value/handlers/validate-props.d.ts +0 -2
  132. package/lib/unfolding/value/throw-unfold-value-error.d.ts +0 -2
  133. package/lib/unfolding/value/unfold-node-value.d.ts +0 -8
  134. package/lib/util/is-object.d.ts +0 -1
  135. package/lib/util/stringify-object.d.ts +0 -1
  136. package/lib/util/unfold-component-definitions.d.ts +0 -4
@@ -0,0 +1,1368 @@
1
+ import { DefinitionStore } from '@kaskad/definition';
2
+ import { NodeTypes, isSwitchComputation, isIfComputation, isForComputation, isTemplateComputation } from '@kaskad/types';
3
+ import { Delimiters, log } from '@kaskad/config';
4
+ import { parse } from 'yaml';
5
+
6
+ // Recipe types used for unfolding
7
+ // These types represent the raw input format before normalization
8
+
9
+ function isObject(value) {
10
+ return value !== null && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Set);
11
+ }
12
+
13
+ function stringifyObject(label, data) {
14
+ return `\n${label}:\n${JSON.stringify(data, null, 2)}`;
15
+ }
16
+
17
+ function parseValueType(type) {
18
+ type = type.replaceAll(' ', '');
19
+ if (type.startsWith('(') && type.endsWith(')')) {
20
+ return createCommandType(type.slice(1, -1));
21
+ }
22
+ // Check for map type first (but not just '{}')
23
+ if (type.endsWith('{}') && type !== '{}') {
24
+ const rest = type.slice(0, -2);
25
+ return {
26
+ type: 'map',
27
+ item: parseValueType(rest),
28
+ };
29
+ }
30
+ // Check for set type
31
+ if (type.endsWith('#')) {
32
+ const rest = type.slice(0, -1);
33
+ return {
34
+ type: 'set',
35
+ item: parseValueType(rest),
36
+ };
37
+ }
38
+ if (type.endsWith('[]')) {
39
+ const rest = type.slice(0, -2);
40
+ return {
41
+ type: 'array',
42
+ item: parseValueType(rest),
43
+ };
44
+ }
45
+ // Check for object type after map/array to handle {} and complex object types
46
+ if (type.startsWith('{') && type.endsWith('}')) {
47
+ return createObjectType(type.slice(1, -1));
48
+ }
49
+ return createSimpleType(type);
50
+ }
51
+ function createObjectType(type) {
52
+ const fieldsPairs = splitTypesByComma(type);
53
+ const fields = fieldsPairs.reduce((acc, str) => {
54
+ if (!str)
55
+ return acc; // Handle empty object case
56
+ const colonIndex = str.indexOf(':');
57
+ const errorPrefix = `Invalid object field definition: "${str}"`;
58
+ if (colonIndex === -1) {
59
+ throw new Error(`${errorPrefix} - expected format "fieldName:type"`);
60
+ }
61
+ const fieldKey = str.substring(0, colonIndex);
62
+ const fieldType = str.substring(colonIndex + 1);
63
+ if (!fieldKey) {
64
+ throw new Error(`${errorPrefix} - field name cannot be empty`);
65
+ }
66
+ if (!fieldType) {
67
+ throw new Error(`${errorPrefix} - field type cannot be empty`);
68
+ }
69
+ acc[fieldKey] = parseValueType(fieldType);
70
+ return acc;
71
+ }, {});
72
+ return {
73
+ type: 'object',
74
+ fields,
75
+ };
76
+ }
77
+ function createCommandType(type) {
78
+ const args = splitTypesByComma(type).filter(Boolean);
79
+ return {
80
+ type: 'command',
81
+ args: args.map(parseValueType),
82
+ };
83
+ }
84
+ function createSimpleType(type) {
85
+ if (NodeTypes.includes(type)) {
86
+ return { type };
87
+ }
88
+ if (type in DefinitionStore.getInstance().shapes) {
89
+ return {
90
+ type: 'shape',
91
+ shapeType: type,
92
+ };
93
+ }
94
+ if (type in DefinitionStore.getInstance().variantShapes) {
95
+ return {
96
+ type: 'variantShape',
97
+ shapeType: type,
98
+ };
99
+ }
100
+ throw new Error(`Unknown type '${type}'`);
101
+ }
102
+ function splitTypesByComma(str) {
103
+ const pairs = [];
104
+ let braceCount = 0;
105
+ let part = '';
106
+ for (let i = 0; i < str.length; i++) {
107
+ const char = str[i];
108
+ if (char === '{') {
109
+ braceCount++;
110
+ part += char;
111
+ continue;
112
+ }
113
+ if (char === '}') {
114
+ braceCount--;
115
+ part += char;
116
+ continue;
117
+ }
118
+ if (char === ',' && braceCount === 0) {
119
+ pairs.push(part);
120
+ part = '';
121
+ }
122
+ else {
123
+ part += char;
124
+ }
125
+ }
126
+ pairs.push(part);
127
+ return pairs;
128
+ }
129
+
130
+ function parsePropertyKeyShorthand(shorthand) {
131
+ const [key, type] = shorthand.split('@');
132
+ if (!type) {
133
+ throw new Error(`Invalid node key expression "${key}". Expected type for key "${key}"`);
134
+ }
135
+ return [key, parseValueType(type)];
136
+ }
137
+
138
+ function unfoldForComputationSchema(schema) {
139
+ if (!isObject(schema) || !('computationType' in schema)) {
140
+ return null;
141
+ }
142
+ let rawDimensions = schema['dimensions'];
143
+ if (rawDimensions) {
144
+ if (!Array.isArray(rawDimensions)) {
145
+ throw new Error(`Invalid dimensions schema "${JSON.stringify(rawDimensions)}". It must be an array.`);
146
+ }
147
+ }
148
+ else {
149
+ // schema can be a flat object if only one dimension is defined
150
+ rawDimensions = [schema];
151
+ }
152
+ const dimensions = rawDimensions.map((dimension) => {
153
+ const { item = 'item', index = 'index', first = null, last = null } = dimension.as || {};
154
+ const itemsKey = Object.keys(dimension).find((key) => key.startsWith('items'));
155
+ const [items, itemsType] = parsePropertyKeyShorthand(itemsKey ?? '');
156
+ if (items !== 'items') {
157
+ throw new Error(`Computation "for" requires "items" field: ${JSON.stringify(schema)}`);
158
+ }
159
+ const itemsArr = dimension[itemsKey];
160
+ return {
161
+ items: unfoldNodeSchema(itemsArr, itemsType, []),
162
+ itemsType,
163
+ item: unfoldNodeSchema(item, { type: 'string' }, []),
164
+ index: unfoldNodeSchema(index, { type: 'string' }, []),
165
+ first: unfoldNodeSchema(first, { type: 'string' }, []),
166
+ last: unfoldNodeSchema(last, { type: 'string' }, []),
167
+ trackBy: unfoldNodeSchema(null, { type: 'unknown' }, []),
168
+ };
169
+ });
170
+ const normalizedYield = unfoldNodeSchema(schema['yield'], { type: 'unknown' }, []);
171
+ const result = {
172
+ computationType: 'for',
173
+ dimensions,
174
+ yield: normalizedYield,
175
+ [Symbol.for('type')]: 'ComputationSchema',
176
+ };
177
+ return result;
178
+ }
179
+
180
+ /**
181
+ * Parses metadata object from the `with` clause
182
+ * Example: "index: $i, first: $isFirst, last: $isLast"
183
+ */
184
+ function parseMetadata(str) {
185
+ const result = {};
186
+ const trimmed = str.trim();
187
+ if (!trimmed) {
188
+ throw new Error('Empty metadata block. Omit "with" clause if no metadata is needed.');
189
+ }
190
+ const pairs = trimmed.split(',').map((s) => s.trim());
191
+ for (const pair of pairs) {
192
+ const colonIndex = pair.indexOf(':');
193
+ if (colonIndex === -1) {
194
+ throw new Error(`Invalid metadata pair: "${pair}". Expected format: "key: $value"`);
195
+ }
196
+ const key = pair.slice(0, colonIndex).trim();
197
+ const value = pair.slice(colonIndex + 1).trim();
198
+ if (!['index', 'first', 'last'].includes(key)) {
199
+ throw new Error(`Invalid metadata key: "${key}". Only "index", "first", and "last" are allowed.`);
200
+ }
201
+ if (!value || !value.startsWith('$')) {
202
+ throw new Error(`Invalid metadata value: "${value}". Must be a variable starting with $.`);
203
+ }
204
+ result[key] = value.slice(1); // Remove $
205
+ }
206
+ return result;
207
+ }
208
+ /**
209
+ * Parses a single for-loop specification
210
+ * Example: "$row@Row of $rows with { index: $i, first: $isFirst } trackBy $row.id"
211
+ */
212
+ function parseForSpec(spec) {
213
+ const trimmed = spec.trim();
214
+ // First, extract trackBy if present at the end
215
+ // trackBy uses $variable or $variable.property syntax (e.g., "$item" or "$item.id")
216
+ let trackBy = null;
217
+ let specWithoutTrackBy = trimmed;
218
+ const trackByMatch = trimmed.match(/\s+trackBy\s+(\$\w+(?:\.\w+)*)$/);
219
+ if (trackByMatch) {
220
+ const trackByValue = trackByMatch[1].trim();
221
+ specWithoutTrackBy = trimmed.slice(0, trackByMatch.index).trim();
222
+ // Parse the $variable.property syntax
223
+ const pathMatch = trackByValue.match(/^\$(\w+)((?:\.\w+)*)$/);
224
+ if (!pathMatch) {
225
+ throw new Error(`Invalid trackBy syntax: ${trackByValue}. Expected format: $variable or $variable.property`);
226
+ }
227
+ const variableName = pathMatch[1]; // e.g., "item"
228
+ const propertyPath = pathMatch[2]; // e.g., ".id" or ""
229
+ // We'll validate against itemName after parsing the main pattern
230
+ // Store both parts temporarily - validation happens after we know itemName
231
+ trackBy = `${variableName}${propertyPath}`;
232
+ }
233
+ // Pattern: $item@Type of $items with { metadata }
234
+ // ^^^^^ ^^^^ ^^^^^^ ^^^^^^^^^^^^^^^
235
+ // name type expr metadata (optional)
236
+ // Note: Type can include dots for namespaced types like "app.table.Row"
237
+ const pattern = /^\$(\w+)@([\w.[\]{}:,\s]+)\s+of\s+(.+?)(?:\s+with\s*\{([^}]+)})?$/;
238
+ const match = specWithoutTrackBy.match(pattern);
239
+ if (!match) {
240
+ throw new Error(`Invalid for-loop syntax: "${spec}". Expected format: "$item@Type of $items" or "$item@Type of $items with { index: $i }"`);
241
+ }
242
+ const [, itemName, typeStr, itemsExpr, metadataStr] = match;
243
+ // Validate and process trackBy
244
+ // null = no trackBy specified (use index)
245
+ // "" = trackBy $var (use item value directly for primitives)
246
+ // "id" or "user.id" = trackBy $var.property (use property path)
247
+ let processedTrackBy = null;
248
+ if (trackBy !== null) {
249
+ // trackBy is in format "variableName" or "variableName.property.path"
250
+ const parts = trackBy.split('.');
251
+ const variableName = parts[0];
252
+ // Validate that trackBy references the loop variable
253
+ if (variableName !== itemName) {
254
+ throw new Error(`trackBy must reference loop variable $${itemName}, got $${variableName}`);
255
+ }
256
+ // Extract the property path (everything after the variable name)
257
+ // If parts.length === 1, it's just the variable (primitive array) -> "" (use item value)
258
+ // If parts.length > 1, it's a property path -> "id" or "user.id"
259
+ processedTrackBy = parts.length > 1 ? parts.slice(1).join('.') : '';
260
+ }
261
+ return {
262
+ itemName,
263
+ itemType: typeStr.trim(),
264
+ itemsExpr,
265
+ metadata: metadataStr ? parseMetadata(metadataStr) : {},
266
+ trackBy: processedTrackBy,
267
+ };
268
+ }
269
+ /**
270
+ * Unfolds the new for-loop syntax into a ForComputationSchema
271
+ * Supports: for: "$item@Type of $items" or for: ["$item1@Type1 of $items1", "$item2@Type2 of $items2"]
272
+ */
273
+ function unfoldForWithNewSyntax(schema) {
274
+ if (!isObject(schema) || !('for' in schema)) {
275
+ return null;
276
+ }
277
+ const forSpec = schema['for'];
278
+ const specs = Array.isArray(forSpec) ? forSpec : [forSpec];
279
+ if (specs.length === 0) {
280
+ throw new Error('For-loop requires at least one dimension');
281
+ }
282
+ const dimensions = specs.map((spec) => {
283
+ if (typeof spec !== 'string') {
284
+ throw new Error(`Invalid for-loop spec: ${JSON.stringify(spec)}. Must be a string.`);
285
+ }
286
+ const parsed = parseForSpec(spec);
287
+ // Parse the type annotation
288
+ // The type can be either:
289
+ // 1. An item type (e.g., "string") - we need to wrap in array
290
+ // 2. Already an array type (e.g., "string[]") - use as-is
291
+ const parsedType = parseValueType(parsed.itemType);
292
+ const itemsType = parsedType.type === 'array' ? parsedType : { type: 'array', item: parsedType };
293
+ return {
294
+ items: unfoldNodeSchema(parsed.itemsExpr, itemsType, []),
295
+ itemsType,
296
+ item: unfoldNodeSchema(parsed.itemName, { type: 'string' }, []),
297
+ index: unfoldNodeSchema(parsed.metadata.index || 'index', { type: 'string' }, []),
298
+ first: unfoldNodeSchema(parsed.metadata.first || null, { type: 'string' }, []),
299
+ last: unfoldNodeSchema(parsed.metadata.last || null, { type: 'string' }, []),
300
+ // trackBy: null = use index, property path (e.g., "id") = extract from item
301
+ trackBy: unfoldNodeSchema(parsed.trackBy, { type: 'string' }, []),
302
+ };
303
+ });
304
+ // Extract yield: either explicit 'yield' property or all other properties (inline yield)
305
+ let yieldSchema;
306
+ if ('yield' in schema) {
307
+ yieldSchema = schema['yield'];
308
+ }
309
+ else {
310
+ // Inline yield: all properties except 'for'
311
+ const { for: _, ...rest } = schema;
312
+ yieldSchema = rest;
313
+ }
314
+ const normalizedYield = unfoldNodeSchema(yieldSchema, { type: 'componentSchema' }, []);
315
+ const result = {
316
+ computationType: 'for',
317
+ dimensions,
318
+ yield: normalizedYield,
319
+ [Symbol.for('type')]: 'ComputationSchema',
320
+ };
321
+ return result;
322
+ }
323
+
324
+ function unfoldIfComputationSchema(schema) {
325
+ if (schema.if === undefined) {
326
+ throw new Error('If computation requires the "if" field.');
327
+ }
328
+ const result = {
329
+ computationType: 'if',
330
+ if: unfoldNodeSchema(schema.if, { type: 'boolean' }, []),
331
+ then: createBranch(schema.then),
332
+ else: createBranch(schema.else),
333
+ [Symbol.for('type')]: 'ComputationSchema',
334
+ };
335
+ return result;
336
+ }
337
+ function createBranch(schema = null) {
338
+ return unfoldNodeSchema(schema, { type: 'componentSchema' }, []);
339
+ }
340
+
341
+ function unfoldImportComputationSchema(schema) {
342
+ if (!isObject(schema) || !('computationType' in schema)) {
343
+ return null;
344
+ }
345
+ const { path, ref, computationType, _defer, ...contractVariables } = schema;
346
+ if (!path) {
347
+ throw new Error(`Import resolver requires a path field.`);
348
+ }
349
+ const defer = _defer
350
+ ? {
351
+ placeholder: unfoldNodeSchema(_defer.placeholder || null, { type: 'component' }, []),
352
+ error: unfoldNodeSchema(_defer.error || null, { type: 'component' }, []),
353
+ }
354
+ : null;
355
+ return {
356
+ computationType,
357
+ path,
358
+ defer,
359
+ ref,
360
+ contractVariables,
361
+ [Symbol.for('type')]: 'ComputationSchema',
362
+ };
363
+ }
364
+
365
+ function unfoldInterpolationComputationSchema(schema) {
366
+ const args = stringToFuncArgs(schema);
367
+ return { computationType: 'formula', formula: `concat(${args})` };
368
+ }
369
+ function replaceInterpolationsInFormula(formula) {
370
+ // Replace {{}} inside single-quoted string literals
371
+ let result = formula.replace(/'([^']*\{\{.*?\}\}[^']*)'/g, (_, content) => {
372
+ return `concat(${stringToFuncArgs(content)})`;
373
+ });
374
+ // Replace {{}} inside double-quoted string literals
375
+ result = result.replace(/"([^"]*\{\{.*?\}\}[^"]*)"/g, (_, content) => {
376
+ return `concat(${stringToFuncArgs(content)})`;
377
+ });
378
+ // Strip bare {{}} delimiters (expression is already valid formula syntax)
379
+ result = result.replace(/\{\{(.*?)\}\}/g, (_, expr) => expr.trim());
380
+ return result;
381
+ }
382
+ function stringToFuncArgs(str) {
383
+ const result = [];
384
+ let currentIndex = 0;
385
+ while (currentIndex < str.length) {
386
+ const startBrace = str.indexOf('{{', currentIndex);
387
+ if (startBrace === -1) {
388
+ const remaining = str.slice(currentIndex);
389
+ if (remaining)
390
+ result.push(`'${remaining}'`);
391
+ break;
392
+ }
393
+ const before = str.slice(currentIndex, startBrace);
394
+ if (before)
395
+ result.push(`'${before}'`);
396
+ const endBrace = str.indexOf('}}', startBrace);
397
+ if (!endBrace) {
398
+ result.push(str);
399
+ break;
400
+ }
401
+ const template = str.slice(startBrace + 2, endBrace).trim();
402
+ if (template)
403
+ result.push(template);
404
+ currentIndex = endBrace + 2;
405
+ }
406
+ return '[' + result.join(', ') + ']';
407
+ }
408
+
409
+ const unfoldFormula = (schema) => {
410
+ const result = {
411
+ computationType: 'formula',
412
+ formula: schema.formula,
413
+ [Symbol.for('type')]: 'ComputationSchema',
414
+ };
415
+ return result;
416
+ };
417
+ const unfoldSwitch = (schema) => {
418
+ return {
419
+ computationType: 'switch',
420
+ source: unfoldNodeSchema(schema.source, { type: 'unknown' }, []),
421
+ cases: schema.cases.map((c) => {
422
+ return {
423
+ equals: c.equals,
424
+ then: unfoldNodeSchema(c.then, { type: 'componentSchema' }),
425
+ };
426
+ }),
427
+ default: unfoldNodeSchema(schema.default, { type: 'componentSchema' }),
428
+ [Symbol.for('type')]: 'ComputationSchema',
429
+ };
430
+ };
431
+ function unfoldComputationSchema(schema) {
432
+ if (typeof schema === 'string') {
433
+ if (schema.startsWith('=> ')) {
434
+ let formula = schema.slice(3);
435
+ if (formula.includes('{{')) {
436
+ formula = replaceInterpolationsInFormula(formula);
437
+ }
438
+ return { computationType: 'formula', formula };
439
+ }
440
+ if (schema.includes('{{') && schema.includes('}}')) {
441
+ return unfoldInterpolationComputationSchema(schema);
442
+ }
443
+ }
444
+ // Check for new for-loop syntax (before computationType check)
445
+ if (isObject(schema) && 'for' in schema && !('computationType' in schema)) {
446
+ return unfoldForWithNewSyntax(schema);
447
+ }
448
+ if (!isObject(schema) || !('computationType' in schema)) {
449
+ return null;
450
+ }
451
+ if (isUnfoldedComputationSchema(schema)) {
452
+ return schema;
453
+ }
454
+ const computationUnfolders = {
455
+ formula: (schema) => unfoldFormula(schema),
456
+ for: (schema) => unfoldForComputationSchema(schema),
457
+ template: (schema) => unfoldImportComputationSchema(schema),
458
+ if: (schema) => unfoldIfComputationSchema(schema),
459
+ switch: (schema) => unfoldSwitch(schema),
460
+ };
461
+ const unfolder = computationUnfolders[schema['computationType']];
462
+ if (unfolder) {
463
+ return unfolder(schema);
464
+ }
465
+ throw new Error(`Unable to unfold computation schema.\n${stringifyObject('Schema', schema)}`);
466
+ }
467
+ function isUnfoldedComputationSchema(schema) {
468
+ return isObject(schema) && Symbol.for('type') in schema;
469
+ }
470
+
471
+ const MIRROR_PATTERN = /^<=>\s*(\S+)$/;
472
+ const SELECTION_PATTERN = /^<#>\s*(\S+)->\((\$\w+),\s*(\$\w+)\)$/;
473
+ const OBJECT_PATTERN = /^<\{\}>\s*(\S+)->\((\$\w+),\s*(\$\w+)\)$/;
474
+ const ARRAY_PATTERN = /^<\[\]>\s*(\S+)->\((\$\w+)\)$/;
475
+ function parseBindingShorthand(value) {
476
+ let match;
477
+ match = value.match(MIRROR_PATTERN);
478
+ if (match) {
479
+ return { bindingType: 'mirror', selector: match[1] };
480
+ }
481
+ match = value.match(SELECTION_PATTERN);
482
+ if (match) {
483
+ return {
484
+ bindingType: 'selection',
485
+ componentSelector: match[1],
486
+ mapping: { key: match[2], selected: match[3] },
487
+ };
488
+ }
489
+ match = value.match(OBJECT_PATTERN);
490
+ if (match) {
491
+ return {
492
+ bindingType: 'object',
493
+ componentSelector: match[1],
494
+ mapping: { key: match[2], value: match[3] },
495
+ };
496
+ }
497
+ match = value.match(ARRAY_PATTERN);
498
+ if (match) {
499
+ return {
500
+ bindingType: 'array',
501
+ componentSelector: match[1],
502
+ mapping: { value: match[2] },
503
+ };
504
+ }
505
+ return null;
506
+ }
507
+ function isBindingShorthand(value) {
508
+ return value.startsWith('<=>') || value.startsWith('<#>') || value.startsWith('<{}>') || value.startsWith('<[]>');
509
+ }
510
+
511
+ const WrapperPrefix = '+';
512
+ const PathSymbol = '/';
513
+ function unfoldFlatWrappers(wrappers, schema, resultingNode) {
514
+ const composed = wrappers
515
+ .reverse()
516
+ .reduce((composed, wrapper) => unfoldWrapper(wrapper, composed), schema);
517
+ if ('computationType' in composed) {
518
+ resultingNode._computation = composed;
519
+ }
520
+ else {
521
+ resultingNode._value = composed;
522
+ }
523
+ return resultingNode;
524
+ }
525
+ function unfoldWrapper(wrapper, schema) {
526
+ const [componentType, props] = wrapper;
527
+ if (componentType.includes(PathSymbol)) {
528
+ return {
529
+ computationType: 'template',
530
+ path: componentType,
531
+ ...props,
532
+ slot: schema,
533
+ };
534
+ }
535
+ const slot = getComponentSlot(componentType);
536
+ return {
537
+ componentType,
538
+ ...props,
539
+ [slot]: {
540
+ ...schema,
541
+ },
542
+ };
543
+ }
544
+ function getComponentSlot(wrapperType) {
545
+ const props = DefinitionStore.getInstance().getComputedComponentContract(wrapperType);
546
+ const componentSlots = Object.entries(props)
547
+ .filter(([, prop]) => prop.valueType.type === 'component')
548
+ .map(([key]) => key);
549
+ if (!componentSlots.length) {
550
+ throw new Error(`No component slot found for wrapper: "${wrapperType}"`);
551
+ }
552
+ if (componentSlots.length > 1) {
553
+ const defaultSlot = DefinitionStore.getInstance().getComponent(wrapperType)?.defaultSlot;
554
+ if (!defaultSlot) {
555
+ throw new Error(`Multiple component slots found for wrapper: "${wrapperType}". Please specify a default slot.`);
556
+ }
557
+ return defaultSlot;
558
+ }
559
+ return componentSlots[0];
560
+ }
561
+ function extractWrappers(schema) {
562
+ const wrappers = [];
563
+ const shallow = { ...schema };
564
+ for (const key in shallow) {
565
+ if (key.startsWith(WrapperPrefix)) {
566
+ const wrapperType = key.slice(1);
567
+ const wrapperProps = shallow[key] || {};
568
+ if (!isObject(wrapperProps)) {
569
+ throw new Error(`Properties for wrapper "${wrapperType}" must be an object`);
570
+ }
571
+ wrappers.push([wrapperType, wrapperProps]);
572
+ delete shallow[key];
573
+ }
574
+ }
575
+ return [wrappers, shallow];
576
+ }
577
+ function hasWrappers(schema) {
578
+ return Object.keys(schema).some((key) => key.startsWith(WrapperPrefix));
579
+ }
580
+
581
+ function toFullNotation(nodeSchema, valueType) {
582
+ const rawNode = {
583
+ _valueType: valueType,
584
+ _value: null,
585
+ _computation: null,
586
+ _binding: null,
587
+ };
588
+ if (nodeSchema === null) {
589
+ rawNode._value = null;
590
+ return rawNode;
591
+ }
592
+ if (typeof nodeSchema === 'string') {
593
+ if (nodeSchema.includes('{{') && nodeSchema.includes('}}')) {
594
+ rawNode._computation = nodeSchema;
595
+ return rawNode;
596
+ }
597
+ if (nodeSchema === '=>') {
598
+ rawNode._value = '=>';
599
+ return rawNode;
600
+ }
601
+ if (nodeSchema.startsWith('=> ')) {
602
+ rawNode._computation = nodeSchema;
603
+ return rawNode;
604
+ }
605
+ if (isBindingShorthand(nodeSchema)) {
606
+ const binding = parseBindingShorthand(nodeSchema);
607
+ if (binding) {
608
+ rawNode._binding = binding;
609
+ return rawNode;
610
+ }
611
+ }
612
+ }
613
+ if (Array.isArray(nodeSchema) && valueType.type === 'string') {
614
+ const desugared = desugarStringArray(nodeSchema);
615
+ return toFullNotation(desugared, valueType);
616
+ }
617
+ if (typeof nodeSchema !== 'object') {
618
+ rawNode._value = nodeSchema;
619
+ return rawNode;
620
+ }
621
+ if ('computationType' in nodeSchema) {
622
+ rawNode._computation = nodeSchema;
623
+ return rawNode;
624
+ }
625
+ if ('bindingType' in nodeSchema) {
626
+ rawNode._binding = nodeSchema;
627
+ return rawNode;
628
+ }
629
+ const isFullNotation = ['_valueType', '_value', '_computation', '_binding'].some((key) => key in nodeSchema);
630
+ if (isFullNotation) {
631
+ if ('_value' in nodeSchema)
632
+ rawNode._value = nodeSchema._value;
633
+ if ('_computation' in nodeSchema)
634
+ rawNode._computation = nodeSchema._computation;
635
+ if ('_binding' in nodeSchema)
636
+ rawNode._binding = nodeSchema._binding;
637
+ return rawNode;
638
+ }
639
+ // detect 'if' short notation
640
+ // TODO: change to _if to isolate keyword)
641
+ if ('if' in nodeSchema) {
642
+ const computation = {
643
+ computationType: 'if',
644
+ if: nodeSchema.if,
645
+ then: null,
646
+ else: null,
647
+ };
648
+ if (!('then' in nodeSchema) && !('else' in nodeSchema)) {
649
+ const { if: _, ...thenSchema } = nodeSchema;
650
+ computation['then'] = thenSchema;
651
+ }
652
+ else {
653
+ if ('then' in nodeSchema) {
654
+ computation['then'] = nodeSchema['then'];
655
+ }
656
+ if ('else' in nodeSchema) {
657
+ computation['else'] = nodeSchema['else'];
658
+ }
659
+ }
660
+ rawNode._computation = computation;
661
+ return rawNode;
662
+ }
663
+ // detect new 'for' syntax
664
+ if ('for' in nodeSchema) {
665
+ rawNode._computation = nodeSchema;
666
+ return rawNode;
667
+ }
668
+ if ('templateUrl' in nodeSchema) {
669
+ const schema = nodeSchema;
670
+ if (hasWrappers(schema)) {
671
+ const [wrappers, cleanedSchema] = extractWrappers(schema);
672
+ return unfoldFlatWrappers(wrappers, toFullNotation(cleanedSchema, valueType), rawNode);
673
+ }
674
+ const { templateUrl: path, ...rest } = nodeSchema;
675
+ rawNode._computation = { computationType: 'template', path, ...rest };
676
+ return rawNode;
677
+ }
678
+ if (hasWrappers(nodeSchema)) {
679
+ const [wrappers, cleanedSchema] = extractWrappers(nodeSchema);
680
+ return unfoldFlatWrappers(wrappers, toFullNotation(cleanedSchema, valueType), rawNode);
681
+ }
682
+ rawNode._value = nodeSchema;
683
+ return rawNode;
684
+ }
685
+ function desugarStringArray(items) {
686
+ if (items.length === 0)
687
+ return '';
688
+ if (items.length === 1)
689
+ return items[0];
690
+ for (const item of items) {
691
+ if (typeof item !== 'string') {
692
+ throw new Error(`styleClass array items must be strings, got ${typeof item}: ${JSON.stringify(item)}`);
693
+ }
694
+ }
695
+ const strings = items;
696
+ const hasFormula = strings.some((s) => s.startsWith('=> '));
697
+ if (!hasFormula)
698
+ return strings.join(' ');
699
+ const parts = strings.map((s) => (s.startsWith('=> ') ? s.slice(3) : `'${s}'`));
700
+ return `=> concat([${parts.join(', ')}], ' ')`;
701
+ }
702
+
703
+ function throwUnfoldError(ctx) {
704
+ let message = 'Error happened while unfolding node';
705
+ if (ctx.nodePath) {
706
+ message += `\nPath: ${JSON.stringify(ctx.nodePath)}.`;
707
+ }
708
+ message += stringifyObject('ValueType', ctx.valueType);
709
+ message += stringifyObject('Provided value', ctx.rawValue);
710
+ throw new Error(message);
711
+ }
712
+
713
+ const unfoldBoolean = (ctx) => {
714
+ if (typeof ctx.rawValue !== 'boolean') {
715
+ throwUnfoldError(ctx);
716
+ }
717
+ return ctx.rawValue;
718
+ };
719
+
720
+ function unfoldNumber(ctx) {
721
+ if (!isValidNumber(ctx.rawValue)) {
722
+ throwUnfoldError(ctx);
723
+ }
724
+ return ctx.rawValue;
725
+ }
726
+ function isValidNumber(value) {
727
+ return typeof value === 'number' && !isNaN(value);
728
+ }
729
+
730
+ function unfoldString(ctx) {
731
+ if (typeof ctx.rawValue !== 'string') {
732
+ throwUnfoldError(ctx);
733
+ }
734
+ return ctx.rawValue;
735
+ }
736
+
737
+ function unfoldUnknown(ctx) {
738
+ return ctx.rawValue;
739
+ }
740
+
741
+ function unfoldArray(ctx) {
742
+ if (!Array.isArray(ctx.rawValue)) {
743
+ throwUnfoldError(ctx);
744
+ }
745
+ return ctx.rawValue.map((item, index) => unfoldNodeSchema(item, ctx.valueType.item, [...ctx.nodePath, index]));
746
+ }
747
+
748
+ function unfoldMap(ctx) {
749
+ if (!isObject(ctx.rawValue)) {
750
+ throwUnfoldError(ctx);
751
+ }
752
+ return Object.entries(ctx.rawValue).reduce((structure, [key, child]) => {
753
+ structure[key] = unfoldNodeSchema(child, ctx.valueType.item, [...ctx.nodePath, key]);
754
+ return structure;
755
+ }, {});
756
+ }
757
+
758
+ function unfoldObject(ctx) {
759
+ if (!isObject(ctx.rawValue)) {
760
+ throwUnfoldError(ctx);
761
+ }
762
+ const rawObject = ctx.rawValue;
763
+ return Object.entries(ctx.valueType.fields).reduce((structure, [fieldKey, fieldValueType]) => {
764
+ const fieldValue = fieldKey in rawObject ? rawObject[fieldKey] : null;
765
+ structure[fieldKey] = unfoldNodeSchema(fieldValue, fieldValueType, [...ctx.nodePath, fieldKey]);
766
+ return structure;
767
+ }, {});
768
+ }
769
+
770
+ function unfoldCommand(ctx) {
771
+ const rawValue = ctx.rawValue;
772
+ if (typeof rawValue === 'string') {
773
+ return { runnerType: 'js', target: rawValue, owner: null };
774
+ }
775
+ if (isObject(rawValue) && 'runnerType' in rawValue && 'target' in rawValue) {
776
+ if (typeof rawValue['runnerType'] !== 'string') {
777
+ throw new Error(`Command schema is invalid. Field "runnerType" must be string.`);
778
+ }
779
+ if (!rawValue['target']) {
780
+ throw new Error(`Command schema is invalid. Field "target" must be defined.`);
781
+ }
782
+ return rawValue;
783
+ }
784
+ if (typeof rawValue === 'function') {
785
+ return { runnerType: 'inline', target: rawValue, owner: null };
786
+ }
787
+ throwUnfoldError(ctx);
788
+ }
789
+
790
+ function validateProps(rawSchema) {
791
+ const componentDefinitionProps = DefinitionStore.getInstance().getComputedComponentContract(rawSchema.componentType);
792
+ const ignoredSchemaProps = new Set([
793
+ 'componentType',
794
+ 'onNodeChange',
795
+ 'ref',
796
+ 'contract',
797
+ 'defaultSlot',
798
+ 'context',
799
+ 'types',
800
+ 'importTypes',
801
+ ]);
802
+ const invalidProps = Object.keys(rawSchema).filter((prop) => !componentDefinitionProps[prop] && !ignoredSchemaProps.has(prop) && !prop.startsWith(Delimiters.Variable));
803
+ if (invalidProps.length) {
804
+ throw new Error(`Component schema "${rawSchema.componentType}" contains unknown properties: ${invalidProps.join(', ')}`);
805
+ }
806
+ }
807
+
808
+ function unfoldComponentSchema(ctx) {
809
+ if (!isObject(ctx.rawValue)) {
810
+ throw new Error('Unfolded component schema type');
811
+ }
812
+ const componentRawSchema = ctx.rawValue;
813
+ if (!componentRawSchema.componentType) {
814
+ componentRawSchema.componentType = 'browser.Element';
815
+ }
816
+ validateProps(componentRawSchema);
817
+ return componentRawSchema;
818
+ }
819
+
820
+ function unfoldProperty(keyEx, rawSchema) {
821
+ if (isUnfoldedNodeSchema(rawSchema)) {
822
+ return [keyEx, rawSchema];
823
+ }
824
+ if (keyEx.includes('@')) {
825
+ const [key, valueType] = parsePropertyKeyShorthand(keyEx);
826
+ const nodeSchema = unfoldNodeSchema(toFullNotation(rawSchema, valueType), valueType, [key]);
827
+ return [key, nodeSchema];
828
+ }
829
+ if (isObject(rawSchema) && '_valueType' in rawSchema) {
830
+ const valueType = rawSchema['_valueType']; // TODO: check if valueType is valid
831
+ const nodeSchema = unfoldNodeSchema(rawSchema, valueType, [keyEx]);
832
+ return [keyEx, nodeSchema];
833
+ }
834
+ throw new Error(`Cannot unfold node schema. Invalid schema notation ${JSON.stringify(rawSchema)} for key "${keyEx}"`);
835
+ }
836
+
837
+ function unfoldComponent(ctx) {
838
+ if (typeof ctx.rawValue === 'string' && ctx.rawValue.startsWith('csid-')) {
839
+ return ctx.rawValue;
840
+ }
841
+ if (!isObject(ctx.rawValue)) {
842
+ throwUnfoldError(ctx);
843
+ }
844
+ const componentRawSchema = ctx.rawValue;
845
+ if (!componentRawSchema.componentType) {
846
+ componentRawSchema.componentType = 'browser.Element';
847
+ }
848
+ validateProps(componentRawSchema);
849
+ const properties = DefinitionStore.getInstance().getComputedComponentContract(componentRawSchema.componentType);
850
+ const props = new Map();
851
+ for (const [key, baseSchema] of Object.entries(properties)) {
852
+ if (!(key in componentRawSchema)) {
853
+ props.set(key, baseSchema);
854
+ continue;
855
+ }
856
+ const unfoldedRawSchema = unfoldNodeSchema(componentRawSchema[key], baseSchema.valueType, [...ctx.nodePath, key]);
857
+ const node = {
858
+ valueType: baseSchema.valueType,
859
+ value: unfoldedRawSchema.value ?? baseSchema.value,
860
+ computation: unfoldedRawSchema.computation || baseSchema.computation,
861
+ binding: unfoldedRawSchema.binding || baseSchema.binding,
862
+ };
863
+ props.set(key, node);
864
+ }
865
+ const variables = new Map();
866
+ for (const [keyEx, schema] of Object.entries(componentRawSchema)) {
867
+ if (!keyEx.startsWith(Delimiters.Variable)) {
868
+ continue;
869
+ }
870
+ const variableKeyEx = keyEx.slice(Delimiters.Variable.length);
871
+ const [key, unfolded] = unfoldProperty(variableKeyEx, schema);
872
+ variables.set(key, unfolded);
873
+ }
874
+ const rawHandlers = componentRawSchema.onNodeChange || [];
875
+ const onNodeChange = rawHandlers.map((handler) => {
876
+ if (!handler.selector) {
877
+ throw new Error(`onNodeChange handler "${JSON.stringify(handler)}" is missing required selector.`);
878
+ }
879
+ if (!handler.command) {
880
+ throw new Error(`onNodeChange handler "${JSON.stringify(handler)}" is missing required command.`);
881
+ }
882
+ return {
883
+ selector: handler.selector,
884
+ command: unfoldNodeSchema(handler.command, { type: 'command' }, ctx.nodePath),
885
+ };
886
+ });
887
+ return {
888
+ componentType: componentRawSchema.componentType,
889
+ ref: componentRawSchema.ref || '',
890
+ props,
891
+ variables,
892
+ onNodeChange,
893
+ };
894
+ }
895
+
896
+ function unfoldSet(ctx) {
897
+ const items = ctx.rawValue instanceof Set ? Array.from(ctx.rawValue) : Array.isArray(ctx.rawValue) ? ctx.rawValue : [];
898
+ return items.map((item, index) => unfoldNodeSchema(item, ctx.valueType.item, [...ctx.nodePath, index]));
899
+ }
900
+
901
+ function unfoldShape(ctx) {
902
+ if (!isObject(ctx.rawValue)) {
903
+ throwUnfoldError(ctx);
904
+ }
905
+ const rawValue = ctx.rawValue;
906
+ const objectValueType = DefinitionStore.getInstance().getShape(ctx.valueType.shapeType);
907
+ return unfoldObject({ rawValue, valueType: objectValueType, nodePath: ctx.nodePath });
908
+ }
909
+
910
+ function unfoldShortValidatorSyntax(rawValue) {
911
+ if ('validatorType' in rawValue) {
912
+ return rawValue;
913
+ }
914
+ const entries = Object.entries(rawValue);
915
+ const validatorEntries = entries.filter(([key]) => key !== 'message' && key !== 'disabled');
916
+ if (validatorEntries.length === 0) {
917
+ throw new Error(`Validator must have at least one validator type specified. Raw value: ${JSON.stringify(rawValue)}`);
918
+ }
919
+ if (validatorEntries.length > 1) {
920
+ throw new Error(`Validator can only have one validator type specified. Raw value: ${JSON.stringify(rawValue)}`);
921
+ }
922
+ const [validatorType, param] = validatorEntries[0];
923
+ const validator = {
924
+ validatorType,
925
+ params: {},
926
+ message: rawValue['message'] || '',
927
+ disabled: !!rawValue['disabled'],
928
+ };
929
+ if (isObject(param)) {
930
+ validator.params = param;
931
+ }
932
+ else {
933
+ validator.params = { [validatorType]: param };
934
+ }
935
+ return validator;
936
+ }
937
+ function unfoldVariantShape(ctx) {
938
+ if (!isObject(ctx.rawValue)) {
939
+ throwUnfoldError(ctx);
940
+ }
941
+ let rawValue = ctx.rawValue;
942
+ if ('kind' in rawValue && 'fields' in rawValue) {
943
+ const kindKey = `${ctx.valueType.shapeType}Type`;
944
+ const kind = rawValue['kind'];
945
+ const fields = rawValue['fields'];
946
+ return {
947
+ ...fields,
948
+ [kindKey]: kind,
949
+ };
950
+ }
951
+ const kindKey = `${ctx.valueType.shapeType}Type`;
952
+ if (kindKey === 'validatorType') {
953
+ rawValue = unfoldShortValidatorSyntax(rawValue);
954
+ }
955
+ const kind = rawValue[kindKey];
956
+ if (!kind) {
957
+ throw new Error(`Missing the kind field "${kindKey}"`);
958
+ }
959
+ const objectValueType = DefinitionStore.getInstance().getVariantShape(ctx.valueType.shapeType, kind);
960
+ const objectNode = unfoldNodeSchema(rawValue, objectValueType, ctx.nodePath);
961
+ return {
962
+ ...objectNode.value,
963
+ [kindKey]: kind,
964
+ };
965
+ }
966
+
967
+ const handlers = {
968
+ unknown: unfoldUnknown,
969
+ number: unfoldNumber,
970
+ string: unfoldString,
971
+ boolean: unfoldBoolean,
972
+ array: unfoldArray,
973
+ map: unfoldMap,
974
+ set: unfoldSet,
975
+ object: unfoldObject,
976
+ shape: unfoldShape,
977
+ variantShape: unfoldVariantShape,
978
+ command: unfoldCommand,
979
+ componentSchema: unfoldComponentSchema,
980
+ component: unfoldComponent,
981
+ };
982
+ function unfoldValue(ctx) {
983
+ const handler = handlers[ctx.valueType.type];
984
+ if (!handler) {
985
+ throw new Error(`Cannot unfold value "${ctx.rawValue}". Unknown value type '${ctx.valueType.type}'`);
986
+ }
987
+ if (ctx.rawValue === undefined || ctx.rawValue === null) {
988
+ return null;
989
+ }
990
+ return handler(ctx);
991
+ }
992
+
993
+ function unfoldNodeSchema(rawSchema, valueType, nodePath = []) {
994
+ if (isUnfoldedNodeSchema(rawSchema)) {
995
+ return rawSchema;
996
+ }
997
+ const node = {
998
+ valueType,
999
+ value: null,
1000
+ computation: null,
1001
+ binding: null,
1002
+ [Symbol.for('type')]: 'NodeSchema',
1003
+ };
1004
+ const nodeSchema = toFullNotation(rawSchema, valueType);
1005
+ if ('_binding' in nodeSchema) {
1006
+ node.binding = nodeSchema['_binding'];
1007
+ }
1008
+ if ('_computation' in nodeSchema) {
1009
+ node.computation = unfoldComputationSchema(nodeSchema._computation);
1010
+ }
1011
+ if ('_value' in nodeSchema) {
1012
+ node.value = unfoldValue({
1013
+ rawValue: nodeSchema._value,
1014
+ valueType,
1015
+ nodePath,
1016
+ });
1017
+ }
1018
+ return node;
1019
+ }
1020
+ function isUnfoldedNodeSchema(nodeSchema) {
1021
+ return isObject(nodeSchema) && ['valueType', 'value', 'computation', 'binding'].every((key) => key in nodeSchema);
1022
+ }
1023
+
1024
+ function extractComponentTypes(schema, types = new Set()) {
1025
+ extractFromValue(schema, types);
1026
+ extractFromComputations(schema, types);
1027
+ return types;
1028
+ }
1029
+ function extractFromValue(schema, types) {
1030
+ if (!schema.value) {
1031
+ return types;
1032
+ }
1033
+ const type = schema.valueType.type;
1034
+ if (type === 'component') {
1035
+ const componentSchema = schema;
1036
+ if (!isObject(componentSchema.value)) {
1037
+ return types;
1038
+ }
1039
+ types.add(componentSchema.value.componentType);
1040
+ for (const prop of componentSchema.value.props.values()) {
1041
+ extractComponentTypes(prop, types);
1042
+ }
1043
+ for (const variable of componentSchema.value.variables.values()) {
1044
+ extractComponentTypes(variable, types);
1045
+ }
1046
+ }
1047
+ if (type === 'componentSchema' || type === 'unknown') {
1048
+ const componentSchema = schema;
1049
+ if (isObject(componentSchema.value) && 'componentType' in componentSchema.value) {
1050
+ extractFromRawComponentSchema(componentSchema.value, types);
1051
+ }
1052
+ }
1053
+ if (type === 'array') {
1054
+ const arraySchema = schema;
1055
+ for (const item of arraySchema.value || []) {
1056
+ extractComponentTypes(item, types);
1057
+ }
1058
+ }
1059
+ if (type === 'object' || type === 'map') {
1060
+ const objectSchema = schema;
1061
+ for (const prop of Object.values(objectSchema.value || {})) {
1062
+ extractComponentTypes(prop, types);
1063
+ }
1064
+ }
1065
+ return types;
1066
+ }
1067
+ function extractFromComputations(schema, types) {
1068
+ if (!schema.computation) {
1069
+ return types;
1070
+ }
1071
+ if (isSwitchComputation(schema.computation)) {
1072
+ for (const caseItem of schema.computation.cases) {
1073
+ extractComponentTypes(caseItem.then, types);
1074
+ }
1075
+ extractComponentTypes(schema.computation.default, types);
1076
+ }
1077
+ if (isIfComputation(schema.computation)) {
1078
+ extractComponentTypes(schema.computation.then, types);
1079
+ extractComponentTypes(schema.computation.else, types);
1080
+ }
1081
+ if (isForComputation(schema.computation)) {
1082
+ extractComponentTypes(schema.computation.yield, types);
1083
+ }
1084
+ if (isTemplateComputation(schema.computation)) {
1085
+ if (schema.computation.defer) {
1086
+ if (schema.computation.defer.placeholder) {
1087
+ extractComponentTypes(schema.computation.defer.placeholder, types);
1088
+ }
1089
+ if (schema.computation.defer.error) {
1090
+ extractComponentTypes(schema.computation.defer.error, types);
1091
+ }
1092
+ }
1093
+ for (const variable of Object.values(schema.computation.contractVariables)) {
1094
+ if (isObject(variable) || Array.isArray(variable)) {
1095
+ extractFromRawComponentSchema(variable, types);
1096
+ }
1097
+ }
1098
+ }
1099
+ return types;
1100
+ }
1101
+ function extractFromRawComponentSchema(schema, types) {
1102
+ if (Array.isArray(schema)) {
1103
+ for (const item of schema) {
1104
+ if (isObject(item) || Array.isArray(item)) {
1105
+ extractFromRawComponentSchema(item, types);
1106
+ }
1107
+ }
1108
+ return types;
1109
+ }
1110
+ if (isObject(schema)) {
1111
+ for (const [key, value] of Object.entries(schema)) {
1112
+ if (key === 'componentType' && typeof value === 'string') {
1113
+ types.add(value);
1114
+ }
1115
+ if (isObject(value) || Array.isArray(value)) {
1116
+ extractFromRawComponentSchema(value, types);
1117
+ }
1118
+ }
1119
+ }
1120
+ return types;
1121
+ }
1122
+
1123
+ const templateRegistry = {
1124
+ entries: new Map(),
1125
+ get(url) {
1126
+ return this.entries.get(url);
1127
+ },
1128
+ getAggregatedComponentTypes(url) {
1129
+ const aggregated = new Set();
1130
+ const visited = new Set();
1131
+ const collect = (currentUrl) => {
1132
+ if (visited.has(currentUrl))
1133
+ return;
1134
+ visited.add(currentUrl);
1135
+ const entry = this.entries.get(currentUrl);
1136
+ if (!entry)
1137
+ return;
1138
+ // Add own component types
1139
+ for (const type of entry.ownComponentTypes) {
1140
+ aggregated.add(type);
1141
+ }
1142
+ // Recursively collect from dependencies
1143
+ for (const depUrl of entry.dependencies) {
1144
+ collect(depUrl);
1145
+ }
1146
+ };
1147
+ collect(url);
1148
+ return aggregated;
1149
+ },
1150
+ getAllComponentTypes() {
1151
+ const allTypes = new Set();
1152
+ for (const entry of this.entries.values()) {
1153
+ for (const type of entry.ownComponentTypes) {
1154
+ allTypes.add(type);
1155
+ }
1156
+ }
1157
+ return allTypes;
1158
+ },
1159
+ set(url, recipe, ownComponentTypes, dependencies) {
1160
+ this.entries.set(url, {
1161
+ recipe,
1162
+ ownComponentTypes,
1163
+ dependencies: dependencies || new Set(),
1164
+ });
1165
+ },
1166
+ has(url) {
1167
+ return this.entries.has(url);
1168
+ },
1169
+ reset() {
1170
+ this.entries.clear();
1171
+ },
1172
+ };
1173
+
1174
+ const withBaseTemplateUrl = (url) => `/recipes/${url}.yml`;
1175
+ async function loadRecipe(url) {
1176
+ const response = await fetch(withBaseTemplateUrl(url));
1177
+ if (response.status === 404) {
1178
+ throw new Error(`Schema not found: ${url}`);
1179
+ }
1180
+ const text = await response.text();
1181
+ try {
1182
+ return parse(text);
1183
+ }
1184
+ catch (e) {
1185
+ throw new Error(`Failed to parse YAML: ${e}`);
1186
+ }
1187
+ }
1188
+
1189
+ function registerDeclaredType(name, definition) {
1190
+ let properties;
1191
+ if (typeof definition === 'string') {
1192
+ const parsed = parseValueType(definition);
1193
+ if (parsed.type !== 'object') {
1194
+ throw new Error(`Type "${name}" must be an object type, got "${parsed.type}"`);
1195
+ }
1196
+ properties = parsed.fields;
1197
+ }
1198
+ else {
1199
+ properties = {};
1200
+ for (const [propName, propType] of Object.entries(definition)) {
1201
+ properties[propName] = parseValueType(propType);
1202
+ }
1203
+ }
1204
+ const store = DefinitionStore.getInstance();
1205
+ const existing = store.shapes[name];
1206
+ if (existing && !isEqualType(existing.properties, properties)) {
1207
+ throw new Error(`Type "${name}" is already defined with a different shape`);
1208
+ }
1209
+ store.setShape(name, properties);
1210
+ }
1211
+ function isEqualType(a, b) {
1212
+ return JSON.stringify(a) === JSON.stringify(b);
1213
+ }
1214
+
1215
+ const reservedKeys = new Set(['importTypes', 'types', 'contract', 'defaultSlot']);
1216
+ function isTypeOnlyTemplate(template) {
1217
+ const contentKeys = Object.keys(template).filter((k) => !reservedKeys.has(k));
1218
+ return contentKeys.length > 0 && contentKeys.every((k) => k.includes('.'));
1219
+ }
1220
+ async function loadTemplates(recipe) {
1221
+ // Pass 1: Recursively load all templates and register types immediately
1222
+ const allTemplates = new Map();
1223
+ await collectAllTemplates(recipe, allTemplates);
1224
+ // Register types from the root recipe (after all dependencies are loaded)
1225
+ const rootTypes = recipe.types;
1226
+ if (rootTypes) {
1227
+ for (const [key, value] of Object.entries(rootTypes)) {
1228
+ if (typeof value === 'string' || isObject(value)) {
1229
+ registerDeclaredType(key, value);
1230
+ }
1231
+ }
1232
+ }
1233
+ // Pass 2: Unfold and register all templates (types are already registered)
1234
+ for (const [url, template] of allTemplates) {
1235
+ try {
1236
+ const { types: _types, importTypes: _importTypes, contract: _contract, defaultSlot: _defaultSlot, ...rootRawSchema } = template;
1237
+ // Skip unfolding for type-only templates (all keys are namespaced)
1238
+ if (isTypeOnlyTemplate(template)) {
1239
+ templateRegistry.set(url, template, new Set(), extractUrls(template));
1240
+ continue;
1241
+ }
1242
+ const schema = unfoldNodeSchema(rootRawSchema, { type: 'component' });
1243
+ const componentTypes = extractComponentTypes(schema);
1244
+ const dependencies = extractUrls(template);
1245
+ templateRegistry.set(url, template, componentTypes, dependencies);
1246
+ }
1247
+ catch (e) {
1248
+ log.error(`Failed to resolve template: ${url}`, e);
1249
+ throw new Error(`Failed to resolve template: ${url}`, { cause: e });
1250
+ }
1251
+ }
1252
+ }
1253
+ async function collectAllTemplates(recipe, collected) {
1254
+ const urls = extractUrls(recipe);
1255
+ const newUrls = Array.from(urls).filter((url) => !templateRegistry.has(url) && !collected.has(url));
1256
+ if (newUrls.length === 0)
1257
+ return;
1258
+ const templates = await Promise.all(newUrls.map((url) => loadRecipe(url)));
1259
+ // Register types immediately after loading (before recursive collection)
1260
+ const pending = new Map();
1261
+ for (const template of templates) {
1262
+ const isTypeOnlyFile = isTypeOnlyTemplate(template);
1263
+ const typesToRegister = isTypeOnlyFile ? template : (template.types ?? {});
1264
+ for (const [key, value] of Object.entries(typesToRegister)) {
1265
+ if (key === 'importTypes')
1266
+ continue;
1267
+ if (isTypeOnlyFile && !key.includes('.')) {
1268
+ throw new Error(`Invalid key "${key}" in type-only file. Type names must be namespaced (e.g., "app.MyType")`);
1269
+ }
1270
+ if (typeof value !== 'string' && !isObject(value)) {
1271
+ throw new Error(`Invalid type definition for "${key}". Expected string or object, got ${typeof value}`);
1272
+ }
1273
+ pending.set(key, value);
1274
+ }
1275
+ }
1276
+ // Register with retries to handle forward references between types
1277
+ let lastSize = -1;
1278
+ while (pending.size > 0 && pending.size !== lastSize) {
1279
+ lastSize = pending.size;
1280
+ for (const [key, value] of pending) {
1281
+ try {
1282
+ registerDeclaredType(key, value);
1283
+ pending.delete(key);
1284
+ }
1285
+ catch {
1286
+ // Will retry after other types are registered
1287
+ }
1288
+ }
1289
+ }
1290
+ for (const [key, value] of pending) {
1291
+ try {
1292
+ registerDeclaredType(key, value);
1293
+ }
1294
+ catch (e) {
1295
+ throw new Error(`Failed to register type "${key}"`, { cause: e });
1296
+ }
1297
+ }
1298
+ for (let i = 0; i < newUrls.length; i++) {
1299
+ collected.set(newUrls[i], templates[i]);
1300
+ }
1301
+ // Recursively collect from loaded templates
1302
+ await Promise.all(templates.map((t) => collectAllTemplates(t, collected)));
1303
+ }
1304
+ function extractUrls(value, urls = new Set()) {
1305
+ if (value == null)
1306
+ return urls;
1307
+ if (Array.isArray(value)) {
1308
+ value.forEach((item) => extractUrls(item, urls));
1309
+ return urls;
1310
+ }
1311
+ if (!isObject(value))
1312
+ return urls;
1313
+ if ('_defer' in value)
1314
+ return urls;
1315
+ for (const [key, val] of Object.entries(value)) {
1316
+ if (key.startsWith(WrapperPrefix) && key.includes(PathSymbol)) {
1317
+ urls.add(key.slice(1));
1318
+ }
1319
+ if (key === 'importTypes') {
1320
+ if (!Array.isArray(val)) {
1321
+ throw new Error(`"importTypes" must be an array, got ${typeof val}`);
1322
+ }
1323
+ for (const url of val) {
1324
+ if (typeof url !== 'string') {
1325
+ throw new Error(`"importTypes" entries must be strings, got ${typeof url}`);
1326
+ }
1327
+ urls.add(url);
1328
+ }
1329
+ continue;
1330
+ }
1331
+ if (typeof val !== 'string') {
1332
+ extractUrls(val, urls);
1333
+ continue;
1334
+ }
1335
+ if (key === 'templateUrl') {
1336
+ urls.add(val);
1337
+ }
1338
+ extractUrls(val, urls);
1339
+ }
1340
+ return urls;
1341
+ }
1342
+
1343
+ function unfoldComponentDefinition(rawDefinition) {
1344
+ const properties = {};
1345
+ for (const entry of Object.entries(rawDefinition.properties)) {
1346
+ const [key, prop] = unfoldProperty(...entry);
1347
+ properties[key] = prop;
1348
+ }
1349
+ return {
1350
+ traits: rawDefinition.traits,
1351
+ properties,
1352
+ defaultSlot: rawDefinition.defaultSlot,
1353
+ };
1354
+ }
1355
+ function unfoldComponentDefinitions(rawDefinitions) {
1356
+ const result = {};
1357
+ for (const [componentType, rawDefinition] of Object.entries(rawDefinitions)) {
1358
+ result[componentType] = unfoldComponentDefinition(rawDefinition);
1359
+ }
1360
+ return result;
1361
+ }
1362
+
1363
+ /**
1364
+ * Generated bundle index. Do not edit.
1365
+ */
1366
+
1367
+ export { PathSymbol, WrapperPrefix, extractComponentTypes, extractWrappers, hasWrappers, isObject, isUnfoldedNodeSchema, loadRecipe, loadTemplates, parseValueType, registerDeclaredType, templateRegistry, toFullNotation, unfoldComponentDefinition, unfoldComponentDefinitions, unfoldFlatWrappers, unfoldNodeSchema, unfoldProperty, withBaseTemplateUrl };
1368
+ //# sourceMappingURL=kaskad-schema.mjs.map