@restforgejs/platform 5.0.3 → 5.0.8
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/build-info.json +2 -2
- package/cli/consumer-deploy.js +1 -1
- package/cli/consumer.js +1 -1
- package/generators/cli/dashboard/create.js +1 -1
- package/generators/cli/endpoint/create.js +1 -1
- package/generators/cli/processor/create.js +1 -1
- package/generators/lib/dbschema-kit/apply-executor.js +15 -1
- package/generators/lib/dbschema-kit/dialect/mysql.js +2 -0
- package/generators/lib/dbschema-kit/dialect/oracle.js +2 -0
- package/generators/lib/dbschema-kit/dialect/postgres.js +3 -0
- package/generators/lib/dbschema-kit/dialect/sqlite.js +2 -0
- package/generators/lib/dbschema-kit/emitters/alter-table.js +7 -0
- package/generators/lib/dbschema-kit/emitters/create-table.js +31 -3
- package/generators/lib/dbschema-kit/statement-modifier.js +12 -2
- package/generators/lib/payload/payload-runner.js +44 -2
- package/generators/lib/payload/schema-diff.js +31 -1
- package/generators/lib/templates/dashboard-catalog.js +1 -1
- package/generators/lib/templates/db-connection-env.js +1 -1
- package/generators/lib/templates/dbschema-catalog.js +1 -1
- package/generators/lib/templates/field-validation-catalog.js +1 -1
- package/generators/lib/templates/mysql-template.js +1 -1
- package/generators/lib/templates/oracle-template.js +1 -1
- package/generators/lib/templates/postgres-template.js +1 -1
- package/generators/lib/templates/query-declarative-catalog.js +1 -1
- package/generators/lib/templates/sqlite-template.js +1 -1
- package/generators/lib/utils/database-introspector.js +417 -6
- package/generators/lib/validators/argument-validator.js +2 -2
- package/integrity-manifest.json +18 -18
- package/package.json +4 -2
- package/scripts/verify-integrity.js +1 -1
- package/server.js +1 -1
- package/src/components/handlers/adjust_handler.js +1 -1
- package/src/components/handlers/audit_handler.js +1 -1
- package/src/components/handlers/delete_handler.js +1 -1
- package/src/components/handlers/export_handler.js +1 -1
- package/src/components/handlers/import_handler.js +1 -1
- package/src/components/handlers/insert_handler.js +1 -1
- package/src/components/handlers/update_handler.js +1 -1
- package/src/components/handlers/upload_handler.js +1 -1
- package/src/components/handlers/workflow_handler.js +1 -1
- package/src/components/integrations/webhook.js +1 -1
- package/src/consumers/baseConsumer.js +1 -1
- package/src/consumers/declarativeMapper.js +1 -1
- package/src/consumers/handlers/apiHandler.js +1 -1
- package/src/consumers/handlers/consoleHandler.js +1 -1
- package/src/consumers/handlers/databaseHandler.js +1 -1
- package/src/consumers/handlers/index.js +1 -1
- package/src/consumers/handlers/kafkaHandler.js +1 -1
- package/src/consumers/index.js +1 -1
- package/src/consumers/messageTransformer.js +1 -1
- package/src/consumers/validator.js +1 -1
- package/src/core/db/dialect/base-dialect.js +1 -1
- package/src/core/db/dialect/index.js +1 -1
- package/src/core/db/dialect/mysql-dialect.js +1 -1
- package/src/core/db/dialect/oracle-dialect.js +1 -1
- package/src/core/db/dialect/postgres-dialect.js +1 -1
- package/src/core/db/dialect/sqlite-dialect.js +1 -1
- package/src/core/db/flatten-helper.js +1 -1
- package/src/core/db/query-builder-error.js +1 -1
- package/src/core/db/query-builder.js +1 -1
- package/src/core/db/relation-helper.js +1 -1
- package/src/core/handlers/delete_handler.js +1 -1
- package/src/core/handlers/insert_handler.js +1 -1
- package/src/core/handlers/update_handler.js +1 -1
- package/src/core/models/base-model.js +1 -1
- package/src/core/utils/cache-manager.js +1 -1
- package/src/core/utils/component-engine.js +1 -1
- package/src/core/utils/context-builder.js +1 -1
- package/src/core/utils/datetime-formatter.js +1 -1
- package/src/core/utils/datetime-parser.js +1 -1
- package/src/core/utils/db.js +1 -1
- package/src/core/utils/logger.js +1 -1
- package/src/core/utils/payload-loader.js +1 -1
- package/src/core/utils/security-checks.js +1 -1
- package/src/middleware/body-options.js +1 -1
- package/src/middleware/cors.js +1 -1
- package/src/middleware/idempotency.js +1 -1
- package/src/middleware/rate-limiter.js +1 -1
- package/src/middleware/request-logger.js +1 -1
- package/src/middleware/security-headers.js +1 -1
- package/src/models/base-model-mysql.js +1 -1
- package/src/models/base-model-oracle.js +1 -1
- package/src/models/base-model-sqlite.js +1 -1
- package/src/models/base-model.js +1 -1
- package/src/pro/caching/redis-client.js +1 -1
- package/src/pro/caching/redis-helper.js +1 -1
- package/src/pro/consumers/baseConsumer.js +1 -1
- package/src/pro/consumers/declarativeMapper.js +1 -1
- package/src/pro/consumers/handlers/apiHandler.js +1 -1
- package/src/pro/consumers/handlers/consoleHandler.js +1 -1
- package/src/pro/consumers/handlers/databaseHandler.js +1 -1
- package/src/pro/consumers/handlers/index.js +1 -1
- package/src/pro/consumers/handlers/kafkaHandler.js +1 -1
- package/src/pro/consumers/index.js +1 -1
- package/src/pro/consumers/messageTransformer.js +1 -1
- package/src/pro/consumers/validator.js +1 -1
- package/src/pro/database/base-model-mysql.js +1 -1
- package/src/pro/database/base-model-oracle.js +1 -1
- package/src/pro/database/base-model-sqlite.js +1 -1
- package/src/pro/database/db-mysql.js +1 -1
- package/src/pro/database/db-oracle.js +1 -1
- package/src/pro/database/db-sqlite.js +1 -1
- package/src/pro/excel/excel-generator.js +1 -1
- package/src/pro/excel/excel-parser.js +1 -1
- package/src/pro/excel/export-service.js +1 -1
- package/src/pro/excel/export_handler.js +1 -1
- package/src/pro/excel/import-service.js +1 -1
- package/src/pro/excel/import-validator.js +1 -1
- package/src/pro/excel/import_handler.js +1 -1
- package/src/pro/excel/upsert-builder.js +1 -1
- package/src/pro/idgen/idgen-routes.js +1 -1
- package/src/pro/integrations/lookup-resolver.js +1 -1
- package/src/pro/integrations/upload-handler-v2.js +1 -1
- package/src/pro/integrations/upload-handler.js +1 -1
- package/src/pro/integrations/webhook.js +1 -1
- package/src/pro/locking/lock-routes.js +1 -1
- package/src/pro/locking/resource-lock-manager.js +1 -1
- package/src/pro/messaging/kafkaConsumerService.js +1 -1
- package/src/pro/messaging/kafkaService.js +1 -1
- package/src/pro/messaging/messagehubService.js +1 -1
- package/src/pro/messaging/rabbitmqService.js +1 -1
- package/src/pro/scheduler/job-manager.js +1 -1
- package/src/pro/scheduler/job-routes.js +1 -1
- package/src/pro/scheduler/job-validator.js +1 -1
- package/src/pro/storage/base-storage-provider.js +1 -1
- package/src/pro/storage/file-metadata-helper.js +1 -1
- package/src/pro/storage/index.js +1 -1
- package/src/pro/storage/local-storage-provider.js +1 -1
- package/src/pro/storage/s3-storage-provider.js +1 -1
- package/src/pro/storage/upload-cleanup-job.js +1 -1
- package/src/pro/storage/upload-cleanup-scheduler.js +1 -1
- package/src/pro/storage/upload-pending-tracker.js +1 -1
- package/src/pro/websocket/broadcast-helper.js +1 -1
- package/src/pro/websocket/index.js +1 -1
- package/src/pro/websocket/livesync-server.js +1 -1
- package/src/pro/websocket/ws-broadcaster.js +1 -1
- package/src/services/export-service.js +1 -1
- package/src/services/import-service.js +1 -1
- package/src/services/kafkaConsumerService.js +1 -1
- package/src/services/kafkaService.js +1 -1
- package/src/services/messagehubService.js +1 -1
- package/src/services/rabbitmqService.js +1 -1
- package/src/utils/cache-invalidation-registry.js +1 -1
- package/src/utils/cache-manager.js +1 -1
- package/src/utils/component-engine.js +1 -1
- package/src/utils/config-extractor.js +1 -1
- package/src/utils/consumerLogger.js +1 -1
- package/src/utils/context-builder.js +1 -1
- package/src/utils/dashboard-helpers.js +1 -1
- package/src/utils/dateHelper.js +1 -1
- package/src/utils/datetime-formatter.js +1 -1
- package/src/utils/datetime-parser.js +1 -1
- package/src/utils/db-bootstrap.js +1 -1
- package/src/utils/db-mysql.js +1 -1
- package/src/utils/db-oracle.js +1 -1
- package/src/utils/db-sqlite.js +1 -1
- package/src/utils/db.js +1 -1
- package/src/utils/demo-generator.js +1 -1
- package/src/utils/excel-generator.js +1 -1
- package/src/utils/excel-parser.js +1 -1
- package/src/utils/file-watcher.js +1 -1
- package/src/utils/id-generator.js +1 -1
- package/src/utils/idempotency-manager.js +1 -1
- package/src/utils/import-validator.js +1 -1
- package/src/utils/license-client.js +1 -1
- package/src/utils/lock-manager.js +1 -1
- package/src/utils/logger.js +1 -1
- package/src/utils/lookup-resolver.js +1 -1
- package/src/utils/payload-loader.js +1 -1
- package/src/utils/processor-response.js +1 -1
- package/src/utils/rabbitmq.js +1 -1
- package/src/utils/redis-client.js +1 -1
- package/src/utils/redis-helper.js +1 -1
- package/src/utils/request-scope.js +1 -1
- package/src/utils/security-checks.js +1 -1
- package/src/utils/service-resolver.js +1 -1
- package/src/utils/shutdown-coordinator.js +1 -1
- package/src/utils/trusted-keys.js +1 -1
- package/src/utils/upload-handler.js +1 -1
- package/src/utils/upsert-builder.js +1 -1
- package/src/utils/workflow-hook-executor.js +1 -1
|
@@ -17,11 +17,12 @@ const path = require('path');
|
|
|
17
17
|
let pg = null;
|
|
18
18
|
let mysql2 = null;
|
|
19
19
|
let oracledb = null;
|
|
20
|
+
let betterSqlite3 = null;
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* Lazy-load database driver berdasarkan DB_TYPE.
|
|
23
24
|
*
|
|
24
|
-
* @param {string} dbType - 'postgresql' | 'mysql' | 'oracle'
|
|
25
|
+
* @param {string} dbType - 'postgresql' | 'mysql' | 'oracle' | 'sqlite'
|
|
25
26
|
* @returns {Object} Driver module
|
|
26
27
|
*/
|
|
27
28
|
function loadDriver(dbType) {
|
|
@@ -47,11 +48,48 @@ function loadDriver(dbType) {
|
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
50
|
return oracledb;
|
|
51
|
+
case 'sqlite':
|
|
52
|
+
if (!betterSqlite3) {
|
|
53
|
+
try { betterSqlite3 = require('better-sqlite3'); } catch (e) {
|
|
54
|
+
throw new Error('SQLite driver (better-sqlite3) not installed. Run: npm install better-sqlite3');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return betterSqlite3;
|
|
50
58
|
default:
|
|
51
|
-
throw new Error(`Unsupported DB_TYPE: ${dbType}. Supported: postgresql, mysql, oracle`);
|
|
59
|
+
throw new Error(`Unsupported DB_TYPE: ${dbType}. Supported: postgresql, mysql, oracle, sqlite`);
|
|
52
60
|
}
|
|
53
61
|
}
|
|
54
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Ekstrak nama kolom yang membawa penanda boolean `<col> IN ('true','false')`
|
|
65
|
+
* dari teks CHECK constraint / DDL. Dipakai introspeksi untuk mengenali kembali
|
|
66
|
+
* kolom VARCHAR sebagai boolean pada dialect non-native (mysql/oracle/sqlite),
|
|
67
|
+
* penanda yang di-emit otomatis oleh dbschema-kit create-table/alter-table.
|
|
68
|
+
*
|
|
69
|
+
* Toleran terhadap variasi quoting identifier ("col" / `col` / [col]),
|
|
70
|
+
* urutan nilai ('true','false' atau 'false','true'), serta charset introducer
|
|
71
|
+
* MySQL (mis. _utf8mb4'true'). Nama kolom dikembalikan lowercase.
|
|
72
|
+
*
|
|
73
|
+
* @param {string} text - Teks CHECK clause atau CREATE TABLE DDL
|
|
74
|
+
* @returns {string[]} Daftar nama kolom boolean (lowercase)
|
|
75
|
+
*/
|
|
76
|
+
function extractBooleanCheckColumns(text) {
|
|
77
|
+
const cols = [];
|
|
78
|
+
if (!text || typeof text !== 'string') return cols;
|
|
79
|
+
// `\\?` mentoleransi backslash-escaped quote yang dikembalikan MySQL pada
|
|
80
|
+
// CHECK_CLAUSE (mis. _utf8mb4\'true\'); SQLite/Oracle memakai 'true' polos.
|
|
81
|
+
const re = /["`\[]?([A-Za-z_][A-Za-z0-9_]*)["`\]]?\s+IN\s*\(\s*(?:_\w+)?\\?'(true|false)\\?'\s*,\s*(?:_\w+)?\\?'(true|false)\\?'\s*\)/gi;
|
|
82
|
+
let m;
|
|
83
|
+
while ((m = re.exec(text)) !== null) {
|
|
84
|
+
const a = m[2].toLowerCase();
|
|
85
|
+
const b = m[3].toLowerCase();
|
|
86
|
+
if ((a === 'true' && b === 'false') || (a === 'false' && b === 'true')) {
|
|
87
|
+
cols.push(m[1].toLowerCase());
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return cols;
|
|
91
|
+
}
|
|
92
|
+
|
|
55
93
|
// PostgreSQL FK action codes -> human-readable
|
|
56
94
|
const PG_FK_ACTION = {
|
|
57
95
|
a: 'NO ACTION',
|
|
@@ -142,12 +180,21 @@ class DatabaseIntrospector {
|
|
|
142
180
|
this.dbType = 'mysql';
|
|
143
181
|
} else if (dbTypeRaw === 'oracle' || dbTypeRaw === 'oracledb') {
|
|
144
182
|
this.dbType = 'oracle';
|
|
183
|
+
} else if (dbTypeRaw === 'sqlite' || dbTypeRaw === 'sqlite3') {
|
|
184
|
+
this.dbType = 'sqlite';
|
|
145
185
|
} else {
|
|
146
186
|
this.dbType = dbTypeRaw;
|
|
147
187
|
}
|
|
148
188
|
|
|
149
|
-
// Validate required keys
|
|
150
|
-
|
|
189
|
+
// Validate required keys.
|
|
190
|
+
//
|
|
191
|
+
// SQLite adalah file-based: tidak ada host/port/user/password. Yang wajib
|
|
192
|
+
// hanya path ke file database, diterima via DB_FILE (konsisten dengan
|
|
193
|
+
// dbschema-kit connection.js) atau DB_NAME sebagai fallback. Server-based
|
|
194
|
+
// dialect (postgresql/mysql/oracle) tetap mewajibkan kredensial koneksi.
|
|
195
|
+
const requiredKeys = this.dbType === 'sqlite'
|
|
196
|
+
? [] // dicek terpisah di bawah karena boleh DB_FILE ATAU DB_NAME
|
|
197
|
+
: ['DB_HOST', 'DB_PORT', 'DB_USER', 'DB_PASSWORD', 'DB_NAME'];
|
|
151
198
|
const missingKeys = requiredKeys.filter(key => !config[key]);
|
|
152
199
|
|
|
153
200
|
if (missingKeys.length > 0) {
|
|
@@ -155,9 +202,19 @@ class DatabaseIntrospector {
|
|
|
155
202
|
return false;
|
|
156
203
|
}
|
|
157
204
|
|
|
205
|
+
if (this.dbType === 'sqlite' && !config.DB_FILE && !config.DB_NAME) {
|
|
206
|
+
console.error('Error: Missing required config key for SQLite: DB_FILE (atau DB_NAME) berisi path file database');
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
|
|
158
210
|
this.config = config;
|
|
159
211
|
if (!this.quiet) {
|
|
160
|
-
|
|
212
|
+
if (this.dbType === 'sqlite') {
|
|
213
|
+
// SQLite file-based: host:port tidak relevan, tampilkan path file saja.
|
|
214
|
+
console.log(`Config loaded: [sqlite] ${config.DB_FILE || config.DB_NAME}`);
|
|
215
|
+
} else {
|
|
216
|
+
console.log(`Config loaded: [${this.dbType}] ${config.DB_NAME}@${config.DB_HOST}:${config.DB_PORT}`);
|
|
217
|
+
}
|
|
161
218
|
}
|
|
162
219
|
return true;
|
|
163
220
|
} catch (error) {
|
|
@@ -227,6 +284,19 @@ class DatabaseIntrospector {
|
|
|
227
284
|
break;
|
|
228
285
|
}
|
|
229
286
|
|
|
287
|
+
case 'sqlite': {
|
|
288
|
+
// better-sqlite3 bersifat synchronous: konstruktor langsung membuka
|
|
289
|
+
// file. Mode readonly + fileMustExist dipakai karena introspeksi
|
|
290
|
+
// bersifat read-only dan men-generate payload dari DB yang sudah ada;
|
|
291
|
+
// tanpa fileMustExist, file yang salah path akan dibuat kosong dan
|
|
292
|
+
// berujung error "table not found" yang membingungkan.
|
|
293
|
+
const Database = driver;
|
|
294
|
+
const filePath = this.config.DB_FILE || this.config.DB_NAME;
|
|
295
|
+
this.pool = new Database(filePath, { readonly: true, fileMustExist: true });
|
|
296
|
+
this.pool.prepare('SELECT 1').get();
|
|
297
|
+
break;
|
|
298
|
+
}
|
|
299
|
+
|
|
230
300
|
default:
|
|
231
301
|
throw new Error(`Unsupported DB_TYPE: ${this.dbType}`);
|
|
232
302
|
}
|
|
@@ -252,6 +322,9 @@ class DatabaseIntrospector {
|
|
|
252
322
|
try {
|
|
253
323
|
if (this.dbType === 'oracle') {
|
|
254
324
|
await this.pool.close(0);
|
|
325
|
+
} else if (this.dbType === 'sqlite') {
|
|
326
|
+
// better-sqlite3 Database.close() synchronous, tidak punya .end()
|
|
327
|
+
this.pool.close();
|
|
255
328
|
} else {
|
|
256
329
|
await this.pool.end();
|
|
257
330
|
}
|
|
@@ -304,6 +377,13 @@ class DatabaseIntrospector {
|
|
|
304
377
|
await conn.close();
|
|
305
378
|
}
|
|
306
379
|
}
|
|
380
|
+
case 'sqlite': {
|
|
381
|
+
// better-sqlite3 synchronous. Placeholder positional '?' kompatibel
|
|
382
|
+
// dengan SQL/PRAGMA table-valued function (mis. pragma_table_info(?)).
|
|
383
|
+
// .all() valid untuk statement yang mengembalikan row (SELECT/PRAGMA).
|
|
384
|
+
const stmt = this.pool.prepare(sql);
|
|
385
|
+
return stmt.all(...(params || []));
|
|
386
|
+
}
|
|
307
387
|
default:
|
|
308
388
|
return [];
|
|
309
389
|
}
|
|
@@ -325,6 +405,9 @@ class DatabaseIntrospector {
|
|
|
325
405
|
case 'postgresql': return { schema: 'public', table: tableName };
|
|
326
406
|
case 'mysql': return { schema: this.config.DB_NAME, table: tableName };
|
|
327
407
|
case 'oracle': return { schema: this.config.DB_USER.toUpperCase(), table: tableName };
|
|
408
|
+
// SQLite tidak mengenal schema (hanya database 'main'); schema diabaikan
|
|
409
|
+
// di seluruh query introspeksi sqlite (memakai sqlite_master / PRAGMA).
|
|
410
|
+
case 'sqlite': return { schema: 'main', table: tableName };
|
|
328
411
|
default: return { schema: 'public', table: tableName };
|
|
329
412
|
}
|
|
330
413
|
}
|
|
@@ -361,7 +444,10 @@ class DatabaseIntrospector {
|
|
|
361
444
|
ORDER BY ordinal_position LIMIT 1`,
|
|
362
445
|
[schema, table]
|
|
363
446
|
);
|
|
364
|
-
|
|
447
|
+
// MySQL 8 information_schema mengembalikan key UPPERCASE (COLUMN_NAME).
|
|
448
|
+
// Tanpa fallback ini PK tidak terdeteksi -> payload jatuh ke tebakan
|
|
449
|
+
// nama PK yang salah (konsisten dengan pola getColumns mysql).
|
|
450
|
+
return rows.length > 0 ? (rows[0].column_name || rows[0].COLUMN_NAME) : null;
|
|
365
451
|
}
|
|
366
452
|
|
|
367
453
|
case 'oracle': {
|
|
@@ -375,6 +461,17 @@ class DatabaseIntrospector {
|
|
|
375
461
|
);
|
|
376
462
|
return rows.length > 0 ? rows[0].column_name.toLowerCase() : null;
|
|
377
463
|
}
|
|
464
|
+
|
|
465
|
+
case 'sqlite': {
|
|
466
|
+
// pragma_table_info.pk = 0 (bukan PK) atau urutan kolom dalam PK
|
|
467
|
+
// composite (1-based). Ambil kolom pertama (pk=1) konsisten dengan
|
|
468
|
+
// dialect lain yang mengembalikan satu kolom PK.
|
|
469
|
+
const rows = await this.query(
|
|
470
|
+
`SELECT name FROM pragma_table_info(?) WHERE pk > 0 ORDER BY pk`,
|
|
471
|
+
[table]
|
|
472
|
+
);
|
|
473
|
+
return rows.length > 0 ? rows[0].name : null;
|
|
474
|
+
}
|
|
378
475
|
}
|
|
379
476
|
return null;
|
|
380
477
|
} catch (error) {
|
|
@@ -422,6 +519,16 @@ class DatabaseIntrospector {
|
|
|
422
519
|
);
|
|
423
520
|
return rows.length > 0;
|
|
424
521
|
}
|
|
522
|
+
|
|
523
|
+
case 'sqlite': {
|
|
524
|
+
const rows = await this.query(
|
|
525
|
+
`SELECT 1 FROM sqlite_master
|
|
526
|
+
WHERE type IN ('table', 'view') AND name = ?
|
|
527
|
+
LIMIT 1`,
|
|
528
|
+
[table]
|
|
529
|
+
);
|
|
530
|
+
return rows.length > 0;
|
|
531
|
+
}
|
|
425
532
|
}
|
|
426
533
|
return false;
|
|
427
534
|
} catch (error) {
|
|
@@ -471,6 +578,14 @@ class DatabaseIntrospector {
|
|
|
471
578
|
);
|
|
472
579
|
return rows.map(r => r.column_name.toLowerCase());
|
|
473
580
|
}
|
|
581
|
+
|
|
582
|
+
case 'sqlite': {
|
|
583
|
+
const rows = await this.query(
|
|
584
|
+
`SELECT name FROM pragma_table_info(?) ORDER BY cid`,
|
|
585
|
+
[table]
|
|
586
|
+
);
|
|
587
|
+
return rows.map(r => r.name);
|
|
588
|
+
}
|
|
474
589
|
}
|
|
475
590
|
return [];
|
|
476
591
|
} catch (error) {
|
|
@@ -528,6 +643,18 @@ class DatabaseIntrospector {
|
|
|
528
643
|
rows.forEach(r => { types[r.column_name.toLowerCase()] = r.data_type.toLowerCase(); });
|
|
529
644
|
break;
|
|
530
645
|
}
|
|
646
|
+
|
|
647
|
+
case 'sqlite': {
|
|
648
|
+
const rows = await this.query(
|
|
649
|
+
`SELECT name, type FROM pragma_table_info(?) ORDER BY cid`,
|
|
650
|
+
[table]
|
|
651
|
+
);
|
|
652
|
+
// Declared type direfleksikan apa adanya (lowercased). generateDateTimeFields
|
|
653
|
+
// mencocokkan terhadap 'date'/'timestamp'/'time'/'datetime'; tipe DDL sqlite
|
|
654
|
+
// RESTForge memakai DATE/TIMESTAMP sehingga match deterministik.
|
|
655
|
+
rows.forEach(r => { types[r.name] = (r.type || '').toLowerCase(); });
|
|
656
|
+
break;
|
|
657
|
+
}
|
|
531
658
|
}
|
|
532
659
|
|
|
533
660
|
return types;
|
|
@@ -616,6 +743,53 @@ class DatabaseIntrospector {
|
|
|
616
743
|
};
|
|
617
744
|
});
|
|
618
745
|
}
|
|
746
|
+
|
|
747
|
+
case 'sqlite': {
|
|
748
|
+
// Alias kolom "notnull" ke `nn`: NOTNULL adalah keyword SQLite sehingga
|
|
749
|
+
// alias unquoted `AS notnull` memicu syntax error.
|
|
750
|
+
const rows = await this.query(
|
|
751
|
+
`SELECT name, type, "notnull" AS nn, dflt_value, pk
|
|
752
|
+
FROM pragma_table_info(?) ORDER BY cid`,
|
|
753
|
+
[table]
|
|
754
|
+
);
|
|
755
|
+
// Normalisasi declared type sqlite -> format PostgreSQL-like agar
|
|
756
|
+
// generateFieldValidation (yang mencocokkan terhadap data_type seperti
|
|
757
|
+
// 'varchar'/'integer'/'decimal'/'date'/'timestamp') berperilaku identik
|
|
758
|
+
// lintas dialect. Tipe DDL sqlite RESTForge: VARCHAR(n), TEXT, INTEGER,
|
|
759
|
+
// BIGINT, DECIMAL(p,s), DATE, TIMESTAMP (boolean & uuid -> VARCHAR).
|
|
760
|
+
const STRING_BASES = ['varchar', 'character varying', 'char', 'character', 'nvarchar', 'nchar', 'text', 'clob'];
|
|
761
|
+
return rows.map(r => {
|
|
762
|
+
const declared = (r.type || '').trim().toLowerCase();
|
|
763
|
+
const paren = declared.match(/^([a-z0-9_ ]+?)\s*\(([^)]*)\)\s*$/);
|
|
764
|
+
const base = paren ? paren[1].trim() : declared;
|
|
765
|
+
const argParts = paren && paren[2].trim() !== ''
|
|
766
|
+
? paren[2].split(',').map(s => parseInt(s.trim(), 10)).filter(n => !Number.isNaN(n))
|
|
767
|
+
: [];
|
|
768
|
+
|
|
769
|
+
let charMaxLen = null;
|
|
770
|
+
let numericPrecision = null;
|
|
771
|
+
let numericScale = null;
|
|
772
|
+
if (argParts.length > 0) {
|
|
773
|
+
if (STRING_BASES.includes(base)) {
|
|
774
|
+
charMaxLen = argParts[0];
|
|
775
|
+
} else {
|
|
776
|
+
numericPrecision = argParts[0];
|
|
777
|
+
numericScale = argParts.length > 1 ? argParts[1] : 0;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
return {
|
|
782
|
+
column_name: r.name,
|
|
783
|
+
data_type: base,
|
|
784
|
+
udt_name: base,
|
|
785
|
+
column_default: r.dflt_value !== undefined && r.dflt_value !== null ? String(r.dflt_value) : null,
|
|
786
|
+
is_nullable: Number(r.nn) === 1 ? 'NO' : 'YES',
|
|
787
|
+
character_maximum_length: charMaxLen,
|
|
788
|
+
numeric_precision: numericPrecision,
|
|
789
|
+
numeric_scale: numericScale
|
|
790
|
+
};
|
|
791
|
+
});
|
|
792
|
+
}
|
|
619
793
|
}
|
|
620
794
|
return [];
|
|
621
795
|
} catch (error) {
|
|
@@ -662,6 +836,14 @@ class DatabaseIntrospector {
|
|
|
662
836
|
);
|
|
663
837
|
return rows.map(r => r.column_name.toLowerCase());
|
|
664
838
|
}
|
|
839
|
+
|
|
840
|
+
case 'sqlite': {
|
|
841
|
+
const rows = await this.query(
|
|
842
|
+
`SELECT name FROM pragma_table_info(?) WHERE "notnull" = 1`,
|
|
843
|
+
[table]
|
|
844
|
+
);
|
|
845
|
+
return rows.map(r => r.name);
|
|
846
|
+
}
|
|
665
847
|
}
|
|
666
848
|
return [];
|
|
667
849
|
} catch (error) {
|
|
@@ -746,6 +928,45 @@ class DatabaseIntrospector {
|
|
|
746
928
|
}
|
|
747
929
|
return result;
|
|
748
930
|
}
|
|
931
|
+
|
|
932
|
+
case 'sqlite': {
|
|
933
|
+
const result = [];
|
|
934
|
+
|
|
935
|
+
// PRIMARY KEY: dari pragma_table_info. Menangani INTEGER PRIMARY KEY
|
|
936
|
+
// (rowid alias) yang TIDAK muncul di pragma_index_list. Composite PK
|
|
937
|
+
// diurutkan berdasarkan kolom pk (1-based).
|
|
938
|
+
const pkRows = await this.query(
|
|
939
|
+
`SELECT name, pk FROM pragma_table_info(?) WHERE pk > 0 ORDER BY pk`,
|
|
940
|
+
[table]
|
|
941
|
+
);
|
|
942
|
+
if (pkRows.length > 0) {
|
|
943
|
+
result.push({
|
|
944
|
+
name: `pk_${table}`,
|
|
945
|
+
type: 'PRIMARY KEY',
|
|
946
|
+
columns: pkRows.map(r => r.name)
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
// UNIQUE: pragma_index_list origin = 'u' (UNIQUE constraint), bukan
|
|
951
|
+
// 'pk' (primary key, sudah ditangani) atau 'c' (CREATE INDEX manual).
|
|
952
|
+
const uniqueIdx = await this.query(
|
|
953
|
+
`SELECT name FROM pragma_index_list(?) WHERE origin = 'u'`,
|
|
954
|
+
[table]
|
|
955
|
+
);
|
|
956
|
+
for (const idx of uniqueIdx) {
|
|
957
|
+
const cols = await this.query(
|
|
958
|
+
`SELECT name FROM pragma_index_info(?) ORDER BY seqno`,
|
|
959
|
+
[idx.name]
|
|
960
|
+
);
|
|
961
|
+
result.push({
|
|
962
|
+
name: idx.name,
|
|
963
|
+
type: 'UNIQUE',
|
|
964
|
+
columns: cols.map(c => c.name)
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
return result;
|
|
969
|
+
}
|
|
749
970
|
}
|
|
750
971
|
return [];
|
|
751
972
|
} catch (error) {
|
|
@@ -754,6 +975,78 @@ class DatabaseIntrospector {
|
|
|
754
975
|
}
|
|
755
976
|
}
|
|
756
977
|
|
|
978
|
+
/**
|
|
979
|
+
* Deteksi kolom boolean pada dialect varchar (mysql/oracle/sqlite) lewat
|
|
980
|
+
* penanda CHECK `<col> IN ('true','false')` yang di-emit dbschema-kit.
|
|
981
|
+
*
|
|
982
|
+
* PostgreSQL memakai tipe BOOLEAN native sehingga sudah terdeteksi di
|
|
983
|
+
* getDetailedColumnInfo (data_type 'boolean'); method ini mengembalikan []
|
|
984
|
+
* untuk postgresql. Degradasi anggun: bila katalog CHECK tidak tersedia
|
|
985
|
+
* (mis. MySQL < 8.0.16 atau Oracle < 12c tanpa search_condition_vc), error
|
|
986
|
+
* di-catch dan dikembalikan [] (kolom tetap diperlakukan string).
|
|
987
|
+
*
|
|
988
|
+
* @param {string} tableName - Nama table
|
|
989
|
+
* @returns {Promise<string[]>} Nama kolom boolean (lowercase)
|
|
990
|
+
*/
|
|
991
|
+
async getBooleanColumns(tableName) {
|
|
992
|
+
if (!this.pool) return [];
|
|
993
|
+
|
|
994
|
+
try {
|
|
995
|
+
const { schema, table } = this.parseTableName(tableName);
|
|
996
|
+
let texts = [];
|
|
997
|
+
|
|
998
|
+
switch (this.dbType) {
|
|
999
|
+
case 'sqlite': {
|
|
1000
|
+
const rows = await this.query(
|
|
1001
|
+
`SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?`,
|
|
1002
|
+
[table]
|
|
1003
|
+
);
|
|
1004
|
+
texts = rows.map(r => r.sql || '');
|
|
1005
|
+
break;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
case 'mysql': {
|
|
1009
|
+
const rows = await this.query(
|
|
1010
|
+
`SELECT cc.CHECK_CLAUSE AS clause
|
|
1011
|
+
FROM information_schema.CHECK_CONSTRAINTS cc
|
|
1012
|
+
JOIN information_schema.TABLE_CONSTRAINTS tc
|
|
1013
|
+
ON cc.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
|
|
1014
|
+
AND cc.CONSTRAINT_SCHEMA = tc.CONSTRAINT_SCHEMA
|
|
1015
|
+
WHERE tc.TABLE_SCHEMA = ? AND tc.TABLE_NAME = ? AND tc.CONSTRAINT_TYPE = 'CHECK'`,
|
|
1016
|
+
[schema, table]
|
|
1017
|
+
);
|
|
1018
|
+
texts = rows.map(r => r.CHECK_CLAUSE || r.clause || '');
|
|
1019
|
+
break;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
case 'oracle': {
|
|
1023
|
+
// search_condition_vc (VARCHAR2) tersedia Oracle 12c+; lebih mudah
|
|
1024
|
+
// di-query daripada kolom LONG search_condition.
|
|
1025
|
+
const rows = await this.query(
|
|
1026
|
+
`SELECT search_condition_vc AS clause FROM user_constraints
|
|
1027
|
+
WHERE table_name = :1 AND constraint_type = 'C'`,
|
|
1028
|
+
[table.toUpperCase()]
|
|
1029
|
+
);
|
|
1030
|
+
texts = rows.map(r => r.clause || r.search_condition_vc || '');
|
|
1031
|
+
break;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
default:
|
|
1035
|
+
// postgresql: boolean native, tidak perlu penanda
|
|
1036
|
+
return [];
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
const cols = new Set();
|
|
1040
|
+
for (const t of texts) {
|
|
1041
|
+
for (const c of extractBooleanCheckColumns(t)) cols.add(c);
|
|
1042
|
+
}
|
|
1043
|
+
return Array.from(cols);
|
|
1044
|
+
} catch (error) {
|
|
1045
|
+
console.warn(` Warning: Could not detect boolean columns: ${error.message}`);
|
|
1046
|
+
return [];
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
|
|
757
1050
|
/**
|
|
758
1051
|
* List all tables (and views) in database.
|
|
759
1052
|
*
|
|
@@ -909,6 +1202,25 @@ class DatabaseIntrospector {
|
|
|
909
1202
|
break;
|
|
910
1203
|
}
|
|
911
1204
|
|
|
1205
|
+
case 'sqlite': {
|
|
1206
|
+
// SQLite tidak punya schema; schemaFilter diabaikan. Tabel internal
|
|
1207
|
+
// (sqlite_*, mis. sqlite_sequence) di-skip kecuali includeSystem.
|
|
1208
|
+
// Legacy mode: BASE TABLE saja; modern mode: table + view.
|
|
1209
|
+
const typeList = modernMode ? "('table', 'view')" : "('table')";
|
|
1210
|
+
let sql = `SELECT name, type FROM sqlite_master WHERE type IN ${typeList}`;
|
|
1211
|
+
if (!includeSystem) {
|
|
1212
|
+
sql += " AND name NOT LIKE 'sqlite_%'";
|
|
1213
|
+
}
|
|
1214
|
+
sql += ' ORDER BY name';
|
|
1215
|
+
const rows = await this.query(sql, []);
|
|
1216
|
+
rawRows = rows.map(r => ({
|
|
1217
|
+
schema: 'main',
|
|
1218
|
+
name: r.name,
|
|
1219
|
+
type: r.type === 'view' ? 'view' : 'table'
|
|
1220
|
+
}));
|
|
1221
|
+
break;
|
|
1222
|
+
}
|
|
1223
|
+
|
|
912
1224
|
default:
|
|
913
1225
|
return [];
|
|
914
1226
|
}
|
|
@@ -1073,6 +1385,39 @@ class DatabaseIntrospector {
|
|
|
1073
1385
|
}
|
|
1074
1386
|
return Object.values(grouped);
|
|
1075
1387
|
}
|
|
1388
|
+
|
|
1389
|
+
case 'sqlite': {
|
|
1390
|
+
// pragma_foreign_key_list: kolom id (group FK composite), seq (urutan
|
|
1391
|
+
// kolom), table (tabel referensi), from/to (kolom lokal/referensi),
|
|
1392
|
+
// on_update/on_delete. SQLite tidak menamai FK constraint, jadi nama
|
|
1393
|
+
// disintesis fk_<table>_<id>. references.schema null (tanpa schema).
|
|
1394
|
+
const rows = await this.query(
|
|
1395
|
+
`SELECT id, seq, "table" AS ref_table, "from" AS col, "to" AS ref_col,
|
|
1396
|
+
on_update, on_delete
|
|
1397
|
+
FROM pragma_foreign_key_list(?) ORDER BY id, seq`,
|
|
1398
|
+
[table]
|
|
1399
|
+
);
|
|
1400
|
+
const grouped = {};
|
|
1401
|
+
for (const r of rows) {
|
|
1402
|
+
const id = r.id;
|
|
1403
|
+
if (!grouped[id]) {
|
|
1404
|
+
grouped[id] = {
|
|
1405
|
+
name: `fk_${table}_${id}`,
|
|
1406
|
+
columns: [],
|
|
1407
|
+
references: {
|
|
1408
|
+
schema: null,
|
|
1409
|
+
table: r.ref_table,
|
|
1410
|
+
columns: []
|
|
1411
|
+
},
|
|
1412
|
+
onDelete: r.on_delete || null,
|
|
1413
|
+
onUpdate: r.on_update || null
|
|
1414
|
+
};
|
|
1415
|
+
}
|
|
1416
|
+
grouped[id].columns.push(r.col);
|
|
1417
|
+
grouped[id].references.columns.push(r.ref_col);
|
|
1418
|
+
}
|
|
1419
|
+
return Object.values(grouped);
|
|
1420
|
+
}
|
|
1076
1421
|
}
|
|
1077
1422
|
return [];
|
|
1078
1423
|
} catch (error) {
|
|
@@ -1178,6 +1523,30 @@ class DatabaseIntrospector {
|
|
|
1178
1523
|
}
|
|
1179
1524
|
return Object.values(grouped);
|
|
1180
1525
|
}
|
|
1526
|
+
|
|
1527
|
+
case 'sqlite': {
|
|
1528
|
+
// pragma_index_list: name, unique (0/1), origin ('pk'|'u'|'c').
|
|
1529
|
+
// INTEGER PRIMARY KEY (rowid) tidak punya index terpisah sehingga
|
|
1530
|
+
// tidak muncul di sini, konsisten dengan realita storage sqlite.
|
|
1531
|
+
const idxRows = await this.query(
|
|
1532
|
+
`SELECT name, "unique" AS is_unique, origin FROM pragma_index_list(?) ORDER BY name`,
|
|
1533
|
+
[table]
|
|
1534
|
+
);
|
|
1535
|
+
const result = [];
|
|
1536
|
+
for (const idx of idxRows) {
|
|
1537
|
+
const cols = await this.query(
|
|
1538
|
+
`SELECT name FROM pragma_index_info(?) ORDER BY seqno`,
|
|
1539
|
+
[idx.name]
|
|
1540
|
+
);
|
|
1541
|
+
result.push({
|
|
1542
|
+
name: idx.name,
|
|
1543
|
+
columns: cols.map(c => c.name),
|
|
1544
|
+
unique: Number(idx.is_unique) === 1,
|
|
1545
|
+
primary: idx.origin === 'pk'
|
|
1546
|
+
});
|
|
1547
|
+
}
|
|
1548
|
+
return result;
|
|
1549
|
+
}
|
|
1181
1550
|
}
|
|
1182
1551
|
return [];
|
|
1183
1552
|
} catch (error) {
|
|
@@ -1249,6 +1618,12 @@ class DatabaseIntrospector {
|
|
|
1249
1618
|
const rows = await this.query(sql, []);
|
|
1250
1619
|
return rows.map(r => String(r.username).toLowerCase());
|
|
1251
1620
|
}
|
|
1621
|
+
|
|
1622
|
+
case 'sqlite': {
|
|
1623
|
+
// SQLite tidak mengenal konsep schema bernama (hanya database 'main'
|
|
1624
|
+
// dan optional 'temp'/attached). Tidak ada user schema untuk diiterasi.
|
|
1625
|
+
return [];
|
|
1626
|
+
}
|
|
1252
1627
|
}
|
|
1253
1628
|
return [];
|
|
1254
1629
|
} catch (error) {
|
|
@@ -1336,6 +1711,25 @@ class DatabaseIntrospector {
|
|
|
1336
1711
|
}
|
|
1337
1712
|
}
|
|
1338
1713
|
|
|
1714
|
+
case 'sqlite': {
|
|
1715
|
+
// prepare() me-resolve metadata kolom tanpa mengeksekusi baris.
|
|
1716
|
+
// stmt.columns() mengembalikan deskriptor kolom untuk statement
|
|
1717
|
+
// yang mengembalikan data (SELECT).
|
|
1718
|
+
try {
|
|
1719
|
+
const stmt = this.pool.prepare(trimmed);
|
|
1720
|
+
const columns = (stmt.columns() || []).map(c => String(c.name).toLowerCase());
|
|
1721
|
+
return { ok: true, columns };
|
|
1722
|
+
} catch (err) {
|
|
1723
|
+
return {
|
|
1724
|
+
ok: false,
|
|
1725
|
+
error: {
|
|
1726
|
+
code: err.code || 'unknown',
|
|
1727
|
+
message: err.message
|
|
1728
|
+
}
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1339
1733
|
default:
|
|
1340
1734
|
throw new Error(`Unsupported DB_TYPE: ${this.dbType}`);
|
|
1341
1735
|
}
|
|
@@ -1398,6 +1792,23 @@ class DatabaseIntrospector {
|
|
|
1398
1792
|
}
|
|
1399
1793
|
}
|
|
1400
1794
|
|
|
1795
|
+
case 'sqlite': {
|
|
1796
|
+
// prepare() melakukan validasi sintaks + resolusi schema tanpa
|
|
1797
|
+
// mengeksekusi statement, ekuivalen dengan peran EXPLAIN di dialect lain.
|
|
1798
|
+
try {
|
|
1799
|
+
this.pool.prepare(sql);
|
|
1800
|
+
return { ok: true };
|
|
1801
|
+
} catch (err) {
|
|
1802
|
+
return {
|
|
1803
|
+
ok: false,
|
|
1804
|
+
error: {
|
|
1805
|
+
code: err.code || 'unknown',
|
|
1806
|
+
message: err.message
|
|
1807
|
+
}
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1401
1812
|
default:
|
|
1402
1813
|
throw new Error(`Unsupported DB_TYPE: ${this.dbType}`);
|
|
1403
1814
|
}
|
|
@@ -330,7 +330,7 @@ class ArgumentValidator {
|
|
|
330
330
|
}
|
|
331
331
|
|
|
332
332
|
const trimmed = databaseType.trim().toLowerCase();
|
|
333
|
-
const validTypes = ['postgres', 'oracle', 'mysql'];
|
|
333
|
+
const validTypes = ['postgres', 'oracle', 'mysql', 'sqlite'];
|
|
334
334
|
|
|
335
335
|
if (!validTypes.includes(trimmed)) {
|
|
336
336
|
throw new Error(`Database type '${databaseType}' is invalid. Valid options: ${validTypes.join(', ')}`);
|
|
@@ -436,7 +436,7 @@ class ArgumentValidator {
|
|
|
436
436
|
--endpoint=<name> Endpoint name to create (OPTIONAL)
|
|
437
437
|
--payload=<file> Payload file name without .json (OPTIONAL)
|
|
438
438
|
|
|
439
|
-
--database=<type> Database type: postgres|oracle|mysql (DEFAULT: postgres)
|
|
439
|
+
--database=<type> Database type: postgres|oracle|mysql|sqlite (DEFAULT: postgres)
|
|
440
440
|
--force=<bool> Overwrite existing files (skip confirmation): true|false (DEFAULT: false)
|
|
441
441
|
--create-examples=<bool> Create example files for testing: true|false (DEFAULT: true)
|
|
442
442
|
--skip-sql-validation=<bool> Skip SQL keywords validation: true|false (DEFAULT: false)
|
package/integrity-manifest.json
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "5.0.
|
|
3
|
-
"generated": "2026-
|
|
2
|
+
"version": "5.0.8",
|
|
3
|
+
"generated": "2026-06-01T03:35:49.528Z",
|
|
4
4
|
"generator": "restforge-build-system",
|
|
5
5
|
"algorithm": "sha256",
|
|
6
6
|
"files": {
|
|
7
|
-
"server.js": "
|
|
8
|
-
"src/utils/license-client.js": "
|
|
9
|
-
"src/utils/trusted-keys.js": "
|
|
10
|
-
"cli/consumer.js": "
|
|
11
|
-
"src/utils/security-checks.js": "
|
|
12
|
-
"scripts/verify-integrity.js": "
|
|
7
|
+
"server.js": "c2dad04d38ab4ab2b287276c84f58b30e26c9c0a5c257f6273ffb820b68c2cf2",
|
|
8
|
+
"src/utils/license-client.js": "ac2ce8875b21009f826d0afddcdae32e5307b123a05fe1b62eec0569843e9472",
|
|
9
|
+
"src/utils/trusted-keys.js": "01a7f3eb841fe8b4062bdc4a10899686d78af7580e10bba23f037021bf9bf905",
|
|
10
|
+
"cli/consumer.js": "a82465404d9da7aca1c0ae0caf820934b9e73d564225e2054d409fa5450fe2db",
|
|
11
|
+
"src/utils/security-checks.js": "e599e1b4c01ef857b5e99bee9d5aec9bd5e326262d04a1272c3a088cdcc9eceb",
|
|
12
|
+
"scripts/verify-integrity.js": "8fd989b49b31c776071b2eeba02831cbf1511d9ee05a906548acf0f6c3909da5",
|
|
13
13
|
"bin/restforge-hwinfo-linux": "d9a0071b0acf58a020e738a29837de356f531a5a84d363a2adc50563e3874b8f",
|
|
14
14
|
"bin/restforge-hwinfo-darwin": "d35b15d4e6d7bbdd8317490335ba9602ce5c218519898fa3eabe18cc9c5bb1b6",
|
|
15
15
|
"bin/restforge-hwinfo.exe": "351fa4d33459d30de804ee78deb5eb977b77b688fe1b7b461cdaa214a82f145e",
|
|
16
|
-
"generators/lib/templates/mysql-template.js": "
|
|
17
|
-
"generators/lib/templates/oracle-template.js": "
|
|
18
|
-
"generators/lib/templates/postgres-template.js": "
|
|
19
|
-
"generators/lib/templates/sqlite-template.js": "
|
|
20
|
-
"generators/lib/templates/dashboard-catalog.js": "
|
|
21
|
-
"generators/lib/templates/dbschema-catalog.js": "
|
|
22
|
-
"generators/lib/templates/field-validation-catalog.js": "
|
|
23
|
-
"generators/lib/templates/query-declarative-catalog.js": "
|
|
24
|
-
"generators/lib/templates/db-connection-env.js": "
|
|
16
|
+
"generators/lib/templates/mysql-template.js": "cef2a73006f4981d2bc008c704e552b61f27cfb0150dc4c1c905d1a7819d7039",
|
|
17
|
+
"generators/lib/templates/oracle-template.js": "57a38d80aced2d735d033bd05a52df89d6238a47d1a0a84c8a4c2250746ff593",
|
|
18
|
+
"generators/lib/templates/postgres-template.js": "0f6bc325e5b41a11e7ce9766e58a020f2cd5b85b88b282778d059d81bf7aa601",
|
|
19
|
+
"generators/lib/templates/sqlite-template.js": "ef3658fb92047abc52c5acf2869ceca4298a3e22fdaf549c7daa806f6ec73aa9",
|
|
20
|
+
"generators/lib/templates/dashboard-catalog.js": "5ad9b475aa7ccd5c097abe4ecf2777a9036714dceb2984e74bf321adb2bdde98",
|
|
21
|
+
"generators/lib/templates/dbschema-catalog.js": "9e45b2219352f321fbb2b5f0a2ea14dc7e689d85ee6ab9c94c66275bda697b81",
|
|
22
|
+
"generators/lib/templates/field-validation-catalog.js": "5d77379d8f069204fdbe2d8700932fc76583cbf2431946413aca72b3dcfe1c93",
|
|
23
|
+
"generators/lib/templates/query-declarative-catalog.js": "d12158465f017aa41ee0eac6699a4a79321fdbb63ea984821e403f0d155e27ba",
|
|
24
|
+
"generators/lib/templates/db-connection-env.js": "3fb59cfdd6badd39543e3725bc163c6672c6f7dd78c69bf126b9b1bf8e5d54c5"
|
|
25
25
|
},
|
|
26
26
|
"stats": {
|
|
27
27
|
"totalFiles": 18,
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"signature": {
|
|
32
32
|
"algorithm": "ed25519",
|
|
33
33
|
"keyId": "rfs-manifest-2026-05",
|
|
34
|
-
"value": "
|
|
34
|
+
"value": "17b3022758981192df691f2b55aeed699e81dd0106ed15eeb445fefdc8392cd4ff7e5e1ab29725dc6592d2156545d0535e115059a5eba5cf8dd9464d710fa80e",
|
|
35
35
|
"scope": "canonical-json(manifest-without-signature)"
|
|
36
36
|
}
|
|
37
37
|
}
|