@reventlessdev/reventless-spec 3.0.0-alpha.62 → 3.0.0-alpha.64

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.
@@ -1,61 +1,9 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
3
  import * as Nodepath from "node:path";
4
+ import * as ComponentKind$Reventless from "../components/ComponentKind.res.mjs";
4
5
  import * as Generator_Node$Reventless from "./Generator_Node.res.mjs";
5
6
 
6
- function folderToComponentType(folder) {
7
- switch (folder) {
8
- case "Aggregate" :
9
- case "Aggregates" :
10
- return "Aggregate";
11
- case "Automation" :
12
- case "AutomationSlice" :
13
- case "AutomationSlices" :
14
- case "Automations" :
15
- return "AutomationSlice";
16
- case "ExtensionPoint" :
17
- case "ExtensionPoints" :
18
- return "ExtensionPoint";
19
- case "Extension" :
20
- case "Extensions" :
21
- return "Extension";
22
- case "InboundTranslation" :
23
- case "InboundTranslationSlice" :
24
- case "InboundTranslationSlices" :
25
- case "InboundTranslations" :
26
- return "InboundTranslationSlice";
27
- case "OutboundTranslation" :
28
- case "OutboundTranslationSlice" :
29
- case "OutboundTranslationSlices" :
30
- case "OutboundTranslations" :
31
- return "OutboundTranslationSlice";
32
- case "ReadModelStream" :
33
- case "ReadModelStreams" :
34
- return "ReadModelStream";
35
- case "ReadModel" :
36
- case "ReadModels" :
37
- return "ReadModel";
38
- case "StateChange" :
39
- case "StateChangeSlice" :
40
- case "StateChangeSlices" :
41
- case "StateChanges" :
42
- return "StateChangeSlice";
43
- case "StateViewSliceStream" :
44
- case "StateViewSliceStreams" :
45
- return "StateViewSliceStream";
46
- case "StateView" :
47
- case "StateViewSlice" :
48
- case "StateViewSlices" :
49
- case "StateViews" :
50
- return "StateViewSlice";
51
- case "Task" :
52
- case "Tasks" :
53
- return "Task";
54
- default:
55
- return;
56
- }
57
- }
58
-
59
7
  function isAlwaysExcludedDir(name) {
60
8
  if (name === "Plugin" || name === "tests") {
61
9
  return true;
@@ -146,7 +94,7 @@ function walkDir(dir, relDir, parentComponentType, epGroup, exclude, acc) {
146
94
  if (isAlwaysExcludedDir(entryName)) {
147
95
  return;
148
96
  }
149
- let ct = folderToComponentType(entryName);
97
+ let ct = ComponentKind$Reventless.folderToKind(entryName);
150
98
  if (ct === undefined) {
151
99
  return walkDir(Nodepath.join(dir, entryName), relPath, parentComponentType, epGroup, exclude, acc);
152
100
  }
@@ -199,6 +147,8 @@ function scan(srcDir, exclude) {
199
147
  return acc;
200
148
  }
201
149
 
150
+ let folderToComponentType = ComponentKind$Reventless.folderToKind;
151
+
202
152
  export {
203
153
  folderToComponentType,
204
154
  isAlwaysExcludedDir,
@@ -165,6 +165,38 @@ let hasAsyncAttribute = (filePath: string): bool => {
165
165
  }
166
166
  }
167
167
 
168
+ // Partition the ExtensionPoint mapping files into flat (ungrouped) and grouped
169
+ // `extensionPointDef`s: every mapping sharing an `epGroup` collects into one
170
+ // grouped entry; the ungrouped ones share a single flat (`group: None`) entry.
171
+ // Pure (no I/O) so the grouping is unit-testable — in particular the A8 fix:
172
+ // `Option.getOr`'s eager default previously re-created the group's array every
173
+ // iteration, so a group with ≥2 mappings collapsed to `[]` and the generator
174
+ // emitted a `Plugin.res` referencing a never-generated module.
175
+ let groupExtensionPoints = (epFiles: array<Discovery.discoveredFile>): array<extensionPointDef> => {
176
+ let epByGroup: Dict.t<array<string>> = Dict.make()
177
+ let flatEpMappings: array<string> = []
178
+
179
+ epFiles->Array.forEach(({stem, epGroup}) =>
180
+ switch epGroup {
181
+ | None => flatEpMappings->Array.push(stem)
182
+ | Some(g) =>
183
+ switch Dict.get(epByGroup, g) {
184
+ | Some(arr) => arr->Array.push(stem)
185
+ | None => Dict.set(epByGroup, g, [stem])
186
+ }
187
+ }
188
+ )
189
+
190
+ let extensionPoints: array<extensionPointDef> = []
191
+ if flatEpMappings->Array.length > 0 {
192
+ extensionPoints->Array.push({group: None, mappings: flatEpMappings->sortedStems})
193
+ }
194
+ Dict.toArray(epByGroup)->Array.forEach(((group, mappings)) =>
195
+ extensionPoints->Array.push({group: Some(group), mappings: mappings->sortedStems})
196
+ )
197
+ extensionPoints
198
+ }
199
+
168
200
  let resolve = (discovered: array<Discovery.discoveredFile>, ~srcDir: string): resolved => {
169
201
  let eventMappings = findEventMappings(~srcDir)
170
202
 
@@ -309,31 +341,7 @@ let resolve = (discovered: array<Discovery.discoveredFile>, ~srcDir: string): re
309
341
  )
310
342
 
311
343
  // ── ExtensionPoint grouping ────────────────────────────────────────────────
312
- let epByGroup: Dict.t<array<string>> = Dict.make()
313
- let flatEpMappings: array<string> = []
314
-
315
- epFiles->Array.forEach(({stem, epGroup}) => {
316
- switch epGroup {
317
- | None => flatEpMappings->Array.push(stem)
318
- | Some(g) =>
319
- let arr = Dict.get(epByGroup, g)->Option.getOr({
320
- let a: array<string> = []
321
- Dict.set(epByGroup, g, a)
322
- a
323
- })
324
- arr->Array.push(stem)
325
- }
326
- })
327
-
328
- let extensionPoints: array<extensionPointDef> = []
329
-
330
- if flatEpMappings->Array.length > 0 {
331
- extensionPoints->Array.push({group: None, mappings: flatEpMappings->sortedStems})
332
- }
333
-
334
- Dict.toArray(epByGroup)->Array.forEach(((group, mappings)) => {
335
- extensionPoints->Array.push({group: Some(group), mappings: mappings->sortedStems})
336
- })
344
+ let extensionPoints = groupExtensionPoints(epFiles)
337
345
 
338
346
  // ── targetName extraction ──────────────────────────────────────────────────
339
347
  let buildTargets = (stems: array<string>, relPaths: Dict.t<string>): Dict.t<option<string>> => {
@@ -3,7 +3,6 @@
3
3
  import * as Nodefs from "node:fs";
4
4
  import * as Nodepath from "node:path";
5
5
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
6
- import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
7
6
  import * as Generator_Node$Reventless from "./Generator_Node.res.mjs";
8
7
 
9
8
  let implSuffixForStateChange = "_Behavior";
@@ -132,6 +131,39 @@ function hasAsyncAttribute(filePath) {
132
131
  }
133
132
  }
134
133
 
134
+ function groupExtensionPoints(epFiles) {
135
+ let epByGroup = {};
136
+ let flatEpMappings = [];
137
+ epFiles.forEach(param => {
138
+ let epGroup = param.epGroup;
139
+ let stem = param.stem;
140
+ if (epGroup !== undefined) {
141
+ let arr = epByGroup[epGroup];
142
+ if (arr !== undefined) {
143
+ arr.push(stem);
144
+ } else {
145
+ epByGroup[epGroup] = [stem];
146
+ }
147
+ return;
148
+ }
149
+ flatEpMappings.push(stem);
150
+ });
151
+ let extensionPoints = [];
152
+ if (flatEpMappings.length !== 0) {
153
+ extensionPoints.push({
154
+ group: undefined,
155
+ mappings: sortedStems(flatEpMappings)
156
+ });
157
+ }
158
+ Object.entries(epByGroup).forEach(param => {
159
+ extensionPoints.push({
160
+ group: param[0],
161
+ mappings: sortedStems(param[1])
162
+ });
163
+ });
164
+ return extensionPoints;
165
+ }
166
+
135
167
  function resolve(discovered, srcDir) {
136
168
  let eventMappings = findEventMappings(srcDir);
137
169
  let stateChangeSlices = [];
@@ -276,32 +308,7 @@ function resolve(discovered, srcDir) {
276
308
  }
277
309
  };
278
310
  let readModels = Stdlib_Array.filterMap(sortedStems(readModelStems), rm => pairReadModel(false, rm)).concat(Stdlib_Array.filterMap(sortedStems(readModelStreamStems), rm => pairReadModel(true, rm)));
279
- let epByGroup = {};
280
- let flatEpMappings = [];
281
- epFiles.forEach(param => {
282
- let epGroup = param.epGroup;
283
- let stem = param.stem;
284
- if (epGroup !== undefined) {
285
- let a = [];
286
- let arr = Stdlib_Option.getOr(epByGroup[epGroup], (epByGroup[epGroup] = a, a));
287
- arr.push(stem);
288
- return;
289
- }
290
- flatEpMappings.push(stem);
291
- });
292
- let extensionPoints = [];
293
- if (flatEpMappings.length !== 0) {
294
- extensionPoints.push({
295
- group: undefined,
296
- mappings: sortedStems(flatEpMappings)
297
- });
298
- }
299
- Object.entries(epByGroup).forEach(param => {
300
- extensionPoints.push({
301
- group: param[0],
302
- mappings: sortedStems(param[1])
303
- });
304
- });
311
+ let extensionPoints = groupExtensionPoints(epFiles);
305
312
  let buildTargets = (stems, relPaths) => {
306
313
  let dict = {};
307
314
  stems.forEach(stem => {
@@ -348,6 +355,7 @@ export {
348
355
  sortedStems,
349
356
  extractTargetName,
350
357
  hasAsyncAttribute,
358
+ groupExtensionPoints,
351
359
  resolve,
352
360
  }
353
361
  /* node:fs Not a pure module */
@@ -2,6 +2,8 @@
2
2
  // Usage: generate-plugin <srcDir>
3
3
  // generate-plugin --aws <Namespace> <srcDir>
4
4
 
5
+ @val external processExit: int => unit = "process.exit"
6
+
5
7
  let () = {
6
8
  let argv2 = Generator_Node.argv->Array.get(2)->Option.getOr("")
7
9
  let argv3 = Generator_Node.argv->Array.get(3)->Option.getOr("")
@@ -23,6 +25,9 @@ let () = {
23
25
  Console.error("Usage: generate-plugin <srcDir>")
24
26
  Console.error(" generate-plugin --aws <Namespace> <srcDir>")
25
27
  }
28
+ // Exit non-zero on a usage error so `prebuild` (and CI) actually fail
29
+ // instead of continuing green with no Plugin.res generated.
30
+ processExit(1)
26
31
  } else {
27
32
  // Resolve to absolute path (handles relative paths and trailing slashes)
28
33
  let srcDir = Generator_Node.resolve([srcDirArg])
@@ -39,6 +39,7 @@ if (srcDirArg === "") {
39
39
  console.error("Usage: generate-plugin <srcDir>");
40
40
  console.error(" generate-plugin --aws <Namespace> <srcDir>");
41
41
  }
42
+ process.exit(1);
42
43
  } else {
43
44
  let srcDir = Nodepath.resolve(srcDirArg);
44
45
  let init = Config$Reventless.read(srcDir);
@@ -72,6 +72,14 @@ module type T = {
72
72
  */
73
73
  let decide: (state, Spec.command) => result<array<Spec.event>, Spec.error>
74
74
 
75
+ /** Persisted-snapshot configuration (docs/plans/aggregate-snapshotting.md).
76
+ `None` — the default, auto-injected by `@@reventless.behavior` — keeps
77
+ full replay; `Some({interval, stateSchema})` writes a keep-one snapshot
78
+ every `interval` events and seeds cold replays from the latest one.
79
+ Enable with `@@reventless.snapshots(<interval>)` (requires
80
+ `@schema type state`) or a manual binding. */
81
+ let snapshot: option<Snapshot.config<state>>
82
+
75
83
  /** File URL of this module (`import.meta.url`). Used by AWS builders to derive
76
84
  the npm specifier for runtime dynamic imports. */
77
85
  let moduleUrl: string