mevento 3.0.4 → 4.0.1

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.
package/README.md CHANGED
@@ -1,59 +1,220 @@
1
- # Mevento
1
+ # MEvento TypeScript
2
2
 
3
- Mevento is a tiny VM one single file that handles `MEvento code` executions inside JS engine. `MEvento` is simple programming language that allows developers exposing an app host function, that way they have the ability to dynamically execute simple script that call host function.
3
+ MEvento is a tiny single-file scripting VM for host applications. The host keeps
4
+ control of native behavior by exposing functions, while scripts stored in a
5
+ database or loaded at runtime can compose those functions without rebuilding the
6
+ host app.
4
7
 
5
- The VM uses a AST Walker to execute MEvento script, so of course the performace is not its concern a lot.
8
+ This module is the TypeScript implementation. It provides synchronous
9
+ `MEvento` and async `MEventoAsync` runtimes.
6
10
 
7
- ## MEvento code syntax
8
- Syntaxically MEvento is a c-like language, but very limited: no function declaration, no class, just assignation, function call and conditional check.
11
+ ## v2 Status
12
+
13
+ v2 is a superset of v1 script syntax, with stricter diagnostics and new
14
+ runtime helpers. Existing v1 scripts should still parse and execute in v2.
15
+
16
+ By default, v2 is stricter: unknown host functions raise `unknown_function`
17
+ instead of silently returning `null`. For existing database scripts that depend
18
+ on the v1 behavior, run the VM with `{ compatV1: true }`. In that mode, missing
19
+ host functions return `null` and instance validation does not report them as
20
+ unknown. New scripts should prefer strict mode and wrap risky calls in `_try_`
21
+ when a script should continue after failure.
22
+
23
+ v2 additions include:
24
+
25
+ - Dot property access: `user.name` is equivalent to `user['name']`.
26
+ - `_try_` result capture and helper functions.
27
+ - Function specs with arity, argument type hints, tags, return type hints, and
28
+ optional documentation metadata.
29
+ - Script manifest validation for required host functions, inputs, and outputs.
30
+ - Trace mode and execution step budgets.
31
+ - v1 compatibility mode for existing scripts that call optional host functions.
32
+ - Minimal collection built-ins for arrays and maps.
33
+
34
+ ## Script Syntax
35
+
36
+ MEvento is intentionally small and C-like. It has no user-defined functions and
37
+ no classes. The host application owns the real logic.
38
+
39
+ Supported syntax includes assignments, expressions, host calls, object and
40
+ array literals, bracket and dot access, `if`, `while`, numeric `for`,
41
+ `for ... in`, `break`, `continue`, `return`, `??`, comments, and multilingual
42
+ keyword dictionaries.
43
+
44
+ Example:
9
45
 
10
46
  ```js
11
- a = 12
12
- a = 23
13
- b = functon1()
14
- c = function2()
15
- d = a + b
16
- if(a == b) {
17
- log('a = b')
18
- } else {
19
- log("a != b")
47
+ user = loadUser()
48
+ name = user.profile.name ?? 'anonymous'
49
+
50
+ items = []
51
+ _push_(items, name)
52
+
53
+ if (_len_(items) > 0) {
54
+ log(items[0])
20
55
  }
21
56
  ```
22
57
 
