@webpieces/http-client-browser 0.4.879 → 0.4.881

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 CHANGED
@@ -12,10 +12,13 @@ HeaderRegistry.configure(AppHeaders.getAllHeaders(), CompanyHeaders.getAllHeader
12
12
 
13
13
  const store = new MutableContextStore();
14
14
  const factory = new ClientHttpBrowserFactory(store);
15
- const saveApi = factory.createRpcClient(SaveApi, new ClientConfig('server'));
15
+ const saveApi = factory.createRpcClient(SaveApi, new ClientConfig('server', ClientRole.END_USER_CLIENT));
16
16
 
17
17
  const res = await saveApi.save({ query: 'test' }); // type-safe
18
18
 
19
+ // ClientRole is required, with no default. A browser bundle acts for a person, so it declares
20
+ // END_USER_CLIENT and a received 401 decodes as ApiUnauthorizedError ("log in again").
21
+
19
22
  // later, after login — every subsequent call carries these
20
23
  store.set(WebpiecesCoreHeaders.AUTHORIZATION, token);
21
24
  store.set(CompanyHeaders.TENANT_ID, tenantId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/http-client-browser",
3
- "version": "0.4.879",
3
+ "version": "0.4.881",
4
4
  "description": "Browser HTTP client for webpieces: DI-free (React or Angular), app-managed context store, no AsyncLocalStorage",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -22,7 +22,7 @@
22
22
  "access": "public"
23
23
  },
24
24
  "dependencies": {
25
- "@webpieces/core-util": "0.4.879",
26
- "@webpieces/http-client-core": "0.4.879"
25
+ "@webpieces/core-util": "0.4.881",
26
+ "@webpieces/http-client-core": "0.4.881"
27
27
  }
28
28
  }
@@ -1,4 +1,4 @@
1
- import { ContextMgr, DestinationTrust, RouteMetadata } from '@webpieces/core-util';
1
+ import { ContextMgr, DestinationTrust, RouteMetadata, ClientRole } from '@webpieces/core-util';
2
2
  import { ApiPrototype, ByteReadableStream, ClientRequest, ProxyClient, RequestOutcome } from '@webpieces/http-client-core';
3
3
  import { ClientConfig } from './ClientConfig';
4
4
  import { RequestLifecycleListener } from './RequestLifecycleListener';
