@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.
Files changed (184) hide show
  1. package/README.md +186 -76
  2. package/dist/apply-steps.d.ts +6 -0
  3. package/dist/apply-steps.js +93 -0
  4. package/dist/apply-steps.js.map +1 -0
  5. package/dist/apply.d.ts +197 -0
  6. package/dist/apply.js +470 -0
  7. package/dist/apply.js.map +1 -0
  8. package/dist/approvals.d.ts +11 -6
  9. package/dist/approvals.js +38 -10
  10. package/dist/approvals.js.map +1 -1
  11. package/dist/call-workflow.d.ts +16 -0
  12. package/dist/call-workflow.js +35 -0
  13. package/dist/call-workflow.js.map +1 -0
  14. package/dist/call.d.ts +35 -2
  15. package/dist/call.js +123 -43
  16. package/dist/call.js.map +1 -1
  17. package/dist/cedar.d.ts +87 -0
  18. package/dist/cedar.js +365 -0
  19. package/dist/cedar.js.map +1 -0
  20. package/dist/client.d.ts +123 -9
  21. package/dist/client.js +355 -34
  22. package/dist/client.js.map +1 -1
  23. package/dist/collection.d.ts +107 -0
  24. package/dist/collection.js +306 -0
  25. package/dist/collection.js.map +1 -0
  26. package/dist/config.d.ts +42 -14
  27. package/dist/config.js +109 -28
  28. package/dist/config.js.map +1 -1
  29. package/dist/constructors.d.ts +2 -0
  30. package/dist/constructors.js +5 -0
  31. package/dist/constructors.js.map +1 -0
  32. package/dist/credentials.d.ts +25 -0
  33. package/dist/credentials.js +73 -5
  34. package/dist/credentials.js.map +1 -1
  35. package/dist/data.d.ts +2 -0
  36. package/dist/data.js +3 -0
  37. package/dist/data.js.map +1 -0
  38. package/dist/datafile.d.ts +38 -3
  39. package/dist/datafile.js +166 -13
  40. package/dist/datafile.js.map +1 -1
  41. package/dist/db.d.ts +5 -3
  42. package/dist/db.js +88 -21
  43. package/dist/db.js.map +1 -1
  44. package/dist/define.d.ts +10 -4
  45. package/dist/define.js +1 -5
  46. package/dist/define.js.map +1 -1
  47. package/dist/describe.d.ts +54 -8
  48. package/dist/describe.js +84 -11
  49. package/dist/describe.js.map +1 -1
  50. package/dist/direct.d.ts +9 -0
  51. package/dist/direct.js +11 -0
  52. package/dist/direct.js.map +1 -0
  53. package/dist/drift.d.ts +64 -12
  54. package/dist/drift.js +118 -41
  55. package/dist/drift.js.map +1 -1
  56. package/dist/errors.d.ts +16 -1
  57. package/dist/errors.js +30 -1
  58. package/dist/errors.js.map +1 -1
  59. package/dist/expectations.d.ts +4 -4
  60. package/dist/expectations.js +43 -9
  61. package/dist/expectations.js.map +1 -1
  62. package/dist/host.d.ts +56 -0
  63. package/dist/host.js +45 -0
  64. package/dist/host.js.map +1 -0
  65. package/dist/index.d.ts +13 -5
  66. package/dist/index.js +15 -5
  67. package/dist/index.js.map +1 -1
  68. package/dist/ledger.d.ts +12 -1
  69. package/dist/ledger.js +18 -11
  70. package/dist/ledger.js.map +1 -1
  71. package/dist/lint.d.ts +5 -4
  72. package/dist/lint.js +24 -7
  73. package/dist/lint.js.map +1 -1
  74. package/dist/lockfile.d.ts +36 -0
  75. package/dist/lockfile.js +69 -0
  76. package/dist/lockfile.js.map +1 -0
  77. package/dist/op.d.ts +9 -0
  78. package/dist/op.js.map +1 -1
  79. package/dist/outline.d.ts +2 -0
  80. package/dist/outline.js +1 -1
  81. package/dist/outline.js.map +1 -1
  82. package/dist/paths.d.ts +4 -0
  83. package/dist/paths.js +17 -3
  84. package/dist/paths.js.map +1 -1
  85. package/dist/person.d.ts +61 -0
  86. package/dist/person.js +69 -0
  87. package/dist/person.js.map +1 -0
  88. package/dist/plugin.js +1 -1
  89. package/dist/plugin.js.map +1 -1
  90. package/dist/policy.d.ts +51 -55
  91. package/dist/policy.js +14 -36
  92. package/dist/policy.js.map +1 -1
  93. package/dist/resource.d.ts +207 -25
  94. package/dist/resource.js +184 -30
  95. package/dist/resource.js.map +1 -1
  96. package/dist/resources.d.ts +76 -9
  97. package/dist/resources.js +170 -30
  98. package/dist/resources.js.map +1 -1
  99. package/dist/run.d.ts +31 -1
  100. package/dist/run.js +6 -1
  101. package/dist/run.js.map +1 -1
  102. package/dist/sandbox.js +5 -1
  103. package/dist/sandbox.js.map +1 -1
  104. package/dist/scenario.d.ts +25 -9
  105. package/dist/scenario.js +125 -27
  106. package/dist/scenario.js.map +1 -1
  107. package/dist/scenarios.d.ts +10 -6
  108. package/dist/scenarios.js +8 -4
  109. package/dist/scenarios.js.map +1 -1
  110. package/dist/schema.d.ts +32 -0
  111. package/dist/schema.js +225 -0
  112. package/dist/schema.js.map +1 -0
  113. package/dist/sdk.d.ts +34 -0
  114. package/dist/sdk.js +168 -0
  115. package/dist/sdk.js.map +1 -0
  116. package/dist/secret.d.ts +42 -0
  117. package/dist/secret.js +58 -0
  118. package/dist/secret.js.map +1 -0
  119. package/dist/shared.d.ts +188 -4
  120. package/dist/shared.js +165 -5
  121. package/dist/shared.js.map +1 -1
  122. package/dist/sites.d.ts +52 -0
  123. package/dist/sites.js +174 -0
  124. package/dist/sites.js.map +1 -0
  125. package/dist/source.d.ts +58 -0
  126. package/dist/source.js +54 -0
  127. package/dist/source.js.map +1 -0
  128. package/dist/version.d.ts +3 -2
  129. package/dist/version.js +3 -2
  130. package/dist/version.js.map +1 -1
  131. package/dist/worker.d.ts +6 -2
  132. package/dist/worker.js +37 -8
  133. package/dist/worker.js.map +1 -1
  134. package/dist/writer.d.ts +35 -0
  135. package/dist/writer.js +201 -0
  136. package/dist/writer.js.map +1 -0
  137. package/oxlint.json +5 -16
  138. package/package.json +11 -1
  139. package/src/apply-steps.ts +110 -0
  140. package/src/apply.ts +626 -0
  141. package/src/approvals.ts +49 -13
  142. package/src/call-workflow.ts +44 -0
  143. package/src/call.ts +173 -46
  144. package/src/cedar.ts +493 -0
  145. package/src/client.ts +450 -49
  146. package/src/collection.ts +431 -0
  147. package/src/config.ts +168 -48
  148. package/src/constructors.ts +4 -0
  149. package/src/credentials.ts +90 -5
  150. package/src/data.ts +10 -0
  151. package/src/datafile.ts +207 -15
  152. package/src/db.ts +90 -22
  153. package/src/define.ts +11 -9
  154. package/src/describe.ts +146 -23
  155. package/src/direct.ts +15 -0
  156. package/src/drift.ts +155 -45
  157. package/src/errors.ts +36 -1
  158. package/src/expectations.ts +46 -11
  159. package/src/host.ts +96 -0
  160. package/src/index.ts +56 -2
  161. package/src/ledger.ts +35 -17
  162. package/src/lint.ts +29 -12
  163. package/src/lockfile.ts +95 -0
  164. package/src/op.ts +9 -0
  165. package/src/outline.ts +1 -1
  166. package/src/paths.ts +18 -3
  167. package/src/person.ts +75 -0
  168. package/src/plugin.ts +1 -1
  169. package/src/policy.ts +59 -63
  170. package/src/resource.ts +302 -52
  171. package/src/resources.ts +226 -30
  172. package/src/run.ts +33 -1
  173. package/src/sandbox.ts +5 -1
  174. package/src/scenario.ts +175 -43
  175. package/src/scenarios.ts +15 -8
  176. package/src/schema.ts +261 -0
  177. package/src/sdk.ts +211 -0
  178. package/src/secret.ts +92 -0
  179. package/src/shared.ts +279 -6
  180. package/src/sites.ts +220 -0
  181. package/src/source.ts +82 -0
  182. package/src/version.ts +3 -2
  183. package/src/worker.ts +45 -11
  184. package/src/writer.ts +225 -0
