@reliantlabs/forge-web-runtime 0.3.1 → 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.d.ts +10 -1
- package/dist/devlog.js +334 -23
- 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/service-hooks.js +9 -0
- package/dist/service-hooks.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 +13 -46
- package/dist/otel.d.ts +0 -31
- package/dist/otel.js +0 -94
- package/dist/otel.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money.js","sourceRoot":"","sources":["../src/money.ts"],"names":[],"mappings":"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","sourcesContent":["// Part of @reliantlabs/forge-web-runtime — the web twin of forge/pkg.\n//\n// Money and basis points as INTEGERS, formatted and parsed at the edge of the\n// UI. This is the web counterpart of forge/pkg/money: an amount is an integer\n// count of its currency's minor unit (cents for USD, yen for JPY, fils for\n// KWD) plus an ISO 4217 code, and no float ever sits between the wire and the\n// screen. protobuf-es surfaces an int64 as a `bigint`; keep it one until it is\n// rendered, and turn user input straight back into one.\n//\n// formatMinorUnits(123456n) -> \"$1,234.56\"\n// formatMinorUnits(1234n, { currency: \"JPY\" }) -> \"¥1,234\"\n// formatMinorUnits(123456n, { currency: \"EUR\", locale: \"de-DE\" }) -> \"1.234,56 €\"\n// parseMinorUnits(\"$1,234.5\") -> 123450n\n// parseMinorUnits(\"1.234\") -> null (USD has 2 digits)\n// minorUnitsToInput(123450n) -> \"1234.50\"\n// currencyMinorDigits(\"KWD\") -> 3\n// formatBasisPoints(825) -> \"8.25%\"\n// parseBasisPoints(\"8.25%\") -> 825\n// basisPointsToInput(850) -> \"8.5\"\n//\n// THE RULES\n//\n// - Amounts are `bigint | number`. A `number` must be an integer: anything\n// else throws a RangeError, because 12.5 \"cents\" is a major-unit value\n// that leaked into a minor-unit field and rounding it would hide the bug.\n// `null` / `undefined` format as \"—\" and write an input value of \"\".\n// - `currency` defaults to \"USD\", and \"\" means the default too (a proto3\n// string field that was never set arrives as \"\"). A malformed code throws\n// Intl's RangeError. `locale` defaults to \"en-US\".\n// - A currency's minor-unit digits come from Intl (CLDR) —\n// `resolvedOptions().maximumFractionDigits` — and formatting and parsing\n// both use that one source, so they cannot disagree. Note CLDR, not ISO\n// 4217, is the authority: HUF and a few others differ.\n// - Formatting is exact at any size. The amount is handed to Intl as a\n// decimal STRING (Intl.NumberFormat v3 — Node 20+, every evergreen browser\n// since 2023), never as a Number, so int64 extremes print to the last\n// digit. An engine without v3 coerces the string to a Number, which is\n// still exact below about 10^15 minor units.\n// - Parsing returns null rather than guessing. Accepted: ASCII digits or\n// the locale's own (Arabic-Indic for ar-EG), surrounding and grouping\n// whitespace, the locale's group and decimal separators, the currency's\n// own symbol or ISO code before or after the number, a leading \"+\", and —\n// only with `allowNegative` — a leading \"-\" or \"−\". Anything Intl\n// rendered for the same options therefore parses back. Refused:\n// more fraction digits than the currency has (never rounded), another\n// currency's symbol, a misplaced group separator (\"12,50\" typed into an\n// en-US field is a decimal comma, so it is not read as 1,250.00), and an\n// amount outside the int64 range the wire carries.\n// - minorUnitsToInput writes the locale's decimal separator with no\n// grouping, so it round-trips through parseMinorUnits given the same\n// options. For an `<input type=\"number\">` keep the en-US default: that\n// element's value is always \".\"-decimal whatever the page's locale.\n// - Basis points are an integer count of 0.01%. Parsing allows at most two\n// decimals of a percent and caps at 100% (10000) unless `max` says\n// otherwise — the typical rate (tax, discount, commission) lives in\n// 0–100%, and the cap catches \"825\" typed where \"8.25\" was meant.\n//\n// Differences from forge/pkg/money: the Go Format is locale-neutral with its\n// own symbol table, while this renders through Intl for the caller's locale;\n// and the Go Parse accepts a negative amount, while this one makes negatives\n// opt-in, because most amounts a form collects cannot be below zero.\n//\n// This module is in the BARREL: it imports nothing, so there is no optional\n// dependency for a subpath to fence off.\n\n/** Currency and locale for a money helper. Both optional. */\nexport interface MoneyOptions {\n /** ISO 4217 code. Default \"USD\"; \"\" (a proto3 unset string) also means USD. */\n currency?: string;\n /** BCP 47 locale for separators and symbol placement. Default \"en-US\". */\n locale?: string;\n}\n\n/** Options for {@link parseMinorUnits}. */\nexport interface ParseMoneyOptions extends MoneyOptions {\n /** Accept a leading \"-\" / \"−\". Default false: a minus sign returns null. */\n allowNegative?: boolean;\n}\n\n/** Locale for a basis-point helper. */\nexport interface BasisPointsOptions {\n /** BCP 47 locale for separators and the percent sign. Default \"en-US\". */\n locale?: string;\n}\n\n/** Options for {@link parseBasisPoints}. */\nexport interface ParseBasisPointsOptions extends BasisPointsOptions {\n /** Accept a leading \"-\" / \"−\". Default false: a minus sign returns null. */\n allowNegative?: boolean;\n /** Largest magnitude accepted, in basis points. Default 10000 (100%). */\n max?: number;\n}\n\nconst DEFAULT_CURRENCY = \"USD\";\nconst DEFAULT_LOCALE = \"en-US\";\nconst UNSET = \"—\";\nconst BASIS_POINT_DIGITS = 2; // 1 bp = 0.01 percent\nconst DEFAULT_MAX_BASIS_POINTS = 10_000;\nconst INT64_MAX = 9_223_372_036_854_775_807n;\nconst INT64_MIN = -9_223_372_036_854_775_808n;\n\n/**\n * formatMinorUnits renders an integer minor-unit amount as currency for a\n * locale: 123456n → \"$1,234.56\", 1234n JPY → \"¥1,234\". Exact at any size.\n * Unset renders \"—\"; a non-integer number throws RangeError.\n */\nexport function formatMinorUnits(\n minor: bigint | number | null | undefined,\n options: MoneyOptions = {},\n): string {\n if (minor === null || minor === undefined) return UNSET;\n const currency = currencyOf(options);\n const decimal = toDecimalString(\n toInteger(minor),\n currencyMinorDigits(currency),\n );\n return currencyFormat(localeOf(options), currency).format(decimal);\n}\n\n/**\n * parseMinorUnits reads user input such as \"1,234.5\", \"$1234.56\" or \"12\" into\n * minor units. Returns null for anything it cannot represent exactly — too\n * many decimals, junk, a misplaced separator, a negative amount without\n * `allowNegative`, or a value beyond int64 — so a form rejects it instead of\n * silently rounding.\n */\nexport function parseMinorUnits(\n input: string,\n options: ParseMoneyOptions = {},\n): bigint | null {\n const currency = currencyOf(options);\n const value = parseScaled(\n input,\n currencyMinorDigits(currency),\n moneyGrammar(localeOf(options), currency),\n options.allowNegative ?? false,\n );\n if (value === null || value > INT64_MAX || value < INT64_MIN) return null;\n return value;\n}\n\n/**\n * minorUnitsToInput renders minor units as the value of an editable field:\n * 123456n → \"1234.56\" (de-DE: \"1234,56\"). No grouping, every minor digit\n * shown, and the result parses back through parseMinorUnits. Unset → \"\";\n * a non-integer number throws RangeError.\n */\nexport function minorUnitsToInput(\n minor: bigint | number | null | undefined,\n options: MoneyOptions = {},\n): string {\n if (minor === null || minor === undefined) return \"\";\n const currency = currencyOf(options);\n const decimal = toDecimalString(\n toInteger(minor),\n currencyMinorDigits(currency),\n );\n return decimal.replace(\n \".\",\n moneyGrammar(localeOf(options), currency).decimal,\n );\n}\n\n/**\n * currencyMinorDigits returns how many decimal digits a currency's minor unit\n * has — USD 2, JPY 0, KWD 3 — as Intl reports it. Useful for an input's\n * `step` or a validation message. Throws RangeError on a malformed code.\n */\nexport function currencyMinorDigits(currency?: string): number {\n const code = currency || DEFAULT_CURRENCY;\n // CLDR's digits are per currency, not per locale, so en-US answers for all.\n return cached(\n minorDigitsCache,\n code,\n () =>\n currencyFormat(DEFAULT_LOCALE, code).resolvedOptions()\n .maximumFractionDigits ?? 2,\n );\n}\n\n/**\n * formatBasisPoints renders an integer basis-point rate as a percentage:\n * 825 → \"8.25%\", 1000 → \"10%\". Unset renders \"—\"; a non-integer number\n * (a percent passed where basis points belong) throws RangeError.\n */\nexport function formatBasisPoints(\n bps: bigint | number | null | undefined,\n options: BasisPointsOptions = {},\n): string {\n if (bps === null || bps === undefined) return UNSET;\n // Intl's percent style multiplies by 100, so hand it the fraction: 825 bp\n // is \"0.0825\".\n const fraction = toDecimalString(toInteger(bps), BASIS_POINT_DIGITS + 2);\n return percentFormat(localeOf(options)).format(fraction);\n}\n\n/**\n * parseBasisPoints reads a percentage such as \"8.25\" or \"8.25%\" into basis\n * points (825). Returns null for more than two decimals, junk, a negative\n * rate without `allowNegative`, or a magnitude above `max` (default 100%).\n */\nexport function parseBasisPoints(\n input: string,\n options: ParseBasisPointsOptions = {},\n): number | null {\n const value = parseScaled(\n input,\n BASIS_POINT_DIGITS,\n percentGrammar(localeOf(options)),\n options.allowNegative ?? false,\n );\n if (value === null) return null;\n const bps = Number(value);\n const max = options.max ?? DEFAULT_MAX_BASIS_POINTS;\n if (!Number.isSafeInteger(bps) || Math.abs(bps) > max) return null;\n return bps;\n}\n\n/**\n * basisPointsToInput renders basis points as the value of an editable percent\n * field: 825 → \"8.25\", 850 → \"8.5\", 1000 → \"10\". Parses back through\n * parseBasisPoints. Unset → \"\".\n */\nexport function basisPointsToInput(\n bps: bigint | number | null | undefined,\n options: BasisPointsOptions = {},\n): string {\n if (bps === null || bps === undefined) return \"\";\n const decimal = toDecimalString(toInteger(bps), BASIS_POINT_DIGITS).replace(\n /\\.?0+$/,\n \"\",\n );\n return decimal.replace(\".\", percentGrammar(localeOf(options)).decimal);\n}\n\n// --- internals ---------------------------------------------------------------\n\n/** A decimal string Intl.NumberFormat v3 formats exactly. */\ntype DecimalString = `${number}`;\n\n/** How a locale spells a number, as far as parsing it back needs to know. */\ninterface NumberGrammar {\n decimal: string;\n /** Group separator; whitespace-like separators are normalised to \" \". */\n group: string;\n /** Symbols and codes that may precede or follow the number, longest first. */\n affixes: string[];\n /** A well-formed grouped integer part: \"1,234,567\" or \"12,34,567\". */\n groupedInteger: RegExp;\n /** The locale's own digits mapped to ASCII; empty when it uses ASCII. */\n nativeDigits: Map<string, string>;\n}\n\nconst minorDigitsCache = new Map<string, number>();\nconst formatCache = new Map<string, Intl.NumberFormat>();\nconst grammarCache = new Map<string, NumberGrammar>();\n\nfunction currencyOf(options: MoneyOptions): string {\n return options.currency || DEFAULT_CURRENCY;\n}\n\nfunction localeOf(options: { locale?: string }): string {\n return options.locale || DEFAULT_LOCALE;\n}\n\nfunction toInteger(value: bigint | number): bigint {\n if (typeof value === \"bigint\") return value;\n if (!Number.isInteger(value)) {\n throw new RangeError(\n `forge-web-runtime money: ${value} is not an integer — amounts and rates are integer minor units / basis points`,\n );\n }\n return BigInt(value);\n}\n\n/** toDecimalString(123456n, 2) → \"1234.56\"; (-5n, 2) → \"-0.05\"; (7n, 0) → \"7\". */\nfunction toDecimalString(value: bigint, digits: number): DecimalString {\n const negative = value < 0n;\n const magnitude = (negative ? -value : value)\n .toString()\n .padStart(digits + 1, \"0\");\n const whole = digits === 0 ? magnitude : magnitude.slice(0, -digits);\n const fraction = digits === 0 ? \"\" : `.${magnitude.slice(-digits)}`;\n return `${negative ? \"-\" : \"\"}${whole}${fraction}` as DecimalString;\n}\n\nfunction cached<T>(cache: Map<string, T>, key: string, build: () => T): T {\n let value = cache.get(key);\n if (value === undefined) {\n value = build();\n cache.set(key, value);\n }\n return value;\n}\n\nfunction currencyFormat(locale: string, currency: string): Intl.NumberFormat {\n return cached(\n formatCache,\n `currency|${locale}|${currency}`,\n () => new Intl.NumberFormat(locale, { style: \"currency\", currency }),\n );\n}\n\nfunction percentFormat(locale: string): Intl.NumberFormat {\n return cached(\n formatCache,\n `percent|${locale}`,\n () =>\n new Intl.NumberFormat(locale, {\n style: \"percent\",\n maximumFractionDigits: BASIS_POINT_DIGITS,\n }),\n );\n}\n\nfunction moneyGrammar(locale: string, currency: string): NumberGrammar {\n return cached(grammarCache, `currency|${locale}|${currency}`, () => {\n // Read the separators off a formatter forced to show a fraction and a\n // group, so a zero-digit currency (JPY) still reveals its decimal mark.\n const separators = new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency,\n minimumFractionDigits: 1,\n maximumFractionDigits: 1,\n }).formatToParts(1234567.5);\n // The currency's own symbols: as this locale writes them, and as en-US\n // does (\"¥\" beside ja-JP's full-width \"¥\"), each in the default and the\n // narrow form (\"CA$\" and \"$\"). Never another currency's.\n const symbols = [locale, DEFAULT_LOCALE].flatMap((symbolLocale) =>\n ([\"symbol\", \"narrowSymbol\"] as const).map(\n (currencyDisplay) =>\n new Intl.NumberFormat(symbolLocale, {\n style: \"currency\",\n currency,\n currencyDisplay,\n })\n .formatToParts(1)\n .find((part) => part.type === \"currency\")?.value ?? \"\",\n ),\n );\n return buildGrammar(locale, separators, [...symbols, currency]);\n });\n}\n\nfunction percentGrammar(locale: string): NumberGrammar {\n return cached(grammarCache, `percent|${locale}`, () => {\n const parts = new Intl.NumberFormat(locale, {\n style: \"percent\",\n minimumFractionDigits: 1,\n maximumFractionDigits: 1,\n }).formatToParts(12345.675);\n const percentSign =\n parts.find((part) => part.type === \"percentSign\")?.value ?? \"%\";\n return buildGrammar(locale, parts, [percentSign, \"%\"]);\n });\n}\n\nfunction buildGrammar(\n locale: string,\n parts: Intl.NumberFormatPart[],\n affixes: string[],\n): NumberGrammar {\n // Locales such as ar-EG render Arabic-Indic digits; read them back as\n // ASCII so a formatted amount parses. ASCII digits are always accepted too.\n const digitFormat = new Intl.NumberFormat(locale, { useGrouping: false });\n const nativeDigits = new Map<string, string>();\n for (let digit = 0; digit <= 9; digit++) {\n const native = digitFormat.format(digit);\n if (native !== String(digit)) nativeDigits.set(native, String(digit));\n }\n const decimal = parts.find((part) => part.type === \"decimal\")?.value ?? \".\";\n const rawGroup = parts.find((part) => part.type === \"group\")?.value ?? \",\";\n const group = /^\\s$/.test(rawGroup) ? \" \" : rawGroup;\n const g = escapeRegExp(group);\n return {\n decimal,\n group,\n affixes: [...new Set(affixes.map(stripInvisible).filter(Boolean))].sort(\n (a, b) => b.length - a.length,\n ),\n // First group 1–3 digits, inner groups 2–3 (Western and Indian\n // grouping), last group exactly 3. The last-group rule is the one that\n // refuses a decimal comma typed where the locale groups with commas.\n groupedInteger: new RegExp(`^\\\\d{1,3}(?:${g}\\\\d{2,3})*${g}\\\\d{3}$`),\n nativeDigits,\n };\n}\n\n/**\n * parseScaled reads a decimal number in the given grammar and returns it\n * scaled by 10^digits as an exact bigint, or null when the input is not a\n * number in that grammar or carries more than `digits` fraction digits.\n */\nfunction parseScaled(\n input: string,\n digits: number,\n grammar: NumberGrammar,\n allowNegative: boolean,\n): bigint | null {\n let text = stripInvisible(input).trim();\n if (grammar.nativeDigits.size > 0) {\n text = Array.from(\n text,\n (char) => grammar.nativeDigits.get(char) ?? char,\n ).join(\"\");\n }\n let sign = \"\";\n const takeSign = (): void => {\n if (sign === \"\" && /^[+\\-\\u2212]/.test(text)) {\n sign = text.charAt(0);\n text = text.slice(1).trimStart();\n }\n };\n takeSign(); // \"-$12\"\n text = stripAffix(text, grammar.affixes);\n takeSign(); // \"$-12\"\n\n // Any run of whitespace left inside the number is grouping, held to the\n // same placement rule as the locale's own separator: \"1 234\" passes,\n // \"12 50\" does not.\n text = text.replace(/\\s+/g, grammar.group);\n\n const [integerPart = \"\", fractionPart, extra] = text.split(grammar.decimal);\n if (extra !== undefined) return null;\n if (integerPart === \"\" && !fractionPart) return null;\n if (\n integerPart !== \"\" &&\n !/^\\d+$/.test(integerPart) &&\n !grammar.groupedInteger.test(integerPart)\n ) {\n return null;\n }\n if (\n fractionPart !== undefined &&\n (!/^\\d*$/.test(fractionPart) || fractionPart.length > digits)\n ) {\n return null;\n }\n\n const negative = sign === \"-\" || sign === \"\\u2212\";\n if (negative && !allowNegative) return null;\n\n const whole = BigInt(integerPart.split(grammar.group).join(\"\") || \"0\");\n const fraction = BigInt((fractionPart ?? \"\").padEnd(digits, \"0\") || \"0\");\n const magnitude = whole * 10n ** BigInt(digits) + fraction;\n return negative ? -magnitude : magnitude;\n}\n\n/** Remove one currency symbol / code / percent sign from either end. */\nfunction stripAffix(text: string, affixes: string[]): string {\n for (const affix of affixes) {\n // Case-insensitive so \"usd 12\" reads like \"USD 12\". Compared slice by\n // slice: upper-casing the whole input could change its length.\n if (text.slice(0, affix.length).toUpperCase() === affix.toUpperCase()) {\n return text.slice(affix.length).trimStart();\n }\n if (text.slice(-affix.length).toUpperCase() === affix.toUpperCase()) {\n return text.slice(0, -affix.length).trimEnd();\n }\n }\n return text;\n}\n\n/**\n * Drop the bidi controls Intl places around numbers in right-to-left locales\n * (LRM, RLM, ALM, embeddings, isolates). Invisible, so a pasted amount\n * carries them without the user knowing.\n */\nfunction stripInvisible(text: string): string {\n return text.replace(/[\\u061c\\u200e\\u200f\\u202a-\\u202e\\u2066-\\u2069]/g, \"\");\n}\n\nfunction escapeRegExp(text: string): string {\n return text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n"]}
|
package/dist/resource.js
CHANGED
|
@@ -12,6 +12,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
12
12
|
// cursor-based (page_token / next_page_token in Connect list RPCs): the page
|
|
13
13
|
// owns the cursor stack and passes onNextPage/onPrevPage.
|
|
14
14
|
import { useEffect, useState } from "react";
|
|
15
|
+
import { userMessage } from "./errors.js";
|
|
15
16
|
export function Resource({ status, data, columns, rowKey, title, actions, error, onRetry, onRowClick, emptyTitle = "Nothing here yet", emptyMessage = "No records match.", filter, onFilterChange, filterPlaceholder = "Search…", filterDebounceMs = 300, onNextPage, onPrevPage, hasNextPage, hasPrevPage, isFetching, }) {
|
|
16
17
|
const [query, setQuery] = useState(filter ?? "");
|
|
17
18
|
// Keep local input in sync when the parent resets the filter externally.
|
|
@@ -66,10 +67,14 @@ export function Resource({ status, data, columns, rowKey, title, actions, error,
|
|
|
66
67
|
// no-aria-hidden-on-focusable — correctly, since a hidden focusable node is
|
|
67
68
|
// a focus trap for screen-reader users.
|
|
68
69
|
function SkeletonRows({ rows, cols }) {
|
|
69
|
-
return (_jsx("tr", { children: _jsxs("td", { colSpan: cols, className: "p-0", children: [_jsx("span", { className: "sr-only", children: "Loading\u2026" }), _jsx("div", { "aria-hidden": "true", className: "divide-y divide-border", children: Array.from({ length: rows }).map((_, r) => (_jsx("div", { className: "flex items-center gap-4 px-4 py-3", children: Array.from({ length: cols }).map((__, c) => (_jsx("div", { className: "h-4 flex-1 animate-pulse rounded bg-surface-muted" }, c))) }, r))) })] }) }));
|
|
70
|
+
return (_jsx("tr", { children: _jsxs("td", { colSpan: cols, className: "p-0", children: [_jsx("span", { className: "sr-only", children: "Loading\u2026" }), _jsx("div", { "aria-hidden": "true", className: "divide-y divide-border", children: Array.from({ length: rows }).map((_, r) => (_jsx("div", { className: "flex items-center gap-4 px-4 py-3", children: Array.from({ length: cols }).map((__, c) => (_jsx("div", { className: "h-4 flex-1 animate-pulse motion-reduce:animate-none rounded bg-surface-muted" }, c))) }, r))) })] }) }));
|
|
70
71
|
}
|
|
72
|
+
// ErrorState shows userMessage(error), never error.message: a ConnectError's
|
|
73
|
+
// message carries the "[code] " transport framing, which is not copy a user
|
|
74
|
+
// should read. It is body text in the empty rung's style, not a monospace
|
|
75
|
+
// debug readout.
|
|
71
76
|
function ErrorState({ error, onRetry, }) {
|
|
72
|
-
return (_jsxs("div", { role: "alert", className: "flex flex-col items-center gap-2 text-center", children: [_jsx("p", { className: "text-sm font-medium text-danger-ink", children: "Couldn't load data" }), error
|
|
77
|
+
return (_jsxs("div", { role: "alert", className: "flex flex-col items-center gap-2 text-center", children: [_jsx("p", { className: "text-sm font-medium text-danger-ink", children: "Couldn't load data" }), error ? (_jsx("p", { className: "max-w-md text-sm text-ink-muted", children: userMessage(error) })) : null, onRetry ? (_jsx("button", { type: "button", onClick: onRetry, className: "mt-1 rounded-md bg-danger px-3 py-1.5 text-sm font-medium text-on-danger hover:bg-danger-hover", children: "Try again" })) : null] }));
|
|
73
78
|
}
|
|
74
79
|
function EmptyState({ title, message }) {
|
|
75
80
|
return (_jsxs("div", { className: "flex flex-col items-center gap-1 text-center", children: [_jsx("p", { className: "text-sm font-medium text-ink", children: title }), _jsx("p", { className: "text-sm text-ink-muted", children: message })] }));
|
package/dist/resource.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource.js","sourceRoot":"","sources":["../src/resource.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// <Resource> — the owned-once data-table container. It encapsulates the\n// loading / error / empty / data tristate ladder, a server-side (debounced)\n// filter, and cursor pagination, so list pages stop re-hand-rolling the\n// ladder and stop client-side-filtering a single page cap.\n//\n// Pairs with the app's useQueryResource hook: map a query result to\n// { status, data, error } and hand it here. Pagination is\n// cursor-based (page_token / next_page_token in Connect list RPCs): the page\n// owns the cursor stack and passes onNextPage/onPrevPage.\nimport { useEffect, useState, type ReactElement, type ReactNode } from \"react\";\n\nexport interface ResourceColumn<T> {\n header: string;\n cell: (row: T) => ReactNode;\n /** Extra classes for the cell + header (alignment, width). */\n className?: string;\n}\n\nexport type ResourceStatus = \"loading\" | \"error\" | \"success\";\n\nexport interface ResourceProps<T> {\n status: ResourceStatus;\n data: T[] | undefined;\n columns: ResourceColumn<T>[];\n rowKey: (row: T) => string;\n\n title?: string;\n /** Header-right slot, e.g. a \"New\" button. */\n actions?: ReactNode;\n error?: Error | null;\n onRetry?: () => void;\n onRowClick?: (row: T) => void;\n\n emptyTitle?: string;\n emptyMessage?: string;\n\n // Server-side filter (debounced). Omit onFilterChange to hide the search box.\n filter?: string;\n onFilterChange?: (value: string) => void;\n filterPlaceholder?: string;\n filterDebounceMs?: number;\n\n // Cursor pagination. Omit the handlers to hide the footer.\n onNextPage?: () => void;\n onPrevPage?: () => void;\n hasNextPage?: boolean;\n hasPrevPage?: boolean;\n /** True during a background refetch (page change / filter change). */\n isFetching?: boolean;\n}\n\nexport function Resource<T>({\n status,\n data,\n columns,\n rowKey,\n title,\n actions,\n error,\n onRetry,\n onRowClick,\n emptyTitle = \"Nothing here yet\",\n emptyMessage = \"No records match.\",\n filter,\n onFilterChange,\n filterPlaceholder = \"Search…\",\n filterDebounceMs = 300,\n onNextPage,\n onPrevPage,\n hasNextPage,\n hasPrevPage,\n isFetching,\n}: ResourceProps<T>): ReactElement {\n const [query, setQuery] = useState(filter ?? \"\");\n\n // Keep local input in sync when the parent resets the filter externally.\n useEffect(() => {\n setQuery(filter ?? \"\");\n }, [filter]);\n\n // Debounce filter changes so each keystroke doesn't fire an RPC.\n useEffect(() => {\n if (!onFilterChange || query === (filter ?? \"\")) {\n return;\n }\n const handle = setTimeout(() => onFilterChange(query), filterDebounceMs);\n return () => clearTimeout(handle);\n }, [query, filter, onFilterChange, filterDebounceMs]);\n\n const showFooter = Boolean(onNextPage || onPrevPage);\n const colCount = columns.length;\n const captionText = title ?? \"Results\";\n\n return (\n <section className=\"flex flex-col gap-4\">\n {(title || actions) && (\n <header className=\"flex items-center justify-between gap-4\">\n {title ? (\n <h1 className=\"text-xl font-semibold text-ink\">{title}</h1>\n ) : (\n <span />\n )}\n {actions ? (\n <div className=\"flex items-center gap-2\">{actions}</div>\n ) : null}\n </header>\n )}\n\n {onFilterChange ? (\n <input\n type=\"search\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={filterPlaceholder}\n aria-label={title ? `Search ${title.toLowerCase()}` : \"Search\"}\n className=\"w-full max-w-sm rounded-md border border-border bg-surface px-3 py-2 text-sm text-ink shadow-sm focus:border-accent focus:outline-none focus:ring-1 focus:ring-accent\"\n />\n ) : null}\n\n <div className=\"overflow-x-auto rounded-lg border border-border bg-surface\">\n <table className=\"min-w-full divide-y divide-border text-sm\">\n <caption className=\"sr-only\">{captionText}</caption>\n <thead className=\"bg-surface-muted\">\n <tr>\n {columns.map((col, i) => (\n <th\n key={i}\n scope=\"col\"\n className={`px-4 py-2.5 text-left font-medium text-ink-muted ${\n col.className ?? \"\"\n }`}\n >\n {col.header}\n </th>\n ))}\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {status === \"loading\" ? (\n <SkeletonRows rows={5} cols={colCount} />\n ) : status === \"error\" ? (\n <tr>\n <td colSpan={colCount} className=\"px-4 py-10\">\n <ErrorState error={error} onRetry={onRetry} />\n </td>\n </tr>\n ) : !data || data.length === 0 ? (\n <tr>\n <td colSpan={colCount} className=\"px-4 py-12\">\n <EmptyState title={emptyTitle} message={emptyMessage} />\n </td>\n </tr>\n ) : (\n data.map((row) => (\n <tr\n key={rowKey(row)}\n onClick={onRowClick ? () => onRowClick(row) : undefined}\n // Keyboard-operable when the row navigates: Enter/Space\n // activate it, and it takes focus in tab order.\n role={onRowClick ? \"button\" : undefined}\n tabIndex={onRowClick ? 0 : undefined}\n onKeyDown={\n onRowClick\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onRowClick(row);\n }\n }\n : undefined\n }\n className={\n onRowClick\n ? \"cursor-pointer transition-colors hover:bg-surface-muted focus:bg-surface-muted focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-accent\"\n : undefined\n }\n >\n {columns.map((col, i) => (\n <td\n key={i}\n className={`px-4 py-2.5 text-ink ${col.className ?? \"\"}`}\n >\n {col.cell(row)}\n </td>\n ))}\n </tr>\n ))\n )}\n </tbody>\n </table>\n </div>\n\n {showFooter ? (\n <footer className=\"flex items-center justify-end gap-2\">\n {isFetching ? (\n <span className=\"mr-auto text-xs text-ink-subtle\">Loading…</span>\n ) : null}\n <button\n type=\"button\"\n onClick={onPrevPage}\n disabled={!hasPrevPage}\n className=\"rounded-md border border-border px-3 py-1.5 text-sm font-medium text-ink-muted hover:bg-surface-muted disabled:cursor-not-allowed disabled:opacity-40\"\n >\n Previous\n </button>\n <button\n type=\"button\"\n onClick={onNextPage}\n disabled={!hasNextPage}\n className=\"rounded-md border border-border px-3 py-1.5 text-sm font-medium text-ink-muted hover:bg-surface-muted disabled:cursor-not-allowed disabled:opacity-40\"\n >\n Next\n </button>\n </footer>\n ) : null}\n </section>\n );\n}\n\n// SkeletonRows is the loading rung of the tristate ladder: a shimmer the\n// width of the table while the first page is in flight.\n//\n// It is ONE row with a colSpan cell, matching the error and empty rungs\n// above — not `rows` × `cols` real table cells. That shape is what makes it\n// accessible, and the reasoning is worth keeping:\n//\n// - The shimmer is pure decoration. It carries no data, so it is wrapped\n// in aria-hidden and the cell carries one sr-only \"Loading…\" instead.\n// Assistive tech hears the state once, not once per placeholder.\n// - A grid of empty <td>s announced as a table of blank cells, and read\n// as a real 5-row result set by anything counting rows. The placeholder\n// geometry is now plain <div>s inside the single cell, so the table has\n// exactly the structure it claims to have at every moment.\n// - Empty cells also tripped jsx-a11y/control-has-associated-label under\n// the eslint config forge scaffolds. That was the symptom that surfaced\n// this; the row-shape mismatch above is the actual defect.\n//\n// aria-hidden goes on the inner <div>, never on the <td> or <tr>: jsx-a11y\n// treats table elements as focusable, so hiding one is\n// no-aria-hidden-on-focusable — correctly, since a hidden focusable node is\n// a focus trap for screen-reader users.\nfunction SkeletonRows({ rows, cols }: { rows: number; cols: number }) {\n return (\n <tr>\n <td colSpan={cols} className=\"p-0\">\n <span className=\"sr-only\">Loading…</span>\n <div aria-hidden=\"true\" className=\"divide-y divide-border\">\n {Array.from({ length: rows }).map((_, r) => (\n <div key={r} className=\"flex items-center gap-4 px-4 py-3\">\n {Array.from({ length: cols }).map((__, c) => (\n <div\n key={c}\n className=\"h-4 flex-1 animate-pulse rounded bg-surface-muted\"\n />\n ))}\n </div>\n ))}\n </div>\n </td>\n </tr>\n );\n}\n\nfunction ErrorState({\n error,\n onRetry,\n}: {\n error?: Error | null;\n onRetry?: () => void;\n}) {\n return (\n <div role=\"alert\" className=\"flex flex-col items-center gap-2 text-center\">\n <p className=\"text-sm font-medium text-danger-ink\">\n Couldn't load data\n </p>\n {error?.message ? (\n <p className=\"max-w-md font-mono text-xs text-danger\">\n {error.message}\n </p>\n ) : null}\n {onRetry ? (\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"mt-1 rounded-md bg-danger px-3 py-1.5 text-sm font-medium text-on-danger hover:bg-danger-hover\"\n >\n Try again\n </button>\n ) : null}\n </div>\n );\n}\n\nfunction EmptyState({ title, message }: { title: string; message: string }) {\n return (\n <div className=\"flex flex-col items-center gap-1 text-center\">\n <p className=\"text-sm font-medium text-ink\">{title}</p>\n <p className=\"text-sm text-ink-muted\">{message}</p>\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"resource.js","sourceRoot":"","sources":["../src/resource.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// <Resource> — the owned-once data-table container. It encapsulates the\n// loading / error / empty / data tristate ladder, a server-side (debounced)\n// filter, and cursor pagination, so list pages stop re-hand-rolling the\n// ladder and stop client-side-filtering a single page cap.\n//\n// Pairs with the app's useQueryResource hook: map a query result to\n// { status, data, error } and hand it here. Pagination is\n// cursor-based (page_token / next_page_token in Connect list RPCs): the page\n// owns the cursor stack and passes onNextPage/onPrevPage.\nimport { useEffect, useState, type ReactElement, type ReactNode } from \"react\";\n\nimport { userMessage } from \"./errors.js\";\n\nexport interface ResourceColumn<T> {\n header: string;\n cell: (row: T) => ReactNode;\n /** Extra classes for the cell + header (alignment, width). */\n className?: string;\n}\n\nexport type ResourceStatus = \"loading\" | \"error\" | \"success\";\n\nexport interface ResourceProps<T> {\n status: ResourceStatus;\n data: T[] | undefined;\n columns: ResourceColumn<T>[];\n rowKey: (row: T) => string;\n\n title?: string;\n /** Header-right slot, e.g. a \"New\" button. */\n actions?: ReactNode;\n error?: Error | null;\n onRetry?: () => void;\n onRowClick?: (row: T) => void;\n\n emptyTitle?: string;\n emptyMessage?: string;\n\n // Server-side filter (debounced). Omit onFilterChange to hide the search box.\n filter?: string;\n onFilterChange?: (value: string) => void;\n filterPlaceholder?: string;\n filterDebounceMs?: number;\n\n // Cursor pagination. Omit the handlers to hide the footer.\n onNextPage?: () => void;\n onPrevPage?: () => void;\n hasNextPage?: boolean;\n hasPrevPage?: boolean;\n /** True during a background refetch (page change / filter change). */\n isFetching?: boolean;\n}\n\nexport function Resource<T>({\n status,\n data,\n columns,\n rowKey,\n title,\n actions,\n error,\n onRetry,\n onRowClick,\n emptyTitle = \"Nothing here yet\",\n emptyMessage = \"No records match.\",\n filter,\n onFilterChange,\n filterPlaceholder = \"Search…\",\n filterDebounceMs = 300,\n onNextPage,\n onPrevPage,\n hasNextPage,\n hasPrevPage,\n isFetching,\n}: ResourceProps<T>): ReactElement {\n const [query, setQuery] = useState(filter ?? \"\");\n\n // Keep local input in sync when the parent resets the filter externally.\n useEffect(() => {\n setQuery(filter ?? \"\");\n }, [filter]);\n\n // Debounce filter changes so each keystroke doesn't fire an RPC.\n useEffect(() => {\n if (!onFilterChange || query === (filter ?? \"\")) {\n return;\n }\n const handle = setTimeout(() => onFilterChange(query), filterDebounceMs);\n return () => clearTimeout(handle);\n }, [query, filter, onFilterChange, filterDebounceMs]);\n\n const showFooter = Boolean(onNextPage || onPrevPage);\n const colCount = columns.length;\n const captionText = title ?? \"Results\";\n\n return (\n <section className=\"flex flex-col gap-4\">\n {(title || actions) && (\n <header className=\"flex items-center justify-between gap-4\">\n {title ? (\n <h1 className=\"text-xl font-semibold text-ink\">{title}</h1>\n ) : (\n <span />\n )}\n {actions ? (\n <div className=\"flex items-center gap-2\">{actions}</div>\n ) : null}\n </header>\n )}\n\n {onFilterChange ? (\n <input\n type=\"search\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={filterPlaceholder}\n aria-label={title ? `Search ${title.toLowerCase()}` : \"Search\"}\n className=\"w-full max-w-sm rounded-md border border-border bg-surface px-3 py-2 text-sm text-ink shadow-sm focus:border-accent focus:outline-none focus:ring-1 focus:ring-accent\"\n />\n ) : null}\n\n <div className=\"overflow-x-auto rounded-lg border border-border bg-surface\">\n <table className=\"min-w-full divide-y divide-border text-sm\">\n <caption className=\"sr-only\">{captionText}</caption>\n <thead className=\"bg-surface-muted\">\n <tr>\n {columns.map((col, i) => (\n <th\n key={i}\n scope=\"col\"\n className={`px-4 py-2.5 text-left font-medium text-ink-muted ${\n col.className ?? \"\"\n }`}\n >\n {col.header}\n </th>\n ))}\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {status === \"loading\" ? (\n <SkeletonRows rows={5} cols={colCount} />\n ) : status === \"error\" ? (\n <tr>\n <td colSpan={colCount} className=\"px-4 py-10\">\n <ErrorState error={error} onRetry={onRetry} />\n </td>\n </tr>\n ) : !data || data.length === 0 ? (\n <tr>\n <td colSpan={colCount} className=\"px-4 py-12\">\n <EmptyState title={emptyTitle} message={emptyMessage} />\n </td>\n </tr>\n ) : (\n data.map((row) => (\n <tr\n key={rowKey(row)}\n onClick={onRowClick ? () => onRowClick(row) : undefined}\n // Keyboard-operable when the row navigates: Enter/Space\n // activate it, and it takes focus in tab order.\n role={onRowClick ? \"button\" : undefined}\n tabIndex={onRowClick ? 0 : undefined}\n onKeyDown={\n onRowClick\n ? (e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onRowClick(row);\n }\n }\n : undefined\n }\n className={\n onRowClick\n ? \"cursor-pointer transition-colors hover:bg-surface-muted focus:bg-surface-muted focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-accent\"\n : undefined\n }\n >\n {columns.map((col, i) => (\n <td\n key={i}\n className={`px-4 py-2.5 text-ink ${col.className ?? \"\"}`}\n >\n {col.cell(row)}\n </td>\n ))}\n </tr>\n ))\n )}\n </tbody>\n </table>\n </div>\n\n {showFooter ? (\n <footer className=\"flex items-center justify-end gap-2\">\n {isFetching ? (\n <span className=\"mr-auto text-xs text-ink-subtle\">Loading…</span>\n ) : null}\n <button\n type=\"button\"\n onClick={onPrevPage}\n disabled={!hasPrevPage}\n className=\"rounded-md border border-border px-3 py-1.5 text-sm font-medium text-ink-muted hover:bg-surface-muted disabled:cursor-not-allowed disabled:opacity-40\"\n >\n Previous\n </button>\n <button\n type=\"button\"\n onClick={onNextPage}\n disabled={!hasNextPage}\n className=\"rounded-md border border-border px-3 py-1.5 text-sm font-medium text-ink-muted hover:bg-surface-muted disabled:cursor-not-allowed disabled:opacity-40\"\n >\n Next\n </button>\n </footer>\n ) : null}\n </section>\n );\n}\n\n// SkeletonRows is the loading rung of the tristate ladder: a shimmer the\n// width of the table while the first page is in flight.\n//\n// It is ONE row with a colSpan cell, matching the error and empty rungs\n// above — not `rows` × `cols` real table cells. That shape is what makes it\n// accessible, and the reasoning is worth keeping:\n//\n// - The shimmer is pure decoration. It carries no data, so it is wrapped\n// in aria-hidden and the cell carries one sr-only \"Loading…\" instead.\n// Assistive tech hears the state once, not once per placeholder.\n// - A grid of empty <td>s announced as a table of blank cells, and read\n// as a real 5-row result set by anything counting rows. The placeholder\n// geometry is now plain <div>s inside the single cell, so the table has\n// exactly the structure it claims to have at every moment.\n// - Empty cells also tripped jsx-a11y/control-has-associated-label under\n// the eslint config forge scaffolds. That was the symptom that surfaced\n// this; the row-shape mismatch above is the actual defect.\n//\n// aria-hidden goes on the inner <div>, never on the <td> or <tr>: jsx-a11y\n// treats table elements as focusable, so hiding one is\n// no-aria-hidden-on-focusable — correctly, since a hidden focusable node is\n// a focus trap for screen-reader users.\nfunction SkeletonRows({ rows, cols }: { rows: number; cols: number }) {\n return (\n <tr>\n <td colSpan={cols} className=\"p-0\">\n <span className=\"sr-only\">Loading…</span>\n <div aria-hidden=\"true\" className=\"divide-y divide-border\">\n {Array.from({ length: rows }).map((_, r) => (\n <div key={r} className=\"flex items-center gap-4 px-4 py-3\">\n {Array.from({ length: cols }).map((__, c) => (\n <div\n key={c}\n className=\"h-4 flex-1 animate-pulse motion-reduce:animate-none rounded bg-surface-muted\"\n />\n ))}\n </div>\n ))}\n </div>\n </td>\n </tr>\n );\n}\n\n// ErrorState shows userMessage(error), never error.message: a ConnectError's\n// message carries the \"[code] \" transport framing, which is not copy a user\n// should read. It is body text in the empty rung's style, not a monospace\n// debug readout.\nfunction ErrorState({\n error,\n onRetry,\n}: {\n error?: Error | null;\n onRetry?: () => void;\n}) {\n return (\n <div role=\"alert\" className=\"flex flex-col items-center gap-2 text-center\">\n <p className=\"text-sm font-medium text-danger-ink\">\n Couldn't load data\n </p>\n {error ? (\n <p className=\"max-w-md text-sm text-ink-muted\">{userMessage(error)}</p>\n ) : null}\n {onRetry ? (\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"mt-1 rounded-md bg-danger px-3 py-1.5 text-sm font-medium text-on-danger hover:bg-danger-hover\"\n >\n Try again\n </button>\n ) : null}\n </div>\n );\n}\n\nfunction EmptyState({ title, message }: { title: string; message: string }) {\n return (\n <div className=\"flex flex-col items-center gap-1 text-center\">\n <p className=\"text-sm font-medium text-ink\">{title}</p>\n <p className=\"text-sm text-ink-muted\">{message}</p>\n </div>\n );\n}\n"]}
|
package/dist/service-hooks.js
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
// Part of @reliantlabs/forge-web-runtime — the web twin of forge/pkg.
|
|
2
3
|
//
|
|
4
|
+
// Every export below is a React hook or builds one, so this module belongs in
|
|
5
|
+
// the CLIENT graph. Without the directive the Next.js App Router compiles it
|
|
6
|
+
// into the SERVER graph, where it binds a SECOND @tanstack/react-query
|
|
7
|
+
// instance and every generated hook throws "No QueryClient set" at runtime —
|
|
8
|
+
// invisible to tsc, eslint AND `next build`. published-surface.test.ts asserts
|
|
9
|
+
// the directive survives into dist/. It is NOT applied package-wide: the
|
|
10
|
+
// ./interceptors subpath exists to be importable without React.
|
|
11
|
+
//
|
|
3
12
|
// The React Query wrapper machinery behind forge's generated per-service hook
|
|
4
13
|
// files (`src/hooks/<svc>-service-hooks_gen.ts`).
|
|
5
14
|
//
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service-hooks.js","sourceRoot":"","sources":["../src/service-hooks.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,8EAA8E;AAC9E,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,qEAAqE;AACrE,qEAAqE;AACrE,+EAA+E;AAC/E,4EAA4E;AAC5E,8EAA8E;AAC9E,sEAAsE;AACtE,EAAE;AACF,+EAA+E;AAC/E,2EAA2E;AAC3E,sEAAsE;AACtE,wEAAwE;AACxE,6EAA6E;AAC7E,4EAA4E;AAC5E,mCAAmC;AACnC,EAAE;AACF,wEAAwE;AACxE,8EAA8E;AAC9E,uDAAuD;AACvD,yEAAyE;AACzE,8EAA8E;AAC9E,8EAA8E;AAC9E,OAAO,EACL,MAAM,EACN,MAAM,GAGP,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,WAAW,EACX,QAAQ,EACR,cAAc,GAKf,MAAM,uBAAuB,CAAC;AAW/B;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU,CACxB,MAAc,EACd,GAA6B;IAE7B,OAAO,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;AAC7C,CAAC;AAoBD;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO;QACL,GAAG,EAAE,CAAC,OAAO,CAAU;QACvB,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,CAAU;QAClD,KAAK,EACH,CACE,MAAc,EACd,MAAc,EACd,WAAoB,EACpB,EAAE,CACJ,CAAC,GAA6B,EAAmB,EAAE,CACjD,WAAW,KAAK,SAAS;YACvB,CAAC,CAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,CAAW;YACvD,CAAC,CAAE,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,CAAW;KAC3E,CAAC;AACJ,CAAC;AAeD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAC7B,MAA0D,EAC1D,IAA0D;IAE1D,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,CACtB,QAAQ,CAAC;QACP,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC;QACrB,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;QACxB,GAAG,OAAO;KACX,CAAC,CAAC;AACP,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,kBAAkB,CAChC,aAA8B,EAC9B,IAA0D;IAE1D,OAAO,CAAC,OAAO,EAAE,EAAE;QACjB,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;QACrC,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,IAAI,EAAE,CAAC;QAC7C,OAAO,WAAW,CAAC;YACjB,UAAU,EAAE,CAAC,GAA6B,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;YACxD,SAAS,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE;gBACrB,WAAW,CAAC,iBAAiB,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC,CAAC;gBAC3D,SAAS,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;YACvB,CAAC;YACD,GAAG,IAAI;SACR,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC","sourcesContent":["// Part of @reliantlabs/forge-web-runtime — the web twin of forge/pkg.\n//\n// The React Query wrapper machinery behind forge's generated per-service hook\n// files (`src/hooks/<svc>-service-hooks_gen.ts`).\n//\n// WHAT WAS HERE BEFORE. Each generated file re-emitted the same three things\n// per RPC: a query-key entry, a useQuery or useMutation wrapper, and — for\n// mutations — the entity-scoped invalidateQueries plus the onSuccess\n// composition, with the reasoning for each re-inlined as comments. A\n// five-service project rendered five copies of machinery that differed only in\n// RPC names and message types. This module IS that machinery, written once.\n// The generated file keeps exactly what varies per RPC: the name, the request\n// schema, the response type, query-vs-mutation, and the entity scope.\n//\n// WHY A SUBPATH AND NOT THE BARREL. This module imports @tanstack/react-query.\n// Every browser frontend forge scaffolds depends on it — but the barrel is\n// also what a consumer imports for interceptors and errors alone, and\n// anchoring React Query there would make a non-React consumer resolve a\n// package it never installed. Same reasoning as ./mock-transport and ./otel:\n// an engine with its own dependency footprint gets its own entry point, and\n// React Query is an OPTIONAL peer.\n//\n// WHAT IT DELIBERATELY DOES NOT DO. It does not emit error toasts — the\n// app-wide MutationCache in src/lib/query-client.ts is the single error-toast\n// chokepoint, and pages that render errors inline pass\n// `meta: { silenceErrorToast: true }`. It never defaults success-side UX\n// either: \"CreateOrder succeeded\" leaks RPC names into the UI, so the toast /\n// redirect / inline confirmation stays the call site's via options.onSuccess.\nimport {\n create,\n toJson,\n type DescMessage,\n type MessageInitShape,\n} from \"@bufbuild/protobuf\";\nimport {\n useMutation,\n useQuery,\n useQueryClient,\n type UseMutationOptions,\n type UseMutationResult,\n type UseQueryOptions,\n type UseQueryResult,\n} from \"@tanstack/react-query\";\n\nimport type { ConnectClientError } from \"./errors.js\";\n\n/**\n * A generated query key. The tuple is [service, entity?, method,\n * protojson(req)] for a method key and [service] / [service, entity] for the\n * broader scopes.\n */\nexport type ServiceQueryKey = readonly unknown[];\n\n/**\n * requestKey projects a request init into protojson for use inside a query\n * key. Two reasons this is not the raw request object:\n *\n * - React Query's default hasher is JSON.stringify, which THROWS on bigint\n * fields (any proto int64) — the raw message crashes the cache.\n * - protojson normalizes representations, so { id: 1 } and { id: \"1\" } land\n * in ONE cache entry instead of silently splitting the cache.\n *\n * Exported because a hand-written hook that wants to share a generated hook's\n * cache entry has to hash its request the identical way.\n */\nexport function requestKey<Schema extends DescMessage>(\n schema: Schema,\n req: MessageInitShape<Schema>,\n): unknown {\n return toJson(schema, create(schema, req));\n}\n\n/** The three key-scope builders a generated key factory is assembled from. */\nexport interface ServiceKeyBuilders {\n /** Every query on this service: [service]. */\n readonly all: readonly [string];\n /** Every query for one CRUD entity: [service, entity]. Mutations invalidate this. */\n entity(name: string): readonly [string, string];\n /**\n * One query: [service, entity?, method, protojson(req)]. Returns the\n * per-request key FUNCTION, which is what both the query hook and any\n * call-site prefetch/setQueryData use.\n */\n query<Schema extends DescMessage>(\n method: string,\n schema: Schema,\n entityScope?: string,\n ): (req: MessageInitShape<Schema>) => ServiceQueryKey;\n}\n\n/**\n * serviceKeys returns the builders for one service's query-key factory.\n *\n * Cache-key semantics are a compatibility contract, not an implementation\n * detail: an existing app's\n * `invalidateQueries({ queryKey: orderServiceKeys.order })` has to keep\n * matching the keys its queries were registered under across a version bump.\n * So the tuple order and contents produced here are fixed, and identical to\n * what forge previously inlined into every generated hook file.\n */\nexport function serviceKeys(service: string): ServiceKeyBuilders {\n return {\n all: [service] as const,\n entity: (name: string) => [service, name] as const,\n query:\n <Schema extends DescMessage>(\n method: string,\n schema: Schema,\n entityScope?: string,\n ) =>\n (req: MessageInitShape<Schema>): ServiceQueryKey =>\n entityScope === undefined\n ? ([service, method, requestKey(schema, req)] as const)\n : ([service, entityScope, method, requestKey(schema, req)] as const),\n };\n}\n\n/** A generated query hook: `useGetOrder(req, options?)`. */\nexport type ServiceQueryHook<Schema extends DescMessage, Response> = (\n req: MessageInitShape<Schema>,\n options?: Partial<UseQueryOptions<Response, ConnectClientError>>,\n) => UseQueryResult<Response, ConnectClientError>;\n\n/** A generated mutation hook: `useCreateOrder(options?)`. */\nexport type ServiceMutationHook<Schema extends DescMessage, Response> = (\n options?: Partial<\n UseMutationOptions<Response, ConnectClientError, MessageInitShape<Schema>>\n >,\n) => UseMutationResult<Response, ConnectClientError, MessageInitShape<Schema>>;\n\n/**\n * createQueryHook builds one useQuery wrapper.\n *\n * The signature it returns is the one call sites already use — `(req,\n * options?)`, with the error typed as ConnectClientError rather than React\n * Query's default `Error`. That type is declared on the OPTIONS parameter\n * because React Query takes the result's error type from the same TError it\n * reads there, so `query.error` is a ConnectClientError at every call site\n * with nothing to pass. Typing it as `Error` hid `.reason`, `.code`, `.status`\n * and `.retryable`, and left `err.message.includes(...)` as the only\n * expression that compiled — the one thing the error contract forbids.\n */\nexport function createQueryHook<Schema extends DescMessage, Response>(\n keyFor: (req: MessageInitShape<Schema>) => ServiceQueryKey,\n call: (req: MessageInitShape<Schema>) => Promise<Response>,\n): ServiceQueryHook<Schema, Response> {\n return (req, options) =>\n useQuery({\n queryKey: keyFor(req),\n queryFn: () => call(req),\n ...options,\n });\n}\n\n/**\n * createMutationHook builds one useMutation wrapper with scoped invalidation.\n *\n * invalidateKey is the entity scope when the RPC maps to a CRUD entity, and\n * the whole-service scope otherwise — a bespoke mutation may touch anything,\n * so over-invalidating is the safe default there.\n *\n * The invalidation is composed BEFORE the caller's onSuccess and is never\n * replaced by it: onSuccess is destructured out and re-invoked after the\n * invalidate, while the rest of the options object is spread separately. A\n * caller passing onSuccess therefore ADDS behaviour, rather than silently\n * disabling the cache refresh — which is exactly what a plain `...options`\n * spread over a default onSuccess would do.\n */\nexport function createMutationHook<Schema extends DescMessage, Response>(\n invalidateKey: ServiceQueryKey,\n call: (req: MessageInitShape<Schema>) => Promise<Response>,\n): ServiceMutationHook<Schema, Response> {\n return (options) => {\n const queryClient = useQueryClient();\n const { onSuccess, ...rest } = options ?? {};\n return useMutation({\n mutationFn: (req: MessageInitShape<Schema>) => call(req),\n onSuccess: (...args) => {\n queryClient.invalidateQueries({ queryKey: invalidateKey });\n onSuccess?.(...args);\n },\n ...rest,\n });\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"service-hooks.js","sourceRoot":"","sources":["../src/service-hooks.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,sEAAsE;AACtE,EAAE;AACF,8EAA8E;AAC9E,6EAA6E;AAC7E,uEAAuE;AACvE,6EAA6E;AAC7E,+EAA+E;AAC/E,yEAAyE;AACzE,gEAAgE;AAChE,EAAE;AACF,8EAA8E;AAC9E,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,qEAAqE;AACrE,qEAAqE;AACrE,+EAA+E;AAC/E,4EAA4E;AAC5E,8EAA8E;AAC9E,sEAAsE;AACtE,EAAE;AACF,+EAA+E;AAC/E,2EAA2E;AAC3E,sEAAsE;AACtE,wEAAwE;AACxE,6EAA6E;AAC7E,4EAA4E;AAC5E,mCAAmC;AACnC,EAAE;AACF,wEAAwE;AACxE,8EAA8E;AAC9E,uDAAuD;AACvD,yEAAyE;AACzE,8EAA8E;AAC9E,8EAA8E;AAC9E,OAAO,EACL,MAAM,EACN,MAAM,GAGP,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,WAAW,EACX,QAAQ,EACR,cAAc,GAKf,MAAM,uBAAuB,CAAC;AAW/B;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU,CACxB,MAAc,EACd,GAA6B;IAE7B,OAAO,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;AAC7C,CAAC;AAoBD;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO;QACL,GAAG,EAAE,CAAC,OAAO,CAAU;QACvB,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,CAAU;QAClD,KAAK,EACH,CACE,MAAc,EACd,MAAc,EACd,WAAoB,EACpB,EAAE,CACJ,CAAC,GAA6B,EAAmB,EAAE,CACjD,WAAW,KAAK,SAAS;YACvB,CAAC,CAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,CAAW;YACvD,CAAC,CAAE,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,CAAW;KAC3E,CAAC;AACJ,CAAC;AAeD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAC7B,MAA0D,EAC1D,IAA0D;IAE1D,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,CACtB,QAAQ,CAAC;QACP,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC;QACrB,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;QACxB,GAAG,OAAO;KACX,CAAC,CAAC;AACP,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,kBAAkB,CAChC,aAA8B,EAC9B,IAA0D;IAE1D,OAAO,CAAC,OAAO,EAAE,EAAE;QACjB,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;QACrC,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,IAAI,EAAE,CAAC;QAC7C,OAAO,WAAW,CAAC;YACjB,UAAU,EAAE,CAAC,GAA6B,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC;YACxD,SAAS,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE;gBACrB,WAAW,CAAC,iBAAiB,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC,CAAC;gBAC3D,SAAS,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;YACvB,CAAC;YACD,GAAG,IAAI;SACR,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC","sourcesContent":["\"use client\";\n\n// Part of @reliantlabs/forge-web-runtime — the web twin of forge/pkg.\n//\n// Every export below is a React hook or builds one, so this module belongs in\n// the CLIENT graph. Without the directive the Next.js App Router compiles it\n// into the SERVER graph, where it binds a SECOND @tanstack/react-query\n// instance and every generated hook throws \"No QueryClient set\" at runtime —\n// invisible to tsc, eslint AND `next build`. published-surface.test.ts asserts\n// the directive survives into dist/. It is NOT applied package-wide: the\n// ./interceptors subpath exists to be importable without React.\n//\n// The React Query wrapper machinery behind forge's generated per-service hook\n// files (`src/hooks/<svc>-service-hooks_gen.ts`).\n//\n// WHAT WAS HERE BEFORE. Each generated file re-emitted the same three things\n// per RPC: a query-key entry, a useQuery or useMutation wrapper, and — for\n// mutations — the entity-scoped invalidateQueries plus the onSuccess\n// composition, with the reasoning for each re-inlined as comments. A\n// five-service project rendered five copies of machinery that differed only in\n// RPC names and message types. This module IS that machinery, written once.\n// The generated file keeps exactly what varies per RPC: the name, the request\n// schema, the response type, query-vs-mutation, and the entity scope.\n//\n// WHY A SUBPATH AND NOT THE BARREL. This module imports @tanstack/react-query.\n// Every browser frontend forge scaffolds depends on it — but the barrel is\n// also what a consumer imports for interceptors and errors alone, and\n// anchoring React Query there would make a non-React consumer resolve a\n// package it never installed. Same reasoning as ./mock-transport and ./otel:\n// an engine with its own dependency footprint gets its own entry point, and\n// React Query is an OPTIONAL peer.\n//\n// WHAT IT DELIBERATELY DOES NOT DO. It does not emit error toasts — the\n// app-wide MutationCache in src/lib/query-client.ts is the single error-toast\n// chokepoint, and pages that render errors inline pass\n// `meta: { silenceErrorToast: true }`. It never defaults success-side UX\n// either: \"CreateOrder succeeded\" leaks RPC names into the UI, so the toast /\n// redirect / inline confirmation stays the call site's via options.onSuccess.\nimport {\n create,\n toJson,\n type DescMessage,\n type MessageInitShape,\n} from \"@bufbuild/protobuf\";\nimport {\n useMutation,\n useQuery,\n useQueryClient,\n type UseMutationOptions,\n type UseMutationResult,\n type UseQueryOptions,\n type UseQueryResult,\n} from \"@tanstack/react-query\";\n\nimport type { ConnectClientError } from \"./errors.js\";\n\n/**\n * A generated query key. The tuple is [service, entity?, method,\n * protojson(req)] for a method key and [service] / [service, entity] for the\n * broader scopes.\n */\nexport type ServiceQueryKey = readonly unknown[];\n\n/**\n * requestKey projects a request init into protojson for use inside a query\n * key. Two reasons this is not the raw request object:\n *\n * - React Query's default hasher is JSON.stringify, which THROWS on bigint\n * fields (any proto int64) — the raw message crashes the cache.\n * - protojson normalizes representations, so { id: 1 } and { id: \"1\" } land\n * in ONE cache entry instead of silently splitting the cache.\n *\n * Exported because a hand-written hook that wants to share a generated hook's\n * cache entry has to hash its request the identical way.\n */\nexport function requestKey<Schema extends DescMessage>(\n schema: Schema,\n req: MessageInitShape<Schema>,\n): unknown {\n return toJson(schema, create(schema, req));\n}\n\n/** The three key-scope builders a generated key factory is assembled from. */\nexport interface ServiceKeyBuilders {\n /** Every query on this service: [service]. */\n readonly all: readonly [string];\n /** Every query for one CRUD entity: [service, entity]. Mutations invalidate this. */\n entity(name: string): readonly [string, string];\n /**\n * One query: [service, entity?, method, protojson(req)]. Returns the\n * per-request key FUNCTION, which is what both the query hook and any\n * call-site prefetch/setQueryData use.\n */\n query<Schema extends DescMessage>(\n method: string,\n schema: Schema,\n entityScope?: string,\n ): (req: MessageInitShape<Schema>) => ServiceQueryKey;\n}\n\n/**\n * serviceKeys returns the builders for one service's query-key factory.\n *\n * Cache-key semantics are a compatibility contract, not an implementation\n * detail: an existing app's\n * `invalidateQueries({ queryKey: orderServiceKeys.order })` has to keep\n * matching the keys its queries were registered under across a version bump.\n * So the tuple order and contents produced here are fixed, and identical to\n * what forge previously inlined into every generated hook file.\n */\nexport function serviceKeys(service: string): ServiceKeyBuilders {\n return {\n all: [service] as const,\n entity: (name: string) => [service, name] as const,\n query:\n <Schema extends DescMessage>(\n method: string,\n schema: Schema,\n entityScope?: string,\n ) =>\n (req: MessageInitShape<Schema>): ServiceQueryKey =>\n entityScope === undefined\n ? ([service, method, requestKey(schema, req)] as const)\n : ([service, entityScope, method, requestKey(schema, req)] as const),\n };\n}\n\n/** A generated query hook: `useGetOrder(req, options?)`. */\nexport type ServiceQueryHook<Schema extends DescMessage, Response> = (\n req: MessageInitShape<Schema>,\n options?: Partial<UseQueryOptions<Response, ConnectClientError>>,\n) => UseQueryResult<Response, ConnectClientError>;\n\n/** A generated mutation hook: `useCreateOrder(options?)`. */\nexport type ServiceMutationHook<Schema extends DescMessage, Response> = (\n options?: Partial<\n UseMutationOptions<Response, ConnectClientError, MessageInitShape<Schema>>\n >,\n) => UseMutationResult<Response, ConnectClientError, MessageInitShape<Schema>>;\n\n/**\n * createQueryHook builds one useQuery wrapper.\n *\n * The signature it returns is the one call sites already use — `(req,\n * options?)`, with the error typed as ConnectClientError rather than React\n * Query's default `Error`. That type is declared on the OPTIONS parameter\n * because React Query takes the result's error type from the same TError it\n * reads there, so `query.error` is a ConnectClientError at every call site\n * with nothing to pass. Typing it as `Error` hid `.reason`, `.code`, `.status`\n * and `.retryable`, and left `err.message.includes(...)` as the only\n * expression that compiled — the one thing the error contract forbids.\n */\nexport function createQueryHook<Schema extends DescMessage, Response>(\n keyFor: (req: MessageInitShape<Schema>) => ServiceQueryKey,\n call: (req: MessageInitShape<Schema>) => Promise<Response>,\n): ServiceQueryHook<Schema, Response> {\n return (req, options) =>\n useQuery({\n queryKey: keyFor(req),\n queryFn: () => call(req),\n ...options,\n });\n}\n\n/**\n * createMutationHook builds one useMutation wrapper with scoped invalidation.\n *\n * invalidateKey is the entity scope when the RPC maps to a CRUD entity, and\n * the whole-service scope otherwise — a bespoke mutation may touch anything,\n * so over-invalidating is the safe default there.\n *\n * The invalidation is composed BEFORE the caller's onSuccess and is never\n * replaced by it: onSuccess is destructured out and re-invoked after the\n * invalidate, while the rest of the options object is spread separately. A\n * caller passing onSuccess therefore ADDS behaviour, rather than silently\n * disabling the cache refresh — which is exactly what a plain `...options`\n * spread over a default onSuccess would do.\n */\nexport function createMutationHook<Schema extends DescMessage, Response>(\n invalidateKey: ServiceQueryKey,\n call: (req: MessageInitShape<Schema>) => Promise<Response>,\n): ServiceMutationHook<Schema, Response> {\n return (options) => {\n const queryClient = useQueryClient();\n const { onSuccess, ...rest } = options ?? {};\n return useMutation({\n mutationFn: (req: MessageInitShape<Schema>) => call(req),\n onSuccess: (...args) => {\n queryClient.invalidateQueries({ queryKey: invalidateKey });\n onSuccess?.(...args);\n },\n ...rest,\n });\n };\n}\n"]}
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -1,25 +1,65 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export interface
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
/** Where the browser ships telemetry: this origin's `/_otel`. */
|
|
2
|
+
export declare const DEFAULT_INGEST_PATH = "/_otel";
|
|
3
|
+
/**
|
|
4
|
+
* The apiKey handed to the SDK. Public by construction: it identifies
|
|
5
|
+
* nothing and authorises nothing. See the header — the proxy or collector
|
|
6
|
+
* owns the real credential.
|
|
7
|
+
*/
|
|
8
|
+
export declare const PLACEHOLDER_API_KEY = "forge-browser-public";
|
|
9
|
+
export interface BrowserTelemetryConfig {
|
|
10
|
+
/** `service.name` on everything this page emits. */
|
|
11
|
+
serviceName: string;
|
|
12
|
+
/** `service.version`. Omit for none. */
|
|
13
|
+
serviceVersion?: string;
|
|
14
|
+
/**
|
|
15
|
+
* The forge environment name. Emitted as BOTH `deployment.environment.name`
|
|
16
|
+
* (current semconv) and `deployment.environment` (the legacy key HyperDX's
|
|
17
|
+
* default sources still key on).
|
|
18
|
+
*/
|
|
19
|
+
environment?: string;
|
|
20
|
+
/** `service.namespace`, conventionally the project name. */
|
|
21
|
+
serviceNamespace?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Ingest base: a path on this origin (`/_otel`) or, as an escape hatch, an
|
|
24
|
+
* absolute URL. Unset, empty, or `off` disables telemetry — the app runs
|
|
25
|
+
* untraced rather than throwing, which is what makes it safe to call
|
|
26
|
+
* unconditionally from the app shell.
|
|
27
|
+
*/
|
|
12
28
|
endpoint?: string;
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Origins (or URLs — only the origin is used) of the app's own backend.
|
|
31
|
+
* Requests to them always get a `traceparent` from the SDK's fetch/XHR
|
|
32
|
+
* spans. This page's own origin is always included.
|
|
33
|
+
*/
|
|
34
|
+
apiOrigins?: Array<string | undefined>;
|
|
35
|
+
/** Mirror console.* into telemetry. Default true. */
|
|
36
|
+
consoleCapture?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Capture full request/response headers AND BODIES on fetch/XHR spans.
|
|
39
|
+
* Default false, and it should stay false: bodies are user data.
|
|
40
|
+
*/
|
|
41
|
+
advancedNetworkCapture?: boolean;
|
|
42
|
+
/** Session replay (rrweb). Default false. When true, all text and inputs are masked. */
|
|
43
|
+
replay?: boolean;
|
|
44
|
+
/** Verbose SDK diagnostics to the console. Default false. */
|
|
45
|
+
debug?: boolean;
|
|
19
46
|
}
|
|
20
47
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
|
|
48
|
+
* initBrowserTelemetry starts the HyperDX browser SDK once per page load and
|
|
49
|
+
* resolves to whether it is running. See the module header for the contract.
|
|
50
|
+
*/
|
|
51
|
+
export declare function initBrowserTelemetry(config: BrowserTelemetryConfig): Promise<boolean>;
|
|
52
|
+
/**
|
|
53
|
+
* The options passed to HyperDX.init. Exported for tests and for apps that
|
|
54
|
+
* want to see exactly what is configured — it is pure.
|
|
55
|
+
*/
|
|
56
|
+
export declare function buildSdkOptions(config: BrowserTelemetryConfig, ingestUrl: string, pageOrigin: string): Record<string, unknown>;
|
|
57
|
+
/**
|
|
58
|
+
* resolveIngestUrl turns the configured endpoint into the absolute URL the SDK
|
|
59
|
+
* is given, or null when telemetry is off. A path resolves against the page
|
|
60
|
+
* origin, so the default is same-origin by construction.
|
|
24
61
|
*/
|
|
25
|
-
export declare function
|
|
62
|
+
export declare function resolveIngestUrl(endpoint: string | undefined, pageOrigin: string): string | null;
|
|
63
|
+
/** The tracer to open manual spans on. Resolves HyperDX's provider once it is running. */
|
|
64
|
+
export declare function getTracer(name?: string): import("@opentelemetry/api").Tracer;
|
|
65
|
+
export { context, propagation, trace } from "@opentelemetry/api";
|