@al8b/vm 0.1.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.
- package/README.md +25 -0
- package/dist/context.d.mts +16 -0
- package/dist/context.d.ts +16 -0
- package/dist/context.js +98 -0
- package/dist/context.js.map +1 -0
- package/dist/context.mjs +74 -0
- package/dist/context.mjs.map +1 -0
- package/dist/extensions.d.mts +19 -0
- package/dist/extensions.d.ts +19 -0
- package/dist/extensions.js +80 -0
- package/dist/extensions.js.map +1 -0
- package/dist/extensions.mjs +57 -0
- package/dist/extensions.mjs.map +1 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +313 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +283 -0
- package/dist/index.mjs.map +1 -0
- package/dist/l8bvm.d.mts +77 -0
- package/dist/l8bvm.d.ts +77 -0
- package/dist/l8bvm.js +293 -0
- package/dist/l8bvm.js.map +1 -0
- package/dist/l8bvm.mjs +270 -0
- package/dist/l8bvm.mjs.map +1 -0
- package/dist/types/index.d.mts +196 -0
- package/dist/types/index.d.ts +196 -0
- package/dist/types/index.js +19 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/index.mjs +1 -0
- package/dist/types/index.mjs.map +1 -0
- package/package.json +38 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/index.ts
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
|
+
L8BVM: () => L8BVM,
|
|
25
|
+
Random: () => import_lootiscript3.Random,
|
|
26
|
+
Routine: () => import_lootiscript3.Routine,
|
|
27
|
+
createMetaFunctions: () => createMetaFunctions,
|
|
28
|
+
createVMContext: () => createVMContext,
|
|
29
|
+
setupArrayExtensions: () => setupArrayExtensions
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(index_exports);
|
|
32
|
+
|
|
33
|
+
// src/context.ts
|
|
34
|
+
var import_lootiscript = require("@al8b/lootiscript");
|
|
35
|
+
var _baseMeta = {
|
|
36
|
+
round: /* @__PURE__ */ __name((x) => Math.round(x), "round"),
|
|
37
|
+
floor: /* @__PURE__ */ __name((x) => Math.floor(x), "floor"),
|
|
38
|
+
ceil: /* @__PURE__ */ __name((x) => Math.ceil(x), "ceil"),
|
|
39
|
+
abs: /* @__PURE__ */ __name((x) => Math.abs(x), "abs"),
|
|
40
|
+
min: /* @__PURE__ */ __name((x, y) => Math.min(x, y), "min"),
|
|
41
|
+
max: /* @__PURE__ */ __name((x, y) => Math.max(x, y), "max"),
|
|
42
|
+
sqrt: /* @__PURE__ */ __name((x) => Math.sqrt(x), "sqrt"),
|
|
43
|
+
pow: /* @__PURE__ */ __name((x, y) => x ** y, "pow"),
|
|
44
|
+
sin: /* @__PURE__ */ __name((x) => Math.sin(x), "sin"),
|
|
45
|
+
cos: /* @__PURE__ */ __name((x) => Math.cos(x), "cos"),
|
|
46
|
+
tan: /* @__PURE__ */ __name((x) => Math.tan(x), "tan"),
|
|
47
|
+
asin: /* @__PURE__ */ __name((x) => Math.asin(x), "asin"),
|
|
48
|
+
acos: /* @__PURE__ */ __name((x) => Math.acos(x), "acos"),
|
|
49
|
+
atan: /* @__PURE__ */ __name((x) => Math.atan(x), "atan"),
|
|
50
|
+
atan2: /* @__PURE__ */ __name((y, x) => Math.atan2(y, x), "atan2"),
|
|
51
|
+
sind: /* @__PURE__ */ __name((x) => Math.sin(x / 180 * Math.PI), "sind"),
|
|
52
|
+
cosd: /* @__PURE__ */ __name((x) => Math.cos(x / 180 * Math.PI), "cosd"),
|
|
53
|
+
tand: /* @__PURE__ */ __name((x) => Math.tan(x / 180 * Math.PI), "tand"),
|
|
54
|
+
asind: /* @__PURE__ */ __name((x) => Math.asin(x) * 180 / Math.PI, "asind"),
|
|
55
|
+
acosd: /* @__PURE__ */ __name((x) => Math.acos(x) * 180 / Math.PI, "acosd"),
|
|
56
|
+
atand: /* @__PURE__ */ __name((x) => Math.atan(x) * 180 / Math.PI, "atand"),
|
|
57
|
+
atan2d: /* @__PURE__ */ __name((y, x) => Math.atan2(y, x) * 180 / Math.PI, "atan2d"),
|
|
58
|
+
log: /* @__PURE__ */ __name((x) => Math.log(x), "log"),
|
|
59
|
+
exp: /* @__PURE__ */ __name((x) => Math.exp(x), "exp"),
|
|
60
|
+
random: new import_lootiscript.Random(0),
|
|
61
|
+
PI: Math.PI,
|
|
62
|
+
true: 1,
|
|
63
|
+
false: 0
|
|
64
|
+
};
|
|
65
|
+
var _defaultPrint = /* @__PURE__ */ __name((text) => console.log(text), "_defaultPrint");
|
|
66
|
+
function createMetaFunctions(customPrint) {
|
|
67
|
+
return {
|
|
68
|
+
..._baseMeta,
|
|
69
|
+
print: customPrint ?? _defaultPrint
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
__name(createMetaFunctions, "createMetaFunctions");
|
|
73
|
+
function createVMContext(meta, global) {
|
|
74
|
+
const fullMeta = {
|
|
75
|
+
..._baseMeta,
|
|
76
|
+
print: meta.print ?? _defaultPrint,
|
|
77
|
+
...meta
|
|
78
|
+
};
|
|
79
|
+
return {
|
|
80
|
+
meta: fullMeta,
|
|
81
|
+
global,
|
|
82
|
+
local: global,
|
|
83
|
+
object: global,
|
|
84
|
+
breakable: 0,
|
|
85
|
+
continuable: 0,
|
|
86
|
+
returnable: 0,
|
|
87
|
+
stack_size: 0,
|
|
88
|
+
timeout: Date.now() + 3e3,
|
|
89
|
+
warnings: {
|
|
90
|
+
using_undefined_variable: {},
|
|
91
|
+
assigning_field_to_undefined: {},
|
|
92
|
+
invoking_non_function: {},
|
|
93
|
+
assigning_api_variable: {},
|
|
94
|
+
assignment_as_condition: {}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
__name(createVMContext, "createVMContext");
|
|
99
|
+
|
|
100
|
+
// src/extensions.ts
|
|
101
|
+
function setupArrayExtensions() {
|
|
102
|
+
if (!Array.prototype.insert) {
|
|
103
|
+
Array.prototype.insert = function(element) {
|
|
104
|
+
this.splice(0, 0, element);
|
|
105
|
+
return element;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (!Array.prototype.insertAt) {
|
|
109
|
+
Array.prototype.insertAt = function(element, index) {
|
|
110
|
+
if (index >= 0 && index < this.length) {
|
|
111
|
+
this.splice(index, 0, element);
|
|
112
|
+
} else {
|
|
113
|
+
this.push(element);
|
|
114
|
+
}
|
|
115
|
+
return element;
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (!Array.prototype.remove) {
|
|
119
|
+
Array.prototype.remove = function(index) {
|
|
120
|
+
if (index >= 0 && index < this.length) {
|
|
121
|
+
return this.splice(index, 1)[0];
|
|
122
|
+
}
|
|
123
|
+
return 0;
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (!Array.prototype.removeElement) {
|
|
127
|
+
Array.prototype.removeElement = function(element) {
|
|
128
|
+
const index = this.indexOf(element);
|
|
129
|
+
if (index >= 0) {
|
|
130
|
+
return this.splice(index, 1)[0];
|
|
131
|
+
}
|
|
132
|
+
return 0;
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (!Array.prototype.contains) {
|
|
136
|
+
Array.prototype.contains = function(element) {
|
|
137
|
+
return this.indexOf(element) >= 0 ? 1 : 0;
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
if (!Array.prototype.sortList) {
|
|
141
|
+
Array.prototype.sortList = function(fn) {
|
|
142
|
+
if (fn) {
|
|
143
|
+
return this.sort(fn);
|
|
144
|
+
}
|
|
145
|
+
return this.sort();
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
__name(setupArrayExtensions, "setupArrayExtensions");
|
|
150
|
+
|
|
151
|
+
// src/l8bvm.ts
|
|
152
|
+
var import_io = require("@al8b/io");
|
|
153
|
+
var import_lootiscript2 = require("@al8b/lootiscript");
|
|
154
|
+
function extractErrorInfo(err, fallbackFile, fallbackType, runner) {
|
|
155
|
+
const errorMessage = typeof err === "object" && err !== null && "error" in err && typeof err.error === "string" ? err.error : err?.message ?? String(err);
|
|
156
|
+
let stackTrace = err?.stackTrace;
|
|
157
|
+
if (!stackTrace && runner?.main_thread?.processor?.generateStackTrace) {
|
|
158
|
+
stackTrace = runner.main_thread.processor.generateStackTrace();
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
error: errorMessage,
|
|
162
|
+
type: err?.type ?? fallbackType,
|
|
163
|
+
line: err?.line,
|
|
164
|
+
column: err?.column,
|
|
165
|
+
file: err?.file ?? fallbackFile,
|
|
166
|
+
stack: err?.stack,
|
|
167
|
+
stackTrace
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
__name(extractErrorInfo, "extractErrorInfo");
|
|
171
|
+
var L8BVM = class {
|
|
172
|
+
static {
|
|
173
|
+
__name(this, "L8BVM");
|
|
174
|
+
}
|
|
175
|
+
context;
|
|
176
|
+
runner;
|
|
177
|
+
storage_service;
|
|
178
|
+
error_info = null;
|
|
179
|
+
constructor(meta = {}, global = {}, namespace = "/l8b", preserve_ls = false) {
|
|
180
|
+
this.context = createVMContext(meta, global);
|
|
181
|
+
this.storage_service = new import_io.StorageService(namespace, preserve_ls);
|
|
182
|
+
this.context.global.storage = this.storage_service.getInterface();
|
|
183
|
+
this.runner = new import_lootiscript2.Runner(this);
|
|
184
|
+
this.runner.init();
|
|
185
|
+
setupArrayExtensions();
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Run source code
|
|
189
|
+
*
|
|
190
|
+
* Compiles and executes a string of LootiScript code.
|
|
191
|
+
*
|
|
192
|
+
* @param {string} source - The source code to execute
|
|
193
|
+
* @param {number} [timeout=3000] - Execution time limit in ms
|
|
194
|
+
* @param {string} [filename=""] - Filename for error reporting
|
|
195
|
+
* @returns {any} The result of the last statement execution
|
|
196
|
+
* @throws {ErrorInfo} If compilation or execution fails
|
|
197
|
+
*/
|
|
198
|
+
run(source, timeout = 3e3, filename = "") {
|
|
199
|
+
this.error_info = null;
|
|
200
|
+
this.context.timeout = Date.now() + timeout;
|
|
201
|
+
this.context.stack_size = 0;
|
|
202
|
+
try {
|
|
203
|
+
const result = this.runner.run(source, filename);
|
|
204
|
+
this.storage_service.check();
|
|
205
|
+
if (result !== null && result !== void 0) {
|
|
206
|
+
return this.runner.toString(result);
|
|
207
|
+
}
|
|
208
|
+
return null;
|
|
209
|
+
} catch (err) {
|
|
210
|
+
this.error_info = extractErrorInfo(err, filename, "runtime", this.runner);
|
|
211
|
+
throw err;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Call a global function
|
|
216
|
+
*
|
|
217
|
+
* Executes a specific function defined in the global scope.
|
|
218
|
+
* Useful for game loop hooks (update, draw) or event handlers.
|
|
219
|
+
*
|
|
220
|
+
* @param {string} name - Name of the function to call
|
|
221
|
+
* @param {any[]} [args=[]] - Arguments to pass to the function
|
|
222
|
+
* @param {number} [timeout=3000] - Execution time limit in ms
|
|
223
|
+
* @returns {any} The return value of the function
|
|
224
|
+
* @throws {ErrorInfo} If the function doesn't exist or execution fails
|
|
225
|
+
*/
|
|
226
|
+
call(name, args = [], timeout = 3e3) {
|
|
227
|
+
this.error_info = null;
|
|
228
|
+
this.context.timeout = Date.now() + timeout;
|
|
229
|
+
this.context.stack_size = 0;
|
|
230
|
+
try {
|
|
231
|
+
const result = this.runner.call(name, ...args);
|
|
232
|
+
this.storage_service.check();
|
|
233
|
+
return result;
|
|
234
|
+
} catch (err) {
|
|
235
|
+
this.error_info = extractErrorInfo(err, name, "call", this.runner);
|
|
236
|
+
throw err;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Load a pre-compiled routine (for production builds)
|
|
241
|
+
*
|
|
242
|
+
* Loads bytecode directly into the VM, bypassing the compilation step.
|
|
243
|
+
* Used in production to improve startup time and obfuscate source.
|
|
244
|
+
*
|
|
245
|
+
* @param {any} routineData - Either a Routine instance or serialized JSON
|
|
246
|
+
* @param {string} [filename=""] - Name of the file for error reporting
|
|
247
|
+
* @throws {ErrorInfo} If loading fails
|
|
248
|
+
*/
|
|
249
|
+
loadRoutine(routineData, filename = "") {
|
|
250
|
+
this.error_info = null;
|
|
251
|
+
try {
|
|
252
|
+
let routine;
|
|
253
|
+
if (routineData instanceof import_lootiscript2.Routine) {
|
|
254
|
+
routine = routineData;
|
|
255
|
+
} else {
|
|
256
|
+
routine = new import_lootiscript2.Routine(0).import(normalizeSerializedRoutine(routineData));
|
|
257
|
+
}
|
|
258
|
+
this.runner.main_thread.addCall(routine);
|
|
259
|
+
this.runner.tick();
|
|
260
|
+
} catch (err) {
|
|
261
|
+
this.error_info = extractErrorInfo(err, filename, "compile");
|
|
262
|
+
throw err;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Clear warnings
|
|
267
|
+
*/
|
|
268
|
+
clearWarnings() {
|
|
269
|
+
this.context.warnings = {
|
|
270
|
+
using_undefined_variable: {},
|
|
271
|
+
assigning_field_to_undefined: {},
|
|
272
|
+
invoking_non_function: {},
|
|
273
|
+
assigning_api_variable: {},
|
|
274
|
+
assignment_as_condition: {}
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Get warnings
|
|
279
|
+
*/
|
|
280
|
+
getWarnings() {
|
|
281
|
+
return this.context.warnings || {};
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Convert value to string (for printing)
|
|
285
|
+
*/
|
|
286
|
+
toString(value) {
|
|
287
|
+
return this.runner.toString(value);
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
function normalizeSerializedRoutine(routineData) {
|
|
291
|
+
if (isCompiledModuleArtifact(routineData)) {
|
|
292
|
+
return routineData.routine;
|
|
293
|
+
}
|
|
294
|
+
return routineData;
|
|
295
|
+
}
|
|
296
|
+
__name(normalizeSerializedRoutine, "normalizeSerializedRoutine");
|
|
297
|
+
function isCompiledModuleArtifact(value) {
|
|
298
|
+
return typeof value === "object" && value !== null && "format" in value && value.format === "l8b-compiled-routine" && "routine" in value;
|
|
299
|
+
}
|
|
300
|
+
__name(isCompiledModuleArtifact, "isCompiledModuleArtifact");
|
|
301
|
+
|
|
302
|
+
// src/index.ts
|
|
303
|
+
var import_lootiscript3 = require("@al8b/lootiscript");
|
|
304
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
305
|
+
0 && (module.exports = {
|
|
306
|
+
L8BVM,
|
|
307
|
+
Random,
|
|
308
|
+
Routine,
|
|
309
|
+
createMetaFunctions,
|
|
310
|
+
createVMContext,
|
|
311
|
+
setupArrayExtensions
|
|
312
|
+
});
|
|
313
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/context.ts","../src/extensions.ts","../src/l8bvm.ts"],"sourcesContent":["// VM public surface — StorageService is intentionally NOT re-exported here.\n// Import it directly from \"@al8b/io\" if you need it.\nexport { createMetaFunctions, createVMContext } from \"./context\";\nexport { setupArrayExtensions } from \"./extensions\";\nexport { L8BVM } from \"./l8bvm\";\nexport type {\n\tErrorInfo,\n\tGlobalAPI,\n\tMetaFunctions,\n\tPlayerAPI,\n\tSystemAPI,\n\tVMContext,\n\tVMWarnings,\n\tWarningInfo,\n} from \"./types\";\n\n// Re-export language primitives that runtime and host code legitimately need.\n// This keeps runtime from bypassing the vm layer and importing lootiscript directly.\nexport { Random, Routine } from \"@al8b/lootiscript\";\n","/**\n * VM Context creation utilities\n */\n\nimport { Random } from \"@al8b/lootiscript\";\nimport type { GlobalAPI, MetaFunctions, VMContext } from \"./types\";\n\n// Shared math lambdas — allocated once, reused across all VM instances\nconst _baseMeta = {\n\tround: (x: number) => Math.round(x),\n\tfloor: (x: number) => Math.floor(x),\n\tceil: (x: number) => Math.ceil(x),\n\tabs: (x: number) => Math.abs(x),\n\tmin: (x: number, y: number) => Math.min(x, y),\n\tmax: (x: number, y: number) => Math.max(x, y),\n\tsqrt: (x: number) => Math.sqrt(x),\n\tpow: (x: number, y: number) => x ** y,\n\tsin: (x: number) => Math.sin(x),\n\tcos: (x: number) => Math.cos(x),\n\ttan: (x: number) => Math.tan(x),\n\tasin: (x: number) => Math.asin(x),\n\tacos: (x: number) => Math.acos(x),\n\tatan: (x: number) => Math.atan(x),\n\tatan2: (y: number, x: number) => Math.atan2(y, x),\n\tsind: (x: number) => Math.sin((x / 180) * Math.PI),\n\tcosd: (x: number) => Math.cos((x / 180) * Math.PI),\n\ttand: (x: number) => Math.tan((x / 180) * Math.PI),\n\tasind: (x: number) => (Math.asin(x) * 180) / Math.PI,\n\tacosd: (x: number) => (Math.acos(x) * 180) / Math.PI,\n\tatand: (x: number) => (Math.atan(x) * 180) / Math.PI,\n\tatan2d: (y: number, x: number) => (Math.atan2(y, x) * 180) / Math.PI,\n\tlog: (x: number) => Math.log(x),\n\texp: (x: number) => Math.exp(x),\n\trandom: new Random(0),\n\tPI: Math.PI,\n\ttrue: 1 as const,\n\tfalse: 0 as const,\n};\n\nconst _defaultPrint = (text: any) => console.log(text);\n\n/**\n * Create meta functions (built-in functions)\n */\nexport function createMetaFunctions(customPrint?: (text: any) => void): MetaFunctions {\n\treturn {\n\t\t..._baseMeta,\n\t\tprint: customPrint ?? _defaultPrint,\n\t};\n}\n\n/**\n * Create VM context\n */\nexport function createVMContext(meta: Partial<MetaFunctions>, global: Partial<GlobalAPI>): VMContext {\n\tconst fullMeta: MetaFunctions = {\n\t\t..._baseMeta,\n\t\tprint: meta.print ?? _defaultPrint,\n\t\t...meta,\n\t};\n\n\treturn {\n\t\tmeta: fullMeta as MetaFunctions,\n\t\tglobal: global as GlobalAPI,\n\t\tlocal: global,\n\t\tobject: global,\n\t\tbreakable: 0,\n\t\tcontinuable: 0,\n\t\treturnable: 0,\n\t\tstack_size: 0,\n\t\ttimeout: Date.now() + 3000,\n\t\twarnings: {\n\t\t\tusing_undefined_variable: {},\n\t\t\tassigning_field_to_undefined: {},\n\t\t\tinvoking_non_function: {},\n\t\t\tassigning_api_variable: {},\n\t\t\tassignment_as_condition: {},\n\t\t},\n\t};\n}\n","/**\n * Array extensions\n */\n\n/**\n * Setup array extensions\n */\nexport function setupArrayExtensions(): void {\n\t// Insert element at beginning of array (returns the inserted element)\n\tif (!Array.prototype.insert) {\n\t\tArray.prototype.insert = function (element: any) {\n\t\t\tthis.splice(0, 0, element);\n\t\t\treturn element;\n\t\t};\n\t}\n\n\t// Insert element at specific index (returns the inserted element)\n\tif (!Array.prototype.insertAt) {\n\t\tArray.prototype.insertAt = function (element: any, index: number) {\n\t\t\tif (index >= 0 && index < this.length) {\n\t\t\t\tthis.splice(index, 0, element);\n\t\t\t} else {\n\t\t\t\tthis.push(element);\n\t\t\t}\n\t\t\treturn element;\n\t\t};\n\t}\n\n\t// Remove element at index (returns removed element or 0 if out of bounds)\n\tif (!Array.prototype.remove) {\n\t\tArray.prototype.remove = function (index: number) {\n\t\t\tif (index >= 0 && index < this.length) {\n\t\t\t\treturn this.splice(index, 1)[0];\n\t\t\t}\n\t\t\treturn 0;\n\t\t};\n\t}\n\n\t// Remove first occurrence of element (returns removed element or 0 if not found)\n\tif (!Array.prototype.removeElement) {\n\t\tArray.prototype.removeElement = function (element: any) {\n\t\t\tconst index = this.indexOf(element);\n\t\t\tif (index >= 0) {\n\t\t\t\treturn this.splice(index, 1)[0];\n\t\t\t}\n\t\t\treturn 0;\n\t\t};\n\t}\n\n\t// Check if array contains element (returns 1 if found, 0 if not found)\n\tif (!Array.prototype.contains) {\n\t\tArray.prototype.contains = function (element: any) {\n\t\t\treturn this.indexOf(element) >= 0 ? 1 : 0;\n\t\t};\n\t}\n\n\t// Sort array with optional comparator function\n\tif (!Array.prototype.sortList) {\n\t\tArray.prototype.sortList = function (fn?: (a: any, b: any) => number) {\n\t\t\tif (fn) {\n\t\t\t\treturn this.sort(fn);\n\t\t\t}\n\t\t\treturn this.sort();\n\t\t};\n\t}\n}\n\n// TypeScript type declarations for Array prototype extensions\ndeclare global {\n\tinterface Array<T> {\n\t\tinsert(element: T): T;\n\t\tinsertAt(element: T, index: number): T;\n\t\tremove(index: number): T | 0;\n\t\tremoveElement(element: T): T | 0;\n\t\tcontains(element: T): 0 | 1;\n\t\tsortList(fn?: (a: T, b: T) => number): T[];\n\t}\n}\n","/**\n * L8BVM - Virtual Machine wrapper for lootiscript\n *\n * High-level interface for the LootiScript virtual machine.\n * Wraps the core Compiler, Processor, and Runner into a usable engine component.\n * Handles context creation, storage persistence, and error formatting.\n *\n * Responsibilities:\n * - Create and manage VM execution context\n * - Provide clean API for running code and calling functions\n * - Handle storage persistence\n * - Format and normalize runtime errors\n *\n * @module vm\n */\n\nimport type { CompiledModuleArtifact, SerializedRoutineData } from \"@al8b/framework-shared\";\nimport { StorageService } from \"@al8b/io\";\nimport { Routine, Runner } from \"@al8b/lootiscript\";\nimport { createVMContext } from \"./context\";\nimport { setupArrayExtensions } from \"./extensions\";\nimport type { ErrorInfo, GlobalAPI, MetaFunctions, VMContext } from \"./types\";\n\n/**\n * Extract normalized ErrorInfo from a caught exception.\n * Centralizes the error-parsing logic shared across run(), call(), and loadRoutine().\n */\nfunction extractErrorInfo(err: any, fallbackFile: string, fallbackType: string, runner?: Runner): ErrorInfo {\n\tconst errorMessage =\n\t\ttypeof err === \"object\" && err !== null && \"error\" in err && typeof err.error === \"string\"\n\t\t\t? err.error\n\t\t\t: err?.message ?? String(err);\n\n\tlet stackTrace = err?.stackTrace;\n\tif (!stackTrace && (runner as any)?.main_thread?.processor?.generateStackTrace) {\n\t\tstackTrace = (runner as any).main_thread.processor.generateStackTrace();\n\t}\n\n\treturn {\n\t\terror: errorMessage,\n\t\ttype: err?.type ?? fallbackType,\n\t\tline: err?.line,\n\t\tcolumn: err?.column,\n\t\tfile: err?.file ?? fallbackFile,\n\t\tstack: err?.stack,\n\t\tstackTrace,\n\t};\n}\n\nexport class L8BVM {\n\tpublic context: VMContext;\n\tpublic runner: Runner;\n\tpublic storage_service: StorageService;\n\tpublic error_info: ErrorInfo | null = null;\n\n\tconstructor(\n\t\tmeta: Partial<MetaFunctions> = {},\n\t\tglobal: Partial<GlobalAPI> = {},\n\t\tnamespace = \"/l8b\",\n\t\tpreserve_ls = false,\n\t) {\n\t\t// Initialize VM execution context with meta functions and global API\n\t\tthis.context = createVMContext(meta, global);\n\n\t\t// Initialize storage service for persistent data (localStorage/sessionStorage)\n\t\tthis.storage_service = new StorageService(namespace, preserve_ls);\n\n\t\t// Inject storage API into global scope for LootiScript access\n\t\tthis.context.global.storage = this.storage_service.getInterface();\n\n\t\t// Create Runner instance with reference to this VM for bidirectional communication\n\t\tthis.runner = new Runner(this as any);\n\n\t\t// Initialize Runner and create main execution thread\n\t\tthis.runner.init();\n\n\t\t// Add custom array methods to Array.prototype for LootiScript\n\t\tsetupArrayExtensions();\n\t}\n\n\t/**\n\t * Run source code\n\t *\n\t * Compiles and executes a string of LootiScript code.\n\t *\n\t * @param {string} source - The source code to execute\n\t * @param {number} [timeout=3000] - Execution time limit in ms\n\t * @param {string} [filename=\"\"] - Filename for error reporting\n\t * @returns {any} The result of the last statement execution\n\t * @throws {ErrorInfo} If compilation or execution fails\n\t */\n\trun(source: string, timeout = 3000, filename = \"\"): any {\n\t\tthis.error_info = null;\n\t\tthis.context.timeout = Date.now() + timeout;\n\t\tthis.context.stack_size = 0;\n\n\t\ttry {\n\t\t\tconst result = this.runner.run(source, filename);\n\t\t\tthis.storage_service.check();\n\n\t\t\tif (result !== null && result !== undefined) {\n\t\t\t\treturn this.runner.toString(result);\n\t\t\t}\n\t\t\treturn null;\n\t\t} catch (err: any) {\n\t\t\tthis.error_info = extractErrorInfo(err, filename, \"runtime\", this.runner);\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t/**\n\t * Call a global function\n\t *\n\t * Executes a specific function defined in the global scope.\n\t * Useful for game loop hooks (update, draw) or event handlers.\n\t *\n\t * @param {string} name - Name of the function to call\n\t * @param {any[]} [args=[]] - Arguments to pass to the function\n\t * @param {number} [timeout=3000] - Execution time limit in ms\n\t * @returns {any} The return value of the function\n\t * @throws {ErrorInfo} If the function doesn't exist or execution fails\n\t */\n\tcall(name: string, args: any[] = [], timeout = 3000): any {\n\t\tthis.error_info = null;\n\t\tthis.context.timeout = Date.now() + timeout;\n\t\tthis.context.stack_size = 0;\n\n\t\ttry {\n\t\t\tconst result = this.runner.call(name, ...args);\n\t\t\tthis.storage_service.check();\n\t\t\treturn result;\n\t\t} catch (err: any) {\n\t\t\tthis.error_info = extractErrorInfo(err, name, \"call\", this.runner);\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t/**\n\t * Load a pre-compiled routine (for production builds)\n\t *\n\t * Loads bytecode directly into the VM, bypassing the compilation step.\n\t * Used in production to improve startup time and obfuscate source.\n\t *\n\t * @param {any} routineData - Either a Routine instance or serialized JSON\n\t * @param {string} [filename=\"\"] - Name of the file for error reporting\n\t * @throws {ErrorInfo} If loading fails\n\t */\n\tloadRoutine(routineData: any, filename: string = \"\"): void {\n\t\tthis.error_info = null;\n\n\t\ttry {\n\t\t\tlet routine: Routine;\n\n\t\t\t// Handle both Routine instances and serialized routine data\n\t\t\t// Serialized data needs to be imported first\n\t\t\tif (routineData instanceof Routine) {\n\t\t\t\troutine = routineData;\n\t\t\t} else {\n\t\t\t\troutine = new Routine(0).import(normalizeSerializedRoutine(routineData));\n\t\t\t}\n\n\t\t\t// Add to main thread for execution\n\t\t\tthis.runner.main_thread.addCall(routine);\n\t\t\tthis.runner.tick();\n\t\t} catch (err: any) {\n\t\t\tthis.error_info = extractErrorInfo(err, filename, \"compile\");\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t/**\n\t * Clear warnings\n\t */\n\tclearWarnings(): void {\n\t\tthis.context.warnings = {\n\t\t\tusing_undefined_variable: {},\n\t\t\tassigning_field_to_undefined: {},\n\t\t\tinvoking_non_function: {},\n\t\t\tassigning_api_variable: {},\n\t\t\tassignment_as_condition: {},\n\t\t};\n\t}\n\n\t/**\n\t * Get warnings\n\t */\n\tgetWarnings(): Record<string, any> {\n\t\treturn this.context.warnings || {};\n\t}\n\n\t/**\n\t * Convert value to string (for printing)\n\t */\n\ttoString(value: any): string {\n\t\treturn this.runner.toString(value);\n\t}\n}\n\nfunction normalizeSerializedRoutine(routineData: CompiledModuleArtifact | SerializedRoutineData): SerializedRoutineData {\n\tif (isCompiledModuleArtifact(routineData)) {\n\t\treturn routineData.routine;\n\t}\n\n\treturn routineData;\n}\n\nfunction isCompiledModuleArtifact(value: unknown): value is CompiledModuleArtifact {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t\"format\" in value &&\n\t\t(value as { format?: unknown }).format === \"l8b-compiled-routine\" &&\n\t\t\"routine\" in value\n\t);\n}\n"],"mappings":"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","names":["_baseMeta","round","x","Math","floor","ceil","abs","min","y","max","sqrt","pow","sin","cos","tan","asin","acos","atan","atan2","sind","PI","cosd","tand","asind","acosd","atand","atan2d","log","exp","random","Random","true","false","_defaultPrint","text","console","createMetaFunctions","customPrint","print","createVMContext","meta","global","fullMeta","local","object","breakable","continuable","returnable","stack_size","timeout","Date","now","warnings","using_undefined_variable","assigning_field_to_undefined","invoking_non_function","assigning_api_variable","assignment_as_condition","setupArrayExtensions","Array","prototype","insert","element","splice","insertAt","index","length","push","remove","removeElement","indexOf","contains","sortList","fn","sort","import_lootiscript","extractErrorInfo","err","fallbackFile","fallbackType","runner","errorMessage","error","message","String","stackTrace","main_thread","processor","generateStackTrace","type","line","column","file","stack","L8BVM","context","storage_service","error_info","meta","global","namespace","preserve_ls","createVMContext","StorageService","storage","getInterface","Runner","init","setupArrayExtensions","run","source","timeout","filename","Date","now","stack_size","result","check","undefined","toString","call","name","args","loadRoutine","routineData","routine","Routine","import","normalizeSerializedRoutine","addCall","tick","clearWarnings","warnings","using_undefined_variable","assigning_field_to_undefined","invoking_non_function","assigning_api_variable","assignment_as_condition","getWarnings","value","isCompiledModuleArtifact","format","import_lootiscript"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// src/context.ts
|
|
5
|
+
import { Random } from "@al8b/lootiscript";
|
|
6
|
+
var _baseMeta = {
|
|
7
|
+
round: /* @__PURE__ */ __name((x) => Math.round(x), "round"),
|
|
8
|
+
floor: /* @__PURE__ */ __name((x) => Math.floor(x), "floor"),
|
|
9
|
+
ceil: /* @__PURE__ */ __name((x) => Math.ceil(x), "ceil"),
|
|
10
|
+
abs: /* @__PURE__ */ __name((x) => Math.abs(x), "abs"),
|
|
11
|
+
min: /* @__PURE__ */ __name((x, y) => Math.min(x, y), "min"),
|
|
12
|
+
max: /* @__PURE__ */ __name((x, y) => Math.max(x, y), "max"),
|
|
13
|
+
sqrt: /* @__PURE__ */ __name((x) => Math.sqrt(x), "sqrt"),
|
|
14
|
+
pow: /* @__PURE__ */ __name((x, y) => x ** y, "pow"),
|
|
15
|
+
sin: /* @__PURE__ */ __name((x) => Math.sin(x), "sin"),
|
|
16
|
+
cos: /* @__PURE__ */ __name((x) => Math.cos(x), "cos"),
|
|
17
|
+
tan: /* @__PURE__ */ __name((x) => Math.tan(x), "tan"),
|
|
18
|
+
asin: /* @__PURE__ */ __name((x) => Math.asin(x), "asin"),
|
|
19
|
+
acos: /* @__PURE__ */ __name((x) => Math.acos(x), "acos"),
|
|
20
|
+
atan: /* @__PURE__ */ __name((x) => Math.atan(x), "atan"),
|
|
21
|
+
atan2: /* @__PURE__ */ __name((y, x) => Math.atan2(y, x), "atan2"),
|
|
22
|
+
sind: /* @__PURE__ */ __name((x) => Math.sin(x / 180 * Math.PI), "sind"),
|
|
23
|
+
cosd: /* @__PURE__ */ __name((x) => Math.cos(x / 180 * Math.PI), "cosd"),
|
|
24
|
+
tand: /* @__PURE__ */ __name((x) => Math.tan(x / 180 * Math.PI), "tand"),
|
|
25
|
+
asind: /* @__PURE__ */ __name((x) => Math.asin(x) * 180 / Math.PI, "asind"),
|
|
26
|
+
acosd: /* @__PURE__ */ __name((x) => Math.acos(x) * 180 / Math.PI, "acosd"),
|
|
27
|
+
atand: /* @__PURE__ */ __name((x) => Math.atan(x) * 180 / Math.PI, "atand"),
|
|
28
|
+
atan2d: /* @__PURE__ */ __name((y, x) => Math.atan2(y, x) * 180 / Math.PI, "atan2d"),
|
|
29
|
+
log: /* @__PURE__ */ __name((x) => Math.log(x), "log"),
|
|
30
|
+
exp: /* @__PURE__ */ __name((x) => Math.exp(x), "exp"),
|
|
31
|
+
random: new Random(0),
|
|
32
|
+
PI: Math.PI,
|
|
33
|
+
true: 1,
|
|
34
|
+
false: 0
|
|
35
|
+
};
|
|
36
|
+
var _defaultPrint = /* @__PURE__ */ __name((text) => console.log(text), "_defaultPrint");
|
|
37
|
+
function createMetaFunctions(customPrint) {
|
|
38
|
+
return {
|
|
39
|
+
..._baseMeta,
|
|
40
|
+
print: customPrint ?? _defaultPrint
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
__name(createMetaFunctions, "createMetaFunctions");
|
|
44
|
+
function createVMContext(meta, global) {
|
|
45
|
+
const fullMeta = {
|
|
46
|
+
..._baseMeta,
|
|
47
|
+
print: meta.print ?? _defaultPrint,
|
|
48
|
+
...meta
|
|
49
|
+
};
|
|
50
|
+
return {
|
|
51
|
+
meta: fullMeta,
|
|
52
|
+
global,
|
|
53
|
+
local: global,
|
|
54
|
+
object: global,
|
|
55
|
+
breakable: 0,
|
|
56
|
+
continuable: 0,
|
|
57
|
+
returnable: 0,
|
|
58
|
+
stack_size: 0,
|
|
59
|
+
timeout: Date.now() + 3e3,
|
|
60
|
+
warnings: {
|
|
61
|
+
using_undefined_variable: {},
|
|
62
|
+
assigning_field_to_undefined: {},
|
|
63
|
+
invoking_non_function: {},
|
|
64
|
+
assigning_api_variable: {},
|
|
65
|
+
assignment_as_condition: {}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
__name(createVMContext, "createVMContext");
|
|
70
|
+
|
|
71
|
+
// src/extensions.ts
|
|
72
|
+
function setupArrayExtensions() {
|
|
73
|
+
if (!Array.prototype.insert) {
|
|
74
|
+
Array.prototype.insert = function(element) {
|
|
75
|
+
this.splice(0, 0, element);
|
|
76
|
+
return element;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (!Array.prototype.insertAt) {
|
|
80
|
+
Array.prototype.insertAt = function(element, index) {
|
|
81
|
+
if (index >= 0 && index < this.length) {
|
|
82
|
+
this.splice(index, 0, element);
|
|
83
|
+
} else {
|
|
84
|
+
this.push(element);
|
|
85
|
+
}
|
|
86
|
+
return element;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (!Array.prototype.remove) {
|
|
90
|
+
Array.prototype.remove = function(index) {
|
|
91
|
+
if (index >= 0 && index < this.length) {
|
|
92
|
+
return this.splice(index, 1)[0];
|
|
93
|
+
}
|
|
94
|
+
return 0;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (!Array.prototype.removeElement) {
|
|
98
|
+
Array.prototype.removeElement = function(element) {
|
|
99
|
+
const index = this.indexOf(element);
|
|
100
|
+
if (index >= 0) {
|
|
101
|
+
return this.splice(index, 1)[0];
|
|
102
|
+
}
|
|
103
|
+
return 0;
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
if (!Array.prototype.contains) {
|
|
107
|
+
Array.prototype.contains = function(element) {
|
|
108
|
+
return this.indexOf(element) >= 0 ? 1 : 0;
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (!Array.prototype.sortList) {
|
|
112
|
+
Array.prototype.sortList = function(fn) {
|
|
113
|
+
if (fn) {
|
|
114
|
+
return this.sort(fn);
|
|
115
|
+
}
|
|
116
|
+
return this.sort();
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
__name(setupArrayExtensions, "setupArrayExtensions");
|
|
121
|
+
|
|
122
|
+
// src/l8bvm.ts
|
|
123
|
+
import { StorageService } from "@al8b/io";
|
|
124
|
+
import { Routine, Runner } from "@al8b/lootiscript";
|
|
125
|
+
function extractErrorInfo(err, fallbackFile, fallbackType, runner) {
|
|
126
|
+
const errorMessage = typeof err === "object" && err !== null && "error" in err && typeof err.error === "string" ? err.error : err?.message ?? String(err);
|
|
127
|
+
let stackTrace = err?.stackTrace;
|
|
128
|
+
if (!stackTrace && runner?.main_thread?.processor?.generateStackTrace) {
|
|
129
|
+
stackTrace = runner.main_thread.processor.generateStackTrace();
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
error: errorMessage,
|
|
133
|
+
type: err?.type ?? fallbackType,
|
|
134
|
+
line: err?.line,
|
|
135
|
+
column: err?.column,
|
|
136
|
+
file: err?.file ?? fallbackFile,
|
|
137
|
+
stack: err?.stack,
|
|
138
|
+
stackTrace
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
__name(extractErrorInfo, "extractErrorInfo");
|
|
142
|
+
var L8BVM = class {
|
|
143
|
+
static {
|
|
144
|
+
__name(this, "L8BVM");
|
|
145
|
+
}
|
|
146
|
+
context;
|
|
147
|
+
runner;
|
|
148
|
+
storage_service;
|
|
149
|
+
error_info = null;
|
|
150
|
+
constructor(meta = {}, global = {}, namespace = "/l8b", preserve_ls = false) {
|
|
151
|
+
this.context = createVMContext(meta, global);
|
|
152
|
+
this.storage_service = new StorageService(namespace, preserve_ls);
|
|
153
|
+
this.context.global.storage = this.storage_service.getInterface();
|
|
154
|
+
this.runner = new Runner(this);
|
|
155
|
+
this.runner.init();
|
|
156
|
+
setupArrayExtensions();
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Run source code
|
|
160
|
+
*
|
|
161
|
+
* Compiles and executes a string of LootiScript code.
|
|
162
|
+
*
|
|
163
|
+
* @param {string} source - The source code to execute
|
|
164
|
+
* @param {number} [timeout=3000] - Execution time limit in ms
|
|
165
|
+
* @param {string} [filename=""] - Filename for error reporting
|
|
166
|
+
* @returns {any} The result of the last statement execution
|
|
167
|
+
* @throws {ErrorInfo} If compilation or execution fails
|
|
168
|
+
*/
|
|
169
|
+
run(source, timeout = 3e3, filename = "") {
|
|
170
|
+
this.error_info = null;
|
|
171
|
+
this.context.timeout = Date.now() + timeout;
|
|
172
|
+
this.context.stack_size = 0;
|
|
173
|
+
try {
|
|
174
|
+
const result = this.runner.run(source, filename);
|
|
175
|
+
this.storage_service.check();
|
|
176
|
+
if (result !== null && result !== void 0) {
|
|
177
|
+
return this.runner.toString(result);
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
} catch (err) {
|
|
181
|
+
this.error_info = extractErrorInfo(err, filename, "runtime", this.runner);
|
|
182
|
+
throw err;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Call a global function
|
|
187
|
+
*
|
|
188
|
+
* Executes a specific function defined in the global scope.
|
|
189
|
+
* Useful for game loop hooks (update, draw) or event handlers.
|
|
190
|
+
*
|
|
191
|
+
* @param {string} name - Name of the function to call
|
|
192
|
+
* @param {any[]} [args=[]] - Arguments to pass to the function
|
|
193
|
+
* @param {number} [timeout=3000] - Execution time limit in ms
|
|
194
|
+
* @returns {any} The return value of the function
|
|
195
|
+
* @throws {ErrorInfo} If the function doesn't exist or execution fails
|
|
196
|
+
*/
|
|
197
|
+
call(name, args = [], timeout = 3e3) {
|
|
198
|
+
this.error_info = null;
|
|
199
|
+
this.context.timeout = Date.now() + timeout;
|
|
200
|
+
this.context.stack_size = 0;
|
|
201
|
+
try {
|
|
202
|
+
const result = this.runner.call(name, ...args);
|
|
203
|
+
this.storage_service.check();
|
|
204
|
+
return result;
|
|
205
|
+
} catch (err) {
|
|
206
|
+
this.error_info = extractErrorInfo(err, name, "call", this.runner);
|
|
207
|
+
throw err;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Load a pre-compiled routine (for production builds)
|
|
212
|
+
*
|
|
213
|
+
* Loads bytecode directly into the VM, bypassing the compilation step.
|
|
214
|
+
* Used in production to improve startup time and obfuscate source.
|
|
215
|
+
*
|
|
216
|
+
* @param {any} routineData - Either a Routine instance or serialized JSON
|
|
217
|
+
* @param {string} [filename=""] - Name of the file for error reporting
|
|
218
|
+
* @throws {ErrorInfo} If loading fails
|
|
219
|
+
*/
|
|
220
|
+
loadRoutine(routineData, filename = "") {
|
|
221
|
+
this.error_info = null;
|
|
222
|
+
try {
|
|
223
|
+
let routine;
|
|
224
|
+
if (routineData instanceof Routine) {
|
|
225
|
+
routine = routineData;
|
|
226
|
+
} else {
|
|
227
|
+
routine = new Routine(0).import(normalizeSerializedRoutine(routineData));
|
|
228
|
+
}
|
|
229
|
+
this.runner.main_thread.addCall(routine);
|
|
230
|
+
this.runner.tick();
|
|
231
|
+
} catch (err) {
|
|
232
|
+
this.error_info = extractErrorInfo(err, filename, "compile");
|
|
233
|
+
throw err;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Clear warnings
|
|
238
|
+
*/
|
|
239
|
+
clearWarnings() {
|
|
240
|
+
this.context.warnings = {
|
|
241
|
+
using_undefined_variable: {},
|
|
242
|
+
assigning_field_to_undefined: {},
|
|
243
|
+
invoking_non_function: {},
|
|
244
|
+
assigning_api_variable: {},
|
|
245
|
+
assignment_as_condition: {}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Get warnings
|
|
250
|
+
*/
|
|
251
|
+
getWarnings() {
|
|
252
|
+
return this.context.warnings || {};
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Convert value to string (for printing)
|
|
256
|
+
*/
|
|
257
|
+
toString(value) {
|
|
258
|
+
return this.runner.toString(value);
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
function normalizeSerializedRoutine(routineData) {
|
|
262
|
+
if (isCompiledModuleArtifact(routineData)) {
|
|
263
|
+
return routineData.routine;
|
|
264
|
+
}
|
|
265
|
+
return routineData;
|
|
266
|
+
}
|
|
267
|
+
__name(normalizeSerializedRoutine, "normalizeSerializedRoutine");
|
|
268
|
+
function isCompiledModuleArtifact(value) {
|
|
269
|
+
return typeof value === "object" && value !== null && "format" in value && value.format === "l8b-compiled-routine" && "routine" in value;
|
|
270
|
+
}
|
|
271
|
+
__name(isCompiledModuleArtifact, "isCompiledModuleArtifact");
|
|
272
|
+
|
|
273
|
+
// src/index.ts
|
|
274
|
+
import { Random as Random2, Routine as Routine2 } from "@al8b/lootiscript";
|
|
275
|
+
export {
|
|
276
|
+
L8BVM,
|
|
277
|
+
Random2 as Random,
|
|
278
|
+
Routine2 as Routine,
|
|
279
|
+
createMetaFunctions,
|
|
280
|
+
createVMContext,
|
|
281
|
+
setupArrayExtensions
|
|
282
|
+
};
|
|
283
|
+
//# sourceMappingURL=index.mjs.map
|