package/dist/shared.d.ts CHANGED
@@ -1,10 +1,13 @@
1
+ import type { RunSummary } from "./client.ts";
1
2
  import type { CredentialStatus } from "./env.ts";
2
3
  import type { ApprovalState, Approver, Principal } from "./define.ts";
3
- import type { ResourceProblem } from "./resources.ts";
4
+ import type { LedgerRecord } from "./ledger.ts";
5
+ import type { DeclaredResource, ResourceProblem } from "./resources.ts";
4
6
  import type { OutlineNode } from "./outline.ts";
5
7
  export type { Declared, Declaring, FieldReference, References, Resource, ResourceFields, ResourceSpec, } from "./resource.ts";
6
8
  export type { DeclaredResource, ResourceProblem } from "./resources.ts";
7
9
  export type { DriftField, DriftReport, DriftResult, DriftStatus } from "./drift.ts";
10
+ export type { ApplyPlan, ApplyReport, ApplyResult, PlanAnswer, PlanStep } from "./apply.ts";
8
11
  export type { CredentialStatus } from "./env.ts";
9
12
  /**
10
13
  * What a `ctx.sleep` without a `{ name }` is called, in its outline node and its ledger record
@@ -13,6 +16,83 @@ export type { CredentialStatus } from "./env.ts";
13
16
  export declare const DEFAULT_SLEEP_NAME = "Sleep";
14
17
  /** The built-in drift workflow's name, which a config's own workflows may not take; a UI and the scenarios name its runs by it. */
