@reventlessdev/reventless-spec 3.0.0-alpha.61

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 (86) hide show
  1. package/CHANGELOG.md +625 -0
  2. package/LICENSE +202 -0
  3. package/README.md +114 -0
  4. package/package.json +34 -0
  5. package/rescript.json +25 -0
  6. package/run-generator.mjs +2 -0
  7. package/src/AnsiStyle.res +40 -0
  8. package/src/AnsiStyle.res.mjs +54 -0
  9. package/src/LogPrefix.res +168 -0
  10. package/src/LogPrefix.res.mjs +148 -0
  11. package/src/PackageVersion.res +73 -0
  12. package/src/PackageVersion.res.mjs +81 -0
  13. package/src/components/Aggregate.res +64 -0
  14. package/src/components/Aggregate.res.mjs +2 -0
  15. package/src/components/AutomationSlice.res +279 -0
  16. package/src/components/AutomationSlice.res.mjs +30 -0
  17. package/src/components/Counter.res +24 -0
  18. package/src/components/Counter.res.mjs +2 -0
  19. package/src/components/DcbDecode.res +128 -0
  20. package/src/components/DcbDecode.res.mjs +117 -0
  21. package/src/components/DcbTag.res +1256 -0
  22. package/src/components/DcbTag.res.mjs +869 -0
  23. package/src/components/DcbValidation.res +362 -0
  24. package/src/components/DcbValidation.res.mjs +360 -0
  25. package/src/components/DisplayName.res +40 -0
  26. package/src/components/DisplayName.res.mjs +26 -0
  27. package/src/components/ExtensionPoint.res +27 -0
  28. package/src/components/ExtensionPoint.res.mjs +2 -0
  29. package/src/components/InboundTranslationSlice.res +79 -0
  30. package/src/components/InboundTranslationSlice.res.mjs +2 -0
  31. package/src/components/OutboundTranslationSlice.res +108 -0
  32. package/src/components/OutboundTranslationSlice.res.mjs +2 -0
  33. package/src/components/Plugin.res +373 -0
  34. package/src/components/Plugin.res.mjs +249 -0
  35. package/src/components/ReadModel.res +199 -0
  36. package/src/components/ReadModel.res.mjs +18 -0
  37. package/src/components/Reference.res +51 -0
  38. package/src/components/Reference.res.mjs +37 -0
  39. package/src/components/StateAnnotations.res +65 -0
  40. package/src/components/StateAnnotations.res.mjs +15 -0
  41. package/src/components/StateChangeSlice.res +131 -0
  42. package/src/components/StateChangeSlice.res.mjs +2 -0
  43. package/src/components/StateViewSlice.res +99 -0
  44. package/src/components/StateViewSlice.res.mjs +2 -0
  45. package/src/components/Task.res +62 -0
  46. package/src/components/Task.res.mjs +2 -0
  47. package/src/generator/Codegen.res +703 -0
  48. package/src/generator/Codegen.res.mjs +485 -0
  49. package/src/generator/Config.res +85 -0
  50. package/src/generator/Config.res.mjs +78 -0
  51. package/src/generator/Discovery.res +204 -0
  52. package/src/generator/Discovery.res.mjs +213 -0
  53. package/src/generator/Generator_Node.res +49 -0
  54. package/src/generator/Generator_Node.res.mjs +18 -0
  55. package/src/generator/Pairing.res +386 -0
  56. package/src/generator/Pairing.res.mjs +353 -0
  57. package/src/generator/PluginGenerator.res +53 -0
  58. package/src/generator/PluginGenerator.res.mjs +70 -0
  59. package/src/types/Authorization.res +23 -0
  60. package/src/types/Authorization.res.mjs +33 -0
  61. package/src/types/Behavior.res +78 -0
  62. package/src/types/Behavior.res.mjs +2 -0
  63. package/src/types/EventMapping.res +100 -0
  64. package/src/types/EventMapping.res.mjs +15 -0
  65. package/src/types/Handler.res +30 -0
  66. package/src/types/Handler.res.mjs +2 -0
  67. package/src/types/Id.res +75 -0
  68. package/src/types/Id.res.mjs +37 -0
  69. package/src/types/Identity.res +46 -0
  70. package/src/types/Identity.res.mjs +51 -0
  71. package/src/types/Message.res +197 -0
  72. package/src/types/Message.res.mjs +108 -0
  73. package/src/types/Projection.res +220 -0
  74. package/src/types/Projection.res.mjs +44 -0
  75. package/src/types/QueryEngine.res +123 -0
  76. package/src/types/QueryEngine.res.mjs +12 -0
  77. package/src/types/ReadConsistency.res +38 -0
  78. package/src/types/ReadConsistency.res.mjs +29 -0
  79. package/src/types/Schedule.res +65 -0
  80. package/src/types/Schedule.res.mjs +68 -0
  81. package/src/types/SideEffect.res +45 -0
  82. package/src/types/SideEffect.res.mjs +2 -0
  83. package/src/types/StoredEvent.res +46 -0
  84. package/src/types/StoredEvent.res.mjs +32 -0
  85. package/src/types/Visibility.res +24 -0
  86. package/src/types/Visibility.res.mjs +25 -0
