@pluv/persistence-cloudflare-transactional-storage 3.2.2 → 4.0.0
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.
- package/.turbo/turbo-build.log +14 -16
- package/CHANGELOG.md +21 -0
- package/dist/index.d.mts +24 -22
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +193 -321
- package/dist/index.mjs.map +1 -0
- package/package.json +18 -14
- package/tsdown.config.ts +15 -0
- package/dist/index.d.ts +0 -28
- package/dist/index.js +0 -396
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
|
|
2
|
-
> @pluv/persistence-cloudflare-transactional-storage@
|
|
3
|
-
>
|
|
2
|
+
> @pluv/persistence-cloudflare-transactional-storage@4.0.0 build /home/runner/work/pluv/pluv/packages/persistence-cloudflare-transactional-storage
|
|
3
|
+
> tsdown
|
|
4
4
|
|
|
5
|
-
[
|
|
6
|
-
[
|
|
7
|
-
[
|
|
8
|
-
[
|
|
9
|
-
[
|
|
10
|
-
[
|
|
11
|
-
[
|
|
12
|
-
[
|
|
13
|
-
[
|
|
14
|
-
[
|
|
15
|
-
[
|
|
16
|
-
[
|
|
17
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[32m1.22 KB[39m
|
|
18
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m1.22 KB[39m
|
|
5
|
+
[34mℹ[39m tsdown [2mv0.20.0-beta.4[22m powered by rolldown [2mv1.0.0-beta.60[22m
|
|
6
|
+
[34mℹ[39m config file: [4m/home/runner/work/pluv/pluv/packages/persistence-cloudflare-transactional-storage/tsdown.config.ts[24m
|
|
7
|
+
[34mℹ[39m entry: [34msrc/index.ts[39m
|
|
8
|
+
[34mℹ[39m target: [34mesnext[39m
|
|
9
|
+
[34mℹ[39m tsconfig: [34mtsconfig.json[39m
|
|
10
|
+
[34mℹ[39m Build start
|
|
11
|
+
[34mℹ[39m [2mdist/[22m[1mindex.mjs[22m [2m 8.23 kB[22m [2m│ gzip: 1.93 kB[22m
|
|
12
|
+
[34mℹ[39m [2mdist/[22mindex.mjs.map [2m17.13 kB[22m [2m│ gzip: 3.93 kB[22m
|
|
13
|
+
[34mℹ[39m [2mdist/[22mindex.d.mts.map [2m 0.73 kB[22m [2m│ gzip: 0.36 kB[22m
|
|
14
|
+
[34mℹ[39m [2mdist/[22m[32m[1mindex.d.mts[22m[39m [2m 1.32 kB[22m [2m│ gzip: 0.46 kB[22m
|
|
15
|
+
[34mℹ[39m 4 files, total: 27.40 kB
|
|
16
|
+
[32m✔[39m Build complete in [32m2700ms[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @pluv/persistence-cloudflare-transactional-storage
|
|
2
2
|
|
|
3
|
+
## 4.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- df5c39c: Update all packages to be ESM only.
|
|
8
|
+
|
|
9
|
+
### Minor Changes
|
|
10
|
+
|
|
11
|
+
- 037074e: Migrated build process from tsup to tsdown.
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- 2eb5def: Bumped dependencies.
|
|
16
|
+
- Updated dependencies [2eb5def]
|
|
17
|
+
- Updated dependencies [df5c39c]
|
|
18
|
+
- Updated dependencies [c142910]
|
|
19
|
+
- Updated dependencies [037074e]
|
|
20
|
+
- Updated dependencies [07e2a1e]
|
|
21
|
+
- @pluv/types@4.0.0
|
|
22
|
+
- @pluv/io@4.0.0
|
|
23
|
+
|
|
3
24
|
## 3.2.2
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
package/dist/index.d.mts
CHANGED
|
@@ -1,28 +1,30 @@
|
|
|
1
|
-
import { AbstractPersistence } from
|
|
2
|
-
import { JsonObject } from
|
|
1
|
+
import { AbstractPersistence } from "@pluv/io";
|
|
2
|
+
import { JsonObject } from "@pluv/types";
|
|
3
3
|
|
|
4
|
+
//#region src/PersistenceCloudflareTransactionalStorage.d.ts
|
|
4
5
|
interface PersistenceCloudflareTransactionalStorageConfig {
|
|
5
|
-
|
|
6
|
+
mode: "kv" | "sqlite";
|
|
6
7
|
}
|
|
7
8
|
declare class PersistenceCloudflareTransactionalStorage extends AbstractPersistence {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
9
|
+
private _mode;
|
|
10
|
+
private _state;
|
|
11
|
+
private _initialized;
|
|
12
|
+
constructor(config: PersistenceCloudflareTransactionalStorageConfig);
|
|
13
|
+
addUser(room: string, connectionId: string, user: JsonObject | null): Promise<void>;
|
|
14
|
+
deleteStorageState(room: string): Promise<void>;
|
|
15
|
+
deleteUser(room: string, connectionId: string): Promise<void>;
|
|
16
|
+
deleteUsers(room: string): Promise<void>;
|
|
17
|
+
getStorageState(room: string): Promise<string | null>;
|
|
18
|
+
getUser(room: string, connectionId: string): Promise<JsonObject | null>;
|
|
19
|
+
getUsers(room: string): Promise<Map<[connectionId: string][0], JsonObject | null>>;
|
|
20
|
+
getUsersSize(room: string): Promise<number>;
|
|
21
|
+
initialize(roomContext: {
|
|
22
|
+
state: DurableObjectState;
|
|
23
|
+
}): typeof this;
|
|
24
|
+
setStorageState(room: string, state: string): Promise<void>;
|
|
25
|
+
private _getStorageKey;
|
|
26
|
+
private _getUserKey;
|
|
26
27
|
}
|
|
27
|
-
|
|
28
|
+
//#endregion
|
|
28
29
|
export { PersistenceCloudflareTransactionalStorage, type PersistenceCloudflareTransactionalStorageConfig };
|
|
30
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/PersistenceCloudflareTransactionalStorage.ts"],"mappings":";;;;UAiBiB,+CAAA;EACb,IAAA;AAAA;AAAA,cAES,yCAAA,SAAkD,mBAAA;EAAA,QACnD,KAAA;EAAA,QACA,MAAA;EAAA,QACA,YAAA;cAEI,MAAA,EAAQ,+CAAA;EASP,OAAA,CACT,IAAA,UACA,YAAA,UACA,IAAA,EAAM,UAAA,UACP,OAAA;EAkCU,kBAAA,CAAmB,IAAA,WAAe,OAAA;EAoBlC,UAAA,CAAW,IAAA,UAAc,YAAA,WAAuB,OAAA;EAuBhD,WAAA,CAAY,IAAA,WAAe,OAAA;EAqC3B,eAAA,CAAgB,IAAA,WAAe,OAAA;EA6B/B,OAAA,CAAQ,IAAA,UAAc,YAAA,WAAuB,OAAA,CAAQ,UAAA;EAmCrD,QAAA,CACT,IAAA,WACD,OAAA,CAAQ,GAAA,EAAK,YAAA,cAA0B,UAAA;EAwC7B,YAAA,CAAa,IAAA,WAAe,OAAA;EAgClC,UAAA,CAAW,WAAA;IAAe,KAAA,EAAO,kBAAA;EAAA;EA8B3B,eAAA,CAAgB,IAAA,UAAc,KAAA,WAAgB,OAAA;EAAA,QA+CnD,cAAA;EAAA,QAQA,WAAA;AAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,299 +1,192 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
3
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
4
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
5
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
-
var __spreadValues = (a, b) => {
|
|
7
|
-
for (var prop in b || (b = {}))
|
|
8
|
-
if (__hasOwnProp.call(b, prop))
|
|
9
|
-
__defNormalProp(a, prop, b[prop]);
|
|
10
|
-
if (__getOwnPropSymbols)
|
|
11
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
12
|
-
if (__propIsEnum.call(b, prop))
|
|
13
|
-
__defNormalProp(a, prop, b[prop]);
|
|
14
|
-
}
|
|
15
|
-
return a;
|
|
16
|
-
};
|
|
17
|
-
var __async = (__this, __arguments, generator) => {
|
|
18
|
-
return new Promise((resolve, reject) => {
|
|
19
|
-
var fulfilled = (value) => {
|
|
20
|
-
try {
|
|
21
|
-
step(generator.next(value));
|
|
22
|
-
} catch (e) {
|
|
23
|
-
reject(e);
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
var rejected = (value) => {
|
|
27
|
-
try {
|
|
28
|
-
step(generator.throw(value));
|
|
29
|
-
} catch (e) {
|
|
30
|
-
reject(e);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
34
|
-
step((generator = generator.apply(__this, __arguments)).next());
|
|
35
|
-
});
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
// src/PersistenceCloudflareTransactionalStorage.ts
|
|
39
1
|
import { AbstractPersistence } from "@pluv/io";
|
|
40
2
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
3
|
+
//#region src/utils/castNumber.ts
|
|
4
|
+
const castNumber = (value) => {
|
|
5
|
+
return typeof value === "number" ? value : typeof value === "string" ? Number.parseInt(value, 10) : Number(value);
|
|
44
6
|
};
|
|
45
7
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return [head, ...partitionByLength(tail, length)];
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/utils/partitionByLength.ts
|
|
10
|
+
const partitionByLength = (arr, length) => {
|
|
11
|
+
if (!arr.length) return [];
|
|
12
|
+
return [arr.slice(0, length), ...partitionByLength(arr.slice(length), length)];
|
|
52
13
|
};
|
|
53
14
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/utils/sql.ts
|
|
17
|
+
const sql = (strings, ...values) => {
|
|
18
|
+
return strings.reduce((result, str, i) => {
|
|
19
|
+
return result + str.replace(/(\n[ \t]*)+/g, " ") + (i < values.length ? values[i] : "");
|
|
20
|
+
}, "").trim();
|
|
60
21
|
};
|
|
61
22
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/PersistenceCloudflareTransactionalStorage.ts
|
|
25
|
+
/**
|
|
26
|
+
* !HACK
|
|
27
|
+
* @description The max amount of keys a Cloudflare transactional storage may delete at once
|
|
28
|
+
* @see https://developers.cloudflare.com/durable-objects/api/transactional-storage-api/#delete
|
|
29
|
+
* @date July 8, 2024
|
|
30
|
+
*/
|
|
31
|
+
const KV_CLOUDFLARE_DELETE_BATCH_LIMIT = 128;
|
|
32
|
+
const KV_STORAGE_PREFIX = "$PLUV_STORAGE;";
|
|
33
|
+
const KV_USER_PREFIX = "$PLUV_USER";
|
|
34
|
+
const SQLITE_STORAGE_TABLE = "__pluv_storage";
|
|
35
|
+
const SQLITE_USER_TABLE = "__pluv_user";
|
|
36
|
+
var PersistenceCloudflareTransactionalStorage = class PersistenceCloudflareTransactionalStorage extends AbstractPersistence {
|
|
37
|
+
_mode;
|
|
38
|
+
_state = null;
|
|
39
|
+
_initialized = null;
|
|
40
|
+
constructor(config) {
|
|
41
|
+
super();
|
|
42
|
+
const { mode } = config;
|
|
43
|
+
this._initialized = config._initialized;
|
|
44
|
+
this._mode = mode;
|
|
45
|
+
}
|
|
46
|
+
async addUser(room, connectionId, user) {
|
|
47
|
+
if (!this._initialized) return;
|
|
48
|
+
if (!this._state) return;
|
|
49
|
+
await this._initialized;
|
|
50
|
+
if (this._mode === "kv") {
|
|
51
|
+
await this._state.storage.put(this._getUserKey(room, connectionId), user);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (!!(this._state.storage.sql.exec(sql`
|
|
89
55
|
SELECT id
|
|
90
56
|
FROM ${SQLITE_USER_TABLE}
|
|
91
57
|
WHERE id = ?;
|
|
92
|
-
`,
|
|
93
|
-
|
|
94
|
-
);
|
|
95
|
-
const existing = !!((_b = (_a = result.toArray()[0]) == null ? void 0 : _a.count) != null ? _b : 0);
|
|
96
|
-
if (existing) return;
|
|
97
|
-
this._state.storage.sql.exec(
|
|
98
|
-
sql`
|
|
58
|
+
`, connectionId).toArray()[0]?.count ?? 0)) return;
|
|
59
|
+
this._state.storage.sql.exec(sql`
|
|
99
60
|
INSERT INTO ${SQLITE_USER_TABLE} VALUES (?, ?, ?);
|
|
100
|
-
`,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
yield this._initialized;
|
|
112
|
-
if (this._mode === "kv") {
|
|
113
|
-
yield this._state.storage.delete(this._getStorageKey(room));
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
this._state.storage.sql.exec(
|
|
117
|
-
sql`
|
|
61
|
+
`, connectionId, JSON.stringify(user), room);
|
|
62
|
+
}
|
|
63
|
+
async deleteStorageState(room) {
|
|
64
|
+
if (!this._initialized) return;
|
|
65
|
+
if (!this._state) return;
|
|
66
|
+
await this._initialized;
|
|
67
|
+
if (this._mode === "kv") {
|
|
68
|
+
await this._state.storage.delete(this._getStorageKey(room));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
this._state.storage.sql.exec(sql`
|
|
118
72
|
DELETE FROM ${SQLITE_STORAGE_TABLE}
|
|
119
73
|
WHERE room = ?;
|
|
120
|
-
`,
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
yield this._state.storage.delete(this._getUserKey(room, connectionId));
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
this._state.storage.sql.exec(
|
|
135
|
-
sql`
|
|
74
|
+
`, room);
|
|
75
|
+
}
|
|
76
|
+
async deleteUser(room, connectionId) {
|
|
77
|
+
if (!this._initialized) return;
|
|
78
|
+
if (!this._state) return;
|
|
79
|
+
await this._initialized;
|
|
80
|
+
if (this._mode === "kv") {
|
|
81
|
+
await this._state.storage.delete(this._getUserKey(room, connectionId));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
this._state.storage.sql.exec(sql`
|
|
136
85
|
DELETE FROM ${SQLITE_USER_TABLE}
|
|
137
86
|
WHERE room = ?
|
|
138
87
|
AND id = ?;
|
|
139
|
-
`,
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
yield partitions.reduce((promise, partition) => __async(this, null, function* () {
|
|
162
|
-
yield promise;
|
|
163
|
-
yield tx.delete(partition);
|
|
164
|
-
}), Promise.resolve());
|
|
165
|
-
}));
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
this._state.storage.sql.exec(
|
|
169
|
-
sql`
|
|
88
|
+
`, room, connectionId);
|
|
89
|
+
}
|
|
90
|
+
async deleteUsers(room) {
|
|
91
|
+
if (!this._initialized) return;
|
|
92
|
+
if (!this._state) return;
|
|
93
|
+
await this._initialized;
|
|
94
|
+
if (this._mode === "kv") {
|
|
95
|
+
const map = await this._state.storage.list({
|
|
96
|
+
allowConcurrency: true,
|
|
97
|
+
noCache: true,
|
|
98
|
+
prefix: `${KV_USER_PREFIX}::${room}`
|
|
99
|
+
});
|
|
100
|
+
const partitions = partitionByLength(Array.from(map.keys()), KV_CLOUDFLARE_DELETE_BATCH_LIMIT);
|
|
101
|
+
await this._state.storage.transaction(async (tx) => {
|
|
102
|
+
await partitions.reduce(async (promise, partition) => {
|
|
103
|
+
await promise;
|
|
104
|
+
await tx.delete(partition);
|
|
105
|
+
}, Promise.resolve());
|
|
106
|
+
});
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
this._state.storage.sql.exec(sql`
|
|
170
110
|
DELETE FROM ${SQLITE_USER_TABLE}
|
|
171
111
|
WHERE room = ?;
|
|
172
|
-
`,
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (!this._state) return null;
|
|
181
|
-
yield this._initialized;
|
|
182
|
-
if (this._mode === "kv") {
|
|
183
|
-
const storage = yield this._state.storage.get(this._getStorageKey(room));
|
|
184
|
-
return storage != null ? storage : null;
|
|
185
|
-
}
|
|
186
|
-
const cursor = this._state.storage.sql.exec(
|
|
187
|
-
sql`
|
|
112
|
+
`, room);
|
|
113
|
+
}
|
|
114
|
+
async getStorageState(room) {
|
|
115
|
+
if (!this._initialized) return null;
|
|
116
|
+
if (!this._state) return null;
|
|
117
|
+
await this._initialized;
|
|
118
|
+
if (this._mode === "kv") return await this._state.storage.get(this._getStorageKey(room)) ?? null;
|
|
119
|
+
const cursor = this._state.storage.sql.exec(sql`
|
|
188
120
|
SELECT room, data
|
|
189
121
|
FROM ${SQLITE_STORAGE_TABLE}
|
|
190
122
|
WHERE room = ?;
|
|
191
|
-
`,
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (!this._initialized) return null;
|
|
205
|
-
if (!this._state) return null;
|
|
206
|
-
yield this._initialized;
|
|
207
|
-
if (this._mode === "kv") {
|
|
208
|
-
const user = yield this._state.storage.get(
|
|
209
|
-
this._getUserKey(room, connectionId)
|
|
210
|
-
);
|
|
211
|
-
return user != null ? user : null;
|
|
212
|
-
}
|
|
213
|
-
const cursor = this._state.storage.sql.exec(
|
|
214
|
-
sql`
|
|
123
|
+
`, room);
|
|
124
|
+
try {
|
|
125
|
+
return cursor.one().data;
|
|
126
|
+
} catch {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async getUser(room, connectionId) {
|
|
131
|
+
if (!this._initialized) return null;
|
|
132
|
+
if (!this._state) return null;
|
|
133
|
+
await this._initialized;
|
|
134
|
+
if (this._mode === "kv") return await this._state.storage.get(this._getUserKey(room, connectionId)) ?? null;
|
|
135
|
+
const cursor = this._state.storage.sql.exec(sql`
|
|
215
136
|
SELECT id, data, room
|
|
216
137
|
FROM ${SQLITE_USER_TABLE}
|
|
217
138
|
WHERE room = ?
|
|
218
139
|
AND id = ?;
|
|
219
|
-
`,
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
yield this._initialized;
|
|
237
|
-
if (this._mode === "kv") {
|
|
238
|
-
const entries = yield this._state.storage.list({
|
|
239
|
-
prefix: `${KV_USER_PREFIX}::${room}`
|
|
240
|
-
}).then((map) => Array.from(map.entries()));
|
|
241
|
-
return entries.reduce((map, [key, user]) => {
|
|
242
|
-
const connectionId = key.split("::").slice(-1)[0];
|
|
243
|
-
return !!connectionId ? map.set(connectionId, user) : map;
|
|
244
|
-
}, /* @__PURE__ */ new Map());
|
|
245
|
-
}
|
|
246
|
-
const cursor = this._state.storage.sql.exec(
|
|
247
|
-
sql`
|
|
140
|
+
`, room, connectionId);
|
|
141
|
+
try {
|
|
142
|
+
const result = cursor.one();
|
|
143
|
+
return JSON.parse(result.data);
|
|
144
|
+
} catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async getUsers(room) {
|
|
149
|
+
if (!this._initialized) return /* @__PURE__ */ new Map();
|
|
150
|
+
if (!this._state) return /* @__PURE__ */ new Map();
|
|
151
|
+
await this._initialized;
|
|
152
|
+
if (this._mode === "kv") return (await this._state.storage.list({ prefix: `${KV_USER_PREFIX}::${room}` }).then((map) => Array.from(map.entries()))).reduce((map, [key, user]) => {
|
|
153
|
+
const connectionId = key.split("::").slice(-1)[0];
|
|
154
|
+
return !!connectionId ? map.set(connectionId, user) : map;
|
|
155
|
+
}, /* @__PURE__ */ new Map());
|
|
156
|
+
return this._state.storage.sql.exec(sql`
|
|
248
157
|
SELECT id, room, data
|
|
249
158
|
FROM ${SQLITE_USER_TABLE}
|
|
250
159
|
WHERE room = ?;
|
|
251
|
-
`,
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
if (!this._initialized) return 0;
|
|
267
|
-
if (!this._state) return 0;
|
|
268
|
-
yield this._initialized;
|
|
269
|
-
if (this._mode) {
|
|
270
|
-
const storage = yield this._state.storage.list({
|
|
271
|
-
prefix: `${KV_USER_PREFIX}::${room}`
|
|
272
|
-
});
|
|
273
|
-
return storage.size;
|
|
274
|
-
}
|
|
275
|
-
const cursor = this._state.storage.sql.exec(
|
|
276
|
-
sql`
|
|
160
|
+
`, room).toArray().reduce((map, { id, data }) => {
|
|
161
|
+
try {
|
|
162
|
+
const user = JSON.parse(data);
|
|
163
|
+
return map.set(id, user);
|
|
164
|
+
} catch {
|
|
165
|
+
return map;
|
|
166
|
+
}
|
|
167
|
+
}, /* @__PURE__ */ new Map());
|
|
168
|
+
}
|
|
169
|
+
async getUsersSize(room) {
|
|
170
|
+
if (!this._initialized) return 0;
|
|
171
|
+
if (!this._state) return 0;
|
|
172
|
+
await this._initialized;
|
|
173
|
+
if (this._mode) return (await this._state.storage.list({ prefix: `${KV_USER_PREFIX}::${room}` })).size;
|
|
174
|
+
const cursor = this._state.storage.sql.exec(sql`
|
|
277
175
|
SELECT COUNT(*) count
|
|
278
176
|
FROM ${SQLITE_USER_TABLE}
|
|
279
177
|
WHERE room = ?;
|
|
280
|
-
`,
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const initialized = (() => __async(this, null, function* () {
|
|
293
|
-
const { state } = roomContext;
|
|
294
|
-
this._state = state;
|
|
295
|
-
if (this._mode === "sqlite") {
|
|
296
|
-
this._state.storage.sql.exec(sql`
|
|
178
|
+
`, room);
|
|
179
|
+
try {
|
|
180
|
+
return castNumber(cursor.one().count);
|
|
181
|
+
} catch {
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
initialize(roomContext) {
|
|
186
|
+
const initialized = (async () => {
|
|
187
|
+
const { state } = roomContext;
|
|
188
|
+
this._state = state;
|
|
189
|
+
if (this._mode === "sqlite") this._state.storage.sql.exec(sql`
|
|
297
190
|
CREATE TABLE IF NOT EXISTS ${SQLITE_STORAGE_TABLE}(
|
|
298
191
|
room TEXT PRIMARY KEY,
|
|
299
192
|
data TEXT NOT NULL
|
|
@@ -305,68 +198,47 @@ var PersistenceCloudflareTransactionalStorage = class _PersistenceCloudflareTran
|
|
|
305
198
|
);
|
|
306
199
|
CREATE INDEX IF NOT EXISTS ${SQLITE_USER_TABLE}_room ON ${SQLITE_USER_TABLE}(room);
|
|
307
200
|
`);
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
});
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
const result = this._state.storage.sql.exec(
|
|
328
|
-
sql`
|
|
201
|
+
return true;
|
|
202
|
+
})();
|
|
203
|
+
return new PersistenceCloudflareTransactionalStorage({
|
|
204
|
+
mode: this._mode,
|
|
205
|
+
_initialized: initialized
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
async setStorageState(room, state) {
|
|
209
|
+
if (!this._initialized) return;
|
|
210
|
+
if (!this._state) return;
|
|
211
|
+
await this._initialized;
|
|
212
|
+
if (this._mode === "kv") {
|
|
213
|
+
await this._state.storage.put(this._getStorageKey(room), state, { allowConcurrency: true });
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (!!(this._state.storage.sql.exec(sql`
|
|
329
217
|
SELECT COUNT(*) as count
|
|
330
218
|
FROM ${SQLITE_STORAGE_TABLE}
|
|
331
219
|
WHERE room = ?;
|
|
332
|
-
`,
|
|
333
|
-
|
|
334
|
-
);
|
|
335
|
-
const existing = !!((_b = (_a = result.toArray()[0]) == null ? void 0 : _a.count) != null ? _b : 0);
|
|
336
|
-
if (existing) {
|
|
337
|
-
this._state.storage.sql.exec(
|
|
338
|
-
sql`
|
|
220
|
+
`, room).toArray()[0]?.count ?? 0)) {
|
|
221
|
+
this._state.storage.sql.exec(sql`
|
|
339
222
|
UPDATE ${SQLITE_STORAGE_TABLE}
|
|
340
223
|
SET data = ?
|
|
341
224
|
WHERE room = ?;
|
|
342
|
-
`,
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
this._state.storage.sql.exec(
|
|
349
|
-
sql`
|
|
225
|
+
`, state, room);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
this._state.storage.sql.exec(sql`
|
|
350
229
|
INSERT INTO ${SQLITE_STORAGE_TABLE} VALUES (?, ?);
|
|
351
|
-
`,
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
return `${KV_STORAGE_PREFIX}::${room}`;
|
|
362
|
-
}
|
|
363
|
-
_getUserKey(room, connectionId) {
|
|
364
|
-
if (this._mode === "sqlite") {
|
|
365
|
-
throw new Error("Attempted to get kv key for sqlite storage");
|
|
366
|
-
}
|
|
367
|
-
return `${KV_USER_PREFIX}::${room}::${connectionId}`;
|
|
368
|
-
}
|
|
369
|
-
};
|
|
370
|
-
export {
|
|
371
|
-
PersistenceCloudflareTransactionalStorage
|
|
230
|
+
`, room, state);
|
|
231
|
+
}
|
|
232
|
+
_getStorageKey(room) {
|
|
233
|
+
if (this._mode === "sqlite") throw new Error("Attempted to get kv key for sqlite storage");
|
|
234
|
+
return `${KV_STORAGE_PREFIX}::${room}`;
|
|
235
|
+
}
|
|
236
|
+
_getUserKey(room, connectionId) {
|
|
237
|
+
if (this._mode === "sqlite") throw new Error("Attempted to get kv key for sqlite storage");
|
|
238
|
+
return `${KV_USER_PREFIX}::${room}::${connectionId}`;
|
|
239
|
+
}
|
|
372
240
|
};
|
|
241
|
+
|
|
242
|
+
//#endregion
|
|
243
|
+
export { PersistenceCloudflareTransactionalStorage };
|
|
244
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/utils/castNumber.ts","../src/utils/partitionByLength.ts","../src/utils/sql.ts","../src/PersistenceCloudflareTransactionalStorage.ts"],"sourcesContent":["export const castNumber = (value: any): number => {\n return typeof value === \"number\"\n ? value\n : typeof value === \"string\"\n ? Number.parseInt(value, 10)\n : Number(value);\n};\n","export const partitionByLength = <T>(\n arr: readonly T[] | T[],\n length: number,\n): readonly (readonly T[])[] => {\n if (!arr.length) return [];\n\n const head = arr.slice(0, length);\n const tail = arr.slice(length);\n\n return [head, ...partitionByLength(tail, length)];\n};\n","export const sql = (strings: TemplateStringsArray, ...values: any[]): string => {\n return strings\n .reduce((result, str, i) => {\n // Compresses the string to a single line (not including the interpolated values)\n const cleaned = str.replace(/(\\n[ \\t]*)+/g, \" \");\n\n return result + cleaned + (i < values.length ? values[i] : \"\");\n }, \"\")\n .trim();\n};\n","import { AbstractPersistence } from \"@pluv/io\";\nimport type { JsonObject } from \"@pluv/types\";\nimport { castNumber, partitionByLength, sql } from \"./utils\";\n\n/**\n * !HACK\n * @description The max amount of keys a Cloudflare transactional storage may delete at once\n * @see https://developers.cloudflare.com/durable-objects/api/transactional-storage-api/#delete\n * @date July 8, 2024\n */\nconst KV_CLOUDFLARE_DELETE_BATCH_LIMIT = 128;\nconst KV_STORAGE_PREFIX = \"$PLUV_STORAGE;\";\nconst KV_USER_PREFIX = \"$PLUV_USER\";\n\nconst SQLITE_STORAGE_TABLE = \"__pluv_storage\";\nconst SQLITE_USER_TABLE = \"__pluv_user\";\n\nexport interface PersistenceCloudflareTransactionalStorageConfig {\n mode: \"kv\" | \"sqlite\";\n}\nexport class PersistenceCloudflareTransactionalStorage extends AbstractPersistence {\n private _mode: \"kv\" | \"sqlite\";\n private _state: DurableObjectState | null = null;\n private _initialized: Promise<true> | null = null;\n\n constructor(config: PersistenceCloudflareTransactionalStorageConfig) {\n super();\n\n const { mode } = config;\n\n this._initialized = (config as any)._initialized;\n this._mode = mode;\n }\n\n public async addUser(\n room: string,\n connectionId: string,\n user: JsonObject | null,\n ): Promise<void> {\n if (!this._initialized) return;\n if (!this._state) return;\n\n await this._initialized;\n\n if (this._mode === \"kv\") {\n await this._state.storage.put(this._getUserKey(room, connectionId), user);\n return;\n }\n\n const result = this._state.storage.sql.exec<{ count: 0 }>(\n sql`\n SELECT id\n FROM ${SQLITE_USER_TABLE}\n WHERE id = ?;\n `,\n connectionId,\n );\n\n const existing = !!(result.toArray()[0]?.count ?? 0);\n\n if (existing) return;\n\n this._state.storage.sql.exec(\n sql`\n INSERT INTO ${SQLITE_USER_TABLE} VALUES (?, ?, ?);\n `,\n connectionId,\n JSON.stringify(user),\n room,\n );\n }\n\n public async deleteStorageState(room: string): Promise<void> {\n if (!this._initialized) return;\n if (!this._state) return;\n\n await this._initialized;\n\n if (this._mode === \"kv\") {\n await this._state.storage.delete(this._getStorageKey(room));\n return;\n }\n\n this._state.storage.sql.exec(\n sql`\n DELETE FROM ${SQLITE_STORAGE_TABLE}\n WHERE room = ?;\n `,\n room,\n );\n }\n\n public async deleteUser(room: string, connectionId: string): Promise<void> {\n if (!this._initialized) return;\n if (!this._state) return;\n\n await this._initialized;\n\n if (this._mode === \"kv\") {\n await this._state.storage.delete(this._getUserKey(room, connectionId));\n\n return;\n }\n\n this._state.storage.sql.exec(\n sql`\n DELETE FROM ${SQLITE_USER_TABLE}\n WHERE room = ?\n AND id = ?;\n `,\n room,\n connectionId,\n );\n }\n\n public async deleteUsers(room: string): Promise<void> {\n if (!this._initialized) return;\n if (!this._state) return;\n\n await this._initialized;\n\n if (this._mode === \"kv\") {\n const map = await this._state.storage.list({\n allowConcurrency: true,\n noCache: true,\n prefix: `${KV_USER_PREFIX}::${room}`,\n });\n\n const partitions = partitionByLength(\n Array.from(map.keys()),\n KV_CLOUDFLARE_DELETE_BATCH_LIMIT,\n );\n\n await this._state.storage.transaction(async (tx) => {\n await partitions.reduce(async (promise, partition) => {\n await promise;\n await tx.delete(partition as string[]);\n }, Promise.resolve());\n });\n\n return;\n }\n\n this._state.storage.sql.exec(\n sql`\n DELETE FROM ${SQLITE_USER_TABLE}\n WHERE room = ?;\n `,\n room,\n );\n }\n\n public async getStorageState(room: string): Promise<string | null> {\n if (!this._initialized) return null;\n if (!this._state) return null;\n\n await this._initialized;\n\n if (this._mode === \"kv\") {\n const storage = await this._state.storage.get<string>(this._getStorageKey(room));\n return storage ?? null;\n }\n\n const cursor = this._state.storage.sql.exec<{ room: string; data: string }>(\n sql`\n SELECT room, data\n FROM ${SQLITE_STORAGE_TABLE}\n WHERE room = ?;\n `,\n room,\n );\n\n try {\n const result = cursor.one();\n\n return result.data;\n } catch {\n return null;\n }\n }\n\n public async getUser(room: string, connectionId: string): Promise<JsonObject | null> {\n if (!this._initialized) return null;\n if (!this._state) return null;\n\n await this._initialized;\n\n if (this._mode === \"kv\") {\n const user = await this._state.storage.get<JsonObject | null>(\n this._getUserKey(room, connectionId),\n );\n\n return user ?? null;\n }\n\n const cursor = this._state.storage.sql.exec<{ id: string; data: string; room: string }>(\n sql`\n SELECT id, data, room\n FROM ${SQLITE_USER_TABLE}\n WHERE room = ?\n AND id = ?;\n `,\n room,\n connectionId,\n );\n\n try {\n const result = cursor.one();\n const user = JSON.parse(result.data);\n\n return user;\n } catch {\n return null;\n }\n }\n\n public async getUsers(\n room: string,\n ): Promise<Map<[connectionId: string][0], JsonObject | null>> {\n if (!this._initialized) return new Map();\n if (!this._state) return new Map();\n\n await this._initialized;\n\n if (this._mode === \"kv\") {\n const entries = await this._state.storage\n .list<JsonObject | null>({\n prefix: `${KV_USER_PREFIX}::${room}`,\n })\n .then((map) => Array.from(map.entries()));\n\n return entries.reduce((map, [key, user]) => {\n const connectionId = key.split(\"::\").slice(-1)[0];\n\n return !!connectionId ? map.set(connectionId, user) : map;\n }, new Map<string, JsonObject | null>());\n }\n\n const cursor = this._state.storage.sql.exec<{ id: string; data: string; room: string }>(\n sql`\n SELECT id, room, data\n FROM ${SQLITE_USER_TABLE}\n WHERE room = ?;\n `,\n room,\n );\n\n return cursor.toArray().reduce((map, { id, data }) => {\n try {\n const user = JSON.parse(data) as JsonObject;\n\n return map.set(id, user);\n } catch {\n return map;\n }\n }, new Map<string, JsonObject | null>());\n }\n\n public async getUsersSize(room: string): Promise<number> {\n if (!this._initialized) return 0;\n if (!this._state) return 0;\n\n await this._initialized;\n\n if (this._mode) {\n const storage = await this._state.storage.list({\n prefix: `${KV_USER_PREFIX}::${room}`,\n });\n\n return storage.size;\n }\n\n const cursor = this._state.storage.sql.exec<{ count: number }>(\n sql`\n SELECT COUNT(*) count\n FROM ${SQLITE_USER_TABLE}\n WHERE room = ?;\n `,\n room,\n );\n\n try {\n const result = cursor.one();\n\n return castNumber(result.count);\n } catch {\n return 0;\n }\n }\n\n public initialize(roomContext: { state: DurableObjectState }): typeof this {\n const initialized = (async () => {\n const { state } = roomContext;\n\n this._state = state;\n\n if (this._mode === \"sqlite\") {\n this._state.storage.sql.exec(sql`\n CREATE TABLE IF NOT EXISTS ${SQLITE_STORAGE_TABLE}(\n room TEXT PRIMARY KEY,\n data TEXT NOT NULL\n );\n CREATE TABLE IF NOT EXISTS ${SQLITE_USER_TABLE}(\n id TEXT PRIMARY KEY,\n data TEXT NOT NULL,\n room TEXT NOT NULL\n );\n CREATE INDEX IF NOT EXISTS ${SQLITE_USER_TABLE}_room ON ${SQLITE_USER_TABLE}(room);\n `);\n }\n\n return true as const;\n })();\n\n return new PersistenceCloudflareTransactionalStorage({\n mode: this._mode,\n ...{ _initialized: initialized },\n }) as typeof this;\n }\n\n public async setStorageState(room: string, state: string): Promise<void> {\n if (!this._initialized) return;\n if (!this._state) return;\n\n await this._initialized;\n\n if (this._mode === \"kv\") {\n await this._state.storage.put(this._getStorageKey(room), state, {\n allowConcurrency: true,\n });\n return;\n }\n\n const result = this._state.storage.sql.exec<{ count: number }>(\n sql`\n SELECT COUNT(*) as count\n FROM ${SQLITE_STORAGE_TABLE}\n WHERE room = ?;\n `,\n room,\n );\n\n const existing = !!(result.toArray()[0]?.count ?? 0);\n\n if (existing) {\n this._state.storage.sql.exec(\n sql`\n UPDATE ${SQLITE_STORAGE_TABLE}\n SET data = ?\n WHERE room = ?;\n `,\n state,\n room,\n );\n\n return;\n }\n\n this._state.storage.sql.exec(\n sql`\n INSERT INTO ${SQLITE_STORAGE_TABLE} VALUES (?, ?);\n `,\n room,\n state,\n );\n }\n\n private _getStorageKey(room: string): string {\n if (this._mode === \"sqlite\") {\n throw new Error(\"Attempted to get kv key for sqlite storage\");\n }\n\n return `${KV_STORAGE_PREFIX}::${room}`;\n }\n\n private _getUserKey(room: string, connectionId: string): string {\n if (this._mode === \"sqlite\") {\n throw new Error(\"Attempted to get kv key for sqlite storage\");\n }\n\n return `${KV_USER_PREFIX}::${room}::${connectionId}`;\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluv/persistence-cloudflare-transactional-storage",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "@pluv/io crdt storage persistence for cloudflare via transactional storage",
|
|
5
5
|
"author": {
|
|
6
6
|
"email": "david@pluv.io",
|
|
@@ -14,28 +14,32 @@
|
|
|
14
14
|
"url": "git+https://github.com/pluv-io/pluv.git",
|
|
15
15
|
"directory": "packages/persistence-cloudflare-transactional-storage"
|
|
16
16
|
},
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"types": "./dist/index.d.ts",
|
|
17
|
+
"type": "module",
|
|
18
|
+
"types": "./dist/index.d.mts",
|
|
20
19
|
"publishConfig": {
|
|
21
20
|
"access": "public"
|
|
22
21
|
},
|
|
23
22
|
"dependencies": {
|
|
24
|
-
"@pluv/io": "^
|
|
25
|
-
"@pluv/types": "^
|
|
23
|
+
"@pluv/io": "^4.0.0",
|
|
24
|
+
"@pluv/types": "^4.0.0"
|
|
26
25
|
},
|
|
27
26
|
"devDependencies": {
|
|
28
|
-
"@cloudflare/workers-types": "^4.
|
|
29
|
-
"eslint": "^9.
|
|
30
|
-
"
|
|
31
|
-
"typescript": "^5.
|
|
32
|
-
"@pluv/tsconfig": "^
|
|
33
|
-
"eslint-config-pluv": "^
|
|
27
|
+
"@cloudflare/workers-types": "^4.20260120.0",
|
|
28
|
+
"eslint": "^9.39.2",
|
|
29
|
+
"tsdown": "0.20.0-beta.4",
|
|
30
|
+
"typescript": "^5.9.3",
|
|
31
|
+
"@pluv/tsconfig": "^4.0.0",
|
|
32
|
+
"eslint-config-pluv": "^4.0.0"
|
|
33
|
+
},
|
|
34
|
+
"exports": {
|
|
35
|
+
".": "./dist/index.mjs",
|
|
36
|
+
"./*": "./*"
|
|
34
37
|
},
|
|
35
38
|
"scripts": {
|
|
36
|
-
"build": "
|
|
37
|
-
"dev": "
|
|
39
|
+
"build": "tsdown",
|
|
40
|
+
"dev": "tsdown --watch",
|
|
38
41
|
"lint": "eslint \"src/**/*.ts*\" --fix --max-warnings 0",
|
|
42
|
+
"typecheck": "tsc --noEmit",
|
|
39
43
|
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
|
|
40
44
|
}
|
|
41
45
|
}
|
package/tsdown.config.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { defineConfig } from "tsdown";
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
format: ["esm"],
|
|
5
|
+
dts: true,
|
|
6
|
+
outDir: "dist",
|
|
7
|
+
entry: "src/index.ts",
|
|
8
|
+
target: "esnext",
|
|
9
|
+
sourcemap: true,
|
|
10
|
+
clean: true,
|
|
11
|
+
external: ["cloudflare:workers"],
|
|
12
|
+
exports: {
|
|
13
|
+
all: true,
|
|
14
|
+
},
|
|
15
|
+
});
|
package/dist/index.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { AbstractPersistence } from '@pluv/io';
|
|
2
|
-
import { JsonObject } from '@pluv/types';
|
|
3
|
-
|
|
4
|
-
interface PersistenceCloudflareTransactionalStorageConfig {
|
|
5
|
-
mode: "kv" | "sqlite";
|
|
6
|
-
}
|
|
7
|
-
declare class PersistenceCloudflareTransactionalStorage extends AbstractPersistence {
|
|
8
|
-
private _mode;
|
|
9
|
-
private _state;
|
|
10
|
-
private _initialized;
|
|
11
|
-
constructor(config: PersistenceCloudflareTransactionalStorageConfig);
|
|
12
|
-
addUser(room: string, connectionId: string, user: JsonObject | null): Promise<void>;
|
|
13
|
-
deleteStorageState(room: string): Promise<void>;
|
|
14
|
-
deleteUser(room: string, connectionId: string): Promise<void>;
|
|
15
|
-
deleteUsers(room: string): Promise<void>;
|
|
16
|
-
getStorageState(room: string): Promise<string | null>;
|
|
17
|
-
getUser(room: string, connectionId: string): Promise<JsonObject | null>;
|
|
18
|
-
getUsers(room: string): Promise<Map<[connectionId: string][0], JsonObject | null>>;
|
|
19
|
-
getUsersSize(room: string): Promise<number>;
|
|
20
|
-
initialize(roomContext: {
|
|
21
|
-
state: DurableObjectState;
|
|
22
|
-
}): typeof this;
|
|
23
|
-
setStorageState(room: string, state: string): Promise<void>;
|
|
24
|
-
private _getStorageKey;
|
|
25
|
-
private _getUserKey;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export { PersistenceCloudflareTransactionalStorage, type PersistenceCloudflareTransactionalStorageConfig };
|
package/dist/index.js
DELETED
|
@@ -1,396 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
8
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
-
var __spreadValues = (a, b) => {
|
|
10
|
-
for (var prop in b || (b = {}))
|
|
11
|
-
if (__hasOwnProp.call(b, prop))
|
|
12
|
-
__defNormalProp(a, prop, b[prop]);
|
|
13
|
-
if (__getOwnPropSymbols)
|
|
14
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
15
|
-
if (__propIsEnum.call(b, prop))
|
|
16
|
-
__defNormalProp(a, prop, b[prop]);
|
|
17
|
-
}
|
|
18
|
-
return a;
|
|
19
|
-
};
|
|
20
|
-
var __export = (target, all) => {
|
|
21
|
-
for (var name in all)
|
|
22
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
23
|
-
};
|
|
24
|
-
var __copyProps = (to, from, except, desc) => {
|
|
25
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
26
|
-
for (let key of __getOwnPropNames(from))
|
|
27
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
28
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
29
|
-
}
|
|
30
|
-
return to;
|
|
31
|
-
};
|
|
32
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
33
|
-
var __async = (__this, __arguments, generator) => {
|
|
34
|
-
return new Promise((resolve, reject) => {
|
|
35
|
-
var fulfilled = (value) => {
|
|
36
|
-
try {
|
|
37
|
-
step(generator.next(value));
|
|
38
|
-
} catch (e) {
|
|
39
|
-
reject(e);
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
var rejected = (value) => {
|
|
43
|
-
try {
|
|
44
|
-
step(generator.throw(value));
|
|
45
|
-
} catch (e) {
|
|
46
|
-
reject(e);
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
50
|
-
step((generator = generator.apply(__this, __arguments)).next());
|
|
51
|
-
});
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
// src/index.ts
|
|
55
|
-
var index_exports = {};
|
|
56
|
-
__export(index_exports, {
|
|
57
|
-
PersistenceCloudflareTransactionalStorage: () => PersistenceCloudflareTransactionalStorage
|
|
58
|
-
});
|
|
59
|
-
module.exports = __toCommonJS(index_exports);
|
|
60
|
-
|
|
61
|
-
// src/PersistenceCloudflareTransactionalStorage.ts
|
|
62
|
-
var import_io = require("@pluv/io");
|
|
63
|
-
|
|
64
|
-
// src/utils/castNumber.ts
|
|
65
|
-
var castNumber = (value) => {
|
|
66
|
-
return typeof value === "number" ? value : typeof value === "string" ? Number.parseInt(value, 10) : Number(value);
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
// src/utils/partitionByLength.ts
|
|
70
|
-
var partitionByLength = (arr, length) => {
|
|
71
|
-
if (!arr.length) return [];
|
|
72
|
-
const head = arr.slice(0, length);
|
|
73
|
-
const tail = arr.slice(length);
|
|
74
|
-
return [head, ...partitionByLength(tail, length)];
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// src/utils/sql.ts
|
|
78
|
-
var sql = (strings, ...values) => {
|
|
79
|
-
return strings.reduce((result, str, i) => {
|
|
80
|
-
const cleaned = str.replace(/(\n[ \t]*)+/g, " ");
|
|
81
|
-
return result + cleaned + (i < values.length ? values[i] : "");
|
|
82
|
-
}, "").trim();
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
// src/PersistenceCloudflareTransactionalStorage.ts
|
|
86
|
-
var KV_CLOUDFLARE_DELETE_BATCH_LIMIT = 128;
|
|
87
|
-
var KV_STORAGE_PREFIX = "$PLUV_STORAGE;";
|
|
88
|
-
var KV_USER_PREFIX = "$PLUV_USER";
|
|
89
|
-
var SQLITE_STORAGE_TABLE = "__pluv_storage";
|
|
90
|
-
var SQLITE_USER_TABLE = "__pluv_user";
|
|
91
|
-
var PersistenceCloudflareTransactionalStorage = class _PersistenceCloudflareTransactionalStorage extends import_io.AbstractPersistence {
|
|
92
|
-
constructor(config) {
|
|
93
|
-
super();
|
|
94
|
-
this._state = null;
|
|
95
|
-
this._initialized = null;
|
|
96
|
-
const { mode } = config;
|
|
97
|
-
this._initialized = config._initialized;
|
|
98
|
-
this._mode = mode;
|
|
99
|
-
}
|
|
100
|
-
addUser(room, connectionId, user) {
|
|
101
|
-
return __async(this, null, function* () {
|
|
102
|
-
var _a, _b;
|
|
103
|
-
if (!this._initialized) return;
|
|
104
|
-
if (!this._state) return;
|
|
105
|
-
yield this._initialized;
|
|
106
|
-
if (this._mode === "kv") {
|
|
107
|
-
yield this._state.storage.put(this._getUserKey(room, connectionId), user);
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
const result = this._state.storage.sql.exec(
|
|
111
|
-
sql`
|
|
112
|
-
SELECT id
|
|
113
|
-
FROM ${SQLITE_USER_TABLE}
|
|
114
|
-
WHERE id = ?;
|
|
115
|
-
`,
|
|
116
|
-
connectionId
|
|
117
|
-
);
|
|
118
|
-
const existing = !!((_b = (_a = result.toArray()[0]) == null ? void 0 : _a.count) != null ? _b : 0);
|
|
119
|
-
if (existing) return;
|
|
120
|
-
this._state.storage.sql.exec(
|
|
121
|
-
sql`
|
|
122
|
-
INSERT INTO ${SQLITE_USER_TABLE} VALUES (?, ?, ?);
|
|
123
|
-
`,
|
|
124
|
-
connectionId,
|
|
125
|
-
JSON.stringify(user),
|
|
126
|
-
room
|
|
127
|
-
);
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
deleteStorageState(room) {
|
|
131
|
-
return __async(this, null, function* () {
|
|
132
|
-
if (!this._initialized) return;
|
|
133
|
-
if (!this._state) return;
|
|
134
|
-
yield this._initialized;
|
|
135
|
-
if (this._mode === "kv") {
|
|
136
|
-
yield this._state.storage.delete(this._getStorageKey(room));
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
this._state.storage.sql.exec(
|
|
140
|
-
sql`
|
|
141
|
-
DELETE FROM ${SQLITE_STORAGE_TABLE}
|
|
142
|
-
WHERE room = ?;
|
|
143
|
-
`,
|
|
144
|
-
room
|
|
145
|
-
);
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
deleteUser(room, connectionId) {
|
|
149
|
-
return __async(this, null, function* () {
|
|
150
|
-
if (!this._initialized) return;
|
|
151
|
-
if (!this._state) return;
|
|
152
|
-
yield this._initialized;
|
|
153
|
-
if (this._mode === "kv") {
|
|
154
|
-
yield this._state.storage.delete(this._getUserKey(room, connectionId));
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
this._state.storage.sql.exec(
|
|
158
|
-
sql`
|
|
159
|
-
DELETE FROM ${SQLITE_USER_TABLE}
|
|
160
|
-
WHERE room = ?
|
|
161
|
-
AND id = ?;
|
|
162
|
-
`,
|
|
163
|
-
room,
|
|
164
|
-
connectionId
|
|
165
|
-
);
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
deleteUsers(room) {
|
|
169
|
-
return __async(this, null, function* () {
|
|
170
|
-
if (!this._initialized) return;
|
|
171
|
-
if (!this._state) return;
|
|
172
|
-
yield this._initialized;
|
|
173
|
-
if (this._mode === "kv") {
|
|
174
|
-
const map = yield this._state.storage.list({
|
|
175
|
-
allowConcurrency: true,
|
|
176
|
-
noCache: true,
|
|
177
|
-
prefix: `${KV_USER_PREFIX}::${room}`
|
|
178
|
-
});
|
|
179
|
-
const partitions = partitionByLength(
|
|
180
|
-
Array.from(map.keys()),
|
|
181
|
-
KV_CLOUDFLARE_DELETE_BATCH_LIMIT
|
|
182
|
-
);
|
|
183
|
-
yield this._state.storage.transaction((tx) => __async(this, null, function* () {
|
|
184
|
-
yield partitions.reduce((promise, partition) => __async(this, null, function* () {
|
|
185
|
-
yield promise;
|
|
186
|
-
yield tx.delete(partition);
|
|
187
|
-
}), Promise.resolve());
|
|
188
|
-
}));
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
this._state.storage.sql.exec(
|
|
192
|
-
sql`
|
|
193
|
-
DELETE FROM ${SQLITE_USER_TABLE}
|
|
194
|
-
WHERE room = ?;
|
|
195
|
-
`,
|
|
196
|
-
room
|
|
197
|
-
);
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
getStorageState(room) {
|
|
201
|
-
return __async(this, null, function* () {
|
|
202
|
-
if (!this._initialized) return null;
|
|
203
|
-
if (!this._state) return null;
|
|
204
|
-
yield this._initialized;
|
|
205
|
-
if (this._mode === "kv") {
|
|
206
|
-
const storage = yield this._state.storage.get(this._getStorageKey(room));
|
|
207
|
-
return storage != null ? storage : null;
|
|
208
|
-
}
|
|
209
|
-
const cursor = this._state.storage.sql.exec(
|
|
210
|
-
sql`
|
|
211
|
-
SELECT room, data
|
|
212
|
-
FROM ${SQLITE_STORAGE_TABLE}
|
|
213
|
-
WHERE room = ?;
|
|
214
|
-
`,
|
|
215
|
-
room
|
|
216
|
-
);
|
|
217
|
-
try {
|
|
218
|
-
const result = cursor.one();
|
|
219
|
-
return result.data;
|
|
220
|
-
} catch (e) {
|
|
221
|
-
return null;
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
getUser(room, connectionId) {
|
|
226
|
-
return __async(this, null, function* () {
|
|
227
|
-
if (!this._initialized) return null;
|
|
228
|
-
if (!this._state) return null;
|
|
229
|
-
yield this._initialized;
|
|
230
|
-
if (this._mode === "kv") {
|
|
231
|
-
const user = yield this._state.storage.get(
|
|
232
|
-
this._getUserKey(room, connectionId)
|
|
233
|
-
);
|
|
234
|
-
return user != null ? user : null;
|
|
235
|
-
}
|
|
236
|
-
const cursor = this._state.storage.sql.exec(
|
|
237
|
-
sql`
|
|
238
|
-
SELECT id, data, room
|
|
239
|
-
FROM ${SQLITE_USER_TABLE}
|
|
240
|
-
WHERE room = ?
|
|
241
|
-
AND id = ?;
|
|
242
|
-
`,
|
|
243
|
-
room,
|
|
244
|
-
connectionId
|
|
245
|
-
);
|
|
246
|
-
try {
|
|
247
|
-
const result = cursor.one();
|
|
248
|
-
const user = JSON.parse(result.data);
|
|
249
|
-
return user;
|
|
250
|
-
} catch (e) {
|
|
251
|
-
return null;
|
|
252
|
-
}
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
getUsers(room) {
|
|
256
|
-
return __async(this, null, function* () {
|
|
257
|
-
if (!this._initialized) return /* @__PURE__ */ new Map();
|
|
258
|
-
if (!this._state) return /* @__PURE__ */ new Map();
|
|
259
|
-
yield this._initialized;
|
|
260
|
-
if (this._mode === "kv") {
|
|
261
|
-
const entries = yield this._state.storage.list({
|
|
262
|
-
prefix: `${KV_USER_PREFIX}::${room}`
|
|
263
|
-
}).then((map) => Array.from(map.entries()));
|
|
264
|
-
return entries.reduce((map, [key, user]) => {
|
|
265
|
-
const connectionId = key.split("::").slice(-1)[0];
|
|
266
|
-
return !!connectionId ? map.set(connectionId, user) : map;
|
|
267
|
-
}, /* @__PURE__ */ new Map());
|
|
268
|
-
}
|
|
269
|
-
const cursor = this._state.storage.sql.exec(
|
|
270
|
-
sql`
|
|
271
|
-
SELECT id, room, data
|
|
272
|
-
FROM ${SQLITE_USER_TABLE}
|
|
273
|
-
WHERE room = ?;
|
|
274
|
-
`,
|
|
275
|
-
room
|
|
276
|
-
);
|
|
277
|
-
return cursor.toArray().reduce((map, { id, data }) => {
|
|
278
|
-
try {
|
|
279
|
-
const user = JSON.parse(data);
|
|
280
|
-
return map.set(id, user);
|
|
281
|
-
} catch (e) {
|
|
282
|
-
return map;
|
|
283
|
-
}
|
|
284
|
-
}, /* @__PURE__ */ new Map());
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
getUsersSize(room) {
|
|
288
|
-
return __async(this, null, function* () {
|
|
289
|
-
if (!this._initialized) return 0;
|
|
290
|
-
if (!this._state) return 0;
|
|
291
|
-
yield this._initialized;
|
|
292
|
-
if (this._mode) {
|
|
293
|
-
const storage = yield this._state.storage.list({
|
|
294
|
-
prefix: `${KV_USER_PREFIX}::${room}`
|
|
295
|
-
});
|
|
296
|
-
return storage.size;
|
|
297
|
-
}
|
|
298
|
-
const cursor = this._state.storage.sql.exec(
|
|
299
|
-
sql`
|
|
300
|
-
SELECT COUNT(*) count
|
|
301
|
-
FROM ${SQLITE_USER_TABLE}
|
|
302
|
-
WHERE room = ?;
|
|
303
|
-
`,
|
|
304
|
-
room
|
|
305
|
-
);
|
|
306
|
-
try {
|
|
307
|
-
const result = cursor.one();
|
|
308
|
-
return castNumber(result.count);
|
|
309
|
-
} catch (e) {
|
|
310
|
-
return 0;
|
|
311
|
-
}
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
initialize(roomContext) {
|
|
315
|
-
const initialized = (() => __async(this, null, function* () {
|
|
316
|
-
const { state } = roomContext;
|
|
317
|
-
this._state = state;
|
|
318
|
-
if (this._mode === "sqlite") {
|
|
319
|
-
this._state.storage.sql.exec(sql`
|
|
320
|
-
CREATE TABLE IF NOT EXISTS ${SQLITE_STORAGE_TABLE}(
|
|
321
|
-
room TEXT PRIMARY KEY,
|
|
322
|
-
data TEXT NOT NULL
|
|
323
|
-
);
|
|
324
|
-
CREATE TABLE IF NOT EXISTS ${SQLITE_USER_TABLE}(
|
|
325
|
-
id TEXT PRIMARY KEY,
|
|
326
|
-
data TEXT NOT NULL,
|
|
327
|
-
room TEXT NOT NULL
|
|
328
|
-
);
|
|
329
|
-
CREATE INDEX IF NOT EXISTS ${SQLITE_USER_TABLE}_room ON ${SQLITE_USER_TABLE}(room);
|
|
330
|
-
`);
|
|
331
|
-
}
|
|
332
|
-
return true;
|
|
333
|
-
}))();
|
|
334
|
-
return new _PersistenceCloudflareTransactionalStorage(__spreadValues({
|
|
335
|
-
mode: this._mode
|
|
336
|
-
}, { _initialized: initialized }));
|
|
337
|
-
}
|
|
338
|
-
setStorageState(room, state) {
|
|
339
|
-
return __async(this, null, function* () {
|
|
340
|
-
var _a, _b;
|
|
341
|
-
if (!this._initialized) return;
|
|
342
|
-
if (!this._state) return;
|
|
343
|
-
yield this._initialized;
|
|
344
|
-
if (this._mode === "kv") {
|
|
345
|
-
yield this._state.storage.put(this._getStorageKey(room), state, {
|
|
346
|
-
allowConcurrency: true
|
|
347
|
-
});
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
350
|
-
const result = this._state.storage.sql.exec(
|
|
351
|
-
sql`
|
|
352
|
-
SELECT COUNT(*) as count
|
|
353
|
-
FROM ${SQLITE_STORAGE_TABLE}
|
|
354
|
-
WHERE room = ?;
|
|
355
|
-
`,
|
|
356
|
-
room
|
|
357
|
-
);
|
|
358
|
-
const existing = !!((_b = (_a = result.toArray()[0]) == null ? void 0 : _a.count) != null ? _b : 0);
|
|
359
|
-
if (existing) {
|
|
360
|
-
this._state.storage.sql.exec(
|
|
361
|
-
sql`
|
|
362
|
-
UPDATE ${SQLITE_STORAGE_TABLE}
|
|
363
|
-
SET data = ?
|
|
364
|
-
WHERE room = ?;
|
|
365
|
-
`,
|
|
366
|
-
state,
|
|
367
|
-
room
|
|
368
|
-
);
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
this._state.storage.sql.exec(
|
|
372
|
-
sql`
|
|
373
|
-
INSERT INTO ${SQLITE_STORAGE_TABLE} VALUES (?, ?);
|
|
374
|
-
`,
|
|
375
|
-
room,
|
|
376
|
-
state
|
|
377
|
-
);
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
_getStorageKey(room) {
|
|
381
|
-
if (this._mode === "sqlite") {
|
|
382
|
-
throw new Error("Attempted to get kv key for sqlite storage");
|
|
383
|
-
}
|
|
384
|
-
return `${KV_STORAGE_PREFIX}::${room}`;
|
|
385
|
-
}
|
|
386
|
-
_getUserKey(room, connectionId) {
|
|
387
|
-
if (this._mode === "sqlite") {
|
|
388
|
-
throw new Error("Attempted to get kv key for sqlite storage");
|
|
389
|
-
}
|
|
390
|
-
return `${KV_USER_PREFIX}::${room}::${connectionId}`;
|
|
391
|
-
}
|
|
392
|
-
};
|
|
393
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
394
|
-
0 && (module.exports = {
|
|
395
|
-
PersistenceCloudflareTransactionalStorage
|
|
396
|
-
});
|