15
18
  export declare const DRIFT_WORKFLOW = "drift";
19
+ /**
20
+ * The built-in apply workflow's name: the only workflow that may call a writable resource type's
21
+ * change operations (`create`, `update`, `delete`).
22
+ */
23
+ export declare const APPLY_WORKFLOW = "apply";
24
+ /**
25
+ * The vendor of the collections' operations, `data.<collection>.<op>`: a collection's `get`,
26
+ * `list`, `save` and `delete`. No connector may take it.
27
+ */
28
+ export declare const DATA_VENDOR = "data";
29
+ /** Every effect an operation may have, each an action group (`effect:<effect>`) of the Cedar schema. */
30
+ export declare const EFFECTS: readonly ["read", "write", "publish", "send", "money", "access"];
31
+ /** What a workflow start's action id starts with, as a policy names it: `workflow:<name>`. */
32
+ export declare const WORKFLOW_ACTION = "workflow:";
33
+ /** A site's folder name under `sites/`, its URL segment: what the app mounts and `readSites` reads. */
34
+ export declare const SITE_NAME: RegExp;
35
+ /**
36
+ * A record of a collection as `get`, `list` and `save` answer it: its key, the record, and when
37
+ * and by whom it was last saved (`updatedBy`: a person's id, or `run:<id>` for a run). A record
38
+ * saved under an older declaration comes back as it is stored, with `issues` saying where the
39
+ * fields no longer accept it.
40
+ */
41
+ export type Saved<R = Record<string, unknown>> = {
42
+ key: string;
43
+ record: R;
44
+ /** ISO 8601. */
45
+ updatedAt: string;
46
+ updatedBy: string;
47
+ issues?: {
48
+ path: (string | number)[];
49
+ message: string;
50
+ }[];
51
+ };
52
+ /**
53
+ * What a collection's `list` takes: fields equal to a value or within a range (`where`), the
54
+ * field to sort by (`-field` for descending; by key otherwise), and how many at most (1 to
55
+ * 1000, default 100).
56
+ */
57
+ export type ListQuery<R> = {
58
+ where?: {
59
+ [F in keyof R]?: Exclude<R[F], undefined> | Range<Exclude<R[F], undefined>>;
60
+ };
61
+ order?: (keyof R & string) | `-${keyof R & string}`;
62
+ limit?: number;
63
+ };
64
+ type Range<V> = {
65
+ gt?: V;
66
+ gte?: V;
67
+ lt?: V;
68
+ lte?: V;
69
+ };
70
+ /** How many records a collection's `list` answers: `default` when the query names no `limit`, `max` at most. */
71
+ export declare const LIST_LIMIT: {
72
+ readonly default: 100;
73
+ readonly max: 1000;
74
+ };
75
+ /** The kinds of value a field holds: text, a number, true or false, a date (ISO 8601) or a person's id. */
76
+ export declare const FIELD_TYPES: readonly ["text", "number", "boolean", "date", "person"];
77
+ /**
78
+ * Throws unless `collection()` takes this declaration: the name a lowercase letter then letters
79
+ * and digits, at least one field, each a type, the type marked optional or an enum of distinct
80
+ * strings, the key a required text field, the title a string. `collection()` and the reader of
81
+ * `resources/data.ts` both ask it, so `sanoma check` refuses what the runtime would.
82
+ */
83
+ export declare function checkCollection(declaration: {
84
+ name?: unknown;
85
+ key?: unknown;
86
+ fields?: unknown;
87
+ title?: unknown;
88
+ }): void;
89
+ /**
90
+ * The built-in call workflow's name: one operation that may be called directly (`isDirect`),
91
+ * made a run when the policy holds a direct call for an approval.
92
+ */
93
+ export declare const CALL_WORKFLOW = "call";
94
+ /** The workflows the runtime adds itself, which a config's own may not be named: drift, apply and call. */
95
+ export declare const BUILT_IN_WORKFLOWS: ReadonlySet<string>;
16
96
  /**
17
97
  * Every run status, in the order a run may pass through them: waiting on the queue, running,
18
98
  * waiting for an approval, or ended. `waiting` is `running` with an approval pending.
@@ -25,10 +105,63 @@ export declare const ENDED_STATUSES: readonly ["finished", "failed", "cancelled"
25
105
  /** True when the run has finished, failed or been cancelled. */
