orchid-orm 1.76.4 → 1.77.0

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.
@@ -1,5 +1,5 @@
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, pushTableDataCode, queryToSql, quoteObjectKey, raw, rawSqlToSql, singleQuote, sqlToRawSql, toArray, toCamelCase, toPascalCase, toSnakeCase } from "pqb/internal";
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";
@@ -1279,7 +1279,7 @@ const collectCodeFkeys = (config, { codeTable, changeTableAst: { shape } }, curr
1279
1279
  };
1280
1280
  const fnOrTableToString = (fnOrTable) => {
1281
1281
  if (typeof fnOrTable !== "string") {
1282
- const { schema, table, nameInDb } = new (fnOrTable())();
1282
+ const { schema, table, nameInDb } = getForeignKeyTableInstance(fnOrTable());
1283
1283
  const name = nameInDb || table;
1284
1284
  if (!name) throw new Error("Referenced table is missing table property");
1285
1285
  fnOrTable = concatSchemaAndName({
@@ -3136,7 +3136,7 @@ const formatPolicyChangeDefinition = (policy) => {
3136
3136
  var AbortSignal = class extends Error {};
3137
3137
  const generate = async (adapters, config, args, afterPull) => {
3138
3138
  let { dbPath } = config;
3139
- if (!dbPath || !config.baseTable) throw invalidConfig(config);
3139
+ if (!dbPath || !getTableFactoryConfig(config)) throw invalidConfig(config);
3140
3140
  if (!adapters.length) throw new Error(`Database options must not be empty`);
3141
3141
  if (!dbPath.endsWith(".ts")) dbPath += ".ts";
3142
3142
  let migrationName = args[0] ?? "generated";
@@ -3206,7 +3206,7 @@ const generate = async (adapters, config, args, afterPull) => {
3206
3206
  if (up) for (const adapter of adapters) await migrateAndClose(adapter, config);
3207
3207
  else if (!afterPull) await closeAdapters(adapters);
3208
3208
  };
3209
- const invalidConfig = (config) => /* @__PURE__ */ new Error(`\`${config.dbPath ? "baseTable" : "dbPath"}\` setting must be set in the migrations config for the generator to work`);
3209
+ 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
3210
  const getDbFromConfig = async (config, dbPath) => {
3211
3211
  const db = (await config.import(pathToFileURL(path.resolve(config.basePath, dbPath)).toString()))[config.dbExportedAs ?? "db"];
3212
3212
  if (!db?.$qb) throw new Error(`Unable to import OrchidORM instance as ${config.dbExportedAs ?? "db"} from ${config.dbPath}`);
@@ -3484,84 +3484,119 @@ const processHasAndBelongsToManyColumn = (column, habtmTables, codeItems) => {
3484
3484
  const closeAdapters = (adapters) => {
3485
3485
  return Promise.all(adapters.map((x) => x.close()));
3486
3486
  };
3487
- const getTableInfosAndFKeys = (asts, config) => {
3487
+ const getTableInfos = (asts, config) => {
3488
3488
  const generateTableTo = config.generateTableTo ?? ((name) => `./tables/${name}.table.ts`);
3489
3489
  const tableInfos = {};
3490
- const fkeys = {};
3491
3490
  for (const ast of asts) if (ast.type === "table") {
3492
3491
  const tableKey = toCamelCase(ast.name);
3493
3492
  const dbTableName = ast.schema ? `${ast.schema}.${ast.name}` : ast.name;
3494
3493
  let tablePath = path.resolve(config.basePath, generateTableTo(tableKey));
3495
3494
  if (!tablePath.endsWith(".ts")) tablePath += ".ts";
3496
3495
  const name = toPascalCase(ast.name);
3497
- const info = {
3496
+ tableInfos[dbTableName] = {
3498
3497
  dbTableName,
3499
3498
  key: tableKey,
3500
3499
  path: tablePath,
3501
3500
  name,
3502
3501
  className: `${name}Table`
3503
3502
  };
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
3503
  }
3513
- return {
3514
- tableInfos,
3515
- fkeys
3516
- };
3504
+ return tableInfos;
3517
3505
  };
3518
- const appCodeGenTable = (tableInfos, fkeys, ast, baseTablePath, baseTableExportedAs, currentSchema) => {
3506
+ const appCodeGenTable = (tableInfos, ast, tableFactorySource, currentSchema) => {
3519
3507
  const tableInfo = tableInfos[ast.schema ? `${ast.schema}.${ast.name}` : ast.name];
3520
- const imports = {
3521
- "orchid-orm": "Selectable, Insertable, Updatable",
3522
- [getImportPath(tableInfo.path, baseTablePath)]: baseTableExportedAs
3508
+ const content = appCodeGenDefineTable(tableInfo, ast, tableFactorySource, currentSchema);
3509
+ return {
3510
+ ...tableInfo,
3511
+ content
3523
3512
  };
3524
- const props = [];
3525
- if (ast.schema) props.push(`schema = ${singleQuote(ast.schema)};`);
3526
- props.push(`readonly table = ${singleQuote(ast.name)};`);
3527
- if (ast.comment) props.push(`comment = ${singleQuote(ast.comment)};`);
3528
- if (ast.noPrimaryKey === "ignore") props.push("noPrimaryKey = true;");
3513
+ };
3514
+ const appCodeGenDefineTable = (tableInfo, ast, tableFactorySource, currentSchema) => {
3515
+ const tableOptions = getDefineTableOptions(tableInfo, ast);
3529
3516
  const hasTableData = Boolean(ast.primaryKey || ast.indexes?.length || ast.excludes?.length || ast.constraints?.length);
3530
- const shapeCode = columnsShapeToCode({
3517
+ const ctx = {
3531
3518
  t: "t",
3519
+ sql: "sql",
3532
3520
  table: ast.name,
3533
3521
  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
3522
  };
3523
+ const shapeCode = columnsShapeToCode(ctx, ast.shape);
3524
+ const { name, className } = tableInfo;
3525
+ const code = [`export type ${name} = Selectable<typeof ${className}>;
3526
+ export type ${name}New = Insertable<typeof ${className}>;
3527
+ export type ${name}Update = Updatable<typeof ${className}>;
3528
+ `];
3529
+ if (tableOptions.length) code.push(`export const ${className} = ${tableFactorySource.exportedAs}(`, [
3530
+ `${singleQuote(tableInfo.key)},`,
3531
+ ...tableOptions,
3532
+ "(t) => ({",
3533
+ shapeCode,
3534
+ "}),"
3535
+ ], `)${hasTableData ? "" : ";"}`);
3536
+ else if (hasTableData && getTableDataItemsCount(ast) === 1) code.push(`export const ${className} = ${tableFactorySource.exportedAs}(`, [
3537
+ `${singleQuote(tableInfo.key)},`,
3538
+ "(t) => ({",
3539
+ shapeCode,
3540
+ "}),"
3541
+ ], ")");
3542
+ else code.push(`export const ${className} = ${tableFactorySource.exportedAs}(${singleQuote(tableInfo.key)}, (t) => ({`, shapeCode, `}))${hasTableData ? "" : ";"}`);
3543
+ if (hasTableData) {
3544
+ let tableDataCode = getTableDataChainCode(ast, ctx);
3545
+ if (ast.constraints?.some(({ check }) => check)) tableDataCode = addTrailingSemicolon(tableDataCode);
3546
+ code.push(tableDataCode, ";");
3547
+ }
3548
+ return importsToCode({
3549
+ "orchid-orm": "Selectable, Insertable, Updatable",
3550
+ [getImportPath(tableInfo.path, tableFactorySource.path)]: ctx.isSqlUsed ? `${tableFactorySource.exportedAs}, sql` : tableFactorySource.exportedAs
3551
+ }) + "\n\n" + formatColumnSqlChains(codeToString(code, "", " ")) + "\n";
3552
+ };
3553
+ const getDefineTableOptions = (tableInfo, ast) => {
3554
+ const options = [];
3555
+ if (ast.schema) options.push(`schema: ${singleQuote(ast.schema)},`);
3556
+ if (tableInfo.key !== ast.name) options.push(`nameInDb: ${singleQuote(ast.name)},`);
3557
+ if (ast.comment) options.push(`comment: ${singleQuote(ast.comment)},`);
3558
+ if (ast.noPrimaryKey === "ignore") options.push("noPrimaryKey: true,");
3559
+ return options.length ? [
3560
+ "{",
3561
+ options,
3562
+ "},"
3563
+ ] : [];
3564
+ };
3565
+ const getTableDataChainCode = (ast, ctx) => {
3566
+ const code = [];
3567
+ if (ast.primaryKey) code.push(primaryKeyInnerToCode(ast.primaryKey, ""));
3568
+ if (ast.indexes) for (const index of ast.indexes) code.push(...removeTrailingComma(indexToCode(index, "")));
3569
+ if (ast.excludes) for (const exclude of ast.excludes) code.push(...removeTrailingComma(excludeToCode(exclude, "")));
3570
+ if (ast.constraints) for (const constraint of ast.constraints) code.push(...removeTrailingComma(constraintToCode(constraint, "", true, "", ctx)));
3571
+ return code;
3572
+ };
3573
+ const getTableDataItemsCount = (ast) => {
3574
+ return (ast.primaryKey ? 1 : 0) + (ast.indexes?.length ?? 0) + (ast.excludes?.length ?? 0) + (ast.constraints?.length ?? 0);
3575
+ };
3576
+ const removeTrailingComma = (code) => {
3577
+ if (typeof code === "string") return removeComma(code);
3578
+ const copy = [...code];
3579
+ const lastIndex = copy.length - 1;
3580
+ const last = copy[lastIndex];
3581
+ if (last !== void 0) copy[lastIndex] = removeTrailingComma(last);
3582
+ return copy;
3583
+ };
3584
+ const removeComma = (code) => code.endsWith(",") ? code.slice(0, -1) : code;
3585
+ const formatColumnSqlChains = (code) => {
3586
+ return code.replace(/^(\s+\S+: )t((?:\.\w+\([^{}\n]*\))+)\.index\(\{\n((?:\s+[^\n]*\n)*?)(\s+)\}\)(\.check\(sql[^\n]*)$/gm, (line, prefix, chain, options, _closeIndent, check) => {
3587
+ const methodIndent = `${" ".repeat(line.match(/^\s*/)?.[0].length ?? 0)} `;
3588
+ const optionIndent = `${methodIndent} `;
3589
+ const optionLines = options.split("\n").filter(Boolean).map((line) => `${optionIndent}${line.trimStart()}`).join("\n");
3590
+ return `${prefix}t${chain.replace(/\./g, `\n${methodIndent}.`)}\n${methodIndent}.index({\n${optionLines}\n${methodIndent}})\n${methodIndent}${check}`;
3591
+ });
3592
+ };
3593
+ const addTrailingSemicolon = (code) => {
3594
+ if (typeof code === "string") return `${code};`;
3595
+ const copy = [...code];
3596
+ const lastIndex = copy.length - 1;
3597
+ const last = copy[lastIndex];
3598
+ if (last !== void 0) copy[lastIndex] = addTrailingSemicolon(last);
3599
+ return copy;
3565
3600
  };
3566
3601
  function importsToCode(imports) {
3567
3602
  return Object.entries(imports).map(([from, name]) => `import { ${name} } from '${from}';`).join("\n");
@@ -3673,14 +3708,20 @@ const applyChanges = (content, changes) => {
3673
3708
  return changes.length ? changes.map((item) => typeof item === "string" ? item : content.slice(item[0], item[1])).join("") : content;
3674
3709
  };
3675
3710
  const pull = async (adapters, config) => {
3676
- if (!config.dbPath || !config.baseTable) throw new Error(`\`${config.dbPath ? "baseTable" : "dbPath"}\` setting must be set in the migrations config for pull command`);
3677
- const baseTablePath = config.baseTable.getFilePath();
3678
- const baseTableExportedAs = config.baseTable.exportAs;
3711
+ const { defineTable } = config;
3712
+ if (!config.dbPath || !defineTable) throw new Error(`\`${config.dbPath ? "defineTable" : "dbPath"}\` setting must be set in the migrations config for pull command`);
3713
+ if (!defineTable.types) throw new Error("defineTable is missing types");
3714
+ if (!defineTable.exportAs) throw new Error("defineTable is missing exportAs");
3715
+ if (typeof defineTable.getFilePath !== "function") throw new Error("defineTable is missing getFilePath");
3716
+ const tableFactorySource = {
3717
+ path: defineTable.getFilePath(),
3718
+ exportedAs: defineTable.exportAs
3719
+ };
3679
3720
  const [adapter] = adapters;
3680
3721
  const adapterSchema = adapter.getSchema();
3681
3722
  const currentSchema = (typeof adapterSchema === "function" ? adapterSchema() : adapterSchema) ?? "public";
3682
3723
  const asts = await structureToAst(makeStructureToAstCtx(config, currentSchema), adapter, config);
3683
- const { tableInfos, fkeys } = getTableInfosAndFKeys(asts, config);
3724
+ const tableInfos = getTableInfos(asts, config);
3684
3725
  const exclusiveWriteOptions = { flag: "wx" };
3685
3726
  const pendingFileWrites = [];
3686
3727
  const tables = {};
@@ -3689,7 +3730,7 @@ const pull = async (adapters, config) => {
3689
3730
  let firstTable;
3690
3731
  for (const ast of asts) switch (ast.type) {
3691
3732
  case "table": {
3692
- const table = appCodeGenTable(tableInfos, fkeys, ast, baseTablePath, baseTableExportedAs, currentSchema);
3733
+ const table = appCodeGenTable(tableInfos, ast, tableFactorySource, currentSchema);
3693
3734
  tables[table.key] = table;
3694
3735
  if (!firstTable) firstTable = table;
3695
3736
  pendingFileWrites.push([