@sanoma-ai/workflows 0.0.0-stage → 0.2.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 (176) hide show
  1. package/LICENSE +105 -0
  2. package/README.md +544 -2
  3. package/dist/apply-steps.d.ts +6 -0
  4. package/dist/apply-steps.js +93 -0
  5. package/dist/apply-steps.js.map +1 -0
  6. package/dist/apply.d.ts +197 -0
  7. package/dist/apply.js +470 -0
  8. package/dist/apply.js.map +1 -0
  9. package/dist/approvals.d.ts +46 -0
  10. package/dist/approvals.js +111 -0
  11. package/dist/approvals.js.map +1 -0
  12. package/dist/ast.d.ts +57 -0
  13. package/dist/ast.js +81 -0
  14. package/dist/ast.js.map +1 -0
  15. package/dist/call.d.ts +37 -0
  16. package/dist/call.js +417 -0
  17. package/dist/call.js.map +1 -0
  18. package/dist/client.d.ts +203 -0
  19. package/dist/client.js +457 -0
  20. package/dist/client.js.map +1 -0
  21. package/dist/config.d.ts +126 -0
  22. package/dist/config.js +264 -0
  23. package/dist/config.js.map +1 -0
  24. package/dist/credentials.d.ts +55 -0
  25. package/dist/credentials.js +261 -0
  26. package/dist/credentials.js.map +1 -0
  27. package/dist/cucumber.d.ts +64 -0
  28. package/dist/cucumber.js +158 -0
  29. package/dist/cucumber.js.map +1 -0
  30. package/dist/datafile.d.ts +71 -0
  31. package/dist/datafile.js +380 -0
  32. package/dist/datafile.js.map +1 -0
  33. package/dist/db.d.ts +58 -0
  34. package/dist/db.js +179 -0
  35. package/dist/db.js.map +1 -0
  36. package/dist/define.d.ts +205 -0
  37. package/dist/define.js +103 -0
  38. package/dist/define.js.map +1 -0
  39. package/dist/describe.d.ts +141 -0
  40. package/dist/describe.js +135 -0
  41. package/dist/describe.js.map +1 -0
  42. package/dist/drift.d.ts +127 -0
  43. package/dist/drift.js +167 -0
  44. package/dist/drift.js.map +1 -0
  45. package/dist/env.d.ts +49 -0
  46. package/dist/env.js +83 -0
  47. package/dist/env.js.map +1 -0
  48. package/dist/errors.d.ts +88 -0
  49. package/dist/errors.js +148 -0
  50. package/dist/errors.js.map +1 -0
  51. package/dist/expectations.d.ts +26 -0
  52. package/dist/expectations.js +162 -0
  53. package/dist/expectations.js.map +1 -0
  54. package/dist/fake.d.ts +55 -0
  55. package/dist/fake.js +87 -0
  56. package/dist/fake.js.map +1 -0
  57. package/dist/fill.d.ts +9 -0
  58. package/dist/fill.js +17 -0
  59. package/dist/fill.js.map +1 -0
  60. package/dist/host.d.ts +56 -0
  61. package/dist/host.js +45 -0
  62. package/dist/host.js.map +1 -0
  63. package/dist/index.d.ts +23 -0
  64. package/dist/index.js +29 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/ledger.d.ts +217 -0
  67. package/dist/ledger.js +251 -0
  68. package/dist/ledger.js.map +1 -0
  69. package/dist/lint.d.ts +32 -0
  70. package/dist/lint.js +168 -0
  71. package/dist/lint.js.map +1 -0
  72. package/dist/lockfile.d.ts +36 -0
  73. package/dist/lockfile.js +69 -0
  74. package/dist/lockfile.js.map +1 -0
  75. package/dist/log.d.ts +24 -0
  76. package/dist/log.js +83 -0
  77. package/dist/log.js.map +1 -0
  78. package/dist/op.d.ts +260 -0
  79. package/dist/op.js +188 -0
  80. package/dist/op.js.map +1 -0
  81. package/dist/outline.d.ts +179 -0
  82. package/dist/outline.js +506 -0
  83. package/dist/outline.js.map +1 -0
  84. package/dist/paths.d.ts +11 -0
  85. package/dist/paths.js +30 -0
  86. package/dist/paths.js.map +1 -0
  87. package/dist/plugin.d.ts +28 -0
  88. package/dist/plugin.js +25 -0
  89. package/dist/plugin.js.map +1 -0
  90. package/dist/policy.d.ts +114 -0
  91. package/dist/policy.js +60 -0
  92. package/dist/policy.js.map +1 -0
  93. package/dist/resource.d.ts +378 -0
  94. package/dist/resource.js +430 -0
  95. package/dist/resource.js.map +1 -0
  96. package/dist/resources.d.ts +77 -0
  97. package/dist/resources.js +244 -0
  98. package/dist/resources.js.map +1 -0
  99. package/dist/run.d.ts +101 -0
  100. package/dist/run.js +7 -0
  101. package/dist/run.js.map +1 -0
  102. package/dist/sandbox.d.ts +33 -0
  103. package/dist/sandbox.js +127 -0
  104. package/dist/sandbox.js.map +1 -0
  105. package/dist/scenario.d.ts +221 -0
  106. package/dist/scenario.js +1104 -0
  107. package/dist/scenario.js.map +1 -0
  108. package/dist/scenarios.d.ts +55 -0
  109. package/dist/scenarios.js +71 -0
  110. package/dist/scenarios.js.map +1 -0
  111. package/dist/secret.d.ts +42 -0
  112. package/dist/secret.js +58 -0
  113. package/dist/secret.js.map +1 -0
  114. package/dist/shared.d.ts +69 -0
  115. package/dist/shared.js +84 -0
  116. package/dist/shared.js.map +1 -0
  117. package/dist/source.d.ts +58 -0
  118. package/dist/source.js +54 -0
  119. package/dist/source.js.map +1 -0
  120. package/dist/steps.d.ts +76 -0
  121. package/dist/steps.js +23 -0
  122. package/dist/steps.js.map +1 -0
  123. package/dist/version.d.ts +29 -0
  124. package/dist/version.js +76 -0
  125. package/dist/version.js.map +1 -0
  126. package/dist/worker.d.ts +37 -0
  127. package/dist/worker.js +228 -0
  128. package/dist/worker.js.map +1 -0
  129. package/dist/writer.d.ts +35 -0
  130. package/dist/writer.js +201 -0
  131. package/dist/writer.js.map +1 -0
  132. package/oxlint.json +128 -0
  133. package/package.json +96 -3
  134. package/src/apply-steps.ts +110 -0
  135. package/src/apply.ts +626 -0
  136. package/src/approvals.ts +157 -0
  137. package/src/ast.ts +115 -0
  138. package/src/call.ts +483 -0
  139. package/src/client.ts +604 -0
  140. package/src/config.ts +404 -0
  141. package/src/credentials.ts +296 -0
  142. package/src/cucumber.ts +230 -0
  143. package/src/datafile.ts +481 -0
  144. package/src/db.ts +197 -0
  145. package/src/define.ts +285 -0
  146. package/src/describe.ts +282 -0
  147. package/src/drift.ts +259 -0
  148. package/src/env.ts +122 -0
  149. package/src/errors.ts +202 -0
  150. package/src/expectations.ts +191 -0
  151. package/src/fake.ts +156 -0
  152. package/src/fill.ts +24 -0
  153. package/src/host.ts +96 -0
  154. package/src/index.ts +143 -0
  155. package/src/ledger.ts +419 -0
  156. package/src/lint.ts +208 -0
  157. package/src/lockfile.ts +95 -0
  158. package/src/log.ts +82 -0
  159. package/src/op.ts +402 -0
  160. package/src/outline.ts +620 -0
  161. package/src/paths.ts +32 -0
  162. package/src/plugin.ts +37 -0
  163. package/src/policy.ts +139 -0
  164. package/src/resource.ts +670 -0
  165. package/src/resources.ts +311 -0
  166. package/src/run.ts +106 -0
  167. package/src/sandbox.ts +161 -0
  168. package/src/scenario.ts +1365 -0
  169. package/src/scenarios.ts +111 -0
  170. package/src/secret.ts +92 -0
  171. package/src/shared.ts +126 -0
  172. package/src/source.ts +82 -0
  173. package/src/steps.ts +90 -0
  174. package/src/version.ts +86 -0
  175. package/src/worker.ts +264 -0
  176. package/src/writer.ts +225 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source.js","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ import type { LedgerRecord } from "./ledger.ts";
