@yielded/agent-storage-cloudflare 0.1.0-beta.167 → 0.1.0-beta.168

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 (71) hide show
  1. package/dist/DoMemoryStore.mjs +1 -1
  2. package/dist/DoMessageDeliveryStore.d.mts +1 -1
  3. package/dist/DoMessageDeliveryStore.mjs +3 -2
  4. package/dist/DoMessageDeliveryStore.mjs.map +1 -1
  5. package/dist/DoScheduleStore.mjs +16 -37
  6. package/dist/DoScheduleStore.mjs.map +1 -1
  7. package/dist/DoStorageConfig.d.mts +2 -10
  8. package/dist/DoStorageConfig.mjs +2 -11
  9. package/dist/DoStorageConfig.mjs.map +1 -1
  10. package/dist/{DoStorageError-CGuAfSwF.d.mts → DoStorageError-CxjMnzUA.d.mts} +7 -18
  11. package/dist/DoStorageError.d.mts +2 -2
  12. package/dist/DoStorageError.mjs +14 -30
  13. package/dist/DoStorageError.mjs.map +1 -1
  14. package/dist/DoStorageFailpoint.d.mts +1 -1
  15. package/dist/DoStorageFailpointTesting.d.mts +1 -1
  16. package/dist/{DoStorageVersion-iNSyi3cJ.d.mts → DoStorageVersion-DTFoLglB.d.mts} +3 -4
  17. package/dist/DoStorageVersion.d.mts +1 -1
  18. package/dist/DoStorageVersion.mjs +2 -2
  19. package/dist/DoStorageVersion.mjs.map +1 -1
  20. package/dist/DoSubmissionLedger.d.mts +7 -5
  21. package/dist/DoSubmissionLedger.mjs +378 -528
  22. package/dist/DoSubmissionLedger.mjs.map +1 -1
  23. package/dist/DoSubscriptionStore.mjs +5 -24
  24. package/dist/DoSubscriptionStore.mjs.map +1 -1
  25. package/dist/{DoThreadStore-D_83OjCH.d.mts → DoThreadStore-Bd4fTns3.d.mts} +17 -18
  26. package/dist/DoThreadStore-CwBvTLoq.mjs +468 -0
  27. package/dist/DoThreadStore-CwBvTLoq.mjs.map +1 -0
  28. package/dist/DoThreadStore.d.mts +2 -2
  29. package/dist/DoThreadStore.mjs +4 -529
  30. package/dist/MemoryProtocol.mjs +3 -3
  31. package/dist/MemoryProtocol.mjs.map +1 -1
  32. package/dist/PortProtocol.d.mts +11727 -3410
  33. package/dist/PortProtocol.mjs +103 -15
  34. package/dist/PortProtocol.mjs.map +1 -1
  35. package/dist/PortRouting.d.mts +14 -9
  36. package/dist/PortRouting.mjs +155 -29
  37. package/dist/PortRouting.mjs.map +1 -1
  38. package/dist/do-journal-Cawnsetj.mjs +761 -0
  39. package/dist/do-journal-Cawnsetj.mjs.map +1 -0
  40. package/dist/index.d.mts +3 -3
  41. package/dist/index.mjs +1 -1
  42. package/dist/migrations-D_EP3KYf.mjs +148 -0
  43. package/dist/migrations-D_EP3KYf.mjs.map +1 -0
  44. package/dist/{owned-state-DHm2aRXx.mjs → owned-state-BHzUi0eb.mjs} +50 -19
  45. package/dist/owned-state-BHzUi0eb.mjs.map +1 -0
  46. package/package.json +1 -1
  47. package/src/DoMessageDeliveryStore.ts +2 -1
  48. package/src/DoScheduleStore.ts +19 -59
  49. package/src/DoStorageConfig.ts +2 -11
  50. package/src/DoStorageError.ts +3 -26
  51. package/src/DoStorageVersion.ts +1 -1
  52. package/src/DoSubmissionLedger.ts +1806 -2030
  53. package/src/DoSubscriptionStore.ts +5 -47
  54. package/src/DoThreadStore.ts +276 -622
  55. package/src/MemoryProtocol.ts +3 -3
  56. package/src/PortProtocol.ts +198 -12
  57. package/src/PortRouting.ts +527 -108
  58. package/src/internal/canonical-append.ts +15 -0
  59. package/src/internal/do-journal.ts +548 -1440
  60. package/src/internal/migrations.ts +184 -287
  61. package/src/internal/owned-state.ts +81 -30
  62. package/src/internal/prompt-checkpoint.ts +158 -0
  63. package/src/internal/storage-span.ts +2 -5
  64. package/dist/DoThreadStore.mjs.map +0 -1
  65. package/dist/do-journal-DUhjZkOf.mjs +0 -1352
  66. package/dist/do-journal-DUhjZkOf.mjs.map +0 -1
  67. package/dist/migrations-DtKz_Gxr.mjs +0 -306
  68. package/dist/migrations-DtKz_Gxr.mjs.map +0 -1
  69. package/dist/owned-state-DHm2aRXx.mjs.map +0 -1
  70. package/src/internal/message-delivery-schema.ts +0 -24
  71. package/src/internal/recovery-checkpoint-schema.ts +0 -17
@@ -1,8 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
2
  import { Context, Effect, Layer, Schema } from "effect";
3
3
  import * as SqlClientService from "effect/sql/SqlClient";
4
- import { upgradeV2Subscriptions } from "@yielded/agent-storage-sql/sql-storage-v2-upgrade";
5
- import { BrowserCrypto } from "@effect/platform-browser";
6
4
  import { makeSqlSubscriptionStore } from "@yielded/agent-storage-sql/sql-subscription-store";
7
5
  import { SourcePartition, SubscriptionError, SubscriptionFailpoint, SubscriptionStore } from "@yielded/agent/subscription";
8
6
  //#region src/DoSubscriptionStore.ts
@@ -32,7 +30,7 @@ const unavailable = (operation) => error("storage", operation);
32
30
  const corrupt = (operation) => error("corrupt", operation);
33
31
  const validate = (schema, value, code) => Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError(() => error("validation", code)));
34
32
  const decodeRows = (schema, rows, code) => Schema.decodeUnknownEffect(Schema.Array(schema))(rows).pipe(Effect.mapError(() => corrupt(code)));