@@ -31,6 +31,7 @@ export declare class BrowserProxyClient extends ProxyClient {
31
31
  * on :8201, while the same bundle served BY that backend in prod registers nothing and goes
32
32
  * relative. No `window` access, so this stays SSR-safe and testable.
33
33
  */
34
+ protected clientRole(): ClientRole;
34
35
  protected resolveBaseUrl(): Promise<string>;
35
36
  /** Browser Fetch request streaming is specified as half-duplex, not concurrent duplex. */
36
37
  protected supportsConcurrentDuplexFetch(): boolean;
@@ -52,6 +52,9 @@ class BrowserProxyClient extends http_client_core_1.ProxyClient {
52
52
  * on :8201, while the same bundle served BY that backend in prod registers nothing and goes
53
53
  * relative. No `window` access, so this stays SSR-safe and testable.
54
54
  */
55
+ clientRole() {
56
+ return this.config.role;
57
+ }
55
58
  async resolveBaseUrl() {
56
59
  return (await core_util_1.ClientRegistry.tryResolve(this.config.svcName)) ?? '';
57
60
  }
@@ -1 +1 @@
1
- {"version":3,"file":"BrowserProxyClient.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/BrowserProxyClient.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ContextMgr,\n ClientRegistry,\n DestinationTrust,\n LogApiCallImpl,\n RouteMetadata,\n StreamDirection,\n} from '@webpieces/core-util';\nimport { BrowserApiCallContext } from './BrowserApiCallContext';\nimport {\n ApiPrototype,\n ByteReadableStream,\n ClientRequest,\n ProxyClient,\n RequestOutcome,\n StreamingCapabilityError,\n} from '@webpieces/http-client-core';\nimport { ClientConfig } from './ClientConfig';\nimport { RequestLifecycleListener } from './RequestLifecycleListener';\n\n/**\n * The browser {@link ProxyClient}. Reads context from the app-held store (via {@link ContextMgr}),\n * because a browser has no ambient request scope.\n *\n * It attaches NO outbound credential and does NO recording — both inherit the base's no-ops. A\n * browser cannot mint an OIDC token and must never hold a shared secret; the user's JWT travels as\n * an ordinary transferred context key, set on the store at login.\n *\n * This is the ONLY class in webpieces that names ContextMgr.\n */\nexport class BrowserProxyClient extends ProxyClient {\n protected override selectAuthMethod(route: RouteMetadata): import('@webpieces/core-util').AuthMode {\n const selected = route.authMeta?.methods.find((method: import('@webpieces/core-util').AuthMode) => method.kind === 'jwt' || method.kind === 'public');\n if (!selected) {\n const methods = route.authMeta?.methods.map((method: import('@webpieces/core-util').AuthMode) => method.kind === 'apikey' ? `apiKey('${method.regime}', credentials)` : method.kind).join(', ');\n throw new Error(`Endpoint ${route.apiName}.${route.methodName} declares ${methods}. A browser cannot hold these credentials; call it from a server-side client with the configured verifier credentials.`);\n }\n return selected;\n }\n private config!: ClientConfig;\n\n constructor(\n private readonly contextMgr: ContextMgr,\n private readonly lifecycleListener?: RequestLifecycleListener,\n ) {\n // The browser's ApiCallContext, constructed here rather than installed at startup: its store\n // is static, so every instance stamps into the one slot BrowserApiCallContext.snapshot()\n // reads — which is how a browser logger folds the `api` tag into each line.\n super(new LogApiCallImpl(new BrowserApiCallContext()));\n }\n\n /** Bind this client to one API contract + base URL. */\n init(apiPrototype: ApiPrototype<object>, config: ClientConfig): void {\n this.config = config;\n // The browser factory does not take app filters yet, so this client runs the framework\n // built-ins only (today: none). The chain itself is isomorphic and ready for them.\n this.initRoutes(apiPrototype, []);\n }\n\n /**\n * The same chain every client runs — a ClientRegistry mapping, else the installed deriver — but\n * with the BROWSER's fallback: `''`, which makes the URL RELATIVE (`/auth/oauth`) and therefore\n * same-origin, by definition. A browser app almost always calls the backend that served it, so\n * that is the default, and an unregistered svcName must NEVER throw the way it used to — a\n * forgotten registration silently killed sign-in, the request never leaving the page.\n *\n * A mapping still wins, which is exactly how an Angular dev server on :4201 reaches its backend\n * on :8201, while the same bundle served BY that backend in prod registers nothing and goes\n * relative. No `window` access, so this stays SSR-safe and testable.\n */\n protected override async resolveBaseUrl(): Promise<string> {\n return (await ClientRegistry.tryResolve(this.config.svcName)) ?? '';\n }\n\n /** Browser Fetch request streaming is specified as half-duplex, not concurrent duplex. */\n protected override supportsConcurrentDuplexFetch(): boolean {\n return false;\n }\n\n protected override sendStreamingTransport(\n _request: ClientRequest,\n _signal: AbortSignal,\n _body: ByteReadableStream,\n ): Promise<Response> {\n return Promise.reject(\n new StreamingCapabilityError(\n 'browser',\n 'Fetch request streaming is half-duplex and has no safe bidirectional fallback.',\n ),\n );\n }\n\n /**\n * `destination` is always the un-verifying kind here — {@link assertEndpointSupported} below\n * refuses to bind an oidc(...) / sharedSecret(...) contract, so every browser destination is\n * jwt() or @WpAuthPublic — which means a browser never puts a trusted context key on the wire. It is\n * still threaded rather than short-circuited: the rule lives in ContextMgr, one place, for both\n * environments.\n */\n protected override outboundContextHeaders(destination: DestinationTrust): Map<string, string> {\n return this.contextMgr.buildOutboundHeaders(destination);\n }\n\n /**\n * Into the app-held store, via the same {@link ContextMgr} that owns the outbound direction — so\n * a value a server set reaches the page without the page naming a header.\n *\n * TRUSTED response keys are dropped there, by type: a browser store holds only untrusted values.\n * `destination` is threaded anyway rather than short-circuited here, for the reason the outbound\n * direction gives — the rule lives in ContextMgr, one place, for both environments.\n */\n protected override acceptResponseContext(\n headers: Headers,\n destination: DestinationTrust,\n ): void {\n this.contextMgr.acceptResponseHeaders(headers, destination);\n }\n\n /**\n * Forward the call's lifecycle to the app's listener, if one was registered on the factory. The\n * optional chain makes both a no-op when no listener is present — the default browser case.\n */\n protected override onRequestStart(route: RouteMetadata): void {\n this.lifecycleListener?.onRequestStart(route);\n }\n\n protected override onRequestEnd(route: RouteMetadata, outcome: RequestOutcome): void {\n this.lifecycleListener?.onRequestEnd(route, outcome);\n }\n\n /**\n * Reject a contract this browser cannot satisfy, at bind time rather than on the first call.\n * Both service-to-service modes need credentials only a server has: oidc(...) needs a runtime\n * service account to mint a token, sharedSecret(...) needs a secret no browser may ship.\n *\n * @WpLocalOnly is deliberately NOT rejected: a browser calling a dev-only endpoint on the\n * developer's own server is the motivating case for that mode (shipping browser logs into the\n * server log). It needs no credential — the server refuses it off-local by not having the route.\n *\n * An exhaustive switch with NO `default`, like {@link DestinationTrust.forAuthMode} and\n * `AuthFilter.verifiesCaller`. This was a NEGATIVE allow-list (`kind !== 'oidc' && kind !==\n * 'shared-secret'`), which silently WAVED THROUGH any future AuthMode kind — the browser would\n * have bound a contract it cannot satisfy and failed on the first call instead of at bind time.\n * Adding `local-only` is what surfaced it: the third reader of the union should fail to compile\n * on a NEW kind for the same reason the other two do.\n */\n protected override assertEndpointSupported(route: RouteMetadata): void {\n const methodName = route.methodName;\n if (route.streaming && route.streaming.direction !== StreamDirection.RESPONSE) {\n throw new StreamingCapabilityError(\n 'browser',\n `Browser Fetch supports only @WpStream(StreamDirection.RESPONSE); ${methodName} is ${route.streaming.direction}.`,\n );\n }\n this.selectAuthMethod(route);\n }\n}\n"]}
1
+ {"version":3,"file":"BrowserProxyClient.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/BrowserProxyClient.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ContextMgr,\n ClientRegistry,\n DestinationTrust,\n LogApiCallImpl,\n RouteMetadata,\n StreamDirection,\n ClientRole,\n} from '@webpieces/core-util';\nimport { BrowserApiCallContext } from './BrowserApiCallContext';\nimport {\n ApiPrototype,\n ByteReadableStream,\n ClientRequest,\n ProxyClient,\n RequestOutcome,\n StreamingCapabilityError,\n} from '@webpieces/http-client-core';\nimport { ClientConfig } from './ClientConfig';\nimport { RequestLifecycleListener } from './RequestLifecycleListener';\n\n/**\n * The browser {@link ProxyClient}. Reads context from the app-held store (via {@link ContextMgr}),\n * because a browser has no ambient request scope.\n *\n * It attaches NO outbound credential and does NO recording — both inherit the base's no-ops. A\n * browser cannot mint an OIDC token and must never hold a shared secret; the user's JWT travels as\n * an ordinary transferred context key, set on the store at login.\n *\n * This is the ONLY class in webpieces that names ContextMgr.\n */\nexport class BrowserProxyClient extends ProxyClient {\n protected override selectAuthMethod(route: RouteMetadata): import('@webpieces/core-util').AuthMode {\n const selected = route.authMeta?.methods.find((method: import('@webpieces/core-util').AuthMode) => method.kind === 'jwt' || method.kind === 'public');\n if (!selected) {\n const methods = route.authMeta?.methods.map((method: import('@webpieces/core-util').AuthMode) => method.kind === 'apikey' ? `apiKey('${method.regime}', credentials)` : method.kind).join(', ');\n throw new Error(`Endpoint ${route.apiName}.${route.methodName} declares ${methods}. A browser cannot hold these credentials; call it from a server-side client with the configured verifier credentials.`);\n }\n return selected;\n }\n private config!: ClientConfig;\n\n constructor(\n private readonly contextMgr: ContextMgr,\n private readonly lifecycleListener?: RequestLifecycleListener,\n ) {\n // The browser's ApiCallContext, constructed here rather than installed at startup: its store\n // is static, so every instance stamps into the one slot BrowserApiCallContext.snapshot()\n // reads — which is how a browser logger folds the `api` tag into each line.\n super(new LogApiCallImpl(new BrowserApiCallContext()));\n }\n\n /** Bind this client to one API contract + base URL. */\n init(apiPrototype: ApiPrototype<object>, config: ClientConfig): void {\n this.config = config;\n // The browser factory does not take app filters yet, so this client runs the framework\n // built-ins only (today: none). The chain itself is isomorphic and ready for them.\n this.initRoutes(apiPrototype, []);\n }\n\n /**\n * The same chain every client runs — a ClientRegistry mapping, else the installed deriver — but\n * with the BROWSER's fallback: `''`, which makes the URL RELATIVE (`/auth/oauth`) and therefore\n * same-origin, by definition. A browser app almost always calls the backend that served it, so\n * that is the default, and an unregistered svcName must NEVER throw the way it used to — a\n * forgotten registration silently killed sign-in, the request never leaving the page.\n *\n * A mapping still wins, which is exactly how an Angular dev server on :4201 reaches its backend\n * on :8201, while the same bundle served BY that backend in prod registers nothing and goes\n * relative. No `window` access, so this stays SSR-safe and testable.\n */\n protected override clientRole(): ClientRole {\n return this.config.role;\n }\n\n protected override async resolveBaseUrl(): Promise<string> {\n return (await ClientRegistry.tryResolve(this.config.svcName)) ?? '';\n }\n\n /** Browser Fetch request streaming is specified as half-duplex, not concurrent duplex. */\n protected override supportsConcurrentDuplexFetch(): boolean {\n return false;\n }\n\n protected override sendStreamingTransport(\n _request: ClientRequest,\n _signal: AbortSignal,\n _body: ByteReadableStream,\n ): Promise<Response> {\n return Promise.reject(\n new StreamingCapabilityError(\n 'browser',\n 'Fetch request streaming is half-duplex and has no safe bidirectional fallback.',\n ),\n );\n }\n\n /**\n * `destination` is always the un-verifying kind here — {@link assertEndpointSupported} below\n * refuses to bind an oidc(...) / sharedSecret(...) contract, so every browser destination is\n * jwt() or @WpAuthPublic — which means a browser never puts a trusted context key on the wire. It is\n * still threaded rather than short-circuited: the rule lives in ContextMgr, one place, for both\n * environments.\n */\n protected override outboundContextHeaders(destination: DestinationTrust): Map<string, string> {\n return this.contextMgr.buildOutboundHeaders(destination);\n }\n\n /**\n * Into the app-held store, via the same {@link ContextMgr} that owns the outbound direction — so\n * a value a server set reaches the page without the page naming a header.\n *\n * TRUSTED response keys are dropped there, by type: a browser store holds only untrusted values.\n * `destination` is threaded anyway rather than short-circuited here, for the reason the outbound\n * direction gives — the rule lives in ContextMgr, one place, for both environments.\n */\n protected override acceptResponseContext(\n headers: Headers,\n destination: DestinationTrust,\n ): void {\n this.contextMgr.acceptResponseHeaders(headers, destination);\n }\n\n /**\n * Forward the call's lifecycle to the app's listener, if one was registered on the factory. The\n * optional chain makes both a no-op when no listener is present — the default browser case.\n */\n protected override onRequestStart(route: RouteMetadata): void {\n this.lifecycleListener?.onRequestStart(route);\n }\n\n protected override onRequestEnd(route: RouteMetadata, outcome: RequestOutcome): void {\n this.lifecycleListener?.onRequestEnd(route, outcome);\n }\n\n /**\n * Reject a contract this browser cannot satisfy, at bind time rather than on the first call.\n * Both service-to-service modes need credentials only a server has: oidc(...) needs a runtime\n * service account to mint a token, sharedSecret(...) needs a secret no browser may ship.\n *\n * @WpLocalOnly is deliberately NOT rejected: a browser calling a dev-only endpoint on the\n * developer's own server is the motivating case for that mode (shipping browser logs into the\n * server log). It needs no credential — the server refuses it off-local by not having the route.\n *\n * An exhaustive switch with NO `default`, like {@link DestinationTrust.forAuthMode} and\n * `AuthFilter.verifiesCaller`. This was a NEGATIVE allow-list (`kind !== 'oidc' && kind !==\n * 'shared-secret'`), which silently WAVED THROUGH any future AuthMode kind — the browser would\n * have bound a contract it cannot satisfy and failed on the first call instead of at bind time.\n * Adding `local-only` is what surfaced it: the third reader of the union should fail to compile\n * on a NEW kind for the same reason the other two do.\n */\n protected override assertEndpointSupported(route: RouteMetadata): void {\n const methodName = route.methodName;\n if (route.streaming && route.streaming.direction !== StreamDirection.RESPONSE) {\n throw new StreamingCapabilityError(\n 'browser',\n `Browser Fetch supports only @WpStream(StreamDirection.RESPONSE); ${methodName} is ${route.streaming.direction}.`,\n );\n }\n this.selectAuthMethod(route);\n }\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import { ClientRole } from '@webpieces/core-util';
1
2
  /**
2
3
  * Per-client STATE for a browser HTTP client — nothing else. A plain class; it extends nothing and
3
4
  * is unrelated to the server package's ClientConfig.
@@ -14,7 +15,25 @@
14
15
  export declare class ClientConfig {
15
16
  /** Service name; resolved to a base URL via ClientRegistry (registered at app startup). */
16
17
  readonly svcName: string;
18
+ /**
19
+ * WHO receives this client's responses — {@link ClientRole.SERVER} for a server calling
20
+ * another server, {@link ClientRole.END_USER_CLIENT} for a client acting for a person (a
21
+ * browser bundle, an Expo shell, a remote MCP client). REQUIRED, with no default (#1173): it
22
+ * decides what a received 401 means — "the user must log in again" for an end-user client,
23
+ * "this service's own credential is broken" (a 500 to its caller) for a server — and the
24
+ * right answer is opposite for the two, so no default could be right for both.
25
+ */
26
+ readonly role: ClientRole;
17
27
  constructor(
18
28
  /** Service name; resolved to a base URL via ClientRegistry (registered at app startup). */
19
- svcName: string);
29
+ svcName: string,
30
+ /**
31
+ * WHO receives this client's responses — {@link ClientRole.SERVER} for a server calling
32
+ * another server, {@link ClientRole.END_USER_CLIENT} for a client acting for a person (a
33
+ * browser bundle, an Expo shell, a remote MCP client). REQUIRED, with no default (#1173): it
34
+ * decides what a received 401 means — "the user must log in again" for an end-user client,
35
+ * "this service's own credential is broken" (a 500 to its caller) for a server — and the
36
+ * right answer is opposite for the two, so no default could be right for both.
37
+ */
38
+ role: ClientRole);
20
39
  }
