@lunora/server 1.0.0-alpha.13 → 1.0.0-alpha.15

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.
Files changed (26) hide show
  1. package/dist/index.d.mts +14 -75
  2. package/dist/index.d.ts +14 -75
  3. package/dist/index.mjs +19 -19
  4. package/dist/packem_shared/{LunoraEnvError-DjFkpkSP.mjs → LunoraEnvError-BGmd1Qs0.mjs} +4 -4
  5. package/dist/packem_shared/LunoraError-WbxmrpxR.mjs +9 -0
  6. package/dist/packem_shared/{PRESENCE_DEFAULT_TTL_MS-D8viLY1S.mjs → PRESENCE_DEFAULT_TTL_MS-BtPCiNLW.mjs} +4 -4
  7. package/dist/packem_shared/{bindOrm-Ce57S3N9.mjs → bindOrm-CNXKgfUa.mjs} +9 -7
  8. package/dist/packem_shared/{buildRlsReadRegistry-Do1CSBTr.mjs → buildRlsReadRegistry-DFBFLydB.mjs} +1 -1
  9. package/dist/packem_shared/{composePluginMiddleware-Ck5_TUO8.mjs → composePluginMiddleware-CcTj1_v2.mjs} +9 -6
  10. package/dist/packem_shared/{createPolicyDsl-De67zPDS.mjs → createPolicyDsl-By3QB4he.mjs} +4 -1
  11. package/dist/packem_shared/{createSecrets-TsIP9lOa.mjs → createSecrets-DwaR2rNG.mjs} +4 -1
  12. package/dist/packem_shared/{defineAggregateIndex-ZdyU78gh.mjs → defineAggregateIndex-znq4ygKQ.mjs} +17 -13
  13. package/dist/packem_shared/{defineIdentity-B_9YD46A.mjs → defineIdentity-DiX4zM9x.mjs} +2 -1
  14. package/dist/packem_shared/{defineMigration-CAJLr6fx.mjs → defineMigration-Hx01yIht.mjs} +3 -1
  15. package/dist/packem_shared/{defineShape-CJ27Wx7o.mjs → defineShape-C5scNOrf.mjs} +3 -2
  16. package/dist/packem_shared/{defineStorageRule-qu0mpilX.mjs → defineStorageRule-B5nL4Z1P.mjs} +4 -1
  17. package/dist/packem_shared/{httpAction-FLwfsePg.mjs → httpAction-B_16WzMO.mjs} +6 -15
  18. package/dist/packem_shared/{initLunora-lxwHTEV3.mjs → initLunora-sQUqaejx.mjs} +1 -1
  19. package/dist/packem_shared/{mask-E8MgAS3N.mjs → mask-Dc8G5Gl7.mjs} +2 -2
  20. package/dist/packem_shared/{protectPublic-BjFkQ_Or.mjs → protectPublic-BlcGpiRc.mjs} +1 -1
  21. package/dist/packem_shared/{rls-DhNgKFeW.mjs → rls-3sCJIH6F.mjs} +2 -2
  22. package/dist/packem_shared/{run-middleware-CYQOuoV6.mjs → run-middleware-I6EiQfxL.mjs} +3 -1
  23. package/dist/packem_shared/{storageRules-Cje6Woea.mjs → storageRules-6QxzDOcx.mjs} +1 -1
  24. package/dist/rls/testing.mjs +1 -1
  25. package/package.json +4 -3
  26. package/dist/packem_shared/LunoraError-DN7Zhhvu.mjs +0 -54
package/dist/index.d.mts CHANGED
@@ -2,6 +2,8 @@ import { Validator, Infer, ValidatorMap, InferValidatorMap, v } from '@lunora/va
2
2
  export { type ColumnValidator, type Id, type Infer, ValidationError, type Validator, type ValidatorKind, v } from '@lunora/values';
3
3
  import { ArgsValidator, InferArgs, RegisteredAction, ActionCtx, MutationCtx, RegisteredMutation, QueryCtx, RegisteredQuery, RegisteredStream, FunctionKind, Secrets, LifecycleEvent, RegisteredLifecycleHook, TableDefinition, RegisteredFunction, VectorIndexDefinition, Schema, AggregateOp, DurableObjectJurisdiction, RelationDefinition, GlobalBackend, OnDeleteAction, ExternalSourceDefinition, TriggerBuilder, TriggerDefinition, VectorEmbedder, VectorMetric, AggregateIndexDefinition, RankIndexDefinition } from "./types.mjs";
4
4
  export { type AnyApi, type AuthState, type DatabaseReader, type DatabaseWriter, type FunctionVisibility, type IndexDefinition, type IndexRangeBuilder, type LifecycleEventKind, type LunoraLogger, type PaginationOptions, type PaginationResult, type RankSortKey, type ReadOnlyStorage, type ScheduledFunctionDoc, type ScheduledJob, type Scheduler, type SearchFilterBuilder, type SearchIndexDefinition, type ShardMode, type Storage, type StorageMetadata, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemTableName, type TableReader, type TableVectorIndex, type TriggerAggregateOptions, type TriggerCtx, type TriggerDatabase, type TriggerDeleteEvent, type TriggerEvent, type TriggerGroupByEntry, type TriggerGroupByOptions, type TriggerHandler, type TriggerInsertEvent, type TriggerOp, type TriggerQueryArgs, type TriggerQueryPage, type TriggerRankOptions, type TriggerRankPageOptions, type TriggerRankResult, type TriggerRow, type TriggerTiming, type TriggerUpdateEvent, type VectorMatch, type VectorMatches, type VectorQueryInput, type VectorRecord, type VectorSearch, type VectorSearchReader, type VectorUpsertInput, type WorkflowCreateOptions, type WorkflowHandle, type WorkflowInstance, type WorkflowInstanceStatus, type WorkflowStatusResult, type Workflows, anyApi } from "./types.mjs";
5
+ import { LunoraError as LunoraError$1, LunoraErrorCode } from '@lunora/errors';
6
+ export type { LunoraErrorCode } from '@lunora/errors';
5
7
  import { Context, Hono } from 'hono';
6
8
  import { b as Permission, R as Role, T as TypedDefinePolicyInput, a as Policy, D as DefinePolicyInput, W as WhereInput, c as RlsOptions } from "./packem_shared/types.d-BB3pjV0m.mjs";
7
9
  export type { d as PolicyContext, e as PolicyDecision, f as PolicyDecisionOf, P as PolicyOperation } from "./packem_shared/types.d-BB3pjV0m.mjs";