23
- ## How to use
24
- The host application can expose functions through Mevento VM that way:
58
+ ## Built-In Functions
59
+
60
+ Result helpers:
61
+
62
+ ```text
63
+ _try_(expression) # returns {ok, value, error}; evaluates lazily
64
+ _ok_(result)
65
+ _err_(result)
66
+ _value_(result, fallback)
67
+ _error_(result)
68
+ _code_(result)
69
+ _message_(result)
70
+ _unwrap_(result)
71
+ ```
72
+
73
+ Collection helpers:
74
+
75
+ ```text
76
+ _len_(array|object|string)
77
+ _push_(array, value) # mutates array, returns array
78
+ _pop_(array) # mutates array, returns removed value or null
79
+ _insert_(array, index, value)
80
+ _remove_at_(array, index) # mutates array, returns removed value or null
81
+ _has_(object, key)
82
+ _keys_(object)
83
+ _values_(object)
84
+ ```
85
+
86
+ Invalid built-in argument types raise `invalid_argument_type` and can be
87
+ captured with `_try_`.
88
+
89
+ ## Usage
90
+
91
+ Register global functions:
92
+
93
+ ```ts
94
+ import { MEvento } from "mevento";
95
+
96
+ MEvento.register("log", (args) => {
97
+ console.log(...args);
98
+ return null;
99
+ });
100
+
101
+ MEvento.register("add", (args) => Number(args[0]) + Number(args[1]), {
102
+ name: "add",
103
+ description: "Adds two numeric values.",
104
+ minArgs: 2,
105
+ maxArgs: 2,
106
+ args: [
107
+ { name: "left", type: "number", description: "First value." },
108
+ { name: "right", type: "number", description: "Second value." },
109
+ ],
110
+ returnType: "number",
111
+ returnDescription: "The sum of the two values.",
112
+ examples: [{ script: "add(12, 23)", result: 35 }],
113
+ metadata: { category: "math" },
114
+ });
115
+
116
+ const value = MEvento.run("add(12, 23)");
117
+ ```
118
+
119
+ Use one VM instance:
120
+
25
121
  ```ts
26
- import {MEvento} from 'mevento';
27
- MEvento.register('log', (args) => console.log); // exposes console.log through MEvento as log function
28
- MEvento.register('cos2', (args) => Math.cos);
122
+ import { MEvento } from "mevento";
123
+
124
+ const vm = MEvento.newInstance();
125
+
126
+ vm.registerFunction("loadUser", () => ({ profile: { name: "Awa" } }), {
127
+ name: "loadUser",
128
+ maxArgs: 0,
129
+ returnType: "object",
130
+ });
131
+
132
+ const result = vm.execute("loadUser().profile.name");
29
133
  ```
30
134
 
31
- Let's assume you want to execute a `MEvento code`:
135
+ Use async host functions:
32
136
 
33
137
  ```ts
34
- import {MEvento} from 'mevento';
138
+ import { MEventoAsync } from "mevento";
35
139
 
36
- const mevento = MEvento.newInstance();
140
+ const vm = MEventoAsync.newInstance();
37
141
 
38
- mevento.execute(`log("molo")`)
142
+ vm.registerFunction("fetchUser", async () => ({ name: "Awa" }), {
143
+ name: "fetchUser",
144
+ maxArgs: 0,
145
+ returnType: "object",
146
+ });
39
147
 
148
+ const name = await vm.execute("fetchUser().name");
40
149
  ```
41
150
 
42
- `MEvento` instance execution is syncrhrone, that's to say, if you wan to consume `async` function exposed through Mevento, you nee to use `MEventoAsync` instead.
151
+ Inject host input for one run:
152
+
153
+ ```ts
154
+ const total = MEvento.run("base + bonus", false, {
155
+ base: 12,
156
+ bonus: 23,
157
+ });
158
+ ```
159
+
160
+ ## Documenting Host Functions
161
+
162
+ Function and argument specs can carry optional documentation metadata:
163
+ `description`, `returnDescription`, `examples`, and `metadata` on functions,
164
+ plus `description` and `metadata` on arguments. The runtime exposes those fields
165
+ through `capabilities()` for diagnostics, documentation, and UI tooling, but
166
+ they do not change script execution.
167
+
168
+ ## Validation And Manifests
43
169
 
170
+ Function specs drive preflight validation and runtime argument checks:
44
171
 