@@ -0,0 +1,204 @@
1
+ // Directory scanner: walks src/, classifies .res files by parent folder name.
2
+
3
+ type componentType =
4
+ | StateChangeSlice
5
+ | StateViewSlice
6
+ | StateViewSliceStream
7
+ | AutomationSlice
8
+ | InboundTranslationSlice
9
+ | OutboundTranslationSlice
10
+ | Aggregate
11
+ | ReadModel
12
+ | ReadModelStream
13
+ | Task
14
+ | ExtensionPoint
15
+ | Extension
16
+
17
+ // relPath: path relative to srcDir (e.g. "ReadModel/ProductsProjections.res")
18
+ type discoveredFile = {stem: string, componentType: componentType, epGroup: option<string>, relPath: string}
19
+
20
+ let folderToComponentType = (folder: string): option<componentType> =>
21
+ switch folder {
22
+ | "StateChange" | "StateChanges" | "StateChangeSlice" | "StateChangeSlices" =>
23
+ Some(StateChangeSlice)
24
+ | "StateView" | "StateViews" | "StateViewSlice" | "StateViewSlices" => Some(StateViewSlice)
25
+ | "StateViewSliceStream" | "StateViewSliceStreams" => Some(StateViewSliceStream)
26
+ | "Automation" | "Automations" | "AutomationSlice" | "AutomationSlices" => Some(AutomationSlice)
27
+ | "InboundTranslation"
28
+ | "InboundTranslations"
29
+ | "InboundTranslationSlice"
30
+ | "InboundTranslationSlices" =>
31
+ Some(InboundTranslationSlice)
32
+ | "OutboundTranslation"
33
+ | "OutboundTranslations"
34
+ | "OutboundTranslationSlice"
35
+ | "OutboundTranslationSlices" =>
36
+ Some(OutboundTranslationSlice)
37
+ | "Aggregate" | "Aggregates" => Some(Aggregate)
38
+ | "ReadModel" | "ReadModels" => Some(ReadModel)
39
+ | "ReadModelStream" | "ReadModelStreams" => Some(ReadModelStream)
40
+ | "Task" | "Tasks" => Some(Task)
41
+ | "ExtensionPoint" | "ExtensionPoints" => Some(ExtensionPoint)
42
+ | "Extension" | "Extensions" => Some(Extension)
43
+ | _ => None
44
+ }
45
+
46
+ let isAlwaysExcludedDir = (name: string): bool =>
47
+ name === "Plugin" || name === "tests" || name === "lib"
48
+
49
+ // Simple glob matching: supports exact match, `/**` prefix, and `/*` single level.
50
+ let matchesGlob = (path: string, pattern: string): bool =>
51
+ if pattern === "**" {
52
+ true
53
+ } else if not(pattern->String.includes("*")) {
54
+ // Exact file match or directory prefix
55
+ path === pattern || path->String.startsWith(pattern ++ "/")
56
+ } else if pattern->String.endsWith("/**") {
57
+ let prefix = pattern->String.slice(~start=0, ~end=pattern->String.length - 3)
58
+ path === prefix || path->String.startsWith(prefix ++ "/")
59
+ } else if pattern->String.endsWith("/*") {
60
+ let prefix = pattern->String.slice(~start=0, ~end=pattern->String.length - 2)
61
+ path->String.startsWith(prefix ++ "/") &&
62
+ !(path
63
+ ->String.slice(~start=prefix->String.length + 1, ~end=path->String.length)
64
+ ->String.includes("/"))
65
+ } else {
66
+ path === pattern
67
+ }
68
+
69
+ let isExcluded = (relPath: string, excludePatterns: array<string>): bool =>
70
+ excludePatterns->Array.some(p => matchesGlob(relPath, p))
71
+
72
+ let stemOf = (filename: string): option<string> =>
73
+ if filename->String.endsWith(".res") {
74
+ Some(filename->String.slice(~start=0, ~end=filename->String.length - 4))
75
+ } else {
76
+ None
77
+ }
78
+
79
+ let isSkipped = (stem: string): bool =>
80
+ stem->String.endsWith("Test")
81
+ || stem->String.endsWith("Fixtures")
82
+ // `_EventMappings` and `_Mappings` files in per-entity Aggregate/ folders
83
+ // are picked up by the dedicated [Pairing.findEventMappings] walker;
84
+ // treating them as Aggregate specs would hunt for a non-existent
85
+ // `_EventMappingsBehavior` / `_MappingsBehavior`.
86
+ || stem->String.endsWith("_EventMappings")
87
+ || stem->String.endsWith("_Mappings")
88
+
89
+ // Collect .res files directly in dir (non-recursive) into acc.
90
+ let collectFiles = (
91
+ ~dir: string,
92
+ ~relDir: string,
93
+ ~componentType: componentType,
94
+ ~epGroup: option<string>,
95
+ ~exclude: array<string>,
96
+ ~acc: array<discoveredFile>,
97
+ ): unit =>
98
+ Generator_Node.readDir(dir)->Array.forEach(entry => {
99
+ if entry->Generator_Node.isFile {
100
+ let filename = entry->Generator_Node.direntName
101
+ let relPath = if relDir === "" {filename} else {relDir ++ "/" ++ filename}
102
+ if !isExcluded(relPath, exclude) {
103
+ switch stemOf(filename) {
104
+ | Some(stem) if !isSkipped(stem) => acc->Array.push({stem, componentType, epGroup, relPath})
105
+ | _ => ()
106
+ }
107
+ }
108
+ }
109
+ })
110
+
111
+ // Recursively walk srcDir, classifying files into acc.
112
+ let rec walkDir = (
113
+ ~dir: string,
114
+ ~relDir: string,
115
+ ~parentComponentType: option<componentType>,
116
+ ~epGroup: option<string>,
117
+ ~exclude: array<string>,
118
+ ~acc: array<discoveredFile>,
119
+ ): unit =>
120
+ Generator_Node.readDir(dir)->Array.forEach(entry => {
121
+ let entryName = entry->Generator_Node.direntName
122
+ let relPath = if relDir === "" {entryName} else {relDir ++ "/" ++ entryName}
123
+
124
+ if isExcluded(relPath, exclude) {
125
+ ()
126
+ } else if entry->Generator_Node.isDirectory {
127
+ if isAlwaysExcludedDir(entryName) {
128
+ ()
129
+ } else {
130
+ switch folderToComponentType(entryName) {
131
+ | Some(ExtensionPoint) =>
132
+ let epDir = Generator_Node.join([dir, entryName])
133
+ let children = Generator_Node.readDir(epDir)
134
+ let hasSubDirs = children->Array.some(e => e->Generator_Node.isDirectory)
135
+ if hasSubDirs {
136
+ // Each subfolder is a named EP group
137
+ children->Array.forEach(child => {
138
+ if child->Generator_Node.isDirectory {
139
+ let groupName = child->Generator_Node.direntName
140
+ let groupDir = Generator_Node.join([epDir, groupName])
141
+ let groupRelDir = relPath ++ "/" ++ groupName
142
+ collectFiles(
143
+ ~dir=groupDir,
144
+ ~relDir=groupRelDir,
145
+ ~componentType=ExtensionPoint,
146
+ ~epGroup=Some(groupName),
147
+ ~exclude,
148
+ ~acc,
149
+ )
150
+ }
151
+ })
152
+ } else {
153
+ // Flat EP folder — all .res files are for one extension point
154
+ collectFiles(
155
+ ~dir=epDir,
156
+ ~relDir=relPath,
157
+ ~componentType=ExtensionPoint,
158
+ ~epGroup=None,
159
+ ~exclude,
160
+ ~acc,
161
+ )
162
+ }
163
+ | Some(ct) =>
164
+ // Recurse into component type folder, collecting files at any depth
165
+ walkDir(
166
+ ~dir=Generator_Node.join([dir, entryName]),
167
+ ~relDir=relPath,
168
+ ~parentComponentType=Some(ct),
169
+ ~epGroup=None,
170
+ ~exclude,
171
+ ~acc,
172
+ )
173
+ | None =>
174
+ // Chapter folder or other — recurse inheriting parent context
175
+ walkDir(
176
+ ~dir=Generator_Node.join([dir, entryName]),
177
+ ~relDir=relPath,
178
+ ~parentComponentType,
179
+ ~epGroup,
180
+ ~exclude,
181
+ ~acc,
182
+ )
183
+ }
184
+ }
185
+ } else if entry->Generator_Node.isFile {
186
+ switch parentComponentType {
187
+ | None => ()
188
+ | Some(ct) =>
189
+ let filename = entryName
190
+ if !isExcluded(relPath, exclude) {
191
+ switch stemOf(filename) {
192
+ | Some(stem) if !isSkipped(stem) => acc->Array.push({stem, componentType: ct, epGroup, relPath})
193
+ | _ => ()
194
+ }
195
+ }
196
+ }
197
+ }
198
+ })
199
+
200
+ let scan = (~srcDir: string, ~exclude: array<string>): array<discoveredFile> => {
201
+ let acc: array<discoveredFile> = []
202
+ walkDir(~dir=srcDir, ~relDir="", ~parentComponentType=None, ~epGroup=None, ~exclude, ~acc)
203
+ acc
204
+ }
@@ -0,0 +1,213 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Nodepath from "node:path";
4
+ import * as Generator_Node$Reventless from "./Generator_Node.res.mjs";
5
+
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
+ function isAlwaysExcludedDir(name) {
60
+ if (name === "Plugin" || name === "tests") {
61
+ return true;
62
+ } else {
63
+ return name === "lib";
64
+ }
65
+ }
66
+
67
+ function matchesGlob(path, pattern) {
68
+ if (pattern === "**") {
69
+ return true;
70
+ }
71
+ if (!pattern.includes("*")) {
72
+ if (path === pattern) {
73
+ return true;
74
+ } else {
75
+ return path.startsWith(pattern + "/");
76
+ }
77
+ }
78
+ if (pattern.endsWith("/**")) {
79
+ let prefix = pattern.slice(0, pattern.length - 3 | 0);
80
+ if (path === prefix) {
81
+ return true;
82
+ } else {
83
+ return path.startsWith(prefix + "/");
84
+ }
85
+ }
86
+ if (!pattern.endsWith("/*")) {
87
+ return path === pattern;
88
+ }
89
+ let prefix$1 = pattern.slice(0, pattern.length - 2 | 0);
90
+ if (path.startsWith(prefix$1 + "/")) {
91
+ return !path.slice(prefix$1.length + 1 | 0, path.length).includes("/");
92
+ } else {
93
+ return false;
94
+ }
95
+ }
96
+
97
+ function isExcluded(relPath, excludePatterns) {
98
+ return excludePatterns.some(p => matchesGlob(relPath, p));
99
+ }
100
+
101
+ function stemOf(filename) {
102
+ if (filename.endsWith(".res")) {
103
+ return filename.slice(0, filename.length - 4 | 0);
104
+ }
105
+ }
106
+
107
+ function isSkipped(stem) {
108
+ if (stem.endsWith("Test") || stem.endsWith("Fixtures") || stem.endsWith("_EventMappings")) {
109
+ return true;
110
+ } else {
111
+ return stem.endsWith("_Mappings");
112
+ }
113
+ }
114
+
115
+ function collectFiles(dir, relDir, componentType, epGroup, exclude, acc) {
116
+ Generator_Node$Reventless.readDir(dir).forEach(entry => {
117
+ if (!entry.isFile()) {
118
+ return;
119
+ }
120
+ let filename = entry.name;
121
+ let relPath = relDir === "" ? filename : relDir + "/" + filename;
122
+ if (isExcluded(relPath, exclude)) {
123
+ return;
124
+ }
125
+ let stem = stemOf(filename);
126
+ if (stem !== undefined && !isSkipped(stem)) {
127
+ acc.push({
128
+ stem: stem,
129
+ componentType: componentType,
130
+ epGroup: epGroup,
131
+ relPath: relPath
132
+ });
133
+ return;
134
+ }
135
+ });
136
+ }
137
+
138
+ function walkDir(dir, relDir, parentComponentType, epGroup, exclude, acc) {
139
+ Generator_Node$Reventless.readDir(dir).forEach(entry => {
140
+ let entryName = entry.name;
141
+ let relPath = relDir === "" ? entryName : relDir + "/" + entryName;
142
+ if (isExcluded(relPath, exclude)) {
143
+ return;
144
+ }
145
+ if (entry.isDirectory()) {
146
+ if (isAlwaysExcludedDir(entryName)) {
147
+ return;
148
+ }
149
+ let ct = folderToComponentType(entryName);
150
+ if (ct === undefined) {
151
+ return walkDir(Nodepath.join(dir, entryName), relPath, parentComponentType, epGroup, exclude, acc);
152
+ }
153
+ if (ct !== "ExtensionPoint") {
154
+ return walkDir(Nodepath.join(dir, entryName), relPath, ct, undefined, exclude, acc);
155
+ }
156
+ let epDir = Nodepath.join(dir, entryName);
157
+ let children = Generator_Node$Reventless.readDir(epDir);
158
+ let hasSubDirs = children.some(e => e.isDirectory());
159
+ if (hasSubDirs) {
160
+ children.forEach(child => {
161
+ if (!child.isDirectory()) {
162
+ return;
163
+ }
164
+ let groupName = child.name;
165
+ let groupDir = Nodepath.join(epDir, groupName);
166
+ let groupRelDir = relPath + "/" + groupName;
167
+ collectFiles(groupDir, groupRelDir, "ExtensionPoint", groupName, exclude, acc);
168
+ });
169
+ return;
170
+ } else {
171
+ return collectFiles(epDir, relPath, "ExtensionPoint", undefined, exclude, acc);
172
+ }
173
+ }
174
+ if (!entry.isFile()) {
175
+ return;
176
+ }
177
+ if (parentComponentType === undefined) {
178
+ return;
179
+ }
180
+ if (isExcluded(relPath, exclude)) {
181
+ return;
182
+ }
183
+ let stem = stemOf(entryName);
184
+ if (stem !== undefined && !isSkipped(stem)) {
185
+ acc.push({
186
+ stem: stem,
187
+ componentType: parentComponentType,
188
+ epGroup: epGroup,
189
+ relPath: relPath
190
+ });
191
+ return;
192
+ }
193
+ });
194
+ }
195
+
196
+ function scan(srcDir, exclude) {
197
+ let acc = [];
198
+ walkDir(srcDir, "", undefined, undefined, exclude, acc);
199
+ return acc;
200
+ }
201
+
202
+ export {
203
+ folderToComponentType,
204
+ isAlwaysExcludedDir,
205
+ matchesGlob,
206
+ isExcluded,
207
+ stemOf,
208
+ isSkipped,
209
+ collectFiles,
210
+ walkDir,
211
+ scan,
212
+ }
213
+ /* node:path Not a pure module */
@@ -0,0 +1,49 @@
1
+ // Node.js bindings for the plugin generator.
2
+
3
+ // ── File system ──────────────────────────────────────────────────────────────
4
+
5
+ @module("node:fs")
6
+ external existsSync: string => bool = "existsSync"
7
+
8
+ @module("node:fs")
9
+ external readFileSync: (string, @as("utf8") _) => string = "readFileSync"
10
+
11
+ @module("node:fs")
12
+ external writeFileSync: (string, string, @as("utf8") _) => unit = "writeFileSync"
13
+
14
+ type dirent
15
+ @send external isDirectory: dirent => bool = "isDirectory"
16
+ @send external isFile: dirent => bool = "isFile"
17
+ @get external direntName: dirent => string = "name"
18
+
19
+ type readdirOptions = {withFileTypes: bool}
20
+
21
+ @module("node:fs")
22
+ external readdirSyncRaw: (string, readdirOptions) => array<dirent> = "readdirSync"
23
+
24
+ let readDir = (dir: string): array<dirent> =>
25
+ try readdirSyncRaw(dir, {withFileTypes: true}) catch {
26
+ | _ => []
27
+ }
28
+
29
+ // ── Path ─────────────────────────────────────────────────────────────────────
30
+
31
+ @module("node:path") @variadic
32
+ external join: array<string> => string = "join"
33
+
34
+ @module("node:path") @variadic
35
+ external resolve: array<string> => string = "resolve"
36
+
37
+ @module("node:path")
38
+ external dirname: string => string = "dirname"
39
+
40
+ @module("node:path")
41
+ external basename: string => string = "basename"
42
+
43
+ // ── Process ──────────────────────────────────────────────────────────────────
44
+
45
+ @val @scope("process")
46
+ external argv: array<string> = "argv"
47
+
48
+ @val @scope("process")
49
+ external cwd: unit => string = "cwd"
@@ -0,0 +1,18 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Nodefs from "node:fs";
4
+
5
+ function readDir(dir) {
6
+ try {
7
+ return Nodefs.readdirSync(dir, {
8
+ withFileTypes: true
9
+ });
10
+ } catch (exn) {
11
+ return [];
12
+ }
13
+ }
14
+
15
+ export {
16
+ readDir,
17
+ }
18
+ /* node:fs Not a pure module */