@sanoma-ai/workflows 0.10.2 → 0.10.3

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 (118) hide show
  1. package/README.md +43 -11
  2. package/dist/apply.js +1 -1
  3. package/dist/apply.js.map +1 -1
  4. package/dist/call.d.ts +5 -2
  5. package/dist/call.js +65 -20
  6. package/dist/call.js.map +1 -1
  7. package/dist/change.d.ts +5 -6
  8. package/dist/change.js +6 -9
  9. package/dist/change.js.map +1 -1
  10. package/dist/client.d.ts +18 -0
  11. package/dist/client.js +35 -3
  12. package/dist/client.js.map +1 -1
  13. package/dist/config.d.ts +42 -4
  14. package/dist/config.js +71 -10
  15. package/dist/config.js.map +1 -1
  16. package/dist/db.d.ts +2 -0
  17. package/dist/db.js +7 -0
  18. package/dist/db.js.map +1 -1
  19. package/dist/deadlines.d.ts +26 -13
  20. package/dist/deadlines.js +50 -11
  21. package/dist/deadlines.js.map +1 -1
  22. package/dist/define.d.ts +22 -0
  23. package/dist/define.js +2 -0
  24. package/dist/define.js.map +1 -1
  25. package/dist/deployment-key.d.ts +20 -1
  26. package/dist/deployment-key.js +28 -3
  27. package/dist/deployment-key.js.map +1 -1
  28. package/dist/describe.js +28 -1
  29. package/dist/describe.js.map +1 -1
  30. package/dist/description.d.ts +3 -0
  31. package/dist/description.js +2 -0
  32. package/dist/description.js.map +1 -1
  33. package/dist/host.d.ts +13 -4
  34. package/dist/host.js +14 -5
  35. package/dist/host.js.map +1 -1
  36. package/dist/index.d.ts +10 -6
  37. package/dist/index.js +8 -5
  38. package/dist/index.js.map +1 -1
  39. package/dist/ledger.d.ts +30 -1
  40. package/dist/ledger.js +31 -0
  41. package/dist/ledger.js.map +1 -1
  42. package/dist/login-code.d.ts +17 -0
  43. package/dist/login-code.js +32 -0
  44. package/dist/login-code.js.map +1 -0
  45. package/dist/op.d.ts +78 -10
  46. package/dist/op.js +20 -0
  47. package/dist/op.js.map +1 -1
  48. package/dist/outline.js +3 -3
  49. package/dist/outline.js.map +1 -1
  50. package/dist/resource.d.ts +6 -4
  51. package/dist/resource.js +8 -5
  52. package/dist/resource.js.map +1 -1
  53. package/dist/resources.d.ts +4 -2
  54. package/dist/resources.js +15 -6
  55. package/dist/resources.js.map +1 -1
  56. package/dist/run.d.ts +3 -0
  57. package/dist/run.js.map +1 -1
  58. package/dist/runtime-ops.d.ts +123 -0
  59. package/dist/runtime-ops.js +222 -0
  60. package/dist/runtime-ops.js.map +1 -0
  61. package/dist/seal.d.ts +27 -0
  62. package/dist/seal.js +76 -0
  63. package/dist/seal.js.map +1 -0
  64. package/dist/secret-store.d.ts +6 -4
  65. package/dist/secret-store.js +11 -6
  66. package/dist/secret-store.js.map +1 -1
  67. package/dist/secret.d.ts +7 -0
  68. package/dist/secret.js +13 -1
  69. package/dist/secret.js.map +1 -1
  70. package/dist/shared.d.ts +15 -4
  71. package/dist/shared.js +27 -7
  72. package/dist/shared.js.map +1 -1
  73. package/dist/ssh-key.d.ts +11 -0
  74. package/dist/ssh-key.js +44 -0
  75. package/dist/ssh-key.js.map +1 -0
  76. package/dist/tokens.d.ts +35 -0
  77. package/dist/tokens.js +71 -0
  78. package/dist/tokens.js.map +1 -0
  79. package/dist/wakeup-service.js +2 -7
  80. package/dist/wakeup-service.js.map +1 -1
  81. package/dist/wakeup.d.ts +18 -0
  82. package/dist/wakeup.js +15 -0
  83. package/dist/wakeup.js.map +1 -1
  84. package/dist/worker.d.ts +2 -2
  85. package/dist/worker.js +17 -9
  86. package/dist/worker.js.map +1 -1
  87. package/oxlint.json +1 -1
  88. package/package.json +1 -1
  89. package/src/apply.ts +1 -1
  90. package/src/call.ts +88 -18
  91. package/src/change.ts +9 -20
  92. package/src/client.ts +48 -7
  93. package/src/config.ts +111 -17
  94. package/src/db.ts +8 -1
  95. package/src/deadlines.ts +66 -20
  96. package/src/define.ts +25 -0
  97. package/src/deployment-key.ts +34 -8
  98. package/src/describe.ts +39 -2
  99. package/src/description.ts +2 -0
  100. package/src/host.ts +22 -6
  101. package/src/index.ts +18 -1
  102. package/src/ledger.ts +52 -1
  103. package/src/login-code.ts +52 -0
  104. package/src/op.ts +84 -11
  105. package/src/outline.ts +3 -3
  106. package/src/resource.ts +8 -5
  107. package/src/resources.ts +13 -4
  108. package/src/run.ts +3 -0
  109. package/src/runtime-ops.ts +234 -0
  110. package/src/seal.ts +95 -0
  111. package/src/secret-store.ts +12 -9
  112. package/src/secret.ts +16 -1
  113. package/src/shared.ts +31 -7
  114. package/src/ssh-key.ts +48 -0
  115. package/src/tokens.ts +100 -0
  116. package/src/wakeup-service.ts +2 -8
  117. package/src/wakeup.ts +21 -0
  118. package/src/worker.ts +29 -11
