admindb 2.2.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 (87) hide show
  1. package/README.md +368 -364
  2. package/dist/app.js +6 -9
  3. package/dist/auth/config.d.ts +1 -1
  4. package/dist/auth/config.js +18 -30
  5. package/dist/auth/crypto.js +11 -21
  6. package/dist/auth/middleware.js +9 -16
  7. package/dist/auth/routes.js +9 -15
  8. package/dist/cli/args.d.ts +1 -1
  9. package/dist/cli/args.js +15 -26
  10. package/dist/cli/config.js +6 -16
  11. package/dist/cli/runner.js +4 -7
  12. package/dist/data/chain.d.ts +1 -1
  13. package/dist/data/chain.js +40 -617
  14. package/dist/data/datasets.d.ts +1 -1
  15. package/dist/data/datasets.js +43 -1
  16. package/dist/data/detector.js +45 -21
  17. package/dist/data/engine.d.ts +1 -1
  18. package/dist/data/engine.js +483 -142
  19. package/dist/data/index.d.ts +1 -1
  20. package/dist/data/index.js +8 -0
  21. package/dist/data/prng.d.ts +1 -0
  22. package/dist/data/prng.js +73 -0
  23. package/dist/data/registry.d.ts +1 -0
  24. package/dist/data/registry.js +805 -0
  25. package/dist/data/schema-graph.d.ts +1 -0
  26. package/dist/data/schema-graph.js +210 -0
  27. package/dist/data/strategies.d.ts +1 -1
  28. package/dist/data/strategies.js +44 -283
  29. package/dist/data/templates.d.ts +1 -0
  30. package/dist/data/templates.js +134 -0
  31. package/dist/data/types.d.ts +1 -1
  32. package/dist/data/validator.d.ts +1 -0
  33. package/dist/data/validator.js +160 -0
  34. package/dist/db/database.js +9 -14
  35. package/dist/db/export.js +32 -55
  36. package/dist/db/filters.d.ts +1 -1
  37. package/dist/db/filters.js +15 -38
  38. package/dist/db/introspection.js +4 -7
  39. package/dist/db/manager.js +27 -51
  40. package/dist/db/migrations.js +7 -14
  41. package/dist/db/postgres.js +30 -78
  42. package/dist/index.d.ts +1 -1
  43. package/dist/public/css/app.css +1 -1
  44. package/dist/public/js/browse.js +1 -1
  45. package/dist/public/js/inspector.js +1 -1
  46. package/dist/public/js/query.js +1 -1
  47. package/dist/public/js/seed.js +1 -1
  48. package/dist/routes/api/erd.js +3 -8
  49. package/dist/routes/api/helpers.d.ts +1 -1
  50. package/dist/routes/api/helpers.js +75 -25
  51. package/dist/routes/api/import-export.js +1 -2
  52. package/dist/routes/api/query.js +2 -6
  53. package/dist/routes/api/rows.js +4 -8
  54. package/dist/routes/api/seed.js +35 -21
  55. package/dist/routes/api/tables.js +15 -30
  56. package/dist/routes/databases.js +8 -26
  57. package/dist/routes/pages.js +67 -18
  58. package/dist/serverless.js +15 -40
  59. package/dist/sql/generator.d.ts +1 -1
  60. package/dist/sql/generator.js +35 -109
  61. package/dist/types/api.d.ts +1 -1
  62. package/dist/utils/common.d.ts +1 -1
  63. package/dist/utils/common.js +45 -85
  64. package/dist/utils/csv.d.ts +1 -1
  65. package/dist/utils/csv.js +11 -17
  66. package/dist/utils/datatype.js +46 -60
  67. package/dist/utils/icons.js +8 -8
  68. package/dist/views/layouts/main.hbs +1 -87
  69. package/dist/views/pages/databases.hbs +1 -233
  70. package/dist/views/pages/designer.hbs +1 -89
  71. package/dist/views/pages/erd.hbs +1 -1241
  72. package/dist/views/pages/error.hbs +1 -10
  73. package/dist/views/pages/form.hbs +1 -59
  74. package/dist/views/pages/home.hbs +1 -220
  75. package/dist/views/pages/info.hbs +1 -107
  76. package/dist/views/pages/login.hbs +1 -98
  77. package/dist/views/pages/query.hbs +1 -129
  78. package/dist/views/pages/schema.hbs +1 -298
  79. package/dist/views/pages/seed-select.hbs +251 -0
  80. package/dist/views/pages/seed.hbs +256 -414
  81. package/dist/views/pages/table.hbs +1 -327
  82. package/dist/views/partials/navbar.hbs +1 -83
  83. package/dist/views/partials/sidebar.hbs +1 -129
  84. package/docs/API.md +149 -149
  85. package/docs/EXAMPLES.md +579 -579
  86. package/docs/SECURITY.md +265 -265
  87. package/package.json +79 -79
