@lunora/server 1.0.0-alpha.55 → 1.0.0-alpha.56

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
@@ -10,19 +10,21 @@ import { b as Permission, R as Role, T as TypedDefinePolicyInput, a as Policy, D
10
10
  export type { d as PolicyContext, e as PolicyDecision, f as PolicyDecisionOf, P as PolicyOperation } from "./packem_shared/types.d-C4CMJK8x.mjs";
11
11
  export { type CronJob, type CronJobsBuilder, type CronScheduleKind, type DailySchedule, type IntervalSchedule, type MonthlySchedule, type WeeklySchedule, cronJobs } from '@lunora/scheduler';
12
12
  /**
13
- * Make any `config.storage` result bucket-aware so `ctx.storage.bucket(name)`
13
+ * Make any resolved storage capability bucket-aware so `ctx.storage.bucket(name)`
14
14
  * always resolves. A `createBucketStorage(...)` result already carries
15
15
  * `.bucket` / `.bucketName` and is returned as-is; a single `createStorage(...)`
16
16
  * (or the no-storage stub) is tagged as the `"default"` bucket, where
17
17
  * `.bucket(name)` is the identity — single-bucket apps address one binding under
18
18
  * every name.
19
19
  *
20
- * This is the runtime counterpart the generated `_generated/shard.ts` imports to
21
- * wrap `ctx.storage`; it lives here (the single source) rather than being stamped
22
- * inline into every generated file, so the bucket-tagging behaviour has one home
23
- * alongside the storage ctx types. The input is genuinely heterogeneous (a thunk
24
- * result cast through `unknown`), so the signature is `unknown unknown`; the
25
- * generated caller casts the result to its storage type.
20
+ * Lives here rather than in `@lunora/server` because two packages need it and
21
+ * neither may depend on the other: `@lunora/server` re-exports it as the runtime
22
+ * counterpart `_generated/shard.ts` imports, and `@lunora/runtime` uses it to
23
+ * build `ctx.storage` for an HTTP action from the worker's own R2 bindings.
24
+ * Inlined into each `dist` by the bundler, so no dependency edge is created.
25
+ *
26
+ * The input is genuinely heterogeneous (a thunk result cast through `unknown`),
27
+ * so the signature is `unknown → unknown`; callers cast the result.
26
28
  */
27
29
  declare const asBucketStorage: (raw: unknown) => unknown;
28
30
  /** Builder discriminator. Codegen reads this kind. */
@@ -583,18 +585,29 @@ type HttpMethod = "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PU
583
585
  /**
584
586
  * Context handed to an HTTP action handler. A narrower view of {@link ActionContext}:
585
587
  * HTTP actions run in the worker (the "action runtime"), separate from the
586
- * transactional store, so there is no direct `db` / `vectors` / `storage`
587
- * surface — reach the data layer through `runQuery` / `runMutation` /
588
- * `runAction`, which forward to the owning shard.
588
+ * transactional store, so there is no direct `db` / `vectors` surface — reach the
589
+ * data layer through `runQuery` / `runMutation` / `runAction`, which forward to
590
+ * the owning shard. `db`'s absence is principled: an HTTP handler is not
591
+ * transactional.
592
+ *
593
+ * `scheduler` and `storage` ARE present, because neither needs the shard — the
594
+ * scheduler talks to the scheduler DO, and R2 is a worker binding an HTTP
595
+ * handler can reach where an action does. Both are optional: each exists only
596
+ * when the app declared the matching capability (`.scheduler(...)` /
597
+ * `.storage(...)`) on the generated app builder.
589
598
  *
590
- * `scheduler` IS present (it talks to the scheduler DO, not the shard) but is
591
- * optional: it exists only when the app declared `.scheduler(...)` on the
592
- * generated app builder. "Receive webhook enqueue the real work return 200"
593
- * is what HTTP actions are for, so omitting it forced every app to hand-roll a
594
- * hop through a mutation plus a closed allow-list of target strings.
599
+ * Omitting them was costly out of proportion to the gap. Without `scheduler`,
600
+ * "receive webhook enqueue the real work return 200" — the shape HTTP
601
+ * actions exist for forced a hop through a mutation plus a closed allow-list
602
+ * of target strings, because a function reference cannot cross the RPC boundary
603
+ * and a free-form target on an unauthenticated endpoint is a "call any internal
604
+ * function" primitive. Without `storage`, any helper the ctx was threaded into
605
+ * had to be typed for its storage-touching branch, so a handler was barred from
606
+ * the helper even on the branches that never went near storage.
595
607
  */
596
608
  type HttpActionCtx = Pick<ActionCtx, "auth" | "cache" | "fetch" | "runAction" | "runMutation" | "runQuery"> & {
597
609
  readonly scheduler?: ActionCtx["scheduler"];
610
+ readonly storage?: ActionCtx["storage"];
598
611
  };
599
612
  /** A raw handler wrapped by {@link httpAction}. Receives the raw request, returns the raw response. */
600
613
  type HttpActionHandler = (context: HttpActionCtx, request: Request) => Promise<Response> | Response;
@@ -1166,13 +1179,47 @@ interface MaskContextIn {
1166
1179
  declare const mask: <Context extends MaskContextIn = MaskContextIn>(policies: MaskPolicies<Context>, options?: MaskOptions<Context>) => Middleware<Context, Context>;
1167
1180
  /** A document handed to a migration transform: the stored row including `_id`/`_creationTime`. */
1168
1181
  type MigrationDocument = Record<string, unknown>;
1182
+ /**
1183
+ * The read surface a transform reaches through its `ctx`.
1184
+ *
1185
+ * Read-only by design: the runner accounts for exactly one rewrite per row read,
1186
+ * and a transform writing directly would make that count describe something
1187
+ * other than what happened. Scoped to the shard the runner is walking.
1188
+ */
1189
+ interface MigrationReader {
1190
+ count: (table: string, where?: Record<string, unknown>) => Promise<number>;
1191
+ findFirst: (table: string, args?: Record<string, unknown>) => Promise<MigrationDocument | null>;
1192
+ findMany: (table: string, args?: Record<string, unknown>) => Promise<{
1193
+ isDone: boolean;
1194
+ page: MigrationDocument[];
1195
+ }>;
1196
+ get: (id: string, expectedTable?: string) => Promise<MigrationDocument | null>;
1197
+ }
1198
+ /** The context handed to a transform alongside the row. */
1199
+ interface MigrationCtx {
1200
+ db: MigrationReader;
1201
+ }
1169
1202
  /**
1170
1203
  * Transform applied to one document. Return a new document to rewrite the row,
1171
1204
  * or `undefined` to leave it untouched (skipped, not counted as changed). The
1172
1205
  * runner always preserves the original `_id` and `_creationTime`, so the
1173
1206
  * returned document neither needs to nor should change row identity.
1207
+ *
1208
+ * The second parameter carries a shard-scoped reader. Without it a transform
1209
+ * could only rewrite the row it was handed — enough for a backfill whose new
1210
+ * value is a pure function of the old row (`displayName = name ?? "Anonymous"`),
1211
+ * but not for the shape people actually write: read the parent, copy a field
1212
+ * down onto its children.
1213
+ *
1214
+ * May return a promise, since a cross-table read is asynchronous.
1215
+ *
1216
+ * **A shard key cannot be backfilled this way, even with a reader.** A row whose
1217
+ * shard-key field is unset does not belong to any shard, so a shard-scoped query
1218
+ * will not enumerate it; and writing the key would have to MOVE the row to a
1219
+ * different Durable Object, which a per-shard runner cannot do. Re-keying is an
1220
+ * export → transform → import, not a migration.
1174
1221
  */
1175
- type MigrationTransform = (document: MigrationDocument) => MigrationDocument | undefined | void;
1222
+ type MigrationTransform = (document: MigrationDocument, ctx: MigrationCtx) => MigrationDocument | Promise<MigrationDocument | undefined | void> | undefined | void;
1176
1223
  interface MigrationDefinition {
1177
1224
  /** Rows fetched and rewritten per batch. Defaults to the runner's batch size when omitted. */
1178
1225
  readonly batchSize?: number;
@@ -2455,4 +2502,4 @@ interface StorageContextIn {
2455
2502
  }
2456
2503
  declare const storageRules: <Context extends StorageContextIn = StorageContextIn>(rules: ReadonlyArray<StorageRule<Context>>, options?: StorageRulesOptions) => Middleware<Context, Context>;
2457
2504
  declare const VERSION = "0.0.0";
2458
- export { type ActionBuilder, type ActionCtx, type AggregateIndexDefinition, type AggregateIndexOptions, type AggregateOp, type ArgsValidator, type Component, type ComponentFunctions, type CreateOptions, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, type DataModelInit, type DefineComponentOptions, type DefineIdentityOptions, type DefineListArgsConfig, type DefinePluginOptions, type DefinePolicyInput, type DefinePresenceOptions, type DefineStorageRuleInput, type DurableObjectJurisdiction, type EmptyArgs, type EnvAccessor, type EnvKeyFailure, type EnvShape, type ExposeConfig, type ExtendableSchema, type FacadeEntry, type FacadeWriterLike, type FunctionKind, type HttpActionCtx, type HttpActionHandler, type HttpMethod, type HttpRoute, type HttpRouteBuilder, type HttpRouteFactory, type HttpRouteHandlerOptions, type HttpStreamHandlerOptions, type IdentityContract, type IdentityRejectMode, type IdentityValidation, type InferArgs, type InferEnv, type InferIdentity, type InlineAggregateIndexOptions, type InlineRankIndexOptions, type InternalActionBuilder, type InternalMutationBuilder, type InternalQueryBuilder, type LifecycleEvent, type LifecycleHandler, type ListArgsSpec, type ListArgsValidators, type ListArgsValue, type ListFilterOperators, type ListOrderByEntry, type ListWhere, type LunoraBuilders, LunoraEnvError, LunoraError, type LunoraHttpApp, type LunoraHttpEnv, type LunoraRouteHandler, type ManyRelation, type MaskColumns, type MaskContext, type MaskFn, type MaskOptions, type MaskPolicies, type MaskStrategy, type Middleware, type MiddlewareNext, type MigrationDefinition, type MigrationDocument, type MigrationTransform, type MutationBuilder, type MutationCtx, type MutatorDefinition, type OnDeleteAction, type OneRelation, type OrmLike, DEFAULT_TTL_MS as PRESENCE_DEFAULT_TTL_MS, PRESENCE_TABLE, type Permission, type Plugin, type Policy, type PrefixedTables, type PresenceComponent, type PresenceFunctions, type PresenceMember, type ProtectPublicOptions, type QueryBuilder, type QueryCtx, type RankIndexDefinition, type RankIndexOptions, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMigration, type RegisteredMutation, type RegisteredMutator, type RegisteredQuery, type RegisteredShape, type RegisteredStream, type RelationBuilder, type RelationDefinition, type RlsOptions, type RlsReadRegistry, type Role, type Schema, type SchemaExtension, type ShapeDefinition, type ShapeReadWhereRequest, type StorageOperation, type StorageRule, type StorageRuleContext, type StorageRuleDecision, type StorageRulesOptions, type TableBuilder, type TableDefinition, type TerminalKind, type TriggerBuilder, type TriggerDefinition, type TypedDefinePolicyInput, VERSION, type VectorEmbedder, type VectorIndexDefinition, type VectorIndexOptions, type VectorMetric, type VectorizeOptions, type WhereInput, allowAll, asBucketStorage, bindOrm, bindTableFacade, buildRlsReadRegistry, clampLimit, composePluginMiddleware, composeShapeReadWhere, createPolicyDsl, createSecrets, defineAggregateIndex, defineComponent, defineEnv, defineIdentity, defineListArgs, defineMigration, defineMutator, definePermission, definePlugin, definePolicies, definePolicy, definePresence, defineRankIndex, defineRole, defineSchema, defineSchemaExtension, defineShape, defineStorageRule, defineStorageRules, defineTable, defineVectorIndex, deny, httpAction, httpRoute, httpRouter, initLunora, installPlugins, isDeny, isSafeHeaderValue, mask, mergeSchemaExtension, onConnect, onDisconnect, presenceExtension, protectPublic, redactSecrets, rls, serveStorageObject, storageRules, toWhereInput };
2505
+ export { type ActionBuilder, type ActionCtx, type AggregateIndexDefinition, type AggregateIndexOptions, type AggregateOp, type ArgsValidator, type Component, type ComponentFunctions, type CreateOptions, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, type DataModelInit, type DefineComponentOptions, type DefineIdentityOptions, type DefineListArgsConfig, type DefinePluginOptions, type DefinePolicyInput, type DefinePresenceOptions, type DefineStorageRuleInput, type DurableObjectJurisdiction, type EmptyArgs, type EnvAccessor, type EnvKeyFailure, type EnvShape, type ExposeConfig, type ExtendableSchema, type FacadeEntry, type FacadeWriterLike, type FunctionKind, type HttpActionCtx, type HttpActionHandler, type HttpMethod, type HttpRoute, type HttpRouteBuilder, type HttpRouteFactory, type HttpRouteHandlerOptions, type HttpStreamHandlerOptions, type IdentityContract, type IdentityRejectMode, type IdentityValidation, type InferArgs, type InferEnv, type InferIdentity, type InlineAggregateIndexOptions, type InlineRankIndexOptions, type InternalActionBuilder, type InternalMutationBuilder, type InternalQueryBuilder, type LifecycleEvent, type LifecycleHandler, type ListArgsSpec, type ListArgsValidators, type ListArgsValue, type ListFilterOperators, type ListOrderByEntry, type ListWhere, type LunoraBuilders, LunoraEnvError, LunoraError, type LunoraHttpApp, type LunoraHttpEnv, type LunoraRouteHandler, type ManyRelation, type MaskColumns, type MaskContext, type MaskFn, type MaskOptions, type MaskPolicies, type MaskStrategy, type Middleware, type MiddlewareNext, type MigrationCtx, type MigrationDefinition, type MigrationDocument, type MigrationReader, type MigrationTransform, type MutationBuilder, type MutationCtx, type MutatorDefinition, type OnDeleteAction, type OneRelation, type OrmLike, DEFAULT_TTL_MS as PRESENCE_DEFAULT_TTL_MS, PRESENCE_TABLE, type Permission, type Plugin, type Policy, type PrefixedTables, type PresenceComponent, type PresenceFunctions, type PresenceMember, type ProtectPublicOptions, type QueryBuilder, type QueryCtx, type RankIndexDefinition, type RankIndexOptions, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMigration, type RegisteredMutation, type RegisteredMutator, type RegisteredQuery, type RegisteredShape, type RegisteredStream, type RelationBuilder, type RelationDefinition, type RlsOptions, type RlsReadRegistry, type Role, type Schema, type SchemaExtension, type ShapeDefinition, type ShapeReadWhereRequest, type StorageOperation, type StorageRule, type StorageRuleContext, type StorageRuleDecision, type StorageRulesOptions, type TableBuilder, type TableDefinition, type TerminalKind, type TriggerBuilder, type TriggerDefinition, type TypedDefinePolicyInput, VERSION, type VectorEmbedder, type VectorIndexDefinition, type VectorIndexOptions, type VectorMetric, type VectorizeOptions, type WhereInput, allowAll, asBucketStorage, bindOrm, bindTableFacade, buildRlsReadRegistry, clampLimit, composePluginMiddleware, composeShapeReadWhere, createPolicyDsl, createSecrets, defineAggregateIndex, defineComponent, defineEnv, defineIdentity, defineListArgs, defineMigration, defineMutator, definePermission, definePlugin, definePolicies, definePolicy, definePresence, defineRankIndex, defineRole, defineSchema, defineSchemaExtension, defineShape, defineStorageRule, defineStorageRules, defineTable, defineVectorIndex, deny, httpAction, httpRoute, httpRouter, initLunora, installPlugins, isDeny, isSafeHeaderValue, mask, mergeSchemaExtension, onConnect, onDisconnect, presenceExtension, protectPublic, redactSecrets, rls, serveStorageObject, storageRules, toWhereInput };
package/dist/index.d.ts CHANGED
@@ -10,19 +10,21 @@ import { b as Permission, R as Role, T as TypedDefinePolicyInput, a as Policy, D
10
10
  export type { d as PolicyContext, e as PolicyDecision, f as PolicyDecisionOf, P as PolicyOperation } from "./packem_shared/types.d-DdYF8E18.js";
11
11
  export { type CronJob, type CronJobsBuilder, type CronScheduleKind, type DailySchedule, type IntervalSchedule, type MonthlySchedule, type WeeklySchedule, cronJobs } from '@lunora/scheduler';
12
12
  /**
13
- * Make any `config.storage` result bucket-aware so `ctx.storage.bucket(name)`
13
+ * Make any resolved storage capability bucket-aware so `ctx.storage.bucket(name)`
14
14
  * always resolves. A `createBucketStorage(...)` result already carries
15
15
  * `.bucket` / `.bucketName` and is returned as-is; a single `createStorage(...)`
16
16
  * (or the no-storage stub) is tagged as the `"default"` bucket, where
17
17
  * `.bucket(name)` is the identity — single-bucket apps address one binding under
18
18
  * every name.
19
19
  *
20
- * This is the runtime counterpart the generated `_generated/shard.ts` imports to
21
- * wrap `ctx.storage`; it lives here (the single source) rather than being stamped
22
- * inline into every generated file, so the bucket-tagging behaviour has one home
23
- * alongside the storage ctx types. The input is genuinely heterogeneous (a thunk
24
- * result cast through `unknown`), so the signature is `unknown unknown`; the
25
- * generated caller casts the result to its storage type.
20
+ * Lives here rather than in `@lunora/server` because two packages need it and
21
+ * neither may depend on the other: `@lunora/server` re-exports it as the runtime
22
+ * counterpart `_generated/shard.ts` imports, and `@lunora/runtime` uses it to
23
+ * build `ctx.storage` for an HTTP action from the worker's own R2 bindings.
24
+ * Inlined into each `dist` by the bundler, so no dependency edge is created.
25
+ *
26
+ * The input is genuinely heterogeneous (a thunk result cast through `unknown`),
27
+ * so the signature is `unknown → unknown`; callers cast the result.
26
28
  */
27
29
  declare const asBucketStorage: (raw: unknown) => unknown;
28
30
  /** Builder discriminator. Codegen reads this kind. */
@@ -583,18 +585,29 @@ type HttpMethod = "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PU
583
585
  /**
584
586
  * Context handed to an HTTP action handler. A narrower view of {@link ActionContext}:
585
587
  * HTTP actions run in the worker (the "action runtime"), separate from the
586
- * transactional store, so there is no direct `db` / `vectors` / `storage`
587
- * surface — reach the data layer through `runQuery` / `runMutation` /
588
- * `runAction`, which forward to the owning shard.
588
+ * transactional store, so there is no direct `db` / `vectors` surface — reach the
589
+ * data layer through `runQuery` / `runMutation` / `runAction`, which forward to
590
+ * the owning shard. `db`'s absence is principled: an HTTP handler is not
591
+ * transactional.
592
+ *
593
+ * `scheduler` and `storage` ARE present, because neither needs the shard — the
594
+ * scheduler talks to the scheduler DO, and R2 is a worker binding an HTTP
595
+ * handler can reach where an action does. Both are optional: each exists only
596
+ * when the app declared the matching capability (`.scheduler(...)` /
597
+ * `.storage(...)`) on the generated app builder.
589
598
  *
590
- * `scheduler` IS present (it talks to the scheduler DO, not the shard) but is
591
- * optional: it exists only when the app declared `.scheduler(...)` on the
592
- * generated app builder. "Receive webhook enqueue the real work return 200"
593
- * is what HTTP actions are for, so omitting it forced every app to hand-roll a
594
- * hop through a mutation plus a closed allow-list of target strings.
599
+ * Omitting them was costly out of proportion to the gap. Without `scheduler`,
600
+ * "receive webhook enqueue the real work return 200" — the shape HTTP
601
+ * actions exist for forced a hop through a mutation plus a closed allow-list
602
+ * of target strings, because a function reference cannot cross the RPC boundary
603
+ * and a free-form target on an unauthenticated endpoint is a "call any internal
604
+ * function" primitive. Without `storage`, any helper the ctx was threaded into
605
+ * had to be typed for its storage-touching branch, so a handler was barred from
606
+ * the helper even on the branches that never went near storage.
595
607
  */
596
608
  type HttpActionCtx = Pick<ActionCtx, "auth" | "cache" | "fetch" | "runAction" | "runMutation" | "runQuery"> & {
597
609
  readonly scheduler?: ActionCtx["scheduler"];
610
+ readonly storage?: ActionCtx["storage"];
598
611
  };
599
612
  /** A raw handler wrapped by {@link httpAction}. Receives the raw request, returns the raw response. */
600
613
  type HttpActionHandler = (context: HttpActionCtx, request: Request) => Promise<Response> | Response;
@@ -1166,13 +1179,47 @@ interface MaskContextIn {
1166
1179
  declare const mask: <Context extends MaskContextIn = MaskContextIn>(policies: MaskPolicies<Context>, options?: MaskOptions<Context>) => Middleware<Context, Context>;
1167
1180
  /** A document handed to a migration transform: the stored row including `_id`/`_creationTime`. */
1168
1181
  type MigrationDocument = Record<string, unknown>;
1182
+ /**
1183
+ * The read surface a transform reaches through its `ctx`.
1184
+ *
1185
+ * Read-only by design: the runner accounts for exactly one rewrite per row read,
1186
+ * and a transform writing directly would make that count describe something
1187
+ * other than what happened. Scoped to the shard the runner is walking.
1188
+ */
1189
+ interface MigrationReader {
1190
+ count: (table: string, where?: Record<string, unknown>) => Promise<number>;
1191
+ findFirst: (table: string, args?: Record<string, unknown>) => Promise<MigrationDocument | null>;
1192
+ findMany: (table: string, args?: Record<string, unknown>) => Promise<{
1193
+ isDone: boolean;
1194
+ page: MigrationDocument[];
1195
+ }>;
1196
+ get: (id: string, expectedTable?: string) => Promise<MigrationDocument | null>;
1197
+ }
1198
+ /** The context handed to a transform alongside the row. */
1199
+ interface MigrationCtx {
1200
+ db: MigrationReader;
1201
+ }
1169
1202
  /**
1170
1203
  * Transform applied to one document. Return a new document to rewrite the row,
1171
1204
  * or `undefined` to leave it untouched (skipped, not counted as changed). The
1172
1205
  * runner always preserves the original `_id` and `_creationTime`, so the
1173
1206
  * returned document neither needs to nor should change row identity.
1207
+ *
1208
+ * The second parameter carries a shard-scoped reader. Without it a transform
1209
+ * could only rewrite the row it was handed — enough for a backfill whose new
1210
+ * value is a pure function of the old row (`displayName = name ?? "Anonymous"`),
1211
+ * but not for the shape people actually write: read the parent, copy a field
1212
+ * down onto its children.
1213
+ *
1214
+ * May return a promise, since a cross-table read is asynchronous.
1215
+ *
1216
+ * **A shard key cannot be backfilled this way, even with a reader.** A row whose
1217
+ * shard-key field is unset does not belong to any shard, so a shard-scoped query
1218
+ * will not enumerate it; and writing the key would have to MOVE the row to a
1219
+ * different Durable Object, which a per-shard runner cannot do. Re-keying is an
1220
+ * export → transform → import, not a migration.
1174
1221
  */
1175
- type MigrationTransform = (document: MigrationDocument) => MigrationDocument | undefined | void;
1222
+ type MigrationTransform = (document: MigrationDocument, ctx: MigrationCtx) => MigrationDocument | Promise<MigrationDocument | undefined | void> | undefined | void;
1176
1223
  interface MigrationDefinition {
1177
1224
  /** Rows fetched and rewritten per batch. Defaults to the runner's batch size when omitted. */
1178
1225
  readonly batchSize?: number;
@@ -2455,4 +2502,4 @@ interface StorageContextIn {
2455
2502
  }
2456
2503
  declare const storageRules: <Context extends StorageContextIn = StorageContextIn>(rules: ReadonlyArray<StorageRule<Context>>, options?: StorageRulesOptions) => Middleware<Context, Context>;
2457
2504
  declare const VERSION = "0.0.0";
2458
- export { type ActionBuilder, type ActionCtx, type AggregateIndexDefinition, type AggregateIndexOptions, type AggregateOp, type ArgsValidator, type Component, type ComponentFunctions, type CreateOptions, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, type DataModelInit, type DefineComponentOptions, type DefineIdentityOptions, type DefineListArgsConfig, type DefinePluginOptions, type DefinePolicyInput, type DefinePresenceOptions, type DefineStorageRuleInput, type DurableObjectJurisdiction, type EmptyArgs, type EnvAccessor, type EnvKeyFailure, type EnvShape, type ExposeConfig, type ExtendableSchema, type FacadeEntry, type FacadeWriterLike, type FunctionKind, type HttpActionCtx, type HttpActionHandler, type HttpMethod, type HttpRoute, type HttpRouteBuilder, type HttpRouteFactory, type HttpRouteHandlerOptions, type HttpStreamHandlerOptions, type IdentityContract, type IdentityRejectMode, type IdentityValidation, type InferArgs, type InferEnv, type InferIdentity, type InlineAggregateIndexOptions, type InlineRankIndexOptions, type InternalActionBuilder, type InternalMutationBuilder, type InternalQueryBuilder, type LifecycleEvent, type LifecycleHandler, type ListArgsSpec, type ListArgsValidators, type ListArgsValue, type ListFilterOperators, type ListOrderByEntry, type ListWhere, type LunoraBuilders, LunoraEnvError, LunoraError, type LunoraHttpApp, type LunoraHttpEnv, type LunoraRouteHandler, type ManyRelation, type MaskColumns, type MaskContext, type MaskFn, type MaskOptions, type MaskPolicies, type MaskStrategy, type Middleware, type MiddlewareNext, type MigrationDefinition, type MigrationDocument, type MigrationTransform, type MutationBuilder, type MutationCtx, type MutatorDefinition, type OnDeleteAction, type OneRelation, type OrmLike, DEFAULT_TTL_MS as PRESENCE_DEFAULT_TTL_MS, PRESENCE_TABLE, type Permission, type Plugin, type Policy, type PrefixedTables, type PresenceComponent, type PresenceFunctions, type PresenceMember, type ProtectPublicOptions, type QueryBuilder, type QueryCtx, type RankIndexDefinition, type RankIndexOptions, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMigration, type RegisteredMutation, type RegisteredMutator, type RegisteredQuery, type RegisteredShape, type RegisteredStream, type RelationBuilder, type RelationDefinition, type RlsOptions, type RlsReadRegistry, type Role, type Schema, type SchemaExtension, type ShapeDefinition, type ShapeReadWhereRequest, type StorageOperation, type StorageRule, type StorageRuleContext, type StorageRuleDecision, type StorageRulesOptions, type TableBuilder, type TableDefinition, type TerminalKind, type TriggerBuilder, type TriggerDefinition, type TypedDefinePolicyInput, VERSION, type VectorEmbedder, type VectorIndexDefinition, type VectorIndexOptions, type VectorMetric, type VectorizeOptions, type WhereInput, allowAll, asBucketStorage, bindOrm, bindTableFacade, buildRlsReadRegistry, clampLimit, composePluginMiddleware, composeShapeReadWhere, createPolicyDsl, createSecrets, defineAggregateIndex, defineComponent, defineEnv, defineIdentity, defineListArgs, defineMigration, defineMutator, definePermission, definePlugin, definePolicies, definePolicy, definePresence, defineRankIndex, defineRole, defineSchema, defineSchemaExtension, defineShape, defineStorageRule, defineStorageRules, defineTable, defineVectorIndex, deny, httpAction, httpRoute, httpRouter, initLunora, installPlugins, isDeny, isSafeHeaderValue, mask, mergeSchemaExtension, onConnect, onDisconnect, presenceExtension, protectPublic, redactSecrets, rls, serveStorageObject, storageRules, toWhereInput };
2505
+ export { type ActionBuilder, type ActionCtx, type AggregateIndexDefinition, type AggregateIndexOptions, type AggregateOp, type ArgsValidator, type Component, type ComponentFunctions, type CreateOptions, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, type DataModelInit, type DefineComponentOptions, type DefineIdentityOptions, type DefineListArgsConfig, type DefinePluginOptions, type DefinePolicyInput, type DefinePresenceOptions, type DefineStorageRuleInput, type DurableObjectJurisdiction, type EmptyArgs, type EnvAccessor, type EnvKeyFailure, type EnvShape, type ExposeConfig, type ExtendableSchema, type FacadeEntry, type FacadeWriterLike, type FunctionKind, type HttpActionCtx, type HttpActionHandler, type HttpMethod, type HttpRoute, type HttpRouteBuilder, type HttpRouteFactory, type HttpRouteHandlerOptions, type HttpStreamHandlerOptions, type IdentityContract, type IdentityRejectMode, type IdentityValidation, type InferArgs, type InferEnv, type InferIdentity, type InlineAggregateIndexOptions, type InlineRankIndexOptions, type InternalActionBuilder, type InternalMutationBuilder, type InternalQueryBuilder, type LifecycleEvent, type LifecycleHandler, type ListArgsSpec, type ListArgsValidators, type ListArgsValue, type ListFilterOperators, type ListOrderByEntry, type ListWhere, type LunoraBuilders, LunoraEnvError, LunoraError, type LunoraHttpApp, type LunoraHttpEnv, type LunoraRouteHandler, type ManyRelation, type MaskColumns, type MaskContext, type MaskFn, type MaskOptions, type MaskPolicies, type MaskStrategy, type Middleware, type MiddlewareNext, type MigrationCtx, type MigrationDefinition, type MigrationDocument, type MigrationReader, type MigrationTransform, type MutationBuilder, type MutationCtx, type MutatorDefinition, type OnDeleteAction, type OneRelation, type OrmLike, DEFAULT_TTL_MS as PRESENCE_DEFAULT_TTL_MS, PRESENCE_TABLE, type Permission, type Plugin, type Policy, type PrefixedTables, type PresenceComponent, type PresenceFunctions, type PresenceMember, type ProtectPublicOptions, type QueryBuilder, type QueryCtx, type RankIndexDefinition, type RankIndexOptions, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMigration, type RegisteredMutation, type RegisteredMutator, type RegisteredQuery, type RegisteredShape, type RegisteredStream, type RelationBuilder, type RelationDefinition, type RlsOptions, type RlsReadRegistry, type Role, type Schema, type SchemaExtension, type ShapeDefinition, type ShapeReadWhereRequest, type StorageOperation, type StorageRule, type StorageRuleContext, type StorageRuleDecision, type StorageRulesOptions, type TableBuilder, type TableDefinition, type TerminalKind, type TriggerBuilder, type TriggerDefinition, type TypedDefinePolicyInput, VERSION, type VectorEmbedder, type VectorIndexDefinition, type VectorIndexOptions, type VectorMetric, type VectorizeOptions, type WhereInput, allowAll, asBucketStorage, bindOrm, bindTableFacade, buildRlsReadRegistry, clampLimit, composePluginMiddleware, composeShapeReadWhere, createPolicyDsl, createSecrets, defineAggregateIndex, defineComponent, defineEnv, defineIdentity, defineListArgs, defineMigration, defineMutator, definePermission, definePlugin, definePolicies, definePolicy, definePresence, defineRankIndex, defineRole, defineSchema, defineSchemaExtension, defineShape, defineStorageRule, defineStorageRules, defineTable, defineVectorIndex, deny, httpAction, httpRoute, httpRouter, initLunora, installPlugins, isDeny, isSafeHeaderValue, mask, mergeSchemaExtension, onConnect, onDisconnect, presenceExtension, protectPublic, redactSecrets, rls, serveStorageObject, storageRules, toWhereInput };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{default as t}from"./packem_shared/asBucketStorage-1pFfH-Tn.mjs";import{initLunora as i}from"./packem_shared/initLunora-QNqOuG0A.mjs";import{createSecrets as p}from"./packem_shared/createSecrets-CgVPiW2C.mjs";import{LunoraEnvError as m,defineEnv as d,redactSecrets as x}from"./packem_shared/LunoraEnvError-CgpI2Mm_.mjs";import{LunoraError as c}from"./packem_shared/LunoraError-LVhdU0Lo.mjs";import{bindOrm as E,bindTableFacade as u}from"./packem_shared/bindOrm-Bp9hsM2q.mjs";import{httpAction as g,httpRoute as R,httpRouter as L,isSafeHeaderValue as P,serveStorageObject as h}from"./packem_shared/httpAction-C14NuF3V.mjs";import{defineIdentity as I}from"./packem_shared/defineIdentity-B7gfAgxx.mjs";import{onConnect as b,onDisconnect as y}from"./packem_shared/onConnect-CEtRmUpJ.mjs";import{DEFAULT_LIMIT as _,DEFAULT_MAX_LIMIT as D,clampLimit as v,defineListArgs as C}from"./packem_shared/DEFAULT_LIMIT-yHJ5O96W.mjs";import{defineMigration as V}from"./packem_shared/defineMigration-Bfpwxv2f.mjs";import{defineMutator as N}from"./packem_shared/defineMutator-BgpQ-xUo.mjs";import{composePluginMiddleware as U,defineComponent as w,definePlugin as B,defineSchemaExtension as W,installPlugins as j,mergeSchemaExtension as H}from"./packem_shared/composePluginMiddleware-COr09CXA.mjs";import{PRESENCE_DEFAULT_TTL_MS as X,PRESENCE_TABLE as q,definePresence as z,presenceExtension as G}from"./packem_shared/PRESENCE_DEFAULT_TTL_MS-C9RX3Cl6.mjs";import{protectPublic as Q}from"./packem_shared/protectPublic-BhKewPqm.mjs";import{defineAggregateIndex as Z,defineRankIndex as $,defineSchema as ee,defineTable as oe,defineVectorIndex as re}from"./packem_shared/defineAggregateIndex-_gWNmkQZ.mjs";import{defineShape as ne}from"./packem_shared/defineShape-Ds8uNqzX.mjs";import{anyApi as fe}from"./types.mjs";import{cronJobs as ae}from"@lunora/scheduler";import{ValidationError as de,v as xe}from"@lunora/values";import{allowAll as ce,deny as le,isDeny as Ee,toWhereInput as ue}from"./packem_shared/allowAll-BnyNbJZT.mjs";import{buildRlsReadRegistry as ge,composeShapeReadWhere as Re}from"./packem_shared/buildRlsReadRegistry-WdiqSj87.mjs";import{createPolicyDsl as Pe,definePermission as he,definePolicies as Ae,definePolicy as Ie,defineRole as Te}from"./packem_shared/createPolicyDsl-sV1swpkD.mjs";import{defineStorageRule as ye,defineStorageRules as Me}from"./packem_shared/defineStorageRule-BDu01PUn.mjs";import{mask as De}from"./packem_shared/mask-C6Bi78qj.mjs";import{rls as Ce}from"./packem_shared/rls-_iVsPvhX.mjs";import{storageRules as Ve}from"./packem_shared/storageRules-BptPZbi8.mjs";const e="0.0.0";export{_ as DEFAULT_LIMIT,D as DEFAULT_MAX_LIMIT,m as LunoraEnvError,c as LunoraError,X as PRESENCE_DEFAULT_TTL_MS,q as PRESENCE_TABLE,e as VERSION,de as ValidationError,ce as allowAll,fe as anyApi,t as asBucketStorage,E as bindOrm,u as bindTableFacade,ge as buildRlsReadRegistry,v as clampLimit,U as composePluginMiddleware,Re as composeShapeReadWhere,Pe as createPolicyDsl,p as createSecrets,ae as cronJobs,Z as defineAggregateIndex,w as defineComponent,d as defineEnv,I as defineIdentity,C as defineListArgs,V as defineMigration,N as defineMutator,he as definePermission,B as definePlugin,Ae as definePolicies,Ie as definePolicy,z as definePresence,$ as defineRankIndex,Te as defineRole,ee as defineSchema,W as defineSchemaExtension,ne as defineShape,ye as defineStorageRule,Me as defineStorageRules,oe as defineTable,re as defineVectorIndex,le as deny,g as httpAction,R as httpRoute,L as httpRouter,i as initLunora,j as installPlugins,Ee as isDeny,P as isSafeHeaderValue,De as mask,H as mergeSchemaExtension,b as onConnect,y as onDisconnect,G as presenceExtension,Q as protectPublic,x as redactSecrets,Ce as rls,h as serveStorageObject,Ve as storageRules,ue as toWhereInput,xe as v};
1
+ import{initLunora as t}from"./packem_shared/initLunora-QNqOuG0A.mjs";import{createSecrets as i}from"./packem_shared/createSecrets-CgVPiW2C.mjs";import{LunoraEnvError as p,defineEnv as m,redactSecrets as a}from"./packem_shared/LunoraEnvError-CgpI2Mm_.mjs";import{LunoraError as x}from"./packem_shared/LunoraError-LVhdU0Lo.mjs";import{bindOrm as c,bindTableFacade as l}from"./packem_shared/bindOrm-Bp9hsM2q.mjs";import{httpAction as u,httpRoute as S,httpRouter as g,isSafeHeaderValue as R,serveStorageObject as L}from"./packem_shared/httpAction-C14NuF3V.mjs";import{defineIdentity as h}from"./packem_shared/defineIdentity-B7gfAgxx.mjs";import{onConnect as I,onDisconnect as T}from"./packem_shared/onConnect-CEtRmUpJ.mjs";import{DEFAULT_LIMIT as y,DEFAULT_MAX_LIMIT as M,clampLimit as _,defineListArgs as D}from"./packem_shared/DEFAULT_LIMIT-yHJ5O96W.mjs";import{defineMigration as C}from"./packem_shared/defineMigration-Bfpwxv2f.mjs";import{defineMutator as V}from"./packem_shared/defineMutator-BgpQ-xUo.mjs";import{composePluginMiddleware as N,defineComponent as O,definePlugin as U,defineSchemaExtension as w,installPlugins as B,mergeSchemaExtension as W}from"./packem_shared/composePluginMiddleware-COr09CXA.mjs";import{PRESENCE_DEFAULT_TTL_MS as H,PRESENCE_TABLE as J,definePresence as X,presenceExtension as q}from"./packem_shared/PRESENCE_DEFAULT_TTL_MS-C9RX3Cl6.mjs";import{protectPublic as G}from"./packem_shared/protectPublic-BhKewPqm.mjs";import{defineAggregateIndex as Q,defineRankIndex as Y,defineSchema as Z,defineTable as $,defineVectorIndex as ee}from"./packem_shared/defineAggregateIndex-_gWNmkQZ.mjs";import{defineShape as re}from"./packem_shared/defineShape-Ds8uNqzX.mjs";import{anyApi as ne}from"./types.mjs";import{cronJobs as fe}from"@lunora/scheduler";import{ValidationError as me,v as ae}from"@lunora/values";import{allowAll as xe,deny as se,isDeny as ce,toWhereInput as le}from"./packem_shared/allowAll-BnyNbJZT.mjs";import{asBucketStorage as ue}from"./packem_shared/asBucketStorage-BthCnWop.mjs";import{buildRlsReadRegistry as ge,composeShapeReadWhere as Re}from"./packem_shared/buildRlsReadRegistry-WdiqSj87.mjs";import{createPolicyDsl as Pe,definePermission as he,definePolicies as Ae,definePolicy as Ie,defineRole as Te}from"./packem_shared/createPolicyDsl-sV1swpkD.mjs";import{defineStorageRule as ye,defineStorageRules as Me}from"./packem_shared/defineStorageRule-BDu01PUn.mjs";import{mask as De}from"./packem_shared/mask-C6Bi78qj.mjs";import{rls as Ce}from"./packem_shared/rls-_iVsPvhX.mjs";import{storageRules as Ve}from"./packem_shared/storageRules-BptPZbi8.mjs";const e="0.0.0";export{y as DEFAULT_LIMIT,M as DEFAULT_MAX_LIMIT,p as LunoraEnvError,x as LunoraError,H as PRESENCE_DEFAULT_TTL_MS,J as PRESENCE_TABLE,e as VERSION,me as ValidationError,xe as allowAll,ne as anyApi,ue as asBucketStorage,c as bindOrm,l as bindTableFacade,ge as buildRlsReadRegistry,_ as clampLimit,N as composePluginMiddleware,Re as composeShapeReadWhere,Pe as createPolicyDsl,i as createSecrets,fe as cronJobs,Q as defineAggregateIndex,O as defineComponent,m as defineEnv,h as defineIdentity,D as defineListArgs,C as defineMigration,V as defineMutator,he as definePermission,U as definePlugin,Ae as definePolicies,Ie as definePolicy,X as definePresence,Y as defineRankIndex,Te as defineRole,Z as defineSchema,w as defineSchemaExtension,re as defineShape,ye as defineStorageRule,Me as defineStorageRules,$ as defineTable,ee as defineVectorIndex,se as deny,u as httpAction,S as httpRoute,g as httpRouter,t as initLunora,B as installPlugins,ce as isDeny,R as isSafeHeaderValue,De as mask,W as mergeSchemaExtension,I as onConnect,T as onDisconnect,q as presenceExtension,G as protectPublic,a as redactSecrets,Ce as rls,L as serveStorageObject,Ve as storageRules,le as toWhereInput,ae as v};
@@ -0,0 +1 @@
1
+ const n=c=>{const t=c??{};if(typeof t.bucket=="function")return t;const e={...t,bucketName:"default"};return e.bucket=()=>e,e};export{n as asBucketStorage};
package/dist/types.d.mts CHANGED
@@ -795,11 +795,34 @@ interface PaginationResult<T = Record<string, unknown>> {
795
795
  /**
796
796
  * The fluent `ctx.db.query(table)` reader. Generic over the document type
797
797
  * `Row` so the generated `ctx.db` can bind it to `Doc&lt;table>` (the chain and
798
- * every terminal then resolve typed rows — no `as unknown as Doc&lt;...>` casts).
799
- * Defaults to the untyped `Record&lt;string, unknown>` shape for the base
800
- * (schema-agnostic) `@lunora/server` reader.
798
+ * every terminal then resolve typed rows — no `as unknown as Doc&lt;...>` casts),
799
+ * and over the table's declared index names so `.withIndex()` / `.withSearchIndex()`
800
+ * / `.withGeoIndex()` reject a name the table does not declare.
801
+ *
802
+ * All four default to the untyped shape for the base (schema-agnostic)
803
+ * `@lunora/server` reader, which is also the wide `(table: string) => TableReader`
804
+ * overload the generated `ctx.db` intersects in — so a caller holding a runtime
805
+ * string (e.g. `@lunora/ratelimit`'s `createDbStore`) is unaffected.
806
+ *
807
+ * The index-name parameters are what make a stale index name a compile error.
808
+ * They resolve to `never` for a table that declares none of that kind, so the
809
+ * only way to satisfy the call is to declare the index. Before this, a renamed
810
+ * or dropped index left its call sites typechecking, and the query either threw
811
+ * at runtime or silently degraded to a full table scan — the second being the
812
+ * worse outcome, since it stays green in tests and surfaces months later as a
813
+ * latency regression.
814
+ *
815
+ * **Why `with*` are method signatures and everything else is a property.**
816
+ * Narrowing a parameter makes the enclosing type contravariant in it, so as
817
+ * function properties these would make a BOUND reader
818
+ * (`TableReader&lt;Doc, "by_x">`, what `ctx.db.query(t)` returns) unassignable to
819
+ * the unbound `TableReader&lt;Doc>` — quietly breaking every helper factored as
820
+ * `(reader: TableReader&lt;Doc&lt;"users">>) => …`, which is the obvious way to share
821
+ * query logic. A method signature is bivariant in its parameters, which keeps
822
+ * that direction working while the narrow parameter still rejects an undeclared
823
+ * name at the call site. Both directions are pinned in `types.test-d.ts`.
801
824
  */
802
- interface TableReader<Row = Record<string, unknown>> {
825
+ interface TableReader<Row = Record<string, unknown>, Indexes extends string = string, SearchIndexes extends string = string, GeoIndexes extends string = string> {
803
826
  /**
804
827
  * Iterate rows lazily: `for await (const row of ctx.db.query("t").withIndex(…))`.
805
828
  *
@@ -822,7 +845,7 @@ interface TableReader<Row = Record<string, unknown>> {
822
845
  */
823
846
  [Symbol.asyncIterator]: () => AsyncIterator<Row>;
824
847
  collect: () => Promise<Row[]>;
825
- filter: (predicate: (document: Row) => boolean) => TableReader<Row>;
848
+ filter: (predicate: (document: Row) => boolean) => TableReader<Row, Indexes, SearchIndexes, GeoIndexes>;
826
849
  first: () => Promise<Row | null>;
827
850
  /**
828
851
  * Set the result order. Orders by the active `.withIndex()` (or by
@@ -831,7 +854,7 @@ interface TableReader<Row = Record<string, unknown>> {
831
854
  * terminal (`collect`/`first`/`take`/`paginate`/`unique`). Mirrors Convex's
832
855
  * `.order("asc" | "desc")`.
833
856
  */
834
- order: (direction: "asc" | "desc") => TableReader<Row>;
857
+ order: (direction: "asc" | "desc") => TableReader<Row, Indexes, SearchIndexes, GeoIndexes>;
835
858
  paginate: (options: PaginationOptions) => Promise<PaginationResult<Row>>;
836
859
  take: (limit: number) => Promise<Row[]>;
837
860
  /**
@@ -847,8 +870,14 @@ interface TableReader<Row = Record<string, unknown>> {
847
870
  * scan over the index's companion followed by a Haversine refine. Pair with
848
871
  * `.take(n)` to cap results (`.paginate()` is not supported on a geo query).
849
872
  */
850
- withGeoIndex: (indexName: string, build: (q: GeoFilterBuilder) => GeoFilterBuilder) => TableReader<Row>;
851
- withIndex: (indexName: string, range?: (q: IndexRangeBuilder) => IndexRangeBuilder) => TableReader<Row>;
873
+ withGeoIndex(indexName: GeoIndexes, build: (q: GeoFilterBuilder) => GeoFilterBuilder): TableReader<Row, Indexes, SearchIndexes, GeoIndexes>;
874
+ /**
875
+ * Restrict the query to a declared `.index()`. `indexName` is constrained to
876
+ * this table's declared index names (`never` when it declares none), so a
877
+ * renamed, dropped, or mistyped index is a compile error rather than a
878
+ * runtime throw or a silent full-table scan.
879
+ */
880
+ withIndex(indexName: Indexes, range?: (q: IndexRangeBuilder) => IndexRangeBuilder): TableReader<Row, Indexes, SearchIndexes, GeoIndexes>;
852
881
  /**
853
882
  * Restrict the query to a declared `.searchIndex()`. The builder's
854
883
  * `.search(field, query)` runs a full-text match against the index's
@@ -856,7 +885,7 @@ interface TableReader<Row = Record<string, unknown>> {
856
885
  * field. Results come back ordered by relevance — pair with `.take(n)`
857
886
  * (`.paginate()` is not supported on a search query).
858
887
  */
859
- withSearchIndex: (indexName: string, search: (q: SearchFilterBuilder) => SearchFilterBuilder) => TableReader<Row>;
888
+ withSearchIndex(indexName: SearchIndexes, search: (q: SearchFilterBuilder) => SearchFilterBuilder): TableReader<Row, Indexes, SearchIndexes, GeoIndexes>;
860
889
  }
861
890
  interface IndexRangeBuilder {
862
891
  eq: (field: string, value: unknown) => IndexRangeBuilder;
package/dist/types.d.ts CHANGED
@@ -795,11 +795,34 @@ interface PaginationResult<T = Record<string, unknown>> {
795
795
  /**
796
796
  * The fluent `ctx.db.query(table)` reader. Generic over the document type
797
797
  * `Row` so the generated `ctx.db` can bind it to `Doc&lt;table>` (the chain and
798
- * every terminal then resolve typed rows — no `as unknown as Doc&lt;...>` casts).
799
- * Defaults to the untyped `Record&lt;string, unknown>` shape for the base
800
- * (schema-agnostic) `@lunora/server` reader.
798
+ * every terminal then resolve typed rows — no `as unknown as Doc&lt;...>` casts),
799
+ * and over the table's declared index names so `.withIndex()` / `.withSearchIndex()`
800
+ * / `.withGeoIndex()` reject a name the table does not declare.
801
+ *
802
+ * All four default to the untyped shape for the base (schema-agnostic)
803
+ * `@lunora/server` reader, which is also the wide `(table: string) => TableReader`
804
+ * overload the generated `ctx.db` intersects in — so a caller holding a runtime
805
+ * string (e.g. `@lunora/ratelimit`'s `createDbStore`) is unaffected.
806
+ *
807
+ * The index-name parameters are what make a stale index name a compile error.
808
+ * They resolve to `never` for a table that declares none of that kind, so the
809
+ * only way to satisfy the call is to declare the index. Before this, a renamed
810
+ * or dropped index left its call sites typechecking, and the query either threw
811
+ * at runtime or silently degraded to a full table scan — the second being the
812
+ * worse outcome, since it stays green in tests and surfaces months later as a
813
+ * latency regression.
814
+ *
815
+ * **Why `with*` are method signatures and everything else is a property.**
816
+ * Narrowing a parameter makes the enclosing type contravariant in it, so as
817
+ * function properties these would make a BOUND reader
818
+ * (`TableReader&lt;Doc, "by_x">`, what `ctx.db.query(t)` returns) unassignable to
819
+ * the unbound `TableReader&lt;Doc>` — quietly breaking every helper factored as
820
+ * `(reader: TableReader&lt;Doc&lt;"users">>) => …`, which is the obvious way to share
821
+ * query logic. A method signature is bivariant in its parameters, which keeps
822
+ * that direction working while the narrow parameter still rejects an undeclared
823
+ * name at the call site. Both directions are pinned in `types.test-d.ts`.
801
824
  */
802
- interface TableReader<Row = Record<string, unknown>> {
825
+ interface TableReader<Row = Record<string, unknown>, Indexes extends string = string, SearchIndexes extends string = string, GeoIndexes extends string = string> {
803
826
  /**
804
827
  * Iterate rows lazily: `for await (const row of ctx.db.query("t").withIndex(…))`.
805
828
  *
@@ -822,7 +845,7 @@ interface TableReader<Row = Record<string, unknown>> {
822
845
  */
823
846
  [Symbol.asyncIterator]: () => AsyncIterator<Row>;
824
847
  collect: () => Promise<Row[]>;
825
- filter: (predicate: (document: Row) => boolean) => TableReader<Row>;
848
+ filter: (predicate: (document: Row) => boolean) => TableReader<Row, Indexes, SearchIndexes, GeoIndexes>;
826
849
  first: () => Promise<Row | null>;
827
850
  /**
828
851
  * Set the result order. Orders by the active `.withIndex()` (or by
@@ -831,7 +854,7 @@ interface TableReader<Row = Record<string, unknown>> {
831
854
  * terminal (`collect`/`first`/`take`/`paginate`/`unique`). Mirrors Convex's
832
855
  * `.order("asc" | "desc")`.
833
856
  */
834
- order: (direction: "asc" | "desc") => TableReader<Row>;
857
+ order: (direction: "asc" | "desc") => TableReader<Row, Indexes, SearchIndexes, GeoIndexes>;
835
858
  paginate: (options: PaginationOptions) => Promise<PaginationResult<Row>>;
836
859
  take: (limit: number) => Promise<Row[]>;
837
860
  /**
@@ -847,8 +870,14 @@ interface TableReader<Row = Record<string, unknown>> {
847
870
  * scan over the index's companion followed by a Haversine refine. Pair with
848
871
  * `.take(n)` to cap results (`.paginate()` is not supported on a geo query).
849
872
  */
850
- withGeoIndex: (indexName: string, build: (q: GeoFilterBuilder) => GeoFilterBuilder) => TableReader<Row>;
851
- withIndex: (indexName: string, range?: (q: IndexRangeBuilder) => IndexRangeBuilder) => TableReader<Row>;
873
+ withGeoIndex(indexName: GeoIndexes, build: (q: GeoFilterBuilder) => GeoFilterBuilder): TableReader<Row, Indexes, SearchIndexes, GeoIndexes>;
874
+ /**
875
+ * Restrict the query to a declared `.index()`. `indexName` is constrained to
876
+ * this table's declared index names (`never` when it declares none), so a
877
+ * renamed, dropped, or mistyped index is a compile error rather than a
878
+ * runtime throw or a silent full-table scan.
879
+ */
880
+ withIndex(indexName: Indexes, range?: (q: IndexRangeBuilder) => IndexRangeBuilder): TableReader<Row, Indexes, SearchIndexes, GeoIndexes>;
852
881
  /**
853
882
  * Restrict the query to a declared `.searchIndex()`. The builder's
854
883
  * `.search(field, query)` runs a full-text match against the index's
@@ -856,7 +885,7 @@ interface TableReader<Row = Record<string, unknown>> {
856
885
  * field. Results come back ordered by relevance — pair with `.take(n)`
857
886
  * (`.paginate()` is not supported on a search query).
858
887
  */
859
- withSearchIndex: (indexName: string, search: (q: SearchFilterBuilder) => SearchFilterBuilder) => TableReader<Row>;
888
+ withSearchIndex(indexName: SearchIndexes, search: (q: SearchFilterBuilder) => SearchFilterBuilder): TableReader<Row, Indexes, SearchIndexes, GeoIndexes>;
860
889
  }
861
890
  interface IndexRangeBuilder {
862
891
  eq: (field: string, value: unknown) => IndexRangeBuilder;
package/dist/types.mjs CHANGED
@@ -1 +1 @@
1
- const a=new Map,u=new Proxy({},{get(i,t){const e=a.get(t);if(e)return e;const r=new Map,o=new Proxy({},{get(c,n){const s=r.get(n);if(s)return s;const g={__lunoraRef:`${String(t)}:${String(n)}`};return r.set(n,g),g}});return a.set(t,o),o}});export{u as anyApi};
1
+ const g=new Map,a=new Proxy({},{get(i,t){const e=g.get(t);if(e)return e;const r=new Map,o=new Proxy({},{get(u,n){const s=r.get(n);if(s)return s;const c={__lunoraRef:`${String(t)}:${String(n)}`};return r.set(n,c),c}});return g.set(t,o),o}}),p=a;export{p as anyApi};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/server",
3
- "version": "1.0.0-alpha.55",
3
+ "version": "1.0.0-alpha.56",
4
4
  "description": "Server primitives for Lunora: defineSchema, defineTable, query, mutation, and action",
5
5
  "keywords": [
6
6
  "backend",
@@ -1 +0,0 @@
1
- const n=u=>{const t=u??{};if(typeof t.bucket=="function")return t;const e={...t,bucketName:"default"};return e.bucket=()=>e,e};export{n as default};