@webpieces/core-util 0.4.807 → 0.4.809

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/core-util",
3
- "version": "0.4.807",
3
+ "version": "0.4.809",
4
4
  "description": "Utility functions for WebPieces - works in browser and Node.js",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -54,6 +54,10 @@ export declare const DEFAULT_API_TYPES: readonly ApiTypeKind[];
54
54
  * contract that does not declare `MCP` is an error. Both directions are asserted by
55
55
  * {@link assertApiTypeMatchesMcpTools}; without that, membership is declared in two places and they
56
56
  * can disagree, which is the defect this whole documentation epic exists to remove.
57
+ *
58
+ * `@InvalidEndpointForMcp` sits OUTSIDE that biconditional: a method declared permanently unusable
59
+ * as a tool is neither required to carry `@WpMcpTool` nor permitted to. A contract on which EVERY
60
+ * endpoint is so declared must not declare `MCP` at all, and that too is asserted there.
57
61
  */
58
62
  export declare function ApiType(first: ApiTypeKind, ...rest: readonly ApiTypeKind[]): ClassDecorator;
59
63
  /** The documents this contract feeds — its declaration, else {@link DEFAULT_API_TYPES}. */
@@ -62,6 +62,10 @@ exports.DEFAULT_API_TYPES = [exports.SVC_TO_SVC];
62
62
  * contract that does not declare `MCP` is an error. Both directions are asserted by
63
63
  * {@link assertApiTypeMatchesMcpTools}; without that, membership is declared in two places and they
64
64
  * can disagree, which is the defect this whole documentation epic exists to remove.
65
+ *
66
+ * `@InvalidEndpointForMcp` sits OUTSIDE that biconditional: a method declared permanently unusable
67
+ * as a tool is neither required to carry `@WpMcpTool` nor permitted to. A contract on which EVERY
68
+ * endpoint is so declared must not declare `MCP` at all, and that too is asserted there.
65
69
  */
66
70
  // webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope
67
71
  function ApiType(first, ...rest) {
@@ -87,7 +91,16 @@ function getApiTypes(apiClass) {
87
91
  function assertApiTypeMatchesMcpTools(apiClass) {
88
92
  const declaresMcp = getApiTypes(apiClass).includes(exports.MCP);
89
93
  const tools = (0, McpMetadata_1.getWpMcpTools)(apiClass);
94
+ const excluded = (0, McpMetadata_1.getInvalidEndpointsForMcp)(apiClass);
95
+ assertNoToolIsAlsoExcluded(apiClass, tools, excluded);
90
96
  if (declaresMcp && tools.length === 0) {
97
+ // The all-excluded case gets its own sentence because its cure is the opposite one: there is
98
+ // nothing to add, and the contract should stop declaring MCP.
99
+ if (excluded.length > 0 && excluded.length === getApiTypeEndpointNames(apiClass).length) {
100
+ throw new Error(`${apiClass.name} declares @ApiType(..., MCP) but EVERY endpoint on it is ` +
101
+ '@InvalidEndpointForMcp. Drop MCP from the @ApiType list — a contract whose ' +
102
+ 'every method is permanently outside MCP does not feed the MCP document.');
103
+ }
91
104
  throw new Error(`${apiClass.name} declares @ApiType(..., MCP) but no method carries @WpMcpTool. ` +
92
105
  "Add @WpMcpTool('<stable_tool_name>') to the methods agents may call, or drop MCP " +
93
106
  'from the @ApiType list.');
@@ -99,6 +112,25 @@ function assertApiTypeMatchesMcpTools(apiClass) {
99
112
  'contract nobody published to agents is a contradiction, not a hint.');
100
113
  }
101
114
  }
115
+ /**
116
+ * `@WpMcpTool` and `@InvalidEndpointForMcp` on ONE method is a contradiction, not a precedence rule.
117
+ *
118
+ * Resolving it either way would make the pair a second spelling of a decision that already has one —
119
+ * shim shape #1 in `.claude/rules/no-backwards-compat.md` — and whichever way it resolved, half the
120
+ * readers of that method would be wrong about what it does.
121
+ */
122
+ // webpieces-disable no-function-outside-class -- private assert of assertApiTypeMatchesMcpTools, beside it
123
+ function assertNoToolIsAlsoExcluded(apiClass, tools, excluded) {
124
+ const excludedNames = new Set(excluded.map((one) => one.methodName));
125
+ const both = tools
126
+ .filter((tool) => excludedNames.has(tool.methodName))
127
+ .map((tool) => tool.methodName);
128
+ if (both.length === 0)
129
+ return;
130
+ throw new Error(`${apiClass.name} carries BOTH @WpMcpTool and @InvalidEndpointForMcp on ${both.join(', ')}. ` +
131
+ 'They contradict each other — one publishes the method to agents and the other declares ' +
132
+ 'it permanently unusable as a tool. Delete whichever one is wrong.');
133
+ }
102
134
  /** Reads the endpoints map so a caller can see which methods exist without importing decorators.ts. */
103
135
  // webpieces-disable no-function-outside-class -- metadata reader, sibling of getApiTypes
104
136
  function getApiTypeEndpointNames(apiClass) {
@@ -1 +1 @@
1
- {"version":3,"file":"api-type.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/api-type.ts"],"names":[],"mappings":";;;AAkEA,0BAKC;AAID,kCAMC;AAUD,oEAkBC;AAID,0DAIC;AArHD,4BAA0B;AAC1B,6CAA6C;AAC7C,oDAAsE;AAEtE,8EAA8E;AAC9E,IAAK,YAIJ;AAJD,WAAK,YAAY;IACb,yCAAyB,CAAA;IACzB,uDAAuC,CAAA;IACvC,2BAAW,CAAA;AACf,CAAC,EAJI,YAAY,KAAZ,YAAY,QAIhB;AAED,wDAAwD;AAC3C,QAAA,UAAU,GAAG,YAAY,CAAC,UAAU,CAAC;AACrC,QAAA,iBAAiB,GAAG,YAAY,CAAC,iBAAiB,CAAC;AACnD,QAAA,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC;AAKpC,0FAA0F;AAC7E,QAAA,qBAAqB,GAAG,oBAAoB,CAAC;AAE1D,8FAA8F;AACjF,QAAA,iBAAiB,GAA2B,CAAC,kBAAU,CAAC,CAAC;AAEtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,2GAA2G;AAC3G,SAAgB,OAAO,CAAC,KAAkB,EAAE,GAAG,IAA4B;IACvE,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;IAC/B,OAAO,CAAC,MAAgB,EAAE,EAAE;QACxB,OAAO,CAAC,cAAc,CAAC,6BAAqB,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IACjE,CAAC,CAAC;AACN,CAAC;AAED,2FAA2F;AAC3F,2FAA2F;AAC3F,SAAgB,WAAW,CAAC,QAAkB;IAC1C,MAAM,QAAQ,GAA8B,OAAO,CAAC,WAAW,CAC3D,6BAAqB,EACrB,QAAQ,CACX,CAAC;IACF,OAAO,QAAQ,IAAI,yBAAiB,CAAC;AACzC,CAAC;AAED;;;;;;GAMG;AACH,+GAA+G;AAC/G,SAAgB,4BAA4B,CAAC,QAAkB;IAC3D,MAAM,WAAW,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,WAAG,CAAC,CAAC;IACxD,MAAM,KAAK,GAAG,IAAA,2BAAa,EAAC,QAAQ,CAAC,CAAC;IACtC,IAAI,WAAW,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CACX,GAAG,QAAQ,CAAC,IAAI,iEAAiE;YAC7E,mFAAmF;YACnF,yBAAyB,CAChC,CAAC;IACN,CAAC;IACD,IAAI,CAAC,WAAW,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAuB,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjF,MAAM,IAAI,KAAK,CACX,GAAG,QAAQ,CAAC,IAAI,sBAAsB,KAAK,4CAA4C;YACnF,6EAA6E;YAC7E,qEAAqE,CAC5E,CAAC;IACN,CAAC;AACL,CAAC;AAED,uGAAuG;AACvG,yFAAyF;AACzF,SAAgB,uBAAuB,CAAC,QAAkB;IACtD,MAAM,SAAS,GACX,OAAO,CAAC,WAAW,CAAC,0BAAa,CAAC,SAAS,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;IACjE,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC","sourcesContent":["import 'reflect-metadata';\nimport { METADATA_KEYS } from './decorators';\nimport { getWpMcpTools, WpMcpToolMetadata } 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// 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 if (declaresMcp && tools.length === 0) {\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, or drop MCP \" +\n '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/** 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, or drop MCP \" +\n '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"]}
@@ -37,6 +37,8 @@ export declare const METADATA_KEYS: {
37
37
  MASK_LOG: string;
38
38
  /** Per-method opt-in metadata for publishing an RPC endpoint as an MCP tool. */
39
39
  MCP_TOOLS: string;
40
+ /** Per-method PERMANENT exclusion from MCP, with the reason — see @InvalidEndpointForMcp. */
41
+ MCP_INVALID: string;
40
42
  /** Per-method request/response event metadata for a typed streaming endpoint. */
41
43
  STREAM_ENDPOINTS: string;
42
44
  /** Per-method MCP-user authorization, intentionally separate from HTTP hop auth. */
@@ -76,6 +76,8 @@ exports.METADATA_KEYS = {
76
76
  MASK_LOG: 'webpieces:mask-log',
77
77
  /** Per-method opt-in metadata for publishing an RPC endpoint as an MCP tool. */
78
78
  MCP_TOOLS: 'webpieces:mcp-tools',
79
+ /** Per-method PERMANENT exclusion from MCP, with the reason — see @InvalidEndpointForMcp. */
80
+ MCP_INVALID: 'webpieces:mcp-invalid',
79
81
  /** Per-method request/response event metadata for a typed streaming endpoint. */
80
82
  STREAM_ENDPOINTS: 'webpieces:stream-endpoints',
81
83
  /** Per-method MCP-user authorization, intentionally separate from HTTP hop auth. */
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/decorators.ts"],"names":[],"mappings":"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","sourcesContent":["import 'reflect-metadata';\nimport { MaskSpec, MaskMode } from './LogFieldMask';\nimport {\n DEFAULT_CALLER_KIND,\n ENDPOINT_CALLER_KEY,\n ExternalCaller,\n getEndpointCaller,\n} from './external-caller';\n// The TYPE layer these decorators attach — split out for file size only (see auth-mode.ts).\nimport { ApiKeyCredentials, AuthMeta, AuthMode, JwtRequirement } from './auth-mode';\nimport {\n EndpointOperation,\n EndpointOptions,\n ExternalEndpointOptions,\n READ,\n WRITE_IDEMPOTENT,\n WRITE,\n} from './HttpEndpointOptions';\nimport { GET, HttpMethod, POST } from './HttpContract';\nimport { HTTP_PARAMETERS_METADATA_KEY } from './http-parameter-decorators';\n\nexport type { EndpointOptions, ExternalEndpointOptions } from './HttpEndpointOptions';\nexport type { EndpointOperation } from './HttpEndpointOptions';\nexport { READ, WRITE_IDEMPOTENT, WRITE } from './HttpEndpointOptions';\nexport type { HttpMethod } from './HttpContract';\nexport { GET, POST } from './HttpContract';\nexport { PathParam, QueryParam, getHttpParameterDeclarations } from './http-parameter-decorators';\n\n/**\n * Metadata keys for storing API routing information.\n * These keys are used by both server-side (routing) and client-side (client generation).\n */\nexport const METADATA_KEYS = {\n API_PATH: 'webpieces:api-path',\n ENDPOINTS: 'webpieces:endpoints',\n AUTH_META: 'webpieces:auth-meta',\n /** Method names carrying one of the canonical authorization decorators. */\n AUTH_METHODS: 'webpieces:auth-methods',\n /** 'rpc' (default, sync request/response) vs 'pubsub' (fire-and-forget cloud task). */\n API_KIND: 'webpieces:api-kind',\n /** Per-method Cloud Tasks queue-name override (set via @Queue). */\n QUEUE_OVERRIDE: 'webpieces:queue-override',\n /** Per-method @Endpoint options (e.g. formPost), parallel to ENDPOINTS. */\n ENDPOINT_OPTIONS: 'webpieces:endpoint-options',\n /** Per-method required HTTP method, parallel to ENDPOINTS. */\n ENDPOINT_HTTP_METHOD: 'webpieces:endpoint-http-method',\n /** Per-method required read/write semantics, parallel to ENDPOINTS. */\n ENDPOINT_OPERATION: 'webpieces:endpoint-operation',\n /** Per-method @Endpoint trigger kind (rpc | cloudtasks | cron | external), parallel to ENDPOINTS. */\n ENDPOINT_KIND: 'webpieces:endpoint-kind',\n /** Per-method declared external CALLER (only for kind 'external'), parallel to ENDPOINTS. */\n ENDPOINT_CALLER: ENDPOINT_CALLER_KEY,\n /** Per-method @MaskLog spec (which DTO fields the LogApiCall path masks). */\n MASK_LOG: 'webpieces:mask-log',\n /** Per-method opt-in metadata for publishing an RPC endpoint as an MCP tool. */\n MCP_TOOLS: 'webpieces:mcp-tools',\n /** Per-method request/response event metadata for a typed streaming endpoint. */\n STREAM_ENDPOINTS: 'webpieces:stream-endpoints',\n /** Per-method MCP-user authorization, intentionally separate from HTTP hop auth. */\n MCP_AUTH_JWT: 'webpieces:mcp-auth-jwt',\n /** Per-method explicit path/query parameter declarations, keyed by parameter index. */\n HTTP_PARAMETERS: HTTP_PARAMETERS_METADATA_KEY,\n};\n\n/** Nominal backing keeps raw string literals out of endpoint declarations. */\nenum EndpointKindValue {\n RPC = 'rpc',\n CLOUDTASKS = 'cloudtasks',\n CRON = 'cron',\n EXTERNAL = 'external',\n}\n\n/** Short, statically importable decorator arguments. */\nexport const RPC = EndpointKindValue.RPC;\nexport const CLOUDTASKS = EndpointKindValue.CLOUDTASKS;\nexport const CRON = EndpointKindValue.CRON;\nexport const EXTERNAL = EndpointKindValue.EXTERNAL;\n\n/** Runtime trigger/delivery owner used by validation, architecture, and infrastructure tooling. */\nexport type EndpointKind = typeof RPC | typeof CLOUDTASKS | typeof CRON | typeof EXTERNAL;\n\n/** Class decorator that sets the shared base path for all endpoints. */\nexport function ApiPath(basePath: string): ClassDecorator {\n return (target: Function) => {\n if (Reflect.hasMetadata('webpieces:ipc-api-id', target)) {\n throw new Error(\n `Class ${target.name || 'Unknown'} cannot combine @ApiPath with @WpInternal.`,\n );\n }\n Reflect.defineMetadata(METADATA_KEYS.API_PATH, basePath, target);\n\n // Initialize endpoints map if not exists\n if (!Reflect.hasMetadata(METADATA_KEYS.ENDPOINTS, target)) {\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINTS, {}, target);\n }\n };\n}\n\n/**\n * Registers `@Endpoint(POST, path, WRITE, RPC)`. Method, operation, and trigger are required enum\n * values. Operation is independent of the HTTP verb. EXTERNAL additionally requires `calledBy`.\n * Each classification rides a parallel metadata map while ENDPOINTS remains method-name -> path.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function Endpoint(\n httpMethod: HttpMethod,\n path: string,\n operation: EndpointOperation,\n kind: typeof EXTERNAL,\n options: ExternalEndpointOptions,\n): MethodDecorator;\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function Endpoint(\n httpMethod: HttpMethod,\n path: string,\n operation: EndpointOperation,\n kind: Exclude<EndpointKind, typeof EXTERNAL>,\n options?: EndpointOptions,\n): MethodDecorator;\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function Endpoint(\n httpMethod: HttpMethod,\n path: string,\n operation: EndpointOperation,\n kind: EndpointKind,\n options: EndpointOptions = {},\n): MethodDecorator {\n validateEndpointClassification(httpMethod, operation, kind);\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any, propertyKey: string | symbol, _descriptor: PropertyDescriptor) => {\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n recordEndpointMetadata(\n metadataTarget,\n propertyKey as string,\n httpMethod,\n path,\n operation,\n kind,\n options,\n );\n };\n}\n\n// webpieces-disable no-function-outside-class -- runtime validation backs up nominal TS enums\nfunction validateEndpointClassification(\n httpMethod: HttpMethod,\n operation: EndpointOperation,\n kind: EndpointKind,\n): void {\n if (!isHttpMethod(httpMethod)) {\n throw new Error(\n `@Endpoint httpMethod must be GET or POST, received '${String(httpMethod)}'.`,\n );\n }\n if (!isEndpointOperation(operation)) {\n throw new Error(\n `@Endpoint operation must be READ, WRITE_IDEMPOTENT, or WRITE, received '${String(operation)}'.`,\n );\n }\n if (!isEndpointKind(kind)) {\n throw new Error(\n `@Endpoint trigger must be RPC, CLOUDTASKS, CRON, or EXTERNAL, received '${String(kind)}'.`,\n );\n }\n}\n\n// webpieces-disable no-function-outside-class -- one metadata write shared by the decorator overloads\nfunction recordEndpointMetadata(\n metadataTarget: Function,\n propertyKey: string,\n httpMethod: HttpMethod,\n path: string,\n operation: EndpointOperation,\n kind: EndpointKind,\n options: EndpointOptions,\n): void {\n const endpoints: Record<string, string> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINTS, metadataTarget) || {};\n\n endpoints[propertyKey] = path;\n\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINTS, endpoints, metadataTarget);\n\n const methods: Record<string, HttpMethod> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_HTTP_METHOD, metadataTarget) || {};\n methods[propertyKey] = httpMethod;\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINT_HTTP_METHOD, methods, metadataTarget);\n\n const operations: Record<string, EndpointOperation> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_OPERATION, metadataTarget) || {};\n operations[propertyKey] = operation;\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINT_OPERATION, operations, metadataTarget);\n\n const kinds: Record<string, EndpointKind> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_KIND, metadataTarget) || {};\n kinds[propertyKey] = kind;\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINT_KIND, kinds, metadataTarget);\n\n const opts: Record<string, EndpointOptions> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_OPTIONS, metadataTarget) || {};\n opts[propertyKey] = options;\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINT_OPTIONS, opts, metadataTarget);\n\n const declared = options as ExternalEndpointOptions;\n if (\n kind !== EXTERNAL ||\n typeof declared.calledBy !== 'string' ||\n declared.calledBy === ''\n )\n return;\n const callers: Record<string, ExternalCaller> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_CALLER, metadataTarget) || {};\n callers[propertyKey] = new ExternalCaller(\n declared.callerKind ?? DEFAULT_CALLER_KIND,\n declared.calledBy,\n );\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINT_CALLER, callers, metadataTarget);\n}\n\n/** Declares request/response DTO field names that API logging must mask at any depth. */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function MaskLog(fields: Record<string, MaskMode>): MethodDecorator {\n const spec = new MaskSpec(fields);\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any, propertyKey: string | symbol, _descriptor: PropertyDescriptor) => {\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n const specs: Record<string, MaskSpec> =\n Reflect.getMetadata(METADATA_KEYS.MASK_LOG, metadataTarget) || {};\n specs[propertyKey as string] = spec;\n Reflect.defineMetadata(METADATA_KEYS.MASK_LOG, specs, metadataTarget);\n };\n}\n\n/**\n * The @MaskLog spec for one method, or undefined if the method declared none (the common case — the\n * caller then logs the DTO verbatim on the plain JSON.stringify fast path).\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpointOptions\nexport function getMaskSpec(apiClass: Function, methodName: string): MaskSpec | undefined {\n const specs: Record<string, MaskSpec> =\n Reflect.getMetadata(METADATA_KEYS.MASK_LOG, apiClass) || {};\n return specs[methodName];\n}\n\n/**\n * Shared implementation for every auth decorator. Authorization is deliberately\n * method-only: a class-level default is too easy to miss when reviewing one endpoint.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nfunction defineAuthMode(mode: AuthMode): MethodDecorator {\n const authMeta = new AuthMeta(mode);\n\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any, propertyKey?: string | symbol, _descriptor?: PropertyDescriptor) => {\n if (propertyKey === undefined) {\n throw new Error(\n 'Authorization decorators are method-only; annotate every @Endpoint explicitly.',\n );\n }\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n if (Reflect.hasMetadata('webpieces:ipc-api-id', metadataTarget)) {\n throw new Error(\n `Internal API ${metadataTarget.name || 'Unknown'} cannot use HTTP authorization decorators.`,\n );\n }\n validateNoConflictingDecorators(metadataTarget, propertyKey as string);\n Reflect.defineMetadata(METADATA_KEYS.AUTH_META, authMeta, metadataTarget, propertyKey);\n const methods = new Set<string>(\n Reflect.getMetadata(METADATA_KEYS.AUTH_METHODS, metadataTarget) || [],\n );\n methods.add(propertyKey as string);\n Reflect.defineMetadata(METADATA_KEYS.AUTH_METHODS, [...methods], metadataTarget);\n };\n}\n\n/**\n * @WpAuthPublic(reason) - endpoint intentionally requires no authentication.\n * The non-empty reason makes anonymous exposure an explicit review decision.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function WpAuthPublic(reason: string): MethodDecorator {\n if (typeof reason !== 'string' || reason.trim() === '') {\n throw new Error(\n '@WpAuthPublic requires a non-empty reason explaining why anonymous access is required.',\n );\n }\n return defineAuthMode({ kind: 'public' });\n}\n\n/**\n * @WpAuthJwt(requirement) - THE user-facing JWT decorator, covering the whole user-JWT axis: the\n * compiler-enforced role decision ({@link JwtRoles}) plus app-defined fields ({@link JwtRequirement}).\n *\n * ```typescript\n * @WpAuthJwt({ roles: ['admin', 'editor'] }) // any-of\n * @WpAuthJwt({ allRolesAllowed: true, inOrg: true }) // wide + an app rule enforced by authorizeJwt\n * ```\n *\n * It absorbed the former `@Auth(requirement)` — same argument, same AuthMode, so two spellings of one\n * decision. One decorator per credential kind now: `@WpAuthPublic` / `@WpAuthJwt` / `@WpAuthOidc` /\n * `@WpAuthSharedSecret` / `@WpAuthLocalOnly`.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function WpAuthJwt(requirement: JwtRequirement): MethodDecorator {\n return defineAuthMode({ kind: 'jwt', requirement });\n}\n\n/**\n * The roles an endpoint accepts, or [] when it accepts every authenticated user. The ONE reader of\n * the {@link JwtRoles} union, so no caller has to re-derive \"does absent mean wide?\" — a question\n * whose two plausible answers is how the widest grant kept hiding behind an absent field.\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getAuthMode\nexport function rolesRequired(requirement: JwtRequirement): readonly string[] {\n return requirement.allRolesAllowed === true ? [] : requirement.roles;\n}\n\n/**\n * @WpAuthOidc(...callers) - Google OIDC service-to-service auth (Cloud Tasks delivery / cross-service\n * RPC). `callers` is an OPTIONAL app-level allow-list of caller service accounts.\n *\n * NO args = TRUST THE EDGE: accept any genuine Google-signed OIDC caller, because a PRIVATE Cloud\n * Run service's edge already gates WHO via `run.invoker` IAM (managed in terraform — one source of\n * truth, no hand-synced list in code). If the service is actually PUBLIC, the verifier logs a loud\n * warning (it can't be the gate then). Pass explicit SAs (`@WpAuthOidc('svc-a')`) only when you want\n * an additional app-level allow-list as defense-in-depth.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function WpAuthOidc(...callers: string[]): MethodDecorator {\n return defineAuthMode({ kind: 'oidc', callers });\n}\n\n/**\n * @WpAuthSharedSecret(key) - constant-time compare of an inbound header against the secret bound for\n * `key`. `key` is a LOOKUP KEY (not an env var): the server looks up its accepted {@link SharedSecrets}\n * by this key, and each client looks up the value it sends by the SAME key (see {@link Secrets}).\n * For internal callers that cannot mint OIDC tokens.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function WpAuthSharedSecret(key: string): MethodDecorator {\n return defineAuthMode({ kind: 'shared-secret', secretKey: key });\n}\n\n/**\n * @WpAuthWebhook(name) - an OUTSIDE vendor signed this request in its OWN scheme; the app's bound\n * `WebhookAuthCallback` proves it. THE mode for every signed inbound webhook — Sentry, GitHub, Stripe, Slack,\n * Twilio — none of which fits the other kinds: no vendor mints Google OIDC tokens, and none sends its\n * secret (they all send a DERIVATION over the request), so `@WpAuthPublic` was the only reachable posture\n * and `calledBy: 'sentry'` stayed a claim rather than a fact.\n *\n * ```typescript\n * @WpAuthWebhook('sentry')\n * @Endpoint(POST, '/hook/sentry/issue', WRITE, EXTERNAL, { calledBy: 'sentry', rawBody: true })\n * abstract notify(request: SentryIssueHook): Promise<HookAck>;\n * ```\n *\n * `name` is a bare STRING resolved through DI in the server's container, exactly as\n * `@WpAuthOidc('gmail-push')` already is — never a function reference. An api contract is level 0: a\n * direct reference to a verifier would invert the dependency graph and drag a vendor SDK into the\n * browser bundle that imports the same contract.\n *\n * THE FRAMEWORK IMPLEMENTS NO VENDOR CRYPTO, deliberately. Every vendor ships an official validator\n * (`twilio.validateRequest`, `stripe.webhooks.constructEvent`, `@octokit/webhooks-methods`) and every\n * vendor revises its scheme (Twilio added `bodySHA256` for JSON bodies; Stripe versions its header).\n * Reimplementing five of those is signing up to track five security changelogs forever and to be\n * wrong at the moment being wrong matters. The framework hands the hook enough of the raw request to\n * call the vendor's own library — hence the REQUIRED `{ rawBody: true }` (see\n * {@link EndpointOptions.rawBody}), which is checked at wiring time.\n *\n * FAILS CLOSED: with no `WebhookAuthCallback` bound, every `@WpAuthWebhook` endpoint 401s, matching `JwtHook`.\n * Silently allowing an unverified webhook is the one default that must not exist.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function WpAuthWebhook(name: string): MethodDecorator {\n return defineAuthMode({ kind: 'webhook', name });\n}\n\n/**\n * Declares a customer-held API-key regime and its non-empty ordered credential locations. The app's\n * `ApiKeyHook` validates the whole request and supplies trusted context; the declaration only drives\n * contract/spec metadata. Unlike shared-secret auth, partner callers cannot assert trusted context.\n * Missing hooks fail closed with 401.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function WpAuthApiKey(regime: string, credentials: ApiKeyCredentials): MethodDecorator {\n return defineAuthMode({ kind: 'apikey', regime, credentials });\n}\n\n/**\n * @WpAuthLocalOnly() - this endpoint exists ONLY on a developer's machine. Off-local it is not\n * registered as a route at all, and if it is somehow reached it 404s. Method-only, like every\n * authorization decorator, so every endpoint's posture is visible at the method.\n *\n * ```typescript\n * @WpAuthLocalOnly()\n * @Endpoint(POST, '/logs', WRITE, RPC)\n * sendBatch(request: SendLogBatchRequest): Promise<SendLogBatchResponse> { ... }\n * ```\n *\n * WHY IT IS AN AUTH MODE AND NOT A ROUTE-MODULE `if`. Apps hand-rolled this in TWO places kept in\n * sync by a comment: a route module that registered the route only locally, PLUS a\n * `if (env !== 'local') throw new ApiForbiddenError(...)` at the top of the handler. Neither half\n * was visible on the CONTRACT, so nothing reading the api — a human, a generated client, or an\n * agent — could tell this endpoint from a `@WpAuthPublic` one. Both halves are the framework's job now,\n * driven by this ONE declaration on the contract, which is where every other \"who may call this\"\n * fact already lives.\n *\n * It is DELIBERATELY a peer of @WpAuthPublic / @WpAuthJwt / @WpAuthOidc / @WpAuthSharedSecret / @WpAuthApiKey rather than an\n * option on one of them: one decorator per credential kind, and \"local-only\" is a different kind of\n * gate — it authenticates nobody, it excludes an entire environment.\n *\n * HOW \"local\" IS DECIDED: {@link RuntimeLocality}, declared once at startup (a REQUIRED input to\n * `RuntimeSetupOptions`). Undeclared means DEPLOYED, so a forgotten wiring call refuses the endpoint\n * rather than exposing it.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function WpAuthLocalOnly(): MethodDecorator {\n return defineAuthMode({ kind: 'local-only' });\n}\n\n// ============================================================\n// Helper functions\n// ============================================================\n\n/**\n * Get the base path from @ApiPath decorator.\n */\nexport function getApiPath(apiClass: Function): string | undefined {\n return Reflect.getMetadata(METADATA_KEYS.API_PATH, apiClass);\n}\n\n/**\n * Get all endpoints from @Endpoint decorators.\n * Returns a record of methodName -> endpoint path.\n */\nexport function getEndpoints(apiClass: Function): Record<string, string> | undefined {\n return Reflect.getMetadata(METADATA_KEYS.ENDPOINTS, apiClass);\n}\n\n/**\n * Every method's declared trigger kind, as `methodName -> kind`. Parallel to {@link getEndpoints}.\n * Empty for a class carrying no @Endpoint at all.\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpoints\nexport function getEndpointKinds(apiClass: Function): Record<string, EndpointKind> {\n return Reflect.getMetadata(METADATA_KEYS.ENDPOINT_KIND, apiClass) || {};\n}\n\n/**\n * What triggers ONE method, or undefined when the method carries no @Endpoint.\n *\n * Defaults to nothing rather than to 'rpc': `kind` is a required argument, so a missing entry means\n * \"this is not an endpoint\", never \"an endpoint that forgot to say\". Silently defaulting here would\n * put an undeclared cron or webhook back into the graph as a normal rpc call — the exact blindness\n * the required argument exists to remove.\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpoints\nexport function getEndpointKind(apiClass: Function, methodName: string): EndpointKind | undefined {\n return getEndpointKinds(apiClass)[methodName];\n}\n\n/** The required HTTP method for one endpoint. */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpointKind\nexport function getEndpointHttpMethod(apiClass: Function, methodName: string): HttpMethod {\n const methods: Record<string, HttpMethod> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_HTTP_METHOD, apiClass) || {};\n const method = methods[methodName];\n if (!isHttpMethod(method)) {\n throw new Error(\n `@Endpoint ${apiClass.name || 'Unknown'}.${methodName} must declare GET or POST.`,\n );\n }\n return method;\n}\n\n/**\n * Get the @Endpoint options for one method (empty object if the method had no options).\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpoints\nexport function getEndpointOptions(apiClass: Function, methodName: string): EndpointOptions {\n const opts: Record<string, EndpointOptions> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_OPTIONS, apiClass) || {};\n const options = opts[methodName];\n if (!options && getEndpoints(apiClass)?.[methodName] === undefined) {\n // Preserve the historical helper behavior for a method that is not an endpoint at all.\n return {} as EndpointOptions;\n }\n return options ?? {};\n}\n\n/** Read one endpoint's required side-effect semantics. */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpointOptions\nexport function getEndpointOperation(apiClass: Function, methodName: string): EndpointOperation {\n const operations: Record<string, EndpointOperation> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_OPERATION, apiClass) || {};\n const operation = operations[methodName];\n if (!isEndpointOperation(operation)) {\n throw new Error(\n `@Endpoint ${apiClass.name || 'Unknown'}.${methodName} must declare READ, ` +\n 'WRITE_IDEMPOTENT, or WRITE.',\n );\n }\n return operation;\n}\n\n// webpieces-disable no-function-outside-class -- runtime backstop for JavaScript and `as any` callers; webpieces-disable no-any-unknown -- untrusted runtime value is narrowed to an endpoint operation\nfunction isEndpointOperation(value: unknown): value is EndpointOperation {\n return (\n value === READ ||\n value === WRITE_IDEMPOTENT ||\n value === WRITE\n );\n}\n\n// webpieces-disable no-function-outside-class -- runtime backstop for JavaScript and `as any` callers; webpieces-disable no-any-unknown -- untrusted runtime value is narrowed to an HTTP method\nfunction isHttpMethod(value: unknown): value is HttpMethod {\n return value === GET || value === POST;\n}\n\n// webpieces-disable no-function-outside-class -- runtime backstop for JavaScript and `as any` callers; webpieces-disable no-any-unknown -- untrusted runtime value is narrowed to an endpoint kind\nfunction isEndpointKind(value: unknown): value is EndpointKind {\n return (\n value === RPC ||\n value === CLOUDTASKS ||\n value === CRON ||\n value === EXTERNAL\n );\n}\n\n/**\n * Fail-fast at wiring time when an `external` endpoint declared no caller. The {@link Endpoint}\n * overloads already make that a COMPILE error; this is the backstop for the ways TS is bypassed —\n * a JS caller, an `as any` options object, a hand-rolled Reflect.defineMetadata.\n * @throws Error naming the first external endpoint with no `calledBy`.\n */\n// webpieces-disable no-function-outside-class -- wiring-time assert, sibling of assertEveryEndpointHasAuthMode\nexport function assertEveryExternalEndpointDeclaresCaller(apiClass: Function): void {\n const kinds = getEndpointKinds(apiClass);\n for (const methodName of Object.keys(kinds)) {\n if (\n kinds[methodName] !== 'external' ||\n getEndpointCaller(apiClass, methodName) !== undefined\n )\n continue;\n throw new Error(\n `External endpoint '${methodName}' in ${apiClass.name || 'Unknown'} declares no caller. Say WHO ` +\n `posts to it: @Endpoint(POST, path, WRITE, EXTERNAL, { calledBy: '<vendor>' }) — the runtime architecture ` +\n `graph cannot name an inbound caller it was never told about.`,\n );\n }\n}\n\n/**\n * True when the method's @Endpoint declared `{ formPost: true }` — its body is\n * application/x-www-form-urlencoded (flat), not JSON.\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpoints\nexport function isFormPost(apiClass: Function, methodName: string): boolean {\n return getEndpointOptions(apiClass, methodName).formPost === true;\n}\n\n/**\n * True when the method's @Endpoint declared `{ rawBody: true }` — the transport must retain the\n * verbatim bytes + absolute url for an {@link WpAuthWebhook} hook to verify.\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of isFormPost\nexport function isRawBody(apiClass: Function, methodName: string): boolean {\n return getEndpointOptions(apiClass, methodName).rawBody === true;\n}\n\n/**\n * Fail-fast at wiring time when an `@WpAuthWebhook` endpoint did not ask the transport to keep the\n * bytes it is supposed to verify. A hook with nothing to verify is a MISCONFIGURATION, and it must\n * surface at startup, naming the fix — not as a 401 in production on exactly the traffic the endpoint\n * exists for.\n *\n * This pairing is a runtime assert rather than a type because the two halves live on DIFFERENT\n * decorators (`@WpAuthWebhook` and `@Endpoint`), and no union over one decorator's argument can say\n * anything about the other's.\n *\n * @throws Error naming the first `@WpAuthWebhook` endpoint missing `{ rawBody: true }`.\n */\n// webpieces-disable no-function-outside-class -- wiring-time assert, sibling of assertEveryEndpointHasAuthMode\nexport function assertEveryWebhookEndpointRetainsRawBody(apiClass: Function): void {\n const endpoints = getEndpoints(apiClass) || {};\n for (const methodName of Object.keys(endpoints)) {\n if (\n getAuthMode(apiClass, methodName)?.kind !== 'webhook' ||\n isRawBody(apiClass, methodName)\n )\n continue;\n throw new Error(\n `Endpoint '${methodName}' in ${apiClass.name || 'Unknown'} is @WpAuthWebhook but its @Endpoint ` +\n `does not declare { rawBody: true }. A webhook hook verifies a signature over the bytes and ` +\n `the url the SENDER transmitted, and without that option the transport parses the body and ` +\n `throws them away — leaving the hook nothing to check.`,\n );\n }\n}\n\n/**\n * Check if a class has @ApiPath decorator.\n */\nexport function isApiPath(apiClass: Function): boolean {\n return Reflect.hasMetadata(METADATA_KEYS.API_PATH, apiClass);\n}\n\n/**\n * Get method-level auth metadata. Class-level authorization is forbidden.\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader paired with the decorator API\nexport function getAuthMeta(apiClass: Function, methodName: string): AuthMeta | undefined {\n return Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass, methodName);\n}\n\n/**\n * Get the auth mode for a method, or undefined.\n * Convenience wrapper over getAuthMeta for callers that only want the mode.\n */\n// webpieces-disable no-function-outside-class -- typed convenience reader over getAuthMeta\nexport function getAuthMode(apiClass: Function, methodName: string): AuthMode | undefined {\n return getAuthMeta(apiClass, methodName)?.mode;\n}\n\n/**\n * The ONE prescription for \"this endpoint declares no auth\", shared by the two places that raise it\n * (here and http-routing's ApiRoutingFactory) because they had drifted into teaching different menus.\n * A message teaching an incomplete API is the same defect as an API with two spellings: whichever menu\n * the caller hits becomes the API they believe exists. It leads with the ROLE-GATED member on purpose —\n * the first thing offered should not be the widest grant.\n */\nexport const MISSING_AUTH_DECORATOR_FIX =\n \"Add one of @WpAuthJwt({roles: ['admin']}) / @WpAuthJwt({allRolesAllowed: true}) / \" +\n '@WpAuthOidc(...callers) / @WpAuthSharedSecret(key) / ' +\n \"@WpAuthWebhook('vendor') / @WpAuthApiKey('regime', [{in: 'header', name: 'x-api-key'}]) / \" +\n \"@WpAuthLocalOnly() / @WpAuthPublic('why anonymous access is required') to \" +\n 'the method.';\n\n/**\n * Fail-fast at wiring time if any endpoint lacks an auth mode. Both the server\n * (ApiRoutingFactory) and the task/rpc clients call this so a missing auth\n * decorator is a startup error, never a silent open endpoint.\n * @throws Error naming the first endpoint with no auth decorator, via {@link MISSING_AUTH_DECORATOR_FIX}.\n */\nexport function assertEveryEndpointHasAuthMode(apiClass: Function): void {\n const apiName = apiClass.name || 'Unknown';\n const endpoints = getEndpoints(apiClass) || {};\n for (const methodName of Object.keys(endpoints)) {\n if (!getAuthMeta(apiClass, methodName)) {\n throw new Error(\n `Endpoint '${methodName}' in ${apiName} has no auth decorator. ` +\n MISSING_AUTH_DECORATOR_FIX,\n );\n }\n }\n}\n\n/**\n * Validate that a class/method doesn't have conflicting auth decorators.\n * @throws Error if multiple auth decorators are found on the same target.\n */\n// webpieces-disable no-function-outside-class -- shared decorator metadata validation\nexport function validateNoConflictingDecorators(\n apiClass: Function,\n methodName: string | undefined,\n): void {\n const existing = methodName\n ? Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass, methodName)\n : Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass);\n\n if (existing) {\n const targetName = apiClass.name || 'Unknown';\n const location = methodName\n ? `method '${methodName}' of ${targetName}`\n : `class ${targetName}`;\n throw new Error(\n `Conflicting auth decorator on ${location}. ` +\n `Only one of @WpAuthJwt({...}) / @WpAuthOidc(...) / @WpAuthSharedSecret(...) / ` +\n `@WpAuthWebhook(...) / @WpAuthApiKey(...) / @WpAuthLocalOnly() / ` +\n `@WpAuthPublic('reason') is allowed per target.`,\n );\n }\n}\n"]}
1
+ {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/decorators.ts"],"names":[],"mappings":"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","sourcesContent":["import 'reflect-metadata';\nimport { MaskSpec, MaskMode } from './LogFieldMask';\nimport {\n DEFAULT_CALLER_KIND,\n ENDPOINT_CALLER_KEY,\n ExternalCaller,\n getEndpointCaller,\n} from './external-caller';\n// The TYPE layer these decorators attach — split out for file size only (see auth-mode.ts).\nimport { ApiKeyCredentials, AuthMeta, AuthMode, JwtRequirement } from './auth-mode';\nimport {\n EndpointOperation,\n EndpointOptions,\n ExternalEndpointOptions,\n READ,\n WRITE_IDEMPOTENT,\n WRITE,\n} from './HttpEndpointOptions';\nimport { GET, HttpMethod, POST } from './HttpContract';\nimport { HTTP_PARAMETERS_METADATA_KEY } from './http-parameter-decorators';\n\nexport type { EndpointOptions, ExternalEndpointOptions } from './HttpEndpointOptions';\nexport type { EndpointOperation } from './HttpEndpointOptions';\nexport { READ, WRITE_IDEMPOTENT, WRITE } from './HttpEndpointOptions';\nexport type { HttpMethod } from './HttpContract';\nexport { GET, POST } from './HttpContract';\nexport { PathParam, QueryParam, getHttpParameterDeclarations } from './http-parameter-decorators';\n\n/**\n * Metadata keys for storing API routing information.\n * These keys are used by both server-side (routing) and client-side (client generation).\n */\nexport const METADATA_KEYS = {\n API_PATH: 'webpieces:api-path',\n ENDPOINTS: 'webpieces:endpoints',\n AUTH_META: 'webpieces:auth-meta',\n /** Method names carrying one of the canonical authorization decorators. */\n AUTH_METHODS: 'webpieces:auth-methods',\n /** 'rpc' (default, sync request/response) vs 'pubsub' (fire-and-forget cloud task). */\n API_KIND: 'webpieces:api-kind',\n /** Per-method Cloud Tasks queue-name override (set via @Queue). */\n QUEUE_OVERRIDE: 'webpieces:queue-override',\n /** Per-method @Endpoint options (e.g. formPost), parallel to ENDPOINTS. */\n ENDPOINT_OPTIONS: 'webpieces:endpoint-options',\n /** Per-method required HTTP method, parallel to ENDPOINTS. */\n ENDPOINT_HTTP_METHOD: 'webpieces:endpoint-http-method',\n /** Per-method required read/write semantics, parallel to ENDPOINTS. */\n ENDPOINT_OPERATION: 'webpieces:endpoint-operation',\n /** Per-method @Endpoint trigger kind (rpc | cloudtasks | cron | external), parallel to ENDPOINTS. */\n ENDPOINT_KIND: 'webpieces:endpoint-kind',\n /** Per-method declared external CALLER (only for kind 'external'), parallel to ENDPOINTS. */\n ENDPOINT_CALLER: ENDPOINT_CALLER_KEY,\n /** Per-method @MaskLog spec (which DTO fields the LogApiCall path masks). */\n MASK_LOG: 'webpieces:mask-log',\n /** Per-method opt-in metadata for publishing an RPC endpoint as an MCP tool. */\n MCP_TOOLS: 'webpieces:mcp-tools',\n /** Per-method PERMANENT exclusion from MCP, with the reason — see @InvalidEndpointForMcp. */\n MCP_INVALID: 'webpieces:mcp-invalid',\n /** Per-method request/response event metadata for a typed streaming endpoint. */\n STREAM_ENDPOINTS: 'webpieces:stream-endpoints',\n /** Per-method MCP-user authorization, intentionally separate from HTTP hop auth. */\n MCP_AUTH_JWT: 'webpieces:mcp-auth-jwt',\n /** Per-method explicit path/query parameter declarations, keyed by parameter index. */\n HTTP_PARAMETERS: HTTP_PARAMETERS_METADATA_KEY,\n};\n\n/** Nominal backing keeps raw string literals out of endpoint declarations. */\nenum EndpointKindValue {\n RPC = 'rpc',\n CLOUDTASKS = 'cloudtasks',\n CRON = 'cron',\n EXTERNAL = 'external',\n}\n\n/** Short, statically importable decorator arguments. */\nexport const RPC = EndpointKindValue.RPC;\nexport const CLOUDTASKS = EndpointKindValue.CLOUDTASKS;\nexport const CRON = EndpointKindValue.CRON;\nexport const EXTERNAL = EndpointKindValue.EXTERNAL;\n\n/** Runtime trigger/delivery owner used by validation, architecture, and infrastructure tooling. */\nexport type EndpointKind = typeof RPC | typeof CLOUDTASKS | typeof CRON | typeof EXTERNAL;\n\n/** Class decorator that sets the shared base path for all endpoints. */\nexport function ApiPath(basePath: string): ClassDecorator {\n return (target: Function) => {\n if (Reflect.hasMetadata('webpieces:ipc-api-id', target)) {\n throw new Error(\n `Class ${target.name || 'Unknown'} cannot combine @ApiPath with @WpInternal.`,\n );\n }\n Reflect.defineMetadata(METADATA_KEYS.API_PATH, basePath, target);\n\n // Initialize endpoints map if not exists\n if (!Reflect.hasMetadata(METADATA_KEYS.ENDPOINTS, target)) {\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINTS, {}, target);\n }\n };\n}\n\n/**\n * Registers `@Endpoint(POST, path, WRITE, RPC)`. Method, operation, and trigger are required enum\n * values. Operation is independent of the HTTP verb. EXTERNAL additionally requires `calledBy`.\n * Each classification rides a parallel metadata map while ENDPOINTS remains method-name -> path.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function Endpoint(\n httpMethod: HttpMethod,\n path: string,\n operation: EndpointOperation,\n kind: typeof EXTERNAL,\n options: ExternalEndpointOptions,\n): MethodDecorator;\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function Endpoint(\n httpMethod: HttpMethod,\n path: string,\n operation: EndpointOperation,\n kind: Exclude<EndpointKind, typeof EXTERNAL>,\n options?: EndpointOptions,\n): MethodDecorator;\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function Endpoint(\n httpMethod: HttpMethod,\n path: string,\n operation: EndpointOperation,\n kind: EndpointKind,\n options: EndpointOptions = {},\n): MethodDecorator {\n validateEndpointClassification(httpMethod, operation, kind);\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any, propertyKey: string | symbol, _descriptor: PropertyDescriptor) => {\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n recordEndpointMetadata(\n metadataTarget,\n propertyKey as string,\n httpMethod,\n path,\n operation,\n kind,\n options,\n );\n };\n}\n\n// webpieces-disable no-function-outside-class -- runtime validation backs up nominal TS enums\nfunction validateEndpointClassification(\n httpMethod: HttpMethod,\n operation: EndpointOperation,\n kind: EndpointKind,\n): void {\n if (!isHttpMethod(httpMethod)) {\n throw new Error(\n `@Endpoint httpMethod must be GET or POST, received '${String(httpMethod)}'.`,\n );\n }\n if (!isEndpointOperation(operation)) {\n throw new Error(\n `@Endpoint operation must be READ, WRITE_IDEMPOTENT, or WRITE, received '${String(operation)}'.`,\n );\n }\n if (!isEndpointKind(kind)) {\n throw new Error(\n `@Endpoint trigger must be RPC, CLOUDTASKS, CRON, or EXTERNAL, received '${String(kind)}'.`,\n );\n }\n}\n\n// webpieces-disable no-function-outside-class -- one metadata write shared by the decorator overloads\nfunction recordEndpointMetadata(\n metadataTarget: Function,\n propertyKey: string,\n httpMethod: HttpMethod,\n path: string,\n operation: EndpointOperation,\n kind: EndpointKind,\n options: EndpointOptions,\n): void {\n const endpoints: Record<string, string> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINTS, metadataTarget) || {};\n\n endpoints[propertyKey] = path;\n\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINTS, endpoints, metadataTarget);\n\n const methods: Record<string, HttpMethod> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_HTTP_METHOD, metadataTarget) || {};\n methods[propertyKey] = httpMethod;\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINT_HTTP_METHOD, methods, metadataTarget);\n\n const operations: Record<string, EndpointOperation> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_OPERATION, metadataTarget) || {};\n operations[propertyKey] = operation;\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINT_OPERATION, operations, metadataTarget);\n\n const kinds: Record<string, EndpointKind> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_KIND, metadataTarget) || {};\n kinds[propertyKey] = kind;\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINT_KIND, kinds, metadataTarget);\n\n const opts: Record<string, EndpointOptions> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_OPTIONS, metadataTarget) || {};\n opts[propertyKey] = options;\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINT_OPTIONS, opts, metadataTarget);\n\n const declared = options as ExternalEndpointOptions;\n if (\n kind !== EXTERNAL ||\n typeof declared.calledBy !== 'string' ||\n declared.calledBy === ''\n )\n return;\n const callers: Record<string, ExternalCaller> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_CALLER, metadataTarget) || {};\n callers[propertyKey] = new ExternalCaller(\n declared.callerKind ?? DEFAULT_CALLER_KIND,\n declared.calledBy,\n );\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINT_CALLER, callers, metadataTarget);\n}\n\n/** Declares request/response DTO field names that API logging must mask at any depth. */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function MaskLog(fields: Record<string, MaskMode>): MethodDecorator {\n const spec = new MaskSpec(fields);\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any, propertyKey: string | symbol, _descriptor: PropertyDescriptor) => {\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n const specs: Record<string, MaskSpec> =\n Reflect.getMetadata(METADATA_KEYS.MASK_LOG, metadataTarget) || {};\n specs[propertyKey as string] = spec;\n Reflect.defineMetadata(METADATA_KEYS.MASK_LOG, specs, metadataTarget);\n };\n}\n\n/**\n * The @MaskLog spec for one method, or undefined if the method declared none (the common case — the\n * caller then logs the DTO verbatim on the plain JSON.stringify fast path).\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpointOptions\nexport function getMaskSpec(apiClass: Function, methodName: string): MaskSpec | undefined {\n const specs: Record<string, MaskSpec> =\n Reflect.getMetadata(METADATA_KEYS.MASK_LOG, apiClass) || {};\n return specs[methodName];\n}\n\n/**\n * Shared implementation for every auth decorator. Authorization is deliberately\n * method-only: a class-level default is too easy to miss when reviewing one endpoint.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nfunction defineAuthMode(mode: AuthMode): MethodDecorator {\n const authMeta = new AuthMeta(mode);\n\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any, propertyKey?: string | symbol, _descriptor?: PropertyDescriptor) => {\n if (propertyKey === undefined) {\n throw new Error(\n 'Authorization decorators are method-only; annotate every @Endpoint explicitly.',\n );\n }\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n if (Reflect.hasMetadata('webpieces:ipc-api-id', metadataTarget)) {\n throw new Error(\n `Internal API ${metadataTarget.name || 'Unknown'} cannot use HTTP authorization decorators.`,\n );\n }\n validateNoConflictingDecorators(metadataTarget, propertyKey as string);\n Reflect.defineMetadata(METADATA_KEYS.AUTH_META, authMeta, metadataTarget, propertyKey);\n const methods = new Set<string>(\n Reflect.getMetadata(METADATA_KEYS.AUTH_METHODS, metadataTarget) || [],\n );\n methods.add(propertyKey as string);\n Reflect.defineMetadata(METADATA_KEYS.AUTH_METHODS, [...methods], metadataTarget);\n };\n}\n\n/**\n * @WpAuthPublic(reason) - endpoint intentionally requires no authentication.\n * The non-empty reason makes anonymous exposure an explicit review decision.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function WpAuthPublic(reason: string): MethodDecorator {\n if (typeof reason !== 'string' || reason.trim() === '') {\n throw new Error(\n '@WpAuthPublic requires a non-empty reason explaining why anonymous access is required.',\n );\n }\n return defineAuthMode({ kind: 'public' });\n}\n\n/**\n * @WpAuthJwt(requirement) - THE user-facing JWT decorator, covering the whole user-JWT axis: the\n * compiler-enforced role decision ({@link JwtRoles}) plus app-defined fields ({@link JwtRequirement}).\n *\n * ```typescript\n * @WpAuthJwt({ roles: ['admin', 'editor'] }) // any-of\n * @WpAuthJwt({ allRolesAllowed: true, inOrg: true }) // wide + an app rule enforced by authorizeJwt\n * ```\n *\n * It absorbed the former `@Auth(requirement)` — same argument, same AuthMode, so two spellings of one\n * decision. One decorator per credential kind now: `@WpAuthPublic` / `@WpAuthJwt` / `@WpAuthOidc` /\n * `@WpAuthSharedSecret` / `@WpAuthLocalOnly`.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function WpAuthJwt(requirement: JwtRequirement): MethodDecorator {\n return defineAuthMode({ kind: 'jwt', requirement });\n}\n\n/**\n * The roles an endpoint accepts, or [] when it accepts every authenticated user. The ONE reader of\n * the {@link JwtRoles} union, so no caller has to re-derive \"does absent mean wide?\" — a question\n * whose two plausible answers is how the widest grant kept hiding behind an absent field.\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getAuthMode\nexport function rolesRequired(requirement: JwtRequirement): readonly string[] {\n return requirement.allRolesAllowed === true ? [] : requirement.roles;\n}\n\n/**\n * @WpAuthOidc(...callers) - Google OIDC service-to-service auth (Cloud Tasks delivery / cross-service\n * RPC). `callers` is an OPTIONAL app-level allow-list of caller service accounts.\n *\n * NO args = TRUST THE EDGE: accept any genuine Google-signed OIDC caller, because a PRIVATE Cloud\n * Run service's edge already gates WHO via `run.invoker` IAM (managed in terraform — one source of\n * truth, no hand-synced list in code). If the service is actually PUBLIC, the verifier logs a loud\n * warning (it can't be the gate then). Pass explicit SAs (`@WpAuthOidc('svc-a')`) only when you want\n * an additional app-level allow-list as defense-in-depth.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function WpAuthOidc(...callers: string[]): MethodDecorator {\n return defineAuthMode({ kind: 'oidc', callers });\n}\n\n/**\n * @WpAuthSharedSecret(key) - constant-time compare of an inbound header against the secret bound for\n * `key`. `key` is a LOOKUP KEY (not an env var): the server looks up its accepted {@link SharedSecrets}\n * by this key, and each client looks up the value it sends by the SAME key (see {@link Secrets}).\n * For internal callers that cannot mint OIDC tokens.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function WpAuthSharedSecret(key: string): MethodDecorator {\n return defineAuthMode({ kind: 'shared-secret', secretKey: key });\n}\n\n/**\n * @WpAuthWebhook(name) - an OUTSIDE vendor signed this request in its OWN scheme; the app's bound\n * `WebhookAuthCallback` proves it. THE mode for every signed inbound webhook — Sentry, GitHub, Stripe, Slack,\n * Twilio — none of which fits the other kinds: no vendor mints Google OIDC tokens, and none sends its\n * secret (they all send a DERIVATION over the request), so `@WpAuthPublic` was the only reachable posture\n * and `calledBy: 'sentry'` stayed a claim rather than a fact.\n *\n * ```typescript\n * @WpAuthWebhook('sentry')\n * @Endpoint(POST, '/hook/sentry/issue', WRITE, EXTERNAL, { calledBy: 'sentry', rawBody: true })\n * abstract notify(request: SentryIssueHook): Promise<HookAck>;\n * ```\n *\n * `name` is a bare STRING resolved through DI in the server's container, exactly as\n * `@WpAuthOidc('gmail-push')` already is — never a function reference. An api contract is level 0: a\n * direct reference to a verifier would invert the dependency graph and drag a vendor SDK into the\n * browser bundle that imports the same contract.\n *\n * THE FRAMEWORK IMPLEMENTS NO VENDOR CRYPTO, deliberately. Every vendor ships an official validator\n * (`twilio.validateRequest`, `stripe.webhooks.constructEvent`, `@octokit/webhooks-methods`) and every\n * vendor revises its scheme (Twilio added `bodySHA256` for JSON bodies; Stripe versions its header).\n * Reimplementing five of those is signing up to track five security changelogs forever and to be\n * wrong at the moment being wrong matters. The framework hands the hook enough of the raw request to\n * call the vendor's own library — hence the REQUIRED `{ rawBody: true }` (see\n * {@link EndpointOptions.rawBody}), which is checked at wiring time.\n *\n * FAILS CLOSED: with no `WebhookAuthCallback` bound, every `@WpAuthWebhook` endpoint 401s, matching `JwtHook`.\n * Silently allowing an unverified webhook is the one default that must not exist.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function WpAuthWebhook(name: string): MethodDecorator {\n return defineAuthMode({ kind: 'webhook', name });\n}\n\n/**\n * Declares a customer-held API-key regime and its non-empty ordered credential locations. The app's\n * `ApiKeyHook` validates the whole request and supplies trusted context; the declaration only drives\n * contract/spec metadata. Unlike shared-secret auth, partner callers cannot assert trusted context.\n * Missing hooks fail closed with 401.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function WpAuthApiKey(regime: string, credentials: ApiKeyCredentials): MethodDecorator {\n return defineAuthMode({ kind: 'apikey', regime, credentials });\n}\n\n/**\n * @WpAuthLocalOnly() - this endpoint exists ONLY on a developer's machine. Off-local it is not\n * registered as a route at all, and if it is somehow reached it 404s. Method-only, like every\n * authorization decorator, so every endpoint's posture is visible at the method.\n *\n * ```typescript\n * @WpAuthLocalOnly()\n * @Endpoint(POST, '/logs', WRITE, RPC)\n * sendBatch(request: SendLogBatchRequest): Promise<SendLogBatchResponse> { ... }\n * ```\n *\n * WHY IT IS AN AUTH MODE AND NOT A ROUTE-MODULE `if`. Apps hand-rolled this in TWO places kept in\n * sync by a comment: a route module that registered the route only locally, PLUS a\n * `if (env !== 'local') throw new ApiForbiddenError(...)` at the top of the handler. Neither half\n * was visible on the CONTRACT, so nothing reading the api — a human, a generated client, or an\n * agent — could tell this endpoint from a `@WpAuthPublic` one. Both halves are the framework's job now,\n * driven by this ONE declaration on the contract, which is where every other \"who may call this\"\n * fact already lives.\n *\n * It is DELIBERATELY a peer of @WpAuthPublic / @WpAuthJwt / @WpAuthOidc / @WpAuthSharedSecret / @WpAuthApiKey rather than an\n * option on one of them: one decorator per credential kind, and \"local-only\" is a different kind of\n * gate — it authenticates nobody, it excludes an entire environment.\n *\n * HOW \"local\" IS DECIDED: {@link RuntimeLocality}, declared once at startup (a REQUIRED input to\n * `RuntimeSetupOptions`). Undeclared means DEPLOYED, so a forgotten wiring call refuses the endpoint\n * rather than exposing it.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function WpAuthLocalOnly(): MethodDecorator {\n return defineAuthMode({ kind: 'local-only' });\n}\n\n// ============================================================\n// Helper functions\n// ============================================================\n\n/**\n * Get the base path from @ApiPath decorator.\n */\nexport function getApiPath(apiClass: Function): string | undefined {\n return Reflect.getMetadata(METADATA_KEYS.API_PATH, apiClass);\n}\n\n/**\n * Get all endpoints from @Endpoint decorators.\n * Returns a record of methodName -> endpoint path.\n */\nexport function getEndpoints(apiClass: Function): Record<string, string> | undefined {\n return Reflect.getMetadata(METADATA_KEYS.ENDPOINTS, apiClass);\n}\n\n/**\n * Every method's declared trigger kind, as `methodName -> kind`. Parallel to {@link getEndpoints}.\n * Empty for a class carrying no @Endpoint at all.\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpoints\nexport function getEndpointKinds(apiClass: Function): Record<string, EndpointKind> {\n return Reflect.getMetadata(METADATA_KEYS.ENDPOINT_KIND, apiClass) || {};\n}\n\n/**\n * What triggers ONE method, or undefined when the method carries no @Endpoint.\n *\n * Defaults to nothing rather than to 'rpc': `kind` is a required argument, so a missing entry means\n * \"this is not an endpoint\", never \"an endpoint that forgot to say\". Silently defaulting here would\n * put an undeclared cron or webhook back into the graph as a normal rpc call — the exact blindness\n * the required argument exists to remove.\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpoints\nexport function getEndpointKind(apiClass: Function, methodName: string): EndpointKind | undefined {\n return getEndpointKinds(apiClass)[methodName];\n}\n\n/** The required HTTP method for one endpoint. */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpointKind\nexport function getEndpointHttpMethod(apiClass: Function, methodName: string): HttpMethod {\n const methods: Record<string, HttpMethod> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_HTTP_METHOD, apiClass) || {};\n const method = methods[methodName];\n if (!isHttpMethod(method)) {\n throw new Error(\n `@Endpoint ${apiClass.name || 'Unknown'}.${methodName} must declare GET or POST.`,\n );\n }\n return method;\n}\n\n/**\n * Get the @Endpoint options for one method (empty object if the method had no options).\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpoints\nexport function getEndpointOptions(apiClass: Function, methodName: string): EndpointOptions {\n const opts: Record<string, EndpointOptions> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_OPTIONS, apiClass) || {};\n const options = opts[methodName];\n if (!options && getEndpoints(apiClass)?.[methodName] === undefined) {\n // Preserve the historical helper behavior for a method that is not an endpoint at all.\n return {} as EndpointOptions;\n }\n return options ?? {};\n}\n\n/** Read one endpoint's required side-effect semantics. */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpointOptions\nexport function getEndpointOperation(apiClass: Function, methodName: string): EndpointOperation {\n const operations: Record<string, EndpointOperation> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_OPERATION, apiClass) || {};\n const operation = operations[methodName];\n if (!isEndpointOperation(operation)) {\n throw new Error(\n `@Endpoint ${apiClass.name || 'Unknown'}.${methodName} must declare READ, ` +\n 'WRITE_IDEMPOTENT, or WRITE.',\n );\n }\n return operation;\n}\n\n// webpieces-disable no-function-outside-class -- runtime backstop for JavaScript and `as any` callers; webpieces-disable no-any-unknown -- untrusted runtime value is narrowed to an endpoint operation\nfunction isEndpointOperation(value: unknown): value is EndpointOperation {\n return (\n value === READ ||\n value === WRITE_IDEMPOTENT ||\n value === WRITE\n );\n}\n\n// webpieces-disable no-function-outside-class -- runtime backstop for JavaScript and `as any` callers; webpieces-disable no-any-unknown -- untrusted runtime value is narrowed to an HTTP method\nfunction isHttpMethod(value: unknown): value is HttpMethod {\n return value === GET || value === POST;\n}\n\n// webpieces-disable no-function-outside-class -- runtime backstop for JavaScript and `as any` callers; webpieces-disable no-any-unknown -- untrusted runtime value is narrowed to an endpoint kind\nfunction isEndpointKind(value: unknown): value is EndpointKind {\n return (\n value === RPC ||\n value === CLOUDTASKS ||\n value === CRON ||\n value === EXTERNAL\n );\n}\n\n/**\n * Fail-fast at wiring time when an `external` endpoint declared no caller. The {@link Endpoint}\n * overloads already make that a COMPILE error; this is the backstop for the ways TS is bypassed —\n * a JS caller, an `as any` options object, a hand-rolled Reflect.defineMetadata.\n * @throws Error naming the first external endpoint with no `calledBy`.\n */\n// webpieces-disable no-function-outside-class -- wiring-time assert, sibling of assertEveryEndpointHasAuthMode\nexport function assertEveryExternalEndpointDeclaresCaller(apiClass: Function): void {\n const kinds = getEndpointKinds(apiClass);\n for (const methodName of Object.keys(kinds)) {\n if (\n kinds[methodName] !== 'external' ||\n getEndpointCaller(apiClass, methodName) !== undefined\n )\n continue;\n throw new Error(\n `External endpoint '${methodName}' in ${apiClass.name || 'Unknown'} declares no caller. Say WHO ` +\n `posts to it: @Endpoint(POST, path, WRITE, EXTERNAL, { calledBy: '<vendor>' }) — the runtime architecture ` +\n `graph cannot name an inbound caller it was never told about.`,\n );\n }\n}\n\n/**\n * True when the method's @Endpoint declared `{ formPost: true }` — its body is\n * application/x-www-form-urlencoded (flat), not JSON.\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpoints\nexport function isFormPost(apiClass: Function, methodName: string): boolean {\n return getEndpointOptions(apiClass, methodName).formPost === true;\n}\n\n/**\n * True when the method's @Endpoint declared `{ rawBody: true }` — the transport must retain the\n * verbatim bytes + absolute url for an {@link WpAuthWebhook} hook to verify.\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of isFormPost\nexport function isRawBody(apiClass: Function, methodName: string): boolean {\n return getEndpointOptions(apiClass, methodName).rawBody === true;\n}\n\n/**\n * Fail-fast at wiring time when an `@WpAuthWebhook` endpoint did not ask the transport to keep the\n * bytes it is supposed to verify. A hook with nothing to verify is a MISCONFIGURATION, and it must\n * surface at startup, naming the fix — not as a 401 in production on exactly the traffic the endpoint\n * exists for.\n *\n * This pairing is a runtime assert rather than a type because the two halves live on DIFFERENT\n * decorators (`@WpAuthWebhook` and `@Endpoint`), and no union over one decorator's argument can say\n * anything about the other's.\n *\n * @throws Error naming the first `@WpAuthWebhook` endpoint missing `{ rawBody: true }`.\n */\n// webpieces-disable no-function-outside-class -- wiring-time assert, sibling of assertEveryEndpointHasAuthMode\nexport function assertEveryWebhookEndpointRetainsRawBody(apiClass: Function): void {\n const endpoints = getEndpoints(apiClass) || {};\n for (const methodName of Object.keys(endpoints)) {\n if (\n getAuthMode(apiClass, methodName)?.kind !== 'webhook' ||\n isRawBody(apiClass, methodName)\n )\n continue;\n throw new Error(\n `Endpoint '${methodName}' in ${apiClass.name || 'Unknown'} is @WpAuthWebhook but its @Endpoint ` +\n `does not declare { rawBody: true }. A webhook hook verifies a signature over the bytes and ` +\n `the url the SENDER transmitted, and without that option the transport parses the body and ` +\n `throws them away — leaving the hook nothing to check.`,\n );\n }\n}\n\n/**\n * Check if a class has @ApiPath decorator.\n */\nexport function isApiPath(apiClass: Function): boolean {\n return Reflect.hasMetadata(METADATA_KEYS.API_PATH, apiClass);\n}\n\n/**\n * Get method-level auth metadata. Class-level authorization is forbidden.\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader paired with the decorator API\nexport function getAuthMeta(apiClass: Function, methodName: string): AuthMeta | undefined {\n return Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass, methodName);\n}\n\n/**\n * Get the auth mode for a method, or undefined.\n * Convenience wrapper over getAuthMeta for callers that only want the mode.\n */\n// webpieces-disable no-function-outside-class -- typed convenience reader over getAuthMeta\nexport function getAuthMode(apiClass: Function, methodName: string): AuthMode | undefined {\n return getAuthMeta(apiClass, methodName)?.mode;\n}\n\n/**\n * The ONE prescription for \"this endpoint declares no auth\", shared by the two places that raise it\n * (here and http-routing's ApiRoutingFactory) because they had drifted into teaching different menus.\n * A message teaching an incomplete API is the same defect as an API with two spellings: whichever menu\n * the caller hits becomes the API they believe exists. It leads with the ROLE-GATED member on purpose —\n * the first thing offered should not be the widest grant.\n */\nexport const MISSING_AUTH_DECORATOR_FIX =\n \"Add one of @WpAuthJwt({roles: ['admin']}) / @WpAuthJwt({allRolesAllowed: true}) / \" +\n '@WpAuthOidc(...callers) / @WpAuthSharedSecret(key) / ' +\n \"@WpAuthWebhook('vendor') / @WpAuthApiKey('regime', [{in: 'header', name: 'x-api-key'}]) / \" +\n \"@WpAuthLocalOnly() / @WpAuthPublic('why anonymous access is required') to \" +\n 'the method.';\n\n/**\n * Fail-fast at wiring time if any endpoint lacks an auth mode. Both the server\n * (ApiRoutingFactory) and the task/rpc clients call this so a missing auth\n * decorator is a startup error, never a silent open endpoint.\n * @throws Error naming the first endpoint with no auth decorator, via {@link MISSING_AUTH_DECORATOR_FIX}.\n */\nexport function assertEveryEndpointHasAuthMode(apiClass: Function): void {\n const apiName = apiClass.name || 'Unknown';\n const endpoints = getEndpoints(apiClass) || {};\n for (const methodName of Object.keys(endpoints)) {\n if (!getAuthMeta(apiClass, methodName)) {\n throw new Error(\n `Endpoint '${methodName}' in ${apiName} has no auth decorator. ` +\n MISSING_AUTH_DECORATOR_FIX,\n );\n }\n }\n}\n\n/**\n * Validate that a class/method doesn't have conflicting auth decorators.\n * @throws Error if multiple auth decorators are found on the same target.\n */\n// webpieces-disable no-function-outside-class -- shared decorator metadata validation\nexport function validateNoConflictingDecorators(\n apiClass: Function,\n methodName: string | undefined,\n): void {\n const existing = methodName\n ? Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass, methodName)\n : Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass);\n\n if (existing) {\n const targetName = apiClass.name || 'Unknown';\n const location = methodName\n ? `method '${methodName}' of ${targetName}`\n : `class ${targetName}`;\n throw new Error(\n `Conflicting auth decorator on ${location}. ` +\n `Only one of @WpAuthJwt({...}) / @WpAuthOidc(...) / @WpAuthSharedSecret(...) / ` +\n `@WpAuthWebhook(...) / @WpAuthApiKey(...) / @WpAuthLocalOnly() / ` +\n `@WpAuthPublic('reason') is allowed per target.`,\n );\n }\n}\n"]}
package/src/index.d.ts CHANGED
@@ -34,10 +34,10 @@ export type { EndpointResponseType, HttpMethod, HttpParameterSource, HttpParamet
34
34
  export { GET, POST } from './http/HttpContract';
35
35
  export type { EndpointKind, EndpointOperation, EndpointOptions, ExternalEndpointOptions, } from './http/decorators';
36
36
  export { RPC, CLOUDTASKS, CRON, EXTERNAL, READ, WRITE_IDEMPOTENT, WRITE } from './http/decorators';
37
- export { WpMcpTool, WpMcpToolHints, WpMcpToolMetadata, getWpMcpTools, mcpHintsForOperation, } from './mcp/McpMetadata';
37
+ export { InvalidEndpointForMcp, InvalidEndpointForMcpMetadata, WpMcpTool, WpMcpToolHints, WpMcpToolMetadata, getInvalidEndpointsForMcp, getWpMcpTools, mcpHintsForOperation, } from './mcp/McpMetadata';
38
38
  export type { WpMcpMethodDecorator } from './mcp/McpMetadata';
39
39
  export { WpMcpAuthJwt, WpMcpJwtAuthMetadata, getWpMcpAuthJwt } from './mcp/McpAuthMetadata';
40
- export { ApiJsonSchema, ApiJsonSchemaValidator, DtoValidationFailure, ObjectSchemaBuilder, } from './mcp/DtoSchema';
40
+ export { ApiJsonSchema, ApiJsonSchemaDiscriminator, ApiJsonSchemaValidator, DtoValidationFailure, ObjectSchemaBuilder, } from './mcp/DtoSchema';
41
41
  export type { ApiJsonSchemaType, DtoValue } from './mcp/DtoSchema';
42
42
  export { McpToolCatalog, McpToolCatalogError, McpToolDefinition } from './mcp/McpToolCatalog';
43
43
  export { AuthMeta } from './http/auth-mode';
package/src/index.js CHANGED
@@ -9,10 +9,10 @@
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
11
  exports.WpMax = exports.WpMin = exports.WpInt = exports.getHttpParameterDeclarations = exports.QueryParam = exports.PathParam = exports.METADATA_KEYS = exports.validateNoConflictingDecorators = exports.assertEveryWebhookEndpointRetainsRawBody = exports.assertEveryExternalEndpointDeclaresCaller = exports.assertEveryEndpointHasAuthMode = exports.getAuthMode = 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.WpAuthLocalOnly = exports.WpAuthApiKey = exports.WpAuthWebhook = exports.WpAuthSharedSecret = exports.WpAuthOidc = exports.MISSING_AUTH_DECORATOR_FIX = exports.rolesRequired = exports.WpAuthJwt = 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
- exports.WpMcpJwtAuthMetadata = exports.WpMcpAuthJwt = exports.mcpHintsForOperation = exports.getWpMcpTools = exports.WpMcpToolMetadata = exports.WpMcpToolHints = exports.WpMcpTool = exports.WRITE = exports.WRITE_IDEMPOTENT = exports.READ = exports.EXTERNAL = exports.CRON = exports.CLOUDTASKS = exports.RPC = exports.POST = exports.GET = exports.getStreamingEndpoint = exports.WpStream = exports.StreamWriter = exports.StreamTransportError = exports.StreamEventValidator = exports.StreamingEndpointMetadata = exports.StreamEnvelope = exports.StreamCorrelation = exports.HttpParameterDeclaration = exports.HttpParameterBinding = exports.HttpContractMapper = exports.BoundHttpRequest = 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 = exports.MCP = exports.EXTERNAL_CUSTOMER = exports.SVC_TO_SVC = exports.ApiType = exports.NUMERIC_METADATA_KEYS = void 0;
13
- exports.WEBPIECES_DEFAULT_ERROR_TRANSLATOR = exports.WebpiecesDefaultErrorTranslator = exports.HttpResponseDto = exports.HttpResponseStatus = exports.HttpHeader = exports.RuntimeLocality = exports.ServiceInfo = exports.ApiCallTimeoutError = exports.CallContext = exports.CallDeadline = exports.CallRegistry = exports.ClientRegistry = exports.HeaderRegistry = exports.DateTimeUtil = exports.TimeUtil = exports.DateUtil = exports.InstantUtil = exports.NetworkRejectClassifier = exports.NO_REG_CODE = exports.WRONG_COMPANY = exports.WRONG_DOMAIN = exports.EMAIL_NOT_CONFIRMED = exports.NOT_APPROVED = exports.WRONG_LOGIN = exports.WRONG_LOGIN_TYPE = exports.ENTITY_NOT_FOUND = exports.SECRETS = exports.Secrets = exports.getEndpointCaller = exports.isExternalSystemKind = exports.ExternalCaller = exports.DEFAULT_CALLER_KIND = exports.EXTERNAL_SYSTEM_KINDS = exports.getQueueName = exports.assertPubSubConventions = exports.assertApiKind = exports.getApiKind = exports.ENDPOINT_KINDS_BY_API_KIND = exports.Queue = exports.PubSub = exports.Rpc = exports.AuthMeta = exports.McpToolDefinition = exports.McpToolCatalogError = exports.McpToolCatalog = exports.ObjectSchemaBuilder = exports.DtoValidationFailure = exports.ApiJsonSchemaValidator = exports.ApiJsonSchema = exports.getWpMcpAuthJwt = void 0;
14
- exports.ApiErrorCodec = exports.ApiCodedError = exports.ApiNotImplementedError = exports.ApiUnsupportedMediaTypeError = exports.ApiPreconditionFailedError = exports.ApiUnprocessableError = exports.ApiConflictError = exports.ApiEndpointNotFoundError = exports.ApiConnectionError = exports.ApiDependencyBackoffError = exports.ApiUnauthorizedError = exports.ApiBadRequestError = exports.ApiEndUserError = exports.ApiRequestTimeoutError = exports.ApiForbiddenError = exports.ApiRateLimitedError = exports.ApiImplementationError = exports.ApiDependencyTimeoutError = exports.ApiUnavailableError = exports.ApiBadGatewayError = exports.ApiDependencyError = exports.ApiNotFoundError = exports.ApiError = exports.FilterChain = exports.Filter = exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.ApiMethodInfo = exports.LOG_API_CALL_LOGGER_NAME = exports.ApiCallLogNameImpl = exports.ApiCallLogName = exports.ApiCallInfo = exports.MaskSpec = exports.LogApiCallImpl = exports.ContextMgr = exports.DestinationTrust = exports.WebpiecesCoreHeaders = exports.templateDeriver = exports.WEBPIECES_DEFAULT_FAILURE_CLASSIFIER = exports.WebpiecesDefaultFailureClassifier = exports.KeyedFailureClassifier = exports.ReceivedApiErrorRule = exports.SurfaceEndUserStatus = void 0;
15
- exports.ApiErrorHttpStatus = exports.ApiErrorBoundary = exports.ApiErrorPayload = void 0;
12
+ exports.getWpMcpTools = exports.getInvalidEndpointsForMcp = exports.WpMcpToolMetadata = exports.WpMcpToolHints = exports.WpMcpTool = exports.InvalidEndpointForMcpMetadata = exports.InvalidEndpointForMcp = exports.WRITE = exports.WRITE_IDEMPOTENT = exports.READ = exports.EXTERNAL = exports.CRON = exports.CLOUDTASKS = exports.RPC = exports.POST = exports.GET = exports.getStreamingEndpoint = exports.WpStream = exports.StreamWriter = exports.StreamTransportError = exports.StreamEventValidator = exports.StreamingEndpointMetadata = exports.StreamEnvelope = exports.StreamCorrelation = exports.HttpParameterDeclaration = exports.HttpParameterBinding = exports.HttpContractMapper = exports.BoundHttpRequest = 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 = exports.MCP = exports.EXTERNAL_CUSTOMER = exports.SVC_TO_SVC = exports.ApiType = exports.NUMERIC_METADATA_KEYS = void 0;
13
+ exports.HttpHeader = exports.RuntimeLocality = exports.ServiceInfo = exports.ApiCallTimeoutError = exports.CallContext = exports.CallDeadline = exports.CallRegistry = exports.ClientRegistry = exports.HeaderRegistry = exports.DateTimeUtil = exports.TimeUtil = exports.DateUtil = exports.InstantUtil = exports.NetworkRejectClassifier = exports.NO_REG_CODE = exports.WRONG_COMPANY = exports.WRONG_DOMAIN = exports.EMAIL_NOT_CONFIRMED = exports.NOT_APPROVED = exports.WRONG_LOGIN = exports.WRONG_LOGIN_TYPE = exports.ENTITY_NOT_FOUND = exports.SECRETS = exports.Secrets = exports.getEndpointCaller = exports.isExternalSystemKind = exports.ExternalCaller = exports.DEFAULT_CALLER_KIND = exports.EXTERNAL_SYSTEM_KINDS = exports.getQueueName = exports.assertPubSubConventions = exports.assertApiKind = exports.getApiKind = exports.ENDPOINT_KINDS_BY_API_KIND = exports.Queue = exports.PubSub = exports.Rpc = exports.AuthMeta = exports.McpToolDefinition = exports.McpToolCatalogError = exports.McpToolCatalog = exports.ObjectSchemaBuilder = exports.DtoValidationFailure = exports.ApiJsonSchemaValidator = exports.ApiJsonSchemaDiscriminator = exports.ApiJsonSchema = exports.getWpMcpAuthJwt = exports.WpMcpJwtAuthMetadata = exports.WpMcpAuthJwt = exports.mcpHintsForOperation = void 0;
14
+ exports.ApiPreconditionFailedError = exports.ApiUnprocessableError = exports.ApiConflictError = exports.ApiEndpointNotFoundError = exports.ApiConnectionError = exports.ApiDependencyBackoffError = exports.ApiUnauthorizedError = exports.ApiBadRequestError = exports.ApiEndUserError = exports.ApiRequestTimeoutError = exports.ApiForbiddenError = exports.ApiRateLimitedError = exports.ApiImplementationError = exports.ApiDependencyTimeoutError = exports.ApiUnavailableError = exports.ApiBadGatewayError = exports.ApiDependencyError = exports.ApiNotFoundError = exports.ApiError = exports.FilterChain = exports.Filter = exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.ApiMethodInfo = exports.LOG_API_CALL_LOGGER_NAME = exports.ApiCallLogNameImpl = exports.ApiCallLogName = exports.ApiCallInfo = exports.MaskSpec = exports.LogApiCallImpl = exports.ContextMgr = exports.DestinationTrust = exports.WebpiecesCoreHeaders = exports.templateDeriver = exports.WEBPIECES_DEFAULT_FAILURE_CLASSIFIER = exports.WebpiecesDefaultFailureClassifier = exports.KeyedFailureClassifier = exports.ReceivedApiErrorRule = exports.SurfaceEndUserStatus = exports.WEBPIECES_DEFAULT_ERROR_TRANSLATOR = exports.WebpiecesDefaultErrorTranslator = exports.HttpResponseDto = exports.HttpResponseStatus = void 0;
15
+ exports.ApiErrorHttpStatus = exports.ApiErrorBoundary = exports.ApiErrorPayload = exports.ApiErrorCodec = exports.ApiCodedError = exports.ApiNotImplementedError = exports.ApiUnsupportedMediaTypeError = 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");
@@ -137,9 +137,12 @@ Object.defineProperty(exports, "READ", { enumerable: true, get: function () { re
137
137
  Object.defineProperty(exports, "WRITE_IDEMPOTENT", { enumerable: true, get: function () { return decorators_2.WRITE_IDEMPOTENT; } });
138
138
  Object.defineProperty(exports, "WRITE", { enumerable: true, get: function () { return decorators_2.WRITE; } });
139
139
  var McpMetadata_1 = require("./mcp/McpMetadata");
140
+ Object.defineProperty(exports, "InvalidEndpointForMcp", { enumerable: true, get: function () { return McpMetadata_1.InvalidEndpointForMcp; } });
141
+ Object.defineProperty(exports, "InvalidEndpointForMcpMetadata", { enumerable: true, get: function () { return McpMetadata_1.InvalidEndpointForMcpMetadata; } });
140
142
  Object.defineProperty(exports, "WpMcpTool", { enumerable: true, get: function () { return McpMetadata_1.WpMcpTool; } });
141
143
  Object.defineProperty(exports, "WpMcpToolHints", { enumerable: true, get: function () { return McpMetadata_1.WpMcpToolHints; } });
142
144
  Object.defineProperty(exports, "WpMcpToolMetadata", { enumerable: true, get: function () { return McpMetadata_1.WpMcpToolMetadata; } });
145
+ Object.defineProperty(exports, "getInvalidEndpointsForMcp", { enumerable: true, get: function () { return McpMetadata_1.getInvalidEndpointsForMcp; } });
143
146
  Object.defineProperty(exports, "getWpMcpTools", { enumerable: true, get: function () { return McpMetadata_1.getWpMcpTools; } });
144
147
  Object.defineProperty(exports, "mcpHintsForOperation", { enumerable: true, get: function () { return McpMetadata_1.mcpHintsForOperation; } });
145
148
  var McpAuthMetadata_1 = require("./mcp/McpAuthMetadata");
@@ -148,6 +151,7 @@ Object.defineProperty(exports, "WpMcpJwtAuthMetadata", { enumerable: true, get:
148
151
  Object.defineProperty(exports, "getWpMcpAuthJwt", { enumerable: true, get: function () { return McpAuthMetadata_1.getWpMcpAuthJwt; } });
149
152
  var DtoSchema_1 = require("./mcp/DtoSchema");
150
153
  Object.defineProperty(exports, "ApiJsonSchema", { enumerable: true, get: function () { return DtoSchema_1.ApiJsonSchema; } });
154
+ Object.defineProperty(exports, "ApiJsonSchemaDiscriminator", { enumerable: true, get: function () { return DtoSchema_1.ApiJsonSchemaDiscriminator; } });
151
155
  Object.defineProperty(exports, "ApiJsonSchemaValidator", { enumerable: true, get: function () { return DtoSchema_1.ApiJsonSchemaValidator; } });
152
156
  Object.defineProperty(exports, "DtoValidationFailure", { enumerable: true, get: function () { return DtoSchema_1.DtoValidationFailure; } });
153
157
  Object.defineProperty(exports, "ObjectSchemaBuilder", { enumerable: true, get: function () { return DtoSchema_1.ObjectSchemaBuilder; } });
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @webpieces/core-util\n *\n * Utility functions for WebPieces applications.\n * This package works in both browser and Node.js environments.\n *\n * @packageDocumentation\n */\n\nexport { toError } from './lib/errorUtils';\nexport { ContextKey } from './ContextKey';\nexport type {\n AnyContextKey,\n AnyTrustedContextKey,\n AnyUntrustedContextKey,\n Trust,\n} from './ContextKey';\nexport { ContextTuple } from './ContextTuple';\n\n// @DocumentDesign — DI-design-root marker. Applies to ANY project kind (server\n// controllers AND library impl classes), so it lives here (browser + Node) rather\n// than in a server-only routing package.\nexport { DocumentDesign, isDocumentDesign, DESIGN_METADATA_KEYS } from './DocumentDesign';\n\n// Logging (merged from former @webpieces/wp-logging).\n// Pluggable logging interface + a browser-safe console default; apps plug in\n// bunyan/winston/pino/etc. via LogManager.setFactory(...). Browser + Node.\nexport type { Logger, LogLevel } from './logging/Logger';\nexport type { LoggerFactory } from './logging/LoggerFactory';\nexport { ConsoleLogger } from './logging/ConsoleLogger';\nexport { ConsoleLoggerFactory } from './logging/ConsoleLoggerFactory';\nexport { LogManager } from './logging/LogManager';\nexport {\n LogChunker,\n LogChunkerImpl,\n LogChunkInfo,\n MAX_GCP_LOG_BYTES,\n GCP_LOG_BUDGET_BYTES,\n} from './logging/LogChunker';\n\n// HTTP API contract (merged from former @webpieces/http-api).\n// Shared HTTP API definition consumed by both client and server: REST\n// decorators, the API error taxonomy, datetime DTOs, platform-header\n// registry/readers, ValidateImplementation, and the test-case recorder\n// contract. Pure definitions — express-free, browser + Node safe.\n\n// API definition decorators\nexport {\n ApiPath,\n Endpoint,\n // Auth mode decorators (clean service-to-service + user JWT model)\n WpAuthPublic,\n WpAuthJwt,\n rolesRequired,\n MISSING_AUTH_DECORATOR_FIX,\n WpAuthOidc,\n WpAuthSharedSecret,\n WpAuthWebhook,\n WpAuthApiKey,\n WpAuthLocalOnly,\n MaskLog,\n getApiPath,\n getEndpoints,\n getEndpointHttpMethod,\n getEndpointOptions,\n getEndpointOperation,\n getEndpointKind,\n getEndpointKinds,\n getMaskSpec,\n isFormPost,\n isRawBody,\n isApiPath,\n getAuthMeta,\n getAuthMode,\n assertEveryEndpointHasAuthMode,\n assertEveryExternalEndpointDeclaresCaller,\n assertEveryWebhookEndpointRetainsRawBody,\n validateNoConflictingDecorators,\n METADATA_KEYS,\n} from './http/decorators';\nexport {\n PathParam,\n QueryParam,\n getHttpParameterDeclarations,\n} from './http/http-parameter-decorators';\nexport { WpInt, WpMin, WpMax, NUMERIC_METADATA_KEYS } from './http/numeric-field-decorators';\nexport type { Integer } from './http/numeric-field-decorators';\nexport {\n ApiType,\n SVC_TO_SVC,\n EXTERNAL_CUSTOMER,\n MCP,\n API_TYPE_METADATA_KEY,\n DEFAULT_API_TYPES,\n getApiTypes,\n getApiTypeEndpointNames,\n assertApiTypeMatchesMcpTools,\n} from './http/api-type';\nexport type { ApiTypeKind } from './http/api-type';\nexport {\n WpInternal,\n WpIpcEndpoint,\n isInternalApi,\n getIpcApiId,\n getIpcEndpoints,\n getIpcEndpointKind,\n getIpcMaskSpec,\n assertInternalApi,\n assertNotInternalApi,\n IPC_METADATA_KEYS,\n} from './ipc/IpcDecorators';\nexport type { IpcApiType, IpcEndpointKind, IpcEndpointOptions } from './ipc/IpcDecorators';\n// The runtime representation of ONE route (split out of decorators.ts for file size only).\nexport { RouteMetadata } from './http/RouteMetadata';\nexport { RouteMetadataFactory } from './http/RouteMetadataFactory';\nexport {\n BoundHttpRequest,\n HttpContractMapper,\n HttpParameterBinding,\n HttpParameterDeclaration,\n} from './http/HttpContract';\nexport {\n StreamCorrelation,\n StreamEnvelope,\n StreamingEndpointMetadata,\n StreamEventValidator,\n StreamTransportError,\n StreamWriter,\n WpStream,\n getStreamingEndpoint,\n} from './http/StreamingContract';\nexport type {\n RequestStream,\n ResponseStream,\n StreamEnvelopeKind,\n StreamFailureOptions,\n} from './http/StreamingContract';\nexport type {\n EndpointResponseType,\n HttpMethod,\n HttpParameterSource,\n HttpParameterValueType,\n} from './http/HttpContract';\nexport { GET, POST } from './http/HttpContract';\nexport type {\n EndpointKind,\n EndpointOperation,\n EndpointOptions,\n ExternalEndpointOptions,\n} from './http/decorators';\nexport { RPC, CLOUDTASKS, CRON, EXTERNAL, READ, WRITE_IDEMPOTENT, WRITE } from './http/decorators';\nexport {\n WpMcpTool,\n WpMcpToolHints,\n WpMcpToolMetadata,\n getWpMcpTools,\n mcpHintsForOperation,\n} from './mcp/McpMetadata';\nexport type { WpMcpMethodDecorator } from './mcp/McpMetadata';\nexport { WpMcpAuthJwt, WpMcpJwtAuthMetadata, getWpMcpAuthJwt } from './mcp/McpAuthMetadata';\nexport {\n ApiJsonSchema,\n ApiJsonSchemaValidator,\n DtoValidationFailure,\n ObjectSchemaBuilder,\n} from './mcp/DtoSchema';\nexport type { ApiJsonSchemaType, DtoValue } from './mcp/DtoSchema';\nexport { McpToolCatalog, McpToolCatalogError, McpToolDefinition } from './mcp/McpToolCatalog';\n// The TYPE layer of the auth surface — likewise split out of decorators.ts for file size only.\nexport { AuthMeta } from './http/auth-mode';\nexport type {\n AuthMode,\n ApiKeyCredential,\n ApiKeyCredentials,\n JwtRoles,\n JwtRequirement,\n} from './http/auth-mode';\n// API kind (RPC vs PubSub/Cloud Tasks) + queue naming. Split out of decorators.ts for file size only;\n// one-way dependency api-kind -> decorators, and the barrel keeps the surface identical.\nexport {\n Rpc,\n PubSub,\n Queue,\n ENDPOINT_KINDS_BY_API_KIND,\n getApiKind,\n assertApiKind,\n assertPubSubConventions,\n getQueueName,\n} from './http/api-kind';\nexport type { ApiKind } from './http/api-kind';\n// WHO calls an `external` endpoint — the caller declaration\n// @Endpoint(POST, ..., WRITE, EXTERNAL, { calledBy })\n// requires, and the reader for it.\nexport {\n EXTERNAL_SYSTEM_KINDS,\n DEFAULT_CALLER_KIND,\n ExternalCaller,\n isExternalSystemKind,\n getEndpointCaller,\n} from './http/external-caller';\nexport type { ExternalSystemKind } from './http/external-caller';\n// Client-side shared-secret store (the value THIS service sends per @WpAuthSharedSecret key).\nexport { Secrets, SECRETS } from './http/Secrets';\n\n// Type validators\nexport { ValidateImplementation } from './http/validators';\n\n// Authentication subtype constants.\nexport {\n ENTITY_NOT_FOUND,\n WRONG_LOGIN_TYPE,\n WRONG_LOGIN,\n NOT_APPROVED,\n EMAIL_NOT_CONFIRMED,\n WRONG_DOMAIN,\n WRONG_COMPANY,\n NO_REG_CODE,\n} from './errors';\n\nexport { NetworkRejectClassifier } from './http/networkReject';\n\n// Date/Time DTOs and Utilities (inspired by Java Time / JSR-310)\nexport {\n InstantDto,\n DateDto,\n TimeDto,\n DateTimeDto,\n InstantUtil,\n DateUtil,\n TimeUtil,\n DateTimeUtil,\n} from './http/datetime';\n\n// Context keys + registry (the global magic-context header system)\nexport { HeaderRegistry } from './http/HeaderRegistry';\nexport { ClientRegistry } from './http/ClientRegistry';\nexport { CallRegistry } from './http/CallRegistry';\nexport { CallDeadline } from './http/CallDeadline';\nexport { CallContext } from './http/CallStrategy';\nexport type { Attempt, CallStrategy } from './http/CallStrategy';\nexport { ApiCallTimeoutError } from './http/ApiCallTimeoutError';\nexport type { ServiceUrlDeriver } from './http/ClientRegistry';\n\n// \"What service am I\" — set once at startup, read by the logging backends and by\n// RequestContextHeaders (to stamp requestIdSource on ids this service mints).\nexport { ServiceInfo } from './http/ServiceInfo';\n// \"Where am I running\" — declared once at startup (setupRuntime, from RuntimeSetupOptions.locality).\n// The ONE input to @WpAuthLocalOnly enforcement. Undeclared reads as DEPLOYED (fail safe).\nexport { RuntimeLocality } from './http/RuntimeLocality';\nexport type { Locality } from './http/RuntimeLocality';\n// The ENTIRE HTTP response as pure data — the ONE form both transports (express, fetch) are\n// normalised into, so an ErrorTranslator implementation is written once and serves both.\nexport { HttpHeader, HttpResponseStatus, HttpResponseDto } from './http/HttpResponseDto';\n// Pluggable, bidirectional error translation (app exception <-> the WHOLE response). Set on\n// ClientRegistry at startup; consulted before the built-in webpieces mapping on BOTH sides.\nexport type { ErrorTranslator } from './http/ErrorTranslator';\n// THE webpieces default translator — always installed, and what an app's own translator delegates to\n// in order to decline an error. ONE class, both halves, node and browser alike.\nexport {\n WebpiecesDefaultErrorTranslator,\n WEBPIECES_DEFAULT_ERROR_TRANSLATOR,\n} from './http/WebpiecesDefaultErrorTranslator';\n// WHICH KIND OF CALLER drove this request (gui / llm / public-api), and the per-request end-user\n// status derived from it — the replacement for the deleted per-router EndUserStatus.\nexport { SurfaceEndUserStatus } from './http/Surface';\nexport type { Surface } from './http/Surface';\n// The ONE rule for an error RECEIVED from another process, shared verbatim by HTTP and IPC.\nexport { ReceivedApiErrorRule } from './errors/ReceivedApiErrorRule';\n// Pluggable per-client failure classification (is a thrown API-call error a real failure or an\n// expected non-failure?). Registered on ClientRegistry at startup; consulted by LogApiCall.\nexport type { FailureClassifier } from './http/FailureClassifier';\nexport { KeyedFailureClassifier } from './http/FailureClassifier';\nexport {\n WebpiecesDefaultFailureClassifier,\n WEBPIECES_DEFAULT_FAILURE_CLASSIFIER,\n} from './http/WebpiecesDefaultFailureClassifier';\nexport { templateDeriver } from './http/templateDeriver';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { ContextReader } from './http/ContextReader';\nexport { ContextWriter, ContextStore } from './http/ContextWriter';\n\n// The OUTBOUND half of the trust model: whether a TRUSTED context key may ride to the endpoint being\n// called. Built ONLY from the destination endpoint's AuthMode — see the class doc.\nexport { DestinationTrust } from './http/DestinationTrust';\n\n// BROWSER-ONLY outbound-header propagation (app-held store + registry -> outbound HTTP headers).\n// Only @webpieces/http-client-browser may name it; the server reads RequestContext directly via\n// RequestContextHeaders in the Node-only @webpieces/core-context.\nexport { ContextMgr } from './http/ContextMgr';\n\n// API-call logging helper (uses LogManager above). NOT a singleton: construct one per environment\n// with that environment's ApiCallContext — `new LogApiCallImpl(new RequestContextApiCallContext())`\n// on node, `new LogApiCallImpl(new BrowserApiCallContext())` in a browser.\nexport { LogApiCallImpl } from './http/LogApiCall';\n\n// Opt-in field masking for the LogApiCall log path — declare per-api sensitive fields so secrets\n// (OAuth refresh tokens, id-token JWTs) are masked in the logs while the real value stays on the wire.\nexport { MaskSpec } from './http/LogFieldMask';\nexport type { MaskMode } from './http/LogFieldMask';\n\n// The structured `api` tag + the context-writer seam LogApiCall stamps through. The Node\n// RequestContext-backed impl lives in @webpieces/core-context, the browser one in\n// @webpieces/http-client-browser; each is CONSTRUCTED by its package, never installed globally.\nexport { ApiCallInfo } from './http/ApiCallInfo';\nexport type { ApiCallDirection, ApiResult } from './http/ApiCallInfo';\n// Console-render bridge: turns LogApiCall's [LogApiCall] bracket into [API.{side}.{phase}] locally.\nexport {\n ApiCallLogName,\n ApiCallLogNameImpl,\n LOG_API_CALL_LOGGER_NAME,\n} from './http/ApiCallLogName';\nexport { ApiMethodInfo } from './http/ApiMethodInfo';\nexport type { ApiSide } from './http/ApiMethodInfo';\nexport type { ApiCallContext } from './http/ApiCallContext';\n\n// Test-case recording contract (impl lives in http-server; hooks in http-client)\nexport { TestCaseRecorder, RecorderKeys } from './http/recorder/TestCaseRecorder';\nexport {\n RecordedEndpoint,\n RecordedError,\n RecordedTestCase,\n} from './http/recorder/RecordedEndpoint';\nexport { DoNotRecord, getDoNotRecordFields } from './http/recorder/DoNotRecord';\nexport { RecordSerializer, SerializedMap, SerializedError } from './http/recorder/RecordSerializer';\n\n// ---------------------------------------------------------------------------------------------\n// Filter-chain primitives, shared by BOTH chains: the inbound server chain\n// (`Filter<MethodMeta, WpResponse<unknown>>`, @webpieces/http-routing) and the outbound client\n// chain (`Filter<ClientRequest, Response>`, @webpieces/http-client-core). Declared once, here, in\n// the package both depend on — see the class doc for why a second pair would be a shim.\nexport { Filter } from './filters/Filter';\nexport type { Service } from './filters/Filter';\nexport { FilterChain } from './filters/FilterChain';\n\nexport {\n ApiError,\n ApiNotFoundError,\n ApiDependencyError,\n ApiBadGatewayError,\n ApiUnavailableError,\n ApiDependencyTimeoutError,\n ApiImplementationError,\n ApiRateLimitedError,\n ApiForbiddenError,\n ApiRequestTimeoutError,\n ApiEndUserError,\n ApiBadRequestError,\n ApiUnauthorizedError,\n ApiDependencyBackoffError,\n ApiConnectionError,\n ApiEndpointNotFoundError,\n ApiConflictError,\n ApiUnprocessableError,\n ApiPreconditionFailedError,\n ApiUnsupportedMediaTypeError,\n ApiNotImplementedError,\n ApiCodedError,\n ApiErrorCodec,\n ApiErrorPayload,\n ApiErrorBoundary,\n} from './errors';\nexport type { ApiErrorKind, ApiStatusCode, EdgeHttpStatus } from './errors';\nexport { ApiErrorHttpStatus } from './http/ApiErrorHttpStatus';\nexport type { PublishedKind, PublishedApiError } from './http/ApiErrorHttpStatus';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @webpieces/core-util\n *\n * Utility functions for WebPieces applications.\n * This package works in both browser and Node.js environments.\n *\n * @packageDocumentation\n */\n\nexport { toError } from './lib/errorUtils';\nexport { ContextKey } from './ContextKey';\nexport type {\n AnyContextKey,\n AnyTrustedContextKey,\n AnyUntrustedContextKey,\n Trust,\n} from './ContextKey';\nexport { ContextTuple } from './ContextTuple';\n\n// @DocumentDesign — DI-design-root marker. Applies to ANY project kind (server\n// controllers AND library impl classes), so it lives here (browser + Node) rather\n// than in a server-only routing package.\nexport { DocumentDesign, isDocumentDesign, DESIGN_METADATA_KEYS } from './DocumentDesign';\n\n// Logging (merged from former @webpieces/wp-logging).\n// Pluggable logging interface + a browser-safe console default; apps plug in\n// bunyan/winston/pino/etc. via LogManager.setFactory(...). Browser + Node.\nexport type { Logger, LogLevel } from './logging/Logger';\nexport type { LoggerFactory } from './logging/LoggerFactory';\nexport { ConsoleLogger } from './logging/ConsoleLogger';\nexport { ConsoleLoggerFactory } from './logging/ConsoleLoggerFactory';\nexport { LogManager } from './logging/LogManager';\nexport {\n LogChunker,\n LogChunkerImpl,\n LogChunkInfo,\n MAX_GCP_LOG_BYTES,\n GCP_LOG_BUDGET_BYTES,\n} from './logging/LogChunker';\n\n// HTTP API contract (merged from former @webpieces/http-api).\n// Shared HTTP API definition consumed by both client and server: REST\n// decorators, the API error taxonomy, datetime DTOs, platform-header\n// registry/readers, ValidateImplementation, and the test-case recorder\n// contract. Pure definitions — express-free, browser + Node safe.\n\n// API definition decorators\nexport {\n ApiPath,\n Endpoint,\n // Auth mode decorators (clean service-to-service + user JWT model)\n WpAuthPublic,\n WpAuthJwt,\n rolesRequired,\n MISSING_AUTH_DECORATOR_FIX,\n WpAuthOidc,\n WpAuthSharedSecret,\n WpAuthWebhook,\n WpAuthApiKey,\n WpAuthLocalOnly,\n MaskLog,\n getApiPath,\n getEndpoints,\n getEndpointHttpMethod,\n getEndpointOptions,\n getEndpointOperation,\n getEndpointKind,\n getEndpointKinds,\n getMaskSpec,\n isFormPost,\n isRawBody,\n isApiPath,\n getAuthMeta,\n getAuthMode,\n assertEveryEndpointHasAuthMode,\n assertEveryExternalEndpointDeclaresCaller,\n assertEveryWebhookEndpointRetainsRawBody,\n validateNoConflictingDecorators,\n METADATA_KEYS,\n} from './http/decorators';\nexport {\n PathParam,\n QueryParam,\n getHttpParameterDeclarations,\n} from './http/http-parameter-decorators';\nexport { WpInt, WpMin, WpMax, NUMERIC_METADATA_KEYS } from './http/numeric-field-decorators';\nexport type { Integer } from './http/numeric-field-decorators';\nexport {\n ApiType,\n SVC_TO_SVC,\n EXTERNAL_CUSTOMER,\n MCP,\n API_TYPE_METADATA_KEY,\n DEFAULT_API_TYPES,\n getApiTypes,\n getApiTypeEndpointNames,\n assertApiTypeMatchesMcpTools,\n} from './http/api-type';\nexport type { ApiTypeKind } from './http/api-type';\nexport {\n WpInternal,\n WpIpcEndpoint,\n isInternalApi,\n getIpcApiId,\n getIpcEndpoints,\n getIpcEndpointKind,\n getIpcMaskSpec,\n assertInternalApi,\n assertNotInternalApi,\n IPC_METADATA_KEYS,\n} from './ipc/IpcDecorators';\nexport type { IpcApiType, IpcEndpointKind, IpcEndpointOptions } from './ipc/IpcDecorators';\n// The runtime representation of ONE route (split out of decorators.ts for file size only).\nexport { RouteMetadata } from './http/RouteMetadata';\nexport { RouteMetadataFactory } from './http/RouteMetadataFactory';\nexport {\n BoundHttpRequest,\n HttpContractMapper,\n HttpParameterBinding,\n HttpParameterDeclaration,\n} from './http/HttpContract';\nexport {\n StreamCorrelation,\n StreamEnvelope,\n StreamingEndpointMetadata,\n StreamEventValidator,\n StreamTransportError,\n StreamWriter,\n WpStream,\n getStreamingEndpoint,\n} from './http/StreamingContract';\nexport type {\n RequestStream,\n ResponseStream,\n StreamEnvelopeKind,\n StreamFailureOptions,\n} from './http/StreamingContract';\nexport type {\n EndpointResponseType,\n HttpMethod,\n HttpParameterSource,\n HttpParameterValueType,\n} from './http/HttpContract';\nexport { GET, POST } from './http/HttpContract';\nexport type {\n EndpointKind,\n EndpointOperation,\n EndpointOptions,\n ExternalEndpointOptions,\n} from './http/decorators';\nexport { RPC, CLOUDTASKS, CRON, EXTERNAL, READ, WRITE_IDEMPOTENT, WRITE } from './http/decorators';\nexport {\n InvalidEndpointForMcp,\n InvalidEndpointForMcpMetadata,\n WpMcpTool,\n WpMcpToolHints,\n WpMcpToolMetadata,\n getInvalidEndpointsForMcp,\n getWpMcpTools,\n mcpHintsForOperation,\n} from './mcp/McpMetadata';\nexport type { WpMcpMethodDecorator } from './mcp/McpMetadata';\nexport { WpMcpAuthJwt, WpMcpJwtAuthMetadata, getWpMcpAuthJwt } from './mcp/McpAuthMetadata';\nexport {\n ApiJsonSchema,\n ApiJsonSchemaDiscriminator,\n ApiJsonSchemaValidator,\n DtoValidationFailure,\n ObjectSchemaBuilder,\n} from './mcp/DtoSchema';\nexport type { ApiJsonSchemaType, DtoValue } from './mcp/DtoSchema';\nexport { McpToolCatalog, McpToolCatalogError, McpToolDefinition } from './mcp/McpToolCatalog';\n// The TYPE layer of the auth surface — likewise split out of decorators.ts for file size only.\nexport { AuthMeta } from './http/auth-mode';\nexport type {\n AuthMode,\n ApiKeyCredential,\n ApiKeyCredentials,\n JwtRoles,\n JwtRequirement,\n} from './http/auth-mode';\n// API kind (RPC vs PubSub/Cloud Tasks) + queue naming. Split out of decorators.ts for file size only;\n// one-way dependency api-kind -> decorators, and the barrel keeps the surface identical.\nexport {\n Rpc,\n PubSub,\n Queue,\n ENDPOINT_KINDS_BY_API_KIND,\n getApiKind,\n assertApiKind,\n assertPubSubConventions,\n getQueueName,\n} from './http/api-kind';\nexport type { ApiKind } from './http/api-kind';\n// WHO calls an `external` endpoint — the caller declaration\n// @Endpoint(POST, ..., WRITE, EXTERNAL, { calledBy })\n// requires, and the reader for it.\nexport {\n EXTERNAL_SYSTEM_KINDS,\n DEFAULT_CALLER_KIND,\n ExternalCaller,\n isExternalSystemKind,\n getEndpointCaller,\n} from './http/external-caller';\nexport type { ExternalSystemKind } from './http/external-caller';\n// Client-side shared-secret store (the value THIS service sends per @WpAuthSharedSecret key).\nexport { Secrets, SECRETS } from './http/Secrets';\n\n// Type validators\nexport { ValidateImplementation } from './http/validators';\n\n// Authentication subtype constants.\nexport {\n ENTITY_NOT_FOUND,\n WRONG_LOGIN_TYPE,\n WRONG_LOGIN,\n NOT_APPROVED,\n EMAIL_NOT_CONFIRMED,\n WRONG_DOMAIN,\n WRONG_COMPANY,\n NO_REG_CODE,\n} from './errors';\n\nexport { NetworkRejectClassifier } from './http/networkReject';\n\n// Date/Time DTOs and Utilities (inspired by Java Time / JSR-310)\nexport {\n InstantDto,\n DateDto,\n TimeDto,\n DateTimeDto,\n InstantUtil,\n DateUtil,\n TimeUtil,\n DateTimeUtil,\n} from './http/datetime';\n\n// Context keys + registry (the global magic-context header system)\nexport { HeaderRegistry } from './http/HeaderRegistry';\nexport { ClientRegistry } from './http/ClientRegistry';\nexport { CallRegistry } from './http/CallRegistry';\nexport { CallDeadline } from './http/CallDeadline';\nexport { CallContext } from './http/CallStrategy';\nexport type { Attempt, CallStrategy } from './http/CallStrategy';\nexport { ApiCallTimeoutError } from './http/ApiCallTimeoutError';\nexport type { ServiceUrlDeriver } from './http/ClientRegistry';\n\n// \"What service am I\" — set once at startup, read by the logging backends and by\n// RequestContextHeaders (to stamp requestIdSource on ids this service mints).\nexport { ServiceInfo } from './http/ServiceInfo';\n// \"Where am I running\" — declared once at startup (setupRuntime, from RuntimeSetupOptions.locality).\n// The ONE input to @WpAuthLocalOnly enforcement. Undeclared reads as DEPLOYED (fail safe).\nexport { RuntimeLocality } from './http/RuntimeLocality';\nexport type { Locality } from './http/RuntimeLocality';\n// The ENTIRE HTTP response as pure data — the ONE form both transports (express, fetch) are\n// normalised into, so an ErrorTranslator implementation is written once and serves both.\nexport { HttpHeader, HttpResponseStatus, HttpResponseDto } from './http/HttpResponseDto';\n// Pluggable, bidirectional error translation (app exception <-> the WHOLE response). Set on\n// ClientRegistry at startup; consulted before the built-in webpieces mapping on BOTH sides.\nexport type { ErrorTranslator } from './http/ErrorTranslator';\n// THE webpieces default translator — always installed, and what an app's own translator delegates to\n// in order to decline an error. ONE class, both halves, node and browser alike.\nexport {\n WebpiecesDefaultErrorTranslator,\n WEBPIECES_DEFAULT_ERROR_TRANSLATOR,\n} from './http/WebpiecesDefaultErrorTranslator';\n// WHICH KIND OF CALLER drove this request (gui / llm / public-api), and the per-request end-user\n// status derived from it — the replacement for the deleted per-router EndUserStatus.\nexport { SurfaceEndUserStatus } from './http/Surface';\nexport type { Surface } from './http/Surface';\n// The ONE rule for an error RECEIVED from another process, shared verbatim by HTTP and IPC.\nexport { ReceivedApiErrorRule } from './errors/ReceivedApiErrorRule';\n// Pluggable per-client failure classification (is a thrown API-call error a real failure or an\n// expected non-failure?). Registered on ClientRegistry at startup; consulted by LogApiCall.\nexport type { FailureClassifier } from './http/FailureClassifier';\nexport { KeyedFailureClassifier } from './http/FailureClassifier';\nexport {\n WebpiecesDefaultFailureClassifier,\n WEBPIECES_DEFAULT_FAILURE_CLASSIFIER,\n} from './http/WebpiecesDefaultFailureClassifier';\nexport { templateDeriver } from './http/templateDeriver';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { ContextReader } from './http/ContextReader';\nexport { ContextWriter, ContextStore } from './http/ContextWriter';\n\n// The OUTBOUND half of the trust model: whether a TRUSTED context key may ride to the endpoint being\n// called. Built ONLY from the destination endpoint's AuthMode — see the class doc.\nexport { DestinationTrust } from './http/DestinationTrust';\n\n// BROWSER-ONLY outbound-header propagation (app-held store + registry -> outbound HTTP headers).\n// Only @webpieces/http-client-browser may name it; the server reads RequestContext directly via\n// RequestContextHeaders in the Node-only @webpieces/core-context.\nexport { ContextMgr } from './http/ContextMgr';\n\n// API-call logging helper (uses LogManager above). NOT a singleton: construct one per environment\n// with that environment's ApiCallContext — `new LogApiCallImpl(new RequestContextApiCallContext())`\n// on node, `new LogApiCallImpl(new BrowserApiCallContext())` in a browser.\nexport { LogApiCallImpl } from './http/LogApiCall';\n\n// Opt-in field masking for the LogApiCall log path — declare per-api sensitive fields so secrets\n// (OAuth refresh tokens, id-token JWTs) are masked in the logs while the real value stays on the wire.\nexport { MaskSpec } from './http/LogFieldMask';\nexport type { MaskMode } from './http/LogFieldMask';\n\n// The structured `api` tag + the context-writer seam LogApiCall stamps through. The Node\n// RequestContext-backed impl lives in @webpieces/core-context, the browser one in\n// @webpieces/http-client-browser; each is CONSTRUCTED by its package, never installed globally.\nexport { ApiCallInfo } from './http/ApiCallInfo';\nexport type { ApiCallDirection, ApiResult } from './http/ApiCallInfo';\n// Console-render bridge: turns LogApiCall's [LogApiCall] bracket into [API.{side}.{phase}] locally.\nexport {\n ApiCallLogName,\n ApiCallLogNameImpl,\n LOG_API_CALL_LOGGER_NAME,\n} from './http/ApiCallLogName';\nexport { ApiMethodInfo } from './http/ApiMethodInfo';\nexport type { ApiSide } from './http/ApiMethodInfo';\nexport type { ApiCallContext } from './http/ApiCallContext';\n\n// Test-case recording contract (impl lives in http-server; hooks in http-client)\nexport { TestCaseRecorder, RecorderKeys } from './http/recorder/TestCaseRecorder';\nexport {\n RecordedEndpoint,\n RecordedError,\n RecordedTestCase,\n} from './http/recorder/RecordedEndpoint';\nexport { DoNotRecord, getDoNotRecordFields } from './http/recorder/DoNotRecord';\nexport { RecordSerializer, SerializedMap, SerializedError } from './http/recorder/RecordSerializer';\n\n// ---------------------------------------------------------------------------------------------\n// Filter-chain primitives, shared by BOTH chains: the inbound server chain\n// (`Filter<MethodMeta, WpResponse<unknown>>`, @webpieces/http-routing) and the outbound client\n// chain (`Filter<ClientRequest, Response>`, @webpieces/http-client-core). Declared once, here, in\n// the package both depend on — see the class doc for why a second pair would be a shim.\nexport { Filter } from './filters/Filter';\nexport type { Service } from './filters/Filter';\nexport { FilterChain } from './filters/FilterChain';\n\nexport {\n ApiError,\n ApiNotFoundError,\n ApiDependencyError,\n ApiBadGatewayError,\n ApiUnavailableError,\n ApiDependencyTimeoutError,\n ApiImplementationError,\n ApiRateLimitedError,\n ApiForbiddenError,\n ApiRequestTimeoutError,\n ApiEndUserError,\n ApiBadRequestError,\n ApiUnauthorizedError,\n ApiDependencyBackoffError,\n ApiConnectionError,\n ApiEndpointNotFoundError,\n ApiConflictError,\n ApiUnprocessableError,\n ApiPreconditionFailedError,\n ApiUnsupportedMediaTypeError,\n ApiNotImplementedError,\n ApiCodedError,\n ApiErrorCodec,\n ApiErrorPayload,\n ApiErrorBoundary,\n} from './errors';\nexport type { ApiErrorKind, ApiStatusCode, EdgeHttpStatus } from './errors';\nexport { ApiErrorHttpStatus } from './http/ApiErrorHttpStatus';\nexport type { PublishedKind, PublishedApiError } from './http/ApiErrorHttpStatus';\n"]}
@@ -22,6 +22,31 @@ export type DtoValue = object | string | number | boolean | null;
22
22
  * ABSENT property, and one the reflect-metadata runtime could neither see nor write down.
23
23
  */
24
24
  export type ApiJsonSchemaType = 'object' | 'string' | 'number' | 'integer' | 'boolean' | 'array' | 'null';
25
+ /**
26
+ * The DISCRIMINATOR of a {@link ApiJsonSchema.oneOf} — which property narrows the union, and what
27
+ * each of its values names.
28
+ *
29
+ * The shape MIRRORS what `OpenApiGenerator`'s `SchemaRenderer` already emits for the same union
30
+ * (`propertyName` + `mapping`), because a union has exactly ONE published spelling in this repo and
31
+ * the OpenAPI renderer is the reference implementation of it. The one difference is what a mapping
32
+ * VALUE holds: OpenAPI maps a value to `#/components/schemas/ScheduledWindow`, and an MCP tool
33
+ * schema is INLINE and has no `$ref` to point at, so it maps to the branch's type NAME.
34
+ *
35
+ * `mapping` is DOCUMENTATION for the reader. {@link ApiJsonSchemaValidator} never narrows with it:
36
+ * it selects the branch whose own `enum` carries the value, so the accepted request and the
37
+ * published document cannot disagree about which branch a value means.
38
+ */
39
+ export declare class ApiJsonSchemaDiscriminator {
40
+ /** The property every branch declares as exactly one string literal, e.g. `kind`. */
41
+ propertyName: string;
42
+ /** discriminator value -> the branch's TYPE NAME (MCP is inline: there is no `$ref`). */
43
+ mapping: Record<string, string>;
44
+ constructor(
45
+ /** The property every branch declares as exactly one string literal, e.g. `kind`. */
46
+ propertyName: string,
47
+ /** discriminator value -> the branch's TYPE NAME (MCP is inline: there is no `$ref`). */
48
+ mapping: Record<string, string>);
49
+ }
25
50
  /** The JSON Schema subset emitted for Webpieces API DTOs. */
26
51
  export declare class ApiJsonSchema {
27
52
  /** One type, or `[T, "null"]` for a nullable field. */
@@ -35,6 +60,22 @@ export declare class ApiJsonSchema {
35
60
  enum?: string[];
36
61
  minimum?: number;
37
62
  maximum?: number;
63
+ /**
64
+ * The branches of a union, INLINE (MCP tool schemas have no `$ref`), in declaration order.
65
+ *
66
+ * A union is legal NESTED, inside a property. It is NOT legal at the ROOT of a tool's parameter
67
+ * schema — both the OpenAI and the Anthropic function-calling APIs reject a top-level
68
+ * `oneOf`/`anyOf`/`allOf`, and because a server sends its whole tool list on every request, one
69
+ * such tool makes EVERY request 400. That is refused at build time by the `no-root-union-api-type`
70
+ * rule, before a contract can be published at all.
71
+ */
72
+ oneOf?: readonly ApiJsonSchema[];
73
+ /**
74
+ * Set ONLY when every branch carries the same property typed as one string literal — DERIVED,
75
+ * never invented. A union TypeScript itself cannot narrow is published as a bare {@link oneOf},
76
+ * because claiming a narrowing the source does not have is worse than admitting there is none.
77
+ */
78
+ discriminator?: ApiJsonSchemaDiscriminator;
38
79
  /** MCP 2026 SEP-2243 schema extension consumed by the official SDK. */
39
80
  'x-mcp-header'?: string;
40
81
  constructor(type?: ApiJsonSchemaType | readonly ApiJsonSchemaType[]);
@@ -95,6 +136,28 @@ export declare class DtoValidationFailure {
95
136
  export declare class ApiJsonSchemaValidator {
96
137
  validate(schema: ApiJsonSchema, value: DtoValue): DtoValidationFailure | undefined;
97
138
  private validateAt;
139
+ /**
140
+ * A union, validated BY DISCRIMINATOR.
141
+ *
142
+ * The discriminator property is read FIRST and the branch chosen from it, so a value naming no
143
+ * branch is reported as what it is — `$.window.kind must be one of: scheduled | asap`, naming the
144
+ * field that is wrong and the values it may hold. Trying every branch and reporting "no branch
145
+ * matched" tells a caller nothing they can act on: it names neither the offending property nor
146
+ * the legal values, and it hides a branch that matched on `kind` and failed three levels down.
147
+ *
148
+ * The branch is selected by the branches' own `enum`s, never by
149
+ * {@link ApiJsonSchemaDiscriminator.mapping} — the branches ARE the schema, so selection and
150
+ * validation read one source and cannot disagree.
151
+ */
152
+ private validateUnion;
153
+ /**
154
+ * A union with NO discriminator — one TypeScript itself cannot narrow. Every branch is tried and
155
+ * the value accepted when one matches. There is no better report when none does: the source does
156
+ * not narrow, so no property can honestly be named as the wrong one.
157
+ */
158
+ private validateAnyBranch;
159
+ /** The string literals ONE branch declares for the discriminator property. */
160
+ private static branchValuesOf;
98
161
  private validateObject;
99
162
  private validateArray;
100
163
  private validateString;
@@ -16,7 +16,34 @@
16
16
  * validator that reads the schema instead of a decorator.
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.ApiJsonSchemaValidator = exports.DtoValidationFailure = exports.ObjectSchemaBuilder = exports.ApiJsonSchema = void 0;
19
+ exports.ApiJsonSchemaValidator = exports.DtoValidationFailure = exports.ObjectSchemaBuilder = exports.ApiJsonSchema = exports.ApiJsonSchemaDiscriminator = void 0;
20
+ /**
21
+ * The DISCRIMINATOR of a {@link ApiJsonSchema.oneOf} — which property narrows the union, and what
22
+ * each of its values names.
23
+ *
24
+ * The shape MIRRORS what `OpenApiGenerator`'s `SchemaRenderer` already emits for the same union
25
+ * (`propertyName` + `mapping`), because a union has exactly ONE published spelling in this repo and
26
+ * the OpenAPI renderer is the reference implementation of it. The one difference is what a mapping
27
+ * VALUE holds: OpenAPI maps a value to `#/components/schemas/ScheduledWindow`, and an MCP tool
28
+ * schema is INLINE and has no `$ref` to point at, so it maps to the branch's type NAME.
29
+ *
30
+ * `mapping` is DOCUMENTATION for the reader. {@link ApiJsonSchemaValidator} never narrows with it:
31
+ * it selects the branch whose own `enum` carries the value, so the accepted request and the
32
+ * published document cannot disagree about which branch a value means.
33
+ */
34
+ class ApiJsonSchemaDiscriminator {
35
+ propertyName;
36
+ mapping;
37
+ constructor(
38
+ /** The property every branch declares as exactly one string literal, e.g. `kind`. */
39
+ propertyName,
40
+ /** discriminator value -> the branch's TYPE NAME (MCP is inline: there is no `$ref`). */
41
+ mapping) {
42
+ this.propertyName = propertyName;
43
+ this.mapping = mapping;
44
+ }
45
+ }
46
+ exports.ApiJsonSchemaDiscriminator = ApiJsonSchemaDiscriminator;
20
47
  /** The JSON Schema subset emitted for Webpieces API DTOs. */
21
48
  class ApiJsonSchema {
22
49
  /** One type, or `[T, "null"]` for a nullable field. */
@@ -30,6 +57,22 @@ class ApiJsonSchema {
30
57
  enum;
31
58
  minimum;
32
59
  maximum;
60
+ /**
61
+ * The branches of a union, INLINE (MCP tool schemas have no `$ref`), in declaration order.
62
+ *
63
+ * A union is legal NESTED, inside a property. It is NOT legal at the ROOT of a tool's parameter
64
+ * schema — both the OpenAI and the Anthropic function-calling APIs reject a top-level
65
+ * `oneOf`/`anyOf`/`allOf`, and because a server sends its whole tool list on every request, one
66
+ * such tool makes EVERY request 400. That is refused at build time by the `no-root-union-api-type`
67
+ * rule, before a contract can be published at all.
68
+ */
69
+ oneOf;
70
+ /**
71
+ * Set ONLY when every branch carries the same property typed as one string literal — DERIVED,
72
+ * never invented. A union TypeScript itself cannot narrow is published as a bare {@link oneOf},
73
+ * because claiming a narrowing the source does not have is worse than admitting there is none.
74
+ */
75
+ discriminator;
33
76
  constructor(type) {
34
77
  this.type = type;
35
78
  }
@@ -129,6 +172,9 @@ class ApiJsonSchemaValidator {
129
172
  ? undefined
130
173
  : new DtoValidationFailure(path, `${path} must not be null`);
131
174
  }
175
+ if (schema.oneOf !== undefined) {
176
+ return this.validateUnion(schema, value, path);
177
+ }
132
178
  switch (ApiJsonSchema.baseTypeOf(schema)) {
133
179
  case 'object':
134
180
  return this.validateObject(schema, value, path);
@@ -147,6 +193,59 @@ class ApiJsonSchemaValidator {
147
193
  return undefined;
148
194
  }
149
195
  }
196
+ /**
197
+ * A union, validated BY DISCRIMINATOR.
198
+ *
199
+ * The discriminator property is read FIRST and the branch chosen from it, so a value naming no
200
+ * branch is reported as what it is — `$.window.kind must be one of: scheduled | asap`, naming the
201
+ * field that is wrong and the values it may hold. Trying every branch and reporting "no branch
202
+ * matched" tells a caller nothing they can act on: it names neither the offending property nor
203
+ * the legal values, and it hides a branch that matched on `kind` and failed three levels down.
204
+ *
205
+ * The branch is selected by the branches' own `enum`s, never by
206
+ * {@link ApiJsonSchemaDiscriminator.mapping} — the branches ARE the schema, so selection and
207
+ * validation read one source and cannot disagree.
208
+ */
209
+ validateUnion(schema, value, path) {
210
+ const branches = schema.oneOf ?? [];
211
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
212
+ return new DtoValidationFailure(path, `${path} must be an object`);
213
+ }
214
+ const discriminator = schema.discriminator;
215
+ if (discriminator === undefined) {
216
+ return this.validateAnyBranch(branches, value, path);
217
+ }
218
+ const property = discriminator.propertyName;
219
+ const held = Object.getOwnPropertyDescriptor(value, property)?.value;
220
+ const branch = branches.find((one) => ApiJsonSchemaValidator.branchValuesOf(one, property).includes(held));
221
+ if (branch === undefined) {
222
+ const legal = branches
223
+ .flatMap((one) => ApiJsonSchemaValidator.branchValuesOf(one, property))
224
+ .join(' | ');
225
+ return new DtoValidationFailure(`${path}.${property}`, `${path}.${property} must be one of: ${legal}`);
226
+ }
227
+ return this.validateAt(branch, value, path);
228
+ }
229
+ /**
230
+ * A union with NO discriminator — one TypeScript itself cannot narrow. Every branch is tried and
231
+ * the value accepted when one matches. There is no better report when none does: the source does
232
+ * not narrow, so no property can honestly be named as the wrong one.
233
+ */
234
+ validateAnyBranch(branches, value, path) {
235
+ for (const branch of branches) {
236
+ if (this.validateAt(branch, value, path) === undefined) {
237
+ return undefined;
238
+ }
239
+ }
240
+ return new DtoValidationFailure(path, `${path} matches none of the ${branches.length} accepted shapes`);
241
+ }
242
+ /** The string literals ONE branch declares for the discriminator property. */
243
+ // webpieces-disable no-function-outside-class -- private static reader of a branch schema
244
+ static branchValuesOf(branch, property) {
245
+ const declared = Object.getOwnPropertyDescriptor(branch.properties ?? {}, property)
246
+ ?.value;
247
+ return declared?.enum ?? [];
248
+ }
150
249
  validateObject(schema, value, path) {
151
250
  if (typeof value !== 'object' || value === null || Array.isArray(value)) {
152
251
  return new DtoValidationFailure(path, `${path} must be an object`);
@@ -1 +1 @@
1
- {"version":3,"file":"DtoSchema.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/mcp/DtoSchema.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The JSON Schema subset webpieces publishes on its API boundaries, and the ONE runtime validator of\n * it.\n *\n * ## Nothing here reads reflect-metadata any more\n *\n * Until #984 this file also carried `@WpDto`, `@WpDtoField`, `WpDtoFieldOptions`,\n * `WpDtoMapFieldOptions`, `WpMcpHeader`, `@WpResponseDto` and `DtoSchemaBuilder` — a second, runtime\n * spelling of facts the TypeScript compiler already holds. #983 measured the two against each other\n * and found the compiler reproduces every one of those arguments byte for byte, so they are deleted\n * rather than deprecated (`.claude/rules/no-backwards-compat.md`): a schema is now BUILT at build\n * time from the source by `@webpieces/api-doc-model`, and the runtime is HANDED the result.\n *\n * What survives is the schema itself, a builder for the boundaries that construct one by hand, and a\n * validator that reads the schema instead of a decorator.\n */\n\n/** Runtime values accepted at the DTO validation boundary. */\nexport type DtoValue = object | string | number | boolean | null;\n\n/**\n * One JSON Schema `type`. `null` is a member because NULLABLE is expressed as `[\"string\", \"null\"]`,\n * which is how JSON Schema says \"present, and may hold null\" — a different wire document from an\n * ABSENT property, and one the reflect-metadata runtime could neither see nor write down.\n */\nexport type ApiJsonSchemaType =\n | 'object'\n | 'string'\n | 'number'\n | 'integer'\n | 'boolean'\n | 'array'\n | 'null';\n\n/** The JSON Schema subset emitted for Webpieces API DTOs. */\nexport class ApiJsonSchema {\n /** One type, or `[T, \"null\"]` for a nullable field. */\n type?: ApiJsonSchemaType | readonly ApiJsonSchemaType[];\n description?: string;\n properties?: Record<string, ApiJsonSchema>;\n required?: string[];\n /** `false` closes an object; a schema makes it a typed map whose values all match that schema. */\n additionalProperties?: boolean | ApiJsonSchema;\n items?: ApiJsonSchema;\n enum?: string[];\n minimum?: number;\n maximum?: number;\n /** MCP 2026 SEP-2243 schema extension consumed by the official SDK. */\n declare 'x-mcp-header'?: string;\n\n constructor(type?: ApiJsonSchemaType | readonly ApiJsonSchemaType[]) {\n this.type = type;\n }\n\n /**\n * The non-`null` member of the type, which is the one that decides how a value is checked.\n *\n * These three are STATIC and not instance methods on purpose: an instance method would put a\n * function on the structural type, and this class is handed straight to the MCP SDK, whose\n * parameter types are index-signature JSON objects that a class carrying methods no longer\n * satisfies. It stays a data-only structure (`CLAUDE.md` §1) and the helpers live beside it.\n */\n // webpieces-disable no-function-outside-class -- static reader of this class\n static baseTypeOf(schema: ApiJsonSchema): ApiJsonSchemaType | undefined {\n if (schema.type === undefined) return undefined;\n if (typeof schema.type === 'string') return schema.type;\n return schema.type.find((one: ApiJsonSchemaType) => one !== 'null');\n }\n\n /** True when the schema was widened with `\"null\"`, i.e. the property may hold `null`. */\n // webpieces-disable no-function-outside-class -- static reader of this class\n static allowsNull(schema: ApiJsonSchema): boolean {\n return typeof schema.type !== 'string' && (schema.type?.includes('null') ?? false);\n }\n\n /**\n * The one closure gate for an object schema. Closed means no key can carry an unspecified value:\n * either extra keys are forbidden (`additionalProperties: false`) or every extra key's value is\n * typed (`additionalProperties: <schema>`, a typed map). A missing or `true` value is open.\n * Use this instead of `schema.additionalProperties === false`, which rejects typed maps.\n */\n // webpieces-disable no-function-outside-class -- static reader of this class\n static isClosed(schema: ApiJsonSchema): boolean {\n const extra = schema.additionalProperties;\n return extra === false || (typeof extra === 'object' && extra !== null);\n }\n}\n\n/**\n * Builds the CLOSED object schema a hand-written runtime boundary validates against — `@WpStream`'s\n * two event schemas are the whole of its client list today.\n *\n * It exists because the alternative spellings are both worse: a decorator that re-states the field\n * types the compiler already knows is exactly what #984 deleted, and a bare object literal is what\n * `CLAUDE.md` §3 forbids. A generated schema never comes through here — it is read from the build's\n * own artifact.\n */\nexport class ObjectSchemaBuilder {\n private readonly schema = new ApiJsonSchema('object');\n private readonly requiredNames: string[] = [];\n\n constructor() {\n this.schema.properties = {};\n this.schema.additionalProperties = false;\n }\n\n required(name: string, type: ApiJsonSchema): this {\n this.requiredNames.push(name);\n return this.optional(name, type);\n }\n\n optional(name: string, type: ApiJsonSchema): this {\n this.schema.properties![name] = type;\n return this;\n }\n\n build(): ApiJsonSchema {\n if (this.requiredNames.length > 0) this.schema.required = [...this.requiredNames];\n return this.schema;\n }\n}\n\n/** A validation result safe to show to an API caller/model. */\nexport class DtoValidationFailure {\n constructor(\n /** JSON path of the offending value, e.g. `$.address.city`. */\n public readonly field: string,\n public readonly message: string,\n ) {}\n}\n\n/**\n * Validates a value against an {@link ApiJsonSchema} — the ONE runtime narrowing boundary for MCP\n * tool arguments, MCP structured output and typed stream events.\n *\n * It reads the SCHEMA, which is the same object the build published to agents, so a document that\n * says a field is required and a server that accepts it missing cannot disagree. The predecessor\n * walked reflect-metadata instead, which meant the published document and the accepted request were\n * derived from two different declarations.\n */\nexport class ApiJsonSchemaValidator {\n validate(schema: ApiJsonSchema, value: DtoValue): DtoValidationFailure | undefined {\n return this.validateAt(schema, value, '$');\n }\n\n private validateAt(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (value === null) {\n return ApiJsonSchema.allowsNull(schema)\n ? undefined\n : new DtoValidationFailure(path, `${path} must not be null`);\n }\n switch (ApiJsonSchema.baseTypeOf(schema)) {\n case 'object':\n return this.validateObject(schema, value, path);\n case 'array':\n return this.validateArray(schema, value, path);\n case 'string':\n return this.validateString(schema, value, path);\n case 'number':\n case 'integer':\n return this.validateNumber(schema, value, path);\n case 'boolean':\n return typeof value === 'boolean'\n ? undefined\n : new DtoValidationFailure(path, `${path} must be a boolean`);\n default:\n return undefined;\n }\n }\n\n private validateObject(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return new DtoValidationFailure(path, `${path} must be an object`);\n }\n const properties = schema.properties ?? {};\n const extra = schema.additionalProperties;\n for (const key of Object.keys(value)) {\n const entry: DtoValue = Object.getOwnPropertyDescriptor(value, key)?.value;\n const declared = Object.getOwnPropertyDescriptor(properties, key)?.value as\n | ApiJsonSchema\n | undefined;\n if (declared === undefined) {\n if (typeof extra !== 'object' || extra === null) {\n return new DtoValidationFailure(\n `${path}.${key}`,\n `${path}.${key} is not allowed`,\n );\n }\n const failure = this.validateAt(extra, entry, `${path}.${key}`);\n if (failure) return failure;\n continue;\n }\n const failure = this.validateAt(declared, entry, `${path}.${key}`);\n if (failure) return failure;\n }\n for (const name of schema.required ?? []) {\n if (Object.getOwnPropertyDescriptor(value, name)?.value === undefined) {\n return new DtoValidationFailure(`${path}.${name}`, `${path}.${name} is required`);\n }\n }\n return undefined;\n }\n\n private validateArray(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (!Array.isArray(value)) {\n return new DtoValidationFailure(path, `${path} must be an array`);\n }\n const items = schema.items;\n if (items === undefined) return undefined;\n for (let index = 0; index < value.length; index += 1) {\n const failure = this.validateAt(items, value[index], `${path}[${index}]`);\n if (failure) return failure;\n }\n return undefined;\n }\n\n private validateString(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (typeof value !== 'string') {\n return new DtoValidationFailure(path, `${path} must be a string`);\n }\n if (schema.enum && !schema.enum.includes(value)) {\n return new DtoValidationFailure(\n path,\n `${path} must be one of: ${schema.enum.join(', ')}`,\n );\n }\n return undefined;\n }\n\n private validateNumber(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n return new DtoValidationFailure(path, `${path} must be a number`);\n }\n if (ApiJsonSchema.baseTypeOf(schema) === 'integer' && !Number.isInteger(value)) {\n return new DtoValidationFailure(path, `${path} must be an integer`);\n }\n if (schema.minimum !== undefined && value < schema.minimum) {\n return new DtoValidationFailure(path, `${path} must be >= ${schema.minimum}`);\n }\n if (schema.maximum !== undefined && value > schema.maximum) {\n return new DtoValidationFailure(path, `${path} must be <= ${schema.maximum}`);\n }\n return undefined;\n }\n}\n"]}
1
+ {"version":3,"file":"DtoSchema.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/mcp/DtoSchema.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The JSON Schema subset webpieces publishes on its API boundaries, and the ONE runtime validator of\n * it.\n *\n * ## Nothing here reads reflect-metadata any more\n *\n * Until #984 this file also carried `@WpDto`, `@WpDtoField`, `WpDtoFieldOptions`,\n * `WpDtoMapFieldOptions`, `WpMcpHeader`, `@WpResponseDto` and `DtoSchemaBuilder` — a second, runtime\n * spelling of facts the TypeScript compiler already holds. #983 measured the two against each other\n * and found the compiler reproduces every one of those arguments byte for byte, so they are deleted\n * rather than deprecated (`.claude/rules/no-backwards-compat.md`): a schema is now BUILT at build\n * time from the source by `@webpieces/api-doc-model`, and the runtime is HANDED the result.\n *\n * What survives is the schema itself, a builder for the boundaries that construct one by hand, and a\n * validator that reads the schema instead of a decorator.\n */\n\n/** Runtime values accepted at the DTO validation boundary. */\nexport type DtoValue = object | string | number | boolean | null;\n\n/**\n * One JSON Schema `type`. `null` is a member because NULLABLE is expressed as `[\"string\", \"null\"]`,\n * which is how JSON Schema says \"present, and may hold null\" — a different wire document from an\n * ABSENT property, and one the reflect-metadata runtime could neither see nor write down.\n */\nexport type ApiJsonSchemaType =\n | 'object'\n | 'string'\n | 'number'\n | 'integer'\n | 'boolean'\n | 'array'\n | 'null';\n\n/**\n * The DISCRIMINATOR of a {@link ApiJsonSchema.oneOf} — which property narrows the union, and what\n * each of its values names.\n *\n * The shape MIRRORS what `OpenApiGenerator`'s `SchemaRenderer` already emits for the same union\n * (`propertyName` + `mapping`), because a union has exactly ONE published spelling in this repo and\n * the OpenAPI renderer is the reference implementation of it. The one difference is what a mapping\n * VALUE holds: OpenAPI maps a value to `#/components/schemas/ScheduledWindow`, and an MCP tool\n * schema is INLINE and has no `$ref` to point at, so it maps to the branch's type NAME.\n *\n * `mapping` is DOCUMENTATION for the reader. {@link ApiJsonSchemaValidator} never narrows with it:\n * it selects the branch whose own `enum` carries the value, so the accepted request and the\n * published document cannot disagree about which branch a value means.\n */\nexport class ApiJsonSchemaDiscriminator {\n constructor(\n /** The property every branch declares as exactly one string literal, e.g. `kind`. */\n public propertyName: string,\n /** discriminator value -> the branch's TYPE NAME (MCP is inline: there is no `$ref`). */\n public mapping: Record<string, string>,\n ) {}\n}\n\n/** The JSON Schema subset emitted for Webpieces API DTOs. */\nexport class ApiJsonSchema {\n /** One type, or `[T, \"null\"]` for a nullable field. */\n type?: ApiJsonSchemaType | readonly ApiJsonSchemaType[];\n description?: string;\n properties?: Record<string, ApiJsonSchema>;\n required?: string[];\n /** `false` closes an object; a schema makes it a typed map whose values all match that schema. */\n additionalProperties?: boolean | ApiJsonSchema;\n items?: ApiJsonSchema;\n enum?: string[];\n minimum?: number;\n maximum?: number;\n /**\n * The branches of a union, INLINE (MCP tool schemas have no `$ref`), in declaration order.\n *\n * A union is legal NESTED, inside a property. It is NOT legal at the ROOT of a tool's parameter\n * schema — both the OpenAI and the Anthropic function-calling APIs reject a top-level\n * `oneOf`/`anyOf`/`allOf`, and because a server sends its whole tool list on every request, one\n * such tool makes EVERY request 400. That is refused at build time by the `no-root-union-api-type`\n * rule, before a contract can be published at all.\n */\n oneOf?: readonly ApiJsonSchema[];\n /**\n * Set ONLY when every branch carries the same property typed as one string literal — DERIVED,\n * never invented. A union TypeScript itself cannot narrow is published as a bare {@link oneOf},\n * because claiming a narrowing the source does not have is worse than admitting there is none.\n */\n discriminator?: ApiJsonSchemaDiscriminator;\n /** MCP 2026 SEP-2243 schema extension consumed by the official SDK. */\n declare 'x-mcp-header'?: string;\n\n constructor(type?: ApiJsonSchemaType | readonly ApiJsonSchemaType[]) {\n this.type = type;\n }\n\n /**\n * The non-`null` member of the type, which is the one that decides how a value is checked.\n *\n * These three are STATIC and not instance methods on purpose: an instance method would put a\n * function on the structural type, and this class is handed straight to the MCP SDK, whose\n * parameter types are index-signature JSON objects that a class carrying methods no longer\n * satisfies. It stays a data-only structure (`CLAUDE.md` §1) and the helpers live beside it.\n */\n // webpieces-disable no-function-outside-class -- static reader of this class\n static baseTypeOf(schema: ApiJsonSchema): ApiJsonSchemaType | undefined {\n if (schema.type === undefined) return undefined;\n if (typeof schema.type === 'string') return schema.type;\n return schema.type.find((one: ApiJsonSchemaType) => one !== 'null');\n }\n\n /** True when the schema was widened with `\"null\"`, i.e. the property may hold `null`. */\n // webpieces-disable no-function-outside-class -- static reader of this class\n static allowsNull(schema: ApiJsonSchema): boolean {\n return typeof schema.type !== 'string' && (schema.type?.includes('null') ?? false);\n }\n\n /**\n * The one closure gate for an object schema. Closed means no key can carry an unspecified value:\n * either extra keys are forbidden (`additionalProperties: false`) or every extra key's value is\n * typed (`additionalProperties: <schema>`, a typed map). A missing or `true` value is open.\n * Use this instead of `schema.additionalProperties === false`, which rejects typed maps.\n */\n // webpieces-disable no-function-outside-class -- static reader of this class\n static isClosed(schema: ApiJsonSchema): boolean {\n const extra = schema.additionalProperties;\n return extra === false || (typeof extra === 'object' && extra !== null);\n }\n}\n\n/**\n * Builds the CLOSED object schema a hand-written runtime boundary validates against — `@WpStream`'s\n * two event schemas are the whole of its client list today.\n *\n * It exists because the alternative spellings are both worse: a decorator that re-states the field\n * types the compiler already knows is exactly what #984 deleted, and a bare object literal is what\n * `CLAUDE.md` §3 forbids. A generated schema never comes through here — it is read from the build's\n * own artifact.\n */\nexport class ObjectSchemaBuilder {\n private readonly schema = new ApiJsonSchema('object');\n private readonly requiredNames: string[] = [];\n\n constructor() {\n this.schema.properties = {};\n this.schema.additionalProperties = false;\n }\n\n required(name: string, type: ApiJsonSchema): this {\n this.requiredNames.push(name);\n return this.optional(name, type);\n }\n\n optional(name: string, type: ApiJsonSchema): this {\n this.schema.properties![name] = type;\n return this;\n }\n\n build(): ApiJsonSchema {\n if (this.requiredNames.length > 0) this.schema.required = [...this.requiredNames];\n return this.schema;\n }\n}\n\n/** A validation result safe to show to an API caller/model. */\nexport class DtoValidationFailure {\n constructor(\n /** JSON path of the offending value, e.g. `$.address.city`. */\n public readonly field: string,\n public readonly message: string,\n ) {}\n}\n\n/**\n * Validates a value against an {@link ApiJsonSchema} — the ONE runtime narrowing boundary for MCP\n * tool arguments, MCP structured output and typed stream events.\n *\n * It reads the SCHEMA, which is the same object the build published to agents, so a document that\n * says a field is required and a server that accepts it missing cannot disagree. The predecessor\n * walked reflect-metadata instead, which meant the published document and the accepted request were\n * derived from two different declarations.\n */\nexport class ApiJsonSchemaValidator {\n validate(schema: ApiJsonSchema, value: DtoValue): DtoValidationFailure | undefined {\n return this.validateAt(schema, value, '$');\n }\n\n private validateAt(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (value === null) {\n return ApiJsonSchema.allowsNull(schema)\n ? undefined\n : new DtoValidationFailure(path, `${path} must not be null`);\n }\n if (schema.oneOf !== undefined) {\n return this.validateUnion(schema, value, path);\n }\n switch (ApiJsonSchema.baseTypeOf(schema)) {\n case 'object':\n return this.validateObject(schema, value, path);\n case 'array':\n return this.validateArray(schema, value, path);\n case 'string':\n return this.validateString(schema, value, path);\n case 'number':\n case 'integer':\n return this.validateNumber(schema, value, path);\n case 'boolean':\n return typeof value === 'boolean'\n ? undefined\n : new DtoValidationFailure(path, `${path} must be a boolean`);\n default:\n return undefined;\n }\n }\n\n /**\n * A union, validated BY DISCRIMINATOR.\n *\n * The discriminator property is read FIRST and the branch chosen from it, so a value naming no\n * branch is reported as what it is — `$.window.kind must be one of: scheduled | asap`, naming the\n * field that is wrong and the values it may hold. Trying every branch and reporting \"no branch\n * matched\" tells a caller nothing they can act on: it names neither the offending property nor\n * the legal values, and it hides a branch that matched on `kind` and failed three levels down.\n *\n * The branch is selected by the branches' own `enum`s, never by\n * {@link ApiJsonSchemaDiscriminator.mapping} — the branches ARE the schema, so selection and\n * validation read one source and cannot disagree.\n */\n private validateUnion(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n const branches = schema.oneOf ?? [];\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return new DtoValidationFailure(path, `${path} must be an object`);\n }\n const discriminator = schema.discriminator;\n if (discriminator === undefined) {\n return this.validateAnyBranch(branches, value, path);\n }\n const property = discriminator.propertyName;\n const held: DtoValue = Object.getOwnPropertyDescriptor(value, property)?.value;\n const branch = branches.find((one: ApiJsonSchema) =>\n ApiJsonSchemaValidator.branchValuesOf(one, property).includes(held as string),\n );\n if (branch === undefined) {\n const legal = branches\n .flatMap((one: ApiJsonSchema) =>\n ApiJsonSchemaValidator.branchValuesOf(one, property),\n )\n .join(' | ');\n return new DtoValidationFailure(\n `${path}.${property}`,\n `${path}.${property} must be one of: ${legal}`,\n );\n }\n return this.validateAt(branch, value, path);\n }\n\n /**\n * A union with NO discriminator — one TypeScript itself cannot narrow. Every branch is tried and\n * the value accepted when one matches. There is no better report when none does: the source does\n * not narrow, so no property can honestly be named as the wrong one.\n */\n private validateAnyBranch(\n branches: readonly ApiJsonSchema[],\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n for (const branch of branches) {\n if (this.validateAt(branch, value, path) === undefined) {\n return undefined;\n }\n }\n return new DtoValidationFailure(\n path,\n `${path} matches none of the ${branches.length} accepted shapes`,\n );\n }\n\n /** The string literals ONE branch declares for the discriminator property. */\n // webpieces-disable no-function-outside-class -- private static reader of a branch schema\n private static branchValuesOf(branch: ApiJsonSchema, property: string): readonly string[] {\n const declared = Object.getOwnPropertyDescriptor(branch.properties ?? {}, property)\n ?.value as ApiJsonSchema | undefined;\n return declared?.enum ?? [];\n }\n\n private validateObject(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return new DtoValidationFailure(path, `${path} must be an object`);\n }\n const properties = schema.properties ?? {};\n const extra = schema.additionalProperties;\n for (const key of Object.keys(value)) {\n const entry: DtoValue = Object.getOwnPropertyDescriptor(value, key)?.value;\n const declared = Object.getOwnPropertyDescriptor(properties, key)?.value as\n | ApiJsonSchema\n | undefined;\n if (declared === undefined) {\n if (typeof extra !== 'object' || extra === null) {\n return new DtoValidationFailure(\n `${path}.${key}`,\n `${path}.${key} is not allowed`,\n );\n }\n const failure = this.validateAt(extra, entry, `${path}.${key}`);\n if (failure) return failure;\n continue;\n }\n const failure = this.validateAt(declared, entry, `${path}.${key}`);\n if (failure) return failure;\n }\n for (const name of schema.required ?? []) {\n if (Object.getOwnPropertyDescriptor(value, name)?.value === undefined) {\n return new DtoValidationFailure(`${path}.${name}`, `${path}.${name} is required`);\n }\n }\n return undefined;\n }\n\n private validateArray(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (!Array.isArray(value)) {\n return new DtoValidationFailure(path, `${path} must be an array`);\n }\n const items = schema.items;\n if (items === undefined) return undefined;\n for (let index = 0; index < value.length; index += 1) {\n const failure = this.validateAt(items, value[index], `${path}[${index}]`);\n if (failure) return failure;\n }\n return undefined;\n }\n\n private validateString(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (typeof value !== 'string') {\n return new DtoValidationFailure(path, `${path} must be a string`);\n }\n if (schema.enum && !schema.enum.includes(value)) {\n return new DtoValidationFailure(\n path,\n `${path} must be one of: ${schema.enum.join(', ')}`,\n );\n }\n return undefined;\n }\n\n private validateNumber(\n schema: ApiJsonSchema,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n return new DtoValidationFailure(path, `${path} must be a number`);\n }\n if (ApiJsonSchema.baseTypeOf(schema) === 'integer' && !Number.isInteger(value)) {\n return new DtoValidationFailure(path, `${path} must be an integer`);\n }\n if (schema.minimum !== undefined && value < schema.minimum) {\n return new DtoValidationFailure(path, `${path} must be >= ${schema.minimum}`);\n }\n if (schema.maximum !== undefined && value > schema.maximum) {\n return new DtoValidationFailure(path, `${path} must be <= ${schema.maximum}`);\n }\n return undefined;\n }\n}\n"]}
@@ -44,6 +44,54 @@ export declare class WpMcpToolMetadata {
44
44
  * held one field, so it is a plain string and `WpMcpToolOptions` is deleted with it.
45
45
  */
46
46
  export declare function WpMcpTool(name: string): WpMcpMethodDecorator;
47
+ /** Runtime metadata for one endpoint declared PERMANENTLY outside MCP. */
48
+ export declare class InvalidEndpointForMcpMetadata {
49
+ readonly methodName: string;
50
+ /** WHY it can never be a tool. Non-empty, enforced at decoration time. */
51
+ readonly reason: string;
52
+ constructor(methodName: string,
53
+ /** WHY it can never be a tool. Non-empty, enforced at decoration time. */
54
+ reason: string);
55
+ }
56
+ /**
57
+ * Declares that this endpoint is PERMANENTLY outside MCP, and says why.
58
+ *
59
+ * ## It is not a suppression, and that distinction is the whole feature
60
+ *
61
+ * `// webpieces-disable api-rules-for-mcp -- <reason>` says "stop complaining". This says "this is
62
+ * structurally impossible, forever", and the two have different futures: a suppression is something
63
+ * somebody should eventually come back to, and this is something nobody should. Both measured cases
64
+ * are types that are CORRECT and must not be "fixed" —
65
+ *
66
+ * - a webhook TRANSPORT envelope, whose `data` is owned by each event's own published contract;
67
+ * typing it here would fork that ownership and make adding an event type a change to the
68
+ * transport contract rather than a registration, and
69
+ * - a freeform-SQL escape hatch, whose result columns depend on the model-authored `SELECT`.
70
+ *
71
+ * The second one is worth stating rather than burying: it is the most obviously useful tool in that
72
+ * repo, and it is excluded by a type that is right.
73
+ *
74
+ * ## Absence already says "not a tool YET"
75
+ *
76
+ * A method with no `@WpMcpTool` is simply not published. This decorator is the NEVER, and the reason
77
+ * is what makes `grep -rn InvalidEndpointForMcp` a list of permanent exclusions with their arguments
78
+ * rather than a list of names. `api-rules-for-mcp` restates that list on every run, so an exclusion
79
+ * stays visible instead of being announced once and forgotten.
80
+ *
81
+ * ## It contradicts `@WpMcpTool`
82
+ *
83
+ * Both on one method is an ERROR, asserted by {@link assertApiTypeMatchesMcpTools}. "Publish this as
84
+ * a tool" and "this can never be a tool" are two spellings of one decision, which is exactly the
85
+ * defect `.claude/rules/no-backwards-compat.md` shim shape #1 rejects.
86
+ *
87
+ * ```typescript
88
+ * @InvalidEndpointForMcp('a transport envelope body is opaque by design; the published partner ' +
89
+ * 'contract for each event type owns its shape')
90
+ * ```
91
+ */
92
+ export declare function InvalidEndpointForMcp(reason: string): MethodDecorator;
93
+ /** Every method declared permanently outside MCP by {@link InvalidEndpointForMcp}. */
94
+ export declare function getInvalidEndpointsForMcp(apiClass: Function): InvalidEndpointForMcpMetadata[];
47
95
  /** The one mapping from endpoint side effects to MCP advisory annotations. */
48
96
  export declare function mcpHintsForOperation(operation: EndpointOperation, openWorldHint: boolean): WpMcpToolHints;
49
97
  /** Returns only methods explicitly opted in through {@link WpMcpTool}. */
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WpMcpToolMetadata = exports.WpMcpToolHints = void 0;
3
+ exports.InvalidEndpointForMcpMetadata = exports.WpMcpToolMetadata = exports.WpMcpToolHints = void 0;
4
4
  exports.WpMcpTool = WpMcpTool;
5
+ exports.InvalidEndpointForMcp = InvalidEndpointForMcp;
6
+ exports.getInvalidEndpointsForMcp = getInvalidEndpointsForMcp;
5
7
  exports.mcpHintsForOperation = mcpHintsForOperation;
6
8
  exports.getWpMcpTools = getWpMcpTools;
7
9
  require("reflect-metadata");
@@ -67,6 +69,74 @@ function WpMcpTool(name) {
67
69
  Reflect.defineMetadata(decorators_1.METADATA_KEYS.MCP_TOOLS, tools, apiClass);
68
70
  };
69
71
  }
72
+ /** Runtime metadata for one endpoint declared PERMANENTLY outside MCP. */
73
+ class InvalidEndpointForMcpMetadata {
74
+ methodName;
75
+ reason;
76
+ constructor(methodName,
77
+ /** WHY it can never be a tool. Non-empty, enforced at decoration time. */
78
+ reason) {
79
+ this.methodName = methodName;
80
+ this.reason = reason;
81
+ }
82
+ }
83
+ exports.InvalidEndpointForMcpMetadata = InvalidEndpointForMcpMetadata;
84
+ /**
85
+ * Declares that this endpoint is PERMANENTLY outside MCP, and says why.
86
+ *
87
+ * ## It is not a suppression, and that distinction is the whole feature
88
+ *
89
+ * `// webpieces-disable api-rules-for-mcp -- <reason>` says "stop complaining". This says "this is
90
+ * structurally impossible, forever", and the two have different futures: a suppression is something
91
+ * somebody should eventually come back to, and this is something nobody should. Both measured cases
92
+ * are types that are CORRECT and must not be "fixed" —
93
+ *
94
+ * - a webhook TRANSPORT envelope, whose `data` is owned by each event's own published contract;
95
+ * typing it here would fork that ownership and make adding an event type a change to the
96
+ * transport contract rather than a registration, and
97
+ * - a freeform-SQL escape hatch, whose result columns depend on the model-authored `SELECT`.
98
+ *
99
+ * The second one is worth stating rather than burying: it is the most obviously useful tool in that
100
+ * repo, and it is excluded by a type that is right.
101
+ *
102
+ * ## Absence already says "not a tool YET"
103
+ *
104
+ * A method with no `@WpMcpTool` is simply not published. This decorator is the NEVER, and the reason
105
+ * is what makes `grep -rn InvalidEndpointForMcp` a list of permanent exclusions with their arguments
106
+ * rather than a list of names. `api-rules-for-mcp` restates that list on every run, so an exclusion
107
+ * stays visible instead of being announced once and forgotten.
108
+ *
109
+ * ## It contradicts `@WpMcpTool`
110
+ *
111
+ * Both on one method is an ERROR, asserted by {@link assertApiTypeMatchesMcpTools}. "Publish this as
112
+ * a tool" and "this can never be a tool" are two spellings of one decision, which is exactly the
113
+ * defect `.claude/rules/no-backwards-compat.md` shim shape #1 rejects.
114
+ *
115
+ * ```typescript
116
+ * @InvalidEndpointForMcp('a transport envelope body is opaque by design; the published partner ' +
117
+ * 'contract for each event type owns its shape')
118
+ * ```
119
+ */
120
+ // webpieces-disable no-function-outside-class -- decorator factories are inherently module-scope
121
+ function InvalidEndpointForMcp(reason) {
122
+ if (reason.trim() === '') {
123
+ throw new Error('@InvalidEndpointForMcp requires a non-empty reason. The reason IS the declaration — ' +
124
+ 'without it this is a suppression with a longer name.');
125
+ }
126
+ return (target, propertyKey) => {
127
+ const apiClass = target.constructor;
128
+ const excluded = Reflect.getMetadata(decorators_1.METADATA_KEYS.MCP_INVALID, apiClass) ?? {};
129
+ const methodName = String(propertyKey);
130
+ excluded[methodName] = new InvalidEndpointForMcpMetadata(methodName, reason);
131
+ Reflect.defineMetadata(decorators_1.METADATA_KEYS.MCP_INVALID, excluded, apiClass);
132
+ };
133
+ }
134
+ /** Every method declared permanently outside MCP by {@link InvalidEndpointForMcp}. */
135
+ // webpieces-disable no-function-outside-class -- metadata reader paired with the decorator
136
+ function getInvalidEndpointsForMcp(apiClass) {
137
+ const excluded = Reflect.getMetadata(decorators_1.METADATA_KEYS.MCP_INVALID, apiClass) ?? {};
138
+ return Object.values(excluded);
139
+ }
70
140
  /** The one mapping from endpoint side effects to MCP advisory annotations. */
71
141
  // webpieces-disable no-function-outside-class -- pure metadata mapping shared by MCP registration
72
142
  function mcpHintsForOperation(operation, openWorldHint) {
@@ -1 +1 @@
1
- {"version":3,"file":"McpMetadata.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/mcp/McpMetadata.ts"],"names":[],"mappings":";;;AA6DA,8BAcC;AAID,oDAYC;AAID,sCAIC;AAnGD,4BAA0B;AAC1B,mDAAmD;AACnD,qEAA+F;AAE/F,wFAAwF;AACxF,MAAa,cAAc;IAEH;IACA;IACA;IACA;IAJpB,YACoB,YAAqB,EACrB,eAAwB,EACxB,cAAuB,EACvB,aAAsB;QAHtB,iBAAY,GAAZ,YAAY,CAAS;QACrB,oBAAe,GAAf,eAAe,CAAS;QACxB,mBAAc,GAAd,cAAc,CAAS;QACvB,kBAAa,GAAb,aAAa,CAAS;IACvC,CAAC;CACP;AAPD,wCAOC;AAgBD,4DAA4D;AAC5D,MAAa,iBAAiB;IAEN;IACA;IAFpB,YACoB,UAAkB,EAClB,IAAY;QADZ,eAAU,GAAV,UAAU,CAAQ;QAClB,SAAI,GAAJ,IAAI,CAAQ;IAC7B,CAAC;CACP;AALD,8CAKC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,iGAAiG;AACjG,SAAgB,SAAS,CAAC,IAAY;IAClC,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IACxF,OAAO,CACH,MAAc,EACd,WAA4B,EAC5B,WAA8C,EAC1C,EAAE;QACN,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC;QACpC,MAAM,KAAK,GACP,OAAO,CAAC,WAAW,CAAC,0BAAa,CAAC,SAAS,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;QACjE,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;QACvC,KAAK,CAAC,UAAU,CAAC,GAAG,IAAI,iBAAiB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAC5D,OAAO,CAAC,cAAc,CAAC,0BAAa,CAAC,SAAS,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IACrE,CAAC,CAAC;AACN,CAAC;AAED,8EAA8E;AAC9E,kGAAkG;AAClG,SAAgB,oBAAoB,CAChC,SAA4B,EAC5B,aAAsB;IAEtB,QAAQ,SAAS,EAAE,CAAC;QAChB,KAAK,0BAAI;YACL,OAAO,IAAI,cAAc,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;QAChE,KAAK,sCAAgB;YACjB,OAAO,IAAI,cAAc,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;QAChE,KAAK,2BAAK;YACN,OAAO,IAAI,cAAc,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC;IACrE,CAAC;AACL,CAAC;AAED,0EAA0E;AAC1E,2FAA2F;AAC3F,SAAgB,aAAa,CAAC,QAAkB;IAC5C,MAAM,KAAK,GACP,OAAO,CAAC,WAAW,CAAC,0BAAa,CAAC,SAAS,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;IACjE,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC","sourcesContent":["import 'reflect-metadata';\nimport { METADATA_KEYS } from '../http/decorators';\nimport { EndpointOperation, READ, WRITE_IDEMPOTENT, WRITE } from '../http/HttpEndpointOptions';\n\n/** The immutable hints MCP clients use when deciding whether and how to call a tool. */\nexport class WpMcpToolHints {\n constructor(\n public readonly readOnlyHint: boolean,\n public readonly destructiveHint: boolean,\n public readonly idempotentHint: boolean,\n public readonly openWorldHint: boolean,\n ) {}\n}\n\ntype AsyncObjectMethod = (...args: never[]) => Promise<object>;\ntype ExactOneParameter<TMethod extends AsyncObjectMethod> = Parameters<TMethod>['length'] extends 1\n ? object\n : never;\ntype ExactOneMethodDescriptor<TMethod extends AsyncObjectMethod> =\n TypedPropertyDescriptor<TMethod> & ExactOneParameter<TMethod>;\n\n/** Method decorator restricted to one request DTO and one async response DTO. */\nexport type WpMcpMethodDecorator = <TMethod extends AsyncObjectMethod>(\n target: object,\n propertyKey: string | symbol,\n descriptor: ExactOneMethodDescriptor<TMethod>,\n) => void;\n\n/** Runtime metadata for one explicitly exposed endpoint. */\nexport class WpMcpToolMetadata {\n constructor(\n public readonly methodName: string,\n public readonly name: string,\n ) {}\n}\n\n/**\n * Explicitly publishes an existing `@Endpoint(POST, path, WRITE, RPC)` method as an MCP tool, under\n * the STABLE protocol name given here.\n *\n * The name is the whole argument, and deliberately the only one. It is independent of the method\n * name because renaming a method must not break a saved agent workflow, and nothing else about a\n * tool is unsayable in the source:\n *\n * | fact | where it comes from |\n * |---|---|\n * | `description` | the method's JSDoc body, or its `@mcp` tag — the same words the partner reads |\n * | `readOnlyHint` / `destructiveHint` / `idempotentHint` | `@Endpoint`'s `operation`, via {@link mcpHintsForOperation} |\n * | `openWorldHint` | `@Endpoint`'s `openWorld` option |\n * | `inputSchema` / `outputSchema` | the declared request and response types |\n *\n * ```typescript\n * @WpMcpTool('search_stores')\n * ```\n *\n * Until #984 it also took a `description`, duplicating the JSDoc. Two authored copies of one\n * paragraph drift the first time somebody edits one, and nothing catches it — the partner reads the\n * OpenAPI text and the agent reads the decorator text. With `description` gone the options object\n * held one field, so it is a plain string and `WpMcpToolOptions` is deleted with it.\n */\n// webpieces-disable no-function-outside-class -- decorator factories are inherently module-scope\nexport function WpMcpTool(name: string): WpMcpMethodDecorator {\n if (name.trim() === '') throw new Error('@WpMcpTool requires a non-empty stable name.');\n return <TMethod extends AsyncObjectMethod>(\n target: object,\n propertyKey: string | symbol,\n _descriptor: ExactOneMethodDescriptor<TMethod>,\n ): void => {\n const apiClass = target.constructor;\n const tools: Record<string, WpMcpToolMetadata> =\n Reflect.getMetadata(METADATA_KEYS.MCP_TOOLS, apiClass) ?? {};\n const methodName = String(propertyKey);\n tools[methodName] = new WpMcpToolMetadata(methodName, name);\n Reflect.defineMetadata(METADATA_KEYS.MCP_TOOLS, tools, apiClass);\n };\n}\n\n/** The one mapping from endpoint side effects to MCP advisory annotations. */\n// webpieces-disable no-function-outside-class -- pure metadata mapping shared by MCP registration\nexport function mcpHintsForOperation(\n operation: EndpointOperation,\n openWorldHint: boolean,\n): WpMcpToolHints {\n switch (operation) {\n case READ:\n return new WpMcpToolHints(true, false, true, openWorldHint);\n case WRITE_IDEMPOTENT:\n return new WpMcpToolHints(false, true, true, openWorldHint);\n case WRITE:\n return new WpMcpToolHints(false, true, false, openWorldHint);\n }\n}\n\n/** Returns only methods explicitly opted in through {@link WpMcpTool}. */\n// webpieces-disable no-function-outside-class -- metadata reader paired with the decorator\nexport function getWpMcpTools(apiClass: Function): WpMcpToolMetadata[] {\n const tools: Record<string, WpMcpToolMetadata> =\n Reflect.getMetadata(METADATA_KEYS.MCP_TOOLS, apiClass) ?? {};\n return Object.values(tools);\n}\n"]}
1
+ {"version":3,"file":"McpMetadata.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/mcp/McpMetadata.ts"],"names":[],"mappings":"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","sourcesContent":["import 'reflect-metadata';\nimport { METADATA_KEYS } from '../http/decorators';\nimport { EndpointOperation, READ, WRITE_IDEMPOTENT, WRITE } from '../http/HttpEndpointOptions';\n\n/** The immutable hints MCP clients use when deciding whether and how to call a tool. */\nexport class WpMcpToolHints {\n constructor(\n public readonly readOnlyHint: boolean,\n public readonly destructiveHint: boolean,\n public readonly idempotentHint: boolean,\n public readonly openWorldHint: boolean,\n ) {}\n}\n\ntype AsyncObjectMethod = (...args: never[]) => Promise<object>;\ntype ExactOneParameter<TMethod extends AsyncObjectMethod> = Parameters<TMethod>['length'] extends 1\n ? object\n : never;\ntype ExactOneMethodDescriptor<TMethod extends AsyncObjectMethod> =\n TypedPropertyDescriptor<TMethod> & ExactOneParameter<TMethod>;\n\n/** Method decorator restricted to one request DTO and one async response DTO. */\nexport type WpMcpMethodDecorator = <TMethod extends AsyncObjectMethod>(\n target: object,\n propertyKey: string | symbol,\n descriptor: ExactOneMethodDescriptor<TMethod>,\n) => void;\n\n/** Runtime metadata for one explicitly exposed endpoint. */\nexport class WpMcpToolMetadata {\n constructor(\n public readonly methodName: string,\n public readonly name: string,\n ) {}\n}\n\n/**\n * Explicitly publishes an existing `@Endpoint(POST, path, WRITE, RPC)` method as an MCP tool, under\n * the STABLE protocol name given here.\n *\n * The name is the whole argument, and deliberately the only one. It is independent of the method\n * name because renaming a method must not break a saved agent workflow, and nothing else about a\n * tool is unsayable in the source:\n *\n * | fact | where it comes from |\n * |---|---|\n * | `description` | the method's JSDoc body, or its `@mcp` tag — the same words the partner reads |\n * | `readOnlyHint` / `destructiveHint` / `idempotentHint` | `@Endpoint`'s `operation`, via {@link mcpHintsForOperation} |\n * | `openWorldHint` | `@Endpoint`'s `openWorld` option |\n * | `inputSchema` / `outputSchema` | the declared request and response types |\n *\n * ```typescript\n * @WpMcpTool('search_stores')\n * ```\n *\n * Until #984 it also took a `description`, duplicating the JSDoc. Two authored copies of one\n * paragraph drift the first time somebody edits one, and nothing catches it — the partner reads the\n * OpenAPI text and the agent reads the decorator text. With `description` gone the options object\n * held one field, so it is a plain string and `WpMcpToolOptions` is deleted with it.\n */\n// webpieces-disable no-function-outside-class -- decorator factories are inherently module-scope\nexport function WpMcpTool(name: string): WpMcpMethodDecorator {\n if (name.trim() === '') throw new Error('@WpMcpTool requires a non-empty stable name.');\n return <TMethod extends AsyncObjectMethod>(\n target: object,\n propertyKey: string | symbol,\n _descriptor: ExactOneMethodDescriptor<TMethod>,\n ): void => {\n const apiClass = target.constructor;\n const tools: Record<string, WpMcpToolMetadata> =\n Reflect.getMetadata(METADATA_KEYS.MCP_TOOLS, apiClass) ?? {};\n const methodName = String(propertyKey);\n tools[methodName] = new WpMcpToolMetadata(methodName, name);\n Reflect.defineMetadata(METADATA_KEYS.MCP_TOOLS, tools, apiClass);\n };\n}\n\n/** Runtime metadata for one endpoint declared PERMANENTLY outside MCP. */\nexport class InvalidEndpointForMcpMetadata {\n constructor(\n public readonly methodName: string,\n /** WHY it can never be a tool. Non-empty, enforced at decoration time. */\n public readonly reason: string,\n ) {}\n}\n\n/**\n * Declares that this endpoint is PERMANENTLY outside MCP, and says why.\n *\n * ## It is not a suppression, and that distinction is the whole feature\n *\n * `// webpieces-disable api-rules-for-mcp -- <reason>` says \"stop complaining\". This says \"this is\n * structurally impossible, forever\", and the two have different futures: a suppression is something\n * somebody should eventually come back to, and this is something nobody should. Both measured cases\n * are types that are CORRECT and must not be \"fixed\" —\n *\n * - a webhook TRANSPORT envelope, whose `data` is owned by each event's own published contract;\n * typing it here would fork that ownership and make adding an event type a change to the\n * transport contract rather than a registration, and\n * - a freeform-SQL escape hatch, whose result columns depend on the model-authored `SELECT`.\n *\n * The second one is worth stating rather than burying: it is the most obviously useful tool in that\n * repo, and it is excluded by a type that is right.\n *\n * ## Absence already says \"not a tool YET\"\n *\n * A method with no `@WpMcpTool` is simply not published. This decorator is the NEVER, and the reason\n * is what makes `grep -rn InvalidEndpointForMcp` a list of permanent exclusions with their arguments\n * rather than a list of names. `api-rules-for-mcp` restates that list on every run, so an exclusion\n * stays visible instead of being announced once and forgotten.\n *\n * ## It contradicts `@WpMcpTool`\n *\n * Both on one method is an ERROR, asserted by {@link assertApiTypeMatchesMcpTools}. \"Publish this as\n * a tool\" and \"this can never be a tool\" are two spellings of one decision, which is exactly the\n * defect `.claude/rules/no-backwards-compat.md` shim shape #1 rejects.\n *\n * ```typescript\n * @InvalidEndpointForMcp('a transport envelope body is opaque by design; the published partner ' +\n * 'contract for each event type owns its shape')\n * ```\n */\n// webpieces-disable no-function-outside-class -- decorator factories are inherently module-scope\nexport function InvalidEndpointForMcp(reason: string): MethodDecorator {\n if (reason.trim() === '') {\n throw new Error(\n '@InvalidEndpointForMcp requires a non-empty reason. The reason IS the declaration — ' +\n 'without it this is a suppression with a longer name.',\n );\n }\n return (target: object, propertyKey: string | symbol): void => {\n const apiClass = target.constructor;\n const excluded: Record<string, InvalidEndpointForMcpMetadata> =\n Reflect.getMetadata(METADATA_KEYS.MCP_INVALID, apiClass) ?? {};\n const methodName = String(propertyKey);\n excluded[methodName] = new InvalidEndpointForMcpMetadata(methodName, reason);\n Reflect.defineMetadata(METADATA_KEYS.MCP_INVALID, excluded, apiClass);\n };\n}\n\n/** Every method declared permanently outside MCP by {@link InvalidEndpointForMcp}. */\n// webpieces-disable no-function-outside-class -- metadata reader paired with the decorator\nexport function getInvalidEndpointsForMcp(apiClass: Function): InvalidEndpointForMcpMetadata[] {\n const excluded: Record<string, InvalidEndpointForMcpMetadata> =\n Reflect.getMetadata(METADATA_KEYS.MCP_INVALID, apiClass) ?? {};\n return Object.values(excluded);\n}\n\n/** The one mapping from endpoint side effects to MCP advisory annotations. */\n// webpieces-disable no-function-outside-class -- pure metadata mapping shared by MCP registration\nexport function mcpHintsForOperation(\n operation: EndpointOperation,\n openWorldHint: boolean,\n): WpMcpToolHints {\n switch (operation) {\n case READ:\n return new WpMcpToolHints(true, false, true, openWorldHint);\n case WRITE_IDEMPOTENT:\n return new WpMcpToolHints(false, true, true, openWorldHint);\n case WRITE:\n return new WpMcpToolHints(false, true, false, openWorldHint);\n }\n}\n\n/** Returns only methods explicitly opted in through {@link WpMcpTool}. */\n// webpieces-disable no-function-outside-class -- metadata reader paired with the decorator\nexport function getWpMcpTools(apiClass: Function): WpMcpToolMetadata[] {\n const tools: Record<string, WpMcpToolMetadata> =\n Reflect.getMetadata(METADATA_KEYS.MCP_TOOLS, apiClass) ?? {};\n return Object.values(tools);\n}\n"]}
@@ -164,8 +164,29 @@ class McpCatalogJson {
164
164
  }
165
165
  schema.properties = built;
166
166
  }
167
+ const oneOf = record['oneOf'];
168
+ if (Array.isArray(oneOf)) {
169
+ // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs
170
+ schema.oneOf = oneOf.map((branch) => this.schemaFrom(branch));
171
+ schema.discriminator = this.discriminatorFrom(record['discriminator']);
172
+ }
167
173
  return schema;
168
174
  }
175
+ /**
176
+ * The `discriminator` block, or undefined — a union TypeScript cannot narrow is published WITHOUT
177
+ * one, so an absent block is a legal document and not a missing field.
178
+ */
179
+ // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs
180
+ discriminatorFrom(value) {
181
+ if (value === undefined)
182
+ return undefined;
183
+ const record = this.record(value, 'a schema discriminator block');
184
+ const mapping = {};
185
+ for (const [key, branch] of Object.entries(this.record(record['mapping'], 'a discriminator mapping block'))) {
186
+ mapping[key] = this.text(branch, `discriminator mapping '${key}'`);
187
+ }
188
+ return new DtoSchema_1.ApiJsonSchemaDiscriminator(this.text(record['propertyName'], 'discriminator propertyName'), mapping);
189
+ }
169
190
  // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs
170
191
  record(value, what) {
171
192
  if (typeof value !== 'object' || value === null || Array.isArray(value)) {
@@ -1 +1 @@
1
- {"version":3,"file":"McpToolCatalog.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/mcp/McpToolCatalog.ts"],"names":[],"mappings":"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","sourcesContent":["import { ApiJsonSchema, ApiJsonSchemaType } from './DtoSchema';\nimport { WpMcpToolHints } from './McpMetadata';\n\n/**\n * ONE MCP tool, as `tools/list` publishes it.\n *\n * It is BUILT from the source by `@webpieces/api-doc-model`'s `McpSchemaRenderer` at build time, and\n * READ here at boot — so the tool an agent is shown and the tool the server accepts are the same\n * bytes by construction, not by two declarations agreeing.\n *\n * It lives in `core-util` rather than in the extractor package because both ends need it and only\n * one end may depend on the TypeScript compiler API. `ApiJsonSchema` and {@link WpMcpToolHints},\n * which it is made of, are already here for the same reason.\n */\nexport class McpToolDefinition {\n constructor(\n /** The stable protocol name from `@WpMcpTool('...')`. */\n readonly name: string,\n /** The contract method it was rendered from, so a mismatch report can name the source. */\n readonly methodName: string,\n /**\n * The tool documentation: the method's `@mcp` tag when it has one, its JSDoc body otherwise.\n * Documentation has exactly ONE source — the doc comment above the declaration — for the\n * partner reading OpenAPI and the agent reading `tools/list` alike.\n */\n readonly description: string,\n /**\n * All four hints. Three are COMPUTED from `@Endpoint`'s `operation` by\n * `mcpHintsForOperation`, which is the one place that mapping lives; `openWorldHint` comes\n * from `@Endpoint`'s `openWorld` option.\n */\n readonly hints: WpMcpToolHints,\n readonly inputSchema: ApiJsonSchema,\n readonly outputSchema: ApiJsonSchema,\n ) {}\n}\n\n/** Thrown when the catalog a server was handed is not a catalog this release can read. */\nexport class McpToolCatalogError extends Error {\n constructor(\n message: string,\n /** What to do instead, in one sentence. */\n readonly cure: string,\n ) {\n super(`${message} ${cure}`);\n this.name = 'McpToolCatalogError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** The one cure every catalog failure prescribes: rebuild the artifact from the contracts. */\nconst REGENERATE = 'Regenerate it with `wp-openapi --manifest <manifest> --out <dir>`.';\n\n/**\n * Every MCP tool one build published, keyed by protocol name — the artifact `wp-openapi` writes as\n * `mcp-tools.json` and `WpMcpServer` is constructed with.\n *\n * A tool the build never saw is a tool whose schema nobody checked, so `McpToolRegistry` fails fast\n * at boot rather than publishing a shape derived from a second source.\n */\nexport class McpToolCatalog {\n private readonly byName: ReadonlyMap<string, McpToolDefinition>;\n\n constructor(readonly tools: readonly McpToolDefinition[]) {\n const byName = new Map<string, McpToolDefinition>();\n for (const tool of tools) {\n if (byName.has(tool.name)) {\n throw new McpToolCatalogError(\n `Duplicate MCP tool name '${tool.name}' in the generated catalog.`,\n 'Tool names are the protocol identity and must be globally unique — rename one ' +\n \"of the two @WpMcpTool('...') declarations.\",\n );\n }\n byName.set(tool.name, tool);\n }\n this.byName = byName;\n }\n\n find(name: string): McpToolDefinition | undefined {\n return this.byName.get(name);\n }\n\n /** The names this catalog carries, sorted, so a failure can print what WAS published. */\n names(): readonly string[] {\n return [...this.byName.keys()].sort();\n }\n\n /** The bytes `wp-openapi` writes. Sorted by name so the artifact is diffable. */\n toJsonText(): string {\n const tools = [...this.tools].sort((left: McpToolDefinition, right: McpToolDefinition) =>\n left.name.localeCompare(right.name),\n );\n return `${JSON.stringify(tools, undefined, 2)}\\n`;\n }\n\n /**\n * Reads `mcp-tools.json`. Every nested value is rebuilt as a real class instance, because this is\n * the one place untyped bytes become the schemas a live protocol surface publishes.\n *\n * Malformed JSON is not caught here: it throws `SyntaxError` to the caller's own top-level\n * handler, which is where every other read-the-world failure in webpieces is reported.\n */\n // webpieces-disable no-function-outside-class -- static factory of this class\n static fromJsonText(text: string): McpToolCatalog {\n return new McpCatalogJson().parse(text);\n }\n}\n\n/**\n * The PARSE BOUNDARY: untyped bytes in, real class instances out.\n *\n * A class of its own rather than statics on {@link McpToolCatalog} so every step is an ordinary\n * instance method — and because it is the one place in this file where `unknown` is the correct type\n * rather than a missing one. Nothing else in webpieces should ever hold a half-narrowed schema.\n */\nclass McpCatalogJson {\n parse(text: string): McpToolCatalog {\n // webpieces-disable no-any-unknown -- this IS the untyped-bytes boundary; every step below narrows it\n const parsed: unknown = JSON.parse(text);\n if (!Array.isArray(parsed)) {\n throw new McpToolCatalogError(\n 'The MCP tool catalog must be a JSON array of tool definitions.',\n REGENERATE,\n );\n }\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n return new McpToolCatalog(parsed.map((entry: unknown) => this.toolFrom(entry)));\n }\n\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private toolFrom(entry: unknown): McpToolDefinition {\n const record = this.record(entry, 'a tool definition');\n const hints = this.record(record['hints'], 'a tool definition hints block');\n return new McpToolDefinition(\n this.text(record['name'], 'name'),\n this.text(record['methodName'], 'methodName'),\n this.text(record['description'], 'description'),\n new WpMcpToolHints(\n hints['readOnlyHint'] === true,\n hints['destructiveHint'] === true,\n hints['idempotentHint'] === true,\n hints['openWorldHint'] === true,\n ),\n this.schemaFrom(record['inputSchema']),\n this.schemaFrom(record['outputSchema']),\n );\n }\n\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private schemaFrom(value: unknown): ApiJsonSchema {\n const record = this.record(value, 'a JSON schema');\n const schema = new ApiJsonSchema(\n record['type'] as ApiJsonSchemaType | readonly ApiJsonSchemaType[],\n );\n if (typeof record['description'] === 'string') schema.description = record['description'];\n if (typeof record['minimum'] === 'number') schema.minimum = record['minimum'];\n if (typeof record['maximum'] === 'number') schema.maximum = record['maximum'];\n if (Array.isArray(record['required'])) schema.required = record['required'] as string[];\n if (Array.isArray(record['enum'])) schema.enum = record['enum'] as string[];\n if (typeof record['x-mcp-header'] === 'string') {\n schema['x-mcp-header'] = record['x-mcp-header'];\n }\n if (record['items'] !== undefined) schema.items = this.schemaFrom(record['items']);\n const extra = record['additionalProperties'];\n if (typeof extra === 'boolean') schema.additionalProperties = extra;\n else if (extra !== undefined) schema.additionalProperties = this.schemaFrom(extra);\n const properties = record['properties'];\n if (properties !== undefined) {\n const built: Record<string, ApiJsonSchema> = {};\n for (const [key, child] of Object.entries(\n this.record(properties, 'a schema properties block'),\n )) {\n built[key] = this.schemaFrom(child);\n }\n schema.properties = built;\n }\n return schema;\n }\n\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private record(value: unknown, what: string): Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new McpToolCatalogError(`The MCP tool catalog is missing ${what}.`, REGENERATE);\n }\n // webpieces-disable no-any-unknown -- the narrowed result of the check above\n return value as Record<string, unknown>;\n }\n\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private text(value: unknown, field: string): string {\n if (typeof value !== 'string') {\n throw new McpToolCatalogError(\n `The MCP tool catalog has no '${field}' on a tool definition.`,\n REGENERATE,\n );\n }\n return value;\n }\n}\n"]}
1
+ {"version":3,"file":"McpToolCatalog.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/mcp/McpToolCatalog.ts"],"names":[],"mappings":"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","sourcesContent":["import { ApiJsonSchema, ApiJsonSchemaDiscriminator, ApiJsonSchemaType } from './DtoSchema';\nimport { WpMcpToolHints } from './McpMetadata';\n\n/**\n * ONE MCP tool, as `tools/list` publishes it.\n *\n * It is BUILT from the source by `@webpieces/api-doc-model`'s `McpSchemaRenderer` at build time, and\n * READ here at boot — so the tool an agent is shown and the tool the server accepts are the same\n * bytes by construction, not by two declarations agreeing.\n *\n * It lives in `core-util` rather than in the extractor package because both ends need it and only\n * one end may depend on the TypeScript compiler API. `ApiJsonSchema` and {@link WpMcpToolHints},\n * which it is made of, are already here for the same reason.\n */\nexport class McpToolDefinition {\n constructor(\n /** The stable protocol name from `@WpMcpTool('...')`. */\n readonly name: string,\n /** The contract method it was rendered from, so a mismatch report can name the source. */\n readonly methodName: string,\n /**\n * The tool documentation: the method's `@mcp` tag when it has one, its JSDoc body otherwise.\n * Documentation has exactly ONE source — the doc comment above the declaration — for the\n * partner reading OpenAPI and the agent reading `tools/list` alike.\n */\n readonly description: string,\n /**\n * All four hints. Three are COMPUTED from `@Endpoint`'s `operation` by\n * `mcpHintsForOperation`, which is the one place that mapping lives; `openWorldHint` comes\n * from `@Endpoint`'s `openWorld` option.\n */\n readonly hints: WpMcpToolHints,\n readonly inputSchema: ApiJsonSchema,\n readonly outputSchema: ApiJsonSchema,\n ) {}\n}\n\n/** Thrown when the catalog a server was handed is not a catalog this release can read. */\nexport class McpToolCatalogError extends Error {\n constructor(\n message: string,\n /** What to do instead, in one sentence. */\n readonly cure: string,\n ) {\n super(`${message} ${cure}`);\n this.name = 'McpToolCatalogError';\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** The one cure every catalog failure prescribes: rebuild the artifact from the contracts. */\nconst REGENERATE = 'Regenerate it with `wp-openapi --manifest <manifest> --out <dir>`.';\n\n/**\n * Every MCP tool one build published, keyed by protocol name — the artifact `wp-openapi` writes as\n * `mcp-tools.json` and `WpMcpServer` is constructed with.\n *\n * A tool the build never saw is a tool whose schema nobody checked, so `McpToolRegistry` fails fast\n * at boot rather than publishing a shape derived from a second source.\n */\nexport class McpToolCatalog {\n private readonly byName: ReadonlyMap<string, McpToolDefinition>;\n\n constructor(readonly tools: readonly McpToolDefinition[]) {\n const byName = new Map<string, McpToolDefinition>();\n for (const tool of tools) {\n if (byName.has(tool.name)) {\n throw new McpToolCatalogError(\n `Duplicate MCP tool name '${tool.name}' in the generated catalog.`,\n 'Tool names are the protocol identity and must be globally unique — rename one ' +\n \"of the two @WpMcpTool('...') declarations.\",\n );\n }\n byName.set(tool.name, tool);\n }\n this.byName = byName;\n }\n\n find(name: string): McpToolDefinition | undefined {\n return this.byName.get(name);\n }\n\n /** The names this catalog carries, sorted, so a failure can print what WAS published. */\n names(): readonly string[] {\n return [...this.byName.keys()].sort();\n }\n\n /** The bytes `wp-openapi` writes. Sorted by name so the artifact is diffable. */\n toJsonText(): string {\n const tools = [...this.tools].sort((left: McpToolDefinition, right: McpToolDefinition) =>\n left.name.localeCompare(right.name),\n );\n return `${JSON.stringify(tools, undefined, 2)}\\n`;\n }\n\n /**\n * Reads `mcp-tools.json`. Every nested value is rebuilt as a real class instance, because this is\n * the one place untyped bytes become the schemas a live protocol surface publishes.\n *\n * Malformed JSON is not caught here: it throws `SyntaxError` to the caller's own top-level\n * handler, which is where every other read-the-world failure in webpieces is reported.\n */\n // webpieces-disable no-function-outside-class -- static factory of this class\n static fromJsonText(text: string): McpToolCatalog {\n return new McpCatalogJson().parse(text);\n }\n}\n\n/**\n * The PARSE BOUNDARY: untyped bytes in, real class instances out.\n *\n * A class of its own rather than statics on {@link McpToolCatalog} so every step is an ordinary\n * instance method — and because it is the one place in this file where `unknown` is the correct type\n * rather than a missing one. Nothing else in webpieces should ever hold a half-narrowed schema.\n */\nclass McpCatalogJson {\n parse(text: string): McpToolCatalog {\n // webpieces-disable no-any-unknown -- this IS the untyped-bytes boundary; every step below narrows it\n const parsed: unknown = JSON.parse(text);\n if (!Array.isArray(parsed)) {\n throw new McpToolCatalogError(\n 'The MCP tool catalog must be a JSON array of tool definitions.',\n REGENERATE,\n );\n }\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n return new McpToolCatalog(parsed.map((entry: unknown) => this.toolFrom(entry)));\n }\n\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private toolFrom(entry: unknown): McpToolDefinition {\n const record = this.record(entry, 'a tool definition');\n const hints = this.record(record['hints'], 'a tool definition hints block');\n return new McpToolDefinition(\n this.text(record['name'], 'name'),\n this.text(record['methodName'], 'methodName'),\n this.text(record['description'], 'description'),\n new WpMcpToolHints(\n hints['readOnlyHint'] === true,\n hints['destructiveHint'] === true,\n hints['idempotentHint'] === true,\n hints['openWorldHint'] === true,\n ),\n this.schemaFrom(record['inputSchema']),\n this.schemaFrom(record['outputSchema']),\n );\n }\n\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private schemaFrom(value: unknown): ApiJsonSchema {\n const record = this.record(value, 'a JSON schema');\n const schema = new ApiJsonSchema(\n record['type'] as ApiJsonSchemaType | readonly ApiJsonSchemaType[],\n );\n if (typeof record['description'] === 'string') schema.description = record['description'];\n if (typeof record['minimum'] === 'number') schema.minimum = record['minimum'];\n if (typeof record['maximum'] === 'number') schema.maximum = record['maximum'];\n if (Array.isArray(record['required'])) schema.required = record['required'] as string[];\n if (Array.isArray(record['enum'])) schema.enum = record['enum'] as string[];\n if (typeof record['x-mcp-header'] === 'string') {\n schema['x-mcp-header'] = record['x-mcp-header'];\n }\n if (record['items'] !== undefined) schema.items = this.schemaFrom(record['items']);\n const extra = record['additionalProperties'];\n if (typeof extra === 'boolean') schema.additionalProperties = extra;\n else if (extra !== undefined) schema.additionalProperties = this.schemaFrom(extra);\n const properties = record['properties'];\n if (properties !== undefined) {\n const built: Record<string, ApiJsonSchema> = {};\n for (const [key, child] of Object.entries(\n this.record(properties, 'a schema properties block'),\n )) {\n built[key] = this.schemaFrom(child);\n }\n schema.properties = built;\n }\n const oneOf = record['oneOf'];\n if (Array.isArray(oneOf)) {\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n schema.oneOf = oneOf.map((branch: unknown) => this.schemaFrom(branch));\n schema.discriminator = this.discriminatorFrom(record['discriminator']);\n }\n return schema;\n }\n\n /**\n * The `discriminator` block, or undefined — a union TypeScript cannot narrow is published WITHOUT\n * one, so an absent block is a legal document and not a missing field.\n */\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private discriminatorFrom(value: unknown): ApiJsonSchemaDiscriminator | undefined {\n if (value === undefined) return undefined;\n const record = this.record(value, 'a schema discriminator block');\n const mapping: Record<string, string> = {};\n for (const [key, branch] of Object.entries(\n this.record(record['mapping'], 'a discriminator mapping block'),\n )) {\n mapping[key] = this.text(branch, `discriminator mapping '${key}'`);\n }\n return new ApiJsonSchemaDiscriminator(\n this.text(record['propertyName'], 'discriminator propertyName'),\n mapping,\n );\n }\n\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private record(value: unknown, what: string): Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new McpToolCatalogError(`The MCP tool catalog is missing ${what}.`, REGENERATE);\n }\n // webpieces-disable no-any-unknown -- the narrowed result of the check above\n return value as Record<string, unknown>;\n }\n\n // webpieces-disable no-any-unknown -- parsing JSON is exactly where unknown belongs\n private text(value: unknown, field: string): string {\n if (typeof value !== 'string') {\n throw new McpToolCatalogError(\n `The MCP tool catalog has no '${field}' on a tool definition.`,\n REGENERATE,\n );\n }\n return value;\n }\n}\n"]}