cliodot 0.1.1 → 1.0.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 +454 -10
- package/dist/ConnectorBuilder.js +1 -1
- package/dist/Flosync.d.ts +3 -0
- package/dist/Flosync.d.ts.map +1 -1
- package/dist/Flosync.js +1 -1
- package/dist/FlosyncClient.d.ts +9 -0
- package/dist/FlosyncClient.d.ts.map +1 -1
- package/dist/FlosyncClient.js +1 -1
- package/dist/FunctionBuilder.js +1 -1
- package/dist/ValidatorBuilder.js +1 -1
- package/dist/WorkflowBuilder.js +1 -1
- package/dist/__tests__/function.test.js +1 -1
- package/dist/__tests__/runner.test.js +1 -1
- package/dist/connectors/builtin.js +1 -1
- package/dist/connectors/registry.js +1 -1
- package/dist/decorators/build.d.ts +3 -0
- package/dist/decorators/build.d.ts.map +1 -0
- package/dist/decorators/build.js +1 -0
- package/dist/decorators/index.d.ts +5 -0
- package/dist/decorators/index.d.ts.map +1 -0
- package/dist/decorators/index.js +1 -0
- package/dist/decorators/metadata.d.ts +73 -0
- package/dist/decorators/metadata.d.ts.map +1 -0
- package/dist/decorators/metadata.js +1 -0
- package/dist/decorators/steps.d.ts +40 -0
- package/dist/decorators/steps.d.ts.map +1 -0
- package/dist/decorators/steps.js +1 -0
- package/dist/decorators/workflow.d.ts +7 -0
- package/dist/decorators/workflow.d.ts.map +1 -0
- package/dist/decorators/workflow.js +1 -0
- package/dist/errors.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/runner/ProcessorEngine.d.ts.map +1 -1
- package/dist/runner/ProcessorEngine.js +1 -1
- package/dist/runner/connector.executor.js +1 -1
- package/dist/runner/utilities/math.executor.js +1 -1
- package/dist/runner/utilities/random.executor.js +1 -1
- package/dist/runner/utilities/string.executor.js +1 -1
- package/dist/template.js +1 -1
- package/dist/transformers/StepsToNodesTransformer.js +1 -1
- package/dist/transformers/WorkflowTransformer.js +1 -1
- package/dist/types/builtin.js +1 -1
- package/dist/types/client.api.js +1 -1
- package/dist/types/connector.js +1 -1
- package/dist/types/function.js +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/validator.js +1 -1
- package/dist/types/workflow.js +1 -1
- package/dist/validators/validators.js +1 -1
- package/dist/variable.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,11 +1,35 @@
|
|
|
1
|
-
#
|
|
1
|
+
# cliodot
|
|
2
2
|
|
|
3
3
|
SDK for building workflows, APIs, and functions with Cliodot Flowsync. Define workflows and functions in code, run them locally, and optionally sync with your Flowsync account.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install
|
|
8
|
+
npm install cliodot
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Module Support
|
|
12
|
+
|
|
13
|
+
Cliodot supports both **ES Modules** (recommended for Node.js 16+, TypeScript, and modern frameworks) and **CommonJS**.
|
|
14
|
+
|
|
15
|
+
**ES Modules (ESM) / TypeScript**
|
|
16
|
+
```javascript
|
|
17
|
+
import { flosync, v } from 'cliodot';
|
|
18
|
+
// or: import { flosync, variable as v } from 'cliodot';
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**CommonJS (CJS)**
|
|
22
|
+
```javascript
|
|
23
|
+
const { flosync, v } = require('cliodot');
|
|
24
|
+
// or: const { flosync, variable: v } = require('cliodot');
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Note for some ESM environments:**
|
|
28
|
+
If you encounter `SyntaxError: Named export 'flosync' not found`, use the default import instead:
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
import pkg from 'cliodot';
|
|
32
|
+
const { flosync, v } = pkg;
|
|
9
33
|
```
|
|
10
34
|
|
|
11
35
|
Optional peer dependencies:
|
|
@@ -29,7 +53,7 @@ npm install argon2 bcrypt
|
|
|
29
53
|
## Quick Start
|
|
30
54
|
|
|
31
55
|
```javascript
|
|
32
|
-
|
|
56
|
+
import { flosync, v } from 'cliodot';
|
|
33
57
|
|
|
34
58
|
flosync.configure({
|
|
35
59
|
connectors: {
|
|
@@ -73,7 +97,7 @@ flosync.run('my-workflow', { body: { id: 1 }, headers: { 'x-request-id': 'abc' }
|
|
|
73
97
|
flosync.configure({
|
|
74
98
|
apiKey: process.env.FLOSYNC_API_KEY,
|
|
75
99
|
apiSecret: process.env.FLOSYNC_API_SECRET,
|
|
76
|
-
baseUrl: 'http://localhost:8080',
|
|
100
|
+
baseUrl: 'http://localhost:8080' //optional,
|
|
77
101
|
projectId: process.env.FLOSYNC_PROJECT_ID,
|
|
78
102
|
connectors: {
|
|
79
103
|
'mongodb.system': { uri: process.env.MONGO_URI, database: 'myapp' },
|
|
@@ -126,7 +150,7 @@ Use `variable` (or `v`) instead of typing `{{ }}`:
|
|
|
126
150
|
| `v.expr(expression)` | `v.expr('length(body.items) * 10')` | `{{ length(body.items) * 10 }}` |
|
|
127
151
|
|
|
128
152
|
```javascript
|
|
129
|
-
|
|
153
|
+
import { flosync, v } from 'cliodot';
|
|
130
154
|
|
|
131
155
|
s.validator({ amount: v.body('amount'), email: v.body('email') });
|
|
132
156
|
s.transform({ total: v.stepResult('calculate', 'total'), id: v.stepResult('charge', 'id') });
|
|
@@ -160,6 +184,426 @@ Steps that can succeed or fail support `.then()` and `.else()`:
|
|
|
160
184
|
.else('invalid'))
|
|
161
185
|
```
|
|
162
186
|
|
|
187
|
+
### Decorator Workflows
|
|
188
|
+
|
|
189
|
+
You can define workflows using TypeScript decorators instead of the fluent `.workflow().step()` builder.
|
|
190
|
+
|
|
191
|
+
1. Enable decorators in your consuming project's `tsconfig.json`:
|
|
192
|
+
|
|
193
|
+
```json
|
|
194
|
+
{
|
|
195
|
+
"compilerOptions": {
|
|
196
|
+
"experimentalDecorators": true
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
2. Define a workflow class and decorate it with `@Workflow()` and `@Http()` (or `@Job()` / `@Schedule()`).
|
|
202
|
+
|
|
203
|
+
3. Decorate class methods to create steps. By default, the step ID is the method name.
|
|
204
|
+
|
|
205
|
+
4. Build and register the workflow with `buildWorkflowFromClass()` or `flosync.registerFromClass()`.
|
|
206
|
+
|
|
207
|
+
5. Optional: add JavaScript inside the decorated method body.
|
|
208
|
+
|
|
209
|
+
If a decorated method contains non-empty code, the SDK uses it based on the step decorator option `stage`:
|
|
210
|
+
|
|
211
|
+
- `stage: "pre"` (default): injects an extra `code` step right before the decorated step. The injected code receives `ctx` with:
|
|
212
|
+
|
|
213
|
+
- `ctx.input` (previous step output, or trigger data for the first step)
|
|
214
|
+
- `ctx.trigger` (trigger payload)
|
|
215
|
+
- `ctx.steps` (all previous `stepResults`)
|
|
216
|
+
- `ctx.vars` (workflow vars, including anything set via `setVars`)
|
|
217
|
+
|
|
218
|
+
To pass values to later steps, return an object with `setVars`:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
return { setVars: { limit: 10 } };
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Later templates can read them via `v.vars("limit")` (resolves to `{{ vars["limit"] }}`) or `{{ vars.limit }}`.
|
|
225
|
+
|
|
226
|
+
- `stage: "post"`: executes the method body after the decorated step finishes, and allows overriding the decorated step output directly.
|
|
227
|
+
|
|
228
|
+
In this mode, return:
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
return { out: <newOutput>, setVars?: { ... } };
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
The overridden output becomes the value of `v.stepResult("<stepId>")` for subsequent steps.
|
|
235
|
+
|
|
236
|
+
In `stage: "post"`, `ctx.input` is the decorated step output produced by the connector/function call.
|
|
237
|
+
|
|
238
|
+
Example: list todos using decorators
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import { flosync, v, Workflow, Http, Connector, Condition, Responder, buildWorkflowFromClass, defineCustomConnector } from "cliodot";
|
|
242
|
+
|
|
243
|
+
const CarrierStore = defineCustomConnector("carrier.store", {
|
|
244
|
+
listTodos: "listTodos",
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
@Workflow("todos-list-decorators", "Todos List (Decorators)")
|
|
248
|
+
@Http("GET", "/todos")
|
|
249
|
+
class TodosListDecoratorsWorkflow {
|
|
250
|
+
@Connector(
|
|
251
|
+
CarrierStore.id,
|
|
252
|
+
CarrierStore.actions.listTodos,
|
|
253
|
+
{ params: { limit: 10 } },
|
|
254
|
+
{ order: 0, stage: "post" }
|
|
255
|
+
)
|
|
256
|
+
list(ctx: any) {
|
|
257
|
+
const todos = Array.isArray(ctx?.input?.todos) ? ctx.input.todos : [];
|
|
258
|
+
const formatted = todos.map((t: any) => {
|
|
259
|
+
const title = String(t?.title ?? "").trim();
|
|
260
|
+
return { ...t, title, title_upper: title.toUpperCase() };
|
|
261
|
+
});
|
|
262
|
+
return { out: { ...(ctx?.input ?? {}), todos: formatted } };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
@Condition(v.stepResult("list", "valid"), { then: "ok", else: "unauth", order: 1 })
|
|
266
|
+
check() {}
|
|
267
|
+
|
|
268
|
+
@Responder("json", { body: { todos: v.stepResult("list", "todos") } }, { order: 2 })
|
|
269
|
+
ok() {}
|
|
270
|
+
|
|
271
|
+
@Responder("json", { statusCode: 401, body: { error: "Unauthorized" } }, { order: 3 })
|
|
272
|
+
unauth() {}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const workflow = buildWorkflowFromClass(TodosListDecoratorsWorkflow);
|
|
276
|
+
flosync.register(workflow);
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
If you prefer direct registration:
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
flosync.registerFromClass(TodosListDecoratorsWorkflow);
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Decorator workflow: validation, MongoDB, string utilities, shaped JSON
|
|
286
|
+
|
|
287
|
+
This pattern matches what many APIs do: validate input, derive a slug with `utility.string`, write to MongoDB, return a single JSON object with nested `note` metadata (not a flat dump of raw step outputs).
|
|
288
|
+
|
|
289
|
+
Configure the database once:
|
|
290
|
+
|
|
291
|
+
```javascript
|
|
292
|
+
flosync.configure({
|
|
293
|
+
connectors: {
|
|
294
|
+
"mongodb.system": { uri: process.env.MONGO_URI, database: "myapp" },
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Workflow (imports: `flosync`, `v`, `Workflow`, `Http`, `Validator`, `Util`, `Db`, `Responder`, `buildWorkflowFromClass`, `ConnectorId`):
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
import {
|
|
303
|
+
flosync,
|
|
304
|
+
v,
|
|
305
|
+
Workflow,
|
|
306
|
+
Http,
|
|
307
|
+
Validator,
|
|
308
|
+
Util,
|
|
309
|
+
Db,
|
|
310
|
+
Responder,
|
|
311
|
+
buildWorkflowFromClass,
|
|
312
|
+
ConnectorId,
|
|
313
|
+
} from "cliodot";
|
|
314
|
+
|
|
315
|
+
@Workflow("notes-create-decorators", "Notes create (decorators)")
|
|
316
|
+
@Http("POST", "/decorators/notes")
|
|
317
|
+
class NotesCreateDecoratorsWorkflow {
|
|
318
|
+
@Validator(
|
|
319
|
+
{ title: v.body("title"), body: v.body("body") },
|
|
320
|
+
{ order: 0, then: "slugify", else: "validationError" }
|
|
321
|
+
)
|
|
322
|
+
validate() {}
|
|
323
|
+
|
|
324
|
+
@Util(
|
|
325
|
+
ConnectorId.Utility.String,
|
|
326
|
+
"slugify",
|
|
327
|
+
{ string: v.body("title") },
|
|
328
|
+
{ order: 1 }
|
|
329
|
+
)
|
|
330
|
+
slugify() {}
|
|
331
|
+
|
|
332
|
+
@Db(
|
|
333
|
+
"mongodb",
|
|
334
|
+
"insertOne",
|
|
335
|
+
{
|
|
336
|
+
collection: "notes",
|
|
337
|
+
document: {
|
|
338
|
+
title: v.body("title"),
|
|
339
|
+
body: v.body("body"),
|
|
340
|
+
slug: v.stepResult("slugify", "value"),
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
{ order: 2 }
|
|
344
|
+
)
|
|
345
|
+
persist() {}
|
|
346
|
+
|
|
347
|
+
@Responder(
|
|
348
|
+
"json",
|
|
349
|
+
{
|
|
350
|
+
statusCode: 201,
|
|
351
|
+
body: {
|
|
352
|
+
ok: true,
|
|
353
|
+
note: {
|
|
354
|
+
id: v.stepResult("persist", "insertedId"),
|
|
355
|
+
title: v.body("title"),
|
|
356
|
+
slug: v.stepResult("slugify", "value"),
|
|
357
|
+
body: v.body("body"),
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
{ order: 3 }
|
|
362
|
+
)
|
|
363
|
+
created() {}
|
|
364
|
+
|
|
365
|
+
@Responder(
|
|
366
|
+
"json",
|
|
367
|
+
{
|
|
368
|
+
statusCode: 400,
|
|
369
|
+
body: {
|
|
370
|
+
ok: false,
|
|
371
|
+
error: "validation_failed",
|
|
372
|
+
details: v.stepResult("validate", "errors"),
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
{ order: 4 }
|
|
376
|
+
)
|
|
377
|
+
validationError() {}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const notesDecoratorsWorkflow = buildWorkflowFromClass(NotesCreateDecoratorsWorkflow);
|
|
381
|
+
flosync.register(notesDecoratorsWorkflow);
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
Example request:
|
|
385
|
+
|
|
386
|
+
```http
|
|
387
|
+
POST /decorators/notes
|
|
388
|
+
Content-Type: application/json
|
|
389
|
+
|
|
390
|
+
{
|
|
391
|
+
"title": "Ship checklist",
|
|
392
|
+
"body": "Pack boxes, print labels, hand off to carrier."
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
Example success response body:
|
|
397
|
+
|
|
398
|
+
```json
|
|
399
|
+
{
|
|
400
|
+
"ok": true,
|
|
401
|
+
"note": {
|
|
402
|
+
"id": "65a1b2c3d4e5f6789012345",
|
|
403
|
+
"title": "Ship checklist",
|
|
404
|
+
"slug": "ship-checklist",
|
|
405
|
+
"body": "Pack boxes, print labels, hand off to carrier."
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Example validation failure response body:
|
|
411
|
+
|
|
412
|
+
```json
|
|
413
|
+
{
|
|
414
|
+
"ok": false,
|
|
415
|
+
"error": "validation_failed",
|
|
416
|
+
"details": {
|
|
417
|
+
"title": "Field is required"
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
`@Util` is typed the same way as `s.util(ConnectorId.Utility.String, "slugify", { ... })` in the fluent builder. `@Db` supports the `mongodb` and `mysql` engines (see [Database](#database)). Step IDs in `v.stepResult("slugify", "value")` refer to the **method name** you put on each decorated step (after sanitization, dots and hyphens in custom IDs become underscores).
|
|
423
|
+
|
|
424
|
+
### Custom `execute` connector plus decorators (catalog sample)
|
|
425
|
+
|
|
426
|
+
For behavior that is not a REST template (in-memory demo data, your own SDK, legacy SOAP adapter, etc.), register a connector with an `execute` function, then call it from decorators with `@Connector` exactly like a built-in connector. This mirrors the `carrier.store` pattern: actions such as `listProducts` and `createProduct`, `options.body` / `options.params` / `options.pathParams` passed from the step config.
|
|
427
|
+
|
|
428
|
+
Define typed action names and the connector:
|
|
429
|
+
|
|
430
|
+
```typescript
|
|
431
|
+
import { flosync, defineCustomConnector } from "cliodot";
|
|
432
|
+
|
|
433
|
+
type CatalogProduct = { id: string; name: string; price: number };
|
|
434
|
+
|
|
435
|
+
const Catalog = defineCustomConnector("acme.catalog", {
|
|
436
|
+
listProducts: "listProducts",
|
|
437
|
+
createProduct: "createProduct",
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
const catalogProducts: CatalogProduct[] = [
|
|
441
|
+
{ id: "p_static_1", name: "Starter", price: 0 },
|
|
442
|
+
{ id: "p_static_2", name: "Pro", price: 49 },
|
|
443
|
+
];
|
|
444
|
+
|
|
445
|
+
flosync.registerConnector(Catalog.id, {
|
|
446
|
+
_id: Catalog.id,
|
|
447
|
+
type: "system",
|
|
448
|
+
name: "Acme Catalog",
|
|
449
|
+
meta: { category: "catalog" },
|
|
450
|
+
auth: { type: "none" },
|
|
451
|
+
execute: async (action, options) => {
|
|
452
|
+
const body = (options.body ?? {}) as Record<string, unknown>;
|
|
453
|
+
const params = (options.params ?? {}) as Record<string, unknown>;
|
|
454
|
+
const pathParams = (options.pathParams ?? {}) as Record<string, unknown>;
|
|
455
|
+
const merged = { ...params, ...pathParams };
|
|
456
|
+
|
|
457
|
+
if (action === Catalog.actions.listProducts) {
|
|
458
|
+
const limitRaw = merged.limit;
|
|
459
|
+
const limit =
|
|
460
|
+
limitRaw === undefined || limitRaw === ""
|
|
461
|
+
? catalogProducts.length
|
|
462
|
+
: Math.max(0, Number(limitRaw));
|
|
463
|
+
const slice = catalogProducts.slice(0, Number.isFinite(limit) ? limit : catalogProducts.length);
|
|
464
|
+
return { ok: true, products: slice, count: slice.length };
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (action === Catalog.actions.createProduct) {
|
|
468
|
+
const name = String(body.name ?? "").trim();
|
|
469
|
+
const price = Number(body.price);
|
|
470
|
+
if (!name || Number.isNaN(price)) {
|
|
471
|
+
return { ok: false, error: "name_and_price_required" };
|
|
472
|
+
}
|
|
473
|
+
const id = `p_${Date.now()}`;
|
|
474
|
+
const product: CatalogProduct = { id, name, price };
|
|
475
|
+
catalogProducts.push(product);
|
|
476
|
+
return { ok: true, product };
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return { ok: false, error: "unknown_action" };
|
|
480
|
+
},
|
|
481
|
+
});
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
List products with decorator `stage: "post"` to shape the payload (add `price_display`, clamp list) before the responder runs:
|
|
485
|
+
|
|
486
|
+
```typescript
|
|
487
|
+
import {
|
|
488
|
+
flosync,
|
|
489
|
+
v,
|
|
490
|
+
Workflow,
|
|
491
|
+
Http,
|
|
492
|
+
Connector,
|
|
493
|
+
Responder,
|
|
494
|
+
buildWorkflowFromClass,
|
|
495
|
+
} from "cliodot";
|
|
496
|
+
|
|
497
|
+
@Workflow("acme-catalog-list-decorators", "Catalog list (decorators)")
|
|
498
|
+
@Http("GET", "/decorators/catalog/products")
|
|
499
|
+
class CatalogListDecoratorsWorkflow {
|
|
500
|
+
@Connector(
|
|
501
|
+
Catalog.id,
|
|
502
|
+
Catalog.actions.listProducts,
|
|
503
|
+
{ params: { limit: v.query("limit") } },
|
|
504
|
+
{ order: 0, stage: "post" }
|
|
505
|
+
)
|
|
506
|
+
list(ctx: any) {
|
|
507
|
+
const input = ctx?.input ?? {};
|
|
508
|
+
const items = Array.isArray(input.products) ? input.products : [];
|
|
509
|
+
const withLabels = items.map((p: CatalogProduct) => ({
|
|
510
|
+
...p,
|
|
511
|
+
price_display: `$${Number(p.price).toFixed(2)}`,
|
|
512
|
+
}));
|
|
513
|
+
return { out: { ...input, products: withLabels, count: withLabels.length } };
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
@Responder(
|
|
517
|
+
"json",
|
|
518
|
+
{
|
|
519
|
+
body: {
|
|
520
|
+
ok: true,
|
|
521
|
+
count: v.stepResult("list", "count"),
|
|
522
|
+
products: v.stepResult("list", "products"),
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
{ order: 1 }
|
|
526
|
+
)
|
|
527
|
+
respond() {}
|
|
528
|
+
}
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
Create product with the same connector; the method body flags bad input and normalizes the successful response:
|
|
532
|
+
|
|
533
|
+
```typescript
|
|
534
|
+
@Workflow("acme-catalog-create-decorators", "Catalog create (decorators)")
|
|
535
|
+
@Http("POST", "/decorators/catalog/products")
|
|
536
|
+
class CatalogCreateDecoratorsWorkflow {
|
|
537
|
+
@Connector(
|
|
538
|
+
Catalog.id,
|
|
539
|
+
Catalog.actions.createProduct,
|
|
540
|
+
{ body: { name: v.body("name"), price: v.body("price") } },
|
|
541
|
+
{ order: 0, stage: "post" }
|
|
542
|
+
)
|
|
543
|
+
save(ctx: any) {
|
|
544
|
+
const input = ctx?.input ?? {};
|
|
545
|
+
if (!input.ok) return { out: input };
|
|
546
|
+
const p = input.product as CatalogProduct;
|
|
547
|
+
return {
|
|
548
|
+
out: {
|
|
549
|
+
...input,
|
|
550
|
+
product: { ...p, price_display: `$${Number(p.price).toFixed(2)}` },
|
|
551
|
+
},
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
@Responder(
|
|
556
|
+
"json",
|
|
557
|
+
{
|
|
558
|
+
statusCode: 201,
|
|
559
|
+
body: {
|
|
560
|
+
ok: true,
|
|
561
|
+
product: v.stepResult("save", "product"),
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
{ order: 1 }
|
|
565
|
+
)
|
|
566
|
+
respond() {}
|
|
567
|
+
}
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
Example `POST /decorators/catalog/products` JSON body:
|
|
571
|
+
|
|
572
|
+
```json
|
|
573
|
+
{
|
|
574
|
+
"name": "Enterprise",
|
|
575
|
+
"price": 199
|
|
576
|
+
}
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
Example `201` response body:
|
|
580
|
+
|
|
581
|
+
```json
|
|
582
|
+
{
|
|
583
|
+
"ok": true,
|
|
584
|
+
"product": {
|
|
585
|
+
"id": "p_1711286400000",
|
|
586
|
+
"name": "Enterprise",
|
|
587
|
+
"price": 199,
|
|
588
|
+
"price_display": "$199.00"
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
Example `GET /decorators/catalog/products?limit=1` response body:
|
|
594
|
+
|
|
595
|
+
```json
|
|
596
|
+
{
|
|
597
|
+
"ok": true,
|
|
598
|
+
"count": 1,
|
|
599
|
+
"products": [
|
|
600
|
+
{ "id": "p_static_1", "name": "Starter", "price": 0, "price_display": "$0.00" }
|
|
601
|
+
]
|
|
602
|
+
}
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
Register both workflows with `buildWorkflowFromClass` / `flosync.register` or `flosync.registerFromClass`. Use `flosync.run("<workflow-id>", { body: { ... } })` with the `@Workflow` string ID (the engine also registers the same workflow under `__rawId` when you use kebab-case IDs).
|
|
606
|
+
|
|
163
607
|
## Functions
|
|
164
608
|
|
|
165
609
|
```javascript
|
|
@@ -226,7 +670,7 @@ Use `s.util(connectorId, action, config)` with built-in utility connectors. Use
|
|
|
226
670
|
| `utility.geo` | calculate_distance, point_in_polygon, get_bounding_box, reverse_geocode, geocode, convert_coordinates |
|
|
227
671
|
|
|
228
672
|
```javascript
|
|
229
|
-
|
|
673
|
+
import { ConnectorId, ConnectorActions, getConnectorActions } from 'cliodot';
|
|
230
674
|
|
|
231
675
|
s.util(ConnectorId.Utility.String, 'slugify', { string: 'Hello World' });
|
|
232
676
|
s.util(ConnectorId.Utility.Math, 'clamp', { number: 150, min: 0, max: 100 });
|
|
@@ -288,7 +732,7 @@ Use `ConnectorId` and `ConnectorActions` for built-in connectors, or `defineCust
|
|
|
288
732
|
### Built-in connectors
|
|
289
733
|
|
|
290
734
|
```javascript
|
|
291
|
-
|
|
735
|
+
import { flosync, ConnectorId, ConnectorActions, getConnectorActions, listBuiltInConnectors } from 'cliodot';
|
|
292
736
|
|
|
293
737
|
// Database
|
|
294
738
|
s.db('mongodb', 'insertOne', { collection: 'orders', document: {} });
|
|
@@ -319,7 +763,7 @@ console.log(listBuiltInConnectors());
|
|
|
319
763
|
Define your connector with typed actions for autocomplete:
|
|
320
764
|
|
|
321
765
|
```javascript
|
|
322
|
-
|
|
766
|
+
import { defineCustomConnector } from 'cliodot';
|
|
323
767
|
|
|
324
768
|
const MyStore = defineCustomConnector('my.store', {
|
|
325
769
|
login: 'login',
|
|
@@ -336,7 +780,7 @@ s.connector(MyStore.id, MyStore.actions.listItems, {});
|
|
|
336
780
|
For a simple list of action names:
|
|
337
781
|
|
|
338
782
|
```javascript
|
|
339
|
-
|
|
783
|
+
import { defineCustomConnectorFromList } from 'cliodot';
|
|
340
784
|
|
|
341
785
|
const MyStore = defineCustomConnectorFromList('my.store', ['login', 'listItems', 'createItem']);
|
|
342
786
|
```
|
|
@@ -445,7 +889,7 @@ flosync.registerConnector('my.auth', {
|
|
|
445
889
|
});
|
|
446
890
|
```
|
|
447
891
|
|
|
448
|
-
Import types for TypeScript: `ConnectorExecuteOptions`, `ConnectorContext`, `ConnectorExecuteFn` from
|
|
892
|
+
Import types for TypeScript: `ConnectorExecuteOptions`, `ConnectorContext`, `ConnectorExecuteFn` from `cliodot`.
|
|
449
893
|
|
|
450
894
|
**Execute signature**
|
|
451
895
|
|
package/dist/ConnectorBuilder.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
'use strict';const
|
|
1
|
+
'use strict';const _0x1bcda6=_0x59e9;(function(_0xbb1e0e,_0x4caa26){const _0x362332=_0x59e9,_0x5d8efd=_0xbb1e0e();while(!![]){try{const _0x5521b1=-parseInt(_0x362332(0x1dd))/0x1+parseInt(_0x362332(0x1da))/0x2*(parseInt(_0x362332(0x1c4))/0x3)+parseInt(_0x362332(0x1e6))/0x4*(parseInt(_0x362332(0x1cd))/0x5)+-parseInt(_0x362332(0x1d1))/0x6*(-parseInt(_0x362332(0x1ca))/0x7)+-parseInt(_0x362332(0x1e3))/0x8+parseInt(_0x362332(0x1de))/0x9*(-parseInt(_0x362332(0x1ea))/0xa)+parseInt(_0x362332(0x1e2))/0xb;if(_0x5521b1===_0x4caa26)break;else _0x5d8efd['push'](_0x5d8efd['shift']());}catch(_0x257461){_0x5d8efd['push'](_0x5d8efd['shift']());}}}(_0x49c0,0x4cf08));Object['defineProperty'](exports,'__esModule',{'value':!![]}),exports[_0x1bcda6(0x1db)]=void 0x0,exports[_0x1bcda6(0x1e0)]=connector;function _0x49c0(){const _0x2ff68d=['zw5KCg9PBNq','yxv0AenVBMzPzW','x25HBwu','CMvZDa','x2vUzhbVAw50CW','nLr5tgrABa','x2rLC2nYAxb0Aw9U','ChvZAa','x2LK','yNvPBgq','yM90vg9Rzw4','n0fxBhbUtW','yMfZzvvYBa','Chv0','mtu3nZv3AMPhCe0','yMvHCMvY','qxv0Ag9YAxPHDgLVBG','qM90ia','mtiZmZa2ENPoBwvc','C2X1zW','Cgf0y2G','yxbPx2TLEq','zgvSzxrL','y3vZDg9TrMXVDW','x3nSDwC','y3vZDg9T','zgvZy3jPChrPB24','ndq5mdC4qw5NEw9i','q29UBMvJDg9YqNvPBgrLCG','uefuq0G','nJiXndmXA2zJEg1y','mJqZu2j4BLbS','x21LDge','y29UBMvJDg9Y','r0vu','nte0ndCZm1DKCLfKvG','mtu5nJqZmKHUAMPYDG','ue9tva','x2jHC2vvCMW','ndm2vxHhDNPx','z2v0','x2f1DgG','x3r5Cgu','ntm3ndbUrfHlwMe','yxbPs2v5','uKvtva','revmrvrf'];_0x49c0=function(){return _0x2ff68d;};return _0x49c0();}class ConnectorBuilder{constructor(_0x26423f,_0x2c0462){const _0x28eaf1=_0x1bcda6;this[_0x28eaf1(0x1e9)]=_0x28eaf1(0x1ec),this['_baseUrl']='',this['_auth']={'type':_0x28eaf1(0x1ce)},this[_0x28eaf1(0x1c3)]=[],this[_0x28eaf1(0x1c7)]=_0x26423f,this['_name']=_0x2c0462??_0x26423f;}[_0x1bcda6(0x1c2)](){const _0x2e42b1=_0x1bcda6;return this[_0x2e42b1(0x1e9)]=_0x2e42b1(0x1ec),this;}[_0x1bcda6(0x1cb)](_0xdd2a11){const _0x438029=_0x1bcda6;return this[_0x438029(0x1e5)]=_0xdd2a11,this;}[_0x1bcda6(0x1ce)](_0xff61df){const _0x3ccf78=_0x1bcda6;return this[_0x3ccf78(0x1e8)]={'type':_0x3ccf78(0x1ce),'token':_0xff61df},this;}[_0x1bcda6(0x1eb)](_0x40e13d,_0x5b7f12){const _0x30fdc0=_0x1bcda6;return this['_auth']={'type':_0x30fdc0(0x1d4),'api_key':_0x40e13d,'key_name':_0x5b7f12},this;}[_0x1bcda6(0x1c9)](_0x585fdd,_0xc5808f=_0x1bcda6(0x1d0)){const _0x45c244=_0x1bcda6;return this[_0x45c244(0x1e8)]={'type':_0x45c244(0x1ce),'token':_0x585fdd,'header_name':_0x45c244(0x1cf),'prefix':_0xc5808f},this;}[_0x1bcda6(0x1d6)](_0xcd22a6){const _0x39d332=_0x1bcda6;return this[_0x39d332(0x1e8)]={'type':_0x39d332(0x1d8),'flow':_0xcd22a6},this;}[_0x1bcda6(0x1c0)](_0x36df53){const _0x5df4e0=_0x1bcda6;return this[_0x5df4e0(0x1e8)]=_0x36df53,this;}[_0x1bcda6(0x1d9)](_0x21f51e){const _0x1c9e35=_0x1bcda6;return this[_0x1c9e35(0x1c5)]=_0x21f51e,this;}[_0x1bcda6(0x1d2)](_0x3e426d){return this['_slug']=_0x3e426d,this;}['meta'](_0x3ffee9){return this['_meta']=_0x3ffee9,this;}['endpoint'](_0x3182a1,_0x160cd,_0x2ba59a,_0x50db82){const _0x33d567=_0x1bcda6;return this[_0x33d567(0x1c3)][_0x33d567(0x1c6)]({'name':_0x3182a1,'method':(_0x160cd||_0x33d567(0x1e1))['toUpperCase'](),'path':_0x2ba59a,..._0x50db82}),this;}[_0x1bcda6(0x1e7)](_0x125dbc,_0x1716f5,_0x8a1a96){const _0x180a62=_0x1bcda6;return this[_0x180a62(0x1ee)](_0x125dbc,_0x180a62(0x1e1),_0x1716f5,_0x8a1a96);}['post'](_0x477677,_0x16df04,_0x436233){const _0x5126a2=_0x1bcda6;return this['endpoint'](_0x477677,_0x5126a2(0x1e4),_0x16df04,_0x436233);}[_0x1bcda6(0x1cc)](_0x579502,_0x55fe20,_0x395ca5){const _0x246b58=_0x1bcda6;return this[_0x246b58(0x1ee)](_0x579502,'PUT',_0x55fe20,_0x395ca5);}[_0x1bcda6(0x1d3)](_0x3d4725,_0x2028a7,_0x3a20d8){const _0x4259a3=_0x1bcda6;return this[_0x4259a3(0x1ee)](_0x3d4725,_0x4259a3(0x1dc),_0x2028a7,_0x3a20d8);}[_0x1bcda6(0x1d5)](_0x20ec78,_0x406ad1,_0x2a0c13){const _0x50ade5=_0x1bcda6;return this[_0x50ade5(0x1ee)](_0x20ec78,_0x50ade5(0x1ed),_0x406ad1,_0x2a0c13);}[_0x1bcda6(0x1c8)](){const _0x17f7cb=_0x1bcda6,_0x5dc74a={'_id':this['_id'],'type':this[_0x17f7cb(0x1e9)],'name':this[_0x17f7cb(0x1c1)],'base_url':this[_0x17f7cb(0x1e5)],'auth':this['_auth'],'endpoints':this[_0x17f7cb(0x1c3)]};if(this[_0x17f7cb(0x1d7)])_0x5dc74a['slug']=this[_0x17f7cb(0x1d7)];if(this[_0x17f7cb(0x1c5)])_0x5dc74a[_0x17f7cb(0x1d9)]=this[_0x17f7cb(0x1c5)];return this[_0x17f7cb(0x1df)]&&(_0x5dc74a['meta']={...this[_0x17f7cb(0x1df)],'last_updated':this['_meta']['last_updated']??new Date()['toISOString']()}),_0x5dc74a;}}exports[_0x1bcda6(0x1db)]=ConnectorBuilder;function _0x59e9(_0x55c469,_0x23f5b3){_0x55c469=_0x55c469-0x1c0;const _0x49c027=_0x49c0();let _0x59e9ca=_0x49c027[_0x55c469];if(_0x59e9['JSgTyf']===undefined){var _0x1e2a06=function(_0x5307a8){const _0x1a8ca0='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x26423f='',_0x2c0462='';for(let _0xdd2a11=0x0,_0xff61df,_0x40e13d,_0x5b7f12=0x0;_0x40e13d=_0x5307a8['charAt'](_0x5b7f12++);~_0x40e13d&&(_0xff61df=_0xdd2a11%0x4?_0xff61df*0x40+_0x40e13d:_0x40e13d,_0xdd2a11++%0x4)?_0x26423f+=String['fromCharCode'](0xff&_0xff61df>>(-0x2*_0xdd2a11&0x6)):0x0){_0x40e13d=_0x1a8ca0['indexOf'](_0x40e13d);}for(let _0x585fdd=0x0,_0xc5808f=_0x26423f['length'];_0x585fdd<_0xc5808f;_0x585fdd++){_0x2c0462+='%'+('00'+_0x26423f['charCodeAt'](_0x585fdd)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x2c0462);};_0x59e9['hsduDW']=_0x1e2a06,_0x59e9['hBFEIA']={},_0x59e9['JSgTyf']=!![];}const _0x33e6ad=_0x49c027[0x0],_0x187d3c=_0x55c469+_0x33e6ad,_0x5a975a=_0x59e9['hBFEIA'][_0x187d3c];return!_0x5a975a?(_0x59e9ca=_0x59e9['hsduDW'](_0x59e9ca),_0x59e9['hBFEIA'][_0x187d3c]=_0x59e9ca):_0x59e9ca=_0x5a975a,_0x59e9ca;}function connector(_0x192170,_0x572454){return new ConnectorBuilder(_0x192170,_0x572454);}
|
package/dist/Flosync.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export interface FlosyncConfig {
|
|
|
14
14
|
baseUrl: string;
|
|
15
15
|
apiKey: string;
|
|
16
16
|
};
|
|
17
|
+
tokenRefreshMarginMs?: number;
|
|
18
|
+
tokenFallbackReuseMs?: number;
|
|
17
19
|
}
|
|
18
20
|
export declare class Flosync {
|
|
19
21
|
private config;
|
|
@@ -27,6 +29,7 @@ export declare class Flosync {
|
|
|
27
29
|
workflow(id: string, name?: string): WorkflowBuilder;
|
|
28
30
|
function(id: string, name?: string): FunctionBuilder;
|
|
29
31
|
register(workflowOrWorkflows: IWorkflow | IWorkflow[]): this;
|
|
32
|
+
registerFromClass(workflowClass: any): this;
|
|
30
33
|
run(workflowIdOrName: string, payload?: any, options?: {
|
|
31
34
|
remote?: boolean;
|
|
32
35
|
}): Promise<any>;
|
package/dist/Flosync.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Flosync.d.ts","sourceRoot":"","sources":["../src/Flosync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"Flosync.d.ts","sourceRoot":"","sources":["../src/Flosync.ts"],"names":[],"mappings":"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"}
|