@webpieces/core-util 0.4.879 → 0.4.881

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.
Files changed (78) hide show
  1. package/package.json +1 -1
  2. package/src/errors/ApiCallSite.d.ts +58 -0
  3. package/src/errors/ApiCallSite.js +96 -0
  4. package/src/errors/ApiCallSite.js.map +1 -0
  5. package/src/errors/ApiError.d.ts +47 -0
  6. package/src/errors/ApiError.js +46 -1
  7. package/src/errors/ApiError.js.map +1 -1
  8. package/src/errors/ApiErrorCodec.js +9 -2
  9. package/src/errors/ApiErrorCodec.js.map +1 -1
  10. package/src/errors/ClientRole.d.ts +27 -0
  11. package/src/errors/ClientRole.js +32 -0
  12. package/src/errors/ClientRole.js.map +1 -0
  13. package/src/errors/EndUserErrorCode.d.ts +22 -0
  14. package/src/errors/EndUserErrorCode.js +38 -0
  15. package/src/errors/EndUserErrorCode.js.map +1 -0
  16. package/src/errors/EndUserErrorRegistry.d.ts +61 -0
  17. package/src/errors/EndUserErrorRegistry.js +103 -0
  18. package/src/errors/EndUserErrorRegistry.js.map +1 -0
  19. package/src/errors/EndUserErrors.d.ts +28 -0
  20. package/src/errors/EndUserErrors.js +59 -0
  21. package/src/errors/EndUserErrors.js.map +1 -0
  22. package/src/errors/ReceivedApiErrorRule.d.ts +33 -3
  23. package/src/errors/ReceivedApiErrorRule.js +47 -8
  24. package/src/errors/ReceivedApiErrorRule.js.map +1 -1
  25. package/src/errors/ReportableApiFailure.d.ts +43 -0
  26. package/src/errors/ReportableApiFailure.js +75 -0
  27. package/src/errors/ReportableApiFailure.js.map +1 -0
  28. package/src/errors/index.d.ts +9 -1
  29. package/src/errors/index.js +17 -1
  30. package/src/errors/index.js.map +1 -1
  31. package/src/http/ContextMgr.d.ts +1 -1
  32. package/src/http/ContextMgr.js +1 -1
  33. package/src/http/ContextMgr.js.map +1 -1
  34. package/src/http/ErrorTranslator.d.ts +10 -4
  35. package/src/http/ErrorTranslator.js.map +1 -1
  36. package/src/http/HttpEndpointOptions.d.ts +8 -0
  37. package/src/http/HttpEndpointOptions.js.map +1 -1
  38. package/src/http/StreamErrorControl.d.ts +3 -1
  39. package/src/http/StreamErrorControl.js +4 -3
  40. package/src/http/StreamErrorControl.js.map +1 -1
  41. package/src/http/WebpiecesDefaultErrorTranslator.d.ts +13 -2
  42. package/src/http/WebpiecesDefaultErrorTranslator.js +21 -3
  43. package/src/http/WebpiecesDefaultErrorTranslator.js.map +1 -1
  44. package/src/http/WebpiecesDefaultFailureClassifier.d.ts +3 -2
  45. package/src/http/WebpiecesDefaultFailureClassifier.js +6 -3
  46. package/src/http/WebpiecesDefaultFailureClassifier.js.map +1 -1
  47. package/src/index.d.ts +8 -2
  48. package/src/index.js +27 -1
  49. package/src/index.js.map +1 -1
  50. package/src/ipc/IpcErrorTranslator.d.ts +6 -2
  51. package/src/ipc/IpcErrorTranslator.js.map +1 -1
  52. package/src/ipc/IpcRegistry.d.ts +4 -1
  53. package/src/ipc/IpcRegistry.js +5 -3
  54. package/src/ipc/IpcRegistry.js.map +1 -1
  55. package/src/ipc/WebpiecesDefaultIpcErrorTranslator.d.ts +6 -2
  56. package/src/ipc/WebpiecesDefaultIpcErrorTranslator.js +6 -3
  57. package/src/ipc/WebpiecesDefaultIpcErrorTranslator.js.map +1 -1
  58. package/src/ipc/index.d.ts +1 -0
  59. package/src/ipc/index.js +3 -1
  60. package/src/ipc/index.js.map +1 -1
  61. package/src/oauth/OAuthAuthorizationErrorRedirect.d.ts +37 -0
  62. package/src/oauth/OAuthAuthorizationErrorRedirect.js +61 -0
  63. package/src/oauth/OAuthAuthorizationErrorRedirect.js.map +1 -0
  64. package/src/oauth/OAuthBearerChallenge.d.ts +31 -0
  65. package/src/oauth/OAuthBearerChallenge.js +52 -0
  66. package/src/oauth/OAuthBearerChallenge.js.map +1 -0
  67. package/src/oauth/OAuthErrorCode.d.ts +69 -0
  68. package/src/oauth/OAuthErrorCode.js +89 -0
  69. package/src/oauth/OAuthErrorCode.js.map +1 -0
  70. package/src/oauth/OAuthErrorResponse.d.ts +34 -0
  71. package/src/oauth/OAuthErrorResponse.js +74 -0
  72. package/src/oauth/OAuthErrorResponse.js.map +1 -0
  73. package/src/oauth/OAuthProtocolError.d.ts +61 -0
  74. package/src/oauth/OAuthProtocolError.js +83 -0
  75. package/src/oauth/OAuthProtocolError.js.map +1 -0
  76. package/src/oauth/OAuthProtocolErrorCompileAssertions.d.ts +10 -0
  77. package/src/oauth/OAuthProtocolErrorCompileAssertions.js +39 -0
  78. package/src/oauth/OAuthProtocolErrorCompileAssertions.js.map +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/core-util",
3
- "version": "0.4.879",
3
+ "version": "0.4.881",
4
4
  "description": "Utility functions for WebPieces - works in browser and Node.js",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Any function value used as the TOP of a captured stack: every frame from it upward is dropped.
