@rcrsr/rill 0.16.0 → 0.18.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 (97) hide show
  1. package/README.md +37 -21
  2. package/dist/ast-nodes.d.ts +14 -4
  3. package/dist/ast-unions.d.ts +1 -1
  4. package/dist/constants.d.ts +1 -1
  5. package/dist/constants.js +1 -0
  6. package/dist/error-registry.js +228 -0
  7. package/dist/ext/crypto/index.d.ts +3 -3
  8. package/dist/ext/crypto/index.js +62 -59
  9. package/dist/ext/exec/index.d.ts +3 -3
  10. package/dist/ext/exec/index.js +15 -9
  11. package/dist/ext/fetch/index.d.ts +3 -3
  12. package/dist/ext/fetch/index.js +17 -12
  13. package/dist/ext/fetch/request.js +1 -1
  14. package/dist/ext/fs/index.d.ts +3 -3
  15. package/dist/ext/fs/index.js +256 -266
  16. package/dist/ext/fs/sandbox.d.ts +18 -0
  17. package/dist/ext/fs/sandbox.js +33 -0
  18. package/dist/ext/kv/index.d.ts +3 -3
  19. package/dist/ext/kv/index.js +198 -196
  20. package/dist/ext/kv/store.d.ts +1 -1
  21. package/dist/ext/kv/store.js +2 -1
  22. package/dist/ext-parse-bridge.d.ts +10 -0
  23. package/dist/ext-parse-bridge.js +10 -0
  24. package/dist/generated/introspection-data.d.ts +1 -1
  25. package/dist/generated/introspection-data.js +385 -296
  26. package/dist/generated/version-data.d.ts +1 -1
  27. package/dist/generated/version-data.js +2 -2
  28. package/dist/highlight-map.js +1 -0
  29. package/dist/index.d.ts +1 -4
  30. package/dist/index.js +1 -5
  31. package/dist/lexer/operators.js +1 -0
  32. package/dist/parser/helpers.js +1 -0
  33. package/dist/parser/parser-expr.js +44 -5
  34. package/dist/parser/parser-literals.js +111 -4
  35. package/dist/parser/parser-shape.js +2 -2
  36. package/dist/parser/parser-types.js +12 -0
  37. package/dist/parser/parser-use.js +26 -3
  38. package/dist/parser/parser.d.ts +2 -0
  39. package/dist/parser/parser.js +2 -0
  40. package/dist/runtime/core/callable.d.ts +24 -13
  41. package/dist/runtime/core/callable.js +71 -38
  42. package/dist/runtime/core/context.d.ts +2 -13
  43. package/dist/runtime/core/context.js +80 -79
  44. package/dist/runtime/core/eval/base.d.ts +2 -2
  45. package/dist/runtime/core/eval/base.js +2 -0
  46. package/dist/runtime/core/eval/evaluator.d.ts +1 -1
  47. package/dist/runtime/core/eval/index.d.ts +3 -3
  48. package/dist/runtime/core/eval/index.js +11 -0
  49. package/dist/runtime/core/eval/mixins/closures.js +381 -41
  50. package/dist/runtime/core/eval/mixins/collections.js +81 -6
  51. package/dist/runtime/core/eval/mixins/control-flow.js +1 -1
  52. package/dist/runtime/core/eval/mixins/conversion.js +61 -115
  53. package/dist/runtime/core/eval/mixins/core.js +17 -4
  54. package/dist/runtime/core/eval/mixins/expressions.js +36 -27
  55. package/dist/runtime/core/eval/mixins/extraction.js +2 -3
  56. package/dist/runtime/core/eval/mixins/list-dispatch.js +1 -1
  57. package/dist/runtime/core/eval/mixins/literals.js +17 -6
  58. package/dist/runtime/core/eval/mixins/types.js +73 -54
  59. package/dist/runtime/core/eval/mixins/variables.js +12 -8
  60. package/dist/runtime/core/execute.d.ts +1 -1
  61. package/dist/runtime/core/field-descriptor.d.ts +3 -3
  62. package/dist/runtime/core/field-descriptor.js +2 -1
  63. package/dist/runtime/core/introspection.d.ts +2 -2
  64. package/dist/runtime/core/introspection.js +7 -6
  65. package/dist/runtime/core/resolvers.d.ts +1 -1
  66. package/dist/runtime/core/signals.d.ts +6 -1
  67. package/dist/runtime/core/signals.js +9 -0
  68. package/dist/runtime/core/types/constructors.d.ts +54 -0
  69. package/dist/runtime/core/types/constructors.js +201 -0
  70. package/dist/runtime/core/types/guards.d.ts +42 -0
  71. package/dist/runtime/core/types/guards.js +88 -0
  72. package/dist/runtime/core/types/index.d.ts +18 -0
  73. package/dist/runtime/core/types/index.js +19 -0
  74. package/dist/runtime/core/types/markers.d.ts +12 -0
  75. package/dist/runtime/core/types/markers.js +7 -0
  76. package/dist/runtime/core/types/operations.d.ts +98 -0
  77. package/dist/runtime/core/types/operations.js +804 -0
  78. package/dist/runtime/core/types/registrations.d.ts +126 -0
  79. package/dist/runtime/core/types/registrations.js +751 -0
  80. package/dist/runtime/core/{types.d.ts → types/runtime.d.ts} +22 -10
  81. package/dist/runtime/core/types/structures.d.ts +146 -0
  82. package/dist/runtime/core/types/structures.js +12 -0
  83. package/dist/runtime/core/values.d.ts +29 -209
  84. package/dist/runtime/core/values.js +56 -968
  85. package/dist/runtime/ext/builtins.js +88 -68
  86. package/dist/runtime/ext/extensions.d.ts +31 -125
  87. package/dist/runtime/ext/extensions.js +2 -94
  88. package/dist/runtime/ext/test-context.d.ts +28 -0
  89. package/dist/runtime/ext/test-context.js +155 -0
  90. package/dist/runtime/index.d.ts +12 -12
  91. package/dist/runtime/index.js +13 -5
  92. package/dist/signature-parser.d.ts +2 -2
  93. package/dist/signature-parser.js +14 -14
  94. package/dist/token-types.d.ts +1 -0
  95. package/dist/token-types.js +1 -0
  96. package/package.json +1 -1
  97. /package/dist/runtime/core/{types.js → types/runtime.js} +0 -0
