@lunora/server 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/index.mjs +3 -3
- package/dist/packem_shared/{PRESENCE_DEFAULT_TTL_MS-CwRGb41u.mjs → PRESENCE_DEFAULT_TTL_MS-Cv1UPaJj.mjs} +1 -1
- package/dist/packem_shared/{defineAggregateIndex-DCzUQLMi.mjs → defineAggregateIndex-DTULzh09.mjs} +44 -6
- package/dist/packem_shared/{mask-DSBtA3qp.mjs → mask-CZuu9WAF.mjs} +17 -0
- package/dist/types.d.mts +48 -9
- package/dist/types.d.ts +48 -9
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -10,9 +10,9 @@ export { onConnect, onDisconnect } from './packem_shared/onConnect-CIPXKPyw.mjs'
|
|
|
10
10
|
export { defineMigration } from './packem_shared/defineMigration-Hx01yIht.mjs';
|
|
11
11
|
export { defineMutator } from './packem_shared/defineMutator-EIXAWhs9.mjs';
|
|
12
12
|
export { composePluginMiddleware, defineComponent, definePlugin, defineSchemaExtension, installPlugins, mergeSchemaExtension } from './packem_shared/composePluginMiddleware-z62dttBo.mjs';
|
|
13
|
-
export { PRESENCE_DEFAULT_TTL_MS, PRESENCE_TABLE, definePresence, presenceExtension } from './packem_shared/PRESENCE_DEFAULT_TTL_MS-
|
|
13
|
+
export { PRESENCE_DEFAULT_TTL_MS, PRESENCE_TABLE, definePresence, presenceExtension } from './packem_shared/PRESENCE_DEFAULT_TTL_MS-Cv1UPaJj.mjs';
|
|
14
14
|
export { protectPublic } from './packem_shared/protectPublic-BlcGpiRc.mjs';
|
|
15
|
-
export { defineAggregateIndex, defineRankIndex, defineSchema, defineTable, defineVectorIndex } from './packem_shared/defineAggregateIndex-
|
|
15
|
+
export { defineAggregateIndex, defineRankIndex, defineSchema, defineTable, defineVectorIndex } from './packem_shared/defineAggregateIndex-DTULzh09.mjs';
|
|
16
16
|
export { defineShape } from './packem_shared/defineShape-C5scNOrf.mjs';
|
|
17
17
|
export { anyApi } from './types.mjs';
|
|
18
18
|
export { cronJobs } from '@lunora/scheduler';
|
|
@@ -20,7 +20,7 @@ export { ValidationError, v } from '@lunora/values';
|
|
|
20
20
|
export { buildRlsReadRegistry, composeShapeReadWhere } from './packem_shared/buildRlsReadRegistry-D54vUQe4.mjs';
|
|
21
21
|
export { createPolicyDsl, definePermission, definePolicies, definePolicy, defineRole } from './packem_shared/createPolicyDsl-By3QB4he.mjs';
|
|
22
22
|
export { defineStorageRule, defineStorageRules } from './packem_shared/defineStorageRule-B5nL4Z1P.mjs';
|
|
23
|
-
export { mask } from './packem_shared/mask-
|
|
23
|
+
export { mask } from './packem_shared/mask-CZuu9WAF.mjs';
|
|
24
24
|
export { rls } from './packem_shared/rls-B_ZWgslr.mjs';
|
|
25
25
|
export { storageRules } from './packem_shared/storageRules-6QxzDOcx.mjs';
|
|
26
26
|
|
|
@@ -3,7 +3,7 @@ import { initLunora } from './initLunora-CmMVk4i7.mjs';
|
|
|
3
3
|
import { LunoraError } from './LunoraError-WbxmrpxR.mjs';
|
|
4
4
|
import { onDisconnect } from './onConnect-CIPXKPyw.mjs';
|
|
5
5
|
import { defineSchemaExtension, defineComponent } from './composePluginMiddleware-z62dttBo.mjs';
|
|
6
|
-
import { defineTable } from './defineAggregateIndex-
|
|
6
|
+
import { defineTable } from './defineAggregateIndex-DTULzh09.mjs';
|
|
7
7
|
|
|
8
8
|
const DEFAULT_TTL_MS = 3e4;
|
|
9
9
|
const MAX_DATA_BYTES = 4096;
|
package/dist/packem_shared/{defineAggregateIndex-DCzUQLMi.mjs → defineAggregateIndex-DTULzh09.mjs}
RENAMED
|
@@ -259,6 +259,49 @@ const attachStandaloneIndexes = (tables, aggregateIndexes, rankIndexes) => {
|
|
|
259
259
|
table.rankIndexes.push(index);
|
|
260
260
|
}
|
|
261
261
|
};
|
|
262
|
+
const strayIncrementalKnob = (source) => {
|
|
263
|
+
if (source.cursor) {
|
|
264
|
+
return "cursor";
|
|
265
|
+
}
|
|
266
|
+
if (source.reconcileEveryMs !== void 0) {
|
|
267
|
+
return "reconcileEveryMs";
|
|
268
|
+
}
|
|
269
|
+
if (source.softDeleteColumn !== void 0) {
|
|
270
|
+
return "softDeleteColumn";
|
|
271
|
+
}
|
|
272
|
+
return void 0;
|
|
273
|
+
};
|
|
274
|
+
const validateExternalSourceMode = (name, source) => {
|
|
275
|
+
const isIncremental = source.mode === "incremental";
|
|
276
|
+
if (source.mode !== void 0 && source.mode !== "full-pull" && !isIncremental) {
|
|
277
|
+
throw new LunoraError$1(
|
|
278
|
+
"INTERNAL",
|
|
279
|
+
`defineSchema: table "${name}" uses \`mode: ${JSON.stringify(source.mode)}\` — supported modes are "full-pull" (default) and "incremental".`
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
if (!isIncremental) {
|
|
283
|
+
const strayKnob = strayIncrementalKnob(source);
|
|
284
|
+
if (strayKnob) {
|
|
285
|
+
throw new LunoraError$1(
|
|
286
|
+
"INTERNAL",
|
|
287
|
+
`defineSchema: table "${name}" sets \`${strayKnob}\` but is not \`mode: "incremental"\` — that knob only applies to incremental ingest. Set \`mode: "incremental"\` or remove \`${strayKnob}\`.`
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (!source.cursor?.column || !source.cursor.query) {
|
|
293
|
+
throw new LunoraError$1(
|
|
294
|
+
"INTERNAL",
|
|
295
|
+
`defineSchema: table "${name}" is \`mode: "incremental"\` but has no \`cursor\` — add \`cursor: { column: "updated_at", query: "… WHERE … > $N" }\` binding the watermark as the trailing parameter.`
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
if (source.reconcileEveryMs === void 0 && source.softDeleteColumn === void 0) {
|
|
299
|
+
throw new LunoraError$1(
|
|
300
|
+
"INTERNAL",
|
|
301
|
+
`defineSchema: table "${name}" is \`mode: "incremental"\` with no delete-visibility path — an incremental pull never sees upstream deletes, so it would accumulate phantom rows. Add \`reconcileEveryMs\` (a periodic full-pull sweep) or \`softDeleteColumn\` (an upstream tombstone column).`
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
};
|
|
262
305
|
const validateExternalSources = (tables) => {
|
|
263
306
|
for (const [name, table] of Object.entries(tables)) {
|
|
264
307
|
const source = table.externalSource;
|
|
@@ -277,12 +320,7 @@ const validateExternalSources = (tables) => {
|
|
|
277
320
|
`defineSchema: sourced + .shardBy() table "${name}" needs a \`tenantBy\` mapper — without it every tenant's DO would run the same unscoped query and replicate the whole multitenant table (a cross-tenant leak). Add \`tenantBy: (shardKey) => [shardKey]\` binding the shard key into the query's parameters.`
|
|
278
321
|
);
|
|
279
322
|
}
|
|
280
|
-
|
|
281
|
-
throw new LunoraError$1(
|
|
282
|
-
"INTERNAL",
|
|
283
|
-
`defineSchema: table "${name}" uses \`mode: ${JSON.stringify(source.mode)}\` — only "full-pull" (the default) is supported. Remove \`mode\` or set it to "full-pull".`
|
|
284
|
-
);
|
|
285
|
-
}
|
|
323
|
+
validateExternalSourceMode(name, source);
|
|
286
324
|
}
|
|
287
325
|
};
|
|
288
326
|
const defineSchema = (tables, vectorIndexes = {}, aggregateIndexes = {}, rankIndexes = {}) => {
|
|
@@ -208,6 +208,20 @@ const wrapDatabase = (base, perTable, context) => {
|
|
|
208
208
|
};
|
|
209
209
|
const wrapped = {
|
|
210
210
|
...base,
|
|
211
|
+
async deleteWhere(tableName, where, options) {
|
|
212
|
+
assertWhereAllowed(tableName, where, "deleteMany({ where })");
|
|
213
|
+
if (base.deleteWhere === void 0) {
|
|
214
|
+
throw new LunoraError("INTERNAL", `ctx.db.${tableName}.deleteMany({ where }) is unavailable: this writer has no where-based delete`);
|
|
215
|
+
}
|
|
216
|
+
return base.deleteWhere(tableName, where, options);
|
|
217
|
+
},
|
|
218
|
+
async patchWhere(tableName, args, options) {
|
|
219
|
+
assertWhereAllowed(tableName, args.where, "patchMany({ where })");
|
|
220
|
+
if (base.patchWhere === void 0) {
|
|
221
|
+
throw new LunoraError("INTERNAL", `ctx.db.${tableName}.patchMany({ where }) is unavailable: this writer has no where-based patch`);
|
|
222
|
+
}
|
|
223
|
+
return base.patchWhere(tableName, args, options);
|
|
224
|
+
},
|
|
211
225
|
aggregate(tableName, options) {
|
|
212
226
|
assertReductionAllowed(tableName, [options.field], "aggregate");
|
|
213
227
|
assertWhereAllowed(tableName, options.where, "aggregate");
|
|
@@ -224,6 +238,7 @@ const wrapDatabase = (base, perTable, context) => {
|
|
|
224
238
|
},
|
|
225
239
|
async findFirst(tableName, args) {
|
|
226
240
|
assertWhereAllowed(tableName, args?.where, "findFirst");
|
|
241
|
+
assertWhereAllowed(tableName, args?.baseWhere, "findFirst");
|
|
227
242
|
assertOrderByAllowed(tableName, args?.orderBy, "findFirst");
|
|
228
243
|
const row = await base.findFirst(tableName, args);
|
|
229
244
|
const columns = perTable.get(tableName);
|
|
@@ -231,6 +246,7 @@ const wrapDatabase = (base, perTable, context) => {
|
|
|
231
246
|
},
|
|
232
247
|
async findFirstOrThrow(tableName, args) {
|
|
233
248
|
assertWhereAllowed(tableName, args?.where, "findFirstOrThrow");
|
|
249
|
+
assertWhereAllowed(tableName, args?.baseWhere, "findFirstOrThrow");
|
|
234
250
|
assertOrderByAllowed(tableName, args?.orderBy, "findFirstOrThrow");
|
|
235
251
|
const row = await base.findFirstOrThrow(tableName, args);
|
|
236
252
|
const columns = perTable.get(tableName);
|
|
@@ -238,6 +254,7 @@ const wrapDatabase = (base, perTable, context) => {
|
|
|
238
254
|
},
|
|
239
255
|
async findMany(tableName, args) {
|
|
240
256
|
assertWhereAllowed(tableName, args?.where, "findMany");
|
|
257
|
+
assertWhereAllowed(tableName, args?.baseWhere, "findMany");
|
|
241
258
|
assertOrderByAllowed(tableName, args?.orderBy, "findMany");
|
|
242
259
|
const page = await base.findMany(tableName, args);
|
|
243
260
|
const columns = perTable.get(tableName);
|
package/dist/types.d.mts
CHANGED
|
@@ -28,14 +28,37 @@ type ExternalSourceRefresh = "manual" | {
|
|
|
28
28
|
everyMs: number;
|
|
29
29
|
};
|
|
30
30
|
/**
|
|
31
|
-
* Delete-detection mode for external-source ingest
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
31
|
+
* Delete-detection mode for external-source ingest (plan 077 / 136).
|
|
32
|
+
*
|
|
33
|
+
* `"full-pull"` (the default) reads the **whole** tenant membership each tick and
|
|
34
|
+
* diffs it, so it observes upstream deletes for free — but costs a full read per tick
|
|
35
|
+
* (the Phase-0 bench put the ceiling at ~10k rows).
|
|
36
|
+
*
|
|
37
|
+
* `"incremental"` pulls **only rows past a durable watermark** (`cursor`), cheap for
|
|
38
|
+
* large low-churn tables above the full-pull cap. Because an absent row then means
|
|
39
|
+
* "unchanged", not "deleted", incremental requires a delete-visibility path: either a
|
|
40
|
+
* `reconcileEveryMs` periodic full-pull sweep, or a `softDeleteColumn` whose
|
|
41
|
+
* tombstones the pull returns. `defineSchema` throws (and the
|
|
42
|
+
* `external_source_incremental_no_delete_path` advisor lint fails the build) when an
|
|
43
|
+
* incremental source declares neither.
|
|
37
44
|
*/
|
|
38
|
-
type ExternalSourceMode = "full-pull";
|
|
45
|
+
type ExternalSourceMode = "full-pull" | "incremental";
|
|
46
|
+
/**
|
|
47
|
+
* Incremental-ingest cursor (plan 136): the monotonic watermark column plus the
|
|
48
|
+
* watermark-parameterized pull query. `column` names the field in the pulled rows
|
|
49
|
+
* whose max becomes the next watermark (e.g. `"updated_at"`). `query` is a second
|
|
50
|
+
* SQL that returns only rows changed since the watermark — the watermark binds as
|
|
51
|
+
* the parameter AFTER `tenantBy`'s params (e.g. Postgres
|
|
52
|
+
* `... WHERE tenant_id = $1 AND updated_at >= $2 ORDER BY updated_at`). Prefer `>=`
|
|
53
|
+
* with the idempotent upsert apply so rows sharing the boundary timestamp are never
|
|
54
|
+
* skipped (re-pulling them is a no-op).
|
|
55
|
+
*/
|
|
56
|
+
interface ExternalSourceCursor {
|
|
57
|
+
/** The monotonic watermark column in the pulled rows; its max advances the stored watermark. */
|
|
58
|
+
column: string;
|
|
59
|
+
/** The incremental pull SQL. `tenantBy`'s params bind first, then the watermark as the trailing param. */
|
|
60
|
+
query: string;
|
|
61
|
+
}
|
|
39
62
|
/**
|
|
40
63
|
* Config for `.source(...)` (plan 077): declares a table as **materialized from an
|
|
41
64
|
* external Postgres/MySQL behind Cloudflare Hyperdrive**, not written by user
|
|
@@ -50,17 +73,33 @@ interface ExternalSourceDefinition {
|
|
|
50
73
|
binding: string;
|
|
51
74
|
/** Project the materialized rows to these columns (passed to the membership diff). Omit ⇒ the full mapped document. */
|
|
52
75
|
columns?: ReadonlyArray<string>;
|
|
76
|
+
/** **Required for `mode: "incremental"`**: the watermark column + watermark-parameterized pull query (plan 136). Rejected on a `"full-pull"` source. */
|
|
77
|
+
cursor?: ExternalSourceCursor;
|
|
53
78
|
/** Column whose value becomes the Lunora `_id`. Defaults to `"id"`. */
|
|
54
79
|
idColumn?: string;
|
|
55
80
|
/** Transform an external row into the stored document body. Omit ⇒ every selected column except `idColumn` is copied. */
|
|
56
81
|
map?: (row: Record<string, unknown>) => Record<string, unknown>;
|
|
57
|
-
/** Delete-detection mode. `"full-pull"` (the default)
|
|
82
|
+
/** Delete-detection mode. `"full-pull"` (the default) diffs the whole membership; `"incremental"` pulls past a `cursor` watermark. */
|
|
58
83
|
mode?: ExternalSourceMode;
|
|
59
84
|
/** The full tenant-membership query, with driver-native placeholders (`$1` / `?`). `tenantBy` binds its params. */
|
|
60
85
|
query: string;
|
|
86
|
+
/**
|
|
87
|
+
* **Incremental delete-visibility (plan 136)**: run a full-pull sweep at most
|
|
88
|
+
* this often (millis) to GC upstream deletes an incremental slice can't see.
|
|
89
|
+
* One of `reconcileEveryMs` / `softDeleteColumn` is required for incremental;
|
|
90
|
+
* rejected on a `"full-pull"` source.
|
|
91
|
+
*/
|
|
92
|
+
reconcileEveryMs?: number;
|
|
61
93
|
/** Poll cadence, or `"manual"`. Omit ⇒ the runtime's size-scaled default. */
|
|
62
94
|
refresh?: ExternalSourceRefresh;
|
|
63
95
|
/**
|
|
96
|
+
* **Incremental delete-visibility (plan 136)**: the upstream soft-delete
|
|
97
|
+
* tombstone column (e.g. `"deleted_at"`). When set, the incremental pull must
|
|
98
|
+
* return tombstoned rows and the ingest turns each into a local delete — an
|
|
99
|
+
* alternative to `reconcileEveryMs`. Rejected on a `"full-pull"` source.
|
|
100
|
+
*/
|
|
101
|
+
softDeleteColumn?: string;
|
|
102
|
+
/**
|
|
64
103
|
* **Mandatory under `.shardBy()`**: map this DO's shard key → the query's bound
|
|
65
104
|
* params, so a tenant DO can only ever pull its own rows. An unscoped sourced +
|
|
66
105
|
* sharded table replicates the whole multitenant table into every shard — the
|
|
@@ -1293,4 +1332,4 @@ interface ActionCtx {
|
|
|
1293
1332
|
*/
|
|
1294
1333
|
type AnyApi = Record<string, Record<string, RegisteredFunction<ArgsValidator, unknown, FunctionKind>>>;
|
|
1295
1334
|
declare const anyApi: AnyApi;
|
|
1296
|
-
export { type ActionCtx, type AggregateIndexDefinition, type AggregateOp, type AnyApi, type ArgsValidator, type AuthState, type CachePurge, type DatabaseReader, type DatabaseWriter, type DurableObjectJurisdiction, type ExternalSourceDefinition, type ExternalSourceMode, type ExternalSourceRefresh, type FunctionKind, type FunctionVisibility, type GlobalBackend, type IndexDefinition, type IndexRangeBuilder, type InferArgs, type LifecycleEvent, type LifecycleEventKind, type LunoraLogger, type MutationCtx, type OnDeleteAction, type PaginationOptions, type PaginationResult, type QueryCtx, type RankIndexDefinition, type RankSortKey, type ReadOnlyStorage, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMutation, type RegisteredQuery, type RegisteredStream, type RelationDefinition, type ScheduledFunctionDoc, type ScheduledJob, type Scheduler, type Schema, type SearchFilterBuilder, type SearchIndexDefinition, type Secrets, type SecretsStoreSecretLike, type ShardMode, type Storage, type StorageMetadata, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemTableName, type TableDefinition, type TableReader, type TableVectorIndex, type TriggerAggregateOptions, type TriggerBuilder, type TriggerCtx, type TriggerDatabase, type TriggerDefinition, type TriggerDeleteEvent, type TriggerEvent, type TriggerGroupByEntry, type TriggerGroupByOptions, type TriggerHandler, type TriggerInsertEvent, type TriggerOp, type TriggerQueryArgs, type TriggerQueryPage, type TriggerRankOptions, type TriggerRankPageOptions, type TriggerRankResult, type TriggerRow, type TriggerTiming, type TriggerUpdateEvent, type VectorEmbedder, type VectorIndexDefinition, type VectorMatch, type VectorMatches, type VectorMetric, type VectorQueryInput, type VectorRecord, type VectorSearch, type VectorSearchReader, type VectorUpsertInput, type WorkflowCreateOptions, type WorkflowHandle, type WorkflowInstance, type WorkflowInstanceStatus, type WorkflowStatusResult, type Workflows, type X402ProcedureConfig, anyApi };
|
|
1335
|
+
export { type ActionCtx, type AggregateIndexDefinition, type AggregateOp, type AnyApi, type ArgsValidator, type AuthState, type CachePurge, type DatabaseReader, type DatabaseWriter, type DurableObjectJurisdiction, type ExternalSourceCursor, type ExternalSourceDefinition, type ExternalSourceMode, type ExternalSourceRefresh, type FunctionKind, type FunctionVisibility, type GlobalBackend, type IndexDefinition, type IndexRangeBuilder, type InferArgs, type LifecycleEvent, type LifecycleEventKind, type LunoraLogger, type MutationCtx, type OnDeleteAction, type PaginationOptions, type PaginationResult, type QueryCtx, type RankIndexDefinition, type RankSortKey, type ReadOnlyStorage, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMutation, type RegisteredQuery, type RegisteredStream, type RelationDefinition, type ScheduledFunctionDoc, type ScheduledJob, type Scheduler, type Schema, type SearchFilterBuilder, type SearchIndexDefinition, type Secrets, type SecretsStoreSecretLike, type ShardMode, type Storage, type StorageMetadata, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemTableName, type TableDefinition, type TableReader, type TableVectorIndex, type TriggerAggregateOptions, type TriggerBuilder, type TriggerCtx, type TriggerDatabase, type TriggerDefinition, type TriggerDeleteEvent, type TriggerEvent, type TriggerGroupByEntry, type TriggerGroupByOptions, type TriggerHandler, type TriggerInsertEvent, type TriggerOp, type TriggerQueryArgs, type TriggerQueryPage, type TriggerRankOptions, type TriggerRankPageOptions, type TriggerRankResult, type TriggerRow, type TriggerTiming, type TriggerUpdateEvent, type VectorEmbedder, type VectorIndexDefinition, type VectorMatch, type VectorMatches, type VectorMetric, type VectorQueryInput, type VectorRecord, type VectorSearch, type VectorSearchReader, type VectorUpsertInput, type WorkflowCreateOptions, type WorkflowHandle, type WorkflowInstance, type WorkflowInstanceStatus, type WorkflowStatusResult, type Workflows, type X402ProcedureConfig, anyApi };
|
package/dist/types.d.ts
CHANGED
|
@@ -28,14 +28,37 @@ type ExternalSourceRefresh = "manual" | {
|
|
|
28
28
|
everyMs: number;
|
|
29
29
|
};
|
|
30
30
|
/**
|
|
31
|
-
* Delete-detection mode for external-source ingest
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
31
|
+
* Delete-detection mode for external-source ingest (plan 077 / 136).
|
|
32
|
+
*
|
|
33
|
+
* `"full-pull"` (the default) reads the **whole** tenant membership each tick and
|
|
34
|
+
* diffs it, so it observes upstream deletes for free — but costs a full read per tick
|
|
35
|
+
* (the Phase-0 bench put the ceiling at ~10k rows).
|
|
36
|
+
*
|
|
37
|
+
* `"incremental"` pulls **only rows past a durable watermark** (`cursor`), cheap for
|
|
38
|
+
* large low-churn tables above the full-pull cap. Because an absent row then means
|
|
39
|
+
* "unchanged", not "deleted", incremental requires a delete-visibility path: either a
|
|
40
|
+
* `reconcileEveryMs` periodic full-pull sweep, or a `softDeleteColumn` whose
|
|
41
|
+
* tombstones the pull returns. `defineSchema` throws (and the
|
|
42
|
+
* `external_source_incremental_no_delete_path` advisor lint fails the build) when an
|
|
43
|
+
* incremental source declares neither.
|
|
37
44
|
*/
|
|
38
|
-
type ExternalSourceMode = "full-pull";
|
|
45
|
+
type ExternalSourceMode = "full-pull" | "incremental";
|
|
46
|
+
/**
|
|
47
|
+
* Incremental-ingest cursor (plan 136): the monotonic watermark column plus the
|
|
48
|
+
* watermark-parameterized pull query. `column` names the field in the pulled rows
|
|
49
|
+
* whose max becomes the next watermark (e.g. `"updated_at"`). `query` is a second
|
|
50
|
+
* SQL that returns only rows changed since the watermark — the watermark binds as
|
|
51
|
+
* the parameter AFTER `tenantBy`'s params (e.g. Postgres
|
|
52
|
+
* `... WHERE tenant_id = $1 AND updated_at >= $2 ORDER BY updated_at`). Prefer `>=`
|
|
53
|
+
* with the idempotent upsert apply so rows sharing the boundary timestamp are never
|
|
54
|
+
* skipped (re-pulling them is a no-op).
|
|
55
|
+
*/
|
|
56
|
+
interface ExternalSourceCursor {
|
|
57
|
+
/** The monotonic watermark column in the pulled rows; its max advances the stored watermark. */
|
|
58
|
+
column: string;
|
|
59
|
+
/** The incremental pull SQL. `tenantBy`'s params bind first, then the watermark as the trailing param. */
|
|
60
|
+
query: string;
|
|
61
|
+
}
|
|
39
62
|
/**
|
|
40
63
|
* Config for `.source(...)` (plan 077): declares a table as **materialized from an
|
|
41
64
|
* external Postgres/MySQL behind Cloudflare Hyperdrive**, not written by user
|
|
@@ -50,17 +73,33 @@ interface ExternalSourceDefinition {
|
|
|
50
73
|
binding: string;
|
|
51
74
|
/** Project the materialized rows to these columns (passed to the membership diff). Omit ⇒ the full mapped document. */
|
|
52
75
|
columns?: ReadonlyArray<string>;
|
|
76
|
+
/** **Required for `mode: "incremental"`**: the watermark column + watermark-parameterized pull query (plan 136). Rejected on a `"full-pull"` source. */
|
|
77
|
+
cursor?: ExternalSourceCursor;
|
|
53
78
|
/** Column whose value becomes the Lunora `_id`. Defaults to `"id"`. */
|
|
54
79
|
idColumn?: string;
|
|
55
80
|
/** Transform an external row into the stored document body. Omit ⇒ every selected column except `idColumn` is copied. */
|
|
56
81
|
map?: (row: Record<string, unknown>) => Record<string, unknown>;
|
|
57
|
-
/** Delete-detection mode. `"full-pull"` (the default)
|
|
82
|
+
/** Delete-detection mode. `"full-pull"` (the default) diffs the whole membership; `"incremental"` pulls past a `cursor` watermark. */
|
|
58
83
|
mode?: ExternalSourceMode;
|
|
59
84
|
/** The full tenant-membership query, with driver-native placeholders (`$1` / `?`). `tenantBy` binds its params. */
|
|
60
85
|
query: string;
|
|
86
|
+
/**
|
|
87
|
+
* **Incremental delete-visibility (plan 136)**: run a full-pull sweep at most
|
|
88
|
+
* this often (millis) to GC upstream deletes an incremental slice can't see.
|
|
89
|
+
* One of `reconcileEveryMs` / `softDeleteColumn` is required for incremental;
|
|
90
|
+
* rejected on a `"full-pull"` source.
|
|
91
|
+
*/
|
|
92
|
+
reconcileEveryMs?: number;
|
|
61
93
|
/** Poll cadence, or `"manual"`. Omit ⇒ the runtime's size-scaled default. */
|
|
62
94
|
refresh?: ExternalSourceRefresh;
|
|
63
95
|
/**
|
|
96
|
+
* **Incremental delete-visibility (plan 136)**: the upstream soft-delete
|
|
97
|
+
* tombstone column (e.g. `"deleted_at"`). When set, the incremental pull must
|
|
98
|
+
* return tombstoned rows and the ingest turns each into a local delete — an
|
|
99
|
+
* alternative to `reconcileEveryMs`. Rejected on a `"full-pull"` source.
|
|
100
|
+
*/
|
|
101
|
+
softDeleteColumn?: string;
|
|
102
|
+
/**
|
|
64
103
|
* **Mandatory under `.shardBy()`**: map this DO's shard key → the query's bound
|
|
65
104
|
* params, so a tenant DO can only ever pull its own rows. An unscoped sourced +
|
|
66
105
|
* sharded table replicates the whole multitenant table into every shard — the
|
|
@@ -1293,4 +1332,4 @@ interface ActionCtx {
|
|
|
1293
1332
|
*/
|
|
1294
1333
|
type AnyApi = Record<string, Record<string, RegisteredFunction<ArgsValidator, unknown, FunctionKind>>>;
|
|
1295
1334
|
declare const anyApi: AnyApi;
|
|
1296
|
-
export { type ActionCtx, type AggregateIndexDefinition, type AggregateOp, type AnyApi, type ArgsValidator, type AuthState, type CachePurge, type DatabaseReader, type DatabaseWriter, type DurableObjectJurisdiction, type ExternalSourceDefinition, type ExternalSourceMode, type ExternalSourceRefresh, type FunctionKind, type FunctionVisibility, type GlobalBackend, type IndexDefinition, type IndexRangeBuilder, type InferArgs, type LifecycleEvent, type LifecycleEventKind, type LunoraLogger, type MutationCtx, type OnDeleteAction, type PaginationOptions, type PaginationResult, type QueryCtx, type RankIndexDefinition, type RankSortKey, type ReadOnlyStorage, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMutation, type RegisteredQuery, type RegisteredStream, type RelationDefinition, type ScheduledFunctionDoc, type ScheduledJob, type Scheduler, type Schema, type SearchFilterBuilder, type SearchIndexDefinition, type Secrets, type SecretsStoreSecretLike, type ShardMode, type Storage, type StorageMetadata, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemTableName, type TableDefinition, type TableReader, type TableVectorIndex, type TriggerAggregateOptions, type TriggerBuilder, type TriggerCtx, type TriggerDatabase, type TriggerDefinition, type TriggerDeleteEvent, type TriggerEvent, type TriggerGroupByEntry, type TriggerGroupByOptions, type TriggerHandler, type TriggerInsertEvent, type TriggerOp, type TriggerQueryArgs, type TriggerQueryPage, type TriggerRankOptions, type TriggerRankPageOptions, type TriggerRankResult, type TriggerRow, type TriggerTiming, type TriggerUpdateEvent, type VectorEmbedder, type VectorIndexDefinition, type VectorMatch, type VectorMatches, type VectorMetric, type VectorQueryInput, type VectorRecord, type VectorSearch, type VectorSearchReader, type VectorUpsertInput, type WorkflowCreateOptions, type WorkflowHandle, type WorkflowInstance, type WorkflowInstanceStatus, type WorkflowStatusResult, type Workflows, type X402ProcedureConfig, anyApi };
|
|
1335
|
+
export { type ActionCtx, type AggregateIndexDefinition, type AggregateOp, type AnyApi, type ArgsValidator, type AuthState, type CachePurge, type DatabaseReader, type DatabaseWriter, type DurableObjectJurisdiction, type ExternalSourceCursor, type ExternalSourceDefinition, type ExternalSourceMode, type ExternalSourceRefresh, type FunctionKind, type FunctionVisibility, type GlobalBackend, type IndexDefinition, type IndexRangeBuilder, type InferArgs, type LifecycleEvent, type LifecycleEventKind, type LunoraLogger, type MutationCtx, type OnDeleteAction, type PaginationOptions, type PaginationResult, type QueryCtx, type RankIndexDefinition, type RankSortKey, type ReadOnlyStorage, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMutation, type RegisteredQuery, type RegisteredStream, type RelationDefinition, type ScheduledFunctionDoc, type ScheduledJob, type Scheduler, type Schema, type SearchFilterBuilder, type SearchIndexDefinition, type Secrets, type SecretsStoreSecretLike, type ShardMode, type Storage, type StorageMetadata, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemTableName, type TableDefinition, type TableReader, type TableVectorIndex, type TriggerAggregateOptions, type TriggerBuilder, type TriggerCtx, type TriggerDatabase, type TriggerDefinition, type TriggerDeleteEvent, type TriggerEvent, type TriggerGroupByEntry, type TriggerGroupByOptions, type TriggerHandler, type TriggerInsertEvent, type TriggerOp, type TriggerQueryArgs, type TriggerQueryPage, type TriggerRankOptions, type TriggerRankPageOptions, type TriggerRankResult, type TriggerRow, type TriggerTiming, type TriggerUpdateEvent, type VectorEmbedder, type VectorIndexDefinition, type VectorMatch, type VectorMatches, type VectorMetric, type VectorQueryInput, type VectorRecord, type VectorSearch, type VectorSearchReader, type VectorUpsertInput, type WorkflowCreateOptions, type WorkflowHandle, type WorkflowInstance, type WorkflowInstanceStatus, type WorkflowStatusResult, type Workflows, type X402ProcedureConfig, anyApi };
|