@ticatec/dyna-js 0.0.3 → 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 +221 -20
- package/README_CN.md +231 -30
- package/dist/DynaJs.d.ts +200 -5
- package/dist/DynaJs.d.ts.map +1 -1
- package/dist/DynaJs.esm.js +199 -10
- package/dist/DynaJs.js +199 -10
- package/dist/ModuleLoader.d.ts +93 -0
- package/dist/ModuleLoader.d.ts.map +1 -0
- package/dist/ModuleLoader.esm.js +106 -0
- package/dist/ModuleLoader.js +110 -0
- package/dist/types.d.ts +0 -6
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -11,13 +11,13 @@ A TypeScript library for safe dynamic code execution using `new Function()` that
|
|
|
11
11
|
|
|
12
12
|
## Features
|
|
13
13
|
|
|
14
|
-
✅ **Universal Compatibility** - Works in both Node.js and browser environments
|
|
15
|
-
✅ **TypeScript Support** - Full type safety with comprehensive type definitions
|
|
16
|
-
✅ **Singleton Pattern** - Initialize once, use anywhere
|
|
17
|
-
✅ **Module Imports** - Pre-define classes and functions for dynamic code
|
|
18
|
-
✅ **Configurable Security** - Fine-grained control over allowed APIs and operations
|
|
19
|
-
✅ **Multiple Build Formats** - CommonJS and ESM support
|
|
20
|
-
✅ **Performance Monitoring** - Built-in execution time tracking
|
|
14
|
+
✅ **Universal Compatibility** - Works in both Node.js and browser environments
|
|
15
|
+
✅ **TypeScript Support** - Full type safety with comprehensive type definitions
|
|
16
|
+
✅ **Singleton Pattern** - Initialize once, use anywhere
|
|
17
|
+
✅ **Module Imports** - Pre-define classes and functions for dynamic code
|
|
18
|
+
✅ **Configurable Security** - Fine-grained control over allowed APIs and operations
|
|
19
|
+
✅ **Multiple Build Formats** - CommonJS and ESM support
|
|
20
|
+
✅ **Performance Monitoring** - Built-in execution time tracking
|
|
21
21
|
|
|
22
22
|
## Installation
|
|
23
23
|
|
|
@@ -75,7 +75,7 @@ const MyFormClass = loader.executeSync(`
|
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
return CustomForm;
|
|
78
|
-
`)
|
|
78
|
+
`);
|
|
79
79
|
|
|
80
80
|
// Instantiate and use
|
|
81
81
|
const form = new MyFormClass();
|
|
@@ -86,27 +86,27 @@ form.show();
|
|
|
86
86
|
|
|
87
87
|
### Core Methods
|
|
88
88
|
|
|
89
|
-
#### `executeSync<T>(code: string, options?: ExecutionOptions):
|
|
89
|
+
#### `executeSync<T>(code: string, options?: ExecutionOptions): T`
|
|
90
90
|
|
|
91
|
-
Synchronously execute code
|
|
91
|
+
Synchronously execute code and return the result directly.
|
|
92
92
|
|
|
93
93
|
```typescript
|
|
94
|
-
const
|
|
94
|
+
const form = loader.executeSync(`
|
|
95
95
|
const form = new FlexiForm();
|
|
96
96
|
form.setTitle('Dynamic Form');
|
|
97
97
|
return form;
|
|
98
98
|
`);
|
|
99
99
|
|
|
100
|
-
console.log(
|
|
101
|
-
|
|
100
|
+
console.log(form); // FlexiForm instance
|
|
101
|
+
// Execution time is logged to console automatically
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
#### `execute<T>(code: string, options?: ExecutionOptions): Promise<
|
|
104
|
+
#### `execute<T>(code: string, options?: ExecutionOptions): Promise<T>`
|
|
105
105
|
|
|
106
106
|
Asynchronously execute code with timeout support.
|
|
107
107
|
|
|
108
108
|
```typescript
|
|
109
|
-
const
|
|
109
|
+
const form = await loader.execute(`
|
|
110
110
|
return new Promise(resolve => {
|
|
111
111
|
const form = new FlexiForm();
|
|
112
112
|
resolve(form);
|
|
@@ -115,14 +115,16 @@ const result = await loader.execute(`
|
|
|
115
115
|
timeout: 3000,
|
|
116
116
|
context: { customVar: 'value' }
|
|
117
117
|
});
|
|
118
|
+
|
|
119
|
+
console.log(form); // FlexiForm instance
|
|
118
120
|
```
|
|
119
121
|
|
|
120
|
-
#### `executeWithImports<T>(code: string, imports: object, options?: ExecutionOptions):
|
|
122
|
+
#### `executeWithImports<T>(code: string, imports: object, options?: ExecutionOptions): T`
|
|
121
123
|
|
|
122
124
|
Execute code with additional temporary imports.
|
|
123
125
|
|
|
124
126
|
```typescript
|
|
125
|
-
const
|
|
127
|
+
const component = loader.executeWithImports(`
|
|
126
128
|
return new CustomComponent({
|
|
127
129
|
message: 'Hello from dynamic code!'
|
|
128
130
|
});
|
|
@@ -254,7 +256,7 @@ const DynamicFormBuilder = loader.executeSync(`
|
|
|
254
256
|
}
|
|
255
257
|
|
|
256
258
|
return FormBuilder;
|
|
257
|
-
`)
|
|
259
|
+
`);
|
|
258
260
|
|
|
259
261
|
// Use the dynamically created form builder
|
|
260
262
|
const formBuilder = new DynamicFormBuilder({
|
|
@@ -296,6 +298,206 @@ const validation = dynamicValidator({
|
|
|
296
298
|
});
|
|
297
299
|
```
|
|
298
300
|
|
|
301
|
+
## ModuleLoader - Dynamic Module Management
|
|
302
|
+
|
|
303
|
+
`ModuleLoader` is a singleton class for managing dynamic JavaScript modules with automatic caching and version control. It's perfect for loading remote code modules and managing their lifecycle.
|
|
304
|
+
|
|
305
|
+
### Key Features
|
|
306
|
+
|
|
307
|
+
- **Singleton Pattern** - Single instance manages all modules
|
|
308
|
+
- **Version Control** - Automatic digest-based freshness checking
|
|
309
|
+
- **Local Caching** - Stores modules in localStorage for offline access
|
|
310
|
+
- **Lazy Loading** - Modules are only instantiated when needed
|
|
311
|
+
- **Custom Hooks** - Override default behaviors with custom implementations
|
|
312
|
+
|
|
313
|
+
### Basic Usage
|
|
314
|
+
|
|
315
|
+
#### 1. Initialize the ModuleLoader
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
import { ModuleLoader } from '@ticatec/dyna-js';
|
|
319
|
+
|
|
320
|
+
// Define how to load modules from your server
|
|
321
|
+
const loadModuleFromServer = async (moduleInfo) => {
|
|
322
|
+
const response = await fetch(`/api/modules/${moduleInfo.code}`);
|
|
323
|
+
return response.json(); // Should return { code, digest, scriptText }
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
// Initialize the singleton
|
|
327
|
+
const moduleLoader = ModuleLoader.initialize(loadModuleFromServer, {
|
|
328
|
+
// All options are optional - defaults use localStorage
|
|
329
|
+
});
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
#### 2. Check and Update Modules
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
// Check a list of modules and update if needed
|
|
336
|
+
await moduleLoader.checkFreshScripts([
|
|
337
|
+
{ code: 'user-form', digest: 'abc123...' },
|
|
338
|
+
{ code: 'data-grid', digest: 'def456...' },
|
|
339
|
+
{ code: 'chart-widget', digest: 'ghi789...' }
|
|
340
|
+
]);
|
|
341
|
+
|
|
342
|
+
// This will:
|
|
343
|
+
// 1. Check each module's digest against localStorage
|
|
344
|
+
// 2. Download and save modules that have changed
|
|
345
|
+
// 3. Clear the module instance cache
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
#### 3. Create and Use Modules
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
// Import dependencies that the module needs
|
|
352
|
+
import React from 'react';
|
|
353
|
+
import ReactDOM from 'react-dom';
|
|
354
|
+
|
|
355
|
+
// Create the module instance
|
|
356
|
+
const UserForm = moduleLoader.createModule('user-form', {
|
|
357
|
+
React,
|
|
358
|
+
ReactDOM,
|
|
359
|
+
// Add any other dependencies your module needs
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// Use the module (it's now cached for subsequent calls)
|
|
363
|
+
const formInstance = new UserForm({
|
|
364
|
+
title: 'User Registration',
|
|
365
|
+
onSubmit: handleSubmit
|
|
366
|
+
});
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Advanced Configuration
|
|
370
|
+
|
|
371
|
+
#### Custom Module Check Function
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
const moduleLoader = ModuleLoader.initialize(loadModuleFromServer, {
|
|
375
|
+
// Custom logic to check if module needs updating
|
|
376
|
+
moduleCheck: (moduleInfo) => {
|
|
377
|
+
const localVersion = localStorage.getItem(`version:${moduleInfo.code}`);
|
|
378
|
+
return localVersion === moduleInfo.version;
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
#### Custom Storage Implementation
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
// Use your own storage solution (IndexedDB, custom cache, etc.)
|
|
387
|
+
const moduleLoader = ModuleLoader.initialize(loadModuleFromServer, {
|
|
388
|
+
// Custom save function
|
|
389
|
+
saveModule: (moduleData) => {
|
|
390
|
+
myCustomCache.set(moduleData.code, {
|
|
391
|
+
digest: moduleData.digest,
|
|
392
|
+
script: moduleData.scriptText,
|
|
393
|
+
timestamp: Date.now()
|
|
394
|
+
});
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
// Custom load function
|
|
398
|
+
loadLocalModule: (moduleCode) => {
|
|
399
|
+
const cached = myCustomCache.get(moduleCode);
|
|
400
|
+
return cached ? cached.script : null;
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Complete Example
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
import { ModuleLoader, initializeDynaJs } from '@ticatec/dyna-js';
|
|
409
|
+
import React from 'react';
|
|
410
|
+
import ReactDOM from 'react-dom';
|
|
411
|
+
|
|
412
|
+
// 1. Initialize DynaJs with your base imports
|
|
413
|
+
initializeDynaJs({
|
|
414
|
+
defaultImports: {
|
|
415
|
+
Dialog: DialogClass,
|
|
416
|
+
MessageBox: MessageBoxClass
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// 2. Setup module loader
|
|
421
|
+
const loadModule = async (moduleInfo) => {
|
|
422
|
+
const response = await fetch(`/api/modules/${moduleInfo.code}`, {
|
|
423
|
+
method: 'POST',
|
|
424
|
+
headers: { 'Content-Type': 'application/json' },
|
|
425
|
+
body: JSON.stringify({ digest: moduleInfo.digest })
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
if (!response.ok) throw new Error('Failed to load module');
|
|
429
|
+
return response.json();
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
const moduleLoader = ModuleLoader.initialize(loadModule, {});
|
|
433
|
+
|
|
434
|
+
// 3. On application startup, check for module updates
|
|
435
|
+
async function initializeApp() {
|
|
436
|
+
try {
|
|
437
|
+
// Get module manifest from server
|
|
438
|
+
const manifest = await fetch('/api/modules/manifest').then(r => r.json());
|
|
439
|
+
|
|
440
|
+
// Update all modules that have changed
|
|
441
|
+
await moduleLoader.checkFreshScripts(manifest.modules);
|
|
442
|
+
|
|
443
|
+
console.log('All modules are up to date!');
|
|
444
|
+
} catch (error) {
|
|
445
|
+
console.error('Failed to update modules:', error);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// 4. Load and use modules on demand
|
|
450
|
+
function loadUserForm() {
|
|
451
|
+
// This will use the cached version from localStorage
|
|
452
|
+
const UserForm = moduleLoader.createModule('user-form', {
|
|
453
|
+
React,
|
|
454
|
+
ReactDOM,
|
|
455
|
+
Dialog: DialogClass,
|
|
456
|
+
MessageBox: MessageBoxClass
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
// Subsequent calls return the same cached instance
|
|
460
|
+
const form = new UserForm();
|
|
461
|
+
form.render();
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Initialize
|
|
465
|
+
initializeApp().then(() => {
|
|
466
|
+
loadUserForm();
|
|
467
|
+
});
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### Module Data Format
|
|
471
|
+
|
|
472
|
+
Your server should return module data in this format:
|
|
473
|
+
|
|
474
|
+
```typescript
|
|
475
|
+
interface ModuleData {
|
|
476
|
+
code: string; // Unique module identifier
|
|
477
|
+
digest: string; // Hash/version identifier (e.g., MD5, SHA-256)
|
|
478
|
+
scriptText: string; // The actual JavaScript code to execute
|
|
479
|
+
}
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### Best Practices
|
|
483
|
+
|
|
484
|
+
1. **Version Control**: Use content-based hashing (MD5, SHA-256) for the digest field
|
|
485
|
+
2. **Graceful Degradation**: Handle network failures when loading modules
|
|
486
|
+
3. **Dependency Injection**: Pass all required dependencies in the imports object
|
|
487
|
+
4. **Cache Invalidation**: Call `checkFreshScripts()` on app startup and periodically
|
|
488
|
+
5. **Error Handling**: Wrap module creation in try-catch blocks
|
|
489
|
+
|
|
490
|
+
```typescript
|
|
491
|
+
try {
|
|
492
|
+
const module = moduleLoader.createModule('my-module', imports);
|
|
493
|
+
const instance = new module();
|
|
494
|
+
instance.run();
|
|
495
|
+
} catch (error) {
|
|
496
|
+
console.error('Module failed to load or execute:', error);
|
|
497
|
+
// Fallback to default behavior
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
299
501
|
## Error Handling
|
|
300
502
|
|
|
301
503
|
```typescript
|
|
@@ -306,7 +508,7 @@ try {
|
|
|
306
508
|
`);
|
|
307
509
|
} catch (error) {
|
|
308
510
|
console.error('Execution failed:', error.message);
|
|
309
|
-
// Error includes execution time and detailed error information
|
|
511
|
+
// Error message includes execution time and detailed error information
|
|
310
512
|
}
|
|
311
513
|
```
|
|
312
514
|
|
|
@@ -325,7 +527,6 @@ Full TypeScript support with comprehensive type definitions:
|
|
|
325
527
|
```typescript
|
|
326
528
|
import {
|
|
327
529
|
DynaJs,
|
|
328
|
-
ExecutionResult,
|
|
329
530
|
ExecutionOptions,
|
|
330
531
|
ModuleImports
|
|
331
532
|
} from '@ticatec/dyna-js';
|
package/README_CN.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/js/@ticatec%2Fdyna-js)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](http://www.typescriptlang.org
|
|
5
|
+
[](http://www.typescriptlang.org())
|
|
6
6
|
[](https://nodejs.org/)
|
|
7
7
|
|
|
8
8
|
[English Documentation](README.md)
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
|
|
12
12
|
## 功能特性
|
|
13
13
|
|
|
14
|
-
✅ **通用兼容性** - 同时支持 Node.js 和浏览器环境
|
|
15
|
-
✅ **TypeScript 支持** - 完整的类型安全和类型定义
|
|
16
|
-
✅ **单例模式** - 一次初始化,全局使用
|
|
17
|
-
✅ **模块导入** - 为动态代码预定义类和函数
|
|
18
|
-
✅ **可配置安全性** - 对允许的 API 和操作进行细粒度控制
|
|
19
|
-
✅ **多种构建格式** - 支持 CommonJS 和 ESM
|
|
14
|
+
✅ **通用兼容性** - 同时支持 Node.js 和浏览器环境
|
|
15
|
+
✅ **TypeScript 支持** - 完整的类型安全和类型定义
|
|
16
|
+
✅ **单例模式** - 一次初始化,全局使用
|
|
17
|
+
✅ **模块导入** - 为动态代码预定义类和函数
|
|
18
|
+
✅ **可配置安全性** - 对允许的 API 和操作进行细粒度控制
|
|
19
|
+
✅ **多种构建格式** - 支持 CommonJS 和 ESM
|
|
20
20
|
✅ **性能监控** - 内置执行时间跟踪
|
|
21
21
|
|
|
22
22
|
## 安装
|
|
@@ -75,7 +75,7 @@ const MyFormClass = loader.executeSync(`
|
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
return CustomForm;
|
|
78
|
-
`)
|
|
78
|
+
`);
|
|
79
79
|
|
|
80
80
|
// 实例化并使用
|
|
81
81
|
const form = new MyFormClass();
|
|
@@ -86,27 +86,27 @@ form.show();
|
|
|
86
86
|
|
|
87
87
|
### 核心方法
|
|
88
88
|
|
|
89
|
-
#### `executeSync<T>(code: string, options?: ExecutionOptions):
|
|
89
|
+
#### `executeSync<T>(code: string, options?: ExecutionOptions): T`
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
同步执行代码并直接返回结果。
|
|
92
92
|
|
|
93
93
|
```typescript
|
|
94
|
-
const
|
|
94
|
+
const form = loader.executeSync(`
|
|
95
95
|
const form = new FlexiForm();
|
|
96
96
|
form.setTitle('动态表单');
|
|
97
97
|
return form;
|
|
98
98
|
`);
|
|
99
99
|
|
|
100
|
-
console.log(
|
|
101
|
-
|
|
100
|
+
console.log(form); // FlexiForm 实例
|
|
101
|
+
// 执行时间会自动记录到控制台
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
#### `execute<T>(code: string, options?: ExecutionOptions): Promise<
|
|
104
|
+
#### `execute<T>(code: string, options?: ExecutionOptions): Promise<T>`
|
|
105
105
|
|
|
106
106
|
异步执行代码,支持超时控制。
|
|
107
107
|
|
|
108
108
|
```typescript
|
|
109
|
-
const
|
|
109
|
+
const form = await loader.execute(`
|
|
110
110
|
return new Promise(resolve => {
|
|
111
111
|
const form = new FlexiForm();
|
|
112
112
|
resolve(form);
|
|
@@ -115,14 +115,16 @@ const result = await loader.execute(`
|
|
|
115
115
|
timeout: 3000,
|
|
116
116
|
context: { customVar: 'value' }
|
|
117
117
|
});
|
|
118
|
+
|
|
119
|
+
console.log(form); // FlexiForm 实例
|
|
118
120
|
```
|
|
119
121
|
|
|
120
|
-
#### `executeWithImports<T>(code: string, imports: object, options?: ExecutionOptions):
|
|
122
|
+
#### `executeWithImports<T>(code: string, imports: object, options?: ExecutionOptions): T`
|
|
121
123
|
|
|
122
124
|
使用额外的临时导入执行代码。
|
|
123
125
|
|
|
124
126
|
```typescript
|
|
125
|
-
const
|
|
127
|
+
const component = loader.executeWithImports(`
|
|
126
128
|
return new CustomComponent({
|
|
127
129
|
message: '来自动态代码的问候!'
|
|
128
130
|
});
|
|
@@ -254,7 +256,7 @@ const DynamicFormBuilder = loader.executeSync(`
|
|
|
254
256
|
}
|
|
255
257
|
|
|
256
258
|
return FormBuilder;
|
|
257
|
-
`)
|
|
259
|
+
`);
|
|
258
260
|
|
|
259
261
|
// 使用动态创建的表单构建器
|
|
260
262
|
const formBuilder = new DynamicFormBuilder({
|
|
@@ -273,15 +275,15 @@ formBuilder
|
|
|
273
275
|
const dynamicValidator = loader.createFunction(`
|
|
274
276
|
return function validateForm(formData) {
|
|
275
277
|
const errors = [];
|
|
276
|
-
|
|
278
|
+
|
|
277
279
|
if (!formData.name) {
|
|
278
280
|
errors.push('姓名是必填项');
|
|
279
281
|
}
|
|
280
|
-
|
|
282
|
+
|
|
281
283
|
if (!formData.email || !formData.email.includes('@')) {
|
|
282
284
|
errors.push('需要有效的邮箱地址');
|
|
283
285
|
}
|
|
284
|
-
|
|
286
|
+
|
|
285
287
|
return {
|
|
286
288
|
isValid: errors.length === 0,
|
|
287
289
|
errors: errors
|
|
@@ -296,6 +298,206 @@ const validation = dynamicValidator({
|
|
|
296
298
|
});
|
|
297
299
|
```
|
|
298
300
|
|
|
301
|
+
## ModuleLoader - 动态模块管理
|
|
302
|
+
|
|
303
|
+
`ModuleLoader` 是一个单例类,用于管理具有自动缓存和版本控制的动态 JavaScript 模块。它非常适合加载远程代码模块并管理它们的生命周期。
|
|
304
|
+
|
|
305
|
+
### 核心特性
|
|
306
|
+
|
|
307
|
+
- **单例模式** - 单个实例管理所有模块
|
|
308
|
+
- **版本控制** - 基于摘要的自动新鲜度检查
|
|
309
|
+
- **本地缓存** - 将模块存储在 localStorage 中以供离线访问
|
|
310
|
+
- **懒加载** - 模块仅在需要时才实例化
|
|
311
|
+
- **自定义钩子** - 使用自定义实现覆盖默认行为
|
|
312
|
+
|
|
313
|
+
### 基础用法
|
|
314
|
+
|
|
315
|
+
#### 1. 初始化 ModuleLoader
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
import { ModuleLoader } from '@ticatec/dyna-js';
|
|
319
|
+
|
|
320
|
+
// 定义如何从服务器加载模块
|
|
321
|
+
const loadModuleFromServer = async (moduleInfo) => {
|
|
322
|
+
const response = await fetch(`/api/modules/${moduleInfo.code}`);
|
|
323
|
+
return response.json(); // 应该返回 { code, digest, scriptText }
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
// 初始化单例
|
|
327
|
+
const moduleLoader = ModuleLoader.initialize(loadModuleFromServer, {
|
|
328
|
+
// 所有选项都是可选的 - 默认使用 localStorage
|
|
329
|
+
});
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
#### 2. 检查和更新模块
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
// 检查模块列表并在需要时更新
|
|
336
|
+
await moduleLoader.checkFreshScripts([
|
|
337
|
+
{ code: 'user-form', digest: 'abc123...' },
|
|
338
|
+
{ code: 'data-grid', digest: 'def456...' },
|
|
339
|
+
{ code: 'chart-widget', digest: 'ghi789...' }
|
|
340
|
+
]);
|
|
341
|
+
|
|
342
|
+
// 这将:
|
|
343
|
+
// 1. 检查每个模块的摘要与 localStorage 中的对比
|
|
344
|
+
// 2. 下载并保存已更改的模块
|
|
345
|
+
// 3. 清除模块实例缓存
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
#### 3. 创建和使用模块
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
// 导入模块需要的依赖项
|
|
352
|
+
import React from 'react';
|
|
353
|
+
import ReactDOM from 'react-dom';
|
|
354
|
+
|
|
355
|
+
// 创建模块实例
|
|
356
|
+
const UserForm = moduleLoader.createModule('user-form', {
|
|
357
|
+
React,
|
|
358
|
+
ReactDOM,
|
|
359
|
+
// 添加模块需要的任何其他依赖项
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// 使用模块(它现在已被缓存以供后续调用)
|
|
363
|
+
const formInstance = new UserForm({
|
|
364
|
+
title: '用户注册',
|
|
365
|
+
onSubmit: handleSubmit
|
|
366
|
+
});
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### 高级配置
|
|
370
|
+
|
|
371
|
+
#### 自定义模块检查函数
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
const moduleLoader = ModuleLoader.initialize(loadModuleFromServer, {
|
|
375
|
+
// 自定义逻辑来检查模块是否需要更新
|
|
376
|
+
moduleCheck: (moduleInfo) => {
|
|
377
|
+
const localVersion = localStorage.getItem(`version:${moduleInfo.code}`);
|
|
378
|
+
return localVersion === moduleInfo.version;
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
#### 自定义存储实现
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
// 使用您自己的存储解决方案(IndexedDB、自定义缓存等)
|
|
387
|
+
const moduleLoader = ModuleLoader.initialize(loadModuleFromServer, {
|
|
388
|
+
// 自定义保存函数
|
|
389
|
+
saveModule: (moduleData) => {
|
|
390
|
+
myCustomCache.set(moduleData.code, {
|
|
391
|
+
digest: moduleData.digest,
|
|
392
|
+
script: moduleData.scriptText,
|
|
393
|
+
timestamp: Date.now()
|
|
394
|
+
});
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
// 自定义加载函数
|
|
398
|
+
loadLocalModule: (moduleCode) => {
|
|
399
|
+
const cached = myCustomCache.get(moduleCode);
|
|
400
|
+
return cached ? cached.script : null;
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### 完整示例
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
import { ModuleLoader, initializeDynaJs } from '@ticatec/dyna-js';
|
|
409
|
+
import React from 'react';
|
|
410
|
+
import ReactDOM from 'react-dom';
|
|
411
|
+
|
|
412
|
+
// 1. 使用基础导入初始化 DynaJs
|
|
413
|
+
initializeDynaJs({
|
|
414
|
+
defaultImports: {
|
|
415
|
+
Dialog: DialogClass,
|
|
416
|
+
MessageBox: MessageBoxClass
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// 2. 设置模块加载器
|
|
421
|
+
const loadModule = async (moduleInfo) => {
|
|
422
|
+
const response = await fetch(`/api/modules/${moduleInfo.code}`, {
|
|
423
|
+
method: 'POST',
|
|
424
|
+
headers: { 'Content-Type': 'application/json' },
|
|
425
|
+
body: JSON.stringify({ digest: moduleInfo.digest })
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
if (!response.ok) throw new Error('加载模块失败');
|
|
429
|
+
return response.json();
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
const moduleLoader = ModuleLoader.initialize(loadModule, {});
|
|
433
|
+
|
|
434
|
+
// 3. 在应用启动时,检查模块更新
|
|
435
|
+
async function initializeApp() {
|
|
436
|
+
try {
|
|
437
|
+
// 从服务器获取模块清单
|
|
438
|
+
const manifest = await fetch('/api/modules/manifest').then(r => r.json());
|
|
439
|
+
|
|
440
|
+
// 更新所有已更改的模块
|
|
441
|
+
await moduleLoader.checkFreshScripts(manifest.modules);
|
|
442
|
+
|
|
443
|
+
console.log('所有模块都是最新的!');
|
|
444
|
+
} catch (error) {
|
|
445
|
+
console.error('更新模块失败:', error);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// 4. 按需加载和使用模块
|
|
450
|
+
function loadUserForm() {
|
|
451
|
+
// 这将使用 localStorage 中的缓存版本
|
|
452
|
+
const UserForm = moduleLoader.createModule('user-form', {
|
|
453
|
+
React,
|
|
454
|
+
ReactDOM,
|
|
455
|
+
Dialog: DialogClass,
|
|
456
|
+
MessageBox: MessageBoxClass
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
// 后续调用返回相同的缓存实例
|
|
460
|
+
const form = new UserForm();
|
|
461
|
+
form.render();
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// 初始化
|
|
465
|
+
initializeApp().then(() => {
|
|
466
|
+
loadUserForm();
|
|
467
|
+
});
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### 模块数据格式
|
|
471
|
+
|
|
472
|
+
您的服务器应以此格式返回模块数据:
|
|
473
|
+
|
|
474
|
+
```typescript
|
|
475
|
+
interface ModuleData {
|
|
476
|
+
code: string; // 唯一的模块标识符
|
|
477
|
+
digest: string; // 哈希/版本标识符(例如 MD5、SHA-256)
|
|
478
|
+
scriptText: string; // 要执行的实际 JavaScript 代码
|
|
479
|
+
}
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### 最佳实践
|
|
483
|
+
|
|
484
|
+
1. **版本控制**:对 digest 字段使用基于内容的哈希(MD5、SHA-256)
|
|
485
|
+
2. **优雅降级**:处理加载模块时的网络故障
|
|
486
|
+
3. **依赖注入**:在 imports 对象中传递所有必需的依赖项
|
|
487
|
+
4. **缓存失效**:在应用启动时和定期调用 `checkFreshScripts()`
|
|
488
|
+
5. **错误处理**:将模块创建包装在 try-catch 块中
|
|
489
|
+
|
|
490
|
+
```typescript
|
|
491
|
+
try {
|
|
492
|
+
const module = moduleLoader.createModule('my-module', imports);
|
|
493
|
+
const instance = new module();
|
|
494
|
+
instance.run();
|
|
495
|
+
} catch (error) {
|
|
496
|
+
console.error('模块加载或执行失败:', error);
|
|
497
|
+
// 回退到默认行为
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
299
501
|
## 错误处理
|
|
300
502
|
|
|
301
503
|
```typescript
|
|
@@ -306,7 +508,7 @@ try {
|
|
|
306
508
|
`);
|
|
307
509
|
} catch (error) {
|
|
308
510
|
console.error('执行失败:', error.message);
|
|
309
|
-
//
|
|
511
|
+
// 错误信息包含执行时间和详细的错误信息
|
|
310
512
|
}
|
|
311
513
|
```
|
|
312
514
|
|
|
@@ -323,11 +525,10 @@ try {
|
|
|
323
525
|
具有完整类型定义的 TypeScript 支持:
|
|
324
526
|
|
|
325
527
|
```typescript
|
|
326
|
-
import {
|
|
528
|
+
import {
|
|
327
529
|
DynaJs,
|
|
328
|
-
ExecutionResult,
|
|
329
530
|
ExecutionOptions,
|
|
330
|
-
ModuleImports
|
|
531
|
+
ModuleImports
|
|
331
532
|
} from '@ticatec/dyna-js';
|
|
332
533
|
```
|
|
333
534
|
|
|
@@ -364,9 +565,9 @@ MIT 许可证 - 详情请查看 [LICENSE](LICENSE) 文件。
|
|
|
364
565
|
### 动态表单创建
|
|
365
566
|
```typescript
|
|
366
567
|
// 适合创建各种动态表单组件
|
|
367
|
-
const ContactForm = loader.executeSync(`...`)
|
|
368
|
-
const SurveyForm = loader.executeSync(`...`)
|
|
369
|
-
const RegistrationForm = loader.executeSync(`...`)
|
|
568
|
+
const ContactForm = loader.executeSync(`...`);
|
|
569
|
+
const SurveyForm = loader.executeSync(`...`);
|
|
570
|
+
const RegistrationForm = loader.executeSync(`...`);
|
|
370
571
|
```
|
|
371
572
|
|
|
372
573
|
### 业务规则执行
|
|
@@ -377,7 +578,7 @@ const businessRule = loader.executeSync(`
|
|
|
377
578
|
// 复杂的业务逻辑
|
|
378
579
|
return processBusinessRule(data);
|
|
379
580
|
};
|
|
380
|
-
`)
|
|
581
|
+
`);
|
|
381
582
|
```
|
|
382
583
|
|
|
383
584
|
### 模板渲染
|
|
@@ -391,5 +592,5 @@ const templateEngine = loader.executeSync(`
|
|
|
391
592
|
}
|
|
392
593
|
}
|
|
393
594
|
return TemplateEngine;
|
|
394
|
-
`)
|
|
595
|
+
`);
|
|
395
596
|
```
|