@reventlessdev/reventless-aws 3.0.0-alpha.339 → 3.0.0-alpha.341

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.
@@ -96,6 +96,7 @@ let makeBinding = (
96
96
  ~retiredField=None,
97
97
  ~retiredValues=None,
98
98
  ~namedWhenRetired=false,
99
+ ~imageSource=None,
99
100
  (),
100
101
  ): PgQueryResolver_Lambda.binding => {
101
102
  ops,
@@ -113,6 +114,7 @@ let makeBinding = (
113
114
  retiredField,
114
115
  retiredValues,
115
116
  namedWhenRetired,
117
+ imageSource,
116
118
  }
117
119
 
118
120
  let mkPayload = (
@@ -697,6 +699,7 @@ let geoBinding = (): PgQueryResolver_Lambda.binding => {
697
699
  retiredField: None,
698
700
  retiredValues: None,
699
701
  namedWhenRetired: false,
702
+ imageSource: None,
700
703
  }
701
704
 
702
705
  // The member type of the first row a door answers with, whatever shape it took.
@@ -121,13 +121,14 @@ let capability = {
121
121
  sortFields: capability_sortFields
122
122
  };
123
123
 
124
- function makeBinding(authorizationOpt, subIdFieldOpt, ownerFieldOpt, retiredFieldOpt, retiredValuesOpt, namedWhenRetiredOpt, param) {
124
+ function makeBinding(authorizationOpt, subIdFieldOpt, ownerFieldOpt, retiredFieldOpt, retiredValuesOpt, namedWhenRetiredOpt, imageSourceOpt, param) {
125
125
  let authorization = authorizationOpt !== undefined ? authorizationOpt : "AllowAnonymous";
126
126
  let subIdField = subIdFieldOpt !== undefined ? Primitive_option.valFromOption(subIdFieldOpt) : undefined;
127
127
  let ownerField = ownerFieldOpt !== undefined ? Primitive_option.valFromOption(ownerFieldOpt) : undefined;
128
128
  let retiredField = retiredFieldOpt !== undefined ? Primitive_option.valFromOption(retiredFieldOpt) : undefined;
129
129
  let retiredValues = retiredValuesOpt !== undefined ? Primitive_option.valFromOption(retiredValuesOpt) : undefined;
130
130
  let namedWhenRetired = namedWhenRetiredOpt !== undefined ? namedWhenRetiredOpt : false;
131
+ let imageSource = imageSourceOpt !== undefined ? Primitive_option.valFromOption(imageSourceOpt) : undefined;
131
132
  return {
132
133
  ops: ops,
133
134
  pushdowns: pushdowns,
@@ -145,7 +146,8 @@ function makeBinding(authorizationOpt, subIdFieldOpt, ownerFieldOpt, retiredFiel
145
146
  ownerField: ownerField,
146
147
  retiredField: retiredField,
147
148
  retiredValues: retiredValues,
148
- namedWhenRetired: namedWhenRetired
149
+ namedWhenRetired: namedWhenRetired,
150
+ imageSource: imageSource
149
151
  };
150
152
  }
151
153
 
@@ -188,7 +190,7 @@ function ids(arr) {
188
190
 
189
191
  globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
190
192
  globalThis.test("getById returns the item (id-carrying)", async () => {
191
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
193
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
192
194
  "id",
193
195
  "p-2"
194
196
  ]]), undefined, undefined));
@@ -196,14 +198,14 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
196
198
  globalThis.expect(str(r, "name")).toEqual("Bravo");
197
199
  });
198
200
  globalThis.test("getById miss → null", async () => {
199
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
201
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
200
202
  "id",
201
203
  "absent"
202
204
  ]]), undefined, undefined));
203
205
  globalThis.expect(r).toBe(null);
204
206
  });
