@reventlessdev/reventless-spec 3.0.0-alpha.133 → 3.0.0-alpha.134

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 (79) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/package.json +2 -2
  3. package/src/AnsiStyle.res +0 -1
  4. package/src/LogPrefix.res +4 -13
  5. package/src/components/AutomationSlice.res +1 -5
  6. package/src/components/CapabilityManifest.res +2 -3
  7. package/src/components/DcbDecode.res +6 -2
  8. package/src/components/DcbScopeInference.res +16 -12
  9. package/src/components/DcbTag.res +74 -46
  10. package/src/components/DcbValidation.res +114 -89
  11. package/src/components/DisplayName.res +4 -2
  12. package/src/components/ExtensionPoint.res +0 -1
  13. package/src/components/FieldDefault.res +2 -4
  14. package/src/components/InboundTranslationSlice.res +0 -2
  15. package/src/components/OutboundTranslationSlice.res +10 -8
  16. package/src/components/Owner.res +4 -4
  17. package/src/components/Plugin.res +10 -4
  18. package/src/components/ReadModel.res +0 -1
  19. package/src/components/Reference.res +2 -8
  20. package/src/components/Sensitive.res +4 -4
  21. package/src/components/StateAnnotations.res +4 -2
  22. package/src/components/StateChangeSlice.res +0 -2
  23. package/src/components/StateViewSlice.res +0 -2
  24. package/src/components/TaggedUnion.res +1 -2
  25. package/src/components/Task.res +0 -1
  26. package/src/components/TraitCertificate.res +2 -2
  27. package/src/components/TraitManifest.res +1 -1
  28. package/src/generator/CertifyTrait.res +2 -4
  29. package/src/generator/Codegen.res +77 -68
  30. package/src/generator/Discovery.res +29 -12
  31. package/src/generator/GraftTrait.res +17 -9
  32. package/src/generator/Pairing.res +39 -13
  33. package/src/generator/PlatformCodegen.res +13 -11
  34. package/src/generator/PlatformManifests.res +1 -3
  35. package/src/generator/PluginGenerator.res +0 -1
  36. package/src/generator/TraitManifestCli.res +7 -7
  37. package/src/lifecycle/CheckLifecycleModel.res +173 -145
  38. package/src/lifecycle/CheckLifecycleModel.res.mjs +17 -2
  39. package/src/semantic/Bytes.res +0 -1
  40. package/src/semantic/CalendarDate.res +2 -2
  41. package/src/semantic/Capabilities.res +4 -6
  42. package/src/semantic/Capabilities.res.mjs +9 -11
  43. package/src/semantic/CapabilityNeed.res +4 -5
  44. package/src/semantic/CaptionedImage.res +0 -1
  45. package/src/semantic/Color.res +0 -1
  46. package/src/semantic/Currency.res +189 -170
  47. package/src/semantic/DateRange.res +3 -4
  48. package/src/semantic/DateTime.res +5 -5
  49. package/src/semantic/Duration.res +2 -2
  50. package/src/semantic/Email.res +0 -1
  51. package/src/semantic/FileRef.res +2 -2
  52. package/src/semantic/GeoPoint.res +17 -21
  53. package/src/semantic/Geocoding.res +8 -9
  54. package/src/semantic/Geolocation.res +6 -9
  55. package/src/semantic/ImageRef.res +2 -2
  56. package/src/semantic/MemberRef.res +0 -1
  57. package/src/semantic/Messaging.res +141 -21
  58. package/src/semantic/Messaging.res.mjs +51 -8
  59. package/src/semantic/Money.res +23 -20
  60. package/src/semantic/Offload.res +6 -4
  61. package/src/semantic/Percent.res +3 -3
  62. package/src/semantic/Phone.res +0 -1
  63. package/src/semantic/RowImage.res +4 -3
  64. package/src/semantic/Semantic.res +8 -11
  65. package/src/semantic/StorageRef.res +17 -17
  66. package/src/semantic/Template.res +5 -7
  67. package/src/semantic/UploadableFile.res +0 -1
  68. package/src/semantic/UploadableImage.res +0 -1
  69. package/src/semantic/Url.res +3 -3
  70. package/src/types/Authorization.res +1 -2
  71. package/src/types/Handler.res +0 -1
  72. package/src/types/Identity.res +1 -3
  73. package/src/types/Lifecycle.res +7 -6
  74. package/src/types/Message.res +6 -4
  75. package/src/types/OwnerScope.res +1 -3
  76. package/src/types/Projection.res +9 -2
  77. package/src/types/StoredEvent.res +2 -4
  78. package/src/types/Transition.res +8 -4
  79. package/src/util/Util_Sury.res +1 -4
@@ -20,40 +20,35 @@ let renderSlices = (
20
20
  stems: array<string>,
21
21
  ): array<string> =>
