@reventlessdev/reventless-core 3.0.0-alpha.254 → 3.0.0-alpha.256

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 (41) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/package.json +8 -8
  3. package/src/admin/Platform_ComponentDefinitionsApi.res +5 -1
  4. package/src/admin/Platform_ComponentDefinitionsApi.res.mjs +5 -1
  5. package/src/components/Api/GraphQL_FragmentGenerator.res +55 -14
  6. package/src/components/Api/GraphQL_FragmentGenerator.res.mjs +43 -12
  7. package/src/components/Api/SchemaType.res +1 -0
  8. package/src/components/Api/SchemaType.res.mjs +4 -0
  9. package/src/components/Api/SuryToJsonSchema.res +41 -0
  10. package/src/components/Api/SuryToJsonSchema.res.mjs +50 -17
  11. package/src/components/StateChangeSlice/StateChangeSlice_Callback.res +2 -2
  12. package/src/plugin/component/Plugin_Structure.res +13 -0
  13. package/src/plugin/component/Plugin_Structure.res.mjs +5 -1
  14. package/src/plugin/lifecycle/PluginsReadModelSpec.res +1 -1
  15. package/src/plugin/lifecycle/PluginsReadModelSpec.res.mjs +1 -1
  16. package/tests/admin/AdminApiSchemaDriftTest.res +1 -0
  17. package/tests/admin/AdminApiSchemaDriftTest.res.mjs +4 -1
  18. package/tests/admin/Platform_ComponentDefinitionsApiTest.res +2 -0
  19. package/tests/admin/Platform_ComponentDefinitionsApiTest.res.mjs +4 -2
  20. package/tests/admin/Platform_PluginStructuresApiTest.res +26 -0
  21. package/tests/admin/Platform_PluginStructuresApiTest.res.mjs +9 -0
  22. package/tests/api/GraphQL_FragmentGeneratorTest.res +47 -0
  23. package/tests/api/GraphQL_FragmentGeneratorTest.res.mjs +32 -0
  24. package/tests/api/SuryToJsonSchemaTest.res +157 -0
  25. package/tests/api/SuryToJsonSchemaTest.res.mjs +81 -0
  26. package/tests/fixtures/plugin-lifecycle/2026-07-22-versionconnected-no-requiredstores.json +1 -1
  27. package/tests/fixtures/plugin-lifecycle/2026-07-22-versionsuperseded-nested-definitions.json +1 -1
  28. package/tests/fixtures/plugin-lifecycle/2026-07-28-versionconnected-requiredstores-only.json +1 -1
  29. package/tests/fixtures/plugin-lifecycle/2026-07-30-versionconnected-declarations-without-annotation.json +1 -1
  30. package/tests/fixtures/plugin-lifecycle/2026-07-30-versiondisconnected-declarations-without-annotation.json +1 -1
  31. package/tests/fixtures/plugin-lifecycle/README.md +12 -0
  32. package/tests/logger/LogFormatTest.res +1 -1
  33. package/tests/logger/LogFormatTest.res.mjs +1 -1
  34. package/tests/plugin/PluginDefinitionRequiredScalarsTest.res +1 -1
  35. package/tests/plugin/PluginDefinitionScalars.res +13 -8
  36. package/tests/plugin/PluginDefinitionScalars.res.mjs +13 -5
  37. package/tests/plugin/PluginStructureTest.res +22 -0
  38. package/tests/plugin/PluginStructureTest.res.mjs +7 -0
  39. package/tests/plugin/StateViewSlice/PsAnnotatedView.res +13 -3
  40. package/tests/plugin/StateViewSlice/PsAnnotatedView.res.mjs +7 -3
  41. package/tests/plugin/pluginDefinitionRequiredScalars.txt +17 -12
@@ -199,13 +199,16 @@ let outbound_externalSystem = "Shipper";
199
199
 
200
200
  let outbound_chapter = "Fulfilment";
201
201
 
202
+ let outbound_consumedSources = ["OrderingDcbEventLog"];
203
+
202
204
  let outbound = {
203
205
  name: "ToShipper",
204
206
  consumedEventTypes: outbound_consumedEventTypes,
205
207
  inboundCommandTypes: outbound_inboundCommandTypes,
206
208
  targetName: outbound_targetName,
207
209
  externalSystem: outbound_externalSystem,
208
- chapter: outbound_chapter
210
+ chapter: outbound_chapter,
211
+ consumedSources: outbound_consumedSources
209
212
  };
210
213
 
211
214
  let inbound_commandTypes = ["RecordPayment"];
@@ -77,6 +77,7 @@ let structure: pluginStructure = {
77
77
  targetName: None,
78
78
  externalSystem: None,
79
79
  chapter: None,
80
+ consumedSources: None,
80
81
  },
81
82
  ],
