@lunora/do 1.0.0-alpha.44 → 1.0.0-alpha.45

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.
Files changed (112) hide show
  1. package/dist/index.d.mts +55 -1
  2. package/dist/index.d.ts +55 -1
  3. package/dist/index.mjs +1 -44
  4. package/dist/packem_shared/ADMIN_FUNCTIONS-PxZ8Lr9e.mjs +1 -0
  5. package/dist/packem_shared/AGGREGATE_SQL_FUNCTION-QYaiuty4.mjs +1 -0
  6. package/dist/packem_shared/AUTH_METRICS_BUCKETS_TABLE-D0wNaez7.mjs +22 -0
  7. package/dist/packem_shared/CDC_LOG_TABLE-E_J5LPoK.mjs +8 -0
  8. package/dist/packem_shared/ConflictError-C8GtJmjS.mjs +1 -0
  9. package/dist/packem_shared/CountRlsUnsupportedError-Cl8XpYDL.mjs +1 -0
  10. package/dist/packem_shared/DATA_MIGRATION_STATE_TABLE-CaO6L0Ee.mjs +31 -0
  11. package/dist/packem_shared/DEFAULT_MAX_RELATION_KEYS-BRdmX7WW.mjs +1 -0
  12. package/dist/packem_shared/FUNCTION_METRICS_BUCKETS_TABLE-BPPeqM11.mjs +56 -0
  13. package/dist/packem_shared/GEO_DEFAULT_PRECISION-CWAm_oYY.mjs +1 -0
  14. package/dist/packem_shared/LogBuffer-bIvCelI-.mjs +1 -0
  15. package/dist/packem_shared/MAIL_RETENTION-KmozO2NQ.mjs +18 -0
  16. package/dist/packem_shared/MAX_SQL_ROWS-Bdu25ASB.mjs +2 -0
  17. package/dist/packem_shared/MIN_ADMIN_TOKEN_LENGTH-Ch6OW6_3.mjs +1 -0
  18. package/dist/packem_shared/NotFoundError-J3tjf4Uo.mjs +1 -0
  19. package/dist/packem_shared/NotUniqueError-BcP6GOem.mjs +1 -0
  20. package/dist/packem_shared/RANK_TIEBREAK-DtX8zQyc.mjs +1 -0
  21. package/dist/packem_shared/RLS_UNWRAP_SYMBOL-C6_WX3dG.mjs +1 -0
  22. package/dist/packem_shared/ROOT_DO_SIZE_WARN_BYTES-CP5wvJOW.mjs +101 -0
  23. package/dist/packem_shared/ReactiveCache-1_9Rs7J_.mjs +1 -0
  24. package/dist/packem_shared/SCAN_DEP-D_yR9EeV.mjs +1 -0
  25. package/dist/packem_shared/SESSION_DO_TTL_DEFAULT-GvBy_DBz.mjs +1 -0
  26. package/dist/packem_shared/SHARD_REGISTRY_DO_NAME-Caa0fR4N.mjs +1 -0
  27. package/dist/packem_shared/aggregateTableName-G-eXyjcz.mjs +1 -0
  28. package/dist/packem_shared/applyOnDelete-BvQN7pDL.mjs +1 -0
  29. package/dist/packem_shared/applySelect-Bq2KOrkL.mjs +1 -0
  30. package/dist/packem_shared/armRestore-BNzdvQ_o.mjs +1 -0
  31. package/dist/packem_shared/backfillAggregateIndexes-BAQ3Fwwh.mjs +1 -0
  32. package/dist/packem_shared/buildFtsMatch-CV0Z7PWv.mjs +1 -0
  33. package/dist/packem_shared/compileWhereSql-BLcfs4QW.mjs +1 -0
  34. package/dist/packem_shared/constant-time-equal-BVG05Guz.mjs +1 -0
  35. package/dist/packem_shared/context-telemetry-8eigmiNQ.mjs +1 -0
  36. package/dist/packem_shared/createMetrics-CyjLFXtA.mjs +1 -0
  37. package/dist/packem_shared/createSystemReader-DcDcrtM3.mjs +1 -0
  38. package/dist/packem_shared/ctx-db-idempotency-wiVoGnpQ.mjs +19 -0
  39. package/dist/packem_shared/ctx-db-shapes-DzX_H5q8.mjs +1 -0
  40. package/dist/packem_shared/diffExternalSource-DMpkJta1.mjs +1 -0
  41. package/dist/packem_shared/do-exec-BLe9lLrN.mjs +1 -0
  42. package/dist/packem_shared/do-sql-BYIQTG3z.mjs +1 -0
  43. package/dist/packem_shared/exportShardRows-kt42wijd.mjs +1 -0
  44. package/dist/packem_shared/hasTrigger-_rexbWMO.mjs +1 -0
  45. package/dist/packem_shared/isSoftDeleted-BvhQov04.mjs +9 -0
  46. package/dist/packem_shared/json-response-wrh9TBPw.mjs +1 -0
  47. package/dist/packem_shared/materializeExternalRows-BFmT9gsw.mjs +1 -0
  48. package/dist/packem_shared/renderSql-B5lF5Jd9.mjs +1 -0
  49. package/dist/packem_shared/runShardMigrations-bxOHpfID.mjs +5 -0
  50. package/dist/packem_shared/security-audit-BKUOgE0x.mjs +1 -0
  51. package/dist/packem_shared/selectExpiredIds-BGVP3d8-.mjs +1 -0
  52. package/dist/packem_shared/serialize-sql-DiRzL7A4.mjs +1 -0
  53. package/dist/packem_shared/serveRelationFanout-Ct5D2Tbk.mjs +1 -0
  54. package/dist/packem_shared/stableStringify-BjLh4gvA.mjs +1 -0
  55. package/dist/packem_shared/stableWireKey-YEHLaX6X.mjs +1 -0
  56. package/dist/packem_shared/subscriptionListDeltas-Bs69JbA8.mjs +1 -0
  57. package/dist/packem_shared/wire-codec-Ctnni0h6.mjs +1 -0
  58. package/package.json +1 -1
  59. package/dist/packem_shared/ADMIN_FUNCTIONS-CnZkbXH_.mjs +0 -374
  60. package/dist/packem_shared/AGGREGATE_SQL_FUNCTION-CQsu2Xga.mjs +0 -56
  61. package/dist/packem_shared/AUTH_METRICS_BUCKETS_TABLE-CiHHYeJi.mjs +0 -84
  62. package/dist/packem_shared/CDC_LOG_TABLE-uwOJxJJZ.mjs +0 -107
  63. package/dist/packem_shared/ConflictError-CLoq37xH.mjs +0 -12
  64. package/dist/packem_shared/CountRlsUnsupportedError-BGxj0pgS.mjs +0 -133
  65. package/dist/packem_shared/DATA_MIGRATION_STATE_TABLE-CYwBpyTr.mjs +0 -245
  66. package/dist/packem_shared/DEFAULT_MAX_RELATION_KEYS-BEan1CRD.mjs +0 -211
  67. package/dist/packem_shared/FUNCTION_METRICS_BUCKETS_TABLE-UDNVD7FS.mjs +0 -248
  68. package/dist/packem_shared/GEO_DEFAULT_PRECISION-BWnsNmpP.mjs +0 -114
  69. package/dist/packem_shared/LogBuffer-B_Ezju_N.mjs +0 -37
  70. package/dist/packem_shared/MAIL_RETENTION-CPpgl-dX.mjs +0 -104
  71. package/dist/packem_shared/MAX_SQL_ROWS-iFAA8FbD.mjs +0 -61
  72. package/dist/packem_shared/MIN_ADMIN_TOKEN_LENGTH-CCAvoFlr.mjs +0 -1
  73. package/dist/packem_shared/NotFoundError-C70b9hLw.mjs +0 -9
  74. package/dist/packem_shared/NotUniqueError-DbtlWwcG.mjs +0 -2031
  75. package/dist/packem_shared/RANK_TIEBREAK-CXhdcA1o.mjs +0 -91
  76. package/dist/packem_shared/RLS_UNWRAP_SYMBOL-DTvHvRzY.mjs +0 -141
  77. package/dist/packem_shared/ROOT_DO_SIZE_WARN_BYTES-B_zt4RQE.mjs +0 -7676
  78. package/dist/packem_shared/ReactiveCache-DnSvbjil.mjs +0 -233
  79. package/dist/packem_shared/SCAN_DEP-DLJF8dsj.mjs +0 -19
  80. package/dist/packem_shared/SESSION_DO_TTL_DEFAULT-BnSKgVO4.mjs +0 -169
  81. package/dist/packem_shared/SHARD_REGISTRY_DO_NAME-D99roc-r.mjs +0 -144
  82. package/dist/packem_shared/aggregateTableName-CxNqY1Sl.mjs +0 -64
  83. package/dist/packem_shared/applyOnDelete-BXSq3S70.mjs +0 -176
  84. package/dist/packem_shared/applySelect-WQY8m62C.mjs +0 -103
  85. package/dist/packem_shared/armRestore-4Px61hHS.mjs +0 -49
  86. package/dist/packem_shared/backfillAggregateIndexes-BGTwynwh.mjs +0 -81
  87. package/dist/packem_shared/buildFtsMatch-BLEMawrp.mjs +0 -38
  88. package/dist/packem_shared/compileWhereSql-DE6yfRcQ.mjs +0 -129
  89. package/dist/packem_shared/constant-time-equal-BVRWZgES.mjs +0 -12
  90. package/dist/packem_shared/context-telemetry-CDpyil58.mjs +0 -223
  91. package/dist/packem_shared/createMetrics-C4I2Yanj.mjs +0 -1
  92. package/dist/packem_shared/createSystemReader-D12eNH13.mjs +0 -82
  93. package/dist/packem_shared/ctx-db-idempotency-BdcNpvY4.mjs +0 -108
  94. package/dist/packem_shared/ctx-db-shapes-DTeFiHYS.mjs +0 -53
  95. package/dist/packem_shared/diffExternalSource-CovHfdyo.mjs +0 -44
  96. package/dist/packem_shared/do-exec-5eQy5cEi.mjs +0 -12
  97. package/dist/packem_shared/do-sql-CGAgiQUz.mjs +0 -88
  98. package/dist/packem_shared/exportShardRows-Dy3oFZ26.mjs +0 -157
  99. package/dist/packem_shared/hasTrigger-5N6_Fx0A.mjs +0 -20
  100. package/dist/packem_shared/isSoftDeleted-CFJmhjFP.mjs +0 -217
  101. package/dist/packem_shared/json-response-BdbtpOhm.mjs +0 -3
  102. package/dist/packem_shared/materializeExternalRows-BtEGs1Fv.mjs +0 -45
  103. package/dist/packem_shared/renderSql-D6eUcn2N.mjs +0 -16
  104. package/dist/packem_shared/runShardMigrations-BVax6rYu.mjs +0 -120
  105. package/dist/packem_shared/security-audit-CucgBice.mjs +0 -158
  106. package/dist/packem_shared/selectExpiredIds-C5W29Upb.mjs +0 -18
  107. package/dist/packem_shared/serialize-sql-BlRUoiQe.mjs +0 -14
  108. package/dist/packem_shared/serveRelationFanout-k7HTh3CC.mjs +0 -21
  109. package/dist/packem_shared/stableStringify-mC40mZts.mjs +0 -40
  110. package/dist/packem_shared/stableWireKey-DKuXO7T5.mjs +0 -6
  111. package/dist/packem_shared/subscriptionListDeltas-CT76bYny.mjs +0 -126
  112. package/dist/packem_shared/wire-codec-CzQc1pvf.mjs +0 -225
