digitaltwin-core 0.14.3 → 1.0.1
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 +218 -1
- package/dist/auth/apisix_parser.d.ts +56 -56
- package/dist/auth/apisix_parser.d.ts.map +1 -1
- package/dist/auth/apisix_parser.js +72 -86
- package/dist/auth/apisix_parser.js.map +1 -1
- package/dist/auth/auth_provider.d.ts +118 -0
- package/dist/auth/auth_provider.d.ts.map +1 -0
- package/dist/auth/auth_provider.js +8 -0
- package/dist/auth/auth_provider.js.map +1 -0
- package/dist/auth/auth_provider_factory.d.ts +91 -0
- package/dist/auth/auth_provider_factory.d.ts.map +1 -0
- package/dist/auth/auth_provider_factory.js +146 -0
- package/dist/auth/auth_provider_factory.js.map +1 -0
- package/dist/auth/index.d.ts +4 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +3 -0
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/providers/gateway_auth_provider.d.ts +78 -0
- package/dist/auth/providers/gateway_auth_provider.d.ts.map +1 -0
- package/dist/auth/providers/gateway_auth_provider.js +109 -0
- package/dist/auth/providers/gateway_auth_provider.js.map +1 -0
- package/dist/auth/providers/index.d.ts +4 -0
- package/dist/auth/providers/index.d.ts.map +1 -0
- package/dist/auth/providers/index.js +4 -0
- package/dist/auth/providers/index.js.map +1 -0
- package/dist/auth/providers/jwt_auth_provider.d.ts +91 -0
- package/dist/auth/providers/jwt_auth_provider.d.ts.map +1 -0
- package/dist/auth/providers/jwt_auth_provider.js +204 -0
- package/dist/auth/providers/jwt_auth_provider.js.map +1 -0
- package/dist/auth/providers/no_auth_provider.d.ts +61 -0
- package/dist/auth/providers/no_auth_provider.d.ts.map +1 -0
- package/dist/auth/providers/no_auth_provider.js +76 -0
- package/dist/auth/providers/no_auth_provider.js.map +1 -0
- package/dist/auth/types.d.ts +5 -3
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/components/assets_manager.d.ts +1 -1
- package/dist/components/assets_manager.d.ts.map +1 -1
- package/dist/components/assets_manager.js +54 -48
- package/dist/components/assets_manager.js.map +1 -1
- package/dist/components/collector.d.ts.map +1 -1
- package/dist/components/collector.js +30 -18
- package/dist/components/collector.js.map +1 -1
- package/dist/components/custom_table_manager.d.ts.map +1 -1
- package/dist/components/custom_table_manager.js +36 -65
- package/dist/components/custom_table_manager.js.map +1 -1
- package/dist/components/global_assets_handler.d.ts +4 -2
- package/dist/components/global_assets_handler.d.ts.map +1 -1
- package/dist/components/global_assets_handler.js.map +1 -1
- package/dist/components/harvester.d.ts.map +1 -1
- package/dist/components/harvester.js +46 -33
- package/dist/components/harvester.js.map +1 -1
- package/dist/components/interfaces.d.ts +3 -2
- package/dist/components/interfaces.d.ts.map +1 -1
- package/dist/components/map_manager.d.ts.map +1 -1
- package/dist/components/map_manager.js.map +1 -1
- package/dist/components/tileset_manager.d.ts +2 -1
- package/dist/components/tileset_manager.d.ts.map +1 -1
- package/dist/components/tileset_manager.js +20 -15
- package/dist/components/tileset_manager.js.map +1 -1
- package/dist/database/adapters/knex_database_adapter.d.ts +6 -1
- package/dist/database/adapters/knex_database_adapter.d.ts.map +1 -1
- package/dist/database/adapters/knex_database_adapter.js +118 -36
- package/dist/database/adapters/knex_database_adapter.js.map +1 -1
- package/dist/database/database_adapter.d.ts +13 -1
- package/dist/database/database_adapter.d.ts.map +1 -1
- package/dist/database/database_adapter.js.map +1 -1
- package/dist/engine/component_types.d.ts +95 -0
- package/dist/engine/component_types.d.ts.map +1 -0
- package/dist/engine/component_types.js +93 -0
- package/dist/engine/component_types.js.map +1 -0
- package/dist/engine/digital_twin_engine.d.ts +121 -6
- package/dist/engine/digital_twin_engine.d.ts.map +1 -1
- package/dist/engine/digital_twin_engine.js +402 -74
- package/dist/engine/digital_twin_engine.js.map +1 -1
- package/dist/engine/endpoints.d.ts.map +1 -1
- package/dist/engine/endpoints.js +35 -3
- package/dist/engine/endpoints.js.map +1 -1
- package/dist/engine/error_handler.d.ts +20 -0
- package/dist/engine/error_handler.d.ts.map +1 -0
- package/dist/engine/error_handler.js +69 -0
- package/dist/engine/error_handler.js.map +1 -0
- package/dist/engine/events.d.ts +1 -1
- package/dist/engine/events.d.ts.map +1 -1
- package/dist/engine/events.js.map +1 -1
- package/dist/engine/health.d.ts +112 -0
- package/dist/engine/health.d.ts.map +1 -0
- package/dist/engine/health.js +190 -0
- package/dist/engine/health.js.map +1 -0
- package/dist/engine/initializer.d.ts.map +1 -1
- package/dist/engine/initializer.js +6 -4
- package/dist/engine/initializer.js.map +1 -1
- package/dist/engine/scheduler.d.ts.map +1 -1
- package/dist/engine/scheduler.js +17 -9
- package/dist/engine/scheduler.js.map +1 -1
- package/dist/engine/upload_processor.d.ts.map +1 -1
- package/dist/engine/upload_processor.js +24 -12
- package/dist/engine/upload_processor.js.map +1 -1
- package/dist/errors/index.d.ts +94 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +149 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -1
- package/dist/loader/component_loader.d.ts +128 -0
- package/dist/loader/component_loader.d.ts.map +1 -0
- package/dist/loader/component_loader.js +330 -0
- package/dist/loader/component_loader.js.map +1 -0
- package/dist/loader/index.d.ts +19 -0
- package/dist/loader/index.d.ts.map +1 -0
- package/dist/loader/index.js +19 -0
- package/dist/loader/index.js.map +1 -0
- package/dist/storage/adapters/local_storage_service.d.ts +6 -0
- package/dist/storage/adapters/local_storage_service.d.ts.map +1 -1
- package/dist/storage/adapters/local_storage_service.js +26 -4
- package/dist/storage/adapters/local_storage_service.js.map +1 -1
- package/dist/storage/adapters/ovh_storage_service.d.ts.map +1 -1
- package/dist/storage/adapters/ovh_storage_service.js +5 -6
- package/dist/storage/adapters/ovh_storage_service.js.map +1 -1
- package/dist/storage/storage_factory.d.ts.map +1 -1
- package/dist/storage/storage_factory.js +4 -1
- package/dist/storage/storage_factory.js.map +1 -1
- package/dist/storage/storage_service.d.ts.map +1 -1
- package/dist/storage/storage_service.js +6 -2
- package/dist/storage/storage_service.js.map +1 -1
- package/dist/types/http.d.ts +156 -0
- package/dist/types/http.d.ts.map +1 -0
- package/dist/types/http.js +8 -0
- package/dist/types/http.js.map +1 -0
- package/dist/utils/graceful_shutdown.d.ts +44 -0
- package/dist/utils/graceful_shutdown.d.ts.map +1 -0
- package/dist/utils/graceful_shutdown.js +79 -0
- package/dist/utils/graceful_shutdown.js.map +1 -0
- package/dist/utils/http_responses.d.ts +20 -0
- package/dist/utils/http_responses.d.ts.map +1 -1
- package/dist/utils/http_responses.js +28 -2
- package/dist/utils/http_responses.js.map +1 -1
- package/dist/utils/logger.d.ts +8 -8
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +8 -8
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/safe_async.d.ts +50 -0
- package/dist/utils/safe_async.d.ts.map +1 -0
- package/dist/utils/safe_async.js +90 -0
- package/dist/utils/safe_async.js.map +1 -0
- package/dist/validation/index.d.ts +3 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +7 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/schemas.d.ts +273 -0
- package/dist/validation/schemas.d.ts.map +1 -0
- package/dist/validation/schemas.js +82 -0
- package/dist/validation/schemas.js.map +1 -0
- package/dist/validation/validate.d.ts +49 -0
- package/dist/validation/validate.d.ts.map +1 -0
- package/dist/validation/validate.js +110 -0
- package/dist/validation/validate.js.map +1 -0
- package/package.json +14 -8
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Component auto-discovery and loading utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides functions to automatically discover and load Digital Twin components
|
|
5
|
+
* from a directory based on file naming conventions.
|
|
6
|
+
*/
|
|
7
|
+
import type { LoadedComponents } from '../engine/component_types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Options for component auto-discovery
|
|
10
|
+
*/
|
|
11
|
+
export interface LoadComponentsOptions {
|
|
12
|
+
/**
|
|
13
|
+
* File patterns to match by suffix.
|
|
14
|
+
* Keys are component types, values are file suffixes (without extension).
|
|
15
|
+
* @default Standard naming conventions
|
|
16
|
+
*/
|
|
17
|
+
patterns?: {
|
|
18
|
+
collectors?: string;
|
|
19
|
+
harvesters?: string;
|
|
20
|
+
handlers?: string;
|
|
21
|
+
assetsManagers?: string;
|
|
22
|
+
customTableManagers?: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* File extensions to scan for.
|
|
26
|
+
* @default ['.js', '.mjs']
|
|
27
|
+
*/
|
|
28
|
+
extensions?: string[];
|
|
29
|
+
/**
|
|
30
|
+
* Whether to scan subdirectories recursively.
|
|
31
|
+
* @default true
|
|
32
|
+
*/
|
|
33
|
+
recursive?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Patterns to exclude (glob-like patterns applied to file names).
|
|
36
|
+
* @default ['*.spec.*', '*.test.*', 'index.*']
|
|
37
|
+
*/
|
|
38
|
+
exclude?: string[];
|
|
39
|
+
/**
|
|
40
|
+
* Enable verbose logging during discovery.
|
|
41
|
+
* @default false
|
|
42
|
+
*/
|
|
43
|
+
verbose?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Result of component loading operation
|
|
47
|
+
*/
|
|
48
|
+
export interface LoadComponentsResult extends LoadedComponents {
|
|
49
|
+
/** Files that were scanned */
|
|
50
|
+
scannedFiles: string[];
|
|
51
|
+
/** Errors encountered during loading */
|
|
52
|
+
errors: Array<{
|
|
53
|
+
file: string;
|
|
54
|
+
error: string;
|
|
55
|
+
}>;
|
|
56
|
+
/** Summary statistics */
|
|
57
|
+
summary: {
|
|
58
|
+
total: number;
|
|
59
|
+
collectors: number;
|
|
60
|
+
harvesters: number;
|
|
61
|
+
handlers: number;
|
|
62
|
+
assetsManagers: number;
|
|
63
|
+
customTableManagers: number;
|
|
64
|
+
errors: number;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Auto-discovers and loads Digital Twin components from a directory.
|
|
69
|
+
*
|
|
70
|
+
* This function scans the specified directory for component files following
|
|
71
|
+
* the naming conventions (*_collector.js, *_harvester.js, etc.), dynamically
|
|
72
|
+
* imports them, and instantiates any component classes found.
|
|
73
|
+
*
|
|
74
|
+
* ## Naming Conventions
|
|
75
|
+
*
|
|
76
|
+
* Component files should follow these naming patterns:
|
|
77
|
+
* - Collectors: `*_collector.js` (e.g., `weather_collector.js`)
|
|
78
|
+
* - Harvesters: `*_harvester.js` (e.g., `traffic_harvester.js`)
|
|
79
|
+
* - Handlers: `*_handler.js` (e.g., `api_handler.js`)
|
|
80
|
+
* - Assets Managers: `*_assets_manager.js` (e.g., `gltf_assets_manager.js`)
|
|
81
|
+
* - Custom Table Managers: `*_custom_table.js` (e.g., `wms_custom_table.js`)
|
|
82
|
+
*
|
|
83
|
+
* ## Component Export Requirements
|
|
84
|
+
*
|
|
85
|
+
* Components should be exported as default export or named export matching the class name:
|
|
86
|
+
* ```typescript
|
|
87
|
+
* // Default export (preferred)
|
|
88
|
+
* export default class WeatherCollector extends Collector { ... }
|
|
89
|
+
*
|
|
90
|
+
* // Named export matching file name
|
|
91
|
+
* export class WeatherCollector extends Collector { ... }
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* @param directory - Directory path to scan for components (absolute or relative to cwd)
|
|
95
|
+
* @param options - Configuration options for discovery
|
|
96
|
+
* @returns Promise resolving to loaded components and metadata
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* import { loadComponents, DigitalTwinEngine } from 'digitaltwin-core'
|
|
101
|
+
*
|
|
102
|
+
* // Basic usage - scan compiled components
|
|
103
|
+
* const result = await loadComponents('./dist/components')
|
|
104
|
+
*
|
|
105
|
+
* console.log(`Loaded ${result.summary.total} components`)
|
|
106
|
+
*
|
|
107
|
+
* const engine = new DigitalTwinEngine({ storage, database })
|
|
108
|
+
* engine.registerComponents(result)
|
|
109
|
+
* await engine.start()
|
|
110
|
+
* ```
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```typescript
|
|
114
|
+
* // Advanced usage with options
|
|
115
|
+
* const result = await loadComponents('./dist/components', {
|
|
116
|
+
* recursive: true,
|
|
117
|
+
* verbose: true,
|
|
118
|
+
* extensions: ['.js'],
|
|
119
|
+
* exclude: ['*.test.*', 'deprecated_*']
|
|
120
|
+
* })
|
|
121
|
+
*
|
|
122
|
+
* if (result.errors.length > 0) {
|
|
123
|
+
* console.warn('Some components failed to load:', result.errors)
|
|
124
|
+
* }
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export declare function loadComponents(directory: string, options?: LoadComponentsOptions): Promise<LoadComponentsResult>;
|
|
128
|
+
//# sourceMappingURL=component_loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component_loader.d.ts","sourceRoot":"","sources":["../../src/loader/component_loader.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,gBAAgB,EAAgB,MAAM,8BAA8B,CAAA;AASlF;;GAEG;AACH,MAAM,WAAW,qBAAqB;IAClC;;;;OAIG;IACH,QAAQ,CAAC,EAAE;QACP,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,mBAAmB,CAAC,EAAE,MAAM,CAAA;KAC/B,CAAA;IAED;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IAErB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IAEnB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAElB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAqB,SAAQ,gBAAgB;IAC1D,8BAA8B;IAC9B,YAAY,EAAE,MAAM,EAAE,CAAA;IAEtB,wCAAwC;IACxC,MAAM,EAAE,KAAK,CAAC;QACV,IAAI,EAAE,MAAM,CAAA;QACZ,KAAK,EAAE,MAAM,CAAA;KAChB,CAAC,CAAA;IAEF,yBAAyB;IACzB,OAAO,EAAE;QACL,KAAK,EAAE,MAAM,CAAA;QACb,UAAU,EAAE,MAAM,CAAA;QAClB,UAAU,EAAE,MAAM,CAAA;QAClB,QAAQ,EAAE,MAAM,CAAA;QAChB,cAAc,EAAE,MAAM,CAAA;QACtB,mBAAmB,EAAE,MAAM,CAAA;QAC3B,MAAM,EAAE,MAAM,CAAA;KACjB,CAAA;CACJ;AA6JD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,wBAAsB,cAAc,CAChC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,qBAA0B,GACpC,OAAO,CAAC,oBAAoB,CAAC,CAoJ/B"}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Component auto-discovery and loading utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides functions to automatically discover and load Digital Twin components
|
|
5
|
+
* from a directory based on file naming conventions.
|
|
6
|
+
*/
|
|
7
|
+
import { pathToFileURL } from 'node:url';
|
|
8
|
+
import fs from 'node:fs/promises';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { isCollector, isHarvester, isHandler, isAssetsManager, isCustomTableManager } from '../engine/component_types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Default file patterns for component detection based on naming conventions
|
|
13
|
+
*/
|
|
14
|
+
const DEFAULT_PATTERNS = {
|
|
15
|
+
collectors: '_collector',
|
|
16
|
+
harvesters: '_harvester',
|
|
17
|
+
handlers: '_handler',
|
|
18
|
+
assetsManagers: '_assets_manager',
|
|
19
|
+
customTableManagers: '_custom_table'
|
|
20
|
+
};
|
|
21
|
+
const DEFAULT_EXTENSIONS = ['.js', '.mjs'];
|
|
22
|
+
const DEFAULT_EXCLUDE = ['*.spec.*', '*.test.*', 'index.*', '*.d.ts'];
|
|
23
|
+
/**
|
|
24
|
+
* Check if a filename matches an exclusion pattern.
|
|
25
|
+
*/
|
|
26
|
+
function matchesExcludePattern(filename, patterns) {
|
|
27
|
+
for (const pattern of patterns) {
|
|
28
|
+
// Convert glob pattern to regex
|
|
29
|
+
const regexPattern = pattern.replace(/\./g, '\\.').replace(/\*/g, '.*');
|
|
30
|
+
const regex = new RegExp(`^${regexPattern}$`, 'i');
|
|
31
|
+
if (regex.test(filename)) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Determine component type from filename based on patterns.
|
|
39
|
+
*/
|
|
40
|
+
function getComponentTypeFromFilename(filename, patterns) {
|
|
41
|
+
const baseName = path.basename(filename);
|
|
42
|
+
// Remove extension for matching
|
|
43
|
+
const nameWithoutExt = baseName.replace(/\.[^.]+$/, '');
|
|
44
|
+
if (nameWithoutExt.endsWith(patterns.collectors))
|
|
45
|
+
return 'collectors';
|
|
46
|
+
if (nameWithoutExt.endsWith(patterns.harvesters))
|
|
47
|
+
return 'harvesters';
|
|
48
|
+
if (nameWithoutExt.endsWith(patterns.handlers))
|
|
49
|
+
return 'handlers';
|
|
50
|
+
if (nameWithoutExt.endsWith(patterns.assetsManagers))
|
|
51
|
+
return 'assetsManagers';
|
|
52
|
+
if (nameWithoutExt.endsWith(patterns.customTableManagers))
|
|
53
|
+
return 'customTableManagers';
|
|
54
|
+
// Also check for *_manager pattern for tileset/map managers
|
|
55
|
+
if (nameWithoutExt.endsWith('_manager') ||
|
|
56
|
+
nameWithoutExt.endsWith('_tileset_manager') ||
|
|
57
|
+
nameWithoutExt.endsWith('_map_manager')) {
|
|
58
|
+
return 'assetsManagers';
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Check if a value is a class constructor.
|
|
64
|
+
*/
|
|
65
|
+
function isClassConstructor(value) {
|
|
66
|
+
return typeof value === 'function' && value.prototype && value.prototype.constructor === value;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Check if an instance is a valid Digital Twin component.
|
|
70
|
+
*/
|
|
71
|
+
function isValidComponent(instance) {
|
|
72
|
+
if (!instance || typeof instance !== 'object')
|
|
73
|
+
return false;
|
|
74
|
+
if (typeof instance.getConfiguration !== 'function')
|
|
75
|
+
return false;
|
|
76
|
+
return (isCollector(instance) ||
|
|
77
|
+
isHarvester(instance) ||
|
|
78
|
+
isHandler(instance) ||
|
|
79
|
+
isAssetsManager(instance) ||
|
|
80
|
+
isCustomTableManager(instance));
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Recursively scan a directory for component files.
|
|
84
|
+
*/
|
|
85
|
+
async function scanDirectory(dir, options) {
|
|
86
|
+
const results = [];
|
|
87
|
+
try {
|
|
88
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
89
|
+
for (const entry of entries) {
|
|
90
|
+
const fullPath = path.join(dir, entry.name);
|
|
91
|
+
// Skip hidden files/directories
|
|
92
|
+
if (entry.name.startsWith('.') || entry.name.startsWith('_')) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (entry.isDirectory() && options.recursive) {
|
|
96
|
+
const subResults = await scanDirectory(fullPath, options);
|
|
97
|
+
results.push(...subResults);
|
|
98
|
+
}
|
|
99
|
+
else if (entry.isFile()) {
|
|
100
|
+
// Check extension
|
|
101
|
+
const ext = path.extname(entry.name);
|
|
102
|
+
if (!options.extensions.includes(ext)) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
// Check exclusions
|
|
106
|
+
if (matchesExcludePattern(entry.name, options.exclude)) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
// Determine component type
|
|
110
|
+
const componentType = getComponentTypeFromFilename(entry.name, options.patterns);
|
|
111
|
+
if (componentType) {
|
|
112
|
+
results.push({ path: fullPath, type: componentType });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Directory doesn't exist or can't be read - return empty array
|
|
119
|
+
}
|
|
120
|
+
return results;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Convert a snake_case or kebab-case string to PascalCase.
|
|
124
|
+
*/
|
|
125
|
+
function toPascalCase(str) {
|
|
126
|
+
return str
|
|
127
|
+
.split(/[-_]/)
|
|
128
|
+
.map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
|
|
129
|
+
.join('');
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Auto-discovers and loads Digital Twin components from a directory.
|
|
133
|
+
*
|
|
134
|
+
* This function scans the specified directory for component files following
|
|
135
|
+
* the naming conventions (*_collector.js, *_harvester.js, etc.), dynamically
|
|
136
|
+
* imports them, and instantiates any component classes found.
|
|
137
|
+
*
|
|
138
|
+
* ## Naming Conventions
|
|
139
|
+
*
|
|
140
|
+
* Component files should follow these naming patterns:
|
|
141
|
+
* - Collectors: `*_collector.js` (e.g., `weather_collector.js`)
|
|
142
|
+
* - Harvesters: `*_harvester.js` (e.g., `traffic_harvester.js`)
|
|
143
|
+
* - Handlers: `*_handler.js` (e.g., `api_handler.js`)
|
|
144
|
+
* - Assets Managers: `*_assets_manager.js` (e.g., `gltf_assets_manager.js`)
|
|
145
|
+
* - Custom Table Managers: `*_custom_table.js` (e.g., `wms_custom_table.js`)
|
|
146
|
+
*
|
|
147
|
+
* ## Component Export Requirements
|
|
148
|
+
*
|
|
149
|
+
* Components should be exported as default export or named export matching the class name:
|
|
150
|
+
* ```typescript
|
|
151
|
+
* // Default export (preferred)
|
|
152
|
+
* export default class WeatherCollector extends Collector { ... }
|
|
153
|
+
*
|
|
154
|
+
* // Named export matching file name
|
|
155
|
+
* export class WeatherCollector extends Collector { ... }
|
|
156
|
+
* ```
|
|
157
|
+
*
|
|
158
|
+
* @param directory - Directory path to scan for components (absolute or relative to cwd)
|
|
159
|
+
* @param options - Configuration options for discovery
|
|
160
|
+
* @returns Promise resolving to loaded components and metadata
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```typescript
|
|
164
|
+
* import { loadComponents, DigitalTwinEngine } from 'digitaltwin-core'
|
|
165
|
+
*
|
|
166
|
+
* // Basic usage - scan compiled components
|
|
167
|
+
* const result = await loadComponents('./dist/components')
|
|
168
|
+
*
|
|
169
|
+
* console.log(`Loaded ${result.summary.total} components`)
|
|
170
|
+
*
|
|
171
|
+
* const engine = new DigitalTwinEngine({ storage, database })
|
|
172
|
+
* engine.registerComponents(result)
|
|
173
|
+
* await engine.start()
|
|
174
|
+
* ```
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```typescript
|
|
178
|
+
* // Advanced usage with options
|
|
179
|
+
* const result = await loadComponents('./dist/components', {
|
|
180
|
+
* recursive: true,
|
|
181
|
+
* verbose: true,
|
|
182
|
+
* extensions: ['.js'],
|
|
183
|
+
* exclude: ['*.test.*', 'deprecated_*']
|
|
184
|
+
* })
|
|
185
|
+
*
|
|
186
|
+
* if (result.errors.length > 0) {
|
|
187
|
+
* console.warn('Some components failed to load:', result.errors)
|
|
188
|
+
* }
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
export async function loadComponents(directory, options = {}) {
|
|
192
|
+
const patterns = { ...DEFAULT_PATTERNS, ...options.patterns };
|
|
193
|
+
const extensions = options.extensions ?? DEFAULT_EXTENSIONS;
|
|
194
|
+
const exclude = options.exclude ?? DEFAULT_EXCLUDE;
|
|
195
|
+
const recursive = options.recursive ?? true;
|
|
196
|
+
const verbose = options.verbose ?? false;
|
|
197
|
+
const result = {
|
|
198
|
+
collectors: [],
|
|
199
|
+
harvesters: [],
|
|
200
|
+
handlers: [],
|
|
201
|
+
assetsManagers: [],
|
|
202
|
+
customTableManagers: [],
|
|
203
|
+
scannedFiles: [],
|
|
204
|
+
errors: [],
|
|
205
|
+
summary: {
|
|
206
|
+
total: 0,
|
|
207
|
+
collectors: 0,
|
|
208
|
+
harvesters: 0,
|
|
209
|
+
handlers: 0,
|
|
210
|
+
assetsManagers: 0,
|
|
211
|
+
customTableManagers: 0,
|
|
212
|
+
errors: 0
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
// Resolve directory path
|
|
216
|
+
const absoluteDir = path.isAbsolute(directory) ? directory : path.resolve(process.cwd(), directory);
|
|
217
|
+
// Check if directory exists
|
|
218
|
+
try {
|
|
219
|
+
await fs.access(absoluteDir);
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
if (verbose) {
|
|
223
|
+
console.warn(`[loadComponents] Directory not found: ${absoluteDir}`);
|
|
224
|
+
}
|
|
225
|
+
return result;
|
|
226
|
+
}
|
|
227
|
+
// Scan for component files
|
|
228
|
+
const files = await scanDirectory(absoluteDir, { extensions, exclude, recursive, patterns });
|
|
229
|
+
result.scannedFiles = files.map(f => f.path);
|
|
230
|
+
if (verbose) {
|
|
231
|
+
console.log(`[loadComponents] Found ${files.length} potential component files`);
|
|
232
|
+
}
|
|
233
|
+
// Load each component file
|
|
234
|
+
for (const { path: filePath, type: expectedType } of files) {
|
|
235
|
+
try {
|
|
236
|
+
// Convert to file URL for ESM import
|
|
237
|
+
const fileUrl = pathToFileURL(filePath).href;
|
|
238
|
+
const module = await import(fileUrl);
|
|
239
|
+
// Find component class in module exports
|
|
240
|
+
let ComponentClass = null;
|
|
241
|
+
// Try default export first
|
|
242
|
+
if (module.default && isClassConstructor(module.default)) {
|
|
243
|
+
ComponentClass = module.default;
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
// Try named exports
|
|
247
|
+
const fileName = path.basename(filePath, path.extname(filePath));
|
|
248
|
+
const expectedClassName = toPascalCase(fileName);
|
|
249
|
+
if (module[expectedClassName] && isClassConstructor(module[expectedClassName])) {
|
|
250
|
+
ComponentClass = module[expectedClassName];
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
// Try to find any class export that's a valid component
|
|
254
|
+
for (const [, value] of Object.entries(module)) {
|
|
255
|
+
if (isClassConstructor(value)) {
|
|
256
|
+
try {
|
|
257
|
+
const testInstance = new value();
|
|
258
|
+
if (isValidComponent(testInstance)) {
|
|
259
|
+
ComponentClass = value;
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
// Skip if instantiation fails
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (!ComponentClass) {
|
|
271
|
+
result.errors.push({
|
|
272
|
+
file: filePath,
|
|
273
|
+
error: 'No valid component class found in module exports'
|
|
274
|
+
});
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
// Instantiate the component
|
|
278
|
+
const instance = new ComponentClass();
|
|
279
|
+
if (!isValidComponent(instance)) {
|
|
280
|
+
result.errors.push({
|
|
281
|
+
file: filePath,
|
|
282
|
+
error: 'Instantiated class is not a valid Digital Twin component'
|
|
283
|
+
});
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
// Add to appropriate array based on actual type (not expected type)
|
|
287
|
+
if (isCollector(instance)) {
|
|
288
|
+
result.collectors.push(instance);
|
|
289
|
+
result.summary.collectors++;
|
|
290
|
+
}
|
|
291
|
+
else if (isHarvester(instance)) {
|
|
292
|
+
result.harvesters.push(instance);
|
|
293
|
+
result.summary.harvesters++;
|
|
294
|
+
}
|
|
295
|
+
else if (isHandler(instance)) {
|
|
296
|
+
result.handlers.push(instance);
|
|
297
|
+
result.summary.handlers++;
|
|
298
|
+
}
|
|
299
|
+
else if (isCustomTableManager(instance)) {
|
|
300
|
+
result.customTableManagers.push(instance);
|
|
301
|
+
result.summary.customTableManagers++;
|
|
302
|
+
}
|
|
303
|
+
else if (isAssetsManager(instance)) {
|
|
304
|
+
result.assetsManagers.push(instance);
|
|
305
|
+
result.summary.assetsManagers++;
|
|
306
|
+
}
|
|
307
|
+
result.summary.total++;
|
|
308
|
+
if (verbose) {
|
|
309
|
+
const config = instance.getConfiguration();
|
|
310
|
+
console.log(`[loadComponents] Loaded: ${config.name} (${expectedType})`);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
315
|
+
result.errors.push({
|
|
316
|
+
file: filePath,
|
|
317
|
+
error: `Failed to import: ${errorMessage}`
|
|
318
|
+
});
|
|
319
|
+
result.summary.errors++;
|
|
320
|
+
if (verbose) {
|
|
321
|
+
console.error(`[loadComponents] Error loading ${filePath}:`, errorMessage);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (verbose) {
|
|
326
|
+
console.log(`[loadComponents] Summary:`, result.summary);
|
|
327
|
+
}
|
|
328
|
+
return result;
|
|
329
|
+
}
|
|
330
|
+
//# sourceMappingURL=component_loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component_loader.js","sourceRoot":"","sources":["../../src/loader/component_loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Component auto-discovery and loading utilities
|
|
3
|
+
*
|
|
4
|
+
* This module provides utilities for automatically discovering and loading
|
|
5
|
+
* Digital Twin components from a directory based on file naming conventions.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { loadComponents } from 'digitaltwin-core'
|
|
10
|
+
*
|
|
11
|
+
* const result = await loadComponents('./dist/components')
|
|
12
|
+
*
|
|
13
|
+
* const engine = new DigitalTwinEngine({ storage, database })
|
|
14
|
+
* engine.registerComponents(result)
|
|
15
|
+
* await engine.start()
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export { loadComponents, type LoadComponentsOptions, type LoadComponentsResult } from './component_loader.js';
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/loader/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,MAAM,uBAAuB,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Component auto-discovery and loading utilities
|
|
3
|
+
*
|
|
4
|
+
* This module provides utilities for automatically discovering and loading
|
|
5
|
+
* Digital Twin components from a directory based on file naming conventions.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { loadComponents } from 'digitaltwin-core'
|
|
10
|
+
*
|
|
11
|
+
* const result = await loadComponents('./dist/components')
|
|
12
|
+
*
|
|
13
|
+
* const engine = new DigitalTwinEngine({ storage, database })
|
|
14
|
+
* engine.registerComponents(result)
|
|
15
|
+
* await engine.start()
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export { loadComponents } from './component_loader.js';
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/loader/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,cAAc,EAAyD,MAAM,uBAAuB,CAAA"}
|
|
@@ -4,6 +4,7 @@ import { StorageService } from '../storage_service.js';
|
|
|
4
4
|
* Saves files in a configured folder using a timestamp as filename.
|
|
5
5
|
*/
|
|
6
6
|
export declare class LocalStorageService extends StorageService {
|
|
7
|
+
#private;
|
|
7
8
|
private baseDir;
|
|
8
9
|
constructor(baseDir?: string);
|
|
9
10
|
/**
|
|
@@ -18,11 +19,13 @@ export declare class LocalStorageService extends StorageService {
|
|
|
18
19
|
* Retrieves a file as buffer using its relative path.
|
|
19
20
|
* @param relativePath - Filename previously returned by `save`
|
|
20
21
|
* @returns File content as Buffer
|
|
22
|
+
* @throws Error if path traversal is detected
|
|
21
23
|
*/
|
|
22
24
|
retrieve(relativePath: string): Promise<Buffer>;
|
|
23
25
|
/**
|
|
24
26
|
* Deletes a stored file.
|
|
25
27
|
* @param relativePath - Filename previously returned by `save`
|
|
28
|
+
* @throws Error if path traversal is detected
|
|
26
29
|
*/
|
|
27
30
|
delete(relativePath: string): Promise<void>;
|
|
28
31
|
/**
|
|
@@ -31,6 +34,7 @@ export declare class LocalStorageService extends StorageService {
|
|
|
31
34
|
* @param buffer - Content to save
|
|
32
35
|
* @param relativePath - Full relative path including filename (e.g., 'tilesets/123/tileset.json')
|
|
33
36
|
* @returns The same relative path that was provided
|
|
37
|
+
* @throws Error if path traversal is detected
|
|
34
38
|
*/
|
|
35
39
|
saveWithPath(buffer: Buffer, relativePath: string): Promise<string>;
|
|
36
40
|
/**
|
|
@@ -39,12 +43,14 @@ export declare class LocalStorageService extends StorageService {
|
|
|
39
43
|
* In production, use a cloud storage service (OVH, S3) for public URLs.
|
|
40
44
|
* @param relativePath - The storage path of the file
|
|
41
45
|
* @returns The file path (relative to baseDir)
|
|
46
|
+
* @throws Error if path traversal is detected
|
|
42
47
|
*/
|
|
43
48
|
getPublicUrl(relativePath: string): string;
|
|
44
49
|
/**
|
|
45
50
|
* Deletes all files under a given prefix (folder).
|
|
46
51
|
* @param prefix - The folder/prefix to delete (e.g., 'tilesets/123')
|
|
47
52
|
* @returns Number of files deleted
|
|
53
|
+
* @throws Error if path traversal is detected
|
|
48
54
|
*/
|
|
49
55
|
deleteByPrefix(prefix: string): Promise<number>;
|
|
50
56
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local_storage_service.d.ts","sourceRoot":"","sources":["../../../src/storage/adapters/local_storage_service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAItD;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,cAAc
|
|
1
|
+
{"version":3,"file":"local_storage_service.d.ts","sourceRoot":"","sources":["../../../src/storage/adapters/local_storage_service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAItD;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,cAAc;;IAGvC,OAAO,CAAC,OAAO;gBAAP,OAAO,GAAE,MAAe;IAqB5C;;;;;;OAMG;IACG,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAetF;;;;;OAKG;IACG,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKrD;;;;OAIG;IACG,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjD;;;;;;;OAOG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUzE;;;;;;;OAOG;IACH,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;IAQ1C;;;;;OAKG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAgCxD"}
|
|
@@ -6,9 +6,24 @@ import path from 'path';
|
|
|
6
6
|
* Saves files in a configured folder using a timestamp as filename.
|
|
7
7
|
*/
|
|
8
8
|
export class LocalStorageService extends StorageService {
|
|
9
|
+
#normalizedBase;
|
|
9
10
|
constructor(baseDir = 'data') {
|
|
10
11
|
super();
|
|
11
12
|
this.baseDir = baseDir;
|
|
13
|
+
this.#normalizedBase = path.resolve(this.baseDir);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Validates that a path does not escape the base directory (path traversal protection).
|
|
17
|
+
* @param relativePath - The relative path to validate
|
|
18
|
+
* @returns The resolved absolute path if valid
|
|
19
|
+
* @throws Error if path traversal is detected
|
|
20
|
+
*/
|
|
21
|
+
#validatePath(relativePath) {
|
|
22
|
+
const resolved = path.resolve(this.#normalizedBase, relativePath);
|
|
23
|
+
if (!resolved.startsWith(this.#normalizedBase + path.sep) && resolved !== this.#normalizedBase) {
|
|
24
|
+
throw new Error(`Invalid path: path traversal detected for "${relativePath}"`);
|
|
25
|
+
}
|
|
26
|
+
return resolved;
|
|
12
27
|
}
|
|
13
28
|
/**
|
|
14
29
|
* Saves the given buffer to disk under a unique filename.
|
|
@@ -33,17 +48,19 @@ export class LocalStorageService extends StorageService {
|
|
|
33
48
|
* Retrieves a file as buffer using its relative path.
|
|
34
49
|
* @param relativePath - Filename previously returned by `save`
|
|
35
50
|
* @returns File content as Buffer
|
|
51
|
+
* @throws Error if path traversal is detected
|
|
36
52
|
*/
|
|
37
53
|
async retrieve(relativePath) {
|
|
38
|
-
const filePath =
|
|
54
|
+
const filePath = this.#validatePath(relativePath);
|
|
39
55
|
return fs.readFile(filePath);
|
|
40
56
|
}
|
|
41
57
|
/**
|
|
42
58
|
* Deletes a stored file.
|
|
43
59
|
* @param relativePath - Filename previously returned by `save`
|
|
60
|
+
* @throws Error if path traversal is detected
|
|
44
61
|
*/
|
|
45
62
|
async delete(relativePath) {
|
|
46
|
-
const filePath =
|
|
63
|
+
const filePath = this.#validatePath(relativePath);
|
|
47
64
|
await fs.rm(filePath, { force: true });
|
|
48
65
|
}
|
|
49
66
|
/**
|
|
@@ -52,9 +69,10 @@ export class LocalStorageService extends StorageService {
|
|
|
52
69
|
* @param buffer - Content to save
|
|
53
70
|
* @param relativePath - Full relative path including filename (e.g., 'tilesets/123/tileset.json')
|
|
54
71
|
* @returns The same relative path that was provided
|
|
72
|
+
* @throws Error if path traversal is detected
|
|
55
73
|
*/
|
|
56
74
|
async saveWithPath(buffer, relativePath) {
|
|
57
|
-
const filePath =
|
|
75
|
+
const filePath = this.#validatePath(relativePath);
|
|
58
76
|
const dirPath = path.dirname(filePath);
|
|
59
77
|
await fs.mkdir(dirPath, { recursive: true });
|
|
60
78
|
await fs.writeFile(filePath, buffer);
|
|
@@ -66,8 +84,11 @@ export class LocalStorageService extends StorageService {
|
|
|
66
84
|
* In production, use a cloud storage service (OVH, S3) for public URLs.
|
|
67
85
|
* @param relativePath - The storage path of the file
|
|
68
86
|
* @returns The file path (relative to baseDir)
|
|
87
|
+
* @throws Error if path traversal is detected
|
|
69
88
|
*/
|
|
70
89
|
getPublicUrl(relativePath) {
|
|
90
|
+
// Validate path to prevent traversal (even though this just returns a string)
|
|
91
|
+
this.#validatePath(relativePath);
|
|
71
92
|
// For local storage, return the file path
|
|
72
93
|
// In a real deployment, you'd need Express static serving or similar
|
|
73
94
|
return path.join(this.baseDir, relativePath);
|
|
@@ -76,9 +97,10 @@ export class LocalStorageService extends StorageService {
|
|
|
76
97
|
* Deletes all files under a given prefix (folder).
|
|
77
98
|
* @param prefix - The folder/prefix to delete (e.g., 'tilesets/123')
|
|
78
99
|
* @returns Number of files deleted
|
|
100
|
+
* @throws Error if path traversal is detected
|
|
79
101
|
*/
|
|
80
102
|
async deleteByPrefix(prefix) {
|
|
81
|
-
const folderPath =
|
|
103
|
+
const folderPath = this.#validatePath(prefix);
|
|
82
104
|
try {
|
|
83
105
|
// Check if folder exists
|
|
84
106
|
await fs.access(folderPath);
|