@reventlessdev/reventless-aws 3.0.0-alpha.287 → 3.0.0-alpha.290

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,20 @@
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.290 (2026-08-13)
7
+
8
+ ### Bug Fixes
9
+
10
+ * **aws:** move past a version number that already names other content ([bffba4f](https://github.com/ReventlessDev/reventless-core/commit/bffba4f15eff4a9c30bbf2e28484bb549b865cfc))
11
+
12
+
13
+ # 3.0.0-alpha.288 (2026-08-12)
14
+
15
+ ### Bug Fixes
16
+
17
+ * **aws:** carry the elevated groups into every Lambda ([ced4d92](https://github.com/ReventlessDev/reventless-core/commit/ced4d9211c3cebe70702757ab331c8a1fa773fca))
18
+
19
+
6
20
  # 3.0.0-alpha.287 (2026-08-12)
7
21
 
8
22
  **Note:** Version bump only for package @reventlessdev/reventless-aws
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-aws",
3
- "version": "3.0.0-alpha.287",
3
+ "version": "3.0.0-alpha.290",
4
4
  "description": "AWS adapters for Reventless",
5
5
  "license": "Apache-2.0",
6
6
  "dependencies": {
@@ -12,18 +12,18 @@
12
12
  "@aws-sdk/s3-request-presigner": "3.970.0",
13
13
  "sury": "11.0.0-alpha.4",
14
14
  "uuid": "^13.0.0",
15
- "@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.7",
16
15
  "@reventlessdev/rescript-effect": "0.1.0-alpha.32",
17
16
  "@reventlessdev/rescript-jest": "1.0.0-alpha.10",
18
17
  "@reventlessdev/rescript-node": "2.0.0-alpha.4",
18
+ "@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.7",
19
19
  "@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.71",
20
20
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.18",
21
- "@reventlessdev/reventless-core": "3.0.0-alpha.228",
22
21
  "@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
22
+ "@reventlessdev/reventless-core": "3.0.0-alpha.229",
23
23
  "@reventlessdev/reventless-infra": "3.0.0-alpha.137",
24
24
  "@reventlessdev/reventless-interop": "3.0.0-alpha.30",
25
- "@reventlessdev/reventless-postgres": "3.0.0-alpha.92",
26
- "@reventlessdev/reventless-spec": "3.0.0-alpha.111"
25
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.111",
26
+ "@reventlessdev/reventless-postgres": "3.0.0-alpha.93"
27
27
  },
28
28
  "devDependencies": {
29
29
  "rescript": "12.3.0",
@@ -237,6 +237,13 @@ let makeFromCodeAsset: (
237
237
  // `Lambda.Function.make` builders apply the identical policy.
238
238
  Util_LambdaLogging.applyLogLevelDefault(variables)
239
239
 
240
+ // Same terms, same reason it is here rather than at each builder: the groups
241
+ // exempt from owner scoping are decided by the deploy program, and the runtime
242
+ // that stamps a command has no other way to learn them. Without it a runtime
243
+ // concludes nobody is elevated and stamps an operator's on-behalf write with
244
+ // the operator's own id.
245
+ Util_OwnerScopeEnv.applyElevatedGroupsDefault(variables)
246
+
240
247
  // ESM self-containment (Option C): every code archive built by
241
248
  // Util_Bundle.buildCodeArchive ships register-hook.mjs + layer-resolver.mjs at
242
249
  // /var/task. --import registers the resolve hook so the ESM entry point (and the
@@ -24,6 +24,7 @@ import * as ComponentType$ReventlessCore from "@reventlessdev/reventless-core/sr
24
24
  import * as Util_DcbMetrics$ReventlessAws from "../../util/Util_DcbMetrics.res.mjs";
25
25
  import * as RuntimeExtension$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/RuntimeExtension/RuntimeExtension.res.mjs";
26
26
  import * as Util_LambdaLogging$ReventlessAws from "../../util/Util_LambdaLogging.res.mjs";
27
+ import * as Util_OwnerScopeEnv$ReventlessAws from "../../util/Util_OwnerScopeEnv.res.mjs";
27
28
  import * as ResourceAttribution$ReventlessCore from "@reventlessdev/reventless-core/src/ResourceAttribution.res.mjs";
28
29
 
29
30
  let additionalEnvVars = {};
@@ -102,6 +103,7 @@ function makeFromCodeAsset(name, unitKind, componentKind, code, sourceCodeHash,
102
103
  variables[key] = value;
103
104
  });
104
105
  Util_LambdaLogging$ReventlessAws.applyLogLevelDefault(variables);
106
+ Util_OwnerScopeEnv$ReventlessAws.applyElevatedGroupsDefault(variables);
105
107
  variables["NODE_OPTIONS"] = Util_Bundle$ReventlessAws.esmLoaderNodeOptions;
106
108
  variables["ESM_FALLBACK_DIRS"] = Util_Bundle$ReventlessAws.esmFallbackDirs;
107
109
  if (!RuntimeExtension$ReventlessCore.isEmpty()) {
@@ -0,0 +1,48 @@
1
+ // Carries the deployment's elevated groups into Lambda runtimes.
2
+ //
3
+ // A cloud deployment is two kinds of process and only one of them is the deploy
4
+ // program: it bakes the owner predicate into generated resolver source, while
5
+ // command stamping (`CommandGenerator_Callback`) and SQL-backed reads
6
+ // (`PgQueryResolver_Lambda`) run later, in function runtimes the deploy never
7
+ // enters. `Reventless.OwnerScope.elevatedGroups()` answers from an explicit
8
+ // `setElevatedGroups` or from the environment, so a value stated in the deploy
9
+ // program reaches those runtimes only if something puts it in their environment.
10
+ //
11
+ // Nothing did, and the two halves fail in opposite directions. A read that
12
+ // believes nobody is elevated shows an operator too little, which is safe and
13
+ // visible. A write that believes it stamps an operator's on-behalf order with
14
+ // the OPERATOR's id — silently, and the row is wrong from then on. The second is
15
+ // why this is a correctness fix rather than a parity one.
16
+ //
17
+ // Scoped deliberately to `RuntimeEnvironment_Lambda.makeFromCodeAsset`, which
18
+ // every command and query runtime is built through. The bespoke builders that
19
+ // append `Util_LambdaLogging.logLevelEntry()` for themselves — dead letters, the
20
+ // state-topic publisher, geocoding, the two upload Lambdas, the platform UI APIs
21
+ // — neither stamp a command nor serve an owner-scoped view, so they sit outside
22
+ // this policy for the same reason they sit outside several others. The
23
+ // serialized-closure `make` path is likewise outside it, and already outside the
24
+ // logging policy.
25
+
26
+ let key = "REVENTLESS_ELEVATED_GROUPS"
27
+
28
+ /** The `("REVENTLESS_ELEVATED_GROUPS", "Admin,Support")` entry, or `None` when
29
+ the deployment named no elevated groups.
30
+
31
+ Comma-separated because that is what `OwnerScope.parseElevatedGroups` reads,
32
+ which trims each part and drops the empties. Absent rather than empty for the
33
+ empty case: the two mean the same thing there, and writing `""` would suggest
34
+ a deployment had answered the question when it has not. */
35
+ let entry = (): option<(string, Pulumi.Input.t<string>)> =>
36
+ switch Reventless.OwnerScope.elevatedGroups() {
37
+ | [] => None
38
+ | groups => Some((key, groups->Array.join(",")->Pulumi.Input.make))
39
+ }
40
+
41
+ /** Default the elevated groups for a Lambda when the caller has not pinned them.
42
+ Mutates in place and must run after the caller's own env vars are set, so
43
+ theirs win — the same contract as `Util_LambdaLogging.applyLogLevelDefault`,
44
+ which it runs beside. */
45
+ let applyElevatedGroupsDefault = (variables: dict<Pulumi.Input.t<string>>) =>
46
+ if variables->Dict.get(key)->Option.isNone {
47
+ entry()->Option.forEach(((k, v)) => variables->Dict.set(k, v))
48
+ }
@@ -0,0 +1,31 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
4
+ import * as OwnerScope$Reventless from "@reventlessdev/reventless-spec/src/types/OwnerScope.res.mjs";
5
+
6
+ let key = "REVENTLESS_ELEVATED_GROUPS";
7
+
8
+ function entry() {
9
+ let groups = OwnerScope$Reventless.elevatedGroups();
10
+ if (groups.length !== 0) {
11
+ return [
12
+ key,
13
+ groups.join(",")
14
+ ];
15
+ }
16
+ }
17
+
18
+ function applyElevatedGroupsDefault(variables) {
19
+ if (Stdlib_Option.isNone(variables[key])) {
20
+ return Stdlib_Option.forEach(entry(), param => {
21
+ variables[param[0]] = param[1];
22
+ });
23
+ }
24
+ }
25
+
26
+ export {
27
+ key,
28
+ entry,
29
+ applyElevatedGroupsDefault,
30
+ }
31
+ /* OwnerScope-Reventless Not a pure module */
@@ -0,0 +1,60 @@
1
+ open JestGlobals
2
+
3
+ // Guards the carrier that tells a Lambda runtime which groups are exempt from
4
+ // owner scoping. The deploy program bakes the read predicate itself, but
5
+ // stamping and SQL-backed reads run in function runtimes it never enters, so
6
+ // without this entry those runtimes conclude nobody is elevated — and an
7
+ // operator's on-behalf write is then stamped with the operator's own id.
8
+ //
9
+ // The encoding is load-bearing in both directions: what is written here is read
10
+ // back by `OwnerScope.parseElevatedGroups`, so the two are asserted together
11
+ // rather than each against a literal.
12
+
13
+ let withElevated = (groups, f) => {
14
+ Reventless.OwnerScope.setElevatedGroups(groups)
15
+ let result = f()
16
+ Reventless.OwnerScope.clearElevatedGroups()
17
+ result
18
+ }
19
+
20
+ describe("Util_OwnerScopeEnv — the elevated-groups carrier", () => {
21
+ testSync("writes what OwnerScope reads back", () => {
22
+ let encoded = withElevated(["Admin", "Support"], () =>
23
+ switch Util_OwnerScopeEnv.entry() {
24
+ | Some((_, value)) => value->Obj.magic
25
+ | None => ""
26
+ }
27
+ )
28
+ // The round trip, not the spelling: a separator change on either side fails.
29
+ expect(Reventless.OwnerScope.parseElevatedGroups(encoded))->toEqual(["Admin", "Support"])
30
+ })
31
+
32
+ testSync("names the variable the runtime actually reads", () => {
33
+ expect(Util_OwnerScopeEnv.key)->toBe("REVENTLESS_ELEVATED_GROUPS")
34
+ })
35
+
36
+ testSync("no elevated groups means no variable, not an empty one", () => {
37
+ let entry = withElevated([], () => Util_OwnerScopeEnv.entry())
38
+ expect(entry->Option.isNone)->toBe(true)
39
+ })
40
+
41
+ testSync("defaults the variable when the caller pinned nothing", () => {
42
+ let variables = Dict.make()
43
+ withElevated(["Admin"], () => Util_OwnerScopeEnv.applyElevatedGroupsDefault(variables))
44
+ expect(variables->Dict.get(Util_OwnerScopeEnv.key)->Option.isSome)->toBe(true)
45
+ })
46
+
47
+ testSync("a caller that pinned the variable wins", () => {
48
+ let variables = Dict.fromArray([(Util_OwnerScopeEnv.key, "Pinned"->Obj.magic)])
49
+ withElevated(["Admin"], () => Util_OwnerScopeEnv.applyElevatedGroupsDefault(variables))
50
+ expect(variables->Dict.get(Util_OwnerScopeEnv.key)->Option.getOr(""->Obj.magic)->Obj.magic)->toBe(
51
+ "Pinned",
52
+ )
53
+ })
54
+
55
+ testSync("adds nothing when the deployment named no elevated groups", () => {
56
+ let variables = Dict.make()
57
+ withElevated([], () => Util_OwnerScopeEnv.applyElevatedGroupsDefault(variables))
58
+ expect(variables->Dict.get(Util_OwnerScopeEnv.key)->Option.isNone)->toBe(true)
59
+ })
60
+ })
@@ -0,0 +1,62 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
4
+ import * as OwnerScope$Reventless from "@reventlessdev/reventless-spec/src/types/OwnerScope.res.mjs";
5
+ import * as Util_OwnerScopeEnv$ReventlessAws from "../src/util/Util_OwnerScopeEnv.res.mjs";
6
+
7
+ function withElevated(groups, f) {
8
+ OwnerScope$Reventless.setElevatedGroups(groups);
9
+ let result = f();
10
+ OwnerScope$Reventless.clearElevatedGroups();
11
+ return result;
12
+ }
13
+
14
+ globalThis.describe("Util_OwnerScopeEnv — the elevated-groups carrier", () => {
15
+ globalThis.test("writes what OwnerScope reads back", () => {
16
+ let encoded = withElevated([
17
+ "Admin",
18
+ "Support"
19
+ ], () => {
20
+ let match = Util_OwnerScopeEnv$ReventlessAws.entry();
21
+ if (match !== undefined) {
22
+ return match[1];
23
+ } else {
24
+ return "";
25
+ }
26
+ });
27
+ globalThis.expect(OwnerScope$Reventless.parseElevatedGroups(encoded)).toEqual([
28
+ "Admin",
29
+ "Support"
30
+ ]);
31
+ });
32
+ globalThis.test("names the variable the runtime actually reads", () => {
33
+ globalThis.expect(Util_OwnerScopeEnv$ReventlessAws.key).toBe("REVENTLESS_ELEVATED_GROUPS");
34
+ });
35
+ globalThis.test("no elevated groups means no variable, not an empty one", () => {
36
+ let entry = withElevated([], () => Util_OwnerScopeEnv$ReventlessAws.entry());
37
+ globalThis.expect(Stdlib_Option.isNone(entry)).toBe(true);
38
+ });
39
+ globalThis.test("defaults the variable when the caller pinned nothing", () => {
40
+ let variables = {};
41
+ withElevated(["Admin"], () => Util_OwnerScopeEnv$ReventlessAws.applyElevatedGroupsDefault(variables));
42
+ globalThis.expect(Stdlib_Option.isSome(variables[Util_OwnerScopeEnv$ReventlessAws.key])).toBe(true);
43
+ });
44
+ globalThis.test("a caller that pinned the variable wins", () => {
45
+ let variables = Object.fromEntries([[
46
+ Util_OwnerScopeEnv$ReventlessAws.key,
47
+ "Pinned"
48
+ ]]);
49
+ withElevated(["Admin"], () => Util_OwnerScopeEnv$ReventlessAws.applyElevatedGroupsDefault(variables));
50
+ globalThis.expect(Stdlib_Option.getOr(variables[Util_OwnerScopeEnv$ReventlessAws.key], "")).toBe("Pinned");
51
+ });
52
+ globalThis.test("adds nothing when the deployment named no elevated groups", () => {
53
+ let variables = {};
54
+ withElevated([], () => Util_OwnerScopeEnv$ReventlessAws.applyElevatedGroupsDefault(variables));
55
+ globalThis.expect(Stdlib_Option.isNone(variables[Util_OwnerScopeEnv$ReventlessAws.key])).toBe(true);
56
+ });
57
+ });
58
+
59
+ export {
60
+ withElevated,
61
+ }
62
+ /* Not a pure module */