uql-orm 0.67.0 → 0.68.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/browser/uql-browser.min.js +2 -2
  2. package/dist/browser/uql-browser.min.js.map +3 -3
  3. package/dist/cockroachdb/cockroachDialect.d.ts +6 -4
  4. package/dist/cockroachdb/cockroachDialect.js +7 -3
  5. package/dist/d1/d1SqliteDialect.d.ts +1 -1
  6. package/dist/d1/d1SqliteDialect.js +1 -1
  7. package/dist/dialect/abstractSqlDialect.d.ts +107 -97
  8. package/dist/dialect/abstractSqlDialect.js +250 -293
  9. package/dist/dialect/hydrateColumn.js +33 -30
  10. package/dist/dialect/jsonSql.d.ts +32 -23
  11. package/dist/dialect/jsonSql.js +42 -31
  12. package/dist/dialect/mysqlLikeSqlDialect.d.ts +19 -23
  13. package/dist/dialect/mysqlLikeSqlDialect.js +34 -51
  14. package/dist/dialect/pgLikeSqlDialect.d.ts +24 -17
  15. package/dist/dialect/pgLikeSqlDialect.js +53 -26
  16. package/dist/dialect/vectorCast.d.ts +2 -0
  17. package/dist/dialect/vectorCast.js +25 -5
  18. package/dist/dialect/vectorSqlDialect.d.ts +8 -8
  19. package/dist/dialect/vectorSqlDialect.js +11 -11
  20. package/dist/index.d.ts +1 -0
  21. package/dist/maria/mariaDialect.d.ts +16 -11
  22. package/dist/maria/mariaDialect.js +23 -19
  23. package/dist/migrate/ddl/indexDdl.d.ts +3 -1
  24. package/dist/migrate/ddl/indexDdl.js +5 -1
  25. package/dist/migrate/ddl/mysqlIndexDdl.d.ts +7 -2
  26. package/dist/migrate/ddl/mysqlIndexDdl.js +28 -6
  27. package/dist/migrate/ddl/pgIndexDdl.d.ts +0 -9
  28. package/dist/migrate/ddl/pgIndexDdl.js +2 -6
  29. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +12 -1
  30. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +13 -3
  31. package/dist/migrate/introspection/mssqlIntrospector.d.ts +2 -9
  32. package/dist/migrate/introspection/mssqlIntrospector.js +2 -9
  33. package/dist/migrate/introspection/mysqlIntrospector.d.ts +2 -9
  34. package/dist/migrate/introspection/mysqlIntrospector.js +2 -8
  35. package/dist/mongo/mongoDialect.d.ts +22 -10
  36. package/dist/mongo/mongoDialect.js +86 -38
  37. package/dist/mssql/mssqlDialect.d.ts +22 -18
  38. package/dist/mssql/mssqlDialect.js +54 -43
  39. package/dist/mysql/mysqlDialect.d.ts +16 -1
  40. package/dist/mysql/mysqlDialect.js +18 -2
  41. package/dist/sqlite/sqliteDialect.d.ts +17 -16
  42. package/dist/sqlite/sqliteDialect.js +35 -39
  43. package/dist/type/dialect.d.ts +0 -4
  44. package/dist/type/entity.d.ts +8 -3
  45. package/dist/type/vector.d.ts +5 -7
  46. package/dist/util/dialect.util.d.ts +2 -0
  47. package/dist/util/dialect.util.js +6 -2
  48. package/dist/util/object.util.d.ts +2 -4
  49. package/dist/util/object.util.js +4 -9
  50. package/package.json +1 -1
  51. package/dist/dialect/jsonArrayElemMatchUtils.d.ts +0 -2
  52. package/dist/dialect/jsonArrayElemMatchUtils.js +0 -7
  53. package/dist/dialect/pgVectorMetrics.d.ts +0 -13
  54. package/dist/dialect/pgVectorMetrics.js +0 -17
  55. package/dist/maria/mariaVectorMetrics.d.ts +0 -8
  56. package/dist/maria/mariaVectorMetrics.js +0 -10
@@ -1,4 +1,4 @@
1
- var d=[];function a(e){for(let t of d)t(e)}function F(e){d.push(e);let t=d.length-1;return()=>{d.splice(t,1)}}class m extends Error{status;constructor(e,t){super(e);this.status=t;this.name="RequestError"}}function l(e,t){return i(e,{method:"get"},t)}function E(e,t,n){let r=JSON.stringify(t);return i(e,{method:"post",body:r},n)}function f(e,t,n){let r=JSON.stringify(t);return i(e,{method:"patch",body:r},n)}function h(e,t,n){let r=JSON.stringify(t);return i(e,{method:"put",body:r},n)}function R(e,t){return i(e,{method:"delete"},t)}function S(e,t,n){let r=JSON.stringify(t);return i(e,{method:"QUERY",body:r},n)}function i(e,t,n){if(a({phase:"start",opts:n}),t.headers={accept:"application/json","content-type":"application/json",...n?.headers},n?.signal)t.signal=n.signal;return fetch(e,t).then((r)=>r.json().then((o)=>{if(r.status>=200&&r.status<300)return a({phase:"success",opts:n}),o;let O=o,c={message:O?.error?.message??r.statusText,code:O?.error?.code??r.status};throw a({phase:"error",error:c,opts:n}),new m(c.message,c.code)})).finally(()=>{a({phase:"complete",opts:n})})}function p(e){return e?Object.keys(e):[]}function T(e){if(typeof e!=="object"||e===null)return!0;if(Array.isArray(e))return!1;let t=Object.getPrototypeOf(e);return t!==Object.prototype&&t!==null}function C(e){let t=e.charAt(0).toLowerCase();for(let n=1;n<e.length;++n)t+=e[n]===e[n].toUpperCase()?"-"+e[n].toLowerCase():e[n];return t}var s={findMany:{method:"GET",path:""},findOne:{method:"GET",path:"/one"},count:{method:"GET",path:"/count"},findOneById:{method:"GET",path:"/:id"},insertOne:{method:"POST",path:""},insertMany:{method:"POST",path:"/many"},saveOne:{method:"PUT",path:""},saveMany:{method:"PUT",path:"/many"},updateMany:{method:"PATCH",path:""},updateOneById:{method:"PATCH",path:"/:id"},deleteOneById:{method:"DELETE",path:"/:id"},deleteMany:{method:"DELETE",path:""}},j=p(s),N=new Map(j.filter((e)=>s[e].method==="GET"&&s[e].path!=="/:id").map((e)=>[s[e].path,e]));function b(e){return C(e.name)}var K=["$select","$populate","$exclude","$where","$sort"],x=["$count"],k=["$skip","$limit"],Q=["$candidates"],w=["$distinct"],W=["$lock",...x,...Q];var H=new Set([...K,...x,...k,...Q,...w,"hardDelete","count"]);function u(e){if(!e)return"";let t=new URLSearchParams;for(let r of p(e)){let o=e[r];if(o===void 0)continue;t.append(r,typeof o==="object"&&o!==null?JSON.stringify(o):String(o))}let n=t.toString();return n?`?${n}`:""}function g(e,t){if(!T(t))throw TypeError(`'${e.name}' was addressed by an id object, which the HTTP route cannot carry.`);return String(t)}class P{basePath;defaults;constructor(e,t={}){this.basePath=e;this.defaults=t}async findOneById(e,t,n,r){let o=this.getBasePath(e),y=u(n);return l(`${o}/${g(e,t)}${y}`,this.buildOptions(r))}findOne(e,t,n){return this.read(`${this.getBasePath(e)}${s.findOne.path}`,t,n)}findMany(e,t,n){let r={...t};if(n?.count)r.count=!0;return this.read(this.getBasePath(e),r,n)}async findManyAndCount(e,t,n){let r=await this.findMany(e,t,{...n,count:!0});if(typeof r.count!=="number")throw TypeError("findManyAndCount response has an invalid count");return{...r,count:r.count}}count(e,t,n){return this.read(`${this.getBasePath(e)}${s.count.path}`,t,n)}async exists(e,t,n){let r=await this.count(e,{...t,$limit:1},n);return{...r,data:r.data>0}}insertOne(e,t,n){let r=this.getBasePath(e);return E(r,t,this.buildOptions(n))}insertMany(e,t,n){let r=this.getBasePath(e);return E(`${r}${s.insertMany.path}`,t,this.buildOptions(n))}async updateOneById(e,t,n,r){let o=this.getBasePath(e);return f(`${o}/${g(e,t)}`,n,this.buildOptions(r))}updateMany(e,t,n,r){let o=this.getBasePath(e),y=u(t);return f(`${o}${y}`,n,this.buildOptions(r))}saveOne(e,t,n){let r=this.getBasePath(e);return h(r,t,this.buildOptions(n))}saveMany(e,t,n){let r=this.getBasePath(e);return h(`${r}${s.saveMany.path}`,t,this.buildOptions(n))}async deleteOneById(e,t,n={}){let r=this.getBasePath(e),o=n.hardDelete?u({hardDelete:n.hardDelete}):"";return R(`${r}/${g(e,t)}${o}`,this.buildOptions(n))}deleteMany(e,t,n={}){let r=this.getBasePath(e),o=u(n.hardDelete?{...t,hardDelete:n.hardDelete}:t);return R(`${r}${o}`,this.buildOptions(n))}getBasePath(e){return`${this.basePath}/${(this.defaults.entityPath??b)(e)}`}read(e,t,n){if(this.defaults.readMethod==="QUERY")return S(e,t??{},this.buildOptions(n));return l(`${e}${u(t)}`,this.buildOptions(n))}buildOptions(e){if(!this.defaults.headers&&!e?.headers)return e;return{...e,headers:{...this.defaults.headers,...e?.headers}}}}var U={getQuerier:()=>new P("/api")};function oe(e){U=e}function q(){return U}function se(){return q().getQuerier()}export{P as HttpQuerier,m as RequestError,l as get,se as getQuerier,q as getQuerierPool,a as notify,F as on,f as patch,E as post,h as put,S as query,R as remove,oe as setQuerierPool};
1
+ var d=[];function a(e){for(let t of d)t(e)}function F(e){d.push(e);let t=d.length-1;return()=>{d.splice(t,1)}}class m extends Error{status;constructor(e,t){super(e);this.status=t;this.name="RequestError"}}function l(e,t){return i(e,{method:"get"},t)}function E(e,t,n){let r=JSON.stringify(t);return i(e,{method:"post",body:r},n)}function f(e,t,n){let r=JSON.stringify(t);return i(e,{method:"patch",body:r},n)}function h(e,t,n){let r=JSON.stringify(t);return i(e,{method:"put",body:r},n)}function R(e,t){return i(e,{method:"delete"},t)}function O(e,t,n){let r=JSON.stringify(t);return i(e,{method:"QUERY",body:r},n)}function i(e,t,n){if(a({phase:"start",opts:n}),t.headers={accept:"application/json","content-type":"application/json",...n?.headers},n?.signal)t.signal=n.signal;return fetch(e,t).then((r)=>r.json().then((o)=>{if(r.status>=200&&r.status<300)return a({phase:"success",opts:n}),o;let S=o,c={message:S?.error?.message??r.statusText,code:S?.error?.code??r.status};throw a({phase:"error",error:c,opts:n}),new m(c.message,c.code)})).finally(()=>{a({phase:"complete",opts:n})})}function p(e){return e?Object.keys(e):[]}function T(e){if(typeof e!=="object"||e===null)return!0;if(Array.isArray(e))return!1;let t=Object.getPrototypeOf(e);return t!==Object.prototype&&t!==null}function C(e){let t=e.charAt(0).toLowerCase();for(let n=1;n<e.length;++n)t+=e[n]===e[n].toUpperCase()?"-"+e[n].toLowerCase():e[n];return t}var s={findMany:{method:"GET",path:""},findOne:{method:"GET",path:"/one"},count:{method:"GET",path:"/count"},findOneById:{method:"GET",path:"/:id"},insertOne:{method:"POST",path:""},insertMany:{method:"POST",path:"/many"},saveOne:{method:"PUT",path:""},saveMany:{method:"PUT",path:"/many"},updateMany:{method:"PATCH",path:""},updateOneById:{method:"PATCH",path:"/:id"},deleteOneById:{method:"DELETE",path:"/:id"},deleteMany:{method:"DELETE",path:""}},j=p(s),v=new Map(j.filter((e)=>s[e].method==="GET"&&s[e].path!=="/:id").map((e)=>[s[e].path,e]));function b(e){return C(e.name)}var K=["$select","$populate","$exclude","$where","$sort"],x=["$count"],k=["$skip","$limit"],Q=["$candidates"],U=["$distinct"],W=["$lock",...x,...Q];var H=new Set([...K,...x,...k,...Q,...U,"hardDelete","count"]);function u(e){if(!e)return"";let t=new URLSearchParams;for(let r of p(e)){let o=e[r];if(o===void 0)continue;t.append(r,typeof o==="object"&&o!==null?JSON.stringify(o):String(o))}let n=t.toString();return n?`?${n}`:""}function g(e,t){if(!T(t))throw TypeError(`'${e.name}' was addressed by an id object, which the HTTP route cannot carry.`);return String(t)}class P{basePath;defaults;constructor(e,t={}){this.basePath=e;this.defaults=t}async findOneById(e,t,n,r){let o=this.getBasePath(e),y=u(n);return l(`${o}/${g(e,t)}${y}`,this.buildOptions(r))}findOne(e,t,n){return this.read(`${this.getBasePath(e)}${s.findOne.path}`,t,n)}findMany(e,t,n){let r={...t};if(n?.count)r.count=!0;return this.read(this.getBasePath(e),r,n)}async findManyAndCount(e,t,n){let r=await this.findMany(e,t,{...n,count:!0});if(typeof r.count!=="number")throw TypeError("findManyAndCount response has an invalid count");return{...r,count:r.count}}count(e,t,n){return this.read(`${this.getBasePath(e)}${s.count.path}`,t,n)}async exists(e,t,n){let r=await this.count(e,{...t,$limit:1},n);return{...r,data:r.data>0}}insertOne(e,t,n){let r=this.getBasePath(e);return E(r,t,this.buildOptions(n))}insertMany(e,t,n){let r=this.getBasePath(e);return E(`${r}${s.insertMany.path}`,t,this.buildOptions(n))}async updateOneById(e,t,n,r){let o=this.getBasePath(e);return f(`${o}/${g(e,t)}`,n,this.buildOptions(r))}updateMany(e,t,n,r){let o=this.getBasePath(e),y=u(t);return f(`${o}${y}`,n,this.buildOptions(r))}saveOne(e,t,n){let r=this.getBasePath(e);return h(r,t,this.buildOptions(n))}saveMany(e,t,n){let r=this.getBasePath(e);return h(`${r}${s.saveMany.path}`,t,this.buildOptions(n))}async deleteOneById(e,t,n={}){let r=this.getBasePath(e),o=n.hardDelete?u({hardDelete:n.hardDelete}):"";return R(`${r}/${g(e,t)}${o}`,this.buildOptions(n))}deleteMany(e,t,n={}){let r=this.getBasePath(e),o=u(n.hardDelete?{...t,hardDelete:n.hardDelete}:t);return R(`${r}${o}`,this.buildOptions(n))}getBasePath(e){return`${this.basePath}/${(this.defaults.entityPath??b)(e)}`}read(e,t,n){if(this.defaults.readMethod==="QUERY")return O(e,t??{},this.buildOptions(n));return l(`${e}${u(t)}`,this.buildOptions(n))}buildOptions(e){if(!this.defaults.headers&&!e?.headers)return e;return{...e,headers:{...this.defaults.headers,...e?.headers}}}}var w={getQuerier:()=>new P("/api")};function oe(e){w=e}function q(){return w}function se(){return q().getQuerier()}export{P as HttpQuerier,m as RequestError,l as get,se as getQuerier,q as getQuerierPool,a as notify,F as on,f as patch,E as post,h as put,O as query,R as remove,oe as setQuerierPool};
2
2
 
