@reventlessdev/reventless-infra 3.0.0-alpha.161 → 3.0.0-alpha.163

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 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.163 (2026-09-11)
7
+
8
+ * feat(spec)!: making a principal is a capability, so the provider is replaceable ([3e59b9b](https://github.com/ReventlessDev/reventless-core/commit/3e59b9b665cc7e4b6b6f156b63b1b6bd0c6181ca))
9
+
10
+ ### BREAKING CHANGES
11
+
12
+ * Capabilities.t gains an identityProvider member. A platform
13
+ that builds the record literally no longer compiles until it supplies one;
14
+ Capabilities.none.identityProvider is the refusing value to pass while no
15
+ backend exists. Code that spreads Capabilities.none is unaffected.
16
+
17
+
18
+
19
+ # 3.0.0-alpha.162 (2026-09-09)
20
+
21
+ **Note:** Version bump only for package @reventlessdev/reventless-infra
22
+
23
+
24
+
25
+
26
+
6
27
  # 3.0.0-alpha.161 (2026-09-08)
7
28
 
8
29
  **Note:** Version bump only for package @reventlessdev/reventless-infra
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-infra",
3
- "version": "3.0.0-alpha.161",
3
+ "version": "3.0.0-alpha.163",
4
4
  "description": "Infrastructure types for Reventless framework",
5
5
  "license": "Apache-2.0",
6
6
  "jest": {
@@ -16,10 +16,10 @@
16
16
  "sury": "11.0.0-rc.2",
17
17
  "sury-ppx": "11.0.0-rc.2",
18
18
  "uuid": "^13.0.0",
19
- "@reventlessdev/rescript-effect": "0.1.0-alpha.32",
19
+ "@reventlessdev/rescript-effect": "0.1.0-alpha.33",
20
+ "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.20",
20
21
  "@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
21
- "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
22
- "@reventlessdev/reventless-spec": "3.0.0-alpha.133"
22
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.135"
23
23
  },
24
24
  "devDependencies": {
25
25
  "rescript": "12.3.0",
@@ -4,8 +4,10 @@
4
4
  let noApiId: S.Metadata.Id.t<bool> = S.Metadata.Id.make(~namespace="api", ~name="noApi")
5
5
 
6
6
  /** Internal sury metadata ID used to mark specific variant names excluded from API exposure. */
7
- let noApiVariantsId: S.Metadata.Id.t<Set.t<string>> =
8
- S.Metadata.Id.make(~namespace="api", ~name="noApiVariants")
7
+ let noApiVariantsId: S.Metadata.Id.t<Set.t<string>> = S.Metadata.Id.make(
8
+ ~namespace="api",
9
+ ~name="noApiVariants",
10
+ )
9
11
 
10
12
  /** The same exclusion, carried by the excluded *member* rather than by the union
11
13
  it sits in — so it survives a variant spread.
@@ -16,12 +18,13 @@ let noApiVariantsId: S.Metadata.Id.t<Set.t<string>> =
16
18
  host publishes a command its author marked internal. A mark on the member
17
19
  travels, because the member is what is spliced — the host's union holds the
18
20
  same schema object. */
19
- let noApiVariantId: S.Metadata.Id.t<bool> =
20
- S.Metadata.Id.make(~namespace="api", ~name="noApiVariant")
21
+ let noApiVariantId: S.Metadata.Id.t<bool> = S.Metadata.Id.make(
22
+ ~namespace="api",
23
+ ~name="noApiVariant",
24
+ )
21
25
 
22
26
  /** PPX helper: attaches the noApi flag to a command schema. Called by generated code. */
23
- let markNoApi = (schema: S.t<'a>): S.t<'a> =>
24
- schema->S.Metadata.set(~id=noApiId, true)
27
+ let markNoApi = (schema: S.t<'a>): S.t<'a> => schema->S.Metadata.set(~id=noApiId, true)
25
28
 
26
29
  /** The constructor a union member stands for: the `TAG` literal of a
27
30
  payload-bearing variant, or the bare literal a payload-less one compiles to. */
@@ -42,7 +42,11 @@ type publishJsons = array<Reventless.Message.commandJson> => promise<unit>
42
42
  Publishes a stream of serialized command envelopes as an `Effect.t`.
43
43
  Use this for high-throughput or streaming command pipelines.
44
44
  */
45
- type publishJsonsStream = Stream.t<Reventless.Message.commandJson, string, unit> => Effect.t<unit, string, unit>
45
+ type publishJsonsStream = Stream.t<Reventless.Message.commandJson, string, unit> => Effect.t<
46
+ unit,
47
+ string,
48
+ unit,
49
+ >
46
50
 
47
51
  /**
48
52
  A command together with an idempotency reference string.
@@ -25,8 +25,7 @@ let _outputsStore = _makeWeakMap()
25
25
  let _operationsStore = _makeWeakMap()
26
26
 
27
27
  /** Access the deploy-time outputs record for this component. */
28
- let outputs = (self: t<'component, 'outputs, 'operations>): 'outputs =>
29
- _outputsStore->_get(self)
28
+ let outputs = (self: t<'component, 'outputs, 'operations>): 'outputs => _outputsStore->_get(self)
30
29
 
31
30
  /**
32
31
  Access the deploy-time outputs wrapped in an `Output.t` (resolved asynchronously).
@@ -126,5 +126,9 @@ type component = Component.t<t, outputs, operations>
126
126
 
127
127
  module type T = {
128
128
  type component = component
129
- let make: (~name: string, ~indexes: array<string>=?, ~opts: Pulumi.ComponentResource.options=?) => component
129
+ let make: (
130
+ ~name: string,
131
+ ~indexes: array<string>=?,
132
+ ~opts: Pulumi.ComponentResource.options=?,
133
+ ) => component
130
134
  }
@@ -14,18 +14,17 @@ Same shape as `ReventlessCore.Monitoring.use`: a module-level registry consulted
14
14
  by an emitted call, so deploy-time extension becomes *registration*, not *file
15
15
  editing*. See `docs/plans/done/deploy-bootstrap-seam.md`.
16
16
  */
17
-
18
17
  /**
19
18
  Ordered phases at which bootstrap contributions run during a deploy program.
20
19
  */
21
20
  type phase =
22
- | /** before `deployPlatform` / `deployPlugin` — seam registration and other
21
+ /** before `deployPlatform` / `deployPlugin` — seam registration and other
23
22
  ordering-sensitive activations that must precede the platform/plugin
24
23
  graph build */
25
- PreDeploy
26
- | /** after the platform/plugin graph is registered — exports, cross-stack
24
+ | PreDeploy
25
+ /** after the platform/plugin graph is registered — exports, cross-stack
27
26
  output emission */
28
- PostDeploy
27
+ | PostDeploy
29
28
 
30
29
  type contribution = unit => unit
31
30
 
@@ -48,4 +48,8 @@ type publishJsonStreamItem = {
48
48
  Publishes a stream of event items as an `Effect.t`.
49
49
  Use this for high-throughput or streaming event pipelines.
50
50
  */
51
- type publishJsonStream = Stream.t<publishJsonStreamItem, string, unit> => Effect.t<unit, string, unit>
51
+ type publishJsonStream = Stream.t<publishJsonStreamItem, string, unit> => Effect.t<
52
+ unit,
53
+ string,
54
+ unit,
55
+ >
@@ -10,7 +10,10 @@ Creates a schedule in the underlying scheduling service (e.g. EventBridge Schedu
10
10
  - `array<Adapter.resolvedResource>` — resolved scheduler resources (ARNs, etc.)
11
11
  - `Reventless.Schedule.schedule` — the schedule definition to create
12
12
  */
13
- type createSchedule = (array<Adapter.resolvedResource>, Reventless.Schedule.schedule) => promise<unit>
13
+ type createSchedule = (
14
+ array<Adapter.resolvedResource>,
15
+ Reventless.Schedule.schedule,
16
+ ) => promise<unit>
14
17
 
15
18
  /**
16
19
  Deletes a schedule by name from the underlying scheduling service.
@@ -145,14 +145,12 @@ module NoDelegate = {
145
145
 
146
146
  let commandSchema = S.unit
147
147
  let moduleUrl: string = %raw(`import.meta.url`)
148
- let commandAuthorization = (_: command): Reventless.Authorization.permission =>
149
- AllowAuthenticated
148
+ let commandAuthorization = (_: command): Reventless.Authorization.permission => AllowAuthenticated
150
149
  type lifecycleState = unit
151
150
  let commandTransition = (_: command): Reventless.Transition.t<lifecycleState> => Unrestricted
152
151
  let traits: array<Reventless.Trait.t> = []
153
152
  }
154
153
 
155
-
156
154
  open PluginExtensionPointSpec
157
155
 
158
156
  type extensionPointName = string
@@ -268,7 +266,12 @@ module Make = (MappingImpl: Mapping): (
268
266
  }
269
267
 
270
268
  let encodeExtensionPointCommandJson = (commandJson, ~id, ~extensionPointName, ~action) => {
271
- compLog(`Extension(${extensionPointName})`, `${action}: ${commandJson->Reventless.Message.variantNameOfJson->Reventless.AnsiStyle.bold}(${id})`)
269
+ compLog(
270
+ `Extension(${extensionPointName})`,
271
+ `${action}: ${commandJson
272
+ ->Reventless.Message.variantNameOfJson
273
+ ->Reventless.AnsiStyle.bold}(${id})`,
274
+ )
272
275
  {
273
276
  Reventless.Message.id,
274
277
  meta: encodeMeta(meta, extensionPointName),
@@ -284,10 +287,7 @@ module Make = (MappingImpl: Mapping): (
284
287
  mapIncomingEventImpl(id, event, meta, pluginDef, queryEngine)->Array.map(x =>
285
288
  switch x {
286
289
  | PublishAggregateCommand(targetId, targetCmd) =>
287
- AbstractPublishAggregateCommand(
288
- delegateName,
289
- targetCmd->encodeTargetCommandJson(targetId),
290
- )
290
+ AbstractPublishAggregateCommand(delegateName, targetCmd->encodeTargetCommandJson(targetId))
291
291
  | PublishAggregateCommandAsync(promise) =>
292
292
  let toCommandJson = async promise => {
293
293
  let (targetId, targetCmd) = await promise
@@ -319,8 +319,7 @@ module Make = (MappingImpl: Mapping): (
319
319
  targetCmd->encodeTargetCommandJson(derivePartitionId(targetCmd)),
320
320
  ))
321
321
  AbstractPublishAggregateCommandsAsync(promise->toCommandJsons)
322
- | PublishExtensionPointCommand(id, command)
323
- if Spec.name == PluginExtensionPointSpec.name =>
322
+ | PublishExtensionPointCommand(id, command) if Spec.name == PluginExtensionPointSpec.name =>
324
323
  AbstractPublishPluginExtensionPointCommand(
325
324
  command->encodeExtensionPointCommand(
326
325
  ~id,
@@ -364,17 +363,20 @@ module Make = (MappingImpl: Mapping): (
364
363
 
365
364
  let doMapOutgoingEvent = (mapOutgoingEventImpl, targetEvent'Json, pluginDef) => {
366
365
  let tag = variantTagOfEnvelope(targetEvent'Json)
366
+
367
367
  // Not this mapping's concern — skip without decoding.
368
368
  if !(acceptedTags->Array.includes(tag)) {
369
369
  []
370
370
  } else {
371
371
  let {id, meta, event} =
372
- targetEvent'Json->Reventless.Message.decodeEvent'(
373
- Delegate.Id.schema,
374
- Delegate.eventSchema,
375
- )
372
+ targetEvent'Json->Reventless.Message.decodeEvent'(Delegate.Id.schema, Delegate.eventSchema)
376
373
  let encodeExtensionPointCommandJson = (commandJson, ~id, ~extensionPointName, ~action) => {
377
- compLog(`Extension(${delegateName})`, `${action}: ${commandJson->Reventless.Message.variantNameOfJson->Reventless.AnsiStyle.bold}(${id})`)
374
+ compLog(
375
+ `Extension(${delegateName})`,
376
+ `${action}: ${commandJson
377
+ ->Reventless.Message.variantNameOfJson
378
+ ->Reventless.AnsiStyle.bold}(${id})`,
379
+ )
378
380
  {
379
381
  Reventless.Message.id,
380
382
  meta: encodeMeta(meta, extensionPointName),
@@ -389,8 +391,7 @@ module Make = (MappingImpl: Mapping): (
389
391
 
390
392
  mapOutgoingEventImpl(id->Delegate.Id.toString, event, meta, pluginDef)->Array.map(x =>
391
393
  switch x {
392
- | PublishExtensionPointCommand(id, command)
393
- if Spec.name == PluginExtensionPointSpec.name =>
394
+ | PublishExtensionPointCommand(id, command) if Spec.name == PluginExtensionPointSpec.name =>
394
395
  AbstractPublishPluginExtensionPointCommand(
395
396
  command->encodeExtensionPointCommand(
396
397
  ~id,
@@ -119,7 +119,11 @@ module type T = {
119
119
 
120
120
  /** Pre-encodes a batch of typed EP commands for the runtime. */
121
121
  let mapIncomingCommands: (
122
- array<CommandTopic.topicItem<Reventless.Message.command'<Reventless.Id.String.t, ExtensionPoint.command>>>,
122
+ array<
123
+ CommandTopic.topicItem<
124
+ Reventless.Message.command'<Reventless.Id.String.t, ExtensionPoint.command>,
125
+ >,
126
+ >,
123
127
  Reventless.Schedule.create,
124
128
  Reventless.Schedule.delete,
125
129
  Reventless.QueryEngine.operations,
@@ -162,12 +166,20 @@ module Make = (MappingImpl: Mapping): (
162
166
  queryEngine,
163
167
  ) =>
164
168
  topicItems
165
- ->Array.map(({CommandTopic.reference: reference, command: {Reventless.Message.id: id, command, meta}}) =>
169
+ ->Array.map(({
170
+ CommandTopic.reference: reference,
171
+ command: {Reventless.Message.id: id, command, meta},
172
+ }) =>
166
173
  mapIncomingEventImpl(id->Reventless.Id.String.toString, command, meta)->Array.map(x =>
167
174
  switch x {
168
175
  | PublishCommand(targetId, targetCmd) =>
169
176
  let cmdJson = targetCmd->Reventless.Message.encode(Delegate.commandSchema)
170
- compLog(`ExtensionPoint(${extensionPointName})`, `EP→${delegateName}: ${cmdJson->Reventless.Message.variantNameOfJson->Reventless.AnsiStyle.bold}(${targetId})`)
177
+ compLog(
178
+ `ExtensionPoint(${extensionPointName})`,
179
+ `EP→${delegateName}: ${cmdJson
180
+ ->Reventless.Message.variantNameOfJson
181
+ ->Reventless.AnsiStyle.bold}(${targetId})`,
182
+ )
171
183
 
172
184
  AbstractPublishCommand(
173
185
  delegateName,
@@ -211,15 +223,13 @@ module Make = (MappingImpl: Mapping): (
211
223
  queryEngine,
212
224
  ) => {
213
225
  let tag = variantTagOfEnvelope(targetEventJson')
226
+
214
227
  // Not this mapping's concern — skip without decoding.
215
228
  if !(acceptedTags->Array.includes(tag)) {
216
229
  []
217
230
  } else {
218
231
  let {id, meta, event} =
219
- targetEventJson'->Reventless.Message.decodeEvent'(
220
- Delegate.Id.schema,
221
- Delegate.eventSchema,
222
- )
232
+ targetEventJson'->Reventless.Message.decodeEvent'(Delegate.Id.schema, Delegate.eventSchema)
223
233
  mapOutgoingEventImpl(
224
234
  id->Delegate.Id.toString,
225
235
  event,
@@ -229,7 +239,12 @@ module Make = (MappingImpl: Mapping): (
229
239
  switch eventAction {
230
240
  | PublishEvent(id, event) =>
231
241
  let eventJson = event->Reventless.Message.encode(Spec.eventSchema)
232
- compLog(`ExtensionPoint(${extensionPointName})`, `mapped ${delegateName} → ${eventJson->Reventless.Message.variantNameOfJson->Reventless.AnsiStyle.bold}(${id})`)
242
+ compLog(
243
+ `ExtensionPoint(${extensionPointName})`,
244
+ `mapped ${delegateName} → ${eventJson
245
+ ->Reventless.Message.variantNameOfJson
246
+ ->Reventless.AnsiStyle.bold}(${id})`,
247
+ )
233
248
  let meta = {
234
249
  ...meta,
235
250
  service: Spec.name,
@@ -241,7 +256,12 @@ module Make = (MappingImpl: Mapping): (
241
256
  let toEvent' = async promise => {
242
257
  let (id, event) = await promise
243
258
  let eventJson = event->Reventless.Message.encode(Spec.eventSchema)
244
- compLog(`ExtensionPoint(${extensionPointName})`, `mapped ${delegateName} → ${eventJson->Reventless.Message.variantNameOfJson->Reventless.AnsiStyle.bold}(${id}) (async)`)
259
+ compLog(
260
+ `ExtensionPoint(${extensionPointName})`,
261
+ `mapped ${delegateName} → ${eventJson
262
+ ->Reventless.Message.variantNameOfJson
263
+ ->Reventless.AnsiStyle.bold}(${id}) (async)`,
264
+ )
245
265
  let eventJson' = Reventless.Message.composeEventJson'(id, meta, eventJson) // TODO: check if meta is correct
246
266
  (id, meta, eventJson')
247
267
  }
@@ -249,8 +269,8 @@ module Make = (MappingImpl: Mapping): (
249
269
  | HandleDirective(handler, directive) =>
250
270
  compLog(`ExtensionPoint(${extensionPointName})`, `mapped ${delegateName} → directive`)
251
271
 
252
- AbstractHandleDirective(() =>
253
- handler(createSchedule, deleteSchedule, queryEngine, directive)
272
+ AbstractHandleDirective(
273
+ () => handler(createSchedule, deleteSchedule, queryEngine, directive),
254
274
  )
255
275
  }
256
276
  )
@@ -20,6 +20,8 @@
20
20
  // remains accessible inside module type T even though T re-declares
21
21
  // `module ExtensionPoint` with a different local type, which would otherwise
22
22
  // shadow the package-level ExtensionPoint.outputs.
23
+ // Type alias so `deployPlugin` can reference `Plugin.outputs` inside module type T,
24
+ // where `module Plugin: Plugin.T` shadows the package-level Plugin module.
23
25
  /**
24
26
  Abstract factory interface for creating Reventless components without coupling
25
27
  application code to a specific infrastructure provider.
@@ -44,9 +46,6 @@ module Platform = ReventlessAws.Platform.Make(AwsConfig)
44
46
  module App = CatalogPlugin.Make(Platform)
45
47
  ```
46
48
  */
47
-
48
- // Type alias so `deployPlugin` can reference `Plugin.outputs` inside module type T,
49
- // where `module Plugin: Plugin.T` shadows the package-level Plugin module.
50
49
  type pluginOutputs = Plugin.outputs
51
50
 
52
51
  // A storage bucket a deployment declares to be served read-only to the UI under
@@ -110,6 +109,7 @@ type capability =
110
109
  | ObjectStore({plugin: string, store: string})
111
110
  | Geocoding
112
111
  | Messaging
112
+ | IdentityProvider
113
113
 
114
114
  // A provisioned geocoding place index, as returned by the framework's geocoding
115
115
  // capability helper. A record rather than a bare name so the handle can grow
@@ -286,13 +286,13 @@ module type T = {
286
286
  Spec: Reventless.Aggregate.Spec,
287
287
  Behavior: Reventless.Behavior.T with module Spec := Spec,
288
288
  EventMappings: EventMapper.Mappings with module Target := Spec,
289
- ) => Aggregate.T with type api = api
289
+ ) => (Aggregate.T with type api = api)
290
290
  /** Async variant — uses FIFO channel, returns `CommandPending`. */
291
291
  module MakeAsync: (
292
292
  Spec: Reventless.Aggregate.Spec,
293
293
  Behavior: Reventless.Behavior.T with module Spec := Spec,
294
294
  EventMappings: EventMapper.Mappings with module Target := Spec,
295
- ) => Aggregate.T with type api = api
295
+ ) => (Aggregate.T with type api = api)
296
296
  }
297
297
 
298
298
  /** Factory for read model (query-side projection) components. */
@@ -300,7 +300,7 @@ module type T = {
300
300
  module Make: (
301
301
  Spec: Reventless.ReadModel.Spec,
302
302
  Mappings: Reventless.Projection.Mappings with module Target := Spec,
303
- ) => ReadModel.T with module Spec = Spec and type api = api and type role = role
303
+ ) => (ReadModel.T with module Spec = Spec and type api = api and type role = role)
304
304
  }
305
305
 
306
306
  /** Factory for stream-enabled read model components — identical to `ReadModel`
@@ -312,29 +312,24 @@ module type T = {
312
312
  module Make: (
313
313
  Spec: Reventless.ReadModel.Spec,
314
314
  Mappings: Reventless.Projection.Mappings with module Target := Spec,
315
- ) => ReadModelComponentT with module Spec = Spec and type api = api and type role = role
315
+ ) => (ReadModelComponentT with module Spec = Spec and type api = api and type role = role)
316
316
  }
317
317
 
318
318
  /** Factory for extension point components (single mapping). */
319
319
  module ExtensionPoint: {
320
- module Make: (
321
- Mapping: ExtensionPointMapping.Mapping,
322
- ) => ExtensionPoint.T
320
+ module Make: (Mapping: ExtensionPointMapping.Mapping) => ExtensionPoint.T
323
321
 
324
322
  /** Two-mapping variant — merges per-slice EP mappings. */
325
323
  module Make2: (
326
324
  Mapping1: ExtensionPointMapping.Mapping,
327
- Mapping2: ExtensionPointMapping.Mapping
328
- with module ExtensionPoint = Mapping1.ExtensionPoint,
325
+ Mapping2: ExtensionPointMapping.Mapping with module ExtensionPoint = Mapping1.ExtensionPoint,
329
326
  ) => ExtensionPoint.T
330
327
 
331
328
  /** Three-mapping variant — merges per-slice EP mappings. */
332
329
  module Make3: (
333
330
  Mapping1: ExtensionPointMapping.Mapping,
334
- Mapping2: ExtensionPointMapping.Mapping
335
- with module ExtensionPoint = Mapping1.ExtensionPoint,
336
- Mapping3: ExtensionPointMapping.Mapping
337
- with module ExtensionPoint = Mapping1.ExtensionPoint,
331
+ Mapping2: ExtensionPointMapping.Mapping with module ExtensionPoint = Mapping1.ExtensionPoint,
332
+ Mapping3: ExtensionPointMapping.Mapping with module ExtensionPoint = Mapping1.ExtensionPoint,
338
333
  ) => ExtensionPoint.T
339
334
 
340
335
  /** Multi-mapping variant with full control over name and mappings array. */
@@ -349,14 +344,12 @@ module type T = {
349
344
  using the plugin name for the extension's component name and auto-merging
350
345
  blueprints that target the same extension point. */
351
346
  module Extension: {
352
- module Make: (
353
- Mapping: ExtensionMapping.Mapping,
354
- ) => Extension.Blueprint
347
+ module Make: (Mapping: ExtensionMapping.Mapping) => Extension.Blueprint
355
348
  }
356
349
 
357
350
  /** Factory for task (background job / S3 trigger) components. */
358
351
  module Task: {
359
- module Make: (Spec: Task.Spec) => Task.T with module Spec = Spec
352
+ module Make: (Spec: Task.Spec) => (Task.T with module Spec = Spec)
360
353
  }
361
354
 
362
355
  /** Ready-to-use counter component (no Make required). */
@@ -367,12 +360,12 @@ module type T = {
367
360
  module Make: (
368
361
  Spec: Reventless.StateChangeSlice.Spec,
369
362
  Behavior: Reventless.StateChangeSlice.Behavior with module Spec := Spec,
370
- ) => StateChangeSlice.T with module Spec = Spec
363
+ ) => (StateChangeSlice.T with module Spec = Spec)
371
364
  /** Async variant — uses FIFO channel, returns `CommandPending`. */
372
365
  module MakeAsync: (
373
366
  Spec: Reventless.StateChangeSlice.Spec,
374
367
  Behavior: Reventless.StateChangeSlice.Behavior with module Spec := Spec,
375
- ) => StateChangeSlice.T with module Spec = Spec
368
+ ) => (StateChangeSlice.T with module Spec = Spec)
376
369
  }
377
370
 
378
371
  /** Factory for DCB read-side state-view slice components. */
@@ -380,7 +373,7 @@ module type T = {
380
373
  module Make: (
381
374
  Spec: Reventless.StateViewSlice.Spec,
382
375
  Projection: Reventless.StateViewSlice.Projection with module Spec := Spec,
383
- ) => StateViewSlice.T with module Spec = Spec
376
+ ) => (StateViewSlice.T with module Spec = Spec)
384
377
  }
385
378
 
386
379
  /** Factory for stream-enabled state-view slice components (enables Source B subscriptions). */
@@ -388,7 +381,7 @@ module type T = {
388
381
  module Make: (
389
382
  Spec: Reventless.StateViewSlice.Spec,
390
383
  Projection: Reventless.StateViewSlice.Projection with module Spec := Spec,
391
- ) => StateViewSliceComponentT with module Spec = Spec
384
+ ) => (StateViewSliceComponentT with module Spec = Spec)
392
385
  }
393
386
 
394
387
  /** Factory for DCB automation slice components (TODO list pattern). */
@@ -396,7 +389,7 @@ module type T = {
396
389
  module Make: (
397
390
  Spec: Reventless.AutomationSlice.Spec,
398
391
  Automation: Reventless.AutomationSlice.Automation with module Spec := Spec,
399
- ) => AutomationSlice.T with module Spec = Spec
392
+ ) => (AutomationSlice.T with module Spec = Spec)
400
393
  }
401
394
 
402
395
  /** Factory for DCB outbound translation slice components (tracked external calls). */
@@ -404,7 +397,7 @@ module type T = {
404
397
  module Make: (
405
398
  Spec: Reventless.OutboundTranslationSlice.Spec,
406
399
  Translation: Reventless.OutboundTranslationSlice.Translation with module Spec := Spec,
407
- ) => OutboundTranslationSlice.T with module Spec = Spec
400
+ ) => (OutboundTranslationSlice.T with module Spec = Spec)
408
401
  }
409
402
 
410
403
  /** Factory for DCB inbound translation slice components (external input to commands). */
@@ -412,7 +405,7 @@ module type T = {
412
405
  module Make: (
413
406
  Spec: Reventless.InboundTranslationSlice.Spec,
414
407
  Translation: Reventless.InboundTranslationSlice.Translation with module Spec := Spec,
415
- ) => InboundTranslationSlice.T with module Spec = Spec
408
+ ) => (InboundTranslationSlice.T with module Spec = Spec)
416
409
  }
417
410
 
418
411
  /** Whether this platform supports MCP (Model Context Protocol) for AI agent access.
@@ -536,7 +529,10 @@ module type T = {
536
529
  Pass `~apiTarget=Platform` to route the plugin's resolvers and schema to the Platform API.
537
530
  Defaults to `Domain`.
538
531
  Returns the Pulumi stack outputs dict for use as the ESM `default` export. */
539
- let deployPlugin: (~plugin: module(PluginMaker), ~apiTarget: apiTarget=?) => dict<Pulumi.Output.t<JSON.t>>
532
+ let deployPlugin: (
533
+ ~plugin: module(PluginMaker),
534
+ ~apiTarget: apiTarget=?,
535
+ ) => dict<Pulumi.Output.t<JSON.t>>
540
536
 
541
537
  /** Start all servers after all makePlatform/deployPlugin calls are complete.
542
538
  In split in-memory mode, servers are deferred until this is called so all
@@ -5,68 +5,86 @@ module DB = DeployBootstrap
5
5
  describe("DeployBootstrap", () => {
6
6
  // The seam holds module-level state, so each case resets first for isolation.
7
7
  describe("run with no registrations", () => {
8
- testSync("is a no-op that does not throw", () => {
9
- DB.reset()
10
- DB.run(PreDeploy)
11
- DB.run(PostDeploy)
12
- expect(true)->toBe(true)
13
- })
8
+ testSync(
9
+ "is a no-op that does not throw",
10
+ () => {
11
+ DB.reset()
12
+ DB.run(PreDeploy)
13
+ DB.run(PostDeploy)
14
+ expect(true)->toBe(true)
15
+ },
16
+ )
14
17
  })
15
18
 
16
19
  describe("registration order within a phase", () => {
17
- testSync("runs contributions in the order they were registered", () => {
18
- DB.reset()
19
- let calls = []
20
- DB.register(() => calls->Array.push("a"))
21
- DB.register(() => calls->Array.push("b"))
22
- DB.register(() => calls->Array.push("c"))
23
- DB.run(PreDeploy)
24
- expect(calls)->toEqual(["a", "b", "c"])
25
- })
20
+ testSync(
21
+ "runs contributions in the order they were registered",
22
+ () => {
23
+ DB.reset()
24
+ let calls = []
25
+ DB.register(() => calls->Array.push("a"))
26
+ DB.register(() => calls->Array.push("b"))
27
+ DB.register(() => calls->Array.push("c"))
28
+ DB.run(PreDeploy)
29
+ expect(calls)->toEqual(["a", "b", "c"])
30
+ },
31
+ )
26
32
  })
27
33
 
28
34
  describe("phase isolation", () => {
29
- testSync("run(PreDeploy) fires only PreDeploy contributions", () => {
30
- DB.reset()
31
- let calls = []
32
- DB.register(~phase=PreDeploy, () => calls->Array.push("pre"))
33
- DB.register(~phase=PostDeploy, () => calls->Array.push("post"))
34
- DB.run(PreDeploy)
35
- expect(calls)->toEqual(["pre"])
36
- DB.run(PostDeploy)
37
- expect(calls)->toEqual(["pre", "post"])
38
- })
35
+ testSync(
36
+ "run(PreDeploy) fires only PreDeploy contributions",
37
+ () => {
38
+ DB.reset()
39
+ let calls = []
40
+ DB.register(~phase=PreDeploy, () => calls->Array.push("pre"))
41
+ DB.register(~phase=PostDeploy, () => calls->Array.push("post"))
42
+ DB.run(PreDeploy)
43
+ expect(calls)->toEqual(["pre"])
44
+ DB.run(PostDeploy)
45
+ expect(calls)->toEqual(["pre", "post"])
46
+ },
47
+ )
39
48
 
40
- testSync("defaults to PreDeploy when no phase is given", () => {
41
- DB.reset()
42
- let calls = []
43
- DB.register(() => calls->Array.push("default"))
44
- DB.run(PostDeploy)
45
- expect(calls)->toEqual([])
46
- DB.run(PreDeploy)
47
- expect(calls)->toEqual(["default"])
48
- })
49
+ testSync(
50
+ "defaults to PreDeploy when no phase is given",
51
+ () => {
52
+ DB.reset()
53
+ let calls = []
54
+ DB.register(() => calls->Array.push("default"))
55
+ DB.run(PostDeploy)
56
+ expect(calls)->toEqual([])
57
+ DB.run(PreDeploy)
58
+ expect(calls)->toEqual(["default"])
59
+ },
60
+ )
49
61
  })
50
62
 
51
63
  describe("idempotent run per phase", () => {
52
- testSync("running an already-run phase does nothing", () => {
53
- DB.reset()
54
- let count = ref(0)
55
- DB.register(() => count := count.contents + 1)
56
- DB.run(PreDeploy)
57
- DB.run(PreDeploy)
58
- DB.run(PreDeploy)
59
- expect(count.contents)->toBe(1)
60
- })
64
+ testSync(
65
+ "running an already-run phase does nothing",
66
+ () => {
67
+ DB.reset()
68
+ let count = ref(0)
69
+ DB.register(() => count := count.contents + 1)
70
+ DB.run(PreDeploy)
71
+ DB.run(PreDeploy)
72
+ DB.run(PreDeploy)
73
+ expect(count.contents)->toBe(1)
74
+ },
75
+ )
61
76
 
62
- testSync("a contribution registered after run does not fire", () => {
63
- DB.reset()
64
- let calls = []
65
- DB.register(() => calls->Array.push("before"))
66
- DB.run(PreDeploy)
67
- DB.register(() => calls->Array.push("after"))
68
- DB.run(PreDeploy)
69
- expect(calls)->toEqual(["before"])
70
- })
77
+ testSync(
78
+ "a contribution registered after run does not fire",
79
+ () => {
80
+ DB.reset()
81
+ let calls = []
82
+ DB.register(() => calls->Array.push("before"))
83
+ DB.run(PreDeploy)
84
+ DB.register(() => calls->Array.push("after"))
85
+ DB.run(PreDeploy)
86
+ expect(calls)->toEqual(["before"])
87
+ },
88
+ )
71
89
  })
72
90
  })
@@ -12,10 +12,14 @@ describe("RuntimeHints.resolveMemory", () => {
12
12
  expect(RH.resolveMemory(None, ~default=1024))->toBe(1024)
13
13
  })
14
14
  testSync("an override above the default wins", () => {
15
- expect(RH.resolveMemory(Some({memorySize: Some(2048), timeout: None}), ~default=1024))->toBe(2048)
15
+ expect(RH.resolveMemory(Some({memorySize: Some(2048), timeout: None}), ~default=1024))->toBe(
16
+ 2048,
17
+ )
16
18
  })
17
19
  testSync("an override below the default is clamped up to the floor", () => {
18
- expect(RH.resolveMemory(Some({memorySize: Some(256), timeout: None}), ~default=1024))->toBe(1024)
20
+ expect(RH.resolveMemory(Some({memorySize: Some(256), timeout: None}), ~default=1024))->toBe(
21
+ 1024,
22
+ )
19
23
  })
20
24
  testSync("an absent memorySize field falls through to the default", () => {
21
25
  expect(RH.resolveMemory(Some({memorySize: None, timeout: Some(60)}), ~default=1024))->toBe(1024)