durablews 1.0.0 → 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,118 +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
- - **Subscription Management**: Simplifies subscribing to topics/channels with ease.
11
- - **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).
12
8
 
13
- ## Installation
9
+ ## Status
14
10
 
15
- ```bash
16
- npm install durablews
17
- ```
11
+ DurableWS v2 is being built in the open. To be accurate about what exists today vs. what's planned:
18
12
 
19
- or
13
+ **Working now**
20
14
 
21
- ```bash
22
- yarn add durablews
23
- ```
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
24
19
 
25
- ## Basic Usage
20
+ **Planned for v2 (not yet implemented)**
26
21
 
27
- 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
28
28
 
29
- ```ts
30
- import { createClient } from "durablews";
31
- ```
29
+ Until these land, treat the durability features as a roadmap, not a guarantee.
32
30
 
33
- Create a new WebSocket client instance by specifying the configuration:
31
+ ## Requirements
34
32
 
35
- ```ts
36
- const client = await createClient({
37
- url: "wss://your.websocket.server",
38
- autoConnect: true, // Automatically connects
39
- maxReconnectAttempts: 5,
40
- maxQueueSize: 50,
41
- idleTimeout: 300000, // 5 minutes
42
- getToken: async () => {
43
- const response = await fetch("/my/token/issuer").then((r) => r.json());
44
- return response.token;
45
- },
46
- });
47
- ```
33
+ DurableWS targets the **standard global `WebSocket`** and ships **zero runtime dependencies**. That means:
34
+
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.
48
37
 
49
- ### Sending Messages
38
+ ## Installation
50
39
 
51
- ```typescript
52
- client.send({ action: "greet", data: { message: "Hello, World!" } });
40
+ ```bash
41
+ # v2 is not yet published; once the alpha ships it will be:
42
+ npm install durablews@alpha
53
43
  ```
54
44
 
55
- ### Handling Events
45
+ ## Quick start
56
46
 
57
- ```typescript
58
- client.onMessage((message) => {
59
- console.log("Received message:", message);
60
- });
47
+ ```ts
48
+ import { defineClient } from "durablews";
61
49
 
62
- client.onOpen(() => {
63
- console.log("Connection opened");
64
- });
50
+ const client = defineClient({ url: "wss://example.com/socket" });
65
51
 
66
- client.onError((error) => {
67
- console.error("Connection error:", error);
52
+ client.on("message", (data) => {
53
+ console.log("received:", data);
68
54
  });
69
55
 
70
- client.onClose(() => {
71
- console.log("Connection closed");
72
- });
56
+ await client.connect();
57
+ client.send({ type: "hello", message: "world" });
73
58
 
74
- client.onIdle(() => {
75
- console.log("Client is idle");
76
- });
59
+ // later
60
+ client.close();
77
61
  ```
78
62
 
79
- ## API Reference
63
+ ## Documentation
80
64
 
81
- ### `createClient(config: WebSocketClientConfig): Promise<WebSocketClient>`
82
-
83
- Initializes and returns a WebSocket client instance.
84
-
85
- #### Config Options
86
-
87
- - `url`: WebSocket server URL.
88
- - `accessToken` (optional): Token for authentication.
89
- - `autoConnect` (optional): Whether to connect immediately upon instantiation.
90
- - `maxReconnectAttempts` (optional): Maximum number of reconnection attempts.
91
- - `maxQueueSize` (optional): Maximum size of the message queue.
92
- - `idleTimeout` (optional): Duration in milliseconds before the client is considered idle.
93
- - `authTokenURL` (optional): A URL for token issuance.
94
- - `getToken (optional): Function that returns a token for authentication. Can be synchronous or asynchronous.
95
-
96
- ### WebSocketClient Methods
97
-
98
- - `send(message: Record<string, unknown>)`: Send a message through the WebSocket connection.
99
- - `subscribe(channelName: string)`: Subscribe to a channel.
100
- - `connect()`: Manually initiate the connection.
101
- - `disconnect()`: Disconnect the WebSocket connection.
102
- - `isReady()`: Returns a promise that resolves when the connection is ready.
103
- - `getStatus()`: Returns the current connection status.
104
- - `onMessage(listener: OnMessageHandler)`: Register a message event listener.
105
- - `onError(listener: OnErrorHandler)`: Register an error event listener.
106
- - `onOpen(listener: OnOpenHandler)`: Register an open event listener.
107
- - `onClose(listener: OnCloseHandler)`: Register a close event listener.
108
- - `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.
109
67
 
110
68
  ## Contributing
111
69
 
112
- We welcome contributions! If you'd like to contribute, please fork the repository and use a feature branch. Pull requests are warmly welcome.
113
-
114
- 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).
115
71
 
116
72
  ## License
117
73
 
118
- [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"]}