@reventlessdev/reventless-spec 3.0.0-alpha.100

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 (139) hide show
  1. package/CHANGELOG.md +931 -0
  2. package/LICENSE +202 -0
  3. package/README.md +109 -0
  4. package/package.json +49 -0
  5. package/rescript.json +32 -0
  6. package/run-generator.mjs +2 -0
  7. package/run-platform-generator.mjs +2 -0
  8. package/scripts/generate-currency.mjs +215 -0
  9. package/scripts/iso-4217-list-one.xml +1956 -0
  10. package/src/AnsiStyle.res +40 -0
  11. package/src/AnsiStyle.res.mjs +54 -0
  12. package/src/LogPrefix.res +192 -0
  13. package/src/LogPrefix.res.mjs +159 -0
  14. package/src/PackageVersion.res +67 -0
  15. package/src/PackageVersion.res.mjs +81 -0
  16. package/src/components/Aggregate.res +64 -0
  17. package/src/components/Aggregate.res.mjs +2 -0
  18. package/src/components/AutomationSlice.res +279 -0
  19. package/src/components/AutomationSlice.res.mjs +30 -0
  20. package/src/components/CapabilityManifest.res +74 -0
  21. package/src/components/CapabilityManifest.res.mjs +61 -0
  22. package/src/components/ComponentKind.res +99 -0
  23. package/src/components/ComponentKind.res.mjs +125 -0
  24. package/src/components/Counter.res +24 -0
  25. package/src/components/Counter.res.mjs +2 -0
  26. package/src/components/DcbDecode.res +118 -0
  27. package/src/components/DcbDecode.res.mjs +100 -0
  28. package/src/components/DcbScopeInference.res +244 -0
  29. package/src/components/DcbScopeInference.res.mjs +177 -0
  30. package/src/components/DcbTag.res +1335 -0
  31. package/src/components/DcbTag.res.mjs +898 -0
  32. package/src/components/DcbValidation.res +427 -0
  33. package/src/components/DcbValidation.res.mjs +423 -0
  34. package/src/components/DisplayName.res +40 -0
  35. package/src/components/DisplayName.res.mjs +26 -0
  36. package/src/components/ExtensionPoint.res +27 -0
  37. package/src/components/ExtensionPoint.res.mjs +2 -0
  38. package/src/components/InboundTranslationSlice.res +85 -0
  39. package/src/components/InboundTranslationSlice.res.mjs +2 -0
  40. package/src/components/OutboundTranslationSlice.res +153 -0
  41. package/src/components/OutboundTranslationSlice.res.mjs +2 -0
  42. package/src/components/Plugin.res +538 -0
  43. package/src/components/Plugin.res.mjs +264 -0
  44. package/src/components/PluginName.res +39 -0
  45. package/src/components/PluginName.res.mjs +45 -0
  46. package/src/components/ReadModel.res +199 -0
  47. package/src/components/ReadModel.res.mjs +18 -0
  48. package/src/components/Reference.res +55 -0
  49. package/src/components/Reference.res.mjs +50 -0
  50. package/src/components/Snapshot.res +26 -0
  51. package/src/components/Snapshot.res.mjs +2 -0
  52. package/src/components/StateAnnotations.res +97 -0
  53. package/src/components/StateAnnotations.res.mjs +15 -0
  54. package/src/components/StateChangeSlice.res +131 -0
  55. package/src/components/StateChangeSlice.res.mjs +2 -0
  56. package/src/components/StateViewSlice.res +123 -0
  57. package/src/components/StateViewSlice.res.mjs +2 -0
  58. package/src/components/Task.res +62 -0
  59. package/src/components/Task.res.mjs +2 -0
  60. package/src/generator/Codegen.res +842 -0
  61. package/src/generator/Codegen.res.mjs +565 -0
  62. package/src/generator/Config.res +106 -0
  63. package/src/generator/Config.res.mjs +69 -0
  64. package/src/generator/Discovery.res +230 -0
  65. package/src/generator/Discovery.res.mjs +198 -0
  66. package/src/generator/Generator_Node.res +14 -0
  67. package/src/generator/Generator_Node.res.mjs +18 -0
  68. package/src/generator/Pairing.res +460 -0
  69. package/src/generator/Pairing.res.mjs +415 -0
  70. package/src/generator/PlatformCodegen.res +207 -0
  71. package/src/generator/PlatformCodegen.res.mjs +154 -0
  72. package/src/generator/PlatformGenerator.res +126 -0
  73. package/src/generator/PlatformGenerator.res.mjs +114 -0
  74. package/src/generator/PlatformManifests.res +203 -0
  75. package/src/generator/PlatformManifests.res.mjs +212 -0
  76. package/src/generator/PluginGenerator.res +57 -0
  77. package/src/generator/PluginGenerator.res.mjs +73 -0
  78. package/src/semantic/Bytes.res +54 -0
  79. package/src/semantic/Bytes.res.mjs +38 -0
  80. package/src/semantic/Capabilities.res +43 -0
  81. package/src/semantic/Capabilities.res.mjs +17 -0
  82. package/src/semantic/Color.res +51 -0
  83. package/src/semantic/Color.res.mjs +29 -0
  84. package/src/semantic/Currency.res +598 -0
  85. package/src/semantic/Currency.res.mjs +743 -0
  86. package/src/semantic/DateRange.res +148 -0
  87. package/src/semantic/DateRange.res.mjs +74 -0
  88. package/src/semantic/Duration.res +53 -0
  89. package/src/semantic/Duration.res.mjs +26 -0
  90. package/src/semantic/Email.res +51 -0
  91. package/src/semantic/Email.res.mjs +31 -0
  92. package/src/semantic/GeoPoint.res +226 -0
  93. package/src/semantic/GeoPoint.res.mjs +190 -0
  94. package/src/semantic/Geocoding.res +127 -0
  95. package/src/semantic/Geocoding.res.mjs +36 -0
  96. package/src/semantic/Money.res +196 -0
  97. package/src/semantic/Money.res.mjs +138 -0
  98. package/src/semantic/Offload.res +294 -0
  99. package/src/semantic/Offload.res.mjs +191 -0
  100. package/src/semantic/Percent.res +53 -0
  101. package/src/semantic/Percent.res.mjs +33 -0
  102. package/src/semantic/Phone.res +55 -0
  103. package/src/semantic/Phone.res.mjs +29 -0
  104. package/src/semantic/Semantic.res +162 -0
  105. package/src/semantic/Semantic.res.mjs +95 -0
  106. package/src/semantic/StorageRef.res +164 -0
  107. package/src/semantic/StorageRef.res.mjs +111 -0
  108. package/src/semantic/Url.res +66 -0
  109. package/src/semantic/Url.res.mjs +48 -0
  110. package/src/types/Authorization.res +23 -0
  111. package/src/types/Authorization.res.mjs +33 -0
  112. package/src/types/Behavior.res +86 -0
  113. package/src/types/Behavior.res.mjs +2 -0
  114. package/src/types/DateTime.res +29 -0
  115. package/src/types/DateTime.res.mjs +16 -0
  116. package/src/types/EventMapping.res +100 -0
  117. package/src/types/EventMapping.res.mjs +15 -0
  118. package/src/types/Handler.res +30 -0
  119. package/src/types/Handler.res.mjs +2 -0
  120. package/src/types/Id.res +75 -0
  121. package/src/types/Id.res.mjs +37 -0
  122. package/src/types/Identity.res +46 -0
  123. package/src/types/Identity.res.mjs +51 -0
  124. package/src/types/Message.res +326 -0
  125. package/src/types/Message.res.mjs +186 -0
  126. package/src/types/Projection.res +220 -0
  127. package/src/types/Projection.res.mjs +44 -0
  128. package/src/types/QueryEngine.res +123 -0
  129. package/src/types/QueryEngine.res.mjs +12 -0
  130. package/src/types/ReadConsistency.res +38 -0
  131. package/src/types/ReadConsistency.res.mjs +29 -0
  132. package/src/types/Schedule.res +65 -0
  133. package/src/types/Schedule.res.mjs +68 -0
  134. package/src/types/SideEffect.res +45 -0
  135. package/src/types/SideEffect.res.mjs +2 -0
  136. package/src/types/StoredEvent.res +46 -0
  137. package/src/types/StoredEvent.res.mjs +32 -0
  138. package/src/types/Visibility.res +24 -0
  139. package/src/types/Visibility.res.mjs +25 -0