@@ -1,7 +1,7 @@
1
- import { createHash, randomUUID } from "node:crypto";
1
+ import { randomUUID } from "node:crypto";
2
2
  import { connectMaking, createTable, type Db, openPool } from "./db.ts";
3
3
  import { info, warn } from "./log.ts";
4
- import { generateSecret } from "./secret.ts";
4
+ import { generateSecret, tokenHash as hashOf } from "./secret.ts";
5
5
  import { errorMessage } from "./shared.ts";
6
6
 
7
7
  /*
@@ -110,12 +110,6 @@ export const WAKEUP_TOKEN = "SANOMA_WAKEUP_TOKEN";
110
110
  /** An instance token the service takes: `swi_`, then at least as many base64url characters as a minted one has. */
111
111
  export const INSTANCE_TOKEN = /^swi_[\w-]{43,256}$/;
112
112
 
113
- /**
114
- * The token's sha256, which is all the tables keep: a token is found by its hash (a unique index),
115
- * so the secret itself is never compared, and a guess's timing says nothing an attacker can steer.
116
- */
117
- const hashOf = (token: string) => createHash("sha256").update(token).digest("hex");
118
-
119
113
  /** A date-time as `Date.toISOString` writes one, or with an offset: four-digit years only. */
120
114
  const ISO_DATE_TIME = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?(Z|[+-]\d{2}:\d{2})$/;
121
115
 
package/src/wakeup.ts CHANGED
@@ -11,6 +11,27 @@ import type { DeclaredResource } from "./resources.ts";
11
11
  // replaced registration starts a worker that finds nothing to do and stops again. The contract is
12
12
  // here; the worker's side is `deadlines.ts`.
13
13
 
14
+ /**
15
+ * A host's own wake route, where a wakeup that the host holds itself takes a registration: `PUT`
16
+ * with `WakeupRegistration` and the instance's `SANOMA_WAKEUP_TOKEN` as a bearer, answered 204.
17
+ * The Cloudflare Worker serves it; a hosted instance that declares no wakeup registers there
18
+ * (`EnvWakeup`).
19
+ */
20
+ export const WAKE_PATH = "/__sanoma/wake";
21
+
22
+ /**
23
+ * The wakeup a hosted instance's environment names, when its data files declare none: its own
24
+ * host's URL (`SANOMA_HOST_URL`) and its token (`SANOMA_WAKEUP_TOKEN`), which the host gives the
25
+ * runtime. The worker registers each deadline at `{url}{WAKE_PATH}` itself, with no driver.
26
+ */
27
+ export interface EnvWakeup {
28
+ url: string;
29
+ token: string;
30
+ }
31
+
32
+ /** Where a host gives the runtime its own URL: the Cloudflare Worker's plain-text binding. */
33
+ export const HOST_URL = "SANOMA_HOST_URL";
34
+
14
35
  /** What a wakeup's state says of it. */
