@lunora/sql-store 1.0.0-alpha.25 → 1.0.0-alpha.27
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
CHANGED
|
@@ -46,9 +46,19 @@ interface SqlDialect {
|
|
|
46
46
|
real: string;
|
|
47
47
|
text: string;
|
|
48
48
|
};
|
|
49
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Map a stored value back to its JS form, by effective validator `kind`
|
|
51
|
+
* (inverse of `encode`). NOTE: currently **unused** by the store core, which
|
|
52
|
+
* hard-codes `sqliteDecode` in `decodeGlobalRow` on every engine. Kept on the
|
|
53
|
+
* seam for a future engine-native codec; an override here does not run today.
|
|
54
|
+
*/
|
|
50
55
|
decode: (value: unknown, kind: string | undefined) => unknown;
|
|
51
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* Map a JS value to its bound storage form (boolean→1/0, bigint→string,
|
|
58
|
+
* object→JSON on SQLite). NOTE: currently **unused** by the store core, which
|
|
59
|
+
* hard-codes `sqliteEncode` as `serializeColumnValue` on every engine. Kept on
|
|
60
|
+
* the seam for a future engine-native codec; an override here does not run today.
|
|
61
|
+
*/
|
|
52
62
|
encode: (value: unknown) => unknown;
|
|
53
63
|
/** The framework columns every global table carries — the `id` primary key and `_creationTime` — as `{ name, type }` so the DDL builder can quote each name through the engine's dialect. */
|
|
54
64
|
frameworkColumns: () => ReadonlyArray<{
|
package/dist/dialect.d.ts
CHANGED
|
@@ -46,9 +46,19 @@ interface SqlDialect {
|
|
|
46
46
|
real: string;
|
|
47
47
|
text: string;
|
|
48
48
|
};
|
|
49
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Map a stored value back to its JS form, by effective validator `kind`
|
|
51
|
+
* (inverse of `encode`). NOTE: currently **unused** by the store core, which
|
|
52
|
+
* hard-codes `sqliteDecode` in `decodeGlobalRow` on every engine. Kept on the
|
|
53
|
+
* seam for a future engine-native codec; an override here does not run today.
|
|
54
|
+
*/
|
|
50
55
|
decode: (value: unknown, kind: string | undefined) => unknown;
|
|
51
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* Map a JS value to its bound storage form (boolean→1/0, bigint→string,
|
|
58
|
+
* object→JSON on SQLite). NOTE: currently **unused** by the store core, which
|
|
59
|
+
* hard-codes `sqliteEncode` as `serializeColumnValue` on every engine. Kept on
|
|
60
|
+
* the seam for a future engine-native codec; an override here does not run today.
|
|
61
|
+
*/
|
|
52
62
|
encode: (value: unknown) => unknown;
|
|
53
63
|
/** The framework columns every global table carries — the `id` primary key and `_creationTime` — as `{ name, type }` so the DDL builder can quote each name through the engine's dialect. */
|
|
54
64
|
frameworkColumns: () => ReadonlyArray<{
|
package/dist/index.d.mts
CHANGED
|
@@ -168,6 +168,12 @@ declare const effectiveColumnKind: (validator: ValidatorLike) => string | undefi
|
|
|
168
168
|
* - `object`/`array`/`record`: JSON string → parsed value.
|
|
169
169
|
* - `union`/`any`: parsed back only when the stored string is a JSON non-scalar
|
|
170
170
|
* (a scalar union member round-trips through SQLite's native column type).
|
|
171
|
+
* CAVEAT: a union/any member is stored verbatim by {@link sqliteEncode}, so a
|
|
172
|
+
* legitimate *string* value that itself looks like JSON (`'{"a":1}'`, `'[1,2]'`)
|
|
173
|
+
* is ambiguous on read and decodes back to the parsed object/array, not the
|
|
174
|
+
* original string. This is inherent to sharing one TEXT column between a string
|
|
175
|
+
* and an object member; disambiguating would require a breaking storage-format
|
|
176
|
+
* change (tagging encoded non-scalars), so it is documented rather than fixed.
|
|
171
177
|
* - everything else (string/number/date/timestamp/id/literal): verbatim.
|
|
172
178
|
*/
|
|
173
179
|
declare const sqliteDecode: (raw: unknown, kind: string | undefined) => unknown;
|
package/dist/index.d.ts
CHANGED
|
@@ -168,6 +168,12 @@ declare const effectiveColumnKind: (validator: ValidatorLike) => string | undefi
|
|
|
168
168
|
* - `object`/`array`/`record`: JSON string → parsed value.
|
|
169
169
|
* - `union`/`any`: parsed back only when the stored string is a JSON non-scalar
|
|
170
170
|
* (a scalar union member round-trips through SQLite's native column type).
|
|
171
|
+
* CAVEAT: a union/any member is stored verbatim by {@link sqliteEncode}, so a
|
|
172
|
+
* legitimate *string* value that itself looks like JSON (`'{"a":1}'`, `'[1,2]'`)
|
|
173
|
+
* is ambiguous on read and decodes back to the parsed object/array, not the
|
|
174
|
+
* original string. This is inherent to sharing one TEXT column between a string
|
|
175
|
+
* and an object member; disambiguating would require a breaking storage-format
|
|
176
|
+
* change (tagging encoded non-scalars), so it is documented rather than fixed.
|
|
171
177
|
* - everything else (string/number/date/timestamp/id/literal): verbatim.
|
|
172
178
|
*/
|
|
173
179
|
declare const sqliteDecode: (raw: unknown, kind: string | undefined) => unknown;
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { createSqlCtxDb, decodeGlobalRow, readSqlCdcChanges, runSqlAggregateMigrations, runSqlCdcMigration, runSqlGlobalTableMigrations, runSqlRankMigrations, runSqlSearchMigrations, trimSqlCdcChanges } from './packem_shared/createSqlCtxDb-
|
|
1
|
+
export { createSqlCtxDb, decodeGlobalRow, readSqlCdcChanges, runSqlAggregateMigrations, runSqlCdcMigration, runSqlGlobalTableMigrations, runSqlRankMigrations, runSqlSearchMigrations, trimSqlCdcChanges } from './packem_shared/createSqlCtxDb-KdluzmSA.mjs';
|
|
2
2
|
export { decodeBigint, effectiveColumnKind, sqliteDecode, sqliteEncode, tryJsonParse } from './packem_shared/decodeBigint-Dedu92k4.mjs';
|
|
@@ -3,6 +3,16 @@ import { LunoraError } from '@lunora/errors';
|
|
|
3
3
|
import { sql } from 'drizzle-orm';
|
|
4
4
|
import { sqliteDecode, sqliteEncode, effectiveColumnKind } from './decodeBigint-Dedu92k4.mjs';
|
|
5
5
|
|
|
6
|
+
const evictOldestEntry = (map, capacity) => {
|
|
7
|
+
if (map.size < capacity) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const oldest = map.keys().next().value;
|
|
11
|
+
if (oldest !== void 0) {
|
|
12
|
+
map.delete(oldest);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
6
16
|
const physicalColumn = (field) => field === "_id" || field === "id" ? "id" : field;
|
|
7
17
|
const columnRefSql = (field) => sql`${sql.identifier(physicalColumn(field))}`;
|
|
8
18
|
const ID_ORDER_FIELDS = /* @__PURE__ */ new Set(["_id", "id"]);
|
|
@@ -158,11 +168,8 @@ const createTableNameCache = () => {
|
|
|
158
168
|
set: (id, table) => {
|
|
159
169
|
if (map.has(id)) {
|
|
160
170
|
map.delete(id);
|
|
161
|
-
} else
|
|
162
|
-
|
|
163
|
-
if (oldest !== void 0) {
|
|
164
|
-
map.delete(oldest);
|
|
165
|
-
}
|
|
171
|
+
} else {
|
|
172
|
+
evictOldestEntry(map, TABLE_NAME_CACHE_CAPACITY);
|
|
166
173
|
}
|
|
167
174
|
map.set(id, table);
|
|
168
175
|
}
|
|
@@ -282,6 +289,10 @@ const createSearchBuilder = (search, tableName) => {
|
|
|
282
289
|
};
|
|
283
290
|
return builder;
|
|
284
291
|
};
|
|
292
|
+
const rankIndexFieldsUnchanged = (index, previous, next) => {
|
|
293
|
+
const fields = [...index.partitionBy ?? [], ...index.sortBy.map((key) => key.field), ...index.where ? Object.keys(index.where) : []];
|
|
294
|
+
return fields.every((field) => previous[field] === next[field]);
|
|
295
|
+
};
|
|
285
296
|
const andBranch = (conditions) => conditions.length === 1 ? conditions[0] : sql`(${sql.join(conditions, sql` AND `)})`;
|
|
286
297
|
const buildRankBeforeBranches = (engine, index, sortColumns, own, rowId) => {
|
|
287
298
|
const branches = [];
|
|
@@ -938,11 +949,18 @@ const createSqlCtxDb = (options) => {
|
|
|
938
949
|
}
|
|
939
950
|
};
|
|
940
951
|
const syncRanks = async (tableName, id, previous, next) => {
|
|
941
|
-
const
|
|
952
|
+
const definition = schema.tables[tableName];
|
|
953
|
+
const indexes = definition?.rankIndexes;
|
|
942
954
|
if (!indexes || indexes.length === 0) {
|
|
943
955
|
return;
|
|
944
956
|
}
|
|
957
|
+
const softField = definition?.softDeleteMode?.field;
|
|
958
|
+
const nextFieldValue = softField === void 0 || next === void 0 ? void 0 : next[softField];
|
|
959
|
+
const nextSoftDeleted = nextFieldValue !== null && nextFieldValue !== void 0;
|
|
945
960
|
for (const index of indexes) {
|
|
961
|
+
if (previous !== void 0 && next !== void 0 && rankIndexFieldsUnchanged(index, previous, next)) {
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
946
964
|
const cacheKey = `${tableName}::rank::${index.name}`;
|
|
947
965
|
const cached = rankBackfilled.get(cacheKey);
|
|
948
966
|
const exists = cached ?? await rankTableExists(tableName, index.name);
|
|
@@ -953,7 +971,7 @@ const createSqlCtxDb = (options) => {
|
|
|
953
971
|
if (previous) {
|
|
954
972
|
await queryRun(exec, dialect, sql`DELETE FROM ${sql.identifier(rankTable)} WHERE ${sql.identifier("__id__")} = ${id}`);
|
|
955
973
|
}
|
|
956
|
-
if (next) {
|
|
974
|
+
if (next && !nextSoftDeleted) {
|
|
957
975
|
if (index.where && !matchesRankStaticWhere(next, index.where)) {
|
|
958
976
|
continue;
|
|
959
977
|
}
|
|
@@ -1091,6 +1109,9 @@ const createSqlCtxDb = (options) => {
|
|
|
1091
1109
|
const row = rowsIndexed2[0];
|
|
1092
1110
|
return [{ key: { ...planned.partial }, value: readAggregateValue(agg.op, { count: row.count, value: aggregateScalar(row.value) }) }];
|
|
1093
1111
|
}
|
|
1112
|
+
if (partialKeys.length > 0) {
|
|
1113
|
+
return void 0;
|
|
1114
|
+
}
|
|
1094
1115
|
const rowsIndexed = await queryAll(
|
|
1095
1116
|
exec,
|
|
1096
1117
|
dialect,
|
|
@@ -1467,7 +1488,7 @@ const createSqlCtxDb = (options) => {
|
|
|
1467
1488
|
id = generateId();
|
|
1468
1489
|
usedExplicitId = false;
|
|
1469
1490
|
}
|
|
1470
|
-
const creationTime = typeof withDefaults["_creationTime"] === "number" ? withDefaults["_creationTime"] : clock();
|
|
1491
|
+
const creationTime = insertOptions?.allowExplicitId && typeof withDefaults["_creationTime"] === "number" ? withDefaults["_creationTime"] : clock();
|
|
1471
1492
|
const documentWithMeta = { ...withDefaults, _creationTime: creationTime, _id: id };
|
|
1472
1493
|
if (hasMatchingTrigger(tableName, "before", "insert")) {
|
|
1473
1494
|
await fireTriggers("before", "insert", { doc: { ...documentWithMeta }, id, op: "insert", table: tableName });
|
|
@@ -1552,7 +1573,7 @@ const createSqlCtxDb = (options) => {
|
|
|
1552
1573
|
if (wasDeleted) {
|
|
1553
1574
|
const row = decodeRow(definition, snapshot);
|
|
1554
1575
|
if (row !== null) {
|
|
1555
|
-
await syncRanks(tableName, id, void 0, row);
|
|
1576
|
+
await syncRanks(tableName, id, void 0, { ...row, [field]: null });
|
|
1556
1577
|
}
|
|
1557
1578
|
}
|
|
1558
1579
|
},
|
|
@@ -1754,7 +1775,7 @@ const createSqlCtxDb = (options) => {
|
|
|
1754
1775
|
}
|
|
1755
1776
|
return { continueCursor, isDone: !hasMore, page: documents };
|
|
1756
1777
|
},
|
|
1757
|
-
async replace(id, document, expectedTable) {
|
|
1778
|
+
async replace(id, document, expectedTable, replaceOptions) {
|
|
1758
1779
|
const tableName = await resolveTableName(id, expectedTable);
|
|
1759
1780
|
if (!tableName) {
|
|
1760
1781
|
throw new LunoraError("INTERNAL", `document not found: ${id}`);
|
|
@@ -1769,7 +1790,7 @@ const createSqlCtxDb = (options) => {
|
|
|
1769
1790
|
}
|
|
1770
1791
|
const needsPrevious = hasTrigger(schema, tableName, "update") || (definition.aggregateIndexes ?? []).length > 0 || (definition.rankIndexes ?? []).length > 0;
|
|
1771
1792
|
const previous = needsPrevious ? decodeRow(definition, snapshot) ?? void 0 : void 0;
|
|
1772
|
-
const creationTime = typeof document["_creationTime"] === "number" ? document["_creationTime"] : clock();
|
|
1793
|
+
const creationTime = replaceOptions?.allowExplicitId && typeof document["_creationTime"] === "number" ? document["_creationTime"] : clock();
|
|
1773
1794
|
const replaced = { ...document, _creationTime: creationTime, _id: id };
|
|
1774
1795
|
applyOnUpdate(definition, document, replaced, auth);
|
|
1775
1796
|
runRowValidators(definition, replaced);
|
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.27",
|
|
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/do": "1.0.0-alpha.
|
|
54
|
-
"@lunora/errors": "1.0.0-alpha.
|
|
53
|
+
"@lunora/do": "1.0.0-alpha.27",
|
|
54
|
+
"@lunora/errors": "1.0.0-alpha.4",
|
|
55
55
|
"drizzle-orm": "^0.45.2"
|
|
56
56
|
},
|
|
57
57
|
"engines": {
|