@@ -9,8 +9,11 @@
9
9
  import { callable, isCallable, isDict } from '../core/callable.js';
10
10
  import { RuntimeError } from '../../types.js';
11
11
  import { parseSignatureRegistration } from '../../signature-parser.js';
12
- import { anyTypeValue, deepEquals, formatValue, inferType, isEmpty, isRillIterator, isVector, rillTypeToTypeValue, valueToJSON, } from '../core/values.js';
12
+ import { deepEquals, formatValue, inferType, serializeValue, } from '../core/types/registrations.js';
13
+ import { isIterator, isVector } from '../core/types/guards.js';
14
+ import { anyTypeValue, isEmpty, structureToTypeValue } from '../core/values.js';
13
15
  import { invokeCallable } from '../core/eval/index.js';
16
+ import { populateBuiltinMethods } from '../core/types/registrations.js';
14
17
  // ============================================================
15
18
  // ITERATOR HELPERS
16
19
  // ============================================================
@@ -70,7 +73,7 @@ export const BUILTIN_FUNCTIONS = {
70
73
  params: [
71
74
  {
72
75
  name: 'value',
73
- type: { type: 'any' },
76
+ type: { kind: 'any' },
74
77
  defaultValue: undefined,
75
78
  annotations: {},
76
79
  },
@@ -83,7 +86,7 @@ export const BUILTIN_FUNCTIONS = {
83
86
  params: [
84
87
  {
85
88
  name: 'message',
86
- type: { type: 'any' },
89
+ type: { kind: 'any' },
87
90
  defaultValue: undefined,
88
91
  annotations: {},
89
92
  },
@@ -103,21 +106,20 @@ export const BUILTIN_FUNCTIONS = {
103
106
  params: [
104
107
  {
105
108
  name: 'value',
106
- type: { type: 'any' },
109
+ type: { kind: 'any' },
107
110
  defaultValue: undefined,
108
111
  annotations: {},
109
112
  },
110
113
  ],
111
- returnType: rillTypeToTypeValue({ type: 'string' }),
114
+ returnType: structureToTypeValue({ kind: 'string' }),
112
115
  fn: (args, _ctx, location) => {
113
116
  const value = args['value'] ?? null;
114
117
  try {
115
- const jsonValue = valueToJSON(value);
118
+ const jsonValue = serializeValue(value);
116
119
  return JSON.stringify(jsonValue);
117
120
  }
118
121
  catch (err) {
119
- if (err instanceof RuntimeError)
120
- throw err;
122
+ // Wrap serialization errors (RILL-R067 from protocol) as RILL-R004
121
123
  if (err instanceof Error) {
122
124
  throw new RuntimeError('RILL-R004', err.message, location);
123
125
  }
@@ -135,14 +137,14 @@ export const BUILTIN_FUNCTIONS = {
135
137
  {
136
138
  name: 'items',
137
139
  type: {
138
- type: 'union',
139
- members: [{ type: 'list' }, { type: 'dict' }, { type: 'string' }],
140
+ kind: 'union',
141
+ members: [{ kind: 'list' }, { kind: 'dict' }, { kind: 'string' }],
140
142
  },
141
143
  defaultValue: undefined,
142
144
  annotations: {},
143
145
  },
144
146
  ],
145
- returnType: rillTypeToTypeValue({ type: 'list' }),
147
+ returnType: structureToTypeValue({ kind: 'list' }),
146
148
  fn: (args) => {
147
149
  const input = args['items'] ?? null;
148
150
  if (Array.isArray(input)) {
@@ -167,19 +169,19 @@ export const BUILTIN_FUNCTIONS = {
167
169
  params: [
168
170
  {
169
171
  name: 'start',
170
- type: { type: 'number' },
172
+ type: { kind: 'number' },
171
173
  defaultValue: undefined,
172
174
  annotations: {},
173
175
  },
174
176
  {
175
177
  name: 'stop',
176
- type: { type: 'number' },
178
+ type: { kind: 'number' },
177
179
  defaultValue: undefined,
178
180
  annotations: {},
179
181
  },
180
182
  {
181
183
  name: 'step',
182
- type: { type: 'number' },
184
+ type: { kind: 'number' },
183
185
  defaultValue: 1,
184
186
  annotations: {},
185
187
  },
@@ -217,13 +219,13 @@ export const BUILTIN_FUNCTIONS = {
217
219
  params: [
218
220
  {
219
221
  name: 'value',
220
- type: { type: 'any' },
222
+ type: { kind: 'any' },
221
223
  defaultValue: undefined,
222
224
  annotations: {},
223
225
  },
224
226
  {
225
227
  name: 'count',
226
- type: { type: 'number' },
228
+ type: { kind: 'number' },
227
229
  defaultValue: undefined,
228
230
  annotations: {},
229
231
  },
@@ -262,13 +264,13 @@ export const BUILTIN_FUNCTIONS = {
262
264
  params: [
263
265
  {
264
266
  name: 'value',
265
- type: { type: 'any' },
267
+ type: { kind: 'any' },
266
268
  defaultValue: undefined,
267
269
  annotations: {},
268
270
  },
269
271
  {
270
272
  name: 'transform',
271
- type: { type: 'any' },
273
+ type: { kind: 'any' },
272
274
  defaultValue: undefined,
273
275
  annotations: {},
274
276
  },
@@ -311,20 +313,10 @@ export const BUILTIN_FUNCTIONS = {
311
313
  // ============================================================
312
314
  // BUILT-IN METHODS
313
315
  // ============================================================
314
- /** Factory for comparison methods (lt, gt, le, ge) */
315
- function createComparisonMethod(compare) {
316
- return (receiver, args) => {
317
- const arg = args[0];
318
- if (typeof receiver === 'number' && typeof arg === 'number') {
319
- return compare(receiver, arg);
320
- }
321
- return compare(formatValue(receiver), formatValue(arg ?? ''));
322
- };
323
- }
324
316
  /** Receiver param prepended to every method's param list */
325
317
  const RECEIVER_PARAM = {
326
318
  name: 'receiver',
327
- type: { type: 'any' },
319
+ type: { kind: 'any' },
328
320
  defaultValue: undefined,
329
321
  annotations: {},
330
322
  };
@@ -337,7 +329,7 @@ const RECEIVER_PARAM = {
337
329
  *
338
330
  * EC-4: Receiver missing from record raises RILL-R044.
339
331
  */
340
- function buildMethodEntry(name, signature, method) {
332
+ function buildMethodEntry(name, signature, method, skipReceiverValidation) {
341
333
  const parsed = parseSignatureRegistration(signature, name);
342
334
  const methodParams = parsed.params;
343
335
  return {
@@ -359,8 +351,9 @@ function buildMethodEntry(name, signature, method) {
359
351
  ? { description: parsed.description }
360
352
  : {},
361
353
  returnType: parsed.returnType !== undefined
362
- ? rillTypeToTypeValue(parsed.returnType)
354
+ ? structureToTypeValue(parsed.returnType)
363
355
  : anyTypeValue,
356
+ ...(skipReceiverValidation ? { skipReceiverValidation: true } : {}),
364
357
  };
365
358
  }
366
359
  export const BUILTIN_METHODS = {
@@ -423,7 +416,7 @@ const mTail = (receiver, _args, _ctx, location) => {
423
416
  };
424
417
  /** Get iterator at first position for any collection */
425
418
  const mFirst = (receiver, _args, _ctx, location) => {
426
- if (isRillIterator(receiver))
419
+ if (isIterator(receiver))
427
420
  return receiver;
428
421
  if (Array.isArray(receiver))
429
422
  return makeListIterator(receiver, 0);
@@ -552,10 +545,34 @@ const mPadEnd = (receiver, args) => {
552
545
  const mEq = (receiver, args) => deepEquals(receiver, args[0] ?? null);
553
546
  /** Inequality check (deep structural comparison) */
554
547
  const mNe = (receiver, args) => !deepEquals(receiver, args[0] ?? null);
555
- const mLt = createComparisonMethod((a, b) => a < b);
556
- const mGt = createComparisonMethod((a, b) => a > b);
557
- const mLe = createComparisonMethod((a, b) => a <= b);
558
- const mGe = createComparisonMethod((a, b) => a >= b);
548
+ /** Less-than comparison (number or string) */
549
+ const mLt = (receiver, args) => {
550
+ const arg = args[0];
551
+ if (typeof receiver === 'number' && typeof arg === 'number')
552
+ return receiver < arg;
553
+ return formatValue(receiver) < formatValue(arg ?? '');
554
+ };
555
+ /** Greater-than comparison (number or string) */
556
+ const mGt = (receiver, args) => {
557
+ const arg = args[0];
558
+ if (typeof receiver === 'number' && typeof arg === 'number')
559
+ return receiver > arg;
560
+ return formatValue(receiver) > formatValue(arg ?? '');
561
+ };
562
+ /** Less-than-or-equal comparison (number or string) */
563
+ const mLe = (receiver, args) => {
564
+ const arg = args[0];
565
+ if (typeof receiver === 'number' && typeof arg === 'number')
566
+ return receiver <= arg;
567
+ return formatValue(receiver) <= formatValue(arg ?? '');
568
+ };
569
+ /** Greater-than-or-equal comparison (number or string) */
570
+ const mGe = (receiver, args) => {
571
+ const arg = args[0];
572
+ if (typeof receiver === 'number' && typeof arg === 'number')
573
+ return receiver >= arg;
574
+ return formatValue(receiver) >= formatValue(arg ?? '');
575
+ };
559
576
  /** Get all keys of a dict as a list */
560
577
  const mKeys = (receiver) => isDict(receiver) ? Object.keys(receiver) : [];
561
578
  /** Get all values of a dict as a list */
@@ -757,10 +774,10 @@ const SIG_CMP = '|other: any|:bool';
757
774
  BUILTIN_METHODS.string = Object.freeze({
758
775
  len: buildMethodEntry('len', SIG_LEN, mLen),
759
776
  trim: buildMethodEntry('trim', '||:string', mTrim),
760
- head: buildMethodEntry('head', SIG_HEAD, mHead),
761
- tail: buildMethodEntry('tail', SIG_TAIL, mTail),
762
- first: buildMethodEntry('first', SIG_FIRST, mFirst),
763
- at: buildMethodEntry('at', SIG_AT, mAt),
777
+ head: buildMethodEntry('head', SIG_HEAD, mHead, true),
778
+ tail: buildMethodEntry('tail', SIG_TAIL, mTail, true),
779
+ first: buildMethodEntry('first', SIG_FIRST, mFirst, true),
780
+ at: buildMethodEntry('at', SIG_AT, mAt, true),
764
781
  split: buildMethodEntry('split', '|separator: string = "\\n"|:list', mSplit),
765
782
  lines: buildMethodEntry('lines', '||:list', mLines),
766
783
  empty: buildMethodEntry('empty', SIG_EMPTY, mEmpty),
@@ -777,8 +794,8 @@ BUILTIN_METHODS.string = Object.freeze({
777
794
  repeat: buildMethodEntry('repeat', '|count: number|:string', mRepeat),
778
795
  pad_start: buildMethodEntry('pad_start', '|length: number, fill: string = " "|:string', mPadStart),
779
796
  pad_end: buildMethodEntry('pad_end', '|length: number, fill: string = " "|:string', mPadEnd),
780
- eq: buildMethodEntry('eq', SIG_EQ, mEq),
781
- ne: buildMethodEntry('ne', SIG_NE, mNe),
797
+ eq: buildMethodEntry('eq', SIG_EQ, mEq, true),
798
+ ne: buildMethodEntry('ne', SIG_NE, mNe, true),
782
799
  lt: buildMethodEntry('lt', SIG_CMP, mLt),
783
800
  gt: buildMethodEntry('gt', SIG_CMP, mGt),
784
801
  le: buildMethodEntry('le', SIG_CMP, mLe),
@@ -786,32 +803,32 @@ BUILTIN_METHODS.string = Object.freeze({
786
803
  });
787
804
  BUILTIN_METHODS.list = Object.freeze({
788
805
  len: buildMethodEntry('len', SIG_LEN, mLen),
789
- head: buildMethodEntry('head', SIG_HEAD, mHead),
790
- tail: buildMethodEntry('tail', SIG_TAIL, mTail),
791
- first: buildMethodEntry('first', SIG_FIRST, mFirst),
792
- at: buildMethodEntry('at', SIG_AT, mAt),
806
+ head: buildMethodEntry('head', SIG_HEAD, mHead, true),
807
+ tail: buildMethodEntry('tail', SIG_TAIL, mTail, true),
808
+ first: buildMethodEntry('first', SIG_FIRST, mFirst, true),
809
+ at: buildMethodEntry('at', SIG_AT, mAt, true),
793
810
  join: buildMethodEntry('join', '|separator: string = ","|:string', mJoin),
794
811
  empty: buildMethodEntry('empty', SIG_EMPTY, mEmpty),
795
- eq: buildMethodEntry('eq', SIG_EQ, mEq),
796
- ne: buildMethodEntry('ne', SIG_NE, mNe),
797
- has: buildMethodEntry('has', '|value: any|:bool', mHas),
798
- has_any: buildMethodEntry('has_any', '|candidates: list|:bool', mHasAny),
799
- has_all: buildMethodEntry('has_all', '|candidates: list|:bool', mHasAll),
812
+ eq: buildMethodEntry('eq', SIG_EQ, mEq, true),
813
+ ne: buildMethodEntry('ne', SIG_NE, mNe, true),
814
+ has: buildMethodEntry('has', '|value: any|:bool', mHas, true),
815
+ has_any: buildMethodEntry('has_any', '|candidates: list|:bool', mHasAny, true),
816
+ has_all: buildMethodEntry('has_all', '|candidates: list|:bool', mHasAll, true),
800
817
  });
801
818
  BUILTIN_METHODS.dict = Object.freeze({
802
819
  len: buildMethodEntry('len', SIG_LEN, mLen),
803
- first: buildMethodEntry('first', SIG_FIRST, mFirst),
820
+ first: buildMethodEntry('first', SIG_FIRST, mFirst, true),
804
821
  empty: buildMethodEntry('empty', SIG_EMPTY, mEmpty),
805
- eq: buildMethodEntry('eq', SIG_EQ, mEq),
806
- ne: buildMethodEntry('ne', SIG_NE, mNe),
807
- keys: buildMethodEntry('keys', '||:list', mKeys),
808
- values: buildMethodEntry('values', '||:list', mValues),
809
- entries: buildMethodEntry('entries', '||:list', mEntries),
822
+ eq: buildMethodEntry('eq', SIG_EQ, mEq, true),
823
+ ne: buildMethodEntry('ne', SIG_NE, mNe, true),
824
+ keys: buildMethodEntry('keys', '||:list', mKeys, true),
825
+ values: buildMethodEntry('values', '||:list', mValues, true),
826
+ entries: buildMethodEntry('entries', '||:list', mEntries, true),
810
827
  });
811
828
  BUILTIN_METHODS.number = Object.freeze({
812
829
  empty: buildMethodEntry('empty', SIG_EMPTY, mEmpty),
813
- eq: buildMethodEntry('eq', SIG_EQ, mEq),
814
- ne: buildMethodEntry('ne', SIG_NE, mNe),
830
+ eq: buildMethodEntry('eq', SIG_EQ, mEq, true),
831
+ ne: buildMethodEntry('ne', SIG_NE, mNe, true),
815
832
  lt: buildMethodEntry('lt', SIG_CMP, mLt),
816
833
  gt: buildMethodEntry('gt', SIG_CMP, mGt),
817
834
  le: buildMethodEntry('le', SIG_CMP, mLe),
@@ -819,18 +836,21 @@ BUILTIN_METHODS.number = Object.freeze({
819
836
  });
820
837
  BUILTIN_METHODS.bool = Object.freeze({
821
838
  empty: buildMethodEntry('empty', SIG_EMPTY, mEmpty),
822
- eq: buildMethodEntry('eq', SIG_EQ, mEq),
823
- ne: buildMethodEntry('ne', SIG_NE, mNe),
839
+ eq: buildMethodEntry('eq', SIG_EQ, mEq, true),
840
+ ne: buildMethodEntry('ne', SIG_NE, mNe, true),
824
841
  });
825
842
  // [ASSUMPTION] vector is a 6th group beyond the 5 specified basic types.
826
843
  // The 7 vector methods do not belong to string/list/dict/number/bool.
827
844
  // Adding this group ensures all 42 methods are accessible (AC-36).
828
845
  BUILTIN_METHODS.vector = Object.freeze({
829
- dimensions: buildMethodEntry('dimensions', '||:number', mDimensions),
830
- model: buildMethodEntry('model', '||:string', mModel),
831
- similarity: buildMethodEntry('similarity', '|other: any|:number', mSimilarity),
832
- dot: buildMethodEntry('dot', '|other: any|:number', mDot),
833
- distance: buildMethodEntry('distance', '|other: any|:number', mDistance),
834
- norm: buildMethodEntry('norm', '||:number', mNorm),
835
- normalize: buildMethodEntry('normalize', '||:any', mNormalize),
846
+ dimensions: buildMethodEntry('dimensions', '||:number', mDimensions, true),
847
+ model: buildMethodEntry('model', '||:string', mModel, true),
848
+ similarity: buildMethodEntry('similarity', '|other: any|:number', mSimilarity, true),
849
+ dot: buildMethodEntry('dot', '|other: any|:number', mDot, true),
850
+ distance: buildMethodEntry('distance', '|other: any|:number', mDistance, true),
851
+ norm: buildMethodEntry('norm', '||:number', mNorm, true),
852
+ normalize: buildMethodEntry('normalize', '||:any', mNormalize, true),
836
853
  });
854
+ // Populate registration methods from BUILTIN_METHODS at module load time.
855
+ // No circular dependency: type-registrations.ts does not import builtins.ts.
856
+ populateBuiltinMethods(BUILTIN_METHODS);
@@ -1,5 +1,6 @@
1
- import type { RillFunction } from '../core/callable.js';
2
- import type { ExtensionEvent, RuntimeCallbacks } from '../core/types.js';
1
+ import type { ApplicationCallable } from '../core/callable.js';
2
+ import type { ExtensionEvent, RuntimeCallbacks } from '../core/types/runtime.js';
3
+ import type { RillValue } from '../core/types/structures.js';
3
4
  /**
4
5
  * Minimal interface for extension event emission.
5
6
  * Allows emitExtensionEvent to accept any context with callbacks.
@@ -9,27 +10,20 @@ interface RuntimeContextLike {
9
10
  }
10
11
  /**
11
12
  * Result object returned by extension factories.
12
- * Contains host function definitions with optional cleanup and session lifecycle hooks.
13
+ * Contains the mounted RillValue with optional lifecycle hooks.
14
+ * Lifecycle hooks live on the factory result, not on the value dict (DD-1).
13
15
  */
14
- export interface ExtensionResult {
15
- [name: string]: RillFunction | ((...args: never[]) => unknown) | undefined;
16
+ export interface ExtensionFactoryResult {
17
+ readonly value: RillValue;
16
18
  dispose?: () => void | Promise<void>;
17
19
  suspend?: () => unknown;
18
20
  restore?: (state: unknown) => void;
19
21
  }
20
- /**
21
- * Result object returned by hoistExtension.
22
- * Separates functions from dispose for safe createRuntimeContext usage.
23
- */
24
- export interface HoistedExtension {
25
- functions: Record<string, RillFunction>;
26
- dispose?: () => void | Promise<void>;
27
- }
28
22
  /**
29
23
  * Factory function contract for creating extensions.
30
24
  * Accepts typed configuration and returns isolated instance.
31
25
  */
32
- export type ExtensionFactory<TConfig> = (config: TConfig) => ExtensionResult | Promise<ExtensionResult>;
26
+ export type ExtensionFactory<TConfig> = (config: TConfig) => ExtensionFactoryResult | Promise<ExtensionFactoryResult>;
33
27
  /**
34
28
  * Descriptor for a single configuration field in an extension schema.
35
29
  * The secret flag is advisory: harness tooling uses it to mask or omit values.
@@ -72,18 +66,17 @@ export interface ExtensionManifest {
72
66
  * - mounts(): List all configured mounts
73
67
  */
74
68
  export type KvExtensionContract = {
75
- readonly get: RillFunction;
76
- readonly get_or: RillFunction;
77
- readonly set: RillFunction;
78
- readonly merge: RillFunction;
79
- readonly delete: RillFunction;
80
- readonly keys: RillFunction;
81
- readonly has: RillFunction;
82
- readonly clear: RillFunction;
83
- readonly getAll: RillFunction;
84
- readonly schema: RillFunction;
85
- readonly mounts: RillFunction;
86
- readonly dispose?: (() => void | Promise<void>) | undefined;
69
+ readonly get: ApplicationCallable;
70
+ readonly get_or: ApplicationCallable;
71
+ readonly set: ApplicationCallable;
72
+ readonly merge: ApplicationCallable;
73
+ readonly delete: ApplicationCallable;
74
+ readonly keys: ApplicationCallable;
75
+ readonly has: ApplicationCallable;
76
+ readonly clear: ApplicationCallable;
77
+ readonly getAll: ApplicationCallable;
78
+ readonly schema: ApplicationCallable;
79
+ readonly mounts: ApplicationCallable;
87
80
  };
88
81
  /**
89
82
  * Contract type for fs extension implementations.
@@ -104,106 +97,19 @@ export type KvExtensionContract = {
104
97
  * - mounts(): List all configured mounts
105
98
  */
106
99
  export type FsExtensionContract = {
107
- readonly read: RillFunction;
108
- readonly write: RillFunction;
109
- readonly append: RillFunction;
110
- readonly list: RillFunction;
111
- readonly find: RillFunction;
112
- readonly exists: RillFunction;
113
- readonly remove: RillFunction;
114
- readonly stat: RillFunction;
115
- readonly mkdir: RillFunction;
116
- readonly copy: RillFunction;
117
- readonly move: RillFunction;
118
- readonly mounts: RillFunction;
119
- readonly dispose?: (() => void | Promise<void>) | undefined;
120
- };
121
- /**
122
- * Contract type for llm extension implementations.
123
- * Enforces exact function structure for compile-time verification.
124
- *
125
- * Backend implementations must provide all 6 functions:
126
- * - message(text, options): Send single message
127
- * - messages(messages, options): Multi-turn conversation
128
- * - embed(text): Generate embedding vector
129
- * - embed_batch(texts): Batch embeddings
130
- * - tool_loop(prompt, options): Tool use orchestration
131
- * - generate(prompt, options): Structured output extraction
132
- */
133
- export type LlmExtensionContract = {
134
- readonly message: RillFunction;
135
- readonly messages: RillFunction;
136
- readonly embed: RillFunction;
137
- readonly embed_batch: RillFunction;
138
- readonly tool_loop: RillFunction;
139
- readonly generate: RillFunction;
140
- readonly dispose?: (() => void | Promise<void>) | undefined;
141
- };
142
- /**
143
- * Contract type for vector extension implementations.
144
- * Enforces exact function structure for compile-time verification.
145
- *
146
- * Backend implementations must provide all 11 functions:
147
- * - upsert(id, vector, metadata): Insert or update vector
148
- * - upsert_batch(items): Batch insert/update
149
- * - search(vector, options): Search k nearest neighbors
150
- * - get(id): Fetch vector by ID
151
- * - delete(id): Delete vector by ID
152
- * - delete_batch(ids): Batch delete
153
- * - count(): Count vectors in collection
154
- * - create_collection(name, options): Create collection
155
- * - delete_collection(name): Delete collection
156
- * - list_collections(): List all collections
157
- * - describe(): Get collection metadata
158
- */
159
- export type VectorExtensionContract = {
160
- readonly upsert: RillFunction;
161
- readonly upsert_batch: RillFunction;
162
- readonly search: RillFunction;
163
- readonly get: RillFunction;
164
- readonly delete: RillFunction;
165
- readonly delete_batch: RillFunction;
166
- readonly count: RillFunction;
167
- readonly create_collection: RillFunction;
168
- readonly delete_collection: RillFunction;
169
- readonly list_collections: RillFunction;
170
- readonly describe: RillFunction;
171
- readonly dispose?: (() => void | Promise<void>) | undefined;
100
+ readonly read: ApplicationCallable;
101
+ readonly write: ApplicationCallable;
102
+ readonly append: ApplicationCallable;
103
+ readonly list: ApplicationCallable;
104
+ readonly find: ApplicationCallable;
105
+ readonly exists: ApplicationCallable;
106
+ readonly remove: ApplicationCallable;
107
+ readonly stat: ApplicationCallable;
108
+ readonly mkdir: ApplicationCallable;
109
+ readonly copy: ApplicationCallable;
110
+ readonly move: ApplicationCallable;
111
+ readonly mounts: ApplicationCallable;
172
112
  };
173
- /**
174
- * Prefix all function names in an extension with a namespace.
175
- *
176
- * @param namespace - Alphanumeric string with underscores/hyphens (e.g., "fs", "claude_code")
177
- * @param functions - Extension result with function definitions
178
- * @returns New ExtensionResult with prefixed function names (namespace::functionName)
179
- * @throws {RuntimeError} RUNTIME_TYPE_ERROR if namespace is invalid
180
- *
181
- * @example
182
- * ```typescript
183
- * const fs = createFsExtension();
184
- * const prefixed = prefixFunctions("fs", fs);
185
- * // { "fs::read": ..., "fs::write": ..., dispose: ... }
186
- * ```
187
- */
188
- export declare function prefixFunctions(namespace: string, functions: ExtensionResult): ExtensionResult;
189
- /**
190
- * Separate dispose from functions for safe createRuntimeContext usage.
191
- * Wraps prefixFunctions and returns separated structure.
192
- *
193
- * @param namespace - String matching /^[a-zA-Z0-9_-]+$/
194
- * @param extension - Output from extension factory
195
- * @returns Separated functions and dispose handler
196
- * @throws {Error} If namespace is empty
197
- * @throws {Error} If namespace has invalid format
198
- * @throws {TypeError} If extension is null or undefined
199
- *
200
- * @example
201
- * ```typescript
202
- * const { functions, dispose } = hoistExtension('db', dbExtension);
203
- * const ctx = createRuntimeContext({ functions });
204
- * ```
205
- */
206
- export declare function hoistExtension(namespace: string, extension: ExtensionResult): HoistedExtension;
207
113
  /**
208
114
  * Emit an extension event with auto-generated timestamp.
209
115
  * Adds ISO timestamp if event.timestamp is undefined, then calls onLogEvent callback.
@@ -1,96 +1,4 @@
1
- /**
2
- * Prefix all function names in an extension with a namespace.
3
- *
4
- * @param namespace - Alphanumeric string with underscores/hyphens (e.g., "fs", "claude_code")
5
- * @param functions - Extension result with function definitions
6
- * @returns New ExtensionResult with prefixed function names (namespace::functionName)
7
- * @throws {RuntimeError} RUNTIME_TYPE_ERROR if namespace is invalid
8
- *
9
- * @example
10
- * ```typescript
11
- * const fs = createFsExtension();
12
- * const prefixed = prefixFunctions("fs", fs);
13
- * // { "fs::read": ..., "fs::write": ..., dispose: ... }
14
- * ```
15
- */
16
- export function prefixFunctions(namespace, functions) {
17
- // EC-7: Extension not object
18
- if (typeof functions !== 'object' ||
19
- functions === null ||
20
- Array.isArray(functions)) {
21
- throw new TypeError('Extension must be an object');
22
- }
23
- // EC-6: Invalid namespace format
24
- const NAMESPACE_PATTERN = /^[a-zA-Z0-9_-]+$/;
25
- if (!NAMESPACE_PATTERN.test(namespace)) {
26
- throw new Error('Invalid namespace format: must match /^[a-zA-Z0-9_-]+$/');
27
- }
28
- // Create new object with prefixed keys
29
- const result = {};
30
- for (const [name, definition] of Object.entries(functions)) {
31
- // Skip lifecycle hooks during prefixing
32
- if (name === 'dispose' || name === 'suspend' || name === 'restore')
33
- continue;
34
- result[`${namespace}::${name}`] = definition;
35
- }
36
- // Preserve lifecycle hooks if present
37
- const resultWithHooks = result;
38
- if (functions.dispose !== undefined) {
39
- resultWithHooks.dispose = functions.dispose;
40
- }
41
- if (functions.suspend !== undefined) {
42
- resultWithHooks.suspend = functions.suspend;
43
- }
44
- if (functions.restore !== undefined) {
45
- resultWithHooks.restore = functions.restore;
46
- }
47
- return resultWithHooks;
48
- }
49
- /**
50
- * Separate dispose from functions for safe createRuntimeContext usage.
51
- * Wraps prefixFunctions and returns separated structure.
52
- *
53
- * @param namespace - String matching /^[a-zA-Z0-9_-]+$/
54
- * @param extension - Output from extension factory
55
- * @returns Separated functions and dispose handler
56
- * @throws {Error} If namespace is empty
57
- * @throws {Error} If namespace has invalid format
58
- * @throws {TypeError} If extension is null or undefined
59
- *
60
- * @example
61
- * ```typescript
62
- * const { functions, dispose } = hoistExtension('db', dbExtension);
63
- * const ctx = createRuntimeContext({ functions });
64
- * ```
65
- */
66
- export function hoistExtension(namespace, extension) {
67
- // EC-3: Null/undefined extension
68
- if (extension === null || extension === undefined) {
69
- throw new TypeError('Extension cannot be null or undefined');
70
- }
71
- // EC-2: Empty namespace
72
- if (namespace === '') {
73
- throw new Error('Namespace cannot be empty');
74
- }
75
- // EC-1: Invalid namespace format
76
- const NAMESPACE_PATTERN = /^[a-zA-Z0-9_-]+$/;
77
- if (!NAMESPACE_PATTERN.test(namespace)) {
78
- throw new Error('Invalid namespace format: must match /^[a-zA-Z0-9_-]+$/');
79
- }
80
- // Call prefixFunctions internally
81
- const prefixed = prefixFunctions(namespace, extension);
82
- // Extract dispose from result
83
- const { dispose, ...functions } = prefixed;
84
- // Return separated structure
85
- const result = {
86
- functions: functions,
87
- };
88
- // Only add dispose if it exists (exactOptionalPropertyTypes)
89
- if (dispose !== undefined) {
90
- result.dispose = dispose;
91
- }
92
- return result;
93
- }
1
+ import { RuntimeError } from '../../types.js';
94
2
  /**
95
3
  * Emit an extension event with auto-generated timestamp.
96
4
  * Adds ISO timestamp if event.timestamp is undefined, then calls onLogEvent callback.
@@ -119,7 +27,7 @@ export function emitExtensionEvent(ctx, event) {
119
27
  if (!event['event'] ||
120
28
  typeof event['event'] !== 'string' ||
121
29
  event['event'].trim() === '') {
122
- throw new Error('Event must include non-empty event field');
30
+ throw new RuntimeError('RILL-R075', 'Event must include non-empty event field');
123
31
  }
124
32
  // IC-2: Guard for callbacks property (graceful degradation)
125
33
  if ('callbacks' in ctx && ctx.callbacks) {
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Test Context Factory
3
+ *
4
+ * Creates a pre-wired RuntimeContext from extension value maps.
5
+ * Designed for testing and lightweight embedding without rill-config.
6
+ */
7
+ import type { RuntimeContext } from '../core/types/runtime.js';
8
+ import type { RillValue } from '../core/types/structures.js';
9
+ /**
10
+ * Error thrown when extension binding generation fails.
11
+ * Mirrors the ExtensionBindingError in rill-config for core-only usage.
12
+ */
13
+ export declare class ExtensionBindingError extends Error {
14
+ readonly code: "EXTENSION_BINDING";
15
+ constructor(message: string);
16
+ }
17
+ /**
18
+ * Create a RuntimeContext pre-wired with extension values.
19
+ * Builds extension bindings, registers ext and module resolvers,
20
+ * and returns a context ready for execute().
21
+ *
22
+ * @throws {TypeError} when an extension value is undefined (EC-9)
23
+ * @throws {ExtensionBindingError} when binding generation fails (EC-10)
24
+ */
25
+ export declare function createTestContext(extensions: Record<string, {
26
+ value: RillValue;
27
+ dispose?: () => void | Promise<void>;
28
+ }>): RuntimeContext;