@yielded/agent-storage-cloudflare 0.1.0-beta.166

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 (77) hide show
  1. package/LICENSE +21 -0
  2. package/dist/DoMemoryStore.d.mts +30 -0
  3. package/dist/DoMemoryStore.mjs +66 -0
  4. package/dist/DoMemoryStore.mjs.map +1 -0
  5. package/dist/DoMessageDeliveryStore.d.mts +17 -0
  6. package/dist/DoMessageDeliveryStore.mjs +30 -0
  7. package/dist/DoMessageDeliveryStore.mjs.map +1 -0
  8. package/dist/DoScheduleStore.d.mts +36 -0
  9. package/dist/DoScheduleStore.mjs +388 -0
  10. package/dist/DoScheduleStore.mjs.map +1 -0
  11. package/dist/DoStorageConfig.d.mts +45 -0
  12. package/dist/DoStorageConfig.mjs +52 -0
  13. package/dist/DoStorageConfig.mjs.map +1 -0
  14. package/dist/DoStorageError-CGuAfSwF.d.mts +101 -0
  15. package/dist/DoStorageError.d.mts +2 -0
  16. package/dist/DoStorageError.mjs +167 -0
  17. package/dist/DoStorageError.mjs.map +1 -0
  18. package/dist/DoStorageFailpoint.d.mts +17 -0
  19. package/dist/DoStorageFailpoint.mjs +14 -0
  20. package/dist/DoStorageFailpoint.mjs.map +1 -0
  21. package/dist/DoStorageFailpointTesting.d.mts +24 -0
  22. package/dist/DoStorageFailpointTesting.mjs +22 -0
  23. package/dist/DoStorageFailpointTesting.mjs.map +1 -0
  24. package/dist/DoStorageVersion-iNSyi3cJ.d.mts +12 -0
  25. package/dist/DoStorageVersion.d.mts +2 -0
  26. package/dist/DoStorageVersion.mjs +8 -0
  27. package/dist/DoStorageVersion.mjs.map +1 -0
  28. package/dist/DoSubmissionLedger.d.mts +23 -0
  29. package/dist/DoSubmissionLedger.mjs +1979 -0
  30. package/dist/DoSubmissionLedger.mjs.map +1 -0
  31. package/dist/DoSubscriptionStore.d.mts +25 -0
  32. package/dist/DoSubscriptionStore.mjs +186 -0
  33. package/dist/DoSubscriptionStore.mjs.map +1 -0
  34. package/dist/DoThreadStore-D_83OjCH.d.mts +72 -0
  35. package/dist/DoThreadStore.d.mts +2 -0
  36. package/dist/DoThreadStore.mjs +529 -0
  37. package/dist/DoThreadStore.mjs.map +1 -0
  38. package/dist/MemoryProtocol.d.mts +38160 -0
  39. package/dist/MemoryProtocol.mjs +219 -0
  40. package/dist/MemoryProtocol.mjs.map +1 -0
  41. package/dist/PortProtocol.d.mts +7726 -0
  42. package/dist/PortProtocol.mjs +234 -0
  43. package/dist/PortProtocol.mjs.map +1 -0
  44. package/dist/PortRouting.d.mts +97 -0
  45. package/dist/PortRouting.mjs +544 -0
  46. package/dist/PortRouting.mjs.map +1 -0
  47. package/dist/do-journal-DUhjZkOf.mjs +1352 -0
  48. package/dist/do-journal-DUhjZkOf.mjs.map +1 -0
  49. package/dist/index.d.mts +14 -0
  50. package/dist/index.mjs +14 -0
  51. package/dist/migrations-DtKz_Gxr.mjs +306 -0
  52. package/dist/migrations-DtKz_Gxr.mjs.map +1 -0
  53. package/dist/owned-state-DHm2aRXx.mjs +119 -0
  54. package/dist/owned-state-DHm2aRXx.mjs.map +1 -0
  55. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  56. package/package.json +1 -0
  57. package/src/DoMemoryStore.ts +73 -0
  58. package/src/DoMessageDeliveryStore.ts +39 -0
  59. package/src/DoScheduleStore.ts +622 -0
  60. package/src/DoStorageConfig.ts +53 -0
  61. package/src/DoStorageError.ts +180 -0
  62. package/src/DoStorageFailpoint.ts +20 -0
  63. package/src/DoStorageFailpointTesting.ts +41 -0
  64. package/src/DoStorageVersion.ts +2 -0
  65. package/src/DoSubmissionLedger.ts +4027 -0
  66. package/src/DoSubscriptionStore.ts +327 -0
  67. package/src/DoThreadStore.ts +1121 -0
  68. package/src/MemoryProtocol.ts +355 -0
  69. package/src/PortProtocol.ts +436 -0
  70. package/src/PortRouting.ts +1464 -0
  71. package/src/index.ts +13 -0
  72. package/src/internal/do-journal.ts +2282 -0
  73. package/src/internal/message-delivery-schema.ts +24 -0
  74. package/src/internal/migrations.ts +304 -0
  75. package/src/internal/owned-state.ts +228 -0
  76. package/src/internal/recovery-checkpoint-schema.ts +17 -0
  77. package/src/internal/storage-span.ts +44 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Daniel van der Merwe
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,30 @@
1
+ import { Layer, Schema } from "effect";
2
+ import { SqliteClient } from "@effect/sql-sqlite-do";
3
+ import { MemoryMutationFailpoint } from "@yielded/agent/memory-store";
4
+ declare namespace DoMemoryStore_d_exports {
5
+ export { DoMemoryStorageLimits, defaultDoMemoryStorageLimits, doMemoryStoreLayer, doMemoryStoreLayerWithFailpoints };
6
+ }
7
+ declare const DoMemoryStorageLimits_base: Schema.Class<DoMemoryStorageLimits, Schema.Struct<{
8
+ readonly maxRowBytes: Schema.Int;
9
+ readonly maxStorageBytes: Schema.Int;
10
+ readonly maxDocuments: Schema.Int;
11
+ readonly maxReceipts: Schema.Int;
12
+ readonly reservedWithdrawalBytes: Schema.optional<Schema.Natural>;
13
+ readonly reservedWithdrawalReceipts: Schema.optional<Schema.Natural>;
14
+ }>, {}>;
15
+ export declare class DoMemoryStorageLimits extends DoMemoryStorageLimits_base {}
16
+ export declare const defaultDoMemoryStorageLimits: DoMemoryStorageLimits;
17
+ /**
18
+ * Local memory only, without Thread tables. Pass the full storage handle: sql-only
19
+ * handles cannot provide atomic receipts and revisions. Services over the same storage
20
+ * share a transaction gate and bounded, write-through document and receipt views.
21
+ * Byte limits conservatively count encoded rows, not SQLite page/index overhead.
22
+ * Optional withdrawal reserves default to zero and stay within hard byte/receipt limits.
23
+ * Ordinary Put cannot consume them. Deploy exclusively upgraded writers before relying
24
+ * on reserves; the SQL accounting migration preserves older writers and their receipts.
25
+ */
26
+ export declare const doMemoryStoreLayerWithFailpoints: (storage: NonNullable<SqliteClient.SqliteClientConfig["storage"]>, limits?: DoMemoryStorageLimits) => Layer.Layer<import("@yielded/agent/memory-store").MemoryReader | import("@yielded/agent/memory-store").MemoryWriter | import("@yielded/agent/sql-memory-store").SqlMemoryBatchWriter, import("@yielded/agent/sql-memory-store").SqliteMemoryInitializationError, MemoryMutationFailpoint>;
27
+ export declare const doMemoryStoreLayer: (storage: NonNullable<SqliteClient.SqliteClientConfig["storage"]>, limits?: DoMemoryStorageLimits) => Layer.Layer<import("@yielded/agent/memory-store").MemoryReader | import("@yielded/agent/memory-store").MemoryWriter | import("@yielded/agent/sql-memory-store").SqlMemoryBatchWriter, import("@yielded/agent/sql-memory-store").SqliteMemoryInitializationError, never>;
28
+ //#endregion
29
+ export { DoMemoryStore_d_exports as t };
30
+ //# sourceMappingURL=DoMemoryStore.d.mts.map
@@ -0,0 +1,66 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { r as ownedState } from "./owned-state-DHm2aRXx.mjs";
3
+ import { SqlMemoryLimits, SqlStorageOwner, memoryStoreLayerWithFailpoints } from "@yielded/agent/sql-memory-store";
4
+ import { Effect, Layer, Schema } from "effect";
5
+ import * as SqlClientService from "effect/sql/SqlClient";
6
+ import { SqliteClient } from "@effect/sql-sqlite-do";
7
+ import { MemoryMutationFailpoint, MemoryStorageError } from "@yielded/agent/memory-store";
8
+ //#region src/DoMemoryStore.ts
9
+ var DoMemoryStore_exports = /* @__PURE__ */ __exportAll({
10
+ DoMemoryStorageLimits: () => DoMemoryStorageLimits,
11
+ defaultDoMemoryStorageLimits: () => defaultDoMemoryStorageLimits,
12
+ doMemoryStoreLayer: () => doMemoryStoreLayer,
13
+ doMemoryStoreLayerWithFailpoints: () => doMemoryStoreLayerWithFailpoints
14
+ });
15
+ var DoMemoryStorageLimits = class extends Schema.Class("@effect-agent/storage-cloudflare/DoMemoryStorageLimits")({
16
+ maxRowBytes: Schema.Int.check(Schema.isBetween({
17
+ minimum: 1,
18
+ maximum: 19e5
19
+ })),
20
+ maxStorageBytes: Schema.Int.check(Schema.isBetween({
21
+ minimum: 1,
22
+ maximum: 536870912
23
+ })),
24
+ maxDocuments: Schema.Int.check(Schema.isBetween({
25
+ minimum: 1,
26
+ maximum: 1e5
27
+ })),
28
+ maxReceipts: Schema.Int.check(Schema.isBetween({
29
+ minimum: 1,
30
+ maximum: 1e6
31
+ })),
32
+ reservedWithdrawalBytes: Schema.optional(Schema.Natural),
33
+ reservedWithdrawalReceipts: Schema.optional(Schema.Natural)
34
+ }) {};
35
+ const defaultDoMemoryStorageLimits = DoMemoryStorageLimits.make({
36
+ maxRowBytes: 19e5,
37
+ maxStorageBytes: 536870912,
38
+ maxDocuments: 1e4,
39
+ maxReceipts: 1e5
40
+ });
41
+ /**
42
+ * Local memory only, without Thread tables. Pass the full storage handle: sql-only
43
+ * handles cannot provide atomic receipts and revisions. Services over the same storage
44
+ * share a transaction gate and bounded, write-through document and receipt views.
45
+ * Byte limits conservatively count encoded rows, not SQLite page/index overhead.
46
+ * Optional withdrawal reserves default to zero and stay within hard byte/receipt limits.
47
+ * Ordinary Put cannot consume them. Deploy exclusively upgraded writers before relying
48
+ * on reserves; the SQL accounting migration preserves older writers and their receipts.
49
+ */
50
+ const doMemoryStoreLayerWithFailpoints = (storage, limits = defaultDoMemoryStorageLimits) => Layer.unwrap(Effect.gen(function* () {
51
+ const validated = yield* Schema.decodeEffect(DoMemoryStorageLimits)(limits).pipe(Effect.mapError(() => MemoryStorageError.make({
52
+ operation: "memory storage limits",
53
+ reason: "invalid-input"
54
+ })));
55
+ const sql = yield* SqlClientService.SqlClient;
56
+ const owner = yield* ownedState(sql).pipe(Effect.mapError(() => MemoryStorageError.make({
57
+ operation: "open memory owner",
58
+ reason: "unavailable"
59
+ })));
60
+ return memoryStoreLayerWithFailpoints.pipe(Layer.provide(Layer.succeed(SqlMemoryLimits, validated)), Layer.provide(Layer.succeed(SqlStorageOwner, owner)));
61
+ })).pipe(Layer.provide(SqliteClient.layer({ storage })));
62
+ const doMemoryStoreLayer = (storage, limits = defaultDoMemoryStorageLimits) => doMemoryStoreLayerWithFailpoints(storage, limits).pipe(Layer.provide(MemoryMutationFailpoint.layer));
63
+ //#endregion
64
+ export { DoMemoryStorageLimits, defaultDoMemoryStorageLimits, doMemoryStoreLayer, doMemoryStoreLayerWithFailpoints, DoMemoryStore_exports as t };
65
+
66
+ //# sourceMappingURL=DoMemoryStore.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DoMemoryStore.mjs","names":[],"sources":["../src/DoMemoryStore.ts"],"sourcesContent":["import { SqliteClient } from \"@effect/sql-sqlite-do\";\nimport { MemoryStorageError, MemoryMutationFailpoint } from \"@yielded/agent/memory-store\";\nimport {\n memoryStoreLayerWithFailpoints,\n SqlMemoryLimits,\n SqlStorageOwner,\n} from \"@yielded/agent/sql-memory-store\";\nimport { Effect, Layer, Schema } from \"effect\";\nimport * as SqlClientService from \"effect/sql/SqlClient\";\n\nimport { ownedState } from \"./internal/owned-state.ts\";\n\nexport class DoMemoryStorageLimits extends Schema.Class<DoMemoryStorageLimits>(\n \"@effect-agent/storage-cloudflare/DoMemoryStorageLimits\",\n)({\n maxRowBytes: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 1_900_000 })),\n maxStorageBytes: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 536_870_912 })),\n maxDocuments: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 100_000 })),\n maxReceipts: Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 1_000_000 })),\n reservedWithdrawalBytes: Schema.optional(Schema.Natural),\n reservedWithdrawalReceipts: Schema.optional(Schema.Natural),\n}) {}\n\nexport const defaultDoMemoryStorageLimits = DoMemoryStorageLimits.make({\n maxRowBytes: 1_900_000,\n maxStorageBytes: 536_870_912,\n maxDocuments: 10_000,\n maxReceipts: 100_000,\n});\n\n/**\n * Local memory only, without Thread tables. Pass the full storage handle: sql-only\n * handles cannot provide atomic receipts and revisions. Services over the same storage\n * share a transaction gate and bounded, write-through document and receipt views.\n * Byte limits conservatively count encoded rows, not SQLite page/index overhead.\n * Optional withdrawal reserves default to zero and stay within hard byte/receipt limits.\n * Ordinary Put cannot consume them. Deploy exclusively upgraded writers before relying\n * on reserves; the SQL accounting migration preserves older writers and their receipts.\n */\nexport const doMemoryStoreLayerWithFailpoints = (\n storage: NonNullable<SqliteClient.SqliteClientConfig[\"storage\"]>,\n limits: DoMemoryStorageLimits = defaultDoMemoryStorageLimits,\n) =>\n Layer.unwrap(\n Effect.gen(function* () {\n const validated = yield* Schema.decodeEffect(DoMemoryStorageLimits)(limits).pipe(\n Effect.mapError(() =>\n MemoryStorageError.make({ operation: \"memory storage limits\", reason: \"invalid-input\" }),\n ),\n );\n\n const sql = yield* SqlClientService.SqlClient;\n\n const owner = yield* ownedState(sql).pipe(\n Effect.mapError(() =>\n MemoryStorageError.make({ operation: \"open memory owner\", reason: \"unavailable\" }),\n ),\n );\n\n return memoryStoreLayerWithFailpoints.pipe(\n Layer.provide(Layer.succeed(SqlMemoryLimits, validated)),\n Layer.provide(Layer.succeed(SqlStorageOwner, owner)),\n );\n }),\n ).pipe(Layer.provide(SqliteClient.layer({ storage })));\n\nexport const doMemoryStoreLayer = (\n storage: NonNullable<SqliteClient.SqliteClientConfig[\"storage\"]>,\n limits: DoMemoryStorageLimits = defaultDoMemoryStorageLimits,\n) =>\n doMemoryStoreLayerWithFailpoints(storage, limits).pipe(\n Layer.provide(MemoryMutationFailpoint.layer),\n );\n"],"mappings":";;;;;;;;;;;;;;AAYA,IAAa,wBAAb,cAA2C,OAAO,MAChD,wDACF,CAAC,CAAC;CACA,aAAa,OAAO,IAAI,MAAM,OAAO,UAAU;EAAE,SAAS;EAAG,SAAS;CAAU,CAAC,CAAC;CAClF,iBAAiB,OAAO,IAAI,MAAM,OAAO,UAAU;EAAE,SAAS;EAAG,SAAS;CAAY,CAAC,CAAC;CACxF,cAAc,OAAO,IAAI,MAAM,OAAO,UAAU;EAAE,SAAS;EAAG,SAAS;CAAQ,CAAC,CAAC;CACjF,aAAa,OAAO,IAAI,MAAM,OAAO,UAAU;EAAE,SAAS;EAAG,SAAS;CAAU,CAAC,CAAC;CAClF,yBAAyB,OAAO,SAAS,OAAO,OAAO;CACvD,4BAA4B,OAAO,SAAS,OAAO,OAAO;AAC5D,CAAC,CAAC,CAAC,CAAC;AAEJ,MAAa,+BAA+B,sBAAsB,KAAK;CACrE,aAAa;CACb,iBAAiB;CACjB,cAAc;CACd,aAAa;AACf,CAAC;;;;;;;;;;AAWD,MAAa,oCACX,SACA,SAAgC,iCAEhC,MAAM,OACJ,OAAO,IAAI,aAAa;CACtB,MAAM,YAAY,OAAO,OAAO,aAAa,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC,KAC1E,OAAO,eACL,mBAAmB,KAAK;EAAE,WAAW;EAAyB,QAAQ;CAAgB,CAAC,CACzF,CACF;CAEA,MAAM,MAAM,OAAO,iBAAiB;CAEpC,MAAM,QAAQ,OAAO,WAAW,GAAG,CAAC,CAAC,KACnC,OAAO,eACL,mBAAmB,KAAK;EAAE,WAAW;EAAqB,QAAQ;CAAc,CAAC,CACnF,CACF;CAEA,OAAO,+BAA+B,KACpC,MAAM,QAAQ,MAAM,QAAQ,iBAAiB,SAAS,CAAC,GACvD,MAAM,QAAQ,MAAM,QAAQ,iBAAiB,KAAK,CAAC,CACrD;AACF,CAAC,CACH,CAAC,CAAC,KAAK,MAAM,QAAQ,aAAa,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;AAEvD,MAAa,sBACX,SACA,SAAgC,iCAEhC,iCAAiC,SAAS,MAAM,CAAC,CAAC,KAChD,MAAM,QAAQ,wBAAwB,KAAK,CAC7C"}
@@ -0,0 +1,17 @@
1
+ import { a as DoStorageCompatibilityError, o as DoStorageCorruptionError, s as DoStorageError } from "./DoStorageError-CGuAfSwF.mjs";
2
+ import { DoStorageConfig } from "./DoStorageConfig.mjs";
3
+ import { DoStorageFailpoint } from "./DoStorageFailpoint.mjs";
4
+ import { MessageDeliveryStore, MessageDeliveryStoreLimits } from "@yielded/agent/message-delivery";
5
+ import { Layer } from "effect";
6
+ import * as SqlClient$1 from "effect/sql/SqlClient";
7
+ declare namespace DoMessageDeliveryStore_d_exports {
8
+ export { doMessageDeliveryStoreLayer };
9
+ }
10
+ /**
11
+ * Message obligations in a Thread Object's own SQL storage. The platform must prearm
12
+ * maintenance before insertion and retain its alarm while nextDeadline is present.
13
+ */
14
+ export declare const doMessageDeliveryStoreLayer: (limits?: MessageDeliveryStoreLimits) => Layer.Layer<MessageDeliveryStore, DoStorageCompatibilityError | DoStorageCorruptionError | DoStorageError | import("@yielded/agent/message-delivery").MessageDeliveryError, DoStorageConfig | DoStorageFailpoint | SqlClient$1.SqlClient>;
15
+ //#endregion
16
+ export { DoMessageDeliveryStore_d_exports as t };
17
+ //# sourceMappingURL=DoMessageDeliveryStore.d.mts.map
@@ -0,0 +1,30 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { DoStorageConfig } from "./DoStorageConfig.mjs";
3
+ import { DoStorageFailpoint } from "./DoStorageFailpoint.mjs";
4
+ import { a as initializeDoJournal } from "./do-journal-DUhjZkOf.mjs";
5
+ import { SqlLifecycleSource } from "@yielded/agent-storage-sql/sql-lifecycle-publication";
6
+ import { makeSqlMessageDeliveryStore } from "@yielded/agent-storage-sql/sql-message-delivery-store";
7
+ import { MessageDeliveryStore } from "@yielded/agent/message-delivery";
8
+ import { SqlStorageOwner } from "@yielded/agent/sql-memory-store";
9
+ import { Effect, Layer } from "effect";
10
+ import * as SqlClient$1 from "effect/sql/SqlClient";
11
+ //#region src/DoMessageDeliveryStore.ts
12
+ var DoMessageDeliveryStore_exports = /* @__PURE__ */ __exportAll({ doMessageDeliveryStoreLayer: () => doMessageDeliveryStoreLayer });
13
+ /**
14
+ * Message obligations in a Thread Object's own SQL storage. The platform must prearm
15
+ * maintenance before insertion and retain its alarm while nextDeadline is present.
16
+ */
17
+ const doMessageDeliveryStoreLayer = (limits) => Layer.effect(MessageDeliveryStore, Effect.gen(function* () {
18
+ const sql = yield* SqlClient$1.SqlClient;
19
+ const config = yield* DoStorageConfig;
20
+ const failpoint = yield* DoStorageFailpoint;
21
+ const journal = yield* initializeDoJournal(sql, failpoint.hit, config.maxStoredValueBytes);
22
+ return yield* makeSqlMessageDeliveryStore(limits, { maxStoredValueBytes: config.maxStoredValueBytes }).pipe(Effect.provideService(SqlStorageOwner, journal.owner), Effect.provideService(SqlLifecycleSource, {
23
+ beforeRetain: (threadId) => journal.flushCanonical(threadId),
24
+ beforePending: journal.flushPublications()
25
+ }));
26
+ }));
27
+ //#endregion
28
+ export { doMessageDeliveryStoreLayer, DoMessageDeliveryStore_exports as t };
29
+
30
+ //# sourceMappingURL=DoMessageDeliveryStore.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DoMessageDeliveryStore.mjs","names":["SqlClient"],"sources":["../src/DoMessageDeliveryStore.ts"],"sourcesContent":["import { SqlLifecycleSource } from \"@yielded/agent-storage-sql/sql-lifecycle-publication\";\nimport { makeSqlMessageDeliveryStore } from \"@yielded/agent-storage-sql/sql-message-delivery-store\";\nimport {\n MessageDeliveryStore,\n type MessageDeliveryStoreLimits,\n} from \"@yielded/agent/message-delivery\";\nimport { SqlStorageOwner } from \"@yielded/agent/sql-memory-store\";\nimport { Effect, Layer } from \"effect\";\nimport * as SqlClient from \"effect/sql/SqlClient\";\n\nimport { DoStorageConfig } from \"./DoStorageConfig.ts\";\nimport { DoStorageFailpoint } from \"./DoStorageFailpoint.ts\";\nimport { initializeDoJournal } from \"./internal/do-journal.ts\";\n\n/**\n * Message obligations in a Thread Object's own SQL storage. The platform must prearm\n * maintenance before insertion and retain its alarm while nextDeadline is present.\n */\nexport const doMessageDeliveryStoreLayer = (limits?: MessageDeliveryStoreLimits) =>\n Layer.effect(\n MessageDeliveryStore,\n Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n const config = yield* DoStorageConfig;\n const failpoint = yield* DoStorageFailpoint;\n\n const journal = yield* initializeDoJournal(sql, failpoint.hit, config.maxStoredValueBytes);\n\n return yield* makeSqlMessageDeliveryStore(limits, {\n maxStoredValueBytes: config.maxStoredValueBytes,\n }).pipe(\n Effect.provideService(SqlStorageOwner, journal.owner),\n Effect.provideService(SqlLifecycleSource, {\n beforeRetain: (threadId) => journal.flushCanonical(threadId),\n beforePending: journal.flushPublications(),\n }),\n );\n }),\n );\n"],"mappings":";;;;;;;;;;;;;;;;AAkBA,MAAa,+BAA+B,WAC1C,MAAM,OACJ,sBACA,OAAO,IAAI,aAAa;CACtB,MAAM,MAAM,OAAOA,YAAU;CAC7B,MAAM,SAAS,OAAO;CACtB,MAAM,YAAY,OAAO;CAEzB,MAAM,UAAU,OAAO,oBAAoB,KAAK,UAAU,KAAK,OAAO,mBAAmB;CAEzF,OAAO,OAAO,4BAA4B,QAAQ,EAChD,qBAAqB,OAAO,oBAC9B,CAAC,CAAC,CAAC,KACD,OAAO,eAAe,iBAAiB,QAAQ,KAAK,GACpD,OAAO,eAAe,oBAAoB;EACxC,eAAe,aAAa,QAAQ,eAAe,QAAQ;EAC3D,eAAe,QAAQ,kBAAkB;CAC3C,CAAC,CACH;AACF,CAAC,CACH"}
@@ -0,0 +1,36 @@
1
+ import { Context, Effect, Layer } from "effect";
2
+ import * as SqlClientService from "effect/sql/SqlClient";
3
+ import { ScheduleFailpointError, ScheduleStorageError, ScheduleStore } from "@yielded/agent/schedule";
4
+ declare namespace DoScheduleStore_d_exports {
5
+ export { DoScheduleAlarmControl, DoScheduleAlarmReplacement, DoScheduleReplaceAlarm, DoScheduleTransaction, scheduleStoreLayer };
6
+ }
7
+ export interface DoScheduleAlarmReplacement {
8
+ readonly deadlineAtMillis: number | null;
9
+ /** Included in every logical alarm payload so equal-time replacement stays distinguishable. */
10
+ readonly generation: number;
11
+ }
12
+ export type DoScheduleReplaceAlarm = (replacement: DoScheduleAlarmReplacement) => Effect.Effect<void, ScheduleStorageError>;
13
+ declare const DoScheduleTransaction_base: Context.ServiceClass<DoScheduleTransaction, "@effect-agent/storage-cloudflare/DoScheduleTransaction", {
14
+ readonly run: <A, E, R>(body: (replaceAlarm: DoScheduleReplaceAlarm) => Effect.Effect<A, E, R>) => Effect.Effect<A, E | ScheduleStorageError, R>;
15
+ }>;
16
+ /**
17
+ * Platform-owned transaction boundary for schedule SQL and logical alarm mutation. The callback
18
+ * and its `replaceAlarm` capability belong to one fiber and must not escape or fork.
19
+ */
20
+ export declare class DoScheduleTransaction extends DoScheduleTransaction_base {}
21
+ declare const DoScheduleAlarmControl_base: Context.ServiceClass<DoScheduleAlarmControl, "@effect-agent/storage-cloudflare/DoScheduleAlarmControl", {
22
+ /** Establish a future recovery wake before cross-Object admission starts. */
23
+ readonly prearm: (deadlineAtMillis: number) => Effect.Effect<void, ScheduleStorageError | ScheduleFailpointError>;
24
+ /** Replace or cancel the wake from the object's authoritative indexed deadline. */
25
+ readonly reconcile: Effect.Effect<void, ScheduleStorageError | ScheduleFailpointError>;
26
+ }>;
27
+ /** Platform driver operations that use the same schedule-state and alarm transaction. */
28
+ export declare class DoScheduleAlarmControl extends DoScheduleAlarmControl_base {}
29
+ /**
30
+ * Durable Object SQLite ScheduleStore. Platform code supplies the one transaction owner that
31
+ * combines these SQL mutations with its logical and native alarm lifecycle.
32
+ */
33
+ export declare const scheduleStoreLayer: Layer.Layer<ScheduleStore | DoScheduleAlarmControl, ScheduleStorageError, SqlClientService.SqlClient | DoScheduleTransaction>;
34
+ //#endregion
35
+ export { DoScheduleStore_d_exports as t };
36
+ //# sourceMappingURL=DoScheduleStore.d.mts.map
@@ -0,0 +1,388 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { Context, Effect, Layer, Result, Schema } from "effect";
3
+ import * as SqlClientService from "effect/sql/SqlClient";
4
+ import { upgradeV2Schedules } from "@yielded/agent-storage-sql/sql-storage-v2-upgrade";
5
+ import { ScheduleCapacityError, ScheduleChange, ScheduleConflict, ScheduleDueCursor, ScheduleFailpoint, ScheduleId, ScheduleInstant, ScheduleKey, ScheduleNotFound, ScheduleOwner, SchedulePageRequest, ScheduleRecord, ScheduleStorageError, ScheduleStore, defaultSchedulingLimits } from "@yielded/agent/schedule";
6
+ import { applyScheduleChange, scheduleDeadline, scheduleUsesCapacity } from "@yielded/agent/schedule-transition";
7
+ //#region src/DoScheduleStore.ts
8
+ var DoScheduleStore_exports = /* @__PURE__ */ __exportAll({
9
+ DoScheduleAlarmControl: () => DoScheduleAlarmControl,
10
+ DoScheduleTransaction: () => DoScheduleTransaction,
11
+ scheduleStoreLayer: () => scheduleStoreLayer
12
+ });
13
+ const CURRENT_SCHEDULE_STORE_VERSION = 3;
14
+ const MAX_STORED_SCHEDULE_BYTES = 19e5;
15
+ const StoredScheduleJson = Schema.String.check(Schema.isMaxLength(MAX_STORED_SCHEDULE_BYTES));
16
+ const StoredDeadline = Schema.NullOr(ScheduleInstant);
17
+ var ScheduleRow = class extends Schema.Class("@effect-agent/storage-cloudflare/ScheduleRow")({
18
+ tenant_id: ScheduleOwner.fields.tenantId,
19
+ owner_id: ScheduleOwner.fields.ownerId,
20
+ schedule_id: ScheduleId,
21
+ deadline_at_millis: StoredDeadline,
22
+ record_json: StoredScheduleJson
23
+ }) {};
24
+ const ScheduleDueRow = Schema.Struct({
25
+ tenant_id: ScheduleOwner.fields.tenantId,
26
+ owner_id: ScheduleOwner.fields.ownerId,
27
+ schedule_id: ScheduleId,
28
+ deadline_at_millis: ScheduleInstant
29
+ });
30
+ var ScheduleCountRow = class extends Schema.Class("@effect-agent/storage-cloudflare/ScheduleCountRow")({ schedule_count: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)) }) {};
31
+ var ScheduleDeadlineRow = class extends Schema.Class("@effect-agent/storage-cloudflare/ScheduleDeadlineRow")({ deadline_at_millis: StoredDeadline }) {};
32
+ var ScheduleStoreStateRow = class extends Schema.Class("@effect-agent/storage-cloudflare/ScheduleStoreStateRow")({
33
+ storage_version: Schema.Int.check(Schema.isGreaterThan(0)),
34
+ alarm_generation: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))
35
+ }) {};
36
+ var ScheduleTableNameRow = class extends Schema.Class("@effect-agent/storage-cloudflare/ScheduleTableNameRow")({ name: Schema.String }) {};
37
+ /**
38
+ * Platform-owned transaction boundary for schedule SQL and logical alarm mutation. The callback
39
+ * and its `replaceAlarm` capability belong to one fiber and must not escape or fork.
40
+ */
41
+ var DoScheduleTransaction = class extends Context.Service()("@effect-agent/storage-cloudflare/DoScheduleTransaction") {};
42
+ /** Platform driver operations that use the same schedule-state and alarm transaction. */
43
+ var DoScheduleAlarmControl = class extends Context.Service()("@effect-agent/storage-cloudflare/DoScheduleAlarmControl") {};
44
+ const unavailable = (operation) => ScheduleStorageError.make({
45
+ operation,
46
+ reason: "unavailable"
47
+ });
48
+ const corrupt = (operation) => ScheduleStorageError.make({
49
+ operation,
50
+ reason: "corrupt"
51
+ });
52
+ const decodeRows = Effect.fn("DoScheduleStore.decodeRows")(function* (schema, rows, operation) {
53
+ return yield* Schema.decodeUnknownEffect(schema)(rows).pipe(Effect.mapError(() => corrupt(operation)));
54
+ });
55
+ const decodeBoundary = (schema, value, operation) => Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError(() => corrupt(operation)));
56
+ const decodeRecord = Effect.fn("DoScheduleStore.decodeRecord")(function* (row) {
57
+ const record = yield* Schema.decodeEffect(Schema.fromJsonString(ScheduleRecord))(row.record_json).pipe(Effect.mapError(() => corrupt("decode schedule")));
58
+ if (record.owner.tenantId !== row.tenant_id || record.owner.ownerId !== row.owner_id || record.scheduleId !== row.schedule_id || scheduleDeadline(record) !== row.deadline_at_millis) return yield* corrupt("decode schedule index");
59
+ return record;
60
+ });
61
+ const encodeRecord = Effect.fn("DoScheduleStore.encodeRecord")(function* (record) {
62
+ const encoded = yield* Schema.encodeEffect(Schema.fromJsonString(ScheduleRecord))(record).pipe(Effect.mapError(() => corrupt("encode schedule")));
63
+ return yield* Schema.decodeEffect(StoredScheduleJson)(encoded).pipe(Effect.mapError(() => corrupt("encode schedule bounds")));
64
+ });
65
+ const initializeScheduleStore = Effect.fn("DoScheduleStore.initialize")(function* () {
66
+ const sql = yield* SqlClientService.SqlClient;
67
+ const operation = "initialize schedule store";
68
+ const rawTables = yield* sql`
69
+ SELECT name
70
+ FROM sqlite_master
71
+ WHERE type = 'table'
72
+ AND name IN (
73
+ 'effect_agent_schedule_store_state',
74
+ 'effect_agent_schedules'
75
+ )
76
+ ORDER BY name
77
+ `.pipe(Effect.mapError(() => unavailable(operation)));
78
+ const tables = yield* decodeRows(Schema.Array(ScheduleTableNameRow), rawTables, operation);
79
+ const hasState = tables.some((row) => row.name === "effect_agent_schedule_store_state");
80
+ const hasSchedules = tables.some((row) => row.name === "effect_agent_schedules");
81
+ if (!hasState) {
82
+ if (hasSchedules) return yield* corrupt(operation);
83
+ yield* sql.withTransaction(Effect.gen(function* () {
84
+ yield* sql`
85
+ CREATE TABLE effect_agent_schedule_store_state (
86
+ singleton INTEGER PRIMARY KEY NOT NULL CHECK (singleton = 1),
87
+ storage_version INTEGER NOT NULL,
88
+ alarm_generation INTEGER NOT NULL
89
+ )
90
+ `.withoutTransform;
91
+ yield* sql`
92
+ CREATE TABLE effect_agent_schedules (
93
+ tenant_id TEXT NOT NULL,
94
+ owner_id TEXT NOT NULL,
95
+ schedule_id TEXT NOT NULL,
96
+ deadline_at_millis INTEGER,
97
+ record_json TEXT NOT NULL,
98
+ PRIMARY KEY (tenant_id, owner_id, schedule_id)
99
+ )
100
+ `.withoutTransform;
101
+ yield* sql`
102
+ CREATE INDEX effect_agent_schedules_deadline
103
+ ON effect_agent_schedules (deadline_at_millis, tenant_id, owner_id, schedule_id)
104
+ WHERE deadline_at_millis IS NOT NULL
105
+ `.withoutTransform;
106
+ yield* sql`
107
+ CREATE INDEX effect_agent_schedules_owner_deadline
108
+ ON effect_agent_schedules (tenant_id, owner_id, deadline_at_millis, schedule_id)
109
+ WHERE deadline_at_millis IS NOT NULL
110
+ `.withoutTransform;
111
+ yield* sql`
112
+ INSERT INTO effect_agent_schedule_store_state (
113
+ singleton, storage_version, alarm_generation
114
+ ) VALUES (1, ${CURRENT_SCHEDULE_STORE_VERSION}, 0)
115
+ `.withoutTransform;
116
+ })).pipe(Effect.mapError(() => unavailable(operation)));
117
+ return;
118
+ }
119
+ if (!hasSchedules) return yield* corrupt(operation);
120
+ const rawState = yield* sql`
121
+ SELECT storage_version, alarm_generation
122
+ FROM effect_agent_schedule_store_state
123
+ WHERE singleton = 1
124
+ `.pipe(Effect.mapError(() => unavailable(operation)));
125
+ const state = yield* decodeRows(Schema.Array(ScheduleStoreStateRow), rawState, operation);
126
+ if (state.length === 1 && state[0].storage_version === 2) {
127
+ const failpoint = yield* ScheduleFailpoint;
128
+ yield* sql.withTransaction(Effect.gen(function* () {
129
+ const current = yield* sql`SELECT storage_version FROM effect_agent_schedule_store_state WHERE singleton=1`;
130
+ if (current.length === 1 && current[0].storage_version === 3) return;
131
+ if (current.length !== 1 || current[0].storage_version !== 2) return yield* corrupt("schedule version changed during upgrade");
132
+ yield* upgradeV2Schedules(MAX_STORED_SCHEDULE_BYTES);
133
+ yield* failpoint.hit("upgrade:before-version");
134
+ yield* sql`UPDATE effect_agent_schedule_store_state SET storage_version=3 WHERE singleton=1`;
135
+ yield* failpoint.hit("upgrade:after-version");
136
+ })).pipe(Effect.catchTag("StorageUpgradeError", (error) => ScheduleStorageError.make({
137
+ reason: "corrupt",
138
+ operation: "upgrade v2 schedules",
139
+ cause: error
140
+ })), Effect.catchTag("SqlError", () => unavailable("upgrade v2 schedules")), Effect.catchTag("ScheduleFailpointError", () => unavailable("upgrade v2 schedules failpoint")));
141
+ return;
142
+ }
143
+ if (state.length !== 1 || state[0].storage_version !== CURRENT_SCHEDULE_STORE_VERSION) return yield* corrupt(state.length === 1 ? `${operation}: incompatible storage version ${state[0].storage_version}; expected ${CURRENT_SCHEDULE_STORE_VERSION}` : `${operation}: invalid storage version row`);
144
+ });
145
+ const makeServices = Effect.gen(function* () {
146
+ const sql = yield* SqlClientService.SqlClient;
147
+ const transactions = yield* DoScheduleTransaction;
148
+ const scheduleFailpoint = yield* ScheduleFailpoint;
149
+ yield* initializeScheduleStore();
150
+ const readRows = Effect.fn("DoScheduleStore.readRows")(function* (key, operation) {
151
+ const rows = yield* sql`
152
+ SELECT tenant_id, owner_id, schedule_id, deadline_at_millis, record_json
153
+ FROM effect_agent_schedules
154
+ WHERE tenant_id = ${key.owner.tenantId}
155
+ AND owner_id = ${key.owner.ownerId}
156
+ AND schedule_id = ${key.scheduleId}
157
+ `.pipe(Effect.mapError(() => unavailable(operation)));
158
+ return yield* decodeRows(Schema.Array(ScheduleRow), rows, operation);
159
+ });
160
+ const readOne = Effect.fn("DoScheduleStore.readOne")(function* (key, operation) {
161
+ const rows = yield* readRows(key, operation);
162
+ if (rows.length === 0) return null;
163
+ if (rows.length !== 1) return yield* corrupt(operation);
164
+ return yield* decodeRecord(rows[0]);
165
+ });
166
+ const readNextDeadline = Effect.fn("DoScheduleStore.readNextDeadline")(function* (owner, operation) {
167
+ const rows = owner === void 0 ? yield* sql`
168
+ SELECT MIN(deadline_at_millis) AS deadline_at_millis
169
+ FROM effect_agent_schedules
170
+ WHERE deadline_at_millis IS NOT NULL
171
+ `.pipe(Effect.mapError(() => unavailable(operation))) : yield* sql`
172
+ SELECT MIN(deadline_at_millis) AS deadline_at_millis
173
+ FROM effect_agent_schedules
174
+ WHERE tenant_id = ${owner.tenantId}
175
+ AND owner_id = ${owner.ownerId}
176
+ AND deadline_at_millis IS NOT NULL
177
+ `.pipe(Effect.mapError(() => unavailable(operation)));
178
+ const decoded = yield* decodeRows(Schema.Array(ScheduleDeadlineRow), rows, operation);
179
+ if (decoded.length !== 1) return yield* corrupt(operation);
180
+ return decoded[0].deadline_at_millis;
181
+ });
182
+ const replaceAlarm = Effect.fn("DoScheduleStore.replaceAlarm")(function* (replace, deadlineAtMillis, operation) {
183
+ const rawState = yield* sql`
184
+ UPDATE effect_agent_schedule_store_state
185
+ SET alarm_generation = alarm_generation + 1
186
+ WHERE singleton = 1
187
+ RETURNING storage_version, alarm_generation
188
+ `.pipe(Effect.mapError(() => unavailable(operation)));
189
+ const state = yield* decodeRows(Schema.Array(ScheduleStoreStateRow), rawState, operation);
190
+ if (state.length !== 1 || state[0].storage_version !== CURRENT_SCHEDULE_STORE_VERSION) return yield* corrupt(operation);
191
+ yield* scheduleFailpoint.hit("schedule:alarm:before");
192
+ yield* replace({
193
+ deadlineAtMillis,
194
+ generation: state[0].alarm_generation
195
+ });
196
+ yield* scheduleFailpoint.hit("schedule:alarm:after");
197
+ });
198
+ const insert = Effect.fn("DoScheduleStore.insert")(function* (record, ownerLimit) {
199
+ const operation = "insert schedule";
200
+ const canonical = yield* decodeBoundary(ScheduleRecord, record, operation);
201
+ const recordJson = yield* encodeRecord(canonical);
202
+ const result = yield* transactions.run((replace) => Effect.gen(function* () {
203
+ const existing = yield* readOne(canonical, operation);
204
+ if (existing !== null) {
205
+ if (existing.creationFingerprint === canonical.creationFingerprint) return {
206
+ record: existing,
207
+ inserted: false
208
+ };
209
+ return yield* ScheduleConflict.make({
210
+ reason: "creation",
211
+ key: canonical
212
+ });
213
+ }
214
+ const rawCounts = yield* sql`
215
+ SELECT COUNT(*) AS schedule_count
216
+ FROM effect_agent_schedules
217
+ WHERE tenant_id = ${canonical.owner.tenantId}
218
+ AND owner_id = ${canonical.owner.ownerId}
219
+ AND (json_extract(record_json, '$.pending') IS NOT NULL OR
220
+ (json_extract(record_json, '$.state') != 'cancelled' AND json_extract(record_json, '$.nextAtMillis') IS NOT NULL))
221
+ `.pipe(Effect.mapError(() => unavailable(operation)));
222
+ const counts = yield* decodeRows(Schema.Array(ScheduleCountRow), rawCounts, operation);
223
+ if (counts.length !== 1) return yield* corrupt(operation);
224
+ if (counts[0].schedule_count >= ownerLimit) return yield* ScheduleCapacityError.make({ limit: ownerLimit });
225
+ yield* scheduleFailpoint.hit("schedule:insert:before");
226
+ yield* sql`
227
+ INSERT INTO effect_agent_schedules (
228
+ tenant_id, owner_id, schedule_id, deadline_at_millis, record_json
229
+ ) VALUES (
230
+ ${canonical.owner.tenantId},
231
+ ${canonical.owner.ownerId},
232
+ ${canonical.scheduleId},
233
+ ${scheduleDeadline(canonical)},
234
+ ${recordJson}
235
+ )
236
+ `.pipe(Effect.mapError(() => unavailable(operation)));
237
+ const deadline = yield* readNextDeadline(void 0, operation);
238
+ yield* replaceAlarm(replace, deadline, operation);
239
+ return {
240
+ record: canonical,
241
+ inserted: true
242
+ };
243
+ }));
244
+ if (result.inserted) yield* scheduleFailpoint.hit("schedule:insert:after");
245
+ return result.record;
246
+ });
247
+ const get = Effect.fn("DoScheduleStore.get")(function* (key) {
248
+ const canonical = yield* decodeBoundary(ScheduleKey, key, "get schedule");
249
+ return yield* readOne(canonical, "get schedule");
250
+ });
251
+ const list = Effect.fn("DoScheduleStore.list")(function* (requestValue) {
252
+ const operation = "list schedules";
253
+ const request = yield* decodeBoundary(SchedulePageRequest, requestValue, operation);
254
+ const rows = request.after === void 0 ? yield* sql`
255
+ SELECT tenant_id, owner_id, schedule_id, deadline_at_millis, record_json
256
+ FROM effect_agent_schedules
257
+ WHERE tenant_id = ${request.owner.tenantId}
258
+ AND owner_id = ${request.owner.ownerId}
259
+ ORDER BY schedule_id
260
+ LIMIT ${request.limit + 1}
261
+ `.pipe(Effect.mapError(() => unavailable(operation))) : yield* sql`
262
+ SELECT tenant_id, owner_id, schedule_id, deadline_at_millis, record_json
263
+ FROM effect_agent_schedules
264
+ WHERE tenant_id = ${request.owner.tenantId}
265
+ AND owner_id = ${request.owner.ownerId}
266
+ AND schedule_id > ${request.after}
267
+ ORDER BY schedule_id
268
+ LIMIT ${request.limit + 1}
269
+ `.pipe(Effect.mapError(() => unavailable(operation)));
270
+ const decoded = yield* decodeRows(Schema.Array(ScheduleRow), rows, operation);
271
+ const records = yield* Effect.forEach(decoded, decodeRecord);
272
+ const hasNext = records.length > request.limit;
273
+ const items = hasNext ? records.slice(0, request.limit) : records;
274
+ return {
275
+ items,
276
+ next: hasNext ? items.at(-1)?.scheduleId ?? null : null
277
+ };
278
+ });
279
+ const change = Effect.fn("DoScheduleStore.change")(function* (key, change, ownerLimit = defaultSchedulingLimits.maxSchedulesPerOwner) {
280
+ const operation = "change schedule";
281
+ const canonicalKey = yield* decodeBoundary(ScheduleKey, key, operation);
282
+ const canonicalChange = yield* decodeBoundary(ScheduleChange, change, operation);
283
+ const result = yield* transactions.run((replace) => Effect.gen(function* () {
284
+ const current = yield* readOne(canonicalKey, operation);
285
+ if (current === null) return yield* ScheduleNotFound.make({ key: canonicalKey });
286
+ const transition = applyScheduleChange(current, canonicalChange);
287
+ if (Result.isFailure(transition)) return yield* transition.failure;
288
+ const next = transition.success;
289
+ if (!scheduleUsesCapacity(current) && scheduleUsesCapacity(next)) {
290
+ const rawCounts = yield* sql`
291
+ SELECT COUNT(*) AS schedule_count FROM effect_agent_schedules
292
+ WHERE tenant_id = ${key.owner.tenantId} AND owner_id = ${key.owner.ownerId}
293
+ AND (json_extract(record_json, '$.pending') IS NOT NULL OR
294
+ (json_extract(record_json, '$.state') != 'cancelled' AND json_extract(record_json, '$.nextAtMillis') IS NOT NULL))
295
+ `.pipe(Effect.mapError(() => unavailable(operation)));
296
+ const counts = yield* decodeRows(Schema.Array(ScheduleCountRow), rawCounts, operation);
297
+ if (counts.length !== 1) return yield* corrupt(operation);
298
+ if (counts[0].schedule_count >= ownerLimit) return yield* ScheduleCapacityError.make({ limit: ownerLimit });
299
+ }
300
+ if (next === current) return {
301
+ record: current,
302
+ changed: false
303
+ };
304
+ const recordJson = yield* encodeRecord(next);
305
+ yield* scheduleFailpoint.hit(`schedule:${canonicalChange._tag.toLowerCase()}:before`);
306
+ yield* sql`
307
+ UPDATE effect_agent_schedules
308
+ SET deadline_at_millis = ${scheduleDeadline(next)}, record_json = ${recordJson}
309
+ WHERE tenant_id = ${canonicalKey.owner.tenantId}
310
+ AND owner_id = ${canonicalKey.owner.ownerId}
311
+ AND schedule_id = ${canonicalKey.scheduleId}
312
+ `.pipe(Effect.mapError(() => unavailable(operation)));
313
+ const deadline = yield* readNextDeadline(void 0, operation);
314
+ yield* replaceAlarm(replace, deadline, operation);
315
+ return {
316
+ record: next,
317
+ changed: true
318
+ };
319
+ }));
320
+ if (result.changed) yield* scheduleFailpoint.hit(`schedule:${canonicalChange._tag.toLowerCase()}:after`);
321
+ return result.record;
322
+ });
323
+ const due = Effect.fn("DoScheduleStore.due")(function* (nowMillis, limit, owner, after) {
324
+ const operation = "query due schedules";
325
+ const cursor = after === void 0 ? void 0 : yield* Schema.decodeEffect(ScheduleDueCursor)(after).pipe(Effect.mapError(() => corrupt(operation)));
326
+ const continuation = cursor === void 0 ? sql`1 = 1` : sql`
327
+ (deadline_at_millis, tenant_id, owner_id, schedule_id) >
328
+ (${cursor.deadlineAtMillis}, ${cursor.owner.tenantId}, ${cursor.owner.ownerId}, ${cursor.scheduleId})`;
329
+ const rows = owner === void 0 ? yield* sql`
330
+ SELECT tenant_id, owner_id, schedule_id, deadline_at_millis
331
+ FROM effect_agent_schedules
332
+ WHERE deadline_at_millis <= ${nowMillis} AND ${continuation}
333
+ ORDER BY deadline_at_millis, tenant_id, owner_id, schedule_id
334
+ LIMIT ${limit}
335
+ `.pipe(Effect.mapError(() => unavailable(operation))) : yield* sql`
336
+ SELECT tenant_id, owner_id, schedule_id, deadline_at_millis
337
+ FROM effect_agent_schedules
338
+ WHERE tenant_id = ${owner.tenantId}
339
+ AND owner_id = ${owner.ownerId}
340
+ AND deadline_at_millis <= ${nowMillis} AND ${continuation}
341
+ ORDER BY deadline_at_millis, schedule_id
342
+ LIMIT ${limit}
343
+ `.pipe(Effect.mapError(() => unavailable(operation)));
344
+ return (yield* decodeRows(Schema.Array(ScheduleDueRow), rows, operation)).map((row) => ({
345
+ owner: {
346
+ tenantId: row.tenant_id,
347
+ ownerId: row.owner_id
348
+ },
349
+ scheduleId: row.schedule_id,
350
+ deadlineAtMillis: row.deadline_at_millis
351
+ }));
352
+ });
353
+ const nextDeadline = Effect.fn("DoScheduleStore.nextDeadline")(function* (owner) {
354
+ return yield* readNextDeadline(owner, "query next schedule deadline");
355
+ });
356
+ const prearm = Effect.fn("DoScheduleStore.prearm")(function* (deadlineAtMillis) {
357
+ yield* decodeBoundary(ScheduleInstant, deadlineAtMillis, "pre-arm schedule recovery");
358
+ yield* transactions.run((replace) => replaceAlarm(replace, deadlineAtMillis, "pre-arm schedule recovery"));
359
+ yield* scheduleFailpoint.hit("schedule:prearm:after");
360
+ });
361
+ const reconcile = Effect.gen(function* () {
362
+ yield* transactions.run((replace) => Effect.gen(function* () {
363
+ const deadline = yield* readNextDeadline(void 0, "reconcile schedule alarm");
364
+ yield* replaceAlarm(replace, deadline, "reconcile schedule alarm");
365
+ }));
366
+ yield* scheduleFailpoint.hit("schedule:reconcile:after");
367
+ });
368
+ return Context.make(ScheduleStore, {
369
+ insert,
370
+ get,
371
+ list,
372
+ change,
373
+ due,
374
+ nextDeadline
375
+ }).pipe(Context.add(DoScheduleAlarmControl, {
376
+ prearm,
377
+ reconcile
378
+ }));
379
+ });
380
+ /**
381
+ * Durable Object SQLite ScheduleStore. Platform code supplies the one transaction owner that
382
+ * combines these SQL mutations with its logical and native alarm lifecycle.
383
+ */
384
+ const scheduleStoreLayer = Layer.effectContext(makeServices);
385
+ //#endregion
386
+ export { DoScheduleAlarmControl, DoScheduleTransaction, scheduleStoreLayer, DoScheduleStore_exports as t };
387
+
388
+ //# sourceMappingURL=DoScheduleStore.mjs.map