@lunora/errors 1.0.0-alpha.34 → 1.0.0-alpha.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -653,6 +653,21 @@ declare const ERROR_CATALOG: {
653
653
  readonly status: 502;
654
654
  readonly title: "Could not decode a server frame";
655
655
  };
656
+ /**
657
+ * A function's RETURN value cannot be carried by the wire codec — a class
658
+ * instance (`Decimal`, an ORM entity, `Temporal.*`, `RegExp`, `Headers`), or
659
+ * nesting past the codec's depth cap.
660
+ *
661
+ * Deliberately NOT `internal`: the message names the offending constructor,
662
+ * which is the caller's own handler code and the only thing that makes the
663
+ * failure actionable. Redacting it leaves a bare 500 with nothing to grep.
664
+ * Raised INSIDE a mutation's transaction, so the writes roll back rather than
665
+ * committing behind a response that then fails to serialize.
666
+ */
667
+ readonly WIRE_ENCODE_FAILED: {
668
+ readonly status: 500;
669
+ readonly title: "Could not encode a return value";
670
+ };
656
671
  readonly UNKNOWN_COLUMN: {
657
672
  readonly status: 404;
658
673
  readonly title: "Unknown column";
@@ -848,9 +863,35 @@ declare const ERROR_CATALOG: {
848
863
  readonly status: 404;
849
864
  readonly title: "Unknown mutation function";
850
865
  };
866
+ /**
867
+ * A local tool the CLI shells out to — `wrangler`, `git`, `docker`, a
868
+ * package manager — is not on PATH, so the child process never started.
869
+ *
870
+ * CLI-only and build-time-like: it never crosses the RPC wire (the same
871
+ * posture as `CODEGEN_DIAGNOSTIC`), so it is deliberately not `internal` —
872
+ * the message names the missing command, which IS the fix. The `500` is a
873
+ * placeholder for a transport that never carries it; `@lunora/cli` maps this
874
+ * code to its `missing local dependency` exit code BY NAME, because no HTTP
875
+ * status means "that program isn't installed".
876
+ */
877
+ readonly LOCAL_DEPENDENCY_MISSING: {
878
+ readonly hint: readonly ["The command Lunora tried to run is not on your PATH, so nothing ran.", "", "Install it (or put it on PATH) and retry — `wrangler` ships as a dependency of a Lunora app, so `pnpm install` usually fixes that one; `git` and `docker` are installed separately."];
879
+ readonly status: 500;
880
+ readonly title: "Required local tool not found";
881
+ };
851
882
  };
852
883
  /** A well-known Lunora error code (a key of {@link ERROR_CATALOG}). */
853
884
  type LunoraErrorCode = keyof typeof ERROR_CATALOG;
885
+ /**
886
+ * Look up a catalog entry by `code`, or `undefined` when the code isn't
887
+ * registered. The single guarded seam for reading `ERROR_CATALOG` by an
888
+ * arbitrary string: because the catalog is a plain object literal, a bracket
889
+ * read for an inherited key (e.g. `"constructor"`, `"toString"`) would resolve
890
+ * to `Object.prototype`'s member instead of `undefined`, so this uses
891
+ * `Object.hasOwn` to only ever return an own entry. Reused by the
892
+ * `LunoraError` constructor, {@link isInternalCode}, and {@link resolveHint}.
893
+ */
894
+ declare const getCatalogEntry: (code: string) => ErrorCatalogEntry | undefined;
854
895
  /**
855
896
  * True when `code` is an internal/redacted code — an internal failure or
856
897
  * unhandled invariant whose `message` must NOT cross the wire (it may carry SQL
@@ -1132,4 +1173,4 @@ type LunoraErrorCodeInput, type LunoraErrorLike,
1132
1173
  * renderer (`renderLunoraError`, using `@visulima/error`'s `renderError`) lives
1133
1174
  * in `@lunora/cli`, which already depends on `@visulima/error`.
1134
1175
  */
1135
- type LunoraErrorOptions, MESSAGE_SOLUTIONS, type Solution, type SolutionRule, type ToErrorBodyOptions, type ToErrorBodyResult, findCloudflarePlatformSolution, findIssueSolution, findSolutionByMessage, flattenHint, invariant, isInternalCode, isLunoraError, raise, resolveHint, toErrorBody, unreachable };
1176
+ type LunoraErrorOptions, MESSAGE_SOLUTIONS, type Solution, type SolutionRule, type ToErrorBodyOptions, type ToErrorBodyResult, findCloudflarePlatformSolution, findIssueSolution, findSolutionByMessage, flattenHint, getCatalogEntry, invariant, isInternalCode, isLunoraError, raise, resolveHint, toErrorBody, unreachable };
package/dist/index.d.ts CHANGED
@@ -653,6 +653,21 @@ declare const ERROR_CATALOG: {
653
653
  readonly status: 502;
654
654
  readonly title: "Could not decode a server frame";
655
655
  };
656
+ /**
657
+ * A function's RETURN value cannot be carried by the wire codec — a class
658
+ * instance (`Decimal`, an ORM entity, `Temporal.*`, `RegExp`, `Headers`), or
659
+ * nesting past the codec's depth cap.
660
+ *
661
+ * Deliberately NOT `internal`: the message names the offending constructor,
662
+ * which is the caller's own handler code and the only thing that makes the
663
+ * failure actionable. Redacting it leaves a bare 500 with nothing to grep.
664
+ * Raised INSIDE a mutation's transaction, so the writes roll back rather than
665
+ * committing behind a response that then fails to serialize.
666
+ */
667
+ readonly WIRE_ENCODE_FAILED: {
668
+ readonly status: 500;
669
+ readonly title: "Could not encode a return value";
670
+ };
656
671
  readonly UNKNOWN_COLUMN: {
657
672
  readonly status: 404;
658
673
  readonly title: "Unknown column";
@@ -848,9 +863,35 @@ declare const ERROR_CATALOG: {
848
863
  readonly status: 404;
849
864
  readonly title: "Unknown mutation function";
850
865
  };
866
+ /**
867
+ * A local tool the CLI shells out to — `wrangler`, `git`, `docker`, a
868
+ * package manager — is not on PATH, so the child process never started.
869
+ *
870
+ * CLI-only and build-time-like: it never crosses the RPC wire (the same
871
+ * posture as `CODEGEN_DIAGNOSTIC`), so it is deliberately not `internal` —
872
+ * the message names the missing command, which IS the fix. The `500` is a
873
+ * placeholder for a transport that never carries it; `@lunora/cli` maps this
874
+ * code to its `missing local dependency` exit code BY NAME, because no HTTP
875
+ * status means "that program isn't installed".
876
+ */
877
+ readonly LOCAL_DEPENDENCY_MISSING: {
878
+ readonly hint: readonly ["The command Lunora tried to run is not on your PATH, so nothing ran.", "", "Install it (or put it on PATH) and retry — `wrangler` ships as a dependency of a Lunora app, so `pnpm install` usually fixes that one; `git` and `docker` are installed separately."];
879
+ readonly status: 500;
880
+ readonly title: "Required local tool not found";
881
+ };
851
882
  };
852
883
  /** A well-known Lunora error code (a key of {@link ERROR_CATALOG}). */
853
884
  type LunoraErrorCode = keyof typeof ERROR_CATALOG;
885
+ /**
886
+ * Look up a catalog entry by `code`, or `undefined` when the code isn't
887
+ * registered. The single guarded seam for reading `ERROR_CATALOG` by an
888
+ * arbitrary string: because the catalog is a plain object literal, a bracket
889
+ * read for an inherited key (e.g. `"constructor"`, `"toString"`) would resolve
890
+ * to `Object.prototype`'s member instead of `undefined`, so this uses
891
+ * `Object.hasOwn` to only ever return an own entry. Reused by the
892
+ * `LunoraError` constructor, {@link isInternalCode}, and {@link resolveHint}.
893
+ */
894
+ declare const getCatalogEntry: (code: string) => ErrorCatalogEntry | undefined;
854
895
  /**
855
896
  * True when `code` is an internal/redacted code — an internal failure or
856
897
  * unhandled invariant whose `message` must NOT cross the wire (it may carry SQL
@@ -1132,4 +1173,4 @@ type LunoraErrorCodeInput, type LunoraErrorLike,
1132
1173
  * renderer (`renderLunoraError`, using `@visulima/error`'s `renderError`) lives
1133
1174
  * in `@lunora/cli`, which already depends on `@visulima/error`.
1134
1175
  */
