@reventlessdev/reventless-aws 3.0.0-alpha.397 → 3.0.0-alpha.399

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 (83) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/package.json +9 -9
  3. package/scripts/BakeManifest.res +15 -15
  4. package/scripts/DeployApp.res +8 -9
  5. package/scripts/DeployApp.res.mjs +4 -13
  6. package/scripts/DeployManifest.res +4 -5
  7. package/scripts/DeployManifest.res.mjs +0 -1
  8. package/scripts/ProvisionAccounts.res +41 -33
  9. package/scripts/ProvisionAccounts.res.mjs +6 -9
  10. package/scripts/ProvisionAdmin.res +10 -10
  11. package/scripts/ProvisionIdentity.res +2 -2
  12. package/scripts/ResolveEnvironment.res +3 -3
  13. package/src/Platform.res +6 -4
  14. package/src/Platform.res.mjs +6 -4
  15. package/src/Platform_ProvidedGroups.res +4 -4
  16. package/src/Platform_Stack.res +2 -3
  17. package/src/Platform_Stack.res.mjs +1 -2
  18. package/src/adapter/Api/AppSyncEventsSigner_Ops.res +2 -2
  19. package/src/adapter/Api/AppSync_EventsApi.res +2 -2
  20. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +6 -6
  21. package/src/adapter/DcbEventLog/DcbBackend.res +3 -3
  22. package/src/adapter/DcbEventLog/DcbBackend.res.mjs +1 -3
  23. package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res +11 -9
  24. package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res.mjs +4 -6
  25. package/src/adapter/EventLog/EventLogBackend.res +3 -3
  26. package/src/adapter/EventLog/EventLogBackend.res.mjs +1 -3
  27. package/src/adapter/Geocoder/Geocoder_AwsLocation_Backend.res +1 -1
  28. package/src/adapter/QueryDb/PgQueryResolver_Lambda.res +5 -5
  29. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res +2 -2
  30. package/src/adapter/Runtime/AggregateRuntime_Builder_Micro_Async.res +2 -2
  31. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +32 -32
  32. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +4 -3
  33. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +6 -6
  34. package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res +16 -5
  35. package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res.mjs +3 -3
  36. package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +5 -5
  37. package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +24 -18
  38. package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res +16 -14
  39. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +2 -2
  40. package/src/adapter/Runtime/RuntimeExtensionEntryPoint_Ops.res +4 -4
  41. package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res +4 -4
  42. package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res +9 -9
  43. package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res +11 -9
  44. package/src/components/Api/AppSync_Adapter.res +4 -4
  45. package/src/components/InboundTranslationSlice_Builder.res +19 -0
  46. package/src/components/InboundTranslationSlice_Builder.res.mjs +12 -2
  47. package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res +4 -7
  48. package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res.mjs +1 -4
  49. package/src/plugin/runtime/PluginRuntime_Builder.res +17 -26
  50. package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +7 -22
  51. package/tests/AppSyncEventsSigner_OpsTest.res +1 -1
  52. package/tests/AppSync_AdapterTest.res +4 -4
  53. package/tests/AppSync_AdapterTest.res.mjs +4 -8
  54. package/tests/Auth_ActiveRoleStoreTest.res +1 -1
  55. package/tests/Auth_ActiveRoleTrigger_OpsTest.res +1 -1
  56. package/tests/AutomationSliceEntryPoint_OpsTest.res +0 -2
  57. package/tests/AutomationSliceEntryPoint_OpsTest.res.mjs +3 -6
  58. package/tests/BakeConvergenceTest.res +1 -8
  59. package/tests/BakeConvergenceTest.res.mjs +3 -23
  60. package/tests/BakeManifestTest.res +5 -10
  61. package/tests/BakeManifestTest.res.mjs +1 -6
  62. package/tests/CommandLineTest.res +3 -3
  63. package/tests/DeployAppTest.res +3 -4
  64. package/tests/DeployAppTest.res.mjs +6 -15
  65. package/tests/EpInboundTestSlice.res +1 -0
  66. package/tests/EpInboundTestSlice.res.mjs +3 -0
  67. package/tests/EventCollectorPluginDefinitionAssetTest.res +1 -2
  68. package/tests/EventCollectorPluginDefinitionAssetTest.res.mjs +11 -21
  69. package/tests/IdentityProvider_Cognito_BackendTest.res +0 -3
  70. package/tests/IdentityProvider_Cognito_BackendTest.res.mjs +0 -3
  71. package/tests/PgQueryResolver_LambdaTest.res +6 -10
  72. package/tests/PgQueryResolver_LambdaTest.res.mjs +4 -34
  73. package/tests/Platform_ProvidedGroupsTest.res +2 -2
  74. package/tests/Platform_ProvidedGroupsTest.res.mjs +1 -2
  75. package/tests/ProvisionAccountsTest.res +5 -10
  76. package/tests/ProvisionAccountsTest.res.mjs +0 -9
  77. package/tests/ProvisionAdminTest.res +4 -9
  78. package/tests/ProvisionAdminTest.res.mjs +0 -7
  79. package/tests/ProvisionIdentityTest.res +2 -4
  80. package/tests/ProvisionIdentityTest.res.mjs +0 -2
  81. package/tests/SideEffectEntryPoint_OpsTest.res +2 -2
  82. package/tests/Util_ShellConfigTest.res +3 -3
  83. package/tests/Util_ShellConfigTest.res.mjs +1 -2
