@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
@@ -73,12 +73,12 @@ let rec schemasAreCompatible = (a: S.t<unknown>, b: S.t<unknown>): bool =>
73
73
  let at = av->Array.map(v => v.tagName)->Array.toSorted(String.compare)
74
74
  let bt = bv->Array.map(v => v.tagName)->Array.toSorted(String.compare)
75
75
  at == bt &&
76
- at->Array.every(tag =>
77
- switch (av->Array.find(v => v.tagName == tag), bv->Array.find(v => v.tagName == tag)) {
78
- | (Some(x), Some(y)) => propsCompatible(x.fields, y.fields)
79
- | _ => false
80
- }
81
- )
76
+ at->Array.every(tag =>
77
+ switch (av->Array.find(v => v.tagName == tag), bv->Array.find(v => v.tagName == tag)) {
78
+ | (Some(x), Some(y)) => propsCompatible(x.fields, y.fields)
79
+ | _ => false
80
+ }
81
+ )
82
82
  | _ => false
83
83
  }
84
84
  // Two property maps are compatible when they carry the same field names and each
@@ -87,12 +87,12 @@ and propsCompatible = (aProps: dict<S.t<unknown>>, bProps: dict<S.t<unknown>>):
87
87
  let aKeys = aProps->Dict.keysToArray->Array.toSorted(String.compare)
88
88
  let bKeys = bProps->Dict.keysToArray->Array.toSorted(String.compare)
89
89
  aKeys == bKeys &&
90
- aKeys->Array.every(k =>
91
- switch (aProps->Dict.get(k), bProps->Dict.get(k)) {
92
- | (Some(av), Some(bv)) => schemasAreCompatible(av, bv)
93
- | _ => false
94
- }
95
- )
90
+ aKeys->Array.every(k =>
91
+ switch (aProps->Dict.get(k), bProps->Dict.get(k)) {
92
+ | (Some(av), Some(bv)) => schemasAreCompatible(av, bv)
93
+ | _ => false
94
+ }
95
+ )
96
96
  }
97
97
 
98
98
  // Get a human-readable type name for a schema
