@reventlessdev/reventless-local 3.0.0-alpha.248 → 3.0.0-alpha.250

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.
Files changed (170) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/package.json +14 -14
  3. package/src/BakedManifest.res +5 -6
  4. package/src/EmitCapabilities.res +3 -1
  5. package/src/LocalPlatformRegistry.res +3 -1
  6. package/src/LocalPlatformStart.res +7 -6
  7. package/src/LocalSeedTarget.res +3 -1
  8. package/src/Platform.res +291 -215
  9. package/src/ShellConfig.res +10 -17
  10. package/src/UiHints.res +8 -14
  11. package/src/UiSlots.res +10 -17
  12. package/src/adapter/Api/LocalEvents_Server.res +5 -3
  13. package/src/adapter/Api/LocalGraphQL_Adapter.res +4 -4
  14. package/src/adapter/Auth/LocalAuth.res +11 -15
  15. package/src/adapter/Auth/UserStore.res +5 -7
  16. package/src/adapter/Backend.res +1 -2
  17. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_GraphQL.res +42 -26
  18. package/src/adapter/CommandGenerator/InboundTranslationResolvers_GraphQL.res +13 -3
  19. package/src/adapter/CommandTopic/LocalCommandTopicChannel.res +15 -17
  20. package/src/adapter/CommandTopic/LocalCommandTopicRemoteChannel.res +18 -22
  21. package/src/adapter/Counter/LocalCounterHandler.res +1 -1
  22. package/src/adapter/DcbEventLog/DcbEventLogStorage_InMemory.res +33 -22
  23. package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res +57 -57
  24. package/src/adapter/DomainGraphQL_Server.res +35 -34
  25. package/src/adapter/DomainMCP_Server.res +45 -41
  26. package/src/adapter/EventCollector/LocalEventCollectorChannel.res +47 -36
  27. package/src/adapter/EventHistory/EventHistoryResolvers_GraphQL.res +15 -12
  28. package/src/adapter/EventLog/EventLogStorage_InMemory.res +9 -4
  29. package/src/adapter/EventLog/EventLogStorage_Sqlite.res +36 -24
  30. package/src/adapter/EventTopic/LocalEventTopicPublisher.res +9 -3
  31. package/src/adapter/Heartbeat/LocalHeartbeatRunner.res +9 -14
  32. package/src/adapter/LocalBus.res +12 -12
  33. package/src/adapter/LocalCapabilities.res.mjs +1 -1
  34. package/src/adapter/LocalGeocodeResolvers.res +1 -4
  35. package/src/adapter/LocalSideEffectHandler.res +1 -1
  36. package/src/adapter/LocalStateChangeDescriptor.res +2 -1
  37. package/src/adapter/MCP_ServerInstance.res +51 -39
  38. package/src/adapter/ObjectStore/LocalObjectStore.res +5 -2
  39. package/src/adapter/ObjectStore/ObjectStoreStorage_FileSystem.res +1 -2
  40. package/src/adapter/PlatformGraphQL_Server.res +9 -5
  41. package/src/adapter/ProjectionCheckpoint.res +8 -1
  42. package/src/adapter/ProjectionPending.res +1 -3
  43. package/src/adapter/QueryDb/LocalQueryDbStorage.res +2 -1
  44. package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res +367 -269
  45. package/src/adapter/QueryDb/QueryDbStorage_InMemory.res +5 -3
  46. package/src/adapter/QueryDb/QueryDbStorage_Sqlite.res +63 -40
  47. package/src/adapter/QueryDb/SortKey_Filter.res +14 -6
  48. package/src/adapter/Runtime/LocalAggregateRuntime_Builder.res +12 -10
  49. package/src/adapter/Runtime/LocalEventCollectorRuntime_Builder.res +8 -2
  50. package/src/adapter/Runtime/LocalPluginRuntime_Builder.res +1 -4
  51. package/src/adapter/Scheduler/LocalScheduledPublisher.res +12 -12
  52. package/src/adapter/SqliteDriver.res +16 -4
  53. package/src/adapter/Task/TaskBucket_Sqlite.res +5 -6
  54. package/src/components/Aggregate_Builder.res +6 -1
  55. package/src/components/Counter_Builder.res +1 -5
  56. package/src/components/DcbEventLog_Builder.res +5 -3
  57. package/src/components/ExtensionPoint_Builder.res +13 -15
  58. package/src/components/InboundTranslationSlice_Builder.res +3 -2
  59. package/src/components/OutboundTranslationSlice_Builder.res +3 -2
  60. package/src/components/ReadModel_Builder.res +18 -20
  61. package/src/components/StateViewSlice_Builder.res +3 -2
  62. package/src/components/Task_Builder.res +12 -13
  63. package/src/reset/LocalSeedReset.res +2 -5
  64. package/src/test/Mocks/MockDcbEventLogStorage.res +5 -1
  65. package/src/test/Mocks/MockEventLogStorage.res +8 -3
  66. package/src/test/Mocks/MockQueryDbStorage.res +3 -2
  67. package/src/test/TestRunner.res +1 -1
  68. package/tests/BakedManifestFilesTest.res +7 -10
  69. package/tests/LocalEventTapTest.res +26 -21
  70. package/tests/LocalPlatformStartTest.res +33 -28
  71. package/tests/PluginEventDecodeTest.res +3 -3
  72. package/tests/ShellConfigTest.res +30 -21
  73. package/tests/SplitApiFixtures.res +34 -10
  74. package/tests/SplitApiTest.res +92 -62
  75. package/tests/adapter/BackendParityTest.res +180 -130
  76. package/tests/adapter/CollectNEventsTest.res +6 -4
  77. package/tests/adapter/CommandAuthorizationTest.res +47 -41
  78. package/tests/adapter/CommandTopicChannelTest.res +163 -139
  79. package/tests/adapter/CounterHandlerTest.res +95 -77
  80. package/tests/adapter/DcbEventLogStorageSqliteTest.res +171 -168
  81. package/tests/adapter/DcbEventLogStorageTest.res +264 -207
  82. package/tests/adapter/DomainGraphQL_ServerTest.res +53 -43
  83. package/tests/adapter/EventCollectorChannelTest.res +153 -124
  84. package/tests/adapter/EventHistoryResolverTest.res +46 -18
  85. package/tests/adapter/EventLogSnapshotParityTest.res +57 -45
  86. package/tests/adapter/EventLogStorageSqliteTest.res +9 -10
  87. package/tests/adapter/EventTapTest.res +37 -20
  88. package/tests/adapter/EventTopicPublisherTest.res +74 -50
  89. package/tests/adapter/GraphQLSubscriptionBridgeTest.res +4 -3
  90. package/tests/adapter/GraphQL_SchemaInspectorTest.res +666 -568
  91. package/tests/adapter/GraphQL_SubscriptionResolversTest.res +105 -96
  92. package/tests/adapter/HeartbeatRunnerTest.res +99 -81
  93. package/tests/adapter/InboundTranslationMutationTest.res +4 -1
  94. package/tests/adapter/LocalAuthHttpTest.res +79 -85
  95. package/tests/adapter/LocalAuthLoginTest.res +42 -50
  96. package/tests/adapter/LocalAuthTest.res +8 -16
  97. package/tests/adapter/LocalAuthUserStoreTest.res +2 -7
  98. package/tests/adapter/LocalBusBoundedTest.res +187 -117
  99. package/tests/adapter/LocalBusPubSubTest.res +118 -76
  100. package/tests/adapter/LocalDispatchContextTest.res +1 -2
  101. package/tests/adapter/LocalEvents_ServerTest.res +242 -187
  102. package/tests/adapter/PgProjectionCatchupTest.res +44 -10
  103. package/tests/adapter/ProjectionCheckpointTest.res +261 -206
  104. package/tests/adapter/QueryDbGsiTtlTest.res +123 -83
  105. package/tests/adapter/QueryDbListPushdownParityTest.res +107 -67
  106. package/tests/adapter/QueryDbListResolverTest.res +225 -208
  107. package/tests/adapter/QueryDbResolvedFieldsTest.res +17 -14
  108. package/tests/adapter/QueryDbStorageSqliteTest.res +33 -49
  109. package/tests/adapter/QueryDbStorageTest.res +163 -117
  110. package/tests/adapter/QueryDbUnionRoundTripTest.res +8 -10
  111. package/tests/adapter/QueryEngineTest.res +179 -126
  112. package/tests/adapter/ScheduledPublisherTest.res +171 -135
  113. package/tests/adapter/ServedBucketHttpTest.res +65 -59
  114. package/tests/adapter/SortKey_FilterTest.res +19 -3
  115. package/tests/adapter/SqliteDriverTest.res +13 -10
  116. package/tests/adapter/TaskBucketTest.res +94 -80
  117. package/tests/components/aggregate/AggregateTest.res +31 -18
  118. package/tests/components/automationslice/AutomationSliceCallbackTest.res +259 -222
  119. package/tests/components/automationslice/AutomationSliceFixtures.res +4 -2
  120. package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res +38 -38
  121. package/tests/components/automationslice/MixedSourceAutomationSliceFixtures.res +12 -14
  122. package/tests/components/automationslice/MixedSourceAutomationSliceTest.res +8 -32
  123. package/tests/components/commandgenerator/CommandGeneratorFixtures.res +7 -5
  124. package/tests/components/commandgenerator/CommandGeneratorTest.res +40 -31
  125. package/tests/components/commandtopic/CommandTopicFixtures.res +3 -4
  126. package/tests/components/commandtopic/CommandTopicStreamFixtures.res +7 -5
  127. package/tests/components/commandtopic/CommandTopicTest.res +24 -15
  128. package/tests/components/counter/CounterFixtures.res +6 -8
  129. package/tests/components/dcb/DcbCrossPartitionFixtures.res +8 -2
  130. package/tests/components/dcb/DcbCrossPartitionReadTest.res +20 -13
  131. package/tests/components/dcb/DcbEventLogAppendStreamTest.res +12 -8
  132. package/tests/components/dcb/DcbEventLogStreamTest.res +88 -61
  133. package/tests/components/dcb/DcbFixtures.res +4 -1
  134. package/tests/components/dcb/DcbTest.res +9 -6
  135. package/tests/components/eventcollector/EventCollectorFixtures.res +0 -1
  136. package/tests/components/eventcollector/EventCollectorTest.res +6 -3
  137. package/tests/components/eventlog/EventLogAppendStreamTest.res +88 -68
  138. package/tests/components/eventlog/EventLogConflictTest.res +8 -4
  139. package/tests/components/eventlog/EventLogFixtures.res +3 -3
  140. package/tests/components/eventlog/EventLogProvisioningSeamTest.res +26 -13
  141. package/tests/components/eventlog/EventLogStreamTest.res +119 -56
  142. package/tests/components/eventlog/EventLogTest.res +20 -4
  143. package/tests/components/eventtopic/EventTopicFixtures.res +3 -3
  144. package/tests/components/eventtopic/EventTopicStreamFixtures.res +1 -2
  145. package/tests/components/eventtopic/EventTopicStreamTest.res +7 -1
  146. package/tests/components/extensionpoint/ExtensionPointFixtures.res +14 -9
  147. package/tests/components/extensionpoint/ExtensionPointTest.res +3 -2
  148. package/tests/components/heartbeat/HeartbeatFixtures.res +5 -6
  149. package/tests/components/heartbeat/HeartbeatTest.res +56 -47
  150. package/tests/components/inboundtranslationslice/InboundTranslationSliceCallbackTest.res +271 -249
  151. package/tests/components/inboundtranslationslice/InboundTranslationSliceFixtures.res +3 -2
  152. package/tests/components/outboundtranslationslice/OutboundTranslationSliceCallbackTest.res +361 -276
  153. package/tests/components/outboundtranslationslice/OutboundTranslationSliceFixtures.res +8 -10
  154. package/tests/components/outboundtranslationslice/OutboundTranslationSlicePlatformFixtures.res +37 -43
  155. package/tests/components/querydb/QueryDbFixtures.res +1 -5
  156. package/tests/components/querydb/QueryDbTest.res +2 -4
  157. package/tests/components/readmodel/DcbReadModelE2EFixtures.res +15 -18
  158. package/tests/components/readmodel/MixedSourceReadModelFixtures.res +5 -7
  159. package/tests/components/readmodel/ReadModelFixtures.res +8 -8
  160. package/tests/components/scheduler/SchedulerTest.res +76 -58
  161. package/tests/components/sideeffecthandler/SideEffectHandlerFixtures.res +4 -4
  162. package/tests/components/stateviewslice/StateViewSliceDisplayNameFixtures.res +5 -7
  163. package/tests/components/stateviewslice/StateViewSliceFixtures.res +8 -6
  164. package/tests/components/stateviewslice/StateViewSliceSubIdFixtures.res +17 -11
  165. package/tests/components/stateviewslice/StateViewSliceSubIdTest.res +12 -2
  166. package/tests/components/task/TaskFixtures.res +5 -5
  167. package/tests/components/task/TaskTest.res +95 -80
  168. package/tests/plugin/PluginsProjection_GWT.res +3 -1
  169. package/tests/plugin/UiFragments_GWT.res +6 -1
  170. package/tests/reset/LocalSeedResetTest.res +1 -0
