@webpieces/core-util 0.4.933 → 0.4.935
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/package.json +1 -1
- package/src/errors/ApiError.d.ts +8 -0
- package/src/errors/ApiError.js.map +1 -1
- package/src/errors/CallMode.d.ts +41 -0
- package/src/errors/CallMode.js +70 -0
- package/src/errors/CallMode.js.map +1 -0
- package/src/errors/index.d.ts +1 -0
- package/src/errors/index.js +4 -1
- package/src/errors/index.js.map +1 -1
- package/src/http/RouteMetadata.d.ts +13 -0
- package/src/http/RouteMetadata.js +22 -0
- package/src/http/RouteMetadata.js.map +1 -1
- package/src/http/api-type.js +2 -2
- package/src/http/api-type.js.map +1 -1
- package/src/index.d.ts +2 -1
- package/src/index.js +7 -3
- package/src/index.js.map +1 -1
- package/src/mcp/McpCompileAssertions.js +19 -9
- package/src/mcp/McpCompileAssertions.js.map +1 -1
- package/src/mcp/McpMetadata.d.ts +22 -22
- package/src/mcp/McpMetadata.js +29 -30
- package/src/mcp/McpMetadata.js.map +1 -1
- package/src/mcp/McpToolCatalogFile.d.ts +16 -8
- package/src/mcp/McpToolCatalogFile.js +22 -7
- package/src/mcp/McpToolCatalogFile.js.map +1 -1
- package/src/mcp/McpToolName.d.ts +22 -0
- package/src/mcp/McpToolName.js +45 -0
- package/src/mcp/McpToolName.js.map +1 -0
package/README.md
CHANGED
|
@@ -156,8 +156,10 @@ Apache-2.0
|
|
|
156
156
|
|
|
157
157
|
### MCP profile membership
|
|
158
158
|
|
|
159
|
-
`Mcp.DEFAULT` is the reserved base tool group. `@WpMcpTool(name
|
|
160
|
-
`profiles` option select only that group.
|
|
159
|
+
`Mcp.DEFAULT` is the reserved base tool group. `@WpMcpTool(name)` and an omitted
|
|
160
|
+
`profiles` option select only that group. The decorator takes the stable tool name only: the tool's
|
|
161
|
+
title is the method's `@summary` JSDoc tag (#1296), and a name must be 1-64 characters of
|
|
162
|
+
`[A-Za-z0-9_-]` (`McpToolName`) or the decorator throws. `{ profiles: ['admin'] }` selects only admin;
|
|
161
163
|
`{ profiles: [Mcp.DEFAULT, 'admin'] }` explicitly shares a tool between both groups. Membership is
|
|
162
164
|
independent of `@WpAuthorization` roles and never grants account permissions. Identifiers match
|
|
163
165
|
`[a-z][a-z0-9-]{0,63}`; empty lists, duplicates and malformed options are rejected.
|
package/package.json
CHANGED
package/src/errors/ApiError.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ApiCallSite } from './ApiCallSite';
|
|
2
|
+
import { CallMode } from './CallMode';
|
|
2
3
|
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' | 'retries-exhausted' | 'connection';
|
|
3
4
|
/** Category for portable API failures. Concrete subclasses alone define protocol mappings. */
|
|
4
5
|
export declare abstract class ApiError extends Error {
|
|
@@ -12,6 +13,13 @@ export declare abstract class ApiError extends Error {
|
|
|
12
13
|
* no `callSite` key at all. See {@link ApiCallSite} for why it is not `cause`.
|
|
13
14
|
*/
|
|
14
15
|
readonly callSite?: ApiCallSite;
|
|
16
|
+
/**
|
|
17
|
+
* Whether the user was waiting on the generated client call that rejected with this error
|
|
18
|
+
* (#1293) — `CallMode.BACKGROUND` for a client built with a background `ClientConfig`. Set by
|
|
19
|
+
* {@link CallModeMarker.attachTo} as a NON-ENUMERABLE own property, exactly like
|
|
20
|
+
* {@link callSite}; an error that never failed a client call carries no `callMode` key.
|
|
21
|
+
*/
|
|
22
|
+
readonly callMode?: CallMode;
|
|
15
23
|
constructor(message: string, cause?: Error);
|
|
16
24
|
}
|
|
17
25
|
export declare class ApiNotFoundError extends ApiError {
|
|
@@ -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":["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 | 'retries-exhausted'\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/**\n * A `RetryStrategy` retried a TRANSIENT failure and ran out of attempts. Thrown ONLY by the strategy,\n * and only when it actually retried — a single attempt rethrows its own error unchanged. `cause` is\n * the LAST attempt's error.\n *\n * Classified `CallOutcome.FAILURE`, never transient: that is the anti-amplification property. Retries\n * happen at ONE layer; without it, 4 attempts x 4 attempts x 4 attempts across three hops is 64 calls\n * into a struggling dependency. For the same reason it publishes HTTP 502 (Bad Gateway: \"the thing\n * behind me failed\"), not 503 — generic clients and load balancers retry a 503.\n *\n * A webpieces peer decodes `kind: 'retries-exhausted'` back into this class (see\n * {@link ApiRetriesExhaustedError.received}), so the upstream classifies it FAILURE and does not\n * retry; a foreign caller just sees a 502.\n */\nexport class ApiRetriesExhaustedError extends ApiDependencyError {\n override readonly kind = 'retries-exhausted' as const;\n\n constructor(\n public readonly attempts: number,\n public readonly lastError: Error,\n ) {\n super(`gave up after ${attempts} attempts: ${lastError.message}`, lastError);\n }\n\n /**\n * The error as DECODED from a peer's wire payload. The peer published neither its attempt count\n * nor its last error (an operator message never crosses the wire), so `attempts` is 0 — \"unknown,\n * a peer gave up\" — and `lastError` carries the published message.\n */\n // webpieces-disable no-function-outside-class -- wire-decode factory owned by this error\n static received(message: string): ApiRetriesExhaustedError {\n const error = new ApiRetriesExhaustedError(0, new ApiDependencyError(message));\n error.message = message;\n return error;\n }\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"]}
|
|
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';\nimport { CallMode } from './CallMode';\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 | 'retries-exhausted'\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 /**\n * Whether the user was waiting on the generated client call that rejected with this error\n * (#1293) — `CallMode.BACKGROUND` for a client built with a background `ClientConfig`. Set by\n * {@link CallModeMarker.attachTo} as a NON-ENUMERABLE own property, exactly like\n * {@link callSite}; an error that never failed a client call carries no `callMode` key.\n */\n declare readonly callMode?: CallMode;\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/**\n * A `RetryStrategy` retried a TRANSIENT failure and ran out of attempts. Thrown ONLY by the strategy,\n * and only when it actually retried — a single attempt rethrows its own error unchanged. `cause` is\n * the LAST attempt's error.\n *\n * Classified `CallOutcome.FAILURE`, never transient: that is the anti-amplification property. Retries\n * happen at ONE layer; without it, 4 attempts x 4 attempts x 4 attempts across three hops is 64 calls\n * into a struggling dependency. For the same reason it publishes HTTP 502 (Bad Gateway: \"the thing\n * behind me failed\"), not 503 — generic clients and load balancers retry a 503.\n *\n * A webpieces peer decodes `kind: 'retries-exhausted'` back into this class (see\n * {@link ApiRetriesExhaustedError.received}), so the upstream classifies it FAILURE and does not\n * retry; a foreign caller just sees a 502.\n */\nexport class ApiRetriesExhaustedError extends ApiDependencyError {\n override readonly kind = 'retries-exhausted' as const;\n\n constructor(\n public readonly attempts: number,\n public readonly lastError: Error,\n ) {\n super(`gave up after ${attempts} attempts: ${lastError.message}`, lastError);\n }\n\n /**\n * The error as DECODED from a peer's wire payload. The peer published neither its attempt count\n * nor its last error (an operator message never crosses the wire), so `attempts` is 0 — \"unknown,\n * a peer gave up\" — and `lastError` carries the published message.\n */\n // webpieces-disable no-function-outside-class -- wire-decode factory owned by this error\n static received(message: string): ApiRetriesExhaustedError {\n const error = new ApiRetriesExhaustedError(0, new ApiDependencyError(message));\n error.message = message;\n return error;\n }\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"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether the USER is waiting on one api client call (#1293).
|
|
3
|
+
*
|
|
4
|
+
* WHY per CALL and not per endpoint: the same `listResumeItems` is FOREGROUND when the user clicks
|
|
5
|
+
* Refresh and BACKGROUND when a timer polls it. `@Endpoint(..., { hideProgress: true })` is a fact
|
|
6
|
+
* about the ROUTE; this is a fact about the CALL SITE, so it is chosen where the client is built —
|
|
7
|
+
* a browser app builds a second client for the same contract with
|
|
8
|
+
* `new ClientConfig(svcName, role, CallMode.BACKGROUND)`.
|
|
9
|
+
*
|
|
10
|
+
* A failed call carries it as `err.callMode` (see {@link CallModeMarker}), so an app's global error
|
|
11
|
+
* handler can decide "log + report, no dialog" for a background failure with no app-side bookkeeping.
|
|
12
|
+
* The error's CLASS never changes — every `instanceof` check keeps working.
|
|
13
|
+
*/
|
|
14
|
+
export declare enum CallMode {
|
|
15
|
+
/** The user is waiting on this call: show progress, and surface its failure. */
|
|
16
|
+
FOREGROUND = "FOREGROUND",
|
|
17
|
+
/** Nobody is waiting (a poll, a prefetch, a sync): no progress UI, and a failure is not a dialog. */
|
|
18
|
+
BACKGROUND = "BACKGROUND"
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Parks a {@link CallMode} on the value a generated client call rejected with, and reads it back —
|
|
22
|
+
* the same mechanism, for the same reasons, as `ApiCallSite`: a NON-ENUMERABLE own property, so it
|
|
23
|
+
* never reaches JSON, a structured log record or an error reporter's extra data by accident, and the
|
|
24
|
+
* SAME instance is rethrown so the error's class is untouched.
|
|
25
|
+
*
|
|
26
|
+
* It is typed on `ApiError` (`err.callMode`); {@link CallModeMarker.of} reads it off any thrown value,
|
|
27
|
+
* because an app's own `ErrorTranslator` may reject with a type that is not an `ApiError`.
|
|
28
|
+
*/
|
|
29
|
+
export declare class CallModeMarker {
|
|
30
|
+
/** The property name the call mode is parked under on a rejected error. */
|
|
31
|
+
static readonly PROPERTY = "callMode";
|
|
32
|
+
/**
|
|
33
|
+
* Park `mode` on `failure` WITHOUT changing it. Never clobbers: a failure that already carries a
|
|
34
|
+
* mode keeps the innermost (the call that actually failed), exactly as `ApiCallSite.attachTo`
|
|
35
|
+
* does. A non-`Error` or a frozen/sealed error is left untouched — attaching is a marker and must
|
|
36
|
+
* never itself throw.
|
|
37
|
+
*/
|
|
38
|
+
static attachTo(failure: unknown, mode: CallMode): void;
|
|
39
|
+
/** The mode a generated client parked on `failure`, or undefined when no client call rejected with it. */
|
|
40
|
+
static of(failure: unknown): CallMode | undefined;
|
|
41
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CallModeMarker = exports.CallMode = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Whether the USER is waiting on one api client call (#1293).
|
|
6
|
+
*
|
|
7
|
+
* WHY per CALL and not per endpoint: the same `listResumeItems` is FOREGROUND when the user clicks
|
|
8
|
+
* Refresh and BACKGROUND when a timer polls it. `@Endpoint(..., { hideProgress: true })` is a fact
|
|
9
|
+
* about the ROUTE; this is a fact about the CALL SITE, so it is chosen where the client is built —
|
|
10
|
+
* a browser app builds a second client for the same contract with
|
|
11
|
+
* `new ClientConfig(svcName, role, CallMode.BACKGROUND)`.
|
|
12
|
+
*
|
|
13
|
+
* A failed call carries it as `err.callMode` (see {@link CallModeMarker}), so an app's global error
|
|
14
|
+
* handler can decide "log + report, no dialog" for a background failure with no app-side bookkeeping.
|
|
15
|
+
* The error's CLASS never changes — every `instanceof` check keeps working.
|
|
16
|
+
*/
|
|
17
|
+
var CallMode;
|
|
18
|
+
(function (CallMode) {
|
|
19
|
+
/** The user is waiting on this call: show progress, and surface its failure. */
|
|
20
|
+
CallMode["FOREGROUND"] = "FOREGROUND";
|
|
21
|
+
/** Nobody is waiting (a poll, a prefetch, a sync): no progress UI, and a failure is not a dialog. */
|
|
22
|
+
CallMode["BACKGROUND"] = "BACKGROUND";
|
|
23
|
+
})(CallMode || (exports.CallMode = CallMode = {}));
|
|
24
|
+
/**
|
|
25
|
+
* Parks a {@link CallMode} on the value a generated client call rejected with, and reads it back —
|
|
26
|
+
* the same mechanism, for the same reasons, as `ApiCallSite`: a NON-ENUMERABLE own property, so it
|
|
27
|
+
* never reaches JSON, a structured log record or an error reporter's extra data by accident, and the
|
|
28
|
+
* SAME instance is rethrown so the error's class is untouched.
|
|
29
|
+
*
|
|
30
|
+
* It is typed on `ApiError` (`err.callMode`); {@link CallModeMarker.of} reads it off any thrown value,
|
|
31
|
+
* because an app's own `ErrorTranslator` may reject with a type that is not an `ApiError`.
|
|
32
|
+
*/
|
|
33
|
+
class CallModeMarker {
|
|
34
|
+
/** The property name the call mode is parked under on a rejected error. */
|
|
35
|
+
static PROPERTY = 'callMode';
|
|
36
|
+
/**
|
|
37
|
+
* Park `mode` on `failure` WITHOUT changing it. Never clobbers: a failure that already carries a
|
|
38
|
+
* mode keeps the innermost (the call that actually failed), exactly as `ApiCallSite.attachTo`
|
|
39
|
+
* does. A non-`Error` or a frozen/sealed error is left untouched — attaching is a marker and must
|
|
40
|
+
* never itself throw.
|
|
41
|
+
*/
|
|
42
|
+
// webpieces-disable no-function-outside-class -- stateless writer paired with of(); webpieces-disable no-any-unknown -- a rejection value is genuinely unknown; only an Error is touched
|
|
43
|
+
static attachTo(failure, mode) {
|
|
44
|
+
if (!(failure instanceof Error) || !Object.isExtensible(failure)) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (Object.prototype.hasOwnProperty.call(failure, CallModeMarker.PROPERTY)) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const descriptor = {
|
|
51
|
+
value: mode,
|
|
52
|
+
enumerable: false,
|
|
53
|
+
writable: false,
|
|
54
|
+
configurable: true,
|
|
55
|
+
};
|
|
56
|
+
Object.defineProperty(failure, CallModeMarker.PROPERTY, descriptor);
|
|
57
|
+
}
|
|
58
|
+
/** The mode a generated client parked on `failure`, or undefined when no client call rejected with it. */
|
|
59
|
+
// 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
|
|
60
|
+
static of(failure) {
|
|
61
|
+
if (!(failure instanceof Error)) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
// webpieces-disable no-any-unknown -- a property descriptor's value is untyped until narrowed below
|
|
65
|
+
const value = Object.getOwnPropertyDescriptor(failure, CallModeMarker.PROPERTY)?.value;
|
|
66
|
+
return value === CallMode.FOREGROUND || value === CallMode.BACKGROUND ? value : undefined;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.CallModeMarker = CallModeMarker;
|
|
70
|
+
//# sourceMappingURL=CallMode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CallMode.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/errors/CallMode.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;GAYG;AACH,IAAY,QAKX;AALD,WAAY,QAAQ;IAChB,gFAAgF;IAChF,qCAAyB,CAAA;IACzB,qGAAqG;IACrG,qCAAyB,CAAA;AAC7B,CAAC,EALW,QAAQ,wBAAR,QAAQ,QAKnB;AAED;;;;;;;;GAQG;AACH,MAAa,cAAc;IACvB,2EAA2E;IAC3E,MAAM,CAAU,QAAQ,GAAG,UAAU,CAAC;IAEtC;;;;;OAKG;IACH,yLAAyL;IACzL,MAAM,CAAC,QAAQ,CAAC,OAAgB,EAAE,IAAc;QAC5C,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,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzE,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,cAAc,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACxE,CAAC;IAED,0GAA0G;IAC1G,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,CAAC,OAAO,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC;QAChG,OAAO,KAAK,KAAK,QAAQ,CAAC,UAAU,IAAI,KAAK,KAAK,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9F,CAAC;;AApCL,wCAqCC","sourcesContent":["/**\n * Whether the USER is waiting on one api client call (#1293).\n *\n * WHY per CALL and not per endpoint: the same `listResumeItems` is FOREGROUND when the user clicks\n * Refresh and BACKGROUND when a timer polls it. `@Endpoint(..., { hideProgress: true })` is a fact\n * about the ROUTE; this is a fact about the CALL SITE, so it is chosen where the client is built —\n * a browser app builds a second client for the same contract with\n * `new ClientConfig(svcName, role, CallMode.BACKGROUND)`.\n *\n * A failed call carries it as `err.callMode` (see {@link CallModeMarker}), so an app's global error\n * handler can decide \"log + report, no dialog\" for a background failure with no app-side bookkeeping.\n * The error's CLASS never changes — every `instanceof` check keeps working.\n */\nexport enum CallMode {\n /** The user is waiting on this call: show progress, and surface its failure. */\n FOREGROUND = 'FOREGROUND',\n /** Nobody is waiting (a poll, a prefetch, a sync): no progress UI, and a failure is not a dialog. */\n BACKGROUND = 'BACKGROUND',\n}\n\n/**\n * Parks a {@link CallMode} on the value a generated client call rejected with, and reads it back —\n * the same mechanism, for the same reasons, as `ApiCallSite`: a NON-ENUMERABLE own property, so it\n * never reaches JSON, a structured log record or an error reporter's extra data by accident, and the\n * SAME instance is rethrown so the error's class is untouched.\n *\n * It is typed on `ApiError` (`err.callMode`); {@link CallModeMarker.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 */\nexport class CallModeMarker {\n /** The property name the call mode is parked under on a rejected error. */\n static readonly PROPERTY = 'callMode';\n\n /**\n * Park `mode` on `failure` WITHOUT changing it. Never clobbers: a failure that already carries a\n * mode keeps the innermost (the call that actually failed), exactly as `ApiCallSite.attachTo`\n * does. A non-`Error` or a frozen/sealed error is left untouched — attaching is a marker and must\n * never itself throw.\n */\n // webpieces-disable no-function-outside-class -- stateless writer paired with of(); webpieces-disable no-any-unknown -- a rejection value is genuinely unknown; only an Error is touched\n static attachTo(failure: unknown, mode: CallMode): void {\n if (!(failure instanceof Error) || !Object.isExtensible(failure)) {\n return;\n }\n if (Object.prototype.hasOwnProperty.call(failure, CallModeMarker.PROPERTY)) {\n return;\n }\n const descriptor: PropertyDescriptor = {\n value: mode,\n enumerable: false,\n writable: false,\n configurable: true,\n };\n Object.defineProperty(failure, CallModeMarker.PROPERTY, descriptor);\n }\n\n /** The mode a generated client parked on `failure`, or undefined when no client call rejected with it. */\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): CallMode | 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(failure, CallModeMarker.PROPERTY)?.value;\n return value === CallMode.FOREGROUND || value === CallMode.BACKGROUND ? value : undefined;\n }\n}\n"]}
|
package/src/errors/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { EndUserErrorRegistry } from './EndUserErrorRegistry';
|
|
|
5
5
|
export type { EndUserErrorFactory } from './EndUserErrorRegistry';
|
|
6
6
|
export { ClientRole } from './ClientRole';
|
|
7
7
|
export { ApiCallSite } from './ApiCallSite';
|
|
8
|
+
export { CallMode, CallModeMarker } from './CallMode';
|
|
8
9
|
export type { CallSiteBoundary } from './ApiCallSite';
|
|
9
10
|
export { ReportableApiFailure, RpcCallFailed } from './ReportableApiFailure';
|
|
10
11
|
export type { ApiErrorKind, ApiStatusCode, EdgeHttpStatus } from './ApiError';
|
package/src/errors/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
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.ReceivedApiErrorRule = exports.ApiErrorBoundary = exports.ApiErrorPayload = exports.ApiErrorCodec = exports.RpcCallFailed = exports.ReportableApiFailure = exports.ApiCallSite = 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.ApiRetriesExhaustedError = 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.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.ReceivedApiErrorRule = exports.ApiErrorBoundary = exports.ApiErrorPayload = exports.ApiErrorCodec = exports.RpcCallFailed = exports.ReportableApiFailure = exports.CallModeMarker = exports.CallMode = exports.ApiCallSite = 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.ApiRetriesExhaustedError = 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
|
var ApiError_1 = require("./ApiError");
|
|
5
5
|
Object.defineProperty(exports, "ApiError", { enumerable: true, get: function () { return ApiError_1.ApiError; } });
|
|
6
6
|
Object.defineProperty(exports, "ApiNotFoundError", { enumerable: true, get: function () { return ApiError_1.ApiNotFoundError; } });
|
|
@@ -38,6 +38,9 @@ var ClientRole_1 = require("./ClientRole");
|
|
|
38
38
|
Object.defineProperty(exports, "ClientRole", { enumerable: true, get: function () { return ClientRole_1.ClientRole; } });
|
|
39
39
|
var ApiCallSite_1 = require("./ApiCallSite");
|
|
40
40
|
Object.defineProperty(exports, "ApiCallSite", { enumerable: true, get: function () { return ApiCallSite_1.ApiCallSite; } });
|
|
41
|
+
var CallMode_1 = require("./CallMode");
|
|
42
|
+
Object.defineProperty(exports, "CallMode", { enumerable: true, get: function () { return CallMode_1.CallMode; } });
|
|
43
|
+
Object.defineProperty(exports, "CallModeMarker", { enumerable: true, get: function () { return CallMode_1.CallModeMarker; } });
|
|
41
44
|
var ReportableApiFailure_1 = require("./ReportableApiFailure");
|
|
42
45
|
Object.defineProperty(exports, "ReportableApiFailure", { enumerable: true, get: function () { return ReportableApiFailure_1.ReportableApiFailure; } });
|
|
43
46
|
Object.defineProperty(exports, "RpcCallFailed", { enumerable: true, get: function () { return ReportableApiFailure_1.RpcCallFailed; } });
|
package/src/errors/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/errors/index.ts"],"names":[],"mappings":";;;AAAA,uCAyBoB;AAxBhB,oGAAA,QAAQ,OAAA;AACR,4GAAA,gBAAgB,OAAA;AAChB,8GAAA,kBAAkB,OAAA;AAClB,8GAAA,kBAAkB,OAAA;AAClB,+GAAA,mBAAmB,OAAA;AACnB,qHAAA,yBAAyB,OAAA;AACzB,kHAAA,sBAAsB,OAAA;AACtB,+GAAA,mBAAmB,OAAA;AACnB,6GAAA,iBAAiB,OAAA;AACjB,kHAAA,sBAAsB,OAAA;AACtB,8GAAA,kBAAkB,OAAA;AAClB,oHAAA,wBAAwB,OAAA;AACxB,2GAAA,eAAe,OAAA;AACf,8GAAA,kBAAkB,OAAA;AAClB,gHAAA,oBAAoB,OAAA;AACpB,qHAAA,yBAAyB,OAAA;AACzB,oHAAA,wBAAwB,OAAA;AACxB,4GAAA,gBAAgB,OAAA;AAChB,iHAAA,qBAAqB,OAAA;AACrB,sHAAA,0BAA0B,OAAA;AAC1B,wHAAA,4BAA4B,OAAA;AAC5B,kHAAA,sBAAsB,OAAA;AACtB,yGAAA,aAAa,OAAA;AACb,gHAAA,oBAAoB,OAAA;AAExB,iDAIyB;AAHrB,wHAAA,uBAAuB,OAAA;AACvB,yHAAA,wBAAwB,OAAA;AACxB,0HAAA,yBAAyB,OAAA;AAE7B,uDAAsD;AAA7C,oHAAA,gBAAgB,OAAA;AACzB,+DAA8D;AAArD,4HAAA,oBAAoB,OAAA;AAE7B,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AACnB,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/errors/index.ts"],"names":[],"mappings":";;;AAAA,uCAyBoB;AAxBhB,oGAAA,QAAQ,OAAA;AACR,4GAAA,gBAAgB,OAAA;AAChB,8GAAA,kBAAkB,OAAA;AAClB,8GAAA,kBAAkB,OAAA;AAClB,+GAAA,mBAAmB,OAAA;AACnB,qHAAA,yBAAyB,OAAA;AACzB,kHAAA,sBAAsB,OAAA;AACtB,+GAAA,mBAAmB,OAAA;AACnB,6GAAA,iBAAiB,OAAA;AACjB,kHAAA,sBAAsB,OAAA;AACtB,8GAAA,kBAAkB,OAAA;AAClB,oHAAA,wBAAwB,OAAA;AACxB,2GAAA,eAAe,OAAA;AACf,8GAAA,kBAAkB,OAAA;AAClB,gHAAA,oBAAoB,OAAA;AACpB,qHAAA,yBAAyB,OAAA;AACzB,oHAAA,wBAAwB,OAAA;AACxB,4GAAA,gBAAgB,OAAA;AAChB,iHAAA,qBAAqB,OAAA;AACrB,sHAAA,0BAA0B,OAAA;AAC1B,wHAAA,4BAA4B,OAAA;AAC5B,kHAAA,sBAAsB,OAAA;AACtB,yGAAA,aAAa,OAAA;AACb,gHAAA,oBAAoB,OAAA;AAExB,iDAIyB;AAHrB,wHAAA,uBAAuB,OAAA;AACvB,yHAAA,wBAAwB,OAAA;AACxB,0HAAA,yBAAyB,OAAA;AAE7B,uDAAsD;AAA7C,oHAAA,gBAAgB,OAAA;AACzB,+DAA8D;AAArD,4HAAA,oBAAoB,OAAA;AAE7B,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AACnB,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AACpB,uCAAsD;AAA7C,oGAAA,QAAQ,OAAA;AAAE,0GAAA,cAAc,OAAA;AAEjC,+DAA6E;AAApE,4HAAA,oBAAoB,OAAA;AAAE,qHAAA,aAAa,OAAA;AAE5C,iDAAiE;AAAxD,8GAAA,aAAa,OAAA;AAAE,gHAAA,eAAe,OAAA;AACvC,uDAAsD;AAA7C,oHAAA,gBAAgB,OAAA;AACzB,+DAA8D;AAArD,4HAAA,oBAAoB,OAAA;AAC7B,yDAS6B;AARzB,qHAAA,gBAAgB,OAAA;AAChB,qHAAA,gBAAgB,OAAA;AAChB,gHAAA,WAAW,OAAA;AACX,iHAAA,YAAY,OAAA;AACZ,wHAAA,mBAAmB,OAAA;AACnB,iHAAA,YAAY,OAAA;AACZ,kHAAA,aAAa,OAAA;AACb,gHAAA,WAAW,OAAA","sourcesContent":["export {\n ApiError,\n ApiNotFoundError,\n ApiDependencyError,\n ApiBadGatewayError,\n ApiUnavailableError,\n ApiDependencyTimeoutError,\n ApiImplementationError,\n ApiRateLimitedError,\n ApiForbiddenError,\n ApiRequestTimeoutError,\n ApiConnectionError,\n ApiEndpointNotFoundError,\n ApiEndUserError,\n ApiBadRequestError,\n ApiUnauthorizedError,\n ApiDependencyBackoffError,\n ApiRetriesExhaustedError,\n ApiConflictError,\n ApiUnprocessableError,\n ApiPreconditionFailedError,\n ApiUnsupportedMediaTypeError,\n ApiNotImplementedError,\n ApiCodedError,\n ApiClientTooOldError,\n} from './ApiError';\nexport {\n ApiEndUserNotFoundError,\n ApiEndUserForbiddenError,\n ApiEndUserBadRequestError,\n} from './EndUserErrors';\nexport { EndUserErrorCode } from './EndUserErrorCode';\nexport { EndUserErrorRegistry } from './EndUserErrorRegistry';\nexport type { EndUserErrorFactory } from './EndUserErrorRegistry';\nexport { ClientRole } from './ClientRole';\nexport { ApiCallSite } from './ApiCallSite';\nexport { CallMode, CallModeMarker } from './CallMode';\nexport type { CallSiteBoundary } from './ApiCallSite';\nexport { ReportableApiFailure, RpcCallFailed } from './ReportableApiFailure';\nexport type { ApiErrorKind, ApiStatusCode, EdgeHttpStatus } from './ApiError';\nexport { ApiErrorCodec, ApiErrorPayload } from './ApiErrorCodec';\nexport { ApiErrorBoundary } from './ApiErrorBoundary';\nexport { ReceivedApiErrorRule } from './ReceivedApiErrorRule';\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 './AuthErrorSubtypes';\n"]}
|
|
@@ -3,6 +3,7 @@ import { AuthMeta } from './auth-mode';
|
|
|
3
3
|
import { EndpointResponseType, HttpParameterBinding } from './HttpContract';
|
|
4
4
|
import { StreamingEndpointMetadata } from './StreamingContract';
|
|
5
5
|
import { EndpointOperation } from './HttpEndpointOptions';
|
|
6
|
+
import { CallMode } from '../errors/CallMode';
|
|
6
7
|
/**
|
|
7
8
|
* Route metadata stored per-method at runtime.
|
|
8
9
|
* Used internally by http-routing and http-client as the runtime representation
|
|
@@ -28,6 +29,18 @@ export declare class RouteMetadata {
|
|
|
28
29
|
localOnly: boolean;
|
|
29
30
|
/** Clients clone metadata per call so a concrete winning mode governs both trust directions. */
|
|
30
31
|
withSelectedAuth(method: import('./auth-mode').AuthMode): RouteMetadata;
|
|
32
|
+
/**
|
|
33
|
+
* Whether the user is waiting on THIS call (#1293). Not an endpoint declaration: a client stamps
|
|
34
|
+
* it per call with {@link withCallMode}, from the `ClientConfig` it was built with. FOREGROUND on
|
|
35
|
+
* every route a contract's decorators build.
|
|
36
|
+
*/
|
|
37
|
+
callMode: CallMode;
|
|
38
|
+
/**
|
|
39
|
+
* A per-call copy carrying `mode`. A BACKGROUND call also gets {@link hideProgress} — nobody is
|
|
40
|
+
* waiting on it — so an app's `RequestLifecycleListener` keeps it out of the progress bar with
|
|
41
|
+
* no endpoint declaration. FOREGROUND returns this route unchanged.
|
|
42
|
+
*/
|
|
43
|
+
withCallMode(mode: CallMode): RouteMetadata;
|
|
31
44
|
httpMethod: string;
|
|
32
45
|
path: string;
|
|
33
46
|
methodName: string;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RouteMetadata = void 0;
|
|
4
4
|
const auth_mode_1 = require("./auth-mode");
|
|
5
|
+
const CallMode_1 = require("../errors/CallMode");
|
|
5
6
|
/**
|
|
6
7
|
* Route metadata stored per-method at runtime.
|
|
7
8
|
* Used internally by http-routing and http-client as the runtime representation
|
|
@@ -29,6 +30,27 @@ class RouteMetadata {
|
|
|
29
30
|
route.authMeta = new auth_mode_1.AuthMeta([method], method.kind === 'public' ? this.authMeta.publicReason : undefined);
|
|
30
31
|
return route;
|
|
31
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Whether the user is waiting on THIS call (#1293). Not an endpoint declaration: a client stamps
|
|
35
|
+
* it per call with {@link withCallMode}, from the `ClientConfig` it was built with. FOREGROUND on
|
|
36
|
+
* every route a contract's decorators build.
|
|
37
|
+
*/
|
|
38
|
+
callMode = CallMode_1.CallMode.FOREGROUND;
|
|
39
|
+
/**
|
|
40
|
+
* A per-call copy carrying `mode`. A BACKGROUND call also gets {@link hideProgress} — nobody is
|
|
41
|
+
* waiting on it — so an app's `RequestLifecycleListener` keeps it out of the progress bar with
|
|
42
|
+
* no endpoint declaration. FOREGROUND returns this route unchanged.
|
|
43
|
+
*/
|
|
44
|
+
withCallMode(mode) {
|
|
45
|
+
if (mode === CallMode_1.CallMode.FOREGROUND)
|
|
46
|
+
return this;
|
|
47
|
+
const route = new RouteMetadata(this.httpMethod, this.path, this.methodName, this.operation, this.controllerClassName, this.authMeta, this.apiName, this.formPost, this.mask, this.rawBody, this.parameterBindings, this.bodyParameterIndex, this.responseType, this.streaming, true, this.noLogging, this.allowUpgradeInFlight);
|
|
48
|
+
route.apiClass = this.apiClass;
|
|
49
|
+
route.authorization = this.authorization;
|
|
50
|
+
route.localOnly = this.localOnly;
|
|
51
|
+
route.callMode = mode;
|
|
52
|
+
return route;
|
|
53
|
+
}
|
|
32
54
|
httpMethod;
|
|
33
55
|
path;
|
|
34
56
|
methodName;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RouteMetadata.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/RouteMetadata.ts"],"names":[],"mappings":"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","sourcesContent":["import { MaskSpec } from './LogFieldMask';\nimport { AuthMeta } from './auth-mode';\nimport { EndpointResponseType, HttpParameterBinding } from './HttpContract';\nimport { StreamingEndpointMetadata } from './StreamingContract';\nimport { EndpointOperation } from './HttpEndpointOptions';\n\n/**\n * Route metadata stored per-method at runtime.\n * Used internally by http-routing and http-client as the runtime representation\n * of a route. Constructed from @ApiPath + @Endpoint metadata by ProxyClient\n * and ApiRoutingFactory.\n *\n * Lives in its own file (one class per file) purely for file size, exactly as `api-kind.ts` and\n * `external-caller.ts` were split off `decorators.ts` before it. Nothing about its role changed.\n */\nexport class RouteMetadata {\n apiClass?: Function;\n // webpieces-disable no-any-unknown -- erased application policy is validated by AuthorizationHook at server startup\n authorization?: import('./authorization').AuthorizationRequirement<unknown>;\n localOnly = false;\n\n /** Clients clone metadata per call so a concrete winning mode governs both trust directions. */\n withSelectedAuth(method: import('./auth-mode').AuthMode): RouteMetadata {\n if (!this.authMeta?.methods.includes(method)) throw new Error('Selected credential is not declared by this endpoint.');\n const route = Object.assign(new RouteMetadata(this.httpMethod, this.path, this.methodName, this.operation), this);\n route.authMeta = new AuthMeta([method], method.kind === 'public' ? this.authMeta.publicReason : undefined);\n return route;\n }\n httpMethod: string;\n path: string;\n methodName: string;\n controllerClassName?: string;\n authMeta?: AuthMeta;\n /** The API contract class name (e.g. 'SaveApi') — distinct from the controller name. */\n apiName?: string;\n /**\n * True when @Endpoint(..., { formPost: true }): the body is application/x-www-form-urlencoded\n * (flat key→value), not JSON. Rides the route metadata so the per-route body parse can branch\n * without knowing the apiClass/methodName. Default false = JSON.\n */\n readonly formPost: boolean;\n /**\n * The @MaskLog field-mask spec for this route, or undefined when the method declared none. Read\n * ONCE here at route-build time and handed to {@link LogApiCallImpl} via ApiMethodInfo, so the per-call\n * log path pays for masking only on routes that opted in (the rest stay on plain JSON.stringify).\n */\n readonly mask?: MaskSpec;\n /**\n * True when @Endpoint(..., { rawBody: true }): the transport must retain the verbatim bytes +\n * absolute url for the `webhook(...)` hook to verify a vendor signature over. Rides the route\n * metadata for the same reason {@link formPost} does — the transport adapter decides how to read\n * the body from the ROUTE, without knowing the apiClass/methodName.\n */\n readonly rawBody: boolean;\n /**\n * True when @Endpoint(..., { hideProgress: true }): the app should show no progress UI for this\n * call. webpieces does not act on it; an app's `RequestLifecycleListener.onRequestStart/onRequestEnd`\n * reads it from the ROUTE, for the same reason {@link formPost} and {@link rawBody} ride here —\n * the consumer branches without knowing the apiClass/methodName. Default false = the call drives\n * the progress UI.\n */\n readonly hideProgress: boolean;\n /**\n * True when @Endpoint(..., { noLogging: true }): read by {@link LogApiCallImpl} (via\n * ApiMethodInfo, exactly as {@link mask} is) so no `[API-*-req]`/`[API-*-resp-*]` line is\n * emitted for the call, on the client or the server. Default false = an ordinary, fully-logged\n * route.\n */\n readonly noLogging: boolean;\n /**\n * True when @Endpoint(..., { allowUpgradeInFlight: true }): an app may upgrade while this call is\n * in flight, and the call neither blocks nor arms an upgrade. webpieces only carries it; the\n * app's `RequestLifecycleListener` / upgrade logic reads it. Default false = the app must not\n * upgrade while the call is in flight, and its success counts toward arming one.\n */\n readonly allowUpgradeInFlight: boolean;\n\n constructor(\n httpMethod: string,\n path: string,\n methodName: string,\n /** Explicit side-effect semantics; deliberately independent of the HTTP verb. */\n readonly operation: EndpointOperation,\n controllerClassName?: string,\n authMeta?: AuthMeta,\n apiName?: string,\n formPost: boolean = false,\n mask?: MaskSpec,\n rawBody: boolean = false,\n /** Explicit path/query mappings, in API declaration order. */\n readonly parameterBindings: readonly HttpParameterBinding[] = [],\n /** The sole unannotated POST parameter, or undefined for a bodyless route. */\n readonly bodyParameterIndex?: number,\n /** Whether clients receive only the body or the complete status/header/body response. */\n readonly responseType: EndpointResponseType = 'body',\n /** Present only for `(ResponseStream) => Promise<RequestStream>` contracts. */\n readonly streaming?: StreamingEndpointMetadata,\n hideProgress: boolean = false,\n noLogging: boolean = false,\n allowUpgradeInFlight: boolean = false,\n ) {\n this.httpMethod = httpMethod;\n this.path = path;\n this.methodName = methodName;\n this.controllerClassName = controllerClassName;\n this.authMeta = authMeta;\n this.apiName = apiName;\n this.formPost = formPost;\n this.mask = mask;\n this.rawBody = rawBody;\n this.parameterBindings = parameterBindings;\n this.bodyParameterIndex = bodyParameterIndex;\n this.responseType = responseType;\n this.streaming = streaming;\n this.hideProgress = hideProgress;\n this.noLogging = noLogging;\n this.allowUpgradeInFlight = allowUpgradeInFlight;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"RouteMetadata.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/RouteMetadata.ts"],"names":[],"mappings":"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","sourcesContent":["import { MaskSpec } from './LogFieldMask';\nimport { AuthMeta } from './auth-mode';\nimport { EndpointResponseType, HttpParameterBinding } from './HttpContract';\nimport { StreamingEndpointMetadata } from './StreamingContract';\nimport { EndpointOperation } from './HttpEndpointOptions';\nimport { CallMode } from '../errors/CallMode';\n\n/**\n * Route metadata stored per-method at runtime.\n * Used internally by http-routing and http-client as the runtime representation\n * of a route. Constructed from @ApiPath + @Endpoint metadata by ProxyClient\n * and ApiRoutingFactory.\n *\n * Lives in its own file (one class per file) purely for file size, exactly as `api-kind.ts` and\n * `external-caller.ts` were split off `decorators.ts` before it. Nothing about its role changed.\n */\nexport class RouteMetadata {\n apiClass?: Function;\n // webpieces-disable no-any-unknown -- erased application policy is validated by AuthorizationHook at server startup\n authorization?: import('./authorization').AuthorizationRequirement<unknown>;\n localOnly = false;\n\n /** Clients clone metadata per call so a concrete winning mode governs both trust directions. */\n withSelectedAuth(method: import('./auth-mode').AuthMode): RouteMetadata {\n if (!this.authMeta?.methods.includes(method)) throw new Error('Selected credential is not declared by this endpoint.');\n const route = Object.assign(new RouteMetadata(this.httpMethod, this.path, this.methodName, this.operation), this);\n route.authMeta = new AuthMeta([method], method.kind === 'public' ? this.authMeta.publicReason : undefined);\n return route;\n }\n /**\n * Whether the user is waiting on THIS call (#1293). Not an endpoint declaration: a client stamps\n * it per call with {@link withCallMode}, from the `ClientConfig` it was built with. FOREGROUND on\n * every route a contract's decorators build.\n */\n callMode: CallMode = CallMode.FOREGROUND;\n\n /**\n * A per-call copy carrying `mode`. A BACKGROUND call also gets {@link hideProgress} — nobody is\n * waiting on it — so an app's `RequestLifecycleListener` keeps it out of the progress bar with\n * no endpoint declaration. FOREGROUND returns this route unchanged.\n */\n withCallMode(mode: CallMode): RouteMetadata {\n if (mode === CallMode.FOREGROUND) return this;\n const route = new RouteMetadata(\n this.httpMethod,\n this.path,\n this.methodName,\n this.operation,\n this.controllerClassName,\n this.authMeta,\n this.apiName,\n this.formPost,\n this.mask,\n this.rawBody,\n this.parameterBindings,\n this.bodyParameterIndex,\n this.responseType,\n this.streaming,\n true,\n this.noLogging,\n this.allowUpgradeInFlight,\n );\n route.apiClass = this.apiClass;\n route.authorization = this.authorization;\n route.localOnly = this.localOnly;\n route.callMode = mode;\n return route;\n }\n httpMethod: string;\n path: string;\n methodName: string;\n controllerClassName?: string;\n authMeta?: AuthMeta;\n /** The API contract class name (e.g. 'SaveApi') — distinct from the controller name. */\n apiName?: string;\n /**\n * True when @Endpoint(..., { formPost: true }): the body is application/x-www-form-urlencoded\n * (flat key→value), not JSON. Rides the route metadata so the per-route body parse can branch\n * without knowing the apiClass/methodName. Default false = JSON.\n */\n readonly formPost: boolean;\n /**\n * The @MaskLog field-mask spec for this route, or undefined when the method declared none. Read\n * ONCE here at route-build time and handed to {@link LogApiCallImpl} via ApiMethodInfo, so the per-call\n * log path pays for masking only on routes that opted in (the rest stay on plain JSON.stringify).\n */\n readonly mask?: MaskSpec;\n /**\n * True when @Endpoint(..., { rawBody: true }): the transport must retain the verbatim bytes +\n * absolute url for the `webhook(...)` hook to verify a vendor signature over. Rides the route\n * metadata for the same reason {@link formPost} does — the transport adapter decides how to read\n * the body from the ROUTE, without knowing the apiClass/methodName.\n */\n readonly rawBody: boolean;\n /**\n * True when @Endpoint(..., { hideProgress: true }): the app should show no progress UI for this\n * call. webpieces does not act on it; an app's `RequestLifecycleListener.onRequestStart/onRequestEnd`\n * reads it from the ROUTE, for the same reason {@link formPost} and {@link rawBody} ride here —\n * the consumer branches without knowing the apiClass/methodName. Default false = the call drives\n * the progress UI.\n */\n readonly hideProgress: boolean;\n /**\n * True when @Endpoint(..., { noLogging: true }): read by {@link LogApiCallImpl} (via\n * ApiMethodInfo, exactly as {@link mask} is) so no `[API-*-req]`/`[API-*-resp-*]` line is\n * emitted for the call, on the client or the server. Default false = an ordinary, fully-logged\n * route.\n */\n readonly noLogging: boolean;\n /**\n * True when @Endpoint(..., { allowUpgradeInFlight: true }): an app may upgrade while this call is\n * in flight, and the call neither blocks nor arms an upgrade. webpieces only carries it; the\n * app's `RequestLifecycleListener` / upgrade logic reads it. Default false = the app must not\n * upgrade while the call is in flight, and its success counts toward arming one.\n */\n readonly allowUpgradeInFlight: boolean;\n\n constructor(\n httpMethod: string,\n path: string,\n methodName: string,\n /** Explicit side-effect semantics; deliberately independent of the HTTP verb. */\n readonly operation: EndpointOperation,\n controllerClassName?: string,\n authMeta?: AuthMeta,\n apiName?: string,\n formPost: boolean = false,\n mask?: MaskSpec,\n rawBody: boolean = false,\n /** Explicit path/query mappings, in API declaration order. */\n readonly parameterBindings: readonly HttpParameterBinding[] = [],\n /** The sole unannotated POST parameter, or undefined for a bodyless route. */\n readonly bodyParameterIndex?: number,\n /** Whether clients receive only the body or the complete status/header/body response. */\n readonly responseType: EndpointResponseType = 'body',\n /** Present only for `(ResponseStream) => Promise<RequestStream>` contracts. */\n readonly streaming?: StreamingEndpointMetadata,\n hideProgress: boolean = false,\n noLogging: boolean = false,\n allowUpgradeInFlight: boolean = false,\n ) {\n this.httpMethod = httpMethod;\n this.path = path;\n this.methodName = methodName;\n this.controllerClassName = controllerClassName;\n this.authMeta = authMeta;\n this.apiName = apiName;\n this.formPost = formPost;\n this.mask = mask;\n this.rawBody = rawBody;\n this.parameterBindings = parameterBindings;\n this.bodyParameterIndex = bodyParameterIndex;\n this.responseType = responseType;\n this.streaming = streaming;\n this.hideProgress = hideProgress;\n this.noLogging = noLogging;\n this.allowUpgradeInFlight = allowUpgradeInFlight;\n }\n}\n"]}
|
package/src/http/api-type.js
CHANGED
|
@@ -102,8 +102,8 @@ function assertApiTypeMatchesMcpTools(apiClass) {
|
|
|
102
102
|
'every method is permanently outside MCP does not feed the MCP document.');
|
|
103
103
|
}
|
|
104
104
|
throw new Error(`${apiClass.name} declares @ApiType(..., MCP) but no method carries @WpMcpTool. ` +
|
|
105
|
-
"Add @WpMcpTool('<stable_tool_name>'
|
|
106
|
-
'
|
|
105
|
+
"Add @WpMcpTool('<stable_tool_name>') to the methods agents may call (each one's " +
|
|
106
|
+
'@summary JSDoc line is its title), or drop MCP from the @ApiType list.');
|
|
107
107
|
}
|
|
108
108
|
if (!declaresMcp && tools.length > 0) {
|
|
109
109
|
const named = tools.map((tool) => tool.methodName).join(', ');
|
package/src/http/api-type.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-type.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/api-type.ts"],"names":[],"mappings":"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","sourcesContent":["import 'reflect-metadata';\nimport { METADATA_KEYS } from './decorators';\nimport {\n InvalidEndpointForMcpMetadata,\n getInvalidEndpointsForMcp,\n getWpMcpTools,\n WpMcpToolMetadata,\n} from '../mcp/McpMetadata';\n\n/** Nominal backing keeps raw string literals out of contract declarations. */\nenum ApiTypeValue {\n SVC_TO_SVC = 'svc-to-svc',\n EXTERNAL_CUSTOMER = 'external-customer',\n MCP = 'mcp',\n}\n\n/** Short, statically importable decorator arguments. */\nexport const SVC_TO_SVC = ApiTypeValue.SVC_TO_SVC;\nexport const EXTERNAL_CUSTOMER = ApiTypeValue.EXTERNAL_CUSTOMER;\nexport const MCP = ApiTypeValue.MCP;\n\n/** WHICH generated documents a contract feeds. One value per document. */\nexport type ApiTypeKind = typeof SVC_TO_SVC | typeof EXTERNAL_CUSTOMER | typeof MCP;\n\n/** Metadata key for the contract-level declaration, parallel to {@link METADATA_KEYS}. */\nexport const API_TYPE_METADATA_KEY = 'webpieces:api-type';\n\n/** The fail-closed default: a contract that says nothing feeds only the internal document. */\nexport const DEFAULT_API_TYPES: readonly ApiTypeKind[] = [SVC_TO_SVC];\n\n/**\n * WHICH generated documents this CONTRACT feeds.\n *\n * ```typescript\n * @ApiPath('/stores')\n * @ApiType(SVC_TO_SVC, EXTERNAL_CUSTOMER) // private + customer documents\n * export abstract class StoreApi { ... }\n * ```\n *\n * | value | document |\n * |---|---|\n * | `SVC_TO_SVC` | `full-private-openapi.json` |\n * | `EXTERNAL_CUSTOMER` | `public-openapi.json` |\n * | `MCP` | `mcp-openapi.json` |\n *\n * A document is WRITTEN when at least one contract declares its type, and it contains the contracts\n * that declared it and no others. No contract declaring `MCP` therefore means no `mcp-openapi.json`\n * at all — there is no separate emptiness rule to keep in step with this one.\n *\n * ## Absent means `SVC_TO_SVC` only, and the level is why that is affordable\n *\n * `.claude/rules/no-backwards-compat.md` shim shape #5 rejects \"a widening that is an ABSENCE rather\n * than a token\": a default that publishes to customers would mean you reach them having typed\n * nothing, so the safe state requires an edit and the widest grant is free. Naming the type inverts\n * that — `grep -rn EXTERNAL_CUSTOMER` is the entire customer-facing surface on one screen.\n *\n * It is a CLASS-level declaration because that costs one token per contract rather than one per\n * method. A per-method audience list was the earlier design and was rejected on exactly that\n * ergonomic ground; the per-METHOD case that remains — a new endpoint on an already-published API —\n * is served by `{ hidden: true }` in `EndpointOptions`, which subtracts one method from the customer\n * document and leaves it in the other two. The two are orthogonal: this one selects FILES, that one\n * subtracts a METHOD from one file.\n *\n * ## `MCP` and `@WpMcpTool` are ONE decision\n *\n * `MCP` on a contract with no `@WpMcpTool` method is an error, and a `@WpMcpTool` method on a\n * contract that does not declare `MCP` is an error. Both directions are asserted by\n * {@link assertApiTypeMatchesMcpTools}; without that, membership is declared in two places and they\n * can disagree, which is the defect this whole documentation epic exists to remove.\n *\n * `@InvalidEndpointForMcp` sits OUTSIDE that biconditional: a method declared permanently unusable\n * as a tool is neither required to carry `@WpMcpTool` nor permitted to. A contract on which EVERY\n * endpoint is so declared must not declare `MCP` at all, and that too is asserted there.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function ApiType(first: ApiTypeKind, ...rest: readonly ApiTypeKind[]): ClassDecorator {\n const types = [first, ...rest];\n return (target: Function) => {\n Reflect.defineMetadata(API_TYPE_METADATA_KEY, types, target);\n };\n}\n\n/** The documents this contract feeds — its declaration, else {@link DEFAULT_API_TYPES}. */\n// webpieces-disable no-function-outside-class -- metadata reader paired with the decorator\nexport function getApiTypes(apiClass: Function): readonly ApiTypeKind[] {\n const declared: ApiTypeKind[] | undefined = Reflect.getMetadata(\n API_TYPE_METADATA_KEY,\n apiClass,\n );\n return declared ?? DEFAULT_API_TYPES;\n}\n\n/**\n * MCP membership and `@WpMcpTool` must AGREE, in both directions.\n *\n * A wiring-time assert rather than a type, because the two declarations sit on different decorators\n * at different levels and TypeScript cannot relate them. It is the sibling of\n * `assertEveryEndpointHasAuthMode`, and exists for the same reason.\n */\n// webpieces-disable no-function-outside-class -- wiring-time assert, sibling of assertEveryEndpointHasAuthMode\nexport function assertApiTypeMatchesMcpTools(apiClass: Function): void {\n const declaresMcp = getApiTypes(apiClass).includes(MCP);\n const tools = getWpMcpTools(apiClass);\n const excluded = getInvalidEndpointsForMcp(apiClass);\n assertNoToolIsAlsoExcluded(apiClass, tools, excluded);\n if (declaresMcp && tools.length === 0) {\n // The all-excluded case gets its own sentence because its cure is the opposite one: there is\n // nothing to add, and the contract should stop declaring MCP.\n if (excluded.length > 0 && excluded.length === getApiTypeEndpointNames(apiClass).length) {\n throw new Error(\n `${apiClass.name} declares @ApiType(..., MCP) but EVERY endpoint on it is ` +\n '@InvalidEndpointForMcp. Drop MCP from the @ApiType list — a contract whose ' +\n 'every method is permanently outside MCP does not feed the MCP document.',\n );\n }\n throw new Error(\n `${apiClass.name} declares @ApiType(..., MCP) but no method carries @WpMcpTool. ` +\n \"Add @WpMcpTool('<stable_tool_name>', '<Human-readable title>') to the methods \" +\n 'agents may call, or drop MCP from the @ApiType list.',\n );\n }\n if (!declaresMcp && tools.length > 0) {\n const named = tools.map((tool: WpMcpToolMetadata) => tool.methodName).join(', ');\n throw new Error(\n `${apiClass.name} has @WpMcpTool on ${named} but does not declare @ApiType(..., MCP). ` +\n 'Add MCP to the @ApiType list — membership has ONE spelling, so a tool on a ' +\n 'contract nobody published to agents is a contradiction, not a hint.',\n );\n }\n}\n\n/**\n * `@WpMcpTool` and `@InvalidEndpointForMcp` on ONE method is a contradiction, not a precedence rule.\n *\n * Resolving it either way would make the pair a second spelling of a decision that already has one —\n * shim shape #1 in `.claude/rules/no-backwards-compat.md` — and whichever way it resolved, half the\n * readers of that method would be wrong about what it does.\n */\n// webpieces-disable no-function-outside-class -- private assert of assertApiTypeMatchesMcpTools, beside it\nfunction assertNoToolIsAlsoExcluded(\n apiClass: Function,\n tools: readonly WpMcpToolMetadata[],\n excluded: readonly InvalidEndpointForMcpMetadata[],\n): void {\n const excludedNames = new Set(\n excluded.map((one: InvalidEndpointForMcpMetadata) => one.methodName),\n );\n const both = tools\n .filter((tool: WpMcpToolMetadata) => excludedNames.has(tool.methodName))\n .map((tool: WpMcpToolMetadata) => tool.methodName);\n if (both.length === 0) return;\n throw new Error(\n `${apiClass.name} carries BOTH @WpMcpTool and @InvalidEndpointForMcp on ${both.join(', ')}. ` +\n 'They contradict each other — one publishes the method to agents and the other declares ' +\n 'it permanently unusable as a tool. Delete whichever one is wrong.',\n );\n}\n\n/** Reads the endpoints map so a caller can see which methods exist without importing decorators.ts. */\n// webpieces-disable no-function-outside-class -- metadata reader, sibling of getApiTypes\nexport function getApiTypeEndpointNames(apiClass: Function): readonly string[] {\n const endpoints: Record<string, string> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINTS, apiClass) ?? {};\n return Object.keys(endpoints);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"api-type.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/api-type.ts"],"names":[],"mappings":"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","sourcesContent":["import 'reflect-metadata';\nimport { METADATA_KEYS } from './decorators';\nimport {\n InvalidEndpointForMcpMetadata,\n getInvalidEndpointsForMcp,\n getWpMcpTools,\n WpMcpToolMetadata,\n} from '../mcp/McpMetadata';\n\n/** Nominal backing keeps raw string literals out of contract declarations. */\nenum ApiTypeValue {\n SVC_TO_SVC = 'svc-to-svc',\n EXTERNAL_CUSTOMER = 'external-customer',\n MCP = 'mcp',\n}\n\n/** Short, statically importable decorator arguments. */\nexport const SVC_TO_SVC = ApiTypeValue.SVC_TO_SVC;\nexport const EXTERNAL_CUSTOMER = ApiTypeValue.EXTERNAL_CUSTOMER;\nexport const MCP = ApiTypeValue.MCP;\n\n/** WHICH generated documents a contract feeds. One value per document. */\nexport type ApiTypeKind = typeof SVC_TO_SVC | typeof EXTERNAL_CUSTOMER | typeof MCP;\n\n/** Metadata key for the contract-level declaration, parallel to {@link METADATA_KEYS}. */\nexport const API_TYPE_METADATA_KEY = 'webpieces:api-type';\n\n/** The fail-closed default: a contract that says nothing feeds only the internal document. */\nexport const DEFAULT_API_TYPES: readonly ApiTypeKind[] = [SVC_TO_SVC];\n\n/**\n * WHICH generated documents this CONTRACT feeds.\n *\n * ```typescript\n * @ApiPath('/stores')\n * @ApiType(SVC_TO_SVC, EXTERNAL_CUSTOMER) // private + customer documents\n * export abstract class StoreApi { ... }\n * ```\n *\n * | value | document |\n * |---|---|\n * | `SVC_TO_SVC` | `full-private-openapi.json` |\n * | `EXTERNAL_CUSTOMER` | `public-openapi.json` |\n * | `MCP` | `mcp-openapi.json` |\n *\n * A document is WRITTEN when at least one contract declares its type, and it contains the contracts\n * that declared it and no others. No contract declaring `MCP` therefore means no `mcp-openapi.json`\n * at all — there is no separate emptiness rule to keep in step with this one.\n *\n * ## Absent means `SVC_TO_SVC` only, and the level is why that is affordable\n *\n * `.claude/rules/no-backwards-compat.md` shim shape #5 rejects \"a widening that is an ABSENCE rather\n * than a token\": a default that publishes to customers would mean you reach them having typed\n * nothing, so the safe state requires an edit and the widest grant is free. Naming the type inverts\n * that — `grep -rn EXTERNAL_CUSTOMER` is the entire customer-facing surface on one screen.\n *\n * It is a CLASS-level declaration because that costs one token per contract rather than one per\n * method. A per-method audience list was the earlier design and was rejected on exactly that\n * ergonomic ground; the per-METHOD case that remains — a new endpoint on an already-published API —\n * is served by `{ hidden: true }` in `EndpointOptions`, which subtracts one method from the customer\n * document and leaves it in the other two. The two are orthogonal: this one selects FILES, that one\n * subtracts a METHOD from one file.\n *\n * ## `MCP` and `@WpMcpTool` are ONE decision\n *\n * `MCP` on a contract with no `@WpMcpTool` method is an error, and a `@WpMcpTool` method on a\n * contract that does not declare `MCP` is an error. Both directions are asserted by\n * {@link assertApiTypeMatchesMcpTools}; without that, membership is declared in two places and they\n * can disagree, which is the defect this whole documentation epic exists to remove.\n *\n * `@InvalidEndpointForMcp` sits OUTSIDE that biconditional: a method declared permanently unusable\n * as a tool is neither required to carry `@WpMcpTool` nor permitted to. A contract on which EVERY\n * endpoint is so declared must not declare `MCP` at all, and that too is asserted there.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function ApiType(first: ApiTypeKind, ...rest: readonly ApiTypeKind[]): ClassDecorator {\n const types = [first, ...rest];\n return (target: Function) => {\n Reflect.defineMetadata(API_TYPE_METADATA_KEY, types, target);\n };\n}\n\n/** The documents this contract feeds — its declaration, else {@link DEFAULT_API_TYPES}. */\n// webpieces-disable no-function-outside-class -- metadata reader paired with the decorator\nexport function getApiTypes(apiClass: Function): readonly ApiTypeKind[] {\n const declared: ApiTypeKind[] | undefined = Reflect.getMetadata(\n API_TYPE_METADATA_KEY,\n apiClass,\n );\n return declared ?? DEFAULT_API_TYPES;\n}\n\n/**\n * MCP membership and `@WpMcpTool` must AGREE, in both directions.\n *\n * A wiring-time assert rather than a type, because the two declarations sit on different decorators\n * at different levels and TypeScript cannot relate them. It is the sibling of\n * `assertEveryEndpointHasAuthMode`, and exists for the same reason.\n */\n// webpieces-disable no-function-outside-class -- wiring-time assert, sibling of assertEveryEndpointHasAuthMode\nexport function assertApiTypeMatchesMcpTools(apiClass: Function): void {\n const declaresMcp = getApiTypes(apiClass).includes(MCP);\n const tools = getWpMcpTools(apiClass);\n const excluded = getInvalidEndpointsForMcp(apiClass);\n assertNoToolIsAlsoExcluded(apiClass, tools, excluded);\n if (declaresMcp && tools.length === 0) {\n // The all-excluded case gets its own sentence because its cure is the opposite one: there is\n // nothing to add, and the contract should stop declaring MCP.\n if (excluded.length > 0 && excluded.length === getApiTypeEndpointNames(apiClass).length) {\n throw new Error(\n `${apiClass.name} declares @ApiType(..., MCP) but EVERY endpoint on it is ` +\n '@InvalidEndpointForMcp. Drop MCP from the @ApiType list — a contract whose ' +\n 'every method is permanently outside MCP does not feed the MCP document.',\n );\n }\n throw new Error(\n `${apiClass.name} declares @ApiType(..., MCP) but no method carries @WpMcpTool. ` +\n \"Add @WpMcpTool('<stable_tool_name>') to the methods agents may call (each one's \" +\n '@summary JSDoc line is its title), or drop MCP from the @ApiType list.',\n );\n }\n if (!declaresMcp && tools.length > 0) {\n const named = tools.map((tool: WpMcpToolMetadata) => tool.methodName).join(', ');\n throw new Error(\n `${apiClass.name} has @WpMcpTool on ${named} but does not declare @ApiType(..., MCP). ` +\n 'Add MCP to the @ApiType list — membership has ONE spelling, so a tool on a ' +\n 'contract nobody published to agents is a contradiction, not a hint.',\n );\n }\n}\n\n/**\n * `@WpMcpTool` and `@InvalidEndpointForMcp` on ONE method is a contradiction, not a precedence rule.\n *\n * Resolving it either way would make the pair a second spelling of a decision that already has one —\n * shim shape #1 in `.claude/rules/no-backwards-compat.md` — and whichever way it resolved, half the\n * readers of that method would be wrong about what it does.\n */\n// webpieces-disable no-function-outside-class -- private assert of assertApiTypeMatchesMcpTools, beside it\nfunction assertNoToolIsAlsoExcluded(\n apiClass: Function,\n tools: readonly WpMcpToolMetadata[],\n excluded: readonly InvalidEndpointForMcpMetadata[],\n): void {\n const excludedNames = new Set(\n excluded.map((one: InvalidEndpointForMcpMetadata) => one.methodName),\n );\n const both = tools\n .filter((tool: WpMcpToolMetadata) => excludedNames.has(tool.methodName))\n .map((tool: WpMcpToolMetadata) => tool.methodName);\n if (both.length === 0) return;\n throw new Error(\n `${apiClass.name} carries BOTH @WpMcpTool and @InvalidEndpointForMcp on ${both.join(', ')}. ` +\n 'They contradict each other — one publishes the method to agents and the other declares ' +\n 'it permanently unusable as a tool. Delete whichever one is wrong.',\n );\n}\n\n/** Reads the endpoints map so a caller can see which methods exist without importing decorators.ts. */\n// webpieces-disable no-function-outside-class -- metadata reader, sibling of getApiTypes\nexport function getApiTypeEndpointNames(apiClass: Function): readonly string[] {\n const endpoints: Record<string, string> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINTS, apiClass) ?? {};\n return Object.keys(endpoints);\n}\n"]}
|
package/src/index.d.ts
CHANGED
|
@@ -40,6 +40,7 @@ export type { EndpointKind, EndpointOperation, EndpointOptions, ExternalEndpoint
|
|
|
40
40
|
export { RPC, CLOUDTASKS, CRON, EXTERNAL, READ, WRITE_IDEMPOTENT, WRITE } from './http/decorators';
|
|
41
41
|
export { InvalidEndpointForMcp, InvalidEndpointForMcpMetadata, WpMcpTool, WpMcpToolHints, WpMcpToolMetadata, getInvalidEndpointsForMcp, getWpMcpTools, mcpHintsForOperation, } from './mcp/McpMetadata';
|
|
42
42
|
export type { WpMcpMethodDecorator } from './mcp/McpMetadata';
|
|
43
|
+
export { McpToolName } from './mcp/McpToolName';
|
|
43
44
|
export { ApiJsonSchema, ApiJsonSchemaDiscriminator, ApiJsonSchemaValidator, DtoValidationFailure, ObjectSchemaBuilder, } from './mcp/DtoSchema';
|
|
44
45
|
export type { ApiJsonSchemaType, DtoValue } from './mcp/DtoSchema';
|
|
45
46
|
export { McpToolCatalogFile, McpToolCatalogError, McpToolDefinition, } from './mcp/McpToolCatalogFile';
|
|
@@ -101,7 +102,7 @@ export { RecordSerializer, SerializedMap, SerializedError } from './http/recorde
|
|
|
101
102
|
export { Filter } from './filters/Filter';
|
|
102
103
|
export type { Service } from './filters/Filter';
|
|
103
104
|
export { FilterChain } from './filters/FilterChain';
|
|
104
|
-
export { ApiError, ApiNotFoundError, ApiDependencyError, ApiBadGatewayError, ApiUnavailableError, ApiDependencyTimeoutError, ApiImplementationError, ApiRateLimitedError, ApiForbiddenError, ApiRequestTimeoutError, ApiEndUserError, ApiBadRequestError, ApiUnauthorizedError, ApiDependencyBackoffError, ApiRetriesExhaustedError, ApiConnectionError, ApiEndpointNotFoundError, ApiConflictError, ApiUnprocessableError, ApiPreconditionFailedError, ApiUnsupportedMediaTypeError, ApiNotImplementedError, ApiCodedError, ApiClientTooOldError, ApiEndUserNotFoundError, ApiEndUserForbiddenError, ApiEndUserBadRequestError, EndUserErrorCode, EndUserErrorRegistry, ClientRole, ApiErrorCodec, ApiErrorPayload, ApiErrorBoundary, ApiCallSite, ReportableApiFailure, RpcCallFailed, } from './errors';
|
|
105
|
+
export { ApiError, ApiNotFoundError, ApiDependencyError, ApiBadGatewayError, ApiUnavailableError, ApiDependencyTimeoutError, ApiImplementationError, ApiRateLimitedError, ApiForbiddenError, ApiRequestTimeoutError, ApiEndUserError, ApiBadRequestError, ApiUnauthorizedError, ApiDependencyBackoffError, ApiRetriesExhaustedError, ApiConnectionError, ApiEndpointNotFoundError, ApiConflictError, ApiUnprocessableError, ApiPreconditionFailedError, ApiUnsupportedMediaTypeError, ApiNotImplementedError, ApiCodedError, ApiClientTooOldError, ApiEndUserNotFoundError, ApiEndUserForbiddenError, ApiEndUserBadRequestError, EndUserErrorCode, EndUserErrorRegistry, ClientRole, ApiErrorCodec, ApiErrorPayload, ApiErrorBoundary, ApiCallSite, CallMode, CallModeMarker, ReportableApiFailure, RpcCallFailed, } from './errors';
|
|
105
106
|
export type { ApiErrorKind, ApiStatusCode, EdgeHttpStatus, EndUserErrorFactory, CallSiteBoundary, } from './errors';
|
|
106
107
|
export { ApiErrorHttpStatus } from './http/ApiErrorHttpStatus';
|
|
107
108
|
export { OAuthErrorCode, OAuthErrorStatus, OAuthText } from './oauth/OAuthErrorCode';
|
package/src/index.js
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
exports.MCP = exports.EXTERNAL_CUSTOMER = exports.SVC_TO_SVC = exports.ApiType = exports.NUMERIC_METADATA_KEYS = exports.WpMax = exports.WpMin = exports.WpInt = exports.getHttpParameterDeclarations = exports.QueryParam = exports.PathParam = exports.METADATA_KEYS = exports.validateNoConflictingDecorators = exports.assertEveryWebhookEndpointRetainsRawBody = exports.assertEveryExternalEndpointDeclaresCaller = exports.assertEveryEndpointHasAuthMode = exports.getAuthMeta = exports.isApiPath = exports.isRawBody = exports.isFormPost = exports.getMaskSpec = exports.getEndpointKinds = exports.getEndpointKind = exports.getEndpointOperation = exports.getEndpointOptions = exports.getEndpointHttpMethod = exports.getEndpoints = exports.getApiPath = exports.MaskLog = exports.isLocalOnly = exports.WpLocalOnly = exports.MISSING_AUTH_DECORATOR_FIX = exports.WpAuth = exports.WpAuthPublic = exports.Endpoint = exports.ApiPath = exports.GCP_LOG_BUDGET_BYTES = exports.MAX_GCP_LOG_BYTES = exports.LogChunkInfo = exports.LogChunkerImpl = exports.LogChunker = exports.LogManager = exports.ConsoleLoggerFactory = exports.ConsoleLogger = exports.DESIGN_METADATA_KEYS = exports.isDocumentDesign = exports.DocumentDesign = exports.ContextTuple = exports.ContextKey = exports.toError = void 0;
|
|
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
|
-
exports.
|
|
14
|
-
exports.
|
|
15
|
-
exports.McpToolProfilesOptions = exports.Mcp = exports.OAuthAuthorizationErrorRedirect = exports.OAuthErrorResponse = exports.OAuthErrorBody = exports.OAuthBearerChallenge = exports.OAuthErrorExtras = exports.OAuthProtocolError = exports.OAuthText = exports.OAuthErrorStatus = exports.OAuthErrorCode = exports.ApiErrorHttpStatus = exports.RpcCallFailed = exports.ReportableApiFailure = exports.ApiCallSite = 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.ApiRetriesExhaustedError = exports.ApiDependencyBackoffError = exports.ApiUnauthorizedError = exports.ApiBadRequestError = exports.ApiEndUserError = exports.ApiRequestTimeoutError = exports.ApiForbiddenError = exports.ApiRateLimitedError = exports.ApiImplementationError = exports.ApiDependencyTimeoutError = exports.ApiUnavailableError = exports.ApiBadGatewayError = exports.ApiDependencyError = void 0;
|
|
13
|
+
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.oidcSvcs = exports.oidcSameProject = 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.McpToolName = exports.mcpHintsForOperation = exports.getWpMcpTools = exports.getInvalidEndpointsForMcp = exports.WpMcpToolMetadata = exports.WpMcpToolHints = exports.WpMcpTool = exports.InvalidEndpointForMcpMetadata = void 0;
|
|
14
|
+
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_ERROR_CLASSIFIER = exports.WebpiecesDefaultErrorClassifier = exports.KeyedErrorClassifier = exports.CallOutcome = 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.RetryStrategy = exports.TransportDefault = exports.CallRegistry = exports.ClientRegistry = exports.HeaderRegistry = exports.DateTimeUtil = exports.TimeUtil = exports.DateUtil = exports.InstantUtil = exports.NetworkRejectClassifier = exports.NO_REG_CODE = void 0;
|
|
15
|
+
exports.McpToolProfilesOptions = exports.Mcp = exports.OAuthAuthorizationErrorRedirect = exports.OAuthErrorResponse = exports.OAuthErrorBody = exports.OAuthBearerChallenge = exports.OAuthErrorExtras = exports.OAuthProtocolError = exports.OAuthText = exports.OAuthErrorStatus = exports.OAuthErrorCode = exports.ApiErrorHttpStatus = exports.RpcCallFailed = exports.ReportableApiFailure = exports.CallModeMarker = exports.CallMode = exports.ApiCallSite = 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.ApiRetriesExhaustedError = exports.ApiDependencyBackoffError = exports.ApiUnauthorizedError = exports.ApiBadRequestError = exports.ApiEndUserError = exports.ApiRequestTimeoutError = exports.ApiForbiddenError = exports.ApiRateLimitedError = exports.ApiImplementationError = exports.ApiDependencyTimeoutError = exports.ApiUnavailableError = exports.ApiBadGatewayError = exports.ApiDependencyError = exports.ApiNotFoundError = 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");
|
|
@@ -154,6 +154,8 @@ Object.defineProperty(exports, "WpMcpToolMetadata", { enumerable: true, get: fun
|
|
|
154
154
|
Object.defineProperty(exports, "getInvalidEndpointsForMcp", { enumerable: true, get: function () { return McpMetadata_1.getInvalidEndpointsForMcp; } });
|
|
155
155
|
Object.defineProperty(exports, "getWpMcpTools", { enumerable: true, get: function () { return McpMetadata_1.getWpMcpTools; } });
|
|
156
156
|
Object.defineProperty(exports, "mcpHintsForOperation", { enumerable: true, get: function () { return McpMetadata_1.mcpHintsForOperation; } });
|
|
157
|
+
var McpToolName_1 = require("./mcp/McpToolName");
|
|
158
|
+
Object.defineProperty(exports, "McpToolName", { enumerable: true, get: function () { return McpToolName_1.McpToolName; } });
|
|
157
159
|
var DtoSchema_1 = require("./mcp/DtoSchema");
|
|
158
160
|
Object.defineProperty(exports, "ApiJsonSchema", { enumerable: true, get: function () { return DtoSchema_1.ApiJsonSchema; } });
|
|
159
161
|
Object.defineProperty(exports, "ApiJsonSchemaDiscriminator", { enumerable: true, get: function () { return DtoSchema_1.ApiJsonSchemaDiscriminator; } });
|
|
@@ -362,6 +364,8 @@ Object.defineProperty(exports, "ApiErrorCodec", { enumerable: true, get: functio
|
|
|
362
364
|
Object.defineProperty(exports, "ApiErrorPayload", { enumerable: true, get: function () { return errors_2.ApiErrorPayload; } });
|
|
363
365
|
Object.defineProperty(exports, "ApiErrorBoundary", { enumerable: true, get: function () { return errors_2.ApiErrorBoundary; } });
|
|
364
366
|
Object.defineProperty(exports, "ApiCallSite", { enumerable: true, get: function () { return errors_2.ApiCallSite; } });
|
|
367
|
+
Object.defineProperty(exports, "CallMode", { enumerable: true, get: function () { return errors_2.CallMode; } });
|
|
368
|
+
Object.defineProperty(exports, "CallModeMarker", { enumerable: true, get: function () { return errors_2.CallModeMarker; } });
|
|
365
369
|
Object.defineProperty(exports, "ReportableApiFailure", { enumerable: true, get: function () { return errors_2.ReportableApiFailure; } });
|
|
366
370
|
Object.defineProperty(exports, "RpcCallFailed", { enumerable: true, get: function () { return errors_2.RpcCallFailed; } });
|
|
367
371
|
var ApiErrorHttpStatus_1 = require("./http/ApiErrorHttpStatus");
|