26
106
  export declare const isEnded: (status: RunStatus) => boolean;
27
107
  /**
28
- * True when `by` may decide the approval: `by.id` is the approver, or `by.groups` holds the
29
- * approver group. Pure: everything it reads is already recorded.
108
+ * Why the run's own actor may not decide an approval on it: separation of duties (`mayDecide`),
109
+ * while someone else could decide it.
30
110
  */
31
- export declare function mayDecide(approval: Pick<ApprovalState, "approver">, by: Principal): boolean;
111
+ export declare const OWN_CHANGE = "own change";
112
+ /** A run as `mayDecide` reads it: who started it, its own actor. */
113
+ type DecidedRun = {
114
+ actor?: Pick<Principal, "id"> | undefined;
115
+ };
116
+ /**
117
+ * True when `by` may decide the approval on `run`: `by.id` is the approver, or `by.groups` holds
118
+ * the approver group. The run's own actor is refused only while someone else could decide (separation
119
+ * of duties): the approver is a group with another declared person in `people` (an agent does not
120
+ * count). Pure: everything it reads is passed in.
121
+ */
122
+ export declare const mayDecide: (approval: Pick<ApprovalState, "approver">, by: Principal, run: DecidedRun, people: readonly PeopleResource[]) => boolean;
123
+ /** Why `by` may not decide the approval on `run` (`mayDecide`), naming who may, or `OWN_CHANGE`; undefined when they may. */
124
+ export declare function refusalOf({ approver }: Pick<ApprovalState, "approver">, by: Principal, run: DecidedRun, people: readonly PeopleResource[]): string | undefined;
125
+ /** A declared resource as the people functions read it: only the runtime's people types count. */
126
+ export type PeopleResource = Pick<DeclaredResource, "vendor" | "type" | "desired">;
127
+ /** The group every agent is in, the runtime's own among them: no data file declares it. */
128
+ export declare const AGENTS = "agents";
129
+ /** The runtime's own agents (scenarios, the host check, scheduled runs), declared whether a data file names them or not. */
130
+ export declare const RUNTIME_AGENTS: readonly string[];
131
+ type PersonFields = {
132
+ id: string;
133
+ name?: string;
134
+ email?: string;
135
+ github?: string;
136
+ };
137
+ /** The groups a policy may name and a principal may be in: the declared ones and `agents`, sorted. */
138
+ export declare const groupNamesOf: (resources: readonly PeopleResource[]) => string[];
139
+ /** The declared people (`sanoma.person`), in the order declared: not agents, who never sign in. */
140
+ export declare const personsOf: (resources: readonly PeopleResource[]) => PersonFields[];
141
+ /** The ids of the declared people and agents (not the runtime's own). */
142
+ export declare const principalIdsOf: (resources: readonly PeopleResource[]) => string[];
143
+ /** True when the data files declare a person, agent or group: then only a declared principal is admitted. */
144
+ export declare const declaresPeople: (resources: readonly PeopleResource[]) => boolean;
145
+ /**
146
+ * The principal a declared person or agent (or one of the runtime's own agents) acts as, read
147
+ * from the data files' resources: its id, every group that lists it (and `agents` for an agent),
148
+ * sorted, and an agent's `onBehalfOf`. Undefined for an id nobody declares.
149
+ */
150
+ export declare function principalOf(id: string, resources: readonly PeopleResource[]): Principal | undefined;
151
+ /**
152
+ * Who `name` is as an approver: a declared group's name (or `agents`) as `{ group }`, else a
153
+ * declared person's or agent's id; undefined when it is neither.
154
+ */
155
+ export declare function approverOf(name: string, resources: readonly PeopleResource[]): Approver | undefined;
156
+ /**
157
+ * The id of the declared person a sign-in vouches for, or undefined: by email first, then by
158
+ * GitHub login (each ignoring case), then by id. Never an agent's: agents do not sign in.
159
+ */
160
+ export declare function personOf(login: {
161
+ email?: string;
162
+ github?: string;
163
+ id?: string;
164
+ }, resources: readonly PeopleResource[]): string | undefined;
32
165
  /** Who may decide, as text: the person's id, or "group <name>". */
