@webpieces/http-client-browser 0.4.393 → 0.4.395

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.393",
3
+ "version": "0.4.395",
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.393",
26
- "@webpieces/http-client-core": "0.4.393"
25
+ "@webpieces/core-util": "0.4.395",
26
+ "@webpieces/http-client-core": "0.4.395"
27
27
  }
28
28
  }
@@ -1,7 +1,7 @@
1
1
  import { AuthMeta, ContextMgr, RouteMetadata } from '@webpieces/core-util';
2
- import { ApiPrototype, ProxyClient } from '@webpieces/http-client-core';
2
+ import { ApiPrototype, ProxyClient, RequestOutcome } from '@webpieces/http-client-core';
3
3
  import { ClientConfig } from './ClientConfig';
4
- import { ResponseHeadersListener } from './ResponseHeadersListener';
4
+ import { RequestLifecycleListener } from './RequestLifecycleListener';
5
5
  /**
6
6
  * The browser {@link ProxyClient}. Reads context from the app-held store (via {@link ContextMgr}),
7
7
  * because a browser has no ambient request scope.
@@ -14,9 +14,9 @@ import { ResponseHeadersListener } from './ResponseHeadersListener';
14
14
  */
15
15
  export declare class BrowserProxyClient extends ProxyClient {
16
16
  private readonly contextMgr;
17
- private readonly headersListener?;
17
+ private readonly lifecycleListener?;
18
18
  private config;
19
- constructor(contextMgr: ContextMgr, headersListener?: ResponseHeadersListener | undefined);
19
+ constructor(contextMgr: ContextMgr, lifecycleListener?: RequestLifecycleListener | undefined);
20
20
  /** Bind this client to one API contract + base URL. */
21
21
  init(apiPrototype: ApiPrototype<object>, config: ClientConfig): void;
22
22
  /**
@@ -33,10 +33,11 @@ export declare class BrowserProxyClient extends ProxyClient {
33
33
  protected resolveBaseUrl(): Promise<string>;
34
34
  protected outboundHeaders(): Map<string, string>;
35
35
  /**
36
- * Forward the response headers to the app's listener, if one was registered on the factory.
37
- * The optional chain makes this a no-op when no listener is present — the default browser case.
36
+ * Forward the call's lifecycle to the app's listener, if one was registered on the factory. The
37
+ * optional chain makes both a no-op when no listener is present — the default browser case.
38
38
  */
39
- protected onResponseHeaders(route: RouteMetadata, headers: Headers): void;
39
+ protected onRequestStart(route: RouteMetadata): void;
40
+ protected onRequestEnd(route: RouteMetadata, outcome: RequestOutcome): void;
40
41
  /**
41
42
  * Reject a contract this browser cannot satisfy, at bind time rather than on the first call.
42
43
  * Both service-to-service modes need credentials only a server has: @AuthOidc needs a runtime
@@ -15,12 +15,12 @@ const http_client_core_1 = require("@webpieces/http-client-core");
15
15
  */
16
16
  class BrowserProxyClient extends http_client_core_1.ProxyClient {
17
17
  contextMgr;
18
- headersListener;
18
+ lifecycleListener;
19
19
  config;
20
- constructor(contextMgr, headersListener) {
20
+ constructor(contextMgr, lifecycleListener) {
21
21
  super();
22
22
  this.contextMgr = contextMgr;
23
- this.headersListener = headersListener;
23
+ this.lifecycleListener = lifecycleListener;
24
24
  }
25
25
  /** Bind this client to one API contract + base URL. */
26
26
  init(apiPrototype, config) {
@@ -45,11 +45,14 @@ class BrowserProxyClient extends http_client_core_1.ProxyClient {
45
45
  return this.contextMgr.buildOutboundHeaders();
46
46
  }
47
47
  /**
48
- * Forward the response headers to the app's listener, if one was registered on the factory.
49
- * The optional chain makes this a no-op when no listener is present — the default browser case.
48
+ * Forward the call's lifecycle to the app's listener, if one was registered on the factory. The
49
+ * optional chain makes both a no-op when no listener is present — the default browser case.
50
50
  */
51
- onResponseHeaders(route, headers) {
52
- this.headersListener?.onResponseHeaders(route, headers);
51
+ onRequestStart(route) {
52
+ this.lifecycleListener?.onRequestStart(route);
53
+ }
54
+ onRequestEnd(route, outcome) {
55
+ this.lifecycleListener?.onRequestEnd(route, outcome);
53
56
  }
54
57
  /**
55
58
  * Reject a contract this browser cannot satisfy, at bind time rather than on the first call.
@@ -1 +1 @@
1
- {"version":3,"file":"BrowserProxyClient.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/BrowserProxyClient.ts"],"names":[],"mappings":";;;AAAA,oDAA2F;AAC3F,kEAAwE;AAIxE;;;;;;;;;GASG;AACH,MAAa,kBAAmB,SAAQ,8BAAW;IAI1B;IACA;IAJb,MAAM,CAAgB;IAE9B,YACqB,UAAsB,EACtB,eAAyC;QAE1D,KAAK,EAAE,CAAC;QAHS,eAAU,GAAV,UAAU,CAAY;QACtB,oBAAe,GAAf,eAAe,CAA0B;IAG9D,CAAC;IAED,uDAAuD;IACvD,IAAI,CAAC,YAAkC,EAAE,MAAoB;QACzD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;IAClC,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;IAEkB,eAAe;QAC9B,OAAO,IAAI,CAAC,UAAU,CAAC,oBAAoB,EAAE,CAAC;IAClD,CAAC;IAED;;;OAGG;IACgB,iBAAiB,CAAC,KAAoB,EAAE,OAAgB;QACvE,IAAI,CAAC,eAAe,EAAE,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC5D,CAAC;IAED;;;;OAIG;IACgB,uBAAuB,CAAC,QAA8B,EAAE,UAAkB;QACzF,MAAM,IAAI,GAAG,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC;QACjC,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,eAAe,EAAE,CAAC;YAC9C,OAAO;QACX,CAAC;QACD,MAAM,IAAI,KAAK,CACX,YAAY,UAAU,QAAQ,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,kBAAkB,sBAAsB;YACrG,wGAAwG,CAC3G,CAAC;IACN,CAAC;CACJ;AA1DD,gDA0DC","sourcesContent":["import { AuthMeta, ContextMgr, ClientRegistry, RouteMetadata } from '@webpieces/core-util';\nimport { ApiPrototype, ProxyClient } from '@webpieces/http-client-core';\nimport { ClientConfig } from './ClientConfig';\nimport { ResponseHeadersListener } from './ResponseHeadersListener';\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 headersListener?: ResponseHeadersListener,\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 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 protected override outboundHeaders(): Map<string, string> {\n return this.contextMgr.buildOutboundHeaders();\n }\n\n /**\n * Forward the response headers to the app's listener, if one was registered on the factory.\n * The optional chain makes this a no-op when no listener is present — the default browser case.\n */\n protected override onResponseHeaders(route: RouteMetadata, headers: Headers): void {\n this.headersListener?.onResponseHeaders(route, headers);\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 protected override assertEndpointSupported(authMeta: AuthMeta | undefined, methodName: string): void {\n const kind = authMeta?.mode.kind;\n if (kind !== 'oidc' && kind !== 'shared-secret') {\n return;\n }\n throw new Error(\n `Endpoint ${methodName} is @${kind === 'oidc' ? 'AuthOidc' : 'AuthSharedSecret'} — a browser cannot ` +\n `hold service credentials. Call it server-side with ClientHttpFactory from @webpieces/http-client-node.`,\n );\n }\n}\n"]}
1
+ {"version":3,"file":"BrowserProxyClient.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/BrowserProxyClient.ts"],"names":[],"mappings":";;;AAAA,oDAA2F;AAC3F,kEAAwF;AAIxF;;;;;;;;;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,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;IAClC,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;IAEkB,eAAe;QAC9B,OAAO,IAAI,CAAC,UAAU,CAAC,oBAAoB,EAAE,CAAC;IAClD,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;;;;OAIG;IACgB,uBAAuB,CAAC,QAA8B,EAAE,UAAkB;QACzF,MAAM,IAAI,GAAG,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC;QACjC,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,eAAe,EAAE,CAAC;YAC9C,OAAO;QACX,CAAC;QACD,MAAM,IAAI,KAAK,CACX,YAAY,UAAU,QAAQ,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,kBAAkB,sBAAsB;YACrG,wGAAwG,CAC3G,CAAC;IACN,CAAC;CACJ;AA9DD,gDA8DC","sourcesContent":["import { AuthMeta, ContextMgr, ClientRegistry, RouteMetadata } from '@webpieces/core-util';\nimport { ApiPrototype, ProxyClient, RequestOutcome } 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 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 protected override outboundHeaders(): Map<string, string> {\n return this.contextMgr.buildOutboundHeaders();\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: @AuthOidc needs a runtime\n * service account to mint a token, @AuthSharedSecret needs a secret no browser may ship.\n */\n protected override assertEndpointSupported(authMeta: AuthMeta | undefined, methodName: string): void {\n const kind = authMeta?.mode.kind;\n if (kind !== 'oidc' && kind !== 'shared-secret') {\n return;\n }\n throw new Error(\n `Endpoint ${methodName} is @${kind === 'oidc' ? 'AuthOidc' : 'AuthSharedSecret'} — a browser cannot ` +\n `hold service credentials. Call it server-side with ClientHttpFactory from @webpieces/http-client-node.`,\n );\n }\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  import { ApiPrototype } from '@webpieces/http-client-core';
2
2
  import { ClientConfig } from './ClientConfig';
3
3
  import { MutableContextStore } from './MutableContextStore';
4
- import { ResponseHeadersListener } from './ResponseHeadersListener';
4
+ import { RequestLifecycleListener } from './RequestLifecycleListener';
5
5
  /**
6
6
  * ClientHttpBrowserFactory - builds type-safe HTTP clients for a BROWSER from API prototypes
7
7
  * carrying @ApiPath/@Endpoint decorators.
@@ -31,9 +31,9 @@ import { ResponseHeadersListener } from './ResponseHeadersListener';
31
31
  * endpoint throws in `createRpcClient`, not on the first call.
32
32
  */
33
33
  export declare class ClientHttpBrowserFactory {
34
- private readonly headersListener?;
34
+ private readonly lifecycleListener?;
35
35
  private readonly contextMgr;
36
- constructor(store: MutableContextStore, headersListener?: ResponseHeadersListener | undefined);
36
+ constructor(store: MutableContextStore, lifecycleListener?: RequestLifecycleListener | undefined);
37
37
  /**
38
38
  * Create a type-safe HTTP client for one API contract.
39
39
  *
@@ -36,10 +36,10 @@ const MutableContextStore_1 = require("./MutableContextStore");
36
36
  * endpoint throws in `createRpcClient`, not on the first call.
37
37
  */
38
38
  let ClientHttpBrowserFactory = class ClientHttpBrowserFactory {
39
- headersListener;
39
+ lifecycleListener;
40
40
  contextMgr;
41
- constructor(store, headersListener) {
42
- this.headersListener = headersListener;
41
+ constructor(store, lifecycleListener) {
42
+ this.lifecycleListener = lifecycleListener;
43
43
  this.contextMgr = new core_util_1.ContextMgr(store);
44
44
  // Bind the BROWSER ApiCallContext so LogApiCall (core-util) stamps the `api` tag into the
45
45
  // module-global browser slot. Idempotent — building the factory more than once is harmless.
@@ -53,7 +53,7 @@ let ClientHttpBrowserFactory = class ClientHttpBrowserFactory {
53
53
  * @param config - This client's state (its baseUrl)
54
54
  */
55
55
  createRpcClient(apiPrototype, config) {
56
- const proxyClient = new BrowserProxyClient_1.BrowserProxyClient(this.contextMgr, this.headersListener);
56
+ const proxyClient = new BrowserProxyClient_1.BrowserProxyClient(this.contextMgr, this.lifecycleListener);
57
57
  proxyClient.init(apiPrototype, config);
58
58
  return (0, http_client_core_1.buildClientProxy)(apiPrototype, proxyClient);
59
59
  }
@@ -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,eAAyC;QAAzC,oBAAe,GAAf,eAAe,CAA0B;QAE1D,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,eAAe,CAAC,CAAC;QAClF,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 { ResponseHeadersListener } from './ResponseHeadersListener';\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 headersListener?: ResponseHeadersListener,\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.headersListener);\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,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"]}
@@ -0,0 +1,42 @@
1
+ import { RouteMetadata } from '@webpieces/core-util';
2
+ import { RequestOutcome } from '@webpieces/http-client-core';
3
+ /**
4
+ * The inbound seam an app implements to observe the FULL lifecycle of every RPC call — symmetric
5
+ * with the outbound header propagation done by {@link MutableContextStore} / {@link ContextMgr}.
6
+ * Registered once on {@link ClientHttpBrowserFactory}; invoked by {@link BrowserProxyClient} around
7
+ * each call.
8
+ *
9
+ * Replaces the older header-only ResponseHeadersListener. `onRequestEnd`'s `outcome.headers` carries
10
+ * the same Response Headers (still before the body is read, on both ok and error), and it adds the
11
+ * two signals the header-only seam could not give: a START marker, and success-or-error.
12
+ *
13
+ * The driver is a browser app that must drive ONE progress bar across N requests per user action,
14
+ * AND detect that a newer server is deployed than this bundle (the server stamps
15
+ * `x-<org>-server-version` on every response) — reloading only at a safe moment, so a half-filled
16
+ * form is never blown away. It needs, per RPC: bar on, bar off, the outcome, and the headers.
17
+ *
18
+ * `onRequestStart` and `onRequestEnd` are PAIRED: every start is followed by exactly one end, on
19
+ * every path (2xx, HTTP error, network reject), so a counter driven off them cannot leak.
20
+ *
21
+ * ```typescript
22
+ * class RpcLifecycleListener implements RequestLifecycleListener {
23
+ * onRequestStart(_route: RouteMetadata): void {
24
+ * progressBar.noteRequestStart();
25
+ * }
26
+ * onRequestEnd(_route: RouteMetadata, outcome: RequestOutcome): void {
27
+ * serverVersionBridge.set(outcome.headers?.get('x-myorg-server-version'));
28
+ * progressBar.noteRequestEnd(!outcome.ok);
29
+ * }
30
+ * }
31
+ * const factory = new ClientHttpBrowserFactory(store, new RpcLifecycleListener());
32
+ * ```
33
+ *
34
+ * This is BUSINESS LOGIC (methods), so it is an interface, not a data class — unlike the
35
+ * {@link RequestOutcome} it is handed.
36
+ */
37
+ export interface RequestLifecycleListener {
38
+ /** Before fetch, once per RPC. */
39
+ onRequestStart(route: RouteMetadata): void;
40
+ /** Once, after the call settles, on every path (success, HTTP error, network reject). */
41
+ onRequestEnd(route: RouteMetadata, outcome: RequestOutcome): void;
42
+ }
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=ResponseHeadersListener.js.map
3
+ //# sourceMappingURL=RequestLifecycleListener.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RequestLifecycleListener.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/RequestLifecycleListener.ts"],"names":[],"mappings":"","sourcesContent":["import { RouteMetadata } from '@webpieces/core-util';\nimport { RequestOutcome } from '@webpieces/http-client-core';\n\n/**\n * The inbound seam an app implements to observe the FULL lifecycle of every RPC call — symmetric\n * with the outbound header propagation done by {@link MutableContextStore} / {@link ContextMgr}.\n * Registered once on {@link ClientHttpBrowserFactory}; invoked by {@link BrowserProxyClient} around\n * each call.\n *\n * Replaces the older header-only ResponseHeadersListener. `onRequestEnd`'s `outcome.headers` carries\n * the same Response Headers (still before the body is read, on both ok and error), and it adds the\n * two signals the header-only seam could not give: a START marker, and success-or-error.\n *\n * The driver is a browser app that must drive ONE progress bar across N requests per user action,\n * AND detect that a newer server is deployed than this bundle (the server stamps\n * `x-<org>-server-version` on every response) — reloading only at a safe moment, so a half-filled\n * form is never blown away. It needs, per RPC: bar on, bar off, the outcome, and the headers.\n *\n * `onRequestStart` and `onRequestEnd` are PAIRED: every start is followed by exactly one end, on\n * every path (2xx, HTTP error, network reject), so a counter driven off them cannot leak.\n *\n * ```typescript\n * class RpcLifecycleListener implements RequestLifecycleListener {\n * onRequestStart(_route: RouteMetadata): void {\n * progressBar.noteRequestStart();\n * }\n * onRequestEnd(_route: RouteMetadata, outcome: RequestOutcome): void {\n * serverVersionBridge.set(outcome.headers?.get('x-myorg-server-version'));\n * progressBar.noteRequestEnd(!outcome.ok);\n * }\n * }\n * const factory = new ClientHttpBrowserFactory(store, new RpcLifecycleListener());\n * ```\n *\n * This is BUSINESS LOGIC (methods), so it is an interface, not a data class — unlike the\n * {@link RequestOutcome} it is handed.\n */\nexport interface RequestLifecycleListener {\n /** Before fetch, once per RPC. */\n onRequestStart(route: RouteMetadata): void;\n\n /** Once, after the call settles, on every path (success, HTTP error, network reject). */\n onRequestEnd(route: RouteMetadata, outcome: RequestOutcome): void;\n}\n"]}
package/src/index.d.ts CHANGED
@@ -28,8 +28,8 @@ export { BrowserProxyClient } from './BrowserProxyClient';
28
28
  export { BrowserApiCallContext } from './BrowserApiCallContext';
29
29
  export { ClientConfig } from './ClientConfig';
30
30
  export { MutableContextStore } from './MutableContextStore';
31
- export type { ResponseHeadersListener } from './ResponseHeadersListener';
32
- export { ProxyClient, ClientErrorTranslator } from '@webpieces/http-client-core';
31
+ export type { RequestLifecycleListener } from './RequestLifecycleListener';
32
+ export { ProxyClient, ClientErrorTranslator, RequestOutcome } from '@webpieces/http-client-core';
33
33
  export type { ApiPrototype } from '@webpieces/http-client-core';
34
34
  export { ContextMgr } from '@webpieces/core-util';
35
35
  export { ContextReader, ContextKey, HeaderRegistry, ClientRegistry, templateDeriver, WebpiecesCoreHeaders, } from '@webpieces/core-util';
package/src/index.js CHANGED
@@ -25,7 +25,7 @@
25
25
  * The server twin is @webpieces/http-client-node.
26
26
  */
27
27
  Object.defineProperty(exports, "__esModule", { value: true });
28
- exports.Queue = exports.PubSub = exports.Rpc = exports.AuthSharedSecret = exports.AuthOidc = exports.AuthJwt = exports.Public = exports.AuthenticationConfig = exports.Authentication = exports.Endpoint = exports.ApiPath = exports.WebpiecesCoreHeaders = exports.templateDeriver = exports.ClientRegistry = exports.HeaderRegistry = exports.ContextKey = exports.ContextMgr = exports.ClientErrorTranslator = exports.ProxyClient = exports.MutableContextStore = exports.ClientConfig = exports.BrowserApiCallContext = exports.BrowserProxyClient = exports.ClientHttpBrowserFactory = void 0;
28
+ exports.Queue = exports.PubSub = exports.Rpc = exports.AuthSharedSecret = exports.AuthOidc = exports.AuthJwt = exports.Public = exports.AuthenticationConfig = exports.Authentication = exports.Endpoint = exports.ApiPath = exports.WebpiecesCoreHeaders = exports.templateDeriver = exports.ClientRegistry = exports.HeaderRegistry = exports.ContextKey = exports.ContextMgr = 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");
@@ -42,6 +42,7 @@ Object.defineProperty(exports, "MutableContextStore", { enumerable: true, get: f
42
42
  var http_client_core_1 = require("@webpieces/http-client-core");
43
43
  Object.defineProperty(exports, "ProxyClient", { enumerable: true, get: function () { return http_client_core_1.ProxyClient; } });
44
44
  Object.defineProperty(exports, "ClientErrorTranslator", { enumerable: true, get: function () { return http_client_core_1.ClientErrorTranslator; } });
45
+ Object.defineProperty(exports, "RequestOutcome", { enumerable: true, get: function () { return http_client_core_1.RequestOutcome; } });
45
46
  // ContextMgr is the BROWSER's outbound-header propagation. This is the only package that may use
46
47
  // it; the server reads RequestContext directly (RequestContextHeaders in @webpieces/core-context).
47
48
  var core_util_1 = require("@webpieces/core-util");
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,gEAAiF;AAAxE,+GAAA,WAAW,OAAA;AAAE,yHAAA,qBAAqB,OAAA;AAG3C,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,kDAa8B;AAZ1B,oGAAA,OAAO,OAAA;AACP,qGAAA,QAAQ,OAAA;AACR,2GAAA,cAAc,OAAA;AACd,iHAAA,oBAAoB,OAAA;AACpB,mGAAA,MAAM,OAAA;AACN,oGAAA,OAAO,OAAA;AACP,qGAAA,QAAQ,OAAA;AACR,6GAAA,gBAAgB,OAAA;AAChB,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.getAllHeaders(), 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 { ResponseHeadersListener } from './ResponseHeadersListener';\n\n// The isomorphic engine, re-exported so a browser app needs one import.\nexport { ProxyClient, ClientErrorTranslator } 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 Authentication,\n AuthenticationConfig,\n Public,\n AuthJwt,\n AuthOidc,\n AuthSharedSecret,\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,oGAAoG;AACpG,oGAAoG;AACpG,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;AAG3D,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,kDAa8B;AAZ1B,oGAAA,OAAO,OAAA;AACP,qGAAA,QAAQ,OAAA;AACR,2GAAA,cAAc,OAAA;AACd,iHAAA,oBAAoB,OAAA;AACpB,mGAAA,MAAM,OAAA;AACN,oGAAA,OAAO,OAAA;AACP,qGAAA,QAAQ,OAAA;AACR,6GAAA,gBAAgB,OAAA;AAChB,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.getAllHeaders(), 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 } 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 Authentication,\n AuthenticationConfig,\n Public,\n AuthJwt,\n AuthOidc,\n AuthSharedSecret,\n Rpc,\n PubSub,\n Queue,\n ValidateImplementation,\n} from '@webpieces/core-util';\n"]}
@@ -1,27 +0,0 @@
1
- import { RouteMetadata } from '@webpieces/core-util';
2
- /**
3
- * The inbound seam an app implements to observe response headers on every RPC call — symmetric with
4
- * the outbound header propagation done by {@link MutableContextStore} / {@link ContextMgr}.
5
- *
6
- * Registered once on {@link ClientHttpBrowserFactory}, it is invoked by {@link BrowserProxyClient}
7
- * with the `fetch` Response `Headers` after each call, on BOTH the ok and error paths (so a
8
- * version — or any future — header is observed even on error responses), before the body is read.
9
- *
10
- * The driver is client↔server version matching: the server stamps `x-<org>-server-version` on every
11
- * response, and the browser reads it off each RPC response to detect that a newer server is deployed
12
- * than this bundle and prompt a reload.
13
- *
14
- * ```typescript
15
- * const factory = new ClientHttpBrowserFactory(store, {
16
- * onResponseHeaders: (_route, headers) => {
17
- * const v = headers.get('x-myorg-server-version');
18
- * if (v) serverVersionBridge.set(v);
19
- * },
20
- * });
21
- * ```
22
- *
23
- * This is BUSINESS LOGIC (a method), so it is an interface, not a data class.
24
- */
25
- export interface ResponseHeadersListener {
26
- onResponseHeaders(route: RouteMetadata, headers: Headers): void;
27
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"ResponseHeadersListener.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-browser/src/ResponseHeadersListener.ts"],"names":[],"mappings":"","sourcesContent":["import { RouteMetadata } from '@webpieces/core-util';\n\n/**\n * The inbound seam an app implements to observe response headers on every RPC call — symmetric with\n * the outbound header propagation done by {@link MutableContextStore} / {@link ContextMgr}.\n *\n * Registered once on {@link ClientHttpBrowserFactory}, it is invoked by {@link BrowserProxyClient}\n * with the `fetch` Response `Headers` after each call, on BOTH the ok and error paths (so a\n * version — or any future — header is observed even on error responses), before the body is read.\n *\n * The driver is client↔server version matching: the server stamps `x-<org>-server-version` on every\n * response, and the browser reads it off each RPC response to detect that a newer server is deployed\n * than this bundle and prompt a reload.\n *\n * ```typescript\n * const factory = new ClientHttpBrowserFactory(store, {\n * onResponseHeaders: (_route, headers) => {\n * const v = headers.get('x-myorg-server-version');\n * if (v) serverVersionBridge.set(v);\n * },\n * });\n * ```\n *\n * This is BUSINESS LOGIC (a method), so it is an interface, not a data class.\n */\nexport interface ResponseHeadersListener {\n onResponseHeaders(route: RouteMetadata, headers: Headers): void;\n}\n"]}