@rcrsr/rill 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +37 -21
  2. package/dist/ext/crypto/index.d.ts +3 -3
  3. package/dist/ext/crypto/index.js +61 -58
  4. package/dist/ext/exec/index.d.ts +3 -3
  5. package/dist/ext/exec/index.js +14 -8
  6. package/dist/ext/fetch/index.d.ts +3 -3
  7. package/dist/ext/fetch/index.js +16 -11
  8. package/dist/ext/fs/index.d.ts +3 -3
  9. package/dist/ext/fs/index.js +242 -239
  10. package/dist/ext/kv/index.d.ts +3 -3
  11. package/dist/ext/kv/index.js +197 -195
  12. package/dist/ext/kv/store.js +2 -1
  13. package/dist/ext-parse-bridge.d.ts +10 -0
  14. package/dist/ext-parse-bridge.js +10 -0
  15. package/dist/generated/introspection-data.d.ts +1 -1
  16. package/dist/generated/introspection-data.js +385 -296
  17. package/dist/generated/version-data.d.ts +1 -1
  18. package/dist/generated/version-data.js +2 -2
  19. package/dist/index.d.ts +15 -4
  20. package/dist/index.js +14 -5
  21. package/dist/parser/parser-types.js +12 -0
  22. package/dist/parser/parser-use.js +7 -1
  23. package/dist/runtime/core/callable.d.ts +20 -8
  24. package/dist/runtime/core/callable.js +63 -23
  25. package/dist/runtime/core/context.d.ts +0 -11
  26. package/dist/runtime/core/context.js +76 -75
  27. package/dist/runtime/core/eval/index.d.ts +2 -2
  28. package/dist/runtime/core/eval/index.js +11 -0
  29. package/dist/runtime/core/eval/mixins/closures.js +15 -15
  30. package/dist/runtime/core/eval/mixins/conversion.js +51 -110
  31. package/dist/runtime/core/eval/mixins/core.js +2 -2
  32. package/dist/runtime/core/eval/mixins/expressions.js +35 -27
  33. package/dist/runtime/core/eval/mixins/literals.js +3 -3
  34. package/dist/runtime/core/eval/mixins/types.js +44 -54
  35. package/dist/runtime/core/eval/mixins/variables.js +10 -8
  36. package/dist/runtime/core/field-descriptor.d.ts +3 -3
  37. package/dist/runtime/core/field-descriptor.js +2 -1
  38. package/dist/runtime/core/introspection.js +6 -6
  39. package/dist/runtime/core/markers.d.ts +12 -0
  40. package/dist/runtime/core/markers.js +7 -0
  41. package/dist/runtime/core/type-registrations.d.ts +136 -0
  42. package/dist/runtime/core/type-registrations.js +749 -0
  43. package/dist/runtime/core/type-structures.d.ts +128 -0
  44. package/dist/runtime/core/type-structures.js +12 -0
  45. package/dist/runtime/core/types.d.ts +15 -3
  46. package/dist/runtime/core/values.d.ts +62 -153
  47. package/dist/runtime/core/values.js +308 -524
  48. package/dist/runtime/ext/builtins.js +83 -64
  49. package/dist/runtime/ext/extensions.d.ts +30 -124
  50. package/dist/runtime/ext/extensions.js +0 -93
  51. package/dist/runtime/ext/test-context.d.ts +28 -0
  52. package/dist/runtime/ext/test-context.js +154 -0
  53. package/dist/runtime/index.d.ts +22 -8
  54. package/dist/runtime/index.js +18 -4
  55. package/dist/signature-parser.d.ts +2 -2
  56. package/dist/signature-parser.js +14 -14
  57. package/package.json +1 -1
@@ -3,10 +3,30 @@
3
3
  *
4
4
  * Core value types that flow through Rill programs.
5
5
  * Public API for host applications.
6
+ *
7
+ * Dispatch functions (inferType, formatValue, deepEquals, serializeValue,
8
+ * copyValue) re-export from type-registrations.ts protocol implementations.
6
9
  */
7
10
  import { RuntimeError } from '../../types.js';
8
11
  import { VALID_TYPE_NAMES } from '../../constants.js';
9
- import { callableEquals, isCallable, isDict, isScriptCallable, } from './callable.js';
12
+ import { isCallable, isDict } from './callable.js';
13
+ import { inferType as registryInferType, formatValue as registryFormatValue, deepEquals as registryDeepEquals, serializeValue as registrySerializeValue, copyValue as registryCopyValue, } from './type-registrations.js';
14
+ /**
15
+ * Normalize a TypeStructure that may use the legacy `.type` discriminator.
16
+ * During migration, some code (builtins.ts, ext/) constructs objects with
17
+ * `{ type: 'string' }` instead of `{ kind: 'string' }`. This function
18
+ * converts legacy format to current format on the fly.
19
+ */
20
+ function normalizeStructure(ts) {
21
+ if (ts.kind !== undefined)
22
+ return ts;
23
+ // Legacy format: { type: 'string' } → { kind: 'string' }
24
+ const legacy = ts;
25
+ if (typeof legacy['type'] === 'string') {
26
+ return { ...legacy, kind: legacy['type'] };
27
+ }
28
+ return ts;
29
+ }
10
30
  /** Type guard for RillTuple (spread args) */
