@lunora/do 1.0.0-alpha.5 → 1.0.0-alpha.7

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 CHANGED
@@ -2292,6 +2292,7 @@ declare const ADMIN_FUNCTIONS: {
2292
2292
  readonly getSettings: "__lunora_admin__:getSettings";
2293
2293
  readonly getWorkflowInstanceStatus: "__lunora_admin__:getWorkflowInstanceStatus";
2294
2294
  readonly importShard: "__lunora_admin__:importShard";
2295
+ readonly listQueues: "__lunora_admin__:listQueues";
2295
2296
  readonly listTables: "__lunora_admin__:listTables";
2296
2297
  readonly listWorkflows: "__lunora_admin__:listWorkflows";
2297
2298
  readonly maskPolicies: "__lunora_admin__:maskPolicies";
@@ -2575,11 +2576,13 @@ interface StudioFeaturesResult {
2575
2576
  mail: boolean;
2576
2577
  /** `@lunora/payment` is used (import or `ctx.payments`) or a declared dependency. */
2577
2578
  payments: boolean;
2579
+ /** `@lunora/queue` / `ctx.queues` is used, the app declares queues, or it is a declared dependency. */
2580
+ queues: boolean;
2578
2581
  /** `@lunora/scheduler` / `ctx.scheduler` is used, the app declares crons, or it is a declared dependency. */
2579
2582
  scheduler: boolean;
2580
2583
  /** `@lunora/storage` / `ctx.storage` is used, the schema declares storage columns/rules, or it is a declared dependency. */
2581
2584
  storage: boolean;
2582
- /** The schema declares vector indexes, `@lunora/vectors` / `ctx.vectors` is used, or it is a declared dependency. */
2585
+ /** The schema declares vector indexes, `@lunora/bindings/vectors` / `ctx.vectors` is used, or it is a declared dependency. */
2583
2586
  vectors: boolean;
2584
2587
  /** `@lunora/workflow` / `ctx.workflows` is used, the app declares workflows, or it is a declared dependency. */
2585
2588
  workflows: boolean;
@@ -2605,6 +2608,28 @@ interface WorkflowsResult {
2605
2608
  workflows: WorkflowMetadata[];
2606
2609
  }
2607
2610
  /**
2611
+ * One declared Cloudflare Queue, surfaced by `__lunora_admin__:listQueues` for
2612
+ * the studio's Queues page. Statically discovered by `@lunora/codegen` from
2613
+ * `lunora/queues.ts` (the codegen subclass overrides the base hook); queues are
2614
+ * not Durable Objects and carry no runtime state in the shard, so this is pure
2615
+ * declaration metadata. `binding` is the generated `QUEUE_*` producer binding,
2616
+ * `name` the deployed `queues.producers[].queue`, `exportName` the
2617
+ * `lunora/queues.ts` export (`ctx.queues.<exportName>`), `mode` whether the
2618
+ * queue is consumed by a worker (`push`) or polled externally (`pull`), and
2619
+ * `deadLetterQueue` the optional DLQ a push consumer dead-letters to.
2620
+ */
2621
+ interface QueueMetadata {
2622
+ binding: string;
2623
+ deadLetterQueue?: string;
2624
+ exportName: string;
2625
+ mode: "pull" | "push";
2626
+ name: string;
2627
+ }
2628
+ /** Payload of a `__lunora_admin__:listQueues` call: every declared queue, sorted by export name. */
2629
+ interface QueuesResult {
2630
+ queues: QueueMetadata[];
2631
+ }
2632
+ /**
2608
2633
  * Lifecycle state of a workflow instance, mirrored from `@lunora/workflow`'s
2609
2634
  * `WorkflowInstanceStatus` so `@lunora/do` carries no dependency on the workflow
2610
2635
  * package. Returned by `getWorkflowInstanceStatus` and `createWorkflowInstance`.
@@ -3641,6 +3666,44 @@ declare class SessionDO {
3641
3666
  private handleRevoke;
3642
3667
  }
3643
3668
  /**
3669
+ * Diff the previously-sent list snapshot (`previousJson`, the memo's
3670
+ * `lastJson`) against the new query result and produce per-row
3671
+ * {@link MutationDelta}s the client can merge in place via `applyDelta` —
3672
+ * Convex-parity live-pagination deltas (server half of gap #20).
3673
+ *
3674
+ * Returns `undefined` (caller falls back to a full `{type:"data"}` snapshot)
3675
+ * unless ALL of these hold:
3676
+ *
3677
+ * 1. `previousJson` parses to an array (there IS a previous list to diff against).
3678
+ * 2. `nextResult` is also an array.
3679
+ * 3. Every row in both arrays is a plain object carrying a string `_id`.
3680
+ * 4. Order preservation — rows present in BOTH arrays appear in the same relative order.
3681
+ * 5. Chattiness cap — the number of deltas does not exceed the new array length (a near-total change is cheaper as a snapshot).
3682
+ *
3683
+ * Diff is keyed by `_id`: rows only in prev → `delete`; rows only in next →
3684
+ * `insert`; rows in both whose JSON differs → `update`. Insert/update carry the
3685
+ * full new `row`; delete omits it (matching the wire contract `@lunora/client`
3686
+ * parses). Deltas are ordered deletes-then-inserts/updates so the client never
3687
+ * sees a transient over-length page.
3688
+ *
3689
+ * Per-row serialization is done exactly **once** per refresh (finding #6). Each
3690
+ * row is stringified a single time into a fingerprint reused for both the
3691
+ * `prev !== next` change-detection compare and — when the caller passes the
3692
+ * optional `frames` sink — the pre-serialized delta frame body. The returned
3693
+ * `MutationDelta[]` shape is unchanged; `frames`, when supplied, receives the
3694
+ * exact `JSON.stringify(delta)` string for each returned delta, in the same
3695
+ * order, so the caller can splice it straight into the `{type:"delta"}` frame
3696
+ * without serializing the delta (and the row inside it) a second time.
3697
+ * @returns the per-row deltas to send, or `undefined` when any precondition fails and a full snapshot should be sent instead
3698
+ */
3699
+ declare const subscriptionListDeltas: (previousJson: string, nextResult: unknown, table: string, frames?: string[]) => MutationDelta[] | undefined;
3700
+ /**
3701
+ * Send one WebSocket frame, reporting whether it left the socket. A throw from
3702
+ * `ws.send` (socket closed mid-flush, outbound buffer gone) is the only
3703
+ * delivery-failure signal the runtime exposes; callers use the boolean to decide
3704
+ * whether to advance a subscription's delivered-diff baseline.
3705
+ */
3706
+ /**
3644
3707
  * Optional programmatic log sink, resolved from `createShardDO({ observability })`.
3645
3708
  * Structurally a subset of `@lunora/runtime`'s `ObservabilitySink`, so a user can
3646
3709
  * pass the SAME sink object to `createWorker` (which drives `onRpc`) and
@@ -3893,38 +3956,6 @@ interface RunShardRankPageArgs {
3893
3956
  take?: number;
3894
3957
  }
3895
3958
  /**
3896
- * Diff the previously-sent list snapshot (`previousJson`, the memo's
3897
- * `lastJson`) against the new query result and produce per-row
3898
- * {@link MutationDelta}s the client can merge in place via `applyDelta` —
3899
- * Convex-parity live-pagination deltas (server half of gap #20).
3900
- *
3901
- * Returns `undefined` (caller falls back to a full `{type:"data"}` snapshot)
3902
- * unless ALL of these hold:
3903
- *
3904
- * 1. `previousJson` parses to an array (there IS a previous list to diff against).
3905
- * 2. `nextResult` is also an array.
3906
- * 3. Every row in both arrays is a plain object carrying a string `_id`.
3907
- * 4. Order preservation — rows present in BOTH arrays appear in the same relative order.
3908
- * 5. Chattiness cap — the number of deltas does not exceed the new array length (a near-total change is cheaper as a snapshot).
3909
- *
3910
- * Diff is keyed by `_id`: rows only in prev → `delete`; rows only in next →
3911
- * `insert`; rows in both whose JSON differs → `update`. Insert/update carry the
3912
- * full new `row`; delete omits it (matching the wire contract `@lunora/client`
3913
- * parses). Deltas are ordered deletes-then-inserts/updates so the client never
3914
- * sees a transient over-length page.
3915
- *
3916
- * Per-row serialization is done exactly **once** per refresh (finding #6). Each
3917
- * row is stringified a single time into a fingerprint reused for both the
3918
- * `prev !== next` change-detection compare and — when the caller passes the
3919
- * optional `frames` sink — the pre-serialized delta frame body. The returned
3920
- * `MutationDelta[]` shape is unchanged; `frames`, when supplied, receives the
3921
- * exact `JSON.stringify(delta)` string for each returned delta, in the same
3922
- * order, so the caller can splice it straight into the `{type:"delta"}` frame
3923
- * without serializing the delta (and the row inside it) a second time.
3924
- * @returns the per-row deltas to send, or `undefined` when any precondition fails and a full snapshot should be sent instead
3925
- */
3926
- declare const subscriptionListDeltas: (previousJson: string, nextResult: unknown, table: string, frames?: string[]) => MutationDelta[] | undefined;
3927
- /**
3928
3959
  * Threshold at which a `__root__` DO triggers the size warning. 1 GiB —
3929
3960
  * exactly 10% of the 10 GiB per-DO SQLite ceiling, leaving plenty of runway
3930
3961
  * to plan a `.shardBy()` migration before the wall hits.
@@ -4106,6 +4137,18 @@ declare abstract class ShardDO {
4106
4137
  */
4107
4138
  private pendingChangedTables;
4108
4139
  /**
4140
+ * Coalesced set of tables awaiting a subscription-refresh pass, merged
4141
+ * across every {@link ShardDO.flushChangedTables} call that lands while a
4142
+ * pass is already draining. The single drain loop
4143
+ * ({@link ShardDO.drainSubscriptionRefreshes}) owns this set; a burst of N
4144
+ * writes to the same table therefore collapses into one (or two) refresh
4145
+ * passes instead of N, so each affected subscription's handler re-runs once
4146
+ * per burst rather than once per write. `undefined` when nothing is pending.
4147
+ */
4148
+ private pendingRefreshTables;
4149
+ /** True while {@link ShardDO.drainSubscriptionRefreshes} is running; the single-waiter gate that coalesces concurrent flushes. */
4150
+ private refreshInFlight;
4151
+ /**
4109
4152
  * Last pushed result per `(socket, subId)`, keyed by socket. Lets
4110
4153
  * `refreshSubscriptions` skip re-running queries whose tables were
4111
4154
  * untouched and suppress pushes when the re-run result is unchanged. Held
@@ -4477,6 +4520,15 @@ declare abstract class ShardDO {
4477
4520
  */
4478
4521
  protected studioFeatures(): StudioFeaturesResult;
4479
4522
  /**
4523
+ * The Cloudflare Queues declared by this app, surfaced via
4524
+ * `__lunora_admin__:listQueues` for the studio's Queues page. Queues are NOT
4525
+ * Durable Objects and hold no shard state, so this is pure declaration
4526
+ * metadata statically discovered by `@lunora/codegen` from `lunora/queues.ts`
4527
+ * and emitted into the generated subclass, which overrides this. The base
4528
+ * class can't see the user's project, so it reports none.
4529
+ */
4530
+ protected queuesMetadata(): QueuesResult;
4531
+ /**
4480
4532
  * The Cloudflare Workflows declared by this app, surfaced via
4481
4533
  * `__lunora_admin__:listWorkflows` for the studio's Workflows page. Workflows
4482
4534
  * are NOT Durable Objects and hold no shard state, so this is pure
@@ -5269,6 +5321,17 @@ declare abstract class ShardDO {
5269
5321
  */
5270
5322
  private flushChangedTables;
5271
5323
  /**
5324
+ * Drain {@link ShardDO.pendingRefreshTables} one coalesced batch at a time
5325
+ * until it is empty, then release the {@link ShardDO.refreshInFlight} gate.
5326
+ * Tables merged by a `flushChangedTables` that lands mid-pass are picked up
5327
+ * by the next loop iteration, so every committed write is observed by a
5328
+ * refresh that runs after it — bursts simply share a pass. The post-write
5329
+ * high-watermark and live-socket set are re-read inside each
5330
+ * `refreshSubscriptions` call, so a later batch always reflects the latest
5331
+ * committed state.
5332
+ */
5333
+ private drainSubscriptionRefreshes;
5334
+ /**
5272
5335
  * For every live subscription whose query reads one of `changed`, re-run
5273
5336
  * the query and push a fresh `{ type: "data" }` frame when the result
5274
5337
  * differs from the last one sent. Subscriptions with no `functionPath`
@@ -5655,4 +5718,4 @@ interface WhereSqlStrategy {
5655
5718
  * `undefined` when the input imposes no constraint (empty `where`).
5656
5719
  */
5657
5720
  declare const compileWhereSql: (where: WhereInput | undefined, strategy: WhereSqlStrategy) => SQL | undefined;
5658
- 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, 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 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 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, 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 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, ShardDO, type ShardDOOptions, type ShardDOState, type ShardRankPageResult, ShardRegistryDO, type SocketAttachment, type SortDirection, 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, assertValidClientId, backfillAggregateIndexes, backfillRankIndexes, buildFtsMatch, buildSecurityAudit, buildSeekWhere, clearCapturedMail, coerceAggregateNumber, compileWhereSql, containsRelationPredicate, createDependencyTracker, createShardCtxDb, createSystemReader, decodeCursor, depKey, encodeAggregateKey, encodeCursor, encodePartitionKey, ensureAuthMetricsTables, ensureFunctionMetricsTables, ensureMailTable, exportShardRows, exportShardTable, facetColumn, foldAggregateTally, ftsTableName, guardWriter, hasTrigger, importShardRows, isRelationPredicate, listTables, matchesRankStaticWhere, matchesStaticWhere, mergeWhere, normalizeCountArgument, normalizeIdStructurally, normalizeOrderKeys, parseExportShardArgs, parseImportShardArgs, planAggregateLookup, rankTableName, reactiveCacheKey, readAggregateValue, readAuthMetrics, readBookmark, readCapturedMail, readCdcChanges, readFunctionMetricBuckets, readFunctionMetricIndexHits, readFunctionMetrics, readFunctionMetricsTotals, readMigrationStatus, readTablePage, recordAuthEvent, recordCapturedMail, recordFunctionMetric, renderSql, resolveRankPartition, resolveRelationPredicates, resolveWith, runDataMigration, runReadonlySql, runRowValidators, runShardMigrations, runTriggers, scoreDocument, selectExportTables, selectIndexForAggregate, selectIndexForCount, selectIndexForGroupBy, selectMatchingIds, serveRelationFanout, softDeleteScope, sortColumnName, stableStringify, stringifySearchText, subscriptionListDeltas, throwingScheduler, tokenizeSearch, trimCdcChanges, validateImportRow };
5721
+ 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, 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 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 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 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, ShardDO, type ShardDOOptions, type ShardDOState, type ShardRankPageResult, ShardRegistryDO, type SocketAttachment, type SortDirection, 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, assertValidClientId, backfillAggregateIndexes, backfillRankIndexes, buildFtsMatch, buildSecurityAudit, buildSeekWhere, clearCapturedMail, coerceAggregateNumber, compileWhereSql, containsRelationPredicate, createDependencyTracker, createShardCtxDb, createSystemReader, decodeCursor, depKey, encodeAggregateKey, encodeCursor, encodePartitionKey, ensureAuthMetricsTables, ensureFunctionMetricsTables, ensureMailTable, exportShardRows, exportShardTable, facetColumn, foldAggregateTally, ftsTableName, guardWriter, hasTrigger, importShardRows, isRelationPredicate, listTables, matchesRankStaticWhere, matchesStaticWhere, mergeWhere, normalizeCountArgument, normalizeIdStructurally, normalizeOrderKeys, parseExportShardArgs, parseImportShardArgs, planAggregateLookup, rankTableName, reactiveCacheKey, readAggregateValue, readAuthMetrics, readBookmark, readCapturedMail, readCdcChanges, readFunctionMetricBuckets, readFunctionMetricIndexHits, readFunctionMetrics, readFunctionMetricsTotals, readMigrationStatus, readTablePage, recordAuthEvent, recordCapturedMail, recordFunctionMetric, renderSql, resolveRankPartition, resolveRelationPredicates, resolveWith, runDataMigration, runReadonlySql, runRowValidators, runShardMigrations, runTriggers, scoreDocument, selectExportTables, selectIndexForAggregate, selectIndexForCount, selectIndexForGroupBy, selectMatchingIds, serveRelationFanout, softDeleteScope, sortColumnName, stableStringify, stringifySearchText, subscriptionListDeltas, throwingScheduler, tokenizeSearch, trimCdcChanges, validateImportRow };
package/dist/index.d.ts CHANGED
@@ -2292,6 +2292,7 @@ declare const ADMIN_FUNCTIONS: {
2292
2292
  readonly getSettings: "__lunora_admin__:getSettings";
2293
2293
  readonly getWorkflowInstanceStatus: "__lunora_admin__:getWorkflowInstanceStatus";
2294
2294
  readonly importShard: "__lunora_admin__:importShard";
2295
+ readonly listQueues: "__lunora_admin__:listQueues";
2295
2296
  readonly listTables: "__lunora_admin__:listTables";
2296
2297
  readonly listWorkflows: "__lunora_admin__:listWorkflows";
2297
2298
  readonly maskPolicies: "__lunora_admin__:maskPolicies";
@@ -2575,11 +2576,13 @@ interface StudioFeaturesResult {
2575
2576
  mail: boolean;
2576
2577
  /** `@lunora/payment` is used (import or `ctx.payments`) or a declared dependency. */
2577
2578
  payments: boolean;
2579
+ /** `@lunora/queue` / `ctx.queues` is used, the app declares queues, or it is a declared dependency. */
2580
+ queues: boolean;
2578
2581
  /** `@lunora/scheduler` / `ctx.scheduler` is used, the app declares crons, or it is a declared dependency. */
2579
2582
  scheduler: boolean;
2580
2583
  /** `@lunora/storage` / `ctx.storage` is used, the schema declares storage columns/rules, or it is a declared dependency. */
2581
2584
  storage: boolean;
2582
- /** The schema declares vector indexes, `@lunora/vectors` / `ctx.vectors` is used, or it is a declared dependency. */
2585
+ /** The schema declares vector indexes, `@lunora/bindings/vectors` / `ctx.vectors` is used, or it is a declared dependency. */
2583
2586
  vectors: boolean;
2584
2587
  /** `@lunora/workflow` / `ctx.workflows` is used, the app declares workflows, or it is a declared dependency. */
2585
2588
  workflows: boolean;
@@ -2605,6 +2608,28 @@ interface WorkflowsResult {
2605
2608
  workflows: WorkflowMetadata[];
2606
2609
  }
2607
2610
  /**
2611
+ * One declared Cloudflare Queue, surfaced by `__lunora_admin__:listQueues` for
2612
+ * the studio's Queues page. Statically discovered by `@lunora/codegen` from
2613
+ * `lunora/queues.ts` (the codegen subclass overrides the base hook); queues are
2614
+ * not Durable Objects and carry no runtime state in the shard, so this is pure
2615
+ * declaration metadata. `binding` is the generated `QUEUE_*` producer binding,
2616
+ * `name` the deployed `queues.producers[].queue`, `exportName` the
2617
+ * `lunora/queues.ts` export (`ctx.queues.<exportName>`), `mode` whether the
2618
+ * queue is consumed by a worker (`push`) or polled externally (`pull`), and
2619
+ * `deadLetterQueue` the optional DLQ a push consumer dead-letters to.
2620
+ */
2621
+ interface QueueMetadata {
2622
+ binding: string;
2623
+ deadLetterQueue?: string;
2624
+ exportName: string;
2625
+ mode: "pull" | "push";
2626
+ name: string;
2627
+ }
2628
+ /** Payload of a `__lunora_admin__:listQueues` call: every declared queue, sorted by export name. */
2629
+ interface QueuesResult {
2630
+ queues: QueueMetadata[];
2631
+ }
2632
+ /**
2608
2633
  * Lifecycle state of a workflow instance, mirrored from `@lunora/workflow`'s
2609
2634
  * `WorkflowInstanceStatus` so `@lunora/do` carries no dependency on the workflow
2610
2635
  * package. Returned by `getWorkflowInstanceStatus` and `createWorkflowInstance`.
@@ -3641,6 +3666,44 @@ declare class SessionDO {
3641
3666
  private handleRevoke;
3642
3667
  }
3643
3668
  /**
3669
+ * Diff the previously-sent list snapshot (`previousJson`, the memo's
3670
+ * `lastJson`) against the new query result and produce per-row
3671
+ * {@link MutationDelta}s the client can merge in place via `applyDelta` —
3672
+ * Convex-parity live-pagination deltas (server half of gap #20).
3673
+ *
3674
+ * Returns `undefined` (caller falls back to a full `{type:"data"}` snapshot)
3675
+ * unless ALL of these hold:
3676
+ *
3677
+ * 1. `previousJson` parses to an array (there IS a previous list to diff against).
3678
+ * 2. `nextResult` is also an array.
3679
+ * 3. Every row in both arrays is a plain object carrying a string `_id`.
3680
+ * 4. Order preservation — rows present in BOTH arrays appear in the same relative order.
3681
+ * 5. Chattiness cap — the number of deltas does not exceed the new array length (a near-total change is cheaper as a snapshot).
3682
+ *
3683
+ * Diff is keyed by `_id`: rows only in prev → `delete`; rows only in next →
3684
+ * `insert`; rows in both whose JSON differs → `update`. Insert/update carry the
3685
+ * full new `row`; delete omits it (matching the wire contract `@lunora/client`
3686
+ * parses). Deltas are ordered deletes-then-inserts/updates so the client never
3687
+ * sees a transient over-length page.
3688
+ *
3689
+ * Per-row serialization is done exactly **once** per refresh (finding #6). Each
3690
+ * row is stringified a single time into a fingerprint reused for both the
3691
+ * `prev !== next` change-detection compare and — when the caller passes the
3692
+ * optional `frames` sink — the pre-serialized delta frame body. The returned
3693
+ * `MutationDelta[]` shape is unchanged; `frames`, when supplied, receives the
3694
+ * exact `JSON.stringify(delta)` string for each returned delta, in the same
3695
+ * order, so the caller can splice it straight into the `{type:"delta"}` frame
3696
+ * without serializing the delta (and the row inside it) a second time.
3697
+ * @returns the per-row deltas to send, or `undefined` when any precondition fails and a full snapshot should be sent instead
3698
+ */
3699
+ declare const subscriptionListDeltas: (previousJson: string, nextResult: unknown, table: string, frames?: string[]) => MutationDelta[] | undefined;
3700
+ /**
3701
+ * Send one WebSocket frame, reporting whether it left the socket. A throw from
3702
+ * `ws.send` (socket closed mid-flush, outbound buffer gone) is the only
3703
+ * delivery-failure signal the runtime exposes; callers use the boolean to decide
3704
+ * whether to advance a subscription's delivered-diff baseline.
3705
+ */
3706
+ /**
3644
3707
  * Optional programmatic log sink, resolved from `createShardDO({ observability })`.
3645
3708
  * Structurally a subset of `@lunora/runtime`'s `ObservabilitySink`, so a user can
3646
3709
  * pass the SAME sink object to `createWorker` (which drives `onRpc`) and
@@ -3893,38 +3956,6 @@ interface RunShardRankPageArgs {
3893
3956
  take?: number;
3894
3957
  }
3895
3958
  /**
3896
- * Diff the previously-sent list snapshot (`previousJson`, the memo's
3897
- * `lastJson`) against the new query result and produce per-row
3898
- * {@link MutationDelta}s the client can merge in place via `applyDelta` —
3899
- * Convex-parity live-pagination deltas (server half of gap #20).
3900
- *
3901
- * Returns `undefined` (caller falls back to a full `{type:"data"}` snapshot)
3902
- * unless ALL of these hold:
3903
- *
3904
- * 1. `previousJson` parses to an array (there IS a previous list to diff against).
3905
- * 2. `nextResult` is also an array.
3906
- * 3. Every row in both arrays is a plain object carrying a string `_id`.
3907
- * 4. Order preservation — rows present in BOTH arrays appear in the same relative order.
3908
- * 5. Chattiness cap — the number of deltas does not exceed the new array length (a near-total change is cheaper as a snapshot).
3909
- *
3910
- * Diff is keyed by `_id`: rows only in prev → `delete`; rows only in next →
3911
- * `insert`; rows in both whose JSON differs → `update`. Insert/update carry the
3912
- * full new `row`; delete omits it (matching the wire contract `@lunora/client`
3913
- * parses). Deltas are ordered deletes-then-inserts/updates so the client never
3914
- * sees a transient over-length page.
3915
- *
3916
- * Per-row serialization is done exactly **once** per refresh (finding #6). Each
3917
- * row is stringified a single time into a fingerprint reused for both the
3918
- * `prev !== next` change-detection compare and — when the caller passes the
3919
- * optional `frames` sink — the pre-serialized delta frame body. The returned
3920
- * `MutationDelta[]` shape is unchanged; `frames`, when supplied, receives the
3921
- * exact `JSON.stringify(delta)` string for each returned delta, in the same
3922
- * order, so the caller can splice it straight into the `{type:"delta"}` frame
3923
- * without serializing the delta (and the row inside it) a second time.
3924
- * @returns the per-row deltas to send, or `undefined` when any precondition fails and a full snapshot should be sent instead
3925
- */
3926
- declare const subscriptionListDeltas: (previousJson: string, nextResult: unknown, table: string, frames?: string[]) => MutationDelta[] | undefined;
3927
- /**
3928
3959
  * Threshold at which a `__root__` DO triggers the size warning. 1 GiB —
3929
3960
  * exactly 10% of the 10 GiB per-DO SQLite ceiling, leaving plenty of runway
3930
3961
  * to plan a `.shardBy()` migration before the wall hits.
@@ -4106,6 +4137,18 @@ declare abstract class ShardDO {
4106
4137
  */
4107
4138
  private pendingChangedTables;
4108
4139
  /**
4140
+ * Coalesced set of tables awaiting a subscription-refresh pass, merged
4141
+ * across every {@link ShardDO.flushChangedTables} call that lands while a
4142
+ * pass is already draining. The single drain loop
4143
+ * ({@link ShardDO.drainSubscriptionRefreshes}) owns this set; a burst of N
4144
+ * writes to the same table therefore collapses into one (or two) refresh
4145
+ * passes instead of N, so each affected subscription's handler re-runs once
4146
+ * per burst rather than once per write. `undefined` when nothing is pending.
4147
+ */
4148
+ private pendingRefreshTables;
4149
+ /** True while {@link ShardDO.drainSubscriptionRefreshes} is running; the single-waiter gate that coalesces concurrent flushes. */
4150
+ private refreshInFlight;
4151
+ /**
4109
4152
  * Last pushed result per `(socket, subId)`, keyed by socket. Lets
4110
4153
  * `refreshSubscriptions` skip re-running queries whose tables were
4111
4154
  * untouched and suppress pushes when the re-run result is unchanged. Held
@@ -4477,6 +4520,15 @@ declare abstract class ShardDO {
4477
4520
  */
4478
4521
  protected studioFeatures(): StudioFeaturesResult;
4479
4522
  /**
4523
+ * The Cloudflare Queues declared by this app, surfaced via
4524
+ * `__lunora_admin__:listQueues` for the studio's Queues page. Queues are NOT
4525
+ * Durable Objects and hold no shard state, so this is pure declaration
4526
+ * metadata statically discovered by `@lunora/codegen` from `lunora/queues.ts`
4527
+ * and emitted into the generated subclass, which overrides this. The base
4528
+ * class can't see the user's project, so it reports none.
4529
+ */
4530
+ protected queuesMetadata(): QueuesResult;
4531
+ /**
4480
4532
  * The Cloudflare Workflows declared by this app, surfaced via
4481
4533
  * `__lunora_admin__:listWorkflows` for the studio's Workflows page. Workflows
4482
4534
  * are NOT Durable Objects and hold no shard state, so this is pure
@@ -5269,6 +5321,17 @@ declare abstract class ShardDO {
5269
5321
  */
5270
5322
  private flushChangedTables;
5271
5323
  /**
5324
+ * Drain {@link ShardDO.pendingRefreshTables} one coalesced batch at a time
5325
+ * until it is empty, then release the {@link ShardDO.refreshInFlight} gate.
5326
+ * Tables merged by a `flushChangedTables` that lands mid-pass are picked up
5327
+ * by the next loop iteration, so every committed write is observed by a
5328
+ * refresh that runs after it — bursts simply share a pass. The post-write
5329
+ * high-watermark and live-socket set are re-read inside each
5330
+ * `refreshSubscriptions` call, so a later batch always reflects the latest
5331
+ * committed state.
5332
+ */
5333
+ private drainSubscriptionRefreshes;
5334
+ /**
5272
5335
  * For every live subscription whose query reads one of `changed`, re-run
5273
5336
  * the query and push a fresh `{ type: "data" }` frame when the result
5274
5337
  * differs from the last one sent. Subscriptions with no `functionPath`
@@ -5655,4 +5718,4 @@ interface WhereSqlStrategy {
5655
5718
  * `undefined` when the input imposes no constraint (empty `where`).
5656
5719
  */
5657
5720
  declare const compileWhereSql: (where: WhereInput | undefined, strategy: WhereSqlStrategy) => SQL | undefined;
5658
- 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, 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 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 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, 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 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, ShardDO, type ShardDOOptions, type ShardDOState, type ShardRankPageResult, ShardRegistryDO, type SocketAttachment, type SortDirection, 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, assertValidClientId, backfillAggregateIndexes, backfillRankIndexes, buildFtsMatch, buildSecurityAudit, buildSeekWhere, clearCapturedMail, coerceAggregateNumber, compileWhereSql, containsRelationPredicate, createDependencyTracker, createShardCtxDb, createSystemReader, decodeCursor, depKey, encodeAggregateKey, encodeCursor, encodePartitionKey, ensureAuthMetricsTables, ensureFunctionMetricsTables, ensureMailTable, exportShardRows, exportShardTable, facetColumn, foldAggregateTally, ftsTableName, guardWriter, hasTrigger, importShardRows, isRelationPredicate, listTables, matchesRankStaticWhere, matchesStaticWhere, mergeWhere, normalizeCountArgument, normalizeIdStructurally, normalizeOrderKeys, parseExportShardArgs, parseImportShardArgs, planAggregateLookup, rankTableName, reactiveCacheKey, readAggregateValue, readAuthMetrics, readBookmark, readCapturedMail, readCdcChanges, readFunctionMetricBuckets, readFunctionMetricIndexHits, readFunctionMetrics, readFunctionMetricsTotals, readMigrationStatus, readTablePage, recordAuthEvent, recordCapturedMail, recordFunctionMetric, renderSql, resolveRankPartition, resolveRelationPredicates, resolveWith, runDataMigration, runReadonlySql, runRowValidators, runShardMigrations, runTriggers, scoreDocument, selectExportTables, selectIndexForAggregate, selectIndexForCount, selectIndexForGroupBy, selectMatchingIds, serveRelationFanout, softDeleteScope, sortColumnName, stableStringify, stringifySearchText, subscriptionListDeltas, throwingScheduler, tokenizeSearch, trimCdcChanges, validateImportRow };
5721
+ 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, 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 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 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 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, ShardDO, type ShardDOOptions, type ShardDOState, type ShardRankPageResult, ShardRegistryDO, type SocketAttachment, type SortDirection, 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, assertValidClientId, backfillAggregateIndexes, backfillRankIndexes, buildFtsMatch, buildSecurityAudit, buildSeekWhere, clearCapturedMail, coerceAggregateNumber, compileWhereSql, containsRelationPredicate, createDependencyTracker, createShardCtxDb, createSystemReader, decodeCursor, depKey, encodeAggregateKey, encodeCursor, encodePartitionKey, ensureAuthMetricsTables, ensureFunctionMetricsTables, ensureMailTable, exportShardRows, exportShardTable, facetColumn, foldAggregateTally, ftsTableName, guardWriter, hasTrigger, importShardRows, isRelationPredicate, listTables, matchesRankStaticWhere, matchesStaticWhere, mergeWhere, normalizeCountArgument, normalizeIdStructurally, normalizeOrderKeys, parseExportShardArgs, parseImportShardArgs, planAggregateLookup, rankTableName, reactiveCacheKey, readAggregateValue, readAuthMetrics, readBookmark, readCapturedMail, readCdcChanges, readFunctionMetricBuckets, readFunctionMetricIndexHits, readFunctionMetrics, readFunctionMetricsTotals, readMigrationStatus, readTablePage, recordAuthEvent, recordCapturedMail, recordFunctionMetric, renderSql, resolveRankPartition, resolveRelationPredicates, resolveWith, runDataMigration, runReadonlySql, runRowValidators, runShardMigrations, runTriggers, scoreDocument, selectExportTables, selectIndexForAggregate, selectIndexForCount, selectIndexForGroupBy, selectMatchingIds, serveRelationFanout, softDeleteScope, sortColumnName, stableStringify, stringifySearchText, subscriptionListDeltas, throwingScheduler, tokenizeSearch, trimCdcChanges, validateImportRow };
package/dist/index.mjs CHANGED
@@ -8,7 +8,7 @@ export { DATA_MIGRATION_STATE_TABLE, readMigrationStatus, runDataMigration } fro
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 { 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';
11
- export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, RELATION_FUNCTION_PREFIX, facetColumn, listTables, readTablePage, selectMatchingIds } from './packem_shared/ADMIN_FUNCTIONS-Dzdqq5J2.mjs';
11
+ export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, RELATION_FUNCTION_PREFIX, facetColumn, listTables, readTablePage, selectMatchingIds } from './packem_shared/ADMIN_FUNCTIONS-CHcC8fKV.mjs';
12
12
  export { LogBuffer } from './packem_shared/LogBuffer-B_Ezju_N.mjs';
