@reventlessdev/reventless-aws 3.0.0-alpha.253 → 3.0.0-alpha.254

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 (36) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/package.json +9 -9
  3. package/src/Platform.res +134 -20
  4. package/src/Platform.res.mjs +95 -6
  5. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +5 -0
  6. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs +3 -0
  7. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res +5 -0
  8. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res.mjs +3 -0
  9. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +66 -8
  10. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs +16 -3
  11. package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res +5 -0
  12. package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res.mjs +3 -0
  13. package/src/adapter/Upload/Upload_Claim_S3.res +361 -0
  14. package/src/adapter/Upload/Upload_Claim_S3.res.mjs +203 -0
  15. package/src/adapter/Upload/Upload_Claim_S3_Ops.res +279 -0
  16. package/src/adapter/Upload/Upload_Claim_S3_Ops.res.mjs +261 -0
  17. package/src/adapter/Upload/Upload_PendingTag.res +41 -0
  18. package/src/adapter/Upload/Upload_PendingTag.res.mjs +15 -0
  19. package/src/adapter/Upload/Upload_Presign_S3.res +12 -2
  20. package/src/adapter/Upload/Upload_Presign_S3.res.mjs +1 -0
  21. package/src/adapter/Upload/Upload_Presign_S3_Ops.res +13 -1
  22. package/src/adapter/Upload/Upload_Presign_S3_Ops.res.mjs +3 -1
  23. package/src/capability/Capability_ObjectStore_S3.res +42 -1
  24. package/src/capability/Capability_ObjectStore_S3.res.mjs +19 -1
  25. package/src/components/Api/AppSync_Adapter.res +36 -0
  26. package/src/components/Api/AppSync_Adapter.res.mjs +14 -0
  27. package/src/util/Util_LogRetention.res +86 -0
  28. package/src/util/Util_LogRetention.res.mjs +43 -0
  29. package/src/util/Util_StoreLayout.res +37 -0
  30. package/src/util/Util_StoreLayout.res.mjs +18 -0
  31. package/tests/Upload_ClaimTest.res +132 -0
  32. package/tests/Upload_ClaimTest.res.mjs +101 -0
  33. package/tests/Util_LogRetentionTest.res +122 -0
  34. package/tests/Util_LogRetentionTest.res.mjs +95 -0
  35. package/tests/Util_StoreLayoutTest.res +63 -0
  36. package/tests/Util_StoreLayoutTest.res.mjs +42 -0
