@microsoft/fast-element 2.0.0-beta.2 → 2.0.0-beta.20
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/CHANGELOG.json +488 -0
- package/CHANGELOG.md +180 -1
- package/dist/dts/components/attributes.d.ts +15 -0
- package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +74 -28
- package/dist/dts/components/fast-definitions.d.ts +41 -9
- package/dist/dts/components/fast-element.d.ts +14 -26
- package/dist/dts/components/hydration.d.ts +14 -0
- package/dist/{esm/observation/behavior.js → dts/components/install-hydration.d.ts} +0 -0
- package/dist/dts/context.d.ts +1 -1
- package/dist/dts/di/di.d.ts +894 -0
- package/dist/dts/dom-policy.d.ts +68 -0
- package/dist/dts/dom.d.ts +100 -0
- package/dist/dts/index.d.ts +5 -4
- package/dist/dts/index.rollup.d.ts +0 -1
- package/dist/dts/index.rollup.debug.d.ts +0 -1
- package/dist/dts/interfaces.d.ts +60 -79
- package/dist/dts/observation/observable.d.ts +99 -54
- package/dist/dts/pending-task.d.ts +20 -0
- package/dist/dts/platform.d.ts +7 -0
- package/dist/dts/polyfills.d.ts +0 -8
- package/dist/dts/state/exports.d.ts +3 -0
- package/dist/dts/state/reactive.d.ts +8 -0
- package/dist/dts/state/state.d.ts +141 -0
- package/dist/dts/state/visitor.d.ts +6 -0
- package/dist/dts/state/watch.d.ts +10 -0
- package/dist/dts/styles/css-directive.d.ts +2 -2
- package/dist/dts/styles/css.d.ts +0 -5
- package/dist/dts/styles/element-styles.d.ts +10 -17
- package/dist/dts/styles/host.d.ts +68 -0
- package/dist/dts/styles/style-strategy.d.ts +42 -0
- package/dist/dts/templating/binding-signal.d.ts +12 -27
- package/dist/dts/templating/binding-two-way.d.ts +22 -37
- package/dist/dts/templating/binding.d.ts +76 -208
- package/dist/dts/templating/children.d.ts +1 -1
- package/dist/dts/templating/compiler.d.ts +11 -13
- package/dist/dts/templating/html-directive.d.ts +91 -97
- package/dist/dts/templating/node-observation.d.ts +15 -6
- package/dist/dts/templating/ref.d.ts +7 -11
- package/dist/dts/templating/render.d.ts +296 -0
- package/dist/dts/templating/repeat.d.ts +23 -34
- package/dist/dts/templating/slotted.d.ts +1 -1
- package/dist/dts/templating/template.d.ts +92 -14
- package/dist/dts/templating/view.d.ts +81 -11
- package/dist/dts/templating/when.d.ts +3 -3
- package/dist/dts/testing/exports.d.ts +3 -0
- package/dist/dts/testing/fakes.d.ts +14 -0
- package/dist/dts/testing/fixture.d.ts +84 -0
- package/dist/dts/testing/timeout.d.ts +7 -0
- package/dist/dts/utilities.d.ts +53 -18
- package/dist/esm/components/attributes.js +28 -5
- package/dist/esm/components/{controller.js → element-controller.js} +239 -137
- package/dist/esm/components/fast-definitions.js +38 -30
- package/dist/esm/components/fast-element.js +27 -16
- package/dist/esm/components/hydration.js +35 -0
- package/dist/esm/components/install-hydration.js +2 -0
- package/dist/esm/context.js +5 -1
- package/dist/esm/debug.js +40 -5
- package/dist/esm/di/di.js +1430 -0
- package/dist/esm/dom-policy.js +337 -0
- package/dist/esm/dom.js +101 -0
- package/dist/esm/index.js +4 -2
- package/dist/esm/index.rollup.debug.js +3 -1
- package/dist/esm/index.rollup.js +3 -1
- package/dist/esm/interfaces.js +52 -0
- package/dist/esm/observation/arrays.js +303 -2
- package/dist/esm/observation/observable.js +88 -142
- package/dist/esm/observation/update-queue.js +2 -2
- package/dist/esm/pending-task.js +16 -0
- package/dist/esm/platform.js +27 -2
- package/dist/esm/polyfills.js +3 -61
- package/dist/esm/state/exports.js +3 -0
- package/dist/esm/state/reactive.js +34 -0
- package/dist/esm/state/state.js +148 -0
- package/dist/esm/state/visitor.js +28 -0
- package/dist/esm/state/watch.js +36 -0
- package/dist/esm/styles/css.js +4 -9
- package/dist/esm/styles/element-styles.js +14 -33
- package/dist/esm/styles/host.js +1 -0
- package/dist/esm/styles/style-strategy.js +1 -0
- package/dist/esm/templating/binding-signal.js +67 -62
- package/dist/esm/templating/binding-two-way.js +72 -39
- package/dist/esm/templating/binding.js +142 -286
- package/dist/esm/templating/children.js +8 -4
- package/dist/esm/templating/compiler.js +59 -43
- package/dist/esm/templating/html-directive.js +56 -75
- package/dist/esm/templating/node-observation.js +20 -13
- package/dist/esm/templating/ref.js +4 -12
- package/dist/esm/templating/render.js +402 -0
- package/dist/esm/templating/repeat.js +88 -75
- package/dist/esm/templating/template.js +132 -60
- package/dist/esm/templating/view.js +113 -29
- package/dist/esm/templating/when.js +5 -4
- package/dist/esm/testing/exports.js +3 -0
- package/dist/esm/testing/fakes.js +107 -0
- package/dist/esm/testing/fixture.js +86 -0
- package/dist/esm/testing/timeout.js +24 -0
- package/dist/esm/utilities.js +95 -95
- package/dist/fast-element.api.json +9487 -8326
- package/dist/fast-element.d.ts +847 -644
- package/dist/fast-element.debug.js +1993 -1166
- package/dist/fast-element.debug.min.js +1 -1
- package/dist/fast-element.js +1903 -1111
- package/dist/fast-element.min.js +1 -1
- package/dist/fast-element.untrimmed.d.ts +911 -701
- package/docs/api-report.md +329 -252
- package/package.json +38 -16
- package/dist/dts/hooks.d.ts +0 -20
- package/dist/dts/observation/behavior.d.ts +0 -19
- package/dist/dts/observation/splice-strategies.d.ts +0 -13
- package/dist/dts/templating/dom.d.ts +0 -41
- package/dist/esm/hooks.js +0 -32
- package/dist/esm/observation/splice-strategies.js +0 -400
- package/dist/esm/templating/dom.js +0 -49
|
@@ -0,0 +1,1430 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Big thanks to https://github.com/fkleuver and the https://github.com/aurelia/aurelia project
|
|
12
|
+
* for the bulk of this code and many of the associated tests.
|
|
13
|
+
*/
|
|
14
|
+
import { Context } from "../context.js";
|
|
15
|
+
import "../interfaces.js";
|
|
16
|
+
import { Metadata } from "../metadata.js";
|
|
17
|
+
import { emptyArray, FAST } from "../platform.js";
|
|
18
|
+
/**
|
|
19
|
+
* A utility class used that constructs and registers resolvers for a dependency
|
|
20
|
+
* injection container. Supports a standard set of object lifetimes.
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
23
|
+
export class ResolverBuilder {
|
|
24
|
+
/**
|
|
25
|
+
*
|
|
26
|
+
* @param container - The container to create resolvers for.
|
|
27
|
+
* @param key - The key to register resolvers under.
|
|
28
|
+
*/
|
|
29
|
+
constructor(container, key) {
|
|
30
|
+
this.container = container;
|
|
31
|
+
this.key = key;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Creates a resolver for an existing object instance.
|
|
35
|
+
* @param value - The instance to resolve.
|
|
36
|
+
* @returns The resolver.
|
|
37
|
+
*/
|
|
38
|
+
instance(value) {
|
|
39
|
+
return this.registerResolver(0 /* ResolverStrategy.instance */, value);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Creates a resolver that enforces a singleton lifetime.
|
|
43
|
+
* @param value - The type to create and cache the singleton for.
|
|
44
|
+
* @returns The resolver.
|
|
45
|
+
*/
|
|
46
|
+
singleton(value) {
|
|
47
|
+
return this.registerResolver(1 /* ResolverStrategy.singleton */, value);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Creates a resolver that creates a new instance for every dependency request.
|
|
51
|
+
* @param value - The type to create instances of.
|
|
52
|
+
* @returns - The resolver.
|
|
53
|
+
*/
|
|
54
|
+
transient(value) {
|
|
55
|
+
return this.registerResolver(2 /* ResolverStrategy.transient */, value);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Creates a resolver that invokes a callback function for every dependency resolution
|
|
59
|
+
* request, allowing custom logic to return the dependency.
|
|
60
|
+
* @param value - The callback to call during resolution.
|
|
61
|
+
* @returns The resolver.
|
|
62
|
+
*/
|
|
63
|
+
callback(value) {
|
|
64
|
+
return this.registerResolver(3 /* ResolverStrategy.callback */, value);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Creates a resolver that invokes a callback function the first time that a dependency
|
|
68
|
+
* resolution is requested. The returned value is then cached and provided for all
|
|
69
|
+
* subsequent requests.
|
|
70
|
+
* @param value - The callback to call during the first resolution.
|
|
71
|
+
* @returns The resolver.
|
|
72
|
+
*/
|
|
73
|
+
cachedCallback(value) {
|
|
74
|
+
return this.registerResolver(3 /* ResolverStrategy.callback */, cacheCallbackResult(value));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Aliases the current key to a different key.
|
|
78
|
+
* @param destinationKey - The key to point the alias to.
|
|
79
|
+
* @returns The resolver.
|
|
80
|
+
*/
|
|
81
|
+
aliasTo(destinationKey) {
|
|
82
|
+
return this.registerResolver(5 /* ResolverStrategy.alias */, destinationKey);
|
|
83
|
+
}
|
|
84
|
+
registerResolver(strategy, state) {
|
|
85
|
+
const { container, key } = this;
|
|
86
|
+
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
|
|
87
|
+
this.container = this.key = (void 0);
|
|
88
|
+
return container.registerResolver(key, new ResolverImpl(key, strategy, state));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function cloneArrayWithPossibleProps(source) {
|
|
92
|
+
const clone = source.slice();
|
|
93
|
+
const keys = Object.keys(source);
|
|
94
|
+
const len = keys.length;
|
|
95
|
+
let key;
|
|
96
|
+
for (let i = 0; i < len; ++i) {
|
|
97
|
+
key = keys[i];
|
|
98
|
+
if (!isArrayIndex(key)) {
|
|
99
|
+
clone[key] = source[key];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return clone;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* A set of default resolvers useful in configuring a container.
|
|
106
|
+
* @public
|
|
107
|
+
*/
|
|
108
|
+
export const DefaultResolver = Object.freeze({
|
|
109
|
+
/**
|
|
110
|
+
* Disables auto-registration and throws for all un-registered dependencies.
|
|
111
|
+
* @param key - The key to create the resolver for.
|
|
112
|
+
*/
|
|
113
|
+
none(key) {
|
|
114
|
+
throw FAST.error(1512 /* Message.noDefaultResolver */, { key });
|
|
115
|
+
},
|
|
116
|
+
/**
|
|
117
|
+
* Provides default singleton resolution behavior during auto-registration.
|
|
118
|
+
* @param key - The key to create the resolver for.
|
|
119
|
+
* @returns The resolver.
|
|
120
|
+
*/
|
|
121
|
+
singleton(key) {
|
|
122
|
+
return new ResolverImpl(key, 1 /* ResolverStrategy.singleton */, key);
|
|
123
|
+
},
|
|
124
|
+
/**
|
|
125
|
+
* Provides default transient resolution behavior during auto-registration.
|
|
126
|
+
* @param key - The key to create the resolver for.
|
|
127
|
+
* @returns The resolver.
|
|
128
|
+
*/
|
|
129
|
+
transient(key) {
|
|
130
|
+
return new ResolverImpl(key, 2 /* ResolverStrategy.transient */, key);
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
/**
|
|
134
|
+
* Configuration for a dependency injection container.
|
|
135
|
+
* @public
|
|
136
|
+
*/
|
|
137
|
+
export const ContainerConfiguration = Object.freeze({
|
|
138
|
+
/**
|
|
139
|
+
* The default configuration used when creating a DOM-disconnected container.
|
|
140
|
+
* @remarks
|
|
141
|
+
* The default creates a root container, with no parent container. It does not handle
|
|
142
|
+
* owner requests and it uses singleton resolution behavior for auto-registration.
|
|
143
|
+
*/
|
|
144
|
+
default: Object.freeze({
|
|
145
|
+
parentLocator: () => null,
|
|
146
|
+
asyncRegistrationLocator: () => __awaiter(void 0, void 0, void 0, function* () { return null; }),
|
|
147
|
+
responsibleForOwnerRequests: false,
|
|
148
|
+
defaultResolver: DefaultResolver.singleton,
|
|
149
|
+
}),
|
|
150
|
+
});
|
|
151
|
+
function createContext(nameConfigOrCallback, configuror) {
|
|
152
|
+
const configure = typeof nameConfigOrCallback === "function" ? nameConfigOrCallback : configuror;
|
|
153
|
+
const friendlyName = typeof nameConfigOrCallback === "string"
|
|
154
|
+
? nameConfigOrCallback
|
|
155
|
+
: nameConfigOrCallback && "friendlyName" in nameConfigOrCallback
|
|
156
|
+
? nameConfigOrCallback.friendlyName || defaultFriendlyName
|
|
157
|
+
: defaultFriendlyName;
|
|
158
|
+
const respectConnection = typeof nameConfigOrCallback === "string"
|
|
159
|
+
? false
|
|
160
|
+
: nameConfigOrCallback && "respectConnection" in nameConfigOrCallback
|
|
161
|
+
? nameConfigOrCallback.respectConnection || false
|
|
162
|
+
: false;
|
|
163
|
+
const Interface = function (target, property, index) {
|
|
164
|
+
if (target == null || new.target !== undefined) {
|
|
165
|
+
throw FAST.error(1501 /* Message.noRegistrationForContext */, { name: Interface.name });
|
|
166
|
+
}
|
|
167
|
+
if (property) {
|
|
168
|
+
DI.defineProperty(target, property, Interface, respectConnection);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
const annotationParamtypes = Metadata.getOrCreateAnnotationParamTypes(target);
|
|
172
|
+
annotationParamtypes[index] = Interface;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
Interface.$isInterface = true;
|
|
176
|
+
Reflect.defineProperty(Interface, "name", {
|
|
177
|
+
value: friendlyName !== null && friendlyName !== void 0 ? friendlyName : defaultFriendlyName,
|
|
178
|
+
});
|
|
179
|
+
if (configure != null) {
|
|
180
|
+
Interface.register = function (container, key) {
|
|
181
|
+
return configure(new ResolverBuilder(container, key !== null && key !== void 0 ? key : Interface));
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
Interface.toString = function toString() {
|
|
185
|
+
return `DIContext<${Interface.name}>`;
|
|
186
|
+
};
|
|
187
|
+
return Interface;
|
|
188
|
+
}
|
|
189
|
+
const dependencyLookup = new Map();
|
|
190
|
+
let rootDOMContainer = null;
|
|
191
|
+
let nonRootDOMContainerCount = 0;
|
|
192
|
+
/**
|
|
193
|
+
* The gateway to dependency injection APIs.
|
|
194
|
+
* @public
|
|
195
|
+
*/
|
|
196
|
+
export const DI = Object.freeze({
|
|
197
|
+
/**
|
|
198
|
+
* Installs dependency injection as the default strategy for handling
|
|
199
|
+
* all calls to Context.request.
|
|
200
|
+
* @param fallback - Creates a container if one cannot be found.
|
|
201
|
+
*/
|
|
202
|
+
installAsContextRequestStrategy(fallback) {
|
|
203
|
+
Context.setDefaultRequestStrategy((target, context, callback) => {
|
|
204
|
+
const container = DI.findResponsibleContainer(target, fallback);
|
|
205
|
+
callback(container.get(context));
|
|
206
|
+
});
|
|
207
|
+
},
|
|
208
|
+
/**
|
|
209
|
+
* Creates a new dependency injection container.
|
|
210
|
+
* @param config - The configuration for the container.
|
|
211
|
+
* @returns A newly created dependency injection container.
|
|
212
|
+
*/
|
|
213
|
+
createContainer(config) {
|
|
214
|
+
return new ContainerImpl(null, Object.assign({}, ContainerConfiguration.default, config));
|
|
215
|
+
},
|
|
216
|
+
/**
|
|
217
|
+
* Finds the dependency injection container responsible for providing dependencies
|
|
218
|
+
* to the specified node.
|
|
219
|
+
* @param target - The node to find the responsible container for.
|
|
220
|
+
* @param fallback - Creates a container if one cannot be found.
|
|
221
|
+
* @returns The container responsible for providing dependencies to the node.
|
|
222
|
+
* @remarks
|
|
223
|
+
* This will be the same as the parent container if the specified node
|
|
224
|
+
* does not itself host a container configured with responsibleForOwnerRequests.
|
|
225
|
+
*/
|
|
226
|
+
findResponsibleContainer(target, fallback) {
|
|
227
|
+
const owned = target.$$container$$;
|
|
228
|
+
if (owned && owned.responsibleForOwnerRequests) {
|
|
229
|
+
return owned;
|
|
230
|
+
}
|
|
231
|
+
return DI.findParentContainer(target, fallback);
|
|
232
|
+
},
|
|
233
|
+
/**
|
|
234
|
+
* Find the dependency injection container up the DOM tree from this node.
|
|
235
|
+
* @param target - The node to find the parent container for.
|
|
236
|
+
* @param fallback - Creates a container if one cannot be found.
|
|
237
|
+
* @returns The parent container of this node.
|
|
238
|
+
* @remarks
|
|
239
|
+
* This will be the same as the responsible container if the specified node
|
|
240
|
+
* does not itself host a container configured with responsibleForOwnerRequests.
|
|
241
|
+
*/
|
|
242
|
+
findParentContainer(target, fallback) {
|
|
243
|
+
// NOTE: If there are no node-specific containers in existence other
|
|
244
|
+
// than the root, then we can bypass raising events and instead just grab
|
|
245
|
+
// the reference to the root container because we know it's the parent
|
|
246
|
+
// for this node.
|
|
247
|
+
if (nonRootDOMContainerCount < 1) {
|
|
248
|
+
return fallback ? fallback() : DI.getOrCreateDOMContainer();
|
|
249
|
+
}
|
|
250
|
+
// NOTE: If even one node-specific container has been created then we can
|
|
251
|
+
// no longer assume that the parent container for the target is the root
|
|
252
|
+
// and we must dispatch a context event in order to find the parent
|
|
253
|
+
// container in the DOM.
|
|
254
|
+
let container;
|
|
255
|
+
Context.dispatch(target, DOMContainer, value => (container = value));
|
|
256
|
+
// NOTE: If there are node-specific containers but there doesn't happen to
|
|
257
|
+
// be one that is a parent to the target node, then we still need to fall
|
|
258
|
+
// back to the root container.
|
|
259
|
+
return container !== null && container !== void 0 ? container : (fallback ? fallback() : DI.getOrCreateDOMContainer());
|
|
260
|
+
},
|
|
261
|
+
/**
|
|
262
|
+
* Returns a dependency injection container if one is explicitly owned by the specified
|
|
263
|
+
* node. If one is not owned, then a new container is created and assigned to the node.
|
|
264
|
+
* @param target - The node to find or create the container for.
|
|
265
|
+
* @param config - The configuration for the container if one needs to be created.
|
|
266
|
+
* @returns The located or created container.
|
|
267
|
+
* @remarks
|
|
268
|
+
* This API does not search for a responsible or parent container. It looks only for a container
|
|
269
|
+
* directly defined on the specified node and creates one at that location if one does not
|
|
270
|
+
* already exist.
|
|
271
|
+
*/
|
|
272
|
+
getOrCreateDOMContainer(target, config) {
|
|
273
|
+
if (!target) {
|
|
274
|
+
return (rootDOMContainer ||
|
|
275
|
+
(rootDOMContainer = new ContainerImpl(typeof window !== "undefined" ? window : null, Object.assign({}, ContainerConfiguration.default, config, {
|
|
276
|
+
parentLocator: () => null,
|
|
277
|
+
}))));
|
|
278
|
+
}
|
|
279
|
+
let container = target.$$container$$;
|
|
280
|
+
if (container === void 0) {
|
|
281
|
+
// NOTE: Creating a node-specific container de-optimizes container resolution.
|
|
282
|
+
nonRootDOMContainerCount++;
|
|
283
|
+
container = new ContainerImpl(target, Object.assign({}, ContainerConfiguration.default, config, {
|
|
284
|
+
parentLocator: DI.findParentContainer,
|
|
285
|
+
}));
|
|
286
|
+
}
|
|
287
|
+
return container;
|
|
288
|
+
},
|
|
289
|
+
/**
|
|
290
|
+
* Gets the dependency keys representing what is needed to instantiate the specified type.
|
|
291
|
+
* @param Type - The type to get the dependencies for.
|
|
292
|
+
* @returns An array of dependency keys.
|
|
293
|
+
*/
|
|
294
|
+
getDependencies(Type) {
|
|
295
|
+
// Note: Every detail of this getDependencies method is pretty deliberate at the moment, and probably not yet 100% tested from every possible angle,
|
|
296
|
+
// so be careful with making changes here as it can have a huge impact on complex end user apps.
|
|
297
|
+
// Preferably, only make changes to the dependency resolution process via a RFC.
|
|
298
|
+
let dependencies = dependencyLookup.get(Type);
|
|
299
|
+
if (dependencies === void 0) {
|
|
300
|
+
// Type.length is the number of constructor parameters. If this is 0, it could mean the class has an empty constructor
|
|
301
|
+
// but it could also mean the class has no constructor at all (in which case it inherits the constructor from the prototype).
|
|
302
|
+
// Non-zero constructor length + no paramtypes means emitDecoratorMetadata is off, or the class has no decorator.
|
|
303
|
+
// We're not doing anything with the above right now, but it's good to keep in mind for any future issues.
|
|
304
|
+
const inject = Type.inject;
|
|
305
|
+
if (inject === void 0) {
|
|
306
|
+
// design:paramtypes is set by tsc when emitDecoratorMetadata is enabled.
|
|
307
|
+
const designParamtypes = Metadata.getDesignParamTypes(Type);
|
|
308
|
+
// di:paramtypes is set by the parameter decorator from DI.createInterface or by @inject
|
|
309
|
+
const annotationParamtypes = Metadata.getAnnotationParamTypes(Type);
|
|
310
|
+
if (designParamtypes === emptyArray) {
|
|
311
|
+
if (annotationParamtypes === emptyArray) {
|
|
312
|
+
// Only go up the prototype if neither static inject nor any of the paramtypes is defined, as
|
|
313
|
+
// there is no sound way to merge a type's deps with its prototype's deps
|
|
314
|
+
const Proto = Object.getPrototypeOf(Type);
|
|
315
|
+
if (typeof Proto === "function" && Proto !== Function.prototype) {
|
|
316
|
+
dependencies = cloneArrayWithPossibleProps(DI.getDependencies(Proto));
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
dependencies = [];
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
// No design:paramtypes so just use the di:paramtypes
|
|
324
|
+
dependencies = cloneArrayWithPossibleProps(annotationParamtypes);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
else if (annotationParamtypes === emptyArray) {
|
|
328
|
+
// No di:paramtypes so just use the design:paramtypes
|
|
329
|
+
dependencies = cloneArrayWithPossibleProps(designParamtypes);
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
// We've got both, so merge them (in case of conflict on same index, di:paramtypes take precedence)
|
|
333
|
+
dependencies = cloneArrayWithPossibleProps(designParamtypes);
|
|
334
|
+
let len = annotationParamtypes.length;
|
|
335
|
+
let auAnnotationParamtype;
|
|
336
|
+
for (let i = 0; i < len; ++i) {
|
|
337
|
+
auAnnotationParamtype = annotationParamtypes[i];
|
|
338
|
+
if (auAnnotationParamtype !== void 0) {
|
|
339
|
+
dependencies[i] = auAnnotationParamtype;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
const keys = Object.keys(annotationParamtypes);
|
|
343
|
+
len = keys.length;
|
|
344
|
+
let key;
|
|
345
|
+
for (let i = 0; i < len; ++i) {
|
|
346
|
+
key = keys[i];
|
|
347
|
+
if (!isArrayIndex(key)) {
|
|
348
|
+
dependencies[key] = annotationParamtypes[key];
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
// Ignore paramtypes if we have static inject
|
|
355
|
+
dependencies = cloneArrayWithPossibleProps(inject);
|
|
356
|
+
}
|
|
357
|
+
dependencyLookup.set(Type, dependencies);
|
|
358
|
+
}
|
|
359
|
+
return dependencies;
|
|
360
|
+
},
|
|
361
|
+
/**
|
|
362
|
+
* Defines a property on a web component class. The value of this property will
|
|
363
|
+
* be resolved from the dependency injection container responsible for the element
|
|
364
|
+
* instance, based on where it is connected in the DOM.
|
|
365
|
+
* @param target - The target to define the property on.
|
|
366
|
+
* @param propertyName - The name of the property to define.
|
|
367
|
+
* @param key - The dependency injection key.
|
|
368
|
+
* @param respectConnection - Indicates whether or not to update the property value if the
|
|
369
|
+
* hosting component is disconnected and then re-connected at a different location in the DOM.
|
|
370
|
+
* @remarks
|
|
371
|
+
* The respectConnection option is only applicable to elements that descend from FASTElement.
|
|
372
|
+
*/
|
|
373
|
+
defineProperty(target, propertyName, key, respectConnection = false) {
|
|
374
|
+
const diPropertyKey = `$di_${propertyName}`;
|
|
375
|
+
Reflect.defineProperty(target, propertyName, {
|
|
376
|
+
get: function () {
|
|
377
|
+
let value = this[diPropertyKey];
|
|
378
|
+
if (value === void 0) {
|
|
379
|
+
const container = this instanceof Node
|
|
380
|
+
? DI.findResponsibleContainer(this)
|
|
381
|
+
: DI.getOrCreateDOMContainer();
|
|
382
|
+
value = container.get(key);
|
|
383
|
+
this[diPropertyKey] = value;
|
|
384
|
+
if (respectConnection) {
|
|
385
|
+
const notifier = this.$fastController;
|
|
386
|
+
if (!notifier) {
|
|
387
|
+
throw FAST.error(1514 /* Message.connectUpdateRequiresController */);
|
|
388
|
+
}
|
|
389
|
+
const handleChange = () => {
|
|
390
|
+
const newContainer = DI.findResponsibleContainer(this);
|
|
391
|
+
const newValue = newContainer.get(key);
|
|
392
|
+
const oldValue = this[diPropertyKey];
|
|
393
|
+
if (newValue !== oldValue) {
|
|
394
|
+
this[diPropertyKey] = value;
|
|
395
|
+
notifier.notify(propertyName);
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
notifier.subscribe({ handleChange }, "isConnected");
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return value;
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
},
|
|
405
|
+
/**
|
|
406
|
+
* Creates a dependency injection key.
|
|
407
|
+
* @param nameConfigOrCallback - A friendly name for the key or a lambda that configures a
|
|
408
|
+
* default resolution for the dependency.
|
|
409
|
+
* @param configuror - If a friendly name was provided for the first parameter, then an optional
|
|
410
|
+
* lambda that configures a default resolution for the dependency can be provided second.
|
|
411
|
+
* @returns The created key.
|
|
412
|
+
* @remarks
|
|
413
|
+
* The created key can be used as a property decorator or constructor parameter decorator,
|
|
414
|
+
* in addition to its standard use in an inject array or through direct container APIs.
|
|
415
|
+
*/
|
|
416
|
+
createContext,
|
|
417
|
+
/**
|
|
418
|
+
* A decorator that specifies what to inject into its target.
|
|
419
|
+
* @param dependencies - The dependencies to inject.
|
|
420
|
+
* @returns The decorator to be applied to the target class.
|
|
421
|
+
* @remarks
|
|
422
|
+
* The decorator can be used to decorate a class, listing all of the classes dependencies.
|
|
423
|
+
* Or it can be used to decorate a constructor parameter, indicating what to inject for that
|
|
424
|
+
* parameter.
|
|
425
|
+
* Or it can be used for a web component property, indicating what that property should resolve to.
|
|
426
|
+
*/
|
|
427
|
+
inject(...dependencies) {
|
|
428
|
+
return function (target, key, descriptor) {
|
|
429
|
+
if (typeof descriptor === "number") {
|
|
430
|
+
// It's a parameter decorator.
|
|
431
|
+
const annotationParamtypes = Metadata.getOrCreateAnnotationParamTypes(target);
|
|
432
|
+
const dep = dependencies[0];
|
|
433
|
+
if (dep !== void 0) {
|
|
434
|
+
annotationParamtypes[descriptor] = dep;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
else if (key) {
|
|
438
|
+
DI.defineProperty(target, key, dependencies[0]);
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
const annotationParamtypes = descriptor
|
|
442
|
+
? Metadata.getOrCreateAnnotationParamTypes(descriptor.value)
|
|
443
|
+
: Metadata.getOrCreateAnnotationParamTypes(target);
|
|
444
|
+
let dep;
|
|
445
|
+
for (let i = 0; i < dependencies.length; ++i) {
|
|
446
|
+
dep = dependencies[i];
|
|
447
|
+
if (dep !== void 0) {
|
|
448
|
+
annotationParamtypes[i] = dep;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
},
|
|
454
|
+
/**
|
|
455
|
+
* Registers the `target` class as a transient dependency; each time the dependency is resolved
|
|
456
|
+
* a new instance will be created.
|
|
457
|
+
*
|
|
458
|
+
* @param target - The class / constructor function to register as transient.
|
|
459
|
+
* @returns The same class, with a static `register` method that takes a container and returns the appropriate resolver.
|
|
460
|
+
*
|
|
461
|
+
* @example
|
|
462
|
+
* On an existing class
|
|
463
|
+
* ```ts
|
|
464
|
+
* class Foo { }
|
|
465
|
+
* DI.transient(Foo);
|
|
466
|
+
* ```
|
|
467
|
+
*
|
|
468
|
+
* @example
|
|
469
|
+
* Inline declaration
|
|
470
|
+
*
|
|
471
|
+
* ```ts
|
|
472
|
+
* const Foo = DI.transient(class { });
|
|
473
|
+
* // Foo is now strongly typed with register
|
|
474
|
+
* Foo.register(container);
|
|
475
|
+
* ```
|
|
476
|
+
*
|
|
477
|
+
* @public
|
|
478
|
+
*/
|
|
479
|
+
transient(target) {
|
|
480
|
+
target.register = function register(container) {
|
|
481
|
+
const registration = Registration.transient(target, target);
|
|
482
|
+
return registration.register(container);
|
|
483
|
+
};
|
|
484
|
+
target.registerInRequestor = false;
|
|
485
|
+
return target;
|
|
486
|
+
},
|
|
487
|
+
/**
|
|
488
|
+
* Registers the `target` class as a singleton dependency; the class will only be created once. Each
|
|
489
|
+
* consecutive time the dependency is resolved, the same instance will be returned.
|
|
490
|
+
*
|
|
491
|
+
* @param target - The class / constructor function to register as a singleton.
|
|
492
|
+
* @returns The same class, with a static `register` method that takes a container and returns the appropriate resolver.
|
|
493
|
+
* @example
|
|
494
|
+
* On an existing class
|
|
495
|
+
* ```ts
|
|
496
|
+
* class Foo { }
|
|
497
|
+
* DI.singleton(Foo);
|
|
498
|
+
* ```
|
|
499
|
+
*
|
|
500
|
+
* @example
|
|
501
|
+
* Inline declaration
|
|
502
|
+
* ```ts
|
|
503
|
+
* const Foo = DI.singleton(class { });
|
|
504
|
+
* // Foo is now strongly typed with register
|
|
505
|
+
* Foo.register(container);
|
|
506
|
+
* ```
|
|
507
|
+
*
|
|
508
|
+
* @public
|
|
509
|
+
*/
|
|
510
|
+
singleton(target, options = defaultSingletonOptions) {
|
|
511
|
+
target.register = function register(container) {
|
|
512
|
+
const registration = Registration.singleton(target, target);
|
|
513
|
+
return registration.register(container);
|
|
514
|
+
};
|
|
515
|
+
target.registerInRequestor = options.scoped;
|
|
516
|
+
return target;
|
|
517
|
+
},
|
|
518
|
+
});
|
|
519
|
+
/**
|
|
520
|
+
* The key that resolves the dependency injection Container itself.
|
|
521
|
+
* @public
|
|
522
|
+
*/
|
|
523
|
+
export const Container = DI.createContext("Container");
|
|
524
|
+
/**
|
|
525
|
+
* The key that resolves a DOMContainer itself.
|
|
526
|
+
* @public
|
|
527
|
+
*/
|
|
528
|
+
export const DOMContainer = Container;
|
|
529
|
+
/**
|
|
530
|
+
* The key that resolves the ServiceLocator itself.
|
|
531
|
+
* @public
|
|
532
|
+
*/
|
|
533
|
+
export const ServiceLocator = Container;
|
|
534
|
+
function createResolver(getter) {
|
|
535
|
+
return function (key) {
|
|
536
|
+
const resolver = function (target, property, descriptor) {
|
|
537
|
+
DI.inject(resolver)(target, property, descriptor);
|
|
538
|
+
};
|
|
539
|
+
resolver.$isResolver = true;
|
|
540
|
+
resolver.resolve = function (handler, requestor) {
|
|
541
|
+
return getter(key, handler, requestor);
|
|
542
|
+
};
|
|
543
|
+
return resolver;
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* A decorator that specifies what to inject into its target.
|
|
548
|
+
* @param dependencies - The dependencies to inject.
|
|
549
|
+
* @returns The decorator to be applied to the target class.
|
|
550
|
+
* @remarks
|
|
551
|
+
* The decorator can be used to decorate a class, listing all of the classes dependencies.
|
|
552
|
+
* Or it can be used to decorate a constructor paramter, indicating what to inject for that
|
|
553
|
+
* parameter.
|
|
554
|
+
* Or it can be used for a web component property, indicating what that property should resolve to.
|
|
555
|
+
*
|
|
556
|
+
* @public
|
|
557
|
+
*/
|
|
558
|
+
export const inject = DI.inject;
|
|
559
|
+
function transientDecorator(target) {
|
|
560
|
+
return DI.transient(target);
|
|
561
|
+
}
|
|
562
|
+
export function transient(target) {
|
|
563
|
+
return target == null ? transientDecorator : transientDecorator(target);
|
|
564
|
+
}
|
|
565
|
+
const defaultSingletonOptions = { scoped: false };
|
|
566
|
+
function singletonDecorator(target) {
|
|
567
|
+
return DI.singleton(target);
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* @public
|
|
571
|
+
*/
|
|
572
|
+
export function singleton(targetOrOptions) {
|
|
573
|
+
if (typeof targetOrOptions === "function") {
|
|
574
|
+
return DI.singleton(targetOrOptions);
|
|
575
|
+
}
|
|
576
|
+
return function ($target) {
|
|
577
|
+
return DI.singleton($target, targetOrOptions);
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
function createAllResolver(getter) {
|
|
581
|
+
return function (key, searchAncestors) {
|
|
582
|
+
searchAncestors = !!searchAncestors;
|
|
583
|
+
const resolver = function (target, property, descriptor) {
|
|
584
|
+
DI.inject(resolver)(target, property, descriptor);
|
|
585
|
+
};
|
|
586
|
+
resolver.$isResolver = true;
|
|
587
|
+
resolver.resolve = function (handler, requestor) {
|
|
588
|
+
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
|
|
589
|
+
return getter(key, handler, requestor, searchAncestors);
|
|
590
|
+
};
|
|
591
|
+
return resolver;
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* A decorator and DI resolver that will resolve an array of all dependencies
|
|
596
|
+
* registered with the specified key.
|
|
597
|
+
* @param key - The key to resolve all dependencies for.
|
|
598
|
+
* @param searchAncestors - [optional] Indicates whether to search ancestor containers.
|
|
599
|
+
* @public
|
|
600
|
+
*/
|
|
601
|
+
export const all = createAllResolver((key, handler, requestor, searchAncestors) => requestor.getAll(key, searchAncestors));
|
|
602
|
+
/**
|
|
603
|
+
* A decorator that lazily injects a dependency depending on whether the `Key` is present at the time of function call.
|
|
604
|
+
*
|
|
605
|
+
* @example
|
|
606
|
+
* You need to make your argument a function that returns the type, for example
|
|
607
|
+
* ```ts
|
|
608
|
+
* class Foo {
|
|
609
|
+
* constructor( @lazy('random') public random: () => number )
|
|
610
|
+
* }
|
|
611
|
+
* const foo = container.get(Foo); // instanceof Foo
|
|
612
|
+
* foo.random(); // throws
|
|
613
|
+
* ```
|
|
614
|
+
* would throw an exception because you haven't registered `'random'` before calling the method.
|
|
615
|
+
* @example
|
|
616
|
+
* This, would give you a new 'Math.random()' number each time.
|
|
617
|
+
* ```ts
|
|
618
|
+
* class Foo {
|
|
619
|
+
* constructor( @lazy('random') public random: () => random )
|
|
620
|
+
* }
|
|
621
|
+
* container.register(Registration.callback('random', Math.random ));
|
|
622
|
+
* container.get(Foo).random(); // some random number
|
|
623
|
+
* container.get(Foo).random(); // another random number
|
|
624
|
+
* ```
|
|
625
|
+
*
|
|
626
|
+
* `@lazy` does not manage the lifecycle of the underlying key. If you want a singleton, you have to register as a
|
|
627
|
+
* `singleton`, `transient` would also behave as you would expect, providing you a new instance each time.
|
|
628
|
+
*
|
|
629
|
+
* @param key - The key to lazily resolve.
|
|
630
|
+
* see {@link DI.createContext} on interactions with interfaces
|
|
631
|
+
*
|
|
632
|
+
* @public
|
|
633
|
+
*/
|
|
634
|
+
export const lazy = createResolver((key, handler, requestor) => {
|
|
635
|
+
return () => requestor.get(key);
|
|
636
|
+
});
|
|
637
|
+
/**
|
|
638
|
+
* A decorator that allows you to optionally inject a dependency depending on whether the [[`Key`]] is present, for example:
|
|
639
|
+
* @example
|
|
640
|
+
* ```ts
|
|
641
|
+
* class Foo {
|
|
642
|
+
* constructor( @inject('mystring') public str: string = 'somestring' )
|
|
643
|
+
* }
|
|
644
|
+
* container.get(Foo); // throws
|
|
645
|
+
* ```
|
|
646
|
+
* would fail
|
|
647
|
+
*
|
|
648
|
+
* @example
|
|
649
|
+
* ```ts
|
|
650
|
+
* class Foo {
|
|
651
|
+
* constructor( @optional('mystring') public str: string = 'somestring' )
|
|
652
|
+
* }
|
|
653
|
+
* container.get(Foo).str // somestring
|
|
654
|
+
* ```
|
|
655
|
+
* if you use it without a default it will inject `undefined`, so remember to mark your input type as
|
|
656
|
+
* possibly `undefined`!
|
|
657
|
+
*
|
|
658
|
+
* @param key - The key to optionally resolve.
|
|
659
|
+
* see {@link DI.createContext} on interactions with interfaces
|
|
660
|
+
*
|
|
661
|
+
* @public
|
|
662
|
+
*/
|
|
663
|
+
export const optional = createResolver((key, handler, requestor) => {
|
|
664
|
+
if (requestor.has(key, true)) {
|
|
665
|
+
return requestor.get(key);
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
return undefined;
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
/**
|
|
672
|
+
* A decorator that tells the container not to try to inject a dependency.
|
|
673
|
+
*
|
|
674
|
+
* @public
|
|
675
|
+
*/
|
|
676
|
+
export function ignore(target, property, descriptor) {
|
|
677
|
+
DI.inject(ignore)(target, property, descriptor);
|
|
678
|
+
}
|
|
679
|
+
// Hack: casting below used to prevent TS from generate a namespace which can't be commented
|
|
680
|
+
// and results in documentation validation errors.
|
|
681
|
+
ignore.$isResolver = true;
|
|
682
|
+
ignore.resolve = () => undefined;
|
|
683
|
+
/**
|
|
684
|
+
* A decorator that indicates that a new instance should be injected scoped to the
|
|
685
|
+
* container that requested the instance.
|
|
686
|
+
* @param key - The dependency key for the new instance.
|
|
687
|
+
* @remarks
|
|
688
|
+
* This creates a resolver with an instance strategy pointing to the new instance, effectively
|
|
689
|
+
* making this a singleton, scoped to the container or DOM's subtree.
|
|
690
|
+
*
|
|
691
|
+
* @public
|
|
692
|
+
*/
|
|
693
|
+
export const newInstanceForScope = createResolver((key, handler, requestor) => {
|
|
694
|
+
const instance = createNewInstance(key, handler);
|
|
695
|
+
const resolver = new ResolverImpl(key, 0 /* ResolverStrategy.instance */, instance);
|
|
696
|
+
requestor.registerResolver(key, resolver);
|
|
697
|
+
return instance;
|
|
698
|
+
});
|
|
699
|
+
/**
|
|
700
|
+
* A decorator that indicates that a new instance should be injected.
|
|
701
|
+
* @param key - The dependency key for the new instance.
|
|
702
|
+
* @remarks
|
|
703
|
+
* The instance is not internally cached with a resolver as newInstanceForScope does.
|
|
704
|
+
*
|
|
705
|
+
* @public
|
|
706
|
+
*/
|
|
707
|
+
export const newInstanceOf = createResolver((key, handler, _requestor) => createNewInstance(key, handler));
|
|
708
|
+
function createNewInstance(key, handler) {
|
|
709
|
+
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
|
|
710
|
+
return handler.getFactory(key).construct(handler);
|
|
711
|
+
}
|
|
712
|
+
/** @internal */
|
|
713
|
+
export class ResolverImpl {
|
|
714
|
+
constructor(key, strategy, state) {
|
|
715
|
+
this.key = key;
|
|
716
|
+
this.strategy = strategy;
|
|
717
|
+
this.state = state;
|
|
718
|
+
this.resolving = false;
|
|
719
|
+
}
|
|
720
|
+
get $isResolver() {
|
|
721
|
+
return true;
|
|
722
|
+
}
|
|
723
|
+
register(container) {
|
|
724
|
+
return container.registerResolver(this.key, this);
|
|
725
|
+
}
|
|
726
|
+
resolveAsync(handler, requestor) {
|
|
727
|
+
switch (this.strategy) {
|
|
728
|
+
case 1 /* ResolverStrategy.singleton */: {
|
|
729
|
+
if (this.resolving) {
|
|
730
|
+
throw FAST.error(1513 /* Message.cyclicDependency */, { name: this.state.name });
|
|
731
|
+
}
|
|
732
|
+
this.resolving = true;
|
|
733
|
+
return handler
|
|
734
|
+
.getFactory(this.state)
|
|
735
|
+
.constructAsync(requestor)
|
|
736
|
+
.then(instance => {
|
|
737
|
+
this.state = instance;
|
|
738
|
+
this.strategy = 0 /* ResolverStrategy.instance */;
|
|
739
|
+
this.resolving = false;
|
|
740
|
+
return instance;
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
case 2 /* ResolverStrategy.transient */: {
|
|
744
|
+
// Always create transients from the requesting container
|
|
745
|
+
const factory = handler.getFactory(this.state);
|
|
746
|
+
if (factory === null) {
|
|
747
|
+
throw FAST.error(1502 /* Message.noFactoryForResolver */, { key: this.key });
|
|
748
|
+
}
|
|
749
|
+
return factory.constructAsync(requestor);
|
|
750
|
+
}
|
|
751
|
+
default:
|
|
752
|
+
return Promise.resolve(this.resolve(handler, requestor));
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
resolve(handler, requestor) {
|
|
756
|
+
switch (this.strategy) {
|
|
757
|
+
case 0 /* ResolverStrategy.instance */:
|
|
758
|
+
return this.state;
|
|
759
|
+
case 1 /* ResolverStrategy.singleton */: {
|
|
760
|
+
if (this.resolving) {
|
|
761
|
+
throw FAST.error(1513 /* Message.cyclicDependency */, { name: this.state.name });
|
|
762
|
+
}
|
|
763
|
+
this.resolving = true;
|
|
764
|
+
this.state = handler
|
|
765
|
+
.getFactory(this.state)
|
|
766
|
+
.construct(requestor);
|
|
767
|
+
this.strategy = 0 /* ResolverStrategy.instance */;
|
|
768
|
+
this.resolving = false;
|
|
769
|
+
return this.state;
|
|
770
|
+
}
|
|
771
|
+
case 2 /* ResolverStrategy.transient */: {
|
|
772
|
+
// Always create transients from the requesting container
|
|
773
|
+
const factory = handler.getFactory(this.state);
|
|
774
|
+
if (factory === null) {
|
|
775
|
+
throw FAST.error(1502 /* Message.noFactoryForResolver */, { key: this.key });
|
|
776
|
+
}
|
|
777
|
+
return factory.construct(requestor);
|
|
778
|
+
}
|
|
779
|
+
case 3 /* ResolverStrategy.callback */:
|
|
780
|
+
return this.state(handler, requestor, this);
|
|
781
|
+
case 4 /* ResolverStrategy.array */:
|
|
782
|
+
return this.state[0].resolve(handler, requestor);
|
|
783
|
+
case 5 /* ResolverStrategy.alias */:
|
|
784
|
+
return requestor.get(this.state);
|
|
785
|
+
default:
|
|
786
|
+
throw FAST.error(1503 /* Message.invalidResolverStrategy */, {
|
|
787
|
+
strategy: this.strategy,
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
getFactory(container) {
|
|
792
|
+
var _a, _b, _c;
|
|
793
|
+
switch (this.strategy) {
|
|
794
|
+
case 1 /* ResolverStrategy.singleton */:
|
|
795
|
+
case 2 /* ResolverStrategy.transient */:
|
|
796
|
+
return container.getFactory(this.state);
|
|
797
|
+
case 5 /* ResolverStrategy.alias */:
|
|
798
|
+
return (_c = (_b = (_a = container.getResolver(this.state)) === null || _a === void 0 ? void 0 : _a.getFactory) === null || _b === void 0 ? void 0 : _b.call(_a, container)) !== null && _c !== void 0 ? _c : null;
|
|
799
|
+
default:
|
|
800
|
+
return null;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
function containerGetKey(d) {
|
|
805
|
+
return this.get(d);
|
|
806
|
+
}
|
|
807
|
+
function transformInstance(inst, transform) {
|
|
808
|
+
return transform(inst);
|
|
809
|
+
}
|
|
810
|
+
/** @internal */
|
|
811
|
+
export class FactoryImpl {
|
|
812
|
+
constructor(Type, dependencies) {
|
|
813
|
+
this.Type = Type;
|
|
814
|
+
this.dependencies = dependencies;
|
|
815
|
+
this.transformers = null;
|
|
816
|
+
}
|
|
817
|
+
constructAsync(container, dynamicDependencies) {
|
|
818
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
819
|
+
const resolved = yield Promise.all(this.dependencies.map(x => container.getAsync(x)));
|
|
820
|
+
return this.constructCore(resolved, dynamicDependencies);
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
construct(container, dynamicDependencies) {
|
|
824
|
+
const resolved = this.dependencies.map(containerGetKey, container);
|
|
825
|
+
return this.constructCore(resolved, dynamicDependencies);
|
|
826
|
+
}
|
|
827
|
+
constructCore(resolved, dynamicDependencies) {
|
|
828
|
+
let instance;
|
|
829
|
+
if (dynamicDependencies === void 0) {
|
|
830
|
+
instance = new this.Type(...resolved);
|
|
831
|
+
}
|
|
832
|
+
else {
|
|
833
|
+
instance = new this.Type(...resolved, ...dynamicDependencies);
|
|
834
|
+
}
|
|
835
|
+
if (this.transformers === null) {
|
|
836
|
+
return instance;
|
|
837
|
+
}
|
|
838
|
+
return this.transformers.reduce(transformInstance, instance);
|
|
839
|
+
}
|
|
840
|
+
registerTransformer(transformer) {
|
|
841
|
+
(this.transformers || (this.transformers = [])).push(transformer);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
const containerResolver = {
|
|
845
|
+
$isResolver: true,
|
|
846
|
+
resolve(handler, requestor) {
|
|
847
|
+
return requestor;
|
|
848
|
+
},
|
|
849
|
+
resolveAsync: function (handler, requestor) {
|
|
850
|
+
return Promise.resolve(requestor);
|
|
851
|
+
},
|
|
852
|
+
};
|
|
853
|
+
function isRegistry(obj) {
|
|
854
|
+
return typeof obj.register === "function";
|
|
855
|
+
}
|
|
856
|
+
function isSelfRegistry(obj) {
|
|
857
|
+
return isRegistry(obj) && typeof obj.registerInRequestor === "boolean";
|
|
858
|
+
}
|
|
859
|
+
function isRegisterInRequester(obj) {
|
|
860
|
+
return isSelfRegistry(obj) && obj.registerInRequestor;
|
|
861
|
+
}
|
|
862
|
+
function isClass(obj) {
|
|
863
|
+
return obj.prototype !== void 0;
|
|
864
|
+
}
|
|
865
|
+
const InstrinsicTypeNames = new Set([
|
|
866
|
+
"Array",
|
|
867
|
+
"ArrayBuffer",
|
|
868
|
+
"Boolean",
|
|
869
|
+
"DataView",
|
|
870
|
+
"Date",
|
|
871
|
+
"Error",
|
|
872
|
+
"EvalError",
|
|
873
|
+
"Float32Array",
|
|
874
|
+
"Float64Array",
|
|
875
|
+
"Function",
|
|
876
|
+
"Int8Array",
|
|
877
|
+
"Int16Array",
|
|
878
|
+
"Int32Array",
|
|
879
|
+
"Map",
|
|
880
|
+
"Number",
|
|
881
|
+
"Object",
|
|
882
|
+
"Promise",
|
|
883
|
+
"RangeError",
|
|
884
|
+
"ReferenceError",
|
|
885
|
+
"RegExp",
|
|
886
|
+
"Set",
|
|
887
|
+
"SharedArrayBuffer",
|
|
888
|
+
"String",
|
|
889
|
+
"SyntaxError",
|
|
890
|
+
"TypeError",
|
|
891
|
+
"Uint8Array",
|
|
892
|
+
"Uint8ClampedArray",
|
|
893
|
+
"Uint16Array",
|
|
894
|
+
"Uint32Array",
|
|
895
|
+
"URIError",
|
|
896
|
+
"WeakMap",
|
|
897
|
+
"WeakSet",
|
|
898
|
+
]);
|
|
899
|
+
const factories = new Map();
|
|
900
|
+
/**
|
|
901
|
+
* @internal
|
|
902
|
+
*/
|
|
903
|
+
export class ContainerImpl {
|
|
904
|
+
constructor(owner, config) {
|
|
905
|
+
this.owner = owner;
|
|
906
|
+
this.config = config;
|
|
907
|
+
this._parent = void 0;
|
|
908
|
+
this.registerDepth = 0;
|
|
909
|
+
this.isHandlingContextRequests = false;
|
|
910
|
+
this.resolvers = new Map();
|
|
911
|
+
this.resolvers.set(Container, containerResolver);
|
|
912
|
+
if (owner) {
|
|
913
|
+
owner.$$container$$ = this;
|
|
914
|
+
if ("addEventListener" in owner) {
|
|
915
|
+
Context.handle(owner, (e) => {
|
|
916
|
+
if (this.isHandlingContextRequests) {
|
|
917
|
+
try {
|
|
918
|
+
const value = this.get(e.context);
|
|
919
|
+
e.stopImmediatePropagation();
|
|
920
|
+
e.callback(value);
|
|
921
|
+
}
|
|
922
|
+
catch (_a) {
|
|
923
|
+
// Container failed to find the context, so we need to
|
|
924
|
+
// let the event propagate.
|
|
925
|
+
// TODO: Introduce a tryGet API to Container. Issue #4582
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
else if (e.context === Container &&
|
|
929
|
+
e.composedPath()[0] !== this.owner) {
|
|
930
|
+
e.stopImmediatePropagation();
|
|
931
|
+
e.callback(this);
|
|
932
|
+
}
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
get parent() {
|
|
938
|
+
if (this._parent === void 0) {
|
|
939
|
+
this._parent = this.config.parentLocator(this.owner);
|
|
940
|
+
}
|
|
941
|
+
return this._parent;
|
|
942
|
+
}
|
|
943
|
+
get depth() {
|
|
944
|
+
return this.parent === null ? 0 : this.parent.depth + 1;
|
|
945
|
+
}
|
|
946
|
+
get responsibleForOwnerRequests() {
|
|
947
|
+
return this.config.responsibleForOwnerRequests;
|
|
948
|
+
}
|
|
949
|
+
handleContextRequests(enable) {
|
|
950
|
+
this.isHandlingContextRequests = enable;
|
|
951
|
+
}
|
|
952
|
+
register(...params) {
|
|
953
|
+
if (++this.registerDepth === 100) {
|
|
954
|
+
// Most likely cause is trying to register a plain object that does not have a
|
|
955
|
+
// register method and is not a class constructor
|
|
956
|
+
throw FAST.error(1504 /* Message.cannotAutoregisterDependency */);
|
|
957
|
+
}
|
|
958
|
+
let current;
|
|
959
|
+
let keys;
|
|
960
|
+
let value;
|
|
961
|
+
let j;
|
|
962
|
+
let jj;
|
|
963
|
+
for (let i = 0, ii = params.length; i < ii; ++i) {
|
|
964
|
+
current = params[i];
|
|
965
|
+
if (!isObject(current)) {
|
|
966
|
+
continue;
|
|
967
|
+
}
|
|
968
|
+
if (isRegistry(current)) {
|
|
969
|
+
current.register(this);
|
|
970
|
+
}
|
|
971
|
+
else if (isClass(current)) {
|
|
972
|
+
Registration.singleton(current, current).register(this);
|
|
973
|
+
}
|
|
974
|
+
else {
|
|
975
|
+
keys = Object.keys(current);
|
|
976
|
+
j = 0;
|
|
977
|
+
jj = keys.length;
|
|
978
|
+
for (; j < jj; ++j) {
|
|
979
|
+
value = current[keys[j]];
|
|
980
|
+
if (!isObject(value)) {
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
// note: we could remove this if-branch and call this.register directly
|
|
984
|
+
// - the extra check is just a perf tweak to create fewer unnecessary arrays by the spread operator
|
|
985
|
+
if (isRegistry(value)) {
|
|
986
|
+
value.register(this);
|
|
987
|
+
}
|
|
988
|
+
else {
|
|
989
|
+
this.register(value);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
--this.registerDepth;
|
|
995
|
+
return this;
|
|
996
|
+
}
|
|
997
|
+
registerResolver(key, resolver) {
|
|
998
|
+
validateKey(key);
|
|
999
|
+
const resolvers = this.resolvers;
|
|
1000
|
+
const result = resolvers.get(key);
|
|
1001
|
+
if (result == null) {
|
|
1002
|
+
resolvers.set(key, resolver);
|
|
1003
|
+
}
|
|
1004
|
+
else if (result instanceof ResolverImpl &&
|
|
1005
|
+
result.strategy === 4 /* ResolverStrategy.array */) {
|
|
1006
|
+
result.state.push(resolver);
|
|
1007
|
+
}
|
|
1008
|
+
else {
|
|
1009
|
+
resolvers.set(key, new ResolverImpl(key, 4 /* ResolverStrategy.array */, [result, resolver]));
|
|
1010
|
+
}
|
|
1011
|
+
return resolver;
|
|
1012
|
+
}
|
|
1013
|
+
registerTransformer(key, transformer) {
|
|
1014
|
+
const resolver = this.getResolver(key);
|
|
1015
|
+
if (resolver == null) {
|
|
1016
|
+
return false;
|
|
1017
|
+
}
|
|
1018
|
+
if (resolver.getFactory) {
|
|
1019
|
+
const factory = resolver.getFactory(this);
|
|
1020
|
+
if (factory == null) {
|
|
1021
|
+
return false;
|
|
1022
|
+
}
|
|
1023
|
+
// This type cast is a bit of a hacky one, necessary due to the duplicity of IResolverLike.
|
|
1024
|
+
// Problem is that that interface's type arg can be of type Key, but the getFactory method only works on
|
|
1025
|
+
// type Constructable. So the return type of that optional method has this additional constraint, which
|
|
1026
|
+
// seems to confuse the type checker.
|
|
1027
|
+
factory.registerTransformer(transformer);
|
|
1028
|
+
return true;
|
|
1029
|
+
}
|
|
1030
|
+
return false;
|
|
1031
|
+
}
|
|
1032
|
+
getResolver(key, autoRegister = true) {
|
|
1033
|
+
validateKey(key);
|
|
1034
|
+
if (key.resolve !== void 0) {
|
|
1035
|
+
return key;
|
|
1036
|
+
}
|
|
1037
|
+
/* eslint-disable-next-line @typescript-eslint/no-this-alias */
|
|
1038
|
+
let current = this;
|
|
1039
|
+
let resolver;
|
|
1040
|
+
while (current != null) {
|
|
1041
|
+
resolver = current.resolvers.get(key);
|
|
1042
|
+
if (resolver == null) {
|
|
1043
|
+
if (current.parent == null) {
|
|
1044
|
+
const handler = isRegisterInRequester(key)
|
|
1045
|
+
? this
|
|
1046
|
+
: current;
|
|
1047
|
+
return autoRegister ? this.jitRegister(key, handler) : null;
|
|
1048
|
+
}
|
|
1049
|
+
current = current.parent;
|
|
1050
|
+
}
|
|
1051
|
+
else {
|
|
1052
|
+
return resolver;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
return null;
|
|
1056
|
+
}
|
|
1057
|
+
has(key, searchAncestors = false) {
|
|
1058
|
+
return this.resolvers.has(key)
|
|
1059
|
+
? true
|
|
1060
|
+
: searchAncestors && this.parent != null
|
|
1061
|
+
? this.parent.has(key, true)
|
|
1062
|
+
: false;
|
|
1063
|
+
}
|
|
1064
|
+
getAsync(key) {
|
|
1065
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1066
|
+
validateKey(key);
|
|
1067
|
+
if (key.$isResolver) {
|
|
1068
|
+
return key.resolveAsync(this, this);
|
|
1069
|
+
}
|
|
1070
|
+
/* eslint-disable-next-line @typescript-eslint/no-this-alias */
|
|
1071
|
+
let current = this;
|
|
1072
|
+
let resolver;
|
|
1073
|
+
while (current != null) {
|
|
1074
|
+
resolver = current.resolvers.get(key);
|
|
1075
|
+
if (resolver == null) {
|
|
1076
|
+
if (current.parent == null) {
|
|
1077
|
+
const registration = yield this.config.asyncRegistrationLocator(key);
|
|
1078
|
+
if (!registration) {
|
|
1079
|
+
throw FAST.error(1505 /* Message.cannotResolveKey */, { key });
|
|
1080
|
+
}
|
|
1081
|
+
const handler = isRegisterInRequester(key)
|
|
1082
|
+
? this
|
|
1083
|
+
: current;
|
|
1084
|
+
resolver = registration.register(handler);
|
|
1085
|
+
return resolver.resolveAsync(current, this);
|
|
1086
|
+
}
|
|
1087
|
+
current = current.parent;
|
|
1088
|
+
}
|
|
1089
|
+
else {
|
|
1090
|
+
return resolver.resolveAsync(current, this);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
throw FAST.error(1505 /* Message.cannotResolveKey */, { key });
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
get(key) {
|
|
1097
|
+
validateKey(key);
|
|
1098
|
+
if (key.$isResolver) {
|
|
1099
|
+
return key.resolve(this, this);
|
|
1100
|
+
}
|
|
1101
|
+
/* eslint-disable-next-line @typescript-eslint/no-this-alias */
|
|
1102
|
+
let current = this;
|
|
1103
|
+
let resolver;
|
|
1104
|
+
while (current != null) {
|
|
1105
|
+
resolver = current.resolvers.get(key);
|
|
1106
|
+
if (resolver == null) {
|
|
1107
|
+
if (current.parent == null) {
|
|
1108
|
+
const handler = isRegisterInRequester(key)
|
|
1109
|
+
? this
|
|
1110
|
+
: current;
|
|
1111
|
+
resolver = this.jitRegister(key, handler);
|
|
1112
|
+
return resolver.resolve(current, this);
|
|
1113
|
+
}
|
|
1114
|
+
current = current.parent;
|
|
1115
|
+
}
|
|
1116
|
+
else {
|
|
1117
|
+
return resolver.resolve(current, this);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
throw FAST.error(1505 /* Message.cannotResolveKey */, { key });
|
|
1121
|
+
}
|
|
1122
|
+
getAll(key, searchAncestors = false) {
|
|
1123
|
+
validateKey(key);
|
|
1124
|
+
/* eslint-disable-next-line @typescript-eslint/no-this-alias */
|
|
1125
|
+
const requestor = this;
|
|
1126
|
+
let current = requestor;
|
|
1127
|
+
let resolver;
|
|
1128
|
+
if (searchAncestors) {
|
|
1129
|
+
let resolutions = emptyArray;
|
|
1130
|
+
while (current != null) {
|
|
1131
|
+
resolver = current.resolvers.get(key);
|
|
1132
|
+
if (resolver != null) {
|
|
1133
|
+
resolutions = resolutions.concat(
|
|
1134
|
+
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */
|
|
1135
|
+
buildAllResponse(resolver, current, requestor));
|
|
1136
|
+
}
|
|
1137
|
+
current = current.parent;
|
|
1138
|
+
}
|
|
1139
|
+
return resolutions;
|
|
1140
|
+
}
|
|
1141
|
+
else {
|
|
1142
|
+
while (current != null) {
|
|
1143
|
+
resolver = current.resolvers.get(key);
|
|
1144
|
+
if (resolver == null) {
|
|
1145
|
+
current = current.parent;
|
|
1146
|
+
if (current == null) {
|
|
1147
|
+
return emptyArray;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
else {
|
|
1151
|
+
return buildAllResponse(resolver, current, requestor);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
return emptyArray;
|
|
1156
|
+
}
|
|
1157
|
+
getFactory(Type) {
|
|
1158
|
+
let factory = factories.get(Type);
|
|
1159
|
+
if (factory === void 0) {
|
|
1160
|
+
if (isNativeFunction(Type)) {
|
|
1161
|
+
throw FAST.error(1506 /* Message.cannotConstructNativeFunction */, {
|
|
1162
|
+
name: Type.name,
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
factories.set(Type, (factory = new FactoryImpl(Type, DI.getDependencies(Type))));
|
|
1166
|
+
}
|
|
1167
|
+
return factory;
|
|
1168
|
+
}
|
|
1169
|
+
registerFactory(key, factory) {
|
|
1170
|
+
factories.set(key, factory);
|
|
1171
|
+
}
|
|
1172
|
+
createChild(config) {
|
|
1173
|
+
return new ContainerImpl(null, Object.assign({}, this.config, config, { parentLocator: () => this }));
|
|
1174
|
+
}
|
|
1175
|
+
jitRegister(keyAsValue, handler) {
|
|
1176
|
+
if (typeof keyAsValue !== "function") {
|
|
1177
|
+
throw FAST.error(1507 /* Message.cannotJITRegisterNonConstructor */, {
|
|
1178
|
+
value: keyAsValue,
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
if (InstrinsicTypeNames.has(keyAsValue.name)) {
|
|
1182
|
+
throw FAST.error(1508 /* Message.cannotJITRegisterIntrinsic */, {
|
|
1183
|
+
value: keyAsValue.name,
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
if (isRegistry(keyAsValue)) {
|
|
1187
|
+
const registrationResolver = keyAsValue.register(handler);
|
|
1188
|
+
if (!(registrationResolver instanceof Object) ||
|
|
1189
|
+
registrationResolver.resolve == null) {
|
|
1190
|
+
const newResolver = handler.resolvers.get(keyAsValue);
|
|
1191
|
+
if (newResolver != void 0) {
|
|
1192
|
+
return newResolver;
|
|
1193
|
+
}
|
|
1194
|
+
throw FAST.error(1510 /* Message.invalidResolver */);
|
|
1195
|
+
}
|
|
1196
|
+
return registrationResolver;
|
|
1197
|
+
}
|
|
1198
|
+
else if (keyAsValue.$isInterface) {
|
|
1199
|
+
throw FAST.error(1509 /* Message.cannotJITRegisterInterface */, {
|
|
1200
|
+
value: keyAsValue.name,
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
else {
|
|
1204
|
+
const resolver = this.config.defaultResolver(keyAsValue, handler);
|
|
1205
|
+
handler.resolvers.set(keyAsValue, resolver);
|
|
1206
|
+
return resolver;
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
const cache = new WeakMap();
|
|
1211
|
+
function cacheCallbackResult(fun) {
|
|
1212
|
+
return function (handler, requestor, resolver) {
|
|
1213
|
+
if (cache.has(resolver)) {
|
|
1214
|
+
return cache.get(resolver);
|
|
1215
|
+
}
|
|
1216
|
+
const t = fun(handler, requestor, resolver);
|
|
1217
|
+
cache.set(resolver, t);
|
|
1218
|
+
return t;
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
/**
|
|
1222
|
+
* You can use the resulting Registration of any of the factory methods
|
|
1223
|
+
* to register with the container.
|
|
1224
|
+
*
|
|
1225
|
+
* @example
|
|
1226
|
+
* ```
|
|
1227
|
+
* class Foo {}
|
|
1228
|
+
* const container = DI.createContainer();
|
|
1229
|
+
* container.register(Registration.instance(Foo, new Foo()));
|
|
1230
|
+
* container.get(Foo);
|
|
1231
|
+
* ```
|
|
1232
|
+
*
|
|
1233
|
+
* @public
|
|
1234
|
+
*/
|
|
1235
|
+
export const Registration = Object.freeze({
|
|
1236
|
+
/**
|
|
1237
|
+
* Allows you to pass an instance.
|
|
1238
|
+
* Every time you request this {@link Key} you will get this instance back.
|
|
1239
|
+
*
|
|
1240
|
+
* @example
|
|
1241
|
+
* ```
|
|
1242
|
+
* Registration.instance(Foo, new Foo()));
|
|
1243
|
+
* ```
|
|
1244
|
+
*
|
|
1245
|
+
* @param key - The key to register the instance under.
|
|
1246
|
+
* @param value - The instance to return when the key is requested.
|
|
1247
|
+
*/
|
|
1248
|
+
instance(key, value) {
|
|
1249
|
+
return new ResolverImpl(key, 0 /* ResolverStrategy.instance */, value);
|
|
1250
|
+
},
|
|
1251
|
+
/**
|
|
1252
|
+
* Creates an instance from the class.
|
|
1253
|
+
* Every time you request this {@link Key} you will get the same one back.
|
|
1254
|
+
*
|
|
1255
|
+
* @example
|
|
1256
|
+
* ```
|
|
1257
|
+
* Registration.singleton(Foo, Foo);
|
|
1258
|
+
* ```
|
|
1259
|
+
*
|
|
1260
|
+
* @param key - The key to register the singleton under.
|
|
1261
|
+
* @param value - The class to instantiate as a singleton when first requested.
|
|
1262
|
+
*/
|
|
1263
|
+
singleton(key, value) {
|
|
1264
|
+
return new ResolverImpl(key, 1 /* ResolverStrategy.singleton */, value);
|
|
1265
|
+
},
|
|
1266
|
+
/**
|
|
1267
|
+
* Creates an instance from a class.
|
|
1268
|
+
* Every time you request this {@link Key} you will get a new instance.
|
|
1269
|
+
*
|
|
1270
|
+
* @example
|
|
1271
|
+
* ```
|
|
1272
|
+
* Registration.instance(Foo, Foo);
|
|
1273
|
+
* ```
|
|
1274
|
+
*
|
|
1275
|
+
* @param key - The key to register the instance type under.
|
|
1276
|
+
* @param value - The class to instantiate each time the key is requested.
|
|
1277
|
+
*/
|
|
1278
|
+
transient(key, value) {
|
|
1279
|
+
return new ResolverImpl(key, 2 /* ResolverStrategy.transient */, value);
|
|
1280
|
+
},
|
|
1281
|
+
/**
|
|
1282
|
+
* Delegates to a callback function to provide the dependency.
|
|
1283
|
+
* Every time you request this {@link Key} the callback will be invoked to provide
|
|
1284
|
+
* the dependency.
|
|
1285
|
+
*
|
|
1286
|
+
* @example
|
|
1287
|
+
* ```
|
|
1288
|
+
* Registration.callback(Foo, () => new Foo());
|
|
1289
|
+
* Registration.callback(Bar, (c: Container) => new Bar(c.get(Foo)));
|
|
1290
|
+
* ```
|
|
1291
|
+
*
|
|
1292
|
+
* @param key - The key to register the callback for.
|
|
1293
|
+
* @param callback - The function that is expected to return the dependency.
|
|
1294
|
+
*/
|
|
1295
|
+
callback(key, callback) {
|
|
1296
|
+
return new ResolverImpl(key, 3 /* ResolverStrategy.callback */, callback);
|
|
1297
|
+
},
|
|
1298
|
+
/**
|
|
1299
|
+
* Delegates to a callback function to provide the dependency and then caches the
|
|
1300
|
+
* dependency for future requests.
|
|
1301
|
+
*
|
|
1302
|
+
* @example
|
|
1303
|
+
* ```
|
|
1304
|
+
* Registration.cachedCallback(Foo, () => new Foo());
|
|
1305
|
+
* Registration.cachedCallback(Bar, (c: Container) => new Bar(c.get(Foo)));
|
|
1306
|
+
* ```
|
|
1307
|
+
*
|
|
1308
|
+
* @param key - The key to register the callback for.
|
|
1309
|
+
* @param callback - The function that is expected to return the dependency.
|
|
1310
|
+
* @remarks
|
|
1311
|
+
* If you pass the same Registration to another container, the same cached value will be used.
|
|
1312
|
+
* Should all references to the resolver returned be removed, the cache will expire.
|
|
1313
|
+
*/
|
|
1314
|
+
cachedCallback(key, callback) {
|
|
1315
|
+
return new ResolverImpl(key, 3 /* ResolverStrategy.callback */, cacheCallbackResult(callback));
|
|
1316
|
+
},
|
|
1317
|
+
/**
|
|
1318
|
+
* Creates an alternate {@link Key} to retrieve an instance by.
|
|
1319
|
+
*
|
|
1320
|
+
* @example
|
|
1321
|
+
* ```
|
|
1322
|
+
* Register.singleton(Foo, Foo)
|
|
1323
|
+
* Register.aliasTo(Foo, MyFoos);
|
|
1324
|
+
*
|
|
1325
|
+
* container.getAll(MyFoos) // contains an instance of Foo
|
|
1326
|
+
* ```
|
|
1327
|
+
*
|
|
1328
|
+
* @param originalKey - The original key that has been registered.
|
|
1329
|
+
* @param aliasKey - The alias to the original key.
|
|
1330
|
+
*/
|
|
1331
|
+
aliasTo(originalKey, aliasKey) {
|
|
1332
|
+
return new ResolverImpl(aliasKey, 5 /* ResolverStrategy.alias */, originalKey);
|
|
1333
|
+
},
|
|
1334
|
+
});
|
|
1335
|
+
/** @internal */
|
|
1336
|
+
export function validateKey(key) {
|
|
1337
|
+
if (key === null || key === void 0) {
|
|
1338
|
+
throw FAST.error(1511 /* Message.invalidKey */);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
function buildAllResponse(resolver, handler, requestor) {
|
|
1342
|
+
if (resolver instanceof ResolverImpl &&
|
|
1343
|
+
resolver.strategy === 4 /* ResolverStrategy.array */) {
|
|
1344
|
+
const state = resolver.state;
|
|
1345
|
+
let i = state.length;
|
|
1346
|
+
const results = new Array(i);
|
|
1347
|
+
while (i--) {
|
|
1348
|
+
results[i] = state[i].resolve(handler, requestor);
|
|
1349
|
+
}
|
|
1350
|
+
return results;
|
|
1351
|
+
}
|
|
1352
|
+
return [resolver.resolve(handler, requestor)];
|
|
1353
|
+
}
|
|
1354
|
+
const defaultFriendlyName = "(anonymous)";
|
|
1355
|
+
function isObject(value) {
|
|
1356
|
+
return (typeof value === "object" && value !== null) || typeof value === "function";
|
|
1357
|
+
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Determine whether the value is a native function.
|
|
1360
|
+
*
|
|
1361
|
+
* @param fn - The function to check.
|
|
1362
|
+
* @returns `true` is the function is a native function, otherwise `false`
|
|
1363
|
+
*/
|
|
1364
|
+
const isNativeFunction = (function () {
|
|
1365
|
+
const lookup = new WeakMap();
|
|
1366
|
+
let isNative = false;
|
|
1367
|
+
let sourceText = "";
|
|
1368
|
+
let i = 0;
|
|
1369
|
+
return function (fn) {
|
|
1370
|
+
isNative = lookup.get(fn);
|
|
1371
|
+
if (isNative === void 0) {
|
|
1372
|
+
sourceText = fn.toString();
|
|
1373
|
+
i = sourceText.length;
|
|
1374
|
+
// http://www.ecma-international.org/ecma-262/#prod-NativeFunction
|
|
1375
|
+
isNative =
|
|
1376
|
+
// 29 is the length of 'function () { [native code] }' which is the smallest length of a native function string
|
|
1377
|
+
i >= 29 &&
|
|
1378
|
+
// 100 seems to be a safe upper bound of the max length of a native function. In Chrome and FF it's 56, in Edge it's 61.
|
|
1379
|
+
i <= 100 &&
|
|
1380
|
+
// This whole heuristic *could* be tricked by a comment. Do we need to care about that?
|
|
1381
|
+
sourceText.charCodeAt(i - 1) === 0x7d && // }
|
|
1382
|
+
// TODO: the spec is a little vague about the precise constraints, so we do need to test this across various browsers to make sure just one whitespace is a safe assumption.
|
|
1383
|
+
sourceText.charCodeAt(i - 2) <= 0x20 && // whitespace
|
|
1384
|
+
sourceText.charCodeAt(i - 3) === 0x5d && // ]
|
|
1385
|
+
sourceText.charCodeAt(i - 4) === 0x65 && // e
|
|
1386
|
+
sourceText.charCodeAt(i - 5) === 0x64 && // d
|
|
1387
|
+
sourceText.charCodeAt(i - 6) === 0x6f && // o
|
|
1388
|
+
sourceText.charCodeAt(i - 7) === 0x63 && // c
|
|
1389
|
+
sourceText.charCodeAt(i - 8) === 0x20 && //
|
|
1390
|
+
sourceText.charCodeAt(i - 9) === 0x65 && // e
|
|
1391
|
+
sourceText.charCodeAt(i - 10) === 0x76 && // v
|
|
1392
|
+
sourceText.charCodeAt(i - 11) === 0x69 && // i
|
|
1393
|
+
sourceText.charCodeAt(i - 12) === 0x74 && // t
|
|
1394
|
+
sourceText.charCodeAt(i - 13) === 0x61 && // a
|
|
1395
|
+
sourceText.charCodeAt(i - 14) === 0x6e && // n
|
|
1396
|
+
sourceText.charCodeAt(i - 15) === 0x58; // [
|
|
1397
|
+
lookup.set(fn, isNative);
|
|
1398
|
+
}
|
|
1399
|
+
return isNative;
|
|
1400
|
+
};
|
|
1401
|
+
})();
|
|
1402
|
+
const isNumericLookup = {};
|
|
1403
|
+
function isArrayIndex(value) {
|
|
1404
|
+
switch (typeof value) {
|
|
1405
|
+
case "number":
|
|
1406
|
+
return value >= 0 && (value | 0) === value;
|
|
1407
|
+
case "string": {
|
|
1408
|
+
const result = isNumericLookup[value];
|
|
1409
|
+
if (result !== void 0) {
|
|
1410
|
+
return result;
|
|
1411
|
+
}
|
|
1412
|
+
const length = value.length;
|
|
1413
|
+
if (length === 0) {
|
|
1414
|
+
return (isNumericLookup[value] = false);
|
|
1415
|
+
}
|
|
1416
|
+
let ch = 0;
|
|
1417
|
+
for (let i = 0; i < length; ++i) {
|
|
1418
|
+
ch = value.charCodeAt(i);
|
|
1419
|
+
if ((i === 0 && ch === 0x30 && length > 1) /* must not start with 0 */ ||
|
|
1420
|
+
ch < 0x30 /* 0 */ ||
|
|
1421
|
+
ch > 0x39 /* 9 */) {
|
|
1422
|
+
return (isNumericLookup[value] = false);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
return (isNumericLookup[value] = true);
|
|
1426
|
+
}
|
|
1427
|
+
default:
|
|
1428
|
+
return false;
|
|
1429
|
+
}
|
|
1430
|
+
}
|