@effect-agent/storage-cloudflare 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.
@@ -136,6 +136,7 @@ const decodeRecord = Effect.fn("DoScheduleStore.decodeRecord")(function* (
136
136
  const record = yield* Schema.decodeEffect(Schema.fromJsonString(ScheduleRecord))(
137
137
  row.record_json,
138
138
  ).pipe(Effect.mapError(() => corrupt("decode schedule")));
139
+
139
140
  if (
140
141
  record.owner.tenantId !== row.tenant_id ||
141
142
  record.owner.ownerId !== row.owner_id ||
@@ -144,6 +145,7 @@ const decodeRecord = Effect.fn("DoScheduleStore.decodeRecord")(function* (
144
145
  ) {
145
146
  return yield* corrupt("decode schedule index");
146
147
  }
148
+
147
149
  return record;
148
150
  });
149
151
 
@@ -153,6 +155,7 @@ const encodeRecord = Effect.fn("DoScheduleStore.encodeRecord")(function* (
153
155
  const encoded = yield* Schema.encodeEffect(Schema.fromJsonString(ScheduleRecord))(record).pipe(
154
156
  Effect.mapError(() => corrupt("encode schedule")),
155
157
  );
158
+
156
159
  return yield* Schema.decodeUnknownEffect(StoredScheduleJson)(encoded).pipe(
157
160
  Effect.mapError(() => corrupt("encode schedule bounds")),
158
161
  );
@@ -161,6 +164,7 @@ const encodeRecord = Effect.fn("DoScheduleStore.encodeRecord")(function* (
161
164
  const initializeScheduleStore = Effect.fn("DoScheduleStore.initialize")(function* () {
162
165
  const sql = yield* SqlClientService.SqlClient;
163
166
  const operation = "initialize schedule store";
167
+
164
168
  const rawTables = yield* sql<Record<string, unknown>>`
165
169
  SELECT name
166
170
  FROM sqlite_master
@@ -171,6 +175,7 @@ const initializeScheduleStore = Effect.fn("DoScheduleStore.initialize")(function
171
175
  )
172
176
  ORDER BY name
173
177
  `.pipe(Effect.mapError(() => unavailable(operation)));
178
+
174
179
  const tables = yield* decodeRows(Schema.Array(ScheduleTableNameRow), rawTables, operation);
175
180
  const hasState = tables.some((row) => row.name === "effect_agent_schedule_store_state");
176
181
  const hasSchedules = tables.some((row) => row.name === "effect_agent_schedules");
@@ -215,16 +220,20 @@ const initializeScheduleStore = Effect.fn("DoScheduleStore.initialize")(function
215
220
  }),
216
221
  )
217
222
  .pipe(Effect.mapError(() => unavailable(operation)));
223
+
218
224
  return;
219
225
  }
220
226
 
221
227
  if (!hasSchedules) return yield* corrupt(operation);
228
+
222
229
  const rawState = yield* sql<Record<string, unknown>>`
223
230
  SELECT storage_version, alarm_generation
224
231
  FROM effect_agent_schedule_store_state
225
232
  WHERE singleton = 1
226
233
  `.pipe(Effect.mapError(() => unavailable(operation)));
234
+
227
235
  const state = yield* decodeRows(Schema.Array(ScheduleStoreStateRow), rawState, operation);
236
+
228
237
  if (state.length !== 1 || state[0].storage_version !== CURRENT_SCHEDULE_STORE_VERSION) {
229
238
  return yield* corrupt(
230
239
  state.length === 1
@@ -252,6 +261,7 @@ const makeServices = Effect.gen(function* () {
252
261
  AND owner_id = ${key.owner.ownerId}
253
262
  AND schedule_id = ${key.scheduleId}
254
263
  `.pipe(Effect.mapError(() => unavailable(operation)));
264
+
255
265
  return yield* decodeRows(Schema.Array(ScheduleRow), rows, operation);
256
266
  });
257
267
 
@@ -260,8 +270,10 @@ const makeServices = Effect.gen(function* () {
260
270
  operation: string,
261
271
  ): Effect.fn.Return<ScheduleRecord | null, ScheduleStorageError> {
262
272
  const rows = yield* readRows(key, operation);
273
+
263
274
  if (rows.length === 0) return null;
264
275
  if (rows.length !== 1) return yield* corrupt(operation);
276
+
265
277
  return yield* decodeRecord(rows[0]);
266
278
  });
267
279
 
@@ -283,8 +295,11 @@ const makeServices = Effect.gen(function* () {
283
295
  AND owner_id = ${owner.ownerId}
284
296
  AND deadline_at_millis IS NOT NULL
285
297
  `.pipe(Effect.mapError(() => unavailable(operation)));
298
+
286
299
  const decoded = yield* decodeRows(Schema.Array(ScheduleDeadlineRow), rows, operation);
300
+
287
301
  if (decoded.length !== 1) return yield* corrupt(operation);
302
+
288
303
  return decoded[0].deadline_at_millis;
289
304
  });
290
305
 
@@ -299,7 +314,9 @@ const makeServices = Effect.gen(function* () {
299
314
  WHERE singleton = 1
300
315
  RETURNING storage_version, alarm_generation
301
316
  `.pipe(Effect.mapError(() => unavailable(operation)));
317
+
302
318
  const state = yield* decodeRows(Schema.Array(ScheduleStoreStateRow), rawState, operation);
319
+
303
320
  if (state.length !== 1 || state[0].storage_version !== CURRENT_SCHEDULE_STORE_VERSION) {
304
321
  return yield* corrupt(operation);
305
322
  }
@@ -313,15 +330,19 @@ const makeServices = Effect.gen(function* () {
313
330
  const operation = "insert schedule";
314
331
  const canonical = yield* decodeBoundary(ScheduleRecord, record, operation);
315
332
  const recordJson = yield* encodeRecord(canonical);
333
+
316
334
  const result = yield* transactions.run((replace) =>
317
335
  Effect.gen(function* () {
318
336
  const existing = yield* readOne(canonical, operation);
337
+
319
338
  if (existing !== null) {
320
339
  if (existing.creationFingerprint === canonical.creationFingerprint) {
321
340
  return { record: existing, inserted: false } as const;
322
341
  }
342
+
323
343
  return yield* ScheduleConflict.make({ reason: "creation", key: canonical });
324
344
  }
345
+
325
346
  const rawCounts = yield* sql<Record<string, unknown>>`
326
347
  SELECT COUNT(*) AS schedule_count
327
348
  FROM effect_agent_schedules
@@ -330,7 +351,9 @@ const makeServices = Effect.gen(function* () {
330
351
  AND (json_extract(record_json, '$.pending') IS NOT NULL OR
331
352
  (json_extract(record_json, '$.state') != 'cancelled' AND json_extract(record_json, '$.nextAtMillis') IS NOT NULL))
332
353
  `.pipe(Effect.mapError(() => unavailable(operation)));
354
+
333
355
  const counts = yield* decodeRows(Schema.Array(ScheduleCountRow), rawCounts, operation);
356
+
334
357
  if (counts.length !== 1) return yield* corrupt(operation);
335
358
  if (counts[0].schedule_count >= ownerLimit) {
336
359
  return yield* ScheduleCapacityError.make({ limit: ownerLimit });
@@ -348,17 +371,22 @@ const makeServices = Effect.gen(function* () {
348
371
  )
349
372
  `.pipe(Effect.mapError(() => unavailable(operation)));
350
373
  const deadline = yield* readNextDeadline(undefined, operation);
374
+
351
375
  yield* replaceAlarm(replace, deadline, operation);
376
+
352
377
  return { record: canonical, inserted: true } as const;
353
378
  }),
354
379
  );
380
+
355
381
  if (result.inserted) yield* scheduleFailpoint.hit("schedule:insert:after");
382
+
356
383
  return result.record;
357
384
  },
358
385
  );
359
386
 
360
387
  const get: ScheduleStore["Service"]["get"] = Effect.fn("DoScheduleStore.get")(function* (key) {
361
388
  const canonical = yield* decodeBoundary(ScheduleKey, key, "get schedule");
389
+
362
390
  return yield* readOne(canonical, "get schedule");
363
391
  });
364
392
 
@@ -367,6 +395,7 @@ const makeServices = Effect.gen(function* () {
367
395
  ): Effect.fn.Return<SchedulePage, ScheduleStorageError> {
368
396
  const operation = "list schedules";
369
397
  const request = yield* decodeBoundary(SchedulePageRequest, requestValue, operation);
398
+
370
399
  const rows =
371
400
  request.after === undefined
372
401
  ? yield* sql<Record<string, unknown>>`
@@ -386,10 +415,12 @@ const makeServices = Effect.gen(function* () {
386
415
  ORDER BY schedule_id
387
416
  LIMIT ${request.limit + 1}
388
417
  `.pipe(Effect.mapError(() => unavailable(operation)));
418
+
389
419
  const decoded = yield* decodeRows(Schema.Array(ScheduleRow), rows, operation);
390
420
  const records = yield* Effect.forEach(decoded, decodeRecord);
391
421
  const hasNext = records.length > request.limit;
392
422
  const items = hasNext ? records.slice(0, request.limit) : records;
423
+
393
424
  return { items, next: hasNext ? (items.at(-1)?.scheduleId ?? null) : null };
394
425
  });
395
426
 
@@ -401,13 +432,17 @@ const makeServices = Effect.gen(function* () {
401
432
  const operation = "change schedule";
402
433
  const canonicalKey = yield* decodeBoundary(ScheduleKey, key, operation);
403
434
  const canonicalChange = yield* decodeBoundary(ScheduleChange, change, operation);
435
+
404
436
  const result = yield* transactions.run((replace) =>
405
437
  Effect.gen(function* () {
406
438
  const current = yield* readOne(canonicalKey, operation);
439
+
407
440
  if (current === null) return yield* ScheduleNotFound.make({ key: canonicalKey });
408
441
  const transition = applyScheduleChange(current, canonicalChange);
442
+
409
443
  if (Result.isFailure(transition)) return yield* transition.failure;
410
444
  const next = transition.success;
445
+
411
446
  if (!scheduleUsesCapacity(current) && scheduleUsesCapacity(next)) {
412
447
  const rawCounts = yield* sql<Record<string, unknown>>`
413
448
  SELECT COUNT(*) AS schedule_count FROM effect_agent_schedules
@@ -415,13 +450,16 @@ const makeServices = Effect.gen(function* () {
415
450
  AND (json_extract(record_json, '$.pending') IS NOT NULL OR
416
451
  (json_extract(record_json, '$.state') != 'cancelled' AND json_extract(record_json, '$.nextAtMillis') IS NOT NULL))
417
452
  `.pipe(Effect.mapError(() => unavailable(operation)));
453
+
418
454
  const counts = yield* decodeRows(Schema.Array(ScheduleCountRow), rawCounts, operation);
455
+
419
456
  if (counts.length !== 1) return yield* corrupt(operation);
420
457
  if (counts[0].schedule_count >= ownerLimit)
421
458
  return yield* ScheduleCapacityError.make({ limit: ownerLimit });
422
459
  }
423
460
  if (next === current) return { record: current, changed: false } as const;
424
461
  const recordJson = yield* encodeRecord(next);
462
+
425
463
  yield* scheduleFailpoint.hit(`schedule:${canonicalChange._tag.toLowerCase()}:before`);
426
464
  yield* sql`
427
465
  UPDATE effect_agent_schedules
@@ -431,13 +469,17 @@ const makeServices = Effect.gen(function* () {
431
469
  AND schedule_id = ${canonicalKey.scheduleId}
432
470
  `.pipe(Effect.mapError(() => unavailable(operation)));
433
471
  const deadline = yield* readNextDeadline(undefined, operation);
472
+
434
473
  yield* replaceAlarm(replace, deadline, operation);
474
+
435
475
  return { record: next, changed: true } as const;
436
476
  }),
437
477
  );
478
+
438
479
  if (result.changed) {
439
480
  yield* scheduleFailpoint.hit(`schedule:${canonicalChange._tag.toLowerCase()}:after`);
440
481
  }
482
+
441
483
  return result.record;
442
484
  });
443
485
 
@@ -448,18 +490,21 @@ const makeServices = Effect.gen(function* () {
448
490
  after?: ScheduleDueCursor,
449
491
  ) {
450
492
  const operation = "query due schedules";
493
+
451
494
  const cursor =
452
495
  after === undefined
453
496
  ? undefined
454
497
  : yield* Schema.decodeUnknownEffect(ScheduleDueCursor)(after).pipe(
455
498
  Effect.mapError(() => corrupt(operation)),
456
499
  );
500
+
457
501
  const continuation =
458
502
  cursor === undefined
459
503
  ? sql`1 = 1`
460
504
  : sql`
461
505
  (deadline_at_millis, tenant_id, owner_id, schedule_id) >
462
506
  (${cursor.deadlineAtMillis}, ${cursor.owner.tenantId}, ${cursor.owner.ownerId}, ${cursor.scheduleId})`;
507
+
463
508
  const rows =
464
509
  owner === undefined
465
510
  ? yield* sql<Record<string, unknown>>`
@@ -478,7 +523,9 @@ const makeServices = Effect.gen(function* () {
478
523
  ORDER BY deadline_at_millis, schedule_id
479
524
  LIMIT ${limit}
480
525
  `.pipe(Effect.mapError(() => unavailable(operation)));
526
+
481
527
  const decoded = yield* decodeRows(Schema.Array(ScheduleDueRow), rows, operation);
528
+
482
529
  return decoded.map((row) => ({
483
530
  owner: { tenantId: row.tenant_id, ownerId: row.owner_id },
484
531
  scheduleId: row.schedule_id,
@@ -490,6 +537,7 @@ const makeServices = Effect.gen(function* () {
490
537
  "DoScheduleStore.nextDeadline",
491
538
  )(function* (owner?: ScheduleOwner) {
492
539
  const operation = "query next schedule deadline";
540
+
493
541
  return yield* readNextDeadline(owner, operation);
494
542
  });
495
543
 
@@ -507,6 +555,7 @@ const makeServices = Effect.gen(function* () {
507
555
  yield* transactions.run((replace) =>
508
556
  Effect.gen(function* () {
509
557
  const deadline = yield* readNextDeadline(undefined, "reconcile schedule alarm");
558
+
510
559
  yield* replaceAlarm(replace, deadline, "reconcile schedule alarm");
511
560
  }),
512
561
  );
@@ -17,6 +17,7 @@ export class DoStorageFailpointTestControl extends Context.Service<
17
17
  static readonly layer = Layer.effectContext(
18
18
  Effect.gen(function* () {
19
19
  const handler = yield* Ref.make<DoStorageFailpointHandler>(noFailpoint);
20
+
20
21
  return Context.make(
21
22
  DoStorageFailpoint,
22
23
  DoStorageFailpoint.of({