@lunora/sql-store 1.0.0-alpha.47 → 1.0.0-alpha.49

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.
@@ -77,6 +77,49 @@ interface SqlDialect {
77
77
  isUniqueViolation: (error: unknown) => boolean;
78
78
  /** A short engine tag for diagnostics/branching (`"sqlite" | "postgres" | "mysql"`). The store core selects drizzle's matching dialect for rendering off this. */
79
79
  readonly name: "mysql" | "postgres" | "sqlite";
80
+ /**
81
+ * Optional: the engine's own full-text index, opted into per search index
82
+ * with `.searchIndex({ strategy: "native" })`.
83
+ *
84
+ * Only Postgres supplies one today (`tsvector` + GIN + `to_tsquery`). It
85
+ * scales sublinearly where the portable inverted companion aggregates every
86
+ * matching token row, but it ranks with the engine's formula rather than the
87
+ * shared scorer — which is why it is opt-in and why the parity suite asserts
88
+ * matching, not order, for indexes that use it.
89
+ *
90
+ * Recall still matches the portable path: the stored form is built from the
91
+ * tokens Lunora's analyzer already produced, under a configuration that adds
92
+ * no stemming or stopwords of the engine's own.
93
+ *
94
+ * Every member returns a *statement*, not a fragment, so no engine grammar
95
+ * reaches the store core: Postgres matches with `@@` against a `tsvector`
96
+ * column while MySQL would use `MATCH … AGAINST` against a text column, and
97
+ * both fit here without the caller knowing which.
98
+ */
99
+ nativeTextSearch?: {
100
+ /** DDL for the companion table holding the engine's indexed form, keyed by document id. */
101
+ createCompanion: (companion: string, keyType: string) => SQL;
102
+ /** DDL for the indexes that make the match fast. */
103
+ createIndexes: (companion: string) => SQL[];
104
+ /** Replace one document's row, given its already-analyzed token stream. */
105
+ indexDocument: (companion: string, id: string, analyzed: string) => SQL;
106
+ /** The `WHERE` predicate matching a query's analyzed terms, final term as a prefix. */
107
+ matches: (companion: string, terms: ReadonlyArray<string>) => SQL;
108
+ /** The `ORDER BY` expression, best first. */
109
+ rank: (companion: string, terms: ReadonlyArray<string>) => SQL;
110
+ };
111
+ /**
112
+ * True when the engine ships SQLite's FTS5 module, which decides whether a
113
+ * search index is stored as an FTS5 shadow or as the portable inverted
114
+ * companion.
115
+ *
116
+ * A static property of the engine, so it is declared rather than probed:
117
+ * the previous `CREATE VIRTUAL TABLE` capability probe spent a round trip
118
+ * (and an error in the database's log) on every fresh connection to
119
+ * rediscover something the dialect already knows. Tests that want the
120
+ * portable layout override this instead of intercepting SQL strings.
121
+ */
122
+ supportsFts5: boolean;
80
123
  /** True when the engine supports `UPDATE/DELETE ... RETURNING` (SQLite/PG yes, MySQL no → use `affectedRows`). */
81
124
  supportsReturning: boolean;
82
125
  /**
@@ -88,5 +131,14 @@ interface SqlDialect {
88
131
  * `information_schema.tables`.
89
132
  */
90
133
  tableExists: (table: string) => SQL;
134
+ /**
135
+ * Optional: btree operator class appended to an indexed text column so a
136
+ * `LIKE 'prefix%'` scan can use the index whatever the database collation.
137
+ * Postgres needs `text_pattern_ops` (a default `text_ops` btree built under
138
+ * e.g. `en_US.UTF-8` is useless to `LIKE`); SQLite and MySQL index prefix
139
+ * matches off the plain index and omit this. Only the search companion's
140
+ * token index reads it.
141
+ */
142
+ textPatternOperatorClass?: string;
91
143
  }
92
144
  export { SqlDialect, SqlExec, SqlRunResult };
package/dist/dialect.d.ts CHANGED
@@ -77,6 +77,49 @@ interface SqlDialect {
77
77
  isUniqueViolation: (error: unknown) => boolean;
78
78
  /** A short engine tag for diagnostics/branching (`"sqlite" | "postgres" | "mysql"`). The store core selects drizzle's matching dialect for rendering off this. */
79
79
  readonly name: "mysql" | "postgres" | "sqlite";
80
+ /**
81
+ * Optional: the engine's own full-text index, opted into per search index
82
+ * with `.searchIndex({ strategy: "native" })`.
83
+ *
84
+ * Only Postgres supplies one today (`tsvector` + GIN + `to_tsquery`). It
85
+ * scales sublinearly where the portable inverted companion aggregates every
86
+ * matching token row, but it ranks with the engine's formula rather than the
87
+ * shared scorer — which is why it is opt-in and why the parity suite asserts
88
+ * matching, not order, for indexes that use it.
89
+ *
90
+ * Recall still matches the portable path: the stored form is built from the
91
+ * tokens Lunora's analyzer already produced, under a configuration that adds
92
+ * no stemming or stopwords of the engine's own.
93
+ *
94
+ * Every member returns a *statement*, not a fragment, so no engine grammar
95
+ * reaches the store core: Postgres matches with `@@` against a `tsvector`
96
+ * column while MySQL would use `MATCH … AGAINST` against a text column, and
97
+ * both fit here without the caller knowing which.
98
+ */
99
+ nativeTextSearch?: {
100
+ /** DDL for the companion table holding the engine's indexed form, keyed by document id. */
101
+ createCompanion: (companion: string, keyType: string) => SQL;
102
+ /** DDL for the indexes that make the match fast. */
103
+ createIndexes: (companion: string) => SQL[];
104
+ /** Replace one document's row, given its already-analyzed token stream. */
105
+ indexDocument: (companion: string, id: string, analyzed: string) => SQL;
106
+ /** The `WHERE` predicate matching a query's analyzed terms, final term as a prefix. */
107
+ matches: (companion: string, terms: ReadonlyArray<string>) => SQL;
108
+ /** The `ORDER BY` expression, best first. */
109
+ rank: (companion: string, terms: ReadonlyArray<string>) => SQL;
110
+ };
111
+ /**
112
+ * True when the engine ships SQLite's FTS5 module, which decides whether a
113
+ * search index is stored as an FTS5 shadow or as the portable inverted
114
+ * companion.
115
+ *
116
+ * A static property of the engine, so it is declared rather than probed:
117
+ * the previous `CREATE VIRTUAL TABLE` capability probe spent a round trip
118
+ * (and an error in the database's log) on every fresh connection to
119
+ * rediscover something the dialect already knows. Tests that want the
120
+ * portable layout override this instead of intercepting SQL strings.
121
+ */
122
+ supportsFts5: boolean;
80
123
  /** True when the engine supports `UPDATE/DELETE ... RETURNING` (SQLite/PG yes, MySQL no → use `affectedRows`). */
81
124
  supportsReturning: boolean;
82
125
  /**
@@ -88,5 +131,14 @@ interface SqlDialect {
88
131
  * `information_schema.tables`.
89
132
  */
90
133
  tableExists: (table: string) => SQL;
134
+ /**
135
+ * Optional: btree operator class appended to an indexed text column so a
136
+ * `LIKE 'prefix%'` scan can use the index whatever the database collation.
137
+ * Postgres needs `text_pattern_ops` (a default `text_ops` btree built under
138
+ * e.g. `en_US.UTF-8` is useless to `LIKE`); SQLite and MySQL index prefix
139
+ * matches off the plain index and omit this. Only the search companion's
140
+ * token index reads it.
141
+ */
142
+ textPatternOperatorClass?: string;
91
143
  }
92
144
  export { SqlDialect, SqlExec, SqlRunResult };
package/dist/index.d.mts CHANGED
@@ -1,7 +1,8 @@
1
- import { ServerDefaultContextLike, DatabaseWriterLike, SchedulerLike, SchemaLike, TableDefinitionLike, ValidatorLike } from '@lunora/do';
2
- import { SqlDialect, SqlRunResult } from "./dialect.mjs";
1
+ import { TableDefinitionLike, SchemaLike, ServerDefaultContextLike, DatabaseWriterLike, SchedulerLike, ValidatorLike } from '@lunora/do';
2
+ import { SqlRunResult, SqlDialect } from "./dialect.mjs";
3
3
  export type { SqlExec } from "./dialect.mjs";
4
4
  import 'drizzle-orm';