2
+ import type { Op } from "./op.ts";
3
+ /** Any operation, whatever its input and output. */
4
+ type AnyOp<I = any, O = any> = Op<string, string, string, I, O>;
5
+ /**
6
+ * What a `Given` step sets up before the run: a call through an operation of the run's fake,
7
+ * with the fields of its input to set (the rest is made up), or a fault to inject into its next
8
+ * call. `as` names a call's output, so a later step's table of an operation's own test can use
9
+ * its fields: `$draft.id`.
10
+ */
11
+ export type Seed = {
12
+ op: AnyOp;
13
+ input: Record<string, unknown>;
14
+ as?: string;
15
+ } | {
16
+ op: AnyOp;
17
+ fault: "failNext" | "rateLimit" | "loseReply";
18
+ };
19
+ /** A call through an operation: what it was given, and what it returned. */
20
+ export interface Call<I, O> {
21
+ input: I;
22
+ output: O;
23
+ }
24
+ /** A run as a `Then` step reads it. */
25
+ export interface RunView {
26
+ /** Every record of the run, in order. */
27
+ readonly records: readonly LedgerRecord[];
28
+ /** The calls the run made through the operation, in order: those that did not fail. */
29
+ calls<I, O>(op: AnyOp<I, O>): Call<I, O>[];
30
+ /** The calls the scenario's `Given` steps made through the operation, to seed the run's fake. */
31
+ seeded<I, O>(op: AnyOp<I, O>): Call<I, O>[];
32
+ /** The seeded calls, then the run's own: everything made through the operation. */
33
+ made<I, O>(op: AnyOp<I, O>): Call<I, O>[];
34
+ }
35
+ /**
36
+ * What a `Then` step expects. `check` reads the run and throws, or returns why the expectation
37
+ * does not hold; returning nothing passes. `op` and `called` say what the step is about, for the
38
+ * graph: the operation, and whether it expects a call to it (true) or rules one out (false).
39
+ */
40
+ export interface Expect {
41
+ op?: AnyOp;
42
+ called?: boolean;
43
+ check(run: RunView): string | undefined | void;
44
+ }
45
+ /** One step definition: a Cucumber expression, and what the step says, from its parameters' values. */
46
+ export type StepDefinition = {
47
+ kind: "given";
48
+ expression: string;
49
+ fn: (...values: any[]) => Seed;
50
+ } | {
51
+ kind: "then";
52
+ expression: string;
53
+ fn: (...values: any[]) => Expect;
54
+ };
55
+ /** A connector's or a company's steps: `owner` (the vendor, say) heads them in a list of known steps. */
56
+ export interface Steps {
57
+ readonly owner: string;
58
+ readonly steps: readonly StepDefinition[];
59
+ }
60
+ /**
61
+ * Declares steps for Gherkin tests, as data the config's scenarios and a connector's own feature
62
+ * files can use: `given` steps say what to seed (`Seed`), `then` steps what to expect
63
+ * (`Expect`). A step's function gets its expression's values, in order, and must be pure:
64
+ * compiling a scenario calls it, and so does the app, whenever a feature file changes.
65
+ *
66
+ * ```ts
67
+ * export const ghostSteps = defineSteps("ghost", ({ given }) => {
68
+ * given("a Ghost draft titled {string}", (title: string) => ({ op: ghostOps.post.create, input: { title }, as: "draft" }));
69
+ * });
70
+ * ```
71
+ */
72
+ export declare function defineSteps(owner: string, define: (steps: {
73
+ given(expression: string, fn: (...values: any[]) => Seed): void;
74
+ then(expression: string, fn: (...values: any[]) => Expect): void;
75
+ }) => void): Steps;
76
+ export {};
package/dist/steps.js ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Declares steps for Gherkin tests, as data the config's scenarios and a connector's own feature
3
+ * files can use: `given` steps say what to seed (`Seed`), `then` steps what to expect
4
+ * (`Expect`). A step's function gets its expression's values, in order, and must be pure:
5
+ * compiling a scenario calls it, and so does the app, whenever a feature file changes.
6
+ *
7
+ * ```ts
8
+ * export const ghostSteps = defineSteps("ghost", ({ given }) => {
9
+ * given("a Ghost draft titled {string}", (title: string) => ({ op: ghostOps.post.create, input: { title }, as: "draft" }));
10
+ * });
11
+ * ```
12
+ */
13
+ export function defineSteps(owner, define) {
14
+ const steps = [];
15
+ define({
16
+ given: (expression, fn) => void steps.push({ kind: "given", expression, fn }),
17
+ // Named for Gherkin's keyword; the object goes to `define` only, which never awaits it.
18
+ // oxlint-disable-next-line unicorn/no-thenable
19
+ then: (expression, fn) => void steps.push({ kind: "then", expression, fn }),
20
+ });
21
+ return Object.freeze({ owner, steps: Object.freeze(steps) });
22
+ }
23
+ //# sourceMappingURL=steps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"steps.js","sourceRoot":"","sources":["../src/steps.ts"],"names":[],"mappings":"AA8DA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,WAAW,CACzB,KAAa,EACb,MAGU;IAEV,MAAM,KAAK,GAAqB,EAAE,CAAC;IACnC,MAAM,CAAC;QACL,KAAK,EAAE,CAAC,UAAU,EAAE,EAAE,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;QAC7E,wFAAwF;QACxF,+CAA+C;QAC/C,IAAI,EAAE,CAAC,UAAU,EAAE,EAAE,EAAE,EAAE,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;KAC5E,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAC/D,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { type WorkflowDefinition } from "./define.ts";
2
+ /** This package's version. A unit test keeps it equal to package.json. */
3
+ export declare const RUNTIME_VERSION = "0.2.0";
4
+ /**
5
+ * The sequence of DBOS calls the runtime makes around each `ctx` call: policy step, approval
6
+ * events and messages, the driver step. Bump it whenever that sequence changes, even without a
7
+ * package release, so a run in flight on the old layout is not replayed against the new one.
8
+ * 2: calls run in program order; a decision event per approval (2026-10-07).
9
+ */
10
+ export declare const STEP_LAYOUT = 2;
11
+ /**
12
+ * The application version DBOS stamps on every run, always `<appName>@…`: DBOS version names
13
+ * are unique per system database across apps. A worker recovers and dequeues only runs of its
14
+ * own version, so the version must change whenever replaying an old run on the new code could
15
+ * go wrong.
16
+ *
17
+ * It is a hash of the app name, `RUNTIME_VERSION`, `STEP_LAYOUT`, the installed DBOS version,
18
+ * and for each workflow its name, the source text of its `run` function, the ids of the
19
+ * operations it uses and its input schema as JSON Schema: DBOS's own automatic versioning, over
20
+ * the workflow code instead of the runtime's one registration wrapper. The hash cannot see
21
+ * functions `run` calls that live elsewhere (their source is not reachable from the definition),
22
+ * op schemas, drivers or the policy. DBOS's `DBOS__APPVERSION` environment variable names a
23
+ * version instead (a git commit, say), still prefixed with the app name.
24
+ */
25
+ export declare function computeVersion({ appName, workflows: definitions, }: {
26
+ /** The resolved config's `appName`. */
27
+ appName: string;
28
+ workflows: readonly WorkflowDefinition<any, any>[];
29
+ }): string;
@@ -0,0 +1,76 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFileSync } from "node:fs";
3
+ import { createRequire } from "node:module";
4
+ import { dirname, join } from "node:path";
5
+ import { jsonSchemaOf, opIdsOf } from "./define.js";
6
+ import { upFrom } from "./paths.js";
7
+ /** This package's version. A unit test keeps it equal to package.json. */
8
+ export const RUNTIME_VERSION = "0.2.0";
9
+ /**
10
+ * The sequence of DBOS calls the runtime makes around each `ctx` call: policy step, approval
11
+ * events and messages, the driver step. Bump it whenever that sequence changes, even without a
12
+ * package release, so a run in flight on the old layout is not replayed against the new one.
13
+ * 2: calls run in program order; a decision event per approval (2026-10-07).
14
+ */
15
+ export const STEP_LAYOUT = 2;
16
+ let dbosVersion;
17
+ /** The installed DBOS SDK's version, read from its package.json (which its exports don't expose). */
18
+ function installedDbosVersion() {
19
+ dbosVersion ??= upFrom(dirname(createRequire(import.meta.url).resolve("@dbos-inc/dbos-sdk")), (dir) => {
20
+ let pkg;
21
+ try {
22
+ pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
23
+ }
24
+ catch (err) {
25
+ if (err.code === "ENOENT")
26
+ return undefined;
27
+ throw err;
28
+ }
29
+ return pkg.name === "@dbos-inc/dbos-sdk" && pkg.version ? pkg.version : undefined;
30
+ });
31
+ if (!dbosVersion)
32
+ throw new Error("Cannot find the package.json of @dbos-inc/dbos-sdk");
33
+ return dbosVersion;
34
+ }
35
+ /**
36
+ * The application version DBOS stamps on every run, always `<appName>@…`: DBOS version names
37
+ * are unique per system database across apps. A worker recovers and dequeues only runs of its
38
+ * own version, so the version must change whenever replaying an old run on the new code could
39
+ * go wrong.
40
+ *
41
+ * It is a hash of the app name, `RUNTIME_VERSION`, `STEP_LAYOUT`, the installed DBOS version,
42
+ * and for each workflow its name, the source text of its `run` function, the ids of the
43
+ * operations it uses and its input schema as JSON Schema: DBOS's own automatic versioning, over
44
+ * the workflow code instead of the runtime's one registration wrapper. The hash cannot see
45
+ * functions `run` calls that live elsewhere (their source is not reachable from the definition),
46
+ * op schemas, drivers or the policy. DBOS's `DBOS__APPVERSION` environment variable names a
47
+ * version instead (a git commit, say), still prefixed with the app name.
48
+ */
49
+ export function computeVersion({ appName, workflows: definitions, }) {
50
+ const named = process.env.DBOS__APPVERSION?.trim();
51
+ if (named)
52
+ return `${appName}@${named}`;
53
+ const workflows = definitions
54
+ .map((wf) => [
55
+ wf.name,
56
+ wf.run.toString(),
57
+ opIdsOf(wf),
58
+ // The input schema is parsed outside steps on replay, so a change to it must change the version.
59
+ inputSchemaOf(wf),
60
+ ])
61
+ .toSorted(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
62
+ const hash = createHash("sha256")
63
+ .update(JSON.stringify([appName, RUNTIME_VERSION, STEP_LAYOUT, installedDbosVersion(), ...workflows]))
64
+ .digest("hex");
65
+ return `${appName}@${hash}`;
66
+ }
67
+ /** The workflow's input schema as the hash sees it: its JSON Schema, or its text when zod can't convert it. */
68
+ function inputSchemaOf(wf) {
69
+ try {
70
+ return jsonSchemaOf(wf.input);
71
+ }
72
+ catch {
73
+ return String(wf.input);
74
+ }
75
+ }
76
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { type SanomaConfig } from "./config.ts";
2
+ import type { Source } from "./source.ts";
3
+ export interface Worker {
4
+ /**
5
+ * The vendors whose drivers could not run when the worker started, in the config's order: a
6
+ * live call to one fails, saying why (`unconfigured` in env.ts), until its variables are set.
7
+ * The worker names them in one line at start, unless started `quiet`.
8
+ */
9
+ readonly unconfigured: readonly string[];
10
+ stop(): Promise<void>;
11
+ }
12
+ export interface WorkerOptions {
13
+ /** The least severe level of the runtime's and DBOS's messages to show, for the whole process: `warn` by default. */
14
+ logLevel?: string;
15
+ /**
16
+ * Start without naming the vendors not configured yet (`unconfigured` still does): for a worker
17
+ * whose runs use the fakes alone, as `runScenarios`'s.
18
+ */
19
+ quiet?: boolean;
20
+ /**
21
+ * Make this worker's version the app's latest, so runs queued without a version come here.
22
+ * A brand-new version becomes the latest on its own; set this only when deliberately starting
23
+ * a worker on a version seen before (a rollback). Off by default, because an old instance
24
+ * restarting during a rolling deploy would otherwise take the new workers' runs.
25
+ */
26
+ promote?: boolean;
27
+ /** The company's repository, which apply commits the lockfile to; the launcher passes its checkout. */
28
+ source?: Source;
29
+ }
30
+ /**
31
+ * Registers the workflows, connects to Postgres and recovers any runs that were interrupted.
32
+ * When a driver declares variables the environment leaves unset, it first loads their stored
33
+ * values into `process.env` and keeps them current while it runs. It starts whether or not they
34
+ * are all there: it prints one line naming the vendors that lack one (`unconfigured`), and a live
35
+ * call to such a vendor fails, saying why, as the call's own final failure, until they are set.
36
+ */
37
+ export declare function startWorker(config: SanomaConfig, options?: WorkerOptions): Promise<Worker>;
package/dist/worker.js ADDED
@@ -0,0 +1,228 @@
1
+ import { DBOS } from "@dbos-inc/dbos-sdk";
2
+ import { buildCtx, isInfrastructureError } from "./call.js";
3
+ import { dbosStatusesOf } from "./client.js";
4
+ import { resolveConfig } from "./config.js";
5
+ import { storedSecrets, watchCredentials } from "./credentials.js";
6
+ import { DBOS_DATABASE_OPTIONS, lazyPool, prepareDatabase } from "./db.js";
7
+ import { credentialsOf, unconfigured, unconfiguredSummary } from "./env.js";
8
+ import { errorInfo, parseOrThrow } from "./errors.js";
9
+ import { entry, ownedLedger, skipped, write, writeFailure } from "./ledger.js";
10
+ import { dbosLogger, quietIfSandbox, setLogLevel, warn } from "./log.js";
11
+ import { lineStartsOf, readSource } from "./ast.js";
12
+ import { callsOf, outlineWithSource } from "./outline.js";
13
+ import { currentRun } from "./run.js";
14
+ import { errorMessage } from "./shared.js";
15
+ // DBOS registrations are process-wide and survive shutdown, so a name is registered once, and
16
+ // its function reads the state of the worker running when a run starts, definition and all; the
17
+ // run keeps that state.
18
+ let current;
19
+ const registered = new Set();
20
+ /**
21
+ * Registers the workflows, connects to Postgres and recovers any runs that were interrupted.
22
+ * When a driver declares variables the environment leaves unset, it first loads their stored
23
+ * values into `process.env` and keeps them current while it runs. It starts whether or not they
24
+ * are all there: it prints one line naming the vendors that lack one (`unconfigured`), and a live
25
+ * call to such a vendor fails, saying why, as the call's own final failure, until they are set.
26
+ */
27
+ export async function startWorker(config, options = {}) {
28
+ // Check everything before touching the state a running worker reads.
29
+ const resolved = resolveConfig(config);
30
+ setLogLevel(options.logLevel);
31
+ // The config's own, or a Postgres store made for this worker, which `stop` then closes.
32
+ const ledger = ownedLedger(resolved);
33
+ const secrets = lazyPool(resolved.databaseUrl, "secrets");
34
+ const state = {
35
+ app: resolved.appName,
36
+ ops: resolved.ops,
37
+ drivers: resolved.drivers,
38
+ fakes: resolved.fakes,
39
+ ...(resolved.scenarios && { scenarios: resolved.scenarios }),
40
+ ...(options.source && { source: options.source }),
41
+ types: resolved.types,
42
+ steps: resolved.steps,
43
+ policy: resolved.policy,
44
+ ledger: ledger.store,
45
+ secrets: storedSecrets(() => secrets.db(), "sanoma"),
46
+ workflows: resolved.workflows,
47
+ outlines: new Map([...resolved.workflows].map(([name, wf]) => [name, outlineOf(wf)])),
48
+ stopped: false,
49
+ };
50
+ const credentials = await watchCredentials(resolved.databaseUrl, resolved.driversByVendor);
51
+ const worker = {
52
+ unconfigured: [...credentialsOf(resolved.driversByVendor, (name) => process.env[name])]
53
+ .filter(([vendor, statuses]) => unconfigured(vendor, statuses))
54
+ .map(([vendor]) => vendor),
55
+ async stop() {
56
+ state.stopped = true;
57
+ // Each is closed whatever the one before did; the first failure is what stop throws.
58
+ let failed;
59
+ for (const close of [
60
+ () => DBOS.shutdown(),
61
+ () => credentials.close(),
62
+ () => secrets.close(),
63
+ () => ledger.close(),
64
+ ]) {
65
+ await close().catch((error) => (failed ??= { error }));
66
+ }
67
+ if (failed)
68
+ throw failed.error;
69
+ },
70
+ };
71
+ // Runs recovered during launch start before it returns, so they must already find this
72
+ // state. If starting fails, a worker still running in the process gets its own back, once
73
+ // DBOS has stopped: until then a run DBOS dispatches finds this state stopped, and is refused.
74
+ const previous = current;
75
+ let launched = false;
76
+ try {
77
+ for (const name of resolved.workflows.keys()) {
78
+ if (!registered.has(name)) {
79
+ register(name);
80
+ registered.add(name);
81
+ }
82
+ }
83
+ current = state;
84
+ // Before DBOS's pool connects, so its connections start with the database's idle-transaction
85
+ // timeout (`DBOS_DATABASE_OPTIONS`); once per process and database.
86
+ await prepareDatabase(resolved.databaseUrl);
87
+ DBOS.setConfig({
88
+ name: resolved.appName,
89
+ systemDatabaseUrl: resolved.databaseUrl,
90
+ ...DBOS_DATABASE_OPTIONS,
91
+ // A logger of our own replaces DBOS's OTLP log export, so only while DBOS exports none (it
92
+ // does on DBOS Cloud); its level is then the threshold `setLogLevel` set, DBOS's own `logLevel` unread.
93
+ ...(process.env.DBOS__CLOUD === "true" ? { logLevel: options.logLevel ?? "warn" } : { logger: dbosLogger }),
94
+ applicationVersion: resolved.version,
95
+ });
96
+ await DBOS.launch();
97
+ launched = true;
98
+ // DBOS gives runs queued without a version only to the app's latest version, which is the
99
+ // newest one registered. A worker started on code seen before (a rollback) is not it.
100
+ const latest = (await DBOS.getLatestApplicationVersion()).versionName;
101
+ if (latest !== resolved.version) {
102
+ if (options.promote)
103
+ await DBOS.setLatestApplicationVersion(resolved.version);
104
+ else {
105
+ warn(`this worker runs version ${resolved.version} but the app's latest is ${latest}; ` +
106
+ "runs queued without a version go to the latest. Start with { promote: true } to take them here");
107
+ }
108
+ }
109
+ await DBOS.registerQueue(resolved.queueName);
110
+ await warnAboutStrandedRuns(resolved);
111
+ const summary = unconfiguredSummary(worker.unconfigured, resolved.driversByVendor.size);
112
+ if (summary && !options.quiet)
113
+ warn(summary);
114
+ }
115
+ catch (err) {
116
+ // Once DBOS is launched, the caller gets no worker to stop: stop it here, so no run goes on
117
+ // in a worker nobody holds, and the next startWorker launches afresh.
118
+ let stopFailed;
119
+ if (launched)
120
+ await worker.stop().catch((error) => (stopFailed = { error }));
121
+ else {
122
+ await credentials
123
+ .close()
124
+ .catch((e) => warn(`closing the credentials listener failed: ${errorMessage(e)}`));
125
+ }
126
+ current = previous;
127
+ if (stopFailed) {
128
+ throw new AggregateError([err, stopFailed.error], `${errorMessage(err)} (and stopping DBOS failed too: ${errorMessage(stopFailed.error)})`, { cause: err });
129
+ }
130
+ throw err;
131
+ }
132
+ return worker;
133
+ }
134
+ /**
135
+ * The workflow's outline, read from its file, which every run is held to. A workflow whose
136
+ * outline cannot be read does not start: nothing could say where its calls are.
137
+ */
138
+ function outlineOf(wf) {
139
+ const { outline, source } = outlineWithSource(wf, readSource(wf.file));
140
+ if ("error" in outline) {
141
+ throw new Error(`Workflow "${wf.name}" cannot be outlined, so its runs could not be held to its code: ${outline.error}`);
142
+ }
143
+ return { file: outline.file, lineStarts: lineStartsOf(source), calls: callsOf(outline.nodes) };
144
+ }
145
+ /**
146
+ * Warns about unfinished runs this worker will never pick up: started on another version of
147
+ * the app, or queued on another queue (such as the single "sanoma" queue before queues were
148
+ * named per app, or the sandbox queue before sandbox runs went on the app's).
149
+ */
150
+ async function warnAboutStrandedRuns({ appName, version, queueName }) {
151
+ const runs = await DBOS.listWorkflows({
152
+ status: dbosStatusesOf("queued", "running"),
153
+ applicationName: appName,
154
+ loadInput: false,
155
+ loadOutput: false,
156
+ });
157
+ const otherVersion = runs.filter((r) => r.applicationVersion && r.applicationVersion !== version);
158
+ // Recovery moves a worker's own runs to DBOS's internal queues, which are fine.
159
+ const otherQueue = runs.filter((r) => !otherVersion.includes(r) && r.queueName && r.queueName !== queueName && !r.queueName.startsWith("_dbos_"));
160
+ if (!otherVersion.length && !otherQueue.length)
161
+ return;
162
+ const ids = [...otherVersion, ...otherQueue].map((r) => r.workflowID);
163
+ warn(`${otherVersion.length} unfinished run(s) of "${appName}" belong to another version and ${otherQueue.length} ` +
164
+ `wait on another queue, so this worker (version ${version}, queue ${queueName}) will not run them: ` +
165
+ `${ids.slice(0, 5).join(", ")}${ids.length > 5 ? ", …" : ""}. Run the version that started them, or move each ` +
166
+ `with DBOSClient.forkWorkflow(id, step, { applicationVersion: "${version}", queueName: "${queueName}" }) ` +
167
+ `and cancel the original; resumeWorkflow(id, { queueName: "${queueName}" }) moves a run on this version to this queue.`);
168
+ }
169
+ /** Registers the name with DBOS: its runs take the definition of that name from the running worker. */
170
+ function register(name) {
171
+ DBOS.registerWorkflow((args) => quietIfSandbox(args.sandbox !== undefined, () => runWorkflow(name, args)), {
172
+ name,
173
+ });
174
+ }
175
+ /** One run of the workflow registered as `name`, on the state of the worker running when it starts. */
176
+ async function runWorkflow(name, { input, startedBy, sandbox }) {
177
+ const state = current;
178
+ // A stopped worker's state stays current while DBOS shuts down.
179
+ if (!state || state.stopped)
180
+ throw new Error(`Run of "${name}" started with no worker running`);
181
+ const wf = state.workflows.get(name);
182
+ const outline = state.outlines.get(name);
183
+ // Registered by an earlier worker in this process, whose config had it; this one's has not.
184
+ if (!wf || !outline)
185
+ throw new Error(`Run of "${name}" refused: this worker's config has no workflow of that name`);
186
+ const run = {
187
+ id: DBOS.workflowID,
188
+ workflow: wf.name,
189
+ actor: startedBy,
190
+ ...(sandbox === undefined ? {} : { sandbox }),
191
+ approvals: [],
192
+ seq: 0,
193
+ tail: Promise.resolve(),
194
+ inAll: false,
195
+ ended: false,
196
+ outline,
197
+ state,
198
+ };
199
+ await write(run, entry(run, { type: "run.started", input }));
200
+ let output;
201
+ try {
202
+ try {
203
+ // The one parse of the input: the client checked it, but sent it as given.
204
+ const parsed = parseOrThrow(wf.input, input, `The input does not match ${wf.name}'s schema`);
205
+ if (sandbox !== undefined) {
206
+ // Imported here, so a live worker never loads the Gherkin parser or faker.
207
+ const { seedSandbox } = await import("./sandbox.js");
208
+ await seedSandbox(run, sandbox);
209
+ }
210
+ output = await currentRun.run(run, () => wf.run(buildCtx(wf, run), parsed));
211
+ }
212
+ finally {
213
+ // Before the outcome is written: a call still queued must find the run ended.
214
+ run.ended = true;
215
+ }
216
+ }
217
+ catch (err) {
218
+ const record = entry(run, { type: "run.failed", error: errorInfo(err) });
219
+ if (isInfrastructureError(err, state))
220
+ skipped(record, `the run was interrupted (${errorMessage(err)})`);
221
+ else
222
+ await writeFailure(run, record, err);
223
+ throw err;
224
+ }
225
+ await write(run, entry(run, { type: "run.finished", output }));
226
+ return output;
227
+ }
228
+ //# sourceMappingURL=worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAuB,aAAa,EAAqB,MAAM,aAAa,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE3E,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC5E,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAkE,MAAM,UAAU,CAAC;AAEtG,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAY3C,8FAA8F;AAC9F,gGAAgG;AAChG,wBAAwB;AACxB,IAAI,OAAgC,CAAC;AACrC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;AAqBrC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAoB,EAAE,OAAO,GAAkB,EAAE;IACjF,qEAAqE;IACrE,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACvC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC9B,wFAAwF;IACxF,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IAC1D,MAAM,KAAK,GAAgB;QACzB,GAAG,EAAE,QAAQ,CAAC,OAAO;QACrB,GAAG,EAAE,QAAQ,CAAC,GAAG;QACjB,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,GAAG,CAAC,QAAQ,CAAC,SAAS,IAAI,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC;QAC5D,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;QACjD,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,MAAM,EAAE,MAAM,CAAC,KAAK;QACpB,OAAO,EAAE,aAAa,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC;QACpD,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,QAAQ,EAAE,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACrF,OAAO,EAAE,KAAK;KACf,CAAC;IACF,MAAM,WAAW,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IAC3F,MAAM,MAAM,GAAW;QACrB,YAAY,EAAE,CAAC,GAAG,aAAa,CAAC,QAAQ,CAAC,eAAe,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;aACpF,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;aAC9D,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC;QAC5B,KAAK,CAAC,IAAI;YACR,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;YACrB,qFAAqF;YACrF,IAAI,MAAsC,CAAC;YAC3C,KAAK,MAAM,KAAK,IAAI;gBAClB,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE;gBACrB,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE;gBACzB,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE;gBACrB,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE;aACrB,EAAE,CAAC;gBACF,MAAM,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE,CAAC,CAAC,MAAM,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;YAClE,CAAC;YACD,IAAI,MAAM;gBAAE,MAAM,MAAM,CAAC,KAAK,CAAC;QACjC,CAAC;KACF,CAAC;IACF,uFAAuF;IACvF,0FAA0F;IAC1F,+FAA+F;IAC/F,MAAM,QAAQ,GAAG,OAAO,CAAC;IACzB,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,CAAC;QACH,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC;YAC7C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1B,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACf,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;QACD,OAAO,GAAG,KAAK,CAAC;QAChB,6FAA6F;QAC7F,oEAAoE;QACpE,MAAM,eAAe,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC5C,IAAI,CAAC,SAAS,CAAC;YACb,IAAI,EAAE,QAAQ,CAAC,OAAO;YACtB,iBAAiB,EAAE,QAAQ,CAAC,WAAW;YACvC,GAAG,qBAAqB;YACxB,2FAA2F;YAC3F,wGAAwG;YACxG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;YAC3G,kBAAkB,EAAE,QAAQ,CAAC,OAAO;SACrC,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACpB,QAAQ,GAAG,IAAI,CAAC;QAChB,0FAA0F;QAC1F,sFAAsF;QACtF,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,2BAA2B,EAAE,CAAC,CAAC,WAAW,CAAC;QACtE,IAAI,MAAM,KAAK,QAAQ,CAAC,OAAO,EAAE,CAAC;YAChC,IAAI,OAAO,CAAC,OAAO;gBAAE,MAAM,IAAI,CAAC,2BAA2B,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;iBACzE,CAAC;gBACJ,IAAI,CACF,4BAA4B,QAAQ,CAAC,OAAO,4BAA4B,MAAM,IAAI;oBAChF,gGAAgG,CACnG,CAAC;YACJ,CAAC;QACH,CAAC;QACD,MAAM,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC7C,MAAM,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,mBAAmB,CAAC,MAAM,CAAC,YAAY,EAAE,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QACxF,IAAI,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK;YAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,4FAA4F;QAC5F,sEAAsE;QACtE,IAAI,UAA0C,CAAC;QAC/C,IAAI,QAAQ;YAAE,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE,CAAC,CAAC,UAAU,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;aACjF,CAAC;YACJ,MAAM,WAAW;iBACd,KAAK,EAAE;iBACP,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE,CAAC,IAAI,CAAC,4CAA4C,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAChG,CAAC;QACD,OAAO,GAAG,QAAQ,CAAC;QACnB,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,IAAI,cAAc,CACtB,CAAC,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC,EACvB,GAAG,YAAY,CAAC,GAAG,CAAC,mCAAmC,YAAY,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,EACxF,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAC;QACJ,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,SAAS,SAAS,CAAC,EAAgC;IACjD,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,iBAAiB,CAAC,EAAE,EAAE,UAAU,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IACvE,IAAI,OAAO,IAAI,OAAO,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,aAAa,EAAE,CAAC,IAAI,oEAAoE,OAAO,CAAC,KAAK,EAAE,CACxG,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,YAAY,CAAC,MAAO,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;AAClG,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,qBAAqB,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAkB;IAClF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC;QACpC,MAAM,EAAE,cAAc,CAAC,QAAQ,EAAE,SAAS,CAAC;QAC3C,eAAe,EAAE,OAAO;QACxB,SAAS,EAAE,KAAK;QAChB,UAAU,EAAE,KAAK;KAClB,CAAC,CAAC;IACH,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,kBAAkB,IAAI,CAAC,CAAC,kBAAkB,KAAK,OAAO,CAAC,CAAC;IAClG,gFAAgF;IAChF,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAC5B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,QAAQ,CAAC,CAClH,CAAC;IACF,IAAI,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM;QAAE,OAAO;IACvD,MAAM,GAAG,GAAG,CAAC,GAAG,YAAY,EAAE,GAAG,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;IACtE,IAAI,CACF,GAAG,YAAY,CAAC,MAAM,0BAA0B,OAAO,mCAAmC,UAAU,CAAC,MAAM,GAAG;QAC5G,kDAAkD,OAAO,WAAW,SAAS,uBAAuB;QACpG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,oDAAoD;QAC/G,iEAAiE,OAAO,kBAAkB,SAAS,OAAO;QAC1G,6DAA6D,SAAS,iDAAiD,CAC1H,CAAC;AACJ,CAAC;AAED,uGAAuG;AACvG,SAAS,QAAQ,CAAC,IAAY;IAC5B,IAAI,CAAC,gBAAgB,CAAC,CAAC,IAAa,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE;QAClH,IAAI;KACL,CAAC,CAAC;AACL,CAAC;AAED,uGAAuG;AACvG,KAAK,UAAU,WAAW,CAAC,IAAY,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAW;IAC7E,MAAM,KAAK,GAAG,OAAO,CAAC;IACtB,gEAAgE;IAChE,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,WAAW,IAAI,kCAAkC,CAAC,CAAC;IAChG,MAAM,EAAE,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzC,4FAA4F;IAC5F,IAAI,CAAC,EAAE,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,WAAW,IAAI,8DAA8D,CAAC,CAAC;IACpH,MAAM,GAAG,GAAQ;QACf,EAAE,EAAE,IAAI,CAAC,UAAW;QACpB,QAAQ,EAAE,EAAE,CAAC,IAAI;QACjB,KAAK,EAAE,SAAS;QAChB,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;QAC7C,SAAS,EAAE,EAAE;QACb,GAAG,EAAE,CAAC;QACN,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE;QACvB,KAAK,EAAE,KAAK;QACZ,KAAK,EAAE,KAAK;QACZ,OAAO;QACP,KAAK;KACN,CAAC;IACF,MAAM,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IAC7D,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,IAAI,CAAC;YACH,2EAA2E;YAC3E,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,4BAA4B,EAAE,CAAC,IAAI,WAAW,CAAC,CAAC;YAC7F,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC1B,2EAA2E;gBAC3E,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;gBACrD,MAAM,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAClC,CAAC;YACD,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QAC9E,CAAC;gBAAS,CAAC;YACT,8EAA8E;YAC9E,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC;QACnB,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACzE,IAAI,qBAAqB,CAAC,GAAG,EAAE,KAAK,CAAC;YAAE,OAAO,CAAC,MAAM,EAAE,4BAA4B,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;;YACpG,MAAM,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;QAC1C,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,MAAM,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAC/D,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,35 @@
1
+ import type { FormatConfig } from "oxfmt";
2
+ import { type DataFileExport, type DataFileImport, type DataValue } from "./datafile.ts";
3
+ /** A whole data file to render: its imports and its resources, in order. */
4
+ export interface DataFileContent {
5
+ imports: readonly Pick<DataFileImport, "source" | "imported" | "local">[];
6
+ exports: readonly Pick<DataFileExport, "name" | "vendor" | "type" | "desired">[];
7
+ }
8
+ /**
9
+ * oxfmt's options from `<root>/.oxfmtrc.json`, what `oxfmt` itself reads there, less what only
10
+ * its CLI uses (`ignorePatterns`, `overrides`); none, oxfmt's defaults, without the file.
11
+ */
12
+ export declare function formatOptions(root: string): FormatConfig;
13
+ /**
14
+ * `source`, a data file's text, with fields of its resource `exportName` set to new values:
15
+ * `fields` by dotted path, a list item's with its index, as a drift report names them
16
+ * (`required_pull_request_reviews.0.dismiss_stale_reviews`). A field the declaration does not
17
+ * have is added at the end of the object that should hold it. Only that `export const` changes:
18
+ * it is formatted with oxfmt under `options` (`formatOptions`), and every other byte of the file
19
+ * stays as it was.
20
+ *
21
+ * A value naming a secret adds `import { secret } from "sanoma/resources"` after the file's
22
+ * imports when it has none.
23
+ *
24
+ * Throws when the file does not parse, declares no such resource, or a path cannot be set: it
25
+ * runs through a value that is not an object or a list, or ends at a reference to another
26
+ * resource (a person changes a reference; a vendor's value never replaces one).
27
+ */
28
+ export declare function setFields(source: string, exportName: string, fields: Readonly<Record<string, DataValue>>, options?: FormatConfig): Promise<string>;
29
+ /**
30
+ * A whole data file: the imports, one per source, then each resource as `export const`, then
31
+ * `export default` naming them all, formatted with oxfmt under `options`. `readDataFile` reads it
32
+ * back as it was given: what `readDataFile` returns renders the file again. A file whose values
33
+ * name a secret also imports `secret` from "sanoma/resources".
34
+ */
35
+ export declare function renderDataFile({ imports, exports }: DataFileContent, options?: FormatConfig): Promise<string>;