@reventlessdev/reventless-core 3.0.0-alpha.267 → 3.0.0-alpha.268

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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,13 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 3.0.0-alpha.268 (2026-09-21)
7
+
8
+ ### Bug Fixes
9
+
10
+ * **core:** a creating command's own id derives no reference ([58a921c](https://github.com/ReventlessDev/reventless-core/commit/58a921c84352f6522e7c175387927e32365aa94a))
11
+
12
+
6
13
  # 3.0.0-alpha.267 (2026-09-21)
7
14
 
8
15
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-core",
3
- "version": "3.0.0-alpha.267",
3
+ "version": "3.0.0-alpha.268",
4
4
  "description": "Core package for Reventless framework",
5
5
  "license": "Apache-2.0",
6
6
  "jest": {
@@ -29,15 +29,15 @@
29
29
  "sury": "11.0.0-rc.2",
30
30
  "uuid": "^13.0.0",
31
31
  "@reventlessdev/rescript-effect": "0.1.0-alpha.33",
32
- "@reventlessdev/rescript-fast-csv": "2.0.0-alpha.12",
33
32
  "@reventlessdev/rescript-hash-object": "1.2.0-alpha.14",
33
+ "@reventlessdev/rescript-fast-csv": "2.0.0-alpha.12",
34
34
  "@reventlessdev/rescript-jest": "1.0.0-alpha.10",
35
35
  "@reventlessdev/rescript-node": "2.0.0-alpha.12",
36
36
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.21",
37
37
  "@reventlessdev/rescript-ssh2": "2.0.0-alpha.12",
38
38
  "@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
39
- "@reventlessdev/reventless-infra": "3.0.0-alpha.168",
40
39
  "@reventlessdev/reventless-interop": "3.0.0-alpha.37",
40
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.168",
41
41
  "@reventlessdev/reventless-spec": "3.0.0-alpha.140"
42
42
  },
