lacspace-fake 0.1.0 → 0.2.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 CHANGED
@@ -11,6 +11,17 @@ every machine), **Nepal-aware** with `--locale ne`, and it emits ready-to-run
11
11
  npx lacspace-fake --fields "id:autoincrement,name:fullName,email:email,age:int(18..65)" -n 5
12
12
  ```
13
13
 
14
+ ## New in 0.2.0
15
+
16
+ - **Relations / linked tables** — `--relations schema.json` generates several entities where one references another's ids (`ref(users.id)`), with guaranteed referential integrity (every foreign key exists).
17
+ - **Template fields** — `full:template({{firstName}} {{lastName}})` interpolates earlier fields and inline generators.
18
+ - **`unique(...)` constraint** — `email:unique(email)` guarantees no duplicate values across rows.
19
+ - **More locales** — added `es` (Spanish) and `fr` (French) alongside `en` and `ne`.
20
+ - **`--ddl`** — for `-f sql`, also emit a `CREATE TABLE` with column types inferred from the data.
21
+ - **~15 new generators** — `ulid`, `hexColor`/`rgb`/`hsl`, `semver`, `mimeType`, `fileExt`/`fileName`/`filePath`, `timezone`, `currencyCode`, `creditCardMasked`/`cardBrand`, `iban`, `bic`.
22
+
23
+ All additive and backward compatible — existing schemas, flags and output are unchanged.
24
+
14
25
  ## Why it exists
15
26
 
16
27
  - **Free & keyless.** No API key, no account, no network calls, no telemetry — runs fully offline.
@@ -32,11 +43,21 @@ npx lacspace-fake --fields "id:autoincrement,name:fullName,role:oneOf(admin|user
32
43
  # From a JSON schema, out to a CSV file
33
44
  npx lacspace-fake --schema users.json -n 50 -f csv -o users.csv
34
45
 
35
- # Nepal locale
46
+ # Nepal / Spanish / French locale
36
47
  npx lacspace-fake --fields "name:fullName,phone:phone,district:city" --locale ne -n 10
48
+ npx lacspace-fake --fields "name:fullName,phone:phone" --locale es -n 5
49
+
50
+ # Template + unique fields
51
+ npx lacspace-fake --fields "id:unique(uuid),first:firstName,handle:template({{first}}-{{int(1..99)}})" -n 5
37
52
 
38
- # Just five emails
39
- npx lacspace-fake email -n 5 --seed 42
53
+ # SQL with CREATE TABLE DDL
54
+ npx lacspace-fake --fields "id:autoincrement,name:fullName,score:float(0..100)" -f sql --table users --ddl
55
+
56
+ # Linked tables with foreign keys
57
+ npx lacspace-fake --relations shop.json -f sql --ddl
58
+
59
+ # Just five ULIDs
60
+ npx lacspace-fake ulid -n 5 --seed 42
40
61
 
41
62
  # Discover every generator
42
63
  npx lacspace-fake list
@@ -109,11 +130,14 @@ key:generator(args) age:int(18..65) role:oneOf(admin|user)
109
130
 
110
131
  - Numeric ranges use `..` — `int(18..65)`, `price(10..999)`, `float(0..1)`
111
132
  - Option lists use `|` — `oneOf(admin|user|guest)`
112
- - Weighted picks use `value:weight` — `weighted(admin:1|user:9)`
133
+ - Weighted picks use `value:weight` — `weighted(admin:1|user:9)` (a weighted enum)
113
134
  - Date windows use `..` — `between(2020-01-01..2024-12-31)`
135
+ - **Unique** wraps any generator — `email:unique(email)`, `id:unique(int(1..1000000))` — no two rows repeat a value
136
+ - **Templates** interpolate `{{...}}` — `full:template({{firstName}} {{lastName}})`, `email:template({{username}}@{{domain}})`
114
137
 
115
138
  Fields evaluate in order, so a later field can derive from an earlier one:
116
139
  `firstName:firstName,lastName:lastName,email:email` yields emails built from each row's name.
140
+ Inside a `template(...)`, a `{{token}}` that matches an earlier field reuses its value; otherwise it is evaluated as a generator.
117
141
 
118
142
  ## Generators
119
143
 
@@ -123,13 +147,37 @@ street · city · state · country · countryCode · zip · address · latitude
123
147
  latlng · company · catchphrase · jobTitle · department · productName · price · sku ·
124
148
  currency · category · color · word · words · sentence · paragraph · lorem · past ·
125
149
  future · recent · soon · between · timestamp · date · time · int · float · bool ·
126
- oneOf · weighted · digit · autoincrement · nanoid · objectId · pan · vat`
150
+ oneOf · weighted · digit · autoincrement · nanoid · objectId · pan · vat ·
151
+ ulid · hexColor · rgb · hsl · semver · mimeType · fileExt · fileName · filePath ·
152
+ timezone · currencyCode · creditCardMasked · cardBrand · iban · bic`
127
153
 
128
154
  Run `lacspace-fake list` for a live sample of each.
129
155
 
130
- > **Foreign keys / relations:** model an FK column with an `int` range that points at
131
- > your parent table's id space, e.g. `user_id:int(1..100)`. Deterministic seeds keep the
132
- > references stable across regenerations.
156
+ ## Relations (linked tables with foreign keys)
157
+
158
+ A relations schema maps each entity to `{ count, fields }`. A field whose spec is
159
+ `ref(<entity>.<field>)` is a **foreign key**, drawn from the already-generated column of
160
+ an earlier entity — so every FK is guaranteed to exist. The whole dataset is drawn from
161
+ one seed, so it's byte-reproducible.
162
+
163
+ ```json
164
+ {
165
+ "users": { "count": 10, "fields": { "id": "unique(int(1..99999))", "name": "fullName" } },
166
+ "orders": { "count": 30, "fields": { "id": "autoincrement", "userId": "ref(users.id)", "total": "price(5..500)" } }
167
+ }
168
+ ```
169
+
170
+ ```bash
171
+ $ npx lacspace-fake --relations shop.json -f sql --ddl --seed 42
172
+ CREATE TABLE "users" ( "id" INTEGER NOT NULL, "name" TEXT NOT NULL );
173
+ INSERT INTO "users" ("id", "name") VALUES ... ;
174
+ CREATE TABLE "orders" ( "id" INTEGER NOT NULL, "userId" INTEGER NOT NULL, "total" REAL NOT NULL );
175
+ INSERT INTO "orders" ("id", "userId", "total") VALUES ... ; -- every userId exists in users
176
+ ```
177
+
178
+ Entities must be declared parent-first (a `ref(...)` can only point at an entity above it).
179
+ `json` output emits one object of arrays; `sql` emits a block per table; `csv`/`ndjson`
180
+ emit one `# <table>` section per entity.
133
181
 
