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.
- package/README.md +368 -335
- package/dist/app.js +227 -1
- package/dist/auth/config.d.ts +1 -1
- package/dist/auth/config.js +109 -1
- package/dist/auth/crypto.js +98 -1
- package/dist/auth/index.js +21 -1
- package/dist/auth/middleware.js +78 -1
- package/dist/auth/routes.js +64 -1
- package/dist/auth/types.js +7 -1
- package/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.js +309 -1
- package/dist/cli/config.js +118 -1
- package/dist/cli/index.js +20 -1
- package/dist/cli/runner.js +208 -1
- package/dist/cli.js +4 -1
- package/dist/data/chain.d.ts +1 -0
- package/dist/data/chain.js +130 -0
- package/dist/data/datasets.d.ts +1 -1
- package/dist/data/datasets.js +171 -1
- package/dist/data/detector.d.ts +1 -1
- package/dist/data/detector.js +535 -1
- package/dist/data/engine.d.ts +1 -1
- package/dist/data/engine.js +586 -1
- package/dist/data/index.d.ts +1 -1
- package/dist/data/index.js +30 -1
- package/dist/data/prng.d.ts +1 -0
- package/dist/data/prng.js +73 -0
- package/dist/data/registry.d.ts +1 -0
- package/dist/data/registry.js +805 -0
- package/dist/data/schema-graph.d.ts +1 -0
- package/dist/data/schema-graph.js +210 -0
- package/dist/data/strategies.d.ts +1 -1
- package/dist/data/strategies.js +100 -1
- package/dist/data/templates.d.ts +1 -0
- package/dist/data/templates.js +134 -0
- package/dist/data/types.d.ts +1 -1
- package/dist/data/types.js +5 -1
- package/dist/data/validator.d.ts +1 -0
- package/dist/data/validator.js +160 -0
- package/dist/db/database.d.ts +1 -1
- package/dist/db/database.js +472 -1
- package/dist/db/export.d.ts +1 -1
- package/dist/db/export.js +67 -1
- package/dist/db/filters.d.ts +1 -1
- package/dist/db/filters.js +119 -1
- package/dist/db/index.js +24 -1
- package/dist/db/introspection.js +164 -1
- package/dist/db/manager.js +327 -1
- package/dist/db/migrations.js +100 -1
- package/dist/db/postgres.d.ts +1 -1
- package/dist/db/postgres.js +687 -1
- package/dist/db/types.d.ts +1 -1
- package/dist/db/types.js +6 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +52 -1
- package/dist/public/css/app.css +1 -1
- package/dist/public/js/api.js +1 -1
- package/dist/public/js/browse.js +1 -1
- package/dist/public/js/common.js +1 -0
- package/dist/public/js/databases.js +1 -1
- package/dist/public/js/designer.js +1 -1
- package/dist/public/js/forms.js +1 -1
- package/dist/public/js/home.js +1 -0
- package/dist/public/js/inspector.js +1 -1
- package/dist/public/js/query.js +1 -1
- package/dist/public/js/schema.js +1 -1
- package/dist/public/js/seed.js +1 -1
- package/dist/routes/api/erd.d.ts +1 -0
- package/dist/routes/api/erd.js +45 -0
- package/dist/routes/api/helpers.d.ts +1 -1
- package/dist/routes/api/helpers.js +213 -1
- package/dist/routes/api/import-export.js +82 -1
- package/dist/routes/api/index.js +48 -1
- package/dist/routes/api/query.js +74 -1
- package/dist/routes/api/rows.js +358 -1
- package/dist/routes/api/seed.js +167 -1
- package/dist/routes/api/tables.js +216 -1
- package/dist/routes/databases.js +210 -1
- package/dist/routes/index.js +19 -1
- package/dist/routes/pages.js +446 -1
- package/dist/serverless.js +149 -1
- package/dist/sql/classifier.js +24 -1
- package/dist/sql/error-analyzer.d.ts +1 -0
- package/dist/sql/error-analyzer.js +550 -0
- package/dist/sql/generator.d.ts +1 -1
- package/dist/sql/generator.js +245 -1
- package/dist/sql/index.js +18 -1
- package/dist/types/api.d.ts +1 -1
- package/dist/types/api.js +2 -1
- package/dist/utils/colors.js +54 -1
- package/dist/utils/common.d.ts +1 -1
- package/dist/utils/common.js +185 -1
- package/dist/utils/csv.d.ts +1 -1
- package/dist/utils/csv.js +61 -1
- package/dist/utils/datatype.js +167 -1
- package/dist/utils/icons.js +81 -1
- package/dist/utils/index.js +20 -1
- package/dist/utils/logger.js +36 -1
- package/dist/views/layouts/main.hbs +1 -74
- package/dist/views/pages/databases.hbs +1 -233
- package/dist/views/pages/designer.hbs +1 -89
- package/dist/views/pages/erd.hbs +1 -0
- package/dist/views/pages/error.hbs +1 -10
- package/dist/views/pages/form.hbs +1 -59
- package/dist/views/pages/home.hbs +1 -124
- package/dist/views/pages/info.hbs +1 -0
- package/dist/views/pages/login.hbs +1 -98
- package/dist/views/pages/query.hbs +1 -87
- package/dist/views/pages/schema.hbs +1 -298
- package/dist/views/pages/seed-select.hbs +251 -0
- package/dist/views/pages/seed.hbs +273 -167
- package/dist/views/pages/table.hbs +1 -327
- package/dist/views/partials/navbar.hbs +1 -83
- package/dist/views/partials/sidebar.hbs +1 -117
- package/docs/API.md +149 -144
- package/docs/EXAMPLES.md +579 -576
- package/docs/SECURITY.md +265 -265
- package/package.json +79 -78
- package/dist/public/js/icons.js +0 -1
|
@@ -0,0 +1,805 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GeneratorRegistry = void 0;
|
|
4
|
+
exports.makeSlug = makeSlug;
|
|
5
|
+
exports.makeEmail = makeEmail;
|
|
6
|
+
exports.makeUsername = makeUsername;
|
|
7
|
+
exports.makePhone = makePhone;
|
|
8
|
+
exports.makeAddress = makeAddress;
|
|
9
|
+
exports.makeIp = makeIp;
|
|
10
|
+
exports.makeMacAddress = makeMacAddress;
|
|
11
|
+
exports.makeCreditCard = makeCreditCard;
|
|
12
|
+
exports.makeAvatar = makeAvatar;
|
|
13
|
+
exports.makeColor = makeColor;
|
|
14
|
+
exports.makeUuid = makeUuid;
|
|
15
|
+
exports.makeWords = makeWords;
|
|
16
|
+
exports.makeSentences = makeSentences;
|
|
17
|
+
exports.makeParagraph = makeParagraph;
|
|
18
|
+
exports.formatPattern = formatPattern;
|
|
19
|
+
exports.randomDate = randomDate;
|
|
20
|
+
exports.randomTime = randomTime;
|
|
21
|
+
exports.randomUnixTimestamp = randomUnixTimestamp;
|
|
22
|
+
exports.randomAlnum = randomAlnum;
|
|
23
|
+
exports.makeJsonObject = makeJsonObject;
|
|
24
|
+
exports.isTextishType = isTextishType;
|
|
25
|
+
exports.generateColumnValue = generateColumnValue;
|
|
26
|
+
const node_crypto_1 = require("node:crypto");
|
|
27
|
+
const common_1 = require("../utils/common");
|
|
28
|
+
const datasets_1 = require("./datasets");
|
|
29
|
+
const prng_1 = require("./prng");
|
|
30
|
+
const types_1 = require("./types");
|
|
31
|
+
function makeSlug(prng) {
|
|
32
|
+
return `${prng.pick(datasets_1.LOREM_WORDS)}-${prng.pick(datasets_1.LOREM_WORDS)}-${prng.int(100, 999)}`;
|
|
33
|
+
}
|
|
34
|
+
function cleanHandle(s) {
|
|
35
|
+
return String(s)
|
|
36
|
+
.toLowerCase()
|
|
37
|
+
.trim()
|
|
38
|
+
.normalize('NFD')
|
|
39
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
40
|
+
.replace(/[^a-z0-9]+/g, '.')
|
|
41
|
+
.replace(/^\.+|\.+$/g, '');
|
|
42
|
+
}
|
|
43
|
+
function makeEmail(prng, idx, rowValues) {
|
|
44
|
+
let handle = '';
|
|
45
|
+
if (rowValues) {
|
|
46
|
+
const fn = rowValues.first_name || rowValues.firstname || rowValues.firstName;
|
|
47
|
+
const ln = rowValues.last_name || rowValues.lastname || rowValues.lastName;
|
|
48
|
+
if (fn && ln) {
|
|
49
|
+
handle = `${cleanHandle(String(fn))}.${cleanHandle(String(ln))}`;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
const name = rowValues.full_name || rowValues.fullname || rowValues.fullName || rowValues.name || rowValues.customer_name || rowValues.user_name || rowValues.username;
|
|
53
|
+
if (name) {
|
|
54
|
+
handle = cleanHandle(String(name));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!handle) {
|
|
59
|
+
handle = `${cleanHandle(prng.pick(datasets_1.FIRST_NAMES))}.${cleanHandle(prng.pick(datasets_1.LAST_NAMES))}`;
|
|
60
|
+
}
|
|
61
|
+
const suffix = idx != null && idx > 0 ? `${idx}` : '';
|
|
62
|
+
const domain = prng.pick(datasets_1.DOMAINS);
|
|
63
|
+
return `${handle}${suffix ? `.${suffix}` : ''}@${domain}`;
|
|
64
|
+
}
|
|
65
|
+
function makeUsername(prng, idx, rowValues) {
|
|
66
|
+
if (rowValues) {
|
|
67
|
+
const fn = rowValues.first_name || rowValues.firstname || rowValues.firstName;
|
|
68
|
+
const ln = rowValues.last_name || rowValues.lastname || rowValues.lastName;
|
|
69
|
+
if (fn && ln) {
|
|
70
|
+
return `${cleanHandle(String(fn))}${cleanHandle(String(ln))}${idx != null && idx > 0 ? idx : ''}`;
|
|
71
|
+
}
|
|
72
|
+
const name = rowValues.full_name || rowValues.fullname || rowValues.name || rowValues.customer_name;
|
|
73
|
+
if (name) {
|
|
74
|
+
return `${cleanHandle(String(name)).replace(/\./g, '')}${idx != null && idx > 0 ? idx : ''}`;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return `${prng.pick(datasets_1.FIRST_NAMES).toLowerCase()}${prng.pick(datasets_1.LAST_NAMES).toLowerCase()}${idx != null && idx > 0 ? idx : ''}${prng.int(10, 99)}`;
|
|
78
|
+
}
|
|
79
|
+
function makePhone(prng) {
|
|
80
|
+
return `+1 (${prng.int(200, 999)}) ${prng.int(200, 999)}-${String(prng.int(0, 9999)).padStart(4, '0')}`;
|
|
81
|
+
}
|
|
82
|
+
function makeAddress(prng) {
|
|
83
|
+
return `${prng.int(10, 9999)} ${prng.pick(datasets_1.LOREM_WORDS)} ${prng.pick(['St', 'Ave', 'Rd', 'Blvd', 'Ln', 'Dr'])}, ${prng.pick(datasets_1.CITIES)}`;
|
|
84
|
+
}
|
|
85
|
+
function makeIp(prng) {
|
|
86
|
+
return `${prng.int(1, 223)}.${prng.int(0, 255)}.${prng.int(0, 255)}.${prng.int(1, 254)}`;
|
|
87
|
+
}
|
|
88
|
+
function makeMacAddress(prng) {
|
|
89
|
+
const b = () => prng.int(0, 255).toString(16).padStart(2, '0').toUpperCase();
|
|
90
|
+
return `${b()}:${b()}:${b()}:${b()}:${b()}:${b()}`;
|
|
91
|
+
}
|
|
92
|
+
function makeCreditCard(prng) {
|
|
93
|
+
return `4532-${prng.int(1000, 9999)}-${prng.int(1000, 9999)}-${prng.int(1000, 9999)}`;
|
|
94
|
+
}
|
|
95
|
+
function makeAvatar(prng, seedVal) {
|
|
96
|
+
return `https://api.dicebear.com/7.x/avataaars/svg?seed=${encodeURIComponent(seedVal || makeUsername(prng))}`;
|
|
97
|
+
}
|
|
98
|
+
function makeColor(prng, format = 'hex') {
|
|
99
|
+
return format === 'name' ? prng.pick(datasets_1.NAMED_COLORS) : prng.pick(datasets_1.HEX_COLORS);
|
|
100
|
+
}
|
|
101
|
+
function makeUuid(prng) {
|
|
102
|
+
const h = (n) => prng.int(0, 0xffffffff).toString(16).padStart(n, '0');
|
|
103
|
+
return `${h(8)}-${h(4).slice(0, 4)}-4${h(3).slice(0, 3)}-${['8', '9', 'a', 'b'][prng.int(0, 3)]}${h(3).slice(0, 3)}-${h(12).slice(0, 12)}`;
|
|
104
|
+
}
|
|
105
|
+
function makeWords(prng, minLen, maxLen) {
|
|
106
|
+
const n = prng.int(minLen, maxLen);
|
|
107
|
+
const parts = [];
|
|
108
|
+
for (let i = 0; i < n; i++)
|
|
109
|
+
parts.push(prng.pick(datasets_1.LOREM_WORDS));
|
|
110
|
+
return parts.join(' ');
|
|
111
|
+
}
|
|
112
|
+
function makeSentences(prng, minLen, maxLen) {
|
|
113
|
+
const n = prng.int(minLen, maxLen);
|
|
114
|
+
const sentences = [];
|
|
115
|
+
for (let i = 0; i < n; i++) {
|
|
116
|
+
const words = makeWords(prng, 6, 14).split(' ');
|
|
117
|
+
words[0] = words[0].charAt(0).toUpperCase() + words[0].slice(1);
|
|
118
|
+
sentences.push(words.join(' ') + '.');
|
|
119
|
+
}
|
|
120
|
+
return sentences.join(' ');
|
|
121
|
+
}
|
|
122
|
+
function makeParagraph(prng) {
|
|
123
|
+
return makeSentences(prng, 3, 5);
|
|
124
|
+
}
|
|
125
|
+
function formatPattern(pattern, prng) {
|
|
126
|
+
if (!pattern)
|
|
127
|
+
return '';
|
|
128
|
+
const now = new Date();
|
|
129
|
+
const year = String(now.getFullYear());
|
|
130
|
+
const s = pattern
|
|
131
|
+
.replace(/\{YYYY\}/g, year)
|
|
132
|
+
.replace(/\{YY\}/g, year.slice(-2))
|
|
133
|
+
.replace(/\{MM\}/g, String(prng.int(1, 12)).padStart(2, '0'))
|
|
134
|
+
.replace(/\{DD\}/g, String(prng.int(1, 28)).padStart(2, '0'));
|
|
135
|
+
const upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
136
|
+
const lower = 'abcdefghijklmnopqrstuvwxyz';
|
|
137
|
+
let res = '';
|
|
138
|
+
for (let i = 0; i < s.length; i++) {
|
|
139
|
+
const ch = s[i];
|
|
140
|
+
if (ch === '#')
|
|
141
|
+
res += prng.int(0, 9);
|
|
142
|
+
else if (ch === 'A' || ch === '?')
|
|
143
|
+
res += upper[prng.int(0, 25)];
|
|
144
|
+
else if (ch === 'a')
|
|
145
|
+
res += lower[prng.int(0, 25)];
|
|
146
|
+
else
|
|
147
|
+
res += ch;
|
|
148
|
+
}
|
|
149
|
+
return res;
|
|
150
|
+
}
|
|
151
|
+
function randomDate(prng, from, to, kind = 'date') {
|
|
152
|
+
const now = Date.now();
|
|
153
|
+
const lo = from ? Date.parse(from) : now - 730 * 86400_000;
|
|
154
|
+
const hi = to ? Date.parse(to) : now;
|
|
155
|
+
const minT = Number.isFinite(lo) ? lo : now - 730 * 86400_000;
|
|
156
|
+
const maxT = Number.isFinite(hi) ? hi : now;
|
|
157
|
+
const d = new Date(prng.int(Math.min(minT, maxT), Math.max(minT, maxT)));
|
|
158
|
+
if (kind === 'datetime')
|
|
159
|
+
return d.toISOString();
|
|
160
|
+
const p = (n) => String(n).padStart(2, '0');
|
|
161
|
+
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`;
|
|
162
|
+
}
|
|
163
|
+
function randomTime(prng) {
|
|
164
|
+
return `${String(prng.int(0, 23)).padStart(2, '0')}:${String(prng.int(0, 59)).padStart(2, '0')}:${String(prng.int(0, 59)).padStart(2, '0')}`;
|
|
165
|
+
}
|
|
166
|
+
function randomUnixTimestamp(prng, format = 'sec', from, to) {
|
|
167
|
+
const now = Date.now();
|
|
168
|
+
const lo = from ? Date.parse(from) : now - 730 * 86400_000;
|
|
169
|
+
const hi = to ? Date.parse(to) : now;
|
|
170
|
+
const minT = Number.isFinite(lo) ? lo : now - 730 * 86400_000;
|
|
171
|
+
const maxT = Number.isFinite(hi) ? hi : now;
|
|
172
|
+
const t = prng.int(Math.min(minT, maxT), Math.max(minT, maxT));
|
|
173
|
+
return format === 'ms' ? t : Math.floor(t / 1000);
|
|
174
|
+
}
|
|
175
|
+
const ALNUM = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
176
|
+
function randomAlnum(prng, minLen, maxLen) {
|
|
177
|
+
const len = prng.int(minLen, maxLen);
|
|
178
|
+
let s = '';
|
|
179
|
+
for (let i = 0; i < len; i++)
|
|
180
|
+
s += ALNUM[prng.int(0, ALNUM.length - 1)];
|
|
181
|
+
return s;
|
|
182
|
+
}
|
|
183
|
+
const JSON_ROLES = ['admin', 'editor', 'viewer', 'billing', 'member', 'owner', 'guest'];
|
|
184
|
+
const JSON_APPS = ['dashboard', 'api', 'mobile', 'cli', 'webhook', 'cron', 'importer'];
|
|
185
|
+
function jsonValueForKey(prng, key) {
|
|
186
|
+
const lk = key.toLowerCase();
|
|
187
|
+
if (lk.includes('role'))
|
|
188
|
+
return prng.pick(JSON_ROLES);
|
|
189
|
+
if (lk.includes('app'))
|
|
190
|
+
return [prng.pick(JSON_APPS)];
|
|
191
|
+
const roll = prng.next();
|
|
192
|
+
if (roll < 0.45)
|
|
193
|
+
return prng.pick(['alpha', 'beta', 'stable', 'draft', 'on', 'off', 'default', 'custom']);
|
|
194
|
+
if (roll < 0.6)
|
|
195
|
+
return prng.int(0, 1000);
|
|
196
|
+
if (roll < 0.75)
|
|
197
|
+
return prng.chance(50);
|
|
198
|
+
if (roll < 0.9)
|
|
199
|
+
return null;
|
|
200
|
+
return makeUuid(prng);
|
|
201
|
+
}
|
|
202
|
+
function makeJsonObject(prng, keys, jsonValues) {
|
|
203
|
+
const k = (keys && keys.length ? keys : ['scope', 'enabled', 'flags']).slice(0, 16);
|
|
204
|
+
const obj = {};
|
|
205
|
+
for (const key of k) {
|
|
206
|
+
const allowed = jsonValues ? jsonValues[key] : undefined;
|
|
207
|
+
obj[key] = allowed && allowed.length ? prng.pick(allowed) : jsonValueForKey(prng, key);
|
|
208
|
+
}
|
|
209
|
+
return JSON.stringify(obj);
|
|
210
|
+
}
|
|
211
|
+
function isTextishType(type) {
|
|
212
|
+
const t = (type || '').toUpperCase();
|
|
213
|
+
return t === 'TEXT' || t === '' || t.includes('JSON') || t.includes('CHAR') || t.includes('CLOB') || t.includes('VARCHAR');
|
|
214
|
+
}
|
|
215
|
+
class GeneratorRegistryClass {
|
|
216
|
+
registry = new Map();
|
|
217
|
+
constructor() {
|
|
218
|
+
this.registerDefaults();
|
|
219
|
+
}
|
|
220
|
+
register(def) {
|
|
221
|
+
this.registry.set(def.id, def);
|
|
222
|
+
}
|
|
223
|
+
get(id) {
|
|
224
|
+
return this.registry.get(id);
|
|
225
|
+
}
|
|
226
|
+
getAll() {
|
|
227
|
+
return Array.from(this.registry.values());
|
|
228
|
+
}
|
|
229
|
+
getDescriptorsFor(col, fk) {
|
|
230
|
+
const type = (col.type || '').toUpperCase();
|
|
231
|
+
const isText = isTextishType(type);
|
|
232
|
+
const isNum = type.startsWith('INTEGER') || type === 'REAL' || type.includes('DECIMAL') || type.includes('NUMERIC') || isText;
|
|
233
|
+
const isBlob = type === 'BLOB' || type === 'BYTEA' || isText;
|
|
234
|
+
const isDate = type.includes('DATE') || type.includes('TIME') || isText;
|
|
235
|
+
const out = [];
|
|
236
|
+
if (fk) {
|
|
237
|
+
out.push({
|
|
238
|
+
id: 'fk',
|
|
239
|
+
label: `Foreign Key: Sample from ${fk.table}${fk.to ? `.${fk.to}` : ''}`,
|
|
240
|
+
category: 'relational',
|
|
241
|
+
description: `Assigns valid parent IDs from ${fk.table}`,
|
|
242
|
+
});
|
|
243
|
+
out.push({
|
|
244
|
+
id: 'sequentialFk',
|
|
245
|
+
label: `Sequential FK: Ordered match from ${fk.table}`,
|
|
246
|
+
category: 'relational',
|
|
247
|
+
description: `Pairs rows 1-to-1 or evenly across ${fk.table}`,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
for (const def of this.registry.values()) {
|
|
251
|
+
if (def.id === 'fk' || def.id === 'sequentialFk')
|
|
252
|
+
continue;
|
|
253
|
+
const target = def.targetTypes;
|
|
254
|
+
if (target === 'all' ||
|
|
255
|
+
(target === 'text' && isText) ||
|
|
256
|
+
(target === 'numeric' && isNum) ||
|
|
257
|
+
(target === 'blob' && isBlob) ||
|
|
258
|
+
(target === 'date' && isDate)) {
|
|
259
|
+
out.push({
|
|
260
|
+
id: def.id,
|
|
261
|
+
label: def.label,
|
|
262
|
+
category: def.category,
|
|
263
|
+
description: def.description,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return out;
|
|
268
|
+
}
|
|
269
|
+
registerDefaults() {
|
|
270
|
+
this.register({
|
|
271
|
+
id: 'skip',
|
|
272
|
+
label: 'Database Default / Auto Increment',
|
|
273
|
+
category: 'database_aware',
|
|
274
|
+
description: 'Omit from INSERT to let database default or autoincrement apply',
|
|
275
|
+
targetTypes: 'all',
|
|
276
|
+
generate: () => types_1.SKIP,
|
|
277
|
+
});
|
|
278
|
+
this.register({
|
|
279
|
+
id: 'null',
|
|
280
|
+
label: 'NULL',
|
|
281
|
+
category: 'custom_control',
|
|
282
|
+
description: 'Set NULL explicitly',
|
|
283
|
+
targetTypes: 'all',
|
|
284
|
+
generate: () => null,
|
|
285
|
+
});
|
|
286
|
+
this.register({
|
|
287
|
+
id: 'fixed',
|
|
288
|
+
label: 'Fixed Value',
|
|
289
|
+
category: 'custom_control',
|
|
290
|
+
description: 'Static constant value for all rows',
|
|
291
|
+
targetTypes: 'all',
|
|
292
|
+
generate: (ctx) => ctx.plan.value ?? '',
|
|
293
|
+
});
|
|
294
|
+
this.register({
|
|
295
|
+
id: 'list',
|
|
296
|
+
label: 'Custom List / Enum',
|
|
297
|
+
category: 'custom_control',
|
|
298
|
+
description: 'Pick randomly from a list of predefined choices',
|
|
299
|
+
targetTypes: 'all',
|
|
300
|
+
generate: (ctx) => {
|
|
301
|
+
const vals = (ctx.plan.values ?? []).filter((v) => String(v).trim() !== '');
|
|
302
|
+
return vals.length ? ctx.prng.pick(vals) : types_1.SKIP;
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
this.register({
|
|
306
|
+
id: 'pattern',
|
|
307
|
+
label: 'Pattern Template (e.g. INV-####)',
|
|
308
|
+
category: 'custom_control',
|
|
309
|
+
description: 'Formatted token pattern (#=digit, A=letter, {YYYY}=year)',
|
|
310
|
+
targetTypes: 'all',
|
|
311
|
+
generate: (ctx) => formatPattern(ctx.plan.pattern || 'INV-2026-####', ctx.prng),
|
|
312
|
+
});
|
|
313
|
+
this.register({
|
|
314
|
+
id: 'template',
|
|
315
|
+
label: 'Column Template Expression',
|
|
316
|
+
category: 'custom_control',
|
|
317
|
+
description: 'Compose from other columns: {{first_name}}.{{last_name}}@domain.com',
|
|
318
|
+
targetTypes: 'text',
|
|
319
|
+
generate: (ctx) => ctx.plan.template || '',
|
|
320
|
+
});
|
|
321
|
+
this.register({
|
|
322
|
+
id: 'first',
|
|
323
|
+
label: 'First Name',
|
|
324
|
+
category: 'identity',
|
|
325
|
+
description: 'Realistic first names',
|
|
326
|
+
targetTypes: 'text',
|
|
327
|
+
generate: (ctx) => ctx.prng.pick(datasets_1.FIRST_NAMES),
|
|
328
|
+
});
|
|
329
|
+
this.register({
|
|
330
|
+
id: 'last',
|
|
331
|
+
label: 'Last Name / Surname',
|
|
332
|
+
category: 'identity',
|
|
333
|
+
description: 'Realistic surnames',
|
|
334
|
+
targetTypes: 'text',
|
|
335
|
+
generate: (ctx) => ctx.prng.pick(datasets_1.LAST_NAMES),
|
|
336
|
+
});
|
|
337
|
+
this.register({
|
|
338
|
+
id: 'fullname',
|
|
339
|
+
label: 'Full Name',
|
|
340
|
+
category: 'identity',
|
|
341
|
+
description: 'First and last name combined',
|
|
342
|
+
targetTypes: 'text',
|
|
343
|
+
generate: (ctx) => `${ctx.prng.pick(datasets_1.FIRST_NAMES)} ${ctx.prng.pick(datasets_1.LAST_NAMES)}`,
|
|
344
|
+
});
|
|
345
|
+
this.register({
|
|
346
|
+
id: 'username',
|
|
347
|
+
label: 'Username',
|
|
348
|
+
category: 'identity',
|
|
349
|
+
description: 'User handle or login name',
|
|
350
|
+
targetTypes: 'text',
|
|
351
|
+
generate: (ctx) => makeUsername(ctx.prng, ctx.rowIndex, ctx.rowValues),
|
|
352
|
+
});
|
|
353
|
+
this.register({
|
|
354
|
+
id: 'job',
|
|
355
|
+
label: 'Job Title / Profession',
|
|
356
|
+
category: 'identity',
|
|
357
|
+
description: 'Job titles and professions',
|
|
358
|
+
targetTypes: 'text',
|
|
359
|
+
generate: (ctx) => ctx.prng.pick(datasets_1.JOB_TITLES),
|
|
360
|
+
});
|
|
361
|
+
this.register({
|
|
362
|
+
id: 'email',
|
|
363
|
+
label: 'Email Address',
|
|
364
|
+
category: 'contact_web',
|
|
365
|
+
description: 'Realistic email address',
|
|
366
|
+
targetTypes: 'text',
|
|
367
|
+
generate: (ctx) => makeEmail(ctx.prng, ctx.rowIndex, ctx.rowValues),
|
|
368
|
+
});
|
|
369
|
+
this.register({
|
|
370
|
+
id: 'phone',
|
|
371
|
+
label: 'Phone Number',
|
|
372
|
+
category: 'contact_web',
|
|
373
|
+
description: 'Standard phone numbers',
|
|
374
|
+
targetTypes: 'text',
|
|
375
|
+
generate: (ctx) => makePhone(ctx.prng),
|
|
376
|
+
});
|
|
377
|
+
this.register({
|
|
378
|
+
id: 'url',
|
|
379
|
+
label: 'Website URL',
|
|
380
|
+
category: 'contact_web',
|
|
381
|
+
description: 'Valid website URL with slug',
|
|
382
|
+
targetTypes: 'text',
|
|
383
|
+
generate: (ctx) => `https://www.${ctx.prng.pick(datasets_1.DOMAINS)}/${makeSlug(ctx.prng)}`,
|
|
384
|
+
});
|
|
385
|
+
this.register({
|
|
386
|
+
id: 'domain',
|
|
387
|
+
label: 'Domain Name',
|
|
388
|
+
category: 'contact_web',
|
|
389
|
+
description: 'Internet domain name',
|
|
390
|
+
targetTypes: 'text',
|
|
391
|
+
generate: (ctx) => ctx.prng.pick(datasets_1.DOMAINS),
|
|
392
|
+
});
|
|
393
|
+
this.register({
|
|
394
|
+
id: 'ip',
|
|
395
|
+
label: 'IPv4 Address',
|
|
396
|
+
category: 'contact_web',
|
|
397
|
+
description: 'IPv4 address',
|
|
398
|
+
targetTypes: 'text',
|
|
399
|
+
generate: (ctx) => makeIp(ctx.prng),
|
|
400
|
+
});
|
|
401
|
+
this.register({
|
|
402
|
+
id: 'avatar',
|
|
403
|
+
label: 'Avatar URL',
|
|
404
|
+
category: 'contact_web',
|
|
405
|
+
description: 'Avatar image URL',
|
|
406
|
+
targetTypes: 'text',
|
|
407
|
+
generate: (ctx) => makeAvatar(ctx.prng),
|
|
408
|
+
});
|
|
409
|
+
this.register({
|
|
410
|
+
id: 'company',
|
|
411
|
+
label: 'Company Name',
|
|
412
|
+
category: 'commerce',
|
|
413
|
+
description: 'Organization or enterprise name',
|
|
414
|
+
targetTypes: 'text',
|
|
415
|
+
generate: (ctx) => ctx.prng.pick(datasets_1.COMPANIES),
|
|
416
|
+
});
|
|
417
|
+
this.register({
|
|
418
|
+
id: 'currency',
|
|
419
|
+
label: 'Currency Code (ISO-3)',
|
|
420
|
+
category: 'commerce',
|
|
421
|
+
description: 'ISO-4217 3-letter currency code (USD, EUR...)',
|
|
422
|
+
targetTypes: 'text',
|
|
423
|
+
generate: (ctx) => ctx.prng.pick(datasets_1.CURRENCIES),
|
|
424
|
+
});
|
|
425
|
+
this.register({
|
|
426
|
+
id: 'status',
|
|
427
|
+
label: 'Status (active, pending...)',
|
|
428
|
+
category: 'commerce',
|
|
429
|
+
description: 'Workflow or record status',
|
|
430
|
+
targetTypes: 'text',
|
|
431
|
+
generate: (ctx) => ctx.prng.pick(datasets_1.STATUSES),
|
|
432
|
+
});
|
|
433
|
+
this.register({
|
|
434
|
+
id: 'creditCard',
|
|
435
|
+
label: 'Credit Card (Masked Test)',
|
|
436
|
+
category: 'commerce',
|
|
437
|
+
description: 'Formatted masked test payment card',
|
|
438
|
+
targetTypes: 'text',
|
|
439
|
+
generate: (ctx) => makeCreditCard(ctx.prng),
|
|
440
|
+
});
|
|
441
|
+
this.register({
|
|
442
|
+
id: 'address',
|
|
443
|
+
label: 'Street Address',
|
|
444
|
+
category: 'location',
|
|
445
|
+
description: 'Street address with city',
|
|
446
|
+
targetTypes: 'text',
|
|
447
|
+
generate: (ctx) => makeAddress(ctx.prng),
|
|
448
|
+
});
|
|
449
|
+
this.register({
|
|
450
|
+
id: 'city',
|
|
451
|
+
label: 'City',
|
|
452
|
+
category: 'location',
|
|
453
|
+
description: 'City name',
|
|
454
|
+
targetTypes: 'text',
|
|
455
|
+
generate: (ctx) => ctx.prng.pick(datasets_1.CITIES),
|
|
456
|
+
});
|
|
457
|
+
this.register({
|
|
458
|
+
id: 'state',
|
|
459
|
+
label: 'State / Province',
|
|
460
|
+
category: 'location',
|
|
461
|
+
description: 'State / province name',
|
|
462
|
+
targetTypes: 'text',
|
|
463
|
+
generate: (ctx) => ctx.prng.pick(datasets_1.US_STATES),
|
|
464
|
+
});
|
|
465
|
+
this.register({
|
|
466
|
+
id: 'country',
|
|
467
|
+
label: 'Country Name',
|
|
468
|
+
category: 'location',
|
|
469
|
+
description: 'Full country name',
|
|
470
|
+
targetTypes: 'text',
|
|
471
|
+
generate: (ctx) => ctx.prng.pick(datasets_1.COUNTRIES),
|
|
472
|
+
});
|
|
473
|
+
this.register({
|
|
474
|
+
id: 'countryCode',
|
|
475
|
+
label: 'Country Code (ISO-2)',
|
|
476
|
+
category: 'location',
|
|
477
|
+
description: '2-letter country code (US, GB, DE...)',
|
|
478
|
+
targetTypes: 'text',
|
|
479
|
+
generate: (ctx) => ctx.prng.pick(datasets_1.COUNTRY_CODES),
|
|
480
|
+
});
|
|
481
|
+
this.register({
|
|
482
|
+
id: 'postal',
|
|
483
|
+
label: 'Postal / Zip Code',
|
|
484
|
+
category: 'location',
|
|
485
|
+
description: '5-digit postal code',
|
|
486
|
+
targetTypes: 'text',
|
|
487
|
+
generate: (ctx) => String(ctx.prng.int(10000, 99999)),
|
|
488
|
+
});
|
|
489
|
+
this.register({
|
|
490
|
+
id: 'latitude',
|
|
491
|
+
label: 'Latitude',
|
|
492
|
+
category: 'location',
|
|
493
|
+
description: 'Geographic latitude (-90 to 90)',
|
|
494
|
+
targetTypes: 'all',
|
|
495
|
+
generate: (ctx) => Number(ctx.prng.float(ctx.plan.min ?? -90, ctx.plan.max ?? 90).toFixed(6)),
|
|
496
|
+
});
|
|
497
|
+
this.register({
|
|
498
|
+
id: 'longitude',
|
|
499
|
+
label: 'Longitude',
|
|
500
|
+
category: 'location',
|
|
501
|
+
description: 'Geographic longitude (-180 to 180)',
|
|
502
|
+
targetTypes: 'all',
|
|
503
|
+
generate: (ctx) => Number(ctx.prng.float(ctx.plan.min ?? -180, ctx.plan.max ?? 180).toFixed(6)),
|
|
504
|
+
});
|
|
505
|
+
this.register({
|
|
506
|
+
id: 'int',
|
|
507
|
+
label: 'Integer in Range',
|
|
508
|
+
category: 'numeric',
|
|
509
|
+
description: 'Random integer between min and max',
|
|
510
|
+
targetTypes: 'numeric',
|
|
511
|
+
generate: (ctx) => ctx.prng.int(ctx.plan.min ?? 1, Math.max(ctx.plan.min ?? 1, ctx.plan.max ?? 1000)),
|
|
512
|
+
});
|
|
513
|
+
this.register({
|
|
514
|
+
id: 'decimal',
|
|
515
|
+
label: 'Decimal / Price',
|
|
516
|
+
category: 'numeric',
|
|
517
|
+
description: 'Floating point or monetary number with precision',
|
|
518
|
+
targetTypes: 'numeric',
|
|
519
|
+
generate: (ctx) => ctx.prng.float(ctx.plan.min ?? 1, Math.max(ctx.plan.min ?? 1, ctx.plan.max ?? 1000)).toFixed(Math.max(0, Math.min(10, ctx.plan.precision ?? 2))),
|
|
520
|
+
});
|
|
521
|
+
this.register({
|
|
522
|
+
id: 'sequence',
|
|
523
|
+
label: 'Sequence Increment',
|
|
524
|
+
category: 'numeric',
|
|
525
|
+
description: 'Incremental counter: start + (row * step)',
|
|
526
|
+
targetTypes: 'numeric',
|
|
527
|
+
generate: (ctx) => (ctx.plan.start ?? 1) + ctx.rowIndex * (ctx.plan.step ?? 1),
|
|
528
|
+
});
|
|
529
|
+
this.register({
|
|
530
|
+
id: 'percentage',
|
|
531
|
+
label: 'Percentage (0-100)',
|
|
532
|
+
category: 'numeric',
|
|
533
|
+
description: 'Percentage ratio (0 to 100)',
|
|
534
|
+
targetTypes: 'numeric',
|
|
535
|
+
generate: (ctx) => ctx.prng.int(ctx.plan.min ?? 0, ctx.plan.max ?? 100),
|
|
536
|
+
});
|
|
537
|
+
this.register({
|
|
538
|
+
id: 'date',
|
|
539
|
+
label: 'Date (YYYY-MM-DD)',
|
|
540
|
+
category: 'datetime',
|
|
541
|
+
description: 'Calendar date in ISO YYYY-MM-DD format',
|
|
542
|
+
targetTypes: 'all',
|
|
543
|
+
generate: (ctx) => randomDate(ctx.prng, ctx.plan.from, ctx.plan.to, 'date'),
|
|
544
|
+
});
|
|
545
|
+
this.register({
|
|
546
|
+
id: 'datetime',
|
|
547
|
+
label: 'Datetime (ISO-8601)',
|
|
548
|
+
category: 'datetime',
|
|
549
|
+
description: 'Full timestamp with time',
|
|
550
|
+
targetTypes: 'all',
|
|
551
|
+
generate: (ctx) => randomDate(ctx.prng, ctx.plan.from, ctx.plan.to, 'datetime'),
|
|
552
|
+
});
|
|
553
|
+
this.register({
|
|
554
|
+
id: 'time',
|
|
555
|
+
label: 'Time (HH:MM:SS)',
|
|
556
|
+
category: 'datetime',
|
|
557
|
+
description: 'Time of day in HH:MM:SS format',
|
|
558
|
+
targetTypes: 'all',
|
|
559
|
+
generate: (ctx) => randomTime(ctx.prng),
|
|
560
|
+
});
|
|
561
|
+
this.register({
|
|
562
|
+
id: 'timestampUnix',
|
|
563
|
+
label: 'Unix Timestamp',
|
|
564
|
+
category: 'datetime',
|
|
565
|
+
description: 'Epoch seconds or milliseconds',
|
|
566
|
+
targetTypes: 'all',
|
|
567
|
+
generate: (ctx) => randomUnixTimestamp(ctx.prng, ctx.plan.format, ctx.plan.from, ctx.plan.to),
|
|
568
|
+
});
|
|
569
|
+
this.register({
|
|
570
|
+
id: 'relativeDate',
|
|
571
|
+
label: 'Recent / Future Date',
|
|
572
|
+
category: 'datetime',
|
|
573
|
+
description: 'Relative date within recent days/months',
|
|
574
|
+
targetTypes: 'all',
|
|
575
|
+
generate: (ctx) => randomDate(ctx.prng, ctx.plan.from, ctx.plan.to, 'datetime'),
|
|
576
|
+
});
|
|
577
|
+
this.register({
|
|
578
|
+
id: 'words',
|
|
579
|
+
label: 'Short Words / Tags',
|
|
580
|
+
category: 'system_crypto',
|
|
581
|
+
description: 'Space-delimited keywords or tags',
|
|
582
|
+
targetTypes: 'text',
|
|
583
|
+
generate: (ctx) => makeWords(ctx.prng, ctx.plan.minLen ?? 1, ctx.plan.maxLen ?? 4),
|
|
584
|
+
});
|
|
585
|
+
this.register({
|
|
586
|
+
id: 'sentence',
|
|
587
|
+
label: 'Sentences',
|
|
588
|
+
category: 'system_crypto',
|
|
589
|
+
description: 'Natural reading sentence',
|
|
590
|
+
targetTypes: 'text',
|
|
591
|
+
generate: (ctx) => makeSentences(ctx.prng, ctx.plan.minLen ?? 1, ctx.plan.maxLen ?? 2),
|
|
592
|
+
});
|
|
593
|
+
this.register({
|
|
594
|
+
id: 'paragraph',
|
|
595
|
+
label: 'Paragraph',
|
|
596
|
+
category: 'system_crypto',
|
|
597
|
+
description: 'Multi-sentence prose text block',
|
|
598
|
+
targetTypes: 'text',
|
|
599
|
+
generate: (ctx) => makeParagraph(ctx.prng),
|
|
600
|
+
});
|
|
601
|
+
this.register({
|
|
602
|
+
id: 'slug',
|
|
603
|
+
label: 'URL Slug',
|
|
604
|
+
category: 'system_crypto',
|
|
605
|
+
description: 'Kebab-case URL identifier',
|
|
606
|
+
targetTypes: 'text',
|
|
607
|
+
generate: (ctx) => makeSlug(ctx.prng),
|
|
608
|
+
});
|
|
609
|
+
this.register({
|
|
610
|
+
id: 'bool',
|
|
611
|
+
label: 'Boolean Flag',
|
|
612
|
+
category: 'system_crypto',
|
|
613
|
+
description: 'Boolean flag 1/0 or true/false',
|
|
614
|
+
targetTypes: 'all',
|
|
615
|
+
generate: (ctx) => (ctx.prng.chance(50) ? '1' : '0'),
|
|
616
|
+
});
|
|
617
|
+
this.register({
|
|
618
|
+
id: 'uuid',
|
|
619
|
+
label: 'UUID v4',
|
|
620
|
+
category: 'system_crypto',
|
|
621
|
+
description: 'Standard RFC-4122 v4 UUID',
|
|
622
|
+
targetTypes: 'text',
|
|
623
|
+
generate: (ctx) => makeUuid(ctx.prng),
|
|
624
|
+
});
|
|
625
|
+
this.register({
|
|
626
|
+
id: 'bytes',
|
|
627
|
+
label: 'Hex Bytes / Binary BLOB',
|
|
628
|
+
category: 'system_crypto',
|
|
629
|
+
description: 'Binary data / BLOB',
|
|
630
|
+
targetTypes: 'blob',
|
|
631
|
+
generate: (ctx) => {
|
|
632
|
+
const len = ctx.prng.int(Math.max(0, ctx.plan.minLen ?? 8), Math.max(1, ctx.plan.maxLen ?? 32));
|
|
633
|
+
const buf = Buffer.alloc(len);
|
|
634
|
+
for (let i = 0; i < len; i++)
|
|
635
|
+
buf[i] = ctx.prng.int(0, 255);
|
|
636
|
+
return `0x${buf.toString('hex')}`;
|
|
637
|
+
},
|
|
638
|
+
});
|
|
639
|
+
this.register({
|
|
640
|
+
id: 'token',
|
|
641
|
+
label: 'Alphanumeric Token',
|
|
642
|
+
category: 'system_crypto',
|
|
643
|
+
description: 'Short random alphanumeric code',
|
|
644
|
+
targetTypes: 'text',
|
|
645
|
+
generate: (ctx) => randomAlnum(ctx.prng, Math.max(2, ctx.plan.minLen ?? 6), Math.max(2, ctx.plan.maxLen ?? 12)),
|
|
646
|
+
});
|
|
647
|
+
this.register({
|
|
648
|
+
id: 'hash',
|
|
649
|
+
label: 'Hex Hash (MD5 / SHA)',
|
|
650
|
+
category: 'system_crypto',
|
|
651
|
+
description: 'Hexadecimal hash digest',
|
|
652
|
+
targetTypes: 'text',
|
|
653
|
+
generate: (ctx) => {
|
|
654
|
+
const len = ctx.prng.int(Math.max(8, ctx.plan.minLen ?? 32), Math.max(8, ctx.plan.maxLen ?? 64));
|
|
655
|
+
return (0, node_crypto_1.randomBytes)(Math.ceil(len / 2)).toString('hex').slice(0, len);
|
|
656
|
+
},
|
|
657
|
+
});
|
|
658
|
+
this.register({
|
|
659
|
+
id: 'json',
|
|
660
|
+
label: 'Valid JSON Object',
|
|
661
|
+
category: 'system_crypto',
|
|
662
|
+
description: 'Structured JSON document',
|
|
663
|
+
targetTypes: 'text',
|
|
664
|
+
generate: (ctx) => makeJsonObject(ctx.prng, ctx.plan.jsonKeys, ctx.plan.jsonValues),
|
|
665
|
+
});
|
|
666
|
+
this.register({
|
|
667
|
+
id: 'color',
|
|
668
|
+
label: 'Color (Hex or Name)',
|
|
669
|
+
category: 'system_crypto',
|
|
670
|
+
description: 'Color hex code (#rrggbb) or CSS name',
|
|
671
|
+
targetTypes: 'text',
|
|
672
|
+
generate: (ctx) => makeColor(ctx.prng, ctx.plan.format),
|
|
673
|
+
});
|
|
674
|
+
this.register({
|
|
675
|
+
id: 'mac',
|
|
676
|
+
label: 'MAC Network Address',
|
|
677
|
+
category: 'system_crypto',
|
|
678
|
+
description: 'Hardware ethernet MAC address',
|
|
679
|
+
targetTypes: 'text',
|
|
680
|
+
generate: (ctx) => makeMacAddress(ctx.prng),
|
|
681
|
+
});
|
|
682
|
+
this.register({
|
|
683
|
+
id: 'product',
|
|
684
|
+
label: 'Product Title',
|
|
685
|
+
category: 'commerce',
|
|
686
|
+
description: 'Realistic e-commerce product titles',
|
|
687
|
+
targetTypes: 'text',
|
|
688
|
+
generate: (ctx) => ctx.prng.pick(datasets_1.PRODUCT_NAMES),
|
|
689
|
+
});
|
|
690
|
+
this.register({
|
|
691
|
+
id: 'productCategory',
|
|
692
|
+
label: 'Product Category',
|
|
693
|
+
category: 'commerce',
|
|
694
|
+
description: 'Retail and product category names',
|
|
695
|
+
targetTypes: 'text',
|
|
696
|
+
generate: (ctx) => ctx.prng.pick(datasets_1.PRODUCT_CATEGORIES),
|
|
697
|
+
});
|
|
698
|
+
this.register({
|
|
699
|
+
id: 'department',
|
|
700
|
+
label: 'Company Department',
|
|
701
|
+
category: 'commerce',
|
|
702
|
+
description: 'Corporate and business department names',
|
|
703
|
+
targetTypes: 'text',
|
|
704
|
+
generate: (ctx) => ctx.prng.pick(datasets_1.DEPARTMENTS),
|
|
705
|
+
});
|
|
706
|
+
this.register({
|
|
707
|
+
id: 'paymentMethod',
|
|
708
|
+
label: 'Payment Method',
|
|
709
|
+
category: 'commerce',
|
|
710
|
+
description: 'Payment providers and payment methods',
|
|
711
|
+
targetTypes: 'text',
|
|
712
|
+
generate: (ctx) => ctx.prng.pick(datasets_1.PAYMENT_METHODS),
|
|
713
|
+
});
|
|
714
|
+
this.register({
|
|
715
|
+
id: 'transactionType',
|
|
716
|
+
label: 'Transaction Type',
|
|
717
|
+
category: 'commerce',
|
|
718
|
+
description: 'Financial and e-commerce transaction types',
|
|
719
|
+
targetTypes: 'text',
|
|
720
|
+
generate: (ctx) => ctx.prng.pick(datasets_1.TRANSACTION_TYPES),
|
|
721
|
+
});
|
|
722
|
+
this.register({
|
|
723
|
+
id: 'techSkill',
|
|
724
|
+
label: 'Tech Skill / Tag',
|
|
725
|
+
category: 'system_crypto',
|
|
726
|
+
description: 'Programming languages, tools, and developer skills',
|
|
727
|
+
targetTypes: 'text',
|
|
728
|
+
generate: (ctx) => ctx.prng.pick(datasets_1.TECH_SKILLS),
|
|
729
|
+
});
|
|
730
|
+
this.register({
|
|
731
|
+
id: 'orderStatus',
|
|
732
|
+
label: 'Order Lifecycle Status',
|
|
733
|
+
category: 'commerce',
|
|
734
|
+
description: 'Order lifecycle statuses (pending, shipped, delivered...)',
|
|
735
|
+
targetTypes: 'text',
|
|
736
|
+
generate: (ctx) => ctx.prng.pick(datasets_1.ORDER_STATUSES),
|
|
737
|
+
});
|
|
738
|
+
this.register({
|
|
739
|
+
id: 'fk',
|
|
740
|
+
label: 'Foreign Key (Sample from Parent)',
|
|
741
|
+
category: 'relational',
|
|
742
|
+
description: 'Draw valid foreign keys from generated or existing parent records',
|
|
743
|
+
targetTypes: 'all',
|
|
744
|
+
generate: (ctx) => {
|
|
745
|
+
const pool = ctx.fkPool;
|
|
746
|
+
if (!pool || pool.length === 0)
|
|
747
|
+
return null;
|
|
748
|
+
return ctx.prng.pick(pool);
|
|
749
|
+
},
|
|
750
|
+
});
|
|
751
|
+
this.register({
|
|
752
|
+
id: 'sequentialFk',
|
|
753
|
+
label: 'Sequential Foreign Key',
|
|
754
|
+
category: 'relational',
|
|
755
|
+
description: 'Distribute foreign keys sequentially across available parents',
|
|
756
|
+
targetTypes: 'all',
|
|
757
|
+
generate: (ctx) => {
|
|
758
|
+
const pool = ctx.fkPool;
|
|
759
|
+
if (!pool || pool.length === 0)
|
|
760
|
+
return null;
|
|
761
|
+
return pool[ctx.rowIndex % pool.length];
|
|
762
|
+
},
|
|
763
|
+
});
|
|
764
|
+
this.register({
|
|
765
|
+
id: 'sampleExisting',
|
|
766
|
+
label: 'Sample Existing Values',
|
|
767
|
+
category: 'database_aware',
|
|
768
|
+
description: 'Pick from values already present in the database table',
|
|
769
|
+
targetTypes: 'all',
|
|
770
|
+
generate: (ctx) => {
|
|
771
|
+
const pool = ctx.sampledPool || ctx.fkPool;
|
|
772
|
+
if (!pool || pool.length === 0)
|
|
773
|
+
return types_1.SKIP;
|
|
774
|
+
return ctx.prng.pick(pool);
|
|
775
|
+
},
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
exports.GeneratorRegistry = new GeneratorRegistryClass();
|
|
780
|
+
function generateColumnValue(col, plan, prng = (0, prng_1.createPrng)(), rowIndex = 0, rowValues = {}, fkPool, sampledPool) {
|
|
781
|
+
const type = (col.type || '').toUpperCase();
|
|
782
|
+
if (!col.pk && !col.notnull && plan.nullPct && plan.nullPct > 0) {
|
|
783
|
+
if (prng.chance(plan.nullPct))
|
|
784
|
+
return null;
|
|
785
|
+
}
|
|
786
|
+
const def = exports.GeneratorRegistry.get(plan.strategy);
|
|
787
|
+
if (!def)
|
|
788
|
+
return types_1.SKIP;
|
|
789
|
+
const rawVal = def.generate({
|
|
790
|
+
col,
|
|
791
|
+
plan,
|
|
792
|
+
prng,
|
|
793
|
+
rowIndex,
|
|
794
|
+
rowValues,
|
|
795
|
+
fkPool,
|
|
796
|
+
sampledPool,
|
|
797
|
+
});
|
|
798
|
+
if (rawVal === types_1.SKIP || rawVal === null)
|
|
799
|
+
return rawVal;
|
|
800
|
+
let val = rawVal;
|
|
801
|
+
if (typeof val === 'string' && (plan.prefix || plan.suffix)) {
|
|
802
|
+
val = `${plan.prefix || ''}${val}${plan.suffix || ''}`;
|
|
803
|
+
}
|
|
804
|
+
return (0, common_1.coerceFormValue)(val, type || 'TEXT');
|
|
805
|
+
}
|