@@ -16,10 +16,21 @@ exports.ClientConfig = void 0;
16
16
  */
17
17
  class ClientConfig {
18
18
  svcName;
19
+ role;
19
20
  constructor(
20
21
  /** Service name; resolved to a base URL via ClientRegistry (registered at app startup). */
21
- svcName) {
22
+ svcName,
23
+ /**
24
+ * WHO receives this client's responses — {@link ClientRole.SERVER} for a server calling
25
+ * another server, {@link ClientRole.END_USER_CLIENT} for a client acting for a person (a
26
+ * browser bundle, an Expo shell, a remote MCP client). REQUIRED, with no default (#1173): it
27
+ * decides what a received 401 means — "the user must log in again" for an end-user client,
28
+ * "this service's own credential is broken" (a 500 to its caller) for a server — and the
29
+ * right answer is opposite for the two, so no default could be right for both.
30
+ */
31
+ role) {
22
32
  this.svcName = svcName;
33
+ this.role = role;
23
34
  }
24
35
  }
25
36
  exports.ClientConfig = ClientConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"ClientConfig.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/ClientConfig.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;GAYG;AACH,MAAa,YAAY;IAGD;IAFpB;IACI,2FAA2F;IAC3E,OAAe;QAAf,YAAO,GAAP,OAAO,CAAQ;IAChC,CAAC;CACP;AALD,oCAKC","sourcesContent":["/**\n * Per-client STATE for a browser HTTP client — nothing else. A plain class; it extends nothing and\n * is unrelated to the server package's ClientConfig.\n *\n * The base URL is resolved from `svcName` through the global {@link ClientRegistry}: a registered\n * mapping, else the installed deriver, else RELATIVE — i.e. the origin that served the page, which\n * is what a browser app calling its own backend wants and needs NO configuration. Register a mapping\n * only when the backend is somewhere else (an Angular dev server on :4201 reaching :8201). So this\n * config is just the svcName, and an unregistered one never throws.\n *\n * The context store is NOT config: it is a dependency of {@link ClientHttpBrowserFactory}, shared\n * by every client it builds.\n */\nexport class ClientConfig {\n constructor(\n /** Service name; resolved to a base URL via ClientRegistry (registered at app startup). */\n public readonly svcName: string,\n ) {}\n}\n"]}
1
+ {"version":3,"file":"ClientConfig.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/ClientConfig.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;GAYG;AACH,MAAa,YAAY;IAGD;IASA;IAXpB;IACI,2FAA2F;IAC3E,OAAe;IAC/B;;;;;;;OAOG;IACa,IAAgB;QAThB,YAAO,GAAP,OAAO,CAAQ;QASf,SAAI,GAAJ,IAAI,CAAY;IACjC,CAAC;CACP;AAdD,oCAcC","sourcesContent":["import { ClientRole } from '@webpieces/core-util';\n\n/**\n * Per-client STATE for a browser HTTP client — nothing else. A plain class; it extends nothing and\n * is unrelated to the server package's ClientConfig.\n *\n * The base URL is resolved from `svcName` through the global {@link ClientRegistry}: a registered\n * mapping, else the installed deriver, else RELATIVE — i.e. the origin that served the page, which\n * is what a browser app calling its own backend wants and needs NO configuration. Register a mapping\n * only when the backend is somewhere else (an Angular dev server on :4201 reaching :8201). So this\n * config is just the svcName, and an unregistered one never throws.\n *\n * The context store is NOT config: it is a dependency of {@link ClientHttpBrowserFactory}, shared\n * by every client it builds.\n */\nexport class ClientConfig {\n constructor(\n /** Service name; resolved to a base URL via ClientRegistry (registered at app startup). */\n public readonly svcName: string,\n /**\n * WHO receives this client's responses — {@link ClientRole.SERVER} for a server calling\n * another server, {@link ClientRole.END_USER_CLIENT} for a client acting for a person (a\n * browser bundle, an Expo shell, a remote MCP client). REQUIRED, with no default (#1173): it\n * decides what a received 401 means — \"the user must log in again\" for an end-user client,\n * \"this service's own credential is broken\" (a 500 to its caller) for a server — and the\n * right answer is opposite for the two, so no default could be right for both.\n */\n public readonly role: ClientRole,\n ) {}\n}\n"]}
@@ -19,7 +19,7 @@ import { RequestLifecycleListener } from './RequestLifecycleListener';
19
19
  * const store = new MutableContextStore();
20
20
  * const factory = new ClientHttpBrowserFactory(store);
21
21
  *
22
- * const saveApi = factory.createRpcClient(SaveApi, new ClientConfig('server'));
22
+ * const saveApi = factory.createRpcClient(SaveApi, new ClientConfig('server', ClientRole.END_USER_CLIENT));
23
23
  * const response = await saveApi.save({ query: 'test' }); // type-safe
24
24
  *
25
25
  * // later, when the user logs in / picks a tenant — every subsequent call carries them:
@@ -23,7 +23,7 @@ const MutableContextStore_1 = require("./MutableContextStore");
23
23
  * const store = new MutableContextStore();
24
24
  * const factory = new ClientHttpBrowserFactory(store);
25
25
  *
26
- * const saveApi = factory.createRpcClient(SaveApi, new ClientConfig('server'));
26
+ * const saveApi = factory.createRpcClient(SaveApi, new ClientConfig('server', ClientRole.END_USER_CLIENT));
27
27
  * const response = await saveApi.save({ query: 'test' }); // type-safe
28
28
  *
29
29
  * // later, when the user logs in / picks a tenant — every subsequent call carries them:
