lacspace-fake 0.1.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/dist/cli.js ADDED
@@ -0,0 +1,895 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import { stdout, stderr, argv, exit, env } from "process";
5
+ import { readFileSync, writeFileSync } from "fs";
6
+
7
+ // src/prng.ts
8
+ function hashSeed(input) {
9
+ let h = 2166136261 >>> 0;
10
+ for (let i = 0; i < input.length; i++) {
11
+ h ^= input.charCodeAt(i);
12
+ h = Math.imul(h, 16777619);
13
+ }
14
+ return h >>> 0;
15
+ }
16
+ function normalizeSeed(seed) {
17
+ if (typeof seed === "number") return Math.floor(seed) >>> 0;
18
+ const trimmed = seed.trim();
19
+ if (/^-?\d+$/.test(trimmed)) return Math.abs(Number(trimmed)) >>> 0;
20
+ return hashSeed(trimmed);
21
+ }
22
+ function mulberry32(a) {
23
+ let state = a >>> 0;
24
+ return function next() {
25
+ state = state + 1831565813 | 0;
26
+ let t = state;
27
+ t = Math.imul(t ^ t >>> 15, t | 1);
28
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
29
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
30
+ };
31
+ }
32
+ var RNG = class {
33
+ _next;
34
+ seed;
35
+ constructor(seed) {
36
+ this.seed = normalizeSeed(seed);
37
+ this._next = mulberry32(this.seed);
38
+ }
39
+ /** Uniform float in [0, 1). */
40
+ next() {
41
+ return this._next();
42
+ }
43
+ /** Inclusive integer in [min, max]. */
44
+ int(min, max) {
45
+ if (max < min) [min, max] = [max, min];
46
+ return min + Math.floor(this.next() * (max - min + 1));
47
+ }
48
+ /** Float in [min, max) rounded to `decimals` places (default 2). */
49
+ float(min, max, decimals = 2) {
50
+ if (max < min) [min, max] = [max, min];
51
+ const v = min + this.next() * (max - min);
52
+ const f = 10 ** decimals;
53
+ return Math.round(v * f) / f;
54
+ }
55
+ /** `true` with probability `prob` (default 0.5). */
56
+ bool(prob = 0.5) {
57
+ return this.next() < prob;
58
+ }
59
+ /** Pick one element uniformly. Throws on an empty array. */
60
+ pick(items) {
61
+ if (items.length === 0) throw new Error("pick() called on an empty array");
62
+ return items[Math.floor(this.next() * items.length)];
63
+ }
64
+ /** Pick `n` distinct elements (Fisher–Yates partial shuffle). */
65
+ sample(items, n) {
66
+ const arr = items.slice();
67
+ const k = Math.min(n, arr.length);
68
+ for (let i = 0; i < k; i++) {
69
+ const j = i + Math.floor(this.next() * (arr.length - i));
70
+ [arr[i], arr[j]] = [arr[j], arr[i]];
71
+ }
72
+ return arr.slice(0, k);
73
+ }
74
+ /** Return a shuffled copy. */
75
+ shuffle(items) {
76
+ return this.sample(items, items.length);
77
+ }
78
+ /** Weighted pick. Entries are `[value, weight]`; weights need not sum to 1. */
79
+ weighted(entries) {
80
+ const total = entries.reduce((s, [, w]) => s + (w > 0 ? w : 0), 0);
81
+ if (total <= 0) throw new Error("weighted() needs at least one positive weight");
82
+ let r = this.next() * total;
83
+ for (const [value, w] of entries) {
84
+ if (w <= 0) continue;
85
+ r -= w;
86
+ if (r < 0) return value;
87
+ }
88
+ return entries[entries.length - 1][0];
89
+ }
90
+ /** A string of `len` random hex characters. */
91
+ hex(len) {
92
+ let out = "";
93
+ for (let i = 0; i < len; i++) out += "0123456789abcdef"[this.int(0, 15)];
94
+ return out;
95
+ }
96
+ /** A string of `len` random decimal digits. */
97
+ digits(len) {
98
+ let out = "";
99
+ for (let i = 0; i < len; i++) out += String(this.int(0, 9));
100
+ return out;
101
+ }
102
+ /** Pick `len` characters from an alphabet. */
103
+ chars(alphabet, len) {
104
+ let out = "";
105
+ for (let i = 0; i < len; i++) out += alphabet[this.int(0, alphabet.length - 1)];
106
+ return out;
107
+ }
108
+ /** An RFC-4122 version-4 UUID, drawn deterministically from this RNG. */
109
+ uuid() {
110
+ const h = this.hex(32).split("");
111
+ h[12] = "4";
112
+ h[16] = "89ab"[this.int(0, 3)];
113
+ const s = h.join("");
114
+ return `${s.slice(0, 8)}-${s.slice(8, 12)}-${s.slice(12, 16)}-${s.slice(16, 20)}-${s.slice(20, 32)}`;
115
+ }
116
+ };
117
+
118
+ // src/data.ts
119
+ var LOREM = "lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore et dolore magna aliqua enim ad minim veniam quis nostrud exercitation ullamco laboris nisi aliquip ex ea commodo consequat duis aute irure in reprehenderit voluptate velit esse cillum eu fugiat nulla pariatur excepteur sint occaecat cupidatat non proident sunt culpa qui officia deserunt mollit anim id est laborum".split(" ");
120
+ var LOREM_WORDS = LOREM;
121
+ var EMAIL_DOMAINS = ["gmail.com", "yahoo.com", "outlook.com", "hotmail.com", "proton.me", "icloud.com"];
122
+ var URL_TLDS = ["com", "net", "org", "io", "dev", "co", "app", "xyz"];
123
+ var COMPANY_SUFFIXES = ["Inc", "LLC", "Ltd", "Group", "Labs", "Technologies", "Systems", "Solutions", "Holdings", "Partners"];
124
+ var COMPANY_PREFIXES = ["Blue", "Green", "Bright", "Silver", "North", "Peak", "Core", "Nova", "Prime", "Vertex", "Quantum", "Apex", "Cloud", "Iron", "Golden"];
125
+ var COMPANY_ROOTS = ["wave", "byte", "forge", "sphere", "logic", "sync", "grid", "flux", "scale", "stack", "pulse", "bloom", "shift", "orbit", "loop"];
126
+ var BUZZ_ADJ = ["synergistic", "scalable", "frictionless", "customer-centric", "cutting-edge", "cloud-native", "next-generation", "end-to-end", "data-driven", "seamless", "robust", "agile"];
127
+ var BUZZ_NOUN = ["solutions", "paradigms", "platforms", "architectures", "ecosystems", "workflows", "insights", "experiences", "pipelines", "frameworks", "channels", "networks"];
128
+ var BUZZ_VERB = ["engineer", "empower", "streamline", "orchestrate", "harness", "leverage", "optimize", "accelerate", "transform", "unlock", "scale", "deliver"];
129
+ var JOB_LEVELS = ["Junior", "Senior", "Lead", "Principal", "Staff", "Chief", "Head of", "Associate"];
130
+ var JOB_ROLES = ["Engineer", "Developer", "Designer", "Manager", "Analyst", "Consultant", "Architect", "Specialist", "Officer", "Strategist", "Scientist", "Administrator"];
131
+ var DEPARTMENTS = ["Engineering", "Sales", "Marketing", "Finance", "Human Resources", "Operations", "Support", "Product", "Design", "Legal", "Research", "IT"];
132
+ var PRODUCT_ADJ = ["Ergonomic", "Rustic", "Handcrafted", "Sleek", "Refined", "Intelligent", "Modern", "Premium", "Compact", "Elegant", "Durable", "Lightweight"];
133
+ var PRODUCT_MATERIAL = ["Steel", "Wooden", "Cotton", "Leather", "Bamboo", "Ceramic", "Plastic", "Concrete", "Glass", "Copper"];
134
+ var PRODUCT_NOUN = ["Chair", "Table", "Keyboard", "Bottle", "Backpack", "Lamp", "Shoes", "Watch", "Headphones", "Jacket", "Mug", "Gloves", "Wallet", "Speaker"];
135
+ var PRODUCT_CATEGORIES = ["Electronics", "Books", "Clothing", "Home", "Toys", "Sports", "Beauty", "Grocery", "Automotive", "Garden", "Health", "Office"];
136
+ var COLORS = ["red", "green", "blue", "yellow", "orange", "purple", "teal", "black", "white", "gray", "pink", "brown"];
137
+ var EN = {
138
+ firstNamesMale: ["James", "John", "Michael", "David", "Robert", "William", "Daniel", "Thomas", "Joseph", "Ethan", "Liam", "Noah", "Lucas", "Henry", "Alexander"],
139
+ firstNamesFemale: ["Mary", "Emma", "Olivia", "Sophia", "Isabella", "Ava", "Charlotte", "Amelia", "Emily", "Grace", "Chloe", "Zoe", "Hannah", "Lily", "Nora"],
140
+ lastNames: ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez", "Wilson", "Anderson", "Taylor", "Thomas", "Moore"],
141
+ cities: ["New York", "London", "San Francisco", "Berlin", "Toronto", "Sydney", "Singapore", "Tokyo", "Paris", "Amsterdam", "Austin", "Seattle", "Boston", "Chicago", "Denver"],
142
+ states: ["California", "Texas", "New York", "Florida", "Washington", "Illinois", "Ohio", "Georgia", "Colorado", "Oregon", "Arizona", "Nevada"],
143
+ country: "United States",
144
+ countryCode: "US",
145
+ streetNames: ["Maple", "Oak", "Pine", "Cedar", "Elm", "Washington", "Lake", "Hill", "Sunset", "River", "Park", "Main", "Highland", "Franklin", "Union"],
146
+ streetSuffixes: ["Street", "Avenue", "Boulevard", "Lane", "Road", "Drive", "Court", "Way", "Terrace", "Place"],
147
+ zip: (r) => r.digits(5),
148
+ currency: { code: "USD", symbol: "$" },
149
+ phone: (r) => {
150
+ const area = r.digits(3);
151
+ const rest = `${r.digits(3)}${r.digits(4)}`;
152
+ return { e164: `+1${area}${rest}`, local: `(${area}) ${rest.slice(0, 3)}-${rest.slice(3)}` };
153
+ }
154
+ };
155
+ var NE = {
156
+ firstNamesMale: ["Aarav", "Aayush", "Anish", "Bibek", "Prakash", "Rajesh", "Sujan", "Kiran", "Nabin", "Suman", "Dipesh", "Hari", "Krishna", "Bishal", "Rohit", "Manish", "Saroj", "Sandesh"],
157
+ firstNamesFemale: ["Anjali", "Sita", "Gita", "Puja", "Sunita", "Rita", "Sarita", "Nisha", "Priya", "Laxmi", "Sabina", "Muna", "Rekha", "Asha", "Deepa", "Sushmita", "Manisha", "Pratima"],
158
+ lastNames: ["Sharma", "Shrestha", "Adhikari", "Karki", "Thapa", "Gurung", "Magar", "Rai", "Limbu", "Tamang", "Bhattarai", "Poudel", "Koirala", "Acharya", "Dahal", "Bhandari", "Pandey", "Khadka", "Basnet", "Maharjan"],
159
+ cities: ["Kathmandu", "Lalitpur", "Bhaktapur", "Pokhara", "Biratnagar", "Birgunj", "Dharan", "Butwal", "Hetauda", "Nepalgunj", "Dhangadhi", "Janakpur", "Itahari", "Bharatpur", "Damak"],
160
+ states: ["Koshi", "Madhesh", "Bagmati", "Gandaki", "Lumbini", "Karnali", "Sudurpashchim"],
161
+ country: "Nepal",
162
+ countryCode: "NP",
163
+ streetNames: ["New Road", "Durbar Marg", "Lakeside", "Putalisadak", "Baneshwor", "Thamel", "Kupondole", "Jawalakhel", "Maitighar", "Baluwatar", "Sanepa", "Boudha"],
164
+ streetSuffixes: ["Marg", "Chowk", "Tole", "Path", "Road", "Sadak"],
165
+ zip: (r) => r.digits(5),
166
+ currency: { code: "NPR", symbol: "Rs." },
167
+ phone: (r) => {
168
+ const prefix = r.pick(["98", "97"]);
169
+ const carrier = r.pick(["4", "5", "6", "0", "1", "8"]);
170
+ const rest = r.digits(7);
171
+ const local = `${prefix}${carrier}${rest}`;
172
+ return { e164: `+977${local}`, local: `${local.slice(0, 3)}-${local.slice(3)}` };
173
+ }
174
+ };
175
+ var LOCALES = { en: EN, ne: NE };
176
+ function isLocale(v) {
177
+ return v === "en" || v === "ne";
178
+ }
179
+
180
+ // src/generators.ts
181
+ function data(ctx) {
182
+ return LOCALES[ctx.locale];
183
+ }
184
+ function num(a, fallback) {
185
+ if (a === void 0) return fallback;
186
+ const n = typeof a === "number" ? a : Number(a);
187
+ return Number.isFinite(n) ? n : fallback;
188
+ }
189
+ function str(a, fallback = "") {
190
+ return a === void 0 ? fallback : String(a);
191
+ }
192
+ function slugify(input) {
193
+ return input.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
194
+ }
195
+ function capitalize(s) {
196
+ return s.length ? s[0].toUpperCase() + s.slice(1) : s;
197
+ }
198
+ function pickFirstName(ctx, gender) {
199
+ const d = data(ctx);
200
+ const g = gender ?? (ctx.rng.bool() ? "male" : "female");
201
+ return ctx.rng.pick(g === "male" ? d.firstNamesMale : d.firstNamesFemale);
202
+ }
203
+ function nameParts(ctx) {
204
+ const d = data(ctx);
205
+ const rowFirst = ctx.row["firstName"];
206
+ const rowLast = ctx.row["lastName"];
207
+ const first = typeof rowFirst === "string" && rowFirst ? rowFirst : pickFirstName(ctx);
208
+ const last = typeof rowLast === "string" && rowLast ? rowLast : ctx.rng.pick(d.lastNames);
209
+ return { first, last };
210
+ }
211
+ function makeWords(ctx, count) {
212
+ const out = [];
213
+ for (let i = 0; i < count; i++) out.push(ctx.rng.pick(LOREM_WORDS));
214
+ return out.join(" ");
215
+ }
216
+ function makeSentence(ctx, words) {
217
+ const n = words ?? ctx.rng.int(6, 14);
218
+ return capitalize(makeWords(ctx, n)) + ".";
219
+ }
220
+ function makeParagraph(ctx, sentences) {
221
+ const n = sentences ?? ctx.rng.int(3, 6);
222
+ const out = [];
223
+ for (let i = 0; i < n; i++) out.push(makeSentence(ctx));
224
+ return out.join(" ");
225
+ }
226
+ var DAY_MS = 864e5;
227
+ function isoOf(ms) {
228
+ return new Date(ms).toISOString();
229
+ }
230
+ var generators = {
231
+ // person
232
+ firstName: (ctx, a) => pickFirstName(ctx, a[0] === "male" || a[0] === "female" ? a[0] : void 0),
233
+ lastName: (ctx) => ctx.rng.pick(data(ctx).lastNames),
234
+ fullName: (ctx) => {
235
+ const { first, last } = nameParts(ctx);
236
+ return `${first} ${last}`;
237
+ },
238
+ name: (ctx) => generators["fullName"](ctx, []),
239
+ gender: (ctx) => ctx.rng.pick(["male", "female"]),
240
+ age: (ctx, a) => ctx.rng.int(num(a[0], 18), num(a[1], 80)),
241
+ dateOfBirth: (ctx, a) => {
242
+ const minAge = num(a[0], 18);
243
+ const maxAge = num(a[1], 80);
244
+ const years = ctx.rng.int(minAge, maxAge);
245
+ const ms = Date.now() - years * 365.25 * DAY_MS - ctx.rng.int(0, 364) * DAY_MS;
246
+ return isoOf(ms).slice(0, 10);
247
+ },
248
+ dob: (ctx, a) => generators["dateOfBirth"](ctx, a),
249
+ // internet
250
+ username: (ctx) => {
251
+ const { first, last } = nameParts(ctx);
252
+ const sep = ctx.rng.pick(["", ".", "_"]);
253
+ const tail = ctx.rng.bool(0.6) ? String(ctx.rng.int(1, 999)) : "";
254
+ return `${first.toLowerCase()}${sep}${last.toLowerCase()}${tail}`;
255
+ },
256
+ email: (ctx, a) => {
257
+ const { first, last } = nameParts(ctx);
258
+ const rowUser = ctx.row["username"];
259
+ const localBase = typeof rowUser === "string" && rowUser ? rowUser : `${first.toLowerCase()}${ctx.rng.pick([".", "_", ""])}${last.toLowerCase()}`;
260
+ const suffix = ctx.rng.bool(0.5) ? String(ctx.rng.int(1, 99)) : "";
261
+ const domain = a[0] !== void 0 ? str(a[0]) : ctx.rng.pick(EMAIL_DOMAINS);
262
+ return `${slugify(localBase).replace(/-/g, ".")}${suffix}@${domain}`;
263
+ },
264
+ domain: (ctx) => `${ctx.rng.pick(COMPANY_ROOTS)}${ctx.rng.pick(COMPANY_ROOTS)}.${ctx.rng.pick(URL_TLDS)}`,
265
+ url: (ctx) => `https://${ctx.rng.pick(COMPANY_ROOTS)}${ctx.rng.pick(COMPANY_ROOTS)}.${ctx.rng.pick(URL_TLDS)}`,
266
+ slug: (ctx, a) => {
267
+ const words = num(a[0], ctx.rng.int(2, 4));
268
+ return slugify(makeWords(ctx, words));
269
+ },
270
+ ipv4: (ctx) => `${ctx.rng.int(1, 255)}.${ctx.rng.int(0, 255)}.${ctx.rng.int(0, 255)}.${ctx.rng.int(1, 254)}`,
271
+ ipv6: (ctx) => Array.from({ length: 8 }, () => ctx.rng.hex(4)).join(":"),
272
+ mac: (ctx) => Array.from({ length: 6 }, () => ctx.rng.hex(2)).join(":"),
273
+ password: (ctx, a) => {
274
+ const len = num(a[0], 12);
275
+ return ctx.rng.chars("abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789!@#$%&*", len);
276
+ },
277
+ uuid: (ctx) => ctx.rng.uuid(),
278
+ // phone
279
+ phone: (ctx) => data(ctx).phone(ctx.rng).e164,
280
+ phoneLocal: (ctx) => data(ctx).phone(ctx.rng).local,
281
+ // address
282
+ street: (ctx) => {
283
+ const d = data(ctx);
284
+ return `${ctx.rng.int(1, 9999)} ${ctx.rng.pick(d.streetNames)} ${ctx.rng.pick(d.streetSuffixes)}`;
285
+ },
286
+ city: (ctx) => ctx.rng.pick(data(ctx).cities),
287
+ state: (ctx) => ctx.rng.pick(data(ctx).states),
288
+ province: (ctx) => ctx.rng.pick(data(ctx).states),
289
+ country: (ctx) => data(ctx).country,
290
+ countryCode: (ctx) => data(ctx).countryCode,
291
+ zip: (ctx) => data(ctx).zip(ctx.rng),
292
+ postalCode: (ctx) => data(ctx).zip(ctx.rng),
293
+ address: (ctx) => {
294
+ const d = data(ctx);
295
+ const street = `${ctx.rng.int(1, 9999)} ${ctx.rng.pick(d.streetNames)} ${ctx.rng.pick(d.streetSuffixes)}`;
296
+ return `${street}, ${ctx.rng.pick(d.cities)}, ${ctx.rng.pick(d.states)} ${d.zip(ctx.rng)}`;
297
+ },
298
+ latitude: (ctx) => ctx.rng.float(-90, 90, 6),
299
+ longitude: (ctx) => ctx.rng.float(-180, 180, 6),
300
+ latlng: (ctx) => `${ctx.rng.float(-90, 90, 6)},${ctx.rng.float(-180, 180, 6)}`,
301
+ // company
302
+ company: (ctx) => `${ctx.rng.pick(COMPANY_PREFIXES)}${ctx.rng.pick(COMPANY_ROOTS)} ${ctx.rng.pick(COMPANY_SUFFIXES)}`,
303
+ catchphrase: (ctx) => capitalize(`${ctx.rng.pick(BUZZ_VERB)} ${ctx.rng.pick(BUZZ_ADJ)} ${ctx.rng.pick(BUZZ_NOUN)}`),
304
+ jobTitle: (ctx) => `${ctx.rng.pick(JOB_LEVELS)} ${ctx.rng.pick(JOB_ROLES)}`,
305
+ department: (ctx) => ctx.rng.pick(DEPARTMENTS),
306
+ // commerce
307
+ productName: (ctx) => `${ctx.rng.pick(PRODUCT_ADJ)} ${ctx.rng.pick(PRODUCT_MATERIAL)} ${ctx.rng.pick(PRODUCT_NOUN)}`,
308
+ price: (ctx, a) => ctx.rng.float(num(a[0], 1), num(a[1], 999), 2),
309
+ sku: (ctx) => `${ctx.rng.chars("ABCDEFGHJKLMNPQRSTUVWXYZ", 3)}-${ctx.rng.digits(6)}`,
310
+ currency: (ctx) => data(ctx).currency.code,
311
+ category: (ctx) => ctx.rng.pick(PRODUCT_CATEGORIES),
312
+ color: (ctx) => ctx.rng.pick(COLORS),
313
+ // text
314
+ word: (ctx) => ctx.rng.pick(LOREM_WORDS),
315
+ words: (ctx, a) => makeWords(ctx, num(a[0], 3)),
316
+ sentence: (ctx, a) => makeSentence(ctx, a[0] !== void 0 ? num(a[0], 8) : void 0),
317
+ paragraph: (ctx, a) => makeParagraph(ctx, a[0] !== void 0 ? num(a[0], 4) : void 0),
318
+ lorem: (ctx, a) => makeParagraph(ctx, num(a[0], 3)),
319
+ // datetime
320
+ past: (ctx, a) => {
321
+ const days = num(a[0], 365);
322
+ return isoOf(Date.now() - ctx.rng.int(1, Math.max(1, days)) * DAY_MS - ctx.rng.int(0, DAY_MS));
323
+ },
324
+ future: (ctx, a) => {
325
+ const days = num(a[0], 365);
326
+ return isoOf(Date.now() + ctx.rng.int(1, Math.max(1, days)) * DAY_MS + ctx.rng.int(0, DAY_MS));
327
+ },
328
+ recent: (ctx, a) => {
329
+ const days = num(a[0], 7);
330
+ return isoOf(Date.now() - ctx.rng.int(0, Math.max(1, days) * DAY_MS));
331
+ },
332
+ soon: (ctx, a) => {
333
+ const days = num(a[0], 7);
334
+ return isoOf(Date.now() + ctx.rng.int(0, Math.max(1, days) * DAY_MS));
335
+ },
336
+ between: (ctx, a) => {
337
+ const from = Date.parse(str(a[0]));
338
+ const to = Date.parse(str(a[1]));
339
+ if (Number.isNaN(from) || Number.isNaN(to)) throw new Error("between(from..to) needs two ISO dates");
340
+ const lo = Math.min(from, to);
341
+ const hi = Math.max(from, to);
342
+ return isoOf(lo + Math.floor(ctx.rng.next() * (hi - lo)));
343
+ },
344
+ timestamp: (ctx, a) => {
345
+ const days = num(a[0], 365);
346
+ return isoOf(Date.now() - ctx.rng.int(0, Math.max(1, days)) * DAY_MS);
347
+ },
348
+ date: (ctx, a) => String(generators["timestamp"](ctx, a)).slice(0, 10),
349
+ time: (ctx) => `${String(ctx.rng.int(0, 23)).padStart(2, "0")}:${String(ctx.rng.int(0, 59)).padStart(2, "0")}`,
350
+ // numbers / booleans / enums
351
+ int: (ctx, a) => ctx.rng.int(num(a[0], 0), num(a[1], 100)),
352
+ number: (ctx, a) => ctx.rng.int(num(a[0], 0), num(a[1], 100)),
353
+ float: (ctx, a) => ctx.rng.float(num(a[0], 0), num(a[1], 1), num(a[2], 2)),
354
+ digit: (ctx) => ctx.rng.int(0, 9),
355
+ bool: (ctx, a) => ctx.rng.bool(a[0] !== void 0 ? num(a[0], 0.5) : 0.5),
356
+ boolean: (ctx, a) => ctx.rng.bool(a[0] !== void 0 ? num(a[0], 0.5) : 0.5),
357
+ oneOf: (ctx, a) => {
358
+ if (a.length === 0) throw new Error("oneOf(...) needs at least one option");
359
+ return ctx.rng.pick(a);
360
+ },
361
+ enum: (ctx, a) => generators["oneOf"](ctx, a),
362
+ weighted: (ctx, a) => {
363
+ const entries = a.map((raw) => {
364
+ const s = String(raw);
365
+ const idx = s.lastIndexOf(":");
366
+ if (idx < 0) return [s, 1];
367
+ return [s.slice(0, idx), Number(s.slice(idx + 1)) || 0];
368
+ });
369
+ if (entries.length === 0) throw new Error("weighted(...) needs at least one value:weight");
370
+ return ctx.rng.weighted(entries);
371
+ },
372
+ // ids
373
+ autoincrement: (ctx, a) => num(a[0], 1) + ctx.index,
374
+ autoInc: (ctx, a) => generators["autoincrement"](ctx, a),
375
+ id: (ctx, a) => generators["autoincrement"](ctx, a),
376
+ nanoid: (ctx, a) => ctx.rng.chars("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-", num(a[0], 21)),
377
+ objectId: (ctx) => ctx.rng.hex(24),
378
+ // Nepal-flavoured ids (locale-aware amount/tax numbers)
379
+ pan: (ctx) => ctx.rng.digits(9),
380
+ vat: (ctx) => ctx.rng.digits(9)
381
+ };
382
+ var GEN_ORDER = [
383
+ "firstName",
384
+ "lastName",
385
+ "fullName",
386
+ "gender",
387
+ "age",
388
+ "dateOfBirth",
389
+ "email",
390
+ "username",
391
+ "url",
392
+ "domain",
393
+ "ipv4",
394
+ "ipv6",
395
+ "mac",
396
+ "password",
397
+ "uuid",
398
+ "slug",
399
+ "phone",
400
+ "phoneLocal",
401
+ "street",
402
+ "city",
403
+ "state",
404
+ "country",
405
+ "countryCode",
406
+ "zip",
407
+ "address",
408
+ "latitude",
409
+ "longitude",
410
+ "latlng",
411
+ "company",
412
+ "catchphrase",
413
+ "jobTitle",
414
+ "department",
415
+ "productName",
416
+ "price",
417
+ "sku",
418
+ "currency",
419
+ "category",
420
+ "color",
421
+ "word",
422
+ "words",
423
+ "sentence",
424
+ "paragraph",
425
+ "lorem",
426
+ "past",
427
+ "future",
428
+ "recent",
429
+ "soon",
430
+ "between",
431
+ "timestamp",
432
+ "date",
433
+ "time",
434
+ "int",
435
+ "float",
436
+ "bool",
437
+ "oneOf",
438
+ "weighted",
439
+ "digit",
440
+ "autoincrement",
441
+ "nanoid",
442
+ "objectId",
443
+ "pan",
444
+ "vat"
445
+ ];
446
+ function callGen(name, ctx, args) {
447
+ const gen = generators[name];
448
+ if (!gen) throw new Error(`Unknown generator: "${name}". Run \`lacspace-fake list\` to see all generators.`);
449
+ return gen(ctx, args);
450
+ }
451
+
452
+ // src/schema.ts
453
+ function splitTopLevel(input, sep) {
454
+ const out = [];
455
+ let depth = 0;
456
+ let cur = "";
457
+ for (const ch of input) {
458
+ if (ch === "(") depth++;
459
+ else if (ch === ")") depth = Math.max(0, depth - 1);
460
+ if (ch === sep && depth === 0) {
461
+ out.push(cur);
462
+ cur = "";
463
+ } else {
464
+ cur += ch;
465
+ }
466
+ }
467
+ if (cur.trim() !== "" || out.length) out.push(cur);
468
+ return out.map((s) => s.trim()).filter((s) => s.length > 0);
469
+ }
470
+ function coerceArg(token) {
471
+ const t = token.trim();
472
+ if (/^-?\d+(\.\d+)?$/.test(t)) return Number(t);
473
+ return t;
474
+ }
475
+ function parseArgString(inner) {
476
+ const trimmed = inner.trim();
477
+ if (trimmed === "") return [];
478
+ let parts;
479
+ if (trimmed.includes("|")) parts = trimmed.split("|");
480
+ else if (trimmed.includes("..")) parts = trimmed.split("..");
481
+ else parts = splitTopLevel(trimmed, ",");
482
+ return parts.map((p) => coerceArg(p));
483
+ }
484
+ function specFromString(raw) {
485
+ const s = raw.trim();
486
+ const m = /^([A-Za-z_][A-Za-z0-9_]*)\s*(?:\((.*)\))?$/.exec(s);
487
+ if (!m) throw new Error(`Invalid generator spec: "${raw}"`);
488
+ const name = m[1];
489
+ const args = m[2] !== void 0 ? parseArgString(m[2]) : [];
490
+ callGen(name, probeCtx(), args);
491
+ return (ctx) => callGen(name, ctx, args);
492
+ }
493
+ function probeCtx() {
494
+ return { rng: new RNG(1), locale: "en", index: 0, row: {} };
495
+ }
496
+ function isRecord(v) {
497
+ return typeof v === "object" && v !== null && !Array.isArray(v);
498
+ }
499
+ function specFromJson(value) {
500
+ if (typeof value === "string") return specFromString(value);
501
+ if (typeof value === "number" || typeof value === "boolean" || value === null) {
502
+ return () => value;
503
+ }
504
+ if (Array.isArray(value)) {
505
+ const specs = value.map((v) => specFromJson(v));
506
+ return (ctx) => specs[ctx.rng.int(0, specs.length - 1)](ctx);
507
+ }
508
+ if (isRecord(value)) {
509
+ const type = value["type"];
510
+ if (type === "array") {
511
+ const a = value;
512
+ if (a.of === void 0) throw new Error('array spec needs an "of" field');
513
+ const itemSpec = specFromJson(a.of);
514
+ return (ctx) => {
515
+ const n = typeof a.count === "number" ? a.count : ctx.rng.int(typeof a.min === "number" ? a.min : 1, typeof a.max === "number" ? a.max : 3);
516
+ const out = [];
517
+ for (let i = 0; i < n; i++) out.push(itemSpec(ctx));
518
+ return out;
519
+ };
520
+ }
521
+ if (type === "object") {
522
+ const o = value;
523
+ if (!isRecord(o.properties)) throw new Error('object spec needs a "properties" map');
524
+ const fields2 = fieldsFromRecord(o.properties);
525
+ return (ctx) => buildObject(fields2, ctx);
526
+ }
527
+ if (typeof type === "string") {
528
+ const args = argsFromJsonObject(value);
529
+ return (ctx) => callGen(type, ctx, args);
530
+ }
531
+ const fields = fieldsFromRecord(value);
532
+ return (ctx) => buildObject(fields, ctx);
533
+ }
534
+ throw new Error(`Unsupported schema value: ${JSON.stringify(value)}`);
535
+ }
536
+ function argsFromJsonObject(obj) {
537
+ if (Array.isArray(obj["args"])) return obj["args"].map((v) => typeof v === "number" ? v : String(v));
538
+ if (Array.isArray(obj["values"])) return obj["values"].map((v) => typeof v === "number" ? v : String(v));
539
+ const out = [];
540
+ if (typeof obj["min"] === "number") out.push(obj["min"]);
541
+ if (typeof obj["max"] === "number") out.push(obj["max"]);
542
+ if (typeof obj["decimals"] === "number") out.push(obj["decimals"]);
543
+ if (out.length === 0 && typeof obj["prob"] === "number") out.push(obj["prob"]);
544
+ if (out.length === 0 && typeof obj["count"] === "number") out.push(obj["count"]);
545
+ return out;
546
+ }
547
+ function fieldsFromRecord(rec) {
548
+ return Object.keys(rec).map((key) => ({ key, spec: specFromJson(rec[key]) }));
549
+ }
550
+ function buildObject(fields, parent) {
551
+ const row = {};
552
+ const ctx = { rng: parent.rng, locale: parent.locale, index: parent.index, row };
553
+ for (const f of fields) row[f.key] = f.spec(ctx);
554
+ return row;
555
+ }
556
+ function parseFields(input) {
557
+ const specs = splitTopLevel(input, ",");
558
+ if (specs.length === 0) throw new Error("--fields is empty");
559
+ return specs.map((chunk) => {
560
+ const idx = chunk.indexOf(":");
561
+ if (idx < 0) throw new Error(`Field "${chunk}" must be in the form key:generator`);
562
+ const key = chunk.slice(0, idx).trim();
563
+ const specStr = chunk.slice(idx + 1).trim();
564
+ if (!key) throw new Error(`Field "${chunk}" has an empty key`);
565
+ return { key, spec: specFromString(specStr) };
566
+ });
567
+ }
568
+ function parseJsonSchema(schema) {
569
+ if (!isRecord(schema)) throw new Error("A JSON schema must be an object mapping field \u2192 spec");
570
+ const body = isRecord(schema["fields"]) ? schema["fields"] : isRecord(schema["properties"]) && schema["type"] === "object" ? schema["properties"] : schema;
571
+ const fields = fieldsFromRecord(body);
572
+ if (fields.length === 0) throw new Error("The JSON schema has no fields");
573
+ return fields;
574
+ }
575
+ function generateRows(fields, opts = {}) {
576
+ const count = Math.max(0, opts.count ?? 10);
577
+ const seed = opts.seed ?? Math.floor(Math.random() * 4294967295);
578
+ const locale = opts.locale ?? "en";
579
+ const rng = new RNG(seed);
580
+ const rows = [];
581
+ for (let i = 0; i < count; i++) {
582
+ const row = {};
583
+ const ctx = { rng, locale, index: i, row };
584
+ for (const f of fields) row[f.key] = f.spec(ctx);
585
+ rows.push(row);
586
+ }
587
+ return rows;
588
+ }
589
+ function generateValues(specStr, opts = {}) {
590
+ const spec = specFromString(specStr);
591
+ const count = Math.max(0, opts.count ?? 10);
592
+ const seed = opts.seed ?? Math.floor(Math.random() * 4294967295);
593
+ const locale = opts.locale ?? "en";
594
+ const rng = new RNG(seed);
595
+ const out = [];
596
+ for (let i = 0; i < count; i++) {
597
+ out.push(spec({ rng, locale, index: i, row: {} }));
598
+ }
599
+ return out;
600
+ }
601
+
602
+ // src/format.ts
603
+ function isFormat(v) {
604
+ return v === "json" || v === "ndjson" || v === "csv" || v === "sql";
605
+ }
606
+ function columnsOf(rows) {
607
+ const seen = /* @__PURE__ */ new Set();
608
+ const cols = [];
609
+ for (const r of rows) {
610
+ for (const k of Object.keys(r)) {
611
+ if (!seen.has(k)) {
612
+ seen.add(k);
613
+ cols.push(k);
614
+ }
615
+ }
616
+ }
617
+ return cols;
618
+ }
619
+ function cellString(v) {
620
+ if (v === null || v === void 0) return "";
621
+ if (typeof v === "object") return JSON.stringify(v);
622
+ return String(v);
623
+ }
624
+ function csvEscape(v) {
625
+ if (v === null || v === void 0) return "";
626
+ const s = cellString(v);
627
+ if (/[",\n\r]/.test(s)) return `"${s.replace(/"/g, '""')}"`;
628
+ return s;
629
+ }
630
+ function toCsv(rows) {
631
+ const cols = columnsOf(rows);
632
+ const lines = [cols.map(csvEscape).join(",")];
633
+ for (const r of rows) lines.push(cols.map((c2) => csvEscape(r[c2])).join(","));
634
+ return lines.join("\n");
635
+ }
636
+ function sqlValue(v) {
637
+ if (v === null || v === void 0) return "NULL";
638
+ if (typeof v === "number") return Number.isFinite(v) ? String(v) : "NULL";
639
+ if (typeof v === "boolean") return v ? "TRUE" : "FALSE";
640
+ const s = typeof v === "object" ? JSON.stringify(v) : String(v);
641
+ return `'${s.replace(/'/g, "''")}'`;
642
+ }
643
+ function sqlIdent(name) {
644
+ return `"${String(name).replace(/"/g, '""')}"`;
645
+ }
646
+ function toSql(rows, table) {
647
+ if (!table) throw new Error("SQL output needs a --table name");
648
+ const cols = columnsOf(rows);
649
+ if (rows.length === 0) return `-- no rows for ${sqlIdent(table)}`;
650
+ const colList = cols.map(sqlIdent).join(", ");
651
+ const valueRows = rows.map((r) => ` (${cols.map((c2) => sqlValue(r[c2])).join(", ")})`);
652
+ return `INSERT INTO ${sqlIdent(table)} (${colList}) VALUES
653
+ ${valueRows.join(",\n")};`;
654
+ }
655
+ function formatRows(rows, opts) {
656
+ switch (opts.format) {
657
+ case "json":
658
+ return JSON.stringify(rows, null, opts.pretty ? 2 : 0);
659
+ case "ndjson":
660
+ return rows.map((r) => JSON.stringify(r)).join("\n");
661
+ case "csv":
662
+ return toCsv(rows);
663
+ case "sql":
664
+ return toSql(rows, opts.table ?? "");
665
+ }
666
+ }
667
+ function formatValues(values, column, opts) {
668
+ switch (opts.format) {
669
+ case "json":
670
+ return JSON.stringify(values, null, opts.pretty ? 2 : 0);
671
+ case "ndjson":
672
+ return values.map((v) => JSON.stringify(v)).join("\n");
673
+ case "csv":
674
+ return toCsv(values.map((v) => ({ [column]: v })));
675
+ case "sql":
676
+ return toSql(values.map((v) => ({ [column]: v })), opts.table ?? column);
677
+ }
678
+ }
679
+
680
+ // src/cli.ts
681
+ var VERSION = "0.1.0";
682
+ var NO_COLOR = Boolean(env["NO_COLOR"]) || !stdout.isTTY;
683
+ var RAW = {
684
+ reset: "\x1B[0m",
685
+ bold: "\x1B[1m",
686
+ dim: "\x1B[2m",
687
+ green: "\x1B[32m",
688
+ cyan: "\x1B[36m",
689
+ yellow: "\x1B[33m",
690
+ red: "\x1B[31m",
691
+ magenta: "\x1B[35m"
692
+ };
693
+ var c = (k, s) => NO_COLOR ? s : `${RAW[k]}${s}${RAW.reset}`;
694
+ var log = (s = "") => void stderr.write(s + "\n");
695
+ function parseArgs(list) {
696
+ const a = {
697
+ positional: [],
698
+ locale: "en",
699
+ count: 10,
700
+ format: "json",
701
+ pretty: false,
702
+ help: false,
703
+ version: false
704
+ };
705
+ for (let i = 0; i < list.length; i++) {
706
+ const arg = list[i];
707
+ const nextVal = () => list[++i] ?? "";
708
+ if (arg === "--seed" || arg === "-s") a.seed = nextVal();
709
+ else if (arg === "--locale" || arg === "-l") {
710
+ const l = nextVal();
711
+ if (!isLocale(l)) fail(`Unknown locale "${l}". Use "en" or "ne".`, false);
712
+ a.locale = l;
713
+ } else if (arg === "--count" || arg === "-n") a.count = Math.max(0, Math.floor(Number(nextVal())) || 0);
714
+ else if (arg === "--format" || arg === "-f") {
715
+ const f = nextVal();
716
+ if (!isFormat(f)) fail(`Unknown format "${f}". Use json | ndjson | csv | sql.`, false);
717
+ a.format = f;
718
+ } else if (arg === "--table" || arg === "-t") a.table = nextVal();
719
+ else if (arg === "--pretty") a.pretty = true;
720
+ else if (arg === "--out" || arg === "-o") a.out = nextVal();
721
+ else if (arg === "--fields") a.fields = nextVal();
722
+ else if (arg === "--schema") a.schema = nextVal();
723
+ else if (arg === "-h" || arg === "--help") a.help = true;
724
+ else if (arg === "-v" || arg === "--version") a.version = true;
725
+ else if (!arg.startsWith("-")) a.positional.push(arg);
726
+ else fail(`Unknown flag "${arg}". Run --help for usage.`, false);
727
+ }
728
+ return a;
729
+ }
730
+ var HELP = `
731
+ ${c("bold", c("magenta", "\u25C6 lacspace-fake"))} ${c("dim", "\u2014 keyless, deterministic fake / seed data generator")}
732
+
733
+ ${c("bold", "Usage")}
734
+ npx lacspace-fake --fields "<spec>" [options] ${c("dim", "# schema rows")}
735
+ npx lacspace-fake --schema <file.json> [options] ${c("dim", "# JSON schema rows")}
736
+ npx lacspace-fake <generator> [options] ${c("dim", "# N values of one generator")}
737
+ npx lacspace-fake list ${c("dim", "# every generator + a sample")}
738
+
739
+ ${c("bold", "Options")}
740
+ -n, --count <n> How many rows/values to generate (default 10)
741
+ -f, --format <fmt> json | ndjson | csv | sql (default json)
742
+ -t, --table <name> Table name for -f sql (INSERT INTO <name> ...)
743
+ --fields <spec> Inline schema: "key:gen,key:gen(args),..."
744
+ --schema <file> JSON schema file (nested objects + arrays supported)
745
+ -s, --seed <str> Seed for reproducible output (number or any string)
746
+ -l, --locale <loc> en (default) or ne (Nepal-aware)
747
+ --pretty Pretty-print JSON
748
+ -o, --out <file> Write to a file instead of stdout
749
+ -h, --help Show this help
750
+ -v, --version Print the version
751
+
752
+ ${c("bold", "Inline field syntax")}
753
+ ${c("dim", "key:generator e.g. name:fullName")}
754
+ ${c("dim", "key:generator(args) e.g. age:int(18..65) role:oneOf(admin|user)")}
755
+ ${c("dim", "ranges use .. lists use | weighted uses value:weight")}
756
+
757
+ ${c("bold", "Examples")}
758
+ npx lacspace-fake --fields "id:autoincrement,name:fullName,email:email,age:int(18..65)" -n 5
759
+ npx lacspace-fake --fields "id:autoincrement,name:fullName,role:oneOf(admin|user)" -f sql --table users
760
+ npx lacspace-fake --fields "user:fullName,phone:phone" --locale ne -n 3
761
+ npx lacspace-fake --schema users.json -n 50 -f csv -o users.csv
762
+ npx lacspace-fake email -n 5 --seed 42
763
+ npx lacspace-fake list
764
+
765
+ ${c("dim", "Free \xB7 keyless \xB7 zero-dependency \xB7 runs fully offline \xB7 no telemetry")}
766
+ `;
767
+ function fail(msg, isJsonUnused) {
768
+ log(c("red", `
769
+ \u2717 ${msg}
770
+ `));
771
+ exit(1);
772
+ }
773
+ function readStdin() {
774
+ try {
775
+ return readFileSync(0, "utf8");
776
+ } catch {
777
+ return "";
778
+ }
779
+ }
780
+ function runList(a) {
781
+ const seed = a.seed ?? "sample";
782
+ const lines = [];
783
+ lines.push(`
784
+ ${c("bold", c("magenta", "\u25C6 lacspace-fake generators"))} ${c("dim", `\xB7 locale ${a.locale}`)}`);
785
+ for (const name of GEN_ORDER) {
786
+ const rng = new RNG(`${seed}:${name}`);
787
+ const ctx = { rng, locale: a.locale, index: 0, row: {} };
788
+ let sample;
789
+ try {
790
+ sample = callGen(name, ctx, sampleArgs(name));
791
+ } catch {
792
+ sample = "(needs args)";
793
+ }
794
+ lines.push(` ${c("cyan", name.padEnd(16))} ${c("dim", String(sample))}`);
795
+ }
796
+ lines.push(`
797
+ ${c("dim", `${GEN_ORDER.length} generators \xB7 use as key:${c("dim", "generator")} in --fields, or directly: lacspace-fake <generator>`)}
798
+ `);
799
+ stdout.write(lines.join("\n") + "\n");
800
+ }
801
+ function sampleArgs(name) {
802
+ switch (name) {
803
+ case "int":
804
+ return [1, 100];
805
+ case "float":
806
+ return [0, 1, 2];
807
+ case "oneOf":
808
+ return ["red", "green", "blue"];
809
+ case "weighted":
810
+ return ["admin:1", "user:5"];
811
+ case "between":
812
+ return ["2020-01-01", "2024-12-31"];
813
+ default:
814
+ return [];
815
+ }
816
+ }
817
+ function output(text, a) {
818
+ if (a.out) {
819
+ writeFileSync(a.out, text.endsWith("\n") ? text : text + "\n");
820
+ log(`${c("green", "\u2713")} wrote ${c("bold", a.out)} ${c("dim", `(${a.format})`)}`);
821
+ } else {
822
+ stdout.write(text + "\n");
823
+ }
824
+ }
825
+ function main() {
826
+ const a = parseArgs(argv.slice(2));
827
+ if (a.version) {
828
+ stdout.write(VERSION + "\n");
829
+ return;
830
+ }
831
+ const cmd = a.positional[0];
832
+ if (a.help || a.positional.length === 0 && !a.fields && !a.schema) {
833
+ stdout.write(HELP + "\n");
834
+ return;
835
+ }
836
+ if (cmd === "list") {
837
+ runList(a);
838
+ return;
839
+ }
840
+ if (a.schema) {
841
+ let raw;
842
+ try {
843
+ raw = a.schema === "-" ? readStdin() : readFileSync(a.schema, "utf8");
844
+ } catch (err) {
845
+ fail(`Could not read schema file "${a.schema}": ${err.message}`, false);
846
+ }
847
+ let parsed;
848
+ try {
849
+ parsed = JSON.parse(raw);
850
+ } catch (err) {
851
+ fail(`Schema file is not valid JSON: ${err.message}`, false);
852
+ }
853
+ let text2;
854
+ try {
855
+ const fields = parseJsonSchema(parsed);
856
+ const rows = generateRows(fields, { count: a.count, seed: a.seed, locale: a.locale });
857
+ text2 = formatRows(rows, { format: a.format, pretty: a.pretty, table: a.table });
858
+ } catch (err) {
859
+ fail(err.message, false);
860
+ }
861
+ output(text2, a);
862
+ return;
863
+ }
864
+ if (a.fields) {
865
+ let text2;
866
+ try {
867
+ const fields = parseFields(a.fields);
868
+ const rows = generateRows(fields, { count: a.count, seed: a.seed, locale: a.locale });
869
+ text2 = formatRows(rows, { format: a.format, pretty: a.pretty, table: a.table });
870
+ } catch (err) {
871
+ fail(err.message, false);
872
+ }
873
+ output(text2, a);
874
+ return;
875
+ }
876
+ const specStr = cmd;
877
+ const column = /^([A-Za-z_][A-Za-z0-9_]*)/.exec(specStr)?.[1] ?? "value";
878
+ let text;
879
+ try {
880
+ specFromString(specStr);
881
+ const values = generateValues(specStr, { count: a.count, seed: a.seed, locale: a.locale });
882
+ text = formatValues(values, column, { format: a.format, pretty: a.pretty, table: a.table });
883
+ } catch (err) {
884
+ fail(err.message, false);
885
+ }
886
+ output(text, a);
887
+ }
888
+ try {
889
+ main();
890
+ } catch (err) {
891
+ log(c("red", `
892
+ \u2717 ${err instanceof Error ? err.message : String(err)}
893
+ `));
894
+ exit(1);
895
+ }