15
36
  export interface WakeupOutputs {
16
37
  healthy: boolean;
package/src/worker.ts CHANGED
@@ -2,10 +2,19 @@ import { DBOS, type ScheduledWorkflowFn } from "@dbos-inc/dbos-sdk";
2
2
  import { awaitApproval } from "./approvals.ts";
3
3
  import { buildCtx, decideStart, isInfrastructureError, startHoldOf } from "./call.ts";
4
4
  import { dbosStatusesOf } from "./client.ts";
5
- import { declaredWakeup, type ResolvedConfig, resolveConfig, type SanomaConfig } from "./config.ts";
5
+ import { declaredWakeup, envWakeup, type ResolvedConfig, resolveConfig, type SanomaConfig } from "./config.ts";
6
6
  import { configuredSecrets } from "./credentials.ts";
7
7
  import { type Db, DBOS_DATABASE_OPTIONS, lazyPool, prepareDatabase, stampVersion } from "./db.ts";
8
- import { deadlineRegistry, type Deadlines, idle, onOwnQueue, type SelfStop, stopWhenIdle } from "./deadlines.ts";
8
+ import {
9
+ deadlineRegistry,
10
+ type Deadlines,
11
+ declaredRegister,
12
+ envRegister,
13
+ idle,
14
+ onOwnQueue,
15
+ type SelfStop,
16
+ stopWhenIdle,
17
+ } from "./deadlines.ts";
9
18
  import type { WorkflowDefinition } from "./define.ts";
10
19
  import { credentialsOf, unconfigured, unconfiguredSummary } from "./env.ts";
11
20
  import { errorInfo, parseOrThrow, PolicyDeniedError } from "./errors.ts";
@@ -79,10 +88,10 @@ export interface WorkerOptions {
79
88
  * and other short waits never stop it.
80
89
  */
81
90
  idleHorizonMs?: number;
82
- /** How often a worker whose data files declare a wakeup checks whether it may stop: every 15 s by default. */
91
+ /** How often a worker with a wakeup (declared, or named by its environment: `envWakeup`) checks whether it may stop: every 15 s by default. */
83
92
  stopCheckMs?: number;
84
93
  /**
85
- * Given, and with a wakeup declared, the worker stops itself when idle (`stopCheckMs`); without
94
+ * Given, and with a wakeup (declared, or named by the environment), the worker stops itself when idle (`stopCheckMs`); without
86
95
  * it, nobody would start another, so it never does. Called once the worker has stopped itself, with the deadline it registered with the
87
96
  * wakeup (null for none): a supervisor starts a fresh worker on the next write or wake, since a
88
97
  * stopped one cannot launch again.
@@ -108,9 +117,12 @@ export async function startWorker(config: SanomaConfig, options: WorkerOptions =
108
117
  setLogLevel(options.logLevel);
109
118
  // Read once, when it matters: a worker stops itself, registering its next deadline there, only
110
119
  // when its caller hears it stop (`onStopped`).
111
- const wakeup = options.onStopped || config.wakeup === true ? await declaredWakeup(config) : undefined;
120
+ const declared = options.onStopped || config.wakeup === true ? await declaredWakeup(config) : undefined;
121
+ // Else the one a host Sanoma Cloud made names in the environment, which an instance running the
122
+ // wakeup service ignores: it must not stop.
123
+ const wakeup = declared ?? (options.onStopped && config.wakeup !== true ? envWakeup(process.env) : undefined);
112
124
  // The wakeup service wakes the instances that stop: one that runs it must not stop with its worker.
113
- if (config.wakeup === true && wakeup) {
125
+ if (config.wakeup === true && declared) {
114
126
  throw new Error(
115
127
  "The config runs the wakeup service (`wakeup: true`) and declares a wakeup of its own: an instance that runs the service must not stop, so leave out one of them",
116
128
  );
@@ -130,6 +142,7 @@ export async function startWorker(config: SanomaConfig, options: WorkerOptions =
130
142
  ...(resolved.scenarios && { scenarios: resolved.scenarios }),
131
143
  ...(options.source && { source: options.source }),
132
144
  types: resolved.types,
145
+ declaredObjects: resolved.declaredObjects,
133
146
  collections: resolved.collections,
134
147
  steps: resolved.steps,
135
148
  policy: resolved.policy,
@@ -232,14 +245,19 @@ export async function startWorker(config: SanomaConfig, options: WorkerOptions =
232
245
  warn(`the policies do not hold, so every call and start is refused:\n ${resolved.policyProblems.join("\n ")}`);
233
246
  }
234
247
  if (wakeup && options.onStopped) {
235
- const registerId = wakeup.type.wakeup!.register;
236
248
  stopRule = stopWhenIdle({
237
249
  // The row is one more of the runtime's tables: the secrets' pool, on the same database.
238
250
  db: () => secrets.db(),
239
- wakeup: wakeup.binding ?? wakeup.id,
240
- // `resolveConfig` checked the operation is the config's (`checkWakeupOps`).
241
- register: { op: resolved.ops.get(registerId)!, fn: resolved.drivers.get(registerId) },
242
- secrets: state.secrets,
251
+ register:
252
+ "type" in wakeup
253
+ ? declaredRegister({
254
+ // `resolveConfig` checked the operation is the config's (`checkWakeupOps`).
255
+ op: resolved.ops.get(wakeup.type.wakeup!.register)!,
256
+ fn: resolved.drivers.get(wakeup.type.wakeup!.register),
257
+ secrets: state.secrets,
258
+ wakeup: wakeup.binding ?? wakeup.id,
259
+ })
260
+ : envRegister(wakeup),
243
261
  deadlines: state.deadlines,
244
262
  idle: () => worker.idle(),
245
263
  everyMs: options.stopCheckMs ?? 15_000,