@ticatec/dyna-js 0.0.4 → 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 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
 
@@ -298,6 +298,206 @@ const validation = dynamicValidator({
298
298
  });
299
299
  ```
300
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
+
301
501
  ## Error Handling
302
502
 
303
503
  ```typescript
@@ -327,7 +527,6 @@ Full TypeScript support with comprehensive type definitions:
327
527
  ```typescript
328
528
  import {
329
529
  DynaJs,
330
- ExecutionResult,
331
530
  ExecutionOptions,
332
531
  ModuleImports
333
532
  } from '@ticatec/dyna-js';
package/README_CN.md CHANGED
@@ -11,13 +11,13 @@
11
11
 
12
12
  ## 功能特性
13
13
 
14
- ✅ **通用兼容性** - 同时支持 Node.js 和浏览器环境
15
- ✅ **TypeScript 支持** - 完整的类型安全和类型定义
16
- ✅ **单例模式** - 一次初始化,全局使用
17
- ✅ **模块导入** - 为动态代码预定义类和函数
18
- ✅ **可配置安全性** - 对允许的 API 和操作进行细粒度控制
19
- ✅ **多种构建格式** - 支持 CommonJS 和 ESM
20
- ✅ **性能监控** - 内置执行时间跟踪
14
+ ✅ **通用兼容性** - 同时支持 Node.js 和浏览器环境
15
+ ✅ **TypeScript 支持** - 完整的类型安全和类型定义
16
+ ✅ **单例模式** - 一次初始化,全局使用
17
+ ✅ **模块导入** - 为动态代码预定义类和函数
18
+ ✅ **可配置安全性** - 对允许的 API 和操作进行细粒度控制
19
+ ✅ **多种构建格式** - 支持 CommonJS 和 ESM
20
+ ✅ **性能监控** - 内置执行时间跟踪
21
21
 
22
22
  ## 安装
23
23
 
@@ -298,6 +298,206 @@ const validation = dynamicValidator({
298
298
  });
299
299
  ```
300
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
+
301
501
  ## 错误处理
302
502
 
303
503
  ```typescript
@@ -327,7 +527,6 @@ try {
327
527
  ```typescript
