@webpieces/http-client-browser 0.4.710 → 0.4.711

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/http-client-browser",
3
- "version": "0.4.710",
3
+ "version": "0.4.711",
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.710",
26
- "@webpieces/http-client-core": "0.4.710"
25
+ "@webpieces/core-util": "0.4.711",
26
+ "@webpieces/http-client-core": "0.4.711"
27
27
  }
28
28
  }
@@ -1,10 +1,10 @@
1
1
  import { ApiCallContext, AnyContextKey } from '@webpieces/core-util';
2
2
  /**
3
3
  * BrowserApiCallContext - the BROWSER implementation of the {@link ApiCallContext} seam, so
4
- * {@link LogApiCall} (shared, browser-safe core-util) stamps its structured `api` tag in the browser
5
- * too. Installed by {@link ClientHttpBrowserFactory}.
4
+ * {@link LogApiCallImpl} (shared, browser-safe core-util) stamps its structured `api` tag in the browser
5
+ * too. Constructed by {@link BrowserProxyClient}, which hands it to its LogApiCallImpl.
6
6
  *
7
- * WHY a module-level global is SAFE here (no AsyncLocalStorage / Zone.js): {@link LogApiCall} stamps as
7
+ * WHY a module-level global is SAFE here (no AsyncLocalStorage / Zone.js): {@link LogApiCallImpl} stamps as
8
8
  * `set → log → remove` in ONE synchronous span, so the slot is only ever populated while the logger reads
9
9
  * it and is cleared before the `await fetch`. A browser is single-threaded, so nothing can interleave
10
10
  * between set and remove — a concurrent in-flight call can never clobber the slot. (Because of that, only
@@ -3,10 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BrowserApiCallContext = void 0;
4
4
  /**
5
5
  * BrowserApiCallContext - the BROWSER implementation of the {@link ApiCallContext} seam, so
6
- * {@link LogApiCall} (shared, browser-safe core-util) stamps its structured `api` tag in the browser
7
- * too. Installed by {@link ClientHttpBrowserFactory}.
6
+ * {@link LogApiCallImpl} (shared, browser-safe core-util) stamps its structured `api` tag in the browser
7
+ * too. Constructed by {@link BrowserProxyClient}, which hands it to its LogApiCallImpl.
8
8
  *
9
- * WHY a module-level global is SAFE here (no AsyncLocalStorage / Zone.js): {@link LogApiCall} stamps as
9
+ * WHY a module-level global is SAFE here (no AsyncLocalStorage / Zone.js): {@link LogApiCallImpl} stamps as
10
10
  * `set → log → remove` in ONE synchronous span, so the slot is only ever populated while the logger reads
11
11
  * it and is cleared before the `await fetch`. A browser is single-threaded, so nothing can interleave
12
12
  * between set and remove — a concurrent in-flight call can never clobber the slot. (Because of that, only
@@ -17,8 +17,9 @@ exports.BrowserApiCallContext = void 0;
17
17
  * log emit (inside the set → remove span) and folds `api.{side,type,result,...}` into that line.
18
18
  */
