uql-orm 0.60.0 → 0.62.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 (48) hide show
  1. package/dist/browser/uql-browser.min.js +2 -2
  2. package/dist/browser/uql-browser.min.js.map +4 -4
  3. package/dist/dialect/abstractSqlDialect.d.ts +12 -7
  4. package/dist/dialect/abstractSqlDialect.js +51 -50
  5. package/dist/dialect/mysqlLikeSqlDialect.js +1 -1
  6. package/dist/dialect/pgLikeSqlDialect.d.ts +1 -0
  7. package/dist/dialect/pgLikeSqlDialect.js +8 -7
  8. package/dist/dialect/queryContext.d.ts +5 -6
  9. package/dist/dialect/queryContext.js +8 -8
  10. package/dist/entity/decorator/entity.d.ts +3 -3
  11. package/dist/entity/decorator/entity.js +2 -2
  12. package/dist/entity/decorator/members.d.ts +3 -2
  13. package/dist/entity/decorator/members.js +1 -0
  14. package/dist/entity/metadata/definition.js +12 -6
  15. package/dist/http/contract.d.ts +1 -1
  16. package/dist/http/contract.js +16 -4
  17. package/dist/migrate/builder/migrationBuilder.js +3 -5
  18. package/dist/migrate/builder/tableBuilder.js +2 -4
  19. package/dist/migrate/builder/types.d.ts +11 -3
  20. package/dist/migrate/codegen/indexDecoratorSource.js +3 -1
  21. package/dist/migrate/generator/definitionToNode.d.ts +8 -5
  22. package/dist/migrate/generator/definitionToNode.js +13 -4
  23. package/dist/migrate/generator/mongoSchemaGenerator.d.ts +2 -0
  24. package/dist/migrate/generator/mongoSchemaGenerator.js +6 -1
  25. package/dist/migrate/schemaGenerator.d.ts +5 -3
  26. package/dist/migrate/schemaGenerator.js +9 -2
  27. package/dist/mongo/mongoDialect.js +1 -1
  28. package/dist/mssql/mssqlDialect.js +3 -5
  29. package/dist/querier/queryError.d.ts +15 -12
  30. package/dist/querier/queryError.js +67 -8
  31. package/dist/schema/schemaASTBuilder.d.ts +6 -1
  32. package/dist/schema/schemaASTBuilder.js +17 -16
  33. package/dist/type/dialect.d.ts +15 -6
  34. package/dist/type/entity.d.ts +94 -34
  35. package/dist/type/migration.d.ts +9 -2
  36. package/dist/type/query.d.ts +3 -3
  37. package/dist/type/queryLock.d.ts +7 -7
  38. package/dist/type/queryLock.js +8 -6
  39. package/dist/type/queryRaw.d.ts +22 -30
  40. package/dist/type/queryRaw.js +3 -9
  41. package/dist/util/ddlExpression.util.d.ts +8 -13
  42. package/dist/util/ddlExpression.util.js +17 -19
  43. package/dist/util/dialect.util.d.ts +1 -1
  44. package/dist/util/dialect.util.js +3 -4
  45. package/dist/util/field.util.d.ts +1 -1
  46. package/dist/util/raw.d.ts +19 -17
  47. package/dist/util/raw.js +43 -17
  48. package/package.json +1 -1