@@ -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:(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 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,224 +1,63 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- 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;
4
- exports.makeMacAddress = makeMacAddress;
5
- exports.makeUuid = makeUuid;
6
- exports.makeWords = makeWords;
7
- exports.makeSentences = makeSentences;
8
- exports.formatPattern = formatPattern;
9
- exports.randomDate = randomDate;
10
- exports.randomUnixTimestamp = randomUnixTimestamp;
11
- exports.randomAlnum = randomAlnum;
12
- exports.makeJsonObject = makeJsonObject;
13
- exports.isTextishType = isTextishType;
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;
14
4
  exports.strategiesFor = strategiesFor;
15
5
  exports.fallbackPlanFor = fallbackPlanFor;
16
6
  exports.generateOne = generateOne;
17
7
  exports.generateSampleValue = generateSampleValue;
18
- const node_crypto_1 = require("node:crypto");
19
- const common_1 = require("../utils/common");
20
- const datasets_1 = require("./datasets");
8
+ const prng_1 = require("./prng");
9
+ const registry_1 = require("./registry");
21
10
  const types_1 = require("./types");
22
- const randInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
11
+ const defaultPrng = (0, prng_1.createPrng)();
12
+ const randInt = (min, max) => defaultPrng.int(min, max);
23
13
  exports.randInt = randInt;
24
- const randFloat = (min, max) => Math.random() * (max - min) + min;
14
+ const randFloat = (min, max) => defaultPrng.float(min, max);
25
15
  exports.randFloat = randFloat;
26
- const pick = (arr) => arr[(0, exports.randInt)(0, arr.length - 1)];
16
+ const pick = (arr) => defaultPrng.pick(arr);
27
17
  exports.pick = pick;
28
- const makeSlug = () => `${(0, exports.pick)(datasets_1.LOREM_WORDS)}-${(0, exports.pick)(datasets_1.LOREM_WORDS)}-${(0, exports.randInt)(100, 999)}`;
18
+ const makeSlug = () => (0, registry_1.makeSlug)(defaultPrng);
29
19
  exports.makeSlug = makeSlug;
30
- const makeEmail = (idx) => `${(0, exports.pick)(datasets_1.FIRST_NAMES).toLowerCase()}.${(0, exports.pick)(datasets_1.LAST_NAMES).toLowerCase()}${idx != null ? idx + 1 : ''}${(0, exports.randInt)(10, 9999)}@${(0, exports.pick)(datasets_1.DOMAINS)}`;
20
+ const makeEmail = (idx) => (0, registry_1.makeEmail)(defaultPrng, idx);
31
21
  exports.makeEmail = makeEmail;
32
- const makeUsername = (idx) => `${(0, exports.pick)(datasets_1.FIRST_NAMES).toLowerCase()}${(0, exports.pick)(datasets_1.LAST_NAMES).toLowerCase()}${idx != null ? idx + 1 : ''}${(0, exports.randInt)(10, 9999)}`;
22
+ const makeUsername = (idx) => (0, registry_1.makeUsername)(defaultPrng, idx);
33
23
  exports.makeUsername = makeUsername;
34
- const makePhone = () => `+1 (${(0, exports.randInt)(200, 999)}) ${(0, exports.randInt)(200, 999)}-${String((0, exports.randInt)(0, 9999)).padStart(4, '0')}`;
24
+ const makePhone = () => (0, registry_1.makePhone)(defaultPrng);
35
25
  exports.makePhone = makePhone;
36
- const 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)}`;
26
+ const makeAddress = () => (0, registry_1.makeAddress)(defaultPrng);
37
27
  exports.makeAddress = makeAddress;
38
- const makeIp = () => `${(0, exports.randInt)(1, 223)}.${(0, exports.randInt)(0, 255)}.${(0, exports.randInt)(0, 255)}.${(0, exports.randInt)(1, 254)}`;
28
+ const makeIp = () => (0, registry_1.makeIp)(defaultPrng);
39
29
  exports.makeIp = makeIp;
40
- function makeMacAddress() {
41
- const b = () => (0, exports.randInt)(0, 255).toString(16).padStart(2, '0').toUpperCase();
42
- return `${b()}:${b()}:${b()}:${b()}:${b()}:${b()}`;
43
- }
44
- const makeCreditCard = () => `4532-${(0, exports.randInt)(1000, 9999)}-${(0, exports.randInt)(1000, 9999)}-${(0, exports.randInt)(1000, 9999)}`;
30
+ const makeMacAddress = () => (0, registry_1.makeMacAddress)(defaultPrng);
31
+ exports.makeMacAddress = makeMacAddress;
32
+ const makeCreditCard = () => (0, registry_1.makeCreditCard)(defaultPrng);
45
33
  exports.makeCreditCard = makeCreditCard;
46
- const makeAvatar = (seedVal) => `https://api.dicebear.com/7.x/avataaars/svg?seed=${encodeURIComponent(seedVal || (0, exports.makeUsername)())}`;
34
+ const makeAvatar = (seedVal) => (0, registry_1.makeAvatar)(defaultPrng, seedVal);
47
35
  exports.makeAvatar = makeAvatar;
