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.
- package/CLAUDE.md +3 -3
- 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
package/packages/cli/dist/bin.js
CHANGED
|
@@ -1186,8 +1186,8 @@ function hashPassword(password, salt, iterations = 26e4) {
|
|
|
1186
1186
|
}
|
|
1187
1187
|
function checkPassword(password, hash) {
|
|
1188
1188
|
try {
|
|
1189
|
-
const
|
|
1190
|
-
const parts = hash.split(
|
|
1189
|
+
const delimiter5 = hash.includes("$") ? "$" : ":";
|
|
1190
|
+
const parts = hash.split(delimiter5);
|
|
1191
1191
|
if (parts.length !== 4 || parts[0] !== "pbkdf2_sha256") return false;
|
|
1192
1192
|
const iterations = parseInt(parts[1], 10);
|
|
1193
1193
|
const salt = parts[2];
|
|
@@ -8872,6 +8872,7 @@ var init_mssql = __esm({
|
|
|
8872
8872
|
translated = SQLTranslator.concatPipesToFunc(translated);
|
|
8873
8873
|
translated = SQLTranslator.ilikeToLike(translated);
|
|
8874
8874
|
translated = SQLTranslator.autoIncrementSyntax(translated, "mssql");
|
|
8875
|
+
translated = SQLTranslator.booleanToInt(translated);
|
|
8875
8876
|
translated = SQLTranslator.ddlTypes(translated, "mssql");
|
|
8876
8877
|
return translated;
|
|
8877
8878
|
}
|
|
@@ -12593,7 +12594,7 @@ async function removeMigrationRecord(name) {
|
|
|
12593
12594
|
[name]
|
|
12594
12595
|
);
|
|
12595
12596
|
}
|
|
12596
|
-
async function rollback(migrationsDir,
|
|
12597
|
+
async function rollback(migrationsDir, delimiter5) {
|
|
12597
12598
|
if (migrationsDir instanceof Map) {
|
|
12598
12599
|
const downFunctions = migrationsDir;
|
|
12599
12600
|
const migrations2 = await getLastBatchMigrations();
|
|
@@ -12612,7 +12613,7 @@ async function rollback(migrationsDir, delimiter4) {
|
|
|
12612
12613
|
return rolledBack2;
|
|
12613
12614
|
}
|
|
12614
12615
|
const dir = resolve5(migrationsDir ?? "migrations");
|
|
12615
|
-
const delim =
|
|
12616
|
+
const delim = delimiter5 ?? ";";
|
|
12616
12617
|
const db = getAdapter();
|
|
12617
12618
|
const migrations = await getLastBatchMigrations();
|
|
12618
12619
|
const rolledBack = [];
|
|
@@ -12663,12 +12664,12 @@ function parseSetTerm(statement) {
|
|
|
12663
12664
|
const m = statement.trim().match(SET_TERM_RE);
|
|
12664
12665
|
return m ? m[1] : null;
|
|
12665
12666
|
}
|
|
12666
|
-
function splitStatements(sql,
|
|
12667
|
+
function splitStatements(sql, delimiter5 = ";") {
|
|
12667
12668
|
sql = normalizeQuotes(sql);
|
|
12668
12669
|
const statements = [];
|
|
12669
12670
|
let current = "";
|
|
12670
12671
|
const n = sql.length;
|
|
12671
|
-
let dlen =
|
|
12672
|
+
let dlen = delimiter5.length;
|
|
12672
12673
|
let i = 0;
|
|
12673
12674
|
let inDollarBlock = false;
|
|
12674
12675
|
let inSlashBlock = false;
|
|
@@ -12737,15 +12738,15 @@ function splitStatements(sql, delimiter4 = ";") {
|
|
|
12737
12738
|
}
|
|
12738
12739
|
continue;
|
|
12739
12740
|
}
|
|
12740
|
-
if (dlen > 0 && sql.startsWith(
|
|
12741
|
+
if (dlen > 0 && sql.startsWith(delimiter5, i)) {
|
|
12741
12742
|
i += dlen;
|
|
12742
12743
|
const stmt2 = current.trim();
|
|
12743
12744
|
current = "";
|
|
12744
12745
|
if (stmt2) {
|
|
12745
12746
|
const newTerm = parseSetTerm(stmt2);
|
|
12746
12747
|
if (newTerm !== null) {
|
|
12747
|
-
|
|
12748
|
-
dlen =
|
|
12748
|
+
delimiter5 = newTerm;
|
|
12749
|
+
dlen = delimiter5.length;
|
|
12749
12750
|
} else {
|
|
12750
12751
|
statements.push(stmt2);
|
|
12751
12752
|
}
|
|
@@ -12784,7 +12785,7 @@ function warnUnprefixedMigrations(files) {
|
|
|
12784
12785
|
async function migrate(adapter, options) {
|
|
12785
12786
|
const db = adapter ?? getAdapter();
|
|
12786
12787
|
const dir = resolve5(options?.migrationsDir ?? "migrations");
|
|
12787
|
-
const
|
|
12788
|
+
const delimiter5 = options?.delimiter ?? ";";
|
|
12788
12789
|
const result = { applied: [], skipped: [], failed: [] };
|
|
12789
12790
|
if (!existsSync6(dir)) {
|
|
12790
12791
|
return result;
|
|
@@ -12827,7 +12828,7 @@ async function migrate(adapter, options) {
|
|
|
12827
12828
|
result.skipped.push(file);
|
|
12828
12829
|
continue;
|
|
12829
12830
|
}
|
|
12830
|
-
const statements = splitStatements(sqlContent,
|
|
12831
|
+
const statements = splitStatements(sqlContent, delimiter5);
|
|
12831
12832
|
try {
|
|
12832
12833
|
await adapterStartTransaction(db);
|
|
12833
12834
|
for (const stmt of statements) {
|
|
@@ -15456,7 +15457,7 @@ function mulberry32(seed) {
|
|
|
15456
15457
|
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
15457
15458
|
};
|
|
15458
15459
|
}
|
|
15459
|
-
var FIRST_NAMES, LAST_NAMES, DOMAINS, WORDS, CITIES, COUNTRIES, STREETS, JOB_TITLES, CURRENCIES, FakeData;
|
|
15460
|
+
var FIRST_NAMES, LAST_NAMES, DOMAINS, WORDS, CITIES, COUNTRIES, STREETS, JOB_TITLES, CURRENCIES, PRODUCT_ADJECTIVES, PRODUCT_NOUNS, FakeData;
|
|
15460
15461
|
var init_fakeData = __esm({
|
|
15461
15462
|
"../core/src/fakeData.ts"() {
|
|
15462
15463
|
"use strict";
|
|
@@ -15718,6 +15719,59 @@ var init_fakeData = __esm({
|
|
|
15718
15719
|
"ZAR",
|
|
15719
15720
|
"INR"
|
|
15720
15721
|
];
|
|
15722
|
+
PRODUCT_ADJECTIVES = [
|
|
15723
|
+
"Wireless",
|
|
15724
|
+
"Organic",
|
|
15725
|
+
"Premium",
|
|
15726
|
+
"Classic",
|
|
15727
|
+
"Eco",
|
|
15728
|
+
"Smart",
|
|
15729
|
+
"Portable",
|
|
15730
|
+
"Deluxe",
|
|
15731
|
+
"Compact",
|
|
15732
|
+
"Rustic",
|
|
15733
|
+
"Handcrafted",
|
|
15734
|
+
"Vintage",
|
|
15735
|
+
"Modern",
|
|
15736
|
+
"Ergonomic",
|
|
15737
|
+
"Stainless",
|
|
15738
|
+
"Bamboo",
|
|
15739
|
+
"Recycled",
|
|
15740
|
+
"Artisan",
|
|
15741
|
+
"Professional",
|
|
15742
|
+
"Ultra",
|
|
15743
|
+
"Insulated",
|
|
15744
|
+
"Lightweight",
|
|
15745
|
+
"Adjustable",
|
|
15746
|
+
"Foldable"
|
|
15747
|
+
];
|
|
15748
|
+
PRODUCT_NOUNS = [
|
|
15749
|
+
"Keyboard",
|
|
15750
|
+
"Coffee Beans",
|
|
15751
|
+
"Backpack",
|
|
15752
|
+
"Water Bottle",
|
|
15753
|
+
"Desk Lamp",
|
|
15754
|
+
"Headphones",
|
|
15755
|
+
"Notebook",
|
|
15756
|
+
"Sneakers",
|
|
15757
|
+
"Sunglasses",
|
|
15758
|
+
"Wallet",
|
|
15759
|
+
"Mug",
|
|
15760
|
+
"Chair",
|
|
15761
|
+
"Blender",
|
|
15762
|
+
"Speaker",
|
|
15763
|
+
"Charger",
|
|
15764
|
+
"Umbrella",
|
|
15765
|
+
"Toothbrush",
|
|
15766
|
+
"Jacket",
|
|
15767
|
+
"Watch",
|
|
15768
|
+
"Kettle",
|
|
15769
|
+
"Picture Frame",
|
|
15770
|
+
"Planter",
|
|
15771
|
+
"Cutlery Set",
|
|
15772
|
+
"Yoga Mat",
|
|
15773
|
+
"Phone Case"
|
|
15774
|
+
];
|
|
15721
15775
|
FakeData = class _FakeData {
|
|
15722
15776
|
rng;
|
|
15723
15777
|
seeded;
|
|
@@ -15789,6 +15843,16 @@ var init_fakeData = __esm({
|
|
|
15789
15843
|
jobTitle() {
|
|
15790
15844
|
return this.pick(JOB_TITLES);
|
|
15791
15845
|
}
|
|
15846
|
+
/**
|
|
15847
|
+
* A plausible product name, e.g. "Wireless Keyboard" or "Organic Coffee
|
|
15848
|
+
* Beans" — an adjective + noun from the product vocabulary. Deterministic
|
|
15849
|
+
* under a seed like every other generator (draws from the same instance
|
|
15850
|
+
* PRNG). Used to seed a generic `name` column on a product-ish table instead
|
|
15851
|
+
* of a person name (see the ORM FakeData `forField` heuristic).
|
|
15852
|
+
*/
|
|
15853
|
+
product() {
|
|
15854
|
+
return `${this.pick(PRODUCT_ADJECTIVES)} ${this.pick(PRODUCT_NOUNS)}`;
|
|
15855
|
+
}
|
|
15792
15856
|
paragraph(sentences = 4) {
|
|
15793
15857
|
const parts = [];
|
|
15794
15858
|
for (let i = 0; i < sentences; i++) {
|
|
@@ -15912,11 +15976,27 @@ var init_fakeData = __esm({
|
|
|
15912
15976
|
});
|
|
15913
15977
|
|
|
15914
15978
|
// ../orm/src/fakeData.ts
|
|
15915
|
-
|
|
15979
|
+
function isProductTable(table2) {
|
|
15980
|
+
const t = (table2 ?? "").toLowerCase();
|
|
15981
|
+
return PRODUCT_TABLE_HINTS.some((hint) => t.includes(hint));
|
|
15982
|
+
}
|
|
15983
|
+
var PRODUCT_TABLE_HINTS, FakeData2;
|
|
15916
15984
|
var init_fakeData2 = __esm({
|
|
15917
15985
|
"../orm/src/fakeData.ts"() {
|
|
15918
15986
|
"use strict";
|
|
15919
15987
|
init_fakeData();
|
|
15988
|
+
PRODUCT_TABLE_HINTS = [
|
|
15989
|
+
"product",
|
|
15990
|
+
"item",
|
|
15991
|
+
"catalog",
|
|
15992
|
+
"inventory",
|
|
15993
|
+
"goods",
|
|
15994
|
+
"merchandise",
|
|
15995
|
+
"sku",
|
|
15996
|
+
"listing",
|
|
15997
|
+
"stock",
|
|
15998
|
+
"ware"
|
|
15999
|
+
];
|
|
15920
16000
|
FakeData2 = class extends FakeData {
|
|
15921
16001
|
constructor(seed) {
|
|
15922
16002
|
super(seed);
|
|
@@ -15938,8 +16018,11 @@ var init_fakeData2 = __esm({
|
|
|
15938
16018
|
*
|
|
15939
16019
|
* @param fieldDef - An ORM FieldDefinition object
|
|
15940
16020
|
* @param columnName - Optional column name for heuristic matching (e.g. "email", "phone")
|
|
16021
|
+
* @param table - Optional table/model name. A generic `name`/`full_name`
|
|
16022
|
+
* column on a product-ish table (see {@link isProductTable}) gets a
|
|
16023
|
+
* product name; with no table context it stays a person name (back-compat).
|
|
15941
16024
|
*/
|
|
15942
|
-
forField(fieldDef, columnName) {
|
|
16025
|
+
forField(fieldDef, columnName, table2) {
|
|
15943
16026
|
if (fieldDef.primaryKey && fieldDef.autoIncrement) {
|
|
15944
16027
|
return void 0;
|
|
15945
16028
|
}
|
|
@@ -15949,7 +16032,9 @@ var init_fakeData2 = __esm({
|
|
|
15949
16032
|
const col = (columnName ?? "").toLowerCase();
|
|
15950
16033
|
if (col.includes("email")) return this.email();
|
|
15951
16034
|
if (col.includes("phone") || col.includes("mobile") || col.includes("tel")) return this.phone();
|
|
15952
|
-
if (col === "name" || col === "full_name" || col === "fullname")
|
|
16035
|
+
if (col === "name" || col === "full_name" || col === "fullname") {
|
|
16036
|
+
return isProductTable(table2) ? this.product() : this.name();
|
|
16037
|
+
}
|
|
15953
16038
|
if (col === "first_name" || col === "firstname") return this.firstName();
|
|
15954
16039
|
if (col === "last_name" || col === "lastname" || col === "surname") return this.lastName();
|
|
15955
16040
|
if (col.includes("address")) return this.address();
|
|
@@ -16036,7 +16121,7 @@ async function autoFieldMap(db, table2, fake = new FakeData2()) {
|
|
|
16036
16121
|
continue;
|
|
16037
16122
|
}
|
|
16038
16123
|
const fieldType = sqlTypeToFieldType(sqlType);
|
|
16039
|
-
fieldMap[name] = () => fake.forField({ type: fieldType }, name);
|
|
16124
|
+
fieldMap[name] = () => fake.forField({ type: fieldType }, name, table2);
|
|
16040
16125
|
}
|
|
16041
16126
|
return fieldMap;
|
|
16042
16127
|
}
|
|
@@ -16182,7 +16267,7 @@ async function seedOrm(ormClass, count = 10, overrides, seed, opts, fkPools) {
|
|
|
16182
16267
|
} else if (pools[name] && pools[name].length > 0) {
|
|
16183
16268
|
attrs[name] = fake.choice(pools[name]);
|
|
16184
16269
|
} else {
|
|
16185
|
-
attrs[name] = fake.forField(def, name);
|
|
16270
|
+
attrs[name] = fake.forField(def, name, modelName);
|
|
16186
16271
|
}
|
|
16187
16272
|
}
|
|
16188
16273
|
validateTypes(fields, attrs, modelName);
|
|
@@ -21217,14 +21302,14 @@ function extractBoundary(contentType) {
|
|
|
21217
21302
|
function parseMultipart(body, boundary) {
|
|
21218
21303
|
const fields = {};
|
|
21219
21304
|
const files = {};
|
|
21220
|
-
const
|
|
21305
|
+
const delimiter5 = Buffer.from(`--${boundary}`);
|
|
21221
21306
|
const closeDelimiter = Buffer.from(`--${boundary}--`);
|
|
21222
21307
|
const crlf = Buffer.from("\r\n");
|
|
21223
21308
|
const doubleCrlf = Buffer.from("\r\n\r\n");
|
|
21224
21309
|
let offset = 0;
|
|
21225
|
-
const firstIdx = bufferIndexOf(body,
|
|
21310
|
+
const firstIdx = bufferIndexOf(body, delimiter5, offset);
|
|
21226
21311
|
if (firstIdx === -1) return { fields, files };
|
|
21227
|
-
offset = firstIdx +
|
|
21312
|
+
offset = firstIdx + delimiter5.length;
|
|
21228
21313
|
if (body[offset] === 13 && body[offset + 1] === 10) {
|
|
21229
21314
|
offset += 2;
|
|
21230
21315
|
}
|
|
@@ -21233,7 +21318,7 @@ function parseMultipart(body, boundary) {
|
|
|
21233
21318
|
if (headersEnd === -1) break;
|
|
21234
21319
|
const headersStr = body.subarray(offset, headersEnd).toString("utf-8");
|
|
21235
21320
|
offset = headersEnd + doubleCrlf.length;
|
|
21236
|
-
const nextDelimIdx = bufferIndexOf(body,
|
|
21321
|
+
const nextDelimIdx = bufferIndexOf(body, delimiter5, offset);
|
|
21237
21322
|
if (nextDelimIdx === -1) break;
|
|
21238
21323
|
const contentEnd = nextDelimIdx - crlf.length;
|
|
21239
21324
|
const content = body.subarray(offset, contentEnd);
|
|
@@ -21256,7 +21341,7 @@ function parseMultipart(body, boundary) {
|
|
|
21256
21341
|
} else if (disposition.name) {
|
|
21257
21342
|
fields[disposition.name] = content.toString("utf-8");
|
|
21258
21343
|
}
|
|
21259
|
-
offset = nextDelimIdx +
|
|
21344
|
+
offset = nextDelimIdx + delimiter5.length;
|
|
21260
21345
|
if (body[offset] === 45 && body[offset + 1] === 45) {
|
|
21261
21346
|
break;
|
|
21262
21347
|
}
|
|
@@ -24429,6 +24514,7 @@ __export(generate_exports, {
|
|
|
24429
24514
|
parseEvery: () => parseEvery,
|
|
24430
24515
|
parseFields: () => parseFields,
|
|
24431
24516
|
pluralizeReserved: () => pluralizeReserved,
|
|
24517
|
+
resolveTable: () => resolveTable,
|
|
24432
24518
|
toPascal: () => toPascal,
|
|
24433
24519
|
toSnake: () => toSnake,
|
|
24434
24520
|
toTableName: () => toTableName
|
|
@@ -24471,12 +24557,35 @@ function toTableName(name) {
|
|
|
24471
24557
|
from: raw,
|
|
24472
24558
|
to: safe,
|
|
24473
24559
|
reason: `SQL reserved word '${raw}' would break CREATE TABLE`,
|
|
24474
|
-
override: `--table
|
|
24560
|
+
override: `--table-name <name> (table names interpolate unquoted; forcing a reserved name is yours to quote in raw SQL)`
|
|
24475
24561
|
});
|
|
24476
24562
|
return safe;
|
|
24477
24563
|
}
|
|
24478
24564
|
return raw;
|
|
24479
24565
|
}
|
|
24566
|
+
function resolveTable(name, flags, opts = {}) {
|
|
24567
|
+
const announce = opts.announce ?? false;
|
|
24568
|
+
const override = (flags ?? {})["table-name"];
|
|
24569
|
+
if (typeof override === "string" && override) {
|
|
24570
|
+
if (announce && SQL_RESERVED_TABLE_NAMES.has(toSnake(override))) {
|
|
24571
|
+
console.error(
|
|
24572
|
+
` ! 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.`
|
|
24573
|
+
);
|
|
24574
|
+
}
|
|
24575
|
+
return override;
|
|
24576
|
+
}
|
|
24577
|
+
const bare = toSnake(name);
|
|
24578
|
+
if (!SQL_RESERVED_TABLE_NAMES.has(bare)) {
|
|
24579
|
+
return bare;
|
|
24580
|
+
}
|
|
24581
|
+
const table2 = toTableName(name);
|
|
24582
|
+
if (announce) {
|
|
24583
|
+
console.error(
|
|
24584
|
+
` \xB7 '${bare}' is a SQL reserved word; using table_name '${table2}' (Tina4 interpolates table names unquoted). Override with --table-name <name>.`
|
|
24585
|
+
);
|
|
24586
|
+
}
|
|
24587
|
+
return table2;
|
|
24588
|
+
}
|
|
24480
24589
|
function resetResolution(target, input, opts) {
|
|
24481
24590
|
__resolution.target = target;
|
|
24482
24591
|
__resolution.input = input;
|
|
@@ -24575,10 +24684,11 @@ function printResolution() {
|
|
|
24575
24684
|
lines.push(` migration ${b.migration_path}`);
|
|
24576
24685
|
}
|
|
24577
24686
|
const reserved = b.transformations.find((t) => t.kind === "reserved_word_pluralize");
|
|
24578
|
-
if (reserved && reserved.from
|
|
24687
|
+
if (reserved && reserved.from) {
|
|
24579
24688
|
lines.push("");
|
|
24580
|
-
lines.push(` To
|
|
24581
|
-
lines.push(` tina4nodejs generate ${__resolution.target} ${__resolution.input.name}
|
|
24689
|
+
lines.push(` To set the table name yourself:`);
|
|
24690
|
+
lines.push(` tina4nodejs generate ${__resolution.target} ${__resolution.input.name} --table-name <name>`);
|
|
24691
|
+
lines.push(` Tina4 interpolates table names unquoted; if you force the reserved '${reserved.from}', you own the quoting in raw SQL.`);
|
|
24582
24692
|
}
|
|
24583
24693
|
if (b.test_paths && b.test_paths.length > 0) {
|
|
24584
24694
|
lines.push("");
|
|
@@ -24714,6 +24824,7 @@ async function generate(what, name, extraArgs = []) {
|
|
|
24714
24824
|
console.error(" Usage: tina4nodejs generate <what> <name> [options]");
|
|
24715
24825
|
console.error(` Generators: ${GENERATOR_LIST}`);
|
|
24716
24826
|
console.error(' Options: --fields "name:string,price:float" --model ModelName');
|
|
24827
|
+
console.error(" --table-name <name> force the model's table name (else derived; reserved words auto-pluralise)");
|
|
24717
24828
|
console.error(" --public open a route's writes (default: secure)");
|
|
24718
24829
|
console.error(' --every 5m | --cron "\u2026" service schedule');
|
|
24719
24830
|
console.error(" --json emit machine-readable resolution envelope on stdout");
|
|
@@ -24764,7 +24875,7 @@ async function generateProgrammatic(what, name, extraArgs = []) {
|
|
|
24764
24875
|
}
|
|
24765
24876
|
function generateModel(name, flags, emitTest = true) {
|
|
24766
24877
|
const fields = fieldsOrDefault(flags.fields || "");
|
|
24767
|
-
const table2 =
|
|
24878
|
+
const table2 = resolveTable(name, flags, { announce: true });
|
|
24768
24879
|
const dir = resolve11("src/models");
|
|
24769
24880
|
ensureDir(dir);
|
|
24770
24881
|
const path8 = join21(dir, `${name}.ts`);
|
|
@@ -24815,7 +24926,7 @@ function generateRoute(name, flags, emitTest = true) {
|
|
|
24815
24926
|
if (__resolution.target === "route") {
|
|
24816
24927
|
setResolutionField("file_path", `src/routes/api/${routePath}/get.ts`);
|
|
24817
24928
|
}
|
|
24818
|
-
const table2 = model ?
|
|
24929
|
+
const table2 = model ? resolveTable(model, flags, { announce: false }) : "";
|
|
24819
24930
|
const modelImportBase = model ? `import ${model} from "../../../models/${model}.js";
|
|
24820
24931
|
` : "";
|
|
24821
24932
|
const modelImportId = model ? `import ${model} from "../../../../models/${model}.js";
|
|
@@ -25036,7 +25147,7 @@ ${aiFill(`delete_${singular}`, {
|
|
|
25036
25147
|
}
|
|
25037
25148
|
}
|
|
25038
25149
|
function generateCrud(name, flags) {
|
|
25039
|
-
const table2 =
|
|
25150
|
+
const table2 = resolveTable(name, flags, { announce: false });
|
|
25040
25151
|
const routeName = toPlural(table2);
|
|
25041
25152
|
const isPublic = Boolean(flags.public);
|
|
25042
25153
|
if (!__resolution.jsonMode) console.log(`
|
|
@@ -25063,7 +25174,7 @@ function generateMigration(name, flags, fieldsOverride, tableOverride, emitTest
|
|
|
25063
25174
|
table2 = tableOverride;
|
|
25064
25175
|
} else {
|
|
25065
25176
|
const raw = name.replace(/^create_/, "").replace(/^add_/, "").replace(/^drop_/, "");
|
|
25066
|
-
table2 =
|
|
25177
|
+
table2 = resolveTable(raw, flags, { announce: false });
|
|
25067
25178
|
}
|
|
25068
25179
|
if (__resolution.target === "migration") {
|
|
25069
25180
|
setResolutionField("table_name", table2);
|
|
@@ -25289,7 +25400,7 @@ void test${titleName};
|
|
|
25289
25400
|
}
|
|
25290
25401
|
function generateForm(name, flags) {
|
|
25291
25402
|
const fields = fieldsOrDefault(flags.fields || "");
|
|
25292
|
-
const table2 =
|
|
25403
|
+
const table2 = resolveTable(name, flags, { announce: false });
|
|
25293
25404
|
const routeName = toPlural(table2);
|
|
25294
25405
|
const inputTypes = {
|
|
25295
25406
|
string: "text",
|
|
@@ -25355,7 +25466,7 @@ function generateForm(name, flags) {
|
|
|
25355
25466
|
}
|
|
25356
25467
|
function generateView(name, flags) {
|
|
25357
25468
|
const fields = fieldsOrDefault(flags.fields || "");
|
|
25358
|
-
const table2 =
|
|
25469
|
+
const table2 = resolveTable(name, flags, { announce: false });
|
|
25359
25470
|
const routeName = toPlural(table2);
|
|
25360
25471
|
const cols = fields.map(([f]) => f);
|
|
25361
25472
|
const dir = resolve11("src/templates/pages");
|
|
@@ -25710,8 +25821,8 @@ ${rules} validator.required("name"); // starter rule (matches the model's def
|
|
|
25710
25821
|
writeFileSafe(path8, content);
|
|
25711
25822
|
emitValidatorTest(name, toSnake(name), toPascal(name));
|
|
25712
25823
|
}
|
|
25713
|
-
function generateSeeder(name,
|
|
25714
|
-
const table2 =
|
|
25824
|
+
function generateSeeder(name, flags) {
|
|
25825
|
+
const table2 = resolveTable(name, flags, { announce: false });
|
|
25715
25826
|
const dir = resolve11("src/seeds");
|
|
25716
25827
|
ensureDir(dir);
|
|
25717
25828
|
const path8 = join21(dir, `${table2}_seeder.ts`);
|
|
@@ -26336,7 +26447,7 @@ var init_generate = __esm({
|
|
|
26336
26447
|
TINA4_EDIT_MARKER = /^\s*(?:\/\/|--|\{#|#)\s*tina4:edit\s+(.+?)(?:\s*#\})?\s*$/;
|
|
26337
26448
|
DEFAULT_FIELDS = [["name", "string"]];
|
|
26338
26449
|
GENERATORS = {
|
|
26339
|
-
model: { handler: generateModel, usage: '<Name> [--fields "name:string,price:float"]', summary: "ORM model + matching migration" },
|
|
26450
|
+
model: { handler: generateModel, usage: '<Name> [--fields "name:string,price:float"] [--table-name <name>]', summary: "ORM model + matching migration" },
|
|
26340
26451
|
route: { handler: generateRoute, usage: "<name> [--model Name] [--public]", summary: "CRUD route file, secure by default (--public opens writes)" },
|
|
26341
26452
|
crud: { handler: generateCrud, usage: '<Name> [--fields "..."] [--public]', summary: "Model + migration + routes + form + view + test" },
|
|
26342
26453
|
migration: { handler: (n, f) => generateMigration(n, f, void 0, void 0, !f["no-test"]), usage: "<description>", summary: "Timestamped migration file (UP/DOWN)" },
|
|
@@ -37033,23 +37144,23 @@ var init_devAdmin = __esm({
|
|
|
37033
37144
|
});
|
|
37034
37145
|
};
|
|
37035
37146
|
handleDevAdminJs = async (_req, res) => {
|
|
37036
|
-
const { readFileSync:
|
|
37037
|
-
const { dirname:
|
|
37147
|
+
const { readFileSync: readFileSync30, existsSync: existsSync37 } = await import("node:fs");
|
|
37148
|
+
const { dirname: dirname18, join: join40, resolve: resolve29 } = await import("node:path");
|
|
37038
37149
|
const { fileURLToPath: fileURLToPath10 } = await import("node:url");
|
|
37039
|
-
const dir =
|
|
37150
|
+
const dir = dirname18(fileURLToPath10(import.meta.url));
|
|
37040
37151
|
const candidates = [
|
|
37041
|
-
|
|
37152
|
+
join40(dir, "..", "public", "js", "tina4-dev-admin.min.js"),
|
|
37042
37153
|
// src/../public/js/
|
|
37043
|
-
|
|
37154
|
+
join40(dir, "..", "..", "public", "js", "tina4-dev-admin.min.js"),
|
|
37044
37155
|
// deeper nesting
|
|
37045
37156
|
resolve29(process.cwd(), "node_modules", "tina4-nodejs", "packages", "core", "public", "js", "tina4-dev-admin.min.js"),
|
|
37046
37157
|
resolve29(process.cwd(), "public", "js", "tina4-dev-admin.min.js")
|
|
37047
37158
|
// project public/
|
|
37048
37159
|
];
|
|
37049
37160
|
for (const jsPath of candidates) {
|
|
37050
|
-
if (
|
|
37161
|
+
if (existsSync37(jsPath)) {
|
|
37051
37162
|
try {
|
|
37052
|
-
const content =
|
|
37163
|
+
const content = readFileSync30(jsPath, "utf-8");
|
|
37053
37164
|
res.raw.writeHead(200, { "Content-Type": "application/javascript; charset=utf-8", "Cache-Control": "no-cache" });
|
|
37054
37165
|
res.raw.end(content);
|
|
37055
37166
|
return;
|
|
@@ -40926,23 +41037,23 @@ function guessContentType(filename) {
|
|
|
40926
41037
|
}
|
|
40927
41038
|
function buildMultipartBody(boundary, fieldName, filename, fileContent, contentType, extraFields) {
|
|
40928
41039
|
const crlf = "\r\n";
|
|
40929
|
-
const
|
|
41040
|
+
const delimiter5 = `--${boundary}`;
|
|
40930
41041
|
const parts = [];
|
|
40931
41042
|
if (extraFields) {
|
|
40932
41043
|
for (const [key, value] of Object.entries(extraFields)) {
|
|
40933
|
-
parts.push(Buffer.from(
|
|
41044
|
+
parts.push(Buffer.from(delimiter5 + crlf, "utf-8"));
|
|
40934
41045
|
parts.push(Buffer.from(`Content-Disposition: form-data; name="${key}"` + crlf + crlf, "utf-8"));
|
|
40935
41046
|
parts.push(Buffer.from(String(value) + crlf, "utf-8"));
|
|
40936
41047
|
}
|
|
40937
41048
|
}
|
|
40938
|
-
parts.push(Buffer.from(
|
|
41049
|
+
parts.push(Buffer.from(delimiter5 + crlf, "utf-8"));
|
|
40939
41050
|
parts.push(
|
|
40940
41051
|
Buffer.from(`Content-Disposition: form-data; name="${fieldName}"; filename="${filename}"` + crlf, "utf-8")
|
|
40941
41052
|
);
|
|
40942
41053
|
parts.push(Buffer.from(`Content-Type: ${contentType}` + crlf + crlf, "utf-8"));
|
|
40943
41054
|
parts.push(fileContent);
|
|
40944
41055
|
parts.push(Buffer.from(crlf, "utf-8"));
|
|
40945
|
-
parts.push(Buffer.from(
|
|
41056
|
+
parts.push(Buffer.from(delimiter5 + "--" + crlf, "utf-8"));
|
|
40946
41057
|
return Buffer.concat(parts);
|
|
40947
41058
|
}
|
|
40948
41059
|
async function* parseLineStream(chunks) {
|
|
@@ -47499,8 +47610,8 @@ async function runTests(testPath) {
|
|
|
47499
47610
|
console.log(` Found ${testFiles.length} test file(s)
|
|
47500
47611
|
`);
|
|
47501
47612
|
for (const file of testFiles) {
|
|
47502
|
-
const
|
|
47503
|
-
console.log(` Running: ${
|
|
47613
|
+
const relative12 = file.replace(cwd + "/", "");
|
|
47614
|
+
console.log(` Running: ${relative12}`);
|
|
47504
47615
|
try {
|
|
47505
47616
|
execSync3(`npx tsx "${file}"`, { cwd, stdio: "inherit" });
|
|
47506
47617
|
} catch {
|
|
@@ -47510,19 +47621,233 @@ async function runTests(testPath) {
|
|
|
47510
47621
|
process.exit(inlineFailed || fileFailed ? 1 : 0);
|
|
47511
47622
|
}
|
|
47512
47623
|
|
|
47624
|
+
// src/commands/lint.ts
|
|
47625
|
+
import { existsSync as existsSync33, readdirSync as readdirSync20, readFileSync as readFileSync28, statSync as statSync20, writeFileSync as writeFileSync20 } from "node:fs";
|
|
47626
|
+
import { createRequire as createRequire9 } from "node:module";
|
|
47627
|
+
import { delimiter as delimiter2, dirname as dirname16, join as join35, relative as relative11 } from "node:path";
|
|
47628
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
47629
|
+
var LINT_EXTENSIONS = [".ts", ".mts", ".cts", ".js", ".mjs", ".cjs"];
|
|
47630
|
+
var JS_EXTENSIONS = [".js", ".mjs", ".cjs"];
|
|
47631
|
+
var SKIP_DIRS3 = /* @__PURE__ */ new Set(["node_modules", "dist", ".git"]);
|
|
47632
|
+
function isLintFile(name) {
|
|
47633
|
+
if (name.endsWith(".d.ts")) return false;
|
|
47634
|
+
return LINT_EXTENSIONS.some((ext) => name.endsWith(ext));
|
|
47635
|
+
}
|
|
47636
|
+
function walkSource2(dir, out) {
|
|
47637
|
+
let entries;
|
|
47638
|
+
try {
|
|
47639
|
+
entries = readdirSync20(dir);
|
|
47640
|
+
} catch {
|
|
47641
|
+
return;
|
|
47642
|
+
}
|
|
47643
|
+
for (const name of entries) {
|
|
47644
|
+
if (SKIP_DIRS3.has(name)) continue;
|
|
47645
|
+
const full = join35(dir, name);
|
|
47646
|
+
let st;
|
|
47647
|
+
try {
|
|
47648
|
+
st = statSync20(full);
|
|
47649
|
+
} catch {
|
|
47650
|
+
continue;
|
|
47651
|
+
}
|
|
47652
|
+
if (st.isDirectory()) {
|
|
47653
|
+
walkSource2(full, out);
|
|
47654
|
+
} else if (isLintFile(name)) {
|
|
47655
|
+
out.push(full);
|
|
47656
|
+
}
|
|
47657
|
+
}
|
|
47658
|
+
}
|
|
47659
|
+
function collectAppFiles(cwd) {
|
|
47660
|
+
const files = [];
|
|
47661
|
+
const srcDir = join35(cwd, "src");
|
|
47662
|
+
try {
|
|
47663
|
+
if (statSync20(srcDir).isDirectory()) walkSource2(srcDir, files);
|
|
47664
|
+
} catch {
|
|
47665
|
+
}
|
|
47666
|
+
for (const entry of ["app.ts", "app.mts", "app.js", "app.mjs"]) {
|
|
47667
|
+
const p = join35(cwd, entry);
|
|
47668
|
+
try {
|
|
47669
|
+
if (statSync20(p).isFile()) files.push(p);
|
|
47670
|
+
} catch {
|
|
47671
|
+
}
|
|
47672
|
+
}
|
|
47673
|
+
return files;
|
|
47674
|
+
}
|
|
47675
|
+
var ESLINT_FLAT_CONFIGS = [
|
|
47676
|
+
"eslint.config.js",
|
|
47677
|
+
"eslint.config.mjs",
|
|
47678
|
+
"eslint.config.cjs",
|
|
47679
|
+
"eslint.config.ts",
|
|
47680
|
+
"eslint.config.mts",
|
|
47681
|
+
"eslint.config.cts"
|
|
47682
|
+
];
|
|
47683
|
+
var ESLINT_SCAFFOLD_FILE = "eslint.config.js";
|
|
47684
|
+
var ESLINT_SCAFFOLD = `import js from "@eslint/js";
|
|
47685
|
+
import tseslint from "typescript-eslint";
|
|
47686
|
+
export default [js.configs.recommended, ...tseslint.configs.recommended];
|
|
47687
|
+
`;
|
|
47688
|
+
function findEslintConfig(cwd) {
|
|
47689
|
+
for (const name of ESLINT_FLAT_CONFIGS) {
|
|
47690
|
+
const p = join35(cwd, name);
|
|
47691
|
+
if (existsSync33(p)) return p;
|
|
47692
|
+
}
|
|
47693
|
+
return null;
|
|
47694
|
+
}
|
|
47695
|
+
function resolvePackageBin(cwd, pkg, binRelative) {
|
|
47696
|
+
const direct = join35(cwd, "node_modules", pkg, binRelative);
|
|
47697
|
+
if (existsSync33(direct)) return direct;
|
|
47698
|
+
const require3 = createRequire9(join35(cwd, "package.json"));
|
|
47699
|
+
let entry;
|
|
47700
|
+
try {
|
|
47701
|
+
entry = require3.resolve(pkg);
|
|
47702
|
+
} catch {
|
|
47703
|
+
return null;
|
|
47704
|
+
}
|
|
47705
|
+
let dir = dirname16(entry);
|
|
47706
|
+
for (let i = 0; i < 8; i++) {
|
|
47707
|
+
const pkgJson = join35(dir, "package.json");
|
|
47708
|
+
if (existsSync33(pkgJson)) {
|
|
47709
|
+
try {
|
|
47710
|
+
if (JSON.parse(readFileSync28(pkgJson, "utf-8")).name === pkg) {
|
|
47711
|
+
const bin = join35(dir, binRelative);
|
|
47712
|
+
return existsSync33(bin) ? bin : null;
|
|
47713
|
+
}
|
|
47714
|
+
} catch {
|
|
47715
|
+
}
|
|
47716
|
+
}
|
|
47717
|
+
const parent = dirname16(dir);
|
|
47718
|
+
if (parent === dir) break;
|
|
47719
|
+
dir = parent;
|
|
47720
|
+
}
|
|
47721
|
+
return null;
|
|
47722
|
+
}
|
|
47723
|
+
function resolveEslintBin(cwd) {
|
|
47724
|
+
return resolvePackageBin(cwd, "eslint", "bin/eslint.js");
|
|
47725
|
+
}
|
|
47726
|
+
function hasPackage(cwd, pkg) {
|
|
47727
|
+
return existsSync33(join35(cwd, "node_modules", ...pkg.split("/"), "package.json"));
|
|
47728
|
+
}
|
|
47729
|
+
function resolveNpm() {
|
|
47730
|
+
const windows = process.platform === "win32";
|
|
47731
|
+
const names = windows ? ["npm.cmd", "npm.exe", "npm"] : ["npm"];
|
|
47732
|
+
for (const dir of (process.env.PATH ?? "").split(delimiter2)) {
|
|
47733
|
+
if (!dir) continue;
|
|
47734
|
+
for (const name of names) {
|
|
47735
|
+
const candidate = join35(dir, name);
|
|
47736
|
+
try {
|
|
47737
|
+
if (statSync20(candidate).isFile()) return candidate;
|
|
47738
|
+
} catch {
|
|
47739
|
+
}
|
|
47740
|
+
}
|
|
47741
|
+
}
|
|
47742
|
+
return null;
|
|
47743
|
+
}
|
|
47744
|
+
function runLint(args) {
|
|
47745
|
+
const fix = args.includes("--fix");
|
|
47746
|
+
const noInstall = args.includes("--no-install");
|
|
47747
|
+
const cwd = process.cwd();
|
|
47748
|
+
const files = collectAppFiles(cwd);
|
|
47749
|
+
if (files.length === 0) {
|
|
47750
|
+
console.log(" lint: nothing to lint (no src/ or app.ts).");
|
|
47751
|
+
process.exit(0);
|
|
47752
|
+
}
|
|
47753
|
+
let eslintBin = resolveEslintBin(cwd);
|
|
47754
|
+
let eslintConfig = findEslintConfig(cwd);
|
|
47755
|
+
if (!noInstall && (!eslintBin || !eslintConfig)) {
|
|
47756
|
+
const npm = resolveNpm();
|
|
47757
|
+
if (!npm) {
|
|
47758
|
+
console.log(" \xB7 npm not found \u2014 using the zero-dependency baseline.");
|
|
47759
|
+
} else {
|
|
47760
|
+
if (!eslintBin) {
|
|
47761
|
+
console.log(" \xB7 installing eslint (npm i -D eslint @eslint/js typescript-eslint)...");
|
|
47762
|
+
const rc = spawnSync3(npm, ["install", "-D", "eslint", "@eslint/js", "typescript-eslint"], {
|
|
47763
|
+
cwd,
|
|
47764
|
+
stdio: "inherit"
|
|
47765
|
+
}).status;
|
|
47766
|
+
if (rc === 0) {
|
|
47767
|
+
eslintBin = resolveEslintBin(cwd);
|
|
47768
|
+
} else {
|
|
47769
|
+
console.log(" \xB7 could not install eslint \u2014 using the zero-dependency baseline.");
|
|
47770
|
+
}
|
|
47771
|
+
}
|
|
47772
|
+
if (eslintBin && !eslintConfig && hasPackage(cwd, "@eslint/js") && hasPackage(cwd, "typescript-eslint")) {
|
|
47773
|
+
const scaffold = join35(cwd, ESLINT_SCAFFOLD_FILE);
|
|
47774
|
+
try {
|
|
47775
|
+
writeFileSync20(scaffold, ESLINT_SCAFFOLD, "utf-8");
|
|
47776
|
+
eslintConfig = scaffold;
|
|
47777
|
+
console.log(` \xB7 scaffolded ${ESLINT_SCAFFOLD_FILE} (@eslint/js + typescript-eslint recommended).`);
|
|
47778
|
+
} catch (err) {
|
|
47779
|
+
console.log(
|
|
47780
|
+
` \xB7 could not scaffold ${ESLINT_SCAFFOLD_FILE} (${err instanceof Error ? err.message : String(err)}).`
|
|
47781
|
+
);
|
|
47782
|
+
}
|
|
47783
|
+
}
|
|
47784
|
+
}
|
|
47785
|
+
}
|
|
47786
|
+
if (eslintBin && eslintConfig) {
|
|
47787
|
+
const label = fix ? "eslint --fix" : "eslint";
|
|
47788
|
+
const eslintArgs = [eslintBin, ...files, ...fix ? ["--fix"] : []];
|
|
47789
|
+
const code = spawnSync3(process.execPath, eslintArgs, { cwd, stdio: "inherit" }).status ?? 1;
|
|
47790
|
+
if (code !== 0) {
|
|
47791
|
+
console.log(` \u2717 lint failed \u2014 ${files.length} file(s) [${label}]`);
|
|
47792
|
+
process.exit(1);
|
|
47793
|
+
}
|
|
47794
|
+
console.log(` \u2713 lint clean \u2014 ${files.length} file(s) [${label}]`);
|
|
47795
|
+
process.exit(0);
|
|
47796
|
+
}
|
|
47797
|
+
if (fix) {
|
|
47798
|
+
console.log(" \xB7 --fix needs eslint \u2014 the baseline check has no autofix.");
|
|
47799
|
+
}
|
|
47800
|
+
const hasTsconfig = existsSync33(join35(cwd, "tsconfig.json"));
|
|
47801
|
+
const tscBin = hasTsconfig ? resolvePackageBin(cwd, "typescript", "bin/tsc") : null;
|
|
47802
|
+
if (tscBin) {
|
|
47803
|
+
const code = spawnSync3(process.execPath, [tscBin, "--noEmit"], { cwd, stdio: "inherit" }).status ?? 1;
|
|
47804
|
+
if (code !== 0) {
|
|
47805
|
+
console.log(` \u2717 lint failed \u2014 ${files.length} file(s) [tsc]`);
|
|
47806
|
+
process.exit(1);
|
|
47807
|
+
}
|
|
47808
|
+
console.log(` \u2713 lint clean \u2014 ${files.length} file(s) [tsc]`);
|
|
47809
|
+
process.exit(0);
|
|
47810
|
+
}
|
|
47811
|
+
const jsFiles = files.filter((f) => JS_EXTENSIONS.some((ext) => f.endsWith(ext)));
|
|
47812
|
+
if (jsFiles.length === 0) {
|
|
47813
|
+
console.log(
|
|
47814
|
+
" lint: no JavaScript files to check \u2014 add tsconfig.json + typescript to type-check .ts files."
|
|
47815
|
+
);
|
|
47816
|
+
process.exit(0);
|
|
47817
|
+
}
|
|
47818
|
+
let syntaxErrors = 0;
|
|
47819
|
+
for (const file of jsFiles) {
|
|
47820
|
+
const result = spawnSync3(process.execPath, ["--check", file], { cwd, encoding: "utf-8" });
|
|
47821
|
+
if ((result.status ?? 1) !== 0) {
|
|
47822
|
+
const stderr = (result.stderr || "").trim();
|
|
47823
|
+
const detail = stderr.split("\n").reverse().find((line) => line.includes("Error:")) || "syntax error";
|
|
47824
|
+
console.log(` \u2717 ${relative11(cwd, file)}: ${detail.trim()}`);
|
|
47825
|
+
syntaxErrors++;
|
|
47826
|
+
}
|
|
47827
|
+
}
|
|
47828
|
+
if (syntaxErrors > 0) {
|
|
47829
|
+
console.log(
|
|
47830
|
+
` \u2717 lint failed \u2014 ${syntaxErrors} syntax error(s) in ${jsFiles.length} file(s) [node --check]`
|
|
47831
|
+
);
|
|
47832
|
+
process.exit(1);
|
|
47833
|
+
}
|
|
47834
|
+
console.log(` \u2713 lint clean \u2014 ${jsFiles.length} file(s) [node --check]`);
|
|
47835
|
+
process.exit(0);
|
|
47836
|
+
}
|
|
47837
|
+
|
|
47513
47838
|
// src/bin.ts
|
|
47514
47839
|
init_generate();
|
|
47515
47840
|
|
|
47516
47841
|
// src/commands/seed.ts
|
|
47517
|
-
import { existsSync as
|
|
47518
|
-
import { resolve as resolve28, join as
|
|
47842
|
+
import { existsSync as existsSync34, readdirSync as readdirSync21 } from "node:fs";
|
|
47843
|
+
import { resolve as resolve28, join as join36 } from "node:path";
|
|
47519
47844
|
import { execSync as execSync4 } from "node:child_process";
|
|
47520
47845
|
async function runSeeds(seedPath) {
|
|
47521
47846
|
const cwd = process.cwd();
|
|
47522
47847
|
const seedDir = resolve28(cwd, "src/seeds");
|
|
47523
47848
|
if (seedPath) {
|
|
47524
47849
|
const file = resolve28(seedPath);
|
|
47525
|
-
if (!
|
|
47850
|
+
if (!existsSync34(file)) {
|
|
47526
47851
|
console.error(` Error: Seed file not found: ${seedPath}`);
|
|
47527
47852
|
process.exit(1);
|
|
47528
47853
|
}
|
|
@@ -47535,14 +47860,14 @@ async function runSeeds(seedPath) {
|
|
|
47535
47860
|
}
|
|
47536
47861
|
return;
|
|
47537
47862
|
}
|
|
47538
|
-
if (!
|
|
47863
|
+
if (!existsSync34(seedDir)) {
|
|
47539
47864
|
console.log(" No seeds directory found.");
|
|
47540
47865
|
console.log(" Create seed files in src/seeds/ (e.g. src/seeds/001-users.ts)");
|
|
47541
47866
|
return;
|
|
47542
47867
|
}
|
|
47543
47868
|
let seedFiles;
|
|
47544
47869
|
try {
|
|
47545
|
-
seedFiles =
|
|
47870
|
+
seedFiles = readdirSync21(seedDir).filter((f) => f.endsWith(".ts")).sort().map((f) => join36(seedDir, f));
|
|
47546
47871
|
} catch {
|
|
47547
47872
|
console.log(" Could not read src/seeds/ directory.");
|
|
47548
47873
|
return;
|
|
@@ -47555,8 +47880,8 @@ async function runSeeds(seedPath) {
|
|
|
47555
47880
|
`);
|
|
47556
47881
|
let failed = false;
|
|
47557
47882
|
for (const file of seedFiles) {
|
|
47558
|
-
const
|
|
47559
|
-
console.log(` Seeding: ${
|
|
47883
|
+
const relative12 = file.replace(cwd + "/", "");
|
|
47884
|
+
console.log(` Seeding: ${relative12}`);
|
|
47560
47885
|
try {
|
|
47561
47886
|
execSync4(`npx tsx "${file}"`, { cwd, stdio: "inherit" });
|
|
47562
47887
|
} catch {
|
|
@@ -47573,8 +47898,8 @@ async function runSeeds(seedPath) {
|
|
|
47573
47898
|
// src/commands/queue.ts
|
|
47574
47899
|
init_dotenv();
|
|
47575
47900
|
init_queue();
|
|
47576
|
-
import { readdirSync as
|
|
47577
|
-
import { extname as extname8, join as
|
|
47901
|
+
import { readdirSync as readdirSync22, statSync as statSync21 } from "node:fs";
|
|
47902
|
+
import { extname as extname8, join as join37 } from "node:path";
|
|
47578
47903
|
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
47579
47904
|
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["once", "json"]);
|
|
47580
47905
|
function parseFlags(args) {
|
|
@@ -47605,8 +47930,8 @@ function parseFlags(args) {
|
|
|
47605
47930
|
async function resolveQueueHandler(servicesDir, topic) {
|
|
47606
47931
|
let entries;
|
|
47607
47932
|
try {
|
|
47608
|
-
if (!
|
|
47609
|
-
entries =
|
|
47933
|
+
if (!statSync21(servicesDir).isDirectory()) return null;
|
|
47934
|
+
entries = readdirSync22(servicesDir);
|
|
47610
47935
|
} catch {
|
|
47611
47936
|
return null;
|
|
47612
47937
|
}
|
|
@@ -47614,9 +47939,9 @@ async function resolveQueueHandler(servicesDir, topic) {
|
|
|
47614
47939
|
if (entry.startsWith("_")) continue;
|
|
47615
47940
|
const ext = extname8(entry);
|
|
47616
47941
|
if (ext !== ".ts" && ext !== ".js") continue;
|
|
47617
|
-
const fullPath =
|
|
47942
|
+
const fullPath = join37(servicesDir, entry);
|
|
47618
47943
|
try {
|
|
47619
|
-
if (!
|
|
47944
|
+
if (!statSync21(fullPath).isFile()) continue;
|
|
47620
47945
|
const mod = await import(pathToFileURL3(fullPath).href);
|
|
47621
47946
|
const config = mod.default ?? mod;
|
|
47622
47947
|
if (config && typeof config === "object" && config.topic === topic && typeof config.handle === "function") {
|
|
@@ -47766,9 +48091,9 @@ async function queueCommand(args = []) {
|
|
|
47766
48091
|
}
|
|
47767
48092
|
|
|
47768
48093
|
// src/commands/build.ts
|
|
47769
|
-
import { accessSync as accessSync2, constants as constants2, existsSync as
|
|
47770
|
-
import { basename as basename8, delimiter as
|
|
47771
|
-
import { spawnSync as
|
|
48094
|
+
import { accessSync as accessSync2, constants as constants2, existsSync as existsSync35, statSync as statSync22 } from "node:fs";
|
|
48095
|
+
import { basename as basename8, delimiter as delimiter3, join as join38 } from "node:path";
|
|
48096
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
47772
48097
|
function parseFlags2(args) {
|
|
47773
48098
|
const flags = {};
|
|
47774
48099
|
let i = 0;
|
|
@@ -47793,13 +48118,13 @@ function whichDocker() {
|
|
|
47793
48118
|
const pathValue = process.env.PATH || process.env.Path || "";
|
|
47794
48119
|
if (!pathValue) return null;
|
|
47795
48120
|
const exts = process.platform === "win32" ? (process.env.PATHEXT || ".EXE;.CMD;.BAT;.COM").split(";") : [""];
|
|
47796
|
-
for (const dir of pathValue.split(
|
|
48121
|
+
for (const dir of pathValue.split(delimiter3)) {
|
|
47797
48122
|
if (!dir) continue;
|
|
47798
48123
|
for (const ext of exts) {
|
|
47799
|
-
const candidate =
|
|
48124
|
+
const candidate = join38(dir, `docker${ext}`);
|
|
47800
48125
|
try {
|
|
47801
48126
|
accessSync2(candidate, constants2.X_OK);
|
|
47802
|
-
if (
|
|
48127
|
+
if (statSync22(candidate).isFile()) return candidate;
|
|
47803
48128
|
} catch {
|
|
47804
48129
|
}
|
|
47805
48130
|
}
|
|
@@ -47814,7 +48139,7 @@ function buildImage(args) {
|
|
|
47814
48139
|
tag = `${dirName || "tina4app"}:latest`;
|
|
47815
48140
|
}
|
|
47816
48141
|
const dockerfile = typeof flags.file === "string" && flags.file ? flags.file : "Dockerfile";
|
|
47817
|
-
if (!
|
|
48142
|
+
if (!existsSync35(dockerfile) || !statSync22(dockerfile).isFile()) {
|
|
47818
48143
|
console.log(` \u2717 No ${dockerfile} found.`);
|
|
47819
48144
|
console.log(" A Tina4 app deploys as a container. Scaffold a Dockerfile first:");
|
|
47820
48145
|
console.log(" tina4 deploy docker (or: tina4nodejs init)");
|
|
@@ -47828,7 +48153,7 @@ function buildImage(args) {
|
|
|
47828
48153
|
process.exit(1);
|
|
47829
48154
|
}
|
|
47830
48155
|
console.log(` Building image ${tag} from ${dockerfile} ...`);
|
|
47831
|
-
const result =
|
|
48156
|
+
const result = spawnSync4(docker, ["build", "-t", tag, "-f", dockerfile, "."], {
|
|
47832
48157
|
stdio: "inherit"
|
|
47833
48158
|
});
|
|
47834
48159
|
const code = result.status ?? 1;
|
|
@@ -47841,22 +48166,22 @@ function buildImage(args) {
|
|
|
47841
48166
|
}
|
|
47842
48167
|
|
|
47843
48168
|
// src/bin.ts
|
|
47844
|
-
import { spawnSync as
|
|
47845
|
-
import { existsSync as
|
|
47846
|
-
import { delimiter as
|
|
48169
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
48170
|
+
import { existsSync as existsSync36, readFileSync as readFileSync29, statSync as statSync23 } from "node:fs";
|
|
48171
|
+
import { delimiter as delimiter4, dirname as dirname17, join as join39 } from "node:path";
|
|
47847
48172
|
import { fileURLToPath as fileURLToPath9, pathToFileURL as pathToFileURL4 } from "node:url";
|
|
47848
48173
|
function readCliVersion() {
|
|
47849
|
-
let dir =
|
|
48174
|
+
let dir = dirname17(fileURLToPath9(import.meta.url));
|
|
47850
48175
|
for (let i = 0; i < 6; i++) {
|
|
47851
|
-
const pkgPath =
|
|
47852
|
-
if (
|
|
48176
|
+
const pkgPath = join39(dir, "package.json");
|
|
48177
|
+
if (existsSync36(pkgPath)) {
|
|
47853
48178
|
try {
|
|
47854
|
-
const pkg = JSON.parse(
|
|
48179
|
+
const pkg = JSON.parse(readFileSync29(pkgPath, "utf-8"));
|
|
47855
48180
|
if (typeof pkg.version === "string" && pkg.version) return pkg.version;
|
|
47856
48181
|
} catch {
|
|
47857
48182
|
}
|
|
47858
48183
|
}
|
|
47859
|
-
const parent =
|
|
48184
|
+
const parent = dirname17(dir);
|
|
47860
48185
|
if (parent === dir) break;
|
|
47861
48186
|
dir = parent;
|
|
47862
48187
|
}
|
|
@@ -48072,6 +48397,13 @@ var COMMANDS = {
|
|
|
48072
48397
|
usage: "[file]",
|
|
48073
48398
|
summary: "Run project tests"
|
|
48074
48399
|
},
|
|
48400
|
+
lint: {
|
|
48401
|
+
handler: (a) => {
|
|
48402
|
+
runLint(a);
|
|
48403
|
+
},
|
|
48404
|
+
usage: "[--fix] [--no-install]",
|
|
48405
|
+
summary: "Lint the project (eslint, installed dev-only on demand; else tsc/node --check baseline)"
|
|
48406
|
+
},
|
|
48075
48407
|
queue: {
|
|
48076
48408
|
handler: async (a) => {
|
|
48077
48409
|
await queueCommand(a);
|
|
@@ -48146,12 +48478,12 @@ var CLIENT_INSTALL_HINT = " Install it: curl -fsSL https://tina4.com/install.s
|
|
|
48146
48478
|
function findClient() {
|
|
48147
48479
|
const windows = process.platform === "win32";
|
|
48148
48480
|
const names = windows ? [`${CLIENT_BINARY}.exe`, `${CLIENT_BINARY}.cmd`, `${CLIENT_BINARY}.bat`] : [CLIENT_BINARY];
|
|
48149
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
48481
|
+
for (const dir of (process.env.PATH ?? "").split(delimiter4)) {
|
|
48150
48482
|
if (!dir) continue;
|
|
48151
48483
|
for (const name of names) {
|
|
48152
|
-
const candidate =
|
|
48484
|
+
const candidate = join39(dir, name);
|
|
48153
48485
|
try {
|
|
48154
|
-
if (
|
|
48486
|
+
if (statSync23(candidate).isFile()) return candidate;
|
|
48155
48487
|
} catch {
|
|
48156
48488
|
}
|
|
48157
48489
|
}
|
|
@@ -48179,7 +48511,7 @@ ${CLIENT_INSTALL_HINT}
|
|
|
48179
48511
|
);
|
|
48180
48512
|
return EXIT_CLIENT_UNAVAILABLE;
|
|
48181
48513
|
}
|
|
48182
|
-
const result =
|
|
48514
|
+
const result = spawnSync5(client, [command, ...args], {
|
|
48183
48515
|
stdio: "inherit",
|
|
48184
48516
|
env: { ...process.env, [DELEGATION_GUARD_ENV]: command }
|
|
48185
48517
|
});
|