@sanoma-ai/workflows 0.1.0 → 0.4.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/README.md +186 -76
- 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 +11 -6
- package/dist/approvals.js +38 -10
- package/dist/approvals.js.map +1 -1
- package/dist/call-workflow.d.ts +16 -0
- package/dist/call-workflow.js +35 -0
- package/dist/call-workflow.js.map +1 -0
- package/dist/call.d.ts +35 -2
- package/dist/call.js +123 -43
- package/dist/call.js.map +1 -1
- package/dist/cedar.d.ts +87 -0
- package/dist/cedar.js +365 -0
- package/dist/cedar.js.map +1 -0
- package/dist/client.d.ts +123 -9
- package/dist/client.js +355 -34
- package/dist/client.js.map +1 -1
- package/dist/collection.d.ts +107 -0
- package/dist/collection.js +306 -0
- package/dist/collection.js.map +1 -0
- package/dist/config.d.ts +42 -14
- package/dist/config.js +109 -28
- package/dist/config.js.map +1 -1
- package/dist/constructors.d.ts +2 -0
- package/dist/constructors.js +5 -0
- package/dist/constructors.js.map +1 -0
- package/dist/credentials.d.ts +25 -0
- package/dist/credentials.js +73 -5
- package/dist/credentials.js.map +1 -1
- package/dist/data.d.ts +2 -0
- package/dist/data.js +3 -0
- package/dist/data.js.map +1 -0
- package/dist/datafile.d.ts +38 -3
- package/dist/datafile.js +166 -13
- package/dist/datafile.js.map +1 -1
- package/dist/db.d.ts +5 -3
- package/dist/db.js +88 -21
- package/dist/db.js.map +1 -1
- package/dist/define.d.ts +10 -4
- package/dist/define.js +1 -5
- package/dist/define.js.map +1 -1
- package/dist/describe.d.ts +54 -8
- package/dist/describe.js +84 -11
- package/dist/describe.js.map +1 -1
- package/dist/direct.d.ts +9 -0
- package/dist/direct.js +11 -0
- package/dist/direct.js.map +1 -0
- package/dist/drift.d.ts +64 -12
- package/dist/drift.js +118 -41
- package/dist/drift.js.map +1 -1
- package/dist/errors.d.ts +16 -1
- package/dist/errors.js +30 -1
- package/dist/errors.js.map +1 -1
- package/dist/expectations.d.ts +4 -4
- package/dist/expectations.js +43 -9
- package/dist/expectations.js.map +1 -1
- 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 +13 -5
- package/dist/index.js +15 -5
- package/dist/index.js.map +1 -1
- package/dist/ledger.d.ts +12 -1
- package/dist/ledger.js +18 -11
- package/dist/ledger.js.map +1 -1
- package/dist/lint.d.ts +5 -4
- package/dist/lint.js +24 -7
- package/dist/lint.js.map +1 -1
- package/dist/lockfile.d.ts +36 -0
- package/dist/lockfile.js +69 -0
- package/dist/lockfile.js.map +1 -0
- package/dist/op.d.ts +9 -0
- package/dist/op.js.map +1 -1
- package/dist/outline.d.ts +2 -0
- package/dist/outline.js +1 -1
- package/dist/outline.js.map +1 -1
- package/dist/paths.d.ts +4 -0
- package/dist/paths.js +17 -3
- package/dist/paths.js.map +1 -1
- package/dist/person.d.ts +61 -0
- package/dist/person.js +69 -0
- package/dist/person.js.map +1 -0
- package/dist/plugin.js +1 -1
- package/dist/plugin.js.map +1 -1
- package/dist/policy.d.ts +51 -55
- package/dist/policy.js +14 -36
- package/dist/policy.js.map +1 -1
- package/dist/resource.d.ts +207 -25
- package/dist/resource.js +184 -30
- package/dist/resource.js.map +1 -1
- package/dist/resources.d.ts +76 -9
- package/dist/resources.js +170 -30
- package/dist/resources.js.map +1 -1
- package/dist/run.d.ts +31 -1
- package/dist/run.js +6 -1
- package/dist/run.js.map +1 -1
- package/dist/sandbox.js +5 -1
- package/dist/sandbox.js.map +1 -1
- package/dist/scenario.d.ts +25 -9
- package/dist/scenario.js +125 -27
- package/dist/scenario.js.map +1 -1
- package/dist/scenarios.d.ts +10 -6
- package/dist/scenarios.js +8 -4
- package/dist/scenarios.js.map +1 -1
- package/dist/schema.d.ts +32 -0
- package/dist/schema.js +225 -0
- package/dist/schema.js.map +1 -0
- package/dist/sdk.d.ts +34 -0
- package/dist/sdk.js +168 -0
- package/dist/sdk.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 +188 -4
- package/dist/shared.js +165 -5
- package/dist/shared.js.map +1 -1
- package/dist/sites.d.ts +52 -0
- package/dist/sites.js +174 -0
- package/dist/sites.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/version.d.ts +3 -2
- package/dist/version.js +3 -2
- package/dist/version.js.map +1 -1
- package/dist/worker.d.ts +6 -2
- package/dist/worker.js +37 -8
- package/dist/worker.js.map +1 -1
- package/dist/writer.d.ts +35 -0
- package/dist/writer.js +201 -0
- package/dist/writer.js.map +1 -0
- package/oxlint.json +5 -16
- package/package.json +11 -1
- package/src/apply-steps.ts +110 -0
- package/src/apply.ts +626 -0
- package/src/approvals.ts +49 -13
- package/src/call-workflow.ts +44 -0
- package/src/call.ts +173 -46
- package/src/cedar.ts +493 -0
- package/src/client.ts +450 -49
- package/src/collection.ts +431 -0
- package/src/config.ts +168 -48
- package/src/constructors.ts +4 -0
- package/src/credentials.ts +90 -5
- package/src/data.ts +10 -0
- package/src/datafile.ts +207 -15
- package/src/db.ts +90 -22
- package/src/define.ts +11 -9
- package/src/describe.ts +146 -23
- package/src/direct.ts +15 -0
- package/src/drift.ts +155 -45
- package/src/errors.ts +36 -1
- package/src/expectations.ts +46 -11
- package/src/host.ts +96 -0
- package/src/index.ts +56 -2
- package/src/ledger.ts +35 -17
- package/src/lint.ts +29 -12
- package/src/lockfile.ts +95 -0
- package/src/op.ts +9 -0
- package/src/outline.ts +1 -1
- package/src/paths.ts +18 -3
- package/src/person.ts +75 -0
- package/src/plugin.ts +1 -1
- package/src/policy.ts +59 -63
- package/src/resource.ts +302 -52
- package/src/resources.ts +226 -30
- package/src/run.ts +33 -1
- package/src/sandbox.ts +5 -1
- package/src/scenario.ts +175 -43
- package/src/scenarios.ts +15 -8
- package/src/schema.ts +261 -0
- package/src/sdk.ts +211 -0
- package/src/secret.ts +92 -0
- package/src/shared.ts +279 -6
- package/src/sites.ts +220 -0
- package/src/source.ts +82 -0
- package/src/version.ts +3 -2
- package/src/worker.ts +45 -11
- package/src/writer.ts +225 -0
package/dist/direct.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { isResourceOp } from "./resource.js";
|
|
2
|
+
import { DATA_VENDOR } from "./shared.js";
|
|
3
|
+
/**
|
|
4
|
+
* Whether an operation may be called directly, outside a run (`SanomaClient.call`), as the
|
|
5
|
+
* person: a collection's (`data.<name>.<op>`), or a `read` that is neither the runtime's own
|
|
6
|
+
* (the vendor `sanoma`) nor a resource type's (`isResourceOp`), which only drift and apply call.
|
|
7
|
+
* Anything else (an effect at a vendor) is a workflow's. The one rule: the built-in `call`
|
|
8
|
+
* workflow, the SDK and the app read it, from here or from `OpEntry.direct`, and never derive it.
|
|
9
|
+
*/
|
|
10
|
+
export const isDirect = (op, types) => op.vendor === DATA_VENDOR || (op.effect === "read" && op.vendor !== "sanoma" && !isResourceOp(op, types));
|
|
11
|
+
//# sourceMappingURL=direct.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"direct.js","sourceRoot":"","sources":["../src/direct.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CACtB,EAA8C,EAC9C,KAAmC,EAC1B,EAAE,CAAC,EAAE,CAAC,MAAM,KAAK,WAAW,IAAI,CAAC,EAAE,CAAC,MAAM,KAAK,MAAM,IAAI,EAAE,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC"}
|
package/dist/drift.d.ts
CHANGED
|
@@ -3,14 +3,16 @@ import { type WorkflowDefinition } from "./define.ts";
|
|
|
3
3
|
import type { Op } from "./op.ts";
|
|
4
4
|
import { type DriftField, type Resource } from "./resource.ts";
|
|
5
5
|
import type { DeclaredResource, ResourceProblem } from "./resources.ts";
|
|
6
|
+
import { type sourceOps } from "./source.ts";
|
|
6
7
|
export type { DriftField } from "./resource.ts";
|
|
7
8
|
export { DRIFT_WORKFLOW } from "./shared.ts";
|
|
8
9
|
/**
|
|
9
|
-
* How a declared resource compares: `clean` when every declared field
|
|
10
|
-
* `drifted` when one does not, `gone` when the vendor
|
|
11
|
-
*
|
|
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).
|
|
12
14
|
*/
|
|
13
|
-
export type DriftStatus = "clean" | "drifted" | "gone" | "error";
|
|
15
|
+
export type DriftStatus = "clean" | "drifted" | "gone" | "absent" | "error";
|
|
14
16
|
/** One declared resource in a drift report. */
|
|
15
17
|
export interface DriftResult {
|
|
16
18
|
/** The resource's id, `<file>#<export>`: `resources/identity/github.ts#website`. */
|
|
@@ -18,9 +20,13 @@ export interface DriftResult {
|
|
|
18
20
|
/** Its type within the vendor, such as `repository`. */
|
|
19
21
|
type: string;
|
|
20
22
|
vendor: string;
|
|
21
|
-
/**
|
|
23
|
+
/** What people call it: its identity, else its export's name. */
|
|
22
24
|
name: string;
|
|
23
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;
|
|
24
30
|
/** The declared fields that differ; empty unless `drifted`. */
|
|
25
31
|
fields: DriftField[];
|
|
26
32
|
/** Why it could not be read, for `error`. */
|
|
@@ -47,7 +53,7 @@ export declare const DriftInput: z.ZodObject<{}, z.core.$strict>;
|
|
|
47
53
|
* write-only values, and the problems.
|
|
48
54
|
*/
|
|
49
55
|
export interface DriftDeclared {
|
|
50
|
-
resources: Pick<DeclaredResource, "id" | "vendor" | "type" | "name" | "desired" | "refs">[];
|
|
56
|
+
resources: Pick<DeclaredResource, "id" | "vendor" | "type" | "name" | "identity" | "binding" | "desired" | "refs">[];
|
|
51
57
|
problems: ResourceProblem[];
|
|
52
58
|
}
|
|
53
59
|
type State = Record<string, unknown>;
|
|
@@ -58,18 +64,64 @@ type ImportOp = Op<string, string, "import", {
|
|
|
58
64
|
gone: boolean;
|
|
59
65
|
state?: State;
|
|
60
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[];
|
|
61
105
|
/**
|
|
62
106
|
* The drift workflow for a config: it `uses` the `import` of every resource type the connectors
|
|
63
107
|
* declare (`types`) that has a driver, so its version changes with them, not with the data
|
|
64
108
|
* files. `resolveConfig` adds it to a config whose connectors declare resource types, with
|
|
65
109
|
* `declared`, the step that reads the data files.
|
|
66
110
|
*
|
|
67
|
-
* A run reads the data files (`declared`, recorded as a step, so a replay checks
|
|
68
|
-
* then imports each resource by its
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
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
|
|
72
118
|
* `error`, and the run goes on to the next; a failure that ends the run (`isRunControlError`)
|
|
73
119
|
* ends it.
|
|
74
120
|
*/
|
|
75
|
-
export declare function driftWorkflow(types: ReadonlyMap<string, Resource>, ops: ReadonlyMap<string, Op>, drivers: ReadonlyMap<string, unknown>, declared: () => Promise<DriftDeclared>): WorkflowDefinition<readonly ImportOp[], typeof DriftInput>;
|
|
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
CHANGED
|
@@ -1,39 +1,106 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { defineWorkflow } from "./define.js";
|
|
3
3
|
import { isRunControlError } from "./errors.js";
|
|
4
|
-
import { diffDeclared } from "./resource.js";
|
|
4
|
+
import { diffDeclared, fieldPathOf, valueAt } from "./resource.js";
|
|
5
5
|
import { errorMessage } from "./shared.js";
|
|
6
|
+
import { SOURCE_RESOLVE } from "./source.js";
|
|
6
7
|
export { DRIFT_WORKFLOW } from "./shared.js";
|
|
7
8
|
/**
|
|
8
9
|
* The drift workflow's input: nothing. The run reads the data files itself, so a run says what
|
|
9
10
|
* they declared when it ran, and no caller can hand it other resources to check.
|
|
10
11
|
*/
|
|
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
|
+
}
|
|
12
84
|
/**
|
|
13
85
|
* The drift workflow for a config: it `uses` the `import` of every resource type the connectors
|
|
14
86
|
* declare (`types`) that has a driver, so its version changes with them, not with the data
|
|
15
87
|
* files. `resolveConfig` adds it to a config whose connectors declare resource types, with
|
|
16
88
|
* `declared`, the step that reads the data files.
|
|
17
89
|
*
|
|
18
|
-
* A run reads the data files (`declared`, recorded as a step, so a replay checks
|
|
19
|
-
* then imports each resource by its
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
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
|
|
23
97
|
* `error`, and the run goes on to the next; a failure that ends the run (`isRunControlError`)
|
|
24
98
|
* ends it.
|
|
25
99
|
*/
|
|
26
100
|
export function driftWorkflow(types, ops, drivers, declared) {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
});
|
|
31
|
-
// Why a resource of each type cannot be read, if it cannot.
|
|
32
|
-
const unreadable = new Map();
|
|
33
|
-
for (const type of types.keys()) {
|
|
34
|
-
if (!uses.some((op) => op.id === `${type}.import`))
|
|
35
|
-
unreadable.set(type, `no driver for ${type}.import`);
|
|
36
|
-
}
|
|
101
|
+
const imports = importsOf(types, ops, drivers);
|
|
102
|
+
const resolve = ops.get(SOURCE_RESOLVE);
|
|
103
|
+
const uses = [...imports, ...(resolve ? [resolve] : [])];
|
|
37
104
|
return defineWorkflow({
|
|
38
105
|
// The literal, held to the one name: the outline finds a workflow by a string, or a const of its own file.
|
|
39
106
|
name: "drift",
|
|
@@ -46,45 +113,55 @@ export function driftWorkflow(types, ops, drivers, declared) {
|
|
|
46
113
|
const { resources, problems } = await declared();
|
|
47
114
|
// Every import first, so a reference compares against what its resource holds now.
|
|
48
115
|
const found = new Map();
|
|
49
|
-
for (const
|
|
50
|
-
const
|
|
51
|
-
const why = types.has(key) ? unreadable.get(key) : `the config's connectors declare no resource type ${key}`;
|
|
116
|
+
for (const resource of resources) {
|
|
117
|
+
const by = observeBy(resource, types, imports);
|
|
52
118
|
// An if/else, not a `continue`: the outline draws the import as the way past the error.
|
|
53
|
-
if (
|
|
54
|
-
found.set(id,
|
|
119
|
+
if (!("id" in by))
|
|
120
|
+
found.set(resource.id, by);
|
|
55
121
|
else {
|
|
56
122
|
try {
|
|
57
|
-
|
|
58
|
-
found.set(id, out.gone || !out.state ? { gone: true } : { state: out.state });
|
|
123
|
+
found.set(resource.id, observed(await ctx[resource.vendor][resource.type].import({ id: by.id })));
|
|
59
124
|
}
|
|
60
125
|
catch (err) {
|
|
61
126
|
if (isRunControlError(err))
|
|
62
127
|
throw err;
|
|
63
|
-
found.set(id, { error: errorMessage(err) });
|
|
128
|
+
found.set(resource.id, { error: errorMessage(err) });
|
|
64
129
|
}
|
|
65
130
|
}
|
|
66
131
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if ("error" in now)
|
|
71
|
-
return { ...row, status: "error", fields: [], error: now.error };
|
|
72
|
-
if ("gone" in now)
|
|
73
|
-
return { ...row, status: "gone", fields: [] };
|
|
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) {
|
|
74
135
|
const resource = types.get(`${vendor}.${type}`);
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
+
}
|
|
82
148
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
});
|
|
149
|
+
}
|
|
150
|
+
const results = compare(types, resources, found, revisions);
|
|
86
151
|
return { startedAt, finishedAt: await ctx.now(), problems, resources: results };
|
|
87
152
|
},
|
|
88
153
|
});
|
|
89
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
|
+
}
|
|
90
167
|
//# sourceMappingURL=drift.js.map
|
package/dist/drift.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drift.js","sourceRoot":"","sources":["../src/drift.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAA2B,MAAM,aAAa,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAmB,YAAY,EAAiB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"drift.js","sourceRoot":"","sources":["../src/drift.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { z } from "zod";
|
|
2
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"];
|
|
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", "approval_pending", "not_admitted"];
|
|
4
4
|
/**
|
|
5
5
|
* Why something failed, as a stable string a caller can branch on. Messages are for people
|
|
6
6
|
* and may change; codes are the contract.
|
|
@@ -37,6 +37,21 @@ export declare const isRunControlError: (err: unknown) => boolean;
|
|
|
37
37
|
export declare class PolicyDeniedError extends SanomaError {
|
|
38
38
|
constructor(op: string, reason: string);
|
|
39
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Thrown by a direct call (`SanomaClient.call`) the policy holds for an approval: the call is now
|
|
42
|
+
* run `runId` of the built-in `call` workflow, which makes it once approved.
|
|
43
|
+
*/
|
|
44
|
+
export declare class ApprovalPendingError extends SanomaError {
|
|
45
|
+
constructor(op: string, runId: string);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Thrown when the data files declare people (`sanoma.person`, in `resources/people.ts` by
|
|
49
|
+
* convention) and not this principal's id: nothing is started, called or decided.
|
|
50
|
+
*/
|
|
51
|
+
export declare class NotAdmittedError extends SanomaError {
|
|
52
|
+
/** `problems`: what is wrong in the people's files, while nobody is admitted. */
|
|
53
|
+
constructor(id: string, problems?: readonly string[]);
|
|
54
|
+
}
|
|
40
55
|
/** Thrown into the run when an approver rejects. The run fails with it. */
|
|
41
56
|
export declare class RejectedError extends SanomaError {
|
|
42
57
|
/**
|
package/dist/errors.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Error as DBOSErrors } from "@dbos-inc/dbos-sdk";
|
|
2
|
-
import { errorMessage } from "./shared.js";
|
|
2
|
+
import { CALL_WORKFLOW, errorMessage } from "./shared.js";
|
|
3
3
|
const CODES = [
|
|
4
4
|
"policy_denied",
|
|
5
5
|
"approval_rejected",
|
|
@@ -12,6 +12,8 @@ const CODES = [
|
|
|
12
12
|
"run_ended",
|
|
13
13
|
"run_running",
|
|
14
14
|
"call_not_in_outline",
|
|
15
|
+
"approval_pending",
|
|
16
|
+
"not_admitted",
|
|
15
17
|
];
|
|
16
18
|
const KNOWN = new Set(CODES);
|
|
17
19
|
/**
|
|
@@ -61,6 +63,33 @@ export class PolicyDeniedError extends SanomaError {
|
|
|
61
63
|
this.name = "PolicyDeniedError";
|
|
62
64
|
}
|
|
63
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Thrown by a direct call (`SanomaClient.call`) the policy holds for an approval: the call is now
|
|
68
|
+
* run `runId` of the built-in `call` workflow, which makes it once approved.
|
|
69
|
+
*/
|
|
70
|
+
export class ApprovalPendingError extends SanomaError {
|
|
71
|
+
constructor(op, runId) {
|
|
72
|
+
super("approval_pending", `${op} needs an approval: it waits in run ${runId}`, {
|
|
73
|
+
runId,
|
|
74
|
+
workflow: CALL_WORKFLOW,
|
|
75
|
+
op,
|
|
76
|
+
});
|
|
77
|
+
this.name = "ApprovalPendingError";
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Thrown when the data files declare people (`sanoma.person`, in `resources/people.ts` by
|
|
82
|
+
* convention) and not this principal's id: nothing is started, called or decided.
|
|
83
|
+
*/
|
|
84
|
+
export class NotAdmittedError extends SanomaError {
|
|
85
|
+
/** `problems`: what is wrong in the people's files, while nobody is admitted. */
|
|
86
|
+
constructor(id, problems = []) {
|
|
87
|
+
super("not_admitted", problems.length
|
|
88
|
+
? `${id} cannot be admitted while the people's files have problems: ${problems.join("; ")}`
|
|
89
|
+
: `${id} is not declared in resources/people.ts`, { principal: id, ...(problems.length && { problems }) });
|
|
90
|
+
this.name = "NotAdmittedError";
|
|
91
|
+
}
|
|
92
|
+
}
|
|
64
93
|
/** Thrown into the run when an approver rejects. The run fails with it. */
|
|
65
94
|
export class RejectedError extends SanomaError {
|
|
66
95
|
/**
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGzD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
package/dist/expectations.d.ts
CHANGED
|
@@ -16,10 +16,10 @@ type Unstepped<T> = T extends unknown ? Omit<T, "step"> : never;
|
|
|
16
16
|
* Why an expectation of the generic steps does not hold against a run's ledger records, or
|
|
17
17
|
* undefined when it does. A call is an `op.called` record without an `error`; one with an error
|
|
18
18
|
* (a policy denial, a rejected approval, a vendor's final failure) was attempted, not made. An
|
|
19
|
-
* operation `was denied` when the policy's decision on some call to it is `deny
|
|
20
|
-
*
|
|
21
|
-
* call, or one that `covers` it. A drift expectation reads the report a finished
|
|
22
|
-
* returned. A step named in the workflow's outline is read by the `name` its records carry
|
|
19
|
+
* operation `was denied` when the policy's decision on some call to it is `deny` (for the `reason`
|
|
20
|
+
* expected, when there is one), and `was held for` an approver (a group alone, as `{ group }`) when
|
|
21
|
+
* an approval for that approver holds it: one the policy asked for on its call, or one that `covers` it. A drift or apply expectation reads the report a finished run
|
|
22
|
+
* of it returned. A step named in the workflow's outline is read by the `name` its records carry
|
|
23
23
|
* (its calls made, and `waitsOf`); a record written before steps had names has none.
|
|
24
24
|
*/
|
|
25
25
|
export declare function detailOf(e: CoreExpectation, records: readonly LedgerRecord[]): string | undefined;
|
package/dist/expectations.js
CHANGED
|
@@ -54,8 +54,12 @@ const inputs = (list) => list.map((r) => JSON.stringify(r.input)).join(", ");
|
|
|
54
54
|
const words = (d) => d.kind === "allow" ? "allowed" : d.kind === "deny" ? "denied" : `held for ${approverLabel(d.approver)}`;
|
|
55
55
|
/** What the policy decided for the calls, each decision once: `allowed and held for finance`. */
|
|
56
56
|
const decisions = (calls) => [...new Set(calls.map((r) => words(r.decision)))].join(" and ");
|
|
57
|
-
/** True when `who` names the approver: a person by id, or a group by its name. */
|
|
58
|
-
const isApprover = (approver, who) => typeof
|
|
57
|
+
/** True when `who` names the approver: a person by id, or a group by its name; `{ group }` the group alone. */
|
|
58
|
+
const isApprover = (approver, who) => typeof who !== "string"
|
|
59
|
+
? typeof approver !== "string" && approver.group === who.group
|
|
60
|
+
: typeof approver === "string"
|
|
61
|
+
? approver === who
|
|
62
|
+
: approver.group === who;
|
|
59
63
|
/** How a drift report found a resource, as words: `clean`, `gone`, `drifted in has_wiki, topics`. */
|
|
60
64
|
const found = (r) => r.status === "drifted" ? `drifted in ${r.fields.map((f) => f.path).join(", ")}` : r.status;
|
|
61
65
|
/**
|
|
@@ -77,14 +81,37 @@ function driftDetail(e, end) {
|
|
|
77
81
|
return undefined;
|
|
78
82
|
return `${e.declared} is ${found(resource)}, not ${e.status === "drifted" ? `drifted in ${e.field}` : e.status}`;
|
|
79
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Why an apply expectation fails against the report a finished apply run returned as its output:
|
|
86
|
+
* the resource `created` (a step that created it, done), or `bound` to an object in the lockfile it ended with.
|
|
87
|
+
*/
|
|
88
|
+
function applyDetail(e, end) {
|
|
89
|
+
const report = end.output;
|
|
90
|
+
if (!Array.isArray(report?.results) || !report.lockfile)
|
|
91
|
+
return "the run's output is not an apply report";
|
|
92
|
+
if ("bound" in e) {
|
|
93
|
+
const binding = report.lockfile.bindings[e.declared];
|
|
94
|
+
if (binding?.id === e.bound)
|
|
95
|
+
return undefined;
|
|
96
|
+
return binding ? `${e.declared} is bound to ${binding.id}, not ${e.bound}` : `${e.declared} is not bound`;
|
|
97
|
+
}
|
|
98
|
+
const result = report.results.find((r) => r.id === e.declared);
|
|
99
|
+
// A gone object recreated is bound again; one forgotten is not.
|
|
100
|
+
const created = result?.status === "done" && (result.action === "create" || (result.action === "gone" && !!result.binding));
|
|
101
|
+
if (created)
|
|
102
|
+
return undefined;
|
|
103
|
+
if (!result)
|
|
104
|
+
return `apply did nothing with ${e.declared}`;
|
|
105
|
+
return `apply's ${result.action} of ${e.declared} ${result.status === "failed" ? `failed: ${result.error}` : `was ${result.status}`}`;
|
|
106
|
+
}
|
|
80
107
|
/**
|
|
81
108
|
* Why an expectation of the generic steps does not hold against a run's ledger records, or
|
|
82
109
|
* undefined when it does. A call is an `op.called` record without an `error`; one with an error
|
|
83
110
|
* (a policy denial, a rejected approval, a vendor's final failure) was attempted, not made. An
|
|
84
|
-
* operation `was denied` when the policy's decision on some call to it is `deny
|
|
85
|
-
*
|
|
86
|
-
* call, or one that `covers` it. A drift expectation reads the report a finished
|
|
87
|
-
* returned. A step named in the workflow's outline is read by the `name` its records carry
|
|
111
|
+
* operation `was denied` when the policy's decision on some call to it is `deny` (for the `reason`
|
|
112
|
+
* expected, when there is one), and `was held for` an approver (a group alone, as `{ group }`) when
|
|
113
|
+
* an approval for that approver holds it: one the policy asked for on its call, or one that `covers` it. A drift or apply expectation reads the report a finished run
|
|
114
|
+
* of it returned. A step named in the workflow's outline is read by the `name` its records carry
|
|
88
115
|
* (its calls made, and `waitsOf`); a record written before steps had names has none.
|
|
89
116
|
*/
|
|
90
117
|
export function detailOf(e, records) {
|
|
@@ -96,15 +123,20 @@ export function detailOf(e, records) {
|
|
|
96
123
|
if ("decision" in e) {
|
|
97
124
|
const tries = callsTo(e.op);
|
|
98
125
|
if (e.decision === "denied") {
|
|
99
|
-
|
|
126
|
+
const denials = tries.flatMap(({ decision: d }) => (d.kind === "deny" ? [d.reason] : []));
|
|
127
|
+
if (denials.some((reason) => e.reason === undefined || reason === e.reason))
|
|
100
128
|
return undefined;
|
|
129
|
+
if (denials.length) {
|
|
130
|
+
const said = [...new Set(denials)].map((reason) => JSON.stringify(reason)).join(" and ");
|
|
131
|
+
return `${e.op} was denied with ${said}, not ${JSON.stringify(e.reason)}`;
|
|
132
|
+
}
|
|
101
133
|
return tries.length ? `${e.op} was ${decisions(tries)}, not denied` : `no call to ${e.op}`;
|
|
102
134
|
}
|
|
103
135
|
const holds = records.filter(isRequest).filter((r) => r.op === e.op || r.covers.includes(e.op));
|
|
104
136
|
if (holds.some((r) => isApprover(r.approver, e.approver)))
|
|
105
137
|
return undefined;
|
|
106
138
|
return holds.length
|
|
107
|
-
? `${e.op} was held for ${[...new Set(holds.map((r) => approverLabel(r.approver)))].join(" and ")}, not ${e.approver}`
|
|
139
|
+
? `${e.op} was held for ${[...new Set(holds.map((r) => approverLabel(r.approver)))].join(" and ")}, not ${approverLabel(e.approver)}`
|
|
108
140
|
: tries.length
|
|
109
141
|
? `${e.op} was ${decisions(tries)}, not held`
|
|
110
142
|
: `no call to ${e.op}`;
|
|
@@ -132,6 +164,8 @@ export function detailOf(e, records) {
|
|
|
132
164
|
// A finished run: what `the run succeeds` expects, and what a drift expectation reads its report from.
|
|
133
165
|
if (end.type === "run.failed")
|
|
134
166
|
return `the run failed: ${end.error.message}`;
|
|
135
|
-
|
|
167
|
+
if (!("declared" in e))
|
|
168
|
+
return undefined;
|
|
169
|
+
return "status" in e ? driftDetail(e, end) : applyDetail(e, end);
|
|
136
170
|
}
|
|
137
171
|
//# sourceMappingURL=expectations.js.map
|