@@ -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 S 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 x(e,t){return i(e,{method:"delete"},t)}function m(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 S(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),I=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"],R=["$count"],k=["$skip","$limit"],Q=["$candidates"],U=["$distinct"],W=["$lock",...R,...Q];var H=new Set([...K,...R,...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 P(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 g{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}/${P(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}/${P(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 x(`${r}/${P(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 x(`${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 m(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 g("/api")};function oe(e){w=e}function q(){return w}function se(){return q().getQuerier()}export{g as HttpQuerier,S 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,m as query,x 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 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};
2
2
 
3
- //# debugId=8B6889FD5E02FA9764756E2164756E21
3
+ //# debugId=8A0DBFD4D82E137D64756E2164756E21
4
4
  //# sourceMappingURL=uql-browser.min.js.map
@@ -6,13 +6,13 @@
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
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/**\n * Whether `value` is a non-empty object whose keys are query/update operators (`$eq`, `$push`, ...).\n * The single source of this test: the SQL dialects, the MongoDB dialect and the `$elemMatch` walker\n * all classify operator objects with it, and they used to disagree about `{}`.\n */\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): (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 === null || val === undefined) return val as 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
- "import 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/**\n * Map a thrown error to the wire error envelope. Honors a numeric `status` on the error\n * (e.g. hooks throwing 403), defaults to 500; `code` mirrors the HTTP status.\n */\nexport function toErrorResponse(err: unknown): { status: number; body: RequestErrorResponse } {\n const status = err instanceof Error && 'status' in err && typeof err.status === 'number' ? err.status : 500;\n const message = err instanceof Error ? err.message : 'Internal Server Error';\n return { status, body: { error: { message, code: status } } };\n}\n",
10
- "import type { FieldKey, IdKey, 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 * Ambient per-request context (e.g. `{ tenantId, userId, roles }`) resolved by parameterized\n * filters. Set with `withContext(ctx, cb)`. It's an `interface` (not a type alias) so you can type\n * your keys once via declaration merging and get them typed wherever context is read:\n *\n * ```ts\n * declare module 'uql-orm' {\n * interface UqlContext { tenantId: number; userId: string }\n * }\n * ```\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 FilterCondition<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 condition: FilterCondition<E>;\n /** Applied to every query unless bypassed via `QueryOptions.filters`. Defaults to `true`. */\n readonly default?: boolean;\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.\n */\n readonly security?: boolean;\n /** What to do when the condition returns `undefined`. Defaults to `skip`, or `throw` for `security`. */\n readonly onMissing?: FilterOnMissing;\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 * sort by map - supports field keys, JSON dot-notation paths (restricted to real JSON fields,\n * like `QueryWhere`), relation sort via nested objects, and vector similarity search on\n * `number[]` fields. `Vector` is what confines a vector search to the level the statement ranks:\n * the queried entity. A relation of it is joined in one row at a time, so there is nothing to rank\n * there - the SQL dialects throw, and MongoDB would quietly drop it, so this is its only guard.\n *\n * One mapped type over the three key sets rather than three intersected. The sets are disjoint - a\n * JSON path is dotted, and a field key cannot also be a relation key - and an assignability check\n * against an intersection is repeated per constituent, which made this the single most expensive\n * type in the package 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 * take a row-level lock on the rows this query returns (`SELECT ... FOR UPDATE`). Needs an open\n * transaction: outside one the statement commits and drops the lock before the caller can act on\n * the rows, so it is rejected rather than emitted. Locks only the queried entity, never anything\n * reached through `$populate`. SQL only; MongoDB and the SQLite family reject it.\n *\n * Declared here rather than on {@link QuerySearch}, which `update`/`delete` take: that placement\n * is what keeps the clause off those statements at the type level.\n */\n $lock?: QueryLock;\n\n /**\n * how many candidates an approximate-nearest-neighbour index explores before ranking, for a vector\n * search. Higher trades speed for recall; the default is whatever the engine's own is, which is\n * tuned for speed. Ignored where the search is exact (SQLite, libSQL and Turso scan every row) and\n * where the field carries no ANN index, since there is nothing to widen.\n *\n * The units are the index's, not UQL's, so the number is not comparable across index types: it\n * becomes `hnsw.ef_search` or `ivfflat.probes` on Postgres, `mhnsw_ef_search` on MariaDB, and\n * `numCandidates` on MongoDB Atlas. On Postgres it needs an open transaction, since a `SET LOCAL`\n * outside one applies to nothing.\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 - what a parser reading one off the wire and\n * a validator checking a relation's own query both need, and what each used to enumerate for itself.\n * Declared beside the type they describe so the two cannot drift, and `satisfies` fails the build\n * rather than the runtime if a clause is ever renamed.\n *\n * `$lock` is only in {@link QUERY_STATEMENT_CLAUSES}: neither a wire query nor a relation's query\n * accepts it.\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 decide a row's shape, captured from the query as written: the field names\n * `$select` and `$exclude` list, the value those maps carry (a falsy one subtracts instead of\n * selecting, as it does at runtime, and a widened map is how a projection that is not statically\n * known announces itself), and the relation names `$populate` lists.\n *\n * Each is captured as a *key set* rather than as the map itself, which is what keeps the checks\n * intact: TypeScript skips excess-property checking on a naked type parameter, so a captured map\n * would take a typo'd key without a word, while a captured key set makes that typo fail its own\n * `FieldKey<E>` / `RelationKey<E>` constraint. Every other clause - `$where`, `$sort`, and each\n * populated relation's own query - stays the concrete {@link Query} it is today.\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, mirroring what the runtime projects: the fields a positive\n * `$select` names, or every field minus what a falsy `$select` entry or a truthy `$exclude` entry\n * subtracts, plus the relations `$populate` asked for. A positive `$select` wins outright, which is\n * why `$exclude` is only read on the branch where there is none.\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 row of a find result: the entity narrowed to the fields the query projected, plus the relations\n * it populated - reading anything the query left out is a compile error rather than a silent\n * `undefined`. Modifiers are preserved, so an optional field stays optional. Name a projected row\n * with it where a helper has to take one: `QueryFindResult<User, 'id' | 'name'>`.\n *\n * The entity itself when the query projects nothing, when it uses a raw-projection array (columns,\n * not fields), and when the projection is not uniform - a `Query<E>` built elsewhere, or a map\n * mixing selected and subtracted entries, whose positive keys inference cannot recover. Relations\n * keep their declared type: narrowing them means capturing their queries as maps, which costs those\n * queries their own checks.\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/**\n * What upserting one row reports, against the entity rather than the driver.\n *\n * `created` is here and not on {@link QueryUpsertManyResult} because it is only ever knowable for a\n * single statement: a batch's `affectedRows` is a weighted sum on the dialects that report one at\n * all, and a batch of mixed shapes is several statements.\n */\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",
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
+ "import type { FieldKey, IdKey, 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 * Ambient per-request context (e.g. `{ tenantId, userId, roles }`) resolved by parameterized\n * filters. Set with `withContext(ctx, cb)`. It's an `interface` (not a type alias) so you can type\n * your keys once via declaration merging and get them typed wherever context is read:\n *\n * ```ts\n * declare module 'uql-orm' {\n * interface UqlContext { tenantId: number; userId: string }\n * }\n * ```\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 * 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.\n */\n readonly security?: boolean;\n /** What to do when {@link where} returns `undefined`. Defaults to `skip`, or `throw` for `security`. */\n readonly onMissing?: FilterOnMissing;\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 * sort by map - supports field keys, JSON dot-notation paths (restricted to real JSON fields,\n * like `QueryWhere`), relation sort via nested objects, and vector similarity search on\n * `number[]` fields. `Vector` is what confines a vector search to the level the statement ranks:\n * the queried entity. A relation of it is joined in one row at a time, so there is nothing to rank\n * there - the SQL dialects throw, and MongoDB would quietly drop it, so this is its only guard.\n *\n * One mapped type over the three key sets rather than three intersected. The sets are disjoint - a\n * JSON path is dotted, and a field key cannot also be a relation key - and an assignability check\n * against an intersection is repeated per constituent, which made this the single most expensive\n * type in the package 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 * take a row-level lock on the rows this query returns (`SELECT ... FOR UPDATE`). Needs an open\n * transaction: outside one the statement commits and drops the lock before the caller can act on\n * the rows, so it is rejected rather than emitted. Locks only the queried entity, never anything\n * reached through `$populate`. SQL only; MongoDB and the SQLite family reject it.\n *\n * Declared here rather than on {@link QuerySearch}, which `update`/`delete` take: that placement\n * is what keeps the clause off those statements at the type level.\n */\n $lock?: QueryLock;\n\n /**\n * how many candidates an approximate-nearest-neighbour index explores before ranking, for a vector\n * search. Higher trades speed for recall; the default is whatever the engine's own is, which is\n * tuned for speed. Ignored where the search is exact (SQLite, libSQL and Turso scan every row) and\n * where the field carries no ANN index, since there is nothing to widen.\n *\n * The units are the index's, not UQL's, so the number is not comparable across index types: it\n * becomes `hnsw.ef_search` or `ivfflat.probes` on Postgres, `mhnsw_ef_search` on MariaDB, and\n * `numCandidates` on MongoDB Atlas. On Postgres it needs an open transaction, since a `SET LOCAL`\n * outside one applies to nothing.\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 - what a parser reading one off the wire and\n * a validator checking a relation's own query both need, and what each used to enumerate for itself.\n * Declared beside the type they describe so the two cannot drift, and `satisfies` fails the build\n * rather than the runtime if a clause is ever renamed.\n *\n * `$lock` is only in {@link QUERY_STATEMENT_CLAUSES}: neither a wire query nor a relation's query\n * accepts it.\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 decide a row's shape, captured from the query as written: the field names\n * `$select` and `$exclude` list, the value those maps carry (a falsy one subtracts instead of\n * selecting, as it does at runtime, and a widened map is how a projection that is not statically\n * known announces itself), and the relation names `$populate` lists.\n *\n * Each is captured as a *key set* rather than as the map itself, which is what keeps the checks\n * intact: TypeScript skips excess-property checking on a naked type parameter, so a captured map\n * would take a typo'd key without a word, while a captured key set makes that typo fail its own\n * `FieldKey<E>` / `RelationKey<E>` constraint. Every other clause - `$where`, `$sort`, and each\n * populated relation's own query - stays the concrete {@link Query} it is today.\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, mirroring what the runtime projects: the fields a positive\n * `$select` names, or every field minus what a falsy `$select` entry or a truthy `$exclude` entry\n * subtracts, plus the relations `$populate` asked for. A positive `$select` wins outright, which is\n * why `$exclude` is only read on the branch where there is none.\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 row of a find result: the entity narrowed to the fields the query projected, plus the relations\n * it populated - reading anything the query left out is a compile error rather than a silent\n * `undefined`. Modifiers are preserved, so an optional field stays optional. Name a projected row\n * with it where a helper has to take one: `QueryFindResult<User, 'id' | 'name'>`.\n *\n * The entity itself when the query projects nothing, when it uses a raw-projection array (columns,\n * not fields), and when the projection is not uniform - a `Query<E>` built elsewhere, or a map\n * mixing selected and subtracted entries, whose positive keys inference cannot recover. Relations\n * keep their declared type: narrowing them means capturing their queries as maps, which costs those\n * queries their own checks.\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/**\n * What upserting one row reports, against the entity rather than the driver.\n *\n * `created` is here and not on {@link QueryUpsertManyResult} because it is only ever knowable for a\n * single statement: a batch's `affectedRows` is a weighted sum on the dialects that report one at\n * all, and a batch of mixed shapes is several statements.\n */\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",
11
11
  "import type { Query, QueryOptions } from '../type/index.js';\n// the clause lists themselves, not the barrel: this module is in the browser bundle's graph\nimport {\n QUERY_BOOLEAN_CLAUSES,\n QUERY_NUMBER_CLAUSES,\n QUERY_OBJECT_CLAUSES,\n QUERY_ROOT_NUMBER_CLAUSES,\n QUERY_ROOT_OBJECT_CLAUSES,\n} from '../type/query.js';\n// the specific util module, not the barrel, so the browser bundle does not pull in entity metadata\nimport { getKeys, isWhereMap } from '../util/object.util.js';\n\n/**\n * Keys accepted from the wire - query structure ({@link Query}) plus the `hardDelete`/`count` scalar\n * flags. Anything else (e.g. `filters`, `context`, `$entity`) is dropped so a remote client can't\n * bypass a security filter or inject ambient context - those are server-only. The `satisfies` ties\n * every entry to a real query/option key, so a typo or a renamed option fails to compile.\n */\nconst ALLOWED_QUERY_KEYS = new Set<string>([\n ...QUERY_OBJECT_CLAUSES,\n ...QUERY_ROOT_OBJECT_CLAUSES,\n ...QUERY_NUMBER_CLAUSES,\n ...QUERY_ROOT_NUMBER_CLAUSES,\n ...QUERY_BOOLEAN_CLAUSES,\n 'hardDelete',\n 'count',\n] satisfies (keyof Query<unknown> | keyof Pick<QueryOptions, 'hardDelete'> | 'count')[]);\n\n/**\n * Keys that mean something locally but that this transport can never honor, so they are rejected\n * rather than dropped like the rest. Each request runs on its own auto-committing connection, so a\n * row lock taken here is released before the response is written: honoring `$lock` is impossible,\n * and ignoring it would hand the caller a read they believe is serialized and is not.\n */\nconst REJECTED_QUERY_KEYS = new Set<string>(['$lock'] satisfies (keyof Query<unknown>)[]);\n\n/**\n * Parse raw query-string entries (with JSON-stringified values) into a UQL query object.\n * Symmetric counterpart of {@link stringifyQuery}. Only {@link ALLOWED_QUERY_KEYS} are honored.\n */\nexport function parseQueryParams(params: Record<string, unknown> = {}): Query<unknown> {\n const query: Record<string, unknown> = {};\n for (const key of getKeys(params)) {\n if (REJECTED_QUERY_KEYS.has(key)) {\n throw Object.assign(new TypeError(`'${key}' is not supported over HTTP`), { status: 400 });\n }\n if (ALLOWED_QUERY_KEYS.has(key)) {\n query[key] = params[key];\n }\n }\n\n for (const key of [...QUERY_OBJECT_CLAUSES, ...QUERY_ROOT_OBJECT_CLAUSES]) {\n const value = query[key];\n if (typeof value === 'string') {\n try {\n query[key] = JSON.parse(value);\n } catch {\n throw Object.assign(new SyntaxError(`invalid JSON in '${key}'`), { status: 400 });\n }\n }\n }\n\n query['$where'] ??= {};\n if (!isWhereMap(query['$where'])) {\n throw Object.assign(new TypeError(\"'$where' must be a JSON object\"), { status: 400 });\n }\n\n // A query string carries every value as text, so what decodes a clause is the shape its group\n // declares. `'false'` is the reason the boolean pass exists rather than the raw value being taken:\n // it is a non-empty string, so a `$distinct=false` would otherwise read as asking for one.\n for (const key of [...QUERY_NUMBER_CLAUSES, ...QUERY_ROOT_NUMBER_CLAUSES]) {\n if (query[key] !== undefined) {\n query[key] = Number(query[key]);\n }\n }\n for (const key of QUERY_BOOLEAN_CLAUSES) {\n if (query[key] !== undefined) {\n query[key] = query[key] === true || query[key] === 'true';\n }\n }\n\n return query as Query<unknown>;\n}\n\n/**\n * Serialize a UQL query object into a percent-encoded query string where object values\n * are JSON-stringified. Symmetric counterpart of {@link parseQueryParams}.\n */\nexport function stringifyQuery(query?: Record<string, unknown>): string {\n if (!query) {\n return '';\n }\n const params = new URLSearchParams();\n for (const key of getKeys(query)) {\n const value = query[key];\n if (value === undefined) {\n continue;\n }\n params.append(key, typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value));\n }\n const qs = params.toString();\n return qs ? `?${qs}` : '';\n}\n",
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/**\n * The id as one path segment.\n *\n * A composite key has no spelling here yet - the route is `/:id`, and how several columns share one\n * segment is a serialization to invent rather than copy. Refused rather than interpolated, which\n * would have sent `[object Object]` for the server to reject. Its callers are `async` so this\n * surfaces as a rejection, like every other failure they can hand back.\n */\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": "8B6889FD5E02FA9764756E2164756E21",
15
+ "mappings": "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",
16
+ "debugId": "8A0DBFD4D82E137D64756E2164756E21",
17
17
  "names": []