48
- const makeColor = (format = 'hex') => (format === 'name' ? (0, exports.pick)(datasets_1.NAMED_COLORS) : (0, exports.pick)(datasets_1.HEX_COLORS));
36
+ const makeColor = (format = 'hex') => (0, registry_1.makeColor)(defaultPrng, format);
49
37
  exports.makeColor = makeColor;
50
- function makeUuid() {
51
- const h = (n) => (0, exports.randInt)(0, 0xffffffff).toString(16).padStart(n, '0');
52
- 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)}`;
53
- }
54
- function makeWords(minLen, maxLen) {
55
- const n = (0, exports.randInt)(minLen, maxLen);
56
- const parts = [];
57
- for (let i = 0; i < n; i++)
58
- parts.push((0, exports.pick)(datasets_1.LOREM_WORDS));
59
- return parts.join(' ');
60
- }
61
- function makeSentences(minLen, maxLen) {
62
- const n = (0, exports.randInt)(minLen, maxLen);
63
- const sentences = [];
64
- for (let i = 0; i < n; i++) {
65
- const words = makeWords(6, 14).split(' ');
66
- words[0] = words[0].charAt(0).toUpperCase() + words[0].slice(1);
67
- sentences.push(words.join(' ') + '.');
68
- }
69
- return sentences.join(' ');
70
- }
71
- const makeParagraph = () => makeSentences(3, 5);
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);
72
45
  exports.makeParagraph = makeParagraph;
73
- function formatPattern(pattern) {
74
- if (!pattern)
75
- return '';
76
- const now = new Date();
77
- const year = String(now.getFullYear());
78
- const s = pattern
79
- .replace(/\{YYYY\}/g, year)
80
- .replace(/\{YY\}/g, year.slice(-2))
81
- .replace(/\{MM\}/g, String((0, exports.randInt)(1, 12)).padStart(2, '0'))
82
- .replace(/\{DD\}/g, String((0, exports.randInt)(1, 28)).padStart(2, '0'));
83
- const upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
84
- const lower = 'abcdefghijklmnopqrstuvwxyz';
85
- let res = '';
86
- for (let i = 0; i < s.length; i++) {
87
- const ch = s[i];
88
- if (ch === '#')
89
- res += (0, exports.randInt)(0, 9);
90
- else if (ch === 'A' || ch === '?')
91
- res += upper[(0, exports.randInt)(0, 25)];
92
- else if (ch === 'a')
93
- res += lower[(0, exports.randInt)(0, 25)];
94
- else
95
- res += ch;
96
- }
97
- return res;
98
- }
99
- function randomDate(from, to, kind = 'date') {
100
- const now = Date.now();
101
- const lo = from ? Date.parse(from) : now - 730 * 86400_000;
102
- const hi = to ? Date.parse(to) : now;
103
- const d = new Date((0, exports.randInt)(Number.isFinite(lo) ? lo : now - 730 * 86400_000, Number.isFinite(hi) ? hi : now));
104
- if (kind === 'datetime')
105
- return d.toISOString();
106
- const p = (n) => String(n).padStart(2, '0');
107
- return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`;
108
- }
109
- const 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')}`;
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);
110
51
  exports.randomTime = randomTime;