3
+ * Its parameters are irrelevant, so `never[]` lets every generated client method be passed as-is.
4
+ */
5
+ export type CallSiteBoundary = (...args: never[]) => unknown;
6
+ /**
7
+ * WHERE the app called a generated api client method — captured synchronously on the CALLER's
8
+ * stack, before the request was sent, and parked on the failure the call rejected with (#1175).
9
+ *
10
+ * WHY IT EXISTS: an api failure is CONSTRUCTED when the response arrives (`ClientErrorTranslator`,
11
+ * `ReceivedApiErrorRule`), long after the stack that made the call has unwound. Its own stack is
12
+ * therefore all framework frames — zone.js, `ProxyClient`, the translator — and not one frame of app
13
+ * code. Measured in a consuming app's error reporter, that meant (1) no way to see which line of app
14
+ * code made the failing call, and (2) every rpc failure in the app grouped into ONE issue, because
15
+ * every one ends in the same framework frames. One `new Error()` per call is cheap; the stack string
16
+ * is only formatted if somebody reads it.
17
+ *
18
+ * WHERE IT LIVES: the property `callSite` on the rejected error, NON-ENUMERABLE so it never reaches
19
+ * a `JSON.stringify`, a structured log record or an error reporter's "extra data" by accident. It is
20
+ * typed on {@link ApiError} (`err.callSite`); {@link ApiCallSite.of} reads it off any thrown value,
21
+ * because an app's own `ErrorTranslator` may reject with a type that is not an `ApiError`.
22
+ *
23
+ * Deliberately NOT `cause`: error reporters follow `cause` as "this error was caused by that one",
24
+ * which would claim the server's 500 was caused by the click handler and bury the useful frames.
25
+ * Use {@link ReportableApiFailure.toReportableError} to build the report-time wrapper instead.
26
+ *
27
+ * It is an `Error` subclass only so that engines and reporters treat its `stack` as a real stack.
28
+ * It is never thrown.
29
+ */
30
+ export declare class ApiCallSite extends Error {
31
+ readonly apiName: string;
32
+ readonly methodName: string;
33
+ /** The property name the call site is parked under on a rejected error. */
34
+ static readonly PROPERTY = "callSite";
35
+ private constructor();
36
+ /** `Api.method` — the endpoint identity, for messages and grouping. */
37
+ get label(): string;
38
+ /**
39
+ * Capture the caller's stack NOW. Call it synchronously as the first statement of the generated
40
+ * client method, passing that method as `boundary`, so the top frame is the app's own line.
41
+ *
42
+ * Where the engine has no `Error.captureStackTrace` the stack keeps the client method's own frame
43
+ * on top of the caller's — one extra frame, never a missing one.
44
+ */
45
+ static capture(apiName: string, methodName: string, boundary: CallSiteBoundary): ApiCallSite;
46
+ /**
47
+ * Park this call site on the value a call rejected with, WITHOUT changing that value: the same
48
+ * instance is rethrown, so every `instanceof` check downstream keeps working.
49
+ *
50
+ * Never clobbers: a failure that already carries a call site (an inner client call whose error
51
+ * propagated out through an outer one) keeps the innermost, which is the line that actually made
52
+ * the failing request. A non-`Error` or a frozen/sealed error is left untouched — attaching is a
53
+ * diagnostic nicety and must never itself throw.
54
+ */
55
+ attachTo(failure: unknown): void;
56
+ /** The call site parked on `failure` by a generated client, or undefined when there is none. */
57
+ static of(failure: unknown): ApiCallSite | undefined;
58
+ }
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ApiCallSite = void 0;
4
+ /**
5
+ * WHERE the app called a generated api client method — captured synchronously on the CALLER's
6
+ * stack, before the request was sent, and parked on the failure the call rejected with (#1175).
7
+ *
8
+ * WHY IT EXISTS: an api failure is CONSTRUCTED when the response arrives (`ClientErrorTranslator`,
9
+ * `ReceivedApiErrorRule`), long after the stack that made the call has unwound. Its own stack is
10
+ * therefore all framework frames — zone.js, `ProxyClient`, the translator — and not one frame of app
11
+ * code. Measured in a consuming app's error reporter, that meant (1) no way to see which line of app
12
+ * code made the failing call, and (2) every rpc failure in the app grouped into ONE issue, because
13
+ * every one ends in the same framework frames. One `new Error()` per call is cheap; the stack string
14
+ * is only formatted if somebody reads it.
15
+ *
16
+ * WHERE IT LIVES: the property `callSite` on the rejected error, NON-ENUMERABLE so it never reaches
17
+ * a `JSON.stringify`, a structured log record or an error reporter's "extra data" by accident. It is
18
+ * typed on {@link ApiError} (`err.callSite`); {@link ApiCallSite.of} reads it off any thrown value,
19
+ * because an app's own `ErrorTranslator` may reject with a type that is not an `ApiError`.
20
+ *
21
+ * Deliberately NOT `cause`: error reporters follow `cause` as "this error was caused by that one",
22
+ * which would claim the server's 500 was caused by the click handler and bury the useful frames.
23
+ * Use {@link ReportableApiFailure.toReportableError} to build the report-time wrapper instead.
24
+ *
25
+ * It is an `Error` subclass only so that engines and reporters treat its `stack` as a real stack.
26
+ * It is never thrown.
27
+ */
28
+ class ApiCallSite extends Error {
29
+ apiName;
30
+ methodName;
31
+ /** The property name the call site is parked under on a rejected error. */
32
+ static PROPERTY = 'callSite';
33
+ constructor(apiName, methodName) {
34
+ super(`${apiName}.${methodName} was called here`);
35
+ this.apiName = apiName;
36
+ this.methodName = methodName;
37
+ this.name = 'ApiCallSite';
38
+ }
39
+ /** `Api.method` — the endpoint identity, for messages and grouping. */
40
+ get label() {
41
+ return `${this.apiName}.${this.methodName}`;
42
+ }
43
+ /**
44
+ * Capture the caller's stack NOW. Call it synchronously as the first statement of the generated
45
+ * client method, passing that method as `boundary`, so the top frame is the app's own line.
46
+ *
47
+ * Where the engine has no `Error.captureStackTrace` the stack keeps the client method's own frame
48
+ * on top of the caller's — one extra frame, never a missing one.
49
+ */
50
+ // webpieces-disable no-function-outside-class -- the factory is the only way to build one, and it must run on the caller's stack
51
+ static capture(apiName, methodName, boundary) {
52
+ const site = new ApiCallSite(apiName, methodName);
53
+ const capturer = Error;
54
+ if (typeof capturer.captureStackTrace === 'function') {
55
+ capturer.captureStackTrace(site, boundary);
56
+ }
57
+ return site;
58
+ }
59
+ /**
60
+ * Park this call site on the value a call rejected with, WITHOUT changing that value: the same
61
+ * instance is rethrown, so every `instanceof` check downstream keeps working.
62
+ *
63
+ * Never clobbers: a failure that already carries a call site (an inner client call whose error
64
+ * propagated out through an outer one) keeps the innermost, which is the line that actually made
65
+ * the failing request. A non-`Error` or a frozen/sealed error is left untouched — attaching is a
66
+ * diagnostic nicety and must never itself throw.
67
+ */
68
+ // webpieces-disable no-any-unknown -- a rejection value is genuinely unknown; only an Error is touched
69
+ attachTo(failure) {
70
+ if (!(failure instanceof Error) || !Object.isExtensible(failure)) {
71
+ return;
72
+ }
73
+ if (Object.prototype.hasOwnProperty.call(failure, ApiCallSite.PROPERTY)) {
74
+ return;
75
+ }
76
+ const descriptor = {
77
+ value: this,
78
+ enumerable: false,
79
+ writable: false,
80
+ configurable: true,
81
+ };
82
+ Object.defineProperty(failure, ApiCallSite.PROPERTY, descriptor);
83
+ }
84
+ /** The call site parked on `failure` by a generated client, or undefined when there is none. */
85
+ // webpieces-disable no-function-outside-class -- stateless reader paired with attachTo; webpieces-disable no-any-unknown -- a caught value is genuinely unknown until narrowed here
86
+ static of(failure) {
87
+ if (!(failure instanceof Error)) {
88
+ return undefined;
89
+ }
90
+ // webpieces-disable no-any-unknown -- a property descriptor's value is untyped until narrowed below
91
+ const value = Object.getOwnPropertyDescriptor(failure, ApiCallSite.PROPERTY)?.value;
92
+ return value instanceof ApiCallSite ? value : undefined;
93
+ }
94
+ }
95
+ exports.ApiCallSite = ApiCallSite;
96
+ //# sourceMappingURL=ApiCallSite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ApiCallSite.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/errors/ApiCallSite.ts"],"names":[],"mappings":";;;AAeA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAa,WAAY,SAAQ,KAAK;IAKd;IACA;IALpB,2EAA2E;IAC3E,MAAM,CAAU,QAAQ,GAAG,UAAU,CAAC;IAEtC,YACoB,OAAe,EACf,UAAkB;QAElC,KAAK,CAAC,GAAG,OAAO,IAAI,UAAU,kBAAkB,CAAC,CAAC;QAHlC,YAAO,GAAP,OAAO,CAAQ;QACf,eAAU,GAAV,UAAU,CAAQ;QAGlC,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC9B,CAAC;IAED,uEAAuE;IACvE,IAAI,KAAK;QACL,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;IAChD,CAAC;IAED;;;;;;OAMG;IACH,iIAAiI;IACjI,MAAM,CAAC,OAAO,CAAC,OAAe,EAAE,UAAkB,EAAE,QAA0B;QAC1E,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAG,KAA2B,CAAC;QAC7C,IAAI,OAAO,QAAQ,CAAC,iBAAiB,KAAK,UAAU,EAAE,CAAC;YACnD,QAAQ,CAAC,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;;;;;;OAQG;IACH,uGAAuG;IACvG,QAAQ,CAAC,OAAgB;QACrB,IAAI,CAAC,CAAC,OAAO,YAAY,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC;YAC/D,OAAO;QACX,CAAC;QACD,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;YACtE,OAAO;QACX,CAAC;QACD,MAAM,UAAU,GAAuB;YACnC,KAAK,EAAE,IAAI;YACX,UAAU,EAAE,KAAK;YACjB,QAAQ,EAAE,KAAK;YACf,YAAY,EAAE,IAAI;SACrB,CAAC;QACF,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,WAAW,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACrE,CAAC;IAED,gGAAgG;IAChG,oLAAoL;IACpL,MAAM,CAAC,EAAE,CAAC,OAAgB;QACtB,IAAI,CAAC,CAAC,OAAO,YAAY,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,SAAS,CAAC;QACrB,CAAC;QACD,oGAAoG;QACpG,MAAM,KAAK,GAAY,MAAM,CAAC,wBAAwB,CAClD,OAAO,EACP,WAAW,CAAC,QAAQ,CACvB,EAAE,KAAK,CAAC;QACT,OAAO,KAAK,YAAY,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5D,CAAC;;AAxEL,kCAyEC","sourcesContent":["/**\n * Any function value used as the TOP of a captured stack: every frame from it upward is dropped.\n * Its parameters are irrelevant, so `never[]` lets every generated client method be passed as-is.\n */\n// webpieces-disable no-any-unknown -- any function may be a boundary; its return value is never read\nexport type CallSiteBoundary = (...args: never[]) => unknown;\n\n/**\n * V8's (and SpiderMonkey's) non-standard stack-trimming hook. It is not in the ES `lib` typings and\n * not every engine has it, so it is read through this optional shape rather than assumed.\n */\ninterface StackTraceCapturer {\n captureStackTrace?: (target: object, boundary?: CallSiteBoundary) => void;\n}\n\n/**\n * WHERE the app called a generated api client method — captured synchronously on the CALLER's\n * stack, before the request was sent, and parked on the failure the call rejected with (#1175).\n *\n * WHY IT EXISTS: an api failure is CONSTRUCTED when the response arrives (`ClientErrorTranslator`,\n * `ReceivedApiErrorRule`), long after the stack that made the call has unwound. Its own stack is\n * therefore all framework frames — zone.js, `ProxyClient`, the translator — and not one frame of app\n * code. Measured in a consuming app's error reporter, that meant (1) no way to see which line of app\n * code made the failing call, and (2) every rpc failure in the app grouped into ONE issue, because\n * every one ends in the same framework frames. One `new Error()` per call is cheap; the stack string\n * is only formatted if somebody reads it.\n *\n * WHERE IT LIVES: the property `callSite` on the rejected error, NON-ENUMERABLE so it never reaches\n * a `JSON.stringify`, a structured log record or an error reporter's \"extra data\" by accident. It is\n * typed on {@link ApiError} (`err.callSite`); {@link ApiCallSite.of} reads it off any thrown value,\n * because an app's own `ErrorTranslator` may reject with a type that is not an `ApiError`.\n *\n * Deliberately NOT `cause`: error reporters follow `cause` as \"this error was caused by that one\",\n * which would claim the server's 500 was caused by the click handler and bury the useful frames.\n * Use {@link ReportableApiFailure.toReportableError} to build the report-time wrapper instead.\n *\n * It is an `Error` subclass only so that engines and reporters treat its `stack` as a real stack.\n * It is never thrown.\n */\nexport class ApiCallSite extends Error {\n /** The property name the call site is parked under on a rejected error. */\n static readonly PROPERTY = 'callSite';\n\n private constructor(\n public readonly apiName: string,\n public readonly methodName: string,\n ) {\n super(`${apiName}.${methodName} was called here`);\n this.name = 'ApiCallSite';\n }\n\n /** `Api.method` — the endpoint identity, for messages and grouping. */\n get label(): string {\n return `${this.apiName}.${this.methodName}`;\n }\n\n /**\n * Capture the caller's stack NOW. Call it synchronously as the first statement of the generated\n * client method, passing that method as `boundary`, so the top frame is the app's own line.\n *\n * Where the engine has no `Error.captureStackTrace` the stack keeps the client method's own frame\n * on top of the caller's — one extra frame, never a missing one.\n */\n // webpieces-disable no-function-outside-class -- the factory is the only way to build one, and it must run on the caller's stack\n static capture(apiName: string, methodName: string, boundary: CallSiteBoundary): ApiCallSite {\n const site = new ApiCallSite(apiName, methodName);\n const capturer = Error as StackTraceCapturer;\n if (typeof capturer.captureStackTrace === 'function') {\n capturer.captureStackTrace(site, boundary);\n }\n return site;\n }\n\n /**\n * Park this call site on the value a call rejected with, WITHOUT changing that value: the same\n * instance is rethrown, so every `instanceof` check downstream keeps working.\n *\n * Never clobbers: a failure that already carries a call site (an inner client call whose error\n * propagated out through an outer one) keeps the innermost, which is the line that actually made\n * the failing request. A non-`Error` or a frozen/sealed error is left untouched — attaching is a\n * diagnostic nicety and must never itself throw.\n */\n // webpieces-disable no-any-unknown -- a rejection value is genuinely unknown; only an Error is touched\n attachTo(failure: unknown): void {\n if (!(failure instanceof Error) || !Object.isExtensible(failure)) {\n return;\n }\n if (Object.prototype.hasOwnProperty.call(failure, ApiCallSite.PROPERTY)) {\n return;\n }\n const descriptor: PropertyDescriptor = {\n value: this,\n enumerable: false,\n writable: false,\n configurable: true,\n };\n Object.defineProperty(failure, ApiCallSite.PROPERTY, descriptor);\n }\n\n /** The call site parked on `failure` by a generated client, or undefined when there is none. */\n // webpieces-disable no-function-outside-class -- stateless reader paired with attachTo; webpieces-disable no-any-unknown -- a caught value is genuinely unknown until narrowed here\n static of(failure: unknown): ApiCallSite | undefined {\n if (!(failure instanceof Error)) {\n return undefined;\n }\n // webpieces-disable no-any-unknown -- a property descriptor's value is untyped until narrowed below\n const value: unknown = Object.getOwnPropertyDescriptor(\n failure,\n ApiCallSite.PROPERTY,\n )?.value;\n return value instanceof ApiCallSite ? value : undefined;\n }\n}\n"]}
@@ -1,8 +1,17 @@
1
+ import { ApiCallSite } from './ApiCallSite';
1
2
  export type ApiErrorKind = 'end-user' | 'bad-request' | 'unauthorized' | 'forbidden' | 'not-found' | 'endpoint-not-found' | 'request-timeout' | 'rate-limited' | 'conflict' | 'unprocessable' | 'precondition-failed' | 'unsupported-media-type' | 'not-implemented' | 'coded' | 'implementation' | 'dependency' | 'bad-gateway' | 'unavailable' | 'dependency-timeout' | 'dependency-backoff' | 'connection';
2
3
  /** Category for portable API failures. Concrete subclasses alone define protocol mappings. */
3
4
  export declare abstract class ApiError extends Error {
4
5
  abstract readonly kind: ApiErrorKind;
5
6
  subType?: string;
7
+ /**
8
+ * Where the app called the generated client method that rejected with this error — present only
9
+ * on an error a generated api client (HTTP, IPC or Cloud Tasks) rejected with. Set by
10
+ * {@link ApiCallSite.attachTo} as a NON-ENUMERABLE own property, so it never reaches JSON or a
11
+ * structured log; `declare` emits no field, so an error that never failed a client call carries
12
+ * no `callSite` key at all. See {@link ApiCallSite} for why it is not `cause`.
13
+ */
14
+ readonly callSite?: ApiCallSite;
6
15
  constructor(message: string, cause?: Error);
7
16
  }