45
172
  ```ts
46
- import {MEvento} from 'mevento';
173
+ const validation = vm.validate("add(12)");
174
+ if (!validation.ok) {
175
+ console.log(validation.errors);
176
+ }
177
+ ```
178
+
179
+ Manifest validation checks required functions, inputs, and outputs:
180
+
181
+ ```ts
182
+ const manifest = {
183
+ functions: {
184
+ add: { name: "add", minArgs: 2, maxArgs: 2, returnType: "number" },
185
+ },
186
+ inputs: [{ name: "base", type: "number" }],
187
+ outputs: [{ name: "result", type: "number" }],
188
+ };
189
+
190
+ const checked = MEvento.validateManifestSource(
191
+ "result = add(base, 2)",
192
+ manifest,
193
+ );
194
+ ```
195
+
196
+ ## Options: Compatibility, Trace And Budgets
47
197
 
48
- MEvento.register('async', async (args) => await asyncF(args[0]));
49
- const mevento = MEventoAsync.newInstance();
198
+ Use options to enable v1 compatibility for existing scripts, cap execution
199
+ steps, or collect a lightweight trace:
50
200
 
51
- await mevento.execute(`async("molo")`)
201
+ ```ts
202
+ const legacyVm = MEvento.newInstance({ compatV1: true });
203
+ legacyVm.execute("optionalHostFunction()"); // returns null when missing
52
204
 
205
+ const vm = MEvento.newInstance({ maxSteps: 1000, trace: true });
206
+ vm.execute("log('ok')");
207
+
208
+ const trace = vm.trace();
53
209
  ```
54
- `execute` method on `MEventoAsync` instance return a `Promise`.
55
210
 
211
+ ## Development
56
212
 
57
- ## Notes
58
- MEvento does not have scope variables, all variables are visible everywhere.
213
+ ```bash
214
+ npm install
215
+ npm run build
216
+ npm run test:conformance
217
+ ```
59
218
 
219
+ The package currently uses a custom conformance command. Do not use `npm test`
220
+ unless package metadata is updated.
@@ -45,6 +45,7 @@ declare class TokenType {
45
45
  static CONTINUE: number;
46
46
  static nullity: number;
47
47
  static RETURN: number;
48
+ static dot: number;
48
49
  }