205
207
  globalThis.test("byIds returns matches, drops missing", async () => {
206
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("byIds", undefined, Object.fromEntries([[
208
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("byIds", undefined, Object.fromEntries([[
207
209
  "ids",
208
210
  [
209
211
  "p-1",
@@ -217,7 +219,7 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
217
219
  ]);
218
220
  });
219
221
  globalThis.test("index routes to indexLookup on the idField", async () => {
220
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("index", "byStatus", Object.fromEntries([[
222
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("index", "byStatus", Object.fromEntries([[
221
223
  "byStatus",
222
224
  "active"
223
225
  ]]), undefined, undefined));
@@ -228,13 +230,13 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
228
230
  });
229
231
  globalThis.test("list with push-down → returns the connection listPage gives", async () => {
230
232
  listPageReturn.contents = "PUSHED";
231
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("list", undefined, undefined, undefined, undefined));
233
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("list", undefined, undefined, undefined, undefined));
232
234
  listPageReturn.contents = undefined;
233
235
  globalThis.expect(r).toEqual("PUSHED");
234
236
  });
235
237
  globalThis.test("list without push-down → falls back to the shared spec", async () => {
236
238
  listPageReturn.contents = undefined;
237
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("list", undefined, undefined, undefined, undefined));
239
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("list", undefined, undefined, undefined, undefined));
238
240
  let edgeIds = Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_Option.flatMap(field(r, "edges"), Stdlib_JSON.Decode.array), []), e => Stdlib_Option.flatMap(field(e, "node"), n => str(n, "id")));
239
241
  globalThis.expect(edgeIds).toEqual([
240
242
  "p-1",
@@ -243,21 +245,21 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
243
245
  ]);
244
246
  });
245
247
  globalThis.test("items with subId routes to itemsPage (id echoed)", async () => {
246
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, "seq", undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("items", undefined, Object.fromEntries([[
248
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, "seq", undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("items", undefined, Object.fromEntries([[
247
249
  "id",
248
250
  "p-2"
249
251
  ]]), undefined, undefined));
250
252
  globalThis.expect(str(r, "itemsFor")).toEqual("p-2");
251
253
  });
252
254
  globalThis.test("items without subId → empty connection", async () => {
253
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("items", undefined, Object.fromEntries([[
255
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("items", undefined, Object.fromEntries([[
254
256
  "id",
255
257
  "p-2"
256
258
  ]]), undefined, undefined));
257
259
  globalThis.expect(Stdlib_Option.getOr(Stdlib_Option.flatMap(field(r, "edges"), Stdlib_JSON.Decode.array), []).length).toBe(0);
258
260
  });
259
261
  globalThis.test("authorization DenyAll → empty shape per kind", async () => {
260
- let b = makeBinding("DenyAll", undefined, undefined, undefined, undefined, undefined, undefined);
262
+ let b = makeBinding("DenyAll", undefined, undefined, undefined, undefined, undefined, undefined, undefined);
261
263
  let single = await PgQueryResolver_Lambda$ReventlessAws.dispatch(b, undefined, mkPayload("getById", undefined, Object.fromEntries([[
262
264
  "id",
263
265
  "p-1"
@@ -276,7 +278,7 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
276
278
  TAG: "Deny",
277
279
  _0: "nope"
278
280
  }));
279
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
281
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
280
282
  "id",
281
283
  "p-1"
282
284
  ]]), undefined, undefined));
@@ -302,7 +304,7 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
302
304
  arguments: payload_arguments,
303
305
  identity: Identity$Reventless.anonymous
304
306
  };
305
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, payload);
307
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, payload);
306
308
  globalThis.expect(str(r, "id")).toEqual("p-2");
307
309
  });
308
310
  globalThis.test("resolveOne multi via target index → array", async () => {
@@ -328,7 +330,7 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
328
330
  arguments: payload_arguments,
329
331
  identity: Identity$Reventless.anonymous
330
332
  };
331
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, payload);
333
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, payload);
332
334
  globalThis.expect(ids(r)).toEqual([
333
335
  "p-1",
334
336
  "p-3"
@@ -355,14 +357,14 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
355
357
  arguments: payload_arguments,
356
358
  identity: Identity$Reventless.anonymous
357
359
  };
358
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, payload);
360
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, payload);
359
361
  globalThis.expect(ids(r)).toEqual([
360
362
  "p-1",
361
363
  "p-3"
362
364
  ]);
363
365
  });
364
366
  globalThis.test("node decodes global id → __typename + item", async () => {
365
- PgQueryResolver_Lambda$ReventlessAws.register("Things", makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined));
367
+ PgQueryResolver_Lambda$ReventlessAws.register("Things", makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined));
366
368
  PgQueryResolver_Lambda$ReventlessAws.registerNodeType("Thing", "Things");
