@reventlessdev/reventless-aws 3.0.0-alpha.273 → 3.0.0-alpha.274

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.274 (2026-08-08)
7
+
8
+ ### Bug Fixes
9
+
10
+ * **aws:** scope a managed log group by project as well as stack ([f3ea01a](https://github.com/ReventlessDev/reventless-core/commit/f3ea01a254465012becf6a27b401f10382439864))
11
+
12
+
6
13
  # 3.0.0-alpha.273 (2026-08-08)
7
14
 
8
15
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-aws",
3
- "version": "3.0.0-alpha.273",
3
+ "version": "3.0.0-alpha.274",
4
4
  "description": "AWS adapters for Reventless",
5
5
  "license": "Apache-2.0",
6
6
  "dependencies": {
@@ -13,15 +13,15 @@
13
13
  "sury": "11.0.0-alpha.4",
14
14
  "uuid": "^13.0.0",
15
15
  "@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.4",
16
- "@reventlessdev/rescript-effect": "0.1.0-alpha.32",
17
- "@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.67",
18
16
  "@reventlessdev/rescript-node": "2.0.0-alpha.2",
19
17
  "@reventlessdev/rescript-jest": "1.0.0-alpha.10",
18
+ "@reventlessdev/rescript-effect": "0.1.0-alpha.32",
19
+ "@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.67",
20
20
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.18",
21
- "@reventlessdev/reventless-infra": "3.0.0-alpha.128",
21
+ "@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
22
22
  "@reventlessdev/reventless-core": "3.0.0-alpha.217",
23
23
  "@reventlessdev/reventless-interop": "3.0.0-alpha.30",
24
- "@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
24
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.128",
25
25
  "@reventlessdev/reventless-postgres": "3.0.0-alpha.81",
26
26
  "@reventlessdev/reventless-spec": "3.0.0-alpha.102"
27
27
  },
@@ -51,11 +51,21 @@ let applyLogLevelDefault = (variables: dict<Pulumi.Input.t<string>>) =>
51
51
  - **stable**: it carries no `-<7hex>` Pulumi suffix, so replacing a function
52
52
  keeps its group instead of stranding the old one as an orphan nothing
53
53
  tears down.
54
- - **stack-scoped**: stacks sharing an account cannot collide, which was the
55
- reason the physical name was used before.
54
+ - **deployment-scoped**: deployments sharing an account cannot collide, which
55
+ was the reason the physical name was used before.
56
+
57
+ The scope is `project`/`stack`, and **both halves are load-bearing**. A stack
58
+ name alone is not a discriminator: every plugin of one platform is deployed as
59
+ its own Pulumi project under the *same* stack name (`alpha`, `beta`, …), and
60
+ the components inside them are named by role rather than by owner — so several
61
+ projects each host an `AllStateViewSlices`, an `AllReadModels`, an
62
+ `AllAggregatesCmdHandler`. Scoping on the stack alone gives all of them one
63
+ name, and every deployment after the first fails with
64
+ `ResourceAlreadyExistsException`.
56
65
 
57
66
  Pure, so the naming is decidable without Pulumi. */
58
- let logGroupNameFor = (~stack: string, ~name: string): string => `/aws/lambda/${stack}-${name}`
67
+ let logGroupNameFor = (~project: string, ~stack: string, ~name: string): string =>
68
+ `/aws/lambda/${project}-${stack}-${name}`
59
69
 
60
70
  // Create the managed CloudWatch log group for a Lambda **before** the function
61
71
  // that writes to it, so the function can be pointed at it via `loggingConfig`
@@ -101,7 +111,11 @@ let makeManagedLogGroup = (
101
111
  Cloudwatch.LogGroup.make(
102
112
  ~name=`${name}LogGroup`,
103
113
  ~args={
104
- name: logGroupNameFor(~stack, ~name)->Pulumi.Input.make,
114
+ name: logGroupNameFor(
115
+ ~project=Pulumi.Pulumi.getProjectName(),
116
+ ~stack,
117
+ ~name,
118
+ )->Pulumi.Input.make,
105
119
  retentionInDays: days->Pulumi.Input.make,
106
120
  tags,
107
121
  },
@@ -36,8 +36,8 @@ function applyLogLevelDefault(variables) {
36
36
  variables[match[0]] = match[1];
37
37
  }
38
38
 
39
- function logGroupNameFor(stack, name) {
40
- return `/aws/lambda/` + stack + `-` + name;
39
+ function logGroupNameFor(project, stack, name) {
40
+ return `/aws/lambda/` + project + `-` + stack + `-` + name;
41
41
  }
42
42
 
43
43
  function makeManagedLogGroup(name, retentionDaysOverride, tags, opts, param) {
@@ -48,7 +48,7 @@ function makeManagedLogGroup(name, retentionDaysOverride, tags, opts, param) {
48
48
  match$1 ? Util_LogRetention$ReventlessAws.retentionDaysFor(stack, match[1], Stdlib_Option.flatMap(Util_LocalConfig$ReventlessAws.get("logRetentionDays"), s => Stdlib_Int.fromString(s, undefined))) : undefined
49
49
  );
50
50
  return Stdlib_Option.map(retentionDays, days => new (Aws.cloudwatch.LogGroup)(name + `LogGroup`, {
51
- name: logGroupNameFor(stack, name),
51
+ name: logGroupNameFor(Pulumi.getProject(), stack, name),
52
52
  retentionInDays: days,
53
53
  tags: tags
54
54
  }, opts !== undefined ? Primitive_option.valFromOption(opts) : undefined));
@@ -5,32 +5,44 @@ open JestGlobals
5
5
  // (the group can be created before the function, and pointed at from
6
6
  // `loggingConfig`), so the properties it has to keep are worth pinning.
7
7
  describe("Util_LambdaLogging.logGroupNameFor", () => {
8
+ let nameFor = (~project="online-shop-catalog-aws", ~stack="alpha", ~name) =>
9
+ Util_LambdaLogging.logGroupNameFor(~project, ~stack, ~name)
10
+
8
11
  testSync("is the conventional Lambda group prefix", () =>
9
- expect(Util_LambdaLogging.logGroupNameFor(~stack="alpha", ~name="AllReadModels"))->toBe(
10
- "/aws/lambda/alpha-AllReadModels",
12
+ expect(nameFor(~name="AllReadModels"))->toBe(
13
+ "/aws/lambda/online-shop-catalog-aws-alpha-AllReadModels",
11
14
  )
12
15
  )
13
16
 
14
17
  testSync("carries no Pulumi physical-name suffix, so a replaced function keeps its group", () => {
15
- let before = Util_LambdaLogging.logGroupNameFor(~stack="alpha", ~name="AllReadModels")
16
- let afterReplacement = Util_LambdaLogging.logGroupNameFor(~stack="alpha", ~name="AllReadModels")
18
+ let before = nameFor(~name="AllReadModels")
19
+ let afterReplacement = nameFor(~name="AllReadModels")
17
20
  expect(before)->toBe(afterReplacement)
18
21
  })
19
22
 
20
- testSync("is stack-scoped, so stacks sharing an account cannot collide", () =>
21
- expect(Util_LambdaLogging.logGroupNameFor(~stack="pr-42", ~name="AllReadModels"))->not_->toBe(
22
- Util_LambdaLogging.logGroupNameFor(~stack="alpha", ~name="AllReadModels"),
23
+ testSync("is stack-scoped, so environments of one project cannot collide", () =>
24
+ expect(nameFor(~stack="pr-42", ~name="AllReadModels"))->not_->toBe(
25
+ nameFor(~stack="alpha", ~name="AllReadModels"),
23
26
  )
24
27
  )
25
28
 
29
+ // The regression this scoping exists for. Every plugin of one platform is its
30
+ // own Pulumi PROJECT deployed under the SAME stack name, and components are
31
+ // named by role rather than by owner — so several projects each host an
32
+ // `AllStateViewSlices`. Scoping on the stack alone gave them one name, and
33
+ // every deploy after the first failed with ResourceAlreadyExistsException.
34
+ testSync("is project-scoped, so one platform's plugins cannot collide", () =>
35
+ expect(nameFor(~project="online-shop-catalog-aws", ~name="AllStateViewSlices"))
36
+ ->not_
37
+ ->toBe(nameFor(~project="online-shop-ordering-aws", ~name="AllStateViewSlices"))
38
+ )
39
+
26
40
  testSync("differs from the group Lambda would auto-create for the same function", () => {
27
41
  // Lambda auto-creates `/aws/lambda/<physical name>`, and the physical name
28
42
  // always carries Pulumi's `-<7hex>` suffix. Not colliding with it is what
29
43
  // makes the create unconditionally safe on a stack whose groups AWS already
30
44
  // made.
31
45
  let autoCreated = "/aws/lambda/AllReadModels-0287438"
32
- expect(Util_LambdaLogging.logGroupNameFor(~stack="alpha", ~name="AllReadModels"))->not_->toBe(
33
- autoCreated,
34
- )
46
+ expect(nameFor(~name="AllReadModels"))->not_->toBe(autoCreated)
35
47
  })
36
48
  })
@@ -3,19 +3,27 @@
3
3
  import * as Util_LambdaLogging$ReventlessAws from "../src/util/Util_LambdaLogging.res.mjs";
4
4
 
5
5
  globalThis.describe("Util_LambdaLogging.logGroupNameFor", () => {
6
+ let nameFor = (projectOpt, stackOpt, name) => {
7
+ let project = projectOpt !== undefined ? projectOpt : "online-shop-catalog-aws";
8
+ let stack = stackOpt !== undefined ? stackOpt : "alpha";
9
+ return Util_LambdaLogging$ReventlessAws.logGroupNameFor(project, stack, name);
10
+ };
6
11
  globalThis.test("is the conventional Lambda group prefix", () => {
7
- globalThis.expect(Util_LambdaLogging$ReventlessAws.logGroupNameFor("alpha", "AllReadModels")).toBe("/aws/lambda/alpha-AllReadModels");
12
+ globalThis.expect(nameFor(undefined, undefined, "AllReadModels")).toBe("/aws/lambda/online-shop-catalog-aws-alpha-AllReadModels");
8
13
  });
9
14
  globalThis.test("carries no Pulumi physical-name suffix, so a replaced function keeps its group", () => {
10
- let before = Util_LambdaLogging$ReventlessAws.logGroupNameFor("alpha", "AllReadModels");
11
- let afterReplacement = Util_LambdaLogging$ReventlessAws.logGroupNameFor("alpha", "AllReadModels");
15
+ let before = nameFor(undefined, undefined, "AllReadModels");
16
+ let afterReplacement = nameFor(undefined, undefined, "AllReadModels");
12
17
  globalThis.expect(before).toBe(afterReplacement);
13
18
  });
14
- globalThis.test("is stack-scoped, so stacks sharing an account cannot collide", () => {
15
- globalThis.expect(Util_LambdaLogging$ReventlessAws.logGroupNameFor("pr-42", "AllReadModels")).not.toBe(Util_LambdaLogging$ReventlessAws.logGroupNameFor("alpha", "AllReadModels"));
19
+ globalThis.test("is stack-scoped, so environments of one project cannot collide", () => {
20
+ globalThis.expect(nameFor(undefined, "pr-42", "AllReadModels")).not.toBe(nameFor(undefined, "alpha", "AllReadModels"));
21
+ });
22
+ globalThis.test("is project-scoped, so one platform's plugins cannot collide", () => {
23
+ globalThis.expect(nameFor("online-shop-catalog-aws", undefined, "AllStateViewSlices")).not.toBe(nameFor("online-shop-ordering-aws", undefined, "AllStateViewSlices"));
16
24
  });
17
25
  globalThis.test("differs from the group Lambda would auto-create for the same function", () => {
18
- globalThis.expect(Util_LambdaLogging$ReventlessAws.logGroupNameFor("alpha", "AllReadModels")).not.toBe("/aws/lambda/AllReadModels-0287438");
26
+ globalThis.expect(nameFor(undefined, undefined, "AllReadModels")).not.toBe("/aws/lambda/AllReadModels-0287438");
19
27
  });
20
28
  });
21
29