8
17
  export declare class ApiNotFoundError extends ApiError {
@@ -146,4 +155,42 @@ export declare class ApiCodedError extends ApiError {
146
155
  /** True when the status describes the caller's mistake rather than a fault on the serving side. */
147
156
  isCallerError(): boolean;
148
157
  }
158
+ /**
159
+ * The CALLER's build is too old for this server: HTTP **426 Upgrade Required**, errorCode
160
+ * {@link ApiClientTooOldError.CODE}. It means "upgrade", never "bug".
161
+ *
162
+ * WHO THROWS IT: a server gate that compares the build the caller DECLARED (a header, a version in
163
+ * the request) against the minimum this server still supports. Every webpieces client and server
164
+ * deployed separately hits this skew (a browser tab left open across a release, a mobile WebView
165
+ * pinned to an old bundle), and only the SERVER can tell that its caller is too old.
166
+ *
167
+ * WHY 426 AND NOT 266: a 266 means "the user must fix something" and its message is shown verbatim,
168
+ * so a bundle built before the gate existed would print the server's developer text as a user
169
+ * instruction. A non-266 status reaches every bundle already in the field as a non-user failure.
170
+ *
171
+ * WHAT RECEIVERS DO: `ReceivedApiErrorRule` decodes it back into THIS type on every hop
172
+ * (server-to-server and browser, HTTP and IPC) instead of the `ApiImplementationError` an ordinary
173
+ * received 4xx becomes. A client treats it as "reload / install the new build", never as a red bug
174
+ * dialog. It pairs with `allowUpgradeInFlight` (`HttpEndpointOptions`), which declares which calls
175
+ * an upgrade may cut off: that flag is the client deciding WHEN it may upgrade, this error is the
176
+ * server saying that it MUST.
177
+ *
178
+ * NO user-facing copy: `message` is developer text like every non-end-user kind (it is not published
179
+ * across the wire), and the receiving client owns the wording it shows. The status and the code are
180
+ * FIXED here, so a throw site cannot send a 426 that the receivers would not recognise.
181
+ *
182
+ * It is an {@link ApiCodedError} on purpose: the wire form is exactly `kind: 'coded'`,
183
+ * `statusCode: 426`, `errorCode: 'client-too-old'`, which is what an app's own hand-rolled
184
+ * client-too-old error already publishes, so a bundle in the field and a server on the new release
185
+ * agree without either side changing its payload.
186
+ */
187
+ export declare class ApiClientTooOldError extends ApiCodedError {
188
+ /** The fixed `errorCode`; together with status 426 it is what a receiver matches on. */
189
+ static readonly CODE = "client-too-old";
190
+ /** HTTP 426 Upgrade Required. */
191
+ static readonly STATUS_CODE = 426;
192
+ constructor(message: string, cause?: Error);
193
+ /** True when a coded failure's status and code are this error's wire identity. */
194
+ static matches(statusCode: number, errorCode: string | undefined): boolean;
195
+ }
149
196
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ApiCodedError = exports.ApiNotImplementedError = exports.ApiUnsupportedMediaTypeError = exports.ApiPreconditionFailedError = exports.ApiUnprocessableError = exports.ApiConflictError = exports.ApiDependencyBackoffError = exports.ApiUnauthorizedError = exports.ApiBadRequestError = exports.ApiEndUserError = exports.ApiEndpointNotFoundError = exports.ApiConnectionError = exports.ApiRequestTimeoutError = exports.ApiForbiddenError = exports.ApiRateLimitedError = exports.ApiImplementationError = exports.ApiDependencyTimeoutError = exports.ApiUnavailableError = exports.ApiBadGatewayError = exports.ApiDependencyError = exports.ApiNotFoundError = exports.ApiError = void 0;
3
+ exports.ApiClientTooOldError = exports.ApiCodedError = exports.ApiNotImplementedError = exports.ApiUnsupportedMediaTypeError = exports.ApiPreconditionFailedError = exports.ApiUnprocessableError = exports.ApiConflictError = exports.ApiDependencyBackoffError = exports.ApiUnauthorizedError = exports.ApiBadRequestError = exports.ApiEndUserError = exports.ApiEndpointNotFoundError = exports.ApiConnectionError = exports.ApiRequestTimeoutError = exports.ApiForbiddenError = exports.ApiRateLimitedError = exports.ApiImplementationError = exports.ApiDependencyTimeoutError = exports.ApiUnavailableError = exports.ApiBadGatewayError = exports.ApiDependencyError = exports.ApiNotFoundError = exports.ApiError = void 0;
4
4
  /** Category for portable API failures. Concrete subclasses alone define protocol mappings. */
5
5
  class ApiError extends Error {
6
6
  subType;
@@ -188,4 +188,49 @@ class ApiCodedError extends ApiError {
188
188
  }
189
189
  }
190
190
  exports.ApiCodedError = ApiCodedError;
191
+ /**
192
+ * The CALLER's build is too old for this server: HTTP **426 Upgrade Required**, errorCode
193
+ * {@link ApiClientTooOldError.CODE}. It means "upgrade", never "bug".
194
+ *
195
+ * WHO THROWS IT: a server gate that compares the build the caller DECLARED (a header, a version in
196
+ * the request) against the minimum this server still supports. Every webpieces client and server
197
+ * deployed separately hits this skew (a browser tab left open across a release, a mobile WebView
198
+ * pinned to an old bundle), and only the SERVER can tell that its caller is too old.
199
+ *
200
+ * WHY 426 AND NOT 266: a 266 means "the user must fix something" and its message is shown verbatim,
201
+ * so a bundle built before the gate existed would print the server's developer text as a user
202
+ * instruction. A non-266 status reaches every bundle already in the field as a non-user failure.
203
+ *
204
+ * WHAT RECEIVERS DO: `ReceivedApiErrorRule` decodes it back into THIS type on every hop
205
+ * (server-to-server and browser, HTTP and IPC) instead of the `ApiImplementationError` an ordinary
206
+ * received 4xx becomes. A client treats it as "reload / install the new build", never as a red bug
207
+ * dialog. It pairs with `allowUpgradeInFlight` (`HttpEndpointOptions`), which declares which calls
208
+ * an upgrade may cut off: that flag is the client deciding WHEN it may upgrade, this error is the
209
+ * server saying that it MUST.
210
+ *
211
+ * NO user-facing copy: `message` is developer text like every non-end-user kind (it is not published
212
+ * across the wire), and the receiving client owns the wording it shows. The status and the code are
213
+ * FIXED here, so a throw site cannot send a 426 that the receivers would not recognise.
214
+ *
215
+ * It is an {@link ApiCodedError} on purpose: the wire form is exactly `kind: 'coded'`,
216
+ * `statusCode: 426`, `errorCode: 'client-too-old'`, which is what an app's own hand-rolled
217
+ * client-too-old error already publishes, so a bundle in the field and a server on the new release
218
+ * agree without either side changing its payload.
219
+ */
220
+ class ApiClientTooOldError extends ApiCodedError {
221
+ /** The fixed `errorCode`; together with status 426 it is what a receiver matches on. */
222
+ static CODE = 'client-too-old';
223
+ /** HTTP 426 Upgrade Required. */
224
+ static STATUS_CODE = 426;
225
+ constructor(message, cause) {
226
+ super(message, ApiClientTooOldError.STATUS_CODE, ApiClientTooOldError.CODE, cause);
227
+ }
228
+ /** True when a coded failure's status and code are this error's wire identity. */
229
+ // webpieces-disable no-function-outside-class -- stateless wire-identity predicate
230
+ static matches(statusCode, errorCode) {
231
+ return (statusCode === ApiClientTooOldError.STATUS_CODE &&
232
+ errorCode === ApiClientTooOldError.CODE);
233
+ }
234
+ }
235
+ exports.ApiClientTooOldError = ApiClientTooOldError;
191
236
  //# sourceMappingURL=ApiError.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ApiError.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/errors/ApiError.ts"],"names":[],"mappings":"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","sourcesContent":["export type ApiErrorKind =\n | 'end-user'\n | 'bad-request'\n | 'unauthorized'\n | 'forbidden'\n | 'not-found'\n | 'endpoint-not-found'\n | 'request-timeout'\n | 'rate-limited'\n | 'conflict'\n | 'unprocessable'\n | 'precondition-failed'\n | 'unsupported-media-type'\n | 'not-implemented'\n | 'coded'\n | 'implementation'\n | 'dependency'\n | 'bad-gateway'\n | 'unavailable'\n | 'dependency-timeout'\n | 'dependency-backoff'\n | 'connection';\n\n/** Category for portable API failures. Concrete subclasses alone define protocol mappings. */\nexport abstract class ApiError extends Error {\n abstract readonly kind: ApiErrorKind;\n public subType?: string;\n\n constructor(message: string, cause?: Error) {\n super(message, { cause });\n this.name = new.target.name;\n }\n}\n\nexport class ApiNotFoundError extends ApiError {\n override readonly kind: ApiErrorKind = 'not-found';\n}\nexport class ApiDependencyError extends ApiError {\n override readonly kind: ApiErrorKind = 'dependency';\n}\n/** A retry-eligible HTTP 502/proxy failure, distinct from a generic dependency defect. */\nexport class ApiBadGatewayError extends ApiDependencyError {\n override readonly kind = 'bad-gateway' as const;\n}\nexport class ApiUnavailableError extends ApiDependencyError {\n override readonly kind = 'unavailable' as const;\n}\nexport class ApiDependencyTimeoutError extends ApiDependencyError {\n override readonly kind = 'dependency-timeout' as const;\n}\n\n/** A bug or invalid implementation state. Local errors become server errors only after a remote decode. */\nexport class ApiImplementationError extends ApiError {\n override readonly kind = 'implementation' as const;\n\n constructor(\n message: string,\n cause?: Error,\n public readonly serverError = false,\n ) {\n super(message, cause);\n }\n}\n\nexport class ApiRateLimitedError extends ApiError {\n override readonly kind = 'rate-limited' as const;\n}\nexport class ApiForbiddenError extends ApiError {\n override readonly kind = 'forbidden' as const;\n}\nexport class ApiRequestTimeoutError extends ApiError {\n override readonly kind = 'request-timeout' as const;\n}\n\n/**\n * A caller-local connection failure. HTTP servers normalize this to ApiImplementationError.\n *\n * `message` is the developer text (it names the url and the transport's own reason); `userMessage`\n * is the stable text an app renders verbatim to an end user, matching `ApiCallTimeoutError.userMessage`\n * so the two transport failures read consistently.\n */\nexport class ApiConnectionError extends ApiError {\n static readonly USER_MESSAGE =\n \"We couldn't reach the server. Please check your network connection and try again.\";\n\n override readonly kind = 'connection' as const;\n /** End-user text; `message` stays the developer text. */\n readonly userMessage: string = ApiConnectionError.USER_MESSAGE;\n}\n\n/** The API operation is missing, distinct from an absent domain entity. */\nexport class ApiEndpointNotFoundError extends ApiNotFoundError {\n override readonly kind = 'endpoint-not-found' as const;\n}\n\n/**\n * The HTTP status an API EDGE answers for an {@link ApiEndUserError}. End-user outcomes only, so\n * nobody tunnels a 5xx/401/403/429 through the end-user channel (auth, rate limiting and dependency\n * failure keep their own types). Extend deliberately.\n */\nexport type EdgeHttpStatus = 400 | 404 | 409 | 422;\n\n/**\n * Expected mistake by the ACTOR driving the call: the input was well-formed but wrong in a way the actor\n * must fix (for example \"the two passwords you entered do not match\"). The actor may be a human user in\n * a GUI OR an LLM calling an MCP tool; the same class serves both. Its message is deliberately\n * caller-safe and is the only ApiError message published verbatim: `ApiErrorCodec` keeps it (and\n * `errorCode`) across every remote hop, so a downstream service's text reaches the GUI or the model\n * byte-for-byte.\n *\n * `edgeHttpStatus` travels the same way. It is what a partner-facing API edge (an http-server router\n * in `'edge'` end-user-status mode) answers instead of 266, so one throw site serves both a GUI and a\n * published REST contract: `throw new ApiEndUserError('That report does not exist', 'report_not_found', 404)`.\n * GUI edges ignore it and keep answering 266.\n */\nexport class ApiEndUserError extends ApiError {\n override readonly kind = 'end-user' as const;\n\n constructor(\n message: string,\n public errorCode?: string,\n public edgeHttpStatus?: EdgeHttpStatus,\n cause?: Error,\n ) {\n super(message, cause);\n this.subType = 'USER_ERROR';\n }\n\n /** Narrows a dynamic value (for example one read off the wire) to a legal {@link EdgeHttpStatus}. */\n // webpieces-disable no-function-outside-class -- stateless status guard; webpieces-disable no-any-unknown -- wire values are narrowed here\n static isEdgeHttpStatus(value: unknown): value is EdgeHttpStatus {\n return value === 400 || value === 404 || value === 409 || value === 422;\n }\n}\n\n/**\n * Invalid API caller input (malformed, or failed validation). `message` is operator-only diagnostic\n * text and may name internals; it is never published. `callerMessage` (plus `field`) is what a GUI OR\n * an MCP model is shown so it can correct its input, so throw sites that want the caller to\n * self-correct MUST set it.\n */\nexport class ApiBadRequestError extends ApiError {\n override readonly kind = 'bad-request' as const;\n\n constructor(\n message: string,\n public field?: string,\n public callerMessage?: string,\n cause?: Error,\n ) {\n super(message, cause);\n }\n}\n\nexport class ApiUnauthorizedError extends ApiError {\n override readonly kind = 'unauthorized' as const;\n\n constructor(message: string, subType?: string, cause?: Error) {\n super(message, cause);\n this.subType = subType;\n }\n}\n\nexport class ApiDependencyBackoffError extends ApiDependencyError {\n override readonly kind = 'dependency-backoff' as const;\n\n constructor(\n message: string,\n public retryAfterSeconds = 30,\n cause?: Error,\n ) {\n super(message, cause);\n }\n}\n\n/** The request conflicts with the current state of the target resource (HTTP 409). */\nexport class ApiConflictError extends ApiError {\n override readonly kind = 'conflict' as const;\n}\n\n/** The request was well-formed but semantically unprocessable (HTTP 422). */\nexport class ApiUnprocessableError extends ApiError {\n override readonly kind = 'unprocessable' as const;\n}\n\n/** A caller-supplied precondition (for example If-Match) did not hold (HTTP 412). */\nexport class ApiPreconditionFailedError extends ApiError {\n override readonly kind = 'precondition-failed' as const;\n}\n\n/** The request body is in a media type this operation does not accept (HTTP 415). */\nexport class ApiUnsupportedMediaTypeError extends ApiError {\n override readonly kind = 'unsupported-media-type' as const;\n}\n\n/** The operation exists in the contract but this server does not implement it (HTTP 501). */\nexport class ApiNotImplementedError extends ApiError {\n override readonly kind = 'not-implemented' as const;\n}\n\n/** Builds the tuple [0, 1, ..., N-1]; tail-recursive so TypeScript can reach 600. */\ntype StatusRange<N extends number, Acc extends number[] = []> = Acc['length'] extends N\n ? Acc\n : StatusRange<N, [...Acc, Acc['length']]>;\n\n/**\n * Every integer from 100 through 599 as a literal union. `new ApiCodedError(msg, 600)`,\n * `new ApiCodedError(msg, 99)` and `new ApiCodedError(msg, 404.5)` do not compile; a dynamic\n * `number` must be narrowed with {@link ApiCodedError.isStatusCode} first.\n */\nexport type ApiStatusCode = Exclude<StatusRange<600>[number], StatusRange<100>[number]>;\n\n/**\n * Catch-all carrying an explicit protocol status for outcomes no named ApiError covers. Any code in\n * 100-599 is accepted, including one a named class already owns. `statusCode` and `errorCode` survive\n * every remote hop through `ApiErrorCodec`; `message` stays operator-only like every other non\n * end-user kind. A status below 500 is a caller outcome (except 408 and 429, which mirror\n * `ApiRequestTimeoutError` and `ApiRateLimitedError`); 500 and above is a server/dependency fault.\n */\nexport class ApiCodedError extends ApiError {\n override readonly kind = 'coded' as const;\n\n constructor(\n message: string,\n public readonly statusCode: ApiStatusCode,\n public errorCode?: string,\n cause?: Error,\n ) {\n super(message, cause);\n }\n\n /** Narrows a dynamic number (for example one read off the wire) to a legal status code. */\n // webpieces-disable no-function-outside-class -- stateless status guard; webpieces-disable no-any-unknown -- wire and app values are narrowed here\n static isStatusCode(value: unknown): value is ApiStatusCode {\n return typeof value === 'number' && Number.isInteger(value) && value >= 100 && value <= 599;\n }\n\n /** True when the status describes the caller's mistake rather than a fault on the serving side. */\n isCallerError(): boolean {\n return this.statusCode < 500 && this.statusCode !== 408 && this.statusCode !== 429;\n }\n}\n"]}
1
+ {"version":3,"file":"ApiError.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/errors/ApiError.ts"],"names":[],"mappings":"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","sourcesContent":["import { ApiCallSite } from './ApiCallSite';\n\nexport type ApiErrorKind =\n | 'end-user'\n | 'bad-request'\n | 'unauthorized'\n | 'forbidden'\n | 'not-found'\n | 'endpoint-not-found'\n | 'request-timeout'\n | 'rate-limited'\n | 'conflict'\n | 'unprocessable'\n | 'precondition-failed'\n | 'unsupported-media-type'\n | 'not-implemented'\n | 'coded'\n | 'implementation'\n | 'dependency'\n | 'bad-gateway'\n | 'unavailable'\n | 'dependency-timeout'\n | 'dependency-backoff'\n | 'connection';\n\n/** Category for portable API failures. Concrete subclasses alone define protocol mappings. */\nexport abstract class ApiError extends Error {\n abstract readonly kind: ApiErrorKind;\n public subType?: string;\n\n /**\n * Where the app called the generated client method that rejected with this error — present only\n * on an error a generated api client (HTTP, IPC or Cloud Tasks) rejected with. Set by\n * {@link ApiCallSite.attachTo} as a NON-ENUMERABLE own property, so it never reaches JSON or a\n * structured log; `declare` emits no field, so an error that never failed a client call carries\n * no `callSite` key at all. See {@link ApiCallSite} for why it is not `cause`.\n */\n declare readonly callSite?: ApiCallSite;\n\n constructor(message: string, cause?: Error) {\n super(message, { cause });\n this.name = new.target.name;\n }\n}\n\nexport class ApiNotFoundError extends ApiError {\n override readonly kind: ApiErrorKind = 'not-found';\n}\nexport class ApiDependencyError extends ApiError {\n override readonly kind: ApiErrorKind = 'dependency';\n}\n/** A retry-eligible HTTP 502/proxy failure, distinct from a generic dependency defect. */\nexport class ApiBadGatewayError extends ApiDependencyError {\n override readonly kind = 'bad-gateway' as const;\n}\nexport class ApiUnavailableError extends ApiDependencyError {\n override readonly kind = 'unavailable' as const;\n}\nexport class ApiDependencyTimeoutError extends ApiDependencyError {\n override readonly kind = 'dependency-timeout' as const;\n}\n\n/** A bug or invalid implementation state. Local errors become server errors only after a remote decode. */\nexport class ApiImplementationError extends ApiError {\n override readonly kind = 'implementation' as const;\n\n constructor(\n message: string,\n cause?: Error,\n public readonly serverError = false,\n ) {\n super(message, cause);\n }\n}\n\nexport class ApiRateLimitedError extends ApiError {\n override readonly kind = 'rate-limited' as const;\n}\nexport class ApiForbiddenError extends ApiError {\n override readonly kind = 'forbidden' as const;\n}\nexport class ApiRequestTimeoutError extends ApiError {\n override readonly kind = 'request-timeout' as const;\n}\n\n/**\n * A caller-local connection failure. HTTP servers normalize this to ApiImplementationError.\n *\n * `message` is the developer text (it names the url and the transport's own reason); `userMessage`\n * is the stable text an app renders verbatim to an end user, matching `ApiCallTimeoutError.userMessage`\n * so the two transport failures read consistently.\n */\nexport class ApiConnectionError extends ApiError {\n static readonly USER_MESSAGE =\n \"We couldn't reach the server. Please check your network connection and try again.\";\n\n override readonly kind = 'connection' as const;\n /** End-user text; `message` stays the developer text. */\n readonly userMessage: string = ApiConnectionError.USER_MESSAGE;\n}\n\n/** The API operation is missing, distinct from an absent domain entity. */\nexport class ApiEndpointNotFoundError extends ApiNotFoundError {\n override readonly kind = 'endpoint-not-found' as const;\n}\n\n/**\n * The HTTP status an API EDGE answers for an {@link ApiEndUserError}. End-user outcomes only, so\n * nobody tunnels a 5xx/401/403/429 through the end-user channel (auth, rate limiting and dependency\n * failure keep their own types). Extend deliberately.\n */\nexport type EdgeHttpStatus = 400 | 404 | 409 | 422;\n\n/**\n * Expected mistake by the ACTOR driving the call: the input was well-formed but wrong in a way the actor\n * must fix (for example \"the two passwords you entered do not match\"). The actor may be a human user in\n * a GUI OR an LLM calling an MCP tool; the same class serves both. Its message is deliberately\n * caller-safe and is the only ApiError message published verbatim: `ApiErrorCodec` keeps it (and\n * `errorCode`) across every remote hop, so a downstream service's text reaches the GUI or the model\n * byte-for-byte.\n *\n * `edgeHttpStatus` travels the same way. It is what a partner-facing API edge (an http-server router\n * in `'edge'` end-user-status mode) answers instead of 266, so one throw site serves both a GUI and a\n * published REST contract: `throw new ApiEndUserError('That report does not exist', 'report_not_found', 404)`.\n * GUI edges ignore it and keep answering 266.\n */\nexport class ApiEndUserError extends ApiError {\n override readonly kind = 'end-user' as const;\n\n constructor(\n message: string,\n public errorCode?: string,\n public edgeHttpStatus?: EdgeHttpStatus,\n cause?: Error,\n ) {\n super(message, cause);\n this.subType = 'USER_ERROR';\n }\n\n /** Narrows a dynamic value (for example one read off the wire) to a legal {@link EdgeHttpStatus}. */\n // webpieces-disable no-function-outside-class -- stateless status guard; webpieces-disable no-any-unknown -- wire values are narrowed here\n static isEdgeHttpStatus(value: unknown): value is EdgeHttpStatus {\n return value === 400 || value === 404 || value === 409 || value === 422;\n }\n}\n\n/**\n * Invalid API caller input (malformed, or failed validation). `message` is operator-only diagnostic\n * text and may name internals; it is never published. `callerMessage` (plus `field`) is what a GUI OR\n * an MCP model is shown so it can correct its input, so throw sites that want the caller to\n * self-correct MUST set it.\n */\nexport class ApiBadRequestError extends ApiError {\n override readonly kind = 'bad-request' as const;\n\n constructor(\n message: string,\n public field?: string,\n public callerMessage?: string,\n cause?: Error,\n ) {\n super(message, cause);\n }\n}\n\nexport class ApiUnauthorizedError extends ApiError {\n override readonly kind = 'unauthorized' as const;\n\n constructor(message: string, subType?: string, cause?: Error) {\n super(message, cause);\n this.subType = subType;\n }\n}\n\nexport class ApiDependencyBackoffError extends ApiDependencyError {\n override readonly kind = 'dependency-backoff' as const;\n\n constructor(\n message: string,\n public retryAfterSeconds = 30,\n cause?: Error,\n ) {\n super(message, cause);\n }\n}\n\n/** The request conflicts with the current state of the target resource (HTTP 409). */\nexport class ApiConflictError extends ApiError {\n override readonly kind = 'conflict' as const;\n}\n\n/** The request was well-formed but semantically unprocessable (HTTP 422). */\nexport class ApiUnprocessableError extends ApiError {\n override readonly kind = 'unprocessable' as const;\n}\n\n/** A caller-supplied precondition (for example If-Match) did not hold (HTTP 412). */\nexport class ApiPreconditionFailedError extends ApiError {\n override readonly kind = 'precondition-failed' as const;\n}\n\n/** The request body is in a media type this operation does not accept (HTTP 415). */\nexport class ApiUnsupportedMediaTypeError extends ApiError {\n override readonly kind = 'unsupported-media-type' as const;\n}\n\n/** The operation exists in the contract but this server does not implement it (HTTP 501). */\nexport class ApiNotImplementedError extends ApiError {\n override readonly kind = 'not-implemented' as const;\n}\n\n/** Builds the tuple [0, 1, ..., N-1]; tail-recursive so TypeScript can reach 600. */\ntype StatusRange<N extends number, Acc extends number[] = []> = Acc['length'] extends N\n ? Acc\n : StatusRange<N, [...Acc, Acc['length']]>;\n\n/**\n * Every integer from 100 through 599 as a literal union. `new ApiCodedError(msg, 600)`,\n * `new ApiCodedError(msg, 99)` and `new ApiCodedError(msg, 404.5)` do not compile; a dynamic\n * `number` must be narrowed with {@link ApiCodedError.isStatusCode} first.\n */\nexport type ApiStatusCode = Exclude<StatusRange<600>[number], StatusRange<100>[number]>;\n\n/**\n * Catch-all carrying an explicit protocol status for outcomes no named ApiError covers. Any code in\n * 100-599 is accepted, including one a named class already owns. `statusCode` and `errorCode` survive\n * every remote hop through `ApiErrorCodec`; `message` stays operator-only like every other non\n * end-user kind. A status below 500 is a caller outcome (except 408 and 429, which mirror\n * `ApiRequestTimeoutError` and `ApiRateLimitedError`); 500 and above is a server/dependency fault.\n */\nexport class ApiCodedError extends ApiError {\n override readonly kind = 'coded' as const;\n\n constructor(\n message: string,\n public readonly statusCode: ApiStatusCode,\n public errorCode?: string,\n cause?: Error,\n ) {\n super(message, cause);\n }\n\n /** Narrows a dynamic number (for example one read off the wire) to a legal status code. */\n // webpieces-disable no-function-outside-class -- stateless status guard; webpieces-disable no-any-unknown -- wire and app values are narrowed here\n static isStatusCode(value: unknown): value is ApiStatusCode {\n return typeof value === 'number' && Number.isInteger(value) && value >= 100 && value <= 599;\n }\n\n /** True when the status describes the caller's mistake rather than a fault on the serving side. */\n isCallerError(): boolean {\n return this.statusCode < 500 && this.statusCode !== 408 && this.statusCode !== 429;\n }\n}\n\n/**\n * The CALLER's build is too old for this server: HTTP **426 Upgrade Required**, errorCode\n * {@link ApiClientTooOldError.CODE}. It means \"upgrade\", never \"bug\".\n *\n * WHO THROWS IT: a server gate that compares the build the caller DECLARED (a header, a version in\n * the request) against the minimum this server still supports. Every webpieces client and server\n * deployed separately hits this skew (a browser tab left open across a release, a mobile WebView\n * pinned to an old bundle), and only the SERVER can tell that its caller is too old.\n *\n * WHY 426 AND NOT 266: a 266 means \"the user must fix something\" and its message is shown verbatim,\n * so a bundle built before the gate existed would print the server's developer text as a user\n * instruction. A non-266 status reaches every bundle already in the field as a non-user failure.\n *\n * WHAT RECEIVERS DO: `ReceivedApiErrorRule` decodes it back into THIS type on every hop\n * (server-to-server and browser, HTTP and IPC) instead of the `ApiImplementationError` an ordinary\n * received 4xx becomes. A client treats it as \"reload / install the new build\", never as a red bug\n * dialog. It pairs with `allowUpgradeInFlight` (`HttpEndpointOptions`), which declares which calls\n * an upgrade may cut off: that flag is the client deciding WHEN it may upgrade, this error is the\n * server saying that it MUST.\n *\n * NO user-facing copy: `message` is developer text like every non-end-user kind (it is not published\n * across the wire), and the receiving client owns the wording it shows. The status and the code are\n * FIXED here, so a throw site cannot send a 426 that the receivers would not recognise.\n *\n * It is an {@link ApiCodedError} on purpose: the wire form is exactly `kind: 'coded'`,\n * `statusCode: 426`, `errorCode: 'client-too-old'`, which is what an app's own hand-rolled\n * client-too-old error already publishes, so a bundle in the field and a server on the new release\n * agree without either side changing its payload.\n */\nexport class ApiClientTooOldError extends ApiCodedError {\n /** The fixed `errorCode`; together with status 426 it is what a receiver matches on. */\n static readonly CODE = 'client-too-old';\n /** HTTP 426 Upgrade Required. */\n static readonly STATUS_CODE = 426;\n\n constructor(message: string, cause?: Error) {\n super(message, ApiClientTooOldError.STATUS_CODE, ApiClientTooOldError.CODE, cause);\n }\n\n /** True when a coded failure's status and code are this error's wire identity. */\n // webpieces-disable no-function-outside-class -- stateless wire-identity predicate\n static matches(statusCode: number, errorCode: string | undefined): boolean {\n return (\n statusCode === ApiClientTooOldError.STATUS_CODE &&\n errorCode === ApiClientTooOldError.CODE\n );\n }\n}\n"]}
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ApiErrorCodec = exports.ApiErrorPayload = void 0;
4
4
  const ApiError_1 = require("./ApiError");
