@minpeter/pss-runtime 0.1.0-next.4 → 0.1.0-next.5

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.
@@ -0,0 +1,36 @@
1
+ import { CommitResult, ExpectedSessionVersion, SessionStore, SessionStoreCommit, StoredSession } from "../session/store/types.js";
2
+ import { CloudflareDurableObjectStorage } from "./durable-object-storage.js";
3
+
4
+ //#region src/cloudflare/cloudflare-sqlite-session-store.d.ts
5
+ /**
6
+ * Append-only session store for SQLite-backed Durable Objects.
7
+ *
8
+ * Unlike {@link DurableObjectExecutionSessionStore}, which re-serializes the
9
+ * entire session snapshot into a single `storage.put` value every commit (and
10
+ * eventually crosses the Durable Object ~2MB per-value limit on long sessions),
11
+ * this store persists the message history as one small SQLite row per message.
12
+ * Each commit only INSERTs the messages that are new since the last commit, so
13
+ * no single stored value grows with the conversation and `SQLITE_TOOBIG` can no
14
+ * longer be reached by accumulation.
15
+ *
16
+ * Snapshot v1 state (`{ schemaVersion: 1, history }`) is split into rows. Any
17
+ * other opaque state is stored verbatim in a single meta blob column, so the
18
+ * store remains a correct drop-in {@link SessionStore} for non-snapshot callers.
19
+ *
20
+ * Concurrency: the optimistic version check and the row writes form a single
21
+ * synchronous (await-free) read-modify-write section, so concurrent commits to
22
+ * the same key serialize on the JS event loop inside the single-threaded
23
+ * Durable Object — exactly one writer wins, the rest get a `conflict`.
24
+ */
25
+ declare class DurableObjectSqliteSessionStore implements SessionStore {
26
+ #private;
27
+ constructor(storage: CloudflareDurableObjectStorage, prefix: string);
28
+ commit(sessionKey: string, next: SessionStoreCommit, options: {
29
+ readonly expectedVersion: ExpectedSessionVersion;
30
+ }): Promise<CommitResult>;
31
+ delete(sessionKey: string): Promise<void>;
32
+ load(sessionKey: string): Promise<StoredSession | null>;
33
+ }
34
+ //#endregion
35
+ export { DurableObjectSqliteSessionStore };
36
+ //# sourceMappingURL=cloudflare-sqlite-session-store.d.ts.map
@@ -0,0 +1,182 @@
1
+ import { storeKey } from "./cloudflare-store-utils.js";
2
+ //#region src/cloudflare/cloudflare-sqlite-session-store.ts
3
+ /**
4
+ * Append-only session store for SQLite-backed Durable Objects.
5
+ *
6
+ * Unlike {@link DurableObjectExecutionSessionStore}, which re-serializes the
7
+ * entire session snapshot into a single `storage.put` value every commit (and
8
+ * eventually crosses the Durable Object ~2MB per-value limit on long sessions),
9
+ * this store persists the message history as one small SQLite row per message.
10
+ * Each commit only INSERTs the messages that are new since the last commit, so
11
+ * no single stored value grows with the conversation and `SQLITE_TOOBIG` can no
12
+ * longer be reached by accumulation.
13
+ *
14
+ * Snapshot v1 state (`{ schemaVersion: 1, history }`) is split into rows. Any
15
+ * other opaque state is stored verbatim in a single meta blob column, so the
16
+ * store remains a correct drop-in {@link SessionStore} for non-snapshot callers.
17
+ *
18
+ * Concurrency: the optimistic version check and the row writes form a single
19
+ * synchronous (await-free) read-modify-write section, so concurrent commits to
20
+ * the same key serialize on the JS event loop inside the single-threaded
21
+ * Durable Object — exactly one writer wins, the rest get a `conflict`.
22
+ */
23
+ var DurableObjectSqliteSessionStore = class {
24
+ #migrated = /* @__PURE__ */ new Set();
25
+ #prefix;
26
+ #sql;
27
+ #storage;
28
+ #schemaReady = false;
29
+ constructor(storage, prefix) {
30
+ const sql = storage.sql;
31
+ if (!sql) throw new Error("DurableObjectSqliteSessionStore requires a SQLite-backed Durable Object (storage.sql is unavailable)");
32
+ this.#prefix = prefix;
33
+ this.#sql = sql;
34
+ this.#storage = storage;
35
+ }
36
+ async commit(sessionKey, next, options) {
37
+ this.#ensureSchema();
38
+ const key = this.#rowKey(sessionKey);
39
+ await this.#ensureMigrated(key, sessionKey);
40
+ const meta = this.#readMeta(key);
41
+ const currentVersion = meta ? meta.version : null;
42
+ if (options.expectedVersion !== currentVersion) return {
43
+ ok: false,
44
+ reason: "conflict"
45
+ };
46
+ const version = String(versionCounter(meta?.version) + 1);
47
+ const nextSeqStart = meta?.next_seq ?? 0;
48
+ if (isSnapshotV1(next.state)) {
49
+ const nextSeq = this.#writeHistoryRows(key, next.state.history, nextSeqStart);
50
+ this.#writeMeta(key, {
51
+ message_count: next.state.history.length,
52
+ next_seq: nextSeq,
53
+ state_blob: null,
54
+ version
55
+ });
56
+ } else {
57
+ this.#softDeleteActiveRows(key);
58
+ this.#writeMeta(key, {
59
+ message_count: 0,
60
+ next_seq: nextSeqStart,
61
+ state_blob: JSON.stringify(next.state ?? null),
62
+ version
63
+ });
64
+ }
65
+ return {
66
+ ok: true,
67
+ version
68
+ };
69
+ }
70
+ async delete(sessionKey) {
71
+ this.#ensureSchema();
72
+ const key = this.#rowKey(sessionKey);
73
+ this.#sql.exec("DELETE FROM pss_session_message WHERE session_key = ?", key);
74
+ this.#sql.exec("DELETE FROM pss_session_meta WHERE session_key = ?", key);
75
+ this.#migrated.delete(key);
76
+ await this.#storage.delete(key);
77
+ await this.#storage.delete(storeKey(this.#prefix, "session-version", sessionKey));
78
+ }
79
+ async load(sessionKey) {
80
+ this.#ensureSchema();
81
+ const key = this.#rowKey(sessionKey);
82
+ await this.#ensureMigrated(key, sessionKey);
83
+ const meta = this.#readMeta(key);
84
+ if (!meta) return null;
85
+ const version = meta.version;
86
+ if (meta.state_blob !== null) return {
87
+ state: JSON.parse(meta.state_blob),
88
+ version
89
+ };
90
+ return {
91
+ state: {
92
+ history: this.#readActiveMessages(key).map((row) => JSON.parse(row.message)),
93
+ schemaVersion: 1
94
+ },
95
+ version
96
+ };
97
+ }
98
+ #rowKey(sessionKey) {
99
+ return storeKey(this.#prefix, "session", sessionKey);
100
+ }
101
+ #ensureSchema() {
102
+ if (this.#schemaReady) return;
103
+ this.#sql.exec("CREATE TABLE IF NOT EXISTS pss_session_message (session_key TEXT NOT NULL, seq INTEGER NOT NULL, active INTEGER NOT NULL DEFAULT 1, message TEXT NOT NULL, PRIMARY KEY (session_key, seq))");
104
+ this.#sql.exec("CREATE INDEX IF NOT EXISTS pss_session_message_active ON pss_session_message (session_key, active, seq)");
105
+ this.#sql.exec("CREATE TABLE IF NOT EXISTS pss_session_meta (session_key TEXT PRIMARY KEY, version TEXT NOT NULL, message_count INTEGER NOT NULL, next_seq INTEGER NOT NULL, state_blob TEXT)");
106
+ this.#schemaReady = true;
107
+ }
108
+ #readMeta(key) {
109
+ const row = this.#sql.exec("SELECT version, message_count, next_seq, state_blob FROM pss_session_meta WHERE session_key = ?", key).toArray()[0];
110
+ if (!row) return null;
111
+ return {
112
+ ...row,
113
+ version: String(row.version)
114
+ };
115
+ }
116
+ #readActiveMessages(key) {
117
+ return this.#sql.exec("SELECT seq, message FROM pss_session_message WHERE session_key = ? AND active = 1 ORDER BY seq", key).toArray();
118
+ }
119
+ #writeHistoryRows(key, history, nextSeqStart) {
120
+ const existing = this.#readActiveMessages(key);
121
+ let prefix = 0;
122
+ while (prefix < existing.length && prefix < history.length && existing[prefix].message === JSON.stringify(history[prefix])) prefix += 1;
123
+ if (prefix < existing.length) this.#sql.exec("UPDATE pss_session_message SET active = 0 WHERE session_key = ? AND active = 1 AND seq >= ?", key, existing[prefix].seq);
124
+ let seq = nextSeqStart;
125
+ for (let index = prefix; index < history.length; index += 1) {
126
+ this.#sql.exec("INSERT INTO pss_session_message (session_key, seq, active, message) VALUES (?, ?, 1, ?)", key, seq, JSON.stringify(history[index]));
127
+ seq += 1;
128
+ }
129
+ return seq;
130
+ }
131
+ #softDeleteActiveRows(key) {
132
+ this.#sql.exec("UPDATE pss_session_message SET active = 0 WHERE session_key = ? AND active = 1", key);
133
+ }
134
+ #writeMeta(key, meta) {
135
+ this.#sql.exec("INSERT INTO pss_session_meta (session_key, version, message_count, next_seq, state_blob) VALUES (?, ?, ?, ?, ?) ON CONFLICT(session_key) DO UPDATE SET version = excluded.version, message_count = excluded.message_count, next_seq = excluded.next_seq, state_blob = excluded.state_blob", key, meta.version, meta.message_count, meta.next_seq, meta.state_blob);
136
+ }
137
+ async #ensureMigrated(key, sessionKey) {
138
+ if (this.#migrated.has(key)) return;
139
+ if (this.#readMeta(key)) {
140
+ this.#migrated.add(key);
141
+ return;
142
+ }
143
+ const legacy = await this.#storage.get(key);
144
+ if (this.#migrated.has(key) || this.#readMeta(key)) {
145
+ this.#migrated.add(key);
146
+ return;
147
+ }
148
+ if (legacy) {
149
+ const version = legacy.version;
150
+ if (isSnapshotV1(legacy.state)) {
151
+ const nextSeq = this.#writeHistoryRows(key, legacy.state.history, 0);
152
+ this.#writeMeta(key, {
153
+ message_count: legacy.state.history.length,
154
+ next_seq: nextSeq,
155
+ state_blob: null,
156
+ version
157
+ });
158
+ } else this.#writeMeta(key, {
159
+ message_count: 0,
160
+ next_seq: 0,
161
+ state_blob: JSON.stringify(legacy.state ?? null),
162
+ version
163
+ });
164
+ this.#migrated.add(key);
165
+ await this.#storage.delete(key);
166
+ await this.#storage.delete(storeKey(this.#prefix, "session-version", sessionKey));
167
+ return;
168
+ }
169
+ this.#migrated.add(key);
170
+ }
171
+ };
172
+ function versionCounter(version) {
173
+ const parsed = Number(version);
174
+ return Number.isFinite(parsed) ? parsed : 0;
175
+ }
176
+ function isSnapshotV1(value) {
177
+ return value !== null && typeof value === "object" && "schemaVersion" in value && value.schemaVersion === 1 && "history" in value && Array.isArray(value.history);
178
+ }
179
+ //#endregion
180
+ export { DurableObjectSqliteSessionStore };
181
+
182
+ //# sourceMappingURL=cloudflare-sqlite-session-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare-sqlite-session-store.js","names":["#migrated","#prefix","#sql","#storage","#ensureSchema","#rowKey","#ensureMigrated","#readMeta","#writeHistoryRows","#writeMeta","#softDeleteActiveRows","#readActiveMessages","#schemaReady"],"sources":["../../src/cloudflare/cloudflare-sqlite-session-store.ts"],"sourcesContent":["import type {\n CommitResult,\n ExpectedSessionVersion,\n SessionStore,\n SessionStoreCommit,\n StoredSession,\n} from \"../index\";\nimport { storeKey } from \"./cloudflare-store-utils\";\nimport type { CloudflareDurableObjectStorage } from \"./durable-object-storage\";\nimport type { SqlStorage } from \"./sql-storage\";\n\ninterface MetaRow {\n readonly message_count: number;\n readonly next_seq: number;\n readonly state_blob: string | null;\n readonly version: string;\n}\n\ninterface MessageRow {\n readonly message: string;\n readonly seq: number;\n}\n\ninterface SessionSnapshotV1 {\n readonly history: unknown[];\n readonly schemaVersion: 1;\n}\n\n/**\n * Append-only session store for SQLite-backed Durable Objects.\n *\n * Unlike {@link DurableObjectExecutionSessionStore}, which re-serializes the\n * entire session snapshot into a single `storage.put` value every commit (and\n * eventually crosses the Durable Object ~2MB per-value limit on long sessions),\n * this store persists the message history as one small SQLite row per message.\n * Each commit only INSERTs the messages that are new since the last commit, so\n * no single stored value grows with the conversation and `SQLITE_TOOBIG` can no\n * longer be reached by accumulation.\n *\n * Snapshot v1 state (`{ schemaVersion: 1, history }`) is split into rows. Any\n * other opaque state is stored verbatim in a single meta blob column, so the\n * store remains a correct drop-in {@link SessionStore} for non-snapshot callers.\n *\n * Concurrency: the optimistic version check and the row writes form a single\n * synchronous (await-free) read-modify-write section, so concurrent commits to\n * the same key serialize on the JS event loop inside the single-threaded\n * Durable Object — exactly one writer wins, the rest get a `conflict`.\n */\nexport class DurableObjectSqliteSessionStore implements SessionStore {\n readonly #migrated = new Set<string>();\n readonly #prefix: string;\n readonly #sql: SqlStorage;\n readonly #storage: CloudflareDurableObjectStorage;\n #schemaReady = false;\n\n constructor(storage: CloudflareDurableObjectStorage, prefix: string) {\n // `sql` is read structurally so the shared CloudflareDurableObjectStorage\n // port stays free of a `sql` member — otherwise a real `DurableObjectStorage`\n // would stop being assignable to it. SQLite-backed Durable Objects expose\n // `storage.sql` at runtime; a non-SQLite DO yields undefined and throws here.\n const sql = (storage as { sql?: SqlStorage }).sql;\n if (!sql) {\n throw new Error(\n \"DurableObjectSqliteSessionStore requires a SQLite-backed Durable Object (storage.sql is unavailable)\"\n );\n }\n this.#prefix = prefix;\n this.#sql = sql;\n this.#storage = storage;\n }\n\n async commit(\n sessionKey: string,\n next: SessionStoreCommit,\n options: { readonly expectedVersion: ExpectedSessionVersion }\n ): Promise<CommitResult> {\n this.#ensureSchema();\n const key = this.#rowKey(sessionKey);\n await this.#ensureMigrated(key, sessionKey);\n\n // --- begin synchronous read-modify-write critical section (no await) ---\n const meta = this.#readMeta(key);\n const currentVersion = meta ? meta.version : null;\n if (options.expectedVersion !== currentVersion) {\n return { ok: false, reason: \"conflict\" };\n }\n\n const version = String(versionCounter(meta?.version) + 1);\n const nextSeqStart = meta?.next_seq ?? 0;\n\n if (isSnapshotV1(next.state)) {\n const nextSeq = this.#writeHistoryRows(\n key,\n next.state.history,\n nextSeqStart\n );\n this.#writeMeta(key, {\n message_count: next.state.history.length,\n next_seq: nextSeq,\n state_blob: null,\n version,\n });\n } else {\n this.#softDeleteActiveRows(key);\n this.#writeMeta(key, {\n message_count: 0,\n next_seq: nextSeqStart,\n state_blob: JSON.stringify(next.state ?? null),\n version,\n });\n }\n // --- end critical section ---\n\n return { ok: true, version };\n }\n\n async delete(sessionKey: string): Promise<void> {\n this.#ensureSchema();\n const key = this.#rowKey(sessionKey);\n this.#sql.exec(\n \"DELETE FROM pss_session_message WHERE session_key = ?\",\n key\n );\n this.#sql.exec(\"DELETE FROM pss_session_meta WHERE session_key = ?\", key);\n // Evict from the migration cache so the key does not accumulate over the\n // Durable Object's lifetime (a re-created session re-checks on next access).\n this.#migrated.delete(key);\n await this.#storage.delete(key);\n await this.#storage.delete(\n storeKey(this.#prefix, \"session-version\", sessionKey)\n );\n }\n\n async load(sessionKey: string): Promise<StoredSession | null> {\n this.#ensureSchema();\n const key = this.#rowKey(sessionKey);\n await this.#ensureMigrated(key, sessionKey);\n\n const meta = this.#readMeta(key);\n if (!meta) {\n return null;\n }\n const version = meta.version;\n if (meta.state_blob !== null) {\n const state: unknown = JSON.parse(meta.state_blob);\n return { state, version };\n }\n const history: unknown[] = this.#readActiveMessages(key).map(\n (row) => JSON.parse(row.message) as unknown\n );\n return { state: { history, schemaVersion: 1 }, version };\n }\n\n #rowKey(sessionKey: string): string {\n return storeKey(this.#prefix, \"session\", sessionKey);\n }\n\n #ensureSchema(): void {\n if (this.#schemaReady) {\n return;\n }\n this.#sql.exec(\n \"CREATE TABLE IF NOT EXISTS pss_session_message (session_key TEXT NOT NULL, seq INTEGER NOT NULL, active INTEGER NOT NULL DEFAULT 1, message TEXT NOT NULL, PRIMARY KEY (session_key, seq))\"\n );\n this.#sql.exec(\n \"CREATE INDEX IF NOT EXISTS pss_session_message_active ON pss_session_message (session_key, active, seq)\"\n );\n this.#sql.exec(\n \"CREATE TABLE IF NOT EXISTS pss_session_meta (session_key TEXT PRIMARY KEY, version TEXT NOT NULL, message_count INTEGER NOT NULL, next_seq INTEGER NOT NULL, state_blob TEXT)\"\n );\n this.#schemaReady = true;\n }\n\n #readMeta(key: string): MetaRow | null {\n const rows = this.#sql\n .exec<{\n message_count: number;\n next_seq: number;\n state_blob: string | null;\n version: number | string;\n }>(\n \"SELECT version, message_count, next_seq, state_blob FROM pss_session_meta WHERE session_key = ?\",\n key\n )\n .toArray();\n const row = rows[0];\n if (!row) {\n return null;\n }\n // Normalize the version to a string before the optimistic compare. A meta\n // row written under an INTEGER-affinity column (older schema or schema\n // drift — CREATE TABLE IF NOT EXISTS never alters an existing table) would\n // otherwise read back as a number and spuriously reject a valid commit.\n return { ...row, version: String(row.version) };\n }\n\n #readActiveMessages(key: string): MessageRow[] {\n return this.#sql\n .exec<MessageRow>(\n \"SELECT seq, message FROM pss_session_message WHERE session_key = ? AND active = 1 ORDER BY seq\",\n key\n )\n .toArray();\n }\n\n #writeHistoryRows(\n key: string,\n history: readonly unknown[],\n nextSeqStart: number\n ): number {\n const existing = this.#readActiveMessages(key);\n let prefix = 0;\n while (\n prefix < existing.length &&\n prefix < history.length &&\n existing[prefix].message === JSON.stringify(history[prefix])\n ) {\n prefix += 1;\n }\n if (prefix < existing.length) {\n // History diverged or shrank (rollback): soft-delete the divergent tail.\n this.#sql.exec(\n \"UPDATE pss_session_message SET active = 0 WHERE session_key = ? AND active = 1 AND seq >= ?\",\n key,\n existing[prefix].seq\n );\n }\n let seq = nextSeqStart;\n for (let index = prefix; index < history.length; index += 1) {\n this.#sql.exec(\n \"INSERT INTO pss_session_message (session_key, seq, active, message) VALUES (?, ?, 1, ?)\",\n key,\n seq,\n JSON.stringify(history[index])\n );\n seq += 1;\n }\n return seq;\n }\n\n #softDeleteActiveRows(key: string): void {\n this.#sql.exec(\n \"UPDATE pss_session_message SET active = 0 WHERE session_key = ? AND active = 1\",\n key\n );\n }\n\n #writeMeta(key: string, meta: MetaRow): void {\n this.#sql.exec(\n \"INSERT INTO pss_session_meta (session_key, version, message_count, next_seq, state_blob) VALUES (?, ?, ?, ?, ?) ON CONFLICT(session_key) DO UPDATE SET version = excluded.version, message_count = excluded.message_count, next_seq = excluded.next_seq, state_blob = excluded.state_blob\",\n key,\n meta.version,\n meta.message_count,\n meta.next_seq,\n meta.state_blob\n );\n }\n\n async #ensureMigrated(key: string, sessionKey: string): Promise<void> {\n if (this.#migrated.has(key)) {\n return;\n }\n if (this.#readMeta(key)) {\n this.#migrated.add(key);\n return;\n }\n const legacy = await this.#storage.get<StoredSession>(key);\n // Re-check after the await: a concurrent commit/load may have migrated.\n if (this.#migrated.has(key) || this.#readMeta(key)) {\n this.#migrated.add(key);\n return;\n }\n if (legacy) {\n // Preserve the legacy version string verbatim so optimistic-version\n // continuity holds for both numeric (execution store) and UUID-based\n // (DurableObjectSessionStore) legacy sessions; the next commit derives its\n // counter from it via versionCounter().\n const version = legacy.version;\n if (isSnapshotV1(legacy.state)) {\n const nextSeq = this.#writeHistoryRows(key, legacy.state.history, 0);\n this.#writeMeta(key, {\n message_count: legacy.state.history.length,\n next_seq: nextSeq,\n state_blob: null,\n version,\n });\n } else {\n this.#writeMeta(key, {\n message_count: 0,\n next_seq: 0,\n state_blob: JSON.stringify(legacy.state ?? null),\n version,\n });\n }\n this.#migrated.add(key);\n await this.#storage.delete(key);\n await this.#storage.delete(\n storeKey(this.#prefix, \"session-version\", sessionKey)\n );\n return;\n }\n this.#migrated.add(key);\n }\n}\n\nfunction versionCounter(version: string | undefined): number {\n const parsed = Number(version);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\nfunction isSnapshotV1(value: unknown): value is SessionSnapshotV1 {\n return (\n value !== null &&\n typeof value === \"object\" &&\n \"schemaVersion\" in value &&\n (value as { schemaVersion?: unknown }).schemaVersion === 1 &&\n \"history\" in value &&\n Array.isArray((value as { history?: unknown }).history)\n );\n}\n"],"mappings":"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"}
@@ -1,3 +1,5 @@
1
+ import { SqlStorage } from "./sql-storage.js";
2
+
1
3
  //#region src/cloudflare/durable-object-storage.d.ts