@@ -1 +1 @@
1
- {"version":3,"file":"ClientHttpBrowserFactory.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/ClientHttpBrowserFactory.ts"],"names":[],"mappings":";;;;AAAA,oDAAwF;AACxF,kEAA6E;AAC7E,6DAA0D;AAE1D,+DAA4D;AAG5D;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEI,IAAM,wBAAwB,GAA9B,MAAM,wBAAwB;IAKZ;IAJJ,UAAU,CAAa;IAExC,YACI,KAA0B,EACT,iBAA4C;QAA5C,sBAAiB,GAAjB,iBAAiB,CAA2B;QAE7D,IAAI,CAAC,UAAU,GAAG,IAAI,sBAAU,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;OAKG;IACH,eAAe,CAAmB,YAA6B,EAAE,MAAoB;QACjF,IAAA,gCAAoB,EAAC,YAAY,EAAE,0BAA0B,CAAC,CAAC;QAC/D,MAAM,WAAW,GAAG,IAAI,uCAAkB,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACpF,WAAW,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QACvC,OAAO,IAAA,mCAAgB,EAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IACvD,CAAC;CACJ,CAAA;AAtBY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,0BAAc,GAAE;6CAKF,yCAAmB;GAJrB,wBAAwB,CAsBpC","sourcesContent":["import { assertNotInternalApi, ContextMgr, DocumentDesign } from '@webpieces/core-util';\nimport { ApiPrototype, buildClientProxy } from '@webpieces/http-client-core';\nimport { BrowserProxyClient } from './BrowserProxyClient';\nimport { ClientConfig } from './ClientConfig';\nimport { MutableContextStore } from './MutableContextStore';\nimport { RequestLifecycleListener } from './RequestLifecycleListener';\n\n/**\n * ClientHttpBrowserFactory - builds type-safe HTTP clients for a BROWSER from API prototypes\n * carrying @ApiPath/@Endpoint decorators.\n *\n * Deliberately a plain class with NO decorators and NO inversify: this package may be bundled by\n * React just as easily as by Angular, and neither should be forced to adopt a Node DI container.\n * The app provides it through whatever DI it already has — Angular's `useFactory`, a React context,\n * or a module-level `const`.\n *\n * The factory holds the ONE collaborator every browser client shares (the app's\n * {@link MutableContextStore}); each {@link ClientConfig} holds only that one client's base URL.\n *\n * ```typescript\n * // once, at startup (after HeaderRegistry.configure(...)):\n * const store = new MutableContextStore();\n * const factory = new ClientHttpBrowserFactory(store);\n *\n * const saveApi = factory.createRpcClient(SaveApi, new ClientConfig('server'));\n * const response = await saveApi.save({ query: 'test' }); // type-safe\n *\n * // later, when the user logs in / picks a tenant — every subsequent call carries them:\n * store.set(AppHeaders.AUTHORIZATION, token); // an app-defined key, if it wants auto-attach\n * store.set(CompanyHeaders.TENANT_ID, tenantId);\n * ```\n *\n * A browser cannot hold service credentials, so a contract with an oidc(...) or sharedSecret(...)\n * endpoint throws in `createRpcClient`, not on the first call.\n */\n@DocumentDesign()\nexport class ClientHttpBrowserFactory {\n private readonly contextMgr: ContextMgr;\n\n constructor(\n store: MutableContextStore,\n private readonly lifecycleListener?: RequestLifecycleListener,\n ) {\n this.contextMgr = new ContextMgr(store);\n }\n\n /**\n * Create a type-safe HTTP client for one API contract.\n *\n * @param apiPrototype - The API prototype class with @ApiPath/@Endpoint decorators\n * @param config - This client's state (its baseUrl)\n */\n createRpcClient<T extends object>(apiPrototype: ApiPrototype<T>, config: ClientConfig): T {\n assertNotInternalApi(apiPrototype, 'ClientHttpBrowserFactory');\n const proxyClient = new BrowserProxyClient(this.contextMgr, this.lifecycleListener);\n proxyClient.init(apiPrototype, config);\n return buildClientProxy(apiPrototype, proxyClient);\n }\n}\n"]}
1
+ {"version":3,"file":"ClientHttpBrowserFactory.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/ClientHttpBrowserFactory.ts"],"names":[],"mappings":";;;;AAAA,oDAAwF;AACxF,kEAA6E;AAC7E,6DAA0D;AAE1D,+DAA4D;AAG5D;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEI,IAAM,wBAAwB,GAA9B,MAAM,wBAAwB;IAKZ;IAJJ,UAAU,CAAa;IAExC,YACI,KAA0B,EACT,iBAA4C;QAA5C,sBAAiB,GAAjB,iBAAiB,CAA2B;QAE7D,IAAI,CAAC,UAAU,GAAG,IAAI,sBAAU,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;OAKG;IACH,eAAe,CAAmB,YAA6B,EAAE,MAAoB;QACjF,IAAA,gCAAoB,EAAC,YAAY,EAAE,0BAA0B,CAAC,CAAC;QAC/D,MAAM,WAAW,GAAG,IAAI,uCAAkB,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACpF,WAAW,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QACvC,OAAO,IAAA,mCAAgB,EAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IACvD,CAAC;CACJ,CAAA;AAtBY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,0BAAc,GAAE;6CAKF,yCAAmB;GAJrB,wBAAwB,CAsBpC","sourcesContent":["import { assertNotInternalApi, ContextMgr, DocumentDesign } from '@webpieces/core-util';\nimport { ApiPrototype, buildClientProxy } from '@webpieces/http-client-core';\nimport { BrowserProxyClient } from './BrowserProxyClient';\nimport { ClientConfig } from './ClientConfig';\nimport { MutableContextStore } from './MutableContextStore';\nimport { RequestLifecycleListener } from './RequestLifecycleListener';\n\n/**\n * ClientHttpBrowserFactory - builds type-safe HTTP clients for a BROWSER from API prototypes\n * carrying @ApiPath/@Endpoint decorators.\n *\n * Deliberately a plain class with NO decorators and NO inversify: this package may be bundled by\n * React just as easily as by Angular, and neither should be forced to adopt a Node DI container.\n * The app provides it through whatever DI it already has — Angular's `useFactory`, a React context,\n * or a module-level `const`.\n *\n * The factory holds the ONE collaborator every browser client shares (the app's\n * {@link MutableContextStore}); each {@link ClientConfig} holds only that one client's base URL.\n *\n * ```typescript\n * // once, at startup (after HeaderRegistry.configure(...)):\n * const store = new MutableContextStore();\n * const factory = new ClientHttpBrowserFactory(store);\n *\n * const saveApi = factory.createRpcClient(SaveApi, new ClientConfig('server', ClientRole.END_USER_CLIENT));\n * const response = await saveApi.save({ query: 'test' }); // type-safe\n *\n * // later, when the user logs in / picks a tenant — every subsequent call carries them:\n * store.set(AppHeaders.AUTHORIZATION, token); // an app-defined key, if it wants auto-attach\n * store.set(CompanyHeaders.TENANT_ID, tenantId);\n * ```\n *\n * A browser cannot hold service credentials, so a contract with an oidc(...) or sharedSecret(...)\n * endpoint throws in `createRpcClient`, not on the first call.\n */\n@DocumentDesign()\nexport class ClientHttpBrowserFactory {\n private readonly contextMgr: ContextMgr;\n\n constructor(\n store: MutableContextStore,\n private readonly lifecycleListener?: RequestLifecycleListener,\n ) {\n this.contextMgr = new ContextMgr(store);\n }\n\n /**\n * Create a type-safe HTTP client for one API contract.\n *\n * @param apiPrototype - The API prototype class with @ApiPath/@Endpoint decorators\n * @param config - This client's state (its baseUrl)\n */\n createRpcClient<T extends object>(apiPrototype: ApiPrototype<T>, config: ClientConfig): T {\n assertNotInternalApi(apiPrototype, 'ClientHttpBrowserFactory');\n const proxyClient = new BrowserProxyClient(this.contextMgr, this.lifecycleListener);\n proxyClient.init(apiPrototype, config);\n return buildClientProxy(apiPrototype, proxyClient);\n }\n}\n"]}
@@ -13,7 +13,7 @@ import { AnyContextKey, AnyUntrustedContextKey, ContextStore } from '@webpieces/
13
13
  * // startup:
14
14
  * HeaderRegistry.configure(CompanyHeaders.ALL_HEADERS, true);
15
15
  * const factory = new ClientHttpFactory(new ContextMgr(store));
16
- * const client = factory.createRpcClient(SaveApi, new ClientConfig('server'));
16
+ * const client = factory.createRpcClient(SaveApi, new ClientConfig('server', ClientRole.END_USER_CLIENT));
17
17
  *
18
18
  * // later, when the user logs in / picks a tenant:
19
19
  * store.set(AppHeaders.AUTHORIZATION, token); // an app-defined key, if it wants auto-attach
@@ -15,7 +15,7 @@ exports.MutableContextStore = void 0;
15
15
  * // startup:
