@reventlessdev/reventless-local 3.0.0-alpha.211 → 3.0.0-alpha.213
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 +18 -0
- package/package.json +11 -11
- package/src/BakedManifest.res +72 -0
- package/src/BakedManifest.res.mjs +52 -0
- package/src/Platform.res +66 -20
- package/src/Platform.res.mjs +55 -2
- package/src/adapter/DomainGraphQL_Server.res +6 -18
- package/src/adapter/DomainGraphQL_Server.res.mjs +5 -22
- package/src/adapter/LocalBus.res +3 -0
- package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res +167 -50
- package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res.mjs +85 -30
- package/src/adapter/QueryDb/QueryDbStorage_Sqlite.res +11 -0
- package/src/adapter/QueryDb/QueryDbStorage_Sqlite.res.mjs +5 -1
- package/tests/adapter/GraphQL_SchemaInspectorTest.res +47 -6
- package/tests/adapter/GraphQL_SchemaInspectorTest.res.mjs +29 -3
- package/tests/adapter/QueryDbListPushdownParityTest.res +92 -11
- package/tests/adapter/QueryDbListPushdownParityTest.res.mjs +96 -22
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,24 @@
|
|
|
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.213 (2026-08-12)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **platform:** bake a curated component manifest as a static asset ([4f30265](https://github.com/ReventlessDev/reventless-core/commit/4f30265a51fc2c59e69afd8074cf1b2534c06378))
|
|
11
|
+
* **queries:** narrow owner-bearing reads to the caller ([ba9cc3d](https://github.com/ReventlessDev/reventless-core/commit/ba9cc3d58d7914a9e4827bda90a704a74b1b82dd))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# 3.0.0-alpha.212 (2026-08-11)
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* **api:** one id contract across the read-side query doors ([c52fac2](https://github.com/ReventlessDev/reventless-core/commit/c52fac2bee87b3dd1fa33b4970a74f3cd3866280))
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
* **api:** infer a queryable's key field and publish its provenance ([c835a42](https://github.com/ReventlessDev/reventless-core/commit/c835a42a0da07cdc4a3f010212e1f340a4a0ca27))
|
|
22
|
+
|
|
23
|
+
|
|
6
24
|
# 3.0.0-alpha.211 (2026-08-10)
|
|
7
25
|
|
|
8
26
|
**Note:** Version bump only for package @reventlessdev/reventless-local
|
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.213",
|
|
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": {
|
|
@@ -34,19 +34,19 @@
|
|
|
34
34
|
"graphql-yoga": "^5.21.0",
|
|
35
35
|
"sury": "11.0.0-alpha.4",
|
|
36
36
|
"ws": "^8.18.0",
|
|
37
|
-
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
38
|
-
"@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.26",
|
|
39
37
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
40
38
|
"@reventlessdev/rescript-mcp-sdk": "1.0.0-alpha.21",
|
|
41
|
-
"@reventlessdev/rescript-node": "2.0.0-alpha.
|
|
39
|
+
"@reventlessdev/rescript-node": "2.0.0-alpha.4",
|
|
40
|
+
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
41
|
+
"@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.27",
|
|
42
42
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.18",
|
|
43
|
-
"@reventlessdev/reventless-
|
|
44
|
-
"@reventlessdev/reventless-
|
|
45
|
-
"@reventlessdev/reventless-
|
|
46
|
-
"@reventlessdev/reventless-infra": "3.0.0-alpha.
|
|
47
|
-
"@reventlessdev/reventless-postgres": "3.0.0-alpha.
|
|
48
|
-
"@reventlessdev/reventless-
|
|
49
|
-
"@reventlessdev/reventless-
|
|
43
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.225",
|
|
44
|
+
"@reventlessdev/reventless-graphql-server": "1.0.0-alpha.73",
|
|
45
|
+
"@reventlessdev/reventless-gwt": "1.0.0-alpha.172",
|
|
46
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.135",
|
|
47
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.89",
|
|
48
|
+
"@reventlessdev/reventless-seed": "1.0.0-alpha.11",
|
|
49
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.109"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"rescript": "12.3.0",
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Writes the curated component manifest where the local host shell serves its
|
|
2
|
+
// static assets from, so `manifestUrl` discovery works in dev exactly as it does
|
|
3
|
+
// in a deployment.
|
|
4
|
+
//
|
|
5
|
+
// Locally there is no deploy and no bucket: `reventless-host-shell` serves its
|
|
6
|
+
// own `dist/` (that is where the shipped `config.json` and `ui-hints.json` come
|
|
7
|
+
// from), so that is where the file goes. Rewritten on every boot, which also
|
|
8
|
+
// makes it self-healing — a `pnpm install` that replaces the package directory
|
|
9
|
+
// costs one restart, not a debugging session.
|
|
10
|
+
//
|
|
11
|
+
// The curation itself lives in `ReventlessCore.Platform_BakedManifest`, shared
|
|
12
|
+
// with any other platform that bakes: only the destination is local.
|
|
13
|
+
|
|
14
|
+
let hostShellPackage = "@reventlessdev/reventless-host-shell"
|
|
15
|
+
|
|
16
|
+
let log = ReventlessCore.Logger.fromEnv()
|
|
17
|
+
|
|
18
|
+
// Resolved from the running project rather than from this framework module: the
|
|
19
|
+
// bundle is a deploy input the project names in its own package.json, and a
|
|
20
|
+
// framework-rooted lookup would skip that pin (the same distinction
|
|
21
|
+
// `Util_Bundle.resolvePackageRoot(~fromPulumiProject)` draws on AWS).
|
|
22
|
+
let hostShellDistDir = (): option<string> =>
|
|
23
|
+
try {
|
|
24
|
+
Some(
|
|
25
|
+
NodePath.dirname(
|
|
26
|
+
NodeModule.createRequire(NodeProcess.cwd() ++ "/index.js")->NodeModule.requireResolve(
|
|
27
|
+
hostShellPackage ++ "/package.json",
|
|
28
|
+
),
|
|
29
|
+
) ++ "/dist",
|
|
30
|
+
)
|
|
31
|
+
} catch {
|
|
32
|
+
| _ => None
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let defaultKey = "component-manifest.json"
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
A declared bake writes the file or fails loudly. Both failure modes it can hit
|
|
39
|
+
are the deployment's own mistake — a name that matches no component, or a shell
|
|
40
|
+
package that is not installed — and both produce the same symptom if swallowed:
|
|
41
|
+
a shop that renders nothing, with no line in the log saying why.
|
|
42
|
+
*/
|
|
43
|
+
let emit = (
|
|
44
|
+
~structures: array<(string, Reventless.Plugin.pluginStructure)>,
|
|
45
|
+
~config: ReventlessInfra.Platform.bakedManifest,
|
|
46
|
+
) => {
|
|
47
|
+
let selections =
|
|
48
|
+
config.components->Array.map((
|
|
49
|
+
s
|
|
50
|
+
): ReventlessCore.Platform_BakedManifest.selection => {
|
|
51
|
+
plugin: s.plugin,
|
|
52
|
+
views: s.views,
|
|
53
|
+
commands: s.commands,
|
|
54
|
+
})
|
|
55
|
+
switch ReventlessCore.Platform_BakedManifest.curate(~structures, ~selections) {
|
|
56
|
+
| Error(e) => JsError.throwWithMessage(ReventlessCore.Platform_BakedManifest.describe(e))
|
|
57
|
+
| Ok(manifest) =>
|
|
58
|
+
switch hostShellDistDir() {
|
|
59
|
+
| None =>
|
|
60
|
+
JsError.throwWithMessage(
|
|
61
|
+
`baked manifest: cannot resolve ${hostShellPackage} from ${NodeProcess.cwd()} — ` ++
|
|
62
|
+
`the local shell serves the file from that package's dist/, so declaring a bake ` ++
|
|
63
|
+
`without the package installed would write nothing and render an empty shell.`,
|
|
64
|
+
)
|
|
65
|
+
| Some(dir) =>
|
|
66
|
+
let key = config.key->Option.getOr(defaultKey)
|
|
67
|
+
let path = NodePath.join([dir, key])
|
|
68
|
+
NodeFs.writeFileSync(path, JSON.stringify(manifest, ~space=2))
|
|
69
|
+
log.info(~comp="BakedManifest", `wrote ${key} for ${selections->Array.length->Int.toString} plugin(s): ${path}`)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Nodefs from "node:fs";
|
|
4
|
+
import * as Nodepath from "node:path";
|
|
5
|
+
import * as Nodemodule from "node:module";
|
|
6
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
7
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
8
|
+
import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
|
|
9
|
+
import * as Platform_BakedManifest$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_BakedManifest.res.mjs";
|
|
10
|
+
|
|
11
|
+
let hostShellPackage = "@reventlessdev/reventless-host-shell";
|
|
12
|
+
|
|
13
|
+
let log = Logger$ReventlessCore.fromEnv();
|
|
14
|
+
|
|
15
|
+
function hostShellDistDir() {
|
|
16
|
+
try {
|
|
17
|
+
return Nodepath.dirname(Nodemodule.createRequire(process.cwd() + "/index.js").resolve(hostShellPackage + "/package.json")) + "/dist";
|
|
18
|
+
} catch (exn) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let defaultKey = "component-manifest.json";
|
|
24
|
+
|
|
25
|
+
function emit(structures, config) {
|
|
26
|
+
let selections = config.components.map(s => ({
|
|
27
|
+
plugin: s.plugin,
|
|
28
|
+
views: s.views,
|
|
29
|
+
commands: s.commands
|
|
30
|
+
}));
|
|
31
|
+
let e = Platform_BakedManifest$ReventlessCore.curate(structures, selections);
|
|
32
|
+
if (e.TAG !== "Ok") {
|
|
33
|
+
return Stdlib_JsError.throwWithMessage(Platform_BakedManifest$ReventlessCore.describe(e._0));
|
|
34
|
+
}
|
|
35
|
+
let dir = hostShellDistDir();
|
|
36
|
+
if (dir === undefined) {
|
|
37
|
+
return Stdlib_JsError.throwWithMessage(`baked manifest: cannot resolve ` + hostShellPackage + ` from ` + process.cwd() + ` — the local shell serves the file from that package's dist/, so declaring a bake without the package installed would write nothing and render an empty shell.`);
|
|
38
|
+
}
|
|
39
|
+
let key = Stdlib_Option.getOr(config.key, defaultKey);
|
|
40
|
+
let path = Nodepath.join(dir, key);
|
|
41
|
+
Nodefs.writeFileSync(path, JSON.stringify(e._0, undefined, 2), "utf8");
|
|
42
|
+
log.info("BakedManifest", undefined, `wrote ` + key + ` for ` + selections.length.toString() + ` plugin(s): ` + path);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
hostShellPackage,
|
|
47
|
+
log,
|
|
48
|
+
hostShellDistDir,
|
|
49
|
+
defaultKey,
|
|
50
|
+
emit,
|
|
51
|
+
}
|
|
52
|
+
/* log Not a pure module */
|
package/src/Platform.res
CHANGED
|
@@ -874,6 +874,35 @@ module MakeWithConfig = (
|
|
|
874
874
|
// In-memory store for plugin structures, keyed by plugin ID.
|
|
875
875
|
let pluginStructuresStore: ref<dict<Reventless.Plugin.pluginStructure>> = ref(Dict.make())
|
|
876
876
|
|
|
877
|
+
// Bake the curated manifest from the plugin components directly rather than
|
|
878
|
+
// from `pluginStructuresStore`: a structure only exists once its Output
|
|
879
|
+
// resolves, so a synchronous read of that store at the end of `makePlatform`
|
|
880
|
+
// sees an empty dict. Emitting once the last plugin's structure has landed is
|
|
881
|
+
// both the earliest correct moment and the only one that cannot bake a
|
|
882
|
+
// half-registered platform.
|
|
883
|
+
let bakeManifest = (
|
|
884
|
+
~pluginComponents: array<ReventlessCore.Plugin.component>,
|
|
885
|
+
~config: ReventlessInfra.Platform.bakedManifest,
|
|
886
|
+
) => {
|
|
887
|
+
let resolved: array<(string, Reventless.Plugin.pluginStructure)> = []
|
|
888
|
+
let expected = pluginComponents->Array.length
|
|
889
|
+
pluginComponents->Array.forEach(plugin => {
|
|
890
|
+
let outputs: ReventlessInfra.Plugin.outputs = plugin->ReventlessCore.Component.outputs
|
|
891
|
+
let _ =
|
|
892
|
+
(outputs.id, outputs.pluginStructure)
|
|
893
|
+
->Pulumi.Output.all2
|
|
894
|
+
->Pulumi.Output.apply(((id, ps)) => {
|
|
895
|
+
switch ps {
|
|
896
|
+
| Some(def) => resolved->Array.push((id, def))
|
|
897
|
+
| None => ()
|
|
898
|
+
}
|
|
899
|
+
if resolved->Array.length === expected {
|
|
900
|
+
BakedManifest.emit(~structures=resolved, ~config)
|
|
901
|
+
}
|
|
902
|
+
})
|
|
903
|
+
})
|
|
904
|
+
}
|
|
905
|
+
|
|
877
906
|
// The object stores a plugin's fields declared, as `{plugin}.{store}` keys.
|
|
878
907
|
let declaredStoresOf = (structure: Reventless.Plugin.pluginStructure) =>
|
|
879
908
|
structure.requiredStores->Option.getOr([])
|
|
@@ -1399,7 +1428,35 @@ module MakeWithConfig = (
|
|
|
1399
1428
|
}
|
|
1400
1429
|
})
|
|
1401
1430
|
|
|
1402
|
-
|
|
1431
|
+
// In-memory ignores most of `~hostUiBundle` — the host shell is served by
|
|
1432
|
+
// `vite dev` against the running in-process GraphQL server, not from a CDN,
|
|
1433
|
+
// including `uiHintsFile` (the AWS deploy writes it as a BucketObject; local
|
|
1434
|
+
// dev serves `public/ui-hints.json` directly). `bakedManifest` is the
|
|
1435
|
+
// exception: it is generated content, so there is no hand-authored file for
|
|
1436
|
+
// local dev to serve instead.
|
|
1437
|
+
type hostUiBundleConfig = {
|
|
1438
|
+
assetsDir?: string,
|
|
1439
|
+
bundleVersion?: string,
|
|
1440
|
+
uiHintsFile?: string,
|
|
1441
|
+
// Honoured here, unlike the rest: `makePlatform` writes the curated manifest
|
|
1442
|
+
// where the local host-shell serves its static assets from.
|
|
1443
|
+
bakedManifest?: ReventlessInfra.Platform.bakedManifest,
|
|
1444
|
+
// AWS host-ui deploy knobs — carried to satisfy the shared Platform.T
|
|
1445
|
+
// signature; the in-memory platform provisions no infrastructure and
|
|
1446
|
+
// ignores them.
|
|
1447
|
+
geocoderPlaceIndex?: ReventlessInfra.Platform.geocoderIndex,
|
|
1448
|
+
uploadBucket?: ReventlessInfra.Platform.objectStore,
|
|
1449
|
+
// Same terms: the AWS deploy writes these into the config.json it hosts, and
|
|
1450
|
+
// local dev has no config.json of its own to write — the host-shell package
|
|
1451
|
+
// serves `public/config.json`, which is where local turns a view mode on.
|
|
1452
|
+
viewModes?: array<ReventlessInfra.Platform.viewMode>,
|
|
1453
|
+
shellConfig?: dict<JSON.t>,
|
|
1454
|
+
}
|
|
1455
|
+
let makePlatform = (
|
|
1456
|
+
~version,
|
|
1457
|
+
~plugins: array<module(PluginMaker)>,
|
|
1458
|
+
~hostUiBundle: option<hostUiBundleConfig>=?,
|
|
1459
|
+
) => {
|
|
1403
1460
|
log.info(~comp="Platform", `v${version}`)
|
|
1404
1461
|
log.info(
|
|
1405
1462
|
~comp="Platform",
|
|
@@ -1549,6 +1606,14 @@ module MakeWithConfig = (
|
|
|
1549
1606
|
connectPlugin(~pluginComponents=plugins)
|
|
1550
1607
|
seedPluginStructuresStore(~pluginComponents=plugins)
|
|
1551
1608
|
}
|
|
1609
|
+
// Not inside `seedAdminStores`: the bake describes what this deployment
|
|
1610
|
+
// offers, which is settled at composition and owes nothing to projection
|
|
1611
|
+
// catch-up. Every plugin passed to this call is connected by construction
|
|
1612
|
+
// locally, so there is no status or version dedup to apply.
|
|
1613
|
+
switch hostUiBundle->Option.flatMap(cfg => cfg.bakedManifest) {
|
|
1614
|
+
| None => ()
|
|
1615
|
+
| Some(cfg) => bakeManifest(~pluginComponents=plugins, ~config=cfg)
|
|
1616
|
+
}
|
|
1552
1617
|
switch (projectionCatchup, pgProjectionCatchup) {
|
|
1553
1618
|
| (Some((db, upperBound, dcbUpperBound)), _) =>
|
|
1554
1619
|
let _ =
|
|
@@ -2031,25 +2096,6 @@ module MakeWithConfig = (
|
|
|
2031
2096
|
|
|
2032
2097
|
}
|
|
2033
2098
|
|
|
2034
|
-
// In-memory ignores `~hostUiBundle` — the host shell is served by `vite dev`
|
|
2035
|
-
// against the running in-process GraphQL server, not from a CDN — including
|
|
2036
|
-
// `uiHintsFile` (the AWS deploy writes it as a BucketObject; local dev serves
|
|
2037
|
-
// `public/ui-hints.json` directly).
|
|
2038
|
-
type hostUiBundleConfig = {
|
|
2039
|
-
assetsDir?: string,
|
|
2040
|
-
bundleVersion?: string,
|
|
2041
|
-
uiHintsFile?: string,
|
|
2042
|
-
// AWS host-ui deploy knobs — carried to satisfy the shared Platform.T
|
|
2043
|
-
// signature; the in-memory platform provisions no infrastructure and
|
|
2044
|
-
// ignores them.
|
|
2045
|
-
geocoderPlaceIndex?: ReventlessInfra.Platform.geocoderIndex,
|
|
2046
|
-
uploadBucket?: ReventlessInfra.Platform.objectStore,
|
|
2047
|
-
// Same terms: the AWS deploy writes these into the config.json it hosts, and
|
|
2048
|
-
// local dev has no config.json of its own to write — the host-shell package
|
|
2049
|
-
// serves `public/config.json`, which is where local turns a view mode on.
|
|
2050
|
-
viewModes?: array<ReventlessInfra.Platform.viewMode>,
|
|
2051
|
-
shellConfig?: dict<JSON.t>,
|
|
2052
|
-
}
|
|
2053
2099
|
let deployPlatform = (
|
|
2054
2100
|
~version,
|
|
2055
2101
|
~hostUiBundle as _: option<hostUiBundleConfig>=?,
|
package/src/Platform.res.mjs
CHANGED
|
@@ -31,6 +31,7 @@ import * as BackendState$ReventlessLocal from "./adapter/BackendState.res.mjs";
|
|
|
31
31
|
import * as ComponentType$ReventlessCore from "@reventlessdev/reventless-core/src/ComponentType.res.mjs";
|
|
32
32
|
import * as SqliteDriver$ReventlessLocal from "./adapter/SqliteDriver.res.mjs";
|
|
33
33
|
import * as Task_Builder$ReventlessLocal from "./components/Task_Builder.res.mjs";
|
|
34
|
+
import * as BakedManifest$ReventlessLocal from "./BakedManifest.res.mjs";
|
|
34
35
|
import * as Platform_Admin$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_Admin.res.mjs";
|
|
35
36
|
import * as PluginBehavior$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/lifecycle/PluginBehavior.res.mjs";
|
|
36
37
|
import * as Plugin_Helpers$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/component/Plugin_Helpers.res.mjs";
|
|
@@ -832,6 +833,28 @@ function MakeWithConfig(Config) {
|
|
|
832
833
|
let pluginStructuresStore = {
|
|
833
834
|
contents: {}
|
|
834
835
|
};
|
|
836
|
+
let bakeManifest = (pluginComponents, config) => {
|
|
837
|
+
let resolved = [];
|
|
838
|
+
let expected = pluginComponents.length;
|
|
839
|
+
pluginComponents.forEach(plugin => {
|
|
840
|
+
let outputs = Component$ReventlessCore.outputs(plugin);
|
|
841
|
+
Pulumi.all([
|
|
842
|
+
outputs.id,
|
|
843
|
+
outputs.pluginStructure
|
|
844
|
+
]).apply(param => {
|
|
845
|
+
let ps = param[1];
|
|
846
|
+
if (ps !== undefined) {
|
|
847
|
+
resolved.push([
|
|
848
|
+
param[0],
|
|
849
|
+
ps
|
|
850
|
+
]);
|
|
851
|
+
}
|
|
852
|
+
if (resolved.length === expected) {
|
|
853
|
+
return BakedManifest$ReventlessLocal.emit(resolved, config);
|
|
854
|
+
}
|
|
855
|
+
});
|
|
856
|
+
});
|
|
857
|
+
};
|
|
835
858
|
let seedPluginStructuresStore = pluginComponents => {
|
|
836
859
|
pluginComponents.forEach(plugin => {
|
|
837
860
|
let outputs = Component$ReventlessCore.outputs(plugin);
|
|
@@ -1183,7 +1206,7 @@ function MakeWithConfig(Config) {
|
|
|
1183
1206
|
}
|
|
1184
1207
|
});
|
|
1185
1208
|
};
|
|
1186
|
-
let makePlatform = (version, plugins) => {
|
|
1209
|
+
let makePlatform = (version, plugins, hostUiBundle) => {
|
|
1187
1210
|
log.info("Platform", undefined, `v` + version);
|
|
1188
1211
|
log.info("Platform", undefined, `silent: ` + Stdlib_Bool.toString(Config.silent) + `, splitApi: ` + Stdlib_Bool.toString(Config.splitApi) + `, cloner: ` + Stdlib_Bool.toString(Config.cloner));
|
|
1189
1212
|
let match = Config.backend;
|
|
@@ -1262,6 +1285,10 @@ function MakeWithConfig(Config) {
|
|
|
1262
1285
|
adminResources: []
|
|
1263
1286
|
});
|
|
1264
1287
|
subscribeToPluginEvents();
|
|
1288
|
+
let cfg = Stdlib_Option.flatMap(hostUiBundle, cfg => cfg.bakedManifest);
|
|
1289
|
+
if (cfg !== undefined) {
|
|
1290
|
+
bakeManifest(plugins$1, cfg);
|
|
1291
|
+
}
|
|
1265
1292
|
if (projectionCatchup !== undefined) {
|
|
1266
1293
|
Stdlib_Promise.$$catch(ProjectionCheckpoint$ReventlessLocal.startupCatchup(projectionCatchup[0], projectionCatchup[1], projectionCatchup[2], () => Bus.projectionCatchupHandlers()), e => {
|
|
1267
1294
|
console.error("[Platform] projection catch-up failed:", e);
|
|
@@ -2529,6 +2556,28 @@ function Make($star) {
|
|
|
2529
2556
|
let pluginStructuresStore = {
|
|
2530
2557
|
contents: {}
|
|
2531
2558
|
};
|
|
2559
|
+
let bakeManifest = (pluginComponents, config) => {
|
|
2560
|
+
let resolved = [];
|
|
2561
|
+
let expected = pluginComponents.length;
|
|
2562
|
+
pluginComponents.forEach(plugin => {
|
|
2563
|
+
let outputs = Component$ReventlessCore.outputs(plugin);
|
|
2564
|
+
Pulumi.all([
|
|
2565
|
+
outputs.id,
|
|
2566
|
+
outputs.pluginStructure
|
|
2567
|
+
]).apply(param => {
|
|
2568
|
+
let ps = param[1];
|
|
2569
|
+
if (ps !== undefined) {
|
|
2570
|
+
resolved.push([
|
|
2571
|
+
param[0],
|
|
2572
|
+
ps
|
|
2573
|
+
]);
|
|
2574
|
+
}
|
|
2575
|
+
if (resolved.length === expected) {
|
|
2576
|
+
return BakedManifest$ReventlessLocal.emit(resolved, config);
|
|
2577
|
+
}
|
|
2578
|
+
});
|
|
2579
|
+
});
|
|
2580
|
+
};
|
|
2532
2581
|
let seedPluginStructuresStore = pluginComponents => {
|
|
2533
2582
|
pluginComponents.forEach(plugin => {
|
|
2534
2583
|
let outputs = Component$ReventlessCore.outputs(plugin);
|
|
@@ -2876,7 +2925,7 @@ function Make($star) {
|
|
|
2876
2925
|
}
|
|
2877
2926
|
});
|
|
2878
2927
|
};
|
|
2879
|
-
let makePlatform = (version, plugins) => {
|
|
2928
|
+
let makePlatform = (version, plugins, hostUiBundle) => {
|
|
2880
2929
|
log.info("Platform", undefined, `v` + version);
|
|
2881
2930
|
log.info("Platform", undefined, `silent: ` + Stdlib_Bool.toString(false) + `, splitApi: ` + Stdlib_Bool.toString(true) + `, cloner: ` + Stdlib_Bool.toString(false));
|
|
2882
2931
|
let tmp;
|
|
@@ -2954,6 +3003,10 @@ function Make($star) {
|
|
|
2954
3003
|
adminResources: []
|
|
2955
3004
|
});
|
|
2956
3005
|
subscribeToPluginEvents();
|
|
3006
|
+
let cfg = Stdlib_Option.flatMap(hostUiBundle, cfg => cfg.bakedManifest);
|
|
3007
|
+
if (cfg !== undefined) {
|
|
3008
|
+
bakeManifest(plugins$1, cfg);
|
|
3009
|
+
}
|
|
2957
3010
|
if (projectionCatchup !== undefined) {
|
|
2958
3011
|
Stdlib_Promise.$$catch(ProjectionCheckpoint$ReventlessLocal.startupCatchup(projectionCatchup[0], projectionCatchup[1], projectionCatchup[2], () => Bus.projectionCatchupHandlers()), e => {
|
|
2959
3012
|
console.error("[Platform] projection catch-up failed:", e);
|
|
@@ -257,26 +257,14 @@ type resolverFn = YG.resolverFn
|
|
|
257
257
|
|
|
258
258
|
// -- Relay Global ID encoding/decoding -------------------------------------
|
|
259
259
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
260
|
+
// The encoding itself lives in `ReventlessCore.Api_Ids` — one definition shared
|
|
261
|
+
// with the AWS adapter and with the doors that accept either id form. Kept as
|
|
262
|
+
// named bindings here because the resolver wiring below and the `relay` record
|
|
263
|
+
// passed to QueryDbResolvers_GraphQL both reference them.
|
|
263
264
|
let encodeGlobalId = (~typeName: string, ~localId: string): string =>
|
|
264
|
-
|
|
265
|
+
ReventlessCore.Api_Ids.encode(~typeName, ~localId)
|
|
265
266
|
|
|
266
|
-
let decodeGlobalId =
|
|
267
|
-
try {
|
|
268
|
-
let decoded = atob(globalId)
|
|
269
|
-
let idx = decoded->String.indexOf(":")
|
|
270
|
-
if idx > 0 {
|
|
271
|
-
let typeName = decoded->String.slice(~start=0, ~end=idx)
|
|
272
|
-
let localId = decoded->String.slice(~start=idx + 1, ~end=decoded->String.length)
|
|
273
|
-
Some((typeName, localId))
|
|
274
|
-
} else {
|
|
275
|
-
None
|
|
276
|
-
}
|
|
277
|
-
} catch {
|
|
278
|
-
| _ => None
|
|
279
|
-
}
|
|
267
|
+
let decodeGlobalId = ReventlessCore.Api_Ids.decode
|
|
280
268
|
|
|
281
269
|
// -- Scoped registry ---------------------------------------------------------
|
|
282
270
|
// Mirrors the AWS plugin=source-API, platform=merged-API model: every
|
|
@@ -15,6 +15,7 @@ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js
|
|
|
15
15
|
import * as Identity$Reventless from "@reventlessdev/reventless-spec/src/types/Identity.res.mjs";
|
|
16
16
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
17
17
|
import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
|
|
18
|
+
import * as Api_Ids$ReventlessCore from "@reventlessdev/reventless-core/src/components/Api/Api_Ids.res.mjs";
|
|
18
19
|
import * as LocalAuth$ReventlessLocal from "./Auth/LocalAuth.res.mjs";
|
|
19
20
|
import * as GraphQL_Stitcher$ReventlessCore from "@reventlessdev/reventless-core/src/components/Api/GraphQL_Stitcher.res.mjs";
|
|
20
21
|
import * as LocalObjectStore$ReventlessLocal from "./ObjectStore/LocalObjectStore.res.mjs";
|
|
@@ -193,27 +194,7 @@ function _dispatch(req, res, yoga, getSdl) {
|
|
|
193
194
|
});
|
|
194
195
|
}
|
|
195
196
|
|
|
196
|
-
|
|
197
|
-
return btoa(typeName + `:` + localId);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function decodeGlobalId(globalId) {
|
|
201
|
-
try {
|
|
202
|
-
let decoded = atob(globalId);
|
|
203
|
-
let idx = decoded.indexOf(":");
|
|
204
|
-
if (idx <= 0) {
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
let typeName = decoded.slice(0, idx);
|
|
208
|
-
let localId = decoded.slice(idx + 1 | 0, decoded.length);
|
|
209
|
-
return [
|
|
210
|
-
typeName,
|
|
211
|
-
localId
|
|
212
|
-
];
|
|
213
|
-
} catch (exn) {
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
197
|
+
let encodeGlobalId = Api_Ids$ReventlessCore.encode;
|
|
217
198
|
|
|
218
199
|
let platformScope = "platform";
|
|
219
200
|
|
|
@@ -368,7 +349,7 @@ function buildNodeResolver() {
|
|
|
368
349
|
}
|
|
369
350
|
let resolver = async (_root, args, _ctx) => {
|
|
370
351
|
let id = Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(args), d => d["id"]), Stdlib_JSON.Decode.string), "");
|
|
371
|
-
let match =
|
|
352
|
+
let match = Api_Ids$ReventlessCore.decode(id);
|
|
372
353
|
if (match === undefined) {
|
|
373
354
|
return null;
|
|
374
355
|
}
|
|
@@ -738,6 +719,8 @@ let YG;
|
|
|
738
719
|
|
|
739
720
|
let buildAuthContext = Auth_GraphqlContext$ReventlessLocal.buildAuthContext;
|
|
740
721
|
|
|
722
|
+
let decodeGlobalId = Api_Ids$ReventlessCore.decode;
|
|
723
|
+
|
|
741
724
|
export {
|
|
742
725
|
YG,
|
|
743
726
|
log,
|
package/src/adapter/LocalBus.res
CHANGED
|
@@ -132,6 +132,7 @@ module type T = {
|
|
|
132
132
|
~argsDict: dict<JSON.t>,
|
|
133
133
|
~capability: ReventlessCore.GraphQL_FragmentGenerator.serverCapability,
|
|
134
134
|
~labelField: string,
|
|
135
|
+
~ownerScope: (string, string)=?,
|
|
135
136
|
) => option<JSON.t>,
|
|
136
137
|
) => unit
|
|
137
138
|
let getQueryDbListPage: string => option<
|
|
@@ -139,6 +140,7 @@ module type T = {
|
|
|
139
140
|
~argsDict: dict<JSON.t>,
|
|
140
141
|
~capability: ReventlessCore.GraphQL_FragmentGenerator.serverCapability,
|
|
141
142
|
~labelField: string,
|
|
143
|
+
~ownerScope: (string, string)=?,
|
|
142
144
|
) => option<JSON.t>,
|
|
143
145
|
>
|
|
144
146
|
|
|
@@ -235,6 +237,7 @@ module Impl = (C: BusConfig): T => {
|
|
|
235
237
|
~argsDict: dict<JSON.t>,
|
|
236
238
|
~capability: ReventlessCore.GraphQL_FragmentGenerator.serverCapability,
|
|
237
239
|
~labelField: string,
|
|
240
|
+
~ownerScope: (string, string)=?,
|
|
238
241
|
) => option<JSON.t>,
|
|
239
242
|
>,
|
|
240
243
|
> = ref(Dict.make())
|