22
22
  stems->Array.map(stem =>
23
- " module "
24
- ++ stem
25
- ++ suffix
26
- ++ " = Platform."
27
- ++ platformFactory
28
- ++ ".Make("
29
- ++ stem
30
- ++ ", "
31
- ++ stem
32
- ++ implSuffix
33
- ++ ")"
23
+ " module " ++
24
+ stem ++
25
+ suffix ++
26
+ " = Platform." ++
27
+ platformFactory ++
28
+ ".Make(" ++
29
+ stem ++
30
+ ", " ++
31
+ stem ++
32
+ implSuffix ++ ")"
34
33
  )
35
34
 
36
35
  // StateChangeSlices opt into async dispatch via `@@reventless.async` on the
37
36
  // spec file; the rendered factory becomes MakeAsync (CommandPending response).
38
- let renderStateChangeSlices = (
39
- stems: array<string>,
40
- asyncStems: Dict.t<bool>,
41
- ): array<string> =>
37
+ let renderStateChangeSlices = (stems: array<string>, asyncStems: Dict.t<bool>): array<string> =>
42
38
  stems->Array.map(stem => {
43
39
  let factory = switch Dict.get(asyncStems, stem) {
44
40
  | Some(true) => "MakeAsync"
45
41
  | _ => "Make"
46
42
  }
47
- " module "
48
- ++ stem
49
- ++ "Slice = Platform.StateChangeSlice."
50
- ++ factory
51
- ++ "("
52
- ++ stem
53
- ++ ", "
54
- ++ stem
55
- ++ Pairing.implSuffixForStateChange
56
- ++ ")"
43
+ " module " ++
44
+ stem ++
45
+ "Slice = Platform.StateChangeSlice." ++
46
+ factory ++
47
+ "(" ++
48
+ stem ++
49
+ ", " ++
50
+ stem ++
51
+ Pairing.implSuffixForStateChange ++ ")"
57
52
  })
58
53
 
59
54
  // AutomationSlice — 2-arg form. The merged `_Automation.res` shape exposes
@@ -63,13 +58,12 @@ let renderStateChangeSlices = (
63
58
  // from the sibling `_Mappings.res`.
64
59
  let renderAutomationSlices = (stems: array<string>): array<string> =>
65
60
  stems->Array.map(stem =>
66
- " module "
67
- ++ stem
68
- ++ "Slice = Platform.AutomationSlice.Make("
69
- ++ stem
70
- ++ ", "
71
- ++ stem
72
- ++ "_Automation)"
61
+ " module " ++
62
+ stem ++
63
+ "Slice = Platform.AutomationSlice.Make(" ++
64
+ stem ++
65
+ ", " ++
66
+ stem ++ "_Automation)"
73
67
  )
74
68
 
75
69
  let renderAggregates = (aggregates: array<Pairing.aggregateDef>): array<string> =>
@@ -99,14 +93,19 @@ let renderReadModels = (readModels: array<Pairing.readModelDef>): array<string>
99
93
  // platform wires a StateTopic Lambda for AppSync Events (Source B) live updates.
100
94
  let factory = stream ? "Platform.ReadModelStream.Make" : "Platform.ReadModel.Make"
101
95
  [
102
- " module " ++ wrapperName ++ " = " ++ factory ++ "(" ++ readModel ++ ", " ++ projections ++ ")",
96
+ " module " ++
97
+ wrapperName ++
98
+ " = " ++
99
+ factory ++
100
+ "(" ++
101
+ readModel ++
102
+ ", " ++
103
+ projections ++ ")",
103
104
  ]
104
105
  })
105
106
 
106
107
  let renderTasks = (tasks: array<string>): array<string> =>
107
- tasks->Array.map(stem =>
108
- " module " ++ stem ++ "Task = Platform.Task.Make(" ++ stem ++ ")"
109
- )
108
+ tasks->Array.map(stem => " module " ++ stem ++ "Task = Platform.Task.Make(" ++ stem ++ ")")
110
109
 
111
110
  let renderExtensionPoints = (extensionPoints: array<Pairing.extensionPointDef>): array<string> =>
