@reventlessdev/reventless-local 3.0.0-alpha.114 → 3.0.0-alpha.116
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 +30 -0
- package/package.json +8 -8
- package/src/Platform.res +116 -67
- package/src/Platform.res.mjs +114 -190
- package/src/adapter/DcbEventLog/DcbEventLogStorage_InMemory.res +102 -17
- package/src/adapter/DcbEventLog/DcbEventLogStorage_InMemory.res.mjs +105 -22
- package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res +123 -40
- package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res.mjs +96 -24
- package/src/adapter/EventCollector/LocalEventCollectorChannel.res +33 -0
- package/src/adapter/EventCollector/LocalEventCollectorChannel.res.mjs +44 -0
- package/src/adapter/EventLog/EventLogStorage_InMemory.res +24 -3
- package/src/adapter/EventLog/EventLogStorage_InMemory.res.mjs +21 -4
- package/src/adapter/EventLog/EventLogStorage_Sqlite.res +148 -27
- package/src/adapter/EventLog/EventLogStorage_Sqlite.res.mjs +144 -34
- package/src/adapter/LocalBus.res +85 -3
- package/src/adapter/LocalBus.res.mjs +220 -66
- package/src/adapter/ProjectionCheckpoint.res +350 -0
- package/src/adapter/ProjectionCheckpoint.res.mjs +327 -0
- package/src/adapter/ProjectionPending.res +60 -0
- package/src/adapter/ProjectionPending.res.mjs +64 -0
- package/src/adapter/QueryDb/QueryDbListQuery.res +261 -0
- package/src/adapter/QueryDb/QueryDbListQuery.res.mjs +212 -0
- package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res +54 -286
- package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res.mjs +28 -190
- package/src/adapter/QueryDb/QueryDbStorage_InMemory.res +128 -10
- package/src/adapter/QueryDb/QueryDbStorage_InMemory.res.mjs +123 -23
- package/src/adapter/QueryDb/QueryDbStorage_Sqlite.res +187 -12
- package/src/adapter/QueryDb/QueryDbStorage_Sqlite.res.mjs +120 -7
- package/src/adapter/SqliteDriver.res +17 -2
- package/src/adapter/SqliteDriver.res.mjs +10 -2
- package/src/components/DcbEventLog_Builder.res +7 -1
- package/src/components/DcbEventLog_Builder.res.mjs +3 -3
- package/src/components/ReadModel_Builder.res +3 -1
- package/src/components/ReadModel_Builder.res.mjs +13 -4
- package/src/components/StateViewSlice_Builder.res +4 -1
- package/src/components/StateViewSlice_Builder.res.mjs +9 -3
- package/src/test/Mocks/MockEventLogStorage.res +20 -3
- package/src/test/Mocks/MockEventLogStorage.res.mjs +25 -3
- package/tests/PluginEventDecodeTest.res +51 -0
- package/tests/PluginEventDecodeTest.res.mjs +63 -0
- package/tests/adapter/BackendParityTest.res +64 -0
- package/tests/adapter/BackendParityTest.res.mjs +45 -0
- package/tests/adapter/DcbEventLogStorageSqliteTest.res +73 -0
- package/tests/adapter/DcbEventLogStorageSqliteTest.res.mjs +86 -0
- package/tests/adapter/DcbEventLogStorageTest.res +46 -0
- package/tests/adapter/DcbEventLogStorageTest.res.mjs +63 -0
- package/tests/adapter/EventLogSnapshotParityTest.res +81 -0
- package/tests/adapter/EventLogSnapshotParityTest.res.mjs +141 -0
- package/tests/adapter/EventLogStorageSqliteTest.res +132 -2
- package/tests/adapter/EventLogStorageSqliteTest.res.mjs +195 -1
- package/tests/adapter/LocalBusPubSubTest.res +31 -0
- package/tests/adapter/LocalBusPubSubTest.res.mjs +28 -0
- package/tests/adapter/ProjectionCheckpointTest.res +422 -0
- package/tests/adapter/ProjectionCheckpointTest.res.mjs +401 -0
- package/tests/adapter/QueryDbGsiTtlTest.res +78 -0
- package/tests/adapter/QueryDbGsiTtlTest.res.mjs +77 -0
- package/tests/adapter/QueryDbListPushdownParityTest.res +197 -0
- package/tests/adapter/QueryDbListPushdownParityTest.res.mjs +292 -0
- package/tests/adapter/QueryDbListResolverTest.res +76 -0
- package/tests/adapter/QueryDbListResolverTest.res.mjs +84 -0
- package/tests/components/aggregate/AggregateFixtures.res +4 -0
- package/tests/components/aggregate/AggregateFixtures.res.mjs +1 -0
- package/tests/components/eventlog/EventLogAppendStreamTest.res.mjs +4 -4
- package/tests/components/eventlog/EventLogStreamTest.res.mjs +8 -8
- package/tests/plugin/PluginBehavior_GWT.res.mjs +1 -0
package/src/Platform.res.mjs
CHANGED
|
@@ -4,15 +4,14 @@ import * as S from "sury/src/S.res.mjs";
|
|
|
4
4
|
import * as Stream from "@reventlessdev/rescript-effect/src/Stream.res.mjs";
|
|
5
5
|
import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
|
|
6
6
|
import * as Stdlib_Bool from "@rescript/runtime/lib/es6/Stdlib_Bool.js";
|
|
7
|
-
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
8
7
|
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
9
8
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
10
9
|
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
11
10
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
12
11
|
import * as Stdlib_String from "@rescript/runtime/lib/es6/Stdlib_String.js";
|
|
13
12
|
import * as Effect from "effect/Effect";
|
|
14
|
-
import * as Stream$1 from "effect/Stream";
|
|
15
13
|
import * as Pulumi from "@pulumi/pulumi";
|
|
14
|
+
import * as Stdlib_Promise from "@rescript/runtime/lib/es6/Stdlib_Promise.js";
|
|
16
15
|
import * as Plugin$Reventless from "@reventlessdev/reventless-spec/src/components/Plugin.res.mjs";
|
|
17
16
|
import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
|
|
18
17
|
import * as Plugin$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/component/Plugin.res.mjs";
|
|
@@ -47,12 +46,15 @@ import * as Scheduler_Builder$ReventlessCore from "@reventlessdev/reventless-cor
|
|
|
47
46
|
import * as Aggregate_Builder$ReventlessLocal from "./components/Aggregate_Builder.res.mjs";
|
|
48
47
|
import * as LocalClonerRunner$ReventlessLocal from "./adapter/Cloner/LocalClonerRunner.res.mjs";
|
|
49
48
|
import * as PluginBaseFragment$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/api/PluginBaseFragment.res.mjs";
|
|
49
|
+
import * as ProjectionPending$ReventlessLocal from "./adapter/ProjectionPending.res.mjs";
|
|
50
50
|
import * as ReadModel_Builder$ReventlessLocal from "./components/ReadModel_Builder.res.mjs";
|
|
51
51
|
import * as PlatformMCP_Server$ReventlessLocal from "./adapter/PlatformMCP_Server.res.mjs";
|
|
52
52
|
import * as Auth_GraphqlContext$ReventlessLocal from "./adapter/Auth/Auth_GraphqlContext.res.mjs";
|
|
53
53
|
import * as PluginsReadModelSpec$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/lifecycle/PluginsReadModelSpec.res.mjs";
|
|
54
54
|
import * as DomainGraphQL_Server$ReventlessLocal from "./adapter/DomainGraphQL_Server.res.mjs";
|
|
55
|
+
import * as EventPublish_Callback$ReventlessCore from "@reventlessdev/reventless-core/src/components/EventLog/EventPublish_Callback.res.mjs";
|
|
55
56
|
import * as LocalGraphQL_Adapter$ReventlessLocal from "./adapter/Api/LocalGraphQL_Adapter.res.mjs";
|
|
57
|
+
import * as ProjectionCheckpoint$ReventlessLocal from "./adapter/ProjectionCheckpoint.res.mjs";
|
|
56
58
|
import * as ExtensionPointMapping$ReventlessInfra from "@reventlessdev/reventless-infra/src/types/ExtensionPointMapping.res.mjs";
|
|
57
59
|
import * as EventLogStorage_Sqlite$ReventlessLocal from "./adapter/EventLog/EventLogStorage_Sqlite.res.mjs";
|
|
58
60
|
import * as ExtensionPoint_Builder$ReventlessLocal from "./components/ExtensionPoint_Builder.res.mjs";
|
|
@@ -63,6 +65,7 @@ import * as AutomationSlice_Builder$ReventlessLocal from "./components/Automatio
|
|
|
63
65
|
import * as LocalRuntimeEnvironment$ReventlessLocal from "./adapter/Runtime/LocalRuntimeEnvironment.res.mjs";
|
|
64
66
|
import * as LocalScheduledPublisher$ReventlessLocal from "./adapter/Scheduler/LocalScheduledPublisher.res.mjs";
|
|
65
67
|
import * as Platform_Admin_Structure$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_Admin_Structure.res.mjs";
|
|
68
|
+
import * as QueryDbStorage_InMemory$ReventlessLocal from "./adapter/QueryDb/QueryDbStorage_InMemory.res.mjs";
|
|
66
69
|
import * as LocalCommandTopicChannel$ReventlessLocal from "./adapter/CommandTopic/LocalCommandTopicChannel.res.mjs";
|
|
67
70
|
import * as LocalEventTopicPublisher$ReventlessLocal from "./adapter/EventTopic/LocalEventTopicPublisher.res.mjs";
|
|
68
71
|
import * as StateChangeSlice_Builder$ReventlessLocal from "./components/StateChangeSlice_Builder.res.mjs";
|
|
@@ -94,6 +97,14 @@ let platformMCPRef = {
|
|
|
94
97
|
contents: undefined
|
|
95
98
|
};
|
|
96
99
|
|
|
100
|
+
function decodePluginEventEnvelope(eventJson) {
|
|
101
|
+
try {
|
|
102
|
+
return Stdlib_Option.map(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(eventJson), d => d["event"]), payload => S.parseJsonOrThrow(payload, PluginSpec$ReventlessCore.eventSchema));
|
|
103
|
+
} catch (exn) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
97
108
|
function MakeWithConfig(Config) {
|
|
98
109
|
TestRunner$ReventlessLocal.setup();
|
|
99
110
|
let match = Config.backend;
|
|
@@ -107,6 +118,12 @@ function MakeWithConfig(Config) {
|
|
|
107
118
|
let db = SqliteDriver$ReventlessLocal.openDb(path);
|
|
108
119
|
BackendState$ReventlessLocal.setSqlite(db, path);
|
|
109
120
|
LocalBus$ReventlessLocal.seedEventTapSeq(EventLogStorage_Sqlite$ReventlessLocal.countAll(db) + DcbEventLogStorage_Sqlite$ReventlessLocal.countAll(db) | 0);
|
|
121
|
+
ProjectionPending$ReventlessLocal.enableTracking();
|
|
122
|
+
EventPublish_Callback$ReventlessCore.registerAfterPublish(async publishedEvent => {
|
|
123
|
+
let metas = publishedEvent.metas;
|
|
124
|
+
let msgIds = metas !== undefined ? metas.map(m => m.msgId) : Stdlib_Array.filterMap(publishedEvent.eventsJson, json => Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(json), d => d["msgId"]), Stdlib_JSON.Decode.string));
|
|
125
|
+
return ProjectionCheckpoint$ReventlessLocal.completePublished(db, msgIds);
|
|
126
|
+
});
|
|
110
127
|
}
|
|
111
128
|
let Bus = LocalBus$ReventlessLocal.Impl({
|
|
112
129
|
capacity: undefined,
|
|
@@ -268,7 +285,15 @@ function MakeWithConfig(Config) {
|
|
|
268
285
|
let paginate = (events, getPosition) => {
|
|
269
286
|
let filtered;
|
|
270
287
|
if (after !== undefined) {
|
|
271
|
-
let idx = events.findIndex(e => Stdlib_Option.mapOr(getPosition(e), false, p =>
|
|
288
|
+
let idx = events.findIndex(e => Stdlib_Option.mapOr(getPosition(e), false, p => {
|
|
289
|
+
let match = Stdlib_Int.fromString(p, undefined);
|
|
290
|
+
let match$1 = Stdlib_Int.fromString(after, undefined);
|
|
291
|
+
if (match !== undefined && match$1 !== undefined) {
|
|
292
|
+
return match > match$1;
|
|
293
|
+
} else {
|
|
294
|
+
return p > after;
|
|
295
|
+
}
|
|
296
|
+
}));
|
|
272
297
|
filtered = idx >= 0 ? events.slice(idx, events.length) : [];
|
|
273
298
|
} else {
|
|
274
299
|
filtered = events;
|
|
@@ -630,6 +655,7 @@ function MakeWithConfig(Config) {
|
|
|
630
655
|
initialState: PluginBehavior$ReventlessCore.initialState,
|
|
631
656
|
evolve: PluginBehavior$ReventlessCore.evolve,
|
|
632
657
|
decide: PluginBehavior$ReventlessCore.decide,
|
|
658
|
+
snapshot: undefined,
|
|
633
659
|
moduleUrl: PluginBehavior$ReventlessCore.moduleUrl
|
|
634
660
|
})(NoEventMappings$ReventlessInfra.Make({
|
|
635
661
|
name: PluginSpec$ReventlessCore.name,
|
|
@@ -704,89 +730,14 @@ function MakeWithConfig(Config) {
|
|
|
704
730
|
let uiFragmentQueryDbOpsRef = {
|
|
705
731
|
contents: undefined
|
|
706
732
|
};
|
|
733
|
+
let UIFragmentStorage = QueryDbStorage_InMemory$ReventlessLocal.Make(Bus);
|
|
707
734
|
let ensureUIFragmentRegistryQueryDbStore = () => {
|
|
708
735
|
let ops = uiFragmentQueryDbOpsRef.contents;
|
|
709
736
|
if (ops !== undefined) {
|
|
710
737
|
return ops;
|
|
711
738
|
}
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
};
|
|
715
|
-
let allItems = {
|
|
716
|
-
contents: []
|
|
717
|
-
};
|
|
718
|
-
let syncAll = () => {
|
|
719
|
-
allItems.contents = Object.entries(store.contents).flatMap(param => {
|
|
720
|
-
let id = param[0];
|
|
721
|
-
return param[1].map(item => {
|
|
722
|
-
let obj = Stdlib_Option.getOr(Stdlib_JSON.Decode.object(item), {});
|
|
723
|
-
if (Stdlib_Option.isSome(obj["id"])) {
|
|
724
|
-
return item;
|
|
725
|
-
}
|
|
726
|
-
let copy = {};
|
|
727
|
-
Object.entries(obj).forEach(param => {
|
|
728
|
-
copy[param[0]] = param[1];
|
|
729
|
-
});
|
|
730
|
-
copy["id"] = id;
|
|
731
|
-
return copy;
|
|
732
|
-
});
|
|
733
|
-
});
|
|
734
|
-
};
|
|
735
|
-
let ops_load = async id => ({
|
|
736
|
-
TAG: "Ok",
|
|
737
|
-
_0: Stdlib_Option.getOr(store.contents[id], [])
|
|
738
|
-
});
|
|
739
|
-
let ops_loadStream = id => Stream$1.fromIterable(Stdlib_Option.getOr(store.contents[id], []));
|
|
740
|
-
let ops_save = async (id, state, param, param$1) => {
|
|
741
|
-
store.contents[id] = [state];
|
|
742
|
-
syncAll();
|
|
743
|
-
return {
|
|
744
|
-
TAG: "Ok",
|
|
745
|
-
_0: undefined
|
|
746
|
-
};
|
|
747
|
-
};
|
|
748
|
-
let ops_saveBatch = async batch => {
|
|
749
|
-
batch.forEach(param => {
|
|
750
|
-
store.contents[param[0]] = [param[1]];
|
|
751
|
-
});
|
|
752
|
-
syncAll();
|
|
753
|
-
return {
|
|
754
|
-
TAG: "Ok",
|
|
755
|
-
_0: undefined
|
|
756
|
-
};
|
|
757
|
-
};
|
|
758
|
-
let ops_count = async (param, param$1, inc) => ({
|
|
759
|
-
TAG: "Ok",
|
|
760
|
-
_0: inc
|
|
761
|
-
});
|
|
762
|
-
let ops_delete = async (id, param) => {
|
|
763
|
-
Stdlib_Dict.$$delete(store.contents, id);
|
|
764
|
-
syncAll();
|
|
765
|
-
return {
|
|
766
|
-
TAG: "Ok",
|
|
767
|
-
_0: undefined
|
|
768
|
-
};
|
|
769
|
-
};
|
|
770
|
-
let ops_deleteBatch = async ids => {
|
|
771
|
-
ids.forEach(param => Stdlib_Dict.$$delete(store.contents, param[0]));
|
|
772
|
-
syncAll();
|
|
773
|
-
return {
|
|
774
|
-
TAG: "Ok",
|
|
775
|
-
_0: undefined
|
|
776
|
-
};
|
|
777
|
-
};
|
|
778
|
-
let ops$1 = {
|
|
779
|
-
load: ops_load,
|
|
780
|
-
loadStream: ops_loadStream,
|
|
781
|
-
save: ops_save,
|
|
782
|
-
saveBatch: ops_saveBatch,
|
|
783
|
-
count: ops_count,
|
|
784
|
-
delete: ops_delete,
|
|
785
|
-
deleteBatch: ops_deleteBatch
|
|
786
|
-
};
|
|
787
|
-
Bus.registerQueryDb(UIFragmentRegistryReadModelSpec$ReventlessCore.name, ops$1);
|
|
788
|
-
Bus.registerQueryDbScan(UIFragmentRegistryReadModelSpec$ReventlessCore.name, () => allItems.contents);
|
|
789
|
-
Bus.registerQueryDbStream(UIFragmentRegistryReadModelSpec$ReventlessCore.name, () => Stream$1.fromIterable(allItems.contents));
|
|
739
|
+
UIFragmentStorage.make(UIFragmentRegistryReadModelSpec$ReventlessCore.name, [], undefined, undefined, undefined, undefined, {});
|
|
740
|
+
let ops$1 = Stdlib_Option.getOrThrow(Bus.getQueryDb(UIFragmentRegistryReadModelSpec$ReventlessCore.name), undefined);
|
|
790
741
|
uiFragmentQueryDbOpsRef.contents = ops$1;
|
|
791
742
|
return ops$1;
|
|
792
743
|
};
|
|
@@ -855,33 +806,31 @@ function MakeWithConfig(Config) {
|
|
|
855
806
|
]
|
|
856
807
|
]));
|
|
857
808
|
Bus.subscribeToEvents(pluginEventTopicKey, async (_service, _meta, eventJson) => {
|
|
858
|
-
let
|
|
859
|
-
|
|
860
|
-
def = S.convertOrThrow(eventJson, PluginSpec$ReventlessCore.eventSchema);
|
|
861
|
-
} catch (exn) {
|
|
809
|
+
let match = decodePluginEventEnvelope(eventJson);
|
|
810
|
+
if (match === undefined) {
|
|
862
811
|
return;
|
|
863
812
|
}
|
|
864
|
-
switch (
|
|
813
|
+
switch (match.TAG) {
|
|
865
814
|
case "VersionDisconnected" :
|
|
866
|
-
return publishStatus(
|
|
815
|
+
return publishStatus(match._0.name, "Disconnected");
|
|
867
816
|
case "VersionDeactivated" :
|
|
868
|
-
return publishStatus(
|
|
817
|
+
return publishStatus(match._0.name, "Inactive");
|
|
869
818
|
case "VersionRetired" :
|
|
870
|
-
return publishStatus(
|
|
819
|
+
return publishStatus(match._0.name, "Retired");
|
|
871
820
|
case "VersionDetected" :
|
|
872
821
|
case "VersionSuperseded" :
|
|
873
822
|
case "IncompatiblePluginDetected" :
|
|
874
823
|
return;
|
|
875
824
|
case "UIFragmentRegistered" :
|
|
876
|
-
let data =
|
|
825
|
+
let data = match._0;
|
|
877
826
|
return publishUIFragment(Plugin$ReventlessCore.name(data.pluginId), "Registered", S.reverseConvertToJsonOrThrow(data.manifest, Plugin$Reventless.uiFragmentManifestSchema));
|
|
878
827
|
case "UIFragmentUpdated" :
|
|
879
|
-
let data$1 =
|
|
828
|
+
let data$1 = match._0;
|
|
880
829
|
return publishUIFragment(Plugin$ReventlessCore.name(data$1.pluginId), "Updated", S.reverseConvertToJsonOrThrow(data$1.newManifest, Plugin$Reventless.uiFragmentManifestSchema));
|
|
881
830
|
case "UIFragmentDeregistered" :
|
|
882
|
-
return publishUIFragment(Plugin$ReventlessCore.name(
|
|
831
|
+
return publishUIFragment(Plugin$ReventlessCore.name(match._0.pluginId), "Deregistered", null);
|
|
883
832
|
default:
|
|
884
|
-
return publishStatus(
|
|
833
|
+
return publishStatus(match._0.name, "Connected");
|
|
885
834
|
}
|
|
886
835
|
});
|
|
887
836
|
};
|
|
@@ -1149,6 +1098,11 @@ function MakeWithConfig(Config) {
|
|
|
1149
1098
|
match.resetOnStart ? ", resetOnStart" : ""
|
|
1150
1099
|
) + `)`;
|
|
1151
1100
|
log.info("Platform", undefined, tmp);
|
|
1101
|
+
let projectionCatchup = Stdlib_Option.map(BackendState$ReventlessLocal.getDb(), db => [
|
|
1102
|
+
db,
|
|
1103
|
+
ProjectionCheckpoint$ReventlessLocal.maxPosition(db, "Aggregate"),
|
|
1104
|
+
ProjectionCheckpoint$ReventlessLocal.maxPosition(db, "Dcb")
|
|
1105
|
+
]);
|
|
1152
1106
|
let scheduler = makeScheduler();
|
|
1153
1107
|
hooks_scheduler.contents = scheduler;
|
|
1154
1108
|
hooks_api.contents = {
|
|
@@ -1207,9 +1161,22 @@ function MakeWithConfig(Config) {
|
|
|
1207
1161
|
adminResources: []
|
|
1208
1162
|
});
|
|
1209
1163
|
subscribeToPluginEvents();
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1164
|
+
let seedAdminStores = () => {
|
|
1165
|
+
connectPlugin(plugins$1);
|
|
1166
|
+
seedUIFragmentRegistryQueryDb(plugins$1);
|
|
1167
|
+
seedPluginStructuresStore(plugins$1);
|
|
1168
|
+
};
|
|
1169
|
+
if (projectionCatchup !== undefined) {
|
|
1170
|
+
Stdlib_Promise.$$catch(ProjectionCheckpoint$ReventlessLocal.startupCatchup(projectionCatchup[0], projectionCatchup[1], projectionCatchup[2], () => Bus.projectionCatchupHandlers()), e => {
|
|
1171
|
+
console.error("[Platform] projection catch-up failed:", e);
|
|
1172
|
+
return Promise.resolve();
|
|
1173
|
+
}).then(() => {
|
|
1174
|
+
seedAdminStores();
|
|
1175
|
+
return Promise.resolve();
|
|
1176
|
+
});
|
|
1177
|
+
} else {
|
|
1178
|
+
seedAdminStores();
|
|
1179
|
+
}
|
|
1213
1180
|
pluginStructuresStore.contents[Platform_Admin_Structure$ReventlessCore.pluginId] = Platform_Admin_Structure$ReventlessCore.structure;
|
|
1214
1181
|
CommandGeneratorResolvers_GraphQL$ReventlessLocal.setPluginStatusGate(field => {
|
|
1215
1182
|
let parts = field.split("_");
|
|
@@ -1766,6 +1733,12 @@ function Make($star) {
|
|
|
1766
1733
|
let db = SqliteDriver$ReventlessLocal.openDb(path);
|
|
1767
1734
|
BackendState$ReventlessLocal.setSqlite(db, path);
|
|
1768
1735
|
LocalBus$ReventlessLocal.seedEventTapSeq(EventLogStorage_Sqlite$ReventlessLocal.countAll(db) + DcbEventLogStorage_Sqlite$ReventlessLocal.countAll(db) | 0);
|
|
1736
|
+
ProjectionPending$ReventlessLocal.enableTracking();
|
|
1737
|
+
EventPublish_Callback$ReventlessCore.registerAfterPublish(async publishedEvent => {
|
|
1738
|
+
let metas = publishedEvent.metas;
|
|
1739
|
+
let msgIds = metas !== undefined ? metas.map(m => m.msgId) : Stdlib_Array.filterMap(publishedEvent.eventsJson, json => Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(json), d => d["msgId"]), Stdlib_JSON.Decode.string));
|
|
1740
|
+
return ProjectionCheckpoint$ReventlessLocal.completePublished(db, msgIds);
|
|
1741
|
+
});
|
|
1769
1742
|
}
|
|
1770
1743
|
let Bus = LocalBus$ReventlessLocal.Impl({
|
|
1771
1744
|
capacity: undefined,
|
|
@@ -1921,7 +1894,15 @@ function Make($star) {
|
|
|
1921
1894
|
let paginate = (events, getPosition) => {
|
|
1922
1895
|
let filtered;
|
|
1923
1896
|
if (after !== undefined) {
|
|
1924
|
-
let idx = events.findIndex(e => Stdlib_Option.mapOr(getPosition(e), false, p =>
|
|
1897
|
+
let idx = events.findIndex(e => Stdlib_Option.mapOr(getPosition(e), false, p => {
|
|
1898
|
+
let match = Stdlib_Int.fromString(p, undefined);
|
|
1899
|
+
let match$1 = Stdlib_Int.fromString(after, undefined);
|
|
1900
|
+
if (match !== undefined && match$1 !== undefined) {
|
|
1901
|
+
return match > match$1;
|
|
1902
|
+
} else {
|
|
1903
|
+
return p > after;
|
|
1904
|
+
}
|
|
1905
|
+
}));
|
|
1925
1906
|
filtered = idx >= 0 ? events.slice(idx, events.length) : [];
|
|
1926
1907
|
} else {
|
|
1927
1908
|
filtered = events;
|
|
@@ -2282,6 +2263,7 @@ function Make($star) {
|
|
|
2282
2263
|
initialState: PluginBehavior$ReventlessCore.initialState,
|
|
2283
2264
|
evolve: PluginBehavior$ReventlessCore.evolve,
|
|
2284
2265
|
decide: PluginBehavior$ReventlessCore.decide,
|
|
2266
|
+
snapshot: undefined,
|
|
2285
2267
|
moduleUrl: PluginBehavior$ReventlessCore.moduleUrl
|
|
2286
2268
|
})(NoEventMappings$ReventlessInfra.Make({
|
|
2287
2269
|
name: PluginSpec$ReventlessCore.name,
|
|
@@ -2356,89 +2338,14 @@ function Make($star) {
|
|
|
2356
2338
|
let uiFragmentQueryDbOpsRef = {
|
|
2357
2339
|
contents: undefined
|
|
2358
2340
|
};
|
|
2341
|
+
let UIFragmentStorage = QueryDbStorage_InMemory$ReventlessLocal.Make(Bus);
|
|
2359
2342
|
let ensureUIFragmentRegistryQueryDbStore = () => {
|
|
2360
2343
|
let ops = uiFragmentQueryDbOpsRef.contents;
|
|
2361
2344
|
if (ops !== undefined) {
|
|
2362
2345
|
return ops;
|
|
2363
2346
|
}
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
};
|
|
2367
|
-
let allItems = {
|
|
2368
|
-
contents: []
|
|
2369
|
-
};
|
|
2370
|
-
let syncAll = () => {
|
|
2371
|
-
allItems.contents = Object.entries(store.contents).flatMap(param => {
|
|
2372
|
-
let id = param[0];
|
|
2373
|
-
return param[1].map(item => {
|
|
2374
|
-
let obj = Stdlib_Option.getOr(Stdlib_JSON.Decode.object(item), {});
|
|
2375
|
-
if (Stdlib_Option.isSome(obj["id"])) {
|
|
2376
|
-
return item;
|
|
2377
|
-
}
|
|
2378
|
-
let copy = {};
|
|
2379
|
-
Object.entries(obj).forEach(param => {
|
|
2380
|
-
copy[param[0]] = param[1];
|
|
2381
|
-
});
|
|
2382
|
-
copy["id"] = id;
|
|
2383
|
-
return copy;
|
|
2384
|
-
});
|
|
2385
|
-
});
|
|
2386
|
-
};
|
|
2387
|
-
let ops_load = async id => ({
|
|
2388
|
-
TAG: "Ok",
|
|
2389
|
-
_0: Stdlib_Option.getOr(store.contents[id], [])
|
|
2390
|
-
});
|
|
2391
|
-
let ops_loadStream = id => Stream$1.fromIterable(Stdlib_Option.getOr(store.contents[id], []));
|
|
2392
|
-
let ops_save = async (id, state, param, param$1) => {
|
|
2393
|
-
store.contents[id] = [state];
|
|
2394
|
-
syncAll();
|
|
2395
|
-
return {
|
|
2396
|
-
TAG: "Ok",
|
|
2397
|
-
_0: undefined
|
|
2398
|
-
};
|
|
2399
|
-
};
|
|
2400
|
-
let ops_saveBatch = async batch => {
|
|
2401
|
-
batch.forEach(param => {
|
|
2402
|
-
store.contents[param[0]] = [param[1]];
|
|
2403
|
-
});
|
|
2404
|
-
syncAll();
|
|
2405
|
-
return {
|
|
2406
|
-
TAG: "Ok",
|
|
2407
|
-
_0: undefined
|
|
2408
|
-
};
|
|
2409
|
-
};
|
|
2410
|
-
let ops_count = async (param, param$1, inc) => ({
|
|
2411
|
-
TAG: "Ok",
|
|
2412
|
-
_0: inc
|
|
2413
|
-
});
|
|
2414
|
-
let ops_delete = async (id, param) => {
|
|
2415
|
-
Stdlib_Dict.$$delete(store.contents, id);
|
|
2416
|
-
syncAll();
|
|
2417
|
-
return {
|
|
2418
|
-
TAG: "Ok",
|
|
2419
|
-
_0: undefined
|
|
2420
|
-
};
|
|
2421
|
-
};
|
|
2422
|
-
let ops_deleteBatch = async ids => {
|
|
2423
|
-
ids.forEach(param => Stdlib_Dict.$$delete(store.contents, param[0]));
|
|
2424
|
-
syncAll();
|
|
2425
|
-
return {
|
|
2426
|
-
TAG: "Ok",
|
|
2427
|
-
_0: undefined
|
|
2428
|
-
};
|
|
2429
|
-
};
|
|
2430
|
-
let ops$1 = {
|
|
2431
|
-
load: ops_load,
|
|
2432
|
-
loadStream: ops_loadStream,
|
|
2433
|
-
save: ops_save,
|
|
2434
|
-
saveBatch: ops_saveBatch,
|
|
2435
|
-
count: ops_count,
|
|
2436
|
-
delete: ops_delete,
|
|
2437
|
-
deleteBatch: ops_deleteBatch
|
|
2438
|
-
};
|
|
2439
|
-
Bus.registerQueryDb(UIFragmentRegistryReadModelSpec$ReventlessCore.name, ops$1);
|
|
2440
|
-
Bus.registerQueryDbScan(UIFragmentRegistryReadModelSpec$ReventlessCore.name, () => allItems.contents);
|
|
2441
|
-
Bus.registerQueryDbStream(UIFragmentRegistryReadModelSpec$ReventlessCore.name, () => Stream$1.fromIterable(allItems.contents));
|
|
2347
|
+
UIFragmentStorage.make(UIFragmentRegistryReadModelSpec$ReventlessCore.name, [], undefined, undefined, undefined, undefined, {});
|
|
2348
|
+
let ops$1 = Stdlib_Option.getOrThrow(Bus.getQueryDb(UIFragmentRegistryReadModelSpec$ReventlessCore.name), undefined);
|
|
2442
2349
|
uiFragmentQueryDbOpsRef.contents = ops$1;
|
|
2443
2350
|
return ops$1;
|
|
2444
2351
|
};
|
|
@@ -2507,33 +2414,31 @@ function Make($star) {
|
|
|
2507
2414
|
]
|
|
2508
2415
|
]));
|
|
2509
2416
|
Bus.subscribeToEvents(pluginEventTopicKey, async (_service, _meta, eventJson) => {
|
|
2510
|
-
let
|
|
2511
|
-
|
|
2512
|
-
def = S.convertOrThrow(eventJson, PluginSpec$ReventlessCore.eventSchema);
|
|
2513
|
-
} catch (exn) {
|
|
2417
|
+
let match = decodePluginEventEnvelope(eventJson);
|
|
2418
|
+
if (match === undefined) {
|
|
2514
2419
|
return;
|
|
2515
2420
|
}
|
|
2516
|
-
switch (
|
|
2421
|
+
switch (match.TAG) {
|
|
2517
2422
|
case "VersionDisconnected" :
|
|
2518
|
-
return publishStatus(
|
|
2423
|
+
return publishStatus(match._0.name, "Disconnected");
|
|
2519
2424
|
case "VersionDeactivated" :
|
|
2520
|
-
return publishStatus(
|
|
2425
|
+
return publishStatus(match._0.name, "Inactive");
|
|
2521
2426
|
case "VersionRetired" :
|
|
2522
|
-
return publishStatus(
|
|
2427
|
+
return publishStatus(match._0.name, "Retired");
|
|
2523
2428
|
case "VersionDetected" :
|
|
2524
2429
|
case "VersionSuperseded" :
|
|
2525
2430
|
case "IncompatiblePluginDetected" :
|
|
2526
2431
|
return;
|
|
2527
2432
|
case "UIFragmentRegistered" :
|
|
2528
|
-
let data =
|
|
2433
|
+
let data = match._0;
|
|
2529
2434
|
return publishUIFragment(Plugin$ReventlessCore.name(data.pluginId), "Registered", S.reverseConvertToJsonOrThrow(data.manifest, Plugin$Reventless.uiFragmentManifestSchema));
|
|
2530
2435
|
case "UIFragmentUpdated" :
|
|
2531
|
-
let data$1 =
|
|
2436
|
+
let data$1 = match._0;
|
|
2532
2437
|
return publishUIFragment(Plugin$ReventlessCore.name(data$1.pluginId), "Updated", S.reverseConvertToJsonOrThrow(data$1.newManifest, Plugin$Reventless.uiFragmentManifestSchema));
|
|
2533
2438
|
case "UIFragmentDeregistered" :
|
|
2534
|
-
return publishUIFragment(Plugin$ReventlessCore.name(
|
|
2439
|
+
return publishUIFragment(Plugin$ReventlessCore.name(match._0.pluginId), "Deregistered", null);
|
|
2535
2440
|
default:
|
|
2536
|
-
return publishStatus(
|
|
2441
|
+
return publishStatus(match._0.name, "Connected");
|
|
2537
2442
|
}
|
|
2538
2443
|
});
|
|
2539
2444
|
};
|
|
@@ -2796,6 +2701,11 @@ function Make($star) {
|
|
|
2796
2701
|
backend.resetOnStart ? ", resetOnStart" : ""
|
|
2797
2702
|
) + `)`;
|
|
2798
2703
|
log.info("Platform", undefined, tmp);
|
|
2704
|
+
let projectionCatchup = Stdlib_Option.map(BackendState$ReventlessLocal.getDb(), db => [
|
|
2705
|
+
db,
|
|
2706
|
+
ProjectionCheckpoint$ReventlessLocal.maxPosition(db, "Aggregate"),
|
|
2707
|
+
ProjectionCheckpoint$ReventlessLocal.maxPosition(db, "Dcb")
|
|
2708
|
+
]);
|
|
2799
2709
|
let scheduler = makeScheduler();
|
|
2800
2710
|
hooks_scheduler.contents = scheduler;
|
|
2801
2711
|
hooks_api.contents = {
|
|
@@ -2854,9 +2764,22 @@ function Make($star) {
|
|
|
2854
2764
|
adminResources: []
|
|
2855
2765
|
});
|
|
2856
2766
|
subscribeToPluginEvents();
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2767
|
+
let seedAdminStores = () => {
|
|
2768
|
+
connectPlugin(plugins$1);
|
|
2769
|
+
seedUIFragmentRegistryQueryDb(plugins$1);
|
|
2770
|
+
seedPluginStructuresStore(plugins$1);
|
|
2771
|
+
};
|
|
2772
|
+
if (projectionCatchup !== undefined) {
|
|
2773
|
+
Stdlib_Promise.$$catch(ProjectionCheckpoint$ReventlessLocal.startupCatchup(projectionCatchup[0], projectionCatchup[1], projectionCatchup[2], () => Bus.projectionCatchupHandlers()), e => {
|
|
2774
|
+
console.error("[Platform] projection catch-up failed:", e);
|
|
2775
|
+
return Promise.resolve();
|
|
2776
|
+
}).then(() => {
|
|
2777
|
+
seedAdminStores();
|
|
2778
|
+
return Promise.resolve();
|
|
2779
|
+
});
|
|
2780
|
+
} else {
|
|
2781
|
+
seedAdminStores();
|
|
2782
|
+
}
|
|
2860
2783
|
pluginStructuresStore.contents[Platform_Admin_Structure$ReventlessCore.pluginId] = Platform_Admin_Structure$ReventlessCore.structure;
|
|
2861
2784
|
CommandGeneratorResolvers_GraphQL$ReventlessLocal.setPluginStatusGate(field => {
|
|
2862
2785
|
let parts = field.split("_");
|
|
@@ -3391,6 +3314,7 @@ export {
|
|
|
3391
3314
|
platformGraphQLRef,
|
|
3392
3315
|
getPlatformGraphQL,
|
|
3393
3316
|
platformMCPRef,
|
|
3317
|
+
decodePluginEventEnvelope,
|
|
3394
3318
|
MakeWithConfig,
|
|
3395
3319
|
Make,
|
|
3396
3320
|
}
|
|
@@ -25,18 +25,108 @@ let matchesQuery = (event: DcbEventLog_Adapter.rawSequencedEvent, query: Reventl
|
|
|
25
25
|
})
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
// Composite key for the per-(key,value) tag posting list. NUL can't appear in a
|
|
29
|
+
// tag key or value, so it's an unambiguous separator.
|
|
30
|
+
let tagPostingKey = (key: string, value: string) => key ++ "\x00" ++ value
|
|
31
|
+
|
|
32
|
+
// Intersect two ascending, duplicate-free index arrays (two-pointer merge).
|
|
33
|
+
let intersectSorted = (a: array<int>, b: array<int>): array<int> => {
|
|
34
|
+
let out = []
|
|
35
|
+
let i = ref(0)
|
|
36
|
+
let j = ref(0)
|
|
37
|
+
while i.contents < a->Array.length && j.contents < b->Array.length {
|
|
38
|
+
let av = a->Array.getUnsafe(i.contents)
|
|
39
|
+
let bv = b->Array.getUnsafe(j.contents)
|
|
40
|
+
if av == bv {
|
|
41
|
+
out->Array.push(av)
|
|
42
|
+
i := i.contents + 1
|
|
43
|
+
j := j.contents + 1
|
|
44
|
+
} else if av < bv {
|
|
45
|
+
i := i.contents + 1
|
|
46
|
+
} else {
|
|
47
|
+
j := j.contents + 1
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
out
|
|
51
|
+
}
|
|
52
|
+
|
|
28
53
|
let makeStorage = (~name as _name, ~indexes as _, ~partitionTag as _, ~opts as _) => {
|
|
29
54
|
let events: ref<array<DcbEventLog_Adapter.rawSequencedEvent>> = ref([])
|
|
30
55
|
let position = ref(0)
|
|
56
|
+
// Posting lists: tag "key\0value" / eventType → ascending array indices into
|
|
57
|
+
// `events`. Events are only ever appended (never removed) in position order, so
|
|
58
|
+
// every list stays sorted and duplicate-free by construction. They turn the
|
|
59
|
+
// per-read / per-conditional-append full scan (O(n) → O(n²) over a session)
|
|
60
|
+
// into a lookup + intersection over the (usually tiny) candidate set.
|
|
61
|
+
let byTag: Dict.t<array<int>> = Dict.make()
|
|
62
|
+
let byType: Dict.t<array<int>> = Dict.make()
|
|
63
|
+
let pushInto = (dict: Dict.t<array<int>>, key: string, idx: int) =>
|
|
64
|
+
switch dict->Dict.get(key) {
|
|
65
|
+
| Some(list) => list->Array.push(idx)
|
|
66
|
+
| None => dict->Dict.set(key, [idx])
|
|
67
|
+
}
|
|
68
|
+
let indexEvent = (idx: int, ev: DcbEventLog_Adapter.rawSequencedEvent) => {
|
|
69
|
+
pushInto(byType, ev.eventType, idx)
|
|
70
|
+
ev.tags->Array.forEach(t => pushInto(byTag, tagPostingKey(t.key, t.value), idx))
|
|
71
|
+
}
|
|
31
72
|
|
|
32
|
-
|
|
33
|
-
|
|
73
|
+
// Ascending, duplicate-free superset of the indices that could satisfy the
|
|
74
|
+
// query (an OR of clauses). Narrowing only — the authoritative `matchesQuery`
|
|
75
|
+
// predicate is still applied to each candidate, so the result is identical to
|
|
76
|
+
// the old linear scan even if this over-includes. Callers guarantee query≠[].
|
|
77
|
+
let candidateIndices = (query: Reventless.DcbTag.query): array<int> => {
|
|
78
|
+
let acc: Dict.t<int> = Dict.make()
|
|
79
|
+
let addAll = (arr: array<int>) => arr->Array.forEach(i => acc->Dict.set(Int.toString(i), i))
|
|
80
|
+
query->Array.forEach(clause => {
|
|
81
|
+
switch clause.tags {
|
|
82
|
+
| Some(tags) if tags->Array.length > 0 =>
|
|
83
|
+
// Events carrying ALL of the clause's tags = intersection of the tags'
|
|
84
|
+
// posting lists; a missing tag ⇒ empty ⇒ this clause adds nothing.
|
|
85
|
+
let lists = tags->Array.map(t =>
|
|
86
|
+
byTag->Dict.get(tagPostingKey(t.key, t.value))->Option.getOr([])
|
|
87
|
+
)
|
|
88
|
+
if lists->Array.some(l => l->Array.length == 0) {
|
|
89
|
+
()
|
|
90
|
+
} else {
|
|
91
|
+
let sorted = lists->Array.toSorted((a, b) => Int.toFloat(a->Array.length - b->Array.length))
|
|
92
|
+
let acc0 = sorted->Array.getUnsafe(0)
|
|
93
|
+
let folded =
|
|
94
|
+
sorted->Array.slice(~start=1, ~end=sorted->Array.length)->Array.reduce(acc0, intersectSorted)
|
|
95
|
+
addAll(folded)
|
|
96
|
+
}
|
|
97
|
+
| _ =>
|
|
98
|
+
// No tag constraint: narrow by event type when present (union of the
|
|
99
|
+
// types' posting lists — disjoint since an event has one type), else the
|
|
100
|
+
// clause is unconstrained and matches everything.
|
|
101
|
+
switch clause.eventTypes {
|
|
102
|
+
| Some(types) => types->Array.forEach(t => addAll(byType->Dict.get(t)->Option.getOr([])))
|
|
103
|
+
| None => events.contents->Array.forEachWithIndex((_, i) => acc->Dict.set(Int.toString(i), i))
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
acc->Dict.valuesToArray->Array.toSorted((a, b) => Int.toFloat(a - b))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let matchingEvents = (~query, ~after): array<DcbEventLog_Adapter.rawSequencedEvent> => {
|
|
111
|
+
let pass = (ev: DcbEventLog_Adapter.rawSequencedEvent) => {
|
|
34
112
|
let afterMatch = switch after {
|
|
35
|
-
| Some(afterPos) =>
|
|
113
|
+
| Some(afterPos) => ev.position->posToInt > afterPos->posToInt
|
|
36
114
|
| None => true
|
|
37
115
|
}
|
|
38
|
-
afterMatch && matchesQuery(
|
|
39
|
-
}
|
|
116
|
+
afterMatch && matchesQuery(ev, query)
|
|
117
|
+
}
|
|
118
|
+
if query->Array.length == 0 {
|
|
119
|
+
events.contents->Array.filter(pass)
|
|
120
|
+
} else {
|
|
121
|
+
candidateIndices(query)->Array.filterMap(i => {
|
|
122
|
+
let ev = events.contents->Array.getUnsafe(i)
|
|
123
|
+
pass(ev) ? Some(ev) : None
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let read = async (~query, ~after=?) => {
|
|
129
|
+
let filtered = matchingEvents(~query, ~after)
|
|
40
130
|
let headPosition =
|
|
41
131
|
events.contents
|
|
42
132
|
->Array.get(events.contents->Array.length - 1)
|
|
@@ -50,30 +140,27 @@ let makeStorage = (~name as _name, ~indexes as _, ~partitionTag as _, ~opts as _
|
|
|
50
140
|
let append = async (newEvents, ~condition=?) => {
|
|
51
141
|
let conflictDetected = switch condition {
|
|
52
142
|
| Some(cond: Reventless.DcbTag.appendCondition) =>
|
|
53
|
-
|
|
54
|
-
let afterMatch = switch cond.after {
|
|
55
|
-
| Some(pos) => event.position->posToInt > pos->posToInt
|
|
56
|
-
| None => true
|
|
57
|
-
}
|
|
58
|
-
afterMatch && matchesQuery(event, cond.query)
|
|
59
|
-
})
|
|
143
|
+
matchingEvents(~query=cond.query, ~after=cond.after)->Array.length > 0
|
|
60
144
|
| None => false
|
|
61
145
|
}
|
|
62
146
|
if conflictDetected {
|
|
63
147
|
Error("conflict: condition check failed")
|
|
64
148
|
} else {
|
|
149
|
+
let base = events.contents->Array.length
|
|
65
150
|
let storedEvents =
|
|
66
|
-
newEvents->Array.
|
|
151
|
+
newEvents->Array.mapWithIndex((event: DcbEventLog_Adapter.rawStoredEvent, j) => {
|
|
67
152
|
position := position.contents + 1
|
|
68
153
|
let pos = position.contents->Int.toString
|
|
69
|
-
{
|
|
70
|
-
|
|
154
|
+
let stored: DcbEventLog_Adapter.rawSequencedEvent = {
|
|
155
|
+
position: pos,
|
|
71
156
|
eventType: event.eventType,
|
|
72
157
|
data: event.data,
|
|
73
158
|
tags: event.tags,
|
|
74
159
|
meta: event.meta,
|
|
75
160
|
recordedAt: Message.nowAsISOString(),
|
|
76
161
|
}
|
|
162
|
+
indexEvent(base + j, stored)
|
|
163
|
+
stored
|
|
77
164
|
})
|
|
78
165
|
events := events.contents->Array.concat(storedEvents)
|
|
79
166
|
Ok(position.contents->Int.toString)
|
|
@@ -113,8 +200,6 @@ module Make = (Bus: LocalBus.T) => {
|
|
|
113
200
|
| Some(db) =>
|
|
114
201
|
let (storageName, read, storage) = DcbEventLogStorage_Sqlite.makeStorage(
|
|
115
202
|
~db,
|
|
116
|
-
~bus=(_, _) => (),
|
|
117
|
-
~publishToTopic=(~topicName as _, ~json as _) => (),
|
|
118
203
|
~name,
|
|
119
204
|
~indexes,
|
|
120
205
|
~partitionTag,
|