admindb 2.1.1 → 2.3.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 (119) hide show
  1. package/README.md +368 -335
  2. package/dist/app.js +227 -1
  3. package/dist/auth/config.d.ts +1 -1
  4. package/dist/auth/config.js +109 -1
  5. package/dist/auth/crypto.js +98 -1
  6. package/dist/auth/index.js +21 -1
  7. package/dist/auth/middleware.js +78 -1
  8. package/dist/auth/routes.js +64 -1
  9. package/dist/auth/types.js +7 -1
  10. package/dist/cli/args.d.ts +1 -1
  11. package/dist/cli/args.js +309 -1
  12. package/dist/cli/config.js +118 -1
  13. package/dist/cli/index.js +20 -1
  14. package/dist/cli/runner.js +208 -1
  15. package/dist/cli.js +4 -1
  16. package/dist/data/chain.d.ts +1 -0
  17. package/dist/data/chain.js +130 -0
  18. package/dist/data/datasets.d.ts +1 -1
  19. package/dist/data/datasets.js +171 -1
  20. package/dist/data/detector.d.ts +1 -1
  21. package/dist/data/detector.js +535 -1
  22. package/dist/data/engine.d.ts +1 -1
  23. package/dist/data/engine.js +586 -1
  24. package/dist/data/index.d.ts +1 -1
  25. package/dist/data/index.js +30 -1
  26. package/dist/data/prng.d.ts +1 -0
  27. package/dist/data/prng.js +73 -0
  28. package/dist/data/registry.d.ts +1 -0
  29. package/dist/data/registry.js +805 -0
  30. package/dist/data/schema-graph.d.ts +1 -0
  31. package/dist/data/schema-graph.js +210 -0
  32. package/dist/data/strategies.d.ts +1 -1
  33. package/dist/data/strategies.js +100 -1
  34. package/dist/data/templates.d.ts +1 -0
  35. package/dist/data/templates.js +134 -0
  36. package/dist/data/types.d.ts +1 -1
  37. package/dist/data/types.js +5 -1
  38. package/dist/data/validator.d.ts +1 -0
  39. package/dist/data/validator.js +160 -0
  40. package/dist/db/database.d.ts +1 -1
  41. package/dist/db/database.js +472 -1
  42. package/dist/db/export.d.ts +1 -1
  43. package/dist/db/export.js +67 -1
  44. package/dist/db/filters.d.ts +1 -1
  45. package/dist/db/filters.js +119 -1
  46. package/dist/db/index.js +24 -1
  47. package/dist/db/introspection.js +164 -1
  48. package/dist/db/manager.js +327 -1
  49. package/dist/db/migrations.js +100 -1
  50. package/dist/db/postgres.d.ts +1 -1
  51. package/dist/db/postgres.js +687 -1
  52. package/dist/db/types.d.ts +1 -1
  53. package/dist/db/types.js +6 -1
  54. package/dist/index.d.ts +1 -1
  55. package/dist/index.js +52 -1
  56. package/dist/public/css/app.css +1 -1
  57. package/dist/public/js/api.js +1 -1
  58. package/dist/public/js/browse.js +1 -1
  59. package/dist/public/js/common.js +1 -0
  60. package/dist/public/js/databases.js +1 -1
  61. package/dist/public/js/designer.js +1 -1
  62. package/dist/public/js/forms.js +1 -1
  63. package/dist/public/js/home.js +1 -0
  64. package/dist/public/js/inspector.js +1 -1
  65. package/dist/public/js/query.js +1 -1
  66. package/dist/public/js/schema.js +1 -1
  67. package/dist/public/js/seed.js +1 -1
  68. package/dist/routes/api/erd.d.ts +1 -0
  69. package/dist/routes/api/erd.js +45 -0
  70. package/dist/routes/api/helpers.d.ts +1 -1
  71. package/dist/routes/api/helpers.js +213 -1
  72. package/dist/routes/api/import-export.js +82 -1
  73. package/dist/routes/api/index.js +48 -1
  74. package/dist/routes/api/query.js +74 -1
  75. package/dist/routes/api/rows.js +358 -1
  76. package/dist/routes/api/seed.js +167 -1
  77. package/dist/routes/api/tables.js +216 -1
  78. package/dist/routes/databases.js +210 -1
  79. package/dist/routes/index.js +19 -1
  80. package/dist/routes/pages.js +446 -1
  81. package/dist/serverless.js +149 -1
  82. package/dist/sql/classifier.js +24 -1
  83. package/dist/sql/error-analyzer.d.ts +1 -0
  84. package/dist/sql/error-analyzer.js +550 -0
  85. package/dist/sql/generator.d.ts +1 -1
  86. package/dist/sql/generator.js +245 -1
  87. package/dist/sql/index.js +18 -1
  88. package/dist/types/api.d.ts +1 -1
  89. package/dist/types/api.js +2 -1
  90. package/dist/utils/colors.js +54 -1
  91. package/dist/utils/common.d.ts +1 -1
  92. package/dist/utils/common.js +185 -1
  93. package/dist/utils/csv.d.ts +1 -1
  94. package/dist/utils/csv.js +61 -1
  95. package/dist/utils/datatype.js +167 -1
  96. package/dist/utils/icons.js +81 -1
  97. package/dist/utils/index.js +20 -1
  98. package/dist/utils/logger.js +36 -1
  99. package/dist/views/layouts/main.hbs +1 -74
  100. package/dist/views/pages/databases.hbs +1 -233
  101. package/dist/views/pages/designer.hbs +1 -89
  102. package/dist/views/pages/erd.hbs +1 -0
  103. package/dist/views/pages/error.hbs +1 -10
  104. package/dist/views/pages/form.hbs +1 -59
  105. package/dist/views/pages/home.hbs +1 -124
  106. package/dist/views/pages/info.hbs +1 -0
  107. package/dist/views/pages/login.hbs +1 -98
  108. package/dist/views/pages/query.hbs +1 -87
  109. package/dist/views/pages/schema.hbs +1 -298
  110. package/dist/views/pages/seed-select.hbs +251 -0
  111. package/dist/views/pages/seed.hbs +273 -167
  112. package/dist/views/pages/table.hbs +1 -327
  113. package/dist/views/partials/navbar.hbs +1 -83
  114. package/dist/views/partials/sidebar.hbs +1 -117
  115. package/docs/API.md +149 -144
  116. package/docs/EXAMPLES.md +579 -576
  117. package/docs/SECURITY.md +265 -265
  118. package/package.json +79 -78
  119. package/dist/public/js/icons.js +0 -1
