@lunora/sql-store 1.0.0-alpha.147 → 1.0.0-alpha.149

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { TableDefinitionLike, SchemaLike, ServerDefaultContextLike, DatabaseWriterLike, CrossShardReadArgs, QueryPage, SchedulerLike, CdcChange, ValidatorLike } from '@lunora/shard-engine';
1
+ import { TableDefinitionLike, ServerDefaultContextLike, DatabaseWriterLike, CrossShardReadArgs, QueryPage, SchedulerLike, SchemaLike, CdcChange, ValidatorLike } from '@lunora/shard-engine';
2
2
  import { SqlRunResult, SqlDialect } from "./dialect.mjs";
3
3
  export type { SqlExec } from "./dialect.mjs";
4
4
  import 'drizzle-orm';
@@ -28,36 +28,6 @@ interface SqlCtxExec {
28
28
  * correct on SQLite, Postgres and MySQL.
29
29
  */
30
30
  declare const decodeGlobalRow: (definition: TableDefinitionLike, row: Record<string, unknown>) => Record<string, unknown>;
31
- /** What {@link backfillSqlSearchIndexes} could not finish. */
32
- interface SqlSearchBackfillResult {
33
- /**
34
- * Inverted companions that still lack their unique `(token, id)` key. Writes
35
- * keep working without it, but two backfills racing over one row can index
36
- * it twice; the warning logged for each says why the key was refused.
37
- */
38
- uniqueKeyMissing: string[];
39
- /** A previous build's rows left unmigrated because every attempt at them lost to a concurrent write. */
40
- unmappedSkipped: number;
41
- }
42
- /**
43
- * Provision the search companions, then index one bounded page of the rows that
44
- * predate each index — unless it is declared `staged: true`, which leaves the
45
- * whole backfill to {@link backfillSqlSearchIndexes}.
46
- *
47
- * Idempotent (`CREATE … IF NOT EXISTS` throughout, and the backfill resumes
48
- * from recorded progress).
49
- */
50
- declare const runSqlSearchMigrations: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<void>;
51
- /**
52
- * Run every declared search index — including the `staged: true` ones the
53
- * migration pass skips — through to completion. The entry point a host calls
54
- * out-of-band after deploying a search index over a table too large to index a
55
- * page at a time.
56
- *
57
- * Idempotent and resumable: an index recorded as complete is skipped, and an
58
- * interrupted run picks up from its cursor.
59
- */
60
- declare const backfillSqlSearchIndexes: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<SqlSearchBackfillResult>;
61
31
  interface SqlCtxDbOptions {
62
32
  /**
63
33
  * Resolved request auth handed to `.serverDefault(fn)` column factories so
@@ -233,17 +203,32 @@ declare const readSqlCdcChanges: (exec: SqlCtxExec, options: {
233
203
  * the cursor as the max `seq` actually returned closes that window by
234
204
  * construction: the caller can never advance past a row this scan did not see.
235
205
  *
236
- * **What it does NOT close**, and the poll's resync interval is what covers it:
237
- * Postgres and MySQL allocate `seq` from a sequence BEFORE commit, so a
238
- * transaction holding a lower `seq` can commit after one holding a higher one.
239
- * No single read can see the uncommitted row, so a change can land below a
240
- * cursor already adopted. That change is invisible to the changelog probe until
241
- * the next unconditional pass — bounded by `GLOBAL_SHAPE_RESYNC_MS`, which is
242
- * the same bound already accepted for an out-of-band writer. D1 has no such
243
- * window (single writer, and `withSession` gives both reads one snapshot).
206
+ * **Late commits.** Postgres and MySQL allocate `seq` at INSERT, not at commit,
207
+ * so an append holding `N` can commit after one holding `N + 1`. A poll that
208
+ * adopts `N + 1` while `N` is still in flight skips `N` until the next resync
209
+ * pass. So on those engines the cursor stops below the first HOLE in `seq`
210
+ * rather than at the highest row seen: a hole is an allocated `seq` that is not
211
+ * (yet) visible, which is exactly what an in-flight append looks like. Tables
212
+ * above the hole are still reported, so nothing is delayed — rows past the
213
+ * cursor are just reported again next tick.
214
+ *
215
+ * A hole can also be permanent (a failed append burns its `seq`), and a cursor
216
+ * pinned under one forever would re-report everything above it on every tick.
217
+ * So a hole only holds the cursor while the first row after it is younger than
218
+ * {@link CDC_LATE_COMMIT_WINDOW_MS}: that row's append started before its own
219
+ * `seq` was allocated, which is after the hole's, so once it is that old the
220
+ * hole's owner has either committed (and is visible) or never will. An append
221
+ * slower than the window falls back to the resync interval, as before. A MySQL
222
+ * `auto_increment_increment` above 1 makes every step a hole, which holds the
223
+ * cursor one window behind the head: correct, just less narrowing.
224
+ *
225
+ * D1 skips all of this: single writer, `AUTOINCREMENT`, and `withSession` gives
226
+ * both reads one snapshot, so there is no hole to wait for — and D1 bills rows
227
+ * read.
244
228
  */
245
229
  declare const readSqlCdcChangedTables: (exec: SqlCtxExec, sinceSeq: number, dialect: SqlDialect, options?: {
246
230
  cursorOnly?: boolean;
231
+ now?: number;
247
232
  retained?: boolean;
248
233
  }) => Promise<{
249
234
  cursor: number;
@@ -284,6 +269,36 @@ declare const runSqlAggregateMigrations: (exec: SqlCtxExec, schema: SchemaLike,
284
269
  * Idempotent (`CREATE TABLE IF NOT EXISTS` + `createIndexIfNotExists`).
285
270
  */
286
271
  declare const runSqlRankMigrations: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<void>;
272
+ /** What {@link backfillSqlSearchIndexes} could not finish. */
273
+ interface SqlSearchBackfillResult {
274
+ /**
275
+ * Inverted companions that still lack their unique `(token, id)` key. Writes
276
+ * keep working without it, but two backfills racing over one row can index
277
+ * it twice; the warning logged for each says why the key was refused.
278
+ */
279
+ uniqueKeyMissing: string[];
280
+ /** A previous build's rows left unmigrated because every attempt at them lost to a concurrent write. */
281
+ unmappedSkipped: number;
282
+ }
283
+ /**
284
+ * Provision the search companions, then index one bounded page of the rows that
285
+ * predate each index — unless it is declared `staged: true`, which leaves the
286
+ * whole backfill to {@link backfillSqlSearchIndexes}.
287
+ *
288
+ * Idempotent (`CREATE … IF NOT EXISTS` throughout, and the backfill resumes
289
+ * from recorded progress).
290
+ */
291
+ declare const runSqlSearchMigrations: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<void>;
292
+ /**
293
+ * Run every declared search index — including the `staged: true` ones the
294
+ * migration pass skips — through to completion. The entry point a host calls
295
+ * out-of-band after deploying a search index over a table too large to index a
296
+ * page at a time.
297
+ *
298
+ * Idempotent and resumable: an index recorded as complete is skipped, and an
299
+ * interrupted run picks up from its cursor.
300
+ */
301
+ declare const backfillSqlSearchIndexes: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<SqlSearchBackfillResult>;
287
302
  /** Reserved table holding one backfill-progress row per search companion. */
288
303
  declare const SEARCH_STATE_TABLE = "__lunora_search_state";
289
304
  /** Create the progress table. Idempotent; runs alongside the companion DDL. */
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { TableDefinitionLike, SchemaLike, ServerDefaultContextLike, DatabaseWriterLike, CrossShardReadArgs, QueryPage, SchedulerLike, CdcChange, ValidatorLike } from '@lunora/shard-engine';
1
+ import { TableDefinitionLike, ServerDefaultContextLike, DatabaseWriterLike, CrossShardReadArgs, QueryPage, SchedulerLike, SchemaLike, CdcChange, ValidatorLike } from '@lunora/shard-engine';
2
2
  import { SqlRunResult, SqlDialect } from "./dialect.js";
3
3
  export type { SqlExec } from "./dialect.js";
4
4
  import 'drizzle-orm';
@@ -28,36 +28,6 @@ interface SqlCtxExec {
28
28
  * correct on SQLite, Postgres and MySQL.
29
29
  */
30
30
  declare const decodeGlobalRow: (definition: TableDefinitionLike, row: Record<string, unknown>) => Record<string, unknown>;
31
- /** What {@link backfillSqlSearchIndexes} could not finish. */
32
- interface SqlSearchBackfillResult {
33
- /**
34
- * Inverted companions that still lack their unique `(token, id)` key. Writes
35
- * keep working without it, but two backfills racing over one row can index
36
- * it twice; the warning logged for each says why the key was refused.
37
- */
38
- uniqueKeyMissing: string[];
39
- /** A previous build's rows left unmigrated because every attempt at them lost to a concurrent write. */
40
- unmappedSkipped: number;
41
- }
42
- /**
43
- * Provision the search companions, then index one bounded page of the rows that
44
- * predate each index — unless it is declared `staged: true`, which leaves the
45
- * whole backfill to {@link backfillSqlSearchIndexes}.
46
- *
47
- * Idempotent (`CREATE … IF NOT EXISTS` throughout, and the backfill resumes
48
- * from recorded progress).
49
- */
50
- declare const runSqlSearchMigrations: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<void>;
51
- /**
52
- * Run every declared search index — including the `staged: true` ones the
53
- * migration pass skips — through to completion. The entry point a host calls
54
- * out-of-band after deploying a search index over a table too large to index a
55
- * page at a time.
56
- *
57
- * Idempotent and resumable: an index recorded as complete is skipped, and an
58
- * interrupted run picks up from its cursor.
59
- */
60
- declare const backfillSqlSearchIndexes: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<SqlSearchBackfillResult>;
61
31
  interface SqlCtxDbOptions {
62
32
  /**
63
33
  * Resolved request auth handed to `.serverDefault(fn)` column factories so
@@ -233,17 +203,32 @@ declare const readSqlCdcChanges: (exec: SqlCtxExec, options: {
233
203
  * the cursor as the max `seq` actually returned closes that window by
234
204
  * construction: the caller can never advance past a row this scan did not see.
235
205
  *
236
- * **What it does NOT close**, and the poll's resync interval is what covers it:
237
- * Postgres and MySQL allocate `seq` from a sequence BEFORE commit, so a
238
- * transaction holding a lower `seq` can commit after one holding a higher one.
239
- * No single read can see the uncommitted row, so a change can land below a
240
- * cursor already adopted. That change is invisible to the changelog probe until
241
- * the next unconditional pass — bounded by `GLOBAL_SHAPE_RESYNC_MS`, which is
242
- * the same bound already accepted for an out-of-band writer. D1 has no such
243
- * window (single writer, and `withSession` gives both reads one snapshot).
206
+ * **Late commits.** Postgres and MySQL allocate `seq` at INSERT, not at commit,
207
+ * so an append holding `N` can commit after one holding `N + 1`. A poll that
208
+ * adopts `N + 1` while `N` is still in flight skips `N` until the next resync
209
+ * pass. So on those engines the cursor stops below the first HOLE in `seq`
210
+ * rather than at the highest row seen: a hole is an allocated `seq` that is not
211
+ * (yet) visible, which is exactly what an in-flight append looks like. Tables
212
+ * above the hole are still reported, so nothing is delayed — rows past the
213
+ * cursor are just reported again next tick.
214
+ *
215
+ * A hole can also be permanent (a failed append burns its `seq`), and a cursor
216
+ * pinned under one forever would re-report everything above it on every tick.
217
+ * So a hole only holds the cursor while the first row after it is younger than
218
+ * {@link CDC_LATE_COMMIT_WINDOW_MS}: that row's append started before its own
219
+ * `seq` was allocated, which is after the hole's, so once it is that old the
220
+ * hole's owner has either committed (and is visible) or never will. An append
221
+ * slower than the window falls back to the resync interval, as before. A MySQL
222
+ * `auto_increment_increment` above 1 makes every step a hole, which holds the
223
+ * cursor one window behind the head: correct, just less narrowing.
224
+ *
225
+ * D1 skips all of this: single writer, `AUTOINCREMENT`, and `withSession` gives
226
+ * both reads one snapshot, so there is no hole to wait for — and D1 bills rows
227
+ * read.
244
228
  */
245
229
  declare const readSqlCdcChangedTables: (exec: SqlCtxExec, sinceSeq: number, dialect: SqlDialect, options?: {
246
230
  cursorOnly?: boolean;
231
+ now?: number;
247
232
  retained?: boolean;
248
233
  }) => Promise<{
249
234
  cursor: number;
@@ -284,6 +269,36 @@ declare const runSqlAggregateMigrations: (exec: SqlCtxExec, schema: SchemaLike,
284
269
  * Idempotent (`CREATE TABLE IF NOT EXISTS` + `createIndexIfNotExists`).
285
270
  */
286
271
  declare const runSqlRankMigrations: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<void>;
272
+ /** What {@link backfillSqlSearchIndexes} could not finish. */
273
+ interface SqlSearchBackfillResult {
274
+ /**
275
+ * Inverted companions that still lack their unique `(token, id)` key. Writes
276
+ * keep working without it, but two backfills racing over one row can index
277
+ * it twice; the warning logged for each says why the key was refused.
278
+ */
279
+ uniqueKeyMissing: string[];
280
+ /** A previous build's rows left unmigrated because every attempt at them lost to a concurrent write. */
281
+ unmappedSkipped: number;
282
+ }
283
+ /**
284
+ * Provision the search companions, then index one bounded page of the rows that
285
+ * predate each index — unless it is declared `staged: true`, which leaves the
286
+ * whole backfill to {@link backfillSqlSearchIndexes}.
287
+ *
288
+ * Idempotent (`CREATE … IF NOT EXISTS` throughout, and the backfill resumes
289
+ * from recorded progress).
290
+ */
291
+ declare const runSqlSearchMigrations: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<void>;
292
+ /**
293
+ * Run every declared search index — including the `staged: true` ones the
294
+ * migration pass skips — through to completion. The entry point a host calls
295
+ * out-of-band after deploying a search index over a table too large to index a
296
+ * page at a time.
297
+ *
298
+ * Idempotent and resumable: an index recorded as complete is skipped, and an
299
+ * interrupted run picks up from its cursor.
300
+ */
301
+ declare const backfillSqlSearchIndexes: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<SqlSearchBackfillResult>;
287
302
  /** Reserved table holding one backfill-progress row per search companion. */
288
303
  declare const SEARCH_STATE_TABLE = "__lunora_search_state";
289
304
  /** Create the progress table. Idempotent; runs alongside the companion DDL. */
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{createSqlCtxDb as a,readSqlCdcChangedTables as o,readSqlCdcChanges as t,readSqlCdcFloor as l,runSqlCdcMigration as s,sweepSqlCdcRetention as n}from"./packem_shared/createSqlCtxDb-BxHRvK9z.mjs";import{runSqlAggregateMigrations as d,runSqlGlobalTableMigrations as S,runSqlRankMigrations as c}from"./packem_shared/runSqlAggregateMigrations-CDcXk9js.mjs";import{d as q,r as C}from"./packem_shared/ctx-db-search-Cny_qeYp.mjs";import{S as m,m as x}from"./packem_shared/ctx-db-search-state-CYA6goYx.mjs";import{g as b,a as u,c as h,s as M,t as T}from"./packem_shared/value-codec-CBr0_L1N.mjs";import{decodeGlobalRow as E}from"./packem_shared/decodeGlobalRow-B-3UbPME.mjs";export{m as SEARCH_STATE_TABLE,q as backfillSqlSearchIndexes,a as createSqlCtxDb,b as decodeBigint,E as decodeGlobalRow,u as effectiveColumnKind,x as migrateSearchState,o as readSqlCdcChangedTables,t as readSqlCdcChanges,l as readSqlCdcFloor,d as runSqlAggregateMigrations,s as runSqlCdcMigration,S as runSqlGlobalTableMigrations,c as runSqlRankMigrations,C as runSqlSearchMigrations,h as sqliteDecode,M as sqliteEncode,n as sweepSqlCdcRetention,T as tryJsonParse};
1
+ import{createSqlCtxDb as a,readSqlCdcChangedTables as o,readSqlCdcChanges as t,readSqlCdcFloor as l,runSqlCdcMigration as s,sweepSqlCdcRetention as n}from"./packem_shared/createSqlCtxDb-Cfh_61Ze.mjs";import{runSqlAggregateMigrations as d,runSqlGlobalTableMigrations as S,runSqlRankMigrations as c}from"./packem_shared/runSqlAggregateMigrations-7Po9Lffj.mjs";import{d as q,r as C}from"./packem_shared/ctx-db-search-zU_gIdYJ.mjs";import{S as m,m as x}from"./packem_shared/ctx-db-search-state-DfjwJOxK.mjs";import{g as b,e as u,a as h,s as M,t as T}from"./packem_shared/value-codec-qpu2yJ-6.mjs";import{decodeGlobalRow as E}from"./packem_shared/decodeGlobalRow-CUEAvNjr.mjs";export{m as SEARCH_STATE_TABLE,q as backfillSqlSearchIndexes,a as createSqlCtxDb,b as decodeBigint,E as decodeGlobalRow,u as effectiveColumnKind,x as migrateSearchState,o as readSqlCdcChangedTables,t as readSqlCdcChanges,l as readSqlCdcFloor,d as runSqlAggregateMigrations,s as runSqlCdcMigration,S as runSqlGlobalTableMigrations,c as runSqlRankMigrations,C as runSqlSearchMigrations,h as sqliteDecode,M as sqliteEncode,n as sweepSqlCdcRetention,T as tryJsonParse};
@@ -0,0 +1 @@
1
+ const o="_version";export{o as OCC_VERSION_COLUMN};
@@ -1 +1 @@
1
- import{S as t,a as S,m as c,r as l,b as i,w as s}from"./ctx-db-search-state-CYA6goYx.mjs";import"./decodeGlobalRow-B-3UbPME.mjs";export{t as SEARCH_STATE_TABLE,S as clearSearchBackfillState,c as migrateSearchState,l as readSearchBackfillState,i as readSearchIndexCoverage,s as writeSearchBackfillState};
1
+ import{S as t,a as S,m as c,r as l,b as i,w as s}from"./ctx-db-search-state-DfjwJOxK.mjs";import"./decodeGlobalRow-CUEAvNjr.mjs";export{t as SEARCH_STATE_TABLE,S as clearSearchBackfillState,c as migrateSearchState,l as readSearchBackfillState,i as readSearchIndexCoverage,s as writeSearchBackfillState};
@@ -0,0 +1 @@
1
+ import"./ctx-db-search-state-DfjwJOxK.mjs";import{d as S,c as s,a as i,r as l}from"./ctx-db-search-zU_gIdYJ.mjs";import"./decodeGlobalRow-CUEAvNjr.mjs";export{S as backfillSqlSearchIndexes,s as createSearchSync,i as runSqlSearch,l as runSqlSearchMigrations};
@@ -0,0 +1,7 @@
1
+ import{LunoraError as T}from"@lunora/errors";import{m as Yt,D as Xt}from"./ctx-db-search-state-DfjwJOxK.mjs";import{c as Qt,r as zt,A as Jt,C as Zt,R as en,u as tn,a as nn,T as on,x as rn,b as sn}from"./ctx-db-search-zU_gIdYJ.mjs";import{serverInList as an,throwingScheduler as cn,CDC_LOG_TABLE as H,cdcForkedError as dn,cursorBelowRetainedFloor as ln,cdcTrimmedError as un,assertNoExplicitUndefined as ot,hasTrigger as fn,runRowValidators as Be,assertFlatPredicate as rt,mergeWhere as O,rankTableName as _e,sortColumnName as pe,resolveRankPartition as st,encodePartitionKey as Ie,resolveRankSeekTuple as wn,RANK_TIEBREAK as Re,CountRlsUnsupportedError as at,stripReservedPatchFields as $n,normalizeIdStructurally as _n,assertValidClientId as hn,aggregateSqlFunction as he,softDeleteScope as Ee,compileWhereSql as J,normalizeOrderKeys as En,uniqueIndexFields as yn,equalityPinnedFields as pn,buildSeekWhere as Rn,decodeCursor as Sn,resolveRelationPredicates as ct,resolveWith as dt,relationHooks as lt,applySelect as ut,encodeCursor as gn,NotFoundError as Tn,applyOnDelete as vn,ConflictError as Le,normalizeCountArgument as mn,selectIndexForCount as bn,encodeAggregateKey as Y,aggregateTableName as Z,selectIndexForAggregate as An,readAggregateValue as Oe,runTriggers as Cn,matchesRankStaticWhere as ft,rankPivotConditionSql as mt,CURSOR_PREFIX as In,selectIndexForGroupBy as Ln,tiebreakDirectionFor as Nn,fanOutScalarCounts as Mn,matchesStaticWhere as We,foldAggregateTally as wt,coerceAggregateNumber as Fe,whereOfFilter as kn,isPushableWhere as qn,CDC_LOG_TABLE_SEQ_INDEX as Dn,NotUniqueError as Bn,renderSql as On,WORKERD_SQLITE_LIMITS as Wn}from"@lunora/shard-engine";import{sql as e}from"drizzle-orm";import{d as bt,a as At,n as Ct,f as It,e as Lt}from"./value-codec-qpu2yJ-6.mjs";import{runSqlGlobalTableMigrations as Fn,runSqlAggregateMigrations as Un,runSqlRankMigrations as xn}from"./runSqlAggregateMigrations-7Po9Lffj.mjs";import{queryAll as C,queryRun as q,createIndexIfNotExists as Pn,decodeRow as ne,serializeDocumentColumn as ee,columnRefSql as W,decodeRows as $t,tableColumns as Nt,nextRowVersion as _t,serializeColumnValue as Ue,forEachRowPaged as ht,BACKFILL_BATCH_SIZE as xe,nullSafeEqualsSql as Ge,queryBatch as Et}from"./decodeGlobalRow-CUEAvNjr.mjs";import{decodeGlobalRow as qi}from"./decodeGlobalRow-CUEAvNjr.mjs";import{OCC_VERSION_COLUMN as ye}from"./OCC_VERSION_COLUMN-ByC64Jgx.mjs";const Hn=(a,c)=>{if(a.size<c)return;const w=a.keys().next().value;w!==void 0&&a.delete(w)},yt=50,Kn=(a,c={})=>{const w=c.maxBatch??yt,o=Number.isInteger(w)&&w>0?w:yt,y=new Map;let f=!1;const d=async(g,m)=>{const A=[...m.keys()];for(let I=0;I<A.length;I+=o){const L=A.slice(I,I+o);try{const F=await a(g,L);for(const G of L){const V=F.get(G);for(const re of m.get(G)??[])re.resolve(V)}}catch(F){for(const G of L)for(const V of m.get(G)??[])V.reject(F)}}},k=()=>{f=!1;const g=[...y.entries()];y.clear();for(const[m,A]of g)d(m,A).catch(()=>{})};return{async load(g,m){return new Promise((A,I)=>{let L=y.get(g);L||(L=new Map,y.set(g,L));const F=L.get(m);F?F.push({reject:I,resolve:A}):L.set(m,[{reject:I,resolve:A}]),f||(f=!0,queueMicrotask(k))})}}},jn=new Set(["_id","id"]),Mt=(a,c)=>a.name!=="postgres"||c.nullable!==!0?"":c.direction==="desc"?" NULLS LAST":" NULLS FIRST",Gn=(a,c)=>{const w=a.map(o=>e`${W(o.field)} ${e.raw(`${o.direction==="desc"?"DESC":"ASC"}${Mt(c,o)}`)}`);if(!a.some(o=>jn.has(o.field))){const o=Nn(a);w.push(e`${W("id")} ${e.raw(o==="desc"?"DESC":"ASC")}`)}return e.join(w,e`, `)},pt=50,Vn=(a,c,w)=>{const o={...c};for(const[y,f]of Nt(a)){if(f.serverDefault){o[y]=f.serverDefault({auth:w});continue}o[y]===void 0&&(f.defaultFn?o[y]=f.defaultFn():"defaultValue"in f&&(o[y]=f.defaultValue))}return o},Rt=(a,c,w,o)=>{for(const[y,f]of Nt(a)){if(f.serverDefault){y in c&&(w[y]=f.serverDefault({auth:o}));continue}f.onUpdateFn&&!(y in c)&&(w[y]=f.onUpdateFn())}},Yn=128,Xn=()=>{const a=new Map;return{delete:c=>{a.delete(c)},get:c=>{const w=a.get(c);if(w!==void 0)return a.delete(c),a.set(c,w),w},set:(c,w)=>{a.has(c)?a.delete(c):Hn(a,Yn),a.set(c,w)}}},Qn=async(a,c,w,o,y)=>{const f=y.get(o);if(f!==void 0)return f;const d=Object.entries(w.tables).filter(([,g])=>g.shardMode===void 0||g.shardMode.kind==="global").map(([g])=>g),k=await Promise.all(d.map(async g=>({found:(await C(a,c,e`SELECT 1 FROM ${e.identifier(g)} WHERE ${e.identifier("id")} = ${o} LIMIT 1`)).length>0,tableName:g})));for(const g of k)if(g.found)return y.set(o,g.tableName),g.tableName},ie=a=>a==null?null:Number(a),zn=(a,c,w)=>w.map(o=>{const y={};for(const f of c){const d=a.shape[f];y[f]=At(o[f]??null,d&&Lt(d))}return{key:y,value:ie(o.value)}}),Jn=(a,c,w)=>[...a.partitionBy??[],...a.sortBy.map(y=>y.field),...a.where?Object.keys(a.where):[]].every(y=>c[y]===w[y]),kt=a=>a.length===1?a[0]:e`(${e.join(a,e` AND `)})`,te=a=>e.join(a.map(c=>e`${e.identifier(c)}`),e`, `),oe=a=>e.join(a.map(c=>e`${c}`),e`, `),Pe=(a,c)=>{if(a==="postgres"){for(const[w,o]of Object.entries(c))if(typeof o=="string"&&o.includes("\0"))throw new T("BAD_REQUEST",`"${w}" holds a NUL character (U+0000), which a Postgres text column cannot store`)}},Ne=a=>a instanceof ArrayBuffer?new Uint8Array(a):Array.isArray(a)?Uint8Array.from(a):a,Zn=(a,c,w,o,y)=>{const f=[];for(let d=0;d<w.length+1;d+=1){const k=w[d],g=c.sortBy[d],m=d<w.length&&k!==void 0&&g!==void 0,A=g?.direction==="desc"?"desc":"asc",I=m?mt(k,Ne(o[k]),A,!1):e`${e.identifier(Re)} < ${y}`;if(I===void 0)continue;const L=w.slice(0,d).map(F=>Ge(a,e`${e.identifier(F)}`,Ne(o[F])));L.push(I),f.push(kt(L))}return f.length>0?e.join(f,e` OR `):e`1 = 0`},ei=(a,c,w)=>{if(w.length!==c.length)return;const o=[];for(const[y,f]of c.entries()){const d=mt(f.column,Ne(w[y]),f.direction,!0);if(d===void 0)continue;const k=[];for(let g=0;g<y;g+=1){const m=c[g];m!==void 0&&k.push(Ge(a,e`${e.identifier(m.column)}`,Ne(w[g])))}k.push(d),o.push(kt(k))}return o.length===0?e`(1 = 0)`:e`(${e.join(o,e` OR `)})`},ti=(a,c)=>{if(a.sortBy.length===0)throw new T("INTERNAL",`rankIndex "${a.name}" requires at least one "sortBy" column for stable pagination`);const w=[{column:"__partition__",direction:"asc",nullable:!1}];for(const[o,y]of a.sortBy.entries())w.push({column:c[o]??pe(o),direction:y.direction,nullable:!0});return w.push({column:Re,direction:"asc",nullable:!1}),w},ni=async(a,c,w,o,y)=>{const d=[];for(let A=0;A<y.length;A+=50)d.push(y.slice(A,A+50));const k=await Promise.all(d.map(async A=>C(a,c,e`SELECT * FROM ${e.identifier(o)} WHERE ${e.identifier("id")} IN (${oe(A)})`))),g=new Map;for(const A of k)for(const I of A)g.set(I.id,I);const m=[];for(const A of y){const I=ne(w,g.get(A));I&&m.push(I)}return m},ii=a=>{const c=a.map(f=>At(f,"bytes")),w=JSON.stringify(Ct(c)?It(c):c),o=new TextEncoder().encode(w);let y="";for(const f of o)y+=String.fromCodePoint(f);return In+btoa(y)},qt=a=>{if(a===void 0)return!1;const c=Lt(a);return c==="any"||c==="bigint"||c==="from"||c==="union"},Ke=(a,c,w)=>{if(qt(a.shape[c]))throw new T("BAD_REQUEST",`${w}: "${c}" may hold an order-preserving key rather than a value SQL can reduce or group — declare an aggregateIndex covering this (by, field, op) so the maintained companion answers it instead (its running total is a REAL, so it stays exact only while the total is inside 2^53)`)},oi=(a,c,w,o)=>{for(const y of w)Ke(a,y,`groupBy(${c}, { by: [..."${y}"] })`);o.field!==void 0&&Ke(a,o.field,`groupBy(${c}, { agg: { op: "${o.op}", field: "${o.field}" } })`)},je="__cdc_sweep",Dt="global",ri=6e4,si=1e4,ai=3e4;let St=0;const ci=async(a,c)=>{const{autoincrementPrimaryKey:w,key:o,real:y,text:f}=c.companionTypes;await q(a,c,e`CREATE TABLE IF NOT EXISTS ${e.identifier(H)} (${e.identifier("seq")} ${e.raw(w)}, ${e.identifier("ts")} ${e.raw(y)} NOT NULL, ${e.identifier("table")} ${e.raw(o)} NOT NULL, ${e.identifier("id")} ${e.raw(o)} NOT NULL, ${e.identifier("op")} ${e.raw(o)} NOT NULL, ${e.identifier("doc")} ${e.raw(f)})`);const d=c.indexKeyPrefix?.("string");await Pn(a,c,{columns:e`${d===void 0?e`${e.identifier("table")}`:e`${e.identifier("table")}(${e.raw(String(d))})`}, ${e.identifier("seq")}`,name:Dn,table:H,unique:!1}),await q(a,c,e`CREATE TABLE IF NOT EXISTS ${e.identifier(je)} (${e.identifier("id")} ${e.raw(o)} NOT NULL PRIMARY KEY, ${e.identifier("lease_until")} ${e.raw(y)} NOT NULL)`),await q(a,c,e`INSERT INTO ${e.identifier(je)} (${e.identifier("id")}, ${e.identifier("lease_until")}) VALUES (${Dt}, ${0}) ${c.name==="mysql"?e`ON DUPLICATE KEY UPDATE ${e.identifier("id")} = ${e.identifier("id")}`:e`ON CONFLICT(${e.identifier("id")}) DO NOTHING`}`)},di=async(a,c,w)=>{const o=e`UPDATE ${e.identifier(je)} SET ${e.identifier("lease_until")} = ${w+ri} WHERE ${e.identifier("id")} = ${Dt} AND ${e.identifier("lease_until")} <= ${w}`;if(c.supportsReturning)return(await C(a,c,e`${o} RETURNING ${e.identifier("id")}`)).length>0;const y=await q(a,c,o);return(c.affectedRows?c.affectedRows(y??{rowsAffected:0}):0)>0},li=async(a,c,w,o)=>{if(!await di(a,c,o))return;const y=o-w;await q(a,c,e`
2
+ DELETE FROM ${e.identifier(H)} WHERE ${e.identifier("seq")} IN (
3
+ SELECT ${e.identifier("seq")} FROM (
4
+ SELECT ${e.identifier("seq")} FROM ${e.identifier(H)} WHERE ${e.identifier("ts")} <= ${y} ORDER BY ${e.identifier("seq")} ASC LIMIT ${e.raw(String(si))}
5
+ ) AS ${e.identifier("expired")}
6
+ )
7
+ `)},Bt=async(a,c)=>{const w=await C(a,c,e`SELECT MIN(${e.identifier("seq")}) AS ${e.identifier("seq")} FROM ${e.identifier(H)}`),o=Number(w[0]?.seq??Number.NaN);return Number.isFinite(o)?o:void 0},ui=async(a,c)=>{if(c.name!=="sqlite")return;const w=await C(a,c,e`SELECT ${e.identifier("seq")} AS ${e.identifier("seq")} FROM ${e.identifier("sqlite_sequence")} WHERE ${e.identifier("name")} = ${H}`),o=Number(w[0]?.seq??Number.NaN);return Number.isFinite(o)?o:0},fi=a=>JSON.stringify(Ct(a)?It(a):a),wi=async(a,c,w,o,y,f,d)=>{await q(a,d,e`INSERT INTO ${e.identifier(H)} (${e.identifier("ts")}, ${e.identifier("table")}, ${e.identifier("id")}, ${e.identifier("op")}, ${e.identifier("doc")}) VALUES (${c}, ${w}, ${o}, ${y}, ${f===void 0?null:fi(f)})`)},Li=async(a,c,w)=>{const o=c.sinceSeq??0,y=Math.max(1,Math.min(c.limit??1e3,1e4)),f=await Bt(a,w),d=await ui(a,w);if(d!==void 0&&o>d)throw dn(d,o,"global");if(f!==void 0&&ln(f,o))throw un(f,o,"global");const g=(await C(a,w,e`SELECT seq, ts, ${e.identifier("table")}, id, op, doc FROM ${e.identifier(H)} WHERE seq > ${o} ORDER BY seq ASC LIMIT ${e.raw(String(y))}`)).map(m=>{const{doc:A}=m,I={id:String(m.id),op:String(m.op),seq:Number(m.seq),table:String(m.table),ts:Number(m.ts)};return typeof A=="string"?{...I,doc:bt(JSON.parse(A))}:I});return{changes:g,cursor:g.at(-1)?.seq??o}},$i=1e4,_i=1e3,gt=(a,c)=>{const w=e.identifier(H),o=e`(SELECT MIN(v.seq) FROM ${w} v WHERE v.seq > ${a} AND v.seq - 1 > ${a} AND v.ts > ${c} AND NOT EXISTS (SELECT 1 FROM ${w} p WHERE p.seq = v.seq - 1))`;return e`${o} AS ${e.identifier("gap")}, (SELECT MAX(q.seq) FROM ${w} q WHERE q.seq > ${a} AND q.seq < ${o}) AS ${e.identifier("below")}`},Tt=(a,c,w)=>{const o=Number(c?.gap??Number.NaN);if(!Number.isFinite(o))return a;const y=Number(c?.below??Number.NaN);return Math.min(a,Number.isFinite(y)?y:w)},hi=async(a,c,w,o={})=>{const y=e.identifier(H),f=w.name!=="sqlite",d=(o.now??Date.now())-$i;if(o.cursorOnly){const I=e`GREATEST((SELECT MAX(seq) FROM ${y}) - ${e.raw(String(_i))}, ${c})`,L=await C(a,w,e`SELECT MAX(seq) AS seq${f?e`, ${gt(I,d)}`:e``} FROM ${y}`),F=Number(L[0]?.seq??c);return{cursor:Tt(Number.isFinite(F)?F:c,L[0],c),tables:[]}}const k=await C(a,w,e`SELECT ${e.identifier("table")} AS ${e.identifier("table")}, MAX(seq) AS seq${f?e`, ${gt(e`${c}`,d)}`:e``} FROM ${y} WHERE seq > ${c} GROUP BY ${e.identifier("table")}`);let g=c;const m=[];for(const I of k){const L=Number(I.seq);Number.isFinite(L)&&L>g&&(g=L),m.push(String(I.table))}g=Tt(g,k[0],c);const A=o.retained===!0?await Bt(a,w):void 0;return{cursor:g,tables:m,...A===void 0?{}:{floor:A}}},Ei=/string or blob too big/iu,yi=/row is too big/iu,pi=(a,c)=>{const{code:w,errno:o}=c,y=c instanceof Error?c.message:"";switch(a.name){case"mysql":return o===1118||w==="ER_TOO_BIG_ROWSIZE"?"InnoDB's per-row ceiling — roughly 8 KB, half a 16 KB page, for the part of the row stored inline":void 0;case"postgres":return yi.test(y)?"the 8 KB heap page a tuple must fit once its wide columns have been TOASTed out":void 0;default:return Ei.test(y)?"the storage engine's per-row ceiling (2 MB on D1)":void 0}},Ri=(a,c,w)=>{const o=pi(a,c);if(o!==void 0)throw new T("PAYLOAD_TOO_LARGE",`document is too large to store in "${w}": a single row cannot exceed ${o}. The limit is on the STORED bytes, which are UTF-8. Keep the payload in R2 (ctx.storage) and store a reference on the row.`)},vt=(a,c,w)=>{throw a.isUniqueViolation(c)?new Le(`unique constraint violation on "${w}"`,"unique"):(Ri(a,c,w),c)},He=new WeakMap,Ni=a=>{const{crossShardCounter:c,crossShardReader:w,exec:o,maxRelationKeys:y,schema:f}=a,{dialect:d}=a,k={fieldRef:W,...d.name==="mysql"?{containsExpr:(t,n)=>e`LOCATE(${n}, ${t}) > 0`}:{},...d.name==="postgres"?{containsExpr:(t,n)=>e`strpos(${t}, ${n}) > 0`}:{},...d.name==="sqlite"?{}:{inList:an(d.name)}},g=new WeakMap,m=t=>{const n=g.get(t);if(n!==void 0)return n;const i={...k,serialize:(r,s)=>ee(t,s,r)};return g.set(t,i),i},A=(t,n)=>Ge(d.name,t,n),I=t=>{const n=te(t.columns),i=oe(t.values),r=_=>d.name==="mysql"?e`VALUES(${e.identifier(_)})`:e`excluded.${e.identifier(_)}`,s=_=>d.name==="postgres"?e`${e.identifier(t.table)}.${e.identifier(_)}`:e`${e.identifier(_)}`,u=e.join(Object.entries(t.set(r,s)).map(([_,p])=>e`${e.identifier(_)} = ${p}`),e`, `),h=d.name==="mysql"?e`ON DUPLICATE KEY UPDATE ${u}`:e`ON CONFLICT(${e.identifier(t.conflictKey)}) DO UPDATE SET ${u}`;return e`INSERT INTO ${e.identifier(t.table)} (${n}) VALUES (${i}) ${h}`},L=a.clock??(()=>Date.now()),F=a.idGenerator??(()=>crypto.randomUUID()),G=a.cdc??!1,V=typeof a.cdcRetentionMs=="number"&&Number.isFinite(a.cdcRetentionMs)&&a.cdcRetentionMs>0?a.cdcRetentionMs:void 0,re=a.auth??{identity:null,userId:null},se=async(t,n,i,r)=>{if(!G||(await wi(o,L(),t,n,i,r,d),V===void 0))return;const s=L();if(!(s-St<ai)){St=s;try{await li(o,d,V,s)}catch{}}},Ot=a.scheduler??cn,Me=Xn();let Se=0;const ke=a.provisionScope??{},x=async()=>{const t=He.get(ke);if(t)return t;const n=(async()=>{await Fn(o,f,d),await Un(o,f,d),await xn(o,f,d),await Yt(o,d),await zt(o,f,d),G&&await ci(o,d)})().catch(i=>{throw He.delete(ke),i});return He.set(ke,n),n},ae=async(t,n)=>{await x();const i=await Qn(o,d,f,t,Me);if(!(n!==void 0&&i!==n))return i},ce=new Map,Ve=async(t,n)=>{const i=Z(t,n);return(await C(o,d,d.tableExists(i))).length>0},ge=async(t,n)=>{const i=`${t}::${n.name}`,r=ce.get(i);if(r!==void 0)return r;if(!await Ve(t,n.name))return ce.set(i,!1),!1;const u=f.tables[t];if(!u)return ce.set(i,!1),!1;const h=n.by??[],_=new Map;await ht(o,d,u,t,E=>{if(n.where&&!We(E,n.where))return;const l=Y(h,E);wt(_,l,n,E)});const p=Z(t,n.name);await q(o,d,e`DELETE FROM ${e.identifier(p)}`);const $=[];for(const[E,l]of _)$.push(e`INSERT INTO ${e.identifier(p)} (${e.identifier("__key__")}, ${e.identifier("__value__")}, ${e.identifier("__count__")}) VALUES (${E}, ${l.value}, ${l.count})`);return await Et(o,d,$),ce.set(i,!0),!0},Wt=async(t,n,i,r)=>{const s=new Map;let u,h=!0;for(;h;){const _=u===void 0?r:[...r,e`${e.identifier("id")} > ${u}`],p=_.length>0?e` WHERE ${e.join(_,e` AND `)}`:e``,$=await C(o,d,e`SELECT ${e.identifier("id")}, ${W(i.field??"")} FROM ${e.identifier(n)}${p} ORDER BY ${e.identifier("id")} ASC LIMIT ${e.raw(String(xe))}`);for(const E of $t(t,$))wt(s,"",i,E);u=$.at(-1)?.id,h=$.length===xe}return s.get("")?.value??null},Ft=async(t,n,i)=>{const r=n.field??"",s=[],u=f.tables[t],h=(E,l)=>u?ee(u,E,l):Ue(l);for(const E of n.by??[]){const l=h(E,i[E]??null);s.push(l===null?e`${W(E)} IS NULL`:e`${W(E)} = ${l}`)}for(const[E,l]of Object.entries(n.where??{})){const S=l!==null&&typeof l=="object"&&!Array.isArray(l)?l.eq:l,R=h(E,S);s.push(R===null?e`${W(E)} IS NULL`:e`${W(E)} = ${R}`)}const _=s.length>0?e` WHERE ${e.join(s,e` AND `)}`:e``;if(u&&qt(u.shape[r]))return Wt(u,t,n,s);const p=he(n.op),$=await C(o,d,e`SELECT ${e.raw(p)}(${W(r)}) AS ${e.identifier("value")} FROM ${e.identifier(t)}${_}`);return ie($[0]?.value)},Ut=async(t,n)=>{await q(o,d,e`DELETE FROM ${e.identifier(t)} WHERE ${e.identifier("__key__")} = ${n} AND ${e.identifier("__count__")} <= 0`)},xt=async(t,n,i,r)=>{const s=Z(t,n.name),{op:u}=n,h=n.field??"",_=i&&(!n.where||We(i,n.where))?i:void 0,p=r&&(!n.where||We(r,n.where))?r:void 0;if(!(!_&&!p)){if(u==="count"||u==="sum"||u==="avg"){const $=new Set;for(const[E,l]of[[_,-1],[p,1]]){if(!E)continue;const S=u==="count"?1:Fe(E[h]);if(S===void 0)continue;const R=Y(n.by??[],E);$.add(R),await q(o,d,I({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(v,b)=>({__count__:e`${b("__count__")} + ${v("__count__")}`,__value__:u==="count"?e`${b("__value__")} + ${v("__value__")}`:e`COALESCE(${b("__value__")}, 0) + ${v("__value__")}`}),table:s,values:[R,l*S,l]}))}for(const E of $)await Ut(s,E);return}if(_){const $=Y(n.by??[],_),E=Fe(_[h]),S=(await C(o,d,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(s)} WHERE ${e.identifier("__key__")} = ${$}`))[0],R=ie(S?.value),v=(S?.count??0)-1;if(v<=0)await q(o,d,e`DELETE FROM ${e.identifier(s)} WHERE ${e.identifier("__key__")} = ${$}`);else if(S&&E!==void 0&&R!==null&&E===R){const b=await Ft(t,n,_);await q(o,d,e`UPDATE ${e.identifier(s)} SET ${e.identifier("__value__")} = ${b}, ${e.identifier("__count__")} = ${v} WHERE ${e.identifier("__key__")} = ${$}`)}else await q(o,d,e`UPDATE ${e.identifier(s)} SET ${e.identifier("__count__")} = ${e.identifier("__count__")} - 1 WHERE ${e.identifier("__key__")} = ${$}`)}if(p){const $=Y(n.by??[],p),E=Fe(p[h]);if(E===void 0)await q(o,d,I({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(l,S)=>({__count__:e`${S("__count__")} + 1`}),table:s,values:[$,null,1]}));else{const l=u==="min"?"MIN":"MAX";await q(o,d,I({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(S,R)=>({__count__:e`${R("__count__")} + 1`,__value__:e`${e.raw(l)}(COALESCE(${R("__value__")}, ${S("__value__")}), ${S("__value__")})`}),table:s,values:[$,E,1]}))}}}},Te=async t=>{const n=f.tables[t]?.aggregateIndexes;if(!(!n||n.length===0))for(const i of n)await ge(t,i)},de=async(t,n,i)=>{const r=f.tables[t]?.aggregateIndexes;if(!(!r||r.length===0))for(const s of r){const u=`${t}::${s.name}`;(ce.get(u)??await Ve(t,s.name))&&await xt(t,s,n,i)}},le=new Map,Ye=async(t,n)=>{const i=_e(t,n);return(await C(o,d,d.tableExists(i))).length>0},qe=async(t,n)=>{const i=`${t}::rank::${n.name}`,r=le.get(i);if(r!==void 0)return r;if(!await Ye(t,n.name))return le.set(i,!1),!1;const u=f.tables[t];if(!u)return le.set(i,!1),!1;const h=_e(t,n.name);await q(o,d,e`DELETE FROM ${e.identifier(h)}`);const _=n.sortBy.map((l,S)=>pe(S)),p=te(["__id__","__partition__",..._]);let $=[];const E=async()=>{if($.length===0)return;const l=$;$=[],await Et(o,d,l)};return await ht(o,d,u,t,async l=>{if(n.where&&!ft(l,n.where))return;const S=Ie(n.partitionBy??[],l),R=n.sortBy.map(v=>Ue(l[v.field]??null));$.push(e`INSERT INTO ${e.identifier(h)} (${p}) VALUES (${oe([l._id,S,...R])})`),$.length>=xe&&await E()}),await E(),le.set(i,!0),!0},ve=async t=>{const n=f.tables[t]?.rankIndexes;if(!(!n||n.length===0))for(const i of n)await qe(t,i)},X=async(t,n,i,r)=>{const s=f.tables[t],u=s?.rankIndexes;if(!u||u.length===0)return;const h=s?.softDeleteMode?.field,_=h===void 0||r===void 0?void 0:r[h],p=_!=null;for(const $ of u){if(i!==void 0&&r!==void 0&&Jn($,i,r))continue;const E=`${t}::rank::${$.name}`;if(!(le.get(E)??await Ye(t,$.name)))continue;const R=_e(t,$.name);if(i&&await q(o,d,e`DELETE FROM ${e.identifier(R)} WHERE ${e.identifier("__id__")} = ${n}`),r&&!p){if($.where&&!ft(r,$.where))continue;const v=$.sortBy.map((D,U)=>pe(U)),b=te(["__id__","__partition__",...v]),M=Ie($.partitionBy??[],r),N=$.sortBy.map(D=>Ue(r[D.field]??null));await q(o,d,e`INSERT INTO ${e.identifier(R)} (${b}) VALUES (${oe([n,M,...N])})`)}}},ue=Qt({dialect:d,exec:o,schema:f}),Xe=new Set;for(const[t,n]of Object.entries(f.tables))for(const i of Object.values(n.triggerMap??{}))Xe.add(`${t} ${i.timing} ${i.op}`);const K=(t,n,i)=>Xe.has(`${t} ${n} ${i}`);let Qe;const j=async(t,n,i)=>{if(Se+=1,Se>pt)throw Se-=1,new Le(`trigger recursion exceeded ${String(pt)} levels on "${i.table}" — check for a self-triggering write`,"trigger");try{await Cn({ctx:Qe,event:i,op:n,schema:f,tableName:i.table,timing:t})}finally{Se-=1}},Pt=async(t,n)=>{try{await q(o,d,n)}catch(i){vt(d,i,t)}},me=async(t,n)=>(await C(o,d,e`SELECT * FROM ${e.identifier(t)} WHERE ${e.identifier("id")} = ${n}`))[0],be=async(t,n,i,r)=>{if(r===void 0)return;const s=e`${e.identifier(ye)}`,u=e.join([A(e`${e.identifier("id")}`,r.id),A(s,r[ye]??null)],e` AND `),h=n==="UPDATE"?e`UPDATE ${e.identifier(t)} SET ${i}, ${s} = ${_t.sql(s)} WHERE ${u}`:e`DELETE FROM ${e.identifier(t)} WHERE ${u}`,_=()=>{throw new Le(`optimistic concurrency conflict on "${t}" — the row changed during this mutation; refetch and retry`,"occ")};try{if(d.supportsReturning)(await C(o,d,e`${h} RETURNING ${e.identifier("id")}`)).length===0&&_();else{const p=await q(o,d,h);(d.affectedRows?d.affectedRows(p??{rowsAffected:0}):0)===0&&_()}}catch(p){vt(d,p,t)}},De=(t,n=t?._creationTime)=>({_creationTime:n,[ye]:_t.value(t?.[ye])}),Ht=(t,n,i,r)=>{const s=Object.keys(t.shape);return{columns:["id","_creationTime",...s],values:[n,i,...s.map(u=>ee(t,u,r[u]??null))]}},Kt=async(t,n,i,r)=>{const s=Ln(n,i.op,i.field,r.by,r.where);if(!s||!await ge(t,s.index))return;const h=Z(t,s.index.name),_=Object.keys(s.partial);if(_.length===(s.index.by??[]).length&&_.length>0){const $=Y(s.index.by??[],s.partial),E=await C(o,d,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(h)} WHERE ${e.identifier("__key__")} = ${$}`);if(E.length===0)return[];const l=E[0];return[{key:{...s.partial},value:Oe(i.op,{count:l.count,value:ie(l.value)})}]}return _.length>0?void 0:(await C(o,d,e`SELECT ${e.identifier("__key__")} AS ${e.identifier("key")}, ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(h)}`)).map($=>{const E=$;return{key:bt(JSON.parse(E.key)),value:Oe(i.op,{count:E.count,value:ie(E.value)})}})},ze=t=>{const n=f.tables[t]?.shardMode?.kind;return n==="shardBy"||n==="root"},Je=t=>{throw new T("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`)},jt=(t,n)=>{if(n?.relationMask!==void 0&&n.with!==void 0&&Object.keys(n.with).length>0)throw new T("MASK_UNSUPPORTED",`masking cannot follow a nested \`with\` across the cross-shard relation hop to "${t}" — the nested rows are hydrated on the serving shard, where the mask policy does not exist. Read the nested relation at its own (masked) call site instead.`);const i={};if(n?.relationBaseWhere)for(const r of Object.keys(f.tables)){const s=n.relationBaseWhere(r);s!==void 0&&(i[r]=s)}return{orderBy:n?.orderBy,relationPolicies:i,where:O(n?.baseWhere,n?.where),with:n?.with}},Ze=(t,n)=>ze(t)?w?w(t,jt(t,n)):Je(t):P.findMany(t,n),Ae=(t,n,i)=>ct(t,{fetcher:Ze,maxRelationKeys:y,relationBaseWhere:i,schema:f,tableName:n}),Gt=Kn(async(t,n)=>{const i=await C(o,d,e`SELECT * FROM ${e.identifier(t)} WHERE ${e.identifier("id")} IN (${oe(n)})`),r=new Map;for(const s of i){const{id:u}=s;typeof u=="string"&&r.set(u,s)}return r}),P={async cdcChangedTables(t,n){if(G)try{return await x(),await hi(o,t,d,{...n,now:L(),retained:V!==void 0})}catch{return}},async aggregate(t,n){const i=f.tables[t];if(!i)throw new T("INTERNAL",`unknown table: ${t}`);if(await x(),he(n.op),n.op==="count")return P.count(t,{baseWhere:n.baseWhere,relationBaseWhere:n.relationBaseWhere,restrictsCounts:n.restrictsCounts,where:n.where});if(!n.field)throw new T("INTERNAL",`aggregate(${t}, { op: "${n.op}" }): "field" is required for non-count reducers`);const r=Ee(i.softDeleteMode,void 0),s=O(O(n.baseWhere,n.where),r),u=await Ae(s,t,n.relationBaseWhere),h=u!==s;if(i.aggregateIndexes&&!n.baseWhere&&!h&&!r){const S=An(i.aggregateIndexes,n.op,n.field,n.where);if(S&&await ge(t,S.index)){const v=Y(S.index.by??[],S.key),b=Z(t,S.index.name),N=(await C(o,d,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(b)} WHERE ${e.identifier("__key__")} = ${v}`))[0];return Oe(n.op,N===void 0?void 0:{count:N.count,value:ie(N.value)})}}Ke(i,n.field,`aggregate(${t}, { op: "${n.op}", field: "${n.field}" })`);const _=J(u,m(i)),p=e.raw(he(n.op)),$=e`SELECT ${p}(${W(n.field)}) AS ${e.identifier("value")} FROM ${e.identifier(t)}`,l=(await C(o,d,_?e`${$} WHERE ${_}`:$))[0]?.value;return l==null?null:Number(l)},async count(t,n){const i=f.tables[t];if(!i)throw new T("INTERNAL",`unknown table: ${t}`);await x();const r=mn(n);if(r.restrictsCounts)throw new at(t);const s=Ee(i.softDeleteMode,void 0),u=O(O(r.baseWhere,r.where),s),h=await Ae(u,t,r.relationBaseWhere),_=h!==u;if(i.aggregateIndexes&&!r.baseWhere&&!_&&!s){const l=bn(i.aggregateIndexes,r.where);if(l&&await ge(t,l.index)){const R=Y(l.index.by??[],l.key),v=Z(t,l.index.name),b=await C(o,d,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")} FROM ${e.identifier(v)} WHERE ${e.identifier("__key__")} = ${R}`);return Number(b[0]?.value??0)}}const p=J(h,m(i)),$=e`SELECT COUNT(*) AS ${e.identifier("count")} FROM ${e.identifier(t)}`,E=await C(o,d,p?e`${$} WHERE ${p}`:$);return Number(E[0]?.count??0)},async delete(t,n,i){const r=await ae(t,n);if(!r)return;const s=f.tables[r];if(!s)return;const u=await me(r,t),h=ne(s,u),_=i?.hard===!0,p=!_&&s.softDeleteMode?s.softDeleteMode.field:void 0;if(!(p&&(!h||h[p]!==null&&h[p]!==void 0))){if(K(r,"before","delete")&&await j("before","delete",{id:t,op:"delete",previous:h??void 0,table:r}),await vn({deletedId:t,deletedReference:$=>h?.[$],findHolders:async($,E,l)=>{if(f.tables[$]?.shardMode?.kind==="shardBy")throw new T("INTERNAL",`cross-backend cascade from global '${r}' into shardBy '${$}' is not supported — would require Query Coordinator fan-out across shards`);return(await P.findMany($,{includeDeleted:_,where:{[E]:l}})).page},onCascade:($,E)=>P.delete(E,void 0,i),onRestrict:$=>{throw new Le($,"restrict")},onSetNull:($,E,l)=>P.patch(E,{[l]:null}),schema:f,tableName:r}),await Te(r),await ve(r),p&&h){const $={...h,[p]:L()},E=e.join(Object.keys(s.shape).map(l=>e`${e.identifier(l)} = ${ee(s,l,$[l]??null)}`),e`, `);await be(r,"UPDATE",E,u),await de(r,h,$),await X(r,t,h,void 0),await ue(r,t,{document:$,previous:h,written:De(u)}),await se(r,t,"update",$),K(r,"after","delete")&&await j("after","delete",{id:t,op:"delete",previous:h,table:r});return}await be(r,"DELETE",void 0,u),Me.delete(t),await de(r,h??void 0,void 0),await X(r,t,h??void 0,void 0),await ue(r,t,void 0),await se(r,t,"delete"),K(r,"after","delete")&&await j("after","delete",{id:t,op:"delete",previous:h??void 0,table:r})}},async findFirst(t,n={}){return(await P.findMany(t,{...n,limit:1})).page[0]??null},async findFirstOrThrow(t,n={}){const i=await P.findFirst(t,n);if(i===null)throw new Tn(`findFirstOrThrow: no "${t}" document matched`);return i},async findMany(t,n={}){const i=f.tables[t];if(!i)throw new T("INTERNAL",`unknown table: ${t}`);await x();const r=En(n.orderBy,i.shape,{pinned:pn(n.where),uniqueBy:yn(i.indexes,i.shape)}),s=n.cursor?Rn(r,Sn(n.cursor)):void 0,u=Ze,h=async(N,D,U,Q)=>{if(ze(N))return c?Mn(c,N,D,U,Q):Je(N);const fe=f.tables[N];if(!fe)throw new T("INTERNAL",`unknown table: ${N}`);const Ce=Ee(fe.softDeleteMode,void 0),z={[D]:{in:U}},B=O(O(z,Q),Ce),we=await Ae(B,N,void 0),et=J(we,m(fe)),tt=W(D);let $e=e`SELECT ${tt} AS __fk__, COUNT(*) AS ${e.identifier("count")} FROM ${e.identifier(N)}`;et&&($e=e`${$e} WHERE ${et}`),$e=e`${$e} GROUP BY ${tt}`;const Vt=await C(o,d,$e),nt=new Map;for(const it of Vt)nt.set(it.__fk__,Number(it.count??0));return nt};let _=O(n.baseWhere,n.where);_=O(_,Ee(i.softDeleteMode,n.includeDeleted)),_=await ct(_,{fetcher:u,maxRelationKeys:y,relationBaseWhere:n.relationBaseWhere,schema:f,tableName:t}),s&&(_=_?{AND:[_,s]}:s);const p=J(_,m(i)),$=Gn(r,d);let E=e`SELECT * FROM ${e.identifier(t)}`;p&&(E=e`${E} WHERE ${p}`),E=e`${E} ORDER BY ${$}`;const l=typeof n.limit=="number"?Math.max(0,Math.floor(n.limit)):void 0;l!==void 0&&(E=e`${E} LIMIT ${e.raw(String(l+1))}`);const S=await C(o,d,E),R=$t(i,S);if(l===void 0)return n.with&&await dt({fetcher:u,groupedCounter:h,parents:R,...lt(n),schema:f,tableName:t,with:n.with}),{continueCursor:null,isDone:!0,page:ut(R,n.select,n.with)};const v=R.length>l,b=v?R.slice(0,l):R,M=b.at(-1);return n.with&&await dt({fetcher:u,groupedCounter:h,parents:b,...lt(n),schema:f,tableName:t,with:n.with}),{continueCursor:v&&M?gn(M,r):null,isDone:!v,page:ut(b,n.select,n.with)}},async get(t,n){const i=await ae(t,n);if(!i)return null;const r=f.tables[i];return r?ne(r,await Gt.load(i,t)):null},async groupBy(t,n){const i=f.tables[t];if(!i)throw new T("INTERNAL",`unknown table: ${t}`);await x();const r=n.agg??{op:"count"};if(he(r.op),r.op!=="count"&&!r.field)throw new T("INTERNAL",`groupBy(${t}, { agg: { op: "${r.op}" } }): "field" is required for non-count reducers`);const s=Ee(i.softDeleteMode,void 0),u=O(O(n.baseWhere,n.where),s),h=await Ae(u,t,n.relationBaseWhere),_=h!==u;if(i.aggregateIndexes&&!n.baseWhere&&!_&&!s){const R=await Kt(t,i.aggregateIndexes,r,n);if(R!==void 0)return R}oi(i,t,n.by,r);const p=J(h,m(i)),$=n.by.map(R=>e`${W(R)} AS ${e.identifier(R)}`);if(r.op==="count")$.push(e`COUNT(*) AS ${e.identifier("value")}`);else{if(!r.field)throw new T("INTERNAL",`groupBy(${t}, { agg: { op: "${r.op}" } }): "field" is required for non-count reducers`);$.push(e`${e.raw(he(r.op))}(${W(r.field)}) AS ${e.identifier("value")}`)}const E=e.join(n.by.map(R=>W(R)),e`, `);let l=e`SELECT ${e.join($,e`, `)} FROM ${e.identifier(t)}`;p&&(l=e`${l} WHERE ${p}`),l=e`${l} GROUP BY ${E}`;const S=await C(o,d,l);return zn(i,n.by,S)},async insert(t,n,i){const r=f.tables[t];if(!r)throw new T("INTERNAL",`unknown table: ${t}`);await x(),Pe(d.name,n);const s=Vn(r,n,re);Be(r,s);let u,h=!0;i?.clientId!==void 0?(hn(i.clientId),u=i.clientId):i?.allowExplicitId&&typeof s._id=="string"?u=s._id:(u=F(),h=!1);const _=i?.allowExplicitId&&typeof s._creationTime=="number"?s._creationTime:L(),p={...s,_creationTime:_,_id:u};K(t,"before","insert")&&await j("before","insert",{doc:{...p},id:u,op:"insert",table:t}),await Te(t),await ve(t);const{columns:$,values:E}=Ht(r,u,_,s);return await Pt(t,e`INSERT INTO ${e.identifier(t)} (${te($)}) VALUES (${oe(E)})`),h&&Me.set(u,t),await de(t,void 0,p),await X(t,u,void 0,p),await ue(t,u,{document:p,written:{_creationTime:_,[ye]:null}}),await se(t,u,"insert",p),K(t,"after","insert")&&await j("after","insert",{doc:p,id:u,op:"insert",table:t}),u},normalizeId(t,n){return _n(f,t,n)},async patch(t,n,i){ot("patch",n),Pe(d.name,n);const r=await ae(t,i);if(!r)throw new T("INTERNAL",`document not found: ${t}`);const s=f.tables[r];if(!s)throw new T("INTERNAL",`document not found: ${t}`);const u=await me(r,t),h=ne(s,u);if(!h)throw new T("INTERNAL",`document not found: ${t}`);const _={...h,...$n(n),_id:t};Rt(s,n,_,re),Be(s,_,!0),K(r,"before","update")&&await j("before","update",{doc:{..._},id:t,op:"update",previous:h,table:r}),await Te(r),await ve(r);const p=Object.keys(s.shape),$=e.join(p.map(E=>e`${e.identifier(E)} = ${ee(s,E,_[E]??null)}`),e`, `);await be(r,"UPDATE",$,u),await de(r,h,_),await X(r,t,h,_),await ue(r,t,{document:_,previous:h,written:De(u)}),await se(r,t,"update",_),K(r,"after","update")&&await j("after","update",{doc:_,id:t,op:"update",previous:h,table:r})},async restore(t,n){const i=await ae(t,n);if(!i)throw new T("INTERNAL",`document not found: ${t}`);const r=f.tables[i],s=r?.softDeleteMode?.field;if(!r||!s)throw new T("INTERNAL",`ctx.db.restore: table "${i}" is not a .softDelete() table`);const u=await me(i,t),h=u?.[s]!==null&&u?.[s]!==void 0;if(await P.patch(t,{[s]:null},n),h){const _=ne(r,u);_!==null&&await X(i,t,void 0,{..._,[s]:null})}},query(t){const n=f.tables[t];if(!n)throw new T("INTERNAL",`unknown table: ${t}`);const i="the legacy query()/withIndex() reader is not available on the D1 (global) backend; use findMany",r=[];let s=0,u;const h=d.name!=="mysql",_={...m(n),fieldRef:l=>e`m.${W(l)}`},p=l=>r.every(S=>S(l)),$=async(l,S)=>{await x();const R=2*tn+l.filters.length,v=u===void 0?void 0:J(u,_,void 0,R),b=v!==void 0&&d.name==="sqlite"&&On(d.name,v).params.length+R>Wn.boundParams,M=b?void 0:v,N=s>0||b,D=await nn(o,d,n,t,M===void 0?l:{...l,scope:M},on(N?void 0:S));!N&&S===void 0&&rn(D);const U=D.filter(Q=>p(Q));return S===void 0?U:U.slice(0,S)},E=l=>{const S={async*[Symbol.asyncIterator](){if(!l)throw new T("INTERNAL",i);yield*await $(l,void 0)},async collect(){if(!l)throw new T("INTERNAL",i);return $(l,void 0)},async collectWithScores(){throw new T("GLOBAL_SEARCH_SCORES_UNSUPPORTED",`collectWithScores() is not supported on \`.global()\` tables (table "${t}") — relevance scores are not yet surfaced on this backend; use .collect() instead`)},filter(R){r.push(R);const v=kn(R);return v!==void 0&&qn(v,n.shape,h)?u=O(u,v):s+=1,S},async first(){if(!l)throw new T("INTERNAL",i);return(await $(l,1))[0]??null},order(){return S},async paginate(R){if(!l)throw new T("INTERNAL",i);const v=Zt(R);return en(await $(l,sn(v)),v)},async take(R){if(!l)throw new T("INTERNAL",i);return $(l,R)},async unique(){if(!l)throw new T("INTERNAL",i);const R=await $(l,2);if(R.length>1)throw new Bn(`unique() on table "${t}" matched ${String(R.length)} documents; expected at most one`);return R[0]??null},withGeoIndex(){throw new T("INTERNAL",`geo indexes are not supported on \`.global()\` tables (table "${t}") — geospatial queries run only on sharded tables`)},withIndex(){throw new T("INTERNAL",i)},withSearchIndex(R,v){const b=(n.searchIndexes??[]).find(N=>N.name===R);if(!b)throw new T("INTERNAL",`unknown search index "${R}" on table "${t}"`);const M={definition:b,field:b.field,filters:[],hasQuery:!1,indexName:R,query:""};if(v(Jt(M,t,Xt(b.language))),!M.hasQuery)throw new T("INTERNAL",`search index "${R}" on table "${t}" requires a .search(field, query) call`);return E(M)}};return S};return E(void 0)},async rank(t,n,i){const r=f.tables[t];if(!r)throw new T("INTERNAL",`unknown table: ${t}`);const s=r.rankIndexes?.find(D=>D.name===n);if(!s)throw new T("INTERNAL",`unknown rankIndex "${n}" on table "${t}"`);if(i.restrictsCounts)throw new at(t);if(await x(),!await qe(t,s))return null;const h=typeof i.row=="string"?i.row:i.row._id;if(!h)return null;const _=_e(t,s.name),p=s.sortBy.map((D,U)=>pe(U)),$=te(["__partition__",...p]),l=(await C(o,d,e`SELECT ${$} FROM ${e.identifier(_)} WHERE ${e.identifier("__id__")} = ${h}`))[0];if(!l)return null;const S=l.__partition__,R=O(i.baseWhere,i.where);rt(R,f,t,"rank");const v=st(s,R);if(v&&Ie(s.partitionBy??[],v)!==S)return null;const b=Zn(d.name,s,p,l,h),M=await C(o,d,e`SELECT COUNT(*) AS c FROM ${e.identifier(_)} WHERE ${e.identifier("__partition__")} = ${S} AND (${b})`),N=await C(o,d,e`SELECT COUNT(*) AS c FROM ${e.identifier(_)} WHERE ${e.identifier("__partition__")} = ${S}`);return{position:Number(M[0]?.c??0)+1,total:Number(N[0]?.c??0)}},async rankPage(t,n,i={}){rt(O(i.baseWhere,i.where),f,t,"rankPage");const r=f.tables[t];if(!r)throw new T("INTERNAL",`unknown table: ${t}`);const s=r.rankIndexes?.find(B=>B.name===n);if(!s)throw new T("INTERNAL",`unknown rankIndex "${n}" on table "${t}"`);if(await x(),!await qe(t,s))return{continueCursor:null,isDone:!0,page:[]};const h=_e(t,s.name),_=s.sortBy.map((B,we)=>pe(we)),p=Math.max(1,Math.min(1e3,Math.floor(i.take??100))),$=O(i.baseWhere,i.where),E=st(s,$),l=ti(s,_),S=e.join(l.map(B=>e`${e.identifier(B.column)} ${e.raw(`${B.direction==="desc"?"DESC":"ASC"}${Mt(d,{direction:B.direction,nullable:B.nullable})}`)}`),e`, `),R=[];typeof i.partitionKey=="string"?R.push(e`${e.identifier("__partition__")} = ${i.partitionKey}`):E&&R.push(e`${e.identifier("__partition__")} = ${Ie(s.partitionBy??[],E)}`);const v=wn(i);if(v!==void 0){const B=ei(d.name,l,v);B!==void 0&&R.push(B)}const b=te([Re,"__partition__",..._]);let M=e`SELECT ${b} FROM ${e.identifier(h)}`;R.length>0&&(M=e`${M} WHERE ${e.join(R,e` AND `)}`),M=e`${M} ORDER BY ${S} LIMIT ${e.raw(String(p+1))}`;const N=await C(o,d,M),D=N.length>p,U=D?N.slice(0,p):N,Q=U.map(B=>B[Re]),fe=await ni(o,d,r,t,Q);let Ce=null;const z=U.at(-1);if(D&&z!==void 0){const B=[z.__partition__,..._.map(we=>z[we]),z[Re]];Ce=ii(B)}return{continueCursor:Ce,isDone:!D,page:fe}},async replace(t,n,i,r){ot("replace",n),Pe(d.name,n);const s=await ae(t,i);if(!s)throw new T("INTERNAL",`document not found: ${t}`);const u=f.tables[s];if(!u)throw new T("INTERNAL",`document not found: ${t}`);const h=await me(s,t);if(h===void 0)throw new T("INTERNAL",`document not found: ${t}`);const p=fn(f,s,"update")||(u.aggregateIndexes??[]).length>0||(u.rankIndexes??[]).length>0?ne(u,h)??void 0:void 0,$=r?.allowExplicitId&&typeof n._creationTime=="number"?n._creationTime:void 0,E=typeof h._creationTime=="number"?h._creationTime:void 0,l=$??E??L(),S={...n,_creationTime:l,_id:t};Rt(u,n,S,re),Be(u,S),K(s,"before","update")&&await j("before","update",{doc:{...S},id:t,op:"update",previous:p,table:s}),await Te(s),await ve(s);const R=Object.keys(u.shape),v=e.join([e`${e.identifier("_creationTime")} = ${l}`,...R.map(b=>e`${e.identifier(b)} = ${ee(u,b,S[b]??null)}`)],e`, `);await be(s,"UPDATE",v,h),await de(s,p,S),await X(s,t,p,S),await ue(s,t,{document:S,previous:p,written:De(h,l)}),await se(s,t,"update",S),K(s,"after","update")&&await j("after","update",{doc:S,id:t,op:"update",previous:p,table:s})}};return Qe={db:P,scheduler:Ot},P};export{Ni as createSqlCtxDb,qi as decodeGlobalRow,hi as readSqlCdcChangedTables,Li as readSqlCdcChanges,Bt as readSqlCdcFloor,ci as runSqlCdcMigration,li as sweepSqlCdcRetention};
@@ -1 +1 @@
1
- import{sql as e}from"drizzle-orm";import{queryRun as p,queryAll as m}from"./decodeGlobalRow-B-3UbPME.mjs";const y=["de","en","es","fr","it","nl","none","pt"],L=i=>y.includes(i),A="a an and are as at be but by for if in into is it no not of on or such that the their then there these they this to was will with",R="aber als am an auch auf aus bei bin bis bist da dass der den des dem die das denn dir du ein eine für hat ich im in ist mit nicht noch nur oder sich sie sind über und von vor war wie wir zu zum zur",F="a al como con de del el en es la las lo los mas no o para pero por que se su sus un una uno y ya",N="au aux avec ce ces dans de des du elle en et eux il je la le les leur lui ma mais me même mes moi mon ne nos notre nous on ou par pas pour qu que qui sa se ses son sur ta te tes toi ton tu un une vos votre vous y",_="a ai al alla anche che chi ci coi col come con da dal degli dei del della di do e ed gli ha hai hanno i il in la le lo ma mi ne nei nel non o per più quale quanto se si sono su sul tra un una uno vi",q="aan al als bij dan dat de der deze die dit door een en er het hij ij in is je kan me men met mij na naar niet nog nu of om ons ook op over te tot uit van voor was wat we wij zij zijn zo",x="a ao aos as até com como da das de do dos e em entre era essa esse esta este eu foi há isso já mais mas me mesmo meu na nas no nos num numa o os ou para pela pelo por qual que quem se sem seu só sua também te tem um uma você",C=new RegExp("(?<=\\p{Script=Cyrillic})[\\u0300-\\u0305\\u0307\\u0309-\\u036F]|(?<!\\p{Script=Cyrillic})[\\u0300-\\u036F]","gu"),O=/[\u0080-\u{10FFFF}]/u,b=/[\p{L}\p{N}]+/gu,H=/[\p{Script_Extensions=Han}\p{Script_Extensions=Hiragana}\p{Script_Extensions=Katakana}\p{Script_Extensions=Hangul}]+/gu,g=/[\p{Script_Extensions=Han}\p{Script_Extensions=Hiragana}\p{Script_Extensions=Katakana}\p{Script_Extensions=Hangul}]/u,D=i=>{if(!g.test(i))return[i];const n=[];let t=0;for(const r of i.matchAll(H)){const o=r.index,s=[...r[0]];if(o>t&&n.push(i.slice(t,o)),t=o+r[0].length,s.length===1){n.push(r[0]);continue}for(let a=0;a+1<s.length;a+=1)n.push(`${String(s[a])}${String(s[a+1])}`)}return t<i.length&&n.push(i.slice(t)),n},v=i=>O.test(i)?i.normalize("NFD").replaceAll(C,"").normalize("NFC").toLowerCase():i.toLowerCase(),f=i=>new Set(v(i).split(" ")),j={de:f(R),en:f(A),es:f(F),fr:f(N),it:f(_),nl:f(q),none:new Set,pt:f(x)},z=3,U=256,w=new Map,k=i=>{const n=i!==void 0&&L(i)?i:"none",t=w.get(n);if(t)return t;const r=j[n],o=a=>{const d=v(a),$=d.match(b)??[],u=(g.test(d)?$.flatMap(l=>D(l)):$).filter(l=>l.length<=U);return r.size===0?u:u.filter(l=>!r.has(l))},s={document:o,profile:`${n}-v${String(z)}`,query:a=>{const d=o(a);return d.filter(($,u)=>d.lastIndexOf($)===u)}};return w.set(n,s),s},W=i=>`${k(i.language).profile}:${i.field}`,S=i=>{const n=i.slice(i.indexOf(":")+1),t=n.lastIndexOf("/");return t===-1?n:n.slice(0,t)},V=(i,n)=>i.profile!==n?{cursor:void 0,finished:!1,wipe:i.cursor!==void 0||i.done}:{cursor:i.cursor,finished:i.done,wipe:!1},I=(i,n)=>typeof i=="string"&&S(i)===S(n),c="__lunora_search_state",K=async(i,n)=>{const{integer:t,key:r}=n.companionTypes,o=async(s,a)=>{try{await p(i,n,e`ALTER TABLE ${e.identifier(c)} ADD COLUMN ${e.identifier(s)} ${e.raw(a)}`)}catch{}};await p(i,n,e`CREATE TABLE IF NOT EXISTS ${e.identifier(c)} (${e.identifier("companion")} ${e.raw(r)} PRIMARY KEY, ${e.identifier("cursor")} ${e.raw(r)}, ${e.identifier("done")} ${e.raw(t)} NOT NULL DEFAULT 0, ${e.identifier("profile")} ${e.raw(r)}, ${e.identifier("covered")} ${e.raw(t)} NOT NULL DEFAULT 0)`),await o("profile",r),await o("covered",`${t} NOT NULL DEFAULT 0`),await p(i,n,e`UPDATE ${e.identifier(c)} SET ${e.identifier("covered")} = 1 WHERE ${e.identifier("done")} = 1 AND ${e.identifier("covered")} = 0`)},T=i=>i===1||i===!0||i==="1",P=async(i,n,t)=>{const r=await m(i,n,e`SELECT ${e.identifier("covered")} FROM ${e.identifier(c)} WHERE ${e.identifier("companion")} = ${t}`);return T(r[0]?.covered)},Y=async(i,n,t)=>{await p(i,n,e`DELETE FROM ${e.identifier(c)} WHERE ${e.identifier("companion")} = ${t}`)},X=async(i,n,t)=>{const o=(await m(i,n,e`SELECT ${e.identifier("cursor")}, ${e.identifier("done")}, ${e.identifier("profile")} FROM ${e.identifier(c)} WHERE ${e.identifier("companion")} = ${t}`))[0];if(!o)return{cursor:void 0,done:!1,profile:void 0};const{cursor:s,profile:a}=o;return{cursor:typeof s=="string"?s:void 0,done:T(o.done),profile:typeof a=="string"?a:void 0}},G=async(i,n,t,r,o,s)=>{const a=r??null,d=await m(i,n,e`SELECT ${e.identifier("profile")} FROM ${e.identifier(c)} WHERE ${e.identifier("companion")} = ${t}`),$=I(d[0]?.profile,s),u=o?1:0,l=o||!$?e`${e.identifier("covered")} = ${u}`:e`${e.identifier("covered")} = ${e.identifier("covered")}`,h=e`UPDATE ${e.identifier(c)} SET ${e.identifier("cursor")} = ${a}, ${e.identifier("done")} = ${u}, ${e.identifier("profile")} = ${s}, ${l} WHERE ${e.identifier("companion")} = ${t}`;if(d.length>0){await p(i,n,h);return}try{await p(i,n,e`INSERT INTO ${e.identifier(c)} (${e.identifier("companion")}, ${e.identifier("cursor")}, ${e.identifier("done")}, ${e.identifier("profile")}, ${e.identifier("covered")}) VALUES (${t}, ${a}, ${o?1:0}, ${s}, ${o?1:0})`)}catch(E){if(!n.isUniqueViolation(E))throw E;await p(i,n,h)}};export{k as D,c as S,Y as a,P as b,W as c,V as l,K as m,X as r,G as w};
1
+ import{sql as e}from"drizzle-orm";import{queryRun as p,queryAll as m}from"./decodeGlobalRow-CUEAvNjr.mjs";const y=["de","en","es","fr","it","nl","none","pt"],L=i=>y.includes(i),A="a an and are as at be but by for if in into is it no not of on or such that the their then there these they this to was will with",R="aber als am an auch auf aus bei bin bis bist da dass der den des dem die das denn dir du ein eine für hat ich im in ist mit nicht noch nur oder sich sie sind über und von vor war wie wir zu zum zur",F="a al como con de del el en es la las lo los mas no o para pero por que se su sus un una uno y ya",N="au aux avec ce ces dans de des du elle en et eux il je la le les leur lui ma mais me même mes moi mon ne nos notre nous on ou par pas pour qu que qui sa se ses son sur ta te tes toi ton tu un une vos votre vous y",_="a ai al alla anche che chi ci coi col come con da dal degli dei del della di do e ed gli ha hai hanno i il in la le lo ma mi ne nei nel non o per più quale quanto se si sono su sul tra un una uno vi",q="aan al als bij dan dat de der deze die dit door een en er het hij ij in is je kan me men met mij na naar niet nog nu of om ons ook op over te tot uit van voor was wat we wij zij zijn zo",x="a ao aos as até com como da das de do dos e em entre era essa esse esta este eu foi há isso já mais mas me mesmo meu na nas no nos num numa o os ou para pela pelo por qual que quem se sem seu só sua também te tem um uma você",C=new RegExp("(?<=\\p{Script=Cyrillic})[\\u0300-\\u0305\\u0307\\u0309-\\u036F]|(?<!\\p{Script=Cyrillic})[\\u0300-\\u036F]","gu"),O=/[\u0080-\u{10FFFF}]/u,b=/[\p{L}\p{N}]+/gu,H=/[\p{Script_Extensions=Han}\p{Script_Extensions=Hiragana}\p{Script_Extensions=Katakana}\p{Script_Extensions=Hangul}]+/gu,g=/[\p{Script_Extensions=Han}\p{Script_Extensions=Hiragana}\p{Script_Extensions=Katakana}\p{Script_Extensions=Hangul}]/u,D=i=>{if(!g.test(i))return[i];const n=[];let t=0;for(const r of i.matchAll(H)){const o=r.index,s=[...r[0]];if(o>t&&n.push(i.slice(t,o)),t=o+r[0].length,s.length===1){n.push(r[0]);continue}for(let a=0;a+1<s.length;a+=1)n.push(`${String(s[a])}${String(s[a+1])}`)}return t<i.length&&n.push(i.slice(t)),n},v=i=>O.test(i)?i.normalize("NFD").replaceAll(C,"").normalize("NFC").toLowerCase():i.toLowerCase(),f=i=>new Set(v(i).split(" ")),j={de:f(R),en:f(A),es:f(F),fr:f(N),it:f(_),nl:f(q),none:new Set,pt:f(x)},z=3,U=256,w=new Map,k=i=>{const n=i!==void 0&&L(i)?i:"none",t=w.get(n);if(t)return t;const r=j[n],o=a=>{const d=v(a),$=d.match(b)??[],u=(g.test(d)?$.flatMap(l=>D(l)):$).filter(l=>l.length<=U);return r.size===0?u:u.filter(l=>!r.has(l))},s={document:o,profile:`${n}-v${String(z)}`,query:a=>{const d=o(a);return d.filter(($,u)=>d.lastIndexOf($)===u)}};return w.set(n,s),s},W=i=>`${k(i.language).profile}:${i.field}`,S=i=>{const n=i.slice(i.indexOf(":")+1),t=n.lastIndexOf("/");return t===-1?n:n.slice(0,t)},V=(i,n)=>i.profile!==n?{cursor:void 0,finished:!1,wipe:i.cursor!==void 0||i.done}:{cursor:i.cursor,finished:i.done,wipe:!1},I=(i,n)=>typeof i=="string"&&S(i)===S(n),c="__lunora_search_state",K=async(i,n)=>{const{integer:t,key:r}=n.companionTypes,o=async(s,a)=>{try{await p(i,n,e`ALTER TABLE ${e.identifier(c)} ADD COLUMN ${e.identifier(s)} ${e.raw(a)}`)}catch{}};await p(i,n,e`CREATE TABLE IF NOT EXISTS ${e.identifier(c)} (${e.identifier("companion")} ${e.raw(r)} PRIMARY KEY, ${e.identifier("cursor")} ${e.raw(r)}, ${e.identifier("done")} ${e.raw(t)} NOT NULL DEFAULT 0, ${e.identifier("profile")} ${e.raw(r)}, ${e.identifier("covered")} ${e.raw(t)} NOT NULL DEFAULT 0)`),await o("profile",r),await o("covered",`${t} NOT NULL DEFAULT 0`),await p(i,n,e`UPDATE ${e.identifier(c)} SET ${e.identifier("covered")} = 1 WHERE ${e.identifier("done")} = 1 AND ${e.identifier("covered")} = 0`)},T=i=>i===1||i===!0||i==="1",P=async(i,n,t)=>{const r=await m(i,n,e`SELECT ${e.identifier("covered")} FROM ${e.identifier(c)} WHERE ${e.identifier("companion")} = ${t}`);return T(r[0]?.covered)},Y=async(i,n,t)=>{await p(i,n,e`DELETE FROM ${e.identifier(c)} WHERE ${e.identifier("companion")} = ${t}`)},X=async(i,n,t)=>{const o=(await m(i,n,e`SELECT ${e.identifier("cursor")}, ${e.identifier("done")}, ${e.identifier("profile")} FROM ${e.identifier(c)} WHERE ${e.identifier("companion")} = ${t}`))[0];if(!o)return{cursor:void 0,done:!1,profile:void 0};const{cursor:s,profile:a}=o;return{cursor:typeof s=="string"?s:void 0,done:T(o.done),profile:typeof a=="string"?a:void 0}},G=async(i,n,t,r,o,s)=>{const a=r??null,d=await m(i,n,e`SELECT ${e.identifier("profile")} FROM ${e.identifier(c)} WHERE ${e.identifier("companion")} = ${t}`),$=I(d[0]?.profile,s),u=o?1:0,l=o||!$?e`${e.identifier("covered")} = ${u}`:e`${e.identifier("covered")} = ${e.identifier("covered")}`,h=e`UPDATE ${e.identifier(c)} SET ${e.identifier("cursor")} = ${a}, ${e.identifier("done")} = ${u}, ${e.identifier("profile")} = ${s}, ${l} WHERE ${e.identifier("companion")} = ${t}`;if(d.length>0){await p(i,n,h);return}try{await p(i,n,e`INSERT INTO ${e.identifier(c)} (${e.identifier("companion")}, ${e.identifier("cursor")}, ${e.identifier("done")}, ${e.identifier("profile")}, ${e.identifier("covered")}) VALUES (${t}, ${a}, ${o?1:0}, ${s}, ${o?1:0})`)}catch(E){if(!n.isUniqueViolation(E))throw E;await p(i,n,h)}};export{k as D,c as S,Y as a,P as b,W as c,V as l,K as m,X as r,G as w};
@@ -0,0 +1 @@
1
+ import{LunoraError as h}from"@lunora/errors";import{D as I,c as vt,m as Dt,r as U,a as qt,l as lt,w as F,b as Ft}from"./ctx-db-search-state-DfjwJOxK.mjs";import{ftsUnmappedPage as Ut,groupUnmappedRows as bt,ftsWriteDocument as mt,ftsPurgeDocument as pt,ftsCompanionDdl as kt,unionAll as Ht,ftsRowidMapName as Pt}from"@lunora/shard-engine";import{sql as i}from"drizzle-orm";import{queryAll as p,nullSafeEqualsSql as Bt,decodeRow as V,runInOrder as O,indexState as L,createIndexIfNotExists as X,dropIndexIfExists as M,queryRun as T,decodeRows as ht,columnRefSql as rt,serializeColumnValue as Wt,readRowsPage as jt,forEachRowPaged as xt}from"./decodeGlobalRow-CUEAvNjr.mjs";import{OCC_VERSION_COLUMN as Kt}from"./OCC_VERSION_COLUMN-ByC64Jgx.mjs";const Gt=n=>{const t=atob(n),e=Uint8Array.from(t,r=>r.codePointAt(0)??0);return new TextDecoder().decode(e)},Yt=()=>new h("BAD_REQUEST","invalid cursor"),ot=16,st=8,y=1024,b=(n,t)=>t.query(n),Qt=(n,t)=>{if(!t)return{exact:!0,lower:n,upper:n};const e=[...n].at(-1)??"",r=(e.codePointAt(0)??0)+1;if(r>=55296&&r<=57343||r>1114111)return{exact:!0,lower:n,upper:n};const o=n.slice(0,n.length-e.length);return{exact:!1,lower:n,upper:o+String.fromCodePoint(r)}},Ln=(n,t,e)=>{const r={eq:(o,s)=>{if(!n.definition.filterFields?.includes(o))throw new h("INTERNAL",`field "${o}" is not a filter field of search index "${n.indexName}" on table "${t}"`);if(n.filters.length>=st)throw new h("BAD_REQUEST",`search index "${n.indexName}" on table "${t}": at most ${String(st)} .eq() filters are supported per search query`);return n.filters.push({field:o,value:s}),r},search:(o,s)=>{const a=n;if(o!==a.definition.field)throw new h("INTERNAL",`search index "${a.indexName}" on table "${t}" indexes "${a.definition.field}", not "${o}"`);const f=b(s,e).length;if(f>ot)throw new h("BAD_REQUEST",`search index "${a.indexName}" on table "${t}": at most ${String(ot)} search terms are supported (got ${String(f)})`);return a.field=o,a.query=s,a.hasQuery=!0,r}};return r},Mn=n=>{if(n.length>y)throw new h("BAD_REQUEST",`more than ${String(y)} documents match this search — narrow it with filters or read it a page at a time with .paginate()`)},vn=n=>Math.min(n.offset+n.numItems+1,y),Vt=n=>btoa(`search:${String(n)}`),Xt=n=>{let t;try{t=Gt(n)}catch{return}if(!t.startsWith("search:"))return;const e=Number(t.slice(7));return Number.isInteger(e)&&e>=0?e:void 0},Dn=n=>{if(typeof n.endCursor=="string")throw new h("BAD_REQUEST","bounded (endCursor) pagination is not supported on search queries — relevance order has no stable range boundary");if(!Number.isFinite(n.numItems))throw new h("BAD_REQUEST",`search pagination needs a finite numItems, got ${String(n.numItems)}`);const t=Math.max(0,Math.floor(n.numItems)),e=n.cursor?Xt(n.cursor):0;if(e===void 0)throw Yt();if(e+t>=y)throw new h("BAD_REQUEST",`search pagination reaches the ${String(y)}-document limit (offset ${String(e)} + ${String(t)} requested) — a page must end below the cap so the probe row that answers \`hasMore\` still fits: retry with numItems ${String(Math.max(1,y-e-1))} or fewer, or narrow the query or the filters instead`);return{numItems:t,offset:e}},qn=(n,t)=>{const e=t.offset+t.numItems,r=t.numItems>0&&n.length>e;return{continueCursor:r?Vt(e):null,isDone:!r,page:n.slice(t.offset,e)}},Fn=n=>{if(n===void 0)return y+1;if(!Number.isFinite(n))return y;const t=Math.max(0,Math.floor(n));if(t>y)throw new h("BAD_REQUEST",`search returns at most ${String(y)} documents (asked for ${String(t)}) — narrow the query or paginate instead`);return t},Jt=(n,t)=>{if(!t.includes("."))return n[t];let e=n;for(const r of t.split(".")){if(e===null||typeof e!="object"||Array.isArray(e))return;e=e[r]}return e},k=(n,t)=>`${n}__fts_${t}`,S="__id__",g="__token__",A="__n__",zt=1e3,gt=(n,t)=>t.document(n),x=(n,t)=>Jt(n,t),Zt=n=>typeof n=="string"?n:n==null?"":typeof n=="number"||typeof n=="bigint"||typeof n=="boolean"?String(n):JSON.stringify(n)??"",tn=(n,t,e)=>n!==void 0&&t!==void 0&&x(n,e.field)===x(t,e.field),nn=(n,t)=>gt(Zt(x(n,t.field)),I(t.language)).slice(0,zt),q=(n,t)=>nn(n,t).join(" "),Tt=(n,t)=>{const e=new Map;for(const r of gt(n,t))e.set(r,(e.get(r)??0)+1);return e},yt=[Kt,"_creationTime"],at=50,$=(n,t)=>i`${i.identifier(n)}.${i.identifier(t)}`,v=(n,t,e)=>i`DELETE FROM ${i.identifier(n)} WHERE ${$(n,S)} = ${t}${e===void 0?i``:i` AND ${e}`}`,Rt=(n,{row:t,table:e})=>{const r=yt.map(o=>i` AND ${Bt(n.name,$(e,o),t[o]??null)}`);return i`SELECT 1 FROM ${i.identifier(e)} WHERE ${$(e,"id")} = ${t.id}${i.join(r)}${n.name==="postgres"?i` FOR SHARE`:i``}`},D=(n,t)=>i`EXISTS (${Rt(n,t)})`,H=(n,t)=>i`NOT EXISTS (SELECT 1 FROM ${i.identifier(n)} WHERE ${$(n,"id")} = ${t})`,_t=(n,t,e,r)=>r===void 0?H(t,e):D(n,{row:r,table:t}),J=async(n,t,e,r)=>{const o=new Map;for(let s=0;s<r.length;s+=at){const a=r.slice(s,s+at),f=await p(n,t,i`SELECT * FROM ${i.identifier(e)} WHERE ${$(e,"id")} IN (${i.join(a.map(u=>i`${u}`),i`, `)})`);for(const u of f)o.set(String(u.id),u)}return o},K=(n,t,e)=>n.name==="mysql"?i`${i.identifier(t)}(${i.raw(String(e))})`:n.textPatternOperatorClass===void 0?i`${i.identifier(t)}`:i`${i.identifier(t)} ${i.raw(n.textPatternOperatorClass)}`,z=512,Z=(n,t)=>({columns:i`${K(n,g,256)}, ${K(n,S,z)}`,concurrently:!0,name:`${t}__unique`,table:t,unique:!0}),W=n=>i`${n} COLLATE utf8mb4_0900_bin`,j=(n,t)=>{const e=JSON.stringify(t.map(([r,o])=>({o,t:r})));switch(n.name){case"mysql":return i`JSON_TABLE(${e}, '$[*]' COLUMNS (${i.identifier("t")} VARCHAR(256) PATH '$.t', ${i.identifier("o")} INT PATH '$.o')) AS ${i.identifier("j")}`;case"postgres":return i`json_to_recordset(${e}::json) AS ${i.identifier("j")}(${i.identifier("t")} text, ${i.identifier("o")} integer)`;default:return i`(SELECT json_extract(${$("e","value")}, '$.t') AS ${i.identifier("t")}, json_extract(${$("e","value")}, '$.o') AS ${i.identifier("o")} FROM json_each(${e}) AS ${i.identifier("e")}) AS ${i.identifier("j")}`}},Nt=(n,t,e,r,o)=>{const s=i.join([g,S,A].map(f=>i.identifier(f)),i`, `),a=f=>i`INSERT INTO ${i.identifier(t)} (${s}) SELECT ${$("j","t")}, ${e}, ${$("j","o")} FROM ${j(n,r)} WHERE ${f}`;switch(n.name){case"mysql":{const f=D(n,o),u=$(t,g),c=i.identifier("held");return[i`${a(i`${f} AND NOT EXISTS (SELECT 1 FROM ${i.identifier(t)} AS ${c} WHERE ${$("held",S)} = ${e} AND ${$("held",g)} = ${W($("j","t"))})`)} ON DUPLICATE KEY UPDATE ${$(t,A)} = ${$(t,A)}`,i`UPDATE ${i.identifier(t)} JOIN ${j(n,r)} ON ${u} = ${W($("j","t"))} SET ${$(t,A)} = ${$("j","o")} WHERE ${$(t,S)} = ${e} AND ${f}`,i`DELETE FROM ${i.identifier(t)} WHERE ${$(t,S)} = ${e} AND ${f} AND ${u} NOT IN (SELECT ${W($("j","t"))} FROM ${j(n,r)})`]}case"postgres":{const f=i.identifier("__source__"),u=i.identifier("__purged__"),c=i`EXISTS (SELECT 1 FROM ${f})`;return[i`WITH ${f} AS (${Rt(n,o)}), ${u} AS (${v(t,e,c)} RETURNING 1) ${a(i`${c} AND (SELECT COUNT(*) FROM ${u}) >= 0`)} ON CONFLICT DO NOTHING`]}default:{const f=D(n,o);return[v(t,e,f),i`${a(f)} ON CONFLICT DO NOTHING`]}}},At={uniqueKeyMissing:!1,unmappedSkipped:0},ft=new Set,ut=(n,t)=>{ft.has(n)||(ft.add(n),console.warn(`[@lunora/sql-store] ${t}`))},ct=100,dt=3,G=async(n,t,e,r=0)=>{const{companion:o,definition:s,index:a,tableName:f}=e,u={done:!0,last:r,left:0};if((await p(n,t,t.tableExists(f))).length===0)return u;const d=await p(n,t,Ut(o,ct,r));if(d.length===0)return u;const w=Math.max(...d.map(_=>Number(_.rowid))),E=bt(d),l=await J(n,t,f,[...E.keys()]),m=[];for(const[_,nt]of E){const B=l.get(_),et=B===void 0?void 0:V(s,B),it=_t(t,f,_,B);m.push(...et?mt(o,_,q(et,a),{guard:it,unmappedRowids:nt}):pt(o,_,{guard:it,unmappedRowids:nt}))}await O(n,t,m);const R=await p(n,t,i`SELECT COUNT(*) AS ${i.identifier("n")} FROM ${i.identifier(o)} WHERE ${$(o,"rowid")} > ${Math.max(0,r)} AND ${$(o,"rowid")} <= ${w}`);return{done:d.length<ct,last:w,left:Number(R[0]?.n??0)}},en=async(n,t,e)=>{let r=0,o=0;for(;;){let s=await G(n,t,e,r);for(let a=1;s.left>0&&a<dt;a+=1)s=await G(n,t,e,r);if(s.left>0&&(o+=s.left,console.warn(`[@lunora/sql-store] search migration of "${e.companion}": ${String(s.left)} row(s) lost every one of ${String(dt)} attempts to a concurrent write and were left for a later pass.`)),s.done)return o;r=s.last}},rn=async(n,t,e,r)=>r==="step"?(await G(n,t,e),At):{uniqueKeyMissing:!1,unmappedSkipped:await en(n,t,e)},Y=n=>`${n}__btree`,on=async(n,t,e)=>{const r=Z(t,e),o=await L(n,t,e,r.name);if(o!=="valid"){if(o==="absent"&&(await p(n,t,i`SELECT 1 AS ${i.identifier("present")} FROM ${i.identifier(e)} LIMIT 1`)).length===0)try{await X(n,t,{...r,concurrently:!1}),await M(n,t,e,Y(e));return}catch(a){if(await L(n,t,e,r.name).catch(()=>{})==="valid")return;ut(`${e}:empty`,`search companion "${e}": adding its unique key failed (${String(a)}); run backfillSqlSearchIndexes.`);return}ut(`${e}:missing`,`search companion "${e}" has no usable unique key, so two backfills racing over one row can index it twice. Run backfillSqlSearchIndexes, which adds it.`)}},sn=async(n,t,e)=>{const r=$(e,S),o=$(e,g);return(await p(n,t,i`SELECT ${r} AS ${i.identifier("id")}, ${o} AS ${i.identifier("token")}, COUNT(*) AS ${i.identifier("n")} FROM ${i.identifier(e)} GROUP BY ${r}, ${o} HAVING COUNT(*) > 1`)).map(a=>({count:Number(a.n),id:String(a.id),token:String(a.token)}))},an=(n,t,{count:e,id:r,token:o})=>{const s=i`${$(t,S)} = ${r} AND ${$(t,g)} = ${o}`;switch(n.name){case"mysql":return i`DELETE FROM ${i.identifier(t)} WHERE ${s} LIMIT ${i.raw(String(e-1))}`;case"postgres":return i`DELETE FROM ${i.identifier(t)} WHERE ctid = ANY (ARRAY(SELECT ctid FROM ${i.identifier(t)} WHERE ${s} OFFSET 1))`;default:return i`DELETE FROM ${i.identifier(t)} WHERE rowid IN (SELECT rowid FROM ${i.identifier(t)} WHERE ${s} LIMIT -1 OFFSET 1)`}},fn=async(n,t,e,r)=>{const{companion:o,definition:s,index:a,tableName:f}=e;for(const d of r)await T(n,t,an(t,o,d));const u=[...new Set(r.map(d=>d.id))],c=await J(n,t,f,u);for(const d of u){const w=c.get(d),E=w===void 0?void 0:V(s,w);if(w!==void 0&&E){const l=[...Tt(q(E,a),I(a.language))];await O(n,t,Nt(t,o,d,l,{row:w,table:f}));continue}await T(n,t,v(o,d,_t(t,f,d,w)))}},un=async(n,t,e)=>t.name!=="mysql"?"":(await p(n,t,i`SELECT COLUMN_NAME AS ${i.identifier("name")}, COLLATION_NAME AS ${i.identifier("collation")} FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = ${e} AND COLUMN_NAME IN (${g}, ${S})`)).filter(o=>!String(o.collation).endsWith("_bin")).map(o=>` Its ${String(o.name)} column uses ${String(o.collation)}, which treats values differing only in case or accents as one; convert it with ALTER TABLE \`${e}\` MODIFY \`${String(o.name)}\` VARCHAR(768) COLLATE utf8mb4_0900_bin NOT NULL.`).join(""),cn=async(n,t,e)=>{if(t.name!=="mysql")return 0;const r=$(e,S),o=await p(n,t,i`SELECT COUNT(*) AS ${i.identifier("n")} FROM (SELECT 1 AS ${i.identifier("x")} FROM ${i.identifier(e)} GROUP BY LEFT(${r}, ${i.raw(String(z))}), ${$(e,g)} HAVING COUNT(DISTINCT ${r}) > 1) AS ${i.identifier("shared")}`);return Number(o[0]?.n??0)},dn=async(n,t,e)=>{const r=Z(t,e),o=await L(n,t,e,r.name);if(o==="valid")return"valid";o==="invalid"&&await M(n,t,e,r.name);try{return await X(n,t,r),"built"}catch(s){const a=await L(n,t,e,r.name);if(a==="valid")return"valid";if(a==="invalid"&&await M(n,t,e,r.name),!t.isUniqueViolation(s))throw s;return"refused"}},$t=3,$n=async(n,t,e)=>{const{companion:r}=e,o=await un(n,t,r);let s="";o!==""&&console.warn(`[@lunora/sql-store] search companion "${r}" folds case and accents in its unique key.${o}`);for(let a=0;a<$t;a+=1){const f=await dn(n,t,r);if(f==="valid")return await M(n,t,r,Y(r)),!0;if(f==="refused"){const u=await sn(n,t,r);if(u.length>0)await fn(n,t,e,u);else{const c=await cn(n,t,r);s=c>0?` ${String(c)} token(s) are held by document ids that agree on their first ${String(z)} characters, which the key cannot tell apart; shorten those ids.`:o}}}return await L(n,t,r,Z(t,r).name)==="valid"?(await M(n,t,r,Y(r)),!0):(console.warn(`[@lunora/sql-store] search companion "${r}" still has no unique key after ${String($t)} attempts: writes keep working, but two backfills racing over one row can index it twice.${s}`),!1)},wn=async(n,t,e,r)=>r==="step"?At:{uniqueKeyMissing:!await $n(n,t,e),unmappedSkipped:0},Sn=(n,t)=>t?i`${i.identifier(g)} LIKE ${`${n}%`}`:i`${i.identifier(g)} = ${n}`,Ot=(n,t)=>{const e=t.filters.map(r=>i`m.${rt(r.field)} = ${Wt(r.value)}`);return n.softDeleteMode&&e.push(i`m.${rt(n.softDeleteMode.field)} IS NULL`),t.scope!==void 0&&e.push(i`(${t.scope})`),e},It=async(n,t,e,r,o,s,a)=>{const f=Ot(e,o);let u=i`SELECT m.* FROM (${a}) s JOIN ${i.identifier(r)} m ON m.${i.identifier("id")} = s.${i.identifier(S)}`;return f.length>0&&(u=i`${u} WHERE ${i.join(f,i` AND `)}`),u=i`${u} ORDER BY s.${i.identifier("__score__")} DESC, m.${i.identifier("_creationTime")} DESC, m.${i.identifier("id")} ASC LIMIT ${i.raw(String(s))}`,ht(e,await p(n,t,u))},En=async(n,t,e,r,o,s)=>{const a=b(o.query,I(o.definition.language));if(a.length===0)return[];const f=k(r,o.indexName),u=a.length-1,c=a.map((m,R)=>Sn(m,R===u)),d=i.join(c,i` OR `),w=c.map(m=>i`SUM(CASE WHEN ${m} THEN ${i.identifier(A)} ELSE 0 END)`),E=i.join(w.map(m=>i`${m} > 0`),i` AND `),l=i`SELECT ${i.identifier(S)}, ${i.join(w,i` + `)} AS ${i.identifier("__score__")} FROM ${i.identifier(f)} WHERE ${d} GROUP BY ${i.identifier(S)} HAVING ${E}`;return It(n,t,e,r,o,s,l)},ln=async(n,t,e,r,o,s)=>{const a=b(o.query,I(o.definition.language));if(a.length===0)return[];const f=k(r,o.indexName),u=`${f}__vocab`,c=a.length-1,d=a.map((l,m)=>{const R=Qt(l,m===c),_=R.exact?i`${i.identifier("term")} = ${R.lower}`:i`${i.identifier("term")} >= ${R.lower} AND ${i.identifier("term")} < ${R.upper}`;return i`SELECT ${i.identifier("doc")}, ${i.raw(String(m))} AS ${i.identifier("__term__")}, COUNT(*) AS ${i.identifier("__n__")} FROM ${i.identifier(u)} WHERE ${_} GROUP BY ${i.identifier("doc")}`}),w=a.map((l,m)=>i`SUM(CASE WHEN u.${i.identifier("__term__")} = ${i.raw(String(m))} THEN u.${i.identifier("__n__")} ELSE 0 END)`),E=i`SELECT f.${i.identifier(S)} AS ${i.identifier(S)}, ${i.join(w,i` + `)} AS ${i.identifier("__score__")} FROM (${Ht(d)}) u JOIN ${i.identifier(f)} f ON f.rowid = u.${i.identifier("doc")} GROUP BY f.${i.identifier(S)} HAVING ${i.join(w.map(l=>i`${l} > 0`),i` AND `)}`;return It(n,t,e,r,o,s,E)},mn=async(n,t,e,r,o,s)=>{const a=t.nativeTextSearch,f=b(o.query,I(o.definition.language));if(!a||f.length===0)return[];const u=k(r,o.indexName),c=[a.matches(u,f),...Ot(e,o)],d=i`SELECT m.* FROM ${i.identifier(u)} JOIN ${i.identifier(r)} m ON m.${i.identifier("id")} = ${i.identifier(u)}.${i.identifier(S)} WHERE ${i.join(c,i` AND `)} ORDER BY ${a.rank(u,f)} DESC, m.${i.identifier("_creationTime")} DESC, m.${i.identifier("id")} ASC LIMIT ${i.raw(String(s))}`;return ht(e,await p(n,t,d))},pn={ensureCompanion:async(n,t,e)=>{const{integer:r,key:o}=t.companionTypes;await T(n,t,i`CREATE TABLE IF NOT EXISTS ${i.identifier(e)} (${i.identifier(g)} ${i.raw(o)} NOT NULL, ${i.identifier(S)} ${i.raw(o)} NOT NULL, ${i.identifier(A)} ${i.raw(r)} NOT NULL)`),await X(n,t,{columns:K(t,S,191),name:`${e}__by_id`,table:e,unique:!1}),await on(n,t,e)},indexDocument:async(n,t,e,r,o,s)=>{const a=[...Tt(q(r,o),I(o.language))];await O(n,t,Nt(t,e,String(s.row.id),a,s))},migrate:wn,name:"inverted",purgeDocument:async(n,t,e,r,o)=>{await T(n,t,v(e,r,H(o,r)))},runSearch:En},hn={ensureCompanion:async(n,t,e)=>{await O(n,t,kt(e))},indexDocument:async(n,t,e,r,o,s)=>{await O(n,t,mt(e,String(s.row.id),q(r,o),{guard:D(t,s)}))},migrate:rn,name:"fts5",purgeDocument:async(n,t,e,r,o)=>{await O(n,t,pt(e,r,{guard:H(o,r)}))},runSearch:ln},gn={ensureCompanion:async(n,t,e)=>{const r=t.nativeTextSearch;if(r){await T(n,t,r.createCompanion(e,t.companionTypes.key));for(const o of r.createIndexes(e))await T(n,t,o)}},indexDocument:async(n,t,e,r,o,s)=>{const a=t.nativeTextSearch;a&&await T(n,t,a.indexDocument(e,String(s.row.id),q(r,o),D(t,s)))},name:"native",purgeDocument:async(n,t,e,r,o)=>{await T(n,t,v(e,r,H(o,r)))},runSearch:mn},C=(n,t)=>n.strategy==="native"&&t.nativeTextSearch!==void 0?gn:t.supportsFts5?hn:pn,P=(n,t)=>`${vt(n)}/${C(n,t).name}`,tt=function*(n){for(const[t,e]of Object.entries(n.tables)){const r=e.searchIndexes;if(!(e.shardMode!==void 0&&e.shardMode.kind!=="global"||!r))for(const o of r)yield[t,e,o]}},N=(n,t)=>k(n,t.name),Tn=async(n,t,e,r)=>{const o=N(e,r);return lt(await U(n,t,o),P(r,t)).finished?!0:Ft(n,t,o)},Un=async(n,t,e,r,o,s)=>{if(!await Tn(n,t,r,o.definition))throw new h("SEARCH_INDEX_BUILDING",`search index "${o.indexName}" on table "${r}" is still backfilling and currently covers only part of the table — retry once it finishes${t.searchBackfillHint===void 0?"":`, or complete it now: ${t.searchBackfillHint}`}`);return C(o.definition,t).runSearch(n,t,e,r,o,s)},wt=n=>n.slice(n.lastIndexOf("/")+1),St=200,Et=5,yn=async(n,t,e,r)=>{const o=[...r.keys()],s=await p(n,t,i`SELECT * FROM ${i.identifier(e)} WHERE ${i.identifier("id")} >= ${o[0]} AND ${i.identifier("id")} <= ${o.at(-1)} ORDER BY ${i.identifier("id")} ASC`),a=new Map;for(const f of s){const u=f.id,c=r.get(u)?.row;c&&yt.some(d=>String(c[d])!==String(f[d]))&&a.set(u,{row:f})}return a},Q=async(n,t,e,r,o,s,a)=>{const f=C(s,t);let u=a;for(let c=0;u.size>0&&c<Et;c+=1){for(const{document:d,row:w}of u.values()){const E=d??V(e,w);E&&await f.indexDocument(n,t,o,E,s,{row:w,table:r})}u=await yn(n,t,r,u)}u.size>0&&console.warn(`[@lunora/sql-store] search index "${o}": ${String(u.size)} row(s) were rewritten on every one of ${String(Et)} re-check rounds and were left to their writers — each stays stale only if none of those writes changed its indexed text.`)},Ct=async(n,t,e,r,o)=>{const s=N(r,o),a=P(o,t),f=lt(await U(n,t,s),a);if(f.finished)return!0;if(f.wipe&&await F(n,t,s,void 0,!1,a),(await p(n,t,t.tableExists(r))).length===0)return await F(n,t,s,void 0,!0,a),!0;const c=await jt(n,t,r,f.cursor,St),d=c.findLast(l=>typeof l.id=="string")?.id,w=new Map;for(const l of c)typeof l.id=="string"&&w.set(l.id,{row:l});await Q(n,t,e,r,s,o,w);const E=c.length<St;return await F(n,t,s,d??f.cursor,E,a),E},Lt=async(n,t,e)=>{await Dt(n,e);for(const[r,,o]of tt(t)){const s=N(r,o),a=P(o,e),f=await U(n,e,s);f.profile!==void 0&&wt(f.profile)!==wt(a)&&(await T(n,e,i`DROP TABLE IF EXISTS ${i.identifier(s)}`),await T(n,e,i`DROP TABLE IF EXISTS ${i.identifier(Pt(s))}`),await qt(n,e,s)),await C(o,e).ensureCompanion(n,e,s)}},Rn=async(n,t,e,r,o)=>{const s=N(r,o);if((await U(n,t,s)).profile!==void 0)return;let f=0;(await p(n,t,t.tableExists(r))).length>0&&await xt(n,t,e,r,()=>{f+=1},{limit:1}),await F(n,t,s,void 0,f===0,P(o,t))},Mt=async(n,t,e,r,o,s)=>await C(o,t).migrate?.(n,t,{companion:N(r,o),definition:e,index:o,tableName:r},s)??{uniqueKeyMissing:!1,unmappedSkipped:0},bn=async(n,t,e)=>{await Lt(n,t,e);for(const[r,o,s]of tt(t)){if(await Mt(n,e,o,r,s,"step"),s.staged){await Rn(n,e,o,r,s);continue}await Ct(n,e,o,r,s)}},kn=async(n,t,e)=>{const r={uniqueKeyMissing:[],unmappedSkipped:0};await Lt(n,t,e);for(const[o,s,a]of tt(t)){const f=await Mt(n,e,s,o,a,"drain");r.unmappedSkipped+=f.unmappedSkipped,f.uniqueKeyMissing&&r.uniqueKeyMissing.push(N(o,a));let u=!1;for(;!u;)u=await Ct(n,e,s,o,a)}return r},Hn=n=>{const{dialect:t,exec:e,schema:r}=n;return async(o,s,a)=>{const f=r.tables[o],u=f?.searchIndexes;if(!(!f||!u||u.length===0))for(const c of u){if(tn(a?.previous,a?.document,c))continue;const d=N(o,c);if(a){await Q(e,t,f,o,d,c,new Map([[s,{document:a.document,row:{...a.written,id:s}}]]));continue}await C(c,t).purgeDocument(e,t,d,s,o);const E=(await J(e,t,o,[s])).get(s);E!==void 0&&await Q(e,t,f,o,d,c,new Map([[s,{row:E}]]))}}};export{Ln as A,Dn as C,qn as R,Fn as T,Un as a,vn as b,Hn as c,kn as d,bn as r,ot as u,Mn as x};
@@ -1 +1 @@
1
- import{bigintSqlKey as a}from"@lunora/shard-engine";import{B as t,b as n,U as o,g as r,a as E,i as d,r as c,c as N,s as U,t as _}from"./value-codec-CBr0_L1N.mjs";export{t as BIGINT_KEY_LENGTH,n as UNTYPED_NUMBER_PREFIX,o as UNTYPED_WIRE_PREFIX,a as bigintSqlKey,r as decodeBigint,E as effectiveColumnKind,d as isUntypedColumnKind,c as rewriteLegacyUntypedNumber,N as sqliteDecode,U as sqliteEncode,_ as tryJsonParse};
1
+ import{bigintSqlKey as a}from"@lunora/shard-engine";import{B as t,c as n,U as o,g as r,e as E,i as d,r as c,a as N,s as U,t as _}from"./value-codec-qpu2yJ-6.mjs";export{t as BIGINT_KEY_LENGTH,n as UNTYPED_NUMBER_PREFIX,o as UNTYPED_WIRE_PREFIX,a as bigintSqlKey,r as decodeBigint,E as effectiveColumnKind,d as isUntypedColumnKind,c as rewriteLegacyUntypedNumber,N as sqliteDecode,U as sqliteEncode,_ as tryJsonParse};
@@ -0,0 +1 @@
1
+ import{renderSql as m}from"@lunora/shard-engine";import{sql as r}from"drizzle-orm";import{s as I,a as y,e as N}from"./value-codec-qpu2yJ-6.mjs";import{OCC_VERSION_COLUMN as G}from"./OCC_VERSION_COLUMN-ByC64Jgx.mjs";const C=n=>n==="_id"||n==="id"?"id":n,L=n=>r`${r.identifier(C(n))}`,v=(n,t)=>r`${r.identifier(n)}.${r.identifier(C(t))}`,F=n=>{const t=[];for(const[e,o]of Object.entries(n.shape)){const s=o._meta?.column;s&&t.push([e,s])}return t},d=(n,t,e)=>{const{params:o,sql:s}=m(t.name,e);return n.all(s,o)},u=(n,t,e)=>{const{params:o,sql:s}=m(t.name,e);return n.run(s,o)},K=(n,t,e)=>n==="postgres"?r`${t} IS NOT DISTINCT FROM ${e}`:n==="mysql"?r`${t} <=> ${e}`:r`${t} IS ${e}`,k={sql:n=>r`COALESCE(${n}, 0) + 1`,value:n=>(n==null?0:Number(n))+1},x=async(n,t,e)=>{if(e.length!==0){if(!n.batch){for(const o of e)await u(n,t,o);return}await n.batch(e.map(o=>m(t.name,o)))}},B=async(n,t,e)=>{if(n.batch&&t.name==="sqlite"){await n.batch(e.map(o=>m(t.name,o)));return}for(const o of e)await u(n,t,o)},P=async(n,t,e)=>{const o=e.unique?r`UNIQUE `:r``,s=e.concurrently===!0&&t.name==="postgres"?r`CONCURRENTLY `:r``;if(t.name==="mysql"){try{await u(n,t,r`CREATE ${o}INDEX ${r.identifier(e.name)} ON ${r.identifier(e.table)} (${e.columns})`)}catch(i){const a=i;if(a.errno!==1061&&a.code!=="ER_DUP_KEYNAME"&&a.code!==1061)throw i}return}await u(n,t,r`CREATE ${o}INDEX ${s}IF NOT EXISTS ${r.identifier(e.name)} ON ${r.identifier(e.table)} (${e.columns})`)},W=async(n,t,e,o)=>{if(t.name!=="mysql"){await u(n,t,r`DROP INDEX IF EXISTS ${r.identifier(o)}`);return}try{await u(n,t,r`DROP INDEX ${r.identifier(o)} ON ${r.identifier(e)}`)}catch(s){const i=s;if(i.errno!==1091&&i.code!=="ER_CANT_DROP_FIELD_OR_KEY"&&i.code!==1091)throw s}},X=async(n,t,e,o)=>{switch(t.name){case"mysql":return(await d(n,t,r`SELECT 1 AS ${r.identifier("present")} FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = ${e} AND index_name = ${o} LIMIT 1`)).length>0?"valid":"absent";case"postgres":{const s=await d(n,t,r`SELECT indisvalid FROM pg_index WHERE indexrelid = to_regclass(quote_ident(${o}))`);return s.length===0?"absent":s[0]?.indisvalid===!0?"valid":"invalid"}default:return(await d(n,t,r`SELECT 1 AS ${r.identifier("present")} FROM sqlite_master WHERE type = 'index' AND name = ${o}`)).length>0?"valid":"absent"}},H=I,f=n=>{if(n===void 0)return!1;if(n._meta?.column?.notNull===!1)return!0;switch(n.kind){case"any":case"null":return!0;case"literal":return n._meta?.value===null;case"optional":return f(n._meta?.inner);case"union":return n._meta?.members?.some(t=>f(t))===!0;default:return!1}},q=n=>n.kind==="optional"&&!f(n),R=new WeakMap,S=n=>{let t=R.get(n);return t===void 0&&(t=Object.entries(n.shape).map(([e,o])=>[e,N(o),q(o)]),R.set(n,t)),t},h=new WeakMap,g=(n,t)=>{let e=h.get(n);return e===void 0&&(e=new Map(S(n).map(([o,s])=>[o,s])),h.set(n,e)),e.get(t)},U=(n,t,e)=>I(e,g(n,t)),T=(n,t)=>{const e={};for(const[o,s,i]of S(n)){const a=t[o];a===void 0||i&&a===null||(e[o]=y(a,s))}return e._id=t.id,e._creationTime=t._creationTime,e},p=(n,t)=>t?T(n,t):null,Y=(n,t)=>{const e=[];for(const o of t){const s=p(n,o);s&&e.push(s)}return e},_=500,b=(n,t,e,o,s)=>{const i=r`${r.identifier(e)}.${r.identifier("id")}`,a=o===void 0?r``:r` WHERE ${i} > ${o}`;return d(n,t,r`SELECT * FROM ${r.identifier(e)}${a} ORDER BY ${i} ASC LIMIT ${r.raw(String(s))}`)},z=async(n,t,e,o,s,i={})=>{let a=i.after,c=i.limit,E=!0;for(;E&&(c===void 0||c>0);){const $=c===void 0?_:Math.min(_,c),l=await b(n,t,o,a,$);for(const O of l){const w=p(e,O);w&&await s(w)}a=l.at(-1)?.id,E=l.length===$,c=c===void 0?void 0:c-l.length}};export{_ as BACKFILL_BATCH_SIZE,G as OCC_VERSION_COLUMN,L as columnRefSql,P as createIndexIfNotExists,T as decodeGlobalRow,p as decodeRow,Y as decodeRows,W as dropIndexIfExists,z as forEachRowPaged,X as indexState,k as nextRowVersion,K as nullSafeEqualsSql,v as qualifiedColumnRefSql,d as queryAll,x as queryBatch,u as queryRun,b as readRowsPage,B as runInOrder,H as serializeColumnValue,U as serializeDocumentColumn,F as tableColumns};
@@ -0,0 +1 @@
1
+ import{LunoraError as O}from"@lunora/errors";import{aggregateTableName as B,rankTableName as P,sortColumnName as I,renderSql as j,bigintSqlKey as F}from"@lunora/shard-engine";import{sql as n}from"drizzle-orm";import{b as K,e as p,i as Y,B as L,r as G,U as H,c as V}from"./value-codec-qpu2yJ-6.mjs";import{queryRun as d,queryAll as h,createIndexIfNotExists as E,tableColumns as W,qualifiedColumnRefSql as X,columnRefSql as m,queryBatch as Q}from"./decodeGlobalRow-CUEAvNjr.mjs";import{OCC_VERSION_COLUMN as w}from"./OCC_VERSION_COLUMN-ByC64Jgx.mjs";const z=new Set(["any","null"]),C=(t,e)=>{const i=e.kindOf(t);return i!==void 0&&z.has(i)||e.nullableOf(t)?!0:i==="union"&&(e.membersOf(t)??[]).some(o=>C(o,e))},y=t=>{const e=t._meta?.inner;return t.kind==="optional"&&e?y(e):t},q=t=>C(t,{kindOf:K,membersOf:e=>y(e)._meta?.members,nullableOf:e=>y(e)._meta?.column?.notNull===!1}),v=(t,e,i=!1)=>e.columnType(p(t),{unique:i||t._meta?.column?.unique===!0}),x=t=>{const e=new Set;for(const i of t.indexes)i.unique===!0&&i.fields.length===1&&e.add(i.fields[0]);return e},J=(t,e,i)=>{if(i.indexKeyPrefix!==void 0)for(const o of e.indexes){if(o.unique!==!0||o.fields.length<=1)continue;const r=o.fields.filter(s=>{const u=e.shape[s];return u!==void 0&&i.indexKeyPrefix?.(p(u))!==void 0});if(r.length>0)throw new Error(`${t}.${o.name}: a composite unique index cannot cover ${r.join(", ")} in full on this engine (its key limit forces a prefix, which would constrain the prefix rather than the value). Split it into single-column unique indexes, or store those fields as a bounded type.`)}},Z=t=>t._meta?.column?.notNull===!0&&t.kind!=="optional"&&!q(t),nn=(t,e,i)=>{const o=[],r=x(e);for(const[l,c]of Object.entries(e.shape)){if(!c._meta?.column)continue;const f=Z(c)?" NOT NULL":"";o.push(n`${n.identifier(l)} ${n.raw(`${v(c,i,r.has(l))}${f}`)}`)}const s=[...i.frameworkColumns().map(l=>n`${n.identifier(l.name)} ${n.raw(l.type)}`),n`${n.identifier(w)} ${n.raw(i.companionTypes.integer)}`],u=s.length+o.length,a=i.maxTableColumns;if(a!==void 0&&u>a){const l=u-1===a,c="Move one or more fields into a single object field, or split the table — either way the existing rows need a data migration (`defineMigration` + `lunora migrate up`); there is no in-place path.";throw new O("VALIDATION_ERROR",`@lunora/sql-store: global table "${t}" needs ${String(u)} columns, over this engine's ${String(a)}-column limit. ${l?`One of them is "${w}", the row version every guarded write reads, which caps declared fields at ${String(a-s.length)}. A table provisioned at ${String(a)} columns before that column existed cannot be widened to hold it. `:""}${c}`)}return n.join([...s,...o],n`, `)},en=t=>{if(t.indexKeyPrefix?.("string")===void 0)return n`${m("_creationTime")}, ${m("id")}`},D=async(t,e,i,o)=>{const r=n.join(i.refs.map(u=>n`${u} IS NOT NULL`),n` AND `);if((await h(t,e,n`SELECT ${i.columns} FROM ${n.identifier(i.table)} WHERE ${r} GROUP BY ${i.columns} HAVING COUNT(*) > 1 LIMIT 1`)).length>0)throw new O("INTERNAL",`unique index "${i.name}" on "${i.table}" ${o}: existing rows are duplicates under it. De-duplicate the table with a data migration first.`)},N=async(t,e,i)=>{if(!i.unique){await E(t,e,i);return}try{await E(t,e,i)}catch(o){throw e.isUniqueViolation(o)&&await D(t,e,i,"cannot be created"),o}},tn=async(t,e,i)=>{if(e.name!=="sqlite")return;const r=(await h(t,e,n`SELECT sql FROM sqlite_master WHERE type = 'index' AND name = ${i.name} AND tbl_name = ${i.table}`))[0]?.sql;if(typeof r!="string")return;const s=l=>{const c=l.indexOf("(");return c===-1?void 0:l.slice(c,l.lastIndexOf(")")+1)},u=i.unique?n`UNIQUE `:n``,a=s(j(e.name,n`CREATE ${u}INDEX ${n.identifier(i.name)} ON ${n.identifier(i.table)} (${i.columns})`).sql);a===void 0||a===s(r)||(i.unique&&await D(t,e,i,"cannot be re-created with its new column list; the previous index is left in place"),await d(t,e,n`DROP INDEX IF EXISTS ${n.identifier(i.name)}`))},on=async(t,e,i,o)=>{J(e,i,o);const r=x(i),s=a=>{const l=m(a),c=i.shape[a],f=c&&o.indexKeyPrefix&&c._meta?.column?.unique!==!0&&!r.has(a)?o.indexKeyPrefix(p(c)):void 0;return f===void 0?l:n`${l}(${n.raw(String(f))})`},u=en(o);for(const a of i.indexes){const l=a.fields.map(g=>s(g)),c=n.join(l,n`, `),f=a.unique??!1,$={columns:f||u===void 0?c:n`${c}, ${u}`,name:`${e}_${a.name}`,refs:l,table:e,unique:f};await tn(t,o,$),await N(t,o,$)}u!==void 0&&await E(t,o,{columns:u,name:`${e}__by_creation`,table:e,unique:!1});for(const[a,l]of W(i)){if(!l.unique)continue;const c=s(a);await N(t,o,{columns:c,name:`${e}_unique_${a}`,refs:[c],table:e,unique:!0})}},T="__lunora_migration_state",rn=async(t,e)=>{await d(t,e,n`CREATE TABLE IF NOT EXISTS ${n.identifier(T)} (${n.identifier("name")} ${n.raw(e.companionTypes.key)} PRIMARY KEY)`)},sn=async(t,e,i)=>(await h(t,e,n`SELECT ${n.identifier("name")} FROM ${n.identifier(T)} WHERE ${n.identifier("name")} = ${i}`)).length>0,an=async(t,e,i,o,r,s)=>{r>0&&s!==void 0&&console.warn(`[@lunora/sql-store] storage migration "${i}" on "${o}": ${String(r)} row(s) ${s} and were left as stored — range filters and ORDER BY over them stay wrong until they are corrected by hand.`);try{await d(t,e,n`INSERT INTO ${n.identifier(T)} (${n.identifier("name")}) VALUES (${i})`)}catch(u){if(!e.isUniqueViolation(u))throw u}},ln=100,un=(t,e)=>{const i=[],o=[];for(const r of e.fields){const s=t[r],u=e.convert(s);u!==void 0&&(i.push(n`${m(r)} = ${u}`),o.push(n`${m(r)} = ${s}`))}return{assignments:i,guards:o}},U=async(t,e,i,o)=>{if(o.fields.length===0||(await rn(t,i),await sn(t,i,o.marker)))return;const r=n.join(o.fields.map(l=>o.probe(l)),n` OR `),s=n.join([m("id"),...o.fields.map(l=>m(l))],n`, `);let u="",a=0;for(;;){const l=await h(t,i,n`SELECT ${s} FROM ${n.identifier(e)} WHERE (${r}) AND ${m("id")} > ${u} ORDER BY ${m("id")} LIMIT ${n.raw(String(ln))}`);if(l.length===0){await an(t,i,o.marker,e,a,o.describeUnconvertible);return}const c=[];for(const $ of l){const{id:g}=$,{assignments:A,guards:k}=typeof g=="string"?un($,o):{assignments:[],guards:[]};if(A.length===0){a+=1;continue}c.push(n`UPDATE ${n.identifier(e)} SET ${n.join(A,n`, `)} WHERE ${m("id")} = ${g} AND ${n.join(k,n` AND `)}`)}await Q(t,i,c);const f=l.at(-1)?.id;if(typeof f!="string")return;u=f}},b=t=>`'${t}%'`,cn=t=>Object.entries(t.shape).filter(([,e])=>e._meta?.column!==void 0&&p(e)==="bigint").map(([e])=>e),fn=t=>Object.entries(t.shape).filter(([,e])=>e._meta?.column!==void 0&&Y(p(e))).map(([e])=>e),mn=async(t,e,i,o)=>U(t,e,o,{convert:r=>{if(!(typeof r!="string"||r.length===L))try{return F(BigInt(r))}catch{return}},describeUnconvertible:"hold a value the order-preserving key cannot represent (non-numeric text, or a magnitude past 39 digits)",fields:cn(i),marker:`bigint-key:${e}`,probe:r=>n`(${m(r)} IS NOT NULL AND LENGTH(${m(r)}) <> ${L})`}),dn=async(t,e,i,o)=>U(t,e,o,{convert:G,fields:fn(i),marker:`untyped-number-key:${e}`,probe:r=>n`(${m(r)} LIKE ${n.raw(b(H))} AND ${m(r)} NOT LIKE ${n.raw(b(V))})`}),$n=async(t,e,i,o)=>{const r=[[w,o.companionTypes.integer],...Object.entries(i.shape).filter(([,a])=>a._meta?.column!==void 0).map(([a,l])=>[a,v(l,o)])],s="t",u=async a=>{try{return await h(t,o,n`SELECT ${n.join(a.map(l=>X(s,l)),n`, `)} FROM ${n.identifier(e)} AS ${n.identifier(s)} LIMIT 0`),!0}catch{return!1}};if(!await u(r.map(([a])=>a)))for(const[a,l]of r)await u([a])||await d(t,o,n`ALTER TABLE ${n.identifier(e)} ADD COLUMN ${n.identifier(a)} ${n.raw(l)}`)},S=new Set,R=(t,e)=>{S.has(t)||(S.add(t),console.warn(`[@lunora/sql-store] ${e}`))},gn=(t,e,i)=>{const o=typeof i.charset=="string"?` CHARACTER SET ${i.charset} COLLATE ${String(i.collation)}`:"";return`ALTER TABLE \`${t}\` MODIFY COLUMN \`${e}\` ${String(i.type)}${o} NULL`},pn=async(t,e,i,o)=>{if(o.name==="sqlite")return;const r=Object.keys(i.shape).filter(s=>{const u=i.shape[s];return u._meta?.column?.notNull===!0&&u.kind!=="optional"&&q(u)});if(r.length!==0)try{const s=o.name==="postgres"?n`column_name AS ${n.identifier("name")}`:n`column_name AS ${n.identifier("name")}, column_type AS ${n.identifier("type")}, character_set_name AS ${n.identifier("charset")}, collation_name AS ${n.identifier("collation")}`,u=o.name==="postgres"?n`table_schema = ANY (current_schemas(false))`:n`table_schema = DATABASE()`,a=await h(t,o,n`SELECT ${s} FROM information_schema.columns WHERE ${u} AND table_name = ${e} AND is_nullable = 'NO' AND column_name IN (${n.join(r.map(l=>n`${l}`),n`, `)})`);for(const l of a){const c=String(l.name);if(o.name==="postgres"){await d(t,o,n`ALTER TABLE ${n.identifier(e)} ALTER COLUMN ${n.identifier(c)} DROP NOT NULL`);continue}R(`${e}.${c}`,`"${e}"."${c}" accepts null in the schema but its column is NOT NULL, so writing null there fails. Relaxing it on MySQL can rebuild the table, so it is not done at startup; run this once, in a maintenance window: ${gn(e,c,l)};`)}}catch(s){R(`${e}:relax-failed`,`could not relax NOT NULL on the null-accepting columns of "${e}" (${r.join(", ")}): ${s instanceof Error?s.message:String(s)}. Writing null to them fails until the constraint is dropped by hand.`)}},Nn=async(t,e,i)=>{for(const[o,r]of Object.entries(e.tables)){if(r.shardMode?.kind!=="global")continue;const s=nn(o,r,i);await d(t,i,n`CREATE TABLE IF NOT EXISTS ${n.identifier(o)} (${s})`),await $n(t,o,r,i),await pn(t,o,r,i),await on(t,o,r,i),await mn(t,o,r,i),await dn(t,o,r,i)}},M=t=>t.shardMode===void 0||t.shardMode.kind==="global",bn=async(t,e,i)=>{const{integer:o,key:r,real:s}=i.companionTypes;for(const[u,a]of Object.entries(e.tables)){const l=a.aggregateIndexes;if(!(!l||l.length===0||!M(a)))for(const c of l){const f=B(u,c.name);await d(t,i,n`CREATE TABLE IF NOT EXISTS ${n.identifier(f)} (${n.identifier("__key__")} ${n.raw(r)} PRIMARY KEY, ${n.identifier("__value__")} ${n.raw(s)}, ${n.identifier("__count__")} ${n.raw(o)} NOT NULL DEFAULT 0)`),i.name==="sqlite"&&((await h(t,i,n`PRAGMA table_info(${n.identifier(f)})`)).some(g=>g.name==="__count__")||await d(t,i,n`ALTER TABLE ${n.identifier(f)} ADD COLUMN ${n.identifier("__count__")} ${n.raw(o)} NOT NULL DEFAULT 0`))}}},_=(t,e,i,o)=>{const r=t.name==="mysql"&&o?n`${n.identifier(e)}(191)`:n`${n.identifier(e)}`;return n`${r} ${n.raw(i)}`},hn=(t,e,i)=>{const o=[_(t,"__partition__","ASC",!0)];for(const[r,s]of e.sortBy.entries()){const u=i.shape[s.field],a=u!==void 0&&t.indexKeyPrefix?.(p(u))!==void 0;o.push(_(t,I(r),s.direction==="desc"?"DESC":"ASC",a))}return o.push(_(t,"__id__","ASC",!0)),o},En=(t,e,i)=>e.sortBy.map((o,r)=>{const s=i.shape[o.field],u=t.columnType(s?p(s):void 0);return n`${n.identifier(I(r))} ${n.raw(u)}`}),Sn=async(t,e,i)=>{const{key:o}=i.companionTypes;for(const[r,s]of Object.entries(e.tables)){const u=s.rankIndexes;if(!(!u||u.length===0||!M(s)))for(const a of u){const l=P(r,a.name),c=En(i,a,s),f=c.length>0?n`, ${n.join(c,n`, `)}`:n``;await d(t,i,n`CREATE TABLE IF NOT EXISTS ${n.identifier(l)} (${n.identifier("__id__")} ${n.raw(o)} PRIMARY KEY, ${n.identifier("__partition__")} ${n.raw(o)} NOT NULL${f})`);const $=hn(i,a,s),g=`${r}__rank_${a.name}__btree`;await E(t,i,{columns:n.join($,n`, `),name:g,table:l,unique:!1})}}};export{bn as runSqlAggregateMigrations,Nn as runSqlGlobalTableMigrations,Sn as runSqlRankMigrations};
@@ -1 +1 @@
1
- import{LunoraError as k}from"@lunora/errors";import{decodeBigintSqlKey as N,bigintSqlKey as P,BIGINT_KEY_DIGITS as W,float64SqlKey as _,decodeFloat64SqlKey as D}from"@lunora/shard-engine";const K=r=>{const{value:t}=r._meta??{};if(t===null)return"null";switch(typeof t){case"bigint":case"boolean":case"number":case"string":return typeof t;default:return r.kind}},j=r=>{if(r.kind==="literal")return K(r);if(r.kind!=="optional")return r.kind;const t=r._meta?.inner;return t?j(t):r.kind},l=r=>{let t="";for(let o=0;o<r.length;o+=32768)t+=String.fromCharCode(...r.subarray(o,o+32768));return btoa(t)},L=r=>{const t=atob(r),i=new Uint8Array(t.length);for(let o=0;o<t.length;o+=1)i[o]=t.codePointAt(o)??0;return i},s="$lunora.wire$",m=64,I=1024,p="__proto__",T={BigInt64Array,BigUint64Array,Float32Array,Float64Array,Int8Array,Int16Array,Int32Array,Uint8Array,Uint8ClampedArray,Uint16Array,Uint32Array},U={Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError},R=r=>{if(r===null||typeof r!="object")return!1;const t=Object.getPrototypeOf(r);return t===null||t===Object.prototype},A=(r,t=0)=>{if(t>m)return!0;if(r===null)return!1;const i=typeof r;if(i==="string"||i==="boolean")return!1;if(i==="number")return!Number.isFinite(r);if(i!=="object")return!0;if(Array.isArray(r))return r.length>0&&r[0]===s?!0:r.some(o=>o===void 0||A(o,t+1));if(!R(r)||Object.hasOwn(r,p))return!0;for(const o of Object.keys(r)){const f=r[o];if(f!==void 0&&A(f,t+1))return!0}return!1},b=(r,t=0)=>{if(t>m)throw new RangeError(`wire-codec: value nesting exceeds the ${m}-level limit`);if(r===void 0)return[s,"undefined"];if(r===null)return null;const i=typeof r;if(i==="bigint")return[s,"bigint",r.toString()];if(i==="number"){const e=r;return Number.isNaN(e)?[s,"nan"]:e===1/0?[s,"inf"]:e===-1/0?[s,"-inf"]:e}if(i!=="object")return r;if(r instanceof Date)return[s,"date",b(r.getTime(),t+1)];if(r instanceof Error){const e=r,n={};for(const c of Object.keys(e)){if(e[c]===void 0)continue;const a=b(e[c],t+1);c===p?Object.defineProperty(n,c,{configurable:!0,enumerable:!0,value:a,writable:!0}):n[c]=a}const y=[s,"error",String(e.name),String(e.message),n];return e.cause!==void 0&&y.push(b(e.cause,t+1)),y}if(r instanceof URL)return[s,"url",r.href];if(r instanceof Map)return[s,"map",[...r.entries()].map(([e,n])=>[b(e,t+1),b(n,t+1)])];if(r instanceof Set)return[s,"set",[...r].map(e=>b(e,t+1))];if(r instanceof ArrayBuffer)return[s,"bytes",l(new Uint8Array(r)),"ArrayBuffer"];if(ArrayBuffer.isView(r)){const e=r,n=e.constructor.name,y=new Uint8Array(e.buffer,e.byteOffset,e.byteLength);return n==="Uint8Array"?[s,"bytes",l(y)]:[s,"bytes",l(y),n]}if(Array.isArray(r)){const e=r.map(n=>b(n,t+1));return e.length>0&&e[0]===s?[s,"arr",e]:e}if(!R(r)){const e=r.constructor?.name??"value";throw new TypeError(`wire-codec: cannot encode a ${e} over the Lunora wire — only plain objects, arrays, and the supported built-ins (Date, Error, URL, Map, Set, ArrayBuffer/typed arrays, bigint) round-trip`)}const o=r,f={};for(const e of Object.keys(o)){const n=o[e];if(n===void 0)continue;const y=b(n,t+1);e===p?Object.defineProperty(f,e,{configurable:!0,enumerable:!0,value:y,writable:!0}):f[e]=y}return f},u=(r,t=0)=>{if(t>m)throw new RangeError(`wire-codec: value nesting exceeds the ${m}-level limit`);if(r===null||typeof r!="object")return r;if(Array.isArray(r)){if(r[0]===s)switch(r[1]){case"-inf":return-1/0;case"arr":return r[2].map(e=>u(e,t+1));case"bigint":{const e=r[2];if(typeof e!="string"||e.length>I||!/^-?\d+$/.test(e))throw new RangeError(`wire-codec: invalid or over-long bigint (max ${I} digits)`);return BigInt(e)}case"date":{const e=u(r[2],t+1);if(typeof e!="number")throw new TypeError("wire-codec: malformed date — epoch must be a number");return new Date(e)}case"map":{const e=r[2];return new Map(e.map(n=>{if(!Array.isArray(n)||n.length!==2)throw new TypeError("wire-codec: malformed map entry — expected a [key, value] pair");return[u(n[0],t+1),u(n[1],t+1)]}))}case"set":return new Set(r[2].map(e=>u(e,t+1)));case"url":{const e=r[2];if(typeof e!="string")throw new TypeError("wire-codec: malformed url — href must be a string");return new URL(e)}case"error":{const e=r[2],n=r[3];if(typeof e!="string"||typeof n!="string")throw new TypeError("wire-codec: malformed error — name and message must be strings");const y=(Object.hasOwn(U,e)?U[e]:void 0)??Error,c=new y(n);c.name!==e&&Object.defineProperty(c,"name",{configurable:!0,value:e,writable:!0});const a=u(r[4],t+1);if(a===null||typeof a!="object"||Array.isArray(a))throw new TypeError("wire-codec: malformed error — props must be an object");for(const g of Object.keys(a))g===p?Object.defineProperty(c,g,{configurable:!0,enumerable:!0,value:a[g],writable:!0}):c[g]=a[g];return r.length>5&&Object.defineProperty(c,"cause",{configurable:!0,value:u(r[5],t+1),writable:!0}),c}case"bytes":{const e=r[2];if(typeof e!="string")throw new TypeError("wire-codec: malformed bytes — payload must be a base64 string");const n=L(e);if(l(n)!==e)throw new TypeError("wire-codec: malformed bytes — payload must be canonical padded base64");const y=r[3]??"Uint8Array";if(y==="ArrayBuffer")return n.buffer.byteLength===n.byteLength?n.buffer:n.slice().buffer;const c=Object.hasOwn(T,y)?T[y]:void 0;return c?new c(n.slice().buffer):n}case"inf":return 1/0;case"nan":return Number.NaN;case"undefined":return;default:return r.map(e=>u(e,t+1))}return r.map(f=>u(f,t+1))}const i=r,o={};for(const f of Object.keys(i)){const e=u(i[f],t+1);f===p?Object.defineProperty(o,f,{configurable:!0,enumerable:!0,value:e,writable:!0}):o[f]=e}return o},d=s,E="#",h="$",O=s+E,Y=s,S=(r,t)=>{if(!r.startsWith(d))return t(r);const i=r.slice(d.length);return i.startsWith(E)?D(i.slice(E.length))??r:i.startsWith(h)?i.slice(h.length):u(t(i))},B=new Set(["any","from","union"]),C=(r,t)=>{if(!(t===void 0||!B.has(t)))return typeof r=="number"?O+_(r):typeof r=="string"?r.startsWith(d)?d+h+r:void 0:typeof r=="boolean"?d+JSON.stringify(r):void 0},G=r=>r instanceof ArrayBuffer?r:ArrayBuffer.isView(r)?new Uint8Array(r.buffer,r.byteOffset,r.byteLength).slice().buffer:Array.isArray(r)?Uint8Array.from(r).buffer:r,M=(r,t)=>{const i=C(r,t);if(i!==void 0)return i;if(typeof r=="boolean")return r?1:0;if(r===null||typeof r=="string"||typeof r=="number")return r;if(typeof r=="bigint")return P(r);if(r instanceof Uint8Array)return r;if(r instanceof ArrayBuffer)return new Uint8Array(r);try{return A(r)?d+JSON.stringify(b(r)):JSON.stringify(r)}catch(o){throw new k("BAD_REQUEST",`this value cannot be stored: ${o instanceof Error?o.message:String(o)}`)}},w=r=>{try{return JSON.parse(r)}catch{return r}},q=r=>{if(typeof r!="string"||!r.startsWith(d)||r.startsWith(O))return;const t=w(r.slice(d.length));return typeof t=="number"?O+_(t):void 0},J=r=>r!==void 0&&B.has(r),F=r=>{if(typeof r!="string")return r;const t=N(r);if(t!==void 0)return t;try{return BigInt(r)}catch{return r}},X=r=>j(r),H=(r,t)=>{if(r===null)return r;switch(t){case"any":case"from":case"union":return typeof r!="string"?r:r.startsWith("{")||r.startsWith("[")||r.startsWith(d)?S(r,w):N(r)??r;case"array":case"geoPoint":case"object":case"record":return typeof r=="string"?S(r,w):r;case"bigint":return F(r);case"boolean":return r===0||r===1?r===1:r;case"bytes":return G(r);default:return r}},V=W+1;export{V as B,Y as U,X as a,O as b,H as c,u as d,j as e,b as f,F as g,J as i,A as n,q as r,M as s,w as t};
1
+ import{LunoraError as k}from"@lunora/errors";import{decodeBigintSqlKey as N,bigintSqlKey as P,BIGINT_KEY_DIGITS as W,float64SqlKey as _,decodeFloat64SqlKey as D}from"@lunora/shard-engine";const K=r=>{const{value:t}=r._meta??{};if(t===null)return"null";switch(typeof t){case"bigint":case"boolean":case"number":case"string":return typeof t;default:return r.kind}},j=r=>{if(r.kind==="literal")return K(r);if(r.kind!=="optional")return r.kind;const t=r._meta?.inner;return t?j(t):r.kind},l=r=>{let t="";for(let o=0;o<r.length;o+=32768)t+=String.fromCharCode(...r.subarray(o,o+32768));return btoa(t)},L=r=>{const t=atob(r),i=new Uint8Array(t.length);for(let o=0;o<t.length;o+=1)i[o]=t.codePointAt(o)??0;return i},s="$lunora.wire$",m=64,I=1024,p="__proto__",T={BigInt64Array,BigUint64Array,Float32Array,Float64Array,Int8Array,Int16Array,Int32Array,Uint8Array,Uint8ClampedArray,Uint16Array,Uint32Array},U={Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError},R=r=>{if(r===null||typeof r!="object")return!1;const t=Object.getPrototypeOf(r);return t===null||t===Object.prototype},A=(r,t=0)=>{if(t>m)return!0;if(r===null)return!1;const i=typeof r;if(i==="string"||i==="boolean")return!1;if(i==="number")return!Number.isFinite(r);if(i!=="object")return!0;if(Array.isArray(r))return r.length>0&&r[0]===s?!0:r.some(o=>o===void 0||A(o,t+1));if(!R(r)||Object.hasOwn(r,p))return!0;for(const o of Object.keys(r)){const f=r[o];if(f!==void 0&&A(f,t+1))return!0}return!1},b=(r,t=0)=>{if(t>m)throw new RangeError(`wire-codec: value nesting exceeds the ${m}-level limit`);if(r===void 0)return[s,"undefined"];if(r===null)return null;const i=typeof r;if(i==="bigint")return[s,"bigint",r.toString()];if(i==="number"){const e=r;return Number.isNaN(e)?[s,"nan"]:e===1/0?[s,"inf"]:e===-1/0?[s,"-inf"]:e}if(i!=="object")return r;if(r instanceof Date)return[s,"date",b(r.getTime(),t+1)];if(r instanceof Error){const e=r,n={};for(const c of Object.keys(e)){if(e[c]===void 0)continue;const a=b(e[c],t+1);c===p?Object.defineProperty(n,c,{configurable:!0,enumerable:!0,value:a,writable:!0}):n[c]=a}const y=[s,"error",String(e.name),String(e.message),n];return e.cause!==void 0&&y.push(b(e.cause,t+1)),y}if(r instanceof URL)return[s,"url",r.href];if(r instanceof Map)return[s,"map",[...r.entries()].map(([e,n])=>[b(e,t+1),b(n,t+1)])];if(r instanceof Set)return[s,"set",[...r].map(e=>b(e,t+1))];if(r instanceof ArrayBuffer)return[s,"bytes",l(new Uint8Array(r)),"ArrayBuffer"];if(ArrayBuffer.isView(r)){const e=r,n=e.constructor.name,y=new Uint8Array(e.buffer,e.byteOffset,e.byteLength);return n==="Uint8Array"?[s,"bytes",l(y)]:[s,"bytes",l(y),n]}if(Array.isArray(r)){const e=r.map(n=>b(n,t+1));return e.length>0&&e[0]===s?[s,"arr",e]:e}if(!R(r)){const e=r.constructor?.name??"value";throw new TypeError(`wire-codec: cannot encode a ${e} over the Lunora wire — only plain objects, arrays, and the supported built-ins (Date, Error, URL, Map, Set, ArrayBuffer/typed arrays, bigint) round-trip`)}const o=r,f={};for(const e of Object.keys(o)){const n=o[e];if(n===void 0)continue;const y=b(n,t+1);e===p?Object.defineProperty(f,e,{configurable:!0,enumerable:!0,value:y,writable:!0}):f[e]=y}return f},u=(r,t=0)=>{if(t>m)throw new RangeError(`wire-codec: value nesting exceeds the ${m}-level limit`);if(r===null||typeof r!="object")return r;if(Array.isArray(r)){if(r[0]===s)switch(r[1]){case"-inf":return-1/0;case"arr":return r[2].map(e=>u(e,t+1));case"bigint":{const e=r[2];if(typeof e!="string"||e.length>I||!/^-?\d+$/.test(e))throw new RangeError(`wire-codec: invalid or over-long bigint (max ${I} digits)`);return BigInt(e)}case"date":{const e=u(r[2],t+1);if(typeof e!="number")throw new TypeError("wire-codec: malformed date — epoch must be a number");return new Date(e)}case"map":{const e=r[2];return new Map(e.map(n=>{if(!Array.isArray(n)||n.length!==2)throw new TypeError("wire-codec: malformed map entry — expected a [key, value] pair");return[u(n[0],t+1),u(n[1],t+1)]}))}case"set":return new Set(r[2].map(e=>u(e,t+1)));case"url":{const e=r[2];if(typeof e!="string")throw new TypeError("wire-codec: malformed url — href must be a string");return new URL(e)}case"error":{const e=r[2],n=r[3];if(typeof e!="string"||typeof n!="string")throw new TypeError("wire-codec: malformed error — name and message must be strings");const y=(Object.hasOwn(U,e)?U[e]:void 0)??Error,c=new y(n);c.name!==e&&Object.defineProperty(c,"name",{configurable:!0,value:e,writable:!0});const a=u(r[4],t+1);if(a===null||typeof a!="object"||Array.isArray(a))throw new TypeError("wire-codec: malformed error — props must be an object");for(const g of Object.keys(a))g===p?Object.defineProperty(c,g,{configurable:!0,enumerable:!0,value:a[g],writable:!0}):c[g]=a[g];return r.length>5&&Object.defineProperty(c,"cause",{configurable:!0,value:u(r[5],t+1),writable:!0}),c}case"bytes":{const e=r[2];if(typeof e!="string")throw new TypeError("wire-codec: malformed bytes — payload must be a base64 string");const n=L(e);if(l(n)!==e)throw new TypeError("wire-codec: malformed bytes — payload must be canonical padded base64");const y=r[3]??"Uint8Array";if(y==="ArrayBuffer")return n.buffer.byteLength===n.byteLength?n.buffer:n.slice().buffer;const c=Object.hasOwn(T,y)?T[y]:void 0;return c?new c(n.slice().buffer):n}case"inf":return 1/0;case"nan":return Number.NaN;case"undefined":return;default:return r.map(e=>u(e,t+1))}return r.map(f=>u(f,t+1))}const i=r,o={};for(const f of Object.keys(i)){const e=u(i[f],t+1);f===p?Object.defineProperty(o,f,{configurable:!0,enumerable:!0,value:e,writable:!0}):o[f]=e}return o},d=s,E="#",h="$",O=s+E,Y=s,S=(r,t)=>{if(!r.startsWith(d))return t(r);const i=r.slice(d.length);return i.startsWith(E)?D(i.slice(E.length))??r:i.startsWith(h)?i.slice(h.length):u(t(i))},B=new Set(["any","from","union"]),C=(r,t)=>{if(!(t===void 0||!B.has(t)))return typeof r=="number"?O+_(r):typeof r=="string"?r.startsWith(d)?d+h+r:void 0:typeof r=="boolean"?d+JSON.stringify(r):void 0},G=r=>r instanceof ArrayBuffer?r:ArrayBuffer.isView(r)?new Uint8Array(r.buffer,r.byteOffset,r.byteLength).slice().buffer:Array.isArray(r)?Uint8Array.from(r).buffer:r,M=(r,t)=>{const i=C(r,t);if(i!==void 0)return i;if(typeof r=="boolean")return r?1:0;if(r===null||typeof r=="string"||typeof r=="number")return r;if(typeof r=="bigint")return P(r);if(r instanceof Uint8Array)return r;if(r instanceof ArrayBuffer)return new Uint8Array(r);try{return A(r)?d+JSON.stringify(b(r)):JSON.stringify(r)}catch(o){throw new k("BAD_REQUEST",`this value cannot be stored: ${o instanceof Error?o.message:String(o)}`)}},w=r=>{try{return JSON.parse(r)}catch{return r}},q=r=>{if(typeof r!="string"||!r.startsWith(d)||r.startsWith(O))return;const t=w(r.slice(d.length));return typeof t=="number"?O+_(t):void 0},J=r=>r!==void 0&&B.has(r),F=r=>{if(typeof r!="string")return r;const t=N(r);if(t!==void 0)return t;try{return BigInt(r)}catch{return r}},X=r=>j(r),H=(r,t)=>{if(r===null)return r;switch(t){case"any":case"from":case"union":return typeof r!="string"?r:r.startsWith("{")||r.startsWith("[")||r.startsWith(d)?S(r,w):N(r)??r;case"array":case"geoPoint":case"object":case"record":return typeof r=="string"?S(r,w):r;case"bigint":return F(r);case"boolean":return r===0||r===1?r===1:r;case"bytes":return G(r);default:return r}},V=W+1;export{V as B,Y as U,H as a,j as b,O as c,u as d,X as e,b as f,F as g,J as i,A as n,q as r,M as s,w as t};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/sql-store",
3
- "version": "1.0.0-alpha.147",
3
+ "version": "1.0.0-alpha.149",
4
4
  "description": "Internal dialect-parameterized SQL store core for Lunora .global() backends (D1, PlanetScale)",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -50,8 +50,8 @@
50
50
  "access": "public"
51
51
  },
52
52
  "dependencies": {
53
- "@lunora/errors": "1.0.0-alpha.46",
54
- "@lunora/shard-engine": "1.0.0-alpha.95",
53
+ "@lunora/errors": "1.0.0-alpha.47",
54
+ "@lunora/shard-engine": "1.0.0-alpha.96",
55
55
  "drizzle-orm": "^0.45.2"
56
56
  },
57
57
  "engines": {
@@ -1 +0,0 @@
1
- import"./ctx-db-search-state-CYA6goYx.mjs";import{d as S,c as s,a as i,r as l}from"./ctx-db-search-Cny_qeYp.mjs";import"./decodeGlobalRow-B-3UbPME.mjs";export{S as backfillSqlSearchIndexes,s as createSearchSync,i as runSqlSearch,l as runSqlSearchMigrations};
@@ -1,7 +0,0 @@
1
- import{LunoraError as v}from"@lunora/errors";import{m as Gt,D as Vt}from"./ctx-db-search-state-CYA6goYx.mjs";import{c as Yt,r as Qt,A as Xt,C as zt,R as Jt,u as Zt,a as en,T as tn,x as nn,b as on}from"./ctx-db-search-Cny_qeYp.mjs";import{d as Ci}from"./ctx-db-search-Cny_qeYp.mjs";import{serverInList as rn,throwingScheduler as sn,CDC_LOG_TABLE as H,cdcForkedError as an,cursorBelowRetainedFloor as cn,cdcTrimmedError as dn,assertNoExplicitUndefined as ot,hasTrigger as ln,runRowValidators as Be,assertFlatPredicate as rt,mergeWhere as O,rankTableName as $e,sortColumnName as pe,resolveRankPartition as st,encodePartitionKey as Ce,resolveRankSeekTuple as un,RANK_TIEBREAK as Re,CountRlsUnsupportedError as at,stripReservedPatchFields as fn,normalizeIdStructurally as wn,assertValidClientId as _n,aggregateSqlFunction as he,softDeleteScope as Ee,compileWhereSql as J,normalizeOrderKeys as $n,uniqueIndexFields as hn,equalityPinnedFields as En,buildSeekWhere as yn,decodeCursor as pn,resolveRelationPredicates as ct,resolveWith as dt,relationHooks as lt,applySelect as ut,encodeCursor as Rn,NotFoundError as Sn,applyOnDelete as gn,ConflictError as Le,normalizeCountArgument as vn,selectIndexForCount as Tn,encodeAggregateKey as Y,aggregateTableName as Z,selectIndexForAggregate as mn,readAggregateValue as Oe,runTriggers as bn,matchesRankStaticWhere as ft,rankPivotConditionSql as vt,CURSOR_PREFIX as In,selectIndexForGroupBy as An,tiebreakDirectionFor as Cn,fanOutScalarCounts as Ln,matchesStaticWhere as We,foldAggregateTally as wt,coerceAggregateNumber as Fe,whereOfFilter as kn,isPushableWhere as Mn,CDC_LOG_TABLE_SEQ_INDEX as Nn,NotUniqueError as qn,renderSql as Dn,WORKERD_SQLITE_LIMITS as Bn}from"@lunora/shard-engine";import{sql as e}from"drizzle-orm";import{d as Tt,c as mt,n as bt,f as It,a as At}from"./value-codec-CBr0_L1N.mjs";import{runSqlGlobalTableMigrations as On,runSqlAggregateMigrations as Wn,runSqlRankMigrations as Fn}from"./runSqlAggregateMigrations-CDcXk9js.mjs";import{queryAll as I,queryRun as N,createIndexIfNotExists as Un,decodeRow as ne,serializeDocumentColumn as ee,OCC_VERSION_COLUMN as ye,columnRefSql as W,decodeRows as _t,tableColumns as Ct,nextRowVersion as $t,serializeColumnValue as Ue,forEachRowPaged as ht,BACKFILL_BATCH_SIZE as xe,nullSafeEqualsSql as Ge,queryBatch as Et}from"./decodeGlobalRow-B-3UbPME.mjs";import{decodeGlobalRow as ki}from"./decodeGlobalRow-B-3UbPME.mjs";const xn=(a,d)=>{if(a.size<d)return;const $=a.keys().next().value;$!==void 0&&a.delete($)},yt=50,Pn=(a,d={})=>{const $=d.maxBatch??yt,r=Number.isInteger($)&&$>0?$:yt,R=new Map;let f=!1;const c=async(g,m)=>{const A=[...m.keys()];for(let L=0;L<A.length;L+=r){const q=A.slice(L,L+r);try{const U=await a(g,q);for(const G of q){const V=U.get(G);for(const re of m.get(G)??[])re.resolve(V)}}catch(U){for(const G of q)for(const V of m.get(G)??[])V.reject(U)}}},M=()=>{f=!1;const g=[...R.entries()];R.clear();for(const[m,A]of g)c(m,A).catch(()=>{})};return{async load(g,m){return new Promise((A,L)=>{let q=R.get(g);q||(q=new Map,R.set(g,q));const U=q.get(m);U?U.push({reject:L,resolve:A}):q.set(m,[{reject:L,resolve:A}]),f||(f=!0,queueMicrotask(M))})}}},Hn=new Set(["_id","id"]),Lt=(a,d)=>a.name!=="postgres"||d.nullable!==!0?"":d.direction==="desc"?" NULLS LAST":" NULLS FIRST",Kn=(a,d)=>{const $=a.map(r=>e`${W(r.field)} ${e.raw(`${r.direction==="desc"?"DESC":"ASC"}${Lt(d,r)}`)}`);if(!a.some(r=>Hn.has(r.field))){const r=Cn(a);$.push(e`${W("id")} ${e.raw(r==="desc"?"DESC":"ASC")}`)}return e.join($,e`, `)},pt=50,jn=(a,d,$)=>{const r={...d};for(const[R,f]of Ct(a)){if(f.serverDefault){r[R]=f.serverDefault({auth:$});continue}r[R]===void 0&&(f.defaultFn?r[R]=f.defaultFn():"defaultValue"in f&&(r[R]=f.defaultValue))}return r},Rt=(a,d,$,r)=>{for(const[R,f]of Ct(a)){if(f.serverDefault){R in d&&($[R]=f.serverDefault({auth:r}));continue}f.onUpdateFn&&!(R in d)&&($[R]=f.onUpdateFn())}},Gn=128,Vn=()=>{const a=new Map;return{delete:d=>{a.delete(d)},get:d=>{const $=a.get(d);if($!==void 0)return a.delete(d),a.set(d,$),$},set:(d,$)=>{a.has(d)?a.delete(d):xn(a,Gn),a.set(d,$)}}},Yn=async(a,d,$,r,R)=>{const f=R.get(r);if(f!==void 0)return f;const c=Object.entries($.tables).filter(([,g])=>g.shardMode===void 0||g.shardMode.kind==="global").map(([g])=>g),M=await Promise.all(c.map(async g=>({found:(await I(a,d,e`SELECT 1 FROM ${e.identifier(g)} WHERE ${e.identifier("id")} = ${r} LIMIT 1`)).length>0,tableName:g})));for(const g of M)if(g.found)return R.set(r,g.tableName),g.tableName},ie=a=>a==null?null:Number(a),Qn=(a,d,$)=>$.map(r=>{const R={};for(const f of d){const c=a.shape[f];R[f]=mt(r[f]??null,c&&At(c))}return{key:R,value:ie(r.value)}}),Xn=(a,d,$)=>[...a.partitionBy??[],...a.sortBy.map(R=>R.field),...a.where?Object.keys(a.where):[]].every(R=>d[R]===$[R]),kt=a=>a.length===1?a[0]:e`(${e.join(a,e` AND `)})`,te=a=>e.join(a.map(d=>e`${e.identifier(d)}`),e`, `),oe=a=>e.join(a.map(d=>e`${d}`),e`, `),Pe=(a,d)=>{if(a==="postgres"){for(const[$,r]of Object.entries(d))if(typeof r=="string"&&r.includes("\0"))throw new v("BAD_REQUEST",`"${$}" holds a NUL character (U+0000), which a Postgres text column cannot store`)}},ke=a=>a instanceof ArrayBuffer?new Uint8Array(a):Array.isArray(a)?Uint8Array.from(a):a,zn=(a,d,$,r,R)=>{const f=[];for(let c=0;c<$.length+1;c+=1){const M=$[c],g=d.sortBy[c],m=c<$.length&&M!==void 0&&g!==void 0,A=g?.direction==="desc"?"desc":"asc",L=m?vt(M,ke(r[M]),A,!1):e`${e.identifier(Re)} < ${R}`;if(L===void 0)continue;const q=$.slice(0,c).map(U=>Ge(a,e`${e.identifier(U)}`,ke(r[U])));q.push(L),f.push(kt(q))}return f.length>0?e.join(f,e` OR `):e`1 = 0`},Jn=(a,d,$)=>{if($.length!==d.length)return;const r=[];for(const[R,f]of d.entries()){const c=vt(f.column,ke($[R]),f.direction,!0);if(c===void 0)continue;const M=[];for(let g=0;g<R;g+=1){const m=d[g];m!==void 0&&M.push(Ge(a,e`${e.identifier(m.column)}`,ke($[g])))}M.push(c),r.push(kt(M))}return r.length===0?e`(1 = 0)`:e`(${e.join(r,e` OR `)})`},Zn=(a,d)=>{if(a.sortBy.length===0)throw new v("INTERNAL",`rankIndex "${a.name}" requires at least one "sortBy" column for stable pagination`);const $=[{column:"__partition__",direction:"asc",nullable:!1}];for(const[r,R]of a.sortBy.entries())$.push({column:d[r]??pe(r),direction:R.direction,nullable:!0});return $.push({column:Re,direction:"asc",nullable:!1}),$},ei=async(a,d,$,r,R)=>{const c=[];for(let A=0;A<R.length;A+=50)c.push(R.slice(A,A+50));const M=await Promise.all(c.map(async A=>I(a,d,e`SELECT * FROM ${e.identifier(r)} WHERE ${e.identifier("id")} IN (${oe(A)})`))),g=new Map;for(const A of M)for(const L of A)g.set(L.id,L);const m=[];for(const A of R){const L=ne($,g.get(A));L&&m.push(L)}return m},ti=a=>{const d=a.map(f=>mt(f,"bytes")),$=JSON.stringify(bt(d)?It(d):d),r=new TextEncoder().encode($);let R="";for(const f of r)R+=String.fromCodePoint(f);return In+btoa(R)},Mt=a=>{if(a===void 0)return!1;const d=At(a);return d==="any"||d==="bigint"||d==="from"||d==="union"},Ke=(a,d,$)=>{if(Mt(a.shape[d]))throw new v("BAD_REQUEST",`${$}: "${d}" may hold an order-preserving key rather than a value SQL can reduce or group — declare an aggregateIndex covering this (by, field, op) so the maintained companion answers it instead (its running total is a REAL, so it stays exact only while the total is inside 2^53)`)},ni=(a,d,$,r)=>{for(const R of $)Ke(a,R,`groupBy(${d}, { by: [..."${R}"] })`);r.field!==void 0&&Ke(a,r.field,`groupBy(${d}, { agg: { op: "${r.op}", field: "${r.field}" } })`)},je="__cdc_sweep",Nt="global",ii=6e4,oi=1e4,ri=3e4;let St=0;const si=async(a,d)=>{const{autoincrementPrimaryKey:$,key:r,real:R,text:f}=d.companionTypes;await N(a,d,e`CREATE TABLE IF NOT EXISTS ${e.identifier(H)} (${e.identifier("seq")} ${e.raw($)}, ${e.identifier("ts")} ${e.raw(R)} 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(f)})`);const c=d.indexKeyPrefix?.("string");await Un(a,d,{columns:e`${c===void 0?e`${e.identifier("table")}`:e`${e.identifier("table")}(${e.raw(String(c))})`}, ${e.identifier("seq")}`,name:Nn,table:H,unique:!1}),await N(a,d,e`CREATE TABLE IF NOT EXISTS ${e.identifier(je)} (${e.identifier("id")} ${e.raw(r)} NOT NULL PRIMARY KEY, ${e.identifier("lease_until")} ${e.raw(R)} NOT NULL)`),await N(a,d,e`INSERT INTO ${e.identifier(je)} (${e.identifier("id")}, ${e.identifier("lease_until")}) VALUES (${Nt}, ${0}) ${d.name==="mysql"?e`ON DUPLICATE KEY UPDATE ${e.identifier("id")} = ${e.identifier("id")}`:e`ON CONFLICT(${e.identifier("id")}) DO NOTHING`}`)},ai=async(a,d,$)=>{const r=e`UPDATE ${e.identifier(je)} SET ${e.identifier("lease_until")} = ${$+ii} WHERE ${e.identifier("id")} = ${Nt} AND ${e.identifier("lease_until")} <= ${$}`;if(d.supportsReturning)return(await I(a,d,e`${r} RETURNING ${e.identifier("id")}`)).length>0;const R=await N(a,d,r);return(d.affectedRows?d.affectedRows(R??{rowsAffected:0}):0)>0},ci=async(a,d,$,r)=>{if(!await ai(a,d,r))return;const R=r-$;await N(a,d,e`
2
- DELETE FROM ${e.identifier(H)} WHERE ${e.identifier("seq")} IN (
3
- SELECT ${e.identifier("seq")} FROM (
4
- SELECT ${e.identifier("seq")} FROM ${e.identifier(H)} WHERE ${e.identifier("ts")} <= ${R} ORDER BY ${e.identifier("seq")} ASC LIMIT ${e.raw(String(oi))}
5
- ) AS ${e.identifier("expired")}
6
- )
7
- `)},qt=async(a,d)=>{const $=await I(a,d,e`SELECT MIN(${e.identifier("seq")}) AS ${e.identifier("seq")} FROM ${e.identifier(H)}`),r=Number($[0]?.seq??Number.NaN);return Number.isFinite(r)?r:void 0},di=async(a,d)=>{if(d.name!=="sqlite")return;const $=await I(a,d,e`SELECT ${e.identifier("seq")} AS ${e.identifier("seq")} FROM ${e.identifier("sqlite_sequence")} WHERE ${e.identifier("name")} = ${H}`),r=Number($[0]?.seq??Number.NaN);return Number.isFinite(r)?r:0},li=a=>JSON.stringify(bt(a)?It(a):a),ui=async(a,d,$,r,R,f,c)=>{await N(a,c,e`INSERT INTO ${e.identifier(H)} (${e.identifier("ts")}, ${e.identifier("table")}, ${e.identifier("id")}, ${e.identifier("op")}, ${e.identifier("doc")}) VALUES (${d}, ${$}, ${r}, ${R}, ${f===void 0?null:li(f)})`)},mi=async(a,d,$)=>{const r=d.sinceSeq??0,R=Math.max(1,Math.min(d.limit??1e3,1e4)),f=await qt(a,$),c=await di(a,$);if(c!==void 0&&r>c)throw an(c,r,"global");if(f!==void 0&&cn(f,r))throw dn(f,r,"global");const g=(await I(a,$,e`SELECT seq, ts, ${e.identifier("table")}, id, op, doc FROM ${e.identifier(H)} WHERE seq > ${r} ORDER BY seq ASC LIMIT ${e.raw(String(R))}`)).map(m=>{const{doc:A}=m,L={id:String(m.id),op:String(m.op),seq:Number(m.seq),table:String(m.table),ts:Number(m.ts)};return typeof A=="string"?{...L,doc:Tt(JSON.parse(A))}:L});return{changes:g,cursor:g.at(-1)?.seq??r}},fi=async(a,d,$,r={})=>{if(r.cursorOnly){const g=await I(a,$,e`SELECT MAX(seq) AS seq FROM ${e.identifier(H)}`),m=Number(g[0]?.seq??d);return{cursor:Number.isFinite(m)?m:d,tables:[]}}const R=await I(a,$,e`SELECT ${e.identifier("table")} AS ${e.identifier("table")}, MAX(seq) AS seq FROM ${e.identifier(H)} WHERE seq > ${d} GROUP BY ${e.identifier("table")}`);let f=d;const c=[];for(const g of R){const m=Number(g.seq);Number.isFinite(m)&&m>f&&(f=m),c.push(String(g.table))}const M=r.retained===!0?await qt(a,$):void 0;return{cursor:f,tables:c,...M===void 0?{}:{floor:M}}},wi=/string or blob too big/iu,_i=/row is too big/iu,$i=(a,d)=>{const{code:$,errno:r}=d,R=d instanceof Error?d.message:"";switch(a.name){case"mysql":return r===1118||$==="ER_TOO_BIG_ROWSIZE"?"InnoDB's per-row ceiling — roughly 8 KB, half a 16 KB page, for the part of the row stored inline":void 0;case"postgres":return _i.test(R)?"the 8 KB heap page a tuple must fit once its wide columns have been TOASTed out":void 0;default:return wi.test(R)?"the storage engine's per-row ceiling (2 MB on D1)":void 0}},hi=(a,d,$)=>{const r=$i(a,d);if(r!==void 0)throw new v("PAYLOAD_TOO_LARGE",`document is too large to store in "${$}": a single row cannot exceed ${r}. The limit is on the STORED bytes, which are UTF-8. Keep the payload in R2 (ctx.storage) and store a reference on the row.`)},gt=(a,d,$)=>{throw a.isUniqueViolation(d)?new Le(`unique constraint violation on "${$}"`,"unique"):(hi(a,d,$),d)},He=new WeakMap,bi=a=>{const{crossShardCounter:d,crossShardReader:$,exec:r,maxRelationKeys:R,schema:f}=a,{dialect:c}=a,M={fieldRef:W,...c.name==="mysql"?{containsExpr:(t,n)=>e`LOCATE(${n}, ${t}) > 0`}:{},...c.name==="postgres"?{containsExpr:(t,n)=>e`strpos(${t}, ${n}) > 0`}:{},...c.name==="sqlite"?{}:{inList:rn(c.name)}},g=new WeakMap,m=t=>{const n=g.get(t);if(n!==void 0)return n;const i={...M,serialize:(o,s)=>ee(t,s,o)};return g.set(t,i),i},A=(t,n)=>Ge(c.name,t,n),L=t=>{const n=te(t.columns),i=oe(t.values),o=_=>c.name==="mysql"?e`VALUES(${e.identifier(_)})`:e`excluded.${e.identifier(_)}`,s=_=>c.name==="postgres"?e`${e.identifier(t.table)}.${e.identifier(_)}`:e`${e.identifier(_)}`,u=e.join(Object.entries(t.set(o,s)).map(([_,y])=>e`${e.identifier(_)} = ${y}`),e`, `),h=c.name==="mysql"?e`ON DUPLICATE KEY UPDATE ${u}`:e`ON CONFLICT(${e.identifier(t.conflictKey)}) DO UPDATE SET ${u}`;return e`INSERT INTO ${e.identifier(t.table)} (${n}) VALUES (${i}) ${h}`},q=a.clock??(()=>Date.now()),U=a.idGenerator??(()=>crypto.randomUUID()),G=a.cdc??!1,V=typeof a.cdcRetentionMs=="number"&&Number.isFinite(a.cdcRetentionMs)&&a.cdcRetentionMs>0?a.cdcRetentionMs:void 0,re=a.auth??{identity:null,userId:null},se=async(t,n,i,o)=>{if(!G||(await ui(r,q(),t,n,i,o,c),V===void 0))return;const s=q();if(!(s-St<ri)){St=s;try{await ci(r,c,V,s)}catch{}}},Dt=a.scheduler??sn,Me=Vn();let Se=0;const Ne=a.provisionScope??{},x=async()=>{const t=He.get(Ne);if(t)return t;const n=(async()=>{await On(r,f,c),await Wn(r,f,c),await Fn(r,f,c),await Gt(r,c),await Qt(r,f,c),G&&await si(r,c)})().catch(i=>{throw He.delete(Ne),i});return He.set(Ne,n),n},ae=async(t,n)=>{await x();const i=await Yn(r,c,f,t,Me);if(!(n!==void 0&&i!==n))return i},ce=new Map,Ve=async(t,n)=>{const i=Z(t,n);return(await I(r,c,c.tableExists(i))).length>0},ge=async(t,n)=>{const i=`${t}::${n.name}`,o=ce.get(i);if(o!==void 0)return o;if(!await Ve(t,n.name))return ce.set(i,!1),!1;const u=f.tables[t];if(!u)return ce.set(i,!1),!1;const h=n.by??[],_=new Map;await ht(r,c,u,t,E=>{if(n.where&&!We(E,n.where))return;const l=Y(h,E);wt(_,l,n,E)});const y=Z(t,n.name);await N(r,c,e`DELETE FROM ${e.identifier(y)}`);const w=[];for(const[E,l]of _)w.push(e`INSERT INTO ${e.identifier(y)} (${e.identifier("__key__")}, ${e.identifier("__value__")}, ${e.identifier("__count__")}) VALUES (${E}, ${l.value}, ${l.count})`);return await Et(r,c,w),ce.set(i,!0),!0},Bt=async(t,n,i,o)=>{const s=new Map;let u,h=!0;for(;h;){const _=u===void 0?o:[...o,e`${e.identifier("id")} > ${u}`],y=_.length>0?e` WHERE ${e.join(_,e` AND `)}`:e``,w=await I(r,c,e`SELECT ${e.identifier("id")}, ${W(i.field??"")} FROM ${e.identifier(n)}${y} ORDER BY ${e.identifier("id")} ASC LIMIT ${e.raw(String(xe))}`);for(const E of _t(t,w))wt(s,"",i,E);u=w.at(-1)?.id,h=w.length===xe}return s.get("")?.value??null},Ot=async(t,n,i)=>{const o=n.field??"",s=[],u=f.tables[t],h=(E,l)=>u?ee(u,E,l):Ue(l);for(const E of n.by??[]){const l=h(E,i[E]??null);s.push(l===null?e`${W(E)} IS NULL`:e`${W(E)} = ${l}`)}for(const[E,l]of Object.entries(n.where??{})){const S=l!==null&&typeof l=="object"&&!Array.isArray(l)?l.eq:l,p=h(E,S);s.push(p===null?e`${W(E)} IS NULL`:e`${W(E)} = ${p}`)}const _=s.length>0?e` WHERE ${e.join(s,e` AND `)}`:e``;if(u&&Mt(u.shape[o]))return Bt(u,t,n,s);const y=he(n.op),w=await I(r,c,e`SELECT ${e.raw(y)}(${W(o)}) AS ${e.identifier("value")} FROM ${e.identifier(t)}${_}`);return ie(w[0]?.value)},Wt=async(t,n)=>{await N(r,c,e`DELETE FROM ${e.identifier(t)} WHERE ${e.identifier("__key__")} = ${n} AND ${e.identifier("__count__")} <= 0`)},Ft=async(t,n,i,o)=>{const s=Z(t,n.name),{op:u}=n,h=n.field??"",_=i&&(!n.where||We(i,n.where))?i:void 0,y=o&&(!n.where||We(o,n.where))?o:void 0;if(!(!_&&!y)){if(u==="count"||u==="sum"||u==="avg"){const w=new Set;for(const[E,l]of[[_,-1],[y,1]]){if(!E)continue;const S=u==="count"?1:Fe(E[h]);if(S===void 0)continue;const p=Y(n.by??[],E);w.add(p),await N(r,c,L({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(T,b)=>({__count__:e`${b("__count__")} + ${T("__count__")}`,__value__:u==="count"?e`${b("__value__")} + ${T("__value__")}`:e`COALESCE(${b("__value__")}, 0) + ${T("__value__")}`}),table:s,values:[p,l*S,l]}))}for(const E of w)await Wt(s,E);return}if(_){const w=Y(n.by??[],_),E=Fe(_[h]),S=(await I(r,c,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(s)} WHERE ${e.identifier("__key__")} = ${w}`))[0],p=ie(S?.value),T=(S?.count??0)-1;if(T<=0)await N(r,c,e`DELETE FROM ${e.identifier(s)} WHERE ${e.identifier("__key__")} = ${w}`);else if(S&&E!==void 0&&p!==null&&E===p){const b=await Ot(t,n,_);await N(r,c,e`UPDATE ${e.identifier(s)} SET ${e.identifier("__value__")} = ${b}, ${e.identifier("__count__")} = ${T} WHERE ${e.identifier("__key__")} = ${w}`)}else await N(r,c,e`UPDATE ${e.identifier(s)} SET ${e.identifier("__count__")} = ${e.identifier("__count__")} - 1 WHERE ${e.identifier("__key__")} = ${w}`)}if(y){const w=Y(n.by??[],y),E=Fe(y[h]);if(E===void 0)await N(r,c,L({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(l,S)=>({__count__:e`${S("__count__")} + 1`}),table:s,values:[w,null,1]}));else{const l=u==="min"?"MIN":"MAX";await N(r,c,L({columns:["__key__","__value__","__count__"],conflictKey:"__key__",set:(S,p)=>({__count__:e`${p("__count__")} + 1`,__value__:e`${e.raw(l)}(COALESCE(${p("__value__")}, ${S("__value__")}), ${S("__value__")})`}),table:s,values:[w,E,1]}))}}}},ve=async t=>{const n=f.tables[t]?.aggregateIndexes;if(!(!n||n.length===0))for(const i of n)await ge(t,i)},de=async(t,n,i)=>{const o=f.tables[t]?.aggregateIndexes;if(!(!o||o.length===0))for(const s of o){const u=`${t}::${s.name}`;(ce.get(u)??await Ve(t,s.name))&&await Ft(t,s,n,i)}},le=new Map,Ye=async(t,n)=>{const i=$e(t,n);return(await I(r,c,c.tableExists(i))).length>0},qe=async(t,n)=>{const i=`${t}::rank::${n.name}`,o=le.get(i);if(o!==void 0)return o;if(!await Ye(t,n.name))return le.set(i,!1),!1;const u=f.tables[t];if(!u)return le.set(i,!1),!1;const h=$e(t,n.name);await N(r,c,e`DELETE FROM ${e.identifier(h)}`);const _=n.sortBy.map((l,S)=>pe(S)),y=te(["__id__","__partition__",..._]);let w=[];const E=async()=>{if(w.length===0)return;const l=w;w=[],await Et(r,c,l)};return await ht(r,c,u,t,async l=>{if(n.where&&!ft(l,n.where))return;const S=Ce(n.partitionBy??[],l),p=n.sortBy.map(T=>Ue(l[T.field]??null));w.push(e`INSERT INTO ${e.identifier(h)} (${y}) VALUES (${oe([l._id,S,...p])})`),w.length>=xe&&await E()}),await E(),le.set(i,!0),!0},Te=async t=>{const n=f.tables[t]?.rankIndexes;if(!(!n||n.length===0))for(const i of n)await qe(t,i)},Q=async(t,n,i,o)=>{const s=f.tables[t],u=s?.rankIndexes;if(!u||u.length===0)return;const h=s?.softDeleteMode?.field,_=h===void 0||o===void 0?void 0:o[h],y=_!=null;for(const w of u){if(i!==void 0&&o!==void 0&&Xn(w,i,o))continue;const E=`${t}::rank::${w.name}`;if(!(le.get(E)??await Ye(t,w.name)))continue;const p=$e(t,w.name);if(i&&await N(r,c,e`DELETE FROM ${e.identifier(p)} WHERE ${e.identifier("__id__")} = ${n}`),o&&!y){if(w.where&&!ft(o,w.where))continue;const T=w.sortBy.map((D,F)=>pe(F)),b=te(["__id__","__partition__",...T]),k=Ce(w.partitionBy??[],o),C=w.sortBy.map(D=>Ue(o[D.field]??null));await N(r,c,e`INSERT INTO ${e.identifier(p)} (${b}) VALUES (${oe([n,k,...C])})`)}}},ue=Yt({dialect:c,exec:r,schema:f}),Qe=new Set;for(const[t,n]of Object.entries(f.tables))for(const i of Object.values(n.triggerMap??{}))Qe.add(`${t} ${i.timing} ${i.op}`);const K=(t,n,i)=>Qe.has(`${t} ${n} ${i}`);let Xe;const j=async(t,n,i)=>{if(Se+=1,Se>pt)throw Se-=1,new Le(`trigger recursion exceeded ${String(pt)} levels on "${i.table}" — check for a self-triggering write`,"trigger");try{await bn({ctx:Xe,event:i,op:n,schema:f,tableName:i.table,timing:t})}finally{Se-=1}},Ut=async(t,n)=>{try{await N(r,c,n)}catch(i){gt(c,i,t)}},me=async(t,n)=>(await I(r,c,e`SELECT * FROM ${e.identifier(t)} WHERE ${e.identifier("id")} = ${n}`))[0],be=async(t,n,i,o)=>{if(o===void 0)return;const s=e`${e.identifier(ye)}`,u=e.join([A(e`${e.identifier("id")}`,o.id),A(s,o[ye]??null)],e` AND `),h=n==="UPDATE"?e`UPDATE ${e.identifier(t)} SET ${i}, ${s} = ${$t.sql(s)} WHERE ${u}`:e`DELETE FROM ${e.identifier(t)} WHERE ${u}`,_=()=>{throw new Le(`optimistic concurrency conflict on "${t}" — the row changed during this mutation; refetch and retry`,"occ")};try{if(c.supportsReturning)(await I(r,c,e`${h} RETURNING ${e.identifier("id")}`)).length===0&&_();else{const y=await N(r,c,h);(c.affectedRows?c.affectedRows(y??{rowsAffected:0}):0)===0&&_()}}catch(y){gt(c,y,t)}},De=(t,n=t?._creationTime)=>({_creationTime:n,[ye]:$t.value(t?.[ye])}),xt=(t,n,i,o)=>{const s=Object.keys(t.shape);return{columns:["id","_creationTime",...s],values:[n,i,...s.map(u=>ee(t,u,o[u]??null))]}},Pt=async(t,n,i,o)=>{const s=An(n,i.op,i.field,o.by,o.where);if(!s||!await ge(t,s.index))return;const h=Z(t,s.index.name),_=Object.keys(s.partial);if(_.length===(s.index.by??[]).length&&_.length>0){const w=Y(s.index.by??[],s.partial),E=await I(r,c,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(h)} WHERE ${e.identifier("__key__")} = ${w}`);if(E.length===0)return[];const l=E[0];return[{key:{...s.partial},value:Oe(i.op,{count:l.count,value:ie(l.value)})}]}return _.length>0?void 0:(await I(r,c,e`SELECT ${e.identifier("__key__")} AS ${e.identifier("key")}, ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(h)}`)).map(w=>{const E=w;return{key:Tt(JSON.parse(E.key)),value:Oe(i.op,{count:E.count,value:ie(E.value)})}})},ze=t=>{const n=f.tables[t]?.shardMode?.kind;return n==="shardBy"||n==="root"},Je=t=>{throw new v("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`)},Ht=(t,n)=>{if(n?.relationMask!==void 0&&n.with!==void 0&&Object.keys(n.with).length>0)throw new v("MASK_UNSUPPORTED",`masking cannot follow a nested \`with\` across the cross-shard relation hop to "${t}" — the nested rows are hydrated on the serving shard, where the mask policy does not exist. Read the nested relation at its own (masked) call site instead.`);const i={};if(n?.relationBaseWhere)for(const o of Object.keys(f.tables)){const s=n.relationBaseWhere(o);s!==void 0&&(i[o]=s)}return{orderBy:n?.orderBy,relationPolicies:i,where:O(n?.baseWhere,n?.where),with:n?.with}},Ze=(t,n)=>ze(t)?$?$(t,Ht(t,n)):Je(t):P.findMany(t,n),Ie=(t,n,i)=>ct(t,{fetcher:Ze,maxRelationKeys:R,relationBaseWhere:i,schema:f,tableName:n}),Kt=Pn(async(t,n)=>{const i=await I(r,c,e`SELECT * FROM ${e.identifier(t)} WHERE ${e.identifier("id")} IN (${oe(n)})`),o=new Map;for(const s of i){const{id:u}=s;typeof u=="string"&&o.set(u,s)}return o}),P={async cdcChangedTables(t,n){if(G)try{return await x(),await fi(r,t,c,{...n,retained:V!==void 0})}catch{return}},async aggregate(t,n){const i=f.tables[t];if(!i)throw new v("INTERNAL",`unknown table: ${t}`);if(await x(),he(n.op),n.op==="count")return P.count(t,{baseWhere:n.baseWhere,relationBaseWhere:n.relationBaseWhere,restrictsCounts:n.restrictsCounts,where:n.where});if(!n.field)throw new v("INTERNAL",`aggregate(${t}, { op: "${n.op}" }): "field" is required for non-count reducers`);const o=Ee(i.softDeleteMode,void 0),s=O(O(n.baseWhere,n.where),o),u=await Ie(s,t,n.relationBaseWhere),h=u!==s;if(i.aggregateIndexes&&!n.baseWhere&&!h&&!o){const S=mn(i.aggregateIndexes,n.op,n.field,n.where);if(S&&await ge(t,S.index)){const T=Y(S.index.by??[],S.key),b=Z(t,S.index.name),C=(await I(r,c,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")}, ${e.identifier("__count__")} AS ${e.identifier("count")} FROM ${e.identifier(b)} WHERE ${e.identifier("__key__")} = ${T}`))[0];return Oe(n.op,C===void 0?void 0:{count:C.count,value:ie(C.value)})}}Ke(i,n.field,`aggregate(${t}, { op: "${n.op}", field: "${n.field}" })`);const _=J(u,m(i)),y=e.raw(he(n.op)),w=e`SELECT ${y}(${W(n.field)}) AS ${e.identifier("value")} FROM ${e.identifier(t)}`,l=(await I(r,c,_?e`${w} WHERE ${_}`:w))[0]?.value;return l==null?null:Number(l)},async count(t,n){const i=f.tables[t];if(!i)throw new v("INTERNAL",`unknown table: ${t}`);await x();const o=vn(n);if(o.restrictsCounts)throw new at(t);const s=Ee(i.softDeleteMode,void 0),u=O(O(o.baseWhere,o.where),s),h=await Ie(u,t,o.relationBaseWhere),_=h!==u;if(i.aggregateIndexes&&!o.baseWhere&&!_&&!s){const l=Tn(i.aggregateIndexes,o.where);if(l&&await ge(t,l.index)){const p=Y(l.index.by??[],l.key),T=Z(t,l.index.name),b=await I(r,c,e`SELECT ${e.identifier("__value__")} AS ${e.identifier("value")} FROM ${e.identifier(T)} WHERE ${e.identifier("__key__")} = ${p}`);return Number(b[0]?.value??0)}}const y=J(h,m(i)),w=e`SELECT COUNT(*) AS ${e.identifier("count")} FROM ${e.identifier(t)}`,E=await I(r,c,y?e`${w} WHERE ${y}`:w);return Number(E[0]?.count??0)},async delete(t,n,i){const o=await ae(t,n);if(!o)return;const s=f.tables[o];if(!s)return;const u=await me(o,t),h=ne(s,u),_=i?.hard===!0,y=!_&&s.softDeleteMode?s.softDeleteMode.field:void 0;if(!(y&&(!h||h[y]!==null&&h[y]!==void 0))){if(K(o,"before","delete")&&await j("before","delete",{id:t,op:"delete",previous:h??void 0,table:o}),await gn({deletedId:t,deletedReference:w=>h?.[w],findHolders:async(w,E,l)=>{if(f.tables[w]?.shardMode?.kind==="shardBy")throw new v("INTERNAL",`cross-backend cascade from global '${o}' into shardBy '${w}' is not supported — would require Query Coordinator fan-out across shards`);return(await P.findMany(w,{includeDeleted:_,where:{[E]:l}})).page},onCascade:(w,E)=>P.delete(E,void 0,i),onRestrict:w=>{throw new Le(w,"restrict")},onSetNull:(w,E,l)=>P.patch(E,{[l]:null}),schema:f,tableName:o}),await ve(o),await Te(o),y&&h){const w={...h,[y]:q()},E=e.join(Object.keys(s.shape).map(l=>e`${e.identifier(l)} = ${ee(s,l,w[l]??null)}`),e`, `);await be(o,"UPDATE",E,u),await de(o,h,w),await Q(o,t,h,void 0),await ue(o,t,{document:w,previous:h,written:De(u)}),await se(o,t,"update",w),K(o,"after","delete")&&await j("after","delete",{id:t,op:"delete",previous:h,table:o});return}await be(o,"DELETE",void 0,u),Me.delete(t),await de(o,h??void 0,void 0),await Q(o,t,h??void 0,void 0),await ue(o,t,void 0),await se(o,t,"delete"),K(o,"after","delete")&&await j("after","delete",{id:t,op:"delete",previous:h??void 0,table:o})}},async findFirst(t,n={}){return(await P.findMany(t,{...n,limit:1})).page[0]??null},async findFirstOrThrow(t,n={}){const i=await P.findFirst(t,n);if(i===null)throw new Sn(`findFirstOrThrow: no "${t}" document matched`);return i},async findMany(t,n={}){const i=f.tables[t];if(!i)throw new v("INTERNAL",`unknown table: ${t}`);await x();const o=$n(n.orderBy,i.shape,{pinned:En(n.where),uniqueBy:hn(i.indexes,i.shape)}),s=n.cursor?yn(o,pn(n.cursor)):void 0,u=Ze,h=async(C,D,F,X)=>{if(ze(C))return d?Ln(d,C,D,F,X):Je(C);const fe=f.tables[C];if(!fe)throw new v("INTERNAL",`unknown table: ${C}`);const Ae=Ee(fe.softDeleteMode,void 0),z={[D]:{in:F}},B=O(O(z,X),Ae),we=await Ie(B,C,void 0),et=J(we,m(fe)),tt=W(D);let _e=e`SELECT ${tt} AS __fk__, COUNT(*) AS ${e.identifier("count")} FROM ${e.identifier(C)}`;et&&(_e=e`${_e} WHERE ${et}`),_e=e`${_e} GROUP BY ${tt}`;const jt=await I(r,c,_e),nt=new Map;for(const it of jt)nt.set(it.__fk__,Number(it.count??0));return nt};let _=O(n.baseWhere,n.where);_=O(_,Ee(i.softDeleteMode,n.includeDeleted)),_=await ct(_,{fetcher:u,maxRelationKeys:R,relationBaseWhere:n.relationBaseWhere,schema:f,tableName:t}),s&&(_=_?{AND:[_,s]}:s);const y=J(_,m(i)),w=Kn(o,c);let E=e`SELECT * FROM ${e.identifier(t)}`;y&&(E=e`${E} WHERE ${y}`),E=e`${E} ORDER BY ${w}`;const l=typeof n.limit=="number"?Math.max(0,Math.floor(n.limit)):void 0;l!==void 0&&(E=e`${E} LIMIT ${e.raw(String(l+1))}`);const S=await I(r,c,E),p=_t(i,S);if(l===void 0)return n.with&&await dt({fetcher:u,groupedCounter:h,parents:p,...lt(n),schema:f,tableName:t,with:n.with}),{continueCursor:null,isDone:!0,page:ut(p,n.select,n.with)};const T=p.length>l,b=T?p.slice(0,l):p,k=b.at(-1);return n.with&&await dt({fetcher:u,groupedCounter:h,parents:b,...lt(n),schema:f,tableName:t,with:n.with}),{continueCursor:T&&k?Rn(k,o):null,isDone:!T,page:ut(b,n.select,n.with)}},async get(t,n){const i=await ae(t,n);if(!i)return null;const o=f.tables[i];return o?ne(o,await Kt.load(i,t)):null},async groupBy(t,n){const i=f.tables[t];if(!i)throw new v("INTERNAL",`unknown table: ${t}`);await x();const o=n.agg??{op:"count"};if(he(o.op),o.op!=="count"&&!o.field)throw new v("INTERNAL",`groupBy(${t}, { agg: { op: "${o.op}" } }): "field" is required for non-count reducers`);const s=Ee(i.softDeleteMode,void 0),u=O(O(n.baseWhere,n.where),s),h=await Ie(u,t,n.relationBaseWhere),_=h!==u;if(i.aggregateIndexes&&!n.baseWhere&&!_&&!s){const p=await Pt(t,i.aggregateIndexes,o,n);if(p!==void 0)return p}ni(i,t,n.by,o);const y=J(h,m(i)),w=n.by.map(p=>e`${W(p)} AS ${e.identifier(p)}`);if(o.op==="count")w.push(e`COUNT(*) AS ${e.identifier("value")}`);else{if(!o.field)throw new v("INTERNAL",`groupBy(${t}, { agg: { op: "${o.op}" } }): "field" is required for non-count reducers`);w.push(e`${e.raw(he(o.op))}(${W(o.field)}) AS ${e.identifier("value")}`)}const E=e.join(n.by.map(p=>W(p)),e`, `);let l=e`SELECT ${e.join(w,e`, `)} FROM ${e.identifier(t)}`;y&&(l=e`${l} WHERE ${y}`),l=e`${l} GROUP BY ${E}`;const S=await I(r,c,l);return Qn(i,n.by,S)},async insert(t,n,i){const o=f.tables[t];if(!o)throw new v("INTERNAL",`unknown table: ${t}`);await x(),Pe(c.name,n);const s=jn(o,n,re);Be(o,s);let u,h=!0;i?.clientId!==void 0?(_n(i.clientId),u=i.clientId):i?.allowExplicitId&&typeof s._id=="string"?u=s._id:(u=U(),h=!1);const _=i?.allowExplicitId&&typeof s._creationTime=="number"?s._creationTime:q(),y={...s,_creationTime:_,_id:u};K(t,"before","insert")&&await j("before","insert",{doc:{...y},id:u,op:"insert",table:t}),await ve(t),await Te(t);const{columns:w,values:E}=xt(o,u,_,s);return await Ut(t,e`INSERT INTO ${e.identifier(t)} (${te(w)}) VALUES (${oe(E)})`),h&&Me.set(u,t),await de(t,void 0,y),await Q(t,u,void 0,y),await ue(t,u,{document:y,written:{_creationTime:_,[ye]:null}}),await se(t,u,"insert",y),K(t,"after","insert")&&await j("after","insert",{doc:y,id:u,op:"insert",table:t}),u},normalizeId(t,n){return wn(f,t,n)},async patch(t,n,i){ot("patch",n),Pe(c.name,n);const o=await ae(t,i);if(!o)throw new v("INTERNAL",`document not found: ${t}`);const s=f.tables[o];if(!s)throw new v("INTERNAL",`document not found: ${t}`);const u=await me(o,t),h=ne(s,u);if(!h)throw new v("INTERNAL",`document not found: ${t}`);const _={...h,...fn(n),_id:t};Rt(s,n,_,re),Be(s,_,!0),K(o,"before","update")&&await j("before","update",{doc:{..._},id:t,op:"update",previous:h,table:o}),await ve(o),await Te(o);const y=Object.keys(s.shape),w=e.join(y.map(E=>e`${e.identifier(E)} = ${ee(s,E,_[E]??null)}`),e`, `);await be(o,"UPDATE",w,u),await de(o,h,_),await Q(o,t,h,_),await ue(o,t,{document:_,previous:h,written:De(u)}),await se(o,t,"update",_),K(o,"after","update")&&await j("after","update",{doc:_,id:t,op:"update",previous:h,table:o})},async restore(t,n){const i=await ae(t,n);if(!i)throw new v("INTERNAL",`document not found: ${t}`);const o=f.tables[i],s=o?.softDeleteMode?.field;if(!o||!s)throw new v("INTERNAL",`ctx.db.restore: table "${i}" is not a .softDelete() table`);const u=await me(i,t),h=u?.[s]!==null&&u?.[s]!==void 0;if(await P.patch(t,{[s]:null},n),h){const _=ne(o,u);_!==null&&await Q(i,t,void 0,{..._,[s]:null})}},query(t){const n=f.tables[t];if(!n)throw new v("INTERNAL",`unknown table: ${t}`);const i="the legacy query()/withIndex() reader is not available on the D1 (global) backend; use findMany",o=[];let s=0,u;const h=c.name!=="mysql",_={...m(n),fieldRef:l=>e`m.${W(l)}`},y=l=>o.every(S=>S(l)),w=async(l,S)=>{await x();const p=2*Zt+l.filters.length,T=u===void 0?void 0:J(u,_,void 0,p),b=T!==void 0&&c.name==="sqlite"&&Dn(c.name,T).params.length+p>Bn.boundParams,k=b?void 0:T,C=s>0||b,D=await en(r,c,n,t,k===void 0?l:{...l,scope:k},tn(C?void 0:S));!C&&S===void 0&&nn(D);const F=D.filter(X=>y(X));return S===void 0?F:F.slice(0,S)},E=l=>{const S={async*[Symbol.asyncIterator](){if(!l)throw new v("INTERNAL",i);yield*await w(l,void 0)},async collect(){if(!l)throw new v("INTERNAL",i);return w(l,void 0)},async collectWithScores(){throw new v("GLOBAL_SEARCH_SCORES_UNSUPPORTED",`collectWithScores() is not supported on \`.global()\` tables (table "${t}") — relevance scores are not yet surfaced on this backend; use .collect() instead`)},filter(p){o.push(p);const T=kn(p);return T!==void 0&&Mn(T,n.shape,h)?u=O(u,T):s+=1,S},async first(){if(!l)throw new v("INTERNAL",i);return(await w(l,1))[0]??null},order(){return S},async paginate(p){if(!l)throw new v("INTERNAL",i);const T=zt(p);return Jt(await w(l,on(T)),T)},async take(p){if(!l)throw new v("INTERNAL",i);return w(l,p)},async unique(){if(!l)throw new v("INTERNAL",i);const p=await w(l,2);if(p.length>1)throw new qn(`unique() on table "${t}" matched ${String(p.length)} documents; expected at most one`);return p[0]??null},withGeoIndex(){throw new v("INTERNAL",`geo indexes are not supported on \`.global()\` tables (table "${t}") — geospatial queries run only on sharded tables`)},withIndex(){throw new v("INTERNAL",i)},withSearchIndex(p,T){const b=(n.searchIndexes??[]).find(C=>C.name===p);if(!b)throw new v("INTERNAL",`unknown search index "${p}" on table "${t}"`);const k={definition:b,field:b.field,filters:[],hasQuery:!1,indexName:p,query:""};if(T(Xt(k,t,Vt(b.language))),!k.hasQuery)throw new v("INTERNAL",`search index "${p}" on table "${t}" requires a .search(field, query) call`);return E(k)}};return S};return E(void 0)},async rank(t,n,i){const o=f.tables[t];if(!o)throw new v("INTERNAL",`unknown table: ${t}`);const s=o.rankIndexes?.find(D=>D.name===n);if(!s)throw new v("INTERNAL",`unknown rankIndex "${n}" on table "${t}"`);if(i.restrictsCounts)throw new at(t);if(await x(),!await qe(t,s))return null;const h=typeof i.row=="string"?i.row:i.row._id;if(!h)return null;const _=$e(t,s.name),y=s.sortBy.map((D,F)=>pe(F)),w=te(["__partition__",...y]),l=(await I(r,c,e`SELECT ${w} FROM ${e.identifier(_)} WHERE ${e.identifier("__id__")} = ${h}`))[0];if(!l)return null;const S=l.__partition__,p=O(i.baseWhere,i.where);rt(p,f,t,"rank");const T=st(s,p);if(T&&Ce(s.partitionBy??[],T)!==S)return null;const b=zn(c.name,s,y,l,h),k=await I(r,c,e`SELECT COUNT(*) AS c FROM ${e.identifier(_)} WHERE ${e.identifier("__partition__")} = ${S} AND (${b})`),C=await I(r,c,e`SELECT COUNT(*) AS c FROM ${e.identifier(_)} WHERE ${e.identifier("__partition__")} = ${S}`);return{position:Number(k[0]?.c??0)+1,total:Number(C[0]?.c??0)}},async rankPage(t,n,i={}){rt(O(i.baseWhere,i.where),f,t,"rankPage");const o=f.tables[t];if(!o)throw new v("INTERNAL",`unknown table: ${t}`);const s=o.rankIndexes?.find(B=>B.name===n);if(!s)throw new v("INTERNAL",`unknown rankIndex "${n}" on table "${t}"`);if(await x(),!await qe(t,s))return{continueCursor:null,isDone:!0,page:[]};const h=$e(t,s.name),_=s.sortBy.map((B,we)=>pe(we)),y=Math.max(1,Math.min(1e3,Math.floor(i.take??100))),w=O(i.baseWhere,i.where),E=st(s,w),l=Zn(s,_),S=e.join(l.map(B=>e`${e.identifier(B.column)} ${e.raw(`${B.direction==="desc"?"DESC":"ASC"}${Lt(c,{direction:B.direction,nullable:B.nullable})}`)}`),e`, `),p=[];typeof i.partitionKey=="string"?p.push(e`${e.identifier("__partition__")} = ${i.partitionKey}`):E&&p.push(e`${e.identifier("__partition__")} = ${Ce(s.partitionBy??[],E)}`);const T=un(i);if(T!==void 0){const B=Jn(c.name,l,T);B!==void 0&&p.push(B)}const b=te([Re,"__partition__",..._]);let k=e`SELECT ${b} FROM ${e.identifier(h)}`;p.length>0&&(k=e`${k} WHERE ${e.join(p,e` AND `)}`),k=e`${k} ORDER BY ${S} LIMIT ${e.raw(String(y+1))}`;const C=await I(r,c,k),D=C.length>y,F=D?C.slice(0,y):C,X=F.map(B=>B[Re]),fe=await ei(r,c,o,t,X);let Ae=null;const z=F.at(-1);if(D&&z!==void 0){const B=[z.__partition__,..._.map(we=>z[we]),z[Re]];Ae=ti(B)}return{continueCursor:Ae,isDone:!D,page:fe}},async replace(t,n,i,o){ot("replace",n),Pe(c.name,n);const s=await ae(t,i);if(!s)throw new v("INTERNAL",`document not found: ${t}`);const u=f.tables[s];if(!u)throw new v("INTERNAL",`document not found: ${t}`);const h=await me(s,t);if(h===void 0)throw new v("INTERNAL",`document not found: ${t}`);const y=ln(f,s,"update")||(u.aggregateIndexes??[]).length>0||(u.rankIndexes??[]).length>0?ne(u,h)??void 0:void 0,w=o?.allowExplicitId&&typeof n._creationTime=="number"?n._creationTime:void 0,E=typeof h._creationTime=="number"?h._creationTime:void 0,l=w??E??q(),S={...n,_creationTime:l,_id:t};Rt(u,n,S,re),Be(u,S),K(s,"before","update")&&await j("before","update",{doc:{...S},id:t,op:"update",previous:y,table:s}),await ve(s),await Te(s);const p=Object.keys(u.shape),T=e.join([e`${e.identifier("_creationTime")} = ${l}`,...p.map(b=>e`${e.identifier(b)} = ${ee(u,b,S[b]??null)}`)],e`, `);await be(s,"UPDATE",T,h),await de(s,y,S),await Q(s,t,y,S),await ue(s,t,{document:S,previous:y,written:De(h,l)}),await se(s,t,"update",S),K(s,"after","update")&&await j("after","update",{doc:S,id:t,op:"update",previous:y,table:s})}};return Xe={db:P,scheduler:Dt},P};export{Ci as backfillSqlSearchIndexes,bi as createSqlCtxDb,ki as decodeGlobalRow,fi as readSqlCdcChangedTables,mi as readSqlCdcChanges,qt as readSqlCdcFloor,si as runSqlCdcMigration,Qt as runSqlSearchMigrations,ci as sweepSqlCdcRetention};
@@ -1 +0,0 @@
1
- import{LunoraError as h}from"@lunora/errors";import{D as I,c as vt,m as Dt,r as U,a as qt,l as lt,w as F,b as Ft}from"./ctx-db-search-state-CYA6goYx.mjs";import{ftsUnmappedPage as Ut,groupUnmappedRows as bt,ftsWriteDocument as mt,ftsPurgeDocument as pt,ftsCompanionDdl as kt,unionAll as Ht,ftsRowidMapName as Pt}from"@lunora/shard-engine";import{sql as i}from"drizzle-orm";import{queryAll as p,OCC_VERSION_COLUMN as Bt,nullSafeEqualsSql as Wt,decodeRow as V,runInOrder as O,indexState as L,createIndexIfNotExists as X,dropIndexIfExists as M,queryRun as T,decodeRows as ht,columnRefSql as rt,serializeColumnValue as jt,readRowsPage as xt,forEachRowPaged as Kt}from"./decodeGlobalRow-B-3UbPME.mjs";const Gt=n=>{const t=atob(n),e=Uint8Array.from(t,r=>r.codePointAt(0)??0);return new TextDecoder().decode(e)},Yt=()=>new h("BAD_REQUEST","invalid cursor"),ot=16,st=8,y=1024,b=(n,t)=>t.query(n),Qt=(n,t)=>{if(!t)return{exact:!0,lower:n,upper:n};const e=[...n].at(-1)??"",r=(e.codePointAt(0)??0)+1;if(r>=55296&&r<=57343||r>1114111)return{exact:!0,lower:n,upper:n};const o=n.slice(0,n.length-e.length);return{exact:!1,lower:n,upper:o+String.fromCodePoint(r)}},Cn=(n,t,e)=>{const r={eq:(o,s)=>{if(!n.definition.filterFields?.includes(o))throw new h("INTERNAL",`field "${o}" is not a filter field of search index "${n.indexName}" on table "${t}"`);if(n.filters.length>=st)throw new h("BAD_REQUEST",`search index "${n.indexName}" on table "${t}": at most ${String(st)} .eq() filters are supported per search query`);return n.filters.push({field:o,value:s}),r},search:(o,s)=>{const a=n;if(o!==a.definition.field)throw new h("INTERNAL",`search index "${a.indexName}" on table "${t}" indexes "${a.definition.field}", not "${o}"`);const f=b(s,e).length;if(f>ot)throw new h("BAD_REQUEST",`search index "${a.indexName}" on table "${t}": at most ${String(ot)} search terms are supported (got ${String(f)})`);return a.field=o,a.query=s,a.hasQuery=!0,r}};return r},Ln=n=>{if(n.length>y)throw new h("BAD_REQUEST",`more than ${String(y)} documents match this search — narrow it with filters or read it a page at a time with .paginate()`)},Mn=n=>Math.min(n.offset+n.numItems+1,y),Vt=n=>btoa(`search:${String(n)}`),Xt=n=>{let t;try{t=Gt(n)}catch{return}if(!t.startsWith("search:"))return;const e=Number(t.slice(7));return Number.isInteger(e)&&e>=0?e:void 0},vn=n=>{if(typeof n.endCursor=="string")throw new h("BAD_REQUEST","bounded (endCursor) pagination is not supported on search queries — relevance order has no stable range boundary");if(!Number.isFinite(n.numItems))throw new h("BAD_REQUEST",`search pagination needs a finite numItems, got ${String(n.numItems)}`);const t=Math.max(0,Math.floor(n.numItems)),e=n.cursor?Xt(n.cursor):0;if(e===void 0)throw Yt();if(e+t>=y)throw new h("BAD_REQUEST",`search pagination reaches the ${String(y)}-document limit (offset ${String(e)} + ${String(t)} requested) — a page must end below the cap so the probe row that answers \`hasMore\` still fits: retry with numItems ${String(Math.max(1,y-e-1))} or fewer, or narrow the query or the filters instead`);return{numItems:t,offset:e}},Dn=(n,t)=>{const e=t.offset+t.numItems,r=t.numItems>0&&n.length>e;return{continueCursor:r?Vt(e):null,isDone:!r,page:n.slice(t.offset,e)}},qn=n=>{if(n===void 0)return y+1;if(!Number.isFinite(n))return y;const t=Math.max(0,Math.floor(n));if(t>y)throw new h("BAD_REQUEST",`search returns at most ${String(y)} documents (asked for ${String(t)}) — narrow the query or paginate instead`);return t},Jt=(n,t)=>{if(!t.includes("."))return n[t];let e=n;for(const r of t.split(".")){if(e===null||typeof e!="object"||Array.isArray(e))return;e=e[r]}return e},k=(n,t)=>`${n}__fts_${t}`,S="__id__",g="__token__",A="__n__",zt=1e3,gt=(n,t)=>t.document(n),x=(n,t)=>Jt(n,t),Zt=n=>typeof n=="string"?n:n==null?"":typeof n=="number"||typeof n=="bigint"||typeof n=="boolean"?String(n):JSON.stringify(n)??"",tn=(n,t,e)=>n!==void 0&&t!==void 0&&x(n,e.field)===x(t,e.field),nn=(n,t)=>gt(Zt(x(n,t.field)),I(t.language)).slice(0,zt),q=(n,t)=>nn(n,t).join(" "),Tt=(n,t)=>{const e=new Map;for(const r of gt(n,t))e.set(r,(e.get(r)??0)+1);return e},yt=[Bt,"_creationTime"],at=50,$=(n,t)=>i`${i.identifier(n)}.${i.identifier(t)}`,v=(n,t,e)=>i`DELETE FROM ${i.identifier(n)} WHERE ${$(n,S)} = ${t}${e===void 0?i``:i` AND ${e}`}`,Rt=(n,{row:t,table:e})=>{const r=yt.map(o=>i` AND ${Wt(n.name,$(e,o),t[o]??null)}`);return i`SELECT 1 FROM ${i.identifier(e)} WHERE ${$(e,"id")} = ${t.id}${i.join(r)}${n.name==="postgres"?i` FOR SHARE`:i``}`},D=(n,t)=>i`EXISTS (${Rt(n,t)})`,H=(n,t)=>i`NOT EXISTS (SELECT 1 FROM ${i.identifier(n)} WHERE ${$(n,"id")} = ${t})`,_t=(n,t,e,r)=>r===void 0?H(t,e):D(n,{row:r,table:t}),J=async(n,t,e,r)=>{const o=new Map;for(let s=0;s<r.length;s+=at){const a=r.slice(s,s+at),f=await p(n,t,i`SELECT * FROM ${i.identifier(e)} WHERE ${$(e,"id")} IN (${i.join(a.map(u=>i`${u}`),i`, `)})`);for(const u of f)o.set(String(u.id),u)}return o},K=(n,t,e)=>n.name==="mysql"?i`${i.identifier(t)}(${i.raw(String(e))})`:n.textPatternOperatorClass===void 0?i`${i.identifier(t)}`:i`${i.identifier(t)} ${i.raw(n.textPatternOperatorClass)}`,z=512,Z=(n,t)=>({columns:i`${K(n,g,256)}, ${K(n,S,z)}`,concurrently:!0,name:`${t}__unique`,table:t,unique:!0}),W=n=>i`${n} COLLATE utf8mb4_0900_bin`,j=(n,t)=>{const e=JSON.stringify(t.map(([r,o])=>({o,t:r})));switch(n.name){case"mysql":return i`JSON_TABLE(${e}, '$[*]' COLUMNS (${i.identifier("t")} VARCHAR(256) PATH '$.t', ${i.identifier("o")} INT PATH '$.o')) AS ${i.identifier("j")}`;case"postgres":return i`json_to_recordset(${e}::json) AS ${i.identifier("j")}(${i.identifier("t")} text, ${i.identifier("o")} integer)`;default:return i`(SELECT json_extract(${$("e","value")}, '$.t') AS ${i.identifier("t")}, json_extract(${$("e","value")}, '$.o') AS ${i.identifier("o")} FROM json_each(${e}) AS ${i.identifier("e")}) AS ${i.identifier("j")}`}},Nt=(n,t,e,r,o)=>{const s=i.join([g,S,A].map(f=>i.identifier(f)),i`, `),a=f=>i`INSERT INTO ${i.identifier(t)} (${s}) SELECT ${$("j","t")}, ${e}, ${$("j","o")} FROM ${j(n,r)} WHERE ${f}`;switch(n.name){case"mysql":{const f=D(n,o),u=$(t,g),c=i.identifier("held");return[i`${a(i`${f} AND NOT EXISTS (SELECT 1 FROM ${i.identifier(t)} AS ${c} WHERE ${$("held",S)} = ${e} AND ${$("held",g)} = ${W($("j","t"))})`)} ON DUPLICATE KEY UPDATE ${$(t,A)} = ${$(t,A)}`,i`UPDATE ${i.identifier(t)} JOIN ${j(n,r)} ON ${u} = ${W($("j","t"))} SET ${$(t,A)} = ${$("j","o")} WHERE ${$(t,S)} = ${e} AND ${f}`,i`DELETE FROM ${i.identifier(t)} WHERE ${$(t,S)} = ${e} AND ${f} AND ${u} NOT IN (SELECT ${W($("j","t"))} FROM ${j(n,r)})`]}case"postgres":{const f=i.identifier("__source__"),u=i.identifier("__purged__"),c=i`EXISTS (SELECT 1 FROM ${f})`;return[i`WITH ${f} AS (${Rt(n,o)}), ${u} AS (${v(t,e,c)} RETURNING 1) ${a(i`${c} AND (SELECT COUNT(*) FROM ${u}) >= 0`)} ON CONFLICT DO NOTHING`]}default:{const f=D(n,o);return[v(t,e,f),i`${a(f)} ON CONFLICT DO NOTHING`]}}},At={uniqueKeyMissing:!1,unmappedSkipped:0},ft=new Set,ut=(n,t)=>{ft.has(n)||(ft.add(n),console.warn(`[@lunora/sql-store] ${t}`))},ct=100,dt=3,G=async(n,t,e,r=0)=>{const{companion:o,definition:s,index:a,tableName:f}=e,u={done:!0,last:r,left:0};if((await p(n,t,t.tableExists(f))).length===0)return u;const d=await p(n,t,Ut(o,ct,r));if(d.length===0)return u;const w=Math.max(...d.map(_=>Number(_.rowid))),E=bt(d),l=await J(n,t,f,[...E.keys()]),m=[];for(const[_,nt]of E){const B=l.get(_),et=B===void 0?void 0:V(s,B),it=_t(t,f,_,B);m.push(...et?mt(o,_,q(et,a),{guard:it,unmappedRowids:nt}):pt(o,_,{guard:it,unmappedRowids:nt}))}await O(n,t,m);const R=await p(n,t,i`SELECT COUNT(*) AS ${i.identifier("n")} FROM ${i.identifier(o)} WHERE ${$(o,"rowid")} > ${Math.max(0,r)} AND ${$(o,"rowid")} <= ${w}`);return{done:d.length<ct,last:w,left:Number(R[0]?.n??0)}},en=async(n,t,e)=>{let r=0,o=0;for(;;){let s=await G(n,t,e,r);for(let a=1;s.left>0&&a<dt;a+=1)s=await G(n,t,e,r);if(s.left>0&&(o+=s.left,console.warn(`[@lunora/sql-store] search migration of "${e.companion}": ${String(s.left)} row(s) lost every one of ${String(dt)} attempts to a concurrent write and were left for a later pass.`)),s.done)return o;r=s.last}},rn=async(n,t,e,r)=>r==="step"?(await G(n,t,e),At):{uniqueKeyMissing:!1,unmappedSkipped:await en(n,t,e)},Y=n=>`${n}__btree`,on=async(n,t,e)=>{const r=Z(t,e),o=await L(n,t,e,r.name);if(o!=="valid"){if(o==="absent"&&(await p(n,t,i`SELECT 1 AS ${i.identifier("present")} FROM ${i.identifier(e)} LIMIT 1`)).length===0)try{await X(n,t,{...r,concurrently:!1}),await M(n,t,e,Y(e));return}catch(a){if(await L(n,t,e,r.name).catch(()=>{})==="valid")return;ut(`${e}:empty`,`search companion "${e}": adding its unique key failed (${String(a)}); run backfillSqlSearchIndexes.`);return}ut(`${e}:missing`,`search companion "${e}" has no usable unique key, so two backfills racing over one row can index it twice. Run backfillSqlSearchIndexes, which adds it.`)}},sn=async(n,t,e)=>{const r=$(e,S),o=$(e,g);return(await p(n,t,i`SELECT ${r} AS ${i.identifier("id")}, ${o} AS ${i.identifier("token")}, COUNT(*) AS ${i.identifier("n")} FROM ${i.identifier(e)} GROUP BY ${r}, ${o} HAVING COUNT(*) > 1`)).map(a=>({count:Number(a.n),id:String(a.id),token:String(a.token)}))},an=(n,t,{count:e,id:r,token:o})=>{const s=i`${$(t,S)} = ${r} AND ${$(t,g)} = ${o}`;switch(n.name){case"mysql":return i`DELETE FROM ${i.identifier(t)} WHERE ${s} LIMIT ${i.raw(String(e-1))}`;case"postgres":return i`DELETE FROM ${i.identifier(t)} WHERE ctid = ANY (ARRAY(SELECT ctid FROM ${i.identifier(t)} WHERE ${s} OFFSET 1))`;default:return i`DELETE FROM ${i.identifier(t)} WHERE rowid IN (SELECT rowid FROM ${i.identifier(t)} WHERE ${s} LIMIT -1 OFFSET 1)`}},fn=async(n,t,e,r)=>{const{companion:o,definition:s,index:a,tableName:f}=e;for(const d of r)await T(n,t,an(t,o,d));const u=[...new Set(r.map(d=>d.id))],c=await J(n,t,f,u);for(const d of u){const w=c.get(d),E=w===void 0?void 0:V(s,w);if(w!==void 0&&E){const l=[...Tt(q(E,a),I(a.language))];await O(n,t,Nt(t,o,d,l,{row:w,table:f}));continue}await T(n,t,v(o,d,_t(t,f,d,w)))}},un=async(n,t,e)=>t.name!=="mysql"?"":(await p(n,t,i`SELECT COLUMN_NAME AS ${i.identifier("name")}, COLLATION_NAME AS ${i.identifier("collation")} FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = ${e} AND COLUMN_NAME IN (${g}, ${S})`)).filter(o=>!String(o.collation).endsWith("_bin")).map(o=>` Its ${String(o.name)} column uses ${String(o.collation)}, which treats values differing only in case or accents as one; convert it with ALTER TABLE \`${e}\` MODIFY \`${String(o.name)}\` VARCHAR(768) COLLATE utf8mb4_0900_bin NOT NULL.`).join(""),cn=async(n,t,e)=>{if(t.name!=="mysql")return 0;const r=$(e,S),o=await p(n,t,i`SELECT COUNT(*) AS ${i.identifier("n")} FROM (SELECT 1 AS ${i.identifier("x")} FROM ${i.identifier(e)} GROUP BY LEFT(${r}, ${i.raw(String(z))}), ${$(e,g)} HAVING COUNT(DISTINCT ${r}) > 1) AS ${i.identifier("shared")}`);return Number(o[0]?.n??0)},dn=async(n,t,e)=>{const r=Z(t,e),o=await L(n,t,e,r.name);if(o==="valid")return"valid";o==="invalid"&&await M(n,t,e,r.name);try{return await X(n,t,r),"built"}catch(s){const a=await L(n,t,e,r.name);if(a==="valid")return"valid";if(a==="invalid"&&await M(n,t,e,r.name),!t.isUniqueViolation(s))throw s;return"refused"}},$t=3,$n=async(n,t,e)=>{const{companion:r}=e,o=await un(n,t,r);let s="";o!==""&&console.warn(`[@lunora/sql-store] search companion "${r}" folds case and accents in its unique key.${o}`);for(let a=0;a<$t;a+=1){const f=await dn(n,t,r);if(f==="valid")return await M(n,t,r,Y(r)),!0;if(f==="refused"){const u=await sn(n,t,r);if(u.length>0)await fn(n,t,e,u);else{const c=await cn(n,t,r);s=c>0?` ${String(c)} token(s) are held by document ids that agree on their first ${String(z)} characters, which the key cannot tell apart; shorten those ids.`:o}}}return await L(n,t,r,Z(t,r).name)==="valid"?(await M(n,t,r,Y(r)),!0):(console.warn(`[@lunora/sql-store] search companion "${r}" still has no unique key after ${String($t)} attempts: writes keep working, but two backfills racing over one row can index it twice.${s}`),!1)},wn=async(n,t,e,r)=>r==="step"?At:{uniqueKeyMissing:!await $n(n,t,e),unmappedSkipped:0},Sn=(n,t)=>t?i`${i.identifier(g)} LIKE ${`${n}%`}`:i`${i.identifier(g)} = ${n}`,Ot=(n,t)=>{const e=t.filters.map(r=>i`m.${rt(r.field)} = ${jt(r.value)}`);return n.softDeleteMode&&e.push(i`m.${rt(n.softDeleteMode.field)} IS NULL`),t.scope!==void 0&&e.push(i`(${t.scope})`),e},It=async(n,t,e,r,o,s,a)=>{const f=Ot(e,o);let u=i`SELECT m.* FROM (${a}) s JOIN ${i.identifier(r)} m ON m.${i.identifier("id")} = s.${i.identifier(S)}`;return f.length>0&&(u=i`${u} WHERE ${i.join(f,i` AND `)}`),u=i`${u} ORDER BY s.${i.identifier("__score__")} DESC, m.${i.identifier("_creationTime")} DESC, m.${i.identifier("id")} ASC LIMIT ${i.raw(String(s))}`,ht(e,await p(n,t,u))},En=async(n,t,e,r,o,s)=>{const a=b(o.query,I(o.definition.language));if(a.length===0)return[];const f=k(r,o.indexName),u=a.length-1,c=a.map((m,R)=>Sn(m,R===u)),d=i.join(c,i` OR `),w=c.map(m=>i`SUM(CASE WHEN ${m} THEN ${i.identifier(A)} ELSE 0 END)`),E=i.join(w.map(m=>i`${m} > 0`),i` AND `),l=i`SELECT ${i.identifier(S)}, ${i.join(w,i` + `)} AS ${i.identifier("__score__")} FROM ${i.identifier(f)} WHERE ${d} GROUP BY ${i.identifier(S)} HAVING ${E}`;return It(n,t,e,r,o,s,l)},ln=async(n,t,e,r,o,s)=>{const a=b(o.query,I(o.definition.language));if(a.length===0)return[];const f=k(r,o.indexName),u=`${f}__vocab`,c=a.length-1,d=a.map((l,m)=>{const R=Qt(l,m===c),_=R.exact?i`${i.identifier("term")} = ${R.lower}`:i`${i.identifier("term")} >= ${R.lower} AND ${i.identifier("term")} < ${R.upper}`;return i`SELECT ${i.identifier("doc")}, ${i.raw(String(m))} AS ${i.identifier("__term__")}, COUNT(*) AS ${i.identifier("__n__")} FROM ${i.identifier(u)} WHERE ${_} GROUP BY ${i.identifier("doc")}`}),w=a.map((l,m)=>i`SUM(CASE WHEN u.${i.identifier("__term__")} = ${i.raw(String(m))} THEN u.${i.identifier("__n__")} ELSE 0 END)`),E=i`SELECT f.${i.identifier(S)} AS ${i.identifier(S)}, ${i.join(w,i` + `)} AS ${i.identifier("__score__")} FROM (${Ht(d)}) u JOIN ${i.identifier(f)} f ON f.rowid = u.${i.identifier("doc")} GROUP BY f.${i.identifier(S)} HAVING ${i.join(w.map(l=>i`${l} > 0`),i` AND `)}`;return It(n,t,e,r,o,s,E)},mn=async(n,t,e,r,o,s)=>{const a=t.nativeTextSearch,f=b(o.query,I(o.definition.language));if(!a||f.length===0)return[];const u=k(r,o.indexName),c=[a.matches(u,f),...Ot(e,o)],d=i`SELECT m.* FROM ${i.identifier(u)} JOIN ${i.identifier(r)} m ON m.${i.identifier("id")} = ${i.identifier(u)}.${i.identifier(S)} WHERE ${i.join(c,i` AND `)} ORDER BY ${a.rank(u,f)} DESC, m.${i.identifier("_creationTime")} DESC, m.${i.identifier("id")} ASC LIMIT ${i.raw(String(s))}`;return ht(e,await p(n,t,d))},pn={ensureCompanion:async(n,t,e)=>{const{integer:r,key:o}=t.companionTypes;await T(n,t,i`CREATE TABLE IF NOT EXISTS ${i.identifier(e)} (${i.identifier(g)} ${i.raw(o)} NOT NULL, ${i.identifier(S)} ${i.raw(o)} NOT NULL, ${i.identifier(A)} ${i.raw(r)} NOT NULL)`),await X(n,t,{columns:K(t,S,191),name:`${e}__by_id`,table:e,unique:!1}),await on(n,t,e)},indexDocument:async(n,t,e,r,o,s)=>{const a=[...Tt(q(r,o),I(o.language))];await O(n,t,Nt(t,e,String(s.row.id),a,s))},migrate:wn,name:"inverted",purgeDocument:async(n,t,e,r,o)=>{await T(n,t,v(e,r,H(o,r)))},runSearch:En},hn={ensureCompanion:async(n,t,e)=>{await O(n,t,kt(e))},indexDocument:async(n,t,e,r,o,s)=>{await O(n,t,mt(e,String(s.row.id),q(r,o),{guard:D(t,s)}))},migrate:rn,name:"fts5",purgeDocument:async(n,t,e,r,o)=>{await O(n,t,pt(e,r,{guard:H(o,r)}))},runSearch:ln},gn={ensureCompanion:async(n,t,e)=>{const r=t.nativeTextSearch;if(r){await T(n,t,r.createCompanion(e,t.companionTypes.key));for(const o of r.createIndexes(e))await T(n,t,o)}},indexDocument:async(n,t,e,r,o,s)=>{const a=t.nativeTextSearch;a&&await T(n,t,a.indexDocument(e,String(s.row.id),q(r,o),D(t,s)))},name:"native",purgeDocument:async(n,t,e,r,o)=>{await T(n,t,v(e,r,H(o,r)))},runSearch:mn},C=(n,t)=>n.strategy==="native"&&t.nativeTextSearch!==void 0?gn:t.supportsFts5?hn:pn,P=(n,t)=>`${vt(n)}/${C(n,t).name}`,tt=function*(n){for(const[t,e]of Object.entries(n.tables)){const r=e.searchIndexes;if(!(e.shardMode!==void 0&&e.shardMode.kind!=="global"||!r))for(const o of r)yield[t,e,o]}},N=(n,t)=>k(n,t.name),Tn=async(n,t,e,r)=>{const o=N(e,r);return lt(await U(n,t,o),P(r,t)).finished?!0:Ft(n,t,o)},Fn=async(n,t,e,r,o,s)=>{if(!await Tn(n,t,r,o.definition))throw new h("SEARCH_INDEX_BUILDING",`search index "${o.indexName}" on table "${r}" is still backfilling and currently covers only part of the table — retry once it finishes${t.searchBackfillHint===void 0?"":`, or complete it now: ${t.searchBackfillHint}`}`);return C(o.definition,t).runSearch(n,t,e,r,o,s)},wt=n=>n.slice(n.lastIndexOf("/")+1),St=200,Et=5,yn=async(n,t,e,r)=>{const o=[...r.keys()],s=await p(n,t,i`SELECT * FROM ${i.identifier(e)} WHERE ${i.identifier("id")} >= ${o[0]} AND ${i.identifier("id")} <= ${o.at(-1)} ORDER BY ${i.identifier("id")} ASC`),a=new Map;for(const f of s){const u=f.id,c=r.get(u)?.row;c&&yt.some(d=>String(c[d])!==String(f[d]))&&a.set(u,{row:f})}return a},Q=async(n,t,e,r,o,s,a)=>{const f=C(s,t);let u=a;for(let c=0;u.size>0&&c<Et;c+=1){for(const{document:d,row:w}of u.values()){const E=d??V(e,w);E&&await f.indexDocument(n,t,o,E,s,{row:w,table:r})}u=await yn(n,t,r,u)}u.size>0&&console.warn(`[@lunora/sql-store] search index "${o}": ${String(u.size)} row(s) were rewritten on every one of ${String(Et)} re-check rounds and were left to their writers — each stays stale only if none of those writes changed its indexed text.`)},Ct=async(n,t,e,r,o)=>{const s=N(r,o),a=P(o,t),f=lt(await U(n,t,s),a);if(f.finished)return!0;if(f.wipe&&await F(n,t,s,void 0,!1,a),(await p(n,t,t.tableExists(r))).length===0)return await F(n,t,s,void 0,!0,a),!0;const c=await xt(n,t,r,f.cursor,St),d=c.findLast(l=>typeof l.id=="string")?.id,w=new Map;for(const l of c)typeof l.id=="string"&&w.set(l.id,{row:l});await Q(n,t,e,r,s,o,w);const E=c.length<St;return await F(n,t,s,d??f.cursor,E,a),E},Lt=async(n,t,e)=>{await Dt(n,e);for(const[r,,o]of tt(t)){const s=N(r,o),a=P(o,e),f=await U(n,e,s);f.profile!==void 0&&wt(f.profile)!==wt(a)&&(await T(n,e,i`DROP TABLE IF EXISTS ${i.identifier(s)}`),await T(n,e,i`DROP TABLE IF EXISTS ${i.identifier(Pt(s))}`),await qt(n,e,s)),await C(o,e).ensureCompanion(n,e,s)}},Rn=async(n,t,e,r,o)=>{const s=N(r,o);if((await U(n,t,s)).profile!==void 0)return;let f=0;(await p(n,t,t.tableExists(r))).length>0&&await Kt(n,t,e,r,()=>{f+=1},{limit:1}),await F(n,t,s,void 0,f===0,P(o,t))},Mt=async(n,t,e,r,o,s)=>await C(o,t).migrate?.(n,t,{companion:N(r,o),definition:e,index:o,tableName:r},s)??{uniqueKeyMissing:!1,unmappedSkipped:0},Un=async(n,t,e)=>{await Lt(n,t,e);for(const[r,o,s]of tt(t)){if(await Mt(n,e,o,r,s,"step"),s.staged){await Rn(n,e,o,r,s);continue}await Ct(n,e,o,r,s)}},bn=async(n,t,e)=>{const r={uniqueKeyMissing:[],unmappedSkipped:0};await Lt(n,t,e);for(const[o,s,a]of tt(t)){const f=await Mt(n,e,s,o,a,"drain");r.unmappedSkipped+=f.unmappedSkipped,f.uniqueKeyMissing&&r.uniqueKeyMissing.push(N(o,a));let u=!1;for(;!u;)u=await Ct(n,e,s,o,a)}return r},kn=n=>{const{dialect:t,exec:e,schema:r}=n;return async(o,s,a)=>{const f=r.tables[o],u=f?.searchIndexes;if(!(!f||!u||u.length===0))for(const c of u){if(tn(a?.previous,a?.document,c))continue;const d=N(o,c);if(a){await Q(e,t,f,o,d,c,new Map([[s,{document:a.document,row:{...a.written,id:s}}]]));continue}await C(c,t).purgeDocument(e,t,d,s,o);const E=(await J(e,t,o,[s])).get(s);E!==void 0&&await Q(e,t,f,o,d,c,new Map([[s,{row:E}]]))}}};export{Cn as A,vn as C,Dn as R,qn as T,Fn as a,Mn as b,kn as c,bn as d,Un as r,ot as u,Ln as x};
@@ -1 +0,0 @@
1
- import{renderSql as m}from"@lunora/shard-engine";import{sql as r}from"drizzle-orm";import{s as I,c as y,a as N}from"./value-codec-CBr0_L1N.mjs";const v="_version",C=n=>n==="_id"||n==="id"?"id":n,L=n=>r`${r.identifier(C(n))}`,F=(n,t)=>r`${r.identifier(n)}.${r.identifier(C(t))}`,K=n=>{const t=[];for(const[e,o]of Object.entries(n.shape)){const s=o._meta?.column;s&&t.push([e,s])}return t},d=(n,t,e)=>{const{params:o,sql:s}=m(t.name,e);return n.all(s,o)},u=(n,t,e)=>{const{params:o,sql:s}=m(t.name,e);return n.run(s,o)},k=(n,t,e)=>n==="postgres"?r`${t} IS NOT DISTINCT FROM ${e}`:n==="mysql"?r`${t} <=> ${e}`:r`${t} IS ${e}`,B={sql:n=>r`COALESCE(${n}, 0) + 1`,value:n=>(n==null?0:Number(n))+1},P=async(n,t,e)=>{if(e.length!==0){if(!n.batch){for(const o of e)await u(n,t,o);return}await n.batch(e.map(o=>m(t.name,o)))}},W=async(n,t,e)=>{if(n.batch&&t.name==="sqlite"){await n.batch(e.map(o=>m(t.name,o)));return}for(const o of e)await u(n,t,o)},X=async(n,t,e)=>{const o=e.unique?r`UNIQUE `:r``,s=e.concurrently===!0&&t.name==="postgres"?r`CONCURRENTLY `:r``;if(t.name==="mysql"){try{await u(n,t,r`CREATE ${o}INDEX ${r.identifier(e.name)} ON ${r.identifier(e.table)} (${e.columns})`)}catch(i){const a=i;if(a.errno!==1061&&a.code!=="ER_DUP_KEYNAME"&&a.code!==1061)throw i}return}await u(n,t,r`CREATE ${o}INDEX ${s}IF NOT EXISTS ${r.identifier(e.name)} ON ${r.identifier(e.table)} (${e.columns})`)},x=async(n,t,e,o)=>{if(t.name!=="mysql"){await u(n,t,r`DROP INDEX IF EXISTS ${r.identifier(o)}`);return}try{await u(n,t,r`DROP INDEX ${r.identifier(o)} ON ${r.identifier(e)}`)}catch(s){const i=s;if(i.errno!==1091&&i.code!=="ER_CANT_DROP_FIELD_OR_KEY"&&i.code!==1091)throw s}},H=async(n,t,e,o)=>{switch(t.name){case"mysql":return(await d(n,t,r`SELECT 1 AS ${r.identifier("present")} FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = ${e} AND index_name = ${o} LIMIT 1`)).length>0?"valid":"absent";case"postgres":{const s=await d(n,t,r`SELECT indisvalid FROM pg_index WHERE indexrelid = to_regclass(quote_ident(${o}))`);return s.length===0?"absent":s[0]?.indisvalid===!0?"valid":"invalid"}default:return(await d(n,t,r`SELECT 1 AS ${r.identifier("present")} FROM sqlite_master WHERE type = 'index' AND name = ${o}`)).length>0?"valid":"absent"}},U=I,f=n=>{if(n===void 0)return!1;if(n._meta?.column?.notNull===!1)return!0;switch(n.kind){case"any":case"null":return!0;case"literal":return n._meta?.value===null;case"optional":return f(n._meta?.inner);case"union":return n._meta?.members?.some(t=>f(t))===!0;default:return!1}},q=n=>n.kind==="optional"&&!f(n),R=new WeakMap,S=n=>{let t=R.get(n);return t===void 0&&(t=Object.entries(n.shape).map(([e,o])=>[e,N(o),q(o)]),R.set(n,t)),t},h=new WeakMap,g=(n,t)=>{let e=h.get(n);return e===void 0&&(e=new Map(S(n).map(([o,s])=>[o,s])),h.set(n,e)),e.get(t)},Y=(n,t,e)=>I(e,g(n,t)),T=(n,t)=>{const e={};for(const[o,s,i]of S(n)){const a=t[o];a===void 0||i&&a===null||(e[o]=y(a,s))}return e._id=t.id,e._creationTime=t._creationTime,e},O=(n,t)=>t?T(n,t):null,z=(n,t)=>{const e=[];for(const o of t){const s=O(n,o);s&&e.push(s)}return e},_=500,b=(n,t,e,o,s)=>{const i=r`${r.identifier(e)}.${r.identifier("id")}`,a=o===void 0?r``:r` WHERE ${i} > ${o}`;return d(n,t,r`SELECT * FROM ${r.identifier(e)}${a} ORDER BY ${i} ASC LIMIT ${r.raw(String(s))}`)},V=async(n,t,e,o,s,i={})=>{let a=i.after,c=i.limit,E=!0;for(;E&&(c===void 0||c>0);){const $=c===void 0?_:Math.min(_,c),l=await b(n,t,o,a,$);for(const p of l){const w=O(e,p);w&&await s(w)}a=l.at(-1)?.id,E=l.length===$,c=c===void 0?void 0:c-l.length}};export{_ as BACKFILL_BATCH_SIZE,v as OCC_VERSION_COLUMN,L as columnRefSql,X as createIndexIfNotExists,T as decodeGlobalRow,O as decodeRow,z as decodeRows,x as dropIndexIfExists,V as forEachRowPaged,H as indexState,B as nextRowVersion,k as nullSafeEqualsSql,C as physicalColumn,F as qualifiedColumnRefSql,d as queryAll,P as queryBatch,u as queryRun,b as readRowsPage,W as runInOrder,U as serializeColumnValue,Y as serializeDocumentColumn,K as tableColumns};
@@ -1 +0,0 @@
1
- import{LunoraError as O}from"@lunora/errors";import{aggregateTableName as B,rankTableName as P,sortColumnName as I,renderSql as j,bigintSqlKey as F}from"@lunora/shard-engine";import{sql as n}from"drizzle-orm";import{e as K,a as p,i as Y,B as L,r as G,U as H,b as V}from"./value-codec-CBr0_L1N.mjs";import{queryRun as d,queryAll as h,createIndexIfNotExists as E,OCC_VERSION_COLUMN as w,tableColumns as W,qualifiedColumnRefSql as X,columnRefSql as m,queryBatch as Q}from"./decodeGlobalRow-B-3UbPME.mjs";const z=new Set(["any","null"]),C=(t,e)=>{const i=e.kindOf(t);return i!==void 0&&z.has(i)||e.nullableOf(t)?!0:i==="union"&&(e.membersOf(t)??[]).some(o=>C(o,e))},y=t=>{const e=t._meta?.inner;return t.kind==="optional"&&e?y(e):t},q=t=>C(t,{kindOf:K,membersOf:e=>y(e)._meta?.members,nullableOf:e=>y(e)._meta?.column?.notNull===!1}),v=(t,e,i=!1)=>e.columnType(p(t),{unique:i||t._meta?.column?.unique===!0}),x=t=>{const e=new Set;for(const i of t.indexes)i.unique===!0&&i.fields.length===1&&e.add(i.fields[0]);return e},J=(t,e,i)=>{if(i.indexKeyPrefix!==void 0)for(const o of e.indexes){if(o.unique!==!0||o.fields.length<=1)continue;const r=o.fields.filter(s=>{const u=e.shape[s];return u!==void 0&&i.indexKeyPrefix?.(p(u))!==void 0});if(r.length>0)throw new Error(`${t}.${o.name}: a composite unique index cannot cover ${r.join(", ")} in full on this engine (its key limit forces a prefix, which would constrain the prefix rather than the value). Split it into single-column unique indexes, or store those fields as a bounded type.`)}},Z=t=>t._meta?.column?.notNull===!0&&t.kind!=="optional"&&!q(t),nn=(t,e,i)=>{const o=[],r=x(e);for(const[l,c]of Object.entries(e.shape)){if(!c._meta?.column)continue;const f=Z(c)?" NOT NULL":"";o.push(n`${n.identifier(l)} ${n.raw(`${v(c,i,r.has(l))}${f}`)}`)}const s=[...i.frameworkColumns().map(l=>n`${n.identifier(l.name)} ${n.raw(l.type)}`),n`${n.identifier(w)} ${n.raw(i.companionTypes.integer)}`],u=s.length+o.length,a=i.maxTableColumns;if(a!==void 0&&u>a){const l=u-1===a,c="Move one or more fields into a single object field, or split the table — either way the existing rows need a data migration (`defineMigration` + `lunora migrate up`); there is no in-place path.";throw new O("VALIDATION_ERROR",`@lunora/sql-store: global table "${t}" needs ${String(u)} columns, over this engine's ${String(a)}-column limit. ${l?`One of them is "${w}", the row version every guarded write reads, which caps declared fields at ${String(a-s.length)}. A table provisioned at ${String(a)} columns before that column existed cannot be widened to hold it. `:""}${c}`)}return n.join([...s,...o],n`, `)},en=t=>{if(t.indexKeyPrefix?.("string")===void 0)return n`${m("_creationTime")}, ${m("id")}`},D=async(t,e,i,o)=>{const r=n.join(i.refs.map(u=>n`${u} IS NOT NULL`),n` AND `);if((await h(t,e,n`SELECT ${i.columns} FROM ${n.identifier(i.table)} WHERE ${r} GROUP BY ${i.columns} HAVING COUNT(*) > 1 LIMIT 1`)).length>0)throw new O("INTERNAL",`unique index "${i.name}" on "${i.table}" ${o}: existing rows are duplicates under it. De-duplicate the table with a data migration first.`)},N=async(t,e,i)=>{if(!i.unique){await E(t,e,i);return}try{await E(t,e,i)}catch(o){throw e.isUniqueViolation(o)&&await D(t,e,i,"cannot be created"),o}},tn=async(t,e,i)=>{if(e.name!=="sqlite")return;const r=(await h(t,e,n`SELECT sql FROM sqlite_master WHERE type = 'index' AND name = ${i.name} AND tbl_name = ${i.table}`))[0]?.sql;if(typeof r!="string")return;const s=l=>{const c=l.indexOf("(");return c===-1?void 0:l.slice(c,l.lastIndexOf(")")+1)},u=i.unique?n`UNIQUE `:n``,a=s(j(e.name,n`CREATE ${u}INDEX ${n.identifier(i.name)} ON ${n.identifier(i.table)} (${i.columns})`).sql);a===void 0||a===s(r)||(i.unique&&await D(t,e,i,"cannot be re-created with its new column list; the previous index is left in place"),await d(t,e,n`DROP INDEX IF EXISTS ${n.identifier(i.name)}`))},on=async(t,e,i,o)=>{J(e,i,o);const r=x(i),s=a=>{const l=m(a),c=i.shape[a],f=c&&o.indexKeyPrefix&&c._meta?.column?.unique!==!0&&!r.has(a)?o.indexKeyPrefix(p(c)):void 0;return f===void 0?l:n`${l}(${n.raw(String(f))})`},u=en(o);for(const a of i.indexes){const l=a.fields.map(g=>s(g)),c=n.join(l,n`, `),f=a.unique??!1,$={columns:f||u===void 0?c:n`${c}, ${u}`,name:`${e}_${a.name}`,refs:l,table:e,unique:f};await tn(t,o,$),await N(t,o,$)}u!==void 0&&await E(t,o,{columns:u,name:`${e}__by_creation`,table:e,unique:!1});for(const[a,l]of W(i)){if(!l.unique)continue;const c=s(a);await N(t,o,{columns:c,name:`${e}_unique_${a}`,refs:[c],table:e,unique:!0})}},T="__lunora_migration_state",rn=async(t,e)=>{await d(t,e,n`CREATE TABLE IF NOT EXISTS ${n.identifier(T)} (${n.identifier("name")} ${n.raw(e.companionTypes.key)} PRIMARY KEY)`)},sn=async(t,e,i)=>(await h(t,e,n`SELECT ${n.identifier("name")} FROM ${n.identifier(T)} WHERE ${n.identifier("name")} = ${i}`)).length>0,an=async(t,e,i,o,r,s)=>{r>0&&s!==void 0&&console.warn(`[@lunora/sql-store] storage migration "${i}" on "${o}": ${String(r)} row(s) ${s} and were left as stored — range filters and ORDER BY over them stay wrong until they are corrected by hand.`);try{await d(t,e,n`INSERT INTO ${n.identifier(T)} (${n.identifier("name")}) VALUES (${i})`)}catch(u){if(!e.isUniqueViolation(u))throw u}},ln=100,un=(t,e)=>{const i=[],o=[];for(const r of e.fields){const s=t[r],u=e.convert(s);u!==void 0&&(i.push(n`${m(r)} = ${u}`),o.push(n`${m(r)} = ${s}`))}return{assignments:i,guards:o}},U=async(t,e,i,o)=>{if(o.fields.length===0||(await rn(t,i),await sn(t,i,o.marker)))return;const r=n.join(o.fields.map(l=>o.probe(l)),n` OR `),s=n.join([m("id"),...o.fields.map(l=>m(l))],n`, `);let u="",a=0;for(;;){const l=await h(t,i,n`SELECT ${s} FROM ${n.identifier(e)} WHERE (${r}) AND ${m("id")} > ${u} ORDER BY ${m("id")} LIMIT ${n.raw(String(ln))}`);if(l.length===0){await an(t,i,o.marker,e,a,o.describeUnconvertible);return}const c=[];for(const $ of l){const{id:g}=$,{assignments:A,guards:k}=typeof g=="string"?un($,o):{assignments:[],guards:[]};if(A.length===0){a+=1;continue}c.push(n`UPDATE ${n.identifier(e)} SET ${n.join(A,n`, `)} WHERE ${m("id")} = ${g} AND ${n.join(k,n` AND `)}`)}await Q(t,i,c);const f=l.at(-1)?.id;if(typeof f!="string")return;u=f}},b=t=>`'${t}%'`,cn=t=>Object.entries(t.shape).filter(([,e])=>e._meta?.column!==void 0&&p(e)==="bigint").map(([e])=>e),fn=t=>Object.entries(t.shape).filter(([,e])=>e._meta?.column!==void 0&&Y(p(e))).map(([e])=>e),mn=async(t,e,i,o)=>U(t,e,o,{convert:r=>{if(!(typeof r!="string"||r.length===L))try{return F(BigInt(r))}catch{return}},describeUnconvertible:"hold a value the order-preserving key cannot represent (non-numeric text, or a magnitude past 39 digits)",fields:cn(i),marker:`bigint-key:${e}`,probe:r=>n`(${m(r)} IS NOT NULL AND LENGTH(${m(r)}) <> ${L})`}),dn=async(t,e,i,o)=>U(t,e,o,{convert:G,fields:fn(i),marker:`untyped-number-key:${e}`,probe:r=>n`(${m(r)} LIKE ${n.raw(b(H))} AND ${m(r)} NOT LIKE ${n.raw(b(V))})`}),$n=async(t,e,i,o)=>{const r=[[w,o.companionTypes.integer],...Object.entries(i.shape).filter(([,a])=>a._meta?.column!==void 0).map(([a,l])=>[a,v(l,o)])],s="t",u=async a=>{try{return await h(t,o,n`SELECT ${n.join(a.map(l=>X(s,l)),n`, `)} FROM ${n.identifier(e)} AS ${n.identifier(s)} LIMIT 0`),!0}catch{return!1}};if(!await u(r.map(([a])=>a)))for(const[a,l]of r)await u([a])||await d(t,o,n`ALTER TABLE ${n.identifier(e)} ADD COLUMN ${n.identifier(a)} ${n.raw(l)}`)},S=new Set,R=(t,e)=>{S.has(t)||(S.add(t),console.warn(`[@lunora/sql-store] ${e}`))},gn=(t,e,i)=>{const o=typeof i.charset=="string"?` CHARACTER SET ${i.charset} COLLATE ${String(i.collation)}`:"";return`ALTER TABLE \`${t}\` MODIFY COLUMN \`${e}\` ${String(i.type)}${o} NULL`},pn=async(t,e,i,o)=>{if(o.name==="sqlite")return;const r=Object.keys(i.shape).filter(s=>{const u=i.shape[s];return u._meta?.column?.notNull===!0&&u.kind!=="optional"&&q(u)});if(r.length!==0)try{const s=o.name==="postgres"?n`column_name AS ${n.identifier("name")}`:n`column_name AS ${n.identifier("name")}, column_type AS ${n.identifier("type")}, character_set_name AS ${n.identifier("charset")}, collation_name AS ${n.identifier("collation")}`,u=o.name==="postgres"?n`table_schema = ANY (current_schemas(false))`:n`table_schema = DATABASE()`,a=await h(t,o,n`SELECT ${s} FROM information_schema.columns WHERE ${u} AND table_name = ${e} AND is_nullable = 'NO' AND column_name IN (${n.join(r.map(l=>n`${l}`),n`, `)})`);for(const l of a){const c=String(l.name);if(o.name==="postgres"){await d(t,o,n`ALTER TABLE ${n.identifier(e)} ALTER COLUMN ${n.identifier(c)} DROP NOT NULL`);continue}R(`${e}.${c}`,`"${e}"."${c}" accepts null in the schema but its column is NOT NULL, so writing null there fails. Relaxing it on MySQL can rebuild the table, so it is not done at startup; run this once, in a maintenance window: ${gn(e,c,l)};`)}}catch(s){R(`${e}:relax-failed`,`could not relax NOT NULL on the null-accepting columns of "${e}" (${r.join(", ")}): ${s instanceof Error?s.message:String(s)}. Writing null to them fails until the constraint is dropped by hand.`)}},Ln=async(t,e,i)=>{for(const[o,r]of Object.entries(e.tables)){if(r.shardMode?.kind!=="global")continue;const s=nn(o,r,i);await d(t,i,n`CREATE TABLE IF NOT EXISTS ${n.identifier(o)} (${s})`),await $n(t,o,r,i),await pn(t,o,r,i),await on(t,o,r,i),await mn(t,o,r,i),await dn(t,o,r,i)}},M=t=>t.shardMode===void 0||t.shardMode.kind==="global",Nn=async(t,e,i)=>{const{integer:o,key:r,real:s}=i.companionTypes;for(const[u,a]of Object.entries(e.tables)){const l=a.aggregateIndexes;if(!(!l||l.length===0||!M(a)))for(const c of l){const f=B(u,c.name);await d(t,i,n`CREATE TABLE IF NOT EXISTS ${n.identifier(f)} (${n.identifier("__key__")} ${n.raw(r)} PRIMARY KEY, ${n.identifier("__value__")} ${n.raw(s)}, ${n.identifier("__count__")} ${n.raw(o)} NOT NULL DEFAULT 0)`),i.name==="sqlite"&&((await h(t,i,n`PRAGMA table_info(${n.identifier(f)})`)).some(g=>g.name==="__count__")||await d(t,i,n`ALTER TABLE ${n.identifier(f)} ADD COLUMN ${n.identifier("__count__")} ${n.raw(o)} NOT NULL DEFAULT 0`))}}},_=(t,e,i,o)=>{const r=t.name==="mysql"&&o?n`${n.identifier(e)}(191)`:n`${n.identifier(e)}`;return n`${r} ${n.raw(i)}`},hn=(t,e,i)=>{const o=[_(t,"__partition__","ASC",!0)];for(const[r,s]of e.sortBy.entries()){const u=i.shape[s.field],a=u!==void 0&&t.indexKeyPrefix?.(p(u))!==void 0;o.push(_(t,I(r),s.direction==="desc"?"DESC":"ASC",a))}return o.push(_(t,"__id__","ASC",!0)),o},En=(t,e,i)=>e.sortBy.map((o,r)=>{const s=i.shape[o.field],u=t.columnType(s?p(s):void 0);return n`${n.identifier(I(r))} ${n.raw(u)}`}),bn=async(t,e,i)=>{const{key:o}=i.companionTypes;for(const[r,s]of Object.entries(e.tables)){const u=s.rankIndexes;if(!(!u||u.length===0||!M(s)))for(const a of u){const l=P(r,a.name),c=En(i,a,s),f=c.length>0?n`, ${n.join(c,n`, `)}`:n``;await d(t,i,n`CREATE TABLE IF NOT EXISTS ${n.identifier(l)} (${n.identifier("__id__")} ${n.raw(o)} PRIMARY KEY, ${n.identifier("__partition__")} ${n.raw(o)} NOT NULL${f})`);const $=hn(i,a,s),g=`${r}__rank_${a.name}__btree`;await E(t,i,{columns:n.join($,n`, `),name:g,table:l,unique:!1})}}};export{Nn as runSqlAggregateMigrations,Ln as runSqlGlobalTableMigrations,bn as runSqlRankMigrations};