@pluv/persistence-redis 0.41.6 → 0.42.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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @pluv/persistence-redis@0.41.6 build /home/runner/work/pluv/pluv/packages/persistence-redis
2
+ > @pluv/persistence-redis@0.42.0 build /home/runner/work/pluv/pluv/packages/persistence-redis
3
3
  > tsup src/index.ts --format esm,cjs --dts
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -8,11 +8,11 @@
8
8
  CLI Target: es6
9
9
  ESM Build start
10
10
  CJS Build start
11
- ESM dist/index.mjs 3.37 KB
12
- ESM ⚡️ Build success in 58ms
13
- CJS dist/index.js 4.38 KB
14
- CJS ⚡️ Build success in 63ms
11
+ ESM dist/index.mjs 4.81 KB
12
+ ESM ⚡️ Build success in 73ms
13
+ CJS dist/index.js 5.83 KB
14
+ CJS ⚡️ Build success in 75ms
15
15
  DTS Build start
16
- DTS ⚡️ Build success in 4462ms
17
- DTS dist/index.d.mts 1.09 KB
18
- DTS dist/index.d.ts 1.09 KB
16
+ DTS ⚡️ Build success in 4694ms
17
+ DTS dist/index.d.mts 1.17 KB
18
+ DTS dist/index.d.ts 1.17 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @pluv/persistence-redis
2
2
 
3
+ ## 0.42.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 4ce32ee: **BREAKING** `Persistence.getUsers` now returns a map of connection id strings to user JSON objects instead of an array of user JSON objects. This change is intended to be internal only and generally non-breaking.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [cf1529a]
12
+ - @pluv/io@0.42.0
13
+ - @pluv/types@0.42.0
14
+
15
+ ## 0.41.7
16
+
17
+ ### Patch Changes
18
+
19
+ - @pluv/io@0.41.7
20
+ - @pluv/types@0.41.7
21
+
3
22
  ## 0.41.6
4
23
 
5
24
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -8,6 +8,7 @@ interface PersistenceRedisOptions {
8
8
  }
