durcno 1.0.0-alpha.7 → 1.0.0-alpha.8

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/README.md CHANGED
@@ -22,8 +22,9 @@
22
22
 
23
23
  - **🔗 Relation Mapping** — Intuitive `many`, `one`, and `fk` relations with full type inference.
24
24
  - **🦾 Robust Migrations** — Auto-generated, reversible, and squashable migrations for production applications.
25
- - **🚀 Zero Runtime Overhead** — Thin abstraction layer that compiles to efficient SQL.
25
+ - **⚡ Zero Runtime Overhead** — Thin abstraction layer that compiles to efficient SQL.
26
26
  - **🔌 Multiple Drivers** — Support for `pg`, `postgres`, `bun`, and `pglite` drivers.
27
+ - **🛡️ Zod Integration** — Built-in Zod validators for schema validation and type inference.
27
28
  - **🌍 PostGIS Support** — First-class geographic column types for spatial queries.
28
29
 
29
30
  ## Setup
@@ -40,7 +41,7 @@ npm exec durcno init
40
41
 
41
42
  Get started with Durcno by following our comprehensive documentation.
42
43
 
43
- **[Read the Documentation](https://durcno.dev/docs/latest/intro)**
44
+ **[Getting Started | Durcno](https://durcno.dev/docs/latest/getting-started)**
44
45
 
45
46
  > [!WARNING]
46
47
  > Durcno is currently in the alpha stage.
package/dist/bin.cjs CHANGED
@@ -13603,7 +13603,7 @@ async function status(options) {
13603
13603
  }
13604
13604
 
13605
13605
  // src/cli/index.ts
13606
- program.version("1.0.0-alpha.6");
13606
+ program.version("1.0.0-alpha.7");
13607
13607
  var Options = {
13608
13608
  config: ["--config <path>", "Path to the config file"]
13609
13609
  };
@@ -1,4 +1,4 @@
1
- import { DurcnoLogger } from "../logger.mjs";
1
+ import { QueryLogger } from "../logger.mjs";
2
2
  import { Query } from "../query-builders/query.mjs";
3
3
  import { MigrationOptions } from "../migration/index.mjs";
4
4
  import { ConnectionOptions } from "node:tls";
@@ -43,7 +43,7 @@ type ConnectorOptions = {
43
43
  * When set, all executed queries will be logged at the `info` level with
44
44
  * structured `{ sql, arguments }` metadata.
45
45
  */
46
- logger?: DurcnoLogger;
46
+ logger?: QueryLogger;
47
47
  };
48
48
  /**
49
49
  * Abstract base class for all database connectors.
@@ -83,7 +83,7 @@ declare abstract class Connector {
83
83
  /** Connection pool size override (can be mutated by CLI commands before `getPool()` is called). */
84
84
  pool?: ConnectorOptions["pool"];
85
85
  /** Optional logger instance for query logging. */
86
- logger?: DurcnoLogger;
86
+ logger?: QueryLogger;
87
87
  constructor(options: ConnectorOptions);
88
88
  /**
89
89
  * Creates a single-connection client.
@@ -116,7 +116,7 @@ declare abstract class $QueryExecutor {
116
116
  /** The connector options used to create this executor. */
117
117
  options: ConnectorOptions;
118
118
  /** Optional logger instance for query logging. */
119
- logger?: DurcnoLogger;
119
+ logger?: QueryLogger;
120
120
  constructor(options: ConnectorOptions);
121
121
  /**
122
122
  * Executes a SQL query with optional parameterized arguments.
@@ -1,4 +1,4 @@
1
- import { DurcnoLogger } from "./logger.mjs";
1
+ import { QueryLogger } from "./logger.mjs";
2
2
  import { Is, Key, Prettify, SelfOrArray, SelfOrReadonly, UnionToIntersection, Valueof } from "./types.mjs";
3
3
  import { Column, ColumnConfig, notNull, primaryKey, unique } from "./columns/common.mjs";
4
4
  import { PrimaryKeyConstraint, primaryKeyConstraint } from "./constraints/primary-key.mjs";
@@ -93,4 +93,4 @@ type Config<T extends Connector = Connector> = {
93
93
  connector: T;
94
94
  };
95
95
  //#endregion
96
- export { $, type $Client, type AnyColumn, type AnyTableColumn, Arg, Column, type ColumnConfig, Config, type ConnectorOptions, type DurcnoLogger, Filter, type Is, type Key, Migrations, type Prettify, PrimaryKeyConstraint, Query, type SelfOrArray, type SelfOrReadonly, Sql, type TableColumn, type UnionToIntersection, UniqueConstraint, type UuidVersion, type Valueof, and, arrayAll, arrayContainedBy, arrayContains, arrayHas, arrayOverlaps, asc, bigint, bigserial, boolean, bytea, char, cidr, database, date, defineConfig, desc, enumed, enumtype, eq, fk, geography, gt, gte, index, inet, integer, isIn, isNotNull, isNull, json, jsonb, lt, lte, macaddr, many, ne, notNull, now, numeric, one, or, pk, prequery, primaryKey, primaryKeyConstraint, relations, sequence, serial, smallint, smallserial, sql, table, text, time, timestamp, unique, uniqueConstraint, uniqueIndex, uuid, uuidv4, uuidv7, varchar };
96
+ export { $, type $Client, type AnyColumn, type AnyTableColumn, Arg, Column, type ColumnConfig, Config, type ConnectorOptions, Filter, type Is, type Key, Migrations, type Prettify, PrimaryKeyConstraint, Query, type QueryLogger, type SelfOrArray, type SelfOrReadonly, Sql, type TableColumn, type UnionToIntersection, UniqueConstraint, type UuidVersion, type Valueof, and, arrayAll, arrayContainedBy, arrayContains, arrayHas, arrayOverlaps, asc, bigint, bigserial, boolean, bytea, char, cidr, database, date, defineConfig, desc, enumed, enumtype, eq, fk, geography, gt, gte, index, inet, integer, isIn, isNotNull, isNull, json, jsonb, lt, lte, macaddr, many, ne, notNull, now, numeric, one, or, pk, prequery, primaryKey, primaryKeyConstraint, relations, sequence, serial, smallint, smallserial, sql, table, text, time, timestamp, unique, uniqueConstraint, uniqueIndex, uuid, uuidv4, uuidv7, varchar };
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Any object satisfying this contract can be used as a Durcno query logger.
6
6
  */
7
- interface DurcnoLogger {
7
+ interface QueryLogger {
8
8
  info(message: string, meta?: Record<string, unknown>): void;
9
9
  }
10
10
  /**
@@ -27,10 +27,10 @@ interface DurcnoLogger {
27
27
  * });
28
28
  * ```
29
29
  */
30
- declare function createQueryLogger(): DurcnoLogger;
30
+ declare function createQueryLogger(): QueryLogger;
31
31
  /**
32
32
  * @deprecated Use `createQueryLogger` instead.
33
33
  */
34
34
  declare const createDurcnoLogger: typeof createQueryLogger;
35
35
  //#endregion
36
- export { DurcnoLogger, createDurcnoLogger, createQueryLogger };
36
+ export { QueryLogger, createDurcnoLogger, createQueryLogger };
@@ -1,4 +1,4 @@
1
- import { AnyColumn, AnyRelation, Fk, Many, Relations, StdRelations, TColsToLeftRight, TableWithColumns } from "../table.mjs";
1
+ import { AnyColumn, AnyRelation, Fk, Many, One, Relations, StdRelations, TColsToLeftRight, TableWithColumns } from "../table.mjs";
2
2
  import { OrderBy } from "./orderby-clause.mjs";
3
3
  import { QueryPromise } from "./query-promise.mjs";
4
4
  import { BuildFilterExpression } from "../filters/index.mjs";
@@ -7,12 +7,25 @@ import { QueryExecutor } from "../connectors/common.mjs";
7
7
 
8
8
  //#region src/query-builders/rq.d.ts
9
9
  type RelationReturnType<O, TRelation extends AnyRelation> = TRelation extends Many<any, any, any, any> ? O[] : TRelation extends Fk<any, any, any, infer TCol> ? TCol["isNotNull"] extends true ? O : O | null : O | null;
10
- type OptionsBase<TTSchema extends string, TTName extends string, TTColumns extends Record<string, AnyColumn>, TAllRelations extends Record<string, StdRelations>> = {
10
+ /**
11
+ * Shared column-selection fields used across all option types.
12
+ */
13
+ type ColumnsOption<TTSchema extends string, TTName extends string, TTColumns extends Record<string, AnyColumn>> = {
11
14
  columns?: Partial<Record<keyof TableWithColumns<TTSchema, TTName, TTColumns>["_"]["columns"], true>> | Partial<Record<keyof TableWithColumns<TTSchema, TTName, TTColumns>["_"]["columns"], false>>;
15
+ };
16
+ /**
17
+ * Options allowed for a nested `Fk` or `One` relation.
18
+ * `where`, `orderBy`, and `limit` are excluded because the join condition
19
+ * already uniquely identifies the row — further filtering is meaningless.
20
+ */
21
+ type NestedOptionsFkOne<TTSchema extends string, TTName extends string, TTColumns extends Record<string, AnyColumn>, TAllRelations extends Record<string, StdRelations>> = ColumnsOption<TTSchema, TTName, TTColumns> & {
22
+ with?: keyof TAllRelations[`"${TTSchema}"."${TTName}"`]["map"] extends never ? never : { [TRelationName in keyof TAllRelations[`"${TTSchema}"."${TTName}"`]["map"]]?: TAllRelations[`"${TTSchema}"."${TTName}"`]["map"][TRelationName] extends Fk<any, any, any, any> | One<any, any, any, any> ? NestedOptionsFkOne<TAllRelations[`"${TTSchema}"."${TTName}"`]["map"][TRelationName]["table"]["_"]["schema"], TAllRelations[`"${TTSchema}"."${TTName}"`]["map"][TRelationName]["table"]["_"]["name"], TAllRelations[`"${TTSchema}"."${TTName}"`]["map"][TRelationName]["table"]["_"]["cols"], TAllRelations> : OptionsBase<TAllRelations[`"${TTSchema}"."${TTName}"`]["map"][TRelationName]["table"]["_"]["schema"], TAllRelations[`"${TTSchema}"."${TTName}"`]["map"][TRelationName]["table"]["_"]["name"], TAllRelations[`"${TTSchema}"."${TTName}"`]["map"][TRelationName]["table"]["_"]["cols"], TAllRelations> };
23
+ };
24
+ type OptionsBase<TTSchema extends string, TTName extends string, TTColumns extends Record<string, AnyColumn>, TAllRelations extends Record<string, StdRelations>> = ColumnsOption<TTSchema, TTName, TTColumns> & {
12
25
  where?: BuildFilterExpression<TColsToLeftRight<TableWithColumns<TTSchema, TTName, TTColumns>["_"]["columns"]>>;
13
26
  orderBy?: OrderBy<TableWithColumns<TTSchema, TTName, TTColumns>> | OrderBy<TableWithColumns<TTSchema, TTName, TTColumns>>[];
14
27
  limit?: number;
15
- with?: keyof TAllRelations[`"${TTSchema}"."${TTName}"`]["map"] extends never ? never : { [TRelationName in keyof TAllRelations[`"${TTSchema}"."${TTName}"`]["map"]]?: OptionsBase<TAllRelations[`"${TTSchema}"."${TTName}"`]["map"][TRelationName]["table"]["_"]["schema"], TAllRelations[`"${TTSchema}"."${TTName}"`]["map"][TRelationName]["table"]["_"]["name"], TAllRelations[`"${TTSchema}"."${TTName}"`]["map"][TRelationName]["table"]["_"]["cols"], TAllRelations> };
28
+ with?: keyof TAllRelations[`"${TTSchema}"."${TTName}"`]["map"] extends never ? never : { [TRelationName in keyof TAllRelations[`"${TTSchema}"."${TTName}"`]["map"]]?: TAllRelations[`"${TTSchema}"."${TTName}"`]["map"][TRelationName] extends Fk<any, any, any, any> | One<any, any, any, any> ? NestedOptionsFkOne<TAllRelations[`"${TTSchema}"."${TTName}"`]["map"][TRelationName]["table"]["_"]["schema"], TAllRelations[`"${TTSchema}"."${TTName}"`]["map"][TRelationName]["table"]["_"]["name"], TAllRelations[`"${TTSchema}"."${TTName}"`]["map"][TRelationName]["table"]["_"]["cols"], TAllRelations> : OptionsBase<TAllRelations[`"${TTSchema}"."${TTName}"`]["map"][TRelationName]["table"]["_"]["schema"], TAllRelations[`"${TTSchema}"."${TTName}"`]["map"][TRelationName]["table"]["_"]["name"], TAllRelations[`"${TTSchema}"."${TTName}"`]["map"][TRelationName]["table"]["_"]["cols"], TAllRelations> };
16
29
  };
17
30
  type Options<TTSchema extends string, TTName extends string, TTColumns extends Record<string, AnyColumn>, TAllRelations extends Record<string, StdRelations>, TOffset extends boolean = false> = OptionsBase<TTSchema, TTName, TTColumns, TAllRelations> & {
18
31
  offset?: TOffset extends true ? number : never;
@@ -41,9 +41,7 @@ var RelationQuery = class extends QueryPromise {
41
41
  const options = this.#options;
42
42
  const query = new Query("SELECT ", this.handleRows.bind(this));
43
43
  const selects = [];
44
- if (options.columns === void 0 || Object.keys(options.columns).length === 0) for (const [, column] of Object.entries(this.#table._.columns)) selects.push(column.fullName);
45
- else if (Object.values(options.columns).at(0) === true) for (const [colName, column] of Object.entries(this.#table._.columns)) colName in options.columns && selects.push(column.fullName);
46
- else for (const [colName, column] of Object.entries(this.#table._.columns)) !(colName in options.columns) && selects.push(column.fullName);
44
+ for (const [, column] of getSelectedColumns(options.columns, this.#table._.columns)) selects.push(column.fullName);
47
45
  const relations = this.#allRelations[this.#table._.fullName];
48
46
  if (relations) {
49
47
  if (options.with) {
@@ -61,7 +59,7 @@ var RelationQuery = class extends QueryPromise {
61
59
  const relations = this.#allRelations[this.#table._.fullName];
62
60
  if (relations) {
63
61
  const relation = relations.map[key];
64
- if (relation) query.sql += buildRelationSubquery(key, this.#table._.name, o, relation, this.#allRelations);
62
+ if (relation) buildRelationSubquery(query, key, this.#table._.name, o, relation, this.#allRelations);
65
63
  }
66
64
  }
67
65
  if (options.where) {
@@ -92,6 +90,15 @@ var RelationQuery = class extends QueryPromise {
92
90
  }
93
91
  };
94
92
  /**
93
+ * Returns the [colName, column] entries to include based on the columns filter option.
94
+ */
95
+ function getSelectedColumns(columns, tableColumns) {
96
+ const entries = Object.entries(tableColumns);
97
+ if (columns === void 0 || Object.keys(columns).length === 0) return entries;
98
+ if (Object.values(columns).at(0) === true) return entries.filter(([colName]) => colName in columns);
99
+ return entries.filter(([colName]) => !(colName in columns));
100
+ }
101
+ /**
95
102
  * Build the json_build_object selects for a relation, including nested relations.
96
103
  * @param alias - The alias used for the inner subquery (e.g., "posts", "posts__comments")
97
104
  * @param options - The options for this relation
@@ -100,9 +107,7 @@ var RelationQuery = class extends QueryPromise {
100
107
  */
101
108
  function getJsonBuildObjectSelects(alias, options, table, allRelations) {
102
109
  const selects = [];
103
- if (options.columns === void 0 || Object.keys(options.columns).length === 0) for (const [colName, column] of Object.entries(table._.columns)) selects.push(`'${colName}', "${alias}"."${column.nameSnake}"`);
104
- else if (Object.values(options.columns).at(0) === true) for (const [colName, column] of Object.entries(table._.columns)) colName in options.columns && selects.push(`'${colName}', "${alias}"."${column.nameSnake}"`);
105
- else for (const [colName, column] of Object.entries(table._.columns)) !(colName in options.columns) && selects.push(`'${colName}', "${alias}"."${column.nameSnake}"`);
110
+ for (const [colName, column] of getSelectedColumns(options.columns, table._.columns)) selects.push(`'${colName}', "${alias}"."${column.nameSnake}"`);
106
111
  if (options.with) {
107
112
  const tableRelations = allRelations[table._.fullName];
108
113
  if (tableRelations) {
@@ -113,101 +118,59 @@ function getJsonBuildObjectSelects(alias, options, table, allRelations) {
113
118
  }
114
119
  /**
115
120
  * Build a LATERAL JOIN subquery for a relation, recursively handling nested relations.
121
+ * Mutates query.sql directly to avoid intermediate string allocations.
116
122
  * Alias path format: "relationKey" for top-level, "parent__child" for nested (debuggable).
117
123
  *
124
+ * @param query - The query object to mutate
118
125
  * @param aliasPath - The full alias path (e.g., "posts", "posts__comments")
119
126
  * @param parentTableAlias - The alias of the parent table (e.g., "users" for top-level, "posts" for nested)
120
127
  * @param options - The options for this relation
121
128
  * @param relation - The relation definition (Many, One, or Fk)
122
129
  * @param allRelations - All relations in the schema
123
130
  */
124
- function buildRelationSubquery(aliasPath, parentTableAlias, options, relation, allRelations) {
125
- let sql = " LEFT JOIN LATERAL (";
131
+ function buildRelationSubquery(query, aliasPath, parentTableAlias, options, relation, allRelations) {
132
+ query.sql += " LEFT JOIN LATERAL (";
126
133
  const jsonSelects = getJsonBuildObjectSelects(aliasPath, options, relation.table, allRelations);
127
- if (relation.t === "Many") sql += `SELECT coalesce(json_agg(json_build_object(${jsonSelects.join(", ")})), '[]'::json) AS "data"`;
128
- else sql += `SELECT json_build_object(${jsonSelects.join(", ")}) AS "data"`;
129
- sql += ` FROM (SELECT "${aliasPath}".*`;
130
- if (options.with) {
131
- const tableRelations = allRelations[relation.table._.fullName];
132
- if (tableRelations) {
133
- for (const nestedKey in options.with) if (tableRelations.map[nestedKey]) {
134
- const nestedAliasPath = `${aliasPath}__${nestedKey}`;
135
- sql += `, "${nestedAliasPath}"."data" AS "${nestedKey}_data"`;
136
- }
134
+ if (relation.t === "Many") query.sql += `SELECT coalesce(json_agg(json_build_object(${jsonSelects.join(", ")})), '[]'::json) AS "data"`;
135
+ else query.sql += `SELECT json_build_object(${jsonSelects.join(", ")}) AS "data"`;
136
+ const nestedTableRelations = options.with ? allRelations[relation.table._.fullName] : void 0;
137
+ query.sql += ` FROM (SELECT "${aliasPath}".*`;
138
+ if (nestedTableRelations) {
139
+ for (const nestedKey in options.with) if (nestedTableRelations.map[nestedKey]) {
140
+ const nestedAliasPath = `${aliasPath}__${nestedKey}`;
141
+ query.sql += `, "${nestedAliasPath}"."data" AS "${nestedKey}_data"`;
137
142
  }
138
143
  }
139
- sql += ` FROM ${relation.table._.fullName} "${aliasPath}"`;
140
- if (options.with) {
141
- const tableRelations = allRelations[relation.table._.fullName];
142
- if (tableRelations) for (const nestedKey in options.with) {
143
- const nestedOptions = options.with[nestedKey];
144
- const nestedRelation = tableRelations.map[nestedKey];
145
- if (nestedRelation && nestedOptions) {
146
- const nestedAliasPath = `${aliasPath}__${nestedKey}`;
147
- sql += buildNestedRelationSubquery(nestedAliasPath, aliasPath, nestedOptions, nestedRelation, allRelations);
148
- }
149
- }
144
+ query.sql += ` FROM ${relation.table._.fullName} "${aliasPath}"`;
145
+ if (nestedTableRelations) for (const nestedKey in options.with) {
146
+ const nestedOptions = options.with[nestedKey];
147
+ const nestedRelation = nestedTableRelations.map[nestedKey];
148
+ if (nestedRelation && nestedOptions) buildRelationSubquery(query, `${aliasPath}__${nestedKey}`, aliasPath, nestedOptions, nestedRelation, allRelations);
150
149
  }
151
150
  if (relation.t === "Many" || relation.t === "One") {
152
151
  const referencedCol = relation.col.referencesCol;
153
152
  if (!referencedCol) throw new Error(`Relation column "${relation.col.name}" has no .references() definition. Columns used in 'many' or 'one' relations must call .references() to define the join target.`);
154
- sql += ` WHERE "${aliasPath}"."${relation.col.nameSnake}" = "${parentTableAlias}"."${referencedCol.nameSnake}"`;
155
- if (relation.t === "One") sql += ` LIMIT 1`;
153
+ query.sql += ` WHERE "${aliasPath}"."${relation.col.nameSnake}" = "${parentTableAlias}"."${referencedCol.nameSnake}"`;
154
+ if (relation.t === "Many" && options.where) {
155
+ query.sql += " AND ";
156
+ options.where.toQuery(query);
157
+ }
158
+ if (relation.t === "One") query.sql += ` LIMIT 1`;
156
159
  } else if (relation.t === "Fk") {
157
160
  const referencedCol = relation.col.referencesCol;
158
161
  if (!referencedCol) throw new Error(`Relation column "${relation.col.name}" has no .references() definition. Columns used in 'fk' relations must call .references() to define the join target.`);
159
- sql += ` WHERE "${aliasPath}"."${referencedCol.nameSnake}" = "${parentTableAlias}"."${relation.col.nameSnake}"`;
160
- sql += ` LIMIT 1`;
162
+ query.sql += ` WHERE "${aliasPath}"."${referencedCol.nameSnake}" = "${parentTableAlias}"."${relation.col.nameSnake}"`;
163
+ query.sql += ` LIMIT 1`;
161
164
  }
162
- sql += `) "${aliasPath}"`;
163
- sql += `) "${aliasPath}" ON true`;
164
- return sql;
165
- }
166
- /**
167
- * Build a nested LATERAL JOIN subquery (for relations within relations).
168
- * Similar to buildRelationSubquery but with parent alias reference for WHERE conditions.
169
- */
170
- function buildNestedRelationSubquery(aliasPath, parentAliasPath, options, relation, allRelations) {
171
- let sql = " LEFT JOIN LATERAL (";
172
- const jsonSelects = getJsonBuildObjectSelects(aliasPath, options, relation.table, allRelations);
173
- if (relation.t === "Many") sql += `SELECT coalesce(json_agg(json_build_object(${jsonSelects.join(", ")})), '[]'::json) AS "data"`;
174
- else sql += `SELECT json_build_object(${jsonSelects.join(", ")}) AS "data"`;
175
- sql += ` FROM (SELECT "${aliasPath}".*`;
176
- if (options.with) {
177
- const tableRelations = allRelations[relation.table._.fullName];
178
- if (tableRelations) {
179
- for (const nestedKey in options.with) if (tableRelations.map[nestedKey]) {
180
- const nestedAliasPath = `${aliasPath}__${nestedKey}`;
181
- sql += `, "${nestedAliasPath}"."data" AS "${nestedKey}_data"`;
182
- }
183
- }
184
- }
185
- sql += ` FROM ${relation.table._.fullName} "${aliasPath}"`;
186
- if (options.with) {
187
- const tableRelations = allRelations[relation.table._.fullName];
188
- if (tableRelations) for (const nestedKey in options.with) {
189
- const nestedOptions = options.with[nestedKey];
190
- const nestedRelation = tableRelations.map[nestedKey];
191
- if (nestedRelation && nestedOptions) {
192
- const nestedAliasPath = `${aliasPath}__${nestedKey}`;
193
- sql += buildNestedRelationSubquery(nestedAliasPath, aliasPath, nestedOptions, nestedRelation, allRelations);
194
- }
165
+ if (relation.t === "Many") {
166
+ if (options.orderBy) {
167
+ const orders = Array.isArray(options.orderBy) ? options.orderBy : [options.orderBy];
168
+ query.sql += ` ORDER BY ${orders.map((o) => o.toSQL()).join(", ")}`;
195
169
  }
170
+ if (options.limit) query.sql += ` LIMIT ${options.limit}`;
196
171
  }
197
- if (relation.t === "Many" || relation.t === "One") {
198
- const referencedCol = relation.col.referencesCol;
199
- if (!referencedCol) throw new Error(`Relation column "${relation.col.nameSnake}" has no .references() definition. Columns used in 'many' or 'one' relations must call .references() to define the join target.`);
200
- sql += ` WHERE "${aliasPath}"."${relation.col.nameSnake}" = "${parentAliasPath}"."${referencedCol.nameSnake}"`;
201
- if (relation.t === "One") sql += ` LIMIT 1`;
202
- } else if (relation.t === "Fk") {
203
- const referencedCol = relation.col.referencesCol;
204
- if (!referencedCol) throw new Error(`Relation column "${relation.col.nameSnake}" has no .references() definition. Columns used in 'fk' relations must call .references() to define the join target.`);
205
- sql += ` WHERE "${aliasPath}"."${referencedCol.nameSnake}" = "${parentAliasPath}"."${relation.col.nameSnake}"`;
206
- sql += ` LIMIT 1`;
207
- }
208
- sql += `) "${aliasPath}"`;
209
- sql += `) "${aliasPath}" ON true`;
210
- return sql;
172
+ query.sql += `) "${aliasPath}"`;
173
+ query.sql += `) "${aliasPath}" ON true`;
211
174
  }
212
175
  function convert(object, table, allRelations) {
213
176
  for (const key of Object.keys(object)) {
@@ -139,4 +139,4 @@ type StdRelations = Relations<string, string, Record<string, AnyColumn>, Record<
139
139
  type AnyRelations = Relations<any, any, Record<any, any>, Record<any, any>>;
140
140
  declare function relations<TTSchema extends string, TTName extends string, TColumns extends Record<string, AnyColumn>, TRelations extends Record<string, AnyRelation>>(table: TableWithColumns<TTSchema, TTName, TColumns>, relations: () => TRelations): () => Relations<TTSchema, TTName, TColumns, TRelations>;
141
141
  //#endregion
142
- export { AnyColumn, AnyRelation, AnyRelations, AnyTableColumn, AnyTableWithColumns, BuildScmTblColumns, Fk, IsTableWC, Many, Relations, StdRelations, StdTable, StdTableColumn, TColsToLeftRight, Table, TableColumn, TableColumnArgs, TableWCorNever, TableWithColumns, fk, many, one, relations, table };
142
+ export { AnyColumn, AnyRelation, AnyRelations, AnyTableColumn, AnyTableWithColumns, BuildScmTblColumns, Fk, IsTableWC, Many, One, Relations, StdRelations, StdTable, StdTableColumn, TColsToLeftRight, Table, TableColumn, TableColumnArgs, TableWCorNever, TableWithColumns, fk, many, one, relations, table };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "durcno",
3
- "version": "1.0.0-alpha.7",
3
+ "version": "1.0.0-alpha.8",
4
4
  "description": "A PostgreSQL Query Builder and Migration Manager for TypeScript, from the future.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://durcno.dev",