@reventlessdev/reventless-local 3.0.0-alpha.249 → 3.0.0-alpha.251

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 (174) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/package.json +13 -13
  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 +12 -16
  15. package/src/adapter/Auth/LocalAuth.res.mjs +2 -1
  16. package/src/adapter/Auth/UserStore.res +13 -40
  17. package/src/adapter/Auth/UserStore.res.mjs +11 -67
  18. package/src/adapter/Backend.res +1 -2
  19. package/src/adapter/CommandGenerator/CommandGeneratorResolvers_GraphQL.res +42 -26
  20. package/src/adapter/CommandGenerator/InboundTranslationResolvers_GraphQL.res +13 -3
  21. package/src/adapter/CommandTopic/LocalCommandTopicChannel.res +15 -17
  22. package/src/adapter/CommandTopic/LocalCommandTopicRemoteChannel.res +18 -22
  23. package/src/adapter/Counter/LocalCounterHandler.res +1 -1
  24. package/src/adapter/DcbEventLog/DcbEventLogStorage_InMemory.res +33 -22
  25. package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res +57 -57
  26. package/src/adapter/DomainGraphQL_Server.res +35 -34
  27. package/src/adapter/DomainMCP_Server.res +45 -41
  28. package/src/adapter/EventCollector/LocalEventCollectorChannel.res +47 -36
  29. package/src/adapter/EventHistory/EventHistoryResolvers_GraphQL.res +15 -12
  30. package/src/adapter/EventLog/EventLogStorage_InMemory.res +9 -4
  31. package/src/adapter/EventLog/EventLogStorage_Sqlite.res +36 -24
  32. package/src/adapter/EventTopic/LocalEventTopicPublisher.res +9 -3
  33. package/src/adapter/Heartbeat/LocalHeartbeatRunner.res +9 -14
  34. package/src/adapter/LocalBus.res +12 -12
  35. package/src/adapter/LocalCapabilities.res +6 -0
  36. package/src/adapter/LocalCapabilities.res.mjs +5 -2
  37. package/src/adapter/LocalGeocodeResolvers.res +1 -4
  38. package/src/adapter/LocalSideEffectHandler.res +1 -1
  39. package/src/adapter/LocalStateChangeDescriptor.res +2 -1
  40. package/src/adapter/MCP_ServerInstance.res +51 -39
  41. package/src/adapter/ObjectStore/LocalObjectStore.res +5 -2
  42. package/src/adapter/ObjectStore/ObjectStoreStorage_FileSystem.res +1 -2
  43. package/src/adapter/PlatformGraphQL_Server.res +10 -6
  44. package/src/adapter/PlatformGraphQL_Server.res.mjs +2 -1
  45. package/src/adapter/ProjectionCheckpoint.res +8 -1
  46. package/src/adapter/ProjectionPending.res +1 -3
  47. package/src/adapter/QueryDb/LocalQueryDbStorage.res +2 -1
  48. package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res +367 -269
  49. package/src/adapter/QueryDb/QueryDbStorage_InMemory.res +5 -3
  50. package/src/adapter/QueryDb/QueryDbStorage_Sqlite.res +63 -40
  51. package/src/adapter/QueryDb/SortKey_Filter.res +14 -6
  52. package/src/adapter/Runtime/LocalAggregateRuntime_Builder.res +12 -10
  53. package/src/adapter/Runtime/LocalEventCollectorRuntime_Builder.res +8 -2
  54. package/src/adapter/Runtime/LocalPluginRuntime_Builder.res +1 -4
  55. package/src/adapter/Scheduler/LocalScheduledPublisher.res +12 -12
  56. package/src/adapter/SqliteDriver.res +16 -4
  57. package/src/adapter/Task/TaskBucket_Sqlite.res +5 -6
  58. package/src/components/Aggregate_Builder.res +6 -1
  59. package/src/components/Counter_Builder.res +1 -5
  60. package/src/components/DcbEventLog_Builder.res +5 -3
  61. package/src/components/ExtensionPoint_Builder.res +13 -15
  62. package/src/components/InboundTranslationSlice_Builder.res +3 -2
  63. package/src/components/OutboundTranslationSlice_Builder.res +3 -2
  64. package/src/components/ReadModel_Builder.res +18 -20
  65. package/src/components/StateViewSlice_Builder.res +3 -2
  66. package/src/components/Task_Builder.res +12 -13
  67. package/src/reset/LocalSeedReset.res +2 -5
  68. package/src/test/Mocks/MockDcbEventLogStorage.res +5 -1
  69. package/src/test/Mocks/MockEventLogStorage.res +8 -3
  70. package/src/test/Mocks/MockQueryDbStorage.res +3 -2
  71. package/src/test/TestRunner.res +1 -1
  72. package/tests/BakedManifestFilesTest.res +7 -10
  73. package/tests/LocalEventTapTest.res +26 -21
  74. package/tests/LocalPlatformStartTest.res +33 -28
  75. package/tests/PluginEventDecodeTest.res +3 -3
  76. package/tests/ShellConfigTest.res +30 -21
  77. package/tests/SplitApiFixtures.res +34 -10
  78. package/tests/SplitApiTest.res +92 -62
  79. package/tests/adapter/BackendParityTest.res +180 -130
  80. package/tests/adapter/CollectNEventsTest.res +6 -4
  81. package/tests/adapter/CommandAuthorizationTest.res +47 -41
  82. package/tests/adapter/CommandTopicChannelTest.res +163 -139
  83. package/tests/adapter/CounterHandlerTest.res +95 -77
  84. package/tests/adapter/DcbEventLogStorageSqliteTest.res +171 -168
  85. package/tests/adapter/DcbEventLogStorageTest.res +264 -207
  86. package/tests/adapter/DomainGraphQL_ServerTest.res +53 -43
  87. package/tests/adapter/EventCollectorChannelTest.res +153 -124
  88. package/tests/adapter/EventHistoryResolverTest.res +46 -18
  89. package/tests/adapter/EventLogSnapshotParityTest.res +57 -45
  90. package/tests/adapter/EventLogStorageSqliteTest.res +9 -10
  91. package/tests/adapter/EventTapTest.res +37 -20
  92. package/tests/adapter/EventTopicPublisherTest.res +74 -50
  93. package/tests/adapter/GraphQLSubscriptionBridgeTest.res +4 -3
  94. package/tests/adapter/GraphQL_SchemaInspectorTest.res +666 -568
  95. package/tests/adapter/GraphQL_SubscriptionResolversTest.res +105 -96
  96. package/tests/adapter/HeartbeatRunnerTest.res +99 -81
  97. package/tests/adapter/InboundTranslationMutationTest.res +4 -1
  98. package/tests/adapter/LocalAuthHttpTest.res +79 -85
  99. package/tests/adapter/LocalAuthLoginTest.res +42 -50
  100. package/tests/adapter/LocalAuthTest.res +8 -16
  101. package/tests/adapter/LocalAuthUserStoreTest.res +2 -7
  102. package/tests/adapter/LocalBusBoundedTest.res +187 -117
  103. package/tests/adapter/LocalBusPubSubTest.res +118 -76
  104. package/tests/adapter/LocalDispatchContextTest.res +1 -2
  105. package/tests/adapter/LocalEvents_ServerTest.res +242 -187
  106. package/tests/adapter/PgProjectionCatchupTest.res +44 -10
  107. package/tests/adapter/ProjectionCheckpointTest.res +261 -206
  108. package/tests/adapter/QueryDbGsiTtlTest.res +123 -83
  109. package/tests/adapter/QueryDbListPushdownParityTest.res +107 -67
  110. package/tests/adapter/QueryDbListResolverTest.res +225 -208
  111. package/tests/adapter/QueryDbResolvedFieldsTest.res +17 -14
  112. package/tests/adapter/QueryDbStorageSqliteTest.res +33 -49
  113. package/tests/adapter/QueryDbStorageTest.res +163 -117
  114. package/tests/adapter/QueryDbUnionRoundTripTest.res +8 -10
  115. package/tests/adapter/QueryEngineTest.res +179 -126
  116. package/tests/adapter/ScheduledPublisherTest.res +171 -135
  117. package/tests/adapter/ServedBucketHttpTest.res +65 -59
  118. package/tests/adapter/SortKey_FilterTest.res +19 -3
  119. package/tests/adapter/SqliteDriverTest.res +13 -10
  120. package/tests/adapter/TaskBucketTest.res +94 -80
  121. package/tests/components/aggregate/AggregateTest.res +31 -18
  122. package/tests/components/automationslice/AutomationSliceCallbackTest.res +259 -222
  123. package/tests/components/automationslice/AutomationSliceFixtures.res +4 -2
  124. package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res +38 -38
  125. package/tests/components/automationslice/MixedSourceAutomationSliceFixtures.res +12 -14
  126. package/tests/components/automationslice/MixedSourceAutomationSliceTest.res +8 -32
  127. package/tests/components/commandgenerator/CommandGeneratorFixtures.res +7 -5
  128. package/tests/components/commandgenerator/CommandGeneratorTest.res +40 -31
  129. package/tests/components/commandtopic/CommandTopicFixtures.res +3 -4
  130. package/tests/components/commandtopic/CommandTopicStreamFixtures.res +7 -5
  131. package/tests/components/commandtopic/CommandTopicTest.res +24 -15
  132. package/tests/components/counter/CounterFixtures.res +6 -8
  133. package/tests/components/dcb/DcbCrossPartitionFixtures.res +8 -2
  134. package/tests/components/dcb/DcbCrossPartitionReadTest.res +20 -13
  135. package/tests/components/dcb/DcbEventLogAppendStreamTest.res +12 -8
  136. package/tests/components/dcb/DcbEventLogStreamTest.res +88 -61
  137. package/tests/components/dcb/DcbFixtures.res +4 -1
  138. package/tests/components/dcb/DcbTest.res +9 -6
  139. package/tests/components/eventcollector/EventCollectorFixtures.res +0 -1
  140. package/tests/components/eventcollector/EventCollectorTest.res +6 -3
  141. package/tests/components/eventlog/EventLogAppendStreamTest.res +88 -68
  142. package/tests/components/eventlog/EventLogConflictTest.res +8 -4
  143. package/tests/components/eventlog/EventLogFixtures.res +3 -3
  144. package/tests/components/eventlog/EventLogProvisioningSeamTest.res +26 -13
  145. package/tests/components/eventlog/EventLogStreamTest.res +119 -56
  146. package/tests/components/eventlog/EventLogTest.res +20 -4
  147. package/tests/components/eventtopic/EventTopicFixtures.res +3 -3
  148. package/tests/components/eventtopic/EventTopicStreamFixtures.res +1 -2
  149. package/tests/components/eventtopic/EventTopicStreamTest.res +7 -1
  150. package/tests/components/extensionpoint/ExtensionPointFixtures.res +14 -9
  151. package/tests/components/extensionpoint/ExtensionPointTest.res +3 -2
  152. package/tests/components/heartbeat/HeartbeatFixtures.res +5 -6
  153. package/tests/components/heartbeat/HeartbeatTest.res +56 -47
  154. package/tests/components/inboundtranslationslice/InboundTranslationSliceCallbackTest.res +271 -249
  155. package/tests/components/inboundtranslationslice/InboundTranslationSliceFixtures.res +3 -2
  156. package/tests/components/outboundtranslationslice/OutboundTranslationSliceCallbackTest.res +361 -276
  157. package/tests/components/outboundtranslationslice/OutboundTranslationSliceFixtures.res +8 -10
  158. package/tests/components/outboundtranslationslice/OutboundTranslationSlicePlatformFixtures.res +37 -43
  159. package/tests/components/querydb/QueryDbFixtures.res +1 -5
  160. package/tests/components/querydb/QueryDbTest.res +2 -4
  161. package/tests/components/readmodel/DcbReadModelE2EFixtures.res +15 -18
  162. package/tests/components/readmodel/MixedSourceReadModelFixtures.res +5 -7
  163. package/tests/components/readmodel/ReadModelFixtures.res +8 -8
  164. package/tests/components/scheduler/SchedulerTest.res +76 -58
  165. package/tests/components/sideeffecthandler/SideEffectHandlerFixtures.res +4 -4
  166. package/tests/components/stateviewslice/StateViewSliceDisplayNameFixtures.res +5 -7
  167. package/tests/components/stateviewslice/StateViewSliceFixtures.res +8 -6
  168. package/tests/components/stateviewslice/StateViewSliceSubIdFixtures.res +17 -11
  169. package/tests/components/stateviewslice/StateViewSliceSubIdTest.res +12 -2
  170. package/tests/components/task/TaskFixtures.res +5 -5
  171. package/tests/components/task/TaskTest.res +95 -80
  172. package/tests/plugin/PluginsProjection_GWT.res +3 -1
  173. package/tests/plugin/UiFragments_GWT.res +6 -1
  174. package/tests/reset/LocalSeedResetTest.res +1 -0
