@webpieces/http-api 0.2.93 → 0.2.94

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/http-api",
3
- "version": "0.2.93",
3
+ "version": "0.2.94",
4
4
  "description": "HTTP API decorators for defining REST APIs",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -22,6 +22,6 @@
22
22
  "access": "public"
23
23
  },
24
24
  "dependencies": {
25
- "@webpieces/core-util": "0.2.93"
25
+ "@webpieces/core-util": "0.2.94"
26
26
  }
27
27
  }
@@ -43,7 +43,7 @@ export declare class AuthMeta {
43
43
  * ```typescript
44
44
  * @Authentication({authenticated: true})
45
45
  * @ApiPath('/api/save')
46
- * abstract class SaveApiPrototype {
46
+ * abstract class SaveApi {
47
47
  * @Endpoint('/item')
48
48
  * save(request: SaveRequest): Promise<SaveResponse> { ... }
49
49
  * }
package/src/decorators.js CHANGED
@@ -58,7 +58,7 @@ exports.AuthMeta = AuthMeta;
58
58
  * ```typescript
59
59
  * @Authentication({authenticated: true})
60
60
  * @ApiPath('/api/save')
61
- * abstract class SaveApiPrototype {
61
+ * abstract class SaveApi {
62
62
  * @Endpoint('/item')
63
63
  * save(request: SaveRequest): Promise<SaveResponse> { ... }
64
64
  * }
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../../../../../packages/http/http-api/src/decorators.ts"],"names":[],"mappings":"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","sourcesContent":["import 'reflect-metadata';\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};\n\n/**\n * Route metadata stored per-method at runtime.\n * Used internally by http-routing and http-client as the runtime representation\n * of a route. Constructed from @ApiPath + @Endpoint metadata by createApiClient\n * and ApiRoutingFactory.\n */\nexport class RouteMetadata {\n httpMethod: string;\n path: string;\n methodName: string;\n controllerClassName?: string;\n authMeta?: AuthMeta;\n\n constructor(\n httpMethod: string,\n path: string,\n methodName: string,\n controllerClassName?: string,\n authMeta?: AuthMeta,\n ) {\n this.httpMethod = httpMethod;\n this.path = path;\n this.methodName = methodName;\n this.controllerClassName = controllerClassName;\n this.authMeta = authMeta;\n }\n}\n\n/**\n * Auth metadata attached to a class or method via @Authentication().\n *\n * - authenticated=false → public endpoint (no auth check)\n * - authenticated=true, no roles → requires authentication\n * - authenticated=true, roles=['admin'] → requires authentication + specific roles\n * - authenticated=false + roles → INVALID (caught at decorator time)\n */\nexport class AuthMeta {\n authenticated: boolean;\n roles: string[];\n\n constructor(authenticated: boolean, roles?: string[]) {\n this.authenticated = authenticated;\n this.roles = roles ?? [];\n }\n}\n\n/**\n * @ApiPath(basePath) - Class decorator that marks a class as an API definition\n * and sets the base path for all endpoints.\n *\n * Usage:\n * ```typescript\n * @Authentication({authenticated: true})\n * @ApiPath('/api/save')\n * abstract class SaveApiPrototype {\n * @Endpoint('/item')\n * save(request: SaveRequest): Promise<SaveResponse> { ... }\n * }\n * ```\n */\nexport function ApiPath(basePath: string): ClassDecorator {\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any) => {\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 * @Endpoint(path) - Method decorator that registers a POST endpoint at the given path.\n *\n * All endpoints are POST-only (matching gRPC/thrift style).\n *\n * Usage:\n * ```typescript\n * @Endpoint('/item')\n * save(request: SaveRequest): Promise<SaveResponse> { ... }\n * ```\n */\nexport function Endpoint(path: string): MethodDecorator {\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\n const endpoints: Record<string, string> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINTS, metadataTarget) || {};\n\n endpoints[propertyKey as string] = path;\n\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINTS, endpoints, metadataTarget);\n };\n}\n\n/**\n * Authentication config passed to @Authentication() decorator.\n */\nexport class AuthenticationConfig {\n authenticated: boolean;\n roles?: string[];\n\n constructor(authenticated: boolean, roles?: string[]) {\n this.authenticated = authenticated;\n this.roles = roles;\n }\n}\n\n/**\n * @Authentication(config) - Class or method decorator for auth requirements.\n *\n * Single decorator replaces @Public/@Authenticated/@Roles:\n * - @Authentication({authenticated: false}) → public, no auth check\n * - @Authentication({authenticated: true}) → requires authentication\n * - @Authentication({authenticated: true, roles: ['admin']}) → requires auth + roles\n *\n * Class-level is required. Methods can override class-level.\n * Throws if authenticated=false but roles are specified (contradictory).\n */\nexport function Authentication(config: AuthenticationConfig): ClassDecorator & MethodDecorator {\n // Validate: can't be public with roles\n if (!config.authenticated && config.roles && config.roles.length > 0) {\n throw new Error(\n `Invalid @Authentication config: authenticated=false but roles=${JSON.stringify(config.roles)}. ` +\n `Cannot require roles on a public endpoint. Set authenticated=true or remove roles.`\n );\n }\n\n const authMeta = new AuthMeta(config.authenticated, config.roles);\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 // Method decorator\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n validateNoConflictingDecorators(metadataTarget, propertyKey as string);\n Reflect.defineMetadata(METADATA_KEYS.AUTH_META, authMeta, metadataTarget, propertyKey);\n } else {\n // Class decorator\n validateNoConflictingDecorators(target, undefined);\n Reflect.defineMetadata(METADATA_KEYS.AUTH_META, authMeta, target);\n }\n };\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 * 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 auth metadata for a specific method, falling back to class-level auth.\n * Method-level auth takes precedence over class-level auth.\n */\nexport function getAuthMeta(apiClass: Function, methodName?: string): AuthMeta | undefined {\n // Check method-level first\n if (methodName) {\n const methodAuth = Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass, methodName);\n if (methodAuth) {\n return methodAuth;\n }\n }\n\n // Fall back to class-level\n return Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass);\n}\n\n/**\n * Validate that a class/method doesn't have conflicting auth decorators.\n * @throws Error if multiple @Authentication decorators are found on the same target.\n */\nexport function validateNoConflictingDecorators(apiClass: Function, methodName: string | undefined): 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 ? `method '${methodName}' of ${targetName}` : `class ${targetName}`;\n throw new Error(\n `Conflicting @Authentication on ${location}. ` +\n `Only one @Authentication() decorator allowed per target.`\n );\n }\n}\n"]}
1
+ {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../../../../../packages/http/http-api/src/decorators.ts"],"names":[],"mappings":";;;AAwEA,0BAUC;AAaD,4BAYC;AA0BD,wCAwBC;AASD,gCAEC;AAMD,oCAEC;AAKD,8BAEC;AAMD,kCAWC;AAMD,0EAaC;AA3ND,4BAA0B;AAE1B;;;GAGG;AACU,QAAA,aAAa,GAAG;IACzB,QAAQ,EAAE,oBAAoB;IAC9B,SAAS,EAAE,qBAAqB;IAChC,SAAS,EAAE,qBAAqB;CACnC,CAAC;AAEF;;;;;GAKG;AACH,MAAa,aAAa;IAOtB,YACI,UAAkB,EAClB,IAAY,EACZ,UAAkB,EAClB,mBAA4B,EAC5B,QAAmB;QAEnB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,mBAAmB,GAAG,mBAAmB,CAAC;QAC/C,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7B,CAAC;CACJ;AApBD,sCAoBC;AAED;;;;;;;GAOG;AACH,MAAa,QAAQ;IAIjB,YAAY,aAAsB,EAAE,KAAgB;QAChD,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,KAAK,GAAG,KAAK,IAAI,EAAE,CAAC;IAC7B,CAAC;CACJ;AARD,4BAQC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAgB,OAAO,CAAC,QAAgB;IACpC,kFAAkF;IAClF,OAAO,CAAC,MAAW,EAAE,EAAE;QACnB,OAAO,CAAC,cAAc,CAAC,qBAAa,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAEjE,yCAAyC;QACzC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,qBAAa,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,CAAC;YACxD,OAAO,CAAC,cAAc,CAAC,qBAAa,CAAC,SAAS,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;QAChE,CAAC;IACL,CAAC,CAAC;AACN,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,QAAQ,CAAC,IAAY;IACjC,kFAAkF;IAClF,OAAO,CAAC,MAAW,EAAE,WAA4B,EAAE,WAA+B,EAAE,EAAE;QAClF,MAAM,cAAc,GAAG,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC;QAElF,MAAM,SAAS,GACX,OAAO,CAAC,WAAW,CAAC,qBAAa,CAAC,SAAS,EAAE,cAAc,CAAC,IAAI,EAAE,CAAC;QAEvE,SAAS,CAAC,WAAqB,CAAC,GAAG,IAAI,CAAC;QAExC,OAAO,CAAC,cAAc,CAAC,qBAAa,CAAC,SAAS,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;IAC/E,CAAC,CAAC;AACN,CAAC;AAED;;GAEG;AACH,MAAa,oBAAoB;IAI7B,YAAY,aAAsB,EAAE,KAAgB;QAChD,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,CAAC;CACJ;AARD,oDAQC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,cAAc,CAAC,MAA4B;IACvD,uCAAuC;IACvC,IAAI,CAAC,MAAM,CAAC,aAAa,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnE,MAAM,IAAI,KAAK,CACX,iEAAiE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI;YACjG,oFAAoF,CACvF,CAAC;IACN,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAElE,kFAAkF;IAClF,OAAO,CAAC,MAAW,EAAE,WAA6B,EAAE,WAAgC,EAAE,EAAE;QACpF,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC5B,mBAAmB;YACnB,MAAM,cAAc,GAAG,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC;YAClF,+BAA+B,CAAC,cAAc,EAAE,WAAqB,CAAC,CAAC;YACvE,OAAO,CAAC,cAAc,CAAC,qBAAa,CAAC,SAAS,EAAE,QAAQ,EAAE,cAAc,EAAE,WAAW,CAAC,CAAC;QAC3F,CAAC;aAAM,CAAC;YACJ,kBAAkB;YAClB,+BAA+B,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YACnD,OAAO,CAAC,cAAc,CAAC,qBAAa,CAAC,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QACtE,CAAC;IACL,CAAC,CAAC;AACN,CAAC;AAED,+DAA+D;AAC/D,mBAAmB;AACnB,+DAA+D;AAE/D;;GAEG;AACH,SAAgB,UAAU,CAAC,QAAkB;IACzC,OAAO,OAAO,CAAC,WAAW,CAAC,qBAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACjE,CAAC;AAED;;;GAGG;AACH,SAAgB,YAAY,CAAC,QAAkB;IAC3C,OAAO,OAAO,CAAC,WAAW,CAAC,qBAAa,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAClE,CAAC;AAED;;GAEG;AACH,SAAgB,SAAS,CAAC,QAAkB;IACxC,OAAO,OAAO,CAAC,WAAW,CAAC,qBAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACjE,CAAC;AAED;;;GAGG;AACH,SAAgB,WAAW,CAAC,QAAkB,EAAE,UAAmB;IAC/D,2BAA2B;IAC3B,IAAI,UAAU,EAAE,CAAC;QACb,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,qBAAa,CAAC,SAAS,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;QACtF,IAAI,UAAU,EAAE,CAAC;YACb,OAAO,UAAU,CAAC;QACtB,CAAC;IACL,CAAC;IAED,2BAA2B;IAC3B,OAAO,OAAO,CAAC,WAAW,CAAC,qBAAa,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAClE,CAAC;AAED;;;GAGG;AACH,SAAgB,+BAA+B,CAAC,QAAkB,EAAE,UAA8B;IAC9F,MAAM,QAAQ,GAAG,UAAU;QACvB,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,qBAAa,CAAC,SAAS,EAAE,QAAQ,EAAE,UAAU,CAAC;QACpE,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,qBAAa,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAE7D,IAAI,QAAQ,EAAE,CAAC;QACX,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,IAAI,SAAS,CAAC;QAC9C,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,WAAW,UAAU,QAAQ,UAAU,EAAE,CAAC,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC;QAChG,MAAM,IAAI,KAAK,CACX,kCAAkC,QAAQ,IAAI;YAC9C,0DAA0D,CAC7D,CAAC;IACN,CAAC;AACL,CAAC","sourcesContent":["import 'reflect-metadata';\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};\n\n/**\n * Route metadata stored per-method at runtime.\n * Used internally by http-routing and http-client as the runtime representation\n * of a route. Constructed from @ApiPath + @Endpoint metadata by createApiClient\n * and ApiRoutingFactory.\n */\nexport class RouteMetadata {\n httpMethod: string;\n path: string;\n methodName: string;\n controllerClassName?: string;\n authMeta?: AuthMeta;\n\n constructor(\n httpMethod: string,\n path: string,\n methodName: string,\n controllerClassName?: string,\n authMeta?: AuthMeta,\n ) {\n this.httpMethod = httpMethod;\n this.path = path;\n this.methodName = methodName;\n this.controllerClassName = controllerClassName;\n this.authMeta = authMeta;\n }\n}\n\n/**\n * Auth metadata attached to a class or method via @Authentication().\n *\n * - authenticated=false → public endpoint (no auth check)\n * - authenticated=true, no roles → requires authentication\n * - authenticated=true, roles=['admin'] → requires authentication + specific roles\n * - authenticated=false + roles → INVALID (caught at decorator time)\n */\nexport class AuthMeta {\n authenticated: boolean;\n roles: string[];\n\n constructor(authenticated: boolean, roles?: string[]) {\n this.authenticated = authenticated;\n this.roles = roles ?? [];\n }\n}\n\n/**\n * @ApiPath(basePath) - Class decorator that marks a class as an API definition\n * and sets the base path for all endpoints.\n *\n * Usage:\n * ```typescript\n * @Authentication({authenticated: true})\n * @ApiPath('/api/save')\n * abstract class SaveApi {\n * @Endpoint('/item')\n * save(request: SaveRequest): Promise<SaveResponse> { ... }\n * }\n * ```\n */\nexport function ApiPath(basePath: string): ClassDecorator {\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any) => {\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 * @Endpoint(path) - Method decorator that registers a POST endpoint at the given path.\n *\n * All endpoints are POST-only (matching gRPC/thrift style).\n *\n * Usage:\n * ```typescript\n * @Endpoint('/item')\n * save(request: SaveRequest): Promise<SaveResponse> { ... }\n * ```\n */\nexport function Endpoint(path: string): MethodDecorator {\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\n const endpoints: Record<string, string> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINTS, metadataTarget) || {};\n\n endpoints[propertyKey as string] = path;\n\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINTS, endpoints, metadataTarget);\n };\n}\n\n/**\n * Authentication config passed to @Authentication() decorator.\n */\nexport class AuthenticationConfig {\n authenticated: boolean;\n roles?: string[];\n\n constructor(authenticated: boolean, roles?: string[]) {\n this.authenticated = authenticated;\n this.roles = roles;\n }\n}\n\n/**\n * @Authentication(config) - Class or method decorator for auth requirements.\n *\n * Single decorator replaces @Public/@Authenticated/@Roles:\n * - @Authentication({authenticated: false}) → public, no auth check\n * - @Authentication({authenticated: true}) → requires authentication\n * - @Authentication({authenticated: true, roles: ['admin']}) → requires auth + roles\n *\n * Class-level is required. Methods can override class-level.\n * Throws if authenticated=false but roles are specified (contradictory).\n */\nexport function Authentication(config: AuthenticationConfig): ClassDecorator & MethodDecorator {\n // Validate: can't be public with roles\n if (!config.authenticated && config.roles && config.roles.length > 0) {\n throw new Error(\n `Invalid @Authentication config: authenticated=false but roles=${JSON.stringify(config.roles)}. ` +\n `Cannot require roles on a public endpoint. Set authenticated=true or remove roles.`\n );\n }\n\n const authMeta = new AuthMeta(config.authenticated, config.roles);\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 // Method decorator\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n validateNoConflictingDecorators(metadataTarget, propertyKey as string);\n Reflect.defineMetadata(METADATA_KEYS.AUTH_META, authMeta, metadataTarget, propertyKey);\n } else {\n // Class decorator\n validateNoConflictingDecorators(target, undefined);\n Reflect.defineMetadata(METADATA_KEYS.AUTH_META, authMeta, target);\n }\n };\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 * 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 auth metadata for a specific method, falling back to class-level auth.\n * Method-level auth takes precedence over class-level auth.\n */\nexport function getAuthMeta(apiClass: Function, methodName?: string): AuthMeta | undefined {\n // Check method-level first\n if (methodName) {\n const methodAuth = Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass, methodName);\n if (methodAuth) {\n return methodAuth;\n }\n }\n\n // Fall back to class-level\n return Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass);\n}\n\n/**\n * Validate that a class/method doesn't have conflicting auth decorators.\n * @throws Error if multiple @Authentication decorators are found on the same target.\n */\nexport function validateNoConflictingDecorators(apiClass: Function, methodName: string | undefined): 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 ? `method '${methodName}' of ${targetName}` : `class ${targetName}`;\n throw new Error(\n `Conflicting @Authentication on ${location}. ` +\n `Only one @Authentication() decorator allowed per target.`\n );\n }\n}\n"]}
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Usage in server-side controllers:
8
8
  * ```typescript
9
- * export class SaveController extends SaveApiPrototype implements SaveApi {
9
+ * export class SaveController extends SaveApi implements SaveApi {
10
10
  * // Compile-time check: ensures all SaveApi methods are implemented
11
11
  * private readonly __validator!: ValidateImplementation<SaveController, SaveApi>;
12
12
  *
@@ -1 +1 @@
1
- {"version":3,"file":"validators.js","sourceRoot":"","sources":["../../../../../packages/http/http-api/src/validators.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Type-level validator to ensure a class implements all methods from an interface.\n *\n * This validator provides compile-time verification that an implementation\n * (e.g., controller, client, mock) fully implements an API interface.\n *\n * Usage in server-side controllers:\n * ```typescript\n * export class SaveController extends SaveApiPrototype implements SaveApi {\n * // Compile-time check: ensures all SaveApi methods are implemented\n * private readonly __validator!: ValidateImplementation<SaveController, SaveApi>;\n *\n * save(request: SaveRequest): Promise<SaveResponse> {\n * // implementation\n * }\n * }\n * ```\n *\n * Usage in client-side implementations:\n * ```typescript\n * export class MockSaveClient implements SaveApi {\n * private readonly __validator!: ValidateImplementation<MockSaveClient, SaveApi>;\n *\n * save(request: SaveRequest): Promise<SaveResponse> {\n * // mock implementation\n * }\n * }\n * ```\n *\n * Benefits:\n * - Compile error if any interface method is missing\n * - Compile error if method signatures don't match\n * - Works with controllers, clients, mocks, stubs, etc.\n * - Type-safe contract enforcement\n *\n * Note: The `!` assertion is safe because this field is never accessed at runtime.\n * It only exists for compile-time type checking.\n */\nexport type ValidateImplementation<TImpl, TInterface> = {\n [K in keyof TInterface]: K extends keyof TImpl\n ? TImpl[K] extends TInterface[K]\n ? TInterface[K]\n : never\n : never;\n};\n"]}
1
+ {"version":3,"file":"validators.js","sourceRoot":"","sources":["../../../../../packages/http/http-api/src/validators.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Type-level validator to ensure a class implements all methods from an interface.\n *\n * This validator provides compile-time verification that an implementation\n * (e.g., controller, client, mock) fully implements an API interface.\n *\n * Usage in server-side controllers:\n * ```typescript\n * export class SaveController extends SaveApi implements SaveApi {\n * // Compile-time check: ensures all SaveApi methods are implemented\n * private readonly __validator!: ValidateImplementation<SaveController, SaveApi>;\n *\n * save(request: SaveRequest): Promise<SaveResponse> {\n * // implementation\n * }\n * }\n * ```\n *\n * Usage in client-side implementations:\n * ```typescript\n * export class MockSaveClient implements SaveApi {\n * private readonly __validator!: ValidateImplementation<MockSaveClient, SaveApi>;\n *\n * save(request: SaveRequest): Promise<SaveResponse> {\n * // mock implementation\n * }\n * }\n * ```\n *\n * Benefits:\n * - Compile error if any interface method is missing\n * - Compile error if method signatures don't match\n * - Works with controllers, clients, mocks, stubs, etc.\n * - Type-safe contract enforcement\n *\n * Note: The `!` assertion is safe because this field is never accessed at runtime.\n * It only exists for compile-time type checking.\n */\nexport type ValidateImplementation<TImpl, TInterface> = {\n [K in keyof TInterface]: K extends keyof TImpl\n ? TImpl[K] extends TInterface[K]\n ? TInterface[K]\n : never\n : never;\n};\n"]}