@@ -0,0 +1,264 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as Offload$Reventless from "../semantic/Offload.res.mjs";
5
+ import * as SuryResMjs from "sury/src/Sury.res.mjs";
6
+
7
+ let pluginKindSchema = S.union([
8
+ S.literal("Domain"),
9
+ S.literal("PlatformInfrastructure"),
10
+ S.literal("Commercial"),
11
+ S.literal("Marketplace")
12
+ ]);
13
+
14
+ let extensionPointDefinitionSchema = S.schema(s => ({
15
+ name: s.m(S.string),
16
+ commandTopic: s.m(S.string),
17
+ eventTopic: s.m(S.string)
18
+ }));
19
+
20
+ let extensionDefinitionSchema = S.schema(s => ({
21
+ name: s.m(S.string),
22
+ extensionPointName: s.m(S.string),
23
+ dcbSources: s.m(S.array(S.string))
24
+ }));
25
+
26
+ let dcbEventLogDefinitionSchema = S.schema(s => ({
27
+ name: s.m(S.string),
28
+ eventTopicArn: s.m(S.string)
29
+ }));
30
+
31
+ let extensionProtocolSchema = S.schema(s => ({
32
+ extensionPointName: s.m(S.string),
33
+ commandVersion: s.m(S.string),
34
+ eventVersion: s.m(S.string)
35
+ }));
36
+
37
+ let apiSchemaFragmentSchema = S.schema(s => ({
38
+ encoded: s.m(S.string),
39
+ protocol: s.m(S.string)
40
+ }));
41
+
42
+ let apiTargetSchema = S.union([
43
+ S.literal("Domain"),
44
+ S.literal("Platform")
45
+ ]);
46
+
47
+ let apiSchemaFragmentOffloadSchema = Offload$Reventless.optionSchema(undefined, "pluginApiFragments", undefined, apiSchemaFragmentSchema);
48
+
49
+ let dcbEventLogOptionSchema = SuryResMjs.js_nullable(dcbEventLogDefinitionSchema);
50
+
51
+ let stringOptionSchema = SuryResMjs.js_nullable(S.string);
52
+
53
+ let stringArrayOptionSchema = SuryResMjs.js_nullable(S.array(S.string));
54
+
55
+ let boolOptionSchema = SuryResMjs.js_nullable(S.bool);
56
+
57
+ let panelManifestEntrySchema = S.schema(s => ({
58
+ fragmentId: s.m(S.string),
59
+ title: s.m(S.string),
60
+ description: s.m(S.string),
61
+ positions: s.m(S.array(S.string)),
62
+ requiredAccess: s.m(stringOptionSchema)
63
+ }));
64
+
65
+ let menuEntrySchema = S.schema(s => ({
66
+ label: s.m(S.string),
67
+ icon: s.m(stringOptionSchema),
68
+ group: s.m(stringOptionSchema),
69
+ sortOrder: s.m(S.int)
70
+ }));
71
+
72
+ let pageManifestEntrySchema = S.schema(s => ({
73
+ fragmentId: s.m(S.string),
74
+ title: s.m(S.string),
75
+ menuEntry: s.m(menuEntrySchema),
76
+ requiredAccess: s.m(stringOptionSchema)
77
+ }));
78
+
79
+ let uiFragmentManifestSchema = S.schema(s => ({
80
+ remoteEntryUrl: s.m(S.string),
81
+ panels: s.m(S.array(panelManifestEntrySchema)),
82
+ pages: s.m(S.array(pageManifestEntrySchema))
83
+ }));
84
+
85
+ let uiFragmentManifestOptionSchema = SuryResMjs.js_nullable(uiFragmentManifestSchema);
86
+
87
+ let commandLevelSchema = S.union([
88
+ S.literal("Collection"),
89
+ S.literal("Instance")
90
+ ]);
91
+
92
+ let fieldReferenceSchema = S.schema(s => ({
93
+ fieldName: s.m(S.string),
94
+ entity: s.m(S.string),
95
+ plugin: s.m(stringOptionSchema)
96
+ }));
97
+
98
+ let commandDefSchema = S.schema(s => ({
99
+ name: s.m(S.string),
100
+ schema: s.m(S.string),
101
+ level: s.m(commandLevelSchema),
102
+ aggregateIdField: s.m(stringOptionSchema),
103
+ mutationField: s.m(S.string),
104
+ references: s.m(S.array(fieldReferenceSchema)),
105
+ allowedStates: s.m(stringArrayOptionSchema),
106
+ targetState: s.m(stringOptionSchema),
107
+ apiExposed: s.m(boolOptionSchema)
108
+ }));
109
+
110
+ let queryableDefSchema = S.schema(s => ({
111
+ name: s.m(S.string),
112
+ queryField: s.m(S.string),
113
+ schema: s.m(S.string),
114
+ consumedEventTypes: s.m(S.array(S.string)),
115
+ linkedWriteSide: s.m(S.array(S.string)),
116
+ labelField: s.m(S.string),
117
+ searchableFields: s.m(S.array(S.string)),
118
+ labelFieldSource: s.m(stringOptionSchema),
119
+ statusField: s.m(stringOptionSchema),
120
+ visibility: s.m(stringOptionSchema),
121
+ chapter: s.m(stringOptionSchema)
122
+ }));
123
+
124
+ let eventDefSchema = S.schema(s => ({
125
+ name: s.m(S.string),
126
+ schema: s.m(S.string),
127
+ references: s.m(S.array(fieldReferenceSchema))
128
+ }));
129
+
130
+ let writableDefSchema = S.schema(s => ({
131
+ name: s.m(S.string),
132
+ commands: s.m(S.array(commandDefSchema)),
133
+ producedEventTypes: s.m(S.array(S.string)),
134
+ consumedEventTypes: s.m(S.array(S.string)),
135
+ linkedViews: s.m(S.array(S.string)),
136
+ consistencyRead: s.m(stringOptionSchema),
137
+ events: s.m(S.array(eventDefSchema)),
138
+ chapter: s.m(stringOptionSchema)
139
+ }));
140
+
141
+ let automationSliceDefSchema = S.schema(s => ({
142
+ name: s.m(S.string),
143
+ consumedEventTypes: s.m(S.array(S.string)),
144
+ producedCommandTypes: s.m(S.array(S.string)),
145
+ targetName: s.m(S.string),
146
+ chapter: s.m(stringOptionSchema)
147
+ }));
148
+
149
+ let outboundTranslationSliceDefSchema = S.schema(s => ({
150
+ name: s.m(S.string),
151
+ consumedEventTypes: s.m(S.array(S.string)),
152
+ inboundCommandTypes: s.m(S.array(S.string)),
153
+ targetName: s.m(stringOptionSchema),
154
+ externalSystem: s.m(stringOptionSchema),
155
+ chapter: s.m(stringOptionSchema)
156
+ }));
157
+
158
+ let inboundTranslationSliceDefSchema = S.schema(s => ({
159
+ name: s.m(S.string),
160
+ commandTypes: s.m(S.array(S.string)),
161
+ targetName: s.m(S.string),
162
+ externalSystem: s.m(stringOptionSchema),
163
+ chapter: s.m(stringOptionSchema)
164
+ }));
165
+
166
+ let extensionDefSchema = S.schema(s => ({
167
+ name: s.m(S.string),
168
+ delegateNames: s.m(S.array(S.string)),
169
+ eventTypes: s.m(S.array(S.string)),
170
+ commandTypes: s.m(S.array(S.string))
171
+ }));
172
+
173
+ let extensionPointDefSchema = S.schema(s => ({
174
+ name: s.m(S.string),
175
+ delegateNames: s.m(S.array(S.string)),
176
+ sourceEventTypes: s.m(S.array(S.string)),
177
+ commandTypes: s.m(stringArrayOptionSchema)
178
+ }));
179
+
180
+ let extensionPointDefArrayOptionSchema = SuryResMjs.js_nullable(S.array(extensionPointDefSchema));
181
+
182
+ let requiredStoreDeclarationSchema = S.schema(s => ({
183
+ store: s.m(S.string),
184
+ component: s.m(S.string),
185
+ field: s.m(S.string),
186
+ annotation: s.m(stringOptionSchema)
187
+ }));
188
+
189
+ let requiredStoreDeclarationArrayOptionSchema = SuryResMjs.js_nullable(S.array(requiredStoreDeclarationSchema));
190
+
191
+ let pluginStructureSchema = S.schema(s => ({
192
+ readModels: s.m(S.array(queryableDefSchema)),
193
+ stateViewSlices: s.m(S.array(queryableDefSchema)),
194
+ stateChangeSlices: s.m(S.array(writableDefSchema)),
195
+ aggregates: s.m(S.array(writableDefSchema)),
196
+ automationSlices: s.m(S.array(automationSliceDefSchema)),
197
+ outboundTranslationSlices: s.m(S.array(outboundTranslationSliceDefSchema)),
198
+ inboundTranslationSlices: s.m(S.array(inboundTranslationSliceDefSchema)),
199
+ extensions: s.m(S.array(extensionDefSchema)),
200
+ extensionPoints: s.m(extensionPointDefArrayOptionSchema),
201
+ requiredStores: s.m(stringArrayOptionSchema),
202
+ requiredStoreDeclarations: s.m(requiredStoreDeclarationArrayOptionSchema)
203
+ }));
204
+
205
+ let pluginStructureOffloadSchema = Offload$Reventless.optionSchema(undefined, "pluginStructures", undefined, pluginStructureSchema);
206
+
207
+ let pluginDefinitionSchema = S.schema(s => ({
208
+ id: s.m(S.string),
209
+ name: s.m(S.string),
210
+ version: s.m(S.string),
211
+ extensionPoints: s.m(S.array(extensionPointDefinitionSchema)),
212
+ extensions: s.m(S.array(extensionDefinitionSchema)),
213
+ eventCollector: s.m(S.string),
214
+ extensionProtocols: s.m(S.array(extensionProtocolSchema)),
215
+ apiSchemaFragment: s.m(apiSchemaFragmentOffloadSchema),
216
+ apiTarget: s.m(stringOptionSchema),
217
+ structure: s.m(pluginStructureOffloadSchema),
218
+ dcbEventLog: s.m(dcbEventLogOptionSchema),
219
+ kind: s.m(pluginKindSchema)
220
+ }));
221
+
222
+ let nameSchema = S.string;
223
+
224
+ let versionSchema = S.string;
225
+
226
+ export {
227
+ nameSchema,
228
+ versionSchema,
229
+ pluginKindSchema,
230
+ extensionPointDefinitionSchema,
231
+ extensionDefinitionSchema,
232
+ dcbEventLogDefinitionSchema,
233
+ extensionProtocolSchema,
234
+ apiSchemaFragmentSchema,
235
+ apiTargetSchema,
236
+ apiSchemaFragmentOffloadSchema,
237
+ dcbEventLogOptionSchema,
238
+ stringOptionSchema,
239
+ stringArrayOptionSchema,
240
+ boolOptionSchema,
241
+ panelManifestEntrySchema,
242
+ menuEntrySchema,
243
+ pageManifestEntrySchema,
244
+ uiFragmentManifestSchema,
245
+ uiFragmentManifestOptionSchema,
246
+ commandLevelSchema,
247
+ fieldReferenceSchema,
248
+ commandDefSchema,
249
+ queryableDefSchema,
250
+ eventDefSchema,
251
+ writableDefSchema,
252
+ automationSliceDefSchema,
253
+ outboundTranslationSliceDefSchema,
254
+ inboundTranslationSliceDefSchema,
255
+ extensionDefSchema,
256
+ extensionPointDefSchema,
257
+ extensionPointDefArrayOptionSchema,
258
+ requiredStoreDeclarationSchema,
259
+ requiredStoreDeclarationArrayOptionSchema,
260
+ pluginStructureSchema,
261
+ pluginStructureOffloadSchema,
262
+ pluginDefinitionSchema,
263
+ }
264
+ /* pluginKindSchema Not a pure module */
@@ -0,0 +1,39 @@
1
+ // Single source of truth for deriving a plugin's PascalCase name from its npm
2
+ // package name, and for resolving the final plugin name from the (optional)
3
+ // plugin.json `name` and package.json `name`. The plugin generator
4
+ // (`generator/Config`) and the external tooling that reconstructs the domain
5
+ // graph (`reventless-gwt`'s `LocalHost`) both derive from this, so the two can't
6
+ // drift — a drift here silently breaks graph plugin keys (the graph would key
7
+ // plugins under names that don't match the ones the generator bakes into
8
+ // `Plugin.res`). See [[reference_ppx_spec_name_single_strip]] for the sibling
9
+ // spec-name derivation.
10
+
11
+ // "@scope/my-catalog" → "MyCatalog", "online-shop" → "OnlineShop".
12
+ let fromPackageName = (packageName: string): string => {
13
+ let base = switch packageName->String.split("/") {
14
+ | [_scope, local] => local
15
+ | [local] => local
16
+ | parts => parts->Array.getUnsafe(parts->Array.length - 1)
17
+ }
18
+ base
19
+ ->String.split("-")
20
+ ->Array.flatMap(part => part->String.split("_"))
21
+ ->Array.map(word =>
22
+ if word === "" {
23
+ ""
24
+ } else {
25
+ word->String.slice(~start=0, ~end=1)->String.toUpperCase ++
26
+ word->String.slice(~start=1, ~end=word->String.length)
27
+ }
28
+ )
29
+ ->Array.join("")
30
+ }
31
+
32
+ // plugin.json `name` wins; else PascalCase(package.json `name`); else "Plugin".
33
+ // The two callers each read the raw fields with their own node bindings, then
34
+ // share this precedence so the resolution can never diverge.
35
+ let resolve = (~pluginJsonName: option<string>, ~packageJsonName: option<string>): string =>
36
+ switch pluginJsonName {
37
+ | Some(name) => name
38
+ | None => packageJsonName->Option.map(fromPackageName)->Option.getOr("Plugin")
39
+ }
@@ -0,0 +1,45 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
4
+
5
+ function fromPackageName(packageName) {
6
+ let parts = packageName.split("/");
7
+ let len = parts.length;
8
+ let base;
9
+ if (len >= 3) {
10
+ base = parts[parts.length - 1 | 0];
11
+ } else {
12
+ switch (len) {
13
+ case 0 :
14
+ base = parts[parts.length - 1 | 0];
15
+ break;
16
+ case 1 :
17
+ base = parts[0];
18
+ break;
19
+ case 2 :
20
+ base = parts[1];
21
+ break;
22
+ }
23
+ }
24
+ return base.split("-").flatMap(part => part.split("_")).map(word => {
25
+ if (word === "") {
26
+ return "";
27
+ } else {
28
+ return word.slice(0, 1).toUpperCase() + word.slice(1, word.length);
29
+ }
30
+ }).join("");
31
+ }
32
+
33
+ function resolve(pluginJsonName, packageJsonName) {
34
+ if (pluginJsonName !== undefined) {
35
+ return pluginJsonName;
36
+ } else {
37
+ return Stdlib_Option.getOr(Stdlib_Option.map(packageJsonName, fromPackageName), "Plugin");
38
+ }
39
+ }
40
+
41
+ export {
42
+ fromPackageName,
43
+ resolve,
44
+ }
45
+ /* No side effect */
@@ -0,0 +1,199 @@
1
+ /**
2
+ Specifies how the sub-ID of a projected state row is supplied at query time.
3
+
4
+ - `Field(name)` — the sub-ID comes from a field in the query response
5
+ - `Argument(name)` — the sub-ID is passed as a GraphQL resolver argument
6
+ - `NoSubId` — the read model has no sub-ID dimension
7
+ */
8
+ type subId =
9
+ | Field(string)
10
+ | Argument(string)
11
+ | NoSubId
12
+
13
+ /**
14
+ Whether the GraphQL resolver field resolves to a single value or to a list.
15
+
16
+ - `Single(fieldName)` — returns one item by ID
17
+ - `Multi(fieldName)` — returns an array of items
18
+ */
19
+ type resolvedField =
20
+ | Single(string)
21
+ | Multi(string)
22
+
23
+ /** Source configuration for a single-ID GraphQL resolver. */
24
+ type idResolverSourceConfig = {
25
+ idField: string,
26
+ subId: subId,
27
+ resolvedField: resolvedField,
28
+ }
29
+
30
+ /**
31
+ Specifies which DynamoDB table field serves as the primary key in the resolver target.
32
+
33
+ - `Index(indexName)` — resolve against an index using only the index ID
34
+ - `IndexWithId(indexName, idField)` — resolve against an index with an explicit ID field
35
+ - `Id` — resolve against the table's primary key
36
+ */
37
+ type targetIdField =
38
+ | Index(string)
39
+ | IndexWithId(string, string)
40
+ | Id
41
+
42
+ /** Target configuration for a single-ID GraphQL resolver. */
43
+ type idResolverTargetConfig = {
44
+ pluginName?: string,
45
+ tableName: string,
46
+ idField: targetIdField,
47
+ subIdField?: string,
48
+ }
49
+
50
+ type resolveConfig<'source, 'target> = {
51
+ source: 'source,
52
+ target: 'target,
53
+ }
54
+
55
+ /** Configuration for a GraphQL resolver that resolves a single ID field. */
56
+ type idResolverConfig = resolveConfig<idResolverSourceConfig, idResolverTargetConfig>
57
+
58
+ /** Source configuration for a multi-ID (array) GraphQL resolver. */
59
+ type idsResolverSourceConfig = {
60
+ idsField: string,
61
+ resolvedField: string,
62
+ }
63
+
64
+ /** Target configuration for a multi-ID (array) GraphQL resolver. */
65
+ type idsResolverTargetConfig = {
66
+ pluginName?: string,
67
+ tableName: string,
68
+ subIdField?: string,
69
+ }
70
+
71
+ /** Configuration for a GraphQL resolver that resolves an array of ID fields. */
72
+ type idsResolverConfig = resolveConfig<idsResolverSourceConfig, idsResolverTargetConfig>
73
+
74
+ /** AppSync authorization rule associating a DynamoDB table with a Cognito group. */
75
+ type authorization = {
76
+ tableName: string,
77
+ group: string,
78
+ }
79
+
80
+ /** How much of a DynamoDB global secondary index is projected into the index. */
81
+ type projectionType = KEYS_ONLY | ALL | INCLUDE(array<string>)
82
+
83
+ /**
84
+ Configuration for a DynamoDB Global Secondary Index on a read model table.
85
+
86
+ - `index` — the index name
87
+ - `type_` — the DynamoDB attribute type of the index key (`"S"`, `"N"`, etc.)
88
+ - `idField` / `subIdField` — index key field overrides (set to synthetic `_name_pk`/`_name_sk` for composite keys)
89
+ - `pkFields` / `skFields` — source state field names for composite pk/sk; runtime uses these to compute synthetic attribute values
90
+ - `pkSep` / `skSep` — separator for composite pk/sk concatenation (default `"/"`)
91
+ - `projectionType` — which attributes are projected into the index
92
+ - `authorization` — optional AppSync authorization rule
93
+ */
94
+ type indexConfig = {
95
+ index: string,
96
+ type_: string,
97
+ idField?: string,
98
+ subIdField?: string,
99
+ pkFields?: array<string>,
100
+ pkSep?: string,
101
+ skFields?: array<string>,
102
+ skSep?: string,
103
+ projectionType: projectionType,
104
+ authorization?: authorization,
105
+ }
106
+
107
+ /**
108
+ Composite-key configuration for a read model that has both a primary ID and a sub-ID.
109
+
110
+ - `subIdField` — the DynamoDB range-key attribute name
111
+ - `getSubId` — extracts the sub-ID string from a projected state value
112
+ */
113
+ type subIdConfig<'state> = {
114
+ subIdField: string,
115
+ getSubId: 'state => string,
116
+ }
117
+
118
+ /**
119
+ Infrastructure configuration for a read model.
120
+
121
+ - `idResolvers` — GraphQL resolvers for single-ID lookups
122
+ - `idsResolvers` — GraphQL resolvers for multi-ID (array) lookups
123
+ - `indexes` — additional DynamoDB global secondary indexes
124
+
125
+ Use the `config` factory function to build this with defaults.
126
+ */
127
+ type config = {
128
+ idResolvers: array<idResolverConfig>,
129
+ idsResolvers: array<idsResolverConfig>,
130
+ indexes: array<indexConfig>,
131
+ }
132
+
133
+ /**
134
+ Builds a `ReadModel.config` with all optional fields defaulting to empty arrays.
135
+
136
+ @example
137
+ ```rescript
138
+ // CategoriesReadModel.res
139
+ let config = ReadModel.config()
140
+ ```
141
+ */
142
+ let config = (~idResolvers=[], ~idsResolvers=[], ~indexes=[]) => {
143
+ idResolvers,
144
+ idsResolvers,
145
+ indexes,
146
+ }
147
+
148
+ /**
149
+ Module type for a read model's identity and schema specification.
150
+
151
+ @example
152
+ ```rescript
153
+ // CategoriesReadModel.res
154
+ module Id = Id.String
155
+ let name = "Categories"
156
+
157
+ @schema
158
+ type state = {categoryId: string, name: string, archived: bool}
159
+
160
+ let config = ReadModel.config()
161
+ let subIdConfig = None
162
+ ```
163
+ */
164
+ module type Spec = {
165
+ module Id: Id.T
166
+
167
+ /** Logical read model name, used as the DynamoDB table-name prefix. */
168
+ let name: string
169
+ let moduleUrl: string
170
+
171
+ /** The projected state type stored in the read model. Must carry `@schema`. */
172
+ @schema
173
+ type state
174
+
175
+ /** Sury schema for the state type — generated automatically by `@schema`. */
176
+ let stateSchema: S.t<state>
177
+
178
+ /** Infrastructure configuration (indexes, resolvers). */
179
+ let config: config
180
+
181
+ /** Optional composite-key configuration. `None` for single-key tables. */
182
+ let subIdConfig: option<subIdConfig<state>>
183
+
184
+ /** Authorization rule evaluated at the GraphQL resolver entry before any
185
+ query is resolved. Auto-injected by `@@reventless.spec` and on
186
+ structurally-detected inline spec modules — defaults to
187
+ `AllowAuthenticated`; override at the file/module level with
188
+ `@@reventless.authorize(<rule>)`. */
189
+ let authorization: Authorization.permission
190
+
191
+ /** AutoUI visibility hint. Auto-injected by `@@reventless.spec` and on
192
+ structurally-detected inline spec modules — defaults to
193
+ `Visibility.Public`; override at the file/module level with
194
+ `@@reventless.visibility(Internal)` to hide from the AutoUI manifest.
195
+ Does not affect GraphQL exposure, authorization, or resolver
196
+ provisioning. */
197
+ let visibility: Visibility.t
198
+ }
199
+
@@ -0,0 +1,18 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+
4
+ function config(idResolversOpt, idsResolversOpt, indexesOpt) {
5
+ let idResolvers = idResolversOpt !== undefined ? idResolversOpt : [];
6
+ let idsResolvers = idsResolversOpt !== undefined ? idsResolversOpt : [];
7
+ let indexes = indexesOpt !== undefined ? indexesOpt : [];
8
+ return {
9
+ idResolvers: idResolvers,
10
+ idsResolvers: idsResolvers,
11
+ indexes: indexes
12
+ };
13
+ }
14
+
15
+ export {
16
+ config,
17
+ }
18
+ /* No side effect */
@@ -0,0 +1,55 @@
1
+ /** Identifies which entity a reference field points to. */
2
+ type target = Semantic.referenceTarget
3
+
4
+ /**
5
+ A sury string schema annotated as an entity reference field.
6
+
7
+ Use with `@s.matches(Reference.to_("EntityName"))` on command/event fields that
8
+ carry a foreign-entity ID. Also implies DCB tag semantics so the field is
9
+ automatically queryable in DCB event logs.
10
+
11
+ Pass `~key` to override the DCB tag key (defaults to the field name). The
12
+ `@ref` ppx shorthand supplies it automatically for plural `*Ids: array<string>`
13
+ fields (singularizing, e.g. `productIds` → tag key `productId`) so they share a
14
+ tag key with their singular-named producer events.
15
+
16
+ Prefer the `@ref("EntityName")` ppx shorthand over writing `@s.matches(...)` by hand.
17
+
18
+ @example
19
+ ```rescript
20
+ @schema type command =
21
+ | PlaceOrder({
22
+ orderId: @s.matches(DcbTag.string) string,
23
+ customerId: @s.matches(Reference.to_("Customer")) string,
24
+ })
25
+ ```
26
+ */
27
+ let to_ = (~plugin=?, ~key=?, entity: string): S.t<string> => {
28
+ // Reference-ness and DCB-tagged-ness are two independent facts that happen to
29
+ // co-occur here: this constructor declares both. `toWithoutDcbTag` declares
30
+ // only the first, and plain `DcbTag.string` only the second — so no consumer
31
+ // may infer either one from the other.
32
+ let base =
33
+ S.string
34
+ ->S.Metadata.set(~id=DcbTag.dcbTagId, true)
35
+ ->Semantic.mark(~id=Semantic.Id.reference, ~payload=ReferenceTo({entity, plugin}))
36
+ switch key {
37
+ | Some(k) => base->S.Metadata.set(~id=DcbTag.dcbTagKeyOverrideId, k)
38
+ | None => base
39
+ }
40
+ }
41
+
42
+ /** Returns the reference target if the schema carries `Reference.to_(...)` metadata. */
43
+ let getTarget = (schema: S.t<unknown>): option<target> =>
44
+ switch Semantic.get(schema) {
45
+ | Some({payload: ReferenceTo(target)}) => Some(target)
46
+ | _ => None
47
+ }
48
+
49
+ /**
50
+ Like `to_` but does not imply DCB tag semantics.
51
+ Use with `@ref("Entity") @noDcbTag` when the field references another entity
52
+ but should not participate in content-based event routing.
53
+ */
54
+ let toWithoutDcbTag = (~plugin=?, entity: string): S.t<string> =>
55
+ S.string->Semantic.mark(~id=Semantic.Id.reference, ~payload=ReferenceTo({entity, plugin}))
@@ -0,0 +1,50 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as DcbTag$Reventless from "./DcbTag.res.mjs";
5
+ import * as Semantic$Reventless from "../semantic/Semantic.res.mjs";
6
+
7
+ function to_(plugin, key, entity) {
8
+ let base = Semantic$Reventless.mark(S.Metadata.set(S.string, DcbTag$Reventless.dcbTagId, true), Semantic$Reventless.Id.reference, {
9
+ TAG: "ReferenceTo",
10
+ _0: {
11
+ entity: entity,
12
+ plugin: plugin
13
+ }
14
+ });
15
+ if (key !== undefined) {
16
+ return S.Metadata.set(base, DcbTag$Reventless.dcbTagKeyOverrideId, key);
17
+ } else {
18
+ return base;
19
+ }
20
+ }
21
+
22
+ function getTarget(schema) {
23
+ let match = Semantic$Reventless.get(schema);
24
+ if (match === undefined) {
25
+ return;
26
+ }
27
+ let target = match.payload;
28
+ if (typeof target !== "object" || target.TAG !== "ReferenceTo") {
29
+ return;
30
+ } else {
31
+ return target._0;
32
+ }
33
+ }
34
+
35
+ function toWithoutDcbTag(plugin, entity) {
36
+ return Semantic$Reventless.mark(S.string, Semantic$Reventless.Id.reference, {
37
+ TAG: "ReferenceTo",
38
+ _0: {
39
+ entity: entity,
40
+ plugin: plugin
41
+ }
42
+ });
43
+ }
44
+
45
+ export {
46
+ to_,
47
+ getTarget,
48
+ toWithoutDcbTag,
49
+ }
50
+ /* S Not a pure module */
@@ -0,0 +1,26 @@
1
+ // Per-aggregate persisted-snapshot configuration
2
+ // (docs/plans/aggregate-snapshotting.md).
3
+ //
4
+ // Snapshotting is opt-in and lives on the Behavior module (where `state` is
5
+ // defined): `Behavior.T.snapshot = None` (the default, auto-injected by
6
+ // `@@reventless.behavior`) keeps full replay; `Some(config)` makes the
7
+ // aggregate write a keep-one snapshot every `interval` events and seed cold
8
+ // replays from the latest one. Snapshots are a read optimization only — the
9
+ // OCC append remains the sole consistency primitive, and a missing, corrupt,
10
+ // or schema-drifted snapshot degrades to full replay.
11
+ //
12
+ // Enable via the file-level attribute on a behavior file:
13
+ //
14
+ // @@reventless.snapshots(100)
15
+ //
16
+ // which injects `let snapshot = Some({interval: 100, stateSchema})` (requires
17
+ // `@schema type state` so sury generates the schema), or hand-write the
18
+ // binding for full control.
19
+
20
+ type config<'state> = {
21
+ /** Write a snapshot after every N appended events. Required — no default. */
22
+ interval: int,
23
+ /** Sury schema for the behavior's state — serializes the persisted snapshot
24
+ and (hashed) gates staleness across state-shape changes. */
25
+ stateSchema: S.t<'state>,
26
+ }
@@ -0,0 +1,2 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+ /* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */