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
@@ -0,0 +1 @@
1
+ import type {TableInfoData} from '../db/index';import type {ColumnGeneratorConfig,ErChainScope,RelationshipConfig} from './types';export interface SchemaNode{name:string;info:TableInfoData;columns:ColumnGeneratorConfig[];parents:{table:string; from :string;to:string;notnull:boolean;}[];children:{table:string; from :string;to:string;notnull:boolean;}[];hasSelfRef:boolean;isJunction:boolean;depth:number;}export interface SchemaGraph{nodes:Map<string,SchemaNode>;relationships:RelationshipConfig[];}export declare function isJunctionTable(info:TableInfoData):boolean;export declare function buildSchemaGraph(tables:TableInfoData[]):SchemaGraph;export declare function resolveScopeTables(graph:SchemaGraph,rootTable:string,scope?:ErChainScope):SchemaNode[];export declare function sortTopologically(nodes:SchemaNode[]):{sorted:SchemaNode[];cycleDetected:boolean;cycleTables:string[];};
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isJunctionTable = isJunctionTable;
4
+ exports.buildSchemaGraph = buildSchemaGraph;
5
+ exports.resolveScopeTables = resolveScopeTables;
6
+ exports.sortTopologically = sortTopologically;
7
+ const detector_1 = require("./detector");
8
+ function isJunctionTable(info) {
9
+ if (info.foreignKeys.length < 2)
10
+ return false;
11
+ const fkColumns = new Set(info.foreignKeys.map((fk) => fk.from.toLowerCase()));
12
+ if (fkColumns.size < 2)
13
+ return false;
14
+ const pk = info.primaryKey.map((k) => k.toLowerCase());
15
+ if (pk.length >= 2 && pk.every((k) => fkColumns.has(k)))
16
+ return true;
17
+ for (const ix of info.indexes ?? []) {
18
+ if (ix.unique && ix.columns.length >= 2) {
19
+ if (ix.columns.map((c) => c.toLowerCase()).every((c) => fkColumns.has(c))) {
20
+ return true;
21
+ }
22
+ }
23
+ }
24
+ if (info.columns.length <= 5 && info.foreignKeys.length >= 2) {
25
+ const nonFkCols = info.columns.filter((c) => !fkColumns.has(c.name.toLowerCase()) &&
26
+ !c.pk &&
27
+ !['created_at', 'updated_at', 'createdat', 'updatedat'].includes(c.name.toLowerCase()));
28
+ if (nonFkCols.length <= 1)
29
+ return true;
30
+ }
31
+ return false;
32
+ }
33
+ function buildSchemaGraph(tables) {
34
+ const nodes = new Map();
35
+ const relationships = [];
36
+ for (const info of tables) {
37
+ const columns = (0, detector_1.buildColumnConfigs)(info);
38
+ const colMap = new Map(info.columns.map((c) => [c.name, c]));
39
+ const parents = [];
40
+ let hasSelfRef = false;
41
+ for (const fk of info.foreignKeys) {
42
+ const toCol = fk.to || (tables.find((t) => t.table === fk.table)?.primaryKey[0] ?? 'id');
43
+ if (fk.table === info.table)
44
+ hasSelfRef = true;
45
+ const c = colMap.get(fk.from);
46
+ const notnull = Boolean(c?.notnull || (c?.pk ?? 0) > 0);
47
+ parents.push({ table: fk.table, from: fk.from, to: toCol, notnull });
48
+ relationships.push({
49
+ parentTable: fk.table,
50
+ parentColumn: toCol,
51
+ childTable: info.table,
52
+ childColumn: fk.from,
53
+ minPerParent: notnull ? 1 : 0,
54
+ maxPerParent: 5,
55
+ distribution: 'uniform',
56
+ nullable: !notnull,
57
+ });
58
+ }
59
+ nodes.set(info.table, {
60
+ name: info.table,
61
+ info,
62
+ columns,
63
+ parents,
64
+ children: [],
65
+ hasSelfRef,
66
+ isJunction: isJunctionTable(info),
67
+ depth: 0,
68
+ });
69
+ }
70
+ for (const node of nodes.values()) {
71
+ for (const p of node.parents) {
72
+ if (p.table !== node.name && nodes.has(p.table)) {
73
+ nodes.get(p.table).children.push({
74
+ table: node.name,
75
+ from: p.from,
76
+ to: p.to,
77
+ notnull: p.notnull,
78
+ });
79
+ }
80
+ }
81
+ }
82
+ return { nodes, relationships };
83
+ }
84
+ function resolveScopeTables(graph, rootTable, scope = 'chain') {
85
+ if (scope === 'all')
86
+ return Array.from(graph.nodes.values());
87
+ const root = graph.nodes.get(rootTable);
88
+ if (!root)
89
+ return [];
90
+ if (scope === 'single')
91
+ return [root];
92
+ const selected = new Set([rootTable]);
93
+ const queue = [rootTable];
94
+ while (queue.length > 0) {
95
+ const curr = queue.shift();
96
+ const node = graph.nodes.get(curr);
97
+ if (!node)
98
+ continue;
99
+ if (scope === 'ancestors' || scope === 'chain') {
100
+ for (const p of node.parents) {
101
+ if (p.table !== curr && graph.nodes.has(p.table) && !selected.has(p.table)) {
102
+ selected.add(p.table);
103
+ queue.push(p.table);
104
+ }
105
+ }
106
+ }
107
+ if (scope === 'descendants' || scope === 'chain') {
108
+ for (const c of node.children) {
109
+ if (c.table !== curr && graph.nodes.has(c.table) && !selected.has(c.table)) {
110
+ selected.add(c.table);
111
+ queue.push(c.table);
112
+ }
113
+ }
114
+ }
115
+ }
116
+ return Array.from(selected).map((name) => graph.nodes.get(name));
117
+ }
118
+ function sortTopologically(nodes) {
119
+ const activeNames = new Set(nodes.map((n) => n.name));
120
+ const nodeMap = new Map(nodes.map((n) => [n.name, n]));
121
+ const totalInDegree = new Map();
122
+ const hardInDegree = new Map();
123
+ const allForwardEdges = new Map();
124
+ const hardForwardEdges = new Map();
125
+ for (const n of nodes) {
126
+ totalInDegree.set(n.name, 0);
127
+ hardInDegree.set(n.name, 0);
128
+ allForwardEdges.set(n.name, new Set());
129
+ hardForwardEdges.set(n.name, new Set());
130
+ }
131
+ for (const n of nodes) {
132
+ for (const p of n.parents) {
133
+ if (p.table !== n.name && activeNames.has(p.table)) {
134
+ if (!allForwardEdges.get(p.table).has(n.name)) {
135
+ allForwardEdges.get(p.table).add(n.name);
136
+ totalInDegree.set(n.name, (totalInDegree.get(n.name) ?? 0) + 1);
137
+ }
138
+ if (p.notnull && !hardForwardEdges.get(p.table).has(n.name)) {
139
+ hardForwardEdges.get(p.table).add(n.name);
140
+ hardInDegree.set(n.name, (hardInDegree.get(n.name) ?? 0) + 1);
141
+ }
142
+ }
143
+ }
144
+ }
145
+ const queue = [];
146
+ const visited = new Set();
147
+ for (const [name, deg] of totalInDegree.entries()) {
148
+ if (deg === 0) {
149
+ queue.push(name);
150
+ visited.add(name);
151
+ }
152
+ }
153
+ const sortedNames = [];
154
+ const depths = new Map();
155
+ for (const name of queue)
156
+ depths.set(name, 0);
157
+ let cycleDetected = false;
158
+ const cycleTables = [];
159
+ while (sortedNames.length < nodes.length) {
160
+ while (queue.length > 0) {
161
+ const curr = queue.shift();
162
+ sortedNames.push(curr);
163
+ const currDepth = depths.get(curr) ?? 0;
164
+ for (const child of allForwardEdges.get(curr) ?? []) {
165
+ const newTotalDeg = (totalInDegree.get(child) ?? 1) - 1;
166
+ totalInDegree.set(child, newTotalDeg);
167
+ if (hardForwardEdges.get(curr)?.has(child)) {
168
+ hardInDegree.set(child, (hardInDegree.get(child) ?? 1) - 1);
169
+ }
170
+ depths.set(child, Math.max(depths.get(child) ?? 0, currDepth + 1));
171
+ if (newTotalDeg === 0 && !visited.has(child)) {
172
+ visited.add(child);
173
+ queue.push(child);
174
+ }
175
+ }
176
+ }
177
+ if (sortedNames.length < nodes.length) {
178
+ cycleDetected = true;
179
+ let bestCandidate = '';
180
+ let lowestHardDeg = Infinity;
181
+ let lowestTotalDeg = Infinity;
182
+ for (const n of nodes) {
183
+ if (!visited.has(n.name)) {
184
+ const hDeg = hardInDegree.get(n.name) ?? 0;
185
+ const tDeg = totalInDegree.get(n.name) ?? 0;
186
+ if (hDeg < lowestHardDeg || (hDeg === lowestHardDeg && tDeg < lowestTotalDeg)) {
187
+ lowestHardDeg = hDeg;
188
+ lowestTotalDeg = tDeg;
189
+ bestCandidate = n.name;
190
+ }
191
+ }
192
+ }
193
+ if (bestCandidate) {
194
+ cycleTables.push(bestCandidate);
195
+ visited.add(bestCandidate);
196
+ depths.set(bestCandidate, (depths.get(bestCandidate) ?? 1) + 1);
197
+ queue.push(bestCandidate);
198
+ }
199
+ else {
200
+ break;
201
+ }
202
+ }
203
+ }
204
+ const sorted = sortedNames.map((name) => {
205
+ const node = { ...nodeMap.get(name) };
206
+ node.depth = depths.get(name) ?? 0;
207
+ return node;
208
+ });
209
+ return { sorted, cycleDetected, cycleTables };
210
+ }
@@ -1 +1 @@
1
- import type {ColumnInfo,ForeignKeyInfo} from '../db/index';import{type ColumnPlan,type StrategyDescriptor} from './types';export declare const randInt:(min:number,max:number)=>number;export declare const randFloat:(min:number,max:number)=>number;export declare const pick:<T>(arr:T[])=>T;export declare const makeSlug:()=>string;export declare const makeEmail:()=>string;export declare const makeUsername:()=>string;export declare const makePhone:()=>string;export declare const makeAddress:()=>string;export declare const makeIp:()=>string;export declare function makeMacAddress():string;export declare const makeCreditCard:()=>string;export declare const makeAvatar:(seedVal?:string)=>string;export declare const makeColor:(format?:string)=>string;export declare function makeUuid():string;export declare function makeWords(minLen:number,maxLen:number):string;export declare function makeSentences(minLen:number,maxLen:number):string;export declare const makeParagraph:()=>string;export declare function formatPattern(pattern:string):string;export declare function randomDate( from ?:string,to?:string,kind?:'date'|'datetime'):string;export declare const randomTime:()=>string;export declare function randomUnixTimestamp(format?:string, from ?:string,to?:string):number;export declare function randomAlnum(minLen:number,maxLen:number):string;export declare function makeJsonObject(keys?:string[],jsonValues?:Record<string,string[]>):string;export declare function isTextishType(type:string):boolean;export declare function strategiesFor(col:ColumnInfo,fk:ForeignKeyInfo|null):StrategyDescriptor[];export declare function fallbackPlanFor(col:ColumnInfo):ColumnPlan;export declare function generateOne(col:ColumnInfo,p:ColumnPlan,fkPool?:unknown[],rowIndex?:number):unknown;export declare function generateSampleValue(col:ColumnInfo,plan:ColumnPlan,fkPool?:unknown[]):unknown;
1
+ import type {ColumnInfo,ForeignKeyInfo} from '../db/index';import{isTextishType as regIsTextishType} from './registry';import{type ColumnPlan,type StrategyDescriptor} from './types';export declare const randInt:(min:number,max:number)=>number;export declare const randFloat:(min:number,max:number)=>number;export declare const pick:<T>(arr:readonly T[])=>T;export declare const makeSlug:()=>string;export declare const makeEmail:(idx?:number)=>string;export declare const makeUsername:(idx?:number)=>string;export declare const makePhone:()=>string;export declare const makeAddress:()=>string;export declare const makeIp:()=>string;export declare const makeMacAddress:()=>string;export declare const makeCreditCard:()=>string;export declare const makeAvatar:(seedVal?:string)=>string;export declare const makeColor:(format?:string)=>string;export declare const makeUuid:()=>string;export declare const makeWords:(minLen:number,maxLen:number)=>string;export declare const makeSentences:(minLen:number,maxLen:number)=>string;export declare const makeParagraph:()=>string;export declare const formatPattern:(pattern:string)=>string;export declare const randomDate:( from ?:string,to?:string,kind?:"date"|"datetime")=>string;export declare const randomTime:()=>string;export declare const randomUnixTimestamp:(format?:string, from ?:string,to?:string)=>number;export declare const randomAlnum:(minLen:number,maxLen:number)=>string;export declare const makeJsonObject:(keys?:string[],jsonValues?:Record<string,string[]>)=>string;export declare const isTextishType: typeof regIsTextishType;export declare function strategiesFor(col:ColumnInfo,fk:ForeignKeyInfo|null):StrategyDescriptor[];export declare function fallbackPlanFor(col:ColumnInfo):ColumnPlan;export declare function generateOne(col:ColumnInfo,plan:ColumnPlan,fkPool?:unknown[],rowIndex?:number):unknown;export declare function generateSampleValue(col:ColumnInfo,plan:ColumnPlan,fkPool?:unknown[]):unknown;
@@ -1 +1,100 @@
1
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.randomTime=exports.makeParagraph=exports.makeColor=exports.makeAvatar=exports.makeCreditCard=exports.makeIp=exports.makeAddress=exports.makePhone=exports.makeUsername=exports.makeEmail=exports.makeSlug=exports.pick=exports.randFloat=exports.randInt=void 0,exports.makeMacAddress=makeMacAddress,exports.makeUuid=makeUuid,exports.makeWords=makeWords,exports.makeSentences=makeSentences,exports.formatPattern=formatPattern,exports.randomDate=randomDate,exports.randomUnixTimestamp=randomUnixTimestamp,exports.randomAlnum=randomAlnum,exports.makeJsonObject=makeJsonObject,exports.isTextishType=isTextishType,exports.strategiesFor=function(col,fk){const type=(col.type||"").toUpperCase(),isText=isTextishType(type),isNum=type.startsWith("INTEGER")||"REAL"===type||type.includes("DECIMAL")||type.includes("NUMERIC")||isText,isBlob="BLOB"===type||isText,out=[];fk&&out.push({id:"fk",label:`Foreign Key: Sample from ${fk.table}${fk.to?`.${fk.to}`:""}`,category:"relational"});for(const[id,label,category,target]of STRATEGY_METADATA)("all"===target||"text"===target&&isText||"numeric"===target&&isNum||"blob"===target&&isBlob)&&out.push({id:id,label:label,category:category});return out},exports.fallbackPlanFor=function(col){const type=(col.type||"").toUpperCase();return"BOOLEAN"===type||"BOOL"===type?{strategy:"bool"}:"UUID"===type?{strategy:"uuid"}:"JSON"===type||"JSONB"===type?{strategy:"json"}:type.startsWith("INT")||"BIGINT"===type||"SMALLINT"===type||"SERIAL"===type||"BIGSERIAL"===type?{strategy:"int",min:1,max:1e3}:"REAL"===type||type.includes("DECIMAL")||"NUMERIC"===type||type.includes("FLOAT")||type.includes("DOUBLE")?{strategy:"decimal",min:1,max:1e3,precision:2}:"DATE"===type?{strategy:"date",from:"2020-01-01",to:"2026-12-31"}:type.includes("TIMESTAMP")||"DATETIME"===type?{strategy:"datetime",from:"2024-01-01T00:00:00",to:"2026-12-31T23:59:59"}:"TIME"===type?{strategy:"time"}:"BLOB"===type||"BYTEA"===type?{strategy:"bytes",minLen:8,maxLen:32}:"INET"===type?{strategy:"ip"}:{strategy:"words",minLen:1,maxLen:4}},exports.generateOne=generateOne,exports.generateSampleValue=function(col,plan,fkPool){const v=generateOne(col,plan,fkPool||["[FK sample]"],0);return v===types_1.SKIP?"(omitted / default)":null===v?"NULL":v instanceof Uint8Array||Buffer.isBuffer(v)?`0x${Buffer.from(v).toString("hex").slice(0,16)}...`:v};const node_crypto_1=require("node:crypto"),common_1=require("../utils/common"),datasets_1=require("./datasets"),types_1=require("./types");exports.randInt=(min,max)=>Math.floor(Math.random()*(max-min+1))+min;exports.randFloat=(min,max)=>Math.random()*(max-min)+min;exports.pick=arr=>arr[(0,exports.randInt)(0,arr.length-1)];exports.makeSlug=()=>`${(0,exports.pick)(datasets_1.LOREM_WORDS)}-${(0,exports.pick)(datasets_1.LOREM_WORDS)}-${(0,exports.randInt)(100,999)}`;exports.makeEmail=()=>`${(0,exports.pick)(datasets_1.FIRST_NAMES).toLowerCase()}.${(0,exports.pick)(datasets_1.LAST_NAMES).toLowerCase()}${(0,exports.randInt)(1,99)}@${(0,exports.pick)(datasets_1.DOMAINS)}`;exports.makeUsername=()=>`${(0,exports.pick)(datasets_1.FIRST_NAMES).toLowerCase()}${(0,exports.pick)(datasets_1.LAST_NAMES).toLowerCase()}${(0,exports.randInt)(1,9999)}`;exports.makePhone=()=>`+1 (${(0,exports.randInt)(200,999)}) ${(0,exports.randInt)(200,999)}-${String((0,exports.randInt)(0,9999)).padStart(4,"0")}`;exports.makeAddress=()=>`${(0,exports.randInt)(10,9999)} ${(0,exports.pick)(datasets_1.LOREM_WORDS)} ${(0,exports.pick)(["St","Ave","Rd","Blvd","Ln","Dr"])}, ${(0,exports.pick)(datasets_1.CITIES)}`;function makeMacAddress(){const b=()=>(0,exports.randInt)(0,255).toString(16).padStart(2,"0").toUpperCase();return`${b()}:${b()}:${b()}:${b()}:${b()}:${b()}`}exports.makeIp=()=>`${(0,exports.randInt)(1,223)}.${(0,exports.randInt)(0,255)}.${(0,exports.randInt)(0,255)}.${(0,exports.randInt)(1,254)}`;exports.makeCreditCard=()=>`4532-${(0,exports.randInt)(1e3,9999)}-${(0,exports.randInt)(1e3,9999)}-${(0,exports.randInt)(1e3,9999)}`;exports.makeAvatar=seedVal=>`https://api.dicebear.com/7.x/avataaars/svg?seed=${encodeURIComponent(seedVal||(0,exports.makeUsername)())}`;function makeUuid(){const h=n=>(0,exports.randInt)(0,4294967295).toString(16).padStart(n,"0");return`${h(8)}-${h(4).slice(0,4)}-4${h(3).slice(0,3)}-${["8","9","a","b"][(0,exports.randInt)(0,3)]}${h(3).slice(0,3)}-${h(12).slice(0,12)}`}function makeWords(minLen,maxLen){const n=(0,exports.randInt)(minLen,maxLen),parts=[];for(let i=0;i<n;i++)parts.push((0,exports.pick)(datasets_1.LOREM_WORDS));return parts.join(" ")}function makeSentences(minLen,maxLen){const n=(0,exports.randInt)(minLen,maxLen),sentences=[];for(let i=0;i<n;i++){const words=makeWords(6,14).split(" ");words[0]=words[0].charAt(0).toUpperCase()+words[0].slice(1),sentences.push(words.join(" ")+".")}return sentences.join(" ")}exports.makeColor=(format="hex")=>"name"===format?(0,exports.pick)(datasets_1.NAMED_COLORS):(0,exports.pick)(datasets_1.HEX_COLORS);function formatPattern(pattern){if(!pattern)return"";const now=new Date,year=String(now.getFullYear()),s=pattern.replace(/\{YYYY\}/g,year).replace(/\{YY\}/g,year.slice(-2)).replace(/\{MM\}/g,String((0,exports.randInt)(1,12)).padStart(2,"0")).replace(/\{DD\}/g,String((0,exports.randInt)(1,28)).padStart(2,"0"));let res="";for(let i=0;i<s.length;i++){const ch=s[i];res+="#"===ch?(0,exports.randInt)(0,9):"A"===ch||"?"===ch?"ABCDEFGHIJKLMNOPQRSTUVWXYZ"[(0,exports.randInt)(0,25)]:"a"===ch?"abcdefghijklmnopqrstuvwxyz"[(0,exports.randInt)(0,25)]:ch}return res}function randomDate(from,to,kind="date"){const now=Date.now(),lo=from?Date.parse(from):now-63072e6,hi=to?Date.parse(to):now,d=new Date((0,exports.randInt)(Number.isFinite(lo)?lo:now-63072e6,Number.isFinite(hi)?hi:now));if("datetime"===kind)return d.toISOString();const p=n=>String(n).padStart(2,"0");return`${d.getFullYear()}-${p(d.getMonth()+1)}-${p(d.getDate())}`}exports.makeParagraph=()=>makeSentences(3,5);function randomUnixTimestamp(format="sec",from,to){const now=Date.now(),lo=from?Date.parse(from):now-63072e6,hi=to?Date.parse(to):now,t=(0,exports.randInt)(Number.isFinite(lo)?lo:now-63072e6,Number.isFinite(hi)?hi:now);return"ms"===format?t:Math.floor(t/1e3)}exports.randomTime=()=>`${String((0,exports.randInt)(0,23)).padStart(2,"0")}:${String((0,exports.randInt)(0,59)).padStart(2,"0")}:${String((0,exports.randInt)(0,59)).padStart(2,"0")}`;const ALNUM="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";function randomAlnum(minLen,maxLen){const len=(0,exports.randInt)(minLen,maxLen);let s="";for(let i=0;i<len;i++)s+=ALNUM[(0,exports.randInt)(0,ALNUM.length-1)];return s}const JSON_ROLES=["admin","editor","viewer","billing","member","owner","guest"],JSON_APPS=["dashboard","api","mobile","cli","webhook","cron","importer"];function jsonValueForKey(key){const lk=key.toLowerCase();if(lk.includes("role"))return(0,exports.pick)(JSON_ROLES);if(lk.includes("app"))return[(0,exports.pick)(JSON_APPS)];const roll=Math.random();return roll<.45?(0,exports.pick)(["alpha","beta","stable","draft","on","off","default","custom"]):roll<.6?(0,exports.randInt)(0,1e3):roll<.75?Math.random()<.5:roll<.9?null:makeUuid()}function makeJsonObject(keys,jsonValues){const k=(keys&&keys.length?keys:["scope","enabled","flags"]).slice(0,16),obj={};for(const key of k){const allowed=jsonValues?jsonValues[key]:void 0;obj[key]=allowed&&allowed.length?(0,exports.pick)(allowed):jsonValueForKey(key)}return JSON.stringify(obj)}function isTextishType(type){const t=(type||"").toUpperCase();return"TEXT"===t||""===t||t.includes("JSON")||t.includes("CHAR")||t.includes("CLOB")||t.includes("VARCHAR")}const STRATEGY_METADATA=[["skip","Skip (DB default or NULL applies)","custom_control","all"],["null","Set NULL explicitly","custom_control","all"],["fixed","Fixed constant value","custom_control","all"],["list","Random item from custom list","custom_control","all"],["pattern","Custom pattern template (e.g. INV-####)","custom_control","all"],["fullname","Full name (e.g. John Smith)","identity","text"],["first","First name","identity","text"],["last","Last name / Surname","identity","text"],["username","Username handle","identity","text"],["job","Job title / Profession","identity","text"],["email","Email address","contact_web","text"],["phone","Phone number","contact_web","text"],["url","Website URL","contact_web","text"],["domain","Domain name","contact_web","text"],["ip","IPv4 address","contact_web","text"],["avatar","Avatar image URL","contact_web","text"],["company","Company / Organization name","commerce","text"],["currency","Currency code (USD, EUR...)","commerce","text"],["status","Status badge (active, pending...)","commerce","text"],["creditCard","Masked test credit card","commerce","text"],["address","Street address","location","text"],["city","City name","location","text"],["state","State / Province","location","text"],["country","Country name","location","text"],["countryCode","Country code (ISO-2 e.g. US)","location","text"],["postal","Postal / Zip code","location","text"],["latitude","Latitude (-90 to 90)","location","all"],["longitude","Longitude (-180 to 180)","location","all"],["int","Random integer in range","numeric","numeric"],["decimal","Random decimal / price","numeric","numeric"],["sequence","Sequential numbering (1, 2, 3...)","numeric","numeric"],["date","Random date (YYYY-MM-DD)","datetime","all"],["datetime","Random datetime (ISO-8601)","datetime","all"],["time","Random time (HH:MM:SS)","datetime","all"],["timestampUnix","Unix timestamp (epoch)","datetime","all"],["words","Short words / Tags","system_crypto","text"],["sentence","Sentences","system_crypto","text"],["paragraph","Paragraph (multi-sentence text)","system_crypto","text"],["slug","URL slug (e.g. tech-news-402)","system_crypto","text"],["bool","Boolean flag (1/0 or true/false)","system_crypto","all"],["bytes","Random hex bytes (BLOB)","system_crypto","blob"],["uuid","UUID v4","system_crypto","text"],["token","Short alphanumeric token","system_crypto","text"],["hash","Hexadecimal hash (MD5/SHA)","system_crypto","text"],["json","Valid JSON object","system_crypto","text"],["color","Color (Hex code or name)","system_crypto","text"],["mac","MAC network address","system_crypto","text"]];const BASIC_STRATEGIES={first:()=>(0,exports.pick)(datasets_1.FIRST_NAMES),last:()=>(0,exports.pick)(datasets_1.LAST_NAMES),fullname:()=>`${(0,exports.pick)(datasets_1.FIRST_NAMES)} ${(0,exports.pick)(datasets_1.LAST_NAMES)}`,email:()=>(0,exports.makeEmail)(),username:()=>(0,exports.makeUsername)(),job:()=>(0,exports.pick)(datasets_1.JOB_TITLES),company:()=>(0,exports.pick)(datasets_1.COMPANIES),currency:()=>(0,exports.pick)(datasets_1.CURRENCIES),status:()=>(0,exports.pick)(datasets_1.STATUSES),creditCard:()=>(0,exports.makeCreditCard)(),avatar:()=>(0,exports.makeAvatar)(),color:p=>(0,exports.makeColor)(p.format),phone:()=>(0,exports.makePhone)(),city:()=>(0,exports.pick)(datasets_1.CITIES),state:()=>(0,exports.pick)(datasets_1.US_STATES),country:()=>(0,exports.pick)(datasets_1.COUNTRIES),countryCode:()=>(0,exports.pick)(datasets_1.COUNTRY_CODES),postal:()=>String((0,exports.randInt)(1e4,99999)),address:()=>(0,exports.makeAddress)(),url:()=>`https://www.${(0,exports.pick)(datasets_1.DOMAINS)}/${(0,exports.makeSlug)()}`,domain:()=>(0,exports.pick)(datasets_1.DOMAINS),ip:()=>(0,exports.makeIp)(),mac:()=>makeMacAddress(),uuid:()=>makeUuid(),slug:()=>(0,exports.makeSlug)(),paragraph:()=>(0,exports.makeParagraph)(),time:()=>(0,exports.randomTime)(),bool:()=>(0,exports.pick)(["1","0"]),json:p=>makeJsonObject(p.jsonKeys,p.jsonValues),words:p=>makeWords(p.minLen??1,p.maxLen??4),sentence:p=>makeSentences(p.minLen??1,p.maxLen??2),date:p=>randomDate(p.from,p.to,"date"),datetime:p=>randomDate(p.from,p.to,"datetime"),timestampUnix:p=>randomUnixTimestamp(p.format,p.from,p.to),latitude:p=>Number((0,exports.randFloat)(p.min??-90,p.max??90).toFixed(6)),longitude:p=>Number((0,exports.randFloat)(p.min??-180,p.max??180).toFixed(6)),sequence:(p,idx)=>(p.start??1)+idx*(p.step??1),int:p=>(0,exports.randInt)(p.min??1,Math.max(p.min??1,p.max??1e3)),decimal:p=>(0,exports.randFloat)(p.min??1,Math.max(p.min??1,p.max??1e3)).toFixed(Math.max(0,Math.min(10,p.precision??2))),pattern:p=>formatPattern(p.pattern||"INV-2026-####"),fixed:p=>p.value??"",token:p=>randomAlnum(Math.max(2,p.minLen??6),Math.max(2,p.maxLen??12)),hash:p=>{const len=(0,exports.randInt)(Math.max(8,p.minLen??32),Math.max(8,p.maxLen??64));return(0,node_crypto_1.randomBytes)(Math.ceil(len/2)).toString("hex").slice(0,len)}};function generateOne(col,p,fkPool,rowIndex=0){const type=(col.type||"").toUpperCase();if(!col.pk&&!col.notnull&&p.nullPct&&p.nullPct>0&&100*Math.random()<p.nullPct)return null;if("skip"===p.strategy)return types_1.SKIP;if("null"===p.strategy)return null;if("list"===p.strategy){const vals=(p.values??[]).filter(v=>""!==String(v).trim());return vals.length?(0,common_1.coerceFormValue)(String((0,exports.pick)(vals)),type||"TEXT"):types_1.SKIP}if("fk"===p.strategy)return fkPool&&fkPool.length?fkPool[(0,exports.randInt)(0,fkPool.length-1)]:null;if("bytes"===p.strategy){const len=(0,exports.randInt)(Math.max(0,p.minLen??8),Math.max(1,p.maxLen??32)),buf=Buffer.alloc(len);for(let i=0;i<len;i++)buf[i]=(0,exports.randInt)(0,255);return(0,common_1.coerceFormValue)(`0x${buf.toString("hex")}`,type||"BLOB")}const gen=BASIC_STRATEGIES[p.strategy];if(!gen)return types_1.SKIP;let rawVal=gen(p,rowIndex);return null!=rawVal&&(p.prefix||p.suffix)&&"string"==typeof rawVal&&(rawVal=`${p.prefix||""}${rawVal}${p.suffix||""}`),(0,common_1.coerceFormValue)(String(rawVal),type||"TEXT")}
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isTextishType = exports.makeJsonObject = exports.randomAlnum = exports.randomUnixTimestamp = exports.randomTime = exports.randomDate = exports.formatPattern = exports.makeParagraph = exports.makeSentences = exports.makeWords = exports.makeUuid = exports.makeColor = exports.makeAvatar = exports.makeCreditCard = exports.makeMacAddress = exports.makeIp = exports.makeAddress = exports.makePhone = exports.makeUsername = exports.makeEmail = exports.makeSlug = exports.pick = exports.randFloat = exports.randInt = void 0;
4
+ exports.strategiesFor = strategiesFor;
5
+ exports.fallbackPlanFor = fallbackPlanFor;
6
+ exports.generateOne = generateOne;
7
+ exports.generateSampleValue = generateSampleValue;
8
+ const prng_1 = require("./prng");
9
+ const registry_1 = require("./registry");
10
+ const types_1 = require("./types");
11
+ const defaultPrng = (0, prng_1.createPrng)();
12
+ const randInt = (min, max) => defaultPrng.int(min, max);
13
+ exports.randInt = randInt;
14
+ const randFloat = (min, max) => defaultPrng.float(min, max);
15
+ exports.randFloat = randFloat;
16
+ const pick = (arr) => defaultPrng.pick(arr);
17
+ exports.pick = pick;
18
+ const makeSlug = () => (0, registry_1.makeSlug)(defaultPrng);
19
+ exports.makeSlug = makeSlug;
20
+ const makeEmail = (idx) => (0, registry_1.makeEmail)(defaultPrng, idx);
21
+ exports.makeEmail = makeEmail;
22
+ const makeUsername = (idx) => (0, registry_1.makeUsername)(defaultPrng, idx);
23
+ exports.makeUsername = makeUsername;
24
+ const makePhone = () => (0, registry_1.makePhone)(defaultPrng);
25
+ exports.makePhone = makePhone;
26
+ const makeAddress = () => (0, registry_1.makeAddress)(defaultPrng);
27
+ exports.makeAddress = makeAddress;
28
+ const makeIp = () => (0, registry_1.makeIp)(defaultPrng);
29
+ exports.makeIp = makeIp;
30
+ const makeMacAddress = () => (0, registry_1.makeMacAddress)(defaultPrng);
31
+ exports.makeMacAddress = makeMacAddress;
32
+ const makeCreditCard = () => (0, registry_1.makeCreditCard)(defaultPrng);
33
+ exports.makeCreditCard = makeCreditCard;
34
+ const makeAvatar = (seedVal) => (0, registry_1.makeAvatar)(defaultPrng, seedVal);
35
+ exports.makeAvatar = makeAvatar;
36
+ const makeColor = (format = 'hex') => (0, registry_1.makeColor)(defaultPrng, format);
37
+ exports.makeColor = makeColor;
38
+ const makeUuid = () => (0, registry_1.makeUuid)(defaultPrng);
39
+ exports.makeUuid = makeUuid;
40
+ const makeWords = (minLen, maxLen) => (0, registry_1.makeWords)(defaultPrng, minLen, maxLen);
41
+ exports.makeWords = makeWords;
42
+ const makeSentences = (minLen, maxLen) => (0, registry_1.makeSentences)(defaultPrng, minLen, maxLen);
43
+ exports.makeSentences = makeSentences;
44
+ const makeParagraph = () => (0, registry_1.makeParagraph)(defaultPrng);
45
+ exports.makeParagraph = makeParagraph;
46
+ const formatPattern = (pattern) => (0, registry_1.formatPattern)(pattern, defaultPrng);
47
+ exports.formatPattern = formatPattern;
48
+ const randomDate = (from, to, kind = 'date') => (0, registry_1.randomDate)(defaultPrng, from, to, kind);
49
+ exports.randomDate = randomDate;
50
+ const randomTime = () => (0, registry_1.randomTime)(defaultPrng);
51
+ exports.randomTime = randomTime;
52
+ const randomUnixTimestamp = (format = 'sec', from, to) => (0, registry_1.randomUnixTimestamp)(defaultPrng, format, from, to);
53
+ exports.randomUnixTimestamp = randomUnixTimestamp;
54
+ const randomAlnum = (minLen, maxLen) => (0, registry_1.randomAlnum)(defaultPrng, minLen, maxLen);
55
+ exports.randomAlnum = randomAlnum;
56
+ const makeJsonObject = (keys, jsonValues) => (0, registry_1.makeJsonObject)(defaultPrng, keys, jsonValues);
57
+ exports.makeJsonObject = makeJsonObject;
58
+ exports.isTextishType = registry_1.isTextishType;
59
+ function strategiesFor(col, fk) {
60
+ return registry_1.GeneratorRegistry.getDescriptorsFor(col, fk);
61
+ }
62
+ function fallbackPlanFor(col) {
63
+ const type = (col.type || '').toUpperCase();
64
+ if (type === 'BOOLEAN' || type === 'BOOL')
65
+ return { strategy: 'bool' };
66
+ if (type === 'UUID')
67
+ return { strategy: 'uuid' };
68
+ if (type === 'JSON' || type === 'JSONB')
69
+ return { strategy: 'json' };
70
+ if (type.startsWith('INT') || type === 'BIGINT' || type === 'SMALLINT' || type === 'SERIAL' || type === 'BIGSERIAL') {
71
+ return { strategy: 'int', min: 1, max: 1000 };
72
+ }
73
+ if (type === 'REAL' || type.includes('DECIMAL') || type === 'NUMERIC' || type.includes('FLOAT') || type.includes('DOUBLE')) {
74
+ return { strategy: 'decimal', min: 1, max: 1000, precision: 2 };
75
+ }
76
+ if (type === 'DATE')
77
+ return { strategy: 'date', from: '2020-01-01', to: '2026-12-31' };
78
+ if (type.includes('TIMESTAMP') || type === 'DATETIME')
79
+ return { strategy: 'datetime', from: '2024-01-01T00:00:00', to: '2026-12-31T23:59:59' };
80
+ if (type === 'TIME')
81
+ return { strategy: 'time' };
82
+ if (type === 'BLOB' || type === 'BYTEA')
83
+ return { strategy: 'bytes', minLen: 8, maxLen: 32 };
84
+ if (type === 'INET')
85
+ return { strategy: 'ip' };
86
+ return { strategy: 'words', minLen: 1, maxLen: 4 };
87
+ }
88
+ function generateOne(col, plan, fkPool, rowIndex = 0) {
89
+ return (0, registry_1.generateColumnValue)(col, plan, defaultPrng, rowIndex, {}, fkPool);
90
+ }
91
+ function generateSampleValue(col, plan, fkPool) {
92
+ const v = generateOne(col, plan, fkPool || ['[FK sample]'], 0);
93
+ if (v === types_1.SKIP)
94
+ return '(omitted / default)';
95
+ if (v === null)
96
+ return 'NULL';
97
+ if (v instanceof Uint8Array || Buffer.isBuffer(v))
98
+ return `0x${Buffer.from(v).toString('hex').slice(0, 16)}...`;
99
+ return v;
100
+ }
@@ -0,0 +1 @@
1
+ import type {RandomSource} from './prng';import type {ColumnPlan} from './types';export declare function extractTemplateDependencies(template:string):string[];export declare function evaluateTemplate(template:string,rowValues:Record<string,unknown>,prng:RandomSource,rowIndex?:number):string;export declare function orderColumnDependencies(columnNames:string[],plans:Record<string,ColumnPlan>):{orderedColumns:string[];hasCycle:boolean;cycleColumns:string[];};
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractTemplateDependencies = extractTemplateDependencies;
4
+ exports.evaluateTemplate = evaluateTemplate;
5
+ exports.orderColumnDependencies = orderColumnDependencies;
6
+ const registry_1 = require("./registry");
7
+ function extractTemplateDependencies(template) {
8
+ if (!template)
9
+ return [];
10
+ const deps = [];
11
+ const re = /\{\{\s*([a-zA-Z0-9_]+)(?:\s*:[^}]+)?\s*\}\}/g;
12
+ let m;
13
+ while ((m = re.exec(template))) {
14
+ const name = m[1].trim();
15
+ if (!['sequence', 'random', 'year', 'uuid', 'slug', 'now', 'date'].includes(name.toLowerCase())) {
16
+ if (!deps.includes(name))
17
+ deps.push(name);
18
+ }
19
+ }
20
+ return deps;
21
+ }
22
+ function evaluateTemplate(template, rowValues, prng, rowIndex = 0) {
23
+ if (!template)
24
+ return '';
25
+ return template.replace(/\{\{\s*([a-zA-Z0-9_]+)(?::([^}]+))?\s*\}\}/g, (_, token, argsStr) => {
26
+ const t = token.trim();
27
+ const args = argsStr ? argsStr.split(/[:,]/).map((s) => s.trim()).filter(Boolean) : [];
28
+ switch (t.toLowerCase()) {
29
+ case 'year':
30
+ return String(new Date().getFullYear());
31
+ case 'uuid':
32
+ return (0, registry_1.makeUuid)(prng);
33
+ case 'slug':
34
+ return (0, registry_1.makeSlug)(prng);
35
+ case 'now':
36
+ return new Date().toISOString();
37
+ case 'date':
38
+ return new Date().toISOString().slice(0, 10);
39
+ case 'sequence': {
40
+ const start = args[0] ? Number.parseInt(args[0], 10) : 1;
41
+ const step = args[1] ? Number.parseInt(args[1], 10) : 1;
42
+ const s = Number.isFinite(start) ? start : 1;
43
+ const st = Number.isFinite(step) ? step : 1;
44
+ return String(s + rowIndex * st);
45
+ }
46
+ case 'random': {
47
+ const min = args[0] ? Number.parseInt(args[0], 10) : 1;
48
+ const max = args[1] ? Number.parseInt(args[1], 10) : 100;
49
+ const lo = Number.isFinite(min) ? min : 1;
50
+ const hi = Number.isFinite(max) ? max : 100;
51
+ return String(prng.int(lo, hi));
52
+ }
53
+ }
54
+ let rawVal = rowValues[t];
55
+ if (rawVal === undefined) {
56
+ const lower = t.toLowerCase();
57
+ for (const [k, v] of Object.entries(rowValues)) {
58
+ if (k.toLowerCase() === lower) {
59
+ rawVal = v;
60
+ break;
61
+ }
62
+ }
63
+ }
64
+ if (rawVal != null) {
65
+ let str = String(rawVal);
66
+ if (template.includes('@') && template.indexOf(`{{${token}`) < template.indexOf('@')) {
67
+ str = str
68
+ .toLowerCase()
69
+ .trim()
70
+ .normalize('NFD')
71
+ .replace(/[\u0300-\u036f]/g, '')
72
+ .replace(/[^a-z0-9]+/g, '.')
73
+ .replace(/^\.+|\.+$/g, '');
74
+ }
75
+ return str;
76
+ }
77
+ return '';
78
+ });
79
+ }
80
+ function orderColumnDependencies(columnNames, plans) {
81
+ const colSet = new Set(columnNames);
82
+ const inDegree = new Map();
83
+ const graph = new Map();
84
+ for (const col of columnNames) {
85
+ inDegree.set(col, 0);
86
+ graph.set(col, new Set());
87
+ }
88
+ const nameColumns = columnNames.filter((c) => ['first_name', 'last_name', 'full_name', 'fullname', 'name', 'customer_name'].includes(c.toLowerCase()));
89
+ for (const col of columnNames) {
90
+ const plan = plans[col];
91
+ if (plan?.strategy === 'template' && plan.template) {
92
+ const deps = extractTemplateDependencies(plan.template).filter((d) => colSet.has(d) && d !== col);
93
+ for (const dep of deps) {
94
+ graph.get(dep)?.add(col);
95
+ inDegree.set(col, (inDegree.get(col) ?? 0) + 1);
96
+ }
97
+ }
98
+ else if (plan?.strategy === 'email' || plan?.strategy === 'username') {
99
+ for (const nameCol of nameColumns) {
100
+ if (nameCol !== col && !graph.get(nameCol)?.has(col)) {
101
+ graph.get(nameCol)?.add(col);
102
+ inDegree.set(col, (inDegree.get(col) ?? 0) + 1);
103
+ }
104
+ }
105
+ }
106
+ }
107
+ const queue = [];
108
+ for (const [col, deg] of inDegree.entries()) {
109
+ if (deg === 0)
110
+ queue.push(col);
111
+ }
112
+ const orderedColumns = [];
113
+ while (queue.length > 0) {
114
+ const curr = queue.shift();
115
+ orderedColumns.push(curr);
116
+ for (const dependent of graph.get(curr) ?? []) {
117
+ const newDeg = (inDegree.get(dependent) ?? 1) - 1;
118
+ inDegree.set(dependent, newDeg);
119
+ if (newDeg === 0)
120
+ queue.push(dependent);
121
+ }
122
+ }
123
+ const hasCycle = orderedColumns.length < columnNames.length;
124
+ const cycleColumns = [];
125
+ if (hasCycle) {
126
+ for (const col of columnNames) {
127
+ if (!orderedColumns.includes(col))
128
+ cycleColumns.push(col);
129
+ }
130
+ for (const col of cycleColumns)
131
+ orderedColumns.push(col);
132
+ }
133
+ return { orderedColumns, hasCycle, cycleColumns };
134
+ }
@@ -1 +1 @@
1
- import type {ForeignKeyInfo,WhereClause} from '../db/database';export declare const MAX_SEED_ROWS=5000;export type StrategyCategory='identity'|'contact_web'|'commerce'|'location'|'numeric'|'datetime'|'system_crypto'|'custom_control'|'relational';export type GeneratorStrategyId='skip'|'null'|'fixed'|'list'|'pattern'|'first'|'last'|'fullname'|'username'|'job'|'email'|'phone'|'url'|'domain'|'ip'|'avatar'|'company'|'currency'|'status'|'creditCard'|'city'|'country'|'state'|'postal'|'address'|'countryCode'|'latitude'|'longitude'|'words'|'sentence'|'paragraph'|'slug'|'int'|'decimal'|'sequence'|'date'|'datetime'|'time'|'timestampUnix'|'bool'|'bytes'|'json'|'hash'|'token'|'uuid'|'color'|'mac'|'fk';export interface ColumnPlan{strategy:GeneratorStrategyId;value?:string;values?:string[];min?:number;max?:number;precision?:number; from ?:string;to?:string;minLen?:number;maxLen?:number;jsonKeys?:string[];jsonValues?:Record<string,string[]>;nullPct?:number;prefix?:string;suffix?:string;start?:number;step?:number;pattern?:string;format?:string;}export interface StrategyDescriptor{id:GeneratorStrategyId;label:string;category:StrategyCategory;description?:string;}export interface ColumnGeneratorConfig{name:string;type:string;pk:boolean;notnull:boolean;hasDefault:boolean;unique:boolean;fk:ForeignKeyInfo|null;strategies:StrategyDescriptor[];defaultPlan:ColumnPlan;}export interface GenerateResult{rows:WhereClause[][];previewRows:Record<string,unknown>[];warnings:string[];}export declare const SKIP:unique symbol;
1
+ import type {ForeignKeyInfo,WhereClause} from '../db/database';export declare const MAX_SEED_ROWS=5000;export type StrategyCategory='identity'|'contact_web'|'commerce'|'location'|'numeric'|'datetime'|'system_crypto'|'custom_control'|'database_aware'|'relational';export type GeneratorStrategyId='skip'|'null'|'fixed'|'list'|'pattern'|'template'|'first'|'last'|'fullname'|'username'|'job'|'email'|'phone'|'url'|'domain'|'ip'|'avatar'|'company'|'currency'|'status'|'orderStatus'|'product'|'productCategory'|'department'|'paymentMethod'|'transactionType'|'techSkill'|'creditCard'|'city'|'country'|'state'|'postal'|'address'|'countryCode'|'latitude'|'longitude'|'words'|'sentence'|'paragraph'|'slug'|'int'|'decimal'|'sequence'|'percentage'|'date'|'datetime'|'time'|'timestampUnix'|'relativeDate'|'bool'|'bytes'|'json'|'hash'|'token'|'uuid'|'color'|'mac'|'fk'|'sampleExisting'|'sequentialFk';export interface ColumnPlan{strategy:GeneratorStrategyId;value?:string;values?:string[];template?:string;min?:number;max?:number;precision?:number; from ?:string;to?:string;minLen?:number;maxLen?:number;jsonKeys?:string[];jsonValues?:Record<string,string[]>;nullPct?:number;prefix?:string;suffix?:string;start?:number;step?:number;pattern?:string;format?:string;unique?:boolean;refTable?:string;refColumn?:string;}export interface StrategyDescriptor{id:GeneratorStrategyId;label:string;category:StrategyCategory;description?:string;}export interface ColumnGeneratorConfig{name:string;type:string;pk:boolean;notnull:boolean;hasDefault:boolean;unique:boolean;fk:ForeignKeyInfo|null;strategies:StrategyDescriptor[];defaultPlan:ColumnPlan;}export type TableGenerationMode='generate'|'use_existing'|'generate_if_empty'|'skip';export type CardinalityDistribution='uniform'|'fixed'|'normal'|'weighted';export interface RelationshipConfig{parentTable:string;parentColumn:string;childTable:string;childColumn:string;minPerParent?:number;maxPerParent?:number;distribution?:CardinalityDistribution;uniquePairs?:boolean;nullable?:boolean;nullChance?:number;}export interface TableGenerationSpec{mode:TableGenerationMode;rows?:number;columns:Record<string,ColumnPlan>;truncate?:boolean;}export interface GenerationOptions{seed?:number|null;transaction?:boolean;rollbackOnError?:boolean;truncateAll?:boolean;batchSize?:number;previewLimit?:number;}export interface GenerationPlan{name?:string;description?:string;version?:number;rootTable?:string;scope?:ErChainScope;tables:Record<string,TableGenerationSpec>;relationships?:RelationshipConfig[];options?:GenerationOptions;}export interface ValidationIssue{type:'error'|'warning'|'info';table?:string;column?:string;message:string;code?:string;}export interface ValidationReport{valid:boolean;canExecute:boolean;issues:ValidationIssue[];summary:{totalTables:number;tablesToGenerate:number;tablesToUseExisting:number;tablesToSkip:number;estimatedTotalRows:number;cyclesDetected:boolean;cycleTables:string[];executionOrder:string[];};}export interface GenerateResult{rows:WhereClause[][];previewRows:Record<string,unknown>[];warnings:string[];}export type ErChainScope='chain'|'ancestors'|'descendants'|'all'|'single';export interface ErChainTableNode{name:string;order:number;depth:number;parents:{table:string; from :string;to:string;notnull?:boolean;}[];children:{table:string; from :string;to:string;notnull?:boolean;}[];columns:ColumnGeneratorConfig[];suggestedCount:number;rowCount:number;isRoot:boolean;hasSelfRef:boolean;isJunction?:boolean;mode?:TableGenerationMode;}export interface ErChainConfig{rootTable:string;scope:ErChainScope;tables:ErChainTableNode[];cycleDetected:boolean;cycleTables?:string[];maxTotalRows:number;relationships?:RelationshipConfig[];}export interface ErChainResult{rootTable?:string;scope?:ErChainScope;executionOrder:string[];tableResults:Record<string,{count:number;rows:WhereClause[][];previewRows:Record<string,unknown>[];warnings:string[];}>;totalRows:number;sql:string;warnings:string[];validation?:ValidationReport;}export interface SeedProfile{id:string;name:string;description?:string;createdAt:string;updatedAt:string;plan:GenerationPlan;}export declare const SKIP:unique symbol;
@@ -1 +1,5 @@
1
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.SKIP=exports.MAX_SEED_ROWS=void 0,exports.MAX_SEED_ROWS=5e3,exports.SKIP=Symbol("skip");
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SKIP = exports.MAX_SEED_ROWS = void 0;
4
+ exports.MAX_SEED_ROWS = 5000;
5
+ exports.SKIP = Symbol('skip');
@@ -0,0 +1 @@
1
+ import type {IDatabase,TableInfoData} from '../db/index';import type {GenerationPlan,ValidationReport} from './types';export declare function validateGenerationPlan(db:IDatabase,plan:GenerationPlan,tableInfos?:Map<string,TableInfoData>):Promise<ValidationReport>;