@@ -444,11 +444,8 @@ let build = (db, ~root: string, ~scope: scope): plan => {
444
444
  // structure mentions, which only a `domain` scope reaches.
445
445
  let unattributed = switch scope {
446
446
  | OnePlugin(_) =>
447
- let claimedByAnyPlugin =
448
- plugins->Array.flatMap(p => p.queryables->Array.map(qdbTableName))
449
- qdbTables(db)->Array.filter(t =>
450
- !isPlatformQdb(t) && !(claimedByAnyPlugin->Array.includes(t))
451
- )
447
+ let claimedByAnyPlugin = plugins->Array.flatMap(p => p.queryables->Array.map(qdbTableName))
448
+ qdbTables(db)->Array.filter(t => !isPlatformQdb(t) && !(claimedByAnyPlugin->Array.includes(t)))
452
449
  | Domain | Platform | Everything => []
453
450
  }
454
451
 
@@ -41,7 +41,11 @@ let matchesQuery = (event: DcbEventLog_Adapter.rawSequencedEvent, query: Reventl
41
41
  })
42
42
  }
43
43
 
44
- let make = (~name="mock-dcb-log", ~indexes: array<string>=[], ~opts: Pulumi.CustomResourceOptions.t={}) => {
44
+ let make = (
45
+ ~name="mock-dcb-log",
46
+ ~indexes: array<string>=[],
47
+ ~opts: Pulumi.CustomResourceOptions.t={},
48
+ ) => {
45
49
  let _ = (name, indexes, opts)
46
50
  let events: ref<array<DcbEventLog_Adapter.rawSequencedEvent>> = ref([])
47
51
  let position = ref(0)
@@ -36,15 +36,20 @@ let make = (~name as _="mock-event-log", ~opts as _: Pulumi.CustomResourceOption
36
36
 
37
37
  let snapshots: dict<ReventlessCore.EventLog.snapshot> = Dict.make()
38
38
 
39
- let latestSnapshot: ReventlessCore.EventLog.latestSnapshot<string> = async id =>
40
- Ok(snapshots->Dict.get(id))
39
+ let latestSnapshot: ReventlessCore.EventLog.latestSnapshot<string> = async id => Ok(
40
+ snapshots->Dict.get(id),
41
+ )
41
42
 
42
43
  let writeSnapshot: ReventlessCore.EventLog.writeSnapshot<string> = async (id, snap) => {
43
44
  snapshots->Dict.set(id, snap)
44
45
  Ok()
45
46
  }
46
47
 
47
- let appendStream: ReventlessCore.EventLog.appendStream<string, JSON.t> = (_startingSeqNr, id, stream) =>
48
+ let appendStream: ReventlessCore.EventLog.appendStream<string, JSON.t> = (
49
+ _startingSeqNr,
50
+ id,
51
+ stream,
52
+ ) =>
48
53
  stream->Stream.runForEach(json =>
49
54
  Effect.sync(() => {
50
55
  let existing = events.contents->Dict.get(id)->Option.getOr([])
@@ -28,8 +28,9 @@ module Make = (Bus: LocalBus.T) => {
28
28
  allItems.contents = store.contents->Dict.valuesToArray->Array.flatMap(v => v)
29
29
  }
30
30
 
31
- let load: QueryDb.load<string, JSON.t> = async id =>
32
- Ok(store.contents->Dict.get(id)->Option.getOr([]))
31
+ let load: QueryDb.load<string, JSON.t> = async id => Ok(
32
+ store.contents->Dict.get(id)->Option.getOr([]),
33
+ )
33
34
 
34
35
  let loadStream: QueryDb.loadStream<string, JSON.t> = id =>
35
36
  store.contents->Dict.get(id)->Option.getOr([])->Stream.fromIterable
@@ -15,7 +15,7 @@ type mockResult = {
15
15
  }
16
16
 
17
17
  @module("@pulumi/pulumi") @scope("runtime")
18
- external setMocks: ({"newResource": mockArgs => mockResult, "call": mockArgs => JSON.t}) => unit =
18
+ external setMocks: {"newResource": mockArgs => mockResult, "call": mockArgs => JSON.t} => unit =
19
19
  "setMocks"
20
20
 
21
21
  // Activate Pulumi mock mode. Must be called once before any Platform.Make() or component creation.
@@ -16,7 +16,6 @@ let keysOf = (config: ReventlessInfra.Platform.bakedManifest) =>
16
16
  BakedManifest.files(~config)->Array.map(((key, _)) => key)
17
17
 
18
18
  describe("BakedManifest.files", () => {
19
-
20
19
  // The regression line: one declaration, one file, under the name every
21
20
  // existing deployment's config.json already points at.
22
21
  testSync("a declaration with no journeys produces exactly one file", () => {
@@ -39,11 +38,7 @@ describe("BakedManifest.files", () => {
39
38
  {group: "Fulfilment", components: [sel("Ordering")], key: "fulfil.json"},
40
39
  ],
41
40
  }),
42
- )->toEqual([
43
- "component-manifest.json",
44
- "component-manifest-shopper.json",
45
- "fulfil.json",
46
- ])
41
+ )->toEqual(["component-manifest.json", "component-manifest-shopper.json", "fulfil.json"])
47
42
  })
48
43
 
49
44
  // A group name is a Cognito identifier and a key is part of a URL, so the
@@ -55,10 +50,12 @@ describe("BakedManifest.files", () => {
55
50
  })
56
51
 
57
52
  testSync("carries each journey's own selections", () => {
58
- let files = BakedManifest.files(~config={
59
- components: [sel("Catalog")],
60
- journeys: [{group: "Fulfilment", components: [sel("Ordering")]}],
61
- })
53
+ let files = BakedManifest.files(
54
+ ~config={
55
+ components: [sel("Catalog")],
56
+ journeys: [{group: "Fulfilment", components: [sel("Ordering")]}],
57
+ },
58
+ )
62
59
  expect(files->Array.map(((_, sels)) => sels->Array.map(s => s.plugin)))->toEqual([
63
60
  ["Catalog"],
64
61
  ["Ordering"],
@@ -47,8 +47,8 @@ describe("LocalEventTap.settingFromEnv", () => {
47
47
  })
48
48
 
49
49
  testSync("switches the socket off on request", () =>
50
- ["off", "false", "none"]->Array.forEach(v =>
51
- expect(LocalEventTap.settingFromEnv(~env=envWith(Some(v))))->toEqual(LocalEventTap.Off)
50
+ ["off", "false", "none"]->Array.forEach(
51
+ v => expect(LocalEventTap.settingFromEnv(~env=envWith(Some(v))))->toEqual(LocalEventTap.Off),
52
52
  )
53
53
  )
54
54
  })
@@ -113,8 +113,8 @@ describe("LocalEventTap.start", () => {
113
113
  // picks, reported only once it is actually bound.
114
114
  test("binds an ephemeral port by default and reports the one it got", async () => {
115
115
  LocalEventTap.resetForTests()
116
- let bound = await Promise.make((resolve, _reject) =>
117
- LocalEventTap.start(~env=envWith(None), ~onBound=p => resolve(p), ())
116
+ let bound = await Promise.make(
117
+ (resolve, _reject) => LocalEventTap.start(~env=envWith(None), ~onBound=p => resolve(p), ()),
118
118
  )
119
119
  expect(bound > 0)->toEqual(true)
120
120
  // What the registry entry will carry — the real port, not a hoped-for one.
@@ -127,26 +127,31 @@ describe("LocalEventTap.start", () => {
127
127
  test("serves the lines it is sent on the port it was named", async () => {
128
128
  LocalEventTap.resetForTests()
129
129
  let port = 47311
130
- let bound = await Promise.make((resolve, _reject) =>
131
- LocalEventTap.start(~env=envWith(Some(port->Int.toString)), ~onBound=p => resolve(p), ())
130
+ let bound = await Promise.make(
131
+ (resolve, _reject) =>
132
+ LocalEventTap.start(~env=envWith(Some(port->Int.toString)), ~onBound=p => resolve(p), ()),
132
133
  )
133
134
  expect(bound)->toEqual(port)
134
135
 
135
- let received = await Promise.make((resolve, _reject) => {
136
- let socket = NodeNet.connect(port, "127.0.0.1", () => ())
137
- socket->NodeNet.setEncoding("utf8")
138
- socket->NodeNet.onSocketError(e =>
139
- resolve("connect failed: " ++ e->JsExn.message->Option.getOr("unknown"))
140
- )
141
- // Re-sent until it lands: the server's accept and the client's connect are
142
- // separate events, so a single broadcast could beat the connection.
143
- let ticker = ref(None)
144
- socket->NodeNet.onSocketData(chunk => {
145
- ticker.contents->Option.forEach(clearInterval)
146
- resolve(chunk)
147
- })
148
- ticker := Some(setInterval(() => LocalEventTap.broadcast(`@@RVLESS_EVT@@ {"seq":7}`), 5))
149
- })
136
+ let received = await Promise.make(
137
+ (resolve, _reject) => {
138
+ let socket = NodeNet.connect(port, "127.0.0.1", () => ())
139
+ socket->NodeNet.setEncoding("utf8")
140
+ socket->NodeNet.onSocketError(
141
+ e => resolve("connect failed: " ++ e->JsExn.message->Option.getOr("unknown")),
142
+ )
143
+ // Re-sent until it lands: the server's accept and the client's connect are
144
+ // separate events, so a single broadcast could beat the connection.
145
+ let ticker = ref(None)
146
+ socket->NodeNet.onSocketData(
147
+ chunk => {
148
+ ticker.contents->Option.forEach(clearInterval)
149
+ resolve(chunk)
150
+ },
151
+ )
152
+ ticker := Some(setInterval(() => LocalEventTap.broadcast(`@@RVLESS_EVT@@ {"seq":7}`), 5))
153
+ },
154
+ )
150
155
 
151
156
  expect(received)->toEqual(`@@RVLESS_EVT@@ {"seq":7}\n`)
152
157
  await LocalEventTap.stopForTests()
@@ -122,42 +122,47 @@ describe("LocalPlatformStart.orAddressRunning", () => {
122
122
 
123
123
  describe("LocalPlatformStart.decide", () => {
124
124
  testSync("starts where nothing is running", () =>
125
- withoutBypass(() =>
126
- expect(LocalPlatformStart.decide(~cwd=tempRoot(), ()))->toEqual(LocalPlatformStart.Start)
125
+ withoutBypass(
126
+ () =>
127
+ expect(LocalPlatformStart.decide(~cwd=tempRoot(), ()))->toEqual(LocalPlatformStart.Start),
127
128
  )
128
129
  )
129
130
 
130
131
  testSync("addresses the one platform already serving this directory", () =>
131
- withoutBypass(() => {
132
- let cwd = tempRoot()
133
- let _ = writeAt(~cwd, ~port=4000, ~store=sqliteStore(NodePath.join([cwd, "local.db"])))
134
-
135
- switch LocalPlatformStart.decide(~cwd, ()) {
136
- | Start => fail("expected the running platform to be addressed")
137
- | AlreadyRunning(entries) =>
138
- expect(entries->Array.map(e => e.port))->toEqual([4000])
139
- let lines = LocalPlatformStart.report(entries)
140
- expect(lines->Array.length)->toEqual(2)
141
- expect(lines->Array.join("\n")->String.includes(":4000"))->toEqual(true)
142
- }
143
- })
132
+ withoutBypass(
133
+ () => {
134
+ let cwd = tempRoot()
135
+ let _ = writeAt(~cwd, ~port=4000, ~store=sqliteStore(NodePath.join([cwd, "local.db"])))
136
+
137
+ switch LocalPlatformStart.decide(~cwd, ()) {
138
+ | Start => fail("expected the running platform to be addressed")
139
+ | AlreadyRunning(entries) =>
140
+ expect(entries->Array.map(e => e.port))->toEqual([4000])
141
+ let lines = LocalPlatformStart.report(entries)
142
+ expect(lines->Array.length)->toEqual(2)
143
+ expect(lines->Array.join("\n")->String.includes(":4000"))->toEqual(true)
144
+ }
145
+ },
146
+ )
144
147
  )
145
148
 
146
149
  // The state this is trying to make impossible: say so rather than pick one.
147
150
  testSync("names all of them when two are running", () =>
148
- withoutBypass(() => {
149
- let cwd = tempRoot()
150
- let _ = writeAt(~cwd, ~port=4000, ~store=sqliteStore(NodePath.join([cwd, "local.db"])))
151
- let _ = writeAt(~cwd, ~port=4010, ~store=memoryStore)
152
-
153
- switch LocalPlatformStart.decide(~cwd, ()) {
154
- | Start => fail("expected both running platforms to be reported")
155
- | AlreadyRunning(entries) =>
156
- let printed = LocalPlatformStart.report(entries)->Array.join("\n")
157
- expect(printed->String.includes(":4000"))->toEqual(true)
158
- expect(printed->String.includes(":4010"))->toEqual(true)
159
- }
160
- })
151
+ withoutBypass(
152
+ () => {
153
+ let cwd = tempRoot()
154
+ let _ = writeAt(~cwd, ~port=4000, ~store=sqliteStore(NodePath.join([cwd, "local.db"])))
155
+ let _ = writeAt(~cwd, ~port=4010, ~store=memoryStore)
156
+
157
+ switch LocalPlatformStart.decide(~cwd, ()) {
158
+ | Start => fail("expected both running platforms to be reported")
159
+ | AlreadyRunning(entries) =>
160
+ let printed = LocalPlatformStart.report(entries)->Array.join("\n")
161
+ expect(printed->String.includes(":4000"))->toEqual(true)
162
+ expect(printed->String.includes(":4010"))->toEqual(true)
163
+ }
164
+ },
165
+ )
161
166
  )
162
167
 
163
168
  // The escape hatch the e2e suites and the VS Code runner already take.
@@ -93,9 +93,9 @@ describe("Platform.decodeUiFragmentRegistryEventEnvelope", () => {
93
93
  })
94
94
 
95
95
  testSync("returns None for malformed input", () => {
96
- expect(
97
- Platform.decodeUiFragmentRegistryEventEnvelope(JSON.Encode.string("junk")),
98
- )->toEqual(None)
96
+ expect(Platform.decodeUiFragmentRegistryEventEnvelope(JSON.Encode.string("junk")))->toEqual(
97
+ None,
98
+ )
99
99
  expect(
100
100
  Platform.decodeUiFragmentRegistryEventEnvelope(
101
101
  JSON.Encode.object(Dict.fromArray([("event", JSON.Encode.object(Dict.make()))])),
@@ -11,7 +11,7 @@ let _ = TestRunner.setup()
11
11
 
12
12
  let manifest = (~key: option<string>=?): ReventlessInfra.Platform.bakedManifest => {
13
13
  components: [{plugin: "Catalog", views: ["Products"], commands: []}],
14
- key: ?key,
14
+ ?key,
15
15
  }
16
16
 
17
17
  let str = (d: dict<JSON.t>, k: string): option<string> =>
@@ -65,13 +65,14 @@ describe("ShellConfig.overlay", () => {
65
65
  // did not write, with nothing in the diff to say so.
66
66
  testSync("refuses a shellConfig key the platform computes", () =>
67
67
  expect(
68
- threw(() =>
69
- ShellConfig.overlay(
70
- ~bakedManifest=Some(manifest()),
71
- ~shellConfig=Some(
72
- Dict.fromArray([("manifestUrl", JSON.Encode.string("/elsewhere.json"))]),
73
- ),
74
- )->ignore
68
+ threw(
69
+ () =>
70
+ ShellConfig.overlay(
71
+ ~bakedManifest=Some(manifest()),
72
+ ~shellConfig=Some(
73
+ Dict.fromArray([("manifestUrl", JSON.Encode.string("/elsewhere.json"))]),
74
+ ),
75
+ )->ignore,
75
76
  ),
76
77
  )->toBe(true)
77
78
  )
@@ -107,12 +108,15 @@ describe("ShellConfig.overlay — uiSlotsFile", () => {
107
108
 
108
109
  testSync("refuses a shellConfig key redirecting the computed module", () =>
109
110
  expect(
110
- threw(() =>
111
- ShellConfig.overlay(
112
- ~bakedManifest=None,
113
- ~uiSlotsFile="/src/storefront-slots.js",
114
- ~shellConfig=Some(Dict.fromArray([("uiSlotsUrl", JSON.Encode.string("/elsewhere.js"))])),
115
- )->ignore
111
+ threw(
112
+ () =>
113
+ ShellConfig.overlay(
114
+ ~bakedManifest=None,
115
+ ~uiSlotsFile="/src/storefront-slots.js",
116
+ ~shellConfig=Some(
117
+ Dict.fromArray([("uiSlotsUrl", JSON.Encode.string("/elsewhere.js"))]),
118
+ ),
119
+ )->ignore,
116
120
  ),
117
121
  )->toBe(true)
118
122
  )
@@ -208,7 +212,6 @@ let fulfilment: ReventlessInfra.Platform.bakedJourney = {
208
212
  }
209
213
 
210
214
  describe("ShellConfig.overlay — journeys", () => {
211
-
212
215
  // The regression line. A shell that has never heard of journeys must not
213
216
  // suddenly find a key it does not know.
214
217
  testSync("a bake declaring no journeys writes no map", () => {
@@ -217,7 +220,10 @@ describe("ShellConfig.overlay — journeys", () => {
217
220
  })
218
221
 
219
222
  testSync("an empty journeys array is the same as none", () => {
220
- let out = ShellConfig.overlay(~bakedManifest=Some(withJourneys(~journeys=[])), ~shellConfig=None)
223
+ let out = ShellConfig.overlay(
224
+ ~bakedManifest=Some(withJourneys(~journeys=[])),
225
+ ~shellConfig=None,
226
+ )
221
227
  expect(out->Dict.get("journeyManifestUrls"))->toEqual(None)
222
228
  })
223
229
 
@@ -250,11 +256,14 @@ describe("ShellConfig.overlay — journeys", () => {
250
256
  // `manifestUrl` already carries, extended to the map it now writes beside it.
251
257
  testSync("refuses a shellConfig that sets the journey map itself", () =>
252
258
  expect(
253
- threw(() =>
254
- ShellConfig.overlay(
255
- ~bakedManifest=Some(withJourneys(~journeys=[shopper])),
256
- ~shellConfig=Some(Dict.fromArray([("journeyManifestUrls", JSON.Encode.object(Dict.make()))])),
257
- )->ignore
259
+ threw(
260
+ () =>
261
+ ShellConfig.overlay(
262
+ ~bakedManifest=Some(withJourneys(~journeys=[shopper])),
263
+ ~shellConfig=Some(
264
+ Dict.fromArray([("journeyManifestUrls", JSON.Encode.object(Dict.make()))]),
265
+ ),
266
+ )->ignore,
258
267
  ),
259
268
  )->toBe(true)
260
269
  )
@@ -31,7 +31,9 @@ let extractSdlFieldName = (sdlField: string): string => {
31
31
  let adminGraphQL = ReventlessGraphqlServer.GraphQL_ServerInstance.make(~label="GraphQL:Admin")
32
32
 
33
33
  // Build admin SDL — authoritative source for field names.
34
- let baseParts = ReventlessCore.GraphQL_Stitcher.decode(ReventlessCore.Platform_AdminApi.baseFragment(~cloner=true))
34
+ let baseParts = ReventlessCore.GraphQL_Stitcher.decode(
35
+ ReventlessCore.Platform_AdminApi.baseFragment(~cloner=true),
36
+ )
35
37
  let () = adminGraphQL.registerTypes(~sdlTypes=baseParts.types)
36
38
 
37
39
  // Derive query/mutation field names directly from SDL so that any SDL
@@ -45,16 +47,23 @@ let singleQueryField = adminQueryEntry.singleFieldName
45
47
  let listQueryField = adminQueryEntry.listFieldName
46
48
 
47
49
  let adminQueryResolvers = Dict.make()
48
- let () = adminQueryFieldNames->Array.forEach(field =>
49
- adminQueryResolvers->Dict.set(field, async (_root, _args, _ctx): JSON.t => JSON.Encode.null)
50
- )
50
+ let () =
51
+ adminQueryFieldNames->Array.forEach(field =>
52
+ adminQueryResolvers->Dict.set(field, async (_root, _args, _ctx): JSON.t => JSON.Encode.null)
53
+ )
51
54
  let () = adminGraphQL.registerQueries(~sdlFields=baseParts.queries, ~resolvers=adminQueryResolvers)
52
55
 
53
56
  let adminMutationResolvers = Dict.make()
54
- let () = adminMutationFieldNames->Array.forEach(field =>
55
- adminMutationResolvers->Dict.set(field, async (_root, _args, _ctx): JSON.t => JSON.Encode.string("ok"))
57
+ let () =
58
+ adminMutationFieldNames->Array.forEach(field =>
59
+ adminMutationResolvers->Dict.set(field, async (_root, _args, _ctx): JSON.t =>
60
+ JSON.Encode.string("ok")
61
+ )
62
+ )
63
+ let () = adminGraphQL.registerMutations(
64
+ ~sdlFields=baseParts.mutations,
65
+ ~resolvers=adminMutationResolvers,
56
66
  )
57
- let () = adminGraphQL.registerMutations(~sdlFields=baseParts.mutations, ~resolvers=adminMutationResolvers)
58
67
 
59
68
  // ─────────────────────────────────────────────────────────────
60
69
  // Register fake plugin schema into the singleton (plugin server)
@@ -66,8 +75,18 @@ let pluginTypes = [`type SplitTestPlugin_SplitTestItem { id: ID!, name: String!
66
75
  let () = GraphQL_Server.registerTypes(~sdlTypes=pluginTypes)
67
76
 
68
77
  let pluginQueryResolvers = Dict.make()
69
- let () = pluginQueryResolvers->Dict.set("SplitTestPlugin_SplitTestItem", async (_root, _args, _ctx): JSON.t => JSON.Encode.null)
70
- let () = pluginQueryResolvers->Dict.set("SplitTestPlugin_SplitTestItems", async (_root, _args, _ctx): JSON.t => JSON.Encode.null)
78
+ let () =
79
+ pluginQueryResolvers->Dict.set("SplitTestPlugin_SplitTestItem", async (
80
+ _root,
81
+ _args,
82
+ _ctx,
83
+ ): JSON.t => JSON.Encode.null)
84
+ let () =
85
+ pluginQueryResolvers->Dict.set("SplitTestPlugin_SplitTestItems", async (
86
+ _root,
87
+ _args,
88
+ _ctx,
89
+ ): JSON.t => JSON.Encode.null)
71
90
  let () = GraphQL_Server.registerQueries(
72
91
  ~sdlFields=[
73
92
  `SplitTestPlugin_SplitTestItem(id: ID!): SplitTestPlugin_SplitTestItem`,
@@ -77,7 +96,12 @@ let () = GraphQL_Server.registerQueries(
77
96
  )
78
97
 
79
98
  let pluginMutationResolvers = Dict.make()
80
- let () = pluginMutationResolvers->Dict.set("SplitTestPlugin_SplitTestItem_CreateItem", async (_root, _args, _ctx): JSON.t => JSON.Encode.string("ok"))
99
+ let () =
100
+ pluginMutationResolvers->Dict.set("SplitTestPlugin_SplitTestItem_CreateItem", async (
101
+ _root,
102
+ _args,
103
+ _ctx,
104
+ ): JSON.t => JSON.Encode.string("ok"))
81
105
  let () = GraphQL_Server.registerMutations(
82
106
  ~sdlFields=[`SplitTestPlugin_SplitTestItem_CreateItem(id: ID!, name: String!): String`],
83
107
  ~resolvers=pluginMutationResolvers,
@@ -25,78 +25,108 @@ let pluginQueryPrefix = "SplitTestPlugin_SplitTestItem"
25
25
 
26
26
  describe("Split API Mode — Schema Separation", () => {
27
27
  describe("Plugin GraphQL singleton", () => {
28
- testPromise("contains plugin mutation fields", async () => {
29
- let d = GraphQL_Server.diagnostics()
30
- let hasPluginMutation =
31
- d.registeredMutationFields->Array.some(f => f->String.startsWith(pluginMutationPrefix))
32
- expect(hasPluginMutation)->toBe(true)
33
- })
28
+ testPromise(
29
+ "contains plugin mutation fields",
30
+ async () => {
31
+ let d = GraphQL_Server.diagnostics()
32
+ let hasPluginMutation =
33
+ d.registeredMutationFields->Array.some(f => f->String.startsWith(pluginMutationPrefix))
34
+ expect(hasPluginMutation)->toBe(true)
35
+ },
36
+ )
34
37
 
35
- testPromise("contains plugin query fields", async () => {
36
- let d = GraphQL_Server.diagnostics()
37
- let hasPluginQuery =
38
- d.registeredQueryFields->Array.some(f => f->String.startsWith(pluginQueryPrefix))
39
- expect(hasPluginQuery)->toBe(true)
40
- })
38
+ testPromise(
39
+ "contains plugin query fields",
40
+ async () => {
41
+ let d = GraphQL_Server.diagnostics()
42
+ let hasPluginQuery =
43
+ d.registeredQueryFields->Array.some(f => f->String.startsWith(pluginQueryPrefix))
44
+ expect(hasPluginQuery)->toBe(true)
45
+ },
46
+ )
41
47
 
42
- testPromise("does NOT contain admin mutation fields", async () => {
43
- let d = GraphQL_Server.diagnostics()
44
- let hasAdminField =
45
- d.registeredMutationFields->Array.some(f =>
46
- adminMutationFields->Array.includes(f)
47
- )
48
- expect(hasAdminField)->toBe(false)
49
- })
48
+ testPromise(
49
+ "does NOT contain admin mutation fields",
50
+ async () => {
51
+ let d = GraphQL_Server.diagnostics()
52
+ let hasAdminField =
53
+ d.registeredMutationFields->Array.some(f => adminMutationFields->Array.includes(f))
54
+ expect(hasAdminField)->toBe(false)
55
+ },
56
+ )
50
57
 
51
- testPromise("does NOT contain admin query fields", async () => {
52
- let d = GraphQL_Server.diagnostics()
53
- let hasAdminQuery =
54
- d.registeredQueryFields->Array.some(f =>
55
- adminQueryFields->Array.includes(f)
56
- )
57
- expect(hasAdminQuery)->toBe(false)
58
- })
58
+ testPromise(
59
+ "does NOT contain admin query fields",
60
+ async () => {
61
+ let d = GraphQL_Server.diagnostics()
62
+ let hasAdminQuery =
63
+ d.registeredQueryFields->Array.some(f => adminQueryFields->Array.includes(f))
64
+ expect(hasAdminQuery)->toBe(false)
65
+ },
66
+ )
59
67
  })
60
68
 
61
69
  describe("Admin GraphQL instance", () => {
62
- testPromise("contains admin mutation fields", async () => {
63
- let d = adminGraphQL.diagnostics()
64
- adminMutationFields->Array.forEach(field => {
65
- let hasField = d.registeredMutationFields->Array.includes(field)
66
- expect(hasField)->toBe(true)
67
- })
68
- })
70
+ testPromise(
71
+ "contains admin mutation fields",
72
+ async () => {
73
+ let d = adminGraphQL.diagnostics()
74
+ adminMutationFields->Array.forEach(
75
+ field => {
76
+ let hasField = d.registeredMutationFields->Array.includes(field)
77
+ expect(hasField)->toBe(true)
78
+ },
79
+ )
80
+ },
81
+ )
69
82
 
70
- testPromise("contains admin query fields", async () => {
71
- let d = adminGraphQL.diagnostics()
72
- adminQueryFields->Array.forEach(field => {
73
- let hasField = d.registeredQueryFields->Array.includes(field)
74
- expect(hasField)->toBe(true)
75
- })
76
- })
83
+ testPromise(
84
+ "contains admin query fields",
85
+ async () => {
86
+ let d = adminGraphQL.diagnostics()
87
+ adminQueryFields->Array.forEach(
88
+ field => {
89
+ let hasField = d.registeredQueryFields->Array.includes(field)
90
+ expect(hasField)->toBe(true)
91
+ },
92
+ )
93
+ },
94
+ )
77
95
 
78
- testPromise("does NOT contain plugin mutation fields", async () => {
79
- let d = adminGraphQL.diagnostics()
80
- let hasPluginMutation =
81
- d.registeredMutationFields->Array.some(f => f->String.startsWith(pluginMutationPrefix))
82
- expect(hasPluginMutation)->toBe(false)
83
- })
96
+ testPromise(
97
+ "does NOT contain plugin mutation fields",
98
+ async () => {
99
+ let d = adminGraphQL.diagnostics()
100
+ let hasPluginMutation =
101
+ d.registeredMutationFields->Array.some(f => f->String.startsWith(pluginMutationPrefix))
102
+ expect(hasPluginMutation)->toBe(false)
103
+ },
104
+ )
84
105
 
85
- testPromise("does NOT contain plugin query fields", async () => {
86
- let d = adminGraphQL.diagnostics()
87
- let hasPluginQuery =
88
- d.registeredQueryFields->Array.some(f => f->String.startsWith(pluginQueryPrefix))
89
- expect(hasPluginQuery)->toBe(false)
90
- })
106
+ testPromise(
107
+ "does NOT contain plugin query fields",
108
+ async () => {
109
+ let d = adminGraphQL.diagnostics()
110
+ let hasPluginQuery =
111
+ d.registeredQueryFields->Array.some(f => f->String.startsWith(pluginQueryPrefix))
112
+ expect(hasPluginQuery)->toBe(false)
113
+ },
114
+ )
91
115
 
92
- testPromise("has admin type definitions registered", async () => {
93
- let d = adminGraphQL.diagnostics()
94
- expect(d.typeCount > 0)->toBe(true)
95
- })
116
+ testPromise(
117
+ "has admin type definitions registered",
118
+ async () => {
119
+ let d = adminGraphQL.diagnostics()
120
+ expect(d.typeCount > 0)->toBe(true)
121
+ },
122
+ )
96
123
 
97
- testPromise("has no SDL mismatches", async () => {
98
- let d = adminGraphQL.diagnostics()
99
- expect(d.mismatches->Array.length)->toBe(0)
100
- })
124
+ testPromise(
125
+ "has no SDL mismatches",
126
+ async () => {
127
+ let d = adminGraphQL.diagnostics()
128
+ expect(d.mismatches->Array.length)->toBe(0)
129
+ },
130
+ )
101
131
  })
102
132
  })