@reventlessdev/reventless-local 3.0.0-alpha.180 → 3.0.0-alpha.181
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/CHANGELOG.md +7 -0
- package/emit-capabilities.mjs +2 -52
- package/package.json +7 -7
- package/src/EmitCapabilities.res +113 -0
- package/src/EmitCapabilities.res.mjs +70 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 3.0.0-alpha.181 (2026-07-30)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **spec,local:** resolve capability manifests beyond the example topology ([8bfadd5](https://github.com/ReventlessDev/reventless-core/commit/8bfadd5beef90c0c9f6f6f3fbb753c8b789469ed))
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
# 3.0.0-alpha.180 (2026-07-30)
|
|
7
14
|
|
|
8
15
|
### Bug Fixes
|
package/emit-capabilities.mjs
CHANGED
|
@@ -1,53 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
// Applies the plugin's compiled composition root to the local platform and
|
|
5
|
-
// renders the structure-derived capability manifest — the same
|
|
6
|
-
// `pluginStructure.requiredStores` walk the deployed plugin reports at
|
|
7
|
-
// runtime, never a second scan of the sources. Untyped by necessity: a
|
|
8
|
-
// dynamically imported module cannot be functor-applied from ReScript, so the
|
|
9
|
-
// reflection lives here and everything typed lives in
|
|
10
|
-
// `Reventless.CapabilityManifest`.
|
|
11
|
-
//
|
|
12
|
-
// Usage: emit-capabilities <srcDir> (run from the plugin package, after `rescript build`)
|
|
13
|
-
|
|
14
|
-
import fs from "node:fs";
|
|
15
|
-
import path from "node:path";
|
|
16
|
-
import { pathToFileURL } from "node:url";
|
|
17
|
-
|
|
18
|
-
// The local platform defaults to Debug-level logging; a build step should not.
|
|
19
|
-
if (process.env.LOG_LEVEL === undefined) process.env.LOG_LEVEL = "warn";
|
|
20
|
-
|
|
21
|
-
const srcDirArg = process.argv[2];
|
|
22
|
-
if (srcDirArg === undefined || srcDirArg === "") {
|
|
23
|
-
console.error("Usage: emit-capabilities <srcDir>");
|
|
24
|
-
process.exit(1);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const srcDir = path.resolve(srcDirArg);
|
|
28
|
-
const pluginModulePath = path.join(srcDir, "Plugin.res.mjs");
|
|
29
|
-
if (!fs.existsSync(pluginModulePath)) {
|
|
30
|
-
console.error(
|
|
31
|
-
`emit-capabilities: ${pluginModulePath} not found — run \`rescript build\` first.`,
|
|
32
|
-
);
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const LocalPlatform = await import(
|
|
37
|
-
new URL("./src/Platform.res.mjs", import.meta.url)
|
|
38
|
-
);
|
|
39
|
-
const CapabilityManifest = await import(
|
|
40
|
-
"@reventlessdev/reventless-spec/src/components/CapabilityManifest.res.mjs"
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
const Platform = LocalPlatform.Make();
|
|
44
|
-
const Plugin = await import(pathToFileURL(pluginModulePath));
|
|
45
|
-
const { pluginStructure } = Plugin.Make(Platform);
|
|
46
|
-
|
|
47
|
-
const manifestPath = path.join(srcDir, "capabilities.json");
|
|
48
|
-
fs.writeFileSync(manifestPath, CapabilityManifest.renderForStructure(pluginStructure));
|
|
49
|
-
console.log("Generated: " + manifestPath);
|
|
50
|
-
|
|
51
|
-
// Applying the platform functor wires in-process infrastructure; exit
|
|
52
|
-
// explicitly so no lingering handle keeps the build step alive.
|
|
53
|
-
process.exit(0);
|
|
2
|
+
import { main } from "./src/EmitCapabilities.res.mjs"
|
|
3
|
+
await main()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-local",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.181",
|
|
4
4
|
"description": "Local platform for Reventless (in-memory or SQLite backend, for development and testing without AWS)",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
@@ -39,12 +39,12 @@
|
|
|
39
39
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.9",
|
|
40
40
|
"@reventlessdev/rescript-mcp-sdk": "1.0.0-alpha.20",
|
|
41
41
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.17",
|
|
42
|
-
"@reventlessdev/reventless-core": "3.0.0-alpha.
|
|
43
|
-
"@reventlessdev/reventless-
|
|
44
|
-
"@reventlessdev/reventless-
|
|
45
|
-
"@reventlessdev/reventless-postgres": "3.0.0-alpha.
|
|
46
|
-
"@reventlessdev/reventless-
|
|
47
|
-
"@reventlessdev/reventless-
|
|
42
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.193",
|
|
43
|
+
"@reventlessdev/reventless-gwt": "1.0.0-alpha.140",
|
|
44
|
+
"@reventlessdev/reventless-graphql-server": "1.0.0-alpha.41",
|
|
45
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.57",
|
|
46
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.111",
|
|
47
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.86"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"rescript": "12.3.0",
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Emit `capabilities.json` beside a plugin's compiled composition root.
|
|
2
|
+
//
|
|
3
|
+
// Applies the plugin's composition root to the local platform and renders the
|
|
4
|
+
// structure-derived capability manifest — the same `pluginStructure` walk the
|
|
5
|
+
// deployed plugin reports at runtime, never a second scan of the sources.
|
|
6
|
+
//
|
|
7
|
+
// The two dynamically imported modules are the only untyped surface, and they
|
|
8
|
+
// are typed *at the boundary*: `Platform.res.mjs` and every composition root
|
|
9
|
+
// emit exactly the exports declared below, so `pluginStructure` arrives as
|
|
10
|
+
// `Reventless.Plugin.pluginStructure` and everything downstream is ordinary
|
|
11
|
+
// typed ReScript. Same shape as `ReventlessGwt.LocalHost`, which reflects the
|
|
12
|
+
// domain graph this way.
|
|
13
|
+
//
|
|
14
|
+
// The platform is reached through a dynamic import rather than by applying
|
|
15
|
+
// `Platform.Make` directly: a composition root's `Make` is a JS function
|
|
16
|
+
// expecting the platform *value*, and a ReScript module is not one. Importing
|
|
17
|
+
// it puts both sides on the same footing, and it keeps `LOG_LEVEL` (set below)
|
|
18
|
+
// in force before the platform's logger initialises.
|
|
19
|
+
//
|
|
20
|
+
// Usage: emit-capabilities <srcDir> [<compositionModule>]
|
|
21
|
+
// (run from the plugin package, after `rescript build`)
|
|
22
|
+
|
|
23
|
+
// ── Node ─────────────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
@module("node:fs") external existsSync: string => bool = "existsSync"
|
|
26
|
+
@module("node:fs") external writeFileSync: (string, string, @as("utf8") _) => unit = "writeFileSync"
|
|
27
|
+
|
|
28
|
+
@module("node:path") @variadic external join: array<string> => string = "join"
|
|
29
|
+
@module("node:path") @variadic external resolve: array<string> => string = "resolve"
|
|
30
|
+
@module("node:path") @val external sep: string = "sep"
|
|
31
|
+
|
|
32
|
+
@module("node:url") external pathToFileURL: string => {"href": string} = "pathToFileURL"
|
|
33
|
+
|
|
34
|
+
@val @scope("process") external argv: array<string> = "argv"
|
|
35
|
+
@val @scope("process") external env: Dict.t<string> = "env"
|
|
36
|
+
@val external processExit: int => unit = "process.exit"
|
|
37
|
+
|
|
38
|
+
// Emitted by ReScript as a literal `import(...)` expression — the composition
|
|
39
|
+
// root's path is only known at run time, so it cannot be a static binding.
|
|
40
|
+
@val external dynImport: string => promise<'a> = "import"
|
|
41
|
+
|
|
42
|
+
// ── The dynamic-import boundary ──────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
type platform
|
|
45
|
+
type localPlatformExports = {"Make": unit => platform}
|
|
46
|
+
type builtPlugin = {"pluginStructure": Reventless.Plugin.pluginStructure}
|
|
47
|
+
type compositionExports = {"Make": platform => builtPlugin}
|
|
48
|
+
|
|
49
|
+
// ── Which module holds the composition root ──────────────────────────────────
|
|
50
|
+
|
|
51
|
+
/** `Plugin.res.mjs` is what `generate-plugin` emits, so it is the default and
|
|
52
|
+
every existing caller is unaffected. A hand-written composition root is
|
|
53
|
+
named after its plugin instead and says so: a bare module name (`Catalog`)
|
|
54
|
+
resolves inside `<srcDir>`, a path (`../lib/Catalog.res.mjs`) from the
|
|
55
|
+
working directory. */
|
|
56
|
+
let compositionModulePath = (~srcDir: string, ~moduleArg: option<string>): string => {
|
|
57
|
+
let withExtension = name => name->String.endsWith(".mjs") ? name : name ++ ".res.mjs"
|
|
58
|
+
switch moduleArg {
|
|
59
|
+
| None | Some("") => join([srcDir, "Plugin.res.mjs"])
|
|
60
|
+
| Some(name) =>
|
|
61
|
+
name->String.includes("/") || name->String.includes(sep)
|
|
62
|
+
? resolve([withExtension(name)])
|
|
63
|
+
: join([srcDir, withExtension(name)])
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ── Entry point ──────────────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
let fail = (message: string) => {
|
|
70
|
+
Console.error("emit-capabilities: " ++ message)
|
|
71
|
+
processExit(1)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let main = async () => {
|
|
75
|
+
// The local platform defaults to Debug-level logging; a build step should not.
|
|
76
|
+
switch env->Dict.get("LOG_LEVEL") {
|
|
77
|
+
| Some(_) => ()
|
|
78
|
+
| None => env->Dict.set("LOG_LEVEL", "warn")
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
switch argv->Array.get(2) {
|
|
82
|
+
| None | Some("") => {
|
|
83
|
+
Console.error("Usage: emit-capabilities <srcDir> [<compositionModule>]")
|
|
84
|
+
processExit(1)
|
|
85
|
+
}
|
|
86
|
+
| Some(srcDirArg) => {
|
|
87
|
+
let srcDir = resolve([srcDirArg])
|
|
88
|
+
let modulePath = compositionModulePath(~srcDir, ~moduleArg=argv->Array.get(3))
|
|
89
|
+
if !existsSync(modulePath) {
|
|
90
|
+
fail(`${modulePath} not found — run \`rescript build\` first.`)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Relative specifier: resolved against this module, so it finds the
|
|
94
|
+
// sibling compiled platform whatever the working directory is.
|
|
95
|
+
let platformModule: localPlatformExports = await dynImport("./Platform.res.mjs")
|
|
96
|
+
let platform = platformModule["Make"]()
|
|
97
|
+
|
|
98
|
+
let composition: compositionExports = await dynImport(pathToFileURL(modulePath)["href"])
|
|
99
|
+
let built = composition["Make"](platform)
|
|
100
|
+
|
|
101
|
+
let manifestPath = join([srcDir, "capabilities.json"])
|
|
102
|
+
writeFileSync(
|
|
103
|
+
manifestPath,
|
|
104
|
+
Reventless.CapabilityManifest.renderForStructure(built["pluginStructure"]),
|
|
105
|
+
)
|
|
106
|
+
Console.log("Generated: " ++ manifestPath)
|
|
107
|
+
|
|
108
|
+
// Applying the platform functor wires in-process infrastructure; exit
|
|
109
|
+
// explicitly so no lingering handle keeps the build step alive.
|
|
110
|
+
processExit(0)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Nodefs from "node:fs";
|
|
4
|
+
import * as Nodeurl from "node:url";
|
|
5
|
+
import * as Nodepath from "node:path";
|
|
6
|
+
import * as CapabilityManifest$Reventless from "@reventlessdev/reventless-spec/src/components/CapabilityManifest.res.mjs";
|
|
7
|
+
|
|
8
|
+
function compositionModulePath(srcDir, moduleArg) {
|
|
9
|
+
let withExtension = name => {
|
|
10
|
+
if (name.endsWith(".mjs")) {
|
|
11
|
+
return name;
|
|
12
|
+
} else {
|
|
13
|
+
return name + ".res.mjs";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
if (moduleArg !== undefined && moduleArg !== "") {
|
|
17
|
+
if (moduleArg.includes("/") || moduleArg.includes(Nodepath.sep)) {
|
|
18
|
+
return Nodepath.resolve(withExtension(moduleArg));
|
|
19
|
+
} else {
|
|
20
|
+
return Nodepath.join(srcDir, withExtension(moduleArg));
|
|
21
|
+
}
|
|
22
|
+
} else {
|
|
23
|
+
return Nodepath.join(srcDir, "Plugin.res.mjs");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function fail(message) {
|
|
28
|
+
console.error("emit-capabilities: " + message);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function main() {
|
|
33
|
+
let match = process.env["LOG_LEVEL"];
|
|
34
|
+
if (match !== undefined) {
|
|
35
|
+
|
|
36
|
+
} else {
|
|
37
|
+
process.env["LOG_LEVEL"] = "warn";
|
|
38
|
+
}
|
|
39
|
+
let srcDirArg = process.argv[2];
|
|
40
|
+
if (srcDirArg !== undefined) {
|
|
41
|
+
if (srcDirArg === "") {
|
|
42
|
+
console.error("Usage: emit-capabilities <srcDir> [<compositionModule>]");
|
|
43
|
+
process.exit(1);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
let srcDir = Nodepath.resolve(srcDirArg);
|
|
47
|
+
let modulePath = compositionModulePath(srcDir, process.argv[3]);
|
|
48
|
+
if (!Nodefs.existsSync(modulePath)) {
|
|
49
|
+
fail(modulePath + ` not found — run \`rescript build\` first.`);
|
|
50
|
+
}
|
|
51
|
+
let platformModule = await import("./Platform.res.mjs");
|
|
52
|
+
let platform = platformModule.Make();
|
|
53
|
+
let composition = await import(Nodeurl.pathToFileURL(modulePath).href);
|
|
54
|
+
let built = composition.Make(platform);
|
|
55
|
+
let manifestPath = Nodepath.join(srcDir, "capabilities.json");
|
|
56
|
+
Nodefs.writeFileSync(manifestPath, CapabilityManifest$Reventless.renderForStructure(built.pluginStructure), "utf8");
|
|
57
|
+
console.log("Generated: " + manifestPath);
|
|
58
|
+
process.exit(0);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
console.error("Usage: emit-capabilities <srcDir> [<compositionModule>]");
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export {
|
|
66
|
+
compositionModulePath,
|
|
67
|
+
fail,
|
|
68
|
+
main,
|
|
69
|
+
}
|
|
70
|
+
/* node:fs Not a pure module */
|