tina4-nodejs 3.13.125 → 3.13.131

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.
@@ -8871,6 +8871,7 @@ var init_mssql = __esm({
8871
8871
  translated = SQLTranslator.concatPipesToFunc(translated);
8872
8872
  translated = SQLTranslator.ilikeToLike(translated);
8873
8873
  translated = SQLTranslator.autoIncrementSyntax(translated, "mssql");
8874
+ translated = SQLTranslator.booleanToInt(translated);
8874
8875
  translated = SQLTranslator.ddlTypes(translated, "mssql");
8875
8876
  return translated;
8876
8877
  }
@@ -15455,7 +15456,7 @@ function mulberry32(seed) {
15455
15456
  return ((t ^ t >>> 14) >>> 0) / 4294967296;
15456
15457
  };
15457
15458
  }
15458
- var FIRST_NAMES, LAST_NAMES, DOMAINS, WORDS, CITIES, COUNTRIES, STREETS, JOB_TITLES, CURRENCIES, FakeData;
15459
+ var FIRST_NAMES, LAST_NAMES, DOMAINS, WORDS, CITIES, COUNTRIES, STREETS, JOB_TITLES, CURRENCIES, PRODUCT_ADJECTIVES, PRODUCT_NOUNS, FakeData;
15459
15460
  var init_fakeData = __esm({
15460
15461
  "src/fakeData.ts"() {
15461
15462
  "use strict";
@@ -15717,6 +15718,59 @@ var init_fakeData = __esm({
15717
15718
  "ZAR",
15718
15719
  "INR"
15719
15720
  ];
15721
+ PRODUCT_ADJECTIVES = [
15722
+ "Wireless",
15723
+ "Organic",
15724
+ "Premium",
15725
+ "Classic",
15726
+ "Eco",
15727
+ "Smart",
15728
+ "Portable",
15729
+ "Deluxe",
15730
+ "Compact",
15731
+ "Rustic",
15732
+ "Handcrafted",
15733
+ "Vintage",
15734
+ "Modern",
15735
+ "Ergonomic",
15736
+ "Stainless",
15737
+ "Bamboo",
15738
+ "Recycled",
15739
+ "Artisan",
15740
+ "Professional",
15741
+ "Ultra",
15742
+ "Insulated",
15743
+ "Lightweight",
15744
+ "Adjustable",
15745
+ "Foldable"
15746
+ ];
15747
+ PRODUCT_NOUNS = [
15748
+ "Keyboard",
15749
+ "Coffee Beans",
15750
+ "Backpack",
15751
+ "Water Bottle",
15752
+ "Desk Lamp",
15753
+ "Headphones",
15754
+ "Notebook",
15755
+ "Sneakers",
15756
+ "Sunglasses",
15757
+ "Wallet",
15758
+ "Mug",
15759
+ "Chair",
15760
+ "Blender",
15761
+ "Speaker",
15762
+ "Charger",
15763
+ "Umbrella",
15764
+ "Toothbrush",
15765
+ "Jacket",
15766
+ "Watch",
15767
+ "Kettle",
15768
+ "Picture Frame",
15769
+ "Planter",
15770
+ "Cutlery Set",
15771
+ "Yoga Mat",
15772
+ "Phone Case"
15773
+ ];
15720
15774
  FakeData = class _FakeData {
15721
15775
  rng;
15722
15776
  seeded;
@@ -15788,6 +15842,16 @@ var init_fakeData = __esm({
15788
15842
  jobTitle() {
15789
15843
  return this.pick(JOB_TITLES);
15790
15844
  }
15845
+ /**
15846
+ * A plausible product name, e.g. "Wireless Keyboard" or "Organic Coffee
15847
+ * Beans" — an adjective + noun from the product vocabulary. Deterministic
15848
+ * under a seed like every other generator (draws from the same instance
15849
+ * PRNG). Used to seed a generic `name` column on a product-ish table instead
15850
+ * of a person name (see the ORM FakeData `forField` heuristic).
15851
+ */
15852
+ product() {
15853
+ return `${this.pick(PRODUCT_ADJECTIVES)} ${this.pick(PRODUCT_NOUNS)}`;
15854
+ }
15791
15855
  paragraph(sentences = 4) {
15792
15856
  const parts = [];
15793
15857
  for (let i = 0; i < sentences; i++) {
@@ -15911,11 +15975,27 @@ var init_fakeData = __esm({
15911
15975
  });
15912
15976
 
15913
15977
  // ../orm/src/fakeData.ts
15914
- var FakeData2;
15978
+ function isProductTable(table2) {
15979
+ const t = (table2 ?? "").toLowerCase();
15980
+ return PRODUCT_TABLE_HINTS.some((hint) => t.includes(hint));
15981
+ }
15982
+ var PRODUCT_TABLE_HINTS, FakeData2;
15915
15983
  var init_fakeData2 = __esm({
15916
15984
  "../orm/src/fakeData.ts"() {
15917
15985
  "use strict";
15918
15986
  init_fakeData();
15987
+ PRODUCT_TABLE_HINTS = [
15988
+ "product",
15989
+ "item",
15990
+ "catalog",
15991
+ "inventory",
15992
+ "goods",
15993
+ "merchandise",
15994
+ "sku",
15995
+ "listing",
15996
+ "stock",
15997
+ "ware"
15998
+ ];
15919
15999
  FakeData2 = class extends FakeData {
15920
16000
  constructor(seed) {
15921
16001
  super(seed);
@@ -15937,8 +16017,11 @@ var init_fakeData2 = __esm({
15937
16017
  *
15938
16018
  * @param fieldDef - An ORM FieldDefinition object
15939
16019
  * @param columnName - Optional column name for heuristic matching (e.g. "email", "phone")
16020
+ * @param table - Optional table/model name. A generic `name`/`full_name`
16021
+ * column on a product-ish table (see {@link isProductTable}) gets a
16022
+ * product name; with no table context it stays a person name (back-compat).
15940
16023
  */
15941
- forField(fieldDef, columnName) {
16024
+ forField(fieldDef, columnName, table2) {
15942
16025
  if (fieldDef.primaryKey && fieldDef.autoIncrement) {
15943
16026
  return void 0;
15944
16027
  }
@@ -15948,7 +16031,9 @@ var init_fakeData2 = __esm({
15948
16031
  const col = (columnName ?? "").toLowerCase();
15949
16032
  if (col.includes("email")) return this.email();
15950
16033
  if (col.includes("phone") || col.includes("mobile") || col.includes("tel")) return this.phone();
15951
- if (col === "name" || col === "full_name" || col === "fullname") return this.name();
16034
+ if (col === "name" || col === "full_name" || col === "fullname") {
16035
+ return isProductTable(table2) ? this.product() : this.name();
16036
+ }
15952
16037
  if (col === "first_name" || col === "firstname") return this.firstName();
15953
16038
  if (col === "last_name" || col === "lastname" || col === "surname") return this.lastName();
15954
16039
  if (col.includes("address")) return this.address();
@@ -16035,7 +16120,7 @@ async function autoFieldMap(db, table2, fake = new FakeData2()) {
16035
16120
  continue;
16036
16121
  }
16037
16122
  const fieldType = sqlTypeToFieldType(sqlType);
16038
- fieldMap[name] = () => fake.forField({ type: fieldType }, name);
16123
+ fieldMap[name] = () => fake.forField({ type: fieldType }, name, table2);
16039
16124
  }
16040
16125
  return fieldMap;
16041
16126
  }
@@ -16181,7 +16266,7 @@ async function seedOrm(ormClass, count = 10, overrides, seed, opts, fkPools) {
16181
16266
  } else if (pools[name] && pools[name].length > 0) {
16182
16267
  attrs[name] = fake.choice(pools[name]);
16183
16268
  } else {
16184
- attrs[name] = fake.forField(def, name);
16269
+ attrs[name] = fake.forField(def, name, modelName);
16185
16270
  }
16186
16271
  }
16187
16272
  validateTypes(fields, attrs, modelName);
@@ -24408,6 +24493,7 @@ __export(generate_exports, {
24408
24493
  parseEvery: () => parseEvery,
24409
24494
  parseFields: () => parseFields,
24410
24495
  pluralizeReserved: () => pluralizeReserved,
24496
+ resolveTable: () => resolveTable,
24411
24497
  toPascal: () => toPascal,
24412
24498
  toSnake: () => toSnake,
24413
24499
  toTableName: () => toTableName
@@ -24450,12 +24536,35 @@ function toTableName(name) {
24450
24536
  from: raw,
24451
24537
  to: safe,
24452
24538
  reason: `SQL reserved word '${raw}' would break CREATE TABLE`,
24453
- override: `--table ${raw} --quote (requires quoted-identifier mode, not yet implemented)`
24539
+ override: `--table-name <name> (table names interpolate unquoted; forcing a reserved name is yours to quote in raw SQL)`
24454
24540
  });
24455
24541
  return safe;
24456
24542
  }
24457
24543
  return raw;
24458
24544
  }
24545
+ function resolveTable(name, flags, opts = {}) {
24546
+ const announce = opts.announce ?? false;
24547
+ const override = (flags ?? {})["table-name"];
24548
+ if (typeof override === "string" && override) {
24549
+ if (announce && SQL_RESERVED_TABLE_NAMES.has(toSnake(override))) {
24550
+ console.error(
24551
+ ` ! table_name '${override}' is a SQL reserved word. Tina4 interpolates table names UNQUOTED, so the ORM's generated SQL will fail on it -- quote it yourself in raw SQL and migrations.`
24552
+ );
24553
+ }
24554
+ return override;
24555
+ }
24556
+ const bare = toSnake(name);
24557
+ if (!SQL_RESERVED_TABLE_NAMES.has(bare)) {
24558
+ return bare;
24559
+ }
24560
+ const table2 = toTableName(name);
24561
+ if (announce) {
24562
+ console.error(
24563
+ ` \xB7 '${bare}' is a SQL reserved word; using table_name '${table2}' (Tina4 interpolates table names unquoted). Override with --table-name <name>.`
24564
+ );
24565
+ }
24566
+ return table2;
24567
+ }
24459
24568
  function resetResolution(target, input, opts) {
24460
24569
  __resolution.target = target;
24461
24570
  __resolution.input = input;
@@ -24554,10 +24663,11 @@ function printResolution() {
24554
24663
  lines.push(` migration ${b.migration_path}`);
24555
24664
  }
24556
24665
  const reserved = b.transformations.find((t) => t.kind === "reserved_word_pluralize");
24557
- if (reserved && reserved.from && reserved.override) {
24666
+ if (reserved && reserved.from) {
24558
24667
  lines.push("");
24559
- lines.push(` To keep the raw name '${reserved.from}' as the table:`);
24560
- lines.push(` tina4nodejs generate ${__resolution.target} ${__resolution.input.name} ${reserved.override}`);
24668
+ lines.push(` To set the table name yourself:`);
24669
+ lines.push(` tina4nodejs generate ${__resolution.target} ${__resolution.input.name} --table-name <name>`);
24670
+ lines.push(` Tina4 interpolates table names unquoted; if you force the reserved '${reserved.from}', you own the quoting in raw SQL.`);
24561
24671
  }
24562
24672
  if (b.test_paths && b.test_paths.length > 0) {
24563
24673
  lines.push("");
@@ -24693,6 +24803,7 @@ async function generate(what, name, extraArgs = []) {
24693
24803
  console.error(" Usage: tina4nodejs generate <what> <name> [options]");
24694
24804
  console.error(` Generators: ${GENERATOR_LIST}`);
24695
24805
  console.error(' Options: --fields "name:string,price:float" --model ModelName');
24806
+ console.error(" --table-name <name> force the model's table name (else derived; reserved words auto-pluralise)");
24696
24807
  console.error(" --public open a route's writes (default: secure)");
24697
24808
  console.error(' --every 5m | --cron "\u2026" service schedule');
24698
24809
  console.error(" --json emit machine-readable resolution envelope on stdout");
@@ -24743,7 +24854,7 @@ async function generateProgrammatic(what, name, extraArgs = []) {
24743
24854
  }
24744
24855
  function generateModel(name, flags, emitTest = true) {
24745
24856
  const fields = fieldsOrDefault(flags.fields || "");
24746
- const table2 = toTableName(name);
24857
+ const table2 = resolveTable(name, flags, { announce: true });
24747
24858
  const dir = resolve10("src/models");
24748
24859
  ensureDir(dir);
24749
24860
  const path8 = join20(dir, `${name}.ts`);
@@ -24794,7 +24905,7 @@ function generateRoute(name, flags, emitTest = true) {
24794
24905
  if (__resolution.target === "route") {
24795
24906
  setResolutionField("file_path", `src/routes/api/${routePath}/get.ts`);
24796
24907
  }
24797
- const table2 = model ? toTableName(model) : "";
24908
+ const table2 = model ? resolveTable(model, flags, { announce: false }) : "";
24798
24909
  const modelImportBase = model ? `import ${model} from "../../../models/${model}.js";
24799
24910
  ` : "";
24800
24911
  const modelImportId = model ? `import ${model} from "../../../../models/${model}.js";
@@ -25015,7 +25126,7 @@ ${aiFill(`delete_${singular}`, {
25015
25126
  }
25016
25127
  }
25017
25128
  function generateCrud(name, flags) {
25018
- const table2 = toTableName(name);
25129
+ const table2 = resolveTable(name, flags, { announce: false });
25019
25130
  const routeName = toPlural(table2);
25020
25131
  const isPublic = Boolean(flags.public);
25021
25132
  if (!__resolution.jsonMode) console.log(`
@@ -25042,7 +25153,7 @@ function generateMigration(name, flags, fieldsOverride, tableOverride, emitTest
25042
25153
  table2 = tableOverride;
25043
25154
  } else {
25044
25155
  const raw = name.replace(/^create_/, "").replace(/^add_/, "").replace(/^drop_/, "");
25045
- table2 = toTableName(raw);
25156
+ table2 = resolveTable(raw, flags, { announce: false });
25046
25157
  }
25047
25158
  if (__resolution.target === "migration") {
25048
25159
  setResolutionField("table_name", table2);
@@ -25268,7 +25379,7 @@ void test${titleName};
25268
25379
  }
25269
25380
  function generateForm(name, flags) {
25270
25381
  const fields = fieldsOrDefault(flags.fields || "");
25271
- const table2 = toTableName(name);
25382
+ const table2 = resolveTable(name, flags, { announce: false });
25272
25383
  const routeName = toPlural(table2);
25273
25384
  const inputTypes = {
25274
25385
  string: "text",
@@ -25334,7 +25445,7 @@ function generateForm(name, flags) {
25334
25445
  }
25335
25446
  function generateView(name, flags) {
25336
25447
  const fields = fieldsOrDefault(flags.fields || "");
25337
- const table2 = toTableName(name);
25448
+ const table2 = resolveTable(name, flags, { announce: false });
25338
25449
  const routeName = toPlural(table2);
25339
25450
  const cols = fields.map(([f]) => f);
25340
25451
  const dir = resolve10("src/templates/pages");
@@ -25689,8 +25800,8 @@ ${rules} validator.required("name"); // starter rule (matches the model's def
25689
25800
  writeFileSafe(path8, content);
25690
25801
  emitValidatorTest(name, toSnake(name), toPascal(name));
25691
25802
  }
25692
- function generateSeeder(name, _flags) {
25693
- const table2 = toTableName(name);
25803
+ function generateSeeder(name, flags) {
25804
+ const table2 = resolveTable(name, flags, { announce: false });
25694
25805
  const dir = resolve10("src/seeds");
25695
25806
  ensureDir(dir);
25696
25807
  const path8 = join20(dir, `${table2}_seeder.ts`);
@@ -26315,7 +26426,7 @@ var init_generate = __esm({
26315
26426
  TINA4_EDIT_MARKER = /^\s*(?:\/\/|--|\{#|#)\s*tina4:edit\s+(.+?)(?:\s*#\})?\s*$/;
26316
26427
  DEFAULT_FIELDS = [["name", "string"]];
26317
26428
  GENERATORS = {
26318
- model: { handler: generateModel, usage: '<Name> [--fields "name:string,price:float"]', summary: "ORM model + matching migration" },
26429
+ model: { handler: generateModel, usage: '<Name> [--fields "name:string,price:float"] [--table-name <name>]', summary: "ORM model + matching migration" },
26319
26430
  route: { handler: generateRoute, usage: "<name> [--model Name] [--public]", summary: "CRUD route file, secure by default (--public opens writes)" },
26320
26431
  crud: { handler: generateCrud, usage: '<Name> [--fields "..."] [--public]', summary: "Model + migration + routes + form + view + test" },
26321
26432
  migration: { handler: (n, f) => generateMigration(n, f, void 0, void 0, !f["no-test"]), usage: "<description>", summary: "Timestamped migration file (UP/DOWN)" },
@@ -22,7 +22,10 @@ import { resolve, join } from "node:path";
22
22
 
23
23
  // ── Word Banks ───────────────────────────────────────────────────
24
24
 
25
- const FIRST_NAMES = [
25
+ // Exported (not via the barrel) so seeding tests can build the disjoint
26
+ // product-vs-person vocabulary check, mirroring the Python master's
27
+ // `_FIRST_NAMES` import. Internal-but-importable, like Python's underscore name.
28
+ export const FIRST_NAMES = [
26
29
  "Alice", "Bob", "Charlie", "Diana", "Eve", "Frank", "Grace", "Henry",
27
30
  "Ivy", "Jack", "Kate", "Leo", "Mia", "Noah", "Olivia", "Pete",
28
31
  "Quinn", "Rose", "Sam", "Tina", "Uma", "Vince", "Wendy", "Xander",
@@ -32,7 +35,7 @@ const FIRST_NAMES = [
32
35
  "Zara", "Amber", "Blake", "Clara",
33
36
  ];
34
37
 
35
- const LAST_NAMES = [
38
+ export const LAST_NAMES = [
36
39
  "Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller",
37
40
  "Davis", "Rodriguez", "Martinez", "Hernandez", "Lopez", "Wilson",
38
41
  "Anderson", "Thomas", "Taylor", "Moore", "Jackson", "Martin", "Lee",
@@ -88,6 +91,27 @@ const CURRENCIES = [
88
91
  "SEK", "NZD", "MXN", "SGD", "HKD", "NOK", "ZAR", "INR",
89
92
  ];
90
93
 
94
+ // Product-name vocabulary (adjective + noun). Seeds a generic `name` column on
95
+ // a product-ish table with "Wireless Keyboard" instead of a person name.
96
+ // Exported (not via the barrel) so seeding tests can assert product-vs-person
97
+ // disjointness — mirrors the Python master's `_PRODUCT_ADJECTIVES`. The two
98
+ // vocabularies are disjoint from FIRST_NAMES by construction, so the FIRST word
99
+ // of a generated value tells which generator ran.
100
+ export const PRODUCT_ADJECTIVES = [
101
+ "Wireless", "Organic", "Premium", "Classic", "Eco", "Smart", "Portable",
102
+ "Deluxe", "Compact", "Rustic", "Handcrafted", "Vintage", "Modern",
103
+ "Ergonomic", "Stainless", "Bamboo", "Recycled", "Artisan", "Professional",
104
+ "Ultra", "Insulated", "Lightweight", "Adjustable", "Foldable",
105
+ ];
106
+
107
+ export const PRODUCT_NOUNS = [
108
+ "Keyboard", "Coffee Beans", "Backpack", "Water Bottle", "Desk Lamp",
109
+ "Headphones", "Notebook", "Sneakers", "Sunglasses", "Wallet", "Mug",
110
+ "Chair", "Blender", "Speaker", "Charger", "Umbrella", "Toothbrush",
111
+ "Jacket", "Watch", "Kettle", "Picture Frame", "Planter", "Cutlery Set",
112
+ "Yoga Mat", "Phone Case",
113
+ ];
114
+
91
115
  // ── Seeded PRNG (mulberry32) ─────────────────────────────────────
92
116
 
93
117
  /**
@@ -193,6 +217,17 @@ export class FakeData {
193
217
  return this.pick(JOB_TITLES);
194
218
  }
195
219
 
220
+ /**
221
+ * A plausible product name, e.g. "Wireless Keyboard" or "Organic Coffee
222
+ * Beans" — an adjective + noun from the product vocabulary. Deterministic
223
+ * under a seed like every other generator (draws from the same instance
224
+ * PRNG). Used to seed a generic `name` column on a product-ish table instead
225
+ * of a person name (see the ORM FakeData `forField` heuristic).
226
+ */
227
+ product(): string {
228
+ return `${this.pick(PRODUCT_ADJECTIVES)} ${this.pick(PRODUCT_NOUNS)}`;
229
+ }
230
+
196
231
  paragraph(sentences = 4): string {
197
232
  const parts: string[] = [];
198
233
  for (let i = 0; i < sentences; i++) {
@@ -12761,6 +12761,7 @@ __export(generate_exports, {
12761
12761
  parseEvery: () => parseEvery,
12762
12762
  parseFields: () => parseFields,
12763
12763
  pluralizeReserved: () => pluralizeReserved,
12764
+ resolveTable: () => resolveTable,
12764
12765
  toPascal: () => toPascal,
12765
12766
  toSnake: () => toSnake,
12766
12767
  toTableName: () => toTableName
@@ -12803,12 +12804,35 @@ function toTableName(name) {
12803
12804
  from: raw,
12804
12805
  to: safe,
12805
12806
  reason: `SQL reserved word '${raw}' would break CREATE TABLE`,
12806
- override: `--table ${raw} --quote (requires quoted-identifier mode, not yet implemented)`
12807
+ override: `--table-name <name> (table names interpolate unquoted; forcing a reserved name is yours to quote in raw SQL)`
12807
12808
  });
12808
12809
  return safe;
12809
12810
  }
12810
12811
  return raw;
12811
12812
  }
12813
+ function resolveTable(name, flags, opts = {}) {
12814
+ const announce = opts.announce ?? false;
12815
+ const override = (flags ?? {})["table-name"];
12816
+ if (typeof override === "string" && override) {
12817
+ if (announce && SQL_RESERVED_TABLE_NAMES.has(toSnake(override))) {
12818
+ console.error(
12819
+ ` ! table_name '${override}' is a SQL reserved word. Tina4 interpolates table names UNQUOTED, so the ORM's generated SQL will fail on it -- quote it yourself in raw SQL and migrations.`
12820
+ );
12821
+ }
12822
+ return override;
12823
+ }
12824
+ const bare = toSnake(name);
12825
+ if (!SQL_RESERVED_TABLE_NAMES.has(bare)) {
12826
+ return bare;
12827
+ }
12828
+ const table2 = toTableName(name);
12829
+ if (announce) {
12830
+ console.error(
12831
+ ` \xB7 '${bare}' is a SQL reserved word; using table_name '${table2}' (Tina4 interpolates table names unquoted). Override with --table-name <name>.`
12832
+ );
12833
+ }
12834
+ return table2;
12835
+ }
12812
12836
  function resetResolution(target, input, opts) {
12813
12837
  __resolution.target = target;
12814
12838
  __resolution.input = input;
@@ -12907,10 +12931,11 @@ function printResolution() {
12907
12931
  lines.push(` migration ${b.migration_path}`);
12908
12932
  }
12909
12933
  const reserved = b.transformations.find((t) => t.kind === "reserved_word_pluralize");
12910
- if (reserved && reserved.from && reserved.override) {
12934
+ if (reserved && reserved.from) {
12911
12935
  lines.push("");
12912
- lines.push(` To keep the raw name '${reserved.from}' as the table:`);
12913
- lines.push(` tina4nodejs generate ${__resolution.target} ${__resolution.input.name} ${reserved.override}`);
12936
+ lines.push(` To set the table name yourself:`);
12937
+ lines.push(` tina4nodejs generate ${__resolution.target} ${__resolution.input.name} --table-name <name>`);
12938
+ lines.push(` Tina4 interpolates table names unquoted; if you force the reserved '${reserved.from}', you own the quoting in raw SQL.`);
12914
12939
  }
12915
12940
  if (b.test_paths && b.test_paths.length > 0) {
12916
12941
  lines.push("");
@@ -13046,6 +13071,7 @@ async function generate(what, name, extraArgs = []) {
13046
13071
  console.error(" Usage: tina4nodejs generate <what> <name> [options]");
13047
13072
  console.error(` Generators: ${GENERATOR_LIST}`);
13048
13073
  console.error(' Options: --fields "name:string,price:float" --model ModelName');
13074
+ console.error(" --table-name <name> force the model's table name (else derived; reserved words auto-pluralise)");
13049
13075
  console.error(" --public open a route's writes (default: secure)");
13050
13076
  console.error(' --every 5m | --cron "\u2026" service schedule');
13051
13077
  console.error(" --json emit machine-readable resolution envelope on stdout");
@@ -13096,7 +13122,7 @@ async function generateProgrammatic(what, name, extraArgs = []) {
13096
13122
  }
13097
13123
  function generateModel(name, flags, emitTest = true) {
13098
13124
  const fields = fieldsOrDefault(flags.fields || "");
13099
- const table2 = toTableName(name);
13125
+ const table2 = resolveTable(name, flags, { announce: true });
13100
13126
  const dir = resolve6("src/models");
13101
13127
  ensureDir(dir);
13102
13128
  const path8 = join15(dir, `${name}.ts`);
@@ -13147,7 +13173,7 @@ function generateRoute(name, flags, emitTest = true) {
13147
13173
  if (__resolution.target === "route") {
13148
13174
  setResolutionField("file_path", `src/routes/api/${routePath}/get.ts`);
13149
13175
  }
13150
- const table2 = model ? toTableName(model) : "";
13176
+ const table2 = model ? resolveTable(model, flags, { announce: false }) : "";
13151
13177
  const modelImportBase = model ? `import ${model} from "../../../models/${model}.js";
13152
13178
  ` : "";
13153
13179
  const modelImportId = model ? `import ${model} from "../../../../models/${model}.js";
@@ -13368,7 +13394,7 @@ ${aiFill(`delete_${singular}`, {
13368
13394
  }
13369
13395
  }
13370
13396
  function generateCrud(name, flags) {
13371
- const table2 = toTableName(name);
13397
+ const table2 = resolveTable(name, flags, { announce: false });
13372
13398
  const routeName = toPlural(table2);
13373
13399
  const isPublic = Boolean(flags.public);
13374
13400
  if (!__resolution.jsonMode) console.log(`
@@ -13395,7 +13421,7 @@ function generateMigration(name, flags, fieldsOverride, tableOverride, emitTest
13395
13421
  table2 = tableOverride;
13396
13422
  } else {
13397
13423
  const raw = name.replace(/^create_/, "").replace(/^add_/, "").replace(/^drop_/, "");
13398
- table2 = toTableName(raw);
13424
+ table2 = resolveTable(raw, flags, { announce: false });
13399
13425
  }
13400
13426
  if (__resolution.target === "migration") {
13401
13427
  setResolutionField("table_name", table2);
@@ -13621,7 +13647,7 @@ void test${titleName};
13621
13647
  }
13622
13648
  function generateForm(name, flags) {
13623
13649
  const fields = fieldsOrDefault(flags.fields || "");
13624
- const table2 = toTableName(name);
13650
+ const table2 = resolveTable(name, flags, { announce: false });
13625
13651
  const routeName = toPlural(table2);
13626
13652
  const inputTypes = {
13627
13653
  string: "text",
@@ -13687,7 +13713,7 @@ function generateForm(name, flags) {
13687
13713
  }
13688
13714
  function generateView(name, flags) {
13689
13715
  const fields = fieldsOrDefault(flags.fields || "");
13690
- const table2 = toTableName(name);
13716
+ const table2 = resolveTable(name, flags, { announce: false });
13691
13717
  const routeName = toPlural(table2);
13692
13718
  const cols = fields.map(([f]) => f);
13693
13719
  const dir = resolve6("src/templates/pages");
@@ -14042,8 +14068,8 @@ ${rules} validator.required("name"); // starter rule (matches the model's def
14042
14068
  writeFileSafe(path8, content);
14043
14069
  emitValidatorTest(name, toSnake(name), toPascal(name));
14044
14070
  }
14045
- function generateSeeder(name, _flags) {
14046
- const table2 = toTableName(name);
14071
+ function generateSeeder(name, flags) {
14072
+ const table2 = resolveTable(name, flags, { announce: false });
14047
14073
  const dir = resolve6("src/seeds");
14048
14074
  ensureDir(dir);
14049
14075
  const path8 = join15(dir, `${table2}_seeder.ts`);
@@ -14668,7 +14694,7 @@ var init_generate = __esm({
14668
14694
  TINA4_EDIT_MARKER = /^\s*(?:\/\/|--|\{#|#)\s*tina4:edit\s+(.+?)(?:\s*#\})?\s*$/;
14669
14695
  DEFAULT_FIELDS = [["name", "string"]];
14670
14696
  GENERATORS = {
14671
- model: { handler: generateModel, usage: '<Name> [--fields "name:string,price:float"]', summary: "ORM model + matching migration" },
14697
+ model: { handler: generateModel, usage: '<Name> [--fields "name:string,price:float"] [--table-name <name>]', summary: "ORM model + matching migration" },
14672
14698
  route: { handler: generateRoute, usage: "<name> [--model Name] [--public]", summary: "CRUD route file, secure by default (--public opens writes)" },
14673
14699
  crud: { handler: generateCrud, usage: '<Name> [--fields "..."] [--public]', summary: "Model + migration + routes + form + view + test" },
14674
14700
  migration: { handler: (n, f) => generateMigration(n, f, void 0, void 0, !f["no-test"]), usage: "<description>", summary: "Timestamped migration file (UP/DOWN)" },
@@ -28123,7 +28149,7 @@ function mulberry32(seed) {
28123
28149
  return ((t ^ t >>> 14) >>> 0) / 4294967296;
28124
28150
  };
28125
28151
  }
28126
- var FIRST_NAMES, LAST_NAMES, DOMAINS, WORDS, CITIES, COUNTRIES, STREETS, JOB_TITLES, CURRENCIES, FakeData;
28152
+ var FIRST_NAMES, LAST_NAMES, DOMAINS, WORDS, CITIES, COUNTRIES, STREETS, JOB_TITLES, CURRENCIES, PRODUCT_ADJECTIVES, PRODUCT_NOUNS, FakeData;
28127
28153
  var init_fakeData = __esm({
28128
28154
  "../core/src/fakeData.ts"() {
28129
28155
  "use strict";
@@ -28385,6 +28411,59 @@ var init_fakeData = __esm({
28385
28411
  "ZAR",
28386
28412
  "INR"
28387
28413
  ];
28414
+ PRODUCT_ADJECTIVES = [
28415
+ "Wireless",
28416
+ "Organic",
28417
+ "Premium",
28418
+ "Classic",
28419
+ "Eco",
28420
+ "Smart",
28421
+ "Portable",
28422
+ "Deluxe",
28423
+ "Compact",
28424
+ "Rustic",
28425
+ "Handcrafted",
28426
+ "Vintage",
28427
+ "Modern",
28428
+ "Ergonomic",
28429
+ "Stainless",
28430
+ "Bamboo",
28431
+ "Recycled",
28432
+ "Artisan",
28433
+ "Professional",
28434
+ "Ultra",
28435
+ "Insulated",
28436
+ "Lightweight",
28437
+ "Adjustable",
28438
+ "Foldable"
28439
+ ];
28440
+ PRODUCT_NOUNS = [
28441
+ "Keyboard",
28442
+ "Coffee Beans",
28443
+ "Backpack",
28444
+ "Water Bottle",
28445
+ "Desk Lamp",
28446
+ "Headphones",
28447
+ "Notebook",
28448
+ "Sneakers",
28449
+ "Sunglasses",
28450
+ "Wallet",
28451
+ "Mug",
28452
+ "Chair",
28453
+ "Blender",
28454
+ "Speaker",
28455
+ "Charger",
28456
+ "Umbrella",
28457
+ "Toothbrush",
28458
+ "Jacket",
28459
+ "Watch",
28460
+ "Kettle",
28461
+ "Picture Frame",
28462
+ "Planter",
28463
+ "Cutlery Set",
28464
+ "Yoga Mat",
28465
+ "Phone Case"
28466
+ ];
28388
28467
  FakeData = class _FakeData {
28389
28468
  rng;
28390
28469
  seeded;
@@ -28456,6 +28535,16 @@ var init_fakeData = __esm({
28456
28535
  jobTitle() {
28457
28536
  return this.pick(JOB_TITLES);
28458
28537
  }
28538
+ /**
28539
+ * A plausible product name, e.g. "Wireless Keyboard" or "Organic Coffee
28540
+ * Beans" — an adjective + noun from the product vocabulary. Deterministic
28541
+ * under a seed like every other generator (draws from the same instance
28542
+ * PRNG). Used to seed a generic `name` column on a product-ish table instead
28543
+ * of a person name (see the ORM FakeData `forField` heuristic).
28544
+ */
28545
+ product() {
28546
+ return `${this.pick(PRODUCT_ADJECTIVES)} ${this.pick(PRODUCT_NOUNS)}`;
28547
+ }
28459
28548
  paragraph(sentences = 4) {
28460
28549
  const parts = [];
28461
28550
  for (let i = 0; i < sentences; i++) {
@@ -37766,6 +37855,7 @@ var init_mssql = __esm({
37766
37855
  translated = SQLTranslator.concatPipesToFunc(translated);
37767
37856
  translated = SQLTranslator.ilikeToLike(translated);
37768
37857
  translated = SQLTranslator.autoIncrementSyntax(translated, "mssql");
37858
+ translated = SQLTranslator.booleanToInt(translated);
37769
37859
  translated = SQLTranslator.ddlTypes(translated, "mssql");
37770
37860
  return translated;
37771
37861
  }
@@ -44338,11 +44428,27 @@ var init_baseModel = __esm({
44338
44428
  });
44339
44429
 
44340
44430
  // src/fakeData.ts
44341
- var FakeData2;
44431
+ function isProductTable(table2) {
44432
+ const t = (table2 ?? "").toLowerCase();
44433
+ return PRODUCT_TABLE_HINTS.some((hint) => t.includes(hint));
44434
+ }
44435
+ var PRODUCT_TABLE_HINTS, FakeData2;
44342
44436
  var init_fakeData2 = __esm({
44343
44437
  "src/fakeData.ts"() {
44344
44438
  "use strict";
44345
44439
  init_fakeData();
44440
+ PRODUCT_TABLE_HINTS = [
44441
+ "product",
44442
+ "item",
44443
+ "catalog",
44444
+ "inventory",
44445
+ "goods",
44446
+ "merchandise",
44447
+ "sku",
44448
+ "listing",
44449
+ "stock",
44450
+ "ware"
44451
+ ];
44346
44452
  FakeData2 = class extends FakeData {
44347
44453
  constructor(seed) {
44348
44454
  super(seed);
@@ -44364,8 +44470,11 @@ var init_fakeData2 = __esm({
44364
44470
  *
44365
44471
  * @param fieldDef - An ORM FieldDefinition object
44366
44472
  * @param columnName - Optional column name for heuristic matching (e.g. "email", "phone")
44473
+ * @param table - Optional table/model name. A generic `name`/`full_name`
44474
+ * column on a product-ish table (see {@link isProductTable}) gets a
44475
+ * product name; with no table context it stays a person name (back-compat).
44367
44476
  */
44368
- forField(fieldDef, columnName) {
44477
+ forField(fieldDef, columnName, table2) {
44369
44478
  if (fieldDef.primaryKey && fieldDef.autoIncrement) {
44370
44479
  return void 0;
44371
44480
  }
@@ -44375,7 +44484,9 @@ var init_fakeData2 = __esm({
44375
44484
  const col = (columnName ?? "").toLowerCase();
44376
44485
  if (col.includes("email")) return this.email();
44377
44486
  if (col.includes("phone") || col.includes("mobile") || col.includes("tel")) return this.phone();
44378
- if (col === "name" || col === "full_name" || col === "fullname") return this.name();
44487
+ if (col === "name" || col === "full_name" || col === "fullname") {
44488
+ return isProductTable(table2) ? this.product() : this.name();
44489
+ }
44379
44490
  if (col === "first_name" || col === "firstname") return this.firstName();
44380
44491
  if (col === "last_name" || col === "lastname" || col === "surname") return this.lastName();
44381
44492
  if (col.includes("address")) return this.address();
@@ -44462,7 +44573,7 @@ async function autoFieldMap(db, table2, fake = new FakeData2()) {
44462
44573
  continue;
44463
44574
  }
44464
44575
  const fieldType = sqlTypeToFieldType(sqlType);
44465
- fieldMap[name] = () => fake.forField({ type: fieldType }, name);
44576
+ fieldMap[name] = () => fake.forField({ type: fieldType }, name, table2);
44466
44577
  }
44467
44578
  return fieldMap;
44468
44579
  }
@@ -44608,7 +44719,7 @@ async function seedOrm(ormClass, count = 10, overrides, seed, opts, fkPools) {
44608
44719
  } else if (pools[name] && pools[name].length > 0) {
44609
44720
  attrs[name] = fake.choice(pools[name]);
44610
44721
  } else {
44611
- attrs[name] = fake.forField(def, name);
44722
+ attrs[name] = fake.forField(def, name, modelName);
44612
44723
  }
44613
44724
  }
44614
44725
  validateTypes(fields, attrs, modelName);