9
9
  declare class PersistenceRedis extends AbstractPersistence {
10
10
  private _client;
11
+ private _initialized;
11
12
  constructor(options: PersistenceRedisOptions);
12
13
  addUser(room: string, connectionId: string, user: JsonObject | null): Promise<void>;
13
14
  deleteStorageState(room: string): Promise<void>;
@@ -16,8 +17,9 @@ declare class PersistenceRedis extends AbstractPersistence {
16
17
  getStorageState(room: string): Promise<string | null>;
17
18
  setStorageState(room: string, state: string): Promise<void>;
18
19
  getUser(room: string, connectionId: string): Promise<JsonObject | null>;
19
- getUsers(room: string): Promise<readonly (JsonObject | null)[]>;
20
+ getUsers(room: string): Promise<Map<[connectionId: string][0], JsonObject | null>>;
20
21
  getUsersSize(room: string): Promise<number>;
22
+ initialize(roomContext: {}): this;
21
23
  private _getClusterKey;
22
24
  private _getRoomStorageKey;
23
25
  private _getRoomUsersKey;
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ interface PersistenceRedisOptions {
8
8
  }
9
9
  declare class PersistenceRedis extends AbstractPersistence {
10
10
  private _client;
11
+ private _initialized;
11
12
  constructor(options: PersistenceRedisOptions);
12
13
  addUser(room: string, connectionId: string, user: JsonObject | null): Promise<void>;
13
14
  deleteStorageState(room: string): Promise<void>;
@@ -16,8 +17,9 @@ declare class PersistenceRedis extends AbstractPersistence {
16
17
  getStorageState(room: string): Promise<string | null>;
17
18
  setStorageState(room: string, state: string): Promise<void>;
18
19
  getUser(room: string, connectionId: string): Promise<JsonObject | null>;
19
- getUsers(room: string): Promise<readonly (JsonObject | null)[]>;
20
+ getUsers(room: string): Promise<Map<[connectionId: string][0], JsonObject | null>>;
20
21
  getUsersSize(room: string): Promise<number>;
22
+ initialize(roomContext: {}): this;
21
23
  private _getClusterKey;
22
24
  private _getRoomStorageKey;
23
25
  private _getRoomUsersKey;
package/dist/index.js CHANGED
@@ -46,26 +46,44 @@ module.exports = __toCommonJS(index_exports);
46
46
 
47
47
  // src/PersistenceRedis.ts
48
48
  var import_io = require("@pluv/io");
49
+
50
+ // src/utils/partitionByLength.ts
51
+ var partitionByLength = (arr, length) => {
52
+ if (!arr.length) return [];
53
+ const head = arr.slice(0, length);
54
+ const tail = arr.slice(length);
55
+ return [head, ...partitionByLength(tail, length)];
56
+ };
57
+
58
+ // src/PersistenceRedis.ts
49
59
  var DELIMITER = ":::";
50
60
  var HASH_TAG = "{PluvPersistenceRedis}";
61
+ var GET_USERS_CONCURRENCY = 4;
51
62
  var PersistenceRedis = class extends import_io.AbstractPersistence {
52
63
  constructor(options) {
53
64
  super();
65
+ this._initialized = null;
54
66
  const { client } = options;
55
67
  this._client = client;
56
68
  }
57
69
  addUser(room, connectionId, user) {
58
70
  return __async(this, null, function* () {
71
+ if (!this._initialized) return;
72
+ yield this._initialized;
59
73
  yield this._client.multi().sadd(this._getRoomUsersKey(room), connectionId).set(this._getRoomUsersUserKey(room, connectionId), JSON.stringify(user)).exec();
60
74
  });
61
75
  }
62
76
  deleteStorageState(room) {
63
77
  return __async(this, null, function* () {
78
+ if (!this._initialized) return;
79
+ yield this._initialized;
64
80
  yield this._client.del(this._getRoomStorageKey(room));
65
81
  });
66
82
  }
67
83
  deleteUser(room, connectionId) {
68
84
  return __async(this, null, function* () {
85
+ if (!this._initialized) return;
86
+ yield this._initialized;
69
87
  const size = yield this.getUsersSize(room);
70
88
  let multi = this._client.multi().srem(this._getRoomUsersKey(room), connectionId).del(this._getRoomUsersUserKey(room, connectionId));
71
89
  if (size === 1) {
@@ -76,6 +94,8 @@ var PersistenceRedis = class extends import_io.AbstractPersistence {
76
94
  }
77
95
  deleteUsers(room) {
78
96
  return __async(this, null, function* () {
97
+ if (!this._initialized) return;
98
+ yield this._initialized;
79
99
  const members = yield this._client.smembers(this._getRoomUsersKey(room));
80
100
  yield members.reduce(
81
101
  (commands, connectionId) => {
@@ -87,31 +107,53 @@ var PersistenceRedis = class extends import_io.AbstractPersistence {
87
107
  }
88
108
  getStorageState(room) {
89
109
  return __async(this, null, function* () {
110
+ if (!this._initialized) return null;
111
+ yield this._initialized;
90
112
  return yield this._client.get(this._getRoomStorageKey(room));
91
113
  });
92
114
  }
93
115
  setStorageState(room, state) {
94
116
  return __async(this, null, function* () {
117
+ if (!this._initialized) return;
118
+ yield this._initialized;
95
119
  yield this._client.set(this._getRoomStorageKey(room), state);
96
120
  });
97
121
  }
98
122
  getUser(room, connectionId) {
99
123
  return __async(this, null, function* () {
124
+ if (!this._initialized) return null;
125
+ yield this._initialized;
100
126
  const data = yield this._client.get(this._getRoomUsersUserKey(room, connectionId));
101
127
  return !data ? null : JSON.parse(data);
102
128
  });
103
129
  }
104
130
  getUsers(room) {
105
131
  return __async(this, null, function* () {
132
+ if (!this._initialized) return /* @__PURE__ */ new Map();
133
+ yield this._initialized;
106
134
  const members = yield this._client.smembers(this._getRoomUsersKey(room));
107
- return yield Promise.all(members.map((connectionId) => this.getUser(room, connectionId)));
135
+ const partitions = partitionByLength(members, GET_USERS_CONCURRENCY);
136
+ return yield partitions.reduce((promise, partition) => __async(this, null, function* () {
137
+ const map = yield promise;
138
+ const promises = partition.map((connectionId) => __async(this, null, function* () {
139
+ map.set(connectionId, yield this.getUser(room, connectionId));
140
+ }));
141
+ yield Promise.all(promises);
142
+ return map;
143
+ }), Promise.resolve(/* @__PURE__ */ new Map()));
108
144
  });
109
145
  }
110
146
  getUsersSize(room) {
111
147
  return __async(this, null, function* () {
148
+ if (!this._initialized) return 0;
149
+ yield this._initialized;
112
150
  return yield this._client.scard(this._getRoomUsersKey(room));
113
151
  });
114
152
  }
153
+ initialize(roomContext) {
154
+ this._initialized = Promise.resolve(true);
155
+ return this;
156
+ }
115
157
  _getClusterKey(key) {
116
158
  return `${HASH_TAG}${key}`;
117
159
  }
package/dist/index.mjs CHANGED
@@ -21,26 +21,44 @@ var __async = (__this, __arguments, generator) => {
21
21
 
22
22
  // src/PersistenceRedis.ts
23
23
  import { AbstractPersistence } from "@pluv/io";
24
+
25
+ // src/utils/partitionByLength.ts
26
+ var partitionByLength = (arr, length) => {
27
+ if (!arr.length) return [];
28
+ const head = arr.slice(0, length);
29
+ const tail = arr.slice(length);
30
+ return [head, ...partitionByLength(tail, length)];
31
+ };
32
+
33
+ // src/PersistenceRedis.ts
24
34
  var DELIMITER = ":::";
25
35
  var HASH_TAG = "{PluvPersistenceRedis}";
36
+ var GET_USERS_CONCURRENCY = 4;
26
37
  var PersistenceRedis = class extends AbstractPersistence {
27
38
  constructor(options) {
28
39
  super();
40
+ this._initialized = null;
29
41
  const { client } = options;
30
42
  this._client = client;
31
43
  }
32
44
  addUser(room, connectionId, user) {
33
45
  return __async(this, null, function* () {
46
+ if (!this._initialized) return;
47
+ yield this._initialized;
34
48
  yield this._client.multi().sadd(this._getRoomUsersKey(room), connectionId).set(this._getRoomUsersUserKey(room, connectionId), JSON.stringify(user)).exec();
35
49
  });
36
50
  }
37
51
  deleteStorageState(room) {
38
52
  return __async(this, null, function* () {
53
+ if (!this._initialized) return;
54
+ yield this._initialized;
39
55
  yield this._client.del(this._getRoomStorageKey(room));
40
56
  });
41
57
  }
42
58
  deleteUser(room, connectionId) {
43
59
  return __async(this, null, function* () {
60
+ if (!this._initialized) return;
61
+ yield this._initialized;
44
62
  const size = yield this.getUsersSize(room);
45
63
  let multi = this._client.multi().srem(this._getRoomUsersKey(room), connectionId).del(this._getRoomUsersUserKey(room, connectionId));
46
64
  if (size === 1) {
@@ -51,6 +69,8 @@ var PersistenceRedis = class extends AbstractPersistence {
51
69
  }
52
70
  deleteUsers(room) {
53
71
  return __async(this, null, function* () {
72
+ if (!this._initialized) return;
73
+ yield this._initialized;
54
74
  const members = yield this._client.smembers(this._getRoomUsersKey(room));
55
75
  yield members.reduce(
56
76
  (commands, connectionId) => {
@@ -62,31 +82,53 @@ var PersistenceRedis = class extends AbstractPersistence {
62
82
  }
63
83
  getStorageState(room) {
64
84
  return __async(this, null, function* () {
85
+ if (!this._initialized) return null;
86
+ yield this._initialized;
65
87
  return yield this._client.get(this._getRoomStorageKey(room));
66
88
  });
67
89
  }
68
90
  setStorageState(room, state) {
69
91
  return __async(this, null, function* () {
92
+ if (!this._initialized) return;
93
+ yield this._initialized;
70
94
  yield this._client.set(this._getRoomStorageKey(room), state);
71
95
  });
72
96
  }
73
97
  getUser(room, connectionId) {
74
98
  return __async(this, null, function* () {
99
+ if (!this._initialized) return null;
100
+ yield this._initialized;
75
101
  const data = yield this._client.get(this._getRoomUsersUserKey(room, connectionId));
76
102
  return !data ? null : JSON.parse(data);
77
103
  });
78
104
  }
79
105
  getUsers(room) {
80
106
  return __async(this, null, function* () {
107
+ if (!this._initialized) return /* @__PURE__ */ new Map();
108
+ yield this._initialized;
81
109
  const members = yield this._client.smembers(this._getRoomUsersKey(room));
82
- return yield Promise.all(members.map((connectionId) => this.getUser(room, connectionId)));
110
+ const partitions = partitionByLength(members, GET_USERS_CONCURRENCY);
111
+ return yield partitions.reduce((promise, partition) => __async(this, null, function* () {
112
+ const map = yield promise;
113
+ const promises = partition.map((connectionId) => __async(this, null, function* () {
114
+ map.set(connectionId, yield this.getUser(room, connectionId));
115
+ }));
116
+ yield Promise.all(promises);
117
+ return map;
118
+ }), Promise.resolve(/* @__PURE__ */ new Map()));
83
119
  });
84
120
  }
85
121
  getUsersSize(room) {
86
122
  return __async(this, null, function* () {
123
+ if (!this._initialized) return 0;
124
+ yield this._initialized;
87
125
  return yield this._client.scard(this._getRoomUsersKey(room));
88
126
  });
89
127
  }
128
+ initialize(roomContext) {
129
+ this._initialized = Promise.resolve(true);
130
+ return this;
131
+ }
90
132
  _getClusterKey(key) {
91
133
  return `${HASH_TAG}${key}`;
92
134
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/persistence-redis",
3
- "version": "0.41.6",
3
+ "version": "0.42.0",
4
4
  "description": "@pluv/io crdt storage persistence for node.js",
5
5
  "author": "leedavidcs",
6
6
  "license": "MIT",
@@ -17,20 +17,20 @@
17
17
  "access": "public"
18
18
  },
19
19
  "dependencies": {
20
- "@types/node": "^22.14.1",
21
- "@pluv/io": "^0.41.6",
22
- "@pluv/types": "^0.41.6"
20
+ "@types/node": "^22.15.2",
21
+ "@pluv/io": "^0.42.0",
22
+ "@pluv/types": "^0.42.0"
23
23
  },
24
24
  "peerDependencies": {
25
25
  "ioredis": "^5.0.0"
26
26
  },
27
27
  "devDependencies": {
28
- "eslint": "^9.25.0",
28
+ "eslint": "^9.25.1",
29
29
  "ioredis": "^5.6.1",
30
30
  "tsup": "^8.4.0",
31
31
  "typescript": "^5.8.3",
32
- "@pluv/tsconfig": "^0.41.6",
33
- "eslint-config-pluv": "^0.41.6"
32
+ "@pluv/tsconfig": "^0.42.0",
33
+ "eslint-config-pluv": "^0.42.0"
34
34
  },
35
35
  "scripts": {
36
36
  "build": "tsup src/index.ts --format esm,cjs --dts",
@@ -1,10 +1,13 @@
1
1
  import { AbstractPersistence } from "@pluv/io";
2
2
  import type { JsonObject } from "@pluv/types";
3
3
  import type { Cluster, Redis } from "ioredis";
4
+ import { partitionByLength } from "./utils";
4
5
 
5
6
  const DELIMITER = ":::";
6
7
  const HASH_TAG = "{PluvPersistenceRedis}";
7
8
 
9
+ const GET_USERS_CONCURRENCY = 4;
10
+
8
11
  export type RedisClient = Cluster | Redis;
9
12
 
10
13
  export interface PersistenceRedisOptions {
@@ -13,6 +16,7 @@ export interface PersistenceRedisOptions {
13
16
 
14
17
  export class PersistenceRedis extends AbstractPersistence {
15
18
  private _client: RedisClient;
19
+ private _initialized: Promise<true> | null = null;
16
20
 
17
21
  constructor(options: PersistenceRedisOptions) {
18
22
  super();
@@ -23,6 +27,10 @@ export class PersistenceRedis extends AbstractPersistence {
23
27
  }
24
28
 
25
29
  public async addUser(room: string, connectionId: string, user: JsonObject | null): Promise<void> {
30
+ if (!this._initialized) return;
31
+
32
+ await this._initialized;
33
+
26
34
  await this._client
27
35
  .multi()
28
36
  .sadd(this._getRoomUsersKey(room), connectionId)
@@ -31,10 +39,18 @@ export class PersistenceRedis extends AbstractPersistence {
31
39
  }
32
40
 
33
41
  public async deleteStorageState(room: string): Promise<void> {
42
+ if (!this._initialized) return;
43
+
44
+ await this._initialized;
45
+
34
46
  await this._client.del(this._getRoomStorageKey(room));
35
47
  }
36
48
 
37
49
  public async deleteUser(room: string, connectionId: string): Promise<void> {
50
+ if (!this._initialized) return;
51
+
52
+ await this._initialized;
53
+
38
54
  const size = await this.getUsersSize(room);
39
55
 
40
56
  let multi = this._client
@@ -50,6 +66,10 @@ export class PersistenceRedis extends AbstractPersistence {
50
66
  }
51
67
 
52
68
  public async deleteUsers(room: string): Promise<void> {
69
+ if (!this._initialized) return;
70
+
71
+ await this._initialized;
72
+
53
73
  const members = await this._client.smembers(this._getRoomUsersKey(room));
54
74
 
55
75
  await members
@@ -63,29 +83,66 @@ export class PersistenceRedis extends AbstractPersistence {
63
83
  }
64
84
 
65
85
  public async getStorageState(room: string): Promise<string | null> {
86
+ if (!this._initialized) return null;
87
+
88
+ await this._initialized;
89
+
66
90
  return await this._client.get(this._getRoomStorageKey(room));
67
91
  }
68
92
 
69
93
  public async setStorageState(room: string, state: string): Promise<void> {
94
+ if (!this._initialized) return;
95
+
96
+ await this._initialized;
97
+
70
98
  await this._client.set(this._getRoomStorageKey(room), state);
71
99
  }
72
100
 
73
101
  public async getUser(room: string, connectionId: string): Promise<JsonObject | null> {
102
+ if (!this._initialized) return null;
103
+
104
+ await this._initialized;
105
+
74
106
  const data = await this._client.get(this._getRoomUsersUserKey(room, connectionId));
75
107
 
76
108
  return !data ? null : JSON.parse(data);
77
109
  }
78
110
 
79
- public async getUsers(room: string): Promise<readonly (JsonObject | null)[]> {
111
+ public async getUsers(room: string): Promise<Map<[connectionId: string][0], JsonObject | null>> {
112
+ if (!this._initialized) return new Map();
113
+
114
+ await this._initialized;
115
+
80
116
  const members = await this._client.smembers(this._getRoomUsersKey(room));
117
+ const partitions = partitionByLength(members, GET_USERS_CONCURRENCY);
118
+
119
+ return await partitions.reduce(async (promise, partition) => {
120
+ const map = await promise;
121
+
122
+ const promises = partition.map(async (connectionId) => {
123
+ map.set(connectionId, await this.getUser(room, connectionId));
124
+ });
81
125
 
82
- return await Promise.all(members.map((connectionId) => this.getUser(room, connectionId)));
126
+ await Promise.all(promises);
127
+
128
+ return map;
129
+ }, Promise.resolve(new Map<string, JsonObject | null>()));
83
130
  }
84
131
 
85
132
  public async getUsersSize(room: string): Promise<number> {
133
+ if (!this._initialized) return 0;
134
+
135
+ await this._initialized;
136
+
86
137
  return await this._client.scard(this._getRoomUsersKey(room));
87
138
  }
88
139
 
140
+ public initialize(roomContext: {}): this {
141
+ this._initialized = Promise.resolve(true as const);
142
+
143
+ return this;
144
+ }
145
+
89
146
  private _getClusterKey(key: string): string {
90
147
  return `${HASH_TAG}${key}`;
91
148
  }
@@ -0,0 +1 @@
1
+ export { partitionByLength } from "./partitionByLength";
@@ -0,0 +1,8 @@
1
+ export const partitionByLength = <T>(arr: readonly T[] | T[], length: number): readonly (readonly T[])[] => {
2
+ if (!arr.length) return [];
3
+
4
+ const head = arr.slice(0, length);
5
+ const tail = arr.slice(length);
6
+
7
+ return [head, ...partitionByLength(tail, length)];
8
+ };