112
111
  extensionPoints->Array.flatMap(({group, mappings}) => {
@@ -118,8 +117,7 @@ let renderExtensionPoints = (extensionPoints: array<Pairing.extensionPointDef>):
118
117
  }
119
118
  switch count {
120
119
  | 0 => []
121
- | 1 =>
122
- [" module " ++ moduleName ++ " = Platform.ExtensionPoint.Make(" ++ firstMapping ++ ")"]
120
+ | 1 => [" module " ++ moduleName ++ " = Platform.ExtensionPoint.Make(" ++ firstMapping ++ ")"]
123
121
  | 2 =>
124
122
  let m2 = mappings->Array.getUnsafe(1)
125
123
  [
@@ -140,8 +138,7 @@ let renderExtensionPoints = (extensionPoints: array<Pairing.extensionPointDef>):
140
138
  ]
141
139
  | _ =>
142
140
  // MakeMulti — inline module expression
143
- let mappingLines =
144
- mappings->Array.map(m => " module(" ++ m ++ "),")
141
+ let mappingLines = mappings->Array.map(m => " module(" ++ m ++ "),")
145
142
  Array.flat([
146
143
  [
147
144
  " module " ++ moduleName ++ " = Platform.ExtensionPoint.MakeMulti({",
@@ -152,10 +149,7 @@ let renderExtensionPoints = (extensionPoints: array<Pairing.extensionPointDef>):
152
149
  " let mappings: array<module(Mapping)> = [",
153
150
  ],
154
151
  mappingLines,
155
- [
156
- " ]",
157
- " })",
158
- ],
152
+ [" ]", " })"],
159
153
  ])
160
154
  }
161
155
  })
@@ -172,11 +166,9 @@ let renderExtensions = (extensions: array<string>): array<string> =>
172
166
 
173
167
  // ── make() call ──────────────────────────────────────────────────────────────
174
168
 