11
31
  export function isTuple(value) {
12
32
  return (typeof value === 'object' &&
@@ -57,53 +77,24 @@ export function createVector(data, model) {
57
77
  }
58
78
  return { __rill_vector: true, data, model };
59
79
  }
60
- /** Infer the Rill type from a runtime value */
61
- export function inferType(value) {
62
- if (value === null)
63
- return 'string'; // null treated as empty string
64
- if (typeof value === 'string')
65
- return 'string';
66
- if (typeof value === 'number')
67
- return 'number';
68
- if (typeof value === 'boolean')
69
- return 'bool';
70
- if (isTuple(value))
71
- return 'tuple';
72
- if (isOrdered(value))
73
- return 'ordered';
74
- if (isVector(value))
75
- return 'vector';
76
- if (Array.isArray(value))
77
- return 'list';
78
- if (isTypeValue(value))
79
- return 'type';
80
- if (isRillIterator(value))
81
- return 'iterator';
82
- if (typeof value === 'object' &&
83
- '__type' in value &&
84
- value.__type === 'callable') {
85
- return 'closure';
86
- }
87
- if (typeof value === 'object')
88
- return 'dict';
89
- return 'string'; // fallback
90
- }
80
+ /** Infer the Rill type from a runtime value. Delegates to type-registrations. */
81
+ export const inferType = registryInferType;
91
82
  /**
92
83
  * Infer the element type for a homogeneous list.
93
- * Empty arrays return { type: 'any' }.
84
+ * Empty arrays return { kind: 'any' }.
94
85
  * Mixed types throw RILL-R002.
95
86
  */
96
87
  export function inferElementType(elements) {
97
88
  if (elements.length === 0)
98
- return { type: 'any' };
89
+ return { kind: 'any' };
99
90
  const firstElem = elements[0];
100
- let accType = inferStructuralType(firstElem);
91
+ let accType = inferStructure(firstElem);
101
92
  for (let i = 1; i < elements.length; i++) {
102
93
  const elem = elements[i];
103
- const elemType = inferStructuralType(elem);
94
+ const elemType = inferStructure(elem);
104
95
  const merged = commonType(accType, elemType);
105
96
  if (merged === null) {
106
- throw new RuntimeError('RILL-R002', `List elements must be the same type: expected ${formatStructuralType(accType)}, got ${formatStructuralType(elemType)} at index ${i}`);
97
+ throw new RuntimeError('RILL-R002', `List elements must be the same type: expected ${formatStructure(accType)}, got ${formatStructure(elemType)} at index ${i}`);
107
98
  }
108
99
  accType = merged;
109
100
  }
@@ -113,7 +104,7 @@ export function inferElementType(elements) {
113
104
  * Merge uniform value types from two sides of the same compound type.
114
105
  * Sub-case A: both carry valueType -> recurse commonType.
115
106
  * Sub-case B: both carry structural fields -> extract value types, merge all.
116
- * Returns the merged RillType on success, undefined when no uniform merge applies.
107
+ * Returns the merged TypeStructure on success, undefined when no uniform merge applies.
117
108
  */
118
109
  function mergeUniformValueType(aValue, bValue, aFields, bFields) {
119
110
  // Sub-case A: both carry valueType
@@ -143,12 +134,12 @@ function mergeUniformValueType(aValue, bValue, aFields, bFields) {
143
134
  return undefined;
144
135
  }
145
136
  /**
146
- * Return the most specific shared type for two RillType values.
137
+ * Return the most specific shared type for two TypeStructure values.
147
138
  * Returns null when types are incompatible at the top level.
148
139
  *
149
140
  * Cascade priority:
150
141
  * 1. Any-narrowing: if either side is `any`, return the other
151
- * 2. Structural match: delegate to structuralTypeEquals; on true, return a
142
+ * 2. Structural match: delegate to structureEquals; on true, return a
152
143
  * 3. Recursive list: merge inner element types
153
144
  * 3b. Uniform valueType: merge dict/tuple/ordered value types
154
145
  * 4. Bare type fallback: same compound type but structural mismatch
@@ -156,103 +147,115 @@ function mergeUniformValueType(aValue, bValue, aFields, bFields) {
156
147
  */
157
148
  export function commonType(a, b) {
158
149
  // 1. Any-narrowing
159
- if (a.type === 'any')
150
+ if (a.kind === 'any')
160
151
  return b;
161
- if (b.type === 'any')
152
+ if (b.kind === 'any')
162
153
  return a;
163
154
  // 5. Incompatible top-level types (checked early to short-circuit)
164
- if (a.type !== b.type)
155
+ if (a.kind !== b.kind)
165
156
  return null;
166
157
  // 2. Structural match
167
- if (structuralTypeEquals(a, b))
158
+ if (structureEquals(a, b))
168
159
  return a;
169
160
  // 3. Recursive list element merging
170
- if (a.type === 'list' && b.type === 'list') {
171
- if (a.element !== undefined && b.element !== undefined) {
172
- const inner = commonType(a.element, b.element);
161
+ if (a.kind === 'list' && b.kind === 'list') {
162
+ const aList = a;
163
+ const bList = b;
164
+ if (aList.element !== undefined && bList.element !== undefined) {
165
+ const inner = commonType(aList.element, bList.element);
173
166
  if (inner !== null)
174
- return { type: 'list', element: inner };
167
+ return { kind: 'list', element: inner };
175
168
  }
176
- return { type: 'list' };
169
+ return { kind: 'list' };
177
170
  }
178
171
  // 3b. Uniform valueType merging for dict/tuple/ordered
179
- if (a.type === 'dict' && b.type === 'dict') {
180
- const merged = mergeUniformValueType(a.valueType, b.valueType, a.fields ? Object.values(a.fields) : undefined, b.fields ? Object.values(b.fields) : undefined);
172
+ if (a.kind === 'dict' && b.kind === 'dict') {
173
+ const aDict = a;
174
+ const bDict = b;
175
+ const merged = mergeUniformValueType(aDict.valueType, bDict.valueType, aDict.fields ? Object.values(aDict.fields) : undefined, bDict.fields ? Object.values(bDict.fields) : undefined);
181
176
  if (merged !== undefined)
182
- return { type: 'dict', valueType: merged };
177
+ return { kind: 'dict', valueType: merged };
183
178
  }
184
- if (a.type === 'tuple' && b.type === 'tuple') {
185
- const merged = mergeUniformValueType(a.valueType, b.valueType, a.elements, b.elements);
179
+ if (a.kind === 'tuple' && b.kind === 'tuple') {
180
+ const aTuple = a;
181
+ const bTuple = b;
182
+ const merged = mergeUniformValueType(aTuple.valueType, bTuple.valueType, aTuple.elements, bTuple.elements);
186
183
  if (merged !== undefined)
187
- return { type: 'tuple', valueType: merged };
184
+ return { kind: 'tuple', valueType: merged };
188
185
  }
189
- if (a.type === 'ordered' && b.type === 'ordered') {
190
- const merged = mergeUniformValueType(a.valueType, b.valueType, a.fields, b.fields);
186
+ if (a.kind === 'ordered' && b.kind === 'ordered') {
187
+ const aOrd = a;
188
+ const bOrd = b;
189
+ const merged = mergeUniformValueType(aOrd.valueType, bOrd.valueType, aOrd.fields, bOrd.fields);
191
190
  if (merged !== undefined)
192
- return { type: 'ordered', valueType: merged };
191
+ return { kind: 'ordered', valueType: merged };
193
192
  }
194
193
  // 4. Bare type fallback for compound types.
195
194
  // The cast is safe for closure/dict/tuple/ordered (all sub-fields optional).
196
- // For union, members is required by RillType but omitted here intentionally:
195
+ // For union, members is required by TypeStructure but omitted here intentionally:
197
196
  // bare union signals structural incompatibility without enumerating members.
198
- if (a.type === 'closure' ||
199
- a.type === 'dict' ||
200
- a.type === 'tuple' ||
201
- a.type === 'ordered' ||
202
- a.type === 'union') {
203
- return { type: a.type };
197
+ if (a.kind === 'closure' ||
198
+ a.kind === 'dict' ||
199
+ a.kind === 'tuple' ||
200
+ a.kind === 'ordered' ||
201
+ a.kind === 'union') {
202
+ return { kind: a.kind };
204
203
  }
205
204
  return null;
206
205
  }
207
206
  /** Compare two structural types for equality. */
208
- export function structuralTypeEquals(a, b) {
209
- if (a.type !== b.type)
207
+ export function structureEquals(a, b) {
208
+ if (a.kind !== b.kind)
210
209
  return false;
211
- // Leaf variants compare by type alone
212
- if (a.type === 'number' ||
213
- a.type === 'string' ||
214
- a.type === 'bool' ||
215
- a.type === 'vector' ||
216
- a.type === 'type' ||
217
- a.type === 'any') {
210
+ // Leaf variants compare by kind alone
211
+ if (a.kind === 'number' ||
212
+ a.kind === 'string' ||
213
+ a.kind === 'bool' ||
214
+ a.kind === 'vector' ||
215
+ a.kind === 'type' ||
216
+ a.kind === 'any') {
218
217
  return true;
219
218
  }
220
- if (a.type === 'list' && b.type === 'list') {
221
- if (a.element === undefined && b.element === undefined)
219
+ if (a.kind === 'list' && b.kind === 'list') {
220
+ const aList = a;
221
+ const bList = b;
222
+ if (aList.element === undefined && bList.element === undefined)
222
223
  return true;
223
- if (a.element === undefined || b.element === undefined)
224
+ if (aList.element === undefined || bList.element === undefined)
224
225
  return false;
225
- return structuralTypeEquals(a.element, b.element);
226
- }
227
- if (a.type === 'dict' && b.type === 'dict') {
228
- // Uniform valueType comparison (mirrors list element at line 308)
229
- const aHasValue = a.valueType !== undefined;
230
- const bHasValue = b.valueType !== undefined;
226
+ return structureEquals(aList.element, bList.element);
227
+ }
228
+ if (a.kind === 'dict' && b.kind === 'dict') {
229
+ const aDict = a;
230
+ const bDict = b;
231
+ // Uniform valueType comparison
232
+ const aHasValue = aDict.valueType !== undefined;
233
+ const bHasValue = bDict.valueType !== undefined;
231
234
  if (aHasValue || bHasValue) {
232
235
  if (!aHasValue || !bHasValue)
233
236
  return false;
234
- return structuralTypeEquals(a.valueType, b.valueType);
237
+ return structureEquals(aDict.valueType, bDict.valueType);
235
238
  }
236
239
  // Structural fields comparison
237
- if (a.fields === undefined && b.fields === undefined)
240
+ if (aDict.fields === undefined && bDict.fields === undefined)
238
241
  return true;
239
- if (a.fields === undefined || b.fields === undefined)
242
+ if (aDict.fields === undefined || bDict.fields === undefined)
240
243
  return false;
241
- const aKeys = Object.keys(a.fields).sort();
242
- const bKeys = Object.keys(b.fields).sort();
244
+ const aKeys = Object.keys(aDict.fields).sort();
245
+ const bKeys = Object.keys(bDict.fields).sort();
243
246
  if (aKeys.length !== bKeys.length)
244
247
  return false;
245
248
  for (let i = 0; i < aKeys.length; i++) {
246
249
  const key = aKeys[i];
247
250
  if (key !== bKeys[i])
248
251
  return false;
249
- const aField = a.fields[key];
250
- const bField = b.fields[key];
252
+ const aField = aDict.fields[key];
253
+ const bField = bDict.fields[key];
251
254
  const aHasDefault = aField.defaultValue !== undefined;
252
255
  const bHasDefault = bField.defaultValue !== undefined;
253
256
  if (aHasDefault !== bHasDefault)
254
257
  return false;
255
- if (!structuralTypeEquals(aField.type, bField.type))
258
+ if (!structureEquals(aField.type, bField.type))
256
259
  return false;
257
260
  if (aHasDefault && bHasDefault) {
258
261
  if (!deepEquals(aField.defaultValue, bField.defaultValue))
@@ -261,26 +264,28 @@ export function structuralTypeEquals(a, b) {
261
264
  }
262
265
  return true;
263
266
  }
264
- if (a.type === 'tuple' && b.type === 'tuple') {
265
- // Uniform valueType comparison (mirrors list element at line 308)
266
- const aHasValue = a.valueType !== undefined;
267
- const bHasValue = b.valueType !== undefined;
267
+ if (a.kind === 'tuple' && b.kind === 'tuple') {
268
+ const aTuple = a;
269
+ const bTuple = b;
270
+ // Uniform valueType comparison
271
+ const aHasValue = aTuple.valueType !== undefined;
272
+ const bHasValue = bTuple.valueType !== undefined;
268
273
  if (aHasValue || bHasValue) {
269
274
  if (!aHasValue || !bHasValue)
270
275
  return false;
271
- return structuralTypeEquals(a.valueType, b.valueType);
276
+ return structureEquals(aTuple.valueType, bTuple.valueType);
272
277
  }
273
278
  // Structural elements comparison
274
- if (a.elements === undefined && b.elements === undefined)
279
+ if (aTuple.elements === undefined && bTuple.elements === undefined)
275
280
  return true;
276
- if (a.elements === undefined || b.elements === undefined)
281
+ if (aTuple.elements === undefined || bTuple.elements === undefined)
277
282
  return false;
278
- if (a.elements.length !== b.elements.length)
283
+ if (aTuple.elements.length !== bTuple.elements.length)
279
284
  return false;
280
- for (let i = 0; i < a.elements.length; i++) {
281
- const aElem = a.elements[i];
282
- const bElem = b.elements[i];
283
- if (!structuralTypeEquals(aElem.type, bElem.type))
285
+ for (let i = 0; i < aTuple.elements.length; i++) {
286
+ const aElem = aTuple.elements[i];
287
+ const bElem = bTuple.elements[i];
288
+ if (!structureEquals(aElem.type, bElem.type))
284
289
  return false;
285
290
  const aDefault = aElem.defaultValue;
286
291
  const bDefault = bElem.defaultValue;
@@ -293,28 +298,30 @@ export function structuralTypeEquals(a, b) {
293
298
  }
294
299
  return true;
295
300
  }
296
- if (a.type === 'ordered' && b.type === 'ordered') {
297
- // Uniform valueType comparison (mirrors list element at line 308)
298
- const aHasValue = a.valueType !== undefined;
299
- const bHasValue = b.valueType !== undefined;
301
+ if (a.kind === 'ordered' && b.kind === 'ordered') {
302
+ const aOrd = a;
303
+ const bOrd = b;
304
+ // Uniform valueType comparison
305
+ const aHasValue = aOrd.valueType !== undefined;
306
+ const bHasValue = bOrd.valueType !== undefined;
300
307
  if (aHasValue || bHasValue) {
301
308
  if (!aHasValue || !bHasValue)
302
309
  return false;
303
- return structuralTypeEquals(a.valueType, b.valueType);
310
+ return structureEquals(aOrd.valueType, bOrd.valueType);
304
311
  }
305
312
  // Structural fields comparison
306
- if (a.fields === undefined && b.fields === undefined)
313
+ if (aOrd.fields === undefined && bOrd.fields === undefined)
307
314
  return true;
308
- if (a.fields === undefined || b.fields === undefined)
315
+ if (aOrd.fields === undefined || bOrd.fields === undefined)
309
316
  return false;
310
- if (a.fields.length !== b.fields.length)
317
+ if (aOrd.fields.length !== bOrd.fields.length)
311
318
  return false;
312
- for (let i = 0; i < a.fields.length; i++) {
313
- const aField = a.fields[i];
314
- const bField = b.fields[i];
319
+ for (let i = 0; i < aOrd.fields.length; i++) {
320
+ const aField = aOrd.fields[i];
321
+ const bField = bOrd.fields[i];
315
322
  if (aField.name !== bField.name)
316
323
  return false;
317
- if (!structuralTypeEquals(aField.type, bField.type))
324
+ if (!structureEquals(aField.type, bField.type))
318
325
  return false;
319
326
  const aDefault = aField.defaultValue;
320
327
  const bDefault = bField.defaultValue;
@@ -327,31 +334,35 @@ export function structuralTypeEquals(a, b) {
327
334
  }
328
335
  return true;
329
336
  }
330
- if (a.type === 'union' && b.type === 'union') {
331
- if (a.members.length !== b.members.length)
337
+ if (a.kind === 'union' && b.kind === 'union') {
338
+ const aUnion = a;
339
+ const bUnion = b;
340
+ if (aUnion.members.length !== bUnion.members.length)
332
341
  return false;
333
- for (let i = 0; i < a.members.length; i++) {
334
- if (!structuralTypeEquals(a.members[i], b.members[i]))
342
+ for (let i = 0; i < aUnion.members.length; i++) {
343
+ if (!structureEquals(aUnion.members[i], bUnion.members[i]))
335
344
  return false;
336
345
  }
337
346
  return true;
338
347
  }
339
- if (a.type === 'closure' && b.type === 'closure') {
340
- if (a.params === undefined && b.params === undefined) {
348
+ if (a.kind === 'closure' && b.kind === 'closure') {
349
+ const aCls = a;
350
+ const bCls = b;
351
+ if (aCls.params === undefined && bCls.params === undefined) {
341
352
  // Both absent: compare ret
342
353
  }
343
- else if (a.params === undefined || b.params === undefined) {
354
+ else if (aCls.params === undefined || bCls.params === undefined) {
344
355
  return false;
345
356
  }
346
357
  else {
347
- if (a.params.length !== b.params.length)
358
+ if (aCls.params.length !== bCls.params.length)
348
359
  return false;
349
- for (let i = 0; i < a.params.length; i++) {
350
- const aParam = a.params[i];
351
- const bParam = b.params[i];
360
+ for (let i = 0; i < aCls.params.length; i++) {
361
+ const aParam = aCls.params[i];
362
+ const bParam = bCls.params[i];
352
363
  if (aParam.name !== bParam.name)
353
364
  return false;
354
- if (!structuralTypeEquals(aParam.type, bParam.type))
365
+ if (!structureEquals(aParam.type, bParam.type))
355
366
  return false;
356
367
  const aDefault = aParam.defaultValue;
357
368
  const bDefault = bParam.defaultValue;
@@ -363,213 +374,213 @@ export function structuralTypeEquals(a, b) {
363
374
  return false;
364
375
  }
365
376
  }
366
- if (a.ret === undefined && b.ret === undefined)
377
+ if (aCls.ret === undefined && bCls.ret === undefined)
367
378
  return true;
368
- if (a.ret === undefined || b.ret === undefined)
379
+ if (aCls.ret === undefined || bCls.ret === undefined)
369
380
  return false;
370
- return structuralTypeEquals(a.ret, b.ret);
381
+ return structureEquals(aCls.ret, bCls.ret);
371
382
  }
372
383
  return false;
373
384
  }
385
+ /** @deprecated Use structureEquals instead. */
386
+ export const structuralTypeEquals = structureEquals;
374
387
  /** Infer the structural type descriptor for any Rill value. */
375
- export function inferStructuralType(value) {
388
+ export function inferStructure(value) {
376
389
  if (value === null || typeof value === 'string') {
377
- return { type: 'string' };
390
+ return { kind: 'string' };
378
391
  }
379
392
  if (typeof value === 'number') {
380
- return { type: 'number' };
393
+ return { kind: 'number' };
381
394
  }
382
395
  if (typeof value === 'boolean') {
383
- return { type: 'bool' };
396
+ return { kind: 'bool' };
384
397
  }
385
398
  if (isTypeValue(value)) {
386
- return { type: 'type' };
399
+ return { kind: 'type' };
387
400
  }
388
401
  if (Array.isArray(value)) {
389
- return { type: 'list', element: inferElementType(value) };
402
+ return { kind: 'list', element: inferElementType(value) };
390
403
  }
391
404
  if (isTuple(value)) {
392
405
  return {
393
- type: 'tuple',
406
+ kind: 'tuple',
394
407
  elements: value.entries.map((e) => ({
395
- type: inferStructuralType(e),
408
+ type: inferStructure(e),
396
409
  })),
397
410
  };
398
411
  }
399
412
  if (isOrdered(value)) {
400
413
  return {
401
- type: 'ordered',
414
+ kind: 'ordered',
402
415
  fields: value.entries.map(([k, v]) => ({
403
416
  name: k,
404
- type: inferStructuralType(v),
417
+ type: inferStructure(v),
405
418
  })),
406
419
  };
407
420
  }
408
421
  if (isVector(value)) {
409
- return { type: 'vector' };
422
+ return { kind: 'vector' };
410
423
  }
411
424
  if (isCallable(value)) {
412
- const params = (value.params ?? []).map((p) => paramToFieldDef(p.name, p.type ?? { type: 'any' }, p.defaultValue));
425
+ const params = (value.params ?? []).map((p) => paramToFieldDef(p.name, p.type ?? { kind: 'any' }, p.defaultValue));
413
426
  const ret = value.returnType.structure;
414
- return { type: 'closure', params, ret };
427
+ return { kind: 'closure', params, ret };
415
428
  }
416
429
  if (typeof value === 'object') {
417
430
  const dict = value;
418
431
  const fields = {};
419
432
  for (const [k, v] of Object.entries(dict)) {
420
- fields[k] = { type: inferStructuralType(v) };
433
+ fields[k] = { type: inferStructure(v) };
421
434
  }
422
- return { type: 'dict', fields };
435
+ return { kind: 'dict', fields };
423
436
  }
424
437
  throw new RuntimeError('RILL-R004', `Cannot infer structural type for ${formatValue(value)}`);
425
438
  }
439
+ /** @deprecated Use inferStructure instead. */
440
+ export const inferStructuralType = inferStructure;
426
441
  /**
427
442
  * Check if a value matches a structural type descriptor.
428
443
  * Used for runtime type checking (`:?` operator).
429
444
  */
430
- export function structuralTypeMatches(value, type) {
445
+ export function structureMatches(value, type) {
446
+ type = normalizeStructure(type);
431
447
  if (typeof value === 'undefined') {
432
448
  throw new RuntimeError('RILL-R004', 'Cannot type-check non-value');
433
449
  }
434
- if (type.type === 'any')
450
+ if (type.kind === 'any')
435
451
  return true;
436
452
  // Leaf primitive variants: match by inferred type name
437
- if (type.type === 'number' ||
438
- type.type === 'string' ||
439
- type.type === 'bool' ||
440
- type.type === 'vector' ||
441
- type.type === 'type') {
442
- return inferType(value) === type.type;
443
- }
444
- if (type.type === 'list') {
453
+ if (type.kind === 'number' ||
454
+ type.kind === 'string' ||
455
+ type.kind === 'bool' ||
456
+ type.kind === 'vector' ||
457
+ type.kind === 'type') {
458
+ return inferType(value) === type.kind;
459
+ }
460
+ if (type.kind === 'list') {
461
+ const t = type;
445
462
  if (!Array.isArray(value))
446
463
  return false;
447
- // Absent element sub-field: matches any list value
448
- if (type.element === undefined)
464
+ if (t.element === undefined)
449
465
  return true;
450
- if (type.element.type === 'any')
466
+ if (t.element.kind === 'any')
451
467
  return true;
452
- return value.every((elem) => structuralTypeMatches(elem, type.element));
468
+ return value.every((elem) => structureMatches(elem, t.element));
453
469
  }
454
- if (type.type === 'dict') {
470
+ if (type.kind === 'dict') {
471
+ const t = type;
455
472
  if (!isDict(value))
456
473
  return false;
457
- // Uniform value type check: every value must match valueType
458
- if (type.valueType !== undefined) {
474
+ if (t.valueType !== undefined) {
459
475
  const vals = Object.values(value);
460
- return vals.every((v) => structuralTypeMatches(v, type.valueType));
476
+ return vals.every((v) => structureMatches(v, t.valueType));
461
477
  }
462
- // Absent fields sub-field: matches any dict value
463
- if (type.fields === undefined)
478
+ if (t.fields === undefined)
464
479
  return true;
465
- const dictKeys = Object.keys(type.fields);
466
- // Empty fields object matches any dict
480
+ const dictKeys = Object.keys(t.fields);
467
481
  if (dictKeys.length === 0)
468
482
  return true;
469
483
  const dict = value;
470
484
  for (const key of dictKeys) {
471
485
  if (!(key in dict)) {
472
- const field = type.fields[key];
486
+ const field = t.fields[key];
473
487
  if (field.defaultValue !== undefined)
474
488
  continue;
475
489
  return false;
476
490
  }
477
- const field = type.fields[key];
478
- if (!structuralTypeMatches(dict[key], field.type))
491
+ const field = t.fields[key];
492
+ if (!structureMatches(dict[key], field.type))
479
493
  return false;
480
494
  }
481
495
  return true;
482
496
  }
483
- if (type.type === 'tuple') {
497
+ if (type.kind === 'tuple') {
498
+ const t = type;
484
499
  if (!isTuple(value))
485
500
  return false;
486
- // Uniform value type check: every entry must match valueType
487
- if (type.valueType !== undefined) {
488
- return value.entries.every((v) => structuralTypeMatches(v, type.valueType));
501
+ if (t.valueType !== undefined) {
502
+ return value.entries.every((v) => structureMatches(v, t.valueType));
489
503
  }
490
- // Absent elements sub-field: matches any tuple value
491
- if (type.elements === undefined)
504
+ if (t.elements === undefined)
492
505
  return true;
493
- if (type.elements.length === 0)
506
+ if (t.elements.length === 0)
494
507
  return value.entries.length === 0;
495
- // Reject if value has more entries than type elements
496
- if (value.entries.length > type.elements.length)
508
+ if (value.entries.length > t.elements.length)
497
509
  return false;
498
- // Reject if value is shorter and any trailing missing element lacks a default
499
- if (value.entries.length < type.elements.length) {
500
- for (let i = value.entries.length; i < type.elements.length; i++) {
501
- const field = type.elements[i];
510
+ if (value.entries.length < t.elements.length) {
511
+ for (let i = value.entries.length; i < t.elements.length; i++) {
512
+ const field = t.elements[i];
502
513
  if (field.defaultValue === undefined)
503
514
  return false;
504
515
  }
505
516
  }
506
517
  for (let i = 0; i < value.entries.length; i++) {
507
- if (!structuralTypeMatches(value.entries[i], type.elements[i].type))
518
+ if (!structureMatches(value.entries[i], t.elements[i].type))
508
519
  return false;
509
520
  }
510
521
  return true;
511
522
  }
512
- if (type.type === 'ordered') {
523
+ if (type.kind === 'ordered') {
524
+ const t = type;
513
525
  if (!isOrdered(value))
514
526
  return false;
515
- // Uniform value type check: every entry value must match valueType
516
- if (type.valueType !== undefined) {
517
- return value.entries.every(([, v]) => structuralTypeMatches(v, type.valueType));
527
+ if (t.valueType !== undefined) {
528
+ return value.entries.every(([, v]) => structureMatches(v, t.valueType));
518
529
  }
519
- // Absent fields sub-field: matches any ordered value
520
- if (type.fields === undefined)
530
+ if (t.fields === undefined)
521
531
  return true;
522
- if (type.fields.length === 0)
532
+ if (t.fields.length === 0)
523
533
  return value.entries.length === 0;
524
- // Reject if value has more entries than type fields
525
- if (value.entries.length > type.fields.length)
534
+ if (value.entries.length > t.fields.length)
526
535
  return false;
527
- // Reject if value is shorter and any trailing missing field lacks a default
528
- if (value.entries.length < type.fields.length) {
529
- for (let i = value.entries.length; i < type.fields.length; i++) {
530
- const field = type.fields[i];
536
+ if (value.entries.length < t.fields.length) {
537
+ for (let i = value.entries.length; i < t.fields.length; i++) {
538
+ const field = t.fields[i];
531
539
  if (field.defaultValue === undefined)
532
540
  return false;
533
541
  }
534
542
  }
535
543
  for (let i = 0; i < value.entries.length; i++) {
536
- const field = type.fields[i];
544
+ const field = t.fields[i];
537
545
  const [actualName, actualValue] = value.entries[i];
538
546
  if (actualName !== field.name)
539
547
  return false;
540
- if (!structuralTypeMatches(actualValue, field.type))
548
+ if (!structureMatches(actualValue, field.type))
541
549
  return false;
542
550
  }
543
551
  return true;
544
552
  }
545
- if (type.type === 'closure') {
553
+ if (type.kind === 'closure') {
554
+ const t = type;
546
555
  if (!isCallable(value))
547
556
  return false;
548
- // Absent params sub-field: matches any closure value of that compound type
549
- if (type.params === undefined)
557
+ if (t.params === undefined)
550
558
  return true;
551
559
  const valueParams = value.params ?? [];
552
- if (valueParams.length !== type.params.length)
560
+ if (valueParams.length !== t.params.length)
553
561
  return false;
554
- for (let i = 0; i < type.params.length; i++) {
555
- const field = type.params[i];
562
+ for (let i = 0; i < t.params.length; i++) {
563
+ const field = t.params[i];
556
564
  const param = valueParams[i];
557
565
  if (param.name !== field.name)
558
566
  return false;
559
- const paramType = param.type ?? { type: 'any' };
560
- if (!structuralTypeEquals(paramType, field.type))
567
+ const paramType = param.type ?? { kind: 'any' };
568
+ if (!structureEquals(paramType, field.type))
561
569
  return false;
562
570
  }
563
571
  const retType = value.returnType.structure;
564
- if (type.ret === undefined)
572
+ if (t.ret === undefined)
565
573
  return true;
566
- return structuralTypeEquals(retType, type.ret);
574
+ return structureEquals(retType, t.ret);
567
575
  }
568
- if (type.type === 'union') {
569
- return type.members.some((member) => structuralTypeMatches(value, member));
576
+ if (type.kind === 'union') {
577
+ const t = type;
578
+ return t.members.some((member) => structureMatches(value, member));
570
579
  }
571
580
  return false;
572
581
  }
582
+ /** @deprecated Use structureMatches instead. */
583
+ export const structuralTypeMatches = structureMatches;
573
584
  /** Build a closure param field definition from name, type, and optional default. */
574
585
  export function paramToFieldDef(name, type, defaultValue) {
575
586
  const field = { name, type };
@@ -592,84 +603,92 @@ function formatRillLiteral(value) {
592
603
  return formatValue(value);
593
604
  }
594
605
  /** Format a structural type descriptor as a human-readable string. */
595
- export function formatStructuralType(type) {
596
- if (type.type === 'any' ||
597
- type.type === 'number' ||
598
- type.type === 'string' ||
599
- type.type === 'bool' ||
600
- type.type === 'vector' ||
601
- type.type === 'type') {
602
- return type.type;
603
- }
604
- if (type.type === 'list') {
605
- if (type.element === undefined)
606
+ export function formatStructure(type) {
607
+ if (type.kind === 'any' ||
608
+ type.kind === 'number' ||
609
+ type.kind === 'string' ||
610
+ type.kind === 'bool' ||
611
+ type.kind === 'vector' ||
612
+ type.kind === 'type') {
613
+ return type.kind;
614
+ }
615
+ if (type.kind === 'list') {
616
+ const t = type;
617
+ if (t.element === undefined)
606
618
  return 'list';
607
- return `list(${formatStructuralType(type.element)})`;
619
+ return `list(${formatStructure(t.element)})`;
608
620
  }
609
- if (type.type === 'dict') {
610
- if (type.valueType !== undefined && type.fields === undefined) {
611
- return `dict(${formatStructuralType(type.valueType)})`;
621
+ if (type.kind === 'dict') {
622
+ const t = type;
623
+ if (t.valueType !== undefined && t.fields === undefined) {
624
+ return `dict(${formatStructure(t.valueType)})`;
612
625
  }
613
- if (type.fields === undefined)
626
+ if (t.fields === undefined)
614
627
  return 'dict';
615
- const parts = Object.keys(type.fields)
628
+ const parts = Object.keys(t.fields)
616
629
  .sort()
617
630
  .map((k) => {
618
- const field = type.fields[k];
619
- const base = `${k}: ${formatStructuralType(field.type)}`;
631
+ const field = t.fields[k];
632
+ const base = `${k}: ${formatStructure(field.type)}`;
620
633
  if (field.defaultValue === undefined)
621
634
  return base;
622
635
  return `${base} = ${formatRillLiteral(field.defaultValue)}`;
623
636
  });
624
637
  return `dict(${parts.join(', ')})`;
625
638
  }
626
- if (type.type === 'tuple') {
627
- if (type.valueType !== undefined && type.elements === undefined) {
628
- return `tuple(${formatStructuralType(type.valueType)})`;
639
+ if (type.kind === 'tuple') {
640
+ const t = type;
641
+ if (t.valueType !== undefined && t.elements === undefined) {
642
+ return `tuple(${formatStructure(t.valueType)})`;
629
643
  }
630
- if (type.elements === undefined)
644
+ if (t.elements === undefined)
631
645
  return 'tuple';
632
- const parts = type.elements.map((field) => {
633
- const base = formatStructuralType(field.type);
646
+ const parts = t.elements.map((field) => {
647
+ const base = formatStructure(field.type);
634
648
  if (field.defaultValue === undefined)
635
649
  return base;
636
650
  return `${base} = ${formatRillLiteral(field.defaultValue)}`;
637
651
  });
638
652
  return `tuple(${parts.join(', ')})`;
639
653
  }
640
- if (type.type === 'ordered') {
641
- if (type.valueType !== undefined && type.fields === undefined) {
642
- return `ordered(${formatStructuralType(type.valueType)})`;
654
+ if (type.kind === 'ordered') {
655
+ const t = type;
656
+ if (t.valueType !== undefined && t.fields === undefined) {
657
+ return `ordered(${formatStructure(t.valueType)})`;
643
658
  }
644
- if (type.fields === undefined)
659
+ if (t.fields === undefined)
645
660
  return 'ordered';
646
- const parts = type.fields.map((field) => {
647
- const base = `${field.name}: ${formatStructuralType(field.type)}`;
661
+ const parts = t.fields.map((field) => {
662
+ const base = `${field.name}: ${formatStructure(field.type)}`;
648
663
  if (field.defaultValue === undefined)
649
664
  return base;
650
665
  return `${base} = ${formatRillLiteral(field.defaultValue)}`;
651
666
  });
652
667
  return `ordered(${parts.join(', ')})`;
653
668
  }
654
- if (type.type === 'closure') {
655
- if (type.params === undefined)
669
+ if (type.kind === 'closure') {
670
+ const t = type;
671
+ if (t.params === undefined)
656
672
  return 'closure';
657
- const params = type.params
673
+ const params = t.params
658
674
  .map((field) => {
659
- const base = `${field.name}: ${formatStructuralType(field.type)}`;
675
+ const base = `${field.name}: ${formatStructure(field.type)}`;
660
676
  if (field.defaultValue === undefined)
661
677
  return base;
662
678
  return `${base} = ${formatRillLiteral(field.defaultValue)}`;
663
679
  })
664
680
  .join(', ');
665
- const ret = type.ret !== undefined ? formatStructuralType(type.ret) : 'any';
681
+ const ret = t.ret !== undefined ? formatStructure(t.ret) : 'any';
666
682
  return `|${params}| :${ret}`;
667
683
  }
668
- if (type.type === 'union') {
669
- return type.members.map(formatStructuralType).join('|');
684
+ if (type.kind === 'union') {
685
+ const t = type;
686
+ return t.members.map(formatStructure).join('|');
670
687
  }
671
688
  return 'any';
672
689
  }
690
+ /** @deprecated Use formatStructure instead. */
691
+ export const formatStructuralType = formatStructure;
673
692
  /**
674
693
  * Check if a value is of the expected type.
675
694
  * Returns true if the value matches the expected type, false otherwise.
@@ -706,88 +725,12 @@ export function isTruthy(value) {
706
725
  export function isEmpty(value) {
707
726
  return !isTruthy(value);
708
727
  }
709
- /** Format a value for display */
710
- export function formatValue(value) {
711
- if (value === null)
712
- return 'type(null)';
713
- if (typeof value === 'string')
714
- return value;
715
- if (typeof value === 'number')
716
- return String(value);
717
- if (typeof value === 'boolean')
718
- return value ? 'true' : 'false';
719
- if (isCallable(value)) {
720
- return 'type(closure)';
721
- }
722
- if (isTuple(value)) {
723
- return `tuple[${value.entries.map(formatValue).join(', ')}]`;
724
- }
725
- if (isOrdered(value)) {
726
- const parts = value.entries.map(([k, v]) => `${k}: ${formatValue(v)}`);
727
- return `ordered[${parts.join(', ')}]`;
728
- }
729
- if (isRillIterator(value)) {
730
- return 'type(iterator)';
731
- }
732
- if (Array.isArray(value)) {
733
- return `list[${value.map(formatValue).join(', ')}]`;
734
- }
735
- if (isVector(value)) {
736
- return `vector(${value.model}, ${value.data.length}d)`;
737
- }
738
- if (isTypeValue(value)) {
739
- return formatStructuralType(value.structure);
740
- }
741
- // Plain dict
742
- if (typeof value === 'object') {
743
- const dict = value;
744
- const parts = Object.entries(dict).map(([k, v]) => `${k}: ${formatValue(v)}`);
745
- return `dict[${parts.join(', ')}]`;
746
- }
747
- return String(value);
748
- }
749
- /**
750
- * Convert a RillValue to a JSON-serializable value.
751
- * @throws {Error} plain Error (not RuntimeError) for non-serializable types
752
- */
753
- export function valueToJSON(value) {
754
- if (value === null)
755
- return null;
756
- if (typeof value === 'string')
757
- return value;
758
- if (typeof value === 'number')
759
- return value;
760
- if (typeof value === 'boolean')
761
- return value;
762
- if (Array.isArray(value)) {
763
- return value.map(valueToJSON);
764
- }
765
- if (isCallable(value)) {
766
- throw new Error('closures are not JSON-serializable');
767
- }
768
- if (isTuple(value)) {
769
- throw new Error('tuples are not JSON-serializable');
770
- }
771
- if (isOrdered(value)) {
772
- throw new Error('ordered values are not JSON-serializable');
773
- }
774
- if (isVector(value)) {
775
- throw new Error('vectors are not JSON-serializable');
776
- }
777
- if (isTypeValue(value)) {
778
- throw new Error('type values are not JSON-serializable');
779
- }
780
- if (isRillIterator(value)) {
781
- throw new Error('iterators are not JSON-serializable');
782
- }
783
- // Plain dict
784
- const dict = value;
785
- const result = {};
786
- for (const [k, v] of Object.entries(dict)) {
787
- result[k] = valueToJSON(v);
788
- }
789
- return result;
790
- }
728
+ /** Format a value for display. Delegates to type-registrations. */
729
+ export const formatValue = registryFormatValue;
730
+ /** Serialize a Rill value for JSON transport. Delegates to type-registrations. */
731
+ export const serializeValue = registrySerializeValue;
732
+ /** @deprecated Use serializeValue instead. */
733
+ export const valueToJSON = serializeValue;
791
734
  /**
792
735
  * Convert a RillValue to a NativeResult for host consumption.
793
736
  * Non-representable types (closures, vectors, type values, iterators) produce descriptor objects.
@@ -795,7 +738,7 @@ export function valueToJSON(value) {
795
738
  */
796
739
  export function toNative(value) {
797
740
  const rillTypeName = inferType(value);
798
- const rillTypeSignature = formatStructuralType(inferStructuralType(value));
741
+ const rillTypeSignature = formatStructure(inferStructure(value));
799
742
  const nativeValue = toNativeValue(value);
800
743
  return { rillTypeName, rillTypeSignature, value: nativeValue };
801
744
  }
@@ -812,7 +755,7 @@ function toNativeValue(value) {
812
755
  return value.map(toNativeValue);
813
756
  }
814
757
  if (isCallable(value)) {
815
- return { signature: formatStructuralType(inferStructuralType(value)) };
758
+ return { signature: formatStructure(inferStructure(value)) };
816
759
  }
817
760
  if (isTuple(value)) {
818
761
  return value.entries.map(toNativeValue);
@@ -830,10 +773,10 @@ function toNativeValue(value) {
830
773
  if (isTypeValue(value)) {
831
774
  return {
832
775
  name: value.typeName,
833
- signature: formatStructuralType(value.structure),
776
+ signature: formatStructure(value.structure),
834
777
  };
835
778
  }
836
- if (isRillIterator(value)) {
779
+ if (isIterator(value)) {
837
780
  return { done: value.done };
838
781
  }
839
782
  // Plain dict
@@ -844,152 +787,8 @@ function toNativeValue(value) {
844
787
  }
845
788
  return result;
846
789
  }
847
- /**
848
- * Deep structural equality for all Rill values.
849
- * - Primitives: value equality
850
- * - Tuples: length + recursive element equality
851
- * - Dicts: same keys + recursive value equality (order-independent)
852
- */
853
- export function deepEquals(a, b) {
854
- // Handle primitives and null
855
- if (a === b)
856
- return true;
857
- if (a === null || b === null)
858
- return false;
859
- if (typeof a !== typeof b)
860
- return false;
861
- // Primitives (string, number, boolean) - covered by === above
862
- if (typeof a !== 'object' || typeof b !== 'object')
863
- return false;
864
- // Both are non-null objects at this point
865
- const aObj = a;
866
- const bObj = b;
867
- // Check for tuples (positional spread args)
868
- const aIsTuple = isTuple(a);
869
- const bIsTuple = isTuple(b);
870
- if (aIsTuple !== bIsTuple)
871
- return false;
872
- if (aIsTuple && bIsTuple) {
873
- if (a.entries.length !== b.entries.length)
874
- return false;
875
- for (let i = 0; i < a.entries.length; i++) {
876
- const aVal = a.entries[i];
877
- const bVal = b.entries[i];
878
- if (aVal === undefined || bVal === undefined) {
879
- if (aVal !== bVal)
880
- return false;
881
- }
882
- else if (!deepEquals(aVal, bVal)) {
883
- return false;
884
- }
885
- }
886
- return true;
887
- }
888
- // Check for ordered (named spread args)
889
- const aIsOrdered = isOrdered(a);
890
- const bIsOrdered = isOrdered(b);
891
- if (aIsOrdered !== bIsOrdered)
892
- return false;
893
- if (aIsOrdered && bIsOrdered) {
894
- if (a.entries.length !== b.entries.length)
895
- return false;
896
- for (let i = 0; i < a.entries.length; i++) {
897
- const aEntry = a.entries[i];
898
- const bEntry = b.entries[i];
899
- if (aEntry === undefined || bEntry === undefined)
900
- return false;
901
- if (aEntry[0] !== bEntry[0])
902
- return false;
903
- if (!deepEquals(aEntry[1], bEntry[1]))
904
- return false;
905
- }
906
- return true;
907
- }
908
- // Check for vectors
909
- const aIsVector = isVector(a);
910
- const bIsVector = isVector(b);
911
- if (aIsVector !== bIsVector)
912
- return false;
913
- if (aIsVector && bIsVector) {
914
- // Vectors equal when model matches AND all float elements match
915
- if (a.model !== b.model)
916
- return false;
917
- if (a.data.length !== b.data.length)
918
- return false;
919
- for (let i = 0; i < a.data.length; i++) {
920
- const aVal = a.data[i];
921
- const bVal = b.data[i];
922
- if (aVal !== bVal)
923
- return false;
924
- }
925
- return true;
926
- }
927
- // Check for type values (first-class type names)
928
- const aIsTypeValue = isTypeValue(a);
929
- const bIsTypeValue = isTypeValue(b);
930
- if (aIsTypeValue !== bIsTypeValue)
931
- return false;
932
- if (aIsTypeValue && bIsTypeValue) {
933
- return structuralTypeEquals(a.structure, b.structure);
934
- }
935
- // Check for arrays (lists)
936
- const aIsArray = Array.isArray(a);
937
- const bIsArray = Array.isArray(b);
938
- if (aIsArray !== bIsArray)
939
- return false;
940
- if (aIsArray && bIsArray) {
941
- if (a.length !== b.length)
942
- return false;
943
- for (let i = 0; i < a.length; i++) {
944
- const aElem = a[i];
945
- const bElem = b[i];
946
- if (aElem === undefined || bElem === undefined) {
947
- if (aElem !== bElem)
948
- return false;
949
- }
950
- else if (!deepEquals(aElem, bElem)) {
951
- return false;
952
- }
953
- }
954
- return true;
955
- }
956
- // Both are dicts (plain objects) or callables
957
- // For script callables, use structural equality (params + body AST + captured values)
958
- // For runtime/application callables, use reference equality
959
- if ('__type' in aObj || '__type' in bObj) {
960
- // Both must be callables to be equal
961
- if (!('__type' in aObj) || !('__type' in bObj))
962
- return false;
963
- if (aObj.__type !== 'callable' || bObj.__type !== 'callable')
964
- return false;
965
- // Script callables: structural equality
966
- if (isScriptCallable(a) && isScriptCallable(b)) {
967
- return callableEquals(a, b, deepEquals);
968
- }
969
- // Runtime/application callables: reference equality
970
- return a === b;
971
- }
972
- const aDict = a;
973
- const bDict = b;
974
- const aKeys = Object.keys(aDict);
975
- const bKeys = Object.keys(bDict);
976
- if (aKeys.length !== bKeys.length)
977
- return false;
978
- for (const key of aKeys) {
979
- if (!(key in bDict))
980
- return false;
981
- const aVal = aDict[key];
982
- const bVal = bDict[key];
983
- if (aVal === undefined || bVal === undefined) {
984
- if (aVal !== bVal)
985
- return false;
986
- }
987
- else if (!deepEquals(aVal, bVal)) {
988
- return false;
989
- }
990
- }
991
- return true;
992
- }
790
+ /** Deep structural equality for all Rill values. Delegates to type-registrations. */
791
+ export const deepEquals = registryDeepEquals;
993
792
  /** Reserved dict method names that cannot be overridden */
994
793
  export const RESERVED_DICT_METHODS = ['keys', 'values', 'entries'];
995
794
  /**
@@ -999,43 +798,51 @@ export const RESERVED_DICT_METHODS = ['keys', 'values', 'entries'];
999
798
  export const anyTypeValue = Object.freeze({
1000
799
  __rill_type: true,
1001
800
  typeName: 'any',
1002
- structure: { type: 'any' },
801
+ structure: { kind: 'any' },
1003
802
  });
1004
803
  /**
1005
- * Convert a RillType structural descriptor to a RillTypeValue.
1006
- * Uses the RillType's `type` field as the `typeName`.
804
+ * Convert a TypeStructure descriptor to a RillTypeValue.
805
+ * Uses the TypeStructure's `kind` field as the `typeName`.
1007
806
  * Falls back to 'any' for compound types that lack a direct RillTypeName mapping.
1008
807
  */
1009
- export function rillTypeToTypeValue(type) {
808
+ export function structureToTypeValue(type) {
1010
809
  const validNames = VALID_TYPE_NAMES;
1011
810
  return Object.freeze({
1012
811
  __rill_type: true,
1013
- typeName: (validNames.includes(type.type)
1014
- ? type.type
812
+ typeName: (validNames.includes(type.kind)
813
+ ? type.kind
1015
814
  : 'any'),
1016
815
  structure: type,
1017
816
  });
1018
817
  }
818
+ /** @deprecated Use structureToTypeValue instead. */
819
+ export const rillTypeToTypeValue = structureToTypeValue;
1019
820
  /**
1020
821
  * Check if a type is a collection (dict, ordered, tuple) with defined
1021
822
  * fields or elements. Used to decide if an empty collection can be
1022
823
  * synthesized and hydrated.
1023
824
  */
1024
825
  export function hasCollectionFields(type) {
1025
- return ((type.type === 'dict' && (!!type.fields || !!type.valueType)) ||
1026
- (type.type === 'ordered' && (!!type.fields || !!type.valueType)) ||
1027
- (type.type === 'tuple' && (!!type.elements || !!type.valueType)));
826
+ return ((type.kind === 'dict' &&
827
+ (!!type.fields ||
828
+ !!type.valueType)) ||
829
+ (type.kind === 'ordered' &&
830
+ (!!type.fields ||
831
+ !!type.valueType)) ||
832
+ (type.kind === 'tuple' &&
833
+ (!!type.elements ||
834
+ !!type.valueType)));
1028
835
  }
1029
836
  /**
1030
- * Create an empty collection value matching the given RillType.
837
+ * Create an empty collection value matching the given TypeStructure.
1031
838
  * Assumes the type is dict, ordered, or tuple.
1032
839
  */
1033
840
  export function emptyForType(type) {
1034
- if (type.type === 'dict')
841
+ if (type.kind === 'dict')
1035
842
  return {};
1036
- if (type.type === 'ordered')
843
+ if (type.kind === 'ordered')
1037
844
  return createOrdered([]);
1038
- if (type.type === 'tuple')
845
+ if (type.kind === 'tuple')
1039
846
  return createTuple([]);
1040
847
  return {};
1041
848
  }
@@ -1050,7 +857,7 @@ export function isReservedMethod(name) {
1050
857
  * - next: callable - function to get next iterator
1051
858
  * - value: any (only required when not done) - current element
1052
859
  */
1053
- export function isRillIterator(value) {
860
+ export function isIterator(value) {
1054
861
  if (!isDict(value))
1055
862
  return false;
1056
863
  const dict = value;
@@ -1063,32 +870,9 @@ export function isRillIterator(value) {
1063
870
  return false;
1064
871
  return true;
1065
872
  }
1066
- /**
1067
- * Deep copy a RillValue, producing a new independent value.
1068
- * Handles primitives, arrays, plain dicts, and null.
1069
- * Special markers (closures, tuples, ordered, vectors, type values) are returned
1070
- * as-is since they are immutable by contract.
1071
- */
1072
- export function deepCopyRillValue(value) {
1073
- if (value === null || typeof value !== 'object') {
1074
- return value;
1075
- }
1076
- if (Array.isArray(value)) {
1077
- return value.map(deepCopyRillValue);
1078
- }
1079
- // Plain dict: copy recursively. Special markers (RillTuple, RillOrdered, etc.)
1080
- // carry __rill_* own properties and are treated as immutable; return as-is.
1081
- if (!('__rill_tuple' in value) &&
1082
- !('__rill_ordered' in value) &&
1083
- !('__rill_vector' in value) &&
1084
- !('__rill_type' in value) &&
1085
- !('__type' in value) &&
1086
- !('__rill_field_descriptor' in value)) {
1087
- const copy = {};
1088
- for (const [k, v] of Object.entries(value)) {
1089
- copy[k] = deepCopyRillValue(v);
1090
- }
1091
- return copy;
1092
- }
1093
- return value;
1094
- }
873
+ /** @deprecated Use isIterator instead. */
874
+ export const isRillIterator = isIterator;
875
+ /** Copy a RillValue. Delegates to type-registrations. */
876
+ export const copyValue = registryCopyValue;
877
+ /** @deprecated Use copyValue instead. */
878
+ export const deepCopyRillValue = copyValue;