16
16
  * HeaderRegistry.configure(CompanyHeaders.ALL_HEADERS, true);
17
17
  * const factory = new ClientHttpFactory(new ContextMgr(store));
18
- * const client = factory.createRpcClient(SaveApi, new ClientConfig('server'));
18
+ * const client = factory.createRpcClient(SaveApi, new ClientConfig('server', ClientRole.END_USER_CLIENT));
19
19
  *
20
20
  * // later, when the user logs in / picks a tenant:
21
21
  * store.set(AppHeaders.AUTHORIZATION, token); // an app-defined key, if it wants auto-attach
@@ -1 +1 @@
1
- {"version":3,"file":"MutableContextStore.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/MutableContextStore.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAa,mBAAmB;IAC5B,wIAAwI;IAChI,MAAM,GAAyB,IAAI,GAAG,EAAE,CAAC;IAEjD;;;;;;;;;OASG;IACH,+DAA+D;IAC/D,GAAG,CAAC,GAA2B,EAAE,KAAc;QAC3C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACrC,CAAC;IAED,mEAAmE;IACnE,MAAM,CAAC,GAA2B;QAC9B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,+BAA+B;IAC/B,KAAK;QACD,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;IAED;;;;OAIG;IACH,IAAI,CAAC,GAAkB;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxC,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IACzD,CAAC;IAED,4EAA4E;IAC5E,+DAA+D;IAC/D,SAAS,CAAC,GAAkB;QACxB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;CACJ;AA5CD,kDA4CC","sourcesContent":["import { AnyContextKey, AnyUntrustedContextKey, ContextStore } from '@webpieces/core-util';\n\n/**\n * MutableContextStore - the BROWSER ContextStore (read AND write).\n *\n * Browsers have no AsyncLocalStorage, so apps (Angular/React) hold one of these\n * (e.g. as an Angular service / React context value) and set values as they become\n * known (login token, selected tenant, ...). The ContextMgr then reads from it on\n * every outbound request.\n *\n * Example (Angular):\n * ```typescript\n * const store = new MutableContextStore();\n * // startup:\n * HeaderRegistry.configure(CompanyHeaders.ALL_HEADERS, true);\n * const factory = new ClientHttpFactory(new ContextMgr(store));\n * const client = factory.createRpcClient(SaveApi, new ClientConfig('server'));\n *\n * // later, when the user logs in / picks a tenant:\n * store.set(AppHeaders.AUTHORIZATION, token); // an app-defined key, if it wants auto-attach\n * store.set(CompanyHeaders.TENANT_ID, tenantId);\n * ```\n */\nexport class MutableContextStore implements ContextStore {\n // webpieces-disable no-any-unknown -- heterogeneous by construction: a scalar key holds a string, a `collect` response key holds a list\n private values: Map<string, unknown> = new Map();\n\n /**\n * Set (or overwrite) the current value for a context key. UNTRUSTED keys only, by type. A browser cannot PROVE anything — every value in here was typed\n * by the app or the user — so a store that accepted a trusted key would be a forgery side door,\n * exactly the one closed on the {@link ApiCallContext} seam. Narrowing it here makes the mistake\n * a compile error in the browser bundle instead of a 401 at the far end of an HTTP call.\n *\n * `value` is `unknown` because this is also the write side {@link ContextMgr.acceptResponseHeaders}\n * uses, and a `collect` response key accumulates a LIST. {@link read} keeps its string return and\n * simply does not see those — a list has no single outbound header value.\n */\n // webpieces-disable no-any-unknown -- see the values map above\n set(key: AnyUntrustedContextKey, value: unknown): void {\n this.values.set(key.name, value);\n }\n\n /** Remove the current value for a context key (e.g. on logout). */\n remove(key: AnyUntrustedContextKey): void {\n this.values.delete(key.name);\n }\n\n /** Clear all stored values. */\n clear(): void {\n this.values.clear();\n }\n\n /**\n * The string value, or undefined. A non-string (a `collect` key's list) reads as ABSENT rather\n * than being coerced: this feeds the outbound header builder, and `['a','b'].toString()` on the\n * wire would be a silently wrong header.\n */\n read(key: AnyContextKey): string | undefined {\n const value = this.values.get(key.name);\n return typeof value === 'string' ? value : undefined;\n }\n\n /** The raw stored value, whatever its type — the merge path's read half. */\n // webpieces-disable no-any-unknown -- see the values map above\n readValue(key: AnyContextKey): unknown {\n return this.values.get(key.name);\n }\n}\n"]}
1
+ {"version":3,"file":"MutableContextStore.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/MutableContextStore.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAa,mBAAmB;IAC5B,wIAAwI;IAChI,MAAM,GAAyB,IAAI,GAAG,EAAE,CAAC;IAEjD;;;;;;;;;OASG;IACH,+DAA+D;IAC/D,GAAG,CAAC,GAA2B,EAAE,KAAc;QAC3C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACrC,CAAC;IAED,mEAAmE;IACnE,MAAM,CAAC,GAA2B;QAC9B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,+BAA+B;IAC/B,KAAK;QACD,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;IAED;;;;OAIG;IACH,IAAI,CAAC,GAAkB;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxC,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IACzD,CAAC;IAED,4EAA4E;IAC5E,+DAA+D;IAC/D,SAAS,CAAC,GAAkB;QACxB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;CACJ;AA5CD,kDA4CC","sourcesContent":["import { AnyContextKey, AnyUntrustedContextKey, ContextStore } from '@webpieces/core-util';\n\n/**\n * MutableContextStore - the BROWSER ContextStore (read AND write).\n *\n * Browsers have no AsyncLocalStorage, so apps (Angular/React) hold one of these\n * (e.g. as an Angular service / React context value) and set values as they become\n * known (login token, selected tenant, ...). The ContextMgr then reads from it on\n * every outbound request.\n *\n * Example (Angular):\n * ```typescript\n * const store = new MutableContextStore();\n * // startup:\n * HeaderRegistry.configure(CompanyHeaders.ALL_HEADERS, true);\n * const factory = new ClientHttpFactory(new ContextMgr(store));\n * const client = factory.createRpcClient(SaveApi, new ClientConfig('server', ClientRole.END_USER_CLIENT));\n *\n * // later, when the user logs in / picks a tenant:\n * store.set(AppHeaders.AUTHORIZATION, token); // an app-defined key, if it wants auto-attach\n * store.set(CompanyHeaders.TENANT_ID, tenantId);\n * ```\n */\nexport class MutableContextStore implements ContextStore {\n // webpieces-disable no-any-unknown -- heterogeneous by construction: a scalar key holds a string, a `collect` response key holds a list\n private values: Map<string, unknown> = new Map();\n\n /**\n * Set (or overwrite) the current value for a context key. UNTRUSTED keys only, by type. A browser cannot PROVE anything — every value in here was typed\n * by the app or the user — so a store that accepted a trusted key would be a forgery side door,\n * exactly the one closed on the {@link ApiCallContext} seam. Narrowing it here makes the mistake\n * a compile error in the browser bundle instead of a 401 at the far end of an HTTP call.\n *\n * `value` is `unknown` because this is also the write side {@link ContextMgr.acceptResponseHeaders}\n * uses, and a `collect` response key accumulates a LIST. {@link read} keeps its string return and\n * simply does not see those — a list has no single outbound header value.\n */\n // webpieces-disable no-any-unknown -- see the values map above\n set(key: AnyUntrustedContextKey, value: unknown): void {\n this.values.set(key.name, value);\n }\n\n /** Remove the current value for a context key (e.g. on logout). */\n remove(key: AnyUntrustedContextKey): void {\n this.values.delete(key.name);\n }\n\n /** Clear all stored values. */\n clear(): void {\n this.values.clear();\n }\n\n /**\n * The string value, or undefined. A non-string (a `collect` key's list) reads as ABSENT rather\n * than being coerced: this feeds the outbound header builder, and `['a','b'].toString()` on the\n * wire would be a silently wrong header.\n */\n read(key: AnyContextKey): string | undefined {\n const value = this.values.get(key.name);\n return typeof value === 'string' ? value : undefined;\n }\n\n /** The raw stored value, whatever its type — the merge path's read half. */\n // webpieces-disable no-any-unknown -- see the values map above\n readValue(key: AnyContextKey): unknown {\n return this.values.get(key.name);\n }\n}\n"]}
@@ -12,5 +12,10 @@ export declare class RpcClientProvider<T extends object> {
12
12
  private readonly destination;
13
13
  readonly deps: (typeof ClientConfig | typeof ClientHttpBrowserFactory)[];
14
14
  constructor(provide: BrowserToken, api: ApiPrototype<T>, destination: string);
15
- readonly useFactory: (factory: ClientHttpBrowserFactory, _config: ClientConfig) => T;
15
+ /**
16
+ * The app's own DI-provided {@link ClientConfig} is where this browser host DECLARED its
17
+ * {@link ClientConfig.role}; every bound client takes the role from it and only the destination
18
+ * from the binding, so the role is stated once, by the app, with no default.
19
+ */
20
+ readonly useFactory: (factory: ClientHttpBrowserFactory, config: ClientConfig) => T;
16
21
  }
