@shrub/core 0.5.26

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 jjvainav
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # Description
2
+
3
+ Shrub is a simple framework for building modular server-side applications and front-end components. The Core package provides a basic API for defining and loading modules.
4
+
5
+ ## Creating a Module
6
+
7
+ A module can be defined as a class that implements the IModule interface or as a JSON object that satisfies the IModule interface.
8
+
9
+ ```typescript
10
+ export class FooModule implements IModule {
11
+ readonly name = "foo";
12
+ }
13
+ ```
14
+
15
+ ```typescript
16
+ const fooModule: IModule = {
17
+ name: "foo"
18
+ };
19
+ ```
20
+
21
+ There are a few methods a module can define that allow it to configure functionality provided by the module and each method is invoked in the following order.
22
+
23
+ ### 1. `initialize(init: IModuleInitializer): void`
24
+
25
+ Allows a module the ability to perform pre-initialization that needs to happen before any modules or services are configured. Such tasks include binding settings or registering a configuration interface that is exposed to other modules.
26
+
27
+ ### 2. `configureServices(registration: IServiceRegistration): void`
28
+
29
+ Register services with a service collection.
30
+
31
+ ### 3. `configure(configurator: IModuleConfigurator): void | Promise<void>`
32
+
33
+ Allows a module to perform additional configuration, such as configure a dependency module. This method supports async operations which can be useful if data needs to be fetched from a remote service during configuration.
34
+
35
+ ## Dependencies
36
+
37
+ Module depenedencies are specified by defining a `dependencies` property on a module and lifecycle methods get invoked on the dependency before they are invoked on the dependent.
38
+
39
+ ```typescript
40
+ export class FooModule implements IModule {
41
+ readonly name = "foo";
42
+ readonly dependencies = [BarModule];
43
+ }
44
+ ```
45
+
46
+ ## Configuration
47
+
48
+ ```typescript
49
+ export const IBarModuleConfiguration = createConfig<IBarModuleConfiguration>();
50
+ export interface IBarModuleConfiguration {
51
+ registerWidget(widget: IWidget): void;
52
+ }
53
+
54
+ export class BarModule implements IModule {
55
+ readonly name = "bar";
56
+
57
+ initialize({ config }: IModuleInitializer): void {
58
+ config(IBarModuleConfiguration).register(() => ({
59
+ registerWidget: widget => {}
60
+ }));
61
+ }
62
+ }
63
+
64
+ export class FooModule implements IModule {
65
+ readonly name = "foo";
66
+ readonly dependencies = [BarModule];
67
+
68
+ configure({ config }: IModuleConfigurator): void {
69
+ config.get(IBarModuleConfiguration).registerWidget({});
70
+ }
71
+ }
72
+ ```
73
+
74
+ ## Settings and Options
75
+
76
+ While Dependency Configuration is a way for one module to configure another at load time. Settings provide a way to provide settings/configuration externally, such as from a config file.
77
+
78
+ Module Settings are provided as a simple object keyed by a module's name; for example, the below is an example settings object that defines settings for the 'foo' module:
79
+
80
+ ```typescript
81
+ const settings = {
82
+ foo: {
83
+ key: value
84
+ }
85
+ };
86
+ ```
87
+
88
+ A module can access these settings directly from the `configure` method:
89
+
90
+ ```typescript
91
+ export class FooModule implements IModule {
92
+ readonly name = "foo";
93
+
94
+ configure({ settings }: IModuleConfigurator): void {
95
+ const keyValue = settings.key;
96
+ }
97
+ }
98
+ ```
99
+
100
+ Sometimes it's useful to pass settings to service instances and this can be done via Service Options.
101
+
102
+ ```typescript
103
+ export const IFooOptions = createOptions<IFooOptions>("foo-options");
104
+ export interface IFooOptions {
105
+ readonly value: string;
106
+ }
107
+
108
+ export class FooModule implements IModule {
109
+ readonly name = "foo";
110
+
111
+ initialize({ settings }: IModuleInitializer): void {
112
+ settings.bindToOptions(IFooOptions);
113
+ }
114
+
115
+ configureServices(registration: IServiceRegistration): void {
116
+ registration.registerTransient(IFooService, FooService);
117
+ }
118
+ }
119
+
120
+ export class FooService implements IFooService {
121
+ constructor(@IFooOptions private readonly options: IFooOptions) {
122
+ }
123
+ }
124
+ ```
125
+
126
+ ## Loading Modules
127
+
128
+ Modules are loaded using the `ModuleLoader` class by simply invoking `ModuleLoader.load`.
129
+
130
+ ```typescript
131
+ await ModuleLoader.load([
132
+ FooModule,
133
+ BarModule
134
+ ]);
135
+ ```
136
+
137
+ Note: If a module has any dependencies not specified when calling `ModuleLoader.load` those dependencies will automatically get loaded.
138
+
139
+ If you want a little more control the module loader provides additional methods for configuring the service collection or settings.
140
+
141
+ ```typescript
142
+ await ModuleLoader()
143
+ .useModules([
144
+ FooModule,
145
+ BarModule
146
+ ])
147
+ .useSettings({
148
+ foo: { value: "Hello!" }
149
+ })
150
+ .load();
151
+ ```
@@ -0,0 +1,3 @@
1
+ export * from "./module";
2
+ export * from "./service-collection";
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYyxVQUFVLENBQUM7QUFDekIsY0FBYyxzQkFBc0IsQ0FBQyJ9
@@ -0,0 +1,247 @@
1
+ import { ServiceMap } from "./service-collection";
2
+ /** Creates a module configuration used to configure a module. */
3
+ export function createConfig() {
4
+ return { key: Symbol() };
5
+ }
6
+ export class ModuleLoadError extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ Object.setPrototypeOf(this, ModuleLoadError.prototype);
10
+ }
11
+ }
12
+ /** Handles initializing and loading modules. */
13
+ export class ModuleLoader {
14
+ constructor() {
15
+ this.services = new ServiceMap();
16
+ this.modules = [];
17
+ this.settings = {};
18
+ }
19
+ static load(modulesOrOptions) {
20
+ const options = Array.isArray(modulesOrOptions) ? { modules: modulesOrOptions } : modulesOrOptions;
21
+ const loader = new ModuleLoader();
22
+ if (options.settings) {
23
+ loader.useSettings(options.settings);
24
+ }
25
+ return loader.useModules(options.modules).load();
26
+ }
27
+ static configureServices(callback) {
28
+ return new ModuleLoader().configureServices(callback);
29
+ }
30
+ static useModules(modules) {
31
+ return new ModuleLoader().useModules(modules);
32
+ }
33
+ static useSettings(settings) {
34
+ return new ModuleLoader().useSettings(settings);
35
+ }
36
+ /**
37
+ * Loads the specified core modules into the manager. The load operation will discover
38
+ * the core module dependencies and then initialize/configure each module.
39
+ *
40
+ * This will first discover all dependent modules, create module instances, and sort the
41
+ * list of discovered modules by dependency (note: there is no guarantee of the order when creating
42
+ * module instances but each step against the discovered modules will execute against module dependents first).
43
+ *
44
+ * Module Steps:
45
+ *
46
+ * 1) Initialize
47
+ * 2) Configure Services
48
+ * 3) Configure
49
+ */
50
+ async load() {
51
+ this.ensureNotLoaded();
52
+ this.isLoaded = true;
53
+ // get all modules and sort by dependencies
54
+ const loadedModules = await this.expandAndSortModules();
55
+ // initialize discovered modules
56
+ const loader = this;
57
+ const configs = new Map();
58
+ loadedModules.forEach(module => {
59
+ if (module.initialize) {
60
+ module.initialize({
61
+ config: type => ({
62
+ register: callback => configs.set(type.key, { callback, module })
63
+ }),
64
+ get settings() {
65
+ return {
66
+ bindToOptions: (options, sectionName) => {
67
+ loader.services.addOptionsProvider({
68
+ tryGet: (opt) => {
69
+ if (options === opt) {
70
+ const settings = loader.getSettingsForModule(module);
71
+ return sectionName ? settings[sectionName] : settings;
72
+ }
73
+ return undefined;
74
+ }
75
+ });
76
+ }
77
+ };
78
+ }
79
+ });
80
+ }
81
+ });
82
+ // configure the module services
83
+ for (const module of loadedModules) {
84
+ if (module.configureServices) {
85
+ module.configureServices(this.services);
86
+ }
87
+ }
88
+ this.services.freeze();
89
+ // lastly, configure the modules
90
+ const iterator = loadedModules[Symbol.iterator]();
91
+ const next = async () => {
92
+ for (const module of iterator) {
93
+ await configure(module);
94
+ }
95
+ };
96
+ const self = this;
97
+ const configure = (module) => module.configure && module.configure({
98
+ services: this.services,
99
+ settings: this.getSettingsForModule(module),
100
+ get options() {
101
+ return {
102
+ get: (options) => self.services.getOptions(options)
103
+ };
104
+ },
105
+ get config() {
106
+ return {
107
+ get: (type) => {
108
+ const item = configs.get(type.key);
109
+ if (!item) {
110
+ throw new Error(`Config ${type.key.toString()} not found`);
111
+ }
112
+ return item.callback({
113
+ ...this,
114
+ // need to grab the module's settings that is being configured
115
+ settings: loader.getSettingsForModule(item.module)
116
+ });
117
+ }
118
+ };
119
+ },
120
+ next
121
+ });
122
+ // invoke the iterator to start configuring the modules
123
+ return next().then(() => ({
124
+ services: this.services,
125
+ getInstance: (ctor) => {
126
+ const result = loadedModules.find(module => module instanceof ctor);
127
+ if (!result) {
128
+ throw new Error("Module instance not found");
129
+ }
130
+ return result;
131
+ }
132
+ }));
133
+ }
134
+ configureServices(callback) {
135
+ this.ensureNotLoaded();
136
+ callback(this.services);
137
+ return this;
138
+ }
139
+ useModules(modules) {
140
+ this.ensureNotLoaded();
141
+ this.modules.push(...modules);
142
+ return this;
143
+ }
144
+ useSettings(settings) {
145
+ this.ensureNotLoaded();
146
+ this.settings = this.merge(settings, this.settings);
147
+ return this;
148
+ }
149
+ ensureNotLoaded() {
150
+ if (this.isLoaded) {
151
+ throw new Error("Modules have already been loaded.");
152
+ }
153
+ }
154
+ getSettingsForModule(module) {
155
+ return this.settings[module.name] || {};
156
+ }
157
+ async expandAndSortModules() {
158
+ const instances = new Map();
159
+ const sorted = [];
160
+ const visited = {};
161
+ const getInstance = async (dependency) => {
162
+ if (typeof dependency === "function") {
163
+ let instance = instances.get(dependency);
164
+ if (!instance) {
165
+ try {
166
+ instance = new dependency();
167
+ if (instance.constructor === dependency) {
168
+ // save the instance if the dependency is a constructor
169
+ instances.set(dependency, instance);
170
+ }
171
+ }
172
+ catch {
173
+ // the dependency is a non-constructble function so invoke and assume the return will be a promise and use Promise.resolve to resolve it
174
+ const instanceOrCtor = await Promise.resolve(dependency());
175
+ instance = await getInstance(instanceOrCtor);
176
+ }
177
+ }
178
+ // resolve incase the dependency is a function that returned a promise
179
+ return instance;
180
+ }
181
+ if (typeof dependency === "object") {
182
+ if (!this.isModule(dependency)) {
183
+ throw new ModuleLoadError(`Object (${dependency.constructor.name}) is not a module.`);
184
+ }
185
+ const ctor = dependency.constructor;
186
+ if (ctor !== Object.prototype.constructor) {
187
+ if (instances.has(ctor)) {
188
+ throw new ModuleLoadError(`Duplicate module instances ${dependency.name}.`);
189
+ }
190
+ instances.set(ctor, dependency);
191
+ }
192
+ return dependency;
193
+ }
194
+ throw new ModuleLoadError(`Invalid dependency type (${typeof dependency}).`);
195
+ };
196
+ const visit = async (dependency, ancestors) => {
197
+ const module = await getInstance(dependency);
198
+ if (visited[module.name]) {
199
+ return;
200
+ }
201
+ if (!ancestors) {
202
+ ancestors = {};
203
+ }
204
+ if (ancestors[module.name]) {
205
+ throw new ModuleLoadError(`Circular dependency has been detected with module ${module.name}`);
206
+ }
207
+ ancestors[module.name] = true;
208
+ if (module.dependencies) {
209
+ for (const dependency of module.dependencies) {
210
+ await visit(dependency, ancestors);
211
+ }
212
+ }
213
+ visited[module.name] = true;
214
+ // this performs a depth-first topological sort but instead of inserting
215
+ // to the front, push the module to the back of the list so that dependencies
216
+ // come first
217
+ sorted.push(module);
218
+ };
219
+ for (const module of this.modules) {
220
+ await visit(module);
221
+ }
222
+ return sorted;
223
+ }
224
+ merge(source, target) {
225
+ if (this.isObject(source) && this.isObject(target)) {
226
+ for (const key in source) {
227
+ if (source[key] === undefined) {
228
+ // do not merge if the source value is undefined
229
+ continue;
230
+ }
231
+ target = {
232
+ ...target,
233
+ [key]: this.isObject(source[key]) ? this.merge(source[key], target[key] || {}) : source[key]
234
+ };
235
+ }
236
+ }
237
+ return target;
238
+ }
239
+ isObject(obj) {
240
+ return typeof obj === "object" && !Array.isArray(obj);
241
+ }
242
+ isModule(obj) {
243
+ // a module only requires a name so that's all we can really check for
244
+ return typeof obj === "object" && obj.name !== undefined;
245
+ }
246
+ }
247
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kdWxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL21vZHVsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQXNELFVBQVUsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBcUZ0RyxpRUFBaUU7QUFDakUsTUFBTSxVQUFVLFlBQVk7SUFDeEIsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLEVBQUUsRUFBRSxDQUFDO0FBQzdCLENBQUM7QUFFRCxNQUFNLE9BQU8sZUFBZ0IsU0FBUSxLQUFLO0lBQ3RDLFlBQVksT0FBZTtRQUN2QixLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDZixNQUFNLENBQUMsY0FBYyxDQUFDLElBQUksRUFBRSxlQUFlLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDM0QsQ0FBQztDQUNKO0FBRUQsZ0RBQWdEO0FBQ2hELE1BQU0sT0FBTyxZQUFZO0lBQXpCO1FBQ3FCLGFBQVEsR0FBRyxJQUFJLFVBQVUsRUFBRSxDQUFDO1FBQzVCLFlBQU8sR0FBa0MsRUFBRSxDQUFDO1FBQ3JELGFBQVEsR0FBOEIsRUFBRSxDQUFDO0lBb1JyRCxDQUFDO0lBL1FHLE1BQU0sQ0FBQyxJQUFJLENBQUMsZ0JBQThEO1FBQ3RFLE1BQU0sT0FBTyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsQ0FBQyxDQUFDLENBQUMsZ0JBQWdCLENBQUM7UUFDbkcsTUFBTSxNQUFNLEdBQUcsSUFBSSxZQUFZLEVBQUUsQ0FBQztRQUVsQyxJQUFJLE9BQU8sQ0FBQyxRQUFRLEVBQUU7WUFDbEIsTUFBTSxDQUFDLFdBQVcsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUM7U0FDeEM7UUFFRCxPQUFPLE1BQU0sQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO0lBQ3JELENBQUM7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