@protobase/client 0.59.1 → 0.59.2
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,29 @@
|
|
|
1
|
+
//#region src/channel-binary-frame.ts
|
|
2
|
+
const encoder = new TextEncoder();
|
|
3
|
+
const decoder = new TextDecoder();
|
|
4
|
+
const lengthBytes = 4;
|
|
5
|
+
/**
|
|
6
|
+
* A binary frame of `/api/channels`, which carries a broadcast of bytes: the byte length of a JSON header, the header,
|
|
7
|
+
* the message without its payload, then the payload's bytes.
|
|
8
|
+
*/
|
|
9
|
+
const encodeBinaryFrame = (header, bytes) => {
|
|
10
|
+
const head = encoder.encode(JSON.stringify(header));
|
|
11
|
+
const body = bytes instanceof ArrayBuffer ? new Uint8Array(bytes) : new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
12
|
+
const frame = new Uint8Array(lengthBytes + head.length + body.length);
|
|
13
|
+
new DataView(frame.buffer).setUint32(0, head.length);
|
|
14
|
+
frame.set(head, lengthBytes);
|
|
15
|
+
frame.set(body, lengthBytes + head.length);
|
|
16
|
+
return frame.buffer;
|
|
17
|
+
};
|
|
18
|
+
/** The message a binary frame from the server carries, its bytes as its `payload`. */
|
|
19
|
+
const decodeBinaryFrame = (frame) => {
|
|
20
|
+
const end = lengthBytes + new DataView(frame).getUint32(0);
|
|
21
|
+
return {
|
|
22
|
+
...JSON.parse(decoder.decode(new Uint8Array(frame, lengthBytes, end - lengthBytes))),
|
|
23
|
+
payload: frame.slice(end)
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
/** Whether a payload is bytes, sent as a binary frame. */
|
|
27
|
+
const isBytes = (payload) => payload instanceof ArrayBuffer || ArrayBuffer.isView(payload);
|
|
28
|
+
//#endregion
|
|
29
|
+
export { decodeBinaryFrame, encodeBinaryFrame, isBytes };
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
/** The part of the browser's `WebSocket` a connection uses, which a test hands its own of. */
|
|
3
3
|
export type SocketLike = {
|
|
4
4
|
readonly readyState: number;
|
|
5
|
-
|
|
5
|
+
binaryType: string;
|
|
6
|
+
send(data: string | ArrayBuffer): void;
|
|
6
7
|
close(code?: number): void;
|
|
7
8
|
onopen: ((event: unknown) => void) | null;
|
|
8
9
|
onmessage: ((event: {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { decodeBinaryFrame, encodeBinaryFrame } from "./channel-binary-frame.js";
|
|
1
2
|
import { ChannelRefusal } from "./channel-types.js";
|
|
2
3
|
//#region src/channel-connection.ts
|
|
3
4
|
/** How often the client pings, the server's `channelBounds.pingMs`; no pong in twice that drops the connection. */
|
|
@@ -38,6 +39,7 @@ const channelConnection = ({ url, token, socket: open, retryDelay, onOpen, onDow
|
|
|
38
39
|
sentToken = offered;
|
|
39
40
|
const current = open(url(), offered === void 0 ? [protocol] : [protocol, `bearer.${offered}`]);
|
|
40
41
|
socket = current;
|
|
42
|
+
current.binaryType = "arraybuffer";
|
|
41
43
|
current.onopen = () => {
|
|
42
44
|
failures = 0;
|
|
43
45
|
heardAt = Date.now();
|
|
@@ -50,8 +52,8 @@ const channelConnection = ({ url, token, socket: open, retryDelay, onOpen, onDow
|
|
|
50
52
|
};
|
|
51
53
|
current.onmessage = ({ data }) => {
|
|
52
54
|
heardAt = Date.now();
|
|
53
|
-
if (typeof data !== "string") return;
|
|
54
|
-
const message = JSON.parse(data);
|
|
55
|
+
if (typeof data !== "string" && !(data instanceof ArrayBuffer)) return;
|
|
56
|
+
const message = typeof data === "string" ? JSON.parse(data) : decodeBinaryFrame(data);
|
|
55
57
|
if (message.type !== "reply") return onMessage(message);
|
|
56
58
|
const waiting = pending.get(message.ref);
|
|
57
59
|
pending.delete(message.ref);
|
|
@@ -71,9 +73,9 @@ const channelConnection = ({ url, token, socket: open, retryDelay, onOpen, onDow
|
|
|
71
73
|
}, retryDelay(failures++));
|
|
72
74
|
};
|
|
73
75
|
};
|
|
74
|
-
const sendNow = (message) => {
|
|
76
|
+
const sendNow = (message, bytes) => {
|
|
75
77
|
if (socket?.readyState !== openState) return false;
|
|
76
|
-
socket.send(JSON.stringify(message));
|
|
78
|
+
socket.send(bytes === void 0 ? JSON.stringify(message) : encodeBinaryFrame(message, bytes));
|
|
77
79
|
return true;
|
|
78
80
|
};
|
|
79
81
|
return {
|
|
@@ -93,19 +95,19 @@ const channelConnection = ({ url, token, socket: open, retryDelay, onOpen, onDow
|
|
|
93
95
|
dropPending();
|
|
94
96
|
current?.close(1e3);
|
|
95
97
|
},
|
|
96
|
-
/** Sends a message with a ref, and resolves with its reply, or rejects with its refusal. */
|
|
97
|
-
request: (message) => new Promise((resolve, reject) => {
|
|
98
|
+
/** Sends a message with a ref, and bytes as its payload if any, and resolves with its reply, or rejects with its refusal. */
|
|
99
|
+
request: (message, bytes) => new Promise((resolve, reject) => {
|
|
98
100
|
const ref = String(++lastRef);
|
|
99
101
|
if (!sendNow({
|
|
100
102
|
...message,
|
|
101
103
|
ref
|
|
102
|
-
})) return reject(new ChannelRefusal("disconnected", "The connection is down"));
|
|
104
|
+
}, bytes)) return reject(new ChannelRefusal("disconnected", "The connection is down"));
|
|
103
105
|
pending.set(ref, {
|
|
104
106
|
resolve,
|
|
105
107
|
reject
|
|
106
108
|
});
|
|
107
109
|
}),
|
|
108
|
-
/** Sends a message that gets no reply; `false` when the socket is not open. */
|
|
110
|
+
/** Sends a message that gets no reply, and bytes as its payload if any; `false` when the socket is not open. */
|
|
109
111
|
post: sendNow
|
|
110
112
|
};
|
|
111
113
|
};
|
package/dist/channel-types.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export type ChannelError = {
|
|
|
18
18
|
code: string;
|
|
19
19
|
message: string;
|
|
20
20
|
};
|
|
21
|
-
/** A broadcast as a channel hears it
|
|
21
|
+
/** A broadcast as a channel hears it, its payload JSON or, for bytes, an `ArrayBuffer`: with `server`, the app's own code sent it, and `user` is `null`. */
|
|
22
22
|
export type ChannelMessage = {
|
|
23
23
|
event: string;
|
|
24
24
|
payload: unknown;
|
package/dist/channels.d.ts
CHANGED
|
@@ -14,8 +14,9 @@ export type Channel = {
|
|
|
14
14
|
track: (state: unknown) => Promise<void>;
|
|
15
15
|
untrack: () => Promise<void>;
|
|
16
16
|
/**
|
|
17
|
-
* Broadcasts to the others on the channel
|
|
18
|
-
*
|
|
17
|
+
* Broadcasts to the others on the channel: a JSON payload, or bytes in an `ArrayBuffer` or typed array, which the
|
|
18
|
+
* others hear as an `ArrayBuffer`. With `self` this connection hears it too. With `ack` it resolves once the server
|
|
19
|
+
* took it, or rejects with its `ChannelRefusal`; without, once it was sent.
|
|
19
20
|
*/
|
|
20
21
|
send: (event: string, payload: unknown, options?: {
|
|
21
22
|
self?: boolean;
|
package/dist/channels.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isBytes } from "./channel-binary-frame.js";
|
|
1
2
|
import { ChannelRefusal } from "./channel-types.js";
|
|
2
3
|
import { channelConnection } from "./channel-connection.js";
|
|
3
4
|
import { encodeKey } from "@protobase/schema/internal";
|
|
@@ -145,21 +146,22 @@ const channelsClient = (options) => {
|
|
|
145
146
|
send: async (event, payload, { self, ack } = {}) => {
|
|
146
147
|
const name = joinedName();
|
|
147
148
|
if (name === void 0) throw notJoined();
|
|
149
|
+
const bytes = isBytes(payload) ? payload : void 0;
|
|
148
150
|
const message = {
|
|
149
151
|
type: "send",
|
|
150
152
|
channel: name,
|
|
151
153
|
event,
|
|
152
|
-
payload,
|
|
154
|
+
...bytes === void 0 && { payload },
|
|
153
155
|
...self && { self }
|
|
154
156
|
};
|
|
155
157
|
if (ack) {
|
|
156
158
|
await connection.request({
|
|
157
159
|
...message,
|
|
158
160
|
ack
|
|
159
|
-
});
|
|
161
|
+
}, bytes);
|
|
160
162
|
return;
|
|
161
163
|
}
|
|
162
|
-
if (!connection.post(message)) throw new ChannelRefusal("disconnected", "The connection is down");
|
|
164
|
+
if (!connection.post(message, bytes)) throw new ChannelRefusal("disconnected", "The connection is down");
|
|
163
165
|
},
|
|
164
166
|
on: (event, listener) => {
|
|
165
167
|
const listeners = channel.eventListeners.get(event) ?? /* @__PURE__ */ new Set();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@protobase/client",
|
|
3
|
-
"version": "0.59.
|
|
3
|
+
"version": "0.59.2",
|
|
4
4
|
"description": "Typed fetch client for the Protobase REST API.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@better-auth/passkey": "^1.7.12",
|
|
34
|
-
"@protobase/layout": "0.59.
|
|
35
|
-
"@protobase/schema": "0.59.
|
|
34
|
+
"@protobase/layout": "0.59.2",
|
|
35
|
+
"@protobase/schema": "0.59.2",
|
|
36
36
|
"@simplewebauthn/browser": "^13.3.0",
|
|
37
37
|
"better-auth": "^1.7.12"
|
|
38
38
|
}
|