tina4-nodejs 3.13.125 → 3.13.130

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.
@@ -177,6 +177,10 @@ export class MssqlAdapter implements DatabaseAdapter {
177
177
  // so ONE portable migration applies here. Both are DDL-only, so DML is
178
178
  // untouched. Mirrors the Python master's mssql.py::_translate_sql.
179
179
  translated = SQLTranslator.autoIncrementSyntax(translated, "mssql");
180
+ // MSSQL has BIT, not a boolean type, so bare TRUE/FALSE must become 1/0
181
+ // (a TRUE/FALSE inside a string literal is data and is left untouched).
182
+ // Mirrors the Python master's mssql.py::_translate_sql.
183
+ translated = SQLTranslator.booleanToInt(translated);
180
184
  translated = SQLTranslator.ddlTypes(translated, "mssql");
181
185
  return translated;
182
186
  }
@@ -5,6 +5,26 @@
5
5
  import { FakeData as CoreFakeData } from "../../core/src/fakeData.js";
6
6
  import type { FieldDefinition } from "./types.js";
7
7
 
8
+ // A table/model whose name contains any of these gets product names on its
9
+ // generic `name`/`full_name` column instead of a person name. Mirrors the
10
+ // Python master's `_PRODUCT_TABLE_HINTS`.
11
+ const PRODUCT_TABLE_HINTS = [
12
+ "product", "item", "catalog", "inventory", "goods", "merchandise",
13
+ "sku", "listing", "stock", "ware",
14
+ ] as const;
15
+
16
+ /**
17
+ * True when the table/model name looks like a product catalogue, so a generic
18
+ * `name` column should seed a product name, not a person name. With NO table
19
+ * context (undefined/null/empty) this is false, so the person-name default is
20
+ * kept — back-compat. Exported (not via the barrel) so seeding tests can assert
21
+ * it directly, mirroring the Python master's `_is_product_table`.
22
+ */
23
+ export function isProductTable(table?: string | null): boolean {
24
+ const t = (table ?? "").toLowerCase();
25
+ return PRODUCT_TABLE_HINTS.some((hint) => t.includes(hint));
26
+ }
27
+
8
28
  /**
9
29
  * ORM-aware FakeData — wraps the core FakeData and adds forField()
10
30
  * which generates appropriate fake data based on an ORM FieldDefinition.
@@ -32,8 +52,11 @@ export class FakeData extends CoreFakeData {
32
52
  *
33
53
  * @param fieldDef - An ORM FieldDefinition object
34
54
  * @param columnName - Optional column name for heuristic matching (e.g. "email", "phone")
55
+ * @param table - Optional table/model name. A generic `name`/`full_name`
56
+ * column on a product-ish table (see {@link isProductTable}) gets a
57
+ * product name; with no table context it stays a person name (back-compat).
35
58
  */
