@reventlessdev/reventless-core 3.0.0-alpha.212 → 3.0.0-alpha.214

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 (30) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/package.json +6 -6
  3. package/src/Projection.res +5 -2
  4. package/src/Projection.res.mjs +2 -1
  5. package/src/adapter/EventLogProvisioning/EventLogProvisioning.res +165 -0
  6. package/src/adapter/EventLogProvisioning/EventLogProvisioning.res.mjs +49 -0
  7. package/src/components/AutomationSlice/AutomationSlice_Callback.res +30 -0
  8. package/src/components/AutomationSlice/AutomationSlice_Callback.res.mjs +12 -0
  9. package/src/components/Dcb/Dcb_Builder.res +6 -2
  10. package/src/components/Dcb/Dcb_Builder.res.mjs +2 -2
  11. package/src/components/DcbEventLog/DcbEventLog_Builder.res +17 -4
  12. package/src/components/DcbEventLog/DcbEventLog_Builder.res.mjs +9 -5
  13. package/src/components/EventLog/EventLog_Builder.res +10 -2
  14. package/src/components/EventLog/EventLog_Builder.res.mjs +4 -1
  15. package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Callback.res +32 -0
  16. package/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Callback.res.mjs +13 -0
  17. package/src/plugin/component/Plugin_Builder.res +6 -13
  18. package/src/plugin/component/Plugin_Builder.res.mjs +2 -16
  19. package/src/plugin/component/Plugin_Helpers.res +21 -3
  20. package/src/plugin/component/Plugin_Helpers.res.mjs +16 -0
  21. package/src/util/LogFormat.res +52 -1
  22. package/src/util/LogFormat.res.mjs +21 -2
  23. package/src/util/StoreLayout.res +38 -0
  24. package/src/util/StoreLayout.res.mjs +22 -0
  25. package/tests/adapter/EventLogProvisioningTest.res +207 -0
  26. package/tests/adapter/EventLogProvisioningTest.res.mjs +150 -0
  27. package/tests/api/GraphQL_FragmentGeneratorTest.res +40 -0
  28. package/tests/api/GraphQL_FragmentGeneratorTest.res.mjs +35 -0
  29. package/tests/logger/LogFormatTest.res +42 -0
  30. package/tests/logger/LogFormatTest.res.mjs +37 -0
@@ -4,6 +4,7 @@ import * as S from "sury/src/S.res.mjs";
4
4
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
5
  import * as GraphQL_Stitcher$ReventlessCore from "../../src/components/Api/GraphQL_Stitcher.res.mjs";
6
6
  import * as GraphQL_FragmentGenerator$ReventlessCore from "../../src/components/Api/GraphQL_FragmentGenerator.res.mjs";
7
+ import * as OutboundTranslationSlice_Callback$ReventlessCore from "../../src/components/OutboundTranslationSlice/OutboundTranslationSlice_Callback.res.mjs";
7
8
 
8
9
  let commandSchema = S.union([
9
10
  S.schema(s => ({
@@ -50,8 +51,42 @@ globalThis.describe("GraphQL_FragmentGenerator.generate mutation id injection",
50
51
  });
51
52
  });
52
53
 
54
+ let outboundItemSchema = S.schema(s => ({
55
+ customerId: s.m(S.string),
56
+ address: s.m(S.string)
57
+ }));
58
+
59
+ let GeocodeItem = {
60
+ outboundItemSchema: outboundItemSchema,
61
+ externalSystem: undefined
62
+ };
63
+
64
+ function typeDefFor(fragment, needle) {
65
+ return GraphQL_Stitcher$ReventlessCore.decode(fragment).types.find(t => t.includes(needle));
66
+ }
67
+
68
+ globalThis.describe("GraphQL_FragmentGenerator slice TODO rows", () => {
69
+ globalThis.test("item carries the slice's item type, not String", () => {
70
+ let fragment = GraphQL_FragmentGenerator$ReventlessCore.generate([], [{
71
+ singleFieldName: "Ordering_GeocodeCustomerAddressTodo",
72
+ listFieldName: "Ordering_GeocodeCustomerAddressTodos",
73
+ returnTypeName: "Ordering_GeocodeCustomerAddressTodo",
74
+ stateSchema: OutboundTranslationSlice_Callback$ReventlessCore.todoRowSchemaFor(outboundItemSchema),
75
+ authorization: undefined,
76
+ connectionSpec: true
77
+ }]);
78
+ let row = Stdlib_Option.getOr(typeDefFor(fragment, "type Ordering_GeocodeCustomerAddressTodo "), "");
79
+ globalThis.expect(row.includes("item: Ordering_GeocodeCustomerAddressTodoItem!")).toBe(true);
80
+ let item = Stdlib_Option.getOr(typeDefFor(fragment, "type Ordering_GeocodeCustomerAddressTodoItem "), "");
81
+ globalThis.expect(item.includes("customerId: ID!")).toBe(true);
82
+ globalThis.expect(item.includes("address: String!")).toBe(true);
83
+ });
84
+ });
85
+
53
86
  export {
54
87
  OrderSliceCmd,
55
88
  mutationFor,
89
+ GeocodeItem,
90
+ typeDefFor,
56
91
  }
57
92
  /* commandSchema Not a pure module */
@@ -127,6 +127,48 @@ describe("LogFormat", () => {
127
127
  },
128
128
  )
129
129
  })
