@reventlessdev/reventless-aws 3.0.0-alpha.301 → 3.0.0-alpha.303

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 (35) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/package.json +7 -7
  3. package/src/Platform.res +18 -4
  4. package/src/Platform.res.mjs +24 -4
  5. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +39 -4
  6. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +34 -13
  7. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +108 -20
  8. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs +99 -17
  9. package/src/adapter/QueryDb/PgQueryResolver_Lambda.res +41 -3
  10. package/src/adapter/QueryDb/PgQueryResolver_Lambda.res.mjs +27 -6
  11. package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res +34 -4
  12. package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res.mjs +16 -5
  13. package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +10 -0
  14. package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res.mjs +4 -1
  15. package/src/adapter/Runtime/StateTopicPublish.mjs +38 -5
  16. package/src/adapter/StateTopic/StateTopic_AppSync.res +49 -1
  17. package/src/adapter/StateTopic/StateTopic_AppSync.res.mjs +28 -3
  18. package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res +73 -2
  19. package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res.mjs +39 -4
  20. package/src/util/Util_DynamoDbStream.res +15 -4
  21. package/src/util/Util_DynamoDbStream.res.mjs +15 -5
  22. package/src/util/Util_ShellConfig.res +22 -2
  23. package/src/util/Util_ShellConfig.res.mjs +11 -0
  24. package/tests/PgQueryResolver_LambdaTest.res +16 -1
  25. package/tests/PgQueryResolver_LambdaTest.res.mjs +46 -32
  26. package/tests/Platform_ComponentDefinitions_Lambda_OpsTest.res +111 -0
  27. package/tests/Platform_ComponentDefinitions_Lambda_OpsTest.res.mjs +67 -0
  28. package/tests/QueryDbResolvers_AppSyncTest.res.mjs +3 -3
  29. package/tests/StateChangeDescriptorParityTest.res +82 -1
  30. package/tests/StateChangeDescriptorParityTest.res.mjs +97 -1
  31. package/tests/Util_DynamoDbStreamTest.res +43 -0
  32. package/tests/Util_DynamoDbStreamTest.res.mjs +41 -0
  33. package/tests/Util_ShellConfigTest.res +107 -0
  34. package/tests/Util_ShellConfigTest.res.mjs +100 -0
  35. package/tests/stateChangeDescriptorParity.mjs +72 -3
@@ -1,5 +1,6 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
+ import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
3
4
  import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
4
5
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
5
6
  import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
@@ -7,6 +8,7 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
7
8
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
8
9
  import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
9
10
  import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
11
+ import * as OwnerScope$Reventless from "@reventlessdev/reventless-spec/src/types/OwnerScope.res.mjs";
10
12
  import * as DynamoDb_Util_Helpers$AwsSdk from "@reventlessdev/rescript-aws-sdk/src/DynamoDb_Util_Helpers.res.mjs";
11
13
  import * as AppSyncEventsSigner_Ops$ReventlessAws from "../Api/AppSyncEventsSigner_Ops.res.mjs";
12
14
 
@@ -22,6 +24,33 @@ let topicMap = obj !== undefined ? Object.fromEntries(Stdlib_Array.filterMap(Obj
22
24
  ]);
23
25
  })) : ({});
24
26
 
27
+ let d = Stdlib_JSON.Decode.object(JSON.parse(Stdlib_Option.getOr(process.env["STATE_RETIRED_MAP"], "{}")));
28
+
29
+ let retiredMap;
30
+
31
+ if (d !== undefined) {
32
+ let out = {};
33
+ Stdlib_Dict.forEachWithKey(d, (v, k) => Stdlib_Option.forEach(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(v), o => Stdlib_Option.map(Stdlib_Option.flatMap(o["field"], Stdlib_JSON.Decode.string), field => ({
34
+ field: field,
35
+ values: Stdlib_Option.map(Stdlib_Option.flatMap(o["values"], Stdlib_JSON.Decode.array), vs => Stdlib_Array.filterMap(vs, Stdlib_JSON.Decode.string))
36
+ }))), scope => {
37
+ out[k] = scope;
38
+ }));
39
+ retiredMap = out;
40
+ } else {
41
+ retiredMap = {};
42
+ }
43
+
44
+ function tableNameFromEventSourceArn(arn) {
45
+ let parts = arn.split("/");
46
+ let match = parts[0];
47
+ let match$1 = parts[1];
48
+ let match$2 = parts[2];
49
+ if (match !== undefined && match$1 !== undefined && match$2 !== undefined && match$2 === "stream" && match.endsWith(":table")) {
50
+ return match$1;
51
+ }
52
+ }
53
+
25
54
  function topicRootFromEventSourceArn(arn) {
26
55
  let parts = arn.split("/");
27
56
  let match = parts[0];
@@ -89,12 +118,16 @@ function pickSortKeyValue(image) {
89
118
  }
90
119
  }