33
166
  export declare const approverLabel: (approver: Approver) => string;
34
167
  /** An error's message, or the value as text when it is not an Error. */
@@ -59,3 +192,54 @@ export declare function problemLine(p: {
59
192
  export declare const isLoopback: (host: string) => boolean;
60
193
  /** Whether a driver's environment variable lets it run: set, or unset and optional. */
61
194
  export declare const credentialReady: ({ status, optional }: CredentialStatus) => boolean;
195
+ /**
196
+ * A workflow's name in a site's `api`: kebab-case made camelCase, `outbound-next` as
197
+ * `api.outboundNext`. `describeConfig` reports two workflows that come out the same, or one that
198
+ * comes out as a vendor's id or `data`, as a problem on the workflow.
199
+ */
200
+ export declare const apiNameOf: (name: string) => string;
201
+ /**
202
+ * A workflow as a site's `api` has it (`api.<apiNameOf(name)>`), started as the person on the
203
+ * page. Called, it starts a run and resolves with its output once the run ends, or rejects with
204
+ * its error; `start` gives the run's id at once, and the rest read runs of this workflow.
205
+ */
206
+ export interface WorkflowFn<I, O> {
207
+ (input: I): Promise<O>;
208
+ /** Starts a run with this input, and answers with its id. */
209
+ start(input: I): Promise<{
210
+ runId: string;
211
+ }>;
212
+ /** The run's output once it ends, or its error; `run_running` when it has not ended within `waitMs` (at most 20 s). */
213
+ result(runId: string, waitMs?: number): Promise<O>;
214
+ /** The newest finished run's output, and when it finished (ms since the epoch); null when none has. */
215
+ latest(): Promise<{
216
+ runId: string;
217
+ output: O;
218
+ at: number;
219
+ } | null>;
220
+ /** Its latest runs, newest first. */
221
+ runs(query?: {
222
+ limit?: number;
223
+ }): Promise<RunSummary[]>;
224
+ /** The run's approvals, decided and waiting. */
225
+ approvals(runId: string): Promise<ApprovalState[]>;
226
+ /** The run's ledger records, in order. */
227
+ ledger(runId: string): Promise<LedgerRecord[]>;
228
+ }
229
+ /** An operation a site may call directly (a read, or a collection's write), as the person on the page. */
230
+ export type OpFn<I, O> = (input: I) => Promise<O>;
231
+ /** A collection as a site's `api.data.<name>` has it: its four operations (`Saved`, `ListQuery`). */
232
+ export interface CollectionApi<R> {
233
+ get: OpFn<{
234
+ key: string;
235
+ }, Saved<R> | null>;
236
+ list(query?: ListQuery<R>): Promise<Saved<R>[]>;
237
+ save: OpFn<{
238
+ record: R;
239
+ }, Saved<R>>;
240
+ delete: OpFn<{
241
+ key: string;
242
+ }, {
243
+ deleted: boolean;
244
+ }>;
245
+ }
package/dist/shared.js CHANGED
@@ -8,6 +8,90 @@
8
8
  export const DEFAULT_SLEEP_NAME = "Sleep";
9
9
  /** The built-in drift workflow's name, which a config's own workflows may not take; a UI and the scenarios name its runs by it. */
10
10
  export const DRIFT_WORKFLOW = "drift";
11
+ /**
12
+ * The built-in apply workflow's name: the only workflow that may call a writable resource type's
13
+ * change operations (`create`, `update`, `delete`).
14
+ */
15
+ export const APPLY_WORKFLOW = "apply";
16
+ /**
17
+ * The vendor of the collections' operations, `data.<collection>.<op>`: a collection's `get`,
18
+ * `list`, `save` and `delete`. No connector may take it.
19
+ */
20
+ export const DATA_VENDOR = "data";
21
+ /** Every effect an operation may have, each an action group (`effect:<effect>`) of the Cedar schema. */
22
+ export const EFFECTS = ["read", "write", "publish", "send", "money", "access"];
23
+ /** What a workflow start's action id starts with, as a policy names it: `workflow:<name>`. */
24
+ export const WORKFLOW_ACTION = "workflow:";
25
+ /** A site's folder name under `sites/`, its URL segment: what the app mounts and `readSites` reads. */
26
+ export const SITE_NAME = /^[a-z][a-z0-9-]*$/;
27
+ /** How many records a collection's `list` answers: `default` when the query names no `limit`, `max` at most. */
28
+ export const LIST_LIMIT = { default: 100, max: 1000 };
29
+ /** The kinds of value a field holds: text, a number, true or false, a date (ISO 8601) or a person's id. */
30
+ export const FIELD_TYPES = ["text", "number", "boolean", "date", "person"];
31
+ const COLLECTION_NAME = /^[a-z][a-zA-Z0-9]*$/;
32
+ const FIELD_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
33
+ const KINDS = `${FIELD_TYPES.map((t) => `"${t}"`).join(", ")}, { type, optional: true } or { enum: [...] }`;
34
+ const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
35
+ /**
36
+ * Throws unless `collection()` takes this declaration: the name a lowercase letter then letters
37
+ * and digits, at least one field, each a type, the type marked optional or an enum of distinct
38
+ * strings, the key a required text field, the title a string. `collection()` and the reader of
39
+ * `resources/data.ts` both ask it, so `sanoma check` refuses what the runtime would.
40
+ */
41
+ export function checkCollection(declaration) {
42
+ const { name, key, fields, title } = declaration;
43
+ const where = `collection("${String(name)}")`;
44
+ if (typeof name !== "string" || !COLLECTION_NAME.test(name)) {
45
+ throw new Error(`${where}: the name must be a lowercase letter, then letters and digits, such as "outbound"`);
46
+ }
47
+ if (!isRecord(fields) || !Object.keys(fields).length) {
48
+ throw new Error(`${where}: it needs fields, by name: each one of ${KINDS}`);
49
+ }
50
+ for (const [field, kind] of Object.entries(fields))
51
+ checkField(where, field, kind);
52
+ const keyKind = typeof key === "string" && Object.hasOwn(fields, key) ? fields[key] : undefined;
53
+ if (!(keyKind === "text" || (isRecord(keyKind) && keyKind.type === "text" && keyKind.optional === undefined))) {
54
+ throw new Error(`${where}: the key "${String(key)}" must be one of its fields, a required "text" one`);
55
+ }
56
+ if (title !== undefined && typeof title !== "string")
57
+ throw new Error(`${where}: the title must be a string`);
58
+ }
59
+ /** Throws unless `kind` is a field `collection()` takes: a type, the type marked optional, or an enum of distinct strings. */
60
+ function checkField(where, field, kind) {
61
+ const refused = (why) => new Error(`${where}: the field "${field}" ${why}`);
62
+ if (!FIELD_NAME.test(field))
63
+ throw refused("must be named with letters, digits and _, not a digit first");
64
+ const types = FIELD_TYPES;
65
+ const kinds = `is ${JSON.stringify(kind)}; a field is one of ${KINDS}`;
66
+ if (typeof kind === "string") {
67
+ if (!types.includes(kind))
68
+ throw refused(kinds);
69
+ return;
70
+ }
71
+ if (!isRecord(kind))
72
+ throw refused(kinds);
73
+ const { type, enum: values, optional, ...rest } = kind;
74
+ if (Object.keys(rest).length || (type === undefined) === (values === undefined))
75
+ throw refused(kinds);
76
+ if (optional !== undefined && optional !== true)
77
+ throw refused("may only be marked `optional: true`");
78
+ if (type !== undefined && !types.includes(type))
79
+ throw refused(kinds);
80
+ if (values !== undefined &&
81
+ (!Array.isArray(values) ||
82
+ !values.length ||
83
+ values.some((v) => typeof v !== "string") ||
84
+ new Set(values).size < values.length)) {
85
+ throw refused("needs an enum of distinct strings, at least one");
86
+ }
87
+ }
88
+ /**
89
+ * The built-in call workflow's name: one operation that may be called directly (`isDirect`),
90
+ * made a run when the policy holds a direct call for an approval.
91
+ */
92
+ export const CALL_WORKFLOW = "call";
93
+ /** The workflows the runtime adds itself, which a config's own may not be named: drift, apply and call. */
94
+ export const BUILT_IN_WORKFLOWS = new Set([DRIFT_WORKFLOW, APPLY_WORKFLOW, CALL_WORKFLOW]);
11
95
  /**
12
96
  * Every run status, in the order a run may pass through them: waiting on the queue, running,
13
97
  * waiting for an approval, or ended. `waiting` is `running` with an approval pending.
@@ -18,12 +102,82 @@ export const ENDED_STATUSES = ["finished", "failed", "cancelled"];
18
102
  /** True when the run has finished, failed or been cancelled. */
19
103
  export const isEnded = (status) => ENDED_STATUSES.includes(status);
20
104
  /**
21
- * True when `by` may decide the approval: `by.id` is the approver, or `by.groups` holds the
22
- * approver group. Pure: everything it reads is already recorded.
105
+ * Why the run's own actor may not decide an approval on it: separation of duties (`mayDecide`),
106
+ * while someone else could decide it.
23
107
  */
24
- export function mayDecide(approval, by) {
25
- const { approver } = approval;
26
- return typeof approver === "string" ? approver === by.id : (by.groups?.includes(approver.group) ?? false);
108
+ export const OWN_CHANGE = "own change";
109
+ /**
110
+ * True when `by` may decide the approval on `run`: `by.id` is the approver, or `by.groups` holds
111
+ * the approver group. The run's own actor is refused only while someone else could decide (separation
112
+ * of duties): the approver is a group with another declared person in `people` (an agent does not
113
+ * count). Pure: everything it reads is passed in.
114
+ */
115
+ export const mayDecide = (approval, by, run, people) => refusalOf(approval, by, run, people) === undefined;
116
+ /** Why `by` may not decide the approval on `run` (`mayDecide`), naming who may, or `OWN_CHANGE`; undefined when they may. */
117
+ export function refusalOf({ approver }, by, run, people) {
118
+ if (typeof approver === "string") {
119
+ return approver === by.id ? undefined : `${by.id} is not the approver; ${approverLabel(approver)} is`;
120
+ }
121
+ if (!by.groups?.includes(approver.group))
122
+ return `${by.id} is not in ${approverLabel(approver)}`;
123
+ if (by.id !== run.actor?.id)
124
+ return undefined;
125
+ const someoneElse = personsOf(people).some(({ id }) => id !== by.id && principalOf(id, people)?.groups?.includes(approver.group));
126
+ return someoneElse ? OWN_CHANGE : undefined;
127
+ }
128
+ /** The group every agent is in, the runtime's own among them: no data file declares it. */
129
+ export const AGENTS = "agents";
130
+ /** The runtime's own agents (scenarios, the host check, scheduled runs), declared whether a data file names them or not. */
131
+ export const RUNTIME_AGENTS = ["scenarios", "host-check", "schedule"];
132
+ /** The declared resources of one of the runtime's people types, by `desired`. */
133
+ const peopleOfType = (resources, type) => resources.flatMap((r) => (r.vendor === "sanoma" && r.type === type ? [r.desired] : []));
134
+ /** The groups a policy may name and a principal may be in: the declared ones and `agents`, sorted. */
135
+ export const groupNamesOf = (resources) => [...new Set([...peopleOfType(resources, "group").map((g) => g.name), AGENTS])].toSorted();
136
+ /** The declared people (`sanoma.person`), in the order declared: not agents, who never sign in. */
137
+ export const personsOf = (resources) => peopleOfType(resources, "person");
138
+ /** The ids of the declared people and agents (not the runtime's own). */
139
+ export const principalIdsOf = (resources) => [...personsOf(resources), ...peopleOfType(resources, "agent")].map((p) => p.id);
140
+ /** True when the data files declare a person, agent or group: then only a declared principal is admitted. */
141
+ export const declaresPeople = (resources) => resources.some((r) => r.vendor === "sanoma");
142
+ /**
143
+ * The principal a declared person or agent (or one of the runtime's own agents) acts as, read
144
+ * from the data files' resources: its id, every group that lists it (and `agents` for an agent),
145
+ * sorted, and an agent's `onBehalfOf`. Undefined for an id nobody declares.
146
+ */
147
+ export function principalOf(id, resources) {
148
+ const agent = peopleOfType(resources, "agent").find((a) => a.id === id);
149
+ const isAgent = agent !== undefined || RUNTIME_AGENTS.includes(id);
150
+ if (!isAgent && !peopleOfType(resources, "person").some((p) => p.id === id))
151
+ return undefined;
152
+ const groups = peopleOfType(resources, "group")
153
+ .filter((g) => g.members.includes(id))
154
+ .map((g) => g.name);
155
+ return {
156
+ id,
157
+ groups: [...new Set(isAgent ? [...groups, AGENTS] : groups)].toSorted(),
158
+ ...(agent?.onBehalfOf !== undefined && { onBehalfOf: agent.onBehalfOf }),
159
+ };
160
+ }
161
+ /**
162
+ * Who `name` is as an approver: a declared group's name (or `agents`) as `{ group }`, else a
163
+ * declared person's or agent's id; undefined when it is neither.
164
+ */
165
+ export function approverOf(name, resources) {
166
+ if (groupNamesOf(resources).includes(name))
167
+ return { group: name };
168
+ return principalOf(name, resources) && name;
169
+ }
170
+ /** True for two strings the same but for case: an email or a GitHub login. */
171
+ const same = (a, b) => a !== undefined && b !== undefined && a.toLowerCase() === b.toLowerCase();
172
+ /**
173
+ * The id of the declared person a sign-in vouches for, or undefined: by email first, then by
174
+ * GitHub login (each ignoring case), then by id. Never an agent's: agents do not sign in.
175
+ */
176
+ export function personOf(login, resources) {
177
+ const people = personsOf(resources);
178
+ return (people.find((p) => same(p.email, login.email)) ??
179
+ people.find((p) => same(p.github, login.github)) ??
180
+ people.find((p) => login.id !== undefined && p.id === login.id))?.id;
27
181
  }
28
182
  /** Who may decide, as text: the person's id, or "group <name>". */
29
183
  export const approverLabel = (approver) => typeof approver === "string" ? approver : `group ${approver.group}`;
@@ -74,4 +228,10 @@ export function problemLine(p) {
74
228
  export const isLoopback = (host) => ["localhost", "::1", "[::1]"].includes(host) || /^127\.\d+\.\d+\.\d+$/.test(host);
75
229
  /** Whether a driver's environment variable lets it run: set, or unset and optional. */
76
230
  export const credentialReady = ({ status, optional }) => status === "set" || (status === "missing" && optional);
231
+ /**
232
+ * A workflow's name in a site's `api`: kebab-case made camelCase, `outbound-next` as
233
+ * `api.outboundNext`. `describeConfig` reports two workflows that come out the same, or one that
234
+ * comes out as a vendor's id or `data`, as a problem on the workflow.
235
+ */
236
+ export const apiNameOf = (name) => name.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
77
237
  //# sourceMappingURL=shared.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared.js","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../src/shared.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import { type LintProblem } from "./ast.ts";
2
+ import type { ConfigDescription } from "./describe.ts";
3
+ /** A call a site makes, as written in its source. */
4
+ export interface SiteCall {
5
+ /** What it calls: a workflow, a collection (`api.data.<name>.<op>`) or an operation (`api.<vendor>.<resource>.<op>`). */
6
+ kind: "workflow" | "collection" | "op";
7
+ /**
8
+ * The workflow's name (`RunForm`'s `workflow`, or the workflow whose `apiNameOf` is the name
9
+ * `api` gives; that name itself when none is), the collection's name, or the operation's id.
10
+ */
11
+ target: string;
12
+ /** `<RunForm workflow="…" />` from `@sanoma-ai/app/ui`, or a call on `api` from `sanoma/site`. */
13
+ via: "RunForm" | "api";
14
+ /** The file, relative to the config's root, with `/`: `sites/sales/routes/index.tsx`. */
15
+ file: string;
16
+ /** From 1. */
17
+ line: number;
18
+ /** From 1, in UTF-16 code units. */
19
+ column: number;
20
+ /**
21
+ * The path of the page the file is, from its `createFileRoute` (or `createLazyFileRoute`) id: groups and pathless
22
+ * segments left out, no trailing slash (`/sites/sales`). Absent for a file that is no page,
23
+ * such as a component's.
24
+ */
25
+ page?: string;
26
+ }
27
+ /** A site, `sites/<name>/`, and what it calls. */
28
+ export interface SiteEntry {
29
+ /** Its folder's name, where it is mounted: `/sites/<name>`. */
30
+ name: string;
31
+ /** `defineSite({ title })` in its `site.ts`, when that is a string literal. */
32
+ title?: string;
33
+ /** Its calls, by file and then in the file's order. */
34
+ calls: SiteCall[];
35
+ /** Each call to a workflow, a collection or an operation the config does not have, or to an operation a page may not call directly, at its file. */
36
+ problems: (LintProblem & {
37
+ file: string;
38
+ })[];
39
+ }
40
+ /**
41
+ * The sites under `root`: each folder of `sites/` with a `routes/` whose name is a URL segment,
42
+ * by name (as `@sanoma-ai/app/build`'s `sitesOf` mounts them). Reads `routes/**` and
43
+ * `components/**` (`.ts` and `.tsx`, less tests and declarations) of each with oxc, never running
44
+ * them: each `<RunForm workflow="…" />` whose `RunForm` is imported from `@sanoma-ai/app/ui`, and
45
+ * each use of `api` imported from `sanoma/site`, called or passed on (`queryFn: api.data.leads.list`):
46
+ * `api.<name>` and `api.<name>.<method>` are a workflow, `api.data.<collection>.<op>` a collection,
47
+ * and `api.<vendor>.<resource>.<op>` an operation. A name that is not written out (a variable, a
48
+ * template with holes, a computed member) is skipped, as is a use through a namespace import or a
49
+ * destructured `api`. A call to what `description` does not have, or to an operation a page may
50
+ * not call directly, is a problem.
51
+ */
52
+ export declare function readSites(root: string | undefined, description: Pick<ConfigDescription, "workflows" | "collections" | "ops">): SiteEntry[];