digitaltwin-core 0.14.2 → 1.0.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/LICENSE +20 -20
- package/README.md +494 -359
- package/dist/auth/apisix_parser.d.ts +141 -0
- package/dist/auth/apisix_parser.d.ts.map +1 -0
- package/dist/auth/apisix_parser.js +161 -0
- package/dist/auth/apisix_parser.js.map +1 -0
- package/dist/auth/auth_config.d.ts +126 -0
- package/dist/auth/auth_config.d.ts.map +1 -0
- package/dist/auth/auth_config.js +169 -0
- package/dist/auth/auth_config.js.map +1 -0
- 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 +8 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +7 -0
- package/dist/auth/index.js.map +1 -0
- 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 +100 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +2 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/auth/user_service.d.ts +86 -0
- package/dist/auth/user_service.d.ts.map +1 -0
- package/dist/auth/user_service.js +237 -0
- package/dist/auth/user_service.js.map +1 -0
- package/dist/components/assets_manager.d.ts +662 -0
- package/dist/components/assets_manager.d.ts.map +1 -0
- package/dist/components/assets_manager.js +1537 -0
- package/dist/components/assets_manager.js.map +1 -0
- package/dist/components/async_upload.d.ts +20 -0
- package/dist/components/async_upload.d.ts.map +1 -0
- package/dist/components/async_upload.js +10 -0
- package/dist/components/async_upload.js.map +1 -0
- package/dist/components/collector.d.ts +203 -0
- package/dist/components/collector.d.ts.map +1 -0
- package/dist/components/collector.js +214 -0
- package/dist/components/collector.js.map +1 -0
- package/dist/components/custom_table_manager.d.ts +503 -0
- package/dist/components/custom_table_manager.d.ts.map +1 -0
- package/dist/components/custom_table_manager.js +1023 -0
- package/dist/components/custom_table_manager.js.map +1 -0
- package/dist/components/global_assets_handler.d.ts +63 -0
- package/dist/components/global_assets_handler.d.ts.map +1 -0
- package/dist/components/global_assets_handler.js +127 -0
- package/dist/components/global_assets_handler.js.map +1 -0
- package/dist/components/handler.d.ts +104 -0
- package/dist/components/handler.d.ts.map +1 -0
- package/dist/components/handler.js +110 -0
- package/dist/components/handler.js.map +1 -0
- package/dist/components/harvester.d.ts +182 -0
- package/dist/components/harvester.d.ts.map +1 -0
- package/dist/components/harvester.js +406 -0
- package/dist/components/harvester.js.map +1 -0
- package/dist/components/index.d.ts +11 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +9 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/interfaces.d.ts +126 -0
- package/dist/components/interfaces.d.ts.map +1 -0
- package/dist/components/interfaces.js +8 -0
- package/dist/components/interfaces.js.map +1 -0
- package/dist/components/map_manager.d.ts +61 -0
- package/dist/components/map_manager.d.ts.map +1 -0
- package/dist/components/map_manager.js +242 -0
- package/dist/components/map_manager.js.map +1 -0
- package/dist/components/tileset_manager.d.ts +125 -0
- package/dist/components/tileset_manager.d.ts.map +1 -0
- package/dist/components/tileset_manager.js +623 -0
- package/dist/components/tileset_manager.js.map +1 -0
- package/dist/components/types.d.ts +226 -0
- package/dist/components/types.d.ts.map +1 -0
- package/dist/components/types.js +8 -0
- package/dist/components/types.js.map +1 -0
- package/dist/database/adapters/knex_database_adapter.d.ts +97 -0
- package/dist/database/adapters/knex_database_adapter.d.ts.map +1 -0
- package/dist/database/adapters/knex_database_adapter.js +729 -0
- package/dist/database/adapters/knex_database_adapter.js.map +1 -0
- package/dist/database/database_adapter.d.ts +262 -0
- package/dist/database/database_adapter.d.ts.map +1 -0
- package/dist/database/database_adapter.js +46 -0
- package/dist/database/database_adapter.js.map +1 -0
- package/dist/engine/digital_twin_engine.d.ts +295 -0
- package/dist/engine/digital_twin_engine.d.ts.map +1 -0
- package/dist/engine/digital_twin_engine.js +907 -0
- package/dist/engine/digital_twin_engine.js.map +1 -0
- package/dist/engine/endpoints.d.ts +47 -0
- package/dist/engine/endpoints.d.ts.map +1 -0
- package/dist/engine/endpoints.js +88 -0
- package/dist/engine/endpoints.js.map +1 -0
- 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 +93 -0
- package/dist/engine/events.d.ts.map +1 -0
- package/dist/engine/events.js +71 -0
- package/dist/engine/events.js.map +1 -0
- 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 +62 -0
- package/dist/engine/initializer.d.ts.map +1 -0
- package/dist/engine/initializer.js +108 -0
- package/dist/engine/initializer.js.map +1 -0
- package/dist/engine/queue_manager.d.ts +87 -0
- package/dist/engine/queue_manager.d.ts.map +1 -0
- package/dist/engine/queue_manager.js +196 -0
- package/dist/engine/queue_manager.js.map +1 -0
- package/dist/engine/scheduler.d.ts +30 -0
- package/dist/engine/scheduler.d.ts.map +1 -0
- package/dist/engine/scheduler.js +378 -0
- package/dist/engine/scheduler.js.map +1 -0
- package/dist/engine/upload_processor.d.ts +36 -0
- package/dist/engine/upload_processor.d.ts.map +1 -0
- package/dist/engine/upload_processor.js +113 -0
- package/dist/engine/upload_processor.js.map +1 -0
- package/dist/env/env.d.ts +134 -0
- package/dist/env/env.d.ts.map +1 -0
- package/dist/env/env.js +177 -0
- package/dist/env/env.js.map +1 -0
- 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 +55 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +65 -0
- package/dist/index.js.map +1 -0
- package/dist/openapi/generator.d.ts +93 -0
- package/dist/openapi/generator.d.ts.map +1 -0
- package/dist/openapi/generator.js +293 -0
- package/dist/openapi/generator.js.map +1 -0
- package/dist/openapi/index.d.ts +9 -0
- package/dist/openapi/index.d.ts.map +1 -0
- package/dist/openapi/index.js +9 -0
- package/dist/openapi/index.js.map +1 -0
- package/dist/openapi/types.d.ts +182 -0
- package/dist/openapi/types.d.ts.map +1 -0
- package/dist/openapi/types.js +16 -0
- package/dist/openapi/types.js.map +1 -0
- package/dist/storage/adapters/local_storage_service.d.ts +57 -0
- package/dist/storage/adapters/local_storage_service.d.ts.map +1 -0
- package/dist/storage/adapters/local_storage_service.js +132 -0
- package/dist/storage/adapters/local_storage_service.js.map +1 -0
- package/dist/storage/adapters/ovh_storage_service.d.ts +72 -0
- package/dist/storage/adapters/ovh_storage_service.d.ts.map +1 -0
- package/dist/storage/adapters/ovh_storage_service.js +205 -0
- package/dist/storage/adapters/ovh_storage_service.js.map +1 -0
- package/dist/storage/storage_factory.d.ts +14 -0
- package/dist/storage/storage_factory.d.ts.map +1 -0
- package/dist/storage/storage_factory.js +43 -0
- package/dist/storage/storage_factory.js.map +1 -0
- package/dist/storage/storage_service.d.ts +163 -0
- package/dist/storage/storage_service.d.ts.map +1 -0
- package/dist/storage/storage_service.js +58 -0
- package/dist/storage/storage_service.js.map +1 -0
- package/dist/types/data_record.d.ts +123 -0
- package/dist/types/data_record.d.ts.map +1 -0
- package/dist/types/data_record.js +8 -0
- package/dist/types/data_record.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 +175 -0
- package/dist/utils/http_responses.d.ts.map +1 -0
- package/dist/utils/http_responses.js +216 -0
- package/dist/utils/http_responses.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +74 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +92 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/map_to_data_record.d.ts +10 -0
- package/dist/utils/map_to_data_record.d.ts.map +1 -0
- package/dist/utils/map_to_data_record.js +36 -0
- package/dist/utils/map_to_data_record.js.map +1 -0
- 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/utils/servable_endpoint.d.ts +63 -0
- package/dist/utils/servable_endpoint.d.ts.map +1 -0
- package/dist/utils/servable_endpoint.js +67 -0
- package/dist/utils/servable_endpoint.js.map +1 -0
- package/dist/utils/zip_utils.d.ts +66 -0
- package/dist/utils/zip_utils.d.ts.map +1 -0
- package/dist/utils/zip_utils.js +169 -0
- package/dist/utils/zip_utils.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 +23 -13
package/README.md
CHANGED
|
@@ -1,360 +1,495 @@
|
|
|
1
|
-
# Digital Twin Core
|
|
2
|
-
|
|
3
|
-
Digital Twin Core is a minimalist TypeScript framework used to collect and process data for Digital Twin projects. It provides building blocks to create scheduled collectors, harvesters and HTTP handlers while abstracting storage and database access.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **Collectors** - fetch regular data from APIs (typically JSON) based on a Buffer schedule, store it and expose it via GET endpoints.
|
|
8
|
-
- **Harvesters** – transform data collected by collectors, store the results and expose them via GET endpoints.
|
|
9
|
-
- **Handlers** – expose GET endpoints that directly return the result of the method defined in the decorator.
|
|
10
|
-
- **Assets Manager** – upload, store and manage file assets with metadata, providing RESTful endpoints for CRUD operations.
|
|
11
|
-
- **Custom Table Manager** – manage structured data in custom database tables with automatic CRUD endpoints and custom business logic endpoints.
|
|
12
|
-
- **Storage adapters** – currently local filesystem and OVH Object Storage via S3 API.
|
|
13
|
-
- **Database adapter** – implemented with [Knex](https://knexjs.org/) to index metadata.
|
|
14
|
-
- **Engine** – orchestrates components, schedules jobs with BullMQ and exposes endpoints via Express.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
54
|
-
import {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
engine
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
-
|
|
77
|
-
-
|
|
78
|
-
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
-
|
|
89
|
-
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
- `
|
|
96
|
-
- `
|
|
97
|
-
- `GET /{assetType}/{id}
|
|
98
|
-
- `
|
|
99
|
-
- `
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
-
|
|
121
|
-
-
|
|
122
|
-
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
-
|
|
149
|
-
-
|
|
150
|
-
-
|
|
151
|
-
-
|
|
152
|
-
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
- `
|
|
157
|
-
- `
|
|
158
|
-
- `
|
|
159
|
-
- `
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
'
|
|
171
|
-
'
|
|
172
|
-
'
|
|
173
|
-
'
|
|
174
|
-
'
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
endpoints
|
|
178
|
-
|
|
179
|
-
{ path: '/
|
|
180
|
-
{ path: '/
|
|
181
|
-
{ path: '/
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
layer.
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
- `
|
|
315
|
-
- `
|
|
316
|
-
- `
|
|
317
|
-
- `
|
|
318
|
-
- `
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
-
|
|
322
|
-
- `
|
|
323
|
-
- `
|
|
324
|
-
- `
|
|
325
|
-
- `
|
|
326
|
-
- `
|
|
327
|
-
- `
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
1
|
+
# Digital Twin Core
|
|
2
|
+
|
|
3
|
+
Digital Twin Core is a minimalist TypeScript framework used to collect and process data for Digital Twin projects. It provides building blocks to create scheduled collectors, harvesters and HTTP handlers while abstracting storage and database access.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Collectors** - fetch regular data from APIs (typically JSON) based on a Buffer schedule, store it and expose it via GET endpoints.
|
|
8
|
+
- **Harvesters** – transform data collected by collectors, store the results and expose them via GET endpoints.
|
|
9
|
+
- **Handlers** – expose GET endpoints that directly return the result of the method defined in the decorator.
|
|
10
|
+
- **Assets Manager** – upload, store and manage file assets with metadata, providing RESTful endpoints for CRUD operations.
|
|
11
|
+
- **Custom Table Manager** – manage structured data in custom database tables with automatic CRUD endpoints and custom business logic endpoints.
|
|
12
|
+
- **Storage adapters** – currently local filesystem and OVH Object Storage via S3 API.
|
|
13
|
+
- **Database adapter** – implemented with [Knex](https://knexjs.org/) to index metadata.
|
|
14
|
+
- **Engine** – orchestrates components, schedules jobs with BullMQ and exposes endpoints via Express.
|
|
15
|
+
- **Authentication** – pluggable authentication system supporting API gateway headers, JWT tokens, or no-auth mode.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm add digitaltwin-core
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The project requires Node.js 20 or later.
|
|
24
|
+
|
|
25
|
+
## Building
|
|
26
|
+
|
|
27
|
+
Compile the TypeScript sources to `dist/`:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm run build
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
During development you can use the watcher:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm run dev
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Running tests
|
|
40
|
+
|
|
41
|
+
The test suite uses [Japa](https://github.com/japa/runner). Run all tests with:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm test
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Example usage
|
|
48
|
+
|
|
49
|
+
Below is a very small example showing how the engine may be instantiated. Storage and database implementations are selected through the provided factories.
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { DigitalTwinEngine } from './src/engine/digital_twin_engine.js';
|
|
53
|
+
import { StorageServiceFactory } from './src/storage/storage_factory.js';
|
|
54
|
+
import { KnexDatabaseAdapter } from './src/database/adapters/knex_database_adapter.js';
|
|
55
|
+
import { Env } from './src/.env/.env.js';
|
|
56
|
+
|
|
57
|
+
// Validate environment variables and bootstrap services
|
|
58
|
+
const env = Env.validate({
|
|
59
|
+
STORAGE_CONFIG: Env.schema.enum(['local', 'ovh'])
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const storage = StorageServiceFactory.create();
|
|
63
|
+
const database = new KnexDatabaseAdapter({ client: 'sqlite3', connection: ':memory:' }, storage);
|
|
64
|
+
|
|
65
|
+
const engine = new DigitalTwinEngine({ storage, database });
|
|
66
|
+
engine.start();
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Components
|
|
70
|
+
|
|
71
|
+
### Collectors
|
|
72
|
+
|
|
73
|
+
Collectors are scheduled components that fetch data from external sources at regular intervals. They implement a `collect()` method that returns a Buffer, which is then stored and exposed via HTTP endpoints.
|
|
74
|
+
|
|
75
|
+
**Key features:**
|
|
76
|
+
- Cron-based scheduling
|
|
77
|
+
- Automatic storage and metadata indexing
|
|
78
|
+
- HTTP GET endpoint for retrieving latest data
|
|
79
|
+
- Event emission on successful collection
|
|
80
|
+
|
|
81
|
+
### Assets Manager
|
|
82
|
+
|
|
83
|
+
The Assets Manager provides a complete solution for file asset management with metadata support. It's an abstract base class that can be extended for specific asset types.
|
|
84
|
+
|
|
85
|
+
**Key features:**
|
|
86
|
+
- File upload with metadata (description, source URL, owner, filename)
|
|
87
|
+
- RESTful CRUD operations via HTTP endpoints
|
|
88
|
+
- Content-type aware storage and retrieval
|
|
89
|
+
- Separate display and download endpoints
|
|
90
|
+
- Source URL validation for data provenance
|
|
91
|
+
- File extension validation for upload security
|
|
92
|
+
- Component isolation (each manager handles its own asset type)
|
|
93
|
+
|
|
94
|
+
**Available endpoints:**
|
|
95
|
+
- `GET /{assetType}` - List all assets with metadata
|
|
96
|
+
- `POST /{assetType}/upload` - Upload new asset with metadata
|
|
97
|
+
- `GET /{assetType}/{id}` - Retrieve asset content for display
|
|
98
|
+
- `GET /{assetType}/{id}/download` - Download asset with attachment headers
|
|
99
|
+
- `PUT /{assetType}/{id}` - Update asset metadata
|
|
100
|
+
- `DELETE /{assetType}/{id}` - Delete asset
|
|
101
|
+
|
|
102
|
+
**Example usage:**
|
|
103
|
+
```typescript
|
|
104
|
+
class GLTFAssetsManager extends AssetsManager {
|
|
105
|
+
getConfiguration() {
|
|
106
|
+
return {
|
|
107
|
+
name: 'gltf',
|
|
108
|
+
description: 'GLTF 3D models manager',
|
|
109
|
+
contentType: 'model/gltf-binary',
|
|
110
|
+
extension: '.glb', // Optional: restricts uploads to .glb files only
|
|
111
|
+
tags: ['assets', '3d', 'gltf']
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**File Extension Validation:**
|
|
118
|
+
|
|
119
|
+
When the `extension` property is set in the configuration, the Assets Manager will automatically validate uploaded files:
|
|
120
|
+
- POST `/upload` and POST `/upload-batch` endpoints will reject files that don't match the specified extension
|
|
121
|
+
- Validation is case-insensitive (`.GLB` and `.glb` are treated the same)
|
|
122
|
+
- If no extension is specified, all file types are accepted
|
|
123
|
+
- Error message clearly indicates the expected extension
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
// Example with extension validation
|
|
127
|
+
class DocumentsManager extends AssetsManager {
|
|
128
|
+
getConfiguration() {
|
|
129
|
+
return {
|
|
130
|
+
name: 'documents',
|
|
131
|
+
description: 'PDF documents manager',
|
|
132
|
+
contentType: 'application/pdf',
|
|
133
|
+
extension: '.pdf' // Only PDF files allowed
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Upload attempt with wrong extension will return:
|
|
139
|
+
// Status: 400
|
|
140
|
+
// Error: "Invalid file extension. Expected: .pdf"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Custom Table Manager
|
|
144
|
+
|
|
145
|
+
The Custom Table Manager provides a powerful solution for managing structured data with custom database tables. It automatically generates CRUD endpoints and supports custom business logic endpoints.
|
|
146
|
+
|
|
147
|
+
**Key features:**
|
|
148
|
+
- Custom database table creation with configurable columns and SQL types
|
|
149
|
+
- Automatic CRUD endpoints (GET, POST, PUT, DELETE)
|
|
150
|
+
- Custom business logic endpoints with full request/response control
|
|
151
|
+
- Query validation and field requirements
|
|
152
|
+
- Built-in search and filtering capabilities
|
|
153
|
+
- Support for complex data relationships
|
|
154
|
+
|
|
155
|
+
**Available endpoints (automatic):**
|
|
156
|
+
- `GET /{tableName}` - List all records
|
|
157
|
+
- `POST /{tableName}` - Create new record
|
|
158
|
+
- `GET /{tableName}/{id}` - Get specific record
|
|
159
|
+
- `PUT /{tableName}/{id}` - Update specific record
|
|
160
|
+
- `DELETE /{tableName}/{id}` - Delete specific record
|
|
161
|
+
|
|
162
|
+
**Example usage:**
|
|
163
|
+
```typescript
|
|
164
|
+
class WMSLayersManager extends CustomTableManager {
|
|
165
|
+
getConfiguration() {
|
|
166
|
+
return {
|
|
167
|
+
name: 'wms_layers',
|
|
168
|
+
description: 'Manage WMS layers for mapping applications',
|
|
169
|
+
columns: {
|
|
170
|
+
'wms_url': 'text not null',
|
|
171
|
+
'layer_name': 'text not null',
|
|
172
|
+
'description': 'text',
|
|
173
|
+
'active': 'boolean default true',
|
|
174
|
+
'created_by': 'text',
|
|
175
|
+
'projection': 'text default "EPSG:4326"'
|
|
176
|
+
},
|
|
177
|
+
// Custom endpoints for business logic
|
|
178
|
+
endpoints: [
|
|
179
|
+
{ path: '/add-layers', method: 'post', handler: 'addMultipleLayers' },
|
|
180
|
+
{ path: '/activate/:id', method: 'put', handler: 'toggleLayerStatus' },
|
|
181
|
+
{ path: '/search', method: 'get', handler: 'searchLayers' },
|
|
182
|
+
{ path: '/by-projection/:projection', method: 'get', handler: 'findByProjection' }
|
|
183
|
+
]
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Custom endpoint: Add multiple layers at once
|
|
188
|
+
async addMultipleLayers(req: any): Promise<DataResponse> {
|
|
189
|
+
try {
|
|
190
|
+
const { layers } = req.body
|
|
191
|
+
const results = []
|
|
192
|
+
|
|
193
|
+
for (const layerData of layers) {
|
|
194
|
+
// Use built-in validation
|
|
195
|
+
const id = await this.create({
|
|
196
|
+
wms_url: layerData.url,
|
|
197
|
+
layer_name: layerData.name,
|
|
198
|
+
description: layerData.description || '',
|
|
199
|
+
active: true,
|
|
200
|
+
created_by: layerData.user || 'system'
|
|
201
|
+
})
|
|
202
|
+
results.push({ id, name: layerData.name })
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
status: 200,
|
|
207
|
+
content: JSON.stringify({
|
|
208
|
+
message: `Successfully added ${results.length} layers`,
|
|
209
|
+
layers: results
|
|
210
|
+
}),
|
|
211
|
+
headers: { 'Content-Type': 'application/json' }
|
|
212
|
+
}
|
|
213
|
+
} catch (error) {
|
|
214
|
+
return {
|
|
215
|
+
status: 400,
|
|
216
|
+
content: JSON.stringify({ error: error.message }),
|
|
217
|
+
headers: { 'Content-Type': 'application/json' }
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Custom endpoint: Toggle layer active status
|
|
223
|
+
async toggleLayerStatus(req: any): Promise<DataResponse> {
|
|
224
|
+
try {
|
|
225
|
+
const { id } = req.params
|
|
226
|
+
const layer = await this.findById(parseInt(id))
|
|
227
|
+
|
|
228
|
+
if (!layer) {
|
|
229
|
+
return {
|
|
230
|
+
status: 404,
|
|
231
|
+
content: JSON.stringify({ error: 'Layer not found' }),
|
|
232
|
+
headers: { 'Content-Type': 'application/json' }
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const newStatus = !layer.active
|
|
237
|
+
await this.update(parseInt(id), { active: newStatus })
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
status: 200,
|
|
241
|
+
content: JSON.stringify({
|
|
242
|
+
message: `Layer ${newStatus ? 'activated' : 'deactivated'}`,
|
|
243
|
+
layer_id: id,
|
|
244
|
+
active: newStatus
|
|
245
|
+
}),
|
|
246
|
+
headers: { 'Content-Type': 'application/json' }
|
|
247
|
+
}
|
|
248
|
+
} catch (error) {
|
|
249
|
+
return {
|
|
250
|
+
status: 500,
|
|
251
|
+
content: JSON.stringify({ error: error.message }),
|
|
252
|
+
headers: { 'Content-Type': 'application/json' }
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Custom endpoint: Advanced search with validation
|
|
258
|
+
async searchLayers(req: any): Promise<DataResponse> {
|
|
259
|
+
try {
|
|
260
|
+
const { query, active_only, projection } = req.query
|
|
261
|
+
const conditions: Record<string, any> = {}
|
|
262
|
+
|
|
263
|
+
if (active_only === 'true') {
|
|
264
|
+
conditions.active = true
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (projection) {
|
|
268
|
+
conditions.projection = projection
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Use built-in search with validation
|
|
272
|
+
const layers = await this.findByColumns(conditions, {
|
|
273
|
+
validate: (conditions) => {
|
|
274
|
+
if (query && query.length < 3) {
|
|
275
|
+
throw new Error('Search query must be at least 3 characters long')
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
// Filter by text search if provided
|
|
281
|
+
let results = layers
|
|
282
|
+
if (query) {
|
|
283
|
+
results = layers.filter(layer =>
|
|
284
|
+
layer.layer_name.toLowerCase().includes(query.toLowerCase()) ||
|
|
285
|
+
layer.description?.toLowerCase().includes(query.toLowerCase())
|
|
286
|
+
)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
status: 200,
|
|
291
|
+
content: JSON.stringify({
|
|
292
|
+
results,
|
|
293
|
+
total: results.length,
|
|
294
|
+
query: { query, active_only, projection }
|
|
295
|
+
}),
|
|
296
|
+
headers: { 'Content-Type': 'application/json' }
|
|
297
|
+
}
|
|
298
|
+
} catch (error) {
|
|
299
|
+
return {
|
|
300
|
+
status: 400,
|
|
301
|
+
content: JSON.stringify({ error: error.message }),
|
|
302
|
+
headers: { 'Content-Type': 'application/json' }
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**Generated endpoints for above example:**
|
|
310
|
+
- Standard CRUD: `GET /wms_layers`, `POST /wms_layers`, etc.
|
|
311
|
+
- Custom business logic: `POST /wms_layers/add-layers`, `PUT /wms_layers/activate/:id`, `GET /wms_layers/search`
|
|
312
|
+
|
|
313
|
+
**SQL Types supported:**
|
|
314
|
+
- `text` / `text not null` - Variable length text
|
|
315
|
+
- `varchar(255)` / `varchar(100) not null` - Fixed length text
|
|
316
|
+
- `integer` / `integer not null` - Whole numbers
|
|
317
|
+
- `boolean` / `boolean default true` - True/false values
|
|
318
|
+
- `datetime` / `timestamp` - Date and time values
|
|
319
|
+
- `real` / `decimal` / `float` - Decimal numbers
|
|
320
|
+
|
|
321
|
+
**Built-in query methods:**
|
|
322
|
+
- `findAll()` - Get all records
|
|
323
|
+
- `findById(id)` - Get specific record
|
|
324
|
+
- `findByColumn(column, value)` - Search by single column
|
|
325
|
+
- `findByColumns(conditions, validation)` - Advanced search with validation
|
|
326
|
+
- `create(data)` - Create new record
|
|
327
|
+
- `update(id, data)` - Update existing record
|
|
328
|
+
- `delete(id)` - Delete record
|
|
329
|
+
|
|
330
|
+
## Request Validation
|
|
331
|
+
|
|
332
|
+
The framework includes VineJS integration for type-safe request validation:
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
import { validate, AssetUploadSchema } from 'digitaltwin-core'
|
|
336
|
+
|
|
337
|
+
// In your component
|
|
338
|
+
async handleUpload(req: any) {
|
|
339
|
+
const data = await validate(AssetUploadSchema, req.body)
|
|
340
|
+
// data is now typed and validated
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Validation errors return HTTP 422 with detailed error messages:
|
|
345
|
+
|
|
346
|
+
```json
|
|
347
|
+
{
|
|
348
|
+
"error": "Validation failed",
|
|
349
|
+
"details": [
|
|
350
|
+
{ "field": "description", "message": "The description field must be a string" }
|
|
351
|
+
]
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## Error Handling
|
|
356
|
+
|
|
357
|
+
The framework provides custom error classes for structured error handling:
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
import { CollectorError, ValidationError, StorageError } from 'digitaltwin-core'
|
|
361
|
+
|
|
362
|
+
// Errors include context
|
|
363
|
+
throw new CollectorError('Failed to fetch data', 'weather-collector', originalError)
|
|
364
|
+
|
|
365
|
+
// Validation errors return 422
|
|
366
|
+
throw new ValidationError('Invalid input', details)
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
All component errors are caught and logged with context (component name, stack trace). Non-critical operations use `safeAsync` to log errors without crashing:
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
import { safeAsync } from 'digitaltwin-core'
|
|
373
|
+
|
|
374
|
+
// Won't throw, just logs on failure
|
|
375
|
+
await safeAsync(() => cleanup(), 'cleanup temporary files', logger)
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## Production Features
|
|
379
|
+
|
|
380
|
+
### Graceful Shutdown
|
|
381
|
+
|
|
382
|
+
The engine supports graceful shutdown with configurable timeout:
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
const engine = new DigitalTwinEngine({ database, storage })
|
|
386
|
+
|
|
387
|
+
// Configure shutdown timeout (default: 30s)
|
|
388
|
+
engine.setShutdownTimeout(60000)
|
|
389
|
+
|
|
390
|
+
// Check if shutting down
|
|
391
|
+
if (engine.isShuttingDown()) {
|
|
392
|
+
// Don't accept new work
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Graceful stop
|
|
396
|
+
await engine.stop()
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Health Checks
|
|
400
|
+
|
|
401
|
+
Register custom health checks for monitoring:
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
engine.registerHealthCheck('external-api', async () => {
|
|
405
|
+
const response = await fetch('https://api.example.com/health')
|
|
406
|
+
return { status: response.ok ? 'up' : 'down' }
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
// Built-in checks: database, redis (if configured)
|
|
410
|
+
const names = engine.getHealthCheckNames() // ['database', 'redis', 'external-api']
|
|
411
|
+
|
|
412
|
+
// Remove check
|
|
413
|
+
engine.removeHealthCheck('external-api')
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### OpenAPI Specification
|
|
417
|
+
|
|
418
|
+
Generate OpenAPI 3.0 specs from your components:
|
|
419
|
+
|
|
420
|
+
```typescript
|
|
421
|
+
import { OpenAPIGenerator } from 'digitaltwin-core'
|
|
422
|
+
|
|
423
|
+
const spec = OpenAPIGenerator.generate({
|
|
424
|
+
info: { title: 'My API', version: '1.0.0' },
|
|
425
|
+
components: [collector, assetsManager, handler]
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
// Output as JSON or YAML
|
|
429
|
+
const json = OpenAPIGenerator.toJSON(spec)
|
|
430
|
+
const yaml = OpenAPIGenerator.toYAML(spec)
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
## Authentication
|
|
434
|
+
|
|
435
|
+
The framework supports multiple authentication modes:
|
|
436
|
+
|
|
437
|
+
- **Gateway** (default): Uses headers from API gateways (Apache APISIX, KrakenD)
|
|
438
|
+
- **JWT**: Direct JWT token validation
|
|
439
|
+
- **None**: Disabled for development/testing
|
|
440
|
+
|
|
441
|
+
### Gateway Mode (Default)
|
|
442
|
+
|
|
443
|
+
No configuration needed. The framework reads `x-user-id` and `x-user-roles` headers set by your API gateway.
|
|
444
|
+
|
|
445
|
+
### JWT Mode
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
export AUTH_MODE=jwt
|
|
449
|
+
export JWT_SECRET=your-secret-key
|
|
450
|
+
# Or for RSA: JWT_PUBLIC_KEY or JWT_PUBLIC_KEY_FILE
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Disable Authentication
|
|
454
|
+
|
|
455
|
+
```bash
|
|
456
|
+
export DIGITALTWIN_DISABLE_AUTH=true
|
|
457
|
+
# Or
|
|
458
|
+
export AUTH_MODE=none
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
For detailed configuration options, see [src/auth/README.md](src/auth/README.md).
|
|
462
|
+
|
|
463
|
+
## Project Scaffolding
|
|
464
|
+
|
|
465
|
+
Use [create-digitaltwin](https://github.com/CePseudoBE/create-digitaltwin) to quickly bootstrap new projects:
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
npm init digitaltwin my-project
|
|
469
|
+
cd my-project
|
|
470
|
+
npm install
|
|
471
|
+
npm run dev
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
Generated projects include [digitaltwin-cli](https://github.com/CePseudoBE/digitaltwin-cli) for component generation:
|
|
475
|
+
|
|
476
|
+
```bash
|
|
477
|
+
node dt make:collector WeatherCollector --description "Weather data collector"
|
|
478
|
+
node dt make:handler ApiHandler --method post
|
|
479
|
+
node dt make:harvester DataProcessor --source weather-collector
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
## Folder structure
|
|
483
|
+
|
|
484
|
+
- `src/` – framework sources
|
|
485
|
+
- `auth/` – authentication providers and user management
|
|
486
|
+
- `components/` – base classes for collectors, harvesters, handlers and assets manager
|
|
487
|
+
- `engine/` – orchestration logic
|
|
488
|
+
- `storage/` – storage service abstractions and adapters
|
|
489
|
+
- `database/` – metadata database adapter
|
|
490
|
+
- `env/` – environment configuration helper
|
|
491
|
+
- `tests/` – unit tests
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
360
495
|
This project is licensed under the MIT License.
|