digitaltwin-core 0.13.3 → 0.14.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.
Files changed (169) hide show
  1. package/package.json +101 -106
  2. package/dist/auth/apisix_parser.d.ts +0 -146
  3. package/dist/auth/apisix_parser.d.ts.map +0 -1
  4. package/dist/auth/apisix_parser.js +0 -185
  5. package/dist/auth/apisix_parser.js.map +0 -1
  6. package/dist/auth/auth_config.d.ts +0 -126
  7. package/dist/auth/auth_config.d.ts.map +0 -1
  8. package/dist/auth/auth_config.js +0 -169
  9. package/dist/auth/auth_config.js.map +0 -1
  10. package/dist/auth/index.d.ts +0 -5
  11. package/dist/auth/index.d.ts.map +0 -1
  12. package/dist/auth/index.js +0 -4
  13. package/dist/auth/index.js.map +0 -1
  14. package/dist/auth/types.d.ts +0 -100
  15. package/dist/auth/types.d.ts.map +0 -1
  16. package/dist/auth/types.js +0 -2
  17. package/dist/auth/types.js.map +0 -1
  18. package/dist/auth/user_service.d.ts +0 -86
  19. package/dist/auth/user_service.d.ts.map +0 -1
  20. package/dist/auth/user_service.js +0 -237
  21. package/dist/auth/user_service.js.map +0 -1
  22. package/dist/components/assets_manager.d.ts +0 -662
  23. package/dist/components/assets_manager.d.ts.map +0 -1
  24. package/dist/components/assets_manager.js +0 -1529
  25. package/dist/components/assets_manager.js.map +0 -1
  26. package/dist/components/async_upload.d.ts +0 -20
  27. package/dist/components/async_upload.d.ts.map +0 -1
  28. package/dist/components/async_upload.js +0 -10
  29. package/dist/components/async_upload.js.map +0 -1
  30. package/dist/components/collector.d.ts +0 -203
  31. package/dist/components/collector.d.ts.map +0 -1
  32. package/dist/components/collector.js +0 -202
  33. package/dist/components/collector.js.map +0 -1
  34. package/dist/components/custom_table_manager.d.ts +0 -503
  35. package/dist/components/custom_table_manager.d.ts.map +0 -1
  36. package/dist/components/custom_table_manager.js +0 -1052
  37. package/dist/components/custom_table_manager.js.map +0 -1
  38. package/dist/components/global_assets_handler.d.ts +0 -63
  39. package/dist/components/global_assets_handler.d.ts.map +0 -1
  40. package/dist/components/global_assets_handler.js +0 -127
  41. package/dist/components/global_assets_handler.js.map +0 -1
  42. package/dist/components/handler.d.ts +0 -104
  43. package/dist/components/handler.d.ts.map +0 -1
  44. package/dist/components/handler.js +0 -110
  45. package/dist/components/handler.js.map +0 -1
  46. package/dist/components/harvester.d.ts +0 -182
  47. package/dist/components/harvester.d.ts.map +0 -1
  48. package/dist/components/harvester.js +0 -393
  49. package/dist/components/harvester.js.map +0 -1
  50. package/dist/components/index.d.ts +0 -11
  51. package/dist/components/index.d.ts.map +0 -1
  52. package/dist/components/index.js +0 -9
  53. package/dist/components/index.js.map +0 -1
  54. package/dist/components/interfaces.d.ts +0 -126
  55. package/dist/components/interfaces.d.ts.map +0 -1
  56. package/dist/components/interfaces.js +0 -8
  57. package/dist/components/interfaces.js.map +0 -1
  58. package/dist/components/map_manager.d.ts +0 -61
  59. package/dist/components/map_manager.d.ts.map +0 -1
  60. package/dist/components/map_manager.js +0 -242
  61. package/dist/components/map_manager.js.map +0 -1
  62. package/dist/components/tileset_manager.d.ts +0 -125
  63. package/dist/components/tileset_manager.d.ts.map +0 -1
  64. package/dist/components/tileset_manager.js +0 -618
  65. package/dist/components/tileset_manager.js.map +0 -1
  66. package/dist/components/types.d.ts +0 -226
  67. package/dist/components/types.d.ts.map +0 -1
  68. package/dist/components/types.js +0 -8
  69. package/dist/components/types.js.map +0 -1
  70. package/dist/database/adapters/knex_database_adapter.d.ts +0 -92
  71. package/dist/database/adapters/knex_database_adapter.d.ts.map +0 -1
  72. package/dist/database/adapters/knex_database_adapter.js +0 -647
  73. package/dist/database/adapters/knex_database_adapter.js.map +0 -1
  74. package/dist/database/database_adapter.d.ts +0 -251
  75. package/dist/database/database_adapter.d.ts.map +0 -1
  76. package/dist/database/database_adapter.js +0 -46
  77. package/dist/database/database_adapter.js.map +0 -1
  78. package/dist/engine/digital_twin_engine.d.ts +0 -253
  79. package/dist/engine/digital_twin_engine.d.ts.map +0 -1
  80. package/dist/engine/digital_twin_engine.js +0 -790
  81. package/dist/engine/digital_twin_engine.js.map +0 -1
  82. package/dist/engine/endpoints.d.ts +0 -47
  83. package/dist/engine/endpoints.d.ts.map +0 -1
  84. package/dist/engine/endpoints.js +0 -56
  85. package/dist/engine/endpoints.js.map +0 -1
  86. package/dist/engine/events.d.ts +0 -93
  87. package/dist/engine/events.d.ts.map +0 -1
  88. package/dist/engine/events.js +0 -71
  89. package/dist/engine/events.js.map +0 -1
  90. package/dist/engine/initializer.d.ts +0 -62
  91. package/dist/engine/initializer.d.ts.map +0 -1
  92. package/dist/engine/initializer.js +0 -108
  93. package/dist/engine/initializer.js.map +0 -1
  94. package/dist/engine/queue_manager.d.ts +0 -87
  95. package/dist/engine/queue_manager.d.ts.map +0 -1
  96. package/dist/engine/queue_manager.js +0 -196
  97. package/dist/engine/queue_manager.js.map +0 -1
  98. package/dist/engine/scheduler.d.ts +0 -30
  99. package/dist/engine/scheduler.d.ts.map +0 -1
  100. package/dist/engine/scheduler.js +0 -370
  101. package/dist/engine/scheduler.js.map +0 -1
  102. package/dist/engine/upload_processor.d.ts +0 -36
  103. package/dist/engine/upload_processor.d.ts.map +0 -1
  104. package/dist/engine/upload_processor.js +0 -101
  105. package/dist/engine/upload_processor.js.map +0 -1
  106. package/dist/env/env.d.ts +0 -134
  107. package/dist/env/env.d.ts.map +0 -1
  108. package/dist/env/env.js +0 -177
  109. package/dist/env/env.js.map +0 -1
  110. package/dist/index.d.ts +0 -49
  111. package/dist/index.d.ts.map +0 -1
  112. package/dist/index.js +0 -57
  113. package/dist/index.js.map +0 -1
  114. package/dist/openapi/generator.d.ts +0 -93
  115. package/dist/openapi/generator.d.ts.map +0 -1
  116. package/dist/openapi/generator.js +0 -293
  117. package/dist/openapi/generator.js.map +0 -1
  118. package/dist/openapi/index.d.ts +0 -9
  119. package/dist/openapi/index.d.ts.map +0 -1
  120. package/dist/openapi/index.js +0 -9
  121. package/dist/openapi/index.js.map +0 -1
  122. package/dist/openapi/types.d.ts +0 -182
  123. package/dist/openapi/types.d.ts.map +0 -1
  124. package/dist/openapi/types.js +0 -16
  125. package/dist/openapi/types.js.map +0 -1
  126. package/dist/storage/adapters/local_storage_service.d.ts +0 -51
  127. package/dist/storage/adapters/local_storage_service.d.ts.map +0 -1
  128. package/dist/storage/adapters/local_storage_service.js +0 -110
  129. package/dist/storage/adapters/local_storage_service.js.map +0 -1
  130. package/dist/storage/adapters/ovh_storage_service.d.ts +0 -61
  131. package/dist/storage/adapters/ovh_storage_service.d.ts.map +0 -1
  132. package/dist/storage/adapters/ovh_storage_service.js +0 -172
  133. package/dist/storage/adapters/ovh_storage_service.js.map +0 -1
  134. package/dist/storage/storage_factory.d.ts +0 -14
  135. package/dist/storage/storage_factory.d.ts.map +0 -1
  136. package/dist/storage/storage_factory.js +0 -36
  137. package/dist/storage/storage_factory.js.map +0 -1
  138. package/dist/storage/storage_service.d.ts +0 -163
  139. package/dist/storage/storage_service.d.ts.map +0 -1
  140. package/dist/storage/storage_service.js +0 -54
  141. package/dist/storage/storage_service.js.map +0 -1
  142. package/dist/types/data_record.d.ts +0 -123
  143. package/dist/types/data_record.d.ts.map +0 -1
  144. package/dist/types/data_record.js +0 -8
  145. package/dist/types/data_record.js.map +0 -1
  146. package/dist/utils/http_responses.d.ts +0 -155
  147. package/dist/utils/http_responses.d.ts.map +0 -1
  148. package/dist/utils/http_responses.js +0 -190
  149. package/dist/utils/http_responses.js.map +0 -1
  150. package/dist/utils/index.d.ts +0 -8
  151. package/dist/utils/index.d.ts.map +0 -1
  152. package/dist/utils/index.js +0 -6
  153. package/dist/utils/index.js.map +0 -1
  154. package/dist/utils/logger.d.ts +0 -74
  155. package/dist/utils/logger.d.ts.map +0 -1
  156. package/dist/utils/logger.js +0 -92
  157. package/dist/utils/logger.js.map +0 -1
  158. package/dist/utils/map_to_data_record.d.ts +0 -10
  159. package/dist/utils/map_to_data_record.d.ts.map +0 -1
  160. package/dist/utils/map_to_data_record.js +0 -36
  161. package/dist/utils/map_to_data_record.js.map +0 -1
  162. package/dist/utils/servable_endpoint.d.ts +0 -63
  163. package/dist/utils/servable_endpoint.d.ts.map +0 -1
  164. package/dist/utils/servable_endpoint.js +0 -67
  165. package/dist/utils/servable_endpoint.js.map +0 -1
  166. package/dist/utils/zip_utils.d.ts +0 -66
  167. package/dist/utils/zip_utils.d.ts.map +0 -1
  168. package/dist/utils/zip_utils.js +0 -169
  169. package/dist/utils/zip_utils.js.map +0 -1