111
- function randomUnixTimestamp(format = 'sec', from, to) {
112
- const now = Date.now();
113
- const lo = from ? Date.parse(from) : now - 730 * 86400_000;
114
- const hi = to ? Date.parse(to) : now;
115
- const t = (0, exports.randInt)(Number.isFinite(lo) ? lo : now - 730 * 86400_000, Number.isFinite(hi) ? hi : now);
116
- return format === 'ms' ? t : Math.floor(t / 1000);
117
- }
118
- const ALNUM = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
119
- function randomAlnum(minLen, maxLen) {
120
- const len = (0, exports.randInt)(minLen, maxLen);
121
- let s = '';
122
- for (let i = 0; i < len; i++)
123
- s += ALNUM[(0, exports.randInt)(0, ALNUM.length - 1)];
124
- return s;
125
- }
126
- const JSON_ROLES = ['admin', 'editor', 'viewer', 'billing', 'member', 'owner', 'guest'];
127
- const JSON_APPS = ['dashboard', 'api', 'mobile', 'cli', 'webhook', 'cron', 'importer'];
128
- function jsonValueForKey(key) {
129
- const lk = key.toLowerCase();
130
- if (lk.includes('role'))
131
- return (0, exports.pick)(JSON_ROLES);
132
- if (lk.includes('app'))
133
- return [(0, exports.pick)(JSON_APPS)];
134
- const roll = Math.random();
135
- if (roll < 0.45)
136
- return (0, exports.pick)(['alpha', 'beta', 'stable', 'draft', 'on', 'off', 'default', 'custom']);
137
- if (roll < 0.6)
138
- return (0, exports.randInt)(0, 1000);
139
- if (roll < 0.75)
140
- return Math.random() < 0.5;
141
- if (roll < 0.9)
142
- return null;
143
- return makeUuid();
144
- }
145
- function makeJsonObject(keys, jsonValues) {
146
- const k = (keys && keys.length ? keys : ['scope', 'enabled', 'flags']).slice(0, 16);
147
- const obj = {};
148
- for (const key of k) {
149
- const allowed = jsonValues ? jsonValues[key] : undefined;
150
- obj[key] = allowed && allowed.length ? (0, exports.pick)(allowed) : jsonValueForKey(key);
151
- }
152
- return JSON.stringify(obj);
153
- }
154
- function isTextishType(type) {
155
- const t = (type || '').toUpperCase();
156
- return t === 'TEXT' || t === '' || t.includes('JSON') || t.includes('CHAR') || t.includes('CLOB') || t.includes('VARCHAR');
157
- }
158
- const STRATEGY_METADATA = [
159
- ['skip', 'Skip (DB default or NULL applies)', 'custom_control', 'all'],
160
- ['null', 'Set NULL explicitly', 'custom_control', 'all'],
161
- ['fixed', 'Fixed constant value', 'custom_control', 'all'],
162
- ['list', 'Random item from custom list', 'custom_control', 'all'],
163
- ['pattern', 'Custom pattern template (e.g. INV-####)', 'custom_control', 'all'],
164
- ['fullname', 'Full name (e.g. John Smith)', 'identity', 'text'],
165
- ['first', 'First name', 'identity', 'text'],
166
- ['last', 'Last name / Surname', 'identity', 'text'],
167
- ['username', 'Username handle', 'identity', 'text'],
168
- ['job', 'Job title / Profession', 'identity', 'text'],
169
- ['email', 'Email address', 'contact_web', 'text'],
170
- ['phone', 'Phone number', 'contact_web', 'text'],
171
- ['url', 'Website URL', 'contact_web', 'text'],
172
- ['domain', 'Domain name', 'contact_web', 'text'],
173
- ['ip', 'IPv4 address', 'contact_web', 'text'],
174
- ['avatar', 'Avatar image URL', 'contact_web', 'text'],
175
- ['company', 'Company / Organization name', 'commerce', 'text'],
176
- ['currency', 'Currency code (USD, EUR...)', 'commerce', 'text'],
177
- ['status', 'Status badge (active, pending...)', 'commerce', 'text'],
178
- ['creditCard', 'Masked test credit card', 'commerce', 'text'],
179
- ['address', 'Street address', 'location', 'text'],
180
- ['city', 'City name', 'location', 'text'],
181
- ['state', 'State / Province', 'location', 'text'],
182
- ['country', 'Country name', 'location', 'text'],
183
- ['countryCode', 'Country code (ISO-2 e.g. US)', 'location', 'text'],
184
- ['postal', 'Postal / Zip code', 'location', 'text'],
185
- ['latitude', 'Latitude (-90 to 90)', 'location', 'all'],
186
- ['longitude', 'Longitude (-180 to 180)', 'location', 'all'],
187
- ['int', 'Random integer in range', 'numeric', 'numeric'],
188
- ['decimal', 'Random decimal / price', 'numeric', 'numeric'],
189
- ['sequence', 'Sequential numbering (1, 2, 3...)', 'numeric', 'numeric'],
190
- ['date', 'Random date (YYYY-MM-DD)', 'datetime', 'all'],
191
- ['datetime', 'Random datetime (ISO-8601)', 'datetime', 'all'],
192
- ['time', 'Random time (HH:MM:SS)', 'datetime', 'all'],
193
- ['timestampUnix', 'Unix timestamp (epoch)', 'datetime', 'all'],
194
- ['words', 'Short words / Tags', 'system_crypto', 'text'],
195
- ['sentence', 'Sentences', 'system_crypto', 'text'],
196
- ['paragraph', 'Paragraph (multi-sentence text)', 'system_crypto', 'text'],
197
- ['slug', 'URL slug (e.g. tech-news-402)', 'system_crypto', 'text'],
198
- ['bool', 'Boolean flag (1/0 or true/false)', 'system_crypto', 'all'],
199
- ['bytes', 'Random hex bytes (BLOB)', 'system_crypto', 'blob'],
200
- ['uuid', 'UUID v4', 'system_crypto', 'text'],
201
- ['token', 'Short alphanumeric token', 'system_crypto', 'text'],
202
- ['hash', 'Hexadecimal hash (MD5/SHA)', 'system_crypto', 'text'],
203
- ['json', 'Valid JSON object', 'system_crypto', 'text'],
204
- ['color', 'Color (Hex code or name)', 'system_crypto', 'text'],
205
- ['mac', 'MAC network address', 'system_crypto', 'text'],
206
- ];
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;
207
59
  function strategiesFor(col, fk) {
208
- const type = (col.type || '').toUpperCase();
209
- const isText = isTextishType(type);
210
- const isNum = type.startsWith('INTEGER') || type === 'REAL' || type.includes('DECIMAL') || type.includes('NUMERIC') || isText;
211
- const isBlob = type === 'BLOB' || isText;
212
- const out = [];
213
- if (fk) {
214
- out.push({ id: 'fk', label: `Foreign Key: Sample from ${fk.table}${fk.to ? `.${fk.to}` : ''}`, category: 'relational' });
215
- }
216
- for (const [id, label, category, target] of STRATEGY_METADATA) {
217
- if (target === 'all' || (target === 'text' && isText) || (target === 'numeric' && isNum) || (target === 'blob' && isBlob)) {
218
- out.push({ id, label, category });
219
- }
220
- }
221
- return out;
60
+ return registry_1.GeneratorRegistry.getDescriptorsFor(col, fk);
222
61
  }