367
369
  let payload_arguments = Object.fromEntries([[
368
370
  "id",
@@ -412,7 +414,7 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
412
414
  delete: 0,
413
415
  deleteBatch: 0
414
416
  };
415
- let init = makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined);
417
+ let init = makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
416
418
  let authBinding_pushdowns = init.pushdowns;
417
419
  let authBinding_indexes = init.indexes;
418
420
  let authBinding_subIdField = init.subIdField;
@@ -424,6 +426,7 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
424
426
  let authBinding_retiredField = init.retiredField;
425
427
  let authBinding_retiredValues = init.retiredValues;
426
428
  let authBinding_namedWhenRetired = init.namedWhenRetired;
429
+ let authBinding_imageSource = init.imageSource;
427
430
  let authBinding = {
428
431
  ops: authOps,
429
432
  pushdowns: authBinding_pushdowns,
@@ -436,7 +439,8 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
436
439
  ownerField: authBinding_ownerField,
437
440
  retiredField: authBinding_retiredField,
438
441
  retiredValues: authBinding_retiredValues,
439
- namedWhenRetired: authBinding_namedWhenRetired
442
+ namedWhenRetired: authBinding_namedWhenRetired,
443
+ imageSource: authBinding_imageSource
440
444
  };
441
445
  let lookupAuth = name => {
442
446
  if (name === "AuthTable") {
@@ -462,18 +466,18 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
462
466
  identity: ident(username, groups)
463
467
  });
464
468
  globalThis.test("auth index: group member who owns → results", async () => {
465
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), lookupAuth, authIndexPayload("alice", ["Owner"]));
469
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), lookupAuth, authIndexPayload("alice", ["Owner"]));
466
470
  globalThis.expect(ids(r)).toEqual([
467
471
  "p-1",
468
472
  "p-3"
469
473
  ]);
470
474
  });
471
475
  globalThis.test("auth index: group member who does NOT own → empty", async () => {
472
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), lookupAuth, authIndexPayload("bob", ["Owner"]));
476
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), lookupAuth, authIndexPayload("bob", ["Owner"]));
473
477
  globalThis.expect(Stdlib_Option.getOr(Stdlib_JSON.Decode.array(r), []).length).toBe(0);
474
478
  });
475
479
  globalThis.test("auth index: non-member passes through → results", async () => {
476
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), lookupAuth, authIndexPayload("bob", []));
480
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), lookupAuth, authIndexPayload("bob", []));
477
481
  globalThis.expect(ids(r)).toEqual([
478
482
  "p-1",
479
483
  "p-3"
@@ -482,7 +486,7 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
482
486
  globalThis.test("unmapped kind throws", async () => {
483
487
  let threw;
484
488
  try {
485
- await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("frobnicate", undefined, undefined, undefined, undefined));
489
+ await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("frobnicate", undefined, undefined, undefined, undefined));
486
490
  threw = false;
487
491
  } catch (exn) {
488
492
  threw = true;
@@ -493,35 +497,35 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
493
497
 
494
498
  globalThis.describe("id form — the typed doors take either", () => {
495
499
  globalThis.test("getById resolves a Relay global id", async () => {
496
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
500
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
497
501
  "id",
498
502
  Api_Ids$ReventlessCore.encode("Thing", "p-2")
499
503
  ]]), undefined, undefined));
500
504
  globalThis.expect(str(r, "name")).toEqual("Bravo");
501
505
  });
502
506
  globalThis.test("getById reports the storage key it resolved to", async () => {
503
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
507
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
504
508
  "id",
505
509
  Api_Ids$ReventlessCore.encode("Thing", "p-2")
506
510
  ]]), undefined, undefined));
507
511
  globalThis.expect(str(r, "id")).toEqual("p-2");
508
512
  });
509
513
  globalThis.test("getById still resolves a plain storage key", async () => {
510
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
514
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
511
515
  "id",
512
516
  "p-2"
513
517
  ]]), undefined, undefined));
514
518
  globalThis.expect(str(r, "name")).toEqual("Bravo");
515
519
  });
516
520
  globalThis.test("getById on a global id for a row that does not exist is still null", async () => {
517
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
521
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
518
522
  "id",
519
523
  Api_Ids$ReventlessCore.encode("Thing", "absent")
520
524
  ]]), undefined, undefined));
