@reventlessdev/reventless-local 3.0.0-alpha.113 → 3.0.0-alpha.115
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 +21 -0
- package/package.json +8 -8
- package/src/Platform.res +25 -50
- package/src/Platform.res.mjs +33 -160
- package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res +10 -1
- package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res.mjs +9 -1
- package/src/adapter/EventLog/EventLogStorage_InMemory.res +1 -1
- package/src/adapter/EventLog/EventLogStorage_InMemory.res.mjs +1 -1
- package/src/adapter/EventLog/EventLogStorage_Sqlite.res +16 -3
- package/src/adapter/EventLog/EventLogStorage_Sqlite.res.mjs +35 -7
- package/src/adapter/LocalBus.res +23 -3
- package/src/adapter/LocalBus.res.mjs +100 -66
- package/src/adapter/QueryDb/QueryDbStorage_InMemory.res +99 -7
- package/src/adapter/QueryDb/QueryDbStorage_InMemory.res.mjs +97 -13
- package/src/adapter/QueryDb/QueryDbStorage_Sqlite.res +24 -1
- package/src/adapter/QueryDb/QueryDbStorage_Sqlite.res.mjs +19 -4
- package/src/adapter/SqliteDriver.res +6 -1
- package/src/adapter/SqliteDriver.res.mjs +5 -1
- package/src/components/DcbEventLog_Builder.res +7 -1
- package/src/components/DcbEventLog_Builder.res.mjs +3 -3
- package/src/test/Mocks/MockEventLogStorage.res +1 -1
- package/src/test/Mocks/MockEventLogStorage.res.mjs +4 -1
- package/tests/adapter/BackendParityTest.res +64 -0
- package/tests/adapter/BackendParityTest.res.mjs +45 -0
- package/tests/adapter/EventLogStorageSqliteTest.res +4 -2
- package/tests/adapter/EventLogStorageSqliteTest.res.mjs +6 -1
- package/tests/adapter/LocalBusPubSubTest.res +31 -0
- package/tests/adapter/LocalBusPubSubTest.res.mjs +28 -0
- package/tests/components/inboundtranslationslice/InboundTranslationSliceCallbackTest.res +2 -0
- package/tests/components/inboundtranslationslice/InboundTranslationSliceCallbackTest.res.mjs +3 -0
- package/tests/components/inboundtranslationslice/InboundTranslationSliceFixtures.res +1 -0
- package/tests/components/inboundtranslationslice/InboundTranslationSliceFixtures.res.mjs +1 -0
- package/tests/components/outboundtranslationslice/OutboundTranslationSliceCallbackTest.res.mjs +4 -2
- package/tests/components/outboundtranslationslice/OutboundTranslationSliceFixtures.res +2 -0
- package/tests/components/outboundtranslationslice/OutboundTranslationSliceFixtures.res.mjs +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,27 @@
|
|
|
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.115 (2026-07-02)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **core,local:** projection/publisher/bus correctness (plan A5,A7) ([86e27a5](https://github.com/ReventlessDev/reventless-core/commit/86e27a5d68873d7dd9e746f9b4126f4d5bad63c9))
|
|
11
|
+
* **local:** conflict classification, rollback masking, MCP pagination (plan A6) ([b3e6d24](https://github.com/ReventlessDev/reventless-core/commit/b3e6d242ddf2df066ca94b0db2266275f1453fbf))
|
|
12
|
+
* **local:** InMemory QueryDb TTL parity + backend-aware UIFragment store (plan A6) ([c31727f](https://github.com/ReventlessDev/reventless-core/commit/c31727fa3fe98b4f15b55300fdf8b80221fabbee))
|
|
13
|
+
* **local:** QueryDb count returns a running total, not the increment (plan A6) ([4ac3822](https://github.com/ReventlessDev/reventless-core/commit/4ac3822ac310d62bb4b1c4c021a85f45883f4785))
|
|
14
|
+
* **local:** wire standalone DCB log through the backend-aware storage (plan A6) ([40734e9](https://github.com/ReventlessDev/reventless-core/commit/40734e902436dfce623061416e9edeafa822f1d3))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# 3.0.0-alpha.114 (2026-06-29)
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* **test:** set externalSystem on translation slice fixtures for published PPX ([19bbb2f](https://github.com/ReventlessDev/reventless-core/commit/19bbb2fa7f4606c0ff00aa9b403ebf74415d58c0))
|
|
22
|
+
### Features
|
|
23
|
+
|
|
24
|
+
* external-system boxes for translation slices (Event Graph data) ([3f8ad39](https://github.com/ReventlessDev/reventless-core/commit/3f8ad39b78a3cb1182d59a0e1fb203b7dcb7379b))
|
|
25
|
+
|
|
26
|
+
|
|
6
27
|
# 3.0.0-alpha.113 (2026-06-27)
|
|
7
28
|
|
|
8
29
|
### 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.115",
|
|
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
|
"jest": {
|
|
@@ -31,20 +31,20 @@
|
|
|
31
31
|
"graphql-yoga": "^5.21.0",
|
|
32
32
|
"sury": "11.0.0-alpha.4",
|
|
33
33
|
"ws": "^8.18.0",
|
|
34
|
-
"@reventlessdev/rescript-effect": "0.1.0-alpha.
|
|
34
|
+
"@reventlessdev/rescript-effect": "0.1.0-alpha.24",
|
|
35
35
|
"@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.17",
|
|
36
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.
|
|
36
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.5",
|
|
37
37
|
"@reventlessdev/rescript-mcp-sdk": "1.0.0-alpha.16",
|
|
38
38
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.14",
|
|
39
|
-
"@reventlessdev/reventless-core": "3.0.0-alpha.
|
|
40
|
-
"@reventlessdev/reventless-gwt": "1.0.0-alpha.
|
|
41
|
-
"@reventlessdev/reventless-infra": "3.0.0-alpha.
|
|
42
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.
|
|
39
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.136",
|
|
40
|
+
"@reventlessdev/reventless-gwt": "1.0.0-alpha.76",
|
|
41
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.83",
|
|
42
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.63"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"rescript": "^12.3.0",
|
|
46
46
|
"sury-ppx": "11.0.0-alpha.2",
|
|
47
|
-
"@reventlessdev/reventless-ppx": "1.0.0-alpha.
|
|
47
|
+
"@reventlessdev/reventless-ppx": "1.0.0-alpha.48"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"rescript": "^12.3.0"
|
package/src/Platform.res
CHANGED
|
@@ -279,10 +279,18 @@ module MakeWithConfig = (
|
|
|
279
279
|
])->JSON.Encode.object
|
|
280
280
|
|
|
281
281
|
let paginate = (events: array<JSON.t>, getPosition: JSON.t => option<string>) => {
|
|
282
|
+
// Positions are numeric strings; compare them as ints, not lexically
|
|
283
|
+
// ("10" > "9" is false as strings) — otherwise cursor pages skip or
|
|
284
|
+
// duplicate events once positions cross a digit boundary.
|
|
285
|
+
let positionGt = (p, afterPos) =>
|
|
286
|
+
switch (Int.fromString(p), Int.fromString(afterPos)) {
|
|
287
|
+
| (Some(pi), Some(ai)) => pi > ai
|
|
288
|
+
| _ => p > afterPos
|
|
289
|
+
}
|
|
282
290
|
let filtered = switch after {
|
|
283
291
|
| Some(afterPos) =>
|
|
284
292
|
let idx =
|
|
285
|
-
events->Array.findIndex(e => getPosition(e)->Option.mapOr(false, p => p
|
|
293
|
+
events->Array.findIndex(e => getPosition(e)->Option.mapOr(false, p => positionGt(p, afterPos)))
|
|
286
294
|
if idx >= 0 {
|
|
287
295
|
events->Array.slice(~start=idx, ~end=events->Array.length)
|
|
288
296
|
} else {
|
|
@@ -749,60 +757,27 @@ module MakeWithConfig = (
|
|
|
749
757
|
// UIFragmentRegistry QueryDb store ops — initialized lazily on first seed call.
|
|
750
758
|
let uiFragmentQueryDbOpsRef: ref<option<ReventlessCore.QueryDb_Adapter.operations>> = ref(None)
|
|
751
759
|
|
|
760
|
+
// Backend-aware storage for the UIFragment registry — the same functor every
|
|
761
|
+
// other read model uses. Replaces a hand-rolled memory-only store that ignored
|
|
762
|
+
// BackendState, so the registry now persists under SQLite too.
|
|
763
|
+
module UIFragmentStorage = QueryDbStorage_InMemory.Make(Bus)
|
|
764
|
+
|
|
752
765
|
let ensureUIFragmentRegistryQueryDbStore = () => {
|
|
753
766
|
switch uiFragmentQueryDbOpsRef.contents {
|
|
754
767
|
| Some(ops) => ops
|
|
755
768
|
| None =>
|
|
756
769
|
let name = ReventlessCore.UIFragmentRegistryReadModelSpec.name
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
obj->Dict.toArray->Array.forEach(((k, v)) => copy->Dict.set(k, v))
|
|
769
|
-
copy->Dict.set("id", JSON.Encode.string(id))
|
|
770
|
-
JSON.Encode.object(copy)
|
|
771
|
-
} else {
|
|
772
|
-
item
|
|
773
|
-
}
|
|
774
|
-
})
|
|
775
|
-
)
|
|
776
|
-
}
|
|
777
|
-
let ops: ReventlessCore.QueryDb_Adapter.operations = {
|
|
778
|
-
load: async id => Ok(store.contents->Dict.get(id)->Option.getOr([])),
|
|
779
|
-
loadStream: id =>
|
|
780
|
-
store.contents->Dict.get(id)->Option.getOr([])->Stream.fromIterable,
|
|
781
|
-
save: async (id, state, _, _) => {
|
|
782
|
-
store.contents->Dict.set(id, [state])
|
|
783
|
-
syncAll()
|
|
784
|
-
Ok()
|
|
785
|
-
},
|
|
786
|
-
saveBatch: async batch => {
|
|
787
|
-
batch->Array.forEach(((id, state, _)) => store.contents->Dict.set(id, [state]))
|
|
788
|
-
syncAll()
|
|
789
|
-
Ok()
|
|
790
|
-
},
|
|
791
|
-
count: async (_, _, inc) => Ok(inc),
|
|
792
|
-
delete: async (id, _) => {
|
|
793
|
-
store.contents->Dict.delete(id)
|
|
794
|
-
syncAll()
|
|
795
|
-
Ok()
|
|
796
|
-
},
|
|
797
|
-
deleteBatch: async ids => {
|
|
798
|
-
ids->Array.forEach(((id, _)) => store.contents->Dict.delete(id))
|
|
799
|
-
syncAll()
|
|
800
|
-
Ok()
|
|
801
|
-
},
|
|
802
|
-
}
|
|
803
|
-
Bus.registerQueryDb(name, ops)
|
|
804
|
-
Bus.registerQueryDbScan(name, () => allItems.contents)
|
|
805
|
-
Bus.registerQueryDbStream(name, () => allItems.contents->Stream.fromIterable)
|
|
770
|
+
// `make` registers the ops (and scan/stream) with the Bus for the active
|
|
771
|
+
// backend; retrieve them synchronously via getQueryDb rather than resolving
|
|
772
|
+
// the Pulumi Output.
|
|
773
|
+
let _ = UIFragmentStorage.make(
|
|
774
|
+
~name,
|
|
775
|
+
~indexes=[],
|
|
776
|
+
~api=(),
|
|
777
|
+
~apiRole=(),
|
|
778
|
+
~opts=({}: Pulumi.CustomResourceOptions.t),
|
|
779
|
+
)
|
|
780
|
+
let ops = Bus.getQueryDb(name)->Option.getOrThrow
|
|
806
781
|
uiFragmentQueryDbOpsRef := Some(ops)
|
|
807
782
|
ops
|
|
808
783
|
}
|
package/src/Platform.res.mjs
CHANGED
|
@@ -4,14 +4,12 @@ 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";
|
|
16
14
|
import * as Plugin$Reventless from "@reventlessdev/reventless-spec/src/components/Plugin.res.mjs";
|
|
17
15
|
import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
|
|
@@ -63,6 +61,7 @@ import * as AutomationSlice_Builder$ReventlessLocal from "./components/Automatio
|
|
|
63
61
|
import * as LocalRuntimeEnvironment$ReventlessLocal from "./adapter/Runtime/LocalRuntimeEnvironment.res.mjs";
|
|
64
62
|
import * as LocalScheduledPublisher$ReventlessLocal from "./adapter/Scheduler/LocalScheduledPublisher.res.mjs";
|
|
65
63
|
import * as Platform_Admin_Structure$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_Admin_Structure.res.mjs";
|
|
64
|
+
import * as QueryDbStorage_InMemory$ReventlessLocal from "./adapter/QueryDb/QueryDbStorage_InMemory.res.mjs";
|
|
66
65
|
import * as LocalCommandTopicChannel$ReventlessLocal from "./adapter/CommandTopic/LocalCommandTopicChannel.res.mjs";
|
|
67
66
|
import * as LocalEventTopicPublisher$ReventlessLocal from "./adapter/EventTopic/LocalEventTopicPublisher.res.mjs";
|
|
68
67
|
import * as StateChangeSlice_Builder$ReventlessLocal from "./components/StateChangeSlice_Builder.res.mjs";
|
|
@@ -268,7 +267,15 @@ function MakeWithConfig(Config) {
|
|
|
268
267
|
let paginate = (events, getPosition) => {
|
|
269
268
|
let filtered;
|
|
270
269
|
if (after !== undefined) {
|
|
271
|
-
let idx = events.findIndex(e => Stdlib_Option.mapOr(getPosition(e), false, p =>
|
|
270
|
+
let idx = events.findIndex(e => Stdlib_Option.mapOr(getPosition(e), false, p => {
|
|
271
|
+
let match = Stdlib_Int.fromString(p, undefined);
|
|
272
|
+
let match$1 = Stdlib_Int.fromString(after, undefined);
|
|
273
|
+
if (match !== undefined && match$1 !== undefined) {
|
|
274
|
+
return match > match$1;
|
|
275
|
+
} else {
|
|
276
|
+
return p > after;
|
|
277
|
+
}
|
|
278
|
+
}));
|
|
272
279
|
filtered = idx >= 0 ? events.slice(idx, events.length) : [];
|
|
273
280
|
} else {
|
|
274
281
|
filtered = events;
|
|
@@ -424,6 +431,7 @@ function MakeWithConfig(Config) {
|
|
|
424
431
|
return {
|
|
425
432
|
Spec: $$let.Spec,
|
|
426
433
|
sourceNames: $$let.sourceNames,
|
|
434
|
+
consumedEventNames: $$let.consumedEventNames,
|
|
427
435
|
make: $$let.make,
|
|
428
436
|
outputs: $$let.outputs,
|
|
429
437
|
operations: $$let.operations,
|
|
@@ -438,6 +446,7 @@ function MakeWithConfig(Config) {
|
|
|
438
446
|
return {
|
|
439
447
|
Spec: $$let.Spec,
|
|
440
448
|
sourceNames: $$let.sourceNames,
|
|
449
|
+
consumedEventNames: $$let.consumedEventNames,
|
|
441
450
|
make: $$let.make,
|
|
442
451
|
outputs: $$let.outputs,
|
|
443
452
|
operations: $$let.operations,
|
|
@@ -702,89 +711,14 @@ function MakeWithConfig(Config) {
|
|
|
702
711
|
let uiFragmentQueryDbOpsRef = {
|
|
703
712
|
contents: undefined
|
|
704
713
|
};
|
|
714
|
+
let UIFragmentStorage = QueryDbStorage_InMemory$ReventlessLocal.Make(Bus);
|
|
705
715
|
let ensureUIFragmentRegistryQueryDbStore = () => {
|
|
706
716
|
let ops = uiFragmentQueryDbOpsRef.contents;
|
|
707
717
|
if (ops !== undefined) {
|
|
708
718
|
return ops;
|
|
709
719
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
};
|
|
713
|
-
let allItems = {
|
|
714
|
-
contents: []
|
|
715
|
-
};
|
|
716
|
-
let syncAll = () => {
|
|
717
|
-
allItems.contents = Object.entries(store.contents).flatMap(param => {
|
|
718
|
-
let id = param[0];
|
|
719
|
-
return param[1].map(item => {
|
|
720
|
-
let obj = Stdlib_Option.getOr(Stdlib_JSON.Decode.object(item), {});
|
|
721
|
-
if (Stdlib_Option.isSome(obj["id"])) {
|
|
722
|
-
return item;
|
|
723
|
-
}
|
|
724
|
-
let copy = {};
|
|
725
|
-
Object.entries(obj).forEach(param => {
|
|
726
|
-
copy[param[0]] = param[1];
|
|
727
|
-
});
|
|
728
|
-
copy["id"] = id;
|
|
729
|
-
return copy;
|
|
730
|
-
});
|
|
731
|
-
});
|
|
732
|
-
};
|
|
733
|
-
let ops_load = async id => ({
|
|
734
|
-
TAG: "Ok",
|
|
735
|
-
_0: Stdlib_Option.getOr(store.contents[id], [])
|
|
736
|
-
});
|
|
737
|
-
let ops_loadStream = id => Stream$1.fromIterable(Stdlib_Option.getOr(store.contents[id], []));
|
|
738
|
-
let ops_save = async (id, state, param, param$1) => {
|
|
739
|
-
store.contents[id] = [state];
|
|
740
|
-
syncAll();
|
|
741
|
-
return {
|
|
742
|
-
TAG: "Ok",
|
|
743
|
-
_0: undefined
|
|
744
|
-
};
|
|
745
|
-
};
|
|
746
|
-
let ops_saveBatch = async batch => {
|
|
747
|
-
batch.forEach(param => {
|
|
748
|
-
store.contents[param[0]] = [param[1]];
|
|
749
|
-
});
|
|
750
|
-
syncAll();
|
|
751
|
-
return {
|
|
752
|
-
TAG: "Ok",
|
|
753
|
-
_0: undefined
|
|
754
|
-
};
|
|
755
|
-
};
|
|
756
|
-
let ops_count = async (param, param$1, inc) => ({
|
|
757
|
-
TAG: "Ok",
|
|
758
|
-
_0: inc
|
|
759
|
-
});
|
|
760
|
-
let ops_delete = async (id, param) => {
|
|
761
|
-
Stdlib_Dict.$$delete(store.contents, id);
|
|
762
|
-
syncAll();
|
|
763
|
-
return {
|
|
764
|
-
TAG: "Ok",
|
|
765
|
-
_0: undefined
|
|
766
|
-
};
|
|
767
|
-
};
|
|
768
|
-
let ops_deleteBatch = async ids => {
|
|
769
|
-
ids.forEach(param => Stdlib_Dict.$$delete(store.contents, param[0]));
|
|
770
|
-
syncAll();
|
|
771
|
-
return {
|
|
772
|
-
TAG: "Ok",
|
|
773
|
-
_0: undefined
|
|
774
|
-
};
|
|
775
|
-
};
|
|
776
|
-
let ops$1 = {
|
|
777
|
-
load: ops_load,
|
|
778
|
-
loadStream: ops_loadStream,
|
|
779
|
-
save: ops_save,
|
|
780
|
-
saveBatch: ops_saveBatch,
|
|
781
|
-
count: ops_count,
|
|
782
|
-
delete: ops_delete,
|
|
783
|
-
deleteBatch: ops_deleteBatch
|
|
784
|
-
};
|
|
785
|
-
Bus.registerQueryDb(UIFragmentRegistryReadModelSpec$ReventlessCore.name, ops$1);
|
|
786
|
-
Bus.registerQueryDbScan(UIFragmentRegistryReadModelSpec$ReventlessCore.name, () => allItems.contents);
|
|
787
|
-
Bus.registerQueryDbStream(UIFragmentRegistryReadModelSpec$ReventlessCore.name, () => Stream$1.fromIterable(allItems.contents));
|
|
720
|
+
UIFragmentStorage.make(UIFragmentRegistryReadModelSpec$ReventlessCore.name, [], undefined, undefined, undefined, undefined, {});
|
|
721
|
+
let ops$1 = Stdlib_Option.getOrThrow(Bus.getQueryDb(UIFragmentRegistryReadModelSpec$ReventlessCore.name), undefined);
|
|
788
722
|
uiFragmentQueryDbOpsRef.contents = ops$1;
|
|
789
723
|
return ops$1;
|
|
790
724
|
};
|
|
@@ -1158,6 +1092,7 @@ function MakeWithConfig(Config) {
|
|
|
1158
1092
|
let admin = Admin.construct(version, [], [LocalPluginAggregate], [{
|
|
1159
1093
|
Spec: PluginsReadModel.Spec,
|
|
1160
1094
|
sourceNames: PluginsReadModel.sourceNames,
|
|
1095
|
+
consumedEventNames: PluginsReadModel.consumedEventNames,
|
|
1161
1096
|
make: PluginsReadModel.make,
|
|
1162
1097
|
outputs: PluginsReadModel.outputs,
|
|
1163
1098
|
operations: PluginsReadModel.operations,
|
|
@@ -1614,6 +1549,7 @@ function MakeWithConfig(Config) {
|
|
|
1614
1549
|
let admin = Admin.construct("", [], [LocalPluginAggregate], [{
|
|
1615
1550
|
Spec: PluginsReadModel.Spec,
|
|
1616
1551
|
sourceNames: PluginsReadModel.sourceNames,
|
|
1552
|
+
consumedEventNames: PluginsReadModel.consumedEventNames,
|
|
1617
1553
|
make: PluginsReadModel.make,
|
|
1618
1554
|
outputs: PluginsReadModel.outputs,
|
|
1619
1555
|
operations: PluginsReadModel.operations,
|
|
@@ -1917,7 +1853,15 @@ function Make($star) {
|
|
|
1917
1853
|
let paginate = (events, getPosition) => {
|
|
1918
1854
|
let filtered;
|
|
1919
1855
|
if (after !== undefined) {
|
|
1920
|
-
let idx = events.findIndex(e => Stdlib_Option.mapOr(getPosition(e), false, p =>
|
|
1856
|
+
let idx = events.findIndex(e => Stdlib_Option.mapOr(getPosition(e), false, p => {
|
|
1857
|
+
let match = Stdlib_Int.fromString(p, undefined);
|
|
1858
|
+
let match$1 = Stdlib_Int.fromString(after, undefined);
|
|
1859
|
+
if (match !== undefined && match$1 !== undefined) {
|
|
1860
|
+
return match > match$1;
|
|
1861
|
+
} else {
|
|
1862
|
+
return p > after;
|
|
1863
|
+
}
|
|
1864
|
+
}));
|
|
1921
1865
|
filtered = idx >= 0 ? events.slice(idx, events.length) : [];
|
|
1922
1866
|
} else {
|
|
1923
1867
|
filtered = events;
|
|
@@ -2073,6 +2017,7 @@ function Make($star) {
|
|
|
2073
2017
|
return {
|
|
2074
2018
|
Spec: $$let.Spec,
|
|
2075
2019
|
sourceNames: $$let.sourceNames,
|
|
2020
|
+
consumedEventNames: $$let.consumedEventNames,
|
|
2076
2021
|
make: $$let.make,
|
|
2077
2022
|
outputs: $$let.outputs,
|
|
2078
2023
|
operations: $$let.operations,
|
|
@@ -2087,6 +2032,7 @@ function Make($star) {
|
|
|
2087
2032
|
return {
|
|
2088
2033
|
Spec: $$let.Spec,
|
|
2089
2034
|
sourceNames: $$let.sourceNames,
|
|
2035
|
+
consumedEventNames: $$let.consumedEventNames,
|
|
2090
2036
|
make: $$let.make,
|
|
2091
2037
|
outputs: $$let.outputs,
|
|
2092
2038
|
operations: $$let.operations,
|
|
@@ -2350,89 +2296,14 @@ function Make($star) {
|
|
|
2350
2296
|
let uiFragmentQueryDbOpsRef = {
|
|
2351
2297
|
contents: undefined
|
|
2352
2298
|
};
|
|
2299
|
+
let UIFragmentStorage = QueryDbStorage_InMemory$ReventlessLocal.Make(Bus);
|
|
2353
2300
|
let ensureUIFragmentRegistryQueryDbStore = () => {
|
|
2354
2301
|
let ops = uiFragmentQueryDbOpsRef.contents;
|
|
2355
2302
|
if (ops !== undefined) {
|
|
2356
2303
|
return ops;
|
|
2357
2304
|
}
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
};
|
|
2361
|
-
let allItems = {
|
|
2362
|
-
contents: []
|
|
2363
|
-
};
|
|
2364
|
-
let syncAll = () => {
|
|
2365
|
-
allItems.contents = Object.entries(store.contents).flatMap(param => {
|
|
2366
|
-
let id = param[0];
|
|
2367
|
-
return param[1].map(item => {
|
|
2368
|
-
let obj = Stdlib_Option.getOr(Stdlib_JSON.Decode.object(item), {});
|
|
2369
|
-
if (Stdlib_Option.isSome(obj["id"])) {
|
|
2370
|
-
return item;
|
|
2371
|
-
}
|
|
2372
|
-
let copy = {};
|
|
2373
|
-
Object.entries(obj).forEach(param => {
|
|
2374
|
-
copy[param[0]] = param[1];
|
|
2375
|
-
});
|
|
2376
|
-
copy["id"] = id;
|
|
2377
|
-
return copy;
|
|
2378
|
-
});
|
|
2379
|
-
});
|
|
2380
|
-
};
|
|
2381
|
-
let ops_load = async id => ({
|
|
2382
|
-
TAG: "Ok",
|
|
2383
|
-
_0: Stdlib_Option.getOr(store.contents[id], [])
|
|
2384
|
-
});
|
|
2385
|
-
let ops_loadStream = id => Stream$1.fromIterable(Stdlib_Option.getOr(store.contents[id], []));
|
|
2386
|
-
let ops_save = async (id, state, param, param$1) => {
|
|
2387
|
-
store.contents[id] = [state];
|
|
2388
|
-
syncAll();
|
|
2389
|
-
return {
|
|
2390
|
-
TAG: "Ok",
|
|
2391
|
-
_0: undefined
|
|
2392
|
-
};
|
|
2393
|
-
};
|
|
2394
|
-
let ops_saveBatch = async batch => {
|
|
2395
|
-
batch.forEach(param => {
|
|
2396
|
-
store.contents[param[0]] = [param[1]];
|
|
2397
|
-
});
|
|
2398
|
-
syncAll();
|
|
2399
|
-
return {
|
|
2400
|
-
TAG: "Ok",
|
|
2401
|
-
_0: undefined
|
|
2402
|
-
};
|
|
2403
|
-
};
|
|
2404
|
-
let ops_count = async (param, param$1, inc) => ({
|
|
2405
|
-
TAG: "Ok",
|
|
2406
|
-
_0: inc
|
|
2407
|
-
});
|
|
2408
|
-
let ops_delete = async (id, param) => {
|
|
2409
|
-
Stdlib_Dict.$$delete(store.contents, id);
|
|
2410
|
-
syncAll();
|
|
2411
|
-
return {
|
|
2412
|
-
TAG: "Ok",
|
|
2413
|
-
_0: undefined
|
|
2414
|
-
};
|
|
2415
|
-
};
|
|
2416
|
-
let ops_deleteBatch = async ids => {
|
|
2417
|
-
ids.forEach(param => Stdlib_Dict.$$delete(store.contents, param[0]));
|
|
2418
|
-
syncAll();
|
|
2419
|
-
return {
|
|
2420
|
-
TAG: "Ok",
|
|
2421
|
-
_0: undefined
|
|
2422
|
-
};
|
|
2423
|
-
};
|
|
2424
|
-
let ops$1 = {
|
|
2425
|
-
load: ops_load,
|
|
2426
|
-
loadStream: ops_loadStream,
|
|
2427
|
-
save: ops_save,
|
|
2428
|
-
saveBatch: ops_saveBatch,
|
|
2429
|
-
count: ops_count,
|
|
2430
|
-
delete: ops_delete,
|
|
2431
|
-
deleteBatch: ops_deleteBatch
|
|
2432
|
-
};
|
|
2433
|
-
Bus.registerQueryDb(UIFragmentRegistryReadModelSpec$ReventlessCore.name, ops$1);
|
|
2434
|
-
Bus.registerQueryDbScan(UIFragmentRegistryReadModelSpec$ReventlessCore.name, () => allItems.contents);
|
|
2435
|
-
Bus.registerQueryDbStream(UIFragmentRegistryReadModelSpec$ReventlessCore.name, () => Stream$1.fromIterable(allItems.contents));
|
|
2305
|
+
UIFragmentStorage.make(UIFragmentRegistryReadModelSpec$ReventlessCore.name, [], undefined, undefined, undefined, undefined, {});
|
|
2306
|
+
let ops$1 = Stdlib_Option.getOrThrow(Bus.getQueryDb(UIFragmentRegistryReadModelSpec$ReventlessCore.name), undefined);
|
|
2436
2307
|
uiFragmentQueryDbOpsRef.contents = ops$1;
|
|
2437
2308
|
return ops$1;
|
|
2438
2309
|
};
|
|
@@ -2801,6 +2672,7 @@ function Make($star) {
|
|
|
2801
2672
|
let admin = Admin.construct(version, [], [LocalPluginAggregate], [{
|
|
2802
2673
|
Spec: PluginsReadModel.Spec,
|
|
2803
2674
|
sourceNames: PluginsReadModel.sourceNames,
|
|
2675
|
+
consumedEventNames: PluginsReadModel.consumedEventNames,
|
|
2804
2676
|
make: PluginsReadModel.make,
|
|
2805
2677
|
outputs: PluginsReadModel.outputs,
|
|
2806
2678
|
operations: PluginsReadModel.operations,
|
|
@@ -3248,6 +3120,7 @@ function Make($star) {
|
|
|
3248
3120
|
let admin = Admin.construct("", [], [LocalPluginAggregate], [{
|
|
3249
3121
|
Spec: PluginsReadModel.Spec,
|
|
3250
3122
|
sourceNames: PluginsReadModel.sourceNames,
|
|
3123
|
+
consumedEventNames: PluginsReadModel.consumedEventNames,
|
|
3251
3124
|
make: PluginsReadModel.make,
|
|
3252
3125
|
outputs: PluginsReadModel.outputs,
|
|
3253
3126
|
operations: PluginsReadModel.operations,
|
|
@@ -306,7 +306,16 @@ let makeStorage = (
|
|
|
306
306
|
result.contents
|
|
307
307
|
} catch {
|
|
308
308
|
| Failure(msg) => Error(msg)
|
|
309
|
-
|
|
|
309
|
+
| exn =>
|
|
310
|
+
// The deliberate append-condition failure above throws
|
|
311
|
+
// Failure("conflict: …"); a lost race on the computed position shows up here
|
|
312
|
+
// as a PRIMARY KEY / UNIQUE violation — also a genuine conflict. Every other
|
|
313
|
+
// failure (disk full, SQL error, locked db) must surface as a real error,
|
|
314
|
+
// not the retryable "conflict" sentinel that would be retried forever.
|
|
315
|
+
let msg = exn->JsExn.fromException->Option.flatMap(JsExn.message)->Option.getOr("")
|
|
316
|
+
msg->String.includes("constraint failed")
|
|
317
|
+
? Error("conflict")
|
|
318
|
+
: Error(msg == "" ? "storage error" : msg)
|
|
310
319
|
}
|
|
311
320
|
}
|
|
312
321
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as S from "sury/src/S.res.mjs";
|
|
4
4
|
import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
|
|
5
|
+
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
5
6
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
7
|
import * as Effect from "effect/Effect";
|
|
7
8
|
import * as Stream from "effect/Stream";
|
|
@@ -262,11 +263,18 @@ function makeStorage(db, bus, publishToTopic, name, param, param$1, param$2) {
|
|
|
262
263
|
TAG: "Error",
|
|
263
264
|
_0: msg._1
|
|
264
265
|
};
|
|
265
|
-
}
|
|
266
|
+
}
|
|
267
|
+
let msg$1 = Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_JsExn.fromException(msg), Stdlib_JsExn.message), "");
|
|
268
|
+
if (msg$1.includes("constraint failed")) {
|
|
266
269
|
return {
|
|
267
270
|
TAG: "Error",
|
|
268
271
|
_0: "conflict"
|
|
269
272
|
};
|
|
273
|
+
} else {
|
|
274
|
+
return {
|
|
275
|
+
TAG: "Error",
|
|
276
|
+
_0: msg$1 === "" ? "storage error" : msg$1
|
|
277
|
+
};
|
|
270
278
|
}
|
|
271
279
|
}
|
|
272
280
|
};
|
|
@@ -17,7 +17,7 @@ let makeMemoryStorage = (~name as _name, ~opts as _) => {
|
|
|
17
17
|
let existing = events->Dict.get(id)->Option.getOr([])
|
|
18
18
|
let currentCount = existing->Array.length
|
|
19
19
|
if seqNr != currentCount {
|
|
20
|
-
(Error(
|
|
20
|
+
(Error(ReventlessCore.EventLog.Conflict), events)
|
|
21
21
|
} else {
|
|
22
22
|
events->Dict.set(id, existing->Array.concat(jsons))
|
|
23
23
|
(Ok(), events)
|
|
@@ -88,8 +88,19 @@ let makeStorage = (~db: SqliteDriver.t, ~name: string, ~opts as _) => {
|
|
|
88
88
|
})
|
|
89
89
|
Ok()
|
|
90
90
|
} catch {
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
// The deliberate seq_nr check above throws Failure("conflict"); a lost race
|
|
92
|
+
// shows up as a PRIMARY KEY violation on (log_name, aggregate_id, seq_nr) —
|
|
93
|
+
// also a genuine OCC conflict. Every OTHER failure (disk full, SQL error,
|
|
94
|
+
// locked db) is a real StorageFailure, not the retryable Conflict sentinel.
|
|
95
|
+
| Failure(msg) =>
|
|
96
|
+
msg == "conflict"
|
|
97
|
+
? Error(ReventlessCore.EventLog.Conflict)
|
|
98
|
+
: Error(ReventlessCore.EventLog.StorageFailure(msg))
|
|
99
|
+
| exn =>
|
|
100
|
+
let msg = exn->JsExn.fromException->Option.flatMap(JsExn.message)->Option.getOr("")
|
|
101
|
+
msg->String.includes("constraint failed")
|
|
102
|
+
? Error(ReventlessCore.EventLog.Conflict)
|
|
103
|
+
: Error(ReventlessCore.EventLog.StorageFailure(msg == "" ? "storage error" : msg))
|
|
93
104
|
}
|
|
94
105
|
}
|
|
95
106
|
|
|
@@ -115,7 +126,9 @@ let makeStorage = (~db: SqliteDriver.t, ~name: string, ~opts as _) => {
|
|
|
115
126
|
| Ok() =>
|
|
116
127
|
seqNrRef := currentSeq + 1
|
|
117
128
|
Effect.succeed()
|
|
118
|
-
|
|
129
|
+
// appendStream's error channel is a string; map the typed append error.
|
|
130
|
+
| Error(ReventlessCore.EventLog.Conflict) => Effect.fail("conflict")
|
|
131
|
+
| Error(StorageFailure(msg)) => Effect.fail(msg)
|
|
119
132
|
}
|
|
120
133
|
)
|
|
121
134
|
})
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
4
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
3
5
|
import * as Effect from "effect/Effect";
|
|
4
6
|
import * as Stream from "effect/Stream";
|
|
5
7
|
import * as Pulumi from "@pulumi/pulumi";
|
|
@@ -84,14 +86,35 @@ function makeStorage(db, name, param) {
|
|
|
84
86
|
} catch (raw_msg) {
|
|
85
87
|
let msg = Primitive_exceptions.internalToException(raw_msg);
|
|
86
88
|
if (msg.RE_EXN_ID === "Failure") {
|
|
89
|
+
let msg$1 = msg._1;
|
|
90
|
+
if (msg$1 === "conflict") {
|
|
91
|
+
return {
|
|
92
|
+
TAG: "Error",
|
|
93
|
+
_0: "Conflict"
|
|
94
|
+
};
|
|
95
|
+
} else {
|
|
96
|
+
return {
|
|
97
|
+
TAG: "Error",
|
|
98
|
+
_0: {
|
|
99
|
+
TAG: "StorageFailure",
|
|
100
|
+
_0: msg$1
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
let msg$2 = Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_JsExn.fromException(msg), Stdlib_JsExn.message), "");
|
|
106
|
+
if (msg$2.includes("constraint failed")) {
|
|
87
107
|
return {
|
|
88
108
|
TAG: "Error",
|
|
89
|
-
_0:
|
|
109
|
+
_0: "Conflict"
|
|
90
110
|
};
|
|
91
111
|
} else {
|
|
92
112
|
return {
|
|
93
113
|
TAG: "Error",
|
|
94
|
-
_0:
|
|
114
|
+
_0: {
|
|
115
|
+
TAG: "StorageFailure",
|
|
116
|
+
_0: msg$2 === "" ? "storage error" : msg$2
|
|
117
|
+
}
|
|
95
118
|
};
|
|
96
119
|
}
|
|
97
120
|
}
|
|
@@ -109,11 +132,16 @@ function makeStorage(db, name, param) {
|
|
|
109
132
|
return Stream.runForEach(stream, json => {
|
|
110
133
|
let currentSeq = seqNrRef.contents;
|
|
111
134
|
return Effect.flatMap(Effect.promise(() => append(currentSeq, id, [json])), result => {
|
|
112
|
-
if (result.TAG
|
|
113
|
-
|
|
135
|
+
if (result.TAG === "Ok") {
|
|
136
|
+
seqNrRef.contents = currentSeq + 1 | 0;
|
|
137
|
+
return Effect.succeed();
|
|
138
|
+
}
|
|
139
|
+
let msg = result._0;
|
|
140
|
+
if (typeof msg !== "object") {
|
|
141
|
+
return Effect.fail("conflict");
|
|
142
|
+
} else {
|
|
143
|
+
return Effect.fail(msg._0);
|
|
114
144
|
}
|
|
115
|
-
seqNrRef.contents = currentSeq + 1 | 0;
|
|
116
|
-
return Effect.succeed();
|
|
117
145
|
});
|
|
118
146
|
});
|
|
119
147
|
};
|
|
@@ -152,4 +180,4 @@ export {
|
|
|
152
180
|
makeStorage,
|
|
153
181
|
Make,
|
|
154
182
|
}
|
|
155
|
-
/*
|
|
183
|
+
/* Stdlib_JsExn Not a pure module */
|