durablews 1.0.1 → 2.0.0-alpha.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/README.md CHANGED
@@ -1,122 +1,74 @@
1
- # DurableWS (Durable Web Sockets)
1
+ # DurableWS
2
2
 
3
- DurableWS is a resilient, TypeScript-based WebSocket client designed for modern web applications. It offers robust connection handling, automatic reconnection with exponential backoff, idle detection, message queueing, and easy subscription management, ensuring your application maintains a reliable real-time connection under varying network conditions.
3
+ > A resilient, modern, zero-dependency WebSocket **client** for TypeScript built on the standard `WebSocket`, durable by default, and the same in every modern runtime.
4
4
 
5
- ## Features
5
+ > ⚠️ **v2 is under active development (`2.0.0-alpha`).** The API is changing and several headline features below are still being built. See the [v2 architecture RFC](https://github.com/imnsco/DurableWS/blob/main/rfcs/0001-v2-architecture.md) for the design and live status. The current npm release (`1.x`) predates this redesign.
6
6
 
7
- - **Automatic Reconnection**: Implements exponential backoff strategy to handle disconnections gracefully.
8
- - **Idle Detection**: Automatically detects idle states and triggers custom handlers.
9
- - **Message Queueing**: Outgoing messages are queued when the connection is down and sent when it's restored.
10
- - **TypeScript Support**: Fully typed interfaces for a better development experience.
7
+ DurableWS aims to be "the Hono of WebSockets": tiny, ergonomic, Web-Standards-based, and **durable by default** automatic reconnection, message queueing, and idle detection out of the box — with a middleware + codec pipeline for extensibility (custom wire formats, auth, a socket.io-compatibility layer, and more).
11
8
 
12
- ## Installation
9
+ ## Status
13
10
 
14
- ```bash
15
- npm install durablews
16
- ```
11
+ DurableWS v2 is being built in the open. To be accurate about what exists today vs. what's planned:
17
12
 
18
- or
13
+ **Working now**
19
14
 
20
- ```bash
21
- yarn add durablews
22
- ```
15
+ - Connect / send / close and incoming-message handling over the standard `WebSocket`
16
+ - Event subscriptions (`on` / `off`)
17
+ - A middleware pipeline (`use`)
18
+ - Built-in JSON-safe message parsing and a ping/pong middleware
23
19
 
24
- ## Basic Usage
20
+ **Planned for v2 (not yet implemented)**
25
21
 
26
- To get started with DurableWS, first import the client in your project:
22
+ - Automatic reconnection with exponential backoff
23
+ - Message queueing while disconnected, flushed on reconnect
24
+ - Idle detection
25
+ - A typed connection state machine
26
+ - Pluggable codecs (msgpack, socket.io framing, …) and an authentication helper
27
+ - Channels / subscriptions
27
28
 
28
- ```ts
29
- import { defineClient } from "durablews";
30
- ```
29
+ Until these land, treat the durability features as a roadmap, not a guarantee.
31
30
 
32
- Create a new WebSocket client instance by specifying the configuration:
31
+ ## Requirements
33
32
 
34
- ```ts
35
- const client = await defineClient({
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
- }
46
- });
47
- ```
33
+ DurableWS targets the **standard global `WebSocket`** and ships **zero runtime dependencies**. That means:
48
34
 
49
- ### Sending Messages
35
+ - **Node.js ≥ 22** — the first release where the global `WebSocket` is available unflagged. (There is no `ws` dependency, by design.)
36
+ - Any modern runtime with a global `WebSocket`: current browsers, Deno, Bun, and Cloudflare Workers.
50
37
 
51
- ```typescript
52
- client.send({
53
- action: "greet",
54
- data: {
55
- message: "Hello, World!"
56
- }
57
- });
38
+ ## Installation
39
+
40
+ ```bash
41
+ # v2 is not yet published; once the alpha ships it will be:
42
+ npm install durablews@alpha
58
43
  ```
59
44
 
60
- ### Handling Events
45
+ ## Quick start
61
46
 
62
- ```typescript
63
- client.onMessage((message) => {
64
- console.log("Received message:", message);
65
- });
47
+ ```ts
48
+ import { defineClient } from "durablews";
66
49
 
67
- client.onOpen(() => {
68
- console.log("Connection opened");
69
- });
50
+ const client = defineClient({ url: "wss://example.com/socket" });
70
51
 
71
- client.onError((error) => {
72
- console.error("Connection error:", error);
52
+ client.on("message", (data) => {
53
+ console.log("received:", data);
73
54
  });
74
55
 
75
- client.onClose(() => {
76
- console.log("Connection closed");
77
- });
56
+ await client.connect();
57
+ client.send({ type: "hello", message: "world" });
78
58
 
79
- client.onIdle(() => {
80
- console.log("Client is idle");
81
- });
59
+ // later
60
+ client.close();
82
61
  ```
83
62
 
84
- ## API Reference
63
+ ## Documentation
85
64
 
86
- ### `defineClient(config: WebSocketClientConfig): Promise<WebSocketClient>`
87
-
88
- Initializes and returns a WebSocket client instance.
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.
65
+ - [v2 architecture RFC](https://github.com/imnsco/DurableWS/blob/main/rfcs/0001-v2-architecture.md)
66
+ - A full documentation site (Astro + Starlight) is coming as part of v2.
113
67
 
114
68
  ## Contributing
115
69
 
116
- We welcome contributions! If you'd like to contribute, please fork the repository and use a feature branch. Pull requests are warmly welcome.
117
-
118
- For major changes, please open an issue first to discuss what you would like to change.
70
+ Contributions are welcome see [CONTRIBUTING](https://github.com/imnsco/DurableWS/blob/main/CONTRIBUTING.md).
119
71
 
120
72
  ## License
121
73
 
122
- [Mozilla Public License 2.0](https://www.mozilla.org/en-US/MPL/2.0/)
74
+ [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"]}