aimyapi 0.2.1 → 1.0.3

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/src/sandbox.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import "quickjs-emscripten";
2
2
 
3
+ import { QuickJSWASMModule } from "quickjs-emscripten";
3
4
  import * as ts from "typescript";
4
5
  import {
5
6
  injectTimingFunctions,
@@ -11,10 +12,10 @@ import {
11
12
  // Should we use "noEmitOnError": true ?
12
13
  function tsCompile(
13
14
  source: string,
14
- options: ts.TranspileOptions = null
15
+ options?: ts.TranspileOptions
15
16
  ): string {
16
17
  // Default options -- you could also perform a merge, or use the project tsconfig.json
17
- if (null === options) {
18
+ if (!options) {
18
19
  options = {
19
20
  compilerOptions: {
20
21
  target: ts.ScriptTarget.ES2020,
@@ -34,13 +35,59 @@ export interface Globals {
34
35
  [key: string]: GlobalOption;
35
36
  }
36
37
 
38
+ export type SandboxLog = {
39
+ type: "log" | "error";
40
+ args: any[];
41
+ source: string
42
+ };
43
+ export type CodeRunResult = {
44
+ success: boolean;
45
+ logs: SandboxLog[];
46
+ }
47
+ type LogSource = "sandbox" | "compiler" | "runner";
48
+ class Logger {
49
+ logs: SandboxLog[] = [];
50
+ debug: boolean = false;
51
+
52
+ constructor(debug: boolean = false) {
53
+ this.debug = debug;
54
+ }
55
+
56
+ log(args: any, source: LogSource) {
57
+ if(Array.isArray(args)) {
58
+ this.logs.push({ type: "log", args, source });
59
+ } else {
60
+ this.logs.push({ type: "log", args: [args], source });
61
+ }
62
+ if (this.debug) {
63
+ console.log(`[${source}]:`, ...args);
64
+ }
65
+ }
66
+
67
+ error(args: any, source: LogSource) {
68
+ if(Array.isArray(args)) {
69
+ this.logs.push({ type: "error", args, source });
70
+ } else {
71
+ this.logs.push({ type: "error", args: [args], source });
72
+ }
73
+
74
+ console.error(`[${source}]:`, ...args);
75
+ }
76
+
77
+ clear() {
78
+ this.logs = [];
79
+ }
80
+ }
81
+
82
+
83
+
37
84
  // function whitelist can be produced using something like:
38
85
  // const assistantFunctions = Object.getOwnPropertyNames(Object.getPrototypeOf(apiObject))
39
86
  // .filter((f) => f !== "constructor" && !f.startsWith("_"));
40
87
 
41
88
  // Is there some way we can treat the API and globals the same?
42
89
  // We could just provide an object for all globals (including the API) and also have the whitelist as a parameter..
43
- export default async function createSandbox(QuickJS, requireLookup:any = {}, globals: Globals = {}, debug:boolean = false) {
90
+ export default async function createSandbox(QuickJS: QuickJSWASMModule, requireLookup:any = {}, globals: Globals = {}, debug:boolean = false) {
44
91
  const vm = QuickJS.newContext();
45
92
 
46
93
  let errorState = false;
@@ -63,27 +110,31 @@ export default async function createSandbox(QuickJS, requireLookup:any = {}, glo
63
110
  return asyncProcessesRunning > 0;
64
111
  };
65
112
 
113
+ const logger = new Logger(debug);
114
+
115
+
116
+
66
117
  // const assistantHandle = wrapObjectWhitelist(vm, apiObject, functionWhitelist, beginAsyncProcess, endAsyncProcess);
67
118
  // vm.setProp(vm.global, apiGlobalName, assistantHandle)
68
119
 
69
120
  // assistantHandle.dispose();
70
121
 
71
- const logHandle = vm.newFunction("log", (...args) => {
122
+ const logHandle = vm.newFunction("log", (...args:any) => {
72
123
  const nativeArgs = args.map(vm.dump);
73
- console.log("QuickJS Log:", ...nativeArgs);
124
+ logger.log(nativeArgs, "sandbox");
74
125
  });
75
126
  const consoleHandle = vm.newObject();
76
127
  const exportsHandle = vm.newObject();
77
128
 
78
- const errorHandle = vm.newFunction("error", (...args) => {
129
+ const errorHandle = vm.newFunction("error", (...args:any) => {
79
130
  try {
80
131
  const nativeArgs = args.map(vm.dump);
81
132
 
82
- console.log("QuickJS console.error:", ...nativeArgs);
133
+ logger.error(nativeArgs, "sandbox");
83
134
  lastError = JSON.stringify(nativeArgs);
84
135
  errorState = true;
85
136
  } catch (e) {
86
- console.log("Error in error:", e);
137
+ logger.error(["? Error in error:", e], "sandbox");
87
138
  }
88
139
  });
89
140
 
@@ -134,13 +185,42 @@ export default async function createSandbox(QuickJS, requireLookup:any = {}, glo
134
185
  return lastError;
135
186
  },
136
187
  isAsyncProcessRunning,
137
- runTask: (task: string): boolean => {
188
+ // make sure code passes the typescript compilation
189
+ checkCode: (code: string) => {
190
+ logger.clear();
138
191
  try {
139
- const compiled = tsCompile(task);
192
+ const compiled = tsCompile(code);
193
+ if (!compiled || compiled.length === 0) {
194
+ logger.error(["Could not compile:", code], "compiler");
195
+ return {
196
+ success: false,
197
+ logs: logger.logs,
198
+ };
199
+ }
200
+ } catch (e) {
201
+ logger.error(["Error testing code:", e], "compiler");
202
+ return {
203
+ success: false,
204
+ logs: logger.logs,
205
+ };
206
+ }
207
+
208
+ return {
209
+ success: true,
210
+ logs: logger.logs,
211
+ }
212
+ },
213
+ runCode: (code: string): CodeRunResult => {
214
+ try {
215
+ logger.clear();
216
+ const compiled = tsCompile(code);
140
217
 
141
218
  if (!compiled || compiled.length === 0) {
142
- console.error("Could not compile", task);
143
- return false;
219
+ logger.error(["Could not compile:", code], "compiler");
220
+ return {
221
+ success: false,
222
+ logs: logger.logs,
223
+ };
144
224
  }
145
225
 
146
226
  // View compiled typescript code for debugging
@@ -152,33 +232,46 @@ export default async function createSandbox(QuickJS, requireLookup:any = {}, glo
152
232
 
153
233
  if (result.error) {
154
234
  // log out the compiled program with line numbers
155
- const lines = compiled.split("\n");
156
- for (let i = 0; i < lines.length; i++) {
157
- console.log(`${i + 1}: ${lines[i]}`);
158
- }
235
+ const lines = compiled.split("\n").map((line, index) => `${index + 1}: ${line}`);
236
+
159
237
 
160
- console.log("Execution failed:", vm.dump(result.error));
238
+ logger.error(["Execution failed:", lines.join("\n"), vm.dump(result.error)], "runner");
161
239
 
162
240
  result.error.dispose();
163
- return false;
164
- } else {
241
+ return {
242
+ success: false,
243
+ logs: logger.logs,
244
+ };
245
+ } else if("value" in result){
165
246
  //console.log("Task Result:", vm.dump(result.value));
247
+
166
248
  result.value.dispose();
167
249
 
250
+
168
251
  if (errorState) {
169
- const lines = compiled.split("\n");
170
- for (let i = 0; i < lines.length; i++) {
171
- console.log(`${i + 1}: ${lines[i]}`);
172
- }
252
+ const lines = compiled.split("\n").map((line, index) => `${index + 1}: ${line}`);
253
+ logger.error(["An error was reported during execution. Compiled code with line numbers:", lines.join("\n")], "runner");
173
254
  }
174
255
 
175
- return !errorState;
256
+ return {
257
+ success: !errorState,
258
+ logs: logger.logs,
259
+ };
260
+ } else {
261
+ logger.error(["Unknown execution result:", result], "runner");
262
+ return {
263
+ success: false,
264
+ logs: logger.logs,
265
+ };
176
266
  }
177
267
  } catch (e) {
178
- console.log(e);
268
+ logger.error(["Error running code:", e], "runner");
179
269
  }
180
270
 
181
- return false;
271
+ return {
272
+ success: false,
273
+ logs: logger.logs,
274
+ };
182
275
  },
183
276
  };
184
277
  }
package/tsconfig.json CHANGED
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "compilerOptions": {
3
+ "rootDir": "./src",
3
4
  "target": "ES2021" /* Node 16+ per https://github.com/microsoft/TypeScript/wiki/Node-Target-Mapping */,
4
5
  "module": "nodenext" /* Some imports are really imports: https://www.typescriptlang.org/docs/handbook/esm-node.html */,
5
6
  "moduleResolution": "nodenext" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,