@@ -185,26 +185,27 @@ let validateCompositeReads = (
185
185
  ->Array.forEach(cmdKeys => {
186
186
  let querySet = dedupeKeys(cmdKeys)
187
187
  if querySet->Array.length >= 2 {
188
- consumedTypes->Array.forEach(consumedType =>
189
- switch producedTagKeys->Dict.get(consumedType) {
190
- | None => ()
191
- | Some(producedKeys) =>
192
- let producedSet = dedupeKeys(producedKeys)
193
- let isSuperset = querySet->Array.every(k => producedSet->Array.includes(k))
194
- if isSuperset && producedSet->Array.length > querySet->Array.length {
195
- let extra = producedSet->Array.filter(k => !(querySet->Array.includes(k)))
196
- let _ = warnings->Array.push({
197
- sliceName,
198
- message: `composite read on tags [${querySet->Array.join(
199
- ", ",
200
- )}] will silently miss '${consumedType}', which also carries [${extra->Array.join(
201
- ", ",
202
- )}] — the tag_composite key includes ALL of an event's tags, so a composite query matches only events tagged exactly [${querySet->Array.join(
203
- ", ",
204
- )}]. Align the event's tag set with the query, or read '${consumedType}' via a single-tag clause.`,
205
- })
206
- }
207
- }
188
+ consumedTypes->Array.forEach(
189
+ consumedType =>
190
+ switch producedTagKeys->Dict.get(consumedType) {
191
+ | None => ()
192
+ | Some(producedKeys) =>
193
+ let producedSet = dedupeKeys(producedKeys)
194
+ let isSuperset = querySet->Array.every(k => producedSet->Array.includes(k))
195
+ if isSuperset && producedSet->Array.length > querySet->Array.length {
196
+ let extra = producedSet->Array.filter(k => !(querySet->Array.includes(k)))
197
+ let _ = warnings->Array.push({
198
+ sliceName,
199
+ message: `composite read on tags [${querySet->Array.join(
200
+ ", ",
201
+ )}] will silently miss '${consumedType}', which also carries [${extra->Array.join(
202
+ ", ",
203
+ )}] — the tag_composite key includes ALL of an event's tags, so a composite query matches only events tagged exactly [${querySet->Array.join(
204
+ ", ",
205
+ )}]. Align the event's tag set with the query, or read '${consumedType}' via a single-tag clause.`,
206
+ })
207
+ }
208
+ },
208
209
  )
209
210
  }
210
211
  })
@@ -231,7 +232,9 @@ let validateProducedAndConsumed = (
231
232
 
232
233
  // Rule 1: Payload equivalence across producers
233
234
  // All producers of the same TAG must have identical fields, types, and tag annotations
234
- producerMap->Dict.toArray->Array.forEach(((tagName, entries)) => {
235
+ producerMap
236
+ ->Dict.toArray
237
+ ->Array.forEach(((tagName, entries)) => {
235
238
  if entries->Array.length > 1 {
236
239
  let first = entries->Array.getUnsafe(0)
237
240
  let firstFields = first.variant.fields->Dict.keysToArray->Array.toSorted(String.compare)
@@ -243,70 +246,85 @@ let validateProducedAndConsumed = (
243
246
  let entryTagged = entry.variant.taggedFields->Array.toSorted(String.compare)
244
247
 
245
248
  // Check field names match
246
- if firstFields->Array.length != entryFields->Array.length ||
247
- firstFields->Array.some(f => !(entryFields->Array.includes(f))) {
248
- let missingInEntry =
249
- firstFields->Array.filter(f => !(entryFields->Array.includes(f)))
250
- let extraInEntry =
251
- entryFields->Array.filter(f => !(firstFields->Array.includes(f)))
249
+ if (
250
+ firstFields->Array.length != entryFields->Array.length ||
251
+ firstFields->Array.some(f => !(entryFields->Array.includes(f)))
252
+ ) {
253
+ let missingInEntry = firstFields->Array.filter(f => !(entryFields->Array.includes(f)))
254
+ let extraInEntry = entryFields->Array.filter(f => !(firstFields->Array.includes(f)))
252
255
  let parts = []
253
256
  if missingInEntry->Array.length > 0 {
254
257
  let _ =
255
258
  parts->Array.push(
256
- `'${first.sliceName}' has fields [${missingInEntry->Array.join(", ")}] which '${entry.sliceName}' is missing`,
259
+ `'${first.sliceName}' has fields [${missingInEntry->Array.join(
260
+ ", ",
261
+ )}] which '${entry.sliceName}' is missing`,
257
262
  )
258
263
  }
259
264
  if extraInEntry->Array.length > 0 {
260
265
  let _ =
261
266
  parts->Array.push(
262
- `'${entry.sliceName}' has fields [${extraInEntry->Array.join(", ")}] which '${first.sliceName}' is missing`,
267
+ `'${entry.sliceName}' has fields [${extraInEntry->Array.join(
268
+ ", ",
269
+ )}] which '${first.sliceName}' is missing`,
263
270
  )
264
271
  }
265
- let _ =
266
- errors->Array.push({
267
- sliceName: entry.sliceName,
268
- message: `Producers '${first.sliceName}' and '${entry.sliceName}' both produce '${tagName}' but with different fields: ${parts->Array.join("; ")}`,
269
- })
272
+ let _ = errors->Array.push({
273
+ sliceName: entry.sliceName,
274
+ message: `Producers '${first.sliceName}' and '${entry.sliceName}' both produce '${tagName}' but with different fields: ${parts->Array.join(
275
+ "; ",
276
+ )}`,
277
+ })
270
278
  } else {
271
279
  // Fields match by name — check types are compatible
272
- firstFields->Array.forEach(fieldName => {
273
- let firstSchema = first.variant.fields->Dict.getUnsafe(fieldName)
274
- let entrySchema = entry.variant.fields->Dict.getUnsafe(fieldName)
275
- if !schemasAreCompatible(firstSchema, entrySchema) {
276
- let _ =
277
- errors->Array.push({
280
+ firstFields->Array.forEach(
281
+ fieldName => {
282
+ let firstSchema = first.variant.fields->Dict.getUnsafe(fieldName)
283
+ let entrySchema = entry.variant.fields->Dict.getUnsafe(fieldName)
284
+ if !schemasAreCompatible(firstSchema, entrySchema) {
285
+ let _ = errors->Array.push({
278
286
  sliceName: entry.sliceName,
279
- message: `Producers '${first.sliceName}' and '${entry.sliceName}' both produce '${tagName}' but field '${fieldName}' has type '${schemaTypeName(firstSchema)}' in '${first.sliceName}' and '${schemaTypeName(entrySchema)}' in '${entry.sliceName}'`,
287
+ message: `Producers '${first.sliceName}' and '${entry.sliceName}' both produce '${tagName}' but field '${fieldName}' has type '${schemaTypeName(
288
+ firstSchema,
289
+ )}' in '${first.sliceName}' and '${schemaTypeName(
290
+ entrySchema,
291
+ )}' in '${entry.sliceName}'`,
280
292
  })
281
- }
282
- })
293
+ }
294
+ },
295
+ )
283
296
  }
284
297
 
285
298
  // Check tag annotations match
286
- if firstTagged->Array.length != entryTagged->Array.length ||
287
- firstTagged->Array.some(f => !(entryTagged->Array.includes(f))) {
288
- let untaggedInEntry =
289
- firstTagged->Array.filter(f => !(entryTagged->Array.includes(f)))
290
- let untaggedInFirst =
291
- entryTagged->Array.filter(f => !(firstTagged->Array.includes(f)))
299
+ if (
300
+ firstTagged->Array.length != entryTagged->Array.length ||
301
+ firstTagged->Array.some(f => !(entryTagged->Array.includes(f)))
302
+ ) {
303
+ let untaggedInEntry = firstTagged->Array.filter(f => !(entryTagged->Array.includes(f)))
304
+ let untaggedInFirst = entryTagged->Array.filter(f => !(firstTagged->Array.includes(f)))
292
305
  let parts = []
293
306
  if untaggedInEntry->Array.length > 0 {
294
307
  let _ =
295
308
  parts->Array.push(
296
- `[${untaggedInEntry->Array.join(", ")}] tagged in '${first.sliceName}' but not in '${entry.sliceName}'`,
309
+ `[${untaggedInEntry->Array.join(
310
+ ", ",
311
+ )}] tagged in '${first.sliceName}' but not in '${entry.sliceName}'`,
297
312
  )
298
313
  }
299
314
  if untaggedInFirst->Array.length > 0 {
300
315
  let _ =
301
316
  parts->Array.push(
302
- `[${untaggedInFirst->Array.join(", ")}] tagged in '${entry.sliceName}' but not in '${first.sliceName}'`,
317
+ `[${untaggedInFirst->Array.join(
318
+ ", ",
319
+ )}] tagged in '${entry.sliceName}' but not in '${first.sliceName}'`,
303
320
  )
304
321
  }
305
- let _ =
306
- errors->Array.push({
307
- sliceName: entry.sliceName,
308
- message: `Producers '${first.sliceName}' and '${entry.sliceName}' both produce '${tagName}' but tag annotations differ: ${parts->Array.join("; ")}`,
309
- })
322
+ let _ = errors->Array.push({
323
+ sliceName: entry.sliceName,
324
+ message: `Producers '${first.sliceName}' and '${entry.sliceName}' both produce '${tagName}' but tag annotations differ: ${parts->Array.join(
325
+ "; ",
326
+ )}`,
327
+ })
310
328
  }
311
329
  }
312
330
  })
@@ -321,35 +339,42 @@ let validateProducedAndConsumed = (
321
339
  variants->Array.forEach(variant => {
322
340
  switch producerMap->Dict.get(variant.tagName) {
323
341
  | None =>
324
- let _ =
325
- errors->Array.push({
326
- sliceName,
327
- message: `Slice '${sliceName}' consumes '${variant.tagName}' but no slice produces it`,
328
- })
342
+ let _ = errors->Array.push({
343
+ sliceName,
344
+ message: `Slice '${sliceName}' consumes '${variant.tagName}' but no slice produces it`,
345
+ })
329
346
  | Some(producers) =>
330
347
  // Use first producer as the authoritative shape (Rule 1 ensures they're all identical)
331
348
  let producer = producers->Array.getUnsafe(0)
332
349
 
333
350
  // Skip field checks for payload-less consumed variants
334
351
  if !isPayloadLess(variant) {
335
- variant.fields->Dict.toArray->Array.forEach(((fieldName, consumedFieldSchema)) => {
336
- switch producer.variant.fields->Dict.get(fieldName) {
337
- | None =>
338
- let _ =
339
- errors->Array.push({
352
+ variant.fields
353
+ ->Dict.toArray
354
+ ->Array.forEach(
355
+ ((fieldName, consumedFieldSchema)) => {
356
+ switch producer.variant.fields->Dict.get(fieldName) {
357
+ | None =>
358
+ let _ = errors->Array.push({
340
359
  sliceName,
341
- message: `Slice '${sliceName}' consumes '${variant.tagName}.${fieldName}' (${schemaTypeName(consumedFieldSchema)}) but produced '${variant.tagName}' has no field '${fieldName}'`,
360
+ message: `Slice '${sliceName}' consumes '${variant.tagName}.${fieldName}' (${schemaTypeName(
361
+ consumedFieldSchema,
362
+ )}) but produced '${variant.tagName}' has no field '${fieldName}'`,
342
363
  })
343
- | Some(producedFieldSchema) =>
344
- if !schemasAreCompatible(consumedFieldSchema, producedFieldSchema) {
345
- let _ =
346
- errors->Array.push({
364
+ | Some(producedFieldSchema) =>
365
+ if !schemasAreCompatible(consumedFieldSchema, producedFieldSchema) {
366
+ let _ = errors->Array.push({
347
367
  sliceName,
348
- message: `Slice '${sliceName}' consumes '${variant.tagName}.${fieldName}' as ${describeSchema(consumedFieldSchema)} but producer '${producer.sliceName}' declares it as ${describeSchema(producedFieldSchema)}`,
368
+ message: `Slice '${sliceName}' consumes '${variant.tagName}.${fieldName}' as ${describeSchema(
369
+ consumedFieldSchema,
370
+ )} but producer '${producer.sliceName}' declares it as ${describeSchema(
371
+ producedFieldSchema,
372
+ )}`,
349
373
  })
374
+ }
350
375
  }
351
- }
352
- })
376
+ },
377
+ )
353
378
  }
354
379
  }
355
380
  })
@@ -375,9 +400,9 @@ one warning per producer that carries a key some *other* producer declared
375
400
 
376
401
  @param producers `(sliceName, producedEventSchema)` per producing slice.
377
402
  */
378
- let validateCrossPartitionScope = (
379
- ~producers: array<(string, S.t<unknown>)>,
380
- ): array<validationError> => {
403
+ let validateCrossPartitionScope = (~producers: array<(string, S.t<unknown>)>): array<
404
+ validationError,
405
+ > => {
381
406
  let perProducer = producers->Array.map(((name, schema)) => {
382
407
  let cpSet = Set.make()
383
408
  DcbTag.extractCrossPartitionTagKeys(schema)->Array.forEach(k => cpSet->Set.add(k))
@@ -14,8 +14,10 @@ type displayNameSpec = {
14
14
  }
15
15
 
16
16
  /** Sury metadata ID used to attach a `displayNameSpec` to a state schema. */
17
- let displayNameId: S.Metadata.Id.t<displayNameSpec> =
18
- S.Metadata.Id.make(~namespace="reventless", ~name="displayName")
17
+ let displayNameId: S.Metadata.Id.t<displayNameSpec> = S.Metadata.Id.make(
18
+ ~namespace="reventless",
19
+ ~name="displayName",
20
+ )
19
21
 
20
22
  /** Returns the spec attached to a state schema, if any. */
21
23
  let getSpec = (schema: S.t<unknown>): option<displayNameSpec> =>
@@ -24,4 +24,3 @@ module type Spec = {
24
24
  @schema
25
25
  type directive
26
26
  }
27
-
@@ -29,13 +29,11 @@ let defaultId: S.Metadata.Id.t<JSON.t> = S.Metadata.Id.make(
29
29
  /** Layers a default onto whatever the field already resolved to. Wraps rather
30
30
  than replaces, for the reason `Sensitive.mark` does: a field carries at most
31
31
  one `@s.matches`. */
32
- let mark = (schema: S.t<'a>, value: JSON.t): S.t<'a> =>
33
- schema->S.Metadata.set(~id=defaultId, value)
32
+ let mark = (schema: S.t<'a>, value: JSON.t): S.t<'a> => schema->S.Metadata.set(~id=defaultId, value)
34
33
 
35
34
  let int = (schema: S.t<int>, value: int): S.t<int> => schema->mark(JSON.Encode.int(value))
36
35
 
37
- let float = (schema: S.t<float>, value: float): S.t<float> =>
38
- schema->mark(JSON.Encode.float(value))
36
+ let float = (schema: S.t<float>, value: float): S.t<float> => schema->mark(JSON.Encode.float(value))
39
37
 
40
38
  let string = (schema: S.t<string>, value: string): S.t<string> =>
41
39
  schema->mark(JSON.Encode.string(value))
@@ -31,7 +31,6 @@ let translate = input => switch input.status {
31
31
  }
32
32
  ```
33
33
  */
34
-
35
34
  /**
36
35
  The lean Spec for an InboundTranslationSlice — types, identity, schemas.
37
36
  */
@@ -96,4 +95,3 @@ module type Translation = {
96
95
  /** File URL of this Translation module (`import.meta.url`). */
97
96
  let moduleUrl: string
98
97
  }
99
-
@@ -44,7 +44,6 @@ let maxRetries = 3
44
44
  let heartbeatInterval = 60
45
45
  ```
46
46
  */
47
-
48
47
  /**
49
48
  The lean Spec for an OutboundTranslationSlice — types, identity, schemas, sweep config.
50
49
  */
@@ -167,9 +166,11 @@ module type Translation = {
167
166
  the framework knows nothing about still reaches it directly and simply ignores
168
167
  this argument.
169
168
  */
170
- let translate: (string, Spec.outboundItem, ~capabilities: Capabilities.t) => promise<
171
- result<option<(string, Spec.inboundCommand)>, string>,
172
- >
169
+ let translate: (
170
+ string,
171
+ Spec.outboundItem,
172
+ ~capabilities: Capabilities.t,
173
+ ) => promise<result<option<(string, Spec.inboundCommand)>, string>>
173
174
 
174
175
  /**
175
176
  The retry budget is spent: this item will never be attempted again.
@@ -186,11 +187,12 @@ module type Translation = {
186
187
  The row is marked `Abandoned` either way; this decides only whether anything
187
188
  downstream hears about it.
188
189
  */
189
- let onExhausted: (string, Spec.outboundItem, ~lastError: option<string>) => option<
190
- (string, Spec.inboundCommand),
191
- >
190
+ let onExhausted: (
191
+ string,
192
+ Spec.outboundItem,
193
+ ~lastError: option<string>,
194
+ ) => option<(string, Spec.inboundCommand)>
192
195
 
193
196
  /** File URL of this Translation module (`import.meta.url`). */
194
197
  let moduleUrl: string
195
198
  }
196
-
@@ -78,10 +78,10 @@ the view on the wrong value.
78
78
  */
79
79
  let isFieldOwner = (schema: S.t<unknown>): bool =>
80
80
  isOwner(schema) ||
81
- switch schema->Semantic.unwrapOptional {
82
- | Some(inner) => isOwner(inner)
83
- | None => false
84
- }
81
+ switch schema->Semantic.unwrapOptional {
82
+ | Some(inner) => isOwner(inner)
83
+ | None => false
84
+ }
85
85
 
86
86
  /**
87
87
  The owner fields declared on an object schema, in declaration order.
@@ -79,7 +79,10 @@ type apiTarget = Domain | Platform
79
79
  // nothing to annotate. The `T | null` these fields used to carry worked around a sury
80
80
  // bug — undefined failing jsonableValidation inside a union variant payload — fixed in
81
81
  // 11.0.0-alpha.11. `Offload` below is not an optional wrapper but an either-or codec.
82
- let apiSchemaFragmentOffloadSchema = Offload.optionSchema(~store="pluginApiFragments", apiSchemaFragmentSchema)
82
+ let apiSchemaFragmentOffloadSchema = Offload.optionSchema(
83
+ ~store="pluginApiFragments",
84
+ apiSchemaFragmentSchema,
85
+ )
83
86
 
84
87
  // ── UI fragment manifest types ────────────────────────────────────────────────
85
88
 
@@ -489,7 +492,10 @@ type pluginStructure = {
489
492
  traitDeclarations: option<array<traitDeclaration>>,
490
493
  }
491
494
 
492
- let pluginStructureOffloadSchema = Offload.optionSchema(~store="pluginStructures", pluginStructureSchema)
495
+ let pluginStructureOffloadSchema = Offload.optionSchema(
496
+ ~store="pluginStructures",
497
+ pluginStructureSchema,
498
+ )
493
499
 
494
500
  /**
495
501
  The self-description of a deployed plugin, persisted in the plugin's event store.
@@ -510,7 +516,8 @@ type pluginDefinition = {
510
516
  extensionProtocols: array<extensionProtocol>,
511
517
  // Offloadable: a large SDL fragment is content-addressed to the pluginApiFragments
512
518
  // store and carried by reference; a small one stays Inline.
513
- apiSchemaFragment: @s.matches(apiSchemaFragmentOffloadSchema) option<Offload.payload<apiSchemaFragment>>,
519
+ apiSchemaFragment: @s.matches(apiSchemaFragmentOffloadSchema)
520
+ option<Offload.payload<apiSchemaFragment>>,
514
521
  // Schema routing in split-API mode: None/"Domain" → DomainApi, Some("Platform") →
515
522
  // PlatformApi (and excluded from the DomainApi runtime schema).
516
523
  apiTarget: option<string>,
@@ -524,4 +531,3 @@ type pluginDefinition = {
524
531
  // variant → a bare JSON string.
525
532
  kind: pluginKind,
526
533
  }
527
-
@@ -205,4 +205,3 @@ module type Spec = {
205
205
  provisioning. */
206
206
  let visibility: Visibility.t
207
207
  }
208
-
@@ -99,10 +99,7 @@ Only one record deep, matching [`DcbTag.nestedRecordProperties`], which is the
99
99
  walk shared with the tag extraction so a marker is found in the same places by
100
100
  both.
101
101
  */
102
- let collectFieldTargets = (
103
- fieldName: string,
104
- schema: S.t<unknown>,
105
- ): array<(string, target)> =>
102
+ let collectFieldTargets = (fieldName: string, schema: S.t<unknown>): array<(string, target)> =>
106
103
  switch getFieldTarget(schema) {
107
104
  | Some(target) => [(fieldName, target)]
108
105
  | None =>
@@ -115,10 +112,7 @@ let collectFieldTargets = (
115
112
  ->Array.filterMap(((propName, propSchema)) =>
116
113
  propName === "TAG"
117
114
  ? None
118
- : getFieldTarget(propSchema)->Option.map(target => (
119
- `${prefix}.${propName}`,
120
- target,
121
- ))
115
+ : getFieldTarget(propSchema)->Option.map(target => (`${prefix}.${propName}`, target))
122
116
  )
123
117
  }
124
118
  }
@@ -83,10 +83,10 @@ the enclosing one would withhold a whole record because one leaf is private.
83
83
  */
84
84
  let isFieldSensitive = (schema: S.t<unknown>): bool =>
85
85
  isSensitive(schema) ||
86
- switch schema->Semantic.unwrapOptional {
87
- | Some(inner) => isSensitive(inner)
88
- | None => false
89
- }
86
+ switch schema->Semantic.unwrapOptional {
87
+ | Some(inner) => isSensitive(inner)
88
+ | None => false
89
+ }
90
90
 
91
91
  /**
92
92
  The sensitive fields declared on an object schema, in declaration order.
@@ -211,8 +211,10 @@ type stateAnnotationSpec = {
211
211
  }
212
212
 
213
213
  /** Sury metadata ID used to attach a `stateAnnotationSpec` to a state schema. */
214
- let stateAnnotationsId: S.Metadata.Id.t<stateAnnotationSpec> =
215
- S.Metadata.Id.make(~namespace="reventless", ~name="stateAnnotations")
214
+ let stateAnnotationsId: S.Metadata.Id.t<stateAnnotationSpec> = S.Metadata.Id.make(
215
+ ~namespace="reventless",
216
+ ~name="stateAnnotations",
217
+ )
216
218
 
217
219
  /** Returns the spec attached to a state schema, if any. */
218
220
  let getSpec = (schema: S.t<unknown>): option<stateAnnotationSpec> =>
@@ -41,7 +41,6 @@ let decide = (state, command) => switch command {
41
41
  }
42
42
  ```
43
43
  */
44
-
45
44
  /**
46
45
  The lean Spec for a StateChangeSlice — types, identity, schemas. State and
47
46
  state-evolution functions live in the sibling `Behavior` module type.
@@ -150,4 +149,3 @@ module type Behavior = {
150
149
  /** File URL of this Behavior module (`import.meta.url`). */
151
150
  let moduleUrl: string
152
151
  }
153
-
@@ -34,7 +34,6 @@ let project = ({event}) => switch event {
34
34
  }
35
35
  ```
36
36
  */
37
-
38
37
  /**
39
38
  The lean Spec for a StateViewSlice — types, identity, schemas, infra config.
40
39
  Per D2, `state` lives here (not in `Projection`) because the projected state
@@ -120,4 +119,3 @@ module type Projection = {
120
119
  /** File URL of this Projection module (`import.meta.url`). */
121
120
  let moduleUrl: string
122
121
  }
123
-
@@ -113,8 +113,7 @@ let armsOf = (schema: S.t<unknown>): option<array<arm>> =>
113
113
  | Some(String({const: ?Some(tag)})) =>
114
114
  let fields = properties->Dict.toArray->Array.filter(((name, _)) => name !== "TAG")
115
115
  if (
116
- fields->Array.length == 0 ||
117
- fields->Array.some(((name, _)) => isPositionalName(name))
116
+ fields->Array.length == 0 || fields->Array.some(((name, _)) => isPositionalName(name))
118
117
  ) {
119
118
  None
120
119
  } else {
@@ -59,4 +59,3 @@ type config = {
59
59
  buckets?: array<bucketSpec>,
60
60
  sideEffects?: sideEffects,
61
61
  }
62
-
@@ -26,7 +26,6 @@ The framework does not own the runner. A consumer runs their own suite, their ow
26
26
  way, and hands the report here — so this module is a pure transformation with no
27
27
  opinion about Jest, CI, or where files live. `fromReport` is the whole of it.
28
28
  */
29
-
30
29
  /** One assertion, and whether it held. Named, not numbered: a count that changed
31
30
  tells a reader nothing, and a name that disappeared tells them everything. */
32
31
  @schema
@@ -101,5 +100,6 @@ let render = (certificate: t): string =>
101
100
  console reader and a machine reader agree without either restating the rule. */
102
101
  let summarize = (certificate: t): string =>
103
102
  `${certificate.trait}@${certificate.traitVersion} → ${certificate.host}: ` ++
104
- `${certificate.passed->Int.toString}/${(certificate.assertions->Array.length)
103
+ `${certificate.passed->Int.toString}/${certificate.assertions
104
+ ->Array.length
105
105
  ->Int.toString} assertions, ` ++ (certificate->verified ? "verified" : "NOT verified")
@@ -24,7 +24,6 @@ the suite — the assertions are registered by a functor, not declared as data
24
24
  so a manifest that listed them would be restating the certificate from memory.
25
25
  A reader who wants to know what was proved reads a certificate.
26
26
  */
27
-
28
27
  /** One field the emitter's config declares. `required` is read off the schema,
29
28
  so an optional field cannot be listed as mandatory by a stale hand. */
30
29
  @schema
@@ -82,6 +81,7 @@ let configFieldsOf = (schema: S.t<unknown>): array<configField> =>
82
81
  }
83
82
  )
84
83
  )
84
+
85
85
  | _ => true
86
86
  },
87
87
  })
@@ -128,8 +128,7 @@ let main = async () => {
128
128
  ) catch {
129
129
  | _ =>
130
130
  fail(
131
- `${traitPackage} ships no conformance suite (looked for ${specifier}).\n` ++
132
- ` A trait without one cannot be certified — there is nothing to prove.`,
131
+ `${traitPackage} ships no conformance suite (looked for ${specifier}).\n` ++ ` A trait without one cannot be certified — there is nothing to prove.`,
133
132
  )
134
133
  %raw(`undefined`)
135
134
  }
@@ -149,8 +148,7 @@ let main = async () => {
149
148
  | [] =>
150
149
  fail(
151
150
  `the report contains no suite titled "${suite}".\n` ++
152
- ` Either the conformance binding was never registered, or ${host} is not the ` ++
153
- `name its Spec declares.`,
151
+ ` Either the conformance binding was never registered, or ${host} is not the ` ++ `name its Spec declares.`,
154
152
  )
155
153
  | results => {
156
154
  let resolveVersion = specifier =>