@reventlessdev/reventless-aws 3.0.0-alpha.370 → 3.0.0-alpha.371

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.371 (2026-10-01)
7
+
8
+ ### Bug Fixes
9
+
10
+ * check a plugin's roles against the user pool's groups ([ddb3c42](https://github.com/ReventlessDev/reventless-core/commit/ddb3c422b7226a19800e781abd5f03af18867007))
11
+
12
+
6
13
  # 3.0.0-alpha.370 (2026-10-01)
7
14
 
8
15
  * feat!: roles a plugin declares, groups a platform provides ([43daace](https://github.com/ReventlessDev/reventless-core/commit/43daace6ca588afe22562d079af1e6f1da81942e))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-aws",
3
- "version": "3.0.0-alpha.370",
3
+ "version": "3.0.0-alpha.371",
4
4
  "description": "AWS adapters for Reventless",
5
5
  "license": "Apache-2.0",
6
6
  "bin": {
@@ -21,19 +21,19 @@
21
21
  "sury": "11.0.0",
22
22
  "uuid": "^13.0.0",
23
23
  "yaml": "^2.8.3",
24
- "@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.27",
24
+ "@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.28",
25
25
  "@reventlessdev/rescript-effect": "0.1.0-alpha.34",
26
- "@reventlessdev/rescript-jest": "1.0.0-alpha.11",
27
26
  "@reventlessdev/rescript-node": "2.0.0-alpha.20",
28
- "@reventlessdev/rescript-pulumi-aws": "3.0.0-alpha.22",
27
+ "@reventlessdev/rescript-jest": "1.0.0-alpha.11",
28
+ "@reventlessdev/rescript-pulumi-aws": "3.0.0-alpha.23",
29
+ "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.23",
29
30
  "@reventlessdev/rescript-uuid": "2.0.0-alpha.1",
30
- "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.22",
31
- "@reventlessdev/reventless-core": "3.0.0-alpha.286",
32
31
  "@reventlessdev/rescript-web": "1.0.0-alpha.7",
33
- "@reventlessdev/reventless-interop": "3.0.0-alpha.38",
34
- "@reventlessdev/reventless-postgres": "3.0.0-alpha.150",
35
- "@reventlessdev/reventless-spec": "3.0.0-alpha.156",
36
- "@reventlessdev/reventless-infra": "3.0.0-alpha.184"
32
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.185",
33
+ "@reventlessdev/reventless-core": "3.0.0-alpha.287",
34
+ "@reventlessdev/reventless-postgres": "3.0.0-alpha.151",
35
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.157",
36
+ "@reventlessdev/reventless-interop": "3.0.0-alpha.39"
37
37
  },
38
38
  "devDependencies": {
39
39
  "rescript": "12.3.1",
package/src/Platform.res CHANGED
@@ -112,11 +112,34 @@ let messagingEmailProviderRef: ref<Pulumi.Output.t<string>> = ref(Pulumi.Output.
112
112
  resolves roles while it is built, and a mapping stated later is refused. */
113
113
  let roleGroups = Reventless.Role.setGroups
114
114
 
115
- /** The groups of a user pool this deployment did not create, which no accounts
116
- manifest names. They count as provided by the check that every role a plugin
117
- needs has a group. */
115
+ /** Groups a root says its identity provider has, for a provider the platform
116
+ cannot list. Where it can (`loadProvidedGroups`), the listing decides, and a
117
+ declared group the pool lacks fails the deploy. */
118
118
  let providedGroups = Reventless.Role.provideGroups
119
119
 
120
+ /** The platform stack a plugin stack deploys against (`platform:stack`), or
121
+ `None` in a program that is its own platform. One reference per program:
122
+ a second with the same name is a duplicate resource. */
123
+ let platformStackRefCache: ref<option<option<Pulumi.StackReference.t>>> = ref(None)
124
+
125
+ let platformStackReference = (): option<Pulumi.StackReference.t> =>
126
+ switch platformStackRefCache.contents {
127
+ | Some(stackRef) => stackRef
128
+ | None =>
129
+ let stackRef =
130
+ Pulumi.Config.make(Some("platform"))
131
+ ->Pulumi.Config.get("stack")
132
+ ->Option.map(stack => Pulumi.StackReference.make(stack))
133
+ platformStackRefCache := Some(stackRef)
134
+ stackRef
135
+ }
136
+
137
+ /** Ask the user pool which groups it has, so the role check compares each
138
+ plugin's roles with the pool. A plugin's deploy program calls `deployPlugin`
139
+ once this resolves; see [Platform_ProvidedGroups]. */
140
+ let loadProvidedGroups = (): promise<unit> =>
141
+ Platform_ProvidedGroups.load(~stackRef=platformStackReference())
142
+
120
143
  /** The group a role resolves to — the name this platform writes into its
121
144
  directives, its shell configuration and the elevated groups. For code that
122
145
  grants access by group, so it never spells one itself. */
@@ -164,10 +187,10 @@ module MakeWithConfig = (
164
187
  // [Reventless.OwnerScope.defaultElevatedGroups].
165
188
  Reventless.OwnerScope.defaultElevatedRoles([Reventless.Role.admin])
166
189
 
167
- // What this deployment provides for the roles its plugins need: the
168
- // administrator group the stack declares, and the groups of the accounts
169
- // provisioning creates. Read when each plugin is built, after the root has
170
- // mapped its roles.
190
+ // What this deployment provides for the roles its plugins need, where the pool
191
+ // was not listed (`loadProvidedGroups`): the administrator group the stack
192
+ // declares, and the groups of the accounts provisioning creates. Read when each
193
+ // plugin is built, after the root has mapped its roles.
171
194
  Reventless.Role.provideGroupsFrom(() =>
172
195
  [Reventless.Role.adminGroup()]->Array.concat(Reventless.AccountsManifest.declaredGroups())
173
196
  )
@@ -228,10 +251,7 @@ module MakeWithConfig = (
228
251
  // Determine API source based on platform:stack config.
229
252
  // - Platform/monolithic mode (no config): create a real AppSync API resource.
230
253
  // - Plugin mode (config set): reference the platform's shared API via StackReference.
231
- let platformStackRef =
232
- Pulumi.Config.make(Some("platform"))
233
- ->Pulumi.Config.get("stack")
234
- ->Option.map(stack => Pulumi.StackReference.make(stack))
254
+ let platformStackRef = platformStackReference()
235
255
 
236
256
  // The declared object stores, as the upload claimer needs them: qualified
237
257
  // name, physical bucket, served prefix.
@@ -80,6 +80,7 @@ import * as EventTopicPublisher_SNS$ReventlessAws from "./adapter/EventTopic/Eve
80
80
  import * as ExtensionPointMapping$ReventlessInfra from "@reventlessdev/reventless-infra/src/types/ExtensionPointMapping.res.mjs";
81
81
  import * as PgQueryResolver_Builder$ReventlessAws from "./adapter/QueryDb/PgQueryResolver_Builder.res.mjs";
82
82
  import * as Platform_BakedManifest$ReventlessCore from "@reventlessdev/reventless-core/src/admin/Platform_BakedManifest.res.mjs";
83
+ import * as Platform_ProvidedGroups$ReventlessAws from "./Platform_ProvidedGroups.res.mjs";
83
84
  import * as UiFragments_Projection$ReventlessCore from "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateView/UiFragments_Projection.res.mjs";
84
85
  import * as Aggregate_Builder_Single$ReventlessAws from "./components/Aggregate_Builder_Single.res.mjs";
85
86
  import * as ReadModel_Builder_Single$ReventlessAws from "./components/ReadModel_Builder_Single.res.mjs";
@@ -152,6 +153,24 @@ let messagingEmailProviderRef = {
152
153
  contents: Pulumi.output("")
153
154
  };
154
155
 
156
+ let platformStackRefCache = {
157
+ contents: undefined
158
+ };
159
+
160
+ function platformStackReference() {
161
+ let stackRef = platformStackRefCache.contents;
162
+ if (stackRef !== undefined) {
163
+ return Primitive_option.valFromOption(stackRef);
164
+ }
165
+ let stackRef$1 = Stdlib_Option.map(new Pulumi.Config("platform").get("stack"), stack => new Pulumi.StackReference(stack));
166
+ platformStackRefCache.contents = Primitive_option.some(stackRef$1);
167
+ return stackRef$1;
168
+ }
169
+
170
+ function loadProvidedGroups() {
171
+ return Platform_ProvidedGroups$ReventlessAws.load(platformStackReference());
172
+ }
173
+
155
174
  function MakeWithConfig(Config) {
156
175
  OwnerScope$Reventless.defaultElevatedRoles([Role$Reventless.admin]);
157
176
  Role$Reventless.provideGroupsFrom(() => [Role$Reventless.adminGroup()].concat(AccountsManifest$Reventless.declaredGroups()));
@@ -183,7 +202,7 @@ function MakeWithConfig(Config) {
183
202
  let currentDeployTarget = {
184
203
  contents: "Domain"
185
204
  };
186
- let platformStackRef = Stdlib_Option.map(new Pulumi.Config("platform").get("stack"), stack => new Pulumi.StackReference(stack));
205
+ let platformStackRef = platformStackReference();
187
206
  let claimStores = () => {
188
207
  if (platformStackRef !== undefined) {
189
208
  return Primitive_option.valFromOption(platformStackRef).getOutput("objectStores").apply(objectStores => Stdlib_Array.filterMap(Object.entries(Stdlib_Option.getOr(Stdlib_Option.flatMap(objectStores, Stdlib_JSON.Decode.object), {})), param => {
@@ -1599,7 +1618,7 @@ function Make($star) {
1599
1618
  let currentDeployTarget = {
1600
1619
  contents: "Domain"
1601
1620
  };
1602
- let platformStackRef = Stdlib_Option.map(new Pulumi.Config("platform").get("stack"), stack => new Pulumi.StackReference(stack));
1621
+ let platformStackRef = platformStackReference();
1603
1622
  let claimStores = () => {
1604
1623
  if (platformStackRef !== undefined) {
1605
1624
  return Primitive_option.valFromOption(platformStackRef).getOutput("objectStores").apply(objectStores => Stdlib_Array.filterMap(Object.entries(Stdlib_Option.getOr(Stdlib_Option.flatMap(objectStores, Stdlib_JSON.Decode.object), {})), param => {
@@ -2984,6 +3003,9 @@ export {
2984
3003
  messagingEmailProviderRef,
2985
3004
  roleGroups,
2986
3005
  providedGroups,
3006
+ platformStackRefCache,
3007
+ platformStackReference,
3008
+ loadProvidedGroups,
2987
3009
  groupOf,
2988
3010
  adminGroup,
2989
3011
  MakeWithConfig,
@@ -0,0 +1,97 @@
1
+ /***
2
+ The user pool's groups, read before a plugin is built so the role check compares
3
+ its roles with the pool, not a manifest (`Role.provideListedGroups`). A group the
4
+ pool lacks refuses every caller, which nothing at runtime can tell from a caller
5
+ without the role. Works in a preview: `ListGroups` is a read, and the pool id is
6
+ read outside any `Output.apply`.
7
+ */
8
+
9
+ module Cognito = AwsSdk.CognitoIdentityServiceProvider
10
+
11
+ type pool = {id: string, region: option<string>}
12
+
13
+ type send = Cognito.ListGroupsCommand.input => promise<Cognito.ListGroupsCommand.output>
14
+
15
+ /** Every group of the pool, following `NextToken` across pages. */
16
+ let listAll = async (~send: send, ~userPoolId: string): array<string> => {
17
+ let rec page = async (nextToken, acc) => {
18
+ let out = await send({userPoolId, limit: 60, ?nextToken})
19
+ let names = out.groups->Option.getOr([])->Array.filterMap(g => g.groupName)
20
+ let acc = acc->Array.concat(names)
21
+ switch out.nextToken {
22
+ | Some(_) as next => await page(next, acc)
23
+ | None => acc
24
+ }
25
+ }
26
+ await page(None, [])
27
+ }
28
+
29
+ /** A plugin stack's pool, from the platform stack's exports. The new key before
30
+ the old one, and each directly before under `default`, where ESM programs
31
+ nest their exports. */
32
+ let poolFromExports = (~get: string => option<JSON.t>): option<pool> => {
33
+ let nested = get("default")->Option.flatMap(JSON.Decode.object)
34
+ let read = key =>
35
+ get(key)
36
+ ->Option.orElse(nested->Option.flatMap(d => d->Dict.get(key)))
37
+ ->Option.flatMap(JSON.Decode.string)
38
+ read("identityProviderId")
39
+ ->Option.orElse(read("cognitoUserPoolId"))
40
+ ->Option.map(id => {
41
+ id,
42
+ region: read("identityProviderRegion")->Option.orElse(read("cognitoRegion")),
43
+ })
44
+ }
45
+
46
+ /**
47
+ The pool this program deploys against, or `None` where it cannot be listed yet:
48
+ a pool this program creates does not exist before its first deploy.
49
+
50
+ A plugin stack reads its platform stack's exports, the same ones its resolvers
51
+ use. A program that is its own platform reads a supplied pool's id from config.
52
+ */
53
+ let resolvePool = async (~stackRef: option<Pulumi.StackReference.t>): option<pool> =>
54
+ switch stackRef {
55
+ | Some(stackRef) =>
56
+ let keys = [
57
+ "identityProviderId",
58
+ "cognitoUserPoolId",
59
+ "identityProviderRegion",
60
+ "cognitoRegion",
61
+ "default",
62
+ ]
63
+ let values = await Promise.all(
64
+ keys->Array.map(key => stackRef->Pulumi.StackReference.getOutputValue(key)),
65
+ )
66
+ let exports = Dict.fromArray(
67
+ keys->Array.mapWithIndex((key, i) => (key, values->Array.getUnsafe(i))),
68
+ )
69
+ poolFromExports(~get=key => exports->Dict.get(key)->Option.flatMap(v => v))
70
+ | None =>
71
+ Platform_Stack._identityProviderId(
72
+ ~cfg=Pulumi.Config.make(Some("platform")),
73
+ )->Option.map(id => {
74
+ id,
75
+ region: Pulumi.Config.make(Some("aws"))->Pulumi.Config.get("region"),
76
+ })
77
+ }
78
+
79
+ let sendTo = (~region: option<string>): send => {
80
+ let client = Cognito.Raw.client(~options={region: ?region})
81
+ input => Cognito.ListGroupsCommand.make(input)->Cognito.ListGroupsCommand.Raw.send(client, _)
82
+ }
83
+
84
+ /** List the pool's groups and record them for the role check. Leaves the check
85
+ on its other sources where there is no pool to ask yet. */
86
+ let load = async (~stackRef: option<Pulumi.StackReference.t>): unit =>
87
+ switch await resolvePool(~stackRef) {
88
+ | None => ()
89
+ | Some({id, region}) =>
90
+ let groups = try await listAll(~send=sendTo(~region), ~userPoolId=id) catch {
91
+ | exn if exn->Util_AwsError.hasCode(~code="AccessDeniedException") =>
92
+ JsError.throwWithMessage(
93
+ `Cannot list the groups of the user pool ${id}: the deploying principal needs ` ++ `cognito-idp:ListGroups on it, so the deploy can check that every role a plugin needs has a group.`,
94
+ )
95
+ }
96
+ Reventless.Role.provideListedGroups(~source=`the user pool ${id}`, groups)
97
+ }
@@ -0,0 +1,103 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
4
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
5
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
+ import * as Pulumi from "@pulumi/pulumi";
7
+ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
8
+ import * as Role$Reventless from "@reventlessdev/reventless-spec/src/types/Role.res.mjs";
9
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
10
+ import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
11
+ import * as Util_AwsError$ReventlessAws from "./util/Util_AwsError.res.mjs";
12
+ import * as Platform_Stack$ReventlessAws from "./Platform_Stack.res.mjs";
13
+ import * as ClientCognitoIdentityProvider from "@aws-sdk/client-cognito-identity-provider";
14
+
15
+ async function listAll(send, userPoolId) {
16
+ let page = async (nextToken, acc) => {
17
+ let out = await send({
18
+ UserPoolId: userPoolId,
19
+ Limit: 60,
20
+ NextToken: nextToken
21
+ });
22
+ let names = Stdlib_Array.filterMap(Stdlib_Option.getOr(out.Groups, []), g => g.GroupName);
23
+ let acc$1 = acc.concat(names);
24
+ let next = out.NextToken;
25
+ if (next !== undefined) {
26
+ return await page(next, acc$1);
27
+ } else {
28
+ return acc$1;
29
+ }
30
+ };
31
+ return await page(undefined, []);
32
+ }
33
+
34
+ function poolFromExports(get) {
35
+ let nested = Stdlib_Option.flatMap(get("default"), Stdlib_JSON.Decode.object);
36
+ let read = key => Stdlib_Option.flatMap(Stdlib_Option.orElse(get(key), Stdlib_Option.flatMap(nested, d => d[key])), Stdlib_JSON.Decode.string);
37
+ return Stdlib_Option.map(Stdlib_Option.orElse(read("identityProviderId"), read("cognitoUserPoolId")), id => ({
38
+ id: id,
39
+ region: Stdlib_Option.orElse(read("identityProviderRegion"), read("cognitoRegion"))
40
+ }));
41
+ }
42
+
43
+ async function resolvePool(stackRef) {
44
+ if (stackRef === undefined) {
45
+ return Stdlib_Option.map(Platform_Stack$ReventlessAws._identityProviderId(new Pulumi.Config("platform")), id => ({
46
+ id: id,
47
+ region: new Pulumi.Config("aws").get("region")
48
+ }));
49
+ }
50
+ let stackRef$1 = Primitive_option.valFromOption(stackRef);
51
+ let keys = [
52
+ "identityProviderId",
53
+ "cognitoUserPoolId",
54
+ "identityProviderRegion",
55
+ "cognitoRegion",
56
+ "default"
57
+ ];
58
+ let values = await Promise.all(keys.map(key => stackRef$1.getOutputValue(key)));
59
+ let $$exports = Object.fromEntries(keys.map((key, i) => [
60
+ key,
61
+ values[i]
62
+ ]));
63
+ return poolFromExports(key => Stdlib_Option.flatMap($$exports[key], v => v));
64
+ }
65
+
66
+ function sendTo(region) {
67
+ let client = new ClientCognitoIdentityProvider.CognitoIdentityProviderClient({
68
+ region: region
69
+ });
70
+ return input => client.send(new ClientCognitoIdentityProvider.ListGroupsCommand(input));
71
+ }
72
+
73
+ async function load(stackRef) {
74
+ let match = await resolvePool(stackRef);
75
+ if (match === undefined) {
76
+ return;
77
+ }
78
+ let id = match.id;
79
+ let groups;
80
+ try {
81
+ groups = await listAll(sendTo(match.region), id);
82
+ } catch (raw_exn) {
83
+ let exn = Primitive_exceptions.internalToException(raw_exn);
84
+ if (Util_AwsError$ReventlessAws.hasCode(exn, "AccessDeniedException")) {
85
+ groups = Stdlib_JsError.throwWithMessage(`Cannot list the groups of the user pool ` + id + `: the deploying principal needs cognito-idp:ListGroups on it, so the deploy can check that every role a plugin needs has a group.`);
86
+ } else {
87
+ throw exn;
88
+ }
89
+ }
90
+ return Role$Reventless.provideListedGroups(`the user pool ` + id, groups);
91
+ }
92
+
93
+ let Cognito;
94
+
95
+ export {
96
+ Cognito,
97
+ listAll,
98
+ poolFromExports,
99
+ resolvePool,
100
+ sendTo,
101
+ load,
102
+ }
103
+ /* @pulumi/pulumi Not a pure module */
@@ -0,0 +1,52 @@
1
+ open JestGlobals
2
+
3
+ // What the role check compares a plugin's roles with on AWS: the groups the user
4
+ // pool reports, found through the platform stack's exports.
5
+
6
+ module Cognito = AwsSdk.CognitoIdentityServiceProvider
7
+
8
+ let group = (name): Cognito.ListGroupsCommand.group => {groupName: name}
9
+
10
+ describe("Platform_ProvidedGroups.listAll", () => {
11
+ test("follows NextToken until the last page", async () => {
12
+ let seen = []
13
+ let send = async (input: Cognito.ListGroupsCommand.input): Cognito.ListGroupsCommand.output => {
14
+ seen->Array.push(input.nextToken)
15
+ switch input.nextToken {
16
+ | None => {groups: [group("Admin"), group("Shopper")], nextToken: "p2"}
17
+ | Some(_) => {groups: [group("Merchandiser")]}
18
+ }
19
+ }
20
+ let groups = await Platform_ProvidedGroups.listAll(~send, ~userPoolId="p1")
21
+ expect((groups, seen))->toEqual((["Admin", "Shopper", "Merchandiser"], [None, Some("p2")]))
22
+ })
23
+ })
24
+
25
+ describe("Platform_ProvidedGroups.poolFromExports", () => {
26
+ let from = (pairs: array<(string, JSON.t)>) => {
27
+ let exports = Dict.fromArray(pairs)
28
+ Platform_ProvidedGroups.poolFromExports(~get=key => exports->Dict.get(key))
29
+ }
30
+
31
+ testSync("reads the pool id and region", () =>
32
+ expect(
33
+ from([
34
+ ("identityProviderId", JSON.Encode.string("p1")),
35
+ ("identityProviderRegion", JSON.Encode.string("eu-west-1")),
36
+ ]),
37
+ )->toEqual(Some({Platform_ProvidedGroups.id: "p1", region: Some("eu-west-1")}))
38
+ )
39
+
40
+ testSync("reads them nested under default, as an ESM platform exports them", () =>
41
+ expect(
42
+ from([
43
+ (
44
+ "default",
45
+ JSON.Encode.object(Dict.fromArray([("cognitoUserPoolId", JSON.Encode.string("p-old"))])),
46
+ ),
47
+ ]),
48
+ )->toEqual(Some({Platform_ProvidedGroups.id: "p-old", region: None}))
49
+ )
50
+
51
+ testSync("no pool id is no pool to list", () => expect(from([]))->toEqual(None))
52
+ })
@@ -0,0 +1,98 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Platform_ProvidedGroups$ReventlessAws from "../src/Platform_ProvidedGroups.res.mjs";
4
+
5
+ function group(name) {
6
+ return {
7
+ GroupName: name
8
+ };
9
+ }
10
+
11
+ globalThis.describe("Platform_ProvidedGroups.listAll", () => {
12
+ globalThis.test("follows NextToken until the last page", async () => {
13
+ let seen = [];
14
+ let send = async input => {
15
+ seen.push(input.NextToken);
16
+ let match = input.NextToken;
17
+ if (match !== undefined) {
18
+ return {
19
+ Groups: [{
20
+ GroupName: "Merchandiser"
21
+ }]
22
+ };
23
+ } else {
24
+ return {
25
+ Groups: [
26
+ {
27
+ GroupName: "Admin"
28
+ },
29
+ {
30
+ GroupName: "Shopper"
31
+ }
32
+ ],
33
+ NextToken: "p2"
34
+ };
35
+ }
36
+ };
37
+ let groups = await Platform_ProvidedGroups$ReventlessAws.listAll(send, "p1");
38
+ globalThis.expect([
39
+ groups,
40
+ seen
41
+ ]).toEqual([
42
+ [
43
+ "Admin",
44
+ "Shopper",
45
+ "Merchandiser"
46
+ ],
47
+ [
48
+ undefined,
49
+ "p2"
50
+ ]
51
+ ]);
52
+ });
53
+ });
54
+
55
+ globalThis.describe("Platform_ProvidedGroups.poolFromExports", () => {
56
+ let from = pairs => {
57
+ let $$exports = Object.fromEntries(pairs);
58
+ return Platform_ProvidedGroups$ReventlessAws.poolFromExports(key => $$exports[key]);
59
+ };
60
+ globalThis.test("reads the pool id and region", () => {
61
+ globalThis.expect(from([
62
+ [
63
+ "identityProviderId",
64
+ "p1"
65
+ ],
66
+ [
67
+ "identityProviderRegion",
68
+ "eu-west-1"
69
+ ]
70
+ ])).toEqual({
71
+ id: "p1",
72
+ region: "eu-west-1"
73
+ });
74
+ });
75
+ globalThis.test("reads them nested under default, as an ESM platform exports them", () => {
76
+ globalThis.expect(from([[
77
+ "default",
78
+ Object.fromEntries([[
79
+ "cognitoUserPoolId",
80
+ "p-old"
81
+ ]])
82
+ ]])).toEqual({
83
+ id: "p-old",
84
+ region: undefined
85
+ });
86
+ });
87
+ globalThis.test("no pool id is no pool to list", () => {
88
+ globalThis.expect(from([])).toEqual(undefined);
89
+ });
90
+ });
91
+
92
+ let Cognito;
93
+
94
+ export {
95
+ Cognito,
96
+ group,
97
+ }
98
+ /* Not a pure module */