@sanoma-ai/workflows 0.9.1 → 0.10.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 +167 -50
- package/dist/apply-steps.js +5 -2
- package/dist/apply-steps.js.map +1 -1
- package/dist/apply.d.ts +3 -0
- package/dist/apply.js +27 -13
- package/dist/apply.js.map +1 -1
- package/dist/approvals.d.ts +1 -0
- package/dist/approvals.js +6 -1
- package/dist/approvals.js.map +1 -1
- package/dist/apps.d.ts +7 -24
- package/dist/apps.js +51 -67
- package/dist/apps.js.map +1 -1
- package/dist/ast.d.ts +9 -7
- package/dist/ast.js +2 -0
- package/dist/ast.js.map +1 -1
- package/dist/authentication.d.ts +24 -0
- package/dist/authentication.js +68 -0
- package/dist/authentication.js.map +1 -0
- package/dist/call-workflow.d.ts +5 -0
- package/dist/call-workflow.js +4 -3
- package/dist/call-workflow.js.map +1 -1
- package/dist/call.d.ts +3 -2
- package/dist/call.js +34 -7
- package/dist/call.js.map +1 -1
- package/dist/cedar.js +14 -1
- package/dist/cedar.js.map +1 -1
- package/dist/change-steps.js +7 -5
- package/dist/change-steps.js.map +1 -1
- package/dist/change.d.ts +7 -0
- package/dist/change.js +7 -0
- package/dist/change.js.map +1 -1
- package/dist/client.d.ts +10 -6
- package/dist/client.js +96 -43
- package/dist/client.js.map +1 -1
- package/dist/collection.d.ts +7 -0
- package/dist/collection.js +5 -2
- package/dist/collection.js.map +1 -1
- package/dist/config.d.ts +9 -0
- package/dist/config.js +45 -9
- package/dist/config.js.map +1 -1
- package/dist/connection.d.ts +168 -0
- package/dist/connection.js +327 -0
- package/dist/connection.js.map +1 -0
- package/dist/credentials.d.ts +7 -53
- package/dist/credentials.js +84 -247
- package/dist/credentials.js.map +1 -1
- package/dist/db.js +21 -2
- package/dist/db.js.map +1 -1
- package/dist/define.d.ts +9 -2
- package/dist/define.js.map +1 -1
- package/dist/definitions.d.ts +20 -0
- package/dist/definitions.js +16 -0
- package/dist/definitions.js.map +1 -0
- package/dist/deployment-key.d.ts +2 -0
- package/dist/deployment-key.js +22 -0
- package/dist/deployment-key.js.map +1 -0
- package/dist/describe.d.ts +9 -177
- package/dist/describe.js +71 -34
- package/dist/describe.js.map +1 -1
- package/dist/description.d.ts +532 -0
- package/dist/description.js +381 -0
- package/dist/description.js.map +1 -0
- package/dist/direct.js +3 -0
- package/dist/direct.js.map +1 -1
- package/dist/drift.d.ts +1 -1
- package/dist/drift.js +1 -1
- package/dist/drift.js.map +1 -1
- package/dist/env.d.ts +2 -0
- package/dist/env.js +2 -2
- package/dist/env.js.map +1 -1
- package/dist/index.d.ts +10 -2
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/dist/ledger.d.ts +2 -0
- package/dist/ledger.js +1 -1
- package/dist/ledger.js.map +1 -1
- package/dist/lint.d.ts +0 -9
- package/dist/lint.js +1 -16
- package/dist/lint.js.map +1 -1
- package/dist/load.d.ts +74 -0
- package/dist/load.js +357 -0
- package/dist/load.js.map +1 -0
- package/dist/lockfile.d.ts +2 -0
- package/dist/lockfile.js +8 -3
- package/dist/lockfile.js.map +1 -1
- package/dist/op.d.ts +10 -1
- package/dist/op.js +1 -1
- package/dist/op.js.map +1 -1
- package/dist/outline.js +13 -4
- package/dist/outline.js.map +1 -1
- package/dist/paths.d.ts +18 -0
- package/dist/paths.js +21 -3
- package/dist/paths.js.map +1 -1
- package/dist/plugin.d.ts +0 -11
- package/dist/plugin.js +3 -18
- package/dist/plugin.js.map +1 -1
- package/dist/policy.d.ts +2 -0
- package/dist/policy.js.map +1 -1
- package/dist/resource.d.ts +10 -4
- package/dist/resource.js +11 -4
- package/dist/resource.js.map +1 -1
- package/dist/resources.d.ts +33 -76
- package/dist/resources.js +219 -227
- package/dist/resources.js.map +1 -1
- package/dist/run.d.ts +2 -0
- package/dist/run.js.map +1 -1
- package/dist/sandbox.d.ts +1 -1
- package/dist/sandbox.js +5 -2
- package/dist/sandbox.js.map +1 -1
- package/dist/scenarios.js +7 -1
- package/dist/scenarios.js.map +1 -1
- package/dist/schema.js +8 -0
- package/dist/schema.js.map +1 -1
- package/dist/secret-store-check.d.ts +11 -0
- package/dist/secret-store-check.js +76 -0
- package/dist/secret-store-check.js.map +1 -0
- package/dist/secret-store.d.ts +106 -0
- package/dist/secret-store.js +228 -0
- package/dist/secret-store.js.map +1 -0
- package/dist/shared.d.ts +17 -11
- package/dist/shared.js +34 -7
- package/dist/shared.js.map +1 -1
- package/dist/sites.d.ts +4 -93
- package/dist/sites.js +61 -84
- package/dist/sites.js.map +1 -1
- package/dist/version.d.ts +2 -1
- package/dist/version.js +2 -1
- package/dist/version.js.map +1 -1
- package/dist/wakeup-driver.js +15 -6
- package/dist/wakeup-driver.js.map +1 -1
- package/dist/worker.js +12 -8
- package/dist/worker.js.map +1 -1
- package/dist/writer.d.ts +53 -14
- package/dist/writer.js +113 -43
- package/dist/writer.js.map +1 -1
- package/oxlint.json +0 -5
- package/package.json +5 -1
- package/src/apply-steps.ts +5 -2
- package/src/apply.ts +54 -21
- package/src/approvals.ts +7 -1
- package/src/apps.ts +54 -85
- package/src/ast.ts +6 -6
- package/src/authentication.ts +74 -0
- package/src/call-workflow.ts +5 -4
- package/src/call.ts +53 -9
- package/src/cedar.ts +14 -1
- package/src/change-steps.ts +7 -4
- package/src/change.ts +10 -0
- package/src/client.ts +108 -41
- package/src/collection.ts +10 -2
- package/src/config.ts +62 -8
- package/src/connection.ts +446 -0
- package/src/credentials.ts +94 -281
- package/src/db.ts +21 -2
- package/src/define.ts +7 -2
- package/src/definitions.ts +31 -0
- package/src/deployment-key.ts +23 -0
- package/src/describe.ts +102 -230
- package/src/description.ts +426 -0
- package/src/direct.ts +3 -0
- package/src/drift.ts +10 -2
- package/src/env.ts +4 -2
- package/src/index.ts +42 -1
- package/src/ledger.ts +3 -1
- package/src/lint.ts +1 -16
- package/src/load.ts +437 -0
- package/src/lockfile.ts +10 -3
- package/src/op.ts +10 -1
- package/src/outline.ts +18 -5
- package/src/paths.ts +24 -2
- package/src/plugin.ts +3 -24
- package/src/policy.ts +2 -0
- package/src/resource.ts +24 -6
- package/src/resources.ts +246 -301
- package/src/run.ts +2 -0
- package/src/sandbox.ts +6 -2
- package/src/scenarios.ts +7 -1
- package/src/schema.ts +10 -0
- package/src/secret-store-check.ts +93 -0
- package/src/secret-store.ts +314 -0
- package/src/shared.ts +35 -10
- package/src/sites.ts +70 -180
- package/src/version.ts +2 -1
- package/src/wakeup-driver.ts +22 -6
- package/src/worker.ts +12 -8
- package/src/writer.ts +145 -56
- package/dist/datafile.d.ts +0 -97
- package/dist/datafile.js +0 -479
- package/dist/datafile.js.map +0 -1
- package/src/datafile.ts +0 -601
package/src/describe.ts
CHANGED
|
@@ -1,53 +1,54 @@
|
|
|
1
|
-
import { join } from "node:path";
|
|
2
1
|
import type { z } from "zod";
|
|
3
2
|
import { type AppEntry, appsAt } from "./apps.ts";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { readSource, type SourceFile } from "./ast.ts";
|
|
4
|
+
import { readPolicyFiles } from "./cedar.ts";
|
|
5
|
+
import { recordSchemaOf } from "./collection.ts";
|
|
6
6
|
import { connectorsOf, resolveConfig, rootOf, type SanomaConfig } from "./config.ts";
|
|
7
|
-
import { type Builtin, jsonSchemaOf, opIdsOf, type
|
|
7
|
+
import { type Builtin, jsonSchemaOf, opIdsOf, type Use } from "./define.ts";
|
|
8
|
+
import {
|
|
9
|
+
CAPABILITIES,
|
|
10
|
+
type CollectionEntry,
|
|
11
|
+
type ConfigDescription,
|
|
12
|
+
type OpEntry,
|
|
13
|
+
type ResourceTypeEntry,
|
|
14
|
+
type VendorEntry,
|
|
15
|
+
type WorkflowEntry,
|
|
16
|
+
} from "./description.ts";
|
|
8
17
|
import { isDirect } from "./direct.ts";
|
|
18
|
+
import { type CredentialStatus, credentialsOf } from "./env.ts";
|
|
9
19
|
import { lintWorkflow } from "./lint.ts";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
20
|
+
import { VENDOR, type VendorInfo } from "./op.ts";
|
|
21
|
+
import { outlineWithSource } from "./outline.ts";
|
|
12
22
|
import { allowAll, policyOpOf } from "./policy.ts";
|
|
13
|
-
import { type Resource,
|
|
14
|
-
import {
|
|
15
|
-
type DataFiles,
|
|
16
|
-
type DeclaredCollection,
|
|
17
|
-
type DeclaredResource,
|
|
18
|
-
inAppPeople,
|
|
19
|
-
readDataFiles,
|
|
20
|
-
type ResourceProblem,
|
|
21
|
-
} from "./resources.ts";
|
|
23
|
+
import { type Resource, resourceTypesOf, valueAt } from "./resource.ts";
|
|
24
|
+
import { type DeclaredResource, inAppPeople, loadDataFiles, type ResourceProblem } from "./resources.ts";
|
|
22
25
|
import { apiNameProblem } from "./sdk.ts";
|
|
23
26
|
import { apiNameOf, BUILT_IN_WORKFLOWS, DATA_VENDOR, problemLine } from "./shared.ts";
|
|
24
27
|
import { readSites, type SiteEntry } from "./sites.ts";
|
|
25
28
|
|
|
26
29
|
// `@sanoma-ai/workflows/describe`: what a UI renders from, apart from the main entry.
|
|
30
|
+
// What `describeConfig` answers, as zod schemas and the types they infer (`./description`).
|
|
31
|
+
export * from "./description.ts";
|
|
27
32
|
export { outlineWorkflow, type Outline, type OutlineNode, type Span } from "./outline.ts";
|
|
28
|
-
|
|
29
|
-
export type { LintProblem } from "./ast.ts";
|
|
30
|
-
export {
|
|
31
|
-
admit,
|
|
32
|
-
readDataFiles,
|
|
33
|
-
readPeople,
|
|
34
|
-
type DataFiles,
|
|
35
|
-
type DeclaredCollection,
|
|
36
|
-
type DeclaredPeople,
|
|
37
|
-
type DeclaredResource,
|
|
38
|
-
type ResourceProblem,
|
|
39
|
-
} from "./resources.ts";
|
|
33
|
+
export { admit, loadDataFiles, readPeople, type DataFiles, type DeclaredPeople } from "./resources.ts";
|
|
40
34
|
// The writer: setting a resource's fields in its data file, and rendering a new one.
|
|
41
|
-
export {
|
|
42
|
-
|
|
35
|
+
export {
|
|
36
|
+
type DataFileContent,
|
|
37
|
+
type DataValue,
|
|
38
|
+
formatOptions,
|
|
39
|
+
NotLiteral,
|
|
40
|
+
Reference,
|
|
41
|
+
renderDataFile,
|
|
42
|
+
setFields,
|
|
43
|
+
} from "./writer.ts";
|
|
43
44
|
|
|
44
45
|
/**
|
|
45
|
-
* The resources the config's data files declare,
|
|
46
|
-
* one error listing every problem,
|
|
46
|
+
* The resources the config's data files declare, as loading them makes them (`loadDataFiles`);
|
|
47
|
+
* throws one error listing every problem, one per line, for a test or CI to fail on.
|
|
47
48
|
* `describeConfig` lists the same, with the problems beside them instead.
|
|
48
49
|
*/
|
|
49
50
|
export function readResources(config: SanomaConfig): DeclaredResource[] {
|
|
50
|
-
const { resources, problems } =
|
|
51
|
+
const { resources, problems } = loadDataFiles(rootOf(config), connectorsOf(config));
|
|
51
52
|
if (problems.length) {
|
|
52
53
|
const lines = problems.map((p) => ` ${problemLine(p)}`);
|
|
53
54
|
throw new Error(`The resources' data files have problems:\n${lines.join("\n")}`);
|
|
@@ -64,19 +65,10 @@ export {
|
|
|
64
65
|
type Bindings,
|
|
65
66
|
type Rebind,
|
|
66
67
|
} from "./lockfile.ts";
|
|
67
|
-
// The apps under `apps/`,
|
|
68
|
-
export { crossAppImports, readApps
|
|
68
|
+
// The apps under `apps/`, with what their `app.ts` declares.
|
|
69
|
+
export { crossAppImports, readApps } from "./apps.ts";
|
|
69
70
|
// What the sites call, read from their source: a workflow's Used by.
|
|
70
|
-
export {
|
|
71
|
-
readSites,
|
|
72
|
-
type SiteCall,
|
|
73
|
-
type SiteEntry,
|
|
74
|
-
type SitePage,
|
|
75
|
-
siteContentPages,
|
|
76
|
-
siteIcons,
|
|
77
|
-
siteLocales,
|
|
78
|
-
sitesUnder,
|
|
79
|
-
} from "./sites.ts";
|
|
71
|
+
export { readSites, siteContentPages, siteIcons, siteLocales, sitesUnder } from "./sites.ts";
|
|
80
72
|
// The generated SDK, `.sanoma/api.ts`: a site's `api` typed from this description.
|
|
81
73
|
export { API_FILE, generateApi, writeApi, writeGenerated } from "./sdk.ts";
|
|
82
74
|
// The Cedar schema, `.sanoma/schema.cedarschema`: what a company's policies are validated and decided against.
|
|
@@ -87,162 +79,6 @@ export { previewRequests } from "./shared.ts";
|
|
|
87
79
|
// The apply workflow's plan and report, which the app renders.
|
|
88
80
|
export type { ApplyInput, ApplyPlan, ApplyReport, ApplyResult, PlanAnswer, PlanStep } from "./apply.ts";
|
|
89
81
|
|
|
90
|
-
/** What a workflow is, read from its definition: enough to draw a start form and show what it may call. */
|
|
91
|
-
export interface WorkflowEntry {
|
|
92
|
-
/** Its name: `<app>.<name>` for one an app's folder defines. */
|
|
93
|
-
name: string;
|
|
94
|
-
/** The app whose folder defines it (`apps/<app>/workflows/`); absent for one at the root. */
|
|
95
|
-
app?: string;
|
|
96
|
-
title?: string;
|
|
97
|
-
/** `manual`, or the schedule it starts on (`defineWorkflow`'s `trigger`). */
|
|
98
|
-
trigger: Trigger;
|
|
99
|
-
/**
|
|
100
|
-
* Set for a workflow the runtime adds itself, `drift`, `apply` or `call`, rather than the config: a label. It
|
|
101
|
-
* starts like any other.
|
|
102
|
-
*/
|
|
103
|
-
builtin?: true;
|
|
104
|
-
/** The input, as JSON Schema (draft 2020-12), from the zod schema. */
|
|
105
|
-
input: Record<string, unknown>;
|
|
106
|
-
/** Operation ids the workflow may call, in the order declared. */
|
|
107
|
-
ops: string[];
|
|
108
|
-
/** Built-ins the workflow may call. */
|
|
109
|
-
builtins: Builtin[];
|
|
110
|
-
/** The file that defined it, absolute. */
|
|
111
|
-
file: string;
|
|
112
|
-
/** What its `run` calls, in order, read from its file (`outlineWorkflow`), which the worker holds its runs to. */
|
|
113
|
-
outline: Outline;
|
|
114
|
-
/**
|
|
115
|
-
* What is wrong in its file, by position: what `lintWorkflow` finds (syntax, an import a
|
|
116
|
-
* workflow may not make, `instanceof` against the runtime's error classes, what the outline
|
|
117
|
-
* cannot draw in each `run`), read the one way the outline is, so a check sees what the
|
|
118
|
-
* worker refuses; and, at 1:1, an outline error that is the file's (it cannot be read, or holds
|
|
119
|
-
* no workflow of this name). A file defining several workflows gives each its problems. Empty
|
|
120
|
-
* for the built-in `drift`, which the package's tests hold to the lint.
|
|
121
|
-
*/
|
|
122
|
-
problems: LintProblem[];
|
|
123
|
-
/**
|
|
124
|
-
* The text the outline's spans index into: the workflow's file, with `\n` line endings.
|
|
125
|
-
* Absent only when the outline is `{ error }`. Whole files: a server may keep it from clients.
|
|
126
|
-
*/
|
|
127
|
-
source?: string;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** What an operation is, read from its connector: enough to badge it and show its contract. */
|
|
131
|
-
export interface OpEntry {
|
|
132
|
-
id: string;
|
|
133
|
-
vendor: string;
|
|
134
|
-
resource: string;
|
|
135
|
-
name: string;
|
|
136
|
-
effect: Effect;
|
|
137
|
-
idempotent: boolean;
|
|
138
|
-
description?: string;
|
|
139
|
-
/** How a scenario's steps name it: Cucumber expressions over its input's fields. */
|
|
140
|
-
phrases?: { given?: string; expect?: string };
|
|
141
|
-
/** Whether it may be called directly, outside a run (`isDirect`): the one rule, read here, never derived again. */
|
|
142
|
-
direct: boolean;
|
|
143
|
-
/** What a call sends, as JSON Schema (`io: "input"`: fields with defaults are optional). */
|
|
144
|
-
input: Record<string, unknown>;
|
|
145
|
-
/** What a call returns once its schema has parsed the vendor's reply (`io: "output"`). */
|
|
146
|
-
output: Record<string, unknown>;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/** Who a vendor is, from its connector's `VendorInfo`: enough to name it, show its logo and link to its connector. */
|
|
150
|
-
export interface VendorEntry {
|
|
151
|
-
/** The connector's `title`, else the vendor's id. */
|
|
152
|
-
title: string;
|
|
153
|
-
/** The logo as `data:image/svg+xml` URLs, for an `<img>`: `dark` is for dark backgrounds. */
|
|
154
|
-
logo?: { src: string; dark?: string };
|
|
155
|
-
/** The connector's npm package name. */
|
|
156
|
-
package?: string;
|
|
157
|
-
/** The connector package's `homepage` from its package.json: where its code and README are, as an https URL. */
|
|
158
|
-
homepage?: string;
|
|
159
|
-
/** Whether the vendor's connector attaches a fake (`withSetup`), for sandbox runs. */
|
|
160
|
-
fake: boolean;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/** A resource type a connector declares with `defineResource`. */
|
|
164
|
-
export interface ResourceTypeEntry {
|
|
165
|
-
/** `<vendor>.<type>`, such as `github.repository`. */
|
|
166
|
-
id: string;
|
|
167
|
-
vendor: string;
|
|
168
|
-
type: string;
|
|
169
|
-
title: string;
|
|
170
|
-
/** How a declared resource's identity is made, such as `name` or `repository_id:pattern`; absent when it is found only by its binding. */
|
|
171
|
-
identity?: string;
|
|
172
|
-
/** Its flagged fields, by dotted path. */
|
|
173
|
-
fields: ResourceFields;
|
|
174
|
-
/**
|
|
175
|
-
* Its state, as a read returns it, as JSON Schema (`io: "output"`), with `$id`
|
|
176
|
-
* `sanoma:resource-type/<id>`: the operations' contracts `$ref` it instead of repeating it.
|
|
177
|
-
*/
|
|
178
|
-
schema: Record<string, unknown>;
|
|
179
|
-
/** Its operations' ids, sorted: `<id>.import` and `<id>.read`, and for a writable type its change operations. */
|
|
180
|
-
ops: string[];
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/** A collection the config declares (`collection()`). */
|
|
184
|
-
export interface CollectionEntry {
|
|
185
|
-
/** Its name: `<app>.<name>` for one an app's `resources/data.ts` declares. */
|
|
186
|
-
name: string;
|
|
187
|
-
/** The app whose folder declares it (`apps/<app>/resources/data.ts`); absent for one at the root. */
|
|
188
|
-
app?: string;
|
|
189
|
-
title?: string;
|
|
190
|
-
/** The field each record is found by. */
|
|
191
|
-
key: string;
|
|
192
|
-
/** Its fields as declared. */
|
|
193
|
-
fields: Record<string, Field>;
|
|
194
|
-
/** What `save` accepts, as JSON Schema, with `$id` `sanoma:collection/<name>`. */
|
|
195
|
-
record: Record<string, unknown>;
|
|
196
|
-
/** Its operations' ids: `data.<name>.get`, `.list`, `.save` and `.delete`. */
|
|
197
|
-
ops: string[];
|
|
198
|
-
/** Where `resources/data.ts` declares it, read as the data files are; absent for one declared elsewhere. */
|
|
199
|
-
declared?: DeclaredCollection;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* A plain-JSON description of what a config can do: its workflows, the operations they call,
|
|
204
|
-
* and whether a policy gates them. The app server builds it from the config and sends it to the
|
|
205
|
-
* browser, so the UI shows exactly what the worker enforces. Never written by hand.
|
|
206
|
-
*/
|
|
207
|
-
export interface ConfigDescription {
|
|
208
|
-
appName: string;
|
|
209
|
-
/** The application version runs started now are stamped with. */
|
|
210
|
-
version: string;
|
|
211
|
-
workflows: WorkflowEntry[];
|
|
212
|
-
ops: OpEntry[];
|
|
213
|
-
/** Every vendor the operations are from, by its id (an `OpEntry`'s `vendor`). */
|
|
214
|
-
vendors: Record<string, VendorEntry>;
|
|
215
|
-
/** The resource types the connectors declare, by `id`. */
|
|
216
|
-
resourceTypes: ResourceTypeEntry[];
|
|
217
|
-
/** The config's collections, by name. */
|
|
218
|
-
collections: CollectionEntry[];
|
|
219
|
-
/**
|
|
220
|
-
* The resources the data files under `resources/` declare, by file and then in file order,
|
|
221
|
-
* less any with a problem and any that names one.
|
|
222
|
-
*/
|
|
223
|
-
resources: DeclaredResource[];
|
|
224
|
-
/** Each binding in the lockfile (`LOCKFILE`) whose key no data file declares, as `readDataFiles` reports it. */
|
|
225
|
-
orphans: DataFiles["orphans"];
|
|
226
|
-
/**
|
|
227
|
-
* What is wrong in the data files (`readDataFiles`) and the apps (a folder of `apps/` that is no
|
|
228
|
-
* app, an `app.ts` that cannot be read, a collection or site at the root named as an app is),
|
|
229
|
-
* each at its file, relative to the root, line and column. A workflow's are on its entry.
|
|
230
|
-
*/
|
|
231
|
-
problems: ResourceProblem[];
|
|
232
|
-
/** The apps under `apps/` (`readApps`), by name. Each workflow, collection and site an app declares names it (`app`). */
|
|
233
|
-
apps: AppEntry[];
|
|
234
|
-
/**
|
|
235
|
-
* `defined` when the root has `policies/**\/*.cedar` (else the policy is `allowAll`), with their
|
|
236
|
-
* `version`, or while they have `problems` (`policyProblems`), none: then every call and start is refused.
|
|
237
|
-
*/
|
|
238
|
-
policy: { defined: boolean; version?: string; problems?: string[] };
|
|
239
|
-
/**
|
|
240
|
-
* The sites under `sites/`, by name, with what each calls (`readSites`), and its calls to what
|
|
241
|
-
* the config does not have as its problems; none when the root has no `sites/`.
|
|
242
|
-
*/
|
|
243
|
-
sites: SiteEntry[];
|
|
244
|
-
}
|
|
245
|
-
|
|
246
82
|
/**
|
|
247
83
|
* Describes a config. Throws what `startWorker` would refuse (see `resolveConfig`), except missing
|
|
248
84
|
* credentials (`SanomaClient.credentials` gives their statuses) and what is wrong in a workflow's
|
|
@@ -254,31 +90,45 @@ export interface ConfigDescription {
|
|
|
254
90
|
export function describeConfig(config: SanomaConfig): ConfigDescription {
|
|
255
91
|
const resolved = resolveConfig(config);
|
|
256
92
|
const vendors: Record<string, VendorEntry> = {};
|
|
93
|
+
// The variables each driver declares, without a value: their names and manuals alone.
|
|
94
|
+
const env = credentialsOf(resolved.driversByVendor, () => undefined);
|
|
257
95
|
// A vendor whose operations are split over several connectors is named by the first; its
|
|
258
96
|
// resource types are every connector's.
|
|
259
97
|
for (const connector of config.connectors) {
|
|
260
98
|
const { id, info } = connector[VENDOR];
|
|
261
|
-
vendors[id] ??= vendorEntry(id, info, resolved.fakes.has(id));
|
|
99
|
+
vendors[id] ??= vendorEntry(id, info, resolved.fakes.has(id), env.get(id));
|
|
262
100
|
}
|
|
263
101
|
const resources = resourceTypesOf(connectorsOf(config));
|
|
102
|
+
// The types a host's `source` names: the repositories a host clones.
|
|
103
|
+
const sources = new Set(
|
|
104
|
+
[...resources.values()].flatMap(({ host, fields }) => {
|
|
105
|
+
const named = host && fields.references?.[host.inputs.source]?.type;
|
|
106
|
+
return named === undefined ? [] : [named].flat();
|
|
107
|
+
}),
|
|
108
|
+
);
|
|
264
109
|
// Each resource type's state is described once, on its entry, and referenced from its operations.
|
|
265
110
|
const refs = new Map<z.ZodType, string>([...resources].map(([id, r]) => [r.schema, resourceTypeUri(id)]));
|
|
266
111
|
const resourceTypes: ResourceTypeEntry[] = [...resources]
|
|
267
|
-
.map(([id,
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
112
|
+
.map(([id, spec]) => {
|
|
113
|
+
const { vendor, type, title, identity, fields, schema, ops } = spec;
|
|
114
|
+
const capabilities = CAPABILITIES.filter((c) => (c === "source" ? sources.has(id) : spec[c] !== undefined));
|
|
115
|
+
return {
|
|
116
|
+
id,
|
|
117
|
+
vendor,
|
|
118
|
+
type,
|
|
119
|
+
title,
|
|
120
|
+
identity,
|
|
121
|
+
fields,
|
|
122
|
+
schema: { $id: resourceTypeUri(id), ...toJsonSchema(schema, `${id} state`, "output") },
|
|
123
|
+
ops: Object.keys(ops)
|
|
124
|
+
.map((name) => `${id}.${name}`)
|
|
125
|
+
.toSorted(),
|
|
126
|
+
...(capabilities.length ? { capabilities } : {}),
|
|
127
|
+
};
|
|
128
|
+
})
|
|
279
129
|
.toSorted((a, b) => a.id.localeCompare(b.id));
|
|
280
130
|
|
|
281
|
-
const dataFiles =
|
|
131
|
+
const dataFiles = loadDataFiles(resolved.root, connectorsOf(config), resources);
|
|
282
132
|
const declared = new Map(dataFiles.collections.map((d) => [d.name, d]));
|
|
283
133
|
const collections: CollectionEntry[] = [...resolved.collections.values()]
|
|
284
134
|
.map((c) => ({
|
|
@@ -344,6 +194,28 @@ export function describeConfig(config: SanomaConfig): ConfigDescription {
|
|
|
344
194
|
);
|
|
345
195
|
|
|
346
196
|
return {
|
|
197
|
+
secrets: {
|
|
198
|
+
store: resolved.secrets.store?.id ?? "postgres",
|
|
199
|
+
namespace: resolved.secrets.namespace ?? "default",
|
|
200
|
+
bindings: Object.fromEntries(
|
|
201
|
+
Object.entries(resolved.secrets.bindings ?? {}).map(([name, b]) => [
|
|
202
|
+
name,
|
|
203
|
+
{
|
|
204
|
+
path: b.bootstrap ?? b.env ?? b.path!,
|
|
205
|
+
management: b.management ?? (b.env || b.bootstrap ? "external" : "sanoma"),
|
|
206
|
+
store: b.bootstrap ? "bootstrap" : b.env ? "environment" : (resolved.secrets.store?.id ?? "postgres"),
|
|
207
|
+
},
|
|
208
|
+
]),
|
|
209
|
+
),
|
|
210
|
+
},
|
|
211
|
+
connections: [...resolved.connections.catalog.definitions.values()].map((d) => ({
|
|
212
|
+
id: d.$connection,
|
|
213
|
+
vendor: d.vendor,
|
|
214
|
+
method: d.method,
|
|
215
|
+
scopes: d.scopes,
|
|
216
|
+
...(d.account && { account: d.account }),
|
|
217
|
+
default: d.default,
|
|
218
|
+
})),
|
|
347
219
|
appName: resolved.appName,
|
|
348
220
|
version: resolved.version,
|
|
349
221
|
workflows,
|
|
@@ -358,13 +230,14 @@ export function describeConfig(config: SanomaConfig): ConfigDescription {
|
|
|
358
230
|
...dataFiles.problems,
|
|
359
231
|
...appProblems,
|
|
360
232
|
...appClashes(apps, collections, sites),
|
|
361
|
-
...domainProblems(
|
|
233
|
+
...domainProblems(dataFiles.resources, resources, sites),
|
|
362
234
|
],
|
|
363
235
|
apps,
|
|
364
236
|
policy: {
|
|
365
237
|
defined: resolved.policy !== allowAll,
|
|
366
238
|
...(resolved.policy.version === undefined ? {} : { version: resolved.policy.version }),
|
|
367
239
|
...(resolved.policyProblems.length ? { problems: resolved.policyProblems } : {}),
|
|
240
|
+
files: resolved.root === undefined ? [] : readPolicyFiles(resolved.root),
|
|
368
241
|
},
|
|
369
242
|
sites,
|
|
370
243
|
};
|
|
@@ -404,11 +277,9 @@ function appClashes(
|
|
|
404
277
|
* only a public site at a domain, and one site at each.
|
|
405
278
|
*/
|
|
406
279
|
function domainProblems(
|
|
407
|
-
root: string | undefined,
|
|
408
280
|
declared: readonly DeclaredResource[],
|
|
409
281
|
types: ReadonlyMap<string, Resource>,
|
|
410
282
|
sites: readonly SiteEntry[],
|
|
411
|
-
read: (path: string) => SourceFile | string,
|
|
412
283
|
): ResourceProblem[] {
|
|
413
284
|
const isPublic = new Set(sites.filter((site) => site.public).map((site) => site.name));
|
|
414
285
|
const problems: ResourceProblem[] = [];
|
|
@@ -426,22 +297,11 @@ function domainProblems(
|
|
|
426
297
|
const others = `${names.length === 2 ? "both " : ""}${names.slice(0, -1).join(", ")}`;
|
|
427
298
|
messages.push(`${domain} is the domain of ${others} and ${names.at(-1)}`);
|
|
428
299
|
}
|
|
429
|
-
|
|
300
|
+
problems.push(...messages.map((message) => ({ ...resource.at, message })));
|
|
430
301
|
}
|
|
431
302
|
return problems;
|
|
432
303
|
}
|
|
433
304
|
|
|
434
|
-
/** `message` at a declaration's `export const`. */
|
|
435
|
-
function problemAt(
|
|
436
|
-
root: string,
|
|
437
|
-
resource: DeclaredResource,
|
|
438
|
-
read: (path: string) => SourceFile | string,
|
|
439
|
-
message: string,
|
|
440
|
-
): ResourceProblem {
|
|
441
|
-
const file = read(join(root, resource.file));
|
|
442
|
-
return { file: resource.file, ...(typeof file === "string" ? {} : file.at(resource.span[0])), message };
|
|
443
|
-
}
|
|
444
|
-
|
|
445
305
|
/**
|
|
446
306
|
* A site's `api` names each workflow `apiNameOf(name)` beside the vendors' operations and the
|
|
447
307
|
* collections (`api.data`), so a workflow whose name comes out as another's, a vendor's id or
|
|
@@ -477,7 +337,12 @@ function memoised<A, R>(fn: (arg: A) => R): (arg: A) => R {
|
|
|
477
337
|
/** A logo's SVG markup as a `data:` URL, for an `<img>`. */
|
|
478
338
|
export const svgDataUrl = (svg: string) => `data:image/svg+xml,${encodeURIComponent(svg.trim())}`;
|
|
479
339
|
|
|
480
|
-
const vendorEntry = (
|
|
340
|
+
const vendorEntry = (
|
|
341
|
+
id: string,
|
|
342
|
+
info: VendorInfo | undefined,
|
|
343
|
+
fake: boolean,
|
|
344
|
+
env: readonly CredentialStatus[] | undefined,
|
|
345
|
+
): VendorEntry => ({
|
|
481
346
|
title: info?.title ?? id,
|
|
482
347
|
...(info?.logo && {
|
|
483
348
|
logo: { src: svgDataUrl(info.logo.svg), ...(info.logo.dark && { dark: svgDataUrl(info.logo.dark) }) },
|
|
@@ -485,6 +350,13 @@ const vendorEntry = (id: string, info: VendorInfo | undefined, fake: boolean): V
|
|
|
485
350
|
...(info?.package && { package: info.package }),
|
|
486
351
|
...(info?.homepage && { homepage: info.homepage }),
|
|
487
352
|
fake,
|
|
353
|
+
...(env && {
|
|
354
|
+
credentials: env.map(({ name, description, optional }) => ({
|
|
355
|
+
name,
|
|
356
|
+
...(description !== undefined && { description }),
|
|
357
|
+
...(optional && { optional }),
|
|
358
|
+
})),
|
|
359
|
+
}),
|
|
488
360
|
});
|
|
489
361
|
|
|
490
362
|
/** Where a resource type's state schema is, for a `$ref`. */
|