@lunora/do 1.0.0-alpha.4 → 1.0.0-alpha.41
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/LICENSE.md +6 -0
- package/__assets__/package-og.svg +1 -1
- package/dist/index.d.mts +5236 -3449
- package/dist/index.d.ts +5236 -3449
- package/dist/index.mjs +34 -25
- package/dist/packem_shared/{ADMIN_FUNCTIONS-Dzdqq5J2.mjs → ADMIN_FUNCTIONS-CnZkbXH_.mjs} +69 -8
- package/dist/packem_shared/{AGGREGATE_SQL_FUNCTION-CFk6adSu.mjs → AGGREGATE_SQL_FUNCTION-CQsu2Xga.mjs} +5 -3
- package/dist/packem_shared/{CDC_LOG_TABLE-Ctdmxmrv.mjs → CDC_LOG_TABLE-uwOJxJJZ.mjs} +8 -4
- package/dist/packem_shared/{ConflictError-C0STs6bU.mjs → ConflictError-CLoq37xH.mjs} +4 -5
- package/dist/packem_shared/{CountRlsUnsupportedError-28ZvvwKS.mjs → CountRlsUnsupportedError-BGxj0pgS.mjs} +4 -4
- package/dist/packem_shared/{DATA_MIGRATION_STATE_TABLE-PTtTiQ7U.mjs → DATA_MIGRATION_STATE_TABLE-CYwBpyTr.mjs} +11 -3
- package/dist/packem_shared/{DEFAULT_MAX_RELATION_KEYS-DyVYReuT.mjs → DEFAULT_MAX_RELATION_KEYS-BEan1CRD.mjs} +58 -7
- package/dist/packem_shared/GEO_DEFAULT_PRECISION-BWnsNmpP.mjs +114 -0
- package/dist/packem_shared/{MAX_SQL_ROWS-dDcFE1YZ.mjs → MAX_SQL_ROWS-iFAA8FbD.mjs} +35 -3
- package/dist/packem_shared/NotFoundError-C70b9hLw.mjs +9 -0
- package/dist/packem_shared/{NotUniqueError-B6tzuDmM.mjs → NotUniqueError-DbtlWwcG.mjs} +400 -114
- package/dist/packem_shared/{rank-CrkEIpF4.mjs → RANK_TIEBREAK-CXhdcA1o.mjs} +2 -13
- package/dist/packem_shared/{RLS_UNWRAP_SYMBOL-u3UlnCH5.mjs → RLS_UNWRAP_SYMBOL-DTvHvRzY.mjs} +22 -9
- package/dist/packem_shared/ROOT_DO_SIZE_WARN_BYTES-B4Cpm7Qv.mjs +7653 -0
- package/dist/packem_shared/{ReactiveCache-ByVzgH3d.mjs → ReactiveCache-DnSvbjil.mjs} +4 -30
- package/dist/packem_shared/{SESSION_DO_TTL_DEFAULT-ilPZsVwu.mjs → SESSION_DO_TTL_DEFAULT-BnSKgVO4.mjs} +16 -27
- package/dist/packem_shared/{SHARD_REGISTRY_DO_NAME-BsAbi5Mn.mjs → SHARD_REGISTRY_DO_NAME-D99roc-r.mjs} +12 -14
- package/dist/packem_shared/{applyOnDelete-CMif2RKw.mjs → applyOnDelete-BXSq3S70.mjs} +23 -12
- package/dist/packem_shared/{buildSeekWhere-lVsNXSLy.mjs → applySelect-WQY8m62C.mjs} +21 -2
- package/dist/packem_shared/{armRestore-BJk53Ro8.mjs → armRestore-4Px61hHS.mjs} +4 -10
- package/dist/packem_shared/{backfillAggregateIndexes-BbVPvciS.mjs → backfillAggregateIndexes-BGTwynwh.mjs} +4 -3
- package/dist/packem_shared/{compileWhereSql-CXrhFA3G.mjs → compileWhereSql-DE6yfRcQ.mjs} +5 -3
- package/dist/packem_shared/constant-time-equal-BVRWZgES.mjs +12 -0
- package/dist/packem_shared/context-telemetry-CDpyil58.mjs +223 -0
- package/dist/packem_shared/createMetrics-C4I2Yanj.mjs +1 -0
- package/dist/packem_shared/{createSystemReader-8CzSZP9V.mjs → createSystemReader-D12eNH13.mjs} +4 -2
- package/dist/packem_shared/ctx-db-idempotency-BdcNpvY4.mjs +108 -0
- package/dist/packem_shared/ctx-db-shapes-DTeFiHYS.mjs +53 -0
- package/dist/packem_shared/diffExternalSource-CovHfdyo.mjs +44 -0
- package/dist/packem_shared/{do-sql-BCHCWtrD.mjs → do-sql-CGAgiQUz.mjs} +2 -1
- package/dist/packem_shared/{exportShardRows-DZEhUeyI.mjs → exportShardRows-Dy3oFZ26.mjs} +4 -3
- package/dist/packem_shared/isSoftDeleted-CFJmhjFP.mjs +217 -0
- package/dist/packem_shared/json-response-BdbtpOhm.mjs +3 -0
- package/dist/packem_shared/materializeExternalRows-BtEGs1Fv.mjs +45 -0
- package/dist/packem_shared/{runShardMigrations-PabobOjF.mjs → runShardMigrations-BVax6rYu.mjs} +21 -4
- package/dist/packem_shared/selectExpiredIds-C5W29Upb.mjs +18 -0
- package/dist/packem_shared/serialize-sql-BlRUoiQe.mjs +14 -0
- package/dist/packem_shared/{serveRelationFanout-CFBKWJ8Q.mjs → serveRelationFanout-k7HTh3CC.mjs} +4 -7
- package/dist/packem_shared/stableStringify-mC40mZts.mjs +40 -0
- package/dist/packem_shared/stableWireKey-DKuXO7T5.mjs +6 -0
- package/dist/packem_shared/subscriptionListDeltas-CT76bYny.mjs +126 -0
- package/dist/packem_shared/wire-codec-CzQc1pvf.mjs +225 -0
- package/package.json +5 -3
- package/dist/packem_shared/NotFoundError-CMuMZt81.mjs +0 -10
- package/dist/packem_shared/RANK_TIEBREAK-C6blLR5K.mjs +0 -1
- package/dist/packem_shared/ROOT_DO_SIZE_WARN_BYTES-3lZ2yigq.mjs +0 -4003
- package/dist/packem_shared/ctx-db-idempotency-DkC9rP91.mjs +0 -35
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { b as applyCloudflareSpanAttributes, c as createMetrics, a as createTracer, d as dispatchRootSpan } from './context-telemetry-CDpyil58.mjs';
|
package/dist/packem_shared/{createSystemReader-8CzSZP9V.mjs → createSystemReader-D12eNH13.mjs}
RENAMED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { LunoraError } from '@lunora/errors';
|
|
2
|
+
|
|
1
3
|
const toScheduledFunctionDoc = (record) => {
|
|
2
4
|
const doc = {
|
|
3
5
|
args: record["args"] ?? {},
|
|
@@ -40,13 +42,13 @@ const toStorageMetadata = (object) => {
|
|
|
40
42
|
const createSystemReader = (options = {}) => {
|
|
41
43
|
const requireScheduler = () => {
|
|
42
44
|
if (!options.scheduler) {
|
|
43
|
-
throw new
|
|
45
|
+
throw new LunoraError("INTERNAL", 'ctx.db.system.query("_scheduled_functions"): no scheduler configured. Pass `scheduler` to createShardDO().');
|
|
44
46
|
}
|
|
45
47
|
return options.scheduler;
|
|
46
48
|
};
|
|
47
49
|
const requireStorage = () => {
|
|
48
50
|
if (!options.storage) {
|
|
49
|
-
throw new
|
|
51
|
+
throw new LunoraError("INTERNAL", 'ctx.db.system.query("_storage"): no storage configured. Pass `storage` to createShardDO().');
|
|
50
52
|
}
|
|
51
53
|
return options.storage;
|
|
52
54
|
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { sql } from 'drizzle-orm';
|
|
2
|
+
import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
|
|
3
|
+
|
|
4
|
+
const CLIENT_WATERMARK_TABLE = "__client_watermark";
|
|
5
|
+
const migrateClientWatermark = (sql$1) => {
|
|
6
|
+
runDrizzle(
|
|
7
|
+
sql$1,
|
|
8
|
+
sql`CREATE TABLE IF NOT EXISTS ${sql.identifier(CLIENT_WATERMARK_TABLE)} (
|
|
9
|
+
identity TEXT NOT NULL,
|
|
10
|
+
client_id TEXT NOT NULL,
|
|
11
|
+
last_mutation_id INTEGER NOT NULL,
|
|
12
|
+
PRIMARY KEY (identity, client_id)
|
|
13
|
+
)`
|
|
14
|
+
);
|
|
15
|
+
};
|
|
16
|
+
const readClientWatermark = (sql$1, identity, clientId) => {
|
|
17
|
+
const rows = runDrizzle(
|
|
18
|
+
sql$1,
|
|
19
|
+
sql`SELECT last_mutation_id FROM ${sql.identifier(CLIENT_WATERMARK_TABLE)} WHERE identity = ${identity} AND client_id = ${clientId} LIMIT 1`
|
|
20
|
+
).toArray();
|
|
21
|
+
return rows[0]?.last_mutation_id ?? 0;
|
|
22
|
+
};
|
|
23
|
+
const advanceClientWatermark = (sql$1, identity, clientId, mutationId) => {
|
|
24
|
+
runDrizzle(
|
|
25
|
+
sql$1,
|
|
26
|
+
sql`INSERT INTO ${sql.identifier(CLIENT_WATERMARK_TABLE)} (identity, client_id, last_mutation_id) VALUES (${identity}, ${clientId}, ${mutationId})
|
|
27
|
+
ON CONFLICT(identity, client_id) DO UPDATE SET last_mutation_id = MAX(last_mutation_id, excluded.last_mutation_id)`
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const GLOBAL_SHAPE_SNAPSHOT_TABLE = "__global_shape_snapshot";
|
|
32
|
+
const migrateGlobalShapeSnapshot = (sql$1) => {
|
|
33
|
+
runDrizzle(
|
|
34
|
+
sql$1,
|
|
35
|
+
sql`CREATE TABLE IF NOT EXISTS ${sql.identifier(GLOBAL_SHAPE_SNAPSHOT_TABLE)} (
|
|
36
|
+
connection_id TEXT NOT NULL,
|
|
37
|
+
sub_id TEXT NOT NULL,
|
|
38
|
+
members TEXT NOT NULL,
|
|
39
|
+
PRIMARY KEY (connection_id, sub_id)
|
|
40
|
+
)`
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
const readGlobalShapeSnapshot = (sql$1, connectionId, subId) => {
|
|
44
|
+
const rows = runDrizzle(
|
|
45
|
+
sql$1,
|
|
46
|
+
sql`SELECT members FROM ${sql.identifier(GLOBAL_SHAPE_SNAPSHOT_TABLE)} WHERE connection_id = ${connectionId} AND sub_id = ${subId} LIMIT 1`
|
|
47
|
+
).toArray();
|
|
48
|
+
const raw = rows[0]?.members;
|
|
49
|
+
if (raw === void 0) {
|
|
50
|
+
return /* @__PURE__ */ new Map();
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const parsed = JSON.parse(raw);
|
|
54
|
+
if (parsed === null || typeof parsed !== "object") {
|
|
55
|
+
return /* @__PURE__ */ new Map();
|
|
56
|
+
}
|
|
57
|
+
return new Map(Object.entries(parsed));
|
|
58
|
+
} catch {
|
|
59
|
+
return /* @__PURE__ */ new Map();
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const writeGlobalShapeSnapshot = (sql$1, connectionId, subId, snapshot) => {
|
|
63
|
+
const members = JSON.stringify(Object.fromEntries(snapshot));
|
|
64
|
+
runDrizzle(
|
|
65
|
+
sql$1,
|
|
66
|
+
sql`INSERT INTO ${sql.identifier(GLOBAL_SHAPE_SNAPSHOT_TABLE)} (connection_id, sub_id, members) VALUES (${connectionId}, ${subId}, ${members})
|
|
67
|
+
ON CONFLICT(connection_id, sub_id) DO UPDATE SET members = excluded.members`
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
const deleteGlobalShapeSnapshot = (sql$1, connectionId, subId) => {
|
|
71
|
+
runDrizzle(sql$1, sql`DELETE FROM ${sql.identifier(GLOBAL_SHAPE_SNAPSHOT_TABLE)} WHERE connection_id = ${connectionId} AND sub_id = ${subId}`);
|
|
72
|
+
};
|
|
73
|
+
const deleteGlobalShapeSnapshotsForConnection = (sql$1, connectionId) => {
|
|
74
|
+
runDrizzle(sql$1, sql`DELETE FROM ${sql.identifier(GLOBAL_SHAPE_SNAPSHOT_TABLE)} WHERE connection_id = ${connectionId}`);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const IDEMPOTENCY_TABLE = "__idempotency";
|
|
78
|
+
const migrateIdempotency = (sql$1) => {
|
|
79
|
+
runDrizzle(
|
|
80
|
+
sql$1,
|
|
81
|
+
sql`CREATE TABLE IF NOT EXISTS ${sql.identifier(IDEMPOTENCY_TABLE)} (
|
|
82
|
+
identity TEXT NOT NULL,
|
|
83
|
+
mutation_id TEXT NOT NULL,
|
|
84
|
+
result_json TEXT NOT NULL,
|
|
85
|
+
ts REAL NOT NULL,
|
|
86
|
+
PRIMARY KEY (identity, mutation_id)
|
|
87
|
+
)`
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
const readIdempotent = (sql$1, identity, mutationId) => {
|
|
91
|
+
const rows = runDrizzle(
|
|
92
|
+
sql$1,
|
|
93
|
+
sql`SELECT result_json, ts FROM ${sql.identifier(IDEMPOTENCY_TABLE)} WHERE identity = ${identity} AND mutation_id = ${mutationId} LIMIT 1`
|
|
94
|
+
).toArray();
|
|
95
|
+
const row = rows[0];
|
|
96
|
+
return row === void 0 ? void 0 : { resultJson: row.result_json, ts: row.ts };
|
|
97
|
+
};
|
|
98
|
+
const writeIdempotent = (sql$1, identity, mutationId, resultJson, ts) => {
|
|
99
|
+
runDrizzle(
|
|
100
|
+
sql$1,
|
|
101
|
+
sql`INSERT OR IGNORE INTO ${sql.identifier(IDEMPOTENCY_TABLE)} (identity, mutation_id, result_json, ts) VALUES (${identity}, ${mutationId}, ${resultJson}, ${ts})`
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
const trimIdempotent = (sql$1, olderThanTs) => {
|
|
105
|
+
runDrizzle(sql$1, sql`DELETE FROM ${sql.identifier(IDEMPOTENCY_TABLE)} WHERE ts < ${olderThanTs}`);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export { CLIENT_WATERMARK_TABLE as C, GLOBAL_SHAPE_SNAPSHOT_TABLE as G, IDEMPOTENCY_TABLE as I, migrateIdempotency as a, migrateGlobalShapeSnapshot as b, advanceClientWatermark as c, deleteGlobalShapeSnapshot as d, deleteGlobalShapeSnapshotsForConnection as e, readGlobalShapeSnapshot as f, readIdempotent as g, writeIdempotent as h, migrateClientWatermark as m, readClientWatermark as r, trimIdempotent as t, writeGlobalShapeSnapshot as w };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { sql } from 'drizzle-orm';
|
|
2
|
+
import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
|
|
3
|
+
import { D as DOC_COLUMN, r as rowToDocument, j as jsonPathSql } from './do-sql-CGAgiQUz.mjs';
|
|
4
|
+
import { compileWhereSql } from './compileWhereSql-DE6yfRcQ.mjs';
|
|
5
|
+
import { s as serializeSqlValue } from './serialize-sql-BlRUoiQe.mjs';
|
|
6
|
+
|
|
7
|
+
const shapeWhereStrategy = { fieldRef: jsonPathSql, serialize: serializeSqlValue };
|
|
8
|
+
const idInClause = (ids) => {
|
|
9
|
+
if (ids.length === 0) {
|
|
10
|
+
return void 0;
|
|
11
|
+
}
|
|
12
|
+
return sql`id IN (${sql.join(
|
|
13
|
+
ids.map((id) => sql`${id}`),
|
|
14
|
+
sql`, `
|
|
15
|
+
)})`;
|
|
16
|
+
};
|
|
17
|
+
const composeWhere = (effectiveWhere, idRestriction) => {
|
|
18
|
+
const conditions = [];
|
|
19
|
+
if (idRestriction) {
|
|
20
|
+
conditions.push(idRestriction);
|
|
21
|
+
}
|
|
22
|
+
const compiled = compileWhereSql(effectiveWhere, shapeWhereStrategy);
|
|
23
|
+
if (compiled) {
|
|
24
|
+
conditions.push(compiled);
|
|
25
|
+
}
|
|
26
|
+
if (conditions.length === 0) {
|
|
27
|
+
return sql``;
|
|
28
|
+
}
|
|
29
|
+
return sql` WHERE ${sql.join(conditions, sql` AND `)}`;
|
|
30
|
+
};
|
|
31
|
+
const selectShapeRows = (sql$1, table, effectiveWhere) => {
|
|
32
|
+
const whereClause = composeWhere(effectiveWhere, void 0);
|
|
33
|
+
const rows = runDrizzle(sql$1, sql`SELECT id, _creationTime, ${sql.identifier(DOC_COLUMN)} FROM ${sql.identifier(table)}${whereClause}`).toArray();
|
|
34
|
+
const result = [];
|
|
35
|
+
for (const row of rows) {
|
|
36
|
+
const doc = rowToDocument(row);
|
|
37
|
+
const { id } = row;
|
|
38
|
+
if (doc !== void 0 && typeof id === "string") {
|
|
39
|
+
result.push({ doc, id });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
const selectShapeMemberIds = (sql$1, table, effectiveWhere, ids) => {
|
|
45
|
+
if (ids.length === 0) {
|
|
46
|
+
return /* @__PURE__ */ new Set();
|
|
47
|
+
}
|
|
48
|
+
const whereClause = composeWhere(effectiveWhere, idInClause(ids));
|
|
49
|
+
const rows = runDrizzle(sql$1, sql`SELECT id FROM ${sql.identifier(table)}${whereClause}`).toArray();
|
|
50
|
+
return new Set(rows.map((row) => row.id));
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export { selectShapeMemberIds as a, selectShapeRows as s };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { stableStringify } from './stableStringify-mC40mZts.mjs';
|
|
2
|
+
|
|
3
|
+
const projectExternalSourceRow = (row, columns) => {
|
|
4
|
+
const projected = { _id: row._id };
|
|
5
|
+
if (columns) {
|
|
6
|
+
for (const key of columns) {
|
|
7
|
+
if (Object.hasOwn(row, key)) {
|
|
8
|
+
projected[key] = row[key];
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return projected;
|
|
12
|
+
}
|
|
13
|
+
for (const [key, value] of Object.entries(row)) {
|
|
14
|
+
if (key !== "_id" && key !== "_creationTime") {
|
|
15
|
+
projected[key] = value;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return projected;
|
|
19
|
+
};
|
|
20
|
+
const diffExternalSource = (pulled, baseline, options) => {
|
|
21
|
+
const { columns, table } = options;
|
|
22
|
+
const changes = [];
|
|
23
|
+
const nextBaseline = /* @__PURE__ */ new Map();
|
|
24
|
+
for (const source of pulled) {
|
|
25
|
+
const value = projectExternalSourceRow(source, columns);
|
|
26
|
+
const id = String(value._id);
|
|
27
|
+
const json = stableStringify(value);
|
|
28
|
+
nextBaseline.set(id, json);
|
|
29
|
+
const before = baseline.get(id);
|
|
30
|
+
if (before === void 0) {
|
|
31
|
+
changes.push({ doc: value, id, op: "insert", seq: 0, table, ts: 0 });
|
|
32
|
+
} else if (before !== json) {
|
|
33
|
+
changes.push({ doc: value, id, op: "update", seq: 0, table, ts: 0 });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
for (const id of baseline.keys()) {
|
|
37
|
+
if (!nextBaseline.has(id)) {
|
|
38
|
+
changes.push({ id, op: "delete", seq: 0, table, ts: 0 });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { changes, nextBaseline };
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export { diffExternalSource, projectExternalSourceRow };
|
|
@@ -2,6 +2,7 @@ import { sql } from 'drizzle-orm';
|
|
|
2
2
|
import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
|
|
3
3
|
|
|
4
4
|
const DOC_COLUMN = "__doc__";
|
|
5
|
+
const geoTableName = (table, indexName) => `${table}__geo_${indexName}`;
|
|
5
6
|
const quoteIdentifier = (name) => `"${name.replaceAll('"', '""')}"`;
|
|
6
7
|
const jsonPath = (field) => {
|
|
7
8
|
if (field === "_id" || field === "id") {
|
|
@@ -84,4 +85,4 @@ const isFtsAvailable = (sql$1) => {
|
|
|
84
85
|
return available;
|
|
85
86
|
};
|
|
86
87
|
|
|
87
|
-
export { AGG_KEY as A, DOC_COLUMN as D, AGG_VALUE as a, AGG_COUNT as b, createIndexSql as c, aggUpsertSql as d, qualifiedJsonPathSql as e, isFtsAvailable as i, jsonPathSql as j, quoteIdentifier as q, rowToDocument as r, tableColumns as t };
|
|
88
|
+
export { AGG_KEY as A, DOC_COLUMN as D, AGG_VALUE as a, AGG_COUNT as b, createIndexSql as c, aggUpsertSql as d, qualifiedJsonPathSql as e, geoTableName as g, isFtsAvailable as i, jsonPathSql as j, quoteIdentifier as q, rowToDocument as r, tableColumns as t };
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { toErrorBody } from '@lunora/errors';
|
|
2
|
+
|
|
1
3
|
const DEFAULT_BATCH_SIZE = 200;
|
|
2
4
|
const selectExportTables = (schema, requested) => {
|
|
3
5
|
const isShardLocal = (table) => {
|
|
@@ -107,9 +109,8 @@ const importOneRow = async (writer, schema, row, line) => {
|
|
|
107
109
|
await writer.insert(table, doc, { allowExplicitId: true });
|
|
108
110
|
return { kind: "inserted", table };
|
|
109
111
|
} catch (error) {
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
return { error: { code, line, message, table }, kind: "error" };
|
|
112
|
+
const { body } = toErrorBody(error, { fallbackCode: "INSERT_FAILED" });
|
|
113
|
+
return { error: { code: body.code, line, message: body.message, table }, kind: "error" };
|
|
113
114
|
}
|
|
114
115
|
};
|
|
115
116
|
const importShardRows = async (writer, schema, args) => {
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { LunoraError } from '@lunora/errors';
|
|
2
|
+
import { sql } from 'drizzle-orm';
|
|
3
|
+
import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
|
|
4
|
+
import { runExternalSourceTick, materializeExternalRowsIncremental } from './materializeExternalRows-BtEGs1Fv.mjs';
|
|
5
|
+
|
|
6
|
+
const SOURCE_CURSOR_TABLE = "__lunora_source_cursor";
|
|
7
|
+
const serializeCursor = (value) => {
|
|
8
|
+
if (value instanceof Date) {
|
|
9
|
+
return `d:${value.toISOString()}`;
|
|
10
|
+
}
|
|
11
|
+
if (typeof value === "bigint") {
|
|
12
|
+
return `b:${value.toString()}`;
|
|
13
|
+
}
|
|
14
|
+
if (typeof value === "number") {
|
|
15
|
+
return `n:${value.toString()}`;
|
|
16
|
+
}
|
|
17
|
+
return `s:${value}`;
|
|
18
|
+
};
|
|
19
|
+
const deserializeCursor = (text) => {
|
|
20
|
+
const rest = text.slice(2);
|
|
21
|
+
switch (text[0]) {
|
|
22
|
+
case "b": {
|
|
23
|
+
return BigInt(rest);
|
|
24
|
+
}
|
|
25
|
+
case "d": {
|
|
26
|
+
return new Date(rest);
|
|
27
|
+
}
|
|
28
|
+
case "n": {
|
|
29
|
+
return Number(rest);
|
|
30
|
+
}
|
|
31
|
+
default: {
|
|
32
|
+
return rest;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const INTEGER_STRING = /^-?\d+$/;
|
|
37
|
+
const DECIMAL_STRING = /^-?\d+(?:\.\d+)?$/;
|
|
38
|
+
const cursorAfter = (a, b) => {
|
|
39
|
+
if (a instanceof Date && b instanceof Date) {
|
|
40
|
+
return a.getTime() > b.getTime();
|
|
41
|
+
}
|
|
42
|
+
if (typeof a === "bigint" && typeof b === "bigint") {
|
|
43
|
+
return a > b;
|
|
44
|
+
}
|
|
45
|
+
if (typeof a === "number" && typeof b === "number") {
|
|
46
|
+
return a > b;
|
|
47
|
+
}
|
|
48
|
+
if (typeof a === "string" && typeof b === "string" && DECIMAL_STRING.test(a) && DECIMAL_STRING.test(b)) {
|
|
49
|
+
if (INTEGER_STRING.test(a) && INTEGER_STRING.test(b)) {
|
|
50
|
+
return BigInt(a) > BigInt(b);
|
|
51
|
+
}
|
|
52
|
+
return Number(a) > Number(b);
|
|
53
|
+
}
|
|
54
|
+
return String(a) > String(b);
|
|
55
|
+
};
|
|
56
|
+
const maxCursorValue = (rows, column, current) => {
|
|
57
|
+
let best = current === null ? void 0 : deserializeCursor(current);
|
|
58
|
+
for (const row of rows) {
|
|
59
|
+
const raw = row[column];
|
|
60
|
+
if (raw === null || raw === void 0) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const value = raw;
|
|
64
|
+
if (best === void 0 || cursorAfter(value, best)) {
|
|
65
|
+
best = value;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return best === void 0 ? null : serializeCursor(best);
|
|
69
|
+
};
|
|
70
|
+
const migrateSourceCursor = (sql$1) => {
|
|
71
|
+
runDrizzle(
|
|
72
|
+
sql$1,
|
|
73
|
+
sql`CREATE TABLE IF NOT EXISTS ${sql.identifier(SOURCE_CURSOR_TABLE)} (
|
|
74
|
+
table_name TEXT NOT NULL,
|
|
75
|
+
shard_key TEXT NOT NULL,
|
|
76
|
+
watermark TEXT,
|
|
77
|
+
last_reconcile_ms INTEGER,
|
|
78
|
+
PRIMARY KEY (table_name, shard_key)
|
|
79
|
+
)`
|
|
80
|
+
);
|
|
81
|
+
};
|
|
82
|
+
const readSourceCursor = (sql$1, table, shardKey) => {
|
|
83
|
+
const rows = runDrizzle(
|
|
84
|
+
sql$1,
|
|
85
|
+
sql`SELECT watermark, last_reconcile_ms FROM ${sql.identifier(SOURCE_CURSOR_TABLE)} WHERE table_name = ${table} AND shard_key = ${shardKey} LIMIT 1`
|
|
86
|
+
).toArray();
|
|
87
|
+
const row = rows[0];
|
|
88
|
+
return { lastReconcileMs: row?.last_reconcile_ms ?? null, watermark: row?.watermark ?? null };
|
|
89
|
+
};
|
|
90
|
+
const writeSourceCursor = (sql$1, table, shardKey, state) => {
|
|
91
|
+
runDrizzle(
|
|
92
|
+
sql$1,
|
|
93
|
+
sql`INSERT INTO ${sql.identifier(SOURCE_CURSOR_TABLE)} (table_name, shard_key, watermark, last_reconcile_ms)
|
|
94
|
+
VALUES (${table}, ${shardKey}, ${state.watermark}, ${state.lastReconcileMs})
|
|
95
|
+
ON CONFLICT(table_name, shard_key) DO UPDATE SET watermark = excluded.watermark, last_reconcile_ms = excluded.last_reconcile_ms`
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const normalizeSourceValue = (value) => {
|
|
100
|
+
if (value instanceof Date) {
|
|
101
|
+
return value.toISOString();
|
|
102
|
+
}
|
|
103
|
+
if (typeof value === "bigint") {
|
|
104
|
+
return String(value);
|
|
105
|
+
}
|
|
106
|
+
if (Array.isArray(value)) {
|
|
107
|
+
return value.map((element) => normalizeSourceValue(element));
|
|
108
|
+
}
|
|
109
|
+
if (value !== null && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
|
|
110
|
+
return normalizeSourceDocument(value);
|
|
111
|
+
}
|
|
112
|
+
return value;
|
|
113
|
+
};
|
|
114
|
+
const normalizeSourceDocument = (document) => {
|
|
115
|
+
const normalized = {};
|
|
116
|
+
for (const [key, value] of Object.entries(document)) {
|
|
117
|
+
normalized[key] = normalizeSourceValue(value);
|
|
118
|
+
}
|
|
119
|
+
return normalized;
|
|
120
|
+
};
|
|
121
|
+
const liftSourceId = (row, options = {}) => {
|
|
122
|
+
const { idColumn = "id", map } = options;
|
|
123
|
+
const idValue = row[idColumn];
|
|
124
|
+
if (idValue === void 0 || idValue === null) {
|
|
125
|
+
throw new LunoraError("INTERNAL", `external-source: row is missing id column "${idColumn}"`);
|
|
126
|
+
}
|
|
127
|
+
if (typeof idValue !== "string" && typeof idValue !== "number" && typeof idValue !== "bigint") {
|
|
128
|
+
throw new TypeError(`external-source: id column "${idColumn}" must be a string or number`);
|
|
129
|
+
}
|
|
130
|
+
const id = String(idValue);
|
|
131
|
+
if (map) {
|
|
132
|
+
return normalizeSourceDocument({ ...map(row), _id: id });
|
|
133
|
+
}
|
|
134
|
+
const body = {};
|
|
135
|
+
for (const [key, value] of Object.entries(row)) {
|
|
136
|
+
if (key !== idColumn) {
|
|
137
|
+
body[key] = value;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return normalizeSourceDocument({ ...body, _id: id });
|
|
141
|
+
};
|
|
142
|
+
const isSourceDue = (refresh, lastPolledMs, nowMs) => {
|
|
143
|
+
if (refresh === "manual") {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
if (refresh === void 0 || lastPolledMs === void 0) {
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
return nowMs - lastPolledMs >= refresh.everyMs;
|
|
150
|
+
};
|
|
151
|
+
const pullAndLift = async (client, query, parameters, source) => {
|
|
152
|
+
const rows = await client.query(query, parameters);
|
|
153
|
+
const documents = rows.map((row) => liftSourceId(row, { idColumn: source.idColumn, map: source.map }));
|
|
154
|
+
return { documents, rows };
|
|
155
|
+
};
|
|
156
|
+
const pullExternalSourceTick = async (sql, writer, client, table, source, shardKey) => {
|
|
157
|
+
const parameters = source.tenantBy ? source.tenantBy(shardKey) : [];
|
|
158
|
+
const { documents } = await pullAndLift(client, source.query, parameters, source);
|
|
159
|
+
return runExternalSourceTick(sql, writer, documents, { columns: source.columns, table });
|
|
160
|
+
};
|
|
161
|
+
const isSoftDeleted = (row, column) => {
|
|
162
|
+
const value = row[column];
|
|
163
|
+
return value !== null && value !== void 0 && value !== false && value !== 0;
|
|
164
|
+
};
|
|
165
|
+
const pullExternalSourceIncrementalTick = async (sql, writer, client, table, source, shardKey, nowMs) => {
|
|
166
|
+
const { cursor } = source;
|
|
167
|
+
if (!cursor) {
|
|
168
|
+
throw new LunoraError(
|
|
169
|
+
"INTERNAL",
|
|
170
|
+
`external-source: table "${table}" is mode "incremental" but has no \`cursor\` — this should have been rejected at defineSchema`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
migrateSourceCursor(sql);
|
|
174
|
+
const state = readSourceCursor(sql, table, shardKey);
|
|
175
|
+
const tenantParameters = source.tenantBy ? source.tenantBy(shardKey) : [];
|
|
176
|
+
const reconcileDue = source.reconcileEveryMs !== void 0 && (state.lastReconcileMs === null || nowMs - state.lastReconcileMs >= source.reconcileEveryMs);
|
|
177
|
+
const fullPull = state.watermark === null || reconcileDue;
|
|
178
|
+
let slice;
|
|
179
|
+
let applied;
|
|
180
|
+
if (state.watermark === null || reconcileDue) {
|
|
181
|
+
slice = await pullAndLift(client, source.query, tenantParameters, source);
|
|
182
|
+
const { softDeleteColumn } = source;
|
|
183
|
+
const documents = softDeleteColumn ? slice.documents.filter((_document, index) => {
|
|
184
|
+
const row = slice.rows[index];
|
|
185
|
+
return !(row && isSoftDeleted(row, softDeleteColumn));
|
|
186
|
+
}) : slice.documents;
|
|
187
|
+
({ applied } = await runExternalSourceTick(sql, writer, documents, { columns: source.columns, table }));
|
|
188
|
+
} else {
|
|
189
|
+
slice = await pullAndLift(client, cursor.query, [...tenantParameters, deserializeCursor(state.watermark)], source);
|
|
190
|
+
const { softDeleteColumn } = source;
|
|
191
|
+
const deletedIds = softDeleteColumn ? new Set(
|
|
192
|
+
slice.documents.flatMap((document, index) => {
|
|
193
|
+
const row = slice.rows[index];
|
|
194
|
+
return row && isSoftDeleted(row, softDeleteColumn) ? [String(document._id)] : [];
|
|
195
|
+
})
|
|
196
|
+
) : void 0;
|
|
197
|
+
({ applied } = await materializeExternalRowsIncremental(writer, slice.documents, { columns: source.columns, deletedIds, table }));
|
|
198
|
+
}
|
|
199
|
+
const watermark = maxCursorValue(slice.rows, cursor.column, state.watermark);
|
|
200
|
+
if (fullPull && watermark === null && slice.rows.length > 0) {
|
|
201
|
+
throw new LunoraError(
|
|
202
|
+
"INTERNAL",
|
|
203
|
+
`external-source: table "${table}" (mode "incremental") pulled ${String(slice.rows.length)} rows but none carry the cursor column "${cursor.column}" — the seed \`query\` must project it (matching \`cursor.query\`'s alias), or the watermark can never advance.`
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
const noRowCarriesCursor = slice.rows.length > 0 && slice.rows.every((row) => row[cursor.column] === null || row[cursor.column] === void 0);
|
|
207
|
+
if (!fullPull && noRowCarriesCursor) {
|
|
208
|
+
throw new LunoraError(
|
|
209
|
+
"INTERNAL",
|
|
210
|
+
`external-source: table "${table}" (mode "incremental") pulled ${String(slice.rows.length)} rows but none carry the cursor column "${cursor.column}" — \`cursor.query\` must project it (matching the seed \`query\`'s alias), or the watermark can never advance and every tick re-pulls the same stranded slice.`
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
writeSourceCursor(sql, table, shardKey, { lastReconcileMs: fullPull ? nowMs : state.lastReconcileMs, watermark });
|
|
214
|
+
return { applied };
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
export { isSoftDeleted, isSourceDue, liftSourceId, normalizeSourceValue, pullExternalSourceIncrementalTick, pullExternalSourceTick };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { applyCdcChanges } from './CDC_LOG_TABLE-uwOJxJJZ.mjs';
|
|
2
|
+
import { s as selectShapeRows } from './ctx-db-shapes-DTeFiHYS.mjs';
|
|
3
|
+
import { diffExternalSource, projectExternalSourceRow } from './diffExternalSource-CovHfdyo.mjs';
|
|
4
|
+
import { stableStringify } from './stableStringify-mC40mZts.mjs';
|
|
5
|
+
|
|
6
|
+
const materializeExternalRows = async (writer, pulled, baseline, options) => {
|
|
7
|
+
const { changes, nextBaseline } = diffExternalSource(pulled, baseline, options);
|
|
8
|
+
await applyCdcChanges(writer, changes);
|
|
9
|
+
return { applied: changes.length, nextBaseline };
|
|
10
|
+
};
|
|
11
|
+
const materializeExternalRowsIncremental = async (writer, pulled, options) => {
|
|
12
|
+
const { columns, deletedIds, table } = options;
|
|
13
|
+
const changes = [];
|
|
14
|
+
for (const source of pulled) {
|
|
15
|
+
const value = projectExternalSourceRow(source, columns);
|
|
16
|
+
const id = String(value._id);
|
|
17
|
+
if (deletedIds?.has(id)) {
|
|
18
|
+
const existing = await writer.get(id, table);
|
|
19
|
+
if (existing) {
|
|
20
|
+
changes.push({ id, op: "delete", seq: 0, table, ts: 0 });
|
|
21
|
+
}
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const stored = await writer.get(id, table);
|
|
25
|
+
if (stored && stableStringify(projectExternalSourceRow({ ...stored, _id: id }, columns)) === stableStringify(value)) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
changes.push({ doc: value, id, op: "insert", seq: 0, table, ts: 0 });
|
|
29
|
+
}
|
|
30
|
+
await applyCdcChanges(writer, changes);
|
|
31
|
+
return { applied: changes.length };
|
|
32
|
+
};
|
|
33
|
+
const readExternalSourceBaseline = (sql, table, columns) => {
|
|
34
|
+
const baseline = /* @__PURE__ */ new Map();
|
|
35
|
+
for (const { doc, id } of selectShapeRows(sql, table, void 0)) {
|
|
36
|
+
baseline.set(id, stableStringify(projectExternalSourceRow({ ...doc, _id: id }, columns)));
|
|
37
|
+
}
|
|
38
|
+
return baseline;
|
|
39
|
+
};
|
|
40
|
+
const runExternalSourceTick = async (sql, writer, pulled, options) => {
|
|
41
|
+
const baseline = readExternalSourceBaseline(sql, options.table, options.columns);
|
|
42
|
+
return materializeExternalRows(writer, pulled, baseline, options);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export { materializeExternalRows, materializeExternalRowsIncremental, readExternalSourceBaseline, runExternalSourceTick };
|
package/dist/packem_shared/{runShardMigrations-PabobOjF.mjs → runShardMigrations-BVax6rYu.mjs}
RENAMED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { sql } from 'drizzle-orm';
|
|
2
2
|
import { aggregateTableName } from './aggregateTableName-CxNqY1Sl.mjs';
|
|
3
|
-
import { migrateCdcLog, migrateCdcMeta } from './CDC_LOG_TABLE-
|
|
4
|
-
import { m as migrateIdempotency } from './ctx-db-idempotency-
|
|
3
|
+
import { migrateCdcLog, migrateCdcMeta } from './CDC_LOG_TABLE-uwOJxJJZ.mjs';
|
|
4
|
+
import { m as migrateClientWatermark, a as migrateIdempotency, b as migrateGlobalShapeSnapshot } from './ctx-db-idempotency-BdcNpvY4.mjs';
|
|
5
5
|
import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
|
|
6
|
-
import { D as DOC_COLUMN, j as jsonPathSql, c as createIndexSql, t as tableColumns, i as isFtsAvailable, A as AGG_KEY, a as AGG_VALUE, b as AGG_COUNT } from './do-sql-
|
|
7
|
-
import {
|
|
6
|
+
import { D as DOC_COLUMN, j as jsonPathSql, c as createIndexSql, t as tableColumns, i as isFtsAvailable, A as AGG_KEY, a as AGG_VALUE, b as AGG_COUNT, g as geoTableName } from './do-sql-CGAgiQUz.mjs';
|
|
7
|
+
import { sortColumnName, rankTableName } from './RANK_TIEBREAK-CXhdcA1o.mjs';
|
|
8
8
|
import { ftsTableName } from './buildFtsMatch-BLEMawrp.mjs';
|
|
9
9
|
|
|
10
10
|
const migrateSecondaryIndexes = (sql$1, tableName, definition) => {
|
|
@@ -36,6 +36,20 @@ const migrateSearchIndexes = (sql$1, tableName, definition) => {
|
|
|
36
36
|
);
|
|
37
37
|
}
|
|
38
38
|
};
|
|
39
|
+
const migrateGeoIndexes = (sql$1, tableName, definition) => {
|
|
40
|
+
if (!definition.geoIndexes) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
for (const index of definition.geoIndexes) {
|
|
44
|
+
const geoTable = geoTableName(tableName, index.name);
|
|
45
|
+
runDrizzle(
|
|
46
|
+
sql$1,
|
|
47
|
+
sql`CREATE TABLE IF NOT EXISTS ${sql.identifier(geoTable)} (${sql.identifier("__id__")} TEXT PRIMARY KEY, ${sql.identifier("__geohash__")} TEXT NOT NULL, ${sql.identifier("__lat__")} REAL NOT NULL, ${sql.identifier("__lng__")} REAL NOT NULL)`
|
|
48
|
+
);
|
|
49
|
+
const btreeName = `${tableName}__geo_${index.name}__btree`;
|
|
50
|
+
runDrizzle(sql$1, createIndexSql(btreeName, geoTable, sql`${sql.identifier("__geohash__")} ASC, ${sql.identifier("__id__")} ASC`, false));
|
|
51
|
+
}
|
|
52
|
+
};
|
|
39
53
|
const migrateAggregateIndexes = (sql$1, tableName, definition) => {
|
|
40
54
|
if (!definition.aggregateIndexes) {
|
|
41
55
|
return;
|
|
@@ -90,14 +104,17 @@ const runShardMigrations = (sql$1, schema, options = {}) => {
|
|
|
90
104
|
);
|
|
91
105
|
migrateSecondaryIndexes(sql$1, tableName, definition);
|
|
92
106
|
migrateSearchIndexes(sql$1, tableName, definition);
|
|
107
|
+
migrateGeoIndexes(sql$1, tableName, definition);
|
|
93
108
|
migrateAggregateIndexes(sql$1, tableName, definition);
|
|
94
109
|
migrateRankIndexes(sql$1, tableName, definition);
|
|
95
110
|
}
|
|
96
111
|
if (options.cdc) {
|
|
97
112
|
migrateCdcLog(sql$1);
|
|
98
113
|
migrateCdcMeta(sql$1);
|
|
114
|
+
migrateClientWatermark(sql$1);
|
|
99
115
|
}
|
|
100
116
|
migrateIdempotency(sql$1);
|
|
117
|
+
migrateGlobalShapeSnapshot(sql$1);
|
|
101
118
|
};
|
|
102
119
|
|
|
103
120
|
export { runShardMigrations };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { sql } from 'drizzle-orm';
|
|
2
|
+
import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
|
|
3
|
+
import { j as jsonPathSql } from './do-sql-CGAgiQUz.mjs';
|
|
4
|
+
|
|
5
|
+
const selectExpiredIds = (sql$1, spec, now, limit) => {
|
|
6
|
+
const cutoff = now - (spec.after ?? 0);
|
|
7
|
+
const conditions = [sql`${jsonPathSql(spec.field)} IS NOT NULL`, sql`${jsonPathSql(spec.field)} < ${cutoff}`];
|
|
8
|
+
if (spec.softDeleteField !== void 0) {
|
|
9
|
+
conditions.push(sql`${jsonPathSql(spec.softDeleteField)} IS NULL`);
|
|
10
|
+
}
|
|
11
|
+
const query = sql`SELECT id FROM ${sql.identifier(spec.table)} WHERE ${sql.join(conditions, sql` AND `)} LIMIT ${sql.raw(String(Math.max(0, Math.floor(limit)) + 1))}`;
|
|
12
|
+
const rows = runDrizzle(sql$1, query).toArray();
|
|
13
|
+
const hasMore = rows.length > limit;
|
|
14
|
+
const ids = rows.slice(0, limit).map((row) => row.id);
|
|
15
|
+
return { hasMore, ids };
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export { selectExpiredIds };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const serializeSqlValue = (value) => {
|
|
2
|
+
if (typeof value === "boolean") {
|
|
3
|
+
return value ? 1 : 0;
|
|
4
|
+
}
|
|
5
|
+
if (value === null || typeof value === "string" || typeof value === "number") {
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
8
|
+
if (typeof value === "bigint") {
|
|
9
|
+
return value.toString();
|
|
10
|
+
}
|
|
11
|
+
return JSON.stringify(value);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export { serializeSqlValue as s };
|
package/dist/packem_shared/{serveRelationFanout-CFBKWJ8Q.mjs → serveRelationFanout-k7HTh3CC.mjs}
RENAMED
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LunoraError } from '@lunora/errors';
|
|
2
|
+
import { RELATION_FUNCTION_PREFIX } from './ADMIN_FUNCTIONS-CnZkbXH_.mjs';
|
|
2
3
|
|
|
3
4
|
const serveRelationFanout = async (schema, database, functionPath, args) => {
|
|
4
5
|
const table = typeof args["table"] === "string" ? args["table"] : "";
|
|
5
6
|
const definition = schema.tables[table];
|
|
6
7
|
if (!definition) {
|
|
7
|
-
throw
|
|
8
|
+
throw new LunoraError("UNKNOWN_TABLE", `${RELATION_FUNCTION_PREFIX} unknown table "${table}"`, { status: 404 });
|
|
8
9
|
}
|
|
9
10
|
if (definition.shardMode?.kind === "global") {
|
|
10
|
-
throw
|
|
11
|
-
code: "BAD_REQUEST",
|
|
12
|
-
name: "LunoraError",
|
|
13
|
-
status: 400
|
|
14
|
-
});
|
|
11
|
+
throw new LunoraError("BAD_REQUEST", `${RELATION_FUNCTION_PREFIX} table "${table}" is global, not shard-local`);
|
|
15
12
|
}
|
|
16
13
|
const where = args["where"] ?? void 0;
|
|
17
14
|
if (functionPath === `${RELATION_FUNCTION_PREFIX}count`) {
|