2
4
  interface CloudflareDurableObjectStorage {
3
5
  delete(key: string): Promise<unknown>;
@@ -8,6 +10,10 @@ interface CloudflareDurableObjectStorage {
8
10
  }
9
11
  declare class InMemoryCloudflareDurableObjectStorage implements CloudflareDurableObjectStorage {
10
12
  #private;
13
+ readonly sql?: SqlStorage;
14
+ constructor(options?: {
15
+ readonly sql?: SqlStorage;
16
+ });
11
17
  alarmTime(): Date | number | undefined;
12
18
  delete(key: string): Promise<boolean>;
13
19
  get<T>(key: string): Promise<T | undefined>;
@@ -1,8 +1,12 @@
1
1
  //#region src/cloudflare/durable-object-storage.ts
2
2
  var InMemoryCloudflareDurableObjectStorage = class {
3
+ sql;
3
4
  #alarmTime;
4
5
  #transactionChain = Promise.resolve();
5
6
  #values = /* @__PURE__ */ new Map();
7
+ constructor(options) {
8
+ if (options?.sql) this.sql = options.sql;
9
+ }
6
10
  alarmTime() {
7
11
  return this.#alarmTime;
8
12
  }
@@ -1 +1 @@
1
- {"version":3,"file":"durable-object-storage.js","names":["#alarmTime","#values","#transactionChain","#setAlarm"],"sources":["../../src/cloudflare/durable-object-storage.ts"],"sourcesContent":["export interface CloudflareDurableObjectStorage {\n delete(key: string): Promise<unknown>;\n get<T>(key: string): Promise<T | undefined>;\n put<T>(key: string, value: T): Promise<void>;\n setAlarm?(scheduledTime: Date | number): Promise<void>;\n transaction?<T>(\n fn: (storage: CloudflareDurableObjectStorage) => Promise<T>\n ): Promise<T>;\n}\n\nexport class InMemoryCloudflareDurableObjectStorage\n implements CloudflareDurableObjectStorage\n{\n #alarmTime: Date | number | undefined;\n #transactionChain: Promise<void> = Promise.resolve();\n #values = new Map<string, unknown>();\n\n alarmTime(): Date | number | undefined {\n return this.#alarmTime;\n }\n\n delete(key: string): Promise<boolean> {\n return Promise.resolve(this.#values.delete(key));\n }\n\n get<T>(key: string): Promise<T | undefined> {\n const value = this.#values.get(key);\n return Promise.resolve(\n value === undefined ? undefined : (structuredClone(value) as T)\n );\n }\n\n put<T>(key: string, value: T): Promise<void> {\n this.#values.set(key, structuredClone(value));\n return Promise.resolve();\n }\n\n setAlarm(scheduledTime: Date | number): Promise<void> {\n this.#alarmTime = scheduledTime;\n return Promise.resolve();\n }\n\n async transaction<T>(\n fn: (storage: CloudflareDurableObjectStorage) => Promise<T>\n ): Promise<T> {\n const previousTransaction = this.#transactionChain;\n let releaseTransaction: () => void = () => undefined;\n this.#transactionChain = new Promise<void>((resolve) => {\n releaseTransaction = resolve;\n });\n await previousTransaction;\n\n const transactionValues = cloneMap(this.#values);\n const transactionStorage = new TransactionalCloudflareStorage(\n transactionValues,\n (scheduledTime) => {\n this.#alarmTime = scheduledTime;\n }\n );\n\n try {\n const result = await fn(transactionStorage);\n this.#values = transactionValues;\n return result;\n } finally {\n releaseTransaction();\n }\n }\n}\n\nclass TransactionalCloudflareStorage implements CloudflareDurableObjectStorage {\n readonly #setAlarm: (scheduledTime: Date | number) => void;\n readonly #values: Map<string, unknown>;\n\n constructor(\n values: Map<string, unknown>,\n setAlarm: (scheduledTime: Date | number) => void\n ) {\n this.#setAlarm = setAlarm;\n this.#values = values;\n }\n\n delete(key: string): Promise<boolean> {\n return Promise.resolve(this.#values.delete(key));\n }\n\n get<T>(key: string): Promise<T | undefined> {\n const value = this.#values.get(key);\n return Promise.resolve(\n value === undefined ? undefined : (structuredClone(value) as T)\n );\n }\n\n put<T>(key: string, value: T): Promise<void> {\n this.#values.set(key, structuredClone(value));\n return Promise.resolve();\n }\n\n setAlarm(scheduledTime: Date | number): Promise<void> {\n this.#setAlarm(scheduledTime);\n return Promise.resolve();\n }\n\n async transaction<T>(\n fn: (storage: CloudflareDurableObjectStorage) => Promise<T>\n ): Promise<T> {\n return await fn(this);\n }\n}\n\nfunction cloneMap(values: Map<string, unknown>): Map<string, unknown> {\n return new Map(\n [...values.entries()].map(([key, value]) => [key, structuredClone(value)])\n );\n}\n"],"mappings":";AAUA,IAAa,yCAAb,MAEA;CACE;CACA,oBAAmC,QAAQ,QAAQ;CACnD,0BAAU,IAAI,IAAqB;CAEnC,YAAuC;EACrC,OAAO,KAAKA;CACd;CAEA,OAAO,KAA+B;EACpC,OAAO,QAAQ,QAAQ,KAAKC,QAAQ,OAAO,GAAG,CAAC;CACjD;CAEA,IAAO,KAAqC;EAC1C,MAAM,QAAQ,KAAKA,QAAQ,IAAI,GAAG;EAClC,OAAO,QAAQ,QACb,UAAU,KAAA,IAAY,KAAA,IAAa,gBAAgB,KAAK,CAC1D;CACF;CAEA,IAAO,KAAa,OAAyB;EAC3C,KAAKA,QAAQ,IAAI,KAAK,gBAAgB,KAAK,CAAC;EAC5C,OAAO,QAAQ,QAAQ;CACzB;CAEA,SAAS,eAA6C;EACpD,KAAKD,aAAa;EAClB,OAAO,QAAQ,QAAQ;CACzB;CAEA,MAAM,YACJ,IACY;EACZ,MAAM,sBAAsB,KAAKE;EACjC,IAAI,2BAAuC,KAAA;EAC3C,KAAKA,oBAAoB,IAAI,SAAe,YAAY;GACtD,qBAAqB;EACvB,CAAC;EACD,MAAM;EAEN,MAAM,oBAAoB,SAAS,KAAKD,OAAO;EAC/C,MAAM,qBAAqB,IAAI,+BAC7B,oBACC,kBAAkB;GACjB,KAAKD,aAAa;EACpB,CACF;EAEA,IAAI;GACF,MAAM,SAAS,MAAM,GAAG,kBAAkB;GAC1C,KAAKC,UAAU;GACf,OAAO;EACT,UAAU;GACR,mBAAmB;EACrB;CACF;AACF;AAEA,IAAM,iCAAN,MAA+E;CAC7E;CACA;CAEA,YACE,QACA,UACA;EACA,KAAKE,YAAY;EACjB,KAAKF,UAAU;CACjB;CAEA,OAAO,KAA+B;EACpC,OAAO,QAAQ,QAAQ,KAAKA,QAAQ,OAAO,GAAG,CAAC;CACjD;CAEA,IAAO,KAAqC;EAC1C,MAAM,QAAQ,KAAKA,QAAQ,IAAI,GAAG;EAClC,OAAO,QAAQ,QACb,UAAU,KAAA,IAAY,KAAA,IAAa,gBAAgB,KAAK,CAC1D;CACF;CAEA,IAAO,KAAa,OAAyB;EAC3C,KAAKA,QAAQ,IAAI,KAAK,gBAAgB,KAAK,CAAC;EAC5C,OAAO,QAAQ,QAAQ;CACzB;CAEA,SAAS,eAA6C;EACpD,KAAKE,UAAU,aAAa;EAC5B,OAAO,QAAQ,QAAQ;CACzB;CAEA,MAAM,YACJ,IACY;EACZ,OAAO,MAAM,GAAG,IAAI;CACtB;AACF;AAEA,SAAS,SAAS,QAAoD;CACpE,OAAO,IAAI,IACT,CAAC,GAAG,OAAO,QAAQ,CAAC,EAAE,KAAK,CAAC,KAAK,WAAW,CAAC,KAAK,gBAAgB,KAAK,CAAC,CAAC,CAC3E;AACF"}
1
+ {"version":3,"file":"durable-object-storage.js","names":["#alarmTime","#values","#transactionChain","#setAlarm"],"sources":["../../src/cloudflare/durable-object-storage.ts"],"sourcesContent":["import type { SqlStorage } from \"./sql-storage\";\n\nexport interface CloudflareDurableObjectStorage {\n delete(key: string): Promise<unknown>;\n get<T>(key: string): Promise<T | undefined>;\n put<T>(key: string, value: T): Promise<void>;\n setAlarm?(scheduledTime: Date | number): Promise<void>;\n transaction?<T>(\n fn: (storage: CloudflareDurableObjectStorage) => Promise<T>\n ): Promise<T>;\n}\n\nexport class InMemoryCloudflareDurableObjectStorage\n implements CloudflareDurableObjectStorage\n{\n readonly sql?: SqlStorage;\n #alarmTime: Date | number | undefined;\n #transactionChain: Promise<void> = Promise.resolve();\n #values = new Map<string, unknown>();\n\n constructor(options?: { readonly sql?: SqlStorage }) {\n if (options?.sql) {\n this.sql = options.sql;\n }\n }\n\n alarmTime(): Date | number | undefined {\n return this.#alarmTime;\n }\n\n delete(key: string): Promise<boolean> {\n return Promise.resolve(this.#values.delete(key));\n }\n\n get<T>(key: string): Promise<T | undefined> {\n const value = this.#values.get(key);\n return Promise.resolve(\n value === undefined ? undefined : (structuredClone(value) as T)\n );\n }\n\n put<T>(key: string, value: T): Promise<void> {\n this.#values.set(key, structuredClone(value));\n return Promise.resolve();\n }\n\n setAlarm(scheduledTime: Date | number): Promise<void> {\n this.#alarmTime = scheduledTime;\n return Promise.resolve();\n }\n\n async transaction<T>(\n fn: (storage: CloudflareDurableObjectStorage) => Promise<T>\n ): Promise<T> {\n const previousTransaction = this.#transactionChain;\n let releaseTransaction: () => void = () => undefined;\n this.#transactionChain = new Promise<void>((resolve) => {\n releaseTransaction = resolve;\n });\n await previousTransaction;\n\n const transactionValues = cloneMap(this.#values);\n const transactionStorage = new TransactionalCloudflareStorage(\n transactionValues,\n (scheduledTime) => {\n this.#alarmTime = scheduledTime;\n }\n );\n\n try {\n const result = await fn(transactionStorage);\n this.#values = transactionValues;\n return result;\n } finally {\n releaseTransaction();\n }\n }\n}\n\nclass TransactionalCloudflareStorage implements CloudflareDurableObjectStorage {\n readonly #setAlarm: (scheduledTime: Date | number) => void;\n readonly #values: Map<string, unknown>;\n\n constructor(\n values: Map<string, unknown>,\n setAlarm: (scheduledTime: Date | number) => void\n ) {\n this.#setAlarm = setAlarm;\n this.#values = values;\n }\n\n delete(key: string): Promise<boolean> {\n return Promise.resolve(this.#values.delete(key));\n }\n\n get<T>(key: string): Promise<T | undefined> {\n const value = this.#values.get(key);\n return Promise.resolve(\n value === undefined ? undefined : (structuredClone(value) as T)\n );\n }\n\n put<T>(key: string, value: T): Promise<void> {\n this.#values.set(key, structuredClone(value));\n return Promise.resolve();\n }\n\n setAlarm(scheduledTime: Date | number): Promise<void> {\n this.#setAlarm(scheduledTime);\n return Promise.resolve();\n }\n\n async transaction<T>(\n fn: (storage: CloudflareDurableObjectStorage) => Promise<T>\n ): Promise<T> {\n return await fn(this);\n }\n}\n\nfunction cloneMap(values: Map<string, unknown>): Map<string, unknown> {\n return new Map(\n [...values.entries()].map(([key, value]) => [key, structuredClone(value)])\n );\n}\n"],"mappings":";AAYA,IAAa,yCAAb,MAEA;CACE;CACA;CACA,oBAAmC,QAAQ,QAAQ;CACnD,0BAAU,IAAI,IAAqB;CAEnC,YAAY,SAAyC;EACnD,IAAI,SAAS,KACX,KAAK,MAAM,QAAQ;CAEvB;CAEA,YAAuC;EACrC,OAAO,KAAKA;CACd;CAEA,OAAO,KAA+B;EACpC,OAAO,QAAQ,QAAQ,KAAKC,QAAQ,OAAO,GAAG,CAAC;CACjD;CAEA,IAAO,KAAqC;EAC1C,MAAM,QAAQ,KAAKA,QAAQ,IAAI,GAAG;EAClC,OAAO,QAAQ,QACb,UAAU,KAAA,IAAY,KAAA,IAAa,gBAAgB,KAAK,CAC1D;CACF;CAEA,IAAO,KAAa,OAAyB;EAC3C,KAAKA,QAAQ,IAAI,KAAK,gBAAgB,KAAK,CAAC;EAC5C,OAAO,QAAQ,QAAQ;CACzB;CAEA,SAAS,eAA6C;EACpD,KAAKD,aAAa;EAClB,OAAO,QAAQ,QAAQ;CACzB;CAEA,MAAM,YACJ,IACY;EACZ,MAAM,sBAAsB,KAAKE;EACjC,IAAI,2BAAuC,KAAA;EAC3C,KAAKA,oBAAoB,IAAI,SAAe,YAAY;GACtD,qBAAqB;EACvB,CAAC;EACD,MAAM;EAEN,MAAM,oBAAoB,SAAS,KAAKD,OAAO;EAC/C,MAAM,qBAAqB,IAAI,+BAC7B,oBACC,kBAAkB;GACjB,KAAKD,aAAa;EACpB,CACF;EAEA,IAAI;GACF,MAAM,SAAS,MAAM,GAAG,kBAAkB;GAC1C,KAAKC,UAAU;GACf,OAAO;EACT,UAAU;GACR,mBAAmB;EACrB;CACF;AACF;AAEA,IAAM,iCAAN,MAA+E;CAC7E;CACA;CAEA,YACE,QACA,UACA;EACA,KAAKE,YAAY;EACjB,KAAKF,UAAU;CACjB;CAEA,OAAO,KAA+B;EACpC,OAAO,QAAQ,QAAQ,KAAKA,QAAQ,OAAO,GAAG,CAAC;CACjD;CAEA,IAAO,KAAqC;EAC1C,MAAM,QAAQ,KAAKA,QAAQ,IAAI,GAAG;EAClC,OAAO,QAAQ,QACb,UAAU,KAAA,IAAY,KAAA,IAAa,gBAAgB,KAAK,CAC1D;CACF;CAEA,IAAO,KAAa,OAAyB;EAC3C,KAAKA,QAAQ,IAAI,KAAK,gBAAgB,KAAK,CAAC;EAC5C,OAAO,QAAQ,QAAQ;CACzB;CAEA,SAAS,eAA6C;EACpD,KAAKE,UAAU,aAAa;EAC5B,OAAO,QAAQ,QAAQ;CACzB;CAEA,MAAM,YACJ,IACY;EACZ,OAAO,MAAM,GAAG,IAAI;CACtB;AACF;AAEA,SAAS,SAAS,QAAoD;CACpE,OAAO,IAAI,IACT,CAAC,GAAG,OAAO,QAAQ,CAAC,EAAE,KAAK,CAAC,KAAK,WAAW,CAAC,KAAK,gBAAgB,KAAK,CAAC,CAAC,CAC3E;AACF"}
@@ -1,7 +1,9 @@
1
1
  import { CloudflareAlarmContinuationReason, CloudflareAlarmDrainBudget, FailedScheduledWork } from "./cloudflare-alarm-budget.js";
2
+ import { SqlStorage, SqlStorageCursorLike } from "./sql-storage.js";
2
3
  import { CloudflareDurableObjectId, CloudflareDurableObjectNamespace, CloudflareDurableObjectState, CloudflareDurableObjectStorage, CloudflareDurableObjectStub, CloudflareScheduledSessionPrompt, InMemoryCloudflareDurableObjectStorage, ackScheduledCloudflareRun, ackScheduledCloudflareSessionPrompt, createCloudflareAlarmScheduler, createCloudflareDurableObjectHost, listScheduledCloudflareRuns, listScheduledCloudflareSessionPrompts, rescheduleCloudflareAlarm } from "./cloudflare-host.js";
3
4
  import { CloudflareAlarmAgent, CloudflareAlarmDrainFailureError, CloudflareAlarmDrainSummary, drainCloudflareAlarm } from "./cloudflare-alarm-drainer.js";
4
5
  import { CloudflareAgentContext, CloudflareAgentContextFactoryOptions, CloudflareAgentContextOptions, CloudflareAgentContextPrefixOptions, createCloudflareAgentContext } from "./cloudflare-agent-context.js";
5
6
  import { CloudflareAgentRunDrainOptions, drainAgentRun } from "./cloudflare-alarm-run-drain.js";
6
7
  import { CloudflareDurableObjectFetchOptions, CloudflareDurableObjectStubOptions, fetchCloudflareDurableObject, getCloudflareDurableObjectStub } from "./cloudflare-durable-object-fetch.js";
7
- export { type CloudflareAgentContext, type CloudflareAgentContextFactoryOptions, type CloudflareAgentContextOptions, type CloudflareAgentContextPrefixOptions, type CloudflareAgentRunDrainOptions, type CloudflareAlarmAgent, type CloudflareAlarmContinuationReason, type CloudflareAlarmDrainBudget, CloudflareAlarmDrainFailureError, type CloudflareAlarmDrainSummary, type CloudflareDurableObjectFetchOptions, type CloudflareDurableObjectId, type CloudflareDurableObjectNamespace, type CloudflareDurableObjectState, type CloudflareDurableObjectStorage, type CloudflareDurableObjectStub, type CloudflareDurableObjectStubOptions, type CloudflareScheduledSessionPrompt, type FailedScheduledWork, InMemoryCloudflareDurableObjectStorage, ackScheduledCloudflareRun, ackScheduledCloudflareSessionPrompt, createCloudflareAgentContext, createCloudflareAlarmScheduler, createCloudflareDurableObjectHost, drainAgentRun, drainCloudflareAlarm, fetchCloudflareDurableObject, getCloudflareDurableObjectStub, listScheduledCloudflareRuns, listScheduledCloudflareSessionPrompts, rescheduleCloudflareAlarm };
8
+ import { DurableObjectSqliteSessionStore } from "./cloudflare-sqlite-session-store.js";
9
+ export { type CloudflareAgentContext, type CloudflareAgentContextFactoryOptions, type CloudflareAgentContextOptions, type CloudflareAgentContextPrefixOptions, type CloudflareAgentRunDrainOptions, type CloudflareAlarmAgent, type CloudflareAlarmContinuationReason, type CloudflareAlarmDrainBudget, CloudflareAlarmDrainFailureError, type CloudflareAlarmDrainSummary, type CloudflareDurableObjectFetchOptions, type CloudflareDurableObjectId, type CloudflareDurableObjectNamespace, type CloudflareDurableObjectState, type CloudflareDurableObjectStorage, type CloudflareDurableObjectStub, type CloudflareDurableObjectStubOptions, type CloudflareScheduledSessionPrompt, DurableObjectSqliteSessionStore, type FailedScheduledWork, InMemoryCloudflareDurableObjectStorage, type SqlStorage, type SqlStorageCursorLike, ackScheduledCloudflareRun, ackScheduledCloudflareSessionPrompt, createCloudflareAgentContext, createCloudflareAlarmScheduler, createCloudflareDurableObjectHost, drainAgentRun, drainCloudflareAlarm, fetchCloudflareDurableObject, getCloudflareDurableObjectStub, listScheduledCloudflareRuns, listScheduledCloudflareSessionPrompts, rescheduleCloudflareAlarm };
@@ -3,4 +3,5 @@ import { InMemoryCloudflareDurableObjectStorage, ackScheduledCloudflareRun, ackS
3
3
  import { CloudflareAlarmDrainFailureError, drainCloudflareAlarm } from "./cloudflare-alarm-drainer.js";
4
4
  import { createCloudflareAgentContext } from "./cloudflare-agent-context.js";
5
5
  import { fetchCloudflareDurableObject, getCloudflareDurableObjectStub } from "./cloudflare-durable-object-fetch.js";
6
- export { CloudflareAlarmDrainFailureError, InMemoryCloudflareDurableObjectStorage, ackScheduledCloudflareRun, ackScheduledCloudflareSessionPrompt, createCloudflareAgentContext, createCloudflareAlarmScheduler, createCloudflareDurableObjectHost, drainAgentRun, drainCloudflareAlarm, fetchCloudflareDurableObject, getCloudflareDurableObjectStub, listScheduledCloudflareRuns, listScheduledCloudflareSessionPrompts, rescheduleCloudflareAlarm };
6
+ import { DurableObjectSqliteSessionStore } from "./cloudflare-sqlite-session-store.js";
7
+ export { CloudflareAlarmDrainFailureError, DurableObjectSqliteSessionStore, InMemoryCloudflareDurableObjectStorage, ackScheduledCloudflareRun, ackScheduledCloudflareSessionPrompt, createCloudflareAgentContext, createCloudflareAlarmScheduler, createCloudflareDurableObjectHost, drainAgentRun, drainCloudflareAlarm, fetchCloudflareDurableObject, getCloudflareDurableObjectStub, listScheduledCloudflareRuns, listScheduledCloudflareSessionPrompts, rescheduleCloudflareAlarm };
@@ -0,0 +1,17 @@
1
+ //#region src/cloudflare/sql-storage.d.ts
2
+ /**
3
+ * Minimal structural subset of Cloudflare's `SqlStorage` that the SQLite-backed
4
+ * session store depends on. Keeping it narrow lets the store stay decoupled from
5
+ * `@cloudflare/workers-types` while remaining assignable from the real
6
+ * `ctx.storage.sql` (a SQLite-backed Durable Object) and from the in-memory
7
+ * `node:sqlite` test double.
8
+ */
9
+ interface SqlStorageCursorLike<T> extends Iterable<T> {
10
+ toArray(): T[];
11
+ }
12
+ interface SqlStorage {
13
+ exec<T = Record<string, unknown>>(query: string, ...bindings: unknown[]): SqlStorageCursorLike<T>;
14
+ }
15
+ //#endregion
16
+ export { SqlStorage, SqlStorageCursorLike };
17
+ //# sourceMappingURL=sql-storage.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minpeter/pss-runtime",
3
- "version": "0.1.0-next.4",
3
+ "version": "0.1.0-next.5",
4
4
  "description": "Generic agent runtime for sessions, model loops, and synchronized run events.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",