@@ -17,7 +17,12 @@ class RpcClientProvider {
17
17
  this.api = api;
18
18
  this.destination = destination;
19
19
  }
20
- useFactory = (factory, _config) => factory.createRpcClient(this.api, new ClientConfig_1.ClientConfig(this.destination));
20
+ /**
21
+ * The app's own DI-provided {@link ClientConfig} is where this browser host DECLARED its
22
+ * {@link ClientConfig.role}; every bound client takes the role from it and only the destination
23
+ * from the binding, so the role is stated once, by the app, with no default.
24
+ */
25
+ useFactory = (factory, config) => factory.createRpcClient(this.api, new ClientConfig_1.ClientConfig(this.destination, config.role));
21
26
  }
22
27
  exports.RpcClientProvider = RpcClientProvider;
23
28
  //# sourceMappingURL=RpcClientProvider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RpcClientProvider.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/RpcClientProvider.ts"],"names":[],"mappings":";;;AAEA,yEAAsE;AACtE,iDAA8C;AAE9C;;;GAGG;AACH,MAAa,iBAAiB;IAIN;IACC;IACA;IALZ,IAAI,GAAG,CAAC,mDAAwB,EAAE,2BAAY,CAAC,CAAC;IAEzD,YACoB,OAAqB,EACpB,GAAoB,EACpB,WAAmB;QAFpB,YAAO,GAAP,OAAO,CAAc;QACpB,QAAG,GAAH,GAAG,CAAiB;QACpB,gBAAW,GAAX,WAAW,CAAQ;IACrC,CAAC;IAEK,UAAU,GAAG,CAAC,OAAiC,EAAE,OAAqB,EAAK,EAAE,CAClF,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,2BAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;CAC7E;AAXD,8CAWC","sourcesContent":["import type { ApiPrototype } from '@webpieces/http-client-core';\nimport type { BrowserToken } from './BrowserProviders';\nimport { ClientHttpBrowserFactory } from './ClientHttpBrowserFactory';\nimport { ClientConfig } from './ClientConfig';\n\n/**\n * Structurally compatible Angular factory provider, created by Binder.createRpcClientAndBind. The\n * token remains overrideable and the client is created lazily, on first inject.\n */\nexport class RpcClientProvider<T extends object> {\n readonly deps = [ClientHttpBrowserFactory, ClientConfig];\n\n constructor(\n public readonly provide: BrowserToken,\n private readonly api: ApiPrototype<T>,\n private readonly destination: string,\n ) {}\n\n readonly useFactory = (factory: ClientHttpBrowserFactory, _config: ClientConfig): T =>\n factory.createRpcClient(this.api, new ClientConfig(this.destination));\n}\n"]}
1
+ {"version":3,"file":"RpcClientProvider.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/RpcClientProvider.ts"],"names":[],"mappings":";;;AAEA,yEAAsE;AACtE,iDAA8C;AAE9C;;;GAGG;AACH,MAAa,iBAAiB;IAIN;IACC;IACA;IALZ,IAAI,GAAG,CAAC,mDAAwB,EAAE,2BAAY,CAAC,CAAC;IAEzD,YACoB,OAAqB,EACpB,GAAoB,EACpB,WAAmB;QAFpB,YAAO,GAAP,OAAO,CAAc;QACpB,QAAG,GAAH,GAAG,CAAiB;QACpB,gBAAW,GAAX,WAAW,CAAQ;IACrC,CAAC;IAEJ;;;;OAIG;IACM,UAAU,GAAG,CAAC,OAAiC,EAAE,MAAoB,EAAK,EAAE,CACjF,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,2BAAY,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;CAC1F;AAhBD,8CAgBC","sourcesContent":["import type { ApiPrototype } from '@webpieces/http-client-core';\nimport type { BrowserToken } from './BrowserProviders';\nimport { ClientHttpBrowserFactory } from './ClientHttpBrowserFactory';\nimport { ClientConfig } from './ClientConfig';\n\n/**\n * Structurally compatible Angular factory provider, created by Binder.createRpcClientAndBind. The\n * token remains overrideable and the client is created lazily, on first inject.\n */\nexport class RpcClientProvider<T extends object> {\n readonly deps = [ClientHttpBrowserFactory, ClientConfig];\n\n constructor(\n public readonly provide: BrowserToken,\n private readonly api: ApiPrototype<T>,\n private readonly destination: string,\n ) {}\n\n /**\n * The app's own DI-provided {@link ClientConfig} is where this browser host DECLARED its\n * {@link ClientConfig.role}; every bound client takes the role from it and only the destination\n * from the binding, so the role is stated once, by the app, with no default.\n */\n readonly useFactory = (factory: ClientHttpBrowserFactory, config: ClientConfig): T =>\n factory.createRpcClient(this.api, new ClientConfig(this.destination, config.role));\n}\n"]}
package/src/index.d.ts CHANGED
@@ -17,7 +17,7 @@
17
17
  * const store = new MutableContextStore();
18
18
  * const factory = new ClientHttpBrowserFactory(store);
19
19
  *
20
- * const client = factory.createRpcClient(SaveApi, new ClientConfig('save-svc'));
20
+ * const client = factory.createRpcClient(SaveApi, new ClientConfig('save-svc', ClientRole.END_USER_CLIENT));
21
21
  * const response = await client.save({ query: 'test' });
22
22
  * ```
23
23
  *
@@ -33,7 +33,7 @@ export { ProxyClient, ClientErrorTranslator, RequestOutcome } from '@webpieces/h
33
33
  export type { ApiPrototype } from '@webpieces/http-client-core';
34
34
  export { StreamingCapabilityError } from '@webpieces/http-client-core';
35
35
  export { ContextMgr } from '@webpieces/core-util';
36
- export { ContextReader, ContextWriter, ContextStore, ContextKey, HeaderRegistry, ClientRegistry, templateDeriver, WebpiecesCoreHeaders, } from '@webpieces/core-util';
36
+ export { ContextReader, ContextWriter, ContextStore, ContextKey, HeaderRegistry, ClientRegistry, ClientRole, templateDeriver, WebpiecesCoreHeaders, } from '@webpieces/core-util';
37
37
  export type { ServiceUrlDeriver } from '@webpieces/core-util';
38
38
  export { ApiPath, Endpoint, PathParam, QueryParam, WpAuthPublic, WpAuth, WpAuthorization, AuthorizationType, WpLocalOnly, jwt, oidc, sharedSecret, webhook, apiKey, Rpc, PubSub, Queue, ValidateImplementation, } from '@webpieces/core-util';
39
39
  export type { Wiring, AppWiring, BindModule } from './Wiring';
package/src/index.js CHANGED
@@ -18,14 +18,14 @@
18
18
  * const store = new MutableContextStore();
19
19
  * const factory = new ClientHttpBrowserFactory(store);
20
20
  *
21
- * const client = factory.createRpcClient(SaveApi, new ClientConfig('save-svc'));
21
+ * const client = factory.createRpcClient(SaveApi, new ClientConfig('save-svc', ClientRole.END_USER_CLIENT));
22
22
  * const response = await client.save({ query: 'test' });
23
23
  * ```
24
24
  *
25
25
  * The server twin is @webpieces/http-client-node.
26
26
  */
27
27
  Object.defineProperty(exports, "__esModule", { value: true });