13
13
  export { MAIL_RETENTION, MAIL_TABLE, clearCapturedMail, ensureMailTable, readCapturedMail, recordCapturedMail } from './packem_shared/MAIL_RETENTION-CPpgl-dX.mjs';
14
14
  export { default as NotFoundError } from './packem_shared/NotFoundError-CMuMZt81.mjs';
@@ -16,14 +16,14 @@ export { armRestore, readBookmark } from './packem_shared/armRestore-BJk53Ro8.mj
16
16
  export { applySelect, buildSeekWhere, decodeCursor, encodeCursor, normalizeOrderKeys, softDeleteScope } from './packem_shared/applySelect-BvZdFUBT.mjs';
17
17
  export { R as RANK_TIEBREAK, e as encodePartitionKey, m as matchesRankStaticWhere, r as rankTableName, a as resolveRankPartition, s as sortColumnName } from './packem_shared/rank-CrkEIpF4.mjs';
18
18
  export { ReactiveCache, reactiveCacheKey, stableStringify } from './packem_shared/ReactiveCache-ByVzgH3d.mjs';
19
- export { serveRelationFanout } from './packem_shared/serveRelationFanout-CFBKWJ8Q.mjs';
19
+ export { serveRelationFanout } from './packem_shared/serveRelationFanout-oxaM6_WL.mjs';
20
20
  export { DEFAULT_MAX_RELATION_KEYS, assertFlatPredicate, containsRelationPredicate, isRelationPredicate, resolveRelationPredicates } from './packem_shared/DEFAULT_MAX_RELATION_KEYS-Dou2PWdO.mjs';
