@sanoma-ai/workflows 0.10.12 → 0.11.1
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 +9 -4
- package/dist/config.d.ts +18 -14
- package/dist/config.js +25 -12
- package/dist/config.js.map +1 -1
- package/dist/describe.js +4 -4
- package/dist/describe.js.map +1 -1
- package/dist/description.d.ts +1 -0
- package/dist/description.js +2 -2
- package/dist/description.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/login-code.d.ts +10 -4
- package/dist/login-code.js +14 -9
- package/dist/login-code.js.map +1 -1
- package/dist/op.d.ts +5 -0
- package/dist/op.js +15 -0
- package/dist/op.js.map +1 -1
- package/dist/scenario.js +12 -0
- package/dist/scenario.js.map +1 -1
- package/dist/wakeup.d.ts +1 -1
- package/dist/wakeup.js +1 -15
- package/dist/wakeup.js.map +1 -1
- package/package.json +1 -1
- package/src/config.ts +52 -17
- package/src/describe.ts +4 -4
- package/src/description.ts +2 -2
- package/src/index.ts +1 -0
- package/src/login-code.ts +19 -13
- package/src/op.ts +15 -0
- package/src/scenario.ts +12 -0
- package/src/wakeup.ts +1 -16
package/src/login-code.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { defineWorkflow, type WorkflowDefinition } from "./define.ts";
|
|
3
|
+
import type { Login } from "./config.ts";
|
|
3
4
|
import type { Op } from "./op.ts";
|
|
4
5
|
import { openLoginCode } from "./seal.ts";
|
|
5
6
|
import type { LOGIN_CODE_WORKFLOW } from "./shared.ts";
|
|
6
7
|
|
|
7
8
|
// The built-in `login-code` workflow: one mail with a sign-in code, through the config's
|
|
8
|
-
// `login.sender`. The app starts it as the runtime's agent `login`
|
|
9
|
-
// sealed (`sealLoginCode`), so neither the run's input nor the
|
|
10
|
-
// recorded with its body as `<text>` (`recorded` in `call.ts`). It
|
|
11
|
-
// the call goes through `ctx`, under the policy, in the ledger.
|
|
9
|
+
// `login.sender`, from its `login.from`. The app starts it as the runtime's agent `login`
|
|
10
|
+
// (`LOGIN_AGENT`) with the code sealed (`sealLoginCode`), so neither the run's input nor the
|
|
11
|
+
// ledger shows it: the call is recorded with its body as `<text>` (`recorded` in `call.ts`). It
|
|
12
|
+
// is a workflow like any other: the call goes through `ctx`, under the policy, in the ledger.
|
|
12
13
|
|
|
13
14
|
/** An operation on `ctx`. */
|
|
14
15
|
type OpCall = (input: unknown) => Promise<unknown>;
|
|
@@ -16,17 +17,26 @@ type OpCall = (input: unknown) => Promise<unknown>;
|
|
|
16
17
|
/** The workflow's input: to whom, and the code, sealed. */
|
|
17
18
|
const LoginCodeInput = z.object({ to: z.string().min(1), code: z.string().min(1) });
|
|
18
19
|
|
|
19
|
-
/**
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* The mail a sign-in code is sent as, from `from`, saying it is `company`'s: the workflow's, and,
|
|
22
|
+
* with a stand-in `to` and `text`, what is checked before any is sent (a sender must take it, and
|
|
23
|
+
* the policy is asked about it).
|
|
24
|
+
*/
|
|
25
|
+
export const codeMail = (from: string, company: string, to = "person@example.com", text = "<text>") => ({
|
|
26
|
+
from,
|
|
27
|
+
to,
|
|
28
|
+
subject: `Your ${company} sign-in code`,
|
|
29
|
+
text,
|
|
30
|
+
});
|
|
21
31
|
|
|
22
32
|
/** The mail's text: the code, what it is for, and how long it lasts; nothing else, and no link. */
|
|
23
33
|
const textOf = (company: string, code: string) =>
|
|
24
34
|
`Your sign-in code for ${company} is ${code}.\n\n` +
|
|
25
35
|
"It works once, for five minutes. If you did not ask for it, ignore this mail.\n";
|
|
26
36
|
|
|
27
|
-
/** The login-code workflow for a config: it sends through `sender`, saying it is `company`'s. */
|
|
37
|
+
/** The login-code workflow for a config: it sends through `sender`, from `from`, saying it is `company`'s. */
|
|
28
38
|
export function loginCodeWorkflow(
|
|
29
|
-
sender:
|
|
39
|
+
{ sender, from }: Login,
|
|
30
40
|
company: string,
|
|
31
41
|
): WorkflowDefinition<readonly Op[], typeof LoginCodeInput> {
|
|
32
42
|
return defineWorkflow({
|
|
@@ -38,11 +48,7 @@ export function loginCodeWorkflow(
|
|
|
38
48
|
uses: [sender],
|
|
39
49
|
run: async (ctx, input) => {
|
|
40
50
|
const { vendor, resource, name } = sender;
|
|
41
|
-
const mail =
|
|
42
|
-
to: input.to,
|
|
43
|
-
subject: `Your ${company} sign-in code`,
|
|
44
|
-
text: textOf(company, openLoginCode(input.code, input.to)),
|
|
45
|
-
};
|
|
51
|
+
const mail = codeMail(from, company, input.to, textOf(company, openLoginCode(input.code, input.to)));
|
|
46
52
|
// `ctx` called directly, so the outline places the call.
|
|
47
53
|
return (ctx as unknown as Record<string, Record<string, Record<string, OpCall>>>)[vendor]![resource]![name]!(
|
|
48
54
|
mail,
|
package/src/op.ts
CHANGED
|
@@ -530,6 +530,21 @@ export function isOp(x: unknown): x is Op {
|
|
|
530
530
|
return typeof x === "object" && x !== null && (x as Op).kind === "op";
|
|
531
531
|
}
|
|
532
532
|
|
|
533
|
+
/**
|
|
534
|
+
* True when `op`'s input takes `sample` and keeps each of its fields: one that strips a field
|
|
535
|
+
* takes none of it. A wakeup's `register` and the config's `login.sender` are checked with it.
|
|
536
|
+
*/
|
|
537
|
+
export function keepsInput(op: Op, sample: Record<string, unknown>): boolean {
|
|
538
|
+
try {
|
|
539
|
+
const parsed = op.input.safeParse(sample);
|
|
540
|
+
const data = parsed.data as Record<string, unknown> | undefined;
|
|
541
|
+
return parsed.success && Object.keys(sample).every((key) => data?.[key] !== undefined);
|
|
542
|
+
} catch {
|
|
543
|
+
// An async refinement, which a sync parse cannot run: no input the runtime can check.
|
|
544
|
+
return false;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
533
548
|
/**
|
|
534
549
|
* What a driver for a connector must implement: every operation, by resource and name. Each
|
|
535
550
|
* function gets the input as the operation's schema parses it and returns what its output
|
package/src/scenario.ts
CHANGED
|
@@ -515,6 +515,18 @@ function rulesOf(scope: Scope): { rules: Rule[]; parameterTypes: ParameterTypeSp
|
|
|
515
515
|
return [c ? { op: c.delete.id, input: { key: name } } : { ...object(resource, name), gone: true }];
|
|
516
516
|
},
|
|
517
517
|
},
|
|
518
|
+
{
|
|
519
|
+
kind: "when",
|
|
520
|
+
source: "{workflow} runs with exactly",
|
|
521
|
+
takes: TAKES_OBJECT,
|
|
522
|
+
read: ([name], pickle) => {
|
|
523
|
+
const { input } = workflowOf(name);
|
|
524
|
+
return {
|
|
525
|
+
workflow: name as string,
|
|
526
|
+
input: input.parse(fieldsFrom(pickle, input, inputOf(name))),
|
|
527
|
+
};
|
|
528
|
+
},
|
|
529
|
+
},
|
|
518
530
|
{
|
|
519
531
|
kind: "when",
|
|
520
532
|
source: "{workflow} runs with",
|
package/src/wakeup.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { z } from "zod";
|
|
2
2
|
import { hasPath } from "./host.ts";
|
|
3
|
-
import type
|
|
3
|
+
import { keepsInput, type Op } from "./op.ts";
|
|
4
4
|
import type { References, Resource } from "./resource.ts";
|
|
5
5
|
import type { DeclaredResource } from "./resources.ts";
|
|
6
6
|
|
|
@@ -110,21 +110,6 @@ export function checkWakeup(
|
|
|
110
110
|
/** A registration every register operation must take: the input `checkWakeupOps` tries. */
|
|
111
111
|
const SAMPLE: WakeupRegistration = { at: new Date(0).toISOString() };
|
|
112
112
|
|
|
113
|
-
/**
|
|
114
|
-
* True when `op`'s input takes `sample` and keeps each of its fields: one that strips a field
|
|
115
|
-
* takes none of it. A wakeup's `register` is checked with it.
|
|
116
|
-
*/
|
|
117
|
-
function keepsInput(op: Op, sample: Record<string, unknown>): boolean {
|
|
118
|
-
try {
|
|
119
|
-
const parsed = op.input.safeParse(sample);
|
|
120
|
-
const data = parsed.data as Record<string, unknown> | undefined;
|
|
121
|
-
return parsed.success && Object.keys(sample).every((key) => data?.[key] !== undefined);
|
|
122
|
-
} catch {
|
|
123
|
-
// An async refinement, which a sync parse cannot run: no input the runtime can check.
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
113
|
/** The host types a wakeup type may name: its `inputs.host` reference's. */
|
|
129
114
|
export const wakeupHostTypes = (type: Pick<Resource, "fields" | "wakeup">): string[] =>
|
|
130
115
|
type.wakeup ? [type.fields.references?.[type.wakeup.inputs.host]?.type ?? []].flat() : [];
|