@ticatec/dyna-js 0.0.2 → 0.0.4
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 +32 -61
- package/README_CN.md +42 -71
- package/dist/DynaJs.d.ts +200 -7
- package/dist/DynaJs.d.ts.map +1 -1
- package/dist/DynaJs.esm.js +199 -64
- package/dist/DynaJs.js +205 -70
- package/dist/types.d.ts +0 -8
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/DynaJs.d.ts
CHANGED
|
@@ -1,20 +1,213 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ExecutionOptions, DynaJsConfig, ModuleImports } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* DynaJs - A safe dynamic code execution library
|
|
4
|
+
*
|
|
5
|
+
* Provides secure execution of dynamic JavaScript code using new Function()
|
|
6
|
+
* with configurable security policies and sandboxing capabilities.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const loader = new DynaJs({
|
|
11
|
+
* defaultImports: { MyClass },
|
|
12
|
+
* validateCode: true,
|
|
13
|
+
* allowBrowserAPIs: false
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* const result = loader.executeSync(`return new MyClass()`);
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
2
19
|
export default class DynaJs {
|
|
3
20
|
private config;
|
|
21
|
+
/**
|
|
22
|
+
* Creates a new DynaJs instance
|
|
23
|
+
*
|
|
24
|
+
* @param config - Configuration options for the DynaJs instance
|
|
25
|
+
* @param config.defaultTimeout - Default timeout for async execution in milliseconds (default: 5000)
|
|
26
|
+
* @param config.defaultStrict - Whether to use strict mode by default (default: true)
|
|
27
|
+
* @param config.allowedGlobals - Whitelist of allowed global variables (empty = allow all defaultImports)
|
|
28
|
+
* @param config.blockedGlobals - Blacklist of blocked global variables
|
|
29
|
+
* @param config.defaultImports - Pre-imported classes/functions available to dynamic code
|
|
30
|
+
* @param config.allowTimers - Allow setTimeout/setInterval in dynamic code (default: false)
|
|
31
|
+
* @param config.allowDynamicImports - Allow import()/require() in dynamic code (default: false)
|
|
32
|
+
* @param config.validateCode - Enable code validation before execution (default: true)
|
|
33
|
+
* @param config.allowBrowserAPIs - Allow access to browser APIs like window/document (default: false)
|
|
34
|
+
* @param config.allowNodeAPIs - Allow access to Node.js APIs like process/require (default: false)
|
|
35
|
+
*/
|
|
4
36
|
constructor(config?: DynaJsConfig);
|
|
5
|
-
|
|
6
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Asynchronously execute dynamic code with timeout support
|
|
39
|
+
*
|
|
40
|
+
* @template T - The expected return type of the executed code
|
|
41
|
+
* @param code - The JavaScript code to execute
|
|
42
|
+
* @param options - Execution options
|
|
43
|
+
* @param options.context - Additional context variables for the code
|
|
44
|
+
* @param options.timeout - Execution timeout in milliseconds (overrides defaultTimeout)
|
|
45
|
+
* @param options.strict - Whether to use strict mode (overrides defaultStrict)
|
|
46
|
+
* @param options.imports - Additional imports for this execution only
|
|
47
|
+
* @returns Promise resolving to the execution result
|
|
48
|
+
*/
|
|
49
|
+
execute<T = any>(code: string, options?: ExecutionOptions): Promise<T>;
|
|
50
|
+
/**
|
|
51
|
+
* Synchronously execute dynamic code and return result directly
|
|
52
|
+
*
|
|
53
|
+
* @template T - The expected return type of the executed code
|
|
54
|
+
* @param code - The JavaScript code to execute
|
|
55
|
+
* @param options - Execution options
|
|
56
|
+
* @param options.context - Additional context variables for the code
|
|
57
|
+
* @param options.strict - Whether to use strict mode (overrides defaultStrict)
|
|
58
|
+
* @param options.imports - Additional imports for this execution only
|
|
59
|
+
* @returns The direct result of code execution
|
|
60
|
+
*/
|
|
61
|
+
executeSync<T = any>(code: string, options?: ExecutionOptions): T;
|
|
62
|
+
/**
|
|
63
|
+
* Execute code with a timeout constraint
|
|
64
|
+
*
|
|
65
|
+
* @private
|
|
66
|
+
* @template T - The expected return type
|
|
67
|
+
* @param code - The JavaScript code to execute
|
|
68
|
+
* @param context - Execution context with available variables
|
|
69
|
+
* @param timeout - Maximum execution time in milliseconds
|
|
70
|
+
* @param strict - Whether to use strict mode
|
|
71
|
+
* @returns Promise resolving to the execution result
|
|
72
|
+
* @throws {Error} If execution exceeds timeout or runtime error occurs
|
|
73
|
+
*/
|
|
7
74
|
private executeWithTimeout;
|
|
75
|
+
/**
|
|
76
|
+
* Core code execution engine using new Function()
|
|
77
|
+
*
|
|
78
|
+
* @private
|
|
79
|
+
* @param code - The JavaScript code to execute
|
|
80
|
+
* @param context - Execution context with available variables
|
|
81
|
+
* @param strict - Whether to use strict mode
|
|
82
|
+
* @returns The execution result
|
|
83
|
+
* @throws {Error} If code execution fails
|
|
84
|
+
*/
|
|
8
85
|
private executeCode;
|
|
86
|
+
/**
|
|
87
|
+
* Create a reusable function from dynamic code
|
|
88
|
+
*
|
|
89
|
+
* @template T - The function signature type
|
|
90
|
+
* @param code - The JavaScript code for the function body
|
|
91
|
+
* @param paramNames - Array of parameter names for the function
|
|
92
|
+
* @param options - Execution options
|
|
93
|
+
* @param options.context - Additional context variables
|
|
94
|
+
* @param options.strict - Whether to use strict mode
|
|
95
|
+
* @param options.imports - Additional imports
|
|
96
|
+
* @returns A function that can be called multiple times
|
|
97
|
+
* @throws {Error} If function creation fails
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* const validator = loader.createFunction<(data: any) => boolean>(`
|
|
102
|
+
* return function(data) {
|
|
103
|
+
* return data.name && data.email;
|
|
104
|
+
* };
|
|
105
|
+
* `);
|
|
106
|
+
* const isValid = validator({ name: 'John', email: 'john@example.com' });
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
9
109
|
createFunction<T extends (...args: any[]) => any>(code: string, paramNames?: string[], options?: ExecutionOptions): T;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
110
|
+
/**
|
|
111
|
+
* Synchronously execute code with additional temporary imports
|
|
112
|
+
*
|
|
113
|
+
* @template T - The expected return type
|
|
114
|
+
* @param code - The JavaScript code to execute
|
|
115
|
+
* @param imports - Additional imports to merge with defaultImports
|
|
116
|
+
* @param options - Additional execution options
|
|
117
|
+
* @returns The direct result of code execution
|
|
118
|
+
* @throws {Error} If code validation fails or runtime error occurs
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```typescript
|
|
122
|
+
* const result = loader.executeWithImports(`
|
|
123
|
+
* return new CustomComponent();
|
|
124
|
+
* `, {
|
|
125
|
+
* CustomComponent: MyCustomComponent
|
|
126
|
+
* });
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
executeWithImports<T = any>(code: string, imports: ModuleImports, options?: ExecutionOptions): T;
|
|
130
|
+
/**
|
|
131
|
+
* Asynchronously execute code with additional temporary imports
|
|
132
|
+
*
|
|
133
|
+
* @template T - The expected return type
|
|
134
|
+
* @param code - The JavaScript code to execute
|
|
135
|
+
* @param imports - Additional imports to merge with defaultImports
|
|
136
|
+
* @param options - Additional execution options (including timeout)
|
|
137
|
+
* @returns Promise resolving to the execution result
|
|
138
|
+
* @throws {Error} If code validation fails, execution times out, or runtime error occurs
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```typescript
|
|
142
|
+
* const result = await loader.executeWithImportsAsync(`
|
|
143
|
+
* return await fetchData();
|
|
144
|
+
* `, {
|
|
145
|
+
* fetchData: myFetchFunction
|
|
146
|
+
* }, { timeout: 5000 });
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
executeWithImportsAsync<T = any>(code: string, imports: ModuleImports, options?: ExecutionOptions): Promise<T>;
|
|
150
|
+
/**
|
|
151
|
+
* Singleton instance of DynaJs
|
|
152
|
+
* @private
|
|
153
|
+
*/
|
|
14
154
|
static instance: DynaJs | null;
|
|
155
|
+
/**
|
|
156
|
+
* Initialize the singleton instance of DynaJs
|
|
157
|
+
*
|
|
158
|
+
* @param config - Configuration options for the singleton instance
|
|
159
|
+
* @returns The initialized singleton instance
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```typescript
|
|
163
|
+
* DynaJs.initialize({
|
|
164
|
+
* defaultImports: { MyClass, MyFunction },
|
|
165
|
+
* validateCode: true
|
|
166
|
+
* });
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
15
169
|
static initialize(config: DynaJsConfig): DynaJs;
|
|
170
|
+
/**
|
|
171
|
+
* Get the singleton instance of DynaJs
|
|
172
|
+
*
|
|
173
|
+
* @returns The singleton instance
|
|
174
|
+
* @throws {Error} If DynaJs hasn't been initialized
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```typescript
|
|
178
|
+
* const loader = DynaJs.getInstance();
|
|
179
|
+
* const result = loader.executeSync(`return 1 + 1`);
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
16
182
|
static getInstance(): DynaJs;
|
|
183
|
+
/**
|
|
184
|
+
* Reset the singleton instance (useful for testing)
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```typescript
|
|
188
|
+
* DynaJs.reset();
|
|
189
|
+
* DynaJs.initialize(newConfig);
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
17
192
|
static reset(): void;
|
|
193
|
+
/**
|
|
194
|
+
* Prepare the execution context by merging user context, imports, and applying security filters
|
|
195
|
+
*
|
|
196
|
+
* Execution flow:
|
|
197
|
+
* 1. Create safe context (filtering browser/node APIs based on config)
|
|
198
|
+
* 2. Merge with defaultImports and additional imports
|
|
199
|
+
* 3. Apply allowedGlobals whitelist filter if configured
|
|
200
|
+
*
|
|
201
|
+
* @private
|
|
202
|
+
* @param userContext - User-provided context variables
|
|
203
|
+
* @param imports - Additional imports for this execution
|
|
204
|
+
* @returns The final execution context
|
|
205
|
+
*
|
|
206
|
+
* @remarks
|
|
207
|
+
* If allowedGlobals is set and not empty, only variables in the whitelist will be available.
|
|
208
|
+
* This happens AFTER merging defaultImports, so make sure to include all needed imports
|
|
209
|
+
* in the allowedGlobals array if you use this feature.
|
|
210
|
+
*/
|
|
18
211
|
private prepareContext;
|
|
19
212
|
}
|
|
20
213
|
//# sourceMappingURL=DynaJs.d.ts.map
|
package/dist/DynaJs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DynaJs.d.ts","sourceRoot":"","sources":["../src/DynaJs.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"DynaJs.d.ts","sourceRoot":"","sources":["../src/DynaJs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,gBAAgB,EAAE,YAAY,EAAE,aAAa,EAAC,MAAM,SAAS,CAAC;AAGxF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,OAAO,OAAO,MAAM;IACvB,OAAO,CAAC,MAAM,CAAyB;IAEvC;;;;;;;;;;;;;;OAcG;gBACS,MAAM,GAAE,YAAiB;IAerC;;;;;;;;;;;OAWG;IACG,OAAO,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,CAAC,CAAC;IAwBhF;;;;;;;;;;OAUG;IACH,WAAW,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,CAAC;IAuBrE;;;;;;;;;;;OAWG;YACW,kBAAkB;IAiBhC;;;;;;;;;OASG;IACH,OAAO,CAAC,WAAW;IAenB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,cAAc,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EAC5C,IAAI,EAAE,MAAM,EACZ,UAAU,GAAE,MAAM,EAAO,EACzB,OAAO,GAAE,gBAAqB,GAC/B,CAAC;IAuBJ;;;;;;;;;;;;;;;;;;OAkBG;IACH,kBAAkB,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,GAAE,gBAAqB,GAAG,CAAC;IAOpG;;;;;;;;;;;;;;;;;;OAkBG;IACG,uBAAuB,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,CAAC,CAAC;IAOxH;;;OAGG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEtC;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM;IAO/C;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,WAAW,IAAI,MAAM;IAO5B;;;;;;;;OAQG;IACH,MAAM,CAAC,KAAK,IAAI,IAAI;IAIpB;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,cAAc;CAwBzB"}
|
package/dist/DynaJs.esm.js
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
import { createSafeContext, validateCode } from './utils';
|
|
2
|
+
/**
|
|
3
|
+
* DynaJs - A safe dynamic code execution library
|
|
4
|
+
*
|
|
5
|
+
* Provides secure execution of dynamic JavaScript code using new Function()
|
|
6
|
+
* with configurable security policies and sandboxing capabilities.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const loader = new DynaJs({
|
|
11
|
+
* defaultImports: { MyClass },
|
|
12
|
+
* validateCode: true,
|
|
13
|
+
* allowBrowserAPIs: false
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* const result = loader.executeSync(`return new MyClass()`);
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
2
19
|
class DynaJs {
|
|
20
|
+
/**
|
|
21
|
+
* Creates a new DynaJs instance
|
|
22
|
+
*
|
|
23
|
+
* @param config - Configuration options for the DynaJs instance
|
|
24
|
+
* @param config.defaultTimeout - Default timeout for async execution in milliseconds (default: 5000)
|
|
25
|
+
* @param config.defaultStrict - Whether to use strict mode by default (default: true)
|
|
26
|
+
* @param config.allowedGlobals - Whitelist of allowed global variables (empty = allow all defaultImports)
|
|
27
|
+
* @param config.blockedGlobals - Blacklist of blocked global variables
|
|
28
|
+
* @param config.defaultImports - Pre-imported classes/functions available to dynamic code
|
|
29
|
+
* @param config.allowTimers - Allow setTimeout/setInterval in dynamic code (default: false)
|
|
30
|
+
* @param config.allowDynamicImports - Allow import()/require() in dynamic code (default: false)
|
|
31
|
+
* @param config.validateCode - Enable code validation before execution (default: true)
|
|
32
|
+
* @param config.allowBrowserAPIs - Allow access to browser APIs like window/document (default: false)
|
|
33
|
+
* @param config.allowNodeAPIs - Allow access to Node.js APIs like process/require (default: false)
|
|
34
|
+
*/
|
|
3
35
|
constructor(config = {}) {
|
|
4
36
|
this.config = {
|
|
5
37
|
defaultTimeout: config.defaultTimeout ?? 5000,
|
|
@@ -7,8 +39,6 @@ class DynaJs {
|
|
|
7
39
|
allowedGlobals: config.allowedGlobals ?? [],
|
|
8
40
|
blockedGlobals: config.blockedGlobals ?? [],
|
|
9
41
|
defaultImports: config.defaultImports ?? {},
|
|
10
|
-
defaultInjectedKeys: config.defaultInjectedKeys ?? [],
|
|
11
|
-
useProxyByDefault: config.useProxyByDefault ?? true,
|
|
12
42
|
allowTimers: config.allowTimers ?? false,
|
|
13
43
|
allowDynamicImports: config.allowDynamicImports ?? false,
|
|
14
44
|
validateCode: config.validateCode ?? true,
|
|
@@ -16,6 +46,18 @@ class DynaJs {
|
|
|
16
46
|
allowNodeAPIs: config.allowNodeAPIs ?? false
|
|
17
47
|
};
|
|
18
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Asynchronously execute dynamic code with timeout support
|
|
51
|
+
*
|
|
52
|
+
* @template T - The expected return type of the executed code
|
|
53
|
+
* @param code - The JavaScript code to execute
|
|
54
|
+
* @param options - Execution options
|
|
55
|
+
* @param options.context - Additional context variables for the code
|
|
56
|
+
* @param options.timeout - Execution timeout in milliseconds (overrides defaultTimeout)
|
|
57
|
+
* @param options.strict - Whether to use strict mode (overrides defaultStrict)
|
|
58
|
+
* @param options.imports - Additional imports for this execution only
|
|
59
|
+
* @returns Promise resolving to the execution result
|
|
60
|
+
*/
|
|
19
61
|
async execute(code, options = {}) {
|
|
20
62
|
const startTime = performance.now();
|
|
21
63
|
try {
|
|
@@ -29,17 +71,25 @@ class DynaJs {
|
|
|
29
71
|
const timeout = options.timeout ?? this.config.defaultTimeout;
|
|
30
72
|
const strict = options.strict ?? this.config.defaultStrict;
|
|
31
73
|
const result = await this.executeWithTimeout(code, context, timeout, strict);
|
|
32
|
-
|
|
33
|
-
return
|
|
34
|
-
result: result,
|
|
35
|
-
executionTime
|
|
36
|
-
};
|
|
74
|
+
console.info(`Build dyna code in ${performance.now() - startTime}ms`);
|
|
75
|
+
return result;
|
|
37
76
|
}
|
|
38
77
|
catch (error) {
|
|
39
78
|
const executionTime = performance.now() - startTime;
|
|
40
79
|
throw new Error(`Script execution failed after ${executionTime.toFixed(2)}ms: ${error instanceof Error ? error.message : String(error)}`);
|
|
41
80
|
}
|
|
42
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Synchronously execute dynamic code and return result directly
|
|
84
|
+
*
|
|
85
|
+
* @template T - The expected return type of the executed code
|
|
86
|
+
* @param code - The JavaScript code to execute
|
|
87
|
+
* @param options - Execution options
|
|
88
|
+
* @param options.context - Additional context variables for the code
|
|
89
|
+
* @param options.strict - Whether to use strict mode (overrides defaultStrict)
|
|
90
|
+
* @param options.imports - Additional imports for this execution only
|
|
91
|
+
* @returns The direct result of code execution
|
|
92
|
+
*/
|
|
43
93
|
executeSync(code, options = {}) {
|
|
44
94
|
const startTime = performance.now();
|
|
45
95
|
try {
|
|
@@ -52,17 +102,26 @@ class DynaJs {
|
|
|
52
102
|
const context = this.prepareContext(options.context, options.imports);
|
|
53
103
|
const strict = options.strict ?? this.config.defaultStrict;
|
|
54
104
|
const result = this.executeCode(code, context, strict);
|
|
55
|
-
|
|
56
|
-
return
|
|
57
|
-
result: result,
|
|
58
|
-
executionTime
|
|
59
|
-
};
|
|
105
|
+
console.info(`Build dyna code in ${performance.now() - startTime}ms`);
|
|
106
|
+
return result;
|
|
60
107
|
}
|
|
61
108
|
catch (error) {
|
|
62
109
|
const executionTime = performance.now() - startTime;
|
|
63
110
|
throw new Error(`Script execution failed after ${executionTime.toFixed(2)}ms: ${error instanceof Error ? error.message : String(error)}`);
|
|
64
111
|
}
|
|
65
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Execute code with a timeout constraint
|
|
115
|
+
*
|
|
116
|
+
* @private
|
|
117
|
+
* @template T - The expected return type
|
|
118
|
+
* @param code - The JavaScript code to execute
|
|
119
|
+
* @param context - Execution context with available variables
|
|
120
|
+
* @param timeout - Maximum execution time in milliseconds
|
|
121
|
+
* @param strict - Whether to use strict mode
|
|
122
|
+
* @returns Promise resolving to the execution result
|
|
123
|
+
* @throws {Error} If execution exceeds timeout or runtime error occurs
|
|
124
|
+
*/
|
|
66
125
|
async executeWithTimeout(code, context, timeout, strict) {
|
|
67
126
|
return new Promise((resolve, reject) => {
|
|
68
127
|
const timer = setTimeout(() => {
|
|
@@ -79,6 +138,16 @@ class DynaJs {
|
|
|
79
138
|
}
|
|
80
139
|
});
|
|
81
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Core code execution engine using new Function()
|
|
143
|
+
*
|
|
144
|
+
* @private
|
|
145
|
+
* @param code - The JavaScript code to execute
|
|
146
|
+
* @param context - Execution context with available variables
|
|
147
|
+
* @param strict - Whether to use strict mode
|
|
148
|
+
* @returns The execution result
|
|
149
|
+
* @throws {Error} If code execution fails
|
|
150
|
+
*/
|
|
82
151
|
executeCode(code, context, strict) {
|
|
83
152
|
const contextKeys = Object.keys(context);
|
|
84
153
|
const contextValues = Object.values(context);
|
|
@@ -92,6 +161,29 @@ class DynaJs {
|
|
|
92
161
|
throw new Error(`Code execution error: ${error instanceof Error ? error.message : String(error)}`);
|
|
93
162
|
}
|
|
94
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Create a reusable function from dynamic code
|
|
166
|
+
*
|
|
167
|
+
* @template T - The function signature type
|
|
168
|
+
* @param code - The JavaScript code for the function body
|
|
169
|
+
* @param paramNames - Array of parameter names for the function
|
|
170
|
+
* @param options - Execution options
|
|
171
|
+
* @param options.context - Additional context variables
|
|
172
|
+
* @param options.strict - Whether to use strict mode
|
|
173
|
+
* @param options.imports - Additional imports
|
|
174
|
+
* @returns A function that can be called multiple times
|
|
175
|
+
* @throws {Error} If function creation fails
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```typescript
|
|
179
|
+
* const validator = loader.createFunction<(data: any) => boolean>(`
|
|
180
|
+
* return function(data) {
|
|
181
|
+
* return data.name && data.email;
|
|
182
|
+
* };
|
|
183
|
+
* `);
|
|
184
|
+
* const isValid = validator({ name: 'John', email: 'john@example.com' });
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
95
187
|
createFunction(code, paramNames = [], options = {}) {
|
|
96
188
|
const context = this.prepareContext(options.context, options.imports);
|
|
97
189
|
const strict = options.strict ?? this.config.defaultStrict;
|
|
@@ -111,85 +203,124 @@ class DynaJs {
|
|
|
111
203
|
throw new Error(`Function creation error: ${error instanceof Error ? error.message : String(error)}`);
|
|
112
204
|
}
|
|
113
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Synchronously execute code with additional temporary imports
|
|
208
|
+
*
|
|
209
|
+
* @template T - The expected return type
|
|
210
|
+
* @param code - The JavaScript code to execute
|
|
211
|
+
* @param imports - Additional imports to merge with defaultImports
|
|
212
|
+
* @param options - Additional execution options
|
|
213
|
+
* @returns The direct result of code execution
|
|
214
|
+
* @throws {Error} If code validation fails or runtime error occurs
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```typescript
|
|
218
|
+
* const result = loader.executeWithImports(`
|
|
219
|
+
* return new CustomComponent();
|
|
220
|
+
* `, {
|
|
221
|
+
* CustomComponent: MyCustomComponent
|
|
222
|
+
* });
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
114
225
|
executeWithImports(code, imports, options = {}) {
|
|
115
226
|
return this.executeSync(code, {
|
|
116
227
|
...options,
|
|
117
228
|
imports: { ...this.config.defaultImports, ...imports }
|
|
118
229
|
});
|
|
119
230
|
}
|
|
231
|
+
/**
|
|
232
|
+
* Asynchronously execute code with additional temporary imports
|
|
233
|
+
*
|
|
234
|
+
* @template T - The expected return type
|
|
235
|
+
* @param code - The JavaScript code to execute
|
|
236
|
+
* @param imports - Additional imports to merge with defaultImports
|
|
237
|
+
* @param options - Additional execution options (including timeout)
|
|
238
|
+
* @returns Promise resolving to the execution result
|
|
239
|
+
* @throws {Error} If code validation fails, execution times out, or runtime error occurs
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```typescript
|
|
243
|
+
* const result = await loader.executeWithImportsAsync(`
|
|
244
|
+
* return await fetchData();
|
|
245
|
+
* `, {
|
|
246
|
+
* fetchData: myFetchFunction
|
|
247
|
+
* }, { timeout: 5000 });
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
120
250
|
async executeWithImportsAsync(code, imports, options = {}) {
|
|
121
251
|
return this.execute(code, {
|
|
122
252
|
...options,
|
|
123
253
|
imports: { ...this.config.defaultImports, ...imports }
|
|
124
254
|
});
|
|
125
255
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
executeWithProxy(code, context, injectedKeys) {
|
|
141
|
-
const injected = {};
|
|
142
|
-
for (const key of injectedKeys) {
|
|
143
|
-
if (context.window && key in context.window) {
|
|
144
|
-
injected[key] = context.window[key];
|
|
145
|
-
}
|
|
146
|
-
else if (key in context) {
|
|
147
|
-
injected[key] = context[key];
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
const sandboxContext = { ...context, ...injected };
|
|
151
|
-
const sandbox = new Proxy(sandboxContext, {
|
|
152
|
-
has(target, key) {
|
|
153
|
-
if (key in target)
|
|
154
|
-
return true;
|
|
155
|
-
if (context.window && key in context.window)
|
|
156
|
-
return true;
|
|
157
|
-
return false;
|
|
158
|
-
},
|
|
159
|
-
get(target, key) {
|
|
160
|
-
if (key in target)
|
|
161
|
-
return target[key];
|
|
162
|
-
if (context.window && key in context.window)
|
|
163
|
-
return context.window[key];
|
|
164
|
-
console.warn(`DynaJs: ${String(key)} not found`);
|
|
165
|
-
return undefined;
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
const wrappedCode = `
|
|
169
|
-
with (sandbox) {
|
|
170
|
-
${Object.keys(sandbox).map(k => `const ${k} = sandbox.${k};`).join('\n')}
|
|
171
|
-
|
|
172
|
-
${code}
|
|
173
|
-
}
|
|
174
|
-
`;
|
|
175
|
-
const fn = new Function('sandbox', wrappedCode);
|
|
176
|
-
return fn(sandbox);
|
|
177
|
-
}
|
|
256
|
+
/**
|
|
257
|
+
* Initialize the singleton instance of DynaJs
|
|
258
|
+
*
|
|
259
|
+
* @param config - Configuration options for the singleton instance
|
|
260
|
+
* @returns The initialized singleton instance
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* ```typescript
|
|
264
|
+
* DynaJs.initialize({
|
|
265
|
+
* defaultImports: { MyClass, MyFunction },
|
|
266
|
+
* validateCode: true
|
|
267
|
+
* });
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
178
270
|
static initialize(config) {
|
|
179
271
|
if (DynaJs.instance == null) {
|
|
180
272
|
DynaJs.instance = new DynaJs(config);
|
|
181
273
|
}
|
|
182
274
|
return DynaJs.instance;
|
|
183
275
|
}
|
|
276
|
+
/**
|
|
277
|
+
* Get the singleton instance of DynaJs
|
|
278
|
+
*
|
|
279
|
+
* @returns The singleton instance
|
|
280
|
+
* @throws {Error} If DynaJs hasn't been initialized
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* ```typescript
|
|
284
|
+
* const loader = DynaJs.getInstance();
|
|
285
|
+
* const result = loader.executeSync(`return 1 + 1`);
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
184
288
|
static getInstance() {
|
|
185
289
|
if (DynaJs.instance == null) {
|
|
186
290
|
throw new Error("DynaJs hasn't been initialized. Call DynaJs.initialize() first.");
|
|
187
291
|
}
|
|
188
292
|
return DynaJs.instance;
|
|
189
293
|
}
|
|
294
|
+
/**
|
|
295
|
+
* Reset the singleton instance (useful for testing)
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* ```typescript
|
|
299
|
+
* DynaJs.reset();
|
|
300
|
+
* DynaJs.initialize(newConfig);
|
|
301
|
+
* ```
|
|
302
|
+
*/
|
|
190
303
|
static reset() {
|
|
191
304
|
DynaJs.instance = null;
|
|
192
305
|
}
|
|
306
|
+
/**
|
|
307
|
+
* Prepare the execution context by merging user context, imports, and applying security filters
|
|
308
|
+
*
|
|
309
|
+
* Execution flow:
|
|
310
|
+
* 1. Create safe context (filtering browser/node APIs based on config)
|
|
311
|
+
* 2. Merge with defaultImports and additional imports
|
|
312
|
+
* 3. Apply allowedGlobals whitelist filter if configured
|
|
313
|
+
*
|
|
314
|
+
* @private
|
|
315
|
+
* @param userContext - User-provided context variables
|
|
316
|
+
* @param imports - Additional imports for this execution
|
|
317
|
+
* @returns The final execution context
|
|
318
|
+
*
|
|
319
|
+
* @remarks
|
|
320
|
+
* If allowedGlobals is set and not empty, only variables in the whitelist will be available.
|
|
321
|
+
* This happens AFTER merging defaultImports, so make sure to include all needed imports
|
|
322
|
+
* in the allowedGlobals array if you use this feature.
|
|
323
|
+
*/
|
|
193
324
|
prepareContext(userContext = {}, imports = {}) {
|
|
194
325
|
let context = createSafeContext(userContext, {
|
|
195
326
|
allowBrowserAPIs: this.config.allowBrowserAPIs,
|
|
@@ -209,5 +340,9 @@ class DynaJs {
|
|
|
209
340
|
return context;
|
|
210
341
|
}
|
|
211
342
|
}
|
|
343
|
+
/**
|
|
344
|
+
* Singleton instance of DynaJs
|
|
345
|
+
* @private
|
|
346
|
+
*/
|
|
212
347
|
DynaJs.instance = null;
|
|
213
348
|
export default DynaJs;
|