admindb 2.4.1 → 2.5.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.
Files changed (135) hide show
  1. package/LICENSE +20 -20
  2. package/dist/app.js +36 -25
  3. package/dist/core/context.d.ts +1 -0
  4. package/dist/core/context.js +2 -0
  5. package/dist/core/errors.d.ts +1 -0
  6. package/dist/core/errors.js +39 -0
  7. package/dist/core/index.d.ts +1 -0
  8. package/dist/core/index.js +20 -0
  9. package/dist/core/result.d.ts +1 -0
  10. package/dist/core/result.js +10 -0
  11. package/dist/core/router.d.ts +1 -0
  12. package/dist/core/router.js +37 -0
  13. package/dist/data/chain.js +1 -1
  14. package/dist/data/engine.js +1 -1
  15. package/dist/db/database.d.ts +1 -1
  16. package/dist/db/database.js +9 -31
  17. package/dist/db/dialects/index.d.ts +1 -0
  18. package/dist/db/dialects/index.js +58 -0
  19. package/dist/db/dialects/postgres/ddl.d.ts +1 -0
  20. package/dist/db/dialects/postgres/ddl.js +107 -0
  21. package/dist/db/dialects/postgres/dialect.d.ts +1 -0
  22. package/dist/db/dialects/postgres/dialect.js +28 -0
  23. package/dist/db/dialects/postgres/introspector.d.ts +1 -0
  24. package/dist/db/dialects/postgres/introspector.js +265 -0
  25. package/dist/db/dialects/postgres/types.d.ts +1 -0
  26. package/dist/db/dialects/postgres/types.js +83 -0
  27. package/dist/db/dialects/sqlite/ddl.d.ts +1 -0
  28. package/dist/db/dialects/sqlite/ddl.js +244 -0
  29. package/dist/db/dialects/sqlite/dialect.d.ts +1 -0
  30. package/dist/db/dialects/sqlite/dialect.js +25 -0
  31. package/dist/db/dialects/sqlite/introspector.d.ts +1 -0
  32. package/dist/db/dialects/sqlite/introspector.js +214 -0
  33. package/dist/db/dialects/sqlite/types.d.ts +1 -0
  34. package/dist/db/dialects/sqlite/types.js +69 -0
  35. package/dist/db/dialects/types.d.ts +1 -0
  36. package/dist/db/dialects/types.js +15 -0
  37. package/dist/db/export.js +1 -1
  38. package/dist/db/index.d.ts +1 -1
  39. package/dist/db/index.js +1 -1
  40. package/dist/db/manager.d.ts +1 -1
  41. package/dist/db/manager.js +101 -21
  42. package/dist/db/postgres.d.ts +1 -1
  43. package/dist/db/postgres.js +20 -284
  44. package/dist/db/types.d.ts +1 -1
  45. package/dist/db/types.js +0 -4
  46. package/dist/index.d.ts +1 -1
  47. package/dist/index.js +4 -1
  48. package/dist/modules/databases/databases.routes.d.ts +1 -0
  49. package/dist/modules/databases/databases.routes.js +150 -0
  50. package/dist/modules/databases/databases.service.d.ts +1 -0
  51. package/dist/modules/databases/databases.service.js +134 -0
  52. package/dist/modules/databases/index.d.ts +1 -0
  53. package/dist/{routes → modules/databases}/index.js +2 -3
  54. package/dist/modules/erd/erd.routes.d.ts +1 -0
  55. package/dist/modules/erd/erd.routes.js +32 -0
  56. package/dist/modules/erd/erd.service.d.ts +1 -0
  57. package/dist/modules/erd/erd.service.js +39 -0
  58. package/dist/modules/erd/index.d.ts +1 -0
  59. package/dist/modules/erd/index.js +18 -0
  60. package/dist/modules/index.d.ts +1 -0
  61. package/dist/modules/index.js +62 -0
  62. package/dist/modules/query/index.d.ts +1 -0
  63. package/dist/modules/query/index.js +18 -0
  64. package/dist/modules/query/query.routes.d.ts +1 -0
  65. package/dist/modules/query/query.routes.js +54 -0
  66. package/dist/modules/query/query.service.d.ts +1 -0
  67. package/dist/modules/query/query.service.js +95 -0
  68. package/dist/modules/rows/blob.helper.d.ts +1 -0
  69. package/dist/modules/rows/blob.helper.js +40 -0
  70. package/dist/modules/rows/display.helper.d.ts +1 -0
  71. package/dist/modules/rows/display.helper.js +73 -0
  72. package/dist/modules/rows/index.d.ts +1 -0
  73. package/dist/modules/rows/index.js +20 -0
  74. package/dist/modules/rows/rows.routes.d.ts +1 -0
  75. package/dist/modules/rows/rows.routes.js +555 -0
  76. package/dist/modules/rows/rows.service.d.ts +1 -0
  77. package/dist/modules/rows/rows.service.js +433 -0
  78. package/dist/modules/schema/index.d.ts +1 -0
  79. package/dist/modules/schema/index.js +18 -0
  80. package/dist/modules/schema/schema.routes.d.ts +1 -0
  81. package/dist/modules/schema/schema.routes.js +171 -0
  82. package/dist/modules/schema/schema.service.d.ts +1 -0
  83. package/dist/modules/schema/schema.service.js +58 -0
  84. package/dist/modules/seed/index.d.ts +1 -0
  85. package/dist/modules/seed/index.js +18 -0
  86. package/dist/modules/seed/seed.routes.d.ts +1 -0
  87. package/dist/modules/seed/seed.routes.js +212 -0
  88. package/dist/modules/seed/seed.service.d.ts +1 -0
  89. package/dist/{routes/api/helpers.js → modules/seed/seed.service.js} +86 -109
  90. package/dist/modules/tables/index.d.ts +1 -0
  91. package/dist/modules/tables/index.js +18 -0
  92. package/dist/modules/tables/tables.routes.d.ts +1 -0
  93. package/dist/modules/tables/tables.routes.js +194 -0
  94. package/dist/modules/tables/tables.service.d.ts +1 -0
  95. package/dist/modules/tables/tables.service.js +218 -0
  96. package/dist/public/js/browse.js +1 -1
  97. package/dist/public/js/query.js +1 -1
  98. package/dist/types/api.d.ts +1 -1
  99. package/dist/utils/common.d.ts +1 -1
  100. package/dist/utils/common.js +1 -3
  101. package/dist/utils/stream.d.ts +1 -0
  102. package/dist/utils/stream.js +131 -0
  103. package/dist/views/pages/error.hbs +10 -1
  104. package/dist/views/pages/home.hbs +1 -1
  105. package/dist/views/pages/query.hbs +1 -1
  106. package/dist/views/pages/seed-select.hbs +1 -252
  107. package/dist/views/pages/seed.hbs +1 -307
  108. package/dist/views/pages/table.hbs +1 -1
  109. package/dist/views/partials/sidebar.hbs +1 -1
  110. package/docs/API.md +4 -17
  111. package/docs/EXAMPLES.md +579 -579
  112. package/docs/SECURITY.md +265 -265
  113. package/package.json +4 -2
  114. package/dist/db/introspection.d.ts +0 -1
  115. package/dist/db/introspection.js +0 -164
  116. package/dist/routes/api/erd.d.ts +0 -1
  117. package/dist/routes/api/erd.js +0 -45
  118. package/dist/routes/api/helpers.d.ts +0 -1
  119. package/dist/routes/api/import-export.d.ts +0 -1
  120. package/dist/routes/api/import-export.js +0 -84
  121. package/dist/routes/api/index.d.ts +0 -1
  122. package/dist/routes/api/index.js +0 -55
  123. package/dist/routes/api/query.d.ts +0 -1
  124. package/dist/routes/api/query.js +0 -77
  125. package/dist/routes/api/rows.d.ts +0 -1
  126. package/dist/routes/api/rows.js +0 -373
  127. package/dist/routes/api/seed.d.ts +0 -1
  128. package/dist/routes/api/seed.js +0 -180
  129. package/dist/routes/api/tables.d.ts +0 -1
  130. package/dist/routes/api/tables.js +0 -230
  131. package/dist/routes/databases.d.ts +0 -1
  132. package/dist/routes/databases.js +0 -212
  133. package/dist/routes/index.d.ts +0 -1
  134. package/dist/routes/pages.d.ts +0 -1
  135. package/dist/routes/pages.js +0 -540
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SchemaService = void 0;
4
+ class SchemaService {
5
+ static async getSchema(ctx, table) {
6
+ const r = await ctx.db.getSchema(table);
7
+ if (!r.success || !r.data)
8
+ throw new Error(r.error ?? 'Failed to load schema.');
9
+ return r.data;
10
+ }
11
+ static async getReferencingTables(ctx, table) {
12
+ const r = await ctx.db.getReferencingTables(table);
13
+ if (!r.success || !r.data)
14
+ throw new Error(r.error ?? 'Failed to get referencing tables.');
15
+ return r.data;
16
+ }
17
+ static async addColumn(ctx, table, col) {
18
+ if (!col?.name)
19
+ throw new Error('Column name is required.');
20
+ const r = await ctx.db.addColumn(table, col);
21
+ if (!r.success)
22
+ throw new Error(r.error ?? 'Failed to add column.');
23
+ }
24
+ static async modifyColumn(ctx, table, colName, col) {
25
+ if (!col)
26
+ throw new Error('Column definition is required.');
27
+ const r = await ctx.db.modifyColumn(table, colName, col);
28
+ if (!r.success)
29
+ throw new Error(r.error ?? 'Failed to modify column.');
30
+ }
31
+ static async renameColumn(ctx, table, colName, newName) {
32
+ const trimmed = String(newName ?? '').trim();
33
+ if (!trimmed)
34
+ throw new Error('New column name is required.');
35
+ const r = await ctx.db.renameColumn(table, colName, trimmed);
36
+ if (!r.success)
37
+ throw new Error(r.error ?? 'Failed to rename column.');
38
+ }
39
+ static async dropColumn(ctx, table, colName) {
40
+ const r = await ctx.db.dropColumn(table, colName);
41
+ if (!r.success)
42
+ throw new Error(r.error ?? 'Failed to drop column.');
43
+ }
44
+ static async createIndex(ctx, table, def) {
45
+ if (!def || !Array.isArray(def.columns) || def.columns.length === 0) {
46
+ throw new Error('At least one column is required for an index.');
47
+ }
48
+ const r = await ctx.db.createIndex(table, def);
49
+ if (!r.success)
50
+ throw new Error(r.error ?? 'Failed to create index.');
51
+ }
52
+ static async dropIndex(ctx, indexName) {
53
+ const r = await ctx.db.dropIndex(indexName);
54
+ if (!r.success)
55
+ throw new Error(r.error ?? 'Failed to drop index.');
56
+ }
57
+ }
58
+ exports.SchemaService = SchemaService;
@@ -0,0 +1 @@
1
+ export * from './seed.service';export * from './seed.routes';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./seed.service"), exports);
18
+ __exportStar(require("./seed.routes"), exports);
@@ -0,0 +1 @@
1
+ import type {Router} from 'express';import{type RouteContext} from '../../core/router';export declare function registerSeedRoutes(router:Router,ctx:RouteContext):void;
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerSeedRoutes = registerSeedRoutes;
4
+ const router_1 = require("../../core/router");
5
+ const tables_service_1 = require("../tables/tables.service");
6
+ const rows_service_1 = require("../rows/rows.service");
7
+ const index_1 = require("../../data/index");
8
+ const seed_service_1 = require("./seed.service");
9
+ function registerSeedRoutes(router, ctx) {
10
+ router.get('/seed/:db', async (req, res, next) => {
11
+ try {
12
+ const db = ctx.getContext(req);
13
+ const dbId = req.params.db;
14
+ res.locals.dbId = dbId;
15
+ const tablesList = await tables_service_1.TableService.listTables(db);
16
+ const allNames = tablesList.map((t) => t.name);
17
+ const { tables: tableNames } = (0, router_1.initPageLocals)(res, db, dbId, allNames);
18
+ if (tableNames.length === 0) {
19
+ return res.render('pages/seed-select', {
20
+ title: 'Seed Data Generator',
21
+ dbId,
22
+ tables: tableNames,
23
+ seedTables: [],
24
+ hasTables: false,
25
+ dialect: db.dialect.name,
26
+ });
27
+ }
28
+ const tableStats = await Promise.all(tableNames.map(async (name) => {
29
+ const [info, rowCount] = await Promise.all([
30
+ tables_service_1.TableService.getTableInfo(db, name),
31
+ rows_service_1.RowsService.getRowCount(db, name),
32
+ ]);
33
+ const cols = info ? info.columns.length : 0;
34
+ const fks = info ? info.foreignKeys.length : 0;
35
+ return {
36
+ name,
37
+ cols,
38
+ fks,
39
+ rowCount,
40
+ };
41
+ }));
42
+ const defaultTable = req.query.table ? String(req.query.table) : tableStats[0]?.name;
43
+ const defaultMode = req.query.mode === 'chain' ? 'chain' : 'single';
44
+ res.render('pages/seed-select', {
45
+ title: 'Seed Data Generator · Choose Table & Mode',
46
+ dbId,
47
+ tables: tableNames,
48
+ seedTables: tableStats,
49
+ hasTables: true,
50
+ defaultTable,
51
+ defaultMode,
52
+ dialect: db.dialect.name,
53
+ });
54
+ }
55
+ catch (err) {
56
+ next(err);
57
+ }
58
+ });
59
+ router.get('/seed/:db/:table', async (req, res, next) => {
60
+ try {
61
+ const db = ctx.getContext(req);
62
+ const dbId = req.params.db;
63
+ const table = req.params.table;
64
+ res.locals.dbId = dbId;
65
+ const tablesList = await tables_service_1.TableService.listTables(db);
66
+ const allNames = tablesList.map((t) => t.name);
67
+ const { tables: allTables } = (0, router_1.initPageLocals)(res, db, dbId, allNames);
68
+ const initialMode = req.query.mode === 'chain' ? 'chain' : 'single';
69
+ const [info, rowCount] = await Promise.all([
70
+ tables_service_1.TableService.getTableInfo(db, table),
71
+ rows_service_1.RowsService.getRowCount(db, table),
72
+ ]);
73
+ if (!info) {
74
+ return (0, router_1.notFound)(res, `Table "${table}" does not exist.`);
75
+ }
76
+ const columns = (0, index_1.buildColumnConfigs)(info);
77
+ res.locals.currentTable = table;
78
+ res.render('pages/seed', {
79
+ title: `Seed data · ${table}`,
80
+ dbId,
81
+ table,
82
+ allTables,
83
+ mode: initialMode,
84
+ isChainMode: initialMode === 'chain',
85
+ rowCount,
86
+ colCount: info.columns.length,
87
+ maxRows: index_1.MAX_SEED_ROWS,
88
+ quickCounts: [10, 50, 100, 500, 1000, index_1.MAX_SEED_ROWS],
89
+ seedConfig: { dbId, table, columns, rowCount, maxRows: index_1.MAX_SEED_ROWS, mode: initialMode },
90
+ });
91
+ }
92
+ catch (err) {
93
+ next(err);
94
+ }
95
+ });
96
+ router.post('/api/seed/:db/validate', (0, router_1.wrap)(async (req, res) => {
97
+ const db = ctx.getContext(req);
98
+ (0, router_1.ok)(res, await seed_service_1.SeedService.validatePlan(db, (0, seed_service_1.parseUnifiedGenerationPlan)(req.body)));
99
+ }));
100
+ router.post('/api/seed/:db/preview', (0, router_1.wrap)(async (req, res) => {
101
+ const db = ctx.getContext(req);
102
+ (0, router_1.ok)(res, await seed_service_1.SeedService.previewPlan(db, (0, seed_service_1.parseUnifiedGenerationPlan)(req.body)));
103
+ }));
104
+ router.post('/api/seed/:db/generate', (0, router_1.wrap)(async (req, res) => {
105
+ const db = ctx.getContext(req);
106
+ (0, router_1.ok)(res, await seed_service_1.SeedService.generatePlan(db, (0, seed_service_1.parseUnifiedGenerationPlan)(req.body)));
107
+ }));
108
+ router.post('/api/seed/:db/execute', (0, router_1.wrap)(async (req, res) => {
109
+ const db = ctx.getContext(req);
110
+ const plan = (0, seed_service_1.parseUnifiedGenerationPlan)(req.body);
111
+ const truncate = Boolean(req.body?.truncate || plan.options?.truncateAll);
112
+ const result = await seed_service_1.SeedService.executePlan(db, plan, truncate);
113
+ (0, router_1.ok)(res, result, 201);
114
+ }));
115
+ const getSeedConfigHandler = (0, router_1.wrap)(async (req, res) => {
116
+ const db = ctx.getContext(req);
117
+ const info = await tables_service_1.TableService.getTableInfo(db, req.params.table);
118
+ if (!info)
119
+ return (0, router_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
120
+ (0, router_1.ok)(res, seed_service_1.SeedService.getSingleTableSeedConfig(info));
121
+ });
122
+ router.get('/api/tables/:db/:table/seed/config', getSeedConfigHandler);
123
+ router.get('/api/tables/:db/:table/seed/plan', getSeedConfigHandler);
124
+ router.post('/api/tables/:db/:table/seed/generate', (0, router_1.wrap)(async (req, res) => {
125
+ const db = ctx.getContext(req);
126
+ const info = await tables_service_1.TableService.getTableInfo(db, req.params.table);
127
+ if (!info)
128
+ return (0, router_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
129
+ const { count, plan } = (0, seed_service_1.parseSeedRequest)(req.body);
130
+ (0, router_1.ok)(res, await seed_service_1.SeedService.generateSingleTable(db, info, count, plan));
131
+ }));
132
+ router.post('/api/tables/:db/:table/seed/preview', (0, router_1.wrap)(async (req, res) => {
133
+ const db = ctx.getContext(req);
134
+ const info = await tables_service_1.TableService.getTableInfo(db, req.params.table);
135
+ if (!info)
136
+ return (0, router_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
137
+ const { count, plan } = (0, seed_service_1.parseSeedRequest)(req.body);
138
+ (0, router_1.ok)(res, await seed_service_1.SeedService.previewSingleTable(db, info, count, plan));
139
+ }));
140
+ router.post('/api/tables/:db/:table/seed', (0, router_1.wrap)(async (req, res) => {
141
+ const db = ctx.getContext(req);
142
+ const info = await tables_service_1.TableService.getTableInfo(db, req.params.table);
143
+ if (!info)
144
+ return (0, router_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
145
+ const { count, plan, truncate } = (0, seed_service_1.parseSeedRequest)(req.body);
146
+ try {
147
+ const result = await seed_service_1.SeedService.seedSingleTable(db, req.params.table, info, count, plan, truncate);
148
+ (0, router_1.ok)(res, result, 201);
149
+ }
150
+ catch (err) {
151
+ (0, router_1.fail)(res, err.message ?? 'Seed failed.', 400);
152
+ }
153
+ }));
154
+ router.get('/api/tables/:db/:table/seed/chain', (0, router_1.wrap)(async (req, res) => {
155
+ const db = ctx.getContext(req);
156
+ const info = await tables_service_1.TableService.getTableInfo(db, req.params.table);
157
+ if (!info)
158
+ return (0, router_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
159
+ const rawScope = String(req.query.scope ?? 'chain');
160
+ const scope = rawScope === 'ancestors' || rawScope === 'descendants' || rawScope === 'all' || rawScope === 'single'
161
+ ? rawScope
162
+ : 'chain';
163
+ (0, router_1.ok)(res, await seed_service_1.SeedService.getChainConfig(db, req.params.table, scope));
164
+ }));
165
+ router.post('/api/tables/:db/:table/seed/chain/preview', (0, router_1.wrap)(async (req, res) => {
166
+ const db = ctx.getContext(req);
167
+ const info = await tables_service_1.TableService.getTableInfo(db, req.params.table);
168
+ if (!info)
169
+ return (0, router_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
170
+ const { scope, counts, plans, modes, seed } = (0, seed_service_1.parseChainSeedRequest)(req.body);
171
+ (0, router_1.ok)(res, await seed_service_1.SeedService.previewChain(db, req.params.table, scope, counts, plans, modes, seed));
172
+ }));
173
+ router.post('/api/tables/:db/:table/seed/chain/generate', (0, router_1.wrap)(async (req, res) => {
174
+ const db = ctx.getContext(req);
175
+ const info = await tables_service_1.TableService.getTableInfo(db, req.params.table);
176
+ if (!info)
177
+ return (0, router_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
178
+ const { scope, counts, plans, modes, seed } = (0, seed_service_1.parseChainSeedRequest)(req.body);
179
+ (0, router_1.ok)(res, await seed_service_1.SeedService.generateChain(db, req.params.table, scope, counts, plans, modes, seed));
180
+ }));
181
+ router.post('/api/tables/:db/:table/seed/chain', (0, router_1.wrap)(async (req, res) => {
182
+ const db = ctx.getContext(req);
183
+ const info = await tables_service_1.TableService.getTableInfo(db, req.params.table);
184
+ if (!info)
185
+ return (0, router_1.fail)(res, `Table "${req.params.table}" does not exist.`, 404);
186
+ const { scope, counts, plans, truncate, modes, seed } = (0, seed_service_1.parseChainSeedRequest)(req.body);
187
+ const result = await seed_service_1.SeedService.executeChain(db, req.params.table, scope, counts, plans, truncate, modes, seed);
188
+ (0, router_1.ok)(res, result, 201);
189
+ }));
190
+ router.get('/api/seed/:db/chain', (0, router_1.wrap)(async (req, res) => {
191
+ const db = ctx.getContext(req);
192
+ const rawRoot = String(req.query.table ?? '');
193
+ const rawScope = String(req.query.scope ?? (rawRoot ? 'chain' : 'all'));
194
+ const scope = rawScope === 'ancestors' || rawScope === 'descendants' || rawScope === 'all' || rawScope === 'single'
195
+ ? rawScope
196
+ : 'all';
197
+ const rootTable = rawRoot || (await db.db.listTables()).data?.[0]?.name || '';
198
+ if (!rootTable)
199
+ return (0, router_1.fail)(res, 'No tables found in database.', 404);
200
+ (0, router_1.ok)(res, await seed_service_1.SeedService.getChainConfig(db, rootTable, scope));
201
+ }));
202
+ router.post('/api/seed/:db/chain', (0, router_1.wrap)(async (req, res) => {
203
+ const db = ctx.getContext(req);
204
+ const { scope, counts, plans, truncate, modes, seed } = (0, seed_service_1.parseChainSeedRequest)(req.body);
205
+ const rawRoot = String(req.body?.rootTable ?? '');
206
+ const rootTable = rawRoot || (await db.db.listTables()).data?.[0]?.name || '';
207
+ if (!rootTable)
208
+ return (0, router_1.fail)(res, 'No tables found in database.', 404);
209
+ const result = await seed_service_1.SeedService.executeChain(db, rootTable, scope, counts, plans, truncate, modes, seed);
210
+ (0, router_1.ok)(res, result, 201);
211
+ }));
212
+ }
@@ -0,0 +1 @@
1
+ import type {TableInfoData} from '../../db/index';import type {DatabaseContext} from '../../core/context';import{type ColumnPlan,type ErChainScope,type GenerationPlan,type TableGenerationMode} from '../../data/index';export declare function parseSeedRequest(body:unknown):{count:number;plan:Record<string,ColumnPlan>;truncate:boolean;seed?:number|null;};export declare function parseChainSeedRequest(body:unknown):{scope:ErChainScope;counts:Record<string,number>;plans:Record<string,Record<string,ColumnPlan>>;truncate:boolean;modes?:Record<string,TableGenerationMode>;seed?:number|null;};export declare function parseUnifiedGenerationPlan(body:unknown):GenerationPlan;export declare class SeedService{static validatePlan(ctx:DatabaseContext,plan:GenerationPlan):Promise<import("../../data/types").ValidationReport>;static previewPlan(ctx:DatabaseContext,plan:GenerationPlan):Promise<import("../../data/types").ErChainResult>;static generatePlan(ctx:DatabaseContext,plan:GenerationPlan):Promise<import("../../data/types").ErChainResult>;static executePlan(ctx:DatabaseContext,plan:GenerationPlan,truncate?:boolean):Promise<{message:string;inserted:Record<string,number>;totalInserted:number;elapsedMs:number;warnings:string[];}>;static getSingleTableSeedConfig(info:TableInfoData):{table:string;columns:import("../../data/types").ColumnGeneratorConfig[];maxRows:number;};static previewSingleTable(ctx:DatabaseContext,info:TableInfoData,count:number,plan:Record<string,ColumnPlan>):Promise<{table:string;previewRows:Record<string,unknown>[];warnings:string[];}>;static generateSingleTable(ctx:DatabaseContext,info:TableInfoData,count:number,plan:Record<string,ColumnPlan>):Promise<{table:string;count:number;sql:string;previewRows:Record<string,unknown>[];warnings:string[];}>;static seedSingleTable(ctx:DatabaseContext,table:string,info:TableInfoData,count:number,plan:Record<string,ColumnPlan>,truncate:boolean):Promise<{message:string;inserted:number;skipped:number;warnings:string[];elapsedMs:number;}>;static getChainConfig(ctx:DatabaseContext,table:string,scope:ErChainScope):Promise<import("../../data/types").ErChainConfig>;static previewChain(ctx:DatabaseContext,table:string,scope:ErChainScope,counts:Record<string,number>,plans:Record<string,Record<string,ColumnPlan>>,modes?:Record<string,TableGenerationMode>,seed?:number|null):Promise<import("../../data/types").ErChainResult>;static generateChain(ctx:DatabaseContext,table:string,scope:ErChainScope,counts:Record<string,number>,plans:Record<string,Record<string,ColumnPlan>>,modes?:Record<string,TableGenerationMode>,seed?:number|null):Promise<import("../../data/types").ErChainResult>;static executeChain(ctx:DatabaseContext,table:string,scope:ErChainScope,counts:Record<string,number>,plans:Record<string,Record<string,ColumnPlan>>,truncate:boolean,modes?:Record<string,TableGenerationMode>,seed?:number|null):Promise<{message:string;inserted:Record<string,number>;totalInserted:number;elapsedMs:number;warnings:string[];}>;}
@@ -1,74 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MAX_BULK_ROWS = exports.wrap = exports.fail = exports.ok = void 0;
4
- exports.requireTable = requireTable;
5
- exports.buildFields = buildFields;
6
- exports.buildUpdateFields = buildUpdateFields;
7
- exports.pkWhere = pkWhere;
3
+ exports.SeedService = void 0;
8
4
  exports.parseSeedRequest = parseSeedRequest;
9
5
  exports.parseChainSeedRequest = parseChainSeedRequest;
10
6
  exports.parseUnifiedGenerationPlan = parseUnifiedGenerationPlan;
11
- exports.resolvePkRows = resolvePkRows;
12
- exports.computeBulkImpact = computeBulkImpact;
13
- const common_1 = require("../../utils/common");
14
- const index_1 = require("../../data/index");
15
7
  const generator_1 = require("../../sql/generator");
16
- const ok = (res, data, status = 200) => res.status(status).json({ success: true, data });
17
- exports.ok = ok;
18
- const fail = (res, error, status = 400, details) => res.status(status).json({ success: false, error, ...(details ? { details } : {}) });
19
- exports.fail = fail;
20
- const wrap = (fn) => async (req, res) => {
21
- try {
22
- await fn(req, res);
23
- }
24
- catch (err) {
25
- (0, exports.fail)(res, (0, common_1.errorMessage)(err), 500);
26
- }
27
- };
28
- exports.wrap = wrap;
29
- async function requireTable(db, table) {
30
- const info = await db.getTableInfo(table);
31
- return info.success && info.data?.columns?.length ? info.data : null;
32
- }
33
- function buildFields(info, values, opts = {}) {
34
- const typeMap = new Map(info.columns.map((c) => [c.name, c.type]));
35
- const pkSet = new Set(info.primaryKey);
36
- const fields = [];
37
- for (const [col, raw] of Object.entries(values ?? {})) {
38
- if (!typeMap.has(col) || (opts.excludePk && pkSet.has(col)))
39
- continue;
40
- const val = (0, common_1.coerceFormValue)(raw, typeMap.get(col));
41
- if (val !== null)
42
- fields.push({ column: col, value: val });
43
- }
44
- return fields;
45
- }
46
- function buildUpdateFields(info, values, nulls) {
47
- const fields = buildFields(info, values, { excludePk: true });
48
- const typeMap = new Map(info.columns.map((c) => [c.name, c.type]));
49
- const pkSet = new Set(info.primaryKey);
50
- for (const col of nulls) {
51
- if (!pkSet.has(col) && typeMap.has(col) && !fields.some((f) => f.column === col)) {
52
- fields.push({ column: col, value: null });
53
- }
54
- }
55
- return fields;
56
- }
57
- function pkWhere(info, encodedId) {
58
- const vals = (0, common_1.decodePk)(encodedId);
59
- if (!info.primaryKey.length) {
60
- if (vals.length === 1 && vals[0] !== '') {
61
- const num = Number(vals[0]);
62
- return [{ column: '_rowid_', value: Number.isFinite(num) ? num : vals[0] }];
63
- }
64
- return null;
65
- }
66
- if (vals.length !== info.primaryKey.length)
67
- return null;
68
- const typeMap = new Map(info.columns.map((c) => [c.name, c.type]));
69
- return info.primaryKey.map((col, i) => ({ column: col, value: (0, common_1.coerceFormValue)(vals[i], typeMap.get(col)) }));
70
- }
71
- exports.MAX_BULK_ROWS = 1000;
8
+ const index_1 = require("../../data/index");
72
9
  function parseSeedRequest(body) {
73
10
  const b = (body ?? {});
74
11
  const count = Math.max(1, Math.min(index_1.MAX_SEED_ROWS, Number.parseInt(String(b.count ?? '10'), 10) || 10));
@@ -144,12 +81,7 @@ function parseUnifiedGenerationPlan(body) {
144
81
  columns[colName] = sanitized;
145
82
  }
146
83
  }
147
- tables[tableName] = {
148
- mode,
149
- rows,
150
- columns,
151
- truncate: Boolean(spec.truncate),
152
- };
84
+ tables[tableName] = { mode, rows, columns, truncate: Boolean(spec.truncate) };
153
85
  }
154
86
  }
155
87
  const rawSeed = b.seed !== undefined ? b.seed : rawPlan.options?.seed;
@@ -170,44 +102,89 @@ function parseUnifiedGenerationPlan(body) {
170
102
  },
171
103
  };
172
104
  }
173
- function resolvePkRows(info, ids) {
174
- const list = Array.isArray(ids) ? ids : ids == null ? [] : [ids];
175
- const out = [];
176
- for (const id of list) {
177
- const where = pkWhere(info, String(id));
178
- if (where)
179
- out.push(where);
180
- }
181
- return out.length ? out : null;
182
- }
183
- async function computeBulkImpact(db, info, wheres) {
184
- const [rowsR, refInfoR] = await Promise.all([
185
- db.getRowsByPks(info.table, wheres),
186
- db.getReferencingTables(info.table),
187
- ]);
188
- const rows = (rowsR.data ?? []);
189
- const referencing = refInfoR.data ?? [];
190
- if (!rows.length || !referencing.length)
191
- return { references: [], total: 0 };
192
- const queries = referencing.flatMap((item) => item.refs.map(async (ref) => {
193
- const targetCol = ref.to || info.primaryKey[0];
194
- if (!targetCol)
195
- return null;
196
- const values = Array.from(new Set(rows.map((r) => r[targetCol]).filter((v) => v != null)));
197
- if (!values.length)
198
- return null;
199
- try {
200
- const placeholders = values.map(() => '?').join(', ');
201
- const sql = `SELECT COUNT(*) AS c FROM ${(0, generator_1.quoteIdentifier)(item.table)} WHERE ${(0, generator_1.quoteIdentifier)(ref.from)} IN (${placeholders})`;
202
- const countR = await db.all(sql, values);
203
- const count = countR.success && countR.data?.[0] ? Number(countR.data[0].c ?? 0) : 0;
204
- return count > 0 ? { table: item.table, from: ref.from, to: targetCol, count } : null;
105
+ class SeedService {
106
+ static async validatePlan(ctx, plan) {
107
+ return (0, index_1.validateGenerationPlan)(ctx.db, plan);
108
+ }
109
+ static async previewPlan(ctx, plan) {
110
+ return index_1.SeedEngine.executePlan(ctx.db, plan, { previewLimit: 50 });
111
+ }
112
+ static async generatePlan(ctx, plan) {
113
+ return index_1.SeedEngine.executePlan(ctx.db, plan);
114
+ }
115
+ static async executePlan(ctx, plan, truncate = false) {
116
+ const gen = await index_1.SeedEngine.executePlan(ctx.db, plan, { truncateAll: truncate });
117
+ const exec = await (0, index_1.executeChainInsert)(ctx.db, gen, truncate);
118
+ return {
119
+ message: `${truncate ? 'Cleared tables and seeded' : 'Seeded'} ${exec.totalInserted} row(s) across ${Object.keys(exec.inserted).length} table(s) in ${exec.elapsedMs}ms.`,
120
+ inserted: exec.inserted,
121
+ totalInserted: exec.totalInserted,
122
+ elapsedMs: exec.elapsedMs,
123
+ warnings: exec.warnings,
124
+ };
125
+ }
126
+ static getSingleTableSeedConfig(info) {
127
+ return { table: info.table, columns: (0, index_1.buildColumnConfigs)(info), maxRows: index_1.MAX_SEED_ROWS };
128
+ }
129
+ static async previewSingleTable(ctx, info, count, plan) {
130
+ const configs = (0, index_1.buildColumnConfigs)(info);
131
+ const gen = await (0, index_1.generateRows)(ctx.db, info, configs, Math.min(50, count), plan);
132
+ return { table: info.table, previewRows: gen.previewRows, warnings: gen.warnings };
133
+ }
134
+ static async generateSingleTable(ctx, info, count, plan) {
135
+ const configs = (0, index_1.buildColumnConfigs)(info);
136
+ const gen = await (0, index_1.generateRows)(ctx.db, info, configs, count, plan);
137
+ return { table: info.table, count: gen.rows.length, sql: (0, index_1.buildSeedInsertSql)(info.table, gen.rows), previewRows: gen.previewRows, warnings: gen.warnings };
138
+ }
139
+ static async seedSingleTable(ctx, table, info, count, plan, truncate) {
140
+ const { db } = ctx;
141
+ const configs = (0, index_1.buildColumnConfigs)(info);
142
+ const t0 = Date.now();
143
+ if (truncate) {
144
+ const clearRes = await db.run(`DELETE FROM ${(0, generator_1.quoteIdentifier)(table)}`);
145
+ if (!clearRes.success) {
146
+ throw new Error(clearRes.error ?? 'Failed to clear table before seeding.');
147
+ }
205
148
  }
206
- catch {
207
- return null;
149
+ const gen = await (0, index_1.generateRows)(db, info, configs, count, plan, truncate);
150
+ const result = await db.insertRows(table, gen.rows);
151
+ if (!result.success)
152
+ throw new Error(result.error ?? 'Seed failed.');
153
+ const elapsedMs = Date.now() - t0;
154
+ return {
155
+ message: `${truncate ? 'Cleared table and inserted' : 'Inserted'} ${result.data?.inserted ?? 0} row(s) in ${elapsedMs}ms.`,
156
+ inserted: result.data?.inserted ?? 0,
157
+ skipped: result.data?.skipped ?? 0,
158
+ warnings: gen.warnings,
159
+ elapsedMs,
160
+ };
161
+ }
162
+ static async getChainConfig(ctx, table, scope) {
163
+ return (0, index_1.getErChainConfig)(ctx.db, table, scope);
164
+ }
165
+ static async previewChain(ctx, table, scope, counts, plans, modes, seed) {
166
+ const config = await (0, index_1.getErChainConfig)(ctx.db, table, scope);
167
+ const previewCounts = {};
168
+ for (const t of config.tables) {
169
+ previewCounts[t.name] = Math.min(50, counts[t.name] ?? t.suggestedCount ?? 10);
208
170
  }
209
- }));
210
- const results = (await Promise.all(queries)).filter((r) => r !== null);
211
- const total = results.reduce((acc, r) => acc + r.count, 0);
212
- return { references: results, total };
171
+ return (0, index_1.generateChainRows)(ctx.db, config, plans, previewCounts, false, modes, seed);
172
+ }
173
+ static async generateChain(ctx, table, scope, counts, plans, modes, seed) {
174
+ const config = await (0, index_1.getErChainConfig)(ctx.db, table, scope);
175
+ return (0, index_1.generateChainRows)(ctx.db, config, plans, counts, false, modes, seed);
176
+ }
177
+ static async executeChain(ctx, table, scope, counts, plans, truncate, modes, seed) {
178
+ const config = await (0, index_1.getErChainConfig)(ctx.db, table, scope);
179
+ const gen = await (0, index_1.generateChainRows)(ctx.db, config, plans, counts, truncate, modes, seed);
180
+ const exec = await (0, index_1.executeChainInsert)(ctx.db, gen, truncate);
181
+ return {
182
+ message: `${truncate ? 'Cleared tables in reverse order and seeded' : 'Seeded'} ${exec.totalInserted} row(s) across ${Object.keys(exec.inserted).length} table(s) in ${exec.elapsedMs}ms.`,
183
+ inserted: exec.inserted,
184
+ totalInserted: exec.totalInserted,
185
+ elapsedMs: exec.elapsedMs,
186
+ warnings: exec.warnings,
187
+ };
188
+ }
213
189
  }
190
+ exports.SeedService = SeedService;
@@ -0,0 +1 @@
1
+ export * from './tables.service';export * from './tables.routes';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./tables.service"), exports);
18
+ __exportStar(require("./tables.routes"), exports);
@@ -0,0 +1 @@
1
+ import type {Router} from 'express';import{type RouteContext} from '../../core/router';export declare function registerTableRoutes(router:Router,ctx:RouteContext):void;