@reventlessdev/reventless-core 3.0.0-alpha.278 → 3.0.0-alpha.279

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.279 (2026-09-27)
7
+
8
+ ### Features
9
+
10
+ * **core,spec:** an identity the plugin declares is information, not a dangling reference ([30d7917](https://github.com/ReventlessDev/reventless-core/commit/30d7917669ff062439f9c65e5274ab63a2e20de0))
11
+
12
+
6
13
  # 3.0.0-alpha.278 (2026-09-25)
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.278",
3
+ "version": "3.0.0-alpha.279",
4
4
  "description": "Core package for Reventless framework",
5
5
  "license": "Apache-2.0",
6
6
  "jest": {
@@ -29,16 +29,16 @@
29
29
  "sury": "11.0.0",
30
30
  "uuid": "^13.0.0",
31
31
  "@reventlessdev/rescript-effect": "0.1.0-alpha.34",
32
- "@reventlessdev/rescript-fast-csv": "2.0.0-alpha.15",
33
- "@reventlessdev/rescript-jest": "1.0.0-alpha.11",
34
32
  "@reventlessdev/rescript-hash-object": "1.2.0-alpha.15",
35
- "@reventlessdev/rescript-node": "2.0.0-alpha.15",
36
- "@reventlessdev/rescript-ssh2": "2.0.0-alpha.15",
33
+ "@reventlessdev/rescript-fast-csv": "2.0.0-alpha.16",
37
34
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.22",
35
+ "@reventlessdev/rescript-jest": "1.0.0-alpha.11",
36
+ "@reventlessdev/rescript-node": "2.0.0-alpha.16",
38
37
  "@reventlessdev/rescript-uuid": "2.0.0-alpha.1",
39
- "@reventlessdev/reventless-spec": "3.0.0-alpha.149",
40
- "@reventlessdev/reventless-infra": "3.0.0-alpha.177",
41
- "@reventlessdev/reventless-interop": "3.0.0-alpha.38"
38
+ "@reventlessdev/rescript-ssh2": "2.0.0-alpha.16",
39
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.178",
40
+ "@reventlessdev/reventless-interop": "3.0.0-alpha.38",
41
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.150"
42
42
  },
43
43
  "devDependencies": {
44
44
  "rescript": "12.3.1",
@@ -55,6 +55,7 @@ module type T = {
55
55
  ~extensionPoints: array<module(ReventlessInfra.ExtensionPointMapping.Mapping)>=?,
56
56
  ~componentChapters: dict<string>=?,
57
57
  ~lifecycleModel: array<Reventless.Plugin.derivedEdge>=?,
58
+ ~identities: array<string>=?,
58
59
  ) => Reventless.Plugin.pluginStructure
59
60
  }
60
61
 
@@ -1109,6 +1109,7 @@ module Make = (
1109
1109
  ~extensionPoints: array<module(ReventlessInfra.ExtensionPointMapping.Mapping)>=[],
1110
1110
  ~componentChapters: dict<string>=Dict.make(),
1111
1111
  ~lifecycleModel: array<Reventless.Plugin.derivedEdge>=[],
1112
+ ~identities: array<string>=[],
1112
1113
  ): Reventless.Plugin.pluginStructure =>
1113
1114
  Plugin_Structure.make(
1114
1115
  ~name,
@@ -1123,6 +1124,7 @@ module Make = (
1123
1124
  ~extensionPoints,
1124
1125
  ~componentChapters,
1125
1126
  ~lifecycleModel,
1127
+ ~identities,
1126
1128
  )
1127
1129
 
1128
1130
  let make = (
@@ -95,7 +95,7 @@ function Make(Spec) {
95
95
  pages: pages
96
96
  };
97
97
  };
98
- let makePluginDefinition = (name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChangeSlicesOpt, automationSlicesOpt, outboundTranslationSlicesOpt, inboundTranslationSlicesOpt, extensionsOpt, extensionPointsOpt, componentChaptersOpt, lifecycleModelOpt) => {
98
+ let makePluginDefinition = (name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChangeSlicesOpt, automationSlicesOpt, outboundTranslationSlicesOpt, inboundTranslationSlicesOpt, extensionsOpt, extensionPointsOpt, componentChaptersOpt, lifecycleModelOpt, identitiesOpt) => {
99
99
  let aggregates = aggregatesOpt !== undefined ? aggregatesOpt : [];
100
100
  let readModels = readModelsOpt !== undefined ? readModelsOpt : [];
101
101
  let stateViewSlices = stateViewSlicesOpt !== undefined ? stateViewSlicesOpt : [];
@@ -107,7 +107,8 @@ function Make(Spec) {
107
107
  let extensionPoints = extensionPointsOpt !== undefined ? extensionPointsOpt : [];
108
108
  let componentChapters = componentChaptersOpt !== undefined ? componentChaptersOpt : ({});
109
109
  let lifecycleModel = lifecycleModelOpt !== undefined ? lifecycleModelOpt : [];
110
- return Plugin_Structure$ReventlessCore.make(name, aggregates, readModels, stateViewSlices, stateChangeSlices, automationSlices, outboundTranslationSlices, inboundTranslationSlices, extensions, extensionPoints, componentChapters, lifecycleModel);
110
+ let identities = identitiesOpt !== undefined ? identitiesOpt : [];
111
+ return Plugin_Structure$ReventlessCore.make(name, aggregates, readModels, stateViewSlices, stateChangeSlices, automationSlices, outboundTranslationSlices, inboundTranslationSlices, extensions, extensionPoints, componentChapters, lifecycleModel, identities);
111
112
  };
112
113
  let make = (name, heartbeatInterval, extensionPointsOpt, extensionsOpt, aggregatesOpt, readModelsOpt, tasksOpt, stateChangeSlicesOpt, stateViewSlicesOpt, automationSlicesOpt, outboundTranslationSlicesOpt, inboundTranslationSlicesOpt, systemCallableComponentsOpt, componentRuntimeOpt, uiFragments, pluginStructure, opts) => {
113
114
  let extensionPoints = extensionPointsOpt !== undefined ? extensionPointsOpt : [];
@@ -591,12 +591,15 @@ let labelFieldsFromStateSchema = (
591
591
  The plugin's views by the identity each is keyed by, which is what lets a typed
592
592
  id find the view that lists it without an `@ref`. `report` says where the type
593
593
  cannot decide: several views keyed by the identity, none, or an `@ref` naming a
594
- view keyed by another.
594
+ view keyed by another. `inform` takes the one case that is not a fault: an
595
+ identity in `ownIdentities`, declared by this plugin, that no view lists yet.
595
596
  */
596
597
  type identityViews = {
597
598
  viewsByKey: dict<array<string>>,
598
599
  viewNames: array<string>,
600
+ ownIdentities: array<string>,
599
601
  report: string => unit,
602
+ inform: string => unit,
600
603
  }
601
604
 
602
605
  // The reference a typed id implies, and the check on one it declares.
@@ -615,7 +618,11 @@ let identityReference = (
615
618
  switch keyed {
616
619
  | [view] => [(fieldName, {Reventless.Semantic.entity: view, plugin: None, identity: key})]
617
620
  | [] =>
618
- identityViews.report(
621
+ let say =
622
+ identityViews.ownIdentities->Array.includes(key)
623
+ ? identityViews.inform
624
+ : identityViews.report
625
+ say(
619
626
  `${fieldName} is a ${key}, and no view in this plugin is keyed by it, so it references nothing. List the identity in a view, or reference another plugin's with @ref("Plugin.View").`,
620
627
  )
621
628
  []
@@ -1110,6 +1117,9 @@ let make = (
1110
1117
  // What the plugin's own scenarios say about each command's lifecycle edge,
1111
1118
  // harvested by `check:lifecycle` and committed as `src/LifecycleModel.res`.
1112
1119
  ~lifecycleModel: array<Reventless.Plugin.derivedEdge>=[],
1120
+ // The keys of the identities the plugin declares in its own `src/`, passed by
1121
+ // the plugin generator. One no view lists is reported as information.
1122
+ ~identities: array<string>=[],
1113
1123
  ): Reventless.Plugin.pluginStructure => {
1114
1124
  let chapterOf = (compName: string): option<string> => componentChapters->Dict.get(compName)
1115
1125
  // The harvest compares its own reading of the corpus with the DECLARATION, so
@@ -1648,14 +1658,17 @@ let make = (
1648
1658
  // Warned rather than refused, like the key-field gap: the field still decodes,
1649
1659
  // it just links nowhere. Once per message, since a field recurs across variants.
1650
1660
  let reported: Set.t<string> = Set.make()
1661
+ let once = (logFn: Logger.logFn, message) =>
1662
+ if !(reported->Set.has(message)) {
1663
+ reported->Set.add(message)
1664
+ logFn(~comp="Plugin_Structure", `${name}: ${message}`)
1665
+ }
1651
1666
  let identityViews = {
1652
1667
  viewsByKey,
1653
1668
  viewNames: viewKeys->Array.map(((view, _)) => view),
1654
- report: message =>
1655
- if !(reported->Set.has(message)) {
1656
- reported->Set.add(message)
1657
- log.warn(~comp="Plugin_Structure", `${name}: ${message}`)
1658
- },
1669
+ ownIdentities: identities,
1670
+ report: message => once(log.warn, message),
1671
+ inform: message => once(log.info, message),
1659
1672
  }
1660
1673
 
1661
1674
  let stateChangeDefs =
@@ -465,10 +465,10 @@ function identityReference(identityViews, fieldName, fieldSchema, declared) {
465
465
  if (len !== 0) {
466
466
  identityViews.report(fieldName + ` is a ` + key + `, and ` + keyed.join(" and ") + ` are all keyed by it. Say which it references with @ref.`);
467
467
  return [];
468
- } else {
469
- identityViews.report(fieldName + ` is a ` + key + `, and no view in this plugin is keyed by it, so it references nothing. List the identity in a view, or reference another plugin's with @ref("Plugin.View").`);
470
- return [];
471
468
  }
469
+ let say = identityViews.ownIdentities.includes(key) ? identityViews.inform : identityViews.report;
470
+ say(fieldName + ` is a ` + key + `, and no view in this plugin is keyed by it, so it references nothing. List the identity in a view, or reference another plugin's with @ref("Plugin.View").`);
471
+ return [];
472
472
  }
473
473
  let view = keyed[0];
474
474
  return [[
@@ -774,7 +774,7 @@ function queryableDefFromSpec(plugin, name, stateSchema, authorization, visibili
774
774
  };
775
775
  }
776
776
 
777
- function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChangeSlicesOpt, automationSlicesOpt, outboundTranslationSlicesOpt, inboundTranslationSlicesOpt, extensionsOpt, extensionPointsOpt, componentChaptersOpt, lifecycleModelOpt) {
777
+ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChangeSlicesOpt, automationSlicesOpt, outboundTranslationSlicesOpt, inboundTranslationSlicesOpt, extensionsOpt, extensionPointsOpt, componentChaptersOpt, lifecycleModelOpt, identitiesOpt) {
778
778
  let aggregates = aggregatesOpt !== undefined ? aggregatesOpt : [];
779
779
  let readModels = readModelsOpt !== undefined ? readModelsOpt : [];
780
780
  let stateViewSlices = stateViewSlicesOpt !== undefined ? stateViewSlicesOpt : [];
@@ -786,6 +786,7 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
786
786
  let extensionPoints = extensionPointsOpt !== undefined ? extensionPointsOpt : [];
787
787
  let componentChapters = componentChaptersOpt !== undefined ? componentChaptersOpt : ({});
788
788
  let lifecycleModel = lifecycleModelOpt !== undefined ? lifecycleModelOpt : [];
789
+ let identities = identitiesOpt !== undefined ? identitiesOpt : [];
789
790
  let model = declaredTransitionsOnly ? [] : lifecycleModel;
790
791
  let derivedEdgeFor = (component, command) => model.find(e => {
791
792
  if (e.component === component) {
@@ -1173,17 +1174,21 @@ function make(name, aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChang
1173
1174
  });
1174
1175
  });
1175
1176
  let reported = new Set();
1176
- let identityViews_viewNames = viewKeys.map(param => param[0]);
1177
- let identityViews_report = message => {
1177
+ let once = (logFn, message) => {
1178
1178
  if (!reported.has(message)) {
1179
1179
  reported.add(message);
1180
- return log.warn("Plugin_Structure", undefined, name + `: ` + message);
1180
+ return logFn("Plugin_Structure", undefined, name + `: ` + message);
1181
1181
  }
1182
1182
  };
1183
+ let identityViews_viewNames = viewKeys.map(param => param[0]);
1184
+ let identityViews_report = message => once(log.warn, message);
1185
+ let identityViews_inform = message => once(log.info, message);
1183
1186
  let identityViews = {
1184
1187
  viewsByKey: viewsByKey,
1185
1188
  viewNames: identityViews_viewNames,
1186
- report: identityViews_report
1189
+ ownIdentities: identities,
1190
+ report: identityViews_report,
1191
+ inform: identityViews_inform
1187
1192
  };
1188
1193
  let stateChangeDefs = stateChangeSlices.map((SCS, i) => {
1189
1194
  let match = scsProduced[i];
@@ -11,12 +11,14 @@ module CustomerId = Reventless.Id.Make({
11
11
  let key = "customerId"
12
12
  })
13
13
 
14
- let refsOf = (~viewsByKey, ~minted=?, schema: S.t<'a>) => {
14
+ let refsOf = (~viewsByKey, ~ownIdentities=[], ~minted=?, schema: S.t<'a>) => {
15
15
  let reports = []
16
16
  let identityViews: Plugin_Structure.identityViews = {
17
17
  viewsByKey: Dict.fromArray(viewsByKey),
18
18
  viewNames: viewsByKey->Array.flatMap(((_, views)) => views),
19
+ ownIdentities,
19
20
  report: message => reports->Array.push(message),
21
+ inform: message => reports->Array.push(`info: ${message}`),
20
22
  }
21
23
  let properties = switch schema->S.castToUnknown {
22
24
  | Object({properties}) => properties
@@ -63,6 +65,27 @@ describe("a reference derived from the type", () => {
63
65
  expect(reports->Array.some(r => r->String.includes("no view in this plugin")))->toBe(true)
64
66
  })
65
67
 
68
+ // A site other parts name but no screen lists: declared on purpose, not a fault.
69
+ testSync("an identity the plugin declares, with no view, is information", () => {
70
+ let (_, reports) = refsOf(
71
+ ~viewsByKey=[("productId", ["AvailableProducts"])],
72
+ ~ownIdentities=["customerId"],
73
+ order,
74
+ )
75
+ expect(reports)->toEqual([
76
+ `info: buyer is a customerId, and no view in this plugin is keyed by it, so it references nothing. List the identity in a view, or reference another plugin's with @ref("Plugin.View").`,
77
+ ])
78
+ })
79
+
80
+ testSync("one it does not declare still warns", () => {
81
+ let (_, reports) = refsOf(
82
+ ~viewsByKey=[("productId", ["AvailableProducts"])],
83
+ ~ownIdentities=["orderId"],
84
+ order,
85
+ )
86
+ expect(reports->Array.some(r => r->String.startsWith("buyer is a customerId")))->toBe(true)
87
+ })
88
+
66
89
  // A create form mints this id; a list of the rows that already exist is the
67
90
  // wrong control for it.
68
91
  testSync("the id a creating command mints references nothing", () => {
@@ -13,17 +13,23 @@ let CustomerId = Id$Reventless.Make({
13
13
  key: "customerId"
14
14
  });
15
15
 
16
- function refsOf(viewsByKey, minted, schema) {
16
+ function refsOf(viewsByKey, ownIdentitiesOpt, minted, schema) {
17
+ let ownIdentities = ownIdentitiesOpt !== undefined ? ownIdentitiesOpt : [];
17
18
  let reports = [];
18
19
  let identityViews_viewsByKey = Object.fromEntries(viewsByKey);
19
20
  let identityViews_viewNames = viewsByKey.flatMap(param => param[1]);
20
21
  let identityViews_report = message => {
21
22
  reports.push(message);
22
23
  };
24
+ let identityViews_inform = message => {
25
+ reports.push(`info: ` + message);
26
+ };
23
27
  let identityViews = {
24
28
  viewsByKey: identityViews_viewsByKey,
25
29
  viewNames: identityViews_viewNames,
26
- report: identityViews_report
30
+ ownIdentities: ownIdentities,
31
+ report: identityViews_report,
32
+ inform: identityViews_inform
27
33
  };
28
34
  let properties;
29
35
  properties = schema.type === "object" ? schema.properties : ({});
@@ -54,7 +60,7 @@ globalThis.describe("a reference derived from the type", () => {
54
60
  "productId",
55
61
  ["AvailableProducts"]
56
62
  ]
57
- ], undefined, order);
63
+ ], undefined, undefined, order);
58
64
  globalThis.expect(match[0]).toEqual([
59
65
  [
60
66
  "buyer",
@@ -80,7 +86,7 @@ globalThis.describe("a reference derived from the type", () => {
80
86
  "ProductDemand"
81
87
  ]
82
88
  ]
83
- ], undefined, order);
89
+ ], undefined, undefined, order);
84
90
  globalThis.expect(match[0]).toEqual([[
85
91
  "buyer",
86
92
  "Customers"
@@ -91,13 +97,27 @@ globalThis.describe("a reference derived from the type", () => {
91
97
  let match = refsOf([[
92
98
  "productId",
93
99
  ["AvailableProducts"]
94
- ]], undefined, order);
100
+ ]], undefined, undefined, order);
95
101
  globalThis.expect(match[0]).toEqual([[
96
102
  "productIds",
97
103
  "AvailableProducts"
98
104
  ]]);
99
105
  globalThis.expect(match[1].some(r => r.includes("no view in this plugin"))).toBe(true);
100
106
  });
107
+ globalThis.test("an identity the plugin declares, with no view, is information", () => {
108
+ let match = refsOf([[
109
+ "productId",
110
+ ["AvailableProducts"]
111
+ ]], ["customerId"], undefined, order);
112
+ globalThis.expect(match[1]).toEqual([`info: buyer is a customerId, and no view in this plugin is keyed by it, so it references nothing. List the identity in a view, or reference another plugin's with @ref("Plugin.View").`]);
113
+ });
114
+ globalThis.test("one it does not declare still warns", () => {
115
+ let match = refsOf([[
116
+ "productId",
117
+ ["AvailableProducts"]
118
+ ]], ["orderId"], undefined, order);
119
+ globalThis.expect(match[1].some(r => r.startsWith("buyer is a customerId"))).toBe(true);
120
+ });
101
121
  globalThis.test("the id a creating command mints references nothing", () => {
102
122
  let match = refsOf([
103
123
  [
@@ -108,7 +128,7 @@ globalThis.describe("a reference derived from the type", () => {
108
128
  "productId",
109
129
  ["AvailableProducts"]
110
130
  ]
111
- ], "buyer", order);
131
+ ], undefined, "buyer", order);
112
132
  globalThis.expect([
113
133
  match[0],
114
134
  match[1]
@@ -124,7 +144,7 @@ globalThis.describe("a reference derived from the type", () => {
124
144
  let match = refsOf([[
125
145
  "customerId",
126
146
  ["Customers"]
127
- ]], undefined, Sury.$schema(s => ({
147
+ ]], undefined, undefined, Sury.$schema(s => ({
128
148
  customerId: s.m(Sury.string)
129
149
  })));
130
150
  globalThis.expect([
@@ -151,7 +171,7 @@ globalThis.describe("a declared @ref on a typed id", () => {
151
171
  "productId",
152
172
  ["Products"]
153
173
  ]
154
- ], undefined, referenced);
174
+ ], undefined, undefined, referenced);
155
175
  globalThis.expect(match[0]).toEqual([[
156
176
  "buyer",
157
177
  "Products"
@@ -168,7 +188,7 @@ globalThis.describe("a declared @ref on a typed id", () => {
168
188
  "Customers",
169
189
  "VipCustomers"
170
190
  ]
171
- ]], undefined, chosen);
191
+ ]], undefined, undefined, chosen);
172
192
  globalThis.expect([
173
193
  match[0],
174
194
  match[1]
@@ -187,7 +207,7 @@ globalThis.describe("a declared @ref on a typed id", () => {
187
207
  let match = refsOf([[
188
208
  "customerId",
189
209
  ["Customers"]
190
- ]], "buyer", chosen);
210
+ ]], undefined, "buyer", chosen);
191
211
  globalThis.expect(match[0]).toEqual([[
192
212
  "buyer",
193
213
  "Customers"
@@ -177,7 +177,7 @@ globalThis.describe("ReadModel visibility filter", () => {
177
177
  });
178
178
 
179
179
  globalThis.describe("Plugin_Structure.make — visibility tagging", () => {
180
- let structure = Plugin_Structure$ReventlessCore.make("VisibilityPlugin", undefined, readModels, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
180
+ let structure = Plugin_Structure$ReventlessCore.make("VisibilityPlugin", undefined, readModels, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
181
181
  globalThis.test("readModels carries both Public and Internal entries", () => {
182
182
  globalThis.expect(structure.readModels.length).toBe(2);
183
183
  });
@@ -108,7 +108,7 @@ let PsOrdersViewSlice = {
108
108
  let structure = Plugin_Structure$ReventlessCore.make("AccessPlugin", undefined, undefined, [
109
109
  PsGatedViewSlice,
110
110
  PsOrdersViewSlice
111
- ], [PsGatedCommandsSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined);
111
+ ], [PsGatedCommandsSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
112
112
 
113
113
  function commandNamed(name) {
114
114
  return Stdlib_Option.flatMap(structure.stateChangeSlices.flatMap(w => w.commands).find(c => c.name === name), c => c.requiredAccess);
@@ -551,7 +551,7 @@ let structure = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, un
551
551
  ], [
552
552
  PsPlaceOrderSlice,
553
553
  PsShipOrderSlice
554
- ], undefined, undefined, undefined, undefined, undefined, undefined, undefined);
554
+ ], undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
555
555
 
556
556
  globalThis.describe("lifecycle topology", () => {
557
557
  let ordersStates = Object.fromEntries([[
@@ -615,7 +615,7 @@ globalThis.describe("lifecycle topology", () => {
615
615
  });
616
616
 
617
617
  globalThis.describe("commandTransition — the edge as a switch", () => {
618
- let structure = Plugin_Structure$ReventlessCore.make("TransitionSwitchPlugin", undefined, undefined, [], [PsTransitionSwitchSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined);
618
+ let structure = Plugin_Structure$ReventlessCore.make("TransitionSwitchPlugin", undefined, undefined, [], [PsTransitionSwitchSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
619
619
  let slice = structure.stateChangeSlices[0];
620
620
  let byName = name => slice.commands.find(c => c.name === name);
621
621
  globalThis.test("the switch fills allowedStates and targetState", () => {
@@ -659,7 +659,7 @@ globalThis.describe("the harvested model against the switch", () => {
659
659
  targets: targets
660
660
  };
661
661
  };
662
- let build = lifecycleModel => Plugin_Structure$ReventlessCore.make("HarvestPlugin", undefined, undefined, [], [PsTransitionSwitchSlice], undefined, undefined, undefined, undefined, undefined, undefined, lifecycleModel);
662
+ let build = lifecycleModel => Plugin_Structure$ReventlessCore.make("HarvestPlugin", undefined, undefined, [], [PsTransitionSwitchSlice], undefined, undefined, undefined, undefined, undefined, undefined, lifecycleModel, undefined);
663
663
  let edgeOf = (model, name) => {
664
664
  let slice = build(model).stateChangeSlices[0];
665
665
  return Stdlib_Option.map(slice.commands.find(c => c.name === name), c => [
@@ -726,7 +726,7 @@ globalThis.describe("the harvested model against the switch", () => {
726
726
  command: "PlaceOrder",
727
727
  allowedStates: ["Draft"],
728
728
  targets: ["Placed"]
729
- }]);
729
+ }], undefined);
730
730
  let slice = structure.stateChangeSlices[0];
731
731
  globalThis.expect(Stdlib_Option.map(slice.commands.find(c => c.name === "PlaceOrder"), c => [
732
732
  c.allowedStates,
@@ -754,7 +754,7 @@ globalThis.describe("traitDeclarations — a graft leaves a trace", () => {
754
754
  let structure = Plugin_Structure$ReventlessCore.make("TraitPlugin", undefined, undefined, [], [
755
755
  PsTransitionSwitchSlice,
756
756
  PsShipOrderSlice
757
- ], undefined, undefined, undefined, undefined, undefined, undefined, undefined);
757
+ ], undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
758
758
  globalThis.test("a declaring component's trait reaches the structure", () => {
759
759
  globalThis.expect(Stdlib_Option.getOr(structure.traitDeclarations, []).map(d => [
760
760
  d.trait,
@@ -783,7 +783,7 @@ globalThis.describe("traitDeclarations — a graft leaves a trace", () => {
783
783
  globalThis.describe("declared-edge cross-check", () => {
784
784
  let buildBad = () => {
785
785
  try {
786
- Plugin_Structure$ReventlessCore.make("TransitionPlugin", undefined, undefined, [PsShipmentsViewSlice], [PsDispatchShipmentSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined);
786
+ Plugin_Structure$ReventlessCore.make("TransitionPlugin", undefined, undefined, [PsShipmentsViewSlice], [PsDispatchShipmentSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
787
787
  return;
788
788
  } catch (raw_e) {
789
789
  let e = Primitive_exceptions.internalToException(raw_e);
@@ -1230,7 +1230,7 @@ globalThis.describe("Plugin_Structure.make — Phase 2 graph fields", () => {
1230
1230
  });
1231
1231
  });
1232
1232
  globalThis.describe("singleQueryField — the singular the schema actually serves", () => {
1233
- let withIrregularPlural = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, [PsCategoriesViewSlice], [], undefined, undefined, undefined, undefined, undefined, undefined, undefined);
1233
+ let withIrregularPlural = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, [PsCategoriesViewSlice], [], undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
1234
1234
  let categories = withIrregularPlural.stateViewSlices[0];
1235
1235
  globalThis.test("an `-ies` plural publishes the `y` singular, not a truncation", () => {
1236
1236
  globalThis.expect([
@@ -1251,7 +1251,7 @@ globalThis.describe("Plugin_Structure.make — Phase 2 graph fields", () => {
1251
1251
  q.idField,
1252
1252
  q.idFieldSource
1253
1253
  ];
1254
- let withCategories = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, [PsCategoriesViewSlice], [], undefined, undefined, undefined, undefined, undefined, undefined, undefined);
1254
+ let withCategories = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, [PsCategoriesViewSlice], [], undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
1255
1255
  globalThis.test("a name-matching `*Id` field is a guess the consumer can also make", () => {
1256
1256
  globalThis.expect(keyOf(structure.stateViewSlices[0])).toEqual([
1257
1257
  "orderId",
@@ -1641,7 +1641,7 @@ globalThis.describe("Plugin_Structure.make — Phase 2 graph fields", () => {
1641
1641
  "Orders",
1642
1642
  "Order"
1643
1643
  ]
1644
- ]), undefined);
1644
+ ]), undefined, undefined);
1645
1645
  globalThis.test("a chaptered state-change slice carries Some(chapter)", () => {
1646
1646
  let placeOrder = chaptered.stateChangeSlices[0];
1647
1647
  globalThis.expect(placeOrder.chapter).toEqual("Order");
@@ -1660,7 +1660,7 @@ globalThis.describe("Plugin_Structure.make — Phase 2 graph fields", () => {
1660
1660
  });
1661
1661
  });
1662
1662
  globalThis.describe("requiredStores", () => {
1663
- let withStores = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, undefined, [PsAttachInvoiceSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined);
1663
+ let withStores = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, undefined, [PsAttachInvoiceSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
1664
1664
  let stores = Stdlib_Option.getOr(withStores.requiredStores, []);
1665
1665
  globalThis.test("an unqualified store resolves against the declaring plugin", () => {
1666
1666
  globalThis.expect(stores.includes("TestPlugin.documents")).toBe(true);
@@ -1816,7 +1816,7 @@ globalThis.describe("Plugin_Structure.make — Phase 2 graph fields", () => {
1816
1816
  });
1817
1817
  });
1818
1818
  globalThis.describe("uploadable types derive their store from the field name", () => {
1819
- let withUploads = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, undefined, [PsUploadImagesSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined);
1819
+ let withUploads = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, undefined, [PsUploadImagesSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
1820
1820
  let stores = Stdlib_Option.getOr(withUploads.requiredStores, []);
1821
1821
  globalThis.test("the derived store is the field name, pluralised", () => {
1822
1822
  globalThis.expect(stores.includes("TestPlugin.productImages")).toBe(true);
@@ -1877,7 +1877,7 @@ globalThis.describe("Plugin_Structure.make — Phase 2 graph fields", () => {
1877
1877
  globalThis.describe("near-duplicate stores are refused", () => {
1878
1878
  let failureText = () => {
1879
1879
  try {
1880
- Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, undefined, [PsTypoStoreSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined);
1880
+ Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, undefined, [PsTypoStoreSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
1881
1881
  return "";
1882
1882
  } catch (raw_e) {
1883
1883
  let e = Primitive_exceptions.internalToException(raw_e);
@@ -1906,7 +1906,7 @@ globalThis.describe("Plugin_Structure.make — Phase 2 graph fields", () => {
1906
1906
  ]);
1907
1907
  });
1908
1908
  globalThis.test("the four stores of the uploadable fixture do not collide", () => {
1909
- globalThis.expect(Capability_Inference$ReventlessCore.collisions(Stdlib_Option.getOr(Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, undefined, [PsUploadImagesSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined).requiredStoreDeclarations, []))).toEqual([]);
1909
+ globalThis.expect(Capability_Inference$ReventlessCore.collisions(Stdlib_Option.getOr(Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, undefined, [PsUploadImagesSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined).requiredStoreDeclarations, []))).toEqual([]);
1910
1910
  });
1911
1911
  globalThis.test("one edit, transpositions included; two edits not", () => {
1912
1912
  globalThis.expect([
@@ -1943,7 +1943,7 @@ globalThis.describe("Plugin_Structure.make — Phase 2 graph fields", () => {
1943
1943
  });
1944
1944
  });
1945
1945
  globalThis.describe("optional carriers keep their declarations", () => {
1946
- let withOptional = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, undefined, [PsUploadAvatarSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined);
1946
+ let withOptional = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, undefined, [PsUploadAvatarSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
1947
1947
  globalThis.test("an optional storage ref still requires its store", () => {
1948
1948
  globalThis.expect(withOptional.requiredStores).toEqual(["TestPlugin.avatars"]);
1949
1949
  });
@@ -1994,7 +1994,7 @@ globalThis.describe("Plugin_Structure.make — Phase 2 graph fields", () => {
1994
1994
  globalThis.expect(eventWarnings).toEqual(expected);
1995
1995
  });
1996
1996
  globalThis.test("a heuristic-only match reaches no manifest entry", () => {
1997
- let withHeuristicField = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, undefined, [PsChangePhotoSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined);
1997
+ let withHeuristicField = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, undefined, [PsChangePhotoSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
1998
1998
  globalThis.expect(withHeuristicField.requiredStores).toEqual(["TestPlugin.productPhotos"]);
1999
1999
  let manifest = CapabilityManifest$Reventless.fromStructure(withHeuristicField);
2000
2000
  globalThis.expect(manifest.capabilities.every(entry => entry.declaredBy.every(site => Primitive_object.notequal(site.field, "photoUrl")))).toBe(true);
@@ -2021,7 +2021,7 @@ globalThis.describe("Plugin_Structure.make — Phase 2 graph fields", () => {
2021
2021
  });
2022
2022
  });
2023
2023
  globalThis.describe("plural references reach the manifest", () => {
2024
- let withPluralRefs = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, undefined, [PsReserveStockSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined);
2024
+ let withPluralRefs = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, undefined, [PsReserveStockSlice], undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
2025
2025
  let slice = withPluralRefs.stateChangeSlices[0];
2026
2026
  let referencesOf = refs => refs.map(param => [
2027
2027
  param.fieldName,
@@ -2261,7 +2261,7 @@ globalThis.describe("duplicate DCB command names", () => {
2261
2261
  Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, undefined, [
2262
2262
  PsShipOrderSlice,
2263
2263
  PsShipOrderSlice
2264
- ], undefined, undefined, undefined, undefined, undefined, undefined, undefined);
2264
+ ], undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
2265
2265
  tmp = false;
2266
2266
  } catch (exn) {
2267
2267
  tmp = true;
@@ -2274,7 +2274,7 @@ globalThis.describe("duplicate DCB command names", () => {
2274
2274
  val = Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, undefined, [
2275
2275
  PsShipOrderSlice,
2276
2276
  PsShipOrderSlice
2277
- ], undefined, undefined, undefined, undefined, undefined, undefined, undefined);
2277
+ ], undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
2278
2278
  } catch (raw_e) {
2279
2279
  let e = Primitive_exceptions.internalToException(raw_e);
2280
2280
  if (e.RE_EXN_ID === "JsExn") {
@@ -2298,7 +2298,7 @@ globalThis.describe("duplicate DCB command names", () => {
2298
2298
  Plugin_Structure$ReventlessCore.make("TestPlugin", undefined, undefined, undefined, [
2299
2299
  PsPlaceOrderSlice,
2300
2300
  PsShipOrderSlice
2301
- ], undefined, undefined, undefined, undefined, undefined, undefined, undefined);
2301
+ ], undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
2302
2302
  tmp = true;
2303
2303
  } catch (exn) {
2304
2304
  tmp = false;