@@ -0,0 +1,122 @@
1
+ open JestGlobals
2
+
3
+ let prodStacks = Util_HostUiDomain.defaultProdStacks
4
+
5
+ describe("Util_LogRetention.retentionDaysFor", () => {
6
+ testSync("prod keeps logs a year", () =>
7
+ expect(Util_LogRetention.retentionDaysFor(~stack="prod", ~prodStacks))->toBe(365)
8
+ )
9
+
10
+ testSync("'main' is production too, by the shared prod list", () =>
11
+ expect(Util_LogRetention.retentionDaysFor(~stack="main", ~prodStacks))->toBe(365)
12
+ )
13
+
14
+ testSync("beta keeps a month of pre-prod history", () =>
15
+ expect(Util_LogRetention.retentionDaysFor(~stack="beta", ~prodStacks))->toBe(30)
16
+ )
17
+
18
+ testSync("a PR stack keeps the minimum — torn down quickly", () =>
19
+ expect(Util_LogRetention.retentionDaysFor(~stack="pr-1234", ~prodStacks))->toBe(3)
20
+ )
21
+
22
+ testSync("alpha keeps a week — active development, consumed within hours", () =>
23
+ expect(Util_LogRetention.retentionDaysFor(~stack="alpha", ~prodStacks))->toBe(7)
24
+ )
25
+
26
+ // Same fail-open as the store-layout allow-list: an unlisted production name
27
+ // silently gets the dev tier. Asserted so it is a decision on record and the
28
+ // config override is the documented fix.
29
+ testSync("an unlisted stack falls open to the dev tier", () =>
30
+ expect(Util_LogRetention.retentionDaysFor(~stack="production", ~prodStacks))->toBe(7)
31
+ )
32
+
33
+ testSync("adding it to the prod list is the fix", () =>
34
+ expect(
35
+ Util_LogRetention.retentionDaysFor(~stack="production", ~prodStacks=["prod", "production"]),
36
+ )->toBe(365)
37
+ )
38
+
39
+ // The config key is the escape hatch — a stack dialled without a code change,
40
+ // including `0` (never expire) as an explicit opt-in.
41
+ testSync("a config override wins over the tier default", () =>
42
+ expect(Util_LogRetention.retentionDaysFor(~stack="alpha", ~prodStacks, ~configOverride=14))->toBe(14)
43
+ )
44
+
45
+ testSync("0 = never expire is expressible via the override", () =>
46
+ expect(Util_LogRetention.retentionDaysFor(~stack="prod", ~prodStacks, ~configOverride=0))->toBe(0)
47
+ )
48
+ })
49
+
50
+ describe("Util_LogRetention.logLevelFor", () => {
51
+ // Retention and level pull in opposite directions: prod is long-lived AND
52
+ // quiet; the dev stacks are short-lived AND loud.
53
+ testSync("prod is quiet — info, no debug noise or payload leakage", () =>
54
+ expect(Util_LogRetention.logLevelFor(~stack="prod", ~prodStacks))->toBe("info")
55
+ )
56
+
57
+ testSync("beta mirrors prod so pre-prod behaves like prod", () =>
58
+ expect(Util_LogRetention.logLevelFor(~stack="beta", ~prodStacks))->toBe("info")
59
+ )
60
+
61
+ testSync("alpha is verbose — debug feedback during active development", () =>
62
+ expect(Util_LogRetention.logLevelFor(~stack="alpha", ~prodStacks))->toBe("debug")
63
+ )
64
+
65
+ testSync("a PR stack is verbose too", () =>
66
+ expect(Util_LogRetention.logLevelFor(~stack="pr-7", ~prodStacks))->toBe("debug")
67
+ )
68
+
69
+ testSync("a config override wins over the tier default", () =>
70
+ expect(
71
+ Util_LogRetention.logLevelFor(~stack="prod", ~prodStacks, ~configOverride="debug"),
72
+ )->toBe("debug")
73
+ )
74
+ })
75
+
76
+ describe("Util_LogRetention.managesLogGroup", () => {
77
+ // Managed for every stack by default — a stack deployed on this framework gets
78
+ // its groups from Pulumi before Lambda/AppSync would auto-create them, so a
79
+ // fresh stack has nothing to adopt.
80
+ testSync("alpha is managed", () =>
81
+ expect(Util_LogRetention.managesLogGroup(~stack="alpha"))->toBe(true)
82
+ )
83
+
84
+ testSync("a PR stack is managed", () =>
85
+ expect(Util_LogRetention.managesLogGroup(~stack="pr-42"))->toBe(true)
86
+ )
87
+
88
+ testSync("prod is managed too — a fresh prod stack has no group to adopt", () =>
89
+ expect(Util_LogRetention.managesLogGroup(~stack="prod"))->toBe(true)
90
+ )
91
+
92
+ testSync("beta is managed too", () =>
93
+ expect(Util_LogRetention.managesLogGroup(~stack="beta"))->toBe(true)
94
+ )
95
+
96
+ // The escape hatch: a stack pending a `pulumi import` stays on auto-created
97
+ // groups until the import is done — a config flip, not a code change.
98
+ testSync("a stack named in unmanagedLogGroupStacks is left auto-created", () =>
99
+ expect(
100
+ Util_LogRetention.managesLogGroup(~stack="legacy-prod", ~unmanagedStacks=["legacy-prod"]),
101
+ )->toBe(false)
102
+ )
103
+
104
+ testSync("only the named stacks are excluded — others stay managed", () =>
105
+ expect(
106
+ Util_LogRetention.managesLogGroup(~stack="alpha", ~unmanagedStacks=["legacy-prod"]),
107
+ )->toBe(true)
108
+ )
109
+ })
110
+
111
+ describe("Util_LogRetention.parseUnmanagedStacks", () => {
112
+ testSync("empty config manages every stack", () =>
113
+ expect(Util_LogRetention.parseUnmanagedStacks(""))->toEqual([])
114
+ )
115
+
116
+ testSync("CSV is trimmed and empty entries dropped", () =>
117
+ expect(Util_LogRetention.parseUnmanagedStacks(" legacy-prod , , old-beta "))->toEqual([
118
+ "legacy-prod",
119
+ "old-beta",
120
+ ])
121
+ )
122
+ })
@@ -0,0 +1,95 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Util_HostUiDomain$ReventlessAws from "../src/util/Util_HostUiDomain.res.mjs";
4
+ import * as Util_LogRetention$ReventlessAws from "../src/util/Util_LogRetention.res.mjs";
5
+
6
+ globalThis.describe("Util_LogRetention.retentionDaysFor", () => {
7
+ globalThis.test("prod keeps logs a year", () => {
8
+ globalThis.expect(Util_LogRetention$ReventlessAws.retentionDaysFor("prod", Util_HostUiDomain$ReventlessAws.defaultProdStacks, undefined)).toBe(365);
9
+ });
10
+ globalThis.test("'main' is production too, by the shared prod list", () => {
11
+ globalThis.expect(Util_LogRetention$ReventlessAws.retentionDaysFor("main", Util_HostUiDomain$ReventlessAws.defaultProdStacks, undefined)).toBe(365);
12
+ });
13
+ globalThis.test("beta keeps a month of pre-prod history", () => {
14
+ globalThis.expect(Util_LogRetention$ReventlessAws.retentionDaysFor("beta", Util_HostUiDomain$ReventlessAws.defaultProdStacks, undefined)).toBe(30);
15
+ });
16
+ globalThis.test("a PR stack keeps the minimum — torn down quickly", () => {
17
+ globalThis.expect(Util_LogRetention$ReventlessAws.retentionDaysFor("pr-1234", Util_HostUiDomain$ReventlessAws.defaultProdStacks, undefined)).toBe(3);
18
+ });
19
+ globalThis.test("alpha keeps a week — active development, consumed within hours", () => {
20
+ globalThis.expect(Util_LogRetention$ReventlessAws.retentionDaysFor("alpha", Util_HostUiDomain$ReventlessAws.defaultProdStacks, undefined)).toBe(7);
21
+ });
22
+ globalThis.test("an unlisted stack falls open to the dev tier", () => {
23
+ globalThis.expect(Util_LogRetention$ReventlessAws.retentionDaysFor("production", Util_HostUiDomain$ReventlessAws.defaultProdStacks, undefined)).toBe(7);
24
+ });
25
+ globalThis.test("adding it to the prod list is the fix", () => {
26
+ globalThis.expect(Util_LogRetention$ReventlessAws.retentionDaysFor("production", [
27
+ "prod",
28
+ "production"
29
+ ], undefined)).toBe(365);
30
+ });
31
+ globalThis.test("a config override wins over the tier default", () => {
32
+ globalThis.expect(Util_LogRetention$ReventlessAws.retentionDaysFor("alpha", Util_HostUiDomain$ReventlessAws.defaultProdStacks, 14)).toBe(14);
33
+ });
34
+ globalThis.test("0 = never expire is expressible via the override", () => {
35
+ globalThis.expect(Util_LogRetention$ReventlessAws.retentionDaysFor("prod", Util_HostUiDomain$ReventlessAws.defaultProdStacks, 0)).toBe(0);
36
+ });
37
+ });
38
+
39
+ globalThis.describe("Util_LogRetention.logLevelFor", () => {
40
+ globalThis.test("prod is quiet — info, no debug noise or payload leakage", () => {
41
+ globalThis.expect(Util_LogRetention$ReventlessAws.logLevelFor("prod", Util_HostUiDomain$ReventlessAws.defaultProdStacks, undefined)).toBe("info");
42
+ });
43
+ globalThis.test("beta mirrors prod so pre-prod behaves like prod", () => {
44
+ globalThis.expect(Util_LogRetention$ReventlessAws.logLevelFor("beta", Util_HostUiDomain$ReventlessAws.defaultProdStacks, undefined)).toBe("info");
45
+ });
46
+ globalThis.test("alpha is verbose — debug feedback during active development", () => {
47
+ globalThis.expect(Util_LogRetention$ReventlessAws.logLevelFor("alpha", Util_HostUiDomain$ReventlessAws.defaultProdStacks, undefined)).toBe("debug");
48
+ });
49
+ globalThis.test("a PR stack is verbose too", () => {
50
+ globalThis.expect(Util_LogRetention$ReventlessAws.logLevelFor("pr-7", Util_HostUiDomain$ReventlessAws.defaultProdStacks, undefined)).toBe("debug");
51
+ });
52
+ globalThis.test("a config override wins over the tier default", () => {
53
+ globalThis.expect(Util_LogRetention$ReventlessAws.logLevelFor("prod", Util_HostUiDomain$ReventlessAws.defaultProdStacks, "debug")).toBe("debug");
54
+ });
55
+ });
56
+
57
+ globalThis.describe("Util_LogRetention.managesLogGroup", () => {
58
+ globalThis.test("alpha is managed", () => {
59
+ globalThis.expect(Util_LogRetention$ReventlessAws.managesLogGroup("alpha", undefined)).toBe(true);
60
+ });
61
+ globalThis.test("a PR stack is managed", () => {
62
+ globalThis.expect(Util_LogRetention$ReventlessAws.managesLogGroup("pr-42", undefined)).toBe(true);
63
+ });
64
+ globalThis.test("prod is managed too — a fresh prod stack has no group to adopt", () => {
65
+ globalThis.expect(Util_LogRetention$ReventlessAws.managesLogGroup("prod", undefined)).toBe(true);
66
+ });
67
+ globalThis.test("beta is managed too", () => {
68
+ globalThis.expect(Util_LogRetention$ReventlessAws.managesLogGroup("beta", undefined)).toBe(true);
69
+ });
70
+ globalThis.test("a stack named in unmanagedLogGroupStacks is left auto-created", () => {
71
+ globalThis.expect(Util_LogRetention$ReventlessAws.managesLogGroup("legacy-prod", ["legacy-prod"])).toBe(false);
72
+ });
73
+ globalThis.test("only the named stacks are excluded — others stay managed", () => {
74
+ globalThis.expect(Util_LogRetention$ReventlessAws.managesLogGroup("alpha", ["legacy-prod"])).toBe(true);
75
+ });
76
+ });
77
+
78
+ globalThis.describe("Util_LogRetention.parseUnmanagedStacks", () => {
79
+ globalThis.test("empty config manages every stack", () => {
80
+ globalThis.expect(Util_LogRetention$ReventlessAws.parseUnmanagedStacks("")).toEqual([]);
81
+ });
82
+ globalThis.test("CSV is trimmed and empty entries dropped", () => {
83
+ globalThis.expect(Util_LogRetention$ReventlessAws.parseUnmanagedStacks(" legacy-prod , , old-beta ")).toEqual([
84
+ "legacy-prod",
85
+ "old-beta"
86
+ ]);
87
+ });
88
+ });
89
+
90
+ let prodStacks = Util_HostUiDomain$ReventlessAws.defaultProdStacks;
91
+
92
+ export {
93
+ prodStacks,
94
+ }
95
+ /* Not a pure module */
@@ -240,3 +240,66 @@ describe("Util_StoreLayout.keyPrefixFor", () => {
240
240
  )->toBe(true)
241
241
  )