@@ -334,8 +334,7 @@ function MakeWithConfig(Config) {
334
334
  };
335
335
  domainEventsApiOpt = {
336
336
  name: "DomainEventsApi",
337
- api: api$2,
338
- defaultNamespace: undefined
337
+ api: api$2
339
338
  };
340
339
  } else {
341
340
  let cognitoPool = Platform_Stack$ReventlessAws.resolveCognitoUserPool();
@@ -1505,6 +1504,8 @@ function MakeWithConfig(Config) {
1505
1504
  return `\n Messaging is \`Capability_Messaging.make\`, passed to the platform as \`~messagingSender\`. The sender itself is configuration and has no default: set \`platform:messagingEmailSender\` in Pulumi.<stack>.yaml (or REVENTLESS_MESSAGING_EMAIL_SENDER), then verify the address with SES — or \`platform:messagingEmailProvider: log\` to log every message and send none.`;
1506
1505
  case "IdentityProvider" :
1507
1506
  return `\n IdentityProvider is the platform's Cognito pool, exported as \`identityProviderId\`. This platform stack exports none — deploy it first.`;
1507
+ case "Stash" :
1508
+ return `\n Stash has no deployed backend yet; the local platform keeps one in memory. Host a plugin that needs it on the local platform.`;
1508
1509
  }
1509
1510
  }))).join(""));
1510
1511
  }
@@ -1768,8 +1769,7 @@ function Make($star) {
1768
1769
  };
1769
1770
  domainEventsApiOpt = {
1770
1771
  name: "DomainEventsApi",
1771
- api: api$2,
1772
- defaultNamespace: undefined
1772
+ api: api$2
1773
1773
  };
1774
1774
  } else {
1775
1775
  let cognitoPool = Platform_Stack$ReventlessAws.resolveCognitoUserPool();
@@ -2914,6 +2914,8 @@ function Make($star) {
2914
2914
  return `\n Messaging is \`Capability_Messaging.make\`, passed to the platform as \`~messagingSender\`. The sender itself is configuration and has no default: set \`platform:messagingEmailSender\` in Pulumi.<stack>.yaml (or REVENTLESS_MESSAGING_EMAIL_SENDER), then verify the address with SES — or \`platform:messagingEmailProvider: log\` to log every message and send none.`;
2915
2915
  case "IdentityProvider" :
2916
2916
  return `\n IdentityProvider is the platform's Cognito pool, exported as \`identityProviderId\`. This platform stack exports none — deploy it first.`;
2917
+ case "Stash" :
2918
+ return `\n Stash has no deployed backend yet; the local platform keeps one in memory. Host a plugin that needs it on the local platform.`;
2917
2919
  }
2918
2920
  }))).join(""));
2919
2921
  }
@@ -8,7 +8,7 @@ read outside any `Output.apply`.
8
8
 
9
9
  module Cognito = AwsSdk.CognitoIdentityServiceProvider
10
10
 
11
- type pool = {id: string, region: option<string>}
11
+ type pool = {id: string, region?: string}
12
12
 
13
13
  type send = Cognito.ListGroupsCommand.input => promise<Cognito.ListGroupsCommand.output>
14
14
 
@@ -39,7 +39,7 @@ let poolFromExports = (~get: string => option<JSON.t>): option<pool> => {
39
39
  ->Option.orElse(read("cognitoUserPoolId"))
40
40
  ->Option.map(id => {
41
41
  id,
42
- region: read("identityProviderRegion")->Option.orElse(read("cognitoRegion")),
42
+ region: ?(read("identityProviderRegion")->Option.orElse(read("cognitoRegion"))),
43
43
  })
44
44
  }
45
45
 
@@ -72,7 +72,7 @@ let resolvePool = async (~stackRef: option<Pulumi.StackReference.t>): option<poo
72
72
  ~cfg=Pulumi.Config.make(Some("platform")),