130
+
131
+ // A ReScript record's absent optional field survives encoding as an own
132
+ // property whose value is `undefined` — the one thing `JSON.t` cannot hold and
133
+ // `JSON.stringify` answers with `undefined` instead of a string. Rendering a
134
+ // command carrying one must not reach a string function with that `undefined`:
135
+ // `variantFields` runs inside the CommandGenerator's log tap, so a throw there
136
+ // takes the command down with it and the slice never sees it.
137
+ describe("variantFields", () => {
138
+ module OptionalFieldCmd = {
139
+ @schema
140
+ type command = AddProduct({productId: string, name: string, imageUrl?: string})
141
+ }
142
+
143
+ testSync("omits an absent optional field", () => {
144
+ let json =
145
+ OptionalFieldCmd.AddProduct({productId: "prd-1", name: "Cirrus"})
146
+ ->Message.encode(OptionalFieldCmd.commandSchema)
147
+ expect(variantFields(json))->toEqual(`, {productId:"prd-1",name:"Cirrus"}`)
148
+ })
149
+
150
+ testSync("keeps a present optional field", () => {
151
+ let json =
152
+ OptionalFieldCmd.AddProduct({productId: "prd-1", name: "Cirrus", imageUrl: "/u/a.png"})
153
+ ->Message.encode(OptionalFieldCmd.commandSchema)
154
+ expect(variantFields(json))->toEqual(
155
+ `, {productId:"prd-1",name:"Cirrus",imageUrl:"/u/a.png"}`,
156
+ )
157
+ })
158
+
159
+ testSync("elides a value past the budget rather than dropping it", () => {
160
+ let long = String.repeat("x", maxValueChars + 20)
161
+ let json =
162
+ OptionalFieldCmd.AddProduct({productId: "prd-1", name: long})
163
+ ->Message.encode(OptionalFieldCmd.commandSchema)
164
+ let rendered = variantFields(json)
165
+ expect(rendered->String.includes("…(+22 chars)"))->toBe(true)
166
+ // The oversized value is capped, not the line: the neighbouring field that
167
+ // says which product this is stays intact.
168
+ expect(rendered->String.includes(long))->toBe(false)
169
+ expect(rendered->String.includes(`productId:"prd-1"`))->toBe(true)
170
+ })
171
+ })
130
172
  })
131
173
 
132
174
  // ─── JSON sink (non-TTY collector: CloudWatch / Datadog / …) ─────────────────
@@ -124,6 +124,43 @@ globalThis.describe("LogFormat", () => {
124
124
  globalThis.expect(msg).toEqual(`\x1b[1mVersionDetected\x1b[0m(testId): {"event":{"TAG":"VersionDetected","_0":"0.0.0"},"meta":{"service":"testService","time":"testTime","ip":"testIp","user":"testUser","msgId":"testMsgId","correlationId":"testCorrelationId"},"id":"testId"}`);
125
125
  });
126
126
  });
127
+ globalThis.describe("variantFields", () => {
128
+ let commandSchema = S.schema(s => ({
129
+ TAG: "AddProduct",
130
+ productId: s.m(S.string),
131
+ name: s.m(S.string),
132
+ imageUrl: s.m(S.option(S.string))
133
+ }));
134
+ globalThis.test("omits an absent optional field", () => {
135
+ let json = Message$ReventlessCore.encode({
136
+ TAG: "AddProduct",
137
+ productId: "prd-1",
138
+ name: "Cirrus"
139
+ }, commandSchema);
140
+ globalThis.expect(LogFormat$ReventlessCore.variantFields(json)).toEqual(`, {productId:"prd-1",name:"Cirrus"}`);
141
+ });
142
+ globalThis.test("keeps a present optional field", () => {
143
+ let json = Message$ReventlessCore.encode({
144
+ TAG: "AddProduct",
145
+ productId: "prd-1",
146
+ name: "Cirrus",
147
+ imageUrl: "/u/a.png"
148
+ }, commandSchema);
149
+ globalThis.expect(LogFormat$ReventlessCore.variantFields(json)).toEqual(`, {productId:"prd-1",name:"Cirrus",imageUrl:"/u/a.png"}`);
150
+ });
151
+ globalThis.test("elides a value past the budget rather than dropping it", () => {
152
+ let long = "x".repeat(LogFormat$ReventlessCore.maxValueChars + 20 | 0);
153
+ let json = Message$ReventlessCore.encode({
154
+ TAG: "AddProduct",
155
+ productId: "prd-1",
156
+ name: long
157
+ }, commandSchema);
158
+ let rendered = LogFormat$ReventlessCore.variantFields(json);
159
+ globalThis.expect(rendered.includes("…(+22 chars)")).toBe(true);
160
+ globalThis.expect(rendered.includes(long)).toBe(false);
161
+ globalThis.expect(rendered.includes(`productId:"prd-1"`)).toBe(true);
162
+ });
163
+ });
127
164
  });
128
165
 
129
166
  function hasAnsi(s) {