18
18
  }
@@ -1,4 +1,4 @@
1
- import { type EntityData, type EntityMeta, 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 QueryDialect, type QueryExclude, type QueryFilter, type QueryGroupMap, type QueryGroupOp, type QueryHavingMap, type QueryOptions, type QueryPager, QueryRaw, type QueryRawFnOptions, type QuerySearch, type QuerySelectValue, type QuerySizeComparisonOps, type QuerySortMap, type QueryTextSearchOptions, type QueryVectorNear, type QueryWhere, type QueryWhereArray, type QueryWhereFieldOperatorMap, type QueryWhereOptions, type RelationMeta, type SqlDialectName, type SqlQueryDialect, type Type, type UpdatePayload } from '../type/index.js';
1
+ import { 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 QueryDialect, type QueryExclude, type QueryFilter, type QueryGroupMap, type QueryGroupOp, type QueryHavingMap, type QueryOptions, type QueryPager, QueryRaw, type QueryRawFnOptions, type QuerySearch, type QuerySelectValue, type QuerySizeComparisonOps, type QuerySortMap, type QueryTextSearchOptions, type QueryVectorNear, type QueryWhere, type QueryWhereArray, type QueryWhereFieldOperatorMap, type QueryWhereOptions, type RelationMeta, type SqlDialectName, type SqlQueryDialect, type Type, type UpdatePayload } from '../type/index.js';
2
2
  import { type ColumnFamily } from '../util/field.util.js';
