joist-core 2.3.0-next.74 → 2.3.0-next.76
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/build/Aliases.cjs +1 -0
- package/build/Aliases.cjs.map +1 -1
- package/build/Aliases.d.cts +2 -1
- package/build/Aliases.d.cts.map +1 -1
- package/build/Aliases.d.mts +2 -1
- package/build/Aliases.d.mts.map +1 -1
- package/build/Aliases.js +1 -0
- package/build/Aliases.js.map +1 -1
- package/build/ConditionBuilder.cjs +1 -1
- package/build/ConditionBuilder.cjs.map +1 -1
- package/build/ConditionBuilder.d.cts +2 -2
- package/build/ConditionBuilder.d.cts.map +1 -1
- package/build/ConditionBuilder.d.mts +2 -2
- package/build/ConditionBuilder.d.mts.map +1 -1
- package/build/ConditionBuilder.js +1 -1
- package/build/ConditionBuilder.js.map +1 -1
- package/build/DeferredAlias.cjs +2 -1
- package/build/DeferredAlias.cjs.map +1 -1
- package/build/DeferredAlias.d.cts +2 -1
- package/build/DeferredAlias.d.cts.map +1 -1
- package/build/DeferredAlias.d.mts +2 -1
- package/build/DeferredAlias.d.mts.map +1 -1
- package/build/DeferredAlias.js +2 -1
- package/build/DeferredAlias.js.map +1 -1
- package/build/EntityFilter.cjs +1 -1
- package/build/EntityFilter.cjs.map +1 -1
- package/build/EntityFilter.d.cts +4 -12
- package/build/EntityFilter.d.cts.map +1 -1
- package/build/EntityFilter.d.mts +4 -12
- package/build/EntityFilter.d.mts.map +1 -1
- package/build/EntityFilter.js +1 -1
- package/build/EntityFilter.js.map +1 -1
- package/build/Expr.cjs +3 -2
- package/build/Expr.cjs.map +1 -1
- package/build/Expr.d.cts +28 -28
- package/build/Expr.d.cts.map +1 -1
- package/build/Expr.d.mts +28 -28
- package/build/Expr.d.mts.map +1 -1
- package/build/Expr.js +3 -2
- package/build/Expr.js.map +1 -1
- package/build/QueryParser.cjs +15 -1
- package/build/QueryParser.cjs.map +1 -1
- package/build/QueryParser.d.cts.map +1 -1
- package/build/QueryParser.d.mts.map +1 -1
- package/build/QueryParser.js +15 -1
- package/build/QueryParser.js.map +1 -1
- package/build/Tables.cjs +1 -0
- package/build/Tables.cjs.map +1 -1
- package/build/Tables.d.cts +34 -35
- package/build/Tables.d.cts.map +1 -1
- package/build/Tables.d.mts +34 -35
- package/build/Tables.d.mts.map +1 -1
- package/build/Tables.js +1 -0
- package/build/Tables.js.map +1 -1
- package/build/conditions.cjs +16 -0
- package/build/conditions.cjs.map +1 -0
- package/build/conditions.d.cts +38 -0
- package/build/conditions.d.cts.map +1 -0
- package/build/conditions.d.mts +38 -0
- package/build/conditions.d.mts.map +1 -0
- package/build/conditions.js +14 -0
- package/build/conditions.js.map +1 -0
- package/build/execute.cjs +1 -1
- package/build/execute.cjs.map +1 -1
- package/build/execute.d.cts +2 -2
- package/build/execute.d.cts.map +1 -1
- package/build/execute.d.mts +2 -2
- package/build/execute.d.mts.map +1 -1
- package/build/execute.js +1 -1
- package/build/execute.js.map +1 -1
- package/build/index.cjs.map +1 -1
- package/build/index.d.cts +2 -1
- package/build/index.d.cts.map +1 -1
- package/build/index.d.mts +2 -1
- package/build/index.d.mts.map +1 -1
- package/build/index.js.map +1 -1
- package/build/query.cjs +15 -8
- package/build/query.cjs.map +1 -1
- package/build/query.d.cts +199 -199
- package/build/query.d.cts.map +1 -1
- package/build/query.d.mts +199 -199
- package/build/query.d.mts.map +1 -1
- package/build/query.js +15 -8
- package/build/query.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityFilter.cjs","names":["isScope","resolveScope"],"sources":["../src/EntityFilter.ts"],"sourcesContent":["import { type Alias } from \"./Aliases.ts\";\nimport { type
|
|
1
|
+
{"version":3,"file":"EntityFilter.cjs","names":["isScope","resolveScope"],"sources":["../src/EntityFilter.ts"],"sourcesContent":["import { type Alias } from \"./Aliases.ts\";\nimport { type AndCondition, type DomainPredicate, type OrCondition, type UnbrandedPredicate } from \"./conditions.ts\";\nimport { type Entity } from \"./Entity.ts\";\nimport { type FindFilterOptions, type IdOf } from \"./EntityManager.ts\";\nimport { type Scope, isScope, resolveScope } from \"./scopes.ts\";\nimport { type FieldsOf, type FilterOf, type OrderOf } from \"./typeMap.ts\";\n\n/** Combines a `where` filter with optional `orderBy`, `limit`, and `offset` settings. */\nexport type FilterAndSettings<T extends Entity> = {\n // Either a `{ ... }` EntityFilter or a Scope like `Adult.parent`\n where: FindFilter<T>;\n conditions?: ExpressionFilter;\n orderBy?: OrderOf<T> | OrderOf<T>[];\n limit?: number | undefined;\n offset?: number | undefined;\n softDeletes?: \"exclude\" | \"include\";\n allowMultipleLeftJoins?: boolean;\n optimizeJoinsToExists?: boolean;\n};\n\nexport type OrderBy = \"ASC\" | \"DESC\";\n\n/**\n * A filter for an entity of type `T` for `em.find` inline conditions.\n *\n * An entity filter can either filter by \"an exact entity\" (i.e. the book with id 2)\n * or its own set of filters (i.e. all books with a title that starts with \"The\").\n *\n * @typeparam T The entity type, i.e. `Author`\n * @typeparam I The ID type of the entity, i.e. `AuthorId`\n * @typeparam F The filter type for the entity, i.e. `AuthorFilter`\n * @typeparam N Either `null | undefined` if the entity can be null, or `never` if it cannot.\n */\nexport type EntityFilter<T extends Entity, I = IdOf<T>, F = FilterOf<T>, N = never> =\n | T\n | readonly T[]\n | I\n | readonly I[]\n | Scope<T>\n // Note that this is a weak type (all optional keys) but TS still enforces at least one overlap\n | EntityFilterObject<T, I, F, N>\n // Always allow `undefined` for condition pruning\n | undefined\n // But only allow `null` for `nullable` relations\n | N\n | { ne: T | I | N | undefined }\n // Allow setting `true` for `is not null` or `false` for `is null`\n | boolean\n | Alias<T>;\n\n/** A nested object filter, including logical composition at the current entity alias. */\nexport type EntityFilterObject<T extends Entity, I = IdOf<T>, F = FilterOf<T>, N = never> = {\n as?: Alias<T>;\n and?: EntityFilter<T, I, F, N> | readonly EntityFilter<T, I, F, N>[];\n or?: EntityFilter<T, I, F, N> | readonly EntityFilter<T, I, F, N>[];\n} & F;\n\nexport type BooleanFilter<N> = true | false | N;\n\n/**\n * The root `em.find` `where` shape: an `EntityFilterObject` with `I`/`F`/`N` defaulted.\n *\n * Intentionally a thin alias rather than the source of truth: `as`/`and`/`or` are defined once on\n * `EntityFilterObject<T, I, F, N>` so the parameterized nested-relation arm of `EntityFilter` and this\n * root can't drift. That arm needs the `F`/`N` params — GraphQL filter shapes (`F`) and relation\n * nullability (`N`) flow through `and`/`or` recursion — which a `FilterOf<T>`-hardcoded root can't\n * supply, so the shape lives on `EntityFilterObject` and this just specializes it.\n */\nexport type FilterWithAlias<T extends Entity> = EntityFilterObject<T>;\n\n/** Allows `em.find` to accept either a `{ ... }` EntityFilter or a Scope like `Adult.parent` */\nexport type FindFilter<T extends Entity> = FilterWithAlias<T> | Scope<T>;\n\nexport type UniqueFilter<T extends Entity> = {\n [K in keyof FieldsOf<T> & keyof FilterOf<T> as FieldsOf<T>[K] extends { unique: true } ? K : never]?: FilterOf<T>[K];\n};\n\n/**\n * Filters against a specific field's values within `em.find` inline conditions.\n *\n * We always allow `undefined` to support condition pruning, but only conditionally\n * allow `null` if the column is actually nullable.\n */\nexport type ValueFilter<V, N> =\n | V\n | readonly V[]\n | N\n | undefined\n // Both eq and in are redundant with `V` and `V[]` above but are convenient for matching GQL filter APIs\n | { eq: V | N | undefined }\n | { ne: V | N | undefined }\n | { in: readonly (V | N | null)[] | undefined }\n | { nin: readonly (V | N)[] | undefined }\n | { gt: V | undefined }\n | { gte: V | undefined }\n | { lt: V | undefined }\n | { lte: V | undefined }\n | { like: V | undefined }\n | { nlike: V | undefined }\n | { ilike: V | undefined }\n | { nilike: V | undefined }\n | { regex?: V | undefined }\n | { iregex?: V | undefined }\n | { nregex?: V | undefined }\n | { niregex?: V | undefined }\n | { search: V | undefined }\n // should put these in a dedicated ArrayFilter\n | { contains: V | undefined }\n | { overlaps: V | undefined }\n | { containedBy: V | undefined }\n // should put these in a dedicated JsonbFilter\n | { pathExists: string | undefined }\n | { pathIsTrue: string | undefined }\n // this is between\n | { gte: V | undefined; lte: V | undefined }\n | { between: [V, V] | undefined };\n\n/**\n * Filters against complex expressions of filters.\n *\n * This is the user-facing DSL that internally will be converted to `ParsedExpressionFilter.\n */\nexport type ExpressionFilter = AndCondition<ExpressionCondition> | OrCondition<ExpressionCondition>;\n\n/** A domain-alias predicate or nested group for em.find and scopes, not SQL table expressions. */\nexport type ExpressionCondition = ExpressionFilter | DomainPredicate | UnbrandedPredicate;\n\n/** Merges root-scope find settings with caller options, letting caller options win. */\nexport function mergeFindOptions<T extends Entity>(\n where: FindFilter<T>,\n options: FindFilterOptions<T>,\n): { where: FindFilter<T>; options: FindFilterOptions<T> } {\n if (!isScope<T>(where)) return { where, options };\n\n const resolved = resolveScope(where);\n const scopeOptions: FindFilterOptions<T> = {};\n if (resolved.orderBys.length > 0) scopeOptions.orderBy = resolved.orderBys;\n if (resolved.limit !== undefined) scopeOptions.limit = resolved.limit;\n if (resolved.offset !== undefined) scopeOptions.offset = resolved.offset;\n if (resolved.softDeletes !== undefined) scopeOptions.softDeletes = resolved.softDeletes;\n return { where, options: { ...scopeOptions, ...options } };\n}\n"],"mappings":";;;;;;;;;AAgIA,SAAgB,iBACd,OACA,SACyD;CACzD,IAAI,CAACA,eAAAA,QAAW,KAAK,GAAG,OAAO;EAAE;EAAO;CAAQ;CAEhD,MAAM,WAAWC,eAAAA,aAAa,KAAK;CACnC,MAAM,eAAqC,CAAC;CAC5C,IAAI,SAAS,SAAS,SAAS,GAAG,aAAa,UAAU,SAAS;CAClE,IAAI,SAAS,UAAU,KAAA,GAAW,aAAa,QAAQ,SAAS;CAChE,IAAI,SAAS,WAAW,KAAA,GAAW,aAAa,SAAS,SAAS;CAClE,IAAI,SAAS,gBAAgB,KAAA,GAAW,aAAa,cAAc,SAAS;CAC5E,OAAO;EAAE;EAAO,SAAS;GAAE,GAAG;GAAc,GAAG;EAAQ;CAAE;AAC3D"}
|
package/build/EntityFilter.d.cts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { AndCondition, DomainPredicate, OrCondition, UnbrandedPredicate } from "./conditions.cjs";
|
|
1
2
|
import { FieldsOf, FilterOf, OrderOf } from "./typeMap.cjs";
|
|
2
3
|
import { Scope } from "./scopes.cjs";
|
|
3
4
|
import { FindFilterOptions, IdOf } from "./EntityManager.cjs";
|
|
4
5
|
import { Entity } from "./Entity.cjs";
|
|
5
6
|
import { Alias } from "./Aliases.cjs";
|
|
6
|
-
import { ColumnCondition, RawCondition } from "./QueryParser.cjs";
|
|
7
7
|
//#region src/EntityFilter.d.ts
|
|
8
8
|
/** Combines a `where` filter with optional `orderBy`, `limit`, and `offset` settings. */
|
|
9
9
|
type FilterAndSettings<T extends Entity> = {
|
|
@@ -114,17 +114,9 @@ type ValueFilter<V, N> = V | readonly V[] | N | undefined | {
|
|
|
114
114
|
*
|
|
115
115
|
* This is the user-facing DSL that internally will be converted to `ParsedExpressionFilter.
|
|
116
116
|
*/
|
|
117
|
-
type ExpressionFilter =
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
} | {
|
|
121
|
-
or: Array<ExpressionCondition | undefined>;
|
|
122
|
-
and?: never;
|
|
123
|
-
}) & {
|
|
124
|
-
pruneIfUndefined?: "any" | "all";
|
|
125
|
-
};
|
|
126
|
-
/** A user-facing filter for maybe-nested/maybe-simple conditions. */
|
|
127
|
-
type ExpressionCondition = ExpressionFilter | ColumnCondition | RawCondition;
|
|
117
|
+
type ExpressionFilter = AndCondition<ExpressionCondition> | OrCondition<ExpressionCondition>;
|
|
118
|
+
/** A domain-alias predicate or nested group for em.find and scopes, not SQL table expressions. */
|
|
119
|
+
type ExpressionCondition = ExpressionFilter | DomainPredicate | UnbrandedPredicate;
|
|
128
120
|
/** Merges root-scope find settings with caller options, letting caller options win. */
|
|
129
121
|
declare function mergeFindOptions<T extends Entity>(where: FindFilter<T>, options: FindFilterOptions<T>): {
|
|
130
122
|
where: FindFilter<T>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityFilter.d.cts","names":[],"sources":["../src/EntityFilter.ts"],"mappings":";;;;;;;;KAQY,kBAAkB,UAAU;EAEtC,OAAO,WAAW;EAClB,aAAa;EACb,UAAU,QAAQ,KAAK,QAAQ;EAC/B;EACA;EACA;EACA;EACA;;KAGU;;;;;;;;;;;;KAaA,aAAa,UAAU,QAAQ,IAAI,KAAK,IAAI,IAAI,SAAS,IAAI,aACrE,aACS,MACT,aACS,MACT,MAAM,KAEN,mBAAmB,GAAG,GAAG,GAAG,iBAI5B;EACE,IAAI,IAAI,IAAI;cAGd,MAAM;;KAGE,mBAAmB,UAAU,QAAQ,IAAI,KAAK,IAAI,IAAI,SAAS,IAAI;EAC7E,KAAK,MAAM;EACX,MAAM,aAAa,GAAG,GAAG,GAAG,cAAc,aAAa,GAAG,GAAG,GAAG;EAChE,KAAK,aAAa,GAAG,GAAG,GAAG,cAAc,aAAa,GAAG,GAAG,GAAG;IAC7D;KAEQ,cAAc,oBAAoB;;;;;;;;;;KAWlC,gBAAgB,UAAU,UAAU,mBAAmB;;KAGvD,WAAW,UAAU,UAAU,gBAAgB,KAAK,MAAM;KAE1D,aAAa,UAAU,aAChC,WAAW,SAAS,WAAW,SAAS,MAAM,SAAS,GAAG;EAAa;IAAiB,aAAa,SAAS,GAAG;;;;;;;KASxG,YAAY,GAAG,KACvB,aACS,MACT;EAGE,IAAI,IAAI;;EACR,IAAI,IAAI;;EACR,cAAc,IAAI;;EAClB,eAAe,IAAI;;EACnB,IAAI;;EACJ,KAAK;;EACL,IAAI;;EACJ,KAAK;;EACL,MAAM;;EACN,OAAO;;EACP,OAAO;;EACP,QAAQ;;EACR,QAAQ;;EACR,SAAS;;EACT,SAAS;;EACT,UAAU;;EACV,QAAQ;;EAER,UAAU;;EACV,UAAU;;EACV,aAAa;;EAEb;;EACA;;EAEA,KAAK;EAAe,KAAK;;EACzB,UAAU,GAAG;;;;;;;KAOP
|
|
1
|
+
{"version":3,"file":"EntityFilter.d.cts","names":[],"sources":["../src/EntityFilter.ts"],"mappings":";;;;;;;;KAQY,kBAAkB,UAAU;EAEtC,OAAO,WAAW;EAClB,aAAa;EACb,UAAU,QAAQ,KAAK,QAAQ;EAC/B;EACA;EACA;EACA;EACA;;KAGU;;;;;;;;;;;;KAaA,aAAa,UAAU,QAAQ,IAAI,KAAK,IAAI,IAAI,SAAS,IAAI,aACrE,aACS,MACT,aACS,MACT,MAAM,KAEN,mBAAmB,GAAG,GAAG,GAAG,iBAI5B;EACE,IAAI,IAAI,IAAI;cAGd,MAAM;;KAGE,mBAAmB,UAAU,QAAQ,IAAI,KAAK,IAAI,IAAI,SAAS,IAAI;EAC7E,KAAK,MAAM;EACX,MAAM,aAAa,GAAG,GAAG,GAAG,cAAc,aAAa,GAAG,GAAG,GAAG;EAChE,KAAK,aAAa,GAAG,GAAG,GAAG,cAAc,aAAa,GAAG,GAAG,GAAG;IAC7D;KAEQ,cAAc,oBAAoB;;;;;;;;;;KAWlC,gBAAgB,UAAU,UAAU,mBAAmB;;KAGvD,WAAW,UAAU,UAAU,gBAAgB,KAAK,MAAM;KAE1D,aAAa,UAAU,aAChC,WAAW,SAAS,WAAW,SAAS,MAAM,SAAS,GAAG;EAAa;IAAiB,aAAa,SAAS,GAAG;;;;;;;KASxG,YAAY,GAAG,KACvB,aACS,MACT;EAGE,IAAI,IAAI;;EACR,IAAI,IAAI;;EACR,cAAc,IAAI;;EAClB,eAAe,IAAI;;EACnB,IAAI;;EACJ,KAAK;;EACL,IAAI;;EACJ,KAAK;;EACL,MAAM;;EACN,OAAO;;EACP,OAAO;;EACP,QAAQ;;EACR,QAAQ;;EACR,SAAS;;EACT,SAAS;;EACT,UAAU;;EACV,QAAQ;;EAER,UAAU;;EACV,UAAU;;EACV,aAAa;;EAEb;;EACA;;EAEA,KAAK;EAAe,KAAK;;EACzB,UAAU,GAAG;;;;;;;KAOP,mBAAmB,aAAa,uBAAuB,YAAY;;KAGnE,sBAAsB,mBAAmB,kBAAkB;;iBAGvD,iBAAiB,UAAU,QACzC,OAAO,WAAW,IAClB,SAAS,kBAAkB;EACxB,OAAO,WAAW;EAAI,SAAS,kBAAkB"}
|
package/build/EntityFilter.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { AndCondition, DomainPredicate, OrCondition, UnbrandedPredicate } from "./conditions.mjs";
|
|
1
2
|
import { FieldsOf, FilterOf, OrderOf } from "./typeMap.mjs";
|
|
2
3
|
import { Scope } from "./scopes.mjs";
|
|
3
4
|
import { FindFilterOptions, IdOf } from "./EntityManager.mjs";
|
|
4
5
|
import { Entity } from "./Entity.mjs";
|
|
5
6
|
import { Alias } from "./Aliases.mjs";
|
|
6
|
-
import { ColumnCondition, RawCondition } from "./QueryParser.mjs";
|
|
7
7
|
//#region src/EntityFilter.d.ts
|
|
8
8
|
/** Combines a `where` filter with optional `orderBy`, `limit`, and `offset` settings. */
|
|
9
9
|
type FilterAndSettings<T extends Entity> = {
|
|
@@ -114,17 +114,9 @@ type ValueFilter<V, N> = V | readonly V[] | N | undefined | {
|
|
|
114
114
|
*
|
|
115
115
|
* This is the user-facing DSL that internally will be converted to `ParsedExpressionFilter.
|
|
116
116
|
*/
|
|
117
|
-
type ExpressionFilter =
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
} | {
|
|
121
|
-
or: Array<ExpressionCondition | undefined>;
|
|
122
|
-
and?: never;
|
|
123
|
-
}) & {
|
|
124
|
-
pruneIfUndefined?: "any" | "all";
|
|
125
|
-
};
|
|
126
|
-
/** A user-facing filter for maybe-nested/maybe-simple conditions. */
|
|
127
|
-
type ExpressionCondition = ExpressionFilter | ColumnCondition | RawCondition;
|
|
117
|
+
type ExpressionFilter = AndCondition<ExpressionCondition> | OrCondition<ExpressionCondition>;
|
|
118
|
+
/** A domain-alias predicate or nested group for em.find and scopes, not SQL table expressions. */
|
|
119
|
+
type ExpressionCondition = ExpressionFilter | DomainPredicate | UnbrandedPredicate;
|
|
128
120
|
/** Merges root-scope find settings with caller options, letting caller options win. */
|
|
129
121
|
declare function mergeFindOptions<T extends Entity>(where: FindFilter<T>, options: FindFilterOptions<T>): {
|
|
130
122
|
where: FindFilter<T>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityFilter.d.mts","names":[],"sources":["../src/EntityFilter.ts"],"mappings":";;;;;;;;KAQY,kBAAkB,UAAU;EAEtC,OAAO,WAAW;EAClB,aAAa;EACb,UAAU,QAAQ,KAAK,QAAQ;EAC/B;EACA;EACA;EACA;EACA;;KAGU;;;;;;;;;;;;KAaA,aAAa,UAAU,QAAQ,IAAI,KAAK,IAAI,IAAI,SAAS,IAAI,aACrE,aACS,MACT,aACS,MACT,MAAM,KAEN,mBAAmB,GAAG,GAAG,GAAG,iBAI5B;EACE,IAAI,IAAI,IAAI;cAGd,MAAM;;KAGE,mBAAmB,UAAU,QAAQ,IAAI,KAAK,IAAI,IAAI,SAAS,IAAI;EAC7E,KAAK,MAAM;EACX,MAAM,aAAa,GAAG,GAAG,GAAG,cAAc,aAAa,GAAG,GAAG,GAAG;EAChE,KAAK,aAAa,GAAG,GAAG,GAAG,cAAc,aAAa,GAAG,GAAG,GAAG;IAC7D;KAEQ,cAAc,oBAAoB;;;;;;;;;;KAWlC,gBAAgB,UAAU,UAAU,mBAAmB;;KAGvD,WAAW,UAAU,UAAU,gBAAgB,KAAK,MAAM;KAE1D,aAAa,UAAU,aAChC,WAAW,SAAS,WAAW,SAAS,MAAM,SAAS,GAAG;EAAa;IAAiB,aAAa,SAAS,GAAG;;;;;;;KASxG,YAAY,GAAG,KACvB,aACS,MACT;EAGE,IAAI,IAAI;;EACR,IAAI,IAAI;;EACR,cAAc,IAAI;;EAClB,eAAe,IAAI;;EACnB,IAAI;;EACJ,KAAK;;EACL,IAAI;;EACJ,KAAK;;EACL,MAAM;;EACN,OAAO;;EACP,OAAO;;EACP,QAAQ;;EACR,QAAQ;;EACR,SAAS;;EACT,SAAS;;EACT,UAAU;;EACV,QAAQ;;EAER,UAAU;;EACV,UAAU;;EACV,aAAa;;EAEb;;EACA;;EAEA,KAAK;EAAe,KAAK;;EACzB,UAAU,GAAG;;;;;;;KAOP
|
|
1
|
+
{"version":3,"file":"EntityFilter.d.mts","names":[],"sources":["../src/EntityFilter.ts"],"mappings":";;;;;;;;KAQY,kBAAkB,UAAU;EAEtC,OAAO,WAAW;EAClB,aAAa;EACb,UAAU,QAAQ,KAAK,QAAQ;EAC/B;EACA;EACA;EACA;EACA;;KAGU;;;;;;;;;;;;KAaA,aAAa,UAAU,QAAQ,IAAI,KAAK,IAAI,IAAI,SAAS,IAAI,aACrE,aACS,MACT,aACS,MACT,MAAM,KAEN,mBAAmB,GAAG,GAAG,GAAG,iBAI5B;EACE,IAAI,IAAI,IAAI;cAGd,MAAM;;KAGE,mBAAmB,UAAU,QAAQ,IAAI,KAAK,IAAI,IAAI,SAAS,IAAI;EAC7E,KAAK,MAAM;EACX,MAAM,aAAa,GAAG,GAAG,GAAG,cAAc,aAAa,GAAG,GAAG,GAAG;EAChE,KAAK,aAAa,GAAG,GAAG,GAAG,cAAc,aAAa,GAAG,GAAG,GAAG;IAC7D;KAEQ,cAAc,oBAAoB;;;;;;;;;;KAWlC,gBAAgB,UAAU,UAAU,mBAAmB;;KAGvD,WAAW,UAAU,UAAU,gBAAgB,KAAK,MAAM;KAE1D,aAAa,UAAU,aAChC,WAAW,SAAS,WAAW,SAAS,MAAM,SAAS,GAAG;EAAa;IAAiB,aAAa,SAAS,GAAG;;;;;;;KASxG,YAAY,GAAG,KACvB,aACS,MACT;EAGE,IAAI,IAAI;;EACR,IAAI,IAAI;;EACR,cAAc,IAAI;;EAClB,eAAe,IAAI;;EACnB,IAAI;;EACJ,KAAK;;EACL,IAAI;;EACJ,KAAK;;EACL,MAAM;;EACN,OAAO;;EACP,OAAO;;EACP,QAAQ;;EACR,QAAQ;;EACR,SAAS;;EACT,SAAS;;EACT,UAAU;;EACV,QAAQ;;EAER,UAAU;;EACV,UAAU;;EACV,aAAa;;EAEb;;EACA;;EAEA,KAAK;EAAe,KAAK;;EACzB,UAAU,GAAG;;;;;;;KAOP,mBAAmB,aAAa,uBAAuB,YAAY;;KAGnE,sBAAsB,mBAAmB,kBAAkB;;iBAGvD,iBAAiB,UAAU,QACzC,OAAO,WAAW,IAClB,SAAS,kBAAkB;EACxB,OAAO,WAAW;EAAI,SAAS,kBAAkB"}
|
package/build/EntityFilter.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import "./conditions.js";
|
|
1
2
|
import { isScope, resolveScope } from "./scopes.js";
|
|
2
3
|
import "./typeMap.js";
|
|
3
4
|
import "./EntityManager.js";
|
|
4
5
|
import "./Entity.js";
|
|
5
6
|
import "./Aliases.js";
|
|
6
|
-
import "./QueryParser.js";
|
|
7
7
|
//#region src/EntityFilter.ts
|
|
8
8
|
/** Merges root-scope find settings with caller options, letting caller options win. */
|
|
9
9
|
function mergeFindOptions(where, options) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntityFilter.js","names":[],"sources":["../src/EntityFilter.ts"],"sourcesContent":["import { type Alias } from \"./Aliases.ts\";\nimport { type
|
|
1
|
+
{"version":3,"file":"EntityFilter.js","names":[],"sources":["../src/EntityFilter.ts"],"sourcesContent":["import { type Alias } from \"./Aliases.ts\";\nimport { type AndCondition, type DomainPredicate, type OrCondition, type UnbrandedPredicate } from \"./conditions.ts\";\nimport { type Entity } from \"./Entity.ts\";\nimport { type FindFilterOptions, type IdOf } from \"./EntityManager.ts\";\nimport { type Scope, isScope, resolveScope } from \"./scopes.ts\";\nimport { type FieldsOf, type FilterOf, type OrderOf } from \"./typeMap.ts\";\n\n/** Combines a `where` filter with optional `orderBy`, `limit`, and `offset` settings. */\nexport type FilterAndSettings<T extends Entity> = {\n // Either a `{ ... }` EntityFilter or a Scope like `Adult.parent`\n where: FindFilter<T>;\n conditions?: ExpressionFilter;\n orderBy?: OrderOf<T> | OrderOf<T>[];\n limit?: number | undefined;\n offset?: number | undefined;\n softDeletes?: \"exclude\" | \"include\";\n allowMultipleLeftJoins?: boolean;\n optimizeJoinsToExists?: boolean;\n};\n\nexport type OrderBy = \"ASC\" | \"DESC\";\n\n/**\n * A filter for an entity of type `T` for `em.find` inline conditions.\n *\n * An entity filter can either filter by \"an exact entity\" (i.e. the book with id 2)\n * or its own set of filters (i.e. all books with a title that starts with \"The\").\n *\n * @typeparam T The entity type, i.e. `Author`\n * @typeparam I The ID type of the entity, i.e. `AuthorId`\n * @typeparam F The filter type for the entity, i.e. `AuthorFilter`\n * @typeparam N Either `null | undefined` if the entity can be null, or `never` if it cannot.\n */\nexport type EntityFilter<T extends Entity, I = IdOf<T>, F = FilterOf<T>, N = never> =\n | T\n | readonly T[]\n | I\n | readonly I[]\n | Scope<T>\n // Note that this is a weak type (all optional keys) but TS still enforces at least one overlap\n | EntityFilterObject<T, I, F, N>\n // Always allow `undefined` for condition pruning\n | undefined\n // But only allow `null` for `nullable` relations\n | N\n | { ne: T | I | N | undefined }\n // Allow setting `true` for `is not null` or `false` for `is null`\n | boolean\n | Alias<T>;\n\n/** A nested object filter, including logical composition at the current entity alias. */\nexport type EntityFilterObject<T extends Entity, I = IdOf<T>, F = FilterOf<T>, N = never> = {\n as?: Alias<T>;\n and?: EntityFilter<T, I, F, N> | readonly EntityFilter<T, I, F, N>[];\n or?: EntityFilter<T, I, F, N> | readonly EntityFilter<T, I, F, N>[];\n} & F;\n\nexport type BooleanFilter<N> = true | false | N;\n\n/**\n * The root `em.find` `where` shape: an `EntityFilterObject` with `I`/`F`/`N` defaulted.\n *\n * Intentionally a thin alias rather than the source of truth: `as`/`and`/`or` are defined once on\n * `EntityFilterObject<T, I, F, N>` so the parameterized nested-relation arm of `EntityFilter` and this\n * root can't drift. That arm needs the `F`/`N` params — GraphQL filter shapes (`F`) and relation\n * nullability (`N`) flow through `and`/`or` recursion — which a `FilterOf<T>`-hardcoded root can't\n * supply, so the shape lives on `EntityFilterObject` and this just specializes it.\n */\nexport type FilterWithAlias<T extends Entity> = EntityFilterObject<T>;\n\n/** Allows `em.find` to accept either a `{ ... }` EntityFilter or a Scope like `Adult.parent` */\nexport type FindFilter<T extends Entity> = FilterWithAlias<T> | Scope<T>;\n\nexport type UniqueFilter<T extends Entity> = {\n [K in keyof FieldsOf<T> & keyof FilterOf<T> as FieldsOf<T>[K] extends { unique: true } ? K : never]?: FilterOf<T>[K];\n};\n\n/**\n * Filters against a specific field's values within `em.find` inline conditions.\n *\n * We always allow `undefined` to support condition pruning, but only conditionally\n * allow `null` if the column is actually nullable.\n */\nexport type ValueFilter<V, N> =\n | V\n | readonly V[]\n | N\n | undefined\n // Both eq and in are redundant with `V` and `V[]` above but are convenient for matching GQL filter APIs\n | { eq: V | N | undefined }\n | { ne: V | N | undefined }\n | { in: readonly (V | N | null)[] | undefined }\n | { nin: readonly (V | N)[] | undefined }\n | { gt: V | undefined }\n | { gte: V | undefined }\n | { lt: V | undefined }\n | { lte: V | undefined }\n | { like: V | undefined }\n | { nlike: V | undefined }\n | { ilike: V | undefined }\n | { nilike: V | undefined }\n | { regex?: V | undefined }\n | { iregex?: V | undefined }\n | { nregex?: V | undefined }\n | { niregex?: V | undefined }\n | { search: V | undefined }\n // should put these in a dedicated ArrayFilter\n | { contains: V | undefined }\n | { overlaps: V | undefined }\n | { containedBy: V | undefined }\n // should put these in a dedicated JsonbFilter\n | { pathExists: string | undefined }\n | { pathIsTrue: string | undefined }\n // this is between\n | { gte: V | undefined; lte: V | undefined }\n | { between: [V, V] | undefined };\n\n/**\n * Filters against complex expressions of filters.\n *\n * This is the user-facing DSL that internally will be converted to `ParsedExpressionFilter.\n */\nexport type ExpressionFilter = AndCondition<ExpressionCondition> | OrCondition<ExpressionCondition>;\n\n/** A domain-alias predicate or nested group for em.find and scopes, not SQL table expressions. */\nexport type ExpressionCondition = ExpressionFilter | DomainPredicate | UnbrandedPredicate;\n\n/** Merges root-scope find settings with caller options, letting caller options win. */\nexport function mergeFindOptions<T extends Entity>(\n where: FindFilter<T>,\n options: FindFilterOptions<T>,\n): { where: FindFilter<T>; options: FindFilterOptions<T> } {\n if (!isScope<T>(where)) return { where, options };\n\n const resolved = resolveScope(where);\n const scopeOptions: FindFilterOptions<T> = {};\n if (resolved.orderBys.length > 0) scopeOptions.orderBy = resolved.orderBys;\n if (resolved.limit !== undefined) scopeOptions.limit = resolved.limit;\n if (resolved.offset !== undefined) scopeOptions.offset = resolved.offset;\n if (resolved.softDeletes !== undefined) scopeOptions.softDeletes = resolved.softDeletes;\n return { where, options: { ...scopeOptions, ...options } };\n}\n"],"mappings":";;;;;;;;AAgIA,SAAgB,iBACd,OACA,SACyD;CACzD,IAAI,CAAC,QAAW,KAAK,GAAG,OAAO;EAAE;EAAO;CAAQ;CAEhD,MAAM,WAAW,aAAa,KAAK;CACnC,MAAM,eAAqC,CAAC;CAC5C,IAAI,SAAS,SAAS,SAAS,GAAG,aAAa,UAAU,SAAS;CAClE,IAAI,SAAS,UAAU,KAAA,GAAW,aAAa,QAAQ,SAAS;CAChE,IAAI,SAAS,WAAW,KAAA,GAAW,aAAa,SAAS,SAAS;CAClE,IAAI,SAAS,gBAAgB,KAAA,GAAW,aAAa,cAAc,SAAS;CAC5E,OAAO;EAAE;EAAO,SAAS;GAAE,GAAG;GAAc,GAAG;EAAQ;CAAE;AAC3D"}
|
package/build/Expr.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_conditions = require("./conditions.cjs");
|
|
2
3
|
const require_keywords = require("./keywords.cjs");
|
|
3
4
|
const require_skipCondition = require("./skipCondition.cjs");
|
|
4
5
|
const require_TypeInfo = require("./TypeInfo.cjs");
|
|
@@ -28,7 +29,7 @@ function isDeferredCondition(cond) {
|
|
|
28
29
|
}
|
|
29
30
|
/** Creates a `DeferredCondition` that generates its SQL with `fn` once the query's aliases are known. */
|
|
30
31
|
function deferredCondition(fn) {
|
|
31
|
-
const cond = {
|
|
32
|
+
const cond = require_conditions.brandPredicate({
|
|
32
33
|
kind: "raw",
|
|
33
34
|
aliases: [],
|
|
34
35
|
condition: "<unresolved>",
|
|
@@ -43,7 +44,7 @@ function deferredCondition(fn) {
|
|
|
43
44
|
aliases: refs
|
|
44
45
|
};
|
|
45
46
|
}
|
|
46
|
-
};
|
|
47
|
+
}, "sql");
|
|
47
48
|
return cond;
|
|
48
49
|
}
|
|
49
50
|
/**
|
package/build/Expr.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Expr.cjs","names":["arrayOutputType","skipCondition","safeKq"],"sources":["../src/Expr.ts"],"sourcesContent":["import type { ExpressionCondition } from \"./EntityFilter.ts\";\nimport type { EntityMetadata } from \"./EntityMetadata.ts\";\nimport { safeKq } from \"./keywords.ts\";\nimport type { RawCondition } from \"./QueryParser.ts\";\nimport { skipCondition } from \"./skipCondition.ts\";\nimport { type TypeInfo, arrayOutputType } from \"./TypeInfo.ts\";\n\n/**\n * The shared expression protocol for `em.query`.\n *\n * Table columns (`a.first_name`), aggregates (`b.id.count()`), `sql` templates, subquery columns\n * (`bookStats.bookCount`), and scalar subqueries all implement it, so any of them can appear in\n * `select`, `where`, `groupBy`, `having`, `orderBy`, and inside other expressions.\n *\n * This module is a leaf on purpose: `Tables.ts` extends `BaseExpr` at load time, so nothing here may\n * import (at runtime) a module that leads back to `Tables.ts`. Anything that needs metadata, alias\n * binding, or SQL generation for conditions is reached through the `ExprContext` the query parser passes in.\n */\n\nexport const exprBrand: unique symbol = Symbol(\"joist.expr\");\n\n/**\n * Phantom type information carried by every `Expr`.\n *\n * `R` is the decoded result type.\n *\n * `Src` is the expression's *source key*: the type-level identity of the table it reads from. An entity\n * table's key is its type name (`table(Author)` gives `\"Author\"`) or the explicit name in\n * `table(Author, \"m\")`; a subquery's key is its `as: \"book_stats\"`, or the shared sentinel `\"?\"` when it\n * has no `as`. Exactly two questions are asked of a source key, and nothing else:\n *\n * - `MaybeNull` asks \"is my source key among the LEFT-joined sources in this query's join list?\" If yes,\n * the value can be `null`.\n * - `CheckScope` asks \"is my source key among `from` + `join` at all?\" If no, the query reads from a\n * table it never joined.\n *\n * Two special keys opt out of both questions:\n *\n * - `Expr<number, never>` reads from nothing that can be left-joined away, i.e. `b.id.count()`\n * - `Expr<number, string>` (the default) is untracked, i.e. `sql.ref` on an unknown table\n */\nexport interface ExprBrand<R, Src extends string> {\n readonly __result: R;\n readonly __source: Src;\n}\n\n/**\n * \"Any expression whose result is `R`\", checked by brand alone.\n *\n * Method parameters use this instead of `Expr<R>` so that `Expr` stays covariant in `R`: checking\n * `Expr<AuthorId>` against `Expr<AuthorId | null>` then only compares the phantom `__result`, not every\n * method's parameter list (which would make `Expr` invariant, and break `select: b.author_id` dispatch and\n * polymorphic joins).\n */\nexport type ExprLike<R> = { readonly [exprBrand]: ExprBrand<R, any> };\n\n/**\n * A typed SQL expression: a table column, an aggregate, a `sql` template, or a scalar subquery.\n *\n * Conditions and SQL functions are methods, so they need no import. Aggregates keep `Src` so scope\n * checking still sees through `bs.x.max()`; `count` is source-less because `count(x)` is 0, not null,\n * when `x`'s table is left-joined away; `coalesce` drops `Src` on purpose, since its whole job is to\n * remove the nullability a left join adds.\n */\nexport interface Expr<R, Src extends string = string> {\n readonly [exprBrand]: ExprBrand<R, Src>;\n eq(value: R | ExprLike<R> | undefined): ExpressionCondition;\n ne(value: R | ExprLike<R> | undefined): ExpressionCondition;\n gt(value: R | ExprLike<R> | undefined): ExpressionCondition;\n gte(value: R | ExprLike<R> | undefined): ExpressionCondition;\n lt(value: R | ExprLike<R> | undefined): ExpressionCondition;\n lte(value: R | ExprLike<R> | undefined): ExpressionCondition;\n // A list subquery may select a nullable column; NULLs in the set never match, so that is fine.\n in(values: readonly R[] | ExprLike<R | null> | undefined): ExpressionCondition;\n nin(values: readonly R[] | ExprLike<R | null> | undefined): ExpressionCondition;\n\n /** Prefixes this expression and a space to a raw predicate; write .is`IS NULL`, not .is`NULL`. */\n is(strings: TemplateStringsArray, ...values: unknown[]): ExpressionCondition;\n\n /** `count(x)::int`; `count(a.id)` is `count(*)` for the FROM table, and the matched-row count for a left-joined one. */\n count(): Expr<number, never>;\n countDistinct(): Expr<number, never>;\n sum(this: Expr<number | null, Src>): Expr<number | null, Src>;\n avg(this: Expr<number | null, Src>): Expr<number | null, Src>;\n min(): Expr<R | null, Src>;\n max(): Expr<R | null, Src>;\n /** PG keeps element NULLs (a left-joined empty group aggregates as `[null]`), and zero rows aggregate as NULL. */\n arrayAgg(): Expr<R[] | null, Src>;\n stringAgg(this: Expr<string | null, Src>, delimiter: string): Expr<string | null, Src>;\n coalesce(fallback: NonNullable<R>): Expr<NonNullable<R>, never>;\n}\n\n/**\n * A join entry: the join kind is the key, the joined source is the value, plus `on`. `inner?: never` /\n * `left?: never` keep an entry to one kind (the `ExpressionFilter` `and`/`or` trick).\n *\n * `on` is required. A join is pruned when nothing references it anymore, not by an `undefined` ON;\n * `keep: true` pins a join that would otherwise prune, i.e. an inner join used as an existence filter,\n * the way em.find's `keepAliases` does. It is a boolean so callers can pass a flag.\n *\n * Declared here (not `query.ts`) so the relation join factories in `Tables.ts` (i.e. `a.books.as(b)`) can\n * return them without importing `query.ts`; `query.ts` re-constrains `A` to its `QuerySource`.\n */\nexport interface InnerJoin<A, C = ExpressionCondition> {\n readonly inner: A;\n readonly left?: never;\n readonly on: C;\n readonly keep?: boolean;\n}\n\nexport interface LeftJoin<A, C = ExpressionCondition> {\n readonly left: A;\n readonly inner?: never;\n readonly on: C;\n readonly keep?: boolean;\n}\n\n/** SQL plus its `?` bindings plus the SQL aliases it references, i.e. for join pruning. */\nexport interface SqlFragment {\n sql: string;\n bindings: any[];\n refs: string[];\n}\n\n/**\n * What an expression needs from the query it is generating SQL for: the SQL alias assigned to each\n * source, and a way to turn nested conditions into SQL (which needs `ConditionBuilder`, so it lives in `query.ts`).\n */\nexport interface ExprContext {\n /** Returns the SQL alias for a table's `TableMgmt` or a subquery handle, searching enclosing queries. */\n aliasFor(handle: object): string;\n /** Turns a user-facing condition into SQL; `undefined` if it pruned away entirely. */\n conditionToSql(cond: ExpressionCondition): SqlFragment | undefined;\n}\n\nexport function isExpr(value: unknown): value is ExprLike<any> {\n return typeof value === \"object\" && value !== null && exprBrand in value;\n}\n\n/** Every `Expr` is a `BaseExpr` at runtime; this cast keeps `isExpr` a plain type guard so unions narrow. */\nexport function asNode(expr: ExprLike<any>): BaseExpr {\n return expr as any as BaseExpr;\n}\n\nexport const deferredSym: unique symbol = Symbol(\"joist.deferredCondition\");\n\n/**\n * A condition whose SQL depends on aliases that are only known once the query is parsed, i.e.\n * `bookStats.bookCount.gt(1)` or `bs.authorId.eq(a.id)`.\n *\n * It is shaped like a `RawCondition` so it can sit in any `ExpressionFilter`; `resolveDeferredConditions`\n * snapshots `condition`, `bindings`, and `aliases` before the filter is parsed. Domain alias conditions\n * use a separate protocol in the `em.find` parser.\n */\nexport interface DeferredCondition extends RawCondition {\n [deferredSym]: (ctx: ExprContext) => RawCondition;\n}\n\nexport function isDeferredCondition(cond: unknown): cond is DeferredCondition {\n return typeof cond === \"object\" && cond !== null && deferredSym in cond;\n}\n\n/** Creates a `DeferredCondition` that generates its SQL with `fn` once the query's aliases are known. */\nexport function deferredCondition(fn: (ctx: ExprContext) => SqlFragment): DeferredCondition {\n const cond: DeferredCondition = {\n kind: \"raw\",\n aliases: [],\n condition: \"<unresolved>\",\n bindings: [],\n pruneable: false,\n [deferredSym]: (ctx) => {\n const { sql, bindings, refs } = fn(ctx);\n return { ...cond, condition: sql, bindings, aliases: refs };\n },\n };\n return cond;\n}\n\n/**\n * Resolve each condition occurrence and snapshot it before resolving the next one. A nested subquery\n * may reuse the same condition under another alias; it must not overwrite this occurrence's SQL.\n */\nexport function resolveDeferredConditions(\n cond: ExpressionCondition | undefined,\n ctx: ExprContext,\n): ExpressionCondition | undefined {\n if (cond === undefined || cond === null) return cond;\n if (isDeferredCondition(cond)) {\n return cond[deferredSym](ctx);\n } else if (\"and\" in cond && cond.and) {\n return { ...cond, and: cond.and.map((c) => resolveDeferredConditions(c, ctx)) };\n } else if (\"or\" in cond && cond.or) {\n return { ...cond, or: cond.or.map((c) => resolveDeferredConditions(c, ctx)) };\n }\n return cond;\n}\n\n/** Concatenates SQL fragments with `sep`, keeping bindings and refs in order. */\nexport function joinFragments(parts: SqlFragment[], sep: string): SqlFragment {\n return {\n sql: parts.map((p) => p.sql).join(sep),\n bindings: parts.flatMap((p) => p.bindings),\n refs: parts.flatMap((p) => p.refs),\n };\n}\n\n/**\n * The methods every expression shares. Subclasses provide `toSql`, and usually `decode`/`encode`.\n *\n * Table columns can override comparisons to apply column-specific conversions. Domain aliases do not\n * implement this protocol.\n */\nexport abstract class BaseExpr {\n readonly [exprBrand]: any = this;\n\n /** Produces this expression's SQL so it can be embedded in a larger expression, i.e. a subquery gets parens. */\n abstract toSql(ctx: ExprContext): SqlFragment;\n\n /** Only an actual scalar/IN subquery exposes its selected expression, not an ordinary ID expression. */\n get subquerySelect(): BaseExpr | undefined {\n return undefined;\n }\n\n /** Known SQL representation and logical domain; raw SQL and unmodeled refs remain unknown. */\n get outputType(): TypeInfo | undefined {\n return undefined;\n }\n\n /** Physical SQL nullability; undefined means unknown, not a NOT NULL guarantee. */\n get sqlNullable(): boolean | undefined {\n return undefined;\n }\n\n /** The source of a direct column reference, whose value becomes NULL under an unmatched LEFT join. */\n get sqlSource(): object | undefined {\n return undefined;\n }\n\n /** Produces the SQL without the outer parens a subquery normally gets; only differs for subqueries. */\n toSqlBare(ctx: ExprContext): SqlFragment {\n return this.toSql(ctx);\n }\n\n /** Converts a result-set value into the domain value, i.e. an int into a tagged id. */\n decode(value: unknown): unknown {\n return value;\n }\n\n /** Converts a domain value into the database value, i.e. a tagged id into an int, for bindings. */\n encode(value: unknown): unknown {\n return value;\n }\n\n eq(value: unknown): ExpressionCondition {\n return this.compare(\"=\", value);\n }\n\n ne(value: unknown): ExpressionCondition {\n return this.compare(\"!=\", value);\n }\n\n gt(value: unknown): ExpressionCondition {\n return this.compare(\">\", value);\n }\n\n gte(value: unknown): ExpressionCondition {\n return this.compare(\">=\", value);\n }\n\n lt(value: unknown): ExpressionCondition {\n return this.compare(\"<\", value);\n }\n\n lte(value: unknown): ExpressionCondition {\n return this.compare(\"<=\", value);\n }\n\n in(values: unknown): ExpressionCondition {\n return this.inList(\"IN\", values);\n }\n\n nin(values: unknown): ExpressionCondition {\n return this.inList(\"NOT IN\", values);\n }\n\n /** Prefixes this expression to a SQL template, retaining bindings and referenced aliases. */\n is(strings: TemplateStringsArray, ...values: unknown[]): ExpressionCondition {\n const suffix = new TemplateExpr(strings, values);\n return deferredCondition((ctx) => joinFragments([this.toSql(ctx), suffix.toSql(ctx)], \" \"));\n }\n\n count(): Expr<number, never> {\n return new FnExpr(\"count\", [this], {\n suffix: \"::int\",\n decode: decodeNumber,\n encode: identity,\n outputType: { dbType: \"int4\", domain: Number, arrayElementSafe: true },\n }) as any;\n }\n\n countDistinct(): Expr<number, never> {\n return new FnExpr(\"count\", [this], {\n prefix: \"DISTINCT \",\n suffix: \"::int\",\n decode: decodeNumber,\n encode: identity,\n outputType: { dbType: \"int4\", domain: Number, arrayElementSafe: true },\n }) as any;\n }\n\n sum(): Expr<number | null, any> {\n return new FnExpr(\"sum\", [this], {\n decode: decodeNumber,\n encode: identity,\n outputType: numericAggregateOutputType(\"sum\", this.outputType),\n }) as any;\n }\n\n avg(): Expr<number | null, any> {\n return new FnExpr(\"avg\", [this], {\n decode: decodeNumber,\n encode: identity,\n outputType: numericAggregateOutputType(\"avg\", this.outputType),\n }) as any;\n }\n\n min(): Expr<any, any> {\n return new FnExpr(\"min\", [this], {\n decode: (v) => this.decode(v),\n outputType: minMaxOutputType(this.outputType),\n }) as any;\n }\n\n max(): Expr<any, any> {\n return new FnExpr(\"max\", [this], {\n decode: (v) => this.decode(v),\n outputType: minMaxOutputType(this.outputType),\n }) as any;\n }\n\n arrayAgg(): Expr<any, any> {\n // Values are arrays while the argument encodes/decodes *elements*, i.e. a `.coalesce([\"b:1\"])`\n // fallback must encode each tagged id, not hand the whole array to the id column's encoder\n return new FnExpr(\"array_agg\", [this], {\n decode: (v) => (Array.isArray(v) ? v.map((e) => this.decode(e)) : v),\n encode: (v) => (Array.isArray(v) ? v.map((e) => this.encode(e)) : v),\n outputType: arrayOutputType(this.outputType),\n }) as any;\n }\n\n stringAgg(delimiter: string): Expr<string | null, any> {\n return new FnExpr(\"string_agg\", [this, new BindingExpr(delimiter)], {\n outputType:\n this.outputType?.domain === String ? { dbType: \"text\", domain: String, arrayElementSafe: true } : undefined,\n }) as any;\n }\n\n coalesce(fallback: unknown): Expr<any, never> {\n return new FnExpr(\"coalesce\", [this, new BindingExpr(this.encode(fallback))], {\n decode: (v) => this.decode(v),\n outputType: this.outputType,\n }) as any;\n }\n\n /** `this op value`, where `value` may be `undefined` (pruned), `null`, another expression, or a literal. */\n protected compare(op: string, value: unknown): ExpressionCondition {\n if (value === undefined) return skipCondition;\n if (value === null) {\n const not = op === \"=\" ? \"\" : op === \"!=\" ? \"NOT \" : fail(`Cannot compare ${op} to null`);\n return deferredCondition((ctx) => {\n const left = this.toSql(ctx);\n return { ...left, sql: `${left.sql} IS ${not}NULL` };\n });\n }\n if (isExpr(value)) {\n return deferredCondition((ctx) => joinFragments([this.toSql(ctx), asNode(value).toSql(ctx)], ` ${op} `));\n }\n return deferredCondition((ctx) => {\n const left = this.toSql(ctx);\n return { sql: `${left.sql} ${op} ?`, bindings: [...left.bindings, this.encode(value)], refs: left.refs };\n });\n }\n\n /** `this IN (subquery)` or `this = ANY(?)` for a list; `NOT IN` / `!= ALL(?)` for `nin`. */\n protected inList(op: \"IN\" | \"NOT IN\", values: unknown): ExpressionCondition {\n if (values === undefined) return skipCondition;\n if (isExpr(values)) {\n return deferredCondition((ctx) => {\n const left = this.toSql(ctx);\n const right = asNode(values).toSqlBare(ctx);\n return joinFragments([left, { ...right, sql: `(${right.sql})` }], ` ${op} `);\n });\n }\n if (!Array.isArray(values)) fail(`Expected an array or subquery for ${op}, got ${values}`);\n const fn = op === \"IN\" ? \"= ANY(?)\" : \"!= ALL(?)\";\n return deferredCondition((ctx) => {\n const left = this.toSql(ctx);\n return {\n sql: `${left.sql} ${fn}`,\n bindings: [...left.bindings, values.map((v) => this.encode(v))],\n refs: left.refs,\n };\n });\n }\n}\n\n/**\n * A SQL function applied to expressions, i.e. `count(a.\"id\")::int` or `coalesce(bs.\"n\", ?)`.\n *\n * By default decoding is identity and encoding follows the first argument. Callers explicitly supply\n * a decoder and output type when needed (`max(a.id)` is still an id); numeric aggregates supply their\n * own decoder/encoder, since `count(a.id)` is a number, not an id. Unknown functions have no output type.\n */\nexport class FnExpr extends BaseExpr {\n constructor(\n private name: string,\n private args: BaseExpr[],\n private opts: {\n prefix?: string;\n suffix?: string;\n decode?: (value: unknown) => unknown;\n encode?: (value: unknown) => unknown;\n outputType?: TypeInfo;\n },\n ) {\n super();\n }\n\n get outputType(): TypeInfo | undefined {\n return this.opts.outputType;\n }\n\n get sqlNullable(): boolean | undefined {\n switch (this.name) {\n case \"count\":\n return false;\n case \"sum\":\n case \"avg\":\n case \"min\":\n case \"max\":\n case \"array_agg\":\n case \"string_agg\":\n return true;\n case \"coalesce\":\n // Only the fallback is independent of an outer query's LEFT joins.\n return this.args[1]?.sqlNullable === false ? false : undefined;\n default:\n return undefined;\n }\n }\n\n toSql(ctx: ExprContext): SqlFragment {\n const args = joinFragments(\n this.args.map((a) => a.toSql(ctx)),\n \", \",\n );\n return { ...args, sql: `${this.name}(${this.opts.prefix ?? \"\"}${args.sql})${this.opts.suffix ?? \"\"}` };\n }\n\n decode(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n return this.opts.decode ? this.opts.decode(value) : value;\n }\n\n encode(value: unknown): unknown {\n return this.opts.encode ? this.opts.encode(value) : this.args[0].encode(value);\n }\n}\n\n/** A bound literal, i.e. the `?` in `coalesce(x, ?)`. */\nexport class BindingExpr extends BaseExpr {\n constructor(private value: unknown) {\n super();\n }\n\n get sqlNullable(): boolean {\n return this.value === null || this.value === undefined;\n }\n\n toSql(): SqlFragment {\n return { sql: \"?\", bindings: [this.value], refs: [] };\n }\n}\n\n/** An unmodeled column on a known source, i.e. `sql.ref(a, \"ts_search\")`; untracked at the type level. */\nexport class RefExpr extends BaseExpr {\n constructor(\n private handle: object,\n private column: string,\n ) {\n super();\n }\n\n get sqlSource(): object {\n return this.handle;\n }\n\n toSql(ctx: ExprContext): SqlFragment {\n const alias = ctx.aliasFor(this.handle);\n // safeKq for both halves: sql.ref takes user strings, and a subquery alias is its `as` name\n return { sql: `${safeKq(alias)}.${safeKq(this.column)}`, bindings: [], refs: [alias] };\n }\n}\n\n/**\n * A `sql` tagged template.\n *\n * For an Author table `a` assigned the SQL alias `a1`:\n *\n * ```ts\n * sql`${a.age} * 2` // Expression: a1.age * 2\n * sql`${a.age.gte(18)}` // Condition: (a1.age >= ?), bindings [18]\n * sql`${\"Alice\"}` // Value: ?, bindings [\"Alice\"]\n * ```\n */\nexport class TemplateExpr extends BaseExpr {\n constructor(\n private strings: TemplateStringsArray,\n private values: unknown[],\n ) {\n super();\n }\n\n toSql(ctx: ExprContext): SqlFragment {\n const parts: SqlFragment[] = [];\n this.strings.forEach((s, i) => {\n parts.push({ sql: s, bindings: [], refs: [] });\n if (i < this.values.length) parts.push(interpolationToSql(this.values[i], ctx));\n });\n return joinFragments(parts, \"\");\n }\n}\n\n/** Turns one `${...}` of a `sql` template into SQL: an expression, a condition, or a bound value. */\nexport function interpolationToSql(value: unknown, ctx: ExprContext): SqlFragment {\n if (isExpr(value)) {\n return asNode(value).toSql(ctx);\n } else if (isConditionLike(value)) {\n return ctx.conditionToSql(value as ExpressionCondition) ?? { sql: \"true\", bindings: [], refs: [] };\n } else {\n return { sql: \"?\", bindings: [value], refs: [] };\n }\n}\n\n/** True for the user-facing condition shapes: `{ and }`, `{ or }`, `ColumnCondition`, `RawCondition`. */\nexport function isConditionLike(value: unknown): boolean {\n if (typeof value !== \"object\" || value === null) return false;\n const v = value as any;\n return \"and\" in v || \"or\" in v || v.kind === \"column\" || v.kind === \"raw\" || v.kind === \"exists\";\n}\n\n/**\n * Resolves the SQL result type of supported numeric aggregates; other overloads remain unknown.\n *\n * I.e. `a.age.sum()` has `dbType: \"int8\"` and Number conversion, while `a.age` has `dbType: \"int4\"`\n * and identity conversion. Their number domains agree, but SQL types reject a union in either order.\n */\nfunction numericAggregateOutputType(name: \"sum\" | \"avg\", outputType: TypeInfo | undefined): TypeInfo | undefined {\n switch (outputType?.dbType) {\n case \"int2\":\n case \"int4\":\n return { dbType: name === \"sum\" ? \"int8\" : \"numeric\", domain: Number, arrayElementSafe: true };\n case \"int8\":\n case \"numeric\":\n return { dbType: \"numeric\", domain: Number, arrayElementSafe: true };\n case \"float4\":\n return { dbType: name === \"sum\" ? \"float4\" : \"float8\", domain: Number, arrayElementSafe: true };\n case \"float8\":\n return { dbType: \"float8\", domain: Number, arrayElementSafe: true };\n default:\n return undefined;\n }\n}\n\n/** Only known MIN/MAX overloads have predictable output types; varchar/name use the text overload. */\nfunction minMaxOutputType(outputType: TypeInfo | undefined): TypeInfo | undefined {\n switch (outputType?.dbType) {\n case \"varchar\":\n case \"name\":\n return { ...outputType, dbType: \"text\" };\n case \"int2\":\n case \"int4\":\n case \"int8\":\n case \"numeric\":\n case \"float4\":\n case \"float8\":\n case \"text\":\n case \"bpchar\":\n case \"date\":\n case \"time\":\n case \"timetz\":\n case \"timestamp\":\n case \"timestamptz\":\n case \"interval\":\n case \"money\":\n case \"inet\":\n return outputType;\n default:\n return undefined;\n }\n}\n\n/** Decodes `count`/`sum`/`avg` results, which Postgres returns as strings for bigint/numeric. */\nfunction decodeNumber(value: unknown): unknown {\n return typeof value === \"string\" ? Number(value) : value;\n}\n\nfunction identity(value: unknown): unknown {\n return value;\n}\n\nfunction fail(message: string): never {\n throw new Error(message);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Expr.cjs","names":["brandPredicate","arrayOutputType","skipCondition","safeKq"],"sources":["../src/Expr.ts"],"sourcesContent":["import { type ConditionInput, type PredicateBrand, type SqlCondition, brandPredicate } from \"./conditions.ts\";\nimport type { EntityMetadata } from \"./EntityMetadata.ts\";\nimport { safeKq } from \"./keywords.ts\";\nimport type { RawCondition } from \"./QueryParser.ts\";\nimport { skipCondition } from \"./skipCondition.ts\";\nimport { type TypeInfo, arrayOutputType } from \"./TypeInfo.ts\";\n\n/**\n * The shared expression protocol for `em.query`.\n *\n * Table columns (`a.first_name`), aggregates (`b.id.count()`), `sql` templates, subquery columns\n * (`bookStats.bookCount`), and scalar subqueries all implement it, so any of them can appear in\n * `select`, `where`, `groupBy`, `having`, `orderBy`, and inside other expressions.\n *\n * This module is a leaf on purpose: `Tables.ts` extends `BaseExpr` at load time, so nothing here may\n * import (at runtime) a module that leads back to `Tables.ts`. Anything that needs metadata, alias\n * binding, or SQL generation for conditions is reached through the `ExprContext` the query parser passes in.\n */\n\nexport const exprBrand: unique symbol = Symbol(\"joist.expr\");\n\n/**\n * Phantom type information carried by every `Expr`.\n *\n * `R` is the decoded result type.\n *\n * `Src` is the expression's *source key*: the type-level identity of the table it reads from. An entity\n * table's key is its type name (`table(Author)` gives `\"Author\"`) or the explicit name in\n * `table(Author, \"m\")`; a subquery's key is its `as: \"book_stats\"`, or the shared sentinel `\"?\"` when it\n * has no `as`. Exactly two questions are asked of a source key, and nothing else:\n *\n * - `MaybeNull` asks \"is my source key among the LEFT-joined sources in this query's join list?\" If yes,\n * the value can be `null`.\n * - `CheckScope` asks \"is my source key among `from` + `join` at all?\" If no, the query reads from a\n * table it never joined.\n *\n * Two special keys opt out of both questions:\n *\n * - `Expr<number, never>` reads from nothing that can be left-joined away, i.e. `b.id.count()`\n * - `Expr<number, string>` (the default) is untracked, i.e. `sql.ref` on an unknown table\n */\nexport interface ExprBrand<R, Src extends string> {\n readonly __result: R;\n readonly __source: Src;\n}\n\n/**\n * \"Any expression whose result is `R`\", checked by brand alone.\n *\n * Method parameters use this instead of `Expr<R>` so that `Expr` stays covariant in `R`: checking\n * `Expr<AuthorId>` against `Expr<AuthorId | null>` then only compares the phantom `__result`, not every\n * method's parameter list (which would make `Expr` invariant, and break `select: b.author_id` dispatch and\n * polymorphic joins).\n */\nexport type ExprLike<R> = { readonly [exprBrand]: ExprBrand<R, any> };\n\n/**\n * A typed SQL expression: a table column, an aggregate, a `sql` template, or a scalar subquery.\n *\n * Conditions and SQL functions are methods, so they need no import. Aggregates keep `Src` so scope\n * checking still sees through `bs.x.max()`; `count` is source-less because `count(x)` is 0, not null,\n * when `x`'s table is left-joined away; `coalesce` drops `Src` on purpose, since its whole job is to\n * remove the nullability a left join adds.\n */\nexport interface Expr<R, Src extends string = string> {\n readonly [exprBrand]: ExprBrand<R, Src>;\n eq(value: R | ExprLike<R> | undefined): SqlCondition;\n ne(value: R | ExprLike<R> | undefined): SqlCondition;\n gt(value: R | ExprLike<R> | undefined): SqlCondition;\n gte(value: R | ExprLike<R> | undefined): SqlCondition;\n lt(value: R | ExprLike<R> | undefined): SqlCondition;\n lte(value: R | ExprLike<R> | undefined): SqlCondition;\n // A list subquery may select a nullable column; NULLs in the set never match, so that is fine.\n in(values: readonly R[] | ExprLike<R | null> | undefined): SqlCondition;\n nin(values: readonly R[] | ExprLike<R | null> | undefined): SqlCondition;\n\n /** Prefixes this expression and a space to a raw predicate; write .is`IS NULL`, not .is`NULL`. */\n is(strings: TemplateStringsArray, ...values: unknown[]): SqlCondition;\n\n /** `count(x)::int`; `count(a.id)` is `count(*)` for the FROM table, and the matched-row count for a left-joined one. */\n count(): Expr<number, never>;\n countDistinct(): Expr<number, never>;\n sum(this: Expr<number | null, Src>): Expr<number | null, Src>;\n avg(this: Expr<number | null, Src>): Expr<number | null, Src>;\n min(): Expr<R | null, Src>;\n max(): Expr<R | null, Src>;\n /** PG keeps element NULLs (a left-joined empty group aggregates as `[null]`), and zero rows aggregate as NULL. */\n arrayAgg(): Expr<R[] | null, Src>;\n stringAgg(this: Expr<string | null, Src>, delimiter: string): Expr<string | null, Src>;\n coalesce(fallback: NonNullable<R>): Expr<NonNullable<R>, never>;\n}\n\n/**\n * A join entry: the join kind is the key, the joined source is the value, plus `on`. `inner?: never` /\n * `left?: never` keep an entry to one kind (the `SqlCondition` `and`/`or` trick).\n *\n * `on` is required. A join is pruned when nothing references it anymore, not by an `undefined` ON;\n * `keep: true` pins a join that would otherwise prune, i.e. an inner join used as an existence filter,\n * the way em.find's `keepAliases` does. It is a boolean so callers can pass a flag.\n *\n * Declared here (not `query.ts`) so the relation join factories in `Tables.ts` (i.e. `a.books.as(b)`) can\n * return them without importing `query.ts`; `query.ts` re-constrains `A` to its `QuerySource`.\n */\nexport interface InnerJoin<A, C = SqlCondition> {\n readonly inner: A;\n readonly left?: never;\n readonly on: C;\n readonly keep?: boolean;\n}\n\nexport interface LeftJoin<A, C = SqlCondition> {\n readonly left: A;\n readonly inner?: never;\n readonly on: C;\n readonly keep?: boolean;\n}\n\n/** SQL plus its `?` bindings plus the SQL aliases it references, i.e. for join pruning. */\nexport interface SqlFragment {\n sql: string;\n bindings: any[];\n refs: string[];\n}\n\n/**\n * What an expression needs from the query it is generating SQL for: the SQL alias assigned to each\n * source, and a way to turn nested conditions into SQL (which needs `ConditionBuilder`, so it lives in `query.ts`).\n */\nexport interface ExprContext {\n /** Returns the SQL alias for a table's `TableMgmt` or a subquery handle, searching enclosing queries. */\n aliasFor(handle: object): string;\n /** Turns a user-facing condition into SQL; `undefined` if it pruned away entirely. */\n conditionToSql(cond: SqlCondition): SqlFragment | undefined;\n}\n\nexport function isExpr(value: unknown): value is ExprLike<any> {\n return typeof value === \"object\" && value !== null && exprBrand in value;\n}\n\n/** Every `Expr` is a `BaseExpr` at runtime; this cast keeps `isExpr` a plain type guard so unions narrow. */\nexport function asNode(expr: ExprLike<any>): BaseExpr {\n return expr as any as BaseExpr;\n}\n\nexport const deferredSym: unique symbol = Symbol(\"joist.deferredCondition\");\n\n/**\n * A condition whose SQL depends on aliases that are only known once the query is parsed, i.e.\n * `bookStats.bookCount.gt(1)` or `bs.authorId.eq(a.id)`.\n *\n * It is shaped like a `RawCondition` with a SQL brand so it can sit in any `SqlCondition`; `resolveDeferredConditions`\n * snapshots `condition`, `bindings`, and `aliases` before the filter is parsed. Domain alias conditions\n * use a separate protocol in the `em.find` parser.\n */\nexport interface DeferredCondition extends RawCondition, PredicateBrand<\"sql\"> {\n [deferredSym]: (ctx: ExprContext) => RawCondition;\n}\n\nexport function isDeferredCondition(cond: unknown): cond is DeferredCondition {\n return typeof cond === \"object\" && cond !== null && deferredSym in cond;\n}\n\n/** Creates a `DeferredCondition` that generates its SQL with `fn` once the query's aliases are known. */\nexport function deferredCondition(fn: (ctx: ExprContext) => SqlFragment): DeferredCondition {\n const cond: DeferredCondition = brandPredicate(\n {\n kind: \"raw\",\n aliases: [],\n condition: \"<unresolved>\",\n bindings: [],\n pruneable: false,\n [deferredSym]: (ctx: ExprContext): RawCondition => {\n const { sql, bindings, refs } = fn(ctx);\n return { ...cond, condition: sql, bindings, aliases: refs };\n },\n },\n \"sql\",\n );\n return cond;\n}\n\n/**\n * Resolve each condition occurrence and snapshot it before resolving the next one. A nested subquery\n * may reuse the same condition under another alias; it must not overwrite this occurrence's SQL.\n */\nexport function resolveDeferredConditions(\n cond: ConditionInput | undefined,\n ctx: ExprContext,\n): ConditionInput | undefined {\n if (cond === undefined || cond === null) return cond;\n if (isDeferredCondition(cond)) {\n return cond[deferredSym](ctx);\n } else if (\"and\" in cond && cond.and) {\n return { ...cond, and: cond.and.map((c) => resolveDeferredConditions(c, ctx)) };\n } else if (\"or\" in cond && cond.or) {\n return { ...cond, or: cond.or.map((c) => resolveDeferredConditions(c, ctx)) };\n }\n return cond;\n}\n\n/** Concatenates SQL fragments with `sep`, keeping bindings and refs in order. */\nexport function joinFragments(parts: SqlFragment[], sep: string): SqlFragment {\n return {\n sql: parts.map((p) => p.sql).join(sep),\n bindings: parts.flatMap((p) => p.bindings),\n refs: parts.flatMap((p) => p.refs),\n };\n}\n\n/**\n * The methods every expression shares. Subclasses provide `toSql`, and usually `decode`/`encode`.\n *\n * Table columns can override comparisons to apply column-specific conversions. Domain aliases do not\n * implement this protocol.\n */\nexport abstract class BaseExpr {\n readonly [exprBrand]: any = this;\n\n /** Produces this expression's SQL so it can be embedded in a larger expression, i.e. a subquery gets parens. */\n abstract toSql(ctx: ExprContext): SqlFragment;\n\n /** Only an actual scalar/IN subquery exposes its selected expression, not an ordinary ID expression. */\n get subquerySelect(): BaseExpr | undefined {\n return undefined;\n }\n\n /** Known SQL representation and logical domain; raw SQL and unmodeled refs remain unknown. */\n get outputType(): TypeInfo | undefined {\n return undefined;\n }\n\n /** Physical SQL nullability; undefined means unknown, not a NOT NULL guarantee. */\n get sqlNullable(): boolean | undefined {\n return undefined;\n }\n\n /** The source of a direct column reference, whose value becomes NULL under an unmatched LEFT join. */\n get sqlSource(): object | undefined {\n return undefined;\n }\n\n /** Produces the SQL without the outer parens a subquery normally gets; only differs for subqueries. */\n toSqlBare(ctx: ExprContext): SqlFragment {\n return this.toSql(ctx);\n }\n\n /** Converts a result-set value into the domain value, i.e. an int into a tagged id. */\n decode(value: unknown): unknown {\n return value;\n }\n\n /** Converts a domain value into the database value, i.e. a tagged id into an int, for bindings. */\n encode(value: unknown): unknown {\n return value;\n }\n\n eq(value: unknown): SqlCondition {\n return this.compare(\"=\", value);\n }\n\n ne(value: unknown): SqlCondition {\n return this.compare(\"!=\", value);\n }\n\n gt(value: unknown): SqlCondition {\n return this.compare(\">\", value);\n }\n\n gte(value: unknown): SqlCondition {\n return this.compare(\">=\", value);\n }\n\n lt(value: unknown): SqlCondition {\n return this.compare(\"<\", value);\n }\n\n lte(value: unknown): SqlCondition {\n return this.compare(\"<=\", value);\n }\n\n in(values: unknown): SqlCondition {\n return this.inList(\"IN\", values);\n }\n\n nin(values: unknown): SqlCondition {\n return this.inList(\"NOT IN\", values);\n }\n\n /** Prefixes this expression to a SQL template, retaining bindings and referenced aliases. */\n is(strings: TemplateStringsArray, ...values: unknown[]): SqlCondition {\n const suffix = new TemplateExpr(strings, values);\n return deferredCondition((ctx) => joinFragments([this.toSql(ctx), suffix.toSql(ctx)], \" \"));\n }\n\n count(): Expr<number, never> {\n return new FnExpr(\"count\", [this], {\n suffix: \"::int\",\n decode: decodeNumber,\n encode: identity,\n outputType: { dbType: \"int4\", domain: Number, arrayElementSafe: true },\n }) as any;\n }\n\n countDistinct(): Expr<number, never> {\n return new FnExpr(\"count\", [this], {\n prefix: \"DISTINCT \",\n suffix: \"::int\",\n decode: decodeNumber,\n encode: identity,\n outputType: { dbType: \"int4\", domain: Number, arrayElementSafe: true },\n }) as any;\n }\n\n sum(): Expr<number | null, any> {\n return new FnExpr(\"sum\", [this], {\n decode: decodeNumber,\n encode: identity,\n outputType: numericAggregateOutputType(\"sum\", this.outputType),\n }) as any;\n }\n\n avg(): Expr<number | null, any> {\n return new FnExpr(\"avg\", [this], {\n decode: decodeNumber,\n encode: identity,\n outputType: numericAggregateOutputType(\"avg\", this.outputType),\n }) as any;\n }\n\n min(): Expr<any, any> {\n return new FnExpr(\"min\", [this], {\n decode: (v) => this.decode(v),\n outputType: minMaxOutputType(this.outputType),\n }) as any;\n }\n\n max(): Expr<any, any> {\n return new FnExpr(\"max\", [this], {\n decode: (v) => this.decode(v),\n outputType: minMaxOutputType(this.outputType),\n }) as any;\n }\n\n arrayAgg(): Expr<any, any> {\n // Values are arrays while the argument encodes/decodes *elements*, i.e. a `.coalesce([\"b:1\"])`\n // fallback must encode each tagged id, not hand the whole array to the id column's encoder\n return new FnExpr(\"array_agg\", [this], {\n decode: (v) => (Array.isArray(v) ? v.map((e) => this.decode(e)) : v),\n encode: (v) => (Array.isArray(v) ? v.map((e) => this.encode(e)) : v),\n outputType: arrayOutputType(this.outputType),\n }) as any;\n }\n\n stringAgg(delimiter: string): Expr<string | null, any> {\n return new FnExpr(\"string_agg\", [this, new BindingExpr(delimiter)], {\n outputType:\n this.outputType?.domain === String ? { dbType: \"text\", domain: String, arrayElementSafe: true } : undefined,\n }) as any;\n }\n\n coalesce(fallback: unknown): Expr<any, never> {\n return new FnExpr(\"coalesce\", [this, new BindingExpr(this.encode(fallback))], {\n decode: (v) => this.decode(v),\n outputType: this.outputType,\n }) as any;\n }\n\n /** `this op value`, where `value` may be `undefined` (pruned), `null`, another expression, or a literal. */\n protected compare(op: string, value: unknown): SqlCondition {\n if (value === undefined) return skipCondition;\n if (value === null) {\n const not = op === \"=\" ? \"\" : op === \"!=\" ? \"NOT \" : fail(`Cannot compare ${op} to null`);\n return deferredCondition((ctx) => {\n const left = this.toSql(ctx);\n return { ...left, sql: `${left.sql} IS ${not}NULL` };\n });\n }\n if (isExpr(value)) {\n return deferredCondition((ctx) => joinFragments([this.toSql(ctx), asNode(value).toSql(ctx)], ` ${op} `));\n }\n return deferredCondition((ctx) => {\n const left = this.toSql(ctx);\n return { sql: `${left.sql} ${op} ?`, bindings: [...left.bindings, this.encode(value)], refs: left.refs };\n });\n }\n\n /** `this IN (subquery)` or `this = ANY(?)` for a list; `NOT IN` / `!= ALL(?)` for `nin`. */\n protected inList(op: \"IN\" | \"NOT IN\", values: unknown): SqlCondition {\n if (values === undefined) return skipCondition;\n if (isExpr(values)) {\n return deferredCondition((ctx) => {\n const left = this.toSql(ctx);\n const right = asNode(values).toSqlBare(ctx);\n return joinFragments([left, { ...right, sql: `(${right.sql})` }], ` ${op} `);\n });\n }\n if (!Array.isArray(values)) fail(`Expected an array or subquery for ${op}, got ${values}`);\n const fn = op === \"IN\" ? \"= ANY(?)\" : \"!= ALL(?)\";\n return deferredCondition((ctx) => {\n const left = this.toSql(ctx);\n return {\n sql: `${left.sql} ${fn}`,\n bindings: [...left.bindings, values.map((v) => this.encode(v))],\n refs: left.refs,\n };\n });\n }\n}\n\n/**\n * A SQL function applied to expressions, i.e. `count(a.\"id\")::int` or `coalesce(bs.\"n\", ?)`.\n *\n * By default decoding is identity and encoding follows the first argument. Callers explicitly supply\n * a decoder and output type when needed (`max(a.id)` is still an id); numeric aggregates supply their\n * own decoder/encoder, since `count(a.id)` is a number, not an id. Unknown functions have no output type.\n */\nexport class FnExpr extends BaseExpr {\n constructor(\n private name: string,\n private args: BaseExpr[],\n private opts: {\n prefix?: string;\n suffix?: string;\n decode?: (value: unknown) => unknown;\n encode?: (value: unknown) => unknown;\n outputType?: TypeInfo;\n },\n ) {\n super();\n }\n\n get outputType(): TypeInfo | undefined {\n return this.opts.outputType;\n }\n\n get sqlNullable(): boolean | undefined {\n switch (this.name) {\n case \"count\":\n return false;\n case \"sum\":\n case \"avg\":\n case \"min\":\n case \"max\":\n case \"array_agg\":\n case \"string_agg\":\n return true;\n case \"coalesce\":\n // Only the fallback is independent of an outer query's LEFT joins.\n return this.args[1]?.sqlNullable === false ? false : undefined;\n default:\n return undefined;\n }\n }\n\n toSql(ctx: ExprContext): SqlFragment {\n const args = joinFragments(\n this.args.map((a) => a.toSql(ctx)),\n \", \",\n );\n return { ...args, sql: `${this.name}(${this.opts.prefix ?? \"\"}${args.sql})${this.opts.suffix ?? \"\"}` };\n }\n\n decode(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n return this.opts.decode ? this.opts.decode(value) : value;\n }\n\n encode(value: unknown): unknown {\n return this.opts.encode ? this.opts.encode(value) : this.args[0].encode(value);\n }\n}\n\n/** A bound literal, i.e. the `?` in `coalesce(x, ?)`. */\nexport class BindingExpr extends BaseExpr {\n constructor(private value: unknown) {\n super();\n }\n\n get sqlNullable(): boolean {\n return this.value === null || this.value === undefined;\n }\n\n toSql(): SqlFragment {\n return { sql: \"?\", bindings: [this.value], refs: [] };\n }\n}\n\n/** An unmodeled column on a known source, i.e. `sql.ref(a, \"ts_search\")`; untracked at the type level. */\nexport class RefExpr extends BaseExpr {\n constructor(\n private handle: object,\n private column: string,\n ) {\n super();\n }\n\n get sqlSource(): object {\n return this.handle;\n }\n\n toSql(ctx: ExprContext): SqlFragment {\n const alias = ctx.aliasFor(this.handle);\n // safeKq for both halves: sql.ref takes user strings, and a subquery alias is its `as` name\n return { sql: `${safeKq(alias)}.${safeKq(this.column)}`, bindings: [], refs: [alias] };\n }\n}\n\n/**\n * A `sql` tagged template.\n *\n * For an Author table `a` assigned the SQL alias `a1`:\n *\n * ```ts\n * sql`${a.age} * 2` // Expression: a1.age * 2\n * sql`${a.age.gte(18)}` // Condition: (a1.age >= ?), bindings [18]\n * sql`${\"Alice\"}` // Value: ?, bindings [\"Alice\"]\n * ```\n */\nexport class TemplateExpr extends BaseExpr {\n constructor(\n private strings: TemplateStringsArray,\n private values: unknown[],\n ) {\n super();\n }\n\n toSql(ctx: ExprContext): SqlFragment {\n const parts: SqlFragment[] = [];\n this.strings.forEach((s, i) => {\n parts.push({ sql: s, bindings: [], refs: [] });\n if (i < this.values.length) parts.push(interpolationToSql(this.values[i], ctx));\n });\n return joinFragments(parts, \"\");\n }\n}\n\n/** Turns one `${...}` of a `sql` template into SQL: an expression, a condition, or a bound value. */\nexport function interpolationToSql(value: unknown, ctx: ExprContext): SqlFragment {\n if (isExpr(value)) {\n return asNode(value).toSql(ctx);\n } else if (isConditionLike(value)) {\n return ctx.conditionToSql(value as SqlCondition) ?? { sql: \"true\", bindings: [], refs: [] };\n } else {\n return { sql: \"?\", bindings: [value], refs: [] };\n }\n}\n\n/** True for the user-facing condition shapes: `{ and }`, `{ or }`, `ColumnCondition`, `RawCondition`. */\nexport function isConditionLike(value: unknown): boolean {\n if (typeof value !== \"object\" || value === null) return false;\n const v = value as any;\n return \"and\" in v || \"or\" in v || v.kind === \"column\" || v.kind === \"raw\" || v.kind === \"exists\";\n}\n\n/**\n * Resolves the SQL result type of supported numeric aggregates; other overloads remain unknown.\n *\n * I.e. `a.age.sum()` has `dbType: \"int8\"` and Number conversion, while `a.age` has `dbType: \"int4\"`\n * and identity conversion. Their number domains agree, but SQL types reject a union in either order.\n */\nfunction numericAggregateOutputType(name: \"sum\" | \"avg\", outputType: TypeInfo | undefined): TypeInfo | undefined {\n switch (outputType?.dbType) {\n case \"int2\":\n case \"int4\":\n return { dbType: name === \"sum\" ? \"int8\" : \"numeric\", domain: Number, arrayElementSafe: true };\n case \"int8\":\n case \"numeric\":\n return { dbType: \"numeric\", domain: Number, arrayElementSafe: true };\n case \"float4\":\n return { dbType: name === \"sum\" ? \"float4\" : \"float8\", domain: Number, arrayElementSafe: true };\n case \"float8\":\n return { dbType: \"float8\", domain: Number, arrayElementSafe: true };\n default:\n return undefined;\n }\n}\n\n/** Only known MIN/MAX overloads have predictable output types; varchar/name use the text overload. */\nfunction minMaxOutputType(outputType: TypeInfo | undefined): TypeInfo | undefined {\n switch (outputType?.dbType) {\n case \"varchar\":\n case \"name\":\n return { ...outputType, dbType: \"text\" };\n case \"int2\":\n case \"int4\":\n case \"int8\":\n case \"numeric\":\n case \"float4\":\n case \"float8\":\n case \"text\":\n case \"bpchar\":\n case \"date\":\n case \"time\":\n case \"timetz\":\n case \"timestamp\":\n case \"timestamptz\":\n case \"interval\":\n case \"money\":\n case \"inet\":\n return outputType;\n default:\n return undefined;\n }\n}\n\n/** Decodes `count`/`sum`/`avg` results, which Postgres returns as strings for bigint/numeric. */\nfunction decodeNumber(value: unknown): unknown {\n return typeof value === \"string\" ? Number(value) : value;\n}\n\nfunction identity(value: unknown): unknown {\n return value;\n}\n\nfunction fail(message: string): never {\n throw new Error(message);\n}\n"],"mappings":"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"}
|
package/build/Expr.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { ConditionInput, PredicateBrand, SqlCondition } from "./conditions.cjs";
|
|
1
2
|
import { TypeInfo } from "./TypeInfo.cjs";
|
|
2
|
-
import { ExpressionCondition } from "./EntityFilter.cjs";
|
|
3
3
|
import { RawCondition } from "./QueryParser.cjs";
|
|
4
4
|
//#region src/Expr.d.ts
|
|
5
5
|
/**
|
|
@@ -59,16 +59,16 @@ type ExprLike<R> = {
|
|
|
59
59
|
*/
|
|
60
60
|
interface Expr<R, Src extends string = string> {
|
|
61
61
|
readonly [exprBrand]: ExprBrand<R, Src>;
|
|
62
|
-
eq(value: R | ExprLike<R> | undefined):
|
|
63
|
-
ne(value: R | ExprLike<R> | undefined):
|
|
64
|
-
gt(value: R | ExprLike<R> | undefined):
|
|
65
|
-
gte(value: R | ExprLike<R> | undefined):
|
|
66
|
-
lt(value: R | ExprLike<R> | undefined):
|
|
67
|
-
lte(value: R | ExprLike<R> | undefined):
|
|
68
|
-
in(values: readonly R[] | ExprLike<R | null> | undefined):
|
|
69
|
-
nin(values: readonly R[] | ExprLike<R | null> | undefined):
|
|
62
|
+
eq(value: R | ExprLike<R> | undefined): SqlCondition;
|
|
63
|
+
ne(value: R | ExprLike<R> | undefined): SqlCondition;
|
|
64
|
+
gt(value: R | ExprLike<R> | undefined): SqlCondition;
|
|
65
|
+
gte(value: R | ExprLike<R> | undefined): SqlCondition;
|
|
66
|
+
lt(value: R | ExprLike<R> | undefined): SqlCondition;
|
|
67
|
+
lte(value: R | ExprLike<R> | undefined): SqlCondition;
|
|
68
|
+
in(values: readonly R[] | ExprLike<R | null> | undefined): SqlCondition;
|
|
69
|
+
nin(values: readonly R[] | ExprLike<R | null> | undefined): SqlCondition;
|
|
70
70
|
/** Prefixes this expression and a space to a raw predicate; write .is`IS NULL`, not .is`NULL`. */
|
|
71
|
-
is(strings: TemplateStringsArray, ...values: unknown[]):
|
|
71
|
+
is(strings: TemplateStringsArray, ...values: unknown[]): SqlCondition;
|
|
72
72
|
/** `count(x)::int`; `count(a.id)` is `count(*)` for the FROM table, and the matched-row count for a left-joined one. */
|
|
73
73
|
count(): Expr<number, never>;
|
|
74
74
|
countDistinct(): Expr<number, never>;
|
|
@@ -83,7 +83,7 @@ interface Expr<R, Src extends string = string> {
|
|
|
83
83
|
}
|
|
84
84
|
/**
|
|
85
85
|
* A join entry: the join kind is the key, the joined source is the value, plus `on`. `inner?: never` /
|
|
86
|
-
* `left?: never` keep an entry to one kind (the `
|
|
86
|
+
* `left?: never` keep an entry to one kind (the `SqlCondition` `and`/`or` trick).
|
|
87
87
|
*
|
|
88
88
|
* `on` is required. A join is pruned when nothing references it anymore, not by an `undefined` ON;
|
|
89
89
|
* `keep: true` pins a join that would otherwise prune, i.e. an inner join used as an existence filter,
|
|
@@ -92,13 +92,13 @@ interface Expr<R, Src extends string = string> {
|
|
|
92
92
|
* Declared here (not `query.ts`) so the relation join factories in `Tables.ts` (i.e. `a.books.as(b)`) can
|
|
93
93
|
* return them without importing `query.ts`; `query.ts` re-constrains `A` to its `QuerySource`.
|
|
94
94
|
*/
|
|
95
|
-
interface InnerJoin<A, C =
|
|
95
|
+
interface InnerJoin<A, C = SqlCondition> {
|
|
96
96
|
readonly inner: A;
|
|
97
97
|
readonly left?: never;
|
|
98
98
|
readonly on: C;
|
|
99
99
|
readonly keep?: boolean;
|
|
100
100
|
}
|
|
101
|
-
interface LeftJoin<A, C =
|
|
101
|
+
interface LeftJoin<A, C = SqlCondition> {
|
|
102
102
|
readonly left: A;
|
|
103
103
|
readonly inner?: never;
|
|
104
104
|
readonly on: C;
|
|
@@ -118,7 +118,7 @@ interface ExprContext {
|
|
|
118
118
|
/** Returns the SQL alias for a table's `TableMgmt` or a subquery handle, searching enclosing queries. */
|
|
119
119
|
aliasFor(handle: object): string;
|
|
120
120
|
/** Turns a user-facing condition into SQL; `undefined` if it pruned away entirely. */
|
|
121
|
-
conditionToSql(cond:
|
|
121
|
+
conditionToSql(cond: SqlCondition): SqlFragment | undefined;
|
|
122
122
|
}
|
|
123
123
|
declare function isExpr(value: unknown): value is ExprLike<any>;
|
|
124
124
|
/** Every `Expr` is a `BaseExpr` at runtime; this cast keeps `isExpr` a plain type guard so unions narrow. */
|
|
@@ -128,11 +128,11 @@ declare const deferredSym: unique symbol;
|
|
|
128
128
|
* A condition whose SQL depends on aliases that are only known once the query is parsed, i.e.
|
|
129
129
|
* `bookStats.bookCount.gt(1)` or `bs.authorId.eq(a.id)`.
|
|
130
130
|
*
|
|
131
|
-
* It is shaped like a `RawCondition` so it can sit in any `
|
|
131
|
+
* It is shaped like a `RawCondition` with a SQL brand so it can sit in any `SqlCondition`; `resolveDeferredConditions`
|
|
132
132
|
* snapshots `condition`, `bindings`, and `aliases` before the filter is parsed. Domain alias conditions
|
|
133
133
|
* use a separate protocol in the `em.find` parser.
|
|
134
134
|
*/
|
|
135
|
-
interface DeferredCondition extends RawCondition {
|
|
135
|
+
interface DeferredCondition extends RawCondition, PredicateBrand<"sql"> {
|
|
136
136
|
[deferredSym]: (ctx: ExprContext) => RawCondition;
|
|
137
137
|
}
|
|
138
138
|
declare function isDeferredCondition(cond: unknown): cond is DeferredCondition;
|
|
@@ -142,7 +142,7 @@ declare function deferredCondition(fn: (ctx: ExprContext) => SqlFragment): Defer
|
|
|
142
142
|
* Resolve each condition occurrence and snapshot it before resolving the next one. A nested subquery
|
|
143
143
|
* may reuse the same condition under another alias; it must not overwrite this occurrence's SQL.
|
|
144
144
|
*/
|
|
145
|
-
declare function resolveDeferredConditions(cond:
|
|
145
|
+
declare function resolveDeferredConditions(cond: ConditionInput | undefined, ctx: ExprContext): ConditionInput | undefined;
|
|
146
146
|
/** Concatenates SQL fragments with `sep`, keeping bindings and refs in order. */
|
|
147
147
|
declare function joinFragments(parts: SqlFragment[], sep: string): SqlFragment;
|
|
148
148
|
/**
|
|
@@ -169,16 +169,16 @@ declare abstract class BaseExpr {
|
|
|
169
169
|
decode(value: unknown): unknown;
|
|
170
170
|
/** Converts a domain value into the database value, i.e. a tagged id into an int, for bindings. */
|
|
171
171
|
encode(value: unknown): unknown;
|
|
172
|
-
eq(value: unknown):
|
|
173
|
-
ne(value: unknown):
|
|
174
|
-
gt(value: unknown):
|
|
175
|
-
gte(value: unknown):
|
|
176
|
-
lt(value: unknown):
|
|
177
|
-
lte(value: unknown):
|
|
178
|
-
in(values: unknown):
|
|
179
|
-
nin(values: unknown):
|
|
172
|
+
eq(value: unknown): SqlCondition;
|
|
173
|
+
ne(value: unknown): SqlCondition;
|
|
174
|
+
gt(value: unknown): SqlCondition;
|
|
175
|
+
gte(value: unknown): SqlCondition;
|
|
176
|
+
lt(value: unknown): SqlCondition;
|
|
177
|
+
lte(value: unknown): SqlCondition;
|
|
178
|
+
in(values: unknown): SqlCondition;
|
|
179
|
+
nin(values: unknown): SqlCondition;
|
|
180
180
|
/** Prefixes this expression to a SQL template, retaining bindings and referenced aliases. */
|
|
181
|
-
is(strings: TemplateStringsArray, ...values: unknown[]):
|
|
181
|
+
is(strings: TemplateStringsArray, ...values: unknown[]): SqlCondition;
|
|
182
182
|
count(): Expr<number, never>;
|
|
183
183
|
countDistinct(): Expr<number, never>;
|
|
184
184
|
sum(): Expr<number | null, any>;
|
|
@@ -189,9 +189,9 @@ declare abstract class BaseExpr {
|
|
|
189
189
|
stringAgg(delimiter: string): Expr<string | null, any>;
|
|
190
190
|
coalesce(fallback: unknown): Expr<any, never>;
|
|
191
191
|
/** `this op value`, where `value` may be `undefined` (pruned), `null`, another expression, or a literal. */
|
|
192
|
-
protected compare(op: string, value: unknown):
|
|
192
|
+
protected compare(op: string, value: unknown): SqlCondition;
|
|
193
193
|
/** `this IN (subquery)` or `this = ANY(?)` for a list; `NOT IN` / `!= ALL(?)` for `nin`. */
|
|
194
|
-
protected inList(op: "IN" | "NOT IN", values: unknown):
|
|
194
|
+
protected inList(op: "IN" | "NOT IN", values: unknown): SqlCondition;
|
|
195
195
|
}
|
|
196
196
|
/**
|
|
197
197
|
* A SQL function applied to expressions, i.e. `count(a."id")::int` or `coalesce(bs."n", ?)`.
|
package/build/Expr.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Expr.d.cts","names":[],"sources":["../src/Expr.ts"],"mappings":";;;;;;;;;;;;;;;cAmBa;;;;;;;;;;;;;;;;;;;;;UAsBI,UAAU,GAAG;WACnB,UAAU;WACV,UAAU;;;;;;;;;;KAWT,SAAS;YAAiB,YAAY,UAAU;;;;;;;;;;UAU3C,KAAK,GAAG;YACb,YAAY,UAAU,GAAG;EACnC,GAAG,OAAO,IAAI,SAAS,iBAAiB;EACxC,GAAG,OAAO,IAAI,SAAS,iBAAiB;EACxC,GAAG,OAAO,IAAI,SAAS,iBAAiB;EACxC,IAAI,OAAO,IAAI,SAAS,iBAAiB;EACzC,GAAG,OAAO,IAAI,SAAS,iBAAiB;EACxC,IAAI,OAAO,IAAI,SAAS,iBAAiB;EAEzC,GAAG,iBAAiB,MAAM,SAAS,wBAAwB;EAC3D,IAAI,iBAAiB,MAAM,SAAS,wBAAwB;;EAG5D,GAAG,SAAS,yBAAyB,oBAAoB;;EAGzD,SAAS;EACT,iBAAiB;EACjB,IAAI,MAAM,oBAAoB,OAAO,oBAAoB;EACzD,IAAI,MAAM,oBAAoB,OAAO,oBAAoB;EACzD,OAAO,KAAK,UAAU;EACtB,OAAO,KAAK,UAAU;;EAEtB,YAAY,KAAK,YAAY;EAC7B,UAAU,MAAM,oBAAoB,MAAM,oBAAoB,oBAAoB;EAClF,SAAS,UAAU,YAAY,KAAK,KAAK,YAAY;;;;;;;;;;;;;UActC,UAAU,GAAG,IAAI;WACvB,OAAO;WACP;WACA,IAAI;WACJ;;UAGM,SAAS,GAAG,IAAI;WACtB,MAAM;WACN;WACA,IAAI;WACJ;;;UAIM;EACf;EACA;EACA;;;;;;UAOe;;EAEf,SAAS;;EAET,eAAe,MAAM,
|
|
1
|
+
{"version":3,"file":"Expr.d.cts","names":[],"sources":["../src/Expr.ts"],"mappings":";;;;;;;;;;;;;;;cAmBa;;;;;;;;;;;;;;;;;;;;;UAsBI,UAAU,GAAG;WACnB,UAAU;WACV,UAAU;;;;;;;;;;KAWT,SAAS;YAAiB,YAAY,UAAU;;;;;;;;;;UAU3C,KAAK,GAAG;YACb,YAAY,UAAU,GAAG;EACnC,GAAG,OAAO,IAAI,SAAS,iBAAiB;EACxC,GAAG,OAAO,IAAI,SAAS,iBAAiB;EACxC,GAAG,OAAO,IAAI,SAAS,iBAAiB;EACxC,IAAI,OAAO,IAAI,SAAS,iBAAiB;EACzC,GAAG,OAAO,IAAI,SAAS,iBAAiB;EACxC,IAAI,OAAO,IAAI,SAAS,iBAAiB;EAEzC,GAAG,iBAAiB,MAAM,SAAS,wBAAwB;EAC3D,IAAI,iBAAiB,MAAM,SAAS,wBAAwB;;EAG5D,GAAG,SAAS,yBAAyB,oBAAoB;;EAGzD,SAAS;EACT,iBAAiB;EACjB,IAAI,MAAM,oBAAoB,OAAO,oBAAoB;EACzD,IAAI,MAAM,oBAAoB,OAAO,oBAAoB;EACzD,OAAO,KAAK,UAAU;EACtB,OAAO,KAAK,UAAU;;EAEtB,YAAY,KAAK,YAAY;EAC7B,UAAU,MAAM,oBAAoB,MAAM,oBAAoB,oBAAoB;EAClF,SAAS,UAAU,YAAY,KAAK,KAAK,YAAY;;;;;;;;;;;;;UActC,UAAU,GAAG,IAAI;WACvB,OAAO;WACP;WACA,IAAI;WACJ;;UAGM,SAAS,GAAG,IAAI;WACtB,MAAM;WACN;WACA,IAAI;WACJ;;;UAIM;EACf;EACA;EACA;;;;;;UAOe;;EAEf,SAAS;;EAET,eAAe,MAAM,eAAe;;iBAGtB,OAAO,iBAAiB,SAAS;;iBAKjC,OAAO,MAAM,gBAAgB;cAIhC;;;;;;;;;UAUI,0BAA0B,cAAc;GACtD,eAAe,KAAK,gBAAgB;;iBAGvB,oBAAoB,gBAAgB,QAAQ;;iBAK5C,kBAAkB,KAAK,KAAK,gBAAgB,cAAc;;;;;iBAsB1D,0BACd,MAAM,4BACN,KAAK,cACJ;;iBAaa,cAAc,OAAO,eAAe,cAAc;;;;;;;uBAc5C;YACV;;WAGD,MAAM,KAAK,cAAc;;MAG9B,kBAAkB;;MAKlB,cAAc;;MAKd;;MAKA;;EAKJ,UAAU,KAAK,cAAc;;EAK7B,OAAO;;EAKP,OAAO;EAIP,GAAG,iBAAiB;EAIpB,GAAG,iBAAiB;EAIpB,GAAG,iBAAiB;EAIpB,IAAI,iBAAiB;EAIrB,GAAG,iBAAiB;EAIpB,IAAI,iBAAiB;EAIrB,GAAG,kBAAkB;EAIrB,IAAI,kBAAkB;;EAKtB,GAAG,SAAS,yBAAyB,oBAAoB;EAKzD,SAAS;EAST,iBAAiB;EAUjB,OAAO;EAQP,OAAO;EAQP,OAAO;EAOP,OAAO;EAOP,YAAY;EAUZ,UAAU,oBAAoB;EAO9B,SAAS,oBAAoB;;YAQnB,QAAQ,YAAY,iBAAiB;;YAmBrC,OAAO,qBAAqB,kBAAkB;;;;;;;;;cA6B7C,eAAe;UAEhB;UACA;UACA;EAHV,YACU,cACA,MAAM,YACN;IACN;IACA;IACA,UAAU;IACV,UAAU;IACV,aAAa;;MAMb,cAAc;MAId;EAmBJ,MAAM,KAAK,cAAc;EAQzB,OAAO;EAKP,OAAO;;;cAMI,oBAAoB;UACX;EAApB,YAAoB;MAIhB;EAIJ,SAAS;;;cAME,gBAAgB;UAEjB;UACA;EAFV,YACU,gBACA;MAKN;EAIJ,MAAM,KAAK,cAAc;;;;;;;;;;;;;cAkBd,qBAAqB;UAEtB;UACA;EAFV,YACU,SAAS,sBACT;EAKV,MAAM,KAAK,cAAc;;;iBAWX,mBAAmB,gBAAgB,KAAK,cAAc;;iBAWtD,gBAAgB"}
|
package/build/Expr.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { ConditionInput, PredicateBrand, SqlCondition } from "./conditions.mjs";
|
|
1
2
|
import { TypeInfo } from "./TypeInfo.mjs";
|
|
2
|
-
import { ExpressionCondition } from "./EntityFilter.mjs";
|
|
3
3
|
import { RawCondition } from "./QueryParser.mjs";
|
|
4
4
|
//#region src/Expr.d.ts
|
|
5
5
|
/**
|
|
@@ -59,16 +59,16 @@ type ExprLike<R> = {
|
|
|
59
59
|
*/
|
|
60
60
|
interface Expr<R, Src extends string = string> {
|
|
61
61
|
readonly [exprBrand]: ExprBrand<R, Src>;
|
|
62
|
-
eq(value: R | ExprLike<R> | undefined):
|
|
63
|
-
ne(value: R | ExprLike<R> | undefined):
|
|
64
|
-
gt(value: R | ExprLike<R> | undefined):
|
|
65
|
-
gte(value: R | ExprLike<R> | undefined):
|
|
66
|
-
lt(value: R | ExprLike<R> | undefined):
|
|
67
|
-
lte(value: R | ExprLike<R> | undefined):
|
|
68
|
-
in(values: readonly R[] | ExprLike<R | null> | undefined):
|
|
69
|
-
nin(values: readonly R[] | ExprLike<R | null> | undefined):
|
|
62
|
+
eq(value: R | ExprLike<R> | undefined): SqlCondition;
|
|
63
|
+
ne(value: R | ExprLike<R> | undefined): SqlCondition;
|
|
64
|
+
gt(value: R | ExprLike<R> | undefined): SqlCondition;
|
|
65
|
+
gte(value: R | ExprLike<R> | undefined): SqlCondition;
|
|
66
|
+
lt(value: R | ExprLike<R> | undefined): SqlCondition;
|
|
67
|
+
lte(value: R | ExprLike<R> | undefined): SqlCondition;
|
|
68
|
+
in(values: readonly R[] | ExprLike<R | null> | undefined): SqlCondition;
|
|
69
|
+
nin(values: readonly R[] | ExprLike<R | null> | undefined): SqlCondition;
|
|
70
70
|
/** Prefixes this expression and a space to a raw predicate; write .is`IS NULL`, not .is`NULL`. */
|
|
71
|
-
is(strings: TemplateStringsArray, ...values: unknown[]):
|
|
71
|
+
is(strings: TemplateStringsArray, ...values: unknown[]): SqlCondition;
|
|
72
72
|
/** `count(x)::int`; `count(a.id)` is `count(*)` for the FROM table, and the matched-row count for a left-joined one. */
|
|
73
73
|
count(): Expr<number, never>;
|
|
74
74
|
countDistinct(): Expr<number, never>;
|
|
@@ -83,7 +83,7 @@ interface Expr<R, Src extends string = string> {
|
|
|
83
83
|
}
|
|
84
84
|
/**
|
|
85
85
|
* A join entry: the join kind is the key, the joined source is the value, plus `on`. `inner?: never` /
|
|
86
|
-
* `left?: never` keep an entry to one kind (the `
|
|
86
|
+
* `left?: never` keep an entry to one kind (the `SqlCondition` `and`/`or` trick).
|
|
87
87
|
*
|
|
88
88
|
* `on` is required. A join is pruned when nothing references it anymore, not by an `undefined` ON;
|
|
89
89
|
* `keep: true` pins a join that would otherwise prune, i.e. an inner join used as an existence filter,
|
|
@@ -92,13 +92,13 @@ interface Expr<R, Src extends string = string> {
|
|
|
92
92
|
* Declared here (not `query.ts`) so the relation join factories in `Tables.ts` (i.e. `a.books.as(b)`) can
|
|
93
93
|
* return them without importing `query.ts`; `query.ts` re-constrains `A` to its `QuerySource`.
|
|
94
94
|
*/
|
|
95
|
-
interface InnerJoin<A, C =
|
|
95
|
+
interface InnerJoin<A, C = SqlCondition> {
|
|
96
96
|
readonly inner: A;
|
|
97
97
|
readonly left?: never;
|
|
98
98
|
readonly on: C;
|
|
99
99
|
readonly keep?: boolean;
|
|
100
100
|
}
|
|
101
|
-
interface LeftJoin<A, C =
|
|
101
|
+
interface LeftJoin<A, C = SqlCondition> {
|
|
102
102
|
readonly left: A;
|
|
103
103
|
readonly inner?: never;
|
|
104
104
|
readonly on: C;
|
|
@@ -118,7 +118,7 @@ interface ExprContext {
|
|
|
118
118
|
/** Returns the SQL alias for a table's `TableMgmt` or a subquery handle, searching enclosing queries. */
|
|
119
119
|
aliasFor(handle: object): string;
|
|
120
120
|
/** Turns a user-facing condition into SQL; `undefined` if it pruned away entirely. */
|
|
121
|
-
conditionToSql(cond:
|
|
121
|
+
conditionToSql(cond: SqlCondition): SqlFragment | undefined;
|
|
122
122
|
}
|
|
123
123
|
declare function isExpr(value: unknown): value is ExprLike<any>;
|
|
124
124
|
/** Every `Expr` is a `BaseExpr` at runtime; this cast keeps `isExpr` a plain type guard so unions narrow. */
|
|
@@ -128,11 +128,11 @@ declare const deferredSym: unique symbol;
|
|
|
128
128
|
* A condition whose SQL depends on aliases that are only known once the query is parsed, i.e.
|
|
129
129
|
* `bookStats.bookCount.gt(1)` or `bs.authorId.eq(a.id)`.
|
|
130
130
|
*
|
|
131
|
-
* It is shaped like a `RawCondition` so it can sit in any `
|
|
131
|
+
* It is shaped like a `RawCondition` with a SQL brand so it can sit in any `SqlCondition`; `resolveDeferredConditions`
|
|
132
132
|
* snapshots `condition`, `bindings`, and `aliases` before the filter is parsed. Domain alias conditions
|
|
133
133
|
* use a separate protocol in the `em.find` parser.
|
|
134
134
|
*/
|
|
135
|
-
interface DeferredCondition extends RawCondition {
|
|
135
|
+
interface DeferredCondition extends RawCondition, PredicateBrand<"sql"> {
|
|
136
136
|
[deferredSym]: (ctx: ExprContext) => RawCondition;
|
|
137
137
|
}
|
|
138
138
|
declare function isDeferredCondition(cond: unknown): cond is DeferredCondition;
|
|
@@ -142,7 +142,7 @@ declare function deferredCondition(fn: (ctx: ExprContext) => SqlFragment): Defer
|
|
|
142
142
|
* Resolve each condition occurrence and snapshot it before resolving the next one. A nested subquery
|
|
143
143
|
* may reuse the same condition under another alias; it must not overwrite this occurrence's SQL.
|
|
144
144
|
*/
|
|
145
|
-
declare function resolveDeferredConditions(cond:
|
|
145
|
+
declare function resolveDeferredConditions(cond: ConditionInput | undefined, ctx: ExprContext): ConditionInput | undefined;
|
|
146
146
|
/** Concatenates SQL fragments with `sep`, keeping bindings and refs in order. */
|
|
147
147
|
declare function joinFragments(parts: SqlFragment[], sep: string): SqlFragment;
|
|
148
148
|
/**
|
|
@@ -169,16 +169,16 @@ declare abstract class BaseExpr {
|
|
|
169
169
|
decode(value: unknown): unknown;
|
|
170
170
|
/** Converts a domain value into the database value, i.e. a tagged id into an int, for bindings. */
|
|
171
171
|
encode(value: unknown): unknown;
|
|
172
|
-
eq(value: unknown):
|
|
173
|
-
ne(value: unknown):
|
|
174
|
-
gt(value: unknown):
|
|
175
|
-
gte(value: unknown):
|
|
176
|
-
lt(value: unknown):
|
|
177
|
-
lte(value: unknown):
|
|
178
|
-
in(values: unknown):
|
|
179
|
-
nin(values: unknown):
|
|
172
|
+
eq(value: unknown): SqlCondition;
|
|
173
|
+
ne(value: unknown): SqlCondition;
|
|
174
|
+
gt(value: unknown): SqlCondition;
|
|
175
|
+
gte(value: unknown): SqlCondition;
|
|
176
|
+
lt(value: unknown): SqlCondition;
|
|
177
|
+
lte(value: unknown): SqlCondition;
|
|
178
|
+
in(values: unknown): SqlCondition;
|
|
179
|
+
nin(values: unknown): SqlCondition;
|
|
180
180
|
/** Prefixes this expression to a SQL template, retaining bindings and referenced aliases. */
|
|
181
|
-
is(strings: TemplateStringsArray, ...values: unknown[]):
|
|
181
|
+
is(strings: TemplateStringsArray, ...values: unknown[]): SqlCondition;
|
|
182
182
|
count(): Expr<number, never>;
|
|
183
183
|
countDistinct(): Expr<number, never>;
|
|
184
184
|
sum(): Expr<number | null, any>;
|
|
@@ -189,9 +189,9 @@ declare abstract class BaseExpr {
|
|
|
189
189
|
stringAgg(delimiter: string): Expr<string | null, any>;
|
|
190
190
|
coalesce(fallback: unknown): Expr<any, never>;
|
|
191
191
|
/** `this op value`, where `value` may be `undefined` (pruned), `null`, another expression, or a literal. */
|
|
192
|
-
protected compare(op: string, value: unknown):
|
|
192
|
+
protected compare(op: string, value: unknown): SqlCondition;
|
|
193
193
|
/** `this IN (subquery)` or `this = ANY(?)` for a list; `NOT IN` / `!= ALL(?)` for `nin`. */
|
|
194
|
-
protected inList(op: "IN" | "NOT IN", values: unknown):
|
|
194
|
+
protected inList(op: "IN" | "NOT IN", values: unknown): SqlCondition;
|
|
195
195
|
}
|
|
196
196
|
/**
|
|
197
197
|
* A SQL function applied to expressions, i.e. `count(a."id")::int` or `coalesce(bs."n", ?)`.
|