35
- const initializeDoSubscriptionStore = Effect.fn("DoSubscriptionStore.initialize")(function* () {
33
+ const initializeDoSubscriptionStore = Effect.fnUntraced(function* () {
36
34
  const sql = yield* SqlClientService.SqlClient;
37
35
  const names = yield* sql`
38
36
  SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'effect_agent_subscription_%'
@@ -89,33 +87,16 @@ const initializeDoSubscriptionStore = Effect.fn("DoSubscriptionStore.initialize"
89
87
  if (decodedNames.length !== expected.size || decodedNames.some(({ name }) => !expected.has(name))) return yield* corrupt("subscription storage tables");
90
88
  const rows = yield* sql`SELECT storage_version, alarm_generation FROM effect_agent_subscription_store_state WHERE singleton=1`.pipe(Effect.mapError(() => unavailable("read subscription storage version")));
91
89
  const state = yield* decodeRows(StoreStateRow, rows, "read subscription storage version");
92
- if (state.length === 1 && state[0].storage_version === 2) {
93
- const failpoint = yield* SubscriptionFailpoint;
94
- yield* sql.withTransaction(Effect.gen(function* () {
95
- const current = yield* sql`SELECT storage_version FROM effect_agent_subscription_store_state WHERE singleton=1`;
96
- if (current.length === 1 && current[0].storage_version === 3) return;
97
- if (current.length !== 1 || current[0].storage_version !== 2) return yield* corrupt("subscription version changed during upgrade");
98
- yield* upgradeV2Subscriptions(19e5);
99
- yield* failpoint.hit("upgrade:before-version");
100
- yield* sql`UPDATE effect_agent_subscription_store_state SET storage_version=3 WHERE singleton=1`;
101
- yield* failpoint.hit("upgrade:after-version");
102
- })).pipe(Effect.provide(BrowserCrypto.layer), Effect.catchTag("StorageUpgradeError", (error) => SubscriptionError.make({
103
- reason: "corrupt",
104
- code: "upgrade-v2-subscriptions",
105
- cause: error
106
- })), Effect.catchTag("SqlError", () => unavailable("upgrade v2 subscriptions")), Effect.catchTag("SchemaError", () => corrupt("upgrade v2 subscription encoding")), Effect.catchTag("SubscriptionFailpointError", () => unavailable("upgrade v2 subscriptions failpoint")));
107
- return;
108
- }
109
90
  if (state.length !== 1 || state[0].storage_version !== CURRENT_SUBSCRIPTION_STORE_VERSION) return yield* corrupt(state.length === 1 ? `incompatible subscription storage version ${state[0].storage_version}; expected ${CURRENT_SUBSCRIPTION_STORE_VERSION}` : "invalid subscription storage version row");
110
91
  });
111
- const makeSubscriptionStore = Effect.fn("DoSubscriptionStore.make")(function* (owned) {
92
+ const makeSubscriptionStore = Effect.fnUntraced(function* (owned) {
112
93
  const partition = yield* validate(SourcePartition, owned, "partition");
113
94
  const sql = yield* SqlClientService.SqlClient;
114
95
  const failpoint = yield* SubscriptionFailpoint;
115
96
  const transactions = yield* DoSubscriptionTransaction;
116
97
  yield* initializeDoSubscriptionStore();
117
98
  const query = (effect, code) => effect.pipe(Effect.mapError(() => unavailable(code)));
118
- const readIndexedDeadline = Effect.fn("DoSubscriptionStore.readIndexedDeadline")(function* () {
99
+ const readIndexedDeadline = Effect.fnUntraced(function* () {
119
100
  const scanRows = yield* query(sql`
120
101
  SELECT event_scan_cursor, delivery_scan_cursor, recovery_scan_cursor FROM effect_agent_subscription_sequences
121
102
  WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}
@@ -141,7 +122,7 @@ const makeSubscriptionStore = Effect.fn("DoSubscriptionStore.make")(function* (o
141
122
  if (decoded.length !== 1) return yield* corrupt("subscription deadline");
142
123
  return decoded[0].deadline;
143
124
  });
144
- const replaceAlarm = Effect.fn("DoSubscriptionStore.replaceAlarm")(function* (replace, deadlineAtMillis) {
125
+ const replaceAlarm = Effect.fnUntraced(function* (replace, deadlineAtMillis) {
145
126
  const rows = yield* query(sql`
146
127
  UPDATE effect_agent_subscription_store_state SET alarm_generation=alarm_generation+1
147
128
  WHERE singleton=1 RETURNING storage_version, alarm_generation
@@ -164,7 +145,7 @@ const makeSubscriptionStore = Effect.fn("DoSubscriptionStore.make")(function* (o
164
145
  maxStoredJsonLength: 19e5,
165
146
  transaction: transact
166
147
  });
167
- const prearm = Effect.fn("DoSubscriptionStore.prearm")(function* (deadlineAtMillis) {
148
+ const prearm = Effect.fnUntraced(function* (deadlineAtMillis) {
168
149
  yield* transactions.run((replace) => replaceAlarm(replace, deadlineAtMillis));
169
150
  yield* failpoint.hit("subscription:prearm:after");
170
151
  });
@@ -1 +1 @@
1
- {"version":3,"file":"DoSubscriptionStore.mjs","names":[],"sources":["../src/DoSubscriptionStore.ts"],"sourcesContent":["import { BrowserCrypto } from \"@effect/platform-browser\";\nimport { upgradeV2Subscriptions } from \"@yielded/agent-storage-sql/sql-storage-v2-upgrade\";\nimport { makeSqlSubscriptionStore } from \"@yielded/agent-storage-sql/sql-subscription-store\";\nimport {\n SourcePartition,\n SubscriptionError,\n SubscriptionFailpoint,\n type SubscriptionFailpointError,\n SubscriptionStore,\n} from \"@yielded/agent/subscription\";\nimport { Context, Effect, Layer, Schema } from \"effect\";\nimport * as SqlClientService from \"effect/sql/SqlClient\";\nimport type { SqlError } from \"effect/sql/SqlError\";\n\nconst CURRENT_SUBSCRIPTION_STORE_VERSION = 3;\n\nconst ScanRow = Schema.Struct({\n event_scan_cursor: Schema.String,\n delivery_scan_cursor: Schema.String,\n recovery_scan_cursor: Schema.Natural,\n});\n\nconst StoreStateRow = Schema.Struct({\n storage_version: Schema.Int,\n alarm_generation: Schema.Natural,\n});\n\nexport interface DoSubscriptionAlarmReplacement {\n readonly deadlineAtMillis: number | null;\n readonly generation: number;\n}\n\nexport type DoSubscriptionReplaceAlarm = (\n replacement: DoSubscriptionAlarmReplacement,\n) => Effect.Effect<void, SubscriptionError>;\n\n/** One Durable Object transaction combining subscription SQL and its single native alarm. */\nexport class DoSubscriptionTransaction extends Context.Service<\n DoSubscriptionTransaction,\n {\n readonly run: <A, E, R>(\n body: (replaceAlarm: DoSubscriptionReplaceAlarm) => Effect.Effect<A, E, R>,\n ) => Effect.Effect<A, E | SubscriptionError, R>;\n }\n>()(\"@effect-agent/storage-cloudflare/DoSubscriptionTransaction\") {}\n\nexport class DoSubscriptionAlarmControl extends Context.Service<\n DoSubscriptionAlarmControl,\n {\n readonly prearm: (\n deadlineAtMillis: number,\n ) => Effect.Effect<void, SubscriptionError | SubscriptionFailpointError>;\n readonly reconcile: Effect.Effect<void, SubscriptionError | SubscriptionFailpointError>;\n }\n>()(\"@effect-agent/storage-cloudflare/DoSubscriptionAlarmControl\") {}\n\nconst error = (reason: SubscriptionError[\"reason\"], code: string) =>\n SubscriptionError.make({ reason, code });\n\nconst unavailable = (operation: string) => error(\"storage\", operation);\nconst corrupt = (operation: string) => error(\"corrupt\", operation);\n\nconst validate = <A, I>(schema: Schema.Codec<A, I>, value: unknown, code: string) =>\n Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError(() => error(\"validation\", code)));\n\nconst decodeRows = <A, I>(schema: Schema.Codec<A, I>, rows: unknown, code: string) =>\n Schema.decodeUnknownEffect(Schema.Array(schema))(rows).pipe(Effect.mapError(() => corrupt(code)));\n\nconst initializeDoSubscriptionStore = Effect.fn(\"DoSubscriptionStore.initialize\")(function* () {\n const sql = yield* SqlClientService.SqlClient;\n\n const names = yield* sql<Record<string, unknown>>`\n SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'effect_agent_subscription_%'\n `.pipe(Effect.mapError(() => unavailable(\"inspect subscription storage\")));\n\n const decodedNames = yield* decodeRows(\n Schema.Struct({ name: Schema.String }),\n names,\n \"inspect subscription storage\",\n );\n\n const expected = new Set([\n \"effect_agent_subscription_store_state\",\n \"effect_agent_subscription_sequences\",\n \"effect_agent_subscriptions\",\n \"effect_agent_subscription_events\",\n \"effect_agent_subscription_deliveries\",\n ]);\n\n const hasState = decodedNames.some(\n ({ name }) => name === \"effect_agent_subscription_store_state\",\n );\n\n if (!hasState && decodedNames.length > 0) return yield* corrupt(\"partial subscription storage\");\n if (!hasState) {\n yield* sql\n .withTransaction(\n Effect.gen(function* () {\n yield* sql`CREATE TABLE effect_agent_subscription_store_state (\n singleton INTEGER PRIMARY KEY NOT NULL CHECK(singleton=1), storage_version INTEGER NOT NULL, alarm_generation INTEGER NOT NULL\n )`.withoutTransform;\n yield* sql`CREATE TABLE effect_agent_subscription_sequences (\n tenant_id TEXT NOT NULL, source_address TEXT NOT NULL, sequence INTEGER NOT NULL,\n event_scan_cursor TEXT NOT NULL, delivery_scan_cursor TEXT NOT NULL, recovery_scan_cursor INTEGER NOT NULL,\n PRIMARY KEY (tenant_id, source_address)\n )`.withoutTransform;\n yield* sql`CREATE TABLE effect_agent_subscriptions (\n tenant_id TEXT NOT NULL, source_address TEXT NOT NULL, owner_id TEXT NOT NULL, subscription_id TEXT NOT NULL,\n ordinal INTEGER NOT NULL, source_name TEXT NOT NULL, source_version TEXT NOT NULL, matching_key TEXT NOT NULL,\n state TEXT NOT NULL, expires_at_millis INTEGER, recovery_at_millis INTEGER, recovery_present INTEGER NOT NULL DEFAULT 0, record_json TEXT NOT NULL,\n PRIMARY KEY (tenant_id, source_address, owner_id, subscription_id), UNIQUE (tenant_id, source_address, ordinal)\n )`.withoutTransform;\n yield* sql`CREATE INDEX effect_agent_subscriptions_owner ON effect_agent_subscriptions (tenant_id, source_address, owner_id, ordinal)`\n .withoutTransform;\n yield* sql`CREATE INDEX effect_agent_subscriptions_candidates ON effect_agent_subscriptions (tenant_id, source_address, source_name, source_version, matching_key, ordinal)`\n .withoutTransform;\n yield* sql`CREATE INDEX effect_agent_subscriptions_recovery ON effect_agent_subscriptions (tenant_id, source_address, recovery_at_millis, ordinal) WHERE recovery_at_millis IS NOT NULL`\n .withoutTransform;\n yield* sql`CREATE TABLE effect_agent_subscription_events (\n tenant_id TEXT NOT NULL, source_address TEXT NOT NULL, event_id TEXT NOT NULL, source_name TEXT NOT NULL,\n source_version TEXT NOT NULL, matching_key TEXT NOT NULL, payload_digest TEXT NOT NULL, cutoff INTEGER NOT NULL,\n cursor INTEGER NOT NULL, routing_complete INTEGER NOT NULL, tombstone INTEGER NOT NULL DEFAULT 0, next_attempt_at_millis INTEGER NOT NULL, record_json TEXT NOT NULL,\n PRIMARY KEY (tenant_id, source_address, event_id)\n )`.withoutTransform;\n yield* sql`CREATE INDEX effect_agent_subscription_events_pending ON effect_agent_subscription_events (tenant_id, source_address, routing_complete, next_attempt_at_millis, event_id)`\n .withoutTransform;\n yield* sql`CREATE TABLE effect_agent_subscription_deliveries (\n tenant_id TEXT NOT NULL, source_address TEXT NOT NULL, owner_id TEXT NOT NULL, subscription_id TEXT NOT NULL,\n event_id TEXT NOT NULL, delivery_key TEXT NOT NULL, state TEXT NOT NULL, next_attempt_at_millis INTEGER NOT NULL,\n record_json TEXT NOT NULL, PRIMARY KEY (tenant_id, source_address, owner_id, subscription_id, event_id),\n UNIQUE (tenant_id, source_address, delivery_key)\n )`.withoutTransform;\n yield* sql`CREATE INDEX effect_agent_subscription_deliveries_pending ON effect_agent_subscription_deliveries (tenant_id, source_address, state, next_attempt_at_millis, delivery_key)`\n .withoutTransform;\n yield* sql`CREATE INDEX effect_agent_subscription_deliveries_registration ON effect_agent_subscription_deliveries (tenant_id, source_address, owner_id, subscription_id, delivery_key)`\n .withoutTransform;\n yield* sql`INSERT INTO effect_agent_subscription_store_state (singleton, storage_version, alarm_generation)\n VALUES (1, ${CURRENT_SUBSCRIPTION_STORE_VERSION}, 0)`.withoutTransform;\n }),\n )\n .pipe(Effect.mapError(() => unavailable(\"initialize subscription storage\")));\n\n return;\n }\n if (decodedNames.length !== expected.size || decodedNames.some(({ name }) => !expected.has(name)))\n return yield* corrupt(\"subscription storage tables\");\n\n const rows = yield* sql<\n Record<string, unknown>\n >`SELECT storage_version, alarm_generation FROM effect_agent_subscription_store_state WHERE singleton=1`.pipe(\n Effect.mapError(() => unavailable(\"read subscription storage version\")),\n );\n\n const state = yield* decodeRows(StoreStateRow, rows, \"read subscription storage version\");\n\n if (state.length === 1 && state[0].storage_version === 2) {\n const failpoint = yield* SubscriptionFailpoint;\n\n yield* sql\n .withTransaction(\n Effect.gen(function* () {\n const current = yield* sql<{\n storage_version: number;\n }>`SELECT storage_version FROM effect_agent_subscription_store_state WHERE singleton=1`;\n\n if (current.length === 1 && current[0].storage_version === 3) return;\n if (current.length !== 1 || current[0].storage_version !== 2)\n return yield* corrupt(\"subscription version changed during upgrade\");\n yield* upgradeV2Subscriptions(1_900_000);\n yield* failpoint.hit(\"upgrade:before-version\");\n yield* sql`UPDATE effect_agent_subscription_store_state SET storage_version=3 WHERE singleton=1`;\n yield* failpoint.hit(\"upgrade:after-version\");\n }),\n )\n .pipe(\n Effect.provide(BrowserCrypto.layer),\n Effect.catchTag(\"StorageUpgradeError\", (error) =>\n SubscriptionError.make({\n reason: \"corrupt\",\n code: \"upgrade-v2-subscriptions\",\n cause: error,\n }),\n ),\n Effect.catchTag(\"SqlError\", () => unavailable(\"upgrade v2 subscriptions\")),\n Effect.catchTag(\"SchemaError\", () => corrupt(\"upgrade v2 subscription encoding\")),\n Effect.catchTag(\"SubscriptionFailpointError\", () =>\n unavailable(\"upgrade v2 subscriptions failpoint\"),\n ),\n );\n\n return;\n }\n\n if (state.length !== 1 || state[0].storage_version !== CURRENT_SUBSCRIPTION_STORE_VERSION)\n return yield* corrupt(\n state.length === 1\n ? `incompatible subscription storage version ${state[0].storage_version}; expected ${CURRENT_SUBSCRIPTION_STORE_VERSION}`\n : \"invalid subscription storage version row\",\n );\n});\n\nconst makeSubscriptionStore = Effect.fn(\"DoSubscriptionStore.make\")(function* (\n owned: SourcePartition,\n) {\n const partition = yield* validate(SourcePartition, owned, \"partition\");\n const sql = yield* SqlClientService.SqlClient;\n const failpoint = yield* SubscriptionFailpoint;\n const transactions = yield* DoSubscriptionTransaction;\n\n yield* initializeDoSubscriptionStore();\n\n const query = <A extends object>(\n effect: Effect.Effect<ReadonlyArray<A>, SqlError>,\n code: string,\n ) => effect.pipe(Effect.mapError(() => unavailable(code)));\n\n const readIndexedDeadline = Effect.fn(\"DoSubscriptionStore.readIndexedDeadline\")(function* () {\n const scanRows = yield* query(\n sql<Record<string, unknown>>`\n SELECT event_scan_cursor, delivery_scan_cursor, recovery_scan_cursor FROM effect_agent_subscription_sequences\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}\n `,\n \"read scan cursor deadline\",\n );\n\n const scans = yield* decodeRows(ScanRow, scanRows, \"read scan cursor deadline\");\n\n if (scans.length !== 1) return yield* corrupt(\"scan cursor deadline\");\n if (\n scans[0].event_scan_cursor !== \"\" ||\n scans[0].delivery_scan_cursor !== \"\" ||\n scans[0].recovery_scan_cursor !== 0\n )\n return 0;\n\n const rows = yield* query(\n sql<Record<string, unknown>>`\n SELECT MIN(deadline) AS deadline FROM (\n SELECT next_attempt_at_millis AS deadline FROM effect_agent_subscription_events\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address} AND routing_complete=0\n UNION ALL SELECT next_attempt_at_millis FROM effect_agent_subscription_deliveries\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address} AND CASE WHEN json_valid(record_json) THEN\n ((state NOT IN ('delivered','refused') AND COALESCE(json_extract(record_json, '$.retry.parked'), 0)=0) OR (state='delivered' AND json_extract(record_json, '$.observeSettlement')=1))\n ELSE state<>'refused' END\n UNION ALL SELECT next_maintenance_at_millis FROM effect_agent_event_retention\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}\n UNION ALL SELECT recovery_at_millis FROM effect_agent_subscriptions\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address} AND state='active' AND recovery_at_millis IS NOT NULL\n )\n `,\n \"read subscription deadline\",\n );\n\n const decoded = yield* decodeRows(\n Schema.Struct({ deadline: Schema.NullOr(Schema.Number) }),\n rows,\n \"read subscription deadline\",\n );\n\n if (decoded.length !== 1) return yield* corrupt(\"subscription deadline\");\n\n return decoded[0].deadline;\n });\n\n const replaceAlarm = Effect.fn(\"DoSubscriptionStore.replaceAlarm\")(function* (\n replace: DoSubscriptionReplaceAlarm,\n deadlineAtMillis: number | null,\n ) {\n const rows = yield* query(\n sql<Record<string, unknown>>`\n UPDATE effect_agent_subscription_store_state SET alarm_generation=alarm_generation+1\n WHERE singleton=1 RETURNING storage_version, alarm_generation\n `,\n \"advance subscription alarm generation\",\n );\n\n const state = yield* decodeRows(StoreStateRow, rows, \"advance subscription alarm generation\");\n\n if (state.length !== 1 || state[0].storage_version !== CURRENT_SUBSCRIPTION_STORE_VERSION)\n return yield* corrupt(\"subscription alarm state\");\n yield* failpoint.hit(\"subscription:alarm:before\");\n yield* replace({ deadlineAtMillis, generation: state[0].alarm_generation });\n yield* failpoint.hit(\"subscription:alarm:after\");\n });\n\n const transact = <A>(effect: Effect.Effect<A, SubscriptionError | SubscriptionFailpointError>) =>\n transactions.run((replace) =>\n Effect.gen(function* () {\n const value = yield* effect;\n\n yield* replaceAlarm(replace, yield* readIndexedDeadline());\n\n return value;\n }),\n );\n\n const store = yield* makeSqlSubscriptionStore(partition, {\n maxStoredJsonLength: 1_900_000,\n transaction: transact,\n });\n\n const prearm = Effect.fn(\"DoSubscriptionStore.prearm\")(function* (deadlineAtMillis: number) {\n yield* transactions.run((replace) => replaceAlarm(replace, deadlineAtMillis));\n yield* failpoint.hit(\"subscription:prearm:after\");\n });\n\n const reconcile = Effect.gen(function* () {\n yield* transactions.run((replace) =>\n Effect.gen(function* () {\n yield* replaceAlarm(replace, yield* readIndexedDeadline());\n }),\n );\n yield* failpoint.hit(\"subscription:reconcile:after\");\n });\n\n return Context.make(SubscriptionStore, store).pipe(\n Context.add(DoSubscriptionAlarmControl, { prearm, reconcile }),\n );\n});\n\nexport const doSubscriptionStoreLayer = (\n partition: SourcePartition,\n): Layer.Layer<\n SubscriptionStore | DoSubscriptionAlarmControl,\n SubscriptionError,\n DoSubscriptionTransaction | SqlClientService.SqlClient\n> => Layer.effectContext(makeSubscriptionStore(partition));\n"],"mappings":"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"}
1
+ {"version":3,"file":"DoSubscriptionStore.mjs","names":[],"sources":["../src/DoSubscriptionStore.ts"],"sourcesContent":["import { makeSqlSubscriptionStore } from \"@yielded/agent-storage-sql/sql-subscription-store\";\nimport {\n SourcePartition,\n SubscriptionError,\n SubscriptionFailpoint,\n type SubscriptionFailpointError,\n SubscriptionStore,\n} from \"@yielded/agent/subscription\";\nimport { Context, Effect, Layer, Schema } from \"effect\";\nimport * as SqlClientService from \"effect/sql/SqlClient\";\nimport type { SqlError } from \"effect/sql/SqlError\";\n\nconst CURRENT_SUBSCRIPTION_STORE_VERSION = 3;\n\nconst ScanRow = Schema.Struct({\n event_scan_cursor: Schema.String,\n delivery_scan_cursor: Schema.String,\n recovery_scan_cursor: Schema.Natural,\n});\n\nconst StoreStateRow = Schema.Struct({\n storage_version: Schema.Int,\n alarm_generation: Schema.Natural,\n});\n\nexport interface DoSubscriptionAlarmReplacement {\n readonly deadlineAtMillis: number | null;\n readonly generation: number;\n}\n\nexport type DoSubscriptionReplaceAlarm = (\n replacement: DoSubscriptionAlarmReplacement,\n) => Effect.Effect<void, SubscriptionError>;\n\n/** One Durable Object transaction combining subscription SQL and its single native alarm. */\nexport class DoSubscriptionTransaction extends Context.Service<\n DoSubscriptionTransaction,\n {\n readonly run: <A, E, R>(\n body: (replaceAlarm: DoSubscriptionReplaceAlarm) => Effect.Effect<A, E, R>,\n ) => Effect.Effect<A, E | SubscriptionError, R>;\n }\n>()(\"@effect-agent/storage-cloudflare/DoSubscriptionTransaction\") {}\n\nexport class DoSubscriptionAlarmControl extends Context.Service<\n DoSubscriptionAlarmControl,\n {\n readonly prearm: (\n deadlineAtMillis: number,\n ) => Effect.Effect<void, SubscriptionError | SubscriptionFailpointError>;\n readonly reconcile: Effect.Effect<void, SubscriptionError | SubscriptionFailpointError>;\n }\n>()(\"@effect-agent/storage-cloudflare/DoSubscriptionAlarmControl\") {}\n\nconst error = (reason: SubscriptionError[\"reason\"], code: string) =>\n SubscriptionError.make({ reason, code });\n\nconst unavailable = (operation: string) => error(\"storage\", operation);\nconst corrupt = (operation: string) => error(\"corrupt\", operation);\n\nconst validate = <A, I>(schema: Schema.Codec<A, I>, value: unknown, code: string) =>\n Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError(() => error(\"validation\", code)));\n\nconst decodeRows = <A, I>(schema: Schema.Codec<A, I>, rows: unknown, code: string) =>\n Schema.decodeUnknownEffect(Schema.Array(schema))(rows).pipe(Effect.mapError(() => corrupt(code)));\n\nconst initializeDoSubscriptionStore = Effect.fnUntraced(function* () {\n const sql = yield* SqlClientService.SqlClient;\n\n const names = yield* sql<Record<string, unknown>>`\n SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'effect_agent_subscription_%'\n `.pipe(Effect.mapError(() => unavailable(\"inspect subscription storage\")));\n\n const decodedNames = yield* decodeRows(\n Schema.Struct({ name: Schema.String }),\n names,\n \"inspect subscription storage\",\n );\n\n const expected = new Set([\n \"effect_agent_subscription_store_state\",\n \"effect_agent_subscription_sequences\",\n \"effect_agent_subscriptions\",\n \"effect_agent_subscription_events\",\n \"effect_agent_subscription_deliveries\",\n ]);\n\n const hasState = decodedNames.some(\n ({ name }) => name === \"effect_agent_subscription_store_state\",\n );\n\n if (!hasState && decodedNames.length > 0) return yield* corrupt(\"partial subscription storage\");\n if (!hasState) {\n yield* sql\n .withTransaction(\n Effect.gen(function* () {\n yield* sql`CREATE TABLE effect_agent_subscription_store_state (\n singleton INTEGER PRIMARY KEY NOT NULL CHECK(singleton=1), storage_version INTEGER NOT NULL, alarm_generation INTEGER NOT NULL\n )`.withoutTransform;\n yield* sql`CREATE TABLE effect_agent_subscription_sequences (\n tenant_id TEXT NOT NULL, source_address TEXT NOT NULL, sequence INTEGER NOT NULL,\n event_scan_cursor TEXT NOT NULL, delivery_scan_cursor TEXT NOT NULL, recovery_scan_cursor INTEGER NOT NULL,\n PRIMARY KEY (tenant_id, source_address)\n )`.withoutTransform;\n yield* sql`CREATE TABLE effect_agent_subscriptions (\n tenant_id TEXT NOT NULL, source_address TEXT NOT NULL, owner_id TEXT NOT NULL, subscription_id TEXT NOT NULL,\n ordinal INTEGER NOT NULL, source_name TEXT NOT NULL, source_version TEXT NOT NULL, matching_key TEXT NOT NULL,\n state TEXT NOT NULL, expires_at_millis INTEGER, recovery_at_millis INTEGER, recovery_present INTEGER NOT NULL DEFAULT 0, record_json TEXT NOT NULL,\n PRIMARY KEY (tenant_id, source_address, owner_id, subscription_id), UNIQUE (tenant_id, source_address, ordinal)\n )`.withoutTransform;\n yield* sql`CREATE INDEX effect_agent_subscriptions_owner ON effect_agent_subscriptions (tenant_id, source_address, owner_id, ordinal)`\n .withoutTransform;\n yield* sql`CREATE INDEX effect_agent_subscriptions_candidates ON effect_agent_subscriptions (tenant_id, source_address, source_name, source_version, matching_key, ordinal)`\n .withoutTransform;\n yield* sql`CREATE INDEX effect_agent_subscriptions_recovery ON effect_agent_subscriptions (tenant_id, source_address, recovery_at_millis, ordinal) WHERE recovery_at_millis IS NOT NULL`\n .withoutTransform;\n yield* sql`CREATE TABLE effect_agent_subscription_events (\n tenant_id TEXT NOT NULL, source_address TEXT NOT NULL, event_id TEXT NOT NULL, source_name TEXT NOT NULL,\n source_version TEXT NOT NULL, matching_key TEXT NOT NULL, payload_digest TEXT NOT NULL, cutoff INTEGER NOT NULL,\n cursor INTEGER NOT NULL, routing_complete INTEGER NOT NULL, tombstone INTEGER NOT NULL DEFAULT 0, next_attempt_at_millis INTEGER NOT NULL, record_json TEXT NOT NULL,\n PRIMARY KEY (tenant_id, source_address, event_id)\n )`.withoutTransform;\n yield* sql`CREATE INDEX effect_agent_subscription_events_pending ON effect_agent_subscription_events (tenant_id, source_address, routing_complete, next_attempt_at_millis, event_id)`\n .withoutTransform;\n yield* sql`CREATE TABLE effect_agent_subscription_deliveries (\n tenant_id TEXT NOT NULL, source_address TEXT NOT NULL, owner_id TEXT NOT NULL, subscription_id TEXT NOT NULL,\n event_id TEXT NOT NULL, delivery_key TEXT NOT NULL, state TEXT NOT NULL, next_attempt_at_millis INTEGER NOT NULL,\n record_json TEXT NOT NULL, PRIMARY KEY (tenant_id, source_address, owner_id, subscription_id, event_id),\n UNIQUE (tenant_id, source_address, delivery_key)\n )`.withoutTransform;\n yield* sql`CREATE INDEX effect_agent_subscription_deliveries_pending ON effect_agent_subscription_deliveries (tenant_id, source_address, state, next_attempt_at_millis, delivery_key)`\n .withoutTransform;\n yield* sql`CREATE INDEX effect_agent_subscription_deliveries_registration ON effect_agent_subscription_deliveries (tenant_id, source_address, owner_id, subscription_id, delivery_key)`\n .withoutTransform;\n yield* sql`INSERT INTO effect_agent_subscription_store_state (singleton, storage_version, alarm_generation)\n VALUES (1, ${CURRENT_SUBSCRIPTION_STORE_VERSION}, 0)`.withoutTransform;\n }),\n )\n .pipe(Effect.mapError(() => unavailable(\"initialize subscription storage\")));\n\n return;\n }\n if (decodedNames.length !== expected.size || decodedNames.some(({ name }) => !expected.has(name)))\n return yield* corrupt(\"subscription storage tables\");\n\n const rows = yield* sql<\n Record<string, unknown>\n >`SELECT storage_version, alarm_generation FROM effect_agent_subscription_store_state WHERE singleton=1`.pipe(\n Effect.mapError(() => unavailable(\"read subscription storage version\")),\n );\n\n const state = yield* decodeRows(StoreStateRow, rows, \"read subscription storage version\");\n\n if (state.length !== 1 || state[0].storage_version !== CURRENT_SUBSCRIPTION_STORE_VERSION)\n return yield* corrupt(\n state.length === 1\n ? `incompatible subscription storage version ${state[0].storage_version}; expected ${CURRENT_SUBSCRIPTION_STORE_VERSION}`\n : \"invalid subscription storage version row\",\n );\n});\n\nconst makeSubscriptionStore = Effect.fnUntraced(function* (owned: SourcePartition) {\n const partition = yield* validate(SourcePartition, owned, \"partition\");\n const sql = yield* SqlClientService.SqlClient;\n const failpoint = yield* SubscriptionFailpoint;\n const transactions = yield* DoSubscriptionTransaction;\n\n yield* initializeDoSubscriptionStore();\n\n const query = <A extends object>(\n effect: Effect.Effect<ReadonlyArray<A>, SqlError>,\n code: string,\n ) => effect.pipe(Effect.mapError(() => unavailable(code)));\n\n const readIndexedDeadline = Effect.fnUntraced(function* () {\n const scanRows = yield* query(\n sql<Record<string, unknown>>`\n SELECT event_scan_cursor, delivery_scan_cursor, recovery_scan_cursor FROM effect_agent_subscription_sequences\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}\n `,\n \"read scan cursor deadline\",\n );\n\n const scans = yield* decodeRows(ScanRow, scanRows, \"read scan cursor deadline\");\n\n if (scans.length !== 1) return yield* corrupt(\"scan cursor deadline\");\n if (\n scans[0].event_scan_cursor !== \"\" ||\n scans[0].delivery_scan_cursor !== \"\" ||\n scans[0].recovery_scan_cursor !== 0\n )\n return 0;\n\n const rows = yield* query(\n sql<Record<string, unknown>>`\n SELECT MIN(deadline) AS deadline FROM (\n SELECT next_attempt_at_millis AS deadline FROM effect_agent_subscription_events\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address} AND routing_complete=0\n UNION ALL SELECT next_attempt_at_millis FROM effect_agent_subscription_deliveries\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address} AND CASE WHEN json_valid(record_json) THEN\n ((state NOT IN ('delivered','refused') AND COALESCE(json_extract(record_json, '$.retry.parked'), 0)=0) OR (state='delivered' AND json_extract(record_json, '$.observeSettlement')=1))\n ELSE state<>'refused' END\n UNION ALL SELECT next_maintenance_at_millis FROM effect_agent_event_retention\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address}\n UNION ALL SELECT recovery_at_millis FROM effect_agent_subscriptions\n WHERE tenant_id=${partition.tenantId} AND source_address=${partition.address} AND state='active' AND recovery_at_millis IS NOT NULL\n )\n `,\n \"read subscription deadline\",\n );\n\n const decoded = yield* decodeRows(\n Schema.Struct({ deadline: Schema.NullOr(Schema.Number) }),\n rows,\n \"read subscription deadline\",\n );\n\n if (decoded.length !== 1) return yield* corrupt(\"subscription deadline\");\n\n return decoded[0].deadline;\n });\n\n const replaceAlarm = Effect.fnUntraced(function* (\n replace: DoSubscriptionReplaceAlarm,\n deadlineAtMillis: number | null,\n ) {\n const rows = yield* query(\n sql<Record<string, unknown>>`\n UPDATE effect_agent_subscription_store_state SET alarm_generation=alarm_generation+1\n WHERE singleton=1 RETURNING storage_version, alarm_generation\n `,\n \"advance subscription alarm generation\",\n );\n\n const state = yield* decodeRows(StoreStateRow, rows, \"advance subscription alarm generation\");\n\n if (state.length !== 1 || state[0].storage_version !== CURRENT_SUBSCRIPTION_STORE_VERSION)\n return yield* corrupt(\"subscription alarm state\");\n yield* failpoint.hit(\"subscription:alarm:before\");\n yield* replace({ deadlineAtMillis, generation: state[0].alarm_generation });\n yield* failpoint.hit(\"subscription:alarm:after\");\n });\n\n const transact = <A>(effect: Effect.Effect<A, SubscriptionError | SubscriptionFailpointError>) =>\n transactions.run((replace) =>\n Effect.gen(function* () {\n const value = yield* effect;\n\n yield* replaceAlarm(replace, yield* readIndexedDeadline());\n\n return value;\n }),\n );\n\n const store = yield* makeSqlSubscriptionStore(partition, {\n maxStoredJsonLength: 1_900_000,\n transaction: transact,\n });\n\n const prearm = Effect.fnUntraced(function* (deadlineAtMillis: number) {\n yield* transactions.run((replace) => replaceAlarm(replace, deadlineAtMillis));\n yield* failpoint.hit(\"subscription:prearm:after\");\n });\n\n const reconcile = Effect.gen(function* () {\n yield* transactions.run((replace) =>\n Effect.gen(function* () {\n yield* replaceAlarm(replace, yield* readIndexedDeadline());\n }),\n );\n yield* failpoint.hit(\"subscription:reconcile:after\");\n });\n\n return Context.make(SubscriptionStore, store).pipe(\n Context.add(DoSubscriptionAlarmControl, { prearm, reconcile }),\n );\n});\n\nexport const doSubscriptionStoreLayer = (\n partition: SourcePartition,\n): Layer.Layer<\n SubscriptionStore | DoSubscriptionAlarmControl,\n SubscriptionError,\n DoSubscriptionTransaction | SqlClientService.SqlClient\n> => Layer.effectContext(makeSubscriptionStore(partition));\n"],"mappings":"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"}
@@ -1,16 +1,18 @@
1
- import { a as DoStorageCompatibilityError, o as DoStorageCorruptionError, s as DoStorageError } from "./DoStorageError-CGuAfSwF.mjs";
1
+ import { a as DoStorageCorruptionError, i as DoStorageCompatibilityError, o as DoStorageError } from "./DoStorageError-CxjMnzUA.mjs";
2
2
  import { DoStorageConfig } from "./DoStorageConfig.mjs";
3
3
  import { DoStorageFailpoint, DoStorageFailpointHandler } from "./DoStorageFailpoint.mjs";
4
- import { Crypto, Effect, Layer, Schema } from "effect";
4
+ import { Crypto, Effect, Layer, Schema, Scope } from "effect";
5
5
  import * as SqlClientService from "effect/sql/SqlClient";
6
6
  import { SqlClient } from "effect/sql/SqlClient";
7
- import { ThreadStore } from "@yielded/agent/thread-store";
7
+ import { ThreadExportRequest, ThreadNotMaterialized, ThreadReader, ThreadStore, ThreadStoreError } from "@yielded/agent/thread-store";
8
8
  import { SqlError } from "effect/sql/SqlError";
9
+ import { StorageOwner } from "@yielded/agent/run-storage";
10
+ import { ThreadImport } from "@yielded/agent/thread-import";
9
11
  //#region src/internal/owned-state.d.ts
10
12
  /** Explicit invalidation for maintenance that writes private tables outside the adapters. */
11
13
  declare const invalidateOwnedState: (storage: DurableObjectStorage) => Effect.Effect<void>;
12
14
  declare namespace DoThreadStore_d_exports {
13
- export { DoStorageInitializationError, DoStorageOptions, invalidate, layer, sqlOwnerLayer, storageConfigLayer, storageFailpointLayer, threadStoreLayer };
15
+ export { DoStorageInitializationError, DoStorageOptions, exportThread, invalidate, layer, sqlOwnerLayer, storageConfigLayer, storageFailpointLayer, threadStoreLayer };
14
16
  }
15
17
  /**
16
18
  * Convenience-layer construction options. `storage` is the Durable Object's own
@@ -20,20 +22,15 @@ declare namespace DoThreadStore_d_exports {
20
22
  interface DoStorageOptions {
21
23
  readonly storage: DurableObjectStorage;
22
24
  readonly observationPollInterval?: number | undefined;
23
- /**
24
- * Submission ownership lease duration in milliseconds (D5). Defaults to
25
- * `DEFAULT_OWNERSHIP_LEASE_DURATION` from `@yielded/agent/submission-ledger`.
26
- */
27
- readonly ownershipLeaseDuration?: number | undefined;
28
25
  /**
29
26
  * Maximum bytes for any single stored value; must stay under the platform's 2 MB
30
27
  * per-value limit. Defaults to `DEFAULT_MAX_STORED_VALUE_BYTES`.
31
28
  */
32
29
  readonly maxStoredValueBytes?: number | undefined;
33
30
  /**
34
- * Re-verify every stored payload and digest chain while opening the store. Defaults to
35
- * off: per-operation Schema decoding and the digest chain already fail clearly on corrupt
36
- * rows without scanning the whole database on every open.
31
+ * Audit journal payloads, ownership, and batch coverage in bounded pages on open, including
32
+ * orphan rows and rows outside declared ranges. Defaults to false. Full reads validate
33
+ * canonical records; readPrompt trusts write-time validation of the omitted fields.
37
34
  */
38
35
  readonly verifyOnOpen?: boolean | undefined;
39
36
  readonly failpoint?: DoStorageFailpointHandler | undefined;
@@ -43,7 +40,7 @@ type DoStorageInitializationError = DoStorageCompatibilityError | DoStorageCorru
43
40
  * Durable Object Thread Store implementation with configuration, failpoint, SQL, and
44
41
  * Crypto authority kept visible in its input channel.
45
42
  */
46
- declare const threadStoreLayer: Layer.Layer<ThreadStore, DoStorageInitializationError, DoStorageConfig | DoStorageFailpoint | SqlClientService.SqlClient | Crypto.Crypto>;
43
+ declare const threadStoreLayer: Layer.Layer<ThreadStore | ThreadReader | ThreadImport | StorageOwner, DoStorageInitializationError, DoStorageConfig | DoStorageFailpoint | SqlClientService.SqlClient | Crypto.Crypto>;
47
44
  /**
48
45
  * Validated Durable Object storage configuration Layer with the documented defaults applied.
49
46
  * Shared by the ThreadStore and SubmissionLedger convenience layers so their defaults
@@ -55,10 +52,12 @@ declare const storageFailpointLayer: (options: DoStorageOptions) => Layer.Layer<
55
52
  /**
56
53
  * A composition-root convenience Layer for canonical Threads inside one Durable Object,
57
54
  * built over `ctx.storage`. Durable accepted work is served by the separate SubmissionLedger
58
- * port; point both at the SAME `ctx.storage` so claims fence the same producer epochs
55
+ * port; point both at the SAME `ctx.storage` so both share the same scoped storage owner
59
56
  * (ADR-0011 D7's "same file" rule, transposed to one object's private database).
60
57
  */
61
- declare const layer: (options: DoStorageOptions) => Layer.Layer<ThreadStore, DoStorageInitializationError>;
58
+ declare const layer: (options: DoStorageOptions) => Layer.Layer<ThreadStore | ThreadReader | ThreadImport | StorageOwner, DoStorageInitializationError>;
59
+ /** Read a quiesced current-layout Object without initializing its layout or ownership. */
60
+ declare const exportThread: (options: Pick<DoStorageOptions, "storage">, request: ThreadExportRequest) => Effect.Effect<import("@yielded/agent/thread-store").ThreadExport, DoStorageCompatibilityError | DoStorageCorruptionError | DoStorageError | import("@yielded/agent/thread-store").ThreadExportRejected | ThreadNotMaterialized | ThreadStoreError, import("effect/Scope").Scope>;
62
61
  /**
63
62
  * Discard the Object's derived thread and ledger state after direct SQL maintenance.
64
63
  * Quiesce port operations during the raw write and invalidation, and enroll the write with
@@ -66,7 +65,7 @@ declare const layer: (options: DoStorageOptions) => Layer.Layer<ThreadStore, DoS
66
65
  */
67
66
  declare const invalidate: typeof invalidateOwnedState;
68
67
  /** Share the Object's transaction gate with owner-local SQL Memory compositions. */
69
- declare const sqlOwnerLayer: Layer.Layer<never, DoStorageError, SqlClientService.SqlClient>;
68
+ declare const sqlOwnerLayer: Layer.Layer<StorageOwner, DoStorageError, SqlClientService.SqlClient>;
70
69
  //#endregion
71
- export { layer as a, storageFailpointLayer as c, invalidate as i, threadStoreLayer as l, DoStorageOptions as n, sqlOwnerLayer as o, DoThreadStore_d_exports as r, storageConfigLayer as s, DoStorageInitializationError as t };
72
- //# sourceMappingURL=DoThreadStore-D_83OjCH.d.mts.map
70
+ export { invalidate as a, storageConfigLayer as c, exportThread as i, storageFailpointLayer as l, DoStorageOptions as n, layer as o, DoThreadStore_d_exports as r, sqlOwnerLayer as s, DoStorageInitializationError as t, threadStoreLayer as u };
71
+ //# sourceMappingURL=DoThreadStore-Bd4fTns3.d.mts.map