3
3
  import type { HydrateKind } from './hydrateColumn.js';
4
4
  import { type JsonAccessMode } from './jsonSql.js';
@@ -128,7 +128,7 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
128
128
  */
129
129
  readonly maxFunctionArgs: number;
130
130
  getBeginTransactionStatements(isolationLevel?: IsolationLevel): string[];
131
- createContext(): QueryContext;
131
+ createContext(options?: QueryContextOptions): QueryContext;
132
132
  /**
133
133
  * Builds SQL text in isolation via `build`, so the caller can embed it inline (e.g.
134
134
  * `"col" = <text>`) instead of appending it at the end of `ctx`. The fragment binds any value
@@ -139,8 +139,8 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
139
139
  * shared for the same reason - see {@link SqlQueryContext}.
140
140
  */
141
141
  protected buildFragment(ctx: QueryContext, build: QueryBuildFn): string;
142
- /** A `raw()` operand, rendered in place: bound, it would reach the driver as the object itself. */
143
- protected rawFragment(ctx: QueryContext, value: QueryRaw): string;
142
+ /** A `raw()` rendered in place, an operand or a projected term: bound, the driver would get the object. */
143
+ protected rawFragment(ctx: QueryContext, value: QueryRaw, prefix?: string, entity?: Type<unknown>): string;
144
144
  /**
145
145
  * Each operand rendered into its own fragment, keeping only those that emitted SQL.
146
146
  *
@@ -150,7 +150,7 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
150
150
  * the caller counts what comes back rather than what it passed in.
151
151
  */