3
- //# debugId=8A0DBFD4D82E137D64756E2164756E21
3
+ //# debugId=76282FBFF94FF89C64756E2164756E21
4
4
  //# sourceMappingURL=uql-browser.min.js.map
@@ -4,7 +4,7 @@
4
4
  "sourcesContent": [
5
5
  "import type { RequestCallback, RequestNotification } from '../type/index.js';\n\nconst subscriptors: RequestCallback[] = [];\n\nexport function notify(notification: RequestNotification): void {\n for (const subscriptor of subscriptors) {\n subscriptor(notification);\n }\n}\n\nexport function on(cb: RequestCallback): () => void {\n subscriptors.push(cb);\n const index = subscriptors.length - 1;\n return (): void => {\n subscriptors.splice(index, 1);\n };\n}\n",
6
6
  "import type { RequestErrorResponse } from '../../http/contract.js';\nimport type { RequestSuccessResponse } from '../../type/index.js';\nimport type { RequestOptions } from '../type/index.js';\nimport { notify } from './bus.js';\n\n/**\n * Error thrown for non-2xx responses. Carries the HTTP status so callers can key\n * behavior on it (401 redirects, 402 payment flows, error-boundary routing).\n */\nexport class RequestError extends Error {\n constructor(\n message: string,\n readonly status: number,\n ) {\n super(message);\n this.name = 'RequestError';\n }\n}\n\nexport function get<T>(url: string, opts?: RequestOptions) {\n return request<T>(url, { method: 'get' }, opts);\n}\n\nexport function post<T>(url: string, payload: unknown, opts?: RequestOptions) {\n const body = JSON.stringify(payload);\n return request<T>(url, { method: 'post', body }, opts);\n}\n\nexport function patch<T>(url: string, payload: unknown, opts?: RequestOptions) {\n const body = JSON.stringify(payload);\n return request<T>(url, { method: 'patch', body }, opts);\n}\n\nexport function put<T>(url: string, payload: unknown, opts?: RequestOptions) {\n const body = JSON.stringify(payload);\n return request<T>(url, { method: 'put', body }, opts);\n}\n\nexport function remove<T>(url: string, opts?: RequestOptions) {\n return request<T>(url, { method: 'delete' }, opts);\n}\n\n/**\n * HTTP QUERY (RFC 10008): a safe, idempotent read whose JSON query travels in the\n * request body, avoiding URL-length limits. Method name must stay uppercase\n * (fetch only normalizes the classic verbs).\n */\nexport function query<T>(url: string, payload: unknown, opts?: RequestOptions) {\n const body = JSON.stringify(payload);\n return request<T>(url, { method: 'QUERY', body }, opts);\n}\n\nfunction request<T>(url: string, init: RequestInit, opts?: RequestOptions) {\n notify({ phase: 'start', opts });\n\n init.headers = {\n accept: 'application/json',\n 'content-type': 'application/json',\n ...opts?.headers,\n };\n if (opts?.signal) {\n init.signal = opts.signal;\n }\n\n return fetch(url, init)\n .then((rawResp) =>\n rawResp.json().then((resp: unknown) => {\n const isSuccess = rawResp.status >= 200 && rawResp.status < 300;\n if (isSuccess) {\n notify({ phase: 'success', opts });\n return resp as RequestSuccessResponse<T>;\n }\n const errorResp = resp as Partial<RequestErrorResponse> | undefined;\n const error = {\n message: errorResp?.error?.message ?? rawResp.statusText,\n code: errorResp?.error?.code ?? rawResp.status,\n };\n notify({ phase: 'error', error, opts });\n throw new RequestError(error.message, error.code);\n }),\n )\n .finally(() => {\n notify({ phase: 'complete', opts });\n });\n}\n",
7
- "import type { EntityMeta, FieldKey, FieldOptions } from '../type/index.js';\n\nexport function throwPendingTransaction(): never {\n throw TypeError('pending transaction');\n}\n\nexport function throwNoPendingTransaction(): never {\n throw TypeError('not a pending transaction');\n}\n\nexport function clone<T>(value: T): T {\n if (typeof value !== 'object' || value === null) {\n return value;\n }\n if (Array.isArray(value)) {\n return value.map((it) => clone(it)) as T;\n }\n return { ...value };\n}\n\n/** Whether `obj` has at least one enumerable key. Narrows away `undefined`/`null` for callers. */\nexport function hasKeys<T>(obj: T): obj is NonNullable<T> {\n if (typeof obj !== 'object' || obj === null) return false;\n for (const _ in obj) return true;\n return false;\n}\n\n/**\n * Whether any enumerable key of `obj` satisfies `pred`, short-circuiting on the first match\n * without materializing a key array (unlike `Object.keys(obj).some(pred)`).\n */\nexport function someKey<T extends object>(obj: T, pred: (key: keyof T & string) => boolean): boolean {\n for (const key in obj) {\n if (pred(key)) return true;\n }\n return false;\n}\n\n/** Whether any enumerable value of `obj` satisfies `pred`, short-circuiting like {@link someKey}. */\nexport function someValue(obj: object, pred: (value: unknown) => boolean): boolean {\n return someKey(obj, (key) => pred((obj as Record<string, unknown>)[key]));\n}\n\nconst isOperatorKey = (key: string) => key.startsWith('$');\n\n/** Whether `value` is a non-empty object with an operator key (`$eq`, `$push`...): the one test every dialect classifies with. */\nexport function isOperatorObject(value: unknown): value is Record<string, unknown> {\n return hasKeys(value) && !Array.isArray(value) && someKey(value, isOperatorKey);\n}\n\n/** Whether every key of the non-empty object `value` is an operator (no plain field names mixed in). */\nexport function isOperatorOnlyObject(value: unknown): value is Record<string, unknown> {\n return hasKeys(value) && !Array.isArray(value) && !someKey(value, (key) => !isOperatorKey(key));\n}\n\n/** Whether `value` is an object that is not an array, whose keys can be read. */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nexport function getKeys<T extends object>(obj: T | null | undefined): (keyof T & string)[] {\n return obj ? (Object.keys(obj) as (keyof T & string)[]) : [];\n}\n\n/** The entries of `record` holding a value: a key declared but left `undefined` is no entry at all. */\nexport function definedEntries<K extends string, V>(record: Partial<Record<K, V>>): [K, V][] {\n return (Object.entries(record) as [K, V | undefined][]).filter((entry): entry is [K, V] => entry[1] !== undefined);\n}\n\n/**\n * The entity's own name, declared or its class's. `meta.name` holds only what the author wrote, so\n * the fallback is what an entity that named no table is called - which is why the sites spelling this\n * out reached for three different fallbacks, `?? ''` among them, and named nothing at all.\n */\nexport function entityName<E>(meta: EntityMeta<E>): string {\n return meta.name ?? meta.entity.name;\n}\n\nexport function getFieldKeys<E>(fields: {\n [K in FieldKey<E>]?: FieldOptions;\n}): FieldKey<E>[] {\n return getKeys(fields).filter((field) => fields[field]!.eager ?? true);\n}\n\n/**\n * Whether `value` addresses a row by itself rather than naming columns: every primitive, and the\n * object ids a driver deals in (`ObjectId`, `Date`, bytes). Only a plain object names columns, which\n * is what a `$where` map and a composite key's id object both are; an array is a list of either.\n */\nexport function isScalarId(value: unknown): boolean {\n if (typeof value !== 'object' || value === null) {\n return true;\n }\n if (Array.isArray(value)) {\n return false;\n }\n // `null` as well as `Object.prototype`: an object with no prototype is what a query-string parser\n // hands back (`qs`, express's `req.params`), and reading one as a bare id would name one column\n // with a map of several.\n const proto = Object.getPrototypeOf(value);\n return proto !== Object.prototype && proto !== null;\n}\n\n/** Whether `value` is a plain object naming columns, the one shape a `$where` takes. */\nexport function isWhereMap(value: unknown): value is Record<string, unknown> {\n return !Array.isArray(value) && !isScalarId(value);\n}\n",
7
+ "import type { EntityMeta, FieldKey, FieldOptions } from '../type/index.js';\n\nexport function throwPendingTransaction(): never {\n throw TypeError('pending transaction');\n}\n\nexport function throwNoPendingTransaction(): never {\n throw TypeError('not a pending transaction');\n}\n\nexport function clone<T>(value: T): T {\n if (typeof value !== 'object' || value === null) {\n return value;\n }\n if (Array.isArray(value)) {\n return value.map((it) => clone(it)) as T;\n }\n return { ...value };\n}\n\n/** Whether `obj` has at least one enumerable key. Narrows away `undefined`/`null` for callers. */\nexport function hasKeys<T>(obj: T): obj is NonNullable<T> {\n if (typeof obj !== 'object' || obj === null) return false;\n for (const _ in obj) return true;\n return false;\n}\n\n/**\n * Whether any enumerable key of `obj` satisfies `pred`, short-circuiting on the first match\n * without materializing a key array (unlike `Object.keys(obj).some(pred)`).\n */\nexport function someKey<T extends object>(obj: T, pred: (key: keyof T & string) => boolean): boolean {\n for (const key in obj) {\n if (pred(key)) return true;\n }\n return false;\n}\n\n/** Whether `key` names an operator (`$eq`, `$push`...) rather than a field. */\nexport function isOperatorKey(key: string): boolean {\n return key.startsWith('$');\n}\n\n/** Whether `value` is a non-empty object with an operator key (`$eq`, `$push`...): the one test every dialect classifies with. */\nexport function isOperatorObject(value: unknown): value is Record<string, unknown> {\n return isRecord(value) && someKey(value, isOperatorKey);\n}\n\n/** Whether `value` is an object that is not an array, whose keys can be read. */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nexport function getKeys<T extends object>(obj: T | null | undefined): (keyof T & string)[] {\n return obj ? (Object.keys(obj) as (keyof T & string)[]) : [];\n}\n\n/** The entries of `record` holding a value: a key declared but left `undefined` is no entry at all. */\nexport function definedEntries<K extends string, V>(record: Partial<Record<K, V>>): [K, V][] {\n return (Object.entries(record) as [K, V | undefined][]).filter((entry): entry is [K, V] => entry[1] !== undefined);\n}\n\n/**\n * The entity's own name, declared or its class's. `meta.name` holds only what the author wrote, so\n * the fallback is what an entity that named no table is called - which is why the sites spelling this\n * out reached for three different fallbacks, `?? ''` among them, and named nothing at all.\n */\nexport function entityName<E>(meta: EntityMeta<E>): string {\n return meta.name ?? meta.entity.name;\n}\n\nexport function getFieldKeys<E>(fields: {\n [K in FieldKey<E>]?: FieldOptions;\n}): FieldKey<E>[] {\n return getKeys(fields).filter((field) => fields[field]!.eager ?? true);\n}\n\n/**\n * Whether `value` addresses a row by itself rather than naming columns: every primitive, and the\n * object ids a driver deals in (`ObjectId`, `Date`, bytes). Only a plain object names columns, which\n * is what a `$where` map and a composite key's id object both are; an array is a list of either.\n */\nexport function isScalarId(value: unknown): boolean {\n if (typeof value !== 'object' || value === null) {\n return true;\n }\n if (Array.isArray(value)) {\n return false;\n }\n // `null` as well as `Object.prototype`: an object with no prototype is what a query-string parser\n // hands back (`qs`, express's `req.params`), and reading one as a bare id would name one column\n // with a map of several.\n const proto = Object.getPrototypeOf(value);\n return proto !== Object.prototype && proto !== null;\n}\n\n/** Whether `value` is a plain object naming columns, the one shape a `$where` takes. */\nexport function isWhereMap(value: unknown): value is Record<string, unknown> {\n return !Array.isArray(value) && !isScalarId(value);\n}\n",
8
8
  "export function kebabCase(val: string): string {\n let resp = val.charAt(0).toLowerCase();\n for (let i = 1; i < val.length; ++i) {\n resp += val[i] === val[i].toUpperCase() ? '-' + val[i].toLowerCase() : val[i];\n }\n return resp;\n}\n\nexport function upperFirst(text: string): string {\n return text.charAt(0).toUpperCase() + text.slice(1);\n}\n\nexport function lowerFirst(text: string): string {\n return text.charAt(0).toLowerCase() + text.slice(1);\n}\n\nexport function snakeCase(val: string): string {\n if (!val) return '';\n let resp = val.charAt(0).toLowerCase();\n for (let i = 1; i < val.length; ++i) {\n const char = val[i];\n const charLower = char.toLowerCase();\n if (char !== charLower && char === char.toUpperCase()) {\n resp += '_' + charLower;\n } else {\n resp += char;\n }\n }\n return resp;\n}\n\n/**\n * Convert a string to PascalCase (UpperCamelCase).\n * @example 'user_profile' -> 'UserProfile'\n * @example 'some-text' -> 'SomeText'\n */\nexport function pascalCase(str: string): string {\n if (!str) return '';\n return str\n .split(/[_\\s-]+/)\n .map((word) => {\n // Lower-casing the rest is only right for a word that carries no case of its own: it turns\n // `USER_ID` into `UserId`, but it also turns `tenantId` into `Tenantid`.\n const rest = word === word.toUpperCase() ? word.slice(1).toLowerCase() : word.slice(1);\n return word.charAt(0).toUpperCase() + rest;\n })\n .join('');\n}\n\n/**\n * Convert a string to camelCase.\n * @example 'user_profile' -> 'userProfile'\n * @example 'SomeText' -> 'someText'\n */\nexport function camelCase(str: string): string {\n const pascal = pascalCase(str);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\n/**\n * Simple singularize function for English words.\n * @example 'users' -> 'user'\n * @example 'categories' -> 'category'\n */\nexport function singularize(name: string): string {\n if (!name) return '';\n if (name.endsWith('ies')) {\n return name.slice(0, -3) + 'y';\n }\n if (name.endsWith('ses') || name.endsWith('xes') || name.endsWith('zes')) {\n return name.slice(0, -2);\n }\n if (name.endsWith('s') && !name.endsWith('ss')) {\n return name.slice(0, -1);\n }\n return name;\n}\n\n/**\n * Simple pluralize function for English words.\n * @example 'user' -> 'users'\n * @example 'category' -> 'categories'\n */\nexport function pluralize(name: string): string {\n if (!name) return '';\n if (name.endsWith('y') && name.length > 1 && !/[aeiou]/.test(name[name.length - 2])) {\n return name.slice(0, -1) + 'ies';\n }\n if (name.endsWith('s') || name.endsWith('x') || name.endsWith('z') || name.endsWith('ch') || name.endsWith('sh')) {\n return name + 'es';\n }\n return name + 's';\n}\n",
9
9
  "import { type QueryErrorKind, queryErrorKind } from '../querier/queryError.js';\nimport type { Type, UniversalQuerier } from '../type/index.js';\n// the specific util modules, not the barrel, so the browser bundle does not pull in entity metadata\nimport { getKeys } from '../util/object.util.js';\nimport { kebabCase } from '../util/string.util.js';\n\ntype RouteShape = {\n readonly method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n readonly path: '' | `/${string}`;\n};\n\n/**\n * Single source of truth for the CRUD-over-HTTP surface, shared by server adapters and the browser client.\n * Keys are constrained to {@link UniversalQuerier} method names, so renaming a querier method\n * (or routing a non-existent one) is a compile error.\n */\nexport const CRUD_ROUTES = {\n findMany: { method: 'GET', path: '' },\n findOne: { method: 'GET', path: '/one' },\n count: { method: 'GET', path: '/count' },\n findOneById: { method: 'GET', path: '/:id' },\n insertOne: { method: 'POST', path: '' },\n insertMany: { method: 'POST', path: '/many' },\n saveOne: { method: 'PUT', path: '' },\n saveMany: { method: 'PUT', path: '/many' },\n updateMany: { method: 'PATCH', path: '' },\n updateOneById: { method: 'PATCH', path: '/:id' },\n deleteOneById: { method: 'DELETE', path: '/:id' },\n deleteMany: { method: 'DELETE', path: '' },\n} as const satisfies Partial<Record<keyof UniversalQuerier, RouteShape>>;\n\nexport type CrudOperation = keyof typeof CRUD_ROUTES;\n\nexport type CrudRoute = (typeof CRUD_ROUTES)[CrudOperation];\n\n/**\n * `QUERY` (RFC 10008) is an alternate transport for the read operations: same semantics as the\n * GET routes, but the JSON query travels in the request body instead of the query string,\n * avoiding URL-length limits for large queries.\n */\nexport type HttpMethod = CrudRoute['method'] | 'QUERY';\n\nconst CRUD_OPS = getKeys(CRUD_ROUTES);\n\n// derived from CRUD_ROUTES (the literal-path GET routes) so the sub-paths live in exactly one place\nconst QUERY_READ_OPS: ReadonlyMap<string, CrudOperation> = new Map(\n CRUD_OPS.filter((op) => CRUD_ROUTES[op].method === 'GET' && CRUD_ROUTES[op].path !== '/:id').map((op) => [\n CRUD_ROUTES[op].path,\n op,\n ]),\n);\n\n/**\n * URL segment for an entity, e.g. `entityPath(UserProfile) === 'user-profile'`.\n */\nexport function entityPath<E>(entity: Type<E>): string {\n return kebabCase(entity.name);\n}\n\nexport type RouteMatch = {\n readonly op: CrudOperation;\n /**\n * the resolved transport method - differs from the op's canonical route method for QUERY.\n */\n readonly method: HttpMethod;\n readonly id?: string;\n};\n\n/**\n * Resolve a (method, sub-path) pair to a CRUD operation. Literal sub-paths win over `:id`.\n */\nexport function matchRoute(method: string, subPath: string | undefined): RouteMatch | undefined {\n const raw = method.toUpperCase();\n const literal = subPath === undefined ? '' : `/${subPath}`;\n if (raw === 'QUERY') {\n const op = QUERY_READ_OPS.get(literal);\n return op ? { op, method: 'QUERY' } : undefined;\n }\n // HEAD reads like GET per HTTP semantics; the server runtime omits the response body\n const verb = raw === 'HEAD' ? 'GET' : raw;\n let idOp: CrudOperation | undefined;\n for (const op of CRUD_OPS) {\n const route = CRUD_ROUTES[op];\n if (route.method !== verb) {\n continue;\n }\n if (route.path === literal) {\n return { op, method: route.method };\n }\n if (route.path === '/:id') {\n idOp = op;\n }\n }\n return idOp && subPath !== undefined ? { op: idOp, method: CRUD_ROUTES[idOp].method, id: subPath } : undefined;\n}\n\nexport type RequestErrorResponse = {\n readonly error: {\n readonly message: string;\n readonly code: number;\n };\n};\n\n/** Generic on purpose: a driver's constraint message names tables and constraints, and Postgres echoes the value. */\nconst CONSTRAINT_ERRORS: ReadonlyMap<QueryErrorKind | undefined, RequestErrorResponse['error']> = new Map([\n ['uniqueViolation', { message: 'Conflict', code: 409 }],\n ['foreignKeyViolation', { message: 'Conflict', code: 409 }],\n ['notNullViolation', { message: 'Bad Request', code: 400 }],\n ['checkViolation', { message: 'Bad Request', code: 400 }],\n]);\n\n/**\n * Map a thrown error to the wire error envelope. Honors a numeric `status` on the error\n * (e.g. hooks throwing 403), then a constraint violation (409/400), defaults to 500; `code` mirrors the HTTP status.\n */\nexport function toErrorResponse(err: unknown): { status: number; body: RequestErrorResponse } {\n const error =\n err instanceof Error && 'status' in err && typeof err.status === 'number'\n ? { message: err.message, code: err.status }\n : (CONSTRAINT_ERRORS.get(queryErrorKind(err)) ?? {\n message: err instanceof Error ? err.message : 'Internal Server Error',\n code: 500,\n });\n return { status: error.code, body: { error } };\n}\n",
10
10
  "import type { FieldKey, JsonFieldPaths, RelationKey, RelationTarget, WrittenId } from './entity.js';\nimport type { QueryLock } from './queryLock.js';\nimport type { QueryRaw } from './queryRaw.js';\nimport type { QueryWhere } from './queryWhere.js';\nimport type { BooleanLike, Except, IsMany, PrimaryKey } from './utility.js';\nimport type { QueryVectorSearch } from './vector.js';\n\nexport type QueryOptions = {\n /**\n * Toggle named entity filters for this query. `false` disables all filters;\n * `{ softDelete: false }` disables one; `{ myFilter: true }` force-enables a `default: false` filter.\n * Security filters cannot be disabled here.\n */\n filters?: false | Record<string, boolean>;\n /**\n * Delete only: physically remove rows instead of soft-deleting, ignoring the soft-delete filter so\n * already-deleted rows are removed too. No effect on entities without a soft-delete field.\n */\n hardDelete?: boolean;\n /**\n * prefix the query with this.\n */\n prefix?: string;\n /**\n * automatically infer the prefix for the query.\n */\n autoPrefix?: boolean;\n};\n\n/**\n * Field selection - `{ name: true }` whitelists fields; relations go in `$populate`. Declared over\n * `F extends keyof E`, like every map keyed by an entity's members, so each key stays linked to its\n * property and an editor rename reaches it. `F` is also how a projection passes its captured key set.\n */\nexport type QuerySelect<E, F extends keyof E = FieldKey<E>, V = BooleanLike> = {\n [K in F]?: V;\n};\n\n/**\n * Accepted `$select` value: a field map, or raw SQL projections built with `raw()`\n * (e.g. ``[raw`*`, raw`LOG10(points)`.as('score')]``). The raw form is SQL-only.\n */\nexport type QuerySelectValue<E> = QuerySelect<E> | readonly QueryRaw[];\n\n/**\n * Fields to exclude from the query result - `{ name: true }` blacklists fields.\n * Mutually exclusive with positive field selections in `$select`.\n */\nexport type QueryExclude<E> = QuerySelect<E>;\n\n/**\n * relation population map.\n */\nexport type QueryPopulate<E, R extends keyof E = RelationKey<E>> = {\n [K in R]?: BooleanLike | QueryPopulateRelationOptions<E[K]>;\n};\n\n/**\n * The key a read carries its relation tallies under. One spelling for the type and the runtime that\n * fills it: they sit in different modules, so a drift would type-check and answer `undefined`.\n */\nexport const COUNT_RESULT_KEY = '_count';\n\n/**\n * How many rows each named relation holds per parent, `true` for all of them or a filter to narrow\n * which ones count: a correlated count in the read's own statement, so no related row is loaded. Comes\n * back under `_count`, which keeps it clear of a relation of the same name `$populate` filled.\n */\nexport type QueryCount<E, R extends keyof E = ToManyRelationKey<E>> = {\n [K in R]?: BooleanLike | QueryFilter<RelationTarget<E[K]>>;\n};\n\n/**\n * query conflict paths - subset of field keys used to detect upsert conflicts.\n */\nexport type QueryConflictPaths<E> = QuerySelect<E, FieldKey<E>, true>;\n\n/**\n * Options to populate a relation declared as `V`, by its cardinality.\n */\nexport type QueryPopulateRelationOptions<V> =\n IsMany<V> extends true ? RelationQuery<RelationTarget<V>> : QueryUnique<RelationTarget<V>> & { $required?: boolean };\n\n/**\n * The per-request context parameterized filters read, set with `withContext(ctx, cb)`. An interface,\n * so its keys can be typed once: `declare module 'uql-orm' { interface UqlContext { tenantId: number } }`.\n */\nexport interface UqlContext {\n [key: string]: unknown;\n}\n\n/**\n * A filter's `$where` fragment: a plain fragment, or a function of the ambient {@link UqlContext}.\n * Return `undefined` when the condition can't resolve (see {@link FilterOptions.onMissing}).\n */\nexport type FilterWhere<E> = QueryWhere<E> | ((context: UqlContext | undefined) => QueryWhere<E> | undefined);\n\n/**\n * What to do when a filter's condition returns `undefined`. `skip` omits it (convenience filters);\n * `throw` fails closed (the default for `security` filters).\n */\nexport type FilterOnMissing = 'skip' | 'throw';\n\n/**\n * Authoring shape for `@Entity({ filters })` / `@Filter` / `defineFilter`.\n */\nexport type FilterOptions<E = unknown> = {\n readonly where: FilterWhere<E>;\n /** Applied to every query unless bypassed via `QueryOptions.filters`. Defaults to `true`. */\n readonly default?: boolean;\n} & (\n | {\n readonly security?: false;\n /** What to do when {@link FilterOptions.where} returns `undefined`. Defaults to `skip`. */\n readonly onMissing?: FilterOnMissing;\n }\n | {\n /**\n * Row-level-security filter: always applied (ignores `QueryOptions.filters` bypass) and\n * AND-merged so a client `$where` on the same field can't override it. It fails closed.\n */\n readonly security: true;\n readonly onMissing?: 'throw';\n }\n);\n\n/**\n * direction for the sort.\n */\nexport type QuerySortDirection = -1 | 1 | 'asc' | 'desc';\n\n/**\n * Accepted value for a field in `$sort` - either a direction or a vector similarity search.\n */\nexport type QuerySortValue = QuerySortDirection | QueryVectorSearch;\n\n/**\n * To-one relations only: a parent holds many rows of a to-many, so there is no single value to order\n * it by, and joining one in would duplicate the parent instead. Order those inside `$populate`.\n */\ntype ToOneRelationKey<E> = { [K in RelationKey<E>]: IsMany<E[K]> extends true ? never : K }[RelationKey<E>];\n\n/** The relation names a parent holds many rows of, which a populated query fills with a list. */\ntype ToManyRelationKey<E> = Exclude<RelationKey<E>, ToOneRelationKey<E>>;\n\n/**\n * Ordering parents by how many rows a to-many relation holds - \"the ten users with the most posts\".\n * The tally is computed per parent as a correlated count, never by loading the rows.\n */\nexport type QuerySortByCount = {\n $count: QuerySortDirection;\n};\n\n/**\n * A sort by fields, JSON paths, a to-one relation's fields, a to-many's `$count`, or a vector distance,\n * which `Vector` confines to the queried entity. One mapped type over the key sets: an intersection is\n * checked once per member, which made this the costliest type to check.\n */\nexport type QuerySortMap<E, Vector extends boolean = true, K extends keyof E = FieldKey<E> | RelationKey<E>> = {\n [P in K]?: P extends RelationKey<E>\n ? // A to-many has no single value to order by, so what it offers instead is its own size.\n IsMany<E[P]> extends true\n ? QuerySortByCount\n : QuerySortMap<RelationTarget<E[P]>, false>\n : Vector extends true\n ? NonNullable<E[P]> extends readonly number[]\n ? QuerySortValue\n : QuerySortDirection\n : QuerySortDirection;\n} & ([JsonFieldPaths<E>] extends [never] ? unknown : { [P in JsonFieldPaths<E>]?: QuerySortDirection });\n\n/**\n * pager options.\n */\nexport type QueryPager = {\n /**\n * Index from where start the search\n */\n $skip?: number;\n\n /**\n * Max number of records to retrieve\n */\n $limit?: number;\n};\n\n/**\n * Which rows a statement addresses.\n */\nexport type QueryFilter<E> = {\n /**\n * filtering options.\n */\n $where?: QueryWhere<E>;\n};\n\n/**\n * A filter plus the page `count` takes. No `$sort`: ordering picks *which* rows a page holds, never\n * how many, so a count that accepted one would promise an influence it cannot have.\n */\nexport type QueryPage<E> = QueryFilter<E> & QueryPager;\n\n/**\n * A filter plus the ordering and page `updateMany`/`deleteMany` take. Both settle the\n * rows they address with a SELECT first, so the page is portable rather than MySQL-only, and a\n * vector `$sort` is as valid here as on a read: it ranks the settle query's rows, which has the\n * projection list to hold the distance. `$lock` stays off these, declared on {@link Query} instead.\n */\nexport type QuerySearch<E> = QueryPage<E> & {\n /**\n * sorting options.\n */\n $sort?: QuerySortMap<E>;\n};\n\n/**\n * query options.\n */\nexport type Query<E> = {\n /**\n * field selection - `{ name: true }` whitelists fields, or raw SQL projections\n * (``[raw`LOG10(points)`.as('score')]``, SQL dialects only - MongoDB rejects the raw-array form).\n * Mutually exclusive with `$exclude`.\n */\n $select?: QuerySelectValue<E>;\n\n /**\n * relation population options.\n */\n $populate?: QueryPopulate<E>;\n\n /**\n * how many rows each named relation holds, under `_count` on every row. See {@link QueryCount}.\n */\n $count?: QueryCount<E>;\n\n /**\n * field exclusion - `{ name: true }` blacklists fields. Mutually exclusive with positive `$select`.\n * Keys a relation is assembled from (a joined row's primary key, a to-many's foreign key) are kept\n * regardless, since subtracting them would leave the relation unfilled.\n */\n $exclude?: QueryExclude<E>;\n\n /**\n * sorting options, vector similarity search included: a SELECT is the one statement with a\n * projection list to hold the distance such a search computes.\n */\n $sort?: QuerySortMap<E>;\n\n /**\n * whether to return only distinct rows.\n */\n $distinct?: boolean;\n\n /**\n * Lock the rows this query returns, `SELECT ... FOR UPDATE`, inside an open transaction: outside one\n * it is refused, since the lock would drop before the rows are used. SQL only, and not the SQLite family.\n */\n $lock?: QueryLock;\n\n /**\n * How many candidates an ANN index explores before ranking a vector search, in that index's own units\n * (`hnsw.ef_search`, `numCandidates`...); ignored where the search is exact. Postgres needs a transaction.\n */\n $candidates?: number;\n\n // `$where`, `$skip` and `$limit` are declared here rather than intersected in from\n // {@link QueryFilter} and {@link QueryPager}: an assignability check against an intersection is\n // repeated per constituent, and every query in a consuming codebase pays that. The two shapes are\n // pinned together in `queryStatementClauses.test-d.ts` so the copies cannot drift.\n\n /**\n * filtering options.\n */\n $where?: QueryWhere<E>;\n\n /**\n * Index from where start the search\n */\n $skip?: number;\n\n /**\n * Max number of records to retrieve\n */\n $limit?: number;\n};\n\n/**\n * `Query`'s clauses grouped by the shape of their value, for the wire parser and the relation query\n * check alike; `satisfies` keeps them in step with `Query`.\n */\nexport const QUERY_OBJECT_CLAUSES = [\n '$select',\n '$populate',\n '$exclude',\n '$where',\n '$sort',\n] as const satisfies readonly (keyof Query<unknown>)[];\n\n/**\n * Object clauses only the statement itself takes: a populated relation's rows keep their declared type,\n * so a `$count` inside one would have no `_count` to land in.\n */\nexport const QUERY_ROOT_OBJECT_CLAUSES = ['$count'] as const satisfies readonly (keyof Query<unknown>)[];\n\nexport const QUERY_NUMBER_CLAUSES = ['$skip', '$limit'] as const satisfies readonly (keyof Query<unknown>)[];\n\n/**\n * Number clauses only the statement itself takes - the numeric mirror of {@link QUERY_ROOT_OBJECT_CLAUSES}.\n * `$candidates` tunes the index behind a vector search, and a vector search only ever ranks the rows\n * the statement returns, so a relation's own query has nothing to tune.\n */\nexport const QUERY_ROOT_NUMBER_CLAUSES = ['$candidates'] as const satisfies readonly (keyof Query<unknown>)[];\n\nexport const QUERY_BOOLEAN_CLAUSES = ['$distinct'] as const satisfies readonly (keyof Query<unknown>)[];\n\n/** The clauses that describe the statement, which a populated relation's own query refuses by name. */\nexport const QUERY_STATEMENT_CLAUSES = [\n '$lock',\n ...QUERY_ROOT_OBJECT_CLAUSES,\n ...QUERY_ROOT_NUMBER_CLAUSES,\n] as const satisfies readonly (keyof Query<unknown>)[];\n\ntype RelationClause = (\n | typeof QUERY_OBJECT_CLAUSES\n | typeof QUERY_NUMBER_CLAUSES\n | typeof QUERY_BOOLEAN_CLAUSES\n)[number];\n\n/**\n * A populated relation's own query: the clause groups its runtime check accepts, so the two cannot\n * drift, and a clause added to {@link Query} stays off it until it joins one of them.\n */\nexport type RelationQuery<E = object> = Pick<Query<E>, RelationClause> & {\n $required?: boolean;\n};\n\n/**\n * options to get a single record.\n */\nexport type QueryOne<E> = Except<Query<E>, '$limit'>;\n\n/**\n * options to get an unique record.\n */\nexport type QueryUnique<E> = Pick<QueryOne<E>, '$select' | '$exclude' | '$populate' | '$where'>;\n\n/**\n * The clauses that shape a row, captured as key sets rather than maps: a naked type parameter skips\n * excess-property checks, while a key set fails its own constraint on a typo.\n * @internal\n */\ntype QueryProjection<\n E,\n S extends FieldKey<E>,\n V,\n X extends FieldKey<E>,\n P extends RelationKey<E>,\n C extends RelationKey<E>,\n> = {\n $select?: QuerySelect<E, S, V> | readonly QueryRaw[];\n $exclude?: QuerySelect<E, X, V>;\n $populate?: QueryPopulate<E, P>;\n // Narrowing the captured names to the to-many ones leaves a to-one relation no key here at all,\n // so counting one is an excess property rather than a value to check.\n $count?: QueryCount<E, C & ToManyRelationKey<E>>;\n};\n\n/**\n * A {@link Query} whose projection is captured, so {@link QueryFindResult} can shape the row.\n */\nexport type QueryProjected<\n E,\n S extends FieldKey<E>,\n V,\n X extends FieldKey<E>,\n P extends RelationKey<E>,\n C extends RelationKey<E> = never,\n> = Query<E> & QueryProjection<E, S, V, X, P, C>;\n\n/**\n * A {@link QueryOne} whose projection is captured, so {@link QueryFindResult} can shape the row.\n */\nexport type QueryOneProjected<\n E,\n S extends FieldKey<E>,\n V,\n X extends FieldKey<E>,\n P extends RelationKey<E>,\n C extends RelationKey<E> = never,\n> = QueryOne<E> & QueryProjection<E, S, V, X, P, C>;\n\n/**\n * The keys a query comes back with, as the runtime projects them: a positive `$select`'s, or every\n * field minus what `$select` or `$exclude` subtracts, plus the populated relations.\n * @internal\n */\ntype ProjectedKeys<E, S, V, X, P> =\n | ([V] extends [false | 0] ? Exclude<FieldKey<E>, S> : [S] extends [never] ? Exclude<FieldKey<E>, X> : S)\n | P;\n\n/**\n * Whether every entry of the captured map says the same thing: all selected, or all subtracted.\n * @internal\n */\ntype IsUniform<V> = [V] extends [true | 1] ? true : [V] extends [false | 0] ? true : false;\n\n/**\n * A find's row: the entity narrowed to what the query projected and populated, so reading anything\n * else does not compile. The entity itself where the projection is raw, absent or not uniform.\n * @example `QueryFindResult<User, 'id' | 'name'>`\n */\nexport type QueryFindResult<\n E,\n S extends FieldKey<E> = never,\n // A whitelist by default, so the hand-written form reads `QueryFindResult<User, 'id' | 'name'>`.\n V = true,\n X extends FieldKey<E> = never,\n P extends RelationKey<E> = never,\n C extends RelationKey<E> = never,\n> = QueryProjectedRow<E, S, V, X, P, C> & CountedRelations<C>;\n\n/**\n * The `_count` a query asked for, or an inert intersection member when it asked for none - so a read\n * without `$count` keeps exactly the row type it had.\n */\ntype CountedRelations<C extends PropertyKey> = [C] extends [never]\n ? unknown\n : { [K in typeof COUNT_RESULT_KEY]: { [R in C]: number } };\n\n/** @internal */\ntype QueryProjectedRow<\n E,\n S extends FieldKey<E>,\n V,\n X extends FieldKey<E>,\n P extends RelationKey<E>,\n C extends RelationKey<E>,\n> = [S | X] extends [never]\n ? E\n : IsUniform<V> extends true\n ? [PopulatedToMany<E, P>] extends [never]\n ? // `Pick`, not a key remap: an entity keyed by an index signature - a content type defined at\n // runtime - has `string` for its keys, and a remap keeps no literal one, so every projection\n // over one came back as `{}`.\n Pick<E, ProjectedKeys<E, S, V, X, P> & keyof E>\n : // A populated to-many is always a list, empty where the parent has no children, so it maps\n // and counts without a guard. Only that promotion needs a second member, and only a query\n // that populates one pays for it; every other key keeps the modifier the entity declared,\n // a to-one relation included, since a join that finds no row leaves it absent.\n Pick<E, Exclude<ProjectedKeys<E, S, V, X, P>, PopulatedToMany<E, P>> & keyof E> & {\n [K in PopulatedToMany<E, P>]-?: NonNullable<E[K]>;\n }\n : E;\n\n/** The to-many relations a query populated, which come back as lists rather than as optional ones. */\ntype PopulatedToMany<E, P> = Extract<P, ToManyRelationKey<E>>;\n\n/**\n * stringified query.\n */\nexport type QueryStringified = {\n [K in keyof Query<unknown>]?: string;\n};\n\n/** What upserting one row reports. `created` is only knowable for a single statement, so a batch has none. */\nexport type QueryUpsertOneResult<E> = {\n readonly id?: WrittenId<E>;\n readonly changes?: number;\n /** Whether the record was created (`true`) or updated (`false`), where the dialect can tell. */\n readonly created?: boolean;\n};\n\n/**\n * What upserting many rows reports. `ids` is payload-aligned like an insert's, so it zips with the\n * rows that were passed, and carries a composite key as the map naming it.\n */\nexport type QueryUpsertManyResult<E> = {\n readonly ids: (WrittenId<E> | undefined)[];\n readonly changes?: number;\n};\n\n/**\n * result of an update operation, as the driver reports it - which is what `run` hands back, where\n * there is no entity to name the ids against. The `QueryUpsert*Result` pair is the entity-level shape.\n */\nexport type QueryUpdateResult = {\n /**\n * number of affected records.\n */\n changes?: number;\n /**\n * the IDs the statement reported, in payload order, `undefined` where it reported none for that\n * row - a MongoDB upsert names only the documents it inserted. Exact on `'returning'` dialects;\n * inferred from the driver header on the others (see {@link InsertIdSource}), and absent\n * altogether when the header reports nothing.\n */\n ids?: (PrimaryKey | undefined)[];\n /**\n * first inserted ID.\n */\n firstId?: PrimaryKey;\n /**\n * whether the record was created (`true`) or updated (`false`).\n * `undefined` when the dialect cannot determine this (e.g. SQLite).\n */\n created?: boolean;\n};\n",
@@ -12,7 +12,7 @@
12
12
  "import { CRUD_ROUTES, entityPath, type HttpMethod } from '../../http/contract.js';\nimport { stringifyQuery } from '../../http/query.js';\nimport type {\n EntityData,\n EntityId,\n FieldKey,\n Query,\n QueryFilter,\n QueryFindResult,\n QueryOneProjected,\n QueryOptions,\n QueryPage,\n QueryProjected,\n QuerySearch,\n RelationKey,\n RequestCountedSuccessResponse,\n RequestSuccessResponse,\n Type,\n UpdatePayload,\n WrittenId,\n} from '../../type/index.js';\nimport { isScalarId } from '../../util/object.util.js';\nimport { get, query as httpQuery, patch, post, put, remove } from '../http/index.js';\nimport type { ClientQuerier, RequestFindOptions, RequestOptions } from '../type/index.js';\n\nexport type HttpQuerierDefaults = {\n /**\n * headers sent with every request from this instance, merged under per-call headers.\n * Create one instance per request (e.g. during SSR) to scope auth headers safely.\n */\n readonly headers?: Record<string, string>;\n /**\n * transport for read queries (findOne, findMany, count). 'QUERY' (RFC 10008) sends the\n * JSON query in the request body, avoiding URL-length limits for large queries; requires\n * infrastructure (proxies, CDNs) that forwards the QUERY method. Defaults to 'GET'.\n */\n readonly readMethod?: Extract<HttpMethod, 'GET' | 'QUERY'>;\n /**\n * The URL segment an entity is addressed by, defaulting to its kebab-cased class name - the same\n * option the server handler takes, so one map serves both. State it where the default cannot: a\n * build that minifies class names renames every route.\n */\n readonly entityPath?: (entity: Type<unknown>) => string;\n};\n\n/** The id as one path segment, refusing a composite key, which has no spelling in `/:id` yet. Callers are `async`. */\nfunction idSegment<E>(entity: Type<E>, id: EntityId<E>): string {\n if (!isScalarId(id)) {\n throw new TypeError(`'${entity.name}' was addressed by an id object, which the HTTP route cannot carry.`);\n }\n return String(id);\n}\n\nexport class HttpQuerier implements ClientQuerier {\n constructor(\n readonly basePath: string,\n readonly defaults: HttpQuerierDefaults = {},\n ) {}\n\n async findOneById<\n E extends object,\n const S extends FieldKey<E> = never,\n const V = true,\n const X extends FieldKey<E> = never,\n const P extends RelationKey<E> = never,\n const C extends RelationKey<E> = never,\n >(\n entity: Type<E>,\n id: EntityId<E>,\n q?: QueryOneProjected<E, S, V, X, P, C>,\n opts?: RequestOptions,\n ): Promise<RequestSuccessResponse<QueryFindResult<E, S, V, X, P, C> | undefined>> {\n const basePath = this.getBasePath(entity);\n const qs = stringifyQuery(q);\n return get<QueryFindResult<E, S, V, X, P, C> | undefined>(\n `${basePath}/${idSegment(entity, id)}${qs}`,\n this.buildOptions(opts),\n );\n }\n\n findOne<\n E extends object,\n const S extends FieldKey<E> = never,\n const V = true,\n const X extends FieldKey<E> = never,\n const P extends RelationKey<E> = never,\n const C extends RelationKey<E> = never,\n >(\n entity: Type<E>,\n q: QueryOneProjected<E, S, V, X, P, C>,\n opts?: RequestOptions,\n ): Promise<RequestSuccessResponse<QueryFindResult<E, S, V, X, P, C> | undefined>> {\n return this.read<QueryFindResult<E, S, V, X, P, C> | undefined>(\n `${this.getBasePath(entity)}${CRUD_ROUTES.findOne.path}`,\n q,\n opts,\n );\n }\n\n findMany<\n E extends object,\n const S extends FieldKey<E> = never,\n const V = true,\n const X extends FieldKey<E> = never,\n const P extends RelationKey<E> = never,\n const C extends RelationKey<E> = never,\n >(\n entity: Type<E>,\n q: QueryProjected<E, S, V, X, P, C>,\n opts?: RequestFindOptions,\n ): Promise<RequestSuccessResponse<QueryFindResult<E, S, V, X, P, C>[]>> {\n const data: Query<E> & { count?: boolean } = { ...q };\n if (opts?.count) {\n data.count = true;\n }\n return this.read<QueryFindResult<E, S, V, X, P, C>[]>(this.getBasePath(entity), data, opts);\n }\n\n async findManyAndCount<\n E extends object,\n const S extends FieldKey<E> = never,\n const V = true,\n const X extends FieldKey<E> = never,\n const P extends RelationKey<E> = never,\n const C extends RelationKey<E> = never,\n >(\n entity: Type<E>,\n q: QueryProjected<E, S, V, X, P, C>,\n opts?: RequestFindOptions,\n ): Promise<RequestCountedSuccessResponse<QueryFindResult<E, S, V, X, P, C>[]>> {\n const response = await this.findMany(entity, q, { ...opts, count: true });\n if (typeof response.count !== 'number') {\n throw new TypeError('findManyAndCount response has an invalid count');\n }\n return { ...response, count: response.count };\n }\n\n count<E extends object>(entity: Type<E>, q?: QueryPage<E>, opts?: RequestOptions) {\n return this.read<number>(`${this.getBasePath(entity)}${CRUD_ROUTES.count.path}`, q, opts);\n }\n\n /** The `count` route capped at one row, so existence needs no endpoint of its own. */\n async exists<E extends object>(entity: Type<E>, q?: QueryFilter<E>, opts?: RequestOptions) {\n const res = await this.count(entity, { ...q, $limit: 1 }, opts);\n return { ...res, data: res.data > 0 };\n }\n\n insertOne<E extends object>(entity: Type<E>, payload: EntityData<E>, opts?: RequestOptions) {\n const basePath = this.getBasePath(entity);\n return post<WrittenId<E> | undefined>(basePath, payload, this.buildOptions(opts));\n }\n\n insertMany<E extends object>(entity: Type<E>, payload: EntityData<E>[], opts?: RequestOptions) {\n const basePath = this.getBasePath(entity);\n return post<(WrittenId<E> | undefined)[]>(\n `${basePath}${CRUD_ROUTES.insertMany.path}`,\n payload,\n this.buildOptions(opts),\n );\n }\n\n async updateOneById<E extends object>(\n entity: Type<E>,\n id: EntityId<E>,\n payload: UpdatePayload<E>,\n opts?: RequestOptions,\n ) {\n const basePath = this.getBasePath(entity);\n return patch<number>(`${basePath}/${idSegment(entity, id)}`, payload, this.buildOptions(opts));\n }\n\n updateMany<E extends object>(entity: Type<E>, q: QuerySearch<E>, payload: UpdatePayload<E>, opts?: RequestOptions) {\n const basePath = this.getBasePath(entity);\n const qs = stringifyQuery(q);\n return patch<number>(`${basePath}${qs}`, payload, this.buildOptions(opts));\n }\n\n saveOne<E extends object>(entity: Type<E>, payload: EntityData<E>, opts?: RequestOptions) {\n const basePath = this.getBasePath(entity);\n return put<WrittenId<E> | undefined>(basePath, payload, this.buildOptions(opts));\n }\n\n saveMany<E extends object>(entity: Type<E>, payload: EntityData<E>[], opts?: RequestOptions) {\n const basePath = this.getBasePath(entity);\n return put<(WrittenId<E> | undefined)[]>(\n `${basePath}${CRUD_ROUTES.saveMany.path}`,\n payload,\n this.buildOptions(opts),\n );\n }\n\n async deleteOneById<E extends object>(entity: Type<E>, id: EntityId<E>, opts: QueryOptions & RequestOptions = {}) {\n const basePath = this.getBasePath(entity);\n const qs = opts.hardDelete ? stringifyQuery({ hardDelete: opts.hardDelete }) : '';\n return remove<number>(`${basePath}/${idSegment(entity, id)}${qs}`, this.buildOptions(opts));\n }\n\n deleteMany<E extends object>(entity: Type<E>, q: QuerySearch<E>, opts: QueryOptions & RequestOptions = {}) {\n const basePath = this.getBasePath(entity);\n const qs = stringifyQuery(opts.hardDelete ? { ...q, hardDelete: opts.hardDelete } : q);\n return remove<number>(`${basePath}${qs}`, this.buildOptions(opts));\n }\n\n getBasePath<E>(entity: Type<E>) {\n return `${this.basePath}/${(this.defaults.entityPath ?? entityPath)(entity)}`;\n }\n\n protected read<T>(path: string, q: Record<string, unknown> | undefined, opts?: RequestOptions) {\n if (this.defaults.readMethod === 'QUERY') {\n return httpQuery<T>(path, q ?? {}, this.buildOptions(opts));\n }\n return get<T>(`${path}${stringifyQuery(q)}`, this.buildOptions(opts));\n }\n\n protected buildOptions(opts?: RequestOptions): RequestOptions | undefined {\n if (!this.defaults.headers && !opts?.headers) {\n return opts;\n }\n return { ...opts, headers: { ...this.defaults.headers, ...opts?.headers } };\n }\n}\n",
13
13
  "import { HttpQuerier } from './querier/httpQuerier.js';\nimport type { ClientQuerier, ClientQuerierPool } from './type/index.js';\n\nlet defaultPool: ClientQuerierPool = {\n getQuerier: () => new HttpQuerier('/api'),\n};\n\nexport function setQuerierPool<T extends ClientQuerierPool>(pool: T) {\n defaultPool = pool;\n}\n\nexport function getQuerierPool(): ClientQuerierPool {\n return defaultPool;\n}\n\nexport function getQuerier(): ClientQuerier {\n return getQuerierPool().getQuerier();\n}\n"
14
14
  ],
