@lunora/server 1.0.0-alpha.97 → 1.0.0-alpha.99

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
@@ -6,8 +6,8 @@ import { LunoraError } from '@lunora/errors';
6
6
  export { LunoraError, type LunoraErrorCode } from '@lunora/errors';
7
7
  import { Context, Hono } from 'hono';
8
8
  import { WhereOperators, QueryArgs as QueryArgs$2 } from "./data-model.mjs";
9
- import { W as WhereInput, a as Policy, b as RlsOptions, c as Permission, R as Role, T as TypedDefinePolicyInput, D as DefinePolicyInput } from "./packem_shared/types.d-B-pIn8rE.mjs";
10
- export type { d as PolicyContext, e as PolicyDecision, f as PolicyDecisionOf, P as PolicyOperation } from "./packem_shared/types.d-B-pIn8rE.mjs";
9
+ import { W as WhereInput, a as Policy, b as RlsOptions, c as Permission, R as Role, T as TypedDefinePolicyInput, D as DefinePolicyInput } from "./packem_shared/types.d-B7FLdYUD.mjs";
10
+ export type { d as PolicyContext, e as PolicyDecision, f as PolicyDecisionOf, P as PolicyOperation } from "./packem_shared/types.d-B7FLdYUD.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
13
  * Options for the `.stream()` terminal. `durable: true` is shorthand for