43
43
  "devDependencies": {
@@ -648,6 +648,9 @@ let identityReference = (
648
648
  // element schema.
649
649
  let extractReferences = (
650
650
  ~identityViews: option<identityViews>=?,
651
+ // The id a creating command mints. Its identity names the row the command
652
+ // brings into existence, not one it refers to, so nothing is derived for it.
653
+ ~minted: option<string>=?,
651
654
  properties: dict<S.t<unknown>>,
652
655
  ): array<Reventless.Plugin.fieldReference> =>
653
656
  properties
@@ -660,8 +663,9 @@ let extractReferences = (
660
663
  // nesting and the other not.
661
664
  let declared = Reventless.Reference.collectFieldTargets(fieldName, fieldSchema)
662
665
  let targets = switch identityViews {
663
- | Some(views) => identityReference(views, ~fieldName, fieldSchema, declared)
664
- | None => declared
666
+ | Some(views) if minted != Some(fieldName) =>
667
+ identityReference(views, ~fieldName, fieldSchema, declared)
668
+ | _ => declared
665
669
  }
666
670
  targets->Array.map(((path, target)): Reventless.Plugin.fieldReference => {
667
671
  Reventless.Plugin.fieldName: path,
@@ -849,7 +853,6 @@ let toCommandDef = (
849
853
  ~variantName,
850
854
  properties,
851
855
  )
852
- let references = extractReferences(~identityViews?, properties)
853
856
  // Evaluated against a synthetic value per constructor, the same shape the
854
857
  // resolver builds at call time: a payload-bearing variant compiles to
855
858
  // `{TAG, ...}`, a payload-less one to a bare string.
@@ -913,6 +916,11 @@ let toCommandDef = (
913
916
  | None => Reventless.Transition.targetState(declared)
914
917
  }
915
918
  let level = derived->Option.flatMap(d => d.level)->Option.getOr(guessedLevel)
919
+ let references = extractReferences(
920
+ ~identityViews?,
921
+ ~minted=?level == Reventless.Plugin.Collection ? aggregateIdField : None,
922
+ properties,
923
+ )
916
924
  // API-exposed iff the whole command isn't @noApi and this variant
917
925
  // isn't in its @noApi-variants set — mirrors the API-generation filter
918
926
  // (Plugin_Helpers / PluginBaseFragment). Drives the event-graph API badge.
@@ -481,12 +481,12 @@ function identityReference(identityViews, fieldName, fieldSchema, declared) {
481
481
  ]];
482
482
  }
483
483
 
484
- function extractReferences(identityViews, properties) {
484
+ function extractReferences(identityViews, minted, properties) {
485
485
  return Object.entries(properties).flatMap(param => {
486
486
  let fieldSchema = param[1];
487
487
  let fieldName = param[0];
488
488
  let declared = Reference$Reventless.collectFieldTargets(fieldName, fieldSchema);
489
- let targets = identityViews !== undefined ? identityReference(identityViews, fieldName, fieldSchema, declared) : declared;
489
+ let targets = identityViews !== undefined && Primitive_object.notequal(minted, fieldName) ? identityReference(identityViews, fieldName, fieldSchema, declared) : declared;
490
490
  return targets.map(param => {
491
491
  let target = param[1];
492
492
  return {
@@ -500,7 +500,7 @@ function extractReferences(identityViews, properties) {
500
500
 
501
501
  function toEventDef(identityViews, v) {
502
502
  let mkDef = (variantName, properties) => {
503
- let references = extractReferences(identityViews, properties);
503
+ let references = extractReferences(identityViews, undefined, properties);
504
504
  return {
505
505
  name: variantName,
506
506
  schema: JSON.stringify(SuryToJsonSchema$ReventlessCore.deriveObjectSchema(undefined, undefined, v)),
@@ -643,7 +643,7 @@ function annotateArgTypes(schema, argTypes) {
643
643
  function toCommandDef(identityViews, isAggregate, partitionKey, mutationFieldFor, parentSchema, commandAuthorization, commandTransition, derivedEdgeFor, v) {
644
644
  let mkDef = (variantName, properties) => {
645
645
  let match = commandLevelAndId(isAggregate, partitionKey, variantName, properties);
646
- let references = extractReferences(identityViews, properties);
646
+ let aggregateIdField = match[1];
647
647
  let syntheticCommand = DcbTag$Reventless.isVariantPayloadBearing(parentSchema, variantName) ? ({
648
648
  TAG: variantName
649
649
  }) : variantName;
@@ -678,6 +678,7 @@ function toCommandDef(identityViews, isAggregate, partitionKey, mutationFieldFor
678
678
  });
679
679
  let targetState = observed !== undefined ? observed : Transition$Reventless.targetState(declared);
680
680
  let level = Stdlib_Option.getOr(Stdlib_Option.flatMap(derived, d => d.level), match[0]);
681
+ let references = extractReferences(identityViews, level === "Collection" ? aggregateIdField : undefined, properties);
681
682
  let apiExposed = false;
682
683
  if (!ApiNoApiHelpers$ReventlessCore.isNoApi(parentSchema)) {
683
684
  let excluded = ApiNoApiHelpers$ReventlessCore.getExcludedVariants(parentSchema);
@@ -691,7 +692,7 @@ function toCommandDef(identityViews, isAggregate, partitionKey, mutationFieldFor
691
692
  name: variantName,
692
693
  schema: JSON.stringify(annotatedSchema),
693
694
  level: level,
694
- aggregateIdField: match[1],
695
+ aggregateIdField: aggregateIdField,
695
696
  mutationField: mutationField,
696
697
  references: references,
697
698
  allowedStates: match$3[0],
@@ -11,7 +11,7 @@ module CustomerId = Reventless.Id.Make({
11
11
  let key = "customerId"
12
12
  })
13
13
 
14
- let refsOf = (~viewsByKey, schema: S.t<'a>) => {
14
+ let refsOf = (~viewsByKey, ~minted=?, schema: S.t<'a>) => {
15
15
  let reports = []
16
16
  let identityViews: Plugin_Structure.identityViews = {
17
17
  viewsByKey: Dict.fromArray(viewsByKey),
@@ -23,7 +23,7 @@ let refsOf = (~viewsByKey, schema: S.t<'a>) => {
23
23
  | _ => Dict.make()
24
24
  }
25
25
  let refs =
26
- Plugin_Structure.extractReferences(~identityViews, properties)->Array.map(r => (
26
+ Plugin_Structure.extractReferences(~identityViews, ~minted?, properties)->Array.map(r => (
27
27
  r.fieldName,
28
28
  r.entity,
29
29
  ))
@@ -63,6 +63,17 @@ describe("a reference derived from the type", () => {
63
63
  expect(reports->Array.some(r => r->String.includes("no view in this plugin")))->toBe(true)
64
64
  })
65
65
 
66
+ // A create form mints this id; a list of the rows that already exist is the
67
+ // wrong control for it.
68
+ testSync("the id a creating command mints references nothing", () => {
69
+ let (refs, reports) = refsOf(
70
+ ~viewsByKey=[("customerId", ["Customers"]), ("productId", ["AvailableProducts"])],
71
+ ~minted="buyer",
72
+ order,
73
+ )
74
+ expect((refs, reports))->toEqual(([("productIds", "AvailableProducts")], []))
75
+ })
76
+
66
77
  testSync("an untyped field is left to its name, as before", () => {
67
78
  let (refs, reports) = refsOf(
68
79
  ~viewsByKey=[("customerId", ["Customers"])],
@@ -96,4 +107,12 @@ describe("a declared @ref on a typed id", () => {
96
107
  )
97
108
  expect((refs, reports))->toEqual(([("buyer", "Customers")], []))
98
109
  })
110
+
111
+ testSync("stands on a minted id: only the derived one steps aside", () => {
112
+ let chosen = S.schema(
113
+ s => {"buyer": s.matches(CustomerId.schema->Reventless.Reference.mark("Customers"))},
114
+ )
115
+ let (refs, _) = refsOf(~viewsByKey=[("customerId", ["Customers"])], ~minted="buyer", chosen)
116
+ expect(refs)->toEqual([("buyer", "Customers")])
117
+ })
99
118
  })
@@ -13,7 +13,7 @@ let CustomerId = Id$Reventless.Make({
13
13
  key: "customerId"
14
14
  });
15
15
 
16
- function refsOf(viewsByKey, schema) {
16
+ function refsOf(viewsByKey, minted, schema) {
17
17
  let reports = [];
18
18
  let identityViews_viewsByKey = Object.fromEntries(viewsByKey);
19
19
  let identityViews_viewNames = viewsByKey.flatMap(param => param[1]);
@@ -27,7 +27,7 @@ function refsOf(viewsByKey, schema) {
27
27
  };
28
28
  let properties;
29
29
  properties = schema.type === "object" ? schema.properties : ({});
30
- let refs = Plugin_Structure$ReventlessCore.extractReferences(identityViews, properties).map(r => [
30
+ let refs = Plugin_Structure$ReventlessCore.extractReferences(identityViews, minted, properties).map(r => [
31
31
  r.fieldName,
32
32
  r.entity
33
33
  ]);
@@ -54,7 +54,7 @@ globalThis.describe("a reference derived from the type", () => {
54
54
  "productId",
55
55
  ["AvailableProducts"]
56
56
  ]
57
- ], order);
57
+ ], undefined, order);
58
58
  globalThis.expect(match[0]).toEqual([
59
59
  [
60
60
  "buyer",
@@ -80,7 +80,7 @@ globalThis.describe("a reference derived from the type", () => {
80
80
  "ProductDemand"
81
81
  ]
82
82
  ]
83
- ], order);
83
+ ], undefined, order);
84
84
  globalThis.expect(match[0]).toEqual([[
85
85
  "buyer",
86
86
  "Customers"
@@ -91,18 +91,40 @@ globalThis.describe("a reference derived from the type", () => {
91
91
  let match = refsOf([[
92
92
  "productId",
93
93
  ["AvailableProducts"]
94
- ]], order);
94
+ ]], undefined, order);
95
95
  globalThis.expect(match[0]).toEqual([[
96
96
  "productIds",
97
97
  "AvailableProducts"
98
98
  ]]);
99
99
  globalThis.expect(match[1].some(r => r.includes("no view in this plugin"))).toBe(true);
100
100
  });
101
+ globalThis.test("the id a creating command mints references nothing", () => {
102
+ let match = refsOf([
103
+ [
104
+ "customerId",
105
+ ["Customers"]
106
+ ],
107
+ [
108
+ "productId",
109
+ ["AvailableProducts"]
110
+ ]
111
+ ], "buyer", order);
112
+ globalThis.expect([
113
+ match[0],
114
+ match[1]
115
+ ]).toEqual([
116
+ [[
117
+ "productIds",
118
+ "AvailableProducts"
119
+ ]],
120
+ []
121
+ ]);
122
+ });
101
123
  globalThis.test("an untyped field is left to its name, as before", () => {
102
124
  let match = refsOf([[
103
125
  "customerId",
104
126
  ["Customers"]
105
- ]], Sury.$schema(s => ({
127
+ ]], undefined, Sury.$schema(s => ({
106
128
  customerId: s.m(Sury.string)
107
129
  })));
108
130
  globalThis.expect([
@@ -129,7 +151,7 @@ globalThis.describe("a declared @ref on a typed id", () => {
129
151
  "productId",
130
152
  ["Products"]
131
153
  ]
132
- ], referenced);
154
+ ], undefined, referenced);
133
155
  globalThis.expect(match[0]).toEqual([[
134
156
  "buyer",
135
157
  "Products"
@@ -146,7 +168,7 @@ globalThis.describe("a declared @ref on a typed id", () => {
146
168
  "Customers",
147
169
  "VipCustomers"
148
170
  ]
149
- ]], chosen);
171
+ ]], undefined, chosen);
150
172
  globalThis.expect([
151
173
  match[0],
152
174
  match[1]
@@ -158,6 +180,19 @@ globalThis.describe("a declared @ref on a typed id", () => {
158
180
  []
159
181
  ]);
160
182
  });
183
+ globalThis.test("stands on a minted id: only the derived one steps aside", () => {
184
+ let chosen = Sury.$schema(s => ({
185
+ buyer: s.m(Reference$Reventless.mark(CustomerId.schema, undefined, undefined, "Customers"))
186
+ }));
187
+ let match = refsOf([[
188
+ "customerId",
189
+ ["Customers"]
190
+ ]], "buyer", chosen);
191
+ globalThis.expect(match[0]).toEqual([[
192
+ "buyer",
193
+ "Customers"
194
+ ]]);
195
+ });
161
196
  });
162
197
 
163
198
  export {