36
- forField(fieldDef: FieldDefinition, columnName?: string): unknown {
59
+ forField(fieldDef: FieldDefinition, columnName?: string, table?: string): unknown {
37
60
  // Auto-increment primary keys should not be generated
38
61
  if (fieldDef.primaryKey && fieldDef.autoIncrement) {
39
62
  return undefined;
@@ -53,7 +76,9 @@ export class FakeData extends CoreFakeData {
53
76
 
54
77
  if (col.includes("email")) return this.email();
55
78
  if (col.includes("phone") || col.includes("mobile") || col.includes("tel")) return this.phone();
56
- if (col === "name" || col === "full_name" || col === "fullname") return this.name();
79
+ if (col === "name" || col === "full_name" || col === "fullname") {
80
+ return isProductTable(table) ? this.product() : this.name();
81
+ }
57
82
  if (col === "first_name" || col === "firstname") return this.firstName();
58
83
  if (col === "last_name" || col === "lastname" || col === "surname") return this.lastName();
59
84
  if (col.includes("address")) return this.address();
@@ -143,8 +143,10 @@ export async function autoFieldMap(
143
143
  }
144
144
  const fieldType = sqlTypeToFieldType(sqlType);
145
145
  // Bind the type + name per column; forField applies the name heuristics
146
- // (email/phone/name/...) before falling back to the type.
147
- fieldMap[name] = () => fake.forField({ type: fieldType }, name);
146
+ // (email/phone/name/...) before falling back to the type. The TABLE name is
147
+ // threaded so a generic `name` column on a product-ish table seeds a
148
+ // product name, not a person name (parity with the Python auto_field_map).
149
+ fieldMap[name] = () => fake.forField({ type: fieldType }, name, table);
148
150
  }
149
151
  return fieldMap;
150
152
  }
@@ -427,7 +429,10 @@ export async function seedOrm(
427
429
  } else if (pools[name] && pools[name].length > 0) {
428
430
  attrs[name] = fake.choice(pools[name]);
429
431
  } else {
430
- attrs[name] = fake.forField(def, name);
432
+ // Thread the MODEL name so a generic `name` column on a product-ish
433
+ // model seeds a product name, not a person name (Python seed_orm
434
+ // passes orm_class.__name__; modelName is name ?? tableName).
435
+ attrs[name] = fake.forField(def, name, modelName);
431
436
  }
432
437
  }
433
438
  validateTypes(fields, attrs, modelName);
@@ -10,6 +10,35 @@ export declare function pluralizeReserved(name: string): string;
10
10
  * routes and tests all agree on the same table name.
11
11
  */
12
12
  export declare function toTableName(name: string): string;
13
+ /**
14
+ * The table name a generator uses (issue #123) — honours `--table-name` and
15
+ * speaks up instead of renaming SILENTLY. Mirrors the Python master's
16
+ * `_resolve_table` (tina4-python/tina4_python/cli/__init__.py).
17
+ *
18
+ * `announce` prints the note/warning; it is TRUE only for `generateModel` (where
19
+ * the table is born). Composite generators (crud) let the model sub-call
20
+ * announce, and generators that target an EXISTING table (route/seeder/form/view/
21
+ * migration) still honour `--table-name` but stay quiet so the note is not
22
+ * repeated — the note prints exactly once per `generate`.
23
+ *
24
+ * • `--table-name <name>` wins verbatim. If that name is ITSELF a reserved word,
25
+ * warn loudly (when announcing): Tina4 interpolates table names UNQUOTED, so
26
+ * the ORM's generated SQL will fail on it — quoting it in raw SQL + migrations
27
+ * is now the developer's job (we do NOT silently quote; identifier quoting is a
28
+ * global storage invariant, not a local fix).
29
+ * • Otherwise fall back to `toTableName` (snake + reserved-word pluralise). When
30
+ * that auto-pluralises a reserved-word class name (`Order` -> `orders`), print a
31
+ * one-line NOTE (when announcing) naming the rename and the `--table-name`
32
+ * escape hatch, so the developer is informed rather than surprised.
33
+ *
34
+ * The note/warning goes to STDERR (console.error) so a `generate … --json` run
35
+ * keeps its stdout envelope pristine for a downstream `| jq`. `toTableName`'s
36
+ * `reserved_word_pluralize` envelope transformation is UNCHANGED (this ADDS the
37
+ * announce path; it does not touch the envelope contract).
38
+ */
39
+ export declare function resolveTable(name: string, flags: Record<string, string | boolean> | undefined, opts?: {
40
+ announce?: boolean;
41
+ }): string;
13
42
  /** One transformation the resolver made — visible to the caller so an AI
14
43
  * agent (or human) knows exactly why the output differs from the input. */
15
44
  export interface ResolutionTransformation {
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Lint the project's source. Exits 0 = clean, 1 = findings (parity with the
3
+ * Python master and with `tina4nodejs test`'s exit-code contract).
4
+ */
5
+ export declare function runLint(args: string[]): void;
@@ -1,3 +1,7 @@
1
+ export declare const FIRST_NAMES: string[];
2
+ export declare const LAST_NAMES: string[];
3
+ export declare const PRODUCT_ADJECTIVES: string[];
4
+ export declare const PRODUCT_NOUNS: string[];
1
5
  export declare class FakeData {
2
6
  private rng;
3
7
  private seeded;
@@ -19,6 +23,14 @@ export declare class FakeData {
19
23
  zipCode(): string;
20
24
  company(): string;
21
25
  jobTitle(): string;
26
+ /**
27
+ * A plausible product name, e.g. "Wireless Keyboard" or "Organic Coffee
28
+ * Beans" — an adjective + noun from the product vocabulary. Deterministic
29
+ * under a seed like every other generator (draws from the same instance
30
+ * PRNG). Used to seed a generic `name` column on a product-ish table instead
31
+ * of a person name (see the ORM FakeData `forField` heuristic).
32
+ */
33
+ product(): string;
22
34
  paragraph(sentences?: number): string;
23
35
  sentence(words?: number): string;
24
36
  word(): string;
@@ -1,5 +1,13 @@
1
1
  import { FakeData as CoreFakeData } from "../../core/src/fakeData.js";
2
2
  import type { FieldDefinition } from "./types.js";
3
+ /**
4
+ * True when the table/model name looks like a product catalogue, so a generic
5
+ * `name` column should seed a product name, not a person name. With NO table
6
+ * context (undefined/null/empty) this is false, so the person-name default is
7
+ * kept — back-compat. Exported (not via the barrel) so seeding tests can assert
8
+ * it directly, mirroring the Python master's `_is_product_table`.
9
+ */
10
+ export declare function isProductTable(table?: string | null): boolean;
3
11
  /**
4
12
  * ORM-aware FakeData — wraps the core FakeData and adds forField()
5
13
  * which generates appropriate fake data based on an ORM FieldDefinition.
@@ -17,6 +25,9 @@ export declare class FakeData extends CoreFakeData {
17
25
  *
18
26
  * @param fieldDef - An ORM FieldDefinition object
19
27
  * @param columnName - Optional column name for heuristic matching (e.g. "email", "phone")
28
+ * @param table - Optional table/model name. A generic `name`/`full_name`
29
+ * column on a product-ish table (see {@link isProductTable}) gets a
30
+ * product name; with no table context it stays a person name (back-compat).
20
31
  */
21
- forField(fieldDef: FieldDefinition, columnName?: string): unknown;
32
+ forField(fieldDef: FieldDefinition, columnName?: string, table?: string): unknown;
22
33
  }