@webpieces/http-client-node 0.4.701 → 0.4.703
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 +5 -5
- package/src/ClientHttpFactory.d.ts +9 -8
- package/src/ClientHttpFactory.js +7 -5
- package/src/ClientHttpFactory.js.map +1 -1
- package/src/CreateRpcClientCompileAssertions.d.ts +12 -9
- package/src/CreateRpcClientCompileAssertions.js +36 -17
- package/src/CreateRpcClientCompileAssertions.js.map +1 -1
- package/src/index.d.ts +1 -1
- package/src/index.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/http-client-node",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.703",
|
|
4
4
|
"description": "Server-side HTTP client for webpieces: inversify-wired, reads RequestContext directly, mints OIDC/shared-secret delivery auth, resolves Cloud Run URLs from a service name",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
"access": "public"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@webpieces/core-context": "0.4.
|
|
26
|
-
"@webpieces/core-util": "0.4.
|
|
27
|
-
"@webpieces/gcp-identity": "0.4.
|
|
28
|
-
"@webpieces/http-client-core": "0.4.
|
|
25
|
+
"@webpieces/core-context": "0.4.703",
|
|
26
|
+
"@webpieces/core-util": "0.4.703",
|
|
27
|
+
"@webpieces/gcp-identity": "0.4.703",
|
|
28
|
+
"@webpieces/http-client-core": "0.4.703",
|
|
29
29
|
"inversify": "7.10.4",
|
|
30
30
|
"reflect-metadata": "0.2.2"
|
|
31
31
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Provider } from '@webpieces/core-context';
|
|
2
|
-
import type { ApiPrototype } from '@webpieces/http-client-core';
|
|
3
|
-
import type { ClientFilters } from '@webpieces/http-client-core';
|
|
2
|
+
import type { ApiPrototype, ClientFilterDefinition } from '@webpieces/http-client-core';
|
|
4
3
|
import { ClientConfig } from './ClientConfig';
|
|
5
4
|
import { NodeProxyClient } from './NodeProxyClient';
|
|
6
5
|
/**
|
|
@@ -63,12 +62,14 @@ export declare class ClientHttpFactory {
|
|
|
63
62
|
* guard and credential minter are installed on every client regardless, BENEATH anything
|
|
64
63
|
* passed here, so there is nothing an app can decline by writing nothing.
|
|
65
64
|
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
65
|
+
* A PLAIN OPTIONAL ARRAY, deliberately. Omitting it, passing `[]`, and passing a list you
|
|
66
|
+
* built up conditionally all mean the same thing and all compile — because the normal way
|
|
67
|
+
* an app arrives at a filter list is to declare a `ClientFilterDefinition[]` and push to
|
|
68
|
+
* it under `if`s, and the declared type of such a local can never be non-empty. A
|
|
69
|
+
* non-empty tuple here would reject that ordinary code and force a cast at the call site.
|
|
70
|
+
* `filters === undefined ? [] : [...filters]` below normalizes the two empty spellings to
|
|
71
|
+
* one value before anything downstream sees them. Pinned in
|
|
71
72
|
* {@link CreateRpcClientCompileAssertions}.
|
|
72
73
|
*/
|
|
73
|
-
createRpcClient<T extends object>(apiPrototype: ApiPrototype<T>, config: ClientConfig, filters?:
|
|
74
|
+
createRpcClient<T extends object>(apiPrototype: ApiPrototype<T>, config: ClientConfig, filters?: readonly ClientFilterDefinition[]): T;
|
|
74
75
|
}
|
package/src/ClientHttpFactory.js
CHANGED
|
@@ -72,11 +72,13 @@ let ClientHttpFactory = class ClientHttpFactory {
|
|
|
72
72
|
* guard and credential minter are installed on every client regardless, BENEATH anything
|
|
73
73
|
* passed here, so there is nothing an app can decline by writing nothing.
|
|
74
74
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
75
|
+
* A PLAIN OPTIONAL ARRAY, deliberately. Omitting it, passing `[]`, and passing a list you
|
|
76
|
+
* built up conditionally all mean the same thing and all compile — because the normal way
|
|
77
|
+
* an app arrives at a filter list is to declare a `ClientFilterDefinition[]` and push to
|
|
78
|
+
* it under `if`s, and the declared type of such a local can never be non-empty. A
|
|
79
|
+
* non-empty tuple here would reject that ordinary code and force a cast at the call site.
|
|
80
|
+
* `filters === undefined ? [] : [...filters]` below normalizes the two empty spellings to
|
|
81
|
+
* one value before anything downstream sees them. Pinned in
|
|
80
82
|
* {@link CreateRpcClientCompileAssertions}.
|
|
81
83
|
*/
|
|
82
84
|
createRpcClient(apiPrototype, config, filters) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClientHttpFactory.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-node/src/ClientHttpFactory.ts"],"names":[],"mappings":";;;;AAAA,yCAAmC;AACnC,oDAAsD;AACtD,0DAAqG;AAErG,kEAA+D;
|
|
1
|
+
{"version":3,"file":"ClientHttpFactory.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-node/src/ClientHttpFactory.ts"],"names":[],"mappings":";;;;AAAA,yCAAmC;AACnC,oDAAsD;AACtD,0DAAqG;AAErG,kEAA+D;AAE/D,uDAAgF;AAEhF,iGAAiG;AACjG,6EAA6E;AAC7E,IAAA,oCAAqB,EAAC,4CAA0B,EAAE,iCAAe,CAAC,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAGI,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAE+B;IADzD,YACyD,mBAA8C;QAA9C,wBAAmB,GAAnB,mBAAmB,CAA2B;IACpG,CAAC;IAEJ;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,eAAe,CACX,YAA6B,EAC7B,MAAoB,EACpB,OAA2C;QAE3C,sFAAsF;QACtF,wEAAwE;QACxE,MAAM,WAAW,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,EAAE,CAAC;QACnD,WAAW,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;QAClF,OAAO,IAAA,mCAAgB,EAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IACvD,CAAC;CACJ,CAAA;AAxCY,8CAAiB;4BAAjB,iBAAiB;IAF7B,IAAA,0BAAc,GAAE;IAChB,IAAA,wCAAyB,GAAE;IAGnB,mBAAA,IAAA,kBAAM,EAAC,4CAA0B,CAAC,CAAA;6CAAuC,uBAAQ;GAF7E,iBAAiB,CAwC7B","sourcesContent":["import { inject } from 'inversify';\nimport { DocumentDesign } from '@webpieces/core-util';\nimport { Provider, bindFrameworkProvider, provideFrameworkSingleton } from '@webpieces/core-context';\nimport type { ApiPrototype, ClientFilterDefinition } from '@webpieces/http-client-core';\nimport { buildClientProxy } from '@webpieces/http-client-core';\nimport { ClientConfig } from './ClientConfig';\nimport { NODE_PROXY_CLIENT_PROVIDER, NodeProxyClient } from './NodeProxyClient';\n\n// Teach the container how to hand out fresh NodeProxyClients. NodeProxyClient is bound TRANSIENT\n// (@provideFrameworkTransient), so each provider.get() constructs a new one.\nbindFrameworkProvider(NODE_PROXY_CLIENT_PROVIDER, NodeProxyClient);\n\n/**\n * ClientHttpFactory - builds type-safe HTTP clients from API prototypes carrying\n * @ApiPath/@Endpoint decorators. The SERVER-side factory.\n *\n * This is the client-side equivalent of ApiRoutingFactory:\n * - Server routing: ApiRoutingFactory reads decorators -> routes HTTP requests to controllers\n * - Server client: ClientHttpFactory reads decorators -> generates HTTP requests from method calls\n *\n * Inject it and ask for a typed client per contract:\n * ```typescript\n * // same project + region as this container; the URL is derived, you maintain nothing\n * const server2 = factory.createRpcClient(Server2Api, new ClientConfig('server2'));\n *\n * // to reach somewhere derivation cannot describe (other region/project, non-Cloud-Run, localhost),\n * // register it once at startup — the client still carries only the svcName:\n * // ClientRegistry.addUrlMapping('legacy', 'https://legacy.corp');\n * const legacy = factory.createRpcClient(LegacyApi, new ClientConfig('legacy'));\n *\n * const response = await server2.fetchValue(req); // inside a RequestContext\n * ```\n *\n * A destination that is DATA rather than deployment — a URL a partner registered — is a FILTER, not\n * a different kind of client. Install `ContextBaseUrlFilter` on the one client that may be\n * re-pointed, and set the URL per call:\n * ```typescript\n * const partner = factory.createRpcClient(PartnerWebhookApi, new ClientConfig('partner-webhooks'), [\n * new ClientFilterDefinition(1000, new ContextBaseUrlFilter()),\n * ]);\n * ```\n * The SSRF guard arms itself the moment that filter re-points a request, and the contract's\n * `@AuthWebhook(name)` selects the app's bound `WebhookSignerCallback` to sign the exact bytes —\n * neither is something the app registers, orders, or can displace.\n *\n * Every client it builds shares one {@link NodeProxyClient} *shape* but never one instance: the\n * injected `Provider<NodeProxyClient>` hands out a fresh one per contract, which `createRpcClient`\n * then `init`s. Their collaborators (RequestContextHeaders, Secrets) come from the container, so\n * the whole dependency graph is visible in this package's design.html.\n *\n * Unlike @webpieces/http-client-browser this package is Node-only, so the factory IS the inversify\n * entry point and the magic context is read straight from the RequestContext. A call made outside\n * `RequestContext.run(...)` throws rather than silently dropping the trace.\n */\n@DocumentDesign()\n@provideFrameworkSingleton()\nexport class ClientHttpFactory {\n constructor(\n @inject(NODE_PROXY_CLIENT_PROVIDER) private readonly proxyClientProvider: Provider<NodeProxyClient>,\n ) {}\n\n /**\n * Create a type-safe RPC (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 svcName, which is what `ClientRegistry` resolves\n * @param filters - This client's own OUTBOUND filters, each with the priority it runs at\n * (highest OUTERMOST). They wrap the send, so a filter may rewrite the URL, add or remove\n * headers, log, or replace `ClientRequest.body` — the exact bytes transmitted. What goes\n * here is APP behaviour: url rewriting, headers, logging, and `ContextBaseUrlFilter` when\n * this client's destination arrives per call.\n *\n * OPTIONAL, and omitting it is not a statement about security: the framework's own SSRF\n * guard and credential minter are installed on every client regardless, BENEATH anything\n * passed here, so there is nothing an app can decline by writing nothing.\n *\n * A PLAIN OPTIONAL ARRAY, deliberately. Omitting it, passing `[]`, and passing a list you\n * built up conditionally all mean the same thing and all compile — because the normal way\n * an app arrives at a filter list is to declare a `ClientFilterDefinition[]` and push to\n * it under `if`s, and the declared type of such a local can never be non-empty. A\n * non-empty tuple here would reject that ordinary code and force a cast at the call site.\n * `filters === undefined ? [] : [...filters]` below normalizes the two empty spellings to\n * one value before anything downstream sees them. Pinned in\n * {@link CreateRpcClientCompileAssertions}.\n */\n createRpcClient<T extends object>(\n apiPrototype: ApiPrototype<T>,\n config: ClientConfig,\n filters?: readonly ClientFilterDefinition[],\n ): T {\n // Fresh instance per contract — NodeProxyClient is transient. init() binds it to this\n // contract + target; the collaborators already came from the container.\n const proxyClient = this.proxyClientProvider.get();\n proxyClient.init(apiPrototype, config, filters === undefined ? [] : [...filters]);\n return buildClientProxy(apiPrototype, proxyClient);\n }\n}\n"]}
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* COMPILE-TIME assertions
|
|
2
|
+
* COMPILE-TIME assertions that `createRpcClient`'s filters argument is GENUINELY optional.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The argument is `readonly ClientFilterDefinition[] | undefined`, so all three ways an app says
|
|
5
|
+
* "here are this client's filters" compile: omit it, pass `[]`, or pass a mutable
|
|
6
|
+
* `ClientFilterDefinition[]` that was declared empty and pushed to under `if`s. That last one is the
|
|
7
|
+
* whole reason this file exists — it is how a real app builds a filter list once the list is not a
|
|
8
|
+
* hardcoded literal, and a non-empty tuple parameter rejects it, because the declared type of a
|
|
9
|
+
* conditionally-populated local cannot be non-empty. The cure would be a cast at every call site,
|
|
10
|
+
* which is worse than the case the tuple was protecting against.
|
|
8
11
|
*
|
|
9
12
|
* WHY THIS FILE AND NOT A SPEC. `tsconfig.lib.json` EXCLUDES `*.spec.ts`, and vitest strips types
|
|
10
|
-
* with esbuild rather than checking them — so a
|
|
11
|
-
* passes whether or not the
|
|
12
|
-
*
|
|
13
|
-
* failure IS the test. Mirrors `core-util/src/http/AuthJwtCompileAssertions.ts`.
|
|
13
|
+
* with esbuild rather than checking them — so a type-level assertion in a spec is inert and the
|
|
14
|
+
* suite passes whether or not the signature still admits these calls. Here it is compiled by the
|
|
15
|
+
* build: if the signature is ever re-narrowed, tsc fails on the line below that stops compiling.
|
|
16
|
+
* That failure IS the test. Mirrors `core-util/src/http/AuthJwtCompileAssertions.ts`.
|
|
14
17
|
*
|
|
15
18
|
* Nothing here runs. The class is never constructed and never exported from the barrel.
|
|
16
19
|
*/
|
|
@@ -1,36 +1,50 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* COMPILE-TIME assertions
|
|
3
|
+
* COMPILE-TIME assertions that `createRpcClient`'s filters argument is GENUINELY optional.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* The argument is `readonly ClientFilterDefinition[] | undefined`, so all three ways an app says
|
|
6
|
+
* "here are this client's filters" compile: omit it, pass `[]`, or pass a mutable
|
|
7
|
+
* `ClientFilterDefinition[]` that was declared empty and pushed to under `if`s. That last one is the
|
|
8
|
+
* whole reason this file exists — it is how a real app builds a filter list once the list is not a
|
|
9
|
+
* hardcoded literal, and a non-empty tuple parameter rejects it, because the declared type of a
|
|
10
|
+
* conditionally-populated local cannot be non-empty. The cure would be a cast at every call site,
|
|
11
|
+
* which is worse than the case the tuple was protecting against.
|
|
9
12
|
*
|
|
10
13
|
* WHY THIS FILE AND NOT A SPEC. `tsconfig.lib.json` EXCLUDES `*.spec.ts`, and vitest strips types
|
|
11
|
-
* with esbuild rather than checking them — so a
|
|
12
|
-
* passes whether or not the
|
|
13
|
-
*
|
|
14
|
-
* failure IS the test. Mirrors `core-util/src/http/AuthJwtCompileAssertions.ts`.
|
|
14
|
+
* with esbuild rather than checking them — so a type-level assertion in a spec is inert and the
|
|
15
|
+
* suite passes whether or not the signature still admits these calls. Here it is compiled by the
|
|
16
|
+
* build: if the signature is ever re-narrowed, tsc fails on the line below that stops compiling.
|
|
17
|
+
* That failure IS the test. Mirrors `core-util/src/http/AuthJwtCompileAssertions.ts`.
|
|
15
18
|
*
|
|
16
19
|
* Nothing here runs. The class is never constructed and never exported from the barrel.
|
|
17
20
|
*/
|
|
18
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
const core_util_1 = require("@webpieces/core-util");
|
|
19
23
|
const http_client_core_1 = require("@webpieces/http-client-core");
|
|
20
24
|
const ClientConfig_1 = require("./ClientConfig");
|
|
21
25
|
const ContextBaseUrlFilter_1 = require("./ContextBaseUrlFilter");
|
|
26
|
+
/** A second app filter, so the conditional-build case below has two branches like a real one. */
|
|
27
|
+
class OutboundLogFilter extends core_util_1.Filter {
|
|
28
|
+
filter(request, next) {
|
|
29
|
+
return next.invoke(request);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
22
32
|
class CreateRpcClientCompileAssertions {
|
|
23
|
-
/**
|
|
33
|
+
/** No app filters, said by omitting the argument. */
|
|
24
34
|
noFilters() {
|
|
25
35
|
factory.createRpcClient(someApi, new ClientConfig_1.ClientConfig('server2'));
|
|
26
36
|
}
|
|
27
|
-
/**
|
|
37
|
+
/** No app filters, said with an empty array — the same thing, normalized to the same value. */
|
|
38
|
+
emptyArray() {
|
|
39
|
+
factory.createRpcClient(someApi, new ClientConfig_1.ClientConfig('server2'), []);
|
|
40
|
+
}
|
|
41
|
+
/** One app filter. */
|
|
28
42
|
oneFilter() {
|
|
29
43
|
factory.createRpcClient(someApi, new ClientConfig_1.ClientConfig('partner-webhooks'), [
|
|
30
44
|
new http_client_core_1.ClientFilterDefinition(1000, new ContextBaseUrlFilter_1.ContextBaseUrlFilter()),
|
|
31
45
|
]);
|
|
32
46
|
}
|
|
33
|
-
/**
|
|
47
|
+
/** Several app filters. */
|
|
34
48
|
severalFilters() {
|
|
35
49
|
factory.createRpcClient(someApi, new ClientConfig_1.ClientConfig('partner-webhooks'), [
|
|
36
50
|
new http_client_core_1.ClientFilterDefinition(1000, new ContextBaseUrlFilter_1.ContextBaseUrlFilter()),
|
|
@@ -38,12 +52,17 @@ class CreateRpcClientCompileAssertions {
|
|
|
38
52
|
]);
|
|
39
53
|
}
|
|
40
54
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
55
|
+
* THE CASE THIS CHANGE EXISTS FOR: a MUTABLE `ClientFilterDefinition[]`, declared empty and
|
|
56
|
+
* pushed to conditionally. Its declared type is not, and cannot be, non-empty; a mutable array
|
|
57
|
+
* is assignable to the `readonly` parameter, so this compiles with no cast.
|
|
43
58
|
*/
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
59
|
+
conditionallyBuiltList() {
|
|
60
|
+
const filters = [];
|
|
61
|
+
if (perTenant)
|
|
62
|
+
filters.push(new http_client_core_1.ClientFilterDefinition(1000, new ContextBaseUrlFilter_1.ContextBaseUrlFilter()));
|
|
63
|
+
if (verbose)
|
|
64
|
+
filters.push(new http_client_core_1.ClientFilterDefinition(500, new OutboundLogFilter()));
|
|
65
|
+
factory.createRpcClient(someApi, new ClientConfig_1.ClientConfig('partner-webhooks'), filters);
|
|
47
66
|
}
|
|
48
67
|
}
|
|
49
68
|
void CreateRpcClientCompileAssertions;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreateRpcClientCompileAssertions.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-node/src/CreateRpcClientCompileAssertions.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"CreateRpcClientCompileAssertions.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-node/src/CreateRpcClientCompileAssertions.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;GAkBG;;AAEH,oDAAuD;AACvD,kEAAoF;AACpF,iDAA8C;AAE9C,iEAA8D;AAQ9D,iGAAiG;AACjG,MAAM,iBAAkB,SAAQ,kBAA+B;IAClD,MAAM,CAAC,OAAsB,EAAE,IAAsC;QAC1E,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;CACJ;AAED,MAAM,gCAAgC;IAClC,qDAAqD;IACrD,SAAS;QACL,OAAO,CAAC,eAAe,CAAC,OAAO,EAAE,IAAI,2BAAY,CAAC,SAAS,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,+FAA+F;IAC/F,UAAU;QACN,OAAO,CAAC,eAAe,CAAC,OAAO,EAAE,IAAI,2BAAY,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,sBAAsB;IACtB,SAAS;QACL,OAAO,CAAC,eAAe,CAAC,OAAO,EAAE,IAAI,2BAAY,CAAC,kBAAkB,CAAC,EAAE;YACnE,IAAI,yCAAsB,CAAC,IAAI,EAAE,IAAI,2CAAoB,EAAE,CAAC;SAC/D,CAAC,CAAC;IACP,CAAC;IAED,2BAA2B;IAC3B,cAAc;QACV,OAAO,CAAC,eAAe,CAAC,OAAO,EAAE,IAAI,2BAAY,CAAC,kBAAkB,CAAC,EAAE;YACnE,IAAI,yCAAsB,CAAC,IAAI,EAAE,IAAI,2CAAoB,EAAE,CAAC;YAC5D,IAAI,yCAAsB,CAAC,GAAG,EAAE,IAAI,2CAAoB,EAAE,CAAC;SAC9D,CAAC,CAAC;IACP,CAAC;IAED;;;;OAIG;IACH,sBAAsB;QAClB,MAAM,OAAO,GAA6B,EAAE,CAAC;QAC7C,IAAI,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,IAAI,yCAAsB,CAAC,IAAI,EAAE,IAAI,2CAAoB,EAAE,CAAC,CAAC,CAAC;QAC1F,IAAI,OAAO;YAAE,OAAO,CAAC,IAAI,CAAC,IAAI,yCAAsB,CAAC,GAAG,EAAE,IAAI,iBAAiB,EAAE,CAAC,CAAC,CAAC;QACpF,OAAO,CAAC,eAAe,CAAC,OAAO,EAAE,IAAI,2BAAY,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC,CAAC;IACpF,CAAC;CACJ;AAED,KAAK,gCAAgC,CAAC","sourcesContent":["/**\n * COMPILE-TIME assertions that `createRpcClient`'s filters argument is GENUINELY optional.\n *\n * The argument is `readonly ClientFilterDefinition[] | undefined`, so all three ways an app says\n * \"here are this client's filters\" compile: omit it, pass `[]`, or pass a mutable\n * `ClientFilterDefinition[]` that was declared empty and pushed to under `if`s. That last one is the\n * whole reason this file exists — it is how a real app builds a filter list once the list is not a\n * hardcoded literal, and a non-empty tuple parameter rejects it, because the declared type of a\n * conditionally-populated local cannot be non-empty. The cure would be a cast at every call site,\n * which is worse than the case the tuple was protecting against.\n *\n * WHY THIS FILE AND NOT A SPEC. `tsconfig.lib.json` EXCLUDES `*.spec.ts`, and vitest strips types\n * with esbuild rather than checking them — so a type-level assertion in a spec is inert and the\n * suite passes whether or not the signature still admits these calls. Here it is compiled by the\n * build: if the signature is ever re-narrowed, tsc fails on the line below that stops compiling.\n * That failure IS the test. Mirrors `core-util/src/http/AuthJwtCompileAssertions.ts`.\n *\n * Nothing here runs. The class is never constructed and never exported from the barrel.\n */\n\nimport { Filter, Service } from '@webpieces/core-util';\nimport { ClientFilterDefinition, ClientRequest } from '@webpieces/http-client-core';\nimport { ClientConfig } from './ClientConfig';\nimport { ClientHttpFactory } from './ClientHttpFactory';\nimport { ContextBaseUrlFilter } from './ContextBaseUrlFilter';\n\n/** Stand-in for a real contract; only its TYPE is used, and only by tsc. */\ndeclare const someApi: Parameters<ClientHttpFactory['createRpcClient']>[0];\ndeclare const factory: ClientHttpFactory;\ndeclare const perTenant: boolean;\ndeclare const verbose: boolean;\n\n/** A second app filter, so the conditional-build case below has two branches like a real one. */\nclass OutboundLogFilter extends Filter<ClientRequest, Response> {\n override filter(request: ClientRequest, next: Service<ClientRequest, Response>): Promise<Response> {\n return next.invoke(request);\n }\n}\n\nclass CreateRpcClientCompileAssertions {\n /** No app filters, said by omitting the argument. */\n noFilters(): void {\n factory.createRpcClient(someApi, new ClientConfig('server2'));\n }\n\n /** No app filters, said with an empty array — the same thing, normalized to the same value. */\n emptyArray(): void {\n factory.createRpcClient(someApi, new ClientConfig('server2'), []);\n }\n\n /** One app filter. */\n oneFilter(): void {\n factory.createRpcClient(someApi, new ClientConfig('partner-webhooks'), [\n new ClientFilterDefinition(1000, new ContextBaseUrlFilter()),\n ]);\n }\n\n /** Several app filters. */\n severalFilters(): void {\n factory.createRpcClient(someApi, new ClientConfig('partner-webhooks'), [\n new ClientFilterDefinition(1000, new ContextBaseUrlFilter()),\n new ClientFilterDefinition(500, new ContextBaseUrlFilter()),\n ]);\n }\n\n /**\n * THE CASE THIS CHANGE EXISTS FOR: a MUTABLE `ClientFilterDefinition[]`, declared empty and\n * pushed to conditionally. Its declared type is not, and cannot be, non-empty; a mutable array\n * is assignable to the `readonly` parameter, so this compiles with no cast.\n */\n conditionallyBuiltList(): void {\n const filters: ClientFilterDefinition[] = [];\n if (perTenant) filters.push(new ClientFilterDefinition(1000, new ContextBaseUrlFilter()));\n if (verbose) filters.push(new ClientFilterDefinition(500, new OutboundLogFilter()));\n factory.createRpcClient(someApi, new ClientConfig('partner-webhooks'), filters);\n }\n}\n\nvoid CreateRpcClientCompileAssertions;\n"]}
|
package/src/index.d.ts
CHANGED
|
@@ -38,5 +38,5 @@ export { InternalAddressRules } from './InternalAddressRules';
|
|
|
38
38
|
export { AddressResolver, DnsAddressResolver } from './AddressResolver';
|
|
39
39
|
export { ProxyClient, ClientErrorTranslator, TranslatedFailure } from '@webpieces/http-client-core';
|
|
40
40
|
export { ClientRequest, ClientFilterDefinition } from '@webpieces/http-client-core';
|
|
41
|
-
export type { ClientFilter
|
|
41
|
+
export type { ClientFilter } from '@webpieces/http-client-core';
|
|
42
42
|
export type { ApiPrototype } from '@webpieces/http-client-core';
|
package/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-node/src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;;;AAEH,yDAAwD;AAA/C,sHAAA,iBAAiB,OAAA;AAC1B,qDAAgF;AAAvE,kHAAA,eAAe,OAAA;AAAE,6HAAA,0BAA0B,OAAA;AACpD,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AAErB,kGAAkG;AAClG,4DAA4D;AAC5D,+DAA8D;AAArD,4HAAA,oBAAoB,OAAA;AAC7B,2EAA0E;AAAjE,wIAAA,0BAA0B,OAAA;AAEnC,mGAAmG;AACnG,kGAAkG;AAClG,6DAA6D;AAC7D,2DAA2F;AAAlF,8HAAA,wBAAwB,OAAA;AAAE,+HAAA,yBAAyB,OAAA;AAE5D,mGAAmG;AACnG,mFAAmF;AACnF,iEAA0G;AAAjG,8HAAA,qBAAqB,OAAA;AAAE,wHAAA,eAAe,OAAA;AAAE,gIAAA,uBAAuB,OAAA;AAExE,qGAAqG;AACrG,+FAA+F;AAC/F,2CAA6D;AAApD,wGAAA,UAAU,OAAA;AAAE,+GAAA,iBAAiB,OAAA;AACtC,qDAAoD;AAA3C,kHAAA,eAAe,OAAA;AACxB,uDAAsD;AAA7C,oHAAA,gBAAgB,OAAA;AACzB,2DAA0D;AAAjD,wHAAA,kBAAkB,OAAA;AAC3B,+DAA8D;AAArD,4HAAA,oBAAoB,OAAA;AAC7B,qDAAwE;AAA/D,kHAAA,eAAe,OAAA;AAAE,qHAAA,kBAAkB,OAAA;AAE5C,uEAAuE;AACvE,gEAAoG;AAA3F,+GAAA,WAAW,OAAA;AAAE,yHAAA,qBAAqB,OAAA;AAAE,qHAAA,iBAAiB,OAAA;AAC9D,gEAAoF;AAA3E,iHAAA,aAAa,OAAA;AAAE,0HAAA,sBAAsB,OAAA","sourcesContent":["/**\n * @webpieces/http-client-node\n *\n * The SERVER-side HTTP client. Reads an API contract's decorators and generates type-safe HTTP\n * clients from it — the same contract the callee's controller implements.\n *\n * Node-only, so unlike @webpieces/http-client-browser it is fully inversify-wired and reads the\n * magic context straight out of the AsyncLocalStorage-backed RequestContext. There is no\n * ContextReader indirection, because a server has exactly one right answer, and a call made\n * OUTSIDE `RequestContext.run(...)` throws instead of silently dropping the trace.\n *\n * Usage:\n * ```typescript\n * import { ClientHttpFactory, ClientConfig } from '@webpieces/http-client-node';\n *\n * // inject the factory, then one client per contract\n * const server2 = factory.createRpcClient(Server2Api, new ClientConfig('server2'));\n * const response = await server2.fetchValue(req);\n *\n * // a client whose destination arrives per call: ONE filter, and nothing else changes\n * const partner = factory.createRpcClient(PartnerWebhookApi, new ClientConfig('partner-webhooks'), [\n * new ClientFilterDefinition(1000, new ContextBaseUrlFilter()),\n * ]);\n * ```\n */\n\nexport { ClientHttpFactory } from './ClientHttpFactory';\nexport { NodeProxyClient, NODE_PROXY_CLIENT_PROVIDER } from './NodeProxyClient';\nexport { ClientConfig } from './ClientConfig';\n\n// Install this on the ONE client whose destination is data. Installing it IS the opt-in; a client\n// without it ignores an ambient OVERRIDE_BASE_URL entirely.\nexport { ContextBaseUrlFilter } from './ContextBaseUrlFilter';\nexport { MissingRuntimeBaseUrlError } from './MissingRuntimeBaseUrlError';\n\n// The two ways outbound auth refuses to send: a binding a contract's auth mode requires is absent.\n// Typed, so a delivery worker tells 'we are misconfigured' from 'the partner registered something\n// hostile' (SsrfRefusedError) without matching message text.\nexport { MissingSharedSecretError, MissingWebhookSignerError } from './OutboundAuthErrors';\n\n// The OUTBOUND half of @AuthWebhook(name) — the mirror of http-routing's WebhookAuthCallback. Bind\n// one, or every outbound @AuthWebhook call throws rather than delivering unsigned.\nexport { WebhookSignerCallback, SignableRequest, WEBHOOK_SIGNER_CALLBACK } from './WebhookSignerCallback';\n\n// The SSRF policy a re-pointed URL is judged under, and the refusal it produces. Automatic, armed by\n// the ACT of re-pointing; the ONLY way to relax it is naming SsrfTestingPolicy at a call site.\nexport { SsrfPolicy, SsrfTestingPolicy } from './SsrfPolicy';\nexport { SsrfGuardFilter } from './SsrfGuardFilter';\nexport { SsrfRefusedError } from './SsrfRefusedError';\nexport { OutboundAuthFilter } from './OutboundAuthFilter';\nexport { InternalAddressRules } from './InternalAddressRules';\nexport { AddressResolver, DnsAddressResolver } from './AddressResolver';\n\n// The isomorphic engine, re-exported so a server app needs one import.\nexport { ProxyClient, ClientErrorTranslator, TranslatedFailure } from '@webpieces/http-client-core';\nexport { ClientRequest, ClientFilterDefinition } from '@webpieces/http-client-core';\nexport type { ClientFilter
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/http/http-client-node/src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;;;AAEH,yDAAwD;AAA/C,sHAAA,iBAAiB,OAAA;AAC1B,qDAAgF;AAAvE,kHAAA,eAAe,OAAA;AAAE,6HAAA,0BAA0B,OAAA;AACpD,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AAErB,kGAAkG;AAClG,4DAA4D;AAC5D,+DAA8D;AAArD,4HAAA,oBAAoB,OAAA;AAC7B,2EAA0E;AAAjE,wIAAA,0BAA0B,OAAA;AAEnC,mGAAmG;AACnG,kGAAkG;AAClG,6DAA6D;AAC7D,2DAA2F;AAAlF,8HAAA,wBAAwB,OAAA;AAAE,+HAAA,yBAAyB,OAAA;AAE5D,mGAAmG;AACnG,mFAAmF;AACnF,iEAA0G;AAAjG,8HAAA,qBAAqB,OAAA;AAAE,wHAAA,eAAe,OAAA;AAAE,gIAAA,uBAAuB,OAAA;AAExE,qGAAqG;AACrG,+FAA+F;AAC/F,2CAA6D;AAApD,wGAAA,UAAU,OAAA;AAAE,+GAAA,iBAAiB,OAAA;AACtC,qDAAoD;AAA3C,kHAAA,eAAe,OAAA;AACxB,uDAAsD;AAA7C,oHAAA,gBAAgB,OAAA;AACzB,2DAA0D;AAAjD,wHAAA,kBAAkB,OAAA;AAC3B,+DAA8D;AAArD,4HAAA,oBAAoB,OAAA;AAC7B,qDAAwE;AAA/D,kHAAA,eAAe,OAAA;AAAE,qHAAA,kBAAkB,OAAA;AAE5C,uEAAuE;AACvE,gEAAoG;AAA3F,+GAAA,WAAW,OAAA;AAAE,yHAAA,qBAAqB,OAAA;AAAE,qHAAA,iBAAiB,OAAA;AAC9D,gEAAoF;AAA3E,iHAAA,aAAa,OAAA;AAAE,0HAAA,sBAAsB,OAAA","sourcesContent":["/**\n * @webpieces/http-client-node\n *\n * The SERVER-side HTTP client. Reads an API contract's decorators and generates type-safe HTTP\n * clients from it — the same contract the callee's controller implements.\n *\n * Node-only, so unlike @webpieces/http-client-browser it is fully inversify-wired and reads the\n * magic context straight out of the AsyncLocalStorage-backed RequestContext. There is no\n * ContextReader indirection, because a server has exactly one right answer, and a call made\n * OUTSIDE `RequestContext.run(...)` throws instead of silently dropping the trace.\n *\n * Usage:\n * ```typescript\n * import { ClientHttpFactory, ClientConfig } from '@webpieces/http-client-node';\n *\n * // inject the factory, then one client per contract\n * const server2 = factory.createRpcClient(Server2Api, new ClientConfig('server2'));\n * const response = await server2.fetchValue(req);\n *\n * // a client whose destination arrives per call: ONE filter, and nothing else changes\n * const partner = factory.createRpcClient(PartnerWebhookApi, new ClientConfig('partner-webhooks'), [\n * new ClientFilterDefinition(1000, new ContextBaseUrlFilter()),\n * ]);\n * ```\n */\n\nexport { ClientHttpFactory } from './ClientHttpFactory';\nexport { NodeProxyClient, NODE_PROXY_CLIENT_PROVIDER } from './NodeProxyClient';\nexport { ClientConfig } from './ClientConfig';\n\n// Install this on the ONE client whose destination is data. Installing it IS the opt-in; a client\n// without it ignores an ambient OVERRIDE_BASE_URL entirely.\nexport { ContextBaseUrlFilter } from './ContextBaseUrlFilter';\nexport { MissingRuntimeBaseUrlError } from './MissingRuntimeBaseUrlError';\n\n// The two ways outbound auth refuses to send: a binding a contract's auth mode requires is absent.\n// Typed, so a delivery worker tells 'we are misconfigured' from 'the partner registered something\n// hostile' (SsrfRefusedError) without matching message text.\nexport { MissingSharedSecretError, MissingWebhookSignerError } from './OutboundAuthErrors';\n\n// The OUTBOUND half of @AuthWebhook(name) — the mirror of http-routing's WebhookAuthCallback. Bind\n// one, or every outbound @AuthWebhook call throws rather than delivering unsigned.\nexport { WebhookSignerCallback, SignableRequest, WEBHOOK_SIGNER_CALLBACK } from './WebhookSignerCallback';\n\n// The SSRF policy a re-pointed URL is judged under, and the refusal it produces. Automatic, armed by\n// the ACT of re-pointing; the ONLY way to relax it is naming SsrfTestingPolicy at a call site.\nexport { SsrfPolicy, SsrfTestingPolicy } from './SsrfPolicy';\nexport { SsrfGuardFilter } from './SsrfGuardFilter';\nexport { SsrfRefusedError } from './SsrfRefusedError';\nexport { OutboundAuthFilter } from './OutboundAuthFilter';\nexport { InternalAddressRules } from './InternalAddressRules';\nexport { AddressResolver, DnsAddressResolver } from './AddressResolver';\n\n// The isomorphic engine, re-exported so a server app needs one import.\nexport { ProxyClient, ClientErrorTranslator, TranslatedFailure } from '@webpieces/http-client-core';\nexport { ClientRequest, ClientFilterDefinition } from '@webpieces/http-client-core';\nexport type { ClientFilter } from '@webpieces/http-client-core';\nexport type { ApiPrototype } from '@webpieces/http-client-core';\n"]}
|