49
50
  declare class Token {
50
51
  type: number;
@@ -73,6 +74,106 @@ declare abstract class AST {
73
74
  constructor(line?: number, col?: number);
74
75
  dump(): string;
75
76
  }
77
+ declare class MEventoRuntimeError extends Error {
78
+ line?: number;
79
+ col?: number;
80
+ nodeType?: string;
81
+ cause?: unknown;
82
+ detail: string;
83
+ code: string;
84
+ diagnosticName?: string;
85
+ argCount?: number;
86
+ minArgs?: number;
87
+ maxArgs?: number;
88
+ argIndex?: number;
89
+ expectedType?: string;
90
+ actualType?: string;
91
+ stepCount?: number;
92
+ maxSteps?: number;
93
+ constructor(detail: string, line?: number, col?: number, nodeType?: string, cause?: unknown, diagnostic?: Partial<Omit<MEventoDiagnostic, "message" | "line" | "col" | "node">>);
94
+ static fromNode(node: AST, error: unknown): MEventoRuntimeError;
95
+ private static format;
96
+ diagnostic(): MEventoDiagnostic;
97
+ }
98
+ type MEventoDiagnostic = {
99
+ code: string;
100
+ message: string;
101
+ name?: string;
102
+ line?: number;
103
+ col?: number;
104
+ node?: string;
105
+ argCount?: number;
106
+ minArgs?: number;
107
+ maxArgs?: number;
108
+ argIndex?: number;
109
+ expectedType?: string;
110
+ actualType?: string;
111
+ stepCount?: number;
112
+ maxSteps?: number;
113
+ };
114
+ type MEventoValidationError = MEventoDiagnostic;
115
+ type MEventoValidationResult = {
116
+ ok: boolean;
117
+ errors: MEventoValidationError[];
118
+ };
119
+ type MEventoFunctionSpec = {
120
+ name: string;
121
+ minArgs?: number;
122
+ maxArgs?: number;
123
+ tags?: Iterable<string>;
124
+ args?: MEventoArgSpec[];
125
+ returnType?: string;
126
+ description?: string;
127
+ returnDescription?: string;
128
+ examples?: MEventoFunctionExample[];
129
+ metadata?: {
130
+ [name: string]: unknown;
131
+ };
132
+ };
133
+ type MEventoFunctionExample = {
134
+ title?: string;
135
+ script: string;
136
+ result?: unknown;
137
+ description?: string;
138
+ };
139
+ type MEventoArgSpec = {
140
+ name: string;
141
+ type?: string;
142
+ required?: boolean;
143
+ description?: string;
144
+ metadata?: {
145
+ [name: string]: unknown;
146
+ };
147
+ };
148
+ type MEventoOptions = {
149
+ maxSteps?: number;
150
+ trace?: boolean;
151
+ compatV1?: boolean;
152
+ };
153
+ type MEventoValueSpec = {
154
+ name: string;
155
+ type?: string;
156
+ required?: boolean;
157
+ };
158
+ type MEventoScriptManifest = {
159
+ functions?: MEventoFunctionList;
160
+ inputs?: MEventoValueSpec[];
161
+ outputs?: MEventoValueSpec[];
162
+ };
163
+ type MEventoTraceEvent = {
164
+ kind: string;
165
+ line?: number;
166
+ col?: number;
167
+ node?: string;
168
+ name?: string;
169
+ stepCount?: number;
170
+ detail: {
171
+ [name: string]: unknown;
172
+ };
173
+ };
174
+ type MEventoFunctionList = Set<string> | string[] | MEventoFunctionSpec[] | {
175
+ [name: string]: MEventoFunctionSpec;
176
+ };
76
177
  declare class RootAST extends AST {
77
178
  body: AST[];
78
179
  source: string;
@@ -220,6 +321,7 @@ declare abstract class ANodeVisitor {
220
321
  declare abstract class NodeVisitor extends ANodeVisitor {
221
322
  constructor();
222
323
  visit(node: AST): any;
324
+ protected beforeVisit(_node: AST): void;
223
325
  protected assignProperty(owner: any, property: any, value: any): any;
224
326
  protected abstract visitRootAST(node: RootAST): any;
225
327
  protected abstract visitBlockStatementAST(node: BlockStatementAST): any;
@@ -261,17 +363,43 @@ declare class MEvento extends NodeVisitor {
261
363
  protected currentScope?: MEventScope;
262
364
  debug: boolean;
263
365
  private static _globalFunctionsRegistry;
366
+ private static _globalFunctionSpecs;
264
367
  private static _cache;
265
368
  protected _functionsRegistry: {
266
369
  [k: string]: MEventoFBinding;
267
370
  };
371
+ protected _functionSpecs: {
372
+ [k: string]: MEventoFunctionSpec;
373
+ };
268
374
  protected _functionResolver?: (name: string) => MEventoFBinding | undefined;
269
- constructor();
375
+ protected _options: MEventoOptions;
376
+ private _executionStepCount;
377
+ private _traceEvents;
378
+ constructor(options?: MEventoOptions);
379
+ get options(): MEventoOptions;
380
+ get executionStepCount(): number;
381
+ trace(): MEventoTraceEvent[];
382
+ protected resetExecutionBudget(): void;
383
+ protected beforeVisit(node: AST): void;
384
+ protected clearTrace(): void;
385
+ protected recordTrace(kind: string, node: AST, name?: string, detail?: {
386
+ [name: string]: unknown;
387
+ }): void;
270
388
  protected resolve(name: string): any;
271
389
  protected changeVariable(name: string, value: any): any;
272
390
  protected pushScope(name: string): void;
273
391
  protected popScope(): void;
274
392
  protected log(message: any): void;
393
+ protected successResult(value: any): {
394
+ ok: boolean;
395
+ value: any;
396
+ error: any;
397
+ };
398
+ protected errorResult(error: MEventoRuntimeError): {
399
+ ok: boolean;
400
+ value: any;
401
+ error: MEventoDiagnostic;
402
+ };
275
403
  protected visitRootAST(node: RootAST): any;
276
404
  protected visitBlockStatementAST(node: BlockStatementAST): any;
277
405
  protected visitIdentifierAST(node: IdentifierAST): any;
@@ -297,23 +425,54 @@ declare class MEvento extends NodeVisitor {
297
425
  resolveArguments(args: AST[]): any;
298
426
  setFunctionResolver(resolver: (name: string) => MEventoFBinding | undefined): void;
299
427
  resolveFunction(name: string): MEventoFBinding | undefined;
300
- registerFunction(id: string, fn: MEventoFBinding): void;
428
+ resolveFunctionSpec(name: string): MEventoFunctionSpec | undefined;
429
+ capabilities(): {
430
+ [name: string]: MEventoFunctionSpec;
431
+ };
432
+ registerFunction(id: string, fn: MEventoFBinding, spec?: MEventoFunctionSpec): void;
301
433
  unregisterFunction(id: string): void;
434
+ validate(source: string, functions?: MEventoFunctionList, cache?: boolean): MEventoValidationResult;
435
+ validateManifest(source: string, manifest: MEventoScriptManifest, cache?: boolean): MEventoValidationResult;
436
+ protected validationFunctionSpecs(functions?: MEventoFunctionList): Map<string, MEventoFunctionSpec>;
437
+ protected validateNode(node: AST | null | undefined, knownFunctions: Map<string, MEventoFunctionSpec>, errors: MEventoValidationError[], protectedByTry?: boolean): void;
438
+ protected validateCallExpression(node: CallExpressionAST, knownFunctions: Map<string, MEventoFunctionSpec>, errors: MEventoValidationError[], protectedByTry: boolean): void;
439
+ protected validateStaticArgumentTypes(node: CallExpressionAST, spec: MEventoFunctionSpec, errors: MEventoValidationError[]): void;
440
+ protected staticArgumentType(node: AST): string | undefined;
441
+ protected argumentTypeMatches(spec: MEventoArgSpec, actualType: string): boolean;
442
+ protected validateManifestNode(module: AST, manifest: MEventoScriptManifest, errors: MEventoValidationError[]): void;
443
+ protected analyzeManifestNode(node: AST | null | undefined, analysis: {
444
+ knownInputs: Set<string>;
445
+ assigned: Set<string>;
446
+ assignedTypes: Map<string, string>;
447
+ reportedInputs: Set<string>;
448
+ }, errors: MEventoValidationError[]): void;
449
+ protected markAssignedTarget(node: AST, assignedType: string | undefined, analysis: {
450
+ assigned: Set<string>;
451
+ assignedTypes: Map<string, string>;
452
+ }): void;
453
+ protected valueSpecTypeMatches(spec: MEventoValueSpec, actualType: string): boolean;
454
+ protected validationError(code: string, node: AST, message: string, name?: string, diagnostic?: Pick<MEventoDiagnostic, "argCount" | "minArgs" | "maxArgs" | "argIndex" | "expectedType" | "actualType">): MEventoValidationError;
455
+ protected validateRuntimeArgumentTypes(node: AST, spec: MEventoFunctionSpec, values: unknown[]): void;
302
456
  execute(source: string, cache?: boolean, input?: {
303
457
  [k: string]: any;
304
458
  }): any;
305
459
  static compile(source: string, cache?: boolean): AST;
306
- static register(id: string, fn: MEventoFBinding): void;
460
+ static register(id: string, fn: MEventoFBinding, spec?: MEventoFunctionSpec): void;
307
461
  static unregister(id: string): void;
462
+ static capabilities(): {
463
+ [name: string]: MEventoFunctionSpec;
464
+ };
465
+ static validateSource(source: string, functions: MEventoFunctionList, cache?: boolean): MEventoValidationResult;
466
+ static validateManifestSource(source: string, manifest: MEventoScriptManifest, cache?: boolean): MEventoValidationResult;
308
467
  static run(source: string, cache?: boolean, input?: {
309
468
  [k: string]: any;
310
- }): any;
311
- static newInstance(): MEvento;
469
+ }, options?: MEventoOptions): any;
470
+ static newInstance(options?: MEventoOptions): MEvento;
312
471
  clone(): MEvento;
313
472
  newAsyncInstance(): MEventoAsync;
314
473
  }
315
474
  declare class MEventoAsync extends MEvento {
316
- constructor();
475
+ constructor(options?: MEventoOptions);
317
476
  protected visitRootAST(node: RootAST): Promise<any>;
318
477
  protected visitBlockStatementAST(node: BlockStatementAST): Promise<any>;
319
478
  protected visitIdentifierAST(node: AST): Promise<any>;
@@ -334,16 +493,16 @@ declare class MEventoAsync extends MEvento {
334
493
  protected visitForOfStatement(node: ForOfStatement): Promise<any[] | ReturnBranch>;
335
494
  protected visitReturnAST(node: ReturnAST): Promise<any>;
336
495
  resolveArgumentsAsync(args: AST[]): Promise<any[]>;
337
- registerFunction(id: string, fn: MEventoFBinding): void;
496
+ registerFunction(id: string, fn: MEventoFBinding, spec?: MEventoFunctionSpec): void;
338
497
  unregisterFunction(id: string): void;
339
498
  execute(source: string, cache?: boolean, input?: {
340
499
  [k: string]: any;
341
500
  }): Promise<any>;
342
501
  static run(source: string, cache?: boolean, input?: {
343
502
  [k: string]: any;
344
- }): Promise<any>;
345
- static newInstance(): MEventoAsync;
503
+ }, options?: MEventoOptions): Promise<any>;
504
+ static newInstance(options?: MEventoOptions): MEventoAsync;
346
505
  clone(): MEventoAsync;
347
506
  }
348
507
 
349
- export { AST, ArrayExpression, AssignmentExpressionAST, BinaryExpressionAST, BlockStatementAST, BreakAST, BreakBranch, CallExpressionAST, ContinueAST, ContinueBranch, ExpressionStatementAST, ForLoopStatement, ForOfStatement, IdentifierAST, IfStatementAST, IndexAccessorAST, LexerDictionary, LiteralAST, LogicalExpressionAST, LoopControl, MEventScope, MEvento, MEventoAsync, type MEventoFBinding, NodeVisitor, ObjectExpression, ObjectProperty, ReturnAST, ReturnBranch, RootAST, Token, TokenType, TupleExpression, UnaryExpressionAST, WhileLoopStatement };
508
+ export { AST, ArrayExpression, AssignmentExpressionAST, BinaryExpressionAST, BlockStatementAST, BreakAST, BreakBranch, CallExpressionAST, ContinueAST, ContinueBranch, ExpressionStatementAST, ForLoopStatement, ForOfStatement, IdentifierAST, IfStatementAST, IndexAccessorAST, LexerDictionary, LiteralAST, LogicalExpressionAST, LoopControl, MEventScope, MEvento, type MEventoArgSpec, MEventoAsync, type MEventoDiagnostic, type MEventoFBinding, type MEventoFunctionExample, type MEventoFunctionList, type MEventoFunctionSpec, type MEventoOptions, MEventoRuntimeError, type MEventoScriptManifest, type MEventoTraceEvent, type MEventoValidationError, type MEventoValidationResult, type MEventoValueSpec, NodeVisitor, ObjectExpression, ObjectProperty, ReturnAST, ReturnBranch, RootAST, Token, TokenType, TupleExpression, UnaryExpressionAST, WhileLoopStatement };