orchid-orm 1.76.5 → 1.77.1
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/dist/bun.d.ts +7 -18
- package/dist/bun.js +3 -2
- package/dist/bun.js.map +1 -1
- package/dist/bun.mjs +3 -2
- package/dist/bun.mjs.map +1 -1
- package/dist/index.d.ts +884 -158
- package/dist/index.js +636 -44
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +637 -46
- package/dist/index.mjs.map +1 -1
- package/dist/migrations/bun.js.map +1 -1
- package/dist/migrations/bun.mjs.map +1 -1
- package/dist/migrations/index.d.ts +2 -1
- package/dist/migrations/index.js +284 -121
- package/dist/migrations/index.js.map +1 -1
- package/dist/migrations/index.mjs +285 -121
- package/dist/migrations/index.mjs.map +1 -1
- package/dist/migrations/node-postgres.js.map +1 -1
- package/dist/migrations/node-postgres.mjs.map +1 -1
- package/dist/migrations/postgres-js.js.map +1 -1
- package/dist/migrations/postgres-js.mjs.map +1 -1
- package/dist/node-postgres.d.ts +7 -17
- package/dist/node-postgres.js +3 -2
- package/dist/node-postgres.js.map +1 -1
- package/dist/node-postgres.mjs +3 -2
- package/dist/node-postgres.mjs.map +1 -1
- package/dist/postgres-js.d.ts +7 -15
- package/dist/postgres-js.js +3 -2
- package/dist/postgres-js.js.map +1 -1
- package/dist/postgres-js.mjs +3 -2
- package/dist/postgres-js.mjs.map +1 -1
- package/package.json +4 -7
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { astToMigration, concatSchemaAndName, createMigrationInterface, dbColumnToAst, encodeColumnDefault, getConstraintName, getDbStructureTableData, getDbTableColumnsChecks, getDbVersion, getExcludeName, getIndexName, getMigrationsSchemaAndTable, getSchemaAndTableFromName, instantiateDbColumn, introspectDbSchema, makeDomainsMap, makeFileVersion, makeStructureToAstCtx, migrate, migrateAndClose, promptSelect, rakeDbCommands, saveMigratedVersion, structureToAst, tableToAst, writeMigrationFile } from "rake-db";
|
|
2
|
-
import { ArrayColumn, DomainColumn, EnumColumn, RawSql, UnknownColumn, VirtualColumn, addCode, codeToString, colors, columnsShapeToCode, deepCompare, emptyArray, emptyObject, exhaustive, getColumnBaseType, getDriverErrorCode, getFreeSetAlias, getImportPath, getQuerySchema, getSupportedDefaultPrivileges, pathToLog, pluralize,
|
|
1
|
+
import { astToMigration, concatSchemaAndName, createMigrationInterface, dbColumnToAst, encodeColumnDefault, getConstraintName, getDbStructureTableData, getDbTableColumnsChecks, getDbVersion, getExcludeName, getIndexName, getMigrationsSchemaAndTable, getSchemaAndTableFromName, getTableFactoryConfig, instantiateDbColumn, introspectDbSchema, makeDomainsMap, makeFileVersion, makeStructureToAstCtx, migrate, migrateAndClose, promptSelect, rakeDbCommands, saveMigratedVersion, structureToAst, tableToAst, writeMigrationFile } from "rake-db";
|
|
2
|
+
import { ArrayColumn, DomainColumn, EnumColumn, RawSql, UnknownColumn, VirtualColumn, addCode, codeToString, colors, columnsShapeToCode, constraintToCode, deepCompare, emptyArray, emptyObject, excludeToCode, exhaustive, getColumnBaseType, getDriverErrorCode, getForeignKeyTableInstance, getFreeSetAlias, getImportPath, getQuerySchema, getSupportedDefaultPrivileges, indexToCode, pathToLog, pluralize, primaryKeyInnerToCode, queryToSql, quoteObjectKey, raw, rawSqlToSql, singleQuote, sqlToRawSql, toArray, toCamelCase, toPascalCase, toSnakeCase } from "pqb/internal";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import fs from "node:fs/promises";
|
|
6
|
-
import typescript from "typescript";
|
|
7
6
|
export * from "rake-db";
|
|
8
7
|
const viewDataToSql = (viewData, viewName) => {
|
|
9
8
|
return sqlToRawSql(viewDataToQuerySql(viewData, viewName));
|
|
@@ -328,8 +327,9 @@ const compareColumns = async (adapter, domainsMap, ast, currentSchema, compareSq
|
|
|
328
327
|
if (!typeCasts.get(dbBaseType)?.has(codeBaseType)) {
|
|
329
328
|
if (!(dbColumn instanceof EnumColumn) || !(codeColumn instanceof EnumColumn) || !deepCompare(dbColumn.options, codeColumn.options)) {
|
|
330
329
|
if (verifying) throw new AbortSignal();
|
|
330
|
+
const tableName = concatSchemaAndName(changeTableData.changeTableAst);
|
|
331
331
|
if (await promptSelect({
|
|
332
|
-
message: `Cannot cast type of ${
|
|
332
|
+
message: `Cannot cast type of ${tableName}'s column ${key} from ${dbType} to ${codeType}`,
|
|
333
333
|
options: [`${colors.yellowBold(`-/+`)} recreate the column, existing data will be ${colors.red("lost")}`, `write migration manually`]
|
|
334
334
|
})) throw new AbortSignal();
|
|
335
335
|
dbColumn.data.name = codeColumn.data.name;
|
|
@@ -409,7 +409,7 @@ JOIN pg_type AS t ON t.oid = casttarget`);
|
|
|
409
409
|
for (const [source, target] of rows) {
|
|
410
410
|
const set = directTypeCasts.get(source);
|
|
411
411
|
if (set) set.add(target);
|
|
412
|
-
else directTypeCasts.set(source, new Set([target]));
|
|
412
|
+
else directTypeCasts.set(source, /* @__PURE__ */ new Set([target]));
|
|
413
413
|
}
|
|
414
414
|
typeCasts = /* @__PURE__ */ new Map();
|
|
415
415
|
for (const [type, directSet] of directTypeCasts.entries()) {
|
|
@@ -540,7 +540,8 @@ const makeComparableDomain = (currentSchema, schemaName, name, column) => {
|
|
|
540
540
|
inner = inner.data.item;
|
|
541
541
|
arrayDims++;
|
|
542
542
|
}
|
|
543
|
-
const
|
|
543
|
+
const fullType = getColumnDbType(inner, currentSchema);
|
|
544
|
+
const [typeSchema = "pg_catalog", type] = getSchemaAndTableFromName(currentSchema, fullType);
|
|
544
545
|
return {
|
|
545
546
|
schemaName,
|
|
546
547
|
name,
|
|
@@ -779,7 +780,7 @@ const processPrimaryKey = (config, changeTableData) => {
|
|
|
779
780
|
};
|
|
780
781
|
const changePrimaryKey = (config, columnsPrimaryKey, { codeTable, dbTableData: { primaryKey: dbPrimaryKey }, changeTableAst: { shape, add, drop }, changingColumns }) => {
|
|
781
782
|
const tablePrimaryKey = codeTable.internal.tableData.primaryKey;
|
|
782
|
-
const primaryKey = [
|
|
783
|
+
const primaryKey = [.../* @__PURE__ */ new Set([...columnsPrimaryKey, ...(config.snakeCase ? tablePrimaryKey?.columns.map((key) => ({
|
|
783
784
|
key,
|
|
784
785
|
name: toSnakeCase(key)
|
|
785
786
|
})) : tablePrimaryKey?.columns.map((key) => ({
|
|
@@ -1279,7 +1280,7 @@ const collectCodeFkeys = (config, { codeTable, changeTableAst: { shape } }, curr
|
|
|
1279
1280
|
};
|
|
1280
1281
|
const fnOrTableToString = (fnOrTable) => {
|
|
1281
1282
|
if (typeof fnOrTable !== "string") {
|
|
1282
|
-
const { schema, table, nameInDb } =
|
|
1283
|
+
const { schema, table, nameInDb } = getForeignKeyTableInstance(fnOrTable());
|
|
1283
1284
|
const name = nameInDb || table;
|
|
1284
1285
|
if (!name) throw new Error("Referenced table is missing table property");
|
|
1285
1286
|
fnOrTable = concatSchemaAndName({
|
|
@@ -2187,7 +2188,7 @@ const processDefaultPrivileges = (ast, dbStructure, { internal: { roles } }) =>
|
|
|
2187
2188
|
const codeObj = codePrivilege.objectConfigs.find((o) => o.object === objectType);
|
|
2188
2189
|
const dbPrivs = dbObj?.privilegeConfigs ?? [];
|
|
2189
2190
|
const codePrivs = codeObj?.privilegeConfigs ?? [];
|
|
2190
|
-
const
|
|
2191
|
+
const collapsedDbPrivilege = {
|
|
2191
2192
|
owner: dbPrivilege.owner,
|
|
2192
2193
|
grantee: dbPrivilege.grantee,
|
|
2193
2194
|
schema: dbPrivilege.schema,
|
|
@@ -2195,7 +2196,8 @@ const processDefaultPrivileges = (ast, dbStructure, { internal: { roles } }) =>
|
|
|
2195
2196
|
object: objectType,
|
|
2196
2197
|
privilegeConfigs: dbPrivs
|
|
2197
2198
|
}]
|
|
2198
|
-
}
|
|
2199
|
+
};
|
|
2200
|
+
const collapsedDbPrivs = collapsePrivilegesToAll(collapsedDbPrivilege, objectTypeToAllPrivileges).objectConfigs[0]?.privilegeConfigs ?? [];
|
|
2199
2201
|
const toGrant = codePrivs.filter((cp) => !collapsedDbPrivs.some((dp) => privilegeConfigsMatch(cp, dp, objectType, objectTypeToAllPrivileges)));
|
|
2200
2202
|
const toRevoke = dbPrivs.filter((dp) => !codePrivs.some((cp) => privilegeConfigsMatch(cp, dp, objectType, objectTypeToAllPrivileges)));
|
|
2201
2203
|
if (toGrant.length) {
|
|
@@ -2619,9 +2621,7 @@ const dbViewOptionsToAst = (view) => {
|
|
|
2619
2621
|
case "security_barrier":
|
|
2620
2622
|
options.securityBarrier = value === "true";
|
|
2621
2623
|
break;
|
|
2622
|
-
case "security_invoker":
|
|
2623
|
-
options.securityInvoker = value === "true";
|
|
2624
|
-
break;
|
|
2624
|
+
case "security_invoker": options.securityInvoker = value === "true";
|
|
2625
2625
|
}
|
|
2626
2626
|
}
|
|
2627
2627
|
return options;
|
|
@@ -3046,10 +3046,11 @@ const report = (ast, config, currentSchema) => {
|
|
|
3046
3046
|
break;
|
|
3047
3047
|
}
|
|
3048
3048
|
case "policy": {
|
|
3049
|
-
const
|
|
3049
|
+
const table = dbItemName({
|
|
3050
3050
|
schema: a.schema,
|
|
3051
3051
|
name: a.table
|
|
3052
|
-
}, currentSchema)
|
|
3052
|
+
}, currentSchema);
|
|
3053
|
+
const summary = formatPolicySummary(a, table);
|
|
3053
3054
|
const message = a.action === "create" ? `${green("+ create policy")} ${a.name}: ${summary}` : `${red("- drop policy")} ${a.name}: ${summary}`;
|
|
3054
3055
|
code.push(message);
|
|
3055
3056
|
break;
|
|
@@ -3136,7 +3137,7 @@ const formatPolicyChangeDefinition = (policy) => {
|
|
|
3136
3137
|
var AbortSignal = class extends Error {};
|
|
3137
3138
|
const generate = async (adapters, config, args, afterPull) => {
|
|
3138
3139
|
let { dbPath } = config;
|
|
3139
|
-
if (!dbPath || !config
|
|
3140
|
+
if (!dbPath || !getTableFactoryConfig(config)) throw invalidConfig(config);
|
|
3140
3141
|
if (!adapters.length) throw new Error(`Database options must not be empty`);
|
|
3141
3142
|
if (!dbPath.endsWith(".ts")) dbPath += ".ts";
|
|
3142
3143
|
let migrationName = args[0] ?? "generated";
|
|
@@ -3206,7 +3207,7 @@ const generate = async (adapters, config, args, afterPull) => {
|
|
|
3206
3207
|
if (up) for (const adapter of adapters) await migrateAndClose(adapter, config);
|
|
3207
3208
|
else if (!afterPull) await closeAdapters(adapters);
|
|
3208
3209
|
};
|
|
3209
|
-
const invalidConfig = (config) => /* @__PURE__ */ new Error(`\`${config.dbPath ? "baseTable" : "dbPath"}\` setting must be set in the migrations config for the generator to work`);
|
|
3210
|
+
const invalidConfig = (config) => /* @__PURE__ */ new Error(`\`${config.dbPath ? "baseTable` or `defineTable" : "dbPath"}\` setting must be set in the migrations config for the generator to work`);
|
|
3210
3211
|
const getDbFromConfig = async (config, dbPath) => {
|
|
3211
3212
|
const db = (await config.import(pathToFileURL(path.resolve(config.basePath, dbPath)).toString()))[config.dbExportedAs ?? "db"];
|
|
3212
3213
|
if (!db?.$qb) throw new Error(`Unable to import OrchidORM instance as ${config.dbExportedAs ?? "db"} from ${config.dbPath}`);
|
|
@@ -3484,95 +3485,128 @@ const processHasAndBelongsToManyColumn = (column, habtmTables, codeItems) => {
|
|
|
3484
3485
|
const closeAdapters = (adapters) => {
|
|
3485
3486
|
return Promise.all(adapters.map((x) => x.close()));
|
|
3486
3487
|
};
|
|
3487
|
-
const
|
|
3488
|
+
const getTableInfos = (asts, config) => {
|
|
3488
3489
|
const generateTableTo = config.generateTableTo ?? ((name) => `./tables/${name}.table.ts`);
|
|
3489
3490
|
const tableInfos = {};
|
|
3490
|
-
const fkeys = {};
|
|
3491
3491
|
for (const ast of asts) if (ast.type === "table") {
|
|
3492
3492
|
const tableKey = toCamelCase(ast.name);
|
|
3493
3493
|
const dbTableName = ast.schema ? `${ast.schema}.${ast.name}` : ast.name;
|
|
3494
3494
|
let tablePath = path.resolve(config.basePath, generateTableTo(tableKey));
|
|
3495
3495
|
if (!tablePath.endsWith(".ts")) tablePath += ".ts";
|
|
3496
3496
|
const name = toPascalCase(ast.name);
|
|
3497
|
-
|
|
3497
|
+
tableInfos[dbTableName] = {
|
|
3498
3498
|
dbTableName,
|
|
3499
3499
|
key: tableKey,
|
|
3500
3500
|
path: tablePath,
|
|
3501
3501
|
name,
|
|
3502
3502
|
className: `${name}Table`
|
|
3503
3503
|
};
|
|
3504
|
-
tableInfos[dbTableName] = info;
|
|
3505
|
-
if (ast.constraints) for (const { references } of ast.constraints) {
|
|
3506
|
-
if (!references) continue;
|
|
3507
|
-
(fkeys[references.fnOrTable] ??= []).push({
|
|
3508
|
-
table: info,
|
|
3509
|
-
references
|
|
3510
|
-
});
|
|
3511
|
-
}
|
|
3512
3504
|
}
|
|
3513
|
-
return
|
|
3514
|
-
tableInfos,
|
|
3515
|
-
fkeys
|
|
3516
|
-
};
|
|
3505
|
+
return tableInfos;
|
|
3517
3506
|
};
|
|
3518
|
-
const appCodeGenTable = (tableInfos,
|
|
3507
|
+
const appCodeGenTable = (tableInfos, ast, tableFactorySource, currentSchema) => {
|
|
3519
3508
|
const tableInfo = tableInfos[ast.schema ? `${ast.schema}.${ast.name}` : ast.name];
|
|
3520
|
-
const
|
|
3521
|
-
|
|
3522
|
-
|
|
3509
|
+
const content = appCodeGenDefineTable(tableInfo, ast, tableFactorySource, currentSchema);
|
|
3510
|
+
return {
|
|
3511
|
+
...tableInfo,
|
|
3512
|
+
content
|
|
3523
3513
|
};
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
if (ast.comment) props.push(`comment = ${singleQuote(ast.comment)};`);
|
|
3528
|
-
if (ast.noPrimaryKey === "ignore") props.push("noPrimaryKey = true;");
|
|
3514
|
+
};
|
|
3515
|
+
const appCodeGenDefineTable = (tableInfo, ast, tableFactorySource, currentSchema) => {
|
|
3516
|
+
const tableOptions = getDefineTableOptions(tableInfo, ast);
|
|
3529
3517
|
const hasTableData = Boolean(ast.primaryKey || ast.indexes?.length || ast.excludes?.length || ast.constraints?.length);
|
|
3530
|
-
const
|
|
3518
|
+
const ctx = {
|
|
3531
3519
|
t: "t",
|
|
3520
|
+
sql: "sql",
|
|
3532
3521
|
table: ast.name,
|
|
3533
3522
|
currentSchema
|
|
3534
|
-
}, ast.shape);
|
|
3535
|
-
props.push(`columns = this.setColumns(${hasTableData ? "\n " : ""}(t) => ({`, hasTableData ? [shapeCode] : shapeCode, hasTableData ? " })," : "}));");
|
|
3536
|
-
if (hasTableData) props.push(pushTableDataCode([], ast), ");");
|
|
3537
|
-
const relations = [];
|
|
3538
|
-
const belongsTo = fkeys[ast.schema ? `${ast.schema}.${ast.name}` : ast.name];
|
|
3539
|
-
if (belongsTo) for (const { table, references } of belongsTo) {
|
|
3540
|
-
imports[getImportPath(tableInfo.path, table.path)] = table.className;
|
|
3541
|
-
relations.push(`${table.key}: this.belongsTo(() => ${table.className}, {`, [`columns: [${references.foreignColumns.map(singleQuote).join(", ")}],`, `references: [${references.columns.map(singleQuote).join(", ")}],`], "}),");
|
|
3542
|
-
}
|
|
3543
|
-
if (ast.constraints) for (const { references } of ast.constraints) {
|
|
3544
|
-
if (!references) continue;
|
|
3545
|
-
const table = tableInfos[references.fnOrTable];
|
|
3546
|
-
imports[getImportPath(tableInfo.path, table.path)] = table.className;
|
|
3547
|
-
relations.push(`${table.key}: this.hasMany(() => ${table.className}, {`, [`columns: [${references.columns.map(singleQuote).join(", ")}],`, `references: [${references.foreignColumns.map(singleQuote).join(", ")}],`], "}),");
|
|
3548
|
-
}
|
|
3549
|
-
if (relations.length) props.push("", "relations = {", relations, "};");
|
|
3550
|
-
const importsCode = importsToCode(imports);
|
|
3551
|
-
const { name, className } = tableInfo;
|
|
3552
|
-
const code = [
|
|
3553
|
-
`export type ${name} = Selectable<${className}>;
|
|
3554
|
-
export type ${name}New = Insertable<${className}>;
|
|
3555
|
-
export type ${name}Update = Updatable<${className}>;
|
|
3556
|
-
|
|
3557
|
-
export class ${className} extends ${baseTableExportedAs} {`,
|
|
3558
|
-
props,
|
|
3559
|
-
"}\n"
|
|
3560
|
-
];
|
|
3561
|
-
return {
|
|
3562
|
-
...tableInfo,
|
|
3563
|
-
content: importsCode + "\n\n" + codeToString(code, "", " ")
|
|
3564
3523
|
};
|
|
3524
|
+
const shapeCode = columnsShapeToCode(ctx, ast.shape);
|
|
3525
|
+
const { name, className } = tableInfo;
|
|
3526
|
+
const code = [`export type ${name} = Selectable<typeof ${className}>;
|
|
3527
|
+
export type ${name}New = Insertable<typeof ${className}>;
|
|
3528
|
+
export type ${name}Update = Updatable<typeof ${className}>;
|
|
3529
|
+
`];
|
|
3530
|
+
if (tableOptions.length) code.push(`export const ${className} = ${tableFactorySource.exportedAs}(`, [
|
|
3531
|
+
`${singleQuote(tableInfo.key)},`,
|
|
3532
|
+
...tableOptions,
|
|
3533
|
+
"(t) => ({",
|
|
3534
|
+
shapeCode,
|
|
3535
|
+
"}),"
|
|
3536
|
+
], `)${hasTableData ? "" : ";"}`);
|
|
3537
|
+
else if (hasTableData && getTableDataItemsCount(ast) === 1) code.push(`export const ${className} = ${tableFactorySource.exportedAs}(`, [
|
|
3538
|
+
`${singleQuote(tableInfo.key)},`,
|
|
3539
|
+
"(t) => ({",
|
|
3540
|
+
shapeCode,
|
|
3541
|
+
"}),"
|
|
3542
|
+
], ")");
|
|
3543
|
+
else code.push(`export const ${className} = ${tableFactorySource.exportedAs}(${singleQuote(tableInfo.key)}, (t) => ({`, shapeCode, `}))${hasTableData ? "" : ";"}`);
|
|
3544
|
+
if (hasTableData) {
|
|
3545
|
+
let tableDataCode = getTableDataChainCode(ast, ctx);
|
|
3546
|
+
if (ast.constraints?.some(({ check }) => check)) tableDataCode = addTrailingSemicolon(tableDataCode);
|
|
3547
|
+
code.push(tableDataCode, ";");
|
|
3548
|
+
}
|
|
3549
|
+
return importsToCode({
|
|
3550
|
+
"orchid-orm": "Selectable, Insertable, Updatable",
|
|
3551
|
+
[getImportPath(tableInfo.path, tableFactorySource.path)]: ctx.isSqlUsed ? `${tableFactorySource.exportedAs}, sql` : tableFactorySource.exportedAs
|
|
3552
|
+
}) + "\n\n" + formatColumnSqlChains(codeToString(code, "", " ")) + "\n";
|
|
3553
|
+
};
|
|
3554
|
+
const getDefineTableOptions = (tableInfo, ast) => {
|
|
3555
|
+
const options = [];
|
|
3556
|
+
if (ast.schema) options.push(`schema: ${singleQuote(ast.schema)},`);
|
|
3557
|
+
if (tableInfo.key !== ast.name) options.push(`nameInDb: ${singleQuote(ast.name)},`);
|
|
3558
|
+
if (ast.comment) options.push(`comment: ${singleQuote(ast.comment)},`);
|
|
3559
|
+
if (ast.noPrimaryKey === "ignore") options.push("noPrimaryKey: true,");
|
|
3560
|
+
return options.length ? [
|
|
3561
|
+
"{",
|
|
3562
|
+
options,
|
|
3563
|
+
"},"
|
|
3564
|
+
] : [];
|
|
3565
|
+
};
|
|
3566
|
+
const getTableDataChainCode = (ast, ctx) => {
|
|
3567
|
+
const code = [];
|
|
3568
|
+
if (ast.primaryKey) code.push(primaryKeyInnerToCode(ast.primaryKey, ""));
|
|
3569
|
+
if (ast.indexes) for (const index of ast.indexes) code.push(...removeTrailingComma(indexToCode(index, "")));
|
|
3570
|
+
if (ast.excludes) for (const exclude of ast.excludes) code.push(...removeTrailingComma(excludeToCode(exclude, "")));
|
|
3571
|
+
if (ast.constraints) for (const constraint of ast.constraints) code.push(...removeTrailingComma(constraintToCode(constraint, "", true, "", ctx)));
|
|
3572
|
+
return code;
|
|
3573
|
+
};
|
|
3574
|
+
const getTableDataItemsCount = (ast) => {
|
|
3575
|
+
return (ast.primaryKey ? 1 : 0) + (ast.indexes?.length ?? 0) + (ast.excludes?.length ?? 0) + (ast.constraints?.length ?? 0);
|
|
3576
|
+
};
|
|
3577
|
+
const removeTrailingComma = (code) => {
|
|
3578
|
+
if (typeof code === "string") return removeComma(code);
|
|
3579
|
+
const copy = [...code];
|
|
3580
|
+
const lastIndex = copy.length - 1;
|
|
3581
|
+
const last = copy[lastIndex];
|
|
3582
|
+
if (last !== void 0) copy[lastIndex] = removeTrailingComma(last);
|
|
3583
|
+
return copy;
|
|
3584
|
+
};
|
|
3585
|
+
const removeComma = (code) => code.endsWith(",") ? code.slice(0, -1) : code;
|
|
3586
|
+
const formatColumnSqlChains = (code) => {
|
|
3587
|
+
return code.replace(/^(\s+\S+: )t((?:\.\w+\([^{}\n]*\))+)\.index\(\{\n((?:\s+[^\n]*\n)*?)(\s+)\}\)(\.check\(sql[^\n]*)$/gm, (line, prefix, chain, options, _closeIndent, check) => {
|
|
3588
|
+
const methodIndent = `${" ".repeat(line.match(/^\s*/)?.[0].length ?? 0)} `;
|
|
3589
|
+
const optionIndent = `${methodIndent} `;
|
|
3590
|
+
const optionLines = options.split("\n").filter(Boolean).map((line) => `${optionIndent}${line.trimStart()}`).join("\n");
|
|
3591
|
+
return `${prefix}t${chain.replace(/\./g, `\n${methodIndent}.`)}\n${methodIndent}.index({\n${optionLines}\n${methodIndent}})\n${methodIndent}${check}`;
|
|
3592
|
+
});
|
|
3593
|
+
};
|
|
3594
|
+
const addTrailingSemicolon = (code) => {
|
|
3595
|
+
if (typeof code === "string") return `${code};`;
|
|
3596
|
+
const copy = [...code];
|
|
3597
|
+
const lastIndex = copy.length - 1;
|
|
3598
|
+
const last = copy[lastIndex];
|
|
3599
|
+
if (last !== void 0) copy[lastIndex] = addTrailingSemicolon(last);
|
|
3600
|
+
return copy;
|
|
3565
3601
|
};
|
|
3566
3602
|
function importsToCode(imports) {
|
|
3567
3603
|
return Object.entries(imports).map(([from, name]) => `import { ${name} } from '${from}';`).join("\n");
|
|
3568
3604
|
}
|
|
3569
|
-
const { createSourceFile, ScriptTarget, SyntaxKind } = typescript;
|
|
3570
3605
|
const appCodeGenUpdateDbFile = async (dbPath, tables, extensions, domains, currentSchema) => {
|
|
3571
3606
|
const content = await fs.readFile(dbPath, "utf-8");
|
|
3572
|
-
const
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
const { config, tablesList } = getOrchidOrmArgs(importName, statements);
|
|
3607
|
+
const importInfo = getOrchidOrmImport(content);
|
|
3608
|
+
if (!importInfo) throw new Error(`Main file does not contain import of orchid-orm`);
|
|
3609
|
+
const { config, tablesList } = getOrchidOrmArgs(content, importInfo.importName);
|
|
3576
3610
|
const changes = [];
|
|
3577
3611
|
let replacedConfig;
|
|
3578
3612
|
if (extensions.length || domains.length) {
|
|
@@ -3588,32 +3622,18 @@ const appCodeGenUpdateDbFile = async (dbPath, tables, extensions, domains, curre
|
|
|
3588
3622
|
}, ast.baseType.data.name ?? "")},`).join("\n ")}\n },`;
|
|
3589
3623
|
replacedConfig = code + "\n}";
|
|
3590
3624
|
}
|
|
3591
|
-
const tablesChanges = makeTablesListChanges(content,
|
|
3625
|
+
const tablesChanges = makeTablesListChanges(content, importInfo.lastImportEnd, tablesList, tables, dbPath);
|
|
3592
3626
|
if (tablesChanges) addChange(content, changes, tablesChanges.imports.pos, tablesChanges.imports.text);
|
|
3593
3627
|
if (replacedConfig) replaceContent(content, changes, config.pos, config.end, replacedConfig);
|
|
3594
3628
|
if (tablesChanges) addChange(content, changes, tablesChanges.tablesList.pos, tablesChanges.tablesList.text);
|
|
3595
3629
|
return applyChanges(content, changes);
|
|
3596
3630
|
};
|
|
3597
|
-
const
|
|
3598
|
-
return createSourceFile("file.ts", content, ScriptTarget.Latest, true).statements;
|
|
3599
|
-
};
|
|
3600
|
-
const getOrchidOrmImportName = (statements) => {
|
|
3601
|
-
for (const node of statements) {
|
|
3602
|
-
if (node.kind !== SyntaxKind.ImportDeclaration) continue;
|
|
3603
|
-
const imp = node;
|
|
3604
|
-
if (imp.moduleSpecifier.getText().slice(1, -1) !== "orchid-orm") continue;
|
|
3605
|
-
if (!imp.importClause) continue;
|
|
3606
|
-
const elements = imp.importClause.namedBindings?.elements;
|
|
3607
|
-
if (!elements);
|
|
3608
|
-
for (const element of elements) if (element.name.escapedText === "orchidORM" || element.propertyName && "escapedText" in element.propertyName && element.propertyName.escapedText === "orchidORM") return element.name.escapedText.toString();
|
|
3609
|
-
}
|
|
3610
|
-
};
|
|
3611
|
-
const makeTablesListChanges = (content, statements, object, tables, dbPath) => {
|
|
3631
|
+
const makeTablesListChanges = (content, importPos, object, tables, dbPath) => {
|
|
3612
3632
|
const spaces = getTablesListSpaces(content, object);
|
|
3613
3633
|
let imports = "";
|
|
3614
3634
|
let tablesList = "";
|
|
3615
|
-
const prependComma = object.
|
|
3616
|
-
const tablesListNewLine = content.slice(object.
|
|
3635
|
+
const prependComma = object.hasProperties && !object.hasTrailingComma;
|
|
3636
|
+
const tablesListNewLine = content.slice(object.propertiesEnd, object.end).includes("\n");
|
|
3617
3637
|
const tablesArr = Object.values(tables);
|
|
3618
3638
|
for (let i = 0; i < tablesArr.length; i++) {
|
|
3619
3639
|
const { path, className, key } = tablesArr[i];
|
|
@@ -3623,15 +3643,13 @@ const makeTablesListChanges = (content, statements, object, tables, dbPath) => {
|
|
|
3623
3643
|
if (i === tablesArr.length - 1 && !tablesListNewLine) tablesList += `\n${spaces}`;
|
|
3624
3644
|
}
|
|
3625
3645
|
if (!imports.length) return;
|
|
3626
|
-
let importPos = 0;
|
|
3627
|
-
for (const node of statements) if (node.kind === SyntaxKind.ImportDeclaration) importPos = node.end;
|
|
3628
3646
|
return {
|
|
3629
3647
|
imports: {
|
|
3630
3648
|
pos: importPos,
|
|
3631
3649
|
text: imports
|
|
3632
3650
|
},
|
|
3633
3651
|
tablesList: {
|
|
3634
|
-
pos: object.
|
|
3652
|
+
pos: object.propertiesEnd,
|
|
3635
3653
|
text: tablesList
|
|
3636
3654
|
}
|
|
3637
3655
|
};
|
|
@@ -3640,24 +3658,165 @@ const getTablesListSpaces = (content, object) => {
|
|
|
3640
3658
|
const lines = content.slice(0, object.end).split("\n");
|
|
3641
3659
|
return lines[lines.length - 1].match(/^\s+/)?.[0] || "";
|
|
3642
3660
|
};
|
|
3643
|
-
const getOrchidOrmArgs = (
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3661
|
+
const getOrchidOrmArgs = (content, importName) => {
|
|
3662
|
+
const call = findCall(content, importName);
|
|
3663
|
+
if (!call) throw new Error("List of tables is not found in main file");
|
|
3664
|
+
const closeParen = findClosingBracket(content, call.openParen);
|
|
3665
|
+
if (closeParen === -1) throw new Error("List of tables is not found in main file");
|
|
3666
|
+
const firstComma = findTopLevelComma(content, call.openParen + 1, closeParen);
|
|
3667
|
+
if (firstComma === -1) throw new Error("Invalid number of arguments when initializing orchid orm");
|
|
3668
|
+
if (findTopLevelComma(content, firstComma + 1, closeParen) !== -1) throw new Error("Invalid number of arguments when initializing orchid orm");
|
|
3669
|
+
const config = makeTrimmedRange(content, call.openParen + 1, firstComma);
|
|
3670
|
+
const tablesList = getObjectRange(content, firstComma + 1, closeParen);
|
|
3671
|
+
if (!tablesList) throw new Error("Second argument of orchidORM must be an object literal");
|
|
3672
|
+
return {
|
|
3673
|
+
config,
|
|
3674
|
+
tablesList
|
|
3675
|
+
};
|
|
3676
|
+
};
|
|
3677
|
+
const getOrchidOrmImport = (content) => {
|
|
3678
|
+
let lastImportEnd = 0;
|
|
3679
|
+
let importName;
|
|
3680
|
+
const importRegex = /(^|\n)\s*import\s+([\s\S]*?)\s+from\s+['"]([^'"]+)['"]\s*;?/g;
|
|
3681
|
+
let match;
|
|
3682
|
+
while (match = importRegex.exec(content)) {
|
|
3683
|
+
lastImportEnd = importRegex.lastIndex;
|
|
3684
|
+
if (match[3] !== "orchid-orm") continue;
|
|
3685
|
+
const named = match[2].match(/\{([\s\S]*)\}/);
|
|
3686
|
+
if (!named) continue;
|
|
3687
|
+
for (const part of named[1].split(",")) {
|
|
3688
|
+
const item = part.trim();
|
|
3689
|
+
const alias = item.match(/^orchidORM\s+as\s+([A-Za-z_$][\w$]*)$/);
|
|
3690
|
+
if (alias) {
|
|
3691
|
+
importName = alias[1];
|
|
3692
|
+
continue;
|
|
3693
|
+
}
|
|
3694
|
+
if (item === "orchidORM") importName = "orchidORM";
|
|
3657
3695
|
}
|
|
3658
3696
|
}
|
|
3697
|
+
if (!importName) return;
|
|
3698
|
+
return {
|
|
3699
|
+
importName,
|
|
3700
|
+
lastImportEnd
|
|
3701
|
+
};
|
|
3702
|
+
};
|
|
3703
|
+
const findCall = (content, importName) => {
|
|
3704
|
+
let index = 0;
|
|
3705
|
+
while (index < content.length) {
|
|
3706
|
+
const found = content.indexOf(importName, index);
|
|
3707
|
+
if (found === -1) return;
|
|
3708
|
+
const before = content[found - 1];
|
|
3709
|
+
const after = content[found + importName.length];
|
|
3710
|
+
if ((!before || !isIdentifierChar(before)) && (!after || !isIdentifierChar(after))) {
|
|
3711
|
+
const openParen = skipWhitespace(content, found + importName.length);
|
|
3712
|
+
if (content[openParen] === "(") return { openParen };
|
|
3713
|
+
}
|
|
3714
|
+
index = found + importName.length;
|
|
3715
|
+
}
|
|
3659
3716
|
throw new Error("List of tables is not found in main file");
|
|
3660
3717
|
};
|
|
3718
|
+
const getObjectRange = (content, from, to) => {
|
|
3719
|
+
const pos = skipWhitespace(content, from);
|
|
3720
|
+
if (content[pos] !== "{") return;
|
|
3721
|
+
const end = findClosingBracket(content, pos);
|
|
3722
|
+
if (end === -1 || skipWhitespace(content, end + 1) < to) return;
|
|
3723
|
+
const lastBodyChar = findLastNonWhitespace(content, pos + 1, end);
|
|
3724
|
+
const hasProperties = lastBodyChar !== -1;
|
|
3725
|
+
const hasTrailingComma = hasProperties && content[lastBodyChar] === ",";
|
|
3726
|
+
const propertiesEnd = hasProperties ? lastBodyChar + 1 : pos + 1;
|
|
3727
|
+
return {
|
|
3728
|
+
pos,
|
|
3729
|
+
end: end + 1,
|
|
3730
|
+
propertiesEnd,
|
|
3731
|
+
hasProperties,
|
|
3732
|
+
hasTrailingComma
|
|
3733
|
+
};
|
|
3734
|
+
};
|
|
3735
|
+
const makeTrimmedRange = (content, from, to) => {
|
|
3736
|
+
return {
|
|
3737
|
+
pos: skipWhitespace(content, from),
|
|
3738
|
+
end: trimWhitespaceEnd(content, from, to)
|
|
3739
|
+
};
|
|
3740
|
+
};
|
|
3741
|
+
const findTopLevelComma = (content, from, to) => {
|
|
3742
|
+
let depth = 0;
|
|
3743
|
+
for (let i = from; i < to; i++) {
|
|
3744
|
+
const skipped = skipCodeFragment(content, i);
|
|
3745
|
+
if (skipped !== i) {
|
|
3746
|
+
i = skipped - 1;
|
|
3747
|
+
continue;
|
|
3748
|
+
}
|
|
3749
|
+
const char = content[i];
|
|
3750
|
+
if (char === "(" || char === "{" || char === "[") depth++;
|
|
3751
|
+
else if (char === ")" || char === "}" || char === "]") depth--;
|
|
3752
|
+
else if (char === "," && depth === 0) return i;
|
|
3753
|
+
}
|
|
3754
|
+
return -1;
|
|
3755
|
+
};
|
|
3756
|
+
const findClosingBracket = (content, open) => {
|
|
3757
|
+
const close = getClosingBracket(content[open]);
|
|
3758
|
+
if (!close) return -1;
|
|
3759
|
+
let depth = 1;
|
|
3760
|
+
for (let i = open + 1; i < content.length; i++) {
|
|
3761
|
+
const skipped = skipCodeFragment(content, i);
|
|
3762
|
+
if (skipped !== i) {
|
|
3763
|
+
i = skipped - 1;
|
|
3764
|
+
continue;
|
|
3765
|
+
}
|
|
3766
|
+
const char = content[i];
|
|
3767
|
+
if (char === content[open]) depth++;
|
|
3768
|
+
else if (char === close) {
|
|
3769
|
+
depth--;
|
|
3770
|
+
if (depth === 0) return i;
|
|
3771
|
+
}
|
|
3772
|
+
}
|
|
3773
|
+
return -1;
|
|
3774
|
+
};
|
|
3775
|
+
const getClosingBracket = (char) => {
|
|
3776
|
+
if (char === "(") return ")";
|
|
3777
|
+
if (char === "{") return "}";
|
|
3778
|
+
if (char === "[") return "]";
|
|
3779
|
+
};
|
|
3780
|
+
const skipCodeFragment = (content, i) => {
|
|
3781
|
+
const char = content[i];
|
|
3782
|
+
if (char === "'" || char === "\"" || char === "`") return skipQuoted(content, i, char);
|
|
3783
|
+
if (char === "/" && content[i + 1] === "/") {
|
|
3784
|
+
const nextLine = content.indexOf("\n", i + 2);
|
|
3785
|
+
return nextLine === -1 ? content.length : nextLine + 1;
|
|
3786
|
+
}
|
|
3787
|
+
if (char === "/" && content[i + 1] === "*") {
|
|
3788
|
+
const end = content.indexOf("*/", i + 2);
|
|
3789
|
+
return end === -1 ? content.length : end + 2;
|
|
3790
|
+
}
|
|
3791
|
+
return i;
|
|
3792
|
+
};
|
|
3793
|
+
const skipQuoted = (content, i, quote) => {
|
|
3794
|
+
for (let j = i + 1; j < content.length; j++) {
|
|
3795
|
+
if (content[j] === "\\") {
|
|
3796
|
+
j++;
|
|
3797
|
+
continue;
|
|
3798
|
+
}
|
|
3799
|
+
if (content[j] === quote) return j + 1;
|
|
3800
|
+
}
|
|
3801
|
+
return content.length;
|
|
3802
|
+
};
|
|
3803
|
+
const skipWhitespace = (content, from) => {
|
|
3804
|
+
let i = from;
|
|
3805
|
+
while (/\s/.test(content[i])) i++;
|
|
3806
|
+
return i;
|
|
3807
|
+
};
|
|
3808
|
+
const trimWhitespaceEnd = (content, from, to) => {
|
|
3809
|
+
let i = to;
|
|
3810
|
+
while (i > from && /\s/.test(content[i - 1])) i--;
|
|
3811
|
+
return i;
|
|
3812
|
+
};
|
|
3813
|
+
const findLastNonWhitespace = (content, from, to) => {
|
|
3814
|
+
for (let i = to - 1; i >= from; i--) if (!/\s/.test(content[i])) return i;
|
|
3815
|
+
return -1;
|
|
3816
|
+
};
|
|
3817
|
+
const isIdentifierChar = (char) => {
|
|
3818
|
+
return /[A-Za-z0-9_$]/.test(char);
|
|
3819
|
+
};
|
|
3661
3820
|
const addChange = (content, changes, at, text, end = at) => {
|
|
3662
3821
|
if (changes.length === 0) changes.push([0, at], text, [end, content.length]);
|
|
3663
3822
|
else {
|
|
@@ -3673,14 +3832,21 @@ const applyChanges = (content, changes) => {
|
|
|
3673
3832
|
return changes.length ? changes.map((item) => typeof item === "string" ? item : content.slice(item[0], item[1])).join("") : content;
|
|
3674
3833
|
};
|
|
3675
3834
|
const pull = async (adapters, config) => {
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3835
|
+
const { defineTable } = config;
|
|
3836
|
+
if (!config.dbPath || !defineTable) throw new Error(`\`${config.dbPath ? "defineTable" : "dbPath"}\` setting must be set in the migrations config for pull command`);
|
|
3837
|
+
if (!defineTable.types) throw new Error("defineTable is missing types");
|
|
3838
|
+
if (!defineTable.exportAs) throw new Error("defineTable is missing exportAs");
|
|
3839
|
+
if (typeof defineTable.getFilePath !== "function") throw new Error("defineTable is missing getFilePath");
|
|
3840
|
+
const tableFactorySource = {
|
|
3841
|
+
path: defineTable.getFilePath(),
|
|
3842
|
+
exportedAs: defineTable.exportAs
|
|
3843
|
+
};
|
|
3679
3844
|
const [adapter] = adapters;
|
|
3680
3845
|
const adapterSchema = adapter.getSchema();
|
|
3681
3846
|
const currentSchema = (typeof adapterSchema === "function" ? adapterSchema() : adapterSchema) ?? "public";
|
|
3682
|
-
const
|
|
3683
|
-
const
|
|
3847
|
+
const ctx = makeStructureToAstCtx(config, currentSchema);
|
|
3848
|
+
const asts = await structureToAst(ctx, adapter, config);
|
|
3849
|
+
const tableInfos = getTableInfos(asts, config);
|
|
3684
3850
|
const exclusiveWriteOptions = { flag: "wx" };
|
|
3685
3851
|
const pendingFileWrites = [];
|
|
3686
3852
|
const tables = {};
|
|
@@ -3689,7 +3855,7 @@ const pull = async (adapters, config) => {
|
|
|
3689
3855
|
let firstTable;
|
|
3690
3856
|
for (const ast of asts) switch (ast.type) {
|
|
3691
3857
|
case "table": {
|
|
3692
|
-
const table = appCodeGenTable(tableInfos,
|
|
3858
|
+
const table = appCodeGenTable(tableInfos, ast, tableFactorySource, currentSchema);
|
|
3693
3859
|
tables[table.key] = table;
|
|
3694
3860
|
if (!firstTable) firstTable = table;
|
|
3695
3861
|
pendingFileWrites.push([
|
|
@@ -3705,9 +3871,7 @@ const pull = async (adapters, config) => {
|
|
|
3705
3871
|
version: ast.version
|
|
3706
3872
|
});
|
|
3707
3873
|
break;
|
|
3708
|
-
case "domain":
|
|
3709
|
-
domains.push(ast);
|
|
3710
|
-
break;
|
|
3874
|
+
case "domain": domains.push(ast);
|
|
3711
3875
|
}
|
|
3712
3876
|
if (!firstTable && !extensions.length && !domains.length) {
|
|
3713
3877
|
await adapter.close();
|