@sanoma-ai/workflows 0.6.0 → 0.7.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 +52 -14
- package/dist/apply.d.ts +2 -1
- package/dist/apply.js +29 -7
- package/dist/apply.js.map +1 -1
- package/dist/approvals.d.ts +27 -2
- package/dist/approvals.js +73 -9
- package/dist/approvals.js.map +1 -1
- package/dist/call.d.ts +3 -2
- package/dist/call.js +38 -30
- package/dist/call.js.map +1 -1
- package/dist/cedar.js +24 -9
- package/dist/cedar.js.map +1 -1
- package/dist/change.d.ts +219 -0
- package/dist/change.js +150 -0
- package/dist/change.js.map +1 -0
- package/dist/chats.d.ts +43 -0
- package/dist/chats.js +48 -0
- package/dist/chats.js.map +1 -0
- package/dist/client.d.ts +52 -6
- package/dist/client.js +88 -47
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +75 -8
- package/dist/config.js +103 -13
- package/dist/config.js.map +1 -1
- package/dist/constructors.d.ts +37 -1
- package/dist/constructors.js +6 -2
- package/dist/constructors.js.map +1 -1
- package/dist/database.d.ts +83 -0
- package/dist/database.js +92 -0
- package/dist/database.js.map +1 -0
- package/dist/db.d.ts +19 -1
- package/dist/db.js +51 -7
- package/dist/db.js.map +1 -1
- package/dist/deadlines.d.ts +91 -0
- package/dist/deadlines.js +165 -0
- package/dist/deadlines.js.map +1 -0
- package/dist/define.d.ts +25 -4
- package/dist/define.js +40 -0
- package/dist/define.js.map +1 -1
- package/dist/demo.d.ts +28 -0
- package/dist/demo.js +72 -0
- package/dist/demo.js.map +1 -0
- package/dist/describe.d.ts +5 -2
- package/dist/describe.js +10 -6
- package/dist/describe.js.map +1 -1
- package/dist/errors.d.ts +10 -1
- package/dist/errors.js +13 -0
- package/dist/errors.js.map +1 -1
- package/dist/fake.d.ts +34 -0
- package/dist/fake.js +134 -1
- package/dist/fake.js.map +1 -1
- package/dist/grid.d.ts +34 -0
- package/dist/grid.js +62 -0
- package/dist/grid.js.map +1 -0
- package/dist/host.d.ts +12 -2
- package/dist/host.js +2 -2
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +15 -8
- package/dist/index.js +10 -5
- package/dist/index.js.map +1 -1
- package/dist/ledger.d.ts +11 -2
- package/dist/ledger.js.map +1 -1
- package/dist/lint.js +2 -2
- package/dist/lint.js.map +1 -1
- package/dist/op.d.ts +21 -2
- package/dist/op.js.map +1 -1
- package/dist/person.d.ts +5 -0
- package/dist/person.js +3 -1
- package/dist/person.js.map +1 -1
- package/dist/policy.d.ts +7 -1
- package/dist/policy.js +2 -1
- package/dist/policy.js.map +1 -1
- package/dist/resource.d.ts +13 -1
- package/dist/resource.js +9 -1
- package/dist/resource.js.map +1 -1
- package/dist/resources.d.ts +2 -0
- package/dist/resources.js +15 -4
- package/dist/resources.js.map +1 -1
- package/dist/run.d.ts +5 -0
- package/dist/run.js.map +1 -1
- package/dist/sandbox.d.ts +38 -10
- package/dist/sandbox.js +66 -35
- package/dist/sandbox.js.map +1 -1
- package/dist/scenario.d.ts +18 -1
- package/dist/scenario.js +77 -27
- package/dist/scenario.js.map +1 -1
- package/dist/scenarios.d.ts +10 -1
- package/dist/scenarios.js +19 -4
- package/dist/scenarios.js.map +1 -1
- package/dist/schedules.d.ts +66 -0
- package/dist/schedules.js +158 -0
- package/dist/schedules.js.map +1 -0
- package/dist/schema.d.ts +6 -0
- package/dist/schema.js +13 -3
- package/dist/schema.js.map +1 -1
- package/dist/shared.d.ts +30 -2
- package/dist/shared.js +42 -4
- package/dist/shared.js.map +1 -1
- package/dist/source.d.ts +185 -2
- package/dist/source.js +9 -0
- package/dist/source.js.map +1 -1
- package/dist/supervisor.d.ts +39 -0
- package/dist/supervisor.js +68 -0
- package/dist/supervisor.js.map +1 -0
- package/dist/version.d.ts +3 -1
- package/dist/version.js +3 -1
- package/dist/version.js.map +1 -1
- package/dist/wakeup-driver.d.ts +128 -0
- package/dist/wakeup-driver.js +216 -0
- package/dist/wakeup-driver.js.map +1 -0
- package/dist/wakeup-service.d.ts +83 -0
- package/dist/wakeup-service.js +415 -0
- package/dist/wakeup-service.js.map +1 -0
- package/dist/wakeup-type.d.ts +43 -0
- package/dist/wakeup-type.js +80 -0
- package/dist/wakeup-type.js.map +1 -0
- package/dist/wakeup.d.ts +77 -0
- package/dist/wakeup.js +78 -0
- package/dist/wakeup.js.map +1 -0
- package/dist/worker.d.ts +39 -1
- package/dist/worker.js +125 -38
- package/dist/worker.js.map +1 -1
- package/oxlint.json +3 -1
- package/package.json +10 -2
- package/src/apply.ts +25 -6
- package/src/approvals.ts +89 -16
- package/src/call.ts +40 -29
- package/src/cedar.ts +24 -6
- package/src/change.ts +222 -0
- package/src/chats.ts +114 -0
- package/src/client.ts +145 -55
- package/src/config.ts +149 -21
- package/src/constructors.ts +8 -2
- package/src/database.ts +152 -0
- package/src/db.ts +58 -9
- package/src/deadlines.ts +228 -0
- package/src/define.ts +71 -4
- package/src/demo.ts +87 -0
- package/src/describe.ts +18 -6
- package/src/errors.ts +14 -0
- package/src/fake.ts +149 -0
- package/src/grid.ts +100 -0
- package/src/host.ts +12 -4
- package/src/index.ts +66 -3
- package/src/ledger.ts +15 -3
- package/src/lint.ts +2 -2
- package/src/op.ts +22 -2
- package/src/person.ts +4 -1
- package/src/policy.ts +7 -1
- package/src/resource.ts +22 -3
- package/src/resources.ts +17 -4
- package/src/run.ts +5 -0
- package/src/sandbox.ts +97 -37
- package/src/scenario.ts +97 -28
- package/src/scenarios.ts +21 -5
- package/src/schedules.ts +206 -0
- package/src/schema.ts +16 -3
- package/src/shared.ts +51 -4
- package/src/source.ts +176 -3
- package/src/supervisor.ts +102 -0
- package/src/version.ts +3 -1
- package/src/wakeup-driver.ts +270 -0
- package/src/wakeup-service.ts +504 -0
- package/src/wakeup-type.ts +90 -0
- package/src/wakeup.ts +139 -0
- package/src/worker.ts +176 -43
package/src/version.ts
CHANGED
|
@@ -16,8 +16,10 @@ export const RUNTIME_VERSION = (
|
|
|
16
16
|
* package release, so a run in flight on the old layout is not replayed against the new one.
|
|
17
17
|
* 2: calls run in program order; a decision event per approval (2026-10-07).
|
|
18
18
|
* 3: a start the policy holds waits on its approval before the body (2026-10-21).
|
|
19
|
+
* 4: a sleep for a duration reads DBOS's clock for its start (2026-10-25).
|
|
20
|
+
* 5: a scheduled run admits its starter and asks the policy in two steps before the body (2026-10-25).
|
|
19
21
|
*/
|
|
20
|
-
export const STEP_LAYOUT =
|
|
22
|
+
export const STEP_LAYOUT = 5;
|
|
21
23
|
|
|
22
24
|
let dbosVersion: string | undefined;
|
|
23
25
|
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type CallContext, defineDriver, DriverError, retryableStatus } from "./op.ts";
|
|
3
|
+
import type { PlanAction } from "./resource.ts";
|
|
4
|
+
import { defineSteps } from "./steps.ts";
|
|
5
|
+
import type { WakeupRegistration } from "./wakeup.ts";
|
|
6
|
+
import { instanceUrl, mintInstanceToken, WAKEUP_TOKEN } from "./wakeup-service.ts";
|
|
7
|
+
import { wakeupOps } from "./wakeup-type.ts";
|
|
8
|
+
|
|
9
|
+
// `sanoma.wakeup`'s operations over a wakeup service (`WakeupApi`): the driver's call it over
|
|
10
|
+
// HTTP (plain `fetch`, the service has no SDK), the fake's (`fakeSanomaWakeup` in `fake.ts`)
|
|
11
|
+
// answer from its state. Both go through `wakeupCalls`, so they plan, post the token and refuse
|
|
12
|
+
// alike. `config.ts` puts the two together as the built-in `sanomaWakeup`.
|
|
13
|
+
|
|
14
|
+
/** An instance as the service shows it (`GET /wakeup/instances/{id}`): the fields read here. */
|
|
15
|
+
const instanceSchema = z.object({
|
|
16
|
+
id: z.string(),
|
|
17
|
+
url: z.string(),
|
|
18
|
+
at: z.string().nullable(),
|
|
19
|
+
lastError: z.string().nullable(),
|
|
20
|
+
});
|
|
21
|
+
export type Instance = z.output<typeof instanceSchema>;
|
|
22
|
+
|
|
23
|
+
/** The wakeup service, as the operations use it. Each call fails with a `DriverError`. */
|
|
24
|
+
export interface WakeupApi {
|
|
25
|
+
/**
|
|
26
|
+
* `POST /wakeup/instances { url, token }` with the account's token: the instance the token
|
|
27
|
+
* names, made, or moved to `url` when the account registered it before.
|
|
28
|
+
*/
|
|
29
|
+
put(url: string, token: string): Promise<{ id: string }>;
|
|
30
|
+
/** `GET /wakeup/instances/{id}` with the account's token; undefined when there is none (404). */
|
|
31
|
+
get(id: string): Promise<Instance | undefined>;
|
|
32
|
+
/** `DELETE /wakeup/instances/{id}` with the account's token; nothing when there is none. */
|
|
33
|
+
remove(id: string): Promise<void>;
|
|
34
|
+
/** `PUT /wakeup/wake { at }` with the instance's token, which names the instance. */
|
|
35
|
+
wake(registration: WakeupRegistration, token: string): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** What the service holds of an instance that registered no wake: a new one's, and one moved to a new URL. */
|
|
39
|
+
export const UNREGISTERED = { at: null, lastError: null } as const;
|
|
40
|
+
|
|
41
|
+
export const fail = (message: string, status: number) =>
|
|
42
|
+
new DriverError(`sanoma.wakeup: ${message}`, { retryable: retryableStatus(status), status });
|
|
43
|
+
|
|
44
|
+
/** The driver's own refusal, before or instead of a call: never retried. */
|
|
45
|
+
const refused = (message: string, vendorCode?: string) =>
|
|
46
|
+
new DriverError(`sanoma.wakeup: ${message}`, { retryable: false, ...(vendorCode && { vendorCode }) });
|
|
47
|
+
|
|
48
|
+
/** A wakeup's state: the service's instance, its URL as `host`. */
|
|
49
|
+
const stateOf = ({ id, url, at, lastError }: Instance) => ({ id, host: url, at, lastError });
|
|
50
|
+
type State = ReturnType<typeof stateOf>;
|
|
51
|
+
|
|
52
|
+
/** What `plan` finds: the action, what it would leave, and the state it planned from. */
|
|
53
|
+
interface Planned {
|
|
54
|
+
action: PlanAction;
|
|
55
|
+
planned?: Record<string, unknown>;
|
|
56
|
+
state?: State;
|
|
57
|
+
unknown: string[];
|
|
58
|
+
replace: string[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** A URL as the service keeps it (`instanceUrl`, the service's own rule), or as given when the service would refuse it. */
|
|
62
|
+
const normalUrl = (url: unknown) => {
|
|
63
|
+
const checked = instanceUrl(url);
|
|
64
|
+
return "url" in checked ? checked.url : String(url);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The wakeup type's operations and `wakeup.register` over `api()`, made on each call. The
|
|
69
|
+
* instance's token, `SANOMA_WAKEUP_TOKEN`, names the instance: `create` and `update` both post it
|
|
70
|
+
* with the host's URL (minted and stored first when none is, as for a data file written by hand),
|
|
71
|
+
* so a new host is the same instance moved, never a replace; `register` sends it.
|
|
72
|
+
*/
|
|
73
|
+
export function wakeupCalls(api: () => WakeupApi) {
|
|
74
|
+
const read = async ({ id }: { id: string }) => {
|
|
75
|
+
const found = await api().get(id);
|
|
76
|
+
return found ? { id, gone: false, state: stateOf(found) } : { id, gone: true };
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* The instance the stored token names, at the desired host: made or moved, its state as a new
|
|
80
|
+
* one's (a repeat after a lost reply keeps the wake the service holds, which the next read shows).
|
|
81
|
+
*/
|
|
82
|
+
const put = async (desired: Record<string, unknown>, { secrets }: CallContext) => {
|
|
83
|
+
const host = normalUrl(desired.host);
|
|
84
|
+
const token = await secrets.ensure(WAKEUP_TOKEN, mintInstanceToken);
|
|
85
|
+
const { id } = await api().put(host, token);
|
|
86
|
+
const state: State = { id, host, ...UNREGISTERED };
|
|
87
|
+
return { id, state };
|
|
88
|
+
};
|
|
89
|
+
const plan = async ({ id, desired }: { id?: string; desired?: Record<string, unknown> }): Promise<Planned> => {
|
|
90
|
+
const state = id === undefined ? undefined : (await read({ id })).state;
|
|
91
|
+
const none = { unknown: [], replace: [] };
|
|
92
|
+
if (!desired) return { action: state ? "delete" : "none", state, ...none };
|
|
93
|
+
const planned = { host: normalUrl(desired.host) };
|
|
94
|
+
if (!state) return { action: "create", planned, unknown: ["id"], replace: [] };
|
|
95
|
+
if (planned.host === state.host) return { action: "none", planned: state, state, ...none };
|
|
96
|
+
// A move forgets the wake registered at the old URL, as the service does.
|
|
97
|
+
return { action: "update", planned: { ...state, ...planned, ...UNREGISTERED }, state, ...none };
|
|
98
|
+
};
|
|
99
|
+
return {
|
|
100
|
+
import: read,
|
|
101
|
+
read,
|
|
102
|
+
plan: async (input: { id?: string; desired?: Record<string, unknown> }) => {
|
|
103
|
+
const { state: _, ...out } = await plan(input);
|
|
104
|
+
return out;
|
|
105
|
+
},
|
|
106
|
+
create: ({ desired }: { desired: Record<string, unknown> }, call: CallContext) => put(desired, call),
|
|
107
|
+
update: async (
|
|
108
|
+
{
|
|
109
|
+
id,
|
|
110
|
+
desired,
|
|
111
|
+
expect,
|
|
112
|
+
}: { id: string; desired: Record<string, unknown>; expect?: { action: PlanAction; replace: string[] } },
|
|
113
|
+
call: CallContext,
|
|
114
|
+
) => {
|
|
115
|
+
const fresh = await plan({ id, desired });
|
|
116
|
+
if (expect && (expect.action !== fresh.action || expect.replace.join() !== fresh.replace.join())) {
|
|
117
|
+
throw refused(`update: the plan changed (${expect.action}, now ${fresh.action})`, "plan_changed");
|
|
118
|
+
}
|
|
119
|
+
if (fresh.state && fresh.action === "none") return { id, state: fresh.state };
|
|
120
|
+
return put(desired, call);
|
|
121
|
+
},
|
|
122
|
+
delete: async ({ id }: { id: string }) => {
|
|
123
|
+
await api().remove(id);
|
|
124
|
+
return { id, gone: true as const };
|
|
125
|
+
},
|
|
126
|
+
register: async (registration: WakeupRegistration, { secrets }: CallContext) => {
|
|
127
|
+
const token = await secrets.get(WAKEUP_TOKEN);
|
|
128
|
+
if (!token)
|
|
129
|
+
throw refused(
|
|
130
|
+
`register: ${WAKEUP_TOKEN} is not set: connect a wakeup service in /setup, then apply the wakeup`,
|
|
131
|
+
);
|
|
132
|
+
await api().wake(registration, token);
|
|
133
|
+
return {};
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** The environment variables the driver reads, on every call. */
|
|
139
|
+
const env = z.object({
|
|
140
|
+
SANOMA_WAKEUP_URL: z.url().optional().describe("the wakeup service's URL: Sanoma's, or your own `sanoma wakeup`"),
|
|
141
|
+
SANOMA_WAKEUP_ACCOUNT_TOKEN: z
|
|
142
|
+
.string()
|
|
143
|
+
.startsWith("swa_", "an account's token starts swa_")
|
|
144
|
+
.optional()
|
|
145
|
+
.describe("an account's token at the wakeup service (swa_…), which creates and deletes the instances"),
|
|
146
|
+
// No check of its form: a host's own wakeup (Cloudflare's alarm) takes this variable too, and
|
|
147
|
+
// the service refuses a token that is not an instance's.
|
|
148
|
+
[WAKEUP_TOKEN]: z
|
|
149
|
+
.string()
|
|
150
|
+
.optional()
|
|
151
|
+
.describe(
|
|
152
|
+
"this instance's token at the wakeup service (swi_…), which /setup mints and the wakeup's create registers",
|
|
153
|
+
),
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
/** The variable `name`, read when an operation is called, or the driver's refusal. */
|
|
157
|
+
function required(name: "SANOMA_WAKEUP_URL" | "SANOMA_WAKEUP_ACCOUNT_TOKEN"): string {
|
|
158
|
+
const value = process.env[name];
|
|
159
|
+
if (!value) throw refused(`${name} is not set (${env.shape[name].description})`);
|
|
160
|
+
return value;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const Posted = z.object({ id: z.string() });
|
|
164
|
+
/** The service's error body. */
|
|
165
|
+
const Problem = z.object({ error: z.string() });
|
|
166
|
+
|
|
167
|
+
const instance = (id: string) => `/wakeup/instances/${encodeURIComponent(id)}`;
|
|
168
|
+
|
|
169
|
+
/** The service at `SANOMA_WAKEUP_URL`, read when an operation is called: each request's failure a `DriverError`. */
|
|
170
|
+
function serviceApi(timeoutMs: number): WakeupApi {
|
|
171
|
+
const base = required("SANOMA_WAKEUP_URL").replace(/\/+$/, "");
|
|
172
|
+
if (!URL.canParse(base)) throw refused("SANOMA_WAKEUP_URL is not a URL");
|
|
173
|
+
const account = () => required("SANOMA_WAKEUP_ACCOUNT_TOKEN");
|
|
174
|
+
/**
|
|
175
|
+
* One request: its reply parsed by `reply` (none: not read), a 404 `undefined` where `absent`
|
|
176
|
+
* says it means no such instance, and any other answer but a 2xx a `DriverError`.
|
|
177
|
+
*/
|
|
178
|
+
const send = async <T>(
|
|
179
|
+
method: "GET" | "POST" | "PUT" | "DELETE",
|
|
180
|
+
path: string,
|
|
181
|
+
token: string,
|
|
182
|
+
{ reply, body, absent = false }: { reply?: z.ZodType<T>; body?: unknown; absent?: boolean } = {},
|
|
183
|
+
): Promise<T | undefined> => {
|
|
184
|
+
const where = `${method} ${path}`;
|
|
185
|
+
let res: Response;
|
|
186
|
+
let text: string;
|
|
187
|
+
try {
|
|
188
|
+
res = await fetch(`${base}${path}`, {
|
|
189
|
+
method,
|
|
190
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
191
|
+
// A redirect is no answer of the service's, and would carry the token elsewhere.
|
|
192
|
+
redirect: "manual",
|
|
193
|
+
headers: {
|
|
194
|
+
authorization: `Bearer ${token}`,
|
|
195
|
+
accept: "application/json",
|
|
196
|
+
...(body !== undefined && { "content-type": "application/json" }),
|
|
197
|
+
},
|
|
198
|
+
...(body !== undefined && { body: JSON.stringify(body) }),
|
|
199
|
+
});
|
|
200
|
+
text = await res.text();
|
|
201
|
+
} catch (cause) {
|
|
202
|
+
// No answer: the request may or may not have reached the service.
|
|
203
|
+
const why = (cause as Error).name === "TimeoutError" ? `no reply in ${timeoutMs} ms` : "the request failed";
|
|
204
|
+
throw new DriverError(`sanoma.wakeup: ${where}: ${why}`, { retryable: true, cause });
|
|
205
|
+
}
|
|
206
|
+
const { status } = res;
|
|
207
|
+
let json: unknown;
|
|
208
|
+
try {
|
|
209
|
+
json = text ? JSON.parse(text) : undefined;
|
|
210
|
+
} catch {
|
|
211
|
+
// A proxy's HTML page, say: no body to read.
|
|
212
|
+
}
|
|
213
|
+
// The service's own 404 (its `{ error }`), not another server's at that URL.
|
|
214
|
+
if (status === 404 && absent && Problem.safeParse(json).success) return undefined;
|
|
215
|
+
if (status < 200 || status >= 300)
|
|
216
|
+
throw fail(`${where}: ${Problem.safeParse(json).data?.error ?? `HTTP ${status}`}`, status);
|
|
217
|
+
if (!reply) return undefined;
|
|
218
|
+
const parsed = reply.safeParse(json);
|
|
219
|
+
if (!parsed.success) {
|
|
220
|
+
throw new DriverError(
|
|
221
|
+
`sanoma.wakeup: ${where}: the reply is not a wakeup service's; is SANOMA_WAKEUP_URL right?`,
|
|
222
|
+
{
|
|
223
|
+
retryable: false,
|
|
224
|
+
status,
|
|
225
|
+
cause: parsed.error,
|
|
226
|
+
},
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
return parsed.data;
|
|
230
|
+
};
|
|
231
|
+
return {
|
|
232
|
+
put: async (url, token) => {
|
|
233
|
+
try {
|
|
234
|
+
return (await send("POST", "/wakeup/instances", account(), { reply: Posted, body: { url, token } }))!;
|
|
235
|
+
} catch (error) {
|
|
236
|
+
// Another account's instance's token: the advice is the company's, so it is here, not the service's.
|
|
237
|
+
if (!(error instanceof DriverError) || error.status !== 409) throw error;
|
|
238
|
+
throw new DriverError(
|
|
239
|
+
`${error.message}: a new account needs a new instance token (store the new account's token in /setup, which mints one)`,
|
|
240
|
+
{ retryable: false, status: 409, cause: error },
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
get: (id) => send("GET", instance(id), account(), { reply: instanceSchema, absent: true }),
|
|
245
|
+
async remove(id) {
|
|
246
|
+
await send("DELETE", instance(id), account(), { absent: true });
|
|
247
|
+
},
|
|
248
|
+
async wake({ at }, token) {
|
|
249
|
+
await send("PUT", "/wakeup/wake", token, { body: { at } });
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* The real wakeup service at `SANOMA_WAKEUP_URL`: instances created, read and deleted with
|
|
256
|
+
* `SANOMA_WAKEUP_ACCOUNT_TOKEN`, each named by its `SANOMA_WAKEUP_TOKEN` (which `create` and
|
|
257
|
+
* `register` send). `timeoutMs` is how long one request may take: 10 s by default.
|
|
258
|
+
*/
|
|
259
|
+
export function sanomaWakeupDriver({ timeoutMs = 10_000 }: { timeoutMs?: number } = {}) {
|
|
260
|
+
return defineDriver(wakeupOps, { wakeup: wakeupCalls(() => serviceApi(timeoutMs)) }, { env });
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** The wakeup's step for Gherkin tests: an instance at the service before the run, named `wakeup` (`$wakeup.id`). */
|
|
264
|
+
export const wakeupSteps = defineSteps("sanoma", ({ given }) => {
|
|
265
|
+
given("a sanoma wakeup for {string}", (host: string) => ({
|
|
266
|
+
op: wakeupOps.wakeup.create,
|
|
267
|
+
input: { desired: { host } },
|
|
268
|
+
as: "wakeup",
|
|
269
|
+
}));
|
|
270
|
+
});
|