@lunora/do 1.0.0-alpha.37 → 1.0.0-alpha.38

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
@@ -1312,6 +1312,13 @@ interface SchemaLike {
1312
1312
  }
1313
1313
  interface TableDefinitionLike {
1314
1314
  readonly aggregateIndexes?: ReadonlyArray<AggregateIndexDefinitionLike>;
1315
+ /**
1316
+ * Mirror of `@lunora/server`'s `TableDefinition.geoIndexes` (set by
1317
+ * `.geoIndex()`). Each declares a geohash companion over a `v.geoPoint()`
1318
+ * column so `withGeoIndex(name, q => q.near(...) | q.within(...))` resolves
1319
+ * proximity / bounding-box reads. Empty/absent ⇒ the table has no geo index.
1320
+ */
1321
+ readonly geoIndexes?: ReadonlyArray<GeoIndexDefinitionLike>;
1315
1322
  readonly indexes: ReadonlyArray<IndexDefinitionLike>;
1316
1323
  /**
1317
1324
  * `true` when `.public()` opted this table OUT of secure-by-default RLS
@@ -1339,6 +1346,15 @@ interface TableDefinitionLike {
1339
1346
  field: string;
1340
1347
  };
1341
1348
  readonly triggerMap?: Record<string, TriggerDefinitionLike>;
1349
+ /**
1350
+ * Mirror of `@lunora/server`'s `TableDefinition.ttlPolicy` (set by `.ttl()`).
1351
+ * Drives the DO alarm-driven expiry sweep — see `ttl-sweep.ts`. Absent ⇒ rows
1352
+ * never auto-expire.
1353
+ */
1354
+ readonly ttlPolicy?: {
1355
+ after?: number;
1356
+ field: string;
1357
+ };
1342
1358
  }
