@xandreed/runtime 0.12.0 → 0.14.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 +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
- package/dist/live-layer.adapter.js +2 -2
- package/package.json +7 -7
- package/dist/config.d.ts +0 -88
- package/dist/config.js +0 -50
- package/dist/config.test.d.ts +0 -1
- package/dist/plugins.d.ts +0 -6
- package/dist/plugins.js +0 -18
package/README.md
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Context, Effect, Exit, Ref, Scope, Semaphore } from "effect";
|
|
2
2
|
import * as Layers from "effect/Layer";
|
|
3
|
-
import { HarnessError } from "@xandreed/core";
|
|
3
|
+
import { HarnessError, replayableContext } from "@xandreed/core";
|
|
4
4
|
export const makeLiveLayer = (initial) => Effect.gen(function* () {
|
|
5
|
-
const dependencies = yield* Effect.context(), gate = yield* Semaphore.make(1), generations = yield* Ref.make(new Map), current = yield* Ref.make(0), closed = yield* Ref.make(!1), unavailable = () => new HarnessError({ code: "module.closed", message: "Module registry is closed" }), stage = (layer, id) => Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
|
|
5
|
+
const dependencies = replayableContext(yield* Effect.context()), gate = yield* Semaphore.make(1), generations = yield* Ref.make(new Map), current = yield* Ref.make(0), closed = yield* Ref.make(!1), unavailable = () => new HarnessError({ code: "module.closed", message: "Module registry is closed" }), stage = (layer, id) => Effect.uninterruptibleMask((restore) => Effect.gen(function* () {
|
|
6
6
|
const scope = yield* Scope.make(), memoMap = yield* Layers.makeMemoMap, services = yield* restore(Layers.buildWithMemoMap(layer, memoMap, scope).pipe(Effect.provide(dependencies))).pipe(Effect.onExit((exit) => Exit.isFailure(exit) ? Scope.close(scope, exit) : Effect.void));
|
|
7
7
|
return { id, services, scope, leases: 0, retired: !1 };
|
|
8
8
|
})), first = yield* stage(initial, 0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xandreed/runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
|
+
"engines": {
|
|
5
|
+
"node": ">=24",
|
|
6
|
+
"bun": ">=1.3.0"
|
|
7
|
+
},
|
|
4
8
|
"type": "module",
|
|
5
9
|
"main": "./dist/index.js",
|
|
6
10
|
"exports": {
|
|
@@ -10,8 +14,8 @@
|
|
|
10
14
|
}
|
|
11
15
|
},
|
|
12
16
|
"dependencies": {
|
|
13
|
-
"@xandreed/core": "0.
|
|
14
|
-
"effect": "4.0.
|
|
17
|
+
"@xandreed/core": "0.14.0",
|
|
18
|
+
"effect": "4.0.7"
|
|
15
19
|
},
|
|
16
20
|
"private": false,
|
|
17
21
|
"types": "./dist/index.d.ts",
|
|
@@ -22,9 +26,5 @@
|
|
|
22
26
|
"README.md",
|
|
23
27
|
"LICENSE"
|
|
24
28
|
],
|
|
25
|
-
"engines": {
|
|
26
|
-
"node": ">=24",
|
|
27
|
-
"bun": ">=1.3.0"
|
|
28
|
-
},
|
|
29
29
|
"license": "MIT"
|
|
30
30
|
}
|
package/dist/config.d.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
2
|
-
import { HarnessConfig, HarnessError } from "@xandreed/core";
|
|
3
|
-
import type { Plugin } from "@xandreed/core";
|
|
4
|
-
export interface ConfigSource {
|
|
5
|
-
readonly path: string;
|
|
6
|
-
readonly config: HarnessConfig;
|
|
7
|
-
readonly plugins?: ReadonlyArray<Plugin>;
|
|
8
|
-
}
|
|
9
|
-
export declare const mergeConfig: (base: HarnessConfig, overlay: HarnessConfig) => HarnessConfig;
|
|
10
|
-
export declare const decodeConfig: (input: unknown, source: string) => Effect.Effect<HarnessConfig, HarnessError>;
|
|
11
|
-
export declare const loadConfig: (options: {
|
|
12
|
-
readonly workspace: string;
|
|
13
|
-
readonly home: string;
|
|
14
|
-
readonly preset: HarnessConfig;
|
|
15
|
-
readonly profile?: string;
|
|
16
|
-
readonly invocation?: HarnessConfig;
|
|
17
|
-
}) => Effect.Effect<{
|
|
18
|
-
config: {
|
|
19
|
-
profile: string;
|
|
20
|
-
version: 1;
|
|
21
|
-
system?: string | undefined;
|
|
22
|
-
plugins?: readonly {
|
|
23
|
-
readonly id: string;
|
|
24
|
-
readonly use: string;
|
|
25
|
-
readonly options?: {
|
|
26
|
-
readonly [x: string]: unknown;
|
|
27
|
-
} | undefined;
|
|
28
|
-
readonly enabled?: boolean | undefined;
|
|
29
|
-
}[] | undefined;
|
|
30
|
-
bindings?: {
|
|
31
|
-
readonly [x: string]: string;
|
|
32
|
-
} | undefined;
|
|
33
|
-
profiles?: {
|
|
34
|
-
readonly [x: string]: {
|
|
35
|
-
readonly system?: string | undefined;
|
|
36
|
-
readonly plugins?: readonly {
|
|
37
|
-
readonly id: string;
|
|
38
|
-
readonly use: string;
|
|
39
|
-
readonly options?: {
|
|
40
|
-
readonly [x: string]: unknown;
|
|
41
|
-
} | undefined;
|
|
42
|
-
readonly enabled?: boolean | undefined;
|
|
43
|
-
}[] | undefined;
|
|
44
|
-
readonly bindings?: {
|
|
45
|
-
readonly [x: string]: string;
|
|
46
|
-
} | undefined;
|
|
47
|
-
};
|
|
48
|
-
} | undefined;
|
|
49
|
-
};
|
|
50
|
-
sources: {
|
|
51
|
-
path: string;
|
|
52
|
-
config: {
|
|
53
|
-
readonly version: 1;
|
|
54
|
-
readonly system?: string | undefined;
|
|
55
|
-
readonly plugins?: readonly {
|
|
56
|
-
readonly id: string;
|
|
57
|
-
readonly use: string;
|
|
58
|
-
readonly options?: {
|
|
59
|
-
readonly [x: string]: unknown;
|
|
60
|
-
} | undefined;
|
|
61
|
-
readonly enabled?: boolean | undefined;
|
|
62
|
-
}[] | undefined;
|
|
63
|
-
readonly bindings?: {
|
|
64
|
-
readonly [x: string]: string;
|
|
65
|
-
} | undefined;
|
|
66
|
-
readonly profile?: string | undefined;
|
|
67
|
-
readonly profiles?: {
|
|
68
|
-
readonly [x: string]: {
|
|
69
|
-
readonly system?: string | undefined;
|
|
70
|
-
readonly plugins?: readonly {
|
|
71
|
-
readonly id: string;
|
|
72
|
-
readonly use: string;
|
|
73
|
-
readonly options?: {
|
|
74
|
-
readonly [x: string]: unknown;
|
|
75
|
-
} | undefined;
|
|
76
|
-
readonly enabled?: boolean | undefined;
|
|
77
|
-
}[] | undefined;
|
|
78
|
-
readonly bindings?: {
|
|
79
|
-
readonly [x: string]: string;
|
|
80
|
-
} | undefined;
|
|
81
|
-
};
|
|
82
|
-
} | undefined;
|
|
83
|
-
};
|
|
84
|
-
}[];
|
|
85
|
-
plugins: Plugin[];
|
|
86
|
-
}, HarnessError, never>;
|
|
87
|
-
export declare const writeConfig: (path: string, config: HarnessConfig) => Effect.Effect<void, HarnessError, never>;
|
|
88
|
-
export declare const redact: (value: unknown) => unknown;
|
package/dist/config.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { access, mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
|
-
import { dirname, join } from "node:path";
|
|
3
|
-
import { pathToFileURL } from "node:url";
|
|
4
|
-
import { Effect, Option, Schema } from "effect";
|
|
5
|
-
import { HarnessConfig, HarnessError } from "@xandreed/core";
|
|
6
|
-
const mergeEntries = (left, right) => right.reduce((entries, incoming) => {
|
|
7
|
-
const old = entries.find((entry) => entry.id === incoming.id), next = old === void 0 || old.use !== incoming.use ? incoming : {
|
|
8
|
-
...old,
|
|
9
|
-
...incoming,
|
|
10
|
-
options: { ...old.options, ...incoming.options }
|
|
11
|
-
};
|
|
12
|
-
return old === void 0 ? [...entries, next] : entries.map((entry) => entry.id === incoming.id ? next : entry);
|
|
13
|
-
}, left);
|
|
14
|
-
export const mergeConfig = (base, overlay) => ({
|
|
15
|
-
...base,
|
|
16
|
-
...overlay,
|
|
17
|
-
plugins: mergeEntries(base.plugins ?? [], overlay.plugins ?? []),
|
|
18
|
-
bindings: { ...base.bindings, ...overlay.bindings },
|
|
19
|
-
profiles: { ...base.profiles, ...overlay.profiles }
|
|
20
|
-
}), decodeConfig = (input, source) => Schema.decodeUnknownEffect(HarnessConfig)(input, { onExcessProperty: "error", reportInput: !0 }).pipe(Effect.mapError((error) => new HarnessError({ code: "config.invalid", message: `${source}: ${String(error)}` })));
|
|
21
|
-
const io = (action, label) => Effect.tryPromise({
|
|
22
|
-
try: action,
|
|
23
|
-
catch: (error) => new HarnessError({ code: "config.io", message: `${label}: ${String(error)}` })
|
|
24
|
-
}), exists = (path) => Effect.tryPromise({ try: () => access(path), catch: () => !1 }).pipe(Effect.as(!0), Effect.orElseSucceed(() => !1)), readJson = (path) => io(() => readFile(path, "utf8"), path).pipe(Effect.flatMap((text) => Schema.decodeUnknownEffect(Schema.fromJsonString(HarnessConfig))(text, { onExcessProperty: "error", reportInput: !0 })), Effect.mapError((error) => new HarnessError({ code: "config.invalid", message: `${path}: ${String(error)}` }))), readBase = (directory) => Effect.gen(function* () {
|
|
25
|
-
const json = join(directory, "efferent.config.json"), ts = join(directory, "efferent.config.ts"), found = yield* Effect.filter([json, ts], exists);
|
|
26
|
-
if (found.length > 1)
|
|
27
|
-
return yield* Effect.fail(new HarnessError({ code: "config.ambiguous", message: `Choose one base configuration in ${directory}: JSON or TypeScript` }));
|
|
28
|
-
const path = found[0];
|
|
29
|
-
if (path === void 0)
|
|
30
|
-
return Option.none();
|
|
31
|
-
if (!path.endsWith(".ts"))
|
|
32
|
-
return Option.some({ path, config: yield* readJson(path) });
|
|
33
|
-
const module = yield* io(() => import(`${pathToFileURL(path).href}?revision=${Date.now()}`), path), config = yield* decodeConfig(module.default, path), plugins = module.plugins ?? [];
|
|
34
|
-
if (!Array.isArray(plugins) || plugins.some((value) => typeof value !== "object" || value === null || !("build" in value) || typeof value.build !== "function" || !("id" in value) || typeof value.id !== "string"))
|
|
35
|
-
return yield* Effect.fail(new HarnessError({ code: "config.plugins", message: `${path}: plugins must be an array of definePlugin definitions` }));
|
|
36
|
-
return Option.some({ path, config, plugins });
|
|
37
|
-
});
|
|
38
|
-
export const loadConfig = (options) => Effect.gen(function* () {
|
|
39
|
-
const sources = (yield* Effect.forEach([join(options.home, ".efferent"), options.workspace], readBase)).flatMap(Option.toArray), combined = sources.reduce((config, source) => mergeConfig(config, source.config), options.preset), profileName = options.profile ?? combined.profile ?? "smith", profile = combined.profiles?.[profileName];
|
|
40
|
-
if (profile === void 0 && profileName !== "smith")
|
|
41
|
-
return yield* Effect.fail(new HarnessError({ code: "config.profile", message: `Unknown profile: ${profileName}` }));
|
|
42
|
-
const selected = profile === void 0 ? combined : mergeConfig(combined, { version: 1, ...profile }), overridesPath = join(options.workspace, ".efferent", "overrides.json"), override = (yield* exists(overridesPath)) ? yield* readJson(overridesPath) : { version: 1 }, allSources = [...sources, { path: `profile:${profileName}`, config: { version: 1, ...profile } }, { path: overridesPath, config: override }];
|
|
43
|
-
return { config: { ...mergeConfig(mergeConfig(selected, override), options.invocation ?? { version: 1 }), profile: profileName }, sources: allSources.map(({ path, config }) => ({ path, config })), plugins: sources.flatMap((source) => source.plugins ?? []) };
|
|
44
|
-
}), writeConfig = (path, config) => decodeConfig(config, path).pipe(Effect.flatMap((validated) => io(async () => {
|
|
45
|
-
await mkdir(dirname(path), { recursive: !0 });
|
|
46
|
-
const temporary = `${path}.${crypto.randomUUID()}.tmp`;
|
|
47
|
-
await writeFile(temporary, `${JSON.stringify(validated, null, 2)}
|
|
48
|
-
`, { mode: 384 });
|
|
49
|
-
await rename(temporary, path);
|
|
50
|
-
}, path))), redact = (value) => Array.isArray(value) ? value.map(redact) : value !== null && typeof value === "object" ? Object.fromEntries(Object.entries(value).map(([key, item]) => [key, /secret|password|token|api.?key|credential/i.test(key) ? "[redacted]" : redact(item)])) : value;
|
package/dist/config.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/plugins.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
2
|
-
import { HarnessError } from "@xandreed/core";
|
|
3
|
-
import type { HarnessConfig, Plugin } from "@xandreed/core";
|
|
4
|
-
export declare const loadPlugins: (config: HarnessConfig, builtins: ReadonlyArray<Plugin>, workspace: string, home: string) => Effect.Effect<ReadonlyArray<Plugin>, HarnessError>;
|
|
5
|
-
/** A plugin's options as one JSON Schema, its definitions inline under `$defs`. */
|
|
6
|
-
export declare const pluginSchema: (plugin: Plugin) => import("effect/JsonSchema").JsonSchema;
|
package/dist/plugins.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
import { join, resolve } from "node:path";
|
|
3
|
-
import { pathToFileURL } from "node:url";
|
|
4
|
-
import { Effect, Schema } from "effect";
|
|
5
|
-
import { HarnessError } from "@xandreed/core";
|
|
6
|
-
export const loadPlugins = (config, builtins, workspace, home) => Effect.reduce([...new Set((config.plugins ?? []).filter((entry) => entry.enabled !== !1).map((entry) => entry.use))], () => builtins, (loaded, use) => {
|
|
7
|
-
if (loaded.some((plugin) => plugin.id === use))
|
|
8
|
-
return Effect.succeed(loaded);
|
|
9
|
-
return (use.startsWith(".") || use.startsWith("/") ? Effect.succeed(resolve(workspace, use)) : Effect.try(() => createRequire(join(workspace, "package.json")).resolve(use)).pipe(Effect.catch(() => Effect.try(() => createRequire(join(home, ".efferent/plugins/package.json")).resolve(use))))).pipe(Effect.flatMap((path) => Effect.tryPromise(() => import(pathToFileURL(path).href))), Effect.map((module) => {
|
|
10
|
-
const value = module.default;
|
|
11
|
-
if (value === null || typeof value !== "object" || typeof value.build !== "function" || !Array.isArray(value.requires) || !Array.isArray(value.provides) || typeof value.version !== "string")
|
|
12
|
-
return { valid: !1 };
|
|
13
|
-
return { valid: !0, plugin: { ...value, id: use } };
|
|
14
|
-
}), Effect.mapError((error) => new HarnessError({ code: "plugin.load", plugin: use, message: `Cannot load installed plugin ${use}: ${String(error)}` })), Effect.flatMap((result) => result.valid ? Effect.succeed([...loaded, result.plugin]) : Effect.fail(new HarnessError({ code: "plugin.manifest", plugin: use, message: "The module must default-export a definePlugin definition" }))));
|
|
15
|
-
}), pluginSchema = (plugin) => {
|
|
16
|
-
const document = Schema.toJsonSchemaDocument(plugin.schema);
|
|
17
|
-
return Object.keys(document.definitions).length > 0 ? { ...document.schema, $defs: document.definitions } : document.schema;
|
|
18
|
-
};
|