@reventlessdev/reventless-local 3.0.0-alpha.199 → 3.0.0-alpha.201
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 +17 -0
- package/package.json +10 -9
- package/rescript.json +2 -1
- package/src/Platform.res +137 -86
- package/src/Platform.res.mjs +57 -44
- package/src/adapter/BackendState.res +13 -0
- package/src/adapter/BackendState.res.mjs +17 -1
- package/src/adapter/DcbEventLog/LocalDcbEventLogStorage.res.mjs +1 -1
- package/src/adapter/DomainGraphQL_Server.res +5 -5
- package/src/adapter/DomainGraphQL_Server.res.mjs +1 -1
- package/src/adapter/EventLog/LocalEventLogStorage.res.mjs +1 -1
- package/src/adapter/LocalBus.res +6 -46
- package/src/adapter/LocalBus.res.mjs +0 -29
- package/src/adapter/LocalStateChangeDescriptor.res +93 -0
- package/src/adapter/LocalStateChangeDescriptor.res.mjs +61 -0
- package/src/adapter/LocalUploadResolvers.res +16 -3
- package/src/adapter/LocalUploadResolvers.res.mjs +6 -4
- package/src/adapter/ObjectStore/LocalObjectStore.res +157 -0
- package/src/adapter/ObjectStore/LocalObjectStore.res.mjs +129 -0
- package/src/adapter/ObjectStore/ObjectStoreStorage_FileSystem.res +215 -0
- package/src/adapter/ObjectStore/ObjectStoreStorage_FileSystem.res.mjs +286 -0
- package/src/adapter/ObjectStore/ObjectStoreStorage_InMemory.res +33 -0
- package/src/adapter/ObjectStore/ObjectStoreStorage_InMemory.res.mjs +47 -0
- package/src/adapter/QueryDb/LocalQueryDbStorage.res.mjs +1 -1
- package/src/adapter/QueryDb/QueryDbStorage_InMemory.res +4 -2
- package/src/adapter/QueryDb/QueryDbStorage_InMemory.res.mjs +3 -3
- package/src/adapter/QueryDb/QueryDbStorage_Sqlite.res +4 -2
- package/src/adapter/QueryDb/QueryDbStorage_Sqlite.res.mjs +3 -3
- package/src/reset/LocalSeedReset.res +552 -0
- package/src/reset/LocalSeedReset.res.mjs +533 -0
- package/tests/adapter/BackendParityTest.res +51 -0
- package/tests/adapter/BackendParityTest.res.mjs +44 -0
- package/tests/adapter/GraphQL_SubscriptionResolversTest.res +6 -4
- package/tests/adapter/GraphQL_SubscriptionResolversTest.res.mjs +4 -3
- package/tests/adapter/ObjectStorePersistenceTest.res +243 -0
- package/tests/adapter/ObjectStorePersistenceTest.res.mjs +159 -0
- package/tests/reset/LocalSeedResetTest.res +252 -0
- package/tests/reset/LocalSeedResetTest.res.mjs +280 -0
- package/src/adapter/LocalObjectStore.res +0 -70
- package/src/adapter/LocalObjectStore.res.mjs +0 -60
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,23 @@
|
|
|
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.201 (2026-08-05)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **local,aws:** carry the changed row in the live-update descriptor ([9272e2e](https://github.com/ReventlessDev/reventless-core/commit/9272e2e18e9e1d740f9a2c5aa83eccbcb41feff7))
|
|
11
|
+
* **local:** offload large pluginDefinition fields to the object store ([84c5fab](https://github.com/ReventlessDev/reventless-core/commit/84c5fab339856b5d64f0c05110b1fa7f38646f5e))
|
|
12
|
+
* **local:** persist the object store beside the SQLite database ([f37e4a1](https://github.com/ReventlessDev/reventless-core/commit/f37e4a1ad3191a97f14f3db7e2ead0e2b27b46c2))
|
|
13
|
+
* **local:** scoped seed:reset for the local platform ([a4dd003](https://github.com/ReventlessDev/reventless-core/commit/a4dd0033b7d01be23be91bac3f01d38ebeab7d45))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# 3.0.0-alpha.200 (2026-08-04)
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* **admin:** serve the complete plugin structure to developer tooling ([e99cda0](https://github.com/ReventlessDev/reventless-core/commit/e99cda02a2f862b36bcfdb018a0fb34bb7426a2b))
|
|
21
|
+
|
|
22
|
+
|
|
6
23
|
# 3.0.0-alpha.199 (2026-08-04)
|
|
7
24
|
|
|
8
25
|
### Features
|
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.201",
|
|
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": {
|
|
@@ -36,16 +36,17 @@
|
|
|
36
36
|
"ws": "^8.18.0",
|
|
37
37
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
38
38
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
39
|
-
"@reventlessdev/rescript-node": "2.0.0-alpha.1",
|
|
40
|
-
"@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.26",
|
|
41
39
|
"@reventlessdev/rescript-mcp-sdk": "1.0.0-alpha.21",
|
|
40
|
+
"@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.26",
|
|
41
|
+
"@reventlessdev/rescript-node": "2.0.0-alpha.2",
|
|
42
42
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.18",
|
|
43
|
-
"@reventlessdev/reventless-core": "3.0.0-alpha.
|
|
44
|
-
"@reventlessdev/reventless-graphql-server": "1.0.0-alpha.
|
|
45
|
-
"@reventlessdev/reventless-infra": "3.0.0-alpha.
|
|
46
|
-
"@reventlessdev/reventless-
|
|
47
|
-
"@reventlessdev/reventless-
|
|
48
|
-
"@reventlessdev/reventless-
|
|
43
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.213",
|
|
44
|
+
"@reventlessdev/reventless-graphql-server": "1.0.0-alpha.61",
|
|
45
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.127",
|
|
46
|
+
"@reventlessdev/reventless-seed": "1.0.0-alpha.9",
|
|
47
|
+
"@reventlessdev/reventless-gwt": "1.0.0-alpha.160",
|
|
48
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.101",
|
|
49
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.77"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"rescript": "12.3.0",
|
package/rescript.json
CHANGED
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
"@reventlessdev/reventless-spec",
|
|
36
36
|
"@reventlessdev/reventless-core",
|
|
37
37
|
"@reventlessdev/reventless-gwt",
|
|
38
|
-
"@reventlessdev/reventless-postgres"
|
|
38
|
+
"@reventlessdev/reventless-postgres",
|
|
39
|
+
"@reventlessdev/reventless-seed"
|
|
39
40
|
],
|
|
40
41
|
"compiler-flags": [],
|
|
41
42
|
"suffix": ".res.mjs"
|
package/src/Platform.res
CHANGED
|
@@ -118,6 +118,14 @@ module MakeWithConfig = (
|
|
|
118
118
|
}
|
|
119
119
|
let db = SqliteDriver.openDb(~path)
|
|
120
120
|
BackendState.setSqlite(~db, ~path)
|
|
121
|
+
// The object store persists beside the database under this backend (see
|
|
122
|
+
// LocalObjectStore), so a reset that wipes the events wipes the bytes they
|
|
123
|
+
// reference too — otherwise the session starts with orphaned uploads and
|
|
124
|
+
// offload payloads no event points at. Runs after setSqlite because the
|
|
125
|
+
// store reads the active backend to find its root.
|
|
126
|
+
if resetOnStart {
|
|
127
|
+
LocalObjectStore.reset()
|
|
128
|
+
}
|
|
121
129
|
// Persistent stores keep their event numbering across restarts and app
|
|
122
130
|
// switches: seed the in-memory event-tap counter from the rows already on
|
|
123
131
|
// disk so the timeline's #N continues instead of restarting at 1. (After a
|
|
@@ -837,6 +845,20 @@ module MakeWithConfig = (
|
|
|
837
845
|
type mcpSupported = | @as(true) McpSupported | @as(false) McpNotSupported
|
|
838
846
|
let mcpSupported = McpSupported
|
|
839
847
|
|
|
848
|
+
// Content-address the two large pluginDefinition fields into the process-local
|
|
849
|
+
// object store, exactly as the AWS platform does into its offload bucket. Local
|
|
850
|
+
// has no bucket, but it does have an object store — and leaving the fields
|
|
851
|
+
// inline here meant every connect handshake carried a whole plugin structure
|
|
852
|
+
// through the command log, and that local never exercised the offload path the
|
|
853
|
+
// deployed platform takes. Registered once at platform setup, before any plugin
|
|
854
|
+
// is built, since the hook fires during graph construction.
|
|
855
|
+
ReventlessCore.Plugin_Helpers.registerOffload((~store, ~bytes) => {
|
|
856
|
+
let hash = NodeCrypto.sha256Hex(bytes)
|
|
857
|
+
let key = "sha256/" ++ hash
|
|
858
|
+
LocalObjectStore.putOffload(~key, ~bytes)
|
|
859
|
+
{Reventless.Offload.store, key, hash, bytes: bytes->String.length}
|
|
860
|
+
})
|
|
861
|
+
|
|
840
862
|
// ---------------------------------------------------------------------------
|
|
841
863
|
// Shared helpers — used by both makePlatform and deployPlugin.
|
|
842
864
|
// ---------------------------------------------------------------------------
|
|
@@ -852,6 +874,10 @@ module MakeWithConfig = (
|
|
|
852
874
|
// In-memory store for plugin structures, keyed by plugin ID.
|
|
853
875
|
let pluginStructuresStore: ref<dict<Reventless.Plugin.pluginStructure>> = ref(Dict.make())
|
|
854
876
|
|
|
877
|
+
// The object stores a plugin's fields declared, as `{plugin}.{store}` keys.
|
|
878
|
+
let declaredStoresOf = (structure: Reventless.Plugin.pluginStructure) =>
|
|
879
|
+
structure.requiredStores->Option.getOr([])
|
|
880
|
+
|
|
855
881
|
let seedPluginStructuresStore = (~pluginComponents: array<ReventlessCore.Plugin.component>) => {
|
|
856
882
|
pluginComponents->Array.forEach(plugin => {
|
|
857
883
|
let outputs: ReventlessInfra.Plugin.outputs = plugin->ReventlessCore.Component.outputs
|
|
@@ -860,45 +886,42 @@ module MakeWithConfig = (
|
|
|
860
886
|
->Pulumi.Output.all2
|
|
861
887
|
->Pulumi.Output.apply(((id, ps)) => {
|
|
862
888
|
switch ps {
|
|
863
|
-
| Some(def) =>
|
|
889
|
+
| Some(def) =>
|
|
890
|
+
pluginStructuresStore.contents->Dict.set(id, def)
|
|
891
|
+
// Inside the apply, not after the forEach: the structure only exists
|
|
892
|
+
// once the Output resolves, so anything reading
|
|
893
|
+
// `pluginStructuresStore` synchronously below reads an empty dict.
|
|
894
|
+
declaredStoresOf(def)->Array.forEach(qualified =>
|
|
895
|
+
// Registering the store is what lets an uploaded object be attributed
|
|
896
|
+
// to the plugin that declared it, the way an S3 key's prefix does —
|
|
897
|
+
// under the served `uploads/` prefix locally, for the reason
|
|
898
|
+
// LocalObjectStore.localPrefixFor gives.
|
|
899
|
+
LocalObjectStore.registerStore(
|
|
900
|
+
~qualified,
|
|
901
|
+
~prefix=LocalObjectStore.localPrefixFor(~qualified),
|
|
902
|
+
)
|
|
903
|
+
)
|
|
904
|
+
// Two plugins declaring one store name is refused by the deployed
|
|
905
|
+
// platform before it provisions anything. Local provisions no stores, so
|
|
906
|
+
// nothing here breaks — but this is the only place a developer meets the
|
|
907
|
+
// rule while the fix is still free: once a store has deployed, its minted
|
|
908
|
+
// refs are in an append-only event log and renaming it strands them.
|
|
909
|
+
// Hence a warning at composition, and the authoritative refusal at deploy.
|
|
910
|
+
//
|
|
911
|
+
// Checked against every store registered so far rather than this
|
|
912
|
+
// plugin's own, since a collision is by definition between two plugins.
|
|
913
|
+
ReventlessCore.StorePrefixCollision.collisionsFor(
|
|
914
|
+
~stores=LocalObjectStore.declaredStoreList()->Array.map(((qualified, prefix)) => {
|
|
915
|
+
ReventlessCore.StorePrefixCollision.qualified,
|
|
916
|
+
prefix,
|
|
917
|
+
}),
|
|
918
|
+
)->Array.forEach(c =>
|
|
919
|
+
log.warn(~comp="Platform:plugins", ReventlessCore.StorePrefixCollision.collisionMessage(c))
|
|
920
|
+
)
|
|
864
921
|
| None => ()
|
|
865
922
|
}
|
|
866
923
|
})
|
|
867
924
|
})
|
|
868
|
-
|
|
869
|
-
// Two plugins declaring one store name is refused by the deployed platform
|
|
870
|
-
// before it provisions anything. Local provisions no stores, so nothing here
|
|
871
|
-
// breaks — but this is the only place a developer meets the rule while the
|
|
872
|
-
// fix is still free: once a store has deployed, its minted refs are in an
|
|
873
|
-
// append-only event log and renaming it strands them. Hence a warning at
|
|
874
|
-
// composition, and the authoritative refusal at deploy.
|
|
875
|
-
let declared =
|
|
876
|
-
pluginStructuresStore.contents
|
|
877
|
-
->Dict.valuesToArray
|
|
878
|
-
->Array.flatMap(structure =>
|
|
879
|
-
structure.requiredStores
|
|
880
|
-
->Option.getOr([])
|
|
881
|
-
->Array.map(qualified => {
|
|
882
|
-
ReventlessCore.StorePrefixCollision.qualified,
|
|
883
|
-
// Local provisions no stores, so it has no prefix of its own — it
|
|
884
|
-
// restates the deployed rule (`{plugin}/{store}`) so the warning it
|
|
885
|
-
// gives matches the refusal a deploy would give. Reporting a collision
|
|
886
|
-
// the deployed platform does not have would be worse than silence.
|
|
887
|
-
prefix: switch qualified->String.indexOfOpt(".") {
|
|
888
|
-
| Some(i) =>
|
|
889
|
-
qualified->String.slice(~start=0, ~end=i) ++
|
|
890
|
-
"/" ++
|
|
891
|
-
qualified->String.slice(~start=i + 1, ~end=qualified->String.length)
|
|
892
|
-
| None => qualified
|
|
893
|
-
},
|
|
894
|
-
})
|
|
895
|
-
)
|
|
896
|
-
ReventlessCore.StorePrefixCollision.collisionsFor(~stores=declared)->Array.forEach(c =>
|
|
897
|
-
log.warn(
|
|
898
|
-
~comp="Platform:plugins",
|
|
899
|
-
ReventlessCore.StorePrefixCollision.collisionMessage(c),
|
|
900
|
-
)
|
|
901
|
-
)
|
|
902
925
|
}
|
|
903
926
|
|
|
904
927
|
// Bus keys for the admin Plugin aggregate (name-keyed). ComponentType.toName maps
|
|
@@ -1113,9 +1136,19 @@ module MakeWithConfig = (
|
|
|
1113
1136
|
},
|
|
1114
1137
|
),
|
|
1115
1138
|
extensionProtocols: [],
|
|
1116
|
-
apiSchemaFragment: apiSchemaFragment->Option.map(f =>
|
|
1139
|
+
apiSchemaFragment: apiSchemaFragment->Option.map(f =>
|
|
1140
|
+
f->ReventlessCore.Plugin_Helpers.offloadPayload(
|
|
1141
|
+
~schema=Reventless.Plugin.apiSchemaFragmentSchema,
|
|
1142
|
+
~store="pluginApiFragments",
|
|
1143
|
+
)
|
|
1144
|
+
),
|
|
1117
1145
|
apiTarget: None,
|
|
1118
|
-
structure: pluginStructure->Option.map(s =>
|
|
1146
|
+
structure: pluginStructure->Option.map(s =>
|
|
1147
|
+
s->ReventlessCore.Plugin_Helpers.offloadPayload(
|
|
1148
|
+
~schema=Reventless.Plugin.pluginStructureSchema,
|
|
1149
|
+
~store="pluginStructures",
|
|
1150
|
+
)
|
|
1151
|
+
),
|
|
1119
1152
|
dcbEventLog: None,
|
|
1120
1153
|
kind: Domain,
|
|
1121
1154
|
}
|
|
@@ -1635,60 +1668,78 @@ module MakeWithConfig = (
|
|
|
1635
1668
|
// whose Plugin RM row is not Connected are hidden until reactivated. The built-in
|
|
1636
1669
|
// Platform_Admin entry has no Plugin RM row — include it unconditionally since
|
|
1637
1670
|
// it can't be deactivated.
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
)
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1671
|
+
// The plugins both structure queries answer over: one entry per plugin name,
|
|
1672
|
+
// Connected only, highest version. Shared so the two fields can never disagree
|
|
1673
|
+
// about which plugins exist — mirrors the single Lambda serving both fields on
|
|
1674
|
+
// the AWS side.
|
|
1675
|
+
let connectedLatestStructures = () => {
|
|
1676
|
+
let pluginStatusById = {
|
|
1677
|
+
let dict = Dict.make()
|
|
1678
|
+
switch Bus.getQueryDbScan(pluginQueryDbName) {
|
|
1679
|
+
| Some(scanAll) =>
|
|
1680
|
+
scanAll()->Array.forEach(json =>
|
|
1681
|
+
switch json->S.convertOrThrow(ReventlessCore.PluginsReadModelSpec.stateSchema) {
|
|
1682
|
+
| state =>
|
|
1683
|
+
let id = state.name ++ "@" ++ state.version
|
|
1684
|
+
dict->Dict.set(id, state.status)
|
|
1685
|
+
| exception _ => ()
|
|
1686
|
+
}
|
|
1687
|
+
)
|
|
1688
|
+
| None => ()
|
|
1656
1689
|
}
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1690
|
+
dict
|
|
1691
|
+
}
|
|
1692
|
+
let adminPluginId = ReventlessCore.Platform_Admin_Structure.pluginId
|
|
1693
|
+
// One entry per plugin name: among Connected versions keep the highest
|
|
1694
|
+
// version, enforcing the deploy-time one-version-per-plugin invariant.
|
|
1695
|
+
// Without this, a lingering Connected older version (rolling deploy or a
|
|
1696
|
+
// missed retire) surfaces a full duplicate set of AutoUI menu entries —
|
|
1697
|
+
// mirrors the dedup in Platform_ComponentDefinitions_Lambda.res.
|
|
1698
|
+
let latestByName = Dict.make()
|
|
1699
|
+
pluginStructuresStore.contents
|
|
1700
|
+
->Dict.toArray
|
|
1701
|
+
->Array.forEach(((pluginId, def)) => {
|
|
1702
|
+
let connected =
|
|
1703
|
+
pluginId === adminPluginId ||
|
|
1704
|
+
switch pluginStatusById->Dict.get(pluginId) {
|
|
1705
|
+
| Some(Connected) => true
|
|
1706
|
+
| _ => false
|
|
1707
|
+
}
|
|
1708
|
+
if connected {
|
|
1709
|
+
let name = ReventlessCore.Plugin.name(pluginId)
|
|
1710
|
+
let version = ReventlessCore.Plugin.version(pluginId)
|
|
1711
|
+
switch latestByName->Dict.get(name) {
|
|
1712
|
+
| Some((prevVersion, _)) =>
|
|
1713
|
+
if ReventlessCore.Plugin.compareVersions(version, prevVersion) > 0 {
|
|
1714
|
+
latestByName->Dict.set(name, (version, (pluginId, def)))
|
|
1682
1715
|
}
|
|
1716
|
+
| None => latestByName->Dict.set(name, (version, (pluginId, def)))
|
|
1683
1717
|
}
|
|
1684
|
-
}
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1718
|
+
}
|
|
1719
|
+
})
|
|
1720
|
+
latestByName->Dict.valuesToArray->Array.map(((_, pair)) => pair)
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
queryResolvers->Dict.set(
|
|
1724
|
+
"Platform_ComponentDefinitions",
|
|
1725
|
+
async (_root, _args, _ctx): JSON.t =>
|
|
1726
|
+
connectedLatestStructures()
|
|
1727
|
+
->Array.map(((pluginId, def)) =>
|
|
1688
1728
|
ReventlessCore.Platform_ComponentDefinitionsApi.encodePluginStructureEntry(~pluginId, def)
|
|
1689
1729
|
)
|
|
1690
|
-
->JSON.Encode.array
|
|
1691
|
-
|
|
1730
|
+
->JSON.Encode.array,
|
|
1731
|
+
)
|
|
1732
|
+
|
|
1733
|
+
// Platform_PluginStructures resolver — the same plugins, encoded unfiltered for
|
|
1734
|
+
// developer tooling (Internal components kept, extension points carried).
|
|
1735
|
+
queryResolvers->Dict.set(
|
|
1736
|
+
"Platform_PluginStructures",
|
|
1737
|
+
async (_root, _args, _ctx): JSON.t =>
|
|
1738
|
+
connectedLatestStructures()
|
|
1739
|
+
->Array.map(((pluginId, def)) =>
|
|
1740
|
+
ReventlessCore.Platform_PluginStructuresApi.encodePluginStructureEntry(~pluginId, def)
|
|
1741
|
+
)
|
|
1742
|
+
->JSON.Encode.array,
|
|
1692
1743
|
)
|
|
1693
1744
|
|
|
1694
1745
|
// Platform_UIFragments resolver — reads the UiFragments StateViewSlice
|
package/src/Platform.res.mjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as S from "sury/src/S.res.mjs";
|
|
4
4
|
import * as Stream from "@reventlessdev/rescript-effect/src/Stream.res.mjs";
|
|
5
|
+
import * as NodeCrypto from "@reventlessdev/rescript-node/src/NodeCrypto.res.mjs";
|
|
5
6
|
import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
|
|
6
7
|
import * as Stdlib_Bool from "@rescript/runtime/lib/es6/Stdlib_Bool.js";
|
|
7
8
|
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
@@ -40,6 +41,7 @@ import * as LocalPluginSpec$ReventlessLocal from "./adapter/LocalPluginSpec.res.
|
|
|
40
41
|
import * as NoEventMappings$ReventlessInfra from "@reventlessdev/reventless-infra/src/types/NoEventMappings.res.mjs";
|
|
41
42
|
import * as DomainMCP_Server$ReventlessLocal from "./adapter/DomainMCP_Server.res.mjs";
|
|
42
43
|
import * as ExtensionMapping$ReventlessInfra from "@reventlessdev/reventless-infra/src/types/ExtensionMapping.res.mjs";
|
|
44
|
+
import * as LocalObjectStore$ReventlessLocal from "./adapter/ObjectStore/LocalObjectStore.res.mjs";
|
|
43
45
|
import * as LocalQueryEngine$ReventlessLocal from "./adapter/QueryEngine/LocalQueryEngine.res.mjs";
|
|
44
46
|
import * as Platform_AdminApi$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_AdminApi.res.mjs";
|
|
45
47
|
import * as PluginsProjection$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/lifecycle/PluginsProjection.res.mjs";
|
|
@@ -80,6 +82,7 @@ import * as DcbEventLogStorage_Sqlite$ReventlessLocal from "./adapter/DcbEventLo
|
|
|
80
82
|
import * as LocalEventCollectorChannel$ReventlessLocal from "./adapter/EventCollector/LocalEventCollectorChannel.res.mjs";
|
|
81
83
|
import * as PluginRuntime_Builder_Micro$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/Runtime/PluginRuntime_Builder_Micro.res.mjs";
|
|
82
84
|
import * as UiFragmentRegistry_Behavior$ReventlessCore from "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateChangeSlice/UiFragmentRegistry_Behavior.res.mjs";
|
|
85
|
+
import * as Platform_PluginStructuresApi$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_PluginStructuresApi.res.mjs";
|
|
83
86
|
import * as EventHistoryResolvers_GraphQL$ReventlessLocal from "./adapter/EventHistory/EventHistoryResolvers_GraphQL.res.mjs";
|
|
84
87
|
import * as InboundTranslationSlice_Builder$ReventlessLocal from "./components/InboundTranslationSlice_Builder.res.mjs";
|
|
85
88
|
import * as Platform_ComponentDefinitionsApi$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_ComponentDefinitionsApi.res.mjs";
|
|
@@ -126,12 +129,16 @@ function MakeWithConfig(Config) {
|
|
|
126
129
|
if (typeof match !== "object") {
|
|
127
130
|
BackendState$ReventlessLocal.setMemory();
|
|
128
131
|
} else if (match.TAG === "Sqlite") {
|
|
132
|
+
let resetOnStart = match.resetOnStart;
|
|
129
133
|
let path = match.path;
|
|
130
|
-
if (
|
|
134
|
+
if (resetOnStart) {
|
|
131
135
|
Backend$ReventlessLocal.removeFileIfExists(path);
|
|
132
136
|
}
|
|
133
137
|
let db = SqliteDriver$ReventlessLocal.openDb(path);
|
|
134
138
|
BackendState$ReventlessLocal.setSqlite(db, path);
|
|
139
|
+
if (resetOnStart) {
|
|
140
|
+
LocalObjectStore$ReventlessLocal.reset();
|
|
141
|
+
}
|
|
135
142
|
LocalBus$ReventlessLocal.seedEventTapSeq(EventLogStorage_Sqlite$ReventlessLocal.countAll(db) + DcbEventLogStorage_Sqlite$ReventlessLocal.countAll(db) | 0);
|
|
136
143
|
ProjectionPending$ReventlessLocal.enableTracking();
|
|
137
144
|
EventPublish_Callback$ReventlessCore.registerAfterPublish(async publishedEvent => {
|
|
@@ -809,6 +816,17 @@ function MakeWithConfig(Config) {
|
|
|
809
816
|
let domainMcpPort = resolvePort("REVENTLESS_DOMAIN_MCP_PORT", 3001);
|
|
810
817
|
let platformPort = resolvePort("REVENTLESS_PLATFORM_PORT", 4001);
|
|
811
818
|
let platformMcpPort = resolvePort("REVENTLESS_PLATFORM_MCP_PORT", 3002);
|
|
819
|
+
Plugin_Helpers$ReventlessCore.registerOffload((store, bytes) => {
|
|
820
|
+
let hash = NodeCrypto.sha256Hex(bytes);
|
|
821
|
+
let key = "sha256/" + hash;
|
|
822
|
+
LocalObjectStore$ReventlessLocal.putOffload(key, bytes);
|
|
823
|
+
return {
|
|
824
|
+
store: store,
|
|
825
|
+
key: key,
|
|
826
|
+
hash: hash,
|
|
827
|
+
bytes: bytes.length
|
|
828
|
+
};
|
|
829
|
+
});
|
|
812
830
|
let pluginStructuresStore = {
|
|
813
831
|
contents: {}
|
|
814
832
|
};
|
|
@@ -822,18 +840,15 @@ function MakeWithConfig(Config) {
|
|
|
822
840
|
let ps = param[1];
|
|
823
841
|
if (ps !== undefined) {
|
|
824
842
|
pluginStructuresStore.contents[param[0]] = ps;
|
|
843
|
+
Stdlib_Option.getOr(ps.requiredStores, []).forEach(qualified => LocalObjectStore$ReventlessLocal.registerStore(qualified, LocalObjectStore$ReventlessLocal.localPrefixFor(qualified)));
|
|
844
|
+
StorePrefixCollision$ReventlessCore.collisionsFor(LocalObjectStore$ReventlessLocal.declaredStoreList().map(param => ({
|
|
845
|
+
qualified: param[0],
|
|
846
|
+
prefix: param[1]
|
|
847
|
+
}))).forEach(c => log.warn("Platform:plugins", undefined, StorePrefixCollision$ReventlessCore.collisionMessage(c)));
|
|
825
848
|
return;
|
|
826
849
|
}
|
|
827
850
|
});
|
|
828
851
|
});
|
|
829
|
-
let declared = Object.values(pluginStructuresStore.contents).flatMap(structure => Stdlib_Option.getOr(structure.requiredStores, []).map(qualified => {
|
|
830
|
-
let i = Stdlib_String.indexOfOpt(qualified, ".");
|
|
831
|
-
return {
|
|
832
|
-
qualified: qualified,
|
|
833
|
-
prefix: i !== undefined ? qualified.slice(0, i) + "/" + qualified.slice(i + 1 | 0, qualified.length) : qualified
|
|
834
|
-
};
|
|
835
|
-
}));
|
|
836
|
-
StorePrefixCollision$ReventlessCore.collisionsFor(declared).forEach(c => log.warn("Platform:plugins", undefined, StorePrefixCollision$ReventlessCore.collisionMessage(c)));
|
|
837
852
|
};
|
|
838
853
|
let pluginCmdTopicKey = PluginSpec$ReventlessCore.name + "AggrCmdTopic";
|
|
839
854
|
let pluginEventTopicKey = PluginSpec$ReventlessCore.name + "AggrEventTopic";
|
|
@@ -970,15 +985,9 @@ function MakeWithConfig(Config) {
|
|
|
970
985
|
}),
|
|
971
986
|
eventCollector: param[2].name,
|
|
972
987
|
extensionProtocols: [],
|
|
973
|
-
apiSchemaFragment: Stdlib_Option.map(param[5], f => (
|
|
974
|
-
TAG: "Inline",
|
|
975
|
-
_0: f
|
|
976
|
-
})),
|
|
988
|
+
apiSchemaFragment: Stdlib_Option.map(param[5], f => Plugin_Helpers$ReventlessCore.offloadPayload(f, Plugin$Reventless.apiSchemaFragmentSchema, "pluginApiFragments")),
|
|
977
989
|
apiTarget: undefined,
|
|
978
|
-
structure: Stdlib_Option.map(param[7], s => (
|
|
979
|
-
TAG: "Inline",
|
|
980
|
-
_0: s
|
|
981
|
-
})),
|
|
990
|
+
structure: Stdlib_Option.map(param[7], s => Plugin_Helpers$ReventlessCore.offloadPayload(s, Plugin$Reventless.pluginStructureSchema, "pluginStructures")),
|
|
982
991
|
dcbEventLog: undefined,
|
|
983
992
|
kind: "Domain"
|
|
984
993
|
};
|
|
@@ -1341,7 +1350,7 @@ function MakeWithConfig(Config) {
|
|
|
1341
1350
|
let scanAll = Bus.getQueryDbScan(PluginsReadModelSpec$ReventlessCore.name);
|
|
1342
1351
|
return connectionResponse(scanAll !== undefined ? scanAll() : []);
|
|
1343
1352
|
};
|
|
1344
|
-
|
|
1353
|
+
let connectedLatestStructures = () => {
|
|
1345
1354
|
let dict = {};
|
|
1346
1355
|
let scanAll = Bus.getQueryDbScan(PluginsReadModelSpec$ReventlessCore.name);
|
|
1347
1356
|
if (scanAll !== undefined) {
|
|
@@ -1386,11 +1395,10 @@ function MakeWithConfig(Config) {
|
|
|
1386
1395
|
return;
|
|
1387
1396
|
}
|
|
1388
1397
|
});
|
|
1389
|
-
return Object.values(latestByName).map(param =>
|
|
1390
|
-
let match = param[1];
|
|
1391
|
-
return Platform_ComponentDefinitionsApi$ReventlessCore.encodePluginStructureEntry(match[0], match[1]);
|
|
1392
|
-
});
|
|
1398
|
+
return Object.values(latestByName).map(param => param[1]);
|
|
1393
1399
|
};
|
|
1400
|
+
queryResolvers["Platform_ComponentDefinitions"] = async (_root, _args, _ctx) => connectedLatestStructures().map(param => Platform_ComponentDefinitionsApi$ReventlessCore.encodePluginStructureEntry(param[0], param[1]));
|
|
1401
|
+
queryResolvers["Platform_PluginStructures"] = async (_root, _args, _ctx) => connectedLatestStructures().map(param => Platform_PluginStructuresApi$ReventlessCore.encodePluginStructureEntry(param[0], param[1]));
|
|
1394
1402
|
queryResolvers["Platform_UIFragments"] = async (_root, _args, _ctx) => {
|
|
1395
1403
|
let scanAll = Bus.getQueryDbScan(UiFragments$ReventlessCore.name);
|
|
1396
1404
|
let items = scanAll !== undefined ? scanAll() : [];
|
|
@@ -1823,12 +1831,16 @@ function Make($star) {
|
|
|
1823
1831
|
if (typeof backend !== "object") {
|
|
1824
1832
|
BackendState$ReventlessLocal.setMemory();
|
|
1825
1833
|
} else if (backend.TAG === "Sqlite") {
|
|
1834
|
+
let resetOnStart = backend.resetOnStart;
|
|
1826
1835
|
let path = backend.path;
|
|
1827
|
-
if (
|
|
1836
|
+
if (resetOnStart) {
|
|
1828
1837
|
Backend$ReventlessLocal.removeFileIfExists(path);
|
|
1829
1838
|
}
|
|
1830
1839
|
let db = SqliteDriver$ReventlessLocal.openDb(path);
|
|
1831
1840
|
BackendState$ReventlessLocal.setSqlite(db, path);
|
|
1841
|
+
if (resetOnStart) {
|
|
1842
|
+
LocalObjectStore$ReventlessLocal.reset();
|
|
1843
|
+
}
|
|
1832
1844
|
LocalBus$ReventlessLocal.seedEventTapSeq(EventLogStorage_Sqlite$ReventlessLocal.countAll(db) + DcbEventLogStorage_Sqlite$ReventlessLocal.countAll(db) | 0);
|
|
1833
1845
|
ProjectionPending$ReventlessLocal.enableTracking();
|
|
1834
1846
|
EventPublish_Callback$ReventlessCore.registerAfterPublish(async publishedEvent => {
|
|
@@ -2500,6 +2512,17 @@ function Make($star) {
|
|
|
2500
2512
|
let domainMcpPort = resolvePort("REVENTLESS_DOMAIN_MCP_PORT", 3001);
|
|
2501
2513
|
let platformPort = resolvePort("REVENTLESS_PLATFORM_PORT", 4001);
|
|
2502
2514
|
let platformMcpPort = resolvePort("REVENTLESS_PLATFORM_MCP_PORT", 3002);
|
|
2515
|
+
Plugin_Helpers$ReventlessCore.registerOffload((store, bytes) => {
|
|
2516
|
+
let hash = NodeCrypto.sha256Hex(bytes);
|
|
2517
|
+
let key = "sha256/" + hash;
|
|
2518
|
+
LocalObjectStore$ReventlessLocal.putOffload(key, bytes);
|
|
2519
|
+
return {
|
|
2520
|
+
store: store,
|
|
2521
|
+
key: key,
|
|
2522
|
+
hash: hash,
|
|
2523
|
+
bytes: bytes.length
|
|
2524
|
+
};
|
|
2525
|
+
});
|
|
2503
2526
|
let pluginStructuresStore = {
|
|
2504
2527
|
contents: {}
|
|
2505
2528
|
};
|
|
@@ -2513,18 +2536,15 @@ function Make($star) {
|
|
|
2513
2536
|
let ps = param[1];
|
|
2514
2537
|
if (ps !== undefined) {
|
|
2515
2538
|
pluginStructuresStore.contents[param[0]] = ps;
|
|
2539
|
+
Stdlib_Option.getOr(ps.requiredStores, []).forEach(qualified => LocalObjectStore$ReventlessLocal.registerStore(qualified, LocalObjectStore$ReventlessLocal.localPrefixFor(qualified)));
|
|
2540
|
+
StorePrefixCollision$ReventlessCore.collisionsFor(LocalObjectStore$ReventlessLocal.declaredStoreList().map(param => ({
|
|
2541
|
+
qualified: param[0],
|
|
2542
|
+
prefix: param[1]
|
|
2543
|
+
}))).forEach(c => log.warn("Platform:plugins", undefined, StorePrefixCollision$ReventlessCore.collisionMessage(c)));
|
|
2516
2544
|
return;
|
|
2517
2545
|
}
|
|
2518
2546
|
});
|
|
2519
2547
|
});
|
|
2520
|
-
let declared = Object.values(pluginStructuresStore.contents).flatMap(structure => Stdlib_Option.getOr(structure.requiredStores, []).map(qualified => {
|
|
2521
|
-
let i = Stdlib_String.indexOfOpt(qualified, ".");
|
|
2522
|
-
return {
|
|
2523
|
-
qualified: qualified,
|
|
2524
|
-
prefix: i !== undefined ? qualified.slice(0, i) + "/" + qualified.slice(i + 1 | 0, qualified.length) : qualified
|
|
2525
|
-
};
|
|
2526
|
-
}));
|
|
2527
|
-
StorePrefixCollision$ReventlessCore.collisionsFor(declared).forEach(c => log.warn("Platform:plugins", undefined, StorePrefixCollision$ReventlessCore.collisionMessage(c)));
|
|
2528
2548
|
};
|
|
2529
2549
|
let pluginCmdTopicKey = PluginSpec$ReventlessCore.name + "AggrCmdTopic";
|
|
2530
2550
|
let pluginEventTopicKey = PluginSpec$ReventlessCore.name + "AggrEventTopic";
|
|
@@ -2661,15 +2681,9 @@ function Make($star) {
|
|
|
2661
2681
|
}),
|
|
2662
2682
|
eventCollector: param[2].name,
|
|
2663
2683
|
extensionProtocols: [],
|
|
2664
|
-
apiSchemaFragment: Stdlib_Option.map(param[5], f => (
|
|
2665
|
-
TAG: "Inline",
|
|
2666
|
-
_0: f
|
|
2667
|
-
})),
|
|
2684
|
+
apiSchemaFragment: Stdlib_Option.map(param[5], f => Plugin_Helpers$ReventlessCore.offloadPayload(f, Plugin$Reventless.apiSchemaFragmentSchema, "pluginApiFragments")),
|
|
2668
2685
|
apiTarget: undefined,
|
|
2669
|
-
structure: Stdlib_Option.map(param[7], s => (
|
|
2670
|
-
TAG: "Inline",
|
|
2671
|
-
_0: s
|
|
2672
|
-
})),
|
|
2686
|
+
structure: Stdlib_Option.map(param[7], s => Plugin_Helpers$ReventlessCore.offloadPayload(s, Plugin$Reventless.pluginStructureSchema, "pluginStructures")),
|
|
2673
2687
|
dcbEventLog: undefined,
|
|
2674
2688
|
kind: "Domain"
|
|
2675
2689
|
};
|
|
@@ -3027,7 +3041,7 @@ function Make($star) {
|
|
|
3027
3041
|
let scanAll = Bus.getQueryDbScan(PluginsReadModelSpec$ReventlessCore.name);
|
|
3028
3042
|
return connectionResponse(scanAll !== undefined ? scanAll() : []);
|
|
3029
3043
|
};
|
|
3030
|
-
|
|
3044
|
+
let connectedLatestStructures = () => {
|
|
3031
3045
|
let dict = {};
|
|
3032
3046
|
let scanAll = Bus.getQueryDbScan(PluginsReadModelSpec$ReventlessCore.name);
|
|
3033
3047
|
if (scanAll !== undefined) {
|
|
@@ -3072,11 +3086,10 @@ function Make($star) {
|
|
|
3072
3086
|
return;
|
|
3073
3087
|
}
|
|
3074
3088
|
});
|
|
3075
|
-
return Object.values(latestByName).map(param =>
|
|
3076
|
-
let match = param[1];
|
|
3077
|
-
return Platform_ComponentDefinitionsApi$ReventlessCore.encodePluginStructureEntry(match[0], match[1]);
|
|
3078
|
-
});
|
|
3089
|
+
return Object.values(latestByName).map(param => param[1]);
|
|
3079
3090
|
};
|
|
3091
|
+
queryResolvers["Platform_ComponentDefinitions"] = async (_root, _args, _ctx) => connectedLatestStructures().map(param => Platform_ComponentDefinitionsApi$ReventlessCore.encodePluginStructureEntry(param[0], param[1]));
|
|
3092
|
+
queryResolvers["Platform_PluginStructures"] = async (_root, _args, _ctx) => connectedLatestStructures().map(param => Platform_PluginStructuresApi$ReventlessCore.encodePluginStructureEntry(param[0], param[1]));
|
|
3080
3093
|
queryResolvers["Platform_UIFragments"] = async (_root, _args, _ctx) => {
|
|
3081
3094
|
let scanAll = Bus.getQueryDbScan(UiFragments$ReventlessCore.name);
|
|
3082
3095
|
let items = scanAll !== undefined ? scanAll() : [];
|
|
@@ -37,3 +37,16 @@ let getPostgresPool = () =>
|
|
|
37
37
|
| Postgres({pool}) => Some(pool)
|
|
38
38
|
| Memory | Sqlite(_) => None
|
|
39
39
|
}
|
|
40
|
+
|
|
41
|
+
// Directory the object store persists under, derived from the SQLite file's own
|
|
42
|
+
// directory (`./.reventless/local.db` → `./.reventless`) so uploaded bytes and
|
|
43
|
+
// offloaded payloads live and die with the events that reference them. None for
|
|
44
|
+
// the backends with nothing durable on this disk: Memory, an in-process
|
|
45
|
+
// `:memory:` SQLite, and Postgres — durable, but off-machine, and a connection
|
|
46
|
+
// string names no local directory to anchor to, so its object store stays
|
|
47
|
+
// in-process.
|
|
48
|
+
let getObjectStoreRoot = () =>
|
|
49
|
+
switch current.contents {
|
|
50
|
+
| Sqlite({path}) if path != ":memory:" => Some(NodePath.dirname(path))
|
|
51
|
+
| Sqlite(_) | Memory | Postgres(_) => None
|
|
52
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as Nodepath from "node:path";
|
|
3
4
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
4
5
|
|
|
5
6
|
let current = {
|
|
@@ -43,6 +44,20 @@ function getPostgresPool() {
|
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
function getObjectStoreRoot() {
|
|
48
|
+
let match = current.contents;
|
|
49
|
+
if (typeof match !== "object") {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (match.TAG !== "Sqlite") {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
let path = match.path;
|
|
56
|
+
if (path !== ":memory:") {
|
|
57
|
+
return Nodepath.dirname(path);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
46
61
|
export {
|
|
47
62
|
current,
|
|
48
63
|
setMemory,
|
|
@@ -50,5 +65,6 @@ export {
|
|
|
50
65
|
setPostgres,
|
|
51
66
|
getSqliteDb,
|
|
52
67
|
getPostgresPool,
|
|
68
|
+
getObjectStoreRoot,
|
|
53
69
|
}
|
|
54
|
-
/*
|
|
70
|
+
/* node:path Not a pure module */
|