@effect-agent/storage-sqlite 0.1.0-beta.42 → 0.1.0-beta.44

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.
@@ -1 +1 @@
1
- {"version":3,"file":"testing.mjs","names":[],"sources":["../src/sqlite-storage-failpoint-testing.ts"],"sourcesContent":["import { Context, Effect, Layer, Ref } from \"effect\";\n\nimport {\n SqliteStorageFailpoint,\n type SqliteStorageFailpointHandler,\n} from \"./sqlite-storage-failpoint.ts\";\n\nconst noFailpoint: SqliteStorageFailpointHandler = () => Effect.void;\n\n/** Test-only control for replacing the active SQLite failpoint handler. */\nexport class SqliteStorageFailpointTestControl extends Context.Service<\n SqliteStorageFailpointTestControl,\n {\n readonly clear: Effect.Effect<void>;\n readonly setHandler: (handler: SqliteStorageFailpointHandler) => Effect.Effect<void>;\n }\n>()(\"@effect-agent/storage-sqlite/SqliteStorageFailpointTestControl\") {\n /** Reusable test Layer with a control service backed by the same handler Ref. */\n static readonly layer = Layer.effectContext(\n Effect.gen(function* () {\n const handler = yield* Ref.make<SqliteStorageFailpointHandler>(noFailpoint);\n return Context.make(\n SqliteStorageFailpoint,\n SqliteStorageFailpoint.of({\n hit: (location) => Ref.get(handler).pipe(Effect.flatMap((current) => current(location))),\n }),\n ).pipe(\n Context.add(\n SqliteStorageFailpointTestControl,\n SqliteStorageFailpointTestControl.of({\n clear: Ref.set(handler, noFailpoint),\n setHandler: (next) => Ref.set(handler, next),\n }),\n ),\n );\n }),\n );\n}\n"],"mappings":";;;AAOA,MAAM,oBAAmD,OAAO;;AAGhE,IAAa,oCAAb,MAAa,0CAA0C,QAAQ,QAM7D,CAAC,CAAC,gEAAgE,CAAC,CAAC;;CAEpE,OAAgB,QAAQ,MAAM,cAC5B,OAAO,IAAI,aAAa;EACtB,MAAM,UAAU,OAAO,IAAI,KAAoC,WAAW;EAC1E,OAAO,QAAQ,KACb,wBACA,uBAAuB,GAAG,EACxB,MAAM,aAAa,IAAI,IAAI,OAAO,CAAC,CAAC,KAAK,OAAO,SAAS,YAAY,QAAQ,QAAQ,CAAC,CAAC,EACzF,CAAC,CACH,CAAC,CAAC,KACA,QAAQ,IACN,mCACA,kCAAkC,GAAG;GACnC,OAAO,IAAI,IAAI,SAAS,WAAW;GACnC,aAAa,SAAS,IAAI,IAAI,SAAS,IAAI;EAC7C,CAAC,CACH,CACF;CACF,CAAC,CACH;AACF"}
1
+ {"version":3,"file":"testing.mjs","names":[],"sources":["../src/sqlite-storage-failpoint-testing.ts"],"sourcesContent":["import { Context, Effect, Layer, Ref } from \"effect\";\n\nimport {\n SqliteStorageFailpoint,\n type SqliteStorageFailpointHandler,\n} from \"./sqlite-storage-failpoint.ts\";\n\nconst noFailpoint: SqliteStorageFailpointHandler = () => Effect.void;\n\n/** Test-only control for replacing the active SQLite failpoint handler. */\nexport class SqliteStorageFailpointTestControl extends Context.Service<\n SqliteStorageFailpointTestControl,\n {\n readonly clear: Effect.Effect<void>;\n readonly setHandler: (handler: SqliteStorageFailpointHandler) => Effect.Effect<void>;\n }\n>()(\"@effect-agent/storage-sqlite/SqliteStorageFailpointTestControl\") {\n /** Reusable test Layer with a control service backed by the same handler Ref. */\n static readonly layer = Layer.effectContext(\n Effect.gen(function* () {\n const handler = yield* Ref.make<SqliteStorageFailpointHandler>(noFailpoint);\n\n return Context.make(\n SqliteStorageFailpoint,\n SqliteStorageFailpoint.of({\n hit: (location) => Ref.get(handler).pipe(Effect.flatMap((current) => current(location))),\n }),\n ).pipe(\n Context.add(\n SqliteStorageFailpointTestControl,\n SqliteStorageFailpointTestControl.of({\n clear: Ref.set(handler, noFailpoint),\n setHandler: (next) => Ref.set(handler, next),\n }),\n ),\n );\n }),\n );\n}\n"],"mappings":";;;AAOA,MAAM,oBAAmD,OAAO;;AAGhE,IAAa,oCAAb,MAAa,0CAA0C,QAAQ,QAM7D,CAAC,CAAC,gEAAgE,CAAC,CAAC;;CAEpE,OAAgB,QAAQ,MAAM,cAC5B,OAAO,IAAI,aAAa;EACtB,MAAM,UAAU,OAAO,IAAI,KAAoC,WAAW;EAE1E,OAAO,QAAQ,KACb,wBACA,uBAAuB,GAAG,EACxB,MAAM,aAAa,IAAI,IAAI,OAAO,CAAC,CAAC,KAAK,OAAO,SAAS,YAAY,QAAQ,QAAQ,CAAC,CAAC,EACzF,CAAC,CACH,CAAC,CAAC,KACA,QAAQ,IACN,mCACA,kCAAkC,GAAG;GACnC,OAAO,IAAI,IAAI,SAAS,WAAW;GACnC,aAAa,SAAS,IAAI,IAAI,SAAS,IAAI;EAC7C,CAAC,CACH,CACF;CACF,CAAC,CACH;AACF"}
package/package.json CHANGED
@@ -1,49 +1 @@
1
- {
2
- "name": "@effect-agent/storage-sqlite",
3
- "version": "0.1.0-beta.42",
4
- "exports": {
5
- ".": {
6
- "types": "./dist/index.d.mts",
7
- "default": "./dist/index.mjs"
8
- },
9
- "./testing": {
10
- "types": "./dist/testing.d.mts",
11
- "default": "./dist/testing.mjs"
12
- }
13
- },
14
- "dependencies": {
15
- "@effect-agent/core": "0.1.0-beta.42",
16
- "@effect-agent/thread": "0.1.0-beta.42",
17
- "@effect/platform-node": "4.0.0-rc.111",
18
- "@effect/sql-sqlite-node": "4.0.0-rc.111"
19
- },
20
- "peerDependencies": {
21
- "effect": "^4.0.0-rc.111"
22
- },
23
- "description": "Node SQLite persistence adapters for the Effect Agent durable runtime and optional memory lifecycle.",
24
- "license": "MIT",
25
- "repository": {
26
- "type": "git",
27
- "url": "git+https://github.com/danieljvdm/effect-agent.git",
28
- "directory": "packages/storage-sqlite"
29
- },
30
- "files": [
31
- "dist",
32
- "src"
33
- ],
34
- "type": "module",
35
- "publishConfig": {
36
- "access": "public"
37
- },
38
- "scripts": {
39
- "build": "vp pack",
40
- "check": "tsc --noEmit -p tsconfig.json",
41
- "test": "vp test --passWithNoTests"
42
- },
43
- "devDependencies": {
44
- "@effect/vitest": "4.0.0-rc.111",
45
- "effect": "4.0.0-rc.111",
46
- "typescript": "7.0.2",
47
- "vite-plus": "0.3.0"
48
- }
49
- }
1
+ {"name":"@effect-agent/storage-sqlite","version":"0.1.0-beta.44","dependencies":{"@effect-agent/core":"0.1.0-beta.44","@effect-agent/thread":"0.1.0-beta.44","@effect/platform-node":"4.0.0-rc.112","@effect/sql-sqlite-node":"4.0.0-rc.112"},"devDependencies":{"@effect/vitest":"4.0.0-rc.112","effect":"4.0.0-rc.112","typescript":"7.0.2","vite-plus":"0.3.0"},"peerDependencies":{"effect":"^4.0.0-rc.112"},"exports":{".":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"./testing":{"types":"./dist/testing.d.mts","default":"./dist/testing.mjs"}},"description":"Node SQLite persistence adapters for the Effect Agent durable runtime and optional memory lifecycle.","license":"MIT","repository":{"type":"git","url":"git+https://github.com/danieljvdm/effect-agent.git","directory":"packages/storage-sqlite"},"files":["dist","src"],"type":"module","publishConfig":{"access":"public"},"scripts":{"build":"vp pack","check":"tsc --noEmit -p tsconfig.json","test":"vp test --passWithNoTests"}}
package/src/errors.ts CHANGED
@@ -149,6 +149,7 @@ export const SqliteStorageFailpointLocation = Schema.Literals([
149
149
  "ledger:child-settled:before",
150
150
  "ledger:child-settled:after",
151
151
  ]);