328
528
  import {
329
529
  DynaJs,
330
- ExecutionResult,
331
530
  ExecutionOptions,
332
531
  ModuleImports
333
532
  } from '@ticatec/dyna-js';
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Function type for checking if a module needs to be updated
3
+ * @param data - Module data containing code and digest information
4
+ * @returns true if the module is up-to-date, false if it needs to be reloaded
5
+ */
6
+ export type ModuleCheck = (data: any) => boolean;
7
+ /**
8
+ * Function type for loading a module from remote source
9
+ * @param data - Module data containing information needed to load the module
10
+ * @returns Promise that resolves to the loaded module data
11
+ */
12
+ export type LoadModule = (data: any) => Promise<any>;
13
+ /**
14
+ * Function type for saving a module to local storage
15
+ * @param data - Module data to be saved
16
+ */
17
+ export type SaveModuleToLocalStorage = (data: any) => void;
18
+ /**
19
+ * Function type for loading a module from local storage
20
+ * @param data - Module identifier or data
21
+ * @returns The module script text
22
+ */
23
+ export type LoadLocalModule = (data: any) => string;
24
+ /**
25
+ * Configuration options for ModuleLoader
26
+ */
27
+ export interface ModuleLoaderOptions {
28
+ /** Optional custom module check function */
29
+ moduleCheck?: ModuleCheck;
30
+ /** Optional custom save module function */
31
+ saveModule?: SaveModuleToLocalStorage;
32
+ /** Optional custom load local module function */
33
+ loadLocalModule?: LoadLocalModule;
34
+ }
35
+ /**
36
+ * ModuleLoader is a singleton class for managing dynamic module loading with caching
37
+ * It supports checking for module updates, loading from remote sources, and caching in localStorage
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * const loader = ModuleLoader.initialize(
42
+ * async (data) => fetch(`/api/modules/${data.code}`).then(r => r.json()),
43
+ * {}
44
+ * );
45
+ *
46
+ * await loader.checkFreshScripts([{ code: 'module1', digest: 'abc123' }]);
47
+ * const module = loader.createModule('module1', { React, ReactDOM });
48
+ * ```
49
+ */
50
+ export default class ModuleLoader {
51
+ private static instance;
52
+ private readonly loadModule;
53
+ private readonly moduleCheck;
54
+ private readonly saveModule;
55
+ private readonly loadLocalModule;
56
+ private modulesMap;
57
+ /**
58
+ * Private constructor for singleton pattern
59
+ * @param loadModule - Function to load module from remote
60
+ * @param moduleCheck - Function to check module freshness
61
+ * @param saveModule - Function to save module locally
62
+ * @param loadLocalModule - Function to load module from local storage
63
+ */
64
+ private constructor();
65
+ /**
66
+ * Initialize the ModuleLoader singleton instance
67
+ * @param loadModule - Function to load module from remote source
68
+ * @param options - Configuration options with optional custom implementations
69
+ * @returns The singleton ModuleLoader instance
70
+ */
71
+ static initialize(loadModule: LoadModule, options: ModuleLoaderOptions): ModuleLoader;
72
+ /**
73
+ * Check and update modules that are not fresh
74
+ * Iterates through the list, checks each module, and reloads if needed
75
+ * Clears the module cache after checking all modules
76
+ *
77
+ * @param list - Array of module data objects to check
78
+ * @returns Promise that resolves when all modules are checked and updated
79
+ * ```
80
+ */
81
+ checkFreshScripts(list: Array<any>): Promise<void>;
82
+ /**
83
+ * Create or retrieve a cached module instance
84
+ * Loads the module script from local storage and executes it with provided imports
85
+ * Caches the result for subsequent calls
86
+ *
87
+ * @param key - The module key/code to load
88
+ * @param imports - Object containing dependencies to inject into the module
89
+ * @returns The instantiated module
90
+ */
91
+ createModule(key: string, imports: any): any;
92
+ }
93
+ //# sourceMappingURL=ModuleLoader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModuleLoader.d.ts","sourceRoot":"","sources":["../src/ModuleLoader.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC;AAEjD;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;AAErD;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;AAE3D;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,GAAG,KAAK,MAAM,CAAC;AA+BpD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,4CAA4C;IAC5C,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,2CAA2C;IAC3C,UAAU,CAAC,EAAE,wBAAwB,CAAC;IACtC,iDAAiD;IACjD,eAAe,CAAC,EAAE,eAAe,CAAA;CACpC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,OAAO,OAAO,YAAY;IAE7B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA2B;IACtD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,UAAU,CAAmB;IAErC;;;;;;OAMG;IACH,OAAO;IAQP;;;;;OAKG;IACH,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,mBAAmB,GAAG,YAAY;IAQrF;;;;;;;;OAQG;IACG,iBAAiB,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAC;IAUxC;;;;;;;;OAQG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,GAAG;CAU/C"}
@@ -0,0 +1,106 @@
1
+ import { getDynaJs } from "./index";
2
+ /**
3
+ * Default module check function that compares message digest
4
+ * @param data - Module data containing code and digest
5
+ * @returns true if the local digest matches the remote digest
6
+ */
7
+ const checkMessageDigest = (data) => {
8
+ let localDigest = window.localStorage.getItem(`md:${data.code}`);
9
+ return localDigest == data.digest;
10
+ };
11
+ /**
12
+ * Default function to save module to localStorage
13
+ * Saves both the module digest and script text
14
+ * @param data - Module data containing code, digest, and scriptText
15
+ */
16
+ const saveModule = (data) => {
17
+ window.localStorage.setItem(`md:${data.code}`, data.digest);
18
+ window.localStorage.setItem(`script:${data.code}`, data.scriptText);
19
+ };
20
+ /**
21
+ * Default function to load module script from localStorage
22
+ * @param key - Module key/code
23
+ * @returns The module script text
24
+ */
25
+ const loadLocalModule = (key) => {
26
+ return window.localStorage.getItem(`script:${key}`);
27
+ };
28
+ /**
29
+ * ModuleLoader is a singleton class for managing dynamic module loading with caching
30
+ * It supports checking for module updates, loading from remote sources, and caching in localStorage
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * const loader = ModuleLoader.initialize(
35
+ * async (data) => fetch(`/api/modules/${data.code}`).then(r => r.json()),
36
+ * {}
37
+ * );
38
+ *
39
+ * await loader.checkFreshScripts([{ code: 'module1', digest: 'abc123' }]);
40
+ * const module = loader.createModule('module1', { React, ReactDOM });
41
+ * ```
42
+ */
43
+ export default class ModuleLoader {
44
+ /**
45
+ * Private constructor for singleton pattern
46
+ * @param loadModule - Function to load module from remote
47
+ * @param moduleCheck - Function to check module freshness
48
+ * @param saveModule - Function to save module locally
49
+ * @param loadLocalModule - Function to load module from local storage
50
+ */
51
+ constructor(loadModule, moduleCheck, saveModule, loadLocalModule) {
52
+ this.loadModule = loadModule;
53
+ this.moduleCheck = moduleCheck;
54
+ this.saveModule = saveModule;
55
+ this.loadLocalModule = loadLocalModule;
56
+ this.modulesMap = new Map();
57
+ }
58
+ /**
59
+ * Initialize the ModuleLoader singleton instance
60
+ * @param loadModule - Function to load module from remote source
61
+ * @param options - Configuration options with optional custom implementations
62
+ * @returns The singleton ModuleLoader instance
63
+ */
64
+ static initialize(loadModule, options) {
65
+ if (ModuleLoader.instance == null) {
66
+ ModuleLoader.instance = new ModuleLoader(loadModule, options.moduleCheck ?? checkMessageDigest, options.saveModule ?? saveModule, options.loadLocalModule ?? loadLocalModule);
67
+ }
68
+ return ModuleLoader.instance;
69
+ }
70
+ /**
71
+ * Check and update modules that are not fresh
72
+ * Iterates through the list, checks each module, and reloads if needed
73
+ * Clears the module cache after checking all modules
74
+ *
75
+ * @param list - Array of module data objects to check
76
+ * @returns Promise that resolves when all modules are checked and updated
77
+ * ```
78
+ */
79
+ async checkFreshScripts(list) {
80
+ for (let item of list) {
81
+ if (!this.moduleCheck(item)) {
82
+ let data = await this.loadModule(item);
83
+ this.saveModule(data);
84
+ }
85
+ }
86
+ this.modulesMap.clear();
87
+ }
88
+ /**
89
+ * Create or retrieve a cached module instance
90
+ * Loads the module script from local storage and executes it with provided imports
91
+ * Caches the result for subsequent calls
92
+ *
93
+ * @param key - The module key/code to load
94
+ * @param imports - Object containing dependencies to inject into the module
95
+ * @returns The instantiated module
96
+ */
97
+ createModule(key, imports) {
98
+ let module = this.modulesMap.get(key);
99
+ if (module == null) {
100
+ let scriptText = this.loadLocalModule(key);
101
+ module = getDynaJs().executeSync(scriptText, { imports });
102
+ this.modulesMap.set(key, module);
103
+ }
104
+ return module;
105
+ }
106
+ }
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const index_1 = require("./index");
4
+ /**
5
+ * Default module check function that compares message digest
6
+ * @param data - Module data containing code and digest
7
+ * @returns true if the local digest matches the remote digest
8
+ */
9
+ const checkMessageDigest = (data) => {
10
+ let localDigest = window.localStorage.getItem(`md:${data.code}`);
11
+ return localDigest == data.digest;
12
+ };
13
+ /**
14
+ * Default function to save module to localStorage
15
+ * Saves both the module digest and script text
16
+ * @param data - Module data containing code, digest, and scriptText
17
+ */
18
+ const saveModule = (data) => {
19
+ window.localStorage.setItem(`md:${data.code}`, data.digest);
20
+ window.localStorage.setItem(`script:${data.code}`, data.scriptText);
21
+ };
22
+ /**
23
+ * Default function to load module script from localStorage
24
+ * @param key - Module key/code
25
+ * @returns The module script text
26
+ */
27
+ const loadLocalModule = (key) => {
28
+ return window.localStorage.getItem(`script:${key}`);
29
+ };
30
+ /**
31
+ * ModuleLoader is a singleton class for managing dynamic module loading with caching
32
+ * It supports checking for module updates, loading from remote sources, and caching in localStorage
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const loader = ModuleLoader.initialize(
37
+ * async (data) => fetch(`/api/modules/${data.code}`).then(r => r.json()),
38
+ * {}
39
+ * );
40
+ *
41
+ * await loader.checkFreshScripts([{ code: 'module1', digest: 'abc123' }]);
42
+ * const module = loader.createModule('module1', { React, ReactDOM });
43
+ * ```
44
+ */
45
+ class ModuleLoader {
46
+ /**
47
+ * Private constructor for singleton pattern
48
+ * @param loadModule - Function to load module from remote
49
+ * @param moduleCheck - Function to check module freshness
50
+ * @param saveModule - Function to save module locally
51
+ * @param loadLocalModule - Function to load module from local storage
52
+ */
53
+ constructor(loadModule, moduleCheck, saveModule, loadLocalModule) {
54
+ this.loadModule = loadModule;
55
+ this.moduleCheck = moduleCheck;
56
+ this.saveModule = saveModule;
57
+ this.loadLocalModule = loadLocalModule;
58
+ this.modulesMap = new Map();
59
+ }
60
+ /**
61
+ * Initialize the ModuleLoader singleton instance
62
+ * @param loadModule - Function to load module from remote source
63
+ * @param options - Configuration options with optional custom implementations
64
+ * @returns The singleton ModuleLoader instance
65
+ */
66
+ static initialize(loadModule, options) {
67
+ var _a, _b, _c;
68
+ if (ModuleLoader.instance == null) {
69
+ ModuleLoader.instance = new ModuleLoader(loadModule, (_a = options.moduleCheck) !== null && _a !== void 0 ? _a : checkMessageDigest, (_b = options.saveModule) !== null && _b !== void 0 ? _b : saveModule, (_c = options.loadLocalModule) !== null && _c !== void 0 ? _c : loadLocalModule);
70
+ }
71
+ return ModuleLoader.instance;
72
+ }
73
+ /**
74
+ * Check and update modules that are not fresh
75
+ * Iterates through the list, checks each module, and reloads if needed
76
+ * Clears the module cache after checking all modules
77
+ *
78
+ * @param list - Array of module data objects to check
79
+ * @returns Promise that resolves when all modules are checked and updated
80
+ * ```
81
+ */
82
+ async checkFreshScripts(list) {
83
+ for (let item of list) {
84
+ if (!this.moduleCheck(item)) {
85
+ let data = await this.loadModule(item);
86
+ this.saveModule(data);
87
+ }
88
+ }
89
+ this.modulesMap.clear();
90
+ }
91
+ /**
92
+ * Create or retrieve a cached module instance
93
+ * Loads the module script from local storage and executes it with provided imports
94
+ * Caches the result for subsequent calls
95
+ *
96
+ * @param key - The module key/code to load
97
+ * @param imports - Object containing dependencies to inject into the module
98
+ * @returns The instantiated module
99
+ */
100
+ createModule(key, imports) {
101
+ let module = this.modulesMap.get(key);
102
+ if (module == null) {
103
+ let scriptText = this.loadLocalModule(key);
104
+ module = (0, index_1.getDynaJs)().executeSync(scriptText, { imports });
105
+ this.modulesMap.set(key, module);
106
+ }
107
+ return module;
108
+ }
109
+ }
110
+ exports.default = ModuleLoader;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ticatec/dyna-js",
3
- "version": "0.0.4",
3
+ "version": "0.1.0",
4
4
  "description": "A TypeScript library for dynamic code execution using new Function() that works in both Node.js and browser environments",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",
@@ -41,8 +41,7 @@
41
41
  "license": "MIT",
42
42
  "repository": {
43
43
  "type": "git",
44
- "url": "git+https://github.com/ticatec/DynaJS.git",
45
- "directory": "dynamic-script-loader"
44
+ "url": "git+https://github.com/ticatec/DynaJS.git"
46
45
  },
47
46
  "bugs": {
48
47
  "url": "https://github.com/ticatec/DynaJS/issues"