5
5
  const errorUtils_1 = require("../lib/errorUtils");
6
+ const EndUserErrorRegistry_1 = require("./EndUserErrorRegistry");
6
7
  /** Allowlisted transport-neutral envelope. It never contains stacks or arbitrary properties. */
7
8
  class ApiErrorPayload {
8
9
  kind;
@@ -108,7 +109,8 @@ class ApiErrorCodec {
108
109
  const message = this.text(field('message')) ?? this.message(kind);
109
110
  switch (kind) {
110
111
  case 'end-user':
111
- return new ApiError_1.ApiEndUserError(message, this.text(field('errorCode')), this.edgeStatus(field('edgeHttpStatus')));
112
+ // The registered SUBCLASS for this errorCode, else the base type with the code intact.
113
+ return EndUserErrorRegistry_1.EndUserErrorRegistry.create(message, this.text(field('errorCode')), this.edgeStatus(field('edgeHttpStatus')));
112
114
  case 'bad-request':
113
115
  return new ApiError_1.ApiBadRequestError(message, this.text(field('field')), this.text(field('callerMessage')));
114
116
  case 'unauthorized':
@@ -157,7 +159,12 @@ class ApiErrorCodec {
157
159
  static coded(message, statusCode, errorCode) {
158
160
  if (!ApiError_1.ApiCodedError.isStatusCode(statusCode))
159
161
  return this.remoteImplementationError();
160
- return new ApiError_1.ApiCodedError(message, statusCode, this.text(errorCode));
162
+ const code = this.text(errorCode);
163
+ // The one coded failure webpieces names: rebuilt as its own type so a receiver sees
164
+ // "upgrade", never a generic coded 4xx (issue #1172).
165
+ if (ApiError_1.ApiClientTooOldError.matches(statusCode, code))
166
+ return new ApiError_1.ApiClientTooOldError(message);
167
+ return new ApiError_1.ApiCodedError(message, statusCode, code);
161
168
  }
162
169
  // webpieces-disable no-function-outside-class -- concrete normalization for malformed remote errors
163
170
  static remoteImplementationError() {
@@ -1 +1 @@
1
- {"version":3,"file":"ApiErrorCodec.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/errors/ApiErrorCodec.ts"],"names":[],"mappings":";;;AAAA,yCAyBoB;AACpB,kDAA4C;AAE5C,gGAAgG;AAChG,MAAa,eAAe;IAYb;IACA;IAZJ,KAAK,CAAmB;IACxB,OAAO,CAAU;IACjB,KAAK,CAAU;IACf,aAAa,CAAU;IACvB,SAAS,CAAU;IACnB,iBAAiB,CAAU;IAC3B,UAAU,CAAU;IAC3B,mGAAmG;IAC5F,cAAc,CAAU;IAE/B,YACW,OAAe,gBAAgB,EAC/B,UAAkB,gBAAgB;QADlC,SAAI,GAAJ,IAAI,CAA2B;QAC/B,YAAO,GAAP,OAAO,CAA2B;IAC1C,CAAC;CACP;AAfD,0CAeC;AAED,yEAAyE;AACzE,MAAa,aAAa;IACtB;;;;;;;;;OASG;IACH,kFAAkF;IAClF,MAAM,CAAC,MAAM,CAAC,KAAY;QACtB,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACtC,CAAC;IAED,mFAAmF;IAC3E,MAAM,CAAC,WAAW,CAAC,KAAY,EAAE,KAAa;QAClD,MAAM,IAAI,GAAG,KAAK,YAAY,mBAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC;QACvE,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;QAC3E,IAAI,KAAK,YAAY,mBAAQ;YAAE,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC1E,IAAI,KAAK,YAAY,0BAAe,EAAE,CAAC;YACnC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YAC/C,OAAO,CAAC,cAAc,GAAG,KAAK,CAAC,cAAc,CAAC;QAClD,CAAC;QACD,IAAI,KAAK,YAAY,wBAAa,EAAE,CAAC;YACjC,OAAO,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;YACtC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACnD,CAAC;QACD,IAAI,KAAK,YAAY,6BAAkB,EAAE,CAAC;YACtC,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,KAAK,YAAY,oCAAyB,EAAE,CAAC;YAC7C,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACnE,CAAC;QACD,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACZ,yFAAyF;YACzF,wFAAwF;YACxF,MAAM,KAAK,GAAG,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC;YACrE,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACxC,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,IAAA,oBAAO,EAAC,KAAK,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;YAChE,CAAC;QACL,CAAC;QACD,OAAO,OAAO,CAAC;IACnB,CAAC;IAED,+FAA+F;IAC/F,sJAAsJ;IACtJ,MAAM,CAAC,MAAM,CAAC,KAAc;QACxB,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACtC,CAAC;IAED,4EAA4E;IAC5E,wJAAwJ;IACxJ,MAAM,CAAC,SAAS,CAAC,KAAc;QAC3B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;QAC9D,MAAM,IAAI,GAAG,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC;QACnE,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAoB,CAAC,CAAC;IAC5E,CAAC;IAED,yJAAyJ;IACjJ,MAAM,CAAC,WAAW,CAAC,KAAc,EAAE,KAAa;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YAC3D,MAAM,KAAK,GAAG,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC;YACrE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBAC9C,wGAAwG;gBACxG,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE;oBAClC,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC;oBACzC,YAAY,EAAE,IAAI;oBAClB,QAAQ,EAAE,IAAI;iBACjB,CAAC,CAAC;YACP,CAAC;QACL,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,4IAA4I;IACpI,MAAM,CAAC,SAAS,CAAC,KAAc;QACnC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC,yBAAyB,EAAE,CAAC;QACzF,sFAAsF;QACtF,MAAM,KAAK,GAAG,CAAC,GAAW,EAAW,EAAE,CAAC,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,KAAK,CAAC;QAC3F,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAClE,QAAQ,IAAI,EAAE,CAAC;YACX,KAAK,UAAU;gBACX,OAAO,IAAI,0BAAe,CACtB,OAAO,EACP,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,EAC7B,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAC3C,CAAC;YACN,KAAK,aAAa;gBACd,OAAO,IAAI,6BAAkB,CACzB,OAAO,EACP,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,EACzB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CACpC,CAAC;YACN,KAAK,cAAc;gBACf,OAAO,IAAI,+BAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YAC1E,KAAK,WAAW;gBACZ,OAAO,IAAI,4BAAiB,CAAC,OAAO,CAAC,CAAC;YAC1C,KAAK,WAAW;gBACZ,OAAO,IAAI,2BAAgB,CAAC,OAAO,CAAC,CAAC;YACzC,KAAK,oBAAoB;gBACrB,OAAO,IAAI,mCAAwB,CAAC,OAAO,CAAC,CAAC;YACjD,KAAK,iBAAiB;gBAClB,OAAO,IAAI,iCAAsB,CAAC,OAAO,CAAC,CAAC;YAC/C,KAAK,cAAc;gBACf,OAAO,IAAI,8BAAmB,CAAC,OAAO,CAAC,CAAC;YAC5C,KAAK,UAAU;gBACX,OAAO,IAAI,2BAAgB,CAAC,OAAO,CAAC,CAAC;YACzC,KAAK,eAAe;gBAChB,OAAO,IAAI,gCAAqB,CAAC,OAAO,CAAC,CAAC;YAC9C,KAAK,qBAAqB;gBACtB,OAAO,IAAI,qCAA0B,CAAC,OAAO,CAAC,CAAC;YACnD,KAAK,wBAAwB;gBACzB,OAAO,IAAI,uCAA4B,CAAC,OAAO,CAAC,CAAC;YACrD,KAAK,iBAAiB;gBAClB,OAAO,IAAI,iCAAsB,CAAC,OAAO,CAAC,CAAC;YAC/C,KAAK,OAAO;gBACR,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,YAAY,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;YACxE,KAAK,YAAY;gBACb,OAAO,IAAI,6BAAkB,CAAC,OAAO,CAAC,CAAC;YAC3C,KAAK,aAAa;gBACd,OAAO,IAAI,6BAAkB,CAAC,OAAO,CAAC,CAAC;YAC3C,KAAK,aAAa;gBACd,OAAO,IAAI,8BAAmB,CAAC,OAAO,CAAC,CAAC;YAC5C,KAAK,oBAAoB;gBACrB,OAAO,IAAI,oCAAyB,CAAC,OAAO,CAAC,CAAC;YAClD,KAAK,oBAAoB;gBACrB,OAAO,IAAI,oCAAyB,CAChC,OAAO,EACP,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,CACxC,CAAC;YACN,KAAK,YAAY;gBACb,OAAO,IAAI,6BAAkB,CAAC,OAAO,CAAC,CAAC;YAC3C,KAAK,gBAAgB;gBACjB,OAAO,IAAI,iCAAsB,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;YAChE;gBACI,OAAO,IAAI,CAAC,yBAAyB,EAAE,CAAC;QAChD,CAAC;IACL,CAAC;IAED,uIAAuI;IAC/H,MAAM,CAAC,KAAK,CAAC,OAAe,EAAE,UAAmB,EAAE,SAAkB;QACzE,IAAI,CAAC,wBAAa,CAAC,YAAY,CAAC,UAAU,CAAC;YAAE,OAAO,IAAI,CAAC,yBAAyB,EAAE,CAAC;QACrF,OAAO,IAAI,wBAAa,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,oGAAoG;IAC5F,MAAM,CAAC,yBAAyB;QACpC,OAAO,IAAI,iCAAsB,CAAC,gBAAgB,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;IACzE,CAAC;IAED,+EAA+E;IACvE,MAAM,CAAC,aAAa,CAAC,KAAY,EAAE,IAAkB;QACzD,IAAI,KAAK,YAAY,0BAAe;YAAE,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAC1E,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,uIAAuI;IAC/H,MAAM,CAAC,IAAI,CAAC,KAAc;QAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACxE,CAAC;IAED,6GAA6G;IAC7G,uIAAuI;IAC/H,MAAM,CAAC,UAAU,CAAC,KAAc;QACpC,OAAO,0BAAe,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IACvE,CAAC;IAED,uIAAuI;IAC/H,MAAM,CAAC,IAAI,CAAC,KAAc;QAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;YACpE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC;YACxB,CAAC,CAAC,SAAS,CAAC;IACpB,CAAC;IAED,wJAAwJ;IAChJ,MAAM,CAAC,OAAO,CAAC,IAAa;QAChC,QAAQ,IAAI,EAAE,CAAC;YACX,KAAK,UAAU;gBACX,OAAO,gBAAgB,CAAC;YAC5B,KAAK,aAAa;gBACd,OAAO,aAAa,CAAC;YACzB,KAAK,cAAc;gBACf,OAAO,cAAc,CAAC;YAC1B,KAAK,WAAW;gBACZ,OAAO,WAAW,CAAC;YACvB,KAAK,WAAW;gBACZ,OAAO,WAAW,CAAC;YACvB,KAAK,oBAAoB;gBACrB,OAAO,oBAAoB,CAAC;YAChC,KAAK,iBAAiB;gBAClB,OAAO,iBAAiB,CAAC;YAC7B,KAAK,cAAc;gBACf,OAAO,cAAc,CAAC;YAC1B,KAAK,UAAU;gBACX,OAAO,UAAU,CAAC;YACtB,KAAK,eAAe;gBAChB,OAAO,uBAAuB,CAAC;YACnC,KAAK,qBAAqB;gBACtB,OAAO,qBAAqB,CAAC;YACjC,KAAK,wBAAwB;gBACzB,OAAO,wBAAwB,CAAC;YACpC,KAAK,iBAAiB;gBAClB,OAAO,iBAAiB,CAAC;YAC7B,KAAK,OAAO;gBACR,OAAO,gBAAgB,CAAC;YAC5B,KAAK,YAAY;gBACb,OAAO,kBAAkB,CAAC;YAC9B,KAAK,aAAa;gBACd,OAAO,aAAa,CAAC;YACzB,KAAK,aAAa;gBACd,OAAO,qBAAqB,CAAC;YACjC,KAAK,oBAAoB;gBACrB,OAAO,oBAAoB,CAAC;YAChC,KAAK,oBAAoB;gBACrB,OAAO,wBAAwB,CAAC;YACpC,KAAK,YAAY;gBACb,OAAO,kBAAkB,CAAC;YAC9B;gBACI,OAAO,gBAAgB,CAAC;QAChC,CAAC;IACL,CAAC;IAEO,MAAM,CAAU,KAAK,GAA8B,IAAI,GAAG,CAAe;QAC7E,UAAU;QACV,aAAa;QACb,cAAc;QACd,WAAW;QACX,WAAW;QACX,oBAAoB;QACpB,iBAAiB;QACjB,cAAc;QACd,UAAU;QACV,eAAe;QACf,qBAAqB;QACrB,wBAAwB;QACxB,iBAAiB;QACjB,OAAO;QACP,gBAAgB;QAChB,YAAY;QACZ,aAAa;QACb,aAAa;QACb,oBAAoB;QACpB,oBAAoB;QACpB,YAAY;KACf,CAAC,CAAC;;AAzPP,sCA0PC","sourcesContent":["import {\n ApiBadRequestError,\n ApiBadGatewayError,\n ApiCodedError,\n ApiConflictError,\n ApiConnectionError,\n ApiDependencyBackoffError,\n ApiDependencyError,\n ApiDependencyTimeoutError,\n ApiEndpointNotFoundError,\n ApiEndUserError,\n ApiError,\n ApiErrorKind,\n ApiForbiddenError,\n ApiImplementationError,\n ApiNotFoundError,\n ApiNotImplementedError,\n ApiPreconditionFailedError,\n ApiRateLimitedError,\n ApiRequestTimeoutError,\n ApiUnauthorizedError,\n ApiUnavailableError,\n ApiUnprocessableError,\n ApiUnsupportedMediaTypeError,\n EdgeHttpStatus,\n} from './ApiError';\nimport { toError } from '../lib/errorUtils';\n\n/** Allowlisted transport-neutral envelope. It never contains stacks or arbitrary properties. */\nexport class ApiErrorPayload {\n public cause?: ApiErrorPayload;\n public subType?: string;\n public field?: string;\n public callerMessage?: string;\n public errorCode?: string;\n public retryAfterSeconds?: number;\n public statusCode?: number;\n /** {@link ApiEndUserError.edgeHttpStatus}; absent when the thrower (or an older peer) set none. */\n public edgeHttpStatus?: number;\n\n constructor(\n public kind: string = 'implementation',\n public message: string = 'Internal Error',\n ) {}\n}\n\n/** Bounded safe codec shared by HTTP, IPC, and other remote adapters. */\nexport class ApiErrorCodec {\n /**\n * Encode a thrown error faithfully. The input is an `Error`, never `unknown`: every caller in\n * the framework has already narrowed with `toError` at its catch, and the `unknown` this used to\n * advertise leaked out of the PRIVATE `.cause` recursion below (`Error.cause` is typed `unknown`\n * in TypeScript), which is an internal concern.\n *\n * This is the FAITHFUL encode: it publishes `kind` exactly as the error declares it. The\n * boundary rule that a caller-local `ApiConnectionError` publishes as `implementation` belongs\n * to {@link ApiErrorBoundary}, which every protocol edge uses instead of calling this directly.\n */\n // webpieces-disable no-function-outside-class -- stateless public transport codec\n static encode(error: Error): ApiErrorPayload {\n return this.encodeDepth(error, 0);\n }\n\n // webpieces-disable no-function-outside-class -- recursive bounded transport codec\n private static encodeDepth(error: Error, depth: number): ApiErrorPayload {\n const kind = error instanceof ApiError ? error.kind : 'implementation';\n const payload = new ApiErrorPayload(kind, this.publicMessage(error, kind));\n if (error instanceof ApiError) payload.subType = this.text(error.subType);\n if (error instanceof ApiEndUserError) {\n payload.errorCode = this.text(error.errorCode);\n payload.edgeHttpStatus = error.edgeHttpStatus;\n }\n if (error instanceof ApiCodedError) {\n payload.statusCode = error.statusCode;\n payload.errorCode = this.text(error.errorCode);\n }\n if (error instanceof ApiBadRequestError) {\n payload.field = this.text(error.field);\n payload.callerMessage = this.text(error.callerMessage);\n }\n if (error instanceof ApiDependencyBackoffError) {\n payload.retryAfterSeconds = this.wait(error.retryAfterSeconds);\n }\n if (depth < 3) {\n // `Error.cause` is `unknown` by construction, so the recursion narrows it with `toError`\n // exactly as a catch block would — and then deals only in `Error` like everything else.\n const cause = Object.getOwnPropertyDescriptor(error, 'cause')?.value;\n if (cause !== undefined && cause !== null) {\n payload.cause = this.encodeDepth(toError(cause), depth + 1);\n }\n }\n return payload;\n }\n\n /** Decode a value received from another process. Implementation failures are marked remote. */\n // webpieces-disable no-function-outside-class -- stateless public transport codec; webpieces-disable no-any-unknown -- wire values require validation\n static decode(value: unknown): ApiError {\n return this.decodeDepth(value, 0);\n }\n\n /** Whether a body carries a recognized Webpieces semantic discriminator. */\n // webpieces-disable no-function-outside-class -- stateless wire discriminator check; webpieces-disable no-any-unknown -- wire values require validation\n static isPayload(value: unknown): boolean {\n if (typeof value !== 'object' || value === null) return false;\n const kind = Object.getOwnPropertyDescriptor(value, 'kind')?.value;\n return typeof kind === 'string' && this.kinds.has(kind as ApiErrorKind);\n }\n\n // webpieces-disable no-function-outside-class -- recursive bounded transport codec; webpieces-disable no-any-unknown -- remote causes require validation\n private static decodeDepth(value: unknown, depth: number): ApiError {\n const error = this.decodeOne(value);\n if (typeof value === 'object' && value !== null && depth < 3) {\n const cause = Object.getOwnPropertyDescriptor(value, 'cause')?.value;\n if (typeof cause === 'object' && cause !== null) {\n // webpieces-disable no-anonymous-object-literals -- standard descriptor preserves Error.cause semantics\n Object.defineProperty(error, 'cause', {\n value: this.decodeDepth(cause, depth + 1),\n configurable: true,\n writable: true,\n });\n }\n }\n return error;\n }\n\n // webpieces-disable no-function-outside-class -- allowlisted wire codec; webpieces-disable no-any-unknown -- wire fields require validation\n private static decodeOne(value: unknown): ApiError {\n if (typeof value !== 'object' || value === null) return this.remoteImplementationError();\n // webpieces-disable no-any-unknown -- own wire fields remain untrusted until narrowed\n const field = (key: string): unknown => Object.getOwnPropertyDescriptor(value, key)?.value;\n const kind = field('kind');\n const message = this.text(field('message')) ?? this.message(kind);\n switch (kind) {\n case 'end-user':\n return new ApiEndUserError(\n message,\n this.text(field('errorCode')),\n this.edgeStatus(field('edgeHttpStatus')),\n );\n case 'bad-request':\n return new ApiBadRequestError(\n message,\n this.text(field('field')),\n this.text(field('callerMessage')),\n );\n case 'unauthorized':\n return new ApiUnauthorizedError(message, this.text(field('subType')));\n case 'forbidden':\n return new ApiForbiddenError(message);\n case 'not-found':\n return new ApiNotFoundError(message);\n case 'endpoint-not-found':\n return new ApiEndpointNotFoundError(message);\n case 'request-timeout':\n return new ApiRequestTimeoutError(message);\n case 'rate-limited':\n return new ApiRateLimitedError(message);\n case 'conflict':\n return new ApiConflictError(message);\n case 'unprocessable':\n return new ApiUnprocessableError(message);\n case 'precondition-failed':\n return new ApiPreconditionFailedError(message);\n case 'unsupported-media-type':\n return new ApiUnsupportedMediaTypeError(message);\n case 'not-implemented':\n return new ApiNotImplementedError(message);\n case 'coded':\n return this.coded(message, field('statusCode'), field('errorCode'));\n case 'dependency':\n return new ApiDependencyError(message);\n case 'bad-gateway':\n return new ApiBadGatewayError(message);\n case 'unavailable':\n return new ApiUnavailableError(message);\n case 'dependency-timeout':\n return new ApiDependencyTimeoutError(message);\n case 'dependency-backoff':\n return new ApiDependencyBackoffError(\n message,\n this.wait(field('retryAfterSeconds')),\n );\n case 'connection':\n return new ApiConnectionError(message);\n case 'implementation':\n return new ApiImplementationError(message, undefined, true);\n default:\n return this.remoteImplementationError();\n }\n }\n\n // webpieces-disable no-function-outside-class -- coded wire decode; webpieces-disable no-any-unknown -- wire fields require validation\n private static coded(message: string, statusCode: unknown, errorCode: unknown): ApiError {\n if (!ApiCodedError.isStatusCode(statusCode)) return this.remoteImplementationError();\n return new ApiCodedError(message, statusCode, this.text(errorCode));\n }\n\n // webpieces-disable no-function-outside-class -- concrete normalization for malformed remote errors\n private static remoteImplementationError(): ApiImplementationError {\n return new ApiImplementationError('Internal Error', undefined, true);\n }\n\n // webpieces-disable no-function-outside-class -- caller-safe message selection\n private static publicMessage(error: Error, kind: ApiErrorKind): string {\n if (error instanceof ApiEndUserError) return error.message.slice(0, 4096);\n return this.message(kind);\n }\n\n // webpieces-disable no-function-outside-class -- wire string bound; webpieces-disable no-any-unknown -- wire values require validation\n private static text(value: unknown): string | undefined {\n return typeof value === 'string' ? value.slice(0, 4096) : undefined;\n }\n\n /** Tolerant: a peer that sends nothing, or a status outside {@link EdgeHttpStatus}, decodes as undefined. */\n // webpieces-disable no-function-outside-class -- wire status bound; webpieces-disable no-any-unknown -- wire values require validation\n private static edgeStatus(value: unknown): EdgeHttpStatus | undefined {\n return ApiEndUserError.isEdgeHttpStatus(value) ? value : undefined;\n }\n\n // webpieces-disable no-function-outside-class -- wire number bound; webpieces-disable no-any-unknown -- wire values require validation\n private static wait(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0\n ? Math.min(value, 86400)\n : undefined;\n }\n\n // webpieces-disable no-function-outside-class -- allowlisted generic messages; webpieces-disable no-any-unknown -- discriminator is validated by switch\n private static message(kind: unknown): string {\n switch (kind) {\n case 'end-user':\n return 'End User Error';\n case 'bad-request':\n return 'Bad Request';\n case 'unauthorized':\n return 'Unauthorized';\n case 'forbidden':\n return 'Forbidden';\n case 'not-found':\n return 'Not Found';\n case 'endpoint-not-found':\n return 'Endpoint Not Found';\n case 'request-timeout':\n return 'Request Timeout';\n case 'rate-limited':\n return 'Rate Limited';\n case 'conflict':\n return 'Conflict';\n case 'unprocessable':\n return 'Unprocessable Content';\n case 'precondition-failed':\n return 'Precondition Failed';\n case 'unsupported-media-type':\n return 'Unsupported Media Type';\n case 'not-implemented':\n return 'Not Implemented';\n case 'coded':\n return 'Request Failed';\n case 'dependency':\n return 'Dependency Error';\n case 'bad-gateway':\n return 'Bad Gateway';\n case 'unavailable':\n return 'Service Unavailable';\n case 'dependency-timeout':\n return 'Dependency Timeout';\n case 'dependency-backoff':\n return 'Dependency Unavailable';\n case 'connection':\n return 'Connection Error';\n default:\n return 'Internal Error';\n }\n }\n\n private static readonly kinds: ReadonlySet<ApiErrorKind> = new Set<ApiErrorKind>([\n 'end-user',\n 'bad-request',\n 'unauthorized',\n 'forbidden',\n 'not-found',\n 'endpoint-not-found',\n 'request-timeout',\n 'rate-limited',\n 'conflict',\n 'unprocessable',\n 'precondition-failed',\n 'unsupported-media-type',\n 'not-implemented',\n 'coded',\n 'implementation',\n 'dependency',\n 'bad-gateway',\n 'unavailable',\n 'dependency-timeout',\n 'dependency-backoff',\n 'connection',\n ]);\n}\n"]}
1
+ {"version":3,"file":"ApiErrorCodec.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/errors/ApiErrorCodec.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ApiBadRequestError,\n ApiBadGatewayError,\n ApiClientTooOldError,\n ApiCodedError,\n ApiConflictError,\n ApiConnectionError,\n ApiDependencyBackoffError,\n ApiDependencyError,\n ApiDependencyTimeoutError,\n ApiEndpointNotFoundError,\n ApiEndUserError,\n ApiError,\n ApiErrorKind,\n ApiForbiddenError,\n ApiImplementationError,\n ApiNotFoundError,\n ApiNotImplementedError,\n ApiPreconditionFailedError,\n ApiRateLimitedError,\n ApiRequestTimeoutError,\n ApiUnauthorizedError,\n ApiUnavailableError,\n ApiUnprocessableError,\n ApiUnsupportedMediaTypeError,\n EdgeHttpStatus,\n} from './ApiError';\nimport { toError } from '../lib/errorUtils';\nimport { EndUserErrorRegistry } from './EndUserErrorRegistry';\n\n/** Allowlisted transport-neutral envelope. It never contains stacks or arbitrary properties. */\nexport class ApiErrorPayload {\n public cause?: ApiErrorPayload;\n public subType?: string;\n public field?: string;\n public callerMessage?: string;\n public errorCode?: string;\n public retryAfterSeconds?: number;\n public statusCode?: number;\n /** {@link ApiEndUserError.edgeHttpStatus}; absent when the thrower (or an older peer) set none. */\n public edgeHttpStatus?: number;\n\n constructor(\n public kind: string = 'implementation',\n public message: string = 'Internal Error',\n ) {}\n}\n\n/** Bounded safe codec shared by HTTP, IPC, and other remote adapters. */\nexport class ApiErrorCodec {\n /**\n * Encode a thrown error faithfully. The input is an `Error`, never `unknown`: every caller in\n * the framework has already narrowed with `toError` at its catch, and the `unknown` this used to\n * advertise leaked out of the PRIVATE `.cause` recursion below (`Error.cause` is typed `unknown`\n * in TypeScript), which is an internal concern.\n *\n * This is the FAITHFUL encode: it publishes `kind` exactly as the error declares it. The\n * boundary rule that a caller-local `ApiConnectionError` publishes as `implementation` belongs\n * to {@link ApiErrorBoundary}, which every protocol edge uses instead of calling this directly.\n */\n // webpieces-disable no-function-outside-class -- stateless public transport codec\n static encode(error: Error): ApiErrorPayload {\n return this.encodeDepth(error, 0);\n }\n\n // webpieces-disable no-function-outside-class -- recursive bounded transport codec\n private static encodeDepth(error: Error, depth: number): ApiErrorPayload {\n const kind = error instanceof ApiError ? error.kind : 'implementation';\n const payload = new ApiErrorPayload(kind, this.publicMessage(error, kind));\n if (error instanceof ApiError) payload.subType = this.text(error.subType);\n if (error instanceof ApiEndUserError) {\n payload.errorCode = this.text(error.errorCode);\n payload.edgeHttpStatus = error.edgeHttpStatus;\n }\n if (error instanceof ApiCodedError) {\n payload.statusCode = error.statusCode;\n payload.errorCode = this.text(error.errorCode);\n }\n if (error instanceof ApiBadRequestError) {\n payload.field = this.text(error.field);\n payload.callerMessage = this.text(error.callerMessage);\n }\n if (error instanceof ApiDependencyBackoffError) {\n payload.retryAfterSeconds = this.wait(error.retryAfterSeconds);\n }\n if (depth < 3) {\n // `Error.cause` is `unknown` by construction, so the recursion narrows it with `toError`\n // exactly as a catch block would — and then deals only in `Error` like everything else.\n const cause = Object.getOwnPropertyDescriptor(error, 'cause')?.value;\n if (cause !== undefined && cause !== null) {\n payload.cause = this.encodeDepth(toError(cause), depth + 1);\n }\n }\n return payload;\n }\n\n /** Decode a value received from another process. Implementation failures are marked remote. */\n // webpieces-disable no-function-outside-class -- stateless public transport codec; webpieces-disable no-any-unknown -- wire values require validation\n static decode(value: unknown): ApiError {\n return this.decodeDepth(value, 0);\n }\n\n /** Whether a body carries a recognized Webpieces semantic discriminator. */\n // webpieces-disable no-function-outside-class -- stateless wire discriminator check; webpieces-disable no-any-unknown -- wire values require validation\n static isPayload(value: unknown): boolean {\n if (typeof value !== 'object' || value === null) return false;\n const kind = Object.getOwnPropertyDescriptor(value, 'kind')?.value;\n return typeof kind === 'string' && this.kinds.has(kind as ApiErrorKind);\n }\n\n // webpieces-disable no-function-outside-class -- recursive bounded transport codec; webpieces-disable no-any-unknown -- remote causes require validation\n private static decodeDepth(value: unknown, depth: number): ApiError {\n const error = this.decodeOne(value);\n if (typeof value === 'object' && value !== null && depth < 3) {\n const cause = Object.getOwnPropertyDescriptor(value, 'cause')?.value;\n if (typeof cause === 'object' && cause !== null) {\n // webpieces-disable no-anonymous-object-literals -- standard descriptor preserves Error.cause semantics\n Object.defineProperty(error, 'cause', {\n value: this.decodeDepth(cause, depth + 1),\n configurable: true,\n writable: true,\n });\n }\n }\n return error;\n }\n\n // webpieces-disable no-function-outside-class -- allowlisted wire codec; webpieces-disable no-any-unknown -- wire fields require validation\n private static decodeOne(value: unknown): ApiError {\n if (typeof value !== 'object' || value === null) return this.remoteImplementationError();\n // webpieces-disable no-any-unknown -- own wire fields remain untrusted until narrowed\n const field = (key: string): unknown => Object.getOwnPropertyDescriptor(value, key)?.value;\n const kind = field('kind');\n const message = this.text(field('message')) ?? this.message(kind);\n switch (kind) {\n case 'end-user':\n // The registered SUBCLASS for this errorCode, else the base type with the code intact.\n return EndUserErrorRegistry.create(\n message,\n this.text(field('errorCode')),\n this.edgeStatus(field('edgeHttpStatus')),\n );\n case 'bad-request':\n return new ApiBadRequestError(\n message,\n this.text(field('field')),\n this.text(field('callerMessage')),\n );\n case 'unauthorized':\n return new ApiUnauthorizedError(message, this.text(field('subType')));\n case 'forbidden':\n return new ApiForbiddenError(message);\n case 'not-found':\n return new ApiNotFoundError(message);\n case 'endpoint-not-found':\n return new ApiEndpointNotFoundError(message);\n case 'request-timeout':\n return new ApiRequestTimeoutError(message);\n case 'rate-limited':\n return new ApiRateLimitedError(message);\n case 'conflict':\n return new ApiConflictError(message);\n case 'unprocessable':\n return new ApiUnprocessableError(message);\n case 'precondition-failed':\n return new ApiPreconditionFailedError(message);\n case 'unsupported-media-type':\n return new ApiUnsupportedMediaTypeError(message);\n case 'not-implemented':\n return new ApiNotImplementedError(message);\n case 'coded':\n return this.coded(message, field('statusCode'), field('errorCode'));\n case 'dependency':\n return new ApiDependencyError(message);\n case 'bad-gateway':\n return new ApiBadGatewayError(message);\n case 'unavailable':\n return new ApiUnavailableError(message);\n case 'dependency-timeout':\n return new ApiDependencyTimeoutError(message);\n case 'dependency-backoff':\n return new ApiDependencyBackoffError(\n message,\n this.wait(field('retryAfterSeconds')),\n );\n case 'connection':\n return new ApiConnectionError(message);\n case 'implementation':\n return new ApiImplementationError(message, undefined, true);\n default:\n return this.remoteImplementationError();\n }\n }\n\n // webpieces-disable no-function-outside-class -- coded wire decode; webpieces-disable no-any-unknown -- wire fields require validation\n private static coded(message: string, statusCode: unknown, errorCode: unknown): ApiError {\n if (!ApiCodedError.isStatusCode(statusCode)) return this.remoteImplementationError();\n const code = this.text(errorCode);\n // The one coded failure webpieces names: rebuilt as its own type so a receiver sees\n // \"upgrade\", never a generic coded 4xx (issue #1172).\n if (ApiClientTooOldError.matches(statusCode, code))\n return new ApiClientTooOldError(message);\n return new ApiCodedError(message, statusCode, code);\n }\n\n // webpieces-disable no-function-outside-class -- concrete normalization for malformed remote errors\n private static remoteImplementationError(): ApiImplementationError {\n return new ApiImplementationError('Internal Error', undefined, true);\n }\n\n // webpieces-disable no-function-outside-class -- caller-safe message selection\n private static publicMessage(error: Error, kind: ApiErrorKind): string {\n if (error instanceof ApiEndUserError) return error.message.slice(0, 4096);\n return this.message(kind);\n }\n\n // webpieces-disable no-function-outside-class -- wire string bound; webpieces-disable no-any-unknown -- wire values require validation\n private static text(value: unknown): string | undefined {\n return typeof value === 'string' ? value.slice(0, 4096) : undefined;\n }\n\n /** Tolerant: a peer that sends nothing, or a status outside {@link EdgeHttpStatus}, decodes as undefined. */\n // webpieces-disable no-function-outside-class -- wire status bound; webpieces-disable no-any-unknown -- wire values require validation\n private static edgeStatus(value: unknown): EdgeHttpStatus | undefined {\n return ApiEndUserError.isEdgeHttpStatus(value) ? value : undefined;\n }\n\n // webpieces-disable no-function-outside-class -- wire number bound; webpieces-disable no-any-unknown -- wire values require validation\n private static wait(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0\n ? Math.min(value, 86400)\n : undefined;\n }\n\n // webpieces-disable no-function-outside-class -- allowlisted generic messages; webpieces-disable no-any-unknown -- discriminator is validated by switch\n private static message(kind: unknown): string {\n switch (kind) {\n case 'end-user':\n return 'End User Error';\n case 'bad-request':\n return 'Bad Request';\n case 'unauthorized':\n return 'Unauthorized';\n case 'forbidden':\n return 'Forbidden';\n case 'not-found':\n return 'Not Found';\n case 'endpoint-not-found':\n return 'Endpoint Not Found';\n case 'request-timeout':\n return 'Request Timeout';\n case 'rate-limited':\n return 'Rate Limited';\n case 'conflict':\n return 'Conflict';\n case 'unprocessable':\n return 'Unprocessable Content';\n case 'precondition-failed':\n return 'Precondition Failed';\n case 'unsupported-media-type':\n return 'Unsupported Media Type';\n case 'not-implemented':\n return 'Not Implemented';\n case 'coded':\n return 'Request Failed';\n case 'dependency':\n return 'Dependency Error';\n case 'bad-gateway':\n return 'Bad Gateway';\n case 'unavailable':\n return 'Service Unavailable';\n case 'dependency-timeout':\n return 'Dependency Timeout';\n case 'dependency-backoff':\n return 'Dependency Unavailable';\n case 'connection':\n return 'Connection Error';\n default:\n return 'Internal Error';\n }\n }\n\n private static readonly kinds: ReadonlySet<ApiErrorKind> = new Set<ApiErrorKind>([\n 'end-user',\n 'bad-request',\n 'unauthorized',\n 'forbidden',\n 'not-found',\n 'endpoint-not-found',\n 'request-timeout',\n 'rate-limited',\n 'conflict',\n 'unprocessable',\n 'precondition-failed',\n 'unsupported-media-type',\n 'not-implemented',\n 'coded',\n 'implementation',\n 'dependency',\n 'bad-gateway',\n 'unavailable',\n 'dependency-timeout',\n 'dependency-backoff',\n 'connection',\n ]);\n}\n"]}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * WHO is receiving an error: the one fact {@link ReceivedApiErrorRule} cannot read off the wire.
3
+ *
4
+ * Every client declares it at SETUP, explicitly, and there is no default (issue #1173): a node
5
+ * `ClientConfig`, a browser `ClientConfig`, an `IpcClientFactory`. It decides exactly one thing today,
6
+ * what a received **401 Unauthorized** means, and the two answers have opposite owners:
7
+ *
8
+ * - {@link ClientRole.END_USER_CLIENT} — a browser bundle, an Expo shell, a remote MCP client. The
9
+ * credential it presented IS the user's session, so a 401 means "log in again": it is decoded as
10
+ * `ApiUnauthorizedError`, the type the app's re-login path catches.
11
+ * - {@link ClientRole.SERVER} — a server calling another server. The credential it presented is ITS
12
+ * OWN (a service token, a shared secret), so a 401 is this service's credential or configuration
13
+ * bug: `ApiImplementationError`, a 500 to its own caller. Relaying the 401 would tell the browser
14
+ * "your session expired", log the user out for a backend misconfiguration, and stop paging the team
15
+ * that owns the broken credential.
16
+ *
17
+ * A server that rejects the INCOMING user's token throws `ApiUnauthorizedError` itself; that is a 401
18
+ * it ORIGINATES, and the end-user client then decodes it typed.
19
+ *
20
+ * Why no default: the safe-looking choice is different for each half of the estate (SERVER is right
21
+ * for a backend, wrong for a browser), so any default would be silently wrong for one of them. Stating
22
+ * it costs one argument at each client's setup.
23
+ */
24
+ export declare enum ClientRole {
25
+ END_USER_CLIENT = "end-user-client",
26
+ SERVER = "server"
27
+ }
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ClientRole = void 0;
4
+ /**
5
+ * WHO is receiving an error: the one fact {@link ReceivedApiErrorRule} cannot read off the wire.
6
+ *
7
+ * Every client declares it at SETUP, explicitly, and there is no default (issue #1173): a node
8
+ * `ClientConfig`, a browser `ClientConfig`, an `IpcClientFactory`. It decides exactly one thing today,
9
+ * what a received **401 Unauthorized** means, and the two answers have opposite owners:
10
+ *
11
+ * - {@link ClientRole.END_USER_CLIENT} — a browser bundle, an Expo shell, a remote MCP client. The
12
+ * credential it presented IS the user's session, so a 401 means "log in again": it is decoded as
13
+ * `ApiUnauthorizedError`, the type the app's re-login path catches.
14
+ * - {@link ClientRole.SERVER} — a server calling another server. The credential it presented is ITS
15
+ * OWN (a service token, a shared secret), so a 401 is this service's credential or configuration
16
+ * bug: `ApiImplementationError`, a 500 to its own caller. Relaying the 401 would tell the browser
17
+ * "your session expired", log the user out for a backend misconfiguration, and stop paging the team
18
+ * that owns the broken credential.
19
+ *
20
+ * A server that rejects the INCOMING user's token throws `ApiUnauthorizedError` itself; that is a 401
21
+ * it ORIGINATES, and the end-user client then decodes it typed.
22
+ *
23
+ * Why no default: the safe-looking choice is different for each half of the estate (SERVER is right
24
+ * for a backend, wrong for a browser), so any default would be silently wrong for one of them. Stating
25
+ * it costs one argument at each client's setup.
26
+ */
27
+ var ClientRole;
28
+ (function (ClientRole) {
29
+ ClientRole["END_USER_CLIENT"] = "end-user-client";
30
+ ClientRole["SERVER"] = "server";
31
+ })(ClientRole || (exports.ClientRole = ClientRole = {}));
32
+ //# sourceMappingURL=ClientRole.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClientRole.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/errors/ClientRole.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,IAAY,UAGX;AAHD,WAAY,UAAU;IAClB,iDAAmC,CAAA;IACnC,+BAAiB,CAAA;AACrB,CAAC,EAHW,UAAU,0BAAV,UAAU,QAGrB","sourcesContent":["/**\n * WHO is receiving an error: the one fact {@link ReceivedApiErrorRule} cannot read off the wire.\n *\n * Every client declares it at SETUP, explicitly, and there is no default (issue #1173): a node\n * `ClientConfig`, a browser `ClientConfig`, an `IpcClientFactory`. It decides exactly one thing today,\n * what a received **401 Unauthorized** means, and the two answers have opposite owners:\n *\n * - {@link ClientRole.END_USER_CLIENT} — a browser bundle, an Expo shell, a remote MCP client. The\n * credential it presented IS the user's session, so a 401 means \"log in again\": it is decoded as\n * `ApiUnauthorizedError`, the type the app's re-login path catches.\n * - {@link ClientRole.SERVER} — a server calling another server. The credential it presented is ITS\n * OWN (a service token, a shared secret), so a 401 is this service's credential or configuration\n * bug: `ApiImplementationError`, a 500 to its own caller. Relaying the 401 would tell the browser\n * \"your session expired\", log the user out for a backend misconfiguration, and stop paging the team\n * that owns the broken credential.\n *\n * A server that rejects the INCOMING user's token throws `ApiUnauthorizedError` itself; that is a 401\n * it ORIGINATES, and the end-user client then decodes it typed.\n *\n * Why no default: the safe-looking choice is different for each half of the estate (SERVER is right\n * for a backend, wrong for a browser), so any default would be silently wrong for one of them. Stating\n * it costs one argument at each client's setup.\n */\nexport enum ClientRole {\n END_USER_CLIENT = 'end-user-client',\n SERVER = 'server',\n}\n"]}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The grammar of `ApiEndUserError.errorCode`: `<code>[:<detail>]`.
3
+ *
4
+ * - `<code>` names WHICH mistake it was (`user-not-found`, `terms-not-accepted`). It is what
5
+ * {@link EndUserErrorRegistry} matches on to rebuild the subclass on the receiving side, so it must
6
+ * not itself contain a `:`.
7
+ * - `<detail>` is optional DATA the client acts on (the id that was not found, the terms version to
8
+ * accept), never prose: prose belongs in the message. Everything after the FIRST `:` is the detail,
9
+ * so a detail may itself contain colons.
10
+ *
11
+ * Kept as one tiny stateless class so a throw site, the registry and an app's own client code all
12
+ * split the string the same way.
13
+ */
14
+ export declare class EndUserErrorCode {
15
+ static readonly SEPARATOR = ":";
16
+ /** `code` alone, or `code:detail` when a detail is given. */
17
+ static format(code: string, detail?: string): string;
18
+ /** The `<code>` part: everything before the first `:`, or the whole string when there is none. */
19
+ static codeOf(errorCode: string): string;
20
+ /** The `<detail>` part: everything after the first `:`, or undefined when there is none. */
21
+ static detailOf(errorCode: string): string | undefined;
22
+ }