152
+
152
153
  export type SqliteStorageFailpointLocation = typeof SqliteStorageFailpointLocation.Type;
153
154
 
154
155
  /** Deterministic test-only fault or pause injected at a SQLite operation boundary. */
@@ -111,14 +111,19 @@ const decodeProgress = Effect.fn("SqliteActivityStore.decodeProgress")(function*
111
111
  const header = yield* Schema.decodeEffect(Schema.fromJsonString(StoredVersionHeader))(value).pipe(
112
112
  Effect.mapError(() => storeError(operation, "corrupt")),
113
113
  );
114
+
114
115
  if (header.version !== STORAGE_VERSION) {
115
116
  return yield* storeError(operation, "incompatible");
116
117
  }
118
+
117
119
  const progress = yield* Schema.decodeEffect(Schema.fromJsonString(ActivityProgress))(value).pipe(
118
120
  Effect.mapError(() => storeError(operation, "corrupt")),
119
121
  );
122
+
120
123
  const canonical = yield* encodeProgress(progress, operation);
124
+
121
125
  if (canonical !== value) return yield* storeError(operation, "corrupt");
126
+
122
127
  return progress;
123
128
  });
124
129
 
@@ -135,6 +140,7 @@ const validateProgress = Effect.fn("SqliteActivityStore.validateProgress")(funct
135
140
  ) {
136
141
  return yield* storeError(operation, "corrupt");
137
142
  }