5
+ import '@lunora/search-core';
5
6
  /**
6
7
  * Async SQL surface the D1 ORM needs: `all` for reads, `run` for writes.
7
8
  * Satisfied by a `D1Session`/`D1Client` in production and a `node:sqlite`
@@ -11,6 +12,38 @@ interface SqlCtxExec {
11
12
  all: (sql: string, parameters: ReadonlyArray<unknown>) => Promise<Record<string, unknown>[]>;
12
13
  run: (sql: string, parameters: ReadonlyArray<unknown>) => Promise<SqlRunResult | void>;
13
14
  }
15
+ /**
16
+ * Decode a SELECTed row back into a document: `id` → `_id`, `_creationTime`
17
+ * preserved, and every column run through the shared {@link sqliteDecode} so the
18
+ * stored form is reversed back into its JS shape. Exported so the data-browser
19
+ * (`introspect.ts`) and admin export/import paths share the exact same decode.
20
+ *
21
+ * The decode is engine-agnostic: every backend stores SQLite-shaped values
22
+ * (boolean → 1/0, JSON → text, bigint → decimal string), and `sqliteDecode` is
23
+ * robust to a driver returning either the stored string OR a natively-parsed
24
+ * value (e.g. mysql2 returns JSON columns pre-parsed) — so the same decoder is
25
+ * correct on SQLite, Postgres and MySQL.
26
+ */
27
+ declare const decodeGlobalRow: (definition: TableDefinitionLike, row: Record<string, unknown>) => Record<string, unknown>;
28
+ /**
29
+ * Provision the search companions, then index one bounded page of the rows that
30
+ * predate each index — unless it is declared `staged: true`, which leaves the
31
+ * whole backfill to {@link backfillSqlSearchIndexes}.
32
+ *
33
+ * Idempotent (`CREATE … IF NOT EXISTS` throughout, and the backfill resumes
34
+ * from recorded progress).
35
+ */
36
+ declare const runSqlSearchMigrations: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<void>;
37
+ /**
38
+ * Run every declared search index — including the `staged: true` ones the
39
+ * migration pass skips — through to completion. The entry point a host calls
40
+ * out-of-band after deploying a search index over a table too large to index a
41
+ * page at a time.
42
+ *
43
+ * Idempotent and resumable: an index recorded as complete is skipped, and an
44
+ * interrupted run picks up from its cursor.
45
+ */
46
+ declare const backfillSqlSearchIndexes: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<void>;
14
47
  interface SqlCtxDbOptions {
15
48
  /**
16
49
  * Resolved request auth handed to `.serverDefault(fn)` column factories so
@@ -73,19 +106,6 @@ interface SqlCtxDbOptions {
73
106
  scheduler?: SchedulerLike;
74
107
  schema: SchemaLike;
75
108
  }
76
- /**
77
- * Decode a SELECTed row back into a document: `id` → `_id`, `_creationTime`
78
- * preserved, and every column run through the shared {@link sqliteDecode} so the
79
- * stored form is reversed back into its JS shape. Exported so the data-browser
80
- * (`introspect.ts`) and admin export/import paths share the exact same decode.
81
- *
82
- * The decode is engine-agnostic: every backend stores SQLite-shaped values
83
- * (boolean → 1/0, JSON → text, bigint → decimal string), and `sqliteDecode` is
84
- * robust to a driver returning either the stored string OR a natively-parsed
85
- * value (e.g. mysql2 returns JSON columns pre-parsed) — so the same decoder is
86
- * correct on SQLite, Postgres and MySQL.
87
- */
88
- declare const decodeGlobalRow: (definition: TableDefinitionLike, row: Record<string, unknown>) => Record<string, unknown>;
89
109
  declare const runSqlGlobalTableMigrations: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<void>;
90
110
  /**
91
111
  * Materialize the `__agg_&lt;index>` companion tables for every declared
@@ -105,17 +125,6 @@ declare const runSqlAggregateMigrations: (exec: SqlCtxExec, schema: SchemaLike,
105
125
  * Idempotent (`CREATE TABLE IF NOT EXISTS` + `createIndexIfNotExists`).
106
126
  */
107
127
  declare const runSqlRankMigrations: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<void>;
108
- /**
109
- * Materialize the `__fts_&lt;index>` FTS5 shadow tables for every declared
110
- * `.searchIndex()` on a global table. Mirrors `runSqlAggregateMigrations` — same
111
- * opt-in pattern so production hosts decide whether to spend the DDL. Only runs
112
- * on engines that ship FTS5 (D1 does; the `node:sqlite` test runner doesn't,
113
- * where `.search()` transparently falls back to a scan). `__text__` holds the
114
- * indexed field; `__id__` (UNINDEXED) joins back to the row.
115
- *
116
- * Idempotent (`CREATE VIRTUAL TABLE IF NOT EXISTS`).
117
- */
118
- declare const runSqlSearchMigrations: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<void>;
119
128
  /** One change-data-capture entry: a committed mutation, in monotonic `seq` order. Mirrors the DO twin. */
120
129
  interface CdcChange {
121
130
  /** Post-image document for insert/update; absent for delete (the `id` identifies the removed row). */
@@ -144,6 +153,10 @@ declare const readSqlCdcChanges: (exec: SqlCtxExec, options: {
144
153
  /** Drop changelog entries at or below a checkpointed `throughSeq` (retention). */
145
154
  declare const trimSqlCdcChanges: (exec: SqlCtxExec, throughSeq: number, dialect: SqlDialect) => Promise<void>;
146
155
  declare const createSqlCtxDb: (options: SqlCtxDbOptions) => DatabaseWriterLike;
156
+ /** Reserved table holding one backfill-progress row per search companion. */
157
+ declare const SEARCH_STATE_TABLE = "__lunora_search_state";
158
+ /** Create the progress table. Idempotent; runs alongside the companion DDL. */
159
+ declare const migrateSearchState: (exec: SqlCtxExec, dialect: SqlDialect) => Promise<void>;
147
160
  /** Map a JS value onto its SQLite storage form — SQLite has no boolean, so true/false → 1/0. */
148
161
  declare const sqliteEncode: (value: unknown) => unknown;
149
162
  /** Parse `raw` as JSON, returning `raw` unchanged when it is not valid JSON. */
@@ -177,4 +190,4 @@ declare const effectiveColumnKind: (validator: ValidatorLike) => string | undefi
177
190
  * - everything else (string/number/date/timestamp/id/literal): verbatim.
178
191
  */
179
192
  declare const sqliteDecode: (raw: unknown, kind: string | undefined) => unknown;
180
- export { type SqlCtxDbOptions, type SqlCtxExec, type SqlDialect, type SqlRunResult, createSqlCtxDb, decodeBigint, decodeGlobalRow, effectiveColumnKind, readSqlCdcChanges, runSqlAggregateMigrations, runSqlCdcMigration, runSqlGlobalTableMigrations, runSqlRankMigrations, runSqlSearchMigrations, sqliteDecode, sqliteEncode, trimSqlCdcChanges, tryJsonParse };
193
+ export { SEARCH_STATE_TABLE, type SqlCtxDbOptions, type SqlCtxExec, type SqlDialect, type SqlRunResult, backfillSqlSearchIndexes, createSqlCtxDb, decodeBigint, decodeGlobalRow, effectiveColumnKind, migrateSearchState, readSqlCdcChanges, runSqlAggregateMigrations, runSqlCdcMigration, runSqlGlobalTableMigrations, runSqlRankMigrations, runSqlSearchMigrations, sqliteDecode, sqliteEncode, trimSqlCdcChanges, tryJsonParse };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { ServerDefaultContextLike, DatabaseWriterLike, SchedulerLike, SchemaLike, TableDefinitionLike, ValidatorLike } from '@lunora/do';
2
- import { SqlDialect, SqlRunResult } from "./dialect.js";
1
+ import { TableDefinitionLike, SchemaLike, ServerDefaultContextLike, DatabaseWriterLike, SchedulerLike, ValidatorLike } from '@lunora/do';
2
+ import { SqlRunResult, SqlDialect } from "./dialect.js";
3
3
  export type { SqlExec } from "./dialect.js";
4
4
  import 'drizzle-orm';
5
+ import '@lunora/search-core';
5
6
  /**
6
7
  * Async SQL surface the D1 ORM needs: `all` for reads, `run` for writes.
7
8
  * Satisfied by a `D1Session`/`D1Client` in production and a `node:sqlite`
@@ -11,6 +12,38 @@ interface SqlCtxExec {
11
12
  all: (sql: string, parameters: ReadonlyArray<unknown>) => Promise<Record<string, unknown>[]>;
12
13
  run: (sql: string, parameters: ReadonlyArray<unknown>) => Promise<SqlRunResult | void>;
13
14
  }
15
+ /**
16
+ * Decode a SELECTed row back into a document: `id` → `_id`, `_creationTime`
17
+ * preserved, and every column run through the shared {@link sqliteDecode} so the
18
+ * stored form is reversed back into its JS shape. Exported so the data-browser
19
+ * (`introspect.ts`) and admin export/import paths share the exact same decode.
20
+ *
21
+ * The decode is engine-agnostic: every backend stores SQLite-shaped values
22
+ * (boolean → 1/0, JSON → text, bigint → decimal string), and `sqliteDecode` is
23
+ * robust to a driver returning either the stored string OR a natively-parsed
24
+ * value (e.g. mysql2 returns JSON columns pre-parsed) — so the same decoder is
25
+ * correct on SQLite, Postgres and MySQL.
26
+ */
27
+ declare const decodeGlobalRow: (definition: TableDefinitionLike, row: Record<string, unknown>) => Record<string, unknown>;
28
+ /**
29
+ * Provision the search companions, then index one bounded page of the rows that
30
+ * predate each index — unless it is declared `staged: true`, which leaves the
31
+ * whole backfill to {@link backfillSqlSearchIndexes}.
32
+ *
33
+ * Idempotent (`CREATE … IF NOT EXISTS` throughout, and the backfill resumes
34
+ * from recorded progress).
35
+ */
36
+ declare const runSqlSearchMigrations: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<void>;
37
+ /**
38
+ * Run every declared search index — including the `staged: true` ones the
39
+ * migration pass skips — through to completion. The entry point a host calls
40
+ * out-of-band after deploying a search index over a table too large to index a
41
+ * page at a time.
42
+ *
43
+ * Idempotent and resumable: an index recorded as complete is skipped, and an
44
+ * interrupted run picks up from its cursor.
45
+ */
46
+ declare const backfillSqlSearchIndexes: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<void>;
14
47
  interface SqlCtxDbOptions {
15
48
  /**
16
49
  * Resolved request auth handed to `.serverDefault(fn)` column factories so
@@ -73,19 +106,6 @@ interface SqlCtxDbOptions {
73
106
  scheduler?: SchedulerLike;
74
107
  schema: SchemaLike;
75
108
  }
76
- /**
77
- * Decode a SELECTed row back into a document: `id` → `_id`, `_creationTime`
78
- * preserved, and every column run through the shared {@link sqliteDecode} so the
79
- * stored form is reversed back into its JS shape. Exported so the data-browser
80
- * (`introspect.ts`) and admin export/import paths share the exact same decode.
81
- *
82
- * The decode is engine-agnostic: every backend stores SQLite-shaped values
83
- * (boolean → 1/0, JSON → text, bigint → decimal string), and `sqliteDecode` is
84
- * robust to a driver returning either the stored string OR a natively-parsed
85
- * value (e.g. mysql2 returns JSON columns pre-parsed) — so the same decoder is
86
- * correct on SQLite, Postgres and MySQL.
87
- */
88
- declare const decodeGlobalRow: (definition: TableDefinitionLike, row: Record<string, unknown>) => Record<string, unknown>;
89
109
  declare const runSqlGlobalTableMigrations: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<void>;
90
110
  /**
91
111
  * Materialize the `__agg_&lt;index>` companion tables for every declared
@@ -105,17 +125,6 @@ declare const runSqlAggregateMigrations: (exec: SqlCtxExec, schema: SchemaLike,
105
125
  * Idempotent (`CREATE TABLE IF NOT EXISTS` + `createIndexIfNotExists`).
106
126
  */
107
127
  declare const runSqlRankMigrations: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<void>;
108
- /**
109
- * Materialize the `__fts_&lt;index>` FTS5 shadow tables for every declared
110
- * `.searchIndex()` on a global table. Mirrors `runSqlAggregateMigrations` — same
111
- * opt-in pattern so production hosts decide whether to spend the DDL. Only runs
112
- * on engines that ship FTS5 (D1 does; the `node:sqlite` test runner doesn't,
113
- * where `.search()` transparently falls back to a scan). `__text__` holds the
114
- * indexed field; `__id__` (UNINDEXED) joins back to the row.
115
- *
116
- * Idempotent (`CREATE VIRTUAL TABLE IF NOT EXISTS`).
117
- */
118
- declare const runSqlSearchMigrations: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<void>;
119
128
  /** One change-data-capture entry: a committed mutation, in monotonic `seq` order. Mirrors the DO twin. */
120
129
  interface CdcChange {
121
130
  /** Post-image document for insert/update; absent for delete (the `id` identifies the removed row). */
@@ -144,6 +153,10 @@ declare const readSqlCdcChanges: (exec: SqlCtxExec, options: {
144
153
  /** Drop changelog entries at or below a checkpointed `throughSeq` (retention). */
145
154
  declare const trimSqlCdcChanges: (exec: SqlCtxExec, throughSeq: number, dialect: SqlDialect) => Promise<void>;
146
155
  declare const createSqlCtxDb: (options: SqlCtxDbOptions) => DatabaseWriterLike;
156
+ /** Reserved table holding one backfill-progress row per search companion. */
157
+ declare const SEARCH_STATE_TABLE = "__lunora_search_state";
158
+ /** Create the progress table. Idempotent; runs alongside the companion DDL. */
159
+ declare const migrateSearchState: (exec: SqlCtxExec, dialect: SqlDialect) => Promise<void>;
147
160
  /** Map a JS value onto its SQLite storage form — SQLite has no boolean, so true/false → 1/0. */
148
161
  declare const sqliteEncode: (value: unknown) => unknown;
149
162
  /** Parse `raw` as JSON, returning `raw` unchanged when it is not valid JSON. */
@@ -177,4 +190,4 @@ declare const effectiveColumnKind: (validator: ValidatorLike) => string | undefi
177
190
  * - everything else (string/number/date/timestamp/id/literal): verbatim.
178
191
  */
179
192
  declare const sqliteDecode: (raw: unknown, kind: string | undefined) => unknown;
180
- export { type SqlCtxDbOptions, type SqlCtxExec, type SqlDialect, type SqlRunResult, createSqlCtxDb, decodeBigint, decodeGlobalRow, effectiveColumnKind, readSqlCdcChanges, runSqlAggregateMigrations, runSqlCdcMigration, runSqlGlobalTableMigrations, runSqlRankMigrations, runSqlSearchMigrations, sqliteDecode, sqliteEncode, trimSqlCdcChanges, tryJsonParse };
193
+ export { SEARCH_STATE_TABLE, type SqlCtxDbOptions, type SqlCtxExec, type SqlDialect, type SqlRunResult, backfillSqlSearchIndexes, createSqlCtxDb, decodeBigint, decodeGlobalRow, effectiveColumnKind, migrateSearchState, readSqlCdcChanges, runSqlAggregateMigrations, runSqlCdcMigration, runSqlGlobalTableMigrations, runSqlRankMigrations, runSqlSearchMigrations, sqliteDecode, sqliteEncode, trimSqlCdcChanges, tryJsonParse };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{createSqlCtxDb as n,decodeGlobalRow as o,readSqlCdcChanges as i,runSqlAggregateMigrations as a,runSqlCdcMigration as l,runSqlGlobalTableMigrations as t,runSqlRankMigrations as d,runSqlSearchMigrations as g,trimSqlCdcChanges as c}from"./packem_shared/createSqlCtxDb-BXAFW5DJ.mjs";import{decodeBigint as s,effectiveColumnKind as S,sqliteDecode as C,sqliteEncode as u,tryJsonParse as M}from"./packem_shared/decodeBigint-DfiI8pkL.mjs";export{n as createSqlCtxDb,s as decodeBigint,o as decodeGlobalRow,S as effectiveColumnKind,i as readSqlCdcChanges,a as runSqlAggregateMigrations,l as runSqlCdcMigration,t as runSqlGlobalTableMigrations,d as runSqlRankMigrations,g as runSqlSearchMigrations,C as sqliteDecode,u as sqliteEncode,c as trimSqlCdcChanges,M as tryJsonParse};
1
+ import{createSqlCtxDb as o,readSqlCdcChanges as t,runSqlAggregateMigrations as a,runSqlCdcMigration as i,runSqlGlobalTableMigrations as l,runSqlRankMigrations as n,trimSqlCdcChanges as S}from"./packem_shared/createSqlCtxDb-BaFoSCeL.mjs";import{backfillSqlSearchIndexes as d,runSqlSearchMigrations as g}from"./packem_shared/backfillSqlSearchIndexes-DBD_C3SK.mjs";import{SEARCH_STATE_TABLE as s,migrateSearchState as f}from"./packem_shared/SEARCH_STATE_TABLE-DWm7Rza8.mjs";import{decodeBigint as C,effectiveColumnKind as x,sqliteDecode as u,sqliteEncode as b,tryJsonParse as h}from"./packem_shared/decodeBigint-DfiI8pkL.mjs";import{decodeGlobalRow as M}from"./packem_shared/decodeGlobalRow-k4q7Ma1g.mjs";export{s as SEARCH_STATE_TABLE,d as backfillSqlSearchIndexes,o as createSqlCtxDb,C as decodeBigint,M as decodeGlobalRow,x as effectiveColumnKind,f as migrateSearchState,t as readSqlCdcChanges,a as runSqlAggregateMigrations,i as runSqlCdcMigration,l as runSqlGlobalTableMigrations,n as runSqlRankMigrations,g as runSqlSearchMigrations,u as sqliteDecode,b as sqliteEncode,S as trimSqlCdcChanges,h as tryJsonParse};
@@ -0,0 +1 @@
1
+ import{sql as i}from"drizzle-orm";import{queryRun as $,queryAll as E}from"./decodeGlobalRow-k4q7Ma1g.mjs";const a="__lunora_search_state",T=async(e,r)=>{const{integer:n,key:t}=r.companionTypes,o=async()=>{try{await $(e,r,i`ALTER TABLE ${i.identifier(a)} ADD COLUMN ${i.identifier("profile")} ${i.raw(t)}`)}catch{}};await $(e,r,i`CREATE TABLE IF NOT EXISTS ${i.identifier(a)} (${i.identifier("companion")} ${i.raw(t)} PRIMARY KEY, ${i.identifier("cursor")} ${i.raw(t)}, ${i.identifier("done")} ${i.raw(n)} NOT NULL DEFAULT 0, ${i.identifier("profile")} ${i.raw(t)})`),await o()},p=e=>e===1||e===!0||e==="1",S=async(e,r,n)=>{const t=(await E(e,r,i`SELECT ${i.identifier("cursor")}, ${i.identifier("done")}, ${i.identifier("profile")} FROM ${i.identifier(a)} WHERE ${i.identifier("companion")} = ${n}`))[0];if(!t)return{cursor:void 0,done:!1,profile:void 0};const{cursor:o,profile:f}=t;return{cursor:typeof o=="string"?o:void 0,done:p(t.done),profile:typeof f=="string"?f:void 0}},w=async(e,r,n,t,o,f)=>{const d=t??null,c=i`UPDATE ${i.identifier(a)} SET ${i.identifier("cursor")} = ${d}, ${i.identifier("done")} = ${o?1:0}, ${i.identifier("profile")} = ${f} WHERE ${i.identifier("companion")} = ${n}`;if((await E(e,r,i`SELECT ${i.identifier("companion")} FROM ${i.identifier(a)} WHERE ${i.identifier("companion")} = ${n}`)).length>0){await $(e,r,c);return}try{await $(e,r,i`INSERT INTO ${i.identifier(a)} (${i.identifier("companion")}, ${i.identifier("cursor")}, ${i.identifier("done")}, ${i.identifier("profile")}) VALUES (${n}, ${d}, ${o?1:0}, ${f})`)}catch(s){if(!r.isUniqueViolation(s))throw s;await $(e,r,c)}};export{a as SEARCH_STATE_TABLE,T as migrateSearchState,S as readSearchBackfillState,w as writeSearchBackfillState};
@@ -0,0 +1 @@
1
+ import{ftsTableName as y,FTS_ID_COLUMN as d,createSearchAnalyzer as I,analyzedSearchText as C,tokenizeSearch as x,FTS_TEXT_COLUMN as P,searchTermRange as V,countSearchTokens as Y,FTS_TOKEN_COLUMN as L,FTS_COUNT_COLUMN as A,searchTextUnchanged as X,planSearchBackfillPass as z}from"@lunora/search-core";import{sql as e}from"drizzle-orm";import{migrateSearchState as J,readSearchBackfillState as j,writeSearchBackfillState as p}from"./SEARCH_STATE_TABLE-DWm7Rza8.mjs";import{queryRun as u,columnRefSql as h,serializeColumnValue as M,decodeRows as U,queryAll as R,createIndexIfNotExists as b,forEachRowPaged as K}from"./decodeGlobalRow-k4q7Ma1g.mjs";const g=async(t,i,a,r)=>{await u(t,i,e`DELETE FROM ${e.identifier(a)} WHERE ${e.identifier(d)} = ${r}`)},D=(t,i)=>t.name==="mysql"?e`${e.identifier(i)}(191)`:t.textPatternOperatorClass===void 0?e`${e.identifier(i)}`:e`${e.identifier(i)} ${e.raw(t.textPatternOperatorClass)}`,B=50,Q=(t,i)=>i?e`${e.identifier(L)} LIKE ${`${t}%`}`:e`${e.identifier(L)} = ${t}`,Z=async(t,i,a,r,n,s)=>{const o=x(n.query,I(n.definition.language));if(o.length===0)return[];const $=y(r,n.indexName),f=o.length-1,c=o.map((S,w)=>Q(S,w===f)),l=e.join(c,e` OR `),m=c.map(S=>e`SUM(CASE WHEN ${S} THEN ${e.identifier(A)} ELSE 0 END)`),N=e.join(m.map(S=>e`${S} > 0`),e` AND `),T=e`SELECT ${e.identifier(d)}, ${e.join(m,e` + `)} AS ${e.identifier("__score__")} FROM ${e.identifier($)} WHERE ${l} GROUP BY ${e.identifier(d)} HAVING ${N}`,_=[];for(const S of n.filters)_.push(e`m.${h(S.field)} = ${M(S.value)}`);a.softDeleteMode&&_.push(e`m.${h(a.softDeleteMode.field)} IS NULL`);let E=e`SELECT m.* FROM (${T}) s JOIN ${e.identifier(r)} m ON m.${e.identifier("id")} = s.${e.identifier(d)}`;return _.length>0&&(E=e`${E} WHERE ${e.join(_,e` AND `)}`),E=e`${E} ORDER BY s.${e.identifier("__score__")} DESC, m.${e.identifier("_creationTime")} DESC, m.${e.identifier("id")} ASC LIMIT ${e.raw(String(s))}`,U(a,await R(t,i,E))},ee=async(t,i,a,r,n,s)=>{const o=x(n.query,I(n.definition.language));if(o.length===0)return[];const $=y(r,n.indexName),f=`${$}__vocab`,c=o.length-1,l=o.map((E,S)=>{const w=V(E,S===c),G=w.exact?e`${e.identifier("term")} = ${w.lower}`:e`${e.identifier("term")} >= ${w.lower} AND ${e.identifier("term")} < ${w.upper}`;return e`SELECT ${e.identifier("doc")}, ${e.raw(String(S))} AS ${e.identifier("__term__")}, COUNT(*) AS ${e.identifier("__n__")} FROM ${e.identifier(f)} WHERE ${G} GROUP BY ${e.identifier("doc")}`}),m=o.map((E,S)=>e`SUM(CASE WHEN u.${e.identifier("__term__")} = ${e.raw(String(S))} THEN u.${e.identifier("__n__")} ELSE 0 END)`),N=e`SELECT f.${e.identifier(d)} AS ${e.identifier(d)}, ${e.join(m,e` + `)} AS ${e.identifier("__score__")} FROM (${e.join(l,e` UNION ALL `)}) u JOIN ${e.identifier($)} f ON f.rowid = u.${e.identifier("doc")} GROUP BY f.${e.identifier(d)} HAVING ${e.join(m.map(E=>e`${E} > 0`),e` AND `)}`,T=[];for(const E of n.filters)T.push(e`m.${h(E.field)} = ${M(E.value)}`);a.softDeleteMode&&T.push(e`m.${h(a.softDeleteMode.field)} IS NULL`);let _=e`SELECT m.* FROM (${N}) s JOIN ${e.identifier(r)} m ON m.${e.identifier("id")} = s.${e.identifier(d)}`;return T.length>0&&(_=e`${_} WHERE ${e.join(T,e` AND `)}`),_=e`${_} ORDER BY s.${e.identifier("__score__")} DESC, m.${e.identifier("_creationTime")} DESC, m.${e.identifier("id")} ASC LIMIT ${e.raw(String(s))}`,U(a,await R(t,i,_))},ie=async(t,i,a,r,n,s)=>{const o=i.nativeTextSearch,$=x(n.query,I(n.definition.language));if(!o||$.length===0)return[];const f=y(r,n.indexName),c=[o.matches(f,$)];for(const m of n.filters)c.push(e`m.${h(m.field)} = ${M(m.value)}`);a.softDeleteMode&&c.push(e`m.${h(a.softDeleteMode.field)} IS NULL`);const l=e`SELECT m.* FROM ${e.identifier(f)} JOIN ${e.identifier(r)} m ON m.${e.identifier("id")} = ${e.identifier(f)}.${e.identifier(d)} WHERE ${e.join(c,e` AND `)} ORDER BY ${o.rank(f,$)} DESC, m.${e.identifier("_creationTime")} DESC, m.${e.identifier("id")} ASC LIMIT ${e.raw(String(s))}`;return U(a,await R(t,i,l))},te={ensureCompanion:async(t,i,a)=>{const{integer:r,key:n}=i.companionTypes;await u(t,i,e`CREATE TABLE IF NOT EXISTS ${e.identifier(a)} (${e.identifier(L)} ${e.raw(n)} NOT NULL, ${e.identifier(d)} ${e.raw(n)} NOT NULL, ${e.identifier(A)} ${e.raw(r)} NOT NULL)`),await b(t,i,{columns:e`${D(i,L)}, ${D(i,d)}`,name:`${a}__btree`,table:a,unique:!1}),await b(t,i,{columns:D(i,d),name:`${a}__by_id`,table:a,unique:!1})},indexDocument:async(t,i,a,r,n,s)=>{await g(t,i,a,r);const o=[...Y(C(n,s),I(s.language))],$=e.join([L,d,A].map(f=>e.identifier(f)),e`, `);for(let f=0;f<o.length;f+=B){const c=e.join(o.slice(f,f+B).map(([l,m])=>e`(${l}, ${r}, ${m})`),e`, `);await u(t,i,e`INSERT INTO ${e.identifier(a)} (${$}) VALUES ${c}`)}},name:"inverted",runSearch:Z},ae={ensureCompanion:async(t,i,a)=>{await u(t,i,e`CREATE VIRTUAL TABLE IF NOT EXISTS ${e.identifier(a)} USING fts5(${e.identifier(P)}, ${e.identifier(d)} UNINDEXED)`),await u(t,i,e`CREATE VIRTUAL TABLE IF NOT EXISTS ${e.identifier(`${a}__vocab`)} USING fts5vocab(${e.identifier(a)}, ${e.raw("instance")})`)},indexDocument:async(t,i,a,r,n,s)=>{await g(t,i,a,r),await u(t,i,e`INSERT INTO ${e.identifier(a)} (${e.identifier("__text__")}, ${e.identifier(d)}) VALUES (${C(n,s)}, ${r})`)},name:"fts5",runSearch:ee},ne={ensureCompanion:async(t,i,a)=>{const r=i.nativeTextSearch;if(r){await u(t,i,r.createCompanion(a,i.companionTypes.key));for(const n of r.createIndexes(a))await u(t,i,n)}},indexDocument:async(t,i,a,r,n,s)=>{const o=i.nativeTextSearch;o&&(await g(t,i,a,r),await u(t,i,o.indexDocument(a,r,C(n,s))))},name:"native",runSearch:ie},O=(t,i)=>t.strategy==="native"&&i.nativeTextSearch!==void 0?ne:i.supportsFts5?ae:te,H=(t,i)=>`${I(t.language).profile}/${O(t,i).name}`,v=function*(t){for(const[i,a]of Object.entries(t.tables)){const r=a.searchIndexes;if(!(a.shardMode!==void 0&&a.shardMode.kind!=="global"||!r))for(const n of r)yield[i,a,n]}},F=(t,i)=>y(t,i.name),$e=async(t,i,a,r,n,s)=>O(n.definition,i).runSearch(t,i,a,r,n,s),q=200,k=async(t,i,a,r,n)=>{const s=F(r,n),o=O(n,i),$=H(n,i),f=z(await j(t,i,s),$);if(f.finished)return!0;if(f.wipe&&(await u(t,i,e`DELETE FROM ${e.identifier(s)}`),await p(t,i,s,void 0,!1,$)),(await R(t,i,i.tableExists(r))).length===0)return await p(t,i,s,void 0,!0,$),!0;let c=0,l=f.cursor;await K(t,i,a,r,async N=>{c+=1;const T=N._id;typeof T=="string"&&(l=T,await o.indexDocument(t,i,s,T,N,n))},{after:f.cursor,limit:q});const m=c<q;return await p(t,i,s,l,m,$),m},W=async(t,i,a)=>{await J(t,a);for(const[r,,n]of v(i)){const s=F(r,n),o=H(n,a),$=await j(t,a,s);$.profile!==void 0&&$.profile!==o&&(await u(t,a,e`DROP TABLE IF EXISTS ${e.identifier(s)}`),await p(t,a,s,void 0,!1,o)),await O(n,a).ensureCompanion(t,a,s)}},ce=async(t,i,a)=>{await W(t,i,a);for(const[r,n,s]of v(i))s.staged||await k(t,a,n,r,s)},de=async(t,i,a)=>{await W(t,i,a);for(const[r,n,s]of v(i)){let o=!1;for(;!o;)o=await k(t,a,n,r,s)}},le=t=>{const{dialect:i,exec:a,schema:r}=t;return async(n,s,o,$)=>{const f=r.tables[n]?.searchIndexes;if(!(!f||f.length===0))for(const c of f){if(X($,o,c))continue;const l=F(n,c);if(o){await O(c,i).indexDocument(a,i,l,s,o,c);continue}await g(a,i,l,s)}}};export{de as backfillSqlSearchIndexes,le as createSearchSync,$e as runSqlSearch,ce as runSqlSearchMigrations};
@@ -0,0 +1 @@
1
+ import{aggregateTableName as K,rankTableName as G,hasTrigger as wt,runRowValidators as Re,assertFlatPredicate as Ge,mergeWhere as M,sortColumnName as V,resolveRankPartition as Ye,encodePartitionKey as me,decodeCursor as Qe,RANK_TIEBREAK as le,CountRlsUnsupportedError as Xe,normalizeIdStructurally as ht,assertValidClientId as pt,aggregateSqlFunction as oe,softDeleteScope as se,compileWhereSql as ce,normalizeOrderKeys as Et,buildSeekWhere as mt,resolveRelationPredicates as Je,resolveWith as Ze,applySelect as et,encodeCursor as yt,NotFoundError as gt,applyOnDelete as bt,ConflictError as de,normalizeCountArgument as Nt,selectIndexForCount as Tt,encodeAggregateKey as U,selectIndexForAggregate as vt,readAggregateValue as Ae,runTriggers as St,matchesRankStaticWhere as tt,selectIndexForGroupBy as Rt,fanOutScalarCounts as At,matchesStaticWhere as Ie,foldAggregateTally as It,coerceAggregateNumber as Le,NotUniqueError as Lt,throwingScheduler as Ot}from"@lunora/do";import{LunoraError as N}from"@lunora/errors";import{createSearchBuilder as Ct,createSearchAnalyzer as kt,planSearchPage as xt,finishSearchPage as Mt,searchPageScan as Dt,resolveSearchScan as qt,assertSearchWithinCap as Wt}from"@lunora/search-core";import{sql as e}from"drizzle-orm";import{createSearchSync as Ft,runSqlSearchMigrations as Bt,runSqlSearch as jt}from"./backfillSqlSearchIndexes-DBD_C3SK.mjs";import{backfillSqlSearchIndexes as Ri}from"./backfillSqlSearchIndexes-DBD_C3SK.mjs";import{migrateSearchState as Ut}from"./SEARCH_STATE_TABLE-DWm7Rza8.mjs";import{queryAll as S,queryRun as A,createIndexIfNotExists as Ce,decodeRow as Y,serializeColumnValue as j,columnRefSql as k,decodeRows as Pt,forEachRowPaged as it}from"./decodeGlobalRow-k4q7Ma1g.mjs";import{decodeGlobalRow as Ii}from"./decodeGlobalRow-k4q7Ma1g.mjs";import{effectiveColumnKind as ye}from"./decodeBigint-DfiI8pkL.mjs";const Ht=(u,$)=>{if(u.size<$)return;const f=u.keys().next().value;f!==void 0&&u.delete(f)},Kt=new Set(["_id","id"]),ke=(u,$,f)=>u==="postgres"?e`${$} IS NOT DISTINCT FROM ${f}`:u==="mysql"?e`${$} <=> ${f}`:e`${$} IS ${f}`,Vt=u=>{const $=u.map(f=>e`${k(f.field)} ${e.raw(f.direction==="desc"?"DESC":"ASC")}`);return u.some(f=>Kt.has(f.field))||$.push(e`${k("id")} ASC`),e.join($,e`, `)},nt=50,xe=u=>{const $=[];for(const[f,r]of Object.entries(u.shape)){const m=r._meta?.column;m&&$.push([f,m])}return $},zt=(u,$,f)=>{const r={...$};for(const[m,l]of xe(u)){if(l.serverDefault){r[m]=l.serverDefault({auth:f});continue}r[m]===void 0&&(l.defaultFn?r[m]=l.defaultFn():"defaultValue"in l&&(r[m]=l.defaultValue))}return r},at=(u,$,f,r)=>{for(const[m,l]of xe(u)){if(l.serverDefault){m in $&&(f[m]=l.serverDefault({auth:r}));continue}l.onUpdateFn&&!(m in $)&&(f[m]=l.onUpdateFn())}},Gt=128,Yt=()=>{const u=new Map;return{delete:$=>{u.delete($)},get:$=>{const f=u.get($);if(f!==void 0)return u.delete($),u.set($,f),f},set:($,f)=>{u.has($)?u.delete($):Ht(u,Gt),u.set($,f)}}},Qt=async(u,$,f,r,m)=>{const l=m.get(r);if(l!==void 0)return l;const c=[];for(const[b,R]of Object.entries(f.tables))R.shardMode!==void 0&&R.shardMode.kind!=="global"||c.push(b);const T=await Promise.all(c.map(async b=>({found:(await S(u,$,e`SELECT 1 FROM ${e.identifier(b)} WHERE ${e.identifier("id")} = ${r} LIMIT 1`)).length>0,tableName:b})));for(const b of T)if(b.found)return m.set(r,b.tableName),b.tableName},Q=u=>u==null?null:Number(u),Xt=(u,$)=>$.map(f=>{const r={};for(const m of u)r[m]=f[m]??null;return{key:r,value:Q(f.value)}}),Jt=(u,$,f)=>[...u.partitionBy??[],...u.sortBy.map(r=>r.field),...u.where?Object.keys(u.where):[]].every(r=>$[r]===f[r]),rt=u=>u.length===1?u[0]:e`(${e.join(u,e` AND `)})`,Zt=(u,$,f,r,m)=>{const l=[];for(let c=0;c<f.length+1;c+=1){const T=f.slice(0,c).map(I=>ke(u,e`${e.identifier(I)}`,r[I])),b=f[c],R=$.sortBy[c];c<f.length&&b!==void 0&&R!==void 0?T.push(e`${e.identifier(b)} ${e.raw(R.direction==="desc"?">":"<")} ${r[b]}`):T.push(e`${e.identifier(le)} < ${m}`),l.push(rt(T))}return l.length>0?e.join(l,e` OR `):void 0},ei=(u,$,f)=>{if(f.length!==$.length)return;const r=[];for(const[m,l]of $.entries()){const c=[];for(let T=0;T<m;T+=1){const b=$[T];b!==void 0&&c.push(ke(u,e`${e.identifier(b.column)}`,f[T]))}c.push(e`${e.identifier(l.column)} ${e.raw(l.direction==="desc"?"<":">")} ${f[m]}`),r.push(rt(c))}return e`(${e.join(r,e` OR `)})`},ti=(u,$)=>{if(u.sortBy.length===0)throw new N("INTERNAL",`rankIndex "${u.name}" requires at least one "sortBy" column for stable pagination`);const f=[{column:"__partition__",direction:"asc"}];for(const[r,m]of u.sortBy.entries())f.push({column:$[r]??V(r),direction:m.direction});return f.push({column:le,direction:"asc"}),f},ii=async(u,$,f,r,m)=>{const l=[];for(let R=0;R<m.length;R+=50)l.push(m.slice(R,R+50));const c=await Promise.all(l.map(async R=>{const I=e.join(R.map(D=>e`${D}`),e`, `);return S(u,$,e`SELECT * FROM ${e.identifier(r)} WHERE ${e.identifier("id")} IN (${I})`)})),T=new Map;for(const R of c)for(const I of R)T.set(I.id,I);const b=[];for(const R of m){const I=Y(f,T.get(R));I&&b.push(I)}return b},ni=u=>{const $=JSON.stringify(u),f=new TextEncoder().encode($);let r="";for(const m of f)r+=String.fromCodePoint(m);return btoa(r)},ai=(u,$)=>$.columnType(ye(u)),ri=(u,$)=>{const f=[];for(const[m,l]of Object.entries(u.shape)){if(!l._meta?.column)continue;const c=l._meta.column.notNull&&l.kind!=="optional"?" NOT NULL":"";f.push(e`${e.identifier(m)} ${e.raw(`${ai(l,$)}${c}`)}`)}const r=$.frameworkColumns().map(m=>e`${e.identifier(m.name)} ${e.raw(m.type)}`);return e.join([...r,...f],e`, `)},oi=async(u,$,f,r)=>{const m=l=>{const c=k(l),T=f.shape[l],b=T&&r.indexKeyPrefix?r.indexKeyPrefix(ye(T)):void 0;return b===void 0?c:e`${c}(${e.raw(String(b))})`};for(const l of f.indexes){const c=e.join(l.fields.map(T=>m(T)),e`, `);await Ce(u,r,{columns:c,name:`${$}_${l.name}`,table:$,unique:l.unique??!1})}for(const[l,c]of xe(f))c.unique&&await Ce(u,r,{columns:m(l),name:`${$}_unique_${l}`,table:$,unique:!0})},si=async(u,$,f)=>{for(const[r,m]of Object.entries($.tables)){if(m.shardMode?.kind!=="global")continue;const l=ri(m,f);await A(u,f,e`CREATE TABLE IF NOT EXISTS ${e.identifier(r)} (${l})`),await oi(u,r,m,f)}},ci=async(u,$,f)=>{const{integer:r,key:m,real:l}=f.companionTypes;for(const[c,T]of Object.entries($.tables)){const b=T.aggregateIndexes;if(!(!b||b.length===0))for(const R of b){const I=K(c,R.name);await A(u,f,e`CREATE TABLE IF NOT EXISTS ${e.identifier(I)} (${e.identifier("__key__")} ${e.raw(m)} PRIMARY KEY, ${e.identifier("__value__")} ${e.raw(l)}, ${e.identifier("__count__")} ${e.raw(r)} NOT NULL DEFAULT 0)`),f.name==="sqlite"&&((await S(u,f,e`PRAGMA table_info(${e.identifier(I)})`)).some(D=>D.name==="__count__")||await A(u,f,e`ALTER TABLE ${e.identifier(I)} ADD COLUMN ${e.identifier("__count__")} ${e.raw(r)} NOT NULL DEFAULT 0`))}}},Oe=(u,$,f,r)=>{const m=u.name==="mysql"&&r?e`${e.identifier($)}(191)`:e`${e.identifier($)}`;return e`${m} ${e.raw(f)}`},di=(u,$,f)=>{const r=[Oe(u,"__partition__","ASC",!0)];for(const[m,l]of $.sortBy.entries()){const c=f.shape[l.field],T=c!==void 0&&u.indexKeyPrefix?.(ye(c))!==void 0;r.push(Oe(u,V(m),l.direction==="desc"?"DESC":"ASC",T))}return r.push(Oe(u,"__id__","ASC",!0)),r},li=(u,$,f)=>$.sortBy.map((r,m)=>{const l=f.shape[r.field],c=u.columnType(l?ye(l):void 0);return e`${e.identifier(V(m))} ${e.raw(c)}`}),ui=async(u,$,f)=>{const{key:r}=f.companionTypes;for(const[m,l]of Object.entries($.tables)){const c=l.rankIndexes;if(!(!c||c.length===0))for(const T of c){const b=G(m,T.name),R=li(f,T,l),I=R.length>0?e`, ${e.join(R,e`, `)}`:e``;await A(u,f,e`CREATE TABLE IF NOT EXISTS ${e.identifier(b)} (${e.identifier("__id__")} ${e.raw(r)} PRIMARY KEY, ${e.identifier("__partition__")} ${e.raw(r)} NOT NULL${I})`);const D=di(f,T,l),be=`${m}__rank_${T.name}__btree`;await Ce(u,f,{columns:e.join(D,e`, `),name:be,table:b,unique:!1})}}},ge="__cdc_log",fi=async(u,$)=>{const{autoincrementPrimaryKey:f,key:r,real:m,text:l}=$.companionTypes;await A(u,$,e`CREATE TABLE IF NOT EXISTS ${e.identifier(ge)} (${e.identifier("seq")} ${e.raw(f)}, ${e.identifier("ts")} ${e.raw(m)} NOT NULL, ${e.identifier("table")} ${e.raw(r)} NOT NULL, ${e.identifier("id")} ${e.raw(r)} NOT NULL, ${e.identifier("op")} ${e.raw(r)} NOT NULL, ${e.identifier("doc")} ${e.raw(l)})`)},_i=async(u,$,f,r,m,l,c)=>{await A(u,c,e`INSERT INTO ${e.identifier(ge)} (${e.identifier("ts")}, ${e.identifier("table")}, ${e.identifier("id")}, ${e.identifier("op")}, ${e.identifier("doc")}) VALUES (${$}, ${f}, ${r}, ${m}, ${l===void 0?null:JSON.stringify(l)})`)},bi=async(u,$,f)=>{const r=$.sinceSeq??0,m=Math.max(1,Math.min($.limit??1e3,1e4)),l=(await S(u,f,e`SELECT seq, ts, ${e.identifier("table")}, id, op, doc FROM ${e.identifier(ge)} WHERE seq > ${r} ORDER BY seq ASC LIMIT ${e.raw(String(m))}`)).map(c=>{const{doc:T}=c,b={id:String(c.id),op:String(c.op),seq:Number(c.seq),table:String(c.table),ts:Number(c.ts)};return typeof T=="string"?{...b,doc:JSON.parse(T)}:b});return{changes:l,cursor:l.at(-1)?.seq??r}},Ni=async(u,$,f)=>{await A(u,f,e`DELETE FROM ${e.identifier(ge)} WHERE ${e.identifier("seq")} <= ${$}`)},Ti=u=>{const{crossShardCounter:$,crossShardReader:f,exec:r,maxRelationKeys:m,schema:l}=u,{dialect:c}=u,{isUniqueViolation:T}=c,b={fieldRef:k,serialize:j,...c.name==="mysql"?{likeContains:(t,i)=>e`${t} LIKE CONCAT('%', ${i}, '%') ESCAPE '\\\\'`}:{}},R=(t,i)=>ke(c.name,t,i),I=t=>{const i=e.join(t.columns.map(d=>e`${e.identifier(d)}`),e`, `),n=e.join(t.values.map(d=>e`${d}`),e`, `),a=d=>c.name==="mysql"?e`VALUES(${e.identifier(d)})`:e`excluded.${e.identifier(d)}`,o=d=>c.name==="postgres"?e`${e.identifier(t.table)}.${e.identifier(d)}`:e`${e.identifier(d)}`,_=e.join(Object.entries(t.set(a,o)).map(([d,h])=>e`${e.identifier(d)} = ${h}`),e`, `),w=c.name==="mysql"?e`ON DUPLICATE KEY UPDATE ${_}`:e`ON CONFLICT(${e.identifier(t.conflictKey)}) DO UPDATE SET ${_}`;return e`INSERT INTO ${e.identifier(t.table)} (${i}) VALUES (${n}) ${w}`},D=u.clock??(()=>Date.now()),be=u.idGenerator??(()=>crypto.randomUUID()),Me=u.cdc??!1,Ne=u.auth??{identity:null,userId:null},X=async(t,i,n,a)=>{Me&&await _i(r,D(),t,i,n,a,c)},ot=u.scheduler??Ot,Te=Yt();let ue=0,ve;const W=async()=>(ve??=(async()=>{await si(r,l,c),await ci(r,l,c),await ui(r,l,c),await Ut(r,c),await Bt(r,l,c),Me&&await fi(r,c)})().catch(t=>{throw ve=void 0,t}),ve),J=async(t,i)=>{await W();const n=await Qt(r,c,l,t,Te);if(!(i!==void 0&&n!==i))return n},Z=new Map,De=async(t,i)=>{const n=K(t,i);return(await S(r,c,c.tableExists(n))).length>0},fe=async(t,i)=>{const n=`${t}::${i.name}`,a=Z.get(n);if(a!==void 0)return a;if(!await De(t,i.name))return Z.set(n,!1),!1;const o=l.tables[t];if(!o)return Z.set(n,!1),!1;const _=i.by??[],w=new Map;await it(r,c,o,t,h=>{if(i.where&&!Ie(h,i.where))return;const s=U(_,h);It(w,s,i,h)});const d=K(t,i.name);await A(r,c,e`DELETE FROM ${e.identifier(d)}`);for(const[h,s]of w)await A(r,c,e`INSERT INTO ${e.identifier(d)} (${e.identifier("__key__")}, ${e.identifier("__value__")}, ${e.identifier("__count__")}) VALUES (${h}, ${s.value}, ${s.count})`);return Z.set(n,!0),!0},st=async(t,i,n)=>{const a=oe(i.op),o=i.field??"",_=[];for(const h of i.by??[]){const s=j(n[h]??null);_.push(s===null?e`${k(h)} IS NULL`:e`${k(h)} = ${s}`)}for(const[h,s]of Object.entries(i.where??{})){const p=s!==null&&typeof s=="object"&&!Array.isArray(s)?s.eq:s,E=j(p);_.push(E===null?e`${k(h)} IS NULL`:e`${k(h)} = ${E}`)}const w=e`SELECT ${e.raw(a)}(${k(o)}) AS value FROM ${e.identifier(t)}`,d=await S(r,c,_.length>0?e`${w} WHERE ${e.join(_,e` AND `)}`:w);return Q(d[0]?.value)},qe=async(t,i)=>{await A(r,c,e`DELETE FROM ${e.identifier(t)} WHERE ${e.identifier("__key__")} = ${i} AND ${e.identifier("__count__")} <= 0`)},ct=async(t,i,n,a)=>{const o=K(t,i.name),{op:_}=i,w=i.field??"",d=n&&(!i.where||Ie(n,i.where))?n:void 0,h=a&&(!i.where||Ie(a,i.where))?a:void 0;if(!(!d&&!h)){if(_==="count"){const s=new Set;for(const[p,E]of[[d,-1],[h,1]]){if(!p)continue;const y=U(i.by??[],p);s.add(y),await A(r,c,I({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(g,v)=>({__count__:e`${v("__count__")} + ${g("__count__")}`,__value__:e`${v("__value__")} + ${g("__value__")}`}),table:o,values:[y,E,E]}))}for(const p of s)await qe(o,p);return}if(_==="sum"||_==="avg"){const s=new Set;for(const[p,E]of[[d,-1],[h,1]]){if(!p)continue;const y=Le(p[w]);if(y===void 0)continue;const g=U(i.by??[],p);s.add(g),await A(r,c,I({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(v,C)=>({__count__:e`${C("__count__")} + ${v("__count__")}`,__value__:e`COALESCE(${C("__value__")}, 0) + ${v("__value__")}`}),table:o,values:[g,E*y,E]}))}for(const p of s)await qe(o,p);return}if(d){const s=U(i.by??[],d),p=Le(d[w]),E=(await S(r,c,e`SELECT ${e.identifier("__value__")} AS value, ${e.identifier("__count__")} AS count FROM ${e.identifier(o)} WHERE ${e.identifier("__key__")} = ${s}`))[0],y=Q(E?.value),g=(E?.count??0)-1;if(g<=0)await A(r,c,e`DELETE FROM ${e.identifier(o)} WHERE ${e.identifier("__key__")} = ${s}`);else if(E&&p!==void 0&&y!==null&&p===y){const v=await st(t,i,d);await A(r,c,e`UPDATE ${e.identifier(o)} SET ${e.identifier("__value__")} = ${v}, ${e.identifier("__count__")} = ${g} WHERE ${e.identifier("__key__")} = ${s}`)}else await A(r,c,e`UPDATE ${e.identifier(o)} SET ${e.identifier("__count__")} = ${e.identifier("__count__")} - 1 WHERE ${e.identifier("__key__")} = ${s}`)}if(h){const s=U(i.by??[],h),p=Le(h[w]);if(p===void 0)await A(r,c,I({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(E,y)=>({__count__:e`${y("__count__")} + 1`}),table:o,values:[s,null,1]}));else{const E=_==="min"?"MIN":"MAX";await A(r,c,I({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(y,g)=>({__count__:e`${g("__count__")} + 1`,__value__:e`${e.raw(E)}(COALESCE(${g("__value__")}, ${y("__value__")}), ${y("__value__")})`}),table:o,values:[s,p,1]}))}}}},_e=async t=>{const i=l.tables[t]?.aggregateIndexes;if(!(!i||i.length===0))for(const n of i)await fe(t,n)},ee=async(t,i,n)=>{const a=l.tables[t]?.aggregateIndexes;if(!(!a||a.length===0))for(const o of a){const _=`${t}::${o.name}`;(Z.get(_)??await De(t,o.name))&&await ct(t,o,i,n)}},te=new Map,We=async(t,i)=>{const n=G(t,i);return(await S(r,c,c.tableExists(n))).length>0},Se=async(t,i)=>{const n=`${t}::rank::${i.name}`,a=te.get(n);if(a!==void 0)return a;if(!await We(t,i.name))return te.set(n,!1),!1;const o=l.tables[t];if(!o)return te.set(n,!1),!1;const _=G(t,i.name);await A(r,c,e`DELETE FROM ${e.identifier(_)}`);const w=i.sortBy.map((s,p)=>V(p)),d=e.join(["__id__","__partition__",...w].map(s=>e`${e.identifier(s)}`),e`, `),h=[];await it(r,c,o,t,s=>{if(i.where&&!tt(s,i.where))return;const p=me(i.partitionBy??[],s),E=i.sortBy.map(y=>j(s[y.field]??null));h.push([s._id,p,...E])});for(const s of h){const p=e.join(s.map(E=>e`${E}`),e`, `);await A(r,c,e`INSERT INTO ${e.identifier(_)} (${d}) VALUES (${p})`)}return te.set(n,!0),!0},$e=async t=>{const i=l.tables[t]?.rankIndexes;if(!(!i||i.length===0))for(const n of i)await Se(t,n)},z=async(t,i,n,a)=>{const o=l.tables[t],_=o?.rankIndexes;if(!_||_.length===0)return;const w=o?.softDeleteMode?.field,d=w===void 0||a===void 0?void 0:a[w],h=d!=null;for(const s of _){if(n!==void 0&&a!==void 0&&Jt(s,n,a))continue;const p=`${t}::rank::${s.name}`;if(!(te.get(p)??await We(t,s.name)))continue;const E=G(t,s.name);if(n&&await A(r,c,e`DELETE FROM ${e.identifier(E)} WHERE ${e.identifier("__id__")} = ${i}`),a&&!h){if(s.where&&!tt(a,s.where))continue;const y=s.sortBy.map((L,P)=>V(P)),g=e.join(["__id__","__partition__",...y].map(L=>e`${e.identifier(L)}`),e`, `),v=me(s.partitionBy??[],a),C=s.sortBy.map(L=>j(a[L.field]??null)),x=e.join([i,v,...C].map(L=>e`${L}`),e`, `);await A(r,c,e`INSERT INTO ${e.identifier(E)} (${g}) VALUES (${x})`)}}},ie=Ft({dialect:c,exec:r,schema:l}),Fe=new Set;for(const[t,i]of Object.entries(l.tables))for(const n of Object.values(i.triggerMap??{}))Fe.add(`${t} ${n.timing} ${n.op}`);const F=(t,i,n)=>Fe.has(`${t} ${i} ${n}`);let Be;const B=async(t,i,n)=>{if(ue+=1,ue>nt)throw ue-=1,new de(`trigger recursion exceeded ${String(nt)} levels on "${n.table}" — check for a self-triggering write`,"trigger");try{await St({ctx:Be,event:n,op:i,schema:l,tableName:n.table,timing:t})}finally{ue-=1}},dt=async(t,i)=>{try{await A(r,c,i)}catch(n){throw T(n)?new de(`unique constraint violation on "${t}"`,"unique"):n}},we=async(t,i)=>(await S(r,c,e`SELECT * FROM ${e.identifier(t)} WHERE ${e.identifier("id")} = ${i}`))[0],he=async(t,i,n,a)=>{if(a===void 0)return;const o=e.join(Object.keys(a).map(d=>R(e`${e.identifier(d)}`,a[d])),e` AND `),_=i==="UPDATE"?e`UPDATE ${e.identifier(t)} SET ${n} WHERE ${o}`:e`DELETE FROM ${e.identifier(t)} WHERE ${o}`,w=()=>{throw new de(`optimistic concurrency conflict on "${t}" — the row changed during this mutation; refetch and retry`,"occ")};try{if(c.supportsReturning)(await S(r,c,e`${_} RETURNING ${e.identifier("id")}`)).length===0&&w();else{const d=await A(r,c,_);(c.affectedRows?c.affectedRows(d??{rowsAffected:0}):0)===0&&w()}}catch(d){throw T(d)?new de(`unique constraint violation on "${t}"`,"unique"):d}},lt=(t,i,n,a)=>{const o=Object.keys(t.shape);return{columns:["id","_creationTime",...o],values:[i,n,...o.map(_=>j(a[_]??null))]}},ut=async(t,i,n,a)=>{const o=Rt(i,n.op,n.field,a.by,a.where);if(!o||!await fe(t,o.index))return;const _=K(t,o.index.name),w=Object.keys(o.partial);if(w.length===(o.index.by??[]).length&&w.length>0){const d=U(o.index.by??[],o.partial),h=await S(r,c,e`SELECT ${e.identifier("__value__")} AS value, ${e.identifier("__count__")} AS count FROM ${e.identifier(_)} WHERE ${e.identifier("__key__")} = ${d}`);if(h.length===0)return[];const s=h[0];return[{key:{...o.partial},value:Ae(n.op,{count:s.count,value:Q(s.value)})}]}return w.length>0?void 0:(await S(r,c,e`SELECT ${e.identifier("__key__")} AS key, ${e.identifier("__value__")} AS value, ${e.identifier("__count__")} AS count FROM ${e.identifier(_)}`)).map(d=>{const h=d;return{key:JSON.parse(h.key),value:Ae(n.op,{count:h.count,value:Q(h.value)})}})},je=t=>{const i=l.tables[t]?.shardMode?.kind;return i==="shardBy"||i==="root"},Ue=t=>{throw new N("INTERNAL",`cross-backend relation: a global table cannot load the shard-local relation '${t}' (it spans every shard) — wire a cross-shard reader to support it`)},Pe=(t,i)=>je(t)?f?f(t,i):Ue(t):q.findMany(t,i),pe=(t,i,n)=>Je(t,{fetcher:Pe,maxRelationKeys:m,relationBaseWhere:n,schema:l,tableName:i}),q={async aggregate(t,i){const n=l.tables[t];if(!n)throw new N("INTERNAL",`unknown table: ${t}`);if(await W(),oe(i.op),i.op==="count")return q.count(t,{baseWhere:i.baseWhere,relationBaseWhere:i.relationBaseWhere,restrictsCounts:i.restrictsCounts,where:i.where});if(!i.field)throw new N("INTERNAL",`aggregate(${t}, { op: "${i.op}" }): "field" is required for non-count reducers`);const a=se(n.softDeleteMode,void 0),o=M(M(i.baseWhere,i.where),a),_=await pe(o,t,i.relationBaseWhere),w=_!==o;if(n.aggregateIndexes&&!i.baseWhere&&!w&&!a){const E=vt(n.aggregateIndexes,i.op,i.field,i.where);if(E&&await fe(t,E.index)){const y=U(E.index.by??[],E.key),g=K(t,E.index.name),v=(await S(r,c,e`SELECT ${e.identifier("__value__")} AS value, ${e.identifier("__count__")} AS count FROM ${e.identifier(g)} WHERE ${e.identifier("__key__")} = ${y}`))[0];return Ae(i.op,v===void 0?void 0:{count:v.count,value:Q(v.value)})}}const d=ce(_,b),h=e.raw(oe(i.op)),s=e`SELECT ${h}(${k(i.field)}) AS value FROM ${e.identifier(t)}`,p=(await S(r,c,d?e`${s} WHERE ${d}`:s))[0]?.value;return p==null?null:Number(p)},async count(t,i){const n=l.tables[t];if(!n)throw new N("INTERNAL",`unknown table: ${t}`);await W();const a=Nt(i);if(a.restrictsCounts)throw new Xe(t);const o=se(n.softDeleteMode,void 0),_=M(M(a.baseWhere,a.where),o),w=await pe(_,t,a.relationBaseWhere),d=w!==_;if(n.aggregateIndexes&&!a.baseWhere&&!d&&!o){const E=Tt(n.aggregateIndexes,a.where);if(E&&await fe(t,E.index)){const y=U(E.index.by??[],E.key),g=K(t,E.index.name),v=await S(r,c,e`SELECT ${e.identifier("__value__")} AS value FROM ${e.identifier(g)} WHERE ${e.identifier("__key__")} = ${y}`);return Number(v[0]?.value??0)}}const h=ce(w,b),s=e`SELECT COUNT(*) AS count FROM ${e.identifier(t)}`,p=await S(r,c,h?e`${s} WHERE ${h}`:s);return Number(p[0]?.count??0)},async delete(t,i,n){const a=await J(t,i);if(!a)return;const o=l.tables[a];if(!o)return;const _=await we(a,t),w=Y(o,_),d=n?.hard===!0,h=!d&&o.softDeleteMode?o.softDeleteMode.field:void 0;if(!(h&&(!w||w[h]!==null&&w[h]!==void 0))){if(F(a,"before","delete")&&await B("before","delete",{id:t,op:"delete",previous:w??void 0,table:a}),await bt({deletedId:t,deletedReference:s=>w?.[s],findHolders:async(s,p,E)=>{if(l.tables[s]?.shardMode?.kind==="shardBy")throw new N("INTERNAL",`cross-backend cascade from global '${a}' into shardBy '${s}' is not supported — would require Query Coordinator fan-out across shards`);return(await q.findMany(s,{includeDeleted:d,where:{[p]:E}})).page},onCascade:(s,p)=>q.delete(p,void 0,n),onRestrict:s=>{throw new de(s,"restrict")},onSetNull:(s,p,E)=>q.patch(p,{[E]:null}),schema:l,tableName:a}),await _e(a),await $e(a),h&&w){const s={...w,[h]:D()},p=e.join(Object.keys(o.shape).map(E=>e`${e.identifier(E)} = ${j(s[E]??null)}`),e`, `);await he(a,"UPDATE",p,_),await ee(a,w,s),await z(a,t,w,void 0),await ie(a,t,s,w),await X(a,t,"update",s),F(a,"after","delete")&&await B("after","delete",{id:t,op:"delete",previous:w,table:a});return}await he(a,"DELETE",void 0,_),Te.delete(t),await ee(a,w??void 0,void 0),await z(a,t,w??void 0,void 0),await ie(a,t,void 0),await X(a,t,"delete"),F(a,"after","delete")&&await B("after","delete",{id:t,op:"delete",previous:w??void 0,table:a})}},async findFirst(t,i={}){return(await q.findMany(t,{...i,limit:1})).page[0]??null},async findFirstOrThrow(t,i={}){const n=await q.findFirst(t,i);if(n===null)throw new gt(`findFirstOrThrow: no "${t}" document matched`);return n},async findMany(t,i={}){const n=l.tables[t];if(!n)throw new N("INTERNAL",`unknown table: ${t}`);await W();const a=Et(i.orderBy),o=i.cursor?mt(a,Qe(i.cursor)):void 0,_=Pe,w=async(L,P,Ee,ne)=>{if(je(L))return $?At($,L,P,Ee,ne):Ue(L);const H=l.tables[L];if(!H)throw new N("INTERNAL",`unknown table: ${L}`);const O=se(H.softDeleteMode,void 0),ae={[P]:{in:Ee}},ft=M(M(ae,ne),O),_t=await pe(ft,L,void 0),He=ce(_t,b),Ke=k(P);let re=e`SELECT ${Ke} AS __fk__, COUNT(*) AS count FROM ${e.identifier(L)}`;He&&(re=e`${re} WHERE ${He}`),re=e`${re} GROUP BY ${Ke}`;const $t=await S(r,c,re),Ve=new Map;for(const ze of $t)Ve.set(ze.__fk__,Number(ze.count??0));return Ve};let d=M(i.baseWhere,i.where);d=M(d,se(n.softDeleteMode,i.includeDeleted)),d=await Je(d,{fetcher:_,maxRelationKeys:m,relationBaseWhere:i.relationBaseWhere,schema:l,tableName:t}),o&&(d=d?{AND:[d,o]}:o);const h=ce(d,b),s=Vt(a);let p=e`SELECT * FROM ${e.identifier(t)}`;h&&(p=e`${p} WHERE ${h}`),p=e`${p} ORDER BY ${s}`;const E=typeof i.limit=="number"?Math.max(0,Math.floor(i.limit)):void 0;E!==void 0&&(p=e`${p} LIMIT ${e.raw(String(E+1))}`);const y=await S(r,c,p),g=Pt(n,y);if(E===void 0)return i.with&&await Ze({fetcher:_,groupedCounter:w,parents:g,schema:l,tableName:t,with:i.with}),{continueCursor:null,isDone:!0,page:et(g,i.select,i.with)};const v=g.length>E,C=v?g.slice(0,E):g,x=C.at(-1);return i.with&&await Ze({fetcher:_,groupedCounter:w,parents:C,schema:l,tableName:t,with:i.with}),{continueCursor:v&&x?yt(x,a):null,isDone:!v,page:et(C,i.select,i.with)}},async get(t,i){const n=await J(t,i);if(!n)return null;const a=l.tables[n];if(!a)return null;const o=await S(r,c,e`SELECT * FROM ${e.identifier(n)} WHERE ${e.identifier("id")} = ${t}`);return Y(a,o[0])},async groupBy(t,i){const n=l.tables[t];if(!n)throw new N("INTERNAL",`unknown table: ${t}`);await W();const a=i.agg??{op:"count"};if(oe(a.op),a.op!=="count"&&!a.field)throw new N("INTERNAL",`groupBy(${t}, { agg: { op: "${a.op}" } }): "field" is required for non-count reducers`);const o=se(n.softDeleteMode,void 0),_=M(M(i.baseWhere,i.where),o),w=await pe(_,t,i.relationBaseWhere),d=w!==_;if(n.aggregateIndexes&&!i.baseWhere&&!d&&!o){const g=await ut(t,n.aggregateIndexes,a,i);if(g!==void 0)return g}const h=ce(w,b),s=i.by.map(g=>e`${k(g)} AS ${e.identifier(g)}`);if(a.op==="count")s.push(e`COUNT(*) AS value`);else{if(!a.field)throw new N("INTERNAL",`groupBy(${t}, { agg: { op: "${a.op}" } }): "field" is required for non-count reducers`);s.push(e`${e.raw(oe(a.op))}(${k(a.field)}) AS value`)}const p=e.join(i.by.map(g=>k(g)),e`, `);let E=e`SELECT ${e.join(s,e`, `)} FROM ${e.identifier(t)}`;h&&(E=e`${E} WHERE ${h}`),E=e`${E} GROUP BY ${p}`;const y=await S(r,c,E);return Xt(i.by,y)},async insert(t,i,n){const a=l.tables[t];if(!a)throw new N("INTERNAL",`unknown table: ${t}`);await W();const o=zt(a,i,Ne);Re(a,o);let _,w=!0;n?.clientId!==void 0?(pt(n.clientId),_=n.clientId):n?.allowExplicitId&&typeof o._id=="string"?_=o._id:(_=be(),w=!1);const d=n?.allowExplicitId&&typeof o._creationTime=="number"?o._creationTime:D(),h={...o,_creationTime:d,_id:_};F(t,"before","insert")&&await B("before","insert",{doc:{...h},id:_,op:"insert",table:t}),await _e(t),await $e(t);const{columns:s,values:p}=lt(a,_,d,o),E=e.join(s.map(g=>e`${e.identifier(g)}`),e`, `),y=e.join(p.map(g=>e`${g}`),e`, `);return await dt(t,e`INSERT INTO ${e.identifier(t)} (${E}) VALUES (${y})`),w&&Te.set(_,t),await ee(t,void 0,h),await z(t,_,void 0,h),await ie(t,_,h),await X(t,_,"insert",h),F(t,"after","insert")&&await B("after","insert",{doc:h,id:_,op:"insert",table:t}),_},normalizeId(t,i){return ht(l,t,i)},async patch(t,i,n){const a=await J(t,n);if(!a)throw new N("INTERNAL",`document not found: ${t}`);const o=l.tables[a];if(!o)throw new N("INTERNAL",`document not found: ${t}`);const _=await we(a,t),w=Y(o,_);if(!w)throw new N("INTERNAL",`document not found: ${t}`);const d={...w,...i,_id:t};at(o,i,d,Ne),Re(o,d),F(a,"before","update")&&await B("before","update",{doc:{...d},id:t,op:"update",previous:w,table:a}),await _e(a),await $e(a);const h=Object.keys(o.shape),s=e.join(h.map(p=>e`${e.identifier(p)} = ${j(d[p]??null)}`),e`, `);await he(a,"UPDATE",s,_),await ee(a,w,d),await z(a,t,w,d),await ie(a,t,d,w),await X(a,t,"update",d),F(a,"after","update")&&await B("after","update",{doc:d,id:t,op:"update",previous:w,table:a})},async restore(t,i){const n=await J(t,i);if(!n)throw new N("INTERNAL",`document not found: ${t}`);const a=l.tables[n],o=a?.softDeleteMode?.field;if(!a||!o)throw new N("INTERNAL",`ctx.db.restore: table "${n}" is not a .softDelete() table`);const _=await we(n,t),w=_?.[o]!==null&&_?.[o]!==void 0;if(await q.patch(t,{[o]:null},i),w){const d=Y(a,_);d!==null&&await z(n,t,void 0,{...d,[o]:null})}},query(t){const i=l.tables[t];if(!i)throw new N("INTERNAL",`unknown table: ${t}`);const n="the legacy query()/withIndex() reader is not available on the D1 (global) backend; use findMany",a=[],o=d=>a.every(h=>h(d)),_=async(d,h)=>{await W();const s=a.length>0,p=await jt(r,c,i,t,d,qt(s?void 0:h));if(!s)return h===void 0&&Wt(p),p;const E=p.filter(y=>o(y));return h===void 0?E:E.slice(0,h)},w=d=>{const h={async collect(){if(!d)throw new N("INTERNAL",n);return _(d,void 0)},filter(s){if(!d)throw new N("INTERNAL",n);return a.push(s),h},async first(){if(!d)throw new N("INTERNAL",n);return(await _(d,1))[0]??null},order(){return h},async paginate(s){if(!d)throw new N("INTERNAL",n);const p=xt(s);return Mt(await _(d,Dt(p)),p)},async take(s){if(!d)throw new N("INTERNAL",n);return _(d,s)},async unique(){if(!d)throw new N("INTERNAL",n);const s=await _(d,2);if(s.length>1)throw new Lt(`unique() on table "${t}" matched ${String(s.length)} documents; expected at most one`);return s[0]??null},withGeoIndex(){throw new N("INTERNAL",`geo indexes are not supported on \`.global()\` tables (table "${t}") — geospatial queries run only on sharded tables`)},withIndex(){throw new N("INTERNAL",n)},withSearchIndex(s,p){const E=(i.searchIndexes??[]).find(g=>g.name===s);if(!E)throw new N("INTERNAL",`unknown search index "${s}" on table "${t}"`);const y={definition:E,field:E.field,filters:[],hasQuery:!1,indexName:s,query:""};if(p(Ct(y,t,kt(E.language))),!y.hasQuery)throw new N("INTERNAL",`search index "${s}" on table "${t}" requires a .search(field, query) call`);return w(y)}};return h};return w(void 0)},async rank(t,i,n){const a=l.tables[t];if(!a)throw new N("INTERNAL",`unknown table: ${t}`);const o=a.rankIndexes?.find(x=>x.name===i);if(!o)throw new N("INTERNAL",`unknown rankIndex "${i}" on table "${t}"`);if(n.restrictsCounts)throw new Xe(t);if(await W(),!await Se(t,o))return null;const _=typeof n.row=="string"?n.row:n.row._id;if(!_)return null;const w=G(t,o.name),d=o.sortBy.map((x,L)=>V(L)),h=e.join([e`${e.identifier("__partition__")}`,...d.map(x=>e`${e.identifier(x)}`)],e`, `),s=(await S(r,c,e`SELECT ${h} FROM ${e.identifier(w)} WHERE ${e.identifier("__id__")} = ${_}`))[0];if(!s)return null;const p=s.__partition__,E=M(n.baseWhere,n.where);Ge(E,l,t,"rank");const y=Ye(o,E);if(y&&me(o.partitionBy??[],y)!==p)return null;const g=Zt(c.name,o,d,s,_),v=await S(r,c,e`SELECT COUNT(*) AS c FROM ${e.identifier(w)} WHERE ${e.identifier("__partition__")} = ${p}${g?e` AND (${g})`:e``}`),C=await S(r,c,e`SELECT COUNT(*) AS c FROM ${e.identifier(w)} WHERE ${e.identifier("__partition__")} = ${p}`);return{position:Number(v[0]?.c??0)+1,total:Number(C[0]?.c??0)}},async rankPage(t,i,n={}){Ge(M(n.baseWhere,n.where),l,t,"rankPage");const a=l.tables[t];if(!a)throw new N("INTERNAL",`unknown table: ${t}`);const o=a.rankIndexes?.find(O=>O.name===i);if(!o)throw new N("INTERNAL",`unknown rankIndex "${i}" on table "${t}"`);if(await W(),!await Se(t,o))return{continueCursor:null,isDone:!0,page:[]};const _=G(t,o.name),w=o.sortBy.map((O,ae)=>V(ae)),d=Math.max(1,Math.min(1e3,Math.floor(n.take??100))),h=M(n.baseWhere,n.where),s=Ye(o,h),p=ti(o,w),E=e.join(p.map(O=>e`${e.identifier(O.column)} ${e.raw(O.direction==="desc"?"DESC":"ASC")}`),e`, `),y=[];if(s&&y.push(e`${e.identifier("__partition__")} = ${me(o.partitionBy??[],s)}`),n.cursor){const O=ei(c.name,p,Qe(n.cursor));O!==void 0&&y.push(O)}const g=e.join([e`${e.identifier(le)}`,e`${e.identifier("__partition__")}`,...w.map(O=>e`${e.identifier(O)}`)],e`, `);let v=e`SELECT ${g} FROM ${e.identifier(_)}`;y.length>0&&(v=e`${v} WHERE ${e.join(y,e` AND `)}`),v=e`${v} ORDER BY ${E} LIMIT ${e.raw(String(d+1))}`;const C=await S(r,c,v),x=C.length>d,L=x?C.slice(0,d):C,P=L.map(O=>O[le]),Ee=await ii(r,c,a,t,P);let ne=null;const H=L.at(-1);if(x&&H!==void 0){const O=[H.__partition__,...w.map(ae=>H[ae]),H[le]];ne=ni(O)}return{continueCursor:ne,isDone:!x,page:Ee}},async replace(t,i,n,a){const o=await J(t,n);if(!o)throw new N("INTERNAL",`document not found: ${t}`);const _=l.tables[o];if(!_)throw new N("INTERNAL",`document not found: ${t}`);const w=await we(o,t);if(w===void 0)throw new N("INTERNAL",`document not found: ${t}`);const d=wt(l,o,"update")||(_.aggregateIndexes??[]).length>0||(_.rankIndexes??[]).length>0?Y(_,w)??void 0:void 0,h=a?.allowExplicitId&&typeof i._creationTime=="number"?i._creationTime:D(),s={...i,_creationTime:h,_id:t};at(_,i,s,Ne),Re(_,s),F(o,"before","update")&&await B("before","update",{doc:{...s},id:t,op:"update",previous:d,table:o}),await _e(o),await $e(o);const p=Object.keys(_.shape),E=e.join([e`${e.identifier("_creationTime")} = ${h}`,...p.map(y=>e`${e.identifier(y)} = ${j(s[y]??null)}`)],e`, `);await he(o,"UPDATE",E,w),await ee(o,d,s),await z(o,t,d,s),await ie(o,t,s,d),await X(o,t,"update",s),F(o,"after","update")&&await B("after","update",{doc:s,id:t,op:"update",previous:d,table:o})}};return Be={db:q,scheduler:ot},q};export{Ri as backfillSqlSearchIndexes,Ti as createSqlCtxDb,Ii as decodeGlobalRow,bi as readSqlCdcChanges,ci as runSqlAggregateMigrations,fi as runSqlCdcMigration,si as runSqlGlobalTableMigrations,ui as runSqlRankMigrations,Bt as runSqlSearchMigrations,Ni as trimSqlCdcChanges};
@@ -0,0 +1 @@
1
+ import{renderSql as w}from"@lunora/do";import{sql as o}from"drizzle-orm";import{sqliteEncode as C,sqliteDecode as S,effectiveColumnKind as q}from"./decodeBigint-DfiI8pkL.mjs";const T=e=>e==="_id"||e==="id"?"id":e,O=e=>o`${o.identifier(T(e))}`,E=(e,i,t)=>{const{params:r,sql:a}=w(i.name,t);return e.all(a,r)},u=(e,i,t)=>{const{params:r,sql:a}=w(i.name,t);return e.run(a,r)},_=async(e,i,t)=>{const r=t.unique?o`UNIQUE `:o``;if(i.name==="mysql"){try{await u(e,i,o`CREATE ${r}INDEX ${o.identifier(t.name)} ON ${o.identifier(t.table)} (${t.columns})`)}catch(a){const n=a;if(n.errno!==1061&&n.code!=="ER_DUP_KEYNAME"&&n.code!==1061)throw a}return}await u(e,i,o`CREATE ${r}INDEX IF NOT EXISTS ${o.identifier(t.name)} ON ${o.identifier(t.table)} (${t.columns})`)},g=C,$=new WeakMap,h=e=>{let i=$.get(e);return i===void 0&&(i=Object.entries(e.shape).map(([t,r])=>[t,q(r)]),$.set(e,i)),i},v=(e,i)=>{const t={};for(const[r,a]of h(e)){const n=i[r];n!==void 0&&(t[r]=S(n,a))}return t._id=i.id,t._creationTime=i._creationTime,t},I=(e,i)=>i?v(e,i):null,M=(e,i)=>{const t=[];for(const r of i){const a=I(e,r);a&&t.push(a)}return t},R=500,D=async(e,i,t,r,a,n={})=>{let c=n.after,s=n.limit,f=!0;for(;f&&(s===void 0||s>0);){const l=s===void 0?R:Math.min(R,s),d=c===void 0?await E(e,i,o`SELECT * FROM ${o.identifier(r)} ORDER BY ${o.identifier("id")} ASC LIMIT ${o.raw(String(l))}`):await E(e,i,o`SELECT * FROM ${o.identifier(r)} WHERE ${o.identifier("id")} > ${c} ORDER BY ${o.identifier("id")} ASC LIMIT ${o.raw(String(l))}`);for(const p of d){const m=I(t,p);m&&await a(m)}c=d.at(-1)?.id,f=d.length===l,s=s===void 0?void 0:s-d.length}};export{R as BACKFILL_BATCH_SIZE,O as columnRefSql,_ as createIndexIfNotExists,v as decodeGlobalRow,I as decodeRow,M as decodeRows,D as forEachRowPaged,T as physicalColumn,E as queryAll,u as queryRun,g as serializeColumnValue};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/sql-store",
3
- "version": "1.0.0-alpha.47",
3
+ "version": "1.0.0-alpha.49",
4
4
  "description": "Internal dialect-parameterized SQL store core for Lunora .global() backends (D1, PlanetScale)",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -50,9 +50,10 @@
50
50
  "access": "public"
51
51
  },
52
52
  "dependencies": {
53
- "@lunora/do": "1.0.0-alpha.50",
53
+ "@lunora/do": "1.0.0-alpha.52",
54
54
  "@lunora/errors": "1.0.0-alpha.8",
55
- "drizzle-orm": "^0.45.2"
55
+ "drizzle-orm": "^0.45.2",
56
+ "@lunora/search-core": "1.0.0-alpha.0"
56
57
  },
57
58
  "engines": {
58
59
  "node": "^22.15.0 || >=24.11.0"
@@ -1 +0,0 @@
1
- import{aggregateTableName as Y,rankTableName as J,ftsTableName as xe,hasTrigger as bt,runRowValidators as Ie,assertFlatPredicate as Qe,mergeWhere as q,sortColumnName as G,resolveRankPartition as Ze,encodePartitionKey as Te,decodeCursor as et,RANK_TIEBREAK as ue,CountRlsUnsupportedError as tt,normalizeIdStructurally as Rt,assertValidClientId as St,aggregateSqlFunction as ce,softDeleteScope as de,compileWhereSql as le,normalizeOrderKeys as vt,buildSeekWhere as It,resolveRelationPredicates as it,resolveWith as nt,applySelect as at,encodeCursor as At,NotFoundError as Lt,applyOnDelete as Ot,ConflictError as fe,normalizeCountArgument as Ct,selectIndexForCount as Mt,encodeAggregateKey as P,selectIndexForAggregate as kt,readAggregateValue as Ae,renderSql as ft,runTriggers as Dt,matchesRankStaticWhere as ot,stringifySearchText as ut,selectIndexForGroupBy as xt,fanOutScalarCounts as qt,matchesStaticWhere as Le,coerceAggregateNumber as Oe,foldAggregateTally as Ft,NotUniqueError as Wt,throwingScheduler as Ut,tokenizeSearch as _t,buildFtsMatch as Bt,scoreDocument as jt}from"@lunora/do";import{LunoraError as b}from"@lunora/errors";import{sql as e}from"drizzle-orm";import{sqliteDecode as Ht,sqliteEncode as Pt,effectiveColumnKind as _e}from"./decodeBigint-DfiI8pkL.mjs";const Kt=(a,f)=>{if(a.size<f)return;const s=a.keys().next().value;s!==void 0&&a.delete(s)},Vt=a=>a==="_id"||a==="id"?"id":a,O=a=>e`${e.identifier(Vt(a))}`,Xt=new Set(["_id","id"]),qe=(a,f,s)=>a==="postgres"?e`${f} IS NOT DISTINCT FROM ${s}`:a==="mysql"?e`${f} <=> ${s}`:e`${f} IS ${s}`,Yt=a=>{const f=a.map(s=>e`${O(s.field)} ${e.raw(s.direction==="desc"?"DESC":"ASC")}`);return a.some(s=>Xt.has(s.field))||f.push(e`${O("id")} ASC`),e.join(f,e`, `)},S=(a,f,s)=>{const{params:n,sql:w}=ft(f.name,s);return a.all(w,n)},v=(a,f,s)=>{const{params:n,sql:w}=ft(f.name,s);return a.run(w,n)},ke=async(a,f,s)=>{const n=s.unique?e`UNIQUE `:e``;if(f.name==="mysql"){try{await v(a,f,e`CREATE ${n}INDEX ${e.identifier(s.name)} ON ${e.identifier(s.table)} (${s.columns})`)}catch(w){if(w.errno!==1061)throw w}return}await v(a,f,e`CREATE ${n}INDEX IF NOT EXISTS ${e.identifier(s.name)} ON ${e.identifier(s.table)} (${s.columns})`)},rt=50,F=Pt,st=new WeakMap,De=a=>{const f=st.get(a);if(f!==void 0)return f;const s=(async()=>{let n;try{await a.run('CREATE VIRTUAL TABLE IF NOT EXISTS "__lunora_fts_probe" USING fts5(x)',[]),n=!0}catch{n=!1}finally{try{await a.run('DROP TABLE IF EXISTS "__lunora_fts_probe"',[])}catch{}}return n})();return st.set(a,s),s},Fe=a=>{const f=[];for(const[s,n]of Object.entries(a.shape)){const w=n._meta?.column;w&&f.push([s,w])}return f},ct=new WeakMap,Gt=a=>{let f=ct.get(a);return f===void 0&&(f=Object.entries(a.shape).map(([s,n])=>[s,_e(n)]),ct.set(a,f)),f},zt=(a,f)=>{const s={};for(const[n,w]of Gt(a)){const c=f[n];c!==void 0&&(s[n]=Ht(c,w))}return s._id=f.id,s._creationTime=f._creationTime,s},K=(a,f)=>f?zt(a,f):null,Jt=(a,f,s)=>{const n={...f};for(const[w,c]of Fe(a)){if(c.serverDefault){n[w]=c.serverDefault({auth:s});continue}n[w]===void 0&&(c.defaultFn?n[w]=c.defaultFn():"defaultValue"in c&&(n[w]=c.defaultValue))}return n},dt=(a,f,s,n)=>{for(const[w,c]of Fe(a)){if(c.serverDefault){w in f&&(s[w]=c.serverDefault({auth:n}));continue}c.onUpdateFn&&!(w in f)&&(s[w]=c.onUpdateFn())}},Qt=128,Zt=()=>{const a=new Map;return{delete:f=>{a.delete(f)},get:f=>{const s=a.get(f);if(s!==void 0)return a.delete(f),a.set(f,s),s},set:(f,s)=>{a.has(f)?a.delete(f):Kt(a,Qt),a.set(f,s)}}},ei=async(a,f,s,n,w)=>{const c=w.get(n);if(c!==void 0)return c;const l=[];for(const[y,R]of Object.entries(s.tables))R.shardMode!==void 0&&R.shardMode.kind!=="global"||l.push(y);const g=await Promise.all(l.map(async y=>({found:(await S(a,f,e`SELECT 1 FROM ${e.identifier(y)} WHERE ${e.identifier("id")} = ${n} LIMIT 1`)).length>0,tableName:y})));for(const y of g)if(y.found)return w.set(n,y.tableName),y.tableName},Q=a=>a==null?null:Number(a),ti=(a,f)=>f.map(s=>{const n={};for(const w of a)n[w]=s[w]??null;return{key:n,value:Q(s.value)}}),We=(a,f)=>{const s=[];for(const n of f){const w=K(a,n);w&&s.push(w)}return s},ii=async(a,f,s,n,w,c)=>{const l=_t(w.query);if(l.length===0)return[];const g=xe(n,w.indexName),y=[e`f.${e.identifier("__text__")} MATCH ${Bt(l)}`];for(const C of w.filters)y.push(e`m.${O(C.field)} = ${F(C.value)}`);s.softDeleteMode&&y.push(e`m.${O(s.softDeleteMode.field)} IS NULL`);let R=e`SELECT m.* FROM ${e.identifier(g)} f JOIN ${e.identifier(n)} m ON m.${e.identifier("id")} = f.${e.identifier("__id__")} WHERE ${e.join(y,e` AND `)} ORDER BY f.rank, m.${e.identifier("_creationTime")} DESC`;typeof c=="number"&&(R=e`${R} LIMIT ${e.raw(String(Math.max(0,Math.floor(c))))}`);const I=await S(a,f,R);return We(s,I)},ni=async(a,f,s,n,w,c)=>{const l=_t(w.query);if(l.length===0)return[];const g=w.filters.map(M=>e`${O(M.field)} = ${F(M.value)}`);s.softDeleteMode&&g.push(e`${O(s.softDeleteMode.field)} IS NULL`);let y=e`SELECT * FROM ${e.identifier(n)}`;g.length>0&&(y=e`${y} WHERE ${e.join(g,e` AND `)}`);const R=await S(a,f,y),I=[];for(const M of We(s,R)){const H=jt(ut(M[w.field]),l);H>0&&I.push({creationTime:typeof M._creationTime=="number"?M._creationTime:0,doc:M,score:H})}I.sort((M,H)=>H.score-M.score||H.creationTime-M.creationTime);const C=I.map(M=>M.doc);return typeof c=="number"?C.slice(0,Math.max(0,Math.floor(c))):C},ai=(a,f)=>{const s={eq:(n,w)=>{if(!a.definition.filterFields?.includes(n))throw new b("INTERNAL",`field "${n}" is not a filter field of search index "${a.indexName}" on table "${f}"`);return a.filters.push({field:n,value:w}),s},search:(n,w)=>{if(n!==a.definition.field)throw new b("INTERNAL",`search index "${a.indexName}" on table "${f}" indexes "${a.definition.field}", not "${n}"`);const c=a;return c.field=n,c.query=w,c.hasQuery=!0,s}};return s},oi=(a,f,s)=>[...a.partitionBy??[],...a.sortBy.map(n=>n.field),...a.where?Object.keys(a.where):[]].every(n=>f[n]===s[n]),$t=a=>a.length===1?a[0]:e`(${e.join(a,e` AND `)})`,ri=(a,f,s,n,w)=>{const c=[];for(let l=0;l<s.length+1;l+=1){const g=s.slice(0,l).map(I=>qe(a,e`${e.identifier(I)}`,n[I])),y=s[l],R=f.sortBy[l];l<s.length&&y!==void 0&&R!==void 0?g.push(e`${e.identifier(y)} ${e.raw(R.direction==="desc"?">":"<")} ${n[y]}`):g.push(e`${e.identifier(ue)} < ${w}`),c.push($t(g))}return c.length>0?e.join(c,e` OR `):void 0},si=(a,f,s)=>{if(s.length!==f.length)return;const n=[];for(const[w,c]of f.entries()){const l=[];for(let g=0;g<w;g+=1){const y=f[g];y!==void 0&&l.push(qe(a,e`${e.identifier(y.column)}`,s[g]))}l.push(e`${e.identifier(c.column)} ${e.raw(c.direction==="desc"?"<":">")} ${s[w]}`),n.push($t(l))}return e`(${e.join(n,e` OR `)})`},ci=(a,f)=>{if(a.sortBy.length===0)throw new b("INTERNAL",`rankIndex "${a.name}" requires at least one "sortBy" column for stable pagination`);const s=[{column:"__partition__",direction:"asc"}];for(const[n,w]of a.sortBy.entries())s.push({column:f[n]??G(n),direction:w.direction});return s.push({column:ue,direction:"asc"}),s},di=async(a,f,s,n,w)=>{const c=[];for(let R=0;R<w.length;R+=50)c.push(w.slice(R,R+50));const l=await Promise.all(c.map(async R=>{const I=e.join(R.map(C=>e`${C}`),e`, `);return S(a,f,e`SELECT * FROM ${e.identifier(n)} WHERE ${e.identifier("id")} IN (${I})`)})),g=new Map;for(const R of l)for(const I of R)g.set(I.id,I);const y=[];for(const R of w){const I=K(s,g.get(R));I&&y.push(I)}return y},li=a=>{const f=JSON.stringify(a),s=new TextEncoder().encode(f);let n="";for(const w of s)n+=String.fromCodePoint(w);return btoa(n)},Ce=500,lt=async(a,f,s,n,w)=>{let c,l=!0;for(;l;){const g=c===void 0?await S(a,f,e`SELECT * FROM ${e.identifier(n)} ORDER BY ${e.identifier("id")} ASC LIMIT ${e.raw(String(Ce))}`):await S(a,f,e`SELECT * FROM ${e.identifier(n)} WHERE ${e.identifier("id")} > ${c} ORDER BY ${e.identifier("id")} ASC LIMIT ${e.raw(String(Ce))}`);for(const y of g){const R=K(s,y);R&&w(R)}c=g.at(-1)?.id,l=g.length===Ce}},fi=(a,f)=>f.columnType(_e(a)),ui=(a,f)=>{const s=[];for(const[w,c]of Object.entries(a.shape)){if(!c._meta?.column)continue;const l=c._meta.column.notNull&&c.kind!=="optional"?" NOT NULL":"";s.push(e`${e.identifier(w)} ${e.raw(`${fi(c,f)}${l}`)}`)}const n=f.frameworkColumns().map(w=>e`${e.identifier(w.name)} ${e.raw(w.type)}`);return e.join([...n,...s],e`, `)},_i=async(a,f,s,n)=>{const w=c=>{const l=O(c),g=s.shape[c],y=g&&n.indexKeyPrefix?n.indexKeyPrefix(_e(g)):void 0;return y===void 0?l:e`${l}(${e.raw(String(y))})`};for(const c of s.indexes){const l=e.join(c.fields.map(g=>w(g)),e`, `);await ke(a,n,{columns:l,name:`${f}_${c.name}`,table:f,unique:c.unique??!1})}for(const[c,l]of Fe(s))l.unique&&await ke(a,n,{columns:w(c),name:`${f}_unique_${c}`,table:f,unique:!0})},$i=async(a,f,s)=>{for(const[n,w]of Object.entries(f.tables)){if(w.shardMode?.kind!=="global")continue;const c=ui(w,s);await v(a,s,e`CREATE TABLE IF NOT EXISTS ${e.identifier(n)} (${c})`),await _i(a,n,w,s)}},wi=async(a,f,s)=>{const{integer:n,key:w,real:c}=s.companionTypes;for(const[l,g]of Object.entries(f.tables)){const y=g.aggregateIndexes;if(!(!y||y.length===0))for(const R of y){const I=Y(l,R.name);await v(a,s,e`CREATE TABLE IF NOT EXISTS ${e.identifier(I)} (${e.identifier("__key__")} ${e.raw(w)} PRIMARY KEY, ${e.identifier("__value__")} ${e.raw(c)}, ${e.identifier("__count__")} ${e.raw(n)} NOT NULL DEFAULT 0)`),s.name==="sqlite"&&((await S(a,s,e`PRAGMA table_info(${e.identifier(I)})`)).some(C=>C.name==="__count__")||await v(a,s,e`ALTER TABLE ${e.identifier(I)} ADD COLUMN ${e.identifier("__count__")} ${e.raw(n)} NOT NULL DEFAULT 0`))}}},Me=(a,f,s,n)=>{const w=a.name==="mysql"&&n?e`${e.identifier(f)}(191)`:e`${e.identifier(f)}`;return e`${w} ${e.raw(s)}`},hi=(a,f,s)=>{const n=[Me(a,"__partition__","ASC",!0)];for(const[w,c]of f.sortBy.entries()){const l=s.shape[c.field],g=l!==void 0&&a.indexKeyPrefix?.(_e(l))!==void 0;n.push(Me(a,G(w),c.direction==="desc"?"DESC":"ASC",g))}return n.push(Me(a,"__id__","ASC",!0)),n},Ei=(a,f,s)=>f.sortBy.map((n,w)=>{const c=s.shape[n.field],l=a.columnType(c?_e(c):void 0);return e`${e.identifier(G(w))} ${e.raw(l)}`}),mi=async(a,f,s)=>{const{key:n}=s.companionTypes;for(const[w,c]of Object.entries(f.tables)){const l=c.rankIndexes;if(!(!l||l.length===0))for(const g of l){const y=J(w,g.name),R=Ei(s,g,c),I=R.length>0?e`, ${e.join(R,e`, `)}`:e``;await v(a,s,e`CREATE TABLE IF NOT EXISTS ${e.identifier(y)} (${e.identifier("__id__")} ${e.raw(n)} PRIMARY KEY, ${e.identifier("__partition__")} ${e.raw(n)} NOT NULL${I})`);const C=hi(s,g,c),M=`${w}__rank_${g.name}__btree`;await ke(a,s,{columns:e.join(C,e`, `),name:M,table:y,unique:!1})}}},pi=async(a,f,s)=>{if(await De(a))for(const[n,w]of Object.entries(f.tables)){const c=w.searchIndexes;if(!(!c||c.length===0))for(const l of c){const g=xe(n,l.name);await v(a,s,e`CREATE VIRTUAL TABLE IF NOT EXISTS ${e.identifier(g)} USING fts5(${e.identifier("__text__")}, ${e.identifier("__id__")} UNINDEXED)`)}}},Ne="__cdc_log",yi=async(a,f)=>{const{autoincrementPrimaryKey:s,key:n,real:w,text:c}=f.companionTypes;await v(a,f,e`CREATE TABLE IF NOT EXISTS ${e.identifier(Ne)} (${e.identifier("seq")} ${e.raw(s)}, ${e.identifier("ts")} ${e.raw(w)} NOT NULL, ${e.identifier("table")} ${e.raw(n)} NOT NULL, ${e.identifier("id")} ${e.raw(n)} NOT NULL, ${e.identifier("op")} ${e.raw(n)} NOT NULL, ${e.identifier("doc")} ${e.raw(c)})`)},gi=async(a,f,s,n,w,c,l)=>{await v(a,l,e`INSERT INTO ${e.identifier(Ne)} (${e.identifier("ts")}, ${e.identifier("table")}, ${e.identifier("id")}, ${e.identifier("op")}, ${e.identifier("doc")}) VALUES (${f}, ${s}, ${n}, ${w}, ${c===void 0?null:JSON.stringify(c)})`)},Si=async(a,f,s)=>{const n=f.sinceSeq??0,w=Math.max(1,Math.min(f.limit??1e3,1e4)),c=(await S(a,s,e`SELECT seq, ts, ${e.identifier("table")}, id, op, doc FROM ${e.identifier(Ne)} WHERE seq > ${n} ORDER BY seq ASC LIMIT ${e.raw(String(w))}`)).map(l=>{const{doc:g}=l,y={id:String(l.id),op:String(l.op),seq:Number(l.seq),table:String(l.table),ts:Number(l.ts)};return typeof g=="string"?{...y,doc:JSON.parse(g)}:y});return{changes:c,cursor:c.at(-1)?.seq??n}},vi=async(a,f,s)=>{await v(a,s,e`DELETE FROM ${e.identifier(Ne)} WHERE ${e.identifier("seq")} <= ${f}`)},Ii=a=>{const{crossShardCounter:f,crossShardReader:s,exec:n,maxRelationKeys:w,schema:c}=a,{dialect:l}=a,{isUniqueViolation:g}=l,y={fieldRef:O,serialize:F,...l.name==="mysql"?{likeContains:(t,i)=>e`${t} LIKE CONCAT('%', ${i}, '%') ESCAPE '\\\\'`}:{}},R=(t,i)=>qe(l.name,t,i),I=t=>{const i=e.join(t.columns.map($=>e`${e.identifier($)}`),e`, `),r=e.join(t.values.map($=>e`${$}`),e`, `),o=$=>l.name==="mysql"?e`VALUES(${e.identifier($)})`:e`excluded.${e.identifier($)}`,d=$=>l.name==="postgres"?e`${e.identifier(t.table)}.${e.identifier($)}`:e`${e.identifier($)}`,_=e.join(Object.entries(t.set(o,d)).map(([$,E])=>e`${e.identifier($)} = ${E}`),e`, `),h=l.name==="mysql"?e`ON DUPLICATE KEY UPDATE ${_}`:e`ON CONFLICT(${e.identifier(t.conflictKey)}) DO UPDATE SET ${_}`;return e`INSERT INTO ${e.identifier(t.table)} (${i}) VALUES (${r}) ${h}`},C=a.clock??(()=>Date.now()),M=a.idGenerator??(()=>crypto.randomUUID()),H=a.cdc??!1,be=a.auth??{identity:null,userId:null},Z=async(t,i,r,o)=>{H&&await gi(n,C(),t,i,r,o,l)},wt=a.scheduler??Ut,Re=Zt();let $e=0,Se;const U=async()=>(Se??=(async()=>{await $i(n,c,l),await wi(n,c,l),await mi(n,c,l),await pi(n,c,l),H&&await yi(n,l)})().catch(t=>{throw Se=void 0,t}),Se),ee=async(t,i)=>{await U();const r=await ei(n,l,c,t,Re);if(!(i!==void 0&&r!==i))return r},te=new Map,Ue=async(t,i)=>{const r=Y(t,i);return(await S(n,l,l.tableExists(r))).length>0},we=async(t,i)=>{const r=`${t}::${i.name}`,o=te.get(r);if(o!==void 0)return o;if(!await Ue(t,i.name))return te.set(r,!1),!1;const d=c.tables[t];if(!d)return te.set(r,!1),!1;const _=i.by??[],h=new Map;await lt(n,l,d,t,E=>{if(i.where&&!Le(E,i.where))return;const u=P(_,E);Ft(h,u,i,E)});const $=Y(t,i.name);await v(n,l,e`DELETE FROM ${e.identifier($)}`);for(const[E,u]of h)await v(n,l,e`INSERT INTO ${e.identifier($)} (${e.identifier("__key__")}, ${e.identifier("__value__")}, ${e.identifier("__count__")}) VALUES (${E}, ${u.value}, ${u.count})`);return te.set(r,!0),!0},ht=async(t,i,r)=>{const o=ce(i.op),d=i.field??"",_=[];for(const E of i.by??[]){const u=F(r[E]??null);_.push(u===null?e`${O(E)} IS NULL`:e`${O(E)} = ${u}`)}for(const[E,u]of Object.entries(i.where??{})){const m=u!==null&&typeof u=="object"&&!Array.isArray(u)?u.eq:u,p=F(m);_.push(p===null?e`${O(E)} IS NULL`:e`${O(E)} = ${p}`)}const h=e`SELECT ${e.raw(o)}(${O(d)}) AS value FROM ${e.identifier(t)}`,$=await S(n,l,_.length>0?e`${h} WHERE ${e.join(_,e` AND `)}`:h);return Q($[0]?.value)},Be=async(t,i)=>{await v(n,l,e`DELETE FROM ${e.identifier(t)} WHERE ${e.identifier("__key__")} = ${i} AND ${e.identifier("__count__")} <= 0`)},Et=async(t,i,r,o)=>{const d=Y(t,i.name),{op:_}=i,h=i.field??"",$=r&&(!i.where||Le(r,i.where))?r:void 0,E=o&&(!i.where||Le(o,i.where))?o:void 0;if(!(!$&&!E)){if(_==="count"){const u=new Set;for(const[m,p]of[[$,-1],[E,1]]){if(!m)continue;const N=P(i.by??[],m);u.add(N),await v(n,l,I({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(T,A)=>({__count__:e`${A("__count__")} + ${T("__count__")}`,__value__:e`${A("__value__")} + ${T("__value__")}`}),table:d,values:[N,p,p]}))}for(const m of u)await Be(d,m);return}if(_==="sum"||_==="avg"){const u=new Set;for(const[m,p]of[[$,-1],[E,1]]){if(!m)continue;const N=Oe(m[h]);if(N===void 0)continue;const T=P(i.by??[],m);u.add(T),await v(n,l,I({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(A,D)=>({__count__:e`${D("__count__")} + ${A("__count__")}`,__value__:e`COALESCE(${D("__value__")}, 0) + ${A("__value__")}`}),table:d,values:[T,p*N,p]}))}for(const m of u)await Be(d,m);return}if($){const u=P(i.by??[],$),m=Oe($[h]),p=(await S(n,l,e`SELECT ${e.identifier("__value__")} AS value, ${e.identifier("__count__")} AS count FROM ${e.identifier(d)} WHERE ${e.identifier("__key__")} = ${u}`))[0],N=Q(p?.value),T=(p?.count??0)-1;if(T<=0)await v(n,l,e`DELETE FROM ${e.identifier(d)} WHERE ${e.identifier("__key__")} = ${u}`);else if(p&&m!==void 0&&N!==null&&m===N){const A=await ht(t,i,$);await v(n,l,e`UPDATE ${e.identifier(d)} SET ${e.identifier("__value__")} = ${A}, ${e.identifier("__count__")} = ${T} WHERE ${e.identifier("__key__")} = ${u}`)}else await v(n,l,e`UPDATE ${e.identifier(d)} SET ${e.identifier("__count__")} = ${e.identifier("__count__")} - 1 WHERE ${e.identifier("__key__")} = ${u}`)}if(E){const u=P(i.by??[],E),m=Oe(E[h]);if(m===void 0)await v(n,l,I({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(p,N)=>({__count__:e`${N("__count__")} + 1`}),table:d,values:[u,null,1]}));else{const p=_==="min"?"MIN":"MAX";await v(n,l,I({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(N,T)=>({__count__:e`${T("__count__")} + 1`,__value__:e`${e.raw(p)}(COALESCE(${T("__value__")}, ${N("__value__")}), ${N("__value__")})`}),table:d,values:[u,m,1]}))}}}},he=async t=>{const i=c.tables[t]?.aggregateIndexes;if(!(!i||i.length===0))for(const r of i)await we(t,r)},ie=async(t,i,r)=>{const o=c.tables[t]?.aggregateIndexes;if(!(!o||o.length===0))for(const d of o){const _=`${t}::${d.name}`;(te.get(_)??await Ue(t,d.name))&&await Et(t,d,i,r)}},ne=new Map,je=async(t,i)=>{const r=J(t,i);return(await S(n,l,l.tableExists(r))).length>0},ve=async(t,i)=>{const r=`${t}::rank::${i.name}`,o=ne.get(r);if(o!==void 0)return o;if(!await je(t,i.name))return ne.set(r,!1),!1;const d=c.tables[t];if(!d)return ne.set(r,!1),!1;const _=J(t,i.name);await v(n,l,e`DELETE FROM ${e.identifier(_)}`);const h=i.sortBy.map((u,m)=>G(m)),$=e.join(["__id__","__partition__",...h].map(u=>e`${e.identifier(u)}`),e`, `),E=[];await lt(n,l,d,t,u=>{if(i.where&&!ot(u,i.where))return;const m=Te(i.partitionBy??[],u),p=i.sortBy.map(N=>F(u[N.field]??null));E.push([u._id,m,...p])});for(const u of E){const m=e.join(u.map(p=>e`${p}`),e`, `);await v(n,l,e`INSERT INTO ${e.identifier(_)} (${$}) VALUES (${m})`)}return ne.set(r,!0),!0},Ee=async t=>{const i=c.tables[t]?.rankIndexes;if(!(!i||i.length===0))for(const r of i)await ve(t,r)},z=async(t,i,r,o)=>{const d=c.tables[t],_=d?.rankIndexes;if(!_||_.length===0)return;const h=d?.softDeleteMode?.field,$=h===void 0||o===void 0?void 0:o[h],E=$!=null;for(const u of _){if(r!==void 0&&o!==void 0&&oi(u,r,o))continue;const m=`${t}::rank::${u.name}`;if(!(ne.get(m)??await je(t,u.name)))continue;const p=J(t,u.name);if(r&&await v(n,l,e`DELETE FROM ${e.identifier(p)} WHERE ${e.identifier("__id__")} = ${i}`),o&&!E){if(u.where&&!ot(o,u.where))continue;const N=u.sortBy.map((L,V)=>G(V)),T=e.join(["__id__","__partition__",...N].map(L=>e`${e.identifier(L)}`),e`, `),A=Te(u.partitionBy??[],o),D=u.sortBy.map(L=>F(o[L.field]??null)),x=e.join([i,A,...D].map(L=>e`${L}`),e`, `);await v(n,l,e`INSERT INTO ${e.identifier(p)} (${T}) VALUES (${x})`)}}},ae=async(t,i,r)=>{const o=c.tables[t]?.searchIndexes;if(!(!o||o.length===0||!await De(n)))for(const d of o){const _=xe(t,d.name);await v(n,l,e`DELETE FROM ${e.identifier(_)} WHERE ${e.identifier("__id__")} = ${i}`),r&&await v(n,l,e`INSERT INTO ${e.identifier(_)} (${e.identifier("__text__")}, ${e.identifier("__id__")}) VALUES (${ut(r[d.field])}, ${i})`)}},He=new Set;for(const[t,i]of Object.entries(c.tables))for(const r of Object.values(i.triggerMap??{}))He.add(`${t} ${r.timing} ${r.op}`);const B=(t,i,r)=>He.has(`${t} ${i} ${r}`);let Pe;const j=async(t,i,r)=>{if($e+=1,$e>rt)throw $e-=1,new fe(`trigger recursion exceeded ${String(rt)} levels on "${r.table}" — check for a self-triggering write`,"trigger");try{await Dt({ctx:Pe,event:r,op:i,schema:c,tableName:r.table,timing:t})}finally{$e-=1}},mt=async(t,i)=>{try{await v(n,l,i)}catch(r){throw g(r)?new fe(`unique constraint violation on "${t}"`,"unique"):r}},me=async(t,i)=>(await S(n,l,e`SELECT * FROM ${e.identifier(t)} WHERE ${e.identifier("id")} = ${i}`))[0],pe=async(t,i,r,o)=>{if(o===void 0)return;const d=e.join(Object.keys(o).map($=>R(e`${e.identifier($)}`,o[$])),e` AND `),_=i==="UPDATE"?e`UPDATE ${e.identifier(t)} SET ${r} WHERE ${d}`:e`DELETE FROM ${e.identifier(t)} WHERE ${d}`,h=()=>{throw new fe(`optimistic concurrency conflict on "${t}" — the row changed during this mutation; refetch and retry`,"occ")};try{if(l.supportsReturning)(await S(n,l,e`${_} RETURNING ${e.identifier("id")}`)).length===0&&h();else{const $=await v(n,l,_);(l.affectedRows?l.affectedRows($??{rowsAffected:0}):0)===0&&h()}}catch($){throw g($)?new fe(`unique constraint violation on "${t}"`,"unique"):$}},pt=(t,i,r,o)=>{const d=Object.keys(t.shape);return{columns:["id","_creationTime",...d],values:[i,r,...d.map(_=>F(o[_]??null))]}},yt=async(t,i,r,o)=>{const d=xt(i,r.op,r.field,o.by,o.where);if(!d||!await we(t,d.index))return;const _=Y(t,d.index.name),h=Object.keys(d.partial);if(h.length===(d.index.by??[]).length&&h.length>0){const $=P(d.index.by??[],d.partial),E=await S(n,l,e`SELECT ${e.identifier("__value__")} AS value, ${e.identifier("__count__")} AS count FROM ${e.identifier(_)} WHERE ${e.identifier("__key__")} = ${$}`);if(E.length===0)return[];const u=E[0];return[{key:{...d.partial},value:Ae(r.op,{count:u.count,value:Q(u.value)})}]}return h.length>0?void 0:(await S(n,l,e`SELECT ${e.identifier("__key__")} AS key, ${e.identifier("__value__")} AS value, ${e.identifier("__count__")} AS count FROM ${e.identifier(_)}`)).map($=>{const E=$;return{key:JSON.parse(E.key),value:Ae(r.op,{count:E.count,value:Q(E.value)})}})},Ke=t=>{const i=c.tables[t]?.shardMode?.kind;return i==="shardBy"||i==="root"},Ve=t=>{throw new b("INTERNAL",`cross-backend relation: a global table cannot load the shard-local relation '${t}' (it spans every shard) — wire a cross-shard reader to support it`)},Xe=(t,i)=>Ke(t)?s?s(t,i):Ve(t):W.findMany(t,i),ye=(t,i,r)=>it(t,{fetcher:Xe,maxRelationKeys:w,relationBaseWhere:r,schema:c,tableName:i}),W={async aggregate(t,i){const r=c.tables[t];if(!r)throw new b("INTERNAL",`unknown table: ${t}`);if(await U(),ce(i.op),i.op==="count")return W.count(t,{baseWhere:i.baseWhere,relationBaseWhere:i.relationBaseWhere,restrictsCounts:i.restrictsCounts,where:i.where});if(!i.field)throw new b("INTERNAL",`aggregate(${t}, { op: "${i.op}" }): "field" is required for non-count reducers`);const o=de(r.softDeleteMode,void 0),d=q(q(i.baseWhere,i.where),o),_=await ye(d,t,i.relationBaseWhere),h=_!==d;if(r.aggregateIndexes&&!i.baseWhere&&!h&&!o){const p=kt(r.aggregateIndexes,i.op,i.field,i.where);if(p&&await we(t,p.index)){const N=P(p.index.by??[],p.key),T=Y(t,p.index.name),A=(await S(n,l,e`SELECT ${e.identifier("__value__")} AS value, ${e.identifier("__count__")} AS count FROM ${e.identifier(T)} WHERE ${e.identifier("__key__")} = ${N}`))[0];return Ae(i.op,A===void 0?void 0:{count:A.count,value:Q(A.value)})}}const $=le(_,y),E=e.raw(ce(i.op)),u=e`SELECT ${E}(${O(i.field)}) AS value FROM ${e.identifier(t)}`,m=(await S(n,l,$?e`${u} WHERE ${$}`:u))[0]?.value;return m==null?null:Number(m)},async count(t,i){const r=c.tables[t];if(!r)throw new b("INTERNAL",`unknown table: ${t}`);await U();const o=Ct(i);if(o.restrictsCounts)throw new tt(t);const d=de(r.softDeleteMode,void 0),_=q(q(o.baseWhere,o.where),d),h=await ye(_,t,o.relationBaseWhere),$=h!==_;if(r.aggregateIndexes&&!o.baseWhere&&!$&&!d){const p=Mt(r.aggregateIndexes,o.where);if(p&&await we(t,p.index)){const N=P(p.index.by??[],p.key),T=Y(t,p.index.name),A=await S(n,l,e`SELECT ${e.identifier("__value__")} AS value FROM ${e.identifier(T)} WHERE ${e.identifier("__key__")} = ${N}`);return Number(A[0]?.value??0)}}const E=le(h,y),u=e`SELECT COUNT(*) AS count FROM ${e.identifier(t)}`,m=await S(n,l,E?e`${u} WHERE ${E}`:u);return Number(m[0]?.count??0)},async delete(t,i,r){const o=await ee(t,i);if(!o)return;const d=c.tables[o];if(!d)return;const _=await me(o,t),h=K(d,_),$=r?.hard===!0,E=!$&&d.softDeleteMode?d.softDeleteMode.field:void 0;if(!(E&&(!h||h[E]!==null&&h[E]!==void 0))){if(B(o,"before","delete")&&await j("before","delete",{id:t,op:"delete",previous:h??void 0,table:o}),await Ot({deletedId:t,deletedReference:u=>h?.[u],findHolders:async(u,m,p)=>{if(c.tables[u]?.shardMode?.kind==="shardBy")throw new b("INTERNAL",`cross-backend cascade from global '${o}' into shardBy '${u}' is not supported — would require Query Coordinator fan-out across shards`);return(await W.findMany(u,{includeDeleted:$,where:{[m]:p}})).page},onCascade:(u,m)=>W.delete(m,void 0,r),onRestrict:u=>{throw new fe(u,"restrict")},onSetNull:(u,m,p)=>W.patch(m,{[p]:null}),schema:c,tableName:o}),await he(o),await Ee(o),E&&h){const u={...h,[E]:C()},m=e.join(Object.keys(d.shape).map(p=>e`${e.identifier(p)} = ${F(u[p]??null)}`),e`, `);await pe(o,"UPDATE",m,_),await ie(o,h,u),await z(o,t,h,void 0),await ae(o,t,u),await Z(o,t,"update",u),B(o,"after","delete")&&await j("after","delete",{id:t,op:"delete",previous:h,table:o});return}await pe(o,"DELETE",void 0,_),Re.delete(t),await ie(o,h??void 0,void 0),await z(o,t,h??void 0,void 0),await ae(o,t,void 0),await Z(o,t,"delete"),B(o,"after","delete")&&await j("after","delete",{id:t,op:"delete",previous:h??void 0,table:o})}},async findFirst(t,i={}){return(await W.findMany(t,{...i,limit:1})).page[0]??null},async findFirstOrThrow(t,i={}){const r=await W.findFirst(t,i);if(r===null)throw new Lt(`findFirstOrThrow: no "${t}" document matched`);return r},async findMany(t,i={}){const r=c.tables[t];if(!r)throw new b("INTERNAL",`unknown table: ${t}`);await U();const o=vt(i.orderBy),d=i.cursor?It(o,et(i.cursor)):void 0,_=Xe,h=async(L,V,ge,oe)=>{if(Ke(L))return f?qt(f,L,V,ge,oe):Ve(L);const X=c.tables[L];if(!X)throw new b("INTERNAL",`unknown table: ${L}`);const k=de(X.softDeleteMode,void 0),re={[V]:{in:ge}},gt=q(q(re,oe),k),Tt=await ye(gt,L,void 0),Ye=le(Tt,y),Ge=O(V);let se=e`SELECT ${Ge} AS __fk__, COUNT(*) AS count FROM ${e.identifier(L)}`;Ye&&(se=e`${se} WHERE ${Ye}`),se=e`${se} GROUP BY ${Ge}`;const Nt=await S(n,l,se),ze=new Map;for(const Je of Nt)ze.set(Je.__fk__,Number(Je.count??0));return ze};let $=q(i.baseWhere,i.where);$=q($,de(r.softDeleteMode,i.includeDeleted)),$=await it($,{fetcher:_,maxRelationKeys:w,relationBaseWhere:i.relationBaseWhere,schema:c,tableName:t}),d&&($=$?{AND:[$,d]}:d);const E=le($,y),u=Yt(o);let m=e`SELECT * FROM ${e.identifier(t)}`;E&&(m=e`${m} WHERE ${E}`),m=e`${m} ORDER BY ${u}`;const p=typeof i.limit=="number"?Math.max(0,Math.floor(i.limit)):void 0;p!==void 0&&(m=e`${m} LIMIT ${e.raw(String(p+1))}`);const N=await S(n,l,m),T=We(r,N);if(p===void 0)return i.with&&await nt({fetcher:_,groupedCounter:h,parents:T,schema:c,tableName:t,with:i.with}),{continueCursor:null,isDone:!0,page:at(T,i.select,i.with)};const A=T.length>p,D=A?T.slice(0,p):T,x=D.at(-1);return i.with&&await nt({fetcher:_,groupedCounter:h,parents:D,schema:c,tableName:t,with:i.with}),{continueCursor:A&&x?At(x,o):null,isDone:!A,page:at(D,i.select,i.with)}},async get(t,i){const r=await ee(t,i);if(!r)return null;const o=c.tables[r];if(!o)return null;const d=await S(n,l,e`SELECT * FROM ${e.identifier(r)} WHERE ${e.identifier("id")} = ${t}`);return K(o,d[0])},async groupBy(t,i){const r=c.tables[t];if(!r)throw new b("INTERNAL",`unknown table: ${t}`);await U();const o=i.agg??{op:"count"};if(ce(o.op),o.op!=="count"&&!o.field)throw new b("INTERNAL",`groupBy(${t}, { agg: { op: "${o.op}" } }): "field" is required for non-count reducers`);const d=de(r.softDeleteMode,void 0),_=q(q(i.baseWhere,i.where),d),h=await ye(_,t,i.relationBaseWhere),$=h!==_;if(r.aggregateIndexes&&!i.baseWhere&&!$&&!d){const T=await yt(t,r.aggregateIndexes,o,i);if(T!==void 0)return T}const E=le(h,y),u=i.by.map(T=>e`${O(T)} AS ${e.identifier(T)}`);if(o.op==="count")u.push(e`COUNT(*) AS value`);else{if(!o.field)throw new b("INTERNAL",`groupBy(${t}, { agg: { op: "${o.op}" } }): "field" is required for non-count reducers`);u.push(e`${e.raw(ce(o.op))}(${O(o.field)}) AS value`)}const m=e.join(i.by.map(T=>O(T)),e`, `);let p=e`SELECT ${e.join(u,e`, `)} FROM ${e.identifier(t)}`;E&&(p=e`${p} WHERE ${E}`),p=e`${p} GROUP BY ${m}`;const N=await S(n,l,p);return ti(i.by,N)},async insert(t,i,r){const o=c.tables[t];if(!o)throw new b("INTERNAL",`unknown table: ${t}`);await U();const d=Jt(o,i,be);Ie(o,d);let _,h=!0;r?.clientId!==void 0?(St(r.clientId),_=r.clientId):r?.allowExplicitId&&typeof d._id=="string"?_=d._id:(_=M(),h=!1);const $=r?.allowExplicitId&&typeof d._creationTime=="number"?d._creationTime:C(),E={...d,_creationTime:$,_id:_};B(t,"before","insert")&&await j("before","insert",{doc:{...E},id:_,op:"insert",table:t}),await he(t),await Ee(t);const{columns:u,values:m}=pt(o,_,$,d),p=e.join(u.map(T=>e`${e.identifier(T)}`),e`, `),N=e.join(m.map(T=>e`${T}`),e`, `);return await mt(t,e`INSERT INTO ${e.identifier(t)} (${p}) VALUES (${N})`),h&&Re.set(_,t),await ie(t,void 0,E),await z(t,_,void 0,E),await ae(t,_,E),await Z(t,_,"insert",E),B(t,"after","insert")&&await j("after","insert",{doc:E,id:_,op:"insert",table:t}),_},normalizeId(t,i){return Rt(c,t,i)},async patch(t,i,r){const o=await ee(t,r);if(!o)throw new b("INTERNAL",`document not found: ${t}`);const d=c.tables[o];if(!d)throw new b("INTERNAL",`document not found: ${t}`);const _=await me(o,t),h=K(d,_);if(!h)throw new b("INTERNAL",`document not found: ${t}`);const $={...h,...i,_id:t};dt(d,i,$,be),Ie(d,$),B(o,"before","update")&&await j("before","update",{doc:{...$},id:t,op:"update",previous:h,table:o}),await he(o),await Ee(o);const E=Object.keys(d.shape),u=e.join(E.map(m=>e`${e.identifier(m)} = ${F($[m]??null)}`),e`, `);await pe(o,"UPDATE",u,_),await ie(o,h,$),await z(o,t,h,$),await ae(o,t,$),await Z(o,t,"update",$),B(o,"after","update")&&await j("after","update",{doc:$,id:t,op:"update",previous:h,table:o})},async restore(t,i){const r=await ee(t,i);if(!r)throw new b("INTERNAL",`document not found: ${t}`);const o=c.tables[r],d=o?.softDeleteMode?.field;if(!o||!d)throw new b("INTERNAL",`ctx.db.restore: table "${r}" is not a .softDelete() table`);const _=await me(r,t),h=_?.[d]!==null&&_?.[d]!==void 0;if(await W.patch(t,{[d]:null},i),h){const $=K(o,_);$!==null&&await z(r,t,void 0,{...$,[d]:null})}},query(t){const i=c.tables[t];if(!i)throw new b("INTERNAL",`unknown table: ${t}`);const r="the legacy query()/withIndex() reader is not available on the D1 (global) backend; use findMany",o=async(_,h)=>(await U(),await De(n)?ii(n,l,i,t,_,h):ni(n,l,i,t,_,h)),d=_=>{const h={async collect(){if(!_)throw new b("INTERNAL",r);return o(_,void 0)},filter(){throw new b("INTERNAL",r)},async first(){if(!_)throw new b("INTERNAL",r);return(await o(_,1))[0]??null},order(){return h},async paginate(){throw _?new b("INTERNAL","pagination is not supported on search queries; use .take(n) or .collect()"):new b("INTERNAL",r)},async take($){if(!_)throw new b("INTERNAL",r);return o(_,$)},async unique(){if(!_)throw new b("INTERNAL",r);const $=await o(_,2);if($.length>1)throw new Wt(`unique() on table "${t}" matched ${String($.length)} documents; expected at most one`);return $[0]??null},withGeoIndex(){throw new b("INTERNAL",`geo indexes are not supported on \`.global()\` tables (table "${t}") — geospatial queries run only on sharded tables`)},withIndex(){throw new b("INTERNAL",r)},withSearchIndex($,E){const u=(i.searchIndexes??[]).find(p=>p.name===$);if(!u)throw new b("INTERNAL",`unknown search index "${$}" on table "${t}"`);const m={definition:u,field:u.field,filters:[],hasQuery:!1,indexName:$,query:""};if(E(ai(m,t)),!m.hasQuery)throw new b("INTERNAL",`search index "${$}" on table "${t}" requires a .search(field, query) call`);return d(m)}};return h};return d(void 0)},async rank(t,i,r){const o=c.tables[t];if(!o)throw new b("INTERNAL",`unknown table: ${t}`);const d=o.rankIndexes?.find(x=>x.name===i);if(!d)throw new b("INTERNAL",`unknown rankIndex "${i}" on table "${t}"`);if(r.restrictsCounts)throw new tt(t);if(await U(),!await ve(t,d))return null;const _=typeof r.row=="string"?r.row:r.row._id;if(!_)return null;const h=J(t,d.name),$=d.sortBy.map((x,L)=>G(L)),E=e.join([e`${e.identifier("__partition__")}`,...$.map(x=>e`${e.identifier(x)}`)],e`, `),u=(await S(n,l,e`SELECT ${E} FROM ${e.identifier(h)} WHERE ${e.identifier("__id__")} = ${_}`))[0];if(!u)return null;const m=u.__partition__,p=q(r.baseWhere,r.where);Qe(p,c,t,"rank");const N=Ze(d,p);if(N&&Te(d.partitionBy??[],N)!==m)return null;const T=ri(l.name,d,$,u,_),A=await S(n,l,e`SELECT COUNT(*) AS c FROM ${e.identifier(h)} WHERE ${e.identifier("__partition__")} = ${m}${T?e` AND (${T})`:e``}`),D=await S(n,l,e`SELECT COUNT(*) AS c FROM ${e.identifier(h)} WHERE ${e.identifier("__partition__")} = ${m}`);return{position:Number(A[0]?.c??0)+1,total:Number(D[0]?.c??0)}},async rankPage(t,i,r={}){Qe(q(r.baseWhere,r.where),c,t,"rankPage");const o=c.tables[t];if(!o)throw new b("INTERNAL",`unknown table: ${t}`);const d=o.rankIndexes?.find(k=>k.name===i);if(!d)throw new b("INTERNAL",`unknown rankIndex "${i}" on table "${t}"`);if(await U(),!await ve(t,d))return{continueCursor:null,isDone:!0,page:[]};const _=J(t,d.name),h=d.sortBy.map((k,re)=>G(re)),$=Math.max(1,Math.min(1e3,Math.floor(r.take??100))),E=q(r.baseWhere,r.where),u=Ze(d,E),m=ci(d,h),p=e.join(m.map(k=>e`${e.identifier(k.column)} ${e.raw(k.direction==="desc"?"DESC":"ASC")}`),e`, `),N=[];if(u&&N.push(e`${e.identifier("__partition__")} = ${Te(d.partitionBy??[],u)}`),r.cursor){const k=si(l.name,m,et(r.cursor));k!==void 0&&N.push(k)}const T=e.join([e`${e.identifier(ue)}`,e`${e.identifier("__partition__")}`,...h.map(k=>e`${e.identifier(k)}`)],e`, `);let A=e`SELECT ${T} FROM ${e.identifier(_)}`;N.length>0&&(A=e`${A} WHERE ${e.join(N,e` AND `)}`),A=e`${A} ORDER BY ${p} LIMIT ${e.raw(String($+1))}`;const D=await S(n,l,A),x=D.length>$,L=x?D.slice(0,$):D,V=L.map(k=>k[ue]),ge=await di(n,l,o,t,V);let oe=null;const X=L.at(-1);if(x&&X!==void 0){const k=[X.__partition__,...h.map(re=>X[re]),X[ue]];oe=li(k)}return{continueCursor:oe,isDone:!x,page:ge}},async replace(t,i,r,o){const d=await ee(t,r);if(!d)throw new b("INTERNAL",`document not found: ${t}`);const _=c.tables[d];if(!_)throw new b("INTERNAL",`document not found: ${t}`);const h=await me(d,t);if(h===void 0)throw new b("INTERNAL",`document not found: ${t}`);const $=bt(c,d,"update")||(_.aggregateIndexes??[]).length>0||(_.rankIndexes??[]).length>0?K(_,h)??void 0:void 0,E=o?.allowExplicitId&&typeof i._creationTime=="number"?i._creationTime:C(),u={...i,_creationTime:E,_id:t};dt(_,i,u,be),Ie(_,u),B(d,"before","update")&&await j("before","update",{doc:{...u},id:t,op:"update",previous:$,table:d}),await he(d),await Ee(d);const m=Object.keys(_.shape),p=e.join([e`${e.identifier("_creationTime")} = ${E}`,...m.map(N=>e`${e.identifier(N)} = ${F(u[N]??null)}`)],e`, `);await pe(d,"UPDATE",p,h),await ie(d,$,u),await z(d,t,$,u),await ae(d,t,u),await Z(d,t,"update",u),B(d,"after","update")&&await j("after","update",{doc:u,id:t,op:"update",previous:$,table:d})}};return Pe={db:W,scheduler:wt},W};export{Ii as createSqlCtxDb,zt as decodeGlobalRow,Si as readSqlCdcChanges,wi as runSqlAggregateMigrations,yi as runSqlCdcMigration,$i as runSqlGlobalTableMigrations,mi as runSqlRankMigrations,pi as runSqlSearchMigrations,vi as trimSqlCdcChanges};