chainflow 0.1.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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +414 -0
  3. package/dist/core/chainflow.d.ts +44 -0
  4. package/dist/core/chainflow.js +113 -0
  5. package/dist/core/chainflow.js.map +1 -0
  6. package/dist/core/inputNode.d.ts +41 -0
  7. package/dist/core/inputNode.js +228 -0
  8. package/dist/core/inputNode.js.map +1 -0
  9. package/dist/core/logger.d.ts +3 -0
  10. package/dist/core/logger.js +15 -0
  11. package/dist/core/logger.js.map +1 -0
  12. package/dist/core/sourceNode.d.ts +24 -0
  13. package/dist/core/sourceNode.js +37 -0
  14. package/dist/core/sourceNode.js.map +1 -0
  15. package/dist/core/store.d.ts +14 -0
  16. package/dist/core/store.js +91 -0
  17. package/dist/core/store.js.map +1 -0
  18. package/dist/core/utils/constants.d.ts +2 -0
  19. package/dist/core/utils/constants.js +6 -0
  20. package/dist/core/utils/constants.js.map +1 -0
  21. package/dist/core/utils/initializers.d.ts +17 -0
  22. package/dist/core/utils/initializers.js +24 -0
  23. package/dist/core/utils/initializers.js.map +1 -0
  24. package/dist/core/utils/link.d.ts +24 -0
  25. package/dist/core/utils/link.js +35 -0
  26. package/dist/core/utils/link.js.map +1 -0
  27. package/dist/core/utils/source.d.ts +13 -0
  28. package/dist/core/utils/source.js +18 -0
  29. package/dist/core/utils/source.js.map +1 -0
  30. package/dist/core/utils/symbols.d.ts +8 -0
  31. package/dist/core/utils/symbols.js +12 -0
  32. package/dist/core/utils/symbols.js.map +1 -0
  33. package/dist/http/endpoint.d.ts +64 -0
  34. package/dist/http/endpoint.js +233 -0
  35. package/dist/http/endpoint.js.map +1 -0
  36. package/dist/http/errors.d.ts +11 -0
  37. package/dist/http/errors.js +27 -0
  38. package/dist/http/errors.js.map +1 -0
  39. package/dist/http/logger.d.ts +3 -0
  40. package/dist/http/logger.js +15 -0
  41. package/dist/http/logger.js.map +1 -0
  42. package/dist/http/originServer.d.ts +28 -0
  43. package/dist/http/originServer.js +60 -0
  44. package/dist/http/originServer.js.map +1 -0
  45. package/dist/http/reqBuilder.d.ts +14 -0
  46. package/dist/http/reqBuilder.js +50 -0
  47. package/dist/http/reqBuilder.js.map +1 -0
  48. package/dist/http/utils/client.d.ts +16 -0
  49. package/dist/http/utils/client.js +39 -0
  50. package/dist/http/utils/client.js.map +1 -0
  51. package/dist/http/utils/constants.d.ts +2 -0
  52. package/dist/http/utils/constants.js +12 -0
  53. package/dist/http/utils/constants.js.map +1 -0
  54. package/dist/http/utils/hash.d.ts +4 -0
  55. package/dist/http/utils/hash.js +8 -0
  56. package/dist/http/utils/hash.js.map +1 -0
  57. package/dist/index.d.ts +8 -0
  58. package/dist/index.js +25 -0
  59. package/dist/index.js.map +1 -0
  60. package/package.json +59 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 - present Edwin Lim (edwinlzs)
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,414 @@
1
+ <h1 align="center" style="border-bottom: none;">🌊hainflow</h1>
2
+ <h3 align="center">Create dynamic and composable API call workflows.</h3>
3
+
4
+ ## Not Released Yet
5
+
6
+ Hi! If you are here, you're a bit early. I'm still setting up some stuff for the first release. Check back in later!
7
+
8
+ ## Use Cases
9
+
10
+ 1. Insert demo data via your app's APIs
11
+ 2. Simulate frontend interactions with backend APIs
12
+ 3. Test edge cases on endpoints with input variations
13
+
14
+ ## Basic Usage
15
+
16
+ ```console
17
+ npm install --save-dev chainflow
18
+ ```
19
+
20
+ Use `originServer` to define your endpoints and their request/response signatures with the `endpoint` method.
21
+
22
+ ```typescript
23
+ import { originServer } from chainflow;
24
+
25
+ const origin = originServer('127.0.0.1:5000');
26
+
27
+ const createUser = origin.post('/user').body({
28
+ name: 'Tom',
29
+ details: {
30
+ age: 40,
31
+ },
32
+ });
33
+
34
+ const createRole = origin.post('/role').body({
35
+ type: 'Engineer',
36
+ userId: createUser.resp.body.id,
37
+ });
38
+
39
+ const getUser = origin.get('/user').query({
40
+ roleType: createRole.resp.body.type,
41
+ });
42
+ ```
43
+
44
+ Use a `chainflow` to define a sequence of endpoint calls that take advantage of the values and links provided above.
45
+
46
+ ```typescript
47
+ import { chainflow } from Chainflow;
48
+
49
+ const flow = chainflow()
50
+ .call(createUser)
51
+ .call(createRole)
52
+ .call(getUser);
53
+
54
+ flow.run();
55
+ ```
56
+
57
+ ---
58
+
59
+ \
60
+ The above setup will result in the following API calls:
61
+
62
+ 1. `POST` Request to `/user` with body:
63
+
64
+ ```json
65
+ {
66
+ "name": "Tom",
67
+ "details": {
68
+ "age": 40
69
+ }
70
+ }
71
+ ```
72
+
73
+ 2. `POST` Request to `/role` with body:
74
+
75
+ ```json
76
+ {
77
+ "type": "Engineer",
78
+ "userId": "['userId' from response to step 1]"
79
+ }
80
+ ```
81
+
82
+ 3. `GET` Request to `/user?roleType=['type' from response to step 2]`
83
+
84
+ &nbsp;
85
+
86
+ ## More Features
87
+
88
+ ### Path params
89
+
90
+ Define path params by wrapping a param name with `{}` in the endpoint path.
91
+
92
+ ```typescript
93
+ const getGroupsWithUser = origin.get('/groups/{userId}');
94
+ ```
95
+
96
+ ### Query params
97
+
98
+ Define query params with the `query` method on an endpoint.
99
+
100
+ ```typescript
101
+ const getUsersInGroup = origin.get('/user').query({ groupId: 'some-id' });
102
+ ```
103
+
104
+ ### Headers
105
+
106
+ Specify headers with `headers` method on endpoints.
107
+
108
+ ```typescript
109
+ const getInfo = origin.get('/info').headers({ token: 'some-token' });
110
+ ```
111
+
112
+ You can also use `headers` on an `OriginServer` to have all endpoints made for that origin bear those headers.
113
+
114
+ ```typescript
115
+ const origin = originServer('127.0.0.1:3001').headers({ token: 'some-token' });
116
+
117
+ const getInfo = origin.get('/info'); // getInfo endpoint will have the headers defined above
118
+ ```
119
+
120
+ The request payloads under `Basic Usage` are defined with only _default_ values - i.e. the values which a Chainflow use if there are no response values from other endpoint calls linked to it.
121
+
122
+ However, you can also use the following features to more flexibly define the values used in a request.
123
+
124
+ ### `required`
125
+
126
+ Marks a value as required but without a default. The chainflow will expect this value to be sourced from another node. If no such source is available, the endpoint call will throw an error.
127
+
128
+ ```typescript
129
+ const createUser = origin.post('/user').body({
130
+ name: required(),
131
+ });
132
+ ```
133
+
134
+ ### [_EXPERIMENTAL_] - `pool`
135
+
136
+ Provide a pool of values to take from when building requests. By default, Chainflow will randomly choose a value from the pool for each call in a non-exhaustive manner.
137
+
138
+ ```typescript
139
+ const createUser = origin.post('/user').body({
140
+ name: pool(['Tom', 'Harry', 'Jane']),
141
+ details: {
142
+ age: 40,
143
+ },
144
+ });
145
+ ```
146
+
147
+ ### `gen`
148
+
149
+ Provide a callback that generates values for building requests.
150
+
151
+ ```typescript
152
+ const randAge = () => Math.floor(Math.random() * 100);
153
+
154
+ const createUser = origin.post('/user').body({
155
+ name: 'Tom',
156
+ details: {
157
+ age: gen(randAge),
158
+ },
159
+ });
160
+ ```
161
+
162
+ ### `source`
163
+
164
+ Specify a source node with a callback.
165
+
166
+ ```typescript
167
+ const addGreeting = (name: string) => `Hello ${name}`;
168
+
169
+ createNotification.body({
170
+ msg: source(getUser.resp.body.name, addGreeting);
171
+ });
172
+ ```
173
+
174
+ ### `sources`
175
+
176
+ Specify multiple source nodes that a value can be taken from, with an optional callback.
177
+
178
+ ```typescript
179
+ createNotification.body({
180
+ msg: sources([getUser.resp.body.name, createUser.resp.body.name], addGreeting);
181
+ });
182
+ ```
183
+
184
+ ### `link`
185
+
186
+ Link a response values to a single request node.
187
+
188
+ ```typescript
189
+ createNotification.set(({ body: { msg } }) => {
190
+ link(msg, getUser.resp.body.name);
191
+ });
192
+ ```
193
+
194
+ Optionally, you can pass a callback to transform the response value before it is passed to the node.
195
+
196
+ ```typescript
197
+ createNotification.set(({ body: { msg } }) => {
198
+ link(msg, getUser.resp.body.name, addGreeting);
199
+ });
200
+ ```
201
+
202
+ ### `linkMany`
203
+
204
+ Link multiple response values to a single request node, providing a callback to transform the values into a single output.
205
+
206
+ ```typescript
207
+ const mergeValues = ({ userName, favAnimal }: { userName: string; favAnimal: string }) =>
208
+ `${userName} likes ${favAnimal}.`;
209
+
210
+ createNotification.set(({ body: { msg } }) => {
211
+ linkMany(
212
+ msg, // the request node
213
+ // specify which response nodes to take values from and assigns them to a key
214
+ {
215
+ userName: getUser.resp.body.name,
216
+ favAnimal: getFavAnimal.resp.body.favAnimal,
217
+ },
218
+ // callback that takes the response values as its argument
219
+ // and returns a single output value for the request node
220
+ mergeValues,
221
+ );
222
+ });
223
+ ```
224
+
225
+ ### Call Options
226
+
227
+ You can declare manual values for an endpoint call in the chainflow itself, should you need to do so, by passing in a Call Options object as a second argument in the `call` method.
228
+
229
+ `body`, `pathParams`, `query` and `headers` can be set this way.
230
+
231
+ ```typescript
232
+ const createUser = origin.post('/user').body({
233
+ name: 'Tom',
234
+ });
235
+
236
+ chainflow()
237
+ .call(createUser, { body: { name: 'Harry' } })
238
+ .run();
239
+ ```
240
+
241
+ ### `seed`
242
+
243
+ You can specify request nodes to take values from the chainflow 'seed' by importing the `seed` object and linking nodes to it. Provide actual seed values by calling the `seed` method on a chainflow before you `run` it, like below.
244
+
245
+ ```typescript
246
+ import { chainflow, link seed, } from 'chainflow';
247
+
248
+ const createUser = origin.post('/user').body({
249
+ name: required(),
250
+ });
251
+
252
+ createUser.set(({ body: { name }}) => {
253
+ link(name, seed.username);
254
+ });
255
+
256
+ chainflow()
257
+ .call()
258
+ .seed({ username: 'Tom' })
259
+ .run();
260
+ ```
261
+
262
+ ### Allow Undefined Sources Values
263
+
264
+ By default, an input node will reject and skip a source node's value if it is unavailable or `undefined`. However, you can change this by passing a source node into the `allowUndefined` function, which modifies its properties to inform an input node to use its value regardless of whether the value is `undefined` or not.
265
+
266
+ ```typescript
267
+ import { allowUndefined } from 'chainflow';
268
+
269
+ createUser.set(({ body: { name } }) => {
270
+ link(name, allowUndefined(seed.username));
271
+ });
272
+ ```
273
+
274
+ This has important implications - it means that as long as the source (e.g. a response from an endpoint call) is available, then the linked source node's value will be taken and used (even if that value is unavailable, which would be taken as `undefined`). Therefore, any other linked sources will not be used UNLESS 1. they have a higher priority or 2. the source providing the linked node that allows `undefined` is unavailable.
275
+
276
+ &nbsp;
277
+
278
+ ### `clone`
279
+
280
+ You can create chainflow "templates" with the use of `clone` to create a copy of a chainflow and its endpoint callqueue. The clone can have endpoint calls added to it without modifying the initial flow.
281
+
282
+ ```typescript
283
+ const initialFlow = chainflow().call(endpoint1).call(endpoint2);
284
+
285
+ const clonedFlow = initialFlow.clone();
286
+
287
+ clonedFlow.call(endpoint3).run(); // calls endpoint 1, 2 and 3
288
+ initialFlow.call(endpoint4).run(); // calls endpoint 1, 2 and 4
289
+ ```
290
+
291
+ ### `extend`
292
+
293
+ You can connect multiple different chainflows together into a longer chainflow using `extend`.
294
+
295
+ ```typescript
296
+ const flow1 = chainflow().call(endpoint1).call(endpoint2);
297
+ const flow2 = chainflow().call(endpoint3);
298
+
299
+ flow1.extend(flow2).run(); // calls endpoint 1, 2 and 3
300
+ ```
301
+
302
+ ### `config`
303
+
304
+ `respParser`
305
+ By default, Chainflows will parse response bodies as JSON objects. To change this, you can call `.config` to change that configuration on an `endpoint` (or on an `OriginServer`, to apply it to all endpoints created from it) like so:
306
+
307
+ ```typescript
308
+ import { RespParser } from 'chainflow';
309
+
310
+ const getUser = origin.get('/user').config({
311
+ respParser: RespParser.Text,
312
+ });
313
+ ```
314
+
315
+ or with camelcase in JavaScript:
316
+
317
+ ```javascript
318
+ const getUser = origin.get('/user').config({
319
+ respParser: 'text',
320
+ });
321
+ ```
322
+
323
+ There are 4 supported ways to parse response bodies (as provided by the underlying HTTP client, `undici`): `arrayBuffer`, `blob`, `json` and `text`.
324
+
325
+ `respValidator`
326
+ Another configuration option is how to validate the response to an endpoint. By default, Chainflow only accepts responses that have HTTP status codes in the 200-299 range, and rejects responses otherwise (meaning their values will not be stored). You can pass in a custom `respValidator` to change this behaviour.
327
+
328
+ ```typescript
329
+ const testEndpoint = origin.get('/user').config({
330
+ respValidator: (resp) => {
331
+ if (resp.statusCode !== 201) return { valid: false, msg: 'Failed to retrieve users.' };
332
+ return { valid: true };
333
+ },
334
+ });
335
+ ```
336
+
337
+ ### `store`
338
+
339
+ Instead of direct links between endpoints, you can use a central store to keep values from some endpoints and have other endpoints take from it via the special `store` object.
340
+
341
+ ```typescript
342
+ import { store } from 'chainflow';
343
+
344
+ const createUser = origin.post('/user').body({
345
+ name: 'Tom',
346
+ }).store((resp) => ({
347
+ // this endpoint will store `id` from a response to `userId` in the store
348
+ userId: resp.body.id,
349
+ }));
350
+
351
+ const addRole = origin.post('/role').body({
352
+ // this endpoint will take `userId` from the store, if available
353
+ userId: store.userId,
354
+ role: 'Engineer',
355
+ });
356
+
357
+ chainflow().call(createUser).call(addRole).run();
358
+ ```
359
+
360
+ This is usually useful when you have endpoints that could take a value from any one of many other endpoints for the same input node. Having a store to centralise these many-to-many relationships (like an API Gateway) can improve the developer experience.
361
+
362
+ ### `continuesFrom` - transferring Chainflow states
363
+
364
+ Say we have 2 endpoints, `login` and `createGroup`. We want to login as a user once, then proceed to proceed 3 groups as that same user without having to login 3 times.
365
+
366
+ ```typescript
367
+ const createGroup = origin.post('/group').headers({
368
+ Authorization: login.resp.body.authToken,
369
+ }).body({
370
+ groupName: seed.groupName,
371
+ })
372
+
373
+ // loggedInFlow will contain a response from the `login` endpoint
374
+ const loggedInFlow = chainflow()
375
+ .call(login)
376
+ .run();
377
+
378
+ // createGroupFlow will take the response that
379
+ // loggedInFlow received and carry on from there
380
+ const createGroupFlow = chainflow()
381
+ .call(createGroup)
382
+ .continuesFrom(loggedInFlow);
383
+
384
+ const groupNames = ['RapGPT', 'Averageexpedition', 'Shaky Osmosis'];
385
+ for (const groupName in groupNames) {
386
+ createGroupFlow.seed({ groupName }).run();
387
+ }
388
+ ```
389
+
390
+ We run a chainflow that calls `login` first to get a response from the login endpoint.
391
+
392
+ Using the `continuesFrom` method, `createGroupFlow` will copy the state of source values (i.e. responses) from `loggedInFlow`. This means `createGroupFlow` will now have the logged in user's `authToken` received from calling `login`, and will use it when calling `createGroup` thrice for each group name in the `groupNames` array.
393
+
394
+ ### `logging`
395
+
396
+ Enable logs from Chainflow by setting `ENABLE_CHAINFLOW_LOGS=true` in your environment variables.
397
+
398
+ ## Future Updates
399
+
400
+ Below features are currently not yet supported but are planned in future releases.
401
+
402
+ 1. More flexibility to log and return responses
403
+ 2. API performance testing
404
+ 3. (Exploratory) Possibly some sort of UI/diagram generation
405
+
406
+ ## Development
407
+
408
+ Run specific test files:
409
+
410
+ `pnpm run test:file ./src/**/chainflow.test.ts`
411
+
412
+ ### Trivia
413
+
414
+ > You probably noticed that I enjoy using the Builder pattern for its clarity.
@@ -0,0 +1,44 @@
1
+ import { SourceValues } from './inputNode';
2
+ import { IStore } from './store';
3
+ export interface CallResult {
4
+ resp: any;
5
+ store: IStore<unknown>;
6
+ }
7
+ /** Defines an endpoint that a chainflow can call upon. */
8
+ export interface IEndpoint {
9
+ hash: string;
10
+ call: (sources: SourceValues, opts?: CallOpts) => Promise<CallResult>;
11
+ }
12
+ /** Options for configuring an endpoint call.
13
+ * @todo to decouple from chainflow in future versions. */
14
+ export interface CallOpts {
15
+ headers?: Record<string, string>;
16
+ query?: Record<string, string>;
17
+ pathParams?: Record<string, string>;
18
+ body?: Record<string, any>;
19
+ }
20
+ /** Special object used to link an InputNode to a chainflow seed. */
21
+ export declare const seed: import("./sourceNode").SourceNode;
22
+ /** Special object that acts as a central "gateway" between input and source values. */
23
+ export declare const store: import("./sourceNode").SourceNode;
24
+ export declare class Chainflow {
25
+ #private;
26
+ /** Run the set up chain */
27
+ run(): Promise<this>;
28
+ /** Adds a seed to this chainflow. */
29
+ seed(seed: Record<string, any>): this;
30
+ /** Adds an endpoint call to the callchain. */
31
+ call(endpoint: IEndpoint, opts?: CallOpts): this;
32
+ /** Resets the chainflow's state by clearing its accumulated sources. */
33
+ reset(): void;
34
+ /** Creates a clone of this chainflow's callqueue and initial sources
35
+ * which can be extended and run independently. */
36
+ clone(): Chainflow;
37
+ /** Extends this chainflow's callqueue with that of another flow. */
38
+ extend(cf: Chainflow): this;
39
+ /** Causes this chainflow to continue from the state of
40
+ * sources values of another chainflow. */
41
+ continuesFrom(cf: Chainflow): this;
42
+ /** @todo Returns the accumulated responses of this chainflow. */ responses(): void;
43
+ }
44
+ export declare const chainflow: () => Chainflow;
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
12
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
13
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
14
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
15
+ };
16
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
17
+ if (kind === "m") throw new TypeError("Private method is not writable");
18
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
19
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
20
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
21
+ };
22
+ var __importDefault = (this && this.__importDefault) || function (mod) {
23
+ return (mod && mod.__esModule) ? mod : { "default": mod };
24
+ };
25
+ var _Chainflow_sources, _Chainflow_initSources, _Chainflow_callqueue;
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.chainflow = exports.Chainflow = exports.store = exports.seed = void 0;
28
+ const sourceNode_1 = require("./sourceNode");
29
+ const deepmerge_1 = __importDefault(require("@fastify/deepmerge"));
30
+ const logger_1 = require("./logger");
31
+ const constants_1 = require("./utils/constants");
32
+ const deepmerge = (0, deepmerge_1.default)();
33
+ /** Special object used to link an InputNode to a chainflow seed. */
34
+ exports.seed = (0, sourceNode_1.sourceNode)(constants_1.SEED_HASH);
35
+ /** Special object that acts as a central "gateway" between input and source values. */
36
+ exports.store = (0, sourceNode_1.sourceNode)(constants_1.STORE_HASH);
37
+ class Chainflow {
38
+ constructor() {
39
+ /** Stores sources such as the seed or values accumulated from
40
+ * endpoint calls in the current flow. */
41
+ _Chainflow_sources.set(this, {});
42
+ /** Stores the sources that this chainflow was initialized with. */
43
+ _Chainflow_initSources.set(this, {});
44
+ _Chainflow_callqueue.set(this, []);
45
+ }
46
+ /** Run the set up chain */
47
+ run() {
48
+ return __awaiter(this, void 0, void 0, function* () {
49
+ (0, logger_1.log)(`Running chainflow...`);
50
+ this.reset();
51
+ __classPrivateFieldSet(this, _Chainflow_sources, __classPrivateFieldGet(this, _Chainflow_initSources, "f"), "f");
52
+ __classPrivateFieldGet(this, _Chainflow_sources, "f")[constants_1.STORE_HASH] = [{}];
53
+ for (const { endpoint, opts } of __classPrivateFieldGet(this, _Chainflow_callqueue, "f")) {
54
+ // call endpoint
55
+ const hash = endpoint.hash;
56
+ (0, logger_1.log)(`Calling endpoint with hash "${hash}"`);
57
+ try {
58
+ const { resp, store } = yield endpoint.call(__classPrivateFieldGet(this, _Chainflow_sources, "f"), opts);
59
+ if (Object.keys(store).length > 0)
60
+ __classPrivateFieldGet(this, _Chainflow_sources, "f")[constants_1.STORE_HASH][0] = deepmerge(__classPrivateFieldGet(this, _Chainflow_sources, "f")[constants_1.STORE_HASH][0], store);
61
+ __classPrivateFieldGet(this, _Chainflow_sources, "f")[hash] = [resp];
62
+ }
63
+ catch (e) {
64
+ (0, logger_1.warn)(`Chainflow stopped at endpoint with hash "${hash}" and error: ${e}`);
65
+ throw e;
66
+ }
67
+ }
68
+ (0, logger_1.log)('Finished running chainflow.');
69
+ return this;
70
+ });
71
+ }
72
+ /** Adds a seed to this chainflow. */
73
+ seed(seed) {
74
+ __classPrivateFieldGet(this, _Chainflow_initSources, "f")[constants_1.SEED_HASH] = [seed];
75
+ return this;
76
+ }
77
+ /** Adds an endpoint call to the callchain. */
78
+ call(endpoint, opts) {
79
+ __classPrivateFieldGet(this, _Chainflow_callqueue, "f").push({ endpoint, opts });
80
+ return this;
81
+ }
82
+ /** Resets the chainflow's state by clearing its accumulated sources. */
83
+ reset() {
84
+ __classPrivateFieldSet(this, _Chainflow_sources, {}, "f");
85
+ }
86
+ /** Creates a clone of this chainflow's callqueue and initial sources
87
+ * which can be extended and run independently. */
88
+ clone() {
89
+ const clone = new Chainflow();
90
+ __classPrivateFieldSet(clone, _Chainflow_initSources, structuredClone(__classPrivateFieldGet(this, _Chainflow_initSources, "f")), "f");
91
+ __classPrivateFieldSet(clone, _Chainflow_callqueue, [...__classPrivateFieldGet(this, _Chainflow_callqueue, "f")], "f");
92
+ return clone;
93
+ }
94
+ /** Extends this chainflow's callqueue with that of another flow. */
95
+ extend(cf) {
96
+ __classPrivateFieldGet(this, _Chainflow_callqueue, "f").push(...__classPrivateFieldGet(cf, _Chainflow_callqueue, "f"));
97
+ return this;
98
+ }
99
+ /** Causes this chainflow to continue from the state of
100
+ * sources values of another chainflow. */
101
+ continuesFrom(cf) {
102
+ __classPrivateFieldSet(this, _Chainflow_initSources, Object.assign(Object.assign({}, __classPrivateFieldGet(this, _Chainflow_initSources, "f")), __classPrivateFieldGet(cf, _Chainflow_sources, "f")), "f");
103
+ return this;
104
+ }
105
+ /** @todo Returns the accumulated responses of this chainflow. */ responses() { }
106
+ }
107
+ exports.Chainflow = Chainflow;
108
+ _Chainflow_sources = new WeakMap(), _Chainflow_initSources = new WeakMap(), _Chainflow_callqueue = new WeakMap();
109
+ const chainflow = () => {
110
+ return new Chainflow();
111
+ };
112
+ exports.chainflow = chainflow;
113
+ //# sourceMappingURL=chainflow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chainflow.js","sourceRoot":"","sources":["../../src/core/chainflow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import { SourceNode } from './sourceNode';
2
+ import { getNodeValue, setSource, setSources, setValuePool } from './utils/symbols';
3
+ /** @experimental How a value pool should choose its values. */
4
+ export declare enum VALUE_POOL_SELECT {
5
+ UNIFORM = 0
6
+ }
7
+ export declare enum NodeValue {
8
+ ValuePool = 0,
9
+ Generator = 1,
10
+ Required = 2,
11
+ Source = 3,
12
+ SourceWithCallback = 4,
13
+ Sources = 5
14
+ }
15
+ type SourceValue = any;
16
+ export type SourceValues = {
17
+ [hash: string]: SourceValue[];
18
+ };
19
+ /** A data node for constructing an input object. */
20
+ export declare class InputNode {
21
+ #private;
22
+ /** Key-values under this node, if this node represents an object. */
23
+ [key: string]: any;
24
+ constructor(val: any);
25
+ /** Sets a source node for this input node. */
26
+ [setSource](source: SourceNode, callback?: (val: any) => any): void;
27
+ /** Sets multiple source nodes to be combined into a single value for this input node */
28
+ [setSources](sources: {
29
+ [key: string]: SourceNode;
30
+ }, callback: (val: any) => any): void;
31
+ /** Sets the pool of values for this input node. */
32
+ [setValuePool](valuePool: any[]): void;
33
+ /** Retrieve value of a node. */
34
+ [getNodeValue](sourceValues: SourceValues, missingValues: string[][], currentPath: string[]): any;
35
+ /**
36
+ * Builds a key-value object from input node values and
37
+ * any available linked sources.
38
+ */
39
+ buildKvObject(currentPath: string[], missingValues: string[][], sourceValues: SourceValues): any;
40
+ }
41
+ export {};