@@ -1,157 +0,0 @@
1
- import { toErrorBody } from '@lunora/errors';
2
-
3
- const DEFAULT_BATCH_SIZE = 200;
4
- const selectExportTables = (schema, requested) => {
5
- const isShardLocal = (table) => {
6
- const definition = schema.tables[table];
7
- if (!definition) {
8
- return false;
9
- }
10
- return definition.shardMode?.kind !== "global";
11
- };
12
- if (requested && requested.length > 0) {
13
- const filtered = [];
14
- for (const name of requested) {
15
- if (isShardLocal(name)) {
16
- filtered.push(name);
17
- }
18
- }
19
- return filtered;
20
- }
21
- const result = [];
22
- for (const name of Object.keys(schema.tables)) {
23
- if (isShardLocal(name)) {
24
- result.push(name);
25
- }
26
- }
27
- return result;
28
- };
29
- const exportShardTable = async function* (writer, table, batchSize = DEFAULT_BATCH_SIZE) {
30
- let cursor = null;
31
- let done = false;
32
- while (!done) {
33
- const page = await writer.findMany(table, { cursor, limit: batchSize });
34
- for (const record of page.page) {
35
- yield { doc: record, table };
36
- }
37
- cursor = page.continueCursor;
38
- done = page.isDone || cursor === null;
39
- }
40
- };
41
- const exportShardRows = async function* (writer, schema, args) {
42
- const tables = selectExportTables(schema, args.tables);
43
- const batchSize = args.batchSize ?? DEFAULT_BATCH_SIZE;
44
- for (const table of tables) {
45
- yield* exportShardTable(writer, table, batchSize);
46
- }
47
- };
48
- const FRAMEWORK_FIELDS = /* @__PURE__ */ new Set(["_creationTime", "_id"]);
49
- const validateAgainstShape = (definition, payload) => {
50
- for (const [field, validator] of Object.entries(definition.shape)) {
51
- const candidate = payload[field];
52
- if (candidate === void 0 && validator.kind === "optional") {
53
- continue;
54
- }
55
- const parser = validator.parse;
56
- if (typeof parser !== "function") {
57
- continue;
58
- }
59
- try {
60
- parser(candidate);
61
- } catch (error) {
62
- const message = error instanceof Error ? error.message : String(error);
63
- return `field "${field}": ${message}`;
64
- }
65
- }
66
- return void 0;
67
- };
68
- const validateImportRow = (schema, table, record) => {
69
- const definition = schema.tables[table];
70
- if (!definition) {
71
- return `unknown table: ${table}`;
72
- }
73
- if (definition.shardMode?.kind === "global") {
74
- return `table "${table}" is a global (.global()) table and is not importable through the shard import path`;
75
- }
76
- const payload = Object.fromEntries(Object.entries(record).filter(([key]) => !FRAMEWORK_FIELDS.has(key)));
77
- for (const key of Object.keys(payload)) {
78
- if (!(key in definition.shape)) {
79
- return `unexpected field "${key}": not declared in table "${table}"`;
80
- }
81
- }
82
- return validateAgainstShape(definition, payload);
83
- };
84
- const idAlreadyExists = async (writer, explicitId) => {
85
- try {
86
- const existing = await writer.get(explicitId);
87
- return existing !== null;
88
- } catch {
89
- return false;
90
- }
91
- };
92
- const importOneRow = async (writer, schema, row, line) => {
93
- const { doc, table } = row;
94
- if (typeof table !== "string" || table.length === 0) {
95
- return { error: { code: "BAD_ROW", line, message: "row is missing `table`", table }, kind: "error" };
96
- }
97
- if (!doc || typeof doc !== "object" || Array.isArray(doc)) {
98
- return { error: { code: "BAD_ROW", line, message: "row is missing or malformed `doc`", table }, kind: "error" };
99
- }
100
- const failure = validateImportRow(schema, table, doc);
101
- if (failure !== void 0) {
102
- return { error: { code: "VALIDATION_ERROR", line, message: failure, table }, kind: "error" };
103
- }
104
- const explicitId = typeof doc["_id"] === "string" ? doc["_id"] : void 0;
105
- if (explicitId !== void 0 && await idAlreadyExists(writer, explicitId)) {
106
- return { kind: "conflict" };
107
- }
108
- try {
109
- await writer.insert(table, doc, { allowExplicitId: true });
110
- return { kind: "inserted", table };
111
- } catch (error) {
112
- const { body } = toErrorBody(error, { fallbackCode: "INSERT_FAILED" });
113
- return { error: { code: body.code, line, message: body.message, table }, kind: "error" };
114
- }
115
- };
116
- const importShardRows = async (writer, schema, args) => {
117
- const errors = [];
118
- const inserted = {};
119
- let conflicts = 0;
120
- let line = (args.startLine ?? 1) - 1;
121
- for (const row of args.rows) {
122
- line += 1;
123
- const outcome = await importOneRow(writer, schema, row, line);
124
- if (outcome.kind === "error") {
125
- errors.push(outcome.error);
126
- } else if (outcome.kind === "conflict") {
127
- conflicts += 1;
128
- } else {
129
- inserted[outcome.table] = (inserted[outcome.table] ?? 0) + 1;
130
- }
131
- }
132
- return { conflicts, errors, inserted };
133
- };
134
- const parseExportShardArgs = (args) => {
135
- const tables = Array.isArray(args["tables"]) ? args["tables"].filter((entry) => typeof entry === "string") : void 0;
136
- const batchSize = typeof args["batchSize"] === "number" ? args["batchSize"] : void 0;
137
- return { batchSize, tables };
138
- };
139
- const parseImportShardArgs = (args) => {
140
- const rawRows = Array.isArray(args["rows"]) ? args["rows"] : [];
141
- const rows = [];
142
- for (const entry of rawRows) {
143
- if (!entry || typeof entry !== "object") {
144
- continue;
145
- }
146
- const candidate = entry;
147
- if (typeof candidate.table !== "string" || !candidate.doc || typeof candidate.doc !== "object" || Array.isArray(candidate.doc)) {
148
- rows.push({ doc: candidate.doc ?? {}, table: typeof candidate.table === "string" ? candidate.table : "" });
149
- continue;
150
- }
151
- rows.push({ doc: candidate.doc, table: candidate.table });
152
- }
153
- const startLine = typeof args["startLine"] === "number" ? args["startLine"] : void 0;
154
- return { rows, startLine };
155
- };
156
-
157
- export { exportShardRows, exportShardTable, importShardRows, parseExportShardArgs, parseImportShardArgs, selectExportTables, validateImportRow };
@@ -1,20 +0,0 @@
1
- const runTriggers = async (options) => {
2
- const definitions = options.schema.tables[options.tableName]?.triggerMap;
3
- if (!definitions) {
4
- return;
5
- }
6
- for (const definition of Object.values(definitions)) {
7
- if (definition.timing === options.timing && definition.op === options.op) {
8
- await definition.handler(options.ctx, options.event);
9
- }
10
- }
11
- };
12
- const hasTrigger = (schema, tableName, op) => {
13
- const definitions = schema.tables[tableName]?.triggerMap;
14
- if (!definitions) {
15
- return false;
16
- }
17
- return Object.values(definitions).some((definition) => definition.op === op);
18
- };
19
-
20
- export { hasTrigger, runTriggers };
@@ -1,217 +0,0 @@
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 };
@@ -1,3 +0,0 @@
1
- const jsonResponse = (body, status = 200, headers) => Response.json(body, { headers: { "content-type": "application/json", ...headers }, status });
2
-
3
- export { jsonResponse as j };
@@ -1,45 +0,0 @@
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 };
@@ -1,16 +0,0 @@
1
- import { sql } from 'drizzle-orm';
2
- import { MySqlDialect } from 'drizzle-orm/mysql-core';
3
- import { PgDialect } from 'drizzle-orm/pg-core';
4
- import { SQLiteSyncDialect } from 'drizzle-orm/sqlite-core';
5
-
6
- const PG_DIALECT = new PgDialect();
7
- const MYSQL_DIALECT = new MySqlDialect();
8
- const SQLITE_DIALECT = new SQLiteSyncDialect();
9
- const DIALECTS = { mysql: MYSQL_DIALECT, postgres: PG_DIALECT, sqlite: SQLITE_DIALECT };
10
- const renderSql = (engine, query) => {
11
- const { params, sql: text } = DIALECTS[engine].sqlToQuery(query);
12
- return { params, sql: text };
13
- };
14
- const param = (value) => sql`${value}`;
15
-
16
- export { param, renderSql };
@@ -1,120 +0,0 @@
1
- import { sql } from 'drizzle-orm';
2
- import { aggregateTableName } from './aggregateTableName-CxNqY1Sl.mjs';
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
- 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, g as geoTableName } from './do-sql-CGAgiQUz.mjs';
7
- import { sortColumnName, rankTableName } from './RANK_TIEBREAK-CXhdcA1o.mjs';
8
- import { ftsTableName } from './buildFtsMatch-BLEMawrp.mjs';
9
-
10
- const migrateSecondaryIndexes = (sql$1, tableName, definition) => {
11
- for (const index of definition.indexes) {
12
- const indexName = `${tableName}_${index.name}`;
13
- const expressions = sql.join(
14
- index.fields.map((field) => jsonPathSql(field)),
15
- sql`, `
16
- );
17
- runDrizzle(sql$1, createIndexSql(indexName, tableName, expressions, index.unique ?? false));
18
- }
19
- for (const [field, column] of tableColumns(definition)) {
20
- if (!column.unique) {
21
- continue;
22
- }
23
- const indexName = `${tableName}_unique_${field}`;
24
- runDrizzle(sql$1, createIndexSql(indexName, tableName, jsonPathSql(field), true));
25
- }
26
- };
27
- const migrateSearchIndexes = (sql$1, tableName, definition) => {
28
- if (!definition.searchIndexes || definition.searchIndexes.length === 0 || !isFtsAvailable(sql$1)) {
29
- return;
30
- }
31
- for (const index of definition.searchIndexes) {
32
- const ftName = ftsTableName(tableName, index.name);
33
- runDrizzle(
34
- sql$1,
35
- sql`CREATE VIRTUAL TABLE IF NOT EXISTS ${sql.identifier(ftName)} USING fts5(${sql.identifier("__text__")}, ${sql.identifier("__id__")} UNINDEXED)`
36
- );
37
- }
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
- };
53
- const migrateAggregateIndexes = (sql$1, tableName, definition) => {
54
- if (!definition.aggregateIndexes) {
55
- return;
56
- }
57
- for (const index of definition.aggregateIndexes) {
58
- const aggTable = aggregateTableName(tableName, index.name);
59
- runDrizzle(
60
- sql$1,
61
- sql`CREATE TABLE IF NOT EXISTS ${sql.identifier(aggTable)} (${AGG_KEY} TEXT PRIMARY KEY, ${AGG_VALUE} REAL, ${AGG_COUNT} INTEGER NOT NULL DEFAULT 0)`
62
- );
63
- const columns = runDrizzle(sql$1, sql`PRAGMA table_info(${sql.identifier(aggTable)})`).toArray();
64
- if (!columns.some((column) => column.name === "__count__")) {
65
- runDrizzle(sql$1, sql`ALTER TABLE ${sql.identifier(aggTable)} ADD COLUMN ${AGG_COUNT} INTEGER NOT NULL DEFAULT 0`);
66
- }
67
- }
68
- };
69
- const migrateRankIndexes = (sql$1, tableName, definition) => {
70
- if (!definition.rankIndexes) {
71
- return;
72
- }
73
- for (const index of definition.rankIndexes) {
74
- const rankTable = rankTableName(tableName, index.name);
75
- const sortColumns = index.sortBy.map((_, i) => sortColumnName(i));
76
- const columnDdls = sortColumns.map((column) => sql`${sql.identifier(column)} BLOB`);
77
- const columnPart = columnDdls.length > 0 ? sql`, ${sql.join(columnDdls, sql`, `)}` : sql``;
78
- runDrizzle(
79
- sql$1,
80
- sql`CREATE TABLE IF NOT EXISTS ${sql.identifier(rankTable)} (${sql.identifier("__id__")} TEXT PRIMARY KEY, ${sql.identifier("__partition__")} TEXT NOT NULL${columnPart})`
81
- );
82
- const orderedColumns = [sql`${sql.identifier("__partition__")} ASC`];
83
- for (const [i, column] of sortColumns.entries()) {
84
- const direction = index.sortBy[i]?.direction;
85
- orderedColumns.push(sql`${sql.identifier(column)} ${sql.raw(direction === "desc" ? "DESC" : "ASC")}`);
86
- }
87
- orderedColumns.push(sql`${sql.identifier("__id__")} ASC`);
88
- const btreeName = `${tableName}__rank_${index.name}__btree`;
89
- runDrizzle(sql$1, createIndexSql(btreeName, rankTable, sql.join(orderedColumns, sql`, `), false));
90
- }
91
- };
92
- const runShardMigrations = (sql$1, schema, options = {}) => {
93
- for (const [tableName, definition] of Object.entries(schema.tables)) {
94
- if (definition.shardMode?.kind === "global") {
95
- continue;
96
- }
97
- runDrizzle(
98
- sql$1,
99
- sql`CREATE TABLE IF NOT EXISTS ${sql.identifier(tableName)} (
100
- id TEXT PRIMARY KEY,
101
- _creationTime REAL NOT NULL,
102
- ${sql.identifier(DOC_COLUMN)} TEXT NOT NULL
103
- )`
104
- );
105
- migrateSecondaryIndexes(sql$1, tableName, definition);
106
- migrateSearchIndexes(sql$1, tableName, definition);
107
- migrateGeoIndexes(sql$1, tableName, definition);
108
- migrateAggregateIndexes(sql$1, tableName, definition);
109
- migrateRankIndexes(sql$1, tableName, definition);
110
- }
111
- if (options.cdc) {
112
- migrateCdcLog(sql$1);
113
- migrateCdcMeta(sql$1);
114
- migrateClientWatermark(sql$1);
115
- }
116
- migrateIdempotency(sql$1);
117
- migrateGlobalShapeSnapshot(sql$1);
118
- };
119
-
120
- export { runShardMigrations };