143
+
138
144
  return progress;
139
145
  });
140
146
 
@@ -170,19 +176,23 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
170
176
  version INTEGER NOT NULL
171
177
  )
172
178
  `;
179
+
173
180
  const metadataRows = yield* sql<Record<string, unknown>>`
174
181
  SELECT version FROM effect_agent_activity_metadata
175
182
  WHERE component = ${METADATA_COMPONENT}
176
183
  `;
184
+
177
185
  const metadata = yield* decodeRows(
178
186
  ActivityMetadataRow,
179
187
  metadataRows,
180
188
  "decode activity schema version",
181
189
  );
190
+
182
191
  if (metadata.length > 1) {
183
192
  return yield* storeError("decode activity schema version", "corrupt");
184
193
  }
185
194
  const currentVersion = metadata[0]?.version;
195
+
186
196
  if (currentVersion !== undefined && currentVersion !== STORAGE_VERSION) {
187
197
  return yield* storeError("initialize activity schema", "incompatible");
188
198
  }
@@ -191,11 +201,13 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
191
201
  SELECT name FROM sqlite_master
192
202
  WHERE type = 'table' AND name = ${STATE_TABLE}
193
203
  `;
204
+
194
205
  const existing = yield* decodeRows(
195
206
  ActivityTableRow,
196
207
  tableRows,
197
208
  "inspect activity schema",
198
209
  );
210
+
199
211
  if (existing.length > 0) {
200
212
  return yield* storeError("initialize activity schema", "incompatible");
201
213
  }