134
182
  ## CLI reference
135
183
 
@@ -140,10 +188,12 @@ Run `lacspace-fake list` for a live sample of each.
140
188
  | `-n, --count <n>` | Number of rows/values (default 10) |
141
189
  | `-f, --format <fmt>` | `json` (default), `ndjson`, `csv`, `sql` |
142
190
  | `-t, --table <name>` | Table name for `-f sql` |
191
+ | `--ddl` | For `-f sql`: also emit `CREATE TABLE` with inferred column types |
143
192
  | `--fields <spec>` | Inline schema string |
144
193
  | `--schema <file>` | JSON schema file (`-` for stdin) |
194
+ | `--relations <file>` | JSON relations schema → linked entities with foreign keys (`-` for stdin) |
145
195
  | `-s, --seed <str>` | Seed for reproducible output (number or any string) |
146
- | `-l, --locale <loc>` | `en` (default) or `ne` (Nepal) |
196
+ | `-l, --locale <loc>` | `en` (default), `ne` (Nepal), `es` (Spanish) or `fr` (French) |
147
197
  | `--pretty` | Pretty-print JSON |
148
198
  | `-o, --out <file>` | Write to a file instead of stdout |
149
199
  | `-h, --help` / `-v, --version` | Help / version |
@@ -164,15 +214,21 @@ formatRows(rows, { format: "sql", table: "users" });
164
214
 