242
242
  })
243
+
244
+ describe("Util_StoreLayout.pendingExpiryFor", () => {
245
+ // The default is the whole point: expiry is the one step in the mechanism that
246
+ // deletes objects, and it is only sound once reconciliation has confirmed the
247
+ // tagged set is the unreferenced set. A store nobody named accumulates.
248
+ testSync("no config at all means no store expires anything", () =>
249
+ expect(
250
+ Util_StoreLayout.pendingExpiryFor(~config=None, ~store="Catalog.productImages"),
251
+ )->toEqual(None)
252
+ )
253
+
254
+ testSync("a store the config does not name expires nothing", () =>
255
+ expect(
256
+ Util_StoreLayout.pendingExpiryFor(
257
+ ~config=Some("Ordering.receipts=14"),
258
+ ~store="Catalog.productImages",
259
+ ),
260
+ )->toEqual(None)
261
+ )
262
+
263
+ testSync("a named store takes its own retention", () =>
264
+ expect(
265
+ Util_StoreLayout.pendingExpiryFor(
266
+ ~config=Some("Catalog.productImages=30"),
267
+ ~store="Catalog.productImages",
268
+ ),
269
+ )->toEqual(Some(30))
270
+ )
271
+
272
+ // Per store, one at a time — the plan's sequencing expressed in the setting.
273
+ testSync("stores are enabled independently, with their own values", () => {
274
+ let config = Some("Catalog.productImages=30, Ordering.receipts=14")
275
+ expect((
276
+ Util_StoreLayout.pendingExpiryFor(~config, ~store="Catalog.productImages"),
277
+ Util_StoreLayout.pendingExpiryFor(~config, ~store="Ordering.receipts"),
278
+ Util_StoreLayout.pendingExpiryFor(~config, ~store="Catalog.other"),
279
+ ))->toEqual((Some(30), Some(14), None))
280
+ })
281
+
282
+ // Guessing at what a typo meant, in the one setting that deletes objects, is
283
+ // not a service — an unreadable entry leaves the store accumulating.
284
+ testSync("a malformed or non-positive entry is ignored, not defaulted", () => {
285
+ let of_ = c => Util_StoreLayout.pendingExpiryFor(~config=Some(c), ~store="Catalog.productImages")
286
+ expect((
287
+ of_("Catalog.productImages"),
288
+ of_("Catalog.productImages="),
289
+ of_("Catalog.productImages=soon"),
290
+ of_("Catalog.productImages=0"),
291
+ of_("Catalog.productImages=-1"),
292
+ ))->toEqual((None, None, None, None, None))
293
+ })
294
+
295
+ // The prefix is a platform-global namespace, so two plugins' stores are
296
+ // distinct keys — enabling one must not enable the other.
297
+ testSync("a store name is matched whole, not by prefix", () =>
298
+ expect(
299
+ Util_StoreLayout.pendingExpiryFor(
300
+ ~config=Some("Catalog.productImages=30"),
301
+ ~store="Catalog.productImagesArchive",
302
+ ),
303
+ )->toEqual(None)
304
+ )
305
+ })
@@ -121,6 +121,48 @@ globalThis.describe("Util_StoreLayout.keyPrefixFor", () => {
121
121
  });