73
73
  )->Option.map(id => {
74
74
  id,
75
- region: Pulumi.Config.make(Some("aws"))->Pulumi.Config.get("region"),
75
+ region: ?(Pulumi.Config.make(Some("aws"))->Pulumi.Config.get("region")),
76
76
  })
77
77
  }
78
78
 
@@ -86,7 +86,7 @@ let sendTo = (~region: option<string>): send => {
86
86
  let load = async (~stackRef: option<Pulumi.StackReference.t>): unit =>
87
87
  switch await resolvePool(~stackRef) {
88
88
  | None => ()
89
- | Some({id, region}) =>
89
+ | Some({id, ?region}) =>
90
90
  let groups = try await listAll(~send=sendTo(~region), ~userPoolId=id) catch {
91
91
  | exn if exn->Util_AwsError.hasCode(~code="AccessDeniedException") =>
92
92
  JsError.throwWithMessage(
@@ -18,7 +18,7 @@ type cognitoUserPool = {
18
18
  /** The sign-in attribute this stack created the pool with. `None` in BYO mode:
19
19
  the pool's own choice was made elsewhere and no lookup here reads it back,
20
20
  so reporting one would be a guess. */
21
- loginIdentifier: option<Auth_LoginIdentifier.t>,
21
+ loginIdentifier?: Auth_LoginIdentifier.t,
22
22
  }
23
23
 
24
24
  /**
@@ -250,7 +250,6 @@ let _resolveUncached = (): cognitoUserPool => {
250
250
  managed: false,
251
251
  activeRoleTable,
252
252
  identityTable,
253
- loginIdentifier: None,
254
253
  }
255
254
 
256
255
  | None =>
@@ -346,7 +345,7 @@ let _resolveUncached = (): cognitoUserPool => {
346
345
  managed: true,
347
346
  activeRoleTable,
348
347
  identityTable,
349
- loginIdentifier: Some(loginIdentifier),
348
+ loginIdentifier,
350
349
  }
351
350
  }
352
351
 
@@ -107,8 +107,7 @@ function _resolveUncached() {
107
107
  poolArn: lookup.apply(r => r.arn),
108
108
  managed: false,
109
109
  activeRoleTable: activeRoleTable,
110
- identityTable: identityTable,
111
- loginIdentifier: undefined
110
+ identityTable: identityTable
112
111
  };
113
112
  } else {
114
113
  let loginIdentifier = _loginIdentifier(cfg);
@@ -23,13 +23,13 @@ let hmacFromBuf = (key: NodeCrypto.buffer, data: string): NodeCrypto.buffer =>
23
23
 
24
24
  // ── SigV4 ───────────────────────────────────────────────────────────────────
25
25
 
26
- type creds = {accessKeyId: string, secretAccessKey: string, sessionToken: option<string>}
26
+ type creds = {accessKeyId: string, secretAccessKey: string, sessionToken?: string}
27
27
 
28
28
  // Ambient Lambda execution-role credentials (the managed runtime injects these).
29
29
  let envCreds = (): creds => {
30
30
  accessKeyId: NodeProcess.env->Dict.get("AWS_ACCESS_KEY_ID")->Option.getOr(""),
31
31
  secretAccessKey: NodeProcess.env->Dict.get("AWS_SECRET_ACCESS_KEY")->Option.getOr(""),
32
- sessionToken: NodeProcess.env->Dict.get("AWS_SESSION_TOKEN"),
32
+ sessionToken: ?(NodeProcess.env->Dict.get("AWS_SESSION_TOKEN")),
33
33
  }
34
34
 
35
35
  let region = (): string => NodeProcess.env->Dict.get("AWS_REGION")->Option.getOr("eu-west-1")
@@ -80,7 +80,7 @@ type t = {
80
80
  /** Full AppSync Events API output — carry this through the stack. */
81
81
  api: Api.t,
82
82
  /** The `default` channel namespace created on the API. */
83
- defaultNamespace: option<ChannelNamespace.t>,
83
+ defaultNamespace?: ChannelNamespace.t,
84
84
  /** The client-publishable `client` namespace. Absent on plugin-stack
85
85
  phantoms (namespaces live on the platform stack's API). */
86
86
  clientNamespace?: ChannelNamespace.t,
@@ -202,7 +202,7 @@ let make = (
202
202
  ~opts=Some({...opts, parent: api->Pulumi.Resource.makeFromJs}),
203
203
  )
204
204
 
205
- {name, api, defaultNamespace: Some(defaultNamespace), clientNamespace}
205
+ {name, api, defaultNamespace, clientNamespace}
206
206
  }
207
207
 
208
208
  /** HTTP endpoint URL for use as Lambda `APPSYNC_ENDPOINT` env var.
@@ -334,9 +334,9 @@ let bakeSelection = (json: JSON.t): option<ReventlessCore.Platform_BakedManifest
334
334
  ->Option.flatMap(JSON.Decode.string)
335
335
  ->Option.map(plugin => {
336
336
  ReventlessCore.Platform_BakedManifest.plugin,
337
- views: strings(o, "views"),
338
- commands: strings(o, "commands"),
339
- derived: strings(o, "derived"),
337
+ views: ?strings(o, "views"),
338
+ commands: ?strings(o, "commands"),
339
+ derived: ?strings(o, "derived"),
340
340
  })
341
341
  )
342
342
  }
@@ -502,8 +502,8 @@ type registrationState =
502
502
  type registration = {
503
503
  plugin: string,
504
504
  expected: string,
505
- found: option<string>,
506
- writtenAt: option<string>,
505
+ found?: string,
506
+ writtenAt?: string,
507
507
  state: registrationState,
508
508
  }
509
509
 
@@ -550,7 +550,7 @@ let registrations = (
550
550
  let row = current->Dict.get(plugin)
551
551
  let found = row->Option.map(((key, _)) => key)
552
552
  let writtenAt = row->Option.flatMap(((_, at)) => at)
553
- {plugin, expected, found, writtenAt, state: classify(~expected, ~found, ~writtenAt, ~since)}
553
+ {plugin, expected, ?found, ?writtenAt, state: classify(~expected, ~found, ~writtenAt, ~since)}
554
554
  })
555
555
  }
556
556
 
@@ -53,8 +53,8 @@ let isPostgres = (): bool => selectionRef.contents->Option.isSome
53
53
  type relayLogEntry = {
54
54
  logName: string,
55
55
  partitionTag: Reventless.DcbTag.derivedPartitionTag,
56
- mutable collectorQueueUrl: option<Pulumi.Output.t<string>>,
57
- mutable collectorQueueArn: option<Pulumi.Output.t<string>>,
56
+ mutable collectorQueueUrl?: Pulumi.Output.t<string>,
57
+ mutable collectorQueueArn?: Pulumi.Output.t<string>,
58
58
  }
59
59
 
60
60
  let relayLogs: array<relayLogEntry> = []
@@ -62,7 +62,7 @@ let relayLogs: array<relayLogEntry> = []
62
62
  /** Step 1: record a Postgres DCB log and its partition tag (from the storage maker). */
63
63
  let registerRelayLog = (~logName: string, ~partitionTag: Reventless.DcbTag.derivedPartitionTag) =>
64
64
  relayLogs
65
- ->Array.push({logName, partitionTag, collectorQueueUrl: None, collectorQueueArn: None})
65
+ ->Array.push({logName, partitionTag})
66
66
  ->ignore
67
67
 
68
68
  /** Step 2: attach a plugin's EventCollector SQS queue to its DCB log's relay entry. */
@@ -23,9 +23,7 @@ let relayLogs = [];
23
23
  function registerRelayLog(logName, partitionTag) {
24
24
  relayLogs.push({
25
25
  logName: logName,
26
- partitionTag: partitionTag,
27
- collectorQueueUrl: undefined,
28
- collectorQueueArn: undefined
26
+ partitionTag: partitionTag
29
27
  });
30
28
  }
31
29
 
@@ -265,9 +265,9 @@ let queryByCompositeTags = async (
265
265
 
266
266
  // Filter clause shared by both scan paths.
267
267
  type scanFilter = {
268
- filterExpression: option<string>,
269
- expressionAttributeNames: option<Dict.t<string>>,
270
- expressionAttributeValues: option<Dict.t<JSON.t>>,
268
+ filterExpression?: string,
269
+ expressionAttributeNames?: Dict.t<string>,
270
+ expressionAttributeValues?: Dict.t<JSON.t>,
271
271
  }
272
272
 
273
273
  // Builds the scan FilterExpression. Always asserts `attribute_exists(event)` so
@@ -294,9 +294,9 @@ let buildScanFilter = (~eventTypes: option<array<string>>=?): scanFilter => {
294
294
 
295
295
  let hasAttributeValues = expressionAttributeValues->Dict.keysToArray->Array.length > 0
296
296
  {
297
- filterExpression: Some(filterParts->Array.join(" AND ")),
298
- expressionAttributeNames: Some(expressionAttributeNames),
299
- expressionAttributeValues: hasAttributeValues ? Some(expressionAttributeValues) : None,
297
+ filterExpression: filterParts->Array.join(" AND "),
298
+ expressionAttributeNames,
299
+ expressionAttributeValues: ?(hasAttributeValues ? Some(expressionAttributeValues) : None),
300
300
  }
301
301
  }
302
302
 
@@ -523,7 +523,7 @@ let deduplicateByPosition = (
523
523
 
524
524
  type mergeSlot = {
525
525
  queue: Queue.t<option<ReventlessCore.DcbEventLog_Adapter.rawSequencedEvent>>,
526
- head: option<ReventlessCore.DcbEventLog_Adapter.rawSequencedEvent>,
526
+ head?: ReventlessCore.DcbEventLog_Adapter.rawSequencedEvent,
527
527
  }
528
528
 
529
529
  // Wraps a sub-stream in a Queue(1) producer fiber and pulls the first element.
@@ -539,7 +539,9 @@ let initSlot = (
539
539
  // unhandled-fiber-failure warning).
540
540
  ->Effect.ensuring(Queue.offer(queue, None)->Effect.map(_ => ()))
541
541
  ->Effect.catchAll(_ => Effect.succeed())
542
- Effect.fork(producer)->Effect.flatMap(_ => Queue.take(queue)->Effect.map(head => {queue, head}))
542
+ Effect.fork(producer)->Effect.flatMap(_ =>
543
+ Queue.take(queue)->Effect.map(head => {queue, ?head})
544
+ )
543
545
  })
544
546
 
545
547
  // Returns the index of the slot with the smallest position, or None if all exhausted.
@@ -596,7 +598,7 @@ let mergeSortedEvents = (
596
598
  let newSlots = slots->Array.mapWithIndex(
597
599
  (s, i) =>
598
600
  if i == idx {
599
- {...s, head: nextHead}
601
+ {...s, head: ?nextHead}
600
602
  } else {
601
603
  s
602
604
  },
@@ -468,14 +468,12 @@ function mergeSortedEvents(streams) {
468
468
  let emitEvent = Stdlib_Option.getOrThrow(slot.head, undefined);
469
469
  return Effect$1.map(Queue.take(slot.queue), nextHead => {
470
470
  let newSlots = slots.map((s, i) => {
471
- if (i === idx) {
472
- return {
473
- queue: s.queue,
474
- head: nextHead
475
- };
476
- } else {
471
+ if (i !== idx) {
477
472
  return s;
478
473
  }
474
+ let newrecord = {...s};
475
+ newrecord.head = nextHead;
476
+ return newrecord;
479
477
  });
480
478
  let hasActive = newSlots.some(s => Stdlib_Option.isSome(s.head));
481
479
  return [
@@ -53,8 +53,8 @@ let isPostgres = (): bool => selectionRef.contents->Option.isSome
53
53
  type relayLogEntry = {
54
54
  logName: string,
55
55
  aggregateName: string,
56
- mutable collectorQueueUrl: option<Pulumi.Output.t<string>>,
57
- mutable collectorQueueArn: option<Pulumi.Output.t<string>>,
56
+ mutable collectorQueueUrl?: Pulumi.Output.t<string>,
57
+ mutable collectorQueueArn?: Pulumi.Output.t<string>,
58
58
  }
59
59
 
60
60
  let relayLogs: array<relayLogEntry> = []
@@ -62,7 +62,7 @@ let relayLogs: array<relayLogEntry> = []
62
62
  /** Step 1: record a Postgres classic log (from the storage maker). */
63
63
  let registerRelayLog = (~logName: string, ~aggregateName: string) =>
64
64
  relayLogs
65
- ->Array.push({logName, aggregateName, collectorQueueUrl: None, collectorQueueArn: None})
65
+ ->Array.push({logName, aggregateName})
66
66
  ->ignore
67
67
 
68
68
  /** Step 2: attach a plugin's EventCollector SQS queue to one aggregate's relay entry. */
@@ -23,9 +23,7 @@ let relayLogs = [];
23
23
  function registerRelayLog(logName, aggregateName) {
24
24
  relayLogs.push({
25
25
  logName: logName,
26
- aggregateName: aggregateName,
27
- collectorQueueUrl: undefined,
28
- collectorQueueArn: undefined
26
+ aggregateName: aggregateName
29
27
  });
30
28
  }
31
29
 
@@ -57,7 +57,7 @@ let search = async (~indexName: string, ~text: string, ~maxResults: int=5): resu
57
57
  Some({
58
58
  Reventless.Geocoding.label: place.label->Option.getOr(""),
59
59
  point,
60
- relevance: r.relevance,
60
+ relevance: ?r.relevance,
61
61
  })
62
62
  // A coordinate that fails the range check is dropped rather than
63
63
  // propagated: it cannot be stored, and one bad row should not lose
@@ -89,7 +89,7 @@ type binding = {
89
89
  ops: ReventlessCore.QueryDb_Adapter.operations,
90
90
  pushdowns: pushdowns,
91
91
  indexes: array<Reventless.ReadModel.indexConfig>,
92
- subIdField: option<string>,
92
+ subIdField?: string,
93
93
  capability: ReventlessCore.GraphQL_FragmentGenerator.serverCapability,
94
94
  labelField: string,
95
95
  includeIdParam: bool,
@@ -97,9 +97,9 @@ type binding = {
97
97
  /** The state's `@owner` field, when it declares one. Derived at Lambda init
98
98
  from the same schema `capability` comes from, so the two cannot disagree
99
99
  about which fields this read model has. */
100
- ownerField: option<string>,
101
- retiredField: option<string>,
102
- retiredValues: option<array<string>>,
100
+ ownerField?: string,
101
+ retiredField?: string,
102
+ retiredValues?: array<string>,
103
103
  /** Whether a reference to a retired row of this read model still resolves its
104
104
  name — the `@namedWhenRetired` opt-in. Read only by the reference door;
105
105
  every other door narrows as it always did. */
@@ -107,7 +107,7 @@ type binding = {
107
107
  /** The field a reference shows this row's picture from, derived from the same
108
108
  schema as `capability` by `Reventless.RowImage`. `None` for a view that
109
109
  declares no picture, which is most of them. */
110
- imageSource: option<Reventless.RowImage.source>,
110
+ imageSource?: Reventless.RowImage.source,
111
111
  }
112
112
 
113
113
  // -- arg helpers -------------------------------------------------------------
@@ -11,7 +11,7 @@ type aggregateInfo = {
11
11
  specModulePath: string,
12
12
  decisionModulePath: string,
13
13
  eventLogTableName: Pulumi.Output.t<string>,
14
- mappingsModulePath: option<string>,
14
+ mappingsModulePath?: string,
15
15
  }
16
16
 
17
17
  let aggregateInfos: dict<aggregateInfo> = Dict.make()
@@ -25,7 +25,7 @@ let registerAggregate = (
25
25
  ) =>
26
26
  aggregateInfos->Dict.set(
27
27
  aggregateName,
28
- {specModulePath, decisionModulePath, eventLogTableName, mappingsModulePath},
28
+ {specModulePath, decisionModulePath, eventLogTableName, ?mappingsModulePath},
29
29
  )
30
30
 
31
31
  type storedSpec = {
@@ -15,7 +15,7 @@ type aggregateInfo = {
15
15
  specModulePath: string,
16
16
  decisionModulePath: string,
17
17
  eventLogTableName: Pulumi.Output.t<string>,
18
- mappingsModulePath: option<string>,
18
+ mappingsModulePath?: string,
19
19
  }
20
20
 
21
21
  let aggregateInfos: dict<aggregateInfo> = Dict.make()
@@ -29,7 +29,7 @@ let registerAggregate = (
29
29
  ) =>
30
30
  aggregateInfos->Dict.set(
31
31
  aggregateName,
32
- {specModulePath, decisionModulePath, eventLogTableName, mappingsModulePath},
32
+ {specModulePath, decisionModulePath, eventLogTableName, ?mappingsModulePath},
33
33
  )
34
34
 
35
35
  type storedSpec = {
@@ -29,11 +29,11 @@ type handlerEntry = {
29
29
  /** Whether `dcbQueueUrl` is a FIFO queue. Absent in a config written before
30
30
  the flavor travelled with the URL, where `false` — a standard queue — is
31
31
  what these command topics actually are. */
32
- commandQueueIsFifo: option<bool>,
32
+ commandQueueIsFifo?: bool,
33
33
  sourceUrn: string,
34
- context: option<Reventless.AutomationSlice.context>,
34
+ context?: Reventless.AutomationSlice.context,
35
35
  /** The slice's schedules store; absent for a slice that maps no `Clock`. */
36
- schedulesTableName: option<string>,
36
+ schedulesTableName?: string,
37
37
  }
38
38
 
39
39
  let strOf = (obj: dict<JSON.t>, key: string): option<string> =>
@@ -58,10 +58,10 @@ let decodeEntry = (json: JSON.t): option<handlerEntry> =>
58
58
  callbackType: h->strOf("callbackType")->Option.getOr("automation"),
59
59
  queryDbTableName: h->strOf("queryDbTableName")->Option.getOr(""),
60
60
  dcbQueueUrl: h->strOf("dcbQueueUrl")->Option.getOr(""),
61
- commandQueueIsFifo: h->Dict.get("commandQueueIsFifo")->Option.flatMap(JSON.Decode.bool),
61
+ commandQueueIsFifo: ?(h->Dict.get("commandQueueIsFifo")->Option.flatMap(JSON.Decode.bool)),
62
62
  sourceUrn: h->strOf("sourceUrn")->Option.getOr(""),
63
- context: h->Dict.get("context")->Option.flatMap(decodeContext),
64
- schedulesTableName: h->strOf("schedulesTableName"),
63
+ context: ?(h->Dict.get("context")->Option.flatMap(decodeContext)),
64
+ schedulesTableName: ?(h->strOf("schedulesTableName")),
65
65
  })
66
66
 
67
67
  // A compact entry names its slice and carries only what the deploy resolved;
@@ -91,15 +91,15 @@ let decodeCompactEntry = (
91
91
  callbackType: m->strOf("callbackType")->Option.getOr("automation"),
92
92
  queryDbTableName: h->strOf("q")->Option.getOr(""),
93
93
  dcbQueueUrl: h->strOf("k")->Option.getOr(sharedQueueUrl),
94
- commandQueueIsFifo: switch h->Dict.get("f")->Option.flatMap(JSON.Decode.bool) {
94
+ commandQueueIsFifo: ?switch h->Dict.get("f")->Option.flatMap(JSON.Decode.bool) {
95
95
  | Some(f) => Some(f)
96
96
  | None => sharedIsFifo
97
97
  },
98
98
  // Only the first, which is all the shell registers today. The remaining
99
99
  // urns stay in the config for the multi-source registration to use.
100
100
  sourceUrn: urns->Array.get(0)->Option.getOr(""),
101
- context: m->Dict.get("context")->Option.flatMap(decodeContext),
102
- schedulesTableName: h->strOf("s"),
101
+ context: ?(m->Dict.get("context")->Option.flatMap(decodeContext)),
102
+ schedulesTableName: ?(h->strOf("s")),
103
103
  }
104
104
  }
105
105
 
@@ -428,6 +428,9 @@ let capabilities = (): Reventless.Capabilities.t => {
428
428
  ~identityStore=NodeProcess.env->Dict.get("IDENTITY_STORE_TABLE")->Option.getOr(""),
429
429
  ),
430
430
  secrets: ReventlessCore.Secrets_Node_Backend.provider,
431
+ // None deployed yet: a slice declaring the need is refused by the deploy gate,
432
+ // and this answers `Unavailable` should one run regardless.
433
+ stash: Reventless.Capabilities.none.stash,
431
434
  }
432
435
 
433
436
  // ── Phase-1/phase-2 pipelines ───────────────────────────────────────────────
@@ -576,7 +579,7 @@ type builtSlice = {
576
579
  tick: option<(~key: string, ~at: float) => promise<unit>>,
577
580
  /** Brings the slice's schedules in line with its spec and its rows, at a deploy;
578
581
  `None` for an outbound slice. */
579
- reconcile: option<unit => promise<unit>>,
582
+ reconcile?: unit => promise<unit>,
580
583
  }
581
584
 
582
585
  let makeAutomationRegisteredHandler = (
@@ -651,27 +654,25 @@ let makeAutomationRegisteredHandler = (
651
654
  await syncTodoItems()
652
655
  },
653
656
  ),
654
- reconcile: Some(
655
- async () =>
656
- switch schedules() {
657
- | Some({load, sync, platform}) =>
658
- await load()
659
- await ReventlessCore.AutomationSlice_Schedules.reconcile(
660
- callback.schedules,
661
- platform,
662
- ~declaredTime=schedule,
663
- ~now=Date.now(),
664
- )
665
- await sync()
666
- // No Clock mapping, so no schedule of the slice may stay on the platform.
667
- | None =>
668
- let platform = platformSchedulesOf(~slice=sliceName)
669
- let names = await platform.names()
670
- for i in 0 to names->Array.length - 1 {
671
- await platform.removeName(names->Array.getUnsafe(i))
672
- }
673
- },
674
- ),
657
+ reconcile: async () =>
658
+ switch schedules() {
659
+ | Some({load, sync, platform}) =>
660
+ await load()
661
+ await ReventlessCore.AutomationSlice_Schedules.reconcile(
662
+ callback.schedules,
663
+ platform,
664
+ ~declaredTime=schedule,
665
+ ~now=Date.now(),
666
+ )
667
+ await sync()
668
+ // No Clock mapping, so no schedule of the slice may stay on the platform.
669
+ | None =>
670
+ let platform = platformSchedulesOf(~slice=sliceName)
671
+ let names = await platform.names()
672
+ for i in 0 to names->Array.length - 1 {
673
+ await platform.removeName(names->Array.getUnsafe(i))
674
+ }
675
+ },
675
676
  }
676
677
  }
677
678
 
@@ -718,7 +719,6 @@ let makeOutboundRegisteredHandler = (
718
719
  comp,
719
720
  name: sliceName,
720
721
  tick: None,
721
- reconcile: None,
722
722
  }
723
723
  }
724
724
 
@@ -13,6 +13,7 @@ import * as DcbDecode$Reventless from "@reventlessdev/reventless-spec/src/compon
13
13
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
14
14
  import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
15
15
  import * as Message$ReventlessCore from "@reventlessdev/reventless-core/src/Message.res.mjs";
16
+ import * as Capabilities$Reventless from "@reventlessdev/reventless-spec/src/semantic/Capabilities.res.mjs";
16
17
  import * as EffectLogger$ReventlessCore from "@reventlessdev/reventless-core/src/util/EffectLogger.res.mjs";
17
18
  import * as DynamoDb_Error$ReventlessAws from "../../errors/DynamoDb_Error.res.mjs";
18
19
  import * as Messaging_Ses_Backend$ReventlessAws from "../Messaging/Messaging_Ses_Backend.res.mjs";
@@ -276,7 +277,8 @@ function capabilities() {
276
277
  geocode: text => Geocoder_AwsLocation_Backend$ReventlessAws.search(Stdlib_Option.getOr(process.env["PLACE_INDEX_NAME"], ""), text, undefined),
277
278
  messaging: messagingEmailProvider(),
278
279
  identityProvider: IdentityProvider_Cognito_Backend$ReventlessAws.provider(Stdlib_Option.getOr(process.env["IDENTITY_PROVIDER_ID"], ""), Stdlib_Option.getOr(process.env["IDENTITY_STORE_TABLE"], "")),
279
- secrets: Secrets_Node_Backend$ReventlessCore.provider
280
+ secrets: Secrets_Node_Backend$ReventlessCore.provider,
281
+ stash: Capabilities$Reventless.none.stash
280
282
  };
281
283
  }
282
284
 
@@ -422,8 +424,7 @@ function makeOutboundRegisteredHandler(entry, sliceName, consumedEventSchema, ca
422
424
  },
423
425
  comp: comp,
424
426
  name: sliceName,
425
- tick: undefined,
426
- reconcile: undefined
427
+ tick: undefined
427
428
  };
428
429
  }
429
430
 
@@ -23,7 +23,7 @@ type sliceInfo = {
23
23
  queryDbResources: array<ReventlessInfra.Adapter.resource>,
24
24
  // Threaded to phase 1 collect/resolve on the automation path (mirrors the
25
25
  // in-process builder's `~context`); outbound slices take no context.
26
- context: option<Reventless.AutomationSlice.context>,
26
+ context?: Reventless.AutomationSlice.context,
27
27
  // The non-DCB topics this slice subscribes to — an Aggregate's EventTopic when
28
28
  // it names one by `Spec.name`. Empty for the common DCB-only slice.
29
29
  //
@@ -47,10 +47,10 @@ type sliceInfo = {
47
47
  // Resolved in the finalizer rather than here: an Aggregate's CommandTopic is
48
48
  // created *after* the slices that target it, so a lookup at registration time
49
49
  // finds nothing. Only the name is knowable this early.
50
- commandTargetName: option<string>,
50
+ commandTargetName?: string,
51
51
  // The automation's declared time; what the deploy's reconcile puts on the
52
52
  // platform, and what re-runs it when it changes.
53
- schedule: option<Reventless.Schedule.time>,
53
+ schedule?: Reventless.Schedule.time,
54
54
  // The schedules store's table, empty for a slice that maps no `Clock`. Granted to
55
55
  // the Lambda with the to-do tables, and named in the slice's handler config.
56
56
  schedulesResources: array<ReventlessInfra.Adapter.resource>,
@@ -116,11 +116,11 @@ let registerAutomationSlice = (
116
116
  callbackType,
117
117
  queryDbTableName,
118
118
  queryDbResources,
119
- context,
119
+ ?context,
120
120
  sourceTopics,
121
121
  consumesDcbLog,
122
- commandTargetName,
123
- schedule,
122
+ ?commandTargetName,
123
+ ?schedule,
124
124
  schedulesResources,
125
125
  capabilityNeeds,
126
126
  },