21
21
  export { applyOnDelete, resolveWith, runRowValidators } from './packem_shared/applyOnDelete-sA7o1CqD.mjs';
22
22
  export { RLS_UNWRAP_SYMBOL, RlsRequiredError, guardWriter } from './packem_shared/RLS_UNWRAP_SYMBOL-EtGQdC9d.mjs';
23
23
  export { buildFtsMatch, ftsTableName, scoreDocument, stringifySearchText, tokenizeSearch } from './packem_shared/buildFtsMatch-BLEMawrp.mjs';
24
24
  export { M as MIN_ADMIN_TOKEN_LENGTH, a as MIN_AUTH_SECRET_LENGTH, b as buildSecurityAudit } from './packem_shared/security-audit-CucgBice.mjs';
25
25
  export { SESSION_DO_TTL_DEFAULT, SessionDO } from './packem_shared/SESSION_DO_TTL_DEFAULT-ilPZsVwu.mjs';
26
- export { ROOT_DO_SIZE_WARN_BYTES, ROOT_SHARD_NAME, ShardDO, subscriptionListDeltas } from './packem_shared/ROOT_DO_SIZE_WARN_BYTES-3lZ2yigq.mjs';
26
+ export { ROOT_DO_SIZE_WARN_BYTES, ROOT_SHARD_NAME, ShardDO } from './packem_shared/ROOT_DO_SIZE_WARN_BYTES-2DxWrdla.mjs';
27
27
  export { SHARD_REGISTRY_DO_NAME, ShardRegistryDO } from './packem_shared/SHARD_REGISTRY_DO_NAME-BsAbi5Mn.mjs';