223
62
  function fallbackPlanFor(col) {
224
63
  const type = (col.type || '').toUpperCase();
@@ -228,10 +67,12 @@ function fallbackPlanFor(col) {
228
67
  return { strategy: 'uuid' };
229
68
  if (type === 'JSON' || type === 'JSONB')
230
69
  return { strategy: 'json' };
231
- if (type.startsWith('INT') || type === 'BIGINT' || type === 'SMALLINT' || type === 'SERIAL' || type === 'BIGSERIAL')
70
+ if (type.startsWith('INT') || type === 'BIGINT' || type === 'SMALLINT' || type === 'SERIAL' || type === 'BIGSERIAL') {
232
71
  return { strategy: 'int', min: 1, max: 1000 };
233
- if (type === 'REAL' || type.includes('DECIMAL') || type === 'NUMERIC' || type.includes('FLOAT') || type.includes('DOUBLE'))
72
+ }
73
+ if (type === 'REAL' || type.includes('DECIMAL') || type === 'NUMERIC' || type.includes('FLOAT') || type.includes('DOUBLE')) {
234
74
  return { strategy: 'decimal', min: 1, max: 1000, precision: 2 };
75
+ }
235
76
  if (type === 'DATE')
236
77
  return { strategy: 'date', from: '2020-01-01', to: '2026-12-31' };
237
78
  if (type.includes('TIMESTAMP') || type === 'DATETIME')
@@ -244,88 +85,8 @@ function fallbackPlanFor(col) {
244
85
  return { strategy: 'ip' };
245
86
  return { strategy: 'words', minLen: 1, maxLen: 4 };
246
87
  }
247
- const BASIC_STRATEGIES = {
248
- first: () => (0, exports.pick)(datasets_1.FIRST_NAMES),
249
- last: () => (0, exports.pick)(datasets_1.LAST_NAMES),
250
- fullname: () => `${(0, exports.pick)(datasets_1.FIRST_NAMES)} ${(0, exports.pick)(datasets_1.LAST_NAMES)}`,
251
- email: (_, idx) => (0, exports.makeEmail)(idx),
252
- username: (_, idx) => (0, exports.makeUsername)(idx),
253
- job: () => (0, exports.pick)(datasets_1.JOB_TITLES),
254
- company: () => (0, exports.pick)(datasets_1.COMPANIES),
255
- currency: () => (0, exports.pick)(datasets_1.CURRENCIES),
256
- status: () => (0, exports.pick)(datasets_1.STATUSES),
257
- creditCard: () => (0, exports.makeCreditCard)(),
258
- avatar: () => (0, exports.makeAvatar)(),
259
- color: (p) => (0, exports.makeColor)(p.format),
260
- phone: () => (0, exports.makePhone)(),
261
- city: () => (0, exports.pick)(datasets_1.CITIES),
262
- state: () => (0, exports.pick)(datasets_1.US_STATES),
263
- country: () => (0, exports.pick)(datasets_1.COUNTRIES),
264
- countryCode: () => (0, exports.pick)(datasets_1.COUNTRY_CODES),
265
- postal: () => String((0, exports.randInt)(10000, 99999)),
266
- address: () => (0, exports.makeAddress)(),
267
- url: () => `https://www.${(0, exports.pick)(datasets_1.DOMAINS)}/${(0, exports.makeSlug)()}`,
268
- domain: () => (0, exports.pick)(datasets_1.DOMAINS),
269
- ip: () => (0, exports.makeIp)(),
270
- mac: () => makeMacAddress(),
271
- uuid: () => makeUuid(),
272
- slug: () => (0, exports.makeSlug)(),
273
- paragraph: () => (0, exports.makeParagraph)(),
274
- time: () => (0, exports.randomTime)(),
275
- bool: () => (0, exports.pick)(['1', '0']),
276
- json: (p) => makeJsonObject(p.jsonKeys, p.jsonValues),
277
- words: (p) => makeWords(p.minLen ?? 1, p.maxLen ?? 4),
278
- sentence: (p) => makeSentences(p.minLen ?? 1, p.maxLen ?? 2),
279
- date: (p) => randomDate(p.from, p.to, 'date'),
280
- datetime: (p) => randomDate(p.from, p.to, 'datetime'),
281
- timestampUnix: (p) => randomUnixTimestamp(p.format, p.from, p.to),
282
- latitude: (p) => Number((0, exports.randFloat)(p.min ?? -90, p.max ?? 90).toFixed(6)),
283
- longitude: (p) => Number((0, exports.randFloat)(p.min ?? -180, p.max ?? 180).toFixed(6)),
284
- sequence: (p, idx) => (p.start ?? 1) + idx * (p.step ?? 1),
285
- int: (p) => (0, exports.randInt)(p.min ?? 1, Math.max(p.min ?? 1, p.max ?? 1000)),
286
- decimal: (p) => (0, exports.randFloat)(p.min ?? 1, Math.max(p.min ?? 1, p.max ?? 1000)).toFixed(Math.max(0, Math.min(10, p.precision ?? 2))),
287
- pattern: (p) => formatPattern(p.pattern || 'INV-2026-####'),
288
- fixed: (p) => p.value ?? '',
289
- token: (p) => randomAlnum(Math.max(2, p.minLen ?? 6), Math.max(2, p.maxLen ?? 12)),
290
- hash: (p) => {
291
- const len = (0, exports.randInt)(Math.max(8, p.minLen ?? 32), Math.max(8, p.maxLen ?? 64));
292
- return (0, node_crypto_1.randomBytes)(Math.ceil(len / 2)).toString('hex').slice(0, len);
293
- },
294
- };
295
- function generateOne(col, p, fkPool, rowIndex = 0) {
296
- const type = (col.type || '').toUpperCase();
297
- if (!col.pk && !col.notnull && p.nullPct && p.nullPct > 0) {
298
- if (Math.random() * 100 < p.nullPct)
299
- return null;
300
- }
301
- if (p.strategy === 'skip')
302
- return types_1.SKIP;
303
- if (p.strategy === 'null')
304
- return null;
305
- if (p.strategy === 'list') {
306
- const vals = (p.values ?? []).filter((v) => String(v).trim() !== '');
307
- if (!vals.length)
308
- return types_1.SKIP;
309
- return (0, common_1.coerceFormValue)(String((0, exports.pick)(vals)), type || 'TEXT');
310
- }
311
- if (p.strategy === 'fk') {
312
- return fkPool && fkPool.length ? fkPool[(0, exports.randInt)(0, fkPool.length - 1)] : null;
313
- }
314
- if (p.strategy === 'bytes') {
315
- const len = (0, exports.randInt)(Math.max(0, p.minLen ?? 8), Math.max(1, p.maxLen ?? 32));
316
- const buf = Buffer.alloc(len);
317
- for (let i = 0; i < len; i++)
318
- buf[i] = (0, exports.randInt)(0, 255);
319
- return (0, common_1.coerceFormValue)(`0x${buf.toString('hex')}`, type || 'BLOB');
320
- }
321
- const gen = BASIC_STRATEGIES[p.strategy];
322
- if (!gen)
323
- return types_1.SKIP;
324
- let rawVal = gen(p, rowIndex);
325
- if (rawVal != null && (p.prefix || p.suffix) && typeof rawVal === 'string') {
326
- rawVal = `${p.prefix || ''}${rawVal}${p.suffix || ''}`;
327
- }
328
- return (0, common_1.coerceFormValue)(String(rawVal), type || 'TEXT');
88
+ function generateOne(col, plan, fkPool, rowIndex = 0) {
89
+ return (0, registry_1.generateColumnValue)(col, plan, defaultPrng, rowIndex, {}, fkPool);
329
90
  }
330
91
  function generateSampleValue(col, plan, fkPool) {
331
92
  const v = generateOne(col, plan, fkPool || ['[FK sample]'], 0);
@@ -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[];};