@webpieces/core-util 0.4.879 → 0.4.880
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/package.json +1 -1
- package/src/errors/ApiError.d.ts +38 -0
- package/src/errors/ApiError.js +46 -1
- package/src/errors/ApiError.js.map +1 -1
- package/src/errors/ApiErrorCodec.js +9 -2
- package/src/errors/ApiErrorCodec.js.map +1 -1
- package/src/errors/ClientRole.d.ts +27 -0
- package/src/errors/ClientRole.js +32 -0
- package/src/errors/ClientRole.js.map +1 -0
- package/src/errors/EndUserErrorCode.d.ts +22 -0
- package/src/errors/EndUserErrorCode.js +38 -0
- package/src/errors/EndUserErrorCode.js.map +1 -0
- package/src/errors/EndUserErrorRegistry.d.ts +61 -0
- package/src/errors/EndUserErrorRegistry.js +103 -0
- package/src/errors/EndUserErrorRegistry.js.map +1 -0
- package/src/errors/EndUserErrors.d.ts +28 -0
- package/src/errors/EndUserErrors.js +59 -0
- package/src/errors/EndUserErrors.js.map +1 -0
- package/src/errors/ReceivedApiErrorRule.d.ts +33 -3
- package/src/errors/ReceivedApiErrorRule.js +47 -8
- package/src/errors/ReceivedApiErrorRule.js.map +1 -1
- package/src/errors/index.d.ts +6 -1
- package/src/errors/index.js +12 -1
- package/src/errors/index.js.map +1 -1
- package/src/http/ContextMgr.d.ts +1 -1
- package/src/http/ContextMgr.js +1 -1
- package/src/http/ContextMgr.js.map +1 -1
- package/src/http/ErrorTranslator.d.ts +10 -4
- package/src/http/ErrorTranslator.js.map +1 -1
- package/src/http/HttpEndpointOptions.d.ts +8 -0
- package/src/http/HttpEndpointOptions.js.map +1 -1
- package/src/http/StreamErrorControl.d.ts +3 -1
- package/src/http/StreamErrorControl.js +4 -3
- package/src/http/StreamErrorControl.js.map +1 -1
- package/src/http/WebpiecesDefaultErrorTranslator.d.ts +10 -2
- package/src/http/WebpiecesDefaultErrorTranslator.js +11 -3
- package/src/http/WebpiecesDefaultErrorTranslator.js.map +1 -1
- package/src/index.d.ts +2 -2
- package/src/index.js +8 -1
- package/src/index.js.map +1 -1
- package/src/ipc/IpcErrorTranslator.d.ts +6 -2
- package/src/ipc/IpcErrorTranslator.js.map +1 -1
- package/src/ipc/IpcRegistry.d.ts +4 -1
- package/src/ipc/IpcRegistry.js +5 -3
- package/src/ipc/IpcRegistry.js.map +1 -1
- package/src/ipc/WebpiecesDefaultIpcErrorTranslator.d.ts +6 -2
- package/src/ipc/WebpiecesDefaultIpcErrorTranslator.js +6 -3
- package/src/ipc/WebpiecesDefaultIpcErrorTranslator.js.map +1 -1
- package/src/ipc/index.d.ts +1 -0
- package/src/ipc/index.js +3 -1
- package/src/ipc/index.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorTranslator.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ErrorTranslator.ts"],"names":[],"mappings":"","sourcesContent":["import { HttpResponseDto } from './HttpResponseDto';\n\n/**\n * ErrorTranslator - ONE symmetric place an app owns HTTP error translation, in BOTH directions, over\n * the WHOLE response.\n *\n * An app implements this ONCE and registers it ONCE per process via\n * {@link ClientRegistry.setErrorTranslator} — on the server AND in the browser. Its `toWire` runs on\n * the SERVER (`ExpressWrapper.handleError`) and its `fromWire` runs on every CLIENT in that process\n * (`ClientErrorTranslator.throwIfFailure`, shared by `http-client-node` and `http-client-browser`).\n * The payoff is type symmetry across the wire: the server throws `OrderNotFoundError` and the caller\n * CATCHES `OrderNotFoundError`, instead of decoding a status code by hand at every call site.\n *\n * # THERE IS ALWAYS A TRANSLATOR\n *\n * {@link ClientRegistry.getErrorTranslator} is non-optional and starts life holding\n * {@link WebpiecesDefaultErrorTranslator}. So no caller anywhere asks \"did anyone register one\" —\n * every call site is ONE unconditional line. A registered app translator REPLACES the default and\n * declines by DELEGATING to it:\n *\n * ```ts\n * private readonly fallback = new WebpiecesDefaultErrorTranslator();\n * ```\n *\n * That is one call, it is visible at the call site, and it composes: an app that wants to DECORATE\n * the default rather than replace it does so by calling it and editing the result. A `next` parameter\n * was considered and rejected — it buys nothing this does not, and it puts the framework back in the\n * business of sequencing somebody else's precedence.\n *\n * # Both halves speak {@link HttpResponseDto}, and that is the point\n *\n * `toWire` PRODUCES exactly what `fromWire` CONSUMES. Reading the two methods against each other in\n * one file is what makes a mistake visible, which is why this is one object rather than two\n * separately-registered functions.\n *\n * # The whole response, not a status plus a body\n *\n * The previous contract could express only `(statusCode, protocolError)`, so an app could not put its\n * own trace id, `Retry-After`, `WWW-Authenticate` or a cookie on an error response. A translator now\n * returns the status code, the reason phrase, the header LIST and the body — everything.\n *\n * # `toWire` RETURNS a response; `fromWire` THROWS\n *\n * They are mirrors, not twins. `toWire` is called inside a catch and its whole job is to produce a\n * value, so it returns one. `fromWire` is called on a response the caller is about to be handed, and\n * its whole job is to stop that from happening, so it THROWS and is typed `void`. Returning an\n * `Error` the caller then has to remember to throw was the previous shape and it is exactly the bug\n * class this deletes: a `void` return cannot be silently dropped.\n *\n * There is exactly ONE registered ErrorTranslator per process (a `set`, not an `add` to a list):\n * precedence between an app's own layers is the APP's to compose explicitly inside its `toWire`,\n * rather than something hidden in registration order inside the framework.\n *\n * # Not covered: 404 / unknown route\n *\n * A request that matches no route has no route context and never reaches this seam. That is\n * deliberate and out of scope.\n *\n * This is a business-logic contract (methods, not data), so it is an interface per the webpieces\n * guidelines.\n *\n * ```ts\n * class OrderErrorPayload {\n * constructor(public message: string, public errorCode: string) {}\n * }\n *\n * export class OrderErrorTranslator implements ErrorTranslator {\n * private readonly fallback = new WebpiecesDefaultErrorTranslator();\n *\n * toWire(error: Error): HttpResponseDto {\n * if (!(error instanceof OrderNotFoundError)) {\n * return this.fallback.toWire(error); // not mine -> webpieces default\n * }\n * const body = new OrderErrorPayload(error.message, 'ORDER_NOT_FOUND');\n * return new HttpResponseDto(\n * new HttpResponseStatus(460, 'Order Not Found'),\n * [new HttpHeader('x-order-trace', error.traceId)],\n * body,\n * );\n * }\n *\n * fromWire(response: HttpResponseDto): void {\n * if (response.status.code !== 460) {\n * this.fallback.fromWire(response);
|
|
1
|
+
{"version":3,"file":"ErrorTranslator.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/ErrorTranslator.ts"],"names":[],"mappings":"","sourcesContent":["import { ClientRole } from '../errors/ClientRole';\nimport { HttpResponseDto } from './HttpResponseDto';\n\n/**\n * ErrorTranslator - ONE symmetric place an app owns HTTP error translation, in BOTH directions, over\n * the WHOLE response.\n *\n * An app implements this ONCE and registers it ONCE per process via\n * {@link ClientRegistry.setErrorTranslator} — on the server AND in the browser. Its `toWire` runs on\n * the SERVER (`ExpressWrapper.handleError`) and its `fromWire` runs on every CLIENT in that process\n * (`ClientErrorTranslator.throwIfFailure`, shared by `http-client-node` and `http-client-browser`).\n * The payoff is type symmetry across the wire: the server throws `OrderNotFoundError` and the caller\n * CATCHES `OrderNotFoundError`, instead of decoding a status code by hand at every call site.\n *\n * # THERE IS ALWAYS A TRANSLATOR\n *\n * {@link ClientRegistry.getErrorTranslator} is non-optional and starts life holding\n * {@link WebpiecesDefaultErrorTranslator}. So no caller anywhere asks \"did anyone register one\" —\n * every call site is ONE unconditional line. A registered app translator REPLACES the default and\n * declines by DELEGATING to it:\n *\n * ```ts\n * private readonly fallback = new WebpiecesDefaultErrorTranslator();\n * ```\n *\n * That is one call, it is visible at the call site, and it composes: an app that wants to DECORATE\n * the default rather than replace it does so by calling it and editing the result. A `next` parameter\n * was considered and rejected — it buys nothing this does not, and it puts the framework back in the\n * business of sequencing somebody else's precedence.\n *\n * # Both halves speak {@link HttpResponseDto}, and that is the point\n *\n * `toWire` PRODUCES exactly what `fromWire` CONSUMES. Reading the two methods against each other in\n * one file is what makes a mistake visible, which is why this is one object rather than two\n * separately-registered functions.\n *\n * # The whole response, not a status plus a body\n *\n * The previous contract could express only `(statusCode, protocolError)`, so an app could not put its\n * own trace id, `Retry-After`, `WWW-Authenticate` or a cookie on an error response. A translator now\n * returns the status code, the reason phrase, the header LIST and the body — everything.\n *\n * # `toWire` RETURNS a response; `fromWire` THROWS\n *\n * They are mirrors, not twins. `toWire` is called inside a catch and its whole job is to produce a\n * value, so it returns one. `fromWire` is called on a response the caller is about to be handed, and\n * its whole job is to stop that from happening, so it THROWS and is typed `void`. Returning an\n * `Error` the caller then has to remember to throw was the previous shape and it is exactly the bug\n * class this deletes: a `void` return cannot be silently dropped.\n *\n * There is exactly ONE registered ErrorTranslator per process (a `set`, not an `add` to a list):\n * precedence between an app's own layers is the APP's to compose explicitly inside its `toWire`,\n * rather than something hidden in registration order inside the framework.\n *\n * # Not covered: 404 / unknown route\n *\n * A request that matches no route has no route context and never reaches this seam. That is\n * deliberate and out of scope.\n *\n * This is a business-logic contract (methods, not data), so it is an interface per the webpieces\n * guidelines.\n *\n * ```ts\n * class OrderErrorPayload {\n * constructor(public message: string, public errorCode: string) {}\n * }\n *\n * export class OrderErrorTranslator implements ErrorTranslator {\n * private readonly fallback = new WebpiecesDefaultErrorTranslator();\n *\n * toWire(error: Error): HttpResponseDto {\n * if (!(error instanceof OrderNotFoundError)) {\n * return this.fallback.toWire(error); // not mine -> webpieces default\n * }\n * const body = new OrderErrorPayload(error.message, 'ORDER_NOT_FOUND');\n * return new HttpResponseDto(\n * new HttpResponseStatus(460, 'Order Not Found'),\n * [new HttpHeader('x-order-trace', error.traceId)],\n * body,\n * );\n * }\n *\n * fromWire(response: HttpResponseDto, role: ClientRole): void {\n * if (response.status.code !== 460) {\n * this.fallback.fromWire(response, role); // not mine -> webpieces default\n * return;\n * }\n * throw new OrderNotFoundError(String(response.body));\n * }\n * }\n *\n * // startup, ONCE per process (server AND browser)\n * ClientRegistry.setErrorTranslator(new OrderErrorTranslator());\n * ```\n */\nexport interface ErrorTranslator {\n /**\n * SERVER: exception -> the ENTIRE response (status code, reason phrase, headers, body). Always a\n * response, never `undefined`; decline by returning\n * `new WebpiecesDefaultErrorTranslator().toWire(error)`.\n */\n toWire(error: Error): HttpResponseDto;\n\n /**\n * CLIENT: the ENTIRE response -> a THROW. Receives the SAME shape `toWire` produces, normalised\n * from whichever transport read it.\n *\n * Called for EVERY response, including a 2xx — deliberately, so an app whose 200 body signals\n * failure can turn it into a throw. Returning normally means \"let this response through\"; the\n * webpieces default returns normally for any 2xx that is not 266 and throws otherwise. Decline by\n * calling `new WebpiecesDefaultErrorTranslator().fromWire(response, role)`.\n *\n * `role` is WHO is receiving ({@link ClientRole}): the end-user client or a server. The client\n * that read the response passes the role ITS setup declared (`ClientConfig`), so one translator\n * registered in a process serves every client there. It decides what a received 401 means; pass\n * it through unchanged when declining.\n *\n * A non-2xx response can never reach a typed caller even if an app translator forgets to throw:\n * `ClientErrorTranslator.throwIfFailure` applies the webpieces default behind it. That is bug\n * containment, not a \"was one registered\" branch.\n */\n fromWire(response: HttpResponseDto, role: ClientRole): void;\n}\n"]}
|
|
@@ -86,6 +86,14 @@ export interface EndpointOptions {
|
|
|
86
86
|
* webpieces only declares and carries it (`RouteMetadata.allowUpgradeInFlight`); the app's
|
|
87
87
|
* `RequestLifecycleListener` / upgrade logic decides what to do with it. Default false.
|
|
88
88
|
* `grep -rn allowUpgradeInFlight` lists every endpoint an upgrade may cut off.
|
|
89
|
+
*
|
|
90
|
+
* THE SERVER'S HALF of the upgrade story is `ApiClientTooOldError` (HTTP 426,
|
|
91
|
+
* `errorCode: 'client-too-old'`). This flag is the CLIENT deciding WHEN it may upgrade; that
|
|
92
|
+
* error is a SERVER saying the client MUST. Who throws it: a server gate that compares the build
|
|
93
|
+
* the caller declared (a header, a version in the request) against the minimum it still
|
|
94
|
+
* supports. Receivers decode it back into `ApiClientTooOldError` on every hop, server-to-server
|
|
95
|
+
* and browser, HTTP and IPC, and treat it as an upgrade, never as a bug: the app's upgrade logic
|
|
96
|
+
* reloads or installs the new build, typically honouring this flag for the calls still in flight.
|
|
89
97
|
*/
|
|
90
98
|
allowUpgradeInFlight?: boolean;
|
|
91
99
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HttpEndpointOptions.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/HttpEndpointOptions.ts"],"names":[],"mappings":";;;AAGA,8EAA8E;AAC9E,IAAK,sBAIJ;AAJD,WAAK,sBAAsB;IACvB,uCAAa,CAAA;IACb,+DAAqC,CAAA;IACrC,yCAAe,CAAA;AACnB,CAAC,EAJI,sBAAsB,KAAtB,sBAAsB,QAI1B;AAED,wDAAwD;AAC3C,QAAA,IAAI,GAAG,sBAAsB,CAAC,IAAI,CAAC;AACnC,QAAA,gBAAgB,GAAG,sBAAsB,CAAC,gBAAgB,CAAC;AAC3D,QAAA,KAAK,GAAG,sBAAsB,CAAC,KAAK,CAAC","sourcesContent":["import { ExternalSystemKind } from './external-caller';\nimport { EndpointResponseType } from './HttpContract';\n\n/** Nominal backing keeps raw string literals out of endpoint declarations. */\nenum EndpointOperationValue {\n READ = 'read',\n WRITE_IDEMPOTENT = 'write-idempotent',\n WRITE = 'write',\n}\n\n/** Short, statically importable decorator arguments. */\nexport const READ = EndpointOperationValue.READ;\nexport const WRITE_IDEMPOTENT = EndpointOperationValue.WRITE_IDEMPOTENT;\nexport const WRITE = EndpointOperationValue.WRITE;\n\n/** The side-effect contract callers use to decide whether repeating an operation is safe. */\nexport type EndpointOperation = typeof READ | typeof WRITE_IDEMPOTENT | typeof WRITE;\n\n/** Options for one `@Endpoint`, stored parallel to the method-to-path metadata. */\nexport interface EndpointOptions {\n /** Return only a JSON body (default), or the full transport-neutral status/header/body value. */\n responseType?: EndpointResponseType;\n /**\n * Parse the request body as application/x-www-form-urlencoded instead of JSON. The request\n * DTO must be flat; urlencoded has no nesting. Default false = JSON.\n */\n formPost?: boolean;\n /**\n * Retain the verbatim request bytes and absolute URL for a `webhook(...)` callback to verify.\n * This is retention, not new buffering: the Express adapter already accumulates the body.\n *\n * `webhook(...)` requires this option at wiring time. It combines with `formPost` for vendors\n * that sign flat form bodies, e.g. `{ formPost: true, rawBody: true }`.\n */\n rawBody?: boolean;\n /**\n * The app should show NO progress UI for this call — it is not something the USER is waiting on\n * (a background download, a log shipper, a heartbeat, a telemetry flush).\n *\n * webpieces draws no progress bar itself; it DECLARES the fact and carries it on\n * `RouteMetadata.hideProgress`, which an app's `RequestLifecycleListener` reads to keep the call\n * out of its bar. WHY a declaration on the endpoint: the alternative is the app matching path\n * strings in its listener, which silently drifts the first time a route is renamed. The ROUTE is\n * the stable fact, so the decision belongs to the route's author.\n *\n * Independent of {@link noLogging} and {@link allowUpgradeInFlight}: a background download hides\n * its progress yet still wants its `[API-*]` lines (exactly what you need when a sync is slow)\n * and still must finish before an app upgrades under it.\n *\n * Default false: absent means the call drives the app's progress UI, like any ordinary route.\n * `grep -rn hideProgress` lists every endpoint the user is not shown waiting on.\n */\n hideProgress?: boolean;\n /**\n * webpieces writes NO `[API-*-req]` / `[API-*-resp-*]` line for this call, on either side —\n * {@link LogApiCallImpl} reads it via `ApiMethodInfo.noLogging`, handed in by both the client\n * proxy and the server's `LogApiFilter` — and stamps no per-call `api` tag.\n *\n * WHY a declaration and not a suppression switch: shipping a log over a logged transport logs\n * about shipping logs — the line describing the call IS the thing being shipped, and emitting it\n * feeds the next batch its own payload. The two mechanisms that do not know the endpoint both\n * failed in prod (see #976) — a wall-clock \"we are shipping\" boolean swallowed unrelated lines\n * including an error alarm, and a logger-name allowlist was incomplete by construction the moment\n * anyone added a logger, producing a steady-state 4-lines-per-second request loop on an idle page.\n * The ENDPOINT is the only stable, declarative fact, so it is where the decision belongs.\n *\n * Rides the route metadata for the same reason {@link formPost} and {@link rawBody} do: the\n * consumer branches on the ROUTE, without knowing the apiClass/methodName.\n *\n * Default false: absent means the ordinary, fully-logged route. The quiet path is the one you\n * have to say out loud, so `grep -rn noLogging` lists every endpoint webpieces stops logging.\n */\n noLogging?: boolean;\n /**\n * An app MAY upgrade (reload onto a newer deployed bundle) while this call is in flight, and the\n * call neither blocks nor arms that upgrade.\n *\n * By default every RPC participates in the app's upgrade decision: the app must not upgrade while\n * it is in flight, and it counts toward the \"finished with a SUCCESS result\" boundary at which an\n * app typically arms its seamless upgrade. That default is the safe one — reloading under an\n * in-flight write or download loses it. A call nobody asked for, and that is harmless to cut off\n * (a log shipper, a heartbeat), declares `true` so it can neither hold an upgrade hostage nor be\n * the success that triggers one.\n *\n * WHY separate from {@link hideProgress}: apps commonly arm an upgrade off their progress bar's\n * request group, so hiding a call from the bar used to drop it out of the upgrade decision as a\n * side effect. A hidden background download must still block an upgrade until it succeeds; only\n * this flag, said out loud, lets a call stop participating.\n *\n * webpieces only declares and carries it (`RouteMetadata.allowUpgradeInFlight`); the app's\n * `RequestLifecycleListener` / upgrade logic decides what to do with it. Default false.\n * `grep -rn allowUpgradeInFlight` lists every endpoint an upgrade may cut off.\n */\n allowUpgradeInFlight?: boolean;\n /**\n * This route is NOT part of the CUSTOMER-facing contract. `public-openapi.json` — the document a\n * customer gets, and the only one a publish path uploads — contains no path, no operation, no\n * schema and no prose for it. It is not \"rendered with a flag set\", it is absent.\n *\n * It is still in `full-private-openapi.json`, and still in `mcp-openapi.json` when it carries\n * `@WpMcpTool`. The reason it is withheld belongs in the method's JSDoc, which is where every\n * other piece of its documentation already lives.\n *\n * WHY A BOOLEAN HERE IS NOT SHIM SHAPE #5, given that `@ApiType` exists precisely to avoid a\n * falsy default meaning \"publish\": the two operate at different levels and the default here is\n * NOT the permissive one. A contract reaches customers only by declaring\n * `@ApiType(..., EXTERNAL_CUSTOMER)` — one deliberate token per contract — and `hidden` only ever\n * SUBTRACTS from what that token already granted. Omitting it on a contract nobody published to\n * customers publishes nothing. The per-method case it exists for is real and cannot be said at\n * class level: a NEW endpoint on an already-published API, built on main before it is announced.\n *\n * Default false, and `grep -rn \"hidden: true\"` lists every method withheld from customers.\n *\n * HIDING IS A DOCUMENTATION DECISION AND NEVER AN ACCESS-CONTROL ONE. The route is served exactly\n * as before and is still reachable by anything holding its credential — including an agent, if it\n * also carries `@WpMcpTool`. Declare its authorization as loudly as a published one.\n */\n hidden?: boolean;\n /**\n * This operation may touch systems OUTSIDE this service — a payment processor, a partner's API,\n * the physical world. It feeds MCP's `openWorldHint`, and it is rendered as a sentence in the\n * operation's description so a human integrating against the document sees it too.\n *\n * DEFAULT FALSE, deliberately unlike the MCP specification's own `true` default. MCP defaults to\n * \"open world\" because it cannot know; webpieces CAN know, because the author of the endpoint is\n * right here. Defaulting to `true` would make the interesting case the silent one and the boring\n * case the one you have to type, which is backwards — and an agent told that every endpoint might\n * reach the outside world has been told nothing. `grep -rn \"openWorld: true\"` lists the ones that\n * genuinely do.\n */\n openWorld?: boolean;\n}\n\n/** An external endpoint must also identify the outside system that calls it. */\nexport interface ExternalEndpointOptions extends EndpointOptions {\n /** The outside system (`'twilio'`) — graph identity, not display text. */\n calledBy: string;\n /** What that caller is; picks its graph shape. Defaults to `'saas'`. */\n callerKind?: ExternalSystemKind;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"HttpEndpointOptions.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/HttpEndpointOptions.ts"],"names":[],"mappings":";;;AAGA,8EAA8E;AAC9E,IAAK,sBAIJ;AAJD,WAAK,sBAAsB;IACvB,uCAAa,CAAA;IACb,+DAAqC,CAAA;IACrC,yCAAe,CAAA;AACnB,CAAC,EAJI,sBAAsB,KAAtB,sBAAsB,QAI1B;AAED,wDAAwD;AAC3C,QAAA,IAAI,GAAG,sBAAsB,CAAC,IAAI,CAAC;AACnC,QAAA,gBAAgB,GAAG,sBAAsB,CAAC,gBAAgB,CAAC;AAC3D,QAAA,KAAK,GAAG,sBAAsB,CAAC,KAAK,CAAC","sourcesContent":["import { ExternalSystemKind } from './external-caller';\nimport { EndpointResponseType } from './HttpContract';\n\n/** Nominal backing keeps raw string literals out of endpoint declarations. */\nenum EndpointOperationValue {\n READ = 'read',\n WRITE_IDEMPOTENT = 'write-idempotent',\n WRITE = 'write',\n}\n\n/** Short, statically importable decorator arguments. */\nexport const READ = EndpointOperationValue.READ;\nexport const WRITE_IDEMPOTENT = EndpointOperationValue.WRITE_IDEMPOTENT;\nexport const WRITE = EndpointOperationValue.WRITE;\n\n/** The side-effect contract callers use to decide whether repeating an operation is safe. */\nexport type EndpointOperation = typeof READ | typeof WRITE_IDEMPOTENT | typeof WRITE;\n\n/** Options for one `@Endpoint`, stored parallel to the method-to-path metadata. */\nexport interface EndpointOptions {\n /** Return only a JSON body (default), or the full transport-neutral status/header/body value. */\n responseType?: EndpointResponseType;\n /**\n * Parse the request body as application/x-www-form-urlencoded instead of JSON. The request\n * DTO must be flat; urlencoded has no nesting. Default false = JSON.\n */\n formPost?: boolean;\n /**\n * Retain the verbatim request bytes and absolute URL for a `webhook(...)` callback to verify.\n * This is retention, not new buffering: the Express adapter already accumulates the body.\n *\n * `webhook(...)` requires this option at wiring time. It combines with `formPost` for vendors\n * that sign flat form bodies, e.g. `{ formPost: true, rawBody: true }`.\n */\n rawBody?: boolean;\n /**\n * The app should show NO progress UI for this call — it is not something the USER is waiting on\n * (a background download, a log shipper, a heartbeat, a telemetry flush).\n *\n * webpieces draws no progress bar itself; it DECLARES the fact and carries it on\n * `RouteMetadata.hideProgress`, which an app's `RequestLifecycleListener` reads to keep the call\n * out of its bar. WHY a declaration on the endpoint: the alternative is the app matching path\n * strings in its listener, which silently drifts the first time a route is renamed. The ROUTE is\n * the stable fact, so the decision belongs to the route's author.\n *\n * Independent of {@link noLogging} and {@link allowUpgradeInFlight}: a background download hides\n * its progress yet still wants its `[API-*]` lines (exactly what you need when a sync is slow)\n * and still must finish before an app upgrades under it.\n *\n * Default false: absent means the call drives the app's progress UI, like any ordinary route.\n * `grep -rn hideProgress` lists every endpoint the user is not shown waiting on.\n */\n hideProgress?: boolean;\n /**\n * webpieces writes NO `[API-*-req]` / `[API-*-resp-*]` line for this call, on either side —\n * {@link LogApiCallImpl} reads it via `ApiMethodInfo.noLogging`, handed in by both the client\n * proxy and the server's `LogApiFilter` — and stamps no per-call `api` tag.\n *\n * WHY a declaration and not a suppression switch: shipping a log over a logged transport logs\n * about shipping logs — the line describing the call IS the thing being shipped, and emitting it\n * feeds the next batch its own payload. The two mechanisms that do not know the endpoint both\n * failed in prod (see #976) — a wall-clock \"we are shipping\" boolean swallowed unrelated lines\n * including an error alarm, and a logger-name allowlist was incomplete by construction the moment\n * anyone added a logger, producing a steady-state 4-lines-per-second request loop on an idle page.\n * The ENDPOINT is the only stable, declarative fact, so it is where the decision belongs.\n *\n * Rides the route metadata for the same reason {@link formPost} and {@link rawBody} do: the\n * consumer branches on the ROUTE, without knowing the apiClass/methodName.\n *\n * Default false: absent means the ordinary, fully-logged route. The quiet path is the one you\n * have to say out loud, so `grep -rn noLogging` lists every endpoint webpieces stops logging.\n */\n noLogging?: boolean;\n /**\n * An app MAY upgrade (reload onto a newer deployed bundle) while this call is in flight, and the\n * call neither blocks nor arms that upgrade.\n *\n * By default every RPC participates in the app's upgrade decision: the app must not upgrade while\n * it is in flight, and it counts toward the \"finished with a SUCCESS result\" boundary at which an\n * app typically arms its seamless upgrade. That default is the safe one — reloading under an\n * in-flight write or download loses it. A call nobody asked for, and that is harmless to cut off\n * (a log shipper, a heartbeat), declares `true` so it can neither hold an upgrade hostage nor be\n * the success that triggers one.\n *\n * WHY separate from {@link hideProgress}: apps commonly arm an upgrade off their progress bar's\n * request group, so hiding a call from the bar used to drop it out of the upgrade decision as a\n * side effect. A hidden background download must still block an upgrade until it succeeds; only\n * this flag, said out loud, lets a call stop participating.\n *\n * webpieces only declares and carries it (`RouteMetadata.allowUpgradeInFlight`); the app's\n * `RequestLifecycleListener` / upgrade logic decides what to do with it. Default false.\n * `grep -rn allowUpgradeInFlight` lists every endpoint an upgrade may cut off.\n *\n * THE SERVER'S HALF of the upgrade story is `ApiClientTooOldError` (HTTP 426,\n * `errorCode: 'client-too-old'`). This flag is the CLIENT deciding WHEN it may upgrade; that\n * error is a SERVER saying the client MUST. Who throws it: a server gate that compares the build\n * the caller declared (a header, a version in the request) against the minimum it still\n * supports. Receivers decode it back into `ApiClientTooOldError` on every hop, server-to-server\n * and browser, HTTP and IPC, and treat it as an upgrade, never as a bug: the app's upgrade logic\n * reloads or installs the new build, typically honouring this flag for the calls still in flight.\n */\n allowUpgradeInFlight?: boolean;\n /**\n * This route is NOT part of the CUSTOMER-facing contract. `public-openapi.json` — the document a\n * customer gets, and the only one a publish path uploads — contains no path, no operation, no\n * schema and no prose for it. It is not \"rendered with a flag set\", it is absent.\n *\n * It is still in `full-private-openapi.json`, and still in `mcp-openapi.json` when it carries\n * `@WpMcpTool`. The reason it is withheld belongs in the method's JSDoc, which is where every\n * other piece of its documentation already lives.\n *\n * WHY A BOOLEAN HERE IS NOT SHIM SHAPE #5, given that `@ApiType` exists precisely to avoid a\n * falsy default meaning \"publish\": the two operate at different levels and the default here is\n * NOT the permissive one. A contract reaches customers only by declaring\n * `@ApiType(..., EXTERNAL_CUSTOMER)` — one deliberate token per contract — and `hidden` only ever\n * SUBTRACTS from what that token already granted. Omitting it on a contract nobody published to\n * customers publishes nothing. The per-method case it exists for is real and cannot be said at\n * class level: a NEW endpoint on an already-published API, built on main before it is announced.\n *\n * Default false, and `grep -rn \"hidden: true\"` lists every method withheld from customers.\n *\n * HIDING IS A DOCUMENTATION DECISION AND NEVER AN ACCESS-CONTROL ONE. The route is served exactly\n * as before and is still reachable by anything holding its credential — including an agent, if it\n * also carries `@WpMcpTool`. Declare its authorization as loudly as a published one.\n */\n hidden?: boolean;\n /**\n * This operation may touch systems OUTSIDE this service — a payment processor, a partner's API,\n * the physical world. It feeds MCP's `openWorldHint`, and it is rendered as a sentence in the\n * operation's description so a human integrating against the document sees it too.\n *\n * DEFAULT FALSE, deliberately unlike the MCP specification's own `true` default. MCP defaults to\n * \"open world\" because it cannot know; webpieces CAN know, because the author of the endpoint is\n * right here. Defaulting to `true` would make the interesting case the silent one and the boring\n * case the one you have to type, which is backwards — and an agent told that every endpoint might\n * reach the outside world has been told nothing. `grep -rn \"openWorld: true\"` lists the ones that\n * genuinely do.\n */\n openWorld?: boolean;\n}\n\n/** An external endpoint must also identify the outside system that calls it. */\nexport interface ExternalEndpointOptions extends EndpointOptions {\n /** The outside system (`'twilio'`) — graph identity, not display text. */\n calledBy: string;\n /** What that caller is; picks its graph shape. Defaults to `'saas'`. */\n callerKind?: ExternalSystemKind;\n}\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ClientRole } from '../errors/ClientRole';
|
|
1
2
|
import { StreamTransportError } from './StreamingContract';
|
|
2
3
|
export declare class StreamProtocolError extends StreamTransportError {
|
|
3
4
|
}
|
|
@@ -6,6 +7,7 @@ export declare class StreamDisconnectedError extends StreamTransportError {
|
|
|
6
7
|
/** Reserved ASCII-RS controls share exactly the unary ErrorTranslator seam in both directions. */
|
|
7
8
|
export declare class StreamErrorControl {
|
|
8
9
|
encode(error: Error): string;
|
|
9
|
-
|
|
10
|
+
/** @param role - who is receiving the record, declared at the reading side's setup. */
|
|
11
|
+
decode(line: string, role: ClientRole): Error;
|
|
10
12
|
private response;
|
|
11
13
|
}
|
|
@@ -35,12 +35,13 @@ class StreamErrorControl {
|
|
|
35
35
|
const response = ClientRegistry_1.ClientRegistry.getErrorTranslator().toWire(error);
|
|
36
36
|
return `${String.fromCharCode(0x1e)}${JSON.stringify({ control: 'error', response })}\n`;
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
/** @param role - who is receiving the record, declared at the reading side's setup. */
|
|
39
|
+
decode(line, role) {
|
|
39
40
|
const response = this.response(line);
|
|
40
41
|
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- fromWire returns its typed result by throwing
|
|
41
42
|
try {
|
|
42
|
-
ClientRegistry_1.ClientRegistry.getErrorTranslator().fromWire(response);
|
|
43
|
-
WebpiecesDefaultErrorTranslator_1.WEBPIECES_DEFAULT_ERROR_TRANSLATOR.fromWire(response);
|
|
43
|
+
ClientRegistry_1.ClientRegistry.getErrorTranslator().fromWire(response, role);
|
|
44
|
+
WebpiecesDefaultErrorTranslator_1.WEBPIECES_DEFAULT_ERROR_TRANSLATOR.fromWire(response, role);
|
|
44
45
|
}
|
|
45
46
|
catch (err) {
|
|
46
47
|
const error = (0, errorUtils_1.toError)(err);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StreamErrorControl.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/StreamErrorControl.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"StreamErrorControl.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/StreamErrorControl.ts"],"names":[],"mappings":"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","sourcesContent":["import { ClientRole } from '../errors/ClientRole';\nimport { ClientRegistry } from './ClientRegistry';\nimport { HttpHeader, HttpResponseDto, HttpResponseStatus } from './HttpResponseDto';\nimport { WEBPIECES_DEFAULT_ERROR_TRANSLATOR } from './WebpiecesDefaultErrorTranslator';\nimport { DtoValue } from '../mcp/DtoSchema';\nimport { toError } from '../lib/errorUtils';\nimport { StreamTransportError } from './StreamingContract';\n\nexport class StreamProtocolError extends StreamTransportError {}\n\nexport class StreamDisconnectedError extends StreamTransportError {}\n\nclass ControlStatus {\n code?: number;\n reason?: string;\n}\n\nclass ControlHeader {\n name?: string;\n value?: string;\n}\n\nclass ControlResponse {\n status?: ControlStatus;\n headers?: ControlHeader[];\n body?: DtoValue;\n}\n\nclass ControlRecord {\n control?: string;\n response?: ControlResponse;\n}\n\n/** Reserved ASCII-RS controls share exactly the unary ErrorTranslator seam in both directions. */\nexport class StreamErrorControl {\n encode(error: Error): string {\n const response = ClientRegistry.getErrorTranslator().toWire(error);\n return `${String.fromCharCode(0x1e)}${JSON.stringify({ control: 'error', response })}\\n`;\n }\n\n /** @param role - who is receiving the record, declared at the reading side's setup. */\n decode(line: string, role: ClientRole): Error {\n const response = this.response(line);\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- fromWire returns its typed result by throwing\n try {\n ClientRegistry.getErrorTranslator().fromWire(response, role);\n WEBPIECES_DEFAULT_ERROR_TRANSLATOR.fromWire(response, role);\n } catch (err: unknown) {\n const error = toError(err);\n return error;\n }\n return new StreamProtocolError(\n 'Error translator returned for a JSONL error control record.',\n );\n }\n\n private response(line: string): HttpResponseDto {\n let control: ControlRecord;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- JSON.parse is the untrusted control boundary\n try {\n control = JSON.parse(line.slice(1)) as ControlRecord;\n } catch (err: unknown) {\n const error = toError(err);\n throw new StreamProtocolError(\n 'Malformed JSONL error control record.',\n undefined,\n undefined,\n error,\n );\n }\n const status = control?.response?.status;\n const headers = control?.response?.headers;\n if (\n control?.control !== 'error' ||\n typeof status?.code !== 'number' ||\n typeof status.reason !== 'string' ||\n !Array.isArray(headers)\n )\n throw new StreamProtocolError('Malformed JSONL error control record.');\n const mapped = headers.map((header: ControlHeader): HttpHeader => {\n if (typeof header?.name !== 'string' || typeof header.value !== 'string') {\n throw new StreamProtocolError('Malformed JSONL error control header.');\n }\n return new HttpHeader(header.name, header.value);\n });\n return new HttpResponseDto(\n new HttpResponseStatus(status.code, status.reason),\n mapped,\n control.response?.body,\n );\n }\n}\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ClientRole } from '../errors/ClientRole';
|
|
1
2
|
import { ErrorTranslator } from './ErrorTranslator';
|
|
2
3
|
import { HttpResponseDto } from './HttpResponseDto';
|
|
3
4
|
/**
|
|
@@ -18,6 +19,10 @@ import { HttpResponseDto } from './HttpResponseDto';
|
|
|
18
19
|
* {@link ApiErrorBoundary} + {@link ApiErrorHttpStatus}, the inbound half wants
|
|
19
20
|
* {@link ApiErrorCodec} + {@link ReceivedApiErrorRule}, and all four were already here.
|
|
20
21
|
*
|
|
22
|
+
* It holds no ROLE: who is receiving ({@link ClientRole}) is an argument of `fromWire`, supplied by
|
|
23
|
+
* the client that read the response from its own setup, so one stateless instance serves a server's
|
|
24
|
+
* clients and a browser's alike.
|
|
25
|
+
*
|
|
21
26
|
* Stateless, so {@link WEBPIECES_DEFAULT_ERROR_TRANSLATOR} is a shared instance — but constructing
|
|
22
27
|
* your own costs nothing and reads better at a delegation site.
|
|
23
28
|
*/
|
|
@@ -62,9 +67,12 @@ export declare class WebpiecesDefaultErrorTranslator implements ErrorTranslator
|
|
|
62
67
|
*
|
|
63
68
|
* The rule applied is {@link ReceivedApiErrorRule}, shared verbatim with IPC: ordinary 4xx is MY
|
|
64
69
|
* bug, ordinary 5xx is THEIRS, retry-relevant statuses retain their specific timeout/gateway/
|
|
65
|
-
* backoff/unavailable meaning,
|
|
70
|
+
* backoff/unavailable meaning, 266 is the end user's own answer, a 426 `client-too-old` stays
|
|
71
|
+
* {@link ApiClientTooOldError}, and a 401 depends on `role` (see {@link ClientRole}).
|
|
72
|
+
*
|
|
73
|
+
* @param role - who is receiving, declared at the client's setup. No default.
|
|
66
74
|
*/
|
|
67
|
-
fromWire(response: HttpResponseDto): void;
|
|
75
|
+
fromWire(response: HttpResponseDto, role: ClientRole): void;
|
|
68
76
|
/** The reason phrase webpieces writes beside a status it chose itself. */
|
|
69
77
|
genericMessage(code: number): string;
|
|
70
78
|
/**
|
|
@@ -26,6 +26,10 @@ const HttpResponseDto_1 = require("./HttpResponseDto");
|
|
|
26
26
|
* {@link ApiErrorBoundary} + {@link ApiErrorHttpStatus}, the inbound half wants
|
|
27
27
|
* {@link ApiErrorCodec} + {@link ReceivedApiErrorRule}, and all four were already here.
|
|
28
28
|
*
|
|
29
|
+
* It holds no ROLE: who is receiving ({@link ClientRole}) is an argument of `fromWire`, supplied by
|
|
30
|
+
* the client that read the response from its own setup, so one stateless instance serves a server's
|
|
31
|
+
* clients and a browser's alike.
|
|
32
|
+
*
|
|
29
33
|
* Stateless, so {@link WEBPIECES_DEFAULT_ERROR_TRANSLATOR} is a shared instance — but constructing
|
|
30
34
|
* your own costs nothing and reads better at a delegation site.
|
|
31
35
|
*/
|
|
@@ -44,6 +48,7 @@ class WebpiecesDefaultErrorTranslator {
|
|
|
44
48
|
[412, 'Precondition Failed'],
|
|
45
49
|
[415, 'Unsupported Media Type'],
|
|
46
50
|
[422, 'Unprocessable Content'],
|
|
51
|
+
[426, 'Upgrade Required'],
|
|
47
52
|
[429, 'Too Many Requests'],
|
|
48
53
|
[500, 'Internal Server Error'],
|
|
49
54
|
[501, 'Not Implemented'],
|
|
@@ -102,16 +107,19 @@ class WebpiecesDefaultErrorTranslator {
|
|
|
102
107
|
*
|
|
103
108
|
* The rule applied is {@link ReceivedApiErrorRule}, shared verbatim with IPC: ordinary 4xx is MY
|
|
104
109
|
* bug, ordinary 5xx is THEIRS, retry-relevant statuses retain their specific timeout/gateway/
|
|
105
|
-
* backoff/unavailable meaning,
|
|
110
|
+
* backoff/unavailable meaning, 266 is the end user's own answer, a 426 `client-too-old` stays
|
|
111
|
+
* {@link ApiClientTooOldError}, and a 401 depends on `role` (see {@link ClientRole}).
|
|
112
|
+
*
|
|
113
|
+
* @param role - who is receiving, declared at the client's setup. No default.
|
|
106
114
|
*/
|
|
107
|
-
fromWire(response) {
|
|
115
|
+
fromWire(response, role) {
|
|
108
116
|
const code = response.status.code;
|
|
109
117
|
if (code >= 200 && code < 300 && code !== 266) {
|
|
110
118
|
return;
|
|
111
119
|
}
|
|
112
120
|
const decoded = this.decodeBody(response);
|
|
113
121
|
const message = decoded?.message ?? this.fallbackMessage(response.body, response.status.reason);
|
|
114
|
-
throw ReceivedApiErrorRule_1.ReceivedApiErrorRule.adapt(code, message, decoded, this.retryAfterSeconds(response.headers));
|
|
122
|
+
throw ReceivedApiErrorRule_1.ReceivedApiErrorRule.adapt(role, code, message, decoded, this.retryAfterSeconds(response.headers));
|
|
115
123
|
}
|
|
116
124
|
/** The reason phrase webpieces writes beside a status it chose itself. */
|
|
117
125
|
genericMessage(code) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WebpiecesDefaultErrorTranslator.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/WebpiecesDefaultErrorTranslator.ts"],"names":[],"mappings":"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","sourcesContent":["import { ApiErrorBoundary } from '../errors/ApiErrorBoundary';\nimport { ApiErrorCodec } from '../errors/ApiErrorCodec';\nimport { ApiCodedError, ApiError } from '../errors/ApiError';\nimport { ReceivedApiErrorRule } from '../errors/ReceivedApiErrorRule';\nimport { ApiCallTimeoutError } from './ApiCallTimeoutError';\nimport { ApiErrorHttpStatus, PublishedKind } from './ApiErrorHttpStatus';\nimport { ErrorTranslator } from './ErrorTranslator';\nimport { HttpHeader, HttpResponseDto, HttpResponseStatus } from './HttpResponseDto';\n\n/**\n * THE webpieces default {@link ErrorTranslator} — ONE class, BOTH halves, used by the node server, by\n * every node client and by the browser bundle alike.\n *\n * It is what {@link ClientRegistry.getErrorTranslator} holds until an app installs its own, and it is\n * what an app's own translator calls to DECLINE an error:\n *\n * ```ts\n * private readonly fallback = new WebpiecesDefaultErrorTranslator();\n * ```\n *\n * so \"no translator registered\" and \"a translator that does not claim this error\" produce\n * byte-identical output. It lives in core-util, beside {@link ClientRegistry}, because core-util is\n * the package every API contract already depends on and the only one both a server and a browser\n * bundle can reach. Both halves needed nothing else: the outbound half wants\n * {@link ApiErrorBoundary} + {@link ApiErrorHttpStatus}, the inbound half wants\n * {@link ApiErrorCodec} + {@link ReceivedApiErrorRule}, and all four were already here.\n *\n * Stateless, so {@link WEBPIECES_DEFAULT_ERROR_TRANSLATOR} is a shared instance — but constructing\n * your own costs nothing and reads better at a delegation site.\n */\nexport class WebpiecesDefaultErrorTranslator implements ErrorTranslator {\n /** Reason phrase for a client deadline; see {@link ApiCallTimeoutError}. Never 'Gateway Timeout'. */\n static readonly CALL_TIMEOUT_REASON = 'Network Timeout';\n\n private readonly boundary = new ApiErrorBoundary();\n\n private readonly genericMessages: Map<number, string> = new Map<number, string>([\n [266, 'End User Error'],\n [400, 'Bad Request'],\n [401, 'Unauthorized'],\n [403, 'Forbidden'],\n [404, 'Not Found'],\n [408, 'Request Timeout'],\n [409, 'Conflict'],\n [412, 'Precondition Failed'],\n [415, 'Unsupported Media Type'],\n [422, 'Unprocessable Content'],\n [429, 'Too Many Requests'],\n [500, 'Internal Server Error'],\n [501, 'Not Implemented'],\n [502, 'Bad Gateway'],\n [503, 'Service Unavailable'],\n [504, 'Gateway Timeout'],\n ]);\n\n /**\n * SERVER: an exception in, the exact HTTP response out.\n *\n * The payload IS the answer. {@link ApiErrorBoundary.encode} has already applied both shared\n * rules — a caller-local `ApiConnectionError` (and every non-`ApiError`) publishes as\n * `implementation`, and only an `ApiEndUserError`'s own message is disclosed — and the payload\n * carries everything HTTP needs after that: `kind`, `statusCode`, `edgeHttpStatus` and\n * `retryAfterSeconds`. So there is no `instanceof` here and nothing re-derived from the raw\n * error; unknown throws and caller-local failures come out as a 500 implementation failure\n * because that is what the payload says.\n *\n * An `ApiEndUserError` publishes 266 here, ALWAYS. Whether a particular REQUEST republishes that\n * as a real 4xx is a property of the CALLER, not of the error, so it is decided once per request\n * from `WebpiecesCoreHeaders.SURFACE` by the server boundary (`ExpressWrapper.handleError`) —\n * see {@link SurfaceEndUserStatus}. That is why this class takes no constructor argument any\n * more: the old `new ApiErrorHttpMapper('gui' | 'edge')` made every delegating app translator\n * responsible for repeating its router's mode, and repeating it wrongly was silent.\n */\n toWire(error: Error): HttpResponseDto {\n const payload = this.boundary.encode(error);\n // The boundary never publishes kind 'connection' — that is the whole first rule above — so\n // this is the published subset ApiErrorHttpStatus.codeFor accepts.\n const kind = payload.kind as PublishedKind;\n const status = ApiErrorHttpStatus.codeFor(kind, payload.statusCode);\n const headers =\n payload.retryAfterSeconds !== undefined\n ? [new HttpHeader('retry-after', String(payload.retryAfterSeconds))]\n : [];\n return new HttpResponseDto(\n new HttpResponseStatus(status, this.reasonPhrase(error, status)),\n headers,\n payload,\n );\n }\n\n /**\n * The reason phrase for `error`. An {@link ApiCallTimeoutError} is THIS process's own client\n * deadline firing on an outbound call: no gateway was involved, so it is never titled\n * 'Gateway Timeout' (issue #1170). It keeps its 504 status and `dependency-timeout` payload —\n * retry semantics are unchanged — and only the human framing differs.\n */\n private reasonPhrase(error: Error, status: number): string {\n if (error instanceof ApiCallTimeoutError) {\n return WebpiecesDefaultErrorTranslator.CALL_TIMEOUT_REASON;\n }\n return this.genericMessage(status);\n }\n\n /**\n * CLIENT: the response in, a THROW out — or a silent return for an ordinary success.\n *\n * Called for EVERY response, 2xx included (see {@link ErrorTranslator.fromWire}). A 2xx that is\n * not 266 is exactly the case where webpieces has nothing to say, so it returns and the caller\n * gets its DTO. Everything else throws, which is what lets `ProxyClient` call this and then simply\n * stop — a failure response can never fall through to a typed caller.\n *\n * The rule applied is {@link ReceivedApiErrorRule}, shared verbatim with IPC: ordinary 4xx is MY\n * bug, ordinary 5xx is THEIRS, retry-relevant statuses retain their specific timeout/gateway/\n * backoff/unavailable meaning, and 266 is the end user's own answer.\n */\n fromWire(response: HttpResponseDto): void {\n const code = response.status.code;\n if (code >= 200 && code < 300 && code !== 266) {\n return;\n }\n const decoded = this.decodeBody(response);\n const message =\n decoded?.message ?? this.fallbackMessage(response.body, response.status.reason);\n throw ReceivedApiErrorRule.adapt(\n code,\n message,\n decoded,\n this.retryAfterSeconds(response.headers),\n );\n }\n\n /** The reason phrase webpieces writes beside a status it chose itself. */\n genericMessage(code: number): string {\n return this.genericMessages.get(code) ?? 'Request Failed';\n }\n\n /**\n * A webpieces peer sends an {@link ApiErrorPayload}; a foreign responder sends anything at all.\n * The status is only believed when it AGREES with the kind the body claims — a body saying\n * `not-found` beside a 500 is a mismatched response, not a 404, and falls back to the status.\n */\n private decodeBody(response: HttpResponseDto): ApiError | undefined {\n if (!ApiErrorCodec.isPayload(response.body)) {\n return undefined;\n }\n const decoded = ApiErrorCodec.decode(response.body);\n if (!ApiErrorHttpStatus.hasCode(decoded)) {\n return undefined;\n }\n // `statusCode` is the 'coded' kind's own status, the one thing the shared table cannot know.\n const statusCode = decoded instanceof ApiCodedError ? decoded.statusCode : undefined;\n const declared = ApiErrorHttpStatus.codeFor(decoded.kind, statusCode);\n return declared === response.status.code ? decoded : undefined;\n }\n\n // webpieces-disable no-any-unknown -- HTTP response bodies are app-owned until this boundary safely inspects them\n private fallbackMessage(body: unknown, reason: string): string {\n if (typeof body === 'object' && body !== null) {\n const message = Object.getOwnPropertyDescriptor(body, 'message')?.value;\n if (typeof message === 'string' && message.length > 0) return message.slice(0, 4096);\n }\n return reason || 'Request Failed';\n }\n\n /** Parse either legal Retry-After form and bound untrusted guidance to one day. */\n private retryAfterSeconds(headers: readonly HttpHeader[]): number | undefined {\n const raw = headers.find(\n (header: HttpHeader) => header.name.toLowerCase() === 'retry-after',\n )?.value;\n if (raw === undefined) return undefined;\n const seconds = Number(raw.trim());\n if (Number.isFinite(seconds) && seconds >= 0) return Math.min(Math.ceil(seconds), 86_400);\n const at = Date.parse(raw);\n if (!Number.isFinite(at)) return undefined;\n return Math.min(Math.max(Math.ceil((at - Date.now()) / 1_000), 0), 86_400);\n }\n}\n\n/** Process-wide built-in instance — stateless, so one shared instance is enough. */\nexport const WEBPIECES_DEFAULT_ERROR_TRANSLATOR = new WebpiecesDefaultErrorTranslator();\n"]}
|
|
1
|
+
{"version":3,"file":"WebpiecesDefaultErrorTranslator.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/WebpiecesDefaultErrorTranslator.ts"],"names":[],"mappings":"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","sourcesContent":["import { ApiErrorBoundary } from '../errors/ApiErrorBoundary';\nimport { ApiErrorCodec } from '../errors/ApiErrorCodec';\nimport { ApiCodedError, ApiError } from '../errors/ApiError';\nimport { ClientRole } from '../errors/ClientRole';\nimport { ReceivedApiErrorRule } from '../errors/ReceivedApiErrorRule';\nimport { ApiCallTimeoutError } from './ApiCallTimeoutError';\nimport { ApiErrorHttpStatus, PublishedKind } from './ApiErrorHttpStatus';\nimport { ErrorTranslator } from './ErrorTranslator';\nimport { HttpHeader, HttpResponseDto, HttpResponseStatus } from './HttpResponseDto';\n\n/**\n * THE webpieces default {@link ErrorTranslator} — ONE class, BOTH halves, used by the node server, by\n * every node client and by the browser bundle alike.\n *\n * It is what {@link ClientRegistry.getErrorTranslator} holds until an app installs its own, and it is\n * what an app's own translator calls to DECLINE an error:\n *\n * ```ts\n * private readonly fallback = new WebpiecesDefaultErrorTranslator();\n * ```\n *\n * so \"no translator registered\" and \"a translator that does not claim this error\" produce\n * byte-identical output. It lives in core-util, beside {@link ClientRegistry}, because core-util is\n * the package every API contract already depends on and the only one both a server and a browser\n * bundle can reach. Both halves needed nothing else: the outbound half wants\n * {@link ApiErrorBoundary} + {@link ApiErrorHttpStatus}, the inbound half wants\n * {@link ApiErrorCodec} + {@link ReceivedApiErrorRule}, and all four were already here.\n *\n * It holds no ROLE: who is receiving ({@link ClientRole}) is an argument of `fromWire`, supplied by\n * the client that read the response from its own setup, so one stateless instance serves a server's\n * clients and a browser's alike.\n *\n * Stateless, so {@link WEBPIECES_DEFAULT_ERROR_TRANSLATOR} is a shared instance — but constructing\n * your own costs nothing and reads better at a delegation site.\n */\nexport class WebpiecesDefaultErrorTranslator implements ErrorTranslator {\n /** Reason phrase for a client deadline; see {@link ApiCallTimeoutError}. Never 'Gateway Timeout'. */\n static readonly CALL_TIMEOUT_REASON = 'Network Timeout';\n\n private readonly boundary = new ApiErrorBoundary();\n\n private readonly genericMessages: Map<number, string> = new Map<number, string>([\n [266, 'End User Error'],\n [400, 'Bad Request'],\n [401, 'Unauthorized'],\n [403, 'Forbidden'],\n [404, 'Not Found'],\n [408, 'Request Timeout'],\n [409, 'Conflict'],\n [412, 'Precondition Failed'],\n [415, 'Unsupported Media Type'],\n [422, 'Unprocessable Content'],\n [426, 'Upgrade Required'],\n [429, 'Too Many Requests'],\n [500, 'Internal Server Error'],\n [501, 'Not Implemented'],\n [502, 'Bad Gateway'],\n [503, 'Service Unavailable'],\n [504, 'Gateway Timeout'],\n ]);\n\n /**\n * SERVER: an exception in, the exact HTTP response out.\n *\n * The payload IS the answer. {@link ApiErrorBoundary.encode} has already applied both shared\n * rules — a caller-local `ApiConnectionError` (and every non-`ApiError`) publishes as\n * `implementation`, and only an `ApiEndUserError`'s own message is disclosed — and the payload\n * carries everything HTTP needs after that: `kind`, `statusCode`, `edgeHttpStatus` and\n * `retryAfterSeconds`. So there is no `instanceof` here and nothing re-derived from the raw\n * error; unknown throws and caller-local failures come out as a 500 implementation failure\n * because that is what the payload says.\n *\n * An `ApiEndUserError` publishes 266 here, ALWAYS. Whether a particular REQUEST republishes that\n * as a real 4xx is a property of the CALLER, not of the error, so it is decided once per request\n * from `WebpiecesCoreHeaders.SURFACE` by the server boundary (`ExpressWrapper.handleError`) —\n * see {@link SurfaceEndUserStatus}. That is why this class takes no constructor argument any\n * more: the old `new ApiErrorHttpMapper('gui' | 'edge')` made every delegating app translator\n * responsible for repeating its router's mode, and repeating it wrongly was silent.\n */\n toWire(error: Error): HttpResponseDto {\n const payload = this.boundary.encode(error);\n // The boundary never publishes kind 'connection' — that is the whole first rule above — so\n // this is the published subset ApiErrorHttpStatus.codeFor accepts.\n const kind = payload.kind as PublishedKind;\n const status = ApiErrorHttpStatus.codeFor(kind, payload.statusCode);\n const headers =\n payload.retryAfterSeconds !== undefined\n ? [new HttpHeader('retry-after', String(payload.retryAfterSeconds))]\n : [];\n return new HttpResponseDto(\n new HttpResponseStatus(status, this.reasonPhrase(error, status)),\n headers,\n payload,\n );\n }\n\n /**\n * The reason phrase for `error`. An {@link ApiCallTimeoutError} is THIS process's own client\n * deadline firing on an outbound call: no gateway was involved, so it is never titled\n * 'Gateway Timeout' (issue #1170). It keeps its 504 status and `dependency-timeout` payload —\n * retry semantics are unchanged — and only the human framing differs.\n */\n private reasonPhrase(error: Error, status: number): string {\n if (error instanceof ApiCallTimeoutError) {\n return WebpiecesDefaultErrorTranslator.CALL_TIMEOUT_REASON;\n }\n return this.genericMessage(status);\n }\n\n /**\n * CLIENT: the response in, a THROW out — or a silent return for an ordinary success.\n *\n * Called for EVERY response, 2xx included (see {@link ErrorTranslator.fromWire}). A 2xx that is\n * not 266 is exactly the case where webpieces has nothing to say, so it returns and the caller\n * gets its DTO. Everything else throws, which is what lets `ProxyClient` call this and then simply\n * stop — a failure response can never fall through to a typed caller.\n *\n * The rule applied is {@link ReceivedApiErrorRule}, shared verbatim with IPC: ordinary 4xx is MY\n * bug, ordinary 5xx is THEIRS, retry-relevant statuses retain their specific timeout/gateway/\n * backoff/unavailable meaning, 266 is the end user's own answer, a 426 `client-too-old` stays\n * {@link ApiClientTooOldError}, and a 401 depends on `role` (see {@link ClientRole}).\n *\n * @param role - who is receiving, declared at the client's setup. No default.\n */\n fromWire(response: HttpResponseDto, role: ClientRole): void {\n const code = response.status.code;\n if (code >= 200 && code < 300 && code !== 266) {\n return;\n }\n const decoded = this.decodeBody(response);\n const message =\n decoded?.message ?? this.fallbackMessage(response.body, response.status.reason);\n throw ReceivedApiErrorRule.adapt(\n role,\n code,\n message,\n decoded,\n this.retryAfterSeconds(response.headers),\n );\n }\n\n /** The reason phrase webpieces writes beside a status it chose itself. */\n genericMessage(code: number): string {\n return this.genericMessages.get(code) ?? 'Request Failed';\n }\n\n /**\n * A webpieces peer sends an {@link ApiErrorPayload}; a foreign responder sends anything at all.\n * The status is only believed when it AGREES with the kind the body claims — a body saying\n * `not-found` beside a 500 is a mismatched response, not a 404, and falls back to the status.\n */\n private decodeBody(response: HttpResponseDto): ApiError | undefined {\n if (!ApiErrorCodec.isPayload(response.body)) {\n return undefined;\n }\n const decoded = ApiErrorCodec.decode(response.body);\n if (!ApiErrorHttpStatus.hasCode(decoded)) {\n return undefined;\n }\n // `statusCode` is the 'coded' kind's own status, the one thing the shared table cannot know.\n const statusCode = decoded instanceof ApiCodedError ? decoded.statusCode : undefined;\n const declared = ApiErrorHttpStatus.codeFor(decoded.kind, statusCode);\n return declared === response.status.code ? decoded : undefined;\n }\n\n // webpieces-disable no-any-unknown -- HTTP response bodies are app-owned until this boundary safely inspects them\n private fallbackMessage(body: unknown, reason: string): string {\n if (typeof body === 'object' && body !== null) {\n const message = Object.getOwnPropertyDescriptor(body, 'message')?.value;\n if (typeof message === 'string' && message.length > 0) return message.slice(0, 4096);\n }\n return reason || 'Request Failed';\n }\n\n /** Parse either legal Retry-After form and bound untrusted guidance to one day. */\n private retryAfterSeconds(headers: readonly HttpHeader[]): number | undefined {\n const raw = headers.find(\n (header: HttpHeader) => header.name.toLowerCase() === 'retry-after',\n )?.value;\n if (raw === undefined) return undefined;\n const seconds = Number(raw.trim());\n if (Number.isFinite(seconds) && seconds >= 0) return Math.min(Math.ceil(seconds), 86_400);\n const at = Date.parse(raw);\n if (!Number.isFinite(at)) return undefined;\n return Math.min(Math.max(Math.ceil((at - Date.now()) / 1_000), 0), 86_400);\n }\n}\n\n/** Process-wide built-in instance — stateless, so one shared instance is enough. */\nexport const WEBPIECES_DEFAULT_ERROR_TRANSLATOR = new WebpiecesDefaultErrorTranslator();\n"]}
|
package/src/index.d.ts
CHANGED
|
@@ -98,7 +98,7 @@ export { RecordSerializer, SerializedMap, SerializedError } from './http/recorde
|
|
|
98
98
|
export { Filter } from './filters/Filter';
|
|
99
99
|
export type { Service } from './filters/Filter';
|
|
100
100
|
export { FilterChain } from './filters/FilterChain';
|
|
101
|
-
export { ApiError, ApiNotFoundError, ApiDependencyError, ApiBadGatewayError, ApiUnavailableError, ApiDependencyTimeoutError, ApiImplementationError, ApiRateLimitedError, ApiForbiddenError, ApiRequestTimeoutError, ApiEndUserError, ApiBadRequestError, ApiUnauthorizedError, ApiDependencyBackoffError, ApiConnectionError, ApiEndpointNotFoundError, ApiConflictError, ApiUnprocessableError, ApiPreconditionFailedError, ApiUnsupportedMediaTypeError, ApiNotImplementedError, ApiCodedError, ApiErrorCodec, ApiErrorPayload, ApiErrorBoundary, } from './errors';
|
|
102
|
-
export type { ApiErrorKind, ApiStatusCode, EdgeHttpStatus } from './errors';
|
|
101
|
+
export { ApiError, ApiNotFoundError, ApiDependencyError, ApiBadGatewayError, ApiUnavailableError, ApiDependencyTimeoutError, ApiImplementationError, ApiRateLimitedError, ApiForbiddenError, ApiRequestTimeoutError, ApiEndUserError, ApiBadRequestError, ApiUnauthorizedError, ApiDependencyBackoffError, ApiConnectionError, ApiEndpointNotFoundError, ApiConflictError, ApiUnprocessableError, ApiPreconditionFailedError, ApiUnsupportedMediaTypeError, ApiNotImplementedError, ApiCodedError, ApiClientTooOldError, ApiEndUserNotFoundError, ApiEndUserForbiddenError, ApiEndUserBadRequestError, EndUserErrorCode, EndUserErrorRegistry, ClientRole, ApiErrorCodec, ApiErrorPayload, ApiErrorBoundary, } from './errors';
|
|
102
|
+
export type { ApiErrorKind, ApiStatusCode, EdgeHttpStatus, EndUserErrorFactory, } from './errors';
|
|
103
103
|
export { ApiErrorHttpStatus } from './http/ApiErrorHttpStatus';
|
|
104
104
|
export type { PublishedKind, PublishedApiError } from './http/ApiErrorHttpStatus';
|
package/src/index.js
CHANGED
|
@@ -12,7 +12,7 @@ exports.MCP = exports.EXTERNAL_CUSTOMER = exports.SVC_TO_SVC = exports.ApiType =
|
|
|
12
12
|
exports.InvalidEndpointForMcp = exports.WRITE = exports.WRITE_IDEMPOTENT = exports.READ = exports.EXTERNAL = exports.CRON = exports.CLOUDTASKS = exports.RPC = exports.POST = exports.GET = exports.registerStreamingSchemas = exports.getStreamingEndpoint = exports.WpStream = exports.StreamDirection = exports.StreamWriter = exports.StreamTransportError = exports.StreamEventValidator = exports.StreamingEndpointMetadata = exports.StreamEnvelope = exports.StreamCorrelation = exports.HttpParameterDeclaration = exports.HttpParameterBinding = exports.HttpContractMapper = exports.BoundHttpRequest = exports.StreamDisconnectedError = exports.StreamProtocolError = exports.StreamErrorControl = exports.registerStreamingCatalog = exports.StreamingSchemaCatalog = exports.getAuthorization = exports.WpAuthorization = exports.AuthorizationDeclaration = exports.AuthorizationType = exports.RouteMetadataFactory = exports.RouteMetadata = exports.IPC_METADATA_KEYS = exports.assertNotInternalApi = exports.assertInternalApi = exports.getIpcMaskSpec = exports.getIpcEndpointKind = exports.getIpcEndpoints = exports.getIpcApiId = exports.isInternalApi = exports.WpIpcEndpoint = exports.WpInternal = exports.assertApiTypeMatchesMcpTools = exports.getApiTypeEndpointNames = exports.getApiTypes = exports.DEFAULT_API_TYPES = exports.API_TYPE_METADATA_KEY = void 0;
|
|
13
13
|
exports.NetworkRejectClassifier = exports.NO_REG_CODE = exports.WRONG_COMPANY = exports.WRONG_DOMAIN = exports.EMAIL_NOT_CONFIRMED = exports.NOT_APPROVED = exports.WRONG_LOGIN = exports.WRONG_LOGIN_TYPE = exports.ENTITY_NOT_FOUND = exports.SECRETS = exports.Secrets = exports.getEndpointCaller = exports.isExternalSystemKind = exports.ExternalCaller = exports.DEFAULT_CALLER_KIND = exports.EXTERNAL_SYSTEM_KINDS = exports.getQueueName = exports.assertPubSubConventions = exports.assertApiKind = exports.getApiKind = exports.ENDPOINT_KINDS_BY_API_KIND = exports.Queue = exports.PubSub = exports.Rpc = exports.apiKey = exports.webhook = exports.sharedSecret = exports.oidc = exports.jwt = exports.AuthDeclaration = exports.AuthMeta = exports.GenerateOutputTarget = exports.GenerateOutputLookup = exports.GenerateLayoutProblem = exports.GeneratedApiDocsLayout = exports.McpToolDefinition = exports.McpToolCatalogError = exports.McpToolCatalogFile = exports.ObjectSchemaBuilder = exports.DtoValidationFailure = exports.ApiJsonSchemaValidator = exports.ApiJsonSchemaDiscriminator = exports.ApiJsonSchema = exports.mcpHintsForOperation = exports.getWpMcpTools = exports.getInvalidEndpointsForMcp = exports.WpMcpToolMetadata = exports.WpMcpToolHints = exports.WpMcpTool = exports.InvalidEndpointForMcpMetadata = void 0;
|
|
14
14
|
exports.ApiDependencyTimeoutError = exports.ApiUnavailableError = exports.ApiBadGatewayError = exports.ApiDependencyError = exports.ApiNotFoundError = exports.ApiError = exports.FilterChain = exports.Filter = exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.ApiMethodInfo = exports.LOG_API_CALL_LOGGER_NAME = exports.ApiCallLogNameImpl = exports.ApiCallLogName = exports.ApiCallInfo = exports.MaskSpec = exports.LogApiCallImpl = exports.ContextMgr = exports.DestinationTrust = exports.WebpiecesCoreHeaders = exports.templateDeriver = exports.WEBPIECES_DEFAULT_FAILURE_CLASSIFIER = exports.WebpiecesDefaultFailureClassifier = exports.KeyedFailureClassifier = exports.ReceivedApiErrorRule = exports.SurfaceEndUserStatus = exports.WEBPIECES_DEFAULT_ERROR_TRANSLATOR = exports.WebpiecesDefaultErrorTranslator = exports.HttpResponseDto = exports.HttpResponseStatus = exports.HttpHeader = exports.RuntimeLocality = exports.ServiceInfo = exports.ApiCallTimeoutError = exports.CallContext = exports.CallDeadline = exports.CallRegistry = exports.ClientRegistry = exports.HeaderRegistry = exports.DateTimeUtil = exports.TimeUtil = exports.DateUtil = exports.InstantUtil = void 0;
|
|
15
|
-
exports.ApiErrorHttpStatus = exports.ApiErrorBoundary = exports.ApiErrorPayload = exports.ApiErrorCodec = exports.ApiCodedError = exports.ApiNotImplementedError = exports.ApiUnsupportedMediaTypeError = exports.ApiPreconditionFailedError = exports.ApiUnprocessableError = exports.ApiConflictError = exports.ApiEndpointNotFoundError = exports.ApiConnectionError = exports.ApiDependencyBackoffError = exports.ApiUnauthorizedError = exports.ApiBadRequestError = exports.ApiEndUserError = exports.ApiRequestTimeoutError = exports.ApiForbiddenError = exports.ApiRateLimitedError = exports.ApiImplementationError = void 0;
|
|
15
|
+
exports.ApiErrorHttpStatus = exports.ApiErrorBoundary = exports.ApiErrorPayload = exports.ApiErrorCodec = exports.ClientRole = exports.EndUserErrorRegistry = exports.EndUserErrorCode = exports.ApiEndUserBadRequestError = exports.ApiEndUserForbiddenError = exports.ApiEndUserNotFoundError = exports.ApiClientTooOldError = exports.ApiCodedError = exports.ApiNotImplementedError = exports.ApiUnsupportedMediaTypeError = exports.ApiPreconditionFailedError = exports.ApiUnprocessableError = exports.ApiConflictError = exports.ApiEndpointNotFoundError = exports.ApiConnectionError = exports.ApiDependencyBackoffError = exports.ApiUnauthorizedError = exports.ApiBadRequestError = exports.ApiEndUserError = exports.ApiRequestTimeoutError = exports.ApiForbiddenError = exports.ApiRateLimitedError = exports.ApiImplementationError = void 0;
|
|
16
16
|
var errorUtils_1 = require("./lib/errorUtils");
|
|
17
17
|
Object.defineProperty(exports, "toError", { enumerable: true, get: function () { return errorUtils_1.toError; } });
|
|
18
18
|
var ContextKey_1 = require("./ContextKey");
|
|
@@ -344,6 +344,13 @@ Object.defineProperty(exports, "ApiPreconditionFailedError", { enumerable: true,
|
|
|
344
344
|
Object.defineProperty(exports, "ApiUnsupportedMediaTypeError", { enumerable: true, get: function () { return errors_2.ApiUnsupportedMediaTypeError; } });
|
|
345
345
|
Object.defineProperty(exports, "ApiNotImplementedError", { enumerable: true, get: function () { return errors_2.ApiNotImplementedError; } });
|
|
346
346
|
Object.defineProperty(exports, "ApiCodedError", { enumerable: true, get: function () { return errors_2.ApiCodedError; } });
|
|
347
|
+
Object.defineProperty(exports, "ApiClientTooOldError", { enumerable: true, get: function () { return errors_2.ApiClientTooOldError; } });
|
|
348
|
+
Object.defineProperty(exports, "ApiEndUserNotFoundError", { enumerable: true, get: function () { return errors_2.ApiEndUserNotFoundError; } });
|
|
349
|
+
Object.defineProperty(exports, "ApiEndUserForbiddenError", { enumerable: true, get: function () { return errors_2.ApiEndUserForbiddenError; } });
|
|
350
|
+
Object.defineProperty(exports, "ApiEndUserBadRequestError", { enumerable: true, get: function () { return errors_2.ApiEndUserBadRequestError; } });
|
|
351
|
+
Object.defineProperty(exports, "EndUserErrorCode", { enumerable: true, get: function () { return errors_2.EndUserErrorCode; } });
|
|
352
|
+
Object.defineProperty(exports, "EndUserErrorRegistry", { enumerable: true, get: function () { return errors_2.EndUserErrorRegistry; } });
|
|
353
|
+
Object.defineProperty(exports, "ClientRole", { enumerable: true, get: function () { return errors_2.ClientRole; } });
|
|
347
354
|
Object.defineProperty(exports, "ApiErrorCodec", { enumerable: true, get: function () { return errors_2.ApiErrorCodec; } });
|
|
348
355
|
Object.defineProperty(exports, "ApiErrorPayload", { enumerable: true, get: function () { return errors_2.ApiErrorPayload; } });
|
|
349
356
|
Object.defineProperty(exports, "ApiErrorBoundary", { enumerable: true, get: function () { return errors_2.ApiErrorBoundary; } });
|
package/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @webpieces/core-util\n *\n * Utility functions for WebPieces applications.\n * This package works in both browser and Node.js environments.\n *\n * @packageDocumentation\n */\n\nexport { toError } from './lib/errorUtils';\nexport { ContextKey } from './ContextKey';\nexport type {\n AnyContextKey,\n AnyTrustedContextKey,\n AnyUntrustedContextKey,\n Trust,\n} from './ContextKey';\nexport { ContextTuple } from './ContextTuple';\n\n// @DocumentDesign — DI-design-root marker. Applies to ANY project kind (server\n// controllers AND library impl classes), so it lives here (browser + Node) rather\n// than in a server-only routing package.\nexport { DocumentDesign, isDocumentDesign, DESIGN_METADATA_KEYS } from './DocumentDesign';\n\n// Logging (merged from former @webpieces/wp-logging).\n// Pluggable logging interface + a browser-safe console default; apps plug in\n// bunyan/winston/pino/etc. via LogManager.setFactory(...). Browser + Node.\nexport type { Logger, LogLevel } from './logging/Logger';\nexport type { LoggerFactory } from './logging/LoggerFactory';\nexport { ConsoleLogger } from './logging/ConsoleLogger';\nexport { ConsoleLoggerFactory } from './logging/ConsoleLoggerFactory';\nexport { LogManager } from './logging/LogManager';\nexport {\n LogChunker,\n LogChunkerImpl,\n LogChunkInfo,\n MAX_GCP_LOG_BYTES,\n GCP_LOG_BUDGET_BYTES,\n} from './logging/LogChunker';\n\n// HTTP API contract (merged from former @webpieces/http-api).\n// Shared HTTP API definition consumed by both client and server: REST\n// decorators, the API error taxonomy, datetime DTOs, platform-header\n// registry/readers, ValidateImplementation, and the test-case recorder\n// contract. Pure definitions — express-free, browser + Node safe.\n\n// API definition decorators\nexport {\n ApiPath,\n Endpoint,\n // Auth mode decorators (clean service-to-service + user JWT model)\n WpAuthPublic,\n WpAuth,\n MISSING_AUTH_DECORATOR_FIX,\n WpLocalOnly,\n isLocalOnly,\n MaskLog,\n getApiPath,\n getEndpoints,\n getEndpointHttpMethod,\n getEndpointOptions,\n getEndpointOperation,\n getEndpointKind,\n getEndpointKinds,\n getMaskSpec,\n isFormPost,\n isRawBody,\n isApiPath,\n getAuthMeta,\n assertEveryEndpointHasAuthMode,\n assertEveryExternalEndpointDeclaresCaller,\n assertEveryWebhookEndpointRetainsRawBody,\n validateNoConflictingDecorators,\n METADATA_KEYS,\n} from './http/decorators';\nexport {\n PathParam,\n QueryParam,\n getHttpParameterDeclarations,\n} from './http/http-parameter-decorators';\nexport { WpInt, WpMin, WpMax, NUMERIC_METADATA_KEYS } from './http/numeric-field-decorators';\nexport type { Integer } from './http/numeric-field-decorators';\nexport {\n ApiType,\n SVC_TO_SVC,\n EXTERNAL_CUSTOMER,\n MCP,\n API_TYPE_METADATA_KEY,\n DEFAULT_API_TYPES,\n getApiTypes,\n getApiTypeEndpointNames,\n assertApiTypeMatchesMcpTools,\n} from './http/api-type';\nexport type { ApiTypeKind } from './http/api-type';\nexport {\n WpInternal,\n WpIpcEndpoint,\n isInternalApi,\n getIpcApiId,\n getIpcEndpoints,\n getIpcEndpointKind,\n getIpcMaskSpec,\n assertInternalApi,\n assertNotInternalApi,\n IPC_METADATA_KEYS,\n} from './ipc/IpcDecorators';\nexport type { IpcApiType, IpcEndpointKind, IpcEndpointOptions } from './ipc/IpcDecorators';\n// The runtime representation of ONE route (split out of decorators.ts for file size only).\nexport { RouteMetadata } from './http/RouteMetadata';\nexport { RouteMetadataFactory } from './http/RouteMetadataFactory';\nexport { AuthorizationType, AuthorizationDeclaration, WpAuthorization, getAuthorization } from './http/authorization';\nexport type { AuthorizationRequirement } from './http/authorization';\nexport { StreamingSchemaCatalog, registerStreamingCatalog } from './http/StreamingSchemaCatalog';\nexport {\n StreamErrorControl,\n StreamProtocolError,\n StreamDisconnectedError,\n} from './http/StreamErrorControl';\nexport {\n BoundHttpRequest,\n HttpContractMapper,\n HttpParameterBinding,\n HttpParameterDeclaration,\n} from './http/HttpContract';\nexport {\n StreamCorrelation,\n StreamEnvelope,\n StreamingEndpointMetadata,\n StreamEventValidator,\n StreamTransportError,\n StreamWriter,\n StreamDirection,\n WpStream,\n getStreamingEndpoint,\n registerStreamingSchemas,\n} from './http/StreamingContract';\nexport type { RequestStream, ResponseStream, StreamEnvelopeKind } from './http/StreamingContract';\nexport type {\n EndpointResponseType,\n HttpMethod,\n HttpParameterSource,\n HttpParameterValueType,\n} from './http/HttpContract';\nexport { GET, POST } from './http/HttpContract';\nexport type {\n EndpointKind,\n EndpointOperation,\n EndpointOptions,\n ExternalEndpointOptions,\n} from './http/decorators';\nexport { RPC, CLOUDTASKS, CRON, EXTERNAL, READ, WRITE_IDEMPOTENT, WRITE } from './http/decorators';\nexport {\n InvalidEndpointForMcp,\n InvalidEndpointForMcpMetadata,\n WpMcpTool,\n WpMcpToolHints,\n WpMcpToolMetadata,\n getInvalidEndpointsForMcp,\n getWpMcpTools,\n mcpHintsForOperation,\n} from './mcp/McpMetadata';\nexport type { WpMcpMethodDecorator } from './mcp/McpMetadata';\nexport {\n ApiJsonSchema,\n ApiJsonSchemaDiscriminator,\n ApiJsonSchemaValidator,\n DtoValidationFailure,\n ObjectSchemaBuilder,\n} from './mcp/DtoSchema';\nexport type { ApiJsonSchemaType, DtoValue } from './mcp/DtoSchema';\nexport {\n McpToolCatalogFile,\n McpToolCatalogError,\n McpToolDefinition,\n} from './mcp/McpToolCatalogFile';\nexport {\n GeneratedApiDocsLayout,\n GenerateLayoutProblem,\n GenerateOutputLookup,\n GenerateOutputTarget,\n} from './mcp/GeneratedApiDocsLayout';\nexport type {\n LayoutDependsOn,\n LayoutDependsOnObject,\n LayoutTarget,\n LayoutTargets,\n} from './mcp/GeneratedApiDocsLayout';\n// The TYPE layer of the auth surface — likewise split out of decorators.ts for file size only.\nexport { AuthMeta, AuthDeclaration, jwt, oidc, sharedSecret, webhook, apiKey } from './http/auth-mode';\nexport type {\n AuthMode,\n ApiKeyCredential,\n ApiKeyCredentials,\n AuthMethod,\n AuthMethods,\n} from './http/auth-mode';\n// API kind (RPC vs PubSub/Cloud Tasks) + queue naming. Split out of decorators.ts for file size only;\n// one-way dependency api-kind -> decorators, and the barrel keeps the surface identical.\nexport {\n Rpc,\n PubSub,\n Queue,\n ENDPOINT_KINDS_BY_API_KIND,\n getApiKind,\n assertApiKind,\n assertPubSubConventions,\n getQueueName,\n} from './http/api-kind';\nexport type { ApiKind } from './http/api-kind';\n// WHO calls an `external` endpoint — the caller declaration\n// @Endpoint(POST, ..., WRITE, EXTERNAL, { calledBy })\n// requires, and the reader for it.\nexport {\n EXTERNAL_SYSTEM_KINDS,\n DEFAULT_CALLER_KIND,\n ExternalCaller,\n isExternalSystemKind,\n getEndpointCaller,\n} from './http/external-caller';\nexport type { ExternalSystemKind } from './http/external-caller';\n// Client-side shared-secret store (the value THIS service sends per sharedSecret(...) key).\nexport { Secrets, SECRETS } from './http/Secrets';\n\n// Type validators\nexport { ValidateImplementation } from './http/validators';\n\n// Authentication subtype constants.\nexport {\n ENTITY_NOT_FOUND,\n WRONG_LOGIN_TYPE,\n WRONG_LOGIN,\n NOT_APPROVED,\n EMAIL_NOT_CONFIRMED,\n WRONG_DOMAIN,\n WRONG_COMPANY,\n NO_REG_CODE,\n} from './errors';\n\nexport { NetworkRejectClassifier } from './http/networkReject';\n\n// Date/Time DTOs and Utilities (inspired by Java Time / JSR-310)\nexport {\n InstantDto,\n DateDto,\n TimeDto,\n DateTimeDto,\n InstantUtil,\n DateUtil,\n TimeUtil,\n DateTimeUtil,\n} from './http/datetime';\n\n// Context keys + registry (the global magic-context header system)\nexport { HeaderRegistry } from './http/HeaderRegistry';\nexport { ClientRegistry } from './http/ClientRegistry';\nexport { CallRegistry } from './http/CallRegistry';\nexport { CallDeadline } from './http/CallDeadline';\nexport { CallContext } from './http/CallStrategy';\nexport type { Attempt, CallStrategy } from './http/CallStrategy';\nexport { ApiCallTimeoutError } from './http/ApiCallTimeoutError';\nexport type { ServiceUrlDeriver } from './http/ClientRegistry';\n\n// \"What service am I\" — set once at startup, read by the logging backends and by\n// RequestContextHeaders (to stamp requestIdSource on ids this service mints).\nexport { ServiceInfo } from './http/ServiceInfo';\n// \"Where am I running\" — declared once at startup (setupRuntime, from RuntimeSetupOptions.locality).\n// The ONE input to @WpLocalOnly enforcement. Undeclared reads as DEPLOYED (fail safe).\nexport { RuntimeLocality } from './http/RuntimeLocality';\nexport type { Locality } from './http/RuntimeLocality';\n// The ENTIRE HTTP response as pure data — the ONE form both transports (express, fetch) are\n// normalised into, so an ErrorTranslator implementation is written once and serves both.\nexport { HttpHeader, HttpResponseStatus, HttpResponseDto } from './http/HttpResponseDto';\n// Pluggable, bidirectional error translation (app exception <-> the WHOLE response). Set on\n// ClientRegistry at startup; consulted before the built-in webpieces mapping on BOTH sides.\nexport type { ErrorTranslator } from './http/ErrorTranslator';\n// THE webpieces default translator — always installed, and what an app's own translator delegates to\n// in order to decline an error. ONE class, both halves, node and browser alike.\nexport {\n WebpiecesDefaultErrorTranslator,\n WEBPIECES_DEFAULT_ERROR_TRANSLATOR,\n} from './http/WebpiecesDefaultErrorTranslator';\n// WHICH KIND OF CALLER drove this request (gui / llm / public-api), and the per-request end-user\n// status derived from it — the replacement for the deleted per-router EndUserStatus.\nexport { SurfaceEndUserStatus } from './http/Surface';\nexport type { Surface } from './http/Surface';\n// The ONE rule for an error RECEIVED from another process, shared verbatim by HTTP and IPC.\nexport { ReceivedApiErrorRule } from './errors/ReceivedApiErrorRule';\n// Pluggable per-client failure classification (is a thrown API-call error a real failure or an\n// expected non-failure?). Registered on ClientRegistry at startup; consulted by LogApiCall.\nexport type { FailureClassifier } from './http/FailureClassifier';\nexport { KeyedFailureClassifier } from './http/FailureClassifier';\nexport {\n WebpiecesDefaultFailureClassifier,\n WEBPIECES_DEFAULT_FAILURE_CLASSIFIER,\n} from './http/WebpiecesDefaultFailureClassifier';\nexport { templateDeriver } from './http/templateDeriver';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { ContextReader } from './http/ContextReader';\nexport { ContextWriter, ContextStore } from './http/ContextWriter';\n\n// The OUTBOUND half of the trust model: whether a TRUSTED context key may ride to the endpoint being\n// called. Built ONLY from the destination endpoint's AuthMode — see the class doc.\nexport { DestinationTrust } from './http/DestinationTrust';\n\n// BROWSER-ONLY outbound-header propagation (app-held store + registry -> outbound HTTP headers).\n// Only @webpieces/http-client-browser may name it; the server reads RequestContext directly via\n// RequestContextHeaders in the Node-only @webpieces/core-context.\nexport { ContextMgr } from './http/ContextMgr';\n\n// API-call logging helper (uses LogManager above). NOT a singleton: construct one per environment\n// with that environment's ApiCallContext — `new LogApiCallImpl(new RequestContextApiCallContext())`\n// on node, `new LogApiCallImpl(new BrowserApiCallContext())` in a browser.\nexport { LogApiCallImpl } from './http/LogApiCall';\n\n// Opt-in field masking for the LogApiCall log path — declare per-api sensitive fields so secrets\n// (OAuth refresh tokens, id-token JWTs) are masked in the logs while the real value stays on the wire.\nexport { MaskSpec } from './http/LogFieldMask';\nexport type { MaskMode } from './http/LogFieldMask';\n\n// The structured `api` tag + the context-writer seam LogApiCall stamps through. The Node\n// RequestContext-backed impl lives in @webpieces/core-context, the browser one in\n// @webpieces/http-client-browser; each is CONSTRUCTED by its package, never installed globally.\nexport { ApiCallInfo } from './http/ApiCallInfo';\nexport type { ApiCallDirection, ApiResult } from './http/ApiCallInfo';\n// Console-render bridge: turns LogApiCall's [LogApiCall] bracket into [API.{side}.{phase}] locally.\nexport {\n ApiCallLogName,\n ApiCallLogNameImpl,\n LOG_API_CALL_LOGGER_NAME,\n} from './http/ApiCallLogName';\nexport { ApiMethodInfo } from './http/ApiMethodInfo';\nexport type { ApiSide } from './http/ApiMethodInfo';\nexport type { ApiCallContext } from './http/ApiCallContext';\n\n// Test-case recording contract (impl lives in http-server; hooks in http-client)\nexport { TestCaseRecorder, RecorderKeys } from './http/recorder/TestCaseRecorder';\nexport {\n RecordedEndpoint,\n RecordedError,\n RecordedTestCase,\n} from './http/recorder/RecordedEndpoint';\nexport { DoNotRecord, getDoNotRecordFields } from './http/recorder/DoNotRecord';\nexport { RecordSerializer, SerializedMap, SerializedError } from './http/recorder/RecordSerializer';\n\n// ---------------------------------------------------------------------------------------------\n// Filter-chain primitives, shared by BOTH chains: the inbound server chain\n// (`Filter<MethodMeta, WpResponse<unknown>>`, @webpieces/http-routing) and the outbound client\n// chain (`Filter<ClientRequest, Response>`, @webpieces/http-client-core). Declared once, here, in\n// the package both depend on — see the class doc for why a second pair would be a shim.\nexport { Filter } from './filters/Filter';\nexport type { Service } from './filters/Filter';\nexport { FilterChain } from './filters/FilterChain';\n\nexport {\n ApiError,\n ApiNotFoundError,\n ApiDependencyError,\n ApiBadGatewayError,\n ApiUnavailableError,\n ApiDependencyTimeoutError,\n ApiImplementationError,\n ApiRateLimitedError,\n ApiForbiddenError,\n ApiRequestTimeoutError,\n ApiEndUserError,\n ApiBadRequestError,\n ApiUnauthorizedError,\n ApiDependencyBackoffError,\n ApiConnectionError,\n ApiEndpointNotFoundError,\n ApiConflictError,\n ApiUnprocessableError,\n ApiPreconditionFailedError,\n ApiUnsupportedMediaTypeError,\n ApiNotImplementedError,\n ApiCodedError,\n ApiErrorCodec,\n ApiErrorPayload,\n ApiErrorBoundary,\n} from './errors';\nexport type { ApiErrorKind, ApiStatusCode, EdgeHttpStatus } from './errors';\nexport { ApiErrorHttpStatus } from './http/ApiErrorHttpStatus';\nexport type { PublishedKind, PublishedApiError } from './http/ApiErrorHttpStatus';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @webpieces/core-util\n *\n * Utility functions for WebPieces applications.\n * This package works in both browser and Node.js environments.\n *\n * @packageDocumentation\n */\n\nexport { toError } from './lib/errorUtils';\nexport { ContextKey } from './ContextKey';\nexport type {\n AnyContextKey,\n AnyTrustedContextKey,\n AnyUntrustedContextKey,\n Trust,\n} from './ContextKey';\nexport { ContextTuple } from './ContextTuple';\n\n// @DocumentDesign — DI-design-root marker. Applies to ANY project kind (server\n// controllers AND library impl classes), so it lives here (browser + Node) rather\n// than in a server-only routing package.\nexport { DocumentDesign, isDocumentDesign, DESIGN_METADATA_KEYS } from './DocumentDesign';\n\n// Logging (merged from former @webpieces/wp-logging).\n// Pluggable logging interface + a browser-safe console default; apps plug in\n// bunyan/winston/pino/etc. via LogManager.setFactory(...). Browser + Node.\nexport type { Logger, LogLevel } from './logging/Logger';\nexport type { LoggerFactory } from './logging/LoggerFactory';\nexport { ConsoleLogger } from './logging/ConsoleLogger';\nexport { ConsoleLoggerFactory } from './logging/ConsoleLoggerFactory';\nexport { LogManager } from './logging/LogManager';\nexport {\n LogChunker,\n LogChunkerImpl,\n LogChunkInfo,\n MAX_GCP_LOG_BYTES,\n GCP_LOG_BUDGET_BYTES,\n} from './logging/LogChunker';\n\n// HTTP API contract (merged from former @webpieces/http-api).\n// Shared HTTP API definition consumed by both client and server: REST\n// decorators, the API error taxonomy, datetime DTOs, platform-header\n// registry/readers, ValidateImplementation, and the test-case recorder\n// contract. Pure definitions — express-free, browser + Node safe.\n\n// API definition decorators\nexport {\n ApiPath,\n Endpoint,\n // Auth mode decorators (clean service-to-service + user JWT model)\n WpAuthPublic,\n WpAuth,\n MISSING_AUTH_DECORATOR_FIX,\n WpLocalOnly,\n isLocalOnly,\n MaskLog,\n getApiPath,\n getEndpoints,\n getEndpointHttpMethod,\n getEndpointOptions,\n getEndpointOperation,\n getEndpointKind,\n getEndpointKinds,\n getMaskSpec,\n isFormPost,\n isRawBody,\n isApiPath,\n getAuthMeta,\n assertEveryEndpointHasAuthMode,\n assertEveryExternalEndpointDeclaresCaller,\n assertEveryWebhookEndpointRetainsRawBody,\n validateNoConflictingDecorators,\n METADATA_KEYS,\n} from './http/decorators';\nexport {\n PathParam,\n QueryParam,\n getHttpParameterDeclarations,\n} from './http/http-parameter-decorators';\nexport { WpInt, WpMin, WpMax, NUMERIC_METADATA_KEYS } from './http/numeric-field-decorators';\nexport type { Integer } from './http/numeric-field-decorators';\nexport {\n ApiType,\n SVC_TO_SVC,\n EXTERNAL_CUSTOMER,\n MCP,\n API_TYPE_METADATA_KEY,\n DEFAULT_API_TYPES,\n getApiTypes,\n getApiTypeEndpointNames,\n assertApiTypeMatchesMcpTools,\n} from './http/api-type';\nexport type { ApiTypeKind } from './http/api-type';\nexport {\n WpInternal,\n WpIpcEndpoint,\n isInternalApi,\n getIpcApiId,\n getIpcEndpoints,\n getIpcEndpointKind,\n getIpcMaskSpec,\n assertInternalApi,\n assertNotInternalApi,\n IPC_METADATA_KEYS,\n} from './ipc/IpcDecorators';\nexport type { IpcApiType, IpcEndpointKind, IpcEndpointOptions } from './ipc/IpcDecorators';\n// The runtime representation of ONE route (split out of decorators.ts for file size only).\nexport { RouteMetadata } from './http/RouteMetadata';\nexport { RouteMetadataFactory } from './http/RouteMetadataFactory';\nexport { AuthorizationType, AuthorizationDeclaration, WpAuthorization, getAuthorization } from './http/authorization';\nexport type { AuthorizationRequirement } from './http/authorization';\nexport { StreamingSchemaCatalog, registerStreamingCatalog } from './http/StreamingSchemaCatalog';\nexport {\n StreamErrorControl,\n StreamProtocolError,\n StreamDisconnectedError,\n} from './http/StreamErrorControl';\nexport {\n BoundHttpRequest,\n HttpContractMapper,\n HttpParameterBinding,\n HttpParameterDeclaration,\n} from './http/HttpContract';\nexport {\n StreamCorrelation,\n StreamEnvelope,\n StreamingEndpointMetadata,\n StreamEventValidator,\n StreamTransportError,\n StreamWriter,\n StreamDirection,\n WpStream,\n getStreamingEndpoint,\n registerStreamingSchemas,\n} from './http/StreamingContract';\nexport type { RequestStream, ResponseStream, StreamEnvelopeKind } from './http/StreamingContract';\nexport type {\n EndpointResponseType,\n HttpMethod,\n HttpParameterSource,\n HttpParameterValueType,\n} from './http/HttpContract';\nexport { GET, POST } from './http/HttpContract';\nexport type {\n EndpointKind,\n EndpointOperation,\n EndpointOptions,\n ExternalEndpointOptions,\n} from './http/decorators';\nexport { RPC, CLOUDTASKS, CRON, EXTERNAL, READ, WRITE_IDEMPOTENT, WRITE } from './http/decorators';\nexport {\n InvalidEndpointForMcp,\n InvalidEndpointForMcpMetadata,\n WpMcpTool,\n WpMcpToolHints,\n WpMcpToolMetadata,\n getInvalidEndpointsForMcp,\n getWpMcpTools,\n mcpHintsForOperation,\n} from './mcp/McpMetadata';\nexport type { WpMcpMethodDecorator } from './mcp/McpMetadata';\nexport {\n ApiJsonSchema,\n ApiJsonSchemaDiscriminator,\n ApiJsonSchemaValidator,\n DtoValidationFailure,\n ObjectSchemaBuilder,\n} from './mcp/DtoSchema';\nexport type { ApiJsonSchemaType, DtoValue } from './mcp/DtoSchema';\nexport {\n McpToolCatalogFile,\n McpToolCatalogError,\n McpToolDefinition,\n} from './mcp/McpToolCatalogFile';\nexport {\n GeneratedApiDocsLayout,\n GenerateLayoutProblem,\n GenerateOutputLookup,\n GenerateOutputTarget,\n} from './mcp/GeneratedApiDocsLayout';\nexport type {\n LayoutDependsOn,\n LayoutDependsOnObject,\n LayoutTarget,\n LayoutTargets,\n} from './mcp/GeneratedApiDocsLayout';\n// The TYPE layer of the auth surface — likewise split out of decorators.ts for file size only.\nexport { AuthMeta, AuthDeclaration, jwt, oidc, sharedSecret, webhook, apiKey } from './http/auth-mode';\nexport type {\n AuthMode,\n ApiKeyCredential,\n ApiKeyCredentials,\n AuthMethod,\n AuthMethods,\n} from './http/auth-mode';\n// API kind (RPC vs PubSub/Cloud Tasks) + queue naming. Split out of decorators.ts for file size only;\n// one-way dependency api-kind -> decorators, and the barrel keeps the surface identical.\nexport {\n Rpc,\n PubSub,\n Queue,\n ENDPOINT_KINDS_BY_API_KIND,\n getApiKind,\n assertApiKind,\n assertPubSubConventions,\n getQueueName,\n} from './http/api-kind';\nexport type { ApiKind } from './http/api-kind';\n// WHO calls an `external` endpoint — the caller declaration\n// @Endpoint(POST, ..., WRITE, EXTERNAL, { calledBy })\n// requires, and the reader for it.\nexport {\n EXTERNAL_SYSTEM_KINDS,\n DEFAULT_CALLER_KIND,\n ExternalCaller,\n isExternalSystemKind,\n getEndpointCaller,\n} from './http/external-caller';\nexport type { ExternalSystemKind } from './http/external-caller';\n// Client-side shared-secret store (the value THIS service sends per sharedSecret(...) key).\nexport { Secrets, SECRETS } from './http/Secrets';\n\n// Type validators\nexport { ValidateImplementation } from './http/validators';\n\n// Authentication subtype constants.\nexport {\n ENTITY_NOT_FOUND,\n WRONG_LOGIN_TYPE,\n WRONG_LOGIN,\n NOT_APPROVED,\n EMAIL_NOT_CONFIRMED,\n WRONG_DOMAIN,\n WRONG_COMPANY,\n NO_REG_CODE,\n} from './errors';\n\nexport { NetworkRejectClassifier } from './http/networkReject';\n\n// Date/Time DTOs and Utilities (inspired by Java Time / JSR-310)\nexport {\n InstantDto,\n DateDto,\n TimeDto,\n DateTimeDto,\n InstantUtil,\n DateUtil,\n TimeUtil,\n DateTimeUtil,\n} from './http/datetime';\n\n// Context keys + registry (the global magic-context header system)\nexport { HeaderRegistry } from './http/HeaderRegistry';\nexport { ClientRegistry } from './http/ClientRegistry';\nexport { CallRegistry } from './http/CallRegistry';\nexport { CallDeadline } from './http/CallDeadline';\nexport { CallContext } from './http/CallStrategy';\nexport type { Attempt, CallStrategy } from './http/CallStrategy';\nexport { ApiCallTimeoutError } from './http/ApiCallTimeoutError';\nexport type { ServiceUrlDeriver } from './http/ClientRegistry';\n\n// \"What service am I\" — set once at startup, read by the logging backends and by\n// RequestContextHeaders (to stamp requestIdSource on ids this service mints).\nexport { ServiceInfo } from './http/ServiceInfo';\n// \"Where am I running\" — declared once at startup (setupRuntime, from RuntimeSetupOptions.locality).\n// The ONE input to @WpLocalOnly enforcement. Undeclared reads as DEPLOYED (fail safe).\nexport { RuntimeLocality } from './http/RuntimeLocality';\nexport type { Locality } from './http/RuntimeLocality';\n// The ENTIRE HTTP response as pure data — the ONE form both transports (express, fetch) are\n// normalised into, so an ErrorTranslator implementation is written once and serves both.\nexport { HttpHeader, HttpResponseStatus, HttpResponseDto } from './http/HttpResponseDto';\n// Pluggable, bidirectional error translation (app exception <-> the WHOLE response). Set on\n// ClientRegistry at startup; consulted before the built-in webpieces mapping on BOTH sides.\nexport type { ErrorTranslator } from './http/ErrorTranslator';\n// THE webpieces default translator — always installed, and what an app's own translator delegates to\n// in order to decline an error. ONE class, both halves, node and browser alike.\nexport {\n WebpiecesDefaultErrorTranslator,\n WEBPIECES_DEFAULT_ERROR_TRANSLATOR,\n} from './http/WebpiecesDefaultErrorTranslator';\n// WHICH KIND OF CALLER drove this request (gui / llm / public-api), and the per-request end-user\n// status derived from it — the replacement for the deleted per-router EndUserStatus.\nexport { SurfaceEndUserStatus } from './http/Surface';\nexport type { Surface } from './http/Surface';\n// The ONE rule for an error RECEIVED from another process, shared verbatim by HTTP and IPC.\nexport { ReceivedApiErrorRule } from './errors/ReceivedApiErrorRule';\n// Pluggable per-client failure classification (is a thrown API-call error a real failure or an\n// expected non-failure?). Registered on ClientRegistry at startup; consulted by LogApiCall.\nexport type { FailureClassifier } from './http/FailureClassifier';\nexport { KeyedFailureClassifier } from './http/FailureClassifier';\nexport {\n WebpiecesDefaultFailureClassifier,\n WEBPIECES_DEFAULT_FAILURE_CLASSIFIER,\n} from './http/WebpiecesDefaultFailureClassifier';\nexport { templateDeriver } from './http/templateDeriver';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { ContextReader } from './http/ContextReader';\nexport { ContextWriter, ContextStore } from './http/ContextWriter';\n\n// The OUTBOUND half of the trust model: whether a TRUSTED context key may ride to the endpoint being\n// called. Built ONLY from the destination endpoint's AuthMode — see the class doc.\nexport { DestinationTrust } from './http/DestinationTrust';\n\n// BROWSER-ONLY outbound-header propagation (app-held store + registry -> outbound HTTP headers).\n// Only @webpieces/http-client-browser may name it; the server reads RequestContext directly via\n// RequestContextHeaders in the Node-only @webpieces/core-context.\nexport { ContextMgr } from './http/ContextMgr';\n\n// API-call logging helper (uses LogManager above). NOT a singleton: construct one per environment\n// with that environment's ApiCallContext — `new LogApiCallImpl(new RequestContextApiCallContext())`\n// on node, `new LogApiCallImpl(new BrowserApiCallContext())` in a browser.\nexport { LogApiCallImpl } from './http/LogApiCall';\n\n// Opt-in field masking for the LogApiCall log path — declare per-api sensitive fields so secrets\n// (OAuth refresh tokens, id-token JWTs) are masked in the logs while the real value stays on the wire.\nexport { MaskSpec } from './http/LogFieldMask';\nexport type { MaskMode } from './http/LogFieldMask';\n\n// The structured `api` tag + the context-writer seam LogApiCall stamps through. The Node\n// RequestContext-backed impl lives in @webpieces/core-context, the browser one in\n// @webpieces/http-client-browser; each is CONSTRUCTED by its package, never installed globally.\nexport { ApiCallInfo } from './http/ApiCallInfo';\nexport type { ApiCallDirection, ApiResult } from './http/ApiCallInfo';\n// Console-render bridge: turns LogApiCall's [LogApiCall] bracket into [API.{side}.{phase}] locally.\nexport {\n ApiCallLogName,\n ApiCallLogNameImpl,\n LOG_API_CALL_LOGGER_NAME,\n} from './http/ApiCallLogName';\nexport { ApiMethodInfo } from './http/ApiMethodInfo';\nexport type { ApiSide } from './http/ApiMethodInfo';\nexport type { ApiCallContext } from './http/ApiCallContext';\n\n// Test-case recording contract (impl lives in http-server; hooks in http-client)\nexport { TestCaseRecorder, RecorderKeys } from './http/recorder/TestCaseRecorder';\nexport {\n RecordedEndpoint,\n RecordedError,\n RecordedTestCase,\n} from './http/recorder/RecordedEndpoint';\nexport { DoNotRecord, getDoNotRecordFields } from './http/recorder/DoNotRecord';\nexport { RecordSerializer, SerializedMap, SerializedError } from './http/recorder/RecordSerializer';\n\n// ---------------------------------------------------------------------------------------------\n// Filter-chain primitives, shared by BOTH chains: the inbound server chain\n// (`Filter<MethodMeta, WpResponse<unknown>>`, @webpieces/http-routing) and the outbound client\n// chain (`Filter<ClientRequest, Response>`, @webpieces/http-client-core). Declared once, here, in\n// the package both depend on — see the class doc for why a second pair would be a shim.\nexport { Filter } from './filters/Filter';\nexport type { Service } from './filters/Filter';\nexport { FilterChain } from './filters/FilterChain';\n\nexport {\n ApiError,\n ApiNotFoundError,\n ApiDependencyError,\n ApiBadGatewayError,\n ApiUnavailableError,\n ApiDependencyTimeoutError,\n ApiImplementationError,\n ApiRateLimitedError,\n ApiForbiddenError,\n ApiRequestTimeoutError,\n ApiEndUserError,\n ApiBadRequestError,\n ApiUnauthorizedError,\n ApiDependencyBackoffError,\n ApiConnectionError,\n ApiEndpointNotFoundError,\n ApiConflictError,\n ApiUnprocessableError,\n ApiPreconditionFailedError,\n ApiUnsupportedMediaTypeError,\n ApiNotImplementedError,\n ApiCodedError,\n ApiClientTooOldError,\n ApiEndUserNotFoundError,\n ApiEndUserForbiddenError,\n ApiEndUserBadRequestError,\n EndUserErrorCode,\n EndUserErrorRegistry,\n ClientRole,\n ApiErrorCodec,\n ApiErrorPayload,\n ApiErrorBoundary,\n} from './errors';\nexport type {\n ApiErrorKind,\n ApiStatusCode,\n EdgeHttpStatus,\n EndUserErrorFactory,\n} from './errors';\nexport { ApiErrorHttpStatus } from './http/ApiErrorHttpStatus';\nexport type { PublishedKind, PublishedApiError } from './http/ApiErrorHttpStatus';\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ApiErrorPayload } from '../errors/ApiErrorCodec';
|
|
2
|
+
import { ClientRole } from '../errors/ClientRole';
|
|
2
3
|
import { IpcReply } from './IpcProtocol';
|
|
3
4
|
/**
|
|
4
5
|
* IpcErrorTranslator - ONE symmetric place an app owns IPC error translation, in BOTH directions.
|
|
@@ -33,7 +34,10 @@ export interface IpcErrorTranslator {
|
|
|
33
34
|
* HTTP side, so an app can turn a "successful" reply into a throw. Returning normally means "let
|
|
34
35
|
* this reply through"; the webpieces default returns for an `IpcSuccess` and throws for an
|
|
35
36
|
* `IpcFailure`. Decline by calling
|
|
36
|
-
* `new WebpiecesDefaultIpcErrorTranslator().fromWire(reply)`.
|
|
37
|
+
* `new WebpiecesDefaultIpcErrorTranslator().fromWire(reply, role)`.
|
|
38
|
+
*
|
|
39
|
+
* `role` is WHO is receiving ({@link ClientRole}), declared once at `IpcClientFactory` setup with
|
|
40
|
+
* no default. It decides what a received `unauthorized` means; pass it through when declining.
|
|
37
41
|
*/
|
|
38
|
-
fromWire(reply: IpcReply): void;
|
|
42
|
+
fromWire(reply: IpcReply, role: ClientRole): void;
|
|
39
43
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IpcErrorTranslator.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/ipc/IpcErrorTranslator.ts"],"names":[],"mappings":"","sourcesContent":["import { ApiErrorPayload } from '../errors/ApiErrorCodec';\nimport { IpcReply } from './IpcProtocol';\n\n/**\n * IpcErrorTranslator - ONE symmetric place an app owns IPC error translation, in BOTH directions.\n *\n * The exact twin of `ErrorTranslator` for HTTP, down to the shape of each half: `toWire` is called in\n * a catch and RETURNS the payload the reply carries, `fromWire` is its mirror and THROWS. It exists\n * for the same reason: IPC already HAD both halves, hand-rolled at three call sites\n * (`IpcServerFactory.handle`, `IpcConnection.dispatch`, `IpcClientFactory`), with no seam an app could\n * reach. An app whose two processes share an error taxonomy had nowhere to put it.\n *\n * Register one with {@link IpcRegistry.setErrorTranslator} — one registry per PROTOCOL, mirroring\n * `ClientRegistry` for HTTP and `McpRegistry` for MCP, because the three speak different wire shapes\n * and an app rarely wants the same answer on all three.\n *\n * There is ALWAYS a translator ({@link IpcRegistry.getErrorTranslator} is non-optional), so every call\n * site is one unconditional line and a registered translator declines by DELEGATING to\n * {@link WebpiecesDefaultIpcErrorTranslator}.\n *\n * This is a business-logic contract (methods, not data), so it is an interface per the webpieces\n * guidelines.\n */\nexport interface IpcErrorTranslator {\n /**\n * SERVER: exception -> the wire payload carried by `IpcFailure`. Always a payload; decline by\n * returning `new WebpiecesDefaultIpcErrorTranslator().toWire(error)`, which applies the shared\n * `ApiErrorBoundary` publication rule (notably: a caller-local `ApiConnectionError` publishes as\n * `implementation`, because to the PEER this process is what is broken).\n */\n toWire(error: Error): ApiErrorPayload;\n\n /**\n * CLIENT: the reply -> a THROW. Called for EVERY reply, success or failure, exactly as on the\n * HTTP side, so an app can turn a \"successful\" reply into a throw. Returning normally means \"let\n * this reply through\"; the webpieces default returns for an `IpcSuccess` and throws for an\n * `IpcFailure`. Decline by calling\n * `new WebpiecesDefaultIpcErrorTranslator().fromWire(reply)`.\n */\n fromWire(reply: IpcReply): void;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"IpcErrorTranslator.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/ipc/IpcErrorTranslator.ts"],"names":[],"mappings":"","sourcesContent":["import { ApiErrorPayload } from '../errors/ApiErrorCodec';\nimport { ClientRole } from '../errors/ClientRole';\nimport { IpcReply } from './IpcProtocol';\n\n/**\n * IpcErrorTranslator - ONE symmetric place an app owns IPC error translation, in BOTH directions.\n *\n * The exact twin of `ErrorTranslator` for HTTP, down to the shape of each half: `toWire` is called in\n * a catch and RETURNS the payload the reply carries, `fromWire` is its mirror and THROWS. It exists\n * for the same reason: IPC already HAD both halves, hand-rolled at three call sites\n * (`IpcServerFactory.handle`, `IpcConnection.dispatch`, `IpcClientFactory`), with no seam an app could\n * reach. An app whose two processes share an error taxonomy had nowhere to put it.\n *\n * Register one with {@link IpcRegistry.setErrorTranslator} — one registry per PROTOCOL, mirroring\n * `ClientRegistry` for HTTP and `McpRegistry` for MCP, because the three speak different wire shapes\n * and an app rarely wants the same answer on all three.\n *\n * There is ALWAYS a translator ({@link IpcRegistry.getErrorTranslator} is non-optional), so every call\n * site is one unconditional line and a registered translator declines by DELEGATING to\n * {@link WebpiecesDefaultIpcErrorTranslator}.\n *\n * This is a business-logic contract (methods, not data), so it is an interface per the webpieces\n * guidelines.\n */\nexport interface IpcErrorTranslator {\n /**\n * SERVER: exception -> the wire payload carried by `IpcFailure`. Always a payload; decline by\n * returning `new WebpiecesDefaultIpcErrorTranslator().toWire(error)`, which applies the shared\n * `ApiErrorBoundary` publication rule (notably: a caller-local `ApiConnectionError` publishes as\n * `implementation`, because to the PEER this process is what is broken).\n */\n toWire(error: Error): ApiErrorPayload;\n\n /**\n * CLIENT: the reply -> a THROW. Called for EVERY reply, success or failure, exactly as on the\n * HTTP side, so an app can turn a \"successful\" reply into a throw. Returning normally means \"let\n * this reply through\"; the webpieces default returns for an `IpcSuccess` and throws for an\n * `IpcFailure`. Decline by calling\n * `new WebpiecesDefaultIpcErrorTranslator().fromWire(reply, role)`.\n *\n * `role` is WHO is receiving ({@link ClientRole}), declared once at `IpcClientFactory` setup with\n * no default. It decides what a received `unauthorized` means; pass it through when declining.\n */\n fromWire(reply: IpcReply, role: ClientRole): void;\n}\n"]}
|
package/src/ipc/IpcRegistry.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ClientRole } from '../errors/ClientRole';
|
|
1
2
|
import { IpcErrorTranslator } from './IpcErrorTranslator';
|
|
2
3
|
import { IpcReply, IpcSuccess } from './IpcProtocol';
|
|
3
4
|
/**
|
|
@@ -50,6 +51,8 @@ export declare class IpcClientErrorTranslator {
|
|
|
50
51
|
* normally for a failure reply has a bug whose symptom would otherwise be `undefined` arriving
|
|
51
52
|
* where the contract promised a DTO. For a genuine success the default returns immediately, so
|
|
52
53
|
* the normal path costs one comparison.
|
|
54
|
+
*
|
|
55
|
+
* @param role - who is receiving, from the `IpcClientFactory` that sent the request.
|
|
53
56
|
*/
|
|
54
|
-
static throwIfFailure(reply: IpcReply): asserts reply is IpcSuccess;
|
|
57
|
+
static throwIfFailure(reply: IpcReply, role: ClientRole): asserts reply is IpcSuccess;
|
|
55
58
|
}
|
package/src/ipc/IpcRegistry.js
CHANGED
|
@@ -62,11 +62,13 @@ class IpcClientErrorTranslator {
|
|
|
62
62
|
* normally for a failure reply has a bug whose symptom would otherwise be `undefined` arriving
|
|
63
63
|
* where the contract promised a DTO. For a genuine success the default returns immediately, so
|
|
64
64
|
* the normal path costs one comparison.
|
|
65
|
+
*
|
|
66
|
+
* @param role - who is receiving, from the `IpcClientFactory` that sent the request.
|
|
65
67
|
*/
|
|
66
68
|
// webpieces-disable no-function-outside-class -- stateless boundary shared by every IPC client proxy
|
|
67
|
-
static throwIfFailure(reply) {
|
|
68
|
-
IpcRegistry.getErrorTranslator().fromWire(reply);
|
|
69
|
-
WebpiecesDefaultIpcErrorTranslator_1.WEBPIECES_DEFAULT_IPC_ERROR_TRANSLATOR.fromWire(reply);
|
|
69
|
+
static throwIfFailure(reply, role) {
|
|
70
|
+
IpcRegistry.getErrorTranslator().fromWire(reply, role);
|
|
71
|
+
WebpiecesDefaultIpcErrorTranslator_1.WEBPIECES_DEFAULT_IPC_ERROR_TRANSLATOR.fromWire(reply, role);
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
74
|
exports.IpcClientErrorTranslator = IpcClientErrorTranslator;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IpcRegistry.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/ipc/IpcRegistry.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"IpcRegistry.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/ipc/IpcRegistry.ts"],"names":[],"mappings":";;;AAGA,6FAA8F;AAE9F;;;;;;;;;;;;;;;;GAgBG;AACH,MAAa,WAAW;IACpB;;;;OAIG;IACK,MAAM,CAAC,eAAe,GAAuB,2EAAsC,CAAC;IAE5F;;;OAGG;IACH,4JAA4J;IAC5J,MAAM,CAAC,kBAAkB,CAAC,UAA8B;QACpD,WAAW,CAAC,eAAe,GAAG,UAAU,CAAC;IAC7C,CAAC;IAED,kGAAkG;IAClG,4JAA4J;IAC5J,MAAM,CAAC,kBAAkB;QACrB,OAAO,WAAW,CAAC,eAAe,CAAC;IACvC,CAAC;IAED,6FAA6F;IAC7F,4JAA4J;IAC5J,MAAM,CAAC,aAAa;QAChB,WAAW,CAAC,eAAe,GAAG,2EAAsC,CAAC;IACzE,CAAC;;AA3BL,kCA4BC;AAED;;;;;;;GAOG;AACH,MAAa,wBAAwB;IACjC;;;;;;;;;OASG;IACH,qGAAqG;IACrG,MAAM,CAAC,cAAc,CAAC,KAAe,EAAE,IAAgB;QACnD,WAAW,CAAC,kBAAkB,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACvD,2EAAsC,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACjE,CAAC;CACJ;AAhBD,4DAgBC","sourcesContent":["import { ClientRole } from '../errors/ClientRole';\nimport { IpcErrorTranslator } from './IpcErrorTranslator';\nimport { IpcReply, IpcSuccess } from './IpcProtocol';\nimport { WEBPIECES_DEFAULT_IPC_ERROR_TRANSLATOR } from './WebpiecesDefaultIpcErrorTranslator';\n\n/**\n * IpcRegistry - the process-global home of the ONE {@link IpcErrorTranslator}, mirroring\n * `ClientRegistry` for HTTP and `McpRegistry` for MCP.\n *\n * ONE REGISTRY PER PROTOCOL, and that is the design: the three speak different wire shapes\n * (`HttpResponseDto`, `CallToolResult`, `ApiErrorPayload`), so one combined registry would either\n * force an app to answer three unrelated questions in one object or hide two of them behind\n * optionality. Three registries, three unconditional call sites.\n *\n * Configured like `HeaderRegistry` / `ClientRegistry` / LogManager — populated once at startup, then\n * globally accessible with NO DI wiring.\n *\n * ```ts\n * // startup, ONCE per process, in BOTH processes that share the connection\n * IpcRegistry.setErrorTranslator(new MyIpcErrorTranslator());\n * ```\n */\nexport class IpcRegistry {\n /**\n * THE translator. NEVER undefined — it starts life holding\n * {@link WEBPIECES_DEFAULT_IPC_ERROR_TRANSLATOR}, and {@link IpcRegistry.setErrorTranslator}\n * REPLACES it. So no IPC call site asks \"did anyone register one\".\n */\n private static errorTranslator: IpcErrorTranslator = WEBPIECES_DEFAULT_IPC_ERROR_TRANSLATOR;\n\n /**\n * Install the app's {@link IpcErrorTranslator}. `set`, not `add`: an app with several layers of\n * error policy composes them INSIDE its own `toWire`, where the precedence is written down.\n */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected\n static setErrorTranslator(translator: IpcErrorTranslator): void {\n IpcRegistry.errorTranslator = translator;\n }\n\n /** THE {@link IpcErrorTranslator} for this process — never `undefined`, so no caller branches. */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected\n static getErrorTranslator(): IpcErrorTranslator {\n return IpcRegistry.errorTranslator;\n }\n\n /** Put the process-global back to the webpieces default. For TESTS, so specs cannot leak. */\n // webpieces-disable no-function-outside-class -- static global singleton (like HeaderRegistry/ClientRegistry); populated once at startup, never DI-injected\n static resetForTests(): void {\n IpcRegistry.errorTranslator = WEBPIECES_DEFAULT_IPC_ERROR_TRANSLATOR;\n }\n}\n\n/**\n * The CLIENT half's one call site, and the guarantee behind it — the IPC twin of\n * `ClientErrorTranslator.throwIfFailure`.\n *\n * The `asserts` return type is what lets `IpcClientFactory` read the success body straight afterwards\n * with no `if (reply.type === 'failure')` branch left over: after this call the compiler knows the\n * reply is an {@link IpcSuccess}, because the only way past it is that it is one.\n */\nexport class IpcClientErrorTranslator {\n /**\n * Run `fromWire` over a reply, and make it impossible for an `IpcFailure` to be read as data.\n *\n * The second call is BUG CONTAINMENT, not a fallback branch: an app translator that returns\n * normally for a failure reply has a bug whose symptom would otherwise be `undefined` arriving\n * where the contract promised a DTO. For a genuine success the default returns immediately, so\n * the normal path costs one comparison.\n *\n * @param role - who is receiving, from the `IpcClientFactory` that sent the request.\n */\n // webpieces-disable no-function-outside-class -- stateless boundary shared by every IPC client proxy\n static throwIfFailure(reply: IpcReply, role: ClientRole): asserts reply is IpcSuccess {\n IpcRegistry.getErrorTranslator().fromWire(reply, role);\n WEBPIECES_DEFAULT_IPC_ERROR_TRANSLATOR.fromWire(reply, role);\n }\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ApiErrorPayload } from '../errors/ApiErrorCodec';
|
|
2
|
+
import { ClientRole } from '../errors/ClientRole';
|
|
2
3
|
import { IpcErrorTranslator } from './IpcErrorTranslator';
|
|
3
4
|
import { IpcReply } from './IpcProtocol';
|
|
4
5
|
/**
|
|
@@ -14,7 +15,9 @@ import { IpcReply } from './IpcProtocol';
|
|
|
14
15
|
* caller-error kind coming back means I sent a bad IPC request (MY bug ->
|
|
15
16
|
* `ApiImplementationError`), a server-side kind means the peer broke (NOT my bug ->
|
|
16
17
|
* `ApiDependencyError`), an incoming `ApiDependencyError` is already attributed and rethrows as-is,
|
|
17
|
-
*
|
|
18
|
+
* an `ApiEndUserError` is the actor's own answer and passes through (as its registered subclass), an
|
|
19
|
+
* `ApiClientTooOldError` passes through on every hop, and an `unauthorized` reply depends on the
|
|
20
|
+
* receiving {@link ClientRole}.
|
|
18
21
|
*
|
|
19
22
|
* The two protocols share ONE rule object rather than two copies for the obvious reason: an app that
|
|
20
23
|
* moves a call from IPC to HTTP or back must not thereby change which team gets paged.
|
|
@@ -22,7 +25,8 @@ import { IpcReply } from './IpcProtocol';
|
|
|
22
25
|
export declare class WebpiecesDefaultIpcErrorTranslator implements IpcErrorTranslator {
|
|
23
26
|
private readonly boundary;
|
|
24
27
|
toWire(error: Error): ApiErrorPayload;
|
|
25
|
-
|
|
28
|
+
/** @param role - who is receiving, declared at `IpcClientFactory` setup. Decides what a 401 means. */
|
|
29
|
+
fromWire(reply: IpcReply, role: ClientRole): void;
|
|
26
30
|
}
|
|
27
31
|
/** Process-wide built-in instance — stateless, so one shared instance is enough. */
|
|
28
32
|
export declare const WEBPIECES_DEFAULT_IPC_ERROR_TRANSLATOR: WebpiecesDefaultIpcErrorTranslator;
|