durablews 1.0.1 → 2.0.0-alpha.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/README.md +63 -93
- package/dist/binding-bZNBe2Bz.d.cts +11 -0
- package/dist/binding-gyPQiK9i.d.ts +11 -0
- package/dist/chunk-GIDTCSPD.js +22 -0
- package/dist/chunk-GIDTCSPD.js.map +1 -0
- package/dist/chunk-MG4TK53H.js +590 -0
- package/dist/chunk-MG4TK53H.js.map +1 -0
- package/dist/compat.cjs +765 -0
- package/dist/compat.cjs.map +1 -0
- package/dist/compat.d.cts +72 -0
- package/dist/compat.d.ts +72 -0
- package/dist/compat.js +177 -0
- package/dist/compat.js.map +1 -0
- package/dist/index.cjs +612 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +81 -0
- package/dist/index.d.ts +81 -44
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/react.cjs +646 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +66 -0
- package/dist/react.d.ts +66 -0
- package/dist/react.js +42 -0
- package/dist/react.js.map +1 -0
- package/dist/types-BDvztGcG.d.cts +410 -0
- package/dist/types-BDvztGcG.d.ts +410 -0
- package/dist/vue.cjs +650 -0
- package/dist/vue.cjs.map +1 -0
- package/dist/vue.d.cts +72 -0
- package/dist/vue.d.ts +72 -0
- package/dist/vue.js +43 -0
- package/dist/vue.js.map +1 -0
- package/package.json +128 -69
- package/dist/durablews.es.js +0 -120
- package/dist/durablews.umd.js +0 -1
package/README.md
CHANGED
|
@@ -1,122 +1,92 @@
|
|
|
1
|
-
# DurableWS
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
# DurableWS
|
|
2
|
+
|
|
3
|
+
> The WebSocket client that survives the real world — automatic reconnection,
|
|
4
|
+
> bounded queueing, and typed messages. Zero dependencies, every modern
|
|
5
|
+
> runtime, durable by default.
|
|
6
|
+
|
|
7
|
+
> ⚠️ **v2 is in alpha** (`npm install durablews@alpha`). The features below
|
|
8
|
+
> are built and tested (unit, integration, real-browser e2e); the API may
|
|
9
|
+
> still shift before 2.0. The
|
|
10
|
+
> [architecture RFC](https://github.com/imnsco/DurableWS/blob/main/rfcs/0001-v2-architecture.md)
|
|
11
|
+
> tracks design and status. The `1.x` release predates this rewrite — don't
|
|
12
|
+
> use it.
|
|
13
|
+
|
|
14
|
+
## What you get
|
|
15
|
+
|
|
16
|
+
- **Automatic reconnection, on by default** — full-jitter exponential
|
|
17
|
+
backoff, unlimited retries, a `shouldReconnect` veto, and `reconnecting`
|
|
18
|
+
events for your UI.
|
|
19
|
+
- **Message queueing while disconnected, on by default** — bounded,
|
|
20
|
+
drop-oldest, flushed in order on open. Every dropped message fires a `drop`
|
|
21
|
+
event; nothing is silently lost.
|
|
22
|
+
- **Opt-in heartbeat / idle detection** — quietly-dead links are closed (code
|
|
23
|
+
`4408`) and recovered through the normal reconnect machinery.
|
|
24
|
+
- **Typed + validated messages** — pass any
|
|
25
|
+
[Standard Schema](https://standardschema.dev) (zod, valibot, arktype, …)
|
|
26
|
+
and inbound messages are type-inferred *and* runtime-validated.
|
|
27
|
+
- **Middleware, inbound and outbound** — an onion pipeline with async-safe,
|
|
28
|
+
ordered outbound execution (auth/token-refresh ready).
|
|
29
|
+
- **Pluggable codec** — JSON by default; swap in msgpack or anything else.
|
|
30
|
+
- **Vue & React bindings in the box** — `durablews/vue` (composable) and
|
|
31
|
+
`durablews/react` (hook); the frameworks are optional peers.
|
|
32
|
+
- **A drop-in `WebSocket` class** — `durablews/compat` for one-line migration
|
|
33
|
+
or `webSocketImpl` injection, with a published known-deviations table.
|
|
34
|
+
- **Zero runtime dependencies**, built on the standard global `WebSocket`.
|
|
35
|
+
|
|
36
|
+
## Requirements
|
|
37
|
+
|
|
38
|
+
- **Node.js ≥ 22** — the first release where the global `WebSocket` is
|
|
39
|
+
available unflagged. (There is no `ws` dependency, by design.)
|
|
40
|
+
- Any modern runtime with a global `WebSocket`: current browsers, Deno, Bun,
|
|
41
|
+
and Cloudflare Workers.
|
|
11
42
|
|
|
12
43
|
## Installation
|
|
13
44
|
|
|
14
45
|
```bash
|
|
15
|
-
npm install durablews
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
or
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
yarn add durablews
|
|
46
|
+
npm install durablews@alpha
|
|
22
47
|
```
|
|
23
48
|
|
|
24
|
-
##
|
|
25
|
-
|
|
26
|
-
To get started with DurableWS, first import the client in your project:
|
|
49
|
+
## Quick start
|
|
27
50
|
|
|
28
51
|
```ts
|
|
29
52
|
import { defineClient } from "durablews";
|
|
30
|
-
```
|
|
31
53
|
|
|
32
|
-
|
|
54
|
+
const client = defineClient({ url: "wss://example.com/socket" });
|
|
33
55
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
url: "wss://your.websocket.server",
|
|
37
|
-
autoConnect: true, // Automatically connects
|
|
38
|
-
maxReconnectAttempts: 5,
|
|
39
|
-
maxQueueSize: 50,
|
|
40
|
-
idleTimeout: 300000, // 5 minutes
|
|
41
|
-
getToken: async () => {
|
|
42
|
-
const response = await fetch("/my/token/issuer");
|
|
43
|
-
const data = await response.json();
|
|
44
|
-
return data.token;
|
|
45
|
-
}
|
|
56
|
+
client.on("message", (data) => {
|
|
57
|
+
console.log("received:", data);
|
|
46
58
|
});
|
|
47
|
-
```
|
|
48
59
|
|
|
49
|
-
|
|
60
|
+
await client.connect();
|
|
61
|
+
client.send({ type: "hello", message: "world" });
|
|
50
62
|
|
|
51
|
-
|
|
52
|
-
client.
|
|
53
|
-
action: "greet",
|
|
54
|
-
data: {
|
|
55
|
-
message: "Hello, World!"
|
|
56
|
-
}
|
|
57
|
-
});
|
|
63
|
+
// later
|
|
64
|
+
client.close();
|
|
58
65
|
```
|
|
59
66
|
|
|
60
|
-
|
|
67
|
+
Typed and validated, with any Standard Schema:
|
|
61
68
|
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
console.log("Received message:", message);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
client.onOpen(() => {
|
|
68
|
-
console.log("Connection opened");
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
client.onError((error) => {
|
|
72
|
-
console.error("Connection error:", error);
|
|
73
|
-
});
|
|
69
|
+
```ts
|
|
70
|
+
import { z } from "zod";
|
|
74
71
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
});
|
|
72
|
+
const Message = z.object({ type: z.string(), body: z.string() });
|
|
73
|
+
const client = defineClient({ url, schema: Message });
|
|
78
74
|
|
|
79
|
-
client.
|
|
80
|
-
|
|
75
|
+
client.on("message", (msg) => {
|
|
76
|
+
// msg: { type: string; body: string } — validated at runtime
|
|
81
77
|
});
|
|
82
78
|
```
|
|
83
79
|
|
|
84
|
-
##
|
|
80
|
+
## Documentation
|
|
85
81
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
#### Config Options
|
|
91
|
-
|
|
92
|
-
- `url`: WebSocket server URL.
|
|
93
|
-
- `autoConnect` (optional): Whether to connect immediately upon instantiation.
|
|
94
|
-
- `maxReconnectAttempts` (optional): Maximum number of reconnection attempts.
|
|
95
|
-
- `maxQueueSize` (optional): Maximum size of the message queue.
|
|
96
|
-
- `idleTimeout` (optional): Duration in milliseconds before the client is considered idle.
|
|
97
|
-
- `authTokenURL` (optional): A URL for token issuance.
|
|
98
|
-
- `getToken (optional): Function that returns a token for authentication. Can be synchronous or asynchronous.
|
|
99
|
-
|
|
100
|
-
### WebSocketClient Methods
|
|
101
|
-
|
|
102
|
-
- `send(message: Record<string, unknown>)`: Send a message through the WebSocket connection.
|
|
103
|
-
- `subscribe(channelName: string)`: Subscribe to a channel.
|
|
104
|
-
- `connect()`: Manually initiate the connection.
|
|
105
|
-
- `disconnect()`: Disconnect the WebSocket connection.
|
|
106
|
-
- `isReady()`: Returns a promise that resolves when the connection is ready.
|
|
107
|
-
- `getStatus()`: Returns the current connection status.
|
|
108
|
-
- `onMessage(listener: OnMessageHandler)`: Register a message event listener.
|
|
109
|
-
- `onError(listener: OnErrorHandler)`: Register an error event listener.
|
|
110
|
-
- `onOpen(listener: OnOpenHandler)`: Register an open event listener.
|
|
111
|
-
- `onClose(listener: OnCloseHandler)`: Register a close event listener.
|
|
112
|
-
- `onIdle(listener: OnIdleHandler)`: Register an idle event listener.
|
|
82
|
+
**[durablews.imns.co](https://durablews.imns.co)** — getting started, guides
|
|
83
|
+
(durability tuning, middleware, codecs, drop-in compat), framework pages, the
|
|
84
|
+
API reference, and an honest comparison with the alternatives.
|
|
113
85
|
|
|
114
86
|
## Contributing
|
|
115
87
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
For major changes, please open an issue first to discuss what you would like to change.
|
|
88
|
+
Contributions are welcome — see [CONTRIBUTING](https://github.com/imnsco/DurableWS/blob/main/CONTRIBUTING.md).
|
|
119
89
|
|
|
120
90
|
## License
|
|
121
91
|
|
|
122
|
-
[
|
|
92
|
+
[MPL-2.0](./LICENSE) © Nate Smith
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { a as WebSocketClientConfig, W as WebSocketClient } from './types-BDvztGcG.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* What a framework binding accepts: either a config (the binding creates and
|
|
5
|
+
* **owns** the client — connecting it and closing it with the component) or an
|
|
6
|
+
* existing client (the binding only **observes** it — sharing one connection
|
|
7
|
+
* across many components without any binding taking over its lifecycle).
|
|
8
|
+
*/
|
|
9
|
+
type UseWebSocketSource<TIn = unknown, TOut = unknown> = WebSocketClientConfig | WebSocketClient<TIn, TOut>;
|
|
10
|
+
|
|
11
|
+
export type { UseWebSocketSource as U };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { a as WebSocketClientConfig, W as WebSocketClient } from './types-BDvztGcG.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* What a framework binding accepts: either a config (the binding creates and
|
|
5
|
+
* **owns** the client — connecting it and closing it with the component) or an
|
|
6
|
+
* existing client (the binding only **observes** it — sharing one connection
|
|
7
|
+
* across many components without any binding taking over its lifecycle).
|
|
8
|
+
*/
|
|
9
|
+
type UseWebSocketSource<TIn = unknown, TOut = unknown> = WebSocketClientConfig | WebSocketClient<TIn, TOut>;
|
|
10
|
+
|
|
11
|
+
export type { UseWebSocketSource as U };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { client } from './chunk-MG4TK53H.js';
|
|
2
|
+
|
|
3
|
+
// src/helpers/binding.ts
|
|
4
|
+
function resolveSource(source) {
|
|
5
|
+
if (isClient(source)) {
|
|
6
|
+
return { client: source, owned: false };
|
|
7
|
+
}
|
|
8
|
+
return {
|
|
9
|
+
client: client(source),
|
|
10
|
+
owned: true
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function isClient(source) {
|
|
14
|
+
return typeof source.subscribe === "function";
|
|
15
|
+
}
|
|
16
|
+
function canConnect() {
|
|
17
|
+
return typeof WebSocket !== "undefined";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { canConnect, resolveSource };
|
|
21
|
+
//# sourceMappingURL=chunk-GIDTCSPD.js.map
|
|
22
|
+
//# sourceMappingURL=chunk-GIDTCSPD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/helpers/binding.ts"],"names":[],"mappings":";;;AAuBO,SAAS,cACZ,MAAA,EACyB;AACzB,EAAA,IAAI,QAAA,CAAS,MAAM,CAAA,EAAG;AAClB,IAAA,OAAO,EAAE,MAAA,EAAQ,MAAA,EAAQ,KAAA,EAAO,KAAA,EAAM;AAAA,EAC1C;AACA,EAAA,OAAO;AAAA,IACH,MAAA,EAAQ,OAAO,MAAM,CAAA;AAAA,IACrB,KAAA,EAAO;AAAA,GACX;AACJ;AAEA,SAAS,SACL,MAAA,EACoC;AACpC,EAAA,OAAO,OAAQ,OAA2B,SAAA,KAAc,UAAA;AAC5D;AAOO,SAAS,UAAA,GAAsB;AAClC,EAAA,OAAO,OAAO,SAAA,KAAc,WAAA;AAChC","file":"chunk-GIDTCSPD.js","sourcesContent":["import { client } from \"@/client\";\nimport type { WebSocketClient, WebSocketClientConfig } from \"@/types\";\n\n/**\n * What a framework binding accepts: either a config (the binding creates and\n * **owns** the client — connecting it and closing it with the component) or an\n * existing client (the binding only **observes** it — sharing one connection\n * across many components without any binding taking over its lifecycle).\n */\nexport type UseWebSocketSource<TIn = unknown, TOut = unknown> =\n | WebSocketClientConfig\n | WebSocketClient<TIn, TOut>;\n\nexport interface ResolvedSource<TIn = unknown, TOut = unknown> {\n readonly client: WebSocketClient<TIn, TOut>;\n /** Whether the binding created the client and therefore owns its lifecycle. */\n readonly owned: boolean;\n}\n\n/**\n * Resolves a binding source into a client plus ownership. A config produces a\n * fresh, owned client; a passed-in client is borrowed as-is.\n */\nexport function resolveSource<TIn = unknown, TOut = unknown>(\n source: UseWebSocketSource<TIn, TOut>\n): ResolvedSource<TIn, TOut> {\n if (isClient(source)) {\n return { client: source, owned: false };\n }\n return {\n client: client(source) as WebSocketClient<TIn, TOut>,\n owned: true\n };\n}\n\nfunction isClient<TIn, TOut>(\n source: UseWebSocketSource<TIn, TOut>\n): source is WebSocketClient<TIn, TOut> {\n return typeof (source as WebSocketClient).subscribe === \"function\";\n}\n\n/**\n * Whether a standard `WebSocket` global exists. Bindings skip auto-connect in\n * its absence (e.g. SSR), so creating a component server-side never throws —\n * the client connects when setup runs again in the browser.\n */\nexport function canConnect(): boolean {\n return typeof WebSocket !== \"undefined\";\n}\n"]}
|