@@ -2268,9 +2268,33 @@ interface DatabaseWriterLike {
2268
2268
  * `ctx`.
2269
2269
  */
2270
2270
  type RlsDatabase = DatabaseWriterLike;
2271
- /** Roles list source on the context. Tolerant of older auth states. */
2271
+ /**
2272
+ * The slice of `ctx.auth` the policy middlewares read.
2273
+ *
2274
+ * `roles` has TWO sources and the effective list is their union, because the two
2275
+ * answer different questions and neither subsumes the other.
2276
+ *
2277
+ * The `roles` CLAIM on the resolved identity ({@link readIdentityRoles}) is what
2278
+ * the identity provider asserted about the caller. `ctx.auth.roles`, set by an
2279
+ * upstream middleware, is what a request-time mapping derived —
2280
+ * `@lunora/cloudflare-access`'s `accessRoles()` is the shipped example: the
2281
+ * Access envelope carries `groups`, never `roles`, and that middleware's entire
2282
+ * job is to map verified groups onto role labels here.
2283
+ *
2284
+ * Reading only the claim silently drops the second, which does not fail loudly —
2285
+ * a role-gated ALLOW branch stops firing and users lose access, and a role-gated
2286
+ * DENY branch stops firing and rows LEAK. `accessRoles` declares its own context
2287
+ * type, so there is no compile-time signal either way.
2288
+ *
2289
+ * What is deliberately absent is the same field on the TEST harness: see
2290
+ * `TestIdentity` in `./testing`. A middleware setting `ctx.auth.roles` is a real
2291
+ * request-path producer; a test setting it directly is a world with no producer
2292
+ * at all, which is how role-gated policies passed their tests while evaluating
2293
+ * against an empty list in production.
2294
+ */
2272
2295
  type AuthLike = {
2273
2296
  getIdentity?: () => Promise<Record<string, unknown> | null>;
2297
+ /** Role labels contributed by an upstream middleware, unioned with the identity's `roles` claim. */
2274
2298
  roles?: ReadonlyArray<string>;
2275
2299
  userId?: null | string;
2276
2300
  };
@@ -2294,6 +2318,7 @@ interface MaskContext<Context = unknown> {
2294
2318
  /** `true` when any of the request's `roles` grants `permission` (see {@link MaskOptions.roles}). Fails closed for unregistered roles. */
2295
2319
  readonly can: (permission: Permission | string) => boolean;
2296
2320
  readonly identity?: Record<string, unknown> | null;
2321
+ /** Role labels from the identity's `roles` claim (see `PolicyContext.auth.roles`). */
2297
2322
  readonly roles: ReadonlyArray<string>;
2298
2323
  readonly userId: null | string;
2299
2324
  };
@@ -2954,8 +2979,6 @@ interface ShapeReadWhereRequest {
2954
2979
  readonly identity: Record<string, unknown> | null;
2955
2980
  /** `true` when the schema is `.rls("required")` — gates the fail-closed branch. */
2956
2981
  readonly rlsRequired: boolean;
2957
- /** Role labels the request carries (drives `auth.can(...)`). */
2958
- readonly roles: ReadonlyArray<string>;
2959
2982
  /** The shape's own predicate (`where(ctx, args)`). */
2960
2983
  readonly shapeWhere: WhereInput;
2961
2984
  /** Logical table the shape replicates. */
@@ -3079,6 +3102,7 @@ interface StorageRuleContext<Context = unknown> {
3079
3102
  readonly auth: {
3080
3103
  readonly can: (permission: Permission | string) => boolean;
3081
3104
  readonly identity?: Record<string, unknown> | null;
3105
+ /** Role labels from the identity's `roles` claim (see `PolicyContext.auth.roles`). */
3082
3106
  readonly roles: ReadonlyArray<string>;
3083
3107
  readonly userId: null | string;
3084
3108
  };
@@ -3093,6 +3117,14 @@ interface StorageRule<Context = unknown> {
3093
3117
  * (`ctx.storage.bucketName`, or the bucket selected via `ctx.storage.bucket(name)`).
3094
3118
  * A rule only applies to operations on its own bucket. The unnamed bucket is
3095
3119
  * `"default"`. Also surfaced in the studio's access-rules view.
3120
+ *
3121
+ * Must name a bucket the request's storage can address, or the middleware
3122
+ * throws `INTERNAL`: an unaddressable rule governs nothing, which leaves its
3123
+ * operation open rather than locked down. Typed `string` rather than the
3124
+ * generated `StorageBucketName` union deliberately — that union is not the
3125
+ * set of registered buckets (it is seeded partly from these very rules), so
3126
+ * narrowing to it would reject valid names and still admit typos. The
3127
+ * runtime check in `./middleware` is the one that has the real set.
3096
3128
  */
3097
3129
  readonly bucket: string;
3098
3130
  readonly on: StorageOperation;
package/dist/index.d.ts CHANGED
@@ -6,8 +6,8 @@ import { LunoraError } from '@lunora/errors';
6
6
  export { LunoraError, type LunoraErrorCode } from '@lunora/errors';
7
7
  import { Context, Hono } from 'hono';
8
8
  import { WhereOperators, QueryArgs as QueryArgs$2 } from "./data-model.js";
9
- import { W as WhereInput, a as Policy, b as RlsOptions, c as Permission, R as Role, T as TypedDefinePolicyInput, D as DefinePolicyInput } from "./packem_shared/types.d-CdPDEjhz.js";
10
- export type { d as PolicyContext, e as PolicyDecision, f as PolicyDecisionOf, P as PolicyOperation } from "./packem_shared/types.d-CdPDEjhz.js";
9
+ import { W as WhereInput, a as Policy, b as RlsOptions, c as Permission, R as Role, T as TypedDefinePolicyInput, D as DefinePolicyInput } from "./packem_shared/types.d-BCtjbIzl.js";
10
+ export type { d as PolicyContext, e as PolicyDecision, f as PolicyDecisionOf, P as PolicyOperation } from "./packem_shared/types.d-BCtjbIzl.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
13
  * Options for the `.stream()` terminal. `durable: true` is shorthand for
@@ -2268,9 +2268,33 @@ interface DatabaseWriterLike {
2268
2268
  * `ctx`.
2269
2269
  */
2270
2270
  type RlsDatabase = DatabaseWriterLike;
2271
- /** Roles list source on the context. Tolerant of older auth states. */
2271
+ /**
2272
+ * The slice of `ctx.auth` the policy middlewares read.
2273
+ *
2274
+ * `roles` has TWO sources and the effective list is their union, because the two
2275
+ * answer different questions and neither subsumes the other.
2276
+ *
2277
+ * The `roles` CLAIM on the resolved identity ({@link readIdentityRoles}) is what
2278
+ * the identity provider asserted about the caller. `ctx.auth.roles`, set by an
2279
+ * upstream middleware, is what a request-time mapping derived —
2280
+ * `@lunora/cloudflare-access`'s `accessRoles()` is the shipped example: the
2281
+ * Access envelope carries `groups`, never `roles`, and that middleware's entire
2282
+ * job is to map verified groups onto role labels here.
2283
+ *
2284
+ * Reading only the claim silently drops the second, which does not fail loudly —
2285
+ * a role-gated ALLOW branch stops firing and users lose access, and a role-gated
2286
+ * DENY branch stops firing and rows LEAK. `accessRoles` declares its own context
2287
+ * type, so there is no compile-time signal either way.
2288
+ *
2289
+ * What is deliberately absent is the same field on the TEST harness: see
2290
+ * `TestIdentity` in `./testing`. A middleware setting `ctx.auth.roles` is a real
2291
+ * request-path producer; a test setting it directly is a world with no producer
2292
+ * at all, which is how role-gated policies passed their tests while evaluating
2293
+ * against an empty list in production.
2294
+ */
2272
2295
  type AuthLike = {
2273
2296
  getIdentity?: () => Promise<Record<string, unknown> | null>;
2297
+ /** Role labels contributed by an upstream middleware, unioned with the identity's `roles` claim. */
2274
2298
  roles?: ReadonlyArray<string>;
2275
2299
  userId?: null | string;
2276
2300
  };
@@ -2294,6 +2318,7 @@ interface MaskContext<Context = unknown> {
2294
2318
  /** `true` when any of the request's `roles` grants `permission` (see {@link MaskOptions.roles}). Fails closed for unregistered roles. */
2295
2319
  readonly can: (permission: Permission | string) => boolean;
2296
2320
  readonly identity?: Record<string, unknown> | null;
2321
+ /** Role labels from the identity's `roles` claim (see `PolicyContext.auth.roles`). */
2297
2322
  readonly roles: ReadonlyArray<string>;
2298
2323
  readonly userId: null | string;
2299
2324
  };
@@ -2954,8 +2979,6 @@ interface ShapeReadWhereRequest {
2954
2979
  readonly identity: Record<string, unknown> | null;
2955
2980
  /** `true` when the schema is `.rls("required")` — gates the fail-closed branch. */
2956
2981
  readonly rlsRequired: boolean;
2957
- /** Role labels the request carries (drives `auth.can(...)`). */
2958
- readonly roles: ReadonlyArray<string>;
2959
2982
  /** The shape's own predicate (`where(ctx, args)`). */
2960
2983
  readonly shapeWhere: WhereInput;
2961
2984
  /** Logical table the shape replicates. */
@@ -3079,6 +3102,7 @@ interface StorageRuleContext<Context = unknown> {
3079
3102
  readonly auth: {
3080
3103
  readonly can: (permission: Permission | string) => boolean;
3081
3104
  readonly identity?: Record<string, unknown> | null;
3105
+ /** Role labels from the identity's `roles` claim (see `PolicyContext.auth.roles`). */
3082
3106
  readonly roles: ReadonlyArray<string>;
3083
3107
  readonly userId: null | string;
3084
3108
  };
@@ -3093,6 +3117,14 @@ interface StorageRule<Context = unknown> {
3093
3117
  * (`ctx.storage.bucketName`, or the bucket selected via `ctx.storage.bucket(name)`).
3094
3118
  * A rule only applies to operations on its own bucket. The unnamed bucket is
3095
3119
  * `"default"`. Also surfaced in the studio's access-rules view.
3120
+ *
3121
+ * Must name a bucket the request's storage can address, or the middleware
3122
+ * throws `INTERNAL`: an unaddressable rule governs nothing, which leaves its
3123
+ * operation open rather than locked down. Typed `string` rather than the
3124
+ * generated `StorageBucketName` union deliberately — that union is not the
3125
+ * set of registered buckets (it is seeded partly from these very rules), so
3126
+ * narrowing to it would reject valid names and still admit typos. The
3127
+ * runtime check in `./middleware` is the one that has the real set.
3096
3128
  */
3097
3129
  readonly bucket: string;
3098
3130
  readonly on: StorageOperation;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{ACTION_CACHE_DEFAULT_TTL_MS as t,ACTION_CACHE_TABLE as n,actionCacheExtension as i,cacheKeyFor as a,defineActionCache as f}from"./packem_shared/ACTION_CACHE_DEFAULT_TTL_MS-OuQ-AJjz.mjs";import{initLunora as m}from"./packem_shared/initLunora-D5TSiy5j.mjs";import{createSecrets as p}from"./packem_shared/createSecrets-D6rLB42U.mjs";import{flushDeferredDeletes as c,withDeferredDeletes as l}from"./packem_shared/flushDeferredDeletes-DEJXlhop.mjs";import{DOCUMENT_HISTORY_REDACTED_FIELDS as u,DOCUMENT_HISTORY_TABLE as S,defineDocumentHistory as T,documentHistoryExtension as h}from"./packem_shared/DOCUMENT_HISTORY_REDACTED_FIELDS-wgfbacHx.mjs";import{LunoraEnvError as g,defineEnv as R,redactSecrets as _}from"./packem_shared/LunoraEnvError-CXbrsFI8.mjs";import{bindOrm as L,bindTableFacade as C}from"./packem_shared/bindOrm-ChQydkdL.mjs";import{httpAction as y,httpRoute as M,httpRouter as P,isSafeHeaderValue as b,serveStorageObject as F}from"./packem_shared/httpAction-B8X6jXen.mjs";import{defineIdentity as H}from"./packem_shared/defineIdentity-DwkNKwYa.mjs";import{onConnect as U,onDisconnect as k,onShardInit as v}from"./packem_shared/onConnect-BLRoOpv2.mjs";import{DEFAULT_LIMIT as V,DEFAULT_MAX_LIMIT as w,clampLimit as j,defineListArgs as W}from"./packem_shared/DEFAULT_LIMIT-DC-M6faS.mjs";import{defineMigration as J}from"./packem_shared/defineMigration-CXOS0Bvq.mjs";import{defineMutator as Q}from"./packem_shared/defineMutator-DKy8UtbB.mjs";import{c as q,d as z,a as G,b as Z,e as $,f as ee,g as oe,h as re,i as te,j as ne,k as ie,m as ae}from"./packem_shared/plugin-yKCbHnlj.mjs";import{PRESENCE_DEFAULT_TTL_MS as se,PRESENCE_TABLE as me,definePresence as de,presenceExtension as pe}from"./packem_shared/PRESENCE_DEFAULT_TTL_MS-lsooaSaF.mjs";import{protectPublic as ce}from"./packem_shared/protectPublic-Csf6ObbJ.mjs";import{onQueryChange as Ee}from"./packem_shared/onQueryChange-pRK6Dh3g.mjs";import{defineShape as Se}from"./packem_shared/defineShape-BWgwFuMT.mjs";import{anyApi as he}from"./types.mjs";import{LunoraError as ge}from"@lunora/errors";import{cronJobs as _e}from"@lunora/scheduler";import{ValidationError as Le,v as Ce}from"@lunora/values";import{allowAll as ye,deny as Me,isDeny as Pe,toWhereInput as be}from"./packem_shared/allowAll-DkRAgItV.mjs";import{asBucketStorage as Oe}from"./packem_shared/asBucketStorage-BthCnWop.mjs";import{buildMaskRegistry as Ne}from"./packem_shared/buildMaskRegistry-DpWMtalG.mjs";import{buildRlsReadRegistry as ke,composeShapeReadWhere as ve}from"./packem_shared/buildRlsReadRegistry-CIGmNx0b.mjs";import{createPolicyDsl as Ve,definePermission as we,definePolicies as je,definePolicy as We,defineRole as Ye}from"./packem_shared/createPolicyDsl-BZa6SqLJ.mjs";import{defineStorageRule as Ke,defineStorageRules as Qe}from"./packem_shared/defineStorageRule-Dv4nJE0H.mjs";import{mask as qe}from"./packem_shared/mask-pIhIWBcr.mjs";import{r as Ge}from"./packem_shared/middleware-Cz5s6CH-.mjs";import{storageRules as $e}from"./packem_shared/storageRules-DHvF6Fjq.mjs";const e="0.0.0";export{t as ACTION_CACHE_DEFAULT_TTL_MS,n as ACTION_CACHE_TABLE,V as DEFAULT_LIMIT,w as DEFAULT_MAX_LIMIT,u as DOCUMENT_HISTORY_REDACTED_FIELDS,S as DOCUMENT_HISTORY_TABLE,g as LunoraEnvError,ge as LunoraError,se as PRESENCE_DEFAULT_TTL_MS,me as PRESENCE_TABLE,e as VERSION,Le as ValidationError,i as actionCacheExtension,ye as allowAll,he as anyApi,Oe as asBucketStorage,L as bindOrm,C as bindTableFacade,Ne as buildMaskRegistry,ke as buildRlsReadRegistry,a as cacheKeyFor,j as clampLimit,q as composePluginMiddleware,ve as composeShapeReadWhere,Ve as createPolicyDsl,p as createSecrets,_e as cronJobs,f as defineActionCache,z as defineAggregateIndex,G as defineComponent,T as defineDocumentHistory,R as defineEnv,H as defineIdentity,W as defineListArgs,J as defineMigration,Q as defineMutator,we as definePermission,Z as definePlugin,je as definePolicies,We as definePolicy,de as definePresence,$ as defineRankIndex,Ye as defineRole,ee as defineSchema,oe as defineSchemaExtension,Se as defineShape,Ke as defineStorageRule,Qe as defineStorageRules,re as defineTable,te as defineVectorIndex,Me as deny,h as documentHistoryExtension,c as flushDeferredDeletes,y as httpAction,M as httpRoute,P as httpRouter,ne as indexFieldsFromSchema,m as initLunora,ie as installPlugins,Pe as isDeny,b as isSafeHeaderValue,qe as mask,ae as mergeSchemaExtension,U as onConnect,k as onDisconnect,Ee as onQueryChange,v as onShardInit,pe as presenceExtension,ce as protectPublic,_ as redactSecrets,Ge as rls,F as serveStorageObject,$e as storageRules,be as toWhereInput,Ce as v,l as withDeferredDeletes};
1
+ import{ACTION_CACHE_DEFAULT_TTL_MS as t,ACTION_CACHE_TABLE as n,actionCacheExtension as i,cacheKeyFor as a,defineActionCache as f}from"./packem_shared/ACTION_CACHE_DEFAULT_TTL_MS-jQGMYvMp.mjs";import{initLunora as m}from"./packem_shared/initLunora-D5TSiy5j.mjs";import{createSecrets as p}from"./packem_shared/createSecrets-D6rLB42U.mjs";import{flushDeferredDeletes as c,withDeferredDeletes as l}from"./packem_shared/flushDeferredDeletes-DEJXlhop.mjs";import{DOCUMENT_HISTORY_REDACTED_FIELDS as u,DOCUMENT_HISTORY_TABLE as S,defineDocumentHistory as T,documentHistoryExtension as h}from"./packem_shared/DOCUMENT_HISTORY_REDACTED_FIELDS-DnNs9p7U.mjs";import{LunoraEnvError as g,defineEnv as R,redactSecrets as _}from"./packem_shared/LunoraEnvError-CXbrsFI8.mjs";import{bindOrm as L,bindTableFacade as C}from"./packem_shared/bindOrm-ChQydkdL.mjs";import{httpAction as y,httpRoute as M,httpRouter as P,isSafeHeaderValue as b,serveStorageObject as F}from"./packem_shared/httpAction-B8X6jXen.mjs";import{defineIdentity as H}from"./packem_shared/defineIdentity-DwkNKwYa.mjs";import{onConnect as U,onDisconnect as k,onShardInit as v}from"./packem_shared/onConnect-BLRoOpv2.mjs";import{DEFAULT_LIMIT as V,DEFAULT_MAX_LIMIT as w,clampLimit as j,defineListArgs as W}from"./packem_shared/DEFAULT_LIMIT-DC-M6faS.mjs";import{defineMigration as J}from"./packem_shared/defineMigration-CXOS0Bvq.mjs";import{defineMutator as Q}from"./packem_shared/defineMutator-DKy8UtbB.mjs";import{c as q,d as z,a as G,b as Z,e as $,f as ee,g as oe,h as re,i as te,j as ne,k as ie,m as ae}from"./packem_shared/plugin-yKCbHnlj.mjs";import{PRESENCE_DEFAULT_TTL_MS as se,PRESENCE_TABLE as me,definePresence as de,presenceExtension as pe}from"./packem_shared/PRESENCE_DEFAULT_TTL_MS-lsooaSaF.mjs";import{protectPublic as ce}from"./packem_shared/protectPublic-Csf6ObbJ.mjs";import{onQueryChange as Ee}from"./packem_shared/onQueryChange-pRK6Dh3g.mjs";import{defineShape as Se}from"./packem_shared/defineShape-BWgwFuMT.mjs";import{anyApi as he}from"./types.mjs";import{LunoraError as ge}from"@lunora/errors";import{cronJobs as _e}from"@lunora/scheduler";import{ValidationError as Le,v as Ce}from"@lunora/values";import{allowAll as ye,deny as Me,isDeny as Pe,toWhereInput as be}from"./packem_shared/allowAll-DkRAgItV.mjs";import{asBucketStorage as Oe}from"./packem_shared/asBucketStorage-BthCnWop.mjs";import{buildMaskRegistry as Ne}from"./packem_shared/buildMaskRegistry-DpWMtalG.mjs";import{buildRlsReadRegistry as ke,composeShapeReadWhere as ve}from"./packem_shared/buildRlsReadRegistry-C1JR4RYW.mjs";import{createPolicyDsl as Ve,definePermission as we,definePolicies as je,definePolicy as We,defineRole as Ye}from"./packem_shared/createPolicyDsl-BZa6SqLJ.mjs";import{defineStorageRule as Ke,defineStorageRules as Qe}from"./packem_shared/defineStorageRule-Dv4nJE0H.mjs";import{mask as qe}from"./packem_shared/mask-BRaPah32.mjs";import{r as Ge}from"./packem_shared/middleware-Dh06LOt-.mjs";import{storageRules as $e}from"./packem_shared/storageRules-oiqaPTsX.mjs";const e="0.0.0";export{t as ACTION_CACHE_DEFAULT_TTL_MS,n as ACTION_CACHE_TABLE,V as DEFAULT_LIMIT,w as DEFAULT_MAX_LIMIT,u as DOCUMENT_HISTORY_REDACTED_FIELDS,S as DOCUMENT_HISTORY_TABLE,g as LunoraEnvError,ge as LunoraError,se as PRESENCE_DEFAULT_TTL_MS,me as PRESENCE_TABLE,e as VERSION,Le as ValidationError,i as actionCacheExtension,ye as allowAll,he as anyApi,Oe as asBucketStorage,L as bindOrm,C as bindTableFacade,Ne as buildMaskRegistry,ke as buildRlsReadRegistry,a as cacheKeyFor,j as clampLimit,q as composePluginMiddleware,ve as composeShapeReadWhere,Ve as createPolicyDsl,p as createSecrets,_e as cronJobs,f as defineActionCache,z as defineAggregateIndex,G as defineComponent,T as defineDocumentHistory,R as defineEnv,H as defineIdentity,W as defineListArgs,J as defineMigration,Q as defineMutator,we as definePermission,Z as definePlugin,je as definePolicies,We as definePolicy,de as definePresence,$ as defineRankIndex,Ye as defineRole,ee as defineSchema,oe as defineSchemaExtension,Se as defineShape,Ke as defineStorageRule,Qe as defineStorageRules,re as defineTable,te as defineVectorIndex,Me as deny,h as documentHistoryExtension,c as flushDeferredDeletes,y as httpAction,M as httpRoute,P as httpRouter,ne as indexFieldsFromSchema,m as initLunora,ie as installPlugins,Pe as isDeny,b as isSafeHeaderValue,qe as mask,ae as mergeSchemaExtension,U as onConnect,k as onDisconnect,Ee as onQueryChange,v as onShardInit,pe as presenceExtension,ce as protectPublic,_ as redactSecrets,Ge as rls,F as serveStorageObject,$e as storageRules,be as toWhereInput,Ce as v,l as withDeferredDeletes};
@@ -1 +1 @@
1
- import{isLunoraError as S}from"@lunora/errors";import{v as d}from"@lunora/values";import{e as f,d as U}from"./wire-codec-uveYSVra.mjs";import{s as D}from"./stable-key-B_BlboiY.mjs";import{initLunora as F}from"./initLunora-D5TSiy5j.mjs";import{g as k,h as q,a as B}from"./plugin-yKCbHnlj.mjs";const P=i=>D(f(i)),R=3600*1e3,H=512*1024,V=8,b=64,K=64,W=256,$=4096,u="actionCache",h="entries",c=`${u}_${h}`,T=i=>i===void 0?"":P(i),x=async(i,m)=>{const A=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(`${i}\0${m}`));return[...new Uint8Array(A)].map(y=>y.toString(16).padStart(2,"0")).join("")},G=k(u,{tables:{[h]:q({expiresAt:d.number(),key:d.string(),name:d.string(),value:d.string()}).index("byKey",["key"],{unique:!0}).index("byName",["name"]).index("byExpiresAt",["expiresAt"])}}),{internalMutation:J}=F.dataModel().create(),te=(i={})=>{const m=i.ttlMs!==void 0&&Number.isFinite(i.ttlMs)?Math.max(1,Math.floor(i.ttlMs)):R,A=i.maxValueBytes!==void 0&&Number.isFinite(i.maxValueBytes)?Math.max(1,Math.floor(i.maxValueBytes)):H,y=async(e,t)=>{const n=(await e.db.query(c).withIndex("byExpiresAt").order("asc").take(V)).filter(r=>r.expiresAt<=t);await Promise.all(n.map(async r=>e.db.delete(r._id)))},C=async(e,t)=>e.db.query(c).withIndex("byKey",a=>a.eq("key",t)).first(),N=new Set(["CONFLICT","NOT_FOUND","NOT_UNIQUE"]),I=async(e,t,a)=>{try{await(t?e.db.patch(t._id,{expiresAt:a.expiresAt,value:a.value}):e.db.insert(c,a))}catch(n){if(S(n)&&N.has(n.code))return;throw n}},M=async(e,t,a,n)=>{const r=T(a),s=await x(t,r),l=Date.now(),o=await C(e,s);if(o&&o.expiresAt>l)return U(JSON.parse(o.value)).v;const p=await n(),w=JSON.stringify(f({v:p})),E=Date.now(),O=E+m;return new TextEncoder().encode(w).length<=A?await I(e,o,{expiresAt:O,key:s,name:t,value:w}):o&&await e.db.delete(o._id),await y(e,E),p},L=async(e,t,a)=>{const n=await x(t,T(a)),r=await e.db.query(c).withIndex("byKey",s=>s.eq("key",n)).take(b);await Promise.all(r.map(async s=>e.db.delete(s._id)))},_=async(e,t,a,n)=>{if(n>=K)return{complete:!1,deleted:a};const r=await e.db.query(c).withIndex("byName",s=>s.eq("name",t)).take(b);return r.length===0?{complete:!0,deleted:a}:(await Promise.all(r.map(async s=>e.db.delete(s._id))),_(e,t,a+r.length,n+1))},g=async(e,t)=>_(e,t,0,0),v=J.input({limit:d.optional(d.number())}).mutation(async({args:e,ctx:t})=>{const a=Date.now(),n=e.limit!==void 0&&Number.isFinite(e.limit)?Math.min($,Math.max(1,Math.floor(e.limit))):W,s=(await t.db.query(c).withIndex("byExpiresAt").order("asc").take(n)).filter(l=>l.expiresAt<=a);return await Promise.all(s.map(async l=>t.db.delete(l._id))),{deleted:s.length}});return{...B(u,{extension:G,functions:{purgeExpired:v}}),invalidate:L,invalidateAll:g,wrap:M}};export{R as ACTION_CACHE_DEFAULT_TTL_MS,c as ACTION_CACHE_TABLE,G as actionCacheExtension,x as cacheKeyFor,te as defineActionCache};
1
+ import{isLunoraError as S}from"@lunora/errors";import{v as d}from"@lunora/values";import{e as f,d as U}from"./wire-codec-DRugirMW.mjs";import{s as D}from"./stable-key-B_BlboiY.mjs";import{initLunora as F}from"./initLunora-D5TSiy5j.mjs";import{g as k,h as q,a as B}from"./plugin-yKCbHnlj.mjs";const P=i=>D(f(i)),R=3600*1e3,H=512*1024,V=8,b=64,K=64,W=256,$=4096,u="actionCache",h="entries",c=`${u}_${h}`,T=i=>i===void 0?"":P(i),x=async(i,m)=>{const A=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(`${i}\0${m}`));return[...new Uint8Array(A)].map(y=>y.toString(16).padStart(2,"0")).join("")},G=k(u,{tables:{[h]:q({expiresAt:d.number(),key:d.string(),name:d.string(),value:d.string()}).index("byKey",["key"],{unique:!0}).index("byName",["name"]).index("byExpiresAt",["expiresAt"])}}),{internalMutation:J}=F.dataModel().create(),te=(i={})=>{const m=i.ttlMs!==void 0&&Number.isFinite(i.ttlMs)?Math.max(1,Math.floor(i.ttlMs)):R,A=i.maxValueBytes!==void 0&&Number.isFinite(i.maxValueBytes)?Math.max(1,Math.floor(i.maxValueBytes)):H,y=async(e,t)=>{const n=(await e.db.query(c).withIndex("byExpiresAt").order("asc").take(V)).filter(r=>r.expiresAt<=t);await Promise.all(n.map(async r=>e.db.delete(r._id)))},C=async(e,t)=>e.db.query(c).withIndex("byKey",a=>a.eq("key",t)).first(),N=new Set(["CONFLICT","NOT_FOUND","NOT_UNIQUE"]),I=async(e,t,a)=>{try{await(t?e.db.patch(t._id,{expiresAt:a.expiresAt,value:a.value}):e.db.insert(c,a))}catch(n){if(S(n)&&N.has(n.code))return;throw n}},M=async(e,t,a,n)=>{const r=T(a),s=await x(t,r),l=Date.now(),o=await C(e,s);if(o&&o.expiresAt>l)return U(JSON.parse(o.value)).v;const p=await n(),w=JSON.stringify(f({v:p})),E=Date.now(),O=E+m;return new TextEncoder().encode(w).length<=A?await I(e,o,{expiresAt:O,key:s,name:t,value:w}):o&&await e.db.delete(o._id),await y(e,E),p},L=async(e,t,a)=>{const n=await x(t,T(a)),r=await e.db.query(c).withIndex("byKey",s=>s.eq("key",n)).take(b);await Promise.all(r.map(async s=>e.db.delete(s._id)))},_=async(e,t,a,n)=>{if(n>=K)return{complete:!1,deleted:a};const r=await e.db.query(c).withIndex("byName",s=>s.eq("name",t)).take(b);return r.length===0?{complete:!0,deleted:a}:(await Promise.all(r.map(async s=>e.db.delete(s._id))),_(e,t,a+r.length,n+1))},g=async(e,t)=>_(e,t,0,0),v=J.input({limit:d.optional(d.number())}).mutation(async({args:e,ctx:t})=>{const a=Date.now(),n=e.limit!==void 0&&Number.isFinite(e.limit)?Math.min($,Math.max(1,Math.floor(e.limit))):W,s=(await t.db.query(c).withIndex("byExpiresAt").order("asc").take(n)).filter(l=>l.expiresAt<=a);return await Promise.all(s.map(async l=>t.db.delete(l._id))),{deleted:s.length}});return{...B(u,{extension:G,functions:{purgeExpired:v}}),invalidate:L,invalidateAll:g,wrap:M}};export{R as ACTION_CACHE_DEFAULT_TTL_MS,c as ACTION_CACHE_TABLE,G as actionCacheExtension,x as cacheKeyFor,te as defineActionCache};
@@ -1 +1 @@
1
- import{v as r}from"@lunora/values";import{d as A,e as h}from"./wire-codec-uveYSVra.mjs";import{initLunora as v}from"./initLunora-D5TSiy5j.mjs";import{g as x,h as R,a as U}from"./plugin-yKCbHnlj.mjs";const H=2160*60*60*1e3,q=64*1024,E=200,C=64,F=512,y=16,L=["_commitSeq","seq"],B=["accessToken","apiKey","backupCodes","clientSecret","hashedPassword","password","privateKey","refreshToken","secret","totpSecret"],p="documentHistory",I="versions",l=`${p}_${I}`,P=x(p,{tables:{[I]:R({doc:r.optional(r.string()),documentId:r.string(),op:r.union(r.literal("delete"),r.literal("insert"),r.literal("update")),previous:r.optional(r.string()),recordedAt:r.number(),seq:r.number(),tableName:r.string(),truncated:r.optional(r.boolean())}).commitOrdered().index("byDocumentRecordedAt",["documentId","recordedAt","seq"]).index("byRecordedAt",["recordedAt"])}}),{internalMutation:Y,internalQuery:j}=v.dataModel().create(),X=(d={})=>{const T=d.retentionMs!==void 0&&Number.isFinite(d.retentionMs)?Math.max(1,Math.floor(d.retentionMs)):H,_=d.maxSnapshotBytes!==void 0&&Number.isFinite(d.maxSnapshotBytes)?Math.max(1,Math.floor(d.maxSnapshotBytes)):q,D=new Set([...B,...d.redact??[]]);let b=0;const M=()=>(b+=1,b),m=(e,t=0)=>{if(Array.isArray(e))return t>=y?void 0:e.map(o=>m(o,t+1));if(typeof e!="object"||e===null||Object.getPrototypeOf(e)!==Object.prototype&&Object.getPrototypeOf(e)!==null)return e;if(!(t>=y))return Object.fromEntries(Object.entries(e).filter(([o])=>!D.has(o)).map(([o,i])=>[o,m(i,t+1)]))},f=e=>{if(e===void 0)return;const t=JSON.stringify(h(m(e)));return new TextEncoder().encode(t).length>_?void 0:t},u=async(e,t)=>{const o=f(t.doc),i=f(t.previous),n=t.doc!==void 0&&o===void 0||t.previous!==void 0&&i===void 0;await e.db.insert(l,{documentId:t.documentId,op:t.op,recordedAt:Date.now(),seq:M(),tableName:t.tableName,...n?{truncated:!0}:{},...o===void 0?{}:{doc:o},...i===void 0?{}:{previous:i}})},S=e=>({documentHistoryDelete:e.afterDelete(async(t,o)=>u(t,{documentId:o.id,op:"delete",previous:o.previous,tableName:o.table})),documentHistoryInsert:e.afterInsert(async(t,o)=>u(t,{doc:o.doc,documentId:o.id,op:"insert",tableName:o.table})),documentHistoryUpdate:e.afterUpdate(async(t,o)=>u(t,{doc:o.doc,documentId:o.id,op:"update",previous:o.previous,tableName:o.table}))}),N=j.input({before:r.optional(r.number()),documentId:r.string(),limit:r.optional(r.number())}).query(async({args:e,ctx:t})=>{const o=e.limit!==void 0&&Number.isFinite(e.limit)?Math.max(1,Math.floor(e.limit)):E,i=await t.db.query(l).withIndex("byDocumentRecordedAt",n=>e.before===void 0?n.eq("documentId",e.documentId):n.eq("documentId",e.documentId).lte("recordedAt",e.before)).order("desc").take(o);return i.sort((n,a)=>{for(const c of L){const s=(a[c]??0)-(n[c]??0);if(s!==0)return s}return 0}),i.map(n=>({documentId:n.documentId,op:n.op,recordedAt:n.recordedAt,tableName:n.tableName,...n.doc===void 0?{}:{doc:A(JSON.parse(n.doc))},...n.previous===void 0?{}:{previous:A(JSON.parse(n.previous))},...n.truncated===!0?{truncated:!0}:{}}))}),O=Y.input({limit:r.optional(r.number())}).mutation(async({args:e,ctx:t})=>{const o=Date.now()-T,i=e.limit!==void 0&&Number.isFinite(e.limit)?Math.max(1,Math.floor(e.limit)):F;let n=0;for(let a=0;a<C&&n<i;a+=1){const c=await t.db.query(l).withIndex("byRecordedAt",s=>s.lt("recordedAt",o)).order("asc").take(Math.min(E,i-n));if(c.length===0)return{deleted:n};await Promise.all(c.map(async s=>t.db.delete(s._id))),n+=c.length}return{deleted:n}});return{...U(p,{extension:P,functions:{listForDocument:N,vacuum:O}}),record:S}};export{B as DOCUMENT_HISTORY_REDACTED_FIELDS,l as DOCUMENT_HISTORY_TABLE,X as defineDocumentHistory,P as documentHistoryExtension};
1
+ import{v as r}from"@lunora/values";import{d as A,e as h}from"./wire-codec-DRugirMW.mjs";import{initLunora as v}from"./initLunora-D5TSiy5j.mjs";import{g as x,h as R,a as U}from"./plugin-yKCbHnlj.mjs";const H=2160*60*60*1e3,q=64*1024,E=200,C=64,F=512,y=16,L=["_commitSeq","seq"],B=["accessToken","apiKey","backupCodes","clientSecret","hashedPassword","password","privateKey","refreshToken","secret","totpSecret"],p="documentHistory",I="versions",l=`${p}_${I}`,P=x(p,{tables:{[I]:R({doc:r.optional(r.string()),documentId:r.string(),op:r.union(r.literal("delete"),r.literal("insert"),r.literal("update")),previous:r.optional(r.string()),recordedAt:r.number(),seq:r.number(),tableName:r.string(),truncated:r.optional(r.boolean())}).commitOrdered().index("byDocumentRecordedAt",["documentId","recordedAt","seq"]).index("byRecordedAt",["recordedAt"])}}),{internalMutation:Y,internalQuery:j}=v.dataModel().create(),X=(d={})=>{const T=d.retentionMs!==void 0&&Number.isFinite(d.retentionMs)?Math.max(1,Math.floor(d.retentionMs)):H,_=d.maxSnapshotBytes!==void 0&&Number.isFinite(d.maxSnapshotBytes)?Math.max(1,Math.floor(d.maxSnapshotBytes)):q,D=new Set([...B,...d.redact??[]]);let b=0;const M=()=>(b+=1,b),m=(e,t=0)=>{if(Array.isArray(e))return t>=y?void 0:e.map(o=>m(o,t+1));if(typeof e!="object"||e===null||Object.getPrototypeOf(e)!==Object.prototype&&Object.getPrototypeOf(e)!==null)return e;if(!(t>=y))return Object.fromEntries(Object.entries(e).filter(([o])=>!D.has(o)).map(([o,i])=>[o,m(i,t+1)]))},f=e=>{if(e===void 0)return;const t=JSON.stringify(h(m(e)));return new TextEncoder().encode(t).length>_?void 0:t},u=async(e,t)=>{const o=f(t.doc),i=f(t.previous),n=t.doc!==void 0&&o===void 0||t.previous!==void 0&&i===void 0;await e.db.insert(l,{documentId:t.documentId,op:t.op,recordedAt:Date.now(),seq:M(),tableName:t.tableName,...n?{truncated:!0}:{},...o===void 0?{}:{doc:o},...i===void 0?{}:{previous:i}})},S=e=>({documentHistoryDelete:e.afterDelete(async(t,o)=>u(t,{documentId:o.id,op:"delete",previous:o.previous,tableName:o.table})),documentHistoryInsert:e.afterInsert(async(t,o)=>u(t,{doc:o.doc,documentId:o.id,op:"insert",tableName:o.table})),documentHistoryUpdate:e.afterUpdate(async(t,o)=>u(t,{doc:o.doc,documentId:o.id,op:"update",previous:o.previous,tableName:o.table}))}),N=j.input({before:r.optional(r.number()),documentId:r.string(),limit:r.optional(r.number())}).query(async({args:e,ctx:t})=>{const o=e.limit!==void 0&&Number.isFinite(e.limit)?Math.max(1,Math.floor(e.limit)):E,i=await t.db.query(l).withIndex("byDocumentRecordedAt",n=>e.before===void 0?n.eq("documentId",e.documentId):n.eq("documentId",e.documentId).lte("recordedAt",e.before)).order("desc").take(o);return i.sort((n,a)=>{for(const c of L){const s=(a[c]??0)-(n[c]??0);if(s!==0)return s}return 0}),i.map(n=>({documentId:n.documentId,op:n.op,recordedAt:n.recordedAt,tableName:n.tableName,...n.doc===void 0?{}:{doc:A(JSON.parse(n.doc))},...n.previous===void 0?{}:{previous:A(JSON.parse(n.previous))},...n.truncated===!0?{truncated:!0}:{}}))}),O=Y.input({limit:r.optional(r.number())}).mutation(async({args:e,ctx:t})=>{const o=Date.now()-T,i=e.limit!==void 0&&Number.isFinite(e.limit)?Math.max(1,Math.floor(e.limit)):F;let n=0;for(let a=0;a<C&&n<i;a+=1){const c=await t.db.query(l).withIndex("byRecordedAt",s=>s.lt("recordedAt",o)).order("asc").take(Math.min(E,i-n));if(c.length===0)return{deleted:n};await Promise.all(c.map(async s=>t.db.delete(s._id))),n+=c.length}return{deleted:n}});return{...U(p,{extension:P,functions:{listForDocument:N,vacuum:O}}),record:S}};export{B as DOCUMENT_HISTORY_REDACTED_FIELDS,l as DOCUMENT_HISTORY_TABLE,X as defineDocumentHistory,P as documentHistoryExtension};
@@ -0,0 +1 @@
1
+ import{i as d,d as f,e as u,f as h}from"./middleware-Dh06LOt-.mjs";import{r as p}from"./policy-tag-V0lqFzgS.mjs";import{deny as g}from"./allowAll-DkRAgItV.mjs";const c=g(),b=s=>{const e=s?.rls?.tags;return e||p(s)},y=(s,e)=>{const o=d(e.roles),n=new Map,r=new Map;for(const t of e.policies){if(t.on!=="read")continue;const a=n.get(t.table)??new Set;if(a.has(t.when))continue;a.add(t.when),n.set(t.table,a);const i=r.get(t.table)??[];i.push(t),r.set(t.table,i)}for(const[t,a]of r){const i=s.get(t)??[];i.push({policies:a,rolePermissions:o}),s.set(t,i)}},l=s=>{const e=s.OR;return Array.isArray(e)&&e.length===0&&Object.keys(s).length===1},R=(s,e)=>!s||Object.keys(s).length===0?e:l(s)||Object.keys(e).length===0?s:{AND:[s,e]},m=(s,e)=>{const o=f(e.identity);return u(s.policies,{auth:{can:h(o,s.rolePermissions),identity:e.identity,roles:o,userId:e.userId},ctx:e.ctx})},T=(s,e)=>{const o=s.byTable.get(e.table);if(!o||o.length===0)return e.rlsRequired&&!e.tablePublic?c:void 0;const n=[];for(const r of o){const t=m(r,e);if(t===void 0)return;l(t)||n.push(t)}return n.length===0?c:n.length===1?n[0]:{OR:n}},W=s=>{const e=new Map,o=new Set;for(const n of s)for(const r of b(n))o.has(r)||(o.add(r),y(e,r));return{byTable:e}},A=(s,e)=>R(T(s,e),e.shapeWhere);export{W as buildRlsReadRegistry,A as composeShapeReadWhere};
@@ -0,0 +1 @@
1
+ import{LunoraError as S}from"@lunora/errors";import{f as U}from"./fnv1a-D6VtE8WR.mjs";import{i as L,a as z,o as _,b as G,c as C}from"./middleware-Dh06LOt-.mjs";import{bindTableFacade as H,bindOrm as J}from"./bindOrm-ChQydkdL.mjs";import{tagMaskMiddleware as Q}from"./buildMaskRegistry-DpWMtalG.mjs";const V=(e,i,a)=>{try{return e==="redact"?null:e==="hash"?i==null?i:typeof i=="bigint"?U(i.toString()):U(typeof i=="string"?i:JSON.stringify(i)):e(i,a)}catch{return null}},u=(e,i,a)=>{const d={...e};for(const[y,g]of Object.entries(i))y in d&&(d[y]=V(g,e[y],{...a,column:y,row:e}));return d},j=(e,i,a)=>({...e,page:e.page.map(d=>u(d,i,a))}),D=(e,i,a,d)=>{if(typeof e!="function")return;const y=new Set,g=()=>new Proxy({},{get:()=>w=>(typeof w=="string"&&y.add(w),g())});e(g());for(const w of y)if(w in i)throw new S("MASK_UNSUPPORTED",`${d}() filtering "${a}" by masked column "${w}" is not supported`)},X=(e,i,a,d)=>{const y=e.rankBefore,g=e.rankPageRows,w=(r,n)=>{const t=i.get(r);return t?n.map(o=>u(o,t,a)):n},l=r=>{const n=r?.relationMask;return{...r,relationMask:n===void 0?w:(t,o)=>n(t,w(t,o))}},k=(r,n,t,o)=>{const s=i.get(r);if(!s)return;const c=d?.[r]?.[o]?.[n];if(!c)return;const f=c.find(E=>E in s);if(f!==void 0)throw new S("MASK_UNSUPPORTED",`${t}() reading "${r}" via index "${n}" would order rows by masked column "${f}" — use an index whose declared fields are all unmasked, or unmask the column`)},p=(r,n,t)=>({async*[Symbol.asyncIterator](){for await(const o of{[Symbol.asyncIterator]:()=>r[Symbol.asyncIterator]()})yield u(o,n,a)},collect:async()=>(await r.collect()).map(s=>u(s,n,a)),collectWithScores:async()=>(await r.collectWithScores()).map(s=>{const c=u(s.document,n,a);return"distanceMeters"in s?{distanceMeters:null,document:c}:{document:c,score:s.score}}),filter:o=>p(r.filter(s=>o(u(s,n,a))),n,t),first:async()=>{const o=await r.first();return o?u(o,n,a):null},order:o=>p(r.order(o),n,t),paginate:async o=>j(await r.paginate(o),n,a),take:async o=>(await r.take(o)).map(c=>u(c,n,a)),unique:async()=>{const o=await r.unique();return o?u(o,n,a):null},withIndex:(o,s)=>(k(t,o,"withIndex","index"),D(s,n,t,"withIndex"),p(r.withIndex(o,s),n,t)),withSearchIndex:(o,s)=>(D(s,n,t,"withSearchIndex"),p(r.withSearchIndex(o,s),n,t)),withGeoIndex:(o,s)=>(k(t,o,"withGeoIndex","geo"),p(r.withGeoIndex(o,s),n,t))}),O=async(r,n)=>{if(e.lookupById){const f=await e.lookupById(r,n);return f?{row:f.row,tableName:i.has(f.tableName)?f.tableName:void 0}:{row:null,tableName:void 0}}const t=await e.get(r,n);if(!t)return{row:null,tableName:void 0};const o=n!==void 0&&i.has(n)?[n]:[],s=n===void 0?[...i.keys()]:o,c=await Promise.all(s.map(async f=>(await e.findFirst(f,{limit:1,where:{_id:r}}))?._id===r?f:void 0));return{row:t,tableName:c.find(f=>f!==void 0)}},P=(r,n,t)=>{const o=i.get(r);if(!o)return;const s=n.find(c=>typeof c=="string"&&c in o);if(s!==void 0)throw new S("MASK_UNSUPPORTED",`${t}() over masked column "${s}" on "${r}" is not supported`)},A=new Set;for(const r of i.values())for(const n of Object.keys(r))A.add(n);const R=(r,n,t,o)=>{if(!(!r||typeof r!="object"||Array.isArray(r)))for(const[s,c]of Object.entries(r))K(s,c,n,t,o)},K=(r,n,t,o,s)=>{if(r==="AND"||r==="OR"){for(const c of Array.isArray(n)?n:[])R(c,t,o,s);return}if(r==="NOT"){R(n,t,o,s);return}if(!r.startsWith("__")){if(t.has(r))throw new S("MASK_UNSUPPORTED",`${s}() filtering "${o}" by masked column "${r}" is not supported`);if(C(n))for(const c of Object.values(n))R(c,A,`${o}.${r}`,s)}},B=r=>{const n=i.get(r);return n?new Set(Object.keys(n)):new Set},h=(r,n,t)=>{A.size===0||n===void 0||R(n,B(r),r,t)},F=(r,n,t,o)=>{if(Array.isArray(n)){for(const s of n)if(!(!s||typeof s!="object"||Array.isArray(s))){for(const c of Object.keys(s))if(t.has(c))throw new S("MASK_UNSUPPORTED",`${o}() ordering "${r}" by masked column "${c}" is not supported`)}}},$=(r,n,t)=>{if(!(A.size===0||!n||typeof n!="object"||Array.isArray(n)))for(const[o,s]of Object.entries(n)){if(o==="_count"||!s||typeof s!="object"||Array.isArray(s))continue;const c=s,f=`${r}.${o}`;R(c.where,A,f,t),F(f,c.orderBy,A,t),$(f,c.with,t)}},W=(r,n,t)=>{h(r,n?.where,t),h(r,n?.baseWhere,t),F(r,n?.orderBy,B(r),t),$(r,n?.with,t)},v=r=>r&&typeof r=="object"&&!Array.isArray(r)?r:void 0,M=(r,n,t)=>{const o=v(n);h(r,o?.where,t),h(r,o?.baseWhere,t),$(r,o?.with,t)},I={...e,async deleteWhere(r,n,t){if(h(r,n,"deleteMany({ where })"),e.deleteWhere===void 0)throw new S("INTERNAL",`ctx.db.${r}.deleteMany({ where }) is unavailable: this writer has no where-based delete`);return e.deleteWhere(r,n,t)},async patchWhere(r,n,t){if(h(r,n.where,"patchMany({ where })"),e.patchWhere===void 0)throw new S("INTERNAL",`ctx.db.${r}.patchMany({ where }) is unavailable: this writer has no where-based patch`);return e.patchWhere(r,n,t)},aggregate(r,n){return P(r,[n.field],"aggregate"),h(r,n.where,"aggregate"),e.aggregate(r,n)},count(r,n){const t=v(n),o=t&&("where"in t||"baseWhere"in t||"restrictsCounts"in t)?t.where:n;return h(r,o,"count"),t&&h(r,t.baseWhere,"count"),e.count(r,n)},async findFirst(r,n){W(r,n,"findFirst");const t=await e.findFirst(r,l(n)),o=i.get(r);return t&&o?u(t,o,a):t},async findFirstOrThrow(r,n){W(r,n,"findFirstOrThrow");const t=await e.findFirstOrThrow(r,l(n)),o=i.get(r);return o?u(t,o,a):t},async findMany(r,n){W(r,n,"findMany");const t=await e.findMany(r,l(n)),o=i.get(r);return o?j(t,o,a):t},async get(r,n){const{row:t,tableName:o}=await O(r,n),s=o===void 0?void 0:i.get(o);return!t||!s?t:u(t,s,a)},async lookupById(r,n){const t=await e.lookupById?.(r,n);if(!t)return null;const o=i.get(t.tableName);return{row:o?u(t.row,o,a):t.row,tableName:t.tableName}},groupBy(r,n){return P(r,[...n.by,n.agg?.field],"groupBy"),h(r,n.where,"groupBy"),e.groupBy(r,n)},query(r){const n=e.query(r),t=i.get(r);return t?p(n,t,r):n},async rank(r,n,t){return M(r,t,"rank"),k(r,n,"rank","rank"),e.rank(r,n,t)},async rankPage(r,n,t){M(r,t,"rankPage"),k(r,n,"rankPage","rank");const o=await e.rankPage(r,n,t),s=i.get(r);return s?j(o,s,a):o},..._("rankBefore",y,r=>(n,t,o)=>(M(n,o,"rankBefore"),k(n,t,"rankBefore","rank"),r(n,t,o))),..._("rankPageRows",g,r=>async(n,t,o)=>{M(n,o,"rankPageRows"),k(n,t,"rankPageRows","rank");const s=await r(n,t,o),c=i.get(n);return c?{...s,rows:s.rows.map(f=>({...f,doc:u(f.doc,c,a)}))}:s})},q=I;if(A.size>0)for(const[r,n]of Object.entries(e))G(n)&&(q[r]=H(I,r));return I},b=(e,i={})=>{const a=new Map(Object.entries(e)),d=L(i.roles),y=async({ctx:w,next:l})=>{const k={auth:await z(w.auth??{},d),ctx:w};if(i.bypass?.(k))return l();const p=X(w.db,a,k,i.indexFields),O={db:p},{orm:P}=w;return P!==null&&typeof P=="object"&&(O.orm=J(p)),l({ctx:O})},g=new Map;for(const[w,l]of a)g.set(w,new Set(Object.keys(l)));return Q(y,{columns:g})};export{b as mask};
@@ -0,0 +1 @@
1
+ import{LunoraError as W}from"@lunora/errors";import{bindTableFacade as U,bindOrm as T}from"./bindOrm-ChQydkdL.mjs";import{t as j}from"./policy-tag-V0lqFzgS.mjs";import{deny as $}from"./allowAll-DkRAgItV.mjs";const N=["every","is","isNot","none","some"],q=new Set(N),Y=r=>{if(r===null||typeof r!="object"||Array.isArray(r))return!1;const t=Object.keys(r);return t.length>0&&t.every(i=>q.has(i))},M=(r,t,i)=>t?{[r]:i(t)}:{},D=500,R=(r,t,i)=>{const a=t??D;if(r>a)throw new W("BATCH_LIMIT_EXCEEDED",`${i}: batch of ${String(r)} exceeds the limit of ${String(a)} (raise options.limit or chunk the call)`,{status:400})},K=$(),z=Symbol.for("lunora.ctxdb.rls-unwrap"),H=r=>{const t=new Map;for(const i of r){const a=t.get(i.table)??[];a.push(i),t.set(i.table,a)}return t},G=(r,t)=>{const i=[];let a=!1;for(const l of r){if(l.on!=="read")continue;const f=l.when(t);if(f!==void 0){if(f===!0){a=!0;break}f!==!1&&i.push(f)}}if(!a)return i.length===0?K:i.length===1?i[0]:{OR:i}},P=r=>typeof r=="object"&&r!==null&&!Array.isArray(r),Q=["contains","eq","gt","gte","in","isNull","lt","lte","ne","notIn"],p=r=>{const t=typeof r;return t==="number"||t==="string"||t==="bigint"},X=(r,t)=>!("lt"in t&&(!p(r)||!p(t.lt)||r>=t.lt)||"lte"in t&&(!p(r)||!p(t.lte)||r>t.lte)||"gt"in t&&(!p(r)||!p(t.gt)||r<=t.gt)||"gte"in t&&(!p(r)||!p(t.gte)||r<t.gte)),J=(r,t)=>{if("in"in t){const i=t.in;if(!Array.isArray(i)||!i.includes(r))return!1}if("notIn"in t){const i=t.notIn;if(Array.isArray(i)&&i.includes(r))return!1}if("contains"in t){const i=t.contains;if(typeof r!="string"||typeof i!="string"||!r.includes(i))return!1}return!("isNull"in t&&t.isNull===!0!==(r==null))},Z=(r,t)=>"eq"in t&&r!==t.eq||"ne"in t&&r===t.ne?!1:J(r,t)&&X(r,t),x=(r,t,i,a)=>t==="AND"?Array.isArray(i)&&i.every(l=>a(r,l)):t==="OR"?Array.isArray(i)&&i.some(l=>a(r,l)):!a(r,i??{}),C=r=>r==="AND"||r==="OR"||r==="NOT",V=r=>P(r)&&Object.keys(r).every(t=>Q.includes(t)),v=r=>Object.keys(r).some(t=>{const i=r[t];return C(t)?t==="NOT"?v(i??{}):Array.isArray(i)&&i.some(a=>v(a??{})):Y(i)}),B=(r,t)=>{for(const i of Object.keys(t)){const a=t[i];if(C(i)){if(!x(r,i,a,B))return!1;continue}const l=r[i];if(V(a)){if(!Z(l,a))return!1;continue}if(l!==a)return!1}return!0},ee=(r,t,i)=>{const a=r.when({...t,row:i});if(a===void 0||a===!0)return!0;if(a===!1)return!1;if(v(a))throw new W("RELATION_PREDICATE_UNSUPPORTED","relation predicates are not supported in write policies; use a read policy or a flat column check");return B(i,a)},re=(r,t,i)=>{const a=r.when(t);if(a===void 0||a===!0)return i===void 0||ee(r,t,i);if(a===!1)return!1;if(v(a))throw new W("RELATION_PREDICATE_UNSUPPORTED","relation predicates are not supported in write policies; use a read policy or a flat column check");return!t.row||!B(t.row,a)?!1:i===void 0||B(i,a)},_=(r,t,i,a)=>{let l=!1;for(const f of r)if(f.on===t&&(l=!0,!re(f,i,a)))return!1;return l},te=new Set(["baseWhere","relationBaseWhere","restrictsCounts","where"]),ne=(r,t)=>t===void 0?!0:r==="restrictsCounts"?typeof t=="boolean":r==="relationBaseWhere"?typeof t=="function":P(t),se=r=>{const t=Object.keys(r);return t.length===0?!1:t.every(i=>te.has(i)&&ne(i,r[i]))},ie=r=>r===void 0?{}:P(r)&&se(r)?r:{where:r},w=(r,t)=>!t||Object.keys(t).length===0?r:!r||Object.keys(r).length===0?t:{AND:[t,r]},oe=r=>{if(typeof r!="object"||r===null||Array.isArray(r))return!1;const t=r;return typeof t.findMany=="function"&&typeof t.withSearchIndex=="function"},ae=(r,t,i,a)=>{const l=new Map,f=e=>{const n=l.get(e);if(n)return n;const s=i.get(e);if(!s||s.length===0||!s.some(u=>u.on==="read")){const u={baseWhere:void 0,restricts:!1};return l.set(e,u),u}const c={baseWhere:G(s,a),restricts:!0};return l.set(e,c),c},d=e=>f(e).restricts?t:r,y=e=>f(e).baseWhere,O=async(e,n)=>{if(t.lookupById){const h=await t.lookupById(e,n);return h?{row:h.row,tableName:i.has(h.tableName)?h.tableName:void 0}:{row:null,tableName:void 0}}const s=await t.get(e,n);if(!s)return{row:null,tableName:void 0};const o=n!==void 0&&i.has(n)?[n]:[],c=n===void 0?[...i.keys()]:o,u=await Promise.all(c.map(async h=>(await t.findFirst(h,{limit:1,where:{_id:e}}))?._id===e?h:void 0));return{row:s,tableName:u.find(h=>h!==void 0)}},I=async(e,n)=>{const s=await O(e,n);return s.row&&s.tableName!==void 0?{row:s.row,tableName:s.tableName}:void 0},g=async(e,n,s,o,c)=>{const u=await I(e,c);if(!u)return s(r);const h=i.get(u.tableName);if(h){const A=o?o(u.row):void 0;if(!_(h,n,{...a,row:u.row},A))throw new W("FORBIDDEN",`${n} denied by policy`)}return s(t)},b=(e,n)=>{const{baseWhere:s,restricts:o}=f(e);if(o)throw new W("COUNT_RLS_UNSUPPORTED",`${n}() is not supported on "${e}" inside an RLS-restricted context`);return s},E=r.rankBefore,F=r.rankPageRows,k={...r,async count(e,n){const{baseWhere:s,restricts:o}=f(e),c=ie(n);return d(e).count(e,{...c,baseWhere:w(c.baseWhere,s),relationBaseWhere:y,restrictsCounts:(c.restrictsCounts??!1)||o})},delete:(e,n,s)=>g(e,"delete",o=>o.delete(e,n,s),void 0,n),async deleteAll(e,n){const s=Math.max(1,n?.chunkSize??D),{baseWhere:o}=f(e);let c=0;for(;;){const h=(await d(e).findMany(e,{baseWhere:o,limit:s,relationBaseWhere:y})).page.map(A=>String(A._id));if(h.length===0)break;for(const A of h)await g(A,"delete",S=>S.delete(A,void 0,n?.hard===void 0?void 0:{hard:n.hard})),c+=1;if(h.length<s)break}return{deleted:c}},async deleteMany(e,n,s){R(e.length,n?.limit,"deleteMany");for(const o of e)await g(o,"delete",c=>c.delete(o,s),void 0,s);return{deleted:e.length}},async deleteWhere(e,n,s){const{baseWhere:o}=f(e),u=(await d(e).findMany(e,{baseWhere:w(n,o),relationBaseWhere:y})).page.map(h=>String(h._id));return R(u.length,s?.limit,"deleteWhere"),k.deleteMany(u,s)},async findFirst(e,n){const{baseWhere:s}=f(e);return d(e).findFirst(e,{...n,baseWhere:w(n?.baseWhere,s),relationBaseWhere:y})},async findFirstOrThrow(e,n){const{baseWhere:s}=f(e);return d(e).findFirstOrThrow(e,{...n,baseWhere:w(n?.baseWhere,s),relationBaseWhere:y})},async findMany(e,n){const{baseWhere:s}=f(e);return d(e).findMany(e,{...n,baseWhere:w(n?.baseWhere,s),relationBaseWhere:y})},async get(e,n){const s=await O(e,n);if(!s.row)return null;if(s.tableName===void 0)return r.get(e,n);const{baseWhere:o,restricts:c}=f(s.tableName);if(!c)return r.get(e,n);if(!o)return s.row;const u=await t.findFirst(s.tableName,{baseWhere:o,limit:1,where:{_id:e}});return u?._id===e?u:null},async lookupById(e,n){const s=await O(e,n);if(!s.row)return null;if(s.tableName===void 0||!f(s.tableName).restricts)return await r.lookupById?.(e,n)??null;const{baseWhere:o}=f(s.tableName);if(!o)return{row:s.row,tableName:s.tableName};const c=await t.findFirst(s.tableName,{baseWhere:o,limit:1,where:{_id:e}});return c?._id===e?{row:c,tableName:s.tableName}:null},async insert(e,n){const s=i.get(e);if(s){if(!_(s,"insert",{...a,row:n}))throw new W("FORBIDDEN",`insert on "${e}" denied by policy`);return t.insert(e,n)}return r.insert(e,n)},async insertMany(e,n,s){R(n.length,s?.limit,"insertMany");const o=i.get(e);if(o){for(const c of n)if(!_(o,"insert",{...a,row:c}))throw new W("FORBIDDEN",`insert on "${e}" denied by policy`);return t.insertMany(e,n,s)}return r.insertMany(e,n,s)},async insertManyUnsafe(e,n,s){R(n.length,s?.limit,"insertManyUnsafe");const o=i.get(e);if(o){for(const c of n)if(!_(o,"insert",{...a,row:c}))throw new W("FORBIDDEN",`insert on "${e}" denied by policy`);return t.insertManyUnsafe(e,n,s)}return r.insertManyUnsafe(e,n,s)},patch:(e,n,s)=>g(e,"update",o=>o.patch(e,n,s),o=>({...o,...n}),s),async patchMany(e,n,s){R(e.length,n?.limit,"patchMany");for(const o of e)await g(o.id,"update",c=>c.patch(o.id,o.patch,s),c=>({...c,...o.patch}),s);return{patched:e.length}},async patchWhere(e,n,s){const{baseWhere:o}=f(e),u=(await d(e).findMany(e,{baseWhere:w(n.where,o),relationBaseWhere:y})).page.map(h=>({id:String(h._id),patch:n.patch}));return R(u.length,s?.limit,"patchWhere"),k.patchMany(u,s)},query(e){const{baseWhere:n}=f(e),s=d(e).query(e);return n?s.filter(o=>B(o,n)):s},restore:(e,n)=>g(e,"update",async s=>{const o=s.restore??t.restore;if(!o)throw new W("BAD_REQUEST","restore is not supported by this writer");await o(e,n)},void 0,n),replace:(e,n,s)=>g(e,"update",o=>o.replace(e,n,s),o=>({...n,_creationTime:n._creationTime??o._creationTime,_id:o._id}),s),aggregate(e,n){const{baseWhere:s}=f(e);return d(e).aggregate(e,{...n,baseWhere:w(n.baseWhere,s),relationBaseWhere:y})},groupBy(e,n){const{baseWhere:s}=f(e);return d(e).groupBy(e,{...n,baseWhere:w(n.baseWhere,s),relationBaseWhere:y})},rank(e,n,s){const o=b(e,"rank");return d(e).rank(e,n,{...s,baseWhere:w(s.baseWhere,o)})},rankPage(e,n,s){const o=b(e,"rankPage");return d(e).rankPage(e,n,{...s,baseWhere:w(s?.baseWhere,o)})},wipeShard(){throw new W("FORBIDDEN","ctx.db.wipeShard() is unavailable under rls(): a whole-shard erase can't be policy-gated per row, and erasing only policy-visible rows would silently leave data behind. Run it from an internalMutation, or use ctx.db.deleteAll(table) per table.",{status:403})},...M("rankBefore",E,e=>(n,s,o)=>(b(n,"rankBefore"),(d(n).rankBefore??e)(n,s,o))),...M("rankPageRows",F,e=>(n,s,o)=>{const c=b(n,"rankPageRows");return(d(n).rankPageRows??e)(n,s,{...o,baseWhere:w(o?.baseWhere,c)})})},L=k;if(i.size>0)for(const[e,n]of Object.entries(r))oe(n)&&(L[e]=U(k,e));return k},m=r=>typeof r=="string"?r:r.name,ce=r=>{const t=new Map;for(const i of r??[])t.set(i.name,new Set((i.permissions??[]).map(a=>m(a))));return t},fe=(r,t)=>{const i=new Set;for(const a of r)for(const l of t.get(a)??[])i.add(l);return a=>i.has(m(a))},le=["roles","role"],ue=r=>{for(const t of le){const i=r?.[t];if(typeof i=="string"){const a=i.split(",").map(l=>l.trim()).filter(l=>l.length>0);if(a.length>0)return a}if(Array.isArray(i)){const a=i.filter(l=>typeof l=="string"&&l.length>0);if(a.length>0)return a}}return[]},he=async(r,t)=>{const i=await r.getIdentity?.()??null,a=[...new Set([...ue(i),...r.roles??[]])];return{can:fe(a,t),identity:i,roles:a,userId:r.userId??null}},We=(r,t={})=>{const i=H(r),a=ce(t.roles);return j(async({ctx:f,next:d})=>{const y={auth:await he(f.auth??{},a),ctx:f},O=f.db,I=O[z]??O,g=ae(O,I,i,y),b={db:g},{orm:E}=f;return E!==null&&typeof E=="object"&&(b.orm=T(g)),d({ctx:b})},[{policies:r,roles:t.roles??[]}])};export{he as a,oe as b,Y as c,ue as d,G as e,fe as f,_ as g,ce as i,B as m,M as o,m as p,We as r};
@@ -0,0 +1 @@
1
+ import{e as o,g as t,i as m,b as l,m as p,p as d,d as n,f as c,a as h,r as u}from"./middleware-Dh06LOt-.mjs";import"./bindOrm-ChQydkdL.mjs";import"./policy-tag-V0lqFzgS.mjs";import"./allowAll-DkRAgItV.mjs";export{o as computeReadBaseWhere,t as evaluateWrite,m as indexRolePermissions,l as isFacadeEntry,p as matchesWhere,d as permissionName,n as readIdentityRoles,c as resolveCan,h as resolvePolicyAuth,u as rls};
@@ -0,0 +1 @@
1
+ import{LunoraError as b}from"@lunora/errors";import{i as y,a as R}from"./middleware-Dh06LOt-.mjs";const U=(e,t)=>e===void 0||t.startsWith(e),v=e=>{const t=e[1],r=typeof t=="object"&&t!==null?t.method:void 0;return typeof r=="string"&&r.toUpperCase()==="PUT"?"write":"read"},N=[["delete","delete"],["download","read"],["generateUploadUrl","write"],["getMetadata","read"],["getSignedUrl",v],["getUrl","read"],["head","read"],["store","write"]],$=(e,t)=>{try{return e(t).bucketName===t}catch{return!1}},A=(e,t)=>{const r=e.bucketName??"default",{bucket:a}=e;for(const s of new Set(t))if(!(s===r||typeof a=="function"&&$(a,s)))throw new b("INTERNAL",`storageRules: rule for bucket "${s}" governs nothing — this request's storage cannot address that bucket (the accessor is "${r}", and selecting "${s}" does not reach a bucket of that name). A rule on an unaddressable bucket leaves the operation it was written to gate wide open. Match the rule's \`bucket\` to the name the binding is registered under in \`.storage({ bucket, buckets })\`.`)},S=(e,t={})=>{const r=y(t.roles);return async({ctx:a,next:s})=>{const g=await R(a.auth??{},r),w=(o,c,i)=>{const d=e.filter(n=>n.on===o&&n.bucket===i);if(d.length===0)return;const u={auth:g,ctx:a,key:c};if(!d.some(n=>U(n.prefix,c)&&n.when(u)===!0))throw new b("FORBIDDEN",`storage ${o} on "${c}" in bucket "${i}" denied by access rule`)},p=o=>{const c=o.bucketName??"default",i={bucketName:c};for(const[u,l]of N){const n=o[u];typeof n=="function"&&(i[u]=(...f)=>{const k=typeof f[0]=="string"?f[0]:"",m=typeof l=="function"?l(f):l;return w(m,k,c),n(...f)})}const{bucket:d}=o;return typeof d=="function"&&(i.bucket=u=>p(d(u))),i},h=a.storage;return h===void 0?s():(A(h,e.map(o=>o.bucket)),s({ctx:{storage:p(h)}}))}};export{S as storageRules};
@@ -51,8 +51,12 @@ interface TypedDefinePolicyInput<DM, REL extends Record<keyof DM, object>, T ext
51
51
  when: (context: PolicyContext<Context, Identity>) => PolicyDecisionOf<DM, REL, T>;
52
52
  }
53
53
  /**
54
- * Context handed to a policy. `auth.roles` is the per-request role list,
55
- * sourced from the identity resolver (better-auth claims today). `auth.can`
54
+ * Context handed to a policy. `auth.roles` is the per-request role list, read
55
+ * from the `roles` CLAIM on the resolved identity — a `string[]` or a
56
+ * comma-separated string returned by `WorkerOptions.resolveIdentity` (and
57
+ * validated by `defineIdentity(...)` at the worker's trust boundary). A
58
+ * resolver that forwards no `roles` claim yields no roles, so every
59
+ * role-branched policy is evaluated against `[]`. `auth.can`
56
60
  * answers whether any of those roles grants a permission (see
57
61
  * {@link Permission} / {@link RlsOptions}). `row` is present only on write
58
62
  * policies (`insert`/`update`/`delete`) — for `update` and `delete` it is the
@@ -117,10 +121,13 @@ interface Permission {
117
121
  readonly name: string;
118
122
  }
119
123
  /**
120
- * A role declaration. Roles are string labels attached to the request's
121
- * identity (via better-auth claims today). `permissions` lists the
122
- * capabilities the role grants at request time the middleware unions the
123
- * permissions of every role in `ctx.auth.roles` so a policy can ask
124
+ * A role declaration. Roles are string labels the request carries in the
125
+ * `roles` claim of its resolved identity — `WorkerOptions.resolveIdentity`
126
+ * forwards every non-`userId` key verbatim, so a resolver returning
127
+ * `{ userId, roles: ["admin"] }` (or the comma-joined `"user,admin"` that
128
+ * better-auth's `admin()` plugin stores) is what puts them there. `permissions`
129
+ * lists the capabilities the role grants — at request time the middleware
130
+ * unions the permissions of every role in `ctx.auth.roles` so a policy can ask
124
131
  * `ctx.auth.can(permission)` rather than enumerate roles.
125
132
  */
126
133
  interface Role {
@@ -51,8 +51,12 @@ interface TypedDefinePolicyInput<DM, REL extends Record<keyof DM, object>, T ext
51
51
  when: (context: PolicyContext<Context, Identity>) => PolicyDecisionOf<DM, REL, T>;
52
52
  }
53
53
  /**
54
- * Context handed to a policy. `auth.roles` is the per-request role list,
55
- * sourced from the identity resolver (better-auth claims today). `auth.can`
54
+ * Context handed to a policy. `auth.roles` is the per-request role list, read
55
+ * from the `roles` CLAIM on the resolved identity — a `string[]` or a
56
+ * comma-separated string returned by `WorkerOptions.resolveIdentity` (and
57
+ * validated by `defineIdentity(...)` at the worker's trust boundary). A
58
+ * resolver that forwards no `roles` claim yields no roles, so every
59
+ * role-branched policy is evaluated against `[]`. `auth.can`
56
60
  * answers whether any of those roles grants a permission (see
57
61
  * {@link Permission} / {@link RlsOptions}). `row` is present only on write
58
62
  * policies (`insert`/`update`/`delete`) — for `update` and `delete` it is the
@@ -117,10 +121,13 @@ interface Permission {
117
121
  readonly name: string;
118
122
  }
119
123
  /**
120
- * A role declaration. Roles are string labels attached to the request's
121
- * identity (via better-auth claims today). `permissions` lists the
122
- * capabilities the role grants at request time the middleware unions the
123
- * permissions of every role in `ctx.auth.roles` so a policy can ask
124
+ * A role declaration. Roles are string labels the request carries in the
125
+ * `roles` claim of its resolved identity — `WorkerOptions.resolveIdentity`
126
+ * forwards every non-`userId` key verbatim, so a resolver returning
127
+ * `{ userId, roles: ["admin"] }` (or the comma-joined `"user,admin"` that
128
+ * better-auth's `admin()` plugin stores) is what puts them there. `permissions`
129
+ * lists the capabilities the role grants — at request time the middleware
130
+ * unions the permissions of every role in `ctx.auth.roles` so a policy can ask
124
131
  * `ctx.auth.can(permission)` rather than enumerate roles.
125
132
  */
126
133
  interface Role {
@@ -0,0 +1 @@
1
+ const m=e=>{let t="";for(let o=0;o<e.length;o+=32768)t+=String.fromCharCode(...e.subarray(o,o+32768));return btoa(t)},p=e=>{const t=atob(e),f=new Uint8Array(t.length);for(let o=0;o<t.length;o+=1)f[o]=t.codePointAt(o)??0;return f},i="$lunora.wire$";const w="__proto__",g={BigInt64Array,BigUint64Array,Float32Array,Float64Array,Int8Array,Int16Array,Int32Array,Uint8Array,Uint8ClampedArray,Uint16Array,Uint32Array},A={Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError},l=e=>{if(e===null||typeof e!="object")return!1;const t=Object.getPrototypeOf(e);return t===null||t===Object.prototype},u=(e,t=0)=>{if(t>64)throw new RangeError("wire-codec: value nesting exceeds the 64-level limit");if(e===void 0)return[i,"undefined"];if(e===null)return null;const f=typeof e;if(f==="bigint")return[i,"bigint",e.toString()];if(f==="number"){const r=e;return Number.isNaN(r)?[i,"nan"]:r===1/0?[i,"inf"]:r===-1/0?[i,"-inf"]:r}if(f!=="object")return e;if(e instanceof Date)return[i,"date",u(e.getTime(),t+1)];if(e instanceof Error){const r=e,n={};for(const a of Object.keys(r))r[a]!==void 0&&(n[a]=u(r[a],t+1));const c=[i,"error",r.name,r.message,n];return r.cause!==void 0&&c.push(u(r.cause,t+1)),c}if(e instanceof URL)return[i,"url",e.href];if(e instanceof Map)return[i,"map",[...e.entries()].map(([r,n])=>[u(r,t+1),u(n,t+1)])];if(e instanceof Set)return[i,"set",[...e].map(r=>u(r,t+1))];if(e instanceof ArrayBuffer)return[i,"bytes",m(new Uint8Array(e)),"ArrayBuffer"];if(ArrayBuffer.isView(e)){const r=e,n=r.constructor.name,c=new Uint8Array(r.buffer,r.byteOffset,r.byteLength);return n==="Uint8Array"?[i,"bytes",m(c)]:[i,"bytes",m(c),n]}if(Array.isArray(e)){const r=e.map(n=>u(n,t+1));return r.length>0&&r[0]===i?[i,"arr",r]:r}if(!l(e)){const r=e.constructor?.name??"value";throw new TypeError(`wire-codec: cannot encode a ${r} over the Lunora wire — only plain objects, arrays, and the supported built-ins (Date, Error, URL, Map, Set, ArrayBuffer/typed arrays, bigint) round-trip`)}const o=e,s={};for(const r of Object.keys(o)){const n=o[r];if(n===void 0)continue;const c=u(n,t+1);r===w?Object.defineProperty(s,r,{configurable:!0,enumerable:!0,value:c,writable:!0}):s[r]=c}return s},y=(e,t=0)=>{if(t>64)throw new RangeError("wire-codec: value nesting exceeds the 64-level limit");if(e===null||typeof e!="object")return e;if(Array.isArray(e)){if(e[0]===i)switch(e[1]){case"-inf":return-1/0;case"arr":return e[2].map(r=>y(r,t+1));case"bigint":{const r=e[2];if(typeof r!="string"||r.length>1024||!/^-?\d+$/.test(r))throw new RangeError("wire-codec: invalid or over-long bigint (max 1024 digits)");return BigInt(r)}case"date":{if(e.length<3)throw new TypeError("wire-codec: malformed date — missing payload");return new Date(y(e[2],t+1))}case"map":{const r=e[2];return new Map(r.map(n=>{if(!Array.isArray(n)||n.length!==2)throw new TypeError("wire-codec: malformed map entry — expected a [key, value] pair");return[y(n[0],t+1),y(n[1],t+1)]}))}case"set":return new Set(e[2].map(r=>y(r,t+1)));case"url":return new URL(e[2]);case"error":{const r=e[2],n=e[3],c=(Object.hasOwn(A,r)?A[r]:void 0)??Error,a=new c(n);a.name!==r&&Object.defineProperty(a,"name",{configurable:!0,value:r,writable:!0});const b=y(e[4],t+1);if(b===null||typeof b!="object"||Array.isArray(b))throw new TypeError("wire-codec: malformed error — props must be an object");for(const d of Object.keys(b))d===w?Object.defineProperty(a,d,{configurable:!0,enumerable:!0,value:b[d],writable:!0}):a[d]=b[d];return e.length>5&&Object.defineProperty(a,"cause",{configurable:!0,value:y(e[5],t+1),writable:!0}),a}case"bytes":{const r=p(e[2]),n=e[3]??"Uint8Array";if(n==="ArrayBuffer")return r.buffer.byteLength===r.byteLength?r.buffer:r.slice().buffer;const c=Object.hasOwn(g,n)?g[n]:void 0;return c?new c(r.slice().buffer):r}case"inf":return 1/0;case"nan":return Number.NaN;case"undefined":return;default:return e.map(r=>y(r,t+1))}return e.map(s=>y(s,t+1))}const f=e,o={};for(const s of Object.keys(f)){const r=y(f[s],t+1);s===w?Object.defineProperty(o,s,{configurable:!0,enumerable:!0,value:r,writable:!0}):o[s]=r}return o};export{y as d,u as e};
@@ -1,16 +1,24 @@
1
- import { P as PolicyOperation, R as Role, a as Policy } from "../packem_shared/types.d-B-pIn8rE.mjs";
1
+ import { P as PolicyOperation, R as Role, a as Policy } from "../packem_shared/types.d-B7FLdYUD.mjs";
2
2
  import "../data-model.mjs";
3
3
  /**
4
4
  * The slice of a request identity a policy reads. Mirrors the
5
5
  * `PolicyContext.auth` shape the middleware builds at request time — every
6
6
  * field is optional and defaults the same way the middleware defaults it
7
- * (`userId`/`identity` → `null`, `roles` → `[]`).
7
+ * (`userId`/`identity` → `null`, `roles` → the `roles` claim on `identity`).
8
+ *
9
+ * There is deliberately NO separate `roles` field: `auth.roles` is derived from
10
+ * the identity claims by the same `readIdentityRoles` the middleware uses, so
11
+ * the harness cannot set up a world the runtime is unable to produce. Give a
12
+ * role-branched policy its roles the way production does —
13
+ * `.as({ identity: { roles: ["admin"] }, userId: "u1" })`.
8
14
  */
9
15
  interface TestIdentity {
10
- /** Raw identity claims a policy may branch on (`auth.identity.email`, …). */
16
+ /**
17
+ * Raw identity claims a policy may branch on (`auth.identity.email`, …).
18
+ * The `roles` claim (a `string[]` or a comma-separated string) is what
19
+ * populates `auth.roles` and backs `auth.can(...)`.
20
+ */
11
21
  identity?: Record<string, unknown> | null;
12
- /** Role labels the request carries; resolved to permissions via the harness `roles` registry. */
13
- roles?: ReadonlyArray<string>;
14
22
  /** The caller id (`auth.userId`); `null`/omitted is the anonymous caller. */
15
23
  userId?: null | string;
16
24
  }
@@ -1,16 +1,24 @@
1
- import { P as PolicyOperation, R as Role, a as Policy } from "../packem_shared/types.d-CdPDEjhz.js";
1
+ import { P as PolicyOperation, R as Role, a as Policy } from "../packem_shared/types.d-BCtjbIzl.js";
2
2
  import "../data-model.js";
3
3
  /**
4
4
  * The slice of a request identity a policy reads. Mirrors the
5
5
  * `PolicyContext.auth` shape the middleware builds at request time — every
6
6
  * field is optional and defaults the same way the middleware defaults it
7
- * (`userId`/`identity` → `null`, `roles` → `[]`).
7
+ * (`userId`/`identity` → `null`, `roles` → the `roles` claim on `identity`).
8
+ *
9
+ * There is deliberately NO separate `roles` field: `auth.roles` is derived from
10
+ * the identity claims by the same `readIdentityRoles` the middleware uses, so
11
+ * the harness cannot set up a world the runtime is unable to produce. Give a
12
+ * role-branched policy its roles the way production does —
13
+ * `.as({ identity: { roles: ["admin"] }, userId: "u1" })`.
8
14
  */
9
15
  interface TestIdentity {
10
- /** Raw identity claims a policy may branch on (`auth.identity.email`, …). */
16
+ /**
17
+ * Raw identity claims a policy may branch on (`auth.identity.email`, …).
18
+ * The `roles` claim (a `string[]` or a comma-separated string) is what
19
+ * populates `auth.roles` and backs `auth.can(...)`.
20
+ */
11
21
  identity?: Record<string, unknown> | null;
12
- /** Role labels the request carries; resolved to permissions via the harness `roles` registry. */
13
- roles?: ReadonlyArray<string>;
14
22
  /** The caller id (`auth.userId`); `null`/omitted is the anonymous caller. */
15
23
  userId?: null | string;
16
24
  }
@@ -1 +1 @@
1
- import{i as b,d as P,c as W,m as v,e as y}from"../packem_shared/middleware-Cz5s6CH-.mjs";const C=(d,c={})=>{const m=b(c.roles),h=c.ctx??{};return{as:x=>{const n=x??{},i=n.roles??[],l={auth:{can:P(i,m),identity:n.identity??null,roles:i,userId:n.userId??null},ctx:h},u=(e,o,t,a)=>{const r=d.filter(s=>s.table===o);if(r.length===0)return!0;if(e==="read"){if(!r.some(f=>f.on==="read"))return!0;const s=W(r,l);return s===void 0||v(t??{},s)}return y(r,e,{...l,row:t},a)};return{can:u,cannot:(e,o,t,a)=>!u(e,o,t,a)}}}};export{C as expectPolicy};
1
+ import{i as y,d as P,f as W,e as v,m as I,g as R}from"../packem_shared/middleware-Dh06LOt-.mjs";const p=(m,o={})=>{const h=y(o.roles),x=o.ctx??{};return{as:f=>{const c=f??{},i=c.identity??null,l=P(i),u={auth:{can:W(l,h),identity:i,roles:l,userId:c.userId??null},ctx:x},d=(e,r,t,a)=>{const s=m.filter(n=>n.table===r);if(s.length===0)return!0;if(e==="read"){if(!s.some(b=>b.on==="read"))return!0;const n=v(s,u);return n===void 0||I(t??{},n)}return R(s,e,{...u,row:t},a)};return{can:d,cannot:(e,r,t,a)=>!d(e,r,t,a)}}}};export{p as expectPolicy};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/server",
3
- "version": "1.0.0-alpha.97",
3
+ "version": "1.0.0-alpha.99",
4
4
  "description": "Server primitives for Lunora: defineSchema, defineTable, query, mutation, and action",
5
5
  "keywords": [
6
6
  "backend",
@@ -66,9 +66,9 @@
66
66
  "access": "public"
67
67
  },
68
68
  "dependencies": {
69
- "@lunora/errors": "1.0.0-alpha.27",
70
- "@lunora/scheduler": "1.0.0-alpha.47",
71
- "@lunora/values": "1.0.0-alpha.35",
69
+ "@lunora/errors": "1.0.0-alpha.29",
70
+ "@lunora/scheduler": "1.0.0-alpha.49",
71
+ "@lunora/values": "1.0.0-alpha.37",
72
72
  "drizzle-orm": "^0.45.2",
73
73
  "hono": "^4.13.1"
74
74
  },
@@ -1 +0,0 @@
1
- import{i as d,c as f,d as h}from"./middleware-Cz5s6CH-.mjs";import{r as p}from"./policy-tag-V0lqFzgS.mjs";import{deny as u}from"./allowAll-DkRAgItV.mjs";const c=u(),g=s=>{const e=s?.rls?.tags;return e||p(s)},b=(s,e)=>{const r=d(e.roles),t=new Map,n=new Map;for(const o of e.policies){if(o.on!=="read")continue;const a=t.get(o.table)??new Set;if(a.has(o.when))continue;a.add(o.when),t.set(o.table,a);const i=n.get(o.table)??[];i.push(o),n.set(o.table,i)}for(const[o,a]of n){const i=s.get(o)??[];i.push({policies:a,rolePermissions:r}),s.set(o,i)}},l=s=>{const e=s.OR;return Array.isArray(e)&&e.length===0&&Object.keys(s).length===1},y=(s,e)=>!s||Object.keys(s).length===0?e:l(s)||Object.keys(e).length===0?s:{AND:[s,e]},R=(s,e)=>f(s.policies,{auth:{can:h(e.roles,s.rolePermissions),identity:e.identity,roles:e.roles,userId:e.userId},ctx:e.ctx}),m=(s,e)=>{const r=s.byTable.get(e.table);if(!r||r.length===0)return e.rlsRequired&&!e.tablePublic?c:void 0;const t=[];for(const n of r){const o=R(n,e);if(o===void 0)return;l(o)||t.push(o)}return t.length===0?c:t.length===1?t[0]:{OR:t}},P=s=>{const e=new Map,r=new Set;for(const t of s)for(const n of g(t))r.has(n)||(r.add(n),b(e,n));return{byTable:e}},W=(s,e)=>y(m(s,e),e.shapeWhere);export{P as buildRlsReadRegistry,W as composeShapeReadWhere};
@@ -1 +0,0 @@
1
- import{LunoraError as P}from"@lunora/errors";import{f as B}from"./fnv1a-D6VtE8WR.mjs";import{bindTableFacade as E,bindOrm as _}from"./bindOrm-ChQydkdL.mjs";import{i as D,a as K,o as v,b as q}from"./middleware-Cz5s6CH-.mjs";import{tagMaskMiddleware as m}from"./buildMaskRegistry-DpWMtalG.mjs";const G=(i,e,c)=>{try{return i==="redact"?null:i==="hash"?e==null?e:typeof e=="bigint"?B(e.toString()):B(typeof e=="string"?e:JSON.stringify(e)):i(e,c)}catch{return null}},a=(i,e,c)=>{const y={...i};for(const[h,g]of Object.entries(e))h in y&&(y[h]=G(g,i[h],{...c,column:h,row:i}));return y},W=(i,e,c)=>({...i,page:i.page.map(y=>a(y,e,c))}),U=(i,e,c,y)=>{if(typeof i!="function")return;const h=new Set,g=()=>new Proxy({},{get:()=>f=>(typeof f=="string"&&h.add(f),g())});i(g());for(const f of h)if(f in e)throw new P("MASK_UNSUPPORTED",`${y}() filtering "${c}" by masked column "${f}" is not supported`)},L=(i,e,c,y)=>{const h=i.rankBefore,g=i.rankPageRows,f=(r,n)=>{const t=e.get(r);return t?n.map(o=>a(o,t,c)):n},p=r=>{const n=r?.relationMask;return{...r,relationMask:n===void 0?f:(t,o)=>n(t,f(t,o))}},k=(r,n,t,o)=>{const s=e.get(r);if(!s)return;const d=y?.[r]?.[o]?.[n];if(!d)return;const w=d.find($=>$ in s);if(w!==void 0)throw new P("MASK_UNSUPPORTED",`${t}() reading "${r}" via index "${n}" would order rows by masked column "${w}" — use an index whose declared fields are all unmasked, or unmask the column`)},l=(r,n,t)=>({async*[Symbol.asyncIterator](){for await(const o of{[Symbol.asyncIterator]:()=>r[Symbol.asyncIterator]()})yield a(o,n,c)},collect:async()=>(await r.collect()).map(s=>a(s,n,c)),collectWithScores:async()=>(await r.collectWithScores()).map(s=>{const d=a(s.document,n,c);return"distanceMeters"in s?{distanceMeters:null,document:d}:{document:d,score:s.score}}),filter:o=>l(r.filter(s=>o(a(s,n,c))),n,t),first:async()=>{const o=await r.first();return o?a(o,n,c):null},order:o=>l(r.order(o),n,t),paginate:async o=>W(await r.paginate(o),n,c),take:async o=>(await r.take(o)).map(d=>a(d,n,c)),unique:async()=>{const o=await r.unique();return o?a(o,n,c):null},withIndex:(o,s)=>(k(t,o,"withIndex","index"),U(s,n,t,"withIndex"),l(r.withIndex(o,s),n,t)),withSearchIndex:(o,s)=>(U(s,n,t,"withSearchIndex"),l(r.withSearchIndex(o,s),n,t)),withGeoIndex:(o,s)=>(k(t,o,"withGeoIndex","geo"),l(r.withGeoIndex(o,s),n,t))}),S=async(r,n)=>{if(i.lookupById){const w=await i.lookupById(r,n);return w?{row:w.row,tableName:e.has(w.tableName)?w.tableName:void 0}:{row:null,tableName:void 0}}const t=await i.get(r,n);if(!t)return{row:null,tableName:void 0};const o=n!==void 0&&e.has(n)?[n]:[],s=n===void 0?[...e.keys()]:o,d=await Promise.all(s.map(async w=>(await i.findFirst(w,{limit:1,where:{_id:r}}))?._id===r?w:void 0));return{row:t,tableName:d.find(w=>w!==void 0)}},M=(r,n,t)=>{const o=e.get(r);if(!o)return;const s=n.find(d=>typeof d=="string"&&d in o);if(s!==void 0)throw new P("MASK_UNSUPPORTED",`${t}() over masked column "${s}" on "${r}" is not supported`)},R=(r,n)=>{if(!(!r||typeof r!="object"||Array.isArray(r)))for(const[t,o]of Object.entries(r))if(t==="AND"||t==="OR"){if(Array.isArray(o))for(const s of o)R(s,n)}else t==="NOT"?R(o,n):t.startsWith("__")||n.add(t)},u=(r,n,t)=>{const o=e.get(r);if(!o||n===void 0)return;const s=new Set;R(n,s);for(const d of s)if(d in o)throw new P("MASK_UNSUPPORTED",`${t}() filtering "${r}" by masked column "${d}" is not supported`)},I=(r,n,t)=>{const o=e.get(r);if(!(!o||!Array.isArray(n))){for(const s of n)if(s&&typeof s=="object"&&!Array.isArray(s)){for(const d of Object.keys(s))if(d in o)throw new P("MASK_UNSUPPORTED",`${t}() ordering "${r}" by masked column "${d}" is not supported`)}}},F=r=>r&&typeof r=="object"&&!Array.isArray(r)?r:void 0,A=(r,n,t)=>{const o=F(n);u(r,o?.where,t),u(r,o?.baseWhere,t)},O={...i,async deleteWhere(r,n,t){if(u(r,n,"deleteMany({ where })"),i.deleteWhere===void 0)throw new P("INTERNAL",`ctx.db.${r}.deleteMany({ where }) is unavailable: this writer has no where-based delete`);return i.deleteWhere(r,n,t)},async patchWhere(r,n,t){if(u(r,n.where,"patchMany({ where })"),i.patchWhere===void 0)throw new P("INTERNAL",`ctx.db.${r}.patchMany({ where }) is unavailable: this writer has no where-based patch`);return i.patchWhere(r,n,t)},aggregate(r,n){return M(r,[n.field],"aggregate"),u(r,n.where,"aggregate"),i.aggregate(r,n)},count(r,n){const t=F(n),o=t&&("where"in t||"baseWhere"in t||"restrictsCounts"in t)?t.where:n;return u(r,o,"count"),t&&u(r,t.baseWhere,"count"),i.count(r,n)},async findFirst(r,n){u(r,n?.where,"findFirst"),u(r,n?.baseWhere,"findFirst"),I(r,n?.orderBy,"findFirst");const t=await i.findFirst(r,p(n)),o=e.get(r);return t&&o?a(t,o,c):t},async findFirstOrThrow(r,n){u(r,n?.where,"findFirstOrThrow"),u(r,n?.baseWhere,"findFirstOrThrow"),I(r,n?.orderBy,"findFirstOrThrow");const t=await i.findFirstOrThrow(r,p(n)),o=e.get(r);return o?a(t,o,c):t},async findMany(r,n){u(r,n?.where,"findMany"),u(r,n?.baseWhere,"findMany"),I(r,n?.orderBy,"findMany");const t=await i.findMany(r,p(n)),o=e.get(r);return o?W(t,o,c):t},async get(r,n){const{row:t,tableName:o}=await S(r,n),s=o===void 0?void 0:e.get(o);return!t||!s?t:a(t,s,c)},async lookupById(r,n){const t=await i.lookupById?.(r,n);if(!t)return null;const o=e.get(t.tableName);return{row:o?a(t.row,o,c):t.row,tableName:t.tableName}},groupBy(r,n){return M(r,[...n.by,n.agg?.field],"groupBy"),u(r,n.where,"groupBy"),i.groupBy(r,n)},query(r){const n=i.query(r),t=e.get(r);return t?l(n,t,r):n},async rank(r,n,t){return A(r,t,"rank"),k(r,n,"rank","rank"),i.rank(r,n,t)},async rankPage(r,n,t){A(r,t,"rankPage"),k(r,n,"rankPage","rank");const o=await i.rankPage(r,n,t),s=e.get(r);return s?W(o,s,c):o},...v("rankBefore",h,r=>(n,t,o)=>(A(n,o,"rankBefore"),k(n,t,"rankBefore","rank"),r(n,t,o))),...v("rankPageRows",g,r=>async(n,t,o)=>{A(n,o,"rankPageRows"),k(n,t,"rankPageRows","rank");const s=await r(n,t,o),d=e.get(n);return d?{...s,rows:s.rows.map(w=>({...w,doc:a(w.doc,d,c)}))}:s})},j=O;for(const r of e.keys())q(i[r])&&(j[r]=E(O,r));return O},V=(i,e={})=>{const c=new Map(Object.entries(i)),y=D(e.roles),h=async({ctx:f,next:p})=>{const k={auth:await K(f.auth??{},y),ctx:f};if(e.bypass?.(k))return p();const l=L(f.db,c,k,e.indexFields),S={db:l},{orm:M}=f;return M!==null&&typeof M=="object"&&(S.orm=_(l)),p({ctx:S})},g=new Map;for(const[f,p]of c)g.set(f,new Set(Object.keys(p)));return m(h,{columns:g})};export{V as mask};
@@ -1 +0,0 @@
1
- import{LunoraError as W}from"@lunora/errors";import{bindTableFacade as L,bindOrm as T}from"./bindOrm-ChQydkdL.mjs";import{t as j}from"./policy-tag-V0lqFzgS.mjs";import{deny as $}from"./allowAll-DkRAgItV.mjs";const S=(r,t,i)=>t?{[r]:i(t)}:{},D=500,B=(r,t,i)=>{const a=t??D;if(r>a)throw new W("BATCH_LIMIT_EXCEEDED",`${i}: batch of ${String(r)} exceeds the limit of ${String(a)} (raise options.limit or chunk the call)`,{status:400})},q=$(),N=Symbol.for("lunora.ctxdb.rls-unwrap"),K=r=>{const t=new Map;for(const i of r){const a=t.get(i.table)??[];a.push(i),t.set(i.table,a)}return t},Y=(r,t)=>{const i=[];let a=!1;for(const h of r){if(h.on!=="read")continue;const f=h.when(t);if(f!==void 0){if(f===!0){a=!0;break}f!==!1&&i.push(f)}}if(!a)return i.length===0?q:i.length===1?i[0]:{OR:i}},P=r=>typeof r=="object"&&r!==null&&!Array.isArray(r),z=["contains","eq","gt","gte","in","isNull","lt","lte","ne","notIn"],p=r=>{const t=typeof r;return t==="number"||t==="string"||t==="bigint"},H=(r,t)=>!("lt"in t&&(!p(r)||!p(t.lt)||r>=t.lt)||"lte"in t&&(!p(r)||!p(t.lte)||r>t.lte)||"gt"in t&&(!p(r)||!p(t.gt)||r<=t.gt)||"gte"in t&&(!p(r)||!p(t.gte)||r<t.gte)),G=(r,t)=>{if("in"in t){const i=t.in;if(!Array.isArray(i)||!i.includes(r))return!1}if("notIn"in t){const i=t.notIn;if(Array.isArray(i)&&i.includes(r))return!1}if("contains"in t){const i=t.contains;if(typeof r!="string"||typeof i!="string"||!r.includes(i))return!1}return!("isNull"in t&&t.isNull===!0!==(r==null))},Q=(r,t)=>"eq"in t&&r!==t.eq||"ne"in t&&r===t.ne?!1:G(r,t)&&H(r,t),X=(r,t,i,a)=>t==="AND"?Array.isArray(i)&&i.every(h=>a(r,h)):t==="OR"?Array.isArray(i)&&i.some(h=>a(r,h)):!a(r,i??{}),C=r=>r==="AND"||r==="OR"||r==="NOT",J=r=>P(r)&&Object.keys(r).every(t=>z.includes(t)),Z=["every","is","isNot","none","some"],x=r=>P(r)&&Object.keys(r).length>0&&Object.keys(r).every(t=>Z.includes(t)),E=r=>Object.keys(r).some(t=>{const i=r[t];return C(t)?t==="NOT"?E(i??{}):Array.isArray(i)&&i.some(a=>E(a??{})):x(i)}),A=(r,t)=>{for(const i of Object.keys(t)){const a=t[i];if(C(i)){if(!X(r,i,a,A))return!1;continue}const h=r[i];if(J(a)){if(!Q(h,a))return!1;continue}if(h!==a)return!1}return!0},V=(r,t,i)=>{const a=r.when({...t,row:i});if(a===void 0||a===!0)return!0;if(a===!1)return!1;if(E(a))throw new W("RELATION_PREDICATE_UNSUPPORTED","relation predicates are not supported in write policies; use a read policy or a flat column check");return A(i,a)},ee=(r,t,i)=>{const a=r.when(t);if(a===void 0||a===!0)return i===void 0||V(r,t,i);if(a===!1)return!1;if(E(a))throw new W("RELATION_PREDICATE_UNSUPPORTED","relation predicates are not supported in write policies; use a read policy or a flat column check");return!t.row||!A(t.row,a)?!1:i===void 0||A(i,a)},_=(r,t,i,a)=>{let h=!1;for(const f of r)if(f.on===t&&(h=!0,!ee(f,i,a)))return!1;return h},re=new Set(["baseWhere","relationBaseWhere","restrictsCounts","where"]),te=(r,t)=>t===void 0?!0:r==="restrictsCounts"?typeof t=="boolean":r==="relationBaseWhere"?typeof t=="function":P(t),ne=r=>{const t=Object.keys(r);return t.length===0?!1:t.every(i=>re.has(i)&&te(i,r[i]))},se=r=>r===void 0?{}:P(r)&&ne(r)?r:{where:r},w=(r,t)=>!t||Object.keys(t).length===0?r:!r||Object.keys(r).length===0?t:{AND:[t,r]},ie=r=>{if(typeof r!="object"||r===null||Array.isArray(r))return!1;const t=r;return typeof t.findMany=="function"&&typeof t.withSearchIndex=="function"},oe=(r,t,i,a)=>{const h=new Map,f=e=>{const n=h.get(e);if(n)return n;const s=i.get(e);if(!s||s.length===0||!s.some(l=>l.on==="read")){const l={baseWhere:void 0,restricts:!1};return h.set(e,l),l}const c={baseWhere:Y(s,a),restricts:!0};return h.set(e,c),c},d=e=>f(e).restricts?t:r,y=e=>f(e).baseWhere,O=async(e,n)=>{if(t.lookupById){const u=await t.lookupById(e,n);return u?{row:u.row,tableName:i.has(u.tableName)?u.tableName:void 0}:{row:null,tableName:void 0}}const s=await t.get(e,n);if(!s)return{row:null,tableName:void 0};const o=n!==void 0&&i.has(n)?[n]:[],c=n===void 0?[...i.keys()]:o,l=await Promise.all(c.map(async u=>(await t.findFirst(u,{limit:1,where:{_id:e}}))?._id===e?u:void 0));return{row:s,tableName:l.find(u=>u!==void 0)}},M=async(e,n)=>{const s=await O(e,n);return s.row&&s.tableName!==void 0?{row:s.row,tableName:s.tableName}:void 0},g=async(e,n,s,o,c)=>{const l=await M(e,c);if(!l)return s(r);const u=i.get(l.tableName);if(u){const k=o?o(l.row):void 0;if(!_(u,n,{...a,row:l.row},k))throw new W("FORBIDDEN",`${n} denied by policy`)}return s(t)},b=(e,n)=>{const{baseWhere:s,restricts:o}=f(e);if(o)throw new W("COUNT_RLS_UNSUPPORTED",`${n}() is not supported on "${e}" inside an RLS-restricted context`);return s},v=r.rankBefore,m=r.rankPageRows,R={...r,async count(e,n){const{baseWhere:s,restricts:o}=f(e),c=se(n);return d(e).count(e,{...c,baseWhere:w(c.baseWhere,s),relationBaseWhere:y,restrictsCounts:(c.restrictsCounts??!1)||o})},delete:(e,n,s)=>g(e,"delete",o=>o.delete(e,n,s),void 0,n),async deleteAll(e,n){const s=Math.max(1,n?.chunkSize??D),{baseWhere:o}=f(e);let c=0;for(;;){const u=(await d(e).findMany(e,{baseWhere:o,limit:s,relationBaseWhere:y})).page.map(k=>String(k._id));if(u.length===0)break;for(const k of u)await g(k,"delete",I=>I.delete(k,void 0,n?.hard===void 0?void 0:{hard:n.hard})),c+=1;if(u.length<s)break}return{deleted:c}},async deleteMany(e,n,s){B(e.length,n?.limit,"deleteMany");for(const o of e)await g(o,"delete",c=>c.delete(o,s),void 0,s);return{deleted:e.length}},async deleteWhere(e,n,s){const{baseWhere:o}=f(e),l=(await d(e).findMany(e,{baseWhere:w(n,o),relationBaseWhere:y})).page.map(u=>String(u._id));return B(l.length,s?.limit,"deleteWhere"),R.deleteMany(l,s)},async findFirst(e,n){const{baseWhere:s}=f(e);return d(e).findFirst(e,{...n,baseWhere:w(n?.baseWhere,s),relationBaseWhere:y})},async findFirstOrThrow(e,n){const{baseWhere:s}=f(e);return d(e).findFirstOrThrow(e,{...n,baseWhere:w(n?.baseWhere,s),relationBaseWhere:y})},async findMany(e,n){const{baseWhere:s}=f(e);return d(e).findMany(e,{...n,baseWhere:w(n?.baseWhere,s),relationBaseWhere:y})},async get(e,n){const s=await O(e,n);if(!s.row)return null;if(s.tableName===void 0)return r.get(e,n);const{baseWhere:o,restricts:c}=f(s.tableName);if(!c)return r.get(e,n);if(!o)return s.row;const l=await t.findFirst(s.tableName,{baseWhere:o,limit:1,where:{_id:e}});return l?._id===e?l:null},async lookupById(e,n){const s=await O(e,n);if(!s.row)return null;if(s.tableName===void 0||!f(s.tableName).restricts)return await r.lookupById?.(e,n)??null;const{baseWhere:o}=f(s.tableName);if(!o)return{row:s.row,tableName:s.tableName};const c=await t.findFirst(s.tableName,{baseWhere:o,limit:1,where:{_id:e}});return c?._id===e?{row:c,tableName:s.tableName}:null},async insert(e,n){const s=i.get(e);if(s){if(!_(s,"insert",{...a,row:n}))throw new W("FORBIDDEN",`insert on "${e}" denied by policy`);return t.insert(e,n)}return r.insert(e,n)},async insertMany(e,n,s){B(n.length,s?.limit,"insertMany");const o=i.get(e);if(o){for(const c of n)if(!_(o,"insert",{...a,row:c}))throw new W("FORBIDDEN",`insert on "${e}" denied by policy`);return t.insertMany(e,n,s)}return r.insertMany(e,n,s)},async insertManyUnsafe(e,n,s){B(n.length,s?.limit,"insertManyUnsafe");const o=i.get(e);if(o){for(const c of n)if(!_(o,"insert",{...a,row:c}))throw new W("FORBIDDEN",`insert on "${e}" denied by policy`);return t.insertManyUnsafe(e,n,s)}return r.insertManyUnsafe(e,n,s)},patch:(e,n,s)=>g(e,"update",o=>o.patch(e,n,s),o=>({...o,...n}),s),async patchMany(e,n,s){B(e.length,n?.limit,"patchMany");for(const o of e)await g(o.id,"update",c=>c.patch(o.id,o.patch,s),c=>({...c,...o.patch}),s);return{patched:e.length}},async patchWhere(e,n,s){const{baseWhere:o}=f(e),l=(await d(e).findMany(e,{baseWhere:w(n.where,o),relationBaseWhere:y})).page.map(u=>({id:String(u._id),patch:n.patch}));return B(l.length,s?.limit,"patchWhere"),R.patchMany(l,s)},query(e){const{baseWhere:n}=f(e),s=d(e).query(e);return n?s.filter(o=>A(o,n)):s},restore:(e,n)=>g(e,"update",async s=>{const o=s.restore??t.restore;if(!o)throw new W("BAD_REQUEST","restore is not supported by this writer");await o(e,n)},void 0,n),replace:(e,n,s)=>g(e,"update",o=>o.replace(e,n,s),o=>({...n,_creationTime:n._creationTime??o._creationTime,_id:o._id}),s),aggregate(e,n){const{baseWhere:s}=f(e);return d(e).aggregate(e,{...n,baseWhere:w(n.baseWhere,s),relationBaseWhere:y})},groupBy(e,n){const{baseWhere:s}=f(e);return d(e).groupBy(e,{...n,baseWhere:w(n.baseWhere,s),relationBaseWhere:y})},rank(e,n,s){const o=b(e,"rank");return d(e).rank(e,n,{...s,baseWhere:w(s.baseWhere,o)})},rankPage(e,n,s){const o=b(e,"rankPage");return d(e).rankPage(e,n,{...s,baseWhere:w(s?.baseWhere,o)})},wipeShard(){throw new W("FORBIDDEN","ctx.db.wipeShard() is unavailable under rls(): a whole-shard erase can't be policy-gated per row, and erasing only policy-visible rows would silently leave data behind. Run it from an internalMutation, or use ctx.db.deleteAll(table) per table.",{status:403})},...S("rankBefore",v,e=>(n,s,o)=>(b(n,"rankBefore"),(d(n).rankBefore??e)(n,s,o))),...S("rankPageRows",m,e=>(n,s,o)=>{const c=b(n,"rankPageRows");return(d(n).rankPageRows??e)(n,s,{...o,baseWhere:w(o?.baseWhere,c)})})},U=R;for(const e of i.keys())ie(r[e])&&(U[e]=L(R,e));return R},F=r=>typeof r=="string"?r:r.name,ae=r=>{const t=new Map;for(const i of r??[])t.set(i.name,new Set((i.permissions??[]).map(a=>F(a))));return t},ce=(r,t)=>{const i=new Set;for(const a of r)for(const h of t.get(a)??[])i.add(h);return a=>i.has(F(a))},fe=async(r,t)=>{const i=r.roles??[];return{can:ce(i,t),identity:await r.getIdentity?.()??null,roles:i,userId:r.userId??null}},ye=(r,t={})=>{const i=K(r),a=ae(t.roles);return j(async({ctx:f,next:d})=>{const y={auth:await fe(f.auth??{},a),ctx:f},O=f.db,M=O[N]??O,g=oe(O,M,i,y),b={db:g},{orm:v}=f;return v!==null&&typeof v=="object"&&(b.orm=T(g)),d({ctx:b})},[{policies:r,roles:t.roles??[]}])};export{fe as a,ie as b,Y as c,ce as d,_ as e,ae as i,A as m,S as o,F as p,ye as r};
@@ -1 +0,0 @@
1
- import"./bindOrm-ChQydkdL.mjs";import{c as o,e as m,i as t,b as p,m as l,p as c,d as n,a as d,r as h}from"./middleware-Cz5s6CH-.mjs";import"./policy-tag-V0lqFzgS.mjs";import"./allowAll-DkRAgItV.mjs";export{o as computeReadBaseWhere,m as evaluateWrite,t as indexRolePermissions,p as isFacadeEntry,l as matchesWhere,c as permissionName,n as resolveCan,d as resolvePolicyAuth,h as rls};
@@ -1 +0,0 @@
1
- import{LunoraError as y}from"@lunora/errors";import{i as U,a as v}from"./middleware-Cz5s6CH-.mjs";const D=(o,e)=>o===void 0||e.startsWith(o),k=o=>{const e=o[1],c=typeof e=="object"&&e!==null?e.method:void 0;return typeof c=="string"&&c.toUpperCase()==="PUT"?"write":"read"},E=[["delete","delete"],["download","read"],["generateUploadUrl","write"],["getMetadata","read"],["getSignedUrl",k],["getUrl","read"],["head","read"],["store","write"]],S=(o,e={})=>{const c=U(e.roles);return async({ctx:u,next:f})=>{const w=await v(u.auth??{},c),g=(n,r,s)=>{const i=o.filter(t=>t.on===n&&t.bucket===s);if(i.length===0)return;const a={auth:w,ctx:u,key:r};if(!i.some(t=>D(t.prefix,r)&&t.when(a)===!0))throw new y("FORBIDDEN",`storage ${n} on "${r}" in bucket "${s}" denied by access rule`)},p=n=>{const r=n.bucketName??"default",s={bucketName:r};for(const[a,d]of E){const t=n[a];typeof t=="function"&&(s[a]=(...l)=>{const m=typeof l[0]=="string"?l[0]:"",b=typeof d=="function"?d(l):d;return g(b,m,r),t(...l)})}const{bucket:i}=n;return typeof i=="function"&&(s.bucket=a=>p(i(a))),s},h=u.storage;return h===void 0?f():f({ctx:{storage:p(h)}})}};export{S as storageRules};
@@ -1 +0,0 @@
1
- const A=e=>{let t="";for(let o=0;o<e.length;o+=32768)t+=String.fromCharCode(...e.subarray(o,o+32768));return btoa(t)},p=e=>{const t=atob(e),f=new Uint8Array(t.length);for(let o=0;o<t.length;o+=1)f[o]=t.codePointAt(o)??0;return f},i="$lunora.wire$";const d="__proto__",m={BigInt64Array,BigUint64Array,Float32Array,Float64Array,Int8Array,Int16Array,Int32Array,Uint8Array,Uint8ClampedArray,Uint16Array,Uint32Array},w={Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError},E=e=>{if(e===null||typeof e!="object")return!1;const t=Object.getPrototypeOf(e);return t===null||t===Object.prototype},u=(e,t=0)=>{if(t>64)throw new RangeError("wire-codec: value nesting exceeds the 64-level limit");if(e===void 0)return[i,"undefined"];if(e===null)return null;const f=typeof e;if(f==="bigint")return[i,"bigint",e.toString()];if(f==="number"){const r=e;return Number.isNaN(r)?[i,"nan"]:r===1/0?[i,"inf"]:r===-1/0?[i,"-inf"]:r}if(f!=="object")return e;if(e instanceof Date)return[i,"date",u(e.getTime(),t+1)];if(e instanceof Error){const r=e,n={};for(const a of Object.keys(r))r[a]!==void 0&&(n[a]=u(r[a],t+1));const c=[i,"error",r.name,r.message,n];return r.cause!==void 0&&c.push(u(r.cause,t+1)),c}if(e instanceof URL)return[i,"url",e.href];if(e instanceof Map)return[i,"map",[...e.entries()].map(([r,n])=>[u(r,t+1),u(n,t+1)])];if(e instanceof Set)return[i,"set",[...e].map(r=>u(r,t+1))];if(e instanceof ArrayBuffer)return[i,"bytes",A(new Uint8Array(e)),"ArrayBuffer"];if(ArrayBuffer.isView(e)){const r=e,n=r.constructor.name,c=new Uint8Array(r.buffer,r.byteOffset,r.byteLength);return n==="Uint8Array"?[i,"bytes",A(c)]:[i,"bytes",A(c),n]}if(Array.isArray(e)){const r=e.map(n=>u(n,t+1));return r.length>0&&r[0]===i?[i,"arr",r]:r}if(!E(e)){const r=e.constructor?.name??"value";throw new TypeError(`wire-codec: cannot encode a ${r} over the Lunora wire — only plain objects, arrays, and the supported built-ins (Date, Error, URL, Map, Set, ArrayBuffer/typed arrays, bigint) round-trip`)}const o=e,s={};for(const r of Object.keys(o)){const n=o[r];if(n===void 0)continue;const c=u(n,t+1);r===d?Object.defineProperty(s,r,{configurable:!0,enumerable:!0,value:c,writable:!0}):s[r]=c}return s},y=(e,t=0)=>{if(t>64)throw new RangeError("wire-codec: value nesting exceeds the 64-level limit");if(e===null||typeof e!="object")return e;if(Array.isArray(e)){if(e[0]===i)switch(e[1]){case"-inf":return-1/0;case"arr":return e[2].map(r=>y(r,t+1));case"bigint":{const r=e[2];if(typeof r!="string"||r.length>1024||!/^-?\d+$/.test(r))throw new RangeError("wire-codec: invalid or over-long bigint (max 1024 digits)");return BigInt(r)}case"date":return new Date(y(e[2],t+1));case"map":{const r=e[2];return new Map(r.map(n=>{if(!Array.isArray(n)||n.length!==2)throw new TypeError("wire-codec: malformed map entry — expected a [key, value] pair");return[y(n[0],t+1),y(n[1],t+1)]}))}case"set":return new Set(e[2].map(r=>y(r,t+1)));case"url":return new URL(e[2]);case"error":{const r=e[2],n=e[3],c=(Object.hasOwn(w,r)?w[r]:void 0)??Error,a=new c(n);a.name!==r&&Object.defineProperty(a,"name",{configurable:!0,value:r,writable:!0});const g=y(e[4],t+1);for(const b of Object.keys(g))b===d?Object.defineProperty(a,b,{configurable:!0,enumerable:!0,value:g[b],writable:!0}):a[b]=g[b];return e.length>5&&Object.defineProperty(a,"cause",{configurable:!0,value:y(e[5],t+1),writable:!0}),a}case"bytes":{const r=p(e[2]),n=e[3]??"Uint8Array";if(n==="ArrayBuffer")return r.buffer.byteLength===r.byteLength?r.buffer:r.slice().buffer;const c=Object.hasOwn(m,n)?m[n]:void 0;return c?new c(r.slice().buffer):r}case"inf":return 1/0;case"nan":return Number.NaN;case"undefined":return;default:return e.map(r=>y(r,t+1))}return e.map(s=>y(s,t+1))}const f=e,o={};for(const s of Object.keys(f)){const r=y(f[s],t+1);s===d?Object.defineProperty(o,s,{configurable:!0,enumerable:!0,value:r,writable:!0}):o[s]=r}return o};export{y as d,u as e};