@webpieces/core-util 0.4.762 → 0.4.765
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/package.json +1 -1
- package/src/errors/ApiError.d.ts +43 -49
- package/src/errors/ApiError.js +54 -59
- package/src/errors/ApiError.js.map +1 -1
- package/src/errors/ApiErrorCodec.d.ts +11 -6
- package/src/errors/ApiErrorCodec.js +93 -87
- package/src/errors/ApiErrorCodec.js.map +1 -1
- package/src/errors/AuthErrorSubtypes.d.ts +9 -0
- package/src/errors/AuthErrorSubtypes.js +13 -0
- package/src/errors/AuthErrorSubtypes.js.map +1 -0
- package/src/errors/index.d.ts +3 -1
- package/src/errors/index.js +24 -15
- package/src/errors/index.js.map +1 -1
- package/src/http/{TimeoutError.d.ts → ApiCallTimeoutError.d.ts} +1 -1
- package/src/http/{TimeoutError.js → ApiCallTimeoutError.js} +5 -5
- package/src/http/ApiCallTimeoutError.js.map +1 -0
- package/src/http/{HttpErrorStatus.d.ts → ApiErrorHttpStatus.d.ts} +2 -1
- package/src/http/ApiErrorHttpStatus.js +43 -0
- package/src/http/ApiErrorHttpStatus.js.map +1 -0
- package/src/http/CallDeadline.js +2 -2
- package/src/http/CallDeadline.js.map +1 -1
- package/src/http/ErrorTranslators.d.ts +5 -3
- package/src/http/ErrorTranslators.js.map +1 -1
- package/src/http/HttpResponseDto.d.ts +1 -1
- package/src/http/HttpResponseDto.js +2 -2
- package/src/http/HttpResponseDto.js.map +1 -1
- package/src/http/WebpiecesDefaultFailureClassifier.d.ts +6 -6
- package/src/http/WebpiecesDefaultFailureClassifier.js +12 -12
- package/src/http/WebpiecesDefaultFailureClassifier.js.map +1 -1
- package/src/http/decorators.d.ts +3 -1
- package/src/http/decorators.js +3 -1
- package/src/http/decorators.js.map +1 -1
- package/src/http/networkReject.d.ts +2 -2
- package/src/http/networkReject.js +5 -5
- package/src/http/networkReject.js.map +1 -1
- package/src/index.d.ts +9 -4
- package/src/index.js +37 -38
- package/src/index.js.map +1 -1
- package/src/ipc/IpcConnection.js +14 -14
- package/src/ipc/IpcConnection.js.map +1 -1
- package/src/ipc/IpcDecorators.js +9 -9
- package/src/ipc/IpcDecorators.js.map +1 -1
- package/src/ipc/IpcIdentity.js +1 -1
- package/src/ipc/IpcIdentity.js.map +1 -1
- package/src/ipc/IpcProtocol.js +5 -5
- package/src/ipc/IpcProtocol.js.map +1 -1
- package/src/ipc/index.d.ts +1 -1
- package/src/ipc/index.js +3 -3
- package/src/ipc/index.js.map +1 -1
- package/src/mcp/DtoSchema.d.ts +70 -0
- package/src/mcp/DtoSchema.js +294 -0
- package/src/mcp/DtoSchema.js.map +1 -0
- package/src/mcp/McpCompileAssertions.d.ts +1 -0
- package/src/mcp/McpCompileAssertions.js +65 -0
- package/src/mcp/McpCompileAssertions.js.map +1 -0
- package/src/mcp/McpMetadata.d.ts +51 -0
- package/src/mcp/McpMetadata.js +64 -0
- package/src/mcp/McpMetadata.js.map +1 -0
- package/src/http/HttpErrorStatus.js +0 -38
- package/src/http/HttpErrorStatus.js.map +0 -1
- package/src/http/TimeoutError.js.map +0 -1
- package/src/http/errors.d.ts +0 -56
- package/src/http/errors.js +0 -95
- package/src/http/errors.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IpcProtocol.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/ipc/IpcProtocol.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"IpcProtocol.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/ipc/IpcProtocol.ts"],"names":[],"mappings":"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","sourcesContent":["import { ApiErrorPayload, ApiImplementationError } from '../errors';\nimport { IpcIdentity } from './IpcIdentity';\n\nexport class IpcCallContext {\n constructor(\n readonly txId: string,\n readonly callId: string,\n readonly parentCallId?: string,\n ) {\n IpcIdentity.assert(txId, 'transaction');\n IpcIdentity.assert(callId, 'call');\n if (parentCallId !== undefined) IpcIdentity.assert(parentCallId, 'parent call');\n }\n}\n\nexport class IpcRequest {\n readonly version = 1;\n readonly type = 'request';\n constructor(\n readonly apiId: string,\n readonly methodId: string,\n readonly context: IpcCallContext,\n // webpieces-disable no-any-unknown -- untrusted IPC data is schema-validated; generic dispatch cannot assume a DTO type before validation\n readonly body: unknown,\n ) {}\n}\nexport class IpcSuccess {\n readonly version = 1;\n readonly type = 'success';\n constructor(\n readonly context: IpcCallContext,\n // webpieces-disable no-any-unknown -- untrusted IPC data is schema-validated; generic dispatch cannot assume a DTO type before validation\n readonly body: unknown,\n ) {}\n}\nexport class IpcFailure {\n readonly version = 1;\n readonly type = 'failure';\n constructor(\n readonly context: IpcCallContext,\n readonly error: ApiErrorPayload,\n ) {}\n}\nexport type IpcReply = IpcSuccess | IpcFailure;\nexport type IpcMessage = IpcRequest | IpcReply;\n\nexport class IpcProtocol {\n // webpieces-disable no-function-outside-class -- portable stateless IPC primitive; no platform DI container exists on this boundary; webpieces-disable no-any-unknown -- untrusted IPC data is schema-validated; generic dispatch cannot assume a DTO type before validation\n static record(value: unknown): Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new ApiImplementationError('Malformed IPC envelope');\n }\n // webpieces-disable no-any-unknown -- untrusted IPC data is schema-validated; generic dispatch cannot assume a DTO type before validation\n return value as Record<string, unknown>;\n }\n\n // webpieces-disable no-function-outside-class -- portable stateless IPC primitive; no platform DI container exists on this boundary\n static parse(json: string): IpcMessage {\n const value = IpcProtocol.record(JSON.parse(json));\n if (value['version'] !== 1)\n throw new ApiImplementationError('Unsupported IPC protocol version');\n const raw = IpcProtocol.record(value['context']);\n const context = new IpcCallContext(\n raw['txId'] as string,\n raw['callId'] as string,\n raw['parentCallId'] as string | undefined,\n );\n switch (value['type']) {\n case 'request':\n IpcIdentity.assert(value['apiId'], 'API');\n IpcIdentity.assert(value['methodId'], 'method');\n if (!Object.prototype.hasOwnProperty.call(value, 'body'))\n throw new ApiImplementationError('Missing IPC request body');\n return new IpcRequest(value['apiId'], value['methodId'], context, value['body']);\n case 'success':\n if (!Object.prototype.hasOwnProperty.call(value, 'body'))\n throw new ApiImplementationError('Missing IPC success body');\n return new IpcSuccess(context, value['body']);\n case 'failure':\n return new IpcFailure(\n context,\n // webpieces-disable no-any-unknown -- untrusted IPC data is schema-validated; generic dispatch cannot assume a DTO type before validation\n IpcProtocol.record(value['error']) as unknown as ApiErrorPayload,\n );\n default:\n throw new ApiImplementationError('Invalid IPC message discriminator');\n }\n }\n\n // webpieces-disable no-function-outside-class -- portable stateless IPC primitive; no platform DI container exists on this boundary\n static sameContext(a: IpcCallContext, b: IpcCallContext): boolean {\n return a.callId === b.callId && a.txId === b.txId && a.parentCallId === b.parentCallId;\n }\n}\n"]}
|
package/src/ipc/index.d.ts
CHANGED
|
@@ -12,5 +12,5 @@ export { MaskLog } from '../http/decorators';
|
|
|
12
12
|
export { ApiMethodInfo } from '../http/ApiMethodInfo';
|
|
13
13
|
export { LogApiCallImpl } from '../http/LogApiCall';
|
|
14
14
|
export type { ApiCallContext } from '../http/ApiCallContext';
|
|
15
|
-
export {
|
|
15
|
+
export { ApiCallTimeoutError } from '../http/ApiCallTimeoutError';
|
|
16
16
|
export { toError } from '../lib/errorUtils';
|
package/src/ipc/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.toError = exports.
|
|
3
|
+
exports.toError = exports.ApiCallTimeoutError = exports.LogApiCallImpl = exports.ApiMethodInfo = exports.MaskLog = exports.MaskSpec = exports.IpcCallLogger = exports.IpcErrors = exports.IpcTransportError = exports.IpcConnectionOptions = exports.IpcConnection = exports.IpcProtocol = exports.IpcFailure = exports.IpcSuccess = exports.IpcRequest = exports.IpcCallContext = exports.IPC_METADATA_KEYS = exports.assertNotInternalApi = exports.assertInternalApi = exports.getIpcMaskSpec = exports.getIpcEndpointKind = exports.getIpcEndpoints = exports.getIpcApiId = exports.isInternalApi = exports.WpIpcEndpoint = exports.WpInternal = exports.IpcIdentity = void 0;
|
|
4
4
|
var IpcIdentity_1 = require("./IpcIdentity");
|
|
5
5
|
Object.defineProperty(exports, "IpcIdentity", { enumerable: true, get: function () { return IpcIdentity_1.IpcIdentity; } });
|
|
6
6
|
var IpcDecorators_1 = require("./IpcDecorators");
|
|
@@ -35,8 +35,8 @@ var ApiMethodInfo_1 = require("../http/ApiMethodInfo");
|
|
|
35
35
|
Object.defineProperty(exports, "ApiMethodInfo", { enumerable: true, get: function () { return ApiMethodInfo_1.ApiMethodInfo; } });
|
|
36
36
|
var LogApiCall_1 = require("../http/LogApiCall");
|
|
37
37
|
Object.defineProperty(exports, "LogApiCallImpl", { enumerable: true, get: function () { return LogApiCall_1.LogApiCallImpl; } });
|
|
38
|
-
var
|
|
39
|
-
Object.defineProperty(exports, "
|
|
38
|
+
var ApiCallTimeoutError_1 = require("../http/ApiCallTimeoutError");
|
|
39
|
+
Object.defineProperty(exports, "ApiCallTimeoutError", { enumerable: true, get: function () { return ApiCallTimeoutError_1.ApiCallTimeoutError; } });
|
|
40
40
|
var errorUtils_1 = require("../lib/errorUtils");
|
|
41
41
|
Object.defineProperty(exports, "toError", { enumerable: true, get: function () { return errorUtils_1.toError; } });
|
|
42
42
|
//# sourceMappingURL=index.js.map
|
package/src/ipc/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/ipc/index.ts"],"names":[],"mappings":";;;AAAA,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AACpB,iDAWyB;AAVrB,2GAAA,UAAU,OAAA;AACV,8GAAA,aAAa,OAAA;AACb,8GAAA,aAAa,OAAA;AACb,4GAAA,WAAW,OAAA;AACX,gHAAA,eAAe,OAAA;AACf,mHAAA,kBAAkB,OAAA;AAClB,+GAAA,cAAc,OAAA;AACd,kHAAA,iBAAiB,OAAA;AACjB,qHAAA,oBAAoB,OAAA;AACpB,kHAAA,iBAAiB,OAAA;AAGrB,6CAAgG;AAAvF,6GAAA,cAAc,OAAA;AAAE,yGAAA,UAAU,OAAA;AAAE,yGAAA,UAAU,OAAA;AAAE,yGAAA,UAAU,OAAA;AAAE,0GAAA,WAAW,OAAA;AAExE,iDAAoG;AAA3F,8GAAA,aAAa,OAAA;AAAE,qHAAA,oBAAoB,OAAA;AAAE,kHAAA,iBAAiB,OAAA;AAAE,0GAAA,SAAS,OAAA;AAE1E,2CAA6C;AAApC,2GAAA,aAAa,OAAA;AAEtB,qDAAgD;AAAvC,wGAAA,QAAQ,OAAA;AACjB,iDAA6C;AAApC,qGAAA,OAAO,OAAA;AAChB,uDAAsD;AAA7C,8GAAA,aAAa,OAAA;AACtB,iDAAoD;AAA3C,4GAAA,cAAc,OAAA;AAEvB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/ipc/index.ts"],"names":[],"mappings":";;;AAAA,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AACpB,iDAWyB;AAVrB,2GAAA,UAAU,OAAA;AACV,8GAAA,aAAa,OAAA;AACb,8GAAA,aAAa,OAAA;AACb,4GAAA,WAAW,OAAA;AACX,gHAAA,eAAe,OAAA;AACf,mHAAA,kBAAkB,OAAA;AAClB,+GAAA,cAAc,OAAA;AACd,kHAAA,iBAAiB,OAAA;AACjB,qHAAA,oBAAoB,OAAA;AACpB,kHAAA,iBAAiB,OAAA;AAGrB,6CAAgG;AAAvF,6GAAA,cAAc,OAAA;AAAE,yGAAA,UAAU,OAAA;AAAE,yGAAA,UAAU,OAAA;AAAE,yGAAA,UAAU,OAAA;AAAE,0GAAA,WAAW,OAAA;AAExE,iDAAoG;AAA3F,8GAAA,aAAa,OAAA;AAAE,qHAAA,oBAAoB,OAAA;AAAE,kHAAA,iBAAiB,OAAA;AAAE,0GAAA,SAAS,OAAA;AAE1E,2CAA6C;AAApC,2GAAA,aAAa,OAAA;AAEtB,qDAAgD;AAAvC,wGAAA,QAAQ,OAAA;AACjB,iDAA6C;AAApC,qGAAA,OAAO,OAAA;AAChB,uDAAsD;AAA7C,8GAAA,aAAa,OAAA;AACtB,iDAAoD;AAA3C,4GAAA,cAAc,OAAA;AAEvB,mEAAkE;AAAzD,0HAAA,mBAAmB,OAAA;AAE5B,gDAA4C;AAAnC,qGAAA,OAAO,OAAA","sourcesContent":["export { IpcIdentity } from './IpcIdentity';\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 './IpcDecorators';\nexport type { IpcApiType, IpcEndpointKind, IpcEndpointOptions } from './IpcDecorators';\nexport { IpcCallContext, IpcRequest, IpcSuccess, IpcFailure, IpcProtocol } from './IpcProtocol';\nexport type { IpcReply, IpcMessage } from './IpcProtocol';\nexport { IpcConnection, IpcConnectionOptions, IpcTransportError, IpcErrors } from './IpcConnection';\nexport type { IpcTransport, IpcScheduler, IpcErrorOwner } from './IpcConnection';\nexport { IpcCallLogger } from './IpcLogging';\nexport type { IpcLogging } from './IpcLogging';\nexport { MaskSpec } from '../http/LogFieldMask';\nexport { MaskLog } from '../http/decorators';\nexport { ApiMethodInfo } from '../http/ApiMethodInfo';\nexport { LogApiCallImpl } from '../http/LogApiCall';\nexport type { ApiCallContext } from '../http/ApiCallContext';\nexport { ApiCallTimeoutError } from '../http/ApiCallTimeoutError';\n\nexport { toError } from '../lib/errorUtils';\n"]}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
export type DtoClass = abstract new (...args: any[]) => unknown;
|
|
3
|
+
export type DtoArrayItem = 'string' | 'number' | 'integer' | 'boolean' | DtoClass;
|
|
4
|
+
/** Runtime values accepted at the DTO validation boundary. */
|
|
5
|
+
export type DtoValue = object | string | number | boolean | null;
|
|
6
|
+
/** Additive field metadata; the scalar/object type itself comes from `design:type`. */
|
|
7
|
+
export declare class WpDtoFieldOptions {
|
|
8
|
+
readonly description: string;
|
|
9
|
+
/** Required/optional is explicit because TypeScript erases `?` at runtime. */
|
|
10
|
+
readonly required: boolean;
|
|
11
|
+
/** Required for arrays because TypeScript erases their element type. */
|
|
12
|
+
readonly arrayItems?: DtoArrayItem | undefined;
|
|
13
|
+
/** A reflected Number is otherwise emitted as JSON Schema `number`. */
|
|
14
|
+
readonly integer: boolean;
|
|
15
|
+
readonly minimum?: number | undefined;
|
|
16
|
+
readonly maximum?: number | undefined;
|
|
17
|
+
readonly enumValues?: readonly [string, ...string[]] | undefined;
|
|
18
|
+
constructor(description: string,
|
|
19
|
+
/** Required/optional is explicit because TypeScript erases `?` at runtime. */
|
|
20
|
+
required: boolean,
|
|
21
|
+
/** Required for arrays because TypeScript erases their element type. */
|
|
22
|
+
arrayItems?: DtoArrayItem | undefined,
|
|
23
|
+
/** A reflected Number is otherwise emitted as JSON Schema `number`. */
|
|
24
|
+
integer?: boolean, minimum?: number | undefined, maximum?: number | undefined, enumValues?: readonly [string, ...string[]] | undefined);
|
|
25
|
+
}
|
|
26
|
+
export declare class WpDtoFieldMetadata {
|
|
27
|
+
readonly propertyKey: string;
|
|
28
|
+
readonly options: WpDtoFieldOptions;
|
|
29
|
+
constructor(propertyKey: string, options: WpDtoFieldOptions);
|
|
30
|
+
}
|
|
31
|
+
/** The JSON Schema subset emitted for Webpieces API DTOs. */
|
|
32
|
+
export declare class ApiJsonSchema {
|
|
33
|
+
type?: 'object' | 'string' | 'number' | 'integer' | 'boolean' | 'array';
|
|
34
|
+
description?: string;
|
|
35
|
+
properties?: Record<string, ApiJsonSchema>;
|
|
36
|
+
required?: string[];
|
|
37
|
+
additionalProperties?: boolean;
|
|
38
|
+
items?: ApiJsonSchema;
|
|
39
|
+
enum?: string[];
|
|
40
|
+
minimum?: number;
|
|
41
|
+
maximum?: number;
|
|
42
|
+
constructor(type?: 'object' | 'string' | 'number' | 'integer' | 'boolean' | 'array');
|
|
43
|
+
}
|
|
44
|
+
/** A validation result safe to show to an API caller/model. */
|
|
45
|
+
export declare class DtoValidationFailure {
|
|
46
|
+
readonly message: string;
|
|
47
|
+
constructor(message: string);
|
|
48
|
+
}
|
|
49
|
+
/** Marks a request/response class as a schema-bearing DTO, including intentionally empty DTOs. */
|
|
50
|
+
export declare function WpDto(): ClassDecorator;
|
|
51
|
+
/** Adds documentation and runtime-only shape facts to a DTO property. */
|
|
52
|
+
export declare function WpDtoField(options: WpDtoFieldOptions): PropertyDecorator;
|
|
53
|
+
/** Declares an async API method's response DTO because reflection sees Promise, not its generic. */
|
|
54
|
+
export declare function WpResponseDto(responseDto: () => DtoClass): MethodDecorator;
|
|
55
|
+
/** Builds and validates the same DTO schema used for MCP input and structured output. */
|
|
56
|
+
export declare class DtoSchemaBuilder {
|
|
57
|
+
requestClassOf(apiClass: Function, methodName: string): DtoClass;
|
|
58
|
+
responseClassOf(apiClass: Function, methodName: string): DtoClass;
|
|
59
|
+
build(dtoClass: DtoClass): ApiJsonSchema;
|
|
60
|
+
validate(dtoClass: DtoClass, value: DtoValue): DtoValidationFailure | undefined;
|
|
61
|
+
private buildAt;
|
|
62
|
+
private fieldSchema;
|
|
63
|
+
private validateOptions;
|
|
64
|
+
private itemSchema;
|
|
65
|
+
private validateAt;
|
|
66
|
+
private validateField;
|
|
67
|
+
private validateArray;
|
|
68
|
+
private fields;
|
|
69
|
+
private requireDto;
|
|
70
|
+
}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DtoSchemaBuilder = exports.DtoValidationFailure = exports.ApiJsonSchema = exports.WpDtoFieldMetadata = exports.WpDtoFieldOptions = void 0;
|
|
4
|
+
exports.WpDto = WpDto;
|
|
5
|
+
exports.WpDtoField = WpDtoField;
|
|
6
|
+
exports.WpResponseDto = WpResponseDto;
|
|
7
|
+
require("reflect-metadata");
|
|
8
|
+
const DTO_MARKER = 'webpieces:dto-schema';
|
|
9
|
+
const DTO_FIELDS = 'webpieces:dto-fields';
|
|
10
|
+
const RESPONSE_DTOS = 'webpieces:response-dtos';
|
|
11
|
+
/** Additive field metadata; the scalar/object type itself comes from `design:type`. */
|
|
12
|
+
class WpDtoFieldOptions {
|
|
13
|
+
description;
|
|
14
|
+
required;
|
|
15
|
+
arrayItems;
|
|
16
|
+
integer;
|
|
17
|
+
minimum;
|
|
18
|
+
maximum;
|
|
19
|
+
enumValues;
|
|
20
|
+
constructor(description,
|
|
21
|
+
/** Required/optional is explicit because TypeScript erases `?` at runtime. */
|
|
22
|
+
required,
|
|
23
|
+
/** Required for arrays because TypeScript erases their element type. */
|
|
24
|
+
arrayItems,
|
|
25
|
+
/** A reflected Number is otherwise emitted as JSON Schema `number`. */
|
|
26
|
+
integer = false, minimum, maximum, enumValues) {
|
|
27
|
+
this.description = description;
|
|
28
|
+
this.required = required;
|
|
29
|
+
this.arrayItems = arrayItems;
|
|
30
|
+
this.integer = integer;
|
|
31
|
+
this.minimum = minimum;
|
|
32
|
+
this.maximum = maximum;
|
|
33
|
+
this.enumValues = enumValues;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.WpDtoFieldOptions = WpDtoFieldOptions;
|
|
37
|
+
class WpDtoFieldMetadata {
|
|
38
|
+
propertyKey;
|
|
39
|
+
options;
|
|
40
|
+
constructor(propertyKey, options) {
|
|
41
|
+
this.propertyKey = propertyKey;
|
|
42
|
+
this.options = options;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.WpDtoFieldMetadata = WpDtoFieldMetadata;
|
|
46
|
+
/** The JSON Schema subset emitted for Webpieces API DTOs. */
|
|
47
|
+
class ApiJsonSchema {
|
|
48
|
+
type;
|
|
49
|
+
description;
|
|
50
|
+
properties;
|
|
51
|
+
required;
|
|
52
|
+
additionalProperties;
|
|
53
|
+
items;
|
|
54
|
+
enum;
|
|
55
|
+
minimum;
|
|
56
|
+
maximum;
|
|
57
|
+
constructor(type) {
|
|
58
|
+
this.type = type;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.ApiJsonSchema = ApiJsonSchema;
|
|
62
|
+
/** A validation result safe to show to an API caller/model. */
|
|
63
|
+
class DtoValidationFailure {
|
|
64
|
+
message;
|
|
65
|
+
constructor(message) {
|
|
66
|
+
this.message = message;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.DtoValidationFailure = DtoValidationFailure;
|
|
70
|
+
/** Marks a request/response class as a schema-bearing DTO, including intentionally empty DTOs. */
|
|
71
|
+
// webpieces-disable no-function-outside-class -- decorator factories are inherently module-scope
|
|
72
|
+
function WpDto() {
|
|
73
|
+
return (target) => Reflect.defineMetadata(DTO_MARKER, true, target);
|
|
74
|
+
}
|
|
75
|
+
/** Adds documentation and runtime-only shape facts to a DTO property. */
|
|
76
|
+
// webpieces-disable no-function-outside-class -- decorator factories are inherently module-scope
|
|
77
|
+
function WpDtoField(options) {
|
|
78
|
+
if (options.description.trim() === '') {
|
|
79
|
+
throw new Error('@WpDtoField requires non-empty field documentation.');
|
|
80
|
+
}
|
|
81
|
+
return (target, propertyKey) => {
|
|
82
|
+
const dtoClass = target.constructor;
|
|
83
|
+
const fields = Reflect.getMetadata(DTO_FIELDS, dtoClass) ?? {};
|
|
84
|
+
const name = String(propertyKey);
|
|
85
|
+
fields[name] = new WpDtoFieldMetadata(name, options);
|
|
86
|
+
Reflect.defineMetadata(DTO_FIELDS, fields, dtoClass);
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/** Declares an async API method's response DTO because reflection sees Promise, not its generic. */
|
|
90
|
+
// webpieces-disable no-function-outside-class -- decorator factories are inherently module-scope
|
|
91
|
+
function WpResponseDto(responseDto) {
|
|
92
|
+
return (target, propertyKey) => {
|
|
93
|
+
const apiClass = target.constructor;
|
|
94
|
+
const responses = Reflect.getMetadata(RESPONSE_DTOS, apiClass) ?? {};
|
|
95
|
+
responses[String(propertyKey)] = responseDto;
|
|
96
|
+
Reflect.defineMetadata(RESPONSE_DTOS, responses, apiClass);
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/** Builds and validates the same DTO schema used for MCP input and structured output. */
|
|
100
|
+
class DtoSchemaBuilder {
|
|
101
|
+
requestClassOf(apiClass, methodName) {
|
|
102
|
+
// webpieces-disable no-any-unknown -- reflect-metadata returns erased runtime constructor tokens
|
|
103
|
+
const params = Reflect.getMetadata('design:paramtypes', apiClass.prototype, methodName);
|
|
104
|
+
if (!params || params.length !== 1) {
|
|
105
|
+
throw new Error(`${apiClass.name}.${methodName} must take exactly one @WpDto request class to be an MCP tool.`);
|
|
106
|
+
}
|
|
107
|
+
return params[0];
|
|
108
|
+
}
|
|
109
|
+
responseClassOf(apiClass, methodName) {
|
|
110
|
+
const responses = Reflect.getMetadata(RESPONSE_DTOS, apiClass) ?? {};
|
|
111
|
+
const response = responses[methodName];
|
|
112
|
+
if (!response) {
|
|
113
|
+
throw new Error(`${apiClass.name}.${methodName} must declare @WpResponseDto(() => ResponseClass) before MCP exposure.`);
|
|
114
|
+
}
|
|
115
|
+
return response();
|
|
116
|
+
}
|
|
117
|
+
build(dtoClass) {
|
|
118
|
+
return this.buildAt(dtoClass, new Set());
|
|
119
|
+
}
|
|
120
|
+
validate(dtoClass, value) {
|
|
121
|
+
return this.validateAt(dtoClass, value, '$');
|
|
122
|
+
}
|
|
123
|
+
buildAt(dtoClass, parents) {
|
|
124
|
+
this.requireDto(dtoClass);
|
|
125
|
+
if (parents.has(dtoClass)) {
|
|
126
|
+
throw new Error(`Recursive DTO ${dtoClass.name} cannot use an inline MCP schema.`);
|
|
127
|
+
}
|
|
128
|
+
const nextParents = new Set(parents);
|
|
129
|
+
nextParents.add(dtoClass);
|
|
130
|
+
const schema = new ApiJsonSchema('object');
|
|
131
|
+
schema.properties = {};
|
|
132
|
+
schema.additionalProperties = false;
|
|
133
|
+
const required = [];
|
|
134
|
+
for (const field of this.fields(dtoClass)) {
|
|
135
|
+
schema.properties[field.propertyKey] = this.fieldSchema(dtoClass, field, nextParents);
|
|
136
|
+
if (field.options.required)
|
|
137
|
+
required.push(field.propertyKey);
|
|
138
|
+
}
|
|
139
|
+
if (required.length > 0)
|
|
140
|
+
schema.required = required;
|
|
141
|
+
return schema;
|
|
142
|
+
}
|
|
143
|
+
fieldSchema(dtoClass, field, parents) {
|
|
144
|
+
// webpieces-disable no-any-unknown -- reflect-metadata returns an erased constructor token
|
|
145
|
+
const reflected = Reflect.getMetadata('design:type', dtoClass.prototype, field.propertyKey);
|
|
146
|
+
if (!reflected)
|
|
147
|
+
throw new Error(`No reflected type for ${dtoClass.name}.${field.propertyKey}.`);
|
|
148
|
+
const options = field.options;
|
|
149
|
+
this.validateOptions(dtoClass, field, reflected);
|
|
150
|
+
let schema;
|
|
151
|
+
switch (reflected.name) {
|
|
152
|
+
case 'String':
|
|
153
|
+
schema = new ApiJsonSchema('string');
|
|
154
|
+
break;
|
|
155
|
+
case 'Number':
|
|
156
|
+
schema = new ApiJsonSchema(options.integer ? 'integer' : 'number');
|
|
157
|
+
break;
|
|
158
|
+
case 'Boolean':
|
|
159
|
+
schema = new ApiJsonSchema('boolean');
|
|
160
|
+
break;
|
|
161
|
+
case 'Array':
|
|
162
|
+
if (!options.arrayItems) {
|
|
163
|
+
throw new Error(`${dtoClass.name}.${field.propertyKey} must declare arrayItems.`);
|
|
164
|
+
}
|
|
165
|
+
schema = new ApiJsonSchema('array');
|
|
166
|
+
schema.items = this.itemSchema(options.arrayItems, parents);
|
|
167
|
+
break;
|
|
168
|
+
default:
|
|
169
|
+
schema = this.buildAt(reflected, parents);
|
|
170
|
+
}
|
|
171
|
+
schema.description = options.description;
|
|
172
|
+
if (options.minimum !== undefined)
|
|
173
|
+
schema.minimum = options.minimum;
|
|
174
|
+
if (options.maximum !== undefined)
|
|
175
|
+
schema.maximum = options.maximum;
|
|
176
|
+
if (options.enumValues)
|
|
177
|
+
schema.enum = [...options.enumValues];
|
|
178
|
+
return schema;
|
|
179
|
+
}
|
|
180
|
+
validateOptions(dtoClass, field, reflected) {
|
|
181
|
+
const label = `${dtoClass.name}.${field.propertyKey}`;
|
|
182
|
+
const options = field.options;
|
|
183
|
+
if (reflected.name !== 'Array' && options.arrayItems) {
|
|
184
|
+
throw new Error(`${label} declares arrayItems but is not an array.`);
|
|
185
|
+
}
|
|
186
|
+
if (reflected.name !== 'Number' &&
|
|
187
|
+
(options.integer || options.minimum !== undefined || options.maximum !== undefined)) {
|
|
188
|
+
throw new Error(`${label} declares numeric constraints but is not a number.`);
|
|
189
|
+
}
|
|
190
|
+
if (options.minimum !== undefined && options.maximum !== undefined && options.minimum > options.maximum) {
|
|
191
|
+
throw new Error(`${label} minimum cannot exceed maximum.`);
|
|
192
|
+
}
|
|
193
|
+
if (options.enumValues && reflected.name !== 'String') {
|
|
194
|
+
throw new Error(`${label} enumValues require a string field.`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
itemSchema(item, parents) {
|
|
198
|
+
if (typeof item !== 'string')
|
|
199
|
+
return this.buildAt(item, parents);
|
|
200
|
+
return new ApiJsonSchema(item === 'integer' ? 'integer' : item);
|
|
201
|
+
}
|
|
202
|
+
validateAt(dtoClass, value, path) {
|
|
203
|
+
this.requireDto(dtoClass);
|
|
204
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
205
|
+
return new DtoValidationFailure(`${path} must be an object`);
|
|
206
|
+
}
|
|
207
|
+
const fields = this.fields(dtoClass);
|
|
208
|
+
const known = new Set(fields.map((field) => field.propertyKey));
|
|
209
|
+
for (const key of Object.keys(value)) {
|
|
210
|
+
if (!known.has(key))
|
|
211
|
+
return new DtoValidationFailure(`${path}.${key} is not allowed`);
|
|
212
|
+
}
|
|
213
|
+
for (const field of fields) {
|
|
214
|
+
const fieldValue = Object.getOwnPropertyDescriptor(value, field.propertyKey)?.value;
|
|
215
|
+
if (fieldValue === undefined) {
|
|
216
|
+
if (field.options.required) {
|
|
217
|
+
return new DtoValidationFailure(`${path}.${field.propertyKey} is required`);
|
|
218
|
+
}
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
const failure = this.validateField(dtoClass, field, fieldValue, `${path}.${field.propertyKey}`);
|
|
222
|
+
if (failure)
|
|
223
|
+
return failure;
|
|
224
|
+
}
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
validateField(dtoClass, field, value, path) {
|
|
228
|
+
// webpieces-disable no-any-unknown -- reflect-metadata returns an erased constructor token
|
|
229
|
+
const reflected = Reflect.getMetadata('design:type', dtoClass.prototype, field.propertyKey);
|
|
230
|
+
const expected = reflected?.name;
|
|
231
|
+
if (expected === 'Array')
|
|
232
|
+
return this.validateArray(field.options, value, path);
|
|
233
|
+
if (expected === 'String') {
|
|
234
|
+
if (typeof value !== 'string')
|
|
235
|
+
return new DtoValidationFailure(`${path} must be a string`);
|
|
236
|
+
if (field.options.enumValues && !field.options.enumValues.includes(value)) {
|
|
237
|
+
return new DtoValidationFailure(`${path} must be one of: ${field.options.enumValues.join(', ')}`);
|
|
238
|
+
}
|
|
239
|
+
return undefined;
|
|
240
|
+
}
|
|
241
|
+
if (expected === 'Number') {
|
|
242
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
243
|
+
return new DtoValidationFailure(`${path} must be a number`);
|
|
244
|
+
}
|
|
245
|
+
if (field.options.integer && !Number.isInteger(value)) {
|
|
246
|
+
return new DtoValidationFailure(`${path} must be an integer`);
|
|
247
|
+
}
|
|
248
|
+
if (field.options.minimum !== undefined && value < field.options.minimum) {
|
|
249
|
+
return new DtoValidationFailure(`${path} must be >= ${field.options.minimum}`);
|
|
250
|
+
}
|
|
251
|
+
if (field.options.maximum !== undefined && value > field.options.maximum) {
|
|
252
|
+
return new DtoValidationFailure(`${path} must be <= ${field.options.maximum}`);
|
|
253
|
+
}
|
|
254
|
+
return undefined;
|
|
255
|
+
}
|
|
256
|
+
if (expected === 'Boolean') {
|
|
257
|
+
return typeof value === 'boolean'
|
|
258
|
+
? undefined
|
|
259
|
+
: new DtoValidationFailure(`${path} must be a boolean`);
|
|
260
|
+
}
|
|
261
|
+
return this.validateAt(reflected, value, path);
|
|
262
|
+
}
|
|
263
|
+
validateArray(options, value, path) {
|
|
264
|
+
if (!Array.isArray(value))
|
|
265
|
+
return new DtoValidationFailure(`${path} must be an array`);
|
|
266
|
+
if (!options.arrayItems)
|
|
267
|
+
return new DtoValidationFailure(`${path} has no item type`);
|
|
268
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
269
|
+
const item = value[index];
|
|
270
|
+
if (typeof options.arrayItems !== 'string') {
|
|
271
|
+
const nested = this.validateAt(options.arrayItems, item, `${path}[${index}]`);
|
|
272
|
+
if (nested)
|
|
273
|
+
return nested;
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
const wanted = options.arrayItems === 'integer' ? 'number' : options.arrayItems;
|
|
277
|
+
if (typeof item !== wanted || (options.arrayItems === 'integer' && !Number.isInteger(item))) {
|
|
278
|
+
return new DtoValidationFailure(`${path}[${index}] must be ${options.arrayItems}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
fields(dtoClass) {
|
|
284
|
+
const fields = Reflect.getMetadata(DTO_FIELDS, dtoClass) ?? {};
|
|
285
|
+
return Object.values(fields);
|
|
286
|
+
}
|
|
287
|
+
requireDto(dtoClass) {
|
|
288
|
+
if (!Reflect.getMetadata(DTO_MARKER, dtoClass)) {
|
|
289
|
+
throw new Error(`${dtoClass.name} must be decorated with @WpDto() for schema generation.`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
exports.DtoSchemaBuilder = DtoSchemaBuilder;
|
|
294
|
+
//# sourceMappingURL=DtoSchema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DtoSchema.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/mcp/DtoSchema.ts"],"names":[],"mappings":"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","sourcesContent":["import 'reflect-metadata';\n\nconst DTO_MARKER = 'webpieces:dto-schema';\nconst DTO_FIELDS = 'webpieces:dto-fields';\nconst RESPONSE_DTOS = 'webpieces:response-dtos';\n\n// webpieces-disable no-any-unknown -- abstract class tokens necessarily erase constructor parameters\nexport type DtoClass = abstract new (...args: any[]) => unknown;\nexport type DtoArrayItem = 'string' | 'number' | 'integer' | 'boolean' | DtoClass;\n/** Runtime values accepted at the DTO validation boundary. */\nexport type DtoValue = object | string | number | boolean | null;\n\n/** Additive field metadata; the scalar/object type itself comes from `design:type`. */\nexport class WpDtoFieldOptions {\n constructor(\n public readonly description: string,\n /** Required/optional is explicit because TypeScript erases `?` at runtime. */\n public readonly required: boolean,\n /** Required for arrays because TypeScript erases their element type. */\n public readonly arrayItems?: DtoArrayItem,\n /** A reflected Number is otherwise emitted as JSON Schema `number`. */\n public readonly integer: boolean = false,\n public readonly minimum?: number,\n public readonly maximum?: number,\n public readonly enumValues?: readonly [string, ...string[]],\n ) {}\n}\n\nexport class WpDtoFieldMetadata {\n constructor(\n public readonly propertyKey: string,\n public readonly options: WpDtoFieldOptions,\n ) {}\n}\n\n/** The JSON Schema subset emitted for Webpieces API DTOs. */\nexport class ApiJsonSchema {\n type?: 'object' | 'string' | 'number' | 'integer' | 'boolean' | 'array';\n description?: string;\n properties?: Record<string, ApiJsonSchema>;\n required?: string[];\n additionalProperties?: boolean;\n items?: ApiJsonSchema;\n enum?: string[];\n minimum?: number;\n maximum?: number;\n\n constructor(type?: 'object' | 'string' | 'number' | 'integer' | 'boolean' | 'array') {\n this.type = type;\n }\n}\n\n/** A validation result safe to show to an API caller/model. */\nexport class DtoValidationFailure {\n constructor(public readonly message: string) {}\n}\n\n/** Marks a request/response class as a schema-bearing DTO, including intentionally empty DTOs. */\n// webpieces-disable no-function-outside-class -- decorator factories are inherently module-scope\nexport function WpDto(): ClassDecorator {\n return (target: Function): void => Reflect.defineMetadata(DTO_MARKER, true, target);\n}\n\n/** Adds documentation and runtime-only shape facts to a DTO property. */\n// webpieces-disable no-function-outside-class -- decorator factories are inherently module-scope\nexport function WpDtoField(options: WpDtoFieldOptions): PropertyDecorator {\n if (options.description.trim() === '') {\n throw new Error('@WpDtoField requires non-empty field documentation.');\n }\n return (target: object, propertyKey: string | symbol): void => {\n const dtoClass = target.constructor;\n const fields: Record<string, WpDtoFieldMetadata> =\n Reflect.getMetadata(DTO_FIELDS, dtoClass) ?? {};\n const name = String(propertyKey);\n fields[name] = new WpDtoFieldMetadata(name, options);\n Reflect.defineMetadata(DTO_FIELDS, fields, dtoClass);\n };\n}\n\n/** Declares an async API method's response DTO because reflection sees Promise, not its generic. */\n// webpieces-disable no-function-outside-class -- decorator factories are inherently module-scope\nexport function WpResponseDto(responseDto: () => DtoClass): MethodDecorator {\n return (target: object, propertyKey: string | symbol): void => {\n const apiClass = target.constructor;\n const responses: Record<string, () => DtoClass> =\n Reflect.getMetadata(RESPONSE_DTOS, apiClass) ?? {};\n responses[String(propertyKey)] = responseDto;\n Reflect.defineMetadata(RESPONSE_DTOS, responses, apiClass);\n };\n}\n\n/** Builds and validates the same DTO schema used for MCP input and structured output. */\nexport class DtoSchemaBuilder {\n requestClassOf(apiClass: Function, methodName: string): DtoClass {\n // webpieces-disable no-any-unknown -- reflect-metadata returns erased runtime constructor tokens\n const params = Reflect.getMetadata('design:paramtypes', apiClass.prototype, methodName) as\n | DtoClass[]\n | undefined;\n if (!params || params.length !== 1) {\n throw new Error(\n `${apiClass.name}.${methodName} must take exactly one @WpDto request class to be an MCP tool.`,\n );\n }\n return params[0];\n }\n\n responseClassOf(apiClass: Function, methodName: string): DtoClass {\n const responses: Record<string, () => DtoClass> =\n Reflect.getMetadata(RESPONSE_DTOS, apiClass) ?? {};\n const response = responses[methodName];\n if (!response) {\n throw new Error(\n `${apiClass.name}.${methodName} must declare @WpResponseDto(() => ResponseClass) before MCP exposure.`,\n );\n }\n return response();\n }\n\n build(dtoClass: DtoClass): ApiJsonSchema {\n return this.buildAt(dtoClass, new Set<DtoClass>());\n }\n\n validate(dtoClass: DtoClass, value: DtoValue): DtoValidationFailure | undefined {\n return this.validateAt(dtoClass, value, '$');\n }\n\n private buildAt(dtoClass: DtoClass, parents: Set<DtoClass>): ApiJsonSchema {\n this.requireDto(dtoClass);\n if (parents.has(dtoClass)) {\n throw new Error(`Recursive DTO ${dtoClass.name} cannot use an inline MCP schema.`);\n }\n const nextParents = new Set(parents);\n nextParents.add(dtoClass);\n const schema = new ApiJsonSchema('object');\n schema.properties = {};\n schema.additionalProperties = false;\n const required: string[] = [];\n for (const field of this.fields(dtoClass)) {\n schema.properties[field.propertyKey] = this.fieldSchema(dtoClass, field, nextParents);\n if (field.options.required) required.push(field.propertyKey);\n }\n if (required.length > 0) schema.required = required;\n return schema;\n }\n\n private fieldSchema(\n dtoClass: DtoClass,\n field: WpDtoFieldMetadata,\n parents: Set<DtoClass>,\n ): ApiJsonSchema {\n // webpieces-disable no-any-unknown -- reflect-metadata returns an erased constructor token\n const reflected = Reflect.getMetadata(\n 'design:type',\n dtoClass.prototype,\n field.propertyKey,\n ) as DtoClass | undefined;\n if (!reflected) throw new Error(`No reflected type for ${dtoClass.name}.${field.propertyKey}.`);\n const options = field.options;\n this.validateOptions(dtoClass, field, reflected);\n let schema: ApiJsonSchema;\n switch (reflected.name) {\n case 'String':\n schema = new ApiJsonSchema('string');\n break;\n case 'Number':\n schema = new ApiJsonSchema(options.integer ? 'integer' : 'number');\n break;\n case 'Boolean':\n schema = new ApiJsonSchema('boolean');\n break;\n case 'Array':\n if (!options.arrayItems) {\n throw new Error(`${dtoClass.name}.${field.propertyKey} must declare arrayItems.`);\n }\n schema = new ApiJsonSchema('array');\n schema.items = this.itemSchema(options.arrayItems, parents);\n break;\n default:\n schema = this.buildAt(reflected, parents);\n }\n schema.description = options.description;\n if (options.minimum !== undefined) schema.minimum = options.minimum;\n if (options.maximum !== undefined) schema.maximum = options.maximum;\n if (options.enumValues) schema.enum = [...options.enumValues];\n return schema;\n }\n\n private validateOptions(\n dtoClass: DtoClass,\n field: WpDtoFieldMetadata,\n reflected: DtoClass,\n ): void {\n const label = `${dtoClass.name}.${field.propertyKey}`;\n const options = field.options;\n if (reflected.name !== 'Array' && options.arrayItems) {\n throw new Error(`${label} declares arrayItems but is not an array.`);\n }\n if (\n reflected.name !== 'Number' &&\n (options.integer || options.minimum !== undefined || options.maximum !== undefined)\n ) {\n throw new Error(`${label} declares numeric constraints but is not a number.`);\n }\n if (options.minimum !== undefined && options.maximum !== undefined && options.minimum > options.maximum) {\n throw new Error(`${label} minimum cannot exceed maximum.`);\n }\n if (options.enumValues && reflected.name !== 'String') {\n throw new Error(`${label} enumValues require a string field.`);\n }\n }\n\n private itemSchema(item: DtoArrayItem, parents: Set<DtoClass>): ApiJsonSchema {\n if (typeof item !== 'string') return this.buildAt(item, parents);\n return new ApiJsonSchema(item === 'integer' ? 'integer' : item);\n }\n\n private validateAt(\n dtoClass: DtoClass,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n this.requireDto(dtoClass);\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return new DtoValidationFailure(`${path} must be an object`);\n }\n const fields = this.fields(dtoClass);\n const known = new Set(fields.map((field: WpDtoFieldMetadata) => field.propertyKey));\n for (const key of Object.keys(value)) {\n if (!known.has(key)) return new DtoValidationFailure(`${path}.${key} is not allowed`);\n }\n for (const field of fields) {\n const fieldValue = Object.getOwnPropertyDescriptor(value, field.propertyKey)?.value;\n if (fieldValue === undefined) {\n if (field.options.required) {\n return new DtoValidationFailure(`${path}.${field.propertyKey} is required`);\n }\n continue;\n }\n const failure = this.validateField(dtoClass, field, fieldValue, `${path}.${field.propertyKey}`);\n if (failure) return failure;\n }\n return undefined;\n }\n\n private validateField(\n dtoClass: DtoClass,\n field: WpDtoFieldMetadata,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n // webpieces-disable no-any-unknown -- reflect-metadata returns an erased constructor token\n const reflected = Reflect.getMetadata(\n 'design:type',\n dtoClass.prototype,\n field.propertyKey,\n ) as DtoClass;\n const expected = reflected?.name;\n if (expected === 'Array') return this.validateArray(field.options, value, path);\n if (expected === 'String') {\n if (typeof value !== 'string') return new DtoValidationFailure(`${path} must be a string`);\n if (field.options.enumValues && !field.options.enumValues.includes(value)) {\n return new DtoValidationFailure(`${path} must be one of: ${field.options.enumValues.join(', ')}`);\n }\n return undefined;\n }\n if (expected === 'Number') {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n return new DtoValidationFailure(`${path} must be a number`);\n }\n if (field.options.integer && !Number.isInteger(value)) {\n return new DtoValidationFailure(`${path} must be an integer`);\n }\n if (field.options.minimum !== undefined && value < field.options.minimum) {\n return new DtoValidationFailure(`${path} must be >= ${field.options.minimum}`);\n }\n if (field.options.maximum !== undefined && value > field.options.maximum) {\n return new DtoValidationFailure(`${path} must be <= ${field.options.maximum}`);\n }\n return undefined;\n }\n if (expected === 'Boolean') {\n return typeof value === 'boolean'\n ? undefined\n : new DtoValidationFailure(`${path} must be a boolean`);\n }\n return this.validateAt(reflected, value, path);\n }\n\n private validateArray(\n options: WpDtoFieldOptions,\n value: DtoValue,\n path: string,\n ): DtoValidationFailure | undefined {\n if (!Array.isArray(value)) return new DtoValidationFailure(`${path} must be an array`);\n if (!options.arrayItems) return new DtoValidationFailure(`${path} has no item type`);\n for (let index = 0; index < value.length; index += 1) {\n const item = value[index];\n if (typeof options.arrayItems !== 'string') {\n const nested = this.validateAt(options.arrayItems, item, `${path}[${index}]`);\n if (nested) return nested;\n continue;\n }\n const wanted = options.arrayItems === 'integer' ? 'number' : options.arrayItems;\n if (typeof item !== wanted || (options.arrayItems === 'integer' && !Number.isInteger(item))) {\n return new DtoValidationFailure(`${path}[${index}] must be ${options.arrayItems}`);\n }\n }\n return undefined;\n }\n\n private fields(dtoClass: DtoClass): WpDtoFieldMetadata[] {\n const fields: Record<string, WpDtoFieldMetadata> =\n Reflect.getMetadata(DTO_FIELDS, dtoClass) ?? {};\n return Object.values(fields);\n }\n\n private requireDto(dtoClass: DtoClass): void {\n if (!Reflect.getMetadata(DTO_MARKER, dtoClass)) {\n throw new Error(`${dtoClass.name} must be decorated with @WpDto() for schema generation.`);\n }\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const McpMetadata_1 = require("./McpMetadata");
|
|
5
|
+
const DtoSchema_1 = require("./DtoSchema");
|
|
6
|
+
const invalidEmptyEnum = new DtoSchema_1.WpDtoFieldOptions('invalid enum', true, undefined, false, undefined, undefined,
|
|
7
|
+
// @ts-expect-error enumValues must contain at least one allowed string
|
|
8
|
+
[]);
|
|
9
|
+
class InvalidMcpContract {
|
|
10
|
+
// @ts-expect-error read-only tools cannot also claim to be destructive
|
|
11
|
+
invalid() {
|
|
12
|
+
throw new Error('contract only');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
tslib_1.__decorate([
|
|
16
|
+
(0, McpMetadata_1.WpMcpTool)({
|
|
17
|
+
name: 'invalid',
|
|
18
|
+
description: 'This declaration must never compile.',
|
|
19
|
+
readOnlyHint: true,
|
|
20
|
+
destructiveHint: true,
|
|
21
|
+
idempotentHint: true,
|
|
22
|
+
openWorldHint: false,
|
|
23
|
+
}),
|
|
24
|
+
tslib_1.__metadata("design:type", Function),
|
|
25
|
+
tslib_1.__metadata("design:paramtypes", []),
|
|
26
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
27
|
+
], InvalidMcpContract.prototype, "invalid", null);
|
|
28
|
+
class InvalidMcpArities {
|
|
29
|
+
// @ts-expect-error MCP tools require exactly one request DTO
|
|
30
|
+
zero() {
|
|
31
|
+
throw new Error('contract only');
|
|
32
|
+
}
|
|
33
|
+
// @ts-expect-error MCP tools require exactly one request DTO
|
|
34
|
+
multi(_first, _second) {
|
|
35
|
+
throw new Error('contract only');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
tslib_1.__decorate([
|
|
39
|
+
(0, McpMetadata_1.WpMcpTool)({
|
|
40
|
+
name: 'zero-arity',
|
|
41
|
+
description: 'This declaration must never compile.',
|
|
42
|
+
readOnlyHint: true,
|
|
43
|
+
idempotentHint: true,
|
|
44
|
+
openWorldHint: false,
|
|
45
|
+
}),
|
|
46
|
+
tslib_1.__metadata("design:type", Function),
|
|
47
|
+
tslib_1.__metadata("design:paramtypes", []),
|
|
48
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
49
|
+
], InvalidMcpArities.prototype, "zero", null);
|
|
50
|
+
tslib_1.__decorate([
|
|
51
|
+
(0, McpMetadata_1.WpMcpTool)({
|
|
52
|
+
name: 'multi-arity',
|
|
53
|
+
description: 'This declaration must never compile.',
|
|
54
|
+
readOnlyHint: true,
|
|
55
|
+
idempotentHint: true,
|
|
56
|
+
openWorldHint: false,
|
|
57
|
+
}),
|
|
58
|
+
tslib_1.__metadata("design:type", Function),
|
|
59
|
+
tslib_1.__metadata("design:paramtypes", [Object, Object]),
|
|
60
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
61
|
+
], InvalidMcpArities.prototype, "multi", null);
|
|
62
|
+
void InvalidMcpContract;
|
|
63
|
+
void InvalidMcpArities;
|
|
64
|
+
void invalidEmptyEnum;
|
|
65
|
+
//# sourceMappingURL=McpCompileAssertions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"McpCompileAssertions.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/mcp/McpCompileAssertions.ts"],"names":[],"mappings":";;;AAAA,+CAA0C;AAC1C,2CAAgD;AAEhD,MAAM,gBAAgB,GAAG,IAAI,6BAAiB,CAC1C,cAAc,EACd,IAAI,EACJ,SAAS,EACT,KAAK,EACL,SAAS,EACT,SAAS;AACT,uEAAuE;AACvE,EAAE,CACL,CAAC;AAEF,MAAe,kBAAkB;IAC7B,uEAAuE;IASvE,OAAO;QACH,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACrC,CAAC;CACJ;AAHG;IARC,IAAA,uBAAS,EAAC;QACP,IAAI,EAAE,SAAS;QACf,WAAW,EAAE,sCAAsC;QACnD,YAAY,EAAE,IAAI;QAClB,eAAe,EAAE,IAAI;QACrB,cAAc,EAAE,IAAI;QACpB,aAAa,EAAE,KAAK;KACvB,CAAC;;;;iDAGD;AAGL,MAAe,iBAAiB;IAC5B,6DAA6D;IAQ7D,IAAI;QACA,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACrC,CAAC;IAED,6DAA6D;IAQ7D,KAAK,CAAC,MAAc,EAAE,OAAe;QACjC,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACrC,CAAC;CACJ;AAfG;IAPC,IAAA,uBAAS,EAAC;QACP,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,sCAAsC;QACnD,YAAY,EAAE,IAAI;QAClB,cAAc,EAAE,IAAI;QACpB,aAAa,EAAE,KAAK;KACvB,CAAC;;;;6CAGD;AAUD;IAPC,IAAA,uBAAS,EAAC;QACP,IAAI,EAAE,aAAa;QACnB,WAAW,EAAE,sCAAsC;QACnD,YAAY,EAAE,IAAI;QAClB,cAAc,EAAE,IAAI;QACpB,aAAa,EAAE,KAAK;KACvB,CAAC;;;;8CAGD;AAGL,KAAK,kBAAkB,CAAC;AACxB,KAAK,iBAAiB,CAAC;AACvB,KAAK,gBAAgB,CAAC","sourcesContent":["import { WpMcpTool } from './McpMetadata';\nimport { WpDtoFieldOptions } from './DtoSchema';\n\nconst invalidEmptyEnum = new WpDtoFieldOptions(\n 'invalid enum',\n true,\n undefined,\n false,\n undefined,\n undefined,\n // @ts-expect-error enumValues must contain at least one allowed string\n [],\n);\n\nabstract class InvalidMcpContract {\n // @ts-expect-error read-only tools cannot also claim to be destructive\n @WpMcpTool({\n name: 'invalid',\n description: 'This declaration must never compile.',\n readOnlyHint: true,\n destructiveHint: true,\n idempotentHint: true,\n openWorldHint: false,\n })\n invalid(): Promise<void> {\n throw new Error('contract only');\n }\n}\n\nabstract class InvalidMcpArities {\n // @ts-expect-error MCP tools require exactly one request DTO\n @WpMcpTool({\n name: 'zero-arity',\n description: 'This declaration must never compile.',\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n })\n zero(): Promise<object> {\n throw new Error('contract only');\n }\n\n // @ts-expect-error MCP tools require exactly one request DTO\n @WpMcpTool({\n name: 'multi-arity',\n description: 'This declaration must never compile.',\n readOnlyHint: true,\n idempotentHint: true,\n openWorldHint: false,\n })\n multi(_first: object, _second: object): Promise<object> {\n throw new Error('contract only');\n }\n}\n\nvoid InvalidMcpContract;\nvoid InvalidMcpArities;\nvoid invalidEmptyEnum;\n"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
/** The immutable hints MCP clients use when deciding whether and how to call a tool. */
|
|
3
|
+
export declare class WpMcpToolHints {
|
|
4
|
+
readonly readOnlyHint: boolean;
|
|
5
|
+
readonly destructiveHint: boolean;
|
|
6
|
+
readonly idempotentHint: boolean;
|
|
7
|
+
readonly openWorldHint: boolean;
|
|
8
|
+
constructor(readOnlyHint: boolean, destructiveHint: boolean, idempotentHint: boolean, openWorldHint: boolean);
|
|
9
|
+
}
|
|
10
|
+
type CommonToolOptions = {
|
|
11
|
+
/** Stable protocol name. Renaming this breaks saved agent workflows. */
|
|
12
|
+
name: string;
|
|
13
|
+
/** Tool-level documentation published verbatim by tools/list. */
|
|
14
|
+
description: string;
|
|
15
|
+
idempotentHint: boolean;
|
|
16
|
+
openWorldHint: boolean;
|
|
17
|
+
};
|
|
18
|
+
type ReadOnlyToolOptions = CommonToolOptions & {
|
|
19
|
+
readOnlyHint: true;
|
|
20
|
+
destructiveHint?: never;
|
|
21
|
+
};
|
|
22
|
+
type MutatingToolOptions = CommonToolOptions & {
|
|
23
|
+
readOnlyHint: false;
|
|
24
|
+
destructiveHint: boolean;
|
|
25
|
+
};
|
|
26
|
+
/** Compiler-enforced exposure: a read-only tool cannot also claim to be destructive. */
|
|
27
|
+
export type WpMcpToolOptions = ReadOnlyToolOptions | MutatingToolOptions;
|
|
28
|
+
type AsyncObjectMethod = (...args: never[]) => Promise<object>;
|
|
29
|
+
type ExactOneParameter<TMethod extends AsyncObjectMethod> = Parameters<TMethod>['length'] extends 1 ? object : never;
|
|
30
|
+
type ExactOneMethodDescriptor<TMethod extends AsyncObjectMethod> = TypedPropertyDescriptor<TMethod> & ExactOneParameter<TMethod>;
|
|
31
|
+
/** Method decorator restricted to one request DTO and one async response DTO. */
|
|
32
|
+
export type WpMcpMethodDecorator = <TMethod extends AsyncObjectMethod>(target: object, propertyKey: string | symbol, descriptor: ExactOneMethodDescriptor<TMethod>) => void;
|
|
33
|
+
/** Runtime metadata for one explicitly exposed endpoint. */
|
|
34
|
+
export declare class WpMcpToolMetadata {
|
|
35
|
+
readonly methodName: string;
|
|
36
|
+
readonly name: string;
|
|
37
|
+
readonly description: string;
|
|
38
|
+
readonly hints: WpMcpToolHints;
|
|
39
|
+
constructor(methodName: string, name: string, description: string, hints: WpMcpToolHints);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Explicitly publishes an existing `@Endpoint(..., 'rpc')` method as an MCP tool.
|
|
43
|
+
*
|
|
44
|
+
* This annotation owns tool-level documentation only. Input/output field documentation and JSON
|
|
45
|
+
* Schema come from the request/response DTO metadata. The endpoint's `@WpAuth*` annotation remains
|
|
46
|
+
* the sole runtime authorization policy.
|
|
47
|
+
*/
|
|
48
|
+
export declare function WpMcpTool(options: WpMcpToolOptions): WpMcpMethodDecorator;
|
|
49
|
+
/** Returns only methods explicitly opted in through {@link WpMcpTool}. */
|
|
50
|
+
export declare function getWpMcpTools(apiClass: Function): WpMcpToolMetadata[];
|
|
51
|
+
export {};
|