@thinair/factory 0.1.170 → 0.1.177
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/cli/evaluate.ts
CHANGED
|
@@ -26,12 +26,8 @@ export const EvaluateInput = Schema.Struct({
|
|
|
26
26
|
});
|
|
27
27
|
export type EvaluateInput = typeof EvaluateInput.Type;
|
|
28
28
|
|
|
29
|
-
const EvaluateNotice = Schema.Struct({ node: Schema.NullOr(NodeId), at: Schema.NullOr(SourceAt), message: Schema.String });
|
|
30
|
-
|
|
31
29
|
export const EvaluateOutput = Schema.Struct({
|
|
32
30
|
program: Schema.NullOr(Program),
|
|
33
|
-
/** Notes that hold nothing: a retired constructor the code still calls runs as its replacement. */
|
|
34
|
-
notices: Schema.Array(EvaluateNotice),
|
|
35
31
|
problems: Schema.Array(Schema.Struct({
|
|
36
32
|
/** `evaluate`: the code threw while it loaded. `value`: the encoder's checks. */
|
|
37
33
|
step: Schema.Literals(["evaluate", "value"]),
|
|
@@ -77,13 +73,12 @@ const evaluate = Effect.fn("factory.evaluate")(function* (inputPath: string) {
|
|
|
77
73
|
hash: (text) => createHash("sha256").update(text).digest("hex"),
|
|
78
74
|
files: input.files,
|
|
79
75
|
});
|
|
80
|
-
return { program: encoded.program,
|
|
76
|
+
return { program: encoded.program, problems: encoded.problems.map((problem) => ({ step: "value" as const, ...problem })) };
|
|
81
77
|
},
|
|
82
78
|
catch: (error) => error,
|
|
83
79
|
})),
|
|
84
80
|
Effect.catch((error) => Effect.succeed<EvaluateOutput>({
|
|
85
81
|
program: null,
|
|
86
|
-
notices: [],
|
|
87
82
|
problems: [{ step: "evaluate", node: null, at: throwAt(error, input.tree), message: `The factory's code threw while it loaded: ${error instanceof Error ? error.message : String(error)}` }],
|
|
88
83
|
})),
|
|
89
84
|
);
|
package/cli/load.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { createRequire } from "node:module";
|
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { Context, Effect, Layer, Result, Schema } from "effect";
|
|
9
|
-
import {
|
|
9
|
+
import { LOAD_VERSION, LoadJson, LoadPost, type Finding, type Load } from "../domain/load";
|
|
10
10
|
import { appShapeProblems, AUTHORING_MODULES, folderKey, IGNORED_FOLDERS, isLoadedPath, memberFiles } from "../domain/system-elements/app-shape";
|
|
11
11
|
import { FrameworkVersion, holesOf, type Node, type Program } from "../domain/system-elements/program";
|
|
12
12
|
import { EvaluateOutputJson, type EvaluateInput } from "./evaluate";
|
|
@@ -272,7 +272,6 @@ const evaluate = Effect.fn("factory.load.evaluate")(function* (input: Omit<Evalu
|
|
|
272
272
|
program: output.program,
|
|
273
273
|
findings: [
|
|
274
274
|
...output.problems.map((problem) => finding(problem.step, problem.message, { node: problem.node, file: problem.at?.file ?? null, line: problem.at?.line ?? null })),
|
|
275
|
-
...output.notices.map((notice) => ({ ...finding("value", notice.message, { node: notice.node, file: notice.at?.file ?? null, line: notice.at?.line ?? null }), notice: true as const })),
|
|
276
275
|
],
|
|
277
276
|
};
|
|
278
277
|
});
|
|
@@ -357,7 +356,7 @@ export const loadFactory = Effect.fn("factory.load")(function* (input: Readonly<
|
|
|
357
356
|
|
|
358
357
|
const load: Load = yield* timed(steps, "encode", Effect.sync((): Load => {
|
|
359
358
|
const all = findings.map(pinned(program));
|
|
360
|
-
return { version: LOAD_VERSION, framework, key: snapshot.key, state: all.
|
|
359
|
+
return { version: LOAD_VERSION, framework, key: snapshot.key, state: all.length > 0 ? "held" : "ready", program, holes: program === null ? [] : holesOf(program), findings: all, cached: false, steps };
|
|
361
360
|
}));
|
|
362
361
|
const encoded = yield* Schema.encodeEffect(LoadJson)(load).pipe(Effect.mapError((error) => new FactoryLoadFailed({ message: `The load does not encode: ${error.message}` })));
|
|
363
362
|
yield* Effect.tryPromise({ try: () => mkdir(path.dirname(cacheFile), { recursive: true }).then(() => writeFile(cacheFile, JSON.stringify(encoded))), catch: failed("Could not write the load cache.") });
|
package/domain/load.ts
CHANGED
|
@@ -22,12 +22,7 @@ export const Finding = Schema.Struct({
|
|
|
22
22
|
file: Schema.NullOr(Schema.String),
|
|
23
23
|
line: Schema.NullOr(Schema.Int),
|
|
24
24
|
message: Schema.String,
|
|
25
|
-
/** A note that holds nothing: the code still runs as written (a retired constructor runs as its replacement). */
|
|
26
|
-
notice: Schema.optionalKey(Schema.Literal(true)),
|
|
27
25
|
});
|
|
28
|
-
|
|
29
|
-
/** Whether a finding holds the load: every finding but a notice does. */
|
|
30
|
-
export const holds = (finding: Readonly<{ notice?: true | undefined }>) => finding.notice !== true;
|
|
31
26
|
export type Finding = typeof Finding.Type;
|
|
32
27
|
|
|
33
28
|
/** `ready` runs (live) or draws (preview). `held` keeps the last good revision: its findings say why. */
|
|
@@ -158,8 +158,6 @@ export interface Encoder {
|
|
|
158
158
|
readonly ref: <T extends NodeTag>(to: T, value: Slot<T>) => Ref<T>;
|
|
159
159
|
/** A problem with this node's settings, pinned to it. */
|
|
160
160
|
readonly problem: (message: string) => void;
|
|
161
|
-
/** A note on this node that holds nothing, pinned to it: its code still runs as written. */
|
|
162
|
-
readonly notice: (message: string) => void;
|
|
163
161
|
}
|
|
164
162
|
|
|
165
163
|
/** The rows a floor reads, by the node each belongs to. The records name their nodes by id. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
|
-
import { AUTHORED, authoredHead, head, type Authored, type
|
|
2
|
+
import { AUTHORED, authoredHead, head, type Authored, type LiveRows } from "./node";
|
|
3
3
|
|
|
4
4
|
/*
|
|
5
5
|
* A pool names a provider and an optional narrowing, nothing else.
|
|
@@ -13,19 +13,11 @@ import { AUTHORED, authoredHead, head, type Authored, type Encoder, type LiveRow
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
export const PROVIDERS = ["fleet", "github-actions"] as const;
|
|
16
|
-
|
|
17
|
-
* Providers a program stored before one `fleet` took their place may still name (`pool:macs`, `pool:thinair-cloud`,
|
|
18
|
-
* `pool:namespace`): read so a running factory's program stays readable until its next live load, never authored.
|
|
19
|
-
* Placement reads `macs` and `thinair-cloud` as `fleet`; `namespace` never had a filling.
|
|
20
|
-
*/
|
|
21
|
-
export const RETIRED_PROVIDERS = ["macs", "thinair-cloud", "namespace"] as const;
|
|
22
|
-
export const Provider = Schema.Literals([...PROVIDERS, ...RETIRED_PROVIDERS]);
|
|
16
|
+
export const Provider = Schema.Literals(PROVIDERS);
|
|
23
17
|
export type Provider = typeof Provider.Type;
|
|
24
|
-
export type AuthoredProvider = (typeof PROVIDERS)[number];
|
|
25
|
-
export type RetiredProvider = (typeof RETIRED_PROVIDERS)[number];
|
|
26
18
|
|
|
27
|
-
/** Whether a pool names the owner's fleet
|
|
28
|
-
export const isFleet = (provider: Provider) => provider === "fleet"
|
|
19
|
+
/** Whether a pool names the owner's fleet. */
|
|
20
|
+
export const isFleet = (provider: Provider) => provider === "fleet";
|
|
29
21
|
|
|
30
22
|
export const Os = Schema.Literals(["mac", "linux", "windows"]);
|
|
31
23
|
export type Os = typeof Os.Type;
|
|
@@ -39,37 +31,21 @@ export type Narrowing = typeof Narrowing.Type;
|
|
|
39
31
|
export const Node = Schema.TaggedStruct("Pool", { ...head, provider: Provider, ...Narrowing.fields });
|
|
40
32
|
export type Node = typeof Node.Type;
|
|
41
33
|
|
|
42
|
-
export interface AuthoredPool<P extends
|
|
34
|
+
export interface AuthoredPool<P extends Provider = Provider> extends Authored<"Pool", string> {
|
|
43
35
|
readonly provider: P;
|
|
44
36
|
readonly os: ReadonlyArray<Os>;
|
|
45
37
|
readonly arch: ReadonlyArray<Arch>;
|
|
46
|
-
/** The retired constructor that made it, when one did: it runs as the fleet, and the load says so. */
|
|
47
|
-
readonly retired?: RetiredProvider;
|
|
48
38
|
}
|
|
49
39
|
|
|
50
40
|
type Narrow = Readonly<{ os?: ReadonlyArray<Os>; arch?: ReadonlyArray<Arch> }>;
|
|
51
41
|
|
|
52
|
-
const made = <const P extends
|
|
42
|
+
const made = <const P extends Provider>(provider: P, narrow: Narrow | undefined): AuthoredPool<P> => ({
|
|
53
43
|
[AUTHORED]: authoredHead("Pool", provider, false),
|
|
54
44
|
provider,
|
|
55
45
|
os: narrow?.os ?? [],
|
|
56
46
|
arch: narrow?.arch ?? [],
|
|
57
47
|
});
|
|
58
48
|
|
|
59
|
-
/**
|
|
60
|
-
* A retired constructor, kept so code written before one fleet still loads and its passes still run: it makes the
|
|
61
|
-
* fleet with the same narrowing, under its own name (`pool:macs`), so its node and every Ref to it stay as they were.
|
|
62
|
-
*/
|
|
63
|
-
const retired = (name: RetiredProvider) => (narrow?: Narrow): AuthoredPool<"fleet"> => ({
|
|
64
|
-
[AUTHORED]: authoredHead("Pool", name, false),
|
|
65
|
-
provider: "fleet",
|
|
66
|
-
os: narrow?.os ?? [],
|
|
67
|
-
arch: narrow?.arch ?? [],
|
|
68
|
-
retired: name,
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
const RETIRED_CALL: Readonly<Record<RetiredProvider, string>> = { macs: "pool.macs()", "thinair-cloud": "pool.thinairCloud()", namespace: "pool.namespace()" };
|
|
72
|
-
|
|
73
49
|
/** The pools a factory can name. Each is named after its provider: `pool:fleet`. */
|
|
74
50
|
export const pool = {
|
|
75
51
|
/**
|
|
@@ -81,19 +57,9 @@ export const pool = {
|
|
|
81
57
|
fleet: (narrow?: Narrow) => made("fleet", narrow),
|
|
82
58
|
/** GitHub Actions runners, dispatched with the Agents App. */
|
|
83
59
|
githubActions: (narrow?: Narrow) => made("github-actions", narrow),
|
|
84
|
-
/** @deprecated The fleet: use `pool.fleet()`. It makes the fleet with this narrowing, and the load says so. */
|
|
85
|
-
macs: retired("macs"),
|
|
86
|
-
/** @deprecated The fleet, which Thinair Cloud joins once the owner names the factory's Cloud Organization: use `pool.fleet()`. */
|
|
87
|
-
thinairCloud: retired("thinair-cloud"),
|
|
88
|
-
/** @deprecated Never had a filling: use `pool.fleet()`. It makes the fleet with this narrowing, and the load says so. */
|
|
89
|
-
namespace: retired("namespace"),
|
|
90
60
|
};
|
|
91
61
|
|
|
92
|
-
export const make = (authored: AuthoredPool,
|
|
93
|
-
if (authored.retired !== undefined)
|
|
94
|
-
encoder.notice(`${RETIRED_CALL[authored.retired]} is pool.fleet() now: it runs as the fleet with the same narrowing. Rename it to pool.fleet().`);
|
|
95
|
-
return { provider: authored.provider, os: [...authored.os], arch: [...authored.arch] };
|
|
96
|
-
};
|
|
62
|
+
export const make = (authored: AuthoredPool) => ({ provider: authored.provider, os: [...authored.os], arch: [...authored.arch] });
|
|
97
63
|
|
|
98
64
|
/** The machines placement can use now, from their own facts. */
|
|
99
65
|
export const live = (node: Node, rows: LiveRows) => {
|
|
@@ -67,7 +67,7 @@ export type EncodeOptions = Readonly<{
|
|
|
67
67
|
files: Readonly<Record<string, FileFacts>>;
|
|
68
68
|
}>;
|
|
69
69
|
|
|
70
|
-
export type Encoded = Readonly<{ program: Program | null; problems: ReadonlyArray<EncodeProblem
|
|
70
|
+
export type Encoded = Readonly<{ program: Program | null; problems: ReadonlyArray<EncodeProblem> }>;
|
|
71
71
|
|
|
72
72
|
/** JSON with keys sorted at every level: two values are the same when this is. */
|
|
73
73
|
const canonicalJson = (value: unknown): string => JSON.stringify(value, (_key, item: unknown) =>
|
|
@@ -80,7 +80,6 @@ type Draft = Readonly<{ node: Readonly<Record<string, unknown>> & { readonly at:
|
|
|
80
80
|
/** Encode a factory's root, as `factory.ts` exports it, into its Program. */
|
|
81
81
|
export const encodeFactory = (root: unknown, options: EncodeOptions): Encoded => {
|
|
82
82
|
const problems: EncodeProblem[] = [];
|
|
83
|
-
const notices: EncodeProblem[] = [];
|
|
84
83
|
const drafts = new Map<NodeId, Draft>();
|
|
85
84
|
const encodedValues = new Map<object, NodeId>();
|
|
86
85
|
const visiting = new Set<object>();
|
|
@@ -118,7 +117,6 @@ export const encodeFactory = (root: unknown, options: EncodeOptions): Encoded =>
|
|
|
118
117
|
const encoder: Encoder = {
|
|
119
118
|
ref: (tag, slot) => encode(tag, slot, { node: id, at }),
|
|
120
119
|
problem: (message) => problems.push({ node: id, at, message }),
|
|
121
|
-
notice: (message) => notices.push({ node: id, at, message }),
|
|
122
120
|
};
|
|
123
121
|
const settings = (definitions[made.tag] as Definition<Tag>).make(value as never, encoder);
|
|
124
122
|
visiting.delete(value);
|
|
@@ -134,10 +132,10 @@ export const encodeFactory = (root: unknown, options: EncodeOptions): Encoded =>
|
|
|
134
132
|
|
|
135
133
|
if (!isAuthored(root) || headOf(root).tag !== "Factory") {
|
|
136
134
|
problems.push({ node: null, at: { file: "factory.ts", line: 1 }, message: "factory.ts's default export is the factory: export default defineFactory({ ... })." });
|
|
137
|
-
return { program: null, problems
|
|
135
|
+
return { program: null, problems };
|
|
138
136
|
}
|
|
139
137
|
encode("Factory", root, null);
|
|
140
|
-
if (broken) return { program: null, problems
|
|
138
|
+
if (broken) return { program: null, problems };
|
|
141
139
|
|
|
142
140
|
// A node's digest covers its content and, when it holds code, the import closure of its file,
|
|
143
141
|
// stopping at files that make other nodes: those nodes have digests of their own.
|
|
@@ -167,9 +165,9 @@ export const encodeFactory = (root: unknown, options: EncodeOptions): Encoded =>
|
|
|
167
165
|
const decoded = Schema.decodeUnknownResult(Program)({ version: LANGUAGE_VERSION, framework: options.framework, root: ROOT_ID, nodes });
|
|
168
166
|
if (Result.isFailure(decoded)) {
|
|
169
167
|
problems.push({ node: null, at: null, message: `The program does not decode: ${decoded.failure.message}` });
|
|
170
|
-
return { program: null, problems
|
|
168
|
+
return { program: null, problems };
|
|
171
169
|
}
|
|
172
170
|
for (const problem of graphProblems(decoded.success))
|
|
173
171
|
problems.push({ node: problem.node, at: (decoded.success.nodes[problem.node] as Node | undefined)?.at ?? null, message: problem.message });
|
|
174
|
-
return { program: decoded.success, problems
|
|
172
|
+
return { program: decoded.success, problems };
|
|
175
173
|
};
|
package/package.json
CHANGED