@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
@@ -10,7 +10,7 @@ export interface VersionInfo {
10
10
  /**
11
11
  * Version string from package.json
12
12
  */
13
- export declare const VERSION = "0.16.0";
13
+ export declare const VERSION = "0.17.0";
14
14
  /**
15
15
  * Parsed version components
16
16
  */
@@ -3,13 +3,13 @@
3
3
  /**
4
4
  * Version string from package.json
5
5
  */
6
- export const VERSION = '0.16.0';
6
+ export const VERSION = '0.17.0';
7
7
  /**
8
8
  * Parsed version components
9
9
  */
10
10
  export const VERSION_INFO = {
11
11
  major: 0,
12
- minor: 16,
12
+ minor: 17,
13
13
  patch: 0,
14
14
  prerelease: undefined,
15
15
  };
package/dist/index.d.ts CHANGED
@@ -5,11 +5,22 @@
5
5
  export { LexerError, tokenize, type TokenizeOptions } from './lexer/index.js';
6
6
  export { parse, parseWithRecovery } from './parser/index.js';
7
7
  export type { ParseResult, RecoveryErrorNode, ErrorNode } from './types.js';
8
- export { anyTypeValue, type ApplicationCallable, BreakSignal, callable, type CallableFn, type CallFrame, commonType, type CaptureEvent, type ConfigFieldDescriptor, createRuntimeContext, createStepper, createTuple, createVector, type DocumentationCoverageResult, emitExtensionEvent, contextResolver, extResolver, type ErrorEvent, execute, type ExecutionResult, type ExecutionStepper, type ExtensionConfigSchema, type ExtensionEvent, type ExtensionFactory, type ExtensionManifest, type ExtensionResult, type FsExtensionContract, type FunctionMetadata, hoistExtension, type HoistedExtension, type HostCallEvent, type FunctionReturnEvent, getCallStack, generateManifest, getDocumentationCoverage, getFunctions, getLanguageReference, buildFieldDescriptor, buildTypeMethodDicts, formatStructuralType, inferElementType, inferStructuralType, inferType, invokeCallable, isApplicationCallable, moduleResolver, isTuple, isCallable, isDict, isReservedMethod, isRillIterator, isRuntimeCallable, isScriptCallable, isTypeValue, isVector, type KvExtensionContract, type NativeArray, type NativePlainObject, type NativeResult, type NativeValue, type LlmExtensionContract, type SchemaEntry, type ObservabilityCallbacks, type ParamMetadata, paramToFieldDef, popCallFrame, prefixFunctions, pushCallFrame, RESERVED_DICT_METHODS, rillTypeToTypeValue, ReturnSignal, type RillCallable, type RillFieldDef, type RillFunction, type RillParam, type RillIterator, type RillTuple, type RillType, type RillTypeValue, type RillValue, type RillVector, type RuntimeCallable, type ResolverResult, type RuntimeCallbacks, type RuntimeContext, type RuntimeOptions, type SchemeResolver, type ScriptCallable, type StepEndEvent, type StepResult, type StepStartEvent, type VectorExtensionContract, structuralTypeEquals, structuralTypeMatches, toNative, VERSION, VERSION_INFO, type VersionInfo, } from './runtime/index.js';
9
- /** @deprecated Use RillType instead. Will be removed in the next major version. */
10
- export type { RillStructuralType } from './runtime/index.js';
8
+ export { anyTypeValue, type ApplicationCallable, BreakSignal, buildFieldDescriptor, callable, type CallableFn, type CallFrame, commonType, type CaptureEvent, type ConfigFieldDescriptor, contextResolver, createRuntimeContext, createStepper, createTestContext, createTuple, createVector, type DocumentationCoverageResult, emitExtensionEvent, type ErrorEvent, execute, type ExecutionResult, type ExecutionStepper, ExtensionBindingError, type ExtensionConfigSchema, type ExtensionEvent, type ExtensionFactory, type ExtensionFactoryResult, type ExtensionManifest, extResolver, formatStructure, type FsExtensionContract, type FunctionMetadata, type FunctionReturnEvent, generateManifest, getCallStack, getDocumentationCoverage, getFunctions, getLanguageReference, type HostCallEvent, inferElementType, inferStructure, inferType, invokeCallable, isApplicationCallable, isCallable, isDict, isIterator, isReservedMethod, isRuntimeCallable, isScriptCallable, isTuple, isTypeValue, isVector, type KvExtensionContract, moduleResolver, type NativeArray, type NativePlainObject, type NativeResult, type NativeValue, type ObservabilityCallbacks, type ParamMetadata, paramToFieldDef, popCallFrame, pushCallFrame, RESERVED_DICT_METHODS, type ResolverResult, ReturnSignal, type RillCallable, type RillFieldDef, type RillFunction, type RillIterator, type RillParam, type RillTuple, type RillTypeValue, type RillValue, type RillVector, type RuntimeCallable, type RuntimeCallbacks, type RuntimeContext, type RuntimeOptions, type SchemaEntry, type SchemeResolver, type ScriptCallable, type StepEndEvent, type StepResult, type StepStartEvent, structureEquals, structureMatches, structureToTypeValue, toCallable, toNative, type TypeDefinition, type TypeProtocol, type TypeStructure, VERSION, VERSION_INFO, type VersionInfo, } from './runtime/index.js';
9
+ /** @deprecated Use TypeStructure instead. Will be removed in the next major version. */
10
+ export type { RillType } from './runtime/index.js';
11
+ /** @deprecated Use formatStructure instead. */
12
+ export { formatStructuralType } from './runtime/index.js';
13
+ /** @deprecated Use inferStructure instead. */
14
+ export { inferStructuralType } from './runtime/index.js';
15
+ /** @deprecated Use isIterator instead. */
16
+ export { isRillIterator } from './runtime/index.js';
17
+ /** @deprecated Use structureToTypeValue instead. */
18
+ export { rillTypeToTypeValue } from './runtime/index.js';
19
+ /** @deprecated Use structureEquals instead. */
20
+ export { structuralTypeEquals } from './runtime/index.js';
21
+ /** @deprecated Use structureMatches instead. */
22
+ export { structuralTypeMatches } from './runtime/index.js';
11
23
  export { type ErrorCategory, type ErrorDefinition, type ErrorSeverity, ERROR_REGISTRY, renderMessage, getHelpUrl, createError, } from './types.js';
12
24
  export { formatRillError, formatRillErrorJson, type FormatErrorOptions, type FormatErrorJsonOptions, type SourceMap, } from './error-formatter.js';
13
- export { VALID_TYPE_NAMES } from './constants.js';
14
25
  export { type HighlightCategory, TOKEN_HIGHLIGHT_MAP, } from './highlight-map.js';
15
26
  export * from './types.js';
package/dist/index.js CHANGED
@@ -4,7 +4,20 @@
4
4
  */
5
5
  export { LexerError, tokenize } from './lexer/index.js';
6
6
  export { parse, parseWithRecovery } from './parser/index.js';
7
- export { anyTypeValue, BreakSignal, callable, commonType, createRuntimeContext, createStepper, createTuple, createVector, emitExtensionEvent, contextResolver, extResolver, execute, hoistExtension, getCallStack, generateManifest, getDocumentationCoverage, getFunctions, getLanguageReference, buildFieldDescriptor, buildTypeMethodDicts, formatStructuralType, inferElementType, inferStructuralType, inferType, invokeCallable, isApplicationCallable, moduleResolver, isTuple, isCallable, isDict, isReservedMethod, isRillIterator, isRuntimeCallable, isScriptCallable, isTypeValue, isVector, paramToFieldDef, popCallFrame, prefixFunctions, pushCallFrame, RESERVED_DICT_METHODS, rillTypeToTypeValue, ReturnSignal, structuralTypeEquals, structuralTypeMatches, toNative, VERSION, VERSION_INFO, } from './runtime/index.js';
7
+ export { anyTypeValue, BreakSignal, buildFieldDescriptor, callable, commonType, contextResolver, createRuntimeContext, createStepper, createTestContext, createTuple, createVector, emitExtensionEvent, execute, ExtensionBindingError, extResolver, formatStructure, generateManifest, getCallStack, getDocumentationCoverage, getFunctions, getLanguageReference, inferElementType, inferStructure, inferType, invokeCallable, isApplicationCallable, isCallable, isDict, isIterator, isReservedMethod, isRuntimeCallable, isScriptCallable, isTuple, isTypeValue, isVector, moduleResolver, paramToFieldDef, popCallFrame, pushCallFrame, RESERVED_DICT_METHODS, ReturnSignal, structureEquals, structureMatches, structureToTypeValue, toCallable, toNative, VERSION, VERSION_INFO, } from './runtime/index.js';
8
+ // Deprecated aliases — old names kept for one release
9
+ /** @deprecated Use formatStructure instead. */
10
+ export { formatStructuralType } from './runtime/index.js';
11
+ /** @deprecated Use inferStructure instead. */
12
+ export { inferStructuralType } from './runtime/index.js';
13
+ /** @deprecated Use isIterator instead. */
14
+ export { isRillIterator } from './runtime/index.js';
15
+ /** @deprecated Use structureToTypeValue instead. */
16
+ export { rillTypeToTypeValue } from './runtime/index.js';
17
+ /** @deprecated Use structureEquals instead. */
18
+ export { structuralTypeEquals } from './runtime/index.js';
19
+ /** @deprecated Use structureMatches instead. */
20
+ export { structuralTypeMatches } from './runtime/index.js';
8
21
  // ============================================================
9
22
  // ERROR TAXONOMY
10
23
  // ============================================================
@@ -14,10 +27,6 @@ export { ERROR_REGISTRY, renderMessage, getHelpUrl, createError, } from './types
14
27
  // ============================================================
15
28
  export { formatRillError, formatRillErrorJson, } from './error-formatter.js';
16
29
  // ============================================================
17
- // CONSTANTS
18
- // ============================================================
19
- export { VALID_TYPE_NAMES } from './constants.js';
20
- // ============================================================
21
30
  // SYNTAX HIGHLIGHTING
22
31
  // ============================================================
23
32
  export { TOKEN_HIGHLIGHT_MAP, } from './highlight-map.js';
@@ -98,6 +98,18 @@ export function parseTypeRef(state, opts) {
98
98
  * @internal
99
99
  */
100
100
  function parseSingleType(state, opts) {
101
+ // Zero-param closure type: || :returnType or bare ||
102
+ if (check(state, TOKEN_TYPES.OR)) {
103
+ advance(state); // consume || (OR token)
104
+ // Check for :returnType
105
+ if (check(state, TOKEN_TYPES.COLON)) {
106
+ advance(state); // consume :
107
+ const ret = parseSingleType(state, opts);
108
+ return { kind: 'static', typeName: 'closure', args: [{ value: ret }] };
109
+ }
110
+ // Bare || without :returnType — equivalent to 'closure'
111
+ return { kind: 'static', typeName: 'closure' };
112
+ }
101
113
  if (check(state, TOKEN_TYPES.DOLLAR)) {
102
114
  advance(state); // consume $
103
115
  const nameToken = expect(state, TOKEN_TYPES.IDENTIFIER, 'Expected variable name after $');
@@ -56,7 +56,13 @@ Parser.prototype.parseUseExpr = function () {
56
56
  let typeRef = null;
57
57
  let closureAnnotation = null;
58
58
  if (check(this.state, TOKEN_TYPES.COLON)) {
59
- if (peek(this.state, 1).type === TOKEN_TYPES.PIPE_BAR) {
59
+ if (peek(this.state, 1).type === TOKEN_TYPES.OR) {
60
+ // Zero-param closure annotation: :||
61
+ advance(this.state); // consume :
62
+ advance(this.state); // consume || (OR token)
63
+ closureAnnotation = [];
64
+ }
65
+ else if (peek(this.state, 1).type === TOKEN_TYPES.PIPE_BAR) {
60
66
  // Closure annotation: :|param: type, ...|
61
67
  advance(this.state); // consume :
62
68
  advance(this.state); // consume opening |
@@ -20,7 +20,7 @@
20
20
  * - Kept for API consistency with marshalArgs signature
21
21
  */
22
22
  import type { BodyNode, SourceLocation } from '../../types.js';
23
- import type { RillType, RillTypeValue, RillValue } from './values.js';
23
+ import type { TypeStructure, RillTypeValue, RillValue } from './values.js';
24
24
  interface RuntimeContextLike {
25
25
  readonly parent?: RuntimeContextLike | undefined;
26
26
  readonly variables: Map<string, RillValue>;
@@ -42,7 +42,7 @@ export type CallableFn = (args: Record<string, RillValue>, ctx: RuntimeContextLi
42
42
  */
43
43
  export interface RillParam {
44
44
  readonly name: string;
45
- readonly type: RillType | undefined;
45
+ readonly type: TypeStructure | undefined;
46
46
  readonly defaultValue: RillValue | undefined;
47
47
  readonly annotations: Record<string, RillValue>;
48
48
  }
@@ -57,6 +57,8 @@ export interface RillFunction {
57
57
  readonly fn: CallableFn;
58
58
  readonly annotations?: Record<string, RillValue>;
59
59
  readonly returnType: RillTypeValue;
60
+ /** When true, RILL-R003 generic receiver validation is skipped for this method. */
61
+ readonly skipReceiverValidation?: boolean;
60
62
  }
61
63
  /** Common fields for all callable types */
62
64
  interface CallableBase {
@@ -112,6 +114,16 @@ export declare function isApplicationCallable(value: RillValue): value is Applic
112
114
  * @param isProperty If true, auto-invokes when accessed from dict (property-style)
113
115
  */
114
116
  export declare function callable(fn: CallableFn, isProperty?: boolean): ApplicationCallable;
117
+ /**
118
+ * Convert a RillFunction to an ApplicationCallable.
119
+ *
120
+ * Validates the input and produces a callable value accepted by the loader.
121
+ * Pure function with no side effects.
122
+ *
123
+ * @param def - Host function definition to convert
124
+ * @returns ApplicationCallable with __type, kind, isProperty, and preserved annotations
125
+ */
126
+ export declare function toCallable(def: RillFunction, isProperty?: boolean): ApplicationCallable;
115
127
  /** Type guard for dict (plain object, not array, not callable, not tuple) */
116
128
  export declare function isDict(value: RillValue): value is Record<string, RillValue>;
117
129
  /** Format a callable for display */
@@ -129,19 +141,19 @@ export declare function formatCallable(callable: RillCallable): string;
129
141
  */
130
142
  export declare function callableEquals(a: ScriptCallable, b: ScriptCallable, valueEquals?: (a: RillValue, b: RillValue) => boolean): boolean;
131
143
  /**
132
- * Build a RillType closure variant from a closure's parameter list.
144
+ * Build a TypeStructure closure variant from a closure's parameter list.
133
145
  *
134
146
  * Called at closure creation time to build the structural type for `$fn.^input`.
135
147
  * - Typed params use param.type directly when present
136
- * - Untyped params (type: undefined) map to { type: 'any' }
137
- * - Return type is always { type: 'any' }
148
+ * - Untyped params (type: undefined) map to { kind: 'any' }
149
+ * - Return type is always { kind: 'any' }
138
150
  *
139
151
  * No validation: parser already validates type names.
140
152
  *
141
153
  * @param params - Closure parameter definitions (RillParam[])
142
- * @returns Frozen RillType with closure variant
154
+ * @returns Frozen TypeStructure with closure variant
143
155
  */
144
- export declare function paramsToStructuralType(params: readonly RillParam[]): RillType;
156
+ export declare function paramsToStructuralType(params: readonly RillParam[]): TypeStructure;
145
157
  /**
146
158
  * Validate defaultValue type matches declared parameter type.
147
159
  *
@@ -171,7 +183,7 @@ export interface MarshalOptions {
171
183
  *
172
184
  * Pure function: no class context, no evaluator, no side effects.
173
185
  */
174
- export declare function hydrateFieldDefaults(value: RillValue, type: RillType): RillValue;
186
+ export declare function hydrateFieldDefaults(value: RillValue, type: TypeStructure): RillValue;
175
187
  /**
176
188
  * Unified marshaling entry point for all 3 invocation paths.
177
189
  *
@@ -21,7 +21,7 @@
21
21
  */
22
22
  import { RuntimeError } from '../../types.js';
23
23
  import { astEquals } from './equals.js';
24
- import { formatValue, formatStructuralType, inferType, isOrdered, createOrdered, deepCopyRillValue, isTuple, paramToFieldDef, structuralTypeEquals, structuralTypeMatches, anyTypeValue, hasCollectionFields, emptyForType, } from './values.js';
24
+ import { formatValue, formatStructure, inferType, isOrdered, createOrdered, copyValue, isTuple, paramToFieldDef, structureEquals, structureMatches, anyTypeValue, hasCollectionFields, emptyForType, } from './values.js';
25
25
  /** Type guard for any callable */
26
26
  export function isCallable(value) {
27
27
  return (typeof value === 'object' &&
@@ -61,6 +61,35 @@ export function callable(fn, isProperty = false) {
61
61
  isProperty,
62
62
  };
63
63
  }
64
+ /**
65
+ * Convert a RillFunction to an ApplicationCallable.
66
+ *
67
+ * Validates the input and produces a callable value accepted by the loader.
68
+ * Pure function with no side effects.
69
+ *
70
+ * @param def - Host function definition to convert
71
+ * @returns ApplicationCallable with __type, kind, isProperty, and preserved annotations
72
+ */
73
+ export function toCallable(def, isProperty = false) {
74
+ if (def == null) {
75
+ throw new TypeError('RillFunction cannot be null or undefined');
76
+ }
77
+ if (typeof def.fn !== 'function') {
78
+ throw new TypeError('RillFunction.fn must be a function');
79
+ }
80
+ if (!Array.isArray(def.params)) {
81
+ throw new TypeError('RillFunction.params must be an array');
82
+ }
83
+ return {
84
+ __type: 'callable',
85
+ kind: 'application',
86
+ isProperty,
87
+ fn: def.fn,
88
+ params: def.params,
89
+ returnType: def.returnType,
90
+ annotations: def.annotations ?? {},
91
+ };
92
+ }
64
93
  /** Type guard for dict (plain object, not array, not callable, not tuple) */
65
94
  export function isDict(value) {
66
95
  return (typeof value === 'object' &&
@@ -116,14 +145,14 @@ export function callableEquals(a, b, valueEquals = (x, y) => formatValue(x) ===
116
145
  return false;
117
146
  if (ap.name !== bp.name)
118
147
  return false;
119
- // Compare type via structuralTypeEquals; absent type (any-typed) matches absent type
148
+ // Compare type via structureEquals; absent type (any-typed) matches absent type
120
149
  if (ap.type === undefined && bp.type !== undefined)
121
150
  return false;
122
151
  if (ap.type !== undefined && bp.type === undefined)
123
152
  return false;
124
153
  if (ap.type !== undefined &&
125
154
  bp.type !== undefined &&
126
- !structuralTypeEquals(ap.type, bp.type))
155
+ !structureEquals(ap.type, bp.type))
127
156
  return false;
128
157
  if (!valueEquals(ap.defaultValue ?? null, bp.defaultValue ?? null)) {
129
158
  return false;
@@ -146,24 +175,24 @@ export function callableEquals(a, b, valueEquals = (x, y) => formatValue(x) ===
146
175
  return true;
147
176
  }
148
177
  /**
149
- * Build a RillType closure variant from a closure's parameter list.
178
+ * Build a TypeStructure closure variant from a closure's parameter list.
150
179
  *
151
180
  * Called at closure creation time to build the structural type for `$fn.^input`.
152
181
  * - Typed params use param.type directly when present
153
- * - Untyped params (type: undefined) map to { type: 'any' }
154
- * - Return type is always { type: 'any' }
182
+ * - Untyped params (type: undefined) map to { kind: 'any' }
183
+ * - Return type is always { kind: 'any' }
155
184
  *
156
185
  * No validation: parser already validates type names.
157
186
  *
158
187
  * @param params - Closure parameter definitions (RillParam[])
159
- * @returns Frozen RillType with closure variant
188
+ * @returns Frozen TypeStructure with closure variant
160
189
  */
161
190
  export function paramsToStructuralType(params) {
162
- const closureParams = params.map((param) => paramToFieldDef(param.name, param.type ?? { type: 'any' }, param.defaultValue));
191
+ const closureParams = params.map((param) => paramToFieldDef(param.name, param.type ?? { kind: 'any' }, param.defaultValue));
163
192
  return Object.freeze({
164
- type: 'closure',
193
+ kind: 'closure',
165
194
  params: closureParams,
166
- ret: { type: 'any' },
195
+ ret: { kind: 'any' },
167
196
  });
168
197
  }
169
198
  /**
@@ -182,9 +211,9 @@ export function validateDefaultValueType(param, _functionName) {
182
211
  // Skip validation when type is undefined (any-typed, all defaults valid)
183
212
  if (param.type === undefined)
184
213
  return;
185
- if (!structuralTypeMatches(param.defaultValue, param.type)) {
214
+ if (!structureMatches(param.defaultValue, param.type)) {
186
215
  const actualType = inferType(param.defaultValue);
187
- const expectedType = formatStructuralType(param.type);
216
+ const expectedType = formatStructure(param.type);
188
217
  throw new Error(`Invalid defaultValue for parameter '${param.name}': expected ${expectedType}, got ${actualType}`);
189
218
  }
190
219
  }
@@ -198,16 +227,17 @@ export function validateDefaultValueType(param, _functionName) {
198
227
  * Pure function: no class context, no evaluator, no side effects.
199
228
  */
200
229
  export function hydrateFieldDefaults(value, type) {
201
- if (type.type === 'dict' && type.fields && isDict(value)) {
230
+ if (type.kind === 'dict' && type.fields && isDict(value)) {
231
+ const t = type;
202
232
  const dictValue = value;
203
233
  // Seed with all input entries so extra keys survive (structural match allows extras)
204
234
  const result = { ...dictValue };
205
- for (const [fieldName, fieldDef] of Object.entries(type.fields)) {
235
+ for (const [fieldName, fieldDef] of Object.entries(t.fields)) {
206
236
  if (fieldName in dictValue) {
207
237
  result[fieldName] = hydrateFieldDefaults(dictValue[fieldName], fieldDef.type);
208
238
  }
209
239
  else if (fieldDef.defaultValue !== undefined) {
210
- result[fieldName] = hydrateFieldDefaults(deepCopyRillValue(fieldDef.defaultValue), fieldDef.type);
240
+ result[fieldName] = hydrateFieldDefaults(copyValue(fieldDef.defaultValue), fieldDef.type);
211
241
  }
212
242
  else if (hasCollectionFields(fieldDef.type)) {
213
243
  result[fieldName] = hydrateFieldDefaults(emptyForType(fieldDef.type), fieldDef.type);
@@ -216,11 +246,14 @@ export function hydrateFieldDefaults(value, type) {
216
246
  }
217
247
  return result;
218
248
  }
219
- if (type.type === 'ordered' && type.fields && isOrdered(value)) {
249
+ if (type.kind === 'ordered' &&
250
+ type.fields &&
251
+ isOrdered(value)) {
252
+ const t = type;
220
253
  const lookup = new Map(value.entries.map(([k, v]) => [k, v]));
221
- const fieldNames = new Set(type.fields.map((f) => f.name ?? ''));
254
+ const fieldNames = new Set(t.fields.map((f) => f.name ?? ''));
222
255
  const resultEntries = [];
223
- for (const field of type.fields) {
256
+ for (const field of t.fields) {
224
257
  const name = field.name ?? '';
225
258
  if (lookup.has(name)) {
226
259
  resultEntries.push([
@@ -231,7 +264,7 @@ export function hydrateFieldDefaults(value, type) {
231
264
  else if (field.defaultValue !== undefined) {
232
265
  resultEntries.push([
233
266
  name,
234
- hydrateFieldDefaults(deepCopyRillValue(field.defaultValue), field.type),
267
+ hydrateFieldDefaults(copyValue(field.defaultValue), field.type),
235
268
  ]);
236
269
  }
237
270
  else if (hasCollectionFields(field.type)) {
@@ -250,7 +283,9 @@ export function hydrateFieldDefaults(value, type) {
250
283
  }
251
284
  return createOrdered(resultEntries);
252
285
  }
253
- if (type.type === 'tuple' && type.elements && isTuple(value)) {
286
+ if (type.kind === 'tuple' &&
287
+ type.elements &&
288
+ isTuple(value)) {
254
289
  const elements = type.elements;
255
290
  const entries = value.entries;
256
291
  // All fields present: recurse into nested types for present positions
@@ -270,7 +305,7 @@ export function hydrateFieldDefaults(value, type) {
270
305
  resultEntries.push(hydrateFieldDefaults(entries[i], el.type));
271
306
  }
272
307
  else if (el.defaultValue !== undefined) {
273
- resultEntries.push(hydrateFieldDefaults(deepCopyRillValue(el.defaultValue), el.type));
308
+ resultEntries.push(hydrateFieldDefaults(copyValue(el.defaultValue), el.type));
274
309
  }
275
310
  else if (hasCollectionFields(el.type)) {
276
311
  resultEntries.push(hydrateFieldDefaults(emptyForType(el.type), el.type));
@@ -327,6 +362,11 @@ export function marshalArgs(args, params, options) {
327
362
  if (param.defaultValue !== undefined) {
328
363
  value = param.defaultValue;
329
364
  }
365
+ else if (param.type !== undefined && hasCollectionFields(param.type)) {
366
+ // Collection-typed param with field-level defaults: synthesize empty
367
+ // collection so Stage 2.5 (hydrateFieldDefaults) can fill in defaults
368
+ value = emptyForType(param.type);
369
+ }
330
370
  else {
331
371
  // Stage 2: Missing required parameter
332
372
  throw new RuntimeError('RILL-R044', `Missing argument for parameter '${param.name}'`, location, {
@@ -341,8 +381,8 @@ export function marshalArgs(args, params, options) {
341
381
  }
342
382
  // Stage 3: Type check when param.type is defined
343
383
  if (param.type !== undefined) {
344
- if (!structuralTypeMatches(value, param.type)) {
345
- const expectedType = formatStructuralType(param.type);
384
+ if (!structureMatches(value, param.type)) {
385
+ const expectedType = formatStructure(param.type);
346
386
  const actualType = inferType(value);
347
387
  throw new RuntimeError('RILL-R001', `Parameter type mismatch: ${param.name} expects ${expectedType}, got ${actualType}`, location, {
348
388
  functionName,
@@ -6,18 +6,7 @@
6
6
  */
7
7
  import type { RuntimeContext, RuntimeOptions } from './types.js';
8
8
  import { type RillValue } from './values.js';
9
- import { type RillFunction } from './callable.js';
10
9
  export declare const UNVALIDATED_METHOD_PARAMS: Set<string>;
11
- export declare const UNVALIDATED_METHOD_RECEIVERS: Set<string>;
12
- /**
13
- * Build a ReadonlyMap of frozen ApplicationCallable dicts from an array of
14
- * [typeName, methods] pairs. Accepts pairs (not a plain object) so the same
15
- * typeName can appear more than once — duplicate method names across entries
16
- * for the same type trigger an Error (EC-6).
17
- *
18
- * Re-exported from the public barrel index for host integration use.
19
- */
20
- export declare function buildTypeMethodDicts(pairs: Array<[string, Record<string, RillFunction>]>): ReadonlyMap<string, Readonly<Record<string, RillValue>>>;
21
10
  /**
22
11
  * Create a runtime context for script execution.
23
12
  * This is the main entry point for configuring the Rill runtime.
@@ -5,7 +5,8 @@
5
5
  * Public API for host applications.
6
6
  */
7
7
  import { RuntimeError } from '../../types.js';
8
- import { BUILTIN_FUNCTIONS, BUILTIN_METHODS } from '../ext/builtins.js';
8
+ import { BUILTIN_FUNCTIONS } from '../ext/builtins.js';
9
+ import { BUILT_IN_TYPES } from './type-registrations.js';
9
10
  import { bindDictCallables } from './types.js';
10
11
  import { inferType } from './values.js';
11
12
  import { callable, validateDefaultValueType, } from './callable.js';
@@ -17,69 +18,23 @@ const UNTYPED_BUILTINS = new Set(['log', 'chain']);
17
18
  // messages expected by protected language tests. Generic marshalArgs
18
19
  // must not fire before the method body's own check.
19
20
  export const UNVALIDATED_METHOD_PARAMS = new Set(['has', 'has_any', 'has_all']);
20
- // Built-in methods that perform their own receiver type checking with specific
21
- // error messages. Generic RILL-R003 must not fire before the method body runs.
22
- // Mirrors the old flat-structure convention of receiverTypes: [].
23
- export const UNVALIDATED_METHOD_RECEIVERS = new Set([
24
- 'head',
25
- 'tail',
26
- 'first',
27
- 'at',
28
- 'eq',
29
- 'ne',
30
- 'keys',
31
- 'values',
32
- 'entries',
33
- 'has',
34
- 'has_any',
35
- 'has_all',
36
- 'dimensions',
37
- 'model',
38
- 'similarity',
39
- 'dot',
40
- 'distance',
41
- 'norm',
42
- 'normalize',
43
- ]);
44
21
  /**
45
- * Build a ReadonlyMap of frozen ApplicationCallable dicts from an array of
46
- * [typeName, methods] pairs. Accepts pairs (not a plain object) so the same
47
- * typeName can appear more than once duplicate method names across entries
48
- * for the same type trigger an Error (EC-6).
49
- *
50
- * Re-exported from the public barrel index for host integration use.
22
+ * Derive the set of method names that handle their own receiver type checking.
23
+ * Collects names from methods where skipReceiverValidation is true.
24
+ * Methods without the flag default to standard RILL-R003 receiver validation.
51
25
  */
52
- export function buildTypeMethodDicts(pairs) {
53
- const registry = new Map();
54
- const result = new Map();
55
- for (const [typeName, methods] of pairs) {
56
- const seen = registry.get(typeName) ?? new Set();
57
- registry.set(typeName, seen);
58
- const existing = result.get(typeName) ?? {};
59
- const dict = { ...existing };
60
- for (const [name, fn] of Object.entries(methods)) {
61
- if (seen.has(name)) {
62
- throw new Error(`Duplicate method '${name}' on type '${typeName}'`);
26
+ function deriveUnvalidatedMethodReceivers(registrations) {
27
+ const bypass = new Set();
28
+ for (const reg of registrations) {
29
+ for (const [name, method] of Object.entries(reg.methods)) {
30
+ if (method.skipReceiverValidation) {
31
+ bypass.add(name);
63
32
  }
64
- seen.add(name);
65
- const appCallable = {
66
- __type: 'callable',
67
- kind: 'application',
68
- params: fn.params,
69
- returnType: fn.returnType,
70
- annotations: fn.annotations ?? {},
71
- isProperty: false,
72
- fn: fn.fn,
73
- };
74
- dict[name] = appCallable;
75
33
  }
76
- result.set(typeName, Object.freeze(dict));
77
34
  }
78
- return result;
35
+ return Object.freeze(bypass);
79
36
  }
80
37
  const BUILTIN_FN_CACHE = new Map();
81
- const BUILTIN_METHOD_PARAMS_CACHE = new Map();
82
- const BUILTIN_METHOD_RECEIVER_TYPES_CACHE = new Map();
83
38
  function initBuiltinCaches() {
84
39
  for (const [name, entry] of Object.entries(BUILTIN_FUNCTIONS)) {
85
40
  if (UNTYPED_BUILTINS.has(name)) {
@@ -94,21 +49,6 @@ function initBuiltinCaches() {
94
49
  };
95
50
  BUILTIN_FN_CACHE.set(name, { appCallable: typedCallable });
96
51
  }
97
- for (const [typeName, methods] of Object.entries(BUILTIN_METHODS)) {
98
- for (const [name, impl] of Object.entries(methods)) {
99
- // Accumulate receiver types across all type groups for this method name.
100
- // Skip methods that perform their own receiver type checking.
101
- if (!UNVALIDATED_METHOD_RECEIVERS.has(name)) {
102
- const existing = BUILTIN_METHOD_RECEIVER_TYPES_CACHE.get(name);
103
- BUILTIN_METHOD_RECEIVER_TYPES_CACHE.set(name, existing !== undefined ? [...existing, typeName] : [typeName]);
104
- }
105
- if (!UNVALIDATED_METHOD_PARAMS.has(name)) {
106
- if (impl.params.length > 0) {
107
- BUILTIN_METHOD_PARAMS_CACHE.set(name, impl.params);
108
- }
109
- }
110
- }
111
- }
112
52
  }
113
53
  // Initialise once at module load.
114
54
  initBuiltinCaches();
@@ -194,15 +134,74 @@ export function createRuntimeContext(options = {}) {
194
134
  const resolverConfigs = new Map(options.configurations?.resolvers
195
135
  ? Object.entries(options.configurations.resolvers)
196
136
  : []);
197
- // Build typeMethodDicts fresh per context so duplicate detection (EC-6)
198
- // uses isolated state and does not leak across context instances.
199
- const typeMethodDicts = buildTypeMethodDicts(Object.entries(BUILTIN_METHODS));
137
+ // EC-1: Validate no duplicate type names in registrations.
138
+ const seenTypeNames = new Set();
139
+ for (const reg of BUILT_IN_TYPES) {
140
+ if (seenTypeNames.has(reg.name)) {
141
+ throw new Error(`Duplicate type registration '${reg.name}'`);
142
+ }
143
+ seenTypeNames.add(reg.name);
144
+ }
145
+ // EC-2: Validate every registration has protocol.format.
146
+ for (const reg of BUILT_IN_TYPES) {
147
+ if (!reg.protocol.format) {
148
+ throw new Error(`Type '${reg.name}' missing required format protocol`);
149
+ }
150
+ }
151
+ // Derive typeNames from registrations (replaces VALID_TYPE_NAMES in context).
152
+ const typeNames = Object.freeze(BUILT_IN_TYPES.map((r) => r.name));
153
+ // Derive leafTypes from registrations where isLeaf === true, plus 'any'
154
+ // which has no registration but rejects type arguments (AC-4).
155
+ const leafTypes = Object.freeze(new Set([
156
+ ...BUILT_IN_TYPES.filter((r) => r.isLeaf).map((r) => r.name),
157
+ 'any',
158
+ ]));
159
+ // Derive method dicts from registration.methods (absorbs buildTypeMethodDicts
160
+ // logic). Validates EC-6: duplicate method on same type.
161
+ const methodRegistry = new Map();
162
+ const typeMethodDicts = new Map();
163
+ for (const reg of BUILT_IN_TYPES) {
164
+ const methods = reg.methods;
165
+ if (!methods || Object.keys(methods).length === 0)
166
+ continue;
167
+ const seen = methodRegistry.get(reg.name) ?? new Set();
168
+ methodRegistry.set(reg.name, seen);
169
+ const existing = typeMethodDicts.get(reg.name) ?? {};
170
+ const dict = { ...existing };
171
+ for (const [name, fn] of Object.entries(methods)) {
172
+ if (seen.has(name)) {
173
+ throw new Error(`Duplicate method '${name}' on type '${reg.name}'`);
174
+ }
175
+ seen.add(name);
176
+ const appCallable = {
177
+ __type: 'callable',
178
+ kind: 'application',
179
+ params: fn.params,
180
+ returnType: fn.returnType,
181
+ annotations: fn.annotations ?? {},
182
+ isProperty: false,
183
+ fn: fn.fn,
184
+ };
185
+ dict[name] = appCallable;
186
+ }
187
+ typeMethodDicts.set(reg.name, Object.freeze(dict));
188
+ }
189
+ // Derive bypass set from registrations: method names that handle their own
190
+ // receiver type checking. Generic RILL-R003 must not fire before the method body.
191
+ const unvalidatedMethodReceivers = deriveUnvalidatedMethodReceivers(BUILT_IN_TYPES);
192
+ // BC-5: Freeze all derived collections after creation.
193
+ Object.freeze(typeNames);
194
+ Object.freeze(typeMethodDicts);
195
+ // Suppress unused-variable warning for typeNames (consumed in later phases).
196
+ void typeNames;
200
197
  return {
201
198
  parent: undefined,
202
199
  variables,
203
200
  variableTypes,
204
201
  functions,
205
202
  typeMethodDicts,
203
+ leafTypes,
204
+ unvalidatedMethodReceivers,
206
205
  callbacks: { ...defaultCallbacks, ...options.callbacks },
207
206
  observability: options.observability ?? {},
208
207
  pipeValue: null,
@@ -232,6 +231,8 @@ export function createChildContext(parent, overrides) {
232
231
  variableTypes: new Map(),
233
232
  functions: parent.functions,
234
233
  typeMethodDicts: parent.typeMethodDicts,
234
+ leafTypes: parent.leafTypes,
235
+ unvalidatedMethodReceivers: parent.unvalidatedMethodReceivers,
235
236
  callbacks: parent.callbacks,
236
237
  observability: parent.observability,
237
238
  pipeValue: parent.pipeValue,
@@ -9,7 +9,7 @@
9
9
  import type { AnnotatedStatementNode, ASTNode, CaptureNode, ExpressionNode, RillTypeName, SourceLocation, StatementNode } from '../../../types.js';
10
10
  import type { RillCallable } from '../callable.js';
11
11
  import type { RuntimeContext } from '../types.js';
12
- import type { RillValue, RillType } from '../values.js';
12
+ import type { RillValue, TypeStructure } from '../values.js';
13
13
  /**
14
14
  * Capture information returned by handleCapture.
15
15
  */
@@ -39,7 +39,7 @@ export declare function handleCapture(capture: CaptureNode | null, value: RillVa
39
39
  * Assert that a value is of the expected type.
40
40
  * Returns the value unchanged if assertion passes, throws on mismatch.
41
41
  */
42
- export declare function assertType(value: RillValue, expected: RillTypeName | RillType, location?: SourceLocation): RillValue;
42
+ export declare function assertType(value: RillValue, expected: RillTypeName | TypeStructure, location?: SourceLocation): RillValue;
43
43
  /**
44
44
  * Evaluate an expression and return its value.
45
45
  * Main entry point for expression evaluation.