@inline-chat/realtime-sdk 0.0.1
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/dist/ids.d.ts +6 -0
- package/dist/ids.js +17 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +7 -0
- package/dist/realtime/mock-transport.d.ts +17 -0
- package/dist/realtime/mock-transport.js +41 -0
- package/dist/realtime/ping-pong.d.ts +26 -0
- package/dist/realtime/ping-pong.js +105 -0
- package/dist/realtime/protocol-client.d.ts +66 -0
- package/dist/realtime/protocol-client.js +277 -0
- package/dist/realtime/transport.d.ts +16 -0
- package/dist/realtime/transport.js +9 -0
- package/dist/realtime/types.d.ts +31 -0
- package/dist/realtime/types.js +1 -0
- package/dist/realtime/ws-transport.d.ts +36 -0
- package/dist/realtime/ws-transport.js +147 -0
- package/dist/sdk/inline-sdk-client.d.ts +79 -0
- package/dist/sdk/inline-sdk-client.js +541 -0
- package/dist/sdk/logger.d.ts +7 -0
- package/dist/sdk/logger.js +1 -0
- package/dist/sdk/sdk-version.d.ts +1 -0
- package/dist/sdk/sdk-version.js +24 -0
- package/dist/sdk/types.d.ts +177 -0
- package/dist/sdk/types.js +76 -0
- package/dist/state/json-file-state-store.d.ts +7 -0
- package/dist/state/json-file-state-store.js +25 -0
- package/dist/state/serde.d.ts +3 -0
- package/dist/state/serde.js +37 -0
- package/dist/time.d.ts +6 -0
- package/dist/time.js +17 -0
- package/dist/utils/async-channel.d.ts +8 -0
- package/dist/utils/async-channel.js +41 -0
- package/package.json +36 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { Message, Peer, Reaction, RpcCall, RpcResult, Update, UpdateBucket, UpdatesPayload } from "@inline-chat/protocol/core";
|
|
2
|
+
import type { InlineId } from "../ids.js";
|
|
3
|
+
import type { InlineUnixSeconds } from "../time.js";
|
|
4
|
+
import type { InlineSdkLogger } from "./logger.js";
|
|
5
|
+
import type { Transport } from "../realtime/transport.js";
|
|
6
|
+
export type InlineSdkClientOptions = {
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
token: string;
|
|
9
|
+
logger?: InlineSdkLogger;
|
|
10
|
+
state?: InlineSdkStateStore;
|
|
11
|
+
transport?: Transport;
|
|
12
|
+
};
|
|
13
|
+
export type InlineInboundEvent = {
|
|
14
|
+
kind: "message.new";
|
|
15
|
+
chatId: InlineId;
|
|
16
|
+
message: Message;
|
|
17
|
+
seq: number;
|
|
18
|
+
date: InlineUnixSeconds;
|
|
19
|
+
} | {
|
|
20
|
+
kind: "message.edit";
|
|
21
|
+
chatId: InlineId;
|
|
22
|
+
message: Message;
|
|
23
|
+
seq: number;
|
|
24
|
+
date: InlineUnixSeconds;
|
|
25
|
+
} | {
|
|
26
|
+
kind: "message.delete";
|
|
27
|
+
chatId: InlineId;
|
|
28
|
+
messageIds: InlineId[];
|
|
29
|
+
seq: number;
|
|
30
|
+
date: InlineUnixSeconds;
|
|
31
|
+
} | {
|
|
32
|
+
kind: "reaction.add";
|
|
33
|
+
chatId: InlineId;
|
|
34
|
+
reaction: Reaction;
|
|
35
|
+
seq: number;
|
|
36
|
+
date: InlineUnixSeconds;
|
|
37
|
+
} | {
|
|
38
|
+
kind: "reaction.delete";
|
|
39
|
+
chatId: InlineId;
|
|
40
|
+
emoji: string;
|
|
41
|
+
messageId: InlineId;
|
|
42
|
+
userId: InlineId;
|
|
43
|
+
seq: number;
|
|
44
|
+
date: InlineUnixSeconds;
|
|
45
|
+
} | {
|
|
46
|
+
kind: "chat.hasUpdates";
|
|
47
|
+
chatId: InlineId;
|
|
48
|
+
seq: number;
|
|
49
|
+
date: InlineUnixSeconds;
|
|
50
|
+
} | {
|
|
51
|
+
kind: "space.hasUpdates";
|
|
52
|
+
spaceId: InlineId;
|
|
53
|
+
seq: number;
|
|
54
|
+
date: InlineUnixSeconds;
|
|
55
|
+
};
|
|
56
|
+
export type InlineSdkState = {
|
|
57
|
+
version: 1;
|
|
58
|
+
dateCursor?: InlineUnixSeconds;
|
|
59
|
+
lastSeqByChatId?: Record<string, number>;
|
|
60
|
+
};
|
|
61
|
+
export interface InlineSdkStateStore {
|
|
62
|
+
load(): Promise<InlineSdkState | null>;
|
|
63
|
+
save(next: InlineSdkState): Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
export type RpcInputKind = RpcCall["input"]["oneofKind"];
|
|
66
|
+
export type RpcResultKind = RpcResult["result"]["oneofKind"];
|
|
67
|
+
export declare const rpcInputKindByMethod: {
|
|
68
|
+
readonly 0: undefined;
|
|
69
|
+
readonly 1: "getMe";
|
|
70
|
+
readonly 2: "sendMessage";
|
|
71
|
+
readonly 3: "getPeerPhoto";
|
|
72
|
+
readonly 4: "deleteMessages";
|
|
73
|
+
readonly 5: "getChatHistory";
|
|
74
|
+
readonly 6: "addReaction";
|
|
75
|
+
readonly 7: "deleteReaction";
|
|
76
|
+
readonly 8: "editMessage";
|
|
77
|
+
readonly 9: "createChat";
|
|
78
|
+
readonly 10: "getSpaceMembers";
|
|
79
|
+
readonly 11: "deleteChat";
|
|
80
|
+
readonly 12: "inviteToSpace";
|
|
81
|
+
readonly 13: "getChatParticipants";
|
|
82
|
+
readonly 14: "addChatParticipant";
|
|
83
|
+
readonly 15: "removeChatParticipant";
|
|
84
|
+
readonly 16: "translateMessages";
|
|
85
|
+
readonly 17: "getChats";
|
|
86
|
+
readonly 18: "updateUserSettings";
|
|
87
|
+
readonly 19: "getUserSettings";
|
|
88
|
+
readonly 20: "sendComposeAction";
|
|
89
|
+
readonly 21: "createBot";
|
|
90
|
+
readonly 22: "deleteMember";
|
|
91
|
+
readonly 23: "markAsUnread";
|
|
92
|
+
readonly 24: "getUpdatesState";
|
|
93
|
+
readonly 25: "getChat";
|
|
94
|
+
readonly 26: "getUpdates";
|
|
95
|
+
readonly 27: "updateMemberAccess";
|
|
96
|
+
readonly 28: "searchMessages";
|
|
97
|
+
readonly 29: "forwardMessages";
|
|
98
|
+
readonly 30: "updateChatVisibility";
|
|
99
|
+
readonly 31: "pinMessage";
|
|
100
|
+
readonly 32: "updateChatInfo";
|
|
101
|
+
readonly 33: "listBots";
|
|
102
|
+
readonly 34: "revealBotToken";
|
|
103
|
+
readonly 35: "moveThread";
|
|
104
|
+
};
|
|
105
|
+
export declare const rpcResultKindByMethod: {
|
|
106
|
+
readonly 0: undefined;
|
|
107
|
+
readonly 1: "getMe";
|
|
108
|
+
readonly 2: "sendMessage";
|
|
109
|
+
readonly 3: "getPeerPhoto";
|
|
110
|
+
readonly 4: "deleteMessages";
|
|
111
|
+
readonly 5: "getChatHistory";
|
|
112
|
+
readonly 6: "addReaction";
|
|
113
|
+
readonly 7: "deleteReaction";
|
|
114
|
+
readonly 8: "editMessage";
|
|
115
|
+
readonly 9: "createChat";
|
|
116
|
+
readonly 10: "getSpaceMembers";
|
|
117
|
+
readonly 11: "deleteChat";
|
|
118
|
+
readonly 12: "inviteToSpace";
|
|
119
|
+
readonly 13: "getChatParticipants";
|
|
120
|
+
readonly 14: "addChatParticipant";
|
|
121
|
+
readonly 15: "removeChatParticipant";
|
|
122
|
+
readonly 16: "translateMessages";
|
|
123
|
+
readonly 17: "getChats";
|
|
124
|
+
readonly 18: "updateUserSettings";
|
|
125
|
+
readonly 19: "getUserSettings";
|
|
126
|
+
readonly 20: "sendComposeAction";
|
|
127
|
+
readonly 21: "createBot";
|
|
128
|
+
readonly 22: "deleteMember";
|
|
129
|
+
readonly 23: "markAsUnread";
|
|
130
|
+
readonly 24: "getUpdatesState";
|
|
131
|
+
readonly 25: "getChat";
|
|
132
|
+
readonly 26: "getUpdates";
|
|
133
|
+
readonly 27: "updateMemberAccess";
|
|
134
|
+
readonly 28: "searchMessages";
|
|
135
|
+
readonly 29: "forwardMessages";
|
|
136
|
+
readonly 30: "updateChatVisibility";
|
|
137
|
+
readonly 31: "pinMessage";
|
|
138
|
+
readonly 32: "updateChatInfo";
|
|
139
|
+
readonly 33: "listBots";
|
|
140
|
+
readonly 34: "revealBotToken";
|
|
141
|
+
readonly 35: "moveThread";
|
|
142
|
+
};
|
|
143
|
+
type RpcInputKindByMethod = typeof rpcInputKindByMethod;
|
|
144
|
+
type RpcResultKindByMethod = typeof rpcResultKindByMethod;
|
|
145
|
+
export type MappedMethod = keyof RpcInputKindByMethod & keyof RpcResultKindByMethod;
|
|
146
|
+
export type RpcInputForMethod<M extends MappedMethod> = RpcInputKindByMethod[M] extends RpcInputKind ? Extract<RpcCall["input"], {
|
|
147
|
+
oneofKind: RpcInputKindByMethod[M];
|
|
148
|
+
}> : Extract<RpcCall["input"], {
|
|
149
|
+
oneofKind: undefined;
|
|
150
|
+
}>;
|
|
151
|
+
export type RpcResultForMethod<M extends MappedMethod> = RpcResultKindByMethod[M] extends RpcResultKind ? Extract<RpcResult["result"], {
|
|
152
|
+
oneofKind: RpcResultKindByMethod[M];
|
|
153
|
+
}> : Extract<RpcResult["result"], {
|
|
154
|
+
oneofKind: undefined;
|
|
155
|
+
}>;
|
|
156
|
+
export type RawUpdatesEvent = {
|
|
157
|
+
updates: UpdatesPayload;
|
|
158
|
+
};
|
|
159
|
+
export type UpdateHandlerContext = {
|
|
160
|
+
emit: (event: InlineInboundEvent) => Promise<void>;
|
|
161
|
+
catchUpChat?: (params: {
|
|
162
|
+
chatId: InlineId;
|
|
163
|
+
peer?: Peer;
|
|
164
|
+
updateSeq: number;
|
|
165
|
+
update: Update;
|
|
166
|
+
}) => Promise<void>;
|
|
167
|
+
catchUpSpace?: (params: {
|
|
168
|
+
spaceId: InlineId;
|
|
169
|
+
updateSeq: number;
|
|
170
|
+
update: Update;
|
|
171
|
+
}) => Promise<void>;
|
|
172
|
+
updateBucketForChat: (params: {
|
|
173
|
+
chatId: InlineId;
|
|
174
|
+
peer?: Peer;
|
|
175
|
+
}) => UpdateBucket;
|
|
176
|
+
};
|
|
177
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export const rpcInputKindByMethod = {
|
|
2
|
+
0: undefined, // UNSPECIFIED
|
|
3
|
+
1: "getMe",
|
|
4
|
+
2: "sendMessage",
|
|
5
|
+
3: "getPeerPhoto",
|
|
6
|
+
4: "deleteMessages",
|
|
7
|
+
5: "getChatHistory",
|
|
8
|
+
6: "addReaction",
|
|
9
|
+
7: "deleteReaction",
|
|
10
|
+
8: "editMessage",
|
|
11
|
+
9: "createChat",
|
|
12
|
+
10: "getSpaceMembers",
|
|
13
|
+
11: "deleteChat",
|
|
14
|
+
12: "inviteToSpace",
|
|
15
|
+
13: "getChatParticipants",
|
|
16
|
+
14: "addChatParticipant",
|
|
17
|
+
15: "removeChatParticipant",
|
|
18
|
+
16: "translateMessages",
|
|
19
|
+
17: "getChats",
|
|
20
|
+
18: "updateUserSettings",
|
|
21
|
+
19: "getUserSettings",
|
|
22
|
+
20: "sendComposeAction",
|
|
23
|
+
21: "createBot",
|
|
24
|
+
22: "deleteMember",
|
|
25
|
+
23: "markAsUnread",
|
|
26
|
+
24: "getUpdatesState",
|
|
27
|
+
25: "getChat",
|
|
28
|
+
26: "getUpdates",
|
|
29
|
+
27: "updateMemberAccess",
|
|
30
|
+
28: "searchMessages",
|
|
31
|
+
29: "forwardMessages",
|
|
32
|
+
30: "updateChatVisibility",
|
|
33
|
+
31: "pinMessage",
|
|
34
|
+
32: "updateChatInfo",
|
|
35
|
+
33: "listBots",
|
|
36
|
+
34: "revealBotToken",
|
|
37
|
+
35: "moveThread",
|
|
38
|
+
};
|
|
39
|
+
export const rpcResultKindByMethod = {
|
|
40
|
+
0: undefined, // UNSPECIFIED
|
|
41
|
+
1: "getMe",
|
|
42
|
+
2: "sendMessage",
|
|
43
|
+
3: "getPeerPhoto",
|
|
44
|
+
4: "deleteMessages",
|
|
45
|
+
5: "getChatHistory",
|
|
46
|
+
6: "addReaction",
|
|
47
|
+
7: "deleteReaction",
|
|
48
|
+
8: "editMessage",
|
|
49
|
+
9: "createChat",
|
|
50
|
+
10: "getSpaceMembers",
|
|
51
|
+
11: "deleteChat",
|
|
52
|
+
12: "inviteToSpace",
|
|
53
|
+
13: "getChatParticipants",
|
|
54
|
+
14: "addChatParticipant",
|
|
55
|
+
15: "removeChatParticipant",
|
|
56
|
+
16: "translateMessages",
|
|
57
|
+
17: "getChats",
|
|
58
|
+
18: "updateUserSettings",
|
|
59
|
+
19: "getUserSettings",
|
|
60
|
+
20: "sendComposeAction",
|
|
61
|
+
21: "createBot",
|
|
62
|
+
22: "deleteMember",
|
|
63
|
+
23: "markAsUnread",
|
|
64
|
+
24: "getUpdatesState",
|
|
65
|
+
25: "getChat",
|
|
66
|
+
26: "getUpdates",
|
|
67
|
+
27: "updateMemberAccess",
|
|
68
|
+
28: "searchMessages",
|
|
69
|
+
29: "forwardMessages",
|
|
70
|
+
30: "updateChatVisibility",
|
|
71
|
+
31: "pinMessage",
|
|
72
|
+
32: "updateChatInfo",
|
|
73
|
+
33: "listBots",
|
|
74
|
+
34: "revealBotToken",
|
|
75
|
+
35: "moveThread",
|
|
76
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { InlineSdkState, InlineSdkStateStore } from "../sdk/types.js";
|
|
2
|
+
export declare class JsonFileStateStore implements InlineSdkStateStore {
|
|
3
|
+
private readonly path;
|
|
4
|
+
constructor(path: string);
|
|
5
|
+
load(): Promise<InlineSdkState | null>;
|
|
6
|
+
save(next: InlineSdkState): Promise<void>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { readFile, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import { deserializeStateV1, serializeStateV1 } from "./serde.js";
|
|
3
|
+
export class JsonFileStateStore {
|
|
4
|
+
path;
|
|
5
|
+
constructor(path) {
|
|
6
|
+
this.path = path;
|
|
7
|
+
}
|
|
8
|
+
async load() {
|
|
9
|
+
try {
|
|
10
|
+
const raw = await readFile(this.path, "utf8");
|
|
11
|
+
return deserializeStateV1(raw);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
// File missing or invalid: treat as no state.
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async save(next) {
|
|
19
|
+
const tempPath = `${this.path}.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
20
|
+
const payload = serializeStateV1(next);
|
|
21
|
+
await writeFile(tempPath, payload, "utf8");
|
|
22
|
+
// Best-effort atomic replace on most platforms (write temp in same directory, then rename).
|
|
23
|
+
await rename(tempPath, this.path);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export const serializeStateV1 = (state) => {
|
|
2
|
+
const json = {
|
|
3
|
+
version: 1,
|
|
4
|
+
...(state.dateCursor != null ? { dateCursor: state.dateCursor.toString() } : {}),
|
|
5
|
+
...(state.lastSeqByChatId != null ? { lastSeqByChatId: state.lastSeqByChatId } : {}),
|
|
6
|
+
};
|
|
7
|
+
return JSON.stringify(json, null, 2);
|
|
8
|
+
};
|
|
9
|
+
export const deserializeStateV1 = (raw) => {
|
|
10
|
+
const parsed = JSON.parse(raw);
|
|
11
|
+
if (!isStateJsonV1(parsed)) {
|
|
12
|
+
throw new Error("invalid state json");
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
version: 1,
|
|
16
|
+
...(parsed.dateCursor != null ? { dateCursor: BigInt(parsed.dateCursor) } : {}),
|
|
17
|
+
...(parsed.lastSeqByChatId != null ? { lastSeqByChatId: parsed.lastSeqByChatId } : {}),
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21
|
+
const isStateJsonV1 = (value) => {
|
|
22
|
+
if (!isRecord(value))
|
|
23
|
+
return false;
|
|
24
|
+
if (value.version !== 1)
|
|
25
|
+
return false;
|
|
26
|
+
if (value.dateCursor != null && typeof value.dateCursor !== "string")
|
|
27
|
+
return false;
|
|
28
|
+
if (value.lastSeqByChatId != null) {
|
|
29
|
+
if (!isRecord(value.lastSeqByChatId))
|
|
30
|
+
return false;
|
|
31
|
+
for (const v of Object.values(value.lastSeqByChatId)) {
|
|
32
|
+
if (typeof v !== "number" || !Number.isFinite(v))
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
};
|
package/dist/time.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type InlineUnixSeconds = bigint;
|
|
2
|
+
export type InlineUnixSecondsLike = InlineUnixSeconds | number;
|
|
3
|
+
export declare class InlineUnixSecondsError extends Error {
|
|
4
|
+
constructor(message: string);
|
|
5
|
+
}
|
|
6
|
+
export declare const asInlineUnixSeconds: (value: InlineUnixSecondsLike, fieldName?: string) => InlineUnixSeconds;
|
package/dist/time.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export class InlineUnixSecondsError extends Error {
|
|
2
|
+
constructor(message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = "InlineUnixSecondsError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export const asInlineUnixSeconds = (value, fieldName = "unixSeconds") => {
|
|
8
|
+
if (typeof value === "bigint")
|
|
9
|
+
return value;
|
|
10
|
+
if (typeof value !== "number") {
|
|
11
|
+
throw new InlineUnixSecondsError(`invalid ${fieldName}: expected number|bigint`);
|
|
12
|
+
}
|
|
13
|
+
if (!Number.isSafeInteger(value)) {
|
|
14
|
+
throw new InlineUnixSecondsError(`invalid ${fieldName}: number must be a safe integer`);
|
|
15
|
+
}
|
|
16
|
+
return BigInt(value);
|
|
17
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export class AsyncChannel {
|
|
2
|
+
queue = [];
|
|
3
|
+
resolvers = [];
|
|
4
|
+
closed = false;
|
|
5
|
+
async send(value) {
|
|
6
|
+
if (this.closed)
|
|
7
|
+
return;
|
|
8
|
+
const resolver = this.resolvers.shift();
|
|
9
|
+
if (resolver) {
|
|
10
|
+
resolver({ value, done: false });
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
this.queue.push(value);
|
|
14
|
+
}
|
|
15
|
+
close() {
|
|
16
|
+
if (this.closed)
|
|
17
|
+
return;
|
|
18
|
+
this.closed = true;
|
|
19
|
+
for (const resolver of this.resolvers) {
|
|
20
|
+
resolver({ value: undefined, done: true });
|
|
21
|
+
}
|
|
22
|
+
this.resolvers = [];
|
|
23
|
+
this.queue = [];
|
|
24
|
+
}
|
|
25
|
+
[Symbol.asyncIterator]() {
|
|
26
|
+
return {
|
|
27
|
+
next: () => {
|
|
28
|
+
if (this.queue.length > 0) {
|
|
29
|
+
const value = this.queue.shift();
|
|
30
|
+
return Promise.resolve({ value, done: false });
|
|
31
|
+
}
|
|
32
|
+
if (this.closed) {
|
|
33
|
+
return Promise.resolve({ value: undefined, done: true });
|
|
34
|
+
}
|
|
35
|
+
return new Promise((resolve) => {
|
|
36
|
+
this.resolvers.push(resolve);
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@inline-chat/realtime-sdk",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc -p tsconfig.json",
|
|
19
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
20
|
+
"test": "vitest run --coverage"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@inline-chat/protocol": "^0.0.1",
|
|
24
|
+
"ws": "^8.18.3"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^20.11.30",
|
|
28
|
+
"@types/ws": "^8.5.12",
|
|
29
|
+
"@vitest/coverage-v8": "4.0.16",
|
|
30
|
+
"typescript": "^5.8.3",
|
|
31
|
+
"vitest": "^4.0.16"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
}
|
|
36
|
+
}
|