165
215
  | Export | Signature |
166
216
  |--------|-----------|
167
- | `parseFields(spec)` | `(input: string) => Field[]` — compile an inline field string |
217
+ | `parseFields(spec)` | `(input: string) => Field[]` — compile an inline field string (`unique(...)`/`template(...)` aware) |
168
218
  | `parseJsonSchema(obj)` | `(schema: unknown) => Field[]` — compile a JSON schema object |
169
- | `generateRows(fields, opts)` | `(Field[], { count?, seed?, locale? }) => Record<string, unknown>[]` |
219
+ | `generateRows(fields, opts)` | `(Field[], { count?, seed?, locale? }) => Record<string, unknown>[]` — enforces `unique` fields |
170
220
  | `generateValues(spec, opts)` | `(specStr: string, { count?, seed?, locale? }) => unknown[]` |
171
- | `formatRows(rows, opts)` | `(rows, { format, pretty?, table? }) => string` |
221
+ | `parseRelations(obj)` | `(schema: unknown) => CompiledEntity[]` — compile a relations schema |
222
+ | `generateDataset(entities, opts)` | `(CompiledEntity[], opts) => Record<string, Row[]>` — linked data with FKs |
223
+ | `generateRelations(obj, opts)` | parse + generate a linked dataset in one call |
224
+ | `formatRows(rows, opts)` | `(rows, { format, pretty?, table?, ddl? }) => string` |
225
+ | `formatDataset(dataset, opts)` | format a `{ entity: rows }` dataset (JSON object / per-table SQL / sections) |
172
226
  | `formatValues(values, col, opts)` | scalar-value formatter |
173
- | `toCsv(rows)` / `toSql(rows, table)` | direct formatters |
227
+ | `toCsv(rows)` / `toSql(rows, table, { ddl? })` | direct formatters |
228
+ | `toCreateTable(rows, table)` / `inferSqlType(values)` | DDL builder + column-type inference |
229
+ | `compileTemplate(body, compile)` | build a template-field resolver |
174
230
  | `sqlValue(v)` / `sqlIdent(name)` | SQL escaping primitives |
175
- | `generators` / `callGen(name, ctx, args)` | the generator registry |
231
+ | `generators` / `callGen(name, ctx, args)` | the generator registry (built-ins + 0.2.0 extras) |
176
232
  | `RNG` | the seeded mulberry32 PRNG (`int`, `float`, `bool`, `pick`, `weighted`, `uuid`, …) |
177
233
  | `slugify(str)` | accent/punctuation-safe URL slug |
178
234
 
@@ -180,9 +236,9 @@ Types (`Field`, `Spec`, `Format`, `FormatOptions`, `GenContext`, `Locale`, …)
180
236
 
181
237
  ## Limitations
182
238
 
183
- - Data is **plausible, not real** — it's for tests and demos, never production identities.
184
- - Locales are `en` and `ne` only; `en` is generic English/US-ish, not per-country.
185
- - Relations are best-effort via `int()` id ranges — there is no cross-table referential engine.
239
+ - Data is **plausible, not real** — it's for tests and demos, never production identities. `creditCardMasked`/`iban`/`bic` are structurally-shaped placeholders, never valid instruments.
240
+ - Locales are `en`, `ne`, `es` and `fr`; `en` is generic English/US-ish, not per-country.
241
+ - Relations resolve foreign keys from the parent's generated column (guaranteed to exist); a `ref(...)` must point at an entity declared earlier in the schema.
186
242
  - Determinism holds for a given package version; generator internals may evolve across minor versions.
187
243
  - Time-based generators (`past`, `future`, `recent`) are anchored to the current clock, so their absolute instants shift day to day (still deterministic within a run given a seed).
188
244