122
122
  });
123
123
 
124
+ globalThis.describe("Util_StoreLayout.pendingExpiryFor", () => {
125
+ globalThis.test("no config at all means no store expires anything", () => {
126
+ globalThis.expect(Util_StoreLayout$ReventlessAws.pendingExpiryFor(undefined, "Catalog.productImages")).toEqual(undefined);
127
+ });
128
+ globalThis.test("a store the config does not name expires nothing", () => {
129
+ globalThis.expect(Util_StoreLayout$ReventlessAws.pendingExpiryFor("Ordering.receipts=14", "Catalog.productImages")).toEqual(undefined);
130
+ });
131
+ globalThis.test("a named store takes its own retention", () => {
132
+ globalThis.expect(Util_StoreLayout$ReventlessAws.pendingExpiryFor("Catalog.productImages=30", "Catalog.productImages")).toEqual(30);
133
+ });
134
+ globalThis.test("stores are enabled independently, with their own values", () => {
135
+ let config = "Catalog.productImages=30, Ordering.receipts=14";
136
+ globalThis.expect([
137
+ Util_StoreLayout$ReventlessAws.pendingExpiryFor(config, "Catalog.productImages"),
138
+ Util_StoreLayout$ReventlessAws.pendingExpiryFor(config, "Ordering.receipts"),
139
+ Util_StoreLayout$ReventlessAws.pendingExpiryFor(config, "Catalog.other")
140
+ ]).toEqual([
141
+ 30,
142
+ 14,
143
+ undefined
144
+ ]);
145
+ });
146
+ globalThis.test("a malformed or non-positive entry is ignored, not defaulted", () => {
147
+ globalThis.expect([
148
+ Util_StoreLayout$ReventlessAws.pendingExpiryFor("Catalog.productImages", "Catalog.productImages"),
149
+ Util_StoreLayout$ReventlessAws.pendingExpiryFor("Catalog.productImages=", "Catalog.productImages"),
150
+ Util_StoreLayout$ReventlessAws.pendingExpiryFor("Catalog.productImages=soon", "Catalog.productImages"),
151
+ Util_StoreLayout$ReventlessAws.pendingExpiryFor("Catalog.productImages=0", "Catalog.productImages"),
152
+ Util_StoreLayout$ReventlessAws.pendingExpiryFor("Catalog.productImages=-1", "Catalog.productImages")
153
+ ]).toEqual([
154
+ undefined,
155
+ undefined,
156
+ undefined,
157
+ undefined,
158
+ undefined
159
+ ]);
160
+ });
161
+ globalThis.test("a store name is matched whole, not by prefix", () => {
162
+ globalThis.expect(Util_StoreLayout$ReventlessAws.pendingExpiryFor("Catalog.productImages=30", "Catalog.productImagesArchive")).toEqual(undefined);
163
+ });
164
+ });
165
+
124
166
  let prodStacks = Util_HostUiDomain$ReventlessAws.defaultProdStacks;
125
167
 
126
168
  export {