@sanoma-ai/workflows 0.9.1 → 0.10.1
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 +193 -50
- package/SECRET-SUPPORT.md +33 -0
- 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 +13 -24
- package/dist/apps.js +69 -68
- 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 +54 -0
- package/dist/authentication.js +138 -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 +36 -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 +62 -10
- package/dist/client.js +272 -47
- 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 +16 -4
- package/dist/config.js +89 -21
- package/dist/config.js.map +1 -1
- package/dist/connection.d.ts +389 -0
- package/dist/connection.js +657 -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 +34 -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 +15 -175
- package/dist/describe.js +122 -35
- 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 +7 -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/fake.d.ts +13 -1
- package/dist/fake.js +70 -0
- package/dist/fake.js.map +1 -1
- package/dist/index.d.ts +11 -3
- 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 +2 -17
- package/dist/lint.js.map +1 -1
- package/dist/load.d.ts +85 -0
- package/dist/load.js +391 -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 +22 -5
- package/dist/op.js +20 -2
- package/dist/op.js.map +1 -1
- package/dist/outline.d.ts +5 -0
- package/dist/outline.js +46 -5
- package/dist/outline.js.map +1 -1
- package/dist/paths.d.ts +18 -2
- package/dist/paths.js +19 -7
- 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 +4 -0
- package/dist/run.js.map +1 -1
- package/dist/sandbox.d.ts +5 -2
- package/dist/sandbox.js +56 -7
- package/dist/sandbox.js.map +1 -1
- package/dist/scenario.d.ts +5 -0
- package/dist/scenario.js +3 -2
- package/dist/scenario.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/secret.d.ts +2 -0
- package/dist/secret.js +32 -0
- package/dist/secret.js.map +1 -1
- package/dist/shared.d.ts +21 -11
- package/dist/shared.js +41 -7
- package/dist/shared.js.map +1 -1
- package/dist/sites.d.ts +4 -93
- package/dist/sites.js +76 -84
- package/dist/sites.js.map +1 -1
- package/dist/steps.d.ts +3 -0
- package/dist/steps.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 +1 -5
- package/package.json +6 -1
- package/src/apply-steps.ts +5 -2
- package/src/apply.ts +54 -21
- package/src/approvals.ts +7 -1
- package/src/apps.ts +71 -86
- package/src/ast.ts +6 -6
- package/src/authentication.ts +191 -0
- package/src/call-workflow.ts +5 -4
- package/src/call.ts +52 -9
- package/src/cedar.ts +14 -1
- package/src/change-steps.ts +7 -4
- package/src/change.ts +10 -0
- package/src/client.ts +346 -45
- package/src/collection.ts +10 -2
- package/src/config.ts +107 -19
- package/src/connection.ts +912 -0
- package/src/credentials.ts +94 -281
- package/src/db.ts +34 -2
- package/src/define.ts +7 -2
- package/src/definitions.ts +31 -0
- package/src/deployment-key.ts +23 -0
- package/src/describe.ts +150 -227
- package/src/description.ts +426 -0
- package/src/direct.ts +7 -0
- package/src/drift.ts +10 -2
- package/src/env.ts +4 -2
- package/src/fake.ts +71 -0
- package/src/index.ts +53 -1
- package/src/ledger.ts +3 -1
- package/src/lint.ts +2 -17
- package/src/load.ts +466 -0
- package/src/lockfile.ts +10 -3
- package/src/op.ts +41 -7
- package/src/outline.ts +53 -7
- package/src/paths.ts +21 -6
- 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 +4 -0
- package/src/sandbox.ts +61 -7
- package/src/scenario.ts +24 -6
- 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/secret.ts +29 -0
- package/src/shared.ts +44 -10
- package/src/sites.ts +85 -179
- package/src/steps.ts +3 -2
- 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/writer.ts
CHANGED
|
@@ -1,27 +1,72 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import type { FormatConfig } from "oxfmt";
|
|
4
|
-
import { type Node, parse } from "./ast.ts";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
readDataFile,
|
|
12
|
-
SECRET_ENTRIES,
|
|
13
|
-
} from "./datafile.ts";
|
|
14
|
-
import { isSecret } from "./secret.ts";
|
|
15
|
-
|
|
16
|
-
// The data-file writer: what the app's Adopt, and setting up a company, write into the files
|
|
17
|
-
// `datafile.ts` reads. A file is changed in place, one declaration at a time, and everything
|
|
4
|
+
import { keyName, type Node, parse } from "./ast.ts";
|
|
5
|
+
import type { ConnectionRef } from "./connection.ts";
|
|
6
|
+
import { SECRET_ENTRIES } from "./paths.ts";
|
|
7
|
+
import { isSecret, type Secret } from "./secret.ts";
|
|
8
|
+
|
|
9
|
+
// The data-file writer: what the app's Adopt and its forms' Save, and setting up a company, write
|
|
10
|
+
// into a company's files. A file is changed in place, one declaration at a time, and everything
|
|
18
11
|
// outside that declaration is kept byte for byte; what is written is formatted with oxfmt, under
|
|
19
|
-
// the repository's own options, so it reads as if a person had written it and formatted it.
|
|
12
|
+
// the repository's own options, so it reads as if a person had written it and formatted it. Files
|
|
13
|
+
// are read by loading them; the writer parses, and changes only what is written out as a literal.
|
|
14
|
+
|
|
15
|
+
/** A name in a data file that refers to a declared resource: one imported from a data file, or exported by this one. */
|
|
16
|
+
export class Reference {
|
|
17
|
+
readonly name: string;
|
|
18
|
+
constructor(name: string) {
|
|
19
|
+
this.name = name;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const REFERENCE = Symbol.for("sanoma.reference");
|
|
24
|
+
Object.defineProperty(Reference.prototype, REFERENCE, { value: true });
|
|
25
|
+
|
|
26
|
+
/** True for a `Reference`, made by any copy of this package: read by a registered symbol, not `instanceof`. */
|
|
27
|
+
export const isReference = (value: unknown): value is Reference =>
|
|
28
|
+
typeof value === "object" && value !== null && REFERENCE in value;
|
|
29
|
+
|
|
30
|
+
/** A value the writer writes: a literal, a reference to a declared resource, or a secret's marker (`secret("NAME")`). */
|
|
31
|
+
export type DataValue =
|
|
32
|
+
| null
|
|
33
|
+
| boolean
|
|
34
|
+
| number
|
|
35
|
+
| string
|
|
36
|
+
| Reference
|
|
37
|
+
| Secret
|
|
38
|
+
| DataValue[]
|
|
39
|
+
| { [field: string]: DataValue };
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Why `setFields` will not write a field: the file computes what is there (a name, a call, a
|
|
43
|
+
* spread), or the declaration is not an object literal, so only a person editing the code can
|
|
44
|
+
* change it. `reason` says which, for the form that asked.
|
|
45
|
+
*/
|
|
46
|
+
export class NotLiteral extends Error {
|
|
47
|
+
readonly reason: string;
|
|
48
|
+
/** The field the file computes, by dotted path; absent when the declaration itself is no object literal. */
|
|
49
|
+
readonly field: string | undefined;
|
|
50
|
+
constructor(reason: string, field?: string) {
|
|
51
|
+
super(reason);
|
|
52
|
+
this.name = "NotLiteral";
|
|
53
|
+
this.reason = reason;
|
|
54
|
+
this.field = field;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
20
57
|
|
|
21
58
|
/** A whole data file to render: its imports and its resources, in order. */
|
|
22
59
|
export interface DataFileContent {
|
|
23
|
-
|
|
24
|
-
|
|
60
|
+
/** Each `import { imported as local } from "source"`. */
|
|
61
|
+
imports: readonly { source: string; imported: string; local: string }[];
|
|
62
|
+
/** Each `export const name = vendor.type({ ...desired }, { connection })`. */
|
|
63
|
+
exports: readonly {
|
|
64
|
+
name: string;
|
|
65
|
+
vendor: string;
|
|
66
|
+
type: string;
|
|
67
|
+
desired: { [field: string]: DataValue };
|
|
68
|
+
connection?: ConnectionRef;
|
|
69
|
+
}[];
|
|
25
70
|
}
|
|
26
71
|
|
|
27
72
|
/**
|
|
@@ -41,19 +86,22 @@ export function formatOptions(root: string): FormatConfig {
|
|
|
41
86
|
}
|
|
42
87
|
|
|
43
88
|
/**
|
|
44
|
-
* `source`, a
|
|
89
|
+
* `source`, a company file's text, with fields of its declaration `exportName` set to new values:
|
|
45
90
|
* `fields` by dotted path, a list item's with its index, as a drift report names them
|
|
46
|
-
* (`required_pull_request_reviews.0.dismiss_stale_reviews`).
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
91
|
+
* (`required_pull_request_reviews.0.dismiss_stale_reviews`). The declaration is the object
|
|
92
|
+
* literal of `export const <exportName> = …` (or `export default …` for `"default"`): the object
|
|
93
|
+
* itself, or the first argument of the call it is (`github.repository({ … })`,
|
|
94
|
+
* `defineSite({ … })`). A field the declaration does not have is added at the end of the object
|
|
95
|
+
* that should hold it. Only that statement changes: it is formatted with oxfmt under `options`
|
|
96
|
+
* (`formatOptions`), and every other byte of the file stays as it was.
|
|
50
97
|
*
|
|
51
98
|
* A value naming a secret adds `import { secret } from "sanoma/resources"` after the file's
|
|
52
99
|
* imports when it has none.
|
|
53
100
|
*
|
|
54
|
-
* Throws when the
|
|
55
|
-
*
|
|
56
|
-
*
|
|
101
|
+
* Throws `NotLiteral` when the declaration is not an object literal, or a path reaches a value the
|
|
102
|
+
* file computes (a name, such as a reference to another resource, a call, a spread): a person
|
|
103
|
+
* changes those in the code. Throws an `Error` when the file does not parse, has no such export,
|
|
104
|
+
* or a path runs through a literal that is not an object or a list.
|
|
57
105
|
*/
|
|
58
106
|
export async function setFields(
|
|
59
107
|
source: string,
|
|
@@ -74,14 +122,13 @@ export async function setFields(
|
|
|
74
122
|
return text.slice(0, start) + formatted.trimEnd() + text.slice(end);
|
|
75
123
|
}
|
|
76
124
|
|
|
77
|
-
/** What a data file imports `secret` from when it names one
|
|
125
|
+
/** What a data file imports `secret` from when it names one. */
|
|
78
126
|
const SECRET_IMPORT = { source: "sanoma/resources", imported: "secret", local: "secret" };
|
|
79
127
|
|
|
80
128
|
/**
|
|
81
129
|
* A whole data file: the imports, one per source, then each resource as `export const`, then
|
|
82
|
-
* `export default` naming them all, formatted with oxfmt under `options
|
|
83
|
-
*
|
|
84
|
-
* name a secret also imports `secret` from "sanoma/resources".
|
|
130
|
+
* `export default` naming them all, formatted with oxfmt under `options`: loaded, it declares
|
|
131
|
+
* what it was given. A file whose values name a secret also imports `secret` from "sanoma/resources".
|
|
85
132
|
*/
|
|
86
133
|
export async function renderDataFile({ imports, exports }: DataFileContent, options: FormatConfig = {}) {
|
|
87
134
|
const names = new Map<string, string[]>();
|
|
@@ -95,9 +142,9 @@ export async function renderDataFile({ imports, exports }: DataFileContent, opti
|
|
|
95
142
|
const parts = [
|
|
96
143
|
[...names].map(([source, list]) => `import { ${list.join(", ")} } from ${JSON.stringify(source)};`).join("\n"),
|
|
97
144
|
// The top-level object on lines of its own, so oxfmt keeps it expanded as people write it.
|
|
98
|
-
...exports.map(({ name, vendor, type, desired }) => {
|
|
145
|
+
...exports.map(({ name, vendor, type, desired, connection }) => {
|
|
99
146
|
const body = Object.entries(desired).map(([key, value]) => `${keyOf(key)}: ${literal(value)},\n`);
|
|
100
|
-
return `export const ${name} = ${vendor}.${type}({${body.length ? `\n${body.join("")}` : ""}});`;
|
|
147
|
+
return `export const ${name} = ${vendor}.${type}({${body.length ? `\n${body.join("")}` : ""}}${connection ? `, ${JSON.stringify({ connection })}` : ""});`;
|
|
101
148
|
}),
|
|
102
149
|
`export default [${exports.map((e) => e.name).join(", ")}];`,
|
|
103
150
|
];
|
|
@@ -118,16 +165,34 @@ function withSecretImport(text: string): string {
|
|
|
118
165
|
return at ? `${text.slice(0, at)}\n${line}${text.slice(at)}` : `${line}\n${text}`;
|
|
119
166
|
}
|
|
120
167
|
|
|
121
|
-
/**
|
|
168
|
+
/**
|
|
169
|
+
* The statement that exports `name` in `text` (`export const <name> = …`, or `export default …`
|
|
170
|
+
* for `"default"`), and the object literal it declares: the value itself, or the first argument
|
|
171
|
+
* of the call it is.
|
|
172
|
+
*/
|
|
122
173
|
function declarationOf(text: string, name: string): { statement: Node; object: Node } {
|
|
123
|
-
|
|
124
|
-
const filename = "/data-file.ts";
|
|
125
|
-
const { program, errors } = parse(filename, text);
|
|
174
|
+
const { program, errors } = parse("/data-file.ts", text);
|
|
126
175
|
if (errors.length) throw new Error(`The data file does not parse: ${errors[0]!.message}`);
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
176
|
+
for (const statement of program.body) {
|
|
177
|
+
const value = exportedValue(statement, name);
|
|
178
|
+
if (value === undefined) continue;
|
|
179
|
+
const object = value?.type === "CallExpression" ? value.arguments[0] : value;
|
|
180
|
+
if (object?.type !== "ObjectExpression") throw new NotLiteral(`${name} is not an object literal: edit it in code`);
|
|
181
|
+
return { statement, object };
|
|
182
|
+
}
|
|
183
|
+
throw new Error(`The data file declares no resource ${name}`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** The value `statement` exports as `name` (null for `export let x;`), or undefined when it exports no such name. */
|
|
187
|
+
function exportedValue(statement: Node, name: string): Node | null | undefined {
|
|
188
|
+
if (name === "default") return statement.type === "ExportDefaultDeclaration" ? statement.declaration : undefined;
|
|
189
|
+
if (statement.type !== "ExportNamedDeclaration" || statement.declaration?.type !== "VariableDeclaration") {
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
const declarator = statement.declaration.declarations.find(
|
|
193
|
+
(d: Node) => d.id.type === "Identifier" && d.id.name === name,
|
|
194
|
+
);
|
|
195
|
+
return declarator && declarator.init;
|
|
131
196
|
}
|
|
132
197
|
|
|
133
198
|
/**
|
|
@@ -139,38 +204,62 @@ function editAt(object: Node, path: string, value: DataValue): { start: number;
|
|
|
139
204
|
const keys = path.split(".");
|
|
140
205
|
let node = object;
|
|
141
206
|
for (let i = 0; i < keys.length;) {
|
|
142
|
-
|
|
207
|
+
// A spread may hold the field, or move the list's items: what is there is the code's.
|
|
208
|
+
if (node.type === "ObjectExpression" && !node.properties.some(isSpread)) {
|
|
143
209
|
const names = new Map<string, Node>(node.properties.map((p: Node) => [fieldName(p), p]));
|
|
144
210
|
let j = keys.length;
|
|
145
211
|
while (j > i && !names.has(keys.slice(i, j).join("."))) j--;
|
|
146
212
|
if (j === i) return added(node, keys.slice(i), path, value);
|
|
147
213
|
node = names.get(keys.slice(i, j).join("."))!.value;
|
|
148
214
|
i = j;
|
|
149
|
-
} else if (
|
|
215
|
+
} else if (
|
|
216
|
+
node.type === "ArrayExpression" &&
|
|
217
|
+
!node.elements.some(isSpread) &&
|
|
218
|
+
/^\d+$/.test(keys[i]!) &&
|
|
219
|
+
node.elements[Number(keys[i])]
|
|
220
|
+
) {
|
|
150
221
|
node = node.elements[Number(keys[i])];
|
|
151
222
|
i++;
|
|
223
|
+
} else if (computed(node)) {
|
|
224
|
+
throw computedAt(keys.slice(0, Math.max(i, 1)).join("."));
|
|
152
225
|
} else {
|
|
153
226
|
const at = keys.slice(0, i).join(".");
|
|
154
227
|
throw new Error(`Cannot set ${path}: ${at} is not an object or a list with item ${keys[i]}`);
|
|
155
228
|
}
|
|
156
229
|
}
|
|
157
|
-
|
|
158
|
-
if (named) {
|
|
159
|
-
throw new Error(`Cannot set ${path}: it names the resource ${named}; change a reference in the file itself`);
|
|
160
|
-
}
|
|
230
|
+
if (computed(node)) throw computedAt(path);
|
|
161
231
|
return { start: node.start, end: node.end, replacement: literal(value) };
|
|
162
232
|
}
|
|
163
233
|
|
|
164
|
-
/** The
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
234
|
+
/** The refusal of a field whose value the file computes. */
|
|
235
|
+
const computedAt = (field: string) => new NotLiteral(`the value of ${field} is computed in the file`, field);
|
|
236
|
+
|
|
237
|
+
const isSpread = (node: Node) => node?.type === "SpreadElement";
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* True when a value is (or holds) code rather than a literal: a name (a reference to another
|
|
241
|
+
* resource, a constant), a call other than `secret(…)`, a spread, a template with expressions.
|
|
242
|
+
* A person changes those in the file; a form's or a vendor's value never replaces one.
|
|
243
|
+
*/
|
|
244
|
+
function computed(node: Node): boolean {
|
|
245
|
+
switch (node.type) {
|
|
246
|
+
case "Literal":
|
|
247
|
+
return false;
|
|
248
|
+
case "TemplateLiteral":
|
|
249
|
+
return node.expressions.length > 0;
|
|
250
|
+
case "UnaryExpression":
|
|
251
|
+
return !["-", "+"].includes(node.operator) || node.argument.type !== "Literal";
|
|
252
|
+
case "ArrayExpression":
|
|
253
|
+
return node.elements.some((item: Node) => item === null || computed(item));
|
|
254
|
+
case "ObjectExpression":
|
|
255
|
+
return node.properties.some(
|
|
256
|
+
(p: Node) => p.type !== "Property" || p.computed || p.kind !== "init" || p.method || computed(p.value),
|
|
257
|
+
);
|
|
258
|
+
case "CallExpression":
|
|
259
|
+
return !(node.callee.type === "Identifier" && node.callee.name === "secret" && !node.arguments.some(computed));
|
|
260
|
+
default:
|
|
261
|
+
return true;
|
|
172
262
|
}
|
|
173
|
-
return undefined;
|
|
174
263
|
}
|
|
175
264
|
|
|
176
265
|
/**
|
|
@@ -188,7 +277,7 @@ function added(object: Node, [key, ...under]: string[], path: string, value: Dat
|
|
|
188
277
|
: { start: object.start, end: object.end, replacement: `{ ${field} }` };
|
|
189
278
|
}
|
|
190
279
|
|
|
191
|
-
/** A property's name
|
|
280
|
+
/** A property's name: undefined for a computed key or a spread. */
|
|
192
281
|
const fieldName = (p: Node): string | undefined => (p.type === "Property" && !p.computed ? keyName(p.key) : undefined);
|
|
193
282
|
|
|
194
283
|
/** A field's name in source: bare when it can be, else a string. */
|
|
@@ -199,7 +288,7 @@ const holdsSecret = (value: DataValue): boolean =>
|
|
|
199
288
|
isSecret(value) ||
|
|
200
289
|
(typeof value === "object" && value !== null && !isReference(value) && Object.values(value).some(holdsSecret));
|
|
201
290
|
|
|
202
|
-
/** A value
|
|
291
|
+
/** A value as source, on one line: oxfmt lays it out. */
|
|
203
292
|
function literal(value: DataValue): string {
|
|
204
293
|
if (isReference(value)) return value.name;
|
|
205
294
|
if (isSecret(value)) return `secret(${JSON.stringify(value.$secret)}${value.generate ? ", { generate: true }" : ""})`;
|
package/dist/datafile.d.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import type { Node } from "./ast.ts";
|
|
2
|
-
import { type Secret } from "./secret.ts";
|
|
3
|
-
/** Something a data file may not hold, at UTF-16 offsets into its text. */
|
|
4
|
-
export interface DataFileProblem {
|
|
5
|
-
start: number;
|
|
6
|
-
end: number;
|
|
7
|
-
message: string;
|
|
8
|
-
}
|
|
9
|
-
/** A name in a data file that refers to a declared resource: one imported from a data file, or exported by this one. */
|
|
10
|
-
export declare class Reference {
|
|
11
|
-
readonly name: string;
|
|
12
|
-
constructor(name: string);
|
|
13
|
-
}
|
|
14
|
-
/** True for a `Reference`, made by any copy of this package: read by a registered symbol, not `instanceof`. */
|
|
15
|
-
export declare const isReference: (value: unknown) => value is Reference;
|
|
16
|
-
/** What `secret("NAME")` stands for in a data file: its marker, `{ $secret: "NAME", generate?: true }`. */
|
|
17
|
-
export type SecretRef = Secret;
|
|
18
|
-
/** A value in a data file: a literal, a reference to a declared resource, or a secret's marker. */
|
|
19
|
-
export type DataValue = null | boolean | number | string | Reference | SecretRef | DataValue[] | {
|
|
20
|
-
[field: string]: DataValue;
|
|
21
|
-
};
|
|
22
|
-
/** An `import { name } from "…"`: of a connector's constructors (`from "…/resources"`), or of another data file's resource. */
|
|
23
|
-
export interface DataFileImport {
|
|
24
|
-
kind: "constructors" | "file";
|
|
25
|
-
/** The specifier as written. */
|
|
26
|
-
source: string;
|
|
27
|
-
/** The name the other module exports. */
|
|
28
|
-
imported: string;
|
|
29
|
-
/** The name it has in this file. */
|
|
30
|
-
local: string;
|
|
31
|
-
start: number;
|
|
32
|
-
}
|
|
33
|
-
/** An `export const name = vendor.type({ … })`. */
|
|
34
|
-
export interface DataFileExport {
|
|
35
|
-
name: string;
|
|
36
|
-
/** The local name of the constructors it calls (`github`). */
|
|
37
|
-
vendor: string;
|
|
38
|
-
type: string;
|
|
39
|
-
desired: {
|
|
40
|
-
[field: string]: DataValue;
|
|
41
|
-
};
|
|
42
|
-
/** The `export const` statement. */
|
|
43
|
-
start: number;
|
|
44
|
-
end: number;
|
|
45
|
-
}
|
|
46
|
-
/** An `export const name = collection({ … })` in `resources/data.ts`, its declaration one `collection()` takes. */
|
|
47
|
-
export interface DataFileCollection {
|
|
48
|
-
/** The `export const`'s name. */
|
|
49
|
-
export: string;
|
|
50
|
-
/** The collection's name. */
|
|
51
|
-
name: string;
|
|
52
|
-
/** The `export const` statement. */
|
|
53
|
-
start: number;
|
|
54
|
-
end: number;
|
|
55
|
-
}
|
|
56
|
-
export interface DataFile {
|
|
57
|
-
imports: DataFileImport[];
|
|
58
|
-
/** Each `export const` whose value is in the subset. */
|
|
59
|
-
exports: DataFileExport[];
|
|
60
|
-
/** Each collection `resources/data.ts` declares (`COLLECTIONS_FILE`); none in any other data file. */
|
|
61
|
-
collections: DataFileCollection[];
|
|
62
|
-
/** Every name an `export const` declares as a resource, its value in the subset or not: none in `resources/data.ts`. */
|
|
63
|
-
declared: string[];
|
|
64
|
-
problems: DataFileProblem[];
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Where a data file imports `secret` from, and `sanoma`, the runtime's own resource constructors
|
|
68
|
-
* (its people: `sanoma.person`, `.agent`, `.group`); checked before `CONSTRUCTORS`, which they match too.
|
|
69
|
-
*/
|
|
70
|
-
export declare const SECRET_ENTRIES: ReadonlySet<string>;
|
|
71
|
-
/** Where `resources/data.ts` imports `collection` from, and the only imports it may make. */
|
|
72
|
-
export declare const DATA_ENTRIES: ReadonlySet<string>;
|
|
73
|
-
/**
|
|
74
|
-
* The one data file that holds collections, relative to `resources/`. Workflows and the runtime
|
|
75
|
-
* import it, so it imports only `collection` from "sanoma/data" and holds nothing else.
|
|
76
|
-
*/
|
|
77
|
-
export declare const COLLECTIONS_FILE = "data.ts";
|
|
78
|
-
/**
|
|
79
|
-
* Reads a data file's program without running it: its imports, each `export const` resource
|
|
80
|
-
* with its fields evaluated, and everything outside the data-file subset as a problem that says
|
|
81
|
-
* what to write instead.
|
|
82
|
-
*
|
|
83
|
-
* The subset: named imports, from a connector's `…/resources` entry or a `.ts` data file;
|
|
84
|
-
* `export const <name> = <vendor>.<type>({ … })`, one per statement, its argument built of
|
|
85
|
-
* strings (template literals without `${}` too), numbers, booleans, null, objects and arrays,
|
|
86
|
-
* and names of resources this file declares or imports; and `export default [a, b]`.
|
|
87
|
-
* `resources/data.ts` (`COLLECTIONS_FILE`) is the exception, the one data file workflows and the
|
|
88
|
-
* runtime import: it imports only `collection` from "sanoma/data", and each `export const` is
|
|
89
|
-
* `collection({ … })` with its values written out (`DataFile.collections`). No other data file
|
|
90
|
-
* declares a collection, and none imports one.
|
|
91
|
-
*
|
|
92
|
-
* `root` is the directory relative imports must stay in: by default the nearest `resources/`
|
|
93
|
-
* directory above `filename`, else the file's own.
|
|
94
|
-
*/
|
|
95
|
-
export declare function readDataFile(program: Node, filename: string, root?: string): DataFile;
|
|
96
|
-
/** A property's name: an identifier's, or a literal's as a string; undefined for any other key. */
|
|
97
|
-
export declare const keyName: (key: Node) => string | undefined;
|