@@ -244,14 +256,18 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
244
256
  `,
245
257
  operation,
246
258
  );
259
+
247
260
  const rows = yield* decodeRows(ActivityStateRow, rawRows, operation);
261
+
248
262
  if (rows.length === 0) return null;
249
263
  if (rows.length !== 1) return yield* storeError(operation, "corrupt");
250
264
  const row = rows[0];
265
+
251
266
  if (row.format_version !== STORAGE_VERSION) {
252
267
  return yield* storeError(operation, "incompatible");
253
268
  }
254
269
  const progress = yield* decodeProgress(row.progress_json, operation);
270
+
255
271
  yield* validateProgress(progress, operation);
256
272
  if (
257
273
  !sameKey(progress.key, key) ||
@@ -265,6 +281,7 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
265
281
  ) {
266
282
  return yield* storeError(operation, "corrupt");
267
283
  }
284
+
268
285
  return progress;
269
286
  });
270
287
 
@@ -275,7 +292,9 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
275
292
  sql<Record<string, unknown>>`SELECT changes() AS changed`,
276
293
  operation,
277
294
  );
295
+
278
296
  const rows = yield* decodeRows(ActivityChangeCountRow, rawRows, operation);
297
+
279
298
  if (rows.length !== 1 || rows[0].changed !== 1) {
280
299
  return yield* storeError(operation, "corrupt");
281
300
  }
@@ -286,6 +305,7 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
286
305
  operation: string,
287
306
  ) {
288
307
  const progressJson = yield* encodeProgress(progress, operation);
308
+
289
309
  yield* sql`
