@lunora/server 1.0.0-alpha.107 → 1.0.0-alpha.109
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 +103 -10
- package/dist/index.d.ts +103 -10
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/{ACTION_CACHE_DEFAULT_TTL_MS-CBCYHUFV.mjs → ACTION_CACHE_DEFAULT_TTL_MS-BUng3KOy.mjs} +1 -1
- package/dist/packem_shared/{DOCUMENT_HISTORY_REDACTED_FIELDS-DScATYVh.mjs → DOCUMENT_HISTORY_REDACTED_FIELDS-g-fdcytr.mjs} +1 -1
- package/dist/packem_shared/{PRESENCE_DEFAULT_TTL_MS-lsooaSaF.mjs → PRESENCE_DEFAULT_TTL_MS-DheiApL3.mjs} +1 -1
- package/dist/packem_shared/assertShapesDeclareReadPolicies-CL7JKaIK.mjs +1 -0
- package/dist/packem_shared/{compose-middleware-3i5Qk3sb.mjs → compose-middleware-MCpZZKh7.mjs} +1 -1
- package/dist/packem_shared/{composePluginMiddleware-pe7Ncehp.mjs → composePluginMiddleware-D4tocdAs.mjs} +1 -1
- package/dist/packem_shared/{defineAggregateIndex-DFVCeuX1.mjs → defineAggregateIndex-COm6F0QJ.mjs} +1 -1
- package/dist/packem_shared/defineShape-DH52g6sI.mjs +1 -0
- package/dist/packem_shared/httpAction-DfKw8ZZt.mjs +5 -0
- package/dist/packem_shared/{initLunora-D5TSiy5j.mjs → initLunora-BzC2zEfH.mjs} +1 -1
- package/dist/packem_shared/mask-DHt_WzTL.mjs +1 -0
- package/dist/packem_shared/middleware-CoVBvN2z.mjs +1 -0
- package/dist/packem_shared/{plugin-yKCbHnlj.mjs → plugin-DY0N7ZWx.mjs} +1 -1
- package/dist/packem_shared/{protectPublic-Csf6ObbJ.mjs → protectPublic-C-STrTgA.mjs} +1 -1
- package/dist/packem_shared/{rls-BjHAvbpA.mjs → rls-DrwDXR5u.mjs} +1 -1
- package/dist/packem_shared/run-middleware-laenw2jQ.mjs +1 -0
- package/dist/packem_shared/{serveStorageObject-te5TWJIE.mjs → serveStorageObject-BzZEP-B5.mjs} +1 -1
- package/dist/packem_shared/{storageRules-B8dXzTI2.mjs → storageRules-BleDJNal.mjs} +1 -1
- package/dist/rls/testing.mjs +1 -1
- package/package.json +5 -5
- package/dist/packem_shared/buildRlsReadRegistry-r63qMKN6.mjs +0 -1
- package/dist/packem_shared/defineShape-BWgwFuMT.mjs +0 -1
- package/dist/packem_shared/httpAction-YRpmc049.mjs +0 -5
- package/dist/packem_shared/mask-Ca8SAJsC.mjs +0 -1
- package/dist/packem_shared/middleware-CJSowZY0.mjs +0 -1
- package/dist/packem_shared/run-middleware-CuM-Chm3.mjs +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -3145,15 +3145,70 @@ interface ShapeReadWhereRequest {
|
|
|
3145
3145
|
readonly userId: null | string;
|
|
3146
3146
|
}
|
|
3147
3147
|
/**
|
|
3148
|
-
* Build the read-policy registry from the
|
|
3149
|
-
* `
|
|
3150
|
-
*
|
|
3151
|
-
*
|
|
3152
|
-
*
|
|
3153
|
-
*
|
|
3154
|
-
*
|
|
3155
|
-
|
|
3156
|
-
|
|
3148
|
+
* Build the read-policy registry for ONE consumer from the `rls()` guards it
|
|
3149
|
+
* declares — `defineShape({ use })` passes its own list, and `defineShape` calls
|
|
3150
|
+
* this once at declaration time. Accepts either the tagged middlewares
|
|
3151
|
+
* themselves or anything carrying hoisted `fn.rls.tags`.
|
|
3152
|
+
*
|
|
3153
|
+
* Only `on: "read"` policies are collected, grouped per `rls()` middleware so
|
|
3154
|
+
* each group keeps its own role→permission map (a `(table, when)` pair is
|
|
3155
|
+
* de-duplicated within a tag). The same tag listed twice (a shared
|
|
3156
|
+
* `const guard = rls(...)`) is folded once. Within a group this mirrors the
|
|
3157
|
+
* request-time `rls()` path: a policy's `auth.can(...)` resolves against the
|
|
3158
|
+
* roles of the middleware that declared it, never a union.
|
|
3159
|
+
*
|
|
3160
|
+
* Do NOT hand this every registered function in the project — see the SCOPE
|
|
3161
|
+
* paragraph in the module docblock for what that silently did to tenant shapes.
|
|
3162
|
+
*/
|
|
3163
|
+
declare const buildRlsReadRegistry: (guards: Iterable<unknown>) => RlsReadRegistry;
|
|
3164
|
+
/** One entry of the generated `LUNORA_SHAPES` registry, as {@link assertShapesDeclareReadPolicies} reads it. */
|
|
3165
|
+
interface ShapeGuardDeclaration {
|
|
3166
|
+
/** The registry `defineShape` already built from `use` — the authority on what those guards actually govern. */
|
|
3167
|
+
readonly rlsRegistry: RlsReadRegistry;
|
|
3168
|
+
/** Logical table the shape replicates. */
|
|
3169
|
+
readonly table: string;
|
|
3170
|
+
/** Present (even as `[]`) exactly when the declaration wrote `use` — the acknowledgement bit. */
|
|
3171
|
+
readonly use?: ReadonlyArray<unknown>;
|
|
3172
|
+
}
|
|
3173
|
+
/**
|
|
3174
|
+
* Fail a project's startup closed when a shape would replicate AROUND the read
|
|
3175
|
+
* policies its table is governed by.
|
|
3176
|
+
*
|
|
3177
|
+
* Scoping shape RLS to `defineShape({ use })` fixed one direction (a project-wide
|
|
3178
|
+
* union let one admin-only `rls([{ on: "read", when: () => true }])` unrestrict
|
|
3179
|
+
* every tenant shape) and opened the other: under the OPT-IN default a shape that
|
|
3180
|
+
* names no guard has an empty registry, `resolveReadBaseWhere` returns
|
|
3181
|
+
* `undefined`, and the shape replicates on its own `where` alone. An app whose
|
|
3182
|
+
* `messages` table is tenant-scoped on its queries and exposes
|
|
3183
|
+
* `defineShape({ table: "messages", where: (_c, { channelId }) => ({ channelId }) })`
|
|
3184
|
+
* therefore replicated `{ channelId }` where it used to replicate
|
|
3185
|
+
* `{ AND: [{ channelId }, { tenantId }] }` — every tenant's messages, to every
|
|
3186
|
+
* subscriber, with no error and no diagnostic. Silence is the defect; this is the
|
|
3187
|
+
* noise.
|
|
3188
|
+
*
|
|
3189
|
+
* `readPolicyTables` is the set of tables the project's `.use(rls(...))` chains
|
|
3190
|
+
* declare an `on: "read"` policy for — statically discovered by codegen, which
|
|
3191
|
+
* stamps the call into the generated shard. The registry is the authority on the
|
|
3192
|
+
* other half: `use` is present exactly when the declaration wrote it, so
|
|
3193
|
+
* `use: []` is the explicit acknowledgement ("this shape's own `where` is the
|
|
3194
|
+
* whole filter") and a missing `use` on a governed table is the accident.
|
|
3195
|
+
*
|
|
3196
|
+
* A non-empty `use` has to earn it: the shape's own `rlsRegistry` must hold a
|
|
3197
|
+
* read-policy group for its table. A list naming a guard for a DIFFERENT table,
|
|
3198
|
+
* or a middleware with no `rls()` policies on it at all, produces no group and
|
|
3199
|
+
* leaves the shape filtered by its `where` alone — the same fail-open, hidden
|
|
3200
|
+
* behind a `use` that looks answered.
|
|
3201
|
+
*
|
|
3202
|
+
* Skipped under `.rls("required")`: there a guard-less shape over a non-`.public()`
|
|
3203
|
+
* table already replicates nothing (see `resolveReadBaseWhere`), so the omission
|
|
3204
|
+
* cannot leak — it is the secure-by-default answer, not a fail-open.
|
|
3205
|
+
*
|
|
3206
|
+
* Throws at module scope in the generated shard, so it surfaces the moment the
|
|
3207
|
+
* worker boots rather than on the first subscription. A codegen-time check would
|
|
3208
|
+
* be louder still, but codegen sees only the shape IR, which does not lift `use`
|
|
3209
|
+
* — and the registry, which does, exists only at runtime.
|
|
3210
|
+
*/
|
|
3211
|
+
declare const assertShapesDeclareReadPolicies: (shapes: Readonly<Record<string, ShapeGuardDeclaration>>, readPolicyTables: Iterable<string>, rlsRequired: boolean) => void;
|
|
3157
3212
|
/**
|
|
3158
3213
|
* Compute the effective `where` a shape replicates: the table's RLS read
|
|
3159
3214
|
* base-where AND the shape's own predicate. Returns the shape predicate
|
|
@@ -3203,6 +3258,32 @@ interface ShapeDefinition<Args extends ValidatorMap = ValidatorMap, Context = Qu
|
|
|
3203
3258
|
readonly owner?: string | true;
|
|
3204
3259
|
/** Logical table this shape replicates a partition of. */
|
|
3205
3260
|
readonly table: string;
|
|
3261
|
+
/**
|
|
3262
|
+
* The `rls(...)` guards whose **read** policies gate this shape, exactly as
|
|
3263
|
+
* a procedure lists them with `.use(rls(...))`.
|
|
3264
|
+
*
|
|
3265
|
+
* A shape runs no procedure, so it has no `.use()` chain to inherit from —
|
|
3266
|
+
* it has to name its own guards. Their read policies are evaluated under the
|
|
3267
|
+
* socket's verified identity and AND-composed with `where` before a single
|
|
3268
|
+
* row replicates, and (like a procedure's chain) several guards compose:
|
|
3269
|
+
* every one of them must admit a row.
|
|
3270
|
+
*
|
|
3271
|
+
* SCOPE, and why it is per-shape: an `rls()` bundle is scoped to the
|
|
3272
|
+
* procedures that opt into it. A shape naming no guard therefore replicates
|
|
3273
|
+
* on `where` alone — the same deal a query with no `.use(rls(...))` gets.
|
|
3274
|
+
* Under a `.rls("required")` schema that is a hard denial for any
|
|
3275
|
+
* non-`.public()` table (nothing replicates), which is the secure-by-default
|
|
3276
|
+
* answer and the reason `required` exists. Otherwise it is a boot failure
|
|
3277
|
+
* whenever the table IS governed on read (see
|
|
3278
|
+
* `assertShapesDeclareReadPolicies`); write `use: []` for a shape that is
|
|
3279
|
+
* meant to be ungoverned.
|
|
3280
|
+
*
|
|
3281
|
+
* Only `rls()` middlewares are read here — their policies, not their bodies.
|
|
3282
|
+
* A shape cannot run an authorization middleware, so a procedure-level gate
|
|
3283
|
+
* (`requireAdmin`, a rate limiter) has no effect on a shape and must not be
|
|
3284
|
+
* relied on to protect one.
|
|
3285
|
+
*/
|
|
3286
|
+
readonly use?: ReadonlyArray<Middleware<never, unknown>>;
|
|
3206
3287
|
/**
|
|
3207
3288
|
* Predicate selecting the rows this shape replicates. AND-composed with the
|
|
3208
3289
|
* table's RLS read base-where on the DO. Runs server-side with a trusted
|
|
@@ -3235,6 +3316,18 @@ interface RegisteredShape<Args extends ValidatorMap = ValidatorMap, Context = Qu
|
|
|
3235
3316
|
readonly compileWhere: (context: unknown, rawArgs: Record<string, unknown>, options?: {
|
|
3236
3317
|
ownerField?: string;
|
|
3237
3318
|
}) => WhereInput;
|
|
3319
|
+
/**
|
|
3320
|
+
* Read-policy registry built from THIS shape's `use` guards — the argument
|
|
3321
|
+
* the DO's `resolveShape` hands `composeShapeReadWhere`.
|
|
3322
|
+
*
|
|
3323
|
+
* Built here, per shape, and not project-wide: the registry is a union, so a
|
|
3324
|
+
* project-wide one folded in the read policies of every procedure that names
|
|
3325
|
+
* the table, and a single `rls([{ on: "read", when: () => true }])` inside an
|
|
3326
|
+
* admin-only procedure collapsed a tenant shape's filter to "no filter" for
|
|
3327
|
+
* every subscriber. Request-time scope is per-procedure; a shape's scope is
|
|
3328
|
+
* its own `use` list.
|
|
3329
|
+
*/
|
|
3330
|
+
readonly rlsRegistry: RlsReadRegistry;
|
|
3238
3331
|
}
|
|
3239
3332
|
/** Declare a replication shape. See the module docs for runtime semantics. */
|
|
3240
3333
|
declare const defineShape: <Args extends ValidatorMap = ValidatorMap, Context = QueryCtx>(definition: ShapeDefinition<Args, Context>) => RegisteredShape<Args, Context>;
|
|
@@ -3339,4 +3432,4 @@ interface StorageContextIn {
|
|
|
3339
3432
|
}
|
|
3340
3433
|
declare const storageRules: <Context extends StorageContextIn = StorageContextIn>(rules: ReadonlyArray<StorageRule<Context>>, options?: StorageRulesOptions) => Middleware<Context, Context>;
|
|
3341
3434
|
declare const VERSION = "0.0.0";
|
|
3342
|
-
export { DEFAULT_ACTION_CACHE_TTL_MS as ACTION_CACHE_DEFAULT_TTL_MS, ACTION_CACHE_TABLE, type ActionBuilder, type ActionCacheComponent, type ActionCacheContext, type ActionCacheDatabase, type ActionCacheFunctions, type ActionCtx, type AggregateIndexDefinition, type AggregateIndexOptions, type AggregateOp, type ArgsValidator, type Component, type ComponentFunctions, type CreateOptions, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_REDACTED_FIELDS as DOCUMENT_HISTORY_REDACTED_FIELDS, DOCUMENT_HISTORY_TABLE, type DataModelInit, type DeferredDeleteFlushResult, type DefineActionCacheOptions, type DefineComponentOptions, type DefineDocumentHistoryOptions, type DefineIdentityOptions, type DefineListArgsConfig, type DefinePluginOptions, type DefinePolicyInput, type DefinePresenceOptions, type DefineStorageRuleInput, type DocumentHistoryComponent, type DocumentHistoryEntry, type DocumentHistoryFunctions, type DurableObjectJurisdiction, type DurableStreamOptions, type EmptyArgs, type EnvAccessor, type EnvKeyFailure, type EnvShape, type ExposeConfig, type ExtendableSchema, type FacadeEntry, type FacadeWriterLike, type FunctionKind, type HttpActionCtx, type HttpActionHandler, type HttpMethod, type HttpRoute, type HttpRouteBuilder, type HttpRouteFactory, type HttpRouteHandlerOptions, type HttpRunners, type HttpStreamHandlerOptions, type IdentityContract, type IdentityRejectMode, type IdentityValidation, type IndexFieldsByTable, type InferArgs, type InferEnv, type InferIdentity, type InlineAggregateIndexOptions, type InlineRankIndexOptions, type InternalActionBuilder, type InternalMutationBuilder, type InternalQueryBuilder, type LifecycleEvent, type LifecycleHandler, type ListArgsSpec, type ListArgsValidators, type ListArgsValue, type ListFilterOperators, type ListOrderByEntry, type ListWhere, type LunoraBuilders, LunoraEnvError, type LunoraHttpApp, type LunoraHttpEnv, type LunoraRouteHandler, type ManyRelation, type MaskColumns, type MaskContext, type MaskFn, type MaskOptions, type MaskPolicies, type MaskRegistry, type MaskStrategy, type Middleware, type MiddlewareContext, type MiddlewareNext, type MigrationCtx, type MigrationDefinition, type MigrationDocument, type MigrationReader, type MigrationTransform, type MutationBuilder, type MutationCtx, type MutatorDefinition, type OnDeleteAction, type OneRelation, type OrmLike, DEFAULT_TTL_MS as PRESENCE_DEFAULT_TTL_MS, PRESENCE_TABLE, type Permission, type Plugin, type Policy, type PrefixedTables, type PresenceComponent, type PresenceFunctions, type PresenceMember, type ProtectPublicOptions, type QueryBuilder, type QueryCtx, type RankIndexDefinition, type RankIndexOptions, type ReactorHandler, type ReactorOutcome, type ReactorSelect, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMigration, type RegisteredMutation, type RegisteredMutator, type RegisteredQuery, type RegisteredReactor, type RegisteredShape, type RegisteredStream, type RelationBuilder, type RelationDefinition, type RlsOptions, type RlsReadRegistry, type Role, type Schema, type SchemaExtension, type ShapeDefinition, type ShapeReadWhereRequest, type ShardInitEvent, type ShardInitHandler, type StorageOperation, type StorageRule, type StorageRuleContext, type StorageRuleDecision, type StorageRulesOptions, type StorageServeAuthorizer, type StorageServeAuthzContext, 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, actionCacheExtension, allowAll, asBucketStorage, beginDeferredSchedules, bindOrm, bindTableFacade, buildMaskRegistry, buildRlsReadRegistry, cacheKeyFor, clampLimit, composePluginMiddleware, composeShapeReadWhere, createPolicyDsl, createSecrets, defineActionCache, defineAggregateIndex, defineComponent, defineDocumentHistory, defineEnv, defineIdentity, defineListArgs, defineMigration, defineMutator, definePermission, definePlugin, definePolicies, definePolicy, definePresence, defineRankIndex, defineRole, defineSchema, defineSchemaExtension, defineShape, defineStorageRule, defineStorageRules, defineTable, defineVectorIndex, deny, documentHistoryExtension, flushDeferredDeletes, httpAction, httpRoute, httpRouter, indexFieldsFromSchema, initLunora, installPlugins, isDeny, isSafeHeaderValue, mask, mergeSchemaExtension, onConnect, onDisconnect, onQueryChange, onShardInit, presenceExtension, protectPublic, redactSecrets, rls, serveStorageObject, storageRules, toWhereInput, withDeferredDeletes, withDeferredSchedules };
|
|
3435
|
+
export { DEFAULT_ACTION_CACHE_TTL_MS as ACTION_CACHE_DEFAULT_TTL_MS, ACTION_CACHE_TABLE, type ActionBuilder, type ActionCacheComponent, type ActionCacheContext, type ActionCacheDatabase, type ActionCacheFunctions, type ActionCtx, type AggregateIndexDefinition, type AggregateIndexOptions, type AggregateOp, type ArgsValidator, type Component, type ComponentFunctions, type CreateOptions, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_REDACTED_FIELDS as DOCUMENT_HISTORY_REDACTED_FIELDS, DOCUMENT_HISTORY_TABLE, type DataModelInit, type DeferredDeleteFlushResult, type DefineActionCacheOptions, type DefineComponentOptions, type DefineDocumentHistoryOptions, type DefineIdentityOptions, type DefineListArgsConfig, type DefinePluginOptions, type DefinePolicyInput, type DefinePresenceOptions, type DefineStorageRuleInput, type DocumentHistoryComponent, type DocumentHistoryEntry, type DocumentHistoryFunctions, type DurableObjectJurisdiction, type DurableStreamOptions, type EmptyArgs, type EnvAccessor, type EnvKeyFailure, type EnvShape, type ExposeConfig, type ExtendableSchema, type FacadeEntry, type FacadeWriterLike, type FunctionKind, type HttpActionCtx, type HttpActionHandler, type HttpMethod, type HttpRoute, type HttpRouteBuilder, type HttpRouteFactory, type HttpRouteHandlerOptions, type HttpRunners, type HttpStreamHandlerOptions, type IdentityContract, type IdentityRejectMode, type IdentityValidation, type IndexFieldsByTable, type InferArgs, type InferEnv, type InferIdentity, type InlineAggregateIndexOptions, type InlineRankIndexOptions, type InternalActionBuilder, type InternalMutationBuilder, type InternalQueryBuilder, type LifecycleEvent, type LifecycleHandler, type ListArgsSpec, type ListArgsValidators, type ListArgsValue, type ListFilterOperators, type ListOrderByEntry, type ListWhere, type LunoraBuilders, LunoraEnvError, type LunoraHttpApp, type LunoraHttpEnv, type LunoraRouteHandler, type ManyRelation, type MaskColumns, type MaskContext, type MaskFn, type MaskOptions, type MaskPolicies, type MaskRegistry, type MaskStrategy, type Middleware, type MiddlewareContext, type MiddlewareNext, type MigrationCtx, type MigrationDefinition, type MigrationDocument, type MigrationReader, type MigrationTransform, type MutationBuilder, type MutationCtx, type MutatorDefinition, type OnDeleteAction, type OneRelation, type OrmLike, DEFAULT_TTL_MS as PRESENCE_DEFAULT_TTL_MS, PRESENCE_TABLE, type Permission, type Plugin, type Policy, type PrefixedTables, type PresenceComponent, type PresenceFunctions, type PresenceMember, type ProtectPublicOptions, type QueryBuilder, type QueryCtx, type RankIndexDefinition, type RankIndexOptions, type ReactorHandler, type ReactorOutcome, type ReactorSelect, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMigration, type RegisteredMutation, type RegisteredMutator, type RegisteredQuery, type RegisteredReactor, type RegisteredShape, type RegisteredStream, type RelationBuilder, type RelationDefinition, type RlsOptions, type RlsReadRegistry, type Role, type Schema, type SchemaExtension, type ShapeDefinition, type ShapeGuardDeclaration, type ShapeReadWhereRequest, type ShardInitEvent, type ShardInitHandler, type StorageOperation, type StorageRule, type StorageRuleContext, type StorageRuleDecision, type StorageRulesOptions, type StorageServeAuthorizer, type StorageServeAuthzContext, 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, actionCacheExtension, allowAll, asBucketStorage, assertShapesDeclareReadPolicies, beginDeferredSchedules, bindOrm, bindTableFacade, buildMaskRegistry, buildRlsReadRegistry, cacheKeyFor, clampLimit, composePluginMiddleware, composeShapeReadWhere, createPolicyDsl, createSecrets, defineActionCache, defineAggregateIndex, defineComponent, defineDocumentHistory, defineEnv, defineIdentity, defineListArgs, defineMigration, defineMutator, definePermission, definePlugin, definePolicies, definePolicy, definePresence, defineRankIndex, defineRole, defineSchema, defineSchemaExtension, defineShape, defineStorageRule, defineStorageRules, defineTable, defineVectorIndex, deny, documentHistoryExtension, flushDeferredDeletes, httpAction, httpRoute, httpRouter, indexFieldsFromSchema, initLunora, installPlugins, isDeny, isSafeHeaderValue, mask, mergeSchemaExtension, onConnect, onDisconnect, onQueryChange, onShardInit, presenceExtension, protectPublic, redactSecrets, rls, serveStorageObject, storageRules, toWhereInput, withDeferredDeletes, withDeferredSchedules };
|
package/dist/index.d.ts
CHANGED
|
@@ -3145,15 +3145,70 @@ interface ShapeReadWhereRequest {
|
|
|
3145
3145
|
readonly userId: null | string;
|
|
3146
3146
|
}
|
|
3147
3147
|
/**
|
|
3148
|
-
* Build the read-policy registry from the
|
|
3149
|
-
* `
|
|
3150
|
-
*
|
|
3151
|
-
*
|
|
3152
|
-
*
|
|
3153
|
-
*
|
|
3154
|
-
*
|
|
3155
|
-
|
|
3156
|
-
|
|
3148
|
+
* Build the read-policy registry for ONE consumer from the `rls()` guards it
|
|
3149
|
+
* declares — `defineShape({ use })` passes its own list, and `defineShape` calls
|
|
3150
|
+
* this once at declaration time. Accepts either the tagged middlewares
|
|
3151
|
+
* themselves or anything carrying hoisted `fn.rls.tags`.
|
|
3152
|
+
*
|
|
3153
|
+
* Only `on: "read"` policies are collected, grouped per `rls()` middleware so
|
|
3154
|
+
* each group keeps its own role→permission map (a `(table, when)` pair is
|
|
3155
|
+
* de-duplicated within a tag). The same tag listed twice (a shared
|
|
3156
|
+
* `const guard = rls(...)`) is folded once. Within a group this mirrors the
|
|
3157
|
+
* request-time `rls()` path: a policy's `auth.can(...)` resolves against the
|
|
3158
|
+
* roles of the middleware that declared it, never a union.
|
|
3159
|
+
*
|
|
3160
|
+
* Do NOT hand this every registered function in the project — see the SCOPE
|
|
3161
|
+
* paragraph in the module docblock for what that silently did to tenant shapes.
|
|
3162
|
+
*/
|
|
3163
|
+
declare const buildRlsReadRegistry: (guards: Iterable<unknown>) => RlsReadRegistry;
|
|
3164
|
+
/** One entry of the generated `LUNORA_SHAPES` registry, as {@link assertShapesDeclareReadPolicies} reads it. */
|
|
3165
|
+
interface ShapeGuardDeclaration {
|
|
3166
|
+
/** The registry `defineShape` already built from `use` — the authority on what those guards actually govern. */
|
|
3167
|
+
readonly rlsRegistry: RlsReadRegistry;
|
|
3168
|
+
/** Logical table the shape replicates. */
|
|
3169
|
+
readonly table: string;
|
|
3170
|
+
/** Present (even as `[]`) exactly when the declaration wrote `use` — the acknowledgement bit. */
|
|
3171
|
+
readonly use?: ReadonlyArray<unknown>;
|
|
3172
|
+
}
|
|
3173
|
+
/**
|
|
3174
|
+
* Fail a project's startup closed when a shape would replicate AROUND the read
|
|
3175
|
+
* policies its table is governed by.
|
|
3176
|
+
*
|
|
3177
|
+
* Scoping shape RLS to `defineShape({ use })` fixed one direction (a project-wide
|
|
3178
|
+
* union let one admin-only `rls([{ on: "read", when: () => true }])` unrestrict
|
|
3179
|
+
* every tenant shape) and opened the other: under the OPT-IN default a shape that
|
|
3180
|
+
* names no guard has an empty registry, `resolveReadBaseWhere` returns
|
|
3181
|
+
* `undefined`, and the shape replicates on its own `where` alone. An app whose
|
|
3182
|
+
* `messages` table is tenant-scoped on its queries and exposes
|
|
3183
|
+
* `defineShape({ table: "messages", where: (_c, { channelId }) => ({ channelId }) })`
|
|
3184
|
+
* therefore replicated `{ channelId }` where it used to replicate
|
|
3185
|
+
* `{ AND: [{ channelId }, { tenantId }] }` — every tenant's messages, to every
|
|
3186
|
+
* subscriber, with no error and no diagnostic. Silence is the defect; this is the
|
|
3187
|
+
* noise.
|
|
3188
|
+
*
|
|
3189
|
+
* `readPolicyTables` is the set of tables the project's `.use(rls(...))` chains
|
|
3190
|
+
* declare an `on: "read"` policy for — statically discovered by codegen, which
|
|
3191
|
+
* stamps the call into the generated shard. The registry is the authority on the
|
|
3192
|
+
* other half: `use` is present exactly when the declaration wrote it, so
|
|
3193
|
+
* `use: []` is the explicit acknowledgement ("this shape's own `where` is the
|
|
3194
|
+
* whole filter") and a missing `use` on a governed table is the accident.
|
|
3195
|
+
*
|
|
3196
|
+
* A non-empty `use` has to earn it: the shape's own `rlsRegistry` must hold a
|
|
3197
|
+
* read-policy group for its table. A list naming a guard for a DIFFERENT table,
|
|
3198
|
+
* or a middleware with no `rls()` policies on it at all, produces no group and
|
|
3199
|
+
* leaves the shape filtered by its `where` alone — the same fail-open, hidden
|
|
3200
|
+
* behind a `use` that looks answered.
|
|
3201
|
+
*
|
|
3202
|
+
* Skipped under `.rls("required")`: there a guard-less shape over a non-`.public()`
|
|
3203
|
+
* table already replicates nothing (see `resolveReadBaseWhere`), so the omission
|
|
3204
|
+
* cannot leak — it is the secure-by-default answer, not a fail-open.
|
|
3205
|
+
*
|
|
3206
|
+
* Throws at module scope in the generated shard, so it surfaces the moment the
|
|
3207
|
+
* worker boots rather than on the first subscription. A codegen-time check would
|
|
3208
|
+
* be louder still, but codegen sees only the shape IR, which does not lift `use`
|
|
3209
|
+
* — and the registry, which does, exists only at runtime.
|
|
3210
|
+
*/
|
|
3211
|
+
declare const assertShapesDeclareReadPolicies: (shapes: Readonly<Record<string, ShapeGuardDeclaration>>, readPolicyTables: Iterable<string>, rlsRequired: boolean) => void;
|
|
3157
3212
|
/**
|
|
3158
3213
|
* Compute the effective `where` a shape replicates: the table's RLS read
|
|
3159
3214
|
* base-where AND the shape's own predicate. Returns the shape predicate
|
|
@@ -3203,6 +3258,32 @@ interface ShapeDefinition<Args extends ValidatorMap = ValidatorMap, Context = Qu
|
|
|
3203
3258
|
readonly owner?: string | true;
|
|
3204
3259
|
/** Logical table this shape replicates a partition of. */
|
|
3205
3260
|
readonly table: string;
|
|
3261
|
+
/**
|
|
3262
|
+
* The `rls(...)` guards whose **read** policies gate this shape, exactly as
|
|
3263
|
+
* a procedure lists them with `.use(rls(...))`.
|
|
3264
|
+
*
|
|
3265
|
+
* A shape runs no procedure, so it has no `.use()` chain to inherit from —
|
|
3266
|
+
* it has to name its own guards. Their read policies are evaluated under the
|
|
3267
|
+
* socket's verified identity and AND-composed with `where` before a single
|
|
3268
|
+
* row replicates, and (like a procedure's chain) several guards compose:
|
|
3269
|
+
* every one of them must admit a row.
|
|
3270
|
+
*
|
|
3271
|
+
* SCOPE, and why it is per-shape: an `rls()` bundle is scoped to the
|
|
3272
|
+
* procedures that opt into it. A shape naming no guard therefore replicates
|
|
3273
|
+
* on `where` alone — the same deal a query with no `.use(rls(...))` gets.
|
|
3274
|
+
* Under a `.rls("required")` schema that is a hard denial for any
|
|
3275
|
+
* non-`.public()` table (nothing replicates), which is the secure-by-default
|
|
3276
|
+
* answer and the reason `required` exists. Otherwise it is a boot failure
|
|
3277
|
+
* whenever the table IS governed on read (see
|
|
3278
|
+
* `assertShapesDeclareReadPolicies`); write `use: []` for a shape that is
|
|
3279
|
+
* meant to be ungoverned.
|
|
3280
|
+
*
|
|
3281
|
+
* Only `rls()` middlewares are read here — their policies, not their bodies.
|
|
3282
|
+
* A shape cannot run an authorization middleware, so a procedure-level gate
|
|
3283
|
+
* (`requireAdmin`, a rate limiter) has no effect on a shape and must not be
|
|
3284
|
+
* relied on to protect one.
|
|
3285
|
+
*/
|
|
3286
|
+
readonly use?: ReadonlyArray<Middleware<never, unknown>>;
|
|
3206
3287
|
/**
|
|
3207
3288
|
* Predicate selecting the rows this shape replicates. AND-composed with the
|
|
3208
3289
|
* table's RLS read base-where on the DO. Runs server-side with a trusted
|
|
@@ -3235,6 +3316,18 @@ interface RegisteredShape<Args extends ValidatorMap = ValidatorMap, Context = Qu
|
|
|
3235
3316
|
readonly compileWhere: (context: unknown, rawArgs: Record<string, unknown>, options?: {
|
|
3236
3317
|
ownerField?: string;
|
|
3237
3318
|
}) => WhereInput;
|
|
3319
|
+
/**
|
|
3320
|
+
* Read-policy registry built from THIS shape's `use` guards — the argument
|
|
3321
|
+
* the DO's `resolveShape` hands `composeShapeReadWhere`.
|
|
3322
|
+
*
|
|
3323
|
+
* Built here, per shape, and not project-wide: the registry is a union, so a
|
|
3324
|
+
* project-wide one folded in the read policies of every procedure that names
|
|
3325
|
+
* the table, and a single `rls([{ on: "read", when: () => true }])` inside an
|
|
3326
|
+
* admin-only procedure collapsed a tenant shape's filter to "no filter" for
|
|
3327
|
+
* every subscriber. Request-time scope is per-procedure; a shape's scope is
|
|
3328
|
+
* its own `use` list.
|
|
3329
|
+
*/
|
|
3330
|
+
readonly rlsRegistry: RlsReadRegistry;
|
|
3238
3331
|
}
|
|
3239
3332
|
/** Declare a replication shape. See the module docs for runtime semantics. */
|
|
3240
3333
|
declare const defineShape: <Args extends ValidatorMap = ValidatorMap, Context = QueryCtx>(definition: ShapeDefinition<Args, Context>) => RegisteredShape<Args, Context>;
|
|
@@ -3339,4 +3432,4 @@ interface StorageContextIn {
|
|
|
3339
3432
|
}
|
|
3340
3433
|
declare const storageRules: <Context extends StorageContextIn = StorageContextIn>(rules: ReadonlyArray<StorageRule<Context>>, options?: StorageRulesOptions) => Middleware<Context, Context>;
|
|
3341
3434
|
declare const VERSION = "0.0.0";
|
|
3342
|
-
export { DEFAULT_ACTION_CACHE_TTL_MS as ACTION_CACHE_DEFAULT_TTL_MS, ACTION_CACHE_TABLE, type ActionBuilder, type ActionCacheComponent, type ActionCacheContext, type ActionCacheDatabase, type ActionCacheFunctions, type ActionCtx, type AggregateIndexDefinition, type AggregateIndexOptions, type AggregateOp, type ArgsValidator, type Component, type ComponentFunctions, type CreateOptions, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_REDACTED_FIELDS as DOCUMENT_HISTORY_REDACTED_FIELDS, DOCUMENT_HISTORY_TABLE, type DataModelInit, type DeferredDeleteFlushResult, type DefineActionCacheOptions, type DefineComponentOptions, type DefineDocumentHistoryOptions, type DefineIdentityOptions, type DefineListArgsConfig, type DefinePluginOptions, type DefinePolicyInput, type DefinePresenceOptions, type DefineStorageRuleInput, type DocumentHistoryComponent, type DocumentHistoryEntry, type DocumentHistoryFunctions, type DurableObjectJurisdiction, type DurableStreamOptions, type EmptyArgs, type EnvAccessor, type EnvKeyFailure, type EnvShape, type ExposeConfig, type ExtendableSchema, type FacadeEntry, type FacadeWriterLike, type FunctionKind, type HttpActionCtx, type HttpActionHandler, type HttpMethod, type HttpRoute, type HttpRouteBuilder, type HttpRouteFactory, type HttpRouteHandlerOptions, type HttpRunners, type HttpStreamHandlerOptions, type IdentityContract, type IdentityRejectMode, type IdentityValidation, type IndexFieldsByTable, type InferArgs, type InferEnv, type InferIdentity, type InlineAggregateIndexOptions, type InlineRankIndexOptions, type InternalActionBuilder, type InternalMutationBuilder, type InternalQueryBuilder, type LifecycleEvent, type LifecycleHandler, type ListArgsSpec, type ListArgsValidators, type ListArgsValue, type ListFilterOperators, type ListOrderByEntry, type ListWhere, type LunoraBuilders, LunoraEnvError, type LunoraHttpApp, type LunoraHttpEnv, type LunoraRouteHandler, type ManyRelation, type MaskColumns, type MaskContext, type MaskFn, type MaskOptions, type MaskPolicies, type MaskRegistry, type MaskStrategy, type Middleware, type MiddlewareContext, type MiddlewareNext, type MigrationCtx, type MigrationDefinition, type MigrationDocument, type MigrationReader, type MigrationTransform, type MutationBuilder, type MutationCtx, type MutatorDefinition, type OnDeleteAction, type OneRelation, type OrmLike, DEFAULT_TTL_MS as PRESENCE_DEFAULT_TTL_MS, PRESENCE_TABLE, type Permission, type Plugin, type Policy, type PrefixedTables, type PresenceComponent, type PresenceFunctions, type PresenceMember, type ProtectPublicOptions, type QueryBuilder, type QueryCtx, type RankIndexDefinition, type RankIndexOptions, type ReactorHandler, type ReactorOutcome, type ReactorSelect, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMigration, type RegisteredMutation, type RegisteredMutator, type RegisteredQuery, type RegisteredReactor, type RegisteredShape, type RegisteredStream, type RelationBuilder, type RelationDefinition, type RlsOptions, type RlsReadRegistry, type Role, type Schema, type SchemaExtension, type ShapeDefinition, type ShapeReadWhereRequest, type ShardInitEvent, type ShardInitHandler, type StorageOperation, type StorageRule, type StorageRuleContext, type StorageRuleDecision, type StorageRulesOptions, type StorageServeAuthorizer, type StorageServeAuthzContext, 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, actionCacheExtension, allowAll, asBucketStorage, beginDeferredSchedules, bindOrm, bindTableFacade, buildMaskRegistry, buildRlsReadRegistry, cacheKeyFor, clampLimit, composePluginMiddleware, composeShapeReadWhere, createPolicyDsl, createSecrets, defineActionCache, defineAggregateIndex, defineComponent, defineDocumentHistory, defineEnv, defineIdentity, defineListArgs, defineMigration, defineMutator, definePermission, definePlugin, definePolicies, definePolicy, definePresence, defineRankIndex, defineRole, defineSchema, defineSchemaExtension, defineShape, defineStorageRule, defineStorageRules, defineTable, defineVectorIndex, deny, documentHistoryExtension, flushDeferredDeletes, httpAction, httpRoute, httpRouter, indexFieldsFromSchema, initLunora, installPlugins, isDeny, isSafeHeaderValue, mask, mergeSchemaExtension, onConnect, onDisconnect, onQueryChange, onShardInit, presenceExtension, protectPublic, redactSecrets, rls, serveStorageObject, storageRules, toWhereInput, withDeferredDeletes, withDeferredSchedules };
|
|
3435
|
+
export { DEFAULT_ACTION_CACHE_TTL_MS as ACTION_CACHE_DEFAULT_TTL_MS, ACTION_CACHE_TABLE, type ActionBuilder, type ActionCacheComponent, type ActionCacheContext, type ActionCacheDatabase, type ActionCacheFunctions, type ActionCtx, type AggregateIndexDefinition, type AggregateIndexOptions, type AggregateOp, type ArgsValidator, type Component, type ComponentFunctions, type CreateOptions, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_REDACTED_FIELDS as DOCUMENT_HISTORY_REDACTED_FIELDS, DOCUMENT_HISTORY_TABLE, type DataModelInit, type DeferredDeleteFlushResult, type DefineActionCacheOptions, type DefineComponentOptions, type DefineDocumentHistoryOptions, type DefineIdentityOptions, type DefineListArgsConfig, type DefinePluginOptions, type DefinePolicyInput, type DefinePresenceOptions, type DefineStorageRuleInput, type DocumentHistoryComponent, type DocumentHistoryEntry, type DocumentHistoryFunctions, type DurableObjectJurisdiction, type DurableStreamOptions, type EmptyArgs, type EnvAccessor, type EnvKeyFailure, type EnvShape, type ExposeConfig, type ExtendableSchema, type FacadeEntry, type FacadeWriterLike, type FunctionKind, type HttpActionCtx, type HttpActionHandler, type HttpMethod, type HttpRoute, type HttpRouteBuilder, type HttpRouteFactory, type HttpRouteHandlerOptions, type HttpRunners, type HttpStreamHandlerOptions, type IdentityContract, type IdentityRejectMode, type IdentityValidation, type IndexFieldsByTable, type InferArgs, type InferEnv, type InferIdentity, type InlineAggregateIndexOptions, type InlineRankIndexOptions, type InternalActionBuilder, type InternalMutationBuilder, type InternalQueryBuilder, type LifecycleEvent, type LifecycleHandler, type ListArgsSpec, type ListArgsValidators, type ListArgsValue, type ListFilterOperators, type ListOrderByEntry, type ListWhere, type LunoraBuilders, LunoraEnvError, type LunoraHttpApp, type LunoraHttpEnv, type LunoraRouteHandler, type ManyRelation, type MaskColumns, type MaskContext, type MaskFn, type MaskOptions, type MaskPolicies, type MaskRegistry, type MaskStrategy, type Middleware, type MiddlewareContext, type MiddlewareNext, type MigrationCtx, type MigrationDefinition, type MigrationDocument, type MigrationReader, type MigrationTransform, type MutationBuilder, type MutationCtx, type MutatorDefinition, type OnDeleteAction, type OneRelation, type OrmLike, DEFAULT_TTL_MS as PRESENCE_DEFAULT_TTL_MS, PRESENCE_TABLE, type Permission, type Plugin, type Policy, type PrefixedTables, type PresenceComponent, type PresenceFunctions, type PresenceMember, type ProtectPublicOptions, type QueryBuilder, type QueryCtx, type RankIndexDefinition, type RankIndexOptions, type ReactorHandler, type ReactorOutcome, type ReactorSelect, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMigration, type RegisteredMutation, type RegisteredMutator, type RegisteredQuery, type RegisteredReactor, type RegisteredShape, type RegisteredStream, type RelationBuilder, type RelationDefinition, type RlsOptions, type RlsReadRegistry, type Role, type Schema, type SchemaExtension, type ShapeDefinition, type ShapeGuardDeclaration, type ShapeReadWhereRequest, type ShardInitEvent, type ShardInitHandler, type StorageOperation, type StorageRule, type StorageRuleContext, type StorageRuleDecision, type StorageRulesOptions, type StorageServeAuthorizer, type StorageServeAuthzContext, 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, actionCacheExtension, allowAll, asBucketStorage, assertShapesDeclareReadPolicies, beginDeferredSchedules, bindOrm, bindTableFacade, buildMaskRegistry, buildRlsReadRegistry, cacheKeyFor, clampLimit, composePluginMiddleware, composeShapeReadWhere, createPolicyDsl, createSecrets, defineActionCache, defineAggregateIndex, defineComponent, defineDocumentHistory, defineEnv, defineIdentity, defineListArgs, defineMigration, defineMutator, definePermission, definePlugin, definePolicies, definePolicy, definePresence, defineRankIndex, defineRole, defineSchema, defineSchemaExtension, defineShape, defineStorageRule, defineStorageRules, defineTable, defineVectorIndex, deny, documentHistoryExtension, flushDeferredDeletes, httpAction, httpRoute, httpRouter, indexFieldsFromSchema, initLunora, installPlugins, isDeny, isSafeHeaderValue, mask, mergeSchemaExtension, onConnect, onDisconnect, onQueryChange, onShardInit, presenceExtension, protectPublic, redactSecrets, rls, serveStorageObject, storageRules, toWhereInput, withDeferredDeletes, withDeferredSchedules };
|
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
|
|
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-BUng3KOy.mjs";import{initLunora as d}from"./packem_shared/initLunora-BzC2zEfH.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{beginDeferredSchedules as S,withDeferredSchedules as u}from"./packem_shared/beginDeferredSchedules-gJlbW6h9.mjs";import{DOCUMENT_HISTORY_REDACTED_FIELDS as T,DOCUMENT_HISTORY_TABLE as D,defineDocumentHistory as g,documentHistoryExtension as A}from"./packem_shared/DOCUMENT_HISTORY_REDACTED_FIELDS-g-fdcytr.mjs";import{LunoraEnvError as _,defineEnv as L,redactSecrets as C}from"./packem_shared/LunoraEnvError-A5I-PzMh.mjs";import{bindOrm as y,bindTableFacade as P}from"./packem_shared/bindOrm-ChQydkdL.mjs";import{httpAction as M,httpRoute as F,httpRouter as O,isSafeHeaderValue as H}from"./packem_shared/httpAction-DfKw8ZZt.mjs";import{serveStorageObject as U}from"./packem_shared/serveStorageObject-BzZEP-B5.mjs";import{defineIdentity as v}from"./packem_shared/defineIdentity-DwkNKwYa.mjs";import{onConnect as B,onDisconnect as V,onShardInit as j}from"./packem_shared/onConnect-BLRoOpv2.mjs";import{DEFAULT_LIMIT as Y,DEFAULT_MAX_LIMIT as J,clampLimit as K,defineListArgs as Q}from"./packem_shared/DEFAULT_LIMIT-vNtdsa5y.mjs";import{defineMigration as q}from"./packem_shared/defineMigration-CXOS0Bvq.mjs";import{defineMutator as G}from"./packem_shared/defineMutator-DKy8UtbB.mjs";import{c as $,d as ee,a as re,b as oe,e as te,f as ne,g as ie,h as ae,i as fe,j as se,k as de,m as me}from"./packem_shared/plugin-DY0N7ZWx.mjs";import{PRESENCE_DEFAULT_TTL_MS as xe,PRESENCE_TABLE as ce,definePresence as le,presenceExtension as Ee}from"./packem_shared/PRESENCE_DEFAULT_TTL_MS-DheiApL3.mjs";import{protectPublic as ue}from"./packem_shared/protectPublic-C-STrTgA.mjs";import{onQueryChange as Te}from"./packem_shared/onQueryChange-CatdYnH5.mjs";import{defineShape as ge}from"./packem_shared/defineShape-DH52g6sI.mjs";import{anyApi as Re}from"./types.mjs";import{LunoraError as Le}from"@lunora/errors";import{cronJobs as Ie}from"@lunora/scheduler";import{ValidationError as Pe,v as be}from"@lunora/values";import{allowAll as Fe,deny as Oe,isDeny as He,toWhereInput as Ne}from"./packem_shared/allowAll-DkRAgItV.mjs";import{asBucketStorage as ke}from"./packem_shared/asBucketStorage-DoGYOjq3.mjs";import{assertShapesDeclareReadPolicies as we,buildRlsReadRegistry as Be,composeShapeReadWhere as Ve}from"./packem_shared/assertShapesDeclareReadPolicies-CL7JKaIK.mjs";import{buildMaskRegistry as We}from"./packem_shared/buildMaskRegistry-DpWMtalG.mjs";import{createPolicyDsl as Je,definePermission as Ke,definePolicies as Qe,definePolicy as Xe,defineRole as qe}from"./packem_shared/createPolicyDsl-BZa6SqLJ.mjs";import{defineStorageRule as Ge,defineStorageRules as Ze}from"./packem_shared/defineStorageRule-Dv4nJE0H.mjs";import{mask as er}from"./packem_shared/mask-DHt_WzTL.mjs";import{r as or}from"./packem_shared/middleware-CoVBvN2z.mjs";import{storageRules as nr}from"./packem_shared/storageRules-BleDJNal.mjs";const e="0.0.0";export{t as ACTION_CACHE_DEFAULT_TTL_MS,n as ACTION_CACHE_TABLE,Y as DEFAULT_LIMIT,J as DEFAULT_MAX_LIMIT,T as DOCUMENT_HISTORY_REDACTED_FIELDS,D as DOCUMENT_HISTORY_TABLE,_ as LunoraEnvError,Le as LunoraError,xe as PRESENCE_DEFAULT_TTL_MS,ce as PRESENCE_TABLE,e as VERSION,Pe as ValidationError,i as actionCacheExtension,Fe as allowAll,Re as anyApi,ke as asBucketStorage,we as assertShapesDeclareReadPolicies,S as beginDeferredSchedules,y as bindOrm,P as bindTableFacade,We as buildMaskRegistry,Be as buildRlsReadRegistry,a as cacheKeyFor,K as clampLimit,$ as composePluginMiddleware,Ve as composeShapeReadWhere,Je as createPolicyDsl,p as createSecrets,Ie as cronJobs,f as defineActionCache,ee as defineAggregateIndex,re as defineComponent,g as defineDocumentHistory,L as defineEnv,v as defineIdentity,Q as defineListArgs,q as defineMigration,G as defineMutator,Ke as definePermission,oe as definePlugin,Qe as definePolicies,Xe as definePolicy,le as definePresence,te as defineRankIndex,qe as defineRole,ne as defineSchema,ie as defineSchemaExtension,ge as defineShape,Ge as defineStorageRule,Ze as defineStorageRules,ae as defineTable,fe as defineVectorIndex,Oe as deny,A as documentHistoryExtension,c as flushDeferredDeletes,M as httpAction,F as httpRoute,O as httpRouter,se as indexFieldsFromSchema,d as initLunora,de as installPlugins,He as isDeny,H as isSafeHeaderValue,er as mask,me as mergeSchemaExtension,B as onConnect,V as onDisconnect,Te as onQueryChange,j as onShardInit,Ee as presenceExtension,ue as protectPublic,C as redactSecrets,or as rls,U as serveStorageObject,nr as storageRules,Ne as toWhereInput,be as v,l as withDeferredDeletes,u as withDeferredSchedules};
|
|
@@ -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-CH4vwdFT.mjs";import{s as D}from"./stable-key-B_BlboiY.mjs";import{initLunora as F}from"./initLunora-
|
|
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-CH4vwdFT.mjs";import{s as D}from"./stable-key-B_BlboiY.mjs";import{initLunora as F}from"./initLunora-BzC2zEfH.mjs";import{g as k,h as q,a as B}from"./plugin-DY0N7ZWx.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 E,e as h}from"./wire-codec-CH4vwdFT.mjs";import{initLunora as x}from"./initLunora-
|
|
1
|
+
import{v as r}from"@lunora/values";import{d as E,e as h}from"./wire-codec-CH4vwdFT.mjs";import{initLunora as x}from"./initLunora-BzC2zEfH.mjs";import{g as v,h as R,a as U}from"./plugin-DY0N7ZWx.mjs";const H=2160*60*60*1e3,L=64*1024,I=200,q=1e3,C=64,F=512,u=16,B=["_commitSeq","seq"],w=["accessToken","apiKey","backupCodes","clientSecret","hashedPassword","password","privateKey","refreshToken","secret","totpSecret"],f="documentHistory",y="versions",p=`${f}_${y}`,P=v(f,{tables:{[y]: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:g}=x.dataModel().create(),V=(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)):L,b=new Set([...w,...d.redact??[]]);let A=0;const S=()=>(A+=1,A),a=(e,o=0)=>{if(Array.isArray(e))return o>=u?void 0:e.map(t=>a(t,o+1));if(typeof e!="object"||e===null)return e;if(e instanceof Map)return o>=u?void 0:new Map([...e.entries()].filter(([t])=>typeof t!="string"||!b.has(t)).map(([t,i])=>[t,a(i,o+1)]));if(e instanceof Set)return o>=u?void 0:new Set([...e].map(t=>a(t,o+1)));if(Object.getPrototypeOf(e)!==Object.prototype&&Object.getPrototypeOf(e)!==null)return e;if(!(o>=u))return Object.fromEntries(Object.entries(e).filter(([t])=>!b.has(t)).map(([t,i])=>[t,a(i,o+1)]))},M=e=>{if(e===void 0)return;const o=JSON.stringify(h(a(e)));return new TextEncoder().encode(o).length>_?void 0:o},l=async(e,o)=>{const t=M(o.doc),i=M(o.previous),n=o.doc!==void 0&&t===void 0||o.previous!==void 0&&i===void 0;await e.db.insert(p,{documentId:o.documentId,op:o.op,recordedAt:Date.now(),seq:S(),tableName:o.tableName,...n?{truncated:!0}:{},...t===void 0?{}:{doc:t},...i===void 0?{}:{previous:i}})},D=e=>({documentHistoryDelete:e.afterDelete(async(o,t)=>l(o,{documentId:t.id,op:"delete",previous:t.previous,tableName:t.table})),documentHistoryInsert:e.afterInsert(async(o,t)=>l(o,{doc:t.doc,documentId:t.id,op:"insert",tableName:t.table})),documentHistoryUpdate:e.afterUpdate(async(o,t)=>l(o,{doc:t.doc,documentId:t.id,op:"update",previous:t.previous,tableName:t.table}))}),N=g.input({before:r.optional(r.number()),documentId:r.string(),limit:r.optional(r.number())}).query(async({args:e,ctx:o})=>{const t=e.limit!==void 0&&Number.isFinite(e.limit)?Math.min(q,Math.max(1,Math.floor(e.limit))):I,i=await o.db.query(p).withIndex("byDocumentRecordedAt",n=>e.before===void 0?n.eq("documentId",e.documentId):n.eq("documentId",e.documentId).lte("recordedAt",e.before)).order("desc").take(t);return i.sort((n,m)=>{for(const c of B){const s=(m[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:E(JSON.parse(n.doc))},...n.previous===void 0?{}:{previous:E(JSON.parse(n.previous))},...n.truncated===!0?{truncated:!0}:{}}))}),O=Y.input({limit:r.optional(r.number())}).mutation(async({args:e,ctx:o})=>{const t=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 m=0;m<C&&n<i;m+=1){const c=await o.db.query(p).withIndex("byRecordedAt",s=>s.lt("recordedAt",t)).order("asc").take(Math.min(I,i-n));if(c.length===0)return{deleted:n};await Promise.all(c.map(async s=>o.db.delete(s._id))),n+=c.length}return{deleted:n}});return{...U(f,{extension:P,functions:{listForDocument:N,vacuum:O}}),record:D}};export{w as DOCUMENT_HISTORY_REDACTED_FIELDS,p as DOCUMENT_HISTORY_TABLE,V as defineDocumentHistory,P as documentHistoryExtension};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{LunoraError as b}from"@lunora/errors";import{v as t}from"@lunora/values";import{initLunora as M}from"./initLunora-
|
|
1
|
+
import{LunoraError as b}from"@lunora/errors";import{v as t}from"@lunora/values";import{initLunora as M}from"./initLunora-BzC2zEfH.mjs";import{onDisconnect as T}from"./onConnect-BLRoOpv2.mjs";import{g as v,h as A,a as L}from"./plugin-DY0N7ZWx.mjs";const D=3e4,g=1024,P=8,p=4096,w="presence",h="present",l=`${w}_${h}`,N=v(w,{tables:{[h]:A({data:t.optional(t.record(t.string(),t.any())),lastSeen:t.number(),roomId:t.string(),sessionId:t.string(),userId:t.optional(t.string())}).index("byRoomSession",["roomId","sessionId"]).index("byRoom",["roomId"]).index("byRoomLastSeen",["roomId","lastSeen"])}}),{mutation:E,query:B}=M.dataModel().create(),G=(u={})=>{const m=u.ttlMs??D,f=Math.max(0,Math.min(u.disconnectGraceMs??0,m)),S=u.maxSessions,y=S!==void 0&&Number.isFinite(S)?Math.max(1,Math.floor(S)):g,_=E.input({data:t.optional(t.record(t.string(),t.any())),roomId:t.string(),sessionId:t.string()}).mutation(async({args:o,ctx:s})=>{const r=Date.now(),d=s.auth.userId??void 0;if(o.data!==void 0&&new TextEncoder().encode(JSON.stringify(o.data)).length>p)throw new b("BAD_REQUEST",`presence data exceeds the ${String(p)}-byte limit`);const e=await s.db.query(l).withIndex("byRoomSession",i=>i.eq("roomId",o.roomId).eq("sessionId",o.sessionId)).first();if(e&&(e.userId??void 0)!==d)throw new b("FORBIDDEN","presence heartbeat denied: this (roomId, sessionId) is held by another identity");const a={lastSeen:r,roomId:o.roomId,sessionId:o.sessionId,...o.data===void 0?{}:{data:o.data},...d===void 0?{}:{userId:d}};await(e?s.db.patch(e._id,a):s.db.insert(l,a));const I=r-m-Math.max(f,m),c=(await s.db.query(l).withIndex("byRoomLastSeen",i=>i.eq("roomId",o.roomId)).order("asc").take(P)).filter(i=>i.lastSeen<=I);return await Promise.all(c.map(i=>s.db.delete(i._id))),{lastSeen:r}}),x=B.input({roomId:t.string()}).query(async({args:o,ctx:s})=>{const r=Date.now()-m,e=(await s.db.query(l).withIndex("byRoomLastSeen",n=>n.eq("roomId",o.roomId)).order("desc").take(y)).filter(n=>n.lastSeen>r),a=new Set,I=[];for(const n of e){const c=n.userId;if(c!==void 0){if(a.has(c))continue;a.add(c)}const i={lastSeen:n.lastSeen,roomId:n.roomId};c!==void 0&&(i.userId=c),n.data!==void 0&&(i.data=n.data),I.push(i)}return I}),R={...E.input({roomId:t.string()}).mutation(async({args:o,ctx:s})=>{const r=Date.now()-m,d=await s.db.query(l).withIndex("byRoom",e=>e.eq("roomId",o.roomId)).filter(e=>e.lastSeen<=r).collect();return await Promise.all(d.map(e=>s.db.delete(e._id))),{deleted:d.length}}),visibility:"internal"},q=T(async(o,s)=>{const r=s.context?.roomId,d=s.context?.sessionId;if(typeof r!="string"||typeof d!="string")return;const e=await o.db.query(l).withIndex("byRoomSession",n=>n.eq("roomId",r).eq("sessionId",d)).first();if(!e)return;const a=s.userId??void 0;if((e.userId??void 0)!==a)return;if(f===0){await o.db.delete(e._id);return}const I=Math.min(e.lastSeen,Date.now()+f-m);await o.db.patch(e._id,{lastSeen:I})});return L(w,{extension:N,functions:{disconnect:q,heartbeat:_,listPresent:x,sweep:R}})};export{D as PRESENCE_DEFAULT_TTL_MS,l as PRESENCE_TABLE,G as definePresence,N as presenceExtension};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as c}from"@lunora/errors";import{i as p,d as g,f as b,e as y}from"./middleware-CoVBvN2z.mjs";import{r as w}from"./policy-tag-V0lqFzgS.mjs";import{deny as m}from"./allowAll-DkRAgItV.mjs";const h=m(),v=t=>{const e=t?.rls?.tags;return e||w(t)},R=(t,e)=>{const r=p(e.roles),o=new Map,n=new Map;for(const s of e.policies){if(s.on!=="read")continue;const a=o.get(s.table)??new Set;if(a.has(s.when))continue;a.add(s.when),o.set(s.table,a);const l=n.get(s.table)??[];l.push(s),n.set(s.table,l)}for(const[s,a]of n){const l=t.get(s)??[];l.push({policies:a,rolePermissions:r}),t.set(s,l)}},d=t=>{const e=t.OR;return Array.isArray(e)&&e.length===0&&Object.keys(t).length===1},T=(t,e)=>!t||Object.keys(t).length===0?e:d(t)||Object.keys(e).length===0?t:{AND:[t,e]},S=(t,e)=>{const r=g(e.identity);return b(t.policies,{auth:{can:y(r,t.rolePermissions),identity:e.identity,roles:r,userId:e.userId},ctx:e.ctx})},A=(t,e)=>{const r=t.byTable.get(e.table);if(!r||r.length===0)return e.rlsRequired&&!e.tablePublic?h:void 0;const o=[];for(const n of r){const s=S(n,e);if(s===void 0)return;d(s)||o.push(s)}return o.length===0?h:o.length===1?o[0]:{OR:o}},O=t=>{const e=new Map,r=new Set;for(const o of t)for(const n of v(o))r.has(n)||(r.add(n),R(e,n));return{byTable:e}},W=(t,e,r)=>{if(r)return;const o=new Set(e),n=Object.entries(t).filter(([,i])=>o.has(i.table)),s=i=>i.map(([u,f])=>`"${u}" (table "${f.table}")`).join(", "),a=n.filter(([,i])=>i.use===void 0);if(a.length>0)throw new c("INTERNAL",`defineShape ${s(a)} replicate${a.length===1?"s":""} a table this project's rls() read policies restrict, but declare${a.length===1?"s":""} no \`use\` — a shape runs no procedure, so those policies never reach it and every subscriber would replicate every row the shape's own \`where\` admits. Name the guards the equivalent query lists: \`defineShape({ use: [<the rls() guard>], ... })\`. If the shape really is meant to be ungoverned (its own \`where\` is the whole filter), say so with \`use: []\`.`);const l=n.filter(([,i])=>i.use!==void 0&&i.use.length>0&&!i.rlsRegistry.byTable.has(i.table));if(l.length>0)throw new c("INTERNAL",`defineShape ${s(l)} name \`use\` guards that declare no \`on: "read"\` policy for that table, so nothing governs what replicates and the shape's own \`where\` is the whole filter — the same gap a missing \`use\` leaves, on a table this project's rls() read policies restrict. A guard for a different table, or a middleware carrying no rls() policies at all, does not count: name the same rls() guard the equivalent query lists. If the shape really is meant to be ungoverned, say so with \`use: []\`.`)},$=(t,e)=>T(A(t,e),e.shapeWhere);export{W as assertShapesDeclareReadPolicies,O as buildRlsReadRegistry,$ as composeShapeReadWhere};
|
package/dist/packem_shared/{compose-middleware-3i5Qk3sb.mjs → compose-middleware-MCpZZKh7.mjs}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{unionMaskColumns as n,tagMaskMiddleware as d}from"./buildMaskRegistry-DpWMtalG.mjs";import{r as m,t as i}from"./policy-tag-V0lqFzgS.mjs";import{r as c}from"./run-middleware-
|
|
1
|
+
import{unionMaskColumns as n,tagMaskMiddleware as d}from"./buildMaskRegistry-DpWMtalG.mjs";import{r as m,t as i}from"./policy-tag-V0lqFzgS.mjs";import{r as c}from"./run-middleware-laenw2jQ.mjs";const g=s=>{const o=(async({ctx:r,next:e})=>c(s,r,l=>e({ctx:l}))),a=s.flatMap(r=>m(r));a.length>0&&i(o,a);const t=n(s);return t&&d(o,{columns:t}),o};export{g as c};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./compose-middleware-
|
|
1
|
+
import"./compose-middleware-MCpZZKh7.mjs";import{c as i,a as s,b as o,g as m,k as l,m as t}from"./plugin-DY0N7ZWx.mjs";export{i as composePluginMiddleware,s as defineComponent,o as definePlugin,m as defineSchemaExtension,l as installPlugins,t as mergeSchemaExtension};
|
package/dist/packem_shared/{defineAggregateIndex-DFVCeuX1.mjs → defineAggregateIndex-COm6F0QJ.mjs}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{d,e as n,f as i,h as s,i as f,j as x,v as r}from"./plugin-
|
|
1
|
+
import{d,e as n,f as i,h as s,i as f,j as x,v as r}from"./plugin-DY0N7ZWx.mjs";export{d as defineAggregateIndex,n as defineRankIndex,i as defineSchema,s as defineTable,f as defineVectorIndex,x as indexFieldsFromSchema,r as validateIndexFields};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as r}from"@lunora/errors";import{c as p}from"./context-identity-B2XYGA0B.mjs";import{v as u}from"./functions-B2-H4PQT.mjs";import{toWhereInput as m,deny as d}from"./allowAll-DkRAgItV.mjs";import{buildRlsReadRegistry as c}from"./assertShapesDeclareReadPolicies-CL7JKaIK.mjs";const I=e=>{if(e.table.trim()==="")throw new r("INTERNAL","defineShape: `table` must be a non-empty string");if(e.columns?.length===0)throw new r("INTERNAL","defineShape: `columns` must list at least one column when provided");if(!e.where&&e.owner===void 0)throw new r("INTERNAL",`defineShape: shape on table "${e.table}" needs a \`where\` predicate or an \`owner\` (\`owner: true\` uses the table's .ownedBy(field), \`owner: "field"\` names the column) — a shape with neither would replicate the whole table`);return{__lunoraShape:!0,...e,compileWhere:(a,l,n)=>{const w=u(e.args??{},l),o=e.where?m(e.where(a,w)):void 0;if(e.owner===void 0)return o;const t=e.owner===!0?n?.ownerField:e.owner;if(t===void 0||t==="")throw new r("INTERNAL",`defineShape: shape on table "${e.table}" declares \`owner: true\` but the table has no \`.ownedBy(field)\` — add it to the table, or name the column directly with \`owner: "field"\``);const s=p(a);if(s===void 0)return d();const h={[t]:s};return o===void 0?h:{AND:[h,o]}},rlsRegistry:c(e.use??[])}};export{I as defineShape};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{LunoraError as E,toErrorBody as O,isLunoraError as N}from"@lunora/errors";import{parseValidatorMap as R,ValidationError as _}from"@lunora/values";import{Hono as q}from"hono";import{e as x}from"./wire-codec-CH4vwdFT.mjs";import{a as k}from"./apply-output-C5wZ5EAL.mjs";const U=e=>async r=>e(r.get("lunora"),r.req.raw),J=()=>{const e=new q;return e.use("*",async(r,o)=>{const n=r.env.__lunoraCtx;if(!n)throw new E("INTERNAL_SERVER_ERROR","HttpActionCtx was not injected — mount httpRouter() on createWorker(), which supplies it per request.");r.set("lunora",n),await o()}),e},v=e=>e.kind==="optional"?e._meta?.inner??e:e,g=(e,r)=>{switch(e){case"bigint":try{return BigInt(r)}catch{return r}case"boolean":return r==="true"||r==="1"?!0:r==="false"||r==="0"?!1:r;case"number":return r===""?Number.NaN:Number(r);default:return r}},C=(e,r,o)=>{const n=v(e);if(n.kind==="array"){const a=r.req.queries(o);if(a===void 0)return;const d=n._meta?.inner;return a.map(i=>g(d?.kind??"string",i))}const t=r.req.query(o);return t===void 0?void 0:g(n.kind,t)},S=(e,r)=>{const o={};for(const n of Object.keys(e)){const t=e[n];t&&(o[n]=C(t,r,n))}return R(e,o,"searchParams")},T=(e,r)=>{const o=r.req.param(),n={};for(const t of Object.keys(e)){const a=e[t];if(!a)continue;const d=o[t];n[t]=d===void 0?void 0:g(v(a).kind,d)}return R(e,n,"params")},H=async(e,r)=>{let o;try{o=await r.req.json()}catch{throw new E("BAD_REQUEST","Invalid JSON body")}if(typeof o!="object"||o===null||Array.isArray(o))throw new E("BAD_REQUEST","Expected a JSON object body");return R(e,o,"body")},j=e=>{if(e instanceof _)return Response.json({code:"BAD_REQUEST",error:e.message},{status:400});if(N(e)){const{body:r,redacted:o,status:n}=O(e,{fallbackCode:"INTERNAL_SERVER_ERROR",redactedMessage:"Internal error"});return o&&console.error("[lunora] http action error (redacted on the wire):",e),Response.json({code:r.code,error:r.message},{status:n})}throw e},A=(e,r)=>{const{method:o}=r.req;if(!(o===e.method||e.method==="GET"&&o==="HEAD"))return Response.json({code:"METHOD_NOT_ALLOWED",error:`${o} is not allowed on this route (declared as ${e.method})`},{headers:{allow:e.method},status:405})},L=(e,r)=>async o=>{const n=A(e,o);if(n)return n;try{const t=o.get("lunora"),a=Object.keys(e.searchParams).length>0?S(e.searchParams,o):{},d=Object.keys(e.params).length>0?T(e.params,o):{},i=Object.keys(e.body).length>0?await H(e.body,o):{},h=await r({body:i,ctx:t,params:d,searchParams:a}),s=e.output?k(e.output,h):h,c={};e.cacheControl&&(c["cache-control"]=e.cacheControl),e.cacheTag&&(c["cache-tag"]=e.cacheTag),e.vary&&(c.vary=e.vary);const f=Object.keys(c).length>0;return s===void 0?new Response(null,{headers:f?c:void 0,status:204}):Response.json(s,{headers:f?c:void 0})}catch(t){return j(t)}},w={"cache-control":"no-cache, no-transform","content-type":"text/event-stream; charset=utf-8","x-accel-buffering":"no"},b=(e,r)=>{const o=JSON.stringify(r===void 0?x(e):e);return`${r?`event: ${r}
|
|
2
|
+
`:""}data: ${o}
|
|
3
|
+
|
|
4
|
+
`},D=(e,r)=>(async o=>{const n=A(e,o);if(n)return n;let t,a;try{t=Object.keys(e.searchParams).length>0?S(e.searchParams,o):{},a=Object.keys(e.params).length>0?T(e.params,o):{}}catch(m){return j(m)}const d=o.get("lunora"),i=o.req.raw,h=new TextEncoder,s=new AbortController;if(i.signal.aborted)return s.abort(),new Response("",{headers:w});const c=()=>{s.abort()};i.signal.addEventListener("abort",c,{once:!0});const f=new ReadableStream({cancel(){i.signal.removeEventListener("abort",c),s.abort()},async start(m){try{const y=r({ctx:d,params:a,request:i,searchParams:t,signal:s.signal});for await(const p of y){if(s.signal.aborted)break;m.enqueue(h.encode(b(e.output?k(e.output,p):p)))}s.signal.aborted||m.enqueue(h.encode(b({},"complete")))}catch(y){const{body:p,redacted:P}=O(y,{fallbackCode:"INTERNAL_SERVER_ERROR",redactedMessage:"Internal error"});P&&console.error("[lunora] unhandled stream handler error:",y),s.signal.aborted||m.enqueue(h.encode(b({code:p.code,message:p.message},"error")))}finally{i.signal.removeEventListener("abort",c);try{m.close()}catch{}}}});return new Response(f,{headers:w})}),u=e=>({body:r=>u({...e,body:{...e.body,...r}}),cacheControl:r=>u({...e,cacheControl:r}),cacheTag:r=>u({...e,cacheTag:r}),handler:r=>L(e,r),output:r=>u({...e,output:r}),params:r=>u({...e,params:{...e.params,...r}}),searchParams:r=>u({...e,searchParams:{...e.searchParams,...r}}),stream:r=>D(e,r),vary:r=>u({...e,vary:r})}),l=e=>r=>u({body:{},method:e,params:{},path:r,searchParams:{}}),Q={delete:l("DELETE"),get:l("GET"),head:l("HEAD"),options:l("OPTIONS"),patch:l("PATCH"),post:l("POST"),put:l("PUT")},W=e=>!(e.includes("\r")||e.includes(`
|
|
5
|
+
`)||e.includes("\0"));export{U as httpAction,Q as httpRoute,J as httpRouter,W as isSafeHeaderValue};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as j}from"./apply-output-C5wZ5EAL.mjs";import{v as i}from"./functions-B2-H4PQT.mjs";import{unionMaskColumns as w}from"./buildMaskRegistry-DpWMtalG.mjs";import{r as C}from"./policy-tag-V0lqFzgS.mjs";import{r as b}from"./run-middleware-
|
|
1
|
+
import{a as j}from"./apply-output-C5wZ5EAL.mjs";import{v as i}from"./functions-B2-H4PQT.mjs";import{unionMaskColumns as w}from"./buildMaskRegistry-DpWMtalG.mjs";import{r as C}from"./policy-tag-V0lqFzgS.mjs";import{r as b}from"./run-middleware-laenw2jQ.mjs";const h=(e,r,o,n)=>n===0&&r===void 0||typeof e!="object"||e===null?e:Object.assign(Object.create(Object.getPrototypeOf(e)),e,{args:Object.freeze({...o}),...r===void 0?{}:{meta:r}}),y=(e,r)=>b(e,r,o=>o),M=(e,r,o,n,t)=>async(s,c)=>{const d=i(e,c),m=await y(r,h(s,t,d,r.length)),l=await o({args:d,ctx:m});return n?j(n,l):l},O=(e,r,o,n)=>(t,s,c)=>{const d=i(e,s);return(async function*(){const l=await y(r,h(t,n,d,r.length)),f=o({args:d,ctx:l,signal:c})[Symbol.asyncIterator]();try{for(;;){if(c.aborted)return;const p=await f.next();if(p.done||c.aborted)return;yield p.value}}finally{await f.return?.()}})()},g=e=>{const r=e.flatMap(o=>C(o));return r.length>0?{tags:r}:void 0},x=e=>{for(const r of["add","clear","delete","set"])typeof e[r]=="function"&&Object.defineProperty(e,r,{configurable:!1,enumerable:!1,value:()=>{throw new TypeError(`Cannot mutate a frozen .meta() declaration (${e.constructor.name}.${r})`)},writable:!1})},u=(e,r)=>{if(e===null||typeof e!="object"||r.has(e))return e;if(r.add(e),e instanceof Map){for(const[o,n]of e)u(o,r),u(n,r);x(e)}else if(e instanceof Set){for(const o of e)u(o,r);x(e)}for(const o of Object.values(e))u(o,r);return Object.freeze(e),e},z=e=>u(structuredClone(e),new WeakSet),a=(e,r,o)=>({__lunoraProcedure:e,...o?{__lunoraVisibility:o}:{},input:n=>a(e,{...r,args:{...r.args,...n}},o),[e]:n=>{const t=g(r.middlewares),s=w(r.middlewares);return{args:r.args,...r.expose?{expose:r.expose}:{},handler:M(r.args,r.middlewares,n,r.output,r.meta),kind:e,...s?{maskedTables:s}:{},...t?{rls:t}:{},...o?{visibility:o}:{},...r.x402?{x402:r.x402}:{}}},meta:n=>a(e,{...r,meta:z({...r.meta,...n})},o),output:n=>a(e,{...r,output:n},o),...e==="query"?{stream:(n,t)=>{const s=g(r.middlewares),c=w(r.middlewares),d=t?.durable===!0?{}:t?.durable;return{args:r.args,...d?{durable:d}:{},...r.expose?{expose:r.expose}:{},handler:O(r.args,r.middlewares,n,r.meta),kind:"stream",...c?{maskedTables:c}:{},...s?{rls:s}:{},...o?{visibility:o}:{},...r.x402?{x402:r.x402}:{}}}}:{},use:n=>a(e,{...r,middlewares:[...r.middlewares,n]},o),...o?{}:{expose:n=>a(e,{...r,expose:n},o)},...o?{}:{x402:n=>a(e,{...r,x402:n},o)}}),H={dataModel:()=>({create:e=>({action:a("action",{args:{},middlewares:[]}),internalAction:a("action",{args:{},middlewares:[]},"internal"),internalMutation:a("mutation",{args:{},middlewares:[]},"internal"),internalQuery:a("query",{args:{},middlewares:[]},"internal"),mutation:a("mutation",{args:{},middlewares:[]}),query:a("query",{args:{},middlewares:[]})})})};export{H as initLunora};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as S}from"@lunora/errors";import{f as U}from"./fnv1a-D3ueNTWB.mjs";import{i as L,a as z,o as _,b as G,c as C}from"./middleware-CoVBvN2z.mjs";import{bindTableFacade as H,bindOrm as J}from"./bindOrm-ChQydkdL.mjs";import{tagMaskMiddleware as Q}from"./buildMaskRegistry-DpWMtalG.mjs";const V=(e,i,f)=>{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,f)}catch{return null}},u=(e,i,f)=>{const d={...e};for(const[y,g]of Object.entries(i))y in d&&(d[y]=V(g,e[y],{...f,column:y,row:e}));return d},j=(e,i,f)=>({...e,page:e.page.map(d=>u(d,i,f))}),D=(e,i,f,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 "${f}" by masked column "${w}" is not supported`)},X=(e,i,f,d)=>{const y=e.rankBefore,g=e.rankPageRows,w=(r,n)=>{const t=i.get(r);return t?n.map(o=>u(o,t,f)):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 a=c.find(E=>E in s);if(a!==void 0)throw new S("MASK_UNSUPPORTED",`${t}() reading "${r}" via index "${n}" would order rows by masked column "${a}" — 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,f)},collect:async()=>(await r.collect()).map(s=>u(s,n,f)),collectWithScores:async()=>(await r.collectWithScores()).map(s=>{const c=u(s.document,n,f);return"distanceMeters"in s?{distanceMeters:null,document:c}:{document:c,score:s.score}}),filter:o=>p(r.filter(s=>o(u(s,n,f))),n,t),first:async()=>{const o=await r.first();return o?u(o,n,f):null},order:o=>p(r.order(o),n,t),paginate:async o=>j(await r.paginate(o),n,f),take:async o=>(await r.take(o)).map(c=>u(c,n,f)),unique:async()=>{const o=await r.unique();return o?u(o,n,f):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 a=await e.lookupById(r,n);if(a)return{row:a.row,tableName:i.has(a.tableName)?a.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 a=>(await e.findFirst(a,{limit:1,where:{_id:r}}))?._id===r?a:void 0));return{row:t,tableName:c.find(a=>a!==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,a=`${r}.${o}`;R(c.where,A,a,t),F(a,c.orderBy,A,t),$(a,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,f):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,f):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,f):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,f)},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,f):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,f):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(a=>({...a,doc:u(a.doc,c,f)}))}: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 f=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)===!0)return l();const p=X(w.db,f,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 f)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{i as j}from"./wire-codec-CH4vwdFT.mjs";import{bindTableFacade as x,bindOrm as V}from"./bindOrm-ChQydkdL.mjs";import{t as ee}from"./policy-tag-V0lqFzgS.mjs";import{deny as te}from"./allowAll-DkRAgItV.mjs";const re=["every","is","isNot","none","some"],ne=new Set(re),se=e=>{if(e===null||typeof e!="object"||Array.isArray(e))return!1;const r=Object.keys(e);return r.length>0&&r.every(n=>ne.has(n))},N=(e,r,n)=>r?{[e]:n(r)}:{},oe=["contains","eq","gt","gte","in","isNull","lt","lte","ne","notIn"],B=-1,v=0,R=1,k=e=>e?R:B,p=(e,r)=>e<r?e:r,ie=(e,r)=>e>r?e:r,Q=e=>e===v?v:k(e===B),q=e=>{const r=typeof e;return r==="number"||r==="string"||r==="bigint"},S=e=>e==null,D=(e,r,n)=>{if(n===void 0)throw new W("BAD_REQUEST",`\`${r}\` on "${e}" received undefined; a comparison needs a value`)},ae={gt:(e,r)=>e>r,gte:(e,r)=>e>=r,lt:(e,r)=>e<r,lte:(e,r)=>e<=r},ce=(e,r,n)=>{let i=R;for(const c of["gt","gte","lt","lte"]){if(!(c in n))continue;const h=n[c];if(D(e,c,h),!q(h))i=p(i,B);else if(S(r))i=p(i,v);else{const f=ae[c];i=p(i,k(q(r)&&f(r,h)))}}return i},Y=(e,r,n)=>{if(!Array.isArray(n))throw new W("BAD_REQUEST",`\`${r}\` on "${e}" expects an array of values, received ${n===null?"null":typeof n}`);if(n.includes(void 0))throw new W("BAD_REQUEST",`\`${r}\` on "${e}" received undefined inside its list; a comparison needs a value`);return n},K=(e,r)=>r.length===0?B:S(e)?v:r.includes(e)?R:r.some(n=>S(n))?v:B,le=(e,r,n)=>{let i=R;if("in"in n&&(i=p(i,K(r,Y(e,"in",n.in)))),"notIn"in n&&(i=p(i,Q(K(r,Y(e,"notIn",n.notIn))))),"contains"in n){const c=n.contains;D(e,"contains",c),i=S(r)?p(i,v):p(i,k(typeof r=="string"&&typeof c=="string"&&r.includes(c)))}return"isNull"in n&&(i=p(i,k(n.isNull===!0===S(r)))),i},F=(e,r,n)=>r===null?k(n===S(e)):S(e)?v:k(n===(e===r)),fe=(e,r,n)=>{let i=R;return"eq"in n&&(D(e,"eq",n.eq),i=p(i,F(r,n.eq,!0))),"ne"in n&&(D(e,"ne",n.ne),i=p(i,F(r,n.ne,!1))),i},ue=(e,r,n)=>p(fe(e,r,n),p(le(e,r,n),ce(e,r,n))),de=(e,r,n,i)=>{if(r==="NOT")return Q(i(e,n??{}));if(!Array.isArray(n))return B;let c=r==="AND"?R:B;for(const h of n){const f=i(e,h);c=r==="AND"?p(c,f):ie(c,f)}return c},z=e=>e==="AND"||e==="OR"||e==="NOT",he=e=>j(e)&&Object.keys(e).every(r=>oe.includes(r)),C=e=>Object.keys(e).some(r=>{const n=e[r];return z(r)?r==="NOT"?C(n??{}):Array.isArray(n)&&n.some(i=>C(i??{})):se(n)}),H=(e,r)=>{let n=R;for(const i of Object.keys(r)){const c=r[i];if(z(i)){n=p(n,de(e,i,c,H));continue}const h=e[i];if(he(c)){n=p(n,ue(i,h,c));continue}D(i,"eq",c),n=p(n,F(h,c,!0))}return n},L=(e,r)=>H(e,r)===R,G=500,T=(e,r,n)=>{const i=r??G;if(e>i)throw new W("BATCH_LIMIT_EXCEEDED",`${n}: batch of ${String(e)} exceeds the limit of ${String(i)} (raise options.limit or chunk the call)`,{status:400})},ye=te(),we=Symbol.for("lunora.ctxdb.rls-unwrap"),X=Symbol.for("lunora.rls.wrapped-chain"),pe=e=>e[X],ge=e=>{const r=new Map;for(const n of e){const i=r.get(n.table)??[];i.push(n),r.set(n.table,i)}return r},We=(e,r)=>{const n=[];let i=!1;for(const c of e){if(c.on!=="read")continue;const h=c.when(r);if(h!==void 0){if(h===!0){i=!0;break}h!==!1&&n.push(h)}}if(!i)return n.length===0?ye:n.length===1?n[0]:{OR:n}},Oe=(e,r,n)=>{const i=e.when({...r,row:n});if(i===void 0||i===!0)return!0;if(i===!1)return!1;if(C(i))throw new W("RELATION_PREDICATE_UNSUPPORTED","relation predicates are not supported in write policies; use a read policy or a flat column check");return L(n,i)},be=(e,r,n)=>{const i=e.when(r);if(i===void 0||i===!0)return n===void 0||Oe(e,r,n);if(i===!1)return!1;if(C(i))throw new W("RELATION_PREDICATE_UNSUPPORTED","relation predicates are not supported in write policies; use a read policy or a flat column check");return!r.row||!L(r.row,i)?!1:n===void 0||L(n,i)},Re=(e,r,n,i)=>{let c=!1;for(const h of e)if(h.on===r&&(c=!0,!be(h,n,i)))return!1;return c},Ae=new Set(["baseWhere","relationBaseWhere","restrictsCounts","where"]),Ee=(e,r)=>r===void 0?!0:e==="restrictsCounts"?typeof r=="boolean":e==="relationBaseWhere"?typeof r=="function":j(r),Be=e=>{const r=Object.keys(e);return r.length===0?!1:r.every(n=>Ae.has(n)&&Ee(n,e[n]))},ve=e=>e===void 0?{}:j(e)&&Be(e)?e:{where:e},$=e=>e!==void 0&&Object.keys(e).length>0,Se=e=>{if(e.length!==0)return e.length===1?e[0]:{AND:e}},b=(e,r)=>$(r)?!e||Object.keys(e).length===0?r:{AND:[r,e]}:e,_e=e=>{if(typeof e!="object"||e===null||Array.isArray(e))return!1;const r=e;return typeof r.findMany=="function"&&typeof r.withSearchIndex=="function"},Te=(e,r,n)=>{const[i]=n,c=n.length===1&&i!==void 0?i.perTable:new Set(n.flatMap(t=>[...t.perTable.keys()])),h=new Map,f=t=>{const s=h.get(t);if(s)return s;const o=[];let a=!1;for(const y of n){const d=y.perTable.get(t);if(!d||d.length===0||!d.some(m=>m.on==="read"))continue;a=!0;const u=We(d,y.context);u!==void 0&&o.push(u)}const l={baseWhere:Se(o),restricts:a};return h.set(t,l),l},_=(t,s,o,a)=>{for(const l of n){const y=l.perTable.get(t);if(y&&!Re(y,s,{...l.context,row:o},a))return!1}return!0},w=t=>f(t).restricts?r:e,g=t=>f(t).baseWhere,A=async(t,s,o)=>{if(r.lookupById){const u=await r.lookupById(t,s);if(u)return{row:u.row,tableName:c.has(u.tableName)?u.tableName:void 0}}const a=await r.get(t,s);if(!a)return{row:null,tableName:void 0};const l=s??o,y=l===void 0?[...c.keys()]:[l].filter(u=>c.has(u)),d=await Promise.all(y.map(async u=>(await r.findFirst(u,{limit:1,where:{_id:t}}))?._id===t?u:void 0));return{row:a,tableName:d.find(u=>u!==void 0)}},U=async(t,s,o)=>{const a=await A(t,s,o);return a.row&&a.tableName!==void 0?{row:a.row,tableName:a.tableName}:void 0},O=async(t,s,o,a,l,y)=>{const d=await U(t,l,y);if(!d)return o(e);const u=a?a(d.row):void 0;if(!_(d.tableName,s,d.row,u))throw new W("FORBIDDEN",`${s} denied by policy`);return o(r)},E=(t,s)=>{const{baseWhere:o}=f(t);if($(o))throw new W("COUNT_RLS_UNSUPPORTED",`${s}() is not supported on "${t}" inside an RLS-restricted context`)},M=e.rankBefore,P=e.rankPageRows,I={...e,async count(t,s){const{baseWhere:o}=f(t),a=ve(s);return w(t).count(t,{...a,baseWhere:b(a.baseWhere,o),relationBaseWhere:g,restrictsCounts:(a.restrictsCounts??!1)||$(o)})},delete:(t,s,o)=>O(t,"delete",a=>a.delete(t,s,o),void 0,s),async deleteAll(t,s){const o=Math.max(1,s?.chunkSize??G),{baseWhere:a}=f(t);let l=0;for(;;){const d=(await w(t).findMany(t,{baseWhere:a,limit:o,relationBaseWhere:g})).page.map(u=>String(u._id));if(d.length===0)break;for(const u of d)await O(u,"delete",m=>m.delete(u,void 0,s?.hard===void 0?void 0:{hard:s.hard}),void 0,void 0,t),l+=1;if(d.length<o)break}return{deleted:l}},async deleteMany(t,s,o){T(t.length,s?.limit,"deleteMany");for(const a of t)await O(a,"delete",l=>l.delete(a,o),void 0,o);return{deleted:t.length}},async deleteWhere(t,s,o){const{baseWhere:a}=f(t),y=(await w(t).findMany(t,{baseWhere:b(s,a),relationBaseWhere:g})).page.map(d=>String(d._id));T(y.length,o?.limit,"deleteWhere");for(const d of y)await O(d,"delete",u=>u.delete(d),void 0,void 0,t);return{deleted:y.length}},async findFirst(t,s){const{baseWhere:o}=f(t);return w(t).findFirst(t,{...s,baseWhere:b(s?.baseWhere,o),relationBaseWhere:g})},async findFirstOrThrow(t,s){const{baseWhere:o}=f(t);return w(t).findFirstOrThrow(t,{...s,baseWhere:b(s?.baseWhere,o),relationBaseWhere:g})},async findMany(t,s){const{baseWhere:o}=f(t);return w(t).findMany(t,{...s,baseWhere:b(s?.baseWhere,o),relationBaseWhere:g})},async get(t,s){const o=await A(t,s);if(!o.row)return null;if(o.tableName===void 0)return e.get(t,s);const{baseWhere:a,restricts:l}=f(o.tableName);if(!l)return e.get(t,s);if(!a)return o.row;const y=await r.findFirst(o.tableName,{baseWhere:a,limit:1,where:{_id:t}});return y?._id===t?y:null},async lookupById(t,s){const o=await A(t,s);if(!o.row)return null;if(o.tableName===void 0||!f(o.tableName).restricts)return await e.lookupById?.(t,s)??null;const{baseWhere:a}=f(o.tableName);if(!a)return{row:o.row,tableName:o.tableName};const l=await r.findFirst(o.tableName,{baseWhere:a,limit:1,where:{_id:t}});return l?._id===t?{row:l,tableName:o.tableName}:null},async insert(t,s){if(c.has(t)){if(!_(t,"insert",s))throw new W("FORBIDDEN",`insert on "${t}" denied by policy`);return r.insert(t,s)}return e.insert(t,s)},async insertMany(t,s,o){if(T(s.length,o?.limit,"insertMany"),c.has(t)){for(const a of s)if(!_(t,"insert",a))throw new W("FORBIDDEN",`insert on "${t}" denied by policy`);return r.insertMany(t,s,o)}return e.insertMany(t,s,o)},async insertManyUnsafe(t,s,o){if(T(s.length,o?.limit,"insertManyUnsafe"),c.has(t)){for(const a of s)if(!_(t,"insert",a))throw new W("FORBIDDEN",`insert on "${t}" denied by policy`);return r.insertManyUnsafe(t,s,o)}return e.insertManyUnsafe(t,s,o)},patch:(t,s,o)=>O(t,"update",a=>a.patch(t,s,o),a=>({...a,...s}),o),async patchMany(t,s,o){T(t.length,s?.limit,"patchMany");for(const a of t)await O(a.id,"update",l=>l.patch(a.id,a.patch,o),l=>({...l,...a.patch}),o);return{patched:t.length}},async patchWhere(t,s,o){const{baseWhere:a}=f(t),y=(await w(t).findMany(t,{baseWhere:b(s.where,a),relationBaseWhere:g})).page.map(d=>({id:String(d._id),patch:s.patch}));return T(y.length,o?.limit,"patchWhere"),I.patchMany(y,o)},query(t){const{baseWhere:s}=f(t),o=w(t).query(t);return s?o.filter(a=>L(a,s)):o},restore:(t,s)=>O(t,"update",async o=>{const a=o.restore??r.restore;if(!a)throw new W("BAD_REQUEST","restore is not supported by this writer");await a(t,s)},void 0,s),replace:(t,s,o)=>O(t,"update",a=>a.replace(t,s,o),a=>({...s,_creationTime:s._creationTime??a._creationTime,_id:a._id}),o),aggregate(t,s){const{baseWhere:o}=f(t);return w(t).aggregate(t,{...s,baseWhere:b(s.baseWhere,o),relationBaseWhere:g})},groupBy(t,s){const{baseWhere:o}=f(t);return w(t).groupBy(t,{...s,baseWhere:b(s.baseWhere,o),relationBaseWhere:g})},rank(t,s,o){return E(t,"rank"),w(t).rank(t,s,o)},rankPage(t,s,o){return E(t,"rankPage"),w(t).rankPage(t,s,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})},...N("rankBefore",M,t=>(s,o,a)=>(E(s,"rankBefore"),(w(s).rankBefore??t)(s,o,a))),...N("rankPageRows",P,t=>(s,o,a)=>(E(s,"rankPageRows"),(w(s).rankPageRows??t)(s,o,a)))},Z=I;if(c.size>0)for(const[t,s]of Object.entries(e))_e(s)&&(Z[t]=x(I,t));return Object.defineProperty(I,X,{configurable:!0,enumerable:!1,value:{base:e,raw:r,steps:n},writable:!1}),I},J=e=>typeof e=="string"?e:e.name,ke=e=>{const r=new Map;for(const n of e??[])r.set(n.name,new Set((n.permissions??[]).map(i=>J(i))));return r},Ie=(e,r)=>{const n=new Set;for(const i of e)for(const c of r.get(i)??[])n.add(c);return i=>n.has(J(i))},De=["roles","role"],Me=e=>{for(const r of De){const n=e?.[r];if(typeof n=="string"){const i=n.split(",").map(c=>c.trim()).filter(c=>c.length>0);if(i.length>0)return i}if(Array.isArray(n)){const i=n.filter(c=>typeof c=="string"&&c.length>0);if(i.length>0)return i}}return[]},Pe=async(e,r)=>{const n=await e.getIdentity?.()??null,i=[...new Set([...Me(n),...e.roles??[]])];return{can:Ie(i,r),identity:n,roles:i,userId:e.userId??null}},$e=(e,r={})=>{const n=e,i=ge(n),c=ke(r.roles);return ee(async({ctx:f,next:_})=>{const w={auth:await Pe(f.auth??{},c),ctx:f},g=f.db,A=pe(g),U=A?.base??g,O=A?.raw??g[we]??g,E=Te(U,O,[...A?.steps??[],{context:w,perTable:i}]),M={db:E},{orm:P}=f;return P!==null&&typeof P=="object"&&(M.orm=V(E)),_({ctx:M})},[{policies:n,roles:r.roles??[]}])};export{Pe as a,_e as b,se as c,Me as d,Ie as e,We as f,Re as g,ke as i,L as m,N as o,J as p,$e as r};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{LunoraError as s}from"@lunora/errors";import{c as K}from"./compose-middleware-3i5Qk3sb.mjs";import{v as R}from"@lunora/values";const j=["de","en","es","fr","it","nl","none","pt"],_=["native","portable"],z=e=>_.includes(e),H=e=>j.includes(e),q=e=>{if(e.kind!=="optional")return e.kind;const t=e._meta?.inner;return t?q(t):e.kind},B=16,Q={many:(e,t)=>({field:t.field,kind:"many",references:t.references??"_id",table:e}),one:(e,t)=>({field:t.field,kind:"one",onDelete:t.onDelete,references:t.references??"_id",table:e})},g=(e,t,n)=>({handler:n,op:t,timing:e}),X=()=>({afterDelete:e=>g("after","delete",e),afterInsert:e=>g("after","insert",e),afterUpdate:e=>g("after","update",e),beforeDelete:e=>g("before","delete",e),beforeInsert:e=>g("before","insert",e),beforeUpdate:e=>g("before","update",e)}),F=(e,t)=>{if(!t||t.length===0)throw new s("INTERNAL",`rankIndex "${e}": "sortBy" is required and must list at least one key`);return t.map(n=>({direction:n.direction??"asc",field:n.field}))},le=e=>{const t={...e},n=[],r=[],o=[],d=[],u={},l=[],f={},m=X(),b=[];let y={kind:"root"},$=!1,N=!1,k=!1,T=!1,L,M,A,O;const c={aggregateIndex(i,a){const h=a?.op??"count";if(h!=="count"&&!a?.field)throw new s("INTERNAL",`aggregateIndex "${i}": op "${h}" requires a "field"`);return n.push({by:a?.by,field:a?.field,name:i,on:"",op:h,where:a?.where}),c},get aggregateIndexes(){return n},get externalSource(){return O},commitOrdered(){return $=!0,c},get commitOrderedMode(){return $},externallyManaged(){return N=!0,c},geoIndex(i,a){return r.push({field:a.field,name:i,precision:a.precision}),c},get geoIndexes(){return r},global(i){return y={backend:i?.backend??"d1",kind:"global"},c},get isExternallyManaged(){return N},get isPublic(){return T},index(i,a,h){return o.push({fields:a,name:i,unique:h?.unique??!1}),c},get indexes(){return o},memory(){return k=!0,c},get memoryMode(){return k},ownedBy(i){return L=i,c},get ownerField(){return L},public(){return T=!0,c},rankIndex(i,a){return d.push({name:i,on:"",partitionBy:a.partitionBy,sortBy:F(i,a.sortBy),where:a.where}),c},get rankIndexes(){return d},get relationMap(){return u},relations(i){return Object.assign(u,i(Q)),c},searchIndex(i,a){if(a.filterFields&&a.filterFields.length>B)throw new s("INTERNAL",`searchIndex "${i}": at most ${String(B)} filterFields are supported (got ${String(a.filterFields.length)})`);const{language:h}=a;if(h!==void 0&&!H(h))throw new s("INTERNAL",`searchIndex "${i}": unknown language "${h}" (supported: ${j.join(", ")})`);const{strategy:v}=a;if(v!==void 0&&!z(v))throw new s("INTERNAL",`searchIndex "${i}": unknown strategy "${v}" (supported: ${_.join(", ")})`);return l.push({field:a.field,filterFields:a.filterFields,language:a.language,name:i,staged:a.staged,strategy:a.strategy}),c},get searchIndexes(){return l},shape:t,shardBy(i){return y={field:i,kind:"shardBy"},c},get shardMode(){return y},get softDeleteMode(){return M},source(i){if(!i.binding)throw new s("INTERNAL","source: `binding` is required (the wrangler Hyperdrive binding name)");if(!i.query)throw new s("INTERNAL","source: `query` is required (the tenant-membership SQL)");return O=i,N=!0,c},softDelete(i){const a=i?.field??"deletedAt";return M={field:a},a in t||(t[a]=R.optional(R.number().nullable())),c},get triggerMap(){return f},triggers(i){return Object.assign(f,i(m)),c},ttl(i,a){return A={after:a?.after,field:i},c},get ttlPolicy(){return A},get vectorIndexes(){return b},vectorize(i,a){return b.push({dimensions:a.dimensions,embed:a.embed,field:i,metadata:a.metadata,metric:a.metric,name:a.index}),c}};return c},ce=e=>({dimensions:e.dimensions,embed:e.embed,kind:"vectorIndex",metadata:e.metadata,metric:e.metric,select:e.source.select,table:e.source.table}),ue=(e,t)=>{const n=t.op??"count";if(n!=="count"&&!t.field)throw new s("INTERNAL",`aggregateIndex "${e}": op "${n}" requires a "field"`);return{by:t.by,field:t.field,name:e,on:t.on,op:n,where:t.where}},fe=(e,t)=>({name:e,on:t.table,partitionBy:t.partitionBy,sortBy:F(e,t.sortBy),where:t.where}),x=e=>({...e,extend(t){return x(C(e,t))},jurisdiction(t){return x(e)},rls(t){return x({...e,rlsMode:t})}}),U=e=>{for(const[t,n]of Object.entries(e))for(const r of[...n.aggregateIndexes,...n.rankIndexes])r.on===""&&(r.on=t)},V=(e,t,n)=>{for(const r of Object.values(t)){const o=e[r.on];if(!o)throw new s("INTERNAL",`defineAggregateIndex "${r.name}": unknown table "${r.on}"`);o.aggregateIndexes.push(r)}for(const r of Object.values(n)){const o=e[r.on];if(!o)throw new s("INTERNAL",`defineRankIndex "${r.name}": unknown table "${r.on}"`);o.rankIndexes.push(r)}},Y=e=>{if(e.cursor)return"cursor";if(e.reconcileEveryMs!==void 0)return"reconcileEveryMs";if(e.softDeleteColumn!==void 0)return"softDeleteColumn"},G=(e,t)=>{const n=t.mode==="incremental";if(t.mode!==void 0&&t.mode!=="full-pull"&&!n)throw new s("INTERNAL",`defineSchema: table "${e}" uses \`mode: ${JSON.stringify(t.mode)}\` — supported modes are "full-pull" (default) and "incremental".`);if(!n){const r=Y(t);if(r)throw new s("INTERNAL",`defineSchema: table "${e}" sets \`${r}\` but is not \`mode: "incremental"\` — that knob only applies to incremental ingest. Set \`mode: "incremental"\` or remove \`${r}\`.`);return}if(!t.cursor?.column||!t.cursor.query)throw new s("INTERNAL",`defineSchema: table "${e}" is \`mode: "incremental"\` but has no \`cursor\` — add \`cursor: { column: "updated_at", query: "… WHERE … > $N" }\` binding the watermark as the trailing parameter.`);if(t.reconcileEveryMs===void 0&&t.softDeleteColumn===void 0)throw new s("INTERNAL",`defineSchema: table "${e}" is \`mode: "incremental"\` with no delete-visibility path — an incremental pull never sees upstream deletes, so it would accumulate phantom rows. Add \`reconcileEveryMs\` (a periodic full-pull sweep) or \`softDeleteColumn\` (an upstream tombstone column).`)},J=e=>{for(const[t,n]of Object.entries(e)){const r=n.externalSource;if(r){if(n.shardMode.kind==="global")throw new s("INTERNAL",`defineSchema: table "${t}" cannot be both .source() and .global() — a sourced table materializes into a shard DO's SQLite, a global table lives in the external tier`);if(n.shardMode.kind==="shardBy"&&!r.tenantBy)throw new s("INTERNAL",`defineSchema: sourced + .shardBy() table "${t}" needs a \`tenantBy\` mapper — without it every tenant's DO would run the same unscoped query and replicate the whole multitenant table (a cross-tenant leak). Add \`tenantBy: (shardKey) => [shardKey]\` binding the shard key into the query's parameters.`);G(t,r)}}},W=["_commitSeq","_creationTime","_id"],D="_commitSeq",Z=new Set(W),w=(e,t,n,r)=>{if(!(Z.has(n)||n in r.shape))throw new s("INTERNAL",`defineSchema: table "${e}" index "${t}" names column "${n}" which is not in the table's shape`)},p=(e,t,n,r)=>{const o=new Set;for(const d of n){if(o.has(d.name))throw new s("INTERNAL",`defineSchema: table "${e}" declares ${t} "${d.name}" more than once`);o.add(d.name),r(d)}},P=e=>{for(const[t,n]of Object.entries(e))p(t,"index",n.indexes,r=>{for(const o of r.fields)w(t,r.name,o,n)}),p(t,"rank index",n.rankIndexes,r=>{for(const o of r.sortBy)w(t,r.name,o.field,n);for(const o of r.partitionBy??[])w(t,r.name,o,n)}),p(t,"geo index",n.geoIndexes,r=>{w(t,r.name,r.field,n)})},E=(e,t,n)=>{if(e.length===0)return;const r={};for(const o of e)r[t(o)]=n(o);return r},ee=e=>{const t=new Set(e.sortBy.map(n=>n.field));for(const n of e.partitionBy??[])t.add(n);return[...t]},me=e=>{const t={};for(const[n,r]of Object.entries(e.tables)){const o=E(r.geoIndexes,l=>l.name,l=>[l.field]),d=E(r.indexes,l=>l.name,l=>l.fields),u=E(r.rankIndexes,l=>l.name,ee);(o||d||u)&&(t[n]={...o?{geo:o}:{},...d?{index:d}:{},...u?{rank:u}:{}})}return t},te=e=>{for(const[t,n]of Object.entries(e)){if(!n.memoryMode)continue;const o=[[n.shardMode.kind==="global",".global() — those rows live in D1, which this shard does not own and cannot clear"],[n.commitOrderedMode===!0,".commitOrdered() — the counter resets with the table, so the sequence would restart and a consumer's cursor would silently skip everything after it"],[n.externalSource!==void 0,".source() — the table is materialized by the ingest loop, so wiping it on every eviction would fight the puller"],[n.searchIndexes.length>0,".searchIndex() — the FTS shadow is a separate table that clearing the rows would leave stale"],[n.geoIndexes.length>0,".geoIndex() — the geohash companion is a separate table that clearing the rows would leave stale"],[n.aggregateIndexes.length>0,".aggregateIndex() — the counter companion is a separate table that clearing the rows would leave stale"],[n.rankIndexes.length>0,".rankIndex() — the rank companion is a separate table that clearing the rows would leave stale"],[n.vectorIndexes.length>0,".vectorize() — vectors live in an external index that a shard restart cannot clear"]].find(([d])=>d)?.[1];if(o!==void 0)throw new s("INTERNAL",`defineSchema: table "${t}" is both .memory() and ${o}. Plain .index() is supported; SQLite maintains it through the DELETE.`)}},ne=e=>{for(const[t,n]of Object.entries(e))if(n.shardMode.kind==="global"){for(const[r,o]of Object.entries(n.shape))if(q(o)==="bigint")throw new s("INTERNAL",`defineSchema: table "${t}" is .global() and column "${r}" is v.bigint(). A global (D1/Hyperdrive) table stores a bigint as decimal TEXT, which SQL compares lexicographically — "100" sorts before "25" — so \`orderBy\`, range filters and \`aggregate\` on this column would silently return wrong results. Use v.number() if the value fits a double, or v.string() if you only need equality.`)}},re=e=>{for(const[t,n]of Object.entries(e)){if(n.commitOrderedMode){if(n.shardMode.kind==="global")throw new s("INTERNAL",`defineSchema: table "${t}" is both .global() and .commitOrdered(). \`_commitSeq\` is allocated inside the shard's write transaction, which a global (D1/Hyperdrive) table does not share — so the stamp would carry no commit-ordering guarantee. Drop one of the two.`);continue}for(const r of n.indexes)if(r.fields.includes(D))throw new s("INTERNAL",`defineSchema: table "${t}" index "${r.name}" names "${D}", but the table is not .commitOrdered() — no row carries that field, so the index would never match. Add .commitOrdered(), or drop the column from the index.`)}},he=(e,t={},n={},r={})=>(U(e),V(e,n,r),J(e),re(e),ne(e),te(e),P(e),x({tables:e,vectorIndexes:t})),I=(e,t)=>`${e}_${t}`,S=(e,t,n)=>n.has(e)?I(t,e):e,ae=(e,t,n,r)=>{const o=I(t,r),d=m=>m===""?o:S(m,t,n),u={};for(const[m,b]of Object.entries(e.relationMap))u[m]={...b,table:S(b.table,t,n)};const l=e.aggregateIndexes.map(m=>({...m,on:d(m.on)})),f=e.rankIndexes.map(m=>({...m,on:d(m.on)}));return{...e,aggregateIndexes:l,rankIndexes:f,relationMap:u}},ge=(e,t)=>{if(!e)throw new s("INTERNAL","defineSchemaExtension: `key` is required and must be a non-empty string");return{key:e,tables:t.tables,...t.vectorIndexes?{vectorIndexes:t.vectorIndexes}:{}}},oe=((e,t)=>{if(!e)throw new s("INTERNAL","definePlugin: `key` is required and must be a non-empty string");if(t.extension&&t.extension.key!==e)throw new s("INTERNAL",`definePlugin("${e}"): extension key "${t.extension.key}" does not match plugin key`);return{key:e,...t.extension?{extension:t.extension}:{},...t.middleware?{middleware:t.middleware}:{}}}),be=(e,t)=>({...oe(e,{...t.extension?{extension:t.extension}:{},...t.middleware?{middleware:t.middleware}:{}}),functions:t.functions??{}}),C=(e,t)=>{const{key:n}=t,r=new Set(Object.keys(t.tables)),o={...e.tables};for(const[u,l]of Object.entries(t.tables)){const f=I(n,u);if(Object.hasOwn(o,f))throw new s("INTERNAL",`defineSchema(...).extend("${n}"): table "${f}" already exists in the base schema — another extension with the same key already contributed it`);o[f]=ae(l,n,r,u)}const d={...e.vectorIndexes};if(t.vectorIndexes)for(const[u,l]of Object.entries(t.vectorIndexes)){const f=I(n,u);if(Object.hasOwn(d,f))throw new s("INTERNAL",`defineSchema(...).extend("${n}"): vector index "${f}" already exists in the base schema — another extension with the same key already contributed it`);d[f]={...l,table:S(l.table,n,r)}}return P(o),{rlsMode:e.rlsMode,tables:o,vectorIndexes:d}},we=(e,t)=>{let n=e;for(const r of t)r.extension&&(n=C(n,r.extension));return n},xe=e=>{const t=e.map(n=>n.middleware).filter(n=>n!==void 0);return K(t)};export{be as a,oe as b,xe as c,ue as d,fe as e,he as f,ge as g,le as h,ce as i,me as j,we as k,C as m,P as v};
|
|
1
|
+
import{LunoraError as s}from"@lunora/errors";import{c as K}from"./compose-middleware-MCpZZKh7.mjs";import{v as R}from"@lunora/values";const j=["de","en","es","fr","it","nl","none","pt"],_=["native","portable"],z=e=>_.includes(e),H=e=>j.includes(e),q=e=>{if(e.kind!=="optional")return e.kind;const t=e._meta?.inner;return t?q(t):e.kind},B=16,Q={many:(e,t)=>({field:t.field,kind:"many",references:t.references??"_id",table:e}),one:(e,t)=>({field:t.field,kind:"one",onDelete:t.onDelete,references:t.references??"_id",table:e})},g=(e,t,n)=>({handler:n,op:t,timing:e}),X=()=>({afterDelete:e=>g("after","delete",e),afterInsert:e=>g("after","insert",e),afterUpdate:e=>g("after","update",e),beforeDelete:e=>g("before","delete",e),beforeInsert:e=>g("before","insert",e),beforeUpdate:e=>g("before","update",e)}),F=(e,t)=>{if(!t||t.length===0)throw new s("INTERNAL",`rankIndex "${e}": "sortBy" is required and must list at least one key`);return t.map(n=>({direction:n.direction??"asc",field:n.field}))},le=e=>{const t={...e},n=[],r=[],o=[],d=[],u={},l=[],f={},m=X(),b=[];let y={kind:"root"},$=!1,N=!1,k=!1,T=!1,L,M,A,O;const c={aggregateIndex(i,a){const h=a?.op??"count";if(h!=="count"&&!a?.field)throw new s("INTERNAL",`aggregateIndex "${i}": op "${h}" requires a "field"`);return n.push({by:a?.by,field:a?.field,name:i,on:"",op:h,where:a?.where}),c},get aggregateIndexes(){return n},get externalSource(){return O},commitOrdered(){return $=!0,c},get commitOrderedMode(){return $},externallyManaged(){return N=!0,c},geoIndex(i,a){return r.push({field:a.field,name:i,precision:a.precision}),c},get geoIndexes(){return r},global(i){return y={backend:i?.backend??"d1",kind:"global"},c},get isExternallyManaged(){return N},get isPublic(){return T},index(i,a,h){return o.push({fields:a,name:i,unique:h?.unique??!1}),c},get indexes(){return o},memory(){return k=!0,c},get memoryMode(){return k},ownedBy(i){return L=i,c},get ownerField(){return L},public(){return T=!0,c},rankIndex(i,a){return d.push({name:i,on:"",partitionBy:a.partitionBy,sortBy:F(i,a.sortBy),where:a.where}),c},get rankIndexes(){return d},get relationMap(){return u},relations(i){return Object.assign(u,i(Q)),c},searchIndex(i,a){if(a.filterFields&&a.filterFields.length>B)throw new s("INTERNAL",`searchIndex "${i}": at most ${String(B)} filterFields are supported (got ${String(a.filterFields.length)})`);const{language:h}=a;if(h!==void 0&&!H(h))throw new s("INTERNAL",`searchIndex "${i}": unknown language "${h}" (supported: ${j.join(", ")})`);const{strategy:v}=a;if(v!==void 0&&!z(v))throw new s("INTERNAL",`searchIndex "${i}": unknown strategy "${v}" (supported: ${_.join(", ")})`);return l.push({field:a.field,filterFields:a.filterFields,language:a.language,name:i,staged:a.staged,strategy:a.strategy}),c},get searchIndexes(){return l},shape:t,shardBy(i){return y={field:i,kind:"shardBy"},c},get shardMode(){return y},get softDeleteMode(){return M},source(i){if(!i.binding)throw new s("INTERNAL","source: `binding` is required (the wrangler Hyperdrive binding name)");if(!i.query)throw new s("INTERNAL","source: `query` is required (the tenant-membership SQL)");return O=i,N=!0,c},softDelete(i){const a=i?.field??"deletedAt";return M={field:a},a in t||(t[a]=R.optional(R.number().nullable())),c},get triggerMap(){return f},triggers(i){return Object.assign(f,i(m)),c},ttl(i,a){return A={after:a?.after,field:i},c},get ttlPolicy(){return A},get vectorIndexes(){return b},vectorize(i,a){return b.push({dimensions:a.dimensions,embed:a.embed,field:i,metadata:a.metadata,metric:a.metric,name:a.index}),c}};return c},ce=e=>({dimensions:e.dimensions,embed:e.embed,kind:"vectorIndex",metadata:e.metadata,metric:e.metric,select:e.source.select,table:e.source.table}),ue=(e,t)=>{const n=t.op??"count";if(n!=="count"&&!t.field)throw new s("INTERNAL",`aggregateIndex "${e}": op "${n}" requires a "field"`);return{by:t.by,field:t.field,name:e,on:t.on,op:n,where:t.where}},fe=(e,t)=>({name:e,on:t.table,partitionBy:t.partitionBy,sortBy:F(e,t.sortBy),where:t.where}),x=e=>({...e,extend(t){return x(C(e,t))},jurisdiction(t){return x(e)},rls(t){return x({...e,rlsMode:t})}}),U=e=>{for(const[t,n]of Object.entries(e))for(const r of[...n.aggregateIndexes,...n.rankIndexes])r.on===""&&(r.on=t)},V=(e,t,n)=>{for(const r of Object.values(t)){const o=e[r.on];if(!o)throw new s("INTERNAL",`defineAggregateIndex "${r.name}": unknown table "${r.on}"`);o.aggregateIndexes.push(r)}for(const r of Object.values(n)){const o=e[r.on];if(!o)throw new s("INTERNAL",`defineRankIndex "${r.name}": unknown table "${r.on}"`);o.rankIndexes.push(r)}},Y=e=>{if(e.cursor)return"cursor";if(e.reconcileEveryMs!==void 0)return"reconcileEveryMs";if(e.softDeleteColumn!==void 0)return"softDeleteColumn"},G=(e,t)=>{const n=t.mode==="incremental";if(t.mode!==void 0&&t.mode!=="full-pull"&&!n)throw new s("INTERNAL",`defineSchema: table "${e}" uses \`mode: ${JSON.stringify(t.mode)}\` — supported modes are "full-pull" (default) and "incremental".`);if(!n){const r=Y(t);if(r)throw new s("INTERNAL",`defineSchema: table "${e}" sets \`${r}\` but is not \`mode: "incremental"\` — that knob only applies to incremental ingest. Set \`mode: "incremental"\` or remove \`${r}\`.`);return}if(!t.cursor?.column||!t.cursor.query)throw new s("INTERNAL",`defineSchema: table "${e}" is \`mode: "incremental"\` but has no \`cursor\` — add \`cursor: { column: "updated_at", query: "… WHERE … > $N" }\` binding the watermark as the trailing parameter.`);if(t.reconcileEveryMs===void 0&&t.softDeleteColumn===void 0)throw new s("INTERNAL",`defineSchema: table "${e}" is \`mode: "incremental"\` with no delete-visibility path — an incremental pull never sees upstream deletes, so it would accumulate phantom rows. Add \`reconcileEveryMs\` (a periodic full-pull sweep) or \`softDeleteColumn\` (an upstream tombstone column).`)},J=e=>{for(const[t,n]of Object.entries(e)){const r=n.externalSource;if(r){if(n.shardMode.kind==="global")throw new s("INTERNAL",`defineSchema: table "${t}" cannot be both .source() and .global() — a sourced table materializes into a shard DO's SQLite, a global table lives in the external tier`);if(n.shardMode.kind==="shardBy"&&!r.tenantBy)throw new s("INTERNAL",`defineSchema: sourced + .shardBy() table "${t}" needs a \`tenantBy\` mapper — without it every tenant's DO would run the same unscoped query and replicate the whole multitenant table (a cross-tenant leak). Add \`tenantBy: (shardKey) => [shardKey]\` binding the shard key into the query's parameters.`);G(t,r)}}},W=["_commitSeq","_creationTime","_id"],D="_commitSeq",Z=new Set(W),w=(e,t,n,r)=>{if(!(Z.has(n)||n in r.shape))throw new s("INTERNAL",`defineSchema: table "${e}" index "${t}" names column "${n}" which is not in the table's shape`)},p=(e,t,n,r)=>{const o=new Set;for(const d of n){if(o.has(d.name))throw new s("INTERNAL",`defineSchema: table "${e}" declares ${t} "${d.name}" more than once`);o.add(d.name),r(d)}},P=e=>{for(const[t,n]of Object.entries(e))p(t,"index",n.indexes,r=>{for(const o of r.fields)w(t,r.name,o,n)}),p(t,"rank index",n.rankIndexes,r=>{for(const o of r.sortBy)w(t,r.name,o.field,n);for(const o of r.partitionBy??[])w(t,r.name,o,n)}),p(t,"geo index",n.geoIndexes,r=>{w(t,r.name,r.field,n)})},E=(e,t,n)=>{if(e.length===0)return;const r={};for(const o of e)r[t(o)]=n(o);return r},ee=e=>{const t=new Set(e.sortBy.map(n=>n.field));for(const n of e.partitionBy??[])t.add(n);return[...t]},me=e=>{const t={};for(const[n,r]of Object.entries(e.tables)){const o=E(r.geoIndexes,l=>l.name,l=>[l.field]),d=E(r.indexes,l=>l.name,l=>l.fields),u=E(r.rankIndexes,l=>l.name,ee);(o||d||u)&&(t[n]={...o?{geo:o}:{},...d?{index:d}:{},...u?{rank:u}:{}})}return t},te=e=>{for(const[t,n]of Object.entries(e)){if(!n.memoryMode)continue;const o=[[n.shardMode.kind==="global",".global() — those rows live in D1, which this shard does not own and cannot clear"],[n.commitOrderedMode===!0,".commitOrdered() — the counter resets with the table, so the sequence would restart and a consumer's cursor would silently skip everything after it"],[n.externalSource!==void 0,".source() — the table is materialized by the ingest loop, so wiping it on every eviction would fight the puller"],[n.searchIndexes.length>0,".searchIndex() — the FTS shadow is a separate table that clearing the rows would leave stale"],[n.geoIndexes.length>0,".geoIndex() — the geohash companion is a separate table that clearing the rows would leave stale"],[n.aggregateIndexes.length>0,".aggregateIndex() — the counter companion is a separate table that clearing the rows would leave stale"],[n.rankIndexes.length>0,".rankIndex() — the rank companion is a separate table that clearing the rows would leave stale"],[n.vectorIndexes.length>0,".vectorize() — vectors live in an external index that a shard restart cannot clear"]].find(([d])=>d)?.[1];if(o!==void 0)throw new s("INTERNAL",`defineSchema: table "${t}" is both .memory() and ${o}. Plain .index() is supported; SQLite maintains it through the DELETE.`)}},ne=e=>{for(const[t,n]of Object.entries(e))if(n.shardMode.kind==="global"){for(const[r,o]of Object.entries(n.shape))if(q(o)==="bigint")throw new s("INTERNAL",`defineSchema: table "${t}" is .global() and column "${r}" is v.bigint(). A global (D1/Hyperdrive) table stores a bigint as decimal TEXT, which SQL compares lexicographically — "100" sorts before "25" — so \`orderBy\`, range filters and \`aggregate\` on this column would silently return wrong results. Use v.number() if the value fits a double, or v.string() if you only need equality.`)}},re=e=>{for(const[t,n]of Object.entries(e)){if(n.commitOrderedMode){if(n.shardMode.kind==="global")throw new s("INTERNAL",`defineSchema: table "${t}" is both .global() and .commitOrdered(). \`_commitSeq\` is allocated inside the shard's write transaction, which a global (D1/Hyperdrive) table does not share — so the stamp would carry no commit-ordering guarantee. Drop one of the two.`);continue}for(const r of n.indexes)if(r.fields.includes(D))throw new s("INTERNAL",`defineSchema: table "${t}" index "${r.name}" names "${D}", but the table is not .commitOrdered() — no row carries that field, so the index would never match. Add .commitOrdered(), or drop the column from the index.`)}},he=(e,t={},n={},r={})=>(U(e),V(e,n,r),J(e),re(e),ne(e),te(e),P(e),x({tables:e,vectorIndexes:t})),I=(e,t)=>`${e}_${t}`,S=(e,t,n)=>n.has(e)?I(t,e):e,ae=(e,t,n,r)=>{const o=I(t,r),d=m=>m===""?o:S(m,t,n),u={};for(const[m,b]of Object.entries(e.relationMap))u[m]={...b,table:S(b.table,t,n)};const l=e.aggregateIndexes.map(m=>({...m,on:d(m.on)})),f=e.rankIndexes.map(m=>({...m,on:d(m.on)}));return{...e,aggregateIndexes:l,rankIndexes:f,relationMap:u}},ge=(e,t)=>{if(!e)throw new s("INTERNAL","defineSchemaExtension: `key` is required and must be a non-empty string");return{key:e,tables:t.tables,...t.vectorIndexes?{vectorIndexes:t.vectorIndexes}:{}}},oe=((e,t)=>{if(!e)throw new s("INTERNAL","definePlugin: `key` is required and must be a non-empty string");if(t.extension&&t.extension.key!==e)throw new s("INTERNAL",`definePlugin("${e}"): extension key "${t.extension.key}" does not match plugin key`);return{key:e,...t.extension?{extension:t.extension}:{},...t.middleware?{middleware:t.middleware}:{}}}),be=(e,t)=>({...oe(e,{...t.extension?{extension:t.extension}:{},...t.middleware?{middleware:t.middleware}:{}}),functions:t.functions??{}}),C=(e,t)=>{const{key:n}=t,r=new Set(Object.keys(t.tables)),o={...e.tables};for(const[u,l]of Object.entries(t.tables)){const f=I(n,u);if(Object.hasOwn(o,f))throw new s("INTERNAL",`defineSchema(...).extend("${n}"): table "${f}" already exists in the base schema — another extension with the same key already contributed it`);o[f]=ae(l,n,r,u)}const d={...e.vectorIndexes};if(t.vectorIndexes)for(const[u,l]of Object.entries(t.vectorIndexes)){const f=I(n,u);if(Object.hasOwn(d,f))throw new s("INTERNAL",`defineSchema(...).extend("${n}"): vector index "${f}" already exists in the base schema — another extension with the same key already contributed it`);d[f]={...l,table:S(l.table,n,r)}}return P(o),{rlsMode:e.rlsMode,tables:o,vectorIndexes:d}},we=(e,t)=>{let n=e;for(const r of t)r.extension&&(n=C(n,r.extension));return n},xe=e=>{const t=e.map(n=>n.middleware).filter(n=>n!==void 0);return K(t)};export{be as a,oe as b,xe as c,ue as d,fe as e,he as f,ge as g,le as h,ce as i,me as j,we as k,C as m,P as v};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as t}from"./compose-middleware-
|
|
1
|
+
import{c as t}from"./compose-middleware-MCpZZKh7.mjs";const i=c=>{const e=[c.rateLimit,c.captcha,...c.use??[]].filter(r=>r!==void 0);return t(e)};export{i as protectPublic};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./wire-codec-CH4vwdFT.mjs";import"./bindOrm-ChQydkdL.mjs";import{
|
|
1
|
+
import"./wire-codec-CH4vwdFT.mjs";import"./bindOrm-ChQydkdL.mjs";import{f as t,g as m,i as p,b as l,m as d,p as n,d as c,e as h,a as u,r as v}from"./middleware-CoVBvN2z.mjs";import"./policy-tag-V0lqFzgS.mjs";import"./allowAll-DkRAgItV.mjs";export{t as computeReadBaseWhere,m as evaluateWrite,p as indexRolePermissions,l as isFacadeEntry,d as matchesWhere,n as permissionName,c as readIdentityRoles,h as resolveCan,u as resolvePolicyAuth,v as rls};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as i}from"@lunora/errors";const u=async(l,d,c)=>{let n=-1;const s=async(e,t)=>{if(e<=n)throw new i("INTERNAL","middleware next() called multiple times");n=e;const o=l[e];if(!o)return c(t);const r={promise:void 0},m=await o({ctx:t,next:(a=>(r.promise=s(e+1,a?.ctx?{...t,...a.ctx}:t),r.promise))});if(r.promise===void 0)throw new i("INTERNAL",`middleware at position ${String(e)} resolved without calling next(): every later .use() step (rls/mask/storageRules) and the handler's context were skipped. Return next() — or next({ ctx }) to extend the context — and throw to deny.`);return await r.promise,m};return s(0,d)};export{u as r};
|
package/dist/packem_shared/{serveStorageObject-te5TWJIE.mjs → serveStorageObject-BzZEP-B5.mjs}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isSafeHeaderValue as l}from"./httpAction-
|
|
1
|
+
import{isSafeHeaderValue as l}from"./httpAction-DfKw8ZZt.mjs";const h=/^bytes=(\d*)-(\d*)$/,g=t=>t.startsWith('"')||t.startsWith('W/"')?t:`"${t}"`,f=(t,e)=>{if(t===null)return{kind:"full"};const n=h.exec(t.trim());if(!n)return{kind:"full"};const s=n[1]??"",a=n[2]??"";if(s===""&&a==="")return{kind:"full"};let o,r;if(s===""){const i=Number(a);if(i===0)return{kind:"unsatisfiable"};o=Math.max(0,e-i),r=e-1}else o=Number(s),r=a===""?e-1:Math.min(Number(a),e-1);return o>r||o>=e?{kind:"unsatisfiable"}:{end:r,kind:"partial",start:o}},m=new Set(["audio/mpeg","audio/ogg","audio/wav","image/apng","image/avif","image/gif","image/jpeg","image/png","image/webp","video/mp4","video/webm"]),p=(t,e)=>{const n=t.httpMetadata?.contentType,s=n!==void 0&&l(n)?n:"application/octet-stream",a={"accept-ranges":"bytes","cache-control":e,"content-type":s,etag:g(t.etag),"x-content-type-options":"nosniff"};return m.has(s.split(";")[0]?.trim().toLowerCase()??"")||(a["content-disposition"]="attachment"),t.sha256Base64!==void 0&&(a["repr-digest"]=`sha-256=:${t.sha256Base64}:`),a},w=t=>f(t,0).kind==="full",u=async(t,e,n)=>{const s=await t.storage.download(e);return s?new Response(s.body,{headers:{...p(s,n),"content-length":String(s.size)},status:200}):new Response("Not Found",{status:404})},b=async(t,e,n)=>{try{return await t({key:e,request:n})===!0}catch{return!1}},y=async(t,e,n,s,a="no-store")=>{if(!await b(s,e,n))return new Response("Forbidden",{status:403});const o=n.headers.get("range");if(w(o))return u(t,e,a);const r=await t.storage.head(e);if(!r)return new Response("Not Found",{status:404});const i=f(o,r.size);if(i.kind==="unsatisfiable")return new Response("Range Not Satisfiable",{headers:{"accept-ranges":"bytes","content-range":`bytes */${String(r.size)}`,"content-type":"text/plain; charset=utf-8",etag:g(r.etag)},status:416});if(i.kind==="full")return u(t,e,a);const c=i.end-i.start+1,d=await t.storage.download(e,{range:{length:c,offset:i.start}});return d?new Response(d.body,{headers:{...p(r,a),"content-length":String(c),"content-range":`bytes ${String(i.start)}-${String(i.end)}/${String(r.size)}`},status:206}):new Response("Not Found",{status:404})};export{y as serveStorageObject};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{LunoraError as A}from"@lunora/errors";import{i as U,a as $}from"./middleware-
|
|
1
|
+
import{LunoraError as A}from"@lunora/errors";import{i as U,a as $}from"./middleware-CoVBvN2z.mjs";const N=(e,t,o)=>{const a=s=>o.isAllowed("list",s,t)&&o.isAllowed("read",s,t),{get:r,query:b}=e,l={...e},w=s=>s.filter(n=>typeof n.key=="string"&&a(n.key));return typeof r=="function"&&(l.get=async(s,n)=>(s==="_storage"&&o.assertAllowed("read",n,t),r(s,n))),typeof b=="function"&&(l.query=s=>{const n=b(s),{collect:p}=n;return s!=="_storage"||typeof p!="function"?n:{...n,collect:async()=>w(await p())}}),l},S=(e,t)=>{if(e===void 0)return!0;const o=e.endsWith("/")?e.slice(0,-1):e;return o===""||t===o||t.startsWith(`${o}/`)},D=e=>{const t=e[1],o=typeof t=="object"&&t!==null?t.method:void 0;return typeof o=="string"&&o.toUpperCase()==="PUT"?"write":"read"},E=[["delete","delete"],["deleteAfterCommit","delete"],["download","read"],["generateUploadUrl","write"],["getMetadata","read"],["getSignedUrl",D],["getUrl","read"],["head","read"],["store","write"]],M=(e,t)=>{try{return e(t).bucketName===t}catch{return!1}},P=(e,t)=>{const o=e.bucketName??"default",{bucket:a}=e;for(const r of new Set(t))if(!(r===o||typeof a=="function"&&M(a,r)))throw new A("INTERNAL",`storageRules: rule for bucket "${r}" governs nothing — this request's storage cannot address that bucket (the accessor is "${o}", and selecting "${r}" 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 })\`.`)},C=(e,t={})=>{const o=U(t.roles);return async({ctx:a,next:r})=>{const b=await $(a.auth??{},o),l=(c,u,d)=>{const h=e.filter(i=>i.on===c&&i.bucket===d);if(h.length===0)return!0;const f={auth:b,ctx:a,key:u};return h.some(i=>S(i.prefix,u)&&i.when(f)===!0)},w=(c,u,d)=>{if(!l(c,u,d))throw new A("FORBIDDEN",`storage ${c} on "${u}" in bucket "${d}" denied by access rule`)},s=c=>{const u=c.bucketName??"default",d={bucketName:u};for(const[f,i]of E){const k=c[f];typeof k=="function"&&(d[f]=(...y)=>{const R=typeof y[0]=="string"?y[0]:"",v=typeof i=="function"?i(y):i;return w(v,R,u),k(...y)})}const{bucket:h}=c;return typeof h=="function"&&(d.bucket=f=>s(h(f))),d},n=a.storage;if(n===void 0)return r();P(n,e.map(c=>c.bucket));const p={storage:s(n)},g=a.db,m=g?.system;return g!==void 0&&m!==void 0&&typeof m=="object"&&(p.db={...g,system:N(m,n.bucketName??"default",{assertAllowed:w,isAllowed:l})}),r({ctx:p})}};export{C as storageRules};
|
package/dist/rls/testing.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{i as y,d as P,
|
|
1
|
+
import{i as y,d as P,e as W,f as v,m as I,g as R}from"../packem_shared/middleware-CoVBvN2z.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.
|
|
3
|
+
"version": "1.0.0-alpha.109",
|
|
4
4
|
"description": "Server primitives for Lunora: defineSchema, defineTable, query, mutation, and action",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"backend",
|
|
@@ -66,11 +66,11 @@
|
|
|
66
66
|
"access": "public"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@lunora/errors": "1.0.0-alpha.
|
|
70
|
-
"@lunora/scheduler": "1.0.0-alpha.
|
|
71
|
-
"@lunora/values": "1.0.0-alpha.
|
|
69
|
+
"@lunora/errors": "1.0.0-alpha.35",
|
|
70
|
+
"@lunora/scheduler": "1.0.0-alpha.59",
|
|
71
|
+
"@lunora/values": "1.0.0-alpha.43",
|
|
72
72
|
"drizzle-orm": "^0.45.2",
|
|
73
|
-
"hono": "^4.13.
|
|
73
|
+
"hono": "^4.13.7"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
76
|
"@opentelemetry/api": "^1.9.0"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{i as d,d as f,e as u,f as h}from"./middleware-CJSowZY0.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};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraError as r}from"@lunora/errors";import{c as p}from"./context-identity-B2XYGA0B.mjs";import{v as u}from"./functions-B2-H4PQT.mjs";import{toWhereInput as d,deny as m}from"./allowAll-DkRAgItV.mjs";const I=e=>{if(e.table.trim()==="")throw new r("INTERNAL","defineShape: `table` must be a non-empty string");if(e.columns?.length===0)throw new r("INTERNAL","defineShape: `columns` must list at least one column when provided");if(!e.where&&e.owner===void 0)throw new r("INTERNAL",`defineShape: shape on table "${e.table}" needs a \`where\` predicate or an \`owner\` (\`owner: true\` uses the table's .ownedBy(field), \`owner: "field"\` names the column) — a shape with neither would replicate the whole table`);return{__lunoraShape:!0,...e,compileWhere:(a,n,s)=>{const w=u(e.args??{},n),o=e.where?d(e.where(a,w)):void 0;if(e.owner===void 0)return o;const t=e.owner===!0?s?.ownerField:e.owner;if(t===void 0||t==="")throw new r("INTERNAL",`defineShape: shape on table "${e.table}" declares \`owner: true\` but the table has no \`.ownedBy(field)\` — add it to the table, or name the column directly with \`owner: "field"\``);const h=p(a);if(h===void 0)return m();const l={[t]:h};return o===void 0?l:{AND:[l,o]}}}};export{I as defineShape};
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import{LunoraError as E,toErrorBody as O,isLunoraError as N}from"@lunora/errors";import{parseValidatorMap as R,ValidationError as _}from"@lunora/values";import{Hono as q}from"hono";import{a as k}from"./apply-output-C5wZ5EAL.mjs";const V=e=>async r=>e(r.get("lunora"),r.req.raw),$=()=>{const e=new q;return e.use("*",async(r,o)=>{const n=r.env.__lunoraCtx;if(!n)throw new E("INTERNAL_SERVER_ERROR","HttpActionCtx was not injected — mount httpRouter() on createWorker(), which supplies it per request.");r.set("lunora",n),await o()}),e},v=e=>e.kind==="optional"?e._meta?.inner??e:e,g=(e,r)=>{switch(e){case"bigint":try{return BigInt(r)}catch{return r}case"boolean":return r==="true"||r==="1"?!0:r==="false"||r==="0"?!1:r;case"number":return r===""?Number.NaN:Number(r);default:return r}},x=(e,r,o)=>{const n=v(e);if(n.kind==="array"){const a=r.req.queries(o);if(a===void 0)return;const d=n._meta?.inner;return a.map(i=>g(d?.kind??"string",i))}const t=r.req.query(o);return t===void 0?void 0:g(n.kind,t)},S=(e,r)=>{const o={};for(const n of Object.keys(e)){const t=e[n];t&&(o[n]=x(t,r,n))}return R(e,o,"searchParams")},T=(e,r)=>{const o=r.req.param(),n={};for(const t of Object.keys(e)){const a=e[t];if(!a)continue;const d=o[t];n[t]=d===void 0?void 0:g(v(a).kind,d)}return R(e,n,"params")},C=async(e,r)=>{let o;try{o=await r.req.json()}catch{throw new E("BAD_REQUEST","Invalid JSON body")}if(typeof o!="object"||o===null||Array.isArray(o))throw new E("BAD_REQUEST","Expected a JSON object body");return R(e,o,"body")},j=e=>{if(e instanceof _)return Response.json({code:"BAD_REQUEST",error:e.message},{status:400});if(N(e)){const{body:r,redacted:o,status:n}=O(e,{fallbackCode:"INTERNAL_SERVER_ERROR",redactedMessage:"Internal error"});return o&&console.error("[lunora] http action error (redacted on the wire):",e),Response.json({code:r.code,error:r.message},{status:n})}throw e},A=(e,r)=>{const{method:o}=r.req;if(!(o===e.method||e.method==="GET"&&o==="HEAD"))return Response.json({code:"METHOD_NOT_ALLOWED",error:`${o} is not allowed on this route (declared as ${e.method})`},{headers:{allow:e.method},status:405})},H=(e,r)=>async o=>{const n=A(e,o);if(n)return n;try{const t=o.get("lunora"),a=Object.keys(e.searchParams).length>0?S(e.searchParams,o):{},d=Object.keys(e.params).length>0?T(e.params,o):{},i=Object.keys(e.body).length>0?await C(e.body,o):{},h=await r({body:i,ctx:t,params:d,searchParams:a}),s=e.output?k(e.output,h):h,c={};e.cacheControl&&(c["cache-control"]=e.cacheControl),e.cacheTag&&(c["cache-tag"]=e.cacheTag),e.vary&&(c.vary=e.vary);const f=Object.keys(c).length>0;return s===void 0?new Response(null,{headers:f?c:void 0,status:204}):Response.json(s,{headers:f?c:void 0})}catch(t){return j(t)}},w={"cache-control":"no-cache, no-transform","content-type":"text/event-stream; charset=utf-8","x-accel-buffering":"no"},b=(e,r)=>{const o=JSON.stringify(e);return`${r?`event: ${r}
|
|
2
|
-
`:""}data: ${o}
|
|
3
|
-
|
|
4
|
-
`},L=(e,r)=>(async o=>{const n=A(e,o);if(n)return n;let t,a;try{t=Object.keys(e.searchParams).length>0?S(e.searchParams,o):{},a=Object.keys(e.params).length>0?T(e.params,o):{}}catch(p){return j(p)}const d=o.get("lunora"),i=o.req.raw,h=new TextEncoder,s=new AbortController;if(i.signal.aborted)return s.abort(),new Response("",{headers:w});const c=()=>{s.abort()};i.signal.addEventListener("abort",c,{once:!0});const f=new ReadableStream({cancel(){i.signal.removeEventListener("abort",c),s.abort()},async start(p){try{const y=r({ctx:d,params:a,request:i,searchParams:t,signal:s.signal});for await(const m of y){if(s.signal.aborted)break;p.enqueue(h.encode(b(e.output?k(e.output,m):m)))}s.signal.aborted||p.enqueue(h.encode(b({},"complete")))}catch(y){const{body:m,redacted:P}=O(y,{fallbackCode:"INTERNAL_SERVER_ERROR",redactedMessage:"Internal error"});P&&console.error("[lunora] unhandled stream handler error:",y),s.signal.aborted||p.enqueue(h.encode(b({code:m.code,message:m.message},"error")))}finally{i.signal.removeEventListener("abort",c);try{p.close()}catch{}}}});return new Response(f,{headers:w})}),u=e=>({body:r=>u({...e,body:{...e.body,...r}}),cacheControl:r=>u({...e,cacheControl:r}),cacheTag:r=>u({...e,cacheTag:r}),handler:r=>H(e,r),output:r=>u({...e,output:r}),params:r=>u({...e,params:{...e.params,...r}}),searchParams:r=>u({...e,searchParams:{...e.searchParams,...r}}),stream:r=>L(e,r),vary:r=>u({...e,vary:r})}),l=e=>r=>u({body:{},method:e,params:{},path:r,searchParams:{}}),U={delete:l("DELETE"),get:l("GET"),head:l("HEAD"),options:l("OPTIONS"),patch:l("PATCH"),post:l("POST"),put:l("PUT")},J=e=>!(e.includes("\r")||e.includes(`
|
|
5
|
-
`)||e.includes("\0"));export{V as httpAction,U as httpRoute,$ as httpRouter,J as isSafeHeaderValue};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraError as S}from"@lunora/errors";import{f as U}from"./fnv1a-D3ueNTWB.mjs";import{i as L,a as z,o as _,b as G,c as C}from"./middleware-CJSowZY0.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)===!0)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};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraError as w}from"@lunora/errors";import{i as U}from"./wire-codec-CH4vwdFT.mjs";import{bindTableFacade as X,bindOrm as J}from"./bindOrm-ChQydkdL.mjs";import{t as Z}from"./policy-tag-V0lqFzgS.mjs";import{deny as V}from"./allowAll-DkRAgItV.mjs";const x=["every","is","isNot","none","some"],ee=new Set(x),re=e=>{if(e===null||typeof e!="object"||Array.isArray(e))return!1;const r=Object.keys(e);return r.length>0&&r.every(n=>ee.has(n))},F=(e,r,n)=>r?{[e]:n(r)}:{},te=["contains","eq","gt","gte","in","isNull","lt","lte","ne","notIn"],A=-1,k=0,O=1,v=e=>e?O:A,y=(e,r)=>e<r?e:r,ne=(e,r)=>e>r?e:r,j=e=>e===k?k:v(e===A),$=e=>{const r=typeof e;return r==="number"||r==="string"||r==="bigint"},B=e=>e==null,I=(e,r,n)=>{if(n===void 0)throw new w("BAD_REQUEST",`\`${r}\` on "${e}" received undefined; a comparison needs a value`)},se={gt:(e,r)=>e>r,gte:(e,r)=>e>=r,lt:(e,r)=>e<r,lte:(e,r)=>e<=r},oe=(e,r,n)=>{let i=O;for(const c of["gt","gte","lt","lte"]){if(!(c in n))continue;const l=n[c];if(I(e,c,l),!$(l))i=y(i,A);else if(B(r))i=y(i,k);else{const h=se[c];i=y(i,v($(r)&&h(r,l)))}}return i},N=(e,r,n)=>{if(!Array.isArray(n))throw new w("BAD_REQUEST",`\`${r}\` on "${e}" expects an array of values, received ${n===null?"null":typeof n}`);if(n.includes(void 0))throw new w("BAD_REQUEST",`\`${r}\` on "${e}" received undefined inside its list; a comparison needs a value`);return n},q=(e,r)=>r.length===0?A:B(e)?k:r.includes(e)?O:r.some(n=>B(n))?k:A,ie=(e,r,n)=>{let i=O;if("in"in n&&(i=y(i,q(r,N(e,"in",n.in)))),"notIn"in n&&(i=y(i,j(q(r,N(e,"notIn",n.notIn))))),"contains"in n){const c=n.contains;I(e,"contains",c),i=B(r)?y(i,k):y(i,v(typeof r=="string"&&typeof c=="string"&&r.includes(c)))}return"isNull"in n&&(i=y(i,v(n.isNull===!0===B(r)))),i},T=(e,r,n)=>r===null?v(n===B(e)):B(e)?k:v(n===(e===r)),ae=(e,r,n)=>{let i=O;return"eq"in n&&(I(e,"eq",n.eq),i=y(i,T(r,n.eq,!0))),"ne"in n&&(I(e,"ne",n.ne),i=y(i,T(r,n.ne,!1))),i},ce=(e,r,n)=>y(ae(e,r,n),y(ie(e,r,n),oe(e,r,n))),le=(e,r,n,i)=>{if(r==="NOT")return j(i(e,n??{}));if(!Array.isArray(n))return A;let c=r==="AND"?O:A;for(const l of n){const h=i(e,l);c=r==="AND"?y(c,h):ne(c,h)}return c},K=e=>e==="AND"||e==="OR"||e==="NOT",fe=e=>U(e)&&Object.keys(e).every(r=>te.includes(r)),M=e=>Object.keys(e).some(r=>{const n=e[r];return K(r)?r==="NOT"?M(n??{}):Array.isArray(n)&&n.some(i=>M(i??{})):re(n)}),Y=(e,r)=>{let n=O;for(const i of Object.keys(r)){const c=r[i];if(K(i)){n=y(n,le(e,i,c,Y));continue}const l=e[i];if(fe(c)){n=y(n,ce(i,l,c));continue}I(i,"eq",c),n=y(n,T(l,c,!0))}return n},m=(e,r)=>Y(e,r)===O,Q=500,_=(e,r,n)=>{const i=r??Q;if(e>i)throw new w("BATCH_LIMIT_EXCEEDED",`${n}: batch of ${String(e)} exceeds the limit of ${String(i)} (raise options.limit or chunk the call)`,{status:400})},ue=V(),he=Symbol.for("lunora.ctxdb.rls-unwrap"),de=e=>{const r=new Map;for(const n of e){const i=r.get(n.table)??[];i.push(n),r.set(n.table,i)}return r},ye=(e,r)=>{const n=[];let i=!1;for(const c of e){if(c.on!=="read")continue;const l=c.when(r);if(l!==void 0){if(l===!0){i=!0;break}l!==!1&&n.push(l)}}if(!i)return n.length===0?ue:n.length===1?n[0]:{OR:n}},we=(e,r,n)=>{const i=e.when({...r,row:n});if(i===void 0||i===!0)return!0;if(i===!1)return!1;if(M(i))throw new w("RELATION_PREDICATE_UNSUPPORTED","relation predicates are not supported in write policies; use a read policy or a flat column check");return m(n,i)},ge=(e,r,n)=>{const i=e.when(r);if(i===void 0||i===!0)return n===void 0||we(e,r,n);if(i===!1)return!1;if(M(i))throw new w("RELATION_PREDICATE_UNSUPPORTED","relation predicates are not supported in write policies; use a read policy or a flat column check");return!r.row||!m(r.row,i)?!1:n===void 0||m(n,i)},P=(e,r,n,i)=>{let c=!1;for(const l of e)if(l.on===r&&(c=!0,!ge(l,n,i)))return!1;return c},pe=new Set(["baseWhere","relationBaseWhere","restrictsCounts","where"]),We=(e,r)=>r===void 0?!0:e==="restrictsCounts"?typeof r=="boolean":e==="relationBaseWhere"?typeof r=="function":U(r),Oe=e=>{const r=Object.keys(e);return r.length===0?!1:r.every(n=>pe.has(n)&&We(n,e[n]))},Re=e=>e===void 0?{}:U(e)&&Oe(e)?e:{where:e},p=(e,r)=>!r||Object.keys(r).length===0?e:!e||Object.keys(e).length===0?r:{AND:[r,e]},be=e=>{if(typeof e!="object"||e===null||Array.isArray(e))return!1;const r=e;return typeof r.findMany=="function"&&typeof r.withSearchIndex=="function"},Ee=(e,r,n,i)=>{const c=new Map,l=t=>{const s=c.get(t);if(s)return s;const o=n.get(t);if(!o||o.length===0||!o.some(u=>u.on==="read")){const u={baseWhere:void 0,restricts:!1};return c.set(t,u),u}const f={baseWhere:ye(o,i),restricts:!0};return c.set(t,f),f},h=t=>l(t).restricts?r:e,g=t=>l(t).baseWhere,R=async(t,s)=>{if(r.lookupById){const d=await r.lookupById(t,s);return d?{row:d.row,tableName:n.has(d.tableName)?d.tableName:void 0}:{row:null,tableName:void 0}}const o=await r.get(t,s);if(!o)return{row:null,tableName:void 0};const a=s!==void 0&&n.has(s)?[s]:[],f=s===void 0?[...n.keys()]:a,u=await Promise.all(f.map(async d=>(await r.findFirst(d,{limit:1,where:{_id:t}}))?._id===t?d:void 0));return{row:o,tableName:u.find(d=>d!==void 0)}},C=async(t,s)=>{const o=await R(t,s);return o.row&&o.tableName!==void 0?{row:o.row,tableName:o.tableName}:void 0},W=async(t,s,o,a,f)=>{const u=await C(t,f);if(!u)return o(e);const d=n.get(u.tableName);if(d){const E=a?a(u.row):void 0;if(!P(d,s,{...i,row:u.row},E))throw new w("FORBIDDEN",`${s} denied by policy`)}return o(r)},b=(t,s)=>{const{baseWhere:o,restricts:a}=l(t);if(a)throw new w("COUNT_RLS_UNSUPPORTED",`${s}() is not supported on "${t}" inside an RLS-restricted context`);return o},D=e.rankBefore,H=e.rankPageRows,S={...e,async count(t,s){const{baseWhere:o,restricts:a}=l(t),f=Re(s);return h(t).count(t,{...f,baseWhere:p(f.baseWhere,o),relationBaseWhere:g,restrictsCounts:(f.restrictsCounts??!1)||a})},delete:(t,s,o)=>W(t,"delete",a=>a.delete(t,s,o),void 0,s),async deleteAll(t,s){const o=Math.max(1,s?.chunkSize??Q),{baseWhere:a}=l(t);let f=0;for(;;){const d=(await h(t).findMany(t,{baseWhere:a,limit:o,relationBaseWhere:g})).page.map(E=>String(E._id));if(d.length===0)break;for(const E of d)await W(E,"delete",L=>L.delete(E,void 0,s?.hard===void 0?void 0:{hard:s.hard})),f+=1;if(d.length<o)break}return{deleted:f}},async deleteMany(t,s,o){_(t.length,s?.limit,"deleteMany");for(const a of t)await W(a,"delete",f=>f.delete(a,o),void 0,o);return{deleted:t.length}},async deleteWhere(t,s,o){const{baseWhere:a}=l(t),u=(await h(t).findMany(t,{baseWhere:p(s,a),relationBaseWhere:g})).page.map(d=>String(d._id));return _(u.length,o?.limit,"deleteWhere"),S.deleteMany(u,o)},async findFirst(t,s){const{baseWhere:o}=l(t);return h(t).findFirst(t,{...s,baseWhere:p(s?.baseWhere,o),relationBaseWhere:g})},async findFirstOrThrow(t,s){const{baseWhere:o}=l(t);return h(t).findFirstOrThrow(t,{...s,baseWhere:p(s?.baseWhere,o),relationBaseWhere:g})},async findMany(t,s){const{baseWhere:o}=l(t);return h(t).findMany(t,{...s,baseWhere:p(s?.baseWhere,o),relationBaseWhere:g})},async get(t,s){const o=await R(t,s);if(!o.row)return null;if(o.tableName===void 0)return e.get(t,s);const{baseWhere:a,restricts:f}=l(o.tableName);if(!f)return e.get(t,s);if(!a)return o.row;const u=await r.findFirst(o.tableName,{baseWhere:a,limit:1,where:{_id:t}});return u?._id===t?u:null},async lookupById(t,s){const o=await R(t,s);if(!o.row)return null;if(o.tableName===void 0||!l(o.tableName).restricts)return await e.lookupById?.(t,s)??null;const{baseWhere:a}=l(o.tableName);if(!a)return{row:o.row,tableName:o.tableName};const f=await r.findFirst(o.tableName,{baseWhere:a,limit:1,where:{_id:t}});return f?._id===t?{row:f,tableName:o.tableName}:null},async insert(t,s){const o=n.get(t);if(o){if(!P(o,"insert",{...i,row:s}))throw new w("FORBIDDEN",`insert on "${t}" denied by policy`);return r.insert(t,s)}return e.insert(t,s)},async insertMany(t,s,o){_(s.length,o?.limit,"insertMany");const a=n.get(t);if(a){for(const f of s)if(!P(a,"insert",{...i,row:f}))throw new w("FORBIDDEN",`insert on "${t}" denied by policy`);return r.insertMany(t,s,o)}return e.insertMany(t,s,o)},async insertManyUnsafe(t,s,o){_(s.length,o?.limit,"insertManyUnsafe");const a=n.get(t);if(a){for(const f of s)if(!P(a,"insert",{...i,row:f}))throw new w("FORBIDDEN",`insert on "${t}" denied by policy`);return r.insertManyUnsafe(t,s,o)}return e.insertManyUnsafe(t,s,o)},patch:(t,s,o)=>W(t,"update",a=>a.patch(t,s,o),a=>({...a,...s}),o),async patchMany(t,s,o){_(t.length,s?.limit,"patchMany");for(const a of t)await W(a.id,"update",f=>f.patch(a.id,a.patch,o),f=>({...f,...a.patch}),o);return{patched:t.length}},async patchWhere(t,s,o){const{baseWhere:a}=l(t),u=(await h(t).findMany(t,{baseWhere:p(s.where,a),relationBaseWhere:g})).page.map(d=>({id:String(d._id),patch:s.patch}));return _(u.length,o?.limit,"patchWhere"),S.patchMany(u,o)},query(t){const{baseWhere:s}=l(t),o=h(t).query(t);return s?o.filter(a=>m(a,s)):o},restore:(t,s)=>W(t,"update",async o=>{const a=o.restore??r.restore;if(!a)throw new w("BAD_REQUEST","restore is not supported by this writer");await a(t,s)},void 0,s),replace:(t,s,o)=>W(t,"update",a=>a.replace(t,s,o),a=>({...s,_creationTime:s._creationTime??a._creationTime,_id:a._id}),o),aggregate(t,s){const{baseWhere:o}=l(t);return h(t).aggregate(t,{...s,baseWhere:p(s.baseWhere,o),relationBaseWhere:g})},groupBy(t,s){const{baseWhere:o}=l(t);return h(t).groupBy(t,{...s,baseWhere:p(s.baseWhere,o),relationBaseWhere:g})},rank(t,s,o){const a=b(t,"rank");return h(t).rank(t,s,{...o,baseWhere:p(o.baseWhere,a)})},rankPage(t,s,o){const a=b(t,"rankPage");return h(t).rankPage(t,s,{...o,baseWhere:p(o?.baseWhere,a)})},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})},...F("rankBefore",D,t=>(s,o,a)=>(b(s,"rankBefore"),(h(s).rankBefore??t)(s,o,a))),...F("rankPageRows",H,t=>(s,o,a)=>{const f=b(s,"rankPageRows");return(h(s).rankPageRows??t)(s,o,{...a,baseWhere:p(a?.baseWhere,f)})})},G=S;if(n.size>0)for(const[t,s]of Object.entries(e))be(s)&&(G[t]=X(S,t));return S},z=e=>typeof e=="string"?e:e.name,Ae=e=>{const r=new Map;for(const n of e??[])r.set(n.name,new Set((n.permissions??[]).map(i=>z(i))));return r},ke=(e,r)=>{const n=new Set;for(const i of e)for(const c of r.get(i)??[])n.add(c);return i=>n.has(z(i))},Be=["roles","role"],_e=e=>{for(const r of Be){const n=e?.[r];if(typeof n=="string"){const i=n.split(",").map(c=>c.trim()).filter(c=>c.length>0);if(i.length>0)return i}if(Array.isArray(n)){const i=n.filter(c=>typeof c=="string"&&c.length>0);if(i.length>0)return i}}return[]},ve=async(e,r)=>{const n=await e.getIdentity?.()??null,i=[...new Set([..._e(n),...e.roles??[]])];return{can:ke(i,r),identity:n,roles:i,userId:e.userId??null}},me=(e,r={})=>{const n=de(e),i=Ae(r.roles);return Z(async({ctx:l,next:h})=>{const g={auth:await ve(l.auth??{},i),ctx:l},R=l.db,C=R[he]??R,W=Ee(R,C,n,g),b={db:W},{orm:D}=l;return D!==null&&typeof D=="object"&&(b.orm=J(W)),h({ctx:b})},[{policies:e,roles:r.roles??[]}])};export{ve as a,be as b,re as c,_e as d,ye as e,ke as f,P as g,Ae as i,m,F as o,z as p,me as r};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraError as d}from"@lunora/errors";const w=async(s,c,i)=>{let a=-1;const e=async(r,t)=>{if(r<=a)throw new d("INTERNAL","middleware next() called multiple times");a=r;const n=s[r];return n?await n({ctx:t,next:(l=>e(r+1,l?.ctx?{...t,...l.ctx}:t))}):i(t)};return e(0,c)};export{w as r};
|