@reventlessdev/reventless-local 3.0.0-alpha.185 → 3.0.0-alpha.186

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.186 (2026-08-01)
7
+
8
+ ### Bug Fixes
9
+
10
+ * **core:** drain the inbound audit log per request, not wholesale ([e18d4ec](https://github.com/ReventlessDev/reventless-core/commit/e18d4ec4b7ffb38a9e2bd3594d39a07e2ee91851))
11
+
12
+
6
13
  # 3.0.0-alpha.185 (2026-08-01)
7
14
 
8
15
  **Note:** Version bump only for package @reventlessdev/reventless-local
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-local",
3
- "version": "3.0.0-alpha.185",
3
+ "version": "3.0.0-alpha.186",
4
4
  "description": "Local platform for Reventless (in-memory or SQLite backend, for development and testing without AWS)",
5
5
  "license": "Apache-2.0",
6
6
  "bin": {
@@ -35,17 +35,17 @@
35
35
  "sury": "11.0.0-alpha.4",
36
36
  "ws": "^8.18.0",
37
37
  "@reventlessdev/rescript-effect": "0.1.0-alpha.32",
38
- "@reventlessdev/rescript-mcp-sdk": "1.0.0-alpha.21",
39
38
  "@reventlessdev/rescript-jest": "1.0.0-alpha.10",
40
39
  "@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.26",
40
+ "@reventlessdev/rescript-mcp-sdk": "1.0.0-alpha.21",
41
41
  "@reventlessdev/rescript-node": "2.0.0-alpha.0",
42
42
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.18",
43
- "@reventlessdev/reventless-core": "3.0.0-alpha.197",
44
- "@reventlessdev/reventless-gwt": "1.0.0-alpha.144",
43
+ "@reventlessdev/reventless-core": "3.0.0-alpha.198",
44
+ "@reventlessdev/reventless-graphql-server": "1.0.0-alpha.46",
45
+ "@reventlessdev/reventless-gwt": "1.0.0-alpha.145",
45
46
  "@reventlessdev/reventless-infra": "3.0.0-alpha.115",
46
- "@reventlessdev/reventless-spec": "3.0.0-alpha.90",
47
- "@reventlessdev/reventless-graphql-server": "1.0.0-alpha.45",
48
- "@reventlessdev/reventless-postgres": "3.0.0-alpha.61"
47
+ "@reventlessdev/reventless-postgres": "3.0.0-alpha.62",
48
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.90"
49
49
  },
50
50
  "devDependencies": {
51
51
  "rescript": "12.3.0",
@@ -125,6 +125,60 @@ describe("InboundTranslationSlice Callback", () => {
125
125
  })
126
126
  })
127
127
 
128
+ // `auditLog` hands a row to whoever persists it and must not keep it: the dict
129
+ // outlives the request (a warm Lambda container serves many), so a drain that
130
+ // re-read the whole dict would rewrite every row the container had ever seen on
131
+ // every request — and, since the write overwrites by row id, would resurrect
132
+ // rows deleted from the table in between. Persisting is per-request and takes.
133
+ describe("draining the audit log", () => {
134
+ testPromise("taking a row leaves the log empty for the next request", async () => {
135
+ let mockPublish: ReventlessInfra.CommandTopic.publishJsons = async _cmds => ()
136
+ let input = status =>
137
+ {"paymentId": "pay-1", "orderId": "ord-1", "status": status}->Obj.magic
138
+
139
+ let first = await Callback.receive(mockPublish, input("completed"))
140
+ let firstId = first->ReventlessCore.InboundTranslationSlice_Callback.requestIdOf
141
+ expect(
142
+ Callback.auditLog
143
+ ->ReventlessCore.InboundTranslationSlice_Callback.takeAuditRow(firstId)
144
+ ->Option.isSome,
145
+ )->toBe(true)
146
+ expect(Callback.auditLog->Dict.toArray->Array.length)->toBe(0)
147
+
148
+ // A second request drains only its own row — the first one is gone, so it
149
+ // cannot be written a second time.
150
+ let second = await Callback.receive(mockPublish, input("completed"))
151
+ let secondId = second->ReventlessCore.InboundTranslationSlice_Callback.requestIdOf
152
+ expect(secondId == firstId)->toBe(false)
153
+ expect(Callback.auditLog->Dict.toArray->Array.length)->toBe(1)
154
+ expect(
155
+ Callback.auditLog
156
+ ->ReventlessCore.InboundTranslationSlice_Callback.takeAuditRow(firstId)
157
+ ->Option.isSome,
158
+ )->toBe(false)
159
+ expect(
160
+ Callback.auditLog
161
+ ->ReventlessCore.InboundTranslationSlice_Callback.takeAuditRow(secondId)
162
+ ->Option.isSome,
163
+ )->toBe(true)
164
+ })
165
+
166
+ testPromise("a rejected request's row is drained the same way", async () => {
167
+ let mockPublish: ReventlessInfra.CommandTopic.publishJsons = async _cmds => ()
168
+ let result = await Callback.receive(
169
+ mockPublish,
170
+ {"paymentId": "pay-1", "orderId": "ord-1", "status": "pending"}->Obj.magic,
171
+ )
172
+ let id = result->ReventlessCore.InboundTranslationSlice_Callback.requestIdOf
173
+ switch Callback.auditLog->ReventlessCore.InboundTranslationSlice_Callback.takeAuditRow(id) {
174
+ | Some(row) =>
175
+ expect(row.status)->toBe(ReventlessCore.InboundTranslationSlice_Callback.Failure)
176
+ | None => expect(true)->toBe(false)
177
+ }
178
+ expect(Callback.auditLog->Dict.toArray->Array.length)->toBe(0)
179
+ })
180
+ })
181
+
128
182
  describe("multi-command", () => {
129
183
  testPromise("translate returning multiple pairs publishes all commands", async () => {
130
184
  // Use a spec that returns multiple commands
@@ -122,6 +122,45 @@ globalThis.describe("InboundTranslationSlice Callback", () => {
122
122
  globalThis.expect(match[1].status).toBe("Failure");
123
123
  });
124
124
  });
125
+ globalThis.describe("draining the audit log", () => {
126
+ globalThis.test("taking a row leaves the log empty for the next request", async () => {
127
+ let mockPublish = async _cmds => {};
128
+ let first = await Callback.receive(mockPublish, {
129
+ paymentId: "pay-1",
130
+ orderId: "ord-1",
131
+ status: "completed"
132
+ });
133
+ let firstId = InboundTranslationSlice_Callback$ReventlessCore.requestIdOf(first);
134
+ globalThis.expect(Stdlib_Option.isSome(InboundTranslationSlice_Callback$ReventlessCore.takeAuditRow(Callback.auditLog, firstId))).toBe(true);
135
+ globalThis.expect(Object.entries(Callback.auditLog).length).toBe(0);
136
+ let second = await Callback.receive(mockPublish, {
137
+ paymentId: "pay-1",
138
+ orderId: "ord-1",
139
+ status: "completed"
140
+ });
141
+ let secondId = InboundTranslationSlice_Callback$ReventlessCore.requestIdOf(second);
142
+ globalThis.expect(secondId === firstId).toBe(false);
143
+ globalThis.expect(Object.entries(Callback.auditLog).length).toBe(1);
144
+ globalThis.expect(Stdlib_Option.isSome(InboundTranslationSlice_Callback$ReventlessCore.takeAuditRow(Callback.auditLog, firstId))).toBe(false);
145
+ globalThis.expect(Stdlib_Option.isSome(InboundTranslationSlice_Callback$ReventlessCore.takeAuditRow(Callback.auditLog, secondId))).toBe(true);
146
+ });
147
+ globalThis.test("a rejected request's row is drained the same way", async () => {
148
+ let mockPublish = async _cmds => {};
149
+ let result = await Callback.receive(mockPublish, {
150
+ paymentId: "pay-1",
151
+ orderId: "ord-1",
152
+ status: "pending"
153
+ });
154
+ let id = InboundTranslationSlice_Callback$ReventlessCore.requestIdOf(result);
155
+ let row = InboundTranslationSlice_Callback$ReventlessCore.takeAuditRow(Callback.auditLog, id);
156
+ if (row !== undefined) {
157
+ globalThis.expect(row.status).toBe("Failure");
158
+ } else {
159
+ globalThis.expect(true).toBe(false);
160
+ }
161
+ globalThis.expect(Object.entries(Callback.auditLog).length).toBe(0);
162
+ });
163
+ });
125
164
  globalThis.describe("multi-command", () => {
126
165
  globalThis.test("translate returning multiple pairs publishes all commands", async () => {
127
166
  let moduleUrl = import.meta.url;