82
83
  inboundTranslationSlices: [
@@ -227,6 +228,7 @@ describe("translation-slice externalSystem round-trip", () => {
227
228
  targetName: None,
228
229
  externalSystem: Some("ShipperGateway"),
229
230
  chapter: None,
231
+ consumedSources: None,
230
232
  },
231
233
  ],
232
234
  inboundTranslationSlices: [
@@ -118,7 +118,8 @@ let structure_outboundTranslationSlices = [{
118
118
  inboundCommandTypes: ["Ship"],
119
119
  targetName: undefined,
120
120
  externalSystem: undefined,
121
- chapter: undefined
121
+ chapter: undefined,
122
+ consumedSources: undefined
122
123
  }];
123
124
 
124
125
  let structure_inboundTranslationSlices = [{
@@ -245,7 +246,8 @@ globalThis.describe("translation-slice externalSystem round-trip", () => {
245
246
  inboundCommandTypes: ["Ship"],
246
247
  targetName: undefined,
247
248
  externalSystem: "ShipperGateway",
248
- chapter: undefined
249
+ chapter: undefined,
250
+ consumedSources: undefined
249
251
  }];
250
252
  let externalStructure_inboundTranslationSlices = [{
251
253
  name: "FromBilling",
@@ -183,4 +183,30 @@ describe("absent optional collections", () => {
183
183
  testSync("encodes absent requiredStores as null", () =>
184
184
  expect(bareJson->String.includes("\"requiredStores\":null"))->toEqual(true)
185
185
  )
186
+
187
+ // The case the frozen corpus cannot reach: every fixture in it carries
188
+ // `"outboundTranslationSlices":[]`, so no stored payload has an outbound slice
189
+ // to be missing a field. A structure written before `consumedSources` existed
190
+ // has no such key. `parseJsonTolerant` is what makes the absence survivable,
191
+ // and it is the path the Plugin aggregate replays through.
192
+ testSync("an outbound slice stored before consumedSources existed still replays", () =>
193
+ expect(
194
+ `{"name":"ToShipper","consumedEventTypes":[],"inboundCommandTypes":[]}`
195
+ ->JSON.parseOrThrow
196
+ ->Reventless.Message.parseJsonTolerant(outboundTranslationSliceDefSchema)
197
+ ->(slice => slice.consumedSources),
198
+ )->toEqual(None)
199
+ )
200
+
201
+ // Absent needs no healer: an omitted key IS the optional encoding, so strict
202
+ // decode takes it. `parseJsonTolerant` above is for the fields a stale payload
203
+ // is missing that are NOT optional.
204
+ testSync("an omitted optional decodes as None on the strict path", () =>
205
+ expect(
206
+ `{"name":"ToShipper","consumedEventTypes":[],"inboundCommandTypes":[]}`
207
+ ->JSON.parseOrThrow
208
+ ->Reventless.Util_Sury.fromJson(outboundTranslationSliceDefSchema)
209
+ ->(slice => slice.consumedSources),
210
+ )->toEqual(None)
211
+ )
186
212
  })
@@ -4,6 +4,9 @@ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
4
4
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
5
5
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
6
  import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
7
+ import * as Plugin$Reventless from "@reventlessdev/reventless-spec/src/components/Plugin.res.mjs";
8
+ import * as Message$Reventless from "@reventlessdev/reventless-spec/src/types/Message.res.mjs";
9
+ import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
7
10
  import * as Platform_PluginStructuresApi$ReventlessCore from "../../src/admin/Platform_PluginStructuresApi.res.mjs";
8
11
 
9
12
  let publicRm_consumedEventTypes = ["ProductAdded"];
@@ -237,6 +240,12 @@ globalThis.describe("absent optional collections", () => {
237
240
  globalThis.test("encodes absent requiredStores as null", () => {
238
241
  globalThis.expect(bareJson.includes("\"requiredStores\":null")).toEqual(true);
239
242
  });
243
+ globalThis.test("an outbound slice stored before consumedSources existed still replays", () => {
244
+ globalThis.expect(Message$Reventless.parseJsonTolerant(JSON.parse(`{"name":"ToShipper","consumedEventTypes":[],"inboundCommandTypes":[]}`), Plugin$Reventless.outboundTranslationSliceDefSchema).consumedSources).toEqual(undefined);
245
+ });
246
+ globalThis.test("an omitted optional decodes as None on the strict path", () => {
247
+ globalThis.expect(Util_Sury$Reventless.fromJson(JSON.parse(`{"name":"ToShipper","consumedEventTypes":[],"inboundCommandTypes":[]}`), Plugin$Reventless.outboundTranslationSliceDefSchema).consumedSources).toEqual(undefined);
248
+ });
240
249
  });
241
250
 
242
251
  export {
@@ -139,6 +139,53 @@ describe("resolveKeyField — the ladder", () => {
139
139
  })
140
140
  })
141
141
 
142
+ // Losing the key is invisible — the view keeps every field and every row, and
143
+ // drops its `<field>Eq` filter and its whole `orderBy` from the SDL. These pin
144
+ // which of the two ways to lose it is worth saying out loud.
145
+ describe("keyFieldGapMessage — which gap is worth a warning", () => {
146
+ let gapFor = (~entityName, schema) =>
147
+ GraphQL_FragmentGenerator.classifyKeyField(~entityName, schema->S.castToUnknown)
148
+ ->GraphQL_FragmentGenerator.keyFieldGapMessage
149
+
150
+ // The accident §14b.2 named: the view had `productId` alone and somebody added
151
+ // `categoryId`, so `sole` stopped firing and the name matches neither.
152
+ testSync("a second `*Id` field taking the key away is named, with both fields", () => {
153
+ let schema = S.schema(s =>
154
+ {"productId": s.matches(S.string), "categoryId": s.matches(S.string)}
155
+ )
156
+ let message = gapFor(~entityName="ProductDemand", schema)->Option.getOr("")
157
+ expect(message->String.includes("productId, categoryId"))->toBe(true)
158
+ expect(message->String.includes("productDemandId"))->toBe(true)
159
+ })
160
+
161
+ // A read model over an aggregate keeps the row's id on the row key, not in its
162
+ // state. Warning here would fire on most of them and get the rule silenced —
163
+ // six views across the example plugins are exactly this shape.
164
+ testSync("a state with no `*Id` field at all is silent, not warned about", () =>
165
+ expect(
166
+ gapFor(
167
+ ~entityName="Customers",
168
+ S.schema(s => {"email": s.matches(S.string), "address": s.matches(S.string)}),
169
+ ),
170
+ )->toEqual(None)
171
+ )
172
+
173
+ testSync("a resolved key says nothing", () =>
174
+ expect(
175
+ gapFor(~entityName="Orders", S.schema(s => {"orderId": s.matches(S.string)})),
176
+ )->toEqual(None)
177
+ )
178
+
179
+ testSync("an @id-less view whose name picks one of its `*Id` fields is fine", () =>
180
+ expect(
181
+ gapFor(
182
+ ~entityName="Products",
183
+ S.schema(s => {"productId": s.matches(S.string), "categoryId": s.matches(S.string)}),
184
+ ),
185
+ )->toEqual(None)
186
+ )
187
+ })
188
+
142
189
  describe("deriveServerCapability — inferred keys reach the SDL surface", () => {
143
190
  let capabilityFor = (~entityName, schema) =>
144
191
  GraphQL_FragmentGenerator.deriveServerCapability(~entityName, schema->S.castToUnknown)
@@ -148,6 +148,38 @@ globalThis.describe("resolveKeyField — the ladder", () => {
148
148
  });
149
149
  });
150
150
 
151
+ globalThis.describe("keyFieldGapMessage — which gap is worth a warning", () => {
152
+ globalThis.test("a second `*Id` field taking the key away is named, with both fields", () => {
153
+ let schema = Sury.$schema(s => ({
154
+ productId: s.m(Sury.string),
155
+ categoryId: s.m(Sury.string)
156
+ }));
157
+ let message = Stdlib_Option.getOr(GraphQL_FragmentGenerator$ReventlessCore.keyFieldGapMessage(GraphQL_FragmentGenerator$ReventlessCore.classifyKeyField("ProductDemand", schema)), "");
158
+ globalThis.expect(message.includes("productId, categoryId")).toBe(true);
159
+ globalThis.expect(message.includes("productDemandId")).toBe(true);
160
+ });
161
+ globalThis.test("a state with no `*Id` field at all is silent, not warned about", () => {
162
+ let schema = Sury.$schema(s => ({
163
+ email: s.m(Sury.string),
164
+ address: s.m(Sury.string)
165
+ }));
166
+ globalThis.expect(GraphQL_FragmentGenerator$ReventlessCore.keyFieldGapMessage(GraphQL_FragmentGenerator$ReventlessCore.classifyKeyField("Customers", schema))).toEqual(undefined);
167
+ });
168
+ globalThis.test("a resolved key says nothing", () => {
169
+ let schema = Sury.$schema(s => ({
170
+ orderId: s.m(Sury.string)
171
+ }));
172
+ globalThis.expect(GraphQL_FragmentGenerator$ReventlessCore.keyFieldGapMessage(GraphQL_FragmentGenerator$ReventlessCore.classifyKeyField("Orders", schema))).toEqual(undefined);
173
+ });
174
+ globalThis.test("an @id-less view whose name picks one of its `*Id` fields is fine", () => {
175
+ let schema = Sury.$schema(s => ({
176
+ productId: s.m(Sury.string),
177
+ categoryId: s.m(Sury.string)
178
+ }));
179
+ globalThis.expect(GraphQL_FragmentGenerator$ReventlessCore.keyFieldGapMessage(GraphQL_FragmentGenerator$ReventlessCore.classifyKeyField("Products", schema))).toEqual(undefined);
180
+ });
181
+ });
182
+
151
183
  globalThis.describe("deriveServerCapability — inferred keys reach the SDL surface", () => {
152
184
  globalThis.test("an inferred key yields an eq filter and a sort field", () => {
153
185
  let schema = Sury.$schema(s => ({
@@ -119,6 +119,78 @@ describe("SuryToJsonSchema:", () => {
119
119
  })
120
120
  })
121
121
 
122
+ describe("deriveObjectSchema with a sensitive field:", () => {
123
+ let sensitiveOf = (json, name) =>
124
+ getPropertyOf(json, name)->Option.flatMap(s => getProperty(s, "x-reventless-sensitive"))
125
+ let ownerOf = (json, name) =>
126
+ getPropertyOf(json, name)->Option.flatMap(s => getProperty(s, "x-reventless-owner"))
127
+
128
+ let json = SuryToJsonSchema.deriveObjectSchema(
129
+ S.schema(s =>
130
+ {
131
+ "resetToken": s.matches(Reventless.Sensitive.string),
132
+ "note": s.matches(S.string),
133
+ "contact": s.matches(Reventless.Email.schema),
134
+ }
135
+ )->S.castToUnknown,
136
+ )
137
+
138
+ testSync("the marked field carries x-reventless-sensitive", () =>
139
+ expect(sensitiveOf(json, "resetToken"))->toEqual(Some(JSON.Encode.bool(true)))
140
+ )
141
+
142
+ // The control. Absent means "not stated", and a reader that misses the
143
+ // marker renders the value — so a bug that marked everything would look like
144
+ // caution while a bug that marked nothing leaks. Both need catching, and
145
+ // only this assertion catches the first.
146
+ testSync("an unmarked string field carries nothing", () =>
147
+ expect(sensitiveOf(json, "note"))->toBe(None)
148
+ )
149
+
150
+ // A contact detail is sensitive whether or not anybody wrote it down: the
151
+ // whole meaning of the semantic is "how to reach a particular person".
152
+ testSync("an email field is sensitive with no annotation", () =>
153
+ expect(sensitiveOf(json, "contact"))->toEqual(Some(JSON.Encode.bool(true)))
154
+ )
155
+
156
+ testSync("the field is still a plain string on the wire", () =>
157
+ expect(
158
+ getPropertyOf(json, "resetToken")->Option.flatMap(s => getProperty(s, "type")),
159
+ )->toEqual(Some(JSON.Encode.string("string")))
160
+ )
161
+
162
+ // Same trap as the owner case, and worse in consequence: a reader that only
163
+ // inspects the outer schema answers "not sensitive" for an optional field,
164
+ // and the value goes into a message.
165
+ testSync("an optional sensitive field is still recognised", () => {
166
+ let optJson = SuryToJsonSchema.deriveObjectSchema(
167
+ S.schema(s =>
168
+ {
169
+ "resetToken": s.matches(S.option(Reventless.Sensitive.string)),
170
+ }
171
+ )->S.castToUnknown,
172
+ )
173
+ expect(sensitiveOf(optJson, "resetToken"))->toEqual(Some(JSON.Encode.bool(true)))
174
+ })
175
+
176
+ // The composition rule. `mark` wraps rather than replaces, so a field that
177
+ // is both an owner and sensitive keeps both — a marker that silently
178
+ // replaced the other would unscope a view or unmask a value.
179
+ testSync("sensitivity composes with ownership on one field", () => {
180
+ let bothJson = SuryToJsonSchema.deriveObjectSchema(
181
+ S.schema(s =>
182
+ {
183
+ "customerId": s.matches(Reventless.Sensitive.mark(Reventless.Owner.string)),
184
+ }
185
+ )->S.castToUnknown,
186
+ )
187
+ expect((sensitiveOf(bothJson, "customerId"), ownerOf(bothJson, "customerId")))->toEqual((
188
+ Some(JSON.Encode.bool(true)),
189
+ Some(JSON.Encode.bool(true)),
190
+ ))
191
+ })
192
+ })
193
+
122
194
  describe("deriveObjectSchema with stateAnnotations metadata:", () => {
123
195
  let withSpec = (schema, spec) =>
124
196
  schema->S.Metadata.set(~id=Reventless.StateAnnotations.stateAnnotationsId, spec)
@@ -927,6 +999,91 @@ describe("SuryToJsonSchema:", () => {
927
999
  })
928
1000
  })
929
1001
 
1002
+ // A selection, not an input. The whole point of the id is that it travels the
1003
+ // same channel as a store declaration and says something a store declaration
1004
+ // cannot: the candidates are on the row, so nothing is provisioned for it.
1005
+ describe("a member-reference field:", () => {
1006
+ let targetOf = (json, name) =>
1007
+ getPropertyOf(json, name)->Option.flatMap(s =>
1008
+ getProperty(s, "x-reventless-semantic-target")
1009
+ )
1010
+
1011
+ testSync("carries the collection, the view holding it, and what a member is", () => {
1012
+ let json = SuryToJsonSchema.deriveObjectSchema(
1013
+ S.schema(s =>
1014
+ {
1015
+ "productImage": s.matches(
1016
+ Reventless.MemberRef.of_(
1017
+ ~view="Products",
1018
+ ~content=Reventless.Semantic.Id.imageRef,
1019
+ ~field="productImages",
1020
+ ),
1021
+ ),
1022
+ }
1023
+ )->S.castToUnknown,
1024
+ )
1025
+ expect(
1026
+ getPropertyOf(json, "productImage")->Option.flatMap(s =>
1027
+ getProperty(s, "x-reventless-semantic")
1028
+ ),
1029
+ )->toEqual(Some(JSON.Encode.string("memberRef")))
1030
+ expect(targetOf(json, "productImage"))->toEqual(
1031
+ Some(
1032
+ JSON.Encode.object(
1033
+ Dict.fromArray([
1034
+ ("field", JSON.Encode.string("productImages")),
1035
+ ("view", JSON.Encode.string("Products")),
1036
+ ("content", JSON.Encode.string("imageRef")),
1037
+ ]),
1038
+ ),
1039
+ ),
1040
+ )
1041
+ })
1042
+
1043
+ // The second position: a declaration on a view's own state, where the
1044
+ // collection is on the same record and naming a view would invite the reader
1045
+ // to think another one could be meant.
1046
+ testSync("omits the view where the collection is on this very record", () =>
1047
+ expect(
1048
+ targetOf(
1049
+ SuryToJsonSchema.deriveObjectSchema(
1050
+ S.schema(s =>
1051
+ {"productImage": s.matches(Reventless.MemberRef.of_(~field="productImages"))}
1052
+ )->S.castToUnknown,
1053
+ ),
1054
+ "productImage",
1055
+ ),
1056
+ )->toEqual(
1057
+ Some(JSON.Encode.object(Dict.fromArray([("field", JSON.Encode.string("productImages"))]))),
1058
+ )
1059
+ )
1060
+
1061
+ // The assertion the whole design rests on, and the one a reader will want to
1062
+ // see rather than infer: a `memberRef` field declares NO store, so the
1063
+ // provisioning walk passes it by and no upload endpoint is bound. This is
1064
+ // exactly what `imageRef` gets today, reached through a different payload.
1065
+ testSync("declares no store, so nothing is provisioned for it", () =>
1066
+ expect(
1067
+ Reventless.StorageRef.getFieldStore(
1068
+ Reventless.MemberRef.of_(~view="Products", ~field="productImages"),
1069
+ ),
1070
+ )->toBe(None)
1071
+ )
1072
+
1073
+ testSync("stays a plain string on the wire", () =>
1074
+ expect(
1075
+ getPropertyOf(
1076
+ SuryToJsonSchema.deriveObjectSchema(
1077
+ S.schema(s =>
1078
+ {"productImage": s.matches(Reventless.MemberRef.of_(~field="productImages"))}
1079
+ )->S.castToUnknown,
1080
+ ),
1081
+ "productImage",
1082
+ )->Option.flatMap(s => getProperty(s, "type")),
1083
+ )->toEqual(Some(JSON.Encode.string("string")))
1084
+ )
1085
+ })
1086
+
930
1087
  // The claim the branded scalars rest on: marking a field adds an annotation,
931
1088
  // not a shape. A string stays a string and a number stays a number on the
932
1089
  // wire, which is what makes them retrofittable onto a log that already has
@@ -13,8 +13,11 @@ import * as Percent$Reventless from "@reventlessdev/reventless-spec/src/semantic
13
13
  import * as Currency$Reventless from "@reventlessdev/reventless-spec/src/semantic/Currency.res.mjs";
14
14
  import * as DateTime$Reventless from "@reventlessdev/reventless-spec/src/types/DateTime.res.mjs";
15
15
  import * as GeoPoint$Reventless from "@reventlessdev/reventless-spec/src/semantic/GeoPoint.res.mjs";
16
+ import * as Semantic$Reventless from "@reventlessdev/reventless-spec/src/semantic/Semantic.res.mjs";
16
17
  import * as DateRange$Reventless from "@reventlessdev/reventless-spec/src/semantic/DateRange.res.mjs";
18
+ import * as MemberRef$Reventless from "@reventlessdev/reventless-spec/src/semantic/MemberRef.res.mjs";
17
19
  import * as Reference$Reventless from "@reventlessdev/reventless-spec/src/components/Reference.res.mjs";
20
+ import * as Sensitive$Reventless from "@reventlessdev/reventless-spec/src/components/Sensitive.res.mjs";
18
21
  import * as StorageRef$Reventless from "@reventlessdev/reventless-spec/src/semantic/StorageRef.res.mjs";
19
22
  import * as StateAnnotations$Reventless from "@reventlessdev/reventless-spec/src/components/StateAnnotations.res.mjs";
20
23
  import * as SuryToJsonSchema$ReventlessCore from "../../src/components/Api/SuryToJsonSchema.res.mjs";
@@ -130,6 +133,45 @@ globalThis.describe("SuryToJsonSchema:", () => {
130
133
  globalThis.expect(ownerOf(optJson, "customerId")).toEqual(true);
131
134
  });
132
135
  });
136
+ globalThis.describe("deriveObjectSchema with a sensitive field:", () => {
137
+ let sensitiveOf = (json, name) => Stdlib_Option.flatMap(getPropertyOf(json, name), s => getProperty(s, "x-reventless-sensitive"));
138
+ let ownerOf = (json, name) => Stdlib_Option.flatMap(getPropertyOf(json, name), s => getProperty(s, "x-reventless-owner"));
139
+ let json = SuryToJsonSchema$ReventlessCore.deriveObjectSchema(Sury.$schema(s => ({
140
+ resetToken: s.m(Sensitive$Reventless.string),
141
+ note: s.m(Sury.string),
142
+ contact: s.m(Email$Reventless.schema)
143
+ })));
144
+ globalThis.test("the marked field carries x-reventless-sensitive", () => {
145
+ globalThis.expect(sensitiveOf(json, "resetToken")).toEqual(true);
146
+ });
147
+ globalThis.test("an unmarked string field carries nothing", () => {
148
+ globalThis.expect(sensitiveOf(json, "note")).toBe(undefined);
149
+ });
150
+ globalThis.test("an email field is sensitive with no annotation", () => {
151
+ globalThis.expect(sensitiveOf(json, "contact")).toEqual(true);
152
+ });
153
+ globalThis.test("the field is still a plain string on the wire", () => {
154
+ globalThis.expect(Stdlib_Option.flatMap(getPropertyOf(json, "resetToken"), s => getProperty(s, "type"))).toEqual("string");
155
+ });
156
+ globalThis.test("an optional sensitive field is still recognised", () => {
157
+ let optJson = SuryToJsonSchema$ReventlessCore.deriveObjectSchema(Sury.$schema(s => ({
158
+ resetToken: s.m(Sury.$option(Sensitive$Reventless.string))
159
+ })));
160
+ globalThis.expect(sensitiveOf(optJson, "resetToken")).toEqual(true);
161
+ });
162
+ globalThis.test("sensitivity composes with ownership on one field", () => {
163
+ let bothJson = SuryToJsonSchema$ReventlessCore.deriveObjectSchema(Sury.$schema(s => ({
164
+ customerId: s.m(Sensitive$Reventless.mark(Owner$Reventless.string))
165
+ })));
166
+ globalThis.expect([
167
+ sensitiveOf(bothJson, "customerId"),
168
+ ownerOf(bothJson, "customerId")
169
+ ]).toEqual([
170
+ true,
171
+ true
172
+ ]);
173
+ });
174
+ });
133
175
  globalThis.describe("deriveObjectSchema with stateAnnotations metadata:", () => {
134
176
  globalThis.test("emits x-reventless-id on field listed in ids", () => {
135
177
  let schema = Sury.$schema(s => ({
@@ -872,6 +914,45 @@ globalThis.describe("SuryToJsonSchema:", () => {
872
914
  globalThis.expect(semanticOf(json, "imageUrl")).toEqual("storageRef");
873
915
  });
874
916
  });
917
+ globalThis.describe("a member-reference field:", () => {
918
+ let targetOf = (json, name) => Stdlib_Option.flatMap(getPropertyOf(json, name), s => getProperty(s, "x-reventless-semantic-target"));
919
+ globalThis.test("carries the collection, the view holding it, and what a member is", () => {
920
+ let json = SuryToJsonSchema$ReventlessCore.deriveObjectSchema(Sury.$schema(s => ({
921
+ productImage: s.m(MemberRef$Reventless.of_(undefined, "Products", Semantic$Reventless.Id.imageRef, "productImages"))
922
+ })));
923
+ globalThis.expect(Stdlib_Option.flatMap(getPropertyOf(json, "productImage"), s => getProperty(s, "x-reventless-semantic"))).toEqual("memberRef");
924
+ globalThis.expect(targetOf(json, "productImage")).toEqual(Object.fromEntries([
925
+ [
926
+ "field",
927
+ "productImages"
928
+ ],
929
+ [
930
+ "view",
931
+ "Products"
932
+ ],
933
+ [
934
+ "content",
935
+ "imageRef"
936
+ ]
937
+ ]));
938
+ });
939
+ globalThis.test("omits the view where the collection is on this very record", () => {
940
+ globalThis.expect(targetOf(SuryToJsonSchema$ReventlessCore.deriveObjectSchema(Sury.$schema(s => ({
941
+ productImage: s.m(MemberRef$Reventless.of_(undefined, undefined, undefined, "productImages"))
942
+ }))), "productImage")).toEqual(Object.fromEntries([[
943
+ "field",
944
+ "productImages"
945
+ ]]));
946
+ });
947
+ globalThis.test("declares no store, so nothing is provisioned for it", () => {
948
+ globalThis.expect(StorageRef$Reventless.getFieldStore(MemberRef$Reventless.of_(undefined, "Products", undefined, "productImages"))).toBe(undefined);
949
+ });
950
+ globalThis.test("stays a plain string on the wire", () => {
951
+ globalThis.expect(Stdlib_Option.flatMap(getPropertyOf(SuryToJsonSchema$ReventlessCore.deriveObjectSchema(Sury.$schema(s => ({
952
+ productImage: s.m(MemberRef$Reventless.of_(undefined, undefined, undefined, "productImages"))
953
+ }))), "productImage"), s => getProperty(s, "type"))).toEqual("string");
954
+ });
955
+ });
875
956
  globalThis.describe("branded scalars keep their underlying shape:", () => {
876
957
  let keyOf = (json, name, key) => Stdlib_Option.flatMap(getPropertyOf(json, name), s => getProperty(s, key));
877
958
  let json = SuryToJsonSchema$ReventlessCore.deriveObjectSchema(Sury.$schema(s => ({
@@ -1 +1 @@
1
- {"event":"VersionConnected","data":{"_0":{"dcbEventLog":{"name":"CatalogDcbEventLog","eventTopicArn":"arn:aws:dynamodb:eu-west-1:000000000000:table/CatalogDcbEventLog-925103d/stream/2026-07-14T08:45:33.852"},"extensions":[{"name":"Ordering.Orders.Catalog","extensionPointName":"Ordering.Orders","dcbSources":[]}],"extensionProtocols":[],"extensionPoints":[{"name":"Catalog.Products","commandTopic":"https://sqs.eu-west-1.amazonaws.com/000000000000/CatalogProductsExtPointCmdTopic-1dd983b","eventTopic":"arn:aws:sns:eu-west-1:000000000000:CatalogProductsExtPointEventTopic-5cb0b5e"}],"kind":"Domain","name":"Catalog","apiTarget":"Domain","id":"Catalog@1.0.0-alpha.149","version":"1.0.0-alpha.149","apiSchemaFragment":{"protocol":"graphql","encoded":"{\"types\":[\"type Catalog_Category implements Node {\\n id: ID!\\n categoryId: ID!\\n name: String!\\n archived: Boolean!\\n}\",\"type Catalog_CategoryEdge {\\n node: Catalog_Category!\\n cursor: String!\\n}\",\"type Catalog_CategoryConnection {\\n edges: [Catalog_CategoryEdge!]!\\n pageInfo: PageInfo!\\n}\",\"input Catalog_CategoryFilter {\\n search: String\\n searchPrefix: String\\n ids: [ID!]\\n}\",\"type Catalog_ProductDemand implements Node {\\n id: ID!\\n productId: ID!\\n name: String!\\n categoryId: ID!\\n orderCount: Float!\\n}\",\"type Catalog_ProductDemandEdge {\\n node: Catalog_ProductDemand!\\n cursor: String!\\n}\",\"type Catalog_ProductDemandConnection {\\n edges: [Catalog_ProductDemandEdge!]!\\n pageInfo: PageInfo!\\n}\",\"input Catalog_ProductDemandFilter {\\n search: String\\n searchPrefix: String\\n ids: [ID!]\\n}\",\"type Catalog_Product implements Node {\\n id: ID!\\n productId: ID!\\n name: String!\\n description: String!\\n price: Float!\\n categoryId: ID!\\n}\",\"type Catalog_ProductEdge {\\n node: Catalog_Product!\\n cursor: String!\\n}\",\"type Catalog_ProductConnection {\\n edges: [Catalog_ProductEdge!]!\\n pageInfo: PageInfo!\\n}\",\"input Catalog_ProductFilter {\\n search: String\\n searchPrefix: String\\n ids: [ID!]\\n}\",\"enum Catalog_ImportProductAuditStatus {\\n Success\\n Failure\\n}\",\"type Catalog_ImportProductAudit implements Node {\\n id: ID!\\n input: String!\\n status: Catalog_ImportProductAuditStatus!\\n targetIds: [ID!]\\n commandCount: Float\\n error: String\\n receivedAt: String!\\n}\",\"type Catalog_ImportProductAuditEdge {\\n node: Catalog_ImportProductAudit!\\n cursor: String!\\n}\",\"type Catalog_ImportProductAuditConnection {\\n edges: [Catalog_ImportProductAuditEdge!]!\\n pageInfo: PageInfo!\\n}\",\"input Catalog_ImportProductAuditFilter {\\n search: String\\n searchPrefix: String\\n ids: [ID!]\\n}\",\"union CommandResult = CommandAccepted | CommandRejected | CommandPending\",\"type CommandAccepted {\\n msgId: ID!\\n entityId: ID\\n eventCount: Int!\\n}\",\"type CommandRejected {\\n msgId: ID!\\n errorCode: String!\\n errorDetail: String\\n}\",\"type CommandPending {\\n msgId: ID!\\n}\",\"type CatalogEventLogEvent {\\n position: String!\\n eventType: String!\\n payload: AWSJSON!\\n}\"],\"mutations\":[\" Catalog_AddCategory(categoryId: ID!, name: String!): CommandResult!\",\" Catalog_AddProduct(productId: ID!, name: String!, description: String!, price: Float!, categoryId: ID!): CommandResult!\",\" Catalog_ArchiveCategory(categoryId: ID!): CommandResult!\\n @aws_auth(cognito_groups: [\\\"Admin\\\"])\",\" Catalog_ChangeProductDescription(productId: ID!, description: String!): CommandResult!\",\" Catalog_ChangeProductName(productId: ID!, name: String!): CommandResult!\",\" Catalog_ChangeProductPrice(productId: ID!, price: Float!): CommandResult!\",\" Catalog_RenameCategory(categoryId: ID!, name: String!): CommandResult!\",\" Catalog_ImportProduct(sku: String!, title: String!, desc: String!, unitPrice: Float!, currency: String!, category: String!): CommandResult!\"],\"queries\":[\" Catalog_Category(id: ID!): Catalog_Category\",\" Catalog_CategoriesByIds(ids: [String!]!): [Catalog_Category!]!\",\" Catalog_Categories(filter: Catalog_CategoryFilter, first: Int, after: String, last: Int, before: String): Catalog_CategoryConnection!\",\" Catalog_ProductDemand(id: ID!): Catalog_ProductDemand\",\" Catalog_ProductDemandsByIds(ids: [String!]!): [Catalog_ProductDemand!]!\",\" Catalog_ProductDemands(filter: Catalog_ProductDemandFilter, first: Int, after: String, last: Int, before: String): Catalog_ProductDemandConnection!\",\" Catalog_Product(id: ID!): Catalog_Product\",\" Catalog_ProductsByIds(ids: [String!]!): [Catalog_Product!]!\",\" Catalog_Products(filter: Catalog_ProductFilter, first: Int, after: String, last: Int, before: String): Catalog_ProductConnection!\",\" Catalog_ImportProductAudit(id: ID!): Catalog_ImportProductAudit\",\" Catalog_ImportProductAuditsByIds(ids: [String!]!): [Catalog_ImportProductAudit!]!\",\" Catalog_ImportProductAudits(filter: Catalog_ImportProductAuditFilter, first: Int, after: String, last: Int, before: String): Catalog_ImportProductAuditConnection!\"],\"subscriptions\":[\" onCatalog_AddCategory(id: ID): CommandResult\",\" onCatalog_AddProduct(id: ID): CommandResult\",\" onCatalog_ArchiveCategory(id: ID): CommandResult\",\" onCatalog_ChangeProductDescription(id: ID): CommandResult\",\" onCatalog_ChangeProductName(id: ID): CommandResult\",\" onCatalog_ChangeProductPrice(id: ID): CommandResult\",\" onCatalog_RenameCategory(id: ID): CommandResult\",\" onCatalog_ImportProduct(id: ID): CommandResult\",\" onCatalogEventLog_eventAppended: CatalogEventLogEvent\"],\"subscriptionSources\":[{\"field\":\"onCatalog_AddCategory\",\"mutations\":[\"Catalog_AddCategory\"]},{\"field\":\"onCatalog_AddProduct\",\"mutations\":[\"Catalog_AddProduct\"]},{\"field\":\"onCatalog_ArchiveCategory\",\"mutations\":[\"Catalog_ArchiveCategory\"]},{\"field\":\"onCatalog_ChangeProductDescription\",\"mutations\":[\"Catalog_ChangeProductDescription\"]},{\"field\":\"onCatalog_ChangeProductName\",\"mutations\":[\"Catalog_ChangeProductName\"]},{\"field\":\"onCatalog_ChangeProductPrice\",\"mutations\":[\"Catalog_ChangeProductPrice\"]},{\"field\":\"onCatalog_RenameCategory\",\"mutations\":[\"Catalog_RenameCategory\"]},{\"field\":\"onCatalog_ImportProduct\",\"mutations\":[\"Catalog_ImportProduct\"]}]}"},"eventCollector":"arn:aws:sqs:eu-west-1:000000000000:CatalogPluginEventColl-fc85d05","structure":{"outboundTranslationSlices":[],"extensions":[{"name":"Ordering.Orders","delegateNames":["RecordProductDemand"],"eventTypes":["Ordering.Orders.ItemOrdered","Ordering.Orders.ItemOrderCancelled"],"commandTypes":[]}],"stateChangeSlices":[{"consistencyRead":null,"chapter":"Category","linkedViews":["Categories"],"producedEventTypes":["Catalog.CategoryAdded"],"name":"AddCategory","commands":[{"schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"AddCategory\"},\"categoryId\":{\"type\":\"string\"},\"name\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"categoryId\",\"name\"]}","aggregateIdField":"categoryId","mutationField":"Catalog_AddCategory","references":[],"level":"Collection","apiExposed":true,"name":"AddCategory","allowedStates":null}],"events":[{"name":"CategoryAdded","schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"CategoryAdded\"},\"categoryId\":{\"type\":\"string\"},\"name\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"categoryId\",\"name\"]}","references":[]}],"consumedEventTypes":[]},{"consistencyRead":"Categories","chapter":"Product","linkedViews":["ProductDemand","Products"],"producedEventTypes":["Catalog.ProductAdded"],"name":"AddProduct","commands":[{"schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"AddProduct\"},\"productId\":{\"type\":\"string\"},\"name\":{\"type\":\"string\"},\"description\":{\"type\":\"string\"},\"price\":{\"type\":\"number\"},\"categoryId\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"name\",\"description\",\"price\",\"categoryId\"]}","aggregateIdField":"productId","mutationField":"Catalog_AddProduct","references":[{"plugin":null,"entity":"Categories","fieldName":"categoryId"}],"level":"Collection","apiExposed":true,"name":"AddProduct","allowedStates":null}],"events":[{"name":"ProductAdded","schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ProductAdded\"},\"productId\":{\"type\":\"string\"},\"name\":{\"type\":\"string\"},\"description\":{\"type\":\"string\"},\"price\":{\"type\":\"number\"},\"categoryId\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"name\",\"description\",\"price\",\"categoryId\"]}","references":[]}],"consumedEventTypes":["Catalog.ProductAdded","Catalog.CategoryAdded","Catalog.CategoryArchived"]},{"consistencyRead":null,"chapter":"Category","linkedViews":["Categories"],"producedEventTypes":["Catalog.CategoryArchived"],"name":"ArchiveCategory","commands":[{"schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ArchiveCategory\"},\"categoryId\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"categoryId\"]}","aggregateIdField":"categoryId","mutationField":"Catalog_ArchiveCategory","references":[],"level":"Instance","apiExposed":true,"name":"ArchiveCategory","allowedStates":null}],"events":[{"name":"CategoryArchived","schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"CategoryArchived\"},\"categoryId\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"categoryId\"]}","references":[]}],"consumedEventTypes":[]},{"consistencyRead":"Products","chapter":"Product","linkedViews":["Products"],"producedEventTypes":["Catalog.ProductDescriptionChanged"],"name":"ChangeProductDescription","commands":[{"schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ChangeProductDescription\"},\"productId\":{\"type\":\"string\"},\"description\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"description\"]}","aggregateIdField":"productId","mutationField":"Catalog_ChangeProductDescription","references":[],"level":"Instance","apiExposed":true,"name":"ChangeProductDescription","allowedStates":null}],"events":[{"name":"ProductDescriptionChanged","schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ProductDescriptionChanged\"},\"productId\":{\"type\":\"string\"},\"description\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"description\"]}","references":[]}],"consumedEventTypes":["Catalog.ProductAdded","Catalog.ProductDescriptionChanged"]},{"consistencyRead":"Products","chapter":"Product","linkedViews":["Products"],"producedEventTypes":["Catalog.ProductNameChanged"],"name":"ChangeProductName","commands":[{"schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ChangeProductName\"},\"productId\":{\"type\":\"string\"},\"name\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"name\"]}","aggregateIdField":"productId","mutationField":"Catalog_ChangeProductName","references":[],"level":"Instance","apiExposed":true,"name":"ChangeProductName","allowedStates":null}],"events":[{"name":"ProductNameChanged","schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ProductNameChanged\"},\"productId\":{\"type\":\"string\"},\"name\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"name\"]}","references":[]}],"consumedEventTypes":["Catalog.ProductAdded","Catalog.ProductNameChanged"]},{"consistencyRead":"Products","chapter":"Product","linkedViews":["Products"],"producedEventTypes":["Catalog.ProductPriceChanged"],"name":"ChangeProductPrice","commands":[{"schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ChangeProductPrice\"},\"productId\":{\"type\":\"string\"},\"price\":{\"type\":\"number\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"price\"]}","aggregateIdField":"productId","mutationField":"Catalog_ChangeProductPrice","references":[],"level":"Instance","apiExposed":true,"name":"ChangeProductPrice","allowedStates":null}],"events":[{"name":"ProductPriceChanged","schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ProductPriceChanged\"},\"productId\":{\"type\":\"string\"},\"price\":{\"type\":\"number\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"price\"]}","references":[]}],"consumedEventTypes":["Catalog.ProductAdded","Catalog.ProductPriceChanged"]},{"consistencyRead":"ProductDemand","chapter":"ProductDemand","linkedViews":["ProductDemand"],"producedEventTypes":["Catalog.ProductDemandRecorded","Catalog.ProductDemandRevoked"],"name":"RecordProductDemand","commands":[{"schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"RecordDemand\"},\"productId\":{\"type\":\"string\"},\"orderId\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"orderId\"]}","aggregateIdField":"productId","mutationField":"Catalog_RecordDemand","references":[],"level":"Instance","apiExposed":false,"name":"RecordDemand","allowedStates":null},{"schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"RevokeDemand\"},\"productId\":{\"type\":\"string\"},\"orderId\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"orderId\"]}","aggregateIdField":"productId","mutationField":"Catalog_RevokeDemand","references":[],"level":"Instance","apiExposed":false,"name":"RevokeDemand","allowedStates":null}],"events":[{"name":"ProductDemandRecorded","schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ProductDemandRecorded\"},\"productId\":{\"type\":\"string\"},\"orderId\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"orderId\"]}","references":[]},{"name":"ProductDemandRevoked","schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ProductDemandRevoked\"},\"productId\":{\"type\":\"string\"},\"orderId\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"orderId\"]}","references":[]}],"consumedEventTypes":["Catalog.ProductDemandRecorded","Catalog.ProductDemandRevoked"]},{"consistencyRead":"Categories","chapter":"Category","linkedViews":["Categories"],"producedEventTypes":["Catalog.CategoryRenamed"],"name":"RenameCategory","commands":[{"schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"RenameCategory\"},\"categoryId\":{\"type\":\"string\"},\"name\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"categoryId\",\"name\"]}","aggregateIdField":"categoryId","mutationField":"Catalog_RenameCategory","references":[],"level":"Instance","apiExposed":true,"name":"RenameCategory","allowedStates":null}],"events":[{"name":"CategoryRenamed","schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"CategoryRenamed\"},\"categoryId\":{\"type\":\"string\"},\"name\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"categoryId\",\"name\"]}","references":[]}],"consumedEventTypes":["Catalog.CategoryAdded","Catalog.CategoryRenamed"]}],"extensionPoints":[{"name":"Catalog.Products","sourceEventTypes":["Catalog.ProductAdded","Catalog.ProductPriceChanged"],"delegateNames":["CatalogDcbEventLog"],"commandTypes":[]}],"readModels":[],"stateViewSlices":[{"schema":"{\"type\":\"object\",\"properties\":{\"categoryId\":{\"type\":\"string\",\"format\":\"uuid\"},\"name\":{\"type\":\"string\"},\"archived\":{\"type\":\"boolean\"}},\"required\":[\"categoryId\",\"name\",\"archived\"]}","searchableFields":["name"],"chapter":"Category","linkedWriteSide":["AddCategory","ArchiveCategory","RenameCategory"],"statusField":null,"visibility":null,"queryField":"Catalog_Categories","name":"Categories","consumedEventTypes":["Catalog.CategoryAdded","Catalog.CategoryRenamed","Catalog.CategoryArchived"],"labelField":"name"},{"schema":"{\"type\":\"object\",\"properties\":{\"productId\":{\"type\":\"string\",\"format\":\"uuid\"},\"name\":{\"type\":\"string\"},\"categoryId\":{\"type\":\"string\",\"format\":\"uuid\"},\"orderCount\":{\"type\":\"number\"}},\"required\":[\"productId\",\"name\",\"categoryId\",\"orderCount\"]}","searchableFields":["name"],"chapter":"Product","linkedWriteSide":["AddProduct","RecordProductDemand"],"statusField":null,"visibility":null,"queryField":"Catalog_ProductDemands","name":"ProductDemand","consumedEventTypes":["Catalog.ProductAdded","Catalog.ProductDemandRecorded","Catalog.ProductDemandRevoked"],"labelField":"name"},{"schema":"{\"type\":\"object\",\"properties\":{\"productId\":{\"type\":\"string\",\"format\":\"uuid\"},\"name\":{\"type\":\"string\"},\"description\":{\"type\":\"string\"},\"price\":{\"type\":\"number\"},\"categoryId\":{\"type\":\"string\",\"format\":\"uuid\"}},\"required\":[\"productId\",\"name\",\"description\",\"price\",\"categoryId\"]}","searchableFields":["name"],"chapter":"Product","linkedWriteSide":["AddProduct","ChangeProductDescription","ChangeProductName","ChangeProductPrice"],"statusField":null,"visibility":null,"queryField":"Catalog_Products","name":"Products","consumedEventTypes":["Catalog.ProductAdded","Catalog.ProductNameChanged","Catalog.ProductDescriptionChanged","Catalog.ProductPriceChanged"],"labelField":"name"}],"aggregates":[],"inboundTranslationSlices":[{"name":"ImportProduct","chapter":"Product","targetName":"AddProduct","commandTypes":["Catalog.AddProduct"],"externalSystem":"SupplierFeed"}],"automationSlices":[]}}}}
1
+ {"event":"VersionConnected","data":{"_0":{"dcbEventLog":{"name":"CatalogDcbEventLog","eventTopicArn":"arn:aws:dynamodb:eu-west-1:000000000000:table/CatalogDcbEventLog-925103d/stream/2026-07-14T08:45:33.852"},"extensions":[{"name":"Ordering.Orders.Catalog","extensionPointName":"Ordering.Orders","dcbSources":[]}],"extensionProtocols":[],"extensionPoints":[{"name":"Catalog.Products","commandTopic":"https://sqs.eu-west-1.amazonaws.com/000000000000/CatalogProductsExtPointCmdTopic-1dd983b","eventTopic":"arn:aws:sns:eu-west-1:000000000000:CatalogProductsExtPointEventTopic-5cb0b5e"}],"kind":"Domain","name":"Catalog","apiTarget":"Domain","id":"Catalog@1.0.0-alpha.149","version":"1.0.0-alpha.149","apiSchemaFragment":{"protocol":"graphql","encoded":"{\"types\":[\"type Catalog_Category implements Node {\\n id: ID!\\n categoryId: ID!\\n name: String!\\n archived: Boolean!\\n}\",\"type Catalog_CategoryEdge {\\n node: Catalog_Category!\\n cursor: String!\\n}\",\"type Catalog_CategoryConnection {\\n edges: [Catalog_CategoryEdge!]!\\n pageInfo: PageInfo!\\n}\",\"input Catalog_CategoryFilter {\\n search: String\\n searchPrefix: String\\n ids: [ID!]\\n}\",\"type Catalog_ProductDemand implements Node {\\n id: ID!\\n productId: ID!\\n name: String!\\n categoryId: ID!\\n orderCount: Float!\\n}\",\"type Catalog_ProductDemandEdge {\\n node: Catalog_ProductDemand!\\n cursor: String!\\n}\",\"type Catalog_ProductDemandConnection {\\n edges: [Catalog_ProductDemandEdge!]!\\n pageInfo: PageInfo!\\n}\",\"input Catalog_ProductDemandFilter {\\n search: String\\n searchPrefix: String\\n ids: [ID!]\\n}\",\"type Catalog_Product implements Node {\\n id: ID!\\n productId: ID!\\n name: String!\\n description: String!\\n price: Float!\\n categoryId: ID!\\n}\",\"type Catalog_ProductEdge {\\n node: Catalog_Product!\\n cursor: String!\\n}\",\"type Catalog_ProductConnection {\\n edges: [Catalog_ProductEdge!]!\\n pageInfo: PageInfo!\\n}\",\"input Catalog_ProductFilter {\\n search: String\\n searchPrefix: String\\n ids: [ID!]\\n}\",\"enum Catalog_ImportProductAuditStatus {\\n Success\\n Failure\\n}\",\"type Catalog_ImportProductAudit implements Node {\\n id: ID!\\n input: String!\\n status: Catalog_ImportProductAuditStatus!\\n targetIds: [ID!]\\n commandCount: Float\\n error: String\\n receivedAt: String!\\n}\",\"type Catalog_ImportProductAuditEdge {\\n node: Catalog_ImportProductAudit!\\n cursor: String!\\n}\",\"type Catalog_ImportProductAuditConnection {\\n edges: [Catalog_ImportProductAuditEdge!]!\\n pageInfo: PageInfo!\\n}\",\"input Catalog_ImportProductAuditFilter {\\n search: String\\n searchPrefix: String\\n ids: [ID!]\\n}\",\"union CommandResult = CommandAccepted | CommandRejected | CommandPending\",\"type CommandAccepted {\\n msgId: ID!\\n entityId: ID\\n eventCount: Int!\\n}\",\"type CommandRejected {\\n msgId: ID!\\n errorCode: String!\\n errorDetail: String\\n}\",\"type CommandPending {\\n msgId: ID!\\n}\",\"type CatalogEventLogEvent {\\n position: String!\\n eventType: String!\\n payload: AWSJSON!\\n}\"],\"mutations\":[\" Catalog_AddCategory(categoryId: ID!, name: String!): CommandResult!\",\" Catalog_AddProduct(productId: ID!, name: String!, description: String!, price: Float!, categoryId: ID!): CommandResult!\",\" Catalog_ArchiveCategory(categoryId: ID!): CommandResult!\\n @aws_auth(cognito_groups: [\\\"Admin\\\"])\",\" Catalog_ChangeProductDescription(productId: ID!, description: String!): CommandResult!\",\" Catalog_ChangeProductName(productId: ID!, name: String!): CommandResult!\",\" Catalog_ChangeProductPrice(productId: ID!, price: Float!): CommandResult!\",\" Catalog_RenameCategory(categoryId: ID!, name: String!): CommandResult!\",\" Catalog_ImportProduct(sku: String!, title: String!, desc: String!, unitPrice: Float!, currency: String!, category: String!): CommandResult!\"],\"queries\":[\" Catalog_Category(id: ID!): Catalog_Category\",\" Catalog_CategoriesByIds(ids: [String!]!): [Catalog_Category!]!\",\" Catalog_Categories(filter: Catalog_CategoryFilter, first: Int, after: String, last: Int, before: String): Catalog_CategoryConnection!\",\" Catalog_ProductDemand(id: ID!): Catalog_ProductDemand\",\" Catalog_ProductDemandsByIds(ids: [String!]!): [Catalog_ProductDemand!]!\",\" Catalog_ProductDemands(filter: Catalog_ProductDemandFilter, first: Int, after: String, last: Int, before: String): Catalog_ProductDemandConnection!\",\" Catalog_Product(id: ID!): Catalog_Product\",\" Catalog_ProductsByIds(ids: [String!]!): [Catalog_Product!]!\",\" Catalog_Products(filter: Catalog_ProductFilter, first: Int, after: String, last: Int, before: String): Catalog_ProductConnection!\",\" Catalog_ImportProductAudit(id: ID!): Catalog_ImportProductAudit\",\" Catalog_ImportProductAuditsByIds(ids: [String!]!): [Catalog_ImportProductAudit!]!\",\" Catalog_ImportProductAudits(filter: Catalog_ImportProductAuditFilter, first: Int, after: String, last: Int, before: String): Catalog_ImportProductAuditConnection!\"],\"subscriptions\":[\" onCatalog_AddCategory(id: ID): CommandResult\",\" onCatalog_AddProduct(id: ID): CommandResult\",\" onCatalog_ArchiveCategory(id: ID): CommandResult\",\" onCatalog_ChangeProductDescription(id: ID): CommandResult\",\" onCatalog_ChangeProductName(id: ID): CommandResult\",\" onCatalog_ChangeProductPrice(id: ID): CommandResult\",\" onCatalog_RenameCategory(id: ID): CommandResult\",\" onCatalog_ImportProduct(id: ID): CommandResult\",\" onCatalogEventLog_eventAppended: CatalogEventLogEvent\"],\"subscriptionSources\":[{\"field\":\"onCatalog_AddCategory\",\"mutations\":[\"Catalog_AddCategory\"]},{\"field\":\"onCatalog_AddProduct\",\"mutations\":[\"Catalog_AddProduct\"]},{\"field\":\"onCatalog_ArchiveCategory\",\"mutations\":[\"Catalog_ArchiveCategory\"]},{\"field\":\"onCatalog_ChangeProductDescription\",\"mutations\":[\"Catalog_ChangeProductDescription\"]},{\"field\":\"onCatalog_ChangeProductName\",\"mutations\":[\"Catalog_ChangeProductName\"]},{\"field\":\"onCatalog_ChangeProductPrice\",\"mutations\":[\"Catalog_ChangeProductPrice\"]},{\"field\":\"onCatalog_RenameCategory\",\"mutations\":[\"Catalog_RenameCategory\"]},{\"field\":\"onCatalog_ImportProduct\",\"mutations\":[\"Catalog_ImportProduct\"]}]}"},"eventCollector":"arn:aws:sqs:eu-west-1:000000000000:CatalogPluginEventColl-fc85d05","structure":{"outboundTranslationSlices":[],"extensions":[{"name":"Ordering.Orders","delegateNames":["RecordProductDemand"],"eventTypes":["Ordering.Orders.ItemOrdered","Ordering.Orders.ItemOrderCancelled"],"commandTypes":[]}],"stateChangeSlices":[{"chapter":"Category","linkedViews":["Categories"],"producedEventTypes":["Catalog.CategoryAdded"],"name":"AddCategory","commands":[{"schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"AddCategory\"},\"categoryId\":{\"type\":\"string\"},\"name\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"categoryId\",\"name\"]}","aggregateIdField":"categoryId","mutationField":"Catalog_AddCategory","references":[],"level":"Collection","apiExposed":true,"name":"AddCategory"}],"events":[{"name":"CategoryAdded","schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"CategoryAdded\"},\"categoryId\":{\"type\":\"string\"},\"name\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"categoryId\",\"name\"]}","references":[]}],"consumedEventTypes":[]},{"consistencyRead":"Categories","chapter":"Product","linkedViews":["ProductDemand","Products"],"producedEventTypes":["Catalog.ProductAdded"],"name":"AddProduct","commands":[{"schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"AddProduct\"},\"productId\":{\"type\":\"string\"},\"name\":{\"type\":\"string\"},\"description\":{\"type\":\"string\"},\"price\":{\"type\":\"number\"},\"categoryId\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"name\",\"description\",\"price\",\"categoryId\"]}","aggregateIdField":"productId","mutationField":"Catalog_AddProduct","references":[{"entity":"Categories","fieldName":"categoryId"}],"level":"Collection","apiExposed":true,"name":"AddProduct"}],"events":[{"name":"ProductAdded","schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ProductAdded\"},\"productId\":{\"type\":\"string\"},\"name\":{\"type\":\"string\"},\"description\":{\"type\":\"string\"},\"price\":{\"type\":\"number\"},\"categoryId\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"name\",\"description\",\"price\",\"categoryId\"]}","references":[]}],"consumedEventTypes":["Catalog.ProductAdded","Catalog.CategoryAdded","Catalog.CategoryArchived"]},{"chapter":"Category","linkedViews":["Categories"],"producedEventTypes":["Catalog.CategoryArchived"],"name":"ArchiveCategory","commands":[{"schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ArchiveCategory\"},\"categoryId\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"categoryId\"]}","aggregateIdField":"categoryId","mutationField":"Catalog_ArchiveCategory","references":[],"level":"Instance","apiExposed":true,"name":"ArchiveCategory"}],"events":[{"name":"CategoryArchived","schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"CategoryArchived\"},\"categoryId\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"categoryId\"]}","references":[]}],"consumedEventTypes":[]},{"consistencyRead":"Products","chapter":"Product","linkedViews":["Products"],"producedEventTypes":["Catalog.ProductDescriptionChanged"],"name":"ChangeProductDescription","commands":[{"schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ChangeProductDescription\"},\"productId\":{\"type\":\"string\"},\"description\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"description\"]}","aggregateIdField":"productId","mutationField":"Catalog_ChangeProductDescription","references":[],"level":"Instance","apiExposed":true,"name":"ChangeProductDescription"}],"events":[{"name":"ProductDescriptionChanged","schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ProductDescriptionChanged\"},\"productId\":{\"type\":\"string\"},\"description\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"description\"]}","references":[]}],"consumedEventTypes":["Catalog.ProductAdded","Catalog.ProductDescriptionChanged"]},{"consistencyRead":"Products","chapter":"Product","linkedViews":["Products"],"producedEventTypes":["Catalog.ProductNameChanged"],"name":"ChangeProductName","commands":[{"schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ChangeProductName\"},\"productId\":{\"type\":\"string\"},\"name\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"name\"]}","aggregateIdField":"productId","mutationField":"Catalog_ChangeProductName","references":[],"level":"Instance","apiExposed":true,"name":"ChangeProductName"}],"events":[{"name":"ProductNameChanged","schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ProductNameChanged\"},\"productId\":{\"type\":\"string\"},\"name\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"name\"]}","references":[]}],"consumedEventTypes":["Catalog.ProductAdded","Catalog.ProductNameChanged"]},{"consistencyRead":"Products","chapter":"Product","linkedViews":["Products"],"producedEventTypes":["Catalog.ProductPriceChanged"],"name":"ChangeProductPrice","commands":[{"schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ChangeProductPrice\"},\"productId\":{\"type\":\"string\"},\"price\":{\"type\":\"number\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"price\"]}","aggregateIdField":"productId","mutationField":"Catalog_ChangeProductPrice","references":[],"level":"Instance","apiExposed":true,"name":"ChangeProductPrice"}],"events":[{"name":"ProductPriceChanged","schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ProductPriceChanged\"},\"productId\":{\"type\":\"string\"},\"price\":{\"type\":\"number\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"price\"]}","references":[]}],"consumedEventTypes":["Catalog.ProductAdded","Catalog.ProductPriceChanged"]},{"consistencyRead":"ProductDemand","chapter":"ProductDemand","linkedViews":["ProductDemand"],"producedEventTypes":["Catalog.ProductDemandRecorded","Catalog.ProductDemandRevoked"],"name":"RecordProductDemand","commands":[{"schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"RecordDemand\"},\"productId\":{\"type\":\"string\"},\"orderId\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"orderId\"]}","aggregateIdField":"productId","mutationField":"Catalog_RecordDemand","references":[],"level":"Instance","apiExposed":false,"name":"RecordDemand"},{"schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"RevokeDemand\"},\"productId\":{\"type\":\"string\"},\"orderId\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"orderId\"]}","aggregateIdField":"productId","mutationField":"Catalog_RevokeDemand","references":[],"level":"Instance","apiExposed":false,"name":"RevokeDemand"}],"events":[{"name":"ProductDemandRecorded","schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ProductDemandRecorded\"},\"productId\":{\"type\":\"string\"},\"orderId\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"orderId\"]}","references":[]},{"name":"ProductDemandRevoked","schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"ProductDemandRevoked\"},\"productId\":{\"type\":\"string\"},\"orderId\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"productId\",\"orderId\"]}","references":[]}],"consumedEventTypes":["Catalog.ProductDemandRecorded","Catalog.ProductDemandRevoked"]},{"consistencyRead":"Categories","chapter":"Category","linkedViews":["Categories"],"producedEventTypes":["Catalog.CategoryRenamed"],"name":"RenameCategory","commands":[{"schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"RenameCategory\"},\"categoryId\":{\"type\":\"string\"},\"name\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"categoryId\",\"name\"]}","aggregateIdField":"categoryId","mutationField":"Catalog_RenameCategory","references":[],"level":"Instance","apiExposed":true,"name":"RenameCategory"}],"events":[{"name":"CategoryRenamed","schema":"{\"type\":\"object\",\"properties\":{\"TAG\":{\"type\":\"string\",\"const\":\"CategoryRenamed\"},\"categoryId\":{\"type\":\"string\"},\"name\":{\"type\":\"string\"}},\"additionalProperties\":true,\"required\":[\"TAG\",\"categoryId\",\"name\"]}","references":[]}],"consumedEventTypes":["Catalog.CategoryAdded","Catalog.CategoryRenamed"]}],"extensionPoints":[{"name":"Catalog.Products","sourceEventTypes":["Catalog.ProductAdded","Catalog.ProductPriceChanged"],"delegateNames":["CatalogDcbEventLog"],"commandTypes":[]}],"readModels":[],"stateViewSlices":[{"schema":"{\"type\":\"object\",\"properties\":{\"categoryId\":{\"type\":\"string\",\"format\":\"uuid\"},\"name\":{\"type\":\"string\"},\"archived\":{\"type\":\"boolean\"}},\"required\":[\"categoryId\",\"name\",\"archived\"]}","searchableFields":["name"],"chapter":"Category","linkedWriteSide":["AddCategory","ArchiveCategory","RenameCategory"],"queryField":"Catalog_Categories","name":"Categories","consumedEventTypes":["Catalog.CategoryAdded","Catalog.CategoryRenamed","Catalog.CategoryArchived"],"labelField":"name"},{"schema":"{\"type\":\"object\",\"properties\":{\"productId\":{\"type\":\"string\",\"format\":\"uuid\"},\"name\":{\"type\":\"string\"},\"categoryId\":{\"type\":\"string\",\"format\":\"uuid\"},\"orderCount\":{\"type\":\"number\"}},\"required\":[\"productId\",\"name\",\"categoryId\",\"orderCount\"]}","searchableFields":["name"],"chapter":"Product","linkedWriteSide":["AddProduct","RecordProductDemand"],"queryField":"Catalog_ProductDemands","name":"ProductDemand","consumedEventTypes":["Catalog.ProductAdded","Catalog.ProductDemandRecorded","Catalog.ProductDemandRevoked"],"labelField":"name"},{"schema":"{\"type\":\"object\",\"properties\":{\"productId\":{\"type\":\"string\",\"format\":\"uuid\"},\"name\":{\"type\":\"string\"},\"description\":{\"type\":\"string\"},\"price\":{\"type\":\"number\"},\"categoryId\":{\"type\":\"string\",\"format\":\"uuid\"}},\"required\":[\"productId\",\"name\",\"description\",\"price\",\"categoryId\"]}","searchableFields":["name"],"chapter":"Product","linkedWriteSide":["AddProduct","ChangeProductDescription","ChangeProductName","ChangeProductPrice"],"queryField":"Catalog_Products","name":"Products","consumedEventTypes":["Catalog.ProductAdded","Catalog.ProductNameChanged","Catalog.ProductDescriptionChanged","Catalog.ProductPriceChanged"],"labelField":"name"}],"aggregates":[],"inboundTranslationSlices":[{"name":"ImportProduct","chapter":"Product","targetName":"AddProduct","commandTypes":["Catalog.AddProduct"],"externalSystem":"SupplierFeed"}],"automationSlices":[]}}}}