@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.
- package/LICENSE +105 -0
- package/README.md +544 -2
- package/dist/apply-steps.d.ts +6 -0
- package/dist/apply-steps.js +93 -0
- package/dist/apply-steps.js.map +1 -0
- package/dist/apply.d.ts +197 -0
- package/dist/apply.js +470 -0
- package/dist/apply.js.map +1 -0
- package/dist/approvals.d.ts +46 -0
- package/dist/approvals.js +111 -0
- package/dist/approvals.js.map +1 -0
- package/dist/ast.d.ts +57 -0
- package/dist/ast.js +81 -0
- package/dist/ast.js.map +1 -0
- package/dist/call.d.ts +37 -0
- package/dist/call.js +417 -0
- package/dist/call.js.map +1 -0
- package/dist/client.d.ts +203 -0
- package/dist/client.js +457 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +126 -0
- package/dist/config.js +264 -0
- package/dist/config.js.map +1 -0
- package/dist/credentials.d.ts +55 -0
- package/dist/credentials.js +261 -0
- package/dist/credentials.js.map +1 -0
- package/dist/cucumber.d.ts +64 -0
- package/dist/cucumber.js +158 -0
- package/dist/cucumber.js.map +1 -0
- package/dist/datafile.d.ts +71 -0
- package/dist/datafile.js +380 -0
- package/dist/datafile.js.map +1 -0
- package/dist/db.d.ts +58 -0
- package/dist/db.js +179 -0
- package/dist/db.js.map +1 -0
- package/dist/define.d.ts +205 -0
- package/dist/define.js +103 -0
- package/dist/define.js.map +1 -0
- package/dist/describe.d.ts +141 -0
- package/dist/describe.js +135 -0
- package/dist/describe.js.map +1 -0
- package/dist/drift.d.ts +127 -0
- package/dist/drift.js +167 -0
- package/dist/drift.js.map +1 -0
- package/dist/env.d.ts +49 -0
- package/dist/env.js +83 -0
- package/dist/env.js.map +1 -0
- package/dist/errors.d.ts +88 -0
- package/dist/errors.js +148 -0
- package/dist/errors.js.map +1 -0
- package/dist/expectations.d.ts +26 -0
- package/dist/expectations.js +162 -0
- package/dist/expectations.js.map +1 -0
- package/dist/fake.d.ts +55 -0
- package/dist/fake.js +87 -0
- package/dist/fake.js.map +1 -0
- package/dist/fill.d.ts +9 -0
- package/dist/fill.js +17 -0
- package/dist/fill.js.map +1 -0
- package/dist/host.d.ts +56 -0
- package/dist/host.js +45 -0
- package/dist/host.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/ledger.d.ts +217 -0
- package/dist/ledger.js +251 -0
- package/dist/ledger.js.map +1 -0
- package/dist/lint.d.ts +32 -0
- package/dist/lint.js +168 -0
- package/dist/lint.js.map +1 -0
- package/dist/lockfile.d.ts +36 -0
- package/dist/lockfile.js +69 -0
- package/dist/lockfile.js.map +1 -0
- package/dist/log.d.ts +24 -0
- package/dist/log.js +83 -0
- package/dist/log.js.map +1 -0
- package/dist/op.d.ts +260 -0
- package/dist/op.js +188 -0
- package/dist/op.js.map +1 -0
- package/dist/outline.d.ts +179 -0
- package/dist/outline.js +506 -0
- package/dist/outline.js.map +1 -0
- package/dist/paths.d.ts +11 -0
- package/dist/paths.js +30 -0
- package/dist/paths.js.map +1 -0
- package/dist/plugin.d.ts +28 -0
- package/dist/plugin.js +25 -0
- package/dist/plugin.js.map +1 -0
- package/dist/policy.d.ts +114 -0
- package/dist/policy.js +60 -0
- package/dist/policy.js.map +1 -0
- package/dist/resource.d.ts +378 -0
- package/dist/resource.js +430 -0
- package/dist/resource.js.map +1 -0
- package/dist/resources.d.ts +77 -0
- package/dist/resources.js +244 -0
- package/dist/resources.js.map +1 -0
- package/dist/run.d.ts +101 -0
- package/dist/run.js +7 -0
- package/dist/run.js.map +1 -0
- package/dist/sandbox.d.ts +33 -0
- package/dist/sandbox.js +127 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/scenario.d.ts +221 -0
- package/dist/scenario.js +1104 -0
- package/dist/scenario.js.map +1 -0
- package/dist/scenarios.d.ts +55 -0
- package/dist/scenarios.js +71 -0
- package/dist/scenarios.js.map +1 -0
- package/dist/secret.d.ts +42 -0
- package/dist/secret.js +58 -0
- package/dist/secret.js.map +1 -0
- package/dist/shared.d.ts +69 -0
- package/dist/shared.js +84 -0
- package/dist/shared.js.map +1 -0
- package/dist/source.d.ts +58 -0
- package/dist/source.js +54 -0
- package/dist/source.js.map +1 -0
- package/dist/steps.d.ts +76 -0
- package/dist/steps.js +23 -0
- package/dist/steps.js.map +1 -0
- package/dist/version.d.ts +29 -0
- package/dist/version.js +76 -0
- package/dist/version.js.map +1 -0
- package/dist/worker.d.ts +37 -0
- package/dist/worker.js +228 -0
- package/dist/worker.js.map +1 -0
- package/dist/writer.d.ts +35 -0
- package/dist/writer.js +201 -0
- package/dist/writer.js.map +1 -0
- package/oxlint.json +128 -0
- package/package.json +96 -3
- package/src/apply-steps.ts +110 -0
- package/src/apply.ts +626 -0
- package/src/approvals.ts +157 -0
- package/src/ast.ts +115 -0
- package/src/call.ts +483 -0
- package/src/client.ts +604 -0
- package/src/config.ts +404 -0
- package/src/credentials.ts +296 -0
- package/src/cucumber.ts +230 -0
- package/src/datafile.ts +481 -0
- package/src/db.ts +197 -0
- package/src/define.ts +285 -0
- package/src/describe.ts +282 -0
- package/src/drift.ts +259 -0
- package/src/env.ts +122 -0
- package/src/errors.ts +202 -0
- package/src/expectations.ts +191 -0
- package/src/fake.ts +156 -0
- package/src/fill.ts +24 -0
- package/src/host.ts +96 -0
- package/src/index.ts +143 -0
- package/src/ledger.ts +419 -0
- package/src/lint.ts +208 -0
- package/src/lockfile.ts +95 -0
- package/src/log.ts +82 -0
- package/src/op.ts +402 -0
- package/src/outline.ts +620 -0
- package/src/paths.ts +32 -0
- package/src/plugin.ts +37 -0
- package/src/policy.ts +139 -0
- package/src/resource.ts +670 -0
- package/src/resources.ts +311 -0
- package/src/run.ts +106 -0
- package/src/sandbox.ts +161 -0
- package/src/scenario.ts +1365 -0
- package/src/scenarios.ts +111 -0
- package/src/secret.ts +92 -0
- package/src/shared.ts +126 -0
- package/src/source.ts +82 -0
- package/src/steps.ts +90 -0
- package/src/version.ts +86 -0
- package/src/worker.ts +264 -0
- package/src/writer.ts +225 -0
package/dist/drift.d.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type WorkflowDefinition } from "./define.ts";
|
|
3
|
+
import type { Op } from "./op.ts";
|
|
4
|
+
import { type DriftField, type Resource } from "./resource.ts";
|
|
5
|
+
import type { DeclaredResource, ResourceProblem } from "./resources.ts";
|
|
6
|
+
import { type sourceOps } from "./source.ts";
|
|
7
|
+
export type { DriftField } from "./resource.ts";
|
|
8
|
+
export { DRIFT_WORKFLOW } from "./shared.ts";
|
|
9
|
+
/**
|
|
10
|
+
* How a declared resource compares: `clean` when every declared field of its bound object holds
|
|
11
|
+
* its declared value, `drifted` when one does not, `gone` when the vendor no longer has its bound
|
|
12
|
+
* object, `absent` when it is bound to none (whether or not its identity finds one: `found`), and
|
|
13
|
+
* `error` when it could not be read (the vendor's error, a policy denial, or no driver for its type).
|
|
14
|
+
*/
|
|
15
|
+
export type DriftStatus = "clean" | "drifted" | "gone" | "absent" | "error";
|
|
16
|
+
/** One declared resource in a drift report. */
|
|
17
|
+
export interface DriftResult {
|
|
18
|
+
/** The resource's id, `<file>#<export>`: `resources/identity/github.ts#website`. */
|
|
19
|
+
id: string;
|
|
20
|
+
/** Its type within the vendor, such as `repository`. */
|
|
21
|
+
type: string;
|
|
22
|
+
vendor: string;
|
|
23
|
+
/** What people call it: its identity, else its export's name. */
|
|
24
|
+
name: string;
|
|
25
|
+
status: DriftStatus;
|
|
26
|
+
/** The vendor's id of the object the lockfile binds it to; absent when unbound. */
|
|
27
|
+
binding?: string;
|
|
28
|
+
/** For `absent`: the binding id of the object its identity found, which apply may bind it to. */
|
|
29
|
+
found?: string;
|
|
30
|
+
/** The declared fields that differ; empty unless `drifted`. */
|
|
31
|
+
fields: DriftField[];
|
|
32
|
+
/** Why it could not be read, for `error`. */
|
|
33
|
+
error?: string;
|
|
34
|
+
}
|
|
35
|
+
/** The drift workflow's output. */
|
|
36
|
+
export interface DriftReport {
|
|
37
|
+
/** Epoch milliseconds, by `ctx.now()`. */
|
|
38
|
+
startedAt: number;
|
|
39
|
+
/** Epoch milliseconds, by `ctx.now()`. */
|
|
40
|
+
finishedAt: number;
|
|
41
|
+
/** What was wrong in the data files when the run read them: their resources are not in `resources`. */
|
|
42
|
+
problems: ResourceProblem[];
|
|
43
|
+
/** Each resource the data files declared when the run read them, in their order. */
|
|
44
|
+
resources: DriftResult[];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The drift workflow's input: nothing. The run reads the data files itself, so a run says what
|
|
48
|
+
* they declared when it ran, and no caller can hand it other resources to check.
|
|
49
|
+
*/
|
|
50
|
+
export declare const DriftInput: z.ZodObject<{}, z.core.$strict>;
|
|
51
|
+
/**
|
|
52
|
+
* What a drift run records of the data files, as its first step: each resource without its
|
|
53
|
+
* write-only values, and the problems.
|
|
54
|
+
*/
|
|
55
|
+
export interface DriftDeclared {
|
|
56
|
+
resources: Pick<DeclaredResource, "id" | "vendor" | "type" | "name" | "identity" | "binding" | "desired" | "refs">[];
|
|
57
|
+
problems: ResourceProblem[];
|
|
58
|
+
}
|
|
59
|
+
type State = Record<string, unknown>;
|
|
60
|
+
type ImportOp = Op<string, string, "import", {
|
|
61
|
+
id: string;
|
|
62
|
+
}, {
|
|
63
|
+
id: string;
|
|
64
|
+
gone: boolean;
|
|
65
|
+
state?: State;
|
|
66
|
+
}>;
|
|
67
|
+
export type ResolveOp = typeof sourceOps.source.resolve;
|
|
68
|
+
/** What one import found: the object's state, that it is gone, or why it could not be read. */
|
|
69
|
+
export type Found = {
|
|
70
|
+
state: State;
|
|
71
|
+
} | {
|
|
72
|
+
gone: true;
|
|
73
|
+
} | {
|
|
74
|
+
error: string;
|
|
75
|
+
};
|
|
76
|
+
/** What a host's ref names now, or why it could not be resolved. */
|
|
77
|
+
export type Revision = {
|
|
78
|
+
revision: string;
|
|
79
|
+
} | {
|
|
80
|
+
error: string;
|
|
81
|
+
};
|
|
82
|
+
/** The `import` ops of the types among `ops` with a driver, by type, sorted: what observing them uses. */
|
|
83
|
+
export declare function importsOf(types: ReadonlyMap<string, Resource>, ops: ReadonlyMap<string, Op>, drivers: ReadonlyMap<string, unknown>): ImportOp[];
|
|
84
|
+
/**
|
|
85
|
+
* What a declared resource is observed by: the import id (`by`, default its binding, else its
|
|
86
|
+
* identity), or without one what it is found to be: `gone` for none, `error` when its type is
|
|
87
|
+
* not the connectors' or has no import among `imports`.
|
|
88
|
+
*/
|
|
89
|
+
export declare function observeBy({ vendor, type, binding, identity }: Pick<DeclaredResource, "vendor" | "type" | "binding" | "identity">, types: ReadonlyMap<string, Resource>, imports: readonly Pick<Op, "id">[], by?: string | undefined): {
|
|
90
|
+
id: string;
|
|
91
|
+
} | Found;
|
|
92
|
+
/** What an import's answer found. */
|
|
93
|
+
export declare const observed: (out: {
|
|
94
|
+
gone: boolean;
|
|
95
|
+
state?: State;
|
|
96
|
+
}) => Found;
|
|
97
|
+
/**
|
|
98
|
+
* Each declared resource against what its import `found`: `error`, `absent` (unbound, with the
|
|
99
|
+
* binding id of what its identity found as `found`), `gone`, else the state and the declared
|
|
100
|
+
* fields compared by `diffDeclared`, a reference against the state of the resource it names
|
|
101
|
+
* (so GitHub's node id for a repository counts as its name). A host with a `revisions` entry
|
|
102
|
+
* also compares its revision against the commit its ref names now (or is that entry's `error`).
|
|
103
|
+
*/
|
|
104
|
+
export declare function compare(types: ReadonlyMap<string, Resource>, resources: DriftDeclared["resources"], found: ReadonlyMap<string, Found>, revisions?: ReadonlyMap<string, Revision>): DriftResult[];
|
|
105
|
+
/**
|
|
106
|
+
* The drift workflow for a config: it `uses` the `import` of every resource type the connectors
|
|
107
|
+
* declare (`types`) that has a driver, so its version changes with them, not with the data
|
|
108
|
+
* files. `resolveConfig` adds it to a config whose connectors declare resource types, with
|
|
109
|
+
* `declared`, the step that reads the data files.
|
|
110
|
+
*
|
|
111
|
+
* A run reads the data files and the lockfile (`declared`, recorded as a step, so a replay checks
|
|
112
|
+
* the same list), then imports each resource by its binding, else by its identity, in the files'
|
|
113
|
+
* order: one vendor call each, none for a resource with neither. A bound resource whose import
|
|
114
|
+
* says gone is `gone`; an unbound one is `absent`, with the binding id of what its identity found
|
|
115
|
+
* as `found`. Else the state and the declared fields are compared by `diffDeclared`, a reference
|
|
116
|
+
* against the state of the resource it names, read in the same run (so GitHub's node id for a
|
|
117
|
+
* repository counts as its name). A failed call is that resource's
|
|
118
|
+
* `error`, and the run goes on to the next; a failure that ends the run (`isRunControlError`)
|
|
119
|
+
* ends it.
|
|
120
|
+
*/
|
|
121
|
+
export declare function driftWorkflow(types: ReadonlyMap<string, Resource>, ops: ReadonlyMap<string, Op>, drivers: ReadonlyMap<string, unknown>, declared: () => Promise<DriftDeclared>): WorkflowDefinition<readonly (ImportOp | ResolveOp)[], typeof DriftInput>;
|
|
122
|
+
/**
|
|
123
|
+
* A host's source: the clone URL its data file gives, or, when it names a declared repository
|
|
124
|
+
* (`target`), the first of the reference's `by` fields that holds one in that repository's state,
|
|
125
|
+
* read in this run.
|
|
126
|
+
*/
|
|
127
|
+
export declare function sourceUrl(desired: State, target: string | undefined, { host, fields }: Resource, found: ReadonlyMap<string, Found>): string | undefined;
|
package/dist/drift.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { defineWorkflow } from "./define.js";
|
|
3
|
+
import { isRunControlError } from "./errors.js";
|
|
4
|
+
import { diffDeclared, fieldPathOf, valueAt } from "./resource.js";
|
|
5
|
+
import { errorMessage } from "./shared.js";
|
|
6
|
+
import { SOURCE_RESOLVE } from "./source.js";
|
|
7
|
+
export { DRIFT_WORKFLOW } from "./shared.js";
|
|
8
|
+
/**
|
|
9
|
+
* The drift workflow's input: nothing. The run reads the data files itself, so a run says what
|
|
10
|
+
* they declared when it ran, and no caller can hand it other resources to check.
|
|
11
|
+
*/
|
|
12
|
+
export const DriftInput = z.object({}).strict();
|
|
13
|
+
/** A clone URL (a scheme's, or scp-like `user@host:path`): what a `by` field of a host's source holds, told from an id. */
|
|
14
|
+
const CLONE_URL = /^((https?|ssh|git|file):\/\/|[\w.-]+@[\w.-]+:)/;
|
|
15
|
+
// What drift and apply share. Observing a resource is one `ctx` call, which a workflow makes in
|
|
16
|
+
// its own `run` (the outline draws only those): `observeBy` says what each import is called with,
|
|
17
|
+
// `observed` reads its answer, and `compare` turns what every import found into drift's results.
|
|
18
|
+
/** The `import` ops of the types among `ops` with a driver, by type, sorted: what observing them uses. */
|
|
19
|
+
export function importsOf(types, ops, drivers) {
|
|
20
|
+
return [...types.keys()].toSorted().flatMap((type) => {
|
|
21
|
+
const op = ops.get(`${type}.import`);
|
|
22
|
+
return op && drivers.has(op.id) ? [op] : [];
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* What a declared resource is observed by: the import id (`by`, default its binding, else its
|
|
27
|
+
* identity), or without one what it is found to be: `gone` for none, `error` when its type is
|
|
28
|
+
* not the connectors' or has no import among `imports`.
|
|
29
|
+
*/
|
|
30
|
+
export function observeBy({ vendor, type, binding, identity }, types, imports, by = binding ?? identity) {
|
|
31
|
+
const key = `${vendor}.${type}`;
|
|
32
|
+
if (!types.has(key))
|
|
33
|
+
return { error: `the config's connectors declare no resource type ${key}` };
|
|
34
|
+
if (!imports.some((op) => op.id === `${key}.import`))
|
|
35
|
+
return { error: `no driver for ${key}.import` };
|
|
36
|
+
return by === undefined ? { gone: true } : { id: by };
|
|
37
|
+
}
|
|
38
|
+
/** What an import's answer found. */
|
|
39
|
+
export const observed = (out) => out.gone || !out.state ? { gone: true } : { state: out.state };
|
|
40
|
+
/**
|
|
41
|
+
* Each declared resource against what its import `found`: `error`, `absent` (unbound, with the
|
|
42
|
+
* binding id of what its identity found as `found`), `gone`, else the state and the declared
|
|
43
|
+
* fields compared by `diffDeclared`, a reference against the state of the resource it names
|
|
44
|
+
* (so GitHub's node id for a repository counts as its name). A host with a `revisions` entry
|
|
45
|
+
* also compares its revision against the commit its ref names now (or is that entry's `error`).
|
|
46
|
+
*/
|
|
47
|
+
export function compare(types, resources, found, revisions = new Map()) {
|
|
48
|
+
return resources.map(({ id, vendor, type, name, binding, desired, refs }) => {
|
|
49
|
+
const row = { id, type, vendor, name, ...(binding !== undefined && { binding }) };
|
|
50
|
+
const now = found.get(id);
|
|
51
|
+
if ("error" in now)
|
|
52
|
+
return { ...row, status: "error", fields: [], error: now.error };
|
|
53
|
+
const resource = types.get(`${vendor}.${type}`);
|
|
54
|
+
if (binding === undefined) {
|
|
55
|
+
return {
|
|
56
|
+
...row,
|
|
57
|
+
status: "absent",
|
|
58
|
+
fields: [],
|
|
59
|
+
...("state" in now && { found: resource.bindingId(now.state) }),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if ("gone" in now)
|
|
63
|
+
return { ...row, status: "gone", fields: [] };
|
|
64
|
+
// Each reference, by where it is: the values the vendor may hold there for its resource.
|
|
65
|
+
const accept = {};
|
|
66
|
+
for (const [at, target] of Object.entries(refs)) {
|
|
67
|
+
const by = resource.fields.references?.[fieldPathOf(at)]?.by ?? [];
|
|
68
|
+
const named = found.get(target);
|
|
69
|
+
if (named && "state" in named)
|
|
70
|
+
accept[at] = by.map((field) => named.state[field]).filter((v) => v != null);
|
|
71
|
+
}
|
|
72
|
+
const fields = diffDeclared(resource, now.state, desired, accept);
|
|
73
|
+
const resolved = revisions.get(id);
|
|
74
|
+
if (resolved && "error" in resolved)
|
|
75
|
+
return { ...row, status: "error", fields: [], error: resolved.error };
|
|
76
|
+
// A host whose state does not say which commit it runs is not compared.
|
|
77
|
+
const actual = resource.host?.outputs(now.state).revision;
|
|
78
|
+
if (resolved && actual !== undefined && actual !== resolved.revision) {
|
|
79
|
+
fields.push({ path: resource.host.revision, desired: resolved.revision, actual });
|
|
80
|
+
}
|
|
81
|
+
return { ...row, status: fields.length ? "drifted" : "clean", fields };
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The drift workflow for a config: it `uses` the `import` of every resource type the connectors
|
|
86
|
+
* declare (`types`) that has a driver, so its version changes with them, not with the data
|
|
87
|
+
* files. `resolveConfig` adds it to a config whose connectors declare resource types, with
|
|
88
|
+
* `declared`, the step that reads the data files.
|
|
89
|
+
*
|
|
90
|
+
* A run reads the data files and the lockfile (`declared`, recorded as a step, so a replay checks
|
|
91
|
+
* the same list), then imports each resource by its binding, else by its identity, in the files'
|
|
92
|
+
* order: one vendor call each, none for a resource with neither. A bound resource whose import
|
|
93
|
+
* says gone is `gone`; an unbound one is `absent`, with the binding id of what its identity found
|
|
94
|
+
* as `found`. Else the state and the declared fields are compared by `diffDeclared`, a reference
|
|
95
|
+
* against the state of the resource it names, read in the same run (so GitHub's node id for a
|
|
96
|
+
* repository counts as its name). A failed call is that resource's
|
|
97
|
+
* `error`, and the run goes on to the next; a failure that ends the run (`isRunControlError`)
|
|
98
|
+
* ends it.
|
|
99
|
+
*/
|
|
100
|
+
export function driftWorkflow(types, ops, drivers, declared) {
|
|
101
|
+
const imports = importsOf(types, ops, drivers);
|
|
102
|
+
const resolve = ops.get(SOURCE_RESOLVE);
|
|
103
|
+
const uses = [...imports, ...(resolve ? [resolve] : [])];
|
|
104
|
+
return defineWorkflow({
|
|
105
|
+
// The literal, held to the one name: the outline finds a workflow by a string, or a const of its own file.
|
|
106
|
+
name: "drift",
|
|
107
|
+
title: "Check resources for drift",
|
|
108
|
+
trigger: "manual",
|
|
109
|
+
input: DriftInput,
|
|
110
|
+
uses,
|
|
111
|
+
run: async (ctx) => {
|
|
112
|
+
const startedAt = await ctx.now();
|
|
113
|
+
const { resources, problems } = await declared();
|
|
114
|
+
// Every import first, so a reference compares against what its resource holds now.
|
|
115
|
+
const found = new Map();
|
|
116
|
+
for (const resource of resources) {
|
|
117
|
+
const by = observeBy(resource, types, imports);
|
|
118
|
+
// An if/else, not a `continue`: the outline draws the import as the way past the error.
|
|
119
|
+
if (!("id" in by))
|
|
120
|
+
found.set(resource.id, by);
|
|
121
|
+
else {
|
|
122
|
+
try {
|
|
123
|
+
found.set(resource.id, observed(await ctx[resource.vendor][resource.type].import({ id: by.id })));
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
if (isRunControlError(err))
|
|
127
|
+
throw err;
|
|
128
|
+
found.set(resource.id, { error: errorMessage(err) });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// A host's revision against the commit its ref names now: a merge is drift on the host.
|
|
133
|
+
const revisions = new Map();
|
|
134
|
+
for (const { id, vendor, type, desired, refs } of resources) {
|
|
135
|
+
const resource = types.get(`${vendor}.${type}`);
|
|
136
|
+
const host = resource?.host;
|
|
137
|
+
const source = host && sourceUrl(desired, refs[host.inputs.source], resource, found);
|
|
138
|
+
const ref = host && valueAt(desired, host.inputs.ref);
|
|
139
|
+
if (host && "state" in found.get(id) && source !== undefined && typeof ref === "string" && ref) {
|
|
140
|
+
try {
|
|
141
|
+
revisions.set(id, await ctx.sanoma.source.resolve({ url: source, ref }));
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
if (isRunControlError(err))
|
|
145
|
+
throw err;
|
|
146
|
+
revisions.set(id, { error: errorMessage(err) });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const results = compare(types, resources, found, revisions);
|
|
151
|
+
return { startedAt, finishedAt: await ctx.now(), problems, resources: results };
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* A host's source: the clone URL its data file gives, or, when it names a declared repository
|
|
157
|
+
* (`target`), the first of the reference's `by` fields that holds one in that repository's state,
|
|
158
|
+
* read in this run.
|
|
159
|
+
*/
|
|
160
|
+
export function sourceUrl(desired, target, { host, fields }, found) {
|
|
161
|
+
const path = host.inputs.source;
|
|
162
|
+
const named = target === undefined ? undefined : found.get(target);
|
|
163
|
+
const by = fields.references?.[path]?.by ?? [];
|
|
164
|
+
const values = target === undefined ? [valueAt(desired, path)] : named && "state" in named ? by.map((f) => named.state[f]) : [];
|
|
165
|
+
return values.find((v) => typeof v === "string" && CLONE_URL.test(v));
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=drift.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drift.js","sourceRoot":"","sources":["../src/drift.ts"],"names":[],"mappings":"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"}
|
package/dist/env.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Driver, DriverEnv } from "./op.ts";
|
|
2
|
+
/** One environment variable a driver declares in `env`, and whether its value lets the driver run: never the value. */
|
|
3
|
+
export interface CredentialStatus {
|
|
4
|
+
/** The variable's name, such as `GHOST_ADMIN_API_KEY`. */
|
|
5
|
+
name: string;
|
|
6
|
+
/** Its one-line manual, from its schema's `.describe()`. */
|
|
7
|
+
description?: string;
|
|
8
|
+
/** It may be unset. */
|
|
9
|
+
optional: boolean;
|
|
10
|
+
/** `missing` when unset or empty; `invalid` when its schema refuses the value. */
|
|
11
|
+
status: "set" | "missing" | "invalid";
|
|
12
|
+
/** Why it is `invalid`: the schema's first issue, never the value. */
|
|
13
|
+
problem?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Where the value is from, when there is one: the process environment, which wins, or the
|
|
16
|
+
* credentials stored with `SanomaClient.setCredential`. Only `SanomaClient.credentials` says.
|
|
17
|
+
*/
|
|
18
|
+
source?: "environment" | "stored";
|
|
19
|
+
/** Who set the stored value (`setCredential`'s `by`). */
|
|
20
|
+
setBy?: string;
|
|
21
|
+
/** When the stored value was set, as an ISO 8601 time. */
|
|
22
|
+
setAt?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Each vendor's driver's `env` (a resolved config's `driversByVendor`), by vendor in the map's
|
|
26
|
+
* order (a vendor whose driver declares none has no entry), each variable in declared order and
|
|
27
|
+
* checked against the value `lookup` gives for its name: only the declared names are looked up,
|
|
28
|
+
* an empty value counts as unset, and a value is never kept. The one walk of the declarations:
|
|
29
|
+
* the worker, the client and its checks all read them through it.
|
|
30
|
+
*/
|
|
31
|
+
export declare function credentialsOf(drivers: ReadonlyMap<string, Driver>, lookup: (name: string) => string | undefined): Map<string, CredentialStatus[]>;
|
|
32
|
+
/** One driver's `env`, each variable checked against `lookup`'s value, as `credentialsOf` checks it. */
|
|
33
|
+
export declare function statusesOf(env: DriverEnv, lookup: (name: string) => string | undefined): CredentialStatus[];
|
|
34
|
+
/**
|
|
35
|
+
* Why the vendor's drivers cannot run on `statuses`, as a call to one fails (`startWorker` names
|
|
36
|
+
* the vendor alone, `unconfiguredSummary`): `<vendor> is not configured: <problems>; <hint>`, a
|
|
37
|
+
* missing variable (not optional) as `NAME (description)`, an invalid one as `NAME is invalid:
|
|
38
|
+
* <why>`, never a value. The hint says
|
|
39
|
+
* where to set them: in the environment when an invalid value is there (the environment wins
|
|
40
|
+
* over a stored value, so the connector page cannot replace it), else on the connector page.
|
|
41
|
+
* Undefined when the drivers can run.
|
|
42
|
+
*/
|
|
43
|
+
export declare function unconfigured(vendor: string, statuses: readonly CredentialStatus[]): string | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* The one line `startWorker` prints for the vendors whose drivers cannot run yet (`vendors`, those
|
|
46
|
+
* `unconfigured` has a message for) among the config's `total`: their names alone, since a call
|
|
47
|
+
* to one fails with its own message, and its connector page says the same. Undefined for none.
|
|
48
|
+
*/
|
|
49
|
+
export declare function unconfiguredSummary(vendors: readonly string[], total: number): string | undefined;
|
package/dist/env.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { credentialReady } from "./shared.js";
|
|
2
|
+
/**
|
|
3
|
+
* Each vendor's driver's `env` (a resolved config's `driversByVendor`), by vendor in the map's
|
|
4
|
+
* order (a vendor whose driver declares none has no entry), each variable in declared order and
|
|
5
|
+
* checked against the value `lookup` gives for its name: only the declared names are looked up,
|
|
6
|
+
* an empty value counts as unset, and a value is never kept. The one walk of the declarations:
|
|
7
|
+
* the worker, the client and its checks all read them through it.
|
|
8
|
+
*/
|
|
9
|
+
export function credentialsOf(drivers, lookup) {
|
|
10
|
+
const map = new Map();
|
|
11
|
+
for (const [vendor, { env }] of drivers)
|
|
12
|
+
if (env)
|
|
13
|
+
map.set(vendor, statusesOf(env, lookup));
|
|
14
|
+
return map;
|
|
15
|
+
}
|
|
16
|
+
/** One driver's `env`, each variable checked against `lookup`'s value, as `credentialsOf` checks it. */
|
|
17
|
+
export function statusesOf(env, lookup) {
|
|
18
|
+
return Object.entries(env.shape).map(([name, schema]) => credentialStatus(name, schema, lookup(name) || undefined));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Why the vendor's drivers cannot run on `statuses`, as a call to one fails (`startWorker` names
|
|
22
|
+
* the vendor alone, `unconfiguredSummary`): `<vendor> is not configured: <problems>; <hint>`, a
|
|
23
|
+
* missing variable (not optional) as `NAME (description)`, an invalid one as `NAME is invalid:
|
|
24
|
+
* <why>`, never a value. The hint says
|
|
25
|
+
* where to set them: in the environment when an invalid value is there (the environment wins
|
|
26
|
+
* over a stored value, so the connector page cannot replace it), else on the connector page.
|
|
27
|
+
* Undefined when the drivers can run.
|
|
28
|
+
*/
|
|
29
|
+
export function unconfigured(vendor, statuses) {
|
|
30
|
+
const unready = statuses.filter((c) => !credentialReady(c));
|
|
31
|
+
if (!unready.length)
|
|
32
|
+
return undefined;
|
|
33
|
+
const problems = unready.map((c) => c.status === "invalid"
|
|
34
|
+
? `${c.name} is invalid: ${c.problem}`
|
|
35
|
+
: `${c.name}${c.description ? ` (${c.description})` : ""}`);
|
|
36
|
+
const inEnvironment = unready.some((c) => c.status === "invalid" && process.env[c.name]);
|
|
37
|
+
const hint = inEnvironment ? "set in the environment" : "set it on the connector page";
|
|
38
|
+
return `${vendor} is not configured: ${problems.join(", ")}; ${hint}`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The one line `startWorker` prints for the vendors whose drivers cannot run yet (`vendors`, those
|
|
42
|
+
* `unconfigured` has a message for) among the config's `total`: their names alone, since a call
|
|
43
|
+
* to one fails with its own message, and its connector page says the same. Undefined for none.
|
|
44
|
+
*/
|
|
45
|
+
export function unconfiguredSummary(vendors, total) {
|
|
46
|
+
if (!vendors.length)
|
|
47
|
+
return undefined;
|
|
48
|
+
return (`${vendors.length} of ${total} connectors not configured yet: ${vendors.join(", ")}; ` +
|
|
49
|
+
"set their variables on the connector pages or in the environment");
|
|
50
|
+
}
|
|
51
|
+
function credentialStatus(name, schema, value) {
|
|
52
|
+
const description = descriptionOf(schema);
|
|
53
|
+
const parsed = value === undefined ? undefined : check(schema, value);
|
|
54
|
+
const problem = parsed?.problem;
|
|
55
|
+
return {
|
|
56
|
+
name,
|
|
57
|
+
...(description !== undefined && { description }),
|
|
58
|
+
optional: check(schema, undefined).success,
|
|
59
|
+
status: !parsed ? "missing" : parsed.success ? "set" : "invalid",
|
|
60
|
+
...(problem !== undefined && { problem }),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* `schema.safeParse(value)`, with its first issue's message as the problem. A check that throws
|
|
65
|
+
* (zod lets a `.refine()` or `.transform()` throw) fails without its error, which may carry the value.
|
|
66
|
+
*/
|
|
67
|
+
function check(schema, value) {
|
|
68
|
+
try {
|
|
69
|
+
const { success, error } = schema.safeParse(value);
|
|
70
|
+
return { success, ...(error && { problem: error.issues[0].message }) };
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return { success: false, problem: "its check threw" };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** A schema's `.describe()`, also through `.optional()`, `.default()` and `.prefault()` in any order. */
|
|
77
|
+
function descriptionOf(schema) {
|
|
78
|
+
let s = schema;
|
|
79
|
+
while (s && !s.description)
|
|
80
|
+
s = "unwrap" in s ? s.unwrap() : undefined;
|
|
81
|
+
return s?.description;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=env.js.map
|
package/dist/env.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type { Principal } from "./define.ts";
|
|
3
|
+
declare const CODES: readonly ["policy_denied", "approval_rejected", "not_approver", "no_pending_approval", "already_decided", "run_not_found", "driver_failed", "invalid_input", "run_ended", "run_running", "call_not_in_outline"];
|
|
4
|
+
/**
|
|
5
|
+
* Why something failed, as a stable string a caller can branch on. Messages are for people
|
|
6
|
+
* and may change; codes are the contract.
|
|
7
|
+
*/
|
|
8
|
+
export type ErrorCode = (typeof CODES)[number];
|
|
9
|
+
/**
|
|
10
|
+
* An error with a `code` and plain-JSON `data`, both own enumerable properties, so they survive
|
|
11
|
+
* the trip through DBOS: a run's error is stored serialized and comes back as a new object that
|
|
12
|
+
* is not an instance of this class. Read the code with `errorCode`, never `instanceof`.
|
|
13
|
+
*/
|
|
14
|
+
export declare class SanomaError extends Error {
|
|
15
|
+
readonly code: ErrorCode;
|
|
16
|
+
readonly data: Record<string, unknown>;
|
|
17
|
+
constructor(code: ErrorCode, message: string, data?: Record<string, unknown>, options?: ErrorOptions);
|
|
18
|
+
}
|
|
19
|
+
/** The error's code, read from its own `code` property, or undefined when it has none of ours. */
|
|
20
|
+
export declare function errorCode(e: unknown): ErrorCode | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* True when the error says trying again would only repeat it (`retryable: false`), as a
|
|
23
|
+
* `DriverError` for a vendor's final answer or a ledger store for a corrupt file does. Read as
|
|
24
|
+
* a property, never `instanceof`: the error may come from another copy of this package.
|
|
25
|
+
*/
|
|
26
|
+
export declare const isFinal: (err: unknown) => boolean;
|
|
27
|
+
/** The error and the ones it was caused by, a few deep. */
|
|
28
|
+
export declare function causes(err: unknown): Error[];
|
|
29
|
+
/**
|
|
30
|
+
* True for a failure that ends the run rather than one call: anything DBOS throws (it cancelled
|
|
31
|
+
* the run, another process took it over, the worker is shutting down, the run is not
|
|
32
|
+
* deterministic) and `run_ended` (a call queued after the run's body ended). Looked for in the
|
|
33
|
+
* error and its causes. A workflow that goes on past a failed call, as `drift` does, rethrows these.
|
|
34
|
+
*/
|
|
35
|
+
export declare const isRunControlError: (err: unknown) => boolean;
|
|
36
|
+
/** Thrown into the run when the policy denies an operation call. The run fails with it. */
|
|
37
|
+
export declare class PolicyDeniedError extends SanomaError {
|
|
38
|
+
constructor(op: string, reason: string);
|
|
39
|
+
}
|
|
40
|
+
/** Thrown into the run when an approver rejects. The run fails with it. */
|
|
41
|
+
export declare class RejectedError extends SanomaError {
|
|
42
|
+
/**
|
|
43
|
+
* @param title The approval's title.
|
|
44
|
+
* @param by Who rejected it.
|
|
45
|
+
* @param approvalId The approval's id in the run, such as "approval-2".
|
|
46
|
+
*/
|
|
47
|
+
constructor(title: string, by: Principal, note: string | undefined, approvalId: string);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The wrapper, given the original's own `code` and `data` when it has one of our codes, so
|
|
51
|
+
* `errorCode` reads the same through it: a denied call whose ledger write failed too is still
|
|
52
|
+
* `policy_denied`.
|
|
53
|
+
*/
|
|
54
|
+
export declare function keepCode<E extends Error>(wrapper: E, original: unknown): E;
|
|
55
|
+
/** A zod issue as the ledger and an API can carry it: no symbols in the path. */
|
|
56
|
+
export interface InputIssue {
|
|
57
|
+
path: (string | number)[];
|
|
58
|
+
message: string;
|
|
59
|
+
code: string;
|
|
60
|
+
}
|
|
61
|
+
/** The value, parsed by the schema, or an `invalid_input` error carrying zod's issues and `data`. */
|
|
62
|
+
export declare function parseOrThrow<T extends z.ZodType>(schema: T, value: unknown, what: string, data?: Record<string, unknown>): z.output<T>;
|
|
63
|
+
/** An `invalid_input` error from zod issues, with the issues in `data` and a one-line message. */
|
|
64
|
+
export declare function invalidInput(what: string, zodIssues: readonly {
|
|
65
|
+
path: PropertyKey[];
|
|
66
|
+
message: string;
|
|
67
|
+
code: string;
|
|
68
|
+
}[], data?: Record<string, unknown>): SanomaError;
|
|
69
|
+
/**
|
|
70
|
+
* What the ledger keeps of an error. `status`, `vendorCode` and `retryable` are a
|
|
71
|
+
* `DriverError`'s account of the vendor's answer; `data` is one of the runtime's errors'. Each is
|
|
72
|
+
* there only when the error has it, and `data`, `status` and `vendorCode` only from those errors.
|
|
73
|
+
*/
|
|
74
|
+
export interface ErrorInfo {
|
|
75
|
+
code?: ErrorCode;
|
|
76
|
+
name: string;
|
|
77
|
+
message: string;
|
|
78
|
+
/** The vendor's HTTP status, from a `DriverError`. */
|
|
79
|
+
status?: number;
|
|
80
|
+
/** The vendor's own error code, from a `DriverError`. */
|
|
81
|
+
vendorCode?: string;
|
|
82
|
+
/** Whether trying again could succeed, from a `DriverError` or a ledger store. */
|
|
83
|
+
retryable?: boolean;
|
|
84
|
+
/** A `SanomaError`'s `data`: the operation, the approval, the issues. */
|
|
85
|
+
data?: Record<string, unknown>;
|
|
86
|
+
}
|
|
87
|
+
export declare function errorInfo(err: unknown): ErrorInfo;
|
|
88
|
+
export {};
|