@bluelibs/runner 1.2.0 → 1.3.0
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/README.md +151 -134
- package/dist/define.d.ts +2 -2
- package/dist/define.js +12 -9
- package/dist/define.js.map +1 -1
- package/dist/defs.d.ts +15 -6
- package/dist/globalEvents.d.ts +3 -1
- package/dist/globalEvents.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/models/DependencyProcessor.js +12 -5
- package/dist/models/DependencyProcessor.js.map +1 -1
- package/dist/models/EventManager.d.ts +2 -1
- package/dist/models/EventManager.js +47 -21
- package/dist/models/EventManager.js.map +1 -1
- package/dist/models/ResourceInitializer.d.ts +1 -0
- package/dist/models/ResourceInitializer.js +35 -3
- package/dist/models/ResourceInitializer.js.map +1 -1
- package/dist/models/Store.d.ts +1 -0
- package/dist/models/Store.js +10 -0
- package/dist/models/Store.js.map +1 -1
- package/dist/models/TaskRunner.js +6 -2
- package/dist/models/TaskRunner.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/index.ts +1 -0
- package/src/__tests__/models/EventManager.test.ts +338 -67
- package/src/__tests__/models/ResourceInitializer.test.ts +1 -0
- package/src/__tests__/models/TaskRunner.test.ts +4 -2
- package/src/__tests__/run.hooks.test.ts +31 -0
- package/src/__tests__/run.middleware.test.ts +223 -0
- package/src/__tests__/run.test.ts +112 -131
- package/src/define.ts +15 -11
- package/src/defs.ts +17 -7
- package/src/globalEvents.ts +4 -2
- package/src/models/DependencyProcessor.ts +17 -8
- package/src/models/EventManager.ts +54 -25
- package/src/models/ResourceInitializer.ts +49 -4
- package/src/models/Store.ts +14 -0
- package/src/models/TaskRunner.ts +5 -2
package/README.md
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
<a href="https://bluelibs.github.io/runner/" target="_blank"><img src="https://img.shields.io/badge/read-typedocs-blue" alt="Docs" /></a>
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
BlueLibs Runner is a framework that provides a functional approach to building applications, whether small or large-scale. Its core concepts include Tasks, Resources, Events, and Middleware. Tasks represent the units of logic, while resources are singletons that provide shared services across the application. Events facilitate communication between different parts of the system, and middleware allows interception and modification of task execution. The framework emphasizes an async-first philosophy, ensuring that all operations are executed asynchronously for smoother application flow.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
## Building Blocks
|
|
12
12
|
|
|
13
13
|
- **Tasks**: Core units of logic that encapsulate specific tasks. They can depend on resources, other tasks, and event emitters.
|
|
14
14
|
- **Resources**: Singleton objects providing shared functionality. They can be constants, services, functions. They can depend on other resources, tasks, and event emitters.
|
|
@@ -89,7 +89,7 @@ Resources are more like services, they are singletons, they are meant to be used
|
|
|
89
89
|
|
|
90
90
|
### Resource dispose()
|
|
91
91
|
|
|
92
|
-
Resources can have a `dispose()` method that can be used
|
|
92
|
+
Resources can have a `dispose()` method that can be used for cleanups. This is useful for cleaning up resources like closing database connections, etc. You typically want to use this when you have opened pending connections or you need to do some cleanup or a graceful shutdown.
|
|
93
93
|
|
|
94
94
|
```ts
|
|
95
95
|
import { task, run, resource } from "@bluelibs/runner";
|
|
@@ -99,13 +99,14 @@ const dbResource = resource({
|
|
|
99
99
|
const db = await connectToDatabase();
|
|
100
100
|
return db;
|
|
101
101
|
},
|
|
102
|
+
// the value returned from init() will be passed to dispose()
|
|
102
103
|
async dispose(db, config, deps) {
|
|
103
104
|
return db.close();
|
|
104
105
|
},
|
|
105
106
|
});
|
|
106
107
|
```
|
|
107
108
|
|
|
108
|
-
If you want to call dispose, you have to do it through the global store.
|
|
109
|
+
If you want to call dispose, you have to do it through the global resource called `store`, as everything is encapsulated.
|
|
109
110
|
|
|
110
111
|
```ts
|
|
111
112
|
import { task, run, resource, globals } from "@bluelibs/runner";
|
|
@@ -117,6 +118,7 @@ const app = resource({
|
|
|
117
118
|
store: globals.resources.store,
|
|
118
119
|
},
|
|
119
120
|
async init(_, deps) {
|
|
121
|
+
// We use the fact that we can reuse the value we got from here
|
|
120
122
|
return {
|
|
121
123
|
dispose: async () => deps.store.dispose(),
|
|
122
124
|
};
|
|
@@ -128,15 +130,43 @@ const value = await run(app);
|
|
|
128
130
|
await value.dispose();
|
|
129
131
|
```
|
|
130
132
|
|
|
131
|
-
|
|
133
|
+
### Resource with()
|
|
132
134
|
|
|
133
|
-
|
|
135
|
+
Resources can be configured with a configuration object. This is useful when you want to pass in configuration to them. For example, you're building a library and you're initialising a mailer service, you can pass in the SMTP credentials as a configuration.
|
|
134
136
|
|
|
135
|
-
|
|
137
|
+
```ts
|
|
138
|
+
import { task, run, resource } from "@bluelibs/runner";
|
|
139
|
+
|
|
140
|
+
type Config = { smtpUrl: string; defaultFrom: string };
|
|
141
|
+
const emailerResource = resource({
|
|
142
|
+
async init(config: Config) {
|
|
143
|
+
// run config checks
|
|
144
|
+
return {
|
|
145
|
+
sendEmail: async (to: string, subject: string, body: string) => {
|
|
146
|
+
// send *email*
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const app = resource({
|
|
153
|
+
id: "app",
|
|
154
|
+
register: [
|
|
155
|
+
// proper autocompletion is present
|
|
156
|
+
emailerResource.with({ smtpUrl: "smtp://localhost", defaultFrom: "" }),
|
|
157
|
+
],
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
If by any chance your main `app` has configs then they will be passed via the second argument of `run`, like this:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
run(app, config);
|
|
165
|
+
```
|
|
136
166
|
|
|
137
167
|
## Dependencies
|
|
138
168
|
|
|
139
|
-
You can depend on `tasks`, `resources`, `events` and `middleware`.
|
|
169
|
+
You can depend on `tasks`, `resources`, `events` and (indirectly) on `middleware`.
|
|
140
170
|
|
|
141
171
|
```ts
|
|
142
172
|
import { task, resource, run, event } from "@bluelibs/runner";
|
|
@@ -150,7 +180,7 @@ const helloWorld = task({
|
|
|
150
180
|
|
|
151
181
|
const app = resource({
|
|
152
182
|
id: "app",
|
|
153
|
-
register: [helloWorld],
|
|
183
|
+
register: [helloWorld, logMiddleware],
|
|
154
184
|
dependencies: {
|
|
155
185
|
helloWorld,
|
|
156
186
|
},
|
|
@@ -211,14 +241,14 @@ const helloTask = task({
|
|
|
211
241
|
},
|
|
212
242
|
});
|
|
213
243
|
|
|
214
|
-
const
|
|
244
|
+
const app = resource({
|
|
215
245
|
id: "app",
|
|
216
246
|
register: [afterRegisterEvent, helloTask],
|
|
217
247
|
dependencies: {
|
|
218
248
|
afterRegisterEvent,
|
|
219
249
|
},
|
|
220
250
|
async init(_, deps) {
|
|
221
|
-
deps.afterRegisterEvent({ userId: "XXX" });
|
|
251
|
+
await deps.afterRegisterEvent({ userId: "XXX" });
|
|
222
252
|
},
|
|
223
253
|
});
|
|
224
254
|
```
|
|
@@ -242,6 +272,7 @@ const root = resource({
|
|
|
242
272
|
{
|
|
243
273
|
event: global.events.afterInit,
|
|
244
274
|
async run(event, deps) {
|
|
275
|
+
// both dependencies and event are properly infered through typescript
|
|
245
276
|
console.log("User has been registered!");
|
|
246
277
|
},
|
|
247
278
|
},
|
|
@@ -252,16 +283,55 @@ const root = resource({
|
|
|
252
283
|
});
|
|
253
284
|
```
|
|
254
285
|
|
|
286
|
+
### hooks wildcard
|
|
287
|
+
|
|
288
|
+
You can listen to all events by using the wildcard `*`.
|
|
289
|
+
|
|
290
|
+
```ts
|
|
291
|
+
import { task, resource, run, event, global } from "@bluelibs/runner";
|
|
292
|
+
|
|
293
|
+
const afterRegisterEvent = event<{ userId: string }>({
|
|
294
|
+
id: "app.user.registered",
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
const root = resource({
|
|
298
|
+
id: "app",
|
|
299
|
+
register: [afterRegisterEvent],
|
|
300
|
+
dependencies: {},
|
|
301
|
+
hooks: [
|
|
302
|
+
{
|
|
303
|
+
event: "*",
|
|
304
|
+
async run(event, deps) {
|
|
305
|
+
console.log(
|
|
306
|
+
"Generic event detected",
|
|
307
|
+
event.id,
|
|
308
|
+
event.data,
|
|
309
|
+
event.timestamp
|
|
310
|
+
);
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
],
|
|
314
|
+
async init(_, deps) {
|
|
315
|
+
deps.afterRegisterEvent({ userId: "XXX" });
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
```
|
|
319
|
+
|
|
255
320
|
When using hooks, inside resource() you benefit of autocompletion, in order to keep things clean, if your hooks become large and long consider switching to tasks and `on`. This is a more explicit way to listen to events, and your resource registers them.
|
|
256
321
|
|
|
257
322
|
The hooks from a `resource` are mostly used for configuration, and blending in the system.
|
|
258
323
|
|
|
324
|
+
### When to use either?
|
|
325
|
+
|
|
326
|
+
- `hooks` are for resources to extend each other, compose functionalities, they are mostly used for configuration and blending in the system.
|
|
327
|
+
- `on` is for when you want to perform a task when something happens.
|
|
328
|
+
|
|
259
329
|
## Middleware
|
|
260
330
|
|
|
261
|
-
Middleware is a way to intercept the execution of tasks. It's a powerful way to add additional functionality
|
|
331
|
+
Middleware is a way to intercept the execution of tasks or initialization of resources. It's a powerful way to add additional functionality. First middleware that gets registered is the first that runs, giving it a form of priority, the last middleware that runs is 'closest' to the task, most likely the last element inside `middleware` array at task level.
|
|
262
332
|
|
|
263
333
|
```ts
|
|
264
|
-
import { task, run, event } from "@bluelibs/runner";
|
|
334
|
+
import { task, resource, run, event } from "@bluelibs/runner";
|
|
265
335
|
|
|
266
336
|
const logMiddleware = middleware({
|
|
267
337
|
id: "app.middleware.log",
|
|
@@ -269,11 +339,18 @@ const logMiddleware = middleware({
|
|
|
269
339
|
// inject tasks, resources, eventCallers here.
|
|
270
340
|
},
|
|
271
341
|
async run(data, deps) {
|
|
272
|
-
const { taskDefinition, next, input } = data;
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
342
|
+
const { taskDefinition, resourceDefinition, config, next, input } = data;
|
|
343
|
+
|
|
344
|
+
// The middleware can be for a task or a resource, depending on which you get the right elements.
|
|
345
|
+
if (taskDefinition) {
|
|
346
|
+
console.log("Before task", taskDefinition.id);
|
|
347
|
+
const result = await next(input); // pass the input to the next middleware or task
|
|
348
|
+
console.log("After task", taskDefinition.id);
|
|
349
|
+
} else {
|
|
350
|
+
console.log("Before resource", resourceDefinition.id);
|
|
351
|
+
const result = await next(config); // pass the input to the next middleware or task
|
|
352
|
+
console.log("After resource", resourceDefinition.id);
|
|
353
|
+
}
|
|
277
354
|
|
|
278
355
|
return result;
|
|
279
356
|
},
|
|
@@ -288,34 +365,14 @@ const helloTask = task({
|
|
|
288
365
|
});
|
|
289
366
|
```
|
|
290
367
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
```ts
|
|
294
|
-
import { middleware } from "@bluelibs/runner";
|
|
295
|
-
|
|
296
|
-
function createLogMiddleware(config) {
|
|
297
|
-
return middleware({
|
|
298
|
-
// your config-based middleware here.
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
However, if you want to register a middleware for all tasks, here's how you can do it:
|
|
368
|
+
However, if you want to register a middleware for all tasks and resources, here's how you can do it:
|
|
304
369
|
|
|
305
370
|
```ts
|
|
306
371
|
import { run, resource } from "@bluelibs/runner";
|
|
307
372
|
|
|
308
373
|
const logMiddleware = middleware({
|
|
309
374
|
id: "app.middleware.log",
|
|
310
|
-
|
|
311
|
-
const { taskDefinition, next, input } = data;
|
|
312
|
-
|
|
313
|
-
console.log("Before task", task.id);
|
|
314
|
-
const result = await next(input);
|
|
315
|
-
console.log("After task", task.id);
|
|
316
|
-
|
|
317
|
-
return result;
|
|
318
|
-
},
|
|
375
|
+
// ... rest
|
|
319
376
|
});
|
|
320
377
|
|
|
321
378
|
const root = resource({
|
|
@@ -326,20 +383,9 @@ const root = resource({
|
|
|
326
383
|
|
|
327
384
|
The middleware can only be registered once. This means that if you register a middleware as global, you cannot specify it as a task middleware.
|
|
328
385
|
|
|
329
|
-
### Middleware for resources
|
|
330
|
-
|
|
331
|
-
Unfortunately, middleware for resources is not supported at the moment. The main reason for this is simplicity and the fact that resources are not meant to be executed, but rather to be initialized.
|
|
332
|
-
|
|
333
|
-
You have access to the global events if you want to hook into the initialisation system.
|
|
334
|
-
|
|
335
|
-
### When to use either?
|
|
336
|
-
|
|
337
|
-
- `hooks` are for resources to extend each other, compose functionalities, they are mostly used for configuration and blending in the system.
|
|
338
|
-
- `on` is for when you want to perform a task when something happens.
|
|
339
|
-
|
|
340
386
|
## Errors
|
|
341
387
|
|
|
342
|
-
If an error is thrown in a task, the error will be propagated up.
|
|
388
|
+
If an error is thrown in a task, the error will be propagated up to the top runner.
|
|
343
389
|
|
|
344
390
|
```ts
|
|
345
391
|
import { task, run, event } from "@bluelibs/runner";
|
|
@@ -362,7 +408,9 @@ const app = resource({
|
|
|
362
408
|
},
|
|
363
409
|
});
|
|
364
410
|
|
|
365
|
-
run(app)
|
|
411
|
+
run(app).catch((err) => {
|
|
412
|
+
console.error(err);
|
|
413
|
+
});
|
|
366
414
|
```
|
|
367
415
|
|
|
368
416
|
You can listen to errors via events:
|
|
@@ -371,8 +419,11 @@ You can listen to errors via events:
|
|
|
371
419
|
const helloWorld = task({
|
|
372
420
|
id: "app.onError",
|
|
373
421
|
on: helloWorld.events.onError,
|
|
374
|
-
run({ error, input }, deps) {
|
|
422
|
+
run({ error, input, suppress }, deps) {
|
|
375
423
|
// this will be called when an error happens
|
|
424
|
+
|
|
425
|
+
// if you handled the error, and you don't want it propagated to the top, supress the propagation.
|
|
426
|
+
suppress();
|
|
376
427
|
},
|
|
377
428
|
});
|
|
378
429
|
```
|
|
@@ -397,7 +448,7 @@ const helloWorld = task({
|
|
|
397
448
|
});
|
|
398
449
|
```
|
|
399
450
|
|
|
400
|
-
This is particularly helpful to use in conjunction with global middlewares, or global events,
|
|
451
|
+
This is particularly helpful to use in conjunction with global middlewares, or global events, they can read some meta tag definition and act accordingly.
|
|
401
452
|
|
|
402
453
|
The interfaces look like this:
|
|
403
454
|
|
|
@@ -495,7 +546,7 @@ Now you can freely use any of the tasks, resources, events, and middlewares from
|
|
|
495
546
|
|
|
496
547
|
This approach is very powerful when you have multiple packages and you want to compose them together.
|
|
497
548
|
|
|
498
|
-
## Real world
|
|
549
|
+
## Real world examples
|
|
499
550
|
|
|
500
551
|
Typically you have an express server (to handle HTTP requests), a database, and a bunch of services. You can define all of these in a single file and run them.
|
|
501
552
|
|
|
@@ -570,47 +621,7 @@ const app = resource({
|
|
|
570
621
|
run();
|
|
571
622
|
```
|
|
572
623
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
Resources are super configurable.
|
|
576
|
-
|
|
577
|
-
```ts
|
|
578
|
-
import { resource, run } from "@bluelibs/runner";
|
|
579
|
-
|
|
580
|
-
type EmailerOptions = {
|
|
581
|
-
smtpUrl: string;
|
|
582
|
-
defaultFrom: string;
|
|
583
|
-
};
|
|
584
|
-
|
|
585
|
-
const emailerResource = resource({
|
|
586
|
-
id: "app.config",
|
|
587
|
-
async init(config: EmailerOptions) {
|
|
588
|
-
return {
|
|
589
|
-
sendEmail: async (to: string, subject: string, body: string) => {
|
|
590
|
-
// send *email*
|
|
591
|
-
},
|
|
592
|
-
};
|
|
593
|
-
// or return some service that sends email
|
|
594
|
-
},
|
|
595
|
-
});
|
|
596
|
-
|
|
597
|
-
const app = resource({
|
|
598
|
-
id: "app",
|
|
599
|
-
register: [
|
|
600
|
-
// You can pass the config here
|
|
601
|
-
emailerResource.with({
|
|
602
|
-
smtpUrl: "smtp://localhost",
|
|
603
|
-
defaultFrom: "",
|
|
604
|
-
}),
|
|
605
|
-
// Leaving it simply emailerResource is similar to passing an empty object.
|
|
606
|
-
// We leave this for simplicity in some cases but we recommend using .with() for clarity.
|
|
607
|
-
],
|
|
608
|
-
});
|
|
609
|
-
|
|
610
|
-
run(app);
|
|
611
|
-
```
|
|
612
|
-
|
|
613
|
-
## Useful events
|
|
624
|
+
## Global Events
|
|
614
625
|
|
|
615
626
|
### Task level
|
|
616
627
|
|
|
@@ -650,15 +661,19 @@ const app = resource({
|
|
|
650
661
|
run(app);
|
|
651
662
|
```
|
|
652
663
|
|
|
653
|
-
|
|
664
|
+
### Business Config
|
|
654
665
|
|
|
655
666
|
```ts
|
|
656
667
|
import { task, run, event } from "@bluelibs/runner";
|
|
657
668
|
|
|
669
|
+
const businessData = {
|
|
670
|
+
pricePerSubscription: 9.99,
|
|
671
|
+
};
|
|
672
|
+
|
|
658
673
|
const businessConfig = resource({
|
|
659
674
|
id: "app.config",
|
|
660
675
|
async init() {
|
|
661
|
-
return businessData;
|
|
676
|
+
return businessData; // if you use it as a const you will have full typesafety
|
|
662
677
|
},
|
|
663
678
|
});
|
|
664
679
|
|
|
@@ -690,10 +705,12 @@ const app = resource({
|
|
|
690
705
|
run(app);
|
|
691
706
|
```
|
|
692
707
|
|
|
693
|
-
|
|
708
|
+
### Moving further
|
|
694
709
|
|
|
695
710
|
This is just a "language" of developing applications. It simplifies dependency injection to the barebones, it forces you to think more functional and use classes less.
|
|
696
711
|
|
|
712
|
+
This doesn't mean you shouldn't use classes, just not for hooking things up together.
|
|
713
|
+
|
|
697
714
|
You can add many services or external things into the runner ecosystem with things like:
|
|
698
715
|
|
|
699
716
|
```ts
|
|
@@ -743,7 +760,7 @@ const app = resource({
|
|
|
743
760
|
run(app);
|
|
744
761
|
```
|
|
745
762
|
|
|
746
|
-
|
|
763
|
+
### Inter-communication between resources
|
|
747
764
|
|
|
748
765
|
By stating dependencies you often don't care about the initialisation order, but sometimes you really do, for example, let's imagine a security service that allows you to inject a custom hashing function let's say to shift from md5 to sha256.
|
|
749
766
|
|
|
@@ -829,6 +846,39 @@ const app = resource({
|
|
|
829
846
|
});
|
|
830
847
|
```
|
|
831
848
|
|
|
849
|
+
## Overrides
|
|
850
|
+
|
|
851
|
+
Previously, we explored how we can extend functionality through events. However, sometimes you want to override a resource with a new one or simply swap out a task or a middleware that you import from another package and they don't offer the ability.
|
|
852
|
+
|
|
853
|
+
```ts
|
|
854
|
+
import { resource, run, event } from "@bluelibs/runner";
|
|
855
|
+
|
|
856
|
+
// This example is for resources but override works for tasks, events, and middleware as well.
|
|
857
|
+
const securityResource = resource({
|
|
858
|
+
id: "app.security",
|
|
859
|
+
async init() {
|
|
860
|
+
// returns a security service
|
|
861
|
+
},
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
const override = resource({
|
|
865
|
+
...securityResource,
|
|
866
|
+
init: async () => {
|
|
867
|
+
// a new and custom service
|
|
868
|
+
},
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
const app = resource({
|
|
872
|
+
id: "app",
|
|
873
|
+
register: [securityResource], // this resource might be registered by any element in the dependency tree.
|
|
874
|
+
overrides: [override],
|
|
875
|
+
});
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
Now the `securityResource` will be overriden by the new one and whenever it's used it will use the new one.
|
|
879
|
+
|
|
880
|
+
Overrides can only happen once and only if the overriden resource is registered. If two resources try to override the same resource, an error will be thrown.
|
|
881
|
+
|
|
832
882
|
## Logging
|
|
833
883
|
|
|
834
884
|
We expose through globals a logger that you can use to log things.
|
|
@@ -891,39 +941,6 @@ You can in theory do it in `hooks` as well, but as specified `hooks` are mostly
|
|
|
891
941
|
|
|
892
942
|
The logger's `log()` function is async as it works with events. If you don't want your system hanging on logs, simply omit the `await`
|
|
893
943
|
|
|
894
|
-
## Overrides
|
|
895
|
-
|
|
896
|
-
Previously, we explored how we can extend functionality through events. However, sometimes you want to override a resource with a new one or simply swap out a task or a middleware that you import from another package and they don't offer the ability.
|
|
897
|
-
|
|
898
|
-
```ts
|
|
899
|
-
import { resource, run, event } from "@bluelibs/runner";
|
|
900
|
-
|
|
901
|
-
// This example is for resources but override works for tasks, events, and middleware as well.
|
|
902
|
-
const securityResource = resource({
|
|
903
|
-
id: "app.security",
|
|
904
|
-
async init() {
|
|
905
|
-
// returns a security service
|
|
906
|
-
},
|
|
907
|
-
});
|
|
908
|
-
|
|
909
|
-
const override = resource({
|
|
910
|
-
...securityResource,
|
|
911
|
-
init: async () => {
|
|
912
|
-
// a new and custom service
|
|
913
|
-
},
|
|
914
|
-
});
|
|
915
|
-
|
|
916
|
-
const app = resource({
|
|
917
|
-
id: "app",
|
|
918
|
-
register: [securityResource], // this resource might be registered by any element in the dependency tree.
|
|
919
|
-
overrides: [override],
|
|
920
|
-
});
|
|
921
|
-
```
|
|
922
|
-
|
|
923
|
-
Now the `securityResource` will be overriden by the new one and whenever it's used it will use the new one.
|
|
924
|
-
|
|
925
|
-
Overrides can only happen once and only if the overriden resource is registered. If two resources try to override the same resource, an error will be thrown.
|
|
926
|
-
|
|
927
944
|
## Testing
|
|
928
945
|
|
|
929
946
|
### Unit Testing
|
package/dist/define.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ITask, ITaskDefinition, IResource, IResourceWithConfig, IResourceDefinintion, IEventDefinition, IMiddlewareDefinition, DependencyMapType, IMiddleware } from "./defs";
|
|
2
|
-
export declare function defineTask<Input = undefined, Output extends Promise<any> = any, Deps extends DependencyMapType = any, Test = any>(
|
|
3
|
-
export declare function defineResource<TConfig = void, TValue = any, TDeps extends DependencyMapType = {}>(constConfig: IResourceDefinintion<TConfig, TValue, TDeps>): IResource<TConfig, TValue, TDeps>;
|
|
2
|
+
export declare function defineTask<Input = undefined, Output extends Promise<any> = any, Deps extends DependencyMapType = any, Test = any>(taskConfig: ITaskDefinition<Input, Output, Deps, Test>): ITask<Input, Output, Deps, Test>;
|
|
3
|
+
export declare function defineResource<TConfig = void, TValue = any, TDeps extends DependencyMapType = {}, THooks = any>(constConfig: IResourceDefinintion<TConfig, TValue, TDeps, THooks>): IResource<TConfig, TValue, TDeps>;
|
|
4
4
|
export declare function defineEvent<TPayload = any>(config: IEventDefinition<TPayload>): IEventDefinition<TPayload>;
|
|
5
5
|
export declare function defineMiddleware<TDeps extends DependencyMapType = {}>(config: IMiddlewareDefinition<TDeps>): IMiddleware<TDeps>;
|
|
6
6
|
export declare function isTask(definition: any): definition is ITask;
|
package/dist/define.js
CHANGED
|
@@ -11,25 +11,26 @@ exports.isEvent = isEvent;
|
|
|
11
11
|
exports.isMiddleware = isMiddleware;
|
|
12
12
|
const defs_1 = require("./defs");
|
|
13
13
|
const errors_1 = require("./errors");
|
|
14
|
-
function defineTask(
|
|
14
|
+
function defineTask(taskConfig) {
|
|
15
15
|
return {
|
|
16
16
|
[defs_1.symbols.task]: true,
|
|
17
|
-
id:
|
|
18
|
-
dependencies:
|
|
19
|
-
middleware:
|
|
20
|
-
run:
|
|
21
|
-
on:
|
|
17
|
+
id: taskConfig.id,
|
|
18
|
+
dependencies: taskConfig.dependencies || {},
|
|
19
|
+
middleware: taskConfig.middleware || [],
|
|
20
|
+
run: taskConfig.run,
|
|
21
|
+
on: taskConfig.on,
|
|
22
22
|
events: {
|
|
23
23
|
beforeRun: defineEvent({
|
|
24
|
-
id: `${
|
|
24
|
+
id: `${taskConfig.id}.beforeRun`,
|
|
25
25
|
}),
|
|
26
26
|
afterRun: defineEvent({
|
|
27
|
-
id: `${
|
|
27
|
+
id: `${taskConfig.id}.afterRun`,
|
|
28
28
|
}),
|
|
29
29
|
onError: defineEvent({
|
|
30
|
-
id: `${
|
|
30
|
+
id: `${taskConfig.id}.onError`,
|
|
31
31
|
}),
|
|
32
32
|
},
|
|
33
|
+
meta: taskConfig.meta || {},
|
|
33
34
|
// autorun,
|
|
34
35
|
};
|
|
35
36
|
}
|
|
@@ -61,6 +62,8 @@ function defineResource(constConfig) {
|
|
|
61
62
|
id: `${constConfig.id}.onError`,
|
|
62
63
|
}),
|
|
63
64
|
},
|
|
65
|
+
meta: constConfig.meta || {},
|
|
66
|
+
middleware: constConfig.middleware || [],
|
|
64
67
|
};
|
|
65
68
|
}
|
|
66
69
|
function defineEvent(config) {
|
package/dist/define.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":";;AAiBA,
|
|
1
|
+
{"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"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"}
|
package/dist/defs.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export declare const symbols: {
|
|
|
9
9
|
export interface IMeta {
|
|
10
10
|
title?: string;
|
|
11
11
|
description?: string;
|
|
12
|
-
tags
|
|
12
|
+
tags?: string[];
|
|
13
13
|
}
|
|
14
14
|
export interface ITaskMeta extends IMeta {
|
|
15
15
|
}
|
|
@@ -44,6 +44,10 @@ export type AfterRunEventPayload<TInput, TOutput> = {
|
|
|
44
44
|
};
|
|
45
45
|
export type OnErrorEventPayload = {
|
|
46
46
|
error: any;
|
|
47
|
+
/**
|
|
48
|
+
* This function can be called to suppress the error from being thrown.
|
|
49
|
+
*/
|
|
50
|
+
suppress(): void;
|
|
47
51
|
};
|
|
48
52
|
export type BeforeInitEventPayload<TConfig> = {
|
|
49
53
|
config: TConfig;
|
|
@@ -68,10 +72,10 @@ export interface ITask<TInput = any, TOutput extends Promise<any> = any, TDepend
|
|
|
68
72
|
onError: IEventDefinition<OnErrorEventPayload>;
|
|
69
73
|
};
|
|
70
74
|
}
|
|
71
|
-
export interface IResourceDefinintion<TConfig = void, TValue = unknown, TDependencies extends DependencyMapType = {}> {
|
|
75
|
+
export interface IResourceDefinintion<TConfig = void, TValue = unknown, TDependencies extends DependencyMapType = {}, THooks = any> {
|
|
72
76
|
id: string;
|
|
73
77
|
dependencies?: TDependencies | ((config: TConfig) => TDependencies);
|
|
74
|
-
hooks?: IHookDefinition<TDependencies>[] | ((config: TConfig) => IHookDefinition<TDependencies>[]);
|
|
78
|
+
hooks?: IHookDefinition<TDependencies, THooks>[] | ((config: TConfig) => IHookDefinition<TDependencies, THooks>[]);
|
|
75
79
|
register?: Array<RegisterableItems> | ((config: TConfig) => Array<RegisterableItems>);
|
|
76
80
|
init?: (config: TConfig, dependencies: DependencyValuesType<TDependencies>) => Promise<TValue>;
|
|
77
81
|
/**
|
|
@@ -85,6 +89,7 @@ export interface IResourceDefinintion<TConfig = void, TValue = unknown, TDepende
|
|
|
85
89
|
dispose?: (value: TValue, config: TConfig, dependencies: DependencyValuesType<TDependencies>) => Promise<TValue>;
|
|
86
90
|
meta?: IResourceMeta;
|
|
87
91
|
overrides?: Array<IResource | ITask | IMiddleware | IResourceWithConfig>;
|
|
92
|
+
middleware?: IMiddlewareDefinition[];
|
|
88
93
|
}
|
|
89
94
|
export interface IResource<TConfig = void, TValue = any, TDependencies extends DependencyMapType = any> extends IResourceDefinintion<TConfig, TValue, TDependencies> {
|
|
90
95
|
with(config: TConfig): IResourceWithConfig<TConfig, TValue, TDependencies>;
|
|
@@ -99,6 +104,7 @@ export interface IResource<TConfig = void, TValue = any, TDependencies extends D
|
|
|
99
104
|
};
|
|
100
105
|
hooks: IHookDefinition<TDependencies>[] | ((config: TConfig) => IHookDefinition<TDependencies>[]);
|
|
101
106
|
overrides: Array<IResource | ITask | IMiddleware | IResourceWithConfig>;
|
|
107
|
+
middleware: IMiddlewareDefinition[];
|
|
102
108
|
}
|
|
103
109
|
export interface IResourceWithConfig<TConfig = any, TValue = any, TDependencies extends DependencyMapType = any> {
|
|
104
110
|
resource: IResource<TConfig, TValue, TDependencies>;
|
|
@@ -107,6 +113,7 @@ export interface IResourceWithConfig<TConfig = any, TValue = any, TDependencies
|
|
|
107
113
|
export interface IEvent<TPayload = any> {
|
|
108
114
|
id: string;
|
|
109
115
|
data: TPayload;
|
|
116
|
+
timestamp: Date;
|
|
110
117
|
}
|
|
111
118
|
export type EventHandlerType<T = any> = (event: IEvent<T>) => any | Promise<any>;
|
|
112
119
|
export interface IEventDefinition<TPayload = void> {
|
|
@@ -129,10 +136,12 @@ export interface IMiddlewareDefinitionConfigured<C extends Record<string, any> =
|
|
|
129
136
|
}
|
|
130
137
|
export interface IMiddlewareExecutionInput {
|
|
131
138
|
taskDefinition?: ITask;
|
|
132
|
-
|
|
133
|
-
|
|
139
|
+
resourceDefinition?: IResource;
|
|
140
|
+
config?: any;
|
|
141
|
+
input?: any;
|
|
142
|
+
next: (taskInputOrResourceConfig?: any) => Promise<any>;
|
|
134
143
|
}
|
|
135
144
|
export interface IHookDefinition<D extends DependencyMapType = {}, T = any> {
|
|
136
|
-
event: IEventDefinition<T>;
|
|
145
|
+
event: "*" | IEventDefinition<T>;
|
|
137
146
|
run: (event: IEvent<T>, dependencies: DependencyValuesType<D>) => Promise<void> | void;
|
|
138
147
|
}
|
package/dist/globalEvents.d.ts
CHANGED
|
@@ -15,7 +15,8 @@ export declare const globalEvents: {
|
|
|
15
15
|
output: any;
|
|
16
16
|
}>;
|
|
17
17
|
onError: import("./defs").IEventDefinition<{
|
|
18
|
-
error:
|
|
18
|
+
error: any;
|
|
19
|
+
suppress: () => void;
|
|
19
20
|
task: ITask<any, any, any>;
|
|
20
21
|
}>;
|
|
21
22
|
};
|
|
@@ -31,6 +32,7 @@ export declare const globalEvents: {
|
|
|
31
32
|
}>;
|
|
32
33
|
onError: import("./defs").IEventDefinition<{
|
|
33
34
|
error: Error;
|
|
35
|
+
suppress: () => void;
|
|
34
36
|
resource: IResource<any, any, any>;
|
|
35
37
|
}>;
|
|
36
38
|
};
|
package/dist/globalEvents.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"globalEvents.js","sourceRoot":"","sources":["../src/globalEvents.ts"],"names":[],"mappings":";;;AAAA,qCAAuC;AAI1B,QAAA,YAAY,GAAG;IAC1B,UAAU,EAAE,IAAA,oBAAW,EAAC;QACtB,EAAE,EAAE,mBAAmB;KACxB,CAAC;IACF,SAAS,EAAE,IAAA,oBAAW,EAAC;QACrB,EAAE,EAAE,kBAAkB;KACvB,CAAC;IACF,GAAG,EAAE,IAAA,oBAAW,EAAO;QACrB,EAAE,EAAE,YAAY;KACjB,CAAC;IACF,KAAK,EAAE;QACL,SAAS,EAAE,IAAA,oBAAW,EAGnB;YACD,EAAE,EAAE,wBAAwB;SAC7B,CAAC;QACF,QAAQ,EAAE,IAAA,oBAAW,EAIlB;YACD,EAAE,EAAE,uBAAuB;SAC5B,CAAC;QACF,OAAO,EAAE,IAAA,oBAAW,
|
|
1
|
+
{"version":3,"file":"globalEvents.js","sourceRoot":"","sources":["../src/globalEvents.ts"],"names":[],"mappings":";;;AAAA,qCAAuC;AAI1B,QAAA,YAAY,GAAG;IAC1B,UAAU,EAAE,IAAA,oBAAW,EAAC;QACtB,EAAE,EAAE,mBAAmB;KACxB,CAAC;IACF,SAAS,EAAE,IAAA,oBAAW,EAAC;QACrB,EAAE,EAAE,kBAAkB;KACvB,CAAC;IACF,GAAG,EAAE,IAAA,oBAAW,EAAO;QACrB,EAAE,EAAE,YAAY;KACjB,CAAC;IACF,KAAK,EAAE;QACL,SAAS,EAAE,IAAA,oBAAW,EAGnB;YACD,EAAE,EAAE,wBAAwB;SAC7B,CAAC;QACF,QAAQ,EAAE,IAAA,oBAAW,EAIlB;YACD,EAAE,EAAE,uBAAuB;SAC5B,CAAC;QACF,OAAO,EAAE,IAAA,oBAAW,EAIjB;YACD,EAAE,EAAE,sBAAsB;SAC3B,CAAC;KACH;IACD,SAAS,EAAE;QACT,UAAU,EAAE,IAAA,oBAAW,EAGpB;YACD,EAAE,EAAE,6BAA6B;SAClC,CAAC;QACF,SAAS,EAAE,IAAA,oBAAW,EAInB;YACD,EAAE,EAAE,4BAA4B;SACjC,CAAC;QACF,OAAO,EAAE,IAAA,oBAAW,EAIjB;YACD,EAAE,EAAE,0BAA0B;SAC/B,CAAC;KACH;CACF,CAAC;AAEW,QAAA,iBAAiB,GAAG;IAC/B,oBAAY,CAAC,UAAU;IACvB,oBAAY,CAAC,SAAS;IACtB,oBAAY,CAAC,KAAK,CAAC,SAAS;IAC5B,oBAAY,CAAC,KAAK,CAAC,QAAQ;IAC3B,oBAAY,CAAC,KAAK,CAAC,OAAO;IAC1B,oBAAY,CAAC,SAAS,CAAC,UAAU;IACjC,oBAAY,CAAC,SAAS,CAAC,SAAS;IAChC,oBAAY,CAAC,SAAS,CAAC,OAAO;CAC/B,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -16,7 +16,8 @@ declare const globals: {
|
|
|
16
16
|
output: any;
|
|
17
17
|
}>;
|
|
18
18
|
onError: import("./defs").IEventDefinition<{
|
|
19
|
-
error:
|
|
19
|
+
error: any;
|
|
20
|
+
suppress: () => void;
|
|
20
21
|
task: import("./defs").ITask<any, any, any>;
|
|
21
22
|
}>;
|
|
22
23
|
};
|
|
@@ -32,6 +33,7 @@ declare const globals: {
|
|
|
32
33
|
}>;
|
|
33
34
|
onError: import("./defs").IEventDefinition<{
|
|
34
35
|
error: Error;
|
|
36
|
+
suppress: () => void;
|
|
35
37
|
resource: import("./defs").IResource<any, any, any>;
|
|
36
38
|
}>;
|
|
37
39
|
};
|