1343
1359
  interface IndexDefinitionLike {
1344
1360
  readonly fields: ReadonlyArray<string>;
@@ -1350,6 +1366,12 @@ interface SearchIndexDefinitionLike {
1350
1366
  readonly filterFields?: ReadonlyArray<string>;
1351
1367
  readonly name: string;
1352
1368
  }
1369
+ /** Mirror of `@lunora/server`'s `GeoIndexDefinition` — a geohash companion over a `v.geoPoint()` column. */
1370
+ interface GeoIndexDefinitionLike {
1371
+ readonly field: string;
1372
+ readonly name: string;
1373
+ readonly precision?: number;
1374
+ }
1353
1375
  /**
1354
1376
  * Column constraints/defaults the write layer honors, mirrored structurally
1355
1377
  * from `@lunora/values`' `ColumnMeta` (kept local so this package doesn't take
@@ -1423,7 +1445,7 @@ type ReadHook = (table: string, idOrScan?: string) => void;
1423
1445
  * No-op by default; called at most once per read (not per row), so it adds no
1424
1446
  * meaningful hot-path cost.
1425
1447
  */
1426
- type IndexUseHook = (table: string, indexName: string, kind: "index" | "rank" | "search") => void;
1448
+ type IndexUseHook = (table: string, indexName: string, kind: "geo" | "index" | "rank" | "search") => void;
1427
1449
  /** Pluggable wall clock — defaults to `Date.now`. */
1428
1450
  type Clock = () => number;
1429
1451
  /** Pluggable ID minter — defaults to `crypto.randomUUID()`. */
@@ -1566,6 +1588,22 @@ interface SearchFilterBuilderLike {
1566
1588
  eq: (field: string, value: unknown) => SearchFilterBuilderLike;
1567
1589
  search: (field: string, query: string) => SearchFilterBuilderLike;
1568
1590
  }
1591
+ interface GeoFilterBuilderLike {
1592
+ near: (point: {
1593
+ lat: number;
1594
+ lng: number;
1595
+ }, radiusMeters: number) => GeoFilterBuilderLike;
1596
+ within: (box: {
1597
+ ne: {
1598
+ lat: number;
1599
+ lng: number;
1600
+ };
1601
+ sw: {
1602
+ lat: number;
1603
+ lng: number;
1604
+ };
1605
+ }) => GeoFilterBuilderLike;
1606
+ }
1569
1607
  /** Options accepted by {@link TableReaderLike.paginate} — Convex-compatible. */
1570
1608
  interface PaginationOptions {
1571
1609
  /** Opaque cursor from a prior page's `continueCursor`; `null`/omitted starts at the first page. */
@@ -1603,6 +1641,7 @@ interface TableReaderLike {
1603
1641
  * more than one matches. Mirrors Convex's `.unique()`.
1604
1642
  */
1605
1643
  unique: () => Promise<Record<string, unknown> | null>;
1644
+ withGeoIndex: (indexName: string, build: (q: GeoFilterBuilderLike) => GeoFilterBuilderLike) => TableReaderLike;
1606
1645
  withIndex: (indexName: string, range?: (q: IndexRangeBuilderLike) => IndexRangeBuilderLike) => TableReaderLike;
1607
1646
  withSearchIndex: (indexName: string, search: (q: SearchFilterBuilderLike) => SearchFilterBuilderLike) => TableReaderLike;
1608
1647
  }
@@ -2899,7 +2938,7 @@ interface FunctionCallStat {
2899
2938
  interface TableIndexInfo {
2900
2939
  fields: string[];
2901
2940
  name: string;
2902
- type: "index" | "rank" | "search" | "vector";
2941
+ type: "geo" | "index" | "rank" | "search" | "vector";
2903
2942
  unique?: boolean;
2904
2943
  }
2905
2944
  /** Payload of a `__lunora_admin__:listTableIndexes` call: every declared index on the table. */
@@ -3543,6 +3582,39 @@ declare const readFunctionMetricsTotals: (sql: SqlExec) => {
3543
3582
  errors: number;
3544
3583
  requests: number;
3545
3584
  };
3585
+ /** Default geohash precision (characters) maintained by a `.geoIndex()` companion — ~4.8 m cells. */
3586
+ declare const GEO_DEFAULT_PRECISION = 9;
3587
+ /** A latitude/longitude point (WGS84 decimal degrees). */
3588
+ interface GeoPoint {
3589
+ lat: number;
3590
+ lng: number;
3591
+ }
3592
+ /** An axis-aligned latitude/longitude bounding box (`sw`/`ne` corners). */
3593
+ interface GeoBoundingBox {
3594
+ ne: GeoPoint;
3595
+ sw: GeoPoint;
3596
+ }
3597
+ /**
3598
+ * Encode `point` to a geohash of `precision` characters. Standard interleaved
3599
+ * lat/lng bisection over the base-32 alphabet.
3600
+ */
3601
+ declare const encodeGeohash: (point: GeoPoint, precision: number) => string;
3602
+ /** Great-circle distance between two points in metres (Haversine). */
3603
+ declare const haversineMeters: (a: GeoPoint, b: GeoPoint) => number;
3604
+ /**
3605
+ * The center cell plus its eight neighbours at a precision chosen so each cell is
3606
+ * at least `radiusMeters` wide — the geohash prefixes to range-scan for a
3607
+ * proximity query. Deduplicated (near a pole neighbours can collapse).
3608
+ */
3609
+ declare const coveringGeohashes: (center: GeoPoint, radiusMeters: number) => string[];
3610
+ /** Whether `point` falls inside `box` (inclusive edges). */
3611
+ declare const pointInBoundingBox: (point: GeoPoint, box: GeoBoundingBox) => boolean;
3612
+ /**
3613
+ * Geohash prefixes covering `box`: the covering cells of the circle centered on
3614
+ * the box whose radius reaches the north-east corner, guaranteeing every point
3615
+ * in the box is scanned before the exact `pointInBoundingBox` refine.
3616
+ */
3617
+ declare const boundingBoxGeohashes: (box: GeoBoundingBox) => string[];
3546
3618
  /**
3547
3619
  * Severity of a `ctx.log.*` call. The five console method names (`log` is the
3548
3620
  * default level, distinct from `info`) plus `trace`/`fatal`, so the logger spans
@@ -4152,6 +4224,28 @@ declare class SessionDO {
4152
4224
  private handleGet;
4153
4225
  private handleRevoke;
4154
4226
  }
4227
+ /** One table's resolved TTL policy, as surfaced to the DO alarm by the generated shard subclass. */
4228
+ interface TtlSweepSpec {
4229
+ /** Millisecond offset added to `field` to derive the expiry (`field + after`); absent ⇒ `field` is the absolute expiry. */
4230
+ after?: number;
4231
+ /** The epoch-millisecond expiry column. */
4232
+ field: string;
4233
+ /** The `.softDelete()` marker column, when the table soft-deletes — expired-but-already-tombstoned rows are skipped. */
4234
+ softDeleteField?: string;
4235
+ /** The table whose expired rows are swept. */
4236
+ table: string;
4237
+ }
4238
+ /**
4239
+ * Select up to `limit` ids of rows in `spec.table` whose TTL expired at `now`
4240
+ * (i.e. `field + (after ?? 0) < now`). `hasMore` is `true` when matches remained
4241
+ * beyond `limit`, so the caller can loop a bounded batch. When `spec.softDeleteField`
4242
+ * is set, rows already soft-deleted (marker non-null) are excluded so the sweep
4243
+ * never re-touches a tombstone.
4244
+ */
4245
+ declare const selectExpiredIds: (sql: SqlExec, spec: TtlSweepSpec, now: number, limit: number) => {
4246
+ hasMore: boolean;
4247
+ ids: string[];
4248
+ };
4155
4249
  /**
4156
4250
  * Diff the previously-sent list snapshot (`previousJson`, the memo's
4157
4251
  * `lastJson`) against the new query result and produce per-row
@@ -5631,6 +5725,35 @@ declare abstract class ShardDO {
5631
5725
  * no-op when the runtime exposes no `setAlarm` (unit harness).
5632
5726
  */
5633
5727
  protected scheduleSourcePoll(): Promise<void>;
5728
+ /**
5729
+ * The resolved TTL policies (`.ttl(field, { after })`) for this DO's schema —
5730
+ * one {@link TtlSweepSpec} per table that declares a TTL. The base `ShardDO`
5731
+ * has no schema, so it returns `[]` and the TTL tier stays dormant. The
5732
+ * codegen subclass overrides it to read each table's `ttlPolicy` (+ its
5733
+ * `.softDelete()` marker) off the imported schema.
5734
+ */
5735
+ protected ttlSweeps(): ReadonlyArray<TtlSweepSpec>;
5736
+ /**
5737
+ * Sweep every `.ttl()` table once: page the rows past their expiry and remove
5738
+ * each THROUGH the schema-aware writer (`deleteRowThroughWriter`) so companions
5739
+ * / CDC / live subscriptions stay correct and a `.softDelete()` table soft-deletes
5740
+ * instead of physically removing the row. Work is bounded per tick
5741
+ * ({@link TTL_SWEEP_BATCH} × {@link TTL_SWEEP_MAX_BATCHES}) so a large backlog
5742
+ * drains across several alarms without stalling the shard.
5743
+ *
5744
+ * Returns the next-due timestamp (a coarse {@link TTL_SWEEP_INTERVAL_MS}
5745
+ * cadence, so freshly-written rows expire within a bounded window) while any
5746
+ * TTL table exists, or `undefined` when there are none — so a DO with no TTL
5747
+ * table never arms this tier.
5748
+ */
5749
+ protected pollTtlSweeps(): Promise<number | undefined>;
5750
+ /**
5751
+ * Arm the shared poll alarm for the TTL sweep. Mirrors {@link scheduleSourcePoll};
5752
+ * the codegen subclass calls it once on construction when the schema declares a
5753
+ * `.ttl()` table so the sweep loop starts, after which {@link ShardDO.alarm}
5754
+ * re-arms itself. Idempotent; a no-op when the runtime exposes no `setAlarm`.
5755
+ */
5756
+ protected scheduleTtlSweep(): Promise<void>;
5634
5757
  /** This DO's shard key (its DO name), or `__root__` for the single-DO default. The `tenantBy` mapper binds it into the source query. */
5635
5758
  protected currentShardKey(): string;
5636
5759
  /** Record a contained external-source ingest failure (one sourced table's poll) into the log ring without aborting the others. */
@@ -7083,4 +7206,4 @@ interface WhereSqlStrategy {
7083
7206
  * `undefined` when the input imposes no constraint (empty `where`).
7084
7207
  */
7085
7208
  declare const compileWhereSql: (where: WhereInput | undefined, strategy: WhereSqlStrategy) => SQL | undefined;
7086
- 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 ContextMetrics, type ContextTracer, 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 MetricsDeps, 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 TelemetrySink, type TraceAnchor, type TracerDeps, 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, createMetrics, createShardCtxDb, createSystemReader, createTracer, decodeCursor, depKey, diffExternalSource, dispatchRootSpan, 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 };
7209
+ 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 ContextMetrics, type ContextTracer, 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, GEO_DEFAULT_PRECISION, type GeoBoundingBox, type GeoFilterBuilderLike, type GeoIndexDefinitionLike, type GeoPoint, 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 MetricsDeps, 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 TelemetrySink, type TraceAnchor, type TracerDeps, type TransactionSqlLike, type TriggerContextLike, type TriggerDefinitionLike, type TriggerEventLike, type TriggerOpLike, type TriggerTimingLike, type TtlSweepSpec, 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, boundingBoxGeohashes, buildFtsMatch, buildSecurityAudit, buildSeekWhere, clearCapturedMail, coerceAggregateNumber, compileWhereSql, containsRelationPredicate, coveringGeohashes, createDependencyTracker, createMetrics, createShardCtxDb, createSystemReader, createTracer, decodeCursor, depKey, diffExternalSource, dispatchRootSpan, encodeAggregateKey, encodeCursor, encodeGeohash, encodePartitionKey, ensureAuthMetricsTables, ensureFunctionMetricsTables, ensureMailTable, exportShardRows, exportShardTable, facetColumn, fanOutScalarCounts, foldAggregateTally, ftsTableName, guardWriter, hasTrigger, haversineMeters, importShardRows, isRelationPredicate, isSoftDeleted, isSourceDue, liftSourceId, listTables, matchesRankStaticWhere, matchesStaticWhere, materializeExternalRows, materializeExternalRowsIncremental, mergeWhere, normalizeCountArgument, normalizeIdStructurally, normalizeOrderKeys, parseExportShardArgs, parseImportShardArgs, planAggregateLookup, pointInBoundingBox, 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, selectExpiredIds, selectExportTables, selectIndexForAggregate, selectIndexForCount, selectIndexForGroupBy, selectMatchingIds, serveRelationFanout, softDeleteScope, sortColumnName, stableStringify, stableWireKey, stringifySearchText, subscriptionListDeltas, throwingScheduler, tokenizeSearch, trimCdcChanges, validateImportRow };
package/dist/index.d.ts CHANGED
@@ -1312,6 +1312,13 @@ interface SchemaLike {
1312
1312
  }
1313
1313
  interface TableDefinitionLike {
1314
1314
  readonly aggregateIndexes?: ReadonlyArray<AggregateIndexDefinitionLike>;
1315
+ /**
1316
+ * Mirror of `@lunora/server`'s `TableDefinition.geoIndexes` (set by
1317
+ * `.geoIndex()`). Each declares a geohash companion over a `v.geoPoint()`
1318
+ * column so `withGeoIndex(name, q => q.near(...) | q.within(...))` resolves
1319
+ * proximity / bounding-box reads. Empty/absent ⇒ the table has no geo index.
1320
+ */
1321
+ readonly geoIndexes?: ReadonlyArray<GeoIndexDefinitionLike>;
1315
1322
  readonly indexes: ReadonlyArray<IndexDefinitionLike>;
1316
1323
  /**
1317
1324
  * `true` when `.public()` opted this table OUT of secure-by-default RLS
@@ -1339,6 +1346,15 @@ interface TableDefinitionLike {
1339
1346
  field: string;
1340
1347
  };
1341
1348
  readonly triggerMap?: Record<string, TriggerDefinitionLike>;
1349
+ /**
1350
+ * Mirror of `@lunora/server`'s `TableDefinition.ttlPolicy` (set by `.ttl()`).
1351
+ * Drives the DO alarm-driven expiry sweep — see `ttl-sweep.ts`. Absent ⇒ rows
1352
+ * never auto-expire.
1353
+ */
1354
+ readonly ttlPolicy?: {
1355
+ after?: number;
1356
+ field: string;
1357
+ };
1342
1358
  }
1343
1359
  interface IndexDefinitionLike {
1344
1360
  readonly fields: ReadonlyArray<string>;
@@ -1350,6 +1366,12 @@ interface SearchIndexDefinitionLike {
1350
1366
  readonly filterFields?: ReadonlyArray<string>;
1351
1367
  readonly name: string;
1352
1368
  }
1369
+ /** Mirror of `@lunora/server`'s `GeoIndexDefinition` — a geohash companion over a `v.geoPoint()` column. */
1370
+ interface GeoIndexDefinitionLike {
1371
+ readonly field: string;
1372
+ readonly name: string;
1373
+ readonly precision?: number;
1374
+ }
1353
1375
  /**
1354
1376
  * Column constraints/defaults the write layer honors, mirrored structurally
1355
1377
  * from `@lunora/values`' `ColumnMeta` (kept local so this package doesn't take
@@ -1423,7 +1445,7 @@ type ReadHook = (table: string, idOrScan?: string) => void;
1423
1445
  * No-op by default; called at most once per read (not per row), so it adds no
1424
1446
  * meaningful hot-path cost.
1425
1447
  */
1426
- type IndexUseHook = (table: string, indexName: string, kind: "index" | "rank" | "search") => void;
1448
+ type IndexUseHook = (table: string, indexName: string, kind: "geo" | "index" | "rank" | "search") => void;
1427
1449
  /** Pluggable wall clock — defaults to `Date.now`. */
1428
1450
  type Clock = () => number;
1429
1451
  /** Pluggable ID minter — defaults to `crypto.randomUUID()`. */
@@ -1566,6 +1588,22 @@ interface SearchFilterBuilderLike {
1566
1588
  eq: (field: string, value: unknown) => SearchFilterBuilderLike;
1567
1589
  search: (field: string, query: string) => SearchFilterBuilderLike;
1568
1590
  }
1591
+ interface GeoFilterBuilderLike {
1592
+ near: (point: {
1593
+ lat: number;
1594
+ lng: number;
1595
+ }, radiusMeters: number) => GeoFilterBuilderLike;
1596
+ within: (box: {
1597
+ ne: {
1598
+ lat: number;
1599
+ lng: number;
1600
+ };
1601
+ sw: {
1602
+ lat: number;
1603
+ lng: number;
1604
+ };
1605
+ }) => GeoFilterBuilderLike;
1606
+ }
1569
1607
  /** Options accepted by {@link TableReaderLike.paginate} — Convex-compatible. */
1570
1608
  interface PaginationOptions {
1571
1609
  /** Opaque cursor from a prior page's `continueCursor`; `null`/omitted starts at the first page. */
@@ -1603,6 +1641,7 @@ interface TableReaderLike {
1603
1641
  * more than one matches. Mirrors Convex's `.unique()`.
1604
1642
  */
1605
1643
  unique: () => Promise<Record<string, unknown> | null>;
1644
+ withGeoIndex: (indexName: string, build: (q: GeoFilterBuilderLike) => GeoFilterBuilderLike) => TableReaderLike;
1606
1645
  withIndex: (indexName: string, range?: (q: IndexRangeBuilderLike) => IndexRangeBuilderLike) => TableReaderLike;
1607
1646
  withSearchIndex: (indexName: string, search: (q: SearchFilterBuilderLike) => SearchFilterBuilderLike) => TableReaderLike;
1608
1647
  }
@@ -2899,7 +2938,7 @@ interface FunctionCallStat {
2899
2938
  interface TableIndexInfo {
2900
2939
  fields: string[];
2901
2940
  name: string;
2902
- type: "index" | "rank" | "search" | "vector";
2941
+ type: "geo" | "index" | "rank" | "search" | "vector";
2903
2942
  unique?: boolean;
2904
2943
  }
2905
2944
  /** Payload of a `__lunora_admin__:listTableIndexes` call: every declared index on the table. */
@@ -3543,6 +3582,39 @@ declare const readFunctionMetricsTotals: (sql: SqlExec) => {
3543
3582
  errors: number;
3544
3583
  requests: number;
3545
3584
  };
3585
+ /** Default geohash precision (characters) maintained by a `.geoIndex()` companion — ~4.8 m cells. */
3586
+ declare const GEO_DEFAULT_PRECISION = 9;
3587
+ /** A latitude/longitude point (WGS84 decimal degrees). */
3588
+ interface GeoPoint {
3589
+ lat: number;
3590
+ lng: number;
3591
+ }
3592
+ /** An axis-aligned latitude/longitude bounding box (`sw`/`ne` corners). */
3593
+ interface GeoBoundingBox {
3594
+ ne: GeoPoint;
3595
+ sw: GeoPoint;
3596
+ }
3597
+ /**
3598
+ * Encode `point` to a geohash of `precision` characters. Standard interleaved
3599
+ * lat/lng bisection over the base-32 alphabet.
3600
+ */
3601
+ declare const encodeGeohash: (point: GeoPoint, precision: number) => string;
3602
+ /** Great-circle distance between two points in metres (Haversine). */
3603
+ declare const haversineMeters: (a: GeoPoint, b: GeoPoint) => number;
3604
+ /**
3605
+ * The center cell plus its eight neighbours at a precision chosen so each cell is
3606
+ * at least `radiusMeters` wide — the geohash prefixes to range-scan for a
3607
+ * proximity query. Deduplicated (near a pole neighbours can collapse).
3608
+ */
3609
+ declare const coveringGeohashes: (center: GeoPoint, radiusMeters: number) => string[];
3610
+ /** Whether `point` falls inside `box` (inclusive edges). */
3611
+ declare const pointInBoundingBox: (point: GeoPoint, box: GeoBoundingBox) => boolean;
3612
+ /**
3613
+ * Geohash prefixes covering `box`: the covering cells of the circle centered on
3614
+ * the box whose radius reaches the north-east corner, guaranteeing every point
3615
+ * in the box is scanned before the exact `pointInBoundingBox` refine.
3616
+ */
3617
+ declare const boundingBoxGeohashes: (box: GeoBoundingBox) => string[];
3546
3618
  /**
3547
3619
  * Severity of a `ctx.log.*` call. The five console method names (`log` is the
3548
3620
  * default level, distinct from `info`) plus `trace`/`fatal`, so the logger spans
@@ -4152,6 +4224,28 @@ declare class SessionDO {
4152
4224
  private handleGet;
4153
4225
  private handleRevoke;
4154
4226
  }
4227
+ /** One table's resolved TTL policy, as surfaced to the DO alarm by the generated shard subclass. */
4228
+ interface TtlSweepSpec {
4229
+ /** Millisecond offset added to `field` to derive the expiry (`field + after`); absent ⇒ `field` is the absolute expiry. */
4230
+ after?: number;
4231
+ /** The epoch-millisecond expiry column. */
4232
+ field: string;
4233
+ /** The `.softDelete()` marker column, when the table soft-deletes — expired-but-already-tombstoned rows are skipped. */
4234
+ softDeleteField?: string;
4235
+ /** The table whose expired rows are swept. */
4236
+ table: string;
4237
+ }
4238
+ /**
4239
+ * Select up to `limit` ids of rows in `spec.table` whose TTL expired at `now`
4240
+ * (i.e. `field + (after ?? 0) < now`). `hasMore` is `true` when matches remained
4241
+ * beyond `limit`, so the caller can loop a bounded batch. When `spec.softDeleteField`
4242
+ * is set, rows already soft-deleted (marker non-null) are excluded so the sweep
4243
+ * never re-touches a tombstone.
4244
+ */
4245
+ declare const selectExpiredIds: (sql: SqlExec, spec: TtlSweepSpec, now: number, limit: number) => {
4246
+ hasMore: boolean;
4247
+ ids: string[];
4248
+ };
4155
4249
  /**
4156
4250
  * Diff the previously-sent list snapshot (`previousJson`, the memo's
4157
4251
  * `lastJson`) against the new query result and produce per-row
@@ -5631,6 +5725,35 @@ declare abstract class ShardDO {
5631
5725
  * no-op when the runtime exposes no `setAlarm` (unit harness).
5632
5726
  */
5633
5727
  protected scheduleSourcePoll(): Promise<void>;
5728
+ /**
5729
+ * The resolved TTL policies (`.ttl(field, { after })`) for this DO's schema —
5730
+ * one {@link TtlSweepSpec} per table that declares a TTL. The base `ShardDO`
5731
+ * has no schema, so it returns `[]` and the TTL tier stays dormant. The
5732
+ * codegen subclass overrides it to read each table's `ttlPolicy` (+ its
5733
+ * `.softDelete()` marker) off the imported schema.
5734
+ */
5735
+ protected ttlSweeps(): ReadonlyArray<TtlSweepSpec>;
5736
+ /**
5737
+ * Sweep every `.ttl()` table once: page the rows past their expiry and remove
5738
+ * each THROUGH the schema-aware writer (`deleteRowThroughWriter`) so companions
5739
+ * / CDC / live subscriptions stay correct and a `.softDelete()` table soft-deletes
5740
+ * instead of physically removing the row. Work is bounded per tick
5741
+ * ({@link TTL_SWEEP_BATCH} × {@link TTL_SWEEP_MAX_BATCHES}) so a large backlog
5742
+ * drains across several alarms without stalling the shard.
5743
+ *
5744
+ * Returns the next-due timestamp (a coarse {@link TTL_SWEEP_INTERVAL_MS}
5745
+ * cadence, so freshly-written rows expire within a bounded window) while any
5746
+ * TTL table exists, or `undefined` when there are none — so a DO with no TTL
5747
+ * table never arms this tier.
5748
+ */
5749
+ protected pollTtlSweeps(): Promise<number | undefined>;
5750
+ /**
5751
+ * Arm the shared poll alarm for the TTL sweep. Mirrors {@link scheduleSourcePoll};
5752
+ * the codegen subclass calls it once on construction when the schema declares a
5753
+ * `.ttl()` table so the sweep loop starts, after which {@link ShardDO.alarm}
5754
+ * re-arms itself. Idempotent; a no-op when the runtime exposes no `setAlarm`.
5755
+ */
5756
+ protected scheduleTtlSweep(): Promise<void>;
5634
5757
  /** This DO's shard key (its DO name), or `__root__` for the single-DO default. The `tenantBy` mapper binds it into the source query. */
5635
5758
  protected currentShardKey(): string;
5636
5759
  /** Record a contained external-source ingest failure (one sourced table's poll) into the log ring without aborting the others. */
@@ -7083,4 +7206,4 @@ interface WhereSqlStrategy {
7083
7206
  * `undefined` when the input imposes no constraint (empty `where`).
7084
7207
  */
7085
7208
  declare const compileWhereSql: (where: WhereInput | undefined, strategy: WhereSqlStrategy) => SQL | undefined;
7086
- 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 ContextMetrics, type ContextTracer, 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 MetricsDeps, 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 TelemetrySink, type TraceAnchor, type TracerDeps, 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, createMetrics, createShardCtxDb, createSystemReader, createTracer, decodeCursor, depKey, diffExternalSource, dispatchRootSpan, 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 };
7209
+ 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 ContextMetrics, type ContextTracer, 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, GEO_DEFAULT_PRECISION, type GeoBoundingBox, type GeoFilterBuilderLike, type GeoIndexDefinitionLike, type GeoPoint, 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 MetricsDeps, 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 TelemetrySink, type TraceAnchor, type TracerDeps, type TransactionSqlLike, type TriggerContextLike, type TriggerDefinitionLike, type TriggerEventLike, type TriggerOpLike, type TriggerTimingLike, type TtlSweepSpec, 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, boundingBoxGeohashes, buildFtsMatch, buildSecurityAudit, buildSeekWhere, clearCapturedMail, coerceAggregateNumber, compileWhereSql, containsRelationPredicate, coveringGeohashes, createDependencyTracker, createMetrics, createShardCtxDb, createSystemReader, createTracer, decodeCursor, depKey, diffExternalSource, dispatchRootSpan, encodeAggregateKey, encodeCursor, encodeGeohash, encodePartitionKey, ensureAuthMetricsTables, ensureFunctionMetricsTables, ensureMailTable, exportShardRows, exportShardTable, facetColumn, fanOutScalarCounts, foldAggregateTally, ftsTableName, guardWriter, hasTrigger, haversineMeters, importShardRows, isRelationPredicate, isSoftDeleted, isSourceDue, liftSourceId, listTables, matchesRankStaticWhere, matchesStaticWhere, materializeExternalRows, materializeExternalRowsIncremental, mergeWhere, normalizeCountArgument, normalizeIdStructurally, normalizeOrderKeys, parseExportShardArgs, parseImportShardArgs, planAggregateLookup, pointInBoundingBox, 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, selectExpiredIds, selectExportTables, selectIndexForAggregate, selectIndexForCount, selectIndexForGroupBy, selectMatchingIds, serveRelationFanout, softDeleteScope, sortColumnName, stableStringify, stableWireKey, stringifySearchText, subscriptionListDeltas, throwingScheduler, tokenizeSearch, trimCdcChanges, validateImportRow };
package/dist/index.mjs CHANGED
@@ -4,14 +4,15 @@ export { aggregateTableName, coerceAggregateNumber, encodeAggregateKey, foldAggr
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
6
  export { c as createMetrics, a as createTracer, d as dispatchRootSpan } from './packem_shared/context-telemetry-DWfYDxCS.mjs';
7
- export { NotUniqueError, assertValidClientId, createShardCtxDb, normalizeIdStructurally } from './packem_shared/NotUniqueError-Ca21uDuU.mjs';
7
+ export { NotUniqueError, assertValidClientId, createShardCtxDb, normalizeIdStructurally } from './packem_shared/NotUniqueError-DbtlWwcG.mjs';
8
8
  export { DATA_MIGRATION_STATE_TABLE, readMigrationStatus, runDataMigration } from './packem_shared/DATA_MIGRATION_STATE_TABLE-CYwBpyTr.mjs';
9
9
  export { SCAN_DEP, createDependencyTracker, depKey } from './packem_shared/SCAN_DEP-DLJF8dsj.mjs';
10
10
  export { renderSql } from './packem_shared/renderSql-D6eUcn2N.mjs';
11
11
  export { diffExternalSource } from './packem_shared/diffExternalSource-CovHfdyo.mjs';
12
- export { materializeExternalRows, materializeExternalRowsIncremental, readExternalSourceBaseline, runExternalSourceTick } from './packem_shared/materializeExternalRows-DlQWMlw_.mjs';
13
- export { isSoftDeleted, isSourceDue, liftSourceId, pullExternalSourceIncrementalTick, pullExternalSourceTick } from './packem_shared/isSoftDeleted-YLKR6JYw.mjs';
12
+ export { materializeExternalRows, materializeExternalRowsIncremental, readExternalSourceBaseline, runExternalSourceTick } from './packem_shared/materializeExternalRows-BtEGs1Fv.mjs';
13
+ export { isSoftDeleted, isSourceDue, liftSourceId, pullExternalSourceIncrementalTick, pullExternalSourceTick } from './packem_shared/isSoftDeleted-CFJmhjFP.mjs';
14
14
  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';
15
+ export { GEO_DEFAULT_PRECISION, boundingBoxGeohashes, coveringGeohashes, encodeGeohash, haversineMeters, pointInBoundingBox } from './packem_shared/GEO_DEFAULT_PRECISION-BWnsNmpP.mjs';
15
16
  export { ADMIN_FUNCTIONS, ADMIN_FUNCTION_PREFIX, FLAGS_FUNCTION_PREFIX, RELATION_FUNCTION_PREFIX, facetColumn, listTables, readTablePage, selectMatchingIds } from './packem_shared/ADMIN_FUNCTIONS-DVk02KpP.mjs';
16
17
  export { LogBuffer } from './packem_shared/LogBuffer-B_Ezju_N.mjs';
17
18
  export { MAIL_RETENTION, MAIL_TABLE, clearCapturedMail, ensureMailTable, readCapturedMail, recordCapturedMail } from './packem_shared/MAIL_RETENTION-CPpgl-dX.mjs';
@@ -27,16 +28,17 @@ export { RLS_UNWRAP_SYMBOL, RlsRequiredError, guardWriter } from './packem_share
27
28
  export { buildFtsMatch, ftsTableName, scoreDocument, stringifySearchText, tokenizeSearch } from './packem_shared/buildFtsMatch-BLEMawrp.mjs';
28
29
  export { M as MIN_ADMIN_TOKEN_LENGTH, a as MIN_AUTH_SECRET_LENGTH, b as buildSecurityAudit } from './packem_shared/security-audit-CucgBice.mjs';
29
30
  export { SESSION_DO_TTL_DEFAULT, SessionDO } from './packem_shared/SESSION_DO_TTL_DEFAULT-BnSKgVO4.mjs';
30
- export { ROOT_DO_SIZE_WARN_BYTES, ROOT_SHARD_NAME, ShardDO } from './packem_shared/ROOT_DO_SIZE_WARN_BYTES-CBlw_ir3.mjs';
31
+ export { ROOT_DO_SIZE_WARN_BYTES, ROOT_SHARD_NAME, ShardDO } from './packem_shared/ROOT_DO_SIZE_WARN_BYTES-FAG0bOO5.mjs';
31
32
  export { SHARD_REGISTRY_DO_NAME, ShardRegistryDO } from './packem_shared/SHARD_REGISTRY_DO_NAME-D99roc-r.mjs';
32
33
  export { MAX_SQL_ROWS, assertReadonly, runReadonlySql } from './packem_shared/MAX_SQL_ROWS-iFAA8FbD.mjs';
33
34
  export { createSystemReader } from './packem_shared/createSystemReader-D12eNH13.mjs';
34
35
  export { ConflictError } from './packem_shared/ConflictError-CLoq37xH.mjs';
35
36
  export { hasTrigger, runTriggers } from './packem_shared/hasTrigger-5N6_Fx0A.mjs';
37
+ export { selectExpiredIds } from './packem_shared/selectExpiredIds-C5W29Upb.mjs';
36
38
  export { compileWhereSql } from './packem_shared/compileWhereSql-DE6yfRcQ.mjs';
37
39
  export { CDC_LOG_TABLE, applyCdcChanges, readCdcChanges, trimCdcChanges } from './packem_shared/CDC_LOG_TABLE-uwOJxJJZ.mjs';
38
- export { backfillAggregateIndexes, backfillRankIndexes } from './packem_shared/backfillAggregateIndexes-DDoT-UUI.mjs';
39
- export { runShardMigrations } from './packem_shared/runShardMigrations-DFzx6Qld.mjs';
40
+ export { backfillAggregateIndexes, backfillRankIndexes } from './packem_shared/backfillAggregateIndexes-BGTwynwh.mjs';
41
+ export { runShardMigrations } from './packem_shared/runShardMigrations-BVax6rYu.mjs';
40
42
  export { stableStringify } from './packem_shared/stableStringify-mC40mZts.mjs';
41
43
  export { stableWireKey } from './packem_shared/stableWireKey-DKuXO7T5.mjs';
42
44
  export { subscriptionListDeltas } from './packem_shared/subscriptionListDeltas-CT76bYny.mjs';
@@ -0,0 +1,114 @@
1
+ const BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz";
2
+ const GEO_DEFAULT_PRECISION = 9;
3
+ const EARTH_RADIUS_METERS = 63710088e-1;
4
+ const CELL_WIDTH_METERS = [Number.POSITIVE_INFINITY, 5009400, 1252300, 156500, 39100, 4900, 1200, 152.9, 38.2, 4.77, 1.19, 0.149, 0.037];
5
+ const clampPrecision = (precision) => Math.min(Math.max(Math.trunc(precision), 1), 12);
6
+ const encodeGeohash = (point, precision) => {
7
+ const chars = clampPrecision(precision);
8
+ let latMin = -90;
9
+ let latMax = 90;
10
+ let lngMin = -180;
11
+ let lngMax = 180;
12
+ let hash = "";
13
+ let bit = 0;
14
+ let index = 0;
15
+ let even = true;
16
+ while (hash.length < chars) {
17
+ if (even) {
18
+ const mid = (lngMin + lngMax) / 2;
19
+ if (point.lng >= mid) {
20
+ index = index * 2 + 1;
21
+ lngMin = mid;
22
+ } else {
23
+ index *= 2;
24
+ lngMax = mid;
25
+ }
26
+ } else {
27
+ const mid = (latMin + latMax) / 2;
28
+ if (point.lat >= mid) {
29
+ index = index * 2 + 1;
30
+ latMin = mid;
31
+ } else {
32
+ index *= 2;
33
+ latMax = mid;
34
+ }
35
+ }
36
+ even = !even;
37
+ if (bit < 4) {
38
+ bit += 1;
39
+ } else {
40
+ hash += BASE32[index] ?? "0";
41
+ bit = 0;
42
+ index = 0;
43
+ }
44
+ }
45
+ return hash;
46
+ };
47
+ const haversineMeters = (a, b) => {
48
+ const toRadians = (degrees) => degrees * Math.PI / 180;
49
+ const dLat = toRadians(b.lat - a.lat);
50
+ const dLng = toRadians(b.lng - a.lng);
51
+ const lat1 = toRadians(a.lat);
52
+ const lat2 = toRadians(b.lat);
53
+ const h = Math.sin(dLat / 2) ** 2 + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLng / 2) ** 2;
54
+ return 2 * EARTH_RADIUS_METERS * Math.asin(Math.min(1, Math.sqrt(h)));
55
+ };
56
+ const precisionForRadius = (radiusMeters) => {
57
+ for (let length = CELL_WIDTH_METERS.length - 1; length >= 1; length -= 1) {
58
+ const width = CELL_WIDTH_METERS[length];
59
+ if (width !== void 0 && width >= radiusMeters) {
60
+ return length;
61
+ }
62
+ }
63
+ return 1;
64
+ };
65
+ const NEIGHBOURS = {
66
+ east: ["bc01fg45238967deuvhjyznpkmstqrwx", "bc01fg45238967deuvhjyznpkmstqrwx"],
67
+ north: ["p0r21436x8zb9dcf5h7kjnmqesgutwvy", "bc01fg45238967deuvhjyznpkmstqrwx"],
68
+ south: ["14365h7k9dcfesgujnmqp0r2twvyx8zb", "238967debc01fg45kmstqrwxuvhjyznp"],
69
+ west: ["238967debc01fg45kmstqrwxuvhjyznp", "238967debc01fg45kmstqrwxuvhjyznp"]
70
+ };
71
+ const BORDERS = {
72
+ east: ["bcfguvyz", "prxz"],
73
+ north: ["prxz", "bcfguvyz"],
74
+ south: ["028b", "0145hjnp"],
75
+ west: ["0145hjnp", "028b"]
76
+ };
77
+ const adjacent = (hash, direction) => {
78
+ const lower = hash.toLowerCase();
79
+ const lastChar = lower.at(-1) ?? "";
80
+ let base = lower.slice(0, -1);
81
+ const type = lower.length % 2 === 0 ? 0 : 1;
82
+ if (BORDERS[direction][type].includes(lastChar) && base !== "") {
83
+ base = adjacent(base, direction);
84
+ }
85
+ return base + (BASE32[NEIGHBOURS[direction][type].indexOf(lastChar)] ?? "");
86
+ };
87
+ const coveringGeohashes = (center, radiusMeters) => {
88
+ const precision = precisionForRadius(Math.max(radiusMeters, 1));
89
+ const origin = encodeGeohash(center, precision);
90
+ const north = adjacent(origin, "north");
91
+ const south = adjacent(origin, "south");
92
+ const cells = [
93
+ origin,
94
+ north,
95
+ south,
96
+ adjacent(origin, "east"),
97
+ adjacent(origin, "west"),
98
+ adjacent(north, "east"),
99
+ adjacent(north, "west"),
100
+ adjacent(south, "east"),
101
+ adjacent(south, "west")
102
+ ];
103
+ return [...new Set(cells)];
104
+ };
105
+ const pointInBoundingBox = (point, box) => point.lat >= box.sw.lat && point.lat <= box.ne.lat && point.lng >= box.sw.lng && point.lng <= box.ne.lng;
106
+ const boundingBoxCenter = (box) => {
107
+ return { lat: (box.sw.lat + box.ne.lat) / 2, lng: (box.sw.lng + box.ne.lng) / 2 };
108
+ };
109
+ const boundingBoxGeohashes = (box) => {
110
+ const center = boundingBoxCenter(box);
111
+ return coveringGeohashes(center, Math.max(haversineMeters(center, box.ne), 1));
112
+ };
113
+
114
+ export { GEO_DEFAULT_PRECISION, boundingBoxCenter, boundingBoxGeohashes, coveringGeohashes, encodeGeohash, haversineMeters, pointInBoundingBox };
@@ -6,8 +6,9 @@ import { mergeWhere, CountRlsUnsupportedError, selectIndexForGroupBy, selectInde
6
6
  import { appendCdcChange } from './CDC_LOG_TABLE-uwOJxJJZ.mjs';
7
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
- 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';
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, g as geoTableName, d as aggUpsertSql, j as jsonPathSql, q as quoteIdentifier, t as tableColumns, e as qualifiedJsonPathSql } from './do-sql-CGAgiQUz.mjs';
10
10
  import { param } from './renderSql-D6eUcn2N.mjs';
11
+ import { encodeGeohash, GEO_DEFAULT_PRECISION, coveringGeohashes, boundingBoxGeohashes, pointInBoundingBox, haversineMeters } from './GEO_DEFAULT_PRECISION-BWnsNmpP.mjs';
11
12
  import { sortColumnName, matchesRankStaticWhere, encodePartitionKey, rankTableName, resolveRankPartition, RANK_TIEBREAK } from './RANK_TIEBREAK-CXhdcA1o.mjs';
12
13
  import { stringifySearchText, ftsTableName, tokenizeSearch, buildFtsMatch, scoreDocument } from './buildFtsMatch-BLEMawrp.mjs';
13
14
  import { s as serializeSqlValue } from './serialize-sql-BlRUoiQe.mjs';
@@ -21,10 +22,10 @@ import { createSystemReader } from './createSystemReader-D12eNH13.mjs';
21
22
  import { ConflictError } from './ConflictError-CLoq37xH.mjs';
22
23
  import { runTriggers } from './hasTrigger-5N6_Fx0A.mjs';
23
24
  import { compileWhereSql } from './compileWhereSql-DE6yfRcQ.mjs';
24
- export { backfillAggregateIndexes, backfillRankIndexes } from './backfillAggregateIndexes-DDoT-UUI.mjs';
25
+ export { backfillAggregateIndexes, backfillRankIndexes } from './backfillAggregateIndexes-BGTwynwh.mjs';
25
26
  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-DFzx6Qld.mjs';
27
- export { a as selectShapeMemberIds, s as selectShapeRows } from './ctx-db-shapes-Cz9dHyh1.mjs';
27
+ export { runShardMigrations } from './runShardMigrations-BVax6rYu.mjs';
28
+ export { a as selectShapeMemberIds, s as selectShapeRows } from './ctx-db-shapes-DTeFiHYS.mjs';
28
29
 
29
30
  const rankIndexFieldsUnchanged = (index, previous, next) => {
30
31
  const fields = [...index.partitionBy ?? [], ...index.sortBy.map((key) => key.field), ...index.where ? Object.keys(index.where) : []];
@@ -314,8 +315,28 @@ const createCompanionSync = (deps) => {
314
315
  }
315
316
  }
316
317
  };
318
+ const syncGeo = (tableName, id, document) => {
319
+ const indexes = schema.tables[tableName]?.geoIndexes;
320
+ if (!indexes || indexes.length === 0) {
321
+ return;
322
+ }
323
+ for (const index of indexes) {
324
+ const geoTable = geoTableName(tableName, index.name);
325
+ runDrizzle(sql$1, sql`DELETE FROM ${sql.identifier(geoTable)} WHERE ${sql.identifier("__id__")} = ${id}`);
326
+ const point = document?.[index.field];
327
+ if (point !== null && typeof point === "object" && typeof point.lat === "number" && typeof point.lng === "number") {
328
+ const { lat, lng } = point;
329
+ const hash = encodeGeohash({ lat, lng }, index.precision ?? GEO_DEFAULT_PRECISION);
330
+ runDrizzle(
331
+ sql$1,
332
+ sql`INSERT INTO ${sql.identifier(geoTable)} (${sql.identifier("__id__")}, ${sql.identifier("__geohash__")}, ${sql.identifier("__lat__")}, ${sql.identifier("__lng__")}) VALUES (${id}, ${hash}, ${lat}, ${lng})`
333
+ );
334
+ }
335
+ }
336
+ };
317
337
  const syncCompanionsForInsert = (tableName, id, document) => {
318
338
  syncSearch(tableName, id, document);
339
+ syncGeo(tableName, id, document);
319
340
  syncAggregates(tableName, void 0, document);
320
341
  syncRanks(tableName, id, void 0, document);
321
342
  invalidateCache(tableName, id);
@@ -329,6 +350,7 @@ const createCompanionSync = (deps) => {
329
350
  ensureRankBackfilledForTable,
330
351
  syncAggregates,
331
352
  syncCompanionsForInsert,
353
+ syncGeo,
332
354
  syncRanks,
333
355
  syncSearch
334
356
  };
@@ -599,6 +621,123 @@ const searchViaScan = (sql$1, tableName, search, limit, scopeCondition) => {
599
621
  const docs = scored.map((entry) => entry.doc);
600
622
  return typeof limit === "number" ? docs.slice(0, Math.max(0, Math.floor(limit))) : docs;
601
623
  };
624
+ const createGeoBuilder = (geo, tableName) => {
625
+ const staged = geo;
626
+ const builder = {
627
+ near: (point, radiusMeters) => {
628
+ if (staged.within) {
629
+ throw new LunoraError("INTERNAL", `geo index "${staged.indexName}" on table "${tableName}": call .near() or .within(), not both`);
630
+ }
631
+ staged.near = { point: { lat: point.lat, lng: point.lng }, radiusMeters };
632
+ return builder;
633
+ },
634
+ within: (box) => {
635
+ if (staged.near) {
636
+ throw new LunoraError("INTERNAL", `geo index "${staged.indexName}" on table "${tableName}": call .near() or .within(), not both`);
637
+ }
638
+ staged.within = { ne: { lat: box.ne.lat, lng: box.ne.lng }, sw: { lat: box.sw.lat, lng: box.sw.lng } };
639
+ return builder;
640
+ }
641
+ };
642
+ return builder;
643
+ };
644
+ const readGeoPoint = (document, field) => {
645
+ const value = document[field];
646
+ if (value === null || typeof value !== "object") {
647
+ return void 0;
648
+ }
649
+ const { lat, lng } = value;
650
+ return typeof lat === "number" && typeof lng === "number" ? { lat, lng } : void 0;
651
+ };
652
+ const scoreGeoRow = (record, geo) => {
653
+ const point = readGeoPoint(record, geo.definition.field);
654
+ if (!point) {
655
+ return void 0;
656
+ }
657
+ const creationTime = typeof record["_creationTime"] === "number" ? record["_creationTime"] : 0;
658
+ if (geo.near) {
659
+ const distance = haversineMeters(geo.near.point, point);
660
+ return distance <= geo.near.radiusMeters ? { creationTime, distance } : void 0;
661
+ }
662
+ return pointInBoundingBox(point, geo.within) ? { creationTime, distance: 0 } : void 0;
663
+ };
664
+ const runGeoFetch = (sql$1, tableName, geo, limit, scopeCondition) => {
665
+ if (!geo.near && !geo.within) {
666
+ throw new LunoraError("INTERNAL", `geo index "${geo.indexName}" on table "${tableName}": call .near(point, radius) or .within(box)`);
667
+ }
668
+ const prefixes = geo.near ? coveringGeohashes(geo.near.point, geo.near.radiusMeters) : boundingBoxGeohashes(geo.within);
669
+ const geoTable = geoTableName(tableName, geo.indexName);
670
+ const prefixClauses = prefixes.map(
671
+ (prefix) => sql`(g.${sql.identifier("__geohash__")} >= ${prefix} AND g.${sql.identifier("__geohash__")} < ${`${prefix}{`})`
672
+ );
673
+ const whereClauses = [sql`(${sql.join(prefixClauses, sql` OR `)})`];
674
+ if (scopeCondition) {
675
+ whereClauses.push(scopeCondition);
676
+ }
677
+ const query = sql`SELECT m.id, m._creationTime, m.${sql.identifier(DOC_COLUMN$1)} FROM ${sql.identifier(geoTable)} g JOIN ${sql.identifier(tableName)} m ON m.id = g.${sql.identifier("__id__")} WHERE ${sql.join(whereClauses, sql` AND `)}`;
678
+ const rows = runDrizzle(sql$1, query).toArray();
679
+ const scored = [];
680
+ for (const row of rows) {
681
+ const record = rowToDocument(row);
682
+ const score = record ? scoreGeoRow(record, geo) : void 0;
683
+ if (record && score) {
684
+ scored.push({ creationTime: score.creationTime, distance: score.distance, doc: record });
685
+ }
686
+ }
687
+ scored.sort((a, b) => a.distance - b.distance || b.creationTime - a.creationTime);
688
+ const docs = scored.map((entry) => entry.doc);
689
+ return typeof limit === "number" ? docs.slice(0, Math.max(0, Math.floor(limit))) : docs;
690
+ };
691
+ const runGeoTerminal = (sql, tableName, stage, scopeCondition, limit) => {
692
+ const { geo } = stage;
693
+ if (!geo) {
694
+ throw new LunoraError("INTERNAL", "runGeoTerminal called without a staged geo query");
695
+ }
696
+ const filtered = stage.inMemoryFilters.length > 0;
697
+ const docs = runGeoFetch(sql, tableName, geo, filtered ? void 0 : limit, scopeCondition);
698
+ if (!filtered) {
699
+ return docs;
700
+ }
701
+ const result = [];
702
+ for (const record of docs) {
703
+ if (stage.inMemoryFilters.every((predicate) => predicate(record))) {
704
+ result.push(record);
705
+ if (typeof limit === "number" && result.length >= limit) {
706
+ break;
707
+ }
708
+ }
709
+ }
710
+ return result;
711
+ };
712
+ const runPlainFetch = (sql$1, tableName, stage, scopeCondition, orderClause, limit) => {
713
+ const whereClauses = [];
714
+ for (const condition of stage.sqlConditions) {
715
+ whereClauses.push(sql`${jsonPathSql(condition.field)} ${sql.raw(condition.comparator)} ${serializeSqlValue(condition.value)}`);
716
+ }
717
+ if (scopeCondition) {
718
+ whereClauses.push(scopeCondition);
719
+ }
720
+ let query = sql`SELECT id, _creationTime, ${sql.identifier(DOC_COLUMN$1)} FROM ${sql.identifier(tableName)}`;
721
+ if (whereClauses.length > 0) {
722
+ query = sql`${query} WHERE ${sql.join(whereClauses, sql` AND `)}`;
723
+ }
724
+ query = sql`${query} ORDER BY ${orderClause}`;
725
+ if (typeof limit === "number" && stage.inMemoryFilters.length === 0) {
726
+ query = sql`${query} LIMIT ${sql.raw(String(Math.max(0, Math.floor(limit))))}`;
727
+ }
728
+ const rows = runDrizzle(sql$1, query).toArray();
729
+ const docs = [];
730
+ for (const row of rows) {
731
+ const record = rowToDocument(row);
732
+ if (record && stage.inMemoryFilters.every((predicate) => predicate(record))) {
733
+ docs.push(record);
734
+ if (typeof limit === "number" && docs.length >= limit) {
735
+ break;
736
+ }
737
+ }
738
+ }
739
+ return docs;
740
+ };
602
741
  const doWhereSqlStrategy = { fieldRef: jsonPathSql, serialize: serializeSqlValue };
603
742
  const makeRelationExistsSqlStrategy = (onRead) => {
604
743
  let aliasCounter = 0;
@@ -774,36 +913,10 @@ const buildReader = (sql$1, schema, tableName, onIndexUse = () => void 0) => {
774
913
  if (stage.search) {
775
914
  return runSearchFetch(limit);
776
915
  }
777
- const whereClauses = [];
778
- for (const condition of stage.sqlConditions) {
779
- whereClauses.push(sql`${jsonPathSql(condition.field)} ${sql.raw(condition.comparator)} ${serializeSqlValue(condition.value)}`);
780
- }
781
- if (scopeCondition) {
782
- whereClauses.push(scopeCondition);
916
+ if (stage.geo) {
917
+ return runGeoTerminal(sql$1, tableName, stage, scopeCondition, limit);
783
918
  }
784
- let query = sql`SELECT id, _creationTime, ${sql.identifier(DOC_COLUMN$1)} FROM ${sql.identifier(tableName)}`;
785
- if (whereClauses.length > 0) {
786
- query = sql`${query} WHERE ${sql.join(whereClauses, sql` AND `)}`;
787
- }
788
- query = sql`${query} ORDER BY ${buildOrderClause()}`;
789
- if (typeof limit === "number" && stage.inMemoryFilters.length === 0) {
790
- query = sql`${query} LIMIT ${sql.raw(String(Math.max(0, Math.floor(limit))))}`;
791
- }
792
- const rows = runDrizzle(sql$1, query).toArray();
793
- const docs = [];
794
- for (const row of rows) {
795
- const record = rowToDocument(row);
796
- if (!record) {
797
- continue;
798
- }
799
- if (stage.inMemoryFilters.every((predicate) => predicate(record))) {
800
- docs.push(record);
801
- if (typeof limit === "number" && docs.length >= limit) {
802
- break;
803
- }
804
- }
805
- }
806
- return docs;
919
+ return runPlainFetch(sql$1, tableName, stage, scopeCondition, buildOrderClause(), limit);
807
920
  };
808
921
  const reader = {
809
922
  // eslint-disable-next-line @typescript-eslint/require-await -- TableReaderLike returns Promises (the D1 twin awaits real I/O); the DO impl is synchronous over local SQLite
@@ -828,6 +941,9 @@ const buildReader = (sql$1, schema, tableName, onIndexUse = () => void 0) => {
828
941
  if (stage.search) {
829
942
  throw new LunoraError("INTERNAL", "pagination is not supported on search queries; use .take(n) or .collect()");
830
943
  }
944
+ if (stage.geo) {
945
+ throw new LunoraError("INTERNAL", "pagination is not supported on geo queries; use .take(n) or .collect()");
946
+ }
831
947
  return paginateStage(sql$1, tableName, stage, options, scopeCondition);
832
948
  },
833
949
  // eslint-disable-next-line @typescript-eslint/require-await -- TableReaderLike returns Promises (the D1 twin awaits real I/O); the DO impl is synchronous over local SQLite
@@ -842,6 +958,20 @@ const buildReader = (sql$1, schema, tableName, onIndexUse = () => void 0) => {
842
958
  }
843
959
  return rows[0] ?? null;
844
960
  },
961
+ withGeoIndex(indexName, build) {
962
+ const definition = (tableDefinition.geoIndexes ?? []).find((index) => index.name === indexName);
963
+ if (!definition) {
964
+ throw new LunoraError("INTERNAL", `unknown geo index "${indexName}" on table "${tableName}"`);
965
+ }
966
+ onIndexUse(tableName, indexName, "geo");
967
+ const geoStage = { definition, indexName };
968
+ stage.geo = geoStage;
969
+ build(createGeoBuilder(geoStage, tableName));
970
+ if (!geoStage.near && !geoStage.within) {
971
+ throw new LunoraError("INTERNAL", `geo index "${indexName}" on table "${tableName}" requires a .near(point, radius) or .within(box) call`);
972
+ }
973
+ return reader;
974
+ },
845
975
  withIndex(indexName, range) {
846
976
  const definition = tableDefinition.indexes.find((index) => index.name === indexName);
847
977
  if (!definition) {
@@ -1090,6 +1220,7 @@ const createShardCtxDb = (options) => {
1090
1220
  ensureRankBackfilledForTable,
1091
1221
  syncAggregates,
1092
1222
  syncCompanionsForInsert,
1223
+ syncGeo,
1093
1224
  syncRanks,
1094
1225
  syncSearch
1095
1226
  } = createCompanionSync({
@@ -1291,6 +1422,7 @@ const createShardCtxDb = (options) => {
1291
1422
  sql`UPDATE ${sql.identifier(tableName)} SET ${sql.identifier(DOC_COLUMN$1)} = ${JSON.stringify(merged)} WHERE id = ${id} AND ${sql.identifier(DOC_COLUMN$1)} = ${existingJson}`
1292
1423
  );
1293
1424
  syncSearch(tableName, id, merged);
1425
+ syncGeo(tableName, id, void 0);
1294
1426
  syncAggregates(tableName, existing, merged);
1295
1427
  syncRanks(tableName, id, existing, void 0);
1296
1428
  cache?.invalidate(tableName, id);
@@ -1308,6 +1440,7 @@ const createShardCtxDb = (options) => {
1308
1440
  sql`DELETE FROM ${sql.identifier(tableName)} WHERE id = ${id} AND ${sql.identifier(DOC_COLUMN$1)} = ${existingJson}`
1309
1441
  );
1310
1442
  syncSearch(tableName, id, void 0);
1443
+ syncGeo(tableName, id, void 0);
1311
1444
  syncAggregates(tableName, existing, void 0);
1312
1445
  syncRanks(tableName, id, existing, void 0);
1313
1446
  cache?.invalidate(tableName, id);
@@ -1682,6 +1815,7 @@ const createShardCtxDb = (options) => {
1682
1815
  sql`UPDATE ${sql.identifier(tableName)} SET ${sql.identifier(DOC_COLUMN$1)} = ${JSON.stringify(merged)} WHERE id = ${id} AND ${sql.identifier(DOC_COLUMN$1)} = ${existingJson}`
1683
1816
  );
1684
1817
  syncSearch(tableName, id, merged);
1818
+ syncGeo(tableName, id, merged);
1685
1819
  syncAggregates(tableName, existing, merged);
1686
1820
  syncRanks(tableName, id, existing, merged);
1687
1821
  cache?.invalidate(tableName, id);
@@ -1878,6 +2012,7 @@ const createShardCtxDb = (options) => {
1878
2012
  sql`UPDATE ${sql.identifier(tableName)} SET _creationTime = ${creationTime}, ${sql.identifier(DOC_COLUMN$1)} = ${JSON.stringify(replaced)} WHERE id = ${id} AND ${sql.identifier(DOC_COLUMN$1)} = ${existingJson}`
1879
2013
  );
1880
2014
  syncSearch(tableName, id, replaced);
2015
+ syncGeo(tableName, id, replaced);
1881
2016
  syncAggregates(tableName, previous, replaced);
1882
2017
  syncRanks(tableName, id, previous, replaced);
1883
2018
  cache?.invalidate(tableName, id);
@@ -22,9 +22,10 @@ import { redact, standardRules } from '@visulima/redact';
22
22
  import { i as isDevEnvironment, c as buildSettings, b as buildSecurityAudit } from './security-audit-CucgBice.mjs';
23
23
  import { runReadonlySql } from './MAX_SQL_ROWS-iFAA8FbD.mjs';
24
24
  import { ConflictError } from './ConflictError-CLoq37xH.mjs';
25
+ import { selectExpiredIds } from './selectExpiredIds-C5W29Upb.mjs';
25
26
  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';
26
27
  import { CDC_LOG_TABLE, readCdcChanges, readCdcCursor, readCdcEpoch, minCdcSeq, bumpCdcEpoch } from './CDC_LOG_TABLE-uwOJxJJZ.mjs';
27
- import { a as selectShapeMemberIds, s as selectShapeRows } from './ctx-db-shapes-Cz9dHyh1.mjs';
28
+ import { a as selectShapeMemberIds, s as selectShapeRows } from './ctx-db-shapes-DTeFiHYS.mjs';
28
29
 
29
30
  const MAX_BATCH_ENTRIES = 500;
30
31
 
@@ -1855,6 +1856,9 @@ const parseRunMigrationArgs = (args) => {
1855
1856
  };
1856
1857
  };
1857
1858
  const SHARD_BULK_DELETE_CAP = MAX_PAGE_SIZE;
1859
+ const TTL_SWEEP_BATCH = 200;
1860
+ const TTL_SWEEP_MAX_BATCHES = 20;
1861
+ const TTL_SWEEP_INTERVAL_MS = 3e4;
1858
1862
  const parseWriteRowArgs = (args) => {
1859
1863
  const { op } = args;
1860
1864
  const table = typeof args["table"] === "string" ? args["table"] : "";
@@ -2461,12 +2465,10 @@ class ShardDO {
2461
2465
  * reason; otherwise the earlier of the two candidate times (never later than
2462
2466
  * `nowMs`, so a source that's already due arms essentially immediately).
2463
2467
  */
2464
- static nextPollAlarmTarget(globalShapesRemaining, nextSourceDueAt, nowMs) {
2468
+ static nextPollAlarmTarget(globalShapesRemaining, nextSourceDueAt, nextTtlDueAt, nowMs) {
2465
2469
  const globalTarget = globalShapesRemaining > 0 ? nowMs + ShardDO.GLOBAL_SHAPE_POLL_INTERVAL_MS : void 0;
2466
- if (globalTarget === void 0) {
2467
- return nextSourceDueAt === void 0 ? void 0 : Math.max(nextSourceDueAt, nowMs);
2468
- }
2469
- return nextSourceDueAt === void 0 ? globalTarget : Math.min(globalTarget, nextSourceDueAt);
2470
+ const candidates = [globalTarget, nextSourceDueAt, nextTtlDueAt].filter((value) => value !== void 0).map((value) => Math.max(value, nowMs));
2471
+ return candidates.length > 0 ? Math.min(...candidates) : void 0;
2470
2472
  }
2471
2473
  state;
2472
2474
  env;
@@ -3147,8 +3149,15 @@ class ShardDO {
3147
3149
  this.recordShapeError("source:poll", error);
3148
3150
  nextSourceDueAt = Date.now() + ShardDO.GLOBAL_SHAPE_POLL_INTERVAL_MS;
3149
3151
  }
3152
+ let nextTtlDueAt;
3153
+ try {
3154
+ nextTtlDueAt = await this.pollTtlSweeps();
3155
+ } catch (error) {
3156
+ this.recordShapeError("ttl:sweep", error);
3157
+ nextTtlDueAt = Date.now() + ShardDO.GLOBAL_SHAPE_POLL_INTERVAL_MS;
3158
+ }
3150
3159
  await this.flushChangedTables();
3151
- const nextAlarmAt = ShardDO.nextPollAlarmTarget(globalShapesRemaining, nextSourceDueAt, Date.now());
3160
+ const nextAlarmAt = ShardDO.nextPollAlarmTarget(globalShapesRemaining, nextSourceDueAt, nextTtlDueAt, Date.now());
3152
3161
  if (nextAlarmAt !== void 0) {
3153
3162
  await this.scheduleGlobalPoll(nextAlarmAt);
3154
3163
  }
@@ -4337,6 +4346,60 @@ class ShardDO {
4337
4346
  scheduleSourcePoll() {
4338
4347
  return this.scheduleGlobalPoll();
4339
4348
  }
4349
+ /**
4350
+ * The resolved TTL policies (`.ttl(field, { after })`) for this DO's schema —
4351
+ * one {@link TtlSweepSpec} per table that declares a TTL. The base `ShardDO`
4352
+ * has no schema, so it returns `[]` and the TTL tier stays dormant. The
4353
+ * codegen subclass overrides it to read each table's `ttlPolicy` (+ its
4354
+ * `.softDelete()` marker) off the imported schema.
4355
+ */
4356
+ // eslint-disable-next-line class-methods-use-this -- base-class override hook: the codegen subclass reads the imported schema
4357
+ ttlSweeps() {
4358
+ return [];
4359
+ }
4360
+ /**
4361
+ * Sweep every `.ttl()` table once: page the rows past their expiry and remove
4362
+ * each THROUGH the schema-aware writer (`deleteRowThroughWriter`) so companions
4363
+ * / CDC / live subscriptions stay correct and a `.softDelete()` table soft-deletes
4364
+ * instead of physically removing the row. Work is bounded per tick
4365
+ * ({@link TTL_SWEEP_BATCH} × {@link TTL_SWEEP_MAX_BATCHES}) so a large backlog
4366
+ * drains across several alarms without stalling the shard.
4367
+ *
4368
+ * Returns the next-due timestamp (a coarse {@link TTL_SWEEP_INTERVAL_MS}
4369
+ * cadence, so freshly-written rows expire within a bounded window) while any
4370
+ * TTL table exists, or `undefined` when there are none — so a DO with no TTL
4371
+ * table never arms this tier.
4372
+ */
4373
+ async pollTtlSweeps() {
4374
+ const specs = this.ttlSweeps();
4375
+ if (specs.length === 0) {
4376
+ return void 0;
4377
+ }
4378
+ const sql = this.sql;
4379
+ const now = Date.now();
4380
+ for (const spec of specs) {
4381
+ let batches = 0;
4382
+ let hasMore = true;
4383
+ while (hasMore && batches < TTL_SWEEP_MAX_BATCHES) {
4384
+ const page = selectExpiredIds(sql, spec, now, TTL_SWEEP_BATCH);
4385
+ for (const id of page.ids) {
4386
+ await this.deleteRowThroughWriter(spec.table, id);
4387
+ }
4388
+ hasMore = page.hasMore;
4389
+ batches += 1;
4390
+ }
4391
+ }
4392
+ return now + TTL_SWEEP_INTERVAL_MS;
4393
+ }
4394
+ /**
4395
+ * Arm the shared poll alarm for the TTL sweep. Mirrors {@link scheduleSourcePoll};
4396
+ * the codegen subclass calls it once on construction when the schema declares a
4397
+ * `.ttl()` table so the sweep loop starts, after which {@link ShardDO.alarm}
4398
+ * re-arms itself. Idempotent; a no-op when the runtime exposes no `setAlarm`.
4399
+ */
4400
+ scheduleTtlSweep() {
4401
+ return this.scheduleGlobalPoll();
4402
+ }
4340
4403
  /** This DO's shard key (its DO name), or `__root__` for the single-DO default. The `tenantBy` mapper binds it into the source query. */
4341
4404
  currentShardKey() {
4342
4405
  return this.state.id?.name ?? ROOT_SHARD_NAME;
@@ -2,7 +2,7 @@ import { sql } from 'drizzle-orm';
2
2
  import { matchesStaticWhere } from './AGGREGATE_SQL_FUNCTION-CQsu2Xga.mjs';
3
3
  import { encodeAggregateKey, foldAggregateTally, aggregateTableName } from './aggregateTableName-CxNqY1Sl.mjs';
4
4
  import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
5
- import { D as DOC_COLUMN, r as rowToDocument, A as AGG_KEY, a as AGG_VALUE, b as AGG_COUNT } from './do-sql-BCHCWtrD.mjs';
5
+ import { D as DOC_COLUMN, r as rowToDocument, A as AGG_KEY, a as AGG_VALUE, b as AGG_COUNT } from './do-sql-CGAgiQUz.mjs';
6
6
  import { param } from './renderSql-D6eUcn2N.mjs';
7
7
  import { sortColumnName, matchesRankStaticWhere, encodePartitionKey, rankTableName } from './RANK_TIEBREAK-CXhdcA1o.mjs';
8
8
  import { s as serializeSqlValue } from './serialize-sql-BlRUoiQe.mjs';
@@ -1,6 +1,6 @@
1
1
  import { sql } from 'drizzle-orm';
2
2
  import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
3
- import { D as DOC_COLUMN, r as rowToDocument, j as jsonPathSql } from './do-sql-BCHCWtrD.mjs';
3
+ import { D as DOC_COLUMN, r as rowToDocument, j as jsonPathSql } from './do-sql-CGAgiQUz.mjs';
4
4
  import { compileWhereSql } from './compileWhereSql-DE6yfRcQ.mjs';
5
5
  import { s as serializeSqlValue } from './serialize-sql-BlRUoiQe.mjs';
6
6
 
@@ -2,6 +2,7 @@ import { sql } from 'drizzle-orm';
2
2
  import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
3
3
 
4
4
  const DOC_COLUMN = "__doc__";
5
+ const geoTableName = (table, indexName) => `${table}__geo_${indexName}`;
5
6
  const quoteIdentifier = (name) => `"${name.replaceAll('"', '""')}"`;
6
7
  const jsonPath = (field) => {
7
8
  if (field === "_id" || field === "id") {
@@ -84,4 +85,4 @@ const isFtsAvailable = (sql$1) => {
84
85
  return available;
85
86
  };
86
87
 
87
- export { AGG_KEY as A, DOC_COLUMN as D, AGG_VALUE as a, AGG_COUNT as b, createIndexSql as c, aggUpsertSql as d, qualifiedJsonPathSql as e, isFtsAvailable as i, jsonPathSql as j, quoteIdentifier as q, rowToDocument as r, tableColumns as t };
88
+ export { AGG_KEY as A, DOC_COLUMN as D, AGG_VALUE as a, AGG_COUNT as b, createIndexSql as c, aggUpsertSql as d, qualifiedJsonPathSql as e, geoTableName as g, isFtsAvailable as i, jsonPathSql as j, quoteIdentifier as q, rowToDocument as r, tableColumns as t };
@@ -1,7 +1,7 @@
1
1
  import { LunoraError } from '@lunora/errors';
2
2
  import { sql } from 'drizzle-orm';
3
3
  import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
4
- import { runExternalSourceTick, materializeExternalRowsIncremental } from './materializeExternalRows-DlQWMlw_.mjs';
4
+ import { runExternalSourceTick, materializeExternalRowsIncremental } from './materializeExternalRows-BtEGs1Fv.mjs';
5
5
 
6
6
  const SOURCE_CURSOR_TABLE = "__lunora_source_cursor";
7
7
  const serializeCursor = (value) => {
@@ -1,5 +1,5 @@
1
1
  import { applyCdcChanges } from './CDC_LOG_TABLE-uwOJxJJZ.mjs';
2
- import { s as selectShapeRows } from './ctx-db-shapes-Cz9dHyh1.mjs';
2
+ import { s as selectShapeRows } from './ctx-db-shapes-DTeFiHYS.mjs';
3
3
  import { diffExternalSource, projectExternalSourceRow } from './diffExternalSource-CovHfdyo.mjs';
4
4
  import { stableStringify } from './stableStringify-mC40mZts.mjs';
5
5
 
@@ -3,7 +3,7 @@ import { aggregateTableName } from './aggregateTableName-CxNqY1Sl.mjs';
3
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
- 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';
6
+ import { D as DOC_COLUMN, j as jsonPathSql, c as createIndexSql, t as tableColumns, i as isFtsAvailable, A as AGG_KEY, a as AGG_VALUE, b as AGG_COUNT, g as geoTableName } from './do-sql-CGAgiQUz.mjs';
7
7
  import { sortColumnName, rankTableName } from './RANK_TIEBREAK-CXhdcA1o.mjs';
8
8
  import { ftsTableName } from './buildFtsMatch-BLEMawrp.mjs';
9
9
 
@@ -36,6 +36,20 @@ const migrateSearchIndexes = (sql$1, tableName, definition) => {
36
36
  );
37
37
  }
38
38
  };
39
+ const migrateGeoIndexes = (sql$1, tableName, definition) => {
40
+ if (!definition.geoIndexes) {
41
+ return;
42
+ }
43
+ for (const index of definition.geoIndexes) {
44
+ const geoTable = geoTableName(tableName, index.name);
45
+ runDrizzle(
46
+ sql$1,
47
+ sql`CREATE TABLE IF NOT EXISTS ${sql.identifier(geoTable)} (${sql.identifier("__id__")} TEXT PRIMARY KEY, ${sql.identifier("__geohash__")} TEXT NOT NULL, ${sql.identifier("__lat__")} REAL NOT NULL, ${sql.identifier("__lng__")} REAL NOT NULL)`
48
+ );
49
+ const btreeName = `${tableName}__geo_${index.name}__btree`;
50
+ runDrizzle(sql$1, createIndexSql(btreeName, geoTable, sql`${sql.identifier("__geohash__")} ASC, ${sql.identifier("__id__")} ASC`, false));
51
+ }
52
+ };
39
53
  const migrateAggregateIndexes = (sql$1, tableName, definition) => {
40
54
  if (!definition.aggregateIndexes) {
41
55
  return;
@@ -90,6 +104,7 @@ const runShardMigrations = (sql$1, schema, options = {}) => {
90
104
  );
91
105
  migrateSecondaryIndexes(sql$1, tableName, definition);
92
106
  migrateSearchIndexes(sql$1, tableName, definition);
107
+ migrateGeoIndexes(sql$1, tableName, definition);
93
108
  migrateAggregateIndexes(sql$1, tableName, definition);
94
109
  migrateRankIndexes(sql$1, tableName, definition);
95
110
  }
@@ -0,0 +1,18 @@
1
+ import { sql } from 'drizzle-orm';
2
+ import { r as runDrizzle } from './do-exec-5eQy5cEi.mjs';
3
+ import { j as jsonPathSql } from './do-sql-CGAgiQUz.mjs';
4
+
5
+ const selectExpiredIds = (sql$1, spec, now, limit) => {
6
+ const cutoff = now - (spec.after ?? 0);
7
+ const conditions = [sql`${jsonPathSql(spec.field)} IS NOT NULL`, sql`${jsonPathSql(spec.field)} < ${cutoff}`];
8
+ if (spec.softDeleteField !== void 0) {
9
+ conditions.push(sql`${jsonPathSql(spec.softDeleteField)} IS NULL`);
10
+ }
11
+ const query = sql`SELECT id FROM ${sql.identifier(spec.table)} WHERE ${sql.join(conditions, sql` AND `)} LIMIT ${sql.raw(String(Math.max(0, Math.floor(limit)) + 1))}`;
12
+ const rows = runDrizzle(sql$1, query).toArray();
13
+ const hasMore = rows.length > limit;
14
+ const ids = rows.slice(0, limit).map((row) => row.id);
15
+ return { hasMore, ids };
16
+ };
17
+
18
+ export { selectExpiredIds };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/do",
3
- "version": "1.0.0-alpha.37",
3
+ "version": "1.0.0-alpha.38",
4
4
  "description": "Lunora Durable Objects: ShardDO (SQLite, OCC, hibernated WebSocket subscriptions) and SessionDO",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -46,7 +46,7 @@
46
46
  "access": "public"
47
47
  },
48
48
  "dependencies": {
49
- "@lunora/errors": "1.0.0-alpha.6",
49
+ "@lunora/errors": "1.0.0-alpha.7",
50
50
  "@lunora/fingerprint": "1.0.0-alpha.3",
51
51
  "@visulima/redact": "3.0.0",
52
52
  "drizzle-orm": "^0.45.2"