@cosmicdrift/kumiko-framework 0.209.0 → 0.209.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-framework",
3
- "version": "0.209.0",
3
+ "version": "0.209.1",
4
4
  "description": "Framework core — engine, pipeline, API, DB, and every other bit that makes Kumiko go.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -190,7 +190,7 @@
190
190
  "./package.json": "./package.json"
191
191
  },
192
192
  "dependencies": {
193
- "@cosmicdrift/kumiko-types": "0.209.0",
193
+ "@cosmicdrift/kumiko-types": "0.209.1",
194
194
  "bullmq": "^5.76.7",
195
195
  "bun-types": "^1.3.13",
196
196
  "hono": "^4.13.1",
@@ -206,7 +206,7 @@
206
206
  "zod": "^4.4.3"
207
207
  },
208
208
  "devDependencies": {
209
- "@cosmicdrift/kumiko-dispatcher-live": "0.209.0",
209
+ "@cosmicdrift/kumiko-dispatcher-live": "0.209.1",
210
210
  "bun-types": "^1.3.13",
211
211
  "pino-pretty": "^13.1.3"
212
212
  },
@@ -141,7 +141,7 @@ export async function backfillEventPiiEncryption(
141
141
  result.erasedFields += outcome.erased;
142
142
  if (!options.dryRun) {
143
143
  await raw.unsafe(`UPDATE "kumiko_events" SET "payload" = $1::jsonb WHERE "id" = $2`, [
144
- JSON.stringify(outcome.payload),
144
+ outcome.payload,
145
145
  row.id,
146
146
  ]);
147
147
  }
@@ -279,3 +279,29 @@ describe("backfillEventPiiEncryption", () => {
279
279
  );
280
280
  });
281
281
  });
282
+
283
+ describe("backfillEventPiiEncryption: raw jsonb column type (fw#2253)", () => {
284
+ // The UPDATE used to wrap outcome.payload in JSON.stringify before handing
285
+ // it to the ::jsonb cast — Bun.SQL already serializes objects, so the
286
+ // double encoding produced a jsonb STRING scalar instead of an object.
287
+ // loadAggregate stayed green either way: the typed read path (bun-db's
288
+ // coerceRow) re-parses string-shaped jsonb columns on the way out, which
289
+ // cancels the write-side bug out. Only raw SQL consumers (this query,
290
+ // GDPR exports, MSP replays) saw the corruption, so assert on the column
291
+ // type directly instead of going through loadAggregate.
292
+ test("UPDATE writes payload as a jsonb object, not a double-encoded string", async () => {
293
+ const c1 = generateId();
294
+ await appendPlain(c1, "contact", "contact.created", { id: c1, email: "raw@x.com" });
295
+
296
+ armKms();
297
+ const result = await backfillEventPiiEncryption(testDb.db, registry);
298
+ expect(result.updatedEvents).toBe(1);
299
+
300
+ const rows = (await asRawClient(testDb.db).unsafe(
301
+ `SELECT jsonb_typeof(payload) AS t FROM "kumiko_events" WHERE aggregate_id = $1`,
302
+ [c1],
303
+ )) as ReadonlyArray<{ t: string }>;
304
+ expect(rows).toHaveLength(1);
305
+ expect(rows[0]?.t).toBe("object");
306
+ });
307
+ });
@@ -200,7 +200,7 @@ export type EnqueueProjectionRebuildDeps = {
200
200
  readonly db: DbConnection;
201
201
  readonly registry: Registry;
202
202
  // Present + projection-rebuild job registered (jobs composed) → tracked,
203
- // retryable job (read_job_runs + store_job_run_logs). Absent → inline rebuild.
203
+ // retryable job (store_job_runs + store_job_run_logs). Absent → inline rebuild.
204
204
  readonly jobRunner?: JobRunner;
205
205
  };
206
206