@genesislcap/foundation-state-machine 14.70.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 +26 -0
- package/dist/dts/core/actions.d.ts +61 -0
- package/dist/dts/core/actions.d.ts.map +1 -0
- package/dist/dts/core/binding.d.ts +37 -0
- package/dist/dts/core/binding.d.ts.map +1 -0
- package/dist/dts/core/errors.d.ts +41 -0
- package/dist/dts/core/errors.d.ts.map +1 -0
- package/dist/dts/core/guards.d.ts +67 -0
- package/dist/dts/core/guards.d.ts.map +1 -0
- package/dist/dts/core/index.d.ts +8 -0
- package/dist/dts/core/index.d.ts.map +1 -0
- package/dist/dts/core/inputs.d.ts +19 -0
- package/dist/dts/core/inputs.d.ts.map +1 -0
- package/dist/dts/core/machine.d.ts +338 -0
- package/dist/dts/core/machine.d.ts.map +1 -0
- package/dist/dts/core/state.d.ts +11 -0
- package/dist/dts/core/state.d.ts.map +1 -0
- package/dist/dts/index.d.ts +4 -0
- package/dist/dts/index.d.ts.map +1 -0
- package/dist/dts/index.federated.d.ts +1 -0
- package/dist/dts/index.federated.d.ts.map +1 -0
- package/dist/dts/machines/fetch/errors.d.ts +39 -0
- package/dist/dts/machines/fetch/errors.d.ts.map +1 -0
- package/dist/dts/machines/fetch/index.d.ts +5 -0
- package/dist/dts/machines/fetch/index.d.ts.map +1 -0
- package/dist/dts/machines/fetch/machine.d.ts +57 -0
- package/dist/dts/machines/fetch/machine.d.ts.map +1 -0
- package/dist/dts/machines/fetch/policies.d.ts +25 -0
- package/dist/dts/machines/fetch/policies.d.ts.map +1 -0
- package/dist/dts/machines/fetch/types.d.ts +118 -0
- package/dist/dts/machines/fetch/types.d.ts.map +1 -0
- package/dist/dts/machines/index.d.ts +2 -0
- package/dist/dts/machines/index.d.ts.map +1 -0
- package/dist/dts/tsdoc-metadata.json +11 -0
- package/dist/dts/utils/index.d.ts +2 -0
- package/dist/dts/utils/index.d.ts.map +1 -0
- package/dist/dts/utils/logger.d.ts +5 -0
- package/dist/dts/utils/logger.d.ts.map +1 -0
- package/dist/esm/core/actions.js +46 -0
- package/dist/esm/core/binding.js +16 -0
- package/dist/esm/core/errors.js +70 -0
- package/dist/esm/core/guards.js +73 -0
- package/dist/esm/core/index.js +7 -0
- package/dist/esm/core/inputs.js +10 -0
- package/dist/esm/core/machine.js +180 -0
- package/dist/esm/core/state.js +26 -0
- package/dist/esm/index.federated.js +1 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/machines/fetch/errors.js +46 -0
- package/dist/esm/machines/fetch/index.js +4 -0
- package/dist/esm/machines/fetch/machine.js +216 -0
- package/dist/esm/machines/fetch/policies.js +48 -0
- package/dist/esm/machines/fetch/types.js +48 -0
- package/dist/esm/machines/index.js +1 -0
- package/dist/esm/utils/index.js +1 -0
- package/dist/esm/utils/logger.js +5 -0
- package/dist/foundation-state-machine.api.json +5594 -0
- package/dist/foundation-state-machine.d.ts +884 -0
- package/docs/.gitattributes +2 -0
- package/docs/api/foundation-state-machine.abstractmachine.actor.md +18 -0
- package/docs/api/foundation-state-machine.abstractmachine.binding.md +27 -0
- package/docs/api/foundation-state-machine.abstractmachine.complete.md +13 -0
- package/docs/api/foundation-state-machine.abstractmachine.context.md +13 -0
- package/docs/api/foundation-state-machine.abstractmachine.error.md +13 -0
- package/docs/api/foundation-state-machine.abstractmachine.errors.md +13 -0
- package/docs/api/foundation-state-machine.abstractmachine.getfromcontainer.md +23 -0
- package/docs/api/foundation-state-machine.abstractmachine.hastag.md +28 -0
- package/docs/api/foundation-state-machine.abstractmachine.machine.md +18 -0
- package/docs/api/foundation-state-machine.abstractmachine.matches.md +28 -0
- package/docs/api/foundation-state-machine.abstractmachine.md +44 -0
- package/docs/api/foundation-state-machine.abstractmachine.onsendevent.md +13 -0
- package/docs/api/foundation-state-machine.abstractmachine.output.md +13 -0
- package/docs/api/foundation-state-machine.abstractmachine.provide.md +24 -0
- package/docs/api/foundation-state-machine.abstractmachine.send.md +24 -0
- package/docs/api/foundation-state-machine.abstractmachine.start.md +28 -0
- package/docs/api/foundation-state-machine.abstractmachine.startingcontext.md +13 -0
- package/docs/api/foundation-state-machine.abstractmachine.startingstate.md +13 -0
- package/docs/api/foundation-state-machine.abstractmachine.state.md +13 -0
- package/docs/api/foundation-state-machine.abstractmachine.stop.md +17 -0
- package/docs/api/foundation-state-machine.apierror._constructor_.md +22 -0
- package/docs/api/foundation-state-machine.apierror.data.md +11 -0
- package/docs/api/foundation-state-machine.apierror.md +32 -0
- package/docs/api/foundation-state-machine.apierror.status.md +11 -0
- package/docs/api/foundation-state-machine.assertevent.md +27 -0
- package/docs/api/foundation-state-machine.createerrorstatenode.md +13 -0
- package/docs/api/foundation-state-machine.createfetchpolicy.md +18 -0
- package/docs/api/foundation-state-machine.createresponsedataextractorwithdeserializer.md +13 -0
- package/docs/api/foundation-state-machine.defaultfetchconfig.md +13 -0
- package/docs/api/foundation-state-machine.defaultfetchmachine._constructor_.md +20 -0
- package/docs/api/foundation-state-machine.defaultfetchmachine.getfromcontainer.md +23 -0
- package/docs/api/foundation-state-machine.defaultfetchmachine.md +30 -0
- package/docs/api/foundation-state-machine.defaultfetchmachine.start.md +28 -0
- package/docs/api/foundation-state-machine.defaultfetchmachinecontext.md +13 -0
- package/docs/api/foundation-state-machine.defaultfetchpolicy.md +13 -0
- package/docs/api/foundation-state-machine.defaultresponsedataextractor.md +18 -0
- package/docs/api/foundation-state-machine.defaultresponseokchecker.md +13 -0
- package/docs/api/foundation-state-machine.defaultretrystatscodes.md +13 -0
- package/docs/api/foundation-state-machine.errorworkflowinput.md +15 -0
- package/docs/api/foundation-state-machine.escalateerror.md +42 -0
- package/docs/api/foundation-state-machine.fetchconfig.fetcher.md +13 -0
- package/docs/api/foundation-state-machine.fetchconfig.fetchpolicy.md +13 -0
- package/docs/api/foundation-state-machine.fetchconfig.md +23 -0
- package/docs/api/foundation-state-machine.fetchconfig.responsedataextractor.md +13 -0
- package/docs/api/foundation-state-machine.fetchconfig.responseokchecker.md +13 -0
- package/docs/api/foundation-state-machine.fetchmachine.md +15 -0
- package/docs/api/foundation-state-machine.fetchmachinecontext.controller.md +13 -0
- package/docs/api/foundation-state-machine.fetchmachinecontext.data.md +13 -0
- package/docs/api/foundation-state-machine.fetchmachinecontext.md +28 -0
- package/docs/api/foundation-state-machine.fetchmachinecontext.params.md +17 -0
- package/docs/api/foundation-state-machine.fetchmachinecontext.response.md +13 -0
- package/docs/api/foundation-state-machine.fetchmachineevent.md +24 -0
- package/docs/api/foundation-state-machine.fetchmachineeventmap.md +21 -0
- package/docs/api/foundation-state-machine.fetchmachineoutput.md +21 -0
- package/docs/api/foundation-state-machine.genesisservererror._constructor_.md +22 -0
- package/docs/api/foundation-state-machine.genesisservererror.getdetailserror.md +15 -0
- package/docs/api/foundation-state-machine.genesisservererror.getmessageerror.md +15 -0
- package/docs/api/foundation-state-machine.genesisservererror.haserrorcode.md +22 -0
- package/docs/api/foundation-state-machine.genesisservererror.isignorableerror.md +17 -0
- package/docs/api/foundation-state-machine.genesisservererror.md +41 -0
- package/docs/api/foundation-state-machine.genesisservererror.received.md +11 -0
- package/docs/api/foundation-state-machine.genesisservererror.sent.md +11 -0
- package/docs/api/foundation-state-machine.isaborterror.md +15 -0
- package/docs/api/foundation-state-machine.isactorerrorevent.md +13 -0
- package/docs/api/foundation-state-machine.isapierror.md +11 -0
- package/docs/api/foundation-state-machine.isdoneinvokeevent.md +13 -0
- package/docs/api/foundation-state-machine.iserrorcustomevent.md +18 -0
- package/docs/api/foundation-state-machine.iserrorevent.md +13 -0
- package/docs/api/foundation-state-machine.iserrortype.md +13 -0
- package/docs/api/foundation-state-machine.isgenesisservererror.md +11 -0
- package/docs/api/foundation-state-machine.ismappingerror.md +11 -0
- package/docs/api/foundation-state-machine.isnetworkerror.md +11 -0
- package/docs/api/foundation-state-machine.isnotfounderror.md +13 -0
- package/docs/api/foundation-state-machine.issyntaxerror.md +15 -0
- package/docs/api/foundation-state-machine.istypeerror.md +15 -0
- package/docs/api/foundation-state-machine.logger.md +12 -0
- package/docs/api/foundation-state-machine.machine.actor.md +18 -0
- package/docs/api/foundation-state-machine.machine.binding.md +47 -0
- package/docs/api/foundation-state-machine.machine.complete.md +13 -0
- package/docs/api/foundation-state-machine.machine.context.md +13 -0
- package/docs/api/foundation-state-machine.machine.error.md +13 -0
- package/docs/api/foundation-state-machine.machine.errors.md +13 -0
- package/docs/api/foundation-state-machine.machine.getfromcontainer.md +35 -0
- package/docs/api/foundation-state-machine.machine.hastag.md +35 -0
- package/docs/api/foundation-state-machine.machine.machine.md +18 -0
- package/docs/api/foundation-state-machine.machine.matches.md +35 -0
- package/docs/api/foundation-state-machine.machine.md +101 -0
- package/docs/api/foundation-state-machine.machine.onsendevent.md +24 -0
- package/docs/api/foundation-state-machine.machine.output.md +13 -0
- package/docs/api/foundation-state-machine.machine.provide.md +44 -0
- package/docs/api/foundation-state-machine.machine.send.md +24 -0
- package/docs/api/foundation-state-machine.machine.start.md +28 -0
- package/docs/api/foundation-state-machine.machine.startingcontext.md +13 -0
- package/docs/api/foundation-state-machine.machine.startingstate.md +13 -0
- package/docs/api/foundation-state-machine.machine.state.md +13 -0
- package/docs/api/foundation-state-machine.machine.stop.md +17 -0
- package/docs/api/foundation-state-machine.machinecontext.error.md +13 -0
- package/docs/api/foundation-state-machine.machinecontext.errors.md +13 -0
- package/docs/api/foundation-state-machine.machinecontext.md +36 -0
- package/docs/api/foundation-state-machine.mappingerror._constructor_.md +21 -0
- package/docs/api/foundation-state-machine.mappingerror.dto.md +11 -0
- package/docs/api/foundation-state-machine.mappingerror.md +27 -0
- package/docs/api/foundation-state-machine.md +81 -0
- package/docs/api/foundation-state-machine.networkerror._constructor_.md +21 -0
- package/docs/api/foundation-state-machine.networkerror.md +31 -0
- package/docs/api/foundation-state-machine.networkerror.url.md +11 -0
- package/docs/api/foundation-state-machine.responsedataextractor.md +18 -0
- package/docs/api/foundation-state-machine.responseokchecker.md +13 -0
- package/docs/api/foundation-state-machine.seterror.md +25 -0
- package/docs/api/foundation-state-machine.seterrorbykey.md +25 -0
- package/docs/api/index.md +12 -0
- package/docs/api-report.md +393 -0
- package/license.txt +46 -0
- package/package.json +60 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { createErrorMap } from '@genesislcap/foundation-utils';
|
|
3
|
+
import { defaultExecutionContext, observable, Observable, } from '@microsoft/fast-element';
|
|
4
|
+
import { Container } from '@microsoft/fast-foundation';
|
|
5
|
+
import { interpret, InterpreterStatus } from 'xstate';
|
|
6
|
+
import { logger } from '../utils';
|
|
7
|
+
import { getBindArgs } from './binding';
|
|
8
|
+
/**
|
|
9
|
+
* AbstractMachine base class.
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export class AbstractMachine {
|
|
13
|
+
constructor() {
|
|
14
|
+
/** {@inheritDoc Machine.onSendEvent} */
|
|
15
|
+
this.onSendEvent = (event) => {
|
|
16
|
+
this.send(event.detail);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** {@inheritDoc Machine.start} */
|
|
20
|
+
start(options) {
|
|
21
|
+
this.interpret(options);
|
|
22
|
+
this.subscribe();
|
|
23
|
+
this.startActor();
|
|
24
|
+
}
|
|
25
|
+
/** {@inheritDoc Machine.stop} */
|
|
26
|
+
stop() {
|
|
27
|
+
this.stopActor();
|
|
28
|
+
this.unsubscribe();
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* @internal
|
|
32
|
+
*/
|
|
33
|
+
interpret(options = {}) {
|
|
34
|
+
if (!this.machine) {
|
|
35
|
+
throw new Error('Create this.machine before calling interpret()');
|
|
36
|
+
}
|
|
37
|
+
const contextWithErrors = {
|
|
38
|
+
errors: createErrorMap(logger.error),
|
|
39
|
+
error: undefined,
|
|
40
|
+
};
|
|
41
|
+
this.startingState = options === null || options === void 0 ? void 0 : options.state;
|
|
42
|
+
this.startingContext = (options.input
|
|
43
|
+
? Object.assign(Object.assign({}, contextWithErrors), options.input) : contextWithErrors);
|
|
44
|
+
this.actor = interpret(this.machine, Object.assign(Object.assign({}, options), { input: this.startingContext, state: this.startingState }));
|
|
45
|
+
const { value: currentValue, context: currentContext } = this.actor.getSnapshot();
|
|
46
|
+
this.state = currentValue;
|
|
47
|
+
this.context = currentContext;
|
|
48
|
+
this.complete = false;
|
|
49
|
+
this.output = undefined;
|
|
50
|
+
this.errors = currentContext.errors;
|
|
51
|
+
this.error = undefined;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* @internal
|
|
55
|
+
*/
|
|
56
|
+
subscribe() {
|
|
57
|
+
if (!this.actor) {
|
|
58
|
+
throw new Error('Call interpret() before calling subscribe()');
|
|
59
|
+
}
|
|
60
|
+
this.subscription = this.actor.subscribe({
|
|
61
|
+
next: (snapshot) => {
|
|
62
|
+
var _a;
|
|
63
|
+
this.state = snapshot.value;
|
|
64
|
+
this.context = snapshot.context;
|
|
65
|
+
this.errors = this.context.errors;
|
|
66
|
+
this.error = (_a = this.context.error) !== null && _a !== void 0 ? _a : this.errors.lastError;
|
|
67
|
+
logger.debug(`${this.machine.id} changed, state:`, this.state, `context:`, this.context);
|
|
68
|
+
},
|
|
69
|
+
error: (err) => {
|
|
70
|
+
this.error = err;
|
|
71
|
+
logger.error(`${this.machine.id} error:`, err);
|
|
72
|
+
},
|
|
73
|
+
complete: () => {
|
|
74
|
+
this.complete = true;
|
|
75
|
+
this.output = this.actor.getSnapshot().output;
|
|
76
|
+
logger.debug(`${this.machine.id} complete:`, this.output);
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* @internal
|
|
82
|
+
*/
|
|
83
|
+
unsubscribe() {
|
|
84
|
+
this.subscription && this.subscription.unsubscribe();
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* @internal
|
|
88
|
+
*/
|
|
89
|
+
startActor() {
|
|
90
|
+
var _a;
|
|
91
|
+
if (((_a = this.actor) === null || _a === void 0 ? void 0 : _a.status) !== InterpreterStatus.Running) {
|
|
92
|
+
this.actor.start();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* @internal
|
|
97
|
+
*/
|
|
98
|
+
stopActor() {
|
|
99
|
+
var _a;
|
|
100
|
+
if (((_a = this.actor) === null || _a === void 0 ? void 0 : _a.status) === InterpreterStatus.Running) {
|
|
101
|
+
this.actor.stop();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** {@inheritDoc Machine.provide} */
|
|
105
|
+
provide(implementations) {
|
|
106
|
+
if (!this.machine) {
|
|
107
|
+
throw new Error('Create this.machine before calling provide()');
|
|
108
|
+
}
|
|
109
|
+
this.machine = this.machine.provide(implementations);
|
|
110
|
+
if (this.actor) {
|
|
111
|
+
this.stop();
|
|
112
|
+
logger.debug('You will need to recall start() after calling provide() on a running machine.');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** {@inheritDoc Machine.matches} */
|
|
116
|
+
matches(value) {
|
|
117
|
+
if (!this.actor) {
|
|
118
|
+
throw new Error('Call interpret() before calling matches()');
|
|
119
|
+
}
|
|
120
|
+
return this.actor.getSnapshot().matches(value);
|
|
121
|
+
}
|
|
122
|
+
/** {@inheritDoc Machine.hasTag} */
|
|
123
|
+
hasTag(tag) {
|
|
124
|
+
if (!this.actor) {
|
|
125
|
+
throw new Error('Call interpret() before calling hasTag()');
|
|
126
|
+
}
|
|
127
|
+
return this.actor.getSnapshot().hasTag(tag);
|
|
128
|
+
}
|
|
129
|
+
/** {@inheritDoc Machine.send} */
|
|
130
|
+
send(event) {
|
|
131
|
+
this.actor.send(event);
|
|
132
|
+
}
|
|
133
|
+
/** {@inheritDoc Machine.binding} */
|
|
134
|
+
binding(token, subscriberChangeCallback, isVolatileBinding = false, context = defaultExecutionContext) {
|
|
135
|
+
const valueGetter = getBindArgs(token);
|
|
136
|
+
const initialSubscriber = {
|
|
137
|
+
handleChange: (binding, args) => {
|
|
138
|
+
if (subscriberChangeCallback) {
|
|
139
|
+
subscriberChangeCallback(binding(this, context), args);
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
const bindingObserver = Observable.binding(valueGetter, initialSubscriber, isVolatileBinding);
|
|
144
|
+
bindingObserver.observe(this, context);
|
|
145
|
+
return bindingObserver;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
__decorate([
|
|
149
|
+
Container
|
|
150
|
+
], AbstractMachine.prototype, "container", void 0);
|
|
151
|
+
__decorate([
|
|
152
|
+
observable
|
|
153
|
+
], AbstractMachine.prototype, "machine", void 0);
|
|
154
|
+
__decorate([
|
|
155
|
+
observable
|
|
156
|
+
], AbstractMachine.prototype, "actor", void 0);
|
|
157
|
+
__decorate([
|
|
158
|
+
observable
|
|
159
|
+
], AbstractMachine.prototype, "state", void 0);
|
|
160
|
+
__decorate([
|
|
161
|
+
observable
|
|
162
|
+
], AbstractMachine.prototype, "startingState", void 0);
|
|
163
|
+
__decorate([
|
|
164
|
+
observable
|
|
165
|
+
], AbstractMachine.prototype, "context", void 0);
|
|
166
|
+
__decorate([
|
|
167
|
+
observable
|
|
168
|
+
], AbstractMachine.prototype, "startingContext", void 0);
|
|
169
|
+
__decorate([
|
|
170
|
+
observable
|
|
171
|
+
], AbstractMachine.prototype, "complete", void 0);
|
|
172
|
+
__decorate([
|
|
173
|
+
observable
|
|
174
|
+
], AbstractMachine.prototype, "output", void 0);
|
|
175
|
+
__decorate([
|
|
176
|
+
observable
|
|
177
|
+
], AbstractMachine.prototype, "errors", void 0);
|
|
178
|
+
__decorate([
|
|
179
|
+
observable
|
|
180
|
+
], AbstractMachine.prototype, "error", void 0);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { setErrorByKey } from './actions';
|
|
2
|
+
/**
|
|
3
|
+
* Creates an error state node.
|
|
4
|
+
*
|
|
5
|
+
* @param key - An error state key.
|
|
6
|
+
* @param metaContent - An optional meta content value.
|
|
7
|
+
* @param final - An optional flag which adds final with output functionality.
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
export const createErrorStateNode = (key, metaContent, final = true) => {
|
|
11
|
+
return {
|
|
12
|
+
tags: ['error', key],
|
|
13
|
+
meta: {
|
|
14
|
+
content: metaContent ? metaContent : `${key} error.`,
|
|
15
|
+
},
|
|
16
|
+
entry: [setErrorByKey(`${key}-error`)],
|
|
17
|
+
/**
|
|
18
|
+
* Provide final output to parent machines.
|
|
19
|
+
*/
|
|
20
|
+
type: final ? 'final' : 'atomic',
|
|
21
|
+
output: ({ context }) => ({
|
|
22
|
+
error: context.error,
|
|
23
|
+
data: context.error,
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import('./index');
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { StatusCodes } from 'http-status-codes';
|
|
2
|
+
/**
|
|
3
|
+
* NetworkError.
|
|
4
|
+
* @remarks
|
|
5
|
+
* Failed to reach server.
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
export class NetworkError extends Error {
|
|
9
|
+
constructor(url, message = 'Network Error Occurred') {
|
|
10
|
+
super(message);
|
|
11
|
+
this.url = url;
|
|
12
|
+
this.name = 'NetworkError';
|
|
13
|
+
Object.setPrototypeOf(this, NetworkError.prototype);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* APIError.
|
|
18
|
+
* @remarks
|
|
19
|
+
* Bad server `Response`.
|
|
20
|
+
* @public
|
|
21
|
+
*/
|
|
22
|
+
export class APIError extends Error {
|
|
23
|
+
constructor(status, data, message = 'API Error Occurred') {
|
|
24
|
+
super(message);
|
|
25
|
+
this.status = status;
|
|
26
|
+
this.data = data;
|
|
27
|
+
this.name = 'APIError';
|
|
28
|
+
Object.setPrototypeOf(this, APIError.prototype);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* @param error - An error.
|
|
33
|
+
* @public
|
|
34
|
+
*/
|
|
35
|
+
export const isAPIError = (error) => error instanceof APIError;
|
|
36
|
+
/**
|
|
37
|
+
* @param error - An error.
|
|
38
|
+
* @public
|
|
39
|
+
*/
|
|
40
|
+
export const isNetworkError = (error) => error instanceof NetworkError ||
|
|
41
|
+
(error instanceof TypeError && error.message === 'Failed to fetch');
|
|
42
|
+
/**
|
|
43
|
+
* @param error - An error.
|
|
44
|
+
* @public
|
|
45
|
+
*/
|
|
46
|
+
export const isNotFoundError = (error) => isAPIError(error) && error.status === StatusCodes.NOT_FOUND;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { __awaiter, __decorate, __param } from "tslib";
|
|
2
|
+
import { DI, optional } from '@microsoft/fast-foundation';
|
|
3
|
+
import { assign, createMachine, fromPromise } from 'xstate';
|
|
4
|
+
import { AbstractMachine, createErrorStateNode, isAbortError, isDoneInvokeEvent, isErrorEvent, isSyntaxError, isTypeError, setError, } from '../../core';
|
|
5
|
+
import { logger } from '../../utils';
|
|
6
|
+
import { APIError, isAPIError, isNetworkError, isNotFoundError, NetworkError } from './errors';
|
|
7
|
+
import { defaultFetchConfig, defaultFetchMachineContext } from './types';
|
|
8
|
+
/**
|
|
9
|
+
* FetchConfig DI key.
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export const FetchConfig = DI.createInterface();
|
|
13
|
+
/**
|
|
14
|
+
* Default FetchMachine.
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
let DefaultFetchMachine = class DefaultFetchMachine extends AbstractMachine {
|
|
18
|
+
constructor(config = defaultFetchConfig) {
|
|
19
|
+
super();
|
|
20
|
+
this.config = config;
|
|
21
|
+
this.machine = createMachine({
|
|
22
|
+
id: 'fetchMachine',
|
|
23
|
+
context: ({ input }) => (Object.assign({}, input)),
|
|
24
|
+
initial: 'idle',
|
|
25
|
+
states: {
|
|
26
|
+
idle: {
|
|
27
|
+
tags: ['idle'],
|
|
28
|
+
meta: {
|
|
29
|
+
content: 'Idle.',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
pending: {
|
|
33
|
+
tags: ['pending'],
|
|
34
|
+
meta: {
|
|
35
|
+
content: 'Fetching, please wait.',
|
|
36
|
+
},
|
|
37
|
+
invoke: {
|
|
38
|
+
src: 'fetch',
|
|
39
|
+
input: ({ context }) => (Object.assign(Object.assign({}, context.params), { controller: context.controller })),
|
|
40
|
+
onDone: 'success',
|
|
41
|
+
onError: [
|
|
42
|
+
{
|
|
43
|
+
guard: 'isNetworkError',
|
|
44
|
+
target: 'error.network',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
guard: 'isNotFoundError',
|
|
48
|
+
target: 'error.notFound',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
guard: 'isAPIError',
|
|
52
|
+
target: 'error.api',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
guard: 'isAbortError',
|
|
56
|
+
target: 'error.abort',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
guard: 'isSyntaxError',
|
|
60
|
+
target: 'error.syntax',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
target: 'error.unknown',
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
success: {
|
|
69
|
+
tags: ['success'],
|
|
70
|
+
meta: {
|
|
71
|
+
content: 'Success.',
|
|
72
|
+
},
|
|
73
|
+
entry: ['setResponse', 'setData', 'successCallback'],
|
|
74
|
+
/**
|
|
75
|
+
* Provide final output to parent machines.
|
|
76
|
+
*/
|
|
77
|
+
type: 'final',
|
|
78
|
+
output: ({ context }) => ({
|
|
79
|
+
response: context.response,
|
|
80
|
+
data: context.data,
|
|
81
|
+
}),
|
|
82
|
+
},
|
|
83
|
+
error: {
|
|
84
|
+
tags: ['error'],
|
|
85
|
+
initial: 'unknown',
|
|
86
|
+
entry: ['setError', 'errorCallback'],
|
|
87
|
+
states: {
|
|
88
|
+
unknown: createErrorStateNode('unknown', 'Unknown error.'),
|
|
89
|
+
network: createErrorStateNode('network', 'Network error.'),
|
|
90
|
+
notFound: createErrorStateNode('notFound', 'Not found error.'),
|
|
91
|
+
api: createErrorStateNode('api', 'API error.'),
|
|
92
|
+
abort: createErrorStateNode('abort', 'Abort error.'),
|
|
93
|
+
syntax: createErrorStateNode('syntax', 'Syntax error.'),
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
on: {
|
|
98
|
+
fetch: {
|
|
99
|
+
actions: ['abortInFlight', 'resetContext', 'setParams'],
|
|
100
|
+
target: '#fetchMachine.pending',
|
|
101
|
+
},
|
|
102
|
+
cancel: {
|
|
103
|
+
actions: ['abortInFlight', 'resetContext'],
|
|
104
|
+
target: '#fetchMachine.idle',
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
}, {
|
|
108
|
+
actors: {
|
|
109
|
+
fetch: fromPromise(({ input }) => __awaiter(this, void 0, void 0, function* () {
|
|
110
|
+
var _a, _b, _c, _d, _e;
|
|
111
|
+
const { controller, fetchConfig, requestInit, url } = input;
|
|
112
|
+
const fetcher = (_b = (_a = fetchConfig === null || fetchConfig === void 0 ? void 0 : fetchConfig.fetcher) !== null && _a !== void 0 ? _a : this.config.fetcher) !== null && _b !== void 0 ? _b : fetch;
|
|
113
|
+
const fetchPolicy = (_c = fetchConfig === null || fetchConfig === void 0 ? void 0 : fetchConfig.fetchPolicy) !== null && _c !== void 0 ? _c : this.config.fetchPolicy;
|
|
114
|
+
const init = requestInit !== null && requestInit !== void 0 ? requestInit : { method: 'GET' };
|
|
115
|
+
const okChecker = (_d = fetchConfig === null || fetchConfig === void 0 ? void 0 : fetchConfig.responseOkChecker) !== null && _d !== void 0 ? _d : this.config.responseOkChecker;
|
|
116
|
+
const dataExtractor = (_e = fetchConfig === null || fetchConfig === void 0 ? void 0 : fetchConfig.responseDataExtractor) !== null && _e !== void 0 ? _e : this.config.responseDataExtractor;
|
|
117
|
+
return fetchPolicy.execute(() => __awaiter(this, void 0, void 0, function* () {
|
|
118
|
+
try {
|
|
119
|
+
const response = yield fetcher(url, Object.assign(Object.assign({}, init), { signal: controller.signal }));
|
|
120
|
+
const data = yield dataExtractor(response);
|
|
121
|
+
if (!okChecker(response)) {
|
|
122
|
+
throw new APIError(response.status, data);
|
|
123
|
+
}
|
|
124
|
+
return [data, response];
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
if (isNetworkError(error)) {
|
|
128
|
+
throw new NetworkError(url);
|
|
129
|
+
}
|
|
130
|
+
throw error; // < API, Syntax / JS Error
|
|
131
|
+
}
|
|
132
|
+
}));
|
|
133
|
+
})),
|
|
134
|
+
},
|
|
135
|
+
actions: {
|
|
136
|
+
setParams: assign(({ event }) => {
|
|
137
|
+
if (event.type === 'fetch') {
|
|
138
|
+
return {
|
|
139
|
+
params: event.params,
|
|
140
|
+
controller: new AbortController(),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}),
|
|
144
|
+
setResponse: assign(({ event }) => {
|
|
145
|
+
if (isDoneInvokeEvent(event)) {
|
|
146
|
+
const [_, response] = event.output;
|
|
147
|
+
return {
|
|
148
|
+
response,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}),
|
|
152
|
+
setData: assign(({ event }) => {
|
|
153
|
+
if (isDoneInvokeEvent(event)) {
|
|
154
|
+
const [data] = event.output;
|
|
155
|
+
return {
|
|
156
|
+
data,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}),
|
|
160
|
+
setError,
|
|
161
|
+
abortInFlight: ({ context }) => {
|
|
162
|
+
if (context.controller) {
|
|
163
|
+
context.controller.abort();
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
resetContext: assign(() => this.startingContext),
|
|
167
|
+
/**
|
|
168
|
+
* successCallback.
|
|
169
|
+
* @remarks
|
|
170
|
+
* Can be overridden by the caller. Alternatively callers can observe context or successful output.
|
|
171
|
+
* @public
|
|
172
|
+
*/
|
|
173
|
+
successCallback: () => {
|
|
174
|
+
logger.debug('FetchMachine.successCallback invoked.');
|
|
175
|
+
},
|
|
176
|
+
/**
|
|
177
|
+
* errorCallback.
|
|
178
|
+
* @remarks
|
|
179
|
+
* Can be overridden by the caller. Alternatively callers can observe context or successful output.
|
|
180
|
+
* @public
|
|
181
|
+
*/
|
|
182
|
+
errorCallback: () => {
|
|
183
|
+
logger.debug('FetchMachine.errorCallback invoked.');
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
guards: {
|
|
187
|
+
isNetworkError: ({ event }) => (isErrorEvent(event) ? isNetworkError(event.data) : false),
|
|
188
|
+
isAPIError: ({ event }) => (isErrorEvent(event) ? isAPIError(event.data) : false),
|
|
189
|
+
isNotFoundError: ({ event }) => isErrorEvent(event) ? isNotFoundError(event.data) : false,
|
|
190
|
+
isAbortError,
|
|
191
|
+
isSyntaxError,
|
|
192
|
+
isTypeError,
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
this.start();
|
|
196
|
+
}
|
|
197
|
+
/** {@inheritDoc Machine.start} */
|
|
198
|
+
start(options = {
|
|
199
|
+
input: defaultFetchMachineContext,
|
|
200
|
+
}) {
|
|
201
|
+
super.start(options);
|
|
202
|
+
}
|
|
203
|
+
/** {@inheritDoc Machine.getFromContainer} */
|
|
204
|
+
getFromContainer() {
|
|
205
|
+
return this.container.get(FetchMachine);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
DefaultFetchMachine = __decorate([
|
|
209
|
+
__param(0, optional(FetchConfig))
|
|
210
|
+
], DefaultFetchMachine);
|
|
211
|
+
export { DefaultFetchMachine };
|
|
212
|
+
/**
|
|
213
|
+
* FetchMachine DI key.
|
|
214
|
+
* @internal
|
|
215
|
+
*/
|
|
216
|
+
export const FetchMachine = DI.createInterface((x) => x.transient(DefaultFetchMachine));
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ExponentialBackoff, TimeoutStrategy, handleType, retry, timeout, wrap } from 'cockatiel';
|
|
2
|
+
import { StatusCodes } from 'http-status-codes';
|
|
3
|
+
import { logger } from '../../utils';
|
|
4
|
+
import { APIError, NetworkError } from './errors';
|
|
5
|
+
/**
|
|
6
|
+
* Default retry stats codes.
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
export const defaultRetryStatsCodes = [
|
|
10
|
+
StatusCodes.REQUEST_TIMEOUT,
|
|
11
|
+
StatusCodes.TOO_MANY_REQUESTS,
|
|
12
|
+
StatusCodes.INTERNAL_SERVER_ERROR,
|
|
13
|
+
StatusCodes.BAD_GATEWAY,
|
|
14
|
+
StatusCodes.SERVICE_UNAVAILABLE,
|
|
15
|
+
StatusCodes.GATEWAY_TIMEOUT,
|
|
16
|
+
];
|
|
17
|
+
/**
|
|
18
|
+
* A fetch policy factory.
|
|
19
|
+
* @remarks
|
|
20
|
+
* Will retry network errors and responses with certain status codes up to the specified max attempts.
|
|
21
|
+
* @param maxAttempts - Max attempts. Defaults to `2`.
|
|
22
|
+
* @param retryCodes - Status code array to retry. Defaults to {@link defaultRetryStatsCodes}.
|
|
23
|
+
* @param backoff - Backoff algo. Defaults to {@link cockatiel#ExponentialBackoff}.
|
|
24
|
+
* @param timeoutDuration - Timeout duration. Defaults to 3 seconds.
|
|
25
|
+
* @param logEvents - Log events to the console. Defaults to `true`.
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
28
|
+
export const createFetchPolicy = (maxAttempts = 2, retryCodes = defaultRetryStatsCodes, backoff = new ExponentialBackoff(), timeoutDuration = 3000, logEvents = true) => {
|
|
29
|
+
const retryPolicy = retry(handleType(NetworkError).orType(APIError, (error) => retryCodes.includes(error.status)), {
|
|
30
|
+
maxAttempts,
|
|
31
|
+
backoff,
|
|
32
|
+
});
|
|
33
|
+
const timeoutPolicy = timeout(timeoutDuration, TimeoutStrategy.Aggressive);
|
|
34
|
+
const policy = wrap(retryPolicy, timeoutPolicy);
|
|
35
|
+
if (logEvents) {
|
|
36
|
+
retryPolicy.onRetry((data) => logger.info('Fetch retrying...', data));
|
|
37
|
+
retryPolicy.onFailure((data) => logger.info('Fetch failed:', data));
|
|
38
|
+
retryPolicy.onSuccess((data) => logger.info('Fetch success:', data));
|
|
39
|
+
retryPolicy.onGiveUp((data) => logger.info('Fetch gave up:', data));
|
|
40
|
+
timeoutPolicy.onTimeout(() => logger.info('Fetch timed out'));
|
|
41
|
+
}
|
|
42
|
+
return policy;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* The default fetch policy.
|
|
46
|
+
* @public
|
|
47
|
+
*/
|
|
48
|
+
export const defaultFetchPolicy = createFetchPolicy();
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { __awaiter } from "tslib";
|
|
2
|
+
import { JSONReviver } from '@genesislcap/foundation-utils';
|
|
3
|
+
import { defaultFetchPolicy } from './policies';
|
|
4
|
+
/**
|
|
5
|
+
* {@inheritDoc ResponseOkChecker}
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
export const defaultResponseOkChecker = (response) => response.ok;
|
|
9
|
+
/**
|
|
10
|
+
* {@inheritDoc ResponseDataExtractor}
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
export const defaultResponseDataExtractor = (response) => __awaiter(void 0, void 0, void 0, function* () {
|
|
14
|
+
const text = yield response.text();
|
|
15
|
+
const contentType = response.headers.get('content-type');
|
|
16
|
+
const expectingJson = contentType === null || contentType === void 0 ? void 0 : contentType.includes('application/json');
|
|
17
|
+
return expectingJson ? JSON.parse(text, JSONReviver) : text;
|
|
18
|
+
});
|
|
19
|
+
/**
|
|
20
|
+
* Creates an extractor that applies a deserializer to the extracted response data.
|
|
21
|
+
* @param deserializer - The deserializer to apply.
|
|
22
|
+
* @param extractor - An optional data extractor. Defaults to {@link defaultResponseDataExtractor}.
|
|
23
|
+
* @public
|
|
24
|
+
*/
|
|
25
|
+
export const createResponseDataExtractorWithDeserializer = (deserializer, extractor = defaultResponseDataExtractor) => (response) => __awaiter(void 0, void 0, void 0, function* () {
|
|
26
|
+
const json = yield extractor(response);
|
|
27
|
+
return deserializer(json);
|
|
28
|
+
});
|
|
29
|
+
/**
|
|
30
|
+
* The default fetch config.
|
|
31
|
+
* @public
|
|
32
|
+
*/
|
|
33
|
+
export const defaultFetchConfig = {
|
|
34
|
+
fetcher: fetch,
|
|
35
|
+
fetchPolicy: defaultFetchPolicy,
|
|
36
|
+
responseOkChecker: defaultResponseOkChecker,
|
|
37
|
+
responseDataExtractor: defaultResponseDataExtractor,
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Default FetchMachineContext.
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
export const defaultFetchMachineContext = {
|
|
44
|
+
params: undefined,
|
|
45
|
+
controller: undefined,
|
|
46
|
+
response: undefined,
|
|
47
|
+
data: undefined,
|
|
48
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './fetch';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './logger';
|