290
310
  INSERT INTO effect_agent_activity_processor_state_v1 (
291
311
  processor_id, processor_version, thread_id, format_version, through_sequence,
@@ -305,6 +325,7 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
305
325
  operation: string,
306
326
  ) {
307
327
  const progressJson = yield* encodeProgress(next, operation);
328
+
308
329
  yield* sql`
309
330
  UPDATE effect_agent_activity_processor_state_v1
310
331
  SET format_version = ${STORAGE_VERSION},
@@ -328,6 +349,7 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
328
349
  requireSequence: boolean,
329
350
  ): Effect.fn.Return<ActivityProgress, ActivityOwnershipLost> {
330
351
  const now = yield* Clock.currentTimeMillis;
352
+
331
353
  if (
332
354
  progress === null ||
333
355
  !sameKey(progress.key, claim.key) ||
@@ -338,6 +360,7 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
338
360
  ) {
339
361
  return yield* ownershipLost(claim);
340
362
  }
363
+
341
364
  return progress;
342
365
  });
343
366
 
@@ -345,6 +368,7 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
345
368
  "SqliteActivityStore.inspect",
346
369
  )(function* (key) {
347
370
  const decodedKey = yield* decodeInput(ActivityProcessorKey, key, "inspect activity progress");
371
+
348
372
  return yield* readProgress(decodedKey, "inspect activity progress");
349
373
  });
350
374
 
@@ -352,18 +376,22 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
352
376
  function* (request) {
353
377
  const operation = "claim activity progress";
354
378
  const decoded = yield* decodeInput(ActivityClaimRequest, request, operation);
379
+
355
380
  yield* failpoint.hit("activity:claim:before");
381
+
356
382
  const claimed = yield* sql
357
383
  .withTransaction(
358
384
  Effect.gen(function* () {
359
385
  const current = yield* readProgress(decoded.key, operation);
360
386
  const now = yield* Clock.currentTimeMillis;
387
+
361
388
  if (current !== null && current.owner !== null && current.leaseExpiresAt > now) {
362
389
  return yield* ActivityBusy.make({
363
390
  key: decoded.key,
364
391
  leaseExpiresAt: current.leaseExpiresAt,
365
392
  });
366
393
  }
394
+
367
395
  const next = yield* Schema.decodeUnknownEffect(ActivityProgress)({
368
396
  version: STORAGE_VERSION,
369
397
  key: decoded.key,
@@ -374,16 +402,21 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
374
402
  pending: current?.pending ?? null,
375
403
  advancedAt: current?.advancedAt ?? null,
376
404
  }).pipe(Effect.mapError(() => storeError(operation, "corrupt")));
405
+
377
406
  if (current === null) yield* insertProgress(next, operation);
378
407
  else yield* updateProgress(current, next, operation);
379
408
  yield* failpoint.hit("activity:claim:after-state");
380
409
  const result = makeClaim(next);
410
+
381
411
  if (result === null) return yield* storeError(operation, "corrupt");
412
+
382
413
  return result;
383
414
  }),
384
415
  )
385
416
  .pipe(Effect.catchTag("SqlError", () => Effect.fail(storeError(operation))));
417
+
386
418
  yield* failpoint.hit("activity:claim:after");
419
+
387
420
  return claimed;
388
421
  },
389
422
  );
@@ -394,7 +427,9 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
394
427
  const operation = "prepare activity output";
395
428
  const claim = yield* decodeInput(ActivityClaim, request.claim, operation);
396
429
  const work = yield* decodeInput(PreparedActivity, request.work, operation);
430
+
397
431
  yield* failpoint.hit("activity:prepare:before");
432
+
398
433
  const result = yield* sql
399
434
  .withTransaction(
400
435
  Effect.gen(function* () {
@@ -403,23 +438,29 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
403
438
  claim,
404
439
  true,
405
440
  );
441
+
406
442
  if (current.pending !== null) {
407
443
  if (sameWork(current.pending, work)) {
408
444
  return { work: current.pending, changed: false } as const;
409
445
  }
446
+
410
447
  return yield* ActivityWorkConflict.make({ key: claim.key, workId: work.workId });
411
448
  }
412
449
  if (!sameKey(work.key, claim.key) || work.sequence !== current.throughSequence + 1) {
413
450
  return yield* ActivityWorkConflict.make({ key: claim.key, workId: work.workId });
414
451
  }
415
452
  const next = ActivityProgress.make({ ...current, pending: work });
453
+
416
454
  yield* updateProgress(current, next, operation);
417
455
  yield* failpoint.hit("activity:prepare:after-state");
456
+
418
457
  return { work, changed: true } as const;
419
458
  }),
420
459
  )
421
460
  .pipe(Effect.catchTag("SqlError", () => Effect.fail(storeError(operation))));
461
+
422
462
  if (result.changed) yield* failpoint.hit("activity:prepare:after");
463
+
423
464
  return result.work;
424
465
  });
425
466
 
@@ -429,7 +470,9 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
429
470
  const operation = "advance activity progress";
430
471
  const claim = yield* decodeInput(ActivityClaim, request.claim, operation);
431
472
  const workId = yield* decodeInput(Digest, request.workId, operation);
473
+
432
474
  yield* failpoint.hit("activity:advance:before");
475
+
433
476
  const nextClaim = yield* sql
434
477
  .withTransaction(
435
478
  Effect.gen(function* () {
@@ -438,24 +481,31 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
438
481
  claim,
439
482
  true,
440
483
  );
484
+
441
485
  if (current.pending === null || current.pending.workId !== workId) {
442
486
  return yield* ActivityWorkConflict.make({ key: claim.key, workId });
443
487
  }
488
+
444
489
  const next = ActivityProgress.make({
445
490
  ...current,
446
491
  throughSequence: current.pending.sequence,
447
492
  pending: null,
448
493
  advancedAt: yield* Clock.currentTimeMillis,
449
494
  });
495
+
450
496
  yield* updateProgress(current, next, operation);
451
497
  yield* failpoint.hit("activity:advance:after-state");
452
498
  const result = makeClaim(next);
499
+
453
500
  if (result === null) return yield* storeError(operation, "corrupt");
501
+
454
502
  return result;
455
503
  }),
456
504
  )
457
505
  .pipe(Effect.catchTag("SqlError", () => Effect.fail(storeError(operation))));
506
+
458
507
  yield* failpoint.hit("activity:advance:after");
508
+
459
509
  return nextClaim;
460
510
  });
461
511
 
@@ -464,11 +514,13 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
464
514
  )(function* (claim) {
465
515
  const operation = "release activity claim";
466
516
  const decoded = yield* decodeInput(ActivityClaim, claim, operation);
517
+
467
518
  yield* failpoint.hit("activity:release:before");
468
519
  yield* sql
469
520
  .withTransaction(
470
521
  Effect.gen(function* () {
471
522
  const current = yield* readProgress(decoded.key, operation);
523
+
472
524
  if (
473
525
  current === null ||
474
526
  current.owner !== decoded.owner ||
@@ -477,6 +529,7 @@ const makeActivityStore = Effect.fn("SqliteActivityStore.make")(function* () {
477
529
  return yield* ownershipLost(decoded);
478
530
  }
479
531
  const next = ActivityProgress.make({ ...current, owner: null, leaseExpiresAt: 0 });
532
+
480
533
  yield* updateProgress(current, next, operation);
481
534
  yield* failpoint.hit("activity:release:after-state");
482
535
  }),