28
28
  export { MAX_SQL_ROWS, assertReadonly, runReadonlySql } from './packem_shared/MAX_SQL_ROWS-dDcFE1YZ.mjs';
29
29
  export { createSystemReader } from './packem_shared/createSystemReader-8CzSZP9V.mjs';
@@ -33,3 +33,4 @@ export { compileWhereSql } from './packem_shared/compileWhereSql-CXrhFA3G.mjs';
33
33
  export { CDC_LOG_TABLE, applyCdcChanges, readCdcChanges, trimCdcChanges } from './packem_shared/CDC_LOG_TABLE-Ctdmxmrv.mjs';
34
34
  export { backfillAggregateIndexes, backfillRankIndexes } from './packem_shared/backfillAggregateIndexes-BbVPvciS.mjs';
35
35
  export { runShardMigrations } from './packem_shared/runShardMigrations-PabobOjF.mjs';
36
+ export { subscriptionListDeltas } from './packem_shared/subscriptionListDeltas-ce84gpwL.mjs';
@@ -27,6 +27,7 @@ const ADMIN_FUNCTIONS = {
27
27
  // eslint-disable-next-line no-secrets/no-secrets -- reserved admin RPC path constant, not a credential
28
28
  getWorkflowInstanceStatus: "__lunora_admin__:getWorkflowInstanceStatus",
29
29
  importShard: "__lunora_admin__:importShard",
30
+ listQueues: "__lunora_admin__:listQueues",
30
31
  listTables: "__lunora_admin__:listTables",
31
32
  listWorkflows: "__lunora_admin__:listWorkflows",
32
33
  maskPolicies: "__lunora_admin__:maskPolicies",
@@ -4,7 +4,7 @@ import { recordAuthEvent, readAuthMetrics } from './AUTH_METRICS_BUCKETS_TABLE-C
4
4
  import { DATA_MIGRATION_STATE_TABLE, readMigrationStatus } from './DATA_MIGRATION_STATE_TABLE-PTtTiQ7U.mjs';
5
5
  import { SCAN_DEP, createDependencyTracker, tableFromDepKey } from './SCAN_DEP-DLJF8dsj.mjs';
6
6
  import { readFunctionMetricsTotals, readFunctionMetricIndexHits, recordFunctionMetric, mergeScanAttribution, readFunctionMetrics, readFunctionMetricBuckets } from './FUNCTION_METRICS_BUCKETS_TABLE-UDNVD7FS.mjs';
7
- import { ADMIN_FUNCTION_PREFIX, RELATION_FUNCTION_PREFIX, selectMatchingIds, ADMIN_FUNCTIONS, findStorageReferences, listTables, summarizeSubscriptions, readTablePage, facetColumn, MAX_PAGE_SIZE } from './ADMIN_FUNCTIONS-Dzdqq5J2.mjs';
7
+ import { ADMIN_FUNCTION_PREFIX, RELATION_FUNCTION_PREFIX, selectMatchingIds, ADMIN_FUNCTIONS, findStorageReferences, listTables, summarizeSubscriptions, readTablePage, facetColumn, MAX_PAGE_SIZE } from './ADMIN_FUNCTIONS-CHcC8fKV.mjs';
8
8
  import { LogBuffer } from './LogBuffer-B_Ezju_N.mjs';
9
9
  import { recordCapturedMail, clearCapturedMail, readCapturedMail, MAIL_TABLE } from './MAIL_RETENTION-CPpgl-dX.mjs';
10
10
  import { readBookmark, armRestore } from './armRestore-BJk53Ro8.mjs';
@@ -12,6 +12,7 @@ import { ReactiveCache, reactiveCacheKey } from './ReactiveCache-ByVzgH3d.mjs';
12
12
  import { redact, standardRules } from '@visulima/redact';
13
13
  import { i as isDevEnvironment, c as buildSettings, b as buildSecurityAudit } from './security-audit-CucgBice.mjs';
14
14
  import { runReadonlySql } from './MAX_SQL_ROWS-dDcFE1YZ.mjs';
15
+ import { trySendFrame, subscriptionListDeltas, sendDeltaFrames } from './subscriptionListDeltas-ce84gpwL.mjs';
15
16
  import { ConflictError } from './ConflictError-C0STs6bU.mjs';
16
17
  import { CDC_LOG_TABLE, readCdcChanges, readCdcCursor, readCdcEpoch, minCdcSeq, bumpCdcEpoch } from './CDC_LOG_TABLE-Ctdmxmrv.mjs';
17
18
  import { r as readIdempotent, w as writeIdempotent, t as trimIdempotent } from './ctx-db-idempotency-DkC9rP91.mjs';
@@ -413,97 +414,7 @@ const findDanglingReferences = (sql, storageColumns, liveKeys) => {
413
414
 
414
415
  const WS_KEEPALIVE_PING = "lunora-ping";
415
416
  const WS_KEEPALIVE_PONG = "lunora-pong";
416
- const ROW_ID_FIELD = "_id";
417
- const DELTA_FALLBACK_TABLE = "__lunora__";
418
- const readRowId = (row) => {
419
- if (typeof row !== "object" || row === null || Array.isArray(row)) {
420
- return void 0;
421
- }
422
- const id = row[ROW_ID_FIELD];
423
- return typeof id === "string" ? id : void 0;
424
- };
425
- const indexRowsById = (rows) => {
426
- const byId = /* @__PURE__ */ new Map();
427
- const order = [];
428
- for (const row of rows) {
429
- const id = readRowId(row);
430
- if (id === void 0 || byId.has(id)) {
431
- return void 0;
432
- }
433
- byId.set(id, row);
434
- order.push(id);
435
- }
436
- return { byId, order };
437
- };
438
- const survivorsKeepOrder = (previous, next) => {
439
- const survivingPrevious = previous.order.filter((id) => next.byId.has(id));
440
- const survivingNext = next.order.filter((id) => previous.byId.has(id));
441
- if (survivingPrevious.length !== survivingNext.length) {
442
- return false;
443
- }
444
- return survivingPrevious.every((id, index) => survivingNext[index] === id);
445
- };
446
- const collectDeleteDeltas = (previous, next, deltaTable, tableJson) => {
447
- const out = [];
448
- for (const id of previous.order) {
449
- if (!next.byId.has(id)) {
450
- out.push({
451
- delta: { key: id, op: "delete", table: deltaTable },
452
- frame: `{"key":${JSON.stringify(id)},"op":"delete","table":${tableJson}}`
453
- });
454
- }
455
- }
456
- return out;
457
- };
458
- const collectUpsertDeltas = (previous, next, deltaTable, tableJson) => {
459
- const out = [];
460
- for (const id of next.order) {
461
- const nextRow = next.byId.get(id);
462
- const previousRow = previous.byId.get(id);
463
- const nextFingerprint = JSON.stringify(nextRow);
464
- const previousFingerprint = previousRow === void 0 ? void 0 : JSON.stringify(previousRow);
465
- if (previousFingerprint === nextFingerprint) {
466
- continue;
467
- }
468
- const op = previousFingerprint === void 0 ? "insert" : "update";
469
- out.push({
470
- delta: { key: id, op, row: nextRow, table: deltaTable },
471
- frame: `{"key":${JSON.stringify(id)},"op":"${op}","row":${nextFingerprint},"table":${tableJson}}`
472
- });
473
- }
474
- return out;
475
- };
476
- const subscriptionListDeltas = (previousJson, nextResult, table, frames) => {
477
- let parsed;
478
- try {
479
- parsed = JSON.parse(previousJson);
480
- } catch {
481
- return void 0;
482
- }
483
- if (!Array.isArray(parsed) || !Array.isArray(nextResult)) {
484
- return void 0;
485
- }
486
- const previous = indexRowsById(parsed);
487
- const next = indexRowsById(nextResult);
488
- if (previous === void 0 || next === void 0) {
489
- return void 0;
490
- }
491
- if (!survivorsKeepOrder(previous, next)) {
492
- return void 0;
493
- }
494
- const deltaTable = table === "" ? DELTA_FALLBACK_TABLE : table;
495
- const tableJson = JSON.stringify(deltaTable);
496
- const framed = [...collectDeleteDeltas(previous, next, deltaTable, tableJson), ...collectUpsertDeltas(previous, next, deltaTable, tableJson)];
497
- if (framed.length > next.order.length) {
498
- return void 0;
499
- }
500
- if (frames !== void 0) {
501
- for (const { frame } of framed) {
502
- frames.push(frame);
503
- }
504
- }
505
- return framed.map(({ delta }) => delta);
506
- };
417
+ const UNDELIVERED_BASELINE = "<undelivered>";
507
418
  const ROOT_DO_SIZE_WARN_BYTES = 1073741824;
508
419
  const CDC_RESUME_SCAN_LIMIT = 1e4;
509
420
  const IDEMPOTENCY_RETENTION_MS = 864e5;
@@ -1133,6 +1044,18 @@ class ShardDO {
1133
1044
  * the common read-only path allocates nothing.
1134
1045
  */
1135
1046
  pendingChangedTables = void 0;
1047
+ /**
1048
+ * Coalesced set of tables awaiting a subscription-refresh pass, merged
1049
+ * across every {@link ShardDO.flushChangedTables} call that lands while a
1050
+ * pass is already draining. The single drain loop
1051
+ * ({@link ShardDO.drainSubscriptionRefreshes}) owns this set; a burst of N
1052
+ * writes to the same table therefore collapses into one (or two) refresh
1053
+ * passes instead of N, so each affected subscription's handler re-runs once
1054
+ * per burst rather than once per write. `undefined` when nothing is pending.
1055
+ */
1056
+ pendingRefreshTables = void 0;
1057
+ /** True while {@link ShardDO.drainSubscriptionRefreshes} is running; the single-waiter gate that coalesces concurrent flushes. */
1058
+ refreshInFlight = false;
1136
1059
  /**
1137
1060
  * Last pushed result per `(socket, subId)`, keyed by socket. Lets
1138
1061
  * `refreshSubscriptions` skip re-running queries whose tables were
@@ -1853,7 +1776,19 @@ class ShardDO {
1853
1776
  */
1854
1777
  // eslint-disable-next-line class-methods-use-this -- base-class override hook: the codegen subclass overrides this with the statically-discovered feature flags
1855
1778
  studioFeatures() {
1856
- return { mail: false, payments: false, scheduler: false, storage: false, vectors: false, workflows: false };
1779
+ return { mail: false, payments: false, queues: false, scheduler: false, storage: false, vectors: false, workflows: false };
1780
+ }
1781
+ /**
1782
+ * The Cloudflare Queues declared by this app, surfaced via
1783
+ * `__lunora_admin__:listQueues` for the studio's Queues page. Queues are NOT
1784
+ * Durable Objects and hold no shard state, so this is pure declaration
1785
+ * metadata statically discovered by `@lunora/codegen` from `lunora/queues.ts`
1786
+ * and emitted into the generated subclass, which overrides this. The base
1787
+ * class can't see the user's project, so it reports none.
1788
+ */
1789
+ // eslint-disable-next-line class-methods-use-this -- base-class override hook: the codegen subclass overrides this with the statically-discovered queue metadata
1790
+ queuesMetadata() {
1791
+ return { queues: [] };
1857
1792
  }
1858
1793
  /**
1859
1794
  * The Cloudflare Workflows declared by this app, surfaced via
@@ -2237,10 +2172,7 @@ class ShardDO {
2237
2172
  if (!this.matchesSubscription(query, delta)) {
2238
2173
  continue;
2239
2174
  }
2240
- try {
2241
- ws.send(`{"type":"delta","id":${JSON.stringify(subId)},"delta":${deltaJson}}`);
2242
- } catch {
2243
- }
2175
+ trySendFrame(ws, `{"type":"delta","id":${JSON.stringify(subId)},"delta":${deltaJson}}`);
2244
2176
  }
2245
2177
  }
2246
2178
  }
@@ -3295,6 +3227,9 @@ class ShardDO {
3295
3227
  if (functionPath === ADMIN_FUNCTIONS.listWorkflows) {
3296
3228
  return this.workflowsMetadata();
3297
3229
  }
3230
+ if (functionPath === ADMIN_FUNCTIONS.listQueues) {
3231
+ return this.queuesMetadata();
3232
+ }
3298
3233
  return void 0;
3299
3234
  }
3300
3235
  /**
@@ -3552,11 +3487,47 @@ class ShardDO {
3552
3487
  if (!changed || changed.size === 0) {
3553
3488
  return;
3554
3489
  }
3490
+ if (this.pendingRefreshTables) {
3491
+ for (const table of changed) {
3492
+ this.pendingRefreshTables.add(table);
3493
+ }
3494
+ } else {
3495
+ this.pendingRefreshTables = changed;
3496
+ }
3497
+ if (this.refreshInFlight) {
3498
+ return;
3499
+ }
3555
3500
  if (typeof this.state.waitUntil === "function") {
3556
- this.state.waitUntil(this.refreshSubscriptions(changed));
3501
+ this.state.waitUntil(this.drainSubscriptionRefreshes());
3502
+ return;
3503
+ }
3504
+ await this.drainSubscriptionRefreshes();
3505
+ }
3506
+ /**
3507
+ * Drain {@link ShardDO.pendingRefreshTables} one coalesced batch at a time
3508
+ * until it is empty, then release the {@link ShardDO.refreshInFlight} gate.
3509
+ * Tables merged by a `flushChangedTables` that lands mid-pass are picked up
3510
+ * by the next loop iteration, so every committed write is observed by a
3511
+ * refresh that runs after it — bursts simply share a pass. The post-write
3512
+ * high-watermark and live-socket set are re-read inside each
3513
+ * `refreshSubscriptions` call, so a later batch always reflects the latest
3514
+ * committed state.
3515
+ */
3516
+ async drainSubscriptionRefreshes() {
3517
+ if (this.refreshInFlight) {
3557
3518
  return;
3558
3519
  }
3559
- await this.refreshSubscriptions(changed);
3520
+ this.refreshInFlight = true;
3521
+ try {
3522
+ let batch = this.pendingRefreshTables;
3523
+ while (batch && batch.size > 0) {
3524
+ this.pendingRefreshTables = void 0;
3525
+ await this.refreshSubscriptions(batch);
3526
+ batch = this.pendingRefreshTables;
3527
+ }
3528
+ } finally {
3529
+ this.refreshInFlight = false;
3530
+ }
3560
3531
  }
3561
3532
  /**
3562
3533
  * For every live subscription whose query reads one of `changed`, re-run
@@ -3748,21 +3719,8 @@ class ShardDO {
3748
3719
  }
3749
3720
  const deltaFrames = [];
3750
3721
  const deltas = existing === void 0 ? void 0 : subscriptionListDeltas(existing.lastJson, outcome.result, outcome.tables.values().next().value ?? "", deltaFrames);
3751
- memos.set(subId, { lastJson: json, tables: outcome.tables });
3752
- if (deltas !== void 0) {
3753
- const idJson = JSON.stringify(subId);
3754
- for (const deltaBody of deltaFrames) {
3755
- try {
3756
- ws.send(`{"type":"delta","id":${idJson},"delta":${deltaBody}${cursorSuffix}}`);
3757
- } catch {
3758
- }
3759
- }
3760
- return;
3761
- }
3762
- try {
3763
- ws.send(`{"type":"data","id":${JSON.stringify(subId)},"data":${json}${cursorSuffix}}`);
3764
- } catch {
3765
- }
3722
+ const delivered = deltas === void 0 ? trySendFrame(ws, `{"type":"data","id":${JSON.stringify(subId)},"data":${json}${cursorSuffix}}`) : sendDeltaFrames(ws, subId, deltaFrames, cursorSuffix);
3723
+ memos.set(subId, { lastJson: delivered ? json : existing?.lastJson ?? UNDELIVERED_BASELINE, tables: outcome.tables });
3766
3724
  }
3767
3725
  /**
3768
3726
  * Gate the upgrade request against two complementary controls:
@@ -3984,10 +3942,7 @@ class ShardDO {
3984
3942
  if (ws === sender || this.readAttachment(ws).whispers?.includes(topic) !== true) {
3985
3943
  continue;
3986
3944
  }
3987
- try {
3988
- ws.send(frame);
3989
- } catch {
3990
- }
3945
+ trySendFrame(ws, frame);
3991
3946
  }
3992
3947
  }
3993
3948
  // eslint-disable-next-line class-methods-use-this -- cohesive DO instance method grouped with the hibernation/attachment helpers; reads only the socket
@@ -1,4 +1,4 @@
1
- import { RELATION_FUNCTION_PREFIX } from './ADMIN_FUNCTIONS-Dzdqq5J2.mjs';
1
+ import { RELATION_FUNCTION_PREFIX } from './ADMIN_FUNCTIONS-CHcC8fKV.mjs';
2
2
 
3
3
  const serveRelationFanout = async (schema, database, functionPath, args) => {
4
4
  const table = typeof args["table"] === "string" ? args["table"] : "";
@@ -0,0 +1,111 @@
1
+ const ROW_ID_FIELD = "_id";
2
+ const DELTA_FALLBACK_TABLE = "__lunora__";
3
+ const readRowId = (row) => {
4
+ if (typeof row !== "object" || row === null || Array.isArray(row)) {
5
+ return void 0;
6
+ }
7
+ const id = row[ROW_ID_FIELD];
8
+ return typeof id === "string" ? id : void 0;
9
+ };
10
+ const indexRowsById = (rows) => {
11
+ const byId = /* @__PURE__ */ new Map();
12
+ const order = [];
13
+ for (const row of rows) {
14
+ const id = readRowId(row);
15
+ if (id === void 0 || byId.has(id)) {
16
+ return void 0;
17
+ }
18
+ byId.set(id, row);
19
+ order.push(id);
20
+ }
21
+ return { byId, order };
22
+ };
23
+ const survivorsKeepOrder = (previous, next) => {
24
+ const survivingPrevious = previous.order.filter((id) => next.byId.has(id));
25
+ const survivingNext = next.order.filter((id) => previous.byId.has(id));
26
+ if (survivingPrevious.length !== survivingNext.length) {
27
+ return false;
28
+ }
29
+ return survivingPrevious.every((id, index) => survivingNext[index] === id);
30
+ };
31
+ const collectDeleteDeltas = (previous, next, deltaTable, tableJson) => {
32
+ const out = [];
33
+ for (const id of previous.order) {
34
+ if (!next.byId.has(id)) {
35
+ out.push({
36
+ delta: { key: id, op: "delete", table: deltaTable },
37
+ frame: `{"key":${JSON.stringify(id)},"op":"delete","table":${tableJson}}`
38
+ });
39
+ }
40
+ }
41
+ return out;
42
+ };
43
+ const collectUpsertDeltas = (previous, next, deltaTable, tableJson) => {
44
+ const out = [];
45
+ for (const id of next.order) {
46
+ const nextRow = next.byId.get(id);
47
+ const previousRow = previous.byId.get(id);
48
+ const nextFingerprint = JSON.stringify(nextRow);
49
+ const previousFingerprint = previousRow === void 0 ? void 0 : JSON.stringify(previousRow);
50
+ if (previousFingerprint === nextFingerprint) {
51
+ continue;
52
+ }
53
+ const op = previousFingerprint === void 0 ? "insert" : "update";
54
+ out.push({
55
+ delta: { key: id, op, row: nextRow, table: deltaTable },
56
+ frame: `{"key":${JSON.stringify(id)},"op":"${op}","row":${nextFingerprint},"table":${tableJson}}`
57
+ });
58
+ }
59
+ return out;
60
+ };
61
+ const subscriptionListDeltas = (previousJson, nextResult, table, frames) => {
62
+ let parsed;
63
+ try {
64
+ parsed = JSON.parse(previousJson);
65
+ } catch {
66
+ return void 0;
67
+ }
68
+ if (!Array.isArray(parsed) || !Array.isArray(nextResult)) {
69
+ return void 0;
70
+ }
71
+ const previous = indexRowsById(parsed);
72
+ const next = indexRowsById(nextResult);
73
+ if (previous === void 0 || next === void 0) {
74
+ return void 0;
75
+ }
76
+ if (!survivorsKeepOrder(previous, next)) {
77
+ return void 0;
78
+ }
79
+ const deltaTable = table === "" ? DELTA_FALLBACK_TABLE : table;
80
+ const tableJson = JSON.stringify(deltaTable);
81
+ const framed = [...collectDeleteDeltas(previous, next, deltaTable, tableJson), ...collectUpsertDeltas(previous, next, deltaTable, tableJson)];
82
+ if (framed.length > next.order.length) {
83
+ return void 0;
84
+ }
85
+ if (frames !== void 0) {
86
+ for (const { frame } of framed) {
87
+ frames.push(frame);
88
+ }
89
+ }
90
+ return framed.map(({ delta }) => delta);
91
+ };
92
+ const trySendFrame = (ws, frame) => {
93
+ try {
94
+ ws.send(frame);
95
+ return true;
96
+ } catch {
97
+ return false;
98
+ }
99
+ };
100
+ const sendDeltaFrames = (ws, subId, deltaFrames, cursorSuffix) => {
101
+ const idJson = JSON.stringify(subId);
102
+ let delivered = true;
103
+ for (const deltaBody of deltaFrames) {
104
+ if (!trySendFrame(ws, `{"type":"delta","id":${idJson},"delta":${deltaBody}${cursorSuffix}}`)) {
105
+ delivered = false;
106
+ }
107
+ }
108
+ return delivered;
109
+ };
110
+
111
+ export { sendDeltaFrames, subscriptionListDeltas, trySendFrame };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/do",
3
- "version": "1.0.0-alpha.5",
3
+ "version": "1.0.0-alpha.7",
4
4
  "description": "Lunora Durable Objects: ShardDO (SQLite, OCC, hibernated WebSocket subscriptions) and SessionDO",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -25,7 +25,7 @@
25
25
  "directory": "packages/do"
26
26
  },
27
27
  "files": [
28
- "dist",
28
+ "./dist",
29
29
  "README.md",
30
30
  "LICENSE.md",
31
31
  "__assets__"