@lunora/sql-store 1.0.0-alpha.132 → 1.0.0-alpha.134
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/dialect.d.mts +12 -0
- package/dist/dialect.d.ts +12 -0
- package/dist/index.d.mts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/{SEARCH_STATE_TABLE-DvsS7EJ8.mjs → SEARCH_STATE_TABLE-qD9Y3Gen.mjs} +1 -1
- package/dist/packem_shared/backfillSqlSearchIndexes-Dq3Xp-4b.mjs +1 -0
- package/dist/packem_shared/{createSqlCtxDb-BBn2L287.mjs → createSqlCtxDb-D07PHR9y.mjs} +1 -1
- package/dist/packem_shared/ctx-db-search-BU0YOW8P.mjs +1 -0
- package/dist/packem_shared/{ctx-db-search-state-Xa6UmA3s.mjs → ctx-db-search-state-DaAD01za.mjs} +1 -1
- package/dist/packem_shared/decodeGlobalRow-C5EqHgwt.mjs +1 -0
- package/dist/packem_shared/{runSqlAggregateMigrations-D_MIiVIp.mjs → runSqlAggregateMigrations-B9-Aq3_d.mjs} +1 -1
- package/package.json +2 -2
- package/dist/packem_shared/backfillSqlSearchIndexes-DlBChpGZ.mjs +0 -1
- package/dist/packem_shared/ctx-db-search-CkWBiP2e.mjs +0 -1
- package/dist/packem_shared/decodeGlobalRow-D0xGl2NU.mjs +0 -1
package/dist/dialect.d.mts
CHANGED
|
@@ -39,6 +39,11 @@ interface SqlExec {
|
|
|
39
39
|
* longer serialized one full RTT at a time, and with no ordering between
|
|
40
40
|
* elements. Safe only for statements whose effects don't depend on each
|
|
41
41
|
* other (distinct-keyed rows), which is what every current caller batches.
|
|
42
|
+
*
|
|
43
|
+
* The one exception is the `sqlite` dialect, whose engines ship FTS5: there
|
|
44
|
+
* an implementation MUST run the array in order, as one transaction, as
|
|
45
|
+
* D1's does. The FTS5 search companion's writes are ordered statement
|
|
46
|
+
* lists that must not interleave with another writer's (`runInOrder`).
|
|
42
47
|
*/
|
|
43
48
|
batch?: (statements: ReadonlyArray<{
|
|
44
49
|
params: ReadonlyArray<unknown>;
|
|
@@ -149,6 +154,13 @@ interface SqlDialect {
|
|
|
149
154
|
/** The `ORDER BY` expression, best first. */
|
|
150
155
|
rank: (companion: string, terms: ReadonlyArray<string>) => SQL;
|
|
151
156
|
};
|
|
157
|
+
/**
|
|
158
|
+
* How an operator completes a search index that is still backfilling on
|
|
159
|
+
* this backend, in words, for the refusal a read against it raises: each
|
|
160
|
+
* backend's entry point differs. Absent, the refusal says only to retry
|
|
161
|
+
* once the backfill finishes.
|
|
162
|
+
*/
|
|
163
|
+
searchBackfillHint?: string;
|
|
152
164
|
/**
|
|
153
165
|
* True when the engine ships SQLite's FTS5 module, which decides whether a
|
|
154
166
|
* search index is stored as an FTS5 shadow or as the portable inverted
|
package/dist/dialect.d.ts
CHANGED
|
@@ -39,6 +39,11 @@ interface SqlExec {
|
|
|
39
39
|
* longer serialized one full RTT at a time, and with no ordering between
|
|
40
40
|
* elements. Safe only for statements whose effects don't depend on each
|
|
41
41
|
* other (distinct-keyed rows), which is what every current caller batches.
|
|
42
|
+
*
|
|
43
|
+
* The one exception is the `sqlite` dialect, whose engines ship FTS5: there
|
|
44
|
+
* an implementation MUST run the array in order, as one transaction, as
|
|
45
|
+
* D1's does. The FTS5 search companion's writes are ordered statement
|
|
46
|
+
* lists that must not interleave with another writer's (`runInOrder`).
|
|
42
47
|
*/
|
|
43
48
|
batch?: (statements: ReadonlyArray<{
|
|
44
49
|
params: ReadonlyArray<unknown>;
|
|
@@ -149,6 +154,13 @@ interface SqlDialect {
|
|
|
149
154
|
/** The `ORDER BY` expression, best first. */
|
|
150
155
|
rank: (companion: string, terms: ReadonlyArray<string>) => SQL;
|
|
151
156
|
};
|
|
157
|
+
/**
|
|
158
|
+
* How an operator completes a search index that is still backfilling on
|
|
159
|
+
* this backend, in words, for the refusal a read against it raises: each
|
|
160
|
+
* backend's entry point differs. Absent, the refusal says only to retry
|
|
161
|
+
* once the backfill finishes.
|
|
162
|
+
*/
|
|
163
|
+
searchBackfillHint?: string;
|
|
152
164
|
/**
|
|
153
165
|
* True when the engine ships SQLite's FTS5 module, which decides whether a
|
|
154
166
|
* search index is stored as an FTS5 shadow or as the portable inverted
|
package/dist/index.d.mts
CHANGED
|
@@ -37,6 +37,11 @@ declare const decodeGlobalRow: (definition: TableDefinitionLike, row: Record<str
|
|
|
37
37
|
* from recorded progress).
|
|
38
38
|
*/
|
|
39
39
|
declare const runSqlSearchMigrations: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<void>;
|
|
40
|
+
/** What {@link backfillSqlSearchIndexes} could not finish. */
|
|
41
|
+
interface SqlSearchBackfillResult {
|
|
42
|
+
/** A previous build's rows left unmigrated because every attempt at them lost to a concurrent write. */
|
|
43
|
+
unmappedSkipped: number;
|
|
44
|
+
}
|
|
40
45
|
/**
|
|
41
46
|
* Run every declared search index — including the `staged: true` ones the
|
|
42
47
|
* migration pass skips — through to completion. The entry point a host calls
|
|
@@ -46,7 +51,7 @@ declare const runSqlSearchMigrations: (exec: SqlCtxExec, schema: SchemaLike, dia
|
|
|
46
51
|
* Idempotent and resumable: an index recorded as complete is skipped, and an
|
|
47
52
|
* interrupted run picks up from its cursor.
|
|
48
53
|
*/
|
|
49
|
-
declare const backfillSqlSearchIndexes: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<
|
|
54
|
+
declare const backfillSqlSearchIndexes: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<SqlSearchBackfillResult>;
|
|
50
55
|
interface SqlCtxDbOptions {
|
|
51
56
|
/**
|
|
52
57
|
* Resolved request auth handed to `.serverDefault(fn)` column factories so
|
|
@@ -387,4 +392,4 @@ type SqlCtxDbOptions,
|
|
|
387
392
|
* This package is internal: consumers depend on `@lunora/d1` or
|
|
388
393
|
* `@lunora/hyperdrive`, which assemble the concrete dialect and wrap the core.
|
|
389
394
|
*/
|
|
390
|
-
type SqlCtxExec, type SqlDialect, type SqlRunResult, backfillSqlSearchIndexes, createSqlCtxDb, decodeBigint, decodeGlobalRow, effectiveColumnKind, migrateSearchState, readSqlCdcChangedTables, readSqlCdcChanges, readSqlCdcFloor, runSqlAggregateMigrations, runSqlCdcMigration, runSqlGlobalTableMigrations, runSqlRankMigrations, runSqlSearchMigrations, sqliteDecode, sqliteEncode, sweepSqlCdcRetention, tryJsonParse };
|
|
395
|
+
type SqlCtxExec, type SqlDialect, type SqlRunResult, type SqlSearchBackfillResult, backfillSqlSearchIndexes, createSqlCtxDb, decodeBigint, decodeGlobalRow, effectiveColumnKind, migrateSearchState, readSqlCdcChangedTables, readSqlCdcChanges, readSqlCdcFloor, runSqlAggregateMigrations, runSqlCdcMigration, runSqlGlobalTableMigrations, runSqlRankMigrations, runSqlSearchMigrations, sqliteDecode, sqliteEncode, sweepSqlCdcRetention, tryJsonParse };
|
package/dist/index.d.ts
CHANGED
|
@@ -37,6 +37,11 @@ declare const decodeGlobalRow: (definition: TableDefinitionLike, row: Record<str
|
|
|
37
37
|
* from recorded progress).
|
|
38
38
|
*/
|
|
39
39
|
declare const runSqlSearchMigrations: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<void>;
|
|
40
|
+
/** What {@link backfillSqlSearchIndexes} could not finish. */
|
|
41
|
+
interface SqlSearchBackfillResult {
|
|
42
|
+
/** A previous build's rows left unmigrated because every attempt at them lost to a concurrent write. */
|
|
43
|
+
unmappedSkipped: number;
|
|
44
|
+
}
|
|
40
45
|
/**
|
|
41
46
|
* Run every declared search index — including the `staged: true` ones the
|
|
42
47
|
* migration pass skips — through to completion. The entry point a host calls
|
|
@@ -46,7 +51,7 @@ declare const runSqlSearchMigrations: (exec: SqlCtxExec, schema: SchemaLike, dia
|
|
|
46
51
|
* Idempotent and resumable: an index recorded as complete is skipped, and an
|
|
47
52
|
* interrupted run picks up from its cursor.
|
|
48
53
|
*/
|
|
49
|
-
declare const backfillSqlSearchIndexes: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<
|
|
54
|
+
declare const backfillSqlSearchIndexes: (exec: SqlCtxExec, schema: SchemaLike, dialect: SqlDialect) => Promise<SqlSearchBackfillResult>;
|
|
50
55
|
interface SqlCtxDbOptions {
|
|
51
56
|
/**
|
|
52
57
|
* Resolved request auth handed to `.serverDefault(fn)` column factories so
|
|
@@ -387,4 +392,4 @@ type SqlCtxDbOptions,
|
|
|
387
392
|
* This package is internal: consumers depend on `@lunora/d1` or
|
|
388
393
|
* `@lunora/hyperdrive`, which assemble the concrete dialect and wrap the core.
|
|
389
394
|
*/
|
|
390
|
-
type SqlCtxExec, type SqlDialect, type SqlRunResult, backfillSqlSearchIndexes, createSqlCtxDb, decodeBigint, decodeGlobalRow, effectiveColumnKind, migrateSearchState, readSqlCdcChangedTables, readSqlCdcChanges, readSqlCdcFloor, runSqlAggregateMigrations, runSqlCdcMigration, runSqlGlobalTableMigrations, runSqlRankMigrations, runSqlSearchMigrations, sqliteDecode, sqliteEncode, sweepSqlCdcRetention, tryJsonParse };
|
|
395
|
+
type SqlCtxExec, type SqlDialect, type SqlRunResult, type SqlSearchBackfillResult, backfillSqlSearchIndexes, createSqlCtxDb, decodeBigint, decodeGlobalRow, effectiveColumnKind, migrateSearchState, readSqlCdcChangedTables, readSqlCdcChanges, readSqlCdcFloor, runSqlAggregateMigrations, runSqlCdcMigration, runSqlGlobalTableMigrations, runSqlRankMigrations, runSqlSearchMigrations, sqliteDecode, sqliteEncode, sweepSqlCdcRetention, tryJsonParse };
|
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-
|
|
1
|
+
import{createSqlCtxDb as a,readSqlCdcChangedTables as o,readSqlCdcChanges as t,readSqlCdcFloor as l,runSqlCdcMigration as s,sweepSqlCdcRetention as n}from"./packem_shared/createSqlCtxDb-D07PHR9y.mjs";import{runSqlAggregateMigrations as d,runSqlGlobalTableMigrations as S,runSqlRankMigrations as c}from"./packem_shared/runSqlAggregateMigrations-B9-Aq3_d.mjs";import{d as g,r as f}from"./packem_shared/ctx-db-search-BU0YOW8P.mjs";import{S as m,m as x}from"./packem_shared/ctx-db-search-state-DaAD01za.mjs";import{f as p,e as u,b as h,s as M,t as T}from"./packem_shared/value-codec-DC_dHxUY.mjs";import{decodeGlobalRow as E}from"./packem_shared/decodeGlobalRow-C5EqHgwt.mjs";export{m as SEARCH_STATE_TABLE,g as backfillSqlSearchIndexes,a as createSqlCtxDb,p 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,f as runSqlSearchMigrations,h as sqliteDecode,M as sqliteEncode,n as sweepSqlCdcRetention,T as tryJsonParse};
|
package/dist/packem_shared/{SEARCH_STATE_TABLE-DvsS7EJ8.mjs → SEARCH_STATE_TABLE-qD9Y3Gen.mjs}
RENAMED
|
@@ -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-
|
|
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-DaAD01za.mjs";import"./decodeGlobalRow-C5EqHgwt.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-DaAD01za.mjs";import{d as S,c as s,a as i,r as l}from"./ctx-db-search-BU0YOW8P.mjs";import"./decodeGlobalRow-C5EqHgwt.mjs";export{S as backfillSqlSearchIndexes,s as createSearchSync,i as runSqlSearch,l as runSqlSearchMigrations};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{LunoraError as T}from"@lunora/errors";import{m as Wt,D as Ut}from"./ctx-db-search-state-
|
|
1
|
+
import{LunoraError as T}from"@lunora/errors";import{m as Wt,D as Ut}from"./ctx-db-search-state-DaAD01za.mjs";import{c as xt,r as Pt,A as Ht,C as Kt,R as jt,a as Gt,T as Vt,x as Yt,b as Xt}from"./ctx-db-search-BU0YOW8P.mjs";import{d as Ri}from"./ctx-db-search-BU0YOW8P.mjs";import{literalInList as zt,throwingScheduler as Qt,CDC_LOG_TABLE as P,cdcForkedError as Jt,cursorBelowRetainedFloor as Zt,cdcTrimmedError as en,assertNoExplicitUndefined as et,hasTrigger as tn,runRowValidators as Ne,assertFlatPredicate as tt,mergeWhere as B,rankTableName as we,sortColumnName as Ee,resolveRankPartition as nt,encodePartitionKey as Ce,resolveRankSeekTuple as nn,RANK_TIEBREAK as ye,CountRlsUnsupportedError as it,stripReservedPatchFields as on,normalizeIdStructurally as rn,assertValidClientId as sn,aggregateSqlFunction as $e,softDeleteScope as _e,compileWhereSql as he,normalizeOrderKeys as an,uniqueIndexFields as cn,equalityPinnedFields as dn,buildSeekWhere as ln,decodeCursor as un,resolveRelationPredicates as ot,resolveWith as rt,relationHooks as st,applySelect as at,encodeCursor as fn,NotFoundError as wn,applyOnDelete as $n,ConflictError as Ae,normalizeCountArgument as _n,selectIndexForCount as hn,encodeAggregateKey as Y,aggregateTableName as Q,selectIndexForAggregate as En,readAggregateValue as qe,runTriggers as yn,matchesRankStaticWhere as ct,rankPivotConditionSql as pt,CURSOR_PREFIX as pn,selectIndexForGroupBy as Rn,tiebreakDirectionFor as Sn,fanOutScalarCounts as gn,matchesStaticWhere as De,foldAggregateTally as dt,coerceAggregateNumber as Be,CDC_LOG_TABLE_SEQ_INDEX as Tn,NotUniqueError as vn}from"@lunora/shard-engine";import{sql as e}from"drizzle-orm";import{d as Rt,b as mn,e as St,n as In,c as bn}from"./value-codec-DC_dHxUY.mjs";import{runSqlGlobalTableMigrations as Cn,runSqlAggregateMigrations as An,runSqlRankMigrations as Ln}from"./runSqlAggregateMigrations-B9-Aq3_d.mjs";import{queryAll as m,queryRun as M,createIndexIfNotExists as kn,decodeRow as ee,serializeDocumentColumn as J,columnRefSql as O,decodeRows as lt,tableColumns as gt,OCC_VERSION_COLUMN as ut,serializeColumnValue as Oe,forEachRowPaged as ft,BACKFILL_BATCH_SIZE as Fe,queryBatch as wt}from"./decodeGlobalRow-C5EqHgwt.mjs";import{decodeGlobalRow as gi}from"./decodeGlobalRow-C5EqHgwt.mjs";const Mn=(a,c)=>{if(a.size<c)return;const h=a.keys().next().value;h!==void 0&&a.delete(h)},$t=50,Nn=(a,c={})=>{const h=c.maxBatch??$t,r=Number.isInteger(h)&&h>0?h:$t,p=new Map;let w=!1;const d=async(g,v)=>{const I=[...v.keys()];for(let C=0;C<I.length;C+=r){const N=I.slice(C,C+r);try{const W=await a(g,N);for(const G of N){const V=W.get(G);for(const ie of v.get(G)??[])ie.resolve(V)}}catch(W){for(const G of N)for(const V of v.get(G)??[])V.reject(W)}}},L=()=>{w=!1;const g=[...p.entries()];p.clear();for(const[v,I]of g)d(v,I).catch(()=>{})};return{async load(g,v){return new Promise((I,C)=>{let N=p.get(g);N||(N=new Map,p.set(g,N));const W=N.get(v);W?W.push({reject:C,resolve:I}):N.set(v,[{reject:C,resolve:I}]),w||(w=!0,queueMicrotask(L))})}}},qn=new Set(["_id","id"]),Pe=(a,c,h)=>a==="postgres"?e`${c} IS NOT DISTINCT FROM ${h}`:a==="mysql"?e`${c} <=> ${h}`:e`${c} IS ${h}`,Tt=(a,c)=>a.name!=="postgres"||c.nullable!==!0?"":c.direction==="desc"?" NULLS LAST":" NULLS FIRST",Dn=(a,c)=>{const h=a.map(r=>e`${O(r.field)} ${e.raw(`${r.direction==="desc"?"DESC":"ASC"}${Tt(c,r)}`)}`);if(!a.some(r=>qn.has(r.field))){const r=Sn(a);h.push(e`${O("id")} ${e.raw(r==="desc"?"DESC":"ASC")}`)}return e.join(h,e`, `)},_t=50,Bn=(a,c,h)=>{const r={...c};for(const[p,w]of gt(a)){if(w.serverDefault){r[p]=w.serverDefault({auth:h});continue}r[p]===void 0&&(w.defaultFn?r[p]=w.defaultFn():"defaultValue"in w&&(r[p]=w.defaultValue))}return r},ht=(a,c,h,r)=>{for(const[p,w]of gt(a)){if(w.serverDefault){p in c&&(h[p]=w.serverDefault({auth:r}));continue}w.onUpdateFn&&!(p in c)&&(h[p]=w.onUpdateFn())}},On=128,Fn=()=>{const a=new Map;return{delete:c=>{a.delete(c)},get:c=>{const h=a.get(c);if(h!==void 0)return a.delete(c),a.set(c,h),h},set:(c,h)=>{a.has(c)?a.delete(c):Mn(a,On),a.set(c,h)}}},Wn=async(a,c,h,r,p)=>{const w=p.get(r);if(w!==void 0)return w;const d=Object.entries(h.tables).filter(([,g])=>g.shardMode===void 0||g.shardMode.kind==="global").map(([g])=>g),L=await Promise.all(d.map(async g=>({found:(await m(a,c,e`SELECT 1 FROM ${e.identifier(g)} WHERE ${e.identifier("id")} = ${r} LIMIT 1`)).length>0,tableName:g})));for(const g of L)if(g.found)return p.set(r,g.tableName),g.tableName},te=a=>a==null?null:Number(a),Un=(a,c,h)=>h.map(r=>{const p={};for(const w of c){const d=a.shape[w];p[w]=mn(r[w]??null,d&&St(d))}return{key:p,value:te(r.value)}}),xn=(a,c,h)=>[...a.partitionBy??[],...a.sortBy.map(p=>p.field),...a.where?Object.keys(a.where):[]].every(p=>c[p]===h[p]),vt=a=>a.length===1?a[0]:e`(${e.join(a,e` AND `)})`,Z=a=>e.join(a.map(c=>e`${e.identifier(c)}`),e`, `),ne=a=>e.join(a.map(c=>e`${c}`),e`, `),Pn=(a,c,h,r,p)=>{const w=[];for(let d=0;d<h.length+1;d+=1){const L=h[d],g=c.sortBy[d],v=d<h.length&&L!==void 0&&g!==void 0,I=g?.direction==="desc"?"desc":"asc",C=v?pt(L,r[L],I,!1):e`${e.identifier(ye)} < ${p}`;if(C===void 0)continue;const N=h.slice(0,d).map(W=>Pe(a,e`${e.identifier(W)}`,r[W]));N.push(C),w.push(vt(N))}return w.length>0?e.join(w,e` OR `):e`1 = 0`},Hn=(a,c,h)=>{if(h.length!==c.length)return;const r=[];for(const[p,w]of c.entries()){const d=pt(w.column,h[p],w.direction,!0);if(d===void 0)continue;const L=[];for(let g=0;g<p;g+=1){const v=c[g];v!==void 0&&L.push(Pe(a,e`${e.identifier(v.column)}`,h[g]))}L.push(d),r.push(vt(L))}return r.length===0?e`(1 = 0)`:e`(${e.join(r,e` OR `)})`},Kn=(a,c)=>{if(a.sortBy.length===0)throw new T("INTERNAL",`rankIndex "${a.name}" requires at least one "sortBy" column for stable pagination`);const h=[{column:"__partition__",direction:"asc",nullable:!1}];for(const[r,p]of a.sortBy.entries())h.push({column:c[r]??Ee(r),direction:p.direction,nullable:!0});return h.push({column:ye,direction:"asc",nullable:!1}),h},jn=async(a,c,h,r,p)=>{const d=[];for(let I=0;I<p.length;I+=50)d.push(p.slice(I,I+50));const L=await Promise.all(d.map(async I=>m(a,c,e`SELECT * FROM ${e.identifier(r)} WHERE ${e.identifier("id")} IN (${ne(I)})`))),g=new Map;for(const I of L)for(const C of I)g.set(C.id,C);const v=[];for(const I of p){const C=ee(h,g.get(I));C&&v.push(C)}return v},Gn=a=>{const c=JSON.stringify(a),h=new TextEncoder().encode(c);let r="";for(const p of h)r+=String.fromCodePoint(p);return pn+btoa(r)},mt=a=>{if(a===void 0)return!1;const c=St(a);return c==="any"||c==="bigint"||c==="from"||c==="union"},Ue=(a,c,h)=>{if(mt(a.shape[c]))throw new T("BAD_REQUEST",`${h}: "${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)`)},Vn=(a,c,h,r)=>{for(const p of h)Ue(a,p,`groupBy(${c}, { by: [..."${p}"] })`);r.field!==void 0&&Ue(a,r.field,`groupBy(${c}, { agg: { op: "${r.op}", field: "${r.field}" } })`)},xe="__cdc_sweep",It="global",Yn=6e4,Xn=1e4,zn=3e4;let Et=0;const Qn=async(a,c)=>{const{autoincrementPrimaryKey:h,key:r,real:p,text:w}=c.companionTypes;await M(a,c,e`CREATE TABLE IF NOT EXISTS ${e.identifier(P)} (${e.identifier("seq")} ${e.raw(h)}, ${e.identifier("ts")} ${e.raw(p)} 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(w)})`);const d=c.indexKeyPrefix?.("string");await kn(a,c,{columns:e`${d===void 0?e`${e.identifier("table")}`:e`${e.identifier("table")}(${e.raw(String(d))})`}, ${e.identifier("seq")}`,name:Tn,table:P,unique:!1}),await M(a,c,e`CREATE TABLE IF NOT EXISTS ${e.identifier(xe)} (${e.identifier("id")} ${e.raw(r)} NOT NULL PRIMARY KEY, ${e.identifier("lease_until")} ${e.raw(p)} NOT NULL)`),await M(a,c,e`INSERT INTO ${e.identifier(xe)} (${e.identifier("id")}, ${e.identifier("lease_until")}) VALUES (${It}, ${0}) ${c.name==="mysql"?e`ON DUPLICATE KEY UPDATE ${e.identifier("id")} = ${e.identifier("id")}`:e`ON CONFLICT(${e.identifier("id")}) DO NOTHING`}`)},Jn=async(a,c,h)=>{const r=e`UPDATE ${e.identifier(xe)} SET ${e.identifier("lease_until")} = ${h+Yn} WHERE ${e.identifier("id")} = ${It} AND ${e.identifier("lease_until")} <= ${h}`;if(c.supportsReturning)return(await m(a,c,e`${r} RETURNING ${e.identifier("id")}`)).length>0;const p=await M(a,c,r);return(c.affectedRows?c.affectedRows(p??{rowsAffected:0}):0)>0},Zn=async(a,c,h,r)=>{if(!await Jn(a,c,r))return;const p=r-h;await M(a,c,e`
|
|
2
2
|
DELETE FROM ${e.identifier(P)} WHERE ${e.identifier("seq")} IN (
|
|
3
3
|
SELECT ${e.identifier("seq")} FROM (
|
|
4
4
|
SELECT ${e.identifier("seq")} FROM ${e.identifier(P)} WHERE ${e.identifier("ts")} <= ${p} ORDER BY ${e.identifier("seq")} ASC LIMIT ${e.raw(String(Xn))}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as S}from"@lunora/errors";import{D,c as Sn,m as hn,r as b,a as pn,l as on,w as M,b as En}from"./ctx-db-search-state-DaAD01za.mjs";import{ftsUnmappedPage as gn,groupUnmappedRows as _n,ftsWriteDocument as W,ftsPurgeDocument as sn,ftsCompanionDdl as yn,unionAll as Rn,ftsRowidMapName as Tn}from"@lunora/shard-engine";import{sql as e}from"drizzle-orm";import{queryAll as g,decodeRow as an,runInOrder as O,queryRun as R,decodeRows as cn,queryBatch as In,createIndexIfNotExists as V,OCC_VERSION_COLUMN as An,columnRefSql as X,serializeColumnValue as Nn,readRowsPage as On,forEachRowPaged as Cn}from"./decodeGlobalRow-C5EqHgwt.mjs";const Dn=n=>{const t=atob(n),r=Uint8Array.from(t,i=>i.codePointAt(0)??0);return new TextDecoder().decode(r)},vn=()=>new S("BAD_REQUEST","invalid cursor"),Y=16,J=8,p=1024,B=(n,t)=>t.query(n),Ln=(n,t)=>{if(!t)return{exact:!0,lower:n,upper:n};const r=[...n].at(-1)??"",i=(r.codePointAt(0)??0)+1;if(i>=55296&&i<=57343||i>1114111)return{exact:!0,lower:n,upper:n};const o=n.slice(0,n.length-r.length);return{exact:!1,lower:n,upper:o+String.fromCodePoint(i)}},it=(n,t,r)=>{const i={eq:(o,s)=>{if(!n.definition.filterFields?.includes(o))throw new S("INTERNAL",`field "${o}" is not a filter field of search index "${n.indexName}" on table "${t}"`);if(n.filters.length>=J)throw new S("BAD_REQUEST",`search index "${n.indexName}" on table "${t}": at most ${String(J)} .eq() filters are supported per search query`);return n.filters.push({field:o,value:s}),i},search:(o,s)=>{const a=n;if(o!==a.definition.field)throw new S("INTERNAL",`search index "${a.indexName}" on table "${t}" indexes "${a.definition.field}", not "${o}"`);const c=B(s,r).length;if(c>Y)throw new S("BAD_REQUEST",`search index "${a.indexName}" on table "${t}": at most ${String(Y)} search terms are supported (got ${String(c)})`);return a.field=o,a.query=s,a.hasQuery=!0,i}};return i},ot=n=>{if(n.length>p)throw new S("BAD_REQUEST",`more than ${String(p)} documents match this search — narrow it with filters or read it a page at a time with .paginate()`)},st=n=>Math.min(n.offset+n.numItems+1,p),Mn=n=>btoa(`search:${String(n)}`),Un=n=>{let t;try{t=Dn(n)}catch{return}if(!t.startsWith("search:"))return;const r=Number(t.slice(7));return Number.isInteger(r)&&r>=0?r:void 0},at=n=>{if(typeof n.endCursor=="string")throw new S("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 S("BAD_REQUEST",`search pagination needs a finite numItems, got ${String(n.numItems)}`);const t=Math.max(0,Math.floor(n.numItems)),r=n.cursor?Un(n.cursor):0;if(r===void 0)throw vn();if(r+t>=p)throw new S("BAD_REQUEST",`search pagination reaches the ${String(p)}-document limit (offset ${String(r)} + ${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,p-r-1))} or fewer, or narrow the query or the filters instead`);return{numItems:t,offset:r}},ct=(n,t)=>{const r=t.offset+t.numItems,i=t.numItems>0&&n.length>r;return{continueCursor:i?Mn(r):null,isDone:!i,page:n.slice(t.offset,r)}},ft=n=>{if(n===void 0)return p+1;if(!Number.isFinite(n))return p;const t=Math.max(0,Math.floor(n));if(t>p)throw new S("BAD_REQUEST",`search returns at most ${String(p)} documents (asked for ${String(t)}) — narrow the query or paginate instead`);return t},bn=(n,t)=>{if(!t.includes("."))return n[t];let r=n;for(const i of t.split(".")){if(r===null||typeof r!="object"||Array.isArray(r))return;r=r[i]}return r},q=(n,t)=>`${n}__fts_${t}`,l="__id__",C="__token__",F="__n__",Bn=1e3,fn=(n,t)=>t.document(n),x=(n,t)=>bn(n,t),qn=n=>typeof n=="string"?n:n==null?"":typeof n=="number"||typeof n=="bigint"||typeof n=="boolean"?String(n):JSON.stringify(n)??"",Pn=(n,t,r)=>n!==void 0&&t!==void 0&&x(n,r.field)===x(t,r.field),kn=(n,t)=>fn(qn(x(n,t.field)),D(t.language)).slice(0,Bn),v=(n,t)=>kn(n,t).join(" "),Fn=(n,t)=>{const r=new Map;for(const i of fn(n,t))r.set(i,(r.get(i)??0)+1);return r},un=[An,"_creationTime"],U=async(n,t,r,i)=>{await R(n,t,e`DELETE FROM ${e.identifier(r)} WHERE ${e.identifier(l)} = ${i}`)},k=(n,t)=>n.name==="mysql"?e`${e.identifier(t)}(191)`:n.textPatternOperatorClass===void 0?e`${e.identifier(t)}`:e`${e.identifier(t)} ${e.raw(n.textPatternOperatorClass)}`,K=50,xn=(n,t)=>t?e`${e.identifier(C)} LIKE ${`${n}%`}`:e`${e.identifier(C)} = ${n}`,dn=(n,t)=>{const r=t.filters.map(i=>e`m.${X(i.field)} = ${Nn(i.value)}`);return n.softDeleteMode&&r.push(e`m.${X(n.softDeleteMode.field)} IS NULL`),r},$n=async(n,t,r,i,o,s,a)=>{const c=dn(r,o);let f=e`SELECT m.* FROM (${a}) s JOIN ${e.identifier(i)} m ON m.${e.identifier("id")} = s.${e.identifier(l)}`;return c.length>0&&(f=e`${f} WHERE ${e.join(c,e` AND `)}`),f=e`${f} ORDER BY s.${e.identifier("__score__")} DESC, m.${e.identifier("_creationTime")} DESC, m.${e.identifier("id")} ASC LIMIT ${e.raw(String(s))}`,cn(r,await g(n,t,f))},Hn=async(n,t,r,i,o,s)=>{const a=B(o.query,D(o.definition.language));if(a.length===0)return[];const c=q(i,o.indexName),f=a.length-1,u=a.map((w,_)=>xn(w,_===f)),d=e.join(u,e` OR `),$=u.map(w=>e`SUM(CASE WHEN ${w} THEN ${e.identifier(F)} ELSE 0 END)`),h=e.join($.map(w=>e`${w} > 0`),e` AND `),E=e`SELECT ${e.identifier(l)}, ${e.join($,e` + `)} AS ${e.identifier("__score__")} FROM ${e.identifier(c)} WHERE ${d} GROUP BY ${e.identifier(l)} HAVING ${h}`;return $n(n,t,r,i,o,s,E)},Wn=async(n,t,r,i,o,s)=>{const a=B(o.query,D(o.definition.language));if(a.length===0)return[];const c=q(i,o.indexName),f=`${c}__vocab`,u=a.length-1,d=a.map((E,w)=>{const _=Ln(E,w===u),m=_.exact?e`${e.identifier("term")} = ${_.lower}`:e`${e.identifier("term")} >= ${_.lower} AND ${e.identifier("term")} < ${_.upper}`;return e`SELECT ${e.identifier("doc")}, ${e.raw(String(w))} AS ${e.identifier("__term__")}, COUNT(*) AS ${e.identifier("__n__")} FROM ${e.identifier(f)} WHERE ${m} GROUP BY ${e.identifier("doc")}`}),$=a.map((E,w)=>e`SUM(CASE WHEN u.${e.identifier("__term__")} = ${e.raw(String(w))} THEN u.${e.identifier("__n__")} ELSE 0 END)`),h=e`SELECT f.${e.identifier(l)} AS ${e.identifier(l)}, ${e.join($,e` + `)} AS ${e.identifier("__score__")} FROM (${Rn(d)}) u JOIN ${e.identifier(c)} f ON f.rowid = u.${e.identifier("doc")} GROUP BY f.${e.identifier(l)} HAVING ${e.join($.map(E=>e`${E} > 0`),e` AND `)}`;return $n(n,t,r,i,o,s,h)},jn=async(n,t,r,i,o,s)=>{const a=t.nativeTextSearch,c=B(o.query,D(o.definition.language));if(!a||c.length===0)return[];const f=q(i,o.indexName),u=[a.matches(f,c),...dn(r,o)],d=e`SELECT m.* FROM ${e.identifier(f)} JOIN ${e.identifier(i)} m ON m.${e.identifier("id")} = ${e.identifier(f)}.${e.identifier(l)} WHERE ${e.join(u,e` AND `)} ORDER BY ${a.rank(f,c)} DESC, m.${e.identifier("_creationTime")} DESC, m.${e.identifier("id")} ASC LIMIT ${e.raw(String(s))}`;return cn(r,await g(n,t,d))},Qn={ensureCompanion:async(n,t,r)=>{const{integer:i,key:o}=t.companionTypes;await R(n,t,e`CREATE TABLE IF NOT EXISTS ${e.identifier(r)} (${e.identifier(C)} ${e.raw(o)} NOT NULL, ${e.identifier(l)} ${e.raw(o)} NOT NULL, ${e.identifier(F)} ${e.raw(i)} NOT NULL)`),await V(n,t,{columns:e`${k(t,C)}, ${k(t,l)}`,name:`${r}__btree`,table:r,unique:!1}),await V(n,t,{columns:k(t,l),name:`${r}__by_id`,table:r,unique:!1})},indexDocument:async(n,t,r,i,o,s)=>{await U(n,t,r,i);const a=[...Fn(v(o,s),D(s.language))],c=e.join([C,l,F].map(u=>e.identifier(u)),e`, `),f=[];for(let u=0;u<a.length;u+=K){const d=e.join(a.slice(u,u+K).map(([$,h])=>e`(${$}, ${i}, ${h})`),e`, `);f.push(e`INSERT INTO ${e.identifier(r)} (${c}) VALUES ${d}`)}await In(n,t,f)},name:"inverted",purgeDocument:U,runSearch:Hn},N=(n,t)=>e`${e.identifier(n)}.${e.identifier(t)}`,wn=({row:n,table:t})=>e`EXISTS (SELECT 1 FROM ${e.identifier(t)} WHERE ${N(t,"id")} = ${n.id}${e.join(un.map(r=>e` AND ${N(t,r)} IS ${n[r]??null}`))})`,j={ensureCompanion:async(n,t,r)=>{await O(n,t,yn(r))},indexDocument:async(n,t,r,i,o,s)=>{await O(n,t,W(r,i,v(o,s)))},name:"fts5",purgeDocument:async(n,t,r,i)=>{await O(n,t,sn(r,i))},runSearch:Wn},Gn={ensureCompanion:async(n,t,r)=>{const i=t.nativeTextSearch;if(i){await R(n,t,i.createCompanion(r,t.companionTypes.key));for(const o of i.createIndexes(r))await R(n,t,o)}},indexDocument:async(n,t,r,i,o,s)=>{const a=t.nativeTextSearch;a&&(await U(n,t,r,i),await R(n,t,a.indexDocument(r,i,v(o,s))))},name:"native",purgeDocument:U,runSearch:jn},T=(n,t)=>n.strategy==="native"&&t.nativeTextSearch!==void 0?Gn:t.supportsFts5?j:Qn,P=(n,t)=>`${Sn(n)}/${T(n,t).name}`,Q=function*(n){for(const[t,r]of Object.entries(n.tables)){const i=r.searchIndexes;if(!(r.shardMode!==void 0&&r.shardMode.kind!=="global"||!i))for(const o of i)yield[t,r,o]}},I=(n,t)=>q(n,t.name),z=100,Z=50,Vn=async(n,t,r,i,o,s,a)=>{const c=String(a.row.id);if(n!==j){await n.indexDocument(t,r,i,c,o,s);return}await O(t,r,W(i,c,v(o,s),{guard:wn(a)}))},H=async(n,t,r,i,o,s=0)=>{const a={done:!0,last:s,left:0};if(T(o,t)!==j||(await g(n,t,t.tableExists(i))).length===0)return a;const f=I(i,o),u=await g(n,t,gn(f,z,s));if(u.length===0)return a;const d=Math.max(...u.map(m=>Number(m.rowid))),$=_n(u),h=[...$.keys()],E=new Map;for(let m=0;m<h.length;m+=Z){const L=h.slice(m,m+Z),A=await g(n,t,e`SELECT * FROM ${e.identifier(i)} WHERE ${N(i,"id")} IN (${e.join(L.map(y=>e`${y}`),e`, `)})`);for(const y of A)E.set(String(y.id),y)}const w=[];for(const[m,L]of $){const A=E.get(m),y=A===void 0?void 0:an(r,A),G=A===void 0?e`NOT EXISTS (SELECT 1 FROM ${e.identifier(i)} WHERE ${N(i,"id")} = ${m})`:wn({row:A,table:i});w.push(...y?W(f,m,v(y,o),{guard:G,unmappedRowids:L}):sn(f,m,{guard:G,unmappedRowids:L}))}await O(n,t,w);const _=await g(n,t,e`SELECT COUNT(*) AS ${e.identifier("n")} FROM ${e.identifier(f)} WHERE ${N(f,"rowid")} > ${Math.max(0,s)} AND ${N(f,"rowid")} <= ${d}`);return{done:u.length<z,last:d,left:Number(_[0]?.n??0)}},Xn=async(n,t,r,i)=>{const o=I(r,i);return on(await b(n,t,o),P(i,t)).finished?!0:En(n,t,o)},ut=async(n,t,r,i,o,s)=>{if(!await Xn(n,t,i,o.definition))throw new S("SEARCH_INDEX_BUILDING",`search index "${o.indexName}" on table "${i}" 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 T(o.definition,t).runSearch(n,t,r,i,o,s)},nn=n=>n.slice(n.lastIndexOf("/")+1),tn=200,en=5,Yn=async(n,t,r,i)=>{const o=[...i.keys()],s=await g(n,t,e`SELECT * FROM ${e.identifier(r)} WHERE ${e.identifier("id")} >= ${o[0]} AND ${e.identifier("id")} <= ${o.at(-1)} ORDER BY ${e.identifier("id")} ASC`),a=new Map;for(const c of s){const f=c.id,u=i.get(f);u&&un.some(d=>String(u[d])!==String(c[d]))&&a.set(f,c)}return a},Jn=async(n,t,r,i,o,s,a)=>{const c=T(s,t);let f=new Map;for(const u of a)typeof u.id=="string"&&f.set(u.id,u);for(let u=0;f.size>0&&u<en;u+=1){for(const d of f.values()){const $=an(r,d);$&&await Vn(c,n,t,o,$,s,{row:d,table:i})}f=await Yn(n,t,i,f)}f.size>0&&console.warn(`[@lunora/sql-store] search backfill of "${o}": ${String(f.size)} row(s) were rewritten on every one of ${String(en)} re-check rounds and were left to their writers — each stays unindexed only if none of those writes changed its indexed text.`)},mn=async(n,t,r,i,o)=>{const s=I(i,o),a=P(o,t),c=on(await b(n,t,s),a);if(c.finished)return!0;if(c.wipe&&await M(n,t,s,void 0,!1,a),(await g(n,t,t.tableExists(i))).length===0)return await M(n,t,s,void 0,!0,a),!0;const u=await On(n,t,i,c.cursor,tn),d=u.findLast(h=>typeof h.id=="string")?.id;await Jn(n,t,r,i,s,o,u);const $=u.length<tn;return await M(n,t,s,d??c.cursor,$,a),$},ln=async(n,t,r)=>{await hn(n,r);for(const[i,,o]of Q(t)){const s=I(i,o),a=P(o,r),c=await b(n,r,s);c.profile!==void 0&&nn(c.profile)!==nn(a)&&(await R(n,r,e`DROP TABLE IF EXISTS ${e.identifier(s)}`),await R(n,r,e`DROP TABLE IF EXISTS ${e.identifier(Tn(s))}`),await pn(n,r,s)),await T(o,r).ensureCompanion(n,r,s)}},Kn=async(n,t,r,i,o)=>{const s=I(i,o);if((await b(n,t,s)).profile!==void 0)return;let c=0;(await g(n,t,t.tableExists(i))).length>0&&await Cn(n,t,r,i,()=>{c+=1},{limit:1}),await M(n,t,s,void 0,c===0,P(o,t))},dt=async(n,t,r)=>{await ln(n,t,r);for(const[i,o,s]of Q(t)){if(await H(n,r,o,i,s),s.staged){await Kn(n,r,o,i,s);continue}await mn(n,r,o,i,s)}},rn=3,zn=async(n,t,r,i,o)=>{let s=0,a=0;for(;;){let c=await H(n,t,r,i,o,s);for(let f=1;c.left>0&&f<rn;f+=1)c=await H(n,t,r,i,o,s);if(c.left>0&&(a+=c.left,console.warn(`[@lunora/sql-store] search migration of "${I(i,o)}": ${String(c.left)} row(s) lost every one of ${String(rn)} attempts to a concurrent write and were left for a later pass.`)),c.done)return a;s=c.last}},$t=async(n,t,r)=>{let i=0;await ln(n,t,r);for(const[o,s,a]of Q(t)){i+=await zn(n,r,s,o,a);let c=!1;for(;!c;)c=await mn(n,r,s,o,a)}return{unmappedSkipped:i}},wt=n=>{const{dialect:t,exec:r,schema:i}=n;return async(o,s,a,c)=>{const f=i.tables[o]?.searchIndexes;if(!(!f||f.length===0))for(const u of f){if(Pn(c,a,u))continue;const d=I(o,u);if(a){await T(u,t).indexDocument(r,t,d,s,a,u);continue}await T(u,t).purgeDocument(r,t,d,s)}}};export{it as A,at as C,ct as R,ft as T,ut as a,st as b,wt as c,$t as d,dt as r,ot as x};
|
package/dist/packem_shared/{ctx-db-search-state-Xa6UmA3s.mjs → ctx-db-search-state-DaAD01za.mjs}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{sql as e}from"drizzle-orm";import{queryRun as p,queryAll as m}from"./decodeGlobalRow-
|
|
1
|
+
import{sql as e}from"drizzle-orm";import{queryRun as p,queryAll as m}from"./decodeGlobalRow-C5EqHgwt.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{renderSql as d}from"@lunora/shard-engine";import{sql as r}from"drizzle-orm";import{s as C,b,e as I}from"./value-codec-DC_dHxUY.mjs";const K="_version",R=n=>n==="_id"||n==="id"?"id":n,v=n=>r`${r.identifier(R(n))}`,D=(n,e)=>r`${r.identifier(n)}.${r.identifier(R(e))}`,B=n=>{const e=[];for(const[t,o]of Object.entries(n.shape)){const s=o._meta?.column;s&&e.push([t,s])}return e},O=(n,e,t)=>{const{params:o,sql:s}=d(e.name,t);return n.all(s,o)},l=(n,e,t)=>{const{params:o,sql:s}=d(e.name,t);return n.run(s,o)},F=async(n,e,t)=>{if(t.length!==0){if(!n.batch){for(const o of t)await l(n,e,o);return}await n.batch(t.map(o=>d(e.name,o)))}},L=async(n,e,t)=>{if(n.batch&&e.name==="sqlite"){await n.batch(t.map(o=>d(e.name,o)));return}for(const o of t)await l(n,e,o)},U=async(n,e,t)=>{const o=t.unique?r`UNIQUE `:r``;if(e.name==="mysql"){try{await l(n,e,r`CREATE ${o}INDEX ${r.identifier(t.name)} ON ${r.identifier(t.table)} (${t.columns})`)}catch(s){const i=s;if(i.errno!==1061&&i.code!=="ER_DUP_KEYNAME"&&i.code!==1061)throw s}return}await l(n,e,r`CREATE ${o}INDEX IF NOT EXISTS ${r.identifier(t.name)} ON ${r.identifier(t.table)} (${t.columns})`)},z=C,m=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 m(n._meta?.inner);case"union":return n._meta?.members?.some(e=>m(e))===!0;default:return!1}},g=n=>n.kind==="optional"&&!m(n),E=new WeakMap,_=n=>{let e=E.get(n);return e===void 0&&(e=Object.entries(n.shape).map(([t,o])=>[t,I(o),g(o)]),E.set(n,e)),e},$=new WeakMap,N=(n,e)=>{let t=$.get(n);return t===void 0&&(t=new Map(_(n).map(([o,s])=>[o,s])),$.set(n,t)),t.get(e)},P=(n,e,t)=>C(t,N(n,e)),S=(n,e)=>{const t={};for(const[o,s,i]of _(n)){const c=e[o];c===void 0||i&&c===null||(t[o]=b(c,s))}return t._id=e.id,t._creationTime=e._creationTime,t},q=(n,e)=>e?S(n,e):null,W=(n,e)=>{const t=[];for(const o of e){const s=q(n,o);s&&t.push(s)}return t},p=500,M=(n,e,t,o,s)=>{const i=r`${r.identifier(t)}.${r.identifier("id")}`,c=o===void 0?r``:r` WHERE ${i} > ${o}`;return O(n,e,r`SELECT * FROM ${r.identifier(t)}${c} ORDER BY ${i} ASC LIMIT ${r.raw(String(s))}`)},X=async(n,e,t,o,s,i={})=>{let c=i.after,a=i.limit,f=!0;for(;f&&(a===void 0||a>0);){const h=a===void 0?p:Math.min(p,a),u=await M(n,e,o,c,h);for(const y of u){const w=q(t,y);w&&await s(w)}c=u.at(-1)?.id,f=u.length===h,a=a===void 0?void 0:a-u.length}};export{p as BACKFILL_BATCH_SIZE,K as OCC_VERSION_COLUMN,v as columnRefSql,U as createIndexIfNotExists,S as decodeGlobalRow,q as decodeRow,W as decodeRows,X as forEachRowPaged,R as physicalColumn,D as qualifiedColumnRefSql,O as queryAll,F as queryBatch,l as queryRun,M as readRowsPage,L as runInOrder,z as serializeColumnValue,P as serializeDocumentColumn,B as tableColumns};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{LunoraError as N}from"@lunora/errors";import{aggregateTableName as x,rankTableName as D,sortColumnName as A,renderSql as U,bigintSqlKey as M}from"@lunora/shard-engine";import{sql as n}from"drizzle-orm";import{queryRun as g,queryAll as E,createIndexIfNotExists as p,OCC_VERSION_COLUMN as h,tableColumns as k,qualifiedColumnRefSql as B,columnRefSql as d,queryBatch as P}from"./decodeGlobalRow-
|
|
1
|
+
import{LunoraError as N}from"@lunora/errors";import{aggregateTableName as x,rankTableName as D,sortColumnName as A,renderSql as U,bigintSqlKey as M}from"@lunora/shard-engine";import{sql as n}from"drizzle-orm";import{queryRun as g,queryAll as E,createIndexIfNotExists as p,OCC_VERSION_COLUMN as h,tableColumns as k,qualifiedColumnRefSql as B,columnRefSql as d,queryBatch as P}from"./decodeGlobalRow-C5EqHgwt.mjs";import{e as w,i as j,B as b,r as F,U as K,a as G}from"./value-codec-DC_dHxUY.mjs";const L=(i,t,e=!1)=>t.columnType(w(i),{unique:e||i._meta?.column?.unique===!0}),S=i=>{const t=new Set;for(const e of i.indexes)e.unique===!0&&e.fields.length===1&&t.add(e.fields[0]);return t},Y=(i,t,e)=>{if(e.indexKeyPrefix!==void 0)for(const o of t.indexes){if(o.unique!==!0||o.fields.length<=1)continue;const r=o.fields.filter(u=>{const l=t.shape[u];return l!==void 0&&e.indexKeyPrefix?.(w(l))!==void 0});if(r.length>0)throw new Error(`${i}.${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.`)}},V=(i,t,e)=>{const o=[],r=S(t);for(const[a,f]of Object.entries(t.shape)){if(!f._meta?.column)continue;const c=f._meta.column.notNull&&f.kind!=="optional"?" NOT NULL":"";o.push(n`${n.identifier(a)} ${n.raw(`${L(f,e,r.has(a))}${c}`)}`)}const u=[...e.frameworkColumns().map(a=>n`${n.identifier(a.name)} ${n.raw(a.type)}`),n`${n.identifier(h)} ${n.raw(e.companionTypes.integer)}`],l=u.length+o.length,s=e.maxTableColumns;if(s!==void 0&&l>s){const a=l-1===s,f="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 N("VALIDATION_ERROR",`@lunora/sql-store: global table "${i}" needs ${String(l)} columns, over this engine's ${String(s)}-column limit. ${a?`One of them is "${h}", the row version every guarded write reads, which caps declared fields at ${String(s-u.length)}. A table provisioned at ${String(s)} columns before that column existed cannot be widened to hold it. `:""}${f}`)}return n.join([...u,...o],n`, `)},H=i=>{if(i.indexKeyPrefix?.("string")===void 0)return n`${d("_creationTime")}, ${d("id")}`},q=async(i,t,e,o)=>{const r=n.join(e.refs.map(l=>n`${l} IS NOT NULL`),n` AND `);if((await E(i,t,n`SELECT ${e.columns} FROM ${n.identifier(e.table)} WHERE ${r} GROUP BY ${e.columns} HAVING COUNT(*) > 1 LIMIT 1`)).length>0)throw new N("INTERNAL",`unique index "${e.name}" on "${e.table}" ${o}: existing rows are duplicates under it. De-duplicate the table with a data migration first.`)},I=async(i,t,e)=>{if(!e.unique){await p(i,t,e);return}try{await p(i,t,e)}catch(o){throw t.isUniqueViolation(o)&&await q(i,t,e,"cannot be created"),o}},X=async(i,t,e)=>{if(t.name!=="sqlite")return;const r=(await E(i,t,n`SELECT sql FROM sqlite_master WHERE type = 'index' AND name = ${e.name} AND tbl_name = ${e.table}`))[0]?.sql;if(typeof r!="string")return;const u=a=>{const f=a.indexOf("(");return f===-1?void 0:a.slice(f,a.lastIndexOf(")")+1)},l=e.unique?n`UNIQUE `:n``,s=u(U(t.name,n`CREATE ${l}INDEX ${n.identifier(e.name)} ON ${n.identifier(e.table)} (${e.columns})`).sql);s===void 0||s===u(r)||(e.unique&&await q(i,t,e,"cannot be re-created with its new column list; the previous index is left in place"),await g(i,t,n`DROP INDEX IF EXISTS ${n.identifier(e.name)}`))},W=async(i,t,e,o)=>{Y(t,e,o);const r=S(e),u=s=>{const a=d(s),f=e.shape[s],c=f&&o.indexKeyPrefix&&f._meta?.column?.unique!==!0&&!r.has(s)?o.indexKeyPrefix(w(f)):void 0;return c===void 0?a:n`${a}(${n.raw(String(c))})`},l=H(o);for(const s of e.indexes){const a=s.fields.map($=>u($)),f=n.join(a,n`, `),c=s.unique??!1,m={columns:c||l===void 0?f:n`${f}, ${l}`,name:`${t}_${s.name}`,refs:a,table:t,unique:c};await X(i,o,m),await I(i,o,m)}l!==void 0&&await p(i,o,{columns:l,name:`${t}__by_creation`,table:t,unique:!1});for(const[s,a]of k(e)){if(!a.unique)continue;const f=u(s);await I(i,o,{columns:f,name:`${t}_unique_${s}`,refs:[f],table:t,unique:!0})}},_="__lunora_migration_state",Q=async(i,t)=>{await g(i,t,n`CREATE TABLE IF NOT EXISTS ${n.identifier(_)} (${n.identifier("name")} ${n.raw(t.companionTypes.key)} PRIMARY KEY)`)},z=async(i,t,e)=>(await E(i,t,n`SELECT ${n.identifier("name")} FROM ${n.identifier(_)} WHERE ${n.identifier("name")} = ${e}`)).length>0,J=async(i,t,e,o,r,u)=>{r>0&&u!==void 0&&console.warn(`[@lunora/sql-store] storage migration "${e}" on "${o}": ${String(r)} row(s) ${u} and were left as stored — range filters and ORDER BY over them stay wrong until they are corrected by hand.`);try{await g(i,t,n`INSERT INTO ${n.identifier(_)} (${n.identifier("name")}) VALUES (${e})`)}catch(l){if(!t.isUniqueViolation(l))throw l}},Z=100,nn=(i,t)=>{const e=[],o=[];for(const r of t.fields){const u=i[r],l=t.convert(u);l!==void 0&&(e.push(n`${d(r)} = ${l}`),o.push(n`${d(r)} = ${u}`))}return{assignments:e,guards:o}},O=async(i,t,e,o)=>{if(o.fields.length===0||(await Q(i,e),await z(i,e,o.marker)))return;const r=n.join(o.fields.map(a=>o.probe(a)),n` OR `),u=n.join([d("id"),...o.fields.map(a=>d(a))],n`, `);let l="",s=0;for(;;){const a=await E(i,e,n`SELECT ${u} FROM ${n.identifier(t)} WHERE (${r}) AND ${d("id")} > ${l} ORDER BY ${d("id")} LIMIT ${n.raw(String(Z))}`);if(a.length===0){await J(i,e,o.marker,t,s,o.describeUnconvertible);return}const f=[];for(const m of a){const{id:$}=m,{assignments:T,guards:C}=typeof $=="string"?nn(m,o):{assignments:[],guards:[]};if(T.length===0){s+=1;continue}f.push(n`UPDATE ${n.identifier(t)} SET ${n.join(T,n`, `)} WHERE ${d("id")} = ${$} AND ${n.join(C,n` AND `)}`)}await P(i,e,f);const c=a.at(-1)?.id;if(typeof c!="string")return;l=c}},R=i=>`'${i}%'`,en=i=>Object.entries(i.shape).filter(([,t])=>t._meta?.column!==void 0&&w(t)==="bigint").map(([t])=>t),tn=i=>Object.entries(i.shape).filter(([,t])=>t._meta?.column!==void 0&&j(w(t))).map(([t])=>t),on=async(i,t,e,o)=>O(i,t,o,{convert:r=>{if(!(typeof r!="string"||r.length===b))try{return M(BigInt(r))}catch{return}},describeUnconvertible:"hold a value the order-preserving key cannot represent (non-numeric text, or a magnitude past 39 digits)",fields:en(e),marker:`bigint-key:${t}`,probe:r=>n`(${d(r)} IS NOT NULL AND LENGTH(${d(r)}) <> ${b})`}),rn=async(i,t,e,o)=>O(i,t,o,{convert:F,fields:tn(e),marker:`untyped-number-key:${t}`,probe:r=>n`(${d(r)} LIKE ${n.raw(R(K))} AND ${d(r)} NOT LIKE ${n.raw(R(G))})`}),sn=async(i,t,e,o)=>{const r=[[h,o.companionTypes.integer],...Object.entries(e.shape).filter(([,s])=>s._meta?.column!==void 0).map(([s,a])=>[s,L(a,o)])],u="t",l=async s=>{try{return await E(i,o,n`SELECT ${n.join(s.map(a=>B(u,a)),n`, `)} FROM ${n.identifier(t)} AS ${n.identifier(u)} LIMIT 0`),!0}catch{return!1}};if(!await l(r.map(([s])=>s)))for(const[s,a]of r)await l([s])||await g(i,o,n`ALTER TABLE ${n.identifier(t)} ADD COLUMN ${n.identifier(s)} ${n.raw(a)}`)},$n=async(i,t,e)=>{for(const[o,r]of Object.entries(t.tables)){if(r.shardMode?.kind!=="global")continue;const u=V(o,r,e);await g(i,e,n`CREATE TABLE IF NOT EXISTS ${n.identifier(o)} (${u})`),await sn(i,o,r,e),await W(i,o,r,e),await on(i,o,r,e),await rn(i,o,r,e)}},v=i=>i.shardMode===void 0||i.shardMode.kind==="global",gn=async(i,t,e)=>{const{integer:o,key:r,real:u}=e.companionTypes;for(const[l,s]of Object.entries(t.tables)){const a=s.aggregateIndexes;if(!(!a||a.length===0||!v(s)))for(const f of a){const c=x(l,f.name);await g(i,e,n`CREATE TABLE IF NOT EXISTS ${n.identifier(c)} (${n.identifier("__key__")} ${n.raw(r)} PRIMARY KEY, ${n.identifier("__value__")} ${n.raw(u)}, ${n.identifier("__count__")} ${n.raw(o)} NOT NULL DEFAULT 0)`),e.name==="sqlite"&&((await E(i,e,n`PRAGMA table_info(${n.identifier(c)})`)).some($=>$.name==="__count__")||await g(i,e,n`ALTER TABLE ${n.identifier(c)} ADD COLUMN ${n.identifier("__count__")} ${n.raw(o)} NOT NULL DEFAULT 0`))}}},y=(i,t,e,o)=>{const r=i.name==="mysql"&&o?n`${n.identifier(t)}(191)`:n`${n.identifier(t)}`;return n`${r} ${n.raw(e)}`},an=(i,t,e)=>{const o=[y(i,"__partition__","ASC",!0)];for(const[r,u]of t.sortBy.entries()){const l=e.shape[u.field],s=l!==void 0&&i.indexKeyPrefix?.(w(l))!==void 0;o.push(y(i,A(r),u.direction==="desc"?"DESC":"ASC",s))}return o.push(y(i,"__id__","ASC",!0)),o},un=(i,t,e)=>t.sortBy.map((o,r)=>{const u=e.shape[o.field],l=i.columnType(u?w(u):void 0);return n`${n.identifier(A(r))} ${n.raw(l)}`}),wn=async(i,t,e)=>{const{key:o}=e.companionTypes;for(const[r,u]of Object.entries(t.tables)){const l=u.rankIndexes;if(!(!l||l.length===0||!v(u)))for(const s of l){const a=D(r,s.name),f=un(e,s,u),c=f.length>0?n`, ${n.join(f,n`, `)}`:n``;await g(i,e,n`CREATE TABLE IF NOT EXISTS ${n.identifier(a)} (${n.identifier("__id__")} ${n.raw(o)} PRIMARY KEY, ${n.identifier("__partition__")} ${n.raw(o)} NOT NULL${c})`);const m=an(e,s,u),$=`${r}__rank_${s.name}__btree`;await p(i,e,{columns:n.join(m,n`, `),name:$,table:a,unique:!1})}}};export{gn as runSqlAggregateMigrations,$n as runSqlGlobalTableMigrations,wn as runSqlRankMigrations};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/sql-store",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.134",
|
|
4
4
|
"description": "Internal dialect-parameterized SQL store core for Lunora .global() backends (D1, PlanetScale)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@lunora/errors": "1.0.0-alpha.41",
|
|
54
|
-
"@lunora/shard-engine": "1.0.0-alpha.
|
|
54
|
+
"@lunora/shard-engine": "1.0.0-alpha.82",
|
|
55
55
|
"drizzle-orm": "^0.45.2"
|
|
56
56
|
},
|
|
57
57
|
"engines": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./ctx-db-search-state-Xa6UmA3s.mjs";import{d as S,c as s,a as i,r as l}from"./ctx-db-search-CkWBiP2e.mjs";import"./decodeGlobalRow-D0xGl2NU.mjs";export{S as backfillSqlSearchIndexes,s as createSearchSync,i as runSqlSearch,l as runSqlSearchMigrations};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraError as l}from"@lunora/errors";import{D as y,c as Z,m as tt,r as R,a as nt,l as W,w as N,b as et}from"./ctx-db-search-state-Xa6UmA3s.mjs";import{sql as e}from"drizzle-orm";import{unionAll as it}from"@lunora/shard-engine";import{queryRun as E,decodeRows as Q,queryAll as A,queryBatch as rt,createIndexIfNotExists as q,columnRefSql as M,serializeColumnValue as ot,forEachRowPaged as G}from"./decodeGlobalRow-D0xGl2NU.mjs";const st=t=>{const n=atob(t),i=Uint8Array.from(n,r=>r.codePointAt(0)??0);return new TextDecoder().decode(i)},at=()=>new l("BAD_REQUEST","invalid cursor"),F=16,k=8,h=1024,v=(t,n)=>n.query(t),ft=(t,n)=>{if(!n)return{exact:!0,lower:t,upper:t};const i=[...t].at(-1)??"",r=(i.codePointAt(0)??0)+1;if(r>=55296&&r<=57343||r>1114111)return{exact:!0,lower:t,upper:t};const o=t.slice(0,t.length-i.length);return{exact:!1,lower:t,upper:o+String.fromCodePoint(r)}},Lt=(t,n,i)=>{const r={eq:(o,s)=>{if(!t.definition.filterFields?.includes(o))throw new l("INTERNAL",`field "${o}" is not a filter field of search index "${t.indexName}" on table "${n}"`);if(t.filters.length>=k)throw new l("BAD_REQUEST",`search index "${t.indexName}" on table "${n}": at most ${String(k)} .eq() filters are supported per search query`);return t.filters.push({field:o,value:s}),r},search:(o,s)=>{const a=t;if(o!==a.definition.field)throw new l("INTERNAL",`search index "${a.indexName}" on table "${n}" indexes "${a.definition.field}", not "${o}"`);const f=v(s,i).length;if(f>F)throw new l("BAD_REQUEST",`search index "${a.indexName}" on table "${n}": at most ${String(F)} search terms are supported (got ${String(f)})`);return a.field=o,a.query=s,a.hasQuery=!0,r}};return r},Ot=t=>{if(t.length>h)throw new l("BAD_REQUEST",`more than ${String(h)} documents match this search — narrow it with filters or read it a page at a time with .paginate()`)},xt=t=>Math.min(t.offset+t.numItems+1,h),ct=t=>btoa(`search:${String(t)}`),ut=t=>{let n;try{n=st(t)}catch{return}if(!n.startsWith("search:"))return;const i=Number(n.slice(7));return Number.isInteger(i)&&i>=0?i:void 0},Ut=t=>{if(typeof t.endCursor=="string")throw new l("BAD_REQUEST","bounded (endCursor) pagination is not supported on search queries — relevance order has no stable range boundary");if(!Number.isFinite(t.numItems))throw new l("BAD_REQUEST",`search pagination needs a finite numItems, got ${String(t.numItems)}`);const n=Math.max(0,Math.floor(t.numItems)),i=t.cursor?ut(t.cursor):0;if(i===void 0)throw at();if(i+n>=h)throw new l("BAD_REQUEST",`search pagination reaches the ${String(h)}-document limit (offset ${String(i)} + ${String(n)} requested) — a page must end below the cap so the probe row that answers \`hasMore\` still fits: retry with numItems ${String(Math.max(1,h-i-1))} or fewer, or narrow the query or the filters instead`);return{numItems:n,offset:i}},Bt=(t,n)=>{const i=n.offset+n.numItems,r=n.numItems>0&&t.length>i;return{continueCursor:r?ct(i):null,isDone:!r,page:t.slice(n.offset,i)}},qt=t=>{if(t===void 0)return h+1;if(!Number.isFinite(t))return h;const n=Math.max(0,Math.floor(t));if(n>h)throw new l("BAD_REQUEST",`search returns at most ${String(h)} documents (asked for ${String(n)}) — narrow the query or paginate instead`);return n},dt=(t,n)=>{if(!n.includes("."))return t[n];let i=t;for(const r of n.split(".")){if(i===null||typeof i!="object"||Array.isArray(i))return;i=i[r]}return i},b=(t,n)=>`${t}__fts_${n}`,$t="__text__",$="__id__",g="__token__",O="__n__",mt=1e3,V=(t,n)=>n.document(t),x=(t,n)=>dt(t,n),lt=t=>typeof t=="string"?t:t==null?"":typeof t=="number"||typeof t=="bigint"||typeof t=="boolean"?String(t):JSON.stringify(t)??"",St=(t,n,i)=>t!==void 0&&n!==void 0&&x(t,i.field)===x(n,i.field),ht=(t,n)=>V(lt(x(t,n.field)),y(n.language)).slice(0,mt),U=(t,n)=>ht(t,n).join(" "),wt=(t,n)=>{const i=new Map;for(const r of V(t,n))i.set(r,(i.get(r)??0)+1);return i},C=async(t,n,i,r)=>{await E(t,n,e`DELETE FROM ${e.identifier(i)} WHERE ${e.identifier($)} = ${r}`)},L=(t,n)=>t.name==="mysql"?e`${e.identifier(n)}(191)`:t.textPatternOperatorClass===void 0?e`${e.identifier(n)}`:e`${e.identifier(n)} ${e.raw(t.textPatternOperatorClass)}`,H=50,Et=(t,n)=>n?e`${e.identifier(g)} LIKE ${`${t}%`}`:e`${e.identifier(g)} = ${t}`,X=(t,n)=>{const i=n.filters.map(r=>e`m.${M(r.field)} = ${ot(r.value)}`);return t.softDeleteMode&&i.push(e`m.${M(t.softDeleteMode.field)} IS NULL`),i},J=async(t,n,i,r,o,s,a)=>{const f=X(i,o);let c=e`SELECT m.* FROM (${a}) s JOIN ${e.identifier(r)} m ON m.${e.identifier("id")} = s.${e.identifier($)}`;return f.length>0&&(c=e`${c} WHERE ${e.join(f,e` AND `)}`),c=e`${c} ORDER BY s.${e.identifier("__score__")} DESC, m.${e.identifier("_creationTime")} DESC, m.${e.identifier("id")} ASC LIMIT ${e.raw(String(s))}`,Q(i,await A(t,n,c))},pt=async(t,n,i,r,o,s)=>{const a=v(o.query,y(o.definition.language));if(a.length===0)return[];const f=b(r,o.indexName),c=a.length-1,u=a.map((d,_)=>Et(d,_===c)),m=e.join(u,e` OR `),S=u.map(d=>e`SUM(CASE WHEN ${d} THEN ${e.identifier(O)} ELSE 0 END)`),p=e.join(S.map(d=>e`${d} > 0`),e` AND `),w=e`SELECT ${e.identifier($)}, ${e.join(S,e` + `)} AS ${e.identifier("__score__")} FROM ${e.identifier(f)} WHERE ${m} GROUP BY ${e.identifier($)} HAVING ${p}`;return J(t,n,i,r,o,s,w)},_t=async(t,n,i,r,o,s)=>{const a=v(o.query,y(o.definition.language));if(a.length===0)return[];const f=b(r,o.indexName),c=`${f}__vocab`,u=a.length-1,m=a.map((w,d)=>{const _=ft(w,d===u),z=_.exact?e`${e.identifier("term")} = ${_.lower}`:e`${e.identifier("term")} >= ${_.lower} AND ${e.identifier("term")} < ${_.upper}`;return e`SELECT ${e.identifier("doc")}, ${e.raw(String(d))} AS ${e.identifier("__term__")}, COUNT(*) AS ${e.identifier("__n__")} FROM ${e.identifier(c)} WHERE ${z} GROUP BY ${e.identifier("doc")}`}),S=a.map((w,d)=>e`SUM(CASE WHEN u.${e.identifier("__term__")} = ${e.raw(String(d))} THEN u.${e.identifier("__n__")} ELSE 0 END)`),p=e`SELECT f.${e.identifier($)} AS ${e.identifier($)}, ${e.join(S,e` + `)} AS ${e.identifier("__score__")} FROM (${it(m)}) u JOIN ${e.identifier(f)} f ON f.rowid = u.${e.identifier("doc")} GROUP BY f.${e.identifier($)} HAVING ${e.join(S.map(w=>e`${w} > 0`),e` AND `)}`;return J(t,n,i,r,o,s,p)},gt=async(t,n,i,r,o,s)=>{const a=n.nativeTextSearch,f=v(o.query,y(o.definition.language));if(!a||f.length===0)return[];const c=b(r,o.indexName),u=[a.matches(c,f),...X(i,o)],m=e`SELECT m.* FROM ${e.identifier(c)} JOIN ${e.identifier(r)} m ON m.${e.identifier("id")} = ${e.identifier(c)}.${e.identifier($)} WHERE ${e.join(u,e` AND `)} ORDER BY ${a.rank(c,f)} DESC, m.${e.identifier("_creationTime")} DESC, m.${e.identifier("id")} ASC LIMIT ${e.raw(String(s))}`;return Q(i,await A(t,n,m))},yt={ensureCompanion:async(t,n,i)=>{const{integer:r,key:o}=n.companionTypes;await E(t,n,e`CREATE TABLE IF NOT EXISTS ${e.identifier(i)} (${e.identifier(g)} ${e.raw(o)} NOT NULL, ${e.identifier($)} ${e.raw(o)} NOT NULL, ${e.identifier(O)} ${e.raw(r)} NOT NULL)`),await q(t,n,{columns:e`${L(n,g)}, ${L(n,$)}`,name:`${i}__btree`,table:i,unique:!1}),await q(t,n,{columns:L(n,$),name:`${i}__by_id`,table:i,unique:!1})},indexDocument:async(t,n,i,r,o,s)=>{await C(t,n,i,r);const a=[...wt(U(o,s),y(s.language))],f=e.join([g,$,O].map(u=>e.identifier(u)),e`, `),c=[];for(let u=0;u<a.length;u+=H){const m=e.join(a.slice(u,u+H).map(([S,p])=>e`(${S}, ${r}, ${p})`),e`, `);c.push(e`INSERT INTO ${e.identifier(i)} (${f}) VALUES ${m}`)}await rt(t,n,c)},name:"inverted",runSearch:pt},Tt={ensureCompanion:async(t,n,i)=>{await E(t,n,e`CREATE VIRTUAL TABLE IF NOT EXISTS ${e.identifier(i)} USING fts5(${e.identifier($t)}, ${e.identifier($)} UNINDEXED)`),await E(t,n,e`CREATE VIRTUAL TABLE IF NOT EXISTS ${e.identifier(`${i}__vocab`)} USING fts5vocab(${e.identifier(i)}, ${e.raw("instance")})`)},indexDocument:async(t,n,i,r,o,s)=>{await C(t,n,i,r),await E(t,n,e`INSERT INTO ${e.identifier(i)} (${e.identifier("__text__")}, ${e.identifier($)}) VALUES (${U(o,s)}, ${r})`)},name:"fts5",runSearch:_t},It={ensureCompanion:async(t,n,i)=>{const r=n.nativeTextSearch;if(r){await E(t,n,r.createCompanion(i,n.companionTypes.key));for(const o of r.createIndexes(i))await E(t,n,o)}},indexDocument:async(t,n,i,r,o,s)=>{const a=n.nativeTextSearch;a&&(await C(t,n,i,r),await E(t,n,a.indexDocument(i,r,U(o,s))))},name:"native",runSearch:gt},T=(t,n)=>t.strategy==="native"&&n.nativeTextSearch!==void 0?It:n.supportsFts5?Tt:yt,D=(t,n)=>`${Z(t)}/${T(t,n).name}`,B=function*(t){for(const[n,i]of Object.entries(t.tables)){const r=i.searchIndexes;if(!(i.shardMode!==void 0&&i.shardMode.kind!=="global"||!r))for(const o of r)yield[n,i,o]}},I=(t,n)=>b(t,n.name),Nt=async(t,n,i,r)=>{const o=I(i,r);return W(await R(t,n,o),D(r,n)).finished?!0:et(t,n,o)},Mt=async(t,n,i,r,o,s)=>{if(!await Nt(t,n,r,o.definition))throw new l("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, or run the backfillSearch admin operation to complete it now`);return T(o.definition,n).runSearch(t,n,i,r,o,s)},P=t=>t.slice(t.lastIndexOf("/")+1),j=200,Y=async(t,n,i,r,o)=>{const s=I(r,o),a=T(o,n),f=D(o,n),c=W(await R(t,n,s),f);if(c.finished)return!0;if(c.wipe&&await N(t,n,s,void 0,!1,f),(await A(t,n,n.tableExists(r))).length===0)return await N(t,n,s,void 0,!0,f),!0;let m=0,S=c.cursor;await G(t,n,i,r,async w=>{m+=1;const d=w._id;typeof d=="string"&&(S=d,await a.indexDocument(t,n,s,d,w,o))},{after:c.cursor,limit:j});const p=m<j;return await N(t,n,s,S,p,f),p},K=async(t,n,i)=>{await tt(t,i);for(const[r,,o]of B(n)){const s=I(r,o),a=D(o,i),f=await R(t,i,s);f.profile!==void 0&&P(f.profile)!==P(a)&&(await E(t,i,e`DROP TABLE IF EXISTS ${e.identifier(s)}`),await nt(t,i,s)),await T(o,i).ensureCompanion(t,i,s)}},Rt=async(t,n,i,r,o)=>{const s=I(r,o);if((await R(t,n,s)).profile!==void 0)return;let f=0;(await A(t,n,n.tableExists(r))).length>0&&await G(t,n,i,r,()=>{f+=1},{limit:1}),await N(t,n,s,void 0,f===0,D(o,n))},Ft=async(t,n,i)=>{await K(t,n,i);for(const[r,o,s]of B(n)){if(s.staged){await Rt(t,i,o,r,s);continue}await Y(t,i,o,r,s)}},kt=async(t,n,i)=>{await K(t,n,i);for(const[r,o,s]of B(n)){let a=!1;for(;!a;)a=await Y(t,i,o,r,s)}},Ht=t=>{const{dialect:n,exec:i,schema:r}=t;return async(o,s,a,f)=>{const c=r.tables[o]?.searchIndexes;if(!(!c||c.length===0))for(const u of c){if(St(f,a,u))continue;const m=I(o,u);if(a){await T(u,n).indexDocument(i,n,m,s,a,u);continue}await C(i,n,m,s)}}};export{Lt as A,Ut as C,Bt as R,qt as T,Mt as a,xt as b,Ht as c,kt as d,Ft as r,Ot as x};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{renderSql as m}from"@lunora/shard-engine";import{sql as i}from"drizzle-orm";import{s as p,b as I,e as N}from"./value-codec-DC_dHxUY.mjs";const K="_version",R=e=>e==="_id"||e==="id"?"id":e,v=e=>i`${i.identifier(R(e))}`,D=(e,n)=>i`${i.identifier(e)}.${i.identifier(R(n))}`,B=e=>{const n=[];for(const[t,o]of Object.entries(e.shape)){const r=o._meta?.column;r&&n.push([t,r])}return n},O=(e,n,t)=>{const{params:o,sql:r}=m(n.name,t);return e.all(r,o)},u=(e,n,t)=>{const{params:o,sql:r}=m(n.name,t);return e.run(r,o)},F=async(e,n,t)=>{if(t.length!==0){if(!e.batch){for(const o of t)await u(e,n,o);return}await e.batch(t.map(o=>m(n.name,o)))}},L=async(e,n,t)=>{const o=t.unique?i`UNIQUE `:i``;if(n.name==="mysql"){try{await u(e,n,i`CREATE ${o}INDEX ${i.identifier(t.name)} ON ${i.identifier(t.table)} (${t.columns})`)}catch(r){const s=r;if(s.errno!==1061&&s.code!=="ER_DUP_KEYNAME"&&s.code!==1061)throw r}return}await u(e,n,i`CREATE ${o}INDEX IF NOT EXISTS ${i.identifier(t.name)} ON ${i.identifier(t.table)} (${t.columns})`)},U=p,d=e=>{if(e===void 0)return!1;if(e._meta?.column?.notNull===!1)return!0;switch(e.kind){case"any":case"null":return!0;case"literal":return e._meta?.value===null;case"optional":return d(e._meta?.inner);case"union":return e._meta?.members?.some(n=>d(n))===!0;default:return!1}},g=e=>e.kind==="optional"&&!d(e),w=new WeakMap,_=e=>{let n=w.get(e);return n===void 0&&(n=Object.entries(e.shape).map(([t,o])=>[t,N(o),g(o)]),w.set(e,n)),n},C=new WeakMap,S=(e,n)=>{let t=C.get(e);return t===void 0&&(t=new Map(_(e).map(([o,r])=>[o,r])),C.set(e,t)),t.get(n)},z=(e,n,t)=>p(t,S(e,n)),M=(e,n)=>{const t={};for(const[o,r,s]of _(e)){const c=n[o];c===void 0||s&&c===null||(t[o]=I(c,r))}return t._id=n.id,t._creationTime=n._creationTime,t},q=(e,n)=>n?M(e,n):null,W=(e,n)=>{const t=[];for(const o of n){const r=q(e,o);r&&t.push(r)}return t},$=500,X=async(e,n,t,o,r,s={})=>{let c=s.after,a=s.limit,f=!0;for(;f&&(a===void 0||a>0);){const E=a===void 0?$:Math.min($,a),b=c===void 0?i``:i` WHERE ${i.identifier("id")} > ${c}`,l=await O(e,n,i`SELECT * FROM ${i.identifier(o)}${b} ORDER BY ${i.identifier("id")} ASC LIMIT ${i.raw(String(E))}`);for(const y of l){const h=q(t,y);h&&await r(h)}c=l.at(-1)?.id,f=l.length===E,a=a===void 0?void 0:a-l.length}};export{$ as BACKFILL_BATCH_SIZE,K as OCC_VERSION_COLUMN,v as columnRefSql,L as createIndexIfNotExists,M as decodeGlobalRow,q as decodeRow,W as decodeRows,X as forEachRowPaged,R as physicalColumn,D as qualifiedColumnRefSql,O as queryAll,F as queryBatch,u as queryRun,U as serializeColumnValue,z as serializeDocumentColumn,B as tableColumns};
|