@lunora/do 1.0.0-alpha.31 → 1.0.0-alpha.33
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.d.mts +149 -22
- package/dist/index.d.ts +149 -22
- package/dist/index.mjs +6 -6
- package/dist/packem_shared/{CDC_LOG_TABLE-DjJEHiM2.mjs → CDC_LOG_TABLE-uwOJxJJZ.mjs} +1 -1
- package/dist/packem_shared/{NotUniqueError-BigrdT_W.mjs → NotUniqueError-Ca21uDuU.mjs} +3 -3
- package/dist/packem_shared/{ROOT_DO_SIZE_WARN_BYTES-BWz51mpB.mjs → ROOT_DO_SIZE_WARN_BYTES-afQHUhyA.mjs} +71 -31
- package/dist/packem_shared/isSoftDeleted-YLKR6JYw.mjs +217 -0
- package/dist/packem_shared/materializeExternalRows-DlQWMlw_.mjs +45 -0
- package/dist/packem_shared/{runShardMigrations-BGx4v2B6.mjs → runShardMigrations-DFzx6Qld.mjs} +1 -1
- package/package.json +2 -2
- package/dist/packem_shared/isSourceDue-Bj7I3v1b.mjs +0 -41
- package/dist/packem_shared/materializeExternalRows-CoGmFmsY.mjs +0 -23
package/dist/index.d.mts
CHANGED
|
@@ -2407,6 +2407,11 @@ interface MaterializeResult {
|
|
|
2407
2407
|
/** `id → projected-value JSON` — the post-tick membership, to feed back as `baseline` next tick. */
|
|
2408
2408
|
nextBaseline: Map<string, string>;
|
|
2409
2409
|
}
|
|
2410
|
+
/** The outcome of one incremental materialize pass. No baseline: incremental applies only the pulled slice, never a full-membership diff. */
|
|
2411
|
+
interface IncrementalMaterializeResult {
|
|
2412
|
+
/** Number of `CdcChange`s applied (upserts + tombstone deletes) for the pulled slice. */
|
|
2413
|
+
applied: number;
|
|
2414
|
+
}
|
|
2410
2415
|
/**
|
|
2411
2416
|
* Diff `pulled` against `baseline` and apply the delta to `writer`. Returns the
|
|
2412
2417
|
* applied count and the next baseline. A steady-state tick (membership unchanged)
|
|
@@ -2417,6 +2422,36 @@ declare const materializeExternalRows: (writer: DatabaseWriterLike, pulled: Read
|
|
|
2417
2422
|
table: string;
|
|
2418
2423
|
}) => Promise<MaterializeResult>;
|
|
2419
2424
|
/**
|
|
2425
|
+
* Apply an **incremental** slice (plan 136): the freshly-pulled rows changed since
|
|
2426
|
+
* the watermark, upsert-only. Unlike {@link materializeExternalRows} this reads no
|
|
2427
|
+
* baseline and never diffs the full membership — an absent row means "unchanged
|
|
2428
|
+
* since the watermark", NOT "deleted". Each pulled row is projected with the SAME
|
|
2429
|
+
* {@link projectExternalSourceRow} full-pull uses, so an incrementally-upserted row
|
|
2430
|
+
* is byte-identical to how the next reconcile sweep's full-pull would store it (no
|
|
2431
|
+
* spurious update on reconcile).
|
|
2432
|
+
*
|
|
2433
|
+
* Delete visibility comes from `deletedIds` — the ids the caller resolved from the
|
|
2434
|
+
* source's soft-delete tombstone column. Every other pulled row is an `insert`,
|
|
2435
|
+
* which {@link applyCdcChanges} upserts (insert, or replace on conflict), so a
|
|
2436
|
+
* changed existing row is updated and a genuinely new row is inserted without the
|
|
2437
|
+
* caller tracking which is which.
|
|
2438
|
+
*
|
|
2439
|
+
* **Content short-circuit.** An incremental cursor query uses `>= watermark` (so
|
|
2440
|
+
* rows sharing the boundary value are never skipped), which means a steady-state
|
|
2441
|
+
* tick re-pulls the boundary row(s) unchanged. Blindly upserting them would append
|
|
2442
|
+
* a `__cdc_log` entry, broadcast a spurious `update` to every `defineShape`
|
|
2443
|
+
* subscriber, re-run search/aggregate/rank sync, and fire `onWrite` (a Vectorize
|
|
2444
|
+
* re-embed = real cost) on every tick — and `replace` would reset `_creationTime`.
|
|
2445
|
+
* So each row is diffed against its stored projection (the SAME
|
|
2446
|
+
* {@link projectExternalSourceRow} + {@link stableStringify} full-pull uses) and
|
|
2447
|
+
* skipped when byte-identical — mirroring the full-pull diff's steady-state no-op.
|
|
2448
|
+
*/
|
|
2449
|
+
declare const materializeExternalRowsIncremental: (writer: DatabaseWriterLike, pulled: ReadonlyArray<Record<string, unknown>>, options: {
|
|
2450
|
+
columns?: ReadonlyArray<string>;
|
|
2451
|
+
deletedIds?: ReadonlySet<string>;
|
|
2452
|
+
table: string;
|
|
2453
|
+
}) => Promise<IncrementalMaterializeResult>;
|
|
2454
|
+
/**
|
|
2420
2455
|
* Read the materialized table's current membership as the canonical full-pull
|
|
2421
2456
|
* baseline (`id → canonical JSON`). Reuses the shape scanner (`selectShapeRows`)
|
|
2422
2457
|
* and the SAME {@link projectExternalSourceRow} + {@link stableStringify} the diff
|
|
@@ -2445,23 +2480,50 @@ interface SourceClientLike {
|
|
|
2445
2480
|
type SourceRefresh = "manual" | {
|
|
2446
2481
|
everyMs: number;
|
|
2447
2482
|
};
|
|
2483
|
+
/** The incremental cursor config (plan 136): the watermark column + the watermark-parameterized pull query. */
|
|
2484
|
+
interface SourceCursorLike {
|
|
2485
|
+
column: string;
|
|
2486
|
+
query: string;
|
|
2487
|
+
}
|
|
2448
2488
|
/** The runtime `.source(...)` config the poll loop reads — a structural mirror of `@lunora/server`'s `ExternalSourceDefinition` (only the fields the tick uses). */
|
|
2449
2489
|
interface ExternalSourceLike {
|
|
2450
2490
|
binding: string;
|
|
2451
2491
|
columns?: ReadonlyArray<string>;
|
|
2492
|
+
cursor?: SourceCursorLike;
|
|
2452
2493
|
idColumn?: string;
|
|
2453
2494
|
map?: (row: Record<string, unknown>) => Record<string, unknown>;
|
|
2495
|
+
mode?: string;
|
|
2454
2496
|
query: string;
|
|
2497
|
+
reconcileEveryMs?: number;
|
|
2455
2498
|
refresh?: SourceRefresh;
|
|
2499
|
+
softDeleteColumn?: string;
|
|
2456
2500
|
tenantBy?: (shardKey: string) => ReadonlyArray<unknown>;
|
|
2457
2501
|
}
|
|
2502
|
+
/**
|
|
2503
|
+
* Coerce a driver-native value that `stableStringify` can't represent (see
|
|
2504
|
+
* `shared/stable-key.ts`) into its JSON-safe form: a `Date` → its ISO string, a
|
|
2505
|
+
* `bigint` → its decimal string. Every other value passes through unchanged.
|
|
2506
|
+
*
|
|
2507
|
+
* node-pg / postgres-js / mysql2 return `timestamp`/`datetime` columns as JS
|
|
2508
|
+
* `Date` and `bigint`/`int8` columns as `bigint` — both throw a `TypeError` out of
|
|
2509
|
+
* `stableStringify` (used by the full-pull diff and the incremental content
|
|
2510
|
+
* short-circuit), which bricks ingest for any table with such a column (e.g. the
|
|
2511
|
+
* canonical `cursor: { column: "updated_at" }` incremental config). This is the
|
|
2512
|
+
* single boundary where driver-native types cross into DO SQLite JSON; a new
|
|
2513
|
+
* source driver (or a new non-JSON column type) must be normalized here too.
|
|
2514
|
+
* `shared/stable-key.ts`'s throw contract is intentionally left unchanged — this
|
|
2515
|
+
* normalizes the value *before* it can ever reach that encoder.
|
|
2516
|
+
*/
|
|
2517
|
+
|
|
2458
2518
|
/**
|
|
2459
2519
|
* Lift an external row to a Lunora document: the `idColumn` value becomes a
|
|
2460
2520
|
* stringified `_id`, then either `map` shapes the body or every other column is
|
|
2461
2521
|
* copied verbatim. Throws on a missing/null id, and on a non-scalar id, so a
|
|
2462
2522
|
* misconfigured query fails loudly instead of materializing rows under the literal
|
|
2463
2523
|
* id `"undefined"` (or collapsing many rows onto one id). Shared with
|
|
2464
|
-
* `@lunora/hyperdrive`'s `projectSourceRow`.
|
|
2524
|
+
* `@lunora/hyperdrive`'s `projectSourceRow`. The returned document's values are
|
|
2525
|
+
* normalized (see {@link normalizeSourceValue}) so a `Date`/`bigint` column never
|
|
2526
|
+
* reaches `stableStringify` un-normalized.
|
|
2465
2527
|
*/
|
|
2466
2528
|
declare const liftSourceId: (row: Record<string, unknown>, options?: {
|
|
2467
2529
|
idColumn?: string;
|
|
@@ -2482,6 +2544,39 @@ declare const isSourceDue: (refresh: SourceRefresh | undefined, lastPolledMs: nu
|
|
|
2482
2544
|
*/
|
|
2483
2545
|
declare const pullExternalSourceTick: (sql: SqlExec, writer: DatabaseWriterLike, client: SourceClientLike, table: string, source: ExternalSourceLike, shardKey: string) => Promise<MaterializeResult>;
|
|
2484
2546
|
/**
|
|
2547
|
+
* Whether an upstream row is a soft-delete tombstone under `column`. A set
|
|
2548
|
+
* `deleted_at` (any non-null value, e.g. a timestamp), an `is_deleted = true`, or a
|
|
2549
|
+
* non-zero flag all read as deleted; `null` / `undefined` / `false` / `0` mean
|
|
2550
|
+
* live. Note an **empty string** reads as deleted (`"" !== 0`), so an upstream that
|
|
2551
|
+
* clears the column to `""` rather than `NULL` for a live row would mis-signal —
|
|
2552
|
+
* use `NULL` for live rows. The incremental query MUST return tombstoned rows
|
|
2553
|
+
* (don't filter `WHERE deleted_at IS NULL`) or the delete is never observed.
|
|
2554
|
+
*/
|
|
2555
|
+
declare const isSoftDeleted: (row: Record<string, unknown>, column: string) => boolean;
|
|
2556
|
+
/**
|
|
2557
|
+
* Run one **incremental** tick (plan 136). Reads the durable watermark for
|
|
2558
|
+
* `(table, shardKey)`; on the first ever poll or when the `reconcileEveryMs` sweep
|
|
2559
|
+
* is due it runs a **full-pull** (seed/GC: {@link runExternalSourceTick} observes
|
|
2560
|
+
* deletes and re-establishes membership), otherwise it pulls only rows past the
|
|
2561
|
+
* watermark via `cursor.query` and upserts them ({@link materializeExternalRowsIncremental},
|
|
2562
|
+
* tombstones → deletes). Either way it advances the watermark to the max cursor
|
|
2563
|
+
* value seen and persists it (and the reconcile timestamp).
|
|
2564
|
+
*
|
|
2565
|
+
* **Crash safety** is by ordering + idempotency, NOT an atomic transaction across
|
|
2566
|
+
* the two write channels (the apply goes through the `writer`; the watermark write
|
|
2567
|
+
* is a raw `sql` write): the watermark advances only AFTER a fully-applied slice,
|
|
2568
|
+
* so a crash between the apply and the watermark write leaves the watermark behind
|
|
2569
|
+
* and the next tick re-pulls the same `>= watermark` slice — which the upsert
|
|
2570
|
+
* short-circuits (unchanged rows) or re-applies idempotently. It only ever replays,
|
|
2571
|
+
* never skips (same self-healing argument as `advanceClientWatermark`).
|
|
2572
|
+
*
|
|
2573
|
+
* Requires `source.cursor` (validated at `defineSchema` for incremental mode); a
|
|
2574
|
+
* missing cursor throws rather than silently degrading to a stuck watermark.
|
|
2575
|
+
*/
|
|
2576
|
+
declare const pullExternalSourceIncrementalTick: (sql: SqlExec, writer: DatabaseWriterLike, client: SourceClientLike, table: string, source: ExternalSourceLike, shardKey: string, nowMs: number) => Promise<{
|
|
2577
|
+
applied: number;
|
|
2578
|
+
}>;
|
|
2579
|
+
/**
|
|
2485
2580
|
* Reserved `functionPath` prefix for admin introspection RPCs. These travel
|
|
2486
2581
|
* over the same `/_lunora/rpc` → shard `/rpc` path as ordinary functions, but
|
|
2487
2582
|
* `ShardDO` intercepts them before user dispatch and serves them from the
|
|
@@ -4325,6 +4420,19 @@ declare abstract class ShardDO {
|
|
|
4325
4420
|
private static rootSizeWarned;
|
|
4326
4421
|
/** Test-only: reset the static "warned once" flag. */
|
|
4327
4422
|
static resetRootSizeWarning(): void;
|
|
4423
|
+
/**
|
|
4424
|
+
* Compute the shared poll alarm's next wake time from both tiers' signals.
|
|
4425
|
+
* Global shapes need the fixed `GLOBAL_SHAPE_POLL_INTERVAL_MS` floor whenever
|
|
4426
|
+
* any are subscribed (a global table has no per-DO op-log, so it can only be
|
|
4427
|
+
* polled). External-source ingest instead reports the earliest NEXT-DUE
|
|
4428
|
+
* timestamp across its non-manual sources (or `undefined` when none exist) —
|
|
4429
|
+
* a source with a large `refresh.everyMs` must sleep until it's actually due,
|
|
4430
|
+
* not spin at the global-shape floor. Returns `undefined` when NEITHER tier
|
|
4431
|
+
* has pending work, so the DO can go fully idle instead of re-arming for no
|
|
4432
|
+
* reason; otherwise the earlier of the two candidate times (never later than
|
|
4433
|
+
* `nowMs`, so a source that's already due arms essentially immediately).
|
|
4434
|
+
*/
|
|
4435
|
+
private static nextPollAlarmTarget;
|
|
4328
4436
|
protected state: ShardDOState;
|
|
4329
4437
|
protected env: unknown;
|
|
4330
4438
|
/**
|
|
@@ -4656,12 +4764,17 @@ declare abstract class ShardDO {
|
|
|
4656
4764
|
/** Hibernation API: invoked on socket error. */
|
|
4657
4765
|
webSocketError(_ws: WebSocket, _error: unknown): void;
|
|
4658
4766
|
/**
|
|
4659
|
-
* Durable Object alarm handler — the heartbeat for `.global()`-table shapes
|
|
4660
|
-
*
|
|
4661
|
-
*
|
|
4662
|
-
*
|
|
4663
|
-
*
|
|
4664
|
-
*
|
|
4767
|
+
* Durable Object alarm handler — the heartbeat for `.global()`-table shapes
|
|
4768
|
+
* AND external-source (`.source(...)`) ingest, which share one alarm. The
|
|
4769
|
+
* runtime wakes this when the poll alarm armed by `scheduleGlobalPoll` fires;
|
|
4770
|
+
* it refreshes every subscribed global shape (diff-poke from the global
|
|
4771
|
+
* backend), materializes any due sourced tables, and re-arms at
|
|
4772
|
+
* {@link ShardDO.nextPollAlarmTarget} — the fixed floor while global shapes
|
|
4773
|
+
* are subscribed, or the earliest source next-due time when only ingest
|
|
4774
|
+
* remains (so a 1-hour-`refresh` source sleeps ~1 hour instead of waking
|
|
4775
|
+
* every 2 s). With neither tier pending, the alarm is not re-armed and the DO
|
|
4776
|
+
* goes idle. A base-only / global-free / source-free DO never arms it, so
|
|
4777
|
+
* this stays dormant there.
|
|
4665
4778
|
*/
|
|
4666
4779
|
alarm(): Promise<void>;
|
|
4667
4780
|
/** Subclasses implement function dispatch. */
|
|
@@ -5328,16 +5441,23 @@ declare abstract class ShardDO {
|
|
|
5328
5441
|
/**
|
|
5329
5442
|
* Poll external-source (`.source(...)`) tables once (plan 077): materialize
|
|
5330
5443
|
* each sourced table's freshly-pulled tenant slice into this DO's SQLite. The
|
|
5331
|
-
* base `ShardDO` has no sourced tables, so it returns `
|
|
5332
|
-
* stays dormant — zero behavior change for every existing DO. The
|
|
5333
|
-
* subclass overrides it to, per sourced table, build a
|
|
5334
|
-
* writer, read the tenant slice from Hyperdrive under this
|
|
5335
|
-
* run `runExternalSourceTick` (read local baseline → diff
|
|
5336
|
-
* validated CDC writer).
|
|
5337
|
-
*
|
|
5338
|
-
*
|
|
5444
|
+
* base `ShardDO` has no sourced tables, so it returns `undefined` and the
|
|
5445
|
+
* ingest tier stays dormant — zero behavior change for every existing DO. The
|
|
5446
|
+
* codegen subclass overrides it to, per sourced table, build a
|
|
5447
|
+
* `createShardCtxDb` writer, read the tenant slice from Hyperdrive under this
|
|
5448
|
+
* DO's shard key, and run `runExternalSourceTick` (read local baseline → diff
|
|
5449
|
+
* → apply via the validated CDC writer).
|
|
5450
|
+
*
|
|
5451
|
+
* Returns the EARLIEST next-due timestamp (absolute epoch ms) across every
|
|
5452
|
+
* non-manual sourced table — `polledAt.get(table) ?? now` plus that source's
|
|
5453
|
+
* `refresh.everyMs` — or `undefined` when every sourced table is
|
|
5454
|
+
* `refresh: "manual"` (or there are none). NOT a bare active count: the
|
|
5455
|
+
* shared poll alarm ({@link ShardDO.alarm}) uses this to re-arm at the exact
|
|
5456
|
+
* next time ingest needs to run, instead of spinning at the fixed
|
|
5457
|
+
* `GLOBAL_SHAPE_POLL_INTERVAL_MS` floor for a source whose `refresh.everyMs`
|
|
5458
|
+
* is, say, an hour away.
|
|
5339
5459
|
*/
|
|
5340
|
-
protected pollExternalSources(): Promise<number>;
|
|
5460
|
+
protected pollExternalSources(): Promise<number | undefined>;
|
|
5341
5461
|
/**
|
|
5342
5462
|
* Arm the shared poll alarm for external-source ingest (plan 077). The alarm is
|
|
5343
5463
|
* shared with the global-shape poll tier; the codegen subclass calls this once
|
|
@@ -6337,11 +6457,18 @@ declare abstract class ShardDO {
|
|
|
6337
6457
|
*/
|
|
6338
6458
|
private saveGlobalSnapshot;
|
|
6339
6459
|
/**
|
|
6340
|
-
* Arm the poll alarm
|
|
6341
|
-
*
|
|
6342
|
-
*
|
|
6343
|
-
*
|
|
6344
|
-
*
|
|
6460
|
+
* Arm the poll alarm if one isn't already pending. Idempotent — every
|
|
6461
|
+
* global-shape seed calls it, but only the first arms the alarm. Degrades to
|
|
6462
|
+
* a no-op when the runtime exposes no `setAlarm` (the unit harness): a global
|
|
6463
|
+
* shape is then seed-only, which the poll-loop tests assert by driving
|
|
6464
|
+
* {@link ShardDO.alarm} directly.
|
|
6465
|
+
*
|
|
6466
|
+
* `atMs` lets {@link ShardDO.alarm} re-arm at a computed target — the
|
|
6467
|
+
* earlier of the fixed global-shape floor and the earliest external-source
|
|
6468
|
+
* next-due time — instead of always the fixed floor. Every OTHER caller
|
|
6469
|
+
* (a fresh global-shape seed, {@link ShardDO.scheduleSourcePoll}'s initial
|
|
6470
|
+
* kick) omits it and gets the original `GLOBAL_SHAPE_POLL_INTERVAL_MS`
|
|
6471
|
+
* default, since neither knows a more precise due time yet.
|
|
6345
6472
|
*/
|
|
6346
6473
|
private scheduleGlobalPoll;
|
|
6347
6474
|
/**
|
|
@@ -6704,4 +6831,4 @@ interface WhereSqlStrategy {
|
|
|
6704
6831
|
* `undefined` when the input imposes no constraint (empty `where`).
|
|
6705
6832
|
*/
|
|
6706
6833
|
declare const compileWhereSql: (where: WhereInput | undefined, strategy: WhereSqlStrategy) => SQL | undefined;
|
|
6707
|
-
export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, AGGREGATE_SQL_FUNCTION, AUTH_METRICS_BUCKETS_TABLE, AUTH_METRICS_BUCKET_MS, AUTH_METRICS_BUCKET_RETENTION, AUTH_METRICS_TABLE, type AdvisoriesResult, type AdvisoryFinding, type AggregateIndexDefinitionLike, type AggregateOp, type AggregateOptions, type AggregateResult, type AggregateTally, type ApplyOnDeleteOptions, type AuditEntry, type AuditLogResult, type AuthMetrics, type AuthMetricsBucket, type BroadcastDelta, CDC_LOG_TABLE, type CacheEntry, type CapturedMailRow, type CdcChange, type Clock, type ColumnMeta, type ColumnMetaLike, ConflictError, type CountArgs, CountRlsUnsupportedError, type CtxDbOptions, DATA_MIGRATION_STATE_TABLE, DEFAULT_MAX_RELATION_KEYS, type DataMigrationDocument, type DataMigrationLike, type DataMigrationTransform, type DatabaseWriterLike, type DependencyTracker, type DeployInfo, type ExportRow, type ExportShardAdminArgs, type ExportShardArgs, type ExternalSourceDiffResult, type ExternalSourceLike, FLAGS_FUNCTION_PREFIX, FUNCTION_METRICS_BUCKETS_TABLE, FUNCTION_METRICS_BUCKET_MS, FUNCTION_METRICS_BUCKET_RETENTION, FUNCTION_METRICS_INDEX_TABLE, FUNCTION_METRICS_TABLE, type FacetColumnOptions, type FacetColumnResult, type FacetValue, type FieldOperators, type FlagEvaluation, type FlagsResult, type FunctionCallStat, type FunctionMetricBucket, type FunctionMetricIndexHit, type FunctionStatsResult, type GroupByEntry, type GroupByOptions, type HibernatableWebSocket, type IdGenerator, type ImportError, type ImportShardAdminArgs, type ImportShardArgs, type ImportShardResult, type IndexDefinitionLike, type IndexRangeBuilderLike, LogBuffer, type LogEntry, type LogEventInput, type LogLevel, type LogSink, MAIL_RETENTION, MAIL_TABLE, MAX_SQL_ROWS, MIN_ADMIN_TOKEN_LENGTH, MIN_AUTH_SECRET_LENGTH, type MaskColumnMetadata, type MaskPoliciesResult, type MaterializeResult, type MigrationDirection, type MigrationRunResult, type MigrationStatus, type MigrationStatusRow, type MutationDelta, type NestedWith, NotFoundError, NotUniqueError, type OnDeleteActionLike, type OrderByInput, type OrderKey, type PaginationOptions, type PitrBookmarkResult, type PitrRestoreArgs, type PitrRestoreResult, type PitrStorage, type QueryArgs, type QueryPage, type QueueMetadata, type QueuesResult, RANK_TIEBREAK, RELATION_FUNCTION_PREFIX, RLS_UNWRAP_SYMBOL, ROOT_DO_SIZE_WARN_BYTES, ROOT_SHARD_NAME, type RankDirection, type RankIndexDefinitionLike, type RankOptions, type RankPage, type RankPageOptions, type RankPageRow, type RankPageRowKey, type RankResult, type RankSortKeyLike, ReactiveCache, type ReactiveCacheOptions, type ReadHook, type ReadTablePageOptions, type RecordAuthEventInput, type RecordFunctionMetricInput, type RecordMailInput, type RelationDefinitionLike, type RenderedSql, type ResolveRelationPredicatesOptions, type ResolveWithOptions, type RestrictableQueryOptions, type RlsPoliciesResult, type RlsPolicyMetadata, RlsRequiredError, type RlsRoleMetadata, type RpcRequest, type RunDataMigrationOptions, type RunShardApplyCdcArgs, type RunShardApplyCdcResult, type RunShardBulkDeleteArgs, type RunShardBulkDeleteResult, type RunShardExportArgs, type RunShardImportArgs, type RunShardMigrationArgs, type RunShardRankBeforeArgs, type RunShardRankPageArgs, type RunShardWriteArgs, type RunShardWriteResult, type RunTriggersOptions, SCAN_DEP, SESSION_DO_TTL_DEFAULT, SHARD_REGISTRY_DO_NAME, type SchedulableWorkflowReferenceLike, type ScheduledFunctionDoc, type SchedulerLike, type SchemaLike, type SearchFilterBuilderLike, type SecurityAuditResult, type SecurityFinding, type SecurityFindingKind, type SecurityFindingLevel, type SelectMatchingIdsOptions, type ServerDefaultContextLike, SessionDO, type SessionRecord, type SettingEntry, type SettingKind, type SettingsResult, type ShapeSubscriptionQuery, ShardDO, type ShardDOOptions, type ShardDOState, type ShardRankPageResult, ShardRegistryDO, type SocketAttachment, type SortDirection, type SourceClientLike, type SourceRefresh, type SqlConsoleResult, type SqlCursor, type SqlEngine, type SqlExec, type StorageRuleMetadata, type StorageRulesResult, type StudioFeaturesResult, type SubscriptionEnvelope, type SubscriptionOutcome, type SubscriptionQuery, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemReaderOptions, type SystemReaderSchedulerLike, type SystemReaderStorageLike, type SystemTableName, type TableColumnsResult, type TableDefinitionLike, type TableIndexInfo, type TableIndexesResult, type TableInfo, type TablePage, type TableReaderLike, type TablesColumnsResult, type TransactionSqlLike, type TriggerContextLike, type TriggerDefinitionLike, type TriggerEventLike, type TriggerOpLike, type TriggerTimingLike, type ValidatorLike, type WhereInput, type WhereSqlStrategy, type WithInput, type WorkflowMetadata, type WorkflowsResult, type WriteEvent, type WriteHook, aggregateSqlFunction, aggregateTableName, applyCdcChanges, applyOnDelete, applySelect, armRestore, assertFlatPredicate, assertReadonly, assertShapeShardable, assertValidClientId, backfillAggregateIndexes, backfillRankIndexes, buildFtsMatch, buildSecurityAudit, buildSeekWhere, clearCapturedMail, coerceAggregateNumber, compileWhereSql, containsRelationPredicate, createDependencyTracker, createShardCtxDb, createSystemReader, decodeCursor, depKey, diffExternalSource, encodeAggregateKey, encodeCursor, encodePartitionKey, ensureAuthMetricsTables, ensureFunctionMetricsTables, ensureMailTable, exportShardRows, exportShardTable, facetColumn, fanOutScalarCounts, foldAggregateTally, ftsTableName, guardWriter, hasTrigger, importShardRows, isRelationPredicate, isSourceDue, liftSourceId, listTables, matchesRankStaticWhere, matchesStaticWhere, materializeExternalRows, mergeWhere, normalizeCountArgument, normalizeIdStructurally, normalizeOrderKeys, parseExportShardArgs, parseImportShardArgs, planAggregateLookup, pullExternalSourceTick, rankTableName, reactiveCacheKey, readAggregateValue, readAuthMetrics, readBookmark, readCapturedMail, readCdcChanges, readExternalSourceBaseline, readFunctionMetricBuckets, readFunctionMetricIndexHits, readFunctionMetrics, readFunctionMetricsTotals, readMigrationStatus, readTablePage, recordAuthEvent, recordCapturedMail, recordFunctionMetric, renderSql, resolveRankPartition, resolveRelationPredicates, resolveWith, runDataMigration, runExternalSourceTick, runReadonlySql, runRowValidators, runShardMigrations, runTriggers, scoreDocument, selectExportTables, selectIndexForAggregate, selectIndexForCount, selectIndexForGroupBy, selectMatchingIds, serveRelationFanout, softDeleteScope, sortColumnName, stableStringify, stableWireKey, stringifySearchText, subscriptionListDeltas, throwingScheduler, tokenizeSearch, trimCdcChanges, validateImportRow };
|
|
6834
|
+
export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, AGGREGATE_SQL_FUNCTION, AUTH_METRICS_BUCKETS_TABLE, AUTH_METRICS_BUCKET_MS, AUTH_METRICS_BUCKET_RETENTION, AUTH_METRICS_TABLE, type AdvisoriesResult, type AdvisoryFinding, type AggregateIndexDefinitionLike, type AggregateOp, type AggregateOptions, type AggregateResult, type AggregateTally, type ApplyOnDeleteOptions, type AuditEntry, type AuditLogResult, type AuthMetrics, type AuthMetricsBucket, type BroadcastDelta, CDC_LOG_TABLE, type CacheEntry, type CapturedMailRow, type CdcChange, type Clock, type ColumnMeta, type ColumnMetaLike, ConflictError, type CountArgs, CountRlsUnsupportedError, type CtxDbOptions, DATA_MIGRATION_STATE_TABLE, DEFAULT_MAX_RELATION_KEYS, type DataMigrationDocument, type DataMigrationLike, type DataMigrationTransform, type DatabaseWriterLike, type DependencyTracker, type DeployInfo, type ExportRow, type ExportShardAdminArgs, type ExportShardArgs, type ExternalSourceDiffResult, type ExternalSourceLike, FLAGS_FUNCTION_PREFIX, FUNCTION_METRICS_BUCKETS_TABLE, FUNCTION_METRICS_BUCKET_MS, FUNCTION_METRICS_BUCKET_RETENTION, FUNCTION_METRICS_INDEX_TABLE, FUNCTION_METRICS_TABLE, type FacetColumnOptions, type FacetColumnResult, type FacetValue, type FieldOperators, type FlagEvaluation, type FlagsResult, type FunctionCallStat, type FunctionMetricBucket, type FunctionMetricIndexHit, type FunctionStatsResult, type GroupByEntry, type GroupByOptions, type HibernatableWebSocket, type IdGenerator, type ImportError, type ImportShardAdminArgs, type ImportShardArgs, type ImportShardResult, type IncrementalMaterializeResult, type IndexDefinitionLike, type IndexRangeBuilderLike, LogBuffer, type LogEntry, type LogEventInput, type LogLevel, type LogSink, MAIL_RETENTION, MAIL_TABLE, MAX_SQL_ROWS, MIN_ADMIN_TOKEN_LENGTH, MIN_AUTH_SECRET_LENGTH, type MaskColumnMetadata, type MaskPoliciesResult, type MaterializeResult, type MigrationDirection, type MigrationRunResult, type MigrationStatus, type MigrationStatusRow, type MutationDelta, type NestedWith, NotFoundError, NotUniqueError, type OnDeleteActionLike, type OrderByInput, type OrderKey, type PaginationOptions, type PitrBookmarkResult, type PitrRestoreArgs, type PitrRestoreResult, type PitrStorage, type QueryArgs, type QueryPage, type QueueMetadata, type QueuesResult, RANK_TIEBREAK, RELATION_FUNCTION_PREFIX, RLS_UNWRAP_SYMBOL, ROOT_DO_SIZE_WARN_BYTES, ROOT_SHARD_NAME, type RankDirection, type RankIndexDefinitionLike, type RankOptions, type RankPage, type RankPageOptions, type RankPageRow, type RankPageRowKey, type RankResult, type RankSortKeyLike, ReactiveCache, type ReactiveCacheOptions, type ReadHook, type ReadTablePageOptions, type RecordAuthEventInput, type RecordFunctionMetricInput, type RecordMailInput, type RelationDefinitionLike, type RenderedSql, type ResolveRelationPredicatesOptions, type ResolveWithOptions, type RestrictableQueryOptions, type RlsPoliciesResult, type RlsPolicyMetadata, RlsRequiredError, type RlsRoleMetadata, type RpcRequest, type RunDataMigrationOptions, type RunShardApplyCdcArgs, type RunShardApplyCdcResult, type RunShardBulkDeleteArgs, type RunShardBulkDeleteResult, type RunShardExportArgs, type RunShardImportArgs, type RunShardMigrationArgs, type RunShardRankBeforeArgs, type RunShardRankPageArgs, type RunShardWriteArgs, type RunShardWriteResult, type RunTriggersOptions, SCAN_DEP, SESSION_DO_TTL_DEFAULT, SHARD_REGISTRY_DO_NAME, type SchedulableWorkflowReferenceLike, type ScheduledFunctionDoc, type SchedulerLike, type SchemaLike, type SearchFilterBuilderLike, type SecurityAuditResult, type SecurityFinding, type SecurityFindingKind, type SecurityFindingLevel, type SelectMatchingIdsOptions, type ServerDefaultContextLike, SessionDO, type SessionRecord, type SettingEntry, type SettingKind, type SettingsResult, type ShapeSubscriptionQuery, ShardDO, type ShardDOOptions, type ShardDOState, type ShardRankPageResult, ShardRegistryDO, type SocketAttachment, type SortDirection, type SourceClientLike, type SourceCursorLike, type SourceRefresh, type SqlConsoleResult, type SqlCursor, type SqlEngine, type SqlExec, type StorageRuleMetadata, type StorageRulesResult, type StudioFeaturesResult, type SubscriptionEnvelope, type SubscriptionOutcome, type SubscriptionQuery, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemReaderOptions, type SystemReaderSchedulerLike, type SystemReaderStorageLike, type SystemTableName, type TableColumnsResult, type TableDefinitionLike, type TableIndexInfo, type TableIndexesResult, type TableInfo, type TablePage, type TableReaderLike, type TablesColumnsResult, type TransactionSqlLike, type TriggerContextLike, type TriggerDefinitionLike, type TriggerEventLike, type TriggerOpLike, type TriggerTimingLike, type ValidatorLike, type WhereInput, type WhereSqlStrategy, type WithInput, type WorkflowMetadata, type WorkflowsResult, type WriteEvent, type WriteHook, aggregateSqlFunction, aggregateTableName, applyCdcChanges, applyOnDelete, applySelect, armRestore, assertFlatPredicate, assertReadonly, assertShapeShardable, assertValidClientId, backfillAggregateIndexes, backfillRankIndexes, buildFtsMatch, buildSecurityAudit, buildSeekWhere, clearCapturedMail, coerceAggregateNumber, compileWhereSql, containsRelationPredicate, createDependencyTracker, createShardCtxDb, createSystemReader, decodeCursor, depKey, diffExternalSource, encodeAggregateKey, encodeCursor, encodePartitionKey, ensureAuthMetricsTables, ensureFunctionMetricsTables, ensureMailTable, exportShardRows, exportShardTable, facetColumn, fanOutScalarCounts, foldAggregateTally, ftsTableName, guardWriter, hasTrigger, importShardRows, isRelationPredicate, isSoftDeleted, isSourceDue, liftSourceId, listTables, matchesRankStaticWhere, matchesStaticWhere, materializeExternalRows, materializeExternalRowsIncremental, mergeWhere, normalizeCountArgument, normalizeIdStructurally, normalizeOrderKeys, parseExportShardArgs, parseImportShardArgs, planAggregateLookup, pullExternalSourceIncrementalTick, pullExternalSourceTick, rankTableName, reactiveCacheKey, readAggregateValue, readAuthMetrics, readBookmark, readCapturedMail, readCdcChanges, readExternalSourceBaseline, readFunctionMetricBuckets, readFunctionMetricIndexHits, readFunctionMetrics, readFunctionMetricsTotals, readMigrationStatus, readTablePage, recordAuthEvent, recordCapturedMail, recordFunctionMetric, renderSql, resolveRankPartition, resolveRelationPredicates, resolveWith, runDataMigration, runExternalSourceTick, runReadonlySql, runRowValidators, runShardMigrations, runTriggers, scoreDocument, selectExportTables, selectIndexForAggregate, selectIndexForCount, selectIndexForGroupBy, selectMatchingIds, serveRelationFanout, softDeleteScope, sortColumnName, stableStringify, stableWireKey, stringifySearchText, subscriptionListDeltas, throwingScheduler, tokenizeSearch, trimCdcChanges, validateImportRow };
|
package/dist/index.d.ts
CHANGED
|
@@ -2407,6 +2407,11 @@ interface MaterializeResult {
|
|
|
2407
2407
|
/** `id → projected-value JSON` — the post-tick membership, to feed back as `baseline` next tick. */
|
|
2408
2408
|
nextBaseline: Map<string, string>;
|
|
2409
2409
|
}
|
|
2410
|
+
/** The outcome of one incremental materialize pass. No baseline: incremental applies only the pulled slice, never a full-membership diff. */
|
|
2411
|
+
interface IncrementalMaterializeResult {
|
|
2412
|
+
/** Number of `CdcChange`s applied (upserts + tombstone deletes) for the pulled slice. */
|
|
2413
|
+
applied: number;
|
|
2414
|
+
}
|
|
2410
2415
|
/**
|
|
2411
2416
|
* Diff `pulled` against `baseline` and apply the delta to `writer`. Returns the
|
|
2412
2417
|
* applied count and the next baseline. A steady-state tick (membership unchanged)
|
|
@@ -2417,6 +2422,36 @@ declare const materializeExternalRows: (writer: DatabaseWriterLike, pulled: Read
|
|
|
2417
2422
|
table: string;
|
|
2418
2423
|
}) => Promise<MaterializeResult>;
|
|
2419
2424
|
/**
|
|
2425
|
+
* Apply an **incremental** slice (plan 136): the freshly-pulled rows changed since
|
|
2426
|
+
* the watermark, upsert-only. Unlike {@link materializeExternalRows} this reads no
|
|
2427
|
+
* baseline and never diffs the full membership — an absent row means "unchanged
|
|
2428
|
+
* since the watermark", NOT "deleted". Each pulled row is projected with the SAME
|
|
2429
|
+
* {@link projectExternalSourceRow} full-pull uses, so an incrementally-upserted row
|
|
2430
|
+
* is byte-identical to how the next reconcile sweep's full-pull would store it (no
|
|
2431
|
+
* spurious update on reconcile).
|
|
2432
|
+
*
|
|
2433
|
+
* Delete visibility comes from `deletedIds` — the ids the caller resolved from the
|
|
2434
|
+
* source's soft-delete tombstone column. Every other pulled row is an `insert`,
|
|
2435
|
+
* which {@link applyCdcChanges} upserts (insert, or replace on conflict), so a
|
|
2436
|
+
* changed existing row is updated and a genuinely new row is inserted without the
|
|
2437
|
+
* caller tracking which is which.
|
|
2438
|
+
*
|
|
2439
|
+
* **Content short-circuit.** An incremental cursor query uses `>= watermark` (so
|
|
2440
|
+
* rows sharing the boundary value are never skipped), which means a steady-state
|
|
2441
|
+
* tick re-pulls the boundary row(s) unchanged. Blindly upserting them would append
|
|
2442
|
+
* a `__cdc_log` entry, broadcast a spurious `update` to every `defineShape`
|
|
2443
|
+
* subscriber, re-run search/aggregate/rank sync, and fire `onWrite` (a Vectorize
|
|
2444
|
+
* re-embed = real cost) on every tick — and `replace` would reset `_creationTime`.
|
|
2445
|
+
* So each row is diffed against its stored projection (the SAME
|
|
2446
|
+
* {@link projectExternalSourceRow} + {@link stableStringify} full-pull uses) and
|
|
2447
|
+
* skipped when byte-identical — mirroring the full-pull diff's steady-state no-op.
|
|
2448
|
+
*/
|
|
2449
|
+
declare const materializeExternalRowsIncremental: (writer: DatabaseWriterLike, pulled: ReadonlyArray<Record<string, unknown>>, options: {
|
|
2450
|
+
columns?: ReadonlyArray<string>;
|
|
2451
|
+
deletedIds?: ReadonlySet<string>;
|
|
2452
|
+
table: string;
|
|
2453
|
+
}) => Promise<IncrementalMaterializeResult>;
|
|
2454
|
+
/**
|
|
2420
2455
|
* Read the materialized table's current membership as the canonical full-pull
|
|
2421
2456
|
* baseline (`id → canonical JSON`). Reuses the shape scanner (`selectShapeRows`)
|
|
2422
2457
|
* and the SAME {@link projectExternalSourceRow} + {@link stableStringify} the diff
|
|
@@ -2445,23 +2480,50 @@ interface SourceClientLike {
|
|
|
2445
2480
|
type SourceRefresh = "manual" | {
|
|
2446
2481
|
everyMs: number;
|
|
2447
2482
|
};
|
|
2483
|
+
/** The incremental cursor config (plan 136): the watermark column + the watermark-parameterized pull query. */
|
|
2484
|
+
interface SourceCursorLike {
|
|
2485
|
+
column: string;
|
|
2486
|
+
query: string;
|
|
2487
|
+
}
|
|
2448
2488
|
/** The runtime `.source(...)` config the poll loop reads — a structural mirror of `@lunora/server`'s `ExternalSourceDefinition` (only the fields the tick uses). */
|
|
2449
2489
|
interface ExternalSourceLike {
|
|
2450
2490
|
binding: string;
|
|
2451
2491
|
columns?: ReadonlyArray<string>;
|
|
2492
|
+
cursor?: SourceCursorLike;
|
|
2452
2493
|
idColumn?: string;
|
|
2453
2494
|
map?: (row: Record<string, unknown>) => Record<string, unknown>;
|
|
2495
|
+
mode?: string;
|
|
2454
2496
|
query: string;
|
|
2497
|
+
reconcileEveryMs?: number;
|
|
2455
2498
|
refresh?: SourceRefresh;
|
|
2499
|
+
softDeleteColumn?: string;
|
|
2456
2500
|
tenantBy?: (shardKey: string) => ReadonlyArray<unknown>;
|
|
2457
2501
|
}
|
|
2502
|
+
/**
|
|
2503
|
+
* Coerce a driver-native value that `stableStringify` can't represent (see
|
|
2504
|
+
* `shared/stable-key.ts`) into its JSON-safe form: a `Date` → its ISO string, a
|
|
2505
|
+
* `bigint` → its decimal string. Every other value passes through unchanged.
|
|
2506
|
+
*
|
|
2507
|
+
* node-pg / postgres-js / mysql2 return `timestamp`/`datetime` columns as JS
|
|
2508
|
+
* `Date` and `bigint`/`int8` columns as `bigint` — both throw a `TypeError` out of
|
|
2509
|
+
* `stableStringify` (used by the full-pull diff and the incremental content
|
|
2510
|
+
* short-circuit), which bricks ingest for any table with such a column (e.g. the
|
|
2511
|
+
* canonical `cursor: { column: "updated_at" }` incremental config). This is the
|
|
2512
|
+
* single boundary where driver-native types cross into DO SQLite JSON; a new
|
|
2513
|
+
* source driver (or a new non-JSON column type) must be normalized here too.
|
|
2514
|
+
* `shared/stable-key.ts`'s throw contract is intentionally left unchanged — this
|
|
2515
|
+
* normalizes the value *before* it can ever reach that encoder.
|
|
2516
|
+
*/
|
|
2517
|
+
|
|
2458
2518
|
/**
|
|
2459
2519
|
* Lift an external row to a Lunora document: the `idColumn` value becomes a
|
|
2460
2520
|
* stringified `_id`, then either `map` shapes the body or every other column is
|
|
2461
2521
|
* copied verbatim. Throws on a missing/null id, and on a non-scalar id, so a
|
|
2462
2522
|
* misconfigured query fails loudly instead of materializing rows under the literal
|
|
2463
2523
|
* id `"undefined"` (or collapsing many rows onto one id). Shared with
|
|
2464
|
-
* `@lunora/hyperdrive`'s `projectSourceRow`.
|
|
2524
|
+
* `@lunora/hyperdrive`'s `projectSourceRow`. The returned document's values are
|
|
2525
|
+
* normalized (see {@link normalizeSourceValue}) so a `Date`/`bigint` column never
|
|
2526
|
+
* reaches `stableStringify` un-normalized.
|
|
2465
2527
|
*/
|
|
2466
2528
|
declare const liftSourceId: (row: Record<string, unknown>, options?: {
|
|
2467
2529
|
idColumn?: string;
|
|
@@ -2482,6 +2544,39 @@ declare const isSourceDue: (refresh: SourceRefresh | undefined, lastPolledMs: nu
|
|
|
2482
2544
|
*/
|
|
2483
2545
|
declare const pullExternalSourceTick: (sql: SqlExec, writer: DatabaseWriterLike, client: SourceClientLike, table: string, source: ExternalSourceLike, shardKey: string) => Promise<MaterializeResult>;
|
|
2484
2546
|
/**
|
|
2547
|
+
* Whether an upstream row is a soft-delete tombstone under `column`. A set
|
|
2548
|
+
* `deleted_at` (any non-null value, e.g. a timestamp), an `is_deleted = true`, or a
|
|
2549
|
+
* non-zero flag all read as deleted; `null` / `undefined` / `false` / `0` mean
|
|
2550
|
+
* live. Note an **empty string** reads as deleted (`"" !== 0`), so an upstream that
|
|
2551
|
+
* clears the column to `""` rather than `NULL` for a live row would mis-signal —
|
|
2552
|
+
* use `NULL` for live rows. The incremental query MUST return tombstoned rows
|
|
2553
|
+
* (don't filter `WHERE deleted_at IS NULL`) or the delete is never observed.
|
|
2554
|
+
*/
|
|
2555
|
+
declare const isSoftDeleted: (row: Record<string, unknown>, column: string) => boolean;
|
|
2556
|
+
/**
|
|
2557
|
+
* Run one **incremental** tick (plan 136). Reads the durable watermark for
|
|
2558
|
+
* `(table, shardKey)`; on the first ever poll or when the `reconcileEveryMs` sweep
|
|
2559
|
+
* is due it runs a **full-pull** (seed/GC: {@link runExternalSourceTick} observes
|
|
2560
|
+
* deletes and re-establishes membership), otherwise it pulls only rows past the
|
|
2561
|
+
* watermark via `cursor.query` and upserts them ({@link materializeExternalRowsIncremental},
|
|
2562
|
+
* tombstones → deletes). Either way it advances the watermark to the max cursor
|
|
2563
|
+
* value seen and persists it (and the reconcile timestamp).
|
|
2564
|
+
*
|
|
2565
|
+
* **Crash safety** is by ordering + idempotency, NOT an atomic transaction across
|
|
2566
|
+
* the two write channels (the apply goes through the `writer`; the watermark write
|
|
2567
|
+
* is a raw `sql` write): the watermark advances only AFTER a fully-applied slice,
|
|
2568
|
+
* so a crash between the apply and the watermark write leaves the watermark behind
|
|
2569
|
+
* and the next tick re-pulls the same `>= watermark` slice — which the upsert
|
|
2570
|
+
* short-circuits (unchanged rows) or re-applies idempotently. It only ever replays,
|
|
2571
|
+
* never skips (same self-healing argument as `advanceClientWatermark`).
|
|
2572
|
+
*
|
|
2573
|
+
* Requires `source.cursor` (validated at `defineSchema` for incremental mode); a
|
|
2574
|
+
* missing cursor throws rather than silently degrading to a stuck watermark.
|
|
2575
|
+
*/
|
|
2576
|
+
declare const pullExternalSourceIncrementalTick: (sql: SqlExec, writer: DatabaseWriterLike, client: SourceClientLike, table: string, source: ExternalSourceLike, shardKey: string, nowMs: number) => Promise<{
|
|
2577
|
+
applied: number;
|
|
2578
|
+
}>;
|
|
2579
|
+
/**
|
|
2485
2580
|
* Reserved `functionPath` prefix for admin introspection RPCs. These travel
|
|
2486
2581
|
* over the same `/_lunora/rpc` → shard `/rpc` path as ordinary functions, but
|
|
2487
2582
|
* `ShardDO` intercepts them before user dispatch and serves them from the
|
|
@@ -4325,6 +4420,19 @@ declare abstract class ShardDO {
|
|
|
4325
4420
|
private static rootSizeWarned;
|
|
4326
4421
|
/** Test-only: reset the static "warned once" flag. */
|
|
4327
4422
|
static resetRootSizeWarning(): void;
|
|
4423
|
+
/**
|
|
4424
|
+
* Compute the shared poll alarm's next wake time from both tiers' signals.
|
|
4425
|
+
* Global shapes need the fixed `GLOBAL_SHAPE_POLL_INTERVAL_MS` floor whenever
|
|
4426
|
+
* any are subscribed (a global table has no per-DO op-log, so it can only be
|
|
4427
|
+
* polled). External-source ingest instead reports the earliest NEXT-DUE
|
|
4428
|
+
* timestamp across its non-manual sources (or `undefined` when none exist) —
|
|
4429
|
+
* a source with a large `refresh.everyMs` must sleep until it's actually due,
|
|
4430
|
+
* not spin at the global-shape floor. Returns `undefined` when NEITHER tier
|
|
4431
|
+
* has pending work, so the DO can go fully idle instead of re-arming for no
|
|
4432
|
+
* reason; otherwise the earlier of the two candidate times (never later than
|
|
4433
|
+
* `nowMs`, so a source that's already due arms essentially immediately).
|
|
4434
|
+
*/
|
|
4435
|
+
private static nextPollAlarmTarget;
|
|
4328
4436
|
protected state: ShardDOState;
|
|
4329
4437
|
protected env: unknown;
|
|
4330
4438
|
/**
|
|
@@ -4656,12 +4764,17 @@ declare abstract class ShardDO {
|
|
|
4656
4764
|
/** Hibernation API: invoked on socket error. */
|
|
4657
4765
|
webSocketError(_ws: WebSocket, _error: unknown): void;
|
|
4658
4766
|
/**
|
|
4659
|
-
* Durable Object alarm handler — the heartbeat for `.global()`-table shapes
|
|
4660
|
-
*
|
|
4661
|
-
*
|
|
4662
|
-
*
|
|
4663
|
-
*
|
|
4664
|
-
*
|
|
4767
|
+
* Durable Object alarm handler — the heartbeat for `.global()`-table shapes
|
|
4768
|
+
* AND external-source (`.source(...)`) ingest, which share one alarm. The
|
|
4769
|
+
* runtime wakes this when the poll alarm armed by `scheduleGlobalPoll` fires;
|
|
4770
|
+
* it refreshes every subscribed global shape (diff-poke from the global
|
|
4771
|
+
* backend), materializes any due sourced tables, and re-arms at
|
|
4772
|
+
* {@link ShardDO.nextPollAlarmTarget} — the fixed floor while global shapes
|
|
4773
|
+
* are subscribed, or the earliest source next-due time when only ingest
|
|
4774
|
+
* remains (so a 1-hour-`refresh` source sleeps ~1 hour instead of waking
|
|
4775
|
+
* every 2 s). With neither tier pending, the alarm is not re-armed and the DO
|
|
4776
|
+
* goes idle. A base-only / global-free / source-free DO never arms it, so
|
|
4777
|
+
* this stays dormant there.
|
|
4665
4778
|
*/
|
|
4666
4779
|
alarm(): Promise<void>;
|
|
4667
4780
|
/** Subclasses implement function dispatch. */
|
|
@@ -5328,16 +5441,23 @@ declare abstract class ShardDO {
|
|
|
5328
5441
|
/**
|
|
5329
5442
|
* Poll external-source (`.source(...)`) tables once (plan 077): materialize
|
|
5330
5443
|
* each sourced table's freshly-pulled tenant slice into this DO's SQLite. The
|
|
5331
|
-
* base `ShardDO` has no sourced tables, so it returns `
|
|
5332
|
-
* stays dormant — zero behavior change for every existing DO. The
|
|
5333
|
-
* subclass overrides it to, per sourced table, build a
|
|
5334
|
-
* writer, read the tenant slice from Hyperdrive under this
|
|
5335
|
-
* run `runExternalSourceTick` (read local baseline → diff
|
|
5336
|
-
* validated CDC writer).
|
|
5337
|
-
*
|
|
5338
|
-
*
|
|
5444
|
+
* base `ShardDO` has no sourced tables, so it returns `undefined` and the
|
|
5445
|
+
* ingest tier stays dormant — zero behavior change for every existing DO. The
|
|
5446
|
+
* codegen subclass overrides it to, per sourced table, build a
|
|
5447
|
+
* `createShardCtxDb` writer, read the tenant slice from Hyperdrive under this
|
|
5448
|
+
* DO's shard key, and run `runExternalSourceTick` (read local baseline → diff
|
|
5449
|
+
* → apply via the validated CDC writer).
|
|
5450
|
+
*
|
|
5451
|
+
* Returns the EARLIEST next-due timestamp (absolute epoch ms) across every
|
|
5452
|
+
* non-manual sourced table — `polledAt.get(table) ?? now` plus that source's
|
|
5453
|
+
* `refresh.everyMs` — or `undefined` when every sourced table is
|
|
5454
|
+
* `refresh: "manual"` (or there are none). NOT a bare active count: the
|
|
5455
|
+
* shared poll alarm ({@link ShardDO.alarm}) uses this to re-arm at the exact
|
|
5456
|
+
* next time ingest needs to run, instead of spinning at the fixed
|
|
5457
|
+
* `GLOBAL_SHAPE_POLL_INTERVAL_MS` floor for a source whose `refresh.everyMs`
|
|
5458
|
+
* is, say, an hour away.
|
|
5339
5459
|
*/
|
|
5340
|
-
protected pollExternalSources(): Promise<number>;
|
|
5460
|
+
protected pollExternalSources(): Promise<number | undefined>;
|
|
5341
5461
|
/**
|
|
5342
5462
|
* Arm the shared poll alarm for external-source ingest (plan 077). The alarm is
|
|
5343
5463
|
* shared with the global-shape poll tier; the codegen subclass calls this once
|
|
@@ -6337,11 +6457,18 @@ declare abstract class ShardDO {
|
|
|
6337
6457
|
*/
|
|
6338
6458
|
private saveGlobalSnapshot;
|
|
6339
6459
|
/**
|
|
6340
|
-
* Arm the poll alarm
|
|
6341
|
-
*
|
|
6342
|
-
*
|
|
6343
|
-
*
|
|
6344
|
-
*
|
|
6460
|
+
* Arm the poll alarm if one isn't already pending. Idempotent — every
|
|
6461
|
+
* global-shape seed calls it, but only the first arms the alarm. Degrades to
|
|
6462
|
+
* a no-op when the runtime exposes no `setAlarm` (the unit harness): a global
|
|
6463
|
+
* shape is then seed-only, which the poll-loop tests assert by driving
|
|
6464
|
+
* {@link ShardDO.alarm} directly.
|
|
6465
|
+
*
|
|
6466
|
+
* `atMs` lets {@link ShardDO.alarm} re-arm at a computed target — the
|
|
6467
|
+
* earlier of the fixed global-shape floor and the earliest external-source
|
|
6468
|
+
* next-due time — instead of always the fixed floor. Every OTHER caller
|
|
6469
|
+
* (a fresh global-shape seed, {@link ShardDO.scheduleSourcePoll}'s initial
|
|
6470
|
+
* kick) omits it and gets the original `GLOBAL_SHAPE_POLL_INTERVAL_MS`
|
|
6471
|
+
* default, since neither knows a more precise due time yet.
|
|
6345
6472
|
*/
|
|
6346
6473
|
private scheduleGlobalPoll;
|
|
6347
6474
|
/**
|
|
@@ -6704,4 +6831,4 @@ interface WhereSqlStrategy {
|
|
|
6704
6831
|
* `undefined` when the input imposes no constraint (empty `where`).
|
|
6705
6832
|
*/
|
|
6706
6833
|
declare const compileWhereSql: (where: WhereInput | undefined, strategy: WhereSqlStrategy) => SQL | undefined;
|
|
6707
|
-
export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, AGGREGATE_SQL_FUNCTION, AUTH_METRICS_BUCKETS_TABLE, AUTH_METRICS_BUCKET_MS, AUTH_METRICS_BUCKET_RETENTION, AUTH_METRICS_TABLE, type AdvisoriesResult, type AdvisoryFinding, type AggregateIndexDefinitionLike, type AggregateOp, type AggregateOptions, type AggregateResult, type AggregateTally, type ApplyOnDeleteOptions, type AuditEntry, type AuditLogResult, type AuthMetrics, type AuthMetricsBucket, type BroadcastDelta, CDC_LOG_TABLE, type CacheEntry, type CapturedMailRow, type CdcChange, type Clock, type ColumnMeta, type ColumnMetaLike, ConflictError, type CountArgs, CountRlsUnsupportedError, type CtxDbOptions, DATA_MIGRATION_STATE_TABLE, DEFAULT_MAX_RELATION_KEYS, type DataMigrationDocument, type DataMigrationLike, type DataMigrationTransform, type DatabaseWriterLike, type DependencyTracker, type DeployInfo, type ExportRow, type ExportShardAdminArgs, type ExportShardArgs, type ExternalSourceDiffResult, type ExternalSourceLike, FLAGS_FUNCTION_PREFIX, FUNCTION_METRICS_BUCKETS_TABLE, FUNCTION_METRICS_BUCKET_MS, FUNCTION_METRICS_BUCKET_RETENTION, FUNCTION_METRICS_INDEX_TABLE, FUNCTION_METRICS_TABLE, type FacetColumnOptions, type FacetColumnResult, type FacetValue, type FieldOperators, type FlagEvaluation, type FlagsResult, type FunctionCallStat, type FunctionMetricBucket, type FunctionMetricIndexHit, type FunctionStatsResult, type GroupByEntry, type GroupByOptions, type HibernatableWebSocket, type IdGenerator, type ImportError, type ImportShardAdminArgs, type ImportShardArgs, type ImportShardResult, type IndexDefinitionLike, type IndexRangeBuilderLike, LogBuffer, type LogEntry, type LogEventInput, type LogLevel, type LogSink, MAIL_RETENTION, MAIL_TABLE, MAX_SQL_ROWS, MIN_ADMIN_TOKEN_LENGTH, MIN_AUTH_SECRET_LENGTH, type MaskColumnMetadata, type MaskPoliciesResult, type MaterializeResult, type MigrationDirection, type MigrationRunResult, type MigrationStatus, type MigrationStatusRow, type MutationDelta, type NestedWith, NotFoundError, NotUniqueError, type OnDeleteActionLike, type OrderByInput, type OrderKey, type PaginationOptions, type PitrBookmarkResult, type PitrRestoreArgs, type PitrRestoreResult, type PitrStorage, type QueryArgs, type QueryPage, type QueueMetadata, type QueuesResult, RANK_TIEBREAK, RELATION_FUNCTION_PREFIX, RLS_UNWRAP_SYMBOL, ROOT_DO_SIZE_WARN_BYTES, ROOT_SHARD_NAME, type RankDirection, type RankIndexDefinitionLike, type RankOptions, type RankPage, type RankPageOptions, type RankPageRow, type RankPageRowKey, type RankResult, type RankSortKeyLike, ReactiveCache, type ReactiveCacheOptions, type ReadHook, type ReadTablePageOptions, type RecordAuthEventInput, type RecordFunctionMetricInput, type RecordMailInput, type RelationDefinitionLike, type RenderedSql, type ResolveRelationPredicatesOptions, type ResolveWithOptions, type RestrictableQueryOptions, type RlsPoliciesResult, type RlsPolicyMetadata, RlsRequiredError, type RlsRoleMetadata, type RpcRequest, type RunDataMigrationOptions, type RunShardApplyCdcArgs, type RunShardApplyCdcResult, type RunShardBulkDeleteArgs, type RunShardBulkDeleteResult, type RunShardExportArgs, type RunShardImportArgs, type RunShardMigrationArgs, type RunShardRankBeforeArgs, type RunShardRankPageArgs, type RunShardWriteArgs, type RunShardWriteResult, type RunTriggersOptions, SCAN_DEP, SESSION_DO_TTL_DEFAULT, SHARD_REGISTRY_DO_NAME, type SchedulableWorkflowReferenceLike, type ScheduledFunctionDoc, type SchedulerLike, type SchemaLike, type SearchFilterBuilderLike, type SecurityAuditResult, type SecurityFinding, type SecurityFindingKind, type SecurityFindingLevel, type SelectMatchingIdsOptions, type ServerDefaultContextLike, SessionDO, type SessionRecord, type SettingEntry, type SettingKind, type SettingsResult, type ShapeSubscriptionQuery, ShardDO, type ShardDOOptions, type ShardDOState, type ShardRankPageResult, ShardRegistryDO, type SocketAttachment, type SortDirection, type SourceClientLike, type SourceRefresh, type SqlConsoleResult, type SqlCursor, type SqlEngine, type SqlExec, type StorageRuleMetadata, type StorageRulesResult, type StudioFeaturesResult, type SubscriptionEnvelope, type SubscriptionOutcome, type SubscriptionQuery, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemReaderOptions, type SystemReaderSchedulerLike, type SystemReaderStorageLike, type SystemTableName, type TableColumnsResult, type TableDefinitionLike, type TableIndexInfo, type TableIndexesResult, type TableInfo, type TablePage, type TableReaderLike, type TablesColumnsResult, type TransactionSqlLike, type TriggerContextLike, type TriggerDefinitionLike, type TriggerEventLike, type TriggerOpLike, type TriggerTimingLike, type ValidatorLike, type WhereInput, type WhereSqlStrategy, type WithInput, type WorkflowMetadata, type WorkflowsResult, type WriteEvent, type WriteHook, aggregateSqlFunction, aggregateTableName, applyCdcChanges, applyOnDelete, applySelect, armRestore, assertFlatPredicate, assertReadonly, assertShapeShardable, assertValidClientId, backfillAggregateIndexes, backfillRankIndexes, buildFtsMatch, buildSecurityAudit, buildSeekWhere, clearCapturedMail, coerceAggregateNumber, compileWhereSql, containsRelationPredicate, createDependencyTracker, createShardCtxDb, createSystemReader, decodeCursor, depKey, diffExternalSource, encodeAggregateKey, encodeCursor, encodePartitionKey, ensureAuthMetricsTables, ensureFunctionMetricsTables, ensureMailTable, exportShardRows, exportShardTable, facetColumn, fanOutScalarCounts, foldAggregateTally, ftsTableName, guardWriter, hasTrigger, importShardRows, isRelationPredicate, isSourceDue, liftSourceId, listTables, matchesRankStaticWhere, matchesStaticWhere, materializeExternalRows, mergeWhere, normalizeCountArgument, normalizeIdStructurally, normalizeOrderKeys, parseExportShardArgs, parseImportShardArgs, planAggregateLookup, pullExternalSourceTick, rankTableName, reactiveCacheKey, readAggregateValue, readAuthMetrics, readBookmark, readCapturedMail, readCdcChanges, readExternalSourceBaseline, readFunctionMetricBuckets, readFunctionMetricIndexHits, readFunctionMetrics, readFunctionMetricsTotals, readMigrationStatus, readTablePage, recordAuthEvent, recordCapturedMail, recordFunctionMetric, renderSql, resolveRankPartition, resolveRelationPredicates, resolveWith, runDataMigration, runExternalSourceTick, runReadonlySql, runRowValidators, runShardMigrations, runTriggers, scoreDocument, selectExportTables, selectIndexForAggregate, selectIndexForCount, selectIndexForGroupBy, selectMatchingIds, serveRelationFanout, softDeleteScope, sortColumnName, stableStringify, stableWireKey, stringifySearchText, subscriptionListDeltas, throwingScheduler, tokenizeSearch, trimCdcChanges, validateImportRow };
|
|
6834
|
+
export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, AGGREGATE_SQL_FUNCTION, AUTH_METRICS_BUCKETS_TABLE, AUTH_METRICS_BUCKET_MS, AUTH_METRICS_BUCKET_RETENTION, AUTH_METRICS_TABLE, type AdvisoriesResult, type AdvisoryFinding, type AggregateIndexDefinitionLike, type AggregateOp, type AggregateOptions, type AggregateResult, type AggregateTally, type ApplyOnDeleteOptions, type AuditEntry, type AuditLogResult, type AuthMetrics, type AuthMetricsBucket, type BroadcastDelta, CDC_LOG_TABLE, type CacheEntry, type CapturedMailRow, type CdcChange, type Clock, type ColumnMeta, type ColumnMetaLike, ConflictError, type CountArgs, CountRlsUnsupportedError, type CtxDbOptions, DATA_MIGRATION_STATE_TABLE, DEFAULT_MAX_RELATION_KEYS, type DataMigrationDocument, type DataMigrationLike, type DataMigrationTransform, type DatabaseWriterLike, type DependencyTracker, type DeployInfo, type ExportRow, type ExportShardAdminArgs, type ExportShardArgs, type ExternalSourceDiffResult, type ExternalSourceLike, FLAGS_FUNCTION_PREFIX, FUNCTION_METRICS_BUCKETS_TABLE, FUNCTION_METRICS_BUCKET_MS, FUNCTION_METRICS_BUCKET_RETENTION, FUNCTION_METRICS_INDEX_TABLE, FUNCTION_METRICS_TABLE, type FacetColumnOptions, type FacetColumnResult, type FacetValue, type FieldOperators, type FlagEvaluation, type FlagsResult, type FunctionCallStat, type FunctionMetricBucket, type FunctionMetricIndexHit, type FunctionStatsResult, type GroupByEntry, type GroupByOptions, type HibernatableWebSocket, type IdGenerator, type ImportError, type ImportShardAdminArgs, type ImportShardArgs, type ImportShardResult, type IncrementalMaterializeResult, type IndexDefinitionLike, type IndexRangeBuilderLike, LogBuffer, type LogEntry, type LogEventInput, type LogLevel, type LogSink, MAIL_RETENTION, MAIL_TABLE, MAX_SQL_ROWS, MIN_ADMIN_TOKEN_LENGTH, MIN_AUTH_SECRET_LENGTH, type MaskColumnMetadata, type MaskPoliciesResult, type MaterializeResult, type MigrationDirection, type MigrationRunResult, type MigrationStatus, type MigrationStatusRow, type MutationDelta, type NestedWith, NotFoundError, NotUniqueError, type OnDeleteActionLike, type OrderByInput, type OrderKey, type PaginationOptions, type PitrBookmarkResult, type PitrRestoreArgs, type PitrRestoreResult, type PitrStorage, type QueryArgs, type QueryPage, type QueueMetadata, type QueuesResult, RANK_TIEBREAK, RELATION_FUNCTION_PREFIX, RLS_UNWRAP_SYMBOL, ROOT_DO_SIZE_WARN_BYTES, ROOT_SHARD_NAME, type RankDirection, type RankIndexDefinitionLike, type RankOptions, type RankPage, type RankPageOptions, type RankPageRow, type RankPageRowKey, type RankResult, type RankSortKeyLike, ReactiveCache, type ReactiveCacheOptions, type ReadHook, type ReadTablePageOptions, type RecordAuthEventInput, type RecordFunctionMetricInput, type RecordMailInput, type RelationDefinitionLike, type RenderedSql, type ResolveRelationPredicatesOptions, type ResolveWithOptions, type RestrictableQueryOptions, type RlsPoliciesResult, type RlsPolicyMetadata, RlsRequiredError, type RlsRoleMetadata, type RpcRequest, type RunDataMigrationOptions, type RunShardApplyCdcArgs, type RunShardApplyCdcResult, type RunShardBulkDeleteArgs, type RunShardBulkDeleteResult, type RunShardExportArgs, type RunShardImportArgs, type RunShardMigrationArgs, type RunShardRankBeforeArgs, type RunShardRankPageArgs, type RunShardWriteArgs, type RunShardWriteResult, type RunTriggersOptions, SCAN_DEP, SESSION_DO_TTL_DEFAULT, SHARD_REGISTRY_DO_NAME, type SchedulableWorkflowReferenceLike, type ScheduledFunctionDoc, type SchedulerLike, type SchemaLike, type SearchFilterBuilderLike, type SecurityAuditResult, type SecurityFinding, type SecurityFindingKind, type SecurityFindingLevel, type SelectMatchingIdsOptions, type ServerDefaultContextLike, SessionDO, type SessionRecord, type SettingEntry, type SettingKind, type SettingsResult, type ShapeSubscriptionQuery, ShardDO, type ShardDOOptions, type ShardDOState, type ShardRankPageResult, ShardRegistryDO, type SocketAttachment, type SortDirection, type SourceClientLike, type SourceCursorLike, type SourceRefresh, type SqlConsoleResult, type SqlCursor, type SqlEngine, type SqlExec, type StorageRuleMetadata, type StorageRulesResult, type StudioFeaturesResult, type SubscriptionEnvelope, type SubscriptionOutcome, type SubscriptionQuery, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemReaderOptions, type SystemReaderSchedulerLike, type SystemReaderStorageLike, type SystemTableName, type TableColumnsResult, type TableDefinitionLike, type TableIndexInfo, type TableIndexesResult, type TableInfo, type TablePage, type TableReaderLike, type TablesColumnsResult, type TransactionSqlLike, type TriggerContextLike, type TriggerDefinitionLike, type TriggerEventLike, type TriggerOpLike, type TriggerTimingLike, type ValidatorLike, type WhereInput, type WhereSqlStrategy, type WithInput, type WorkflowMetadata, type WorkflowsResult, type WriteEvent, type WriteHook, aggregateSqlFunction, aggregateTableName, applyCdcChanges, applyOnDelete, applySelect, armRestore, assertFlatPredicate, assertReadonly, assertShapeShardable, assertValidClientId, backfillAggregateIndexes, backfillRankIndexes, buildFtsMatch, buildSecurityAudit, buildSeekWhere, clearCapturedMail, coerceAggregateNumber, compileWhereSql, containsRelationPredicate, createDependencyTracker, createShardCtxDb, createSystemReader, decodeCursor, depKey, diffExternalSource, encodeAggregateKey, encodeCursor, encodePartitionKey, ensureAuthMetricsTables, ensureFunctionMetricsTables, ensureMailTable, exportShardRows, exportShardTable, facetColumn, fanOutScalarCounts, foldAggregateTally, ftsTableName, guardWriter, hasTrigger, importShardRows, isRelationPredicate, isSoftDeleted, isSourceDue, liftSourceId, listTables, matchesRankStaticWhere, matchesStaticWhere, materializeExternalRows, materializeExternalRowsIncremental, mergeWhere, normalizeCountArgument, normalizeIdStructurally, normalizeOrderKeys, parseExportShardArgs, parseImportShardArgs, planAggregateLookup, pullExternalSourceIncrementalTick, pullExternalSourceTick, rankTableName, reactiveCacheKey, readAggregateValue, readAuthMetrics, readBookmark, readCapturedMail, readCdcChanges, readExternalSourceBaseline, readFunctionMetricBuckets, readFunctionMetricIndexHits, readFunctionMetrics, readFunctionMetricsTotals, readMigrationStatus, readTablePage, recordAuthEvent, recordCapturedMail, recordFunctionMetric, renderSql, resolveRankPartition, resolveRelationPredicates, resolveWith, runDataMigration, runExternalSourceTick, runReadonlySql, runRowValidators, runShardMigrations, runTriggers, scoreDocument, selectExportTables, selectIndexForAggregate, selectIndexForCount, selectIndexForGroupBy, selectMatchingIds, serveRelationFanout, softDeleteScope, sortColumnName, stableStringify, stableWireKey, stringifySearchText, subscriptionListDeltas, throwingScheduler, tokenizeSearch, trimCdcChanges, validateImportRow };
|
package/dist/index.mjs
CHANGED
|
@@ -3,13 +3,13 @@ export { AGGREGATE_SQL_FUNCTION, aggregateSqlFunction, matchesStaticWhere, norma
|
|
|
3
3
|
export { aggregateTableName, coerceAggregateNumber, encodeAggregateKey, foldAggregateTally, readAggregateValue } from './packem_shared/aggregateTableName-CxNqY1Sl.mjs';
|
|
4
4
|
export { CountRlsUnsupportedError, mergeWhere, planAggregateLookup, selectIndexForAggregate, selectIndexForCount, selectIndexForGroupBy } from './packem_shared/CountRlsUnsupportedError-BGxj0pgS.mjs';
|
|
5
5
|
export { AUTH_METRICS_BUCKETS_TABLE, AUTH_METRICS_BUCKET_MS, AUTH_METRICS_BUCKET_RETENTION, AUTH_METRICS_TABLE, ensureAuthMetricsTables, readAuthMetrics, recordAuthEvent } from './packem_shared/AUTH_METRICS_BUCKETS_TABLE-CiHHYeJi.mjs';
|
|
6
|
-
export { NotUniqueError, assertValidClientId, createShardCtxDb, normalizeIdStructurally } from './packem_shared/NotUniqueError-
|
|
6
|
+
export { NotUniqueError, assertValidClientId, createShardCtxDb, normalizeIdStructurally } from './packem_shared/NotUniqueError-Ca21uDuU.mjs';
|
|
7
7
|
export { DATA_MIGRATION_STATE_TABLE, readMigrationStatus, runDataMigration } from './packem_shared/DATA_MIGRATION_STATE_TABLE-CYwBpyTr.mjs';
|
|
8
8
|
export { SCAN_DEP, createDependencyTracker, depKey } from './packem_shared/SCAN_DEP-DLJF8dsj.mjs';
|
|
9
9
|
export { renderSql } from './packem_shared/renderSql-D6eUcn2N.mjs';
|
|
10
10
|
export { diffExternalSource } from './packem_shared/diffExternalSource-CovHfdyo.mjs';
|
|
11
|
-
export { materializeExternalRows, readExternalSourceBaseline, runExternalSourceTick } from './packem_shared/materializeExternalRows-
|
|
12
|
-
export { isSourceDue, liftSourceId, pullExternalSourceTick } from './packem_shared/
|
|
11
|
+
export { materializeExternalRows, materializeExternalRowsIncremental, readExternalSourceBaseline, runExternalSourceTick } from './packem_shared/materializeExternalRows-DlQWMlw_.mjs';
|
|
12
|
+
export { isSoftDeleted, isSourceDue, liftSourceId, pullExternalSourceIncrementalTick, pullExternalSourceTick } from './packem_shared/isSoftDeleted-YLKR6JYw.mjs';
|
|
13
13
|
export { FUNCTION_METRICS_BUCKETS_TABLE, FUNCTION_METRICS_BUCKET_MS, FUNCTION_METRICS_BUCKET_RETENTION, FUNCTION_METRICS_INDEX_TABLE, FUNCTION_METRICS_TABLE, ensureFunctionMetricsTables, readFunctionMetricBuckets, readFunctionMetricIndexHits, readFunctionMetrics, readFunctionMetricsTotals, recordFunctionMetric } from './packem_shared/FUNCTION_METRICS_BUCKETS_TABLE-UDNVD7FS.mjs';
|
|
14
14
|
export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, FLAGS_FUNCTION_PREFIX, RELATION_FUNCTION_PREFIX, facetColumn, listTables, readTablePage, selectMatchingIds } from './packem_shared/ADMIN_FUNCTIONS-CAHLZMj8.mjs';
|
|
15
15
|
export { LogBuffer } from './packem_shared/LogBuffer-B_Ezju_N.mjs';
|
|
@@ -26,16 +26,16 @@ export { RLS_UNWRAP_SYMBOL, RlsRequiredError, guardWriter } from './packem_share
|
|
|
26
26
|
export { buildFtsMatch, ftsTableName, scoreDocument, stringifySearchText, tokenizeSearch } from './packem_shared/buildFtsMatch-BLEMawrp.mjs';
|
|
27
27
|
export { M as MIN_ADMIN_TOKEN_LENGTH, a as MIN_AUTH_SECRET_LENGTH, b as buildSecurityAudit } from './packem_shared/security-audit-CucgBice.mjs';
|
|
28
28
|
export { SESSION_DO_TTL_DEFAULT, SessionDO } from './packem_shared/SESSION_DO_TTL_DEFAULT-BnSKgVO4.mjs';
|
|
29
|
-
export { ROOT_DO_SIZE_WARN_BYTES, ROOT_SHARD_NAME, ShardDO } from './packem_shared/ROOT_DO_SIZE_WARN_BYTES-
|
|
29
|
+
export { ROOT_DO_SIZE_WARN_BYTES, ROOT_SHARD_NAME, ShardDO } from './packem_shared/ROOT_DO_SIZE_WARN_BYTES-afQHUhyA.mjs';
|
|
30
30
|
export { SHARD_REGISTRY_DO_NAME, ShardRegistryDO } from './packem_shared/SHARD_REGISTRY_DO_NAME-D99roc-r.mjs';
|
|
31
31
|
export { MAX_SQL_ROWS, assertReadonly, runReadonlySql } from './packem_shared/MAX_SQL_ROWS-iFAA8FbD.mjs';
|
|
32
32
|
export { createSystemReader } from './packem_shared/createSystemReader-D12eNH13.mjs';
|
|
33
33
|
export { ConflictError } from './packem_shared/ConflictError-CLoq37xH.mjs';
|
|
34
34
|
export { hasTrigger, runTriggers } from './packem_shared/hasTrigger-5N6_Fx0A.mjs';
|
|
35
35
|
export { compileWhereSql } from './packem_shared/compileWhereSql-DE6yfRcQ.mjs';
|
|
36
|
-
export { CDC_LOG_TABLE, applyCdcChanges, readCdcChanges, trimCdcChanges } from './packem_shared/CDC_LOG_TABLE-
|
|
36
|
+
export { CDC_LOG_TABLE, applyCdcChanges, readCdcChanges, trimCdcChanges } from './packem_shared/CDC_LOG_TABLE-uwOJxJJZ.mjs';
|
|
37
37
|
export { backfillAggregateIndexes, backfillRankIndexes } from './packem_shared/backfillAggregateIndexes-DDoT-UUI.mjs';
|
|
38
|
-
export { runShardMigrations } from './packem_shared/runShardMigrations-
|
|
38
|
+
export { runShardMigrations } from './packem_shared/runShardMigrations-DFzx6Qld.mjs';
|
|
39
39
|
export { stableStringify } from './packem_shared/stableStringify-mC40mZts.mjs';
|
|
40
40
|
export { stableWireKey } from './packem_shared/stableWireKey-DKuXO7T5.mjs';
|
|
41
41
|
export { subscriptionListDeltas } from './packem_shared/subscriptionListDeltas-CT76bYny.mjs';
|
|
@@ -79,7 +79,7 @@ const bumpCdcEpoch = (sql$1) => {
|
|
|
79
79
|
};
|
|
80
80
|
const applyCdcChange = async (writer, change) => {
|
|
81
81
|
if (change.op === "delete") {
|
|
82
|
-
await writer.delete(change.id);
|
|
82
|
+
await writer.delete(change.id, change.table);
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
85
|
const document = change.doc ?? {};
|
|
@@ -3,8 +3,8 @@ import { sql } from 'drizzle-orm';
|
|
|
3
3
|
import { matchesStaticWhere, aggregateSqlFunction, normalizeCountArgument, throwingScheduler } from './AGGREGATE_SQL_FUNCTION-CQsu2Xga.mjs';
|
|
4
4
|
import { encodeAggregateKey, foldAggregateTally, aggregateTableName, coerceAggregateNumber, readAggregateValue } from './aggregateTableName-CxNqY1Sl.mjs';
|
|
5
5
|
import { mergeWhere, CountRlsUnsupportedError, selectIndexForGroupBy, selectIndexForCount, selectIndexForAggregate } from './CountRlsUnsupportedError-BGxj0pgS.mjs';
|
|
6
|
-
import { appendCdcChange } from './CDC_LOG_TABLE-
|
|
7
|
-
export { CDC_LOG_TABLE, applyCdcChanges, bumpCdcEpoch, minCdcSeq, readCdcChanges, readCdcCursor, readCdcEpoch, trimCdcChanges } from './CDC_LOG_TABLE-
|
|
6
|
+
import { appendCdcChange } from './CDC_LOG_TABLE-uwOJxJJZ.mjs';
|
|
7
|
+
export { CDC_LOG_TABLE, applyCdcChanges, bumpCdcEpoch, minCdcSeq, readCdcChanges, readCdcCursor, readCdcEpoch, trimCdcChanges } from './CDC_LOG_TABLE-uwOJxJJZ.mjs';
|
|
8
8
|
import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
|
|
9
9
|
import { i as isFtsAvailable, D as DOC_COLUMN$1, r as rowToDocument, A as AGG_KEY, a as AGG_VALUE, b as AGG_COUNT, d as aggUpsertSql, j as jsonPathSql, q as quoteIdentifier, t as tableColumns, e as qualifiedJsonPathSql } from './do-sql-BCHCWtrD.mjs';
|
|
10
10
|
import { param } from './renderSql-D6eUcn2N.mjs';
|
|
@@ -23,7 +23,7 @@ import { runTriggers } from './hasTrigger-5N6_Fx0A.mjs';
|
|
|
23
23
|
import { compileWhereSql } from './compileWhereSql-DE6yfRcQ.mjs';
|
|
24
24
|
export { backfillAggregateIndexes, backfillRankIndexes } from './backfillAggregateIndexes-DDoT-UUI.mjs';
|
|
25
25
|
export { C as CLIENT_WATERMARK_TABLE, G as GLOBAL_SHAPE_SNAPSHOT_TABLE, I as IDEMPOTENCY_TABLE, c as advanceClientWatermark, d as deleteGlobalShapeSnapshot, e as deleteGlobalShapeSnapshotsForConnection, m as migrateClientWatermark, b as migrateGlobalShapeSnapshot, r as readClientWatermark, f as readGlobalShapeSnapshot, g as readIdempotent, t as trimIdempotent, w as writeGlobalShapeSnapshot, h as writeIdempotent } from './ctx-db-idempotency-BdcNpvY4.mjs';
|
|
26
|
-
export { runShardMigrations } from './runShardMigrations-
|
|
26
|
+
export { runShardMigrations } from './runShardMigrations-DFzx6Qld.mjs';
|
|
27
27
|
export { a as selectShapeMemberIds, s as selectShapeRows } from './ctx-db-shapes-Cz9dHyh1.mjs';
|
|
28
28
|
|
|
29
29
|
const rankIndexFieldsUnchanged = (index, previous, next) => {
|
|
@@ -21,7 +21,7 @@ import { i as isDevEnvironment, c as buildSettings, b as buildSecurityAudit } fr
|
|
|
21
21
|
import { runReadonlySql } from './MAX_SQL_ROWS-iFAA8FbD.mjs';
|
|
22
22
|
import { ConflictError } from './ConflictError-CLoq37xH.mjs';
|
|
23
23
|
import { e as deleteGlobalShapeSnapshotsForConnection, g as readIdempotent, h as writeIdempotent, t as trimIdempotent, r as readClientWatermark, m as migrateClientWatermark, c as advanceClientWatermark, d as deleteGlobalShapeSnapshot, f as readGlobalShapeSnapshot, w as writeGlobalShapeSnapshot } from './ctx-db-idempotency-BdcNpvY4.mjs';
|
|
24
|
-
import { CDC_LOG_TABLE, readCdcChanges, readCdcCursor, readCdcEpoch, minCdcSeq, bumpCdcEpoch } from './CDC_LOG_TABLE-
|
|
24
|
+
import { CDC_LOG_TABLE, readCdcChanges, readCdcCursor, readCdcEpoch, minCdcSeq, bumpCdcEpoch } from './CDC_LOG_TABLE-uwOJxJJZ.mjs';
|
|
25
25
|
import { stableStringify } from './stableStringify-mC40mZts.mjs';
|
|
26
26
|
import { a as selectShapeMemberIds, s as selectShapeRows } from './ctx-db-shapes-Cz9dHyh1.mjs';
|
|
27
27
|
|
|
@@ -2088,6 +2088,25 @@ class ShardDO {
|
|
|
2088
2088
|
static resetRootSizeWarning() {
|
|
2089
2089
|
ShardDO.rootSizeWarned = false;
|
|
2090
2090
|
}
|
|
2091
|
+
/**
|
|
2092
|
+
* Compute the shared poll alarm's next wake time from both tiers' signals.
|
|
2093
|
+
* Global shapes need the fixed `GLOBAL_SHAPE_POLL_INTERVAL_MS` floor whenever
|
|
2094
|
+
* any are subscribed (a global table has no per-DO op-log, so it can only be
|
|
2095
|
+
* polled). External-source ingest instead reports the earliest NEXT-DUE
|
|
2096
|
+
* timestamp across its non-manual sources (or `undefined` when none exist) —
|
|
2097
|
+
* a source with a large `refresh.everyMs` must sleep until it's actually due,
|
|
2098
|
+
* not spin at the global-shape floor. Returns `undefined` when NEITHER tier
|
|
2099
|
+
* has pending work, so the DO can go fully idle instead of re-arming for no
|
|
2100
|
+
* reason; otherwise the earlier of the two candidate times (never later than
|
|
2101
|
+
* `nowMs`, so a source that's already due arms essentially immediately).
|
|
2102
|
+
*/
|
|
2103
|
+
static nextPollAlarmTarget(globalShapesRemaining, nextSourceDueAt, nowMs) {
|
|
2104
|
+
const globalTarget = globalShapesRemaining > 0 ? nowMs + ShardDO.GLOBAL_SHAPE_POLL_INTERVAL_MS : void 0;
|
|
2105
|
+
if (globalTarget === void 0) {
|
|
2106
|
+
return nextSourceDueAt === void 0 ? void 0 : Math.max(nextSourceDueAt, nowMs);
|
|
2107
|
+
}
|
|
2108
|
+
return nextSourceDueAt === void 0 ? globalTarget : Math.min(globalTarget, nextSourceDueAt);
|
|
2109
|
+
}
|
|
2091
2110
|
state;
|
|
2092
2111
|
env;
|
|
2093
2112
|
/**
|
|
@@ -2709,31 +2728,38 @@ class ShardDO {
|
|
|
2709
2728
|
webSocketError(_ws, _error) {
|
|
2710
2729
|
}
|
|
2711
2730
|
/**
|
|
2712
|
-
* Durable Object alarm handler — the heartbeat for `.global()`-table shapes
|
|
2713
|
-
*
|
|
2714
|
-
*
|
|
2715
|
-
*
|
|
2716
|
-
*
|
|
2717
|
-
*
|
|
2731
|
+
* Durable Object alarm handler — the heartbeat for `.global()`-table shapes
|
|
2732
|
+
* AND external-source (`.source(...)`) ingest, which share one alarm. The
|
|
2733
|
+
* runtime wakes this when the poll alarm armed by `scheduleGlobalPoll` fires;
|
|
2734
|
+
* it refreshes every subscribed global shape (diff-poke from the global
|
|
2735
|
+
* backend), materializes any due sourced tables, and re-arms at
|
|
2736
|
+
* {@link ShardDO.nextPollAlarmTarget} — the fixed floor while global shapes
|
|
2737
|
+
* are subscribed, or the earliest source next-due time when only ingest
|
|
2738
|
+
* remains (so a 1-hour-`refresh` source sleeps ~1 hour instead of waking
|
|
2739
|
+
* every 2 s). With neither tier pending, the alarm is not re-armed and the DO
|
|
2740
|
+
* goes idle. A base-only / global-free / source-free DO never arms it, so
|
|
2741
|
+
* this stays dormant there.
|
|
2718
2742
|
*/
|
|
2719
2743
|
async alarm() {
|
|
2720
2744
|
this.globalPollScheduled = false;
|
|
2721
|
-
let
|
|
2745
|
+
let globalShapesRemaining;
|
|
2722
2746
|
try {
|
|
2723
|
-
|
|
2747
|
+
globalShapesRemaining = await this.pollGlobalShapes();
|
|
2724
2748
|
} catch (error) {
|
|
2725
2749
|
this.recordShapeError("shape:poll", error);
|
|
2726
|
-
|
|
2750
|
+
globalShapesRemaining = 1;
|
|
2727
2751
|
}
|
|
2752
|
+
let nextSourceDueAt;
|
|
2728
2753
|
try {
|
|
2729
|
-
|
|
2754
|
+
nextSourceDueAt = await this.pollExternalSources();
|
|
2730
2755
|
} catch (error) {
|
|
2731
2756
|
this.recordShapeError("source:poll", error);
|
|
2732
|
-
|
|
2757
|
+
nextSourceDueAt = Date.now() + ShardDO.GLOBAL_SHAPE_POLL_INTERVAL_MS;
|
|
2733
2758
|
}
|
|
2734
2759
|
await this.flushChangedTables();
|
|
2735
|
-
|
|
2736
|
-
|
|
2760
|
+
const nextAlarmAt = ShardDO.nextPollAlarmTarget(globalShapesRemaining, nextSourceDueAt, Date.now());
|
|
2761
|
+
if (nextAlarmAt !== void 0) {
|
|
2762
|
+
await this.scheduleGlobalPoll(nextAlarmAt);
|
|
2737
2763
|
}
|
|
2738
2764
|
}
|
|
2739
2765
|
/**
|
|
@@ -3881,18 +3907,25 @@ class ShardDO {
|
|
|
3881
3907
|
/**
|
|
3882
3908
|
* Poll external-source (`.source(...)`) tables once (plan 077): materialize
|
|
3883
3909
|
* each sourced table's freshly-pulled tenant slice into this DO's SQLite. The
|
|
3884
|
-
* base `ShardDO` has no sourced tables, so it returns `
|
|
3885
|
-
* stays dormant — zero behavior change for every existing DO. The
|
|
3886
|
-
* subclass overrides it to, per sourced table, build a
|
|
3887
|
-
* writer, read the tenant slice from Hyperdrive under this
|
|
3888
|
-
* run `runExternalSourceTick` (read local baseline → diff
|
|
3889
|
-
* validated CDC writer).
|
|
3890
|
-
*
|
|
3891
|
-
*
|
|
3910
|
+
* base `ShardDO` has no sourced tables, so it returns `undefined` and the
|
|
3911
|
+
* ingest tier stays dormant — zero behavior change for every existing DO. The
|
|
3912
|
+
* codegen subclass overrides it to, per sourced table, build a
|
|
3913
|
+
* `createShardCtxDb` writer, read the tenant slice from Hyperdrive under this
|
|
3914
|
+
* DO's shard key, and run `runExternalSourceTick` (read local baseline → diff
|
|
3915
|
+
* → apply via the validated CDC writer).
|
|
3916
|
+
*
|
|
3917
|
+
* Returns the EARLIEST next-due timestamp (absolute epoch ms) across every
|
|
3918
|
+
* non-manual sourced table — `polledAt.get(table) ?? now` plus that source's
|
|
3919
|
+
* `refresh.everyMs` — or `undefined` when every sourced table is
|
|
3920
|
+
* `refresh: "manual"` (or there are none). NOT a bare active count: the
|
|
3921
|
+
* shared poll alarm ({@link ShardDO.alarm}) uses this to re-arm at the exact
|
|
3922
|
+
* next time ingest needs to run, instead of spinning at the fixed
|
|
3923
|
+
* `GLOBAL_SHAPE_POLL_INTERVAL_MS` floor for a source whose `refresh.everyMs`
|
|
3924
|
+
* is, say, an hour away.
|
|
3892
3925
|
*/
|
|
3893
3926
|
// eslint-disable-next-line class-methods-use-this -- base-class override hook: the codegen subclass implements the real Hyperdrive-backed poll
|
|
3894
3927
|
pollExternalSources() {
|
|
3895
|
-
return Promise.resolve(0);
|
|
3928
|
+
return Promise.resolve(void 0);
|
|
3896
3929
|
}
|
|
3897
3930
|
/**
|
|
3898
3931
|
* Arm the shared poll alarm for external-source ingest (plan 077). The alarm is
|
|
@@ -6171,13 +6204,20 @@ class ShardDO {
|
|
|
6171
6204
|
}
|
|
6172
6205
|
}
|
|
6173
6206
|
/**
|
|
6174
|
-
* Arm the poll alarm
|
|
6175
|
-
*
|
|
6176
|
-
*
|
|
6177
|
-
*
|
|
6178
|
-
*
|
|
6179
|
-
|
|
6180
|
-
|
|
6207
|
+
* Arm the poll alarm if one isn't already pending. Idempotent — every
|
|
6208
|
+
* global-shape seed calls it, but only the first arms the alarm. Degrades to
|
|
6209
|
+
* a no-op when the runtime exposes no `setAlarm` (the unit harness): a global
|
|
6210
|
+
* shape is then seed-only, which the poll-loop tests assert by driving
|
|
6211
|
+
* {@link ShardDO.alarm} directly.
|
|
6212
|
+
*
|
|
6213
|
+
* `atMs` lets {@link ShardDO.alarm} re-arm at a computed target — the
|
|
6214
|
+
* earlier of the fixed global-shape floor and the earliest external-source
|
|
6215
|
+
* next-due time — instead of always the fixed floor. Every OTHER caller
|
|
6216
|
+
* (a fresh global-shape seed, {@link ShardDO.scheduleSourcePoll}'s initial
|
|
6217
|
+
* kick) omits it and gets the original `GLOBAL_SHAPE_POLL_INTERVAL_MS`
|
|
6218
|
+
* default, since neither knows a more precise due time yet.
|
|
6219
|
+
*/
|
|
6220
|
+
async scheduleGlobalPoll(atMs) {
|
|
6181
6221
|
if (this.globalPollScheduled) {
|
|
6182
6222
|
return;
|
|
6183
6223
|
}
|
|
@@ -6187,7 +6227,7 @@ class ShardDO {
|
|
|
6187
6227
|
}
|
|
6188
6228
|
this.globalPollScheduled = true;
|
|
6189
6229
|
try {
|
|
6190
|
-
await setAlarm.call(this.state.storage, Date.now() + ShardDO.GLOBAL_SHAPE_POLL_INTERVAL_MS);
|
|
6230
|
+
await setAlarm.call(this.state.storage, atMs ?? Date.now() + ShardDO.GLOBAL_SHAPE_POLL_INTERVAL_MS);
|
|
6191
6231
|
} catch {
|
|
6192
6232
|
this.globalPollScheduled = false;
|
|
6193
6233
|
}
|
|
@@ -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-DlQWMlw_.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-Cz9dHyh1.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-BGx4v2B6.mjs → runShardMigrations-DFzx6Qld.mjs}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { sql } from 'drizzle-orm';
|
|
2
2
|
import { aggregateTableName } from './aggregateTableName-CxNqY1Sl.mjs';
|
|
3
|
-
import { migrateCdcLog, migrateCdcMeta } from './CDC_LOG_TABLE-
|
|
3
|
+
import { migrateCdcLog, migrateCdcMeta } from './CDC_LOG_TABLE-uwOJxJJZ.mjs';
|
|
4
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
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-BCHCWtrD.mjs';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/do",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.33",
|
|
4
4
|
"description": "Lunora Durable Objects: ShardDO (SQLite, OCC, hibernated WebSocket subscriptions) and SessionDO",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@lunora/errors": "1.0.0-alpha.5",
|
|
50
|
-
"@lunora/fingerprint": "1.0.0-alpha.
|
|
50
|
+
"@lunora/fingerprint": "1.0.0-alpha.3",
|
|
51
51
|
"@visulima/redact": "3.0.0",
|
|
52
52
|
"drizzle-orm": "^0.45.2"
|
|
53
53
|
},
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { LunoraError } from '@lunora/errors';
|
|
2
|
-
import { runExternalSourceTick } from './materializeExternalRows-CoGmFmsY.mjs';
|
|
3
|
-
|
|
4
|
-
const liftSourceId = (row, options = {}) => {
|
|
5
|
-
const { idColumn = "id", map } = options;
|
|
6
|
-
const idValue = row[idColumn];
|
|
7
|
-
if (idValue === void 0 || idValue === null) {
|
|
8
|
-
throw new LunoraError("INTERNAL", `external-source: row is missing id column "${idColumn}"`);
|
|
9
|
-
}
|
|
10
|
-
if (typeof idValue !== "string" && typeof idValue !== "number" && typeof idValue !== "bigint") {
|
|
11
|
-
throw new TypeError(`external-source: id column "${idColumn}" must be a string or number`);
|
|
12
|
-
}
|
|
13
|
-
const id = String(idValue);
|
|
14
|
-
if (map) {
|
|
15
|
-
return { ...map(row), _id: id };
|
|
16
|
-
}
|
|
17
|
-
const body = {};
|
|
18
|
-
for (const [key, value] of Object.entries(row)) {
|
|
19
|
-
if (key !== idColumn) {
|
|
20
|
-
body[key] = value;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return { ...body, _id: id };
|
|
24
|
-
};
|
|
25
|
-
const isSourceDue = (refresh, lastPolledMs, nowMs) => {
|
|
26
|
-
if (refresh === "manual") {
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
if (refresh === void 0 || lastPolledMs === void 0) {
|
|
30
|
-
return true;
|
|
31
|
-
}
|
|
32
|
-
return nowMs - lastPolledMs >= refresh.everyMs;
|
|
33
|
-
};
|
|
34
|
-
const pullExternalSourceTick = async (sql, writer, client, table, source, shardKey) => {
|
|
35
|
-
const parameters = source.tenantBy ? source.tenantBy(shardKey) : [];
|
|
36
|
-
const rows = await client.query(source.query, parameters);
|
|
37
|
-
const documents = rows.map((row) => liftSourceId(row, { idColumn: source.idColumn, map: source.map }));
|
|
38
|
-
return runExternalSourceTick(sql, writer, documents, { columns: source.columns, table });
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export { isSourceDue, liftSourceId, pullExternalSourceTick };
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { applyCdcChanges } from './CDC_LOG_TABLE-DjJEHiM2.mjs';
|
|
2
|
-
import { s as selectShapeRows } from './ctx-db-shapes-Cz9dHyh1.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 readExternalSourceBaseline = (sql, table, columns) => {
|
|
12
|
-
const baseline = /* @__PURE__ */ new Map();
|
|
13
|
-
for (const { doc, id } of selectShapeRows(sql, table, void 0)) {
|
|
14
|
-
baseline.set(id, stableStringify(projectExternalSourceRow({ ...doc, _id: id }, columns)));
|
|
15
|
-
}
|
|
16
|
-
return baseline;
|
|
17
|
-
};
|
|
18
|
-
const runExternalSourceTick = async (sql, writer, pulled, options) => {
|
|
19
|
-
const baseline = readExternalSourceBaseline(sql, options.table, options.columns);
|
|
20
|
-
return materializeExternalRows(writer, pulled, baseline, options);
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export { materializeExternalRows, readExternalSourceBaseline, runExternalSourceTick };
|