175
- let renderMakeParam = (
176
- ~param: string,
177
- ~items: array<string>,
178
- ~moduleSuffix: string,
179
- ): option<string> =>
169
+ let renderMakeParam = (~param: string, ~items: array<string>, ~moduleSuffix: string): option<
170
+ string,
171
+ > =>
180
172
  if items->Array.length === 0 {
181
173
  None
182
174
  } else {
@@ -184,10 +176,9 @@ let renderMakeParam = (
184
176
  Some(" ~" ++ param ++ "=[" ++ entries->Array.join(", ") ++ "],")
185
177
  }
186
178
 
187
- let renderStateViewSlicesMakeParam = (
188
- slices: array<string>,
189
- streamSlices: array<string>,
190
- ): option<string> => {
179
+ let renderStateViewSlicesMakeParam = (slices: array<string>, streamSlices: array<string>): option<
180
+ string,
181
+ > => {
191
182
  let entries = Array.flat([
192
183
  slices->Array.map(s => "module(" ++ s ++ "Slice)"),
193
184
  streamSlices->Array.map(s => "module(" ++ s ++ "StreamSlice)"),
@@ -314,9 +305,7 @@ let validateComponentRuntimeKeys = (
314
305
  )
315
306
  }
316
307
 
317
- let renderComponentRuntimeParam = (
318
- componentRuntime: dict<Config.runtimeHints>,
319
- ): option<string> => {
308
+ let renderComponentRuntimeParam = (componentRuntime: dict<Config.runtimeHints>): option<string> => {
320
309
  let entries = componentRuntime->Dict.toArray
321
310
  if entries->Array.length === 0 {
322
311
  None
@@ -419,6 +408,7 @@ let renderPluginStructureCall = (
419
408
  let entries = epMappingStems->Array.map(s => "module(" ++ s ++ ")")
420
409
  ls->Array.push(" ~extensionPoints=[" ++ entries->Array.join(", ") ++ "],")
421
410
  }
411
+
422
412
  // Chapter grouping bands per component, captured from the source folder layout.
423
413
  // Only emitted when at least one component lives under a chapter folder, so
424
414
  // plugins with a flat `src/` keep a byte-identical generated Plugin.res.
@@ -427,8 +417,11 @@ let renderPluginStructureCall = (
427
417
  componentChapters->Array.map(((stem, chapter)) =>
428
418
  "(\"" ++ stem ++ "\", \"" ++ chapter ++ "\")"
429
419
  )
430
- ls->Array.push(" ~componentChapters=Dict.fromArray([" ++ entries->Array.join(", ") ++ "]),")
420
+ ls->Array.push(
421
+ " ~componentChapters=Dict.fromArray([" ++ entries->Array.join(", ") ++ "]),",
422
+ )
431
423
  }
424
+
432
425
  // What the plugin's own scenarios say about each command's lifecycle edge.
433
426
  // Written by `check:lifecycle:update`, not here: the sidecars it harvests are
434
427
  // produced by the build this generator runs *before*, so a generator-written
@@ -463,7 +456,18 @@ let validateUniqueSpecStems = (~discovered: array<Discovery.discoveredFile>) =>
463
456
  ->Dict.toArray
464
457
  ->Array.forEach(((stem, paths)) => {
465
458
  if paths->Array.length > 1 {
466
- let pathList = paths->Array.toSorted((a, b) => if a < b {-1.0} else if a > b {1.0} else {0.0})->Array.join("\n - ")
459
+ let pathList =
460
+ paths
461
+ ->Array.toSorted((a, b) =>
462
+ if a < b {
463
+ -1.0
464
+ } else if a > b {
465
+ 1.0
466
+ } else {
467
+ 0.0
468
+ }
469
+ )
470
+ ->Array.join("\n - ")
467
471
  JsError.throwWithMessage(
468
472
  "Generator: stem `" ++
469
473
  stem ++
@@ -493,7 +497,7 @@ let validateSliceTargets = (~resolved: Pairing.resolved) => {
493
497
  "` is missing `let targetName = \"...\"`. Valid targets: " ++
494
498
  knownReceivers->Array.join(", "),
495
499
  )
496
- | Some(t) when !(knownReceivers->Array.includes(t)) =>
500
+ | Some(t) if !(knownReceivers->Array.includes(t)) =>
497
501
  JsError.throwWithMessage(
498
502
  "Generator: " ++
499
503
  kind ++
@@ -526,7 +530,7 @@ let validateSliceTargets = (~resolved: Pairing.resolved) => {
526
530
  // OutboundTranslationSlice: only validate when targetName = Some(name) (None = fire-and-forget)
527
531
  resolved.outboundTranslationSlices->Array.forEach(stem => {
528
532
  switch resolved.outboundTranslationSliceTargets->Dict.get(stem)->Option.getOr(None) {
529
- | Some(t) when !(knownReceivers->Array.includes(t)) =>
533
+ | Some(t) if !(knownReceivers->Array.includes(t)) =>
530
534
  JsError.throwWithMessage(
531
535
  "Generator: OutboundTranslationSlice `" ++
532
536
  stem ++
@@ -616,9 +620,9 @@ let renderComposition = (
616
620
  lines->Array.push("")
617
621
  lines->Array.push(
618
622
  "@val external uiBundleUrl: option<string> = \"process.env." ++
619
- pluginNameToEnvBase(config.name) ++
620
- "_UI_BUNDLE_URL\"",
623
+ pluginNameToEnvBase(config.name) ++ "_UI_BUNDLE_URL\"",
621
624
  )
625
+
622
626
  // The DCB boundary's slices as schemas, outside `Make` because the scope they
623
627
  // determine is a property of the specs and not of any platform. Emitted rather
624
628
  // than hand-listed so a slice added tomorrow is in it: this is the only value
@@ -651,10 +655,7 @@ let renderComposition = (
651
655
  // StateChangeSlices — per-stem MakeAsync opt-in via @@reventless.async
652
656
  if resolved.stateChangeSlices->Array.length > 0 {
653
657
  lines->Array.push(" // StateChangeSlices")
654
- renderStateChangeSlices(
655
- resolved.stateChangeSlices,
656
- resolved.asyncStateChangeSlices,
657
- )->push
658
+ renderStateChangeSlices(resolved.stateChangeSlices, resolved.asyncStateChangeSlices)->push
658
659
  }
659
660
 
660
661
  // StateViewSlices
@@ -794,9 +795,17 @@ let renderComposition = (
794
795
  renderAggregateMakeParam(resolved.aggregates),
795
796
  renderReadModelMakeParam(resolved.readModels),
796
797
  renderTaskMakeParam(resolved.tasks),
797
- renderMakeParam(~param="stateChangeSlices", ~items=resolved.stateChangeSlices, ~moduleSuffix="Slice"),
798
+ renderMakeParam(
799
+ ~param="stateChangeSlices",
800
+ ~items=resolved.stateChangeSlices,
801
+ ~moduleSuffix="Slice",
802
+ ),
798
803
  renderStateViewSlicesMakeParam(resolved.stateViewSlices, resolved.stateViewSlicesStream),
799
- renderMakeParam(~param="automationSlices", ~items=resolved.automationSlices, ~moduleSuffix="Slice"),
804
+ renderMakeParam(
805
+ ~param="automationSlices",
806
+ ~items=resolved.automationSlices,
807
+ ~moduleSuffix="Slice",
808
+ ),
800
809
  renderMakeParam(
801
810
  ~param="outboundTranslationSlices",
802
811
  ~items=resolved.outboundTranslationSlices,
@@ -18,7 +18,12 @@ type componentType = ComponentKind.t =
18
18
  | Extension
19
19
 
20
20
  // relPath: path relative to srcDir (e.g. "ReadModel/ProductsProjections.res")
21
- type discoveredFile = {stem: string, componentType: componentType, epGroup: option<string>, relPath: string}
21
+ type discoveredFile = {
22
+ stem: string,
23
+ componentType: componentType,
24
+ epGroup: option<string>,
25
+ relPath: string,
26
+ }
22
27
 
23
28
  let folderToComponentType = ComponentKind.folderToKind
24
29
 
@@ -32,6 +37,7 @@ let folderToComponentType = ComponentKind.folderToKind
32
37
  // docs/plans/done/deployed-chapter-grouping.md.
33
38
  let chapterOf = (relPath: string): option<string> => {
34
39
  let segments = relPath->String.split("/")
40
+
35
41
  // Need at least one directory segment before the filename.
36
42
  if segments->Array.length < 2 {
37
43
  None
@@ -76,7 +82,7 @@ let isAlwaysExcludedDir = (name: string): bool =>
76
82
  let matchesGlob = (path: string, pattern: string): bool =>
77
83
  if pattern === "**" {
78
84
  true
79
- } else if not(pattern->String.includes("*")) {
85
+ } else if !(pattern->String.includes("*")) {
80
86
  // Exact file match or directory prefix
81
87
  path === pattern || path->String.startsWith(pattern ++ "/")
82
88
  } else if pattern->String.endsWith("/**") {
@@ -85,9 +91,11 @@ let matchesGlob = (path: string, pattern: string): bool =>
85
91
  } else if pattern->String.endsWith("/*") {
86
92
  let prefix = pattern->String.slice(~start=0, ~end=pattern->String.length - 2)
87
93
  path->String.startsWith(prefix ++ "/") &&
88
- !(path
89
- ->String.slice(~start=prefix->String.length + 1, ~end=path->String.length)
90
- ->String.includes("/"))
94
+ !(
95
+ path
96
+ ->String.slice(~start=prefix->String.length + 1, ~end=path->String.length)
97
+ ->String.includes("/")
98
+ )
91
99
  } else {
92
100
  path === pattern
93
101
  }
@@ -103,14 +111,14 @@ let stemOf = (filename: string): option<string> =>
103
111
  }
104
112
 
105
113
  let isSkipped = (stem: string): bool =>
106
- stem->String.endsWith("Test")
107
- || stem->String.endsWith("Fixtures")
114
+ stem->String.endsWith("Test") ||
115
+ stem->String.endsWith("Fixtures") ||
108
116
  // `_EventMappings` and `_Mappings` files in per-entity Aggregate/ folders
109
117
  // are picked up by the dedicated [Pairing.findEventMappings] walker;
110
118
  // treating them as Aggregate specs would hunt for a non-existent
111
119
  // `_EventMappingsBehavior` / `_MappingsBehavior`.
112
- || stem->String.endsWith("_EventMappings")
113
- || stem->String.endsWith("_Mappings")
120
+ stem->String.endsWith("_EventMappings") ||
121
+ stem->String.endsWith("_Mappings")
114
122
 
115
123
  // Collect .res files directly in dir (non-recursive) into acc.
116
124
  let collectFiles = (
@@ -124,7 +132,11 @@ let collectFiles = (
124
132
  Generator_Node.readDir(dir)->Array.forEach(entry => {
125
133
  if entry->NodeFs.isFile {
126
134
  let filename = entry->NodeFs.direntName
127
- let relPath = if relDir === "" {filename} else {relDir ++ "/" ++ filename}
135
+ let relPath = if relDir === "" {
136
+ filename
137
+ } else {
138
+ relDir ++ "/" ++ filename
139
+ }
128
140
  if !isExcluded(relPath, exclude) {
129
141
  switch stemOf(filename) {
130
142
  | Some(stem) if !isSkipped(stem) => acc->Array.push({stem, componentType, epGroup, relPath})
@@ -145,7 +157,11 @@ let rec walkDir = (
145
157
  ): unit =>
146
158
  Generator_Node.readDir(dir)->Array.forEach(entry => {
147
159
  let entryName = entry->NodeFs.direntName
148
- let relPath = if relDir === "" {entryName} else {relDir ++ "/" ++ entryName}
160
+ let relPath = if relDir === "" {
161
+ entryName
162
+ } else {
163
+ relDir ++ "/" ++ entryName
164
+ }
149
165
 
150
166
  if isExcluded(relPath, exclude) {
151
167
  ()
@@ -215,7 +231,8 @@ let rec walkDir = (
215
231
  let filename = entryName
216
232
  if !isExcluded(relPath, exclude) {
217
233
  switch stemOf(filename) {
218
- | Some(stem) if !isSkipped(stem) => acc->Array.push({stem, componentType: ct, epGroup, relPath})
234
+ | Some(stem) if !isSkipped(stem) =>
235
+ acc->Array.push({stem, componentType: ct, epGroup, relPath})
219
236
  | _ => ()
220
237
  }
221
238
  }
@@ -157,8 +157,7 @@ let main = async () => {
157
157
  | _ =>
158
158
  fail(
159
159
  `${traitPackage} ships no scaffold (looked for ${specifier}).\n` ++
160
- ` Not every trait has one — a trait whose graft is all patches has nothing to write.\n` ++
161
- ` Write the graft by hand instead; its README says what the host must declare.`,
160
+ ` Not every trait has one — a trait whose graft is all patches has nothing to write.\n` ++ ` Write the graft by hand instead; its README says what the host must declare.`,
162
161
  )
163
162
  %raw(`undefined`)
164
163
  }
@@ -177,11 +176,14 @@ let main = async () => {
177
176
  )
178
177
  ["into", "tests", "dry-run"]->Array.forEach(k => raw->Dict.delete(k))
179
178
 
180
- let config = try raw->JSON.Encode.object->Util_Sury.fromJson(scaffold.configSchema) catch {
179
+ let config = try raw
180
+ ->JSON.Encode.object
181
+ ->Util_Sury.fromJson(scaffold.configSchema) catch {
181
182
  | exn => {
182
183
  fail(
183
- `${traitPackage} refused this config: ${Util_Sury.exnMessage(exn)}\n` ++
184
- ` Every --key is a field of the trait's own config; it decides what it needs.`,
184
+ `${traitPackage} refused this config: ${Util_Sury.exnMessage(
185
+ exn,
186
+ )}\n` ++ ` Every --key is a field of the trait's own config; it decides what it needs.`,
185
187
  )
186
188
  %raw(`undefined`)
187
189
  }
@@ -191,7 +193,12 @@ let main = async () => {
191
193
 
192
194
  files->Array.forEach(({path, contents}) =>
193
195
  if dryRun {
194
- Console.log(`Would write: ${path} (${contents->String.split("\n")->Array.length->Int.toString} lines)`)
196
+ Console.log(
197
+ `Would write: ${path} (${contents
198
+ ->String.split("\n")
199
+ ->Array.length
200
+ ->Int.toString} lines)`,
201
+ )
195
202
  } else if NodeFs.existsSync(path) {
196
203
  // Never overwrite. From the moment a graft lands it is the host's
197
204
  // source, edited with the policy the trait deliberately does not
@@ -207,7 +214,9 @@ let main = async () => {
207
214
 
208
215
  if patches->Array.length > 0 {
209
216
  Console.log("")
210
- Console.log("── Paste these; they go into files you already own ──────────────")
217
+ Console.log(
218
+ "── Paste these; they go into files you already own ──────────────",
219
+ )
211
220
  patches->Array.forEach(({into, at, contents}) => {
212
221
  Console.log("")
213
222
  Console.log(`# ${into} — ${at}`)
@@ -216,8 +225,7 @@ let main = async () => {
216
225
  Console.log("")
217
226
  Console.log(
218
227
  "Printed rather than written: placing an arm in an existing ordered `switch`\n" ++
219
- "is an AST operation, and a text splice into the wrong arm is a bug the\n" ++
220
- "compiler cannot see.",
228
+ "is an AST operation, and a text splice into the wrong arm is a bug the\n" ++ "compiler cannot see.",
221
229
  )
222
230
  }
223
231
 
@@ -21,11 +21,11 @@ let implSuffixForTranslation = "_Translation"
21
21
  let mappingsSuffixForAutomation = "_Mappings"
22
22
 
23
23
  let isImplStem = (stem: string): bool =>
24
- stem->String.endsWith(implSuffixForStateChange)
25
- || stem->String.endsWith(implSuffixForStateView)
26
- || stem->String.endsWith(implSuffixForAutomation)
27
- || stem->String.endsWith(implSuffixForTranslation)
28
- || stem->String.endsWith(mappingsSuffixForAutomation)
24
+ stem->String.endsWith(implSuffixForStateChange) ||
25
+ stem->String.endsWith(implSuffixForStateView) ||
26
+ stem->String.endsWith(implSuffixForAutomation) ||
27
+ stem->String.endsWith(implSuffixForTranslation) ||
28
+ stem->String.endsWith(mappingsSuffixForAutomation)
29
29
 
30
30
  type aggregateDef = {spec: string, behavior: string, eventMappings: option<string>, isAsync: bool}
31
31
  // readModelDef pairs a ReadModel spec with its sibling `_Projections.res`
@@ -119,7 +119,15 @@ let findEventMappings = (~srcDir: string): Dict.t<string> => {
119
119
  }
120
120
 
121
121
  let sortedStems = (stems: array<string>): array<string> =>
122
- stems->Array.toSorted((a, b) => if a < b {-1.0} else if a > b {1.0} else {0.0})
122
+ stems->Array.toSorted((a, b) =>
123
+ if a < b {
124
+ -1.0
125
+ } else if a > b {
126
+ 1.0
127
+ } else {
128
+ 0.0
129
+ }
130
+ )
123
131
 
124
132
  // Read a slice spec file and extract the value of `let targetName = ...`.
125
133
  // Returns Some(name) for string literals (including inside Some("...")),
@@ -128,7 +136,9 @@ let extractTargetName = (filePath: string): option<string> => {
128
136
  try {
129
137
  let content = NodeFs.readFileSync(filePath)
130
138
  let result = ref(None)
131
- content->String.split("\n")->Array.forEach(line => {
139
+ content
140
+ ->String.split("\n")
141
+ ->Array.forEach(line => {
132
142
  let trimmed = line->String.trimStart
133
143
  if trimmed->String.startsWith("let targetName = ") {
134
144
  let firstQuote = trimmed->String.indexOf("\"")
@@ -153,8 +163,11 @@ let hasFileAttribute = (filePath: string, ~attr: string): bool => {
153
163
  try {
154
164
  let content = NodeFs.readFileSync(filePath)
155
165
  let found = ref(false)
156
- content->String.split("\n")->Array.forEach(line => {
166
+ content
167
+ ->String.split("\n")
168
+ ->Array.forEach(line => {
157
169
  let trimmed = line->String.trimStart
170
+
158
171
  // Match the bare attribute and tolerate a payload arg in the future.
159
172
  if (
160
173
  trimmed->String.startsWith(attr ++ " ") ||
@@ -189,7 +202,9 @@ let effectiveSpecName = (filePath: string, ~stem: string): string => {
189
202
  try {
190
203
  let content = NodeFs.readFileSync(filePath)
191
204
  let name = ref(None)
192
- content->String.split("\n")->Array.forEach(line => {
205
+ content
206
+ ->String.split("\n")
207
+ ->Array.forEach(line => {
193
208
  let trimmed = line->String.trimStart
194
209
  if trimmed->String.startsWith("@@reventless.spec(") {
195
210
  let firstQuote = trimmed->String.indexOf("\"")
@@ -356,7 +371,14 @@ let resolve = (discovered: array<Discovery.discoveredFile>, ~srcDir: string): re
356
371
  isAsync,
357
372
  })
358
373
  | None =>
359
- Console.warn("Generator: Aggregate spec `" ++ spec ++ "` has no matching `" ++ underscored ++ "` or `" ++ bare ++ "` — skipping")
374
+ Console.warn(
375
+ "Generator: Aggregate spec `" ++
376
+ spec ++
377
+ "` has no matching `" ++
378
+ underscored ++
379
+ "` or `" ++
380
+ bare ++ "` — skipping",
381
+ )
360
382
  None
361
383
  }
362
384
  })
@@ -377,10 +399,14 @@ let resolve = (discovered: array<Discovery.discoveredFile>, ~srcDir: string): re
377
399
  let underscoredProj = baseName ++ "_Projections"
378
400
  switch Dict.get(projectionsByRelPath, underscoredProj) {
379
401
  | None =>
380
- Console.warn("Generator: ReadModel `" ++ rm ++ "` has no matching `" ++ underscoredProj ++ ".res` — skipping")
402
+ Console.warn(
403
+ "Generator: ReadModel `" ++
404
+ rm ++
405
+ "` has no matching `" ++
406
+ underscoredProj ++ ".res` — skipping",
407
+ )
381
408
  None
382
- | Some(_) =>
383
- Some({readModel: rm, projections: underscoredProj, stream})
409
+ | Some(_) => Some({readModel: rm, projections: underscoredProj, stream})
384
410
  }
385
411
  }
386
412
  let readModels = Array.concat(
@@ -44,16 +44,20 @@ let union = (manifests: array<pluginManifest>): array<unionEntry> => {
44
44
  let byKey: dict<unionEntry> = Dict.make()
45
45
  manifests->Array.forEach(({pluginName, manifest}) =>
46
46
  manifest.capabilities->Array.forEach(entry => {
47
- let sites =
48
- entry.declaredBy->Array.map(site => {
47
+ let sites = entry.declaredBy->Array.map(
48
+ site => {
49
49
  pluginName,
50
50
  component: site.component,
51
51
  field: site.field,
52
52
  annotation: site.annotation,
53
- })
53
+ },
54
+ )
54
55
  switch byKey->Dict.get(entry.key) {
55
56
  | Some(existing) =>
56
- byKey->Dict.set(entry.key, {...existing, declaredBy: existing.declaredBy->Array.concat(sites)})
57
+ byKey->Dict.set(
58
+ entry.key,
59
+ {...existing, declaredBy: existing.declaredBy->Array.concat(sites)},
60
+ )
57
61
  | None => byKey->Dict.set(entry.key, {kind: entry.kind, key: entry.key, declaredBy: sites})
58
62
  }
59
63
  })
@@ -71,10 +75,7 @@ let quote = (s: string): string => s->JSON.Encode.string->JSON.stringify
71
75
  let splitKey = (key: string): option<(string, string)> =>
72
76
  key
73
77
  ->String.indexOfOpt(".")
74
- ->Option.map(i => (
75
- key->String.slice(~start=0, ~end=i),
76
- key->String.slice(~start=i + 1),
77
- ))
78
+ ->Option.map(i => (key->String.slice(~start=0, ~end=i), key->String.slice(~start=i + 1)))
78
79
 
79
80
  // The provenance comment names the store as the field spells it — taken from
80
81
  // the manifest, which recorded it at the one place the owning plugin was
@@ -109,7 +110,9 @@ let renderEntry = (entry: unionEntry): result<array<string>, string> =>
109
110
  switch splitKey(entry.key) {
110
111
  | None =>
111
112
  Error(
112
- `malformed capability key ${quote(entry.key)} — expected "{plugin}.{store}" (is the plugin's capabilities.json hand-edited?)`,
113
+ `malformed capability key ${quote(
114
+ entry.key,
115
+ )} — expected "{plugin}.{store}" (is the plugin's capabilities.json hand-edited?)`,
113
116
  )
114
117
  | Some((plugin, store)) =>
115
118
  Ok(
@@ -176,8 +179,7 @@ let duplicatePluginMessage = ((plugin, claimants): (string, array<string>)): str
176
179
  )}.\n` ++
177
180
  ` A platform keys its plugin registry by name, so the second registration is read as a new ` ++
178
181
  `VERSION of the first and supersedes it.\n` ++
179
- ` Give each deployable's plugin a distinct name (plugin.json), or — if these were meant to be ` ++
180
- `one plugin — deploy only one of them.`
182
+ ` Give each deployable's plugin a distinct name (plugin.json), or — if these were meant to be ` ++ `one plugin — deploy only one of them.`
181
183
 
182
184
  let header = [
183
185
  "// AUTO-GENERATED — do not edit. Run `pnpm run generate:platform` to update.",
@@ -75,9 +75,7 @@ let scriptsOf = (pkg: dict<JSON.t>): array<string> =>
75
75
  pkg
76
76
  ->Dict.get("scripts")
77
77
  ->Option.flatMap(asObject)
78
- ->Option.mapOr([], scripts =>
79
- scripts->Dict.valuesToArray->Array.filterMap(JSON.Decode.string)
80
- )
78
+ ->Option.mapOr([], scripts => scripts->Dict.valuesToArray->Array.filterMap(JSON.Decode.string))
81
79
 
82
80
  // The composition `src/` an `-aws` root is generated from, read off the script
83
81
  // that performs the generation.
@@ -2,7 +2,6 @@
2
2
  // Usage: generate-plugin <srcDir>
3
3
  // generate-plugin --aws <Namespace> <srcDir>
4
4
 
5
-
6
5
  let () = {
7
6
  let argv2 = NodeProcess.argv->Array.get(2)->Option.getOr("")
8
7
  let argv3 = NodeProcess.argv->Array.get(3)->Option.getOr("")
@@ -48,7 +48,9 @@ let moduleBase = (traitPackage: string) =>
48
48
 
49
49
  let resolveFrom = (specifier: string) =>
50
50
  try Some(
51
- NodeModule.createRequire(NodeProcess.cwd() ++ "/index.js")->NodeModule.requireResolve(specifier),
51
+ NodeModule.createRequire(NodeProcess.cwd() ++ "/index.js")->NodeModule.requireResolve(
52
+ specifier,
53
+ ),
52
54
  ) catch {
53
55
  | _ => None
54
56
  }
@@ -80,8 +82,7 @@ let main = async () => {
80
82
  let packageJson = switch resolveFrom(`${traitPackage}/package.json`) {
81
83
  | None =>
82
84
  fail(
83
- `${traitPackage} is not installed here. A manifest is derived from the trait, so ` ++
84
- `the trait has to be resolvable.`,
85
+ `${traitPackage} is not installed here. A manifest is derived from the trait, so ` ++ `the trait has to be resolvable.`,
85
86
  )
86
87
  JSON.Encode.null
87
88
  | Some(path) => NodeFs.readFileSync(path)->JSON.parseOrThrow
@@ -95,8 +96,7 @@ let main = async () => {
95
96
  | None =>
96
97
  fail(
97
98
  `${traitPackage} exports no ${base} module, so its capability needs cannot be read.\n` ++
98
- ` A trait states them as a value — an empty array if it brokers nothing — because ` ++
99
- `an unstated need fails silently at run time.`,
99
+ ` A trait states them as a value — an empty array if it brokers nothing — because ` ++ `an unstated need fails silently at run time.`,
100
100
  )
101
101
  %raw(`undefined`)
102
102
  | Some(path) => await dynImport(NodeUrl.pathToFileURL(path)["href"])
@@ -127,8 +127,8 @@ let main = async () => {
127
127
  NodeFs.writeFileSync(out, manifest->TraitManifest.render)
128
128
  Console.log(
129
129
  `trait-manifest: ${manifest.trait}@${manifest.version} — ` ++
130
- `${(manifest.capabilities->Array.length)->Int.toString} capabilit(ies), ` ++
131
- `${(manifest.config->Array.length)->Int.toString} config field(s)`,
130
+ `${manifest.capabilities->Array.length->Int.toString} capabilit(ies), ` ++
131
+ `${manifest.config->Array.length->Int.toString} config field(s)`,
132
132
  )
133
133
  Console.log(`Wrote: ${out}`)
134
134
  }