1135
- type LunoraErrorOptions, MESSAGE_SOLUTIONS, type Solution, type SolutionRule, type ToErrorBodyOptions, type ToErrorBodyResult, findCloudflarePlatformSolution, findIssueSolution, findSolutionByMessage, flattenHint, invariant, isInternalCode, isLunoraError, raise, resolveHint, toErrorBody, unreachable };
1176
+ type LunoraErrorOptions, MESSAGE_SOLUTIONS, type Solution, type SolutionRule, type ToErrorBodyOptions, type ToErrorBodyResult, findCloudflarePlatformSolution, findIssueSolution, findSolutionByMessage, flattenHint, getCatalogEntry, invariant, isInternalCode, isLunoraError, raise, resolveHint, toErrorBody, unreachable };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{LunoraError as e}from"./packem_shared/LunoraError-CVQosO1d.mjs";import{CLOUDFLARE_PLATFORM_ERRORS as t,ERROR_CATALOG as i,MESSAGE_SOLUTIONS as a,findCloudflarePlatformSolution as f,findIssueSolution as l,findSolutionByMessage as s,flattenHint as u,isInternalCode as E,resolveHint as R}from"./packem_shared/CLOUDFLARE_PLATFORM_ERRORS-D7-LE9Iy.mjs";import{isLunoraError as L}from"./packem_shared/isLunoraError-kpNg-Mxd.mjs";import{invariant as d,raise as m,unreachable as p}from"./packem_shared/invariant-CBZkipm5.mjs";import{toErrorBody as A}from"./packem_shared/toErrorBody-BZqxHjCz.mjs";export{t as CLOUDFLARE_PLATFORM_ERRORS,i as ERROR_CATALOG,e as LunoraError,a as MESSAGE_SOLUTIONS,f as findCloudflarePlatformSolution,l as findIssueSolution,s as findSolutionByMessage,u as flattenHint,d as invariant,E as isInternalCode,L as isLunoraError,m as raise,R as resolveHint,A as toErrorBody,p as unreachable};
1
+ import{LunoraError as t}from"./packem_shared/LunoraError-DuUEJZwZ.mjs";import{CLOUDFLARE_PLATFORM_ERRORS as n,ERROR_CATALOG as a,MESSAGE_SOLUTIONS as i,findCloudflarePlatformSolution as f,findIssueSolution as l,findSolutionByMessage as E,flattenHint as s,getCatalogEntry as u,isInternalCode as R,resolveHint as S}from"./packem_shared/CLOUDFLARE_PLATFORM_ERRORS-CdSsObTN.mjs";import{isLunoraError as O}from"./packem_shared/isLunoraError-kpNg-Mxd.mjs";import{invariant as m,raise as p,unreachable as x}from"./packem_shared/invariant-B12xLDo0.mjs";import{toErrorBody as C}from"./packem_shared/toErrorBody-DE4a47J-.mjs";export{n as CLOUDFLARE_PLATFORM_ERRORS,a as ERROR_CATALOG,t as LunoraError,i as MESSAGE_SOLUTIONS,f as findCloudflarePlatformSolution,l as findIssueSolution,E as findSolutionByMessage,s as flattenHint,u as getCatalogEntry,m as invariant,R as isInternalCode,O as isLunoraError,p as raise,S as resolveHint,C as toErrorBody,x as unreachable};
@@ -1,4 +1,4 @@
1
- const i="https://developers.cloudflare.com/support/troubleshooting/http-status-codes/cloudflare-5xx-errors/",r="https://developers.cloudflare.com/support/troubleshooting/http-status-codes/cloudflare-1xxx-errors/",m=/string or blob too big/iu,n={BAD_REQUEST:{status:400,title:"Bad request"},UNAUTHORIZED:{status:401,title:"Unauthorized"},FORBIDDEN:{status:403,title:"Forbidden"},NOT_FOUND:{status:404,title:"Not found"},CONFLICT:{hint:["Another write changed this row while your mutation was running (optimistic concurrency conflict).","","Re-read the row and retry the mutation with the fresh value. Lunora serializes a DO's mutations, so a persistent conflict usually means the handler conflicts **with itself** (e.g. a trigger or cascade touching the same row) — split that work rather than adding a retry loop."],status:409,title:"Conflict"},NOT_UNIQUE:{hint:["`.unique()` matched more than one document — it expects the query to identify at most one row.","","- If several matches are legitimate, use `.first()` (take one) or `.collect()` (take all) instead.","- Otherwise tighten the query (e.g. filter on a unique/indexed field) so it can only match one row."],status:400,title:"Query matched more than one document"},VALIDATION_ERROR:{status:400,title:"Validation failed"},TOO_MANY_REQUESTS:{status:429,title:"Too many requests"},UNPROCESSABLE:{status:422,title:"Unprocessable"},NOT_IMPLEMENTED:{status:501,title:"Not implemented"},FUNCTION_NOT_FOUND:{status:404,title:"Function not found"},METHOD_NOT_ALLOWED:{status:405,title:"Method not allowed"},PAYLOAD_TOO_LARGE:{status:413,title:"Payload too large"},INTERNAL:{internal:!0,status:500,title:"Internal error"},INTERNAL_SERVER_ERROR:{internal:!0,status:500,title:"Internal error"},RPC_FAILED:{internal:!0,status:500,title:"Internal error"},COUNT_RLS_UNSUPPORTED:{status:422,title:"count() is unsupported under an RLS policy"},MASK_UNSUPPORTED:{status:422,title:"Aggregation over a masked column is unsupported"},RELATION_PREDICATE_UNSUPPORTED:{status:422,title:"Relation predicate is unsupported in a write policy"},RLS_REQUIRED:{hint:["This table is secure-by-default: it has no `.public()` marker and no RLS policy resolved for the caller, so the read fails closed.","","Add a read policy with `.rls(...)`, or mark the table `.public()` if it is intentionally world-readable."],status:403,title:"RLS policy required"},RUN_DEPTH_EXCEEDED:{internal:!0,status:500,title:"Run depth exceeded"},RUN_KIND_FORBIDDEN:{status:500,title:"Function kind may not be composed from a query"},TRANSACTION_LIMIT_EXCEEDED:{hint:["A single mutation may only read and write a bounded amount before it is stopped.","","Narrow the read with an index (`.withIndex(...)`) instead of scanning the table, or split the write across several mutations — for a large backfill use `defineMigration` + `lunora migrate up`, which batches and checkpoints for you.","","The ceilings are deliberately conservative — they exist to stop one request taking down the whole shard. A deployment that genuinely needs bigger transactions can raise them by overriding the `transactionLimits()` seam on its generated shard class."],status:413,title:"Transaction limit exceeded"},MIGRATION_NOT_FOUND:{status:404,title:"Data migration not found"},UNKNOWN_TABLE:{status:404,title:"Unknown table"},GLOBAL_TABLE_NOT_EDITABLE:{status:400,title:"Global table is not editable"},SHARD_ERROR:{status:503,title:"Shard error"},SHARD_UNAVAILABLE:{status:503,title:"Shard unavailable"},SHARD_TIMEOUT:{status:504,title:"Shard timeout"},SHARD_HTTP_ERROR:{status:502,title:"Shard HTTP error"},SUBSCRIPTION_PERSIST_FAILED:{status:500,title:"Subscription persist failed"},TOO_MANY_SUBSCRIPTIONS:{status:429,title:"Too many subscriptions"},OFFLINE_IDENTITY_CHANGED:{status:409,title:"Offline identity changed"},CODEGEN_DIAGNOSTIC:{status:500,title:"Codegen diagnostic"},SCHEMA_SNAPSHOT_PARSE:{status:500,title:"Schema snapshot parse error"},ENV_INVALID:{internal:!0,status:500,title:"Invalid environment"},AUTH_HEADERS_MISSING:{internal:!0,status:500,title:"Auth headers missing"},EMAIL_DOMAIN_BLOCKED:{hint:["This address's domain is on the disposable/throwaway blocklist (or your configured deny-list).","","Sign up with a permanent mailbox. To tune the policy, pass `blockDisposable` / `allowDomains` / `denyDomains` to `emailGate(...)` (`@lunora/auth/email-guard`)."],status:400,title:"Email domain not allowed"},EMAIL_UNDELIVERABLE:{hint:["The address's domain publishes no MX (or fallback A/AAAA) records, so it can't receive mail.","","Check for a typo in the domain. MX verification is opt-in (`mx: true`) and needs DNS — leave it off on the edge path if DNS is unavailable."],status:400,title:"Email domain cannot receive mail"},ANALYTICS_SQL_ERROR:{status:502,title:"Analytics Engine SQL API error"},R2_SQL_ERROR:{status:502,title:"R2 SQL API error"},WORKFLOWS_REST_ERROR:{status:502,title:"Cloudflare Workflows REST API error"},CONFIG_INVALID:{internal:!0,status:500,title:"Payment configuration invalid"},CURRENCY_MISMATCH:{status:400,title:"Currency mismatch"},PROVIDER_ERROR:{status:502,title:"Payment provider error"},WEBHOOK_EVENT_ID_MISSING:{status:400,title:"Webhook event id missing"},WEBHOOK_SIGNATURE_INVALID:{status:400,title:"Webhook signature invalid"},WEBHOOK_TIMESTAMP_INVALID:{status:400,title:"Webhook timestamp outside tolerance"},ADMIN_FORBIDDEN:{status:403,title:"Admin access forbidden"},ADMIN_TOKEN_NOT_CONFIGURED:{status:400,title:"Admin token not configured"},AUTH_MIGRATOR_UNSUPPORTED:{hint:["better-auth migrates only through its Kysely adapter, so `ensureMigrated` / `compileMigrationsSql` need the raw D1 binding as `database` — a custom adapter (`lunoraD1Adapter`, `lunoraAuthAdapter`, `lunoraDoAdapter`) cannot be migrated through, and neither can an absent `database`.","","Build a SECOND, migration-only instance over the raw binding — `createAuth({ ...options, database: env.DB })` — and hand that one to `ensureMigrated`. Keep the adapter on the instance that serves requests: the adapter exists to dodge a dev-runner hang in `$context`, which the migration instance never resolves.","","To compile the SQL off-platform (`compileMigrationsSql`), diff against an empty local database — `new DatabaseSync(':memory:')` from `node:sqlite` — rather than passing no `database` at all."],status:500,title:"Auth migrator cannot drive the configured database"},AUTH_NOT_CONFIGURED:{status:400,title:"Auth admin not configured"},AUTH_OP_NOT_SUPPORTED:{status:400,title:"Auth admin operation not supported"},BACKUP_NOT_CONFIGURED:{status:500,title:"Scheduled backup not configured"},BACKUP_RETENTION_NOT_CONFIGURED:{hint:["`lunora backup prune` removes snapshots past the retention window, and this worker has no window: set `backupRetain` (how many to keep) and `backupCron` (which decides whose snapshots retention owns) on `createWorker`.","","Nothing was deleted. A default is deliberately not invented here — retention deleting on its own is exactly what this command exists to replace."],status:400,title:"Backup retention window not configured"},BACKUP_TOO_LARGE:{hint:["The scheduled backup is assembled inside the Worker isolate, so it caps the snapshot it will build. Nothing was written.","","The cap is on the NDJSON, not on peak memory: the export fan-out resolves every shard's rows before the first row is encoded, so a snapshot under the cap can still exhaust the isolate. It is set well below the isolate's limit for that reason.","","Narrow the snapshot with `backupTables`, or take this backup off-platform with `lunora backup create --bucket`, which runs on a machine rather than in an isolate. Backing up more often does not help — every run is a full snapshot."],status:507,title:"Backup too large to assemble in a Worker"},CRON_JOBS_NOT_CONFIGURED:{status:400,title:"Cron jobs not configured"},CRON_JOB_NOT_FOUND:{status:404,title:"Cron job not found"},EXPORT_TAP_NOT_CONFIGURED:{status:400,title:"Export tap not configured"},FUNCTIONS_NOT_CONFIGURED:{status:400,title:"Functions registry not configured"},GLOBALS_NOT_CONFIGURED:{status:400,title:"Global-table introspector not configured"},KV_NOT_CONFIGURED:{status:400,title:"KV introspector not configured"},MIGRATION_ID_REQUIRED:{status:400,title:"Migration id required"},PITR_UNAVAILABLE:{status:409,title:"Point-in-time recovery unavailable"},SCHEDULER_NOT_CONFIGURED:{status:400,title:"Scheduler not configured"},STORAGE_CHECKSUM_MISMATCH:{hint:["The upload body did not match the declared `expectedSize` or `expectedSha256`, so nothing was written — this check fails closed.","","Re-read the bytes from the source export and retry the transfer. A persistent mismatch means the source blob is corrupt or truncated; fix the export rather than bypassing the check."],status:400,title:"Storage checksum mismatch"},STORAGE_DELETE_NOT_CONFIGURED:{status:400,title:"Storage delete not configured"},STORAGE_DOWNLOAD_NOT_CONFIGURED:{hint:["`GET /_lunora/admin/storage/object` needs a `storageDownload` function on the worker. The generated app worker wires it up; a hand-written `createWorker({ ... })` has to pass `(key, opts) => pick(opts?.bucket).download(key)` — forwarding `opts.bucket` to the right bucket, and wrapping rather than passing `createStorage(...).download` itself, whose second parameter is a byte range.","","Without it a bucket-backed `lunora backup restore --bucket` cannot read the snapshot. The object is still readable out of band with `wrangler r2 object get`."],status:400,title:"Storage download not configured"},STORAGE_NOT_CONFIGURED:{status:400,title:"Storage not configured"},STORAGE_OBJECT_NOT_FOUND:{status:404,title:"Storage object not found"},STORAGE_UPLOAD_NOT_CONFIGURED:{status:400,title:"Storage upload not configured"},STORAGE_URL_NOT_CONFIGURED:{status:400,title:"Storage signed URL not configured"},RAG_DIMENSION_MISMATCH:{hint:["A stored vector and the query embedding have different widths, so they cannot be compared.","","This is what changing a RAG index's `embeddingModel` (or a provider's `dimensions` option) without reindexing looks like. Either put the previous model back, or reindex the namespace under the new one — bump `embeddingModelVersion` so the index rebuilds instead of mixing widths."],status:409,title:"Embedding dimension mismatch"},VECTORS_NOT_CONFIGURED:{status:400,title:"Vector index introspector not configured"},VECTOR_QUERY_UNSUPPORTED:{status:400,title:"Vector index querying not enabled"},WORKFLOWS_NOT_CONFIGURED:{status:501,title:"Workflows not configured"},AUTH_AUDIT_NOT_CONFIGURED:{status:400,title:"Auth audit reader not configured"},AUTH_AUDIT_READ_FAILED:{internal:!0,status:500,title:"Auth audit read failed"},CRON_EXPR_INVALID:{status:500,title:"Invalid cron expression"},CRON_EXPR_NOT_STATIC:{status:500,title:"Cron expression is not statically analyzable"},CRON_JOB_FAILED:{internal:!0,status:500,title:"Cron job failed"},CRON_NAME_NOT_STATIC:{status:500,title:"Cron job name is not statically analyzable"},CRON_NON_STATIC_FN:{status:500,title:"Cron function reference is not statically analyzable"},CRON_NON_STATIC_VALUE:{status:500,title:"Cron value is not statically analyzable"},CRON_SCHEDULE_INVALID:{status:500,title:"Invalid cron schedule"},CRON_SCHEDULE_NOT_STATIC:{status:500,title:"Cron schedule is not statically analyzable"},DUPLICATE_CRON_NAME:{status:500,title:"Duplicate cron job name"},DUPLICATE_AGENT_BINDING:{status:500,title:"Duplicate agent binding"},DUPLICATE_AGENT_CLASS:{status:500,title:"Duplicate agent generated class name"},DUPLICATE_AGENT_NAME:{status:500,title:"Duplicate agent name"},DUPLICATE_MIGRATION_ID:{status:500,title:"Duplicate migration id"},DUPLICATE_QUEUE_BINDING:{status:500,title:"Duplicate queue binding"},DUPLICATE_QUEUE_NAME:{status:500,title:"Duplicate queue name"},DUPLICATE_WORKFLOW_CLASS:{status:500,title:"Duplicate workflow generated class name"},MIGRATION_ID_NOT_STATIC:{status:500,title:"Migration id is not statically analyzable"},NAMESPACE_COLLISION:{status:500,title:"Function namespace collision"},BAD_ROW:{status:400,title:"Malformed import row"},BAD_SUBSCRIPTION_ARGS:{status:400,title:"Invalid subscription arguments"},BATCH_LIMIT_EXCEEDED:{status:400,title:"Batch limit exceeded"},CROSS_SHARD_RANK_UNSUPPORTED:{status:400,title:"Cross-shard rank() is unsupported"},DISPATCH_UNAUTHENTICATED:{hint:"The scheduler could not authenticate to the worker. Check that `LUNORA_SCHEDULER_SECRET` matches on both sides, or that `LUNORA_ADMIN_TOKEN` is set and current.",status:403,title:"Dispatch caller not authenticated"},FORBIDDEN_FANOUT:{status:403,title:"Fan-out forbidden"},GLOBAL_SEARCH_SCORES_UNSUPPORTED:{status:400,title:"collectWithScores() is unsupported on a global table"},FORBIDDEN_ORIGIN:{status:403,title:"Origin forbidden"},FORBIDDEN_SHARD:{status:403,title:"Shard access forbidden"},GLOBAL_NOT_CONFIGURED:{status:400,title:"Global table import not configured"},INVALID_INPUT:{status:400,title:"Invalid input"},INVALID_SCHEDULE_ID:{status:400,title:"Invalid schedule id"},RATE_LIMITED:{status:429,title:"Rate limited"},REPLICA_NOT_READY:{status:421,title:"Replica not caught up"},REPLICA_READ_ONLY:{status:421,title:"Replica is read-only"},SEARCH_INDEX_BUILDING:{status:503,title:"Search index is still building"},SERVICE_UNAVAILABLE:{status:503,title:"Service unavailable"},SHAPE_MEMORY_TABLE:{status:400,title:"Shape over a memory table is unsupported"},SHAPE_CROSS_SHARD_JOIN:{status:400,title:"Shape cross-shard join is unsupported"},UNAUTHENTICATED:{status:401,title:"Unauthenticated"},WIRE_DECODE_FAILED:{status:502,title:"Could not decode a server frame"},UNKNOWN_COLUMN:{status:404,title:"Unknown column"},CDC_LOG_TRIMMED:{status:409,title:"CDC log trimmed"},CDC_PAYLOAD_COMPACTED:{status:409,title:"CDC payloads compacted"},EXPIRED:{status:404,title:"Session expired"},NESTED_TRANSACTION:{internal:!0,status:500,title:"Nested transaction"},OUT_OF_ORDER:{status:409,title:"Out-of-order mutation"},SHAPE_GLOBAL_TOO_LARGE:{status:413,title:"Global shape too large"},SHAPE_NOT_FOUND:{status:404,title:"Shape not found"},SHAPE_REQUIRES_CDC:{status:409,title:"Shape requires change-data-capture"},SQL_UNAVAILABLE:{internal:!0,status:500,title:"SQL storage unavailable"},STREAM_ID_IN_USE:{status:409,title:"Stream id already in use"},STREAM_INTERRUPTED:{status:503,title:"Durable stream interrupted"},STREAM_TOO_LONG:{status:507,title:"Durable stream exceeded its chunk ceiling"},TOKEN_EXPIRED:{status:401,title:"Authentication token expired"},TOO_MANY_STREAMS:{status:429,title:"Too many streams"},UNKNOWN_ADMIN_OP:{status:404,title:"Unknown admin operation"},SOCKET_TAG_BUDGET_EXCEEDED:{status:400,title:"Socket tag budget exceeded"},RELAY_CANNOT_SEED:{status:500,title:"Relay cannot seed"},RELAY_MISCONFIGURED:{status:500,title:"Relay misconfigured"},RELAY_SEED_FAILED:{status:502,title:"Relay seed failed"},RELAY_SHAPE_UNROUTABLE:{status:500,title:"Relay shape unroutable"},MISCONFIGURED:{internal:!0,status:500,title:"Worker misconfigured"},LUNORA_RUNTIME_UNAVAILABLE:{status:500,title:"Lunora runtime unavailable"},INTERNAL_ERROR:{internal:!0,status:500,title:"Internal error"},BROWSER_TIMEOUT:{status:504,title:"Browser operation timed out"},CLIENT_CLOSED:{status:400,title:"Client is closed"},HTTP_STREAM_BAD_CHUNK:{status:502,title:"Malformed HTTP stream chunk"},HTTP_STREAM_INTERRUPTED:{status:502,title:"HTTP stream interrupted"},HTTP_STREAM_MISSING_PARAM:{status:400,title:"HTTP stream missing path parameter"},HTTP_STREAM_NO_BODY:{status:502,title:"HTTP stream response has no body"},HTTP_STREAM_STATUS:{status:502,title:"HTTP stream request failed"},HTTP_STREAM_TRANSPORT:{status:502,title:"HTTP stream transport error"},STREAM_BACKPRESSURE:{status:429,title:"Stream backpressure"},STREAM_DISCONNECTED:{status:503,title:"Stream disconnected"},STREAM_QUEUE_OVERFLOW:{status:429,title:"Stream queue overflow"},UNKNOWN_MUTATION_FN:{status:404,title:"Unknown mutation function"}},h=e=>Object.hasOwn(n,e)?n[e]:void 0,A=e=>h(e)?.internal===!0,_=[{body:["A single row exceeded the storage engine's per-row ceiling — 2 MB on a Durable Object's SQLite, and 2,000,000 bytes on D1.","","The limit is on the STORED bytes, which are UTF-8: a document of multi-byte text (CJK, emoji) costs up to 3x its character count. `v.bytes()` and `v.bigint()` columns cost more again on a shard-local table, where the row stores both a SQL-comparable projection and the original.","","Keep the large payload out of the row and store a reference to it:","","```ts","const key = `uploads/${crypto.randomUUID()}`;","await ctx.storage.uploads.put(key, bytes);",'await ctx.db.insert("documents", { storageKey: key, title });',"```","","R2 has no practical object-size ceiling, and the row stays small enough to read, index, and replicate."].join(`
1
+ const i="https://developers.cloudflare.com/support/troubleshooting/http-status-codes/cloudflare-5xx-errors/",r="https://developers.cloudflare.com/support/troubleshooting/http-status-codes/cloudflare-1xxx-errors/",m=/string or blob too big/iu,s={BAD_REQUEST:{status:400,title:"Bad request"},UNAUTHORIZED:{status:401,title:"Unauthorized"},FORBIDDEN:{status:403,title:"Forbidden"},NOT_FOUND:{status:404,title:"Not found"},CONFLICT:{hint:["Another write changed this row while your mutation was running (optimistic concurrency conflict).","","Re-read the row and retry the mutation with the fresh value. Lunora serializes a DO's mutations, so a persistent conflict usually means the handler conflicts **with itself** (e.g. a trigger or cascade touching the same row) — split that work rather than adding a retry loop."],status:409,title:"Conflict"},NOT_UNIQUE:{hint:["`.unique()` matched more than one document — it expects the query to identify at most one row.","","- If several matches are legitimate, use `.first()` (take one) or `.collect()` (take all) instead.","- Otherwise tighten the query (e.g. filter on a unique/indexed field) so it can only match one row."],status:400,title:"Query matched more than one document"},VALIDATION_ERROR:{status:400,title:"Validation failed"},TOO_MANY_REQUESTS:{status:429,title:"Too many requests"},UNPROCESSABLE:{status:422,title:"Unprocessable"},NOT_IMPLEMENTED:{status:501,title:"Not implemented"},FUNCTION_NOT_FOUND:{status:404,title:"Function not found"},METHOD_NOT_ALLOWED:{status:405,title:"Method not allowed"},PAYLOAD_TOO_LARGE:{status:413,title:"Payload too large"},INTERNAL:{internal:!0,status:500,title:"Internal error"},INTERNAL_SERVER_ERROR:{internal:!0,status:500,title:"Internal error"},RPC_FAILED:{internal:!0,status:500,title:"Internal error"},COUNT_RLS_UNSUPPORTED:{status:422,title:"count() is unsupported under an RLS policy"},MASK_UNSUPPORTED:{status:422,title:"Aggregation over a masked column is unsupported"},RELATION_PREDICATE_UNSUPPORTED:{status:422,title:"Relation predicate is unsupported in a write policy"},RLS_REQUIRED:{hint:["This table is secure-by-default: it has no `.public()` marker and no RLS policy resolved for the caller, so the read fails closed.","","Add a read policy with `.rls(...)`, or mark the table `.public()` if it is intentionally world-readable."],status:403,title:"RLS policy required"},RUN_DEPTH_EXCEEDED:{internal:!0,status:500,title:"Run depth exceeded"},RUN_KIND_FORBIDDEN:{status:500,title:"Function kind may not be composed from a query"},TRANSACTION_LIMIT_EXCEEDED:{hint:["A single mutation may only read and write a bounded amount before it is stopped.","","Narrow the read with an index (`.withIndex(...)`) instead of scanning the table, or split the write across several mutations — for a large backfill use `defineMigration` + `lunora migrate up`, which batches and checkpoints for you.","","The ceilings are deliberately conservative — they exist to stop one request taking down the whole shard. A deployment that genuinely needs bigger transactions can raise them by overriding the `transactionLimits()` seam on its generated shard class."],status:413,title:"Transaction limit exceeded"},MIGRATION_NOT_FOUND:{status:404,title:"Data migration not found"},UNKNOWN_TABLE:{status:404,title:"Unknown table"},GLOBAL_TABLE_NOT_EDITABLE:{status:400,title:"Global table is not editable"},SHARD_ERROR:{status:503,title:"Shard error"},SHARD_UNAVAILABLE:{status:503,title:"Shard unavailable"},SHARD_TIMEOUT:{status:504,title:"Shard timeout"},SHARD_HTTP_ERROR:{status:502,title:"Shard HTTP error"},SUBSCRIPTION_PERSIST_FAILED:{status:500,title:"Subscription persist failed"},TOO_MANY_SUBSCRIPTIONS:{status:429,title:"Too many subscriptions"},OFFLINE_IDENTITY_CHANGED:{status:409,title:"Offline identity changed"},CODEGEN_DIAGNOSTIC:{status:500,title:"Codegen diagnostic"},SCHEMA_SNAPSHOT_PARSE:{status:500,title:"Schema snapshot parse error"},ENV_INVALID:{internal:!0,status:500,title:"Invalid environment"},AUTH_HEADERS_MISSING:{internal:!0,status:500,title:"Auth headers missing"},EMAIL_DOMAIN_BLOCKED:{hint:["This address's domain is on the disposable/throwaway blocklist (or your configured deny-list).","","Sign up with a permanent mailbox. To tune the policy, pass `blockDisposable` / `allowDomains` / `denyDomains` to `emailGate(...)` (`@lunora/auth/email-guard`)."],status:400,title:"Email domain not allowed"},EMAIL_UNDELIVERABLE:{hint:["The address's domain publishes no MX (or fallback A/AAAA) records, so it can't receive mail.","","Check for a typo in the domain. MX verification is opt-in (`mx: true`) and needs DNS — leave it off on the edge path if DNS is unavailable."],status:400,title:"Email domain cannot receive mail"},ANALYTICS_SQL_ERROR:{status:502,title:"Analytics Engine SQL API error"},R2_SQL_ERROR:{status:502,title:"R2 SQL API error"},WORKFLOWS_REST_ERROR:{status:502,title:"Cloudflare Workflows REST API error"},CONFIG_INVALID:{internal:!0,status:500,title:"Payment configuration invalid"},CURRENCY_MISMATCH:{status:400,title:"Currency mismatch"},PROVIDER_ERROR:{status:502,title:"Payment provider error"},WEBHOOK_EVENT_ID_MISSING:{status:400,title:"Webhook event id missing"},WEBHOOK_SIGNATURE_INVALID:{status:400,title:"Webhook signature invalid"},WEBHOOK_TIMESTAMP_INVALID:{status:400,title:"Webhook timestamp outside tolerance"},ADMIN_FORBIDDEN:{status:403,title:"Admin access forbidden"},ADMIN_TOKEN_NOT_CONFIGURED:{status:400,title:"Admin token not configured"},AUTH_MIGRATOR_UNSUPPORTED:{hint:["better-auth migrates only through its Kysely adapter, so `ensureMigrated` / `compileMigrationsSql` need the raw D1 binding as `database` — a custom adapter (`lunoraD1Adapter`, `lunoraAuthAdapter`, `lunoraDoAdapter`) cannot be migrated through, and neither can an absent `database`.","","Build a SECOND, migration-only instance over the raw binding — `createAuth({ ...options, database: env.DB })` — and hand that one to `ensureMigrated`. Keep the adapter on the instance that serves requests: the adapter exists to dodge a dev-runner hang in `$context`, which the migration instance never resolves.","","To compile the SQL off-platform (`compileMigrationsSql`), diff against an empty local database — `new DatabaseSync(':memory:')` from `node:sqlite` — rather than passing no `database` at all."],status:500,title:"Auth migrator cannot drive the configured database"},AUTH_NOT_CONFIGURED:{status:400,title:"Auth admin not configured"},AUTH_OP_NOT_SUPPORTED:{status:400,title:"Auth admin operation not supported"},BACKUP_NOT_CONFIGURED:{status:500,title:"Scheduled backup not configured"},BACKUP_RETENTION_NOT_CONFIGURED:{hint:["`lunora backup prune` removes snapshots past the retention window, and this worker has no window: set `backupRetain` (how many to keep) and `backupCron` (which decides whose snapshots retention owns) on `createWorker`.","","Nothing was deleted. A default is deliberately not invented here — retention deleting on its own is exactly what this command exists to replace."],status:400,title:"Backup retention window not configured"},BACKUP_TOO_LARGE:{hint:["The scheduled backup is assembled inside the Worker isolate, so it caps the snapshot it will build. Nothing was written.","","The cap is on the NDJSON, not on peak memory: the export fan-out resolves every shard's rows before the first row is encoded, so a snapshot under the cap can still exhaust the isolate. It is set well below the isolate's limit for that reason.","","Narrow the snapshot with `backupTables`, or take this backup off-platform with `lunora backup create --bucket`, which runs on a machine rather than in an isolate. Backing up more often does not help — every run is a full snapshot."],status:507,title:"Backup too large to assemble in a Worker"},CRON_JOBS_NOT_CONFIGURED:{status:400,title:"Cron jobs not configured"},CRON_JOB_NOT_FOUND:{status:404,title:"Cron job not found"},EXPORT_TAP_NOT_CONFIGURED:{status:400,title:"Export tap not configured"},FUNCTIONS_NOT_CONFIGURED:{status:400,title:"Functions registry not configured"},GLOBALS_NOT_CONFIGURED:{status:400,title:"Global-table introspector not configured"},KV_NOT_CONFIGURED:{status:400,title:"KV introspector not configured"},MIGRATION_ID_REQUIRED:{status:400,title:"Migration id required"},PITR_UNAVAILABLE:{status:409,title:"Point-in-time recovery unavailable"},SCHEDULER_NOT_CONFIGURED:{status:400,title:"Scheduler not configured"},STORAGE_CHECKSUM_MISMATCH:{hint:["The upload body did not match the declared `expectedSize` or `expectedSha256`, so nothing was written — this check fails closed.","","Re-read the bytes from the source export and retry the transfer. A persistent mismatch means the source blob is corrupt or truncated; fix the export rather than bypassing the check."],status:400,title:"Storage checksum mismatch"},STORAGE_DELETE_NOT_CONFIGURED:{status:400,title:"Storage delete not configured"},STORAGE_DOWNLOAD_NOT_CONFIGURED:{hint:["`GET /_lunora/admin/storage/object` needs a `storageDownload` function on the worker. The generated app worker wires it up; a hand-written `createWorker({ ... })` has to pass `(key, opts) => pick(opts?.bucket).download(key)` — forwarding `opts.bucket` to the right bucket, and wrapping rather than passing `createStorage(...).download` itself, whose second parameter is a byte range.","","Without it a bucket-backed `lunora backup restore --bucket` cannot read the snapshot. The object is still readable out of band with `wrangler r2 object get`."],status:400,title:"Storage download not configured"},STORAGE_NOT_CONFIGURED:{status:400,title:"Storage not configured"},STORAGE_OBJECT_NOT_FOUND:{status:404,title:"Storage object not found"},STORAGE_UPLOAD_NOT_CONFIGURED:{status:400,title:"Storage upload not configured"},STORAGE_URL_NOT_CONFIGURED:{status:400,title:"Storage signed URL not configured"},RAG_DIMENSION_MISMATCH:{hint:["A stored vector and the query embedding have different widths, so they cannot be compared.","","This is what changing a RAG index's `embeddingModel` (or a provider's `dimensions` option) without reindexing looks like. Either put the previous model back, or reindex the namespace under the new one — bump `embeddingModelVersion` so the index rebuilds instead of mixing widths."],status:409,title:"Embedding dimension mismatch"},VECTORS_NOT_CONFIGURED:{status:400,title:"Vector index introspector not configured"},VECTOR_QUERY_UNSUPPORTED:{status:400,title:"Vector index querying not enabled"},WORKFLOWS_NOT_CONFIGURED:{status:501,title:"Workflows not configured"},AUTH_AUDIT_NOT_CONFIGURED:{status:400,title:"Auth audit reader not configured"},AUTH_AUDIT_READ_FAILED:{internal:!0,status:500,title:"Auth audit read failed"},CRON_EXPR_INVALID:{status:500,title:"Invalid cron expression"},CRON_EXPR_NOT_STATIC:{status:500,title:"Cron expression is not statically analyzable"},CRON_JOB_FAILED:{internal:!0,status:500,title:"Cron job failed"},CRON_NAME_NOT_STATIC:{status:500,title:"Cron job name is not statically analyzable"},CRON_NON_STATIC_FN:{status:500,title:"Cron function reference is not statically analyzable"},CRON_NON_STATIC_VALUE:{status:500,title:"Cron value is not statically analyzable"},CRON_SCHEDULE_INVALID:{status:500,title:"Invalid cron schedule"},CRON_SCHEDULE_NOT_STATIC:{status:500,title:"Cron schedule is not statically analyzable"},DUPLICATE_CRON_NAME:{status:500,title:"Duplicate cron job name"},DUPLICATE_AGENT_BINDING:{status:500,title:"Duplicate agent binding"},DUPLICATE_AGENT_CLASS:{status:500,title:"Duplicate agent generated class name"},DUPLICATE_AGENT_NAME:{status:500,title:"Duplicate agent name"},DUPLICATE_MIGRATION_ID:{status:500,title:"Duplicate migration id"},DUPLICATE_QUEUE_BINDING:{status:500,title:"Duplicate queue binding"},DUPLICATE_QUEUE_NAME:{status:500,title:"Duplicate queue name"},DUPLICATE_WORKFLOW_CLASS:{status:500,title:"Duplicate workflow generated class name"},MIGRATION_ID_NOT_STATIC:{status:500,title:"Migration id is not statically analyzable"},NAMESPACE_COLLISION:{status:500,title:"Function namespace collision"},BAD_ROW:{status:400,title:"Malformed import row"},BAD_SUBSCRIPTION_ARGS:{status:400,title:"Invalid subscription arguments"},BATCH_LIMIT_EXCEEDED:{status:400,title:"Batch limit exceeded"},CROSS_SHARD_RANK_UNSUPPORTED:{status:400,title:"Cross-shard rank() is unsupported"},DISPATCH_UNAUTHENTICATED:{hint:"The scheduler could not authenticate to the worker. Check that `LUNORA_SCHEDULER_SECRET` matches on both sides, or that `LUNORA_ADMIN_TOKEN` is set and current.",status:403,title:"Dispatch caller not authenticated"},FORBIDDEN_FANOUT:{status:403,title:"Fan-out forbidden"},GLOBAL_SEARCH_SCORES_UNSUPPORTED:{status:400,title:"collectWithScores() is unsupported on a global table"},FORBIDDEN_ORIGIN:{status:403,title:"Origin forbidden"},FORBIDDEN_SHARD:{status:403,title:"Shard access forbidden"},GLOBAL_NOT_CONFIGURED:{status:400,title:"Global table import not configured"},INVALID_INPUT:{status:400,title:"Invalid input"},INVALID_SCHEDULE_ID:{status:400,title:"Invalid schedule id"},RATE_LIMITED:{status:429,title:"Rate limited"},REPLICA_NOT_READY:{status:421,title:"Replica not caught up"},REPLICA_READ_ONLY:{status:421,title:"Replica is read-only"},SEARCH_INDEX_BUILDING:{status:503,title:"Search index is still building"},SERVICE_UNAVAILABLE:{status:503,title:"Service unavailable"},SHAPE_MEMORY_TABLE:{status:400,title:"Shape over a memory table is unsupported"},SHAPE_CROSS_SHARD_JOIN:{status:400,title:"Shape cross-shard join is unsupported"},UNAUTHENTICATED:{status:401,title:"Unauthenticated"},WIRE_DECODE_FAILED:{status:502,title:"Could not decode a server frame"},WIRE_ENCODE_FAILED:{status:500,title:"Could not encode a return value"},UNKNOWN_COLUMN:{status:404,title:"Unknown column"},CDC_LOG_TRIMMED:{status:409,title:"CDC log trimmed"},CDC_PAYLOAD_COMPACTED:{status:409,title:"CDC payloads compacted"},EXPIRED:{status:404,title:"Session expired"},NESTED_TRANSACTION:{internal:!0,status:500,title:"Nested transaction"},OUT_OF_ORDER:{status:409,title:"Out-of-order mutation"},SHAPE_GLOBAL_TOO_LARGE:{status:413,title:"Global shape too large"},SHAPE_NOT_FOUND:{status:404,title:"Shape not found"},SHAPE_REQUIRES_CDC:{status:409,title:"Shape requires change-data-capture"},SQL_UNAVAILABLE:{internal:!0,status:500,title:"SQL storage unavailable"},STREAM_ID_IN_USE:{status:409,title:"Stream id already in use"},STREAM_INTERRUPTED:{status:503,title:"Durable stream interrupted"},STREAM_TOO_LONG:{status:507,title:"Durable stream exceeded its chunk ceiling"},TOKEN_EXPIRED:{status:401,title:"Authentication token expired"},TOO_MANY_STREAMS:{status:429,title:"Too many streams"},UNKNOWN_ADMIN_OP:{status:404,title:"Unknown admin operation"},SOCKET_TAG_BUDGET_EXCEEDED:{status:400,title:"Socket tag budget exceeded"},RELAY_CANNOT_SEED:{status:500,title:"Relay cannot seed"},RELAY_MISCONFIGURED:{status:500,title:"Relay misconfigured"},RELAY_SEED_FAILED:{status:502,title:"Relay seed failed"},RELAY_SHAPE_UNROUTABLE:{status:500,title:"Relay shape unroutable"},MISCONFIGURED:{internal:!0,status:500,title:"Worker misconfigured"},LUNORA_RUNTIME_UNAVAILABLE:{status:500,title:"Lunora runtime unavailable"},INTERNAL_ERROR:{internal:!0,status:500,title:"Internal error"},BROWSER_TIMEOUT:{status:504,title:"Browser operation timed out"},CLIENT_CLOSED:{status:400,title:"Client is closed"},HTTP_STREAM_BAD_CHUNK:{status:502,title:"Malformed HTTP stream chunk"},HTTP_STREAM_INTERRUPTED:{status:502,title:"HTTP stream interrupted"},HTTP_STREAM_MISSING_PARAM:{status:400,title:"HTTP stream missing path parameter"},HTTP_STREAM_NO_BODY:{status:502,title:"HTTP stream response has no body"},HTTP_STREAM_STATUS:{status:502,title:"HTTP stream request failed"},HTTP_STREAM_TRANSPORT:{status:502,title:"HTTP stream transport error"},STREAM_BACKPRESSURE:{status:429,title:"Stream backpressure"},STREAM_DISCONNECTED:{status:503,title:"Stream disconnected"},STREAM_QUEUE_OVERFLOW:{status:429,title:"Stream queue overflow"},UNKNOWN_MUTATION_FN:{status:404,title:"Unknown mutation function"},LOCAL_DEPENDENCY_MISSING:{hint:["The command Lunora tried to run is not on your PATH, so nothing ran.","","Install it (or put it on PATH) and retry — `wrangler` ships as a dependency of a Lunora app, so `pnpm install` usually fixes that one; `git` and `docker` are installed separately."],status:500,title:"Required local tool not found"}},h=e=>Object.hasOwn(s,e)?s[e]:void 0,A=e=>h(e)?.internal===!0,_=[{body:["A single row exceeded the storage engine's per-row ceiling — 2 MB on a Durable Object's SQLite, and 2,000,000 bytes on D1.","","The limit is on the STORED bytes, which are UTF-8: a document of multi-byte text (CJK, emoji) costs up to 3x its character count. `v.bytes()` and `v.bigint()` columns cost more again on a shard-local table, where the row stores both a SQL-comparable projection and the original.","","Keep the large payload out of the row and store a reference to it:","","```ts","const key = `uploads/${crypto.randomUUID()}`;","await ctx.storage.uploads.put(key, bytes);",'await ctx.db.insert("documents", { storageKey: key, title });',"```","","R2 has no practical object-size ceiling, and the row stays small enough to read, index, and replicate."].join(`
2
2
  `),header:"Row too large for the storage engine",id:"lunora-row-too-big",test:e=>m.test(e)},{body:["Lunora codegen couldn't find a schema to generate from.","","Create `lunora/schema.ts` exporting a `defineSchema(...)` call:","","```ts",'import { defineSchema, defineTable, v } from "@lunora/server";',"","export default defineSchema({"," messages: defineTable({ body: v.string() }),","});","```","","Or run `lunora init` to scaffold Lunora (a sample `lunora/schema.ts` included) into your app."].join(`
3
3
  `),header:"No Lunora schema found",id:"lunora-schema-missing",test:e=>e.includes("defineSchema() not found")||e.includes("schema.ts not found at")},{body:["`defineSchema(...)` must be called with an **inline object literal** mapping table names to `defineTable(...)`:","","```ts","export default defineSchema({"," todos: defineTable({ title: v.string(), done: v.boolean() }),","});","```","","Codegen reads the schema statically, so it can't follow a variable or a spread — pass the object literal directly."].join(`
4
4
  `),header:"`defineSchema()` needs an inline object literal",id:"lunora-schema-not-object-literal",test:e=>e.includes("defineSchema() expects an object literal")},{body:["This table name collides with a built-in `ctx.db` member, so the generated client can't expose it.","","Rename the table to anything that isn't a reserved name (the error lists them) — e.g. `userAccounts` instead of `insert`."].join(`
@@ -7,9 +7,9 @@ const i="https://developers.cloudflare.com/support/troubleshooting/http-status-c
7
7
  `),header:"Invalid `.jurisdiction(...)` value",id:"lunora-jurisdiction",test:e=>e.includes("unknown jurisdiction")||e.includes("jurisdiction")&&e.includes('"eu", "us", or "fedramp"')},{body:["The `unique` flag on an index must be a **literal** `true` or `false`, not a computed value — codegen needs to read it statically:","","```ts",'defineTable({ email: v.string() }).index("by_email", ["email"], { unique: true });',"```"].join(`
8
8
  `),header:"`unique` must be a literal",id:"lunora-unique-literal",test:e=>e.includes("must be a literal")&&e.includes("unique")},{body:["A declared container/workflow class isn't re-exported by your worker entry, so `wrangler deploy` would reject it.","","Add the generated re-export shown in the error to your worker entry (e.g. `src/index.ts`):","","```ts",'export * from "./lunora/_generated/containers";',"```"].join(`
9
9
  `),header:"Binding not exported by your worker entry",id:"lunora-worker-entry-export-gap",test:e=>e.includes("not exported by your worker entry")},{body:["A row with the same value already exists in a `unique` index.","","- If you meant to upsert, use `ctx.db.<table>().upsert(...)` (or `.patch(...)` an existing row) instead of `.insert(...)`.",`- Otherwise pick a value that isn't already taken, and consider surfacing a friendly "already exists" message to the user.`].join(`
10
- `),header:"Unique constraint violation",id:"lunora-runtime-unique",test:e=>e.includes("unique constraint violation on")},{body:n.CONFLICT.hint.join(`
11
- `),header:"Optimistic concurrency conflict",id:"lunora-runtime-occ",test:e=>e.includes("optimistic concurrency conflict")}],u=[{causes:"the origin returned an empty, unknown, or malformed response Cloudflare couldn't interpret (often an origin crash or an oversized response header)",code:"520",docsUrl:i,family:"5xx",fix:"check your origin's logs for a crash, ensure it returns a valid HTTP response, and keep response headers under the size limit",summary:"Cloudflare got an unknown/empty response from your origin web server.",title:"Web server returns an unknown error"},{causes:"the origin refused the connection — the web server is down, or a firewall is blocking Cloudflare's IP ranges",code:"521",docsUrl:i,family:"5xx",fix:"confirm the origin process is running and allowlist Cloudflare's published IP ranges in your firewall/security groups",summary:"Cloudflare could not connect to your origin because it refused the connection.",title:"Web server is down"},{causes:"Cloudflare could not establish a TCP connection to the origin in time — the origin is overloaded, a firewall is dropping packets, or the origin IP is wrong",code:"522",docsUrl:i,family:"5xx",fix:"verify the origin is reachable and not overloaded, and that the DNS record points at the correct origin IP",summary:"The connection to your origin timed out before it was established.",title:"Connection timed out"},{causes:"Cloudflare cannot route to the origin at all — a bad DNS record, an origin IP that changed, or invalid routing",code:"523",docsUrl:i,family:"5xx",fix:"verify the DNS A/AAAA record points at a reachable origin IP and that no upstream network is blocking Cloudflare",summary:"Cloudflare could not reach your origin server.",title:"Origin is unreachable"},{causes:"Cloudflare made a TCP connection but the origin did not return an HTTP response within 100 seconds — a slow handler or long-running request",code:"524",docsUrl:i,family:"5xx",fix:"speed up the slow origin handler, or move long work off the request path into a background job (a Durable Object, Queue, or Workflow)",summary:"Cloudflare connected to your origin but it did not respond in time.",title:"A timeout occurred"},{causes:"the TLS handshake between Cloudflare and the origin failed — a missing/invalid origin certificate, or a cipher/SNI mismatch under Full (Strict) SSL",code:"525",docsUrl:i,family:"5xx",fix:"install a valid certificate on the origin and align your Cloudflare SSL/TLS mode with the origin's certificate setup",summary:"The SSL handshake with your origin failed.",title:"SSL handshake failed"},{causes:"Cloudflare could not validate the origin's certificate under Full (Strict) SSL — it is expired, self-signed, or issued for the wrong hostname",code:"526",docsUrl:i,family:"5xx",fix:"install a valid, publicly-trusted certificate on the origin (or use a Cloudflare Origin CA cert), matching the request hostname",summary:"Cloudflare could not validate your origin's SSL certificate.",title:"Invalid SSL certificate"},{causes:"a DNS record points at a Cloudflare IP or another prohibited address instead of your real origin",code:"1000",docsUrl:r,family:"1xxx",fix:"point the DNS record at your real origin IP, not a Cloudflare-owned or loopback address",summary:"DNS points to a prohibited IP.",title:"DNS points to prohibited IP"},{causes:"Cloudflare could not resolve the requested hostname — a Worker fetched an unresolvable host, or a DNS record is misconfigured",code:"1001",docsUrl:r,family:"1xxx",fix:"check the hostname you're requesting and the DNS records for the zone resolve to a valid origin",summary:"Cloudflare could not resolve the origin DNS.",title:"DNS resolution error"},{causes:"a Worker or DNS record targets a restricted IP (for example a Cloudflare-owned or loopback address)",code:"1002",docsUrl:r,family:"1xxx",fix:"change the Worker fetch target or DNS record to a valid, non-restricted origin address",summary:"DNS points to a prohibited IP (Worker/restricted).",title:"DNS points to a prohibited IP"},{causes:"the visitor's IP was blocked by an IP Access Rule, WAF rule, or security configuration on the zone",code:"1006",docsUrl:r,family:"1xxx",fix:"review the zone's Firewall/WAF and IP Access Rules to see why the address was banned, and adjust if it was blocked in error",summary:"Access denied: the visitor's IP has been banned.",title:"Access denied: your IP has been banned"},{causes:"your Worker threw an unhandled JavaScript exception during the request",code:"1101",docsUrl:r,family:"1xxx",fix:"reproduce with `wrangler tail` (or the Workers Logs / Studio Logs panel) to get the stack trace, then handle the throwing code path",summary:"A Worker threw a JavaScript exception.",title:"Worker threw a JavaScript exception"},{causes:"the Worker used more CPU time than a single invocation is allowed — usually a hot loop or heavy synchronous work",code:"1102",docsUrl:r,family:"1xxx",fix:"reduce per-request CPU (optimize hot loops, avoid heavy synchronous work) or offload the heavy work to a Durable Object, Queue, or Workflow",summary:"A Worker exceeded its CPU-time resource limit.",title:"Worker exceeded resource limits"}],l=e=>e!==void 0&&e>="0"&&e<="9",f=(e,t)=>{for(let a=e.indexOf(t);a!==-1;a=e.indexOf(t,a+t.length))if(!l(e[a-1])&&!l(e[a+t.length]))return!0;return!1},E=(e,t)=>{for(const a of[`error ${t}`,`error: ${t}`])for(let o=e.indexOf(a);o!==-1;o=e.indexOf(a,o+a.length))if(!l(e[o+a.length]))return!0;return!1},p=/error|cloudflare/iu,c=e=>({body:[e.summary,"",`**Likely cause:** ${e.causes}.`,"",`**Fix:** ${e.fix}.`,"",`See [Cloudflare's ${e.family} error docs](${e.docsUrl}).`].join(`
12
- `),header:`Cloudflare Error ${e.code}: ${e.title}`,id:`cloudflare-error-${e.code}`}),T=e=>{if(!p.test(e))return;const t=e.toLowerCase(),a=t.includes("error"),o=t.includes("cloudflare");if(a){for(const s of u)if(E(t,s.code))return c(s)}if(o){for(const s of u)if(f(t,s.code))return c(s)}},O=e=>(typeof e=="string"?e:e.join(`
10
+ `),header:"Unique constraint violation",id:"lunora-runtime-unique",test:e=>e.includes("unique constraint violation on")},{body:s.CONFLICT.hint.join(`
11
+ `),header:"Optimistic concurrency conflict",id:"lunora-runtime-occ",test:e=>e.includes("optimistic concurrency conflict")}],u=[{causes:"the origin returned an empty, unknown, or malformed response Cloudflare couldn't interpret (often an origin crash or an oversized response header)",code:"520",docsUrl:i,family:"5xx",fix:"check your origin's logs for a crash, ensure it returns a valid HTTP response, and keep response headers under the size limit",summary:"Cloudflare got an unknown/empty response from your origin web server.",title:"Web server returns an unknown error"},{causes:"the origin refused the connection — the web server is down, or a firewall is blocking Cloudflare's IP ranges",code:"521",docsUrl:i,family:"5xx",fix:"confirm the origin process is running and allowlist Cloudflare's published IP ranges in your firewall/security groups",summary:"Cloudflare could not connect to your origin because it refused the connection.",title:"Web server is down"},{causes:"Cloudflare could not establish a TCP connection to the origin in time — the origin is overloaded, a firewall is dropping packets, or the origin IP is wrong",code:"522",docsUrl:i,family:"5xx",fix:"verify the origin is reachable and not overloaded, and that the DNS record points at the correct origin IP",summary:"The connection to your origin timed out before it was established.",title:"Connection timed out"},{causes:"Cloudflare cannot route to the origin at all — a bad DNS record, an origin IP that changed, or invalid routing",code:"523",docsUrl:i,family:"5xx",fix:"verify the DNS A/AAAA record points at a reachable origin IP and that no upstream network is blocking Cloudflare",summary:"Cloudflare could not reach your origin server.",title:"Origin is unreachable"},{causes:"Cloudflare made a TCP connection but the origin did not return an HTTP response within 100 seconds — a slow handler or long-running request",code:"524",docsUrl:i,family:"5xx",fix:"speed up the slow origin handler, or move long work off the request path into a background job (a Durable Object, Queue, or Workflow)",summary:"Cloudflare connected to your origin but it did not respond in time.",title:"A timeout occurred"},{causes:"the TLS handshake between Cloudflare and the origin failed — a missing/invalid origin certificate, or a cipher/SNI mismatch under Full (Strict) SSL",code:"525",docsUrl:i,family:"5xx",fix:"install a valid certificate on the origin and align your Cloudflare SSL/TLS mode with the origin's certificate setup",summary:"The SSL handshake with your origin failed.",title:"SSL handshake failed"},{causes:"Cloudflare could not validate the origin's certificate under Full (Strict) SSL — it is expired, self-signed, or issued for the wrong hostname",code:"526",docsUrl:i,family:"5xx",fix:"install a valid, publicly-trusted certificate on the origin (or use a Cloudflare Origin CA cert), matching the request hostname",summary:"Cloudflare could not validate your origin's SSL certificate.",title:"Invalid SSL certificate"},{causes:"a DNS record points at a Cloudflare IP or another prohibited address instead of your real origin",code:"1000",docsUrl:r,family:"1xxx",fix:"point the DNS record at your real origin IP, not a Cloudflare-owned or loopback address",summary:"DNS points to a prohibited IP.",title:"DNS points to prohibited IP"},{causes:"Cloudflare could not resolve the requested hostname — a Worker fetched an unresolvable host, or a DNS record is misconfigured",code:"1001",docsUrl:r,family:"1xxx",fix:"check the hostname you're requesting and the DNS records for the zone resolve to a valid origin",summary:"Cloudflare could not resolve the origin DNS.",title:"DNS resolution error"},{causes:"a Worker or DNS record targets a restricted IP (for example a Cloudflare-owned or loopback address)",code:"1002",docsUrl:r,family:"1xxx",fix:"change the Worker fetch target or DNS record to a valid, non-restricted origin address",summary:"DNS points to a prohibited IP (Worker/restricted).",title:"DNS points to a prohibited IP"},{causes:"the visitor's IP was blocked by an IP Access Rule, WAF rule, or security configuration on the zone",code:"1006",docsUrl:r,family:"1xxx",fix:"review the zone's Firewall/WAF and IP Access Rules to see why the address was banned, and adjust if it was blocked in error",summary:"Access denied: the visitor's IP has been banned.",title:"Access denied: your IP has been banned"},{causes:"your Worker threw an unhandled JavaScript exception during the request",code:"1101",docsUrl:r,family:"1xxx",fix:"reproduce with `wrangler tail` (or the Workers Logs / Studio Logs panel) to get the stack trace, then handle the throwing code path",summary:"A Worker threw a JavaScript exception.",title:"Worker threw a JavaScript exception"},{causes:"the Worker used more CPU time than a single invocation is allowed — usually a hot loop or heavy synchronous work",code:"1102",docsUrl:r,family:"1xxx",fix:"reduce per-request CPU (optimize hot loops, avoid heavy synchronous work) or offload the heavy work to a Durable Object, Queue, or Workflow",summary:"A Worker exceeded its CPU-time resource limit.",title:"Worker exceeded resource limits"}],l=e=>e!==void 0&&e>="0"&&e<="9",E=(e,t)=>{for(let a=e.indexOf(t);a!==-1;a=e.indexOf(t,a+t.length))if(!l(e[a-1])&&!l(e[a+t.length]))return!0;return!1},f=(e,t)=>{for(const a of[`error ${t}`,`error: ${t}`])for(let o=e.indexOf(a);o!==-1;o=e.indexOf(a,o+a.length))if(!l(e[o+a.length]))return!0;return!1},p=/error|cloudflare/iu,c=e=>({body:[e.summary,"",`**Likely cause:** ${e.causes}.`,"",`**Fix:** ${e.fix}.`,"",`See [Cloudflare's ${e.family} error docs](${e.docsUrl}).`].join(`
12
+ `),header:`Cloudflare Error ${e.code}: ${e.title}`,id:`cloudflare-error-${e.code}`}),T=e=>{if(!p.test(e))return;const t=e.toLowerCase(),a=t.includes("error"),o=t.includes("cloudflare");if(a){for(const n of u)if(f(t,n.code))return c(n)}if(o){for(const n of u)if(E(t,n.code))return c(n)}},N=e=>(typeof e=="string"?e:e.join(`
13
13
  `)).split(`
14
14
  `).filter(t=>!t.startsWith("```")).join(`
15
- `).replaceAll(/\*\*(.+?)\*\*/gu,"$1").replaceAll(/`([^`]+)`/gu,"$1"),d=e=>{for(const t of _)if(t.test(e))return{body:t.body,header:t.header,id:t.id}},g=e=>d(e)??T(e),N=e=>{if(typeof e=="string")return d(e)?.body;if(e.hint!==void 0)return e.hint;if(e.code!==void 0){const t=h(e.code);if(t?.hint!==void 0)return t.hint}return e.message===void 0?void 0:d(e.message)?.body};export{u as CLOUDFLARE_PLATFORM_ERRORS,n as ERROR_CATALOG,_ as MESSAGE_SOLUTIONS,T as findCloudflarePlatformSolution,g as findIssueSolution,d as findSolutionByMessage,O as flattenHint,h as getCatalogEntry,A as isInternalCode,N as resolveHint};
15
+ `).replaceAll(/\*\*(.+?)\*\*/gu,"$1").replaceAll(/`([^`]+)`/gu,"$1"),d=e=>{for(const t of _)if(t.test(e))return{body:t.body,header:t.header,id:t.id}},g=e=>d(e)??T(e),O=e=>{if(typeof e=="string")return d(e)?.body;if(e.hint!==void 0)return e.hint;if(e.code!==void 0){const t=h(e.code);if(t?.hint!==void 0)return t.hint}return e.message===void 0?void 0:d(e.message)?.body};export{u as CLOUDFLARE_PLATFORM_ERRORS,s as ERROR_CATALOG,_ as MESSAGE_SOLUTIONS,T as findCloudflarePlatformSolution,g as findIssueSolution,d as findSolutionByMessage,N as flattenHint,h as getCatalogEntry,A as isInternalCode,O as resolveHint};
@@ -1 +1 @@
1
- import{getCatalogEntry as e}from"./CLOUDFLARE_PLATFORM_ERRORS-D7-LE9Iy.mjs";class l extends Error{type="VisulimaError";hint;title;loc;code;status;docsUrl;data;constructor(a,s,t={}){const r=e(a);super(s??a,t.cause===void 0?void 0:{cause:t.cause}),this.name=t.name??"LunoraError",this.hint=t.hint??r?.hint,this.title=t.title??r?.title,this.loc=t.location,this.code=a,this.status=t.status??r?.status??500,this.docsUrl=t.docsUrl??r?.docsUrl,this.data=t.data}}export{l as LunoraError};
1
+ import{getCatalogEntry as e}from"./CLOUDFLARE_PLATFORM_ERRORS-CdSsObTN.mjs";class l extends Error{type="VisulimaError";hint;title;loc;code;status;docsUrl;data;constructor(a,s,t={}){const r=e(a);super(s??a,t.cause===void 0?void 0:{cause:t.cause}),this.name=t.name??"LunoraError",this.hint=t.hint??r?.hint,this.title=t.title??r?.title,this.loc=t.location,this.code=a,this.status=t.status??r?.status??500,this.docsUrl=t.docsUrl??r?.docsUrl,this.data=t.data}}export{l as LunoraError};
@@ -1 +1 @@
1
- import{LunoraError as o}from"./LunoraError-CVQosO1d.mjs";const e=(r,n)=>{if(!r)throw new o("INTERNAL",n,{name:"InvariantError"})},i=r=>{throw new o("INTERNAL",r,{name:"InvariantError"})},w=(r,n,a)=>{throw new o(r,n,a)};export{e as invariant,w as raise,i as unreachable};
1
+ import{LunoraError as o}from"./LunoraError-DuUEJZwZ.mjs";const e=(r,n)=>{if(!r)throw new o("INTERNAL",n,{name:"InvariantError"})},i=r=>{throw new o("INTERNAL",r,{name:"InvariantError"})},w=(r,n,a)=>{throw new o(r,n,a)};export{e as invariant,w as raise,i as unreachable};
@@ -1 +1 @@
1
- import{isInternalCode as c,resolveHint as o}from"./CLOUDFLARE_PLATFORM_ERRORS-D7-LE9Iy.mjs";import{isLunoraError as n}from"./isLunoraError-kpNg-Mxd.mjs";const m=(e,t={})=>{const a=t.redactedMessage??"Internal error";if(n(e)){if(c(e.code))return{body:{code:e.code,message:a},redacted:!0,status:e.status};const d={code:e.code,message:e.message};e.data!==void 0&&t.encodeData!==void 0&&(d.data=t.encodeData(e.data));const s=o({code:e.code,hint:e.hint,message:e.message});return s!==void 0&&(d.hint=s),e.docsUrl!==void 0&&(d.docsUrl=e.docsUrl),{body:d,redacted:!1,status:e.status}}return{body:{code:t.fallbackCode??"INTERNAL",message:a},redacted:!0,status:500}};export{m as toErrorBody};
1
+ import{isInternalCode as c,resolveHint as o}from"./CLOUDFLARE_PLATFORM_ERRORS-CdSsObTN.mjs";import{isLunoraError as n}from"./isLunoraError-kpNg-Mxd.mjs";const m=(e,t={})=>{const a=t.redactedMessage??"Internal error";if(n(e)){if(c(e.code))return{body:{code:e.code,message:a},redacted:!0,status:e.status};const d={code:e.code,message:e.message};e.data!==void 0&&t.encodeData!==void 0&&(d.data=t.encodeData(e.data));const s=o({code:e.code,hint:e.hint,message:e.message});return s!==void 0&&(d.hint=s),e.docsUrl!==void 0&&(d.docsUrl=e.docsUrl),{body:d,redacted:!1,status:e.status}}return{body:{code:t.fallbackCode??"INTERNAL",message:a},redacted:!0,status:500}};export{m as toErrorBody};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/errors",
3
- "version": "1.0.0-alpha.34",
3
+ "version": "1.0.0-alpha.36",
4
4
  "description": "Unified error layer for Lunora: one LunoraError base + a central catalog of codes, statuses, and actionable hints, rendered across CLI, overlay, Studio, and the client",
5
5
  "keywords": [
6
6
  "cloudflare",