91
120
 
92
- function makeDescriptor(changeKind, entityKey, image, seq) {
121
+ function makeDescriptor(changeKind, entityKey, image, seq, retiredField, retiredValues) {
93
122
  let removed = changeKind === "Removed";
123
+ let retired = retiredField !== undefined ? !removed && OwnerScope$Reventless.isRetiredValue({
124
+ field: retiredField,
125
+ values: retiredValues
126
+ }, image[retiredField]) : false;
94
127
  let descriptor = {};
95
128
  descriptor["changeKind"] = changeKind;
96
129
  descriptor["id"] = entityKey;
97
- if (!removed) {
130
+ if (!removed && !retired) {
98
131
  Stdlib_Option.forEach(pickSortKeyValue(image), v => {
99
132
  descriptor["sortKeyValue"] = v;
100
133
  });
@@ -102,7 +135,7 @@ function makeDescriptor(changeKind, entityKey, image, seq) {
102
135
  Stdlib_Option.forEach(seq, s => {
103
136
  descriptor["seq"] = s;
104
137
  });
105
- if (!removed) {
138
+ if (!removed && !retired) {
106
139
  let encoded = JSON.stringify(image);
107
140
  if (encoded.length <= 61440) {
108
141
  descriptor["state"] = image;
@@ -127,7 +160,7 @@ async function processRecord(record, region, creds) {
127
160
  let entityKey = entityKeyFromRecord(dynamodb);
128
161
  let channel = `/default/` + topicRoot + `/` + AppSyncEventsSigner_Ops$ReventlessAws.pathSegment(entityKey);
129
162
  let unmarshalled = DynamoDb_Util_Helpers$AwsSdk.unmarshallDict(undefined, image);
130
- let descriptor = makeDescriptor(changeKindFor(record.eventName), entityKey, unmarshalled, dynamodb.SequenceNumber);
163
+ let descriptor = makeDescriptor(changeKindFor(record.eventName), entityKey, unmarshalled, dynamodb.SequenceNumber, Stdlib_Option.map(Stdlib_Option.flatMap(tableNameFromEventSourceArn(record.eventSourceARN), t => retiredMap[t]), scope => scope.field), Stdlib_Option.flatMap(Stdlib_Option.flatMap(tableNameFromEventSourceArn(record.eventSourceARN), t => retiredMap[t]), scope => scope.values));
131
164
  let body = JSON.stringify(Object.fromEntries([
132
165
  [
133
166
  "id",
@@ -193,6 +226,8 @@ let maxStateChars = 61440;
193
226
  export {
194
227
  endpoint,
195
228
  topicMap,
229
+ retiredMap,
230
+ tableNameFromEventSourceArn,
196
231
  topicRootFromEventSourceArn,
197
232
  jsonToString,
198
233
  entityKeyFromRecord,
@@ -1,13 +1,17 @@
1
1
  let toResourceInfo = (table: PulumiAws.DynamoDb.Table.t) =>
2
2
  table.streamArn->Pulumi.Output.apply(streamArn => ReventlessInfra.Adapter.StreamSource({sourceUrn: streamArn}))
3
3
 
4
+ // The table name rides IN the apply rather than being read with `Output.get` on
5
+ // the failure arm: `get` throws "Cannot call '.get' during update or preview" on
6
+ // every deploy, so the arm meant to name the offending table replaced its own
7
+ // message with that one and hid which resource was missing a stream.
4
8
  let streamArnFromDynamoDbTableResource = (resource: ReventlessInfra.Adapter.resource) =>
5
- resource.resourceInfo->Pulumi.Output.apply(resourceInfo =>
9
+ (resource.resourceInfo, resource.name)
10
+ ->Pulumi.Output.all2
11
+ ->Pulumi.Output.apply(((resourceInfo, tableName)) =>
6
12
  switch resourceInfo {
7
13
  | StreamSource({sourceUrn}) => sourceUrn
8
- | _ =>
9
- let tableName = resource.name->Pulumi.Output.get
10
- JsError.throwWithMessage("No streamArn field given for table " ++ tableName)
14
+ | _ => JsError.throwWithMessage("No streamArn field given for table " ++ tableName)
11
15
  }
12
16
  )
13
17
 
@@ -30,6 +34,13 @@ let toStreamResource = (table: ReventlessInfra.Adapter.resource): ReventlessInfr
30
34
  ~id=streamArn,
31
35
  ~urn=streamArn,
32
36
  ~service=table.name->Pulumi.Output.apply(_ => AWS.DynamoDbStream.service),
37
+ // Carries the same StreamSource the table resource carries, so asking a
38
+ // stream resource for its ARN answers instead of throwing. The event-topic
39
+ // resources a publisher exports are these, and `Upload_Claim_S3` asks
40
+ // exactly that of resources[0].
41
+ ~resourceInfo=streamArn->Pulumi.Output.apply(sourceUrn => ReventlessInfra.Adapter.StreamSource({
42
+ sourceUrn: sourceUrn,
43
+ })),
33
44
  ~resourceType="aws:dynamodb:Stream"->Pulumi.Output.make,
34
45
  )
35
46
  }
@@ -22,12 +22,19 @@ function toResourceInfo(table) {
22
22
  }
23
23
 
24
24
  function streamArnFromDynamoDbTableResource(resource) {
25
- return resource.resourceInfo.apply(resourceInfo => {
26
- if (typeof resourceInfo === "object" && resourceInfo.TAG === "StreamSource") {
25
+ return Pulumi.all([
26
+ resource.resourceInfo,
27
+ resource.name
28
+ ]).apply(param => {
29
+ let tableName = param[1];
30
+ let resourceInfo = param[0];
31
+ if (typeof resourceInfo !== "object") {
32
+ return Stdlib_JsError.throwWithMessage("No streamArn field given for table " + tableName);
33
+ } else if (resourceInfo.TAG === "StreamSource") {
27
34
  return resourceInfo.sourceUrn;
35
+ } else {
36
+ return Stdlib_JsError.throwWithMessage("No streamArn field given for table " + tableName);
28
37
  }
29
- let tableName = resource.name.get();
30
- return Stdlib_JsError.throwWithMessage("No streamArn field given for table " + tableName);
31
38
  });
32
39
  }
33
40
 
@@ -37,7 +44,10 @@ function toResource(tags, table) {
37
44
 
38
45
  function toStreamResource(table) {
39
46
  let streamArn = streamArnFromDynamoDbTableResource(table);
40
- return Adapter$ReventlessInfra.make(table.name, streamArn, streamArn, table.name.apply(param => AWS$ReventlessAws.DynamoDbStream.service), undefined, undefined, undefined, Pulumi.output("aws:dynamodb:Stream"), undefined, undefined);
47
+ return Adapter$ReventlessInfra.make(table.name, streamArn, streamArn, table.name.apply(param => AWS$ReventlessAws.DynamoDbStream.service), streamArn.apply(sourceUrn => ({
48
+ TAG: "StreamSource",
49
+ sourceUrn: sourceUrn
50
+ })), undefined, undefined, Pulumi.output("aws:dynamodb:Stream"), undefined, undefined);
41
51
  }
42
52
 
43
53
  let log = Logger$ReventlessCore.fromEnv();
@@ -9,6 +9,10 @@
9
9
 
10
10
  module Platform = ReventlessInfra.Platform
11
11
 
12
+ // Where a caller acting as a given role discovers from — a group→url map. Named
13
+ // here as the shell reads it, the same way `manifestUrl` is.
14
+ let journeyManifestsKey = "journeyManifestUrls"
15
+
12
16
  // A mode's options, flattened to the wire shape. They are payloads of their arm
13
17
  // on the deploy side (so `mapStyle` with the map off cannot be expressed) and
14
18
  // flat siblings of `viewModes` on the wire (because that is where the released
@@ -52,12 +56,28 @@ let fields = (
52
56
  // decides where the file goes — a passthrough could point the shell at a key
53
57
  // nothing writes, and a statically-discovered shell has no admin API behind it
54
58
  // to notice.
55
- bakedManifest->Option.forEach(bake =>
59
+ bakedManifest->Option.forEach(bake => {
56
60
  out->Dict.set(
57
61
  "manifestUrl",
58
62
  JSON.Encode.string(ReventlessCore.Platform_BakedManifest.urlForKey(bake.key)),
59
63
  )
60
- )
64
+ // Where a caller acting as a given role discovers from, beside the default
65
+ // rather than instead of it: `manifestUrl` stays the default journey, which
66
+ // is what a caller matching no declared group gets. Omitted entirely when
67
+ // nothing is declared, so a single-audience deployment writes the key set it
68
+ // always did and a shell that has never heard of journeys sees no new key.
69
+ switch ReventlessCore.Platform_BakedManifest.journeyUrls(~config=bake) {
70
+ | [] => ()
71
+ | urls =>
72
+ out->Dict.set(
73
+ journeyManifestsKey,
74
+ urls
75
+ ->Array.map(((group, url)) => (group, JSON.Encode.string(url)))
76
+ ->Dict.fromArray
77
+ ->JSON.Encode.object,
78
+ )
79
+ }
80
+ })
61
81
 
62
82
  switch viewModes {
63
83
  | Some(modes) =>
@@ -6,6 +6,8 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
6
  import * as Platform$ReventlessInfra from "@reventlessdev/reventless-infra/src/types/Platform.res.mjs";
7
7
  import * as Platform_BakedManifest$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_BakedManifest.res.mjs";
8
8
 
9
+ let journeyManifestsKey = "journeyManifestUrls";
10
+
9
11
  function modeOptions(mode) {
10
12
  if (mode.TAG === "Map") {
11
13
  let style = mode._0.style;
@@ -33,6 +35,14 @@ function fields(computed, viewModes, bakedManifest, shellConfig) {
33
35
  let out = Object.fromEntries(computed);
34
36
  Stdlib_Option.forEach(bakedManifest, bake => {
35
37
  out["manifestUrl"] = Platform_BakedManifest$ReventlessCore.urlForKey(bake.key);
38
+ let urls = Platform_BakedManifest$ReventlessCore.journeyUrls(bake);
39
+ if (urls.length !== 0) {
40
+ out[journeyManifestsKey] = Object.fromEntries(urls.map(param => [
41
+ param[0],
42
+ param[1]
43
+ ]));
44
+ return;
45
+ }
36
46
  });
37
47
  if (viewModes !== undefined) {
38
48
  out["viewModes"] = viewModes.map(Platform$ReventlessInfra.viewModeToString);
@@ -58,6 +68,7 @@ let Platform;
58
68
 
59
69
  export {
60
70
  Platform,
71
+ journeyManifestsKey,
61
72
  modeOptions,
62
73
  fields,
63
74
  }
@@ -52,6 +52,8 @@ let ops: QueryDb_Adapter.operations = {
52
52
  deleteBatch: Obj.magic(0),
53
53
  }
54
54
 
55
+ let lastRetiredScope: ref<option<Reventless.OwnerScope.retiredScope>> = ref(None)
56
+
55
57
  let pushdowns: PgQueryResolver_Lambda.pushdowns = {
56
58
  indexLookup: async (~readModelName as _, field, value) =>
57
59
  allItems()->Array.filter(i => fieldEq(i, field, value)),
@@ -63,11 +65,20 @@ let pushdowns: PgQueryResolver_Lambda.pushdowns = {
63
65
  ~capability as _,
64
66
  ~labelField as _,
65
67
  ~ownerScope: option<(string, string)>=?,
68
+ ~retiredScope: option<Reventless.OwnerScope.retiredScope>=?,
66
69
  ) => {
67
70
  lastOwnerScope := ownerScope
71
+ lastRetiredScope := retiredScope
68
72
  listPageReturn.contents
69
73
  },
70
- itemsPage: async (~readModelName as _, ~subIdField as _, ~id, ~argsDict as _, ~ownerScope as _=?) =>
74
+ itemsPage: async (
75
+ ~readModelName as _,
76
+ ~subIdField as _,
77
+ ~id,
78
+ ~argsDict as _,
79
+ ~ownerScope as _=?,
80
+ ~retiredScope as _=?,
81
+ ) =>
71
82
  // Sentinel echoing the requested id, so the dispatch routing is observable.
72
83
  JSON.Encode.object(Dict.fromArray([("itemsFor", JSON.Encode.string(id))])),
73
84
  scanAll: async (~readModelName as _) => allItems(),
@@ -82,6 +93,8 @@ let makeBinding = (
82
93
  ~authorization=Reventless.Authorization.AllowAnonymous,
83
94
  ~subIdField=None,
84
95
  ~ownerField=None,
96
+ ~retiredField=None,
97
+ ~retiredValues=None,
85
98
  (),
86
99
  ): PgQueryResolver_Lambda.binding => {
87
100
  ops,
@@ -96,6 +109,8 @@ let makeBinding = (
96
109
  includeIdParam: true,
97
110
  authorization,
98
111
  ownerField,
112
+ retiredField,
113
+ retiredValues,
99
114
  }
100
115
 
101
116
  let mkPayload = (
@@ -68,6 +68,10 @@ let ops = {
68
68
  deleteBatch: 0
69
69
  };
70
70
 
71
+ let lastRetiredScope = {
72
+ contents: undefined
73
+ };
74
+
71
75
  async function pushdowns_indexLookup(param, field, value) {
72
76
  return Object.values(store).filter(i => fieldEq(i, field, value));
73
77
  }
@@ -76,12 +80,13 @@ async function pushdowns_byIds(param, ids) {
76
80
  return Stdlib_Array.filterMap(ids, id => store[id]);
77
81
  }
78
82
 
79
- async function pushdowns_listPage(param, param$1, param$2, param$3, ownerScope) {
83
+ async function pushdowns_listPage(param, param$1, param$2, param$3, ownerScope, retiredScope) {
80
84
  lastOwnerScope.contents = ownerScope;
85
+ lastRetiredScope.contents = retiredScope;
81
86
  return listPageReturn.contents;
82
87
  }
83
88
 
84
- async function pushdowns_itemsPage(param, param$1, id, param$2, param$3) {
89
+ async function pushdowns_itemsPage(param, param$1, id, param$2, param$3, param$4) {
85
90
  return Object.fromEntries([[
86
91
  "itemsFor",
87
92
  id
@@ -116,10 +121,12 @@ let capability = {
116
121
  sortFields: capability_sortFields
117
122
  };
118
123
 
119
- function makeBinding(authorizationOpt, subIdFieldOpt, ownerFieldOpt, param) {
124
+ function makeBinding(authorizationOpt, subIdFieldOpt, ownerFieldOpt, retiredFieldOpt, retiredValuesOpt, param) {
120
125
  let authorization = authorizationOpt !== undefined ? authorizationOpt : "AllowAnonymous";
121
126
  let subIdField = subIdFieldOpt !== undefined ? Primitive_option.valFromOption(subIdFieldOpt) : undefined;
122
127
  let ownerField = ownerFieldOpt !== undefined ? Primitive_option.valFromOption(ownerFieldOpt) : undefined;
128
+ let retiredField = retiredFieldOpt !== undefined ? Primitive_option.valFromOption(retiredFieldOpt) : undefined;
129
+ let retiredValues = retiredValuesOpt !== undefined ? Primitive_option.valFromOption(retiredValuesOpt) : undefined;
123
130
  return {
124
131
  ops: ops,
125
132
  pushdowns: pushdowns,
@@ -134,7 +141,9 @@ function makeBinding(authorizationOpt, subIdFieldOpt, ownerFieldOpt, param) {
134
141
  labelField: "name",
135
142
  includeIdParam: true,
136
143
  authorization: authorization,
137
- ownerField: ownerField
144
+ ownerField: ownerField,
145
+ retiredField: retiredField,
146
+ retiredValues: retiredValues
138
147
  };
139
148
  }
140
149
 
@@ -177,7 +186,7 @@ function ids(arr) {
177
186
 
178
187
  globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
179
188
  globalThis.test("getById returns the item (id-carrying)", async () => {
180
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
189
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
181
190
  "id",
182
191
  "p-2"
183
192
  ]]), undefined, undefined));
@@ -185,14 +194,14 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
185
194
  globalThis.expect(str(r, "name")).toEqual("Bravo");
186
195
  });
187
196
  globalThis.test("getById miss → null", async () => {
188
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
197
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
189
198
  "id",
190
199
  "absent"
191
200
  ]]), undefined, undefined));
192
201
  globalThis.expect(r).toBe(null);
193
202
  });
194
203
  globalThis.test("byIds returns matches, drops missing", async () => {
195
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("byIds", undefined, Object.fromEntries([[
204
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("byIds", undefined, Object.fromEntries([[
196
205
  "ids",
197
206
  [
198
207
  "p-1",
@@ -206,7 +215,7 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
206
215
  ]);
207
216
  });
208
217
  globalThis.test("index routes to indexLookup on the idField", async () => {
209
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("index", "byStatus", Object.fromEntries([[
218
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("index", "byStatus", Object.fromEntries([[
210
219
  "byStatus",
211
220
  "active"
212
221
  ]]), undefined, undefined));
@@ -217,13 +226,13 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
217
226
  });
218
227
  globalThis.test("list with push-down → returns the connection listPage gives", async () => {
219
228
  listPageReturn.contents = "PUSHED";
220
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("list", undefined, undefined, undefined, undefined));
229
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("list", undefined, undefined, undefined, undefined));
221
230
  listPageReturn.contents = undefined;
222
231
  globalThis.expect(r).toEqual("PUSHED");
223
232
  });
224
233
  globalThis.test("list without push-down → falls back to the shared spec", async () => {
225
234
  listPageReturn.contents = undefined;
226
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("list", undefined, undefined, undefined, undefined));
235
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("list", undefined, undefined, undefined, undefined));
227
236
  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")));
228
237
  globalThis.expect(edgeIds).toEqual([
229
238
  "p-1",
@@ -232,21 +241,21 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
232
241
  ]);
233
242
  });
234
243
  globalThis.test("items with subId routes to itemsPage (id echoed)", async () => {
235
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, "seq", undefined, undefined), undefined, mkPayload("items", undefined, Object.fromEntries([[
244
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, "seq", undefined, undefined, undefined, undefined), undefined, mkPayload("items", undefined, Object.fromEntries([[
236
245
  "id",
237
246
  "p-2"
238
247
  ]]), undefined, undefined));
239
248
  globalThis.expect(str(r, "itemsFor")).toEqual("p-2");
240
249
  });
241
250
  globalThis.test("items without subId → empty connection", async () => {
242
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("items", undefined, Object.fromEntries([[
251
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("items", undefined, Object.fromEntries([[
243
252
  "id",
244
253
  "p-2"
245
254
  ]]), undefined, undefined));
246
255
  globalThis.expect(Stdlib_Option.getOr(Stdlib_Option.flatMap(field(r, "edges"), Stdlib_JSON.Decode.array), []).length).toBe(0);
247
256
  });
248
257
  globalThis.test("authorization DenyAll → empty shape per kind", async () => {
249
- let b = makeBinding("DenyAll", undefined, undefined, undefined);
258
+ let b = makeBinding("DenyAll", undefined, undefined, undefined, undefined, undefined);
250
259
  let single = await PgQueryResolver_Lambda$ReventlessAws.dispatch(b, undefined, mkPayload("getById", undefined, Object.fromEntries([[
251
260
  "id",
252
261
  "p-1"
@@ -265,7 +274,7 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
265
274
  TAG: "Deny",
266
275
  _0: "nope"
267
276
  }));
268
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
277
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
269
278
  "id",
270
279
  "p-1"
271
280
  ]]), undefined, undefined));
@@ -291,7 +300,7 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
291
300
  arguments: payload_arguments,
292
301
  identity: Identity$Reventless.anonymous
293
302
  };
294
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, payload);
303
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), undefined, payload);
295
304
  globalThis.expect(str(r, "id")).toEqual("p-2");
296
305
  });
297
306
  globalThis.test("resolveOne multi via target index → array", async () => {
@@ -317,7 +326,7 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
317
326
  arguments: payload_arguments,
318
327
  identity: Identity$Reventless.anonymous
319
328
  };
320
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, payload);
329
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), undefined, payload);
321
330
  globalThis.expect(ids(r)).toEqual([
322
331
  "p-1",
323
332
  "p-3"
@@ -344,14 +353,14 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
344
353
  arguments: payload_arguments,
345
354
  identity: Identity$Reventless.anonymous
346
355
  };
347
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, payload);
356
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), undefined, payload);
348
357
  globalThis.expect(ids(r)).toEqual([
349
358
  "p-1",
350
359
  "p-3"
351
360
  ]);
352
361
  });
353
362
  globalThis.test("node decodes global id → __typename + item", async () => {
354
- PgQueryResolver_Lambda$ReventlessAws.register("Things", makeBinding(undefined, undefined, undefined, undefined));
363
+ PgQueryResolver_Lambda$ReventlessAws.register("Things", makeBinding(undefined, undefined, undefined, undefined, undefined, undefined));
355
364
  PgQueryResolver_Lambda$ReventlessAws.registerNodeType("Thing", "Things");
356
365
  let payload_arguments = Object.fromEntries([[
357
366
  "id",
@@ -401,7 +410,7 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
401
410
  delete: 0,
402
411
  deleteBatch: 0
403
412
  };
404
- let init = makeBinding(undefined, undefined, undefined, undefined);
413
+ let init = makeBinding(undefined, undefined, undefined, undefined, undefined, undefined);
405
414
  let authBinding_pushdowns = init.pushdowns;
406
415
  let authBinding_indexes = init.indexes;
407
416
  let authBinding_subIdField = init.subIdField;
@@ -410,6 +419,8 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
410
419
  let authBinding_includeIdParam = init.includeIdParam;
411
420
  let authBinding_authorization = init.authorization;
412
421
  let authBinding_ownerField = init.ownerField;
422
+ let authBinding_retiredField = init.retiredField;
423
+ let authBinding_retiredValues = init.retiredValues;
413
424
  let authBinding = {
414
425
  ops: authOps,
415
426
  pushdowns: authBinding_pushdowns,
@@ -419,7 +430,9 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
419
430
  labelField: authBinding_labelField,
420
431
  includeIdParam: authBinding_includeIdParam,
421
432
  authorization: authBinding_authorization,
422
- ownerField: authBinding_ownerField
433
+ ownerField: authBinding_ownerField,
434
+ retiredField: authBinding_retiredField,
435
+ retiredValues: authBinding_retiredValues
423
436
  };
424
437
  let lookupAuth = name => {
425
438
  if (name === "AuthTable") {
@@ -445,18 +458,18 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
445
458
  identity: ident(username, groups)
446
459
  });
447
460
  globalThis.test("auth index: group member who owns → results", async () => {
448
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), lookupAuth, authIndexPayload("alice", ["Owner"]));
461
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), lookupAuth, authIndexPayload("alice", ["Owner"]));
449
462
  globalThis.expect(ids(r)).toEqual([
450
463
  "p-1",
451
464
  "p-3"
452
465
  ]);
453
466
  });
454
467
  globalThis.test("auth index: group member who does NOT own → empty", async () => {
455
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), lookupAuth, authIndexPayload("bob", ["Owner"]));
468
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), lookupAuth, authIndexPayload("bob", ["Owner"]));
456
469
  globalThis.expect(Stdlib_Option.getOr(Stdlib_JSON.Decode.array(r), []).length).toBe(0);
457
470
  });
458
471
  globalThis.test("auth index: non-member passes through → results", async () => {
459
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), lookupAuth, authIndexPayload("bob", []));
472
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), lookupAuth, authIndexPayload("bob", []));
460
473
  globalThis.expect(ids(r)).toEqual([
461
474
  "p-1",
462
475
  "p-3"
@@ -465,7 +478,7 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
465
478
  globalThis.test("unmapped kind throws", async () => {
466
479
  let threw;
467
480
  try {
468
- await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("frobnicate", undefined, undefined, undefined, undefined));
481
+ await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("frobnicate", undefined, undefined, undefined, undefined));
469
482
  threw = false;
470
483
  } catch (exn) {
471
484
  threw = true;
@@ -476,35 +489,35 @@ globalThis.describe("PgQueryResolver_Lambda.dispatch", () => {
476
489
 
477
490
  globalThis.describe("id form — the typed doors take either", () => {
478
491
  globalThis.test("getById resolves a Relay global id", async () => {
479
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
492
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
480
493
  "id",
481
494
  Api_Ids$ReventlessCore.encode("Thing", "p-2")
482
495
  ]]), undefined, undefined));
483
496
  globalThis.expect(str(r, "name")).toEqual("Bravo");
484
497
  });
485
498
  globalThis.test("getById reports the storage key it resolved to", async () => {
486
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
499
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
487
500
  "id",
488
501
  Api_Ids$ReventlessCore.encode("Thing", "p-2")
489
502
  ]]), undefined, undefined));
490
503
  globalThis.expect(str(r, "id")).toEqual("p-2");
491
504
  });
492
505
  globalThis.test("getById still resolves a plain storage key", async () => {
493
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
506
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
494
507
  "id",
495
508
  "p-2"
496
509
  ]]), undefined, undefined));
497
510
  globalThis.expect(str(r, "name")).toEqual("Bravo");
498
511
  });
499
512
  globalThis.test("getById on a global id for a row that does not exist is still null", async () => {
500
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
513
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("getById", undefined, Object.fromEntries([[
501
514
  "id",
502
515
  Api_Ids$ReventlessCore.encode("Thing", "absent")
503
516
  ]]), undefined, undefined));
504
517
  globalThis.expect(r).toBe(null);
505
518
  });
506
519
  globalThis.test("byIds accepts the two forms mixed in one call", async () => {
507
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("byIds", undefined, Object.fromEntries([[
520
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("byIds", undefined, Object.fromEntries([[
508
521
  "ids",
509
522
  [
510
523
  "p-1",
@@ -517,7 +530,7 @@ globalThis.describe("id form — the typed doors take either", () => {
517
530
  ]);
518
531
  });
519
532
  globalThis.test("byIds with every id found does not double-count", async () => {
520
- let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined), undefined, mkPayload("byIds", undefined, Object.fromEntries([[
533
+ let r = await PgQueryResolver_Lambda$ReventlessAws.dispatch(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), undefined, mkPayload("byIds", undefined, Object.fromEntries([[
521
534
  "ids",
522
535
  [
523
536
  "p-1",
@@ -532,7 +545,7 @@ globalThis.describe("id form — the typed doors take either", () => {
532
545
  });
533
546
 
534
547
  globalThis.describe("owner scoping", () => {
535
- let ownedBinding = () => makeBinding(undefined, undefined, "owner", undefined);
548
+ let ownedBinding = () => makeBinding(undefined, undefined, "owner", undefined, undefined, undefined);
536
549
  let shopper = cognito("u-a", ["User"]);
537
550
  let listWith = async (binding, identity) => {
538
551
  lastOwnerScope.contents = undefined;
@@ -557,7 +570,7 @@ globalThis.describe("owner scoping", () => {
557
570
  globalThis.expect(match[1]).toBe(undefined);
558
571
  });
559
572
  globalThis.test("a view with no owner field is never scoped", async () => {
560
- let match = await listWith(makeBinding(undefined, undefined, undefined, undefined), shopper);
573
+ let match = await listWith(makeBinding(undefined, undefined, undefined, undefined, undefined, undefined), shopper);
561
574
  globalThis.expect(match[1]).toBe(undefined);
562
575
  });
563
576
  globalThis.test("an unidentified caller is refused before the push-down runs", async () => {
@@ -620,6 +633,7 @@ export {
620
633
  listPageReturn,
621
634
  lastOwnerScope,
622
635
  ops,
636
+ lastRetiredScope,
623
637
  pushdowns,
624
638
  capability,
625
639
  makeBinding,