@@ -226,8 +228,7 @@ interface EnvKeyFailure {
226
228
  *
227
229
  * Named export only (no default) per the repo export convention.
228
230
  */
229
- declare class LunoraEnvError extends Error {
230
- override readonly name = "LunoraEnvError";
231
+ declare class LunoraEnvError extends LunoraError$1 {
231
232
  readonly failures: ReadonlyArray<EnvKeyFailure>;
232
233
  constructor(failures: ReadonlyArray<EnvKeyFailure>);
233
234
  }
@@ -277,63 +278,7 @@ interface EnvAccessor<S extends EnvShape> {
277
278
  * invalid — lazily on first access of that key, or eagerly via `config.parse(env)`.
278
279
  */
279
280
  declare const defineEnv: <S extends EnvShape>(shape: S) => EnvAccessor<S>;
280
- /**
281
- * Canonical error type for Lunora procedures and middleware.
282
- *
283
- * The runtime's structural error mapper keys off `name === "LunoraError"` plus
284
- * the numeric `status`, so throwing one of these from a handler or middleware
285
- * yields the right RPC/HTTP status without any further wiring. `code` carries
286
- * the machine-readable reason for clients; the optional `data` carries a
287
- * structured, JSON+wire-encodable payload propagated verbatim to the client
288
- * (e.g. `{ retryAfterMs }`). Only an explicit `LunoraError`'s `data` crosses the
289
- * wire — an unhandled throw is still redacted to a generic message server-side.
290
- */
291
- declare const CODE_STATUS: {
292
- readonly BAD_REQUEST: 400;
293
- readonly CONFLICT: 409;
294
- /**
295
- * `count()` invoked against a table whose context carries an active RLS
296
- * policy. The operation itself is unsupported in an RLS-restricted reader
297
- * (kitcn's documented constraint) — the request is well-formed and the
298
- * caller is authorized, so this is a 422 (semantic conflict) rather than a
299
- * 403 (policy denial).
300
- */
301
- readonly COUNT_RLS_UNSUPPORTED: 422;
302
- readonly FORBIDDEN: 403;
303
- readonly INTERNAL_SERVER_ERROR: 500;
304
- /**
305
- * An analytical reduction (`aggregate` / `groupBy`) was invoked over a
306
- * column that the procedure's `mask()` middleware redacts. A masked column
307
- * can't be summed, averaged, or grouped without leaking the very values the
308
- * mask hides (a group key *is* the raw value; an aggregate is computed from
309
- * it), so the operation fails closed. The request is well-formed and the
310
- * caller is authorized — this is a 422 (semantic conflict), mirroring
311
- * `COUNT_RLS_UNSUPPORTED`.
312
- */
313
- readonly MASK_UNSUPPORTED: 422;
314
- readonly NOT_FOUND: 404;
315
- readonly NOT_IMPLEMENTED: 501;
316
- /**
317
- * A write policy's `when` returned a relation-crossing predicate
318
- * (`some`/`none`/`every`/`is`/`isNot`). The in-memory write-policy evaluator
319
- * has no child fetcher and cannot resolve a relation node, so the policy is
320
- * unsupported as written. Relation predicates are valid in *read* policies
321
- * and query `where` clauses (the pre-resolver handles them there). The
322
- * request is well-formed; this is a 422 (semantic conflict), mirroring the
323
- * sibling `*_UNSUPPORTED` codes.
324
- */
325
- readonly RELATION_PREDICATE_UNSUPPORTED: 422;
326
- readonly TOO_MANY_REQUESTS: 429;
327
- readonly UNAUTHORIZED: 401;
328
- readonly UNPROCESSABLE: 422;
329
- };
330
- type LunoraErrorCode = keyof typeof CODE_STATUS;
331
- declare class LunoraError extends Error {
332
- override readonly name = "LunoraError";
333
- readonly code: LunoraErrorCode;
334
- readonly status: number;
335
- /** Structured, JSON+wire-encodable payload surfaced to the client alongside `code`. */
336
- readonly data?: unknown;
281
+ declare class LunoraError extends LunoraError$1 {
337
282
  constructor(code: LunoraErrorCode, message?: string, data?: unknown);
338
283
  }
339
284
  /**
@@ -659,6 +604,15 @@ interface ContextWithStorage {
659
604
  storage: StorageDownloader;
660
605
  }
661
606
  /**
607
+ * True when `value` is safe to use as an HTTP header field-value: no CR, LF, or
608
+ * NUL. Guards against response-header injection / `Headers`-construction throws
609
+ * when reflecting attacker-influenced object metadata (e.g. a stored
610
+ * `Content-Type`). Exported (see the `export {}` at the file end) so an `httpAction`
611
+ * handler can guard a request-derived header value before writing it — the fix the
612
+ * `http_action_response_header_injection` advisor lint points to.
613
+ */
614
+ declare const isSafeHeaderValue: (value: string) => boolean;
615
+ /**
662
616
  * Stream a stored object as an HTTP {@link Response} from an `httpAction`
663
617
  * handler, with correct `Content-Type`, `ETag`, and `Accept-Ranges: bytes`.
664
618
  * Honors a single-range `Range` request → **206 Partial Content** with
@@ -928,21 +882,6 @@ interface MaskContextIn {
928
882
  * includes this middleware — opt-in, never global (the same invariant as RLS).
929
883
  */
930
884
  declare const mask: <Context extends MaskContextIn = MaskContextIn>(policies: MaskPolicies<Context>, options?: MaskOptions<Context>) => Middleware<Context, Context>;
931
- /**
932
- * Online data-migration authoring API.
933
- *
934
- * `defineMigration` declares a per-document backfill over one table: `up`
935
- * transforms every existing row, `down` (optional) reverses it. Unlike the D1
936
- * SQL schema migrations in `@lunora/d1`, these run *inside each shard's*
937
- * Durable Object against live documents, in keyset batches, and are resumable —
938
- * the per-shard runner in `@lunora/do` tracks progress in a reserved
939
- * `__lunora_migrations` table so an interrupted run picks up where it stopped.
940
- *
941
- * The returned object carries a `__lunoraMigration` brand so codegen can
942
- * discover declarations through the type checker (mirroring the procedure
943
- * builder's `__lunoraProcedure` brand) and emit them into a `LUNORA_MIGRATIONS`
944
- * registry the DO and CLI look migrations up by id.
945
- */
946
885
  /** A document handed to a migration transform: the stored row including `_id`/`_creationTime`. */
947
886
  type MigrationDocument = Record<string, unknown>;
948
887
  /**
@@ -2052,4 +1991,4 @@ interface StorageContextIn {
2052
1991
  }
2053
1992
  declare const storageRules: <Context extends StorageContextIn = StorageContextIn>(rules: ReadonlyArray<StorageRule<Context>>, options?: StorageRulesOptions) => Middleware<Context, Context>;
2054
1993
  declare const VERSION = "0.0.0";
2055
- export { type ActionBuilder, type ActionCtx, type AggregateIndexDefinition, type AggregateIndexOptions, type AggregateOp, type ArgsValidator, type Component, type ComponentFunctions, type CreateOptions, type DataModelInit, type DefineComponentOptions, type DefineIdentityOptions, type DefinePluginOptions, type DefinePolicyInput, type DefinePresenceOptions, type DefineStorageRuleInput, type DurableObjectJurisdiction, type EmptyArgs, type EnvAccessor, type EnvKeyFailure, type EnvShape, type ExtendableSchema, type FacadeEntry, type FacadeWriterLike, type FunctionKind, type HttpActionCtx, type HttpActionHandler, type HttpMethod, type HttpRoute, type HttpRouteBuilder, type HttpRouteFactory, type HttpRouteHandlerOptions, type HttpStreamHandlerOptions, type IdentityContract, type IdentityRejectMode, type IdentityValidation, type InferArgs, type InferEnv, type InferIdentity, type InlineAggregateIndexOptions, type InlineRankIndexOptions, type InternalActionBuilder, type InternalMutationBuilder, type InternalQueryBuilder, type LifecycleEvent, type LifecycleHandler, type LunoraBuilders, LunoraEnvError, LunoraError, type LunoraErrorCode, type LunoraHttpApp, type LunoraHttpEnv, type LunoraRouteHandler, type ManyRelation, type MaskColumns, type MaskContext, type MaskFn, type MaskOptions, type MaskPolicies, type MaskStrategy, type Middleware, type MiddlewareNext, type MigrationDefinition, type MigrationDocument, type MigrationTransform, type MutationBuilder, type MutationCtx, type MutatorDefinition, type OnDeleteAction, type OneRelation, type OrmLike, DEFAULT_TTL_MS as PRESENCE_DEFAULT_TTL_MS, PRESENCE_TABLE, type Permission, type Plugin, type Policy, type PrefixedTables, type PresenceComponent, type PresenceFunctions, type PresenceMember, type ProtectPublicOptions, type QueryBuilder, type QueryCtx, type RankIndexDefinition, type RankIndexOptions, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMigration, type RegisteredMutation, type RegisteredMutator, type RegisteredQuery, type RegisteredShape, type RegisteredStream, type RelationBuilder, type RelationDefinition, type RlsOptions, type RlsReadRegistry, type Role, type Schema, type SchemaExtension, type ShapeDefinition, type ShapeReadWhereRequest, type StorageOperation, type StorageRule, type StorageRuleContext, type StorageRuleDecision, type StorageRulesOptions, type TableBuilder, type TableDefinition, type TerminalKind, type TriggerBuilder, type TriggerDefinition, type TypedDefinePolicyInput, VERSION, type VectorEmbedder, type VectorIndexDefinition, type VectorIndexOptions, type VectorMetric, type VectorizeOptions, type WhereInput, asBucketStorage, bindOrm, bindTableFacade, buildRlsReadRegistry, composePluginMiddleware, composeShapeReadWhere, createPolicyDsl, createSecrets, defineAggregateIndex, defineComponent, defineEnv, defineIdentity, defineMigration, defineMutator, definePermission, definePlugin, definePolicies, definePolicy, definePresence, defineRankIndex, defineRole, defineSchema, defineSchemaExtension, defineShape, defineStorageRule, defineStorageRules, defineTable, defineVectorIndex, httpAction, httpRoute, httpRouter, initLunora, installPlugins, mask, mergeSchemaExtension, onConnect, onDisconnect, presenceExtension, protectPublic, redactSecrets, rls, serveStorageObject, storageRules };
1994
+ export { type ActionBuilder, type ActionCtx, type AggregateIndexDefinition, type AggregateIndexOptions, type AggregateOp, type ArgsValidator, type Component, type ComponentFunctions, type CreateOptions, type DataModelInit, type DefineComponentOptions, type DefineIdentityOptions, type DefinePluginOptions, type DefinePolicyInput, type DefinePresenceOptions, type DefineStorageRuleInput, type DurableObjectJurisdiction, type EmptyArgs, type EnvAccessor, type EnvKeyFailure, type EnvShape, type ExtendableSchema, type FacadeEntry, type FacadeWriterLike, type FunctionKind, type HttpActionCtx, type HttpActionHandler, type HttpMethod, type HttpRoute, type HttpRouteBuilder, type HttpRouteFactory, type HttpRouteHandlerOptions, type HttpStreamHandlerOptions, type IdentityContract, type IdentityRejectMode, type IdentityValidation, type InferArgs, type InferEnv, type InferIdentity, type InlineAggregateIndexOptions, type InlineRankIndexOptions, type InternalActionBuilder, type InternalMutationBuilder, type InternalQueryBuilder, type LifecycleEvent, type LifecycleHandler, type LunoraBuilders, LunoraEnvError, LunoraError, type LunoraHttpApp, type LunoraHttpEnv, type LunoraRouteHandler, type ManyRelation, type MaskColumns, type MaskContext, type MaskFn, type MaskOptions, type MaskPolicies, type MaskStrategy, type Middleware, type MiddlewareNext, type MigrationDefinition, type MigrationDocument, type MigrationTransform, type MutationBuilder, type MutationCtx, type MutatorDefinition, type OnDeleteAction, type OneRelation, type OrmLike, DEFAULT_TTL_MS as PRESENCE_DEFAULT_TTL_MS, PRESENCE_TABLE, type Permission, type Plugin, type Policy, type PrefixedTables, type PresenceComponent, type PresenceFunctions, type PresenceMember, type ProtectPublicOptions, type QueryBuilder, type QueryCtx, type RankIndexDefinition, type RankIndexOptions, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMigration, type RegisteredMutation, type RegisteredMutator, type RegisteredQuery, type RegisteredShape, type RegisteredStream, type RelationBuilder, type RelationDefinition, type RlsOptions, type RlsReadRegistry, type Role, type Schema, type SchemaExtension, type ShapeDefinition, type ShapeReadWhereRequest, type StorageOperation, type StorageRule, type StorageRuleContext, type StorageRuleDecision, type StorageRulesOptions, type TableBuilder, type TableDefinition, type TerminalKind, type TriggerBuilder, type TriggerDefinition, type TypedDefinePolicyInput, VERSION, type VectorEmbedder, type VectorIndexDefinition, type VectorIndexOptions, type VectorMetric, type VectorizeOptions, type WhereInput, asBucketStorage, bindOrm, bindTableFacade, buildRlsReadRegistry, composePluginMiddleware, composeShapeReadWhere, createPolicyDsl, createSecrets, defineAggregateIndex, defineComponent, defineEnv, defineIdentity, defineMigration, defineMutator, definePermission, definePlugin, definePolicies, definePolicy, definePresence, defineRankIndex, defineRole, defineSchema, defineSchemaExtension, defineShape, defineStorageRule, defineStorageRules, defineTable, defineVectorIndex, httpAction, httpRoute, httpRouter, initLunora, installPlugins, isSafeHeaderValue, mask, mergeSchemaExtension, onConnect, onDisconnect, presenceExtension, protectPublic, redactSecrets, rls, serveStorageObject, storageRules };
package/dist/index.d.ts CHANGED
@@ -2,6 +2,8 @@ import { Validator, Infer, ValidatorMap, InferValidatorMap, v } from '@lunora/va
2
2
  export { type ColumnValidator, type Id, type Infer, ValidationError, type Validator, type ValidatorKind, v } from '@lunora/values';
3
3
  import { ArgsValidator, InferArgs, RegisteredAction, ActionCtx, MutationCtx, RegisteredMutation, QueryCtx, RegisteredQuery, RegisteredStream, FunctionKind, Secrets, LifecycleEvent, RegisteredLifecycleHook, TableDefinition, RegisteredFunction, VectorIndexDefinition, Schema, AggregateOp, DurableObjectJurisdiction, RelationDefinition, GlobalBackend, OnDeleteAction, ExternalSourceDefinition, TriggerBuilder, TriggerDefinition, VectorEmbedder, VectorMetric, AggregateIndexDefinition, RankIndexDefinition } from "./types.js";
4
4
  export { type AnyApi, type AuthState, type DatabaseReader, type DatabaseWriter, type FunctionVisibility, type IndexDefinition, type IndexRangeBuilder, type LifecycleEventKind, type LunoraLogger, type PaginationOptions, type PaginationResult, type RankSortKey, type ReadOnlyStorage, type ScheduledFunctionDoc, type ScheduledJob, type Scheduler, type SearchFilterBuilder, type SearchIndexDefinition, type ShardMode, type Storage, type StorageMetadata, type SystemDatabaseReader, type SystemDoc, type SystemQuery, type SystemTableName, type TableReader, type TableVectorIndex, type TriggerAggregateOptions, type TriggerCtx, type TriggerDatabase, type TriggerDeleteEvent, type TriggerEvent, type TriggerGroupByEntry, type TriggerGroupByOptions, type TriggerHandler, type TriggerInsertEvent, type TriggerOp, type TriggerQueryArgs, type TriggerQueryPage, type TriggerRankOptions, type TriggerRankPageOptions, type TriggerRankResult, type TriggerRow, type TriggerTiming, type TriggerUpdateEvent, type VectorMatch, type VectorMatches, type VectorQueryInput, type VectorRecord, type VectorSearch, type VectorSearchReader, type VectorUpsertInput, type WorkflowCreateOptions, type WorkflowHandle, type WorkflowInstance, type WorkflowInstanceStatus, type WorkflowStatusResult, type Workflows, anyApi } from "./types.js";
5
+ import { LunoraError as LunoraError$1, LunoraErrorCode } from '@lunora/errors';
6
+ export type { LunoraErrorCode } from '@lunora/errors';
5
7
  import { Context, Hono } from 'hono';
6
8
  import { b as Permission, R as Role, T as TypedDefinePolicyInput, a as Policy, D as DefinePolicyInput, W as WhereInput, c as RlsOptions } from "./packem_shared/types.d-Cxl6ndhm.js";
7
9
  export type { d as PolicyContext, e as PolicyDecision, f as PolicyDecisionOf, P as PolicyOperation } from "./packem_shared/types.d-Cxl6ndhm.js";
@@ -226,8 +228,7 @@ interface EnvKeyFailure {
226
228
  *
227
229
  * Named export only (no default) per the repo export convention.
228
230
  */
229
- declare class LunoraEnvError extends Error {
230
- override readonly name = "LunoraEnvError";
231
+ declare class LunoraEnvError extends LunoraError$1 {
231
232
  readonly failures: ReadonlyArray<EnvKeyFailure>;
232
233
  constructor(failures: ReadonlyArray<EnvKeyFailure>);
233
234
  }
@@ -277,63 +278,7 @@ interface EnvAccessor<S extends EnvShape> {
277
278
  * invalid — lazily on first access of that key, or eagerly via `config.parse(env)`.
278
279
  */
279
280
  declare const defineEnv: <S extends EnvShape>(shape: S) => EnvAccessor<S>;
280
- /**
281
- * Canonical error type for Lunora procedures and middleware.
282
- *
283
- * The runtime's structural error mapper keys off `name === "LunoraError"` plus
284
- * the numeric `status`, so throwing one of these from a handler or middleware
285
- * yields the right RPC/HTTP status without any further wiring. `code` carries
286
- * the machine-readable reason for clients; the optional `data` carries a
287
- * structured, JSON+wire-encodable payload propagated verbatim to the client
288
- * (e.g. `{ retryAfterMs }`). Only an explicit `LunoraError`'s `data` crosses the
289
- * wire — an unhandled throw is still redacted to a generic message server-side.
290
- */
291
- declare const CODE_STATUS: {
292
- readonly BAD_REQUEST: 400;
293
- readonly CONFLICT: 409;
294
- /**
295
- * `count()` invoked against a table whose context carries an active RLS
296
- * policy. The operation itself is unsupported in an RLS-restricted reader
297
- * (kitcn's documented constraint) — the request is well-formed and the
298
- * caller is authorized, so this is a 422 (semantic conflict) rather than a
299
- * 403 (policy denial).
300
- */
301
- readonly COUNT_RLS_UNSUPPORTED: 422;
302
- readonly FORBIDDEN: 403;
303
- readonly INTERNAL_SERVER_ERROR: 500;
304
- /**
305
- * An analytical reduction (`aggregate` / `groupBy`) was invoked over a
306
- * column that the procedure's `mask()` middleware redacts. A masked column
307
- * can't be summed, averaged, or grouped without leaking the very values the
308
- * mask hides (a group key *is* the raw value; an aggregate is computed from
309
- * it), so the operation fails closed. The request is well-formed and the
310
- * caller is authorized — this is a 422 (semantic conflict), mirroring
311
- * `COUNT_RLS_UNSUPPORTED`.
312
- */
313
- readonly MASK_UNSUPPORTED: 422;
314
- readonly NOT_FOUND: 404;
315
- readonly NOT_IMPLEMENTED: 501;
316
- /**
317
- * A write policy's `when` returned a relation-crossing predicate
318
- * (`some`/`none`/`every`/`is`/`isNot`). The in-memory write-policy evaluator
319
- * has no child fetcher and cannot resolve a relation node, so the policy is
320
- * unsupported as written. Relation predicates are valid in *read* policies
321
- * and query `where` clauses (the pre-resolver handles them there). The
322
- * request is well-formed; this is a 422 (semantic conflict), mirroring the
323
- * sibling `*_UNSUPPORTED` codes.
324
- */
325
- readonly RELATION_PREDICATE_UNSUPPORTED: 422;
326
- readonly TOO_MANY_REQUESTS: 429;
327
- readonly UNAUTHORIZED: 401;
328
- readonly UNPROCESSABLE: 422;
329
- };
330
- type LunoraErrorCode = keyof typeof CODE_STATUS;
331
- declare class LunoraError extends Error {
332
- override readonly name = "LunoraError";
333
- readonly code: LunoraErrorCode;
334
- readonly status: number;
335
- /** Structured, JSON+wire-encodable payload surfaced to the client alongside `code`. */
336
- readonly data?: unknown;
281
+ declare class LunoraError extends LunoraError$1 {
337
282
  constructor(code: LunoraErrorCode, message?: string, data?: unknown);
338
283
  }
339
284
  /**
@@ -659,6 +604,15 @@ interface ContextWithStorage {
659
604
  storage: StorageDownloader;
660
605
  }
661
606
  /**
607
+ * True when `value` is safe to use as an HTTP header field-value: no CR, LF, or
608
+ * NUL. Guards against response-header injection / `Headers`-construction throws
609
+ * when reflecting attacker-influenced object metadata (e.g. a stored
610
+ * `Content-Type`). Exported (see the `export {}` at the file end) so an `httpAction`
611
+ * handler can guard a request-derived header value before writing it — the fix the
612
+ * `http_action_response_header_injection` advisor lint points to.
613
+ */
614
+ declare const isSafeHeaderValue: (value: string) => boolean;
615
+ /**
662
616
  * Stream a stored object as an HTTP {@link Response} from an `httpAction`
663
617
  * handler, with correct `Content-Type`, `ETag`, and `Accept-Ranges: bytes`.
664
618
  * Honors a single-range `Range` request → **206 Partial Content** with
@@ -928,21 +882,6 @@ interface MaskContextIn {
928
882
  * includes this middleware — opt-in, never global (the same invariant as RLS).
929
883
  */
930
884
  declare const mask: <Context extends MaskContextIn = MaskContextIn>(policies: MaskPolicies<Context>, options?: MaskOptions<Context>) => Middleware<Context, Context>;
931
- /**
932
- * Online data-migration authoring API.
933
- *
934
- * `defineMigration` declares a per-document backfill over one table: `up`
935
- * transforms every existing row, `down` (optional) reverses it. Unlike the D1
936
- * SQL schema migrations in `@lunora/d1`, these run *inside each shard's*
937
- * Durable Object against live documents, in keyset batches, and are resumable —
938
- * the per-shard runner in `@lunora/do` tracks progress in a reserved
939
- * `__lunora_migrations` table so an interrupted run picks up where it stopped.
940
- *
941
- * The returned object carries a `__lunoraMigration` brand so codegen can
942
- * discover declarations through the type checker (mirroring the procedure
943
- * builder's `__lunoraProcedure` brand) and emit them into a `LUNORA_MIGRATIONS`
944
- * registry the DO and CLI look migrations up by id.
945
- */
946
885
  /** A document handed to a migration transform: the stored row including `_id`/`_creationTime`. */
947
886
  type MigrationDocument = Record<string, unknown>;
948
887
  /**
@@ -2052,4 +1991,4 @@ interface StorageContextIn {
2052
1991
  }
2053
1992
  declare const storageRules: <Context extends StorageContextIn = StorageContextIn>(rules: ReadonlyArray<StorageRule<Context>>, options?: StorageRulesOptions) => Middleware<Context, Context>;
2054
1993
  declare const VERSION = "0.0.0";
2055
- export { type ActionBuilder, type ActionCtx, type AggregateIndexDefinition, type AggregateIndexOptions, type AggregateOp, type ArgsValidator, type Component, type ComponentFunctions, type CreateOptions, type DataModelInit, type DefineComponentOptions, type DefineIdentityOptions, type DefinePluginOptions, type DefinePolicyInput, type DefinePresenceOptions, type DefineStorageRuleInput, type DurableObjectJurisdiction, type EmptyArgs, type EnvAccessor, type EnvKeyFailure, type EnvShape, type ExtendableSchema, type FacadeEntry, type FacadeWriterLike, type FunctionKind, type HttpActionCtx, type HttpActionHandler, type HttpMethod, type HttpRoute, type HttpRouteBuilder, type HttpRouteFactory, type HttpRouteHandlerOptions, type HttpStreamHandlerOptions, type IdentityContract, type IdentityRejectMode, type IdentityValidation, type InferArgs, type InferEnv, type InferIdentity, type InlineAggregateIndexOptions, type InlineRankIndexOptions, type InternalActionBuilder, type InternalMutationBuilder, type InternalQueryBuilder, type LifecycleEvent, type LifecycleHandler, type LunoraBuilders, LunoraEnvError, LunoraError, type LunoraErrorCode, type LunoraHttpApp, type LunoraHttpEnv, type LunoraRouteHandler, type ManyRelation, type MaskColumns, type MaskContext, type MaskFn, type MaskOptions, type MaskPolicies, type MaskStrategy, type Middleware, type MiddlewareNext, type MigrationDefinition, type MigrationDocument, type MigrationTransform, type MutationBuilder, type MutationCtx, type MutatorDefinition, type OnDeleteAction, type OneRelation, type OrmLike, DEFAULT_TTL_MS as PRESENCE_DEFAULT_TTL_MS, PRESENCE_TABLE, type Permission, type Plugin, type Policy, type PrefixedTables, type PresenceComponent, type PresenceFunctions, type PresenceMember, type ProtectPublicOptions, type QueryBuilder, type QueryCtx, type RankIndexDefinition, type RankIndexOptions, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMigration, type RegisteredMutation, type RegisteredMutator, type RegisteredQuery, type RegisteredShape, type RegisteredStream, type RelationBuilder, type RelationDefinition, type RlsOptions, type RlsReadRegistry, type Role, type Schema, type SchemaExtension, type ShapeDefinition, type ShapeReadWhereRequest, type StorageOperation, type StorageRule, type StorageRuleContext, type StorageRuleDecision, type StorageRulesOptions, type TableBuilder, type TableDefinition, type TerminalKind, type TriggerBuilder, type TriggerDefinition, type TypedDefinePolicyInput, VERSION, type VectorEmbedder, type VectorIndexDefinition, type VectorIndexOptions, type VectorMetric, type VectorizeOptions, type WhereInput, asBucketStorage, bindOrm, bindTableFacade, buildRlsReadRegistry, composePluginMiddleware, composeShapeReadWhere, createPolicyDsl, createSecrets, defineAggregateIndex, defineComponent, defineEnv, defineIdentity, defineMigration, defineMutator, definePermission, definePlugin, definePolicies, definePolicy, definePresence, defineRankIndex, defineRole, defineSchema, defineSchemaExtension, defineShape, defineStorageRule, defineStorageRules, defineTable, defineVectorIndex, httpAction, httpRoute, httpRouter, initLunora, installPlugins, mask, mergeSchemaExtension, onConnect, onDisconnect, presenceExtension, protectPublic, redactSecrets, rls, serveStorageObject, storageRules };
1994
+ export { type ActionBuilder, type ActionCtx, type AggregateIndexDefinition, type AggregateIndexOptions, type AggregateOp, type ArgsValidator, type Component, type ComponentFunctions, type CreateOptions, type DataModelInit, type DefineComponentOptions, type DefineIdentityOptions, type DefinePluginOptions, type DefinePolicyInput, type DefinePresenceOptions, type DefineStorageRuleInput, type DurableObjectJurisdiction, type EmptyArgs, type EnvAccessor, type EnvKeyFailure, type EnvShape, type ExtendableSchema, type FacadeEntry, type FacadeWriterLike, type FunctionKind, type HttpActionCtx, type HttpActionHandler, type HttpMethod, type HttpRoute, type HttpRouteBuilder, type HttpRouteFactory, type HttpRouteHandlerOptions, type HttpStreamHandlerOptions, type IdentityContract, type IdentityRejectMode, type IdentityValidation, type InferArgs, type InferEnv, type InferIdentity, type InlineAggregateIndexOptions, type InlineRankIndexOptions, type InternalActionBuilder, type InternalMutationBuilder, type InternalQueryBuilder, type LifecycleEvent, type LifecycleHandler, type LunoraBuilders, LunoraEnvError, LunoraError, type LunoraHttpApp, type LunoraHttpEnv, type LunoraRouteHandler, type ManyRelation, type MaskColumns, type MaskContext, type MaskFn, type MaskOptions, type MaskPolicies, type MaskStrategy, type Middleware, type MiddlewareNext, type MigrationDefinition, type MigrationDocument, type MigrationTransform, type MutationBuilder, type MutationCtx, type MutatorDefinition, type OnDeleteAction, type OneRelation, type OrmLike, DEFAULT_TTL_MS as PRESENCE_DEFAULT_TTL_MS, PRESENCE_TABLE, type Permission, type Plugin, type Policy, type PrefixedTables, type PresenceComponent, type PresenceFunctions, type PresenceMember, type ProtectPublicOptions, type QueryBuilder, type QueryCtx, type RankIndexDefinition, type RankIndexOptions, type RegisteredAction, type RegisteredFunction, type RegisteredLifecycleHook, type RegisteredMigration, type RegisteredMutation, type RegisteredMutator, type RegisteredQuery, type RegisteredShape, type RegisteredStream, type RelationBuilder, type RelationDefinition, type RlsOptions, type RlsReadRegistry, type Role, type Schema, type SchemaExtension, type ShapeDefinition, type ShapeReadWhereRequest, type StorageOperation, type StorageRule, type StorageRuleContext, type StorageRuleDecision, type StorageRulesOptions, type TableBuilder, type TableDefinition, type TerminalKind, type TriggerBuilder, type TriggerDefinition, type TypedDefinePolicyInput, VERSION, type VectorEmbedder, type VectorIndexDefinition, type VectorIndexOptions, type VectorMetric, type VectorizeOptions, type WhereInput, asBucketStorage, bindOrm, bindTableFacade, buildRlsReadRegistry, composePluginMiddleware, composeShapeReadWhere, createPolicyDsl, createSecrets, defineAggregateIndex, defineComponent, defineEnv, defineIdentity, defineMigration, defineMutator, definePermission, definePlugin, definePolicies, definePolicy, definePresence, defineRankIndex, defineRole, defineSchema, defineSchemaExtension, defineShape, defineStorageRule, defineStorageRules, defineTable, defineVectorIndex, httpAction, httpRoute, httpRouter, initLunora, installPlugins, isSafeHeaderValue, mask, mergeSchemaExtension, onConnect, onDisconnect, presenceExtension, protectPublic, redactSecrets, rls, serveStorageObject, storageRules };
package/dist/index.mjs CHANGED
@@ -1,28 +1,28 @@
1
1
  export { default as asBucketStorage } from './packem_shared/asBucketStorage-Cnxd9y2q.mjs';
2
- export { initLunora } from './packem_shared/initLunora-lxwHTEV3.mjs';
3
- export { createSecrets } from './packem_shared/createSecrets-TsIP9lOa.mjs';
4
- export { LunoraEnvError, defineEnv, redactSecrets } from './packem_shared/LunoraEnvError-DjFkpkSP.mjs';
5
- export { LunoraError } from './packem_shared/LunoraError-DN7Zhhvu.mjs';
6
- export { bindOrm, bindTableFacade } from './packem_shared/bindOrm-Ce57S3N9.mjs';
7
- export { httpAction, httpRoute, httpRouter, serveStorageObject } from './packem_shared/httpAction-FLwfsePg.mjs';
8
- export { defineIdentity } from './packem_shared/defineIdentity-B_9YD46A.mjs';
2
+ export { initLunora } from './packem_shared/initLunora-sQUqaejx.mjs';
3
+ export { createSecrets } from './packem_shared/createSecrets-DwaR2rNG.mjs';
4
+ export { LunoraEnvError, defineEnv, redactSecrets } from './packem_shared/LunoraEnvError-BGmd1Qs0.mjs';
5
+ export { LunoraError } from './packem_shared/LunoraError-WbxmrpxR.mjs';
6
+ export { bindOrm, bindTableFacade } from './packem_shared/bindOrm-CNXKgfUa.mjs';
7
+ export { httpAction, httpRoute, httpRouter, isSafeHeaderValue, serveStorageObject } from './packem_shared/httpAction-B_16WzMO.mjs';
8
+ export { defineIdentity } from './packem_shared/defineIdentity-DiX4zM9x.mjs';
9
9
  export { onConnect, onDisconnect } from './packem_shared/onConnect-CIPXKPyw.mjs';
10
- export { defineMigration } from './packem_shared/defineMigration-CAJLr6fx.mjs';
10
+ export { defineMigration } from './packem_shared/defineMigration-Hx01yIht.mjs';
11
11
  export { defineMutator } from './packem_shared/defineMutator-EIXAWhs9.mjs';
12
- export { composePluginMiddleware, defineComponent, definePlugin, defineSchemaExtension, installPlugins, mergeSchemaExtension } from './packem_shared/composePluginMiddleware-Ck5_TUO8.mjs';
13
- export { PRESENCE_DEFAULT_TTL_MS, PRESENCE_TABLE, definePresence, presenceExtension } from './packem_shared/PRESENCE_DEFAULT_TTL_MS-D8viLY1S.mjs';
14
- export { protectPublic } from './packem_shared/protectPublic-BjFkQ_Or.mjs';
15
- export { defineAggregateIndex, defineRankIndex, defineSchema, defineTable, defineVectorIndex } from './packem_shared/defineAggregateIndex-ZdyU78gh.mjs';
16
- export { defineShape } from './packem_shared/defineShape-CJ27Wx7o.mjs';
12
+ export { composePluginMiddleware, defineComponent, definePlugin, defineSchemaExtension, installPlugins, mergeSchemaExtension } from './packem_shared/composePluginMiddleware-CcTj1_v2.mjs';
13
+ export { PRESENCE_DEFAULT_TTL_MS, PRESENCE_TABLE, definePresence, presenceExtension } from './packem_shared/PRESENCE_DEFAULT_TTL_MS-BtPCiNLW.mjs';
14
+ export { protectPublic } from './packem_shared/protectPublic-BlcGpiRc.mjs';
15
+ export { defineAggregateIndex, defineRankIndex, defineSchema, defineTable, defineVectorIndex } from './packem_shared/defineAggregateIndex-znq4ygKQ.mjs';
16
+ export { defineShape } from './packem_shared/defineShape-C5scNOrf.mjs';
17
17
  export { anyApi } from './types.mjs';
18
18
  export { cronJobs } from '@lunora/scheduler';
19
19
  export { ValidationError, v } from '@lunora/values';
20
- export { buildRlsReadRegistry, composeShapeReadWhere } from './packem_shared/buildRlsReadRegistry-Do1CSBTr.mjs';
21
- export { createPolicyDsl, definePermission, definePolicies, definePolicy, defineRole } from './packem_shared/createPolicyDsl-De67zPDS.mjs';
22
- export { defineStorageRule, defineStorageRules } from './packem_shared/defineStorageRule-qu0mpilX.mjs';
23
- export { mask } from './packem_shared/mask-E8MgAS3N.mjs';
24
- export { rls } from './packem_shared/rls-DhNgKFeW.mjs';
25
- export { storageRules } from './packem_shared/storageRules-Cje6Woea.mjs';
20
+ export { buildRlsReadRegistry, composeShapeReadWhere } from './packem_shared/buildRlsReadRegistry-DFBFLydB.mjs';
21
+ export { createPolicyDsl, definePermission, definePolicies, definePolicy, defineRole } from './packem_shared/createPolicyDsl-By3QB4he.mjs';
22
+ export { defineStorageRule, defineStorageRules } from './packem_shared/defineStorageRule-B5nL4Z1P.mjs';
23
+ export { mask } from './packem_shared/mask-Dc8G5Gl7.mjs';
24
+ export { rls } from './packem_shared/rls-3sCJIH6F.mjs';
25
+ export { storageRules } from './packem_shared/storageRules-6QxzDOcx.mjs';
26
26
 
27
27
  const VERSION = "0.0.0";
28
28
 
@@ -1,3 +1,4 @@
1
+ import { LunoraError as LunoraError$1 } from '@lunora/errors';
1
2
  import { optionalInner } from '@lunora/values';
2
3
 
3
4
  const SECRET_VALUE_PREFIXES = [
@@ -53,13 +54,12 @@ const redactValueForKey = (message, key, raw) => {
53
54
  }
54
55
  return masked;
55
56
  };
56
- class LunoraEnvError extends Error {
57
- name = "LunoraEnvError";
57
+ class LunoraEnvError extends LunoraError$1 {
58
58
  failures;
59
59
  constructor(failures) {
60
60
  const summary = failures.map((failure) => ` - ${failure.key}: ${failure.message}`).join("\n");
61
- super(`Invalid environment (${String(failures.length)} key(s)):
62
- ${summary}`);
61
+ super("ENV_INVALID", `Invalid environment (${String(failures.length)} key(s)):
62
+ ${summary}`, { name: "LunoraEnvError" });
63
63
  this.failures = failures;
64
64
  }
65
65
  }
@@ -0,0 +1,9 @@
1
+ import { LunoraError as LunoraError$1 } from '@lunora/errors';
2
+
3
+ class LunoraError extends LunoraError$1 {
4
+ constructor(code, message, data) {
5
+ super(code, message, { data });
6
+ }
7
+ }
8
+
9
+ export { LunoraError };
@@ -1,9 +1,9 @@
1
1
  import { v } from '@lunora/values';
2
- import { initLunora } from './initLunora-lxwHTEV3.mjs';
3
- import { LunoraError } from './LunoraError-DN7Zhhvu.mjs';
2
+ import { initLunora } from './initLunora-sQUqaejx.mjs';
3
+ import { LunoraError } from './LunoraError-WbxmrpxR.mjs';
4
4
  import { onDisconnect } from './onConnect-CIPXKPyw.mjs';
5
- import { defineSchemaExtension, defineComponent } from './composePluginMiddleware-Ck5_TUO8.mjs';
6
- import { defineTable } from './defineAggregateIndex-ZdyU78gh.mjs';
5
+ import { defineSchemaExtension, defineComponent } from './composePluginMiddleware-CcTj1_v2.mjs';
6
+ import { defineTable } from './defineAggregateIndex-znq4ygKQ.mjs';
7
7
 
8
8
  const DEFAULT_TTL_MS = 3e4;
9
9
  const MAX_DATA_BYTES = 4096;
@@ -1,13 +1,15 @@
1
+ import { LunoraError as LunoraError$1 } from '@lunora/errors';
2
+
1
3
  const isUniqueConflict = (error) => typeof error === "object" && error !== null && error.code === "CONFLICT" && error.kind === "unique";
2
4
  const buildUpsertWhere = (tableName, target, create) => {
3
5
  const fields = typeof target === "string" ? [target] : target;
4
6
  if (fields.length === 0) {
5
- throw new Error(`ctx.db.${tableName}.upsert: "target" must name at least one field`);
7
+ throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.upsert: "target" must name at least one field`);
6
8
  }
7
9
  const where = {};
8
10
  for (const field of fields) {
9
11
  if (!(field in create)) {
10
- throw new Error(`ctx.db.${tableName}.upsert: target field "${field}" is missing from the create document`);
12
+ throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.upsert: target field "${field}" is missing from the create document`);
11
13
  }
12
14
  where[field] = create[field];
13
15
  }
@@ -48,7 +50,7 @@ const bindTableFacade = (writer, tableName) => {
48
50
  // the writer's `{ id, patch }` shape.
49
51
  deleteMany: (ids, options) => {
50
52
  if (writer.deleteMany === void 0) {
51
- throw new Error(`ctx.db.${tableName}.deleteMany is unavailable: this writer has no batch delete`);
53
+ throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.deleteMany is unavailable: this writer has no batch delete`);
52
54
  }
53
55
  return writer.deleteMany(ids, options, tableName);
54
56
  },
@@ -65,14 +67,14 @@ const bindTableFacade = (writer, tableName) => {
65
67
  insert,
66
68
  insertMany: (documents, options) => {
67
69
  if (writer.insertMany === void 0) {
68
- throw new Error(`ctx.db.${tableName}.insertMany is unavailable: this writer has no batch insert`);
70
+ throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.insertMany is unavailable: this writer has no batch insert`);
69
71
  }
70
72
  return writer.insertMany(tableName, documents, options);
71
73
  },
72
74
  patch: (id, patch) => writer.patch(id, patch, tableName),
73
75
  patchMany: (patches, options) => {
74
76
  if (writer.patchMany === void 0) {
75
- throw new Error(`ctx.db.${tableName}.patchMany is unavailable: this writer has no batch patch`);
77
+ throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.patchMany is unavailable: this writer has no batch patch`);
76
78
  }
77
79
  return writer.patchMany(
78
80
  patches.map((entry) => {
@@ -87,7 +89,7 @@ const bindTableFacade = (writer, tableName) => {
87
89
  replace: (id, document) => writer.replace(id, document, tableName),
88
90
  restore: async (id) => {
89
91
  if (!writer.restore) {
90
- throw new Error(`ctx.db.${tableName}.restore is unavailable: this writer has no restore (is the table .softDelete()?)`);
92
+ throw new LunoraError$1("INTERNAL", `ctx.db.${tableName}.restore is unavailable: this writer has no restore (is the table .softDelete()?)`);
91
93
  }
92
94
  await writer.restore(id, tableName);
93
95
  },
@@ -106,7 +108,7 @@ const bindOrm = (facade) => {
106
108
  const resolve = (table) => {
107
109
  const bound = facade[table];
108
110
  if (!bound) {
109
- throw new Error(`unknown table: ${table}`);
111
+ throw new LunoraError$1("INTERNAL", `unknown table: ${table}`);
110
112
  }
111
113
  return bound;
112
114
  };
@@ -1,4 +1,4 @@
1
- import { indexRolePermissions, computeReadBaseWhere, permissionName } from './rls-DhNgKFeW.mjs';
1
+ import { indexRolePermissions, computeReadBaseWhere, permissionName } from './rls-3sCJIH6F.mjs';
2
2
  import { r as readRlsTag } from './policy-tag-DvpVH2tv.mjs';
3
3
 
4
4
  const FALSE_PREDICATE = { OR: [] };
@@ -1,4 +1,5 @@
1
- import { r as runMiddlewareChain } from './run-middleware-CYQOuoV6.mjs';
1
+ import { LunoraError as LunoraError$1 } from '@lunora/errors';
2
+ import { r as runMiddlewareChain } from './run-middleware-I6EiQfxL.mjs';
2
3
 
3
4
  const prefixTableName = (key, bareName) => `${key}_${bareName}`;
4
5
  const rewriteReference = (target, key, bareNames) => bareNames.has(target) ? prefixTableName(key, target) : target;
@@ -19,7 +20,7 @@ const rewriteTableReferences = (table, key, bareNames, ownBareName) => {
19
20
  };
20
21
  const defineSchemaExtension = (key, options) => {
21
22
  if (!key) {
22
- throw new Error("defineSchemaExtension: `key` is required and must be a non-empty string");
23
+ throw new LunoraError$1("INTERNAL", "defineSchemaExtension: `key` is required and must be a non-empty string");
23
24
  }
24
25
  return {
25
26
  key,
@@ -29,10 +30,10 @@ const defineSchemaExtension = (key, options) => {
29
30
  };
30
31
  const definePlugin = (key, options) => {
31
32
  if (!key) {
32
- throw new Error("definePlugin: `key` is required and must be a non-empty string");
33
+ throw new LunoraError$1("INTERNAL", "definePlugin: `key` is required and must be a non-empty string");
33
34
  }
34
35
  if (options.extension && options.extension.key !== key) {
35
- throw new Error(`definePlugin("${key}"): extension key "${options.extension.key}" does not match plugin key`);
36
+ throw new LunoraError$1("INTERNAL", `definePlugin("${key}"): extension key "${options.extension.key}" does not match plugin key`);
36
37
  }
37
38
  return {
38
39
  key,
@@ -57,7 +58,8 @@ const mergeSchemaExtension = (base, extension) => {
57
58
  for (const [bareName, table] of Object.entries(extension.tables)) {
58
59
  const prefixed = prefixTableName(key, bareName);
59
60
  if (Object.hasOwn(merged, prefixed)) {
60
- throw new Error(
61
+ throw new LunoraError$1(
62
+ "INTERNAL",
61
63
  `defineSchema(...).extend("${key}"): table "${prefixed}" already exists in the base schema — another extension with the same key already contributed it`
62
64
  );
63
65
  }
@@ -68,7 +70,8 @@ const mergeSchemaExtension = (base, extension) => {
68
70
  for (const [bareIndexName, index] of Object.entries(extension.vectorIndexes)) {
69
71
  const prefixed = prefixTableName(key, bareIndexName);
70
72
  if (Object.hasOwn(mergedVectorIndexes, prefixed)) {
71
- throw new Error(
73
+ throw new LunoraError$1(
74
+ "INTERNAL",
72
75
  `defineSchema(...).extend("${key}"): vector index "${prefixed}" already exists in the base schema — another extension with the same key already contributed it`
73
76
  );
74
77
  }
@@ -1,3 +1,5 @@
1
+ import { LunoraError as LunoraError$1 } from '@lunora/errors';
2
+
1
3
  const definePolicy = (input) => {
2
4
  return { on: input.on, table: input.table, when: input.when };
3
5
  };
@@ -13,7 +15,8 @@ const definePolicies = (policies) => {
13
15
  const key = JSON.stringify([policy.table, policy.on]);
14
16
  const whens = seenWhenByKey.get(key) ?? /* @__PURE__ */ new Set();
15
17
  if (whens.has(policy.when)) {
16
- throw new Error(
18
+ throw new LunoraError$1(
19
+ "INTERNAL",
17
20
  `definePolicies: duplicate policy for (table "${policy.table}", on "${policy.on}") — the same decision function is registered more than once. Multiple distinct policies per (table, on) are allowed (reads OR, writes AND); remove the duplicate.`
18
21
  );
19
22
  }
@@ -1,3 +1,5 @@
1
+ import { LunoraError as LunoraError$1 } from '@lunora/errors';
2
+
1
3
  const NON_SECRET_BINDING_METHODS = [
2
4
  "put",
3
5
  // KV / R2
@@ -43,7 +45,8 @@ const createSecrets = (env) => {
43
45
  get: async (name) => {
44
46
  const binding = env[name];
45
47
  if (!isSecretBinding(binding)) {
46
- throw new Error(
48
+ throw new LunoraError$1(
49
+ "INTERNAL",
47
50
  `ctx.secrets: no Secrets Store binding named "${name}". Add a \`secrets_store_secrets[]\` entry (binding "${name}", pointing at your store + secret) to wrangler.jsonc — \`ctx.secrets\` reads a Secrets Store binding, not a plain \`.dev.vars\` value.`
48
51
  );
49
52
  }
@@ -1,5 +1,6 @@
1
+ import { LunoraError as LunoraError$1 } from '@lunora/errors';
1
2
  import { isOrWrapsFromValidator, v } from '@lunora/values';
2
- import { mergeSchemaExtension } from './composePluginMiddleware-Ck5_TUO8.mjs';
3
+ import { mergeSchemaExtension } from './composePluginMiddleware-CcTj1_v2.mjs';
3
4
 
4
5
  const relationBuilder = {
5
6
  many: (table, options) => {
@@ -26,7 +27,7 @@ const defineTable = (inputShape) => {
26
27
  const shape = { ...inputShape };
27
28
  for (const [columnName, validator] of Object.entries(shape)) {
28
29
  if (isOrWrapsFromValidator(validator)) {
29
- throw new Error(`defineTable: column "${columnName}" uses v.from() which is args-only — table columns need a concrete v.* type`);
30
+ throw new LunoraError$1("INTERNAL", `defineTable: column "${columnName}" uses v.from() which is args-only — table columns need a concrete v.* type`);
30
31
  }
31
32
  }
32
33
  const aggregateIndexes = [];
@@ -46,7 +47,7 @@ const defineTable = (inputShape) => {
46
47
  aggregateIndex(name, options) {
47
48
  const op = options?.op ?? "count";
48
49
  if (op !== "count" && !options?.field) {
49
- throw new Error(`aggregateIndex "${name}": op "${op}" requires a "field"`);
50
+ throw new LunoraError$1("INTERNAL", `aggregateIndex "${name}": op "${op}" requires a "field"`);
50
51
  }
51
52
  aggregateIndexes.push({
52
53
  by: options?.by,
@@ -94,7 +95,7 @@ const defineTable = (inputShape) => {
94
95
  },
95
96
  rankIndex(name, options) {
96
97
  if (!options.sortBy || options.sortBy.length === 0) {
97
- throw new Error(`rankIndex "${name}": "sortBy" is required and must list at least one key`);
98
+ throw new LunoraError$1("INTERNAL", `rankIndex "${name}": "sortBy" is required and must list at least one key`);
98
99
  }
99
100
  const sortBy = options.sortBy.map((key) => {
100
101
  return {
@@ -143,10 +144,10 @@ const defineTable = (inputShape) => {
143
144
  },
144
145
  source(definition) {
145
146
  if (!definition.binding) {
146
- throw new Error("source: `binding` is required (the wrangler Hyperdrive binding name)");
147
+ throw new LunoraError$1("INTERNAL", "source: `binding` is required (the wrangler Hyperdrive binding name)");
147
148
  }
148
149
  if (!definition.query) {
149
- throw new Error("source: `query` is required (the tenant-membership SQL)");
150
+ throw new LunoraError$1("INTERNAL", "source: `query` is required (the tenant-membership SQL)");
150
151
  }
151
152
  externalSource = definition;
152
153
  isExternallyManaged = true;
@@ -198,13 +199,13 @@ const defineVectorIndex = (options) => {
198
199
  const defineAggregateIndex = (name, options) => {
199
200
  const op = options.op ?? "count";
200
201
  if (op !== "count" && !options.field) {
201
- throw new Error(`aggregateIndex "${name}": op "${op}" requires a "field"`);
202
+ throw new LunoraError$1("INTERNAL", `aggregateIndex "${name}": op "${op}" requires a "field"`);
202
203
  }
203
204
  return { by: options.by, field: options.field, name, on: options.on, op, where: options.where };
204
205
  };
205
206
  const defineRankIndex = (name, options) => {
206
207
  if (!options.sortBy || options.sortBy.length === 0) {
207
- throw new Error(`rankIndex "${name}": "sortBy" is required and must list at least one key`);
208
+ throw new LunoraError$1("INTERNAL", `rankIndex "${name}": "sortBy" is required and must list at least one key`);
208
209
  }
209
210
  const sortBy = options.sortBy.map((key) => {
210
211
  return {
@@ -246,14 +247,14 @@ const attachStandaloneIndexes = (tables, aggregateIndexes, rankIndexes) => {
246
247
  for (const index of Object.values(aggregateIndexes)) {
247
248
  const table = tables[index.on];
248
249
  if (!table) {
249
- throw new Error(`defineAggregateIndex "${index.name}": unknown table "${index.on}"`);
250
+ throw new LunoraError$1("INTERNAL", `defineAggregateIndex "${index.name}": unknown table "${index.on}"`);
250
251
  }
251
252
  table.aggregateIndexes.push(index);
252
253
  }
253
254
  for (const index of Object.values(rankIndexes)) {
254
255
  const table = tables[index.on];
255
256
  if (!table) {
256
- throw new Error(`defineRankIndex "${index.name}": unknown table "${index.on}"`);
257
+ throw new LunoraError$1("INTERNAL", `defineRankIndex "${index.name}": unknown table "${index.on}"`);
257
258
  }
258
259
  table.rankIndexes.push(index);
259
260
  }
@@ -265,17 +266,20 @@ const validateExternalSources = (tables) => {
265
266
  continue;
266
267
  }
267
268
  if (table.shardMode.kind === "global") {
268
- throw new Error(
269
+ throw new LunoraError$1(
270
+ "INTERNAL",
269
271
  `defineSchema: table "${name}" cannot be both .source() and .global() — a sourced table materializes into a shard DO's SQLite, a global table lives in the external tier`
270
272
  );
271
273
  }
272
274
  if (table.shardMode.kind === "shardBy" && !source.tenantBy) {
273
- throw new Error(
275
+ throw new LunoraError$1(
276
+ "INTERNAL",
274
277
  `defineSchema: sourced + .shardBy() table "${name}" 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.`
275
278
  );
276
279
  }
277
280
  if (source.mode === "incremental") {
278
- throw new Error(
281
+ throw new LunoraError$1(
282
+ "INTERNAL",
279
283
  `defineSchema: table "${name}" uses \`mode: "incremental"\`, which is not yet implemented — only "full-pull" (the default) is supported. Remove \`mode\` or set it to "full-pull".`
280
284
  );
281
285
  }
@@ -1,3 +1,4 @@
1
+ import { LunoraError as LunoraError$1 } from '@lunora/errors';
1
2
  import { parseValidatorMap, ValidationError } from '@lunora/values';
2
3
 
3
4
  const NON_STRING_USER_ID_KINDS = /* @__PURE__ */ new Set([
@@ -17,7 +18,7 @@ const defineIdentity = (claims, options = {}) => {
17
18
  const map = claims;
18
19
  const userIdValidator = map["userId"];
19
20
  if (userIdValidator === void 0 || NON_STRING_USER_ID_KINDS.has(userIdValidator.kind)) {
20
- throw new Error("defineIdentity: the claim map must declare a required string `userId` validator (e.g. `v.string()` or `v.id(...)`)");
21
+ throw new LunoraError$1("INTERNAL", "defineIdentity: the claim map must declare a required string `userId` validator (e.g. `v.string()` or `v.id(...)`)");
21
22
  }
22
23
  const onInvalid = options.onInvalid ?? "anonymous";
23
24
  const validate = (identity) => {
@@ -1,6 +1,8 @@
1
+ import { LunoraError as LunoraError$1 } from '@lunora/errors';
2
+
1
3
  const defineMigration = (definition) => {
2
4
  if (definition.id.trim() === "") {
3
- throw new Error("defineMigration: `id` must be a non-empty string");
5
+ throw new LunoraError$1("INTERNAL", "defineMigration: `id` must be a non-empty string");
4
6
  }
5
7
  return { __lunoraMigration: true, ...definition };
6
8
  };
@@ -1,11 +1,12 @@
1
+ import { LunoraError as LunoraError$1 } from '@lunora/errors';
1
2
  import { v as validateArgs } from './functions-Di9FUNkf.mjs';
2
3
 
3
4
  const defineShape = (definition) => {
4
5
  if (definition.table.trim() === "") {
5
- throw new Error("defineShape: `table` must be a non-empty string");
6
+ throw new LunoraError$1("INTERNAL", "defineShape: `table` must be a non-empty string");
6
7
  }
7
8
  if (definition.columns?.length === 0) {
8
- throw new Error("defineShape: `columns` must list at least one column when provided");
9
+ throw new LunoraError$1("INTERNAL", "defineShape: `columns` must list at least one column when provided");
9
10
  }
10
11
  const compileWhere = (context, rawArgs) => {
11
12
  const parsed = validateArgs(definition.args ?? {}, rawArgs);
@@ -1,3 +1,5 @@
1
+ import { LunoraError as LunoraError$1 } from '@lunora/errors';
2
+
1
3
  const defineStorageRule = (input) => {
2
4
  return { bucket: input.bucket, on: input.on, prefix: input.prefix, when: input.when };
3
5
  };
@@ -7,7 +9,8 @@ const defineStorageRules = (rules) => {
7
9
  const key = JSON.stringify([rule.bucket, rule.on, rule.prefix]);
8
10
  const whens = seenWhenByKey.get(key) ?? /* @__PURE__ */ new Set();
9
11
  if (whens.has(rule.when)) {
10
- throw new Error(
12
+ throw new LunoraError$1(
13
+ "INTERNAL",
11
14
  `defineStorageRules: duplicate rule for (bucket "${rule.bucket}", on "${rule.on}"${rule.prefix === void 0 ? "" : `, prefix "${rule.prefix}"`}) — the same decision function is registered more than once. Multiple distinct rules per (bucket, on) are allowed (they OR); remove the duplicate.`
12
15
  );
13
16
  }
@@ -1,6 +1,7 @@
1
+ import { toErrorBody } from '@lunora/errors';
1
2
  import { parseValidatorMap, ValidationError } from '@lunora/values';
2
3
  import { Hono } from 'hono';
3
- import { LunoraError } from './LunoraError-DN7Zhhvu.mjs';
4
+ import { LunoraError } from './LunoraError-WbxmrpxR.mjs';
4
5
 
5
6
  const httpAction = (handler) => async (c) => handler(c.get("lunora"), c.req.raw);
6
7
  const httpRouter = () => {
@@ -126,13 +127,6 @@ const buildRouteHandler = (state, userHandler) => async (c) => {
126
127
  return errorResponse(error);
127
128
  }
128
129
  };
129
- const isLunoraErrorLike = (error) => {
130
- if (!error || typeof error !== "object") {
131
- return false;
132
- }
133
- const candidate = error;
134
- return candidate.name === "LunoraError" && typeof candidate.code === "string" && typeof candidate.message === "string";
135
- };
136
130
  const sseFrame = (chunk, event) => {
137
131
  const data = JSON.stringify(chunk);
138
132
  const prefix = event ? `event: ${event}
@@ -193,14 +187,11 @@ const buildStreamHandler = (state, userHandler) => (
193
187
  }
194
188
  controller.enqueue(encoder.encode(sseFrame({}, "complete")));
195
189
  } catch (error) {
196
- let payload;
197
- if (isLunoraErrorLike(error)) {
198
- payload = { code: error.code, message: error.message };
199
- } else {
190
+ const { body, redacted } = toErrorBody(error, { fallbackCode: "INTERNAL_SERVER_ERROR", redactedMessage: "Internal error" });
191
+ if (redacted) {
200
192
  console.error("[lunora] unhandled stream handler error:", error);
201
- payload = { code: "INTERNAL_SERVER_ERROR", message: "Internal error" };
202
193
  }
203
- controller.enqueue(encoder.encode(sseFrame(payload, "error")));
194
+ controller.enqueue(encoder.encode(sseFrame({ code: body.code, message: body.message }, "error")));
204
195
  } finally {
205
196
  request.signal.removeEventListener("abort", onAbort);
206
197
  controller.close();
@@ -337,4 +328,4 @@ const serveStorageObject = async (context, key, request) => {
337
328
  });
338
329
  };
339
330
 
340
- export { httpAction, httpRoute, httpRouter, serveStorageObject };
331
+ export { httpAction, httpRoute, httpRouter, isSafeHeaderValue, serveStorageObject };
@@ -1,6 +1,6 @@
1
1
  import { v as validateArgs } from './functions-Di9FUNkf.mjs';
2
2
  import { r as readRlsTag } from './policy-tag-DvpVH2tv.mjs';
3
- import { r as runMiddlewareChain } from './run-middleware-CYQOuoV6.mjs';
3
+ import { r as runMiddlewareChain } from './run-middleware-I6EiQfxL.mjs';
4
4
 
5
5
  const runMiddleware = (middlewares, baseContext) => runMiddlewareChain(middlewares, baseContext, (context) => context);
6
6
  const makeHandler = (args, middlewares, userHandler, output) => async (context, rawArgs) => {
@@ -1,5 +1,5 @@
1
- import { LunoraError } from './LunoraError-DN7Zhhvu.mjs';
2
- import { bindTableFacade, bindOrm } from './bindOrm-Ce57S3N9.mjs';
1
+ import { LunoraError } from './LunoraError-WbxmrpxR.mjs';
2
+ import { bindTableFacade, bindOrm } from './bindOrm-CNXKgfUa.mjs';
3
3
 
4
4
  const permissionName = (permission) => typeof permission === "string" ? permission : permission.name;
5
5
  const indexRolePermissions = (roles) => {
@@ -1,4 +1,4 @@
1
- import { r as runMiddlewareChain } from './run-middleware-CYQOuoV6.mjs';
1
+ import { r as runMiddlewareChain } from './run-middleware-I6EiQfxL.mjs';
2
2
 
3
3
  const protectPublic = (options) => {
4
4
  const chain = [options.rateLimit, options.captcha, ...options.use ?? []].filter(
@@ -1,5 +1,5 @@
1
- import { LunoraError } from './LunoraError-DN7Zhhvu.mjs';
2
- import { bindTableFacade, bindOrm } from './bindOrm-Ce57S3N9.mjs';
1
+ import { LunoraError } from './LunoraError-WbxmrpxR.mjs';
2
+ import { bindTableFacade, bindOrm } from './bindOrm-CNXKgfUa.mjs';
3
3
  import { t as tagRlsMiddleware } from './policy-tag-DvpVH2tv.mjs';
4
4
 
5
5
  const DEFAULT_BATCH_LIMIT = 500;
@@ -1,8 +1,10 @@
1
+ import { LunoraError as LunoraError$1 } from '@lunora/errors';
2
+
1
3
  const runMiddlewareChain = async (middlewares, baseContext, terminal) => {
2
4
  let lastIndex = -1;
3
5
  const dispatch = async (index, context) => {
4
6
  if (index <= lastIndex) {
5
- throw new Error("middleware next() called multiple times");
7
+ throw new LunoraError$1("INTERNAL", "middleware next() called multiple times");
6
8
  }
7
9
  lastIndex = index;
8
10
  const middleware = middlewares[index];
@@ -1,4 +1,4 @@
1
- import { LunoraError } from './LunoraError-DN7Zhhvu.mjs';
1
+ import { LunoraError } from './LunoraError-WbxmrpxR.mjs';
2
2
 
3
3
  const permissionName = (permission) => typeof permission === "string" ? permission : permission.name;
4
4
  const indexRolePermissions = (roles = []) => {
@@ -1,4 +1,4 @@
1
- import { indexRolePermissions, computeReadBaseWhere, matchesWhere, evaluateWrite, permissionName } from '../packem_shared/rls-DhNgKFeW.mjs';
1
+ import { indexRolePermissions, computeReadBaseWhere, matchesWhere, evaluateWrite, permissionName } from '../packem_shared/rls-3sCJIH6F.mjs';
2
2
 
3
3
  const expectPolicy = (policies, options = {}) => {
4
4
  const rolePermissions = indexRolePermissions(options.roles);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/server",
3
- "version": "1.0.0-alpha.13",
3
+ "version": "1.0.0-alpha.15",
4
4
  "description": "Server primitives for Lunora: defineSchema, defineTable, query, mutation, and action",
5
5
  "keywords": [
6
6
  "backend",
@@ -62,8 +62,9 @@
62
62
  "access": "public"
63
63
  },
64
64
  "dependencies": {
65
- "@lunora/scheduler": "1.0.0-alpha.4",
66
- "@lunora/values": "1.0.0-alpha.3",
65
+ "@lunora/errors": "1.0.0-alpha.1",
66
+ "@lunora/scheduler": "1.0.0-alpha.5",
67
+ "@lunora/values": "1.0.0-alpha.4",
67
68
  "drizzle-orm": "^0.45.2",
68
69
  "hono": "^4.12.27"
69
70
  },
@@ -1,54 +0,0 @@
1
- const CODE_STATUS = {
2
- BAD_REQUEST: 400,
3
- CONFLICT: 409,
4
- /**
5
- * `count()` invoked against a table whose context carries an active RLS
6
- * policy. The operation itself is unsupported in an RLS-restricted reader
7
- * (kitcn's documented constraint) — the request is well-formed and the
8
- * caller is authorized, so this is a 422 (semantic conflict) rather than a
9
- * 403 (policy denial).
10
- */
11
- COUNT_RLS_UNSUPPORTED: 422,
12
- FORBIDDEN: 403,
13
- INTERNAL_SERVER_ERROR: 500,
14
- /**
15
- * An analytical reduction (`aggregate` / `groupBy`) was invoked over a
16
- * column that the procedure's `mask()` middleware redacts. A masked column
17
- * can't be summed, averaged, or grouped without leaking the very values the
18
- * mask hides (a group key *is* the raw value; an aggregate is computed from
19
- * it), so the operation fails closed. The request is well-formed and the
20
- * caller is authorized — this is a 422 (semantic conflict), mirroring
21
- * `COUNT_RLS_UNSUPPORTED`.
22
- */
23
- MASK_UNSUPPORTED: 422,
24
- NOT_FOUND: 404,
25
- NOT_IMPLEMENTED: 501,
26
- /**
27
- * A write policy's `when` returned a relation-crossing predicate
28
- * (`some`/`none`/`every`/`is`/`isNot`). The in-memory write-policy evaluator
29
- * has no child fetcher and cannot resolve a relation node, so the policy is
30
- * unsupported as written. Relation predicates are valid in *read* policies
31
- * and query `where` clauses (the pre-resolver handles them there). The
32
- * request is well-formed; this is a 422 (semantic conflict), mirroring the
33
- * sibling `*_UNSUPPORTED` codes.
34
- */
35
- RELATION_PREDICATE_UNSUPPORTED: 422,
36
- TOO_MANY_REQUESTS: 429,
37
- UNAUTHORIZED: 401,
38
- UNPROCESSABLE: 422
39
- };
40
- class LunoraError extends Error {
41
- name = "LunoraError";
42
- code;
43
- status;
44
- /** Structured, JSON+wire-encodable payload surfaced to the client alongside `code`. */
45
- data;
46
- constructor(code, message, data) {
47
- super(message ?? code);
48
- this.code = code;
49
- this.status = CODE_STATUS[code];
50
- this.data = data;
51
- }
52
- }
53
-
54
- export { LunoraError };