19
19
  class BrowserApiCallContext {
20
- // Shared across every instance (a browser app may build the factory more than once) so the value a
21
- // logger reads is the same one LogApiCall stamped, whichever instance the holder currently holds.
20
+ // STATIC on purpose, and it must stay that way: every BrowserProxyClient builds its own
21
+ // BrowserApiCallContext, while a browser logger reads the tag back through the STATIC
22
+ // snapshot() below. A per-instance store would silently stop tagging browser log lines.
22
23
  // webpieces-disable no-any-unknown -- context values are heterogeneous (the api struct; future keys)
23
24
  static store = new Map();
24
25
  /** Always active in the browser — there is always the single global slot per key to stamp into. */
@@ -36,7 +37,7 @@ class BrowserApiCallContext {
36
37
  * The current stamped values, keyed by ContextKey name — for a browser logger to fold into each line.
37
38
  */
38
39
  // webpieces-disable no-any-unknown -- context values are heterogeneous (the api struct; future keys)
39
- // webpieces-disable no-function-outside-class -- static read accessor of the module-global browser slot (like ApiCallContextHolder), read by a browser logger, not DI-injected
40
+ // webpieces-disable no-function-outside-class -- static read accessor of the module-global browser slot, read by a browser logger, not DI-injected
40
41
  static snapshot() {
41
42
  return BrowserApiCallContext.store;
42
43
  }
@@ -1 +1 @@
1
- {"version":3,"file":"BrowserApiCallContext.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/BrowserApiCallContext.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;GAcG;AACH,MAAa,qBAAqB;IAC9B,mGAAmG;IACnG,kGAAkG;IAClG,qGAAqG;IAC7F,MAAM,CAAU,KAAK,GAAG,IAAI,GAAG,EAAmB,CAAC;IAE3D,mGAAmG;IACnG,QAAQ;QACJ,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,gHAAgH;IAChH,GAAG,CAAC,UAAyB,EAAE,KAAc;QACzC,qBAAqB,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,CAAC,UAAyB;QAC5B,qBAAqB,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACxD,CAAC;IAED;;OAEG;IACH,qGAAqG;IACrG,+KAA+K;IAC/K,MAAM,CAAC,QAAQ;QACX,OAAO,qBAAqB,CAAC,KAAK,CAAC;IACvC,CAAC;;AA3BL,sDA4BC","sourcesContent":["import { ApiCallContext, AnyContextKey } from '@webpieces/core-util';\n\n/**\n * BrowserApiCallContext - the BROWSER implementation of the {@link ApiCallContext} seam, so\n * {@link LogApiCall} (shared, browser-safe core-util) stamps its structured `api` tag in the browser\n * too. Installed by {@link ClientHttpBrowserFactory}.\n *\n * WHY a module-level global is SAFE here (no AsyncLocalStorage / Zone.js): {@link LogApiCall} stamps as\n * `set → log → remove` in ONE synchronous span, so the slot is only ever populated while the logger reads\n * it and is cleared before the `await fetch`. A browser is single-threaded, so nothing can interleave\n * between set and remove — a concurrent in-flight call can never clobber the slot. (Because of that, only\n * the `[API-*]` req/resp lines carry `api`, not lines emitted mid-fetch — exactly the filter surface.)\n *\n * READ side: the object-valued `api` tag cannot ride the string-only MutableContextStore, so a\n * downstream browser/Angular logger reads it here via {@link BrowserApiCallContext.snapshot} DURING its\n * log emit (inside the set → remove span) and folds `api.{side,type,result,...}` into that line.\n */\nexport class BrowserApiCallContext implements ApiCallContext {\n // Shared across every instance (a browser app may build the factory more than once) so the value a\n // logger reads is the same one LogApiCall stamped, whichever instance the holder currently holds.\n // webpieces-disable no-any-unknown -- context values are heterogeneous (the api struct; future keys)\n private static readonly store = new Map<string, unknown>();\n\n /** Always active in the browser — there is always the single global slot per key to stamp into. */\n isActive(): boolean {\n return true;\n }\n\n // webpieces-disable no-any-unknown -- a context value is heterogeneous (the api struct here; strings elsewhere)\n set(contextKey: AnyContextKey, value: unknown): void {\n BrowserApiCallContext.store.set(contextKey.name, value);\n }\n\n remove(contextKey: AnyContextKey): void {\n BrowserApiCallContext.store.delete(contextKey.name);\n }\n\n /**\n * The current stamped values, keyed by ContextKey name — for a browser logger to fold into each line.\n */\n // webpieces-disable no-any-unknown -- context values are heterogeneous (the api struct; future keys)\n // webpieces-disable no-function-outside-class -- static read accessor of the module-global browser slot (like ApiCallContextHolder), read by a browser logger, not DI-injected\n static snapshot(): ReadonlyMap<string, unknown> {\n return BrowserApiCallContext.store;\n }\n}\n"]}
1
+ {"version":3,"file":"BrowserApiCallContext.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/BrowserApiCallContext.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;;;GAcG;AACH,MAAa,qBAAqB;IAC9B,wFAAwF;IACxF,sFAAsF;IACtF,wFAAwF;IACxF,qGAAqG;IAC7F,MAAM,CAAU,KAAK,GAAG,IAAI,GAAG,EAAmB,CAAC;IAE3D,mGAAmG;IACnG,QAAQ;QACJ,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,gHAAgH;IAChH,GAAG,CAAC,UAAyB,EAAE,KAAc;QACzC,qBAAqB,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,CAAC,UAAyB;QAC5B,qBAAqB,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACxD,CAAC;IAED;;OAEG;IACH,qGAAqG;IACrG,mJAAmJ;IACnJ,MAAM,CAAC,QAAQ;QACX,OAAO,qBAAqB,CAAC,KAAK,CAAC;IACvC,CAAC;;AA5BL,sDA6BC","sourcesContent":["import { ApiCallContext, AnyContextKey } from '@webpieces/core-util';\n\n/**\n * BrowserApiCallContext - the BROWSER implementation of the {@link ApiCallContext} seam, so\n * {@link LogApiCallImpl} (shared, browser-safe core-util) stamps its structured `api` tag in the browser\n * too. Constructed by {@link BrowserProxyClient}, which hands it to its LogApiCallImpl.\n *\n * WHY a module-level global is SAFE here (no AsyncLocalStorage / Zone.js): {@link LogApiCallImpl} stamps as\n * `set → log → remove` in ONE synchronous span, so the slot is only ever populated while the logger reads\n * it and is cleared before the `await fetch`. A browser is single-threaded, so nothing can interleave\n * between set and remove — a concurrent in-flight call can never clobber the slot. (Because of that, only\n * the `[API-*]` req/resp lines carry `api`, not lines emitted mid-fetch — exactly the filter surface.)\n *\n * READ side: the object-valued `api` tag cannot ride the string-only MutableContextStore, so a\n * downstream browser/Angular logger reads it here via {@link BrowserApiCallContext.snapshot} DURING its\n * log emit (inside the set → remove span) and folds `api.{side,type,result,...}` into that line.\n */\nexport class BrowserApiCallContext implements ApiCallContext {\n // STATIC on purpose, and it must stay that way: every BrowserProxyClient builds its own\n // BrowserApiCallContext, while a browser logger reads the tag back through the STATIC\n // snapshot() below. A per-instance store would silently stop tagging browser log lines.\n // webpieces-disable no-any-unknown -- context values are heterogeneous (the api struct; future keys)\n private static readonly store = new Map<string, unknown>();\n\n /** Always active in the browser — there is always the single global slot per key to stamp into. */\n isActive(): boolean {\n return true;\n }\n\n // webpieces-disable no-any-unknown -- a context value is heterogeneous (the api struct here; strings elsewhere)\n set(contextKey: AnyContextKey, value: unknown): void {\n BrowserApiCallContext.store.set(contextKey.name, value);\n }\n\n remove(contextKey: AnyContextKey): void {\n BrowserApiCallContext.store.delete(contextKey.name);\n }\n\n /**\n * The current stamped values, keyed by ContextKey name — for a browser logger to fold into each line.\n */\n // webpieces-disable no-any-unknown -- context values are heterogeneous (the api struct; future keys)\n // webpieces-disable no-function-outside-class -- static read accessor of the module-global browser slot, read by a browser logger, not DI-injected\n static snapshot(): ReadonlyMap<string, unknown> {\n return BrowserApiCallContext.store;\n }\n}\n"]}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BrowserProxyClient = void 0;
4
4
  const core_util_1 = require("@webpieces/core-util");
5
+ const BrowserApiCallContext_1 = require("./BrowserApiCallContext");
5
6
  const http_client_core_1 = require("@webpieces/http-client-core");
6
7
  /**
7
8
  * The browser {@link ProxyClient}. Reads context from the app-held store (via {@link ContextMgr}),
@@ -18,7 +19,10 @@ class BrowserProxyClient extends http_client_core_1.ProxyClient {
18
19
  lifecycleListener;
19
20
  config;
20
21
  constructor(contextMgr, lifecycleListener) {
21
- super();
22
+ // The browser's ApiCallContext, constructed here rather than installed at startup: its store
23
+ // is static, so every instance stamps into the one slot BrowserApiCallContext.snapshot()
24
+ // reads — which is how a browser logger folds the `api` tag into each line.
25
+ super(new core_util_1.LogApiCallImpl(new BrowserApiCallContext_1.BrowserApiCallContext()));
22
26
  this.contextMgr = contextMgr;
23
27
  this.lifecycleListener = lifecycleListener;
24
28
  }
@@ -1 +1 @@
1
- {"version":3,"file":"BrowserProxyClient.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/BrowserProxyClient.ts"],"names":[],"mappings":";;;AAAA,oDAA6G;AAC7G,kEAA2G;AAI3G;;;;;;;;;GASG;AACH,MAAa,kBAAmB,SAAQ,8BAAW;IAI1B;IACA;IAJb,MAAM,CAAgB;IAE9B,YACqB,UAAsB,EACtB,iBAA4C;QAE7D,KAAK,EAAE,CAAC;QAHS,eAAU,GAAV,UAAU,CAAY;QACtB,sBAAiB,GAAjB,iBAAiB,CAA2B;IAGjE,CAAC;IAED,uDAAuD;IACvD,IAAI,CAAC,YAAkC,EAAE,MAAoB;QACzD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,uFAAuF;QACvF,mFAAmF;QACnF,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IACtC,CAAC;IAED;;;;;;;;;;OAUG;IACgB,KAAK,CAAC,cAAc;QACnC,OAAO,CAAC,MAAM,0BAAc,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IACxE,CAAC;IAED;;;;;;OAMG;IACgB,sBAAsB,CAAC,WAA6B;QACnE,OAAO,IAAI,CAAC,UAAU,CAAC,oBAAoB,CAAC,WAAW,CAAC,CAAC;IAC7D,CAAC;IAED;;;OAGG;IACgB,cAAc,CAAC,KAAoB;QAClD,IAAI,CAAC,iBAAiB,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC;IAEkB,YAAY,CAAC,KAAoB,EAAE,OAAuB;QACzE,IAAI,CAAC,iBAAiB,EAAE,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACzD,CAAC;IAED;;;;;;;;;;;OAWG;IACgB,sBAAsB,CAAC,OAA0B,EAAE,OAAe;QACjF,OAAO,OAAO,CAAC,KAAK,CAAC;IACzB,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACgB,uBAAuB,CAAC,QAA8B,EAAE,UAAkB;QACzF,MAAM,IAAI,GAAG,QAAQ,EAAE,IAAI,CAAC;QAC5B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACrB,OAAO;QACX,CAAC;QACD,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;YAChB,KAAK,QAAQ,CAAC;YACd,KAAK,KAAK,CAAC;YACX,KAAK,YAAY;gBACb,OAAO;YACX,KAAK,MAAM,CAAC;YACZ,KAAK,eAAe;gBAChB,MAAM,IAAI,KAAK,CACX,YAAY,UAAU,QAAQ,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,kBAAkB,eAAe;oBACnG,mFAAmF;oBACnF,8BAA8B,CACjC,CAAC;YACN,oFAAoF;YACpF,oFAAoF;YACpF,qFAAqF;YACrF,kDAAkD;YAClD,KAAK,SAAS;gBACV,MAAM,IAAI,KAAK,CACX,YAAY,UAAU,qBAAqB,IAAI,CAAC,IAAI,aAAa,IAAI,CAAC,IAAI,gBAAgB;oBAC1F,gBAAgB,IAAI,CAAC,IAAI,yEAAyE;oBAClG,mCAAmC,CACtC,CAAC;YACN,uFAAuF;YACvF,yFAAyF;YACzF,mFAAmF;YACnF,KAAK,QAAQ;gBACT,MAAM,IAAI,KAAK,CACX,YAAY,UAAU,oBAAoB,IAAI,CAAC,MAAM,yCAAyC;oBAC9F,+FAA+F;oBAC/F,+FAA+F,CAClG,CAAC;QACV,CAAC;IACL,CAAC;CACJ;AA9HD,gDA8HC","sourcesContent":["import { AuthMeta, ContextMgr, ClientRegistry, DestinationTrust, RouteMetadata } from '@webpieces/core-util';\nimport { ApiPrototype, ProxyClient, RequestOutcome, TranslatedFailure } 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 private config!: ClientConfig;\n\n constructor(\n private readonly contextMgr: ContextMgr,\n private readonly lifecycleListener?: RequestLifecycleListener,\n ) {\n super();\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 /**\n * `destination` is always the un-verifying kind here — {@link assertEndpointSupported} below\n * refuses to bind an @AuthOidc / @AuthSharedSecret contract, so every browser destination is\n * @AuthJwt or @Public — 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 * 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 * Rethrow EXACTLY the exception the translator picked, unchanged.\n *\n * This is the browser half of the asymmetry documented on {@link ProxyClient.adaptDownstreamFailure}.\n * Here the client IS the end user's agent and the \"downstream\" is the app's own backend, so a 404\n * really does mean \"that thing does not exist\", a 401 really does mean \"sign in again\", and a 403\n * really does mean \"you may not\". Rewriting any of those to a 500 would delete the only signal the\n * UI has to act on.\n *\n * The server twin (`NodeProxyClient`) does the opposite for exactly the same reason: there the\n * downstream is a dependency, not the user's answer.\n */\n protected override adaptDownstreamFailure(failure: TranslatedFailure, _callId: string): Error {\n return failure.error;\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: @AuthOidc needs a runtime\n * service account to mint a token, @AuthSharedSecret needs a secret no browser may ship.\n *\n * @AuthLocalOnly 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(authMeta: AuthMeta | undefined, methodName: string): void {\n const mode = authMeta?.mode;\n if (mode === undefined) {\n return;\n }\n switch (mode.kind) {\n case 'public':\n case 'jwt':\n case 'local-only':\n return;\n case 'oidc':\n case 'shared-secret':\n throw new Error(\n `Endpoint ${methodName} is @${mode.kind === 'oidc' ? 'AuthOidc' : 'AuthSharedSecret'} — a browser ` +\n `cannot hold service credentials. Call it server-side with ClientHttpFactory from ` +\n `@webpieces/http-client-node.`,\n );\n // @AuthWebhook is not \"a credential a browser lacks\" — it is an endpoint whose ONLY\n // legitimate caller is the outside vendor that signs the request in its own scheme.\n // NOTHING in this repo can call it, browser or server, so it is refused at bind time\n // rather than failing as a 401 on the first call.\n case 'webhook':\n throw new Error(\n `Endpoint ${methodName} is @AuthWebhook('${mode.name}') — only ${mode.name} can call it, ` +\n `because only ${mode.name} can produce the signature its WebhookAuthCallback verifies. It is not ` +\n `callable from a webpieces client.`,\n );\n // @AuthApiKey is the same shape of refusal for a different reason: the credential is a\n // CUSTOMER-held api key whose header names the app's ApiKeyHook chooses, so no webpieces\n // client knows what to send — and a browser must not ship a customer's key at all.\n case 'apikey':\n throw new Error(\n `Endpoint ${methodName} is @AuthApiKey('${mode.regime}') — its credential is a customer-held ` +\n `api key that a browser must never ship, and the header carrying it is the app's ApiKeyHook's ` +\n `choice, so no webpieces client can build the call. Only the partner holding the key calls it.`,\n );\n }\n }\n}\n"]}
1
+ {"version":3,"file":"BrowserProxyClient.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/BrowserProxyClient.ts"],"names":[],"mappings":";;;AAAA,oDAO8B;AAC9B,mEAAgE;AAChE,kEAA2G;AAI3G;;;;;;;;;GASG;AACH,MAAa,kBAAmB,SAAQ,8BAAW;IAI1B;IACA;IAJb,MAAM,CAAgB;IAE9B,YACqB,UAAsB,EACtB,iBAA4C;QAE7D,6FAA6F;QAC7F,yFAAyF;QACzF,4EAA4E;QAC5E,KAAK,CAAC,IAAI,0BAAc,CAAC,IAAI,6CAAqB,EAAE,CAAC,CAAC,CAAC;QANtC,eAAU,GAAV,UAAU,CAAY;QACtB,sBAAiB,GAAjB,iBAAiB,CAA2B;IAMjE,CAAC;IAED,uDAAuD;IACvD,IAAI,CAAC,YAAkC,EAAE,MAAoB;QACzD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,uFAAuF;QACvF,mFAAmF;QACnF,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IACtC,CAAC;IAED;;;;;;;;;;OAUG;IACgB,KAAK,CAAC,cAAc;QACnC,OAAO,CAAC,MAAM,0BAAc,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IACxE,CAAC;IAED;;;;;;OAMG;IACgB,sBAAsB,CAAC,WAA6B;QACnE,OAAO,IAAI,CAAC,UAAU,CAAC,oBAAoB,CAAC,WAAW,CAAC,CAAC;IAC7D,CAAC;IAED;;;OAGG;IACgB,cAAc,CAAC,KAAoB;QAClD,IAAI,CAAC,iBAAiB,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC;IAEkB,YAAY,CAAC,KAAoB,EAAE,OAAuB;QACzE,IAAI,CAAC,iBAAiB,EAAE,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACzD,CAAC;IAED;;;;;;;;;;;OAWG;IACgB,sBAAsB,CAAC,OAA0B,EAAE,OAAe;QACjF,OAAO,OAAO,CAAC,KAAK,CAAC;IACzB,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACgB,uBAAuB,CAAC,QAA8B,EAAE,UAAkB;QACzF,MAAM,IAAI,GAAG,QAAQ,EAAE,IAAI,CAAC;QAC5B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACrB,OAAO;QACX,CAAC;QACD,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;YAChB,KAAK,QAAQ,CAAC;YACd,KAAK,KAAK,CAAC;YACX,KAAK,YAAY;gBACb,OAAO;YACX,KAAK,MAAM,CAAC;YACZ,KAAK,eAAe;gBAChB,MAAM,IAAI,KAAK,CACX,YAAY,UAAU,QAAQ,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,kBAAkB,eAAe;oBACnG,mFAAmF;oBACnF,8BAA8B,CACjC,CAAC;YACN,oFAAoF;YACpF,oFAAoF;YACpF,qFAAqF;YACrF,kDAAkD;YAClD,KAAK,SAAS;gBACV,MAAM,IAAI,KAAK,CACX,YAAY,UAAU,qBAAqB,IAAI,CAAC,IAAI,aAAa,IAAI,CAAC,IAAI,gBAAgB;oBAC1F,gBAAgB,IAAI,CAAC,IAAI,yEAAyE;oBAClG,mCAAmC,CACtC,CAAC;YACN,uFAAuF;YACvF,yFAAyF;YACzF,mFAAmF;YACnF,KAAK,QAAQ;gBACT,MAAM,IAAI,KAAK,CACX,YAAY,UAAU,oBAAoB,IAAI,CAAC,MAAM,yCAAyC;oBAC9F,+FAA+F;oBAC/F,+FAA+F,CAClG,CAAC;QACV,CAAC;IACL,CAAC;CACJ;AAjID,gDAiIC","sourcesContent":["import {\n AuthMeta,\n ContextMgr,\n ClientRegistry,\n DestinationTrust,\n LogApiCallImpl,\n RouteMetadata,\n} from '@webpieces/core-util';\nimport { BrowserApiCallContext } from './BrowserApiCallContext';\nimport { ApiPrototype, ProxyClient, RequestOutcome, TranslatedFailure } 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 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 /**\n * `destination` is always the un-verifying kind here — {@link assertEndpointSupported} below\n * refuses to bind an @AuthOidc / @AuthSharedSecret contract, so every browser destination is\n * @AuthJwt or @Public — 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 * 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 * Rethrow EXACTLY the exception the translator picked, unchanged.\n *\n * This is the browser half of the asymmetry documented on {@link ProxyClient.adaptDownstreamFailure}.\n * Here the client IS the end user's agent and the \"downstream\" is the app's own backend, so a 404\n * really does mean \"that thing does not exist\", a 401 really does mean \"sign in again\", and a 403\n * really does mean \"you may not\". Rewriting any of those to a 500 would delete the only signal the\n * UI has to act on.\n *\n * The server twin (`NodeProxyClient`) does the opposite for exactly the same reason: there the\n * downstream is a dependency, not the user's answer.\n */\n protected override adaptDownstreamFailure(failure: TranslatedFailure, _callId: string): Error {\n return failure.error;\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: @AuthOidc needs a runtime\n * service account to mint a token, @AuthSharedSecret needs a secret no browser may ship.\n *\n * @AuthLocalOnly 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(authMeta: AuthMeta | undefined, methodName: string): void {\n const mode = authMeta?.mode;\n if (mode === undefined) {\n return;\n }\n switch (mode.kind) {\n case 'public':\n case 'jwt':\n case 'local-only':\n return;\n case 'oidc':\n case 'shared-secret':\n throw new Error(\n `Endpoint ${methodName} is @${mode.kind === 'oidc' ? 'AuthOidc' : 'AuthSharedSecret'} — a browser ` +\n `cannot hold service credentials. Call it server-side with ClientHttpFactory from ` +\n `@webpieces/http-client-node.`,\n );\n // @AuthWebhook is not \"a credential a browser lacks\" — it is an endpoint whose ONLY\n // legitimate caller is the outside vendor that signs the request in its own scheme.\n // NOTHING in this repo can call it, browser or server, so it is refused at bind time\n // rather than failing as a 401 on the first call.\n case 'webhook':\n throw new Error(\n `Endpoint ${methodName} is @AuthWebhook('${mode.name}') — only ${mode.name} can call it, ` +\n `because only ${mode.name} can produce the signature its WebhookAuthCallback verifies. It is not ` +\n `callable from a webpieces client.`,\n );\n // @AuthApiKey is the same shape of refusal for a different reason: the credential is a\n // CUSTOMER-held api key whose header names the app's ApiKeyHook chooses, so no webpieces\n // client knows what to send — and a browser must not ship a customer's key at all.\n case 'apikey':\n throw new Error(\n `Endpoint ${methodName} is @AuthApiKey('${mode.regime}') — its credential is a customer-held ` +\n `api key that a browser must never ship, and the header carrying it is the app's ApiKeyHook's ` +\n `choice, so no webpieces client can build the call. Only the partner holding the key calls it.`,\n );\n }\n }\n}\n"]}
@@ -4,7 +4,6 @@ exports.ClientHttpBrowserFactory = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const core_util_1 = require("@webpieces/core-util");
6
6
  const http_client_core_1 = require("@webpieces/http-client-core");
7
- const BrowserApiCallContext_1 = require("./BrowserApiCallContext");
8
7
  const BrowserProxyClient_1 = require("./BrowserProxyClient");
9
8
  const MutableContextStore_1 = require("./MutableContextStore");
10
9
  /**
@@ -41,10 +40,6 @@ let ClientHttpBrowserFactory = class ClientHttpBrowserFactory {
41
40
  constructor(store, lifecycleListener) {
42
41
  this.lifecycleListener = lifecycleListener;
43
42
  this.contextMgr = new core_util_1.ContextMgr(store);
44
- // Bind the BROWSER ApiCallContext so LogApiCall (core-util) stamps the `api` tag into the
45
- // module-global browser slot. Idempotent — building the factory more than once is harmless.
46
- // Without this, the browser keeps NoopApiCallContext and `api` tagging is inert.
47
- core_util_1.ApiCallContextHolder.install(new BrowserApiCallContext_1.BrowserApiCallContext());
48
43
  }
49
44
  /**
50
45
  * Create a type-safe HTTP client for one API contract.
@@ -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,mEAAgE;AAChE,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;QACxC,0FAA0F;QAC1F,4FAA4F;QAC5F,iFAAiF;QACjF,gCAAoB,CAAC,OAAO,CAAC,IAAI,6CAAqB,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED;;;;;OAKG;IACH,eAAe,CAAmB,YAA6B,EAAE,MAAoB;QACjF,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;AAzBY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,0BAAc,GAAE;6CAKF,yCAAmB;GAJrB,wBAAwB,CAyBpC","sourcesContent":["import { ApiCallContextHolder, ContextMgr, DocumentDesign } from '@webpieces/core-util';\nimport { ApiPrototype, buildClientProxy } from '@webpieces/http-client-core';\nimport { BrowserApiCallContext } from './BrowserApiCallContext';\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 @AuthOidc or @AuthSharedSecret\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 // Bind the BROWSER ApiCallContext so LogApiCall (core-util) stamps the `api` tag into the\n // module-global browser slot. Idempotent — building the factory more than once is harmless.\n // Without this, the browser keeps NoopApiCallContext and `api` tagging is inert.\n ApiCallContextHolder.install(new BrowserApiCallContext());\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 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,oDAAkE;AAClE,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,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;AArBY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,0BAAc,GAAE;6CAKF,yCAAmB;GAJrB,wBAAwB,CAqBpC","sourcesContent":["import { 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 @AuthOidc or @AuthSharedSecret\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 const proxyClient = new BrowserProxyClient(this.contextMgr, this.lifecycleListener);\n proxyClient.init(apiPrototype, config);\n return buildClientProxy(apiPrototype, proxyClient);\n }\n}\n"]}
package/src/index.js CHANGED
@@ -30,8 +30,9 @@ 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");
32
32
  Object.defineProperty(exports, "BrowserProxyClient", { enumerable: true, get: function () { return BrowserProxyClient_1.BrowserProxyClient; } });
33
- // Browser impl of the ApiCallContext seam (installed by the factory). Exported so a browser/Angular
34
- // logger can read the current tag via ApiCallContextHolder.get().peek() and fold it into log lines.
33
+ // Browser impl of the ApiCallContext seam, constructed by BrowserProxyClient. Exported so a
34
+ // browser/Angular logger can read the current tag via the STATIC BrowserApiCallContext.snapshot()
35
+ // and fold it into log lines.
35
36
  var BrowserApiCallContext_1 = require("./BrowserApiCallContext");
36
37
  Object.defineProperty(exports, "BrowserApiCallContext", { enumerable: true, get: function () { return BrowserApiCallContext_1.BrowserApiCallContext; } });
37
38
  var ClientConfig_1 = require("./ClientConfig");
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,oGAAoG;AACpG,oGAAoG;AACpG,iEAAgE;AAAvD,8HAAA,qBAAqB,OAAA;AAC9B,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AACrB,6DAA4D;AAAnD,0HAAA,mBAAmB,OAAA;AAG5B,wEAAwE;AACxE,gEAAoH;AAA3G,+GAAA,WAAW,OAAA;AAAE,yHAAA,qBAAqB,OAAA;AAAE,kHAAA,cAAc,OAAA;AAAE,qHAAA,iBAAiB,OAAA;AAG9E,iGAAiG;AACjG,mGAAmG;AACnG,kDAAkD;AAAzC,uGAAA,UAAU,OAAA;AAEnB,yFAAyF;AACzF,kDAO8B;AAL1B,uGAAA,UAAU,OAAA;AACV,2GAAA,cAAc,OAAA;AACd,2GAAA,cAAc,OAAA;AACd,4GAAA,eAAe,OAAA;AACf,iHAAA,oBAAoB,OAAA;AAIxB,uEAAuE;AACvE,kDAY8B;AAX1B,oGAAA,OAAO,OAAA;AACP,qGAAA,QAAQ,OAAA;AACR,mGAAA,MAAM,OAAA;AACN,oGAAA,OAAO,OAAA;AACP,qGAAA,QAAQ,OAAA;AACR,6GAAA,gBAAgB,OAAA;AAChB,0GAAA,aAAa,OAAA;AACb,gGAAA,GAAG,OAAA;AACH,mGAAA,MAAM,OAAA;AACN,kGAAA,KAAK,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 (installed by the factory). Exported so a browser/Angular\n// logger can read the current tag via ApiCallContextHolder.get().peek() 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, TranslatedFailure } from '@webpieces/http-client-core';\nexport type { ApiPrototype } 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 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 Public,\n AuthJwt,\n AuthOidc,\n AuthSharedSecret,\n AuthLocalOnly,\n Rpc,\n PubSub,\n Queue,\n ValidateImplementation,\n} from '@webpieces/core-util';\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,gEAAoH;AAA3G,+GAAA,WAAW,OAAA;AAAE,yHAAA,qBAAqB,OAAA;AAAE,kHAAA,cAAc,OAAA;AAAE,qHAAA,iBAAiB,OAAA;AAG9E,iGAAiG;AACjG,mGAAmG;AACnG,kDAAkD;AAAzC,uGAAA,UAAU,OAAA;AAEnB,yFAAyF;AACzF,kDAO8B;AAL1B,uGAAA,UAAU,OAAA;AACV,2GAAA,cAAc,OAAA;AACd,2GAAA,cAAc,OAAA;AACd,4GAAA,eAAe,OAAA;AACf,iHAAA,oBAAoB,OAAA;AAIxB,uEAAuE;AACvE,kDAY8B;AAX1B,oGAAA,OAAO,OAAA;AACP,qGAAA,QAAQ,OAAA;AACR,mGAAA,MAAM,OAAA;AACN,oGAAA,OAAO,OAAA;AACP,qGAAA,QAAQ,OAAA;AACR,6GAAA,gBAAgB,OAAA;AAChB,0GAAA,aAAa,OAAA;AACb,gGAAA,GAAG,OAAA;AACH,mGAAA,MAAM,OAAA;AACN,kGAAA,KAAK,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, TranslatedFailure } from '@webpieces/http-client-core';\nexport type { ApiPrototype } 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 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 Public,\n AuthJwt,\n AuthOidc,\n AuthSharedSecret,\n AuthLocalOnly,\n Rpc,\n PubSub,\n Queue,\n ValidateImplementation,\n} from '@webpieces/core-util';\n"]}