28
- exports.WiringPolicy = exports.BrowserFactoryProvider = exports.BrowserExistingProvider = exports.BrowserClassProvider = exports.BrowserValueProvider = exports.UseExisting = exports.UseClass = exports.ExternalImpl = exports.BrowserWiringProviders = exports.ClientBindOptions = exports.Binder = exports.Queue = exports.PubSub = exports.Rpc = exports.apiKey = exports.webhook = exports.sharedSecret = exports.oidc = exports.jwt = exports.WpLocalOnly = exports.AuthorizationType = exports.WpAuthorization = exports.WpAuth = exports.WpAuthPublic = exports.QueryParam = exports.PathParam = exports.Endpoint = exports.ApiPath = exports.WebpiecesCoreHeaders = exports.templateDeriver = exports.ClientRegistry = exports.HeaderRegistry = exports.ContextKey = exports.ContextMgr = exports.StreamingCapabilityError = exports.RequestOutcome = exports.ClientErrorTranslator = exports.ProxyClient = exports.MutableContextStore = exports.ClientConfig = exports.BrowserApiCallContext = exports.BrowserProxyClient = exports.ClientHttpBrowserFactory = void 0;
28
+ exports.WiringPolicy = exports.BrowserFactoryProvider = exports.BrowserExistingProvider = exports.BrowserClassProvider = exports.BrowserValueProvider = exports.UseExisting = exports.UseClass = exports.ExternalImpl = exports.BrowserWiringProviders = exports.ClientBindOptions = exports.Binder = exports.Queue = exports.PubSub = exports.Rpc = exports.apiKey = exports.webhook = exports.sharedSecret = exports.oidc = exports.jwt = exports.WpLocalOnly = exports.AuthorizationType = exports.WpAuthorization = exports.WpAuth = exports.WpAuthPublic = exports.QueryParam = exports.PathParam = exports.Endpoint = exports.ApiPath = exports.WebpiecesCoreHeaders = exports.templateDeriver = exports.ClientRole = exports.ClientRegistry = exports.HeaderRegistry = exports.ContextKey = exports.ContextMgr = exports.StreamingCapabilityError = exports.RequestOutcome = exports.ClientErrorTranslator = exports.ProxyClient = exports.MutableContextStore = exports.ClientConfig = exports.BrowserApiCallContext = exports.BrowserProxyClient = exports.ClientHttpBrowserFactory = void 0;
29
29
  var ClientHttpBrowserFactory_1 = require("./ClientHttpBrowserFactory");
30
30
  Object.defineProperty(exports, "ClientHttpBrowserFactory", { enumerable: true, get: function () { return ClientHttpBrowserFactory_1.ClientHttpBrowserFactory; } });
31
31
  var BrowserProxyClient_1 = require("./BrowserProxyClient");
@@ -55,6 +55,7 @@ var core_util_2 = require("@webpieces/core-util");
55
55
  Object.defineProperty(exports, "ContextKey", { enumerable: true, get: function () { return core_util_2.ContextKey; } });
56
56
  Object.defineProperty(exports, "HeaderRegistry", { enumerable: true, get: function () { return core_util_2.HeaderRegistry; } });
57
57
  Object.defineProperty(exports, "ClientRegistry", { enumerable: true, get: function () { return core_util_2.ClientRegistry; } });
58
+ Object.defineProperty(exports, "ClientRole", { enumerable: true, get: function () { return core_util_2.ClientRole; } });
58
59
  Object.defineProperty(exports, "templateDeriver", { enumerable: true, get: function () { return core_util_2.templateDeriver; } });
59
60
  Object.defineProperty(exports, "WebpiecesCoreHeaders", { enumerable: true, get: function () { return core_util_2.WebpiecesCoreHeaders; } });
