@reliantlabs/forge-web-runtime 0.3.2 → 0.4.0
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/README.md +26 -3
- package/dist/devlog.js +6 -2
- package/dist/devlog.js.map +1 -1
- package/dist/error-boundary.d.ts +1 -1
- package/dist/error-boundary.js +9 -2
- package/dist/error-boundary.js.map +1 -1
- package/dist/error-reporter.d.ts +27 -0
- package/dist/error-reporter.js +55 -0
- package/dist/error-reporter.js.map +1 -0
- package/dist/index.d.ts +15 -2
- package/dist/index.js +15 -2
- package/dist/index.js.map +1 -1
- package/dist/mock-transport.d.ts +8 -1
- package/dist/mock-transport.js +198 -6
- package/dist/mock-transport.js.map +1 -1
- package/dist/money.d.ts +69 -0
- package/dist/money.js +335 -0
- package/dist/money.js.map +1 -0
- package/dist/resource.js +7 -2
- package/dist/resource.js.map +1 -1
- package/dist/telemetry.d.ts +61 -21
- package/dist/telemetry.js +254 -111
- package/dist/telemetry.js.map +1 -1
- package/package.json +12 -45
- package/dist/otel.d.ts +0 -31
- package/dist/otel.js +0 -94
- package/dist/otel.js.map +0 -1
package/README.md
CHANGED
|
@@ -12,7 +12,8 @@ import {
|
|
|
12
12
|
useSession, // the signed-in user
|
|
13
13
|
RouteGuard, // signed-in render gate
|
|
14
14
|
Resource, // the list tristate + cursor pagination
|
|
15
|
-
|
|
15
|
+
reportException, // record a caught error with the browser telemetry SDK
|
|
16
|
+
formatMinorUnits, // exact money display; parseMinorUnits for input
|
|
16
17
|
} from "@reliantlabs/forge-web-runtime";
|
|
17
18
|
```
|
|
18
19
|
|
|
@@ -21,8 +22,8 @@ import {
|
|
|
21
22
|
**In:** mechanism. The interceptor stack (auth, brand headers, W3C
|
|
22
23
|
traceparent, error normalization, idempotency-gated retry), the session
|
|
23
24
|
derived from JWT claims, the error boundary, the toast queue, the
|
|
24
|
-
`<Resource>` tristate/pagination container, trace-context propagation,
|
|
25
|
-
|
|
25
|
+
`<Resource>` tristate/pagination container, trace-context propagation,
|
|
26
|
+
the HyperDX browser SDK wiring (`/telemetry` subpath), and integer money / basis-point formatting and parsing.
|
|
26
27
|
|
|
27
28
|
**Out:** presentation a project is meant to own. The component library under
|
|
28
29
|
`src/components/ui`, `globals.css`, the nav and the app layout all stay in the
|
|
@@ -125,6 +126,28 @@ Metro with `unstable_enablePackageExports` off, and `expo/tsconfig.base` sets
|
|
|
125
126
|
build output, reached the old way. Delete it and mobile breaks while web keeps
|
|
126
127
|
working.
|
|
127
128
|
|
|
129
|
+
## Money and basis points
|
|
130
|
+
|
|
131
|
+
The web counterpart of `forge/pkg/money`. An amount is an integer count of
|
|
132
|
+
its currency's minor unit (protobuf-es hands an `int64` over as a `bigint`)
|
|
133
|
+
plus an ISO 4217 code, and no float sits anywhere between the wire and the
|
|
134
|
+
screen.
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
formatMinorUnits(123456n); // "$1,234.56"
|
|
138
|
+
formatMinorUnits(1234n, { currency: "JPY" }); // "¥1,234"
|
|
139
|
+
formatMinorUnits(123456n, { currency: "EUR", locale: "de-DE" }); // "1.234,56 €"
|
|
140
|
+
parseMinorUnits("$1,234.5"); // 123450n
|
|
141
|
+
parseMinorUnits("1.234"); // null: USD has two minor digits, so no rounding
|
|
142
|
+
minorUnitsToInput(123450n); // "1234.50", parses back exactly
|
|
143
|
+
formatBasisPoints(825); // "8.25%"
|
|
144
|
+
parseBasisPoints("8.25%"); // 825
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Minor digits come from Intl (JPY 0, KWD 3). Formatting stays exact past 2^53.
|
|
148
|
+
The parsers return `null` rather than guessing, so a form validates on that.
|
|
149
|
+
`src/money.ts`'s header lists the full rules.
|
|
150
|
+
|
|
128
151
|
## The `--color-*` contract
|
|
129
152
|
|
|
130
153
|
Several components render semantic Tailwind utilities. They are part of this
|
package/dist/devlog.js
CHANGED
|
@@ -145,7 +145,10 @@ function capture(level, args) {
|
|
|
145
145
|
droppedReason = "buffer full";
|
|
146
146
|
return;
|
|
147
147
|
}
|
|
148
|
-
const entry = {
|
|
148
|
+
const entry = {
|
|
149
|
+
level,
|
|
150
|
+
msg: truncate(args.map(render).join(" ")),
|
|
151
|
+
};
|
|
149
152
|
const wasEmpty = buffer.length === 0;
|
|
150
153
|
buffer.push(entry);
|
|
151
154
|
bufferedBytes += entryBytes(entry);
|
|
@@ -309,7 +312,8 @@ function flushOnUnload() {
|
|
|
309
312
|
drainDroppedNotice();
|
|
310
313
|
if (buffer.length === 0)
|
|
311
314
|
return;
|
|
312
|
-
const canBeacon = typeof navigator !== "undefined" &&
|
|
315
|
+
const canBeacon = typeof navigator !== "undefined" &&
|
|
316
|
+
typeof navigator.sendBeacon === "function";
|
|
313
317
|
if (!canBeacon) {
|
|
314
318
|
// No beacon: keepalive fetch is the only remaining option, and one request
|
|
315
319
|
// has a better chance of leaving than a queue of them.
|
package/dist/devlog.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devlog.js","sourceRoot":"","sources":["../src/devlog.ts"],"names":[],"mappings":"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","sourcesContent":["// Dev-only browser log forwarding.\n//\n// A browser's console is a dead end: an agent debugging a scaffolded frontend\n// cannot open devtools, so every console line and — worse — every uncaught\n// error is invisible to it. The backend has no such gap (structured slog, the\n// observe ComponentChain, and forge's dev loop already tees each process's\n// stdout to .forge/logs/<env>/). This closes the frontend half by POSTing\n// browser console output to a dev-server endpoint that prints it, so it lands\n// in .forge/logs/<env>/frontend_<name>.log next to the server logs.\n//\n// ── Why this batches (protocol v2) ───────────────────────────────────────\n//\n// The first version sent one fetch POST per console call. Measured in a real\n// app that logs freely, that is ~80 requests/s at light load and 200+/s in a\n// burst, and those requests are not free bystanders: they share the browser's\n// six-connections-per-origin pool and the dev server's single event loop with\n// the app's own RPCs. In the same session, RPC latency averaged 37ms in quiet\n// seconds and 127ms in seconds carrying 200+ log lines. Worse, `keepalive`\n// requests draw on a 64 KiB in-flight browser quota shared across the page, so\n// a burst does not just slow things down — some of it gets rejected, and the\n// original `.catch(() => {})` made that indistinguishable from silence.\n//\n// So entries are buffered and flushed as a batch:\n//\n// - every FLUSH_INTERVAL_MS, timer armed when an entry lands in an empty\n// buffer (the common case: a few lines coalesce into one request),\n// - immediately once buffered output reaches MAX_BATCH_BYTES,\n// - on a microtask for anything error-level, so a crash never waits 250ms\n// while a same-tick burst still coalesces,\n// - synchronously via sendBeacon on pagehide / visibilitychange→hidden,\n// which is the only way output survives a navigation or a crash-reload.\n//\n// At most ONE request is in flight. Entries logged during it keep buffering and\n// go out when it settles, which is what makes the printed order the log order —\n// six parallel POSTs would interleave on the server.\n//\n// Nothing is ever dropped silently. A full buffer, a rejected request and an\n// over-quota request all increment a counter, and the next batch to leave\n// carries a synthetic warn line saying how many lines were lost and why. The\n// failure this replaces was a dead sink that looked exactly like working code.\n//\n// ── Legacy endpoints ─────────────────────────────────────────────────────\n//\n// The receiving endpoint is a scaffold-once file the PROJECT owns (a Vite\n// plugin / a Next.js route handler), so upgrading this package through npm will\n// routinely meet an endpoint that predates batching. Such an endpoint answers\n// 204 and prints one empty line for the whole batch — total, silent loss. A v2\n// endpoint therefore returns `X-Forge-Devlog: 2`, and a 2xx without it makes\n// this client fall back permanently to one v1 POST per line (plus one warn\n// line telling the developer to update the endpoint).\n//\n// DEV ONLY, and structurally so. The receiving endpoint is a Vite plugin\n// gated `apply: \"serve\"` / a Next.js route behind a NODE_ENV check, so it does\n// not exist in a production build; installDevLogging() additionally no-ops\n// unless it is handed dev: true. A production bundle therefore has no\n// endpoint to post to AND no code that would post.\n//\n// It is also reversible: uninstallDevLogging() restores the original console\n// methods, and the scaffolded call site is an ordinary line in main.tsx /\n// providers.tsx that a project can delete outright.\n\n/** Console methods this module mirrors. */\nconst LEVELS = [\"log\", \"info\", \"warn\", \"error\", \"debug\"] as const;\n\ntype Level = (typeof LEVELS)[number];\n\n/**\n * One log line on the wire.\n *\n * Also the whole v1 body: a legacy endpoint receives exactly this object, which\n * is why the shape is unchanged and still exported.\n */\nexport interface DevLogPayload {\n level: Level | \"error\";\n msg: string;\n}\n\n/** The v2 body: many lines, one request. */\nexport interface DevLogBatch {\n entries: DevLogPayload[];\n}\n\nexport interface DevLoggingOptions {\n /**\n * Whether to install at all. Pass `import.meta.env.DEV` (Vite) or\n * `process.env.NODE_ENV !== \"production\"` (Next.js). When false this is a\n * no-op, so the call site needs no conditional of its own.\n */\n dev: boolean;\n /** Endpoint the dev server serves. Defaults to the forge convention. */\n endpoint?: string;\n /**\n * Also keep writing to the real console. Default true — the browser devtools\n * experience should be unchanged; this feature ADDS a sink, it does not move\n * one.\n */\n mirrorToConsole?: boolean;\n}\n\nexport const DEV_LOG_ENDPOINT = \"/__forge/log\";\n\n/** Response header a batching endpoint sets. Its ABSENCE selects v1 fallback. */\nconst PROTOCOL_HEADER = \"X-Forge-Devlog\";\n\n/** Cap a single line so a runaway loop cannot fill the disk. Matches the endpoints. */\nconst MAX_LINE = 8_000;\n\n/** How long a line may wait for company before it is sent. */\nconst FLUSH_INTERVAL_MS = 250;\n\n/**\n * Per-request ceiling. Deliberately half of the browser's 64 KiB shared\n * `keepalive` in-flight quota: with one request in flight, a batch cannot be\n * the thing that pushes the page over it.\n */\nconst MAX_BATCH_BYTES = 32 * 1024;\n\n/** Buffer ceiling. Past it entries are dropped and counted, never queued forever. */\nconst MAX_BUFFER_ENTRIES = 5_000;\n\nconst original: Partial<Record<Level, (...args: unknown[]) => void>> = {};\nlet installed = false;\n\n// Guards against infinite recursion. The forwarder calls fetch(), and anything\n// that logs inside fetch (an interceptor, a polyfill, a devtools extension)\n// would otherwise re-enter the override forever. Such a line is still mirrored\n// to the real console — it is only forwarding that is suppressed.\nlet forwarding = false;\n\nlet endpointUrl = DEV_LOG_ENDPOINT;\nlet buffer: DevLogPayload[] = [];\nlet bufferedBytes = 0;\nlet flushTimer: ReturnType<typeof setTimeout> | undefined;\nlet requestInFlight = false;\nlet errorFlushQueued = false;\nlet droppedCount = 0;\nlet droppedReason = \"\";\n/** Set for good once an endpoint is known not to understand batches. */\nlet legacyEndpoint = false;\n\n/**\n * Render one console argument as a string.\n *\n * Circular structures and BigInt are the two cases a plain JSON.stringify\n * throws on, and the old fallback to String(value) turned both into\n * \"[object Object]\" — deleting exactly the shape you were trying to read. The\n * replacer keeps the object and names the cycle instead.\n */\nfunction render(value: unknown): string {\n if (typeof value === \"string\") return value;\n if (value instanceof Error) {\n return `${value.name}: ${value.message}${value.stack ? `\\n${value.stack}` : \"\"}`;\n }\n try {\n const seen = new WeakSet<object>();\n return (\n JSON.stringify(value, (_key, val: unknown) => {\n if (typeof val === \"bigint\") return `${val}n`;\n if (typeof val === \"object\" && val !== null) {\n if (seen.has(val)) return \"[Circular]\";\n seen.add(val);\n }\n return val;\n }) ?? String(value)\n );\n } catch {\n // DOM nodes, getters that throw — never let logging throw.\n return String(value);\n }\n}\n\nfunction truncate(msg: string): string {\n return msg.length > MAX_LINE ? `${msg.slice(0, MAX_LINE)}… (truncated)` : msg;\n}\n\nfunction entryBytes(entry: DevLogPayload): number {\n return JSON.stringify(entry).length;\n}\n\n/**\n * Buffer one line and decide whether it can wait.\n *\n * Arguments are rendered HERE rather than at flush time: a console argument is\n * a live object, and by the time a batch leaves 250ms later the array it logged\n * may have been mutated or emptied. The snapshot is the value at the call.\n */\nfunction capture(level: DevLogPayload[\"level\"], args: unknown[]): void {\n if (forwarding) return;\n\n if (buffer.length >= MAX_BUFFER_ENTRIES) {\n droppedCount += 1;\n droppedReason = \"buffer full\";\n return;\n }\n\n const entry: DevLogPayload = { level, msg: truncate(args.map(render).join(\" \")) };\n const wasEmpty = buffer.length === 0;\n buffer.push(entry);\n bufferedBytes += entryBytes(entry);\n\n if (bufferedBytes >= MAX_BATCH_BYTES) {\n flush();\n return;\n }\n\n if (level === \"error\") {\n // An error is what someone is waiting for; it must not sit in a timer. The\n // microtask (rather than a direct flush) lets a same-tick burst — a failing\n // render logging three times — still leave as one request.\n if (!errorFlushQueued) {\n errorFlushQueued = true;\n queueMicrotask(() => {\n errorFlushQueued = false;\n flush();\n });\n }\n return;\n }\n\n if (wasEmpty) armTimer();\n}\n\nfunction armTimer(): void {\n if (flushTimer !== undefined) return;\n flushTimer = setTimeout(() => {\n flushTimer = undefined;\n flush();\n }, FLUSH_INTERVAL_MS);\n}\n\nfunction clearFlushTimer(): void {\n if (flushTimer === undefined) return;\n clearTimeout(flushTimer);\n flushTimer = undefined;\n}\n\n/** Pull entries off the front of the buffer without exceeding MAX_BATCH_BYTES. */\nfunction takeBatch(limit = MAX_BATCH_BYTES): DevLogPayload[] {\n const batch: DevLogPayload[] = [];\n let size = '{\"entries\":[]}'.length;\n while (buffer.length > 0) {\n const entry = buffer[0] as DevLogPayload;\n const cost = entryBytes(entry) + (batch.length > 0 ? 1 : 0);\n // Always take at least one entry: a single line that somehow exceeds the\n // limit must still leave, or it stalls every line behind it forever.\n if (batch.length > 0 && size + cost > limit) break;\n buffer.shift();\n bufferedBytes -= entryBytes(entry);\n batch.push(entry);\n size += cost;\n }\n if (buffer.length === 0) bufferedBytes = 0;\n return batch;\n}\n\n/**\n * Prepend the \"we lost lines\" notice, if any, so it travels with the next batch\n * rather than needing a request of its own.\n */\nfunction drainDroppedNotice(): void {\n if (droppedCount === 0) return;\n const notice: DevLogPayload = {\n level: \"warn\",\n msg: `[forge-devlog] dropped ${droppedCount} lines (${droppedReason})`,\n };\n buffer.unshift(notice);\n bufferedBytes += entryBytes(notice);\n droppedCount = 0;\n droppedReason = \"\";\n}\n\nfunction post(body: string): Promise<Response> {\n // The recursion guard must wrap the CALL, not the promise: a fetch polyfill\n // that logs does so synchronously from inside fetch().\n forwarding = true;\n try {\n return fetch(endpointUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body,\n // A log emitted during unload (the interesting case for a crash on\n // navigation) is cancelled without this.\n keepalive: true,\n // Never let log forwarding trip a credentials or CORS policy.\n credentials: \"omit\",\n });\n } finally {\n forwarding = false;\n }\n}\n\n/**\n * Send one request's worth of buffered output, if nothing is in flight.\n *\n * The single-flight rule is what guarantees the dev server prints lines in the\n * order they were logged, and it doubles as backpressure: a slow endpoint makes\n * batches bigger rather than making requests pile up.\n */\nfunction flush(): void {\n if (requestInFlight) return;\n drainDroppedNotice();\n if (buffer.length === 0) return;\n clearFlushTimer();\n\n const batch = legacyEndpoint ? takeBatch(0) : takeBatch();\n if (batch.length === 0) return;\n\n const body = legacyEndpoint\n ? JSON.stringify(batch[0])\n : JSON.stringify({ entries: batch } satisfies DevLogBatch);\n\n requestInFlight = true;\n post(body).then(\n (response) => {\n requestInFlight = false;\n if (!response.ok) {\n countDropped(batch.length, `endpoint returned ${response.status}`);\n } else if (!legacyEndpoint && !response.headers.get(PROTOCOL_HEADER)) {\n adoptLegacyMode(batch);\n }\n if (buffer.length > 0 || droppedCount > 0) flush();\n },\n () => {\n requestInFlight = false;\n countDropped(batch.length, \"request failed\");\n if (buffer.length > 0) flush();\n },\n );\n}\n\nfunction countDropped(count: number, reason: string): void {\n droppedCount += count;\n droppedReason = reason;\n}\n\n/**\n * A 2xx with no protocol header came from an endpoint written before batching.\n * It parsed `{entries:[...]}` as a v1 body, found no `msg`, and printed one\n * empty line — so this batch is lost unless it is re-sent one line at a time,\n * and so is everything after it.\n */\nfunction adoptLegacyMode(lost: DevLogPayload[]): void {\n legacyEndpoint = true;\n const notice: DevLogPayload = {\n level: \"warn\",\n msg:\n \"[forge-devlog] this dev server's /__forge/log endpoint predates batched \" +\n \"posts; update it (forge skill: debug) — falling back to one request per line\",\n };\n buffer.unshift(notice, ...lost);\n bufferedBytes += [notice, ...lost].reduce((n, e) => n + entryBytes(e), 0);\n}\n\n/**\n * Last-chance flush on page teardown.\n *\n * fetch — even with keepalive — is not reliable here, and an async flush is\n * hopeless: the event loop may not run again. sendBeacon hands the bytes to the\n * browser to deliver after the document is gone, which is precisely the case\n * where the last few log lines explain what happened.\n */\nfunction flushOnUnload(): void {\n drainDroppedNotice();\n if (buffer.length === 0) return;\n\n const canBeacon =\n typeof navigator !== \"undefined\" && typeof navigator.sendBeacon === \"function\";\n if (!canBeacon) {\n // No beacon: keepalive fetch is the only remaining option, and one request\n // has a better chance of leaving than a queue of them.\n flush();\n return;\n }\n\n forwarding = true;\n try {\n while (buffer.length > 0) {\n const batch = legacyEndpoint ? takeBatch(0) : takeBatch();\n const body = legacyEndpoint\n ? JSON.stringify(batch[0])\n : JSON.stringify({ entries: batch } satisfies DevLogBatch);\n // text/plain is not a decoration: sendBeacon with application/json is a\n // non-simple request and would need a CORS preflight it cannot make. The\n // endpoints parse the body regardless of Content-Type for this reason.\n const blob = new Blob([body], { type: \"text/plain;charset=UTF-8\" });\n if (!navigator.sendBeacon(endpointUrl, blob)) {\n // The beacon queue is full (64 KiB per origin). Nothing later will fit\n // either, so stop and account for what is left.\n countDropped(batch.length + buffer.length, \"beacon queue full\");\n buffer = [];\n bufferedBytes = 0;\n return;\n }\n }\n } finally {\n forwarding = false;\n }\n}\n\nfunction onWindowError(event: ErrorEvent): void {\n capture(\"error\", [\n event.error ??\n `${event.message} @ ${event.filename}:${event.lineno}:${event.colno}`,\n ]);\n}\n\nfunction onRejection(event: PromiseRejectionEvent): void {\n capture(\"error\", [\"unhandled rejection:\", event.reason]);\n}\n\nlet errorHandler: ((e: ErrorEvent) => void) | undefined;\nlet rejectionHandler: ((e: PromiseRejectionEvent) => void) | undefined;\nlet pageHideHandler: (() => void) | undefined;\nlet visibilityHandler: (() => void) | undefined;\n\n/**\n * Mirror console output and uncaught errors to the dev server, which prints\n * them into .forge/logs/<env>/frontend_<name>.log.\n *\n * The window listeners are the point of the feature as much as the console\n * override is: an uncaught TypeError or a rejected promise produces no console\n * call of its own, and those are exactly the failures worth seeing.\n *\n * Safe to call more than once; the second call is a no-op.\n */\nexport function installDevLogging(options: DevLoggingOptions): void {\n // `dev` is read off `options` rather than destructured with a default, and\n // checked FIRST. A bundler constant-folds `import.meta.env.DEV` to false at\n // the CALL SITE and can then drop the argument entirely, leaving a bare\n // `installDevLogging({})` — with a defaulted `dev` that call would install\n // the override in a production bundle. `options?.dev !== true` is therefore\n // fail-closed: absent, undefined, or anything non-true means do nothing.\n if (options?.dev !== true) return;\n if (installed) return;\n if (typeof window === \"undefined\") return; // SSR / prerender pass\n\n const { endpoint = DEV_LOG_ENDPOINT, mirrorToConsole = true } = options;\n installed = true;\n endpointUrl = endpoint;\n buffer = [];\n bufferedBytes = 0;\n droppedCount = 0;\n droppedReason = \"\";\n legacyEndpoint = false;\n requestInFlight = false;\n\n for (const level of LEVELS) {\n // Keep the ORIGINAL reference, not a bound copy: uninstall must restore\n // console.log to the identical function it replaced, so a second consumer\n // holding that reference (or a test asserting on it) sees no difference.\n const previous = console[level] as (...args: unknown[]) => void;\n original[level] = previous;\n console[level] = (...args: unknown[]) => {\n if (mirrorToConsole) previous.apply(console, args);\n capture(level, args);\n };\n }\n\n errorHandler = (e) => onWindowError(e);\n rejectionHandler = (e) => onRejection(e);\n pageHideHandler = () => flushOnUnload();\n window.addEventListener(\"error\", errorHandler);\n window.addEventListener(\"unhandledrejection\", rejectionHandler);\n window.addEventListener(\"pagehide\", pageHideHandler);\n\n if (typeof document !== \"undefined\") {\n // visibilitychange→hidden is the only teardown signal mobile Safari\n // reliably fires; pagehide alone loses the whole buffer there.\n visibilityHandler = () => {\n if (document.visibilityState === \"hidden\") flushOnUnload();\n };\n document.addEventListener(\"visibilitychange\", visibilityHandler);\n }\n}\n\n/** Restore the original console methods and remove the window listeners. */\nexport function uninstallDevLogging(): void {\n if (!installed) return;\n\n // Flush before restoring: whatever is buffered was logged while this was\n // installed, and a teardown is no reason to lose it.\n clearFlushTimer();\n flush();\n\n for (const level of LEVELS) {\n const previous = original[level];\n if (previous) console[level] = previous;\n delete original[level];\n }\n if (errorHandler) window.removeEventListener(\"error\", errorHandler);\n if (rejectionHandler)\n window.removeEventListener(\"unhandledrejection\", rejectionHandler);\n if (pageHideHandler) window.removeEventListener(\"pagehide\", pageHideHandler);\n if (visibilityHandler && typeof document !== \"undefined\")\n document.removeEventListener(\"visibilitychange\", visibilityHandler);\n errorHandler = undefined;\n rejectionHandler = undefined;\n pageHideHandler = undefined;\n visibilityHandler = undefined;\n installed = false;\n}\n\n/** Whether the console override is currently installed. Exposed for tests. */\nexport function devLoggingInstalled(): boolean {\n return installed;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"devlog.js","sourceRoot":"","sources":["../src/devlog.ts"],"names":[],"mappings":"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","sourcesContent":["// Dev-only browser log forwarding.\n//\n// A browser's console is a dead end: an agent debugging a scaffolded frontend\n// cannot open devtools, so every console line and — worse — every uncaught\n// error is invisible to it. The backend has no such gap (structured slog, the\n// observe ComponentChain, and forge's dev loop already tees each process's\n// stdout to .forge/logs/<env>/). This closes the frontend half by POSTing\n// browser console output to a dev-server endpoint that prints it, so it lands\n// in .forge/logs/<env>/frontend_<name>.log next to the server logs.\n//\n// ── Why this batches (protocol v2) ───────────────────────────────────────\n//\n// The first version sent one fetch POST per console call. Measured in a real\n// app that logs freely, that is ~80 requests/s at light load and 200+/s in a\n// burst, and those requests are not free bystanders: they share the browser's\n// six-connections-per-origin pool and the dev server's single event loop with\n// the app's own RPCs. In the same session, RPC latency averaged 37ms in quiet\n// seconds and 127ms in seconds carrying 200+ log lines. Worse, `keepalive`\n// requests draw on a 64 KiB in-flight browser quota shared across the page, so\n// a burst does not just slow things down — some of it gets rejected, and the\n// original `.catch(() => {})` made that indistinguishable from silence.\n//\n// So entries are buffered and flushed as a batch:\n//\n// - every FLUSH_INTERVAL_MS, timer armed when an entry lands in an empty\n// buffer (the common case: a few lines coalesce into one request),\n// - immediately once buffered output reaches MAX_BATCH_BYTES,\n// - on a microtask for anything error-level, so a crash never waits 250ms\n// while a same-tick burst still coalesces,\n// - synchronously via sendBeacon on pagehide / visibilitychange→hidden,\n// which is the only way output survives a navigation or a crash-reload.\n//\n// At most ONE request is in flight. Entries logged during it keep buffering and\n// go out when it settles, which is what makes the printed order the log order —\n// six parallel POSTs would interleave on the server.\n//\n// Nothing is ever dropped silently. A full buffer, a rejected request and an\n// over-quota request all increment a counter, and the next batch to leave\n// carries a synthetic warn line saying how many lines were lost and why. The\n// failure this replaces was a dead sink that looked exactly like working code.\n//\n// ── Legacy endpoints ─────────────────────────────────────────────────────\n//\n// The receiving endpoint is a scaffold-once file the PROJECT owns (a Vite\n// plugin / a Next.js route handler), so upgrading this package through npm will\n// routinely meet an endpoint that predates batching. Such an endpoint answers\n// 204 and prints one empty line for the whole batch — total, silent loss. A v2\n// endpoint therefore returns `X-Forge-Devlog: 2`, and a 2xx without it makes\n// this client fall back permanently to one v1 POST per line (plus one warn\n// line telling the developer to update the endpoint).\n//\n// DEV ONLY, and structurally so. The receiving endpoint is a Vite plugin\n// gated `apply: \"serve\"` / a Next.js route behind a NODE_ENV check, so it does\n// not exist in a production build; installDevLogging() additionally no-ops\n// unless it is handed dev: true. A production bundle therefore has no\n// endpoint to post to AND no code that would post.\n//\n// It is also reversible: uninstallDevLogging() restores the original console\n// methods, and the scaffolded call site is an ordinary line in main.tsx /\n// providers.tsx that a project can delete outright.\n\n/** Console methods this module mirrors. */\nconst LEVELS = [\"log\", \"info\", \"warn\", \"error\", \"debug\"] as const;\n\ntype Level = (typeof LEVELS)[number];\n\n/**\n * One log line on the wire.\n *\n * Also the whole v1 body: a legacy endpoint receives exactly this object, which\n * is why the shape is unchanged and still exported.\n */\nexport interface DevLogPayload {\n level: Level | \"error\";\n msg: string;\n}\n\n/** The v2 body: many lines, one request. */\nexport interface DevLogBatch {\n entries: DevLogPayload[];\n}\n\nexport interface DevLoggingOptions {\n /**\n * Whether to install at all. Pass `import.meta.env.DEV` (Vite) or\n * `process.env.NODE_ENV !== \"production\"` (Next.js). When false this is a\n * no-op, so the call site needs no conditional of its own.\n */\n dev: boolean;\n /** Endpoint the dev server serves. Defaults to the forge convention. */\n endpoint?: string;\n /**\n * Also keep writing to the real console. Default true — the browser devtools\n * experience should be unchanged; this feature ADDS a sink, it does not move\n * one.\n */\n mirrorToConsole?: boolean;\n}\n\nexport const DEV_LOG_ENDPOINT = \"/__forge/log\";\n\n/** Response header a batching endpoint sets. Its ABSENCE selects v1 fallback. */\nconst PROTOCOL_HEADER = \"X-Forge-Devlog\";\n\n/** Cap a single line so a runaway loop cannot fill the disk. Matches the endpoints. */\nconst MAX_LINE = 8_000;\n\n/** How long a line may wait for company before it is sent. */\nconst FLUSH_INTERVAL_MS = 250;\n\n/**\n * Per-request ceiling. Deliberately half of the browser's 64 KiB shared\n * `keepalive` in-flight quota: with one request in flight, a batch cannot be\n * the thing that pushes the page over it.\n */\nconst MAX_BATCH_BYTES = 32 * 1024;\n\n/** Buffer ceiling. Past it entries are dropped and counted, never queued forever. */\nconst MAX_BUFFER_ENTRIES = 5_000;\n\nconst original: Partial<Record<Level, (...args: unknown[]) => void>> = {};\nlet installed = false;\n\n// Guards against infinite recursion. The forwarder calls fetch(), and anything\n// that logs inside fetch (an interceptor, a polyfill, a devtools extension)\n// would otherwise re-enter the override forever. Such a line is still mirrored\n// to the real console — it is only forwarding that is suppressed.\nlet forwarding = false;\n\nlet endpointUrl = DEV_LOG_ENDPOINT;\nlet buffer: DevLogPayload[] = [];\nlet bufferedBytes = 0;\nlet flushTimer: ReturnType<typeof setTimeout> | undefined;\nlet requestInFlight = false;\nlet errorFlushQueued = false;\nlet droppedCount = 0;\nlet droppedReason = \"\";\n/** Set for good once an endpoint is known not to understand batches. */\nlet legacyEndpoint = false;\n\n/**\n * Render one console argument as a string.\n *\n * Circular structures and BigInt are the two cases a plain JSON.stringify\n * throws on, and the old fallback to String(value) turned both into\n * \"[object Object]\" — deleting exactly the shape you were trying to read. The\n * replacer keeps the object and names the cycle instead.\n */\nfunction render(value: unknown): string {\n if (typeof value === \"string\") return value;\n if (value instanceof Error) {\n return `${value.name}: ${value.message}${\n value.stack ? `\\n${value.stack}` : \"\"\n }`;\n }\n try {\n const seen = new WeakSet<object>();\n return (\n JSON.stringify(value, (_key, val: unknown) => {\n if (typeof val === \"bigint\") return `${val}n`;\n if (typeof val === \"object\" && val !== null) {\n if (seen.has(val)) return \"[Circular]\";\n seen.add(val);\n }\n return val;\n }) ?? String(value)\n );\n } catch {\n // DOM nodes, getters that throw — never let logging throw.\n return String(value);\n }\n}\n\nfunction truncate(msg: string): string {\n return msg.length > MAX_LINE ? `${msg.slice(0, MAX_LINE)}… (truncated)` : msg;\n}\n\nfunction entryBytes(entry: DevLogPayload): number {\n return JSON.stringify(entry).length;\n}\n\n/**\n * Buffer one line and decide whether it can wait.\n *\n * Arguments are rendered HERE rather than at flush time: a console argument is\n * a live object, and by the time a batch leaves 250ms later the array it logged\n * may have been mutated or emptied. The snapshot is the value at the call.\n */\nfunction capture(level: DevLogPayload[\"level\"], args: unknown[]): void {\n if (forwarding) return;\n\n if (buffer.length >= MAX_BUFFER_ENTRIES) {\n droppedCount += 1;\n droppedReason = \"buffer full\";\n return;\n }\n\n const entry: DevLogPayload = {\n level,\n msg: truncate(args.map(render).join(\" \")),\n };\n const wasEmpty = buffer.length === 0;\n buffer.push(entry);\n bufferedBytes += entryBytes(entry);\n\n if (bufferedBytes >= MAX_BATCH_BYTES) {\n flush();\n return;\n }\n\n if (level === \"error\") {\n // An error is what someone is waiting for; it must not sit in a timer. The\n // microtask (rather than a direct flush) lets a same-tick burst — a failing\n // render logging three times — still leave as one request.\n if (!errorFlushQueued) {\n errorFlushQueued = true;\n queueMicrotask(() => {\n errorFlushQueued = false;\n flush();\n });\n }\n return;\n }\n\n if (wasEmpty) armTimer();\n}\n\nfunction armTimer(): void {\n if (flushTimer !== undefined) return;\n flushTimer = setTimeout(() => {\n flushTimer = undefined;\n flush();\n }, FLUSH_INTERVAL_MS);\n}\n\nfunction clearFlushTimer(): void {\n if (flushTimer === undefined) return;\n clearTimeout(flushTimer);\n flushTimer = undefined;\n}\n\n/** Pull entries off the front of the buffer without exceeding MAX_BATCH_BYTES. */\nfunction takeBatch(limit = MAX_BATCH_BYTES): DevLogPayload[] {\n const batch: DevLogPayload[] = [];\n let size = '{\"entries\":[]}'.length;\n while (buffer.length > 0) {\n const entry = buffer[0] as DevLogPayload;\n const cost = entryBytes(entry) + (batch.length > 0 ? 1 : 0);\n // Always take at least one entry: a single line that somehow exceeds the\n // limit must still leave, or it stalls every line behind it forever.\n if (batch.length > 0 && size + cost > limit) break;\n buffer.shift();\n bufferedBytes -= entryBytes(entry);\n batch.push(entry);\n size += cost;\n }\n if (buffer.length === 0) bufferedBytes = 0;\n return batch;\n}\n\n/**\n * Prepend the \"we lost lines\" notice, if any, so it travels with the next batch\n * rather than needing a request of its own.\n */\nfunction drainDroppedNotice(): void {\n if (droppedCount === 0) return;\n const notice: DevLogPayload = {\n level: \"warn\",\n msg: `[forge-devlog] dropped ${droppedCount} lines (${droppedReason})`,\n };\n buffer.unshift(notice);\n bufferedBytes += entryBytes(notice);\n droppedCount = 0;\n droppedReason = \"\";\n}\n\nfunction post(body: string): Promise<Response> {\n // The recursion guard must wrap the CALL, not the promise: a fetch polyfill\n // that logs does so synchronously from inside fetch().\n forwarding = true;\n try {\n return fetch(endpointUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body,\n // A log emitted during unload (the interesting case for a crash on\n // navigation) is cancelled without this.\n keepalive: true,\n // Never let log forwarding trip a credentials or CORS policy.\n credentials: \"omit\",\n });\n } finally {\n forwarding = false;\n }\n}\n\n/**\n * Send one request's worth of buffered output, if nothing is in flight.\n *\n * The single-flight rule is what guarantees the dev server prints lines in the\n * order they were logged, and it doubles as backpressure: a slow endpoint makes\n * batches bigger rather than making requests pile up.\n */\nfunction flush(): void {\n if (requestInFlight) return;\n drainDroppedNotice();\n if (buffer.length === 0) return;\n clearFlushTimer();\n\n const batch = legacyEndpoint ? takeBatch(0) : takeBatch();\n if (batch.length === 0) return;\n\n const body = legacyEndpoint\n ? JSON.stringify(batch[0])\n : JSON.stringify({ entries: batch } satisfies DevLogBatch);\n\n requestInFlight = true;\n post(body).then(\n (response) => {\n requestInFlight = false;\n if (!response.ok) {\n countDropped(batch.length, `endpoint returned ${response.status}`);\n } else if (!legacyEndpoint && !response.headers.get(PROTOCOL_HEADER)) {\n adoptLegacyMode(batch);\n }\n if (buffer.length > 0 || droppedCount > 0) flush();\n },\n () => {\n requestInFlight = false;\n countDropped(batch.length, \"request failed\");\n if (buffer.length > 0) flush();\n },\n );\n}\n\nfunction countDropped(count: number, reason: string): void {\n droppedCount += count;\n droppedReason = reason;\n}\n\n/**\n * A 2xx with no protocol header came from an endpoint written before batching.\n * It parsed `{entries:[...]}` as a v1 body, found no `msg`, and printed one\n * empty line — so this batch is lost unless it is re-sent one line at a time,\n * and so is everything after it.\n */\nfunction adoptLegacyMode(lost: DevLogPayload[]): void {\n legacyEndpoint = true;\n const notice: DevLogPayload = {\n level: \"warn\",\n msg:\n \"[forge-devlog] this dev server's /__forge/log endpoint predates batched \" +\n \"posts; update it (forge skill: debug) — falling back to one request per line\",\n };\n buffer.unshift(notice, ...lost);\n bufferedBytes += [notice, ...lost].reduce((n, e) => n + entryBytes(e), 0);\n}\n\n/**\n * Last-chance flush on page teardown.\n *\n * fetch — even with keepalive — is not reliable here, and an async flush is\n * hopeless: the event loop may not run again. sendBeacon hands the bytes to the\n * browser to deliver after the document is gone, which is precisely the case\n * where the last few log lines explain what happened.\n */\nfunction flushOnUnload(): void {\n drainDroppedNotice();\n if (buffer.length === 0) return;\n\n const canBeacon =\n typeof navigator !== \"undefined\" &&\n typeof navigator.sendBeacon === \"function\";\n if (!canBeacon) {\n // No beacon: keepalive fetch is the only remaining option, and one request\n // has a better chance of leaving than a queue of them.\n flush();\n return;\n }\n\n forwarding = true;\n try {\n while (buffer.length > 0) {\n const batch = legacyEndpoint ? takeBatch(0) : takeBatch();\n const body = legacyEndpoint\n ? JSON.stringify(batch[0])\n : JSON.stringify({ entries: batch } satisfies DevLogBatch);\n // text/plain is not a decoration: sendBeacon with application/json is a\n // non-simple request and would need a CORS preflight it cannot make. The\n // endpoints parse the body regardless of Content-Type for this reason.\n const blob = new Blob([body], { type: \"text/plain;charset=UTF-8\" });\n if (!navigator.sendBeacon(endpointUrl, blob)) {\n // The beacon queue is full (64 KiB per origin). Nothing later will fit\n // either, so stop and account for what is left.\n countDropped(batch.length + buffer.length, \"beacon queue full\");\n buffer = [];\n bufferedBytes = 0;\n return;\n }\n }\n } finally {\n forwarding = false;\n }\n}\n\nfunction onWindowError(event: ErrorEvent): void {\n capture(\"error\", [\n event.error ??\n `${event.message} @ ${event.filename}:${event.lineno}:${event.colno}`,\n ]);\n}\n\nfunction onRejection(event: PromiseRejectionEvent): void {\n capture(\"error\", [\"unhandled rejection:\", event.reason]);\n}\n\nlet errorHandler: ((e: ErrorEvent) => void) | undefined;\nlet rejectionHandler: ((e: PromiseRejectionEvent) => void) | undefined;\nlet pageHideHandler: (() => void) | undefined;\nlet visibilityHandler: (() => void) | undefined;\n\n/**\n * Mirror console output and uncaught errors to the dev server, which prints\n * them into .forge/logs/<env>/frontend_<name>.log.\n *\n * The window listeners are the point of the feature as much as the console\n * override is: an uncaught TypeError or a rejected promise produces no console\n * call of its own, and those are exactly the failures worth seeing.\n *\n * Safe to call more than once; the second call is a no-op.\n */\nexport function installDevLogging(options: DevLoggingOptions): void {\n // `dev` is read off `options` rather than destructured with a default, and\n // checked FIRST. A bundler constant-folds `import.meta.env.DEV` to false at\n // the CALL SITE and can then drop the argument entirely, leaving a bare\n // `installDevLogging({})` — with a defaulted `dev` that call would install\n // the override in a production bundle. `options?.dev !== true` is therefore\n // fail-closed: absent, undefined, or anything non-true means do nothing.\n if (options?.dev !== true) return;\n if (installed) return;\n if (typeof window === \"undefined\") return; // SSR / prerender pass\n\n const { endpoint = DEV_LOG_ENDPOINT, mirrorToConsole = true } = options;\n installed = true;\n endpointUrl = endpoint;\n buffer = [];\n bufferedBytes = 0;\n droppedCount = 0;\n droppedReason = \"\";\n legacyEndpoint = false;\n requestInFlight = false;\n\n for (const level of LEVELS) {\n // Keep the ORIGINAL reference, not a bound copy: uninstall must restore\n // console.log to the identical function it replaced, so a second consumer\n // holding that reference (or a test asserting on it) sees no difference.\n const previous = console[level] as (...args: unknown[]) => void;\n original[level] = previous;\n console[level] = (...args: unknown[]) => {\n if (mirrorToConsole) previous.apply(console, args);\n capture(level, args);\n };\n }\n\n errorHandler = (e) => onWindowError(e);\n rejectionHandler = (e) => onRejection(e);\n pageHideHandler = () => flushOnUnload();\n window.addEventListener(\"error\", errorHandler);\n window.addEventListener(\"unhandledrejection\", rejectionHandler);\n window.addEventListener(\"pagehide\", pageHideHandler);\n\n if (typeof document !== \"undefined\") {\n // visibilitychange→hidden is the only teardown signal mobile Safari\n // reliably fires; pagehide alone loses the whole buffer there.\n visibilityHandler = () => {\n if (document.visibilityState === \"hidden\") flushOnUnload();\n };\n document.addEventListener(\"visibilitychange\", visibilityHandler);\n }\n}\n\n/** Restore the original console methods and remove the window listeners. */\nexport function uninstallDevLogging(): void {\n if (!installed) return;\n\n // Flush before restoring: whatever is buffered was logged while this was\n // installed, and a teardown is no reason to lose it.\n clearFlushTimer();\n flush();\n\n for (const level of LEVELS) {\n const previous = original[level];\n if (previous) console[level] = previous;\n delete original[level];\n }\n if (errorHandler) window.removeEventListener(\"error\", errorHandler);\n if (rejectionHandler)\n window.removeEventListener(\"unhandledrejection\", rejectionHandler);\n if (pageHideHandler) window.removeEventListener(\"pagehide\", pageHideHandler);\n if (visibilityHandler && typeof document !== \"undefined\")\n document.removeEventListener(\"visibilitychange\", visibilityHandler);\n errorHandler = undefined;\n rejectionHandler = undefined;\n pageHideHandler = undefined;\n visibilityHandler = undefined;\n installed = false;\n}\n\n/** Whether the console override is currently installed. Exposed for tests. */\nexport function devLoggingInstalled(): boolean {\n return installed;\n}\n"]}
|
package/dist/error-boundary.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ interface Props {
|
|
|
7
7
|
children: ReactNode;
|
|
8
8
|
/** Custom fallback UI. Defaults to DefaultErrorFallback. */
|
|
9
9
|
fallback?: (props: FallbackProps) => ReactNode;
|
|
10
|
-
/**
|
|
10
|
+
/** Extra reporter hook, called after the error is recorded with the browser telemetry SDK. */
|
|
11
11
|
onError?: (error: Error, info: ErrorInfo) => void;
|
|
12
12
|
/** When any value in this array changes, the boundary resets. */
|
|
13
13
|
resetKeys?: unknown[];
|
package/dist/error-boundary.js
CHANGED
|
@@ -8,6 +8,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
8
8
|
// dashboard widget, a <Resource> table — so one crashing panel degrades to a
|
|
9
9
|
// small fallback instead of taking the whole route (or the app) white.
|
|
10
10
|
import { Component, } from "react";
|
|
11
|
+
import { reportException } from "./error-reporter.js";
|
|
12
|
+
import { userMessage } from "./errors.js";
|
|
11
13
|
export class RuntimeErrorBoundary extends Component {
|
|
12
14
|
constructor(props) {
|
|
13
15
|
super(props);
|
|
@@ -17,8 +19,13 @@ export class RuntimeErrorBoundary extends Component {
|
|
|
17
19
|
return { error };
|
|
18
20
|
}
|
|
19
21
|
componentDidCatch(error, info) {
|
|
20
|
-
// Always log
|
|
22
|
+
// Always log, and record with the browser telemetry SDK (a no-op when
|
|
23
|
+
// none is running); the app can additionally forward to a sink via onError.
|
|
21
24
|
console.error("[runtime] error boundary caught:", error, info);
|
|
25
|
+
reportException(error, {
|
|
26
|
+
"react.component_stack": info.componentStack ?? "",
|
|
27
|
+
"error.handled_by": "RuntimeErrorBoundary",
|
|
28
|
+
});
|
|
22
29
|
this.props.onError?.(error, info);
|
|
23
30
|
}
|
|
24
31
|
componentDidUpdate(prev) {
|
|
@@ -58,6 +65,6 @@ export function DefaultErrorFallback({ error, reset, compact, }) {
|
|
|
58
65
|
if (compact) {
|
|
59
66
|
return (_jsxs("div", { role: "alert", className: "flex items-center justify-between gap-3 rounded-md border border-danger-border bg-danger-surface px-3 py-2 text-sm text-danger-ink", children: [_jsx("span", { className: "truncate", children: "Couldn't load this section." }), _jsx("button", { type: "button", onClick: reset, className: "shrink-0 font-medium text-danger underline hover:text-danger-hover", children: "Retry" })] }));
|
|
60
67
|
}
|
|
61
|
-
return (_jsxs("div", { role: "alert", className: "flex min-h-[12rem] flex-col items-center justify-center gap-3 rounded-lg border border-danger-border bg-danger-surface p-8 text-center", children: [_jsx("h2", { className: "text-lg font-semibold text-danger-ink", children: "Something went wrong" }), _jsx("p", { className: "max-w-md text-sm text-danger-ink", children: "This section ran into an unexpected error. You can try again \u2014 the rest of the app is still working." }), _jsx("p", { className: "max-w-md
|
|
68
|
+
return (_jsxs("div", { role: "alert", className: "flex min-h-[12rem] flex-col items-center justify-center gap-3 rounded-lg border border-danger-border bg-danger-surface p-8 text-center", children: [_jsx("h2", { className: "text-lg font-semibold text-danger-ink", children: "Something went wrong" }), _jsx("p", { className: "max-w-md text-sm text-danger-ink", children: "This section ran into an unexpected error. You can try again \u2014 the rest of the app is still working." }), _jsx("p", { className: "max-w-md text-sm text-danger-ink", children: userMessage(error) }), _jsx("button", { type: "button", onClick: reset, className: "mt-1 rounded-md bg-danger px-4 py-2 text-sm font-medium text-on-danger hover:bg-danger-hover", children: "Try again" })] }));
|
|
62
69
|
}
|
|
63
70
|
//# sourceMappingURL=error-boundary.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-boundary.js","sourceRoot":"","sources":["../src/error-boundary.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,sEAAsE;AACtE,EAAE;AACF,2EAA2E;AAC3E,sEAAsE;AACtE,uEAAuE;AACvE,6EAA6E;AAC7E,uEAAuE;AACvE,OAAO,EACL,SAAS,GAIV,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"error-boundary.js","sourceRoot":"","sources":["../src/error-boundary.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\n// Part of @reliantlabs/forge-web-runtime — the web twin of forge/pkg.\n//\n// A React error boundary with a designed fallback. Next.js file-convention\n// boundaries (app/error.tsx, app/global-error.tsx) catch route render\n// crashes; this component boundary lets you ALSO isolate a subtree — a\n// dashboard widget, a <Resource> table — so one crashing panel degrades to a\n// small fallback instead of taking the whole route (or the app) white.\nimport {\n Component,\n type ErrorInfo,\n type ReactElement,\n type ReactNode,\n} from \"react\";\n\nimport { reportException } from \"./error-reporter.js\";\nimport { userMessage } from \"./errors.js\";\n\ninterface FallbackProps {\n error: Error;\n reset: () => void;\n}\n\ninterface Props {\n children: ReactNode;\n /** Custom fallback UI. Defaults to DefaultErrorFallback. */\n fallback?: (props: FallbackProps) => ReactNode;\n /** Extra reporter hook, called after the error is recorded with the browser telemetry SDK. */\n onError?: (error: Error, info: ErrorInfo) => void;\n /** When any value in this array changes, the boundary resets. */\n resetKeys?: unknown[];\n /** Compact single-line fallback (for small subtrees). */\n compact?: boolean;\n}\n\ninterface State {\n error: Error | null;\n}\n\nexport class RuntimeErrorBoundary extends Component<Props, State> {\n constructor(props: Props) {\n super(props);\n this.state = { error: null };\n }\n\n static getDerivedStateFromError(error: Error): State {\n return { error };\n }\n\n componentDidCatch(error: Error, info: ErrorInfo): void {\n // Always log, and record with the browser telemetry SDK (a no-op when\n // none is running); the app can additionally forward to a sink via onError.\n console.error(\"[runtime] error boundary caught:\", error, info);\n reportException(error, {\n \"react.component_stack\": info.componentStack ?? \"\",\n \"error.handled_by\": \"RuntimeErrorBoundary\",\n });\n this.props.onError?.(error, info);\n }\n\n componentDidUpdate(prev: Props): void {\n if (\n this.state.error &&\n !shallowEqual(prev.resetKeys, this.props.resetKeys)\n ) {\n this.reset();\n }\n }\n\n reset = (): void => {\n this.setState({ error: null });\n };\n\n render(): ReactNode {\n const { error } = this.state;\n if (!error) {\n return this.props.children;\n }\n if (this.props.fallback) {\n return this.props.fallback({ error, reset: this.reset });\n }\n return (\n <DefaultErrorFallback\n error={error}\n reset={this.reset}\n compact={this.props.compact}\n />\n );\n }\n}\n\nfunction shallowEqual(a?: unknown[], b?: unknown[]): boolean {\n if (a === b) {\n return true;\n }\n if (!a || !b || a.length !== b.length) {\n return false;\n }\n return a.every((v, i) => Object.is(v, b[i]));\n}\n\n// The `: ReactElement` here and on every other exported component is\n// load-bearing, not decoration — see README, \"One .d.ts for React 18 and 19\".\n// Inferring it makes the published declaration name a type whose SPELLING\n// differs between the two @types/react majors this package supports.\nexport function DefaultErrorFallback({\n error,\n reset,\n compact,\n}: FallbackProps & { compact?: boolean }): ReactElement {\n if (compact) {\n return (\n <div\n role=\"alert\"\n className=\"flex items-center justify-between gap-3 rounded-md border border-danger-border bg-danger-surface px-3 py-2 text-sm text-danger-ink\"\n >\n <span className=\"truncate\">Couldn't load this section.</span>\n <button\n type=\"button\"\n onClick={reset}\n className=\"shrink-0 font-medium text-danger underline hover:text-danger-hover\"\n >\n Retry\n </button>\n </div>\n );\n }\n return (\n <div\n role=\"alert\"\n className=\"flex min-h-[12rem] flex-col items-center justify-center gap-3 rounded-lg border border-danger-border bg-danger-surface p-8 text-center\"\n >\n <h2 className=\"text-lg font-semibold text-danger-ink\">\n Something went wrong\n </h2>\n <p className=\"max-w-md text-sm text-danger-ink\">\n This section ran into an unexpected error. You can try again — the rest\n of the app is still working.\n </p>\n {/* userMessage, never error.message — the same display rule as\n <Resource>'s error rung. */}\n <p className=\"max-w-md text-sm text-danger-ink\">{userMessage(error)}</p>\n <button\n type=\"button\"\n onClick={reset}\n className=\"mt-1 rounded-md bg-danger px-4 py-2 text-sm font-medium text-on-danger hover:bg-danger-hover\"\n >\n Try again\n </button>\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Span/log attribute values the recorder accepts. */
|
|
2
|
+
export type ExceptionAttributes = Record<string, string | number | boolean>;
|
|
3
|
+
type ExceptionReporter = (error: unknown, attributes?: ExceptionAttributes) => void;
|
|
4
|
+
/**
|
|
5
|
+
* Install (or clear, with null) the recorder reportException forwards to.
|
|
6
|
+
* Called by initBrowserTelemetry; an app has no reason to call it directly.
|
|
7
|
+
*/
|
|
8
|
+
export declare function setExceptionReporter(next: ExceptionReporter | null): void;
|
|
9
|
+
/**
|
|
10
|
+
* reportException hands a caught error to the browser telemetry SDK, if one
|
|
11
|
+
* is running. Never throws: reporting an error must not become a second
|
|
12
|
+
* error.
|
|
13
|
+
*
|
|
14
|
+
* Returns true when the SDK has the error and false when no SDK is running —
|
|
15
|
+
* so a caller can fall back to console.error and an error never vanishes:
|
|
16
|
+
*
|
|
17
|
+
* if (!reportException(error)) console.error(error);
|
|
18
|
+
*
|
|
19
|
+
* Use it where React or Next catch an error for you (error boundaries,
|
|
20
|
+
* `error.tsx`). The SDK already records uncaught exceptions, unhandled
|
|
21
|
+
* rejections and anything passed to console.error, and React logs a
|
|
22
|
+
* boundary-caught error to console.error itself, so the recorder installed
|
|
23
|
+
* by `/telemetry` skips an error the SDK has already seen: one failure is
|
|
24
|
+
* one record, never two.
|
|
25
|
+
*/
|
|
26
|
+
export declare function reportException(error: unknown, attributes?: ExceptionAttributes): boolean;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Part of @reliantlabs/forge-web-runtime — the web twin of forge/pkg.
|
|
2
|
+
//
|
|
3
|
+
// The seam between "something threw" and "somebody recorded it".
|
|
4
|
+
//
|
|
5
|
+
// The error boundary and the Next.js error files live in the barrel, which
|
|
6
|
+
// every frontend imports — including the Vite SPA and React Native ones that
|
|
7
|
+
// never install the HyperDX browser SDK. So the barrel cannot import the SDK.
|
|
8
|
+
// It imports this dependency-free registry instead, and the `/telemetry`
|
|
9
|
+
// subpath (which does import the SDK) installs the real recorder here once the
|
|
10
|
+
// SDK has initialised. Until then — SSR, telemetry off, SDK still loading —
|
|
11
|
+
// reportException is a no-op, which is what makes it safe to call from any
|
|
12
|
+
// catch block unconditionally.
|
|
13
|
+
/**
|
|
14
|
+
* Reports which recorder is installed, if any. reportException's return
|
|
15
|
+
* value is "the telemetry SDK has this error" — true both when it was just
|
|
16
|
+
* recorded and when it had already been recorded by another route.
|
|
17
|
+
*/
|
|
18
|
+
let reporter = null;
|
|
19
|
+
/**
|
|
20
|
+
* Install (or clear, with null) the recorder reportException forwards to.
|
|
21
|
+
* Called by initBrowserTelemetry; an app has no reason to call it directly.
|
|
22
|
+
*/
|
|
23
|
+
export function setExceptionReporter(next) {
|
|
24
|
+
reporter = next;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* reportException hands a caught error to the browser telemetry SDK, if one
|
|
28
|
+
* is running. Never throws: reporting an error must not become a second
|
|
29
|
+
* error.
|
|
30
|
+
*
|
|
31
|
+
* Returns true when the SDK has the error and false when no SDK is running —
|
|
32
|
+
* so a caller can fall back to console.error and an error never vanishes:
|
|
33
|
+
*
|
|
34
|
+
* if (!reportException(error)) console.error(error);
|
|
35
|
+
*
|
|
36
|
+
* Use it where React or Next catch an error for you (error boundaries,
|
|
37
|
+
* `error.tsx`). The SDK already records uncaught exceptions, unhandled
|
|
38
|
+
* rejections and anything passed to console.error, and React logs a
|
|
39
|
+
* boundary-caught error to console.error itself, so the recorder installed
|
|
40
|
+
* by `/telemetry` skips an error the SDK has already seen: one failure is
|
|
41
|
+
* one record, never two.
|
|
42
|
+
*/
|
|
43
|
+
export function reportException(error, attributes) {
|
|
44
|
+
if (!reporter) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
reporter(error, attributes);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Telemetry must never break the app.
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=error-reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-reporter.js","sourceRoot":"","sources":["../src/error-reporter.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,iEAAiE;AACjE,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,8EAA8E;AAC9E,yEAAyE;AACzE,+EAA+E;AAC/E,4EAA4E;AAC5E,2EAA2E;AAC3E,+BAA+B;AAU/B;;;;GAIG;AAEH,IAAI,QAAQ,GAA6B,IAAI,CAAC;AAE9C;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAA8B;IACjE,QAAQ,GAAG,IAAI,CAAC;AAClB,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,eAAe,CAC7B,KAAc,EACd,UAAgC;IAEhC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,CAAC;QACH,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,sCAAsC;IACxC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["// Part of @reliantlabs/forge-web-runtime — the web twin of forge/pkg.\n//\n// The seam between \"something threw\" and \"somebody recorded it\".\n//\n// The error boundary and the Next.js error files live in the barrel, which\n// every frontend imports — including the Vite SPA and React Native ones that\n// never install the HyperDX browser SDK. So the barrel cannot import the SDK.\n// It imports this dependency-free registry instead, and the `/telemetry`\n// subpath (which does import the SDK) installs the real recorder here once the\n// SDK has initialised. Until then — SSR, telemetry off, SDK still loading —\n// reportException is a no-op, which is what makes it safe to call from any\n// catch block unconditionally.\n\n/** Span/log attribute values the recorder accepts. */\nexport type ExceptionAttributes = Record<string, string | number | boolean>;\n\ntype ExceptionReporter = (\n error: unknown,\n attributes?: ExceptionAttributes,\n) => void;\n\n/**\n * Reports which recorder is installed, if any. reportException's return\n * value is \"the telemetry SDK has this error\" — true both when it was just\n * recorded and when it had already been recorded by another route.\n */\n\nlet reporter: ExceptionReporter | null = null;\n\n/**\n * Install (or clear, with null) the recorder reportException forwards to.\n * Called by initBrowserTelemetry; an app has no reason to call it directly.\n */\nexport function setExceptionReporter(next: ExceptionReporter | null): void {\n reporter = next;\n}\n\n/**\n * reportException hands a caught error to the browser telemetry SDK, if one\n * is running. Never throws: reporting an error must not become a second\n * error.\n *\n * Returns true when the SDK has the error and false when no SDK is running —\n * so a caller can fall back to console.error and an error never vanishes:\n *\n * if (!reportException(error)) console.error(error);\n *\n * Use it where React or Next catch an error for you (error boundaries,\n * `error.tsx`). The SDK already records uncaught exceptions, unhandled\n * rejections and anything passed to console.error, and React logs a\n * boundary-caught error to console.error itself, so the recorder installed\n * by `/telemetry` skips an error the SDK has already seen: one failure is\n * one record, never two.\n */\nexport function reportException(\n error: unknown,\n attributes?: ExceptionAttributes,\n): boolean {\n if (!reporter) {\n return false;\n }\n try {\n reporter(error, attributes);\n } catch {\n // Telemetry must never break the app.\n }\n return true;\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,15 @@ export { ConnectClientError, FORGE_ERROR_REASON_HEADER, isRetryableCode, normali
|
|
|
13
13
|
* no imports, so there is no dependency footprint for a subpath to fence off.
|
|
14
14
|
*/
|
|
15
15
|
export { createBasePath, joinBasePath, normalizeBasePath, type BasePathHelpers, } from "./basepath.js";
|
|
16
|
+
/**
|
|
17
|
+
* Money and basis points as integers: format minor units through Intl, and
|
|
18
|
+
* parse form input back to minor units without rounding.
|
|
19
|
+
*
|
|
20
|
+
* The web counterpart of forge/pkg/money — an int64 count of the currency's
|
|
21
|
+
* minor unit plus an ISO 4217 code, exact past 2^53. Barrel-exported for the
|
|
22
|
+
* same reason as the base-path helpers: it imports nothing.
|
|
23
|
+
*/
|
|
24
|
+
export { basisPointsToInput, currencyMinorDigits, formatBasisPoints, formatMinorUnits, minorUnitsToInput, parseBasisPoints, parseMinorUnits, type BasisPointsOptions, type MoneyOptions, type ParseBasisPointsOptions, type ParseMoneyOptions, } from "./money.js";
|
|
16
25
|
/** W3C trace-context propagation. */
|
|
17
26
|
export { freshTraceparent, injectTraceContext, setTraceSampled, TRACEPARENT_RE, } from "./trace.js";
|
|
18
27
|
/** Session: the signed-in user, derived from the app's auth state and JWT claims. */
|
|
@@ -27,8 +36,12 @@ export { RuntimeToastHost, type RuntimeToast, type RuntimeToastHostProps, type R
|
|
|
27
36
|
export { RouteGuard, type RouteGuardProps } from "./route-guard.js";
|
|
28
37
|
/** Generic data-table container: the tristate ladder, filter and cursor pagination. */
|
|
29
38
|
export { Resource, type ResourceColumn, type ResourceProps, type ResourceStatus, } from "./resource.js";
|
|
30
|
-
/**
|
|
31
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Record a caught error with the browser telemetry SDK, when one is running.
|
|
41
|
+
* A no-op otherwise, so it is safe in any catch block. The SDK itself arrives
|
|
42
|
+
* through the "/telemetry" subpath, never the barrel.
|
|
43
|
+
*/
|
|
44
|
+
export { reportException, setExceptionReporter, type ExceptionAttributes, } from "./error-reporter.js";
|
|
32
45
|
/**
|
|
33
46
|
* Dev-only browser log forwarding: mirrors console output and uncaught errors
|
|
34
47
|
* to the dev server, which prints them into
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,15 @@ export { ConnectClientError, FORGE_ERROR_REASON_HEADER, isRetryableCode, normali
|
|
|
31
31
|
* no imports, so there is no dependency footprint for a subpath to fence off.
|
|
32
32
|
*/
|
|
33
33
|
export { createBasePath, joinBasePath, normalizeBasePath, } from "./basepath.js";
|
|
34
|
+
/**
|
|
35
|
+
* Money and basis points as integers: format minor units through Intl, and
|
|
36
|
+
* parse form input back to minor units without rounding.
|
|
37
|
+
*
|
|
38
|
+
* The web counterpart of forge/pkg/money — an int64 count of the currency's
|
|
39
|
+
* minor unit plus an ISO 4217 code, exact past 2^53. Barrel-exported for the
|
|
40
|
+
* same reason as the base-path helpers: it imports nothing.
|
|
41
|
+
*/
|
|
42
|
+
export { basisPointsToInput, currencyMinorDigits, formatBasisPoints, formatMinorUnits, minorUnitsToInput, parseBasisPoints, parseMinorUnits, } from "./money.js";
|
|
34
43
|
/** W3C trace-context propagation. */
|
|
35
44
|
export { freshTraceparent, injectTraceContext, setTraceSampled, TRACEPARENT_RE, } from "./trace.js";
|
|
36
45
|
/** Session: the signed-in user, derived from the app's auth state and JWT claims. */
|
|
@@ -45,8 +54,12 @@ export { RuntimeToastHost, } from "./toast-host.js";
|
|
|
45
54
|
export { RouteGuard } from "./route-guard.js";
|
|
46
55
|
/** Generic data-table container: the tristate ladder, filter and cursor pagination. */
|
|
47
56
|
export { Resource, } from "./resource.js";
|
|
48
|
-
/**
|
|
49
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Record a caught error with the browser telemetry SDK, when one is running.
|
|
59
|
+
* A no-op otherwise, so it is safe in any catch block. The SDK itself arrives
|
|
60
|
+
* through the "/telemetry" subpath, never the barrel.
|
|
61
|
+
*/
|
|
62
|
+
export { reportException, setExceptionReporter, } from "./error-reporter.js";
|
|
50
63
|
/**
|
|
51
64
|
* Dev-only browser log forwarding: mirrors console output and uncaught errors
|
|
52
65
|
* to the dev server, which prints them into
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,EAAE;AACF,yEAAyE;AACzE,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,4EAA4E;AAC5E,8EAA8E;AAC9E,gEAAgE;AAChE,EAAE;AACF,4EAA4E;AAC5E,0EAA0E;AAC1E,4CAA4C;AAE5C,yEAAyE;AACzE,6EAA6E;AAC7E,4EAA4E;AAC5E,4EAA4E;AAC5E,sEAAsE;AAEtE,mCAAmC;AACnC,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,GAEjB,MAAM,mBAAmB,CAAC;AAE3B;;;GAGG;AACH,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,WAAW,GACZ,MAAM,aAAa,CAAC;AAErB;;;;;;GAMG;AACH,OAAO,EACL,cAAc,EACd,YAAY,EACZ,iBAAiB,GAElB,MAAM,eAAe,CAAC;AAEvB,qCAAqC;AACrC,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,cAAc,GACf,MAAM,YAAY,CAAC;AAEpB,qFAAqF;AACrF,OAAO,EACL,SAAS,EACT,eAAe,EACf,UAAU,GAIX,MAAM,cAAc,CAAC;AAEtB,gFAAgF;AAChF,OAAO,EAAE,YAAY,EAA0B,MAAM,gBAAgB,CAAC;AAEtE,+EAA+E;AAC/E,OAAO,EACL,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAE7B,yEAAyE;AACzE,OAAO,EACL,gBAAgB,GAMjB,MAAM,iBAAiB,CAAC;AAEzB,gEAAgE;AAChE,OAAO,EAAE,UAAU,EAAwB,MAAM,kBAAkB,CAAC;AAEpE,uFAAuF;AACvF,OAAO,EACL,QAAQ,GAIT,MAAM,eAAe,CAAC;AAEvB
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,EAAE;AACF,yEAAyE;AACzE,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,4EAA4E;AAC5E,8EAA8E;AAC9E,gEAAgE;AAChE,EAAE;AACF,4EAA4E;AAC5E,0EAA0E;AAC1E,4CAA4C;AAE5C,yEAAyE;AACzE,6EAA6E;AAC7E,4EAA4E;AAC5E,4EAA4E;AAC5E,sEAAsE;AAEtE,mCAAmC;AACnC,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,yBAAyB,EACzB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,GAEjB,MAAM,mBAAmB,CAAC;AAE3B;;;GAGG;AACH,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,WAAW,GACZ,MAAM,aAAa,CAAC;AAErB;;;;;;GAMG;AACH,OAAO,EACL,cAAc,EACd,YAAY,EACZ,iBAAiB,GAElB,MAAM,eAAe,CAAC;AAEvB;;;;;;;GAOG;AACH,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,GAKhB,MAAM,YAAY,CAAC;AAEpB,qCAAqC;AACrC,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,cAAc,GACf,MAAM,YAAY,CAAC;AAEpB,qFAAqF;AACrF,OAAO,EACL,SAAS,EACT,eAAe,EACf,UAAU,GAIX,MAAM,cAAc,CAAC;AAEtB,gFAAgF;AAChF,OAAO,EAAE,YAAY,EAA0B,MAAM,gBAAgB,CAAC;AAEtE,+EAA+E;AAC/E,OAAO,EACL,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAE7B,yEAAyE;AACzE,OAAO,EACL,gBAAgB,GAMjB,MAAM,iBAAiB,CAAC;AAEzB,gEAAgE;AAChE,OAAO,EAAE,UAAU,EAAwB,MAAM,kBAAkB,CAAC;AAEpE,uFAAuF;AACvF,OAAO,EACL,QAAQ,GAIT,MAAM,eAAe,CAAC;AAEvB;;;;GAIG;AACH,OAAO,EACL,eAAe,EACf,oBAAoB,GAErB,MAAM,qBAAqB,CAAC;AAE7B;;;;;;GAMG;AACH,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,GAGpB,MAAM,aAAa,CAAC","sourcesContent":["// @reliantlabs/forge-web-runtime — the web twin of forge/pkg.\n//\n// Public barrel. Import batteries from \"@reliantlabs/forge-web-runtime\".\n//\n// Everything here is MECHANISM: the transport interceptor stack, the session,\n// the error boundary, the toast queue, the <Resource> tristate ladder, W3C\n// trace propagation, client RUM. Nothing here reaches into an application —\n// app-shaped values (auth state, event subscriptions, toast markup) arrive as\n// props, so an app can restyle or replace its own files freely.\n//\n// Presentation that a project is meant to own — the component library under\n// src/components/ui, globals.css, nav, layout — deliberately stays in the\n// scaffold and is NOT part of this package.\n\n// The per-group docs below are JSDoc, not `//`, on purpose: TypeScript's\n// declaration emit preserves JSDoc and DROPS line comments, and dist/ is now\n// the whole of what this package publishes. `forge project libraries` reads\n// this inventory out of the published barrel, and a consumer's editor shows\n// it on hover — both would get symbols with no explanation otherwise.\n\n/** Transport interceptor stack. */\nexport {\n authInterceptor,\n buildRuntimeInterceptors,\n errorNormalizeInterceptor,\n headerInterceptor,\n retryInterceptor,\n traceInterceptor,\n type RuntimeInterceptorConfig,\n} from \"./interceptors.js\";\n\n/**\n * Typed client errors, and the backend-framing strippers every surface that\n * SHOWS an error goes through.\n */\nexport {\n ConnectClientError,\n FORGE_ERROR_REASON_HEADER,\n isRetryableCode,\n normalizeError,\n stripServerFraming,\n userMessage,\n} from \"./errors.js\";\n\n/**\n * Mount-prefix helpers, for the URLs Next.js's router never sees — absolute\n * URLs handed to external systems that round-trip back into the app.\n *\n * Barrel-exported rather than behind a subpath: three string functions with\n * no imports, so there is no dependency footprint for a subpath to fence off.\n */\nexport {\n createBasePath,\n joinBasePath,\n normalizeBasePath,\n type BasePathHelpers,\n} from \"./basepath.js\";\n\n/**\n * Money and basis points as integers: format minor units through Intl, and\n * parse form input back to minor units without rounding.\n *\n * The web counterpart of forge/pkg/money — an int64 count of the currency's\n * minor unit plus an ISO 4217 code, exact past 2^53. Barrel-exported for the\n * same reason as the base-path helpers: it imports nothing.\n */\nexport {\n basisPointsToInput,\n currencyMinorDigits,\n formatBasisPoints,\n formatMinorUnits,\n minorUnitsToInput,\n parseBasisPoints,\n parseMinorUnits,\n type BasisPointsOptions,\n type MoneyOptions,\n type ParseBasisPointsOptions,\n type ParseMoneyOptions,\n} from \"./money.js\";\n\n/** W3C trace-context propagation. */\nexport {\n freshTraceparent,\n injectTraceContext,\n setTraceSampled,\n TRACEPARENT_RE,\n} from \"./trace.js\";\n\n/** Session: the signed-in user, derived from the app's auth state and JWT claims. */\nexport {\n decodeJwt,\n SessionProvider,\n useSession,\n type JwtClaims,\n type Session,\n type SessionAuth,\n} from \"./session.js\";\n\n/** The app-shell provider set: session + error boundary + toast host in one. */\nexport { RuntimeShell, type RuntimeShellProps } from \"./providers.js\";\n\n/** Error boundary with a designed crash fallback, for a route or a subtree. */\nexport {\n DefaultErrorFallback,\n RuntimeErrorBoundary,\n} from \"./error-boundary.js\";\n\n/** The toast queue. The app supplies the event source and the markup. */\nexport {\n RuntimeToastHost,\n type RuntimeToast,\n type RuntimeToastHostProps,\n type RuntimeToastPayload,\n type RuntimeToastSink,\n type RuntimeToastVariant,\n} from \"./toast-host.js\";\n\n/** Route guarding: render a page only to a signed-in caller. */\nexport { RouteGuard, type RouteGuardProps } from \"./route-guard.js\";\n\n/** Generic data-table container: the tristate ladder, filter and cursor pagination. */\nexport {\n Resource,\n type ResourceColumn,\n type ResourceProps,\n type ResourceStatus,\n} from \"./resource.js\";\n\n/**\n * Record a caught error with the browser telemetry SDK, when one is running.\n * A no-op otherwise, so it is safe in any catch block. The SDK itself arrives\n * through the \"/telemetry\" subpath, never the barrel.\n */\nexport {\n reportException,\n setExceptionReporter,\n type ExceptionAttributes,\n} from \"./error-reporter.js\";\n\n/**\n * Dev-only browser log forwarding: mirrors console output and uncaught errors\n * to the dev server, which prints them into\n * `.forge/logs/<env>/frontend_<name>.log`. No-ops unless `dev: true`, and the\n * receiving endpoint exists only in a dev server — so a production bundle\n * neither posts nor has anywhere to post to.\n */\nexport {\n DEV_LOG_ENDPOINT,\n devLoggingInstalled,\n installDevLogging,\n uninstallDevLogging,\n type DevLoggingOptions,\n type DevLogPayload,\n} from \"./devlog.js\";\n"]}
|
package/dist/mock-transport.d.ts
CHANGED
|
@@ -20,7 +20,14 @@ export interface MockScenarioRegistry {
|
|
|
20
20
|
byName(name: string): MockScenario | undefined;
|
|
21
21
|
defaultScenario: MockScenario;
|
|
22
22
|
}
|
|
23
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* One CRUD arm of an entity's dispatch table.
|
|
25
|
+
*
|
|
26
|
+
* The List arm needs nothing beyond these three fields to honour filters,
|
|
27
|
+
* paging and total_count: the REQUEST schema arrives on every call as the
|
|
28
|
+
* Connect method's `input`, and the entity schema is the element type of
|
|
29
|
+
* `itemsField` on `responseSchema`.
|
|
30
|
+
*/
|
|
24
31
|
export interface MockListRpc {
|
|
25
32
|
rpc: string;
|
|
26
33
|
responseSchema: DescMessage;
|