@@ -41,11 +41,7 @@ let manifestUrlOf = (config: ReventlessInfra.Platform.bakedManifest): string =>
41
41
  // caller matching no declared group gets and what every existing deployment has.
42
42
  let journeyManifestsKey = "journeyManifestUrls"
43
43
 
44
- let computedKeys = [
45
- "manifestUrl",
46
- journeyManifestsKey,
47
- ReventlessCore.Platform_UiSlots.configKey,
48
- ]
44
+ let computedKeys = ["manifestUrl", journeyManifestsKey, ReventlessCore.Platform_UiSlots.configKey]
49
45
 
50
46
  /**
51
47
  The overlay this platform puts on top of the shipped `config.json`.
@@ -96,8 +92,9 @@ let overlay = (
96
92
  if collisions->Array.length > 0 {
97
93
  JsError.throwWithMessage(
98
94
  "host UI config.json: shellConfig sets key(s) the platform already computes — " ++
99
- collisions->Array.join(", ") ++
100
- ". Remove them from shellConfig; a passthrough cannot redirect a computed key.",
95
+ collisions->Array.join(
96
+ ", ",
97
+ ) ++ ". Remove them from shellConfig; a passthrough cannot redirect a computed key.",
101
98
  )
102
99
  }
103
100
  extra->Dict.forEachWithKey((v, k) => out->Dict.set(k, v))
@@ -134,12 +131,10 @@ let emit = (
134
131
  ) => {
135
132
  let overlay = overlay(~bakedManifest, ~uiSlotsFile?, ~shellConfig)
136
133
 
137
- switch (
138
- switch dir {
139
- | Some(_) as given => given
140
- | None => HostShellDist.dir()
141
- }
142
- ) {
134
+ switch switch dir {
135
+ | Some(_) as given => given
136
+ | None => HostShellDist.dir()
137
+ } {
143
138
  | None =>
144
139
  // Nothing declared and no shell installed is the ordinary case for a
145
140
  // platform nobody points a browser at; only a declaration makes the missing
@@ -148,8 +143,7 @@ let emit = (
148
143
  JsError.throwWithMessage(
149
144
  `host UI config.json: cannot resolve ${HostShellDist.package} from ${NodeProcess.cwd()} — ` ++
150
145
  `the local shell reads its config from that package's dist/, so declaring shell ` ++
151
- `config without the package installed would write nothing and leave the shell ` ++
152
- `configured as it shipped.`,
146
+ `config without the package installed would write nothing and leave the shell ` ++ `configured as it shipped.`,
153
147
  )
154
148
  }
155
149
  | Some(dir) =>
@@ -163,8 +157,7 @@ let emit = (
163
157
  if !NodeFs.existsSync(path) {
164
158
  JsError.throwWithMessage(
165
159
  `host UI config.json: ${HostShellDist.package} ships no ${fileName} at ${dir} — ` ++
166
- `there is no baseline to overlay, so the shell would boot with only the keys ` ++
167
- `declared here and none of the ones it expects.`,
160
+ `there is no baseline to overlay, so the shell would boot with only the keys ` ++ `declared here and none of the ones it expects.`,
168
161
  )
169
162
  }
170
163
  NodeFs.writeFileSync(baselinePath, NodeFs.readFileSync(path))
package/src/UiHints.res CHANGED
@@ -50,27 +50,23 @@ let emit = (
50
50
  | exception _ =>
51
51
  JsError.throwWithMessage(
52
52
  `host UI ${fileName}: cannot read the declared uiHintsFile at ${path} — ` ++
53
- `the shell fetches this file at boot, so a declaration pointing nowhere ` ++
54
- `applies no hints and says nothing about why.`,
53
+ `the shell fetches this file at boot, so a declaration pointing nowhere ` ++ `applies no hints and says nothing about why.`,
55
54
  )
56
55
  }
57
56
  switch source->JSON.parseOrThrow {
58
57
  | _ => ()
59
58
  | exception _ =>
60
59
  JsError.throwWithMessage(
61
- `host UI ${fileName}: the declared uiHintsFile at ${path} is not JSON — ` ++
62
- `the shell warns once and applies no hints, which reads as hints that do nothing.`,
60
+ `host UI ${fileName}: the declared uiHintsFile at ${path} is not JSON — ` ++ `the shell warns once and applies no hints, which reads as hints that do nothing.`,
63
61
  )
64
62
  }
65
63
  source
66
64
  })
67
65
 
68
- switch (
69
- switch dir {
70
- | Some(_) as given => given
71
- | None => HostShellDist.dir()
72
- }
73
- ) {
66
+ switch switch dir {
67
+ | Some(_) as given => given
68
+ | None => HostShellDist.dir()
69
+ } {
74
70
  | None =>
75
71
  // No shell installed is the ordinary case for a platform nobody points a
76
72
  // browser at; only a declaration makes the missing package an error.
@@ -78,8 +74,7 @@ let emit = (
78
74
  JsError.throwWithMessage(
79
75
  `host UI ${fileName}: cannot resolve ${HostShellDist.package} from ${NodeProcess.cwd()} — ` ++
80
76
  `the local shell reads its hints from that package's dist/, so declaring a ` ++
81
- `uiHintsFile without the package installed would write nothing and leave the ` ++
82
- `shell applying whatever the package shipped.`,
77
+ `uiHintsFile without the package installed would write nothing and leave the ` ++ `shell applying whatever the package shipped.`,
83
78
  )
84
79
  }
85
80
  | Some(dir) =>
@@ -148,8 +143,7 @@ let watch = (
148
143
  // away between the two — worth a line, not worth a throw.
149
144
  log.warn(
150
145
  ~comp="UiHints",
151
- `not watching ${base}: ${sourceDir} does not exist, so changes to the declared ` ++
152
- `uiHintsFile will need a restart`,
146
+ `not watching ${base}: ${sourceDir} does not exist, so changes to the declared ` ++ `uiHintsFile will need a restart`,
153
147
  )
154
148
  None
155
149
  } else {
package/src/UiSlots.res CHANGED
@@ -66,18 +66,15 @@ let emit = (
66
66
  | exception _ =>
67
67
  JsError.throwWithMessage(
68
68
  `host UI ${fileName}: cannot read the declared uiSlotsFile at ${path} — ` ++
69
- `the shell imports this file at boot, so a declaration pointing nowhere ` ++
70
- `registers no renderers and says nothing about why.`,
69
+ `the shell imports this file at boot, so a declaration pointing nowhere ` ++ `registers no renderers and says nothing about why.`,
71
70
  )
72
71
  }
73
72
  )
74
73
 
75
- switch (
76
- switch dir {
77
- | Some(_) as given => given
78
- | None => HostShellDist.dir()
79
- }
80
- ) {
74
+ switch switch dir {
75
+ | Some(_) as given => given
76
+ | None => HostShellDist.dir()
77
+ } {
81
78
  | None =>
82
79
  // No shell installed is the ordinary case for a platform nobody points a
83
80
  // browser at; only a declaration makes the missing package an error.
@@ -85,8 +82,7 @@ let emit = (
85
82
  JsError.throwWithMessage(
86
83
  `host UI ${fileName}: cannot resolve ${HostShellDist.package} from ${NodeProcess.cwd()} — ` ++
87
84
  `the local shell imports its slot renderers from that package's dist/, so ` ++
88
- `declaring a uiSlotsFile without the package installed would write nothing ` ++
89
- `and leave every mode drawing its own regions.`,
85
+ `declaring a uiSlotsFile without the package installed would write nothing ` ++ `and leave every mode drawing its own regions.`,
90
86
  )
91
87
  }
92
88
  | Some(dir) =>
@@ -132,11 +128,9 @@ let emit = (
132
128
  `onReload` runs only after a re-copy actually succeeded, so a subscriber cannot
133
129
  be told to re-import a file that did not change.
134
130
  */
