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.
- package/CLAUDE.md +2 -2
- package/package.json +1 -1
- package/packages/cli/dist/bin.js +413 -81
- package/packages/cli/src/bin.ts +6 -0
- package/packages/cli/src/commands/generate.ts +89 -15
- package/packages/cli/src/commands/lint.ts +360 -0
- package/packages/core/dist/index.js +130 -19
- package/packages/core/src/fakeData.ts +37 -2
- package/packages/orm/dist/index.js +130 -19
- package/packages/orm/src/adapters/mssql.ts +4 -0
- package/packages/orm/src/fakeData.ts +27 -2
- package/packages/orm/src/seeder.ts +8 -3
- package/types/cli/src/commands/generate.d.ts +29 -0
- package/types/cli/src/commands/lint.d.ts +5 -0
- package/types/core/src/fakeData.d.ts +12 -0
- package/types/orm/src/fakeData.d.ts +12 -1
|
@@ -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")
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
@@ -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
|
}
|