@webpieces/core-context 0.4.390 → 0.4.391
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/RequestContextHeaders.js +1 -3
- package/src/RequestContextHeaders.js.map +1 -1
- package/src/frameworkProvide.js +9 -0
- package/src/frameworkProvide.js.map +1 -1
- package/src/index.d.ts +1 -1
- package/src/index.js +1 -3
- package/src/index.js.map +1 -1
- package/src/provide.d.ts +6 -27
- package/src/provide.js +10 -41
- package/src/provide.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/core-context",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.391",
|
|
4
4
|
"description": "AsyncLocalStorage-based context management for request-scoped data",
|
|
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.
|
|
25
|
+
"@webpieces/core-util": "0.4.391",
|
|
26
26
|
"@inversifyjs/binding-decorators": "1.1.5",
|
|
27
27
|
"inversify": "7.10.4",
|
|
28
28
|
"reflect-metadata": "0.2.2"
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RequestContextHeaders = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
-
const inversify_1 = require("inversify");
|
|
6
5
|
const core_util_1 = require("@webpieces/core-util");
|
|
7
6
|
const frameworkProvide_1 = require("./frameworkProvide");
|
|
8
7
|
const RequestContext_1 = require("./RequestContext");
|
|
@@ -109,7 +108,6 @@ let RequestContextHeaders = class RequestContextHeaders {
|
|
|
109
108
|
};
|
|
110
109
|
exports.RequestContextHeaders = RequestContextHeaders;
|
|
111
110
|
exports.RequestContextHeaders = RequestContextHeaders = tslib_1.__decorate([
|
|
112
|
-
(0, frameworkProvide_1.provideFrameworkSingleton)()
|
|
113
|
-
(0, inversify_1.injectable)()
|
|
111
|
+
(0, frameworkProvide_1.provideFrameworkSingleton)()
|
|
114
112
|
], RequestContextHeaders);
|
|
115
113
|
//# sourceMappingURL=RequestContextHeaders.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RequestContextHeaders.js","sourceRoot":"","sources":["../../../../../packages/core/core-context/src/RequestContextHeaders.ts"],"names":[],"mappings":";;;;AAAA,
|
|
1
|
+
{"version":3,"file":"RequestContextHeaders.js","sourceRoot":"","sources":["../../../../../packages/core/core-context/src/RequestContextHeaders.ts"],"names":[],"mappings":";;;;AAAA,oDAK8B;AAC9B,yDAA+D;AAE/D,qDAAkD;AAElD;;;;;;;;;;;;;;;;GAgBG;AAEI,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IAC9B;;;;;;;;;;OAUG;IACH,oBAAoB;QAChB,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAE5B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC1C,2DAA2D;QAC3D,KAAK,MAAM,GAAG,IAAI,0BAAc,CAAC,GAAG,EAAE,CAAC,kBAAkB,EAAE,EAAE,CAAC;YAC1D,MAAM,KAAK,GAAG,+BAAc,CAAC,SAAS,CAAS,GAAG,CAAC,CAAC;YACpD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;gBACxD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,UAAW,EAAE,KAAK,CAAC,CAAC;YACxC,CAAC;QACL,CAAC;QAED,OAAO,OAAO,CAAC;IACnB,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,eAAe,CAAC,OAAoB;QAChC,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAE5B,+BAAc,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAEnC,gGAAgG;QAChG,+FAA+F;QAC/F,oFAAoF;QACpF,+BAAc,CAAC,SAAS,CAAC,gCAAoB,CAAC,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAC3E,+BAAc,CAAC,SAAS,CAAC,gCAAoB,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAE1E,2DAA2D;QAC3D,KAAK,MAAM,GAAG,IAAI,0BAAc,CAAC,GAAG,EAAE,CAAC,kBAAkB,EAAE,EAAE,CAAC;YAC1D,MAAM,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;YAC5C,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC9B,+BAAc,CAAC,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7C,CAAC;QACL,CAAC;QAED,IAAI,CAAC,+BAAc,CAAC,SAAS,CAAC,gCAAoB,CAAC,UAAU,CAAC,EAAE,CAAC;YAC7D,+BAAc,CAAC,SAAS,CAAC,gCAAoB,CAAC,UAAU,EAAE,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC;QACxF,CAAC;IACL,CAAC;IAED,mFAAmF;IAC3E,iBAAiB;QACrB,OAAO,eAAe,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IACtF,CAAC;IAED;;;;OAIG;IACH,YAAY;QACR,IAAI,CAAC,+BAAc,CAAC,QAAQ,EAAE,EAAE,CAAC;YAC7B,OAAO,SAAS,CAAC;QACrB,CAAC;QACD,OAAO,+BAAc,CAAC,SAAS,CAAmB,wBAAY,CAAC,QAAQ,CAAC,CAAC;IAC7E,CAAC;IAED,iGAAiG;IACzF,oBAAoB;QACxB,IAAI,CAAC,+BAAc,CAAC,QAAQ,EAAE,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CACX,6EAA6E;gBAC7E,iFAAiF;gBACjF,kFAAkF,CACrF,CAAC;QACN,CAAC;IACL,CAAC;CACJ,CAAA;AA/FY,sDAAqB;gCAArB,qBAAqB;IADjC,IAAA,4CAAyB,GAAE;GACf,qBAAqB,CA+FjC","sourcesContent":["import {\n HeaderRegistry,\n RecorderKeys,\n TestCaseRecorder,\n WebpiecesCoreHeaders,\n} from '@webpieces/core-util';\nimport { provideFrameworkSingleton } from './frameworkProvide';\nimport { HttpRequest } from './HttpRequest';\nimport { RequestContext } from './RequestContext';\n\n/**\n * RequestContextHeaders - the magic context ↔ the wire, for a SERVER. Both directions live here:\n *\n * inbound {@link fillFromRequest} the published HttpRequest's headers -> the context\n * outbound {@link buildOutboundHeaders} the context -> the next hop's headers\n *\n * Reads the AsyncLocalStorage-backed {@link RequestContext} straight through — no ContextReader,\n * no ContextMgr, no abstract base. A server has exactly one place its context lives, and the\n * indirection only hid the failure below. (The browser's answer is `ContextMgr` in\n * @webpieces/core-util, which reads an app-held store because a browser has no ambient scope.)\n *\n * FAILS FAST outside a RequestContext. Silently sending an outbound call with NO request id or\n * tenant is far worse than a loud error — the trace just disappears and you find out in production. Every server-side client (RPC and Cloud Tasks) therefore only works\n * inside `RequestContext.run(...)`, which a top-level server filter normally establishes for you.\n *\n * Stateless once built, so it binds as a framework singleton every server-side client shares.\n */\n@provideFrameworkSingleton()\nexport class RequestContextHeaders {\n /**\n * EVERY transferred key with a non-empty value, under its wire name. Nothing is rewritten.\n *\n * That includes `x-request-id`, which propagates unchanged: one id correlates the whole call\n * tree, so the callee keeps ours rather than minting its own. ({@link fillFromRequest} only\n * generates an id when the inbound request carries none.)\n *\n * Values are RAW (unmasked) — this map goes on the wire, not in logs.\n *\n * @throws Error when called outside `RequestContext.run(...)` — see the class doc.\n */\n buildOutboundHeaders(): Map<string, string> {\n this.requireActiveContext();\n\n const headers = new Map<string, string>();\n // getTransferredKeys() is precomputed at configure() time.\n for (const key of HeaderRegistry.get().getTransferredKeys()) {\n const value = RequestContext.getHeader<string>(key);\n if (value !== undefined && value !== null && value !== '') {\n headers.set(key.httpHeader!, value);\n }\n }\n\n return headers;\n }\n\n /**\n * INBOUND — the exact inverse of {@link buildOutboundHeaders}. Publish the request, move every\n * transferrable header off it into the context (read by wire name, stored under the key's\n * `name`), and mint an `x-request-id` if the caller sent none.\n *\n * The request is a PARAMETER, not something we fish back out of the context. Publishing and\n * filling are therefore one atomic step that cannot be half-done or done out of order — the\n * older `setRequest()` + `fillContext()` pair could silently skip the transfer entirely when a\n * caller forgot the first half.\n *\n * This is a PRECONDITION of calling into http-routing, and it belongs ABOVE the api boundary.\n * `WebpiecesMiddleware` does it for every HTTP request; a non-webpieces transport (or a test\n * driving `createApiClient` directly) must do the same. The api proxy only checks that a\n * request scope exists — it never builds one.\n *\n * @throws Error when called outside `RequestContext.run(...)`.\n */\n fillFromRequest(request: HttpRequest): void {\n this.requireActiveContext();\n\n RequestContext.setRequest(request);\n\n // Stamp the inbound method+path as top-level logged keys (jsonPayload.httpMethod / requestPath)\n // so EVERY log line of this request carries them. Sourced from the just-published HttpRequest;\n // NOT transferred over the wire, so a downstream hop stamps its own inbound values.\n RequestContext.putHeader(WebpiecesCoreHeaders.HTTP_METHOD, request.method);\n RequestContext.putHeader(WebpiecesCoreHeaders.REQUEST_PATH, request.path);\n\n // getTransferredKeys() is precomputed at configure() time.\n for (const key of HeaderRegistry.get().getTransferredKeys()) {\n const values = request.getHeaderValues(key);\n if (values && values.length > 0) {\n RequestContext.putHeader(key, values[0]);\n }\n }\n\n if (!RequestContext.hasHeader(WebpiecesCoreHeaders.REQUEST_ID)) {\n RequestContext.putHeader(WebpiecesCoreHeaders.REQUEST_ID, this.generateRequestId());\n }\n }\n\n /** The id every log line of this request, and every downstream hop, will carry. */\n private generateRequestId(): string {\n return `svrGenReqId-${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;\n }\n\n /**\n * The recorder travelling in the context, when a test is recording this call. Absent in normal\n * operation, and ALWAYS absent in a browser — which is why recording lives on the server-side\n * client and never in the isomorphic core.\n */\n findRecorder(): TestCaseRecorder | undefined {\n if (!RequestContext.isActive()) {\n return undefined;\n }\n return RequestContext.getHeader<TestCaseRecorder>(RecorderKeys.RECORDER);\n }\n\n /** Guard both directions: no ambient request scope means there is no context to fill or read. */\n private requireActiveContext(): void {\n if (!RequestContext.isActive()) {\n throw new Error(\n 'No active RequestContext. A webpieces server-side client only works inside ' +\n 'RequestContext.run(...), which a top-level server filter normally establishes. ' +\n 'In a test, wrap the call: await RequestContext.run(async () => client.foo(req));',\n );\n }\n }\n}\n"]}
|
package/src/frameworkProvide.js
CHANGED
|
@@ -37,6 +37,10 @@ const frameworkProviderRegistry = [];
|
|
|
37
37
|
function provideFrameworkSingleton() {
|
|
38
38
|
// webpieces-disable no-any-unknown -- decorator target is any class constructor
|
|
39
39
|
return (target) => {
|
|
40
|
+
// Mark @injectable so Inversify v7 reads the ctor design:paramtypes (inject-by-type). The
|
|
41
|
+
// class no longer needs a separate @injectable. Scope comes from buildFrameworkModule's
|
|
42
|
+
// .inSingletonScope(), not from here.
|
|
43
|
+
(0, inversify_1.injectable)()(target);
|
|
40
44
|
frameworkRegistry.push(new FrameworkBinding(target, target));
|
|
41
45
|
return target;
|
|
42
46
|
};
|
|
@@ -56,6 +60,8 @@ function provideFrameworkSingleton() {
|
|
|
56
60
|
function provideFrameworkSingletonDefaultForApi(serviceIdentifier) {
|
|
57
61
|
// webpieces-disable no-any-unknown -- decorator target is any class constructor
|
|
58
62
|
return (target) => {
|
|
63
|
+
// Mark @injectable so the impl's ctor design:paramtypes are read when bound via .to(target).
|
|
64
|
+
(0, inversify_1.injectable)()(target);
|
|
59
65
|
frameworkRegistry.push(new FrameworkBinding(serviceIdentifier, target));
|
|
60
66
|
return target;
|
|
61
67
|
};
|
|
@@ -68,6 +74,9 @@ function provideFrameworkSingletonDefaultForApi(serviceIdentifier) {
|
|
|
68
74
|
function provideFrameworkTransient() {
|
|
69
75
|
// webpieces-disable no-any-unknown -- decorator target is any class constructor
|
|
70
76
|
return (target) => {
|
|
77
|
+
// Mark @injectable so Inversify v7 reads the ctor design:paramtypes (inject-by-type). Scope
|
|
78
|
+
// (transient) comes from buildFrameworkModule's .inTransientScope(), not from here.
|
|
79
|
+
(0, inversify_1.injectable)()(target);
|
|
71
80
|
frameworkRegistry.push(new FrameworkBinding(target, target, 'transient'));
|
|
72
81
|
return target;
|
|
73
82
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frameworkProvide.js","sourceRoot":"","sources":["../../../../../packages/core/core-context/src/frameworkProvide.ts"],"names":[],"mappings":";;AAoDA,8DAMC;AAcD,wFAMC;AAOD,8DAMC;AAwBD,sDAEC;AAOD,oDAoBC;AAhJD,yCAA4C;AAE5C,uCAAqC;AAwBrC,MAAM,gBAAgB;IAEE;IACA;IACA;IAHpB,YACoB,iBAAoC,EACpC,MAAe,EACf,QAAwB,WAAW;QAFnC,sBAAiB,GAAjB,iBAAiB,CAAmB;QACpC,WAAM,GAAN,MAAM,CAAS;QACf,UAAK,GAAL,KAAK,CAA8B;IACpD,CAAC;CACP;AAED,oGAAoG;AACpG,MAAM,wBAAwB;IAEN;IACA;IAFpB,YACoB,KAAwB,EACxB,MAAe;QADf,UAAK,GAAL,KAAK,CAAmB;QACxB,WAAM,GAAN,MAAM,CAAS;IAChC,CAAC;CACP;AAED,oGAAoG;AACpG,MAAM,iBAAiB,GAAuB,EAAE,CAAC;AAEjD,gGAAgG;AAChG,MAAM,yBAAyB,GAA+B,EAAE,CAAC;AAEjE;;;GAGG;AACH,SAAgB,yBAAyB;IACrC,gFAAgF;IAChF,OAAO,CAAC,MAAW,EAAE,EAAE;QACnB,iBAAiB,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QAC7D,OAAO,MAAM,CAAC;IAClB,CAAC,CAAC;AACN,CAAC;AAED;;;;;;;;;;GAUG;AACH,8FAA8F;AAC9F,SAAgB,sCAAsC,CAAI,iBAAuC;IAC7F,gFAAgF;IAChF,OAAO,CAAC,MAAW,EAAE,EAAE;QACnB,iBAAiB,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,CAAC;QACxE,OAAO,MAAM,CAAC;IAClB,CAAC,CAAC;AACN,CAAC;AAED;;;GAGG;AACH,8FAA8F;AAC9F,SAAgB,yBAAyB;IACrC,gFAAgF;IAChF,OAAO,CAAC,MAAW,EAAE,EAAE;QACnB,iBAAiB,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;QAC1E,OAAO,MAAM,CAAC;IAClB,CAAC,CAAC;AACN,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,oHAAoH;AACpH,SAAgB,qBAAqB,CAAC,KAAwB,EAAE,MAAe;IAC3E,yBAAyB,CAAC,IAAI,CAAC,IAAI,wBAAwB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;AAChF,CAAC;AAED;;;;GAIG;AACH,SAAgB,oBAAoB;IAChC,OAAO,IAAI,2BAAe,CAAC,CAAC,OAAmC,EAAE,EAAE;QAC/D,KAAK,MAAM,OAAO,IAAI,iBAAiB,EAAE,CAAC;YACtC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC1E,IAAI,OAAO,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;gBAChC,MAAM,CAAC,gBAAgB,EAAE,CAAC;YAC9B,CAAC;iBAAM,CAAC;gBACJ,MAAM,CAAC,gBAAgB,EAAE,CAAC;YAC9B,CAAC;QACL,CAAC;QACD,KAAK,MAAM,OAAO,IAAI,yBAAyB,EAAE,CAAC;YAC9C,oFAAoF;YACpF,uEAAuE;YACvE,OAAO;iBACF,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;iBACnB,cAAc,CAAC,CAAC,OAA0B,EAAE,EAAE,CAC3C,IAAI,kBAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;iBACnD,gBAAgB,EAAE,CAAC;QAC5B,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC","sourcesContent":["import { ContainerModule } from 'inversify';\nimport type { ContainerModuleLoadOptions, ResolutionContext, ServiceIdentifier } from 'inversify';\nimport { Provider } from './provide';\n\n/**\n * Framework-only DI provider decorators — a SEPARATE registry from the client-facing\n * @provideSingleton (which uses @inversifyjs/binding-decorators' single global registry).\n *\n * WHY: binding-decorators registers every @provideSingleton class under ONE global\n * reflect-metadata key, and buildProviderModule() scoops up that whole key. If webpieces\n * framework classes (RouteBuilderImpl, the filters, WebpiecesRouter) used @provideSingleton,\n * a CLIENT app's buildProviderModule() would drag those framework internals into its own\n * container. To keep the two worlds separate:\n * - packages/** (framework libs) MUST use provideFrameworkSingleton (this registry),\n * enforced by the no-global-providesingleton-in-packages ESLint rule.\n * - apps/** (and downstream client projects) use plain @provideSingleton (the global one).\n * The router loads BOTH buildFrameworkModule() and buildProviderModule(), so everything\n * resolves — but a client's buildProviderModule() only ever sees the client's own classes.\n */\n\n// webpieces-disable no-any-unknown -- decorator targets are arbitrary class constructors\ntype AnyCtor = new (...args: any[]) => unknown;\n\n/** How a framework binding is scoped. Always explicit — never inherited from the container. */\nexport type FrameworkScope = 'singleton' | 'transient';\n\nclass FrameworkBinding {\n constructor(\n public readonly serviceIdentifier: ServiceIdentifier,\n public readonly target: AnyCtor,\n public readonly scope: FrameworkScope = 'singleton',\n ) {}\n}\n\n/** A Provider token paired with the class its get() resolves. See {@link bindFrameworkProvider}. */\nclass FrameworkProviderBinding {\n constructor(\n public readonly token: ServiceIdentifier,\n public readonly target: AnyCtor,\n ) {}\n}\n\n/** The webpieces-only binding registry (a plain module-level list, one per hosted core-context). */\nconst frameworkRegistry: FrameworkBinding[] = [];\n\n/** Provider<T> bindings, applied after frameworkRegistry so their targets are already bound. */\nconst frameworkProviderRegistry: FrameworkProviderBinding[] = [];\n\n/**\n * Framework equivalent of @provideSingleton: registers the class as a singleton bound to\n * itself, into the webpieces framework registry (NOT the binding-decorators global one).\n */\nexport function provideFrameworkSingleton(): ClassDecorator {\n // webpieces-disable no-any-unknown -- decorator target is any class constructor\n return (target: any) => {\n frameworkRegistry.push(new FrameworkBinding(target, target));\n return target;\n };\n}\n\n/**\n * Framework equivalent of @provideSingletonDefaultForApi: marks this class as the DEFAULT (overridable)\n * singleton implementation OF a contract token (Symbol or abstract class), into the webpieces\n * framework registry. Binds `token -> thisClass` as a singleton.\n *\n * An app overrides it via appOverrides (loaded LAST), same idiom as AuthConfig:\n * `(await options.rebind(TOKEN)).to(OtherImpl)`.\n *\n * The DI-graph designer reads this in pass 1, so `@inject(TOKEN)` renders as `TOKEN (thisClass)`\n * and expands this class's own dependencies.\n */\n// webpieces-disable no-function-outside-class -- a decorator factory cannot be a class method\nexport function provideFrameworkSingletonDefaultForApi<T>(serviceIdentifier: ServiceIdentifier<T>): ClassDecorator {\n // webpieces-disable no-any-unknown -- decorator target is any class constructor\n return (target: any) => {\n frameworkRegistry.push(new FrameworkBinding(serviceIdentifier, target));\n return target;\n };\n}\n\n/**\n * Framework equivalent of @provideTransient: a NEW instance on every resolve. Use it for a\n * class a {@link Provider} hands out per call — e.g. one ProxyClient per API contract.\n */\n// webpieces-disable no-function-outside-class -- a decorator factory cannot be a class method\nexport function provideFrameworkTransient(): ClassDecorator {\n // webpieces-disable no-any-unknown -- decorator target is any class constructor\n return (target: any) => {\n frameworkRegistry.push(new FrameworkBinding(target, target, 'transient'));\n return target;\n };\n}\n\n/**\n * Register a {@link Provider} subclass as the DI token that hands out `target` instances.\n *\n * The provider caches nothing; `target`'s own binding scope decides whether callers share one\n * instance (provideFrameworkSingleton -> lazy singleton) or get a fresh one each `get()`\n * (provideFrameworkTransient -> 1-to-many).\n *\n * The provider itself is a singleton — it holds only the resolve-lambda.\n *\n * `Provider<T>` is erased at runtime and cannot be its own token, so name one after T. The DI-graph\n * analyzer reads `target` from HERE, which is why it can draw `Consumer -> T` with no provider box:\n * a Provider is DI plumbing, not wiring anyone needs to see.\n *\n * ```typescript\n * // webpieces-disable no-symbol-di-tokens -- Provider<T> is erased at runtime; T names the token\n * export const PROXY_CLIENT_PROVIDER = Symbol.for('ProxyClientProvider');\n * bindFrameworkProvider(PROXY_CLIENT_PROVIDER, NodeProxyClient);\n *\n * constructor(@inject(PROXY_CLIENT_PROVIDER) private readonly provider: Provider<NodeProxyClient>) {}\n * ```\n */\n// webpieces-disable no-function-outside-class -- registry side-effect, called at module scope beside the decorators\nexport function bindFrameworkProvider(token: ServiceIdentifier, target: AnyCtor): void {\n frameworkProviderRegistry.push(new FrameworkProviderBinding(token, target));\n}\n\n/**\n * Build a ContainerModule binding every provideFrameworkSingleton(As)/Transient class, then\n * every registered Provider. Load this into the webpieces framework + app containers (the\n * router does this) alongside the client's own buildProviderModule().\n */\nexport function buildFrameworkModule(): ContainerModule {\n return new ContainerModule((options: ContainerModuleLoadOptions) => {\n for (const binding of frameworkRegistry) {\n const bindTo = options.bind(binding.serviceIdentifier).to(binding.target);\n if (binding.scope === 'transient') {\n bindTo.inTransientScope();\n } else {\n bindTo.inSingletonScope();\n }\n }\n for (const binding of frameworkProviderRegistry) {\n // toDynamicValue so the provider closes over the ResolutionContext. Each get() then\n // re-resolves `target`, letting TARGET's scope decide shared-vs-fresh.\n options\n .bind(binding.token)\n .toDynamicValue((context: ResolutionContext) =>\n new Provider(() => context.get(binding.target)))\n .inSingletonScope();\n }\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"frameworkProvide.js","sourceRoot":"","sources":["../../../../../packages/core/core-context/src/frameworkProvide.ts"],"names":[],"mappings":";;AAoDA,8DAUC;AAcD,wFAQC;AAOD,8DASC;AAwBD,sDAEC;AAOD,oDAoBC;AAzJD,yCAAwD;AAExD,uCAAqC;AAwBrC,MAAM,gBAAgB;IAEE;IACA;IACA;IAHpB,YACoB,iBAAoC,EACpC,MAAe,EACf,QAAwB,WAAW;QAFnC,sBAAiB,GAAjB,iBAAiB,CAAmB;QACpC,WAAM,GAAN,MAAM,CAAS;QACf,UAAK,GAAL,KAAK,CAA8B;IACpD,CAAC;CACP;AAED,oGAAoG;AACpG,MAAM,wBAAwB;IAEN;IACA;IAFpB,YACoB,KAAwB,EACxB,MAAe;QADf,UAAK,GAAL,KAAK,CAAmB;QACxB,WAAM,GAAN,MAAM,CAAS;IAChC,CAAC;CACP;AAED,oGAAoG;AACpG,MAAM,iBAAiB,GAAuB,EAAE,CAAC;AAEjD,gGAAgG;AAChG,MAAM,yBAAyB,GAA+B,EAAE,CAAC;AAEjE;;;GAGG;AACH,SAAgB,yBAAyB;IACrC,gFAAgF;IAChF,OAAO,CAAC,MAAW,EAAE,EAAE;QACnB,0FAA0F;QAC1F,wFAAwF;QACxF,sCAAsC;QACtC,IAAA,sBAAU,GAAE,CAAC,MAAM,CAAC,CAAC;QACrB,iBAAiB,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QAC7D,OAAO,MAAM,CAAC;IAClB,CAAC,CAAC;AACN,CAAC;AAED;;;;;;;;;;GAUG;AACH,8FAA8F;AAC9F,SAAgB,sCAAsC,CAAI,iBAAuC;IAC7F,gFAAgF;IAChF,OAAO,CAAC,MAAW,EAAE,EAAE;QACnB,6FAA6F;QAC7F,IAAA,sBAAU,GAAE,CAAC,MAAM,CAAC,CAAC;QACrB,iBAAiB,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,CAAC;QACxE,OAAO,MAAM,CAAC;IAClB,CAAC,CAAC;AACN,CAAC;AAED;;;GAGG;AACH,8FAA8F;AAC9F,SAAgB,yBAAyB;IACrC,gFAAgF;IAChF,OAAO,CAAC,MAAW,EAAE,EAAE;QACnB,4FAA4F;QAC5F,oFAAoF;QACpF,IAAA,sBAAU,GAAE,CAAC,MAAM,CAAC,CAAC;QACrB,iBAAiB,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;QAC1E,OAAO,MAAM,CAAC;IAClB,CAAC,CAAC;AACN,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,oHAAoH;AACpH,SAAgB,qBAAqB,CAAC,KAAwB,EAAE,MAAe;IAC3E,yBAAyB,CAAC,IAAI,CAAC,IAAI,wBAAwB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;AAChF,CAAC;AAED;;;;GAIG;AACH,SAAgB,oBAAoB;IAChC,OAAO,IAAI,2BAAe,CAAC,CAAC,OAAmC,EAAE,EAAE;QAC/D,KAAK,MAAM,OAAO,IAAI,iBAAiB,EAAE,CAAC;YACtC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC1E,IAAI,OAAO,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;gBAChC,MAAM,CAAC,gBAAgB,EAAE,CAAC;YAC9B,CAAC;iBAAM,CAAC;gBACJ,MAAM,CAAC,gBAAgB,EAAE,CAAC;YAC9B,CAAC;QACL,CAAC;QACD,KAAK,MAAM,OAAO,IAAI,yBAAyB,EAAE,CAAC;YAC9C,oFAAoF;YACpF,uEAAuE;YACvE,OAAO;iBACF,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;iBACnB,cAAc,CAAC,CAAC,OAA0B,EAAE,EAAE,CAC3C,IAAI,kBAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;iBACnD,gBAAgB,EAAE,CAAC;QAC5B,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC","sourcesContent":["import { ContainerModule, injectable } from 'inversify';\nimport type { ContainerModuleLoadOptions, ResolutionContext, ServiceIdentifier } from 'inversify';\nimport { Provider } from './provide';\n\n/**\n * Framework-only DI provider decorators — a SEPARATE registry from the client-facing\n * @provideSingleton (which uses @inversifyjs/binding-decorators' single global registry).\n *\n * WHY: binding-decorators registers every @provideSingleton class under ONE global\n * reflect-metadata key, and buildProviderModule() scoops up that whole key. If webpieces\n * framework classes (RouteBuilderImpl, the filters, WebpiecesRouter) used @provideSingleton,\n * a CLIENT app's buildProviderModule() would drag those framework internals into its own\n * container. To keep the two worlds separate:\n * - packages/** (framework libs) MUST use provideFrameworkSingleton (this registry),\n * enforced by the no-global-providesingleton-in-packages ESLint rule.\n * - apps/** (and downstream client projects) use plain @provideSingleton (the global one).\n * The router loads BOTH buildFrameworkModule() and buildProviderModule(), so everything\n * resolves — but a client's buildProviderModule() only ever sees the client's own classes.\n */\n\n// webpieces-disable no-any-unknown -- decorator targets are arbitrary class constructors\ntype AnyCtor = new (...args: any[]) => unknown;\n\n/** How a framework binding is scoped. Always explicit — never inherited from the container. */\nexport type FrameworkScope = 'singleton' | 'transient';\n\nclass FrameworkBinding {\n constructor(\n public readonly serviceIdentifier: ServiceIdentifier,\n public readonly target: AnyCtor,\n public readonly scope: FrameworkScope = 'singleton',\n ) {}\n}\n\n/** A Provider token paired with the class its get() resolves. See {@link bindFrameworkProvider}. */\nclass FrameworkProviderBinding {\n constructor(\n public readonly token: ServiceIdentifier,\n public readonly target: AnyCtor,\n ) {}\n}\n\n/** The webpieces-only binding registry (a plain module-level list, one per hosted core-context). */\nconst frameworkRegistry: FrameworkBinding[] = [];\n\n/** Provider<T> bindings, applied after frameworkRegistry so their targets are already bound. */\nconst frameworkProviderRegistry: FrameworkProviderBinding[] = [];\n\n/**\n * Framework equivalent of @provideSingleton: registers the class as a singleton bound to\n * itself, into the webpieces framework registry (NOT the binding-decorators global one).\n */\nexport function provideFrameworkSingleton(): ClassDecorator {\n // webpieces-disable no-any-unknown -- decorator target is any class constructor\n return (target: any) => {\n // Mark @injectable so Inversify v7 reads the ctor design:paramtypes (inject-by-type). The\n // class no longer needs a separate @injectable. Scope comes from buildFrameworkModule's\n // .inSingletonScope(), not from here.\n injectable()(target);\n frameworkRegistry.push(new FrameworkBinding(target, target));\n return target;\n };\n}\n\n/**\n * Framework equivalent of @provideSingletonDefaultForApi: marks this class as the DEFAULT (overridable)\n * singleton implementation OF a contract token (Symbol or abstract class), into the webpieces\n * framework registry. Binds `token -> thisClass` as a singleton.\n *\n * An app overrides it via appOverrides (loaded LAST), same idiom as AuthConfig:\n * `(await options.rebind(TOKEN)).to(OtherImpl)`.\n *\n * The DI-graph designer reads this in pass 1, so `@inject(TOKEN)` renders as `TOKEN (thisClass)`\n * and expands this class's own dependencies.\n */\n// webpieces-disable no-function-outside-class -- a decorator factory cannot be a class method\nexport function provideFrameworkSingletonDefaultForApi<T>(serviceIdentifier: ServiceIdentifier<T>): ClassDecorator {\n // webpieces-disable no-any-unknown -- decorator target is any class constructor\n return (target: any) => {\n // Mark @injectable so the impl's ctor design:paramtypes are read when bound via .to(target).\n injectable()(target);\n frameworkRegistry.push(new FrameworkBinding(serviceIdentifier, target));\n return target;\n };\n}\n\n/**\n * Framework equivalent of @provideTransient: a NEW instance on every resolve. Use it for a\n * class a {@link Provider} hands out per call — e.g. one ProxyClient per API contract.\n */\n// webpieces-disable no-function-outside-class -- a decorator factory cannot be a class method\nexport function provideFrameworkTransient(): ClassDecorator {\n // webpieces-disable no-any-unknown -- decorator target is any class constructor\n return (target: any) => {\n // Mark @injectable so Inversify v7 reads the ctor design:paramtypes (inject-by-type). Scope\n // (transient) comes from buildFrameworkModule's .inTransientScope(), not from here.\n injectable()(target);\n frameworkRegistry.push(new FrameworkBinding(target, target, 'transient'));\n return target;\n };\n}\n\n/**\n * Register a {@link Provider} subclass as the DI token that hands out `target` instances.\n *\n * The provider caches nothing; `target`'s own binding scope decides whether callers share one\n * instance (provideFrameworkSingleton -> lazy singleton) or get a fresh one each `get()`\n * (provideFrameworkTransient -> 1-to-many).\n *\n * The provider itself is a singleton — it holds only the resolve-lambda.\n *\n * `Provider<T>` is erased at runtime and cannot be its own token, so name one after T. The DI-graph\n * analyzer reads `target` from HERE, which is why it can draw `Consumer -> T` with no provider box:\n * a Provider is DI plumbing, not wiring anyone needs to see.\n *\n * ```typescript\n * // webpieces-disable no-symbol-di-tokens -- Provider<T> is erased at runtime; T names the token\n * export const PROXY_CLIENT_PROVIDER = Symbol.for('ProxyClientProvider');\n * bindFrameworkProvider(PROXY_CLIENT_PROVIDER, NodeProxyClient);\n *\n * constructor(@inject(PROXY_CLIENT_PROVIDER) private readonly provider: Provider<NodeProxyClient>) {}\n * ```\n */\n// webpieces-disable no-function-outside-class -- registry side-effect, called at module scope beside the decorators\nexport function bindFrameworkProvider(token: ServiceIdentifier, target: AnyCtor): void {\n frameworkProviderRegistry.push(new FrameworkProviderBinding(token, target));\n}\n\n/**\n * Build a ContainerModule binding every provideFrameworkSingleton(As)/Transient class, then\n * every registered Provider. Load this into the webpieces framework + app containers (the\n * router does this) alongside the client's own buildProviderModule().\n */\nexport function buildFrameworkModule(): ContainerModule {\n return new ContainerModule((options: ContainerModuleLoadOptions) => {\n for (const binding of frameworkRegistry) {\n const bindTo = options.bind(binding.serviceIdentifier).to(binding.target);\n if (binding.scope === 'transient') {\n bindTo.inTransientScope();\n } else {\n bindTo.inSingletonScope();\n }\n }\n for (const binding of frameworkProviderRegistry) {\n // toDynamicValue so the provider closes over the ResolutionContext. Each get() then\n // re-resolves `target`, letting TARGET's scope decide shared-vs-fresh.\n options\n .bind(binding.token)\n .toDynamicValue((context: ResolutionContext) =>\n new Provider(() => context.get(binding.target)))\n .inSingletonScope();\n }\n });\n}\n"]}
|
package/src/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { RequestContext } from './RequestContext';
|
|
2
2
|
export { RequestContextApiCallContext } from './RequestContextApiCallContext';
|
|
3
3
|
export { HttpRequest } from './HttpRequest';
|
|
4
|
-
export {
|
|
4
|
+
export { provideSingletonDefaultForApi } from './provide';
|
|
5
5
|
export { Provider } from './provide';
|
|
6
6
|
export { provideFrameworkSingleton, provideFrameworkSingletonDefaultForApi, provideFrameworkTransient, bindFrameworkProvider, buildFrameworkModule, } from './frameworkProvide';
|
|
7
7
|
export type { FrameworkScope } from './frameworkProvide';
|
package/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RequestContextReader = exports.RequestContextHeaders = exports.buildFrameworkModule = exports.bindFrameworkProvider = exports.provideFrameworkTransient = exports.provideFrameworkSingletonDefaultForApi = exports.provideFrameworkSingleton = exports.Provider = exports.
|
|
3
|
+
exports.RequestContextReader = exports.RequestContextHeaders = exports.buildFrameworkModule = exports.bindFrameworkProvider = exports.provideFrameworkTransient = exports.provideFrameworkSingletonDefaultForApi = exports.provideFrameworkSingleton = exports.Provider = exports.provideSingletonDefaultForApi = exports.HttpRequest = exports.RequestContextApiCallContext = exports.RequestContext = void 0;
|
|
4
4
|
// Context management with AsyncLocalStorage
|
|
5
5
|
var RequestContext_1 = require("./RequestContext");
|
|
6
6
|
Object.defineProperty(exports, "RequestContext", { enumerable: true, get: function () { return RequestContext_1.RequestContext; } });
|
|
@@ -14,9 +14,7 @@ var HttpRequest_1 = require("./HttpRequest");
|
|
|
14
14
|
Object.defineProperty(exports, "HttpRequest", { enumerable: true, get: function () { return HttpRequest_1.HttpRequest; } });
|
|
15
15
|
// DI provider decorators (shared DI seam; http-routing re-exports for back-compat)
|
|
16
16
|
var provide_1 = require("./provide");
|
|
17
|
-
Object.defineProperty(exports, "provideSingleton", { enumerable: true, get: function () { return provide_1.provideSingleton; } });
|
|
18
17
|
Object.defineProperty(exports, "provideSingletonDefaultForApi", { enumerable: true, get: function () { return provide_1.provideSingletonDefaultForApi; } });
|
|
19
|
-
Object.defineProperty(exports, "provideTransient", { enumerable: true, get: function () { return provide_1.provideTransient; } });
|
|
20
18
|
// Guice-style Provider<T> — lazy singleton OR fresh-per-get, decided by T's binding scope.
|
|
21
19
|
var provide_2 = require("./provide");
|
|
22
20
|
Object.defineProperty(exports, "Provider", { enumerable: true, get: function () { return provide_2.Provider; } });
|
package/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-context/src/index.ts"],"names":[],"mappings":";;;AAAA,4CAA4C;AAC5C,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AACvB,oGAAoG;AACpG,uGAAuG;AACvG,4FAA4F;AAC5F,+EAA8E;AAArE,4IAAA,4BAA4B,OAAA;AACrC,mGAAmG;AACnG,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AAEpB,mFAAmF;AACnF,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-context/src/index.ts"],"names":[],"mappings":";;;AAAA,4CAA4C;AAC5C,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AACvB,oGAAoG;AACpG,uGAAuG;AACvG,4FAA4F;AAC5F,+EAA8E;AAArE,4IAAA,4BAA4B,OAAA;AACrC,mGAAmG;AACnG,6CAA4C;AAAnC,0GAAA,WAAW,OAAA;AAEpB,mFAAmF;AACnF,qCAA0D;AAAjD,wHAAA,6BAA6B,OAAA;AACtC,2FAA2F;AAC3F,qCAAqC;AAA5B,mGAAA,QAAQ,OAAA;AACjB,sFAAsF;AACtF,wEAAwE;AACxE,uDAM4B;AALxB,6HAAA,yBAAyB,OAAA;AACzB,0IAAA,sCAAsC,OAAA;AACtC,6HAAA,yBAAyB,OAAA;AACzB,yHAAA,qBAAqB,OAAA;AACrB,wHAAA,oBAAoB,OAAA;AAIxB,mFAAmF;AACnF,yFAAyF;AACzF,yBAAyB;AACzB,EAAE;AACF,8FAA8F;AAC9F,2FAA2F;AAC3F,uDAAuD;AACvD,iEAAgE;AAAvD,8HAAA,qBAAqB,OAAA;AAC9B,oGAAoG;AACpG,+DAA8D;AAArD,4HAAA,oBAAoB,OAAA","sourcesContent":["// Context management with AsyncLocalStorage\nexport { RequestContext } from './RequestContext';\n// SERVER impl of the core-util ApiCallContext seam, bound to RequestContext. Importing it here runs\n// its install() side effect, so LogApiCall (core-util, browser-safe) stamps the real RequestContext on\n// a Node server without importing it. A browser never loads core-context → keeps the no-op.\nexport { RequestContextApiCallContext } from './RequestContextApiCallContext';\n// Transport-neutral request stored in the context (http-routing's request type; re-exported there)\nexport { HttpRequest } from './HttpRequest';\n\n// DI provider decorators (shared DI seam; http-routing re-exports for back-compat)\nexport { provideSingletonDefaultForApi } from './provide';\n// Guice-style Provider<T> — lazy singleton OR fresh-per-get, decided by T's binding scope.\nexport { Provider } from './provide';\n// Framework-only DI registry (packages/** use these; keeps framework classes out of a\n// client's buildProviderModule() global scan). See frameworkProvide.ts.\nexport {\n provideFrameworkSingleton,\n provideFrameworkSingletonDefaultForApi,\n provideFrameworkTransient,\n bindFrameworkProvider,\n buildFrameworkModule,\n} from './frameworkProvide';\nexport type { FrameworkScope } from './frameworkProvide';\n\n// Outbound headers for a SERVER: reads RequestContext directly, fails fast outside\n// RequestContext.run(...). Server-side clients (http-client-node, cloudtasks-client) and\n// http-routing use THIS.\n//\n// ContextMgr is deliberately NOT re-exported. It is the browser's answer (an app-held store),\n// and only @webpieces/http-client-browser may name it — importing it here would let a node\n// package reach for a ContextReader it has no use for.\nexport { RequestContextHeaders } from './RequestContextHeaders';\n// The browser store's server counterpart, still used by the logging packages + http-server filters.\nexport { RequestContextReader } from './RequestContextReader';\n"]}
|
package/src/provide.d.ts
CHANGED
|
@@ -5,22 +5,14 @@ import type { ServiceIdentifier } from 'inversify';
|
|
|
5
5
|
*
|
|
6
6
|
* These live in @webpieces/core-context — the lowest package that already owns
|
|
7
7
|
* request-scoped context — so libraries (cloudtasks-client, http-client, …) can
|
|
8
|
-
* register
|
|
9
|
-
* package. http-routing re-exports them for back-compat.
|
|
10
|
-
*/
|
|
11
|
-
/**
|
|
12
|
-
* Provides a singleton-scoped dependency.
|
|
13
|
-
* When called without arguments, the decorated class binds to itself.
|
|
8
|
+
* register bindings WITHOUT depending on the server-side @webpieces/http-routing package.
|
|
14
9
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* }
|
|
21
|
-
* ```
|
|
10
|
+
* A plain concrete singleton no longer needs a decorator here: annotate it
|
|
11
|
+
* `@injectable(bindingScopeValues.Singleton)` (from `inversify`) and let the app container's
|
|
12
|
+
* autobind self-bind it on first resolve (inject-by-type). The decorators BELOW cover the two
|
|
13
|
+
* cases autobind cannot: binding an interface/Symbol TOKEN to a default impl, and the
|
|
14
|
+
* framework-registry variants (see frameworkProvide.ts).
|
|
22
15
|
*/
|
|
23
|
-
export declare function provideSingleton(): ClassDecorator;
|
|
24
16
|
/**
|
|
25
17
|
* Marks this class as the DEFAULT (overridable) singleton implementation OF a contract token
|
|
26
18
|
* (a Symbol or an abstract class). Binds `token -> thisClass` as a singleton. Guice's
|
|
@@ -41,19 +33,6 @@ export declare function provideSingleton(): ClassDecorator;
|
|
|
41
33
|
* ```
|
|
42
34
|
*/
|
|
43
35
|
export declare function provideSingletonDefaultForApi<T>(serviceIdentifier: ServiceIdentifier<T>): ClassDecorator;
|
|
44
|
-
/**
|
|
45
|
-
* Provides a transient-scoped dependency (new instance every time).
|
|
46
|
-
* When called without arguments, the decorated class binds to itself.
|
|
47
|
-
*
|
|
48
|
-
* Usage:
|
|
49
|
-
* ```typescript
|
|
50
|
-
* @provideTransient()
|
|
51
|
-
* export class TransientController {
|
|
52
|
-
* // ...
|
|
53
|
-
* }
|
|
54
|
-
* ```
|
|
55
|
-
*/
|
|
56
|
-
export declare function provideTransient(): ClassDecorator;
|
|
57
36
|
/**
|
|
58
37
|
* Provider<T> — Guice's object-oriented `Provider<T>`, which inversify does not have.
|
|
59
38
|
*
|
package/src/provide.js
CHANGED
|
@@ -1,37 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Provider = void 0;
|
|
4
|
-
exports.provideSingleton = provideSingleton;
|
|
5
4
|
exports.provideSingletonDefaultForApi = provideSingletonDefaultForApi;
|
|
6
|
-
exports.provideTransient = provideTransient;
|
|
7
5
|
require("reflect-metadata");
|
|
8
6
|
const binding_decorators_1 = require("@inversifyjs/binding-decorators");
|
|
7
|
+
const inversify_1 = require("inversify");
|
|
9
8
|
/**
|
|
10
9
|
* DI provider decorators (the lightweight DI seam shared across webpieces).
|
|
11
10
|
*
|
|
12
11
|
* These live in @webpieces/core-context — the lowest package that already owns
|
|
13
12
|
* request-scoped context — so libraries (cloudtasks-client, http-client, …) can
|
|
14
|
-
* register
|
|
15
|
-
* package. http-routing re-exports them for back-compat.
|
|
16
|
-
*/
|
|
17
|
-
/**
|
|
18
|
-
* Provides a singleton-scoped dependency.
|
|
19
|
-
* When called without arguments, the decorated class binds to itself.
|
|
13
|
+
* register bindings WITHOUT depending on the server-side @webpieces/http-routing package.
|
|
20
14
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* }
|
|
27
|
-
* ```
|
|
15
|
+
* A plain concrete singleton no longer needs a decorator here: annotate it
|
|
16
|
+
* `@injectable(bindingScopeValues.Singleton)` (from `inversify`) and let the app container's
|
|
17
|
+
* autobind self-bind it on first resolve (inject-by-type). The decorators BELOW cover the two
|
|
18
|
+
* cases autobind cannot: binding an interface/Symbol TOKEN to a default impl, and the
|
|
19
|
+
* framework-registry variants (see frameworkProvide.ts).
|
|
28
20
|
*/
|
|
29
|
-
function provideSingleton() {
|
|
30
|
-
// webpieces-disable no-any-unknown -- decorator target is any class constructor
|
|
31
|
-
return (target) => {
|
|
32
|
-
return (0, binding_decorators_1.provide)(target, (bind) => bind.inSingletonScope())(target);
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
21
|
/**
|
|
36
22
|
* Marks this class as the DEFAULT (overridable) singleton implementation OF a contract token
|
|
37
23
|
* (a Symbol or an abstract class). Binds `token -> thisClass` as a singleton. Guice's
|
|
@@ -53,28 +39,11 @@ function provideSingleton() {
|
|
|
53
39
|
*/
|
|
54
40
|
// webpieces-disable no-function-outside-class -- a decorator factory cannot be a class method
|
|
55
41
|
function provideSingletonDefaultForApi(serviceIdentifier) {
|
|
56
|
-
return (0, binding_decorators_1.provide)(serviceIdentifier, (bind) => bind.inSingletonScope());
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Provides a transient-scoped dependency (new instance every time).
|
|
60
|
-
* When called without arguments, the decorated class binds to itself.
|
|
61
|
-
*
|
|
62
|
-
* Usage:
|
|
63
|
-
* ```typescript
|
|
64
|
-
* @provideTransient()
|
|
65
|
-
* export class TransientController {
|
|
66
|
-
* // ...
|
|
67
|
-
* }
|
|
68
|
-
* ```
|
|
69
|
-
*/
|
|
70
|
-
function provideTransient() {
|
|
71
42
|
// webpieces-disable no-any-unknown -- decorator target is any class constructor
|
|
72
43
|
return (target) => {
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// webpieces-disable no-any-unknown -- inversify's own fluent-syntax generic for a self-binding
|
|
77
|
-
return (0, binding_decorators_1.provide)(target, (bind) => bind.inTransientScope())(target);
|
|
44
|
+
// Mark @injectable so the impl's ctor design:paramtypes are read when bound via .to(target).
|
|
45
|
+
(0, inversify_1.injectable)()(target);
|
|
46
|
+
return (0, binding_decorators_1.provide)(serviceIdentifier, (bind) => bind.inSingletonScope())(target);
|
|
78
47
|
};
|
|
79
48
|
}
|
|
80
49
|
/**
|
package/src/provide.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provide.js","sourceRoot":"","sources":["../../../../../packages/core/core-context/src/provide.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"provide.js","sourceRoot":"","sources":["../../../../../packages/core/core-context/src/provide.ts"],"names":[],"mappings":";;;AAuCA,sEAOC;AA9CD,4BAA0B;AAC1B,wEAA0D;AAC1D,yCAAuC;AAGvC;;;;;;;;;;;;GAYG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,8FAA8F;AAC9F,SAAgB,6BAA6B,CAAI,iBAAuC;IACpF,gFAAgF;IAChF,OAAO,CAAC,MAAW,EAAE,EAAE;QACnB,6FAA6F;QAC7F,IAAA,sBAAU,GAAE,CAAC,MAAM,CAAC,CAAC;QACrB,OAAO,IAAA,4BAAO,EAAC,iBAAiB,EAAE,CAAC,IAAiC,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IAC9G,CAAC,CAAC;AACN,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAa,QAAQ;IACY;IAA7B,YAA6B,OAAgB;QAAhB,YAAO,GAAP,OAAO,CAAS;IAAG,CAAC;IAEjD,GAAG;QACC,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;IAC1B,CAAC;CACJ;AAND,4BAMC","sourcesContent":["import 'reflect-metadata';\nimport { provide } from '@inversifyjs/binding-decorators';\nimport { injectable } from 'inversify';\nimport type { BindInWhenOnFluentSyntax, ServiceIdentifier } from 'inversify';\n\n/**\n * DI provider decorators (the lightweight DI seam shared across webpieces).\n *\n * These live in @webpieces/core-context — the lowest package that already owns\n * request-scoped context — so libraries (cloudtasks-client, http-client, …) can\n * register bindings WITHOUT depending on the server-side @webpieces/http-routing package.\n *\n * A plain concrete singleton no longer needs a decorator here: annotate it\n * `@injectable(bindingScopeValues.Singleton)` (from `inversify`) and let the app container's\n * autobind self-bind it on first resolve (inject-by-type). The decorators BELOW cover the two\n * cases autobind cannot: binding an interface/Symbol TOKEN to a default impl, and the\n * framework-registry variants (see frameworkProvide.ts).\n */\n\n/**\n * Marks this class as the DEFAULT (overridable) singleton implementation OF a contract token\n * (a Symbol or an abstract class). Binds `token -> thisClass` as a singleton. Guice's\n * `@ImplementedBy`, done impl-side so the api never imports the impl (no cycle).\n *\n * An app overrides the default via appOverrides, same idiom as AuthConfig:\n * `(await options.rebind(TOKEN)).to(OtherImpl)`.\n *\n * The DI-graph designer reads this in pass 1, so `@inject(TOKEN)` renders as `TOKEN (thisClass)`\n * and expands this class's own dependencies instead of dead-ending as unresolved.\n *\n * Usage:\n * ```typescript\n * import { SOME_API_TOKEN } from '@myorg/some-api';\n *\n * @provideSingletonDefaultForApi(SOME_API_TOKEN)\n * export class SomeApiImpl { ... }\n * ```\n */\n// webpieces-disable no-function-outside-class -- a decorator factory cannot be a class method\nexport function provideSingletonDefaultForApi<T>(serviceIdentifier: ServiceIdentifier<T>): ClassDecorator {\n // webpieces-disable no-any-unknown -- decorator target is any class constructor\n return (target: any) => {\n // Mark @injectable so the impl's ctor design:paramtypes are read when bound via .to(target).\n injectable()(target);\n return provide(serviceIdentifier, (bind: BindInWhenOnFluentSyntax<T>) => bind.inSingletonScope())(target);\n };\n}\n\n/**\n * Provider<T> — Guice's object-oriented `Provider<T>`, which inversify does not have.\n *\n * Inversify's own `Provider<T>` is a FUNCTION type `(...args) => Promise<T>` and its\n * `toProvider()` binding is deprecated (\"Providers will be removed in v8\"), so we model\n * Guice's seam ourselves.\n *\n * It caches NOTHING, because `ResolutionContext.get()` already applies the BOUND SCOPE of `T`:\n *\n * T bound @provideFrameworkSingleton -> every get() returns the SAME instance, built on the\n * first call. That is a LAZY SINGLETON.\n * T bound @provideFrameworkTransient -> every get() builds a NEW instance. That is 1-to-many.\n *\n * A provider that cached internally would break the transient case outright: the second get()\n * would hand back the first instance.\n *\n * `get()` is SYNCHRONOUS, like Guice's. An async `get()` would force every consumer (e.g.\n * `ClientHttpFactory.createRpcClient`) to become async, and neither Angular's `useFactory` nor\n * inversify's `toDynamicValue` can await.\n *\n * TypeScript erases generics, so `Provider<T>` has NO runtime identity and cannot itself be a DI\n * token. Register it against a Symbol naming T, with {@link bindFrameworkProvider}, and inject it\n * by that token — the declared type is what a reader needs, the Symbol is what inversify needs:\n *\n * ```typescript\n * // webpieces-disable no-symbol-di-tokens -- Provider<T> is erased at runtime; T names the token\n * export const TASK_PROXY_PROVIDER = Symbol.for('TaskProxyClientProvider');\n * bindFrameworkProvider(TASK_PROXY_PROVIDER, TaskProxyClient);\n *\n * constructor(@inject(TASK_PROXY_PROVIDER) private readonly provider: Provider<TaskProxyClient>) {}\n * ```\n *\n * Inject a Provider when you need a dependency LATER or REPEATEDLY rather than at construction\n * time — a lazily-created singleton, or a fresh instance per call.\n */\nexport class Provider<T> {\n constructor(private readonly resolve: () => T) {}\n\n get(): T {\n return this.resolve();\n }\n}\n"]}
|