@lunora/server 1.0.0-alpha.10 → 1.0.0-alpha.12

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
@@ -3,8 +3,8 @@ export { type ColumnValidator, type Id, type Infer, ValidationError, type Valida
3
3
  import { ArgsValidator, InferArgs, RegisteredAction, ActionCtx, MutationCtx, RegisteredMutation, QueryCtx, RegisteredQuery, RegisteredStream, FunctionKind, Secrets, LifecycleEvent, RegisteredLifecycleHook, TableDefinition, RegisteredFunction, VectorIndexDefinition, Schema, AggregateOp, DurableObjectJurisdiction, RelationDefinition, GlobalBackend, OnDeleteAction, ExternalSourceDefinition, TriggerBuilder, TriggerDefinition, VectorEmbedder, VectorMetric, AggregateIndexDefinition, RankIndexDefinition } from "./types.mjs";
4
4
  export { type AnyApi, type AuthState, type DatabaseReader, type DatabaseWriter, type FunctionVisibility, type IndexDefinition, type IndexRangeBuilder, type LifecycleEventKind, type LunoraLogger, type PaginationOptions, type PaginationResult, type RankSortKey, type ReadOnlyStorage, type ScheduledFunctionDoc, type ScheduledJob, type Scheduler, type SearchFilterBuilder, type SearchIndexDefinition, type ShardMode, type Storage, type StorageMetadata, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemTableName, type TableReader, type TableVectorIndex, type TriggerAggregateOptions, type TriggerCtx, type TriggerDatabase, type TriggerDeleteEvent, type TriggerEvent, type TriggerGroupByEntry, type TriggerGroupByOptions, type TriggerHandler, type TriggerInsertEvent, type TriggerOp, type TriggerQueryArgs, type TriggerQueryPage, type TriggerRankOptions, type TriggerRankPageOptions, type TriggerRankResult, type TriggerRow, type TriggerTiming, type TriggerUpdateEvent, type VectorMatch, type VectorMatches, type VectorQueryInput, type VectorRecord, type VectorSearch, type VectorSearchReader, type VectorUpsertInput, type WorkflowCreateOptions, type WorkflowHandle, type WorkflowInstance, type WorkflowInstanceStatus, type WorkflowStatusResult, type Workflows, anyApi } from "./types.mjs";
5
5
  import { Context, Hono } from 'hono';
6
- import { b as Permission, R as Role, T as TypedDefinePolicyInput, a as Policy, D as DefinePolicyInput, W as WhereInput, c as RlsOptions } from "./packem_shared/types.d-DmvyEMD6.mjs";
7
- export type { d as PolicyContext, e as PolicyDecision, f as PolicyDecisionOf, P as PolicyOperation } from "./packem_shared/types.d-DmvyEMD6.mjs";
6
+ import { b as Permission, R as Role, T as TypedDefinePolicyInput, a as Policy, D as DefinePolicyInput, W as WhereInput, c as RlsOptions } from "./packem_shared/types.d-BB3pjV0m.mjs";
7
+ export type { d as PolicyContext, e as PolicyDecision, f as PolicyDecisionOf, P as PolicyOperation } from "./packem_shared/types.d-BB3pjV0m.mjs";
8
8
  export { type CronJob, type CronJobsBuilder, type CronScheduleKind, type DailySchedule, type IntervalSchedule, type MonthlySchedule, type WeeklySchedule, cronJobs } from '@lunora/scheduler';
9
9
  import "./data-model.mjs";
10
10
  /**
@@ -674,6 +674,76 @@ interface ContextWithStorage {
674
674
  * client then ranges against R2/CDN directly with no Worker hop.
675
675
  */
676
676
  declare const serveStorageObject: (context: ContextWithStorage, key: string, request: Request) => Promise<Response>;
677
+ /**
678
+ * What the worker does with a resolver's identity when it fails contract
679
+ * validation (a forged / malformed claim set arriving from an untrusted token).
680
+ * `"anonymous"` (default, safe) treats the request as anonymous, so the bad
681
+ * identity never reaches a policy as a valid identity (`ctx.auth.userId`
682
+ * becomes `undefined`). `"reject"` fails the request closed (a `401`) — use
683
+ * when a malformed credential should be a hard error, not a silent downgrade.
684
+ */
685
+ type IdentityRejectMode = "anonymous" | "reject";
686
+ /** Options for {@link defineIdentity}. */
687
+ interface DefineIdentityOptions {
688
+ /**
689
+ * How to handle a resolver identity that violates the contract at the trust
690
+ * boundary. Defaults to `"anonymous"` (a forged claim set is downgraded to
691
+ * anonymous rather than flowing in as an unchecked cast).
692
+ */
693
+ readonly onInvalid?: IdentityRejectMode;
694
+ }
695
+ /** Result of validating a candidate identity against the contract. */
696
+ type IdentityValidation = {
697
+ ok: true;
698
+ } | {
699
+ error: string;
700
+ ok: false;
701
+ };
702
+ /**
703
+ * A declared identity claim contract. Carries the codegen discovery brand, the
704
+ * declared claim validators, the reject policy, and a runtime `validate`. The
705
+ * `TClaims` type parameter is the inferred claim shape (always extending
706
+ * `{ userId: string }`); it is phantom (no runtime field) and exists so
707
+ * `@lunora/codegen` and {@link InferIdentity} can recover the type.
708
+ */
709
+ interface IdentityContract<TClaims extends {
710
+ userId: string;
711
+ } = {
712
+ userId: string;
713
+ }> {
714
+ /**
715
+ * Phantom carrier for the inferred claim type. Never populated at runtime
716
+ * (`undefined`); present only so the type flows to codegen / {@link InferIdentity}.
717
+ */
718
+ readonly __claimType?: TClaims;
719
+ readonly __lunoraIdentity: true;
720
+ /** The declared claim validators (a `@lunora/values` validator map). */
721
+ readonly claims: ValidatorMap;
722
+ /** Reject policy applied at the trust boundary. See {@link IdentityRejectMode}. */
723
+ readonly onInvalid: IdentityRejectMode;
724
+ /**
725
+ * Validate a resolver's returned identity against the declared claims. On
726
+ * success the caller keeps the original identity untouched (so undeclared
727
+ * claims are forwarded verbatim, preserving today's behaviour); on failure
728
+ * the worker applies the `onInvalid` policy.
729
+ */
730
+ validate: (identity: Record<string, unknown>) => IdentityValidation;
731
+ }
732
+ /** Recover the declared claim type from a {@link defineIdentity} contract. */
733
+ type InferIdentity<T> = T extends IdentityContract<infer TClaims> ? TClaims : never;
734
+ /**
735
+ * Declare the identity claim contract. `claims` is a `@lunora/values` validator
736
+ * map whose inferred type must extend `{ userId: string }` — if it does not
737
+ * (e.g. `userId` is missing or not a required string), the argument type
738
+ * collapses to `never` and the call fails to typecheck.
739
+ * @example
740
+ * export const identity = defineIdentity({ userId: v.string(), tenantId: v.optional(v.string()), scopes: v.optional(v.array(v.string())) });
741
+ */
742
+ declare const defineIdentity: <A extends ValidatorMap>(claims: InferValidatorMap<A> extends {
743
+ userId: string;
744
+ } ? A : never, options?: DefineIdentityOptions) => IdentityContract<InferValidatorMap<A> & {
745
+ userId: string;
746
+ }>;
677
747
  /** Handler for a connection-lifecycle hook. */
678
748
  type LifecycleHandler = (context: MutationCtx, event: LifecycleEvent) => Promise<void> | void;
679
749
  /** Register a hook that fires once when a client's WebSocket connects. */
@@ -1576,7 +1646,7 @@ declare const definePolicy: <Context = unknown>(input: DefinePolicyInput<Context
1576
1646
  * the compile-time surface narrows, so a policy authored either way is
1577
1647
  * discovered identically by the `rls()` chain.
1578
1648
  */
1579
- declare const createPolicyDsl: <DM, REL extends Record<keyof DM, object>>() => <T extends keyof DM, Context = unknown>(input: TypedDefinePolicyInput<DM, REL, T, Context>) => Policy<Context>;
1649
+ declare const createPolicyDsl: <DM, REL extends Record<keyof DM, object>, Identity = Record<string, unknown>>() => <T extends keyof DM, Context = unknown>(input: TypedDefinePolicyInput<DM, REL, T, Context, Identity>) => Policy<Context>;
1580
1650
  /**
1581
1651
  * Declare a named permission a policy can check with `ctx.auth.can(...)`. Grant
1582
1652
  * it to a role through `defineRole`'s `permissions`, register those roles with
@@ -1982,4 +2052,4 @@ interface StorageContextIn {
1982
2052
  }
1983
2053
  declare const storageRules: <Context extends StorageContextIn = StorageContextIn>(rules: ReadonlyArray<StorageRule<Context>>, options?: StorageRulesOptions) => Middleware<Context, Context>;
1984
2054
  declare const VERSION = "0.0.0";
1985
- export { type ActionBuilder, type ActionCtx, type AggregateIndexDefinition, type AggregateIndexOptions, type AggregateOp, type ArgsValidator, type Component, type ComponentFunctions, type CreateOptions, type DataModelInit, type DefineComponentOptions, type DefinePluginOptions, type DefinePolicyInput, type DefinePresenceOptions, type DefineStorageRuleInput, type DurableObjectJurisdiction, type EmptyArgs, type EnvAccessor, type EnvKeyFailure, type EnvShape, 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 InferArgs, type InferEnv, type InlineAggregateIndexOptions, type InlineRankIndexOptions, type InternalActionBuilder, type InternalMutationBuilder, type InternalQueryBuilder, type LifecycleEvent, type LifecycleHandler, type LunoraBuilders, LunoraEnvError, LunoraError, type LunoraErrorCode, 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, asBucketStorage, bindOrm, bindTableFacade, buildRlsReadRegistry, composePluginMiddleware, composeShapeReadWhere, createPolicyDsl, createSecrets, defineAggregateIndex, defineComponent, defineEnv, defineMigration, defineMutator, definePermission, definePlugin, definePolicies, definePolicy, definePresence, defineRankIndex, defineRole, defineSchema, defineSchemaExtension, defineShape, defineStorageRule, defineStorageRules, defineTable, defineVectorIndex, httpAction, httpRoute, httpRouter, initLunora, installPlugins, mask, mergeSchemaExtension, onConnect, onDisconnect, presenceExtension, protectPublic, redactSecrets, rls, serveStorageObject, storageRules };
2055
+ export { type ActionBuilder, type ActionCtx, type AggregateIndexDefinition, type AggregateIndexOptions, type AggregateOp, type ArgsValidator, type Component, type ComponentFunctions, type CreateOptions, type DataModelInit, type DefineComponentOptions, type DefineIdentityOptions, type DefinePluginOptions, type DefinePolicyInput, type DefinePresenceOptions, type DefineStorageRuleInput, type DurableObjectJurisdiction, type EmptyArgs, type EnvAccessor, type EnvKeyFailure, type EnvShape, 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 LunoraBuilders, LunoraEnvError, LunoraError, type LunoraErrorCode, 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, asBucketStorage, bindOrm, bindTableFacade, buildRlsReadRegistry, composePluginMiddleware, composeShapeReadWhere, createPolicyDsl, createSecrets, defineAggregateIndex, defineComponent, defineEnv, defineIdentity, defineMigration, defineMutator, definePermission, definePlugin, definePolicies, definePolicy, definePresence, defineRankIndex, defineRole, defineSchema, defineSchemaExtension, defineShape, defineStorageRule, defineStorageRules, defineTable, defineVectorIndex, httpAction, httpRoute, httpRouter, initLunora, installPlugins, mask, mergeSchemaExtension, onConnect, onDisconnect, presenceExtension, protectPublic, redactSecrets, rls, serveStorageObject, storageRules };
package/dist/index.d.ts CHANGED
@@ -3,8 +3,8 @@ export { type ColumnValidator, type Id, type Infer, ValidationError, type Valida
3
3
  import { ArgsValidator, InferArgs, RegisteredAction, ActionCtx, MutationCtx, RegisteredMutation, QueryCtx, RegisteredQuery, RegisteredStream, FunctionKind, Secrets, LifecycleEvent, RegisteredLifecycleHook, TableDefinition, RegisteredFunction, VectorIndexDefinition, Schema, AggregateOp, DurableObjectJurisdiction, RelationDefinition, GlobalBackend, OnDeleteAction, ExternalSourceDefinition, TriggerBuilder, TriggerDefinition, VectorEmbedder, VectorMetric, AggregateIndexDefinition, RankIndexDefinition } from "./types.js";
4
4
  export { type AnyApi, type AuthState, type DatabaseReader, type DatabaseWriter, type FunctionVisibility, type IndexDefinition, type IndexRangeBuilder, type LifecycleEventKind, type LunoraLogger, type PaginationOptions, type PaginationResult, type RankSortKey, type ReadOnlyStorage, type ScheduledFunctionDoc, type ScheduledJob, type Scheduler, type SearchFilterBuilder, type SearchIndexDefinition, type ShardMode, type Storage, type StorageMetadata, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemTableName, type TableReader, type TableVectorIndex, type TriggerAggregateOptions, type TriggerCtx, type TriggerDatabase, type TriggerDeleteEvent, type TriggerEvent, type TriggerGroupByEntry, type TriggerGroupByOptions, type TriggerHandler, type TriggerInsertEvent, type TriggerOp, type TriggerQueryArgs, type TriggerQueryPage, type TriggerRankOptions, type TriggerRankPageOptions, type TriggerRankResult, type TriggerRow, type TriggerTiming, type TriggerUpdateEvent, type VectorMatch, type VectorMatches, type VectorQueryInput, type VectorRecord, type VectorSearch, type VectorSearchReader, type VectorUpsertInput, type WorkflowCreateOptions, type WorkflowHandle, type WorkflowInstance, type WorkflowInstanceStatus, type WorkflowStatusResult, type Workflows, anyApi } from "./types.js";
5
5
  import { Context, Hono } from 'hono';
6
- import { b as Permission, R as Role, T as TypedDefinePolicyInput, a as Policy, D as DefinePolicyInput, W as WhereInput, c as RlsOptions } from "./packem_shared/types.d-BDY0FYHK.js";
7
- export type { d as PolicyContext, e as PolicyDecision, f as PolicyDecisionOf, P as PolicyOperation } from "./packem_shared/types.d-BDY0FYHK.js";
6
+ import { b as Permission, R as Role, T as TypedDefinePolicyInput, a as Policy, D as DefinePolicyInput, W as WhereInput, c as RlsOptions } from "./packem_shared/types.d-Cxl6ndhm.js";
7
+ export type { d as PolicyContext, e as PolicyDecision, f as PolicyDecisionOf, P as PolicyOperation } from "./packem_shared/types.d-Cxl6ndhm.js";
8
8
  export { type CronJob, type CronJobsBuilder, type CronScheduleKind, type DailySchedule, type IntervalSchedule, type MonthlySchedule, type WeeklySchedule, cronJobs } from '@lunora/scheduler';
9
9
  import "./data-model.js";
10
10
  /**
@@ -674,6 +674,76 @@ interface ContextWithStorage {
674
674
  * client then ranges against R2/CDN directly with no Worker hop.
675
675
  */
676
676
  declare const serveStorageObject: (context: ContextWithStorage, key: string, request: Request) => Promise<Response>;
677
+ /**
678
+ * What the worker does with a resolver's identity when it fails contract
679
+ * validation (a forged / malformed claim set arriving from an untrusted token).
680
+ * `"anonymous"` (default, safe) treats the request as anonymous, so the bad
681
+ * identity never reaches a policy as a valid identity (`ctx.auth.userId`
682
+ * becomes `undefined`). `"reject"` fails the request closed (a `401`) — use
683
+ * when a malformed credential should be a hard error, not a silent downgrade.
684
+ */
685
+ type IdentityRejectMode = "anonymous" | "reject";
686
+ /** Options for {@link defineIdentity}. */
687
+ interface DefineIdentityOptions {
688
+ /**
689
+ * How to handle a resolver identity that violates the contract at the trust
690
+ * boundary. Defaults to `"anonymous"` (a forged claim set is downgraded to
691
+ * anonymous rather than flowing in as an unchecked cast).
692
+ */
693
+ readonly onInvalid?: IdentityRejectMode;
694
+ }
695
+ /** Result of validating a candidate identity against the contract. */
696
+ type IdentityValidation = {
697
+ ok: true;
698
+ } | {
699
+ error: string;
700
+ ok: false;
701
+ };
702
+ /**
703
+ * A declared identity claim contract. Carries the codegen discovery brand, the
704
+ * declared claim validators, the reject policy, and a runtime `validate`. The
705
+ * `TClaims` type parameter is the inferred claim shape (always extending
706
+ * `{ userId: string }`); it is phantom (no runtime field) and exists so
707
+ * `@lunora/codegen` and {@link InferIdentity} can recover the type.
708
+ */
709
+ interface IdentityContract<TClaims extends {
710
+ userId: string;
711
+ } = {
712
+ userId: string;
713
+ }> {
714
+ /**
715
+ * Phantom carrier for the inferred claim type. Never populated at runtime
716
+ * (`undefined`); present only so the type flows to codegen / {@link InferIdentity}.
717
+ */
718
+ readonly __claimType?: TClaims;
719
+ readonly __lunoraIdentity: true;
720
+ /** The declared claim validators (a `@lunora/values` validator map). */
721
+ readonly claims: ValidatorMap;
722
+ /** Reject policy applied at the trust boundary. See {@link IdentityRejectMode}. */
723
+ readonly onInvalid: IdentityRejectMode;
724
+ /**
725
+ * Validate a resolver's returned identity against the declared claims. On
726
+ * success the caller keeps the original identity untouched (so undeclared
727
+ * claims are forwarded verbatim, preserving today's behaviour); on failure
728
+ * the worker applies the `onInvalid` policy.
729
+ */
730
+ validate: (identity: Record<string, unknown>) => IdentityValidation;
731
+ }
732
+ /** Recover the declared claim type from a {@link defineIdentity} contract. */
733
+ type InferIdentity<T> = T extends IdentityContract<infer TClaims> ? TClaims : never;
734
+ /**
735
+ * Declare the identity claim contract. `claims` is a `@lunora/values` validator
736
+ * map whose inferred type must extend `{ userId: string }` — if it does not
737
+ * (e.g. `userId` is missing or not a required string), the argument type
738
+ * collapses to `never` and the call fails to typecheck.
739
+ * @example
740
+ * export const identity = defineIdentity({ userId: v.string(), tenantId: v.optional(v.string()), scopes: v.optional(v.array(v.string())) });
741
+ */
742
+ declare const defineIdentity: <A extends ValidatorMap>(claims: InferValidatorMap<A> extends {
743
+ userId: string;
744
+ } ? A : never, options?: DefineIdentityOptions) => IdentityContract<InferValidatorMap<A> & {
745
+ userId: string;
746
+ }>;
677
747
  /** Handler for a connection-lifecycle hook. */
678
748
  type LifecycleHandler = (context: MutationCtx, event: LifecycleEvent) => Promise<void> | void;
679
749
  /** Register a hook that fires once when a client's WebSocket connects. */
@@ -1576,7 +1646,7 @@ declare const definePolicy: <Context = unknown>(input: DefinePolicyInput<Context
1576
1646
  * the compile-time surface narrows, so a policy authored either way is
1577
1647
  * discovered identically by the `rls()` chain.
1578
1648
  */
1579
- declare const createPolicyDsl: <DM, REL extends Record<keyof DM, object>>() => <T extends keyof DM, Context = unknown>(input: TypedDefinePolicyInput<DM, REL, T, Context>) => Policy<Context>;
1649
+ declare const createPolicyDsl: <DM, REL extends Record<keyof DM, object>, Identity = Record<string, unknown>>() => <T extends keyof DM, Context = unknown>(input: TypedDefinePolicyInput<DM, REL, T, Context, Identity>) => Policy<Context>;
1580
1650
  /**
1581
1651
  * Declare a named permission a policy can check with `ctx.auth.can(...)`. Grant
1582
1652
  * it to a role through `defineRole`'s `permissions`, register those roles with
@@ -1982,4 +2052,4 @@ interface StorageContextIn {
1982
2052
  }
1983
2053
  declare const storageRules: <Context extends StorageContextIn = StorageContextIn>(rules: ReadonlyArray<StorageRule<Context>>, options?: StorageRulesOptions) => Middleware<Context, Context>;
1984
2054
  declare const VERSION = "0.0.0";
1985
- export { type ActionBuilder, type ActionCtx, type AggregateIndexDefinition, type AggregateIndexOptions, type AggregateOp, type ArgsValidator, type Component, type ComponentFunctions, type CreateOptions, type DataModelInit, type DefineComponentOptions, type DefinePluginOptions, type DefinePolicyInput, type DefinePresenceOptions, type DefineStorageRuleInput, type DurableObjectJurisdiction, type EmptyArgs, type EnvAccessor, type EnvKeyFailure, type EnvShape, 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 InferArgs, type InferEnv, type InlineAggregateIndexOptions, type InlineRankIndexOptions, type InternalActionBuilder, type InternalMutationBuilder, type InternalQueryBuilder, type LifecycleEvent, type LifecycleHandler, type LunoraBuilders, LunoraEnvError, LunoraError, type LunoraErrorCode, 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, asBucketStorage, bindOrm, bindTableFacade, buildRlsReadRegistry, composePluginMiddleware, composeShapeReadWhere, createPolicyDsl, createSecrets, defineAggregateIndex, defineComponent, defineEnv, defineMigration, defineMutator, definePermission, definePlugin, definePolicies, definePolicy, definePresence, defineRankIndex, defineRole, defineSchema, defineSchemaExtension, defineShape, defineStorageRule, defineStorageRules, defineTable, defineVectorIndex, httpAction, httpRoute, httpRouter, initLunora, installPlugins, mask, mergeSchemaExtension, onConnect, onDisconnect, presenceExtension, protectPublic, redactSecrets, rls, serveStorageObject, storageRules };
2055
+ export { type ActionBuilder, type ActionCtx, type AggregateIndexDefinition, type AggregateIndexOptions, type AggregateOp, type ArgsValidator, type Component, type ComponentFunctions, type CreateOptions, type DataModelInit, type DefineComponentOptions, type DefineIdentityOptions, type DefinePluginOptions, type DefinePolicyInput, type DefinePresenceOptions, type DefineStorageRuleInput, type DurableObjectJurisdiction, type EmptyArgs, type EnvAccessor, type EnvKeyFailure, type EnvShape, 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 LunoraBuilders, LunoraEnvError, LunoraError, type LunoraErrorCode, 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, asBucketStorage, bindOrm, bindTableFacade, buildRlsReadRegistry, composePluginMiddleware, composeShapeReadWhere, createPolicyDsl, createSecrets, defineAggregateIndex, defineComponent, defineEnv, defineIdentity, defineMigration, defineMutator, definePermission, definePlugin, definePolicies, definePolicy, definePresence, defineRankIndex, defineRole, defineSchema, defineSchemaExtension, defineShape, defineStorageRule, defineStorageRules, defineTable, defineVectorIndex, httpAction, httpRoute, httpRouter, initLunora, installPlugins, mask, mergeSchemaExtension, onConnect, onDisconnect, presenceExtension, protectPublic, redactSecrets, rls, serveStorageObject, storageRules };
package/dist/index.mjs CHANGED
@@ -5,6 +5,7 @@ export { LunoraEnvError, defineEnv, redactSecrets } from './packem_shared/Lunora
5
5
  export { LunoraError } from './packem_shared/LunoraError-DN7Zhhvu.mjs';
6
6
  export { bindOrm, bindTableFacade } from './packem_shared/bindOrm-Ce57S3N9.mjs';
7
7
  export { httpAction, httpRoute, httpRouter, serveStorageObject } from './packem_shared/httpAction-FLwfsePg.mjs';
8
+ export { defineIdentity } from './packem_shared/defineIdentity-B_9YD46A.mjs';
8
9
  export { onConnect, onDisconnect } from './packem_shared/onConnect-CIPXKPyw.mjs';
9
10
  export { defineMigration } from './packem_shared/defineMigration-CAJLr6fx.mjs';
10
11
  export { defineMutator } from './packem_shared/defineMutator-EIXAWhs9.mjs';
@@ -16,11 +17,11 @@ export { defineShape } from './packem_shared/defineShape-CJ27Wx7o.mjs';
16
17
  export { anyApi } from './types.mjs';
17
18
  export { cronJobs } from '@lunora/scheduler';
18
19
  export { ValidationError, v } from '@lunora/values';
19
- export { buildRlsReadRegistry, composeShapeReadWhere } from './packem_shared/buildRlsReadRegistry-1jexWrb3.mjs';
20
+ export { buildRlsReadRegistry, composeShapeReadWhere } from './packem_shared/buildRlsReadRegistry-Do1CSBTr.mjs';
20
21
  export { createPolicyDsl, definePermission, definePolicies, definePolicy, defineRole } from './packem_shared/createPolicyDsl-De67zPDS.mjs';
21
22
  export { defineStorageRule, defineStorageRules } from './packem_shared/defineStorageRule-qu0mpilX.mjs';
22
- export { mask } from './packem_shared/mask-BV_jNzsN.mjs';
23
- export { rls } from './packem_shared/rls-2Jhd0uev.mjs';
23
+ export { mask } from './packem_shared/mask-CWfMjb5Y.mjs';
24
+ export { rls } from './packem_shared/rls-DhNgKFeW.mjs';
24
25
  export { storageRules } from './packem_shared/storageRules-Cje6Woea.mjs';
25
26
 
26
27
  const VERSION = "0.0.0";
@@ -1,4 +1,4 @@
1
- import { indexRolePermissions, computeReadBaseWhere, permissionName } from './rls-2Jhd0uev.mjs';
1
+ import { indexRolePermissions, computeReadBaseWhere, permissionName } from './rls-DhNgKFeW.mjs';
2
2
  import { r as readRlsTag } from './policy-tag-DvpVH2tv.mjs';
3
3
 
4
4
  const FALSE_PREDICATE = { OR: [] };
@@ -0,0 +1,34 @@
1
+ import { parseValidatorMap, ValidationError } from '@lunora/values';
2
+
3
+ const NON_STRING_USER_ID_KINDS = /* @__PURE__ */ new Set([
4
+ "array",
5
+ "bigint",
6
+ "boolean",
7
+ "bytes",
8
+ "date",
9
+ "null",
10
+ "number",
11
+ "object",
12
+ "optional",
13
+ "record",
14
+ "timestamp"
15
+ ]);
16
+ const defineIdentity = (claims, options = {}) => {
17
+ const map = claims;
18
+ const userIdValidator = map["userId"];
19
+ if (userIdValidator === void 0 || NON_STRING_USER_ID_KINDS.has(userIdValidator.kind)) {
20
+ throw new Error("defineIdentity: the claim map must declare a required string `userId` validator (e.g. `v.string()` or `v.id(...)`)");
21
+ }
22
+ const onInvalid = options.onInvalid ?? "anonymous";
23
+ const validate = (identity) => {
24
+ try {
25
+ parseValidatorMap(map, identity, "identity");
26
+ return { ok: true };
27
+ } catch (error) {
28
+ return { error: error instanceof ValidationError ? error.message : String(error), ok: false };
29
+ }
30
+ };
31
+ return { __lunoraIdentity: true, claims: map, onInvalid, validate };
32
+ };
33
+
34
+ export { defineIdentity };
@@ -60,8 +60,13 @@ const wrapDatabase = (base, perTable, context) => {
60
60
  const rows = await reader.collect();
61
61
  return rows.map((row) => maskRow(row, columns, context));
62
62
  },
63
+ // SECURITY (value oracle): the predicate must see the MASKED row, not
64
+ // the raw stored row — otherwise a caller can `.filter(d => d.ssn ===
65
+ // guess)` to read the value the mask hides. Masking before the
66
+ // predicate keeps filtering on non-masked columns working while
67
+ // redacting masked cells the predicate can observe.
63
68
  filter: (predicate) => wrapReader(
64
- reader.filter((document) => predicate(document)),
69
+ reader.filter((document) => predicate(maskRow(document, columns, context))),
65
70
  columns
66
71
  ),
67
72
  first: async () => {
@@ -120,23 +125,63 @@ const wrapDatabase = (base, perTable, context) => {
120
125
  throw new LunoraError("MASK_UNSUPPORTED", `${method}() over masked column "${offending}" on "${tableName}" is not supported`);
121
126
  }
122
127
  };
128
+ const collectWhereFields = (where, into) => {
129
+ if (!where || typeof where !== "object" || Array.isArray(where)) {
130
+ return;
131
+ }
132
+ for (const [key, value] of Object.entries(where)) {
133
+ if (key === "AND" || key === "OR") {
134
+ if (Array.isArray(value)) {
135
+ for (const clause of value) {
136
+ collectWhereFields(clause, into);
137
+ }
138
+ }
139
+ } else if (key === "NOT") {
140
+ collectWhereFields(value, into);
141
+ } else if (!key.startsWith("__")) {
142
+ into.add(key);
143
+ }
144
+ }
145
+ };
146
+ const assertWhereAllowed = (tableName, where, method) => {
147
+ const columns = perTable.get(tableName);
148
+ if (!columns || where === void 0) {
149
+ return;
150
+ }
151
+ const referenced = /* @__PURE__ */ new Set();
152
+ collectWhereFields(where, referenced);
153
+ for (const field of referenced) {
154
+ if (field in columns) {
155
+ throw new LunoraError("MASK_UNSUPPORTED", `${method}() filtering "${tableName}" by masked column "${field}" is not supported`);
156
+ }
157
+ }
158
+ };
123
159
  const wrapped = {
124
160
  ...base,
125
161
  aggregate(tableName, options) {
126
162
  assertReductionAllowed(tableName, [options.field], "aggregate");
127
163
  return base.aggregate(tableName, options);
128
164
  },
165
+ count(tableName, whereOrArgs) {
166
+ const wrapper = whereOrArgs && typeof whereOrArgs === "object" && !Array.isArray(whereOrArgs) ? whereOrArgs : void 0;
167
+ const where = wrapper && ("where" in wrapper || "baseWhere" in wrapper || "restrictsCounts" in wrapper) ? wrapper.where : whereOrArgs;
168
+ assertWhereAllowed(tableName, where, "count");
169
+ return base.count(tableName, whereOrArgs);
170
+ },
129
171
  async findFirst(tableName, args) {
172
+ assertWhereAllowed(tableName, args?.where, "findFirst");
130
173
  const row = await base.findFirst(tableName, args);
131
174
  const columns = perTable.get(tableName);
132
175
  return row && columns ? maskRow(row, columns, context) : row;
133
176
  },
134
177
  async findFirstOrThrow(tableName, args) {
178
+ assertWhereAllowed(tableName, args?.where, "findFirstOrThrow");
135
179
  const row = await base.findFirstOrThrow(tableName, args);
136
180
  const columns = perTable.get(tableName);
137
181
  return columns ? maskRow(row, columns, context) : row;
138
182
  },
139
183
  async findMany(tableName, args) {
184
+ assertWhereAllowed(tableName, args?.where, "findMany");
140
185
  const page = await base.findMany(tableName, args);
141
186
  const columns = perTable.get(tableName);
142
187
  return columns ? maskPage(page, columns, context) : page;
@@ -245,7 +245,6 @@ const isFacadeEntry = (value) => {
245
245
  return typeof candidate["findMany"] === "function" && typeof candidate["withSearchIndex"] === "function";
246
246
  };
247
247
  const wrapDatabase = (base, raw, perTable, context) => {
248
- const route = (tableName) => perTable.has(tableName) ? raw : base;
249
248
  const readBaseCache = /* @__PURE__ */ new Map();
250
249
  const readBase = (tableName) => {
251
250
  const cached = readBaseCache.get(tableName);
@@ -263,6 +262,7 @@ const wrapDatabase = (base, raw, perTable, context) => {
263
262
  readBaseCache.set(tableName, result);
264
263
  return result;
265
264
  };
265
+ const route = (tableName) => readBase(tableName).restricts ? raw : base;
266
266
  const relationReadFilter = (table) => readBase(table).baseWhere;
267
267
  const locateRow = async (id, expectedTable) => {
268
268
  if (raw.lookupById) {
@@ -300,7 +300,7 @@ const wrapDatabase = (base, raw, perTable, context) => {
300
300
  const nextRow = computeNextRow ? computeNextRow(located.row) : void 0;
301
301
  const writeOk = evaluateWrite(policies, op, { ...context, row: located.row }, nextRow);
302
302
  if (!writeOk) {
303
- throw new LunoraError("FORBIDDEN", `${op} on "${located.tableName}" denied by policy`);
303
+ throw new LunoraError("FORBIDDEN", `${op} denied by policy`);
304
304
  }
305
305
  }
306
306
  return perform(raw);
@@ -366,7 +366,10 @@ const wrapDatabase = (base, raw, perTable, context) => {
366
366
  return base.get(id, expectedTable);
367
367
  }
368
368
  const { baseWhere, restricts } = readBase(located.tableName);
369
- if (!restricts || !baseWhere) {
369
+ if (!restricts) {
370
+ return base.get(id, expectedTable);
371
+ }
372
+ if (!baseWhere) {
370
373
  return located.row;
371
374
  }
372
375
  const allowed = await raw.findFirst(located.tableName, { baseWhere, limit: 1, where: { _id: id } });
@@ -45,10 +45,10 @@ type PolicyDecisionOf<DM, REL extends Record<keyof DM, object>, T extends keyof
45
45
  * unknown table, a stray column, or a relation predicate naming a relation the
46
46
  * table does not declare is a compile error rather than a silent runtime deny.
47
47
  */
48
- interface TypedDefinePolicyInput<DM, REL extends Record<keyof DM, object>, T extends keyof DM, Context = unknown> {
48
+ interface TypedDefinePolicyInput<DM, REL extends Record<keyof DM, object>, T extends keyof DM, Context = unknown, Identity = Record<string, unknown>> {
49
49
  on: PolicyOperation;
50
50
  table: T;
51
- when: (context: PolicyContext<Context>) => PolicyDecisionOf<DM, REL, T>;
51
+ when: (context: PolicyContext<Context, Identity>) => PolicyDecisionOf<DM, REL, T>;
52
52
  }
53
53
  /**
54
54
  * Context handed to a policy. `auth.roles` is the per-request role list,
@@ -59,7 +59,7 @@ interface TypedDefinePolicyInput<DM, REL extends Record<keyof DM, object>, T ext
59
59
  * pre-write row; for `insert` it is the candidate document. `ctx` is the full
60
60
  * procedure context the middleware closed over.
61
61
  */
62
- interface PolicyContext<Context = unknown> {
62
+ interface PolicyContext<Context = unknown, Identity = Record<string, unknown>> {
63
63
  readonly auth: {
64
64
  /**
65
65
  * `true` when any of the request's `roles` grants `permission` (passed
@@ -68,7 +68,13 @@ interface PolicyContext<Context = unknown> {
68
68
  * when none of the request's roles lists the permission.
69
69
  */
70
70
  readonly can: (permission: Permission | string) => boolean;
71
- readonly identity?: Record<string, unknown> | null;
71
+ /**
72
+ * The resolved identity, typed to the `Identity` type parameter bound on
73
+ * `createPolicyDsl` — e.g. the app's `defineIdentity(...)` claim type via
74
+ * the `InferIdentity` helper; otherwise the untyped claim bag.
75
+ * `undefined`/`null` when the request is anonymous.
76
+ */
77
+ readonly identity?: Identity | null;
72
78
  readonly roles: ReadonlyArray<string>;
73
79
  readonly userId: null | string;
74
80
  };
@@ -45,10 +45,10 @@ type PolicyDecisionOf<DM, REL extends Record<keyof DM, object>, T extends keyof
45
45
  * unknown table, a stray column, or a relation predicate naming a relation the
46
46
  * table does not declare is a compile error rather than a silent runtime deny.
47
47
  */
48
- interface TypedDefinePolicyInput<DM, REL extends Record<keyof DM, object>, T extends keyof DM, Context = unknown> {
48
+ interface TypedDefinePolicyInput<DM, REL extends Record<keyof DM, object>, T extends keyof DM, Context = unknown, Identity = Record<string, unknown>> {
49
49
  on: PolicyOperation;
50
50
  table: T;
51
- when: (context: PolicyContext<Context>) => PolicyDecisionOf<DM, REL, T>;
51
+ when: (context: PolicyContext<Context, Identity>) => PolicyDecisionOf<DM, REL, T>;
52
52
  }
53
53
  /**
54
54
  * Context handed to a policy. `auth.roles` is the per-request role list,
@@ -59,7 +59,7 @@ interface TypedDefinePolicyInput<DM, REL extends Record<keyof DM, object>, T ext
59
59
  * pre-write row; for `insert` it is the candidate document. `ctx` is the full
60
60
  * procedure context the middleware closed over.
61
61
  */
62
- interface PolicyContext<Context = unknown> {
62
+ interface PolicyContext<Context = unknown, Identity = Record<string, unknown>> {
63
63
  readonly auth: {
64
64
  /**
65
65
  * `true` when any of the request's `roles` grants `permission` (passed
@@ -68,7 +68,13 @@ interface PolicyContext<Context = unknown> {
68
68
  * when none of the request's roles lists the permission.
69
69
  */
70
70
  readonly can: (permission: Permission | string) => boolean;
71
- readonly identity?: Record<string, unknown> | null;
71
+ /**
72
+ * The resolved identity, typed to the `Identity` type parameter bound on
73
+ * `createPolicyDsl` — e.g. the app's `defineIdentity(...)` claim type via
74
+ * the `InferIdentity` helper; otherwise the untyped claim bag.
75
+ * `undefined`/`null` when the request is anonymous.
76
+ */
77
+ readonly identity?: Identity | null;
72
78
  readonly roles: ReadonlyArray<string>;
73
79
  readonly userId: null | string;
74
80
  };
@@ -1,4 +1,4 @@
1
- import { P as PolicyOperation, R as Role, a as Policy } from "../packem_shared/types.d-DmvyEMD6.mjs";
1
+ import { P as PolicyOperation, R as Role, a as Policy } from "../packem_shared/types.d-BB3pjV0m.mjs";
2
2
  import "../data-model.mjs";
3
3
  /**
4
4
  * The slice of a request identity a policy reads. Mirrors the
@@ -1,4 +1,4 @@
1
- import { P as PolicyOperation, R as Role, a as Policy } from "../packem_shared/types.d-BDY0FYHK.js";
1
+ import { P as PolicyOperation, R as Role, a as Policy } from "../packem_shared/types.d-Cxl6ndhm.js";
2
2
  import "../data-model.js";
3
3
  /**
4
4
  * The slice of a request identity a policy reads. Mirrors the
@@ -1,4 +1,4 @@
1
- import { indexRolePermissions, computeReadBaseWhere, matchesWhere, evaluateWrite, permissionName } from '../packem_shared/rls-2Jhd0uev.mjs';
1
+ import { indexRolePermissions, computeReadBaseWhere, matchesWhere, evaluateWrite, permissionName } from '../packem_shared/rls-DhNgKFeW.mjs';
2
2
 
3
3
  const expectPolicy = (policies, options = {}) => {
4
4
  const rolePermissions = indexRolePermissions(options.roles);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/server",
3
- "version": "1.0.0-alpha.10",
3
+ "version": "1.0.0-alpha.12",
4
4
  "description": "Server primitives for Lunora: defineSchema, defineTable, query, mutation, and action",
5
5
  "keywords": [
6
6
  "backend",