135
- let watch = (
136
- ~uiSlotsFile: option<string>,
137
- ~dir: option<string>=?,
138
- ~onReload: unit => unit,
139
- ): option<NodeFs.watcher> =>
131
+ let watch = (~uiSlotsFile: option<string>, ~dir: option<string>=?, ~onReload: unit => unit): option<
132
+ NodeFs.watcher,
133
+ > =>
140
134
  uiSlotsFile->Option.flatMap(path => {
141
135
  // Named apart from the `~dir` above, which is where the file is SERVED. This
142
136
  // is where it is AUTHORED, and the two are never the same place — letting
@@ -150,8 +144,7 @@ let watch = (
150
144
  // away between the two — worth a line, not worth a throw.
151
145
  log.warn(
152
146
  ~comp="UiSlots",
153
- `not watching ${base}: ${sourceDir} does not exist, so changes to the declared ` ++
154
- `uiSlotsFile will need a restart`,
147
+ `not watching ${base}: ${sourceDir} does not exist, so changes to the declared ` ++ `uiSlotsFile will need a restart`,
155
148
  )
156
149
  None
157
150
  } else {
@@ -27,8 +27,7 @@ let log = ReventlessCore.Logger.fromEnv()
27
27
 
28
28
  // Mirrors AppSyncEventsSigner_Ops.pathSegment (aws) and the client's channel
29
29
  // normalizer: any char outside [A-Za-z0-9-] becomes `-`.
30
- let pathSegment = (s: string): string =>
31
- s->String.replaceRegExp(/[^A-Za-z0-9-]/g, "-")
30
+ let pathSegment = (s: string): string => s->String.replaceRegExp(/[^A-Za-z0-9-]/g, "-")
32
31
 
33
32
  let clientChannelPrefix = "/client/"
34
33
 
@@ -172,7 +171,10 @@ let broadcastStateChange = (~name: string, ~descriptor: JSON.t): unit => {
172
171
  // -- Inbound frames (subscribe side) -----------------------------------------
173
172
 
174
173
  let decodeStringField = (json: JSON.t, field: string): option<string> =>
175
- json->JSON.Decode.object->Option.flatMap(o => o->Dict.get(field))->Option.flatMap(JSON.Decode.string)
174
+ json
175
+ ->JSON.Decode.object
176
+ ->Option.flatMap(o => o->Dict.get(field))
177
+ ->Option.flatMap(JSON.Decode.string)
176
178
 
177
179
  /** Handle one client→server text frame on an established connection. */
178
180
  let handleFrame = (conn: connection, text: string): unit => {
@@ -7,10 +7,10 @@
7
7
  type api = unit
8
8
  type role = unit
9
9
 
10
- let makeApiResource = (
11
- ~name as _: string,
12
- ~opts as _: Pulumi.ComponentResource.options,
13
- ): (Pulumi.Output.t<api>, Pulumi.Output.t<role>) => (Pulumi.Output.make(()), Pulumi.Output.make(()))
10
+ let makeApiResource = (~name as _: string, ~opts as _: Pulumi.ComponentResource.options): (
11
+ Pulumi.Output.t<api>,
12
+ Pulumi.Output.t<role>,
13
+ ) => (Pulumi.Output.make(), Pulumi.Output.make())
14
14
 
15
15
  let generateFragment = (
16
16
  ~mutationEntries: array<ReventlessInfra.Api.mutationSchemaEntry>,
@@ -24,7 +24,7 @@ let defaultUser: Identity.t = {
24
24
  let adminUser: Identity.t = {
25
25
  userId: "local-admin",
26
26
  username: "admin",
27
- groups: ["Admin", "User"],
27
+ groups: [AdminGroup.name, "User"],
28
28
  provider: InMemory,
29
29
  }
30
30
 
@@ -33,8 +33,7 @@ let adminUser: Identity.t = {
33
33
  // A `dict<Identity.t>` keyed by `username`. A4 hydrates this from
34
34
  // `.reventless/users.yaml`; tests inject via `registerUser`.
35
35
 
36
- let initialUsers = () =>
37
- Dict.fromArray([("user", defaultUser), ("admin", adminUser)])
36
+ let initialUsers = () => Dict.fromArray([("user", defaultUser), ("admin", adminUser)])
38
37
 
39
38
  let users: ref<dict<Identity.t>> = ref(initialUsers())
40
39
 
@@ -43,8 +42,7 @@ let registerUser = (~username: string, ~identity: Identity.t): unit =>
43
42
 
44
43
  let resetUsers = (): unit => users := initialUsers()
45
44
 
46
- let lookupUser = (username: string): option<Identity.t> =>
47
- users.contents->Dict.get(username)
45
+ let lookupUser = (username: string): option<Identity.t> => users.contents->Dict.get(username)
48
46
 
49
47
  // ── Header parsing ────────────────────────────────────────────────────────
50
48
 
@@ -240,11 +238,10 @@ module Login = {
240
238
  `activeRole` narrows the minted token to one of the caller's own roles; unset
241
239
  mints exactly what it has always minted.
242
240
  */
243
- let issue = async (
244
- ~username: string,
245
- ~password: string,
246
- ~activeRole: option<string>=?,
247
- ): result<string, string> =>
241
+ let issue = async (~username: string, ~password: string, ~activeRole: option<string>=?): result<
242
+ string,
243
+ string,
244
+ > =>
248
245
  switch store.contents->Dict.get(username) {
249
246
  | Some({password: stored, identity}) if stored === password =>
250
247
  switch switch activeRole {
@@ -340,7 +337,6 @@ module Login = {
340
337
  }
341
338
  }
342
339
  }
343
-
344
340
  }
345
341
 
346
342
  // ── Provider implementation ───────────────────────────────────────────────
@@ -354,9 +350,7 @@ let _bearerToken = (header: string): option<string> =>
354
350
  None
355
351
  }
356
352
 
357
- let authenticate = async (
358
- ctx: ReventlessCore.Auth_Adapter.requestContext,
359
- ): Identity.authResult => {
353
+ let authenticate = async (ctx: ReventlessCore.Auth_Adapter.requestContext): Identity.authResult => {
360
354
  // Decoding order: Bearer (when signature valid) → X-User/X-Groups → default.
361
355
  // A *present* Bearer that fails verification is rejected (AuthError) rather
362
356
  // than falling through to defaultUser — otherwise a client whose token was
@@ -402,5 +396,7 @@ let authenticate = async (
402
396
  }
403
397
  }
404
398
 
405
- let make = (~name as _: string, ~opts as _: option<Pulumi.ComponentResource.options>=?): Pulumi.Output.t<authConfig> =>
406
- Pulumi.Output.make()
399
+ let make = (
400
+ ~name as _: string,
401
+ ~opts as _: option<Pulumi.ComponentResource.options>=?,
402
+ ): Pulumi.Output.t<authConfig> => Pulumi.Output.make()
@@ -8,6 +8,7 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
8
8
  import * as Pulumi from "@pulumi/pulumi";
9
9
  import * as Identity$Reventless from "@reventlessdev/reventless-spec/src/types/Identity.res.mjs";
10
10
  import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
11
+ import * as AdminGroup$Reventless from "@reventlessdev/reventless-spec/src/types/AdminGroup.res.mjs";
11
12
  import * as Auth_ActiveRole$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/Auth/Auth_ActiveRole.res.mjs";
12
13
 
13
14
  let defaultUser_groups = ["User"];
@@ -20,7 +21,7 @@ let defaultUser = {
20
21
  };
21
22
 
22
23
  let adminUser_groups = [
23
- "Admin",
24
+ AdminGroup$Reventless.name,
24
25
  "User"
25
26
  ];
26
27
 
@@ -14,40 +14,15 @@ open Reventless
14
14
  /**
15
15
  * One YAML entry. `groups` is required (use `[]` for an unprivileged user);
16
16
  * `userId` defaults to the username when omitted.
17
+ *
18
+ * The shape belongs to [AccountsManifest] rather than to this adapter: the AWS
19
+ * provisioning tool writes the same file, and two definitions of one file format
20
+ * is how a manifest starts working on one platform and failing on the other.
17
21
  */
18
- @schema
19
- type entry = {
20
- username: string,
21
- password: string,
22
- groups: array<string>,
23
- userId?: string,
24
- }
25
-
26
- // ── YAML parsing (eemeli/yaml) ──────────────────────────────────────────────
27
-
28
- @module("yaml") external _yamlParse: string => unknown = "parse"
29
-
30
- let _entriesSchema = S.array(entrySchema)
22
+ type entry = AccountsManifest.entry
31
23
 
32
- /** Parses a YAML document string into entries (best-effort). */
33
- let parseString = (yamlText: string): result<array<entry>, string> =>
34
- try {
35
- let json = _yamlParse(yamlText)->Obj.magic
36
- Ok(S.parseOrThrow(json, ~to=_entriesSchema))
37
- } catch {
38
- | JsExn(err) => Error(JsExn.message(err)->Option.getOr("YAML parse error"))
39
- | _ => Error("YAML parse error")
40
- }
41
-
42
- /** Reads + parses a YAML file. */
43
- let parseFile = (path: string): result<array<entry>, string> =>
44
- try {
45
- let text = NodeFs.readFileSync(path)
46
- parseString(text)
47
- } catch {
48
- | JsExn(err) => Error(JsExn.message(err)->Option.getOr(`Cannot read ${path}`))
49
- | _ => Error(`Cannot read ${path}`)
50
- }
24
+ let parseString = AccountsManifest.parseString
25
+ let parseFile = AccountsManifest.parseFile
51
26
 
52
27
  // ── Resolution & loading ────────────────────────────────────────────────────
53
28
 
@@ -62,7 +37,7 @@ let _registerEntries = (entries: array<entry>): unit =>
62
37
  LocalAuth.Login.setCredentials(~username, ~password, ~identity=id)
63
38
  })
64
39
 
65
- let _defaultPath = (): string => NodePath.join([NodeProcess.cwd(), ".reventless/users.yaml"])
40
+ let _defaultPath = AccountsManifest.defaultPath
66
41
 
67
42
  /**
68
43
  * Hydrates the Login store. Returns the resolution path actually used
@@ -83,11 +58,10 @@ let resolved: ref<bool> = ref(false)
83
58
 
84
59
  let resetResolution = (): unit => resolved := false
85
60
 
86
- let load = (
87
- ~users: option<array<entry>>=?,
88
- ~usersFile: option<string>=?,
89
- (),
90
- ): result<resolution, string> => {
61
+ let load = (~users: option<array<entry>>=?, ~usersFile: option<string>=?, ()): result<
62
+ resolution,
63
+ string,
64
+ > => {
91
65
  resolved := true
92
66
  switch users {
93
67
  | Some(entries) =>
@@ -109,8 +83,7 @@ let load = (
109
83
  })
110
84
  } else {
111
85
  Console.log(
112
- "[LocalAuth] no users configured — POST /__inmemory/login will reject all. " ++
113
- "Provide ~users / ~usersFile or create .reventless/users.yaml relative to cwd.",
86
+ "[LocalAuth] no users configured — POST /__inmemory/login will reject all. " ++ "Provide ~users / ~usersFile or create .reventless/users.yaml relative to cwd.",
114
87
  )
115
88
  Ok(Empty)
116
89
  }
@@ -1,66 +1,10 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
- import * as S from "sury/src/S.res.mjs";
4
- import * as Sury from "sury";
5
- import * as Yaml from "yaml";
6
3
  import * as Nodefs from "node:fs";
7
- import * as Nodepath from "node:path";
8
- import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
9
4
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
10
5
  import * as Stdlib_Result from "@rescript/runtime/lib/es6/Stdlib_Result.js";
11
- import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
12
6
  import * as LocalAuth$ReventlessLocal from "./LocalAuth.res.mjs";
13
-
14
- let entrySchema = Sury.$schema(s => ({
15
- username: s.m(Sury.string),
16
- password: s.m(Sury.string),
17
- groups: s.m(Sury.array(Sury.string)),
18
- userId: s.m(Sury.$option(Sury.string))
19
- }));
20
-
21
- let _entriesSchema = Sury.array(entrySchema);
22
-
23
- function parseString(yamlText) {
24
- try {
25
- let json = Yaml.parse(yamlText);
26
- return {
27
- TAG: "Ok",
28
- _0: S.parseOrThrow(json, _entriesSchema)
29
- };
30
- } catch (raw_err) {
31
- let err = Primitive_exceptions.internalToException(raw_err);
32
- if (err.RE_EXN_ID === "JsExn") {
33
- return {
34
- TAG: "Error",
35
- _0: Stdlib_Option.getOr(Stdlib_JsExn.message(err._1), "YAML parse error")
36
- };
37
- } else {
38
- return {
39
- TAG: "Error",
40
- _0: "YAML parse error"
41
- };
42
- }
43
- }
44
- }
45
-
46
- function parseFile(path) {
47
- try {
48
- return parseString(Nodefs.readFileSync(path, "utf8"));
49
- } catch (raw_err) {
50
- let err = Primitive_exceptions.internalToException(raw_err);
51
- if (err.RE_EXN_ID === "JsExn") {
52
- return {
53
- TAG: "Error",
54
- _0: Stdlib_Option.getOr(Stdlib_JsExn.message(err._1), `Cannot read ` + path)
55
- };
56
- } else {
57
- return {
58
- TAG: "Error",
59
- _0: `Cannot read ` + path
60
- };
61
- }
62
- }
63
- }
7
+ import * as AccountsManifest$Reventless from "@reventlessdev/reventless-spec/src/types/AccountsManifest.res.mjs";
64
8
 
65
9
  function _registerEntries(entries) {
66
10
  entries.forEach(param => {
@@ -77,10 +21,6 @@ function _registerEntries(entries) {
77
21
  });
78
22
  }
79
23
 
80
- function _defaultPath() {
81
- return Nodepath.join(process.cwd(), ".reventless/users.yaml");
82
- }
83
-
84
24
  let resolved = {
85
25
  contents: false
86
26
  };
@@ -99,7 +39,7 @@ function load(users, usersFile, param) {
99
39
  };
100
40
  }
101
41
  if (usersFile !== undefined) {
102
- return Stdlib_Result.map(parseFile(usersFile), entries => {
42
+ return Stdlib_Result.map(AccountsManifest$Reventless.parseFile(usersFile), entries => {
103
43
  _registerEntries(entries);
104
44
  return {
105
45
  TAG: "UsersFile",
@@ -107,9 +47,9 @@ function load(users, usersFile, param) {
107
47
  };
108
48
  });
109
49
  }
110
- let defaultPath = _defaultPath();
50
+ let defaultPath = AccountsManifest$Reventless.defaultPath();
111
51
  if (Nodefs.existsSync(defaultPath)) {
112
- return Stdlib_Result.map(parseFile(defaultPath), entries => {
52
+ return Stdlib_Result.map(AccountsManifest$Reventless.parseFile(defaultPath), entries => {
113
53
  _registerEntries(entries);
114
54
  return {
115
55
  TAG: "DefaultFile",
@@ -132,9 +72,13 @@ function autoLoadOnce() {
132
72
  }
133
73
  }
134
74
 
75
+ let parseString = AccountsManifest$Reventless.parseString;
76
+
77
+ let parseFile = AccountsManifest$Reventless.parseFile;
78
+
79
+ let _defaultPath = AccountsManifest$Reventless.defaultPath;
80
+
135
81
  export {
136
- entrySchema,
137
- _entriesSchema,
138
82
  parseString,
139
83
  parseFile,
140
84
  _registerEntries,
@@ -144,4 +88,4 @@ export {
144
88
  load,
145
89
  autoLoadOnce,
146
90
  }
147
- /* entrySchema Not a pure module */
91
+ /* node:fs Not a pure module */
@@ -30,11 +30,10 @@ let postgres = async (~connection, ~resetOnStart=false) => {
30
30
  }
31
31
  let initialCount =
32
32
  (await ReventlessPostgres.EventLogStorage_Postgres.countAll(pool)) +
33
- (await ReventlessPostgres.DcbEventLogStorage_Postgres.countAll(pool))
33
+ (await ReventlessPostgres.DcbEventLogStorage_Postgres.countAll(pool))
34
34
  Postgres({pool, initialCount, connection})
35
35
  }
36
36
 
37
-
38
37
  // Deletes a file if it exists. No-op if the path is missing.
39
38
 
40
39
  let removeFileIfExists = (path: string) =>
@@ -35,15 +35,14 @@ let extractIdentity = (ctx: JSON.t): Reventless.Identity.t => {
35
35
  // Returns a `CommandRejected` outcome with a fresh msgId so the GraphQL union
36
36
  // resolves consistently — matches the existing CommandResult contract.
37
37
 
38
- let rejectForbidden = (~field: string): JSON.t =>
39
- JSON.Object(
40
- Dict.fromArray([
41
- ("__typename", JSON.String("CommandRejected")),
42
- ("msgId", JSON.String(ReventlessCore.Message.uuid())),
43
- ("errorCode", JSON.String("Forbidden")),
44
- ("errorDetail", JSON.String(`Mutation.${field}: identity is not authorized`)),
45
- ]),
46
- )
38
+ let rejectForbidden = (~field: string): JSON.t => JSON.Object(
39
+ Dict.fromArray([
40
+ ("__typename", JSON.String("CommandRejected")),
41
+ ("msgId", JSON.String(ReventlessCore.Message.uuid())),
42
+ ("errorCode", JSON.String("Forbidden")),
43
+ ("errorDetail", JSON.String(`Mutation.${field}: identity is not authorized`)),
44
+ ]),
45
+ )
47
46
 
48
47
  // -- Plugin status gate -------------------------------------------------------
49
48
  // Set by the platform after Admin.construct to wire a per-mutation lookup against
@@ -61,15 +60,18 @@ let pluginStatusGate: ref<option<string => option<(string, string)>>> = ref(None
61
60
  let setPluginStatusGate = fn => pluginStatusGate := Some(fn)
62
61
  let resetPluginStatusGate = () => pluginStatusGate := None
63
62
 
64
- let rejectPluginStatus = (~field: string, ~errorCode: string, ~detail: string): JSON.t =>
65
- JSON.Object(
66
- Dict.fromArray([
67
- ("__typename", JSON.String("CommandRejected")),
68
- ("msgId", JSON.String(ReventlessCore.Message.uuid())),
69
- ("errorCode", JSON.String(errorCode)),
70
- ("errorDetail", JSON.String(`Mutation.${field}: ${detail}`)),
71
- ]),
72
- )
63
+ let rejectPluginStatus = (
64
+ ~field: string,
65
+ ~errorCode: string,
66
+ ~detail: string,
67
+ ): JSON.t => JSON.Object(
68
+ Dict.fromArray([
69
+ ("__typename", JSON.String("CommandRejected")),
70
+ ("msgId", JSON.String(ReventlessCore.Message.uuid())),
71
+ ("errorCode", JSON.String(errorCode)),
72
+ ("errorDetail", JSON.String(`Mutation.${field}: ${detail}`)),
73
+ ]),
74
+ )
73
75
 
74
76
  let checkPluginStatus = (~field: string): option<JSON.t> =>
75
77
  switch pluginStatusGate.contents {
@@ -92,8 +94,7 @@ let checkPluginStatus = (~field: string): option<JSON.t> =>
92
94
  let syntheticCommand = (cname: string, ~hasPayload: bool): unknown =>
93
95
  hasPayload ? {"TAG": cname}->Obj.magic : cname->Obj.magic
94
96
 
95
- let capitalize = s =>
96
- s->String.charAt(0)->String.toUpperCase ++ s->String.slice(~start=1)
97
+ let capitalize = s => s->String.charAt(0)->String.toUpperCase ++ s->String.slice(~start=1)
97
98
 
98
99
  // -- CommandResult SDL types --------------------------------------------------
99
100
  // Source of truth lives in GraphQL_FragmentGenerator.commandResultSdlTypes so
@@ -194,7 +195,10 @@ let register = (
194
195
  // Aggregate commands target a specific instance — prepend id: ID!
195
196
  let sdlFields = fields->Array.map(field => {
196
197
  let variantIndex = variantIndexForField(commandSchema, ~field)
197
- let sdl = deriveSdlField(~fieldName=field, extractVariantSchema(commandSchema, ~index=variantIndex))
198
+ let sdl = deriveSdlField(
199
+ ~fieldName=field,
200
+ extractVariantSchema(commandSchema, ~index=variantIndex),
201
+ )
198
202
  if sdl->String.includes("(") {
199
203
  sdl->String.replace(`${field}(`, `${field}(id: ID!, `)
200
204
  } else {
@@ -214,7 +218,11 @@ let register = (
214
218
  | _ => false
215
219
  }
216
220
  let commandName = extractCommandName(field)
217
- let resolver: ReventlessGraphqlServer.GraphQL_ServerInstance.resolverFn = async (_root, args, ctx) => {
221
+ let resolver: ReventlessGraphqlServer.GraphQL_ServerInstance.resolverFn = async (
222
+ _root,
223
+ args,
224
+ ctx,
225
+ ) => {
218
226
  switch handlerRef.contents {
219
227
  | Some(generateCommand) =>
220
228
  switch checkPluginStatus(~field) {
@@ -265,19 +273,27 @@ let registerDcb = (
265
273
  // non-first constructor of a multi-command slice.
266
274
  let constructorNames = Reventless.DcbTag.extractAllVariantNames(commandSchema->Obj.magic)
267
275
  let variantIndex = variantIndexForField(commandSchema, ~field=fieldName)
268
- let variantSchema = extractVariantSchema(commandSchema, ~index=variantIndex >= 0 ? variantIndex : 0)
276
+ let variantSchema = extractVariantSchema(
277
+ commandSchema,
278
+ ~index=variantIndex >= 0 ? variantIndex : 0,
279
+ )
269
280
  let sdlFields = [deriveSdlField(~fieldName, variantSchema)]
270
281
 
271
282
  // Extract TAG (variant constructor name) for routing
272
283
  let tag =
273
- (variantIndex >= 0 ? constructorNames->Array.get(variantIndex) : constructorNames->Array.get(0))
274
- ->Option.getOr(fieldName)
284
+ (
285
+ variantIndex >= 0 ? constructorNames->Array.get(variantIndex) : constructorNames->Array.get(0)
286
+ )->Option.getOr(fieldName)
275
287
  let hasPayload = Reventless.DcbTag.isVariantPayloadBearing(commandSchema->Obj.magic, tag)
276
288
 
277
289
  let handlerRef = ref(None)
278
290
  handlerRefs->Dict.set(fieldName, handlerRef)
279
291
 
280
- let resolver: ReventlessGraphqlServer.GraphQL_ServerInstance.resolverFn = async (_root, args, ctx) => {
292
+ let resolver: ReventlessGraphqlServer.GraphQL_ServerInstance.resolverFn = async (
293
+ _root,
294
+ args,
295
+ ctx,
296
+ ) => {
281
297
  switch handlerRef.contents {
282
298
  | Some(generateCommand) =>
283
299
  switch checkPluginStatus(~field=fieldName) {
@@ -25,7 +25,11 @@ let pendingQueueRegistry: dict<ref<array<pendingCall>>> = Dict.make()
25
25
  // Phase 1: Register SDL + resolver stub synchronously (before server starts).
26
26
  // Also pre-registers a queuing forwarder in receiveRegistry so callers can
27
27
  // invoke receive immediately; calls are parked until bindReceive drains them.
28
- let register = (~fieldName: string, ~externalInputSchema: S.t<unknown>, ~server: ReventlessGraphqlServer.GraphQL_ServerInstance.t) => {
28
+ let register = (
29
+ ~fieldName: string,
30
+ ~externalInputSchema: S.t<unknown>,
31
+ ~server: ReventlessGraphqlServer.GraphQL_ServerInstance.t,
32
+ ) => {
29
33
  // The mutation field returns CommandResult, so its union members must exist in
30
34
  // this scope's document. Registered here rather than relying on a plugin's
31
35
  // command handlers having registered them first — a plugin whose only mutation
@@ -53,11 +57,17 @@ let register = (~fieldName: string, ~externalInputSchema: S.t<unknown>, ~server:
53
57
  })
54
58
  receiveRegistry->Dict.set(fieldName, queuingReceive)
55
59
 
56
- let resolver: ReventlessGraphqlServer.GraphQL_ServerInstance.resolverFn = async (_root, args, _ctx) => {
60
+ let resolver: ReventlessGraphqlServer.GraphQL_ServerInstance.resolverFn = async (
61
+ _root,
62
+ args,
63
+ _ctx,
64
+ ) => {
57
65
  let inputJson: JSON.t = args->Obj.magic
58
66
  let receive = receiveRegistry->Dict.getUnsafe(fieldName)
59
67
  let result = await receive(inputJson)
60
- result->InboundTranslationSlice_Callback.receiveResultToOutcome->CommandTopic.commandOutcomeToJson
68
+ result
69
+ ->InboundTranslationSlice_Callback.receiveResultToOutcome
70
+ ->CommandTopic.commandOutcomeToJson
61
71
  }
62
72
 
63
73
  let resolvers = Dict.make()