521
525
  globalThis.expect(r).toBe(null);
522
526
  });
523
527
  globalThis.test("byIds accepts the two forms mixed in one call", async () => {
524
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("byIds", undefined, Object.fromEntries([[
528
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("byIds", undefined, Object.fromEntries([[
525
529
  "ids",
526
530
  [
527
531
  "p-1",
@@ -534,7 +538,7 @@ globalThis.describe("id form — the typed doors take either", () => {
534
538
  ]);
535
539
  });
536
540
  globalThis.test("byIds with every id found does not double-count", async () => {
537
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("byIds", undefined, Object.fromEntries([[
541
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("byIds", undefined, Object.fromEntries([[
538
542
  "ids",
539
543
  [
540
544
  "p-1",
@@ -549,7 +553,7 @@ globalThis.describe("id form — the typed doors take either", () => {
549
553
  });
550
554
 
551
555
  globalThis.describe("owner scoping", () => {
552
- let ownedBinding = () => makeBinding(undefined, undefined, "owner", undefined, undefined, undefined, undefined);
556
+ let ownedBinding = () => makeBinding(undefined, undefined, "owner", undefined, undefined, undefined, undefined, undefined);
553
557
  let shopper = cognito("u-a", ["User"]);
554
558
  let listWith = async (binding, identity) => {
555
559
  lastOwnerScope.contents = undefined;
@@ -574,7 +578,7 @@ globalThis.describe("owner scoping", () => {
574
578
  globalThis.expect(match[1]).toBe(undefined);
575
579
  });
576
580
  globalThis.test("a view with no owner field is never scoped", async () => {
577
- let match = await listWith(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined), shopper);
581
+ let match = await listWith(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined), shopper);
578
582
  globalThis.expect(match[1]).toBe(undefined);
579
583
  });
580
584
  globalThis.test("an unidentified caller is refused before the push-down runs", async () => {
@@ -709,7 +713,8 @@ function geoBinding() {
709
713
  ownerField: undefined,
710
714
  retiredField: undefined,
711
715
  retiredValues: undefined,
712
- namedWhenRetired: false
716
+ namedWhenRetired: false,
717
+ imageSource: undefined
713
718
  };
714
719
  }
715
720
 
@@ -0,0 +1,96 @@
1
+ // The `@displayName` overlay reached the typed paths — the local platform's
2
+ // builder and the GWT harness both rewrite a projection's actions — but a
3
+ // deployed state-view slice assembles its own JSON-level operations, so the
4
+ // synthetic column stayed null on AWS and every surface named the row by its id.
5
+ //
6
+ // The same gap, and for the same reason, as the union stamp beside it.
7
+
8
+ open JestGlobals
9
+
10
+ @schema
11
+ type rowState = {
12
+ orderId: string,
13
+ placedAt: string,
14
+ shippedAt: string,
15
+ displayName: option<string>,
16
+ }
17
+
18
+ // The same state with no `@displayName` on any of its fields.
19
+ let plainRowStateSchema = rowStateSchema
20
+
21
+ // What the ppx emits for `@displayName placedAt`.
22
+ let rowStateSchema =
23
+ rowStateSchema->S.Metadata.set(
24
+ ~id=Reventless.DisplayName.displayNameId,
25
+ {Reventless.DisplayName.fields: ["placedAt"], separator: " "},
26
+ )
27
+
28
+ // A recording stand-in for the storage the runtime would wrap.
29
+ let capture = (): (ReventlessCore.QueryDb_Adapter.operations, array<JSON.t>) => {
30
+ let saved: array<JSON.t> = []
31
+ let base: ReventlessCore.QueryDb_Adapter.operations = {
32
+ load: _ => Promise.resolve(Ok([])),
33
+ loadStream: _ => Stream.empty,
34
+ save: (_id, state, _saveMode, _ttl) => {
35
+ saved->Array.push(state)
36
+ Promise.resolve(Ok())
37
+ },
38
+ saveBatch: items => {
39
+ items->Array.forEach(((_, state, _)) => saved->Array.push(state))
40
+ Promise.resolve(Ok())
41
+ },
42
+ count: (_, _, _) => Promise.resolve(Ok(0)),
43
+ delete: (_, _) => Promise.resolve(Ok()),
44
+ deleteBatch: _ => Promise.resolve(Ok()),
45
+ }
46
+ (base, saved)
47
+ }
48
+
49
+ let labelOf = (json: JSON.t) =>
50
+ json
51
+ ->JSON.Decode.object
52
+ ->Option.flatMap(o => o->Dict.get("displayName"))
53
+ ->Option.flatMap(JSON.Decode.string)
54
+
55
+ let row = (~placedAt="2026-09-07T13:57:46.593Z"): JSON.t =>
56
+ ({orderId: "o1", placedAt, shippedAt: "", displayName: None}: rowState)
57
+ ->ReventlessCore.Message.encode(rowStateSchema)
58
+
59
+ describe("ProjectionEntryPoint_Ops.withDisplayName", () => {
60
+ testPromise("composes the label on save", async () => {
61
+ let (base, saved) = capture()
62
+ let ops =
63
+ base->ProjectionEntryPoint_Ops.withDisplayName(
64
+ ~stateSchema=Some(rowStateSchema->S.castToUnknown),
65
+ )
66
+ let _ = await ops.save("o1", row(), ReventlessCore.QueryDb.Any, None)
67
+ expect(saved->Array.get(0)->Option.flatMap(labelOf))->toEqual(Some("2026-09-07T13:57:46.593Z"))
68
+ })
69
+
70
+ // saveBatch is the path a multi-event projection takes, and it was missing the
71
+ // overlay for the same reason save was.
72
+ testPromise("composes the label for every row in a batch", async () => {
73
+ let (base, saved) = capture()
74
+ let ops =
75
+ base->ProjectionEntryPoint_Ops.withDisplayName(
76
+ ~stateSchema=Some(rowStateSchema->S.castToUnknown),
77
+ )
78
+ let _ = await ops.saveBatch([("o1", row(), None), ("o2", row(~placedAt="2026-09-08T09:00:00Z"), None)])
79
+ expect(saved->Array.map(labelOf))->toEqual([
80
+ Some("2026-09-07T13:57:46.593Z"),
81
+ Some("2026-09-08T09:00:00Z"),
82
+ ])
83
+ })
84
+
85
+ // A state that declares no `@displayName` is left exactly as it was — the
86
+ // wrapper is applied to every slice, annotated or not.
87
+ testPromise("passes rows through when the state declares no label", async () => {
88
+ let (base, saved) = capture()
89
+ let ops =
90
+ base->ProjectionEntryPoint_Ops.withDisplayName(
91
+ ~stateSchema=Some(plainRowStateSchema->S.castToUnknown),
92
+ )
93
+ let _ = await ops.save("o1", row(), ReventlessCore.QueryDb.Any, None)
94
+ expect(saved->Array.get(0)->Option.flatMap(labelOf))->toEqual(None)
95
+ })
96
+ })
@@ -0,0 +1,131 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Sury from "sury";
4
+ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
5
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
+ import * as Stream from "effect/Stream";
7
+ import * as DisplayName$Reventless from "@reventlessdev/reventless-spec/src/components/DisplayName.res.mjs";
8
+ import * as Message$ReventlessCore from "@reventlessdev/reventless-core/src/Message.res.mjs";
9
+ import * as ProjectionEntryPoint_Ops$ReventlessAws from "../src/adapter/Runtime/ProjectionEntryPoint_Ops.res.mjs";
10
+
11
+ let rowStateSchema = Sury.$schema(s => ({
12
+ orderId: s.m(Sury.string),
13
+ placedAt: s.m(Sury.string),
14
+ shippedAt: s.m(Sury.string),
15
+ displayName: s.m(Sury.$option(Sury.string))
16
+ }));
17
+
18
+ let rowStateSchema$1 = Sury.$Metadata_set(rowStateSchema, DisplayName$Reventless.displayNameId, {
19
+ fields: ["placedAt"],
20
+ separator: " "
21
+ });
22
+
23
+ function capture() {
24
+ let saved = [];
25
+ let base_load = param => Promise.resolve({
26
+ TAG: "Ok",
27
+ _0: []
28
+ });
29
+ let base_loadStream = param => Stream.empty;
30
+ let base_save = (_id, state, _saveMode, _ttl) => {
31
+ saved.push(state);
32
+ return Promise.resolve({
33
+ TAG: "Ok",
34
+ _0: undefined
35
+ });
36
+ };
37
+ let base_saveBatch = items => {
38
+ items.forEach(param => {
39
+ saved.push(param[1]);
40
+ });
41
+ return Promise.resolve({
42
+ TAG: "Ok",
43
+ _0: undefined
44
+ });
45
+ };
46
+ let base_count = (param, param$1, param$2) => Promise.resolve({
47
+ TAG: "Ok",
48
+ _0: 0
49
+ });
50
+ let base_delete = (param, param$1) => Promise.resolve({
51
+ TAG: "Ok",
52
+ _0: undefined
53
+ });
54
+ let base_deleteBatch = param => Promise.resolve({
55
+ TAG: "Ok",
56
+ _0: undefined
57
+ });
58
+ let base = {
59
+ load: base_load,
60
+ loadStream: base_loadStream,
61
+ save: base_save,
62
+ saveBatch: base_saveBatch,
63
+ count: base_count,
64
+ delete: base_delete,
65
+ deleteBatch: base_deleteBatch
66
+ };
67
+ return [
68
+ base,
69
+ saved
70
+ ];
71
+ }
72
+
73
+ function labelOf(json) {
74
+ return Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(json), o => o["displayName"]), Stdlib_JSON.Decode.string);
75
+ }
76
+
77
+ function row(placedAtOpt) {
78
+ let placedAt = placedAtOpt !== undefined ? placedAtOpt : "2026-09-07T13:57:46.593Z";
79
+ return Message$ReventlessCore.encode({
80
+ orderId: "o1",
81
+ placedAt: placedAt,
82
+ shippedAt: "",
83
+ displayName: undefined
84
+ }, rowStateSchema$1);
85
+ }
86
+
87
+ globalThis.describe("ProjectionEntryPoint_Ops.withDisplayName", () => {
88
+ globalThis.test("composes the label on save", async () => {
89
+ let match = capture();
90
+ let ops = ProjectionEntryPoint_Ops$ReventlessAws.withDisplayName(match[0], rowStateSchema$1);
91
+ await ops.save("o1", row(undefined), "Any", undefined);
92
+ globalThis.expect(Stdlib_Option.flatMap(match[1][0], labelOf)).toEqual("2026-09-07T13:57:46.593Z");
93
+ });
94
+ globalThis.test("composes the label for every row in a batch", async () => {
95
+ let match = capture();
96
+ let ops = ProjectionEntryPoint_Ops$ReventlessAws.withDisplayName(match[0], rowStateSchema$1);
97
+ await ops.saveBatch([
98
+ [
99
+ "o1",
100
+ row(undefined),
101
+ undefined
102
+ ],
103
+ [
104
+ "o2",
105
+ row("2026-09-08T09:00:00Z"),
106
+ undefined
107
+ ]
108
+ ]);
109
+ globalThis.expect(match[1].map(labelOf)).toEqual([
110
+ "2026-09-07T13:57:46.593Z",
111
+ "2026-09-08T09:00:00Z"
112
+ ]);
113
+ });
114
+ globalThis.test("passes rows through when the state declares no label", async () => {
115
+ let match = capture();
116
+ let ops = ProjectionEntryPoint_Ops$ReventlessAws.withDisplayName(match[0], rowStateSchema);
117
+ await ops.save("o1", row(undefined), "Any", undefined);
118
+ globalThis.expect(Stdlib_Option.flatMap(match[1][0], labelOf)).toEqual(undefined);
119
+ });
120
+ });
121
+
122
+ let plainRowStateSchema = rowStateSchema;
123
+
124
+ export {
125
+ plainRowStateSchema,
126
+ rowStateSchema$1 as rowStateSchema,
127
+ capture,
128
+ labelOf,
129
+ row,
130
+ }
131
+ /* rowStateSchema Not a pure module */
@@ -14,6 +14,19 @@ open JestGlobals
14
14
  let str = JSON.Encode.string
15
15
  let obj = pairs => JSON.Encode.object(Dict.fromArray(pairs))
16
16
 
17
+ @schema
18
+ type trailedLifecycle =
19
+ | Placed
20
+ | Shipped
21
+
22
+ // A view declaring a trail, for the pipeline cases below. The states this path
23
+ // writes are JSON, so only the schema says where the trail and the lifecycle are.
24
+ @schema
25
+ type trailedState = {
26
+ lifecycle: trailedLifecycle,
27
+ trail: Reventless.Lifecycle.Trail.t<trailedLifecycle>,
28
+ }
29
+
17
30
  describe("StateViewSliceEntryPoint_Ops.parseHandlerConfig", () => {
18
31
  testSync("empty raw config yields no handlers", () => {
19
32
  expect(StateViewSliceEntryPoint_Ops.parseHandlerConfig("")->Array.length)->toBe(0)
@@ -141,6 +154,7 @@ describe("StateViewSliceEntryPoint_Ops.makeJsonEventsHandler", () => {
141
154
  ~project,
142
155
  ~queryDbOps=makeStub(saved),
143
156
  ~subIdConfig=None,
157
+ ~stateSchema=None,
144
158
  )
145
159
  let envelope = obj([("event", str("row-1")), ("recordedAt", str("2026-07-27"))])
146
160
  let bare = str("row-2")
@@ -156,6 +170,7 @@ describe("StateViewSliceEntryPoint_Ops.makeJsonEventsHandler", () => {
156
170
  ~project,
157
171
  ~queryDbOps=makeStub(saved),
158
172
  ~subIdConfig=None,
173
+ ~stateSchema=None,
159
174
  )
160
175
  // A number cannot parse as S.string → logged and skipped; the following
161
176
  // record still projects.
@@ -164,4 +179,56 @@ describe("StateViewSliceEntryPoint_Ops.makeJsonEventsHandler", () => {
164
179
  ->Effect.runPromise
165
180
  expect(saved)->toEqual([("row-after", str(""))])
166
181
  })
182
+
183
+ // The deployed slice assembles JSON-level operations instead of going through
184
+ // the typed rewrite, so the trail has to be appended on this path too — the
185
+ // same gap `withDisplayName` was written to close.
186
+ test("appends a trail entry from the envelope's producer time", async () => {
187
+ let saved = []
188
+ let handler = StateViewSliceEntryPoint_Ops.makeJsonEventsHandler(
189
+ ~sliceName="TrailSlice",
190
+ ~eventSchema=S.string,
191
+ ~project=({event, _}) => [
192
+ Set(event, obj([("lifecycle", str("Placed")), ("trail", JSON.Encode.array([]))])),
193
+ ],
194
+ ~queryDbOps=makeStub(saved),
195
+ ~subIdConfig=None,
196
+ ~stateSchema=Some(trailedStateSchema->S.castToUnknown),
197
+ )
198
+ await Stream.fromIterable([
199
+ obj([("event", str("row-1")), ("meta", obj([("time", str("2026-03-02T09:00:00Z"))]))]),
200
+ ])
201
+ ->handler
202
+ ->Effect.runPromise
203
+ expect(saved)->toEqual([
204
+ (
205
+ "row-1",
206
+ obj([
207
+ ("lifecycle", str("Placed")),
208
+ ("trail", JSON.Encode.array([obj([("state", str("Placed")), ("at", str("2026-03-02T09:00:00Z"))])])),
209
+ ]),
210
+ ),
211
+ ])
212
+ })
213
+
214
+ // Without a producer time there is no instant to stamp, so the row is written
215
+ // as the projection produced it rather than with an entry the read path would
216
+ // refuse.
217
+ test("appends nothing when the envelope carries no producer time", async () => {
218
+ let saved = []
219
+ let handler = StateViewSliceEntryPoint_Ops.makeJsonEventsHandler(
220
+ ~sliceName="TrailSlice",
221
+ ~eventSchema=S.string,
222
+ ~project=({event, _}) => [
223
+ Set(event, obj([("lifecycle", str("Placed")), ("trail", JSON.Encode.array([]))])),
224
+ ],
225
+ ~queryDbOps=makeStub(saved),
226
+ ~subIdConfig=None,
227
+ ~stateSchema=Some(trailedStateSchema->S.castToUnknown),
228
+ )
229
+ await Stream.fromIterable([obj([("event", str("row-1"))])])->handler->Effect.runPromise
230
+ expect(saved)->toEqual([
231
+ ("row-1", obj([("lifecycle", str("Placed")), ("trail", JSON.Encode.array([]))])),
232
+ ])
233
+ })
167
234
  })