@@ -1 +1,586 @@
1
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.generateRows=async function(db,info,configs,count,plan={}){const fkByColumn=new Map;for(const fk of info.foreignKeys)fkByColumn.set(fk.from,fk);const uniqueCols=new Set;for(const cfg of configs)cfg.unique&&uniqueCols.add(cfg.name);const checkInfo=(0,detector_1.parseChecks)(info.sql??null),warnings=[],prepared=[],fkPools=new Map,fkOmit=new Set;for(const col of info.columns){const cfg=configs.find(c=>c.name===col.name),raw=plan[col.name],p=raw&&raw.strategy?raw:cfg?.defaultPlan??{strategy:"skip"};if(!("list"!==p.strategy||p.values&&p.values.length)){const allowed=(0,detector_1.findInMap)(checkInfo.columnAllowed,col.name);allowed&&allowed.length&&(p.values=[...allowed])}if(!cfg||!cfg.strategies.some(s=>s.id===p.strategy))throw new Error(`Unsupported generator strategy "${p.strategy}" for column "${col.name}".`);if("list"!==p.strategy||(p.values??[]).some(v=>""!==String(v).trim())||warnings.push(`Column "${col.name}": the list is empty, so it will be skipped (DB default applies).`),"skip"!==p.strategy&&"null"!==p.strategy||col.pk||!col.notnull||null!=col.dflt_value||warnings.push(`Column "${col.name}" is NOT NULL without a default — "${p.strategy}" rows may fail to insert.`),"fk"===p.strategy){const pool=await sampleFkValues(db,fkByColumn.get(col.name)??null);fkPools.set(col.name,pool);const refTable=fkByColumn.get(col.name)?.table??"?";pool.length||(col.notnull&&!col.pk?(fkOmit.add(col.name),warnings.push(`Column "${col.name}" is a NOT NULL foreign key to "${refTable}", which has no rows. It is omitted from generated rows — seed "${refTable}" first so the insert can reference real values.`)):warnings.push(`Column "${col.name}": "${refTable}" is empty, so NULL will be inserted.`))}prepared.push({col:col,plan:p})}const rows=[],previewRows=[],uniqueSeen=new Map;for(let i=0;i<count;i++){const fields=[],previewRow={};for(const{col:col,plan:p}of prepared){if("fk"===p.strategy&&fkOmit.has(col.name)){previewRow[col.name]=null;continue}let v=(0,strategies_1.generateOne)(col,p,fkPools.get(col.name),i);if(v!==types_1.SKIP){if(uniqueCols.has(col.name)){const seen=uniqueSeen.get(col.name)??new Set;let attempts=0;for(;seen.has(stringifyKey(v))&&attempts<20;)v=(0,strategies_1.generateOne)(col,p,fkPools.get(col.name),i),attempts+=1;seen.has(stringifyKey(v))&&(warnings.push(`Column "${col.name}" is UNIQUE but the generator kept colliding — appended a suffix to keep rows insertable.`),v=uniqueSuffix(v,seen)),seen.add(stringifyKey(v)),uniqueSeen.set(col.name,seen)}fields.push({column:col.name,value:v}),previewRow[col.name]=v}else previewRow[col.name]="(default)"}applyOrdering(fields,checkInfo.ordering);for(const f of fields)previewRow[f.column]=f.value;rows.push(fields),previewRows.length<50&&previewRows.push(previewRow)}return{rows:rows,previewRows:previewRows,warnings:warnings}},exports.buildSeedInsertSql=function(table,rows){return rows.map(fields=>{const cols=fields.map(f=>(0,generator_1.quoteIdentifier)(f.column)),vals=fields.map(f=>(0,generator_1.sqlValue)(f.value));return`INSERT INTO ${(0,generator_1.quoteIdentifier)(table)} (${cols.join(", ")}) VALUES (${vals.join(", ")});`}).join("\n")};const generator_1=require("../sql/generator"),types_1=require("./types"),detector_1=require("./detector"),strategies_1=require("./strategies");async function sampleFkValues(db,fk){if(!fk)return[];try{const ref=await db.getTableInfo(fk.table);if(!ref.success||!ref.data||!ref.data.columns.length)return[];const refCol=fk.to||ref.data.primaryKey[0]||ref.data.columns[0].name;if(!refCol)return[];return((await db.all(`SELECT DISTINCT ${(0,generator_1.quoteIdentifier)(refCol)} AS v FROM ${(0,generator_1.quoteIdentifier)(fk.table)} WHERE ${(0,generator_1.quoteIdentifier)(refCol)} IS NOT NULL LIMIT 1000`)).data??[]).map(row=>row.v).filter(v=>null!=v)}catch{return[]}}function stringifyKey(v){return v instanceof Uint8Array||Buffer.isBuffer(v)?`buf:${Buffer.from(v).toString("hex")}`:`${typeof v}:${String(v)}`}function uniqueSuffix(base,seen){if("number"==typeof base&&Number.isFinite(base)){let n=Number(base);for(;seen.has(stringifyKey(n));)n+=1;return n}let i=1,cand=`${String(base)}-${i}`;for(;seen.has(stringifyKey(cand));)i+=1,cand=`${String(base)}-${i}`;return cand}function orderedValue(value,base,dir,orEqual){const v=value,b=base;if("string"==typeof v&&"string"==typeof b){const tv=Date.parse(v),tb=Date.parse(b);if(Number.isFinite(tv)&&Number.isFinite(tb)){if("after"===dir){if(orEqual?tv<tb:tv<=tb)return new Date(tb+1e3).toISOString()}else if(orEqual?tv>tb:tv>=tb)return new Date(tb-1e3).toISOString();return v}if(/^\d{4}-\d{2}-\d{2}$/.test(v)&&/^\d{4}-\d{2}-\d{2}$/.test(b)){const dv=Date.parse(`${v}T00:00:00Z`),db=Date.parse(`${b}T00:00:00Z`);if("after"===dir){if(orEqual?dv<db:dv<=db)return new Date(db+864e5).toISOString().slice(0,10)}else if(orEqual?dv>db:dv>=db)return new Date(db-864e5).toISOString().slice(0,10);return v}}return"number"==typeof v&&"number"==typeof b?"after"===dir?orEqual?Math.max(v,b):Math.max(v,b+1):orEqual?Math.min(v,b):Math.min(v,b-1):v}function applyOrdering(fields,ordering){if(!ordering.size)return;const byCol=new Map(fields.map(f=>[f.column,f]));for(const f of fields){const rel=ordering.get(f.column);if(rel){if(rel.after&&null!=f.value){const other=byCol.get(rel.after.column);other&&null!=other.value&&(f.value=orderedValue(f.value,other.value,"after",rel.after.orEqual))}if(rel.before&&null!=f.value){const other=byCol.get(rel.before.column);other&&null!=other.value&&(f.value=orderedValue(f.value,other.value,"before",rel.before.orEqual))}}}}
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SeedEngine = void 0;
4
+ exports.buildSeedInsertSql = buildSeedInsertSql;
5
+ exports.buildChainInsertSql = buildChainInsertSql;
6
+ exports.generateRows = generateRows;
7
+ const generator_1 = require("../sql/generator");
8
+ const detector_1 = require("./detector");
9
+ const prng_1 = require("./prng");
10
+ const registry_1 = require("./registry");
11
+ const schema_graph_1 = require("./schema-graph");
12
+ const templates_1 = require("./templates");
13
+ const types_1 = require("./types");
14
+ const validator_1 = require("./validator");
15
+ const stringifyKey = (v) => v instanceof Uint8Array || Buffer.isBuffer(v) ? `buf:${Buffer.from(v).toString('hex')}` : `${typeof v}:${String(v)}`;
16
+ function orderedValue(value, base, dir, orEqual) {
17
+ const v = value;
18
+ const b = base;
19
+ if (typeof v === 'string' && typeof b === 'string') {
20
+ const tv = Date.parse(v);
21
+ const tb = Date.parse(b);
22
+ if (Number.isFinite(tv) && Number.isFinite(tb)) {
23
+ if (dir === 'after') {
24
+ if (orEqual ? tv < tb : tv <= tb)
25
+ return new Date(tb + 1000).toISOString();
26
+ }
27
+ else if (orEqual ? tv > tb : tv >= tb) {
28
+ return new Date(tb - 1000).toISOString();
29
+ }
30
+ return v;
31
+ }
32
+ if (/^\d{4}-\d{2}-\d{2}$/.test(v) && /^\d{4}-\d{2}-\d{2}$/.test(b)) {
33
+ const dv = Date.parse(`${v}T00:00:00Z`);
34
+ const db = Date.parse(`${b}T00:00:00Z`);
35
+ if (dir === 'after') {
36
+ if (orEqual ? dv < db : dv <= db)
37
+ return new Date(db + 86400_000).toISOString().slice(0, 10);
38
+ }
39
+ else if (orEqual ? dv > db : dv >= db) {
40
+ return new Date(db - 86400_000).toISOString().slice(0, 10);
41
+ }
42
+ return v;
43
+ }
44
+ }
45
+ if (typeof v === 'number' && typeof b === 'number') {
46
+ if (dir === 'after')
47
+ return orEqual ? Math.max(v, b) : Math.max(v, b + 1);
48
+ return orEqual ? Math.min(v, b) : Math.min(v, b - 1);
49
+ }
50
+ return v;
51
+ }
52
+ function fitStringLength(val, len) {
53
+ if (!len || typeof val !== 'string')
54
+ return val;
55
+ let v = val;
56
+ const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
57
+ if (len.exact != null) {
58
+ if (v.length > len.exact)
59
+ v = v.slice(0, len.exact);
60
+ while (v.length < len.exact)
61
+ v += chars[Math.floor(Math.random() * chars.length)];
62
+ return v;
63
+ }
64
+ if (len.max != null && v.length > len.max)
65
+ v = v.slice(0, len.max);
66
+ if (len.min != null && v.length < len.min) {
67
+ while (v.length < len.min)
68
+ v += chars[Math.floor(Math.random() * chars.length)];
69
+ }
70
+ return v;
71
+ }
72
+ function applyOrdering(fields, ordering) {
73
+ if (!ordering.size)
74
+ return;
75
+ const byCol = new Map(fields.map((f) => [f.column, f]));
76
+ for (const f of fields) {
77
+ const rel = ordering.get(f.column);
78
+ if (!rel)
79
+ continue;
80
+ if (rel.after && f.value != null) {
81
+ const other = byCol.get(rel.after.column);
82
+ if (other && other.value != null)
83
+ f.value = orderedValue(f.value, other.value, 'after', rel.after.orEqual);
84
+ }
85
+ if (rel.before && f.value != null) {
86
+ const other = byCol.get(rel.before.column);
87
+ if (other && other.value != null)
88
+ f.value = orderedValue(f.value, other.value, 'before', rel.before.orEqual);
89
+ }
90
+ }
91
+ }
92
+ async function sampleColumnValues(db, table, column, limit = 1000) {
93
+ try {
94
+ const r = await db.all(`SELECT DISTINCT ${(0, generator_1.quoteIdentifier)(column)} AS v FROM ${(0, generator_1.quoteIdentifier)(table)} WHERE ${(0, generator_1.quoteIdentifier)(column)} IS NOT NULL LIMIT ${limit}`);
95
+ return (r.data ?? []).map((row) => row.v).filter((v) => v != null);
96
+ }
97
+ catch {
98
+ return [];
99
+ }
100
+ }
101
+ class SeedEngine {
102
+ static async executePlan(db, plan, optionsOverride) {
103
+ const options = {
104
+ seed: plan.options?.seed ?? null,
105
+ transaction: plan.options?.transaction ?? true,
106
+ rollbackOnError: plan.options?.rollbackOnError ?? true,
107
+ truncateAll: plan.options?.truncateAll ?? false,
108
+ batchSize: plan.options?.batchSize ?? 500,
109
+ previewLimit: plan.options?.previewLimit ?? 50,
110
+ ...optionsOverride,
111
+ };
112
+ const prng = (0, prng_1.createPrng)(options.seed);
113
+ const warnings = [];
114
+ const tableListRes = await db.listTables();
115
+ const tableNames = (tableListRes.data ?? [])
116
+ .map((t) => t.name)
117
+ .filter((name) => !name.startsWith('sqlite_') && name !== '_saved_queries');
118
+ const tableInfos = new Map();
119
+ const allTableData = [];
120
+ for (const name of tableNames) {
121
+ const info = await db.getTableInfo(name);
122
+ if (info.success && info.data) {
123
+ tableInfos.set(name, info.data);
124
+ allTableData.push(info.data);
125
+ }
126
+ }
127
+ const validation = await (0, validator_1.validateGenerationPlan)(db, plan, tableInfos);
128
+ for (const issue of validation.issues) {
129
+ if (issue.type === 'warning')
130
+ warnings.push(`[${issue.table || 'Plan'}] ${issue.message}`);
131
+ }
132
+ const graph = (0, schema_graph_1.buildSchemaGraph)(allTableData);
133
+ if (plan.scope && plan.scope !== 'single' && plan.rootTable) {
134
+ const scopeNodes = (0, schema_graph_1.resolveScopeTables)(graph, plan.rootTable, plan.scope);
135
+ for (const node of scopeNodes) {
136
+ if (!plan.tables[node.name]) {
137
+ const colConfigs = (0, detector_1.buildColumnConfigs)(node.info);
138
+ const columns = {};
139
+ colConfigs.forEach((c) => {
140
+ columns[c.name] = c.defaultPlan;
141
+ });
142
+ plan.tables[node.name] = {
143
+ mode: 'generate',
144
+ rows: node.depth === 0 ? 5 : node.depth === 1 ? 15 : 25,
145
+ columns,
146
+ truncate: options.truncateAll,
147
+ };
148
+ }
149
+ }
150
+ }
151
+ if (plan.scope && plan.scope !== 'single') {
152
+ let expanded = true;
153
+ while (expanded) {
154
+ expanded = false;
155
+ const currentPlanTables = Object.keys(plan.tables);
156
+ for (const tName of currentPlanTables) {
157
+ const spec = plan.tables[tName];
158
+ if (spec && spec.mode === 'skip')
159
+ continue;
160
+ const info = tableInfos.get(tName);
161
+ if (!info)
162
+ continue;
163
+ for (const fk of info.foreignKeys) {
164
+ if (fk.table && fk.table !== tName && !plan.tables[fk.table] && tableInfos.has(fk.table)) {
165
+ const countRes = await db.getRowCount(fk.table);
166
+ const curCount = countRes.success && typeof countRes.data === 'number' ? countRes.data : 0;
167
+ if (curCount === 0) {
168
+ const pInfo = tableInfos.get(fk.table);
169
+ const pColConfigs = (0, detector_1.buildColumnConfigs)(pInfo);
170
+ const pColumns = {};
171
+ pColConfigs.forEach((c) => {
172
+ pColumns[c.name] = c.defaultPlan;
173
+ });
174
+ plan.tables[fk.table] = {
175
+ mode: 'generate',
176
+ rows: 10,
177
+ columns: pColumns,
178
+ truncate: options.truncateAll,
179
+ };
180
+ expanded = true;
181
+ }
182
+ }
183
+ }
184
+ }
185
+ }
186
+ }
187
+ const planTableNames = Object.keys(plan.tables);
188
+ const planNodes = planTableNames.map((t) => graph.nodes.get(t)).filter((n) => n != null);
189
+ const { sorted } = (0, schema_graph_1.sortTopologically)(planNodes.length ? planNodes : Array.from(graph.nodes.values()));
190
+ const executionOrder = sorted.map((s) => s.name).filter((name) => plan.tables[name] !== undefined);
191
+ const relationalPool = new Map();
192
+ const tableResults = {};
193
+ let totalRows = 0;
194
+ for (const tableName of executionOrder) {
195
+ const spec = plan.tables[tableName];
196
+ const info = tableInfos.get(tableName);
197
+ if (!info)
198
+ continue;
199
+ let mode = spec.mode || 'generate';
200
+ if (mode === 'generate_if_empty') {
201
+ try {
202
+ const countRes = await db.getRowCount(tableName);
203
+ const curCount = countRes.success && typeof countRes.data === 'number' ? countRes.data : 0;
204
+ mode = curCount === 0 ? 'generate' : 'use_existing';
205
+ }
206
+ catch {
207
+ mode = 'generate';
208
+ }
209
+ }
210
+ if (mode === 'skip') {
211
+ tableResults[tableName] = { count: 0, rows: [], previewRows: [], warnings: [] };
212
+ continue;
213
+ }
214
+ const pkColName = info.primaryKey[0] || info.columns[0]?.name || 'id';
215
+ if (mode === 'use_existing') {
216
+ for (const col of info.columns) {
217
+ const pool = await sampleColumnValues(db, tableName, col.name, 1000);
218
+ if (pool.length)
219
+ relationalPool.set(`${tableName}.${col.name}`, pool);
220
+ }
221
+ tableResults[tableName] = { count: 0, rows: [], previewRows: [], warnings: [] };
222
+ continue;
223
+ }
224
+ let requestedCount = Math.max(1, Math.min(types_1.MAX_SEED_ROWS, spec.rows ?? 10));
225
+ const checkInfo = (0, detector_1.parseChecks)(info.sql ?? null);
226
+ const fkByColumn = new Map(info.foreignKeys.map((fk) => [fk.from, fk]));
227
+ const uniqueCols = new Set();
228
+ const compositeUniques = [];
229
+ for (const col of info.columns) {
230
+ if (spec.columns[col.name]?.unique)
231
+ uniqueCols.add(col.name);
232
+ }
233
+ for (const ix of info.indexes ?? []) {
234
+ if (ix.unique) {
235
+ if (ix.columns.length === 1 && ix.columns[0])
236
+ uniqueCols.add(ix.columns[0]);
237
+ else if (ix.columns.length > 1)
238
+ compositeUniques.push(ix.columns);
239
+ }
240
+ }
241
+ if (info.primaryKey.length > 1 && !compositeUniques.some((cu) => cu.join(',') === info.primaryKey.join(','))) {
242
+ compositeUniques.push(info.primaryKey);
243
+ }
244
+ if (info.sql) {
245
+ const uqRe = /UNIQUE\s*\(\s*([^)]+)\s*\)/gi;
246
+ let uqM;
247
+ while ((uqM = uqRe.exec(info.sql))) {
248
+ const rawCols = uqM[1].split(',').map((s) => s.trim().replace(/[`"\[\]]/g, ''));
249
+ if (rawCols.length === 1 && rawCols[0])
250
+ uniqueCols.add(rawCols[0]);
251
+ else if (rawCols.length > 1 && !compositeUniques.some((cu) => cu.join(',') === rawCols.join(','))) {
252
+ compositeUniques.push(rawCols);
253
+ }
254
+ }
255
+ }
256
+ const fkPools = new Map();
257
+ const samplePools = new Map();
258
+ const fkOmit = new Set();
259
+ for (const col of info.columns) {
260
+ const p = spec.columns[col.name] || { strategy: 'skip' };
261
+ if (p.strategy === 'sampleExisting') {
262
+ const refT = p.refTable || tableName;
263
+ const refC = p.refColumn || col.name;
264
+ samplePools.set(col.name, await sampleColumnValues(db, refT, refC, 1000));
265
+ }
266
+ if (p.strategy === 'fk' || p.strategy === 'sequentialFk') {
267
+ const fk = fkByColumn.get(col.name);
268
+ const refTable = p.refTable || fk?.table || '';
269
+ let refCol = p.refColumn || fk?.to || (refTable ? tableInfos.get(refTable)?.primaryKey[0] : 'id') || 'id';
270
+ const poolKey = `${refTable}.${refCol}`;
271
+ let pool = relationalPool.get(poolKey) ?? [];
272
+ if (!pool.length) {
273
+ for (const [k, v] of relationalPool.entries()) {
274
+ if (k.toLowerCase() === poolKey.toLowerCase() && v.length) {
275
+ pool = v;
276
+ break;
277
+ }
278
+ }
279
+ }
280
+ if (!pool.length) {
281
+ for (const [k, v] of relationalPool.entries()) {
282
+ const [tName] = k.split('.');
283
+ if (tName && tName.toLowerCase() === refTable.toLowerCase() && v.length) {
284
+ pool = v;
285
+ break;
286
+ }
287
+ }
288
+ }
289
+ if (!pool.length && refTable) {
290
+ pool = await sampleColumnValues(db, refTable, refCol, 1000);
291
+ }
292
+ if (!pool.length) {
293
+ const isSelfFk = fkByColumn.get(col.name)?.table === tableName;
294
+ if (isSelfFk) {
295
+ }
296
+ else if (col.notnull || (col.pk ?? 0) > 0) {
297
+ if (col.dflt_value != null) {
298
+ fkOmit.add(col.name);
299
+ }
300
+ else {
301
+ pool = [1];
302
+ warnings.push(`Table "${tableName}" Column "${col.name}" is a NOT NULL foreign key to "${refTable}". Parent table had no records; fallback ID 1 was used.`);
303
+ }
304
+ }
305
+ else {
306
+ warnings.push(`Table "${tableName}" Column "${col.name}": "${refTable}" has no rows; NULL will be used.`);
307
+ }
308
+ }
309
+ fkPools.set(col.name, pool);
310
+ }
311
+ }
312
+ const uniqueSeen = new Map();
313
+ const compositeSeen = new Map();
314
+ for (const cu of compositeUniques)
315
+ compositeSeen.set(cu.join('::'), new Set());
316
+ if (!spec.truncate && !options.truncateAll) {
317
+ for (const col of uniqueCols) {
318
+ try {
319
+ const existing = await sampleColumnValues(db, tableName, col, 10000);
320
+ uniqueSeen.set(col, new Set(existing.map(stringifyKey)));
321
+ }
322
+ catch { }
323
+ }
324
+ }
325
+ let pkStartOffset = 0;
326
+ for (const col of info.columns) {
327
+ if (col.pk && (col.type || '').toUpperCase().includes('INT')) {
328
+ try {
329
+ const maxRes = await db.all(`SELECT MAX(${(0, generator_1.quoteIdentifier)(col.name)}) AS mx FROM ${(0, generator_1.quoteIdentifier)(tableName)}`);
330
+ const maxVal = (maxRes.data ?? [])[0]?.mx;
331
+ if (typeof maxVal === 'number' && Number.isFinite(maxVal))
332
+ pkStartOffset = maxVal;
333
+ }
334
+ catch { }
335
+ }
336
+ }
337
+ const allColNames = info.columns.map((c) => c.name);
338
+ const { orderedColumns } = (0, templates_1.orderColumnDependencies)(allColNames, spec.columns);
339
+ const colMap = new Map(info.columns.map((c) => [c.name, c]));
340
+ const isUniqueCol = (name) => {
341
+ const norm = name.toLowerCase().replace(/[`"\[\]]/g, '');
342
+ for (const u of uniqueCols) {
343
+ if (u.toLowerCase().replace(/[`"\[\]]/g, '') === norm)
344
+ return true;
345
+ }
346
+ return false;
347
+ };
348
+ for (const colName of orderedColumns) {
349
+ const p = spec.columns[colName];
350
+ const col = colMap.get(colName);
351
+ if (col && p && p.strategy === 'fk' && ((info.primaryKey.length === 1 && col.pk) || isUniqueCol(col.name))) {
352
+ const pool = fkPools.get(col.name);
353
+ if (pool && pool.length > 0 && pool.length < requestedCount)
354
+ requestedCount = pool.length;
355
+ }
356
+ }
357
+ if (compositeUniques.length > 0) {
358
+ for (const cu of compositeUniques) {
359
+ const isAllFk = cu.every((cName) => {
360
+ const p = spec.columns[cName];
361
+ return p && (p.strategy === 'fk' || p.strategy === 'sequentialFk');
362
+ });
363
+ if (isAllFk) {
364
+ const maxCombos = cu.reduce((acc, cName) => acc * Math.max(1, (fkPools.get(cName) ?? []).length), 1);
365
+ if (maxCombos > 0 && maxCombos < requestedCount)
366
+ requestedCount = maxCombos;
367
+ }
368
+ }
369
+ }
370
+ const rows = [];
371
+ const previewRows = [];
372
+ const tableGeneratedPkVals = {};
373
+ for (let i = 0; i < requestedCount; i++) {
374
+ let fields = [];
375
+ let previewRow = {};
376
+ let rowValid = false;
377
+ let attempt = 0;
378
+ while (!rowValid && attempt < 35) {
379
+ fields = [];
380
+ previewRow = {};
381
+ const rowValues = {};
382
+ for (const colName of orderedColumns) {
383
+ const col = colMap.get(colName);
384
+ const p = spec.columns[colName] || { strategy: 'skip' };
385
+ if ((p.strategy === 'fk' || p.strategy === 'sequentialFk') && fkOmit.has(col.name)) {
386
+ previewRow[col.name] = null;
387
+ rowValues[col.name] = null;
388
+ continue;
389
+ }
390
+ const isSelfFk = fkByColumn.get(col.name)?.table === tableName;
391
+ let pool = fkPools.get(col.name);
392
+ if (isSelfFk) {
393
+ const selfIds = tableGeneratedPkVals[fkByColumn.get(col.name)?.to || pkColName] ?? [];
394
+ pool = (i === 0 || selfIds.length === 0 || (prng.chance(35) && !col.notnull)) ? [] : selfIds;
395
+ }
396
+ let v = p.strategy === 'template' && p.template
397
+ ? (0, templates_1.evaluateTemplate)(p.template, rowValues, prng, i + attempt * 50)
398
+ : (0, registry_1.generateColumnValue)(col, p, prng, i + attempt * 50, rowValues, pool, samplePools.get(col.name));
399
+ if (v === types_1.SKIP && col.pk) {
400
+ const isInt = (col.type || '').toUpperCase().includes('INT') || (col.type || '').toUpperCase().includes('SERIAL') || col.type === '' || col.type === 'NUMBER';
401
+ if (isInt)
402
+ v = pkStartOffset + i + 1;
403
+ }
404
+ if ((col.notnull || (col.pk ?? 0) > 0) && col.dflt_value == null && (v == null || v === types_1.SKIP)) {
405
+ if (p.strategy === 'fk' || p.strategy === 'sequentialFk') {
406
+ v = pool && pool.length > 0 ? pool[0] : 1;
407
+ }
408
+ else if (col.pk) {
409
+ v = pkStartOffset + i + 1;
410
+ }
411
+ else {
412
+ const fbPlan = registry_1.GeneratorRegistry.get('words') ? { strategy: 'words', minLen: 1, maxLen: 3 } : { strategy: 'skip' };
413
+ v = (0, registry_1.generateColumnValue)(col, fbPlan, prng, i + attempt * 50, rowValues);
414
+ if (v == null || v === types_1.SKIP) {
415
+ v = (col.type || '').toUpperCase().includes('INT') ? i + 1 : `val_${i + 1}`;
416
+ }
417
+ }
418
+ }
419
+ if (v === types_1.SKIP) {
420
+ previewRow[col.name] = '(default)';
421
+ rowValues[col.name] = null;
422
+ continue;
423
+ }
424
+ if (isUniqueCol(col.name)) {
425
+ const seen = uniqueSeen.get(col.name) ?? new Set();
426
+ if (p.strategy === 'fk' || p.strategy === 'sequentialFk') {
427
+ const unused = (pool ?? []).filter((x) => !seen.has(stringifyKey(x)));
428
+ if (unused.length > 0) {
429
+ v = unused[0];
430
+ }
431
+ else {
432
+ rowValid = false;
433
+ break;
434
+ }
435
+ }
436
+ else {
437
+ let singleAtt = 0;
438
+ while (seen.has(stringifyKey(v)) && singleAtt < 50) {
439
+ v = (0, registry_1.generateColumnValue)(col, p, prng, i + attempt * 50 + singleAtt * 100, rowValues, pool, samplePools.get(col.name));
440
+ singleAtt++;
441
+ }
442
+ if (seen.has(stringifyKey(v))) {
443
+ rowValid = false;
444
+ break;
445
+ }
446
+ }
447
+ }
448
+ v = fitStringLength(v, (0, detector_1.findInMap)(checkInfo.columnLength, col.name));
449
+ fields.push({ column: col.name, value: v });
450
+ previewRow[col.name] = v;
451
+ rowValues[col.name] = v;
452
+ }
453
+ applyOrdering(fields, checkInfo.ordering);
454
+ for (const f of fields)
455
+ previewRow[f.column] = f.value;
456
+ let compositeCollision = false;
457
+ if (compositeUniques.length > 0) {
458
+ const fieldMap = new Map(fields.map((f) => [f.column, f.value]));
459
+ for (const cu of compositeUniques) {
460
+ const key = cu.join('::');
461
+ const compositeVal = cu.map((cName) => stringifyKey(fieldMap.get(cName))).join('|');
462
+ const seenSet = compositeSeen.get(key) ?? new Set();
463
+ if (seenSet.has(compositeVal)) {
464
+ compositeCollision = true;
465
+ break;
466
+ }
467
+ }
468
+ }
469
+ if (!compositeCollision && fields.length > 0) {
470
+ rowValid = true;
471
+ if (compositeUniques.length > 0) {
472
+ const fieldMap = new Map(fields.map((f) => [f.column, f.value]));
473
+ for (const cu of compositeUniques) {
474
+ const key = cu.join('::');
475
+ const compositeVal = cu.map((cName) => stringifyKey(fieldMap.get(cName))).join('|');
476
+ const seenSet = compositeSeen.get(key) ?? new Set();
477
+ seenSet.add(compositeVal);
478
+ compositeSeen.set(key, seenSet);
479
+ }
480
+ }
481
+ }
482
+ attempt++;
483
+ }
484
+ if (!rowValid)
485
+ break;
486
+ for (const f of fields) {
487
+ if (isUniqueCol(f.column)) {
488
+ const seen = uniqueSeen.get(f.column) ?? new Set();
489
+ seen.add(stringifyKey(f.value));
490
+ uniqueSeen.set(f.column, seen);
491
+ }
492
+ const poolKey = `${tableName}.${f.column}`;
493
+ const cur = relationalPool.get(poolKey) ?? [];
494
+ cur.push(f.value);
495
+ relationalPool.set(poolKey, cur);
496
+ const selfCur = tableGeneratedPkVals[f.column] ?? [];
497
+ selfCur.push(f.value);
498
+ tableGeneratedPkVals[f.column] = selfCur;
499
+ }
500
+ rows.push(fields);
501
+ if (previewRows.length < (options.previewLimit ?? 50)) {
502
+ previewRows.push(previewRow);
503
+ }
504
+ }
505
+ tableResults[tableName] = {
506
+ count: rows.length,
507
+ rows,
508
+ previewRows,
509
+ warnings: [],
510
+ };
511
+ totalRows += rows.length;
512
+ }
513
+ const sql = buildChainInsertSql(executionOrder, tableResults);
514
+ return {
515
+ rootTable: plan.rootTable,
516
+ scope: plan.scope,
517
+ executionOrder,
518
+ tableResults,
519
+ totalRows,
520
+ sql,
521
+ warnings,
522
+ validation,
523
+ };
524
+ }
525
+ }
526
+ exports.SeedEngine = SeedEngine;
527
+ function buildSeedInsertSql(table, rows) {
528
+ return rows
529
+ .map((fields) => {
530
+ const cols = fields.map((f) => (0, generator_1.quoteIdentifier)(f.column));
531
+ const vals = fields.map((f) => (0, generator_1.sqlValue)(f.value));
532
+ return `INSERT INTO ${(0, generator_1.quoteIdentifier)(table)} (${cols.join(', ')}) VALUES (${vals.join(', ')});`;
533
+ })
534
+ .join('\n');
535
+ }
536
+ function buildChainInsertSql(executionOrder, tableResults) {
537
+ const sqlParts = [
538
+ '-- AdminDB Relational Seed Script',
539
+ `-- Order of execution: ${executionOrder.join(' -> ')}`,
540
+ 'BEGIN TRANSACTION;\n',
541
+ ];
542
+ for (const table of executionOrder) {
543
+ const res = tableResults[table];
544
+ if (!res || !res.rows.length)
545
+ continue;
546
+ sqlParts.push('-- --------------------------------------------------------');
547
+ sqlParts.push(`-- Table: ${table} (${res.rows.length} rows)`);
548
+ sqlParts.push('-- --------------------------------------------------------');
549
+ for (const fields of res.rows) {
550
+ if (!fields.length)
551
+ continue;
552
+ const cols = fields.map((f) => (0, generator_1.quoteIdentifier)(f.column)).join(', ');
553
+ const vals = fields.map((f) => (0, generator_1.sqlValue)(f.value)).join(', ');
554
+ sqlParts.push(`INSERT INTO ${(0, generator_1.quoteIdentifier)(table)} (${cols}) VALUES (${vals});`);
555
+ }
556
+ sqlParts.push('');
557
+ }
558
+ sqlParts.push('COMMIT;');
559
+ return sqlParts.join('\n');
560
+ }
561
+ async function generateRows(db, info, configs, count, plan = {}, truncate = false) {
562
+ const tableSpec = {
563
+ mode: 'generate',
564
+ rows: count,
565
+ columns: {},
566
+ truncate,
567
+ };
568
+ for (const col of info.columns) {
569
+ const raw = plan[col.name];
570
+ const cfg = configs.find((c) => c.name === col.name);
571
+ tableSpec.columns[col.name] = raw && raw.strategy ? raw : (cfg?.defaultPlan ?? { strategy: 'skip' });
572
+ }
573
+ const generationPlan = {
574
+ rootTable: info.table,
575
+ scope: 'single',
576
+ tables: { [info.table]: tableSpec },
577
+ options: { truncateAll: truncate },
578
+ };
579
+ const res = await SeedEngine.executePlan(db, generationPlan);
580
+ const tableRes = res.tableResults[info.table] || { rows: [], previewRows: [], warnings: [] };
581
+ return {
582
+ rows: tableRes.rows,
583
+ previewRows: tableRes.previewRows,
584
+ warnings: [...res.warnings, ...tableRes.warnings],
585
+ };
586
+ }
@@ -1 +1 @@
1
- export * from './types';export * from './datasets';export * from './strategies';export * from './detector';export * from './engine';
1
+ export * from './types';export * from './datasets';export * from './prng';export{GeneratorRegistry,generateColumnValue,type GeneratorDefinition,type GeneratorFn,type GeneratorContext} from './registry';export * from './templates';export * from './schema-graph';export * from './validator';export * from './strategies';export * from './detector';export * from './engine';export * from './chain';
@@ -1 +1,30 @@
1
- var __createBinding=this&&this.__createBinding||(Object.create?function(o,m,k,k2){void 0===k2&&(k2=k);var desc=Object.getOwnPropertyDescriptor(m,k);desc&&!("get"in desc?!m.__esModule:desc.writable||desc.configurable)||(desc={enumerable:!0,get:function(){return m[k]}}),Object.defineProperty(o,k2,desc)}:function(o,m,k,k2){void 0===k2&&(k2=k),o[k2]=m[k]}),__exportStar=this&&this.__exportStar||function(m,exports){for(var p in m)"default"===p||Object.prototype.hasOwnProperty.call(exports,p)||__createBinding(exports,m,p)};Object.defineProperty(exports,"__esModule",{value:!0}),__exportStar(require("./types"),exports),__exportStar(require("./datasets"),exports),__exportStar(require("./strategies"),exports),__exportStar(require("./detector"),exports),__exportStar(require("./engine"),exports);
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
+ exports.generateColumnValue = exports.GeneratorRegistry = void 0;
18
+ __exportStar(require("./types"), exports);
19
+ __exportStar(require("./datasets"), exports);
20
+ __exportStar(require("./prng"), exports);
21
+ var registry_1 = require("./registry");
22
+ Object.defineProperty(exports, "GeneratorRegistry", { enumerable: true, get: function () { return registry_1.GeneratorRegistry; } });
23
+ Object.defineProperty(exports, "generateColumnValue", { enumerable: true, get: function () { return registry_1.generateColumnValue; } });
24
+ __exportStar(require("./templates"), exports);
25
+ __exportStar(require("./schema-graph"), exports);
26
+ __exportStar(require("./validator"), exports);
27
+ __exportStar(require("./strategies"), exports);
28
+ __exportStar(require("./detector"), exports);
29
+ __exportStar(require("./engine"), exports);
30
+ __exportStar(require("./chain"), exports);
@@ -0,0 +1 @@
1
+ export interface RandomSource{next():number;int(min:number,max:number):number;float(min:number,max:number):number;pick<T>(arr:readonly T[]):T;pickMany<T>(arr:readonly T[],count:number,unique?:boolean):T[];shuffle<T>(arr:T[]):T[];weightedPick<T>(items:readonly T[],weights:readonly number[]):T;chance(pct:number):boolean;}export declare function createPrng(seed?:number|null):RandomSource;