@@ -1,647 +0,0 @@
1
- import knex from 'knex';
2
- import { DatabaseAdapter } from '../database_adapter.js';
3
- import { mapToDataRecord } from '../../utils/map_to_data_record.js';
4
- /**
5
- * Knex-based implementation with extended querying capabilities.
6
- */
7
- export class KnexDatabaseAdapter extends DatabaseAdapter {
8
- #knex;
9
- #storage;
10
- constructor(config, storage) {
11
- super();
12
- this.#knex = knex(config);
13
- this.#storage = storage;
14
- }
15
- /**
16
- * Create a KnexDatabaseAdapter for PostgreSQL with simplified configuration
17
- */
18
- static forPostgreSQL(pgConfig, storage, _tableName = 'data_index') {
19
- const knexConfig = {
20
- client: 'pg',
21
- connection: {
22
- host: pgConfig.host,
23
- port: pgConfig.port || 5432,
24
- user: pgConfig.user,
25
- password: pgConfig.password,
26
- database: pgConfig.database,
27
- ssl: pgConfig.ssl || false
28
- },
29
- pool: {
30
- min: 2,
31
- max: 15,
32
- acquireTimeoutMillis: 30000,
33
- createTimeoutMillis: 30000,
34
- destroyTimeoutMillis: 5000,
35
- idleTimeoutMillis: 30000,
36
- reapIntervalMillis: 1000
37
- }
38
- };
39
- return new KnexDatabaseAdapter(knexConfig, storage);
40
- }
41
- /**
42
- * Create a KnexDatabaseAdapter for SQLite with simplified configuration
43
- */
44
- static forSQLite(sqliteConfig, storage, _tableName = 'data_index') {
45
- const client = sqliteConfig.client || 'sqlite3';
46
- const knexConfig = {
47
- client,
48
- connection: {
49
- filename: sqliteConfig.filename
50
- },
51
- pool: {
52
- min: 1,
53
- max: 5,
54
- acquireTimeoutMillis: sqliteConfig.busyTimeout || 30000,
55
- afterCreate: (conn, cb) => {
56
- if (sqliteConfig.enableForeignKeys !== false) {
57
- // Both sqlite3 and better-sqlite3 support PRAGMA
58
- if (client === 'better-sqlite3') {
59
- conn.pragma('foreign_keys = ON');
60
- conn.pragma('journal_mode = WAL');
61
- conn.pragma('synchronous = NORMAL');
62
- conn.pragma('cache_size = 10000');
63
- cb();
64
- }
65
- else {
66
- conn.run('PRAGMA foreign_keys = ON', () => {
67
- conn.run('PRAGMA journal_mode = WAL', () => {
68
- conn.run('PRAGMA synchronous = NORMAL', () => {
69
- conn.run('PRAGMA cache_size = 10000', cb);
70
- });
71
- });
72
- });
73
- }
74
- }
75
- else {
76
- cb();
77
- }
78
- }
79
- },
80
- useNullAsDefault: true
81
- };
82
- return new KnexDatabaseAdapter(knexConfig, storage);
83
- }
84
- // ========== Basic methods ==========
85
- async save(meta) {
86
- const insertData = {
87
- id: meta.id,
88
- name: meta.name,
89
- type: meta.type,
90
- url: meta.url,
91
- date: meta.date.toISOString()
92
- };
93
- // Add asset-specific fields if present (for AssetMetadataRow)
94
- if ('description' in meta)
95
- insertData.description = meta.description;
96
- if ('source' in meta)
97
- insertData.source = meta.source;
98
- if ('owner_id' in meta)
99
- insertData.owner_id = meta.owner_id;
100
- if ('filename' in meta)
101
- insertData.filename = meta.filename;
102
- if ('is_public' in meta)
103
- insertData.is_public = meta.is_public;
104
- // TilesetManager support (public URL)
105
- if ('tileset_url' in meta)
106
- insertData.tileset_url = meta.tileset_url;
107
- // Async upload support
108
- if ('upload_status' in meta)
109
- insertData.upload_status = meta.upload_status;
110
- if ('upload_error' in meta)
111
- insertData.upload_error = meta.upload_error;
112
- if ('upload_job_id' in meta)
113
- insertData.upload_job_id = meta.upload_job_id;
114
- // Insert and get the auto-generated ID
115
- const [insertedId] = await this.#knex(meta.name).insert(insertData).returning('id');
116
- // Handle different return formats (PostgreSQL returns object, SQLite returns number)
117
- const newId = typeof insertedId === 'object' ? insertedId.id : insertedId;
118
- // Return record with the generated ID
119
- return mapToDataRecord({ ...meta, id: newId }, this.#storage);
120
- }
121
- async delete(id, name) {
122
- await this.#knex(name).where({ id }).delete();
123
- }
124
- async getById(id, name) {
125
- const row = await this.#knex(name).where({ id }).first();
126
- return row ? mapToDataRecord(row, this.#storage) : undefined;
127
- }
128
- async getLatestByName(name) {
129
- const row = await this.#knex(name).select('*').orderBy('date', 'desc').limit(1).first();
130
- return row ? mapToDataRecord(row, this.#storage) : undefined;
131
- }
132
- async doesTableExists(name) {
133
- return this.#knex.schema.hasTable(name);
134
- }
135
- async createTable(name) {
136
- const tableExists = await this.#knex.schema.hasTable(name);
137
- if (!tableExists) {
138
- await this.#knex.schema.createTable(name, table => {
139
- table.increments('id').primary();
140
- table.string('name').notNullable();
141
- table.string('type').notNullable();
142
- table.string('url').notNullable();
143
- table.datetime('date').notNullable();
144
- // Asset-specific fields (optional, for AssetsManager components)
145
- table.text('description').nullable();
146
- table.string('source').nullable();
147
- table.integer('owner_id').unsigned().nullable();
148
- table.string('filename').nullable();
149
- table.boolean('is_public').defaultTo(true).notNullable();
150
- // TilesetManager support (public URL for Cesium)
151
- table.text('tileset_url').nullable();
152
- // Async upload support (for large file processing)
153
- table.string('upload_status', 20).nullable(); // pending, processing, completed, failed
154
- table.text('upload_error').nullable();
155
- table.string('upload_job_id', 100).nullable(); // BullMQ job ID for status tracking
156
- // Foreign key constraint to users table (if it exists)
157
- // Note: This will only work if users table exists first
158
- try {
159
- table.foreign('owner_id').references('id').inTable('users').onDelete('SET NULL');
160
- }
161
- catch {
162
- // Ignore foreign key creation if users table doesn't exist yet
163
- // This allows backward compatibility for non-authenticated assets
164
- }
165
- // Optimized indexes for most frequent queries
166
- table.index('name', `${name}_idx_name`);
167
- table.index('date', `${name}_idx_date`);
168
- table.index(['name', 'date'], `${name}_idx_name_date`);
169
- table.index(['date', 'name'], `${name}_idx_date_name`); // For date range queries
170
- table.index('owner_id', `${name}_idx_owner_id`); // For asset filtering and foreign key
171
- table.index('is_public', `${name}_idx_is_public`); // For visibility filtering
172
- });
173
- }
174
- }
175
- async createTableWithColumns(name, columns) {
176
- const tableExists = await this.#knex.schema.hasTable(name);
177
- if (!tableExists) {
178
- await this.#knex.schema.createTable(name, table => {
179
- // Standard columns for CustomTableManager
180
- table.increments('id').primary();
181
- table.datetime('created_at').defaultTo(this.#knex.fn.now()).notNullable();
182
- table.datetime('updated_at').defaultTo(this.#knex.fn.now()).notNullable();
183
- // Custom columns from StoreConfiguration
184
- for (const [columnName, columnType] of Object.entries(columns)) {
185
- // Parse SQL type and apply it to the table
186
- this.#addColumnToTable(table, columnName, columnType);
187
- }
188
- // Indexes for performance
189
- table.index('created_at', `${name}_idx_created_at`);
190
- table.index('updated_at', `${name}_idx_updated_at`);
191
- });
192
- }
193
- }
194
- /**
195
- * Helper method to add a column to a Knex table based on SQL type string
196
- * @private
197
- */
198
- #addColumnToTable(table, columnName, sqlType) {
199
- const lowerType = sqlType.toLowerCase();
200
- if (lowerType.includes('text')) {
201
- const col = table.text(columnName);
202
- if (lowerType.includes('not null'))
203
- col.notNullable();
204
- else
205
- col.nullable();
206
- }
207
- else if (lowerType.includes('integer')) {
208
- const col = table.integer(columnName);
209
- if (lowerType.includes('not null'))
210
- col.notNullable();
211
- else
212
- col.nullable();
213
- }
214
- else if (lowerType.includes('boolean')) {
215
- const col = table.boolean(columnName);
216
- if (lowerType.includes('not null'))
217
- col.notNullable();
218
- else
219
- col.nullable();
220
- if (lowerType.includes('default true'))
221
- col.defaultTo(true);
222
- else if (lowerType.includes('default false'))
223
- col.defaultTo(false);
224
- }
225
- else if (lowerType.includes('timestamp') || lowerType.includes('datetime')) {
226
- const col = table.datetime(columnName);
227
- if (lowerType.includes('not null'))
228
- col.notNullable();
229
- else
230
- col.nullable();
231
- if (lowerType.includes('default current_timestamp'))
232
- col.defaultTo(this.#knex.fn.now());
233
- }
234
- else if (lowerType.includes('real') || lowerType.includes('decimal') || lowerType.includes('float')) {
235
- const col = table.decimal(columnName);
236
- if (lowerType.includes('not null'))
237
- col.notNullable();
238
- else
239
- col.nullable();
240
- }
241
- else if (lowerType.includes('varchar')) {
242
- // Extract length from varchar(255)
243
- const match = lowerType.match(/varchar\((\d+)\)/);
244
- const length = match ? parseInt(match[1]) : 255;
245
- const col = table.string(columnName, length);
246
- if (lowerType.includes('not null'))
247
- col.notNullable();
248
- else
249
- col.nullable();
250
- }
251
- else {
252
- // Default to string for unknown types
253
- const col = table.string(columnName);
254
- if (lowerType.includes('not null'))
255
- col.notNullable();
256
- else
257
- col.nullable();
258
- }
259
- }
260
- /**
261
- * Migrate existing table schema to match expected schema.
262
- *
263
- * Automatically adds missing columns and indexes for asset tables.
264
- * Only performs safe operations (adding columns with defaults or nullable).
265
- *
266
- * @param {string} name - Table name to migrate
267
- * @returns {Promise<string[]>} Array of migration messages describing what was done
268
- */
269
- async migrateTableSchema(name) {
270
- const tableExists = await this.#knex.schema.hasTable(name);
271
- if (!tableExists) {
272
- return []; // Table doesn't exist, nothing to migrate
273
- }
274
- const migrations = [];
275
- // Define expected columns for asset tables (those created by createTable)
276
- const expectedColumns = {
277
- is_public: {
278
- exists: await this.#knex.schema.hasColumn(name, 'is_public'),
279
- add: async () => {
280
- await this.#knex.schema.alterTable(name, table => {
281
- table.boolean('is_public').defaultTo(true).notNullable();
282
- });
283
- migrations.push(`Added column 'is_public' (BOOLEAN DEFAULT true NOT NULL)`);
284
- }
285
- },
286
- tileset_url: {
287
- exists: await this.#knex.schema.hasColumn(name, 'tileset_url'),
288
- add: async () => {
289
- await this.#knex.schema.alterTable(name, table => {
290
- table.text('tileset_url').nullable();
291
- });
292
- migrations.push(`Added column 'tileset_url' (TEXT nullable)`);
293
- }
294
- },
295
- upload_status: {
296
- exists: await this.#knex.schema.hasColumn(name, 'upload_status'),
297
- add: async () => {
298
- await this.#knex.schema.alterTable(name, table => {
299
- table.string('upload_status', 20).nullable().defaultTo(null);
300
- });
301
- migrations.push(`Added column 'upload_status' (VARCHAR(20) nullable)`);
302
- }
303
- },
304
- upload_error: {
305
- exists: await this.#knex.schema.hasColumn(name, 'upload_error'),
306
- add: async () => {
307
- await this.#knex.schema.alterTable(name, table => {
308
- table.text('upload_error').nullable();
309
- });
310
- migrations.push(`Added column 'upload_error' (TEXT nullable)`);
311
- }
312
- },
313
- upload_job_id: {
314
- exists: await this.#knex.schema.hasColumn(name, 'upload_job_id'),
315
- add: async () => {
316
- await this.#knex.schema.alterTable(name, table => {
317
- table.string('upload_job_id', 100).nullable();
318
- });
319
- migrations.push(`Added column 'upload_job_id' (VARCHAR(100) nullable)`);
320
- }
321
- },
322
- created_at: {
323
- exists: await this.#knex.schema.hasColumn(name, 'created_at'),
324
- add: async () => {
325
- await this.#knex.schema.alterTable(name, table => {
326
- table.datetime('created_at').defaultTo(this.#knex.fn.now()).nullable();
327
- });
328
- migrations.push(`Added column 'created_at' (DATETIME nullable)`);
329
- }
330
- },
331
- updated_at: {
332
- exists: await this.#knex.schema.hasColumn(name, 'updated_at'),
333
- add: async () => {
334
- await this.#knex.schema.alterTable(name, table => {
335
- table.datetime('updated_at').defaultTo(this.#knex.fn.now()).nullable();
336
- });
337
- migrations.push(`Added column 'updated_at' (DATETIME nullable)`);
338
- }
339
- }
340
- };
341
- // Expected indexes
342
- const expectedIndexes = {
343
- [`${name}_idx_is_public`]: {
344
- exists: await this.#hasIndex(name, `${name}_idx_is_public`),
345
- add: async () => {
346
- await this.#knex.schema.alterTable(name, table => {
347
- table.index('is_public', `${name}_idx_is_public`);
348
- });
349
- migrations.push(`Added index '${name}_idx_is_public'`);
350
- }
351
- }
352
- };
353
- // Add missing columns
354
- for (const [_columnName, config] of Object.entries(expectedColumns)) {
355
- if (!config.exists) {
356
- await config.add();
357
- }
358
- }
359
- // Add missing indexes
360
- for (const [_indexName, config] of Object.entries(expectedIndexes)) {
361
- if (!config.exists) {
362
- await config.add();
363
- }
364
- }
365
- return migrations;
366
- }
367
- /**
368
- * Check if an index exists on a table
369
- * @private
370
- */
371
- async #hasIndex(tableName, indexName) {
372
- try {
373
- // PostgreSQL
374
- if (this.#knex.client.config.client === 'pg') {
375
- const result = await this.#knex.raw(`SELECT 1 FROM pg_indexes WHERE tablename = ? AND indexname = ?`, [
376
- tableName,
377
- indexName
378
- ]);
379
- return result.rows.length > 0;
380
- }
381
- // SQLite - query sqlite_master
382
- if (this.#knex.client.config.client === 'sqlite3' || this.#knex.client.config.client === 'better-sqlite3') {
383
- const result = await this.#knex.raw(`SELECT name FROM sqlite_master WHERE type='index' AND name=?`, [
384
- indexName
385
- ]);
386
- return result.length > 0;
387
- }
388
- // Unknown database, assume index doesn't exist
389
- return false;
390
- }
391
- catch {
392
- // If query fails, assume index doesn't exist
393
- return false;
394
- }
395
- }
396
- // ========== Extended methods ==========
397
- async getFirstByName(name) {
398
- const row = await this.#knex(name).orderBy('date', 'asc').first();
399
- return row ? mapToDataRecord(row, this.#storage) : undefined;
400
- }
401
- async getByDateRange(name, startDate, endDate, limit) {
402
- let query = this.#knex(name).select('*').where('date', '>=', startDate.toISOString());
403
- if (endDate) {
404
- query = query.where('date', '<', endDate.toISOString());
405
- }
406
- query = query.orderBy('date', 'asc');
407
- if (limit) {
408
- query = query.limit(limit);
409
- }
410
- const rows = await query;
411
- return rows.map(row => mapToDataRecord(row, this.#storage));
412
- }
413
- async getAfterDate(name, afterDate, limit) {
414
- let query = this.#knex(name).where('date', '>', afterDate.toISOString()).orderBy('date', 'asc');
415
- if (limit) {
416
- query = query.limit(limit);
417
- }
418
- const rows = await query;
419
- return rows.map(row => mapToDataRecord(row, this.#storage));
420
- }
421
- async getLatestBefore(name, beforeDate) {
422
- const row = await this.#knex(name).where('date', '<', beforeDate.toISOString()).orderBy('date', 'desc').first();
423
- return row ? mapToDataRecord(row, this.#storage) : undefined;
424
- }
425
- async getLatestRecordsBefore(name, beforeDate, limit) {
426
- const rows = await this.#knex(name)
427
- .where('date', '<', beforeDate.toISOString())
428
- .orderBy('date', 'desc')
429
- .limit(limit);
430
- return rows.map(row => mapToDataRecord(row, this.#storage));
431
- }
432
- async hasRecordsAfterDate(name, afterDate) {
433
- const result = await this.#knex(name)
434
- .where('date', '>', afterDate.toISOString())
435
- .select(this.#knex.raw('1'))
436
- .limit(1)
437
- .first();
438
- return !!result;
439
- }
440
- async countByDateRange(name, startDate, endDate) {
441
- let query = this.#knex(name).where('date', '>=', startDate.toISOString());
442
- if (endDate) {
443
- query = query.where('date', '<', endDate.toISOString());
444
- }
445
- const result = await query.count('* as count').first();
446
- return Number(result?.count) || 0;
447
- }
448
- // ========== Batch operations for performance ==========
449
- async saveBatch(metadataList) {
450
- if (metadataList.length === 0)
451
- return [];
452
- // Group by table name for efficient batch inserts
453
- const groupedByTable = new Map();
454
- for (const meta of metadataList) {
455
- const group = groupedByTable.get(meta.name);
456
- if (group) {
457
- group.push(meta);
458
- }
459
- else {
460
- groupedByTable.set(meta.name, [meta]);
461
- }
462
- }
463
- const results = [];
464
- // Process each table in a transaction for consistency
465
- for (const [tableName, metas] of groupedByTable) {
466
- const insertData = metas.map(meta => {
467
- const data = {
468
- name: meta.name,
469
- type: meta.type,
470
- url: meta.url,
471
- date: meta.date.toISOString()
472
- };
473
- // Only include ID if it's explicitly set (for updates)
474
- if (meta.id !== undefined) {
475
- data.id = meta.id;
476
- }
477
- // Add asset-specific fields if present
478
- if ('description' in meta)
479
- data.description = meta.description;
480
- if ('source' in meta)
481
- data.source = meta.source;
482
- if ('owner_id' in meta)
483
- data.owner_id = meta.owner_id;
484
- if ('filename' in meta)
485
- data.filename = meta.filename;
486
- return data;
487
- });
488
- await this.#knex(tableName).insert(insertData);
489
- // Convert to DataRecords
490
- for (const meta of metas) {
491
- results.push(mapToDataRecord(meta, this.#storage));
492
- }
493
- }
494
- return results;
495
- }
496
- async deleteBatch(deleteRequests) {
497
- if (deleteRequests.length === 0)
498
- return;
499
- // Group by table name for efficient batch deletes
500
- const groupedByTable = new Map();
501
- for (const req of deleteRequests) {
502
- const group = groupedByTable.get(req.name);
503
- if (group) {
504
- group.push(req.id);
505
- }
506
- else {
507
- groupedByTable.set(req.name, [req.id]);
508
- }
509
- }
510
- // Process each table
511
- for (const [tableName, ids] of groupedByTable) {
512
- await this.#knex(tableName).whereIn('id', ids).delete();
513
- }
514
- }
515
- async getByIdsBatch(requests) {
516
- if (requests.length === 0)
517
- return [];
518
- const results = [];
519
- // Group by table name for efficient queries
520
- const groupedByTable = new Map();
521
- for (const req of requests) {
522
- const group = groupedByTable.get(req.name);
523
- if (group) {
524
- group.push(req.id);
525
- }
526
- else {
527
- groupedByTable.set(req.name, [req.id]);
528
- }
529
- }
530
- // Query each table
531
- for (const [tableName, ids] of groupedByTable) {
532
- const rows = await this.#knex(tableName).whereIn('id', ids);
533
- for (const row of rows) {
534
- results.push(mapToDataRecord(row, this.#storage));
535
- }
536
- }
537
- return results;
538
- }
539
- // ========== Optimized query for assets manager ==========
540
- async getAllAssetsPaginated(name, offset = 0, limit = 100) {
541
- // Get total count efficiently
542
- const countResult = await this.#knex(name).count('* as count').first();
543
- const total = Number(countResult?.count) || 0;
544
- // Get paginated results
545
- const rows = await this.#knex(name).select('*').orderBy('date', 'desc').offset(offset).limit(limit);
546
- const records = rows.map(row => mapToDataRecord(row, this.#storage));
547
- return { records, total };
548
- }
549
- // ========== Methods for CustomTableManager ==========
550
- async findByConditions(tableName, conditions) {
551
- let query = this.#knex(tableName).select('*');
552
- // Apply each condition
553
- for (const [column, value] of Object.entries(conditions)) {
554
- if (value === null) {
555
- query = query.whereNull(column);
556
- }
557
- else if (value === undefined) {
558
- // Skip undefined values
559
- continue;
560
- }
561
- else {
562
- query = query.where(column, value);
563
- }
564
- }
565
- // Check if table has 'date' column, otherwise use 'created_at'
566
- const hasDateColumn = await this.#knex.schema.hasColumn(tableName, 'date');
567
- const sortColumn = hasDateColumn ? 'date' : 'created_at';
568
- const rows = await query.orderBy(sortColumn, 'desc');
569
- return rows.map(row => mapToDataRecord(row, this.#storage));
570
- }
571
- async updateById(tableName, id, data) {
572
- // Create a clean update object with updated_at timestamp
573
- const updateData = {
574
- ...data,
575
- updated_at: new Date()
576
- };
577
- // Remove system fields that shouldn't be updated
578
- delete updateData.id;
579
- delete updateData.created_at;
580
- delete updateData.date;
581
- // Serialize file_index to JSON string if present (stored as TEXT in DB)
582
- if ('file_index' in updateData && updateData.file_index) {
583
- updateData.file_index =
584
- typeof updateData.file_index === 'string'
585
- ? updateData.file_index
586
- : JSON.stringify(updateData.file_index);
587
- }
588
- const rowsAffected = await this.#knex(tableName).where({ id }).update(updateData);
589
- if (rowsAffected === 0) {
590
- throw new Error(`No record found with ID ${id} in table ${tableName}`);
591
- }
592
- }
593
- async close() {
594
- await this.#knex.destroy();
595
- }
596
- /**
597
- * Find records for custom tables (returns raw database rows, not DataRecords)
598
- * This bypasses mapToDataRecord() which assumes standard table structure
599
- */
600
- async findCustomTableRecords(tableName, conditions = {}) {
601
- let query = this.#knex(tableName).select('*');
602
- // Apply each condition
603
- for (const [column, value] of Object.entries(conditions)) {
604
- if (value === null) {
605
- query = query.whereNull(column);
606
- }
607
- else if (value === undefined) {
608
- // Skip undefined values
609
- continue;
610
- }
611
- else {
612
- query = query.where(column, value);
613
- }
614
- }
615
- // Always sort by created_at for custom tables
616
- const rows = await query.orderBy('created_at', 'desc');
617
- return rows;
618
- }
619
- /**
620
- * Get a single custom table record by ID (returns raw database row, not DataRecord)
621
- */
622
- async getCustomTableRecordById(tableName, id) {
623
- const row = await this.#knex(tableName).where({ id }).first();
624
- return row || null;
625
- }
626
- /**
627
- * Insert a record into a custom table (returns the new record ID)
628
- */
629
- async insertCustomTableRecord(tableName, data) {
630
- const now = new Date();
631
- const insertData = {
632
- ...data,
633
- created_at: now,
634
- updated_at: now
635
- };
636
- const result = await this.#knex(tableName).insert(insertData).returning('id');
637
- const insertedId = result[0];
638
- return typeof insertedId === 'object' ? insertedId.id : insertedId;
639
- }
640
- /**
641
- * Get the underlying Knex instance for advanced operations
642
- */
643
- getKnex() {
644
- return this.#knex;
645
- }
646
- }
647
- //# sourceMappingURL=knex_database_adapter.js.map