60
61
  // Re-export API decorators for convenience (same as http-routing does)
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;;;AAEH,uEAAsE;AAA7D,oIAAA,wBAAwB,OAAA;AACjC,2DAA0D;AAAjD,wHAAA,kBAAkB,OAAA;AAC3B,4FAA4F;AAC5F,kGAAkG;AAClG,8BAA8B;AAC9B,iEAAgE;AAAvD,8HAAA,qBAAqB,OAAA;AAC9B,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AACrB,6DAA4D;AAAnD,0HAAA,mBAAmB,OAAA;AAG5B,wEAAwE;AACxE,gEAAiG;AAAxF,+GAAA,WAAW,OAAA;AAAE,yHAAA,qBAAqB,OAAA;AAAE,kHAAA,cAAc,OAAA;AAE3D,gEAAuE;AAA9D,4HAAA,wBAAwB,OAAA;AAEjC,iGAAiG;AACjG,mGAAmG;AACnG,kDAAkD;AAAzC,uGAAA,UAAU,OAAA;AAEnB,yFAAyF;AACzF,kDAS8B;AAL1B,uGAAA,UAAU,OAAA;AACV,2GAAA,cAAc,OAAA;AACd,2GAAA,cAAc,OAAA;AACd,4GAAA,eAAe,OAAA;AACf,iHAAA,oBAAoB,OAAA;AAIxB,uEAAuE;AACvE,kDAmB8B;AAlB1B,oGAAA,OAAO,OAAA;AACP,qGAAA,QAAQ,OAAA;AACR,sGAAA,SAAS,OAAA;AACT,uGAAA,UAAU,OAAA;AACV,yGAAA,YAAY,OAAA;AACZ,mGAAA,MAAM,OAAA;AACN,4GAAA,eAAe,OAAA;AACf,8GAAA,iBAAiB,OAAA;AACjB,wGAAA,WAAW,OAAA;AACX,gGAAA,GAAG,OAAA;AACH,iGAAA,IAAI,OAAA;AACJ,yGAAA,YAAY,OAAA;AACZ,oGAAA,OAAO,OAAA;AACP,mGAAA,MAAM,OAAA;AACN,gGAAA,GAAG,OAAA;AACH,mGAAA,MAAM,OAAA;AACN,kGAAA,KAAK,OAAA;AAOT,mCAA6E;AAApE,gGAAA,MAAM,OAAA;AAAE,2GAAA,iBAAiB,OAAA;AAAE,gHAAA,sBAAsB,OAAA;AAC1D,+CAAqE;AAA5D,4GAAA,YAAY,OAAA;AAAE,wGAAA,QAAQ,OAAA;AAAE,2GAAA,WAAW,OAAA;AAE5C,uDAAiI;AAAxH,wHAAA,oBAAoB,OAAA;AAAE,wHAAA,oBAAoB,OAAA;AAAE,2HAAA,uBAAuB,OAAA;AAAE,0HAAA,sBAAsB,OAAA;AAGpG,gEAA2D;AAAlD,gHAAA,YAAY,OAAA","sourcesContent":["/**\n * @webpieces/http-client-browser\n *\n * The BROWSER HTTP client. Reads an API contract's decorators and generates type-safe HTTP\n * clients from it — the same contract the server implements.\n *\n * DI-free on purpose: this may be bundled by React or Angular, so it ships no inversify and no\n * @webpieces/core-context (which is AsyncLocalStorage-backed and Node-only). Browsers have no\n * ambient request scope, so the app holds a {@link MutableContextStore} and sets context values\n * as they become known; every outbound call then transfers them as headers.\n *\n * Usage:\n * ```typescript\n * import { ClientHttpBrowserFactory, ClientConfig, MutableContextStore } from '@webpieces/http-client-browser';\n *\n * HeaderRegistry.configure(CompanyHeaders.ALL_HEADERS, true);\n * const store = new MutableContextStore();\n * const factory = new ClientHttpBrowserFactory(store);\n *\n * const client = factory.createRpcClient(SaveApi, new ClientConfig('save-svc'));\n * const response = await client.save({ query: 'test' });\n * ```\n *\n * The server twin is @webpieces/http-client-node.\n */\n\nexport { ClientHttpBrowserFactory } from './ClientHttpBrowserFactory';\nexport { BrowserProxyClient } from './BrowserProxyClient';\n// Browser impl of the ApiCallContext seam, constructed by BrowserProxyClient. Exported so a\n// browser/Angular logger can read the current tag via the STATIC BrowserApiCallContext.snapshot()\n// and fold it into log lines.\nexport { BrowserApiCallContext } from './BrowserApiCallContext';\nexport { ClientConfig } from './ClientConfig';\nexport { MutableContextStore } from './MutableContextStore';\nexport type { RequestLifecycleListener } from './RequestLifecycleListener';\n\n// The isomorphic engine, re-exported so a browser app needs one import.\nexport { ProxyClient, ClientErrorTranslator, RequestOutcome } from '@webpieces/http-client-core';\nexport type { ApiPrototype } from '@webpieces/http-client-core';\nexport { StreamingCapabilityError } from '@webpieces/http-client-core';\n\n// ContextMgr is the BROWSER's outbound-header propagation. This is the only package that may use\n// it; the server reads RequestContext directly (RequestContextHeaders in @webpieces/core-context).\nexport { ContextMgr } from '@webpieces/core-util';\n\n// Re-export the context-key contract from core-util for convenience (browser one-import)\nexport {\n ContextReader,\n ContextWriter,\n ContextStore,\n ContextKey,\n HeaderRegistry,\n ClientRegistry,\n templateDeriver,\n WebpiecesCoreHeaders,\n} from '@webpieces/core-util';\nexport type { ServiceUrlDeriver } from '@webpieces/core-util';\n\n// Re-export API decorators for convenience (same as http-routing does)\nexport {\n ApiPath,\n Endpoint,\n PathParam,\n QueryParam,\n WpAuthPublic,\n WpAuth,\n WpAuthorization,\n AuthorizationType,\n WpLocalOnly,\n jwt,\n oidc,\n sharedSecret,\n webhook,\n apiKey,\n Rpc,\n PubSub,\n Queue,\n ValidateImplementation,\n} from '@webpieces/core-util';\n\n// The browser host's wiring: bind modules only (no route channel), the Binder they configure, and\n// the ExternalImpl recipes (UseClass / UseExisting) Binder.bindExternal takes.\nexport type { Wiring, AppWiring, BindModule } from './Wiring';\nexport { Binder, ClientBindOptions, BrowserWiringProviders } from './Wiring';\nexport { ExternalImpl, UseClass, UseExisting } from './ExternalImpl';\nexport type { ExternalClassType, ExternalExistingType } from './ExternalImpl';\nexport { BrowserValueProvider, BrowserClassProvider, BrowserExistingProvider, BrowserFactoryProvider } from './BrowserProviders';\nexport type { BrowserProvider, BrowserToken, BrowserType } from './BrowserProviders';\n\nexport { WiringPolicy } from '@webpieces/http-client-core';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;;;AAEH,uEAAsE;AAA7D,oIAAA,wBAAwB,OAAA;AACjC,2DAA0D;AAAjD,wHAAA,kBAAkB,OAAA;AAC3B,4FAA4F;AAC5F,kGAAkG;AAClG,8BAA8B;AAC9B,iEAAgE;AAAvD,8HAAA,qBAAqB,OAAA;AAC9B,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AACrB,6DAA4D;AAAnD,0HAAA,mBAAmB,OAAA;AAG5B,wEAAwE;AACxE,gEAAiG;AAAxF,+GAAA,WAAW,OAAA;AAAE,yHAAA,qBAAqB,OAAA;AAAE,kHAAA,cAAc,OAAA;AAE3D,gEAAuE;AAA9D,4HAAA,wBAAwB,OAAA;AAEjC,iGAAiG;AACjG,mGAAmG;AACnG,kDAAkD;AAAzC,uGAAA,UAAU,OAAA;AAEnB,yFAAyF;AACzF,kDAU8B;AAN1B,uGAAA,UAAU,OAAA;AACV,2GAAA,cAAc,OAAA;AACd,2GAAA,cAAc,OAAA;AACd,uGAAA,UAAU,OAAA;AACV,4GAAA,eAAe,OAAA;AACf,iHAAA,oBAAoB,OAAA;AAIxB,uEAAuE;AACvE,kDAmB8B;AAlB1B,oGAAA,OAAO,OAAA;AACP,qGAAA,QAAQ,OAAA;AACR,sGAAA,SAAS,OAAA;AACT,uGAAA,UAAU,OAAA;AACV,yGAAA,YAAY,OAAA;AACZ,mGAAA,MAAM,OAAA;AACN,4GAAA,eAAe,OAAA;AACf,8GAAA,iBAAiB,OAAA;AACjB,wGAAA,WAAW,OAAA;AACX,gGAAA,GAAG,OAAA;AACH,iGAAA,IAAI,OAAA;AACJ,yGAAA,YAAY,OAAA;AACZ,oGAAA,OAAO,OAAA;AACP,mGAAA,MAAM,OAAA;AACN,gGAAA,GAAG,OAAA;AACH,mGAAA,MAAM,OAAA;AACN,kGAAA,KAAK,OAAA;AAOT,mCAA6E;AAApE,gGAAA,MAAM,OAAA;AAAE,2GAAA,iBAAiB,OAAA;AAAE,gHAAA,sBAAsB,OAAA;AAC1D,+CAAqE;AAA5D,4GAAA,YAAY,OAAA;AAAE,wGAAA,QAAQ,OAAA;AAAE,2GAAA,WAAW,OAAA;AAE5C,uDAAiI;AAAxH,wHAAA,oBAAoB,OAAA;AAAE,wHAAA,oBAAoB,OAAA;AAAE,2HAAA,uBAAuB,OAAA;AAAE,0HAAA,sBAAsB,OAAA;AAGpG,gEAA2D;AAAlD,gHAAA,YAAY,OAAA","sourcesContent":["/**\n * @webpieces/http-client-browser\n *\n * The BROWSER HTTP client. Reads an API contract's decorators and generates type-safe HTTP\n * clients from it — the same contract the server implements.\n *\n * DI-free on purpose: this may be bundled by React or Angular, so it ships no inversify and no\n * @webpieces/core-context (which is AsyncLocalStorage-backed and Node-only). Browsers have no\n * ambient request scope, so the app holds a {@link MutableContextStore} and sets context values\n * as they become known; every outbound call then transfers them as headers.\n *\n * Usage:\n * ```typescript\n * import { ClientHttpBrowserFactory, ClientConfig, MutableContextStore } from '@webpieces/http-client-browser';\n *\n * HeaderRegistry.configure(CompanyHeaders.ALL_HEADERS, true);\n * const store = new MutableContextStore();\n * const factory = new ClientHttpBrowserFactory(store);\n *\n * const client = factory.createRpcClient(SaveApi, new ClientConfig('save-svc', ClientRole.END_USER_CLIENT));\n * const response = await client.save({ query: 'test' });\n * ```\n *\n * The server twin is @webpieces/http-client-node.\n */\n\nexport { ClientHttpBrowserFactory } from './ClientHttpBrowserFactory';\nexport { BrowserProxyClient } from './BrowserProxyClient';\n// Browser impl of the ApiCallContext seam, constructed by BrowserProxyClient. Exported so a\n// browser/Angular logger can read the current tag via the STATIC BrowserApiCallContext.snapshot()\n// and fold it into log lines.\nexport { BrowserApiCallContext } from './BrowserApiCallContext';\nexport { ClientConfig } from './ClientConfig';\nexport { MutableContextStore } from './MutableContextStore';\nexport type { RequestLifecycleListener } from './RequestLifecycleListener';\n\n// The isomorphic engine, re-exported so a browser app needs one import.\nexport { ProxyClient, ClientErrorTranslator, RequestOutcome } from '@webpieces/http-client-core';\nexport type { ApiPrototype } from '@webpieces/http-client-core';\nexport { StreamingCapabilityError } from '@webpieces/http-client-core';\n\n// ContextMgr is the BROWSER's outbound-header propagation. This is the only package that may use\n// it; the server reads RequestContext directly (RequestContextHeaders in @webpieces/core-context).\nexport { ContextMgr } from '@webpieces/core-util';\n\n// Re-export the context-key contract from core-util for convenience (browser one-import)\nexport {\n ContextReader,\n ContextWriter,\n ContextStore,\n ContextKey,\n HeaderRegistry,\n ClientRegistry,\n ClientRole,\n templateDeriver,\n WebpiecesCoreHeaders,\n} from '@webpieces/core-util';\nexport type { ServiceUrlDeriver } from '@webpieces/core-util';\n\n// Re-export API decorators for convenience (same as http-routing does)\nexport {\n ApiPath,\n Endpoint,\n PathParam,\n QueryParam,\n WpAuthPublic,\n WpAuth,\n WpAuthorization,\n AuthorizationType,\n WpLocalOnly,\n jwt,\n oidc,\n sharedSecret,\n webhook,\n apiKey,\n Rpc,\n PubSub,\n Queue,\n ValidateImplementation,\n} from '@webpieces/core-util';\n\n// The browser host's wiring: bind modules only (no route channel), the Binder they configure, and\n// the ExternalImpl recipes (UseClass / UseExisting) Binder.bindExternal takes.\nexport type { Wiring, AppWiring, BindModule } from './Wiring';\nexport { Binder, ClientBindOptions, BrowserWiringProviders } from './Wiring';\nexport { ExternalImpl, UseClass, UseExisting } from './ExternalImpl';\nexport type { ExternalClassType, ExternalExistingType } from './ExternalImpl';\nexport { BrowserValueProvider, BrowserClassProvider, BrowserExistingProvider, BrowserFactoryProvider } from './BrowserProviders';\nexport type { BrowserProvider, BrowserToken, BrowserType } from './BrowserProviders';\n\nexport { WiringPolicy } from '@webpieces/http-client-core';\n"]}