15
- "mappings": "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",
16
- "debugId": "8A0DBFD4D82E137D64756E2164756E21",
15
+ "mappings": "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",
16
+ "debugId": "76282FBFF94FF89C64756E2164756E21",
17
17
  "names": []
18
18
  }
@@ -3,10 +3,12 @@ import type { QueryContext, SqlDialectFeatures, Type } from '../type/index.js';
3
3
  /** CockroachDB: the Postgres wire and SQL, without `xmax` (so no upsert `created`) and with native vectors. */
4
4
  export declare class CockroachDialect extends PgLikeSqlDialect {
5
5
  readonly dialectName = "cockroachdb";
6
- readonly vectorMetrics: ReadonlyMap<import("../type/vector.js").VectorDistance, {
7
- readonly op: string;
8
- readonly opsSuffix: string;
9
- }>;
6
+ /**
7
+ * pgvector's but `l1`: `<+>` and `vector_l1_ops` answer "unimplemented: operator class ... is not
8
+ * supported" (verified live on v26.2), tracked at https://github.com/cockroachdb/cockroach/issues/147839.
9
+ * Re-check that issue before adding it.
10
+ */
11
+ readonly vectorMetrics: Map<import("../type/vector.js").VectorDistance, import("../type/vector.js").VectorMetric>;
10
12
  /** An upsert batch mixing an update and an insert returns the update first (verified on v26.2). */
11
13
  readonly features: SqlDialectFeatures;
12
14
  /**
@@ -1,11 +1,15 @@
1
1
  import { COUNT_ALIAS } from '../dialect/aliases.js';
2
- import { PG_FEATURES, PgLikeSqlDialect } from '../dialect/pgLikeSqlDialect.js';
3
- import { COCKROACH_VECTOR_METRICS } from '../dialect/pgVectorMetrics.js';
2
+ import { PG_FEATURES, PG_VECTOR_METRICS, PgLikeSqlDialect } from '../dialect/pgLikeSqlDialect.js';
4
3
  import { getMeta } from '../entity/index.js';
5
4
  /** CockroachDB: the Postgres wire and SQL, without `xmax` (so no upsert `created`) and with native vectors. */
6
5
  export class CockroachDialect extends PgLikeSqlDialect {
7
6
  dialectName = 'cockroachdb';
8
- vectorMetrics = COCKROACH_VECTOR_METRICS;
7
+ /**
8
+ * pgvector's but `l1`: `<+>` and `vector_l1_ops` answer "unimplemented: operator class ... is not
9
+ * supported" (verified live on v26.2), tracked at https://github.com/cockroachdb/cockroach/issues/147839.
10
+ * Re-check that issue before adding it.
11
+ */
12
+ vectorMetrics = new Map([...PG_VECTOR_METRICS].filter(([metric]) => metric !== 'l1'));
9
13
  /** An upsert batch mixing an update and an insert returns the update first (verified on v26.2). */
10
14
  features = { ...PG_FEATURES, orderedUpsertReturning: false };
11
15
  /**
@@ -18,5 +18,5 @@ export declare class D1SqliteDialect extends SqliteDialect {
18
18
  * to SQL that only fails once it reaches the edge. `raw()` is no escape hatch either, hence a
19
19
  * message that names the product that does the job.
20
20
  */
21
- protected appendVectorSort(): never;
21
+ protected appendVectorDistance(): never;
22
22
  }
@@ -22,7 +22,7 @@ export class D1SqliteDialect extends SqliteDialect {
22
22
  * to SQL that only fails once it reaches the edge. `raw()` is no escape hatch either, hence a
23
23
  * message that names the product that does the job.
24
24
  */
25
- appendVectorSort() {
25
+ appendVectorDistance() {
26
26
  throw new TypeError('Cloudflare D1 has no vector functions and cannot load sqlite-vec. Use Cloudflare Vectorize for vector search.');
27
27
  }
28
28
  }
@@ -1,6 +1,6 @@
1
- import { type ColumnFamily, type EntityData, type EntityMeta, type EntityWhereMeta, type FieldKey, type FieldOptions, type IsolationLevel, type JsonColumnType, type JsonUpdateOp, type Query, type QueryAggMap, type QueryAggregate, type QueryBuildFn, type QueryComparisonOptions, type QueryConflictPaths, type QueryContext, type QueryContextOptions, type QueryExclude, type QueryGroupMap, type QueryGroupOp, type QueryHavingMap, type QueryOptions, type QueryPage, type QueryPager, QueryRaw, type QueryRawFnOptions, type QuerySearch, type QuerySelectValue, type QuerySizeComparisonOps, type QuerySortMap, type QueryTextSearchOptions, type QueryVectorNear, type QueryWhere, type QueryWhereArray, type QueryWhereOptions, type RelationMeta, type SqlDialectName, type SqlQueryDialect, type Type, type UpdatePayload } from '../type/index.js';
1
+ import { type ColumnFamily, type EntityData, type EntityMeta, type EntityWhereMeta, type FieldKey, type FieldOptions, type IsolationLevel, type JsonColumnType, type JsonUpdateOp, type Query, type QueryAggMap, type QueryAggregate, type QueryBuildFn, type QueryComparisonOptions, type QueryConflictPaths, type QueryContext, type QueryContextOptions, type QueryExclude, type QueryGroupMap, type QueryGroupOp, type QueryHavingMap, type QueryOptions, type QueryPage, type QueryPager, QueryRaw, type QueryRawFnOptions, type QuerySearch, type QuerySelectValue, type QuerySizeComparisonOps, type QuerySortMap, type QueryTextSearchOptions, type QueryWhere, type QueryWhereArray, type QueryWhereOptions, type RelationMeta, type SqlDialectName, type SqlQueryDialect, type Type, type UpdatePayload } from '../type/index.js';
2
2
  import type { HydrateKind } from './hydrateColumn.js';
3
- import { type JsonAccessMode } from './jsonSql.js';
3
+ import { type JsonAccessMode, type JsonSlot } from './jsonSql.js';
4
4
  import { type QueryJoins, type QuerySortOptions } from './queryJoins.js';
5
5
  import { VectorSqlDialect } from './vectorSqlDialect.js';
6
6
  /** How a column's values are bound: see {@link AbstractSqlDialect.persistKind}. */
@@ -171,8 +171,8 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
171
171
  protected selectModifier<E>(_q: Query<E>): string;
172
172
  /**
173
173
  * The expression a scalar field is read through in the statement's own rows, the plain column by
174
- * default. MariaDB reads a vector column back with `VEC_ToText`, since selecting it raw yields its
175
- * binary form. A related row's column crosses JSON through {@link carriedFields} instead.
174
+ * default. MariaDB reads a vector column back as hex, since selecting it raw yields its binary form.
175
+ * A related row's column crosses JSON through {@link carriedFields} instead.
176
176
  */
177
177
  protected selectFieldExpr(escapedColumn: string, _field: FieldOptions): string;
178
178
  /**
@@ -224,6 +224,8 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
224
224
  /** Renders a `$where` tree without applying entity filters (used for same-scope group-operator recursion). */
225
225
  protected renderWhere<E>(ctx: QueryContext, entity: Type<E>, where?: QueryWhere<E>, opts?: QueryWhereOptions): void;
226
226
  compare<E>(ctx: QueryContext, entity: Type<E>, key: string, val: unknown, opts?: QueryComparisonOptions): void;
227
+ /** Conditions joined by `AND`, parenthesized where there is more than one. */
228
+ private static conjunction;
227
229
  protected compareLogicalOperator<E>(ctx: QueryContext, entity: Type<E>, key: QueryGroupOp, val: QueryWhereArray<E>, opts: QueryComparisonOptions): void;
228
230
  /** Memoizes {@link escapedColumnName}; see there for why it is per dialect instance. */
229
231
  private readonly escapedColumns;
@@ -231,11 +233,13 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
231
233
  /** What a `$near` says about the search itself; everything else in it is a bound. */
232
234
  private static readonly VECTOR_QUERY_KEYS;
233
235
  /**
234
- * The runtime half of {@link QueryVectorNear}'s bounds, derived from the map above rather than
235
- * spelled again: both are `QueryOrderedOp`, so `$near` can never accept a comparison the renderer
236
- * below has no operator for.
236
+ * The ordered comparisons, `QueryOrderedOp` at runtime, derived from the map above rather than spelled
237
+ * again: {@link QueryVectorNear}'s bounds, so `$near` never accepts one the renderer has no operator
238
+ * for, and the operators that read a JSON path as a number.
237
239
  */
238
- private static readonly NEAR_BOUND_OPS;
240
+ private static readonly ORDERED_OPS;
241
+ /** The operators an equality compares by value, which a JSON path reads the way that value compares. */
242
+ private static readonly EQUALITY_OPS;
239
243
  /**
240
244
  * Every `$like`-family operator: the pattern it wraps its value in, and whether it ignores case.
241
245
  * Each case-sensitive operator is paired here with the `$i` twin that shares its pattern, so the
@@ -269,51 +273,69 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
269
273
  * alias exists only when the field was also selected, which `$where` and `$sort` cannot assume.
270
274
  */
271
275
  private inlinedOperand;
272
- /** One operator of a field's condition. Both come from the query as data, so neither is trusted. */
276
+ /** {@link fieldCondition}, appended. */
273
277
  compareFieldOperator<E>(ctx: QueryContext, entity: Type<E>, key: FieldKey<E>, op: string, val: unknown, opts?: QueryOptions): void;
278
+ /** One operator of a field's condition. Both come from the query as data, so neither is trusted. */
279
+ private fieldCondition;
274
280
  /**
275
281
  * `<operand> <op> <value>` for every operator that needs only its left-hand SQL, shared by a column, a
276
- * `HAVING` expression and a `$size` count; `undefined` for the rest.
277
- */
278
- protected operatorCondition(ctx: QueryContext, operand: string, op: string, val: unknown): string | undefined;
279
- /** {@link operatorCondition}, appended; `false` when `op` needs more than an operand. */
280
- protected appendOperatorCondition(ctx: QueryContext, operand: string, op: string, val: unknown): boolean;
281
- /**
282
- * Build a comparison condition for a JSON field.
283
- * Used by both `$elemMatch` and dot-notation paths.
284
- * All dialect-specific behavior comes from overridable methods on `this`.
285
- */
286
- protected buildJsonFieldCondition(ctx: QueryContext, fieldAccessor: (path: string, mode: JsonAccessMode) => string, jsonPath: string, op: string, value: unknown, asJson?: boolean): string;
287
- private jsonInNin;
288
- /** The bound operand of a JSON comparison: JSON-encoded when comparing against the JSON value. */
289
- protected jsonOperand(ctx: QueryContext, value: unknown, asJson: boolean): string;
290
- /** `$all`: the JSON array at `jsonField` contains every value (also serves element containment). */
291
- protected abstract jsonAll(ctx: QueryContext, jsonField: string, value: unknown): string;
292
- /** `$size`: the length of the JSON array at `jsonField`, compared against `value`. */
293
- protected abstract jsonSize(ctx: QueryContext, jsonField: string, value: number | QuerySizeComparisonOps): string;
294
- /**
295
- * The JSON array at `jsonField` as rows, the `FROM` of an `EXISTS`, under a fresh `alias` so a nested
296
- * one cannot shadow it. `fields` are an object element's keys (MySQL lists them up front), empty for
297
- * scalars, which `asJson` reads as JSON rather than text.
298
- */
299
- protected abstract jsonElemFrom(jsonField: string, fields: readonly string[], alias: string, asJson?: boolean): string;
300
- /**
301
- * References an exploded element under `alias` (the same one passed to the {@link jsonElemFrom}
302
- * call it explodes): the element itself, or one `field` of it. `asJson` asks for the JSON-valued
303
- * form instead of the text one - see {@link isJsonbOp} for when that matters.
304
- */
305
- protected abstract jsonElemRef(alias: string, field?: string, asJson?: boolean): string;
306
- /**
307
- * `$elemMatch`: an element satisfies `match`. Operator keys test a scalar element; a plain object is
308
- * containment, which an index can serve; anything else tests each exploded object's fields.
309
- */
310
- protected jsonElemMatch(ctx: QueryContext, jsonField: string, match: Record<string, unknown>): string;
311
- /**
312
- * How a `$elemMatch` reads one exploded element: {@link jsonPathExpr}'s counterpart, over a column
313
- * of the derived table rather than a path of the document. An empty field names the element itself,
314
- * which is what the operator-only form matches on.
315
- */
316
- private elemAccessor;
282
+ * JSON path, a `HAVING` expression, a count and a distance; `undefined` for the rest. `bind` renders
283
+ * each compared value, a plain placeholder unless a JSON path reads it otherwise.
284
+ */
285
+ protected operatorCondition(ctx: QueryContext, operand: string, op: string, val: unknown, bind?: (value: unknown) => string): string | undefined;
286
+ /** `$all`, `$size` and `$elemMatch`, which read the JSON array at `slot`; `undefined` for the rest. */
287
+ private jsonArrayCondition;
288
+ /** A path of a JSON document, read the way each operator reads it. */
289
+ private jsonPathTarget;
290
+ /** Every operator `target` is compared with, `AND`-joined. */
291
+ private jsonConditions;
292
+ private jsonCondition;
293
+ /**
294
+ * How `op` reads a JSON value: an ordered comparison as a number, an equality as its operand compares,
295
+ * and a pattern or a null check as text.
296
+ */
297
+ private static jsonOperatorMode;
298
+ /** A bound operand of a JSON comparison, read the way `mode` reads the value it is compared with. */
299
+ private jsonOperand;
300
+ /** The JSON value at `slot`, as an array operator reads it. */
301
+ protected jsonValue(slot: JsonSlot): string;
302
+ /**
303
+ * `$all`: the JSON array at `slot` has an element holding each value, as Postgres's `@>` and MySQL's
304
+ * `JSON_CONTAINS` read one: a scalar equal, an array each of its elements, an object each of its keys.
305
+ * Plain JSON goes to {@link jsonContains}; an operator anywhere in it is matched element by element.
306
+ */
307
+ private jsonAll;
308
+ /** `$all` over plain JSON, which an engine with containment of its own spells natively, for an index to serve. */
309
+ protected jsonContains(ctx: QueryContext, slot: JsonSlot, values: readonly unknown[]): string;
310
+ /** One `EXISTS` per value, over the elements of the array at `slot`: a scalar equal, anything else held. */
311
+ private jsonElemsHold;
312
+ /**
313
+ * What the JSON `target` reads satisfies to hold `value`: an array each of its elements, an object each
314
+ * of its keys as a path, and an operator map or a scalar compared as a path is.
315
+ */
316
+ private jsonHolds;
317
+ /** An element of the array at `slot` equal to `value`, both read as JSON. */
318
+ protected jsonElemEquals(ctx: QueryContext, slot: JsonSlot, alias: string, value: unknown): string;
319
+ /** The JSON array at `slot` contains at least one of `values`, each as `$all` reads it. */
320
+ protected jsonAny(ctx: QueryContext, slot: JsonSlot, values: readonly unknown[]): string;
321
+ /** How many elements the JSON array at `slot` has, which `$size` compares. */
322
+ protected abstract jsonLength(slot: JsonSlot): string;
323
+ /** Whether the value at `slot` is a JSON array: the array operators match, and `$pull` changes, no other. */
324
+ protected abstract jsonIsArray(slot: JsonSlot): string;
325
+ /** The JSON array at `slot` as one row per element under `alias`, the `FROM` of an `EXISTS`. */
326
+ protected abstract jsonElemFrom(slot: JsonSlot, alias: string): string;
327
+ /** An exploded element as a JSON document, which its fields are paths into. */
328
+ protected abstract jsonElemDoc(alias: string): string;
329
+ /** An element of the array at `slot` itself, read the way `mode` reads a path: its document, or its root. */
330
+ protected jsonElemValue(_slot: JsonSlot, alias: string, mode: JsonAccessMode): string;
331
+ /**
332
+ * `$elemMatch`: an element holds `match`, as `$all` reads one value, its operators testing the element
333
+ * itself. An element equal to one value, or to one of several, is containment whatever the operator
334
+ * says, which compares by JSON type and is what an array index serves.
335
+ */
336
+ protected jsonElemMatch(ctx: QueryContext, slot: JsonSlot, match: Record<string, unknown>): string;
337
+ /** The optimizer hint a `$elemMatch` subquery opens with, where the engine plans one wrong without it. */
338
+ protected readonly jsonElemHint: string;
317
339
  /**
318
340
  * A JSON-encoded bound parameter, cast to the dialect's JSON type. Only the positional-placeholder
319
341
  * dialects use this - PostgreSQL binds JSON through {@link PgLikeSqlDialect.jsonScalarParam} instead.
@@ -337,10 +359,16 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
337
359
  */
338
360
  private collectSortTerms;
339
361
  /**
340
- * The `ORDER BY` operand for one key. A key that is not a field of `meta` - a `raw()` projection, a
341
- * `$select` alias - is an output alias, which is never table-qualified and needs no resolving.
362
+ * The `ORDER BY` operands for one key: a JSON path's in each of {@link jsonSortModes}. A key that is
363
+ * not a field of `meta` - a `raw()` projection, a `$select` alias - is an output alias, which is never
364
+ * table-qualified and needs no resolving.
365
+ */
366
+ private sortColumns;
367
+ /**
368
+ * How a JSON path is sorted by: as the JSON value, which the engine orders by type and a number by its
369
+ * value. An engine that orders JSON as text reads a number first, then the text.
342
370
  */
343
- private sortColumn;
371
+ protected readonly jsonSortModes: readonly JsonAccessMode[];
344
372
  /**
345
373
  * `LIMIT`/`OFFSET`. `sorted` says whether an `ORDER BY` was emitted just before, which
346
374
  * {@link MergeSqlDialect} needs: SQL Server refuses to page a statement that has none.
@@ -392,7 +420,7 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
392
420
  protected having(ctx: QueryContext, having: QueryHavingMap, emittedColumns: Record<string, string>): void;
393
421
  private static readonly SORT_DIRECTION_MAP;
394
422
  private resolveSortDirection;
395
- /** Scalar comparison operators shared by `HAVING` conditions and `$size` comparisons. */
423
+ /** Every operator of one `HAVING` condition, `AND`-joined. */
396
424
  protected havingCondition(ctx: QueryContext, expr: string, condition: QueryHavingMap[string]): void;
397
425
  /**
398
426
  * How many rows the filter matched, on every row of the page. A window function runs before
@@ -495,8 +523,16 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
495
523
  * `escapedCol` rather than the accumulated expression, values bind once, in key order.
496
524
  */
497
525
  protected jsonPull(ctx: QueryContext, expr: string, escapedCol: string, pull: Record<string, unknown>): string;
498
- /** Wrap `expr` so the array at `key` no longer contains `value`. */
499
- protected abstract jsonPullKey(ctx: QueryContext, expr: string, escapedCol: string, key: string, value: unknown): string;
526
+ /**
527
+ * Wrap `expr` so the array at `key` no longer contains `value`: the array rebuilt from the elements that
528
+ * differ from it, and any other value put back as it is. `JSON_REPLACE` leaves an absent key, and a NULL
529
+ * column, untouched.
530
+ */
531
+ protected jsonPullKey(ctx: QueryContext, expr: string, escapedCol: string, key: string, value: unknown): string;
532
+ /** The elements a `$pull` keeps, back in one array, and an empty one where it keeps none. */
533
+ protected jsonArrayOf(elem: string): string;
534
+ /** Whether an element is not the pulled value, both read as JSON. */
535
+ protected jsonDiffers(elem: string, operand: string): string;
500
536
  /** Shallow assignment of top-level keys, matching PostgreSQL's `jsonb || jsonb`. */
501
537
  protected abstract jsonSet(ctx: QueryContext, expr: string, set: Record<string, unknown>, field?: FieldOptions): string;
502
538
  /** Append one value per array key, creating the array when the key is absent. */
@@ -513,37 +549,16 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
513
549
  getRawValue(ctx: QueryContext, opts: QueryRawFnOptions & {
514
550
  value: QueryRaw;
515
551
  }): void;
516
- /**
517
- * Resolves a dot-notation key to its JSON field metadata.
518
- * Shared by `where()` and `sort()` to detect 'column.path' keys where 'column' is a JSON/JSONB field.
519
- *
520
- * @returns resolved metadata or `undefined` if the key is not a JSON dot-notation path
521
- */
522
- protected resolveJsonDotPath<E>(meta: EntityMeta<E>, key: string, prefix?: string): {
523
- jsonPath: string;
524
- column: string;
525
- } | undefined;
552
+ /** A `column.path` key of `meta`'s JSON field as the path it names, shared by `where` and `sort`; else `undefined`. */
553
+ protected resolveJsonDotPath<E>(meta: EntityMeta<E>, key: string, prefix?: string): JsonSlot | undefined;
526
554
  /**
527
555
  * One JSON path, read the way `mode` asks for: the one place the three readings are chosen between,
528
556
  * so `$where`, `$sort` and every operator reach a path the same way. Public because a JSON index
529
557
  * is matched back by its own text, so the migrator's `CREATE INDEX` has to spell it from here too.
530
558
  */
531
- jsonPathExpr(escapedColumn: string, jsonPath: string, mode: JsonAccessMode): string;
532
- /**
533
- * Compare a JSONB dot-notation path, e.g. `'settings.isArchived': { $ne: true }`.
534
- * Receives a pre-resolved `resolveJsonDotPath` result to avoid redundant computation.
535
- */
536
- protected compareJsonPath(ctx: QueryContext, resolved: {
537
- jsonPath: string;
538
- column: string;
539
- }, val: unknown): void;
540
- /**
541
- * Returns SQL that extracts a scalar value from a JSON path.
542
- * Dialects can override this to customize path access syntax while preserving
543
- * the shared comparison/operator pipeline.
544
- */
545
- protected getJsonPathScalarExpr(escapedColumn: string, jsonPath: string): string;
546
- protected getJsonPathJsonbExpr(escapedColumn: string, jsonPath: string): string;
559
+ jsonPathExpr(escapedColumn: string, path: string, mode: JsonAccessMode): string;
560
+ /** A path of the JSON in `escapedColumn`, `''` for the document itself, as its JSON value or its text. */
561
+ protected abstract jsonPathReading(escapedColumn: string, path: string, mode: 'json' | 'text'): string;
547
562
  /**
548
563
  * Normalizes a raw WHERE value into an operator map.
549
564
  * Arrays become `$in`, operator maps pass through, everything else becomes `$eq`.
@@ -624,23 +639,18 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
624
639
  /** Filter by relation size: the same subquery, counting instead of testing for existence. */
625
640
  protected compareRelationSize<E>(ctx: QueryContext, entity: Type<E>, relKey: string, sizeVal: number | QuerySizeComparisonOps, rel: RelationMeta, opts: QueryComparisonOptions): void;
626
641
  /**
627
- * `<expr> <op> <value>` for each operator, AND-joined and parenthesized when there is more than
628
- * one. `exprFn` is re-run per operator because what it appends is an expression, not a column:
629
- * a `WHERE` has no output alias to refer back to, so the only way to compare it twice is to spell
630
- * it twice. Shared by `$size`, which counts, and `$near`, which measures a distance.
642
+ * `<expr> <op> <value>` for each bound, `AND`-joined. `expr` is spelled once per bound because it is an
643
+ * expression, not a column: a `WHERE` has no output alias to refer back to. Shared by `$size`, which
644
+ * counts, and `$near`, which measures a distance.
631
645
  */
632
- private buildExprComparison;
646
+ private boundConditions;
633
647
  /**
634
- * Build a complete `$size` comparison expression.
635
- * @param sizeExprFn - function that appends the size expression to ctx (e.g. `JSONB_ARRAY_LENGTH("col")`)
648
+ * A count compared with `size`, a number or its bounds. A count is never NULL, so its equality stays
649
+ * plain rather than the null-safe `$ne` (`IS DISTINCT FROM`, `IS NOT`): same rows, shorter SQL.
636
650
  */
637
- protected buildSizeComparison(ctx: QueryContext, sizeExprFn: () => void, sizeVal: number | QuerySizeComparisonOps): void;
651
+ private sizeCondition;
638
652
  /** `<distance> <op> ?`, the `$where` half of a vector search, its bounds checked here since `/http` input is untyped. */
639
- protected compareVectorNear<E>(ctx: QueryContext, meta: EntityMeta<E>, key: string, near: QueryVectorNear): void;
640
- /** The runtime half of {@link QuerySizeComparisonOps}: what a count can sensibly be compared with. */
641
- private static readonly SIZE_COMPARE_OPS;
642
- /** ` <op> <value>` after a count already written, refusing any operator a count cannot be compared with. */
643
- private appendSizeOp;
653
+ private vectorNearCondition;
644
654
  /** ANSI-style single-quote escaping. MySQL-family dialects override this for backslash escaping. */
645
655
  escape(value: unknown): string;
646
656
  protected get regexpOp(): string;
@@ -658,8 +668,8 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
658
668
  */
659
669
  protected get neOp(): string;
660
670
  protected neExpr(field: string, ph: string): string;
661
- /** `operand IN (...)` binding each value, or the constant an empty set reduces to: no value is in it. */
662
- protected formatIn(ctx: QueryContext, operand: string, values: unknown[], negate: boolean): string;
671
+ /** `operand IN (...)` of each value as `bind` renders it, or the constant an empty set reduces to: no value is in it. */
672
+ protected formatIn(_ctx: QueryContext, operand: string, values: unknown[], negate: boolean, bind: (value: unknown) => string): string;
663
673
  /** Reads extracted JSON text as a number, which every engine spells its own way. */
664
674
  protected abstract numericCast(expr: string): string;
665
675
  toString(): string;