@webpieces/http-client 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 +2 -2
- package/src/ClientFactory.d.ts +1 -1
- package/src/ClientFactory.js +1 -1
- package/src/ClientFactory.js.map +1 -1
- package/src/ContextMgr.d.ts +1 -1
- package/src/ContextMgr.js +1 -1
- package/src/ContextMgr.js.map +1 -1
- package/src/index.d.ts +2 -2
- package/src/index.js +2 -2
- package/src/index.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/http-client",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.94",
|
|
4
4
|
"description": "HTTP client for WebPieces framework",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -21,6 +21,6 @@
|
|
|
21
21
|
"access": "public"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@webpieces/http-api": "0.2.
|
|
24
|
+
"@webpieces/http-api": "0.2.94"
|
|
25
25
|
}
|
|
26
26
|
}
|
package/src/ClientFactory.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export declare class ClientConfig {
|
|
|
30
30
|
* Usage:
|
|
31
31
|
* ```typescript
|
|
32
32
|
* const config = new ClientConfig('http://localhost:3000');
|
|
33
|
-
* const client = createApiClient(
|
|
33
|
+
* const client = createApiClient(SaveApi, config);
|
|
34
34
|
* const response = await client.save({ query: 'test' }); // Type-safe!
|
|
35
35
|
* ```
|
|
36
36
|
*
|
package/src/ClientFactory.js
CHANGED
|
@@ -55,7 +55,7 @@ const FRAMEWORK_INSPECTION_PROPERTIES = new Set([
|
|
|
55
55
|
* Usage:
|
|
56
56
|
* ```typescript
|
|
57
57
|
* const config = new ClientConfig('http://localhost:3000');
|
|
58
|
-
* const client = createApiClient(
|
|
58
|
+
* const client = createApiClient(SaveApi, config);
|
|
59
59
|
* const response = await client.save({ query: 'test' }); // Type-safe!
|
|
60
60
|
* ```
|
|
61
61
|
*
|
package/src/ClientFactory.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClientFactory.js","sourceRoot":"","sources":["../../../../../packages/http/http-client/src/ClientFactory.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n isApiPath,\n getApiPath,\n getEndpoints,\n RouteMetadata,\n ProtocolError,\n HeaderMethods,\n LogApiCall,\n} from '@webpieces/http-api';\nimport { ClientErrorTranslator } from './ClientErrorTranslator';\nimport { ContextMgr } from './ContextMgr';\n\n/**\n * Type representing a class constructor whose prototype is T.\n * Used as the apiPrototype parameter for createApiClient.\n */\ntype ApiPrototype<T> = Function & { prototype: T };\n\n/**\n * Configuration options for HTTP client.\n */\nexport class ClientConfig {\n /** Base URL for all requests (e.g., 'http://localhost:3000') */\n baseUrl: string;\n\n /**\n * Optional context manager for automatic header propagation.\n * When provided, headers will be read from the ContextReader and added to requests.\n */\n contextMgr?: ContextMgr;\n\n constructor(baseUrl: string, contextMgr?: ContextMgr) {\n this.baseUrl = baseUrl;\n this.contextMgr = contextMgr;\n }\n}\n\n/**\n * Return type for the Proxy get trap — either an async method or undefined for framework inspection.\n */\n// webpieces-disable no-any-unknown -- Proxy get trap returns generic response promises\ntype ProxyGetResult = (...args: never[]) => Promise<unknown>;\n\n/**\n * Properties accessed by DI frameworks (Angular, Vue), debuggers, Promise checks, and serializers.\n * These should return undefined instead of throwing, allowing frameworks to inspect the proxy.\n *\n * Why this exists:\n * - Angular's injector profiler accesses `constructor` after useFactory returns\n * - Promise.resolve() checks for `then` to detect thenables\n * - JSON.stringify checks for `toJSON`\n * - Debuggers access `prototype`, `__proto__`, etc.\n */\nconst FRAMEWORK_INSPECTION_PROPERTIES = new Set([\n 'constructor', // Angular DI profiler, class inspection\n 'prototype', // Prototype chain inspection\n '__proto__', // Legacy prototype access\n 'name', // Angular isNotFound() check, function/class name inspection\n 'then', // Promise/thenable detection\n 'catch', // Promise check\n 'finally', // Promise check\n 'toJSON', // JSON.stringify\n 'valueOf', // Type coercion\n 'toString', // String coercion\n 'nodeType', // DOM element check\n 'tagName', // DOM element check\n '$$typeof', // React element/component check\n '$typeof', // React internal\n '_isVue', // Vue internal\n 'ngOnInit', // Angular lifecycle hook check\n 'ngOnDestroy', // Angular lifecycle hook check\n 'ngOnChanges', // Angular lifecycle hook check\n 'asymmetricMatch', // Jest matcher protocol\n]);\n\n/**\n * Creates a type-safe HTTP client from an API prototype with @ApiPath/@Endpoint decorators.\n *\n * This is the client-side equivalent of ApiRoutingFactory.\n * - Server: ApiRoutingFactory reads decorators -> routes HTTP requests to controllers\n * - Client: createApiClient reads decorators -> generates HTTP requests from method calls\n *\n * Usage:\n * ```typescript\n * const config = new ClientConfig('http://localhost:3000');\n * const client = createApiClient(SaveApiPrototype, config);\n * const response = await client.save({ query: 'test' }); // Type-safe!\n * ```\n *\n * @param apiPrototype - The API prototype class with @ApiPath/@Endpoint decorators\n * @param config - Client configuration with baseUrl\n * @param contextMgr - Optional context manager for header propagation\n * @returns A proxy object that implements the API interface\n */\nexport function createApiClient<T extends object>(\n apiPrototype: ApiPrototype<T>,\n config: ClientConfig,\n contextMgr?: ContextMgr\n): T {\n // Validate that the API prototype is marked with @ApiPath\n if (!isApiPath(apiPrototype)) {\n const className = apiPrototype.name || 'Unknown';\n throw new Error(`Class ${className} must be decorated with @ApiPath()`);\n }\n\n const basePath = getApiPath(apiPrototype)!;\n const endpoints = getEndpoints(apiPrototype) || {};\n\n // Build RouteMetadata array from @ApiPath + @Endpoint metadata\n const routes: RouteMetadata[] = [];\n for (const [methodName, endpointPath] of Object.entries(endpoints)) {\n const fullPath = basePath + endpointPath;\n routes.push(new RouteMetadata('POST', fullPath, methodName));\n }\n\n // Create ProxyClient with injected LogApiCall\n // Our own little DI going on here as angular and nodejs are using 2 different DI systems\n const proxyClient = new ProxyClient(\n config, new LogApiCall(), new HeaderMethods(), routes, contextMgr);\n\n // Create a proxy that intercepts method calls and makes HTTP requests\n return new Proxy({} as T, {\n get(target, prop: string | symbol): ProxyGetResult | undefined {\n // Symbols (Symbol.toStringTag, Symbol.iterator, etc.) - throw for now to learn if this happens\n if (typeof prop !== 'string') {\n throw new Error(\n `Proxy accessed with non-string property: ${String(prop)} (type: ${typeof prop}). ` +\n `Please report this so we can add it to the whitelist.`\n );\n }\n\n // Framework inspection properties - return undefined to allow inspection\n // WITHOUT throwing. This is critical for Angular DI, Promise checks, etc.\n if (FRAMEWORK_INSPECTION_PROPERTIES.has(prop)) {\n return undefined;\n }\n\n // Check if this property is actually a route method\n if (!proxyClient.hasRoute(prop)) {\n // For unknown properties (likely typos), throw a helpful error\n throw new Error(\n `No route found for method '${prop}'. ` +\n `Check for typos or ensure the method has @Endpoint() decorator.`\n );\n }\n\n const route = proxyClient.getRoute(prop);\n\n // Return a function that makes the HTTP request\n return async (...args: any[]) => {\n return proxyClient.makeRequest(route, args);\n };\n },\n });\n}\n\n/**\n * ProxyClient - HTTP client implementation with logging.\n *\n * This class handles:\n * - Making HTTP requests based on route metadata\n * - Header propagation via ContextMgr\n * - Logging via LogApiCall\n * - Error translation via ClientErrorTranslator\n *\n * LogApiCall is injected for consistent logging across the framework.\n */\nexport class ProxyClient {\n private routeMap: Map<string, RouteMetadata>;\n\n constructor(\n private config: ClientConfig,\n private logApiCall: LogApiCall,\n private headerMethods: HeaderMethods,\n routes: RouteMetadata[],\n private contextMgr?: ContextMgr,\n ) {\n // Create a map of method name -> route metadata for fast lookup\n this.routeMap = new Map<string, RouteMetadata>();\n for (const route of routes) {\n this.routeMap.set(route.methodName, route);\n }\n }\n\n /**\n * Check if a route exists for the given method name.\n */\n hasRoute(methodName: string): boolean {\n return this.routeMap.has(methodName);\n }\n\n /**\n * Get route metadata for a method name.\n * @throws Error if no route found\n */\n getRoute(methodName: string): RouteMetadata {\n const route = this.routeMap.get(methodName);\n if (!route) {\n throw new Error(`No route found for method ${methodName}`);\n }\n return route;\n }\n\n /**\n * Make an HTTP request based on route metadata and arguments.\n *\n * All endpoints are POST-only. The request body is the first argument.\n */\n async makeRequest(route: RouteMetadata, args: any[]): Promise<any> {\n const { httpMethod, path } = route;\n\n // Build the full URL\n const url = `${this.config.baseUrl}${path}`;\n\n // Build base headers for the HTTP request\n const httpHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n // Add context headers to httpHeaders (unmasked, for actual HTTP request)\n if (this.contextMgr) {\n for (const header of this.contextMgr.headerSet) {\n const value = this.contextMgr.contextReader.read(header);\n if (value) {\n httpHeaders[header.headerName] = value;\n }\n }\n }\n\n // Build masked headers map for logging\n const headersForLogging = this.contextMgr\n ? this.headerMethods.buildSecureMapForLogs(this.contextMgr.headerSet, this.contextMgr.contextReader)\n : new Map<string, any>();\n\n // Build request options\n const options: RequestInit = {\n method: httpMethod,\n headers: httpHeaders,\n };\n\n // POST body is the first argument as JSON\n let requestDto: unknown;\n if (args.length > 0) {\n requestDto = args[0];\n options.body = JSON.stringify(requestDto);\n }\n\n // Wrap fetch in a method for LogApiCall.execute\n const method = async (): Promise<unknown> => {\n return this.executeFetch(url, options);\n };\n\n return await this.logApiCall.execute(\"CLIENT\", route, requestDto, headersForLogging, method);\n }\n\n /**\n * Execute the fetch request and handle response.\n */\n private async executeFetch(url: string, options: RequestInit): Promise<unknown> {\n const response = await fetch(url, options);\n\n if (response.ok) {\n return await response.json();\n }\n\n // Handle errors (non-2xx responses)\n // Try to parse ProtocolError from response body\n const protocolError = (await response.json()) as ProtocolError;\n\n // Reconstruct appropriate HttpError subclass and throw\n throw ClientErrorTranslator.translateError(response, protocolError);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ClientFactory.js","sourceRoot":"","sources":["../../../../../packages/http/http-client/src/ClientFactory.ts"],"names":[],"mappings":";;;AA8FA,0CA4DC;AA1JD,kDAQ6B;AAC7B,mEAAgE;AAShE;;GAEG;AACH,MAAa,YAAY;IAUrB,YAAY,OAAe,EAAE,UAAuB;QAChD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IACjC,CAAC;CACJ;AAdD,oCAcC;AAQD;;;;;;;;;GASG;AACH,MAAM,+BAA+B,GAAG,IAAI,GAAG,CAAC;IAC5C,aAAa,EAAM,wCAAwC;IAC3D,WAAW,EAAQ,6BAA6B;IAChD,WAAW,EAAQ,0BAA0B;IAC7C,MAAM,EAAa,6DAA6D;IAChF,MAAM,EAAa,6BAA6B;IAChD,OAAO,EAAY,gBAAgB;IACnC,SAAS,EAAU,gBAAgB;IACnC,QAAQ,EAAW,iBAAiB;IACpC,SAAS,EAAU,gBAAgB;IACnC,UAAU,EAAS,kBAAkB;IACrC,UAAU,EAAS,oBAAoB;IACvC,SAAS,EAAU,oBAAoB;IACvC,UAAU,EAAS,gCAAgC;IACnD,SAAS,EAAU,iBAAiB;IACpC,QAAQ,EAAW,eAAe;IAClC,UAAU,EAAS,+BAA+B;IAClD,aAAa,EAAM,+BAA+B;IAClD,aAAa,EAAM,+BAA+B;IAClD,iBAAiB,EAAE,wBAAwB;CAC9C,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAgB,eAAe,CAC3B,YAA6B,EAC7B,MAAoB,EACpB,UAAuB;IAEvB,0DAA0D;IAC1D,IAAI,CAAC,IAAA,oBAAS,EAAC,YAAY,CAAC,EAAE,CAAC;QAC3B,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,IAAI,SAAS,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,SAAS,SAAS,oCAAoC,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,QAAQ,GAAG,IAAA,qBAAU,EAAC,YAAY,CAAE,CAAC;IAC3C,MAAM,SAAS,GAAG,IAAA,uBAAY,EAAC,YAAY,CAAC,IAAI,EAAE,CAAC;IAEnD,+DAA+D;IAC/D,MAAM,MAAM,GAAoB,EAAE,CAAC;IACnC,KAAK,MAAM,CAAC,UAAU,EAAE,YAAY,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACjE,MAAM,QAAQ,GAAG,QAAQ,GAAG,YAAY,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,IAAI,wBAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC;IACjE,CAAC;IAED,8CAA8C;IAC9C,yFAAyF;IACzF,MAAM,WAAW,GAAG,IAAI,WAAW,CAC/B,MAAM,EAAE,IAAI,qBAAU,EAAE,EAAE,IAAI,wBAAa,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;IAEvE,sEAAsE;IACtE,OAAO,IAAI,KAAK,CAAC,EAAO,EAAE;QACtB,GAAG,CAAC,MAAM,EAAE,IAAqB;YAC7B,+FAA+F;YAC/F,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC3B,MAAM,IAAI,KAAK,CACX,4CAA4C,MAAM,CAAC,IAAI,CAAC,WAAW,OAAO,IAAI,KAAK;oBACnF,uDAAuD,CAC1D,CAAC;YACN,CAAC;YAED,yEAAyE;YACzE,0EAA0E;YAC1E,IAAI,+BAA+B,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC5C,OAAO,SAAS,CAAC;YACrB,CAAC;YAED,oDAAoD;YACpD,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9B,+DAA+D;gBAC/D,MAAM,IAAI,KAAK,CACX,8BAA8B,IAAI,KAAK;oBACvC,iEAAiE,CACpE,CAAC;YACN,CAAC;YAED,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAEzC,gDAAgD;YAChD,OAAO,KAAK,EAAE,GAAG,IAAW,EAAE,EAAE;gBAC5B,OAAO,WAAW,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAChD,CAAC,CAAC;QACN,CAAC;KACJ,CAAC,CAAC;AACP,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAa,WAAW;IAGpB,YACY,MAAoB,EACpB,UAAsB,EACtB,aAA4B,EACpC,MAAuB,EACf,UAAuB;QAJvB,WAAM,GAAN,MAAM,CAAc;QACpB,eAAU,GAAV,UAAU,CAAY;QACtB,kBAAa,GAAb,aAAa,CAAe;QAE5B,eAAU,GAAV,UAAU,CAAa;QAE/B,gEAAgE;QAChE,IAAI,CAAC,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;QACjD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YACzB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAC/C,CAAC;IACL,CAAC;IAED;;OAEG;IACH,QAAQ,CAAC,UAAkB;QACvB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACzC,CAAC;IAED;;;OAGG;IACH,QAAQ,CAAC,UAAkB;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC5C,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,MAAM,IAAI,KAAK,CAAC,6BAA6B,UAAU,EAAE,CAAC,CAAC;QAC/D,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW,CAAC,KAAoB,EAAE,IAAW;QAC/C,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;QAEnC,qBAAqB;QACrB,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,IAAI,EAAE,CAAC;QAE5C,0CAA0C;QAC1C,MAAM,WAAW,GAA2B;YACxC,cAAc,EAAE,kBAAkB;SACrC,CAAC;QAEF,yEAAyE;QACzE,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;gBAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACzD,IAAI,KAAK,EAAE,CAAC;oBACR,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,KAAK,CAAC;gBAC3C,CAAC;YACL,CAAC;QACL,CAAC;QAED,uCAAuC;QACvC,MAAM,iBAAiB,GAAG,IAAI,CAAC,UAAU;YACrC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,qBAAqB,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;YACpG,CAAC,CAAC,IAAI,GAAG,EAAe,CAAC;QAE7B,wBAAwB;QACxB,MAAM,OAAO,GAAgB;YACzB,MAAM,EAAE,UAAU;YAClB,OAAO,EAAE,WAAW;SACvB,CAAC;QAEF,0CAA0C;QAC1C,IAAI,UAAmB,CAAC;QACxB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClB,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACrB,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAC9C,CAAC;QAED,gDAAgD;QAChD,MAAM,MAAM,GAAG,KAAK,IAAsB,EAAE;YACxC,OAAO,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC3C,CAAC,CAAC;QAEF,OAAO,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,CAAC,CAAC;IACjG,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,YAAY,CAAC,GAAW,EAAE,OAAoB;QACxD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAE3C,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;YACd,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACjC,CAAC;QAED,oCAAoC;QACpC,gDAAgD;QAChD,MAAM,aAAa,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAkB,CAAC;QAE/D,uDAAuD;QACvD,MAAM,6CAAqB,CAAC,cAAc,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;IACxE,CAAC;CACJ;AAzGD,kCAyGC","sourcesContent":["import {\n isApiPath,\n getApiPath,\n getEndpoints,\n RouteMetadata,\n ProtocolError,\n HeaderMethods,\n LogApiCall,\n} from '@webpieces/http-api';\nimport { ClientErrorTranslator } from './ClientErrorTranslator';\nimport { ContextMgr } from './ContextMgr';\n\n/**\n * Type representing a class constructor whose prototype is T.\n * Used as the apiPrototype parameter for createApiClient.\n */\ntype ApiPrototype<T> = Function & { prototype: T };\n\n/**\n * Configuration options for HTTP client.\n */\nexport class ClientConfig {\n /** Base URL for all requests (e.g., 'http://localhost:3000') */\n baseUrl: string;\n\n /**\n * Optional context manager for automatic header propagation.\n * When provided, headers will be read from the ContextReader and added to requests.\n */\n contextMgr?: ContextMgr;\n\n constructor(baseUrl: string, contextMgr?: ContextMgr) {\n this.baseUrl = baseUrl;\n this.contextMgr = contextMgr;\n }\n}\n\n/**\n * Return type for the Proxy get trap — either an async method or undefined for framework inspection.\n */\n// webpieces-disable no-any-unknown -- Proxy get trap returns generic response promises\ntype ProxyGetResult = (...args: never[]) => Promise<unknown>;\n\n/**\n * Properties accessed by DI frameworks (Angular, Vue), debuggers, Promise checks, and serializers.\n * These should return undefined instead of throwing, allowing frameworks to inspect the proxy.\n *\n * Why this exists:\n * - Angular's injector profiler accesses `constructor` after useFactory returns\n * - Promise.resolve() checks for `then` to detect thenables\n * - JSON.stringify checks for `toJSON`\n * - Debuggers access `prototype`, `__proto__`, etc.\n */\nconst FRAMEWORK_INSPECTION_PROPERTIES = new Set([\n 'constructor', // Angular DI profiler, class inspection\n 'prototype', // Prototype chain inspection\n '__proto__', // Legacy prototype access\n 'name', // Angular isNotFound() check, function/class name inspection\n 'then', // Promise/thenable detection\n 'catch', // Promise check\n 'finally', // Promise check\n 'toJSON', // JSON.stringify\n 'valueOf', // Type coercion\n 'toString', // String coercion\n 'nodeType', // DOM element check\n 'tagName', // DOM element check\n '$$typeof', // React element/component check\n '$typeof', // React internal\n '_isVue', // Vue internal\n 'ngOnInit', // Angular lifecycle hook check\n 'ngOnDestroy', // Angular lifecycle hook check\n 'ngOnChanges', // Angular lifecycle hook check\n 'asymmetricMatch', // Jest matcher protocol\n]);\n\n/**\n * Creates a type-safe HTTP client from an API prototype with @ApiPath/@Endpoint decorators.\n *\n * This is the client-side equivalent of ApiRoutingFactory.\n * - Server: ApiRoutingFactory reads decorators -> routes HTTP requests to controllers\n * - Client: createApiClient reads decorators -> generates HTTP requests from method calls\n *\n * Usage:\n * ```typescript\n * const config = new ClientConfig('http://localhost:3000');\n * const client = createApiClient(SaveApi, config);\n * const response = await client.save({ query: 'test' }); // Type-safe!\n * ```\n *\n * @param apiPrototype - The API prototype class with @ApiPath/@Endpoint decorators\n * @param config - Client configuration with baseUrl\n * @param contextMgr - Optional context manager for header propagation\n * @returns A proxy object that implements the API interface\n */\nexport function createApiClient<T extends object>(\n apiPrototype: ApiPrototype<T>,\n config: ClientConfig,\n contextMgr?: ContextMgr\n): T {\n // Validate that the API prototype is marked with @ApiPath\n if (!isApiPath(apiPrototype)) {\n const className = apiPrototype.name || 'Unknown';\n throw new Error(`Class ${className} must be decorated with @ApiPath()`);\n }\n\n const basePath = getApiPath(apiPrototype)!;\n const endpoints = getEndpoints(apiPrototype) || {};\n\n // Build RouteMetadata array from @ApiPath + @Endpoint metadata\n const routes: RouteMetadata[] = [];\n for (const [methodName, endpointPath] of Object.entries(endpoints)) {\n const fullPath = basePath + endpointPath;\n routes.push(new RouteMetadata('POST', fullPath, methodName));\n }\n\n // Create ProxyClient with injected LogApiCall\n // Our own little DI going on here as angular and nodejs are using 2 different DI systems\n const proxyClient = new ProxyClient(\n config, new LogApiCall(), new HeaderMethods(), routes, contextMgr);\n\n // Create a proxy that intercepts method calls and makes HTTP requests\n return new Proxy({} as T, {\n get(target, prop: string | symbol): ProxyGetResult | undefined {\n // Symbols (Symbol.toStringTag, Symbol.iterator, etc.) - throw for now to learn if this happens\n if (typeof prop !== 'string') {\n throw new Error(\n `Proxy accessed with non-string property: ${String(prop)} (type: ${typeof prop}). ` +\n `Please report this so we can add it to the whitelist.`\n );\n }\n\n // Framework inspection properties - return undefined to allow inspection\n // WITHOUT throwing. This is critical for Angular DI, Promise checks, etc.\n if (FRAMEWORK_INSPECTION_PROPERTIES.has(prop)) {\n return undefined;\n }\n\n // Check if this property is actually a route method\n if (!proxyClient.hasRoute(prop)) {\n // For unknown properties (likely typos), throw a helpful error\n throw new Error(\n `No route found for method '${prop}'. ` +\n `Check for typos or ensure the method has @Endpoint() decorator.`\n );\n }\n\n const route = proxyClient.getRoute(prop);\n\n // Return a function that makes the HTTP request\n return async (...args: any[]) => {\n return proxyClient.makeRequest(route, args);\n };\n },\n });\n}\n\n/**\n * ProxyClient - HTTP client implementation with logging.\n *\n * This class handles:\n * - Making HTTP requests based on route metadata\n * - Header propagation via ContextMgr\n * - Logging via LogApiCall\n * - Error translation via ClientErrorTranslator\n *\n * LogApiCall is injected for consistent logging across the framework.\n */\nexport class ProxyClient {\n private routeMap: Map<string, RouteMetadata>;\n\n constructor(\n private config: ClientConfig,\n private logApiCall: LogApiCall,\n private headerMethods: HeaderMethods,\n routes: RouteMetadata[],\n private contextMgr?: ContextMgr,\n ) {\n // Create a map of method name -> route metadata for fast lookup\n this.routeMap = new Map<string, RouteMetadata>();\n for (const route of routes) {\n this.routeMap.set(route.methodName, route);\n }\n }\n\n /**\n * Check if a route exists for the given method name.\n */\n hasRoute(methodName: string): boolean {\n return this.routeMap.has(methodName);\n }\n\n /**\n * Get route metadata for a method name.\n * @throws Error if no route found\n */\n getRoute(methodName: string): RouteMetadata {\n const route = this.routeMap.get(methodName);\n if (!route) {\n throw new Error(`No route found for method ${methodName}`);\n }\n return route;\n }\n\n /**\n * Make an HTTP request based on route metadata and arguments.\n *\n * All endpoints are POST-only. The request body is the first argument.\n */\n async makeRequest(route: RouteMetadata, args: any[]): Promise<any> {\n const { httpMethod, path } = route;\n\n // Build the full URL\n const url = `${this.config.baseUrl}${path}`;\n\n // Build base headers for the HTTP request\n const httpHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n // Add context headers to httpHeaders (unmasked, for actual HTTP request)\n if (this.contextMgr) {\n for (const header of this.contextMgr.headerSet) {\n const value = this.contextMgr.contextReader.read(header);\n if (value) {\n httpHeaders[header.headerName] = value;\n }\n }\n }\n\n // Build masked headers map for logging\n const headersForLogging = this.contextMgr\n ? this.headerMethods.buildSecureMapForLogs(this.contextMgr.headerSet, this.contextMgr.contextReader)\n : new Map<string, any>();\n\n // Build request options\n const options: RequestInit = {\n method: httpMethod,\n headers: httpHeaders,\n };\n\n // POST body is the first argument as JSON\n let requestDto: unknown;\n if (args.length > 0) {\n requestDto = args[0];\n options.body = JSON.stringify(requestDto);\n }\n\n // Wrap fetch in a method for LogApiCall.execute\n const method = async (): Promise<unknown> => {\n return this.executeFetch(url, options);\n };\n\n return await this.logApiCall.execute(\"CLIENT\", route, requestDto, headersForLogging, method);\n }\n\n /**\n * Execute the fetch request and handle response.\n */\n private async executeFetch(url: string, options: RequestInit): Promise<unknown> {\n const response = await fetch(url, options);\n\n if (response.ok) {\n return await response.json();\n }\n\n // Handle errors (non-2xx responses)\n // Try to parse ProtocolError from response body\n const protocolError = (await response.json()) as ProtocolError;\n\n // Reconstruct appropriate HttpError subclass and throw\n throw ClientErrorTranslator.translateError(response, protocolError);\n }\n}\n"]}
|
package/src/ContextMgr.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ import { PlatformHeader, ContextReader } from '@webpieces/http-api';
|
|
|
23
23
|
*
|
|
24
24
|
* // Both cases
|
|
25
25
|
* const config = new ClientConfig('http://api.example.com', contextMgr);
|
|
26
|
-
* const client = createClient(
|
|
26
|
+
* const client = createClient(SaveApi, config);
|
|
27
27
|
* ```
|
|
28
28
|
*/
|
|
29
29
|
export declare class ContextMgr {
|
package/src/ContextMgr.js
CHANGED
|
@@ -25,7 +25,7 @@ exports.ContextMgr = void 0;
|
|
|
25
25
|
*
|
|
26
26
|
* // Both cases
|
|
27
27
|
* const config = new ClientConfig('http://api.example.com', contextMgr);
|
|
28
|
-
* const client = createClient(
|
|
28
|
+
* const client = createClient(SaveApi, config);
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
31
|
class ContextMgr {
|
package/src/ContextMgr.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContextMgr.js","sourceRoot":"","sources":["../../../../../packages/http/http-client/src/ContextMgr.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAa,UAAU;IACnB;IACI;;;OAGG;IACa,aAA4B;IAE5C;;;OAGG;IACa,SAA2B;QAN3B,kBAAa,GAAb,aAAa,CAAe;QAM5B,cAAS,GAAT,SAAS,CAAkB;IAC5C,CAAC;IAEJ;;;;;;;;;;;;;OAaG;IACH,IAAI,CAAC,UAAkB;QACnB,wCAAwC;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC;QAErE,yCAAyC;QACzC,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,kDAAkD;QAClD,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;YAC5B,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,iCAAiC;QACjC,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAE9C,+BAA+B;QAC/B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YACxD,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,OAAO,SAAS,CAAC;IACrB,CAAC;CACJ;AArDD,gCAqDC","sourcesContent":["import { PlatformHeader, ContextReader } from '@webpieces/http-api';\n\n/**\n * ContextMgr - Manages context reader and header set for HTTP clients.\n *\n * Passed to createClient() via ClientConfig to enable automatic header propagation.\n * Combines a ContextReader (how to read header values) with a header set\n * (which headers to propagate).\n *\n * Example usage:\n * ```typescript\n * // Node.js server-side (reads from RequestContext)\n * const contextMgr = new ContextMgr(\n * new RequestContextReader(),\n * [...WebpiecesCoreHeaders.getAllHeaders(), ...CompanyHeaders.getAllHeaders()]\n * );\n *\n * // Browser client-side (reads from static map)\n * const headers = new Map([['Authorization', getToken()]]);\n * const contextMgr = new ContextMgr(\n * new StaticContextReader(headers),\n * [WebpiecesCoreHeaders.REQUEST_ID]\n * );\n *\n * // Both cases\n * const config = new ClientConfig('http://api.example.com', contextMgr);\n * const client = createClient(
|
|
1
|
+
{"version":3,"file":"ContextMgr.js","sourceRoot":"","sources":["../../../../../packages/http/http-client/src/ContextMgr.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAa,UAAU;IACnB;IACI;;;OAGG;IACa,aAA4B;IAE5C;;;OAGG;IACa,SAA2B;QAN3B,kBAAa,GAAb,aAAa,CAAe;QAM5B,cAAS,GAAT,SAAS,CAAkB;IAC5C,CAAC;IAEJ;;;;;;;;;;;;;OAaG;IACH,IAAI,CAAC,UAAkB;QACnB,wCAAwC;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC;QAErE,yCAAyC;QACzC,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,kDAAkD;QAClD,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;YAC5B,OAAO,SAAS,CAAC;QACrB,CAAC;QAED,iCAAiC;QACjC,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAE9C,+BAA+B;QAC/B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YACxD,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,OAAO,SAAS,CAAC;IACrB,CAAC;CACJ;AArDD,gCAqDC","sourcesContent":["import { PlatformHeader, ContextReader } from '@webpieces/http-api';\n\n/**\n * ContextMgr - Manages context reader and header set for HTTP clients.\n *\n * Passed to createClient() via ClientConfig to enable automatic header propagation.\n * Combines a ContextReader (how to read header values) with a header set\n * (which headers to propagate).\n *\n * Example usage:\n * ```typescript\n * // Node.js server-side (reads from RequestContext)\n * const contextMgr = new ContextMgr(\n * new RequestContextReader(),\n * [...WebpiecesCoreHeaders.getAllHeaders(), ...CompanyHeaders.getAllHeaders()]\n * );\n *\n * // Browser client-side (reads from static map)\n * const headers = new Map([['Authorization', getToken()]]);\n * const contextMgr = new ContextMgr(\n * new StaticContextReader(headers),\n * [WebpiecesCoreHeaders.REQUEST_ID]\n * );\n *\n * // Both cases\n * const config = new ClientConfig('http://api.example.com', contextMgr);\n * const client = createClient(SaveApi, config);\n * ```\n */\nexport class ContextMgr {\n constructor(\n /**\n * The context reader that provides header values.\n * Different implementations for Node.js vs browser.\n */\n public readonly contextReader: ContextReader,\n\n /**\n * The set of platform headers to read and propagate.\n * Only headers in this set will be added to requests.\n */\n public readonly headerSet: PlatformHeader[]\n ) {}\n\n /**\n * Read a single header value by header name.\n *\n * Returns undefined if:\n * - Header name not found in headerSet\n * - Header has isWantTransferred=false\n * - contextReader returns undefined/null/empty string\n *\n * This method is called by ClientFactory for each header in the headerSet,\n * allowing a single loop instead of the previous double-loop pattern.\n *\n * @param headerName - The HTTP header name (e.g., 'x-request-id')\n * @returns The header value, or undefined if not available/transferable\n */\n read(headerName: string): string | undefined {\n // Find the header definition in our set\n const header = this.headerSet.find(h => h.headerName === headerName);\n\n // Not in our header set - don't transfer\n if (!header) {\n return undefined;\n }\n\n // Header not marked for transfer - don't transfer\n if (!header.isWantTransferred) {\n return undefined;\n }\n\n // Read value from context reader\n const value = this.contextReader.read(header);\n\n // Only return non-empty values\n if (value !== undefined && value !== null && value !== '') {\n return value;\n }\n\n return undefined;\n }\n}\n"]}
|
package/src/index.d.ts
CHANGED
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
* Usage:
|
|
22
22
|
* ```typescript
|
|
23
23
|
* import { createApiClient, ClientConfig } from '@webpieces/http-client';
|
|
24
|
-
* import {
|
|
24
|
+
* import { SaveApi } from './api/SaveApi';
|
|
25
25
|
*
|
|
26
26
|
* const config = new ClientConfig('http://localhost:3000');
|
|
27
|
-
* const client = createApiClient(
|
|
27
|
+
* const client = createApiClient(SaveApi, config);
|
|
28
28
|
*
|
|
29
29
|
* const response = await client.save({ query: 'test' });
|
|
30
30
|
* ```
|
package/src/index.js
CHANGED
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
* Usage:
|
|
23
23
|
* ```typescript
|
|
24
24
|
* import { createApiClient, ClientConfig } from '@webpieces/http-client';
|
|
25
|
-
* import {
|
|
25
|
+
* import { SaveApi } from './api/SaveApi';
|
|
26
26
|
*
|
|
27
27
|
* const config = new ClientConfig('http://localhost:3000');
|
|
28
|
-
* const client = createApiClient(
|
|
28
|
+
* const client = createApiClient(SaveApi, config);
|
|
29
29
|
*
|
|
30
30
|
* const response = await client.save({ query: 'test' });
|
|
31
31
|
* ```
|
package/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/http/http-client/src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;;;AAEH,iDAAgE;AAAvD,gHAAA,eAAe,OAAA;AAAE,6GAAA,YAAY,OAAA;AACtC,iEAAgE;AAAvD,8HAAA,qBAAqB,OAAA;AAE9B,4CAA4C;AAC5C,iDAA8E;AAArE,oHAAA,mBAAmB,OAAA;AAAE,uHAAA,sBAAsB,OAAA;AACpD,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AAKnB,uEAAuE;AACvE,gDAM6B;AALzB,mGAAA,OAAO,OAAA;AACP,oGAAA,QAAQ,OAAA;AACR,0GAAA,cAAc,OAAA;AACd,gHAAA,oBAAoB,OAAA","sourcesContent":["/**\n * @webpieces/http-client\n *\n * Client-side HTTP client generation package.\n * Reads API decorators and generates type-safe HTTP clients.\n *\n * This is the client-side counterpart to @webpieces/http-routing:\n * - Server (@webpieces/http-routing): API decorators -> route HTTP to controllers\n * - Client (@webpieces/http-client): API decorators -> generate HTTP from method calls\n *\n * Both packages depend on @webpieces/http-api for shared decorator definitions.\n *\n * Architecture:\n * ```\n * http-api (defines the contract)\n * ^\n * +-- http-routing (server: contract -> handlers)\n * +-- http-client (client: contract -> HTTP requests) <- YOU ARE HERE\n * ```\n *\n * Usage:\n * ```typescript\n * import { createApiClient, ClientConfig } from '@webpieces/http-client';\n * import {
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/http/http-client/src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;;;AAEH,iDAAgE;AAAvD,gHAAA,eAAe,OAAA;AAAE,6GAAA,YAAY,OAAA;AACtC,iEAAgE;AAAvD,8HAAA,qBAAqB,OAAA;AAE9B,4CAA4C;AAC5C,iDAA8E;AAArE,oHAAA,mBAAmB,OAAA;AAAE,uHAAA,sBAAsB,OAAA;AACpD,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AAKnB,uEAAuE;AACvE,gDAM6B;AALzB,mGAAA,OAAO,OAAA;AACP,oGAAA,QAAQ,OAAA;AACR,0GAAA,cAAc,OAAA;AACd,gHAAA,oBAAoB,OAAA","sourcesContent":["/**\n * @webpieces/http-client\n *\n * Client-side HTTP client generation package.\n * Reads API decorators and generates type-safe HTTP clients.\n *\n * This is the client-side counterpart to @webpieces/http-routing:\n * - Server (@webpieces/http-routing): API decorators -> route HTTP to controllers\n * - Client (@webpieces/http-client): API decorators -> generate HTTP from method calls\n *\n * Both packages depend on @webpieces/http-api for shared decorator definitions.\n *\n * Architecture:\n * ```\n * http-api (defines the contract)\n * ^\n * +-- http-routing (server: contract -> handlers)\n * +-- http-client (client: contract -> HTTP requests) <- YOU ARE HERE\n * ```\n *\n * Usage:\n * ```typescript\n * import { createApiClient, ClientConfig } from '@webpieces/http-client';\n * import { SaveApi } from './api/SaveApi';\n *\n * const config = new ClientConfig('http://localhost:3000');\n * const client = createApiClient(SaveApi, config);\n *\n * const response = await client.save({ query: 'test' });\n * ```\n */\n\nexport { createApiClient, ClientConfig } from './ClientFactory';\nexport { ClientErrorTranslator } from './ClientErrorTranslator';\n\n// Context management for header propagation\nexport { StaticContextReader, CompositeContextReader } from './ContextReader';\nexport { ContextMgr } from './ContextMgr';\n\n// Re-export ContextReader interface from http-api for convenience\nexport { ContextReader } from '@webpieces/http-api';\n\n// Re-export API decorators for convenience (same as http-routing does)\nexport {\n ApiPath,\n Endpoint,\n Authentication,\n AuthenticationConfig,\n ValidateImplementation,\n} from '@webpieces/http-api';\n"]}
|