152
152
  protected renderOperands<T>(ctx: QueryContext, operands: readonly T[], render: (ctx: QueryContext, operand: T) => void): string[];
153
- addValue(values: unknown[], value: unknown): string;
153
+ addValue(ctx: QueryContext, value: unknown): string;
154
154
  /**
155
155
  * A parameter value as this engine's driver takes it: a boolean as the integer an engine with no
156
156
  * boolean type stores, and everything else as it is - a `bigint` included, which every driver here
@@ -188,8 +188,6 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
188
188
  selectTerms<E>(ctx: QueryContext, entity: Type<E>, select: QuerySelectValue<E> | undefined, opts?: SelectOptions, exclude?: QueryExclude<E>): SelectTerm[];
189
189
  /** One field's column, or the expression an inlined one stands for, as the projection reads it. */
190
190
  private fieldTerm;
191
- /** A `raw()` rendered where it stands, without the alias a projection writes for it. */
192
- private rawSql;
193
191
  /**
194
192
  * What follows `SELECT` before the projection. Empty everywhere but SQL Server, whose `FETCH` will
195
193
  * not take a zero and which spells "no rows" as `TOP (0)` instead.
@@ -649,6 +647,13 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
649
647
  protected abstract jsonPush(ctx: QueryContext, expr: string, push: Record<string, unknown>): string;
650
648
  /** Remove object keys. */
651
649
  protected abstract jsonUnset(ctx: QueryContext, expr: string, unset: readonly string[]): string;
650
+ /**
651
+ * The text of SQL a schema declares, as DDL carries it: values written as literals, and none left bound,
652
+ * since a `CREATE` statement has no placeholder to bind one into. An entity's SQL reads its fields, a
653
+ * predicate with no entity filter applied; a migration's is `raw` reading none.
654
+ */
655
+ compileDdl(sql: QueryRaw): string;
656
+ compileDdl<E>(sql: EntityWhereMeta<E>, entity: Type<E>): string;
652
657
  getRawValue(ctx: QueryContext, opts: QueryRawFnOptions & {
653
658
  value: QueryRaw;
654
659
  }): void;
@@ -83,8 +83,8 @@ export class AbstractSqlDialect extends VectorSqlDialect {
83
83
  // 'set-before' - MySQL/MariaDB pattern
84
84
  return [`SET TRANSACTION ISOLATION LEVEL ${level}`, this.beginTransactionCommand];
85
85
  }
86
- createContext() {
87
- return new SqlQueryContext(this);
86
+ createContext(options = {}) {
87
+ return new SqlQueryContext(this, [], undefined, options.inlineValues);
88
88
  }
89
89
  /**
90
90
  * Builds SQL text in isolation via `build`, so the caller can embed it inline (e.g.
@@ -100,9 +100,9 @@ export class AbstractSqlDialect extends VectorSqlDialect {
100
100
  build(fragmentCtx);
101
101
  return fragmentCtx.sql;
102
102
  }
103
- /** A `raw()` operand, rendered in place: bound, it would reach the driver as the object itself. */
104
- rawFragment(ctx, value) {
105
- return this.buildFragment(ctx, (fragmentCtx) => this.getRawValue(fragmentCtx, { value }));
103
+ /** A `raw()` rendered in place, an operand or a projected term: bound, the driver would get the object. */
104
+ rawFragment(ctx, value, prefix, entity) {
105
+ return this.buildFragment(ctx, (fragmentCtx) => this.getRawValue(fragmentCtx, { value, prefix, entity }));
106
106
  }
107
107
  /**
108
108
  * Each operand rendered into its own fragment, keeping only those that emitted SQL.
@@ -117,9 +117,15 @@ export class AbstractSqlDialect extends VectorSqlDialect {
117
117
  .map((operand) => this.buildFragment(ctx, (fragmentCtx) => render(fragmentCtx, operand)))
118
118
  .filter((part) => part !== '');
119
119
  }
120
- addValue(values, value) {
121
- values.push(this.normalizeValue(value));
122
- return this.placeholder(values.length);
120
+ addValue(ctx, value) {
121
+ if (value instanceof QueryRaw) {
122
+ return this.rawFragment(ctx, value);
123
+ }
124
+ if (ctx.inlineValues) {
125
+ return this.escape(this.normalizeValue(value));
126
+ }
127
+ ctx.values.push(this.normalizeValue(value));
128
+ return this.placeholder(ctx.values.length);
123
129
  }
124
130
  /**
125
131
  * A parameter value as this engine's driver takes it: a boolean as the integer an engine with no
@@ -196,7 +202,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
196
202
  return [{ sql: `${this.escapeId(opts.prefix, true, true)}*`, bare: true }];
197
203
  }
198
204
  return keys.map((key) => key instanceof QueryRaw
199
- ? { sql: this.rawSql(ctx, key, opts.prefix), key: key[RAW_ALIAS] }
205
+ ? { sql: this.rawFragment(ctx, key, opts.prefix), key: key[RAW_ALIAS] }
200
206
  : this.fieldTerm(ctx, meta, key, opts));
201
207
  }
202
208
  /** One field's column, or the expression an inlined one stands for, as the projection reads it. */
@@ -206,7 +212,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
206
212
  // Qualified even when nothing else in this statement is: the expression is spliced in, and one
207
213
  // that opens a correlated subquery has the inner table's columns in scope, so a bare `"id"`
208
214
  // would bind to *that* table instead of this one.
209
- const sql = this.rawSql(ctx, field.computed, opts.prefix ?? this.resolveTableAlias(meta));
215
+ const sql = this.rawFragment(ctx, field.computed, opts.prefix ?? this.resolveTableAlias(meta), meta.entity);
210
216
  return { sql: opts.json ? this.carried(`(${sql})`, field) : sql, key };
211
217
  }
212
218
  const columnName = this.resolveColumnName(key, field);
@@ -214,15 +220,6 @@ export class AbstractSqlDialect extends VectorSqlDialect {
214
220
  const sql = opts.json ? this.carried(column, field) : this.selectFieldExpr(column, field);
215
221
  return { sql, key, bare: sql === column && columnName === key };
216
222
  }
217
- /** A `raw()` rendered where it stands, without the alias a projection writes for it. */
218
- rawSql(ctx, value, prefix) {
219
- return this.buildFragment(ctx, (fragmentCtx) => value.render({
220
- ctx: fragmentCtx,
221
- dialect: this,
222
- prefix: prefix ?? '',
223
- escapedPrefix: this.escapeId(prefix, true, true),
224
- }));
225
- }
226
223
  /**
227
224
  * What follows `SELECT` before the projection. Empty everywhere but SQL Server, whose `FETCH` will
228
225
  * not take a zero and which spells "no rows" as `TOP (0)` instead.
@@ -425,7 +422,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
425
422
  }
426
423
  this.getComparisonKey(ctx, entity, key, opts);
427
424
  ctx.append(' = ');
428
- this.getRawValue(ctx, { value: val });
425
+ this.getRawValue(ctx, { value: val, prefix: opts.prefix });
429
426
  return;
430
427
  }
431
428
  if (key === '$text') {
@@ -479,7 +476,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
479
476
  const childOperand = items.length > 1 || negate || opts.operand;
480
477
  const parts = this.renderOperands(ctx, items, (fragmentCtx, entry) => {
481
478
  if (entry instanceof QueryRaw) {
482
- this.getRawValue(fragmentCtx, { value: entry });
479
+ this.getRawValue(fragmentCtx, { value: entry, prefix: opts.prefix });
483
480
  }
484
481
  else {
485
482
  this.renderWhere(fragmentCtx, entity, entry, { prefix: opts.prefix, operand: childOperand, clause: false });
@@ -551,7 +548,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
551
548
  }
552
549
  const fold = like.insensitive && this.caseInsensitiveMatch === 'fold';
553
550
  const value = String(val);
554
- const ph = this.addValue(ctx.values, like.pattern(fold ? value.toLowerCase() : value));
551
+ const ph = this.addValue(ctx, like.pattern(fold ? value.toLowerCase() : value));
555
552
  const matchOp = like.insensitive && this.caseInsensitiveMatch === 'ilike' ? 'ILIKE' : this.likeFn;
556
553
  return `${fold ? `LOWER(${operand})` : operand} ${matchOp} ${ph}`;
557
554
  }
@@ -567,7 +564,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
567
564
  resolveOperandField(ctx, entity, key, opts) {
568
565
  const meta = getMeta(entity);
569
566
  const field = meta.fields[key];
570
- return (this.inlinedOperand(ctx, field, opts.prefix ?? this.resolveTableAlias(meta)) ??
567
+ return (this.inlinedOperand(ctx, field, opts.prefix ?? this.resolveTableAlias(meta), entity) ??
571
568
  this.columnWithPrefix(key, field, opts.prefix));
572
569
  }
573
570
  /**
@@ -576,15 +573,9 @@ export class AbstractSqlDialect extends VectorSqlDialect {
576
573
  * Every clause that names such a field needs the expression itself, never the output alias: an
577
574
  * alias exists only when the field was also selected, which `$where` and `$sort` cannot assume.
578
575
  */
579
- inlinedOperand(ctx, field, prefix) {
576
+ inlinedOperand(ctx, field, prefix, entity) {
580
577
  const inlined = field && isInlinedExpression(field) ? field.computed : undefined;
581
- return inlined
582
- ? this.buildFragment(ctx, (fragmentCtx) => this.getRawValue(fragmentCtx, {
583
- value: inlined,
584
- prefix,
585
- escapedPrefix: this.escapeId(prefix, true, true),
586
- }))
587
- : undefined;
578
+ return inlined ? this.rawFragment(ctx, inlined, prefix, entity) : undefined;
588
579
  }
589
580
  compareFieldOperator(ctx, entity, key, op, val, opts = {}) {
590
581
  const field = this.resolveOperandField(ctx, entity, key, opts);
@@ -629,7 +620,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
629
620
  operatorCondition(ctx, operand, op, val) {
630
621
  const compareOp = AbstractSqlDialect.COMPARE_OP_MAP.get(op);
631
622
  if (compareOp) {
632
- return `${operand}${compareOp}${this.addValue(ctx.values, val)}`;
623
+ return `${operand}${compareOp}${this.addValue(ctx, val)}`;
633
624
  }
634
625
  const like = this.likeCondition(ctx, operand, op, val);
635
626
  if (like) {
@@ -637,17 +628,17 @@ export class AbstractSqlDialect extends VectorSqlDialect {
637
628
  }
638
629
  switch (op) {
639
630
  case '$eq':
640
- return val === null ? `${operand} IS NULL` : `${operand} = ${this.addValue(ctx.values, val)}`;
631
+ return val === null ? `${operand} IS NULL` : `${operand} = ${this.addValue(ctx, val)}`;
641
632
  case '$ne':
642
- return val === null ? `${operand} IS NOT NULL` : this.neExpr(operand, this.addValue(ctx.values, val));
633
+ return val === null ? `${operand} IS NOT NULL` : this.neExpr(operand, this.addValue(ctx, val));
643
634
  case '$regex':
644
- return this.regexCondition(operand, this.addValue(ctx.values, val));
635
+ return this.regexCondition(operand, this.addValue(ctx, val));
645
636
  case '$in':
646
637
  case '$nin':
647
638
  return operand + this.formatIn(ctx, inOperands(op, val), op === '$nin');
648
639
  case '$between': {
649
640
  const [min, max] = val;
650
- return `${operand} BETWEEN ${this.addValue(ctx.values, min)} AND ${this.addValue(ctx.values, max)}`;
641
+ return `${operand} BETWEEN ${this.addValue(ctx, min)} AND ${this.addValue(ctx, max)}`;
651
642
  }
652
643
  case '$isNull':
653
644
  return operand + (val ? ' IS NULL' : ' IS NOT NULL');
@@ -685,7 +676,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
685
676
  // operator out of the same table a comparison against a column does.
686
677
  const compareOp = AbstractSqlDialect.COMPARE_OP_MAP.get(op);
687
678
  if (compareOp) {
688
- return `${fieldAccessor(jsonPath, 'numeric')}${compareOp}${this.addValue(ctx.values, value)}`;
679
+ return `${fieldAccessor(jsonPath, 'numeric')}${compareOp}${this.addValue(ctx, value)}`;
689
680
  }
690
681
  switch (op) {
691
682
  case '$eq':
@@ -697,7 +688,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
697
688
  return `${jsonField} IS NOT NULL`;
698
689
  return this.neExpr(comparand(value), this.jsonOperand(ctx, value, asJson));
699
690
  case '$regex':
700
- return this.regexCondition(jsonField, this.addValue(ctx.values, value));
691
+ return this.regexCondition(jsonField, this.addValue(ctx, value));
701
692
  case '$in':
702
693
  case '$nin':
703
694
  return this.jsonInNin(ctx, jsonField, comparand, op, value, asJson);
@@ -723,7 +714,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
723
714
  }
724
715
  /** The bound operand of a JSON comparison: JSON-encoded when comparing against the JSON value. */
725
716
  jsonOperand(ctx, value, asJson) {
726
- return asJson ? this.jsonScalarParam(ctx, value) : this.addValue(ctx.values, value);
717
+ return asJson ? this.jsonScalarParam(ctx, value) : this.addValue(ctx, value);
727
718
  }
728
719
  /**
729
720
  * Whether the dialect's array containment ({@link jsonAll}) matches an object element that merely
@@ -790,9 +781,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
790
781
  if (value instanceof QueryRaw) {
791
782
  return this.rawFragment(ctx, value);
792
783
  }
793
- ctx.pushValue(JSON.stringify(value));
794
- // The placeholder for the value just pushed, so a named or numbered one is spelled correctly.
795
- return this.jsonCast(this.placeholder(ctx.values.length));
784
+ return this.jsonCast(this.addValue(ctx, JSON.stringify(value)));
796
785
  }
797
786
  /** {@link resolveOperandField}, appended. */
798
787
  getComparisonKey(ctx, entity, key, opts = {}) {
@@ -886,7 +875,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
886
875
  sortColumn(ctx, meta, key, prefix) {
887
876
  const field = meta.fields[key];
888
877
  if (field) {
889
- const expr = this.inlinedOperand(ctx, field, prefix ?? this.resolveTableAlias(meta)) ??
878
+ const expr = this.inlinedOperand(ctx, field, prefix ?? this.resolveTableAlias(meta), meta.entity) ??
890
879
  this.columnWithPrefix(key, field, prefix);
891
880
  return { expr, output: false };
892
881
  }
@@ -1210,7 +1199,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1210
1199
  if (value === undefined) {
1211
1200
  this.appendDefaultInsertValue(ctx, fields[i]);
1212
1201
  }
1213
- else if (kinds[i] === 'plain' && !(value instanceof QueryRaw)) {
1202
+ else if (kinds[i] === 'plain') {
1214
1203
  // The overwhelmingly common case in a bulk insert, so it binds without a dispatch.
1215
1204
  ctx.addValue(value);
1216
1205
  }
@@ -1531,6 +1520,22 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1531
1520
  jsonPull(ctx, expr, escapedCol, pull) {
1532
1521
  return Object.entries(pull).reduce((acc, [key, value]) => this.jsonPullKey(ctx, acc, escapedCol, key, value), expr);
1533
1522
  }
1523
+ compileDdl(sql, entity) {
1524
+ const ctx = this.createContext({ inlineValues: true });
1525
+ if (sql instanceof QueryRaw) {
1526
+ sql.render({ ctx, dialect: this, prefix: '', escapedPrefix: '', entity });
1527
+ }
1528
+ else if (entity) {
1529
+ this.renderWhere(ctx, entity, sql, { clause: false });
1530
+ }
1531
+ else {
1532
+ throw new TypeError('a predicate compiles against the entity it is written for, and none was given');
1533
+ }
1534
+ if (ctx.values.length) {
1535
+ throw new TypeError(`DDL has no placeholder to bind a value into, and this SQL left one bound: ${ctx.sql}`);
1536
+ }
1537
+ return ctx.sql;
1538
+ }
1534
1539
  getRawValue(ctx, opts) {
1535
1540
  const { value, prefix = '', escapedPrefix } = opts;
1536
1541
  value.render({
@@ -1540,10 +1545,6 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1540
1545
  prefix,
1541
1546
  escapedPrefix: escapedPrefix ?? this.escapeId(prefix, true, true),
1542
1547
  });
1543
- const alias = value[RAW_ALIAS];
1544
- if (alias) {
1545
- ctx.append(' ' + this.escapeId(alias, true));
1546
- }
1547
1548
  }
1548
1549
  /**
1549
1550
  * Resolves a dot-notation key to its JSON field metadata.
@@ -1917,7 +1918,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1917
1918
  // A COUNT is never NULL, so equality stays plain here instead of taking the shared renderer's
1918
1919
  // null-safe `$ne` (`IS DISTINCT FROM` on Postgres, `IS NOT` on SQLite). Same rows, shorter SQL.
1919
1920
  if (op === '$eq' || op === '$ne') {
1920
- ctx.append(` ${op === '$eq' ? '=' : '<>'} ${this.addValue(ctx.values, val)}`);
1921
+ ctx.append(` ${op === '$eq' ? '=' : '<>'} ${this.addValue(ctx, val)}`);
1921
1922
  return;
1922
1923
  }
1923
1924
  this.appendOperatorCondition(ctx, '', op, val);
@@ -1958,7 +1959,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1958
1959
  formatIn(ctx, values, negate) {
1959
1960
  if (values.length === 0)
1960
1961
  return negate ? ' NOT IN (NULL)' : ' IN (NULL)';
1961
- const phs = values.map((v) => this.addValue(ctx.values, v)).join(', ');
1962
+ const phs = values.map((v) => this.addValue(ctx, v)).join(', ');
1962
1963
  return ` ${negate ? 'NOT IN' : 'IN'} (${phs})`;
1963
1964
  }
1964
1965
  toString() {
@@ -247,7 +247,7 @@ export class MysqlLikeSqlDialect extends AbstractSqlDialect {
247
247
  return `${escapedColumn}->${jsonPath(jsonPathStr)}`;
248
248
  }
249
249
  jsonAll(ctx, jsonField, value) {
250
- return `JSON_CONTAINS(${jsonField}, ${this.addValue(ctx.values, JSON.stringify(value))})`;
250
+ return `JSON_CONTAINS(${jsonField}, ${this.addValue(ctx, JSON.stringify(value))})`;
251
251
  }
252
252
  jsonSize(ctx, jsonField, value) {
253
253
  return this.buildFragment(ctx, (fragmentCtx) => this.buildSizeComparison(fragmentCtx, () => fragmentCtx.append(`JSON_LENGTH(${jsonField})`), value));
@@ -80,6 +80,7 @@ export declare abstract class PgLikeSqlDialect extends AbstractSqlDialect {
80
80
  protected get regexpOp(): string;
81
81
  protected readonly caseInsensitiveMatch = "ilike";
82
82
  protected get neOp(): string;
83
+ /** One array parameter, which a context that inlines values has none of: it lists them instead. */
83
84
  protected formatIn(ctx: QueryContext, values: unknown[], negate: boolean): string;
84
85
  protected numericCast(expr: string): string;
85
86
  protected appendJsonValue(ctx: QueryContext, value: unknown, type: JsonColumnType): void;