specters 2.2.0 → 3.0.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.
Files changed (4) hide show
  1. package/README.md +50 -116
  2. package/index.d.ts +121 -89
  3. package/index.js +21 -1
  4. package/package.json +12 -7
package/README.md CHANGED
@@ -1,161 +1,95 @@
1
1
  # Specter Node.js Bindings
2
2
 
3
- Node.js bindings for Specter, a high-performance HTTP client with full TLS, HTTP/2, and HTTP/3 fingerprint control.
3
+ Node.js bindings for Specter, a high-performance async HTTP client with TLS, HTTP/2, HTTP/3, RFC 6455 WebSocket, and RFC 8441 Extended CONNECT support.
4
4
 
5
5
  ## Features
6
6
 
7
- - **Native async/await**: Promise-based API with native performance
8
- - **Browser fingerprinting**: Impersonate Chrome, Firefox, or use custom TLS/HTTP2 settings
9
- - **HTTP/3 support**: Automatic upgrade via Alt-Svc headers
10
- - **Connection pooling**: HTTP/2 multiplexing and HTTP/1.1 keep-alive
11
- - **Timeout control**: Granular timeouts for connect, TTFB, read/write idle, and total request time
12
- - **Automatic decompression**: gzip, deflate, brotli, zstd
7
+ - Promise-based HTTP, RFC 6455 WebSocket, and RFC 8441 raw tunnel APIs
8
+ - Browser fingerprinting for Chrome, Firefox, or default TLS settings
9
+ - HTTP/2, HTTP/3, connection pooling, and automatic decompression
10
+ - Cookie store and shared cookie jar support across HTTP and WebSocket handshakes
11
+ - Granular connect, TTFB, read/write idle, total, pool, and WebSocket handshake timeouts
13
12
 
14
13
  ## Installation
15
14
 
16
15
  ```bash
17
- npm install @specter/client
16
+ npm install specters
18
17
  ```
19
18
 
20
- ## Quick Start
19
+ ## HTTP
21
20
 
22
21
  ```javascript
23
- const { Client } = require('@specter/client');
24
-
25
- async function main() {
26
- // Create a client with default settings
27
- const client = Client.builder().build();
28
-
29
- // Make a GET request
30
- const response = await client.get('https://httpbin.org/get').send();
31
- console.log(`Status: ${response.status}`);
32
- console.log(await response.text());
33
- }
34
-
35
- main();
36
- ```
37
-
38
- ## Browser Impersonation
39
-
40
- ```javascript
41
- const { Client, FingerprintProfile } = require('@specter/client');
22
+ const { clientBuilder, FingerprintProfile } = require('specters');
42
23
 
43
- // Impersonate Chrome 146 (current stable)
44
- const client = Client.builder()
45
- .fingerprint(FingerprintProfile.Chrome146)
46
- .build();
47
-
48
- // Or pick a specific version (142, 143, 144, 145, 146)
49
- const client = Client.builder()
24
+ const client = clientBuilder()
50
25
  .fingerprint(FingerprintProfile.Chrome142)
26
+ .cookieStore(true)
51
27
  .build();
52
28
 
53
- // Or Firefox 133
54
- const client = Client.builder()
55
- .fingerprint(FingerprintProfile.Firefox133)
56
- .build();
57
- ```
58
-
59
- ## Timeout Configuration
60
-
61
- ```javascript
62
- const { Client, timeoutsApiDefaults, timeoutsStreamingDefaults } = require('@specter/client');
63
-
64
- // Use preset timeout configurations
65
- const client1 = Client.builder().apiTimeouts().build();
66
- const client2 = Client.builder().streamingTimeouts().build();
67
-
68
- // Or configure manually
69
- const timeouts = {
70
- connect: 10.0, // TCP + TLS handshake
71
- ttfb: 30.0, // Time to first byte
72
- readIdle: 60.0, // Max time between chunks
73
- total: 120.0 // Total request deadline
74
- };
29
+ const response = await client
30
+ .post('https://example.com/items')
31
+ .header('Authorization', 'Bearer token')
32
+ .json(JSON.stringify({ name: 'example' }))
33
+ .send();
75
34
 
76
- const client = Client.builder().timeouts(timeouts).build();
35
+ console.log(response.status);
36
+ console.log(response.text());
77
37
  ```
78
38
 
79
- ## HTTP Methods
39
+ ## RFC 6455 WebSockets
80
40
 
81
41
  ```javascript
82
- const { Client } = require('@specter/client');
83
-
84
- const client = Client.builder().build();
85
-
86
- // GET
87
- const response = await client.get('https://api.example.com/items').send();
42
+ const { CLOSE_NORMAL, clientBuilder } = require('specters');
88
43
 
89
- // POST
90
- const response = await client.post('https://api.example.com/items').send();
91
-
92
- // PUT
93
- const response = await client.put('https://api.example.com/items/1').send();
94
-
95
- // DELETE
96
- const response = await client.delete('https://api.example.com/items/1').send();
44
+ const client = clientBuilder()
45
+ .cookieStore(true)
46
+ .http2PriorKnowledge(false)
47
+ .build();
97
48
 
98
- // PATCH
99
- const response = await client.patch('https://api.example.com/items/1').send();
49
+ const ws = await client.websocket('wss://example.com/socket')
50
+ .header('Origin', 'https://example.com')
51
+ .subprotocol('chat.v1')
52
+ .maxMessageSize(1 << 20)
53
+ .handshakeTimeout(10)
54
+ .connect();
100
55
 
101
- // HEAD
102
- const response = await client.head('https://api.example.com/items/1').send();
56
+ await ws.sendText('hello');
57
+ const message = await ws.next();
103
58
 
104
- // OPTIONS
105
- const response = await client.options('https://api.example.com/items').send();
59
+ if (message.type === 'text') {
60
+ console.log(message.text);
61
+ }
106
62
 
107
- // Arbitrary method
108
- const response = await client.request('PURGE', 'https://api.example.com/cache').send();
63
+ await ws.close({ code: CLOSE_NORMAL, reason: 'done' });
109
64
  ```
110
65
 
111
- ## Response Handling
112
-
113
- ```javascript
114
- const response = await client.get('https://api.example.com/data').send();
115
-
116
- // Status code
117
- console.log(response.status);
118
-
119
- // Headers
120
- console.log(response.headers); // Record<string, string>
121
- console.log(response.getHeader('content-type'));
122
-
123
- // Body
124
- console.log(response.text()); // Decompressed text
125
- console.log(response.json()); // JSON string (use JSON.parse)
126
- const data = response.bytes(); // Buffer
127
-
128
- // Response metadata
129
- console.log(response.httpVersion); // "HTTP/2", "HTTP/1.1", etc.
130
- console.log(response.isSuccess); // true for 2xx status
66
+ RFC 6455 sockets are framed message connections. `send*`, `next`, and `close` operations are serialized per socket to preserve Rust's mutable socket contract; avoid running multiple receive loops on the same socket.
131
67
 
132
- ## Request Builder
68
+ ## RFC 8441 HTTP/2 Tunnels
133
69
 
134
70
  ```javascript
135
- const { Client } = require('@specter/client');
71
+ const { clientBuilder } = require('specters');
136
72
 
137
- const client = Client.builder().build();
73
+ const client = clientBuilder()
74
+ .http2PriorKnowledge(true)
75
+ .build();
138
76
 
139
- const response = await client
140
- .post('https://api.example.com/items')
141
- .header('Authorization', 'Bearer token')
142
- .json(JSON.stringify({ name: 'example' }))
143
- .send();
77
+ const tunnel = await client.websocketH2('https://example.com/h2-tunnel')
78
+ .header('Origin', 'https://example.com')
79
+ .open();
144
80
 
145
- console.log(response.status);
146
- ```
81
+ await tunnel.sendBytes(Buffer.from('raw bytes'), false);
82
+ const data = await tunnel.recvBytes();
83
+ await tunnel.closeSend();
147
84
  ```
148
85
 
86
+ RFC 8441 exposes a raw byte tunnel over HTTP/2 Extended CONNECT. It is intentionally separate from the RFC 6455 framed `WebSocket` API.
87
+
149
88
  ## Development
150
89
 
151
90
  ```bash
152
- # Install dependencies
153
91
  npm install
154
-
155
- # Build the native module
156
92
  npm run build
157
-
158
- # Run tests
159
93
  npm test
160
94
  ```
161
95
 
package/index.d.ts CHANGED
@@ -1,164 +1,196 @@
1
- /**
2
- * Specter - Node.js bindings for the Specter HTTP client.
3
- *
4
- * A high-performance async HTTP client with full TLS, HTTP/2, and HTTP/3
5
- * fingerprint control for browser impersonation.
6
- */
7
-
8
- /** Browser fingerprint profiles for impersonation. */
9
1
  export enum FingerprintProfile {
10
- /** Chrome 142 on macOS */
11
- Chrome142 = 0,
12
- /** Firefox 133 on macOS */
13
- Firefox133 = 1,
14
- /** No fingerprinting - use default TLS settings */
15
- None = 2,
2
+ Chrome142,
3
+ Chrome143,
4
+ Chrome144,
5
+ Chrome145,
6
+ Chrome146,
7
+ Firefox133,
8
+ None,
16
9
  }
17
10
 
18
- /** HTTP version preference. */
19
11
  export enum HttpVersion {
20
- /** Force HTTP/1.1 */
21
- Http1_1 = 0,
22
- /** Attempt HTTP/2, fallback to HTTP/1.1 */
23
- Http2 = 1,
24
- /** Attempt HTTP/3, fallback to HTTP/2, fallback to HTTP/1.1 */
25
- Http3 = 2,
26
- /** HTTP/3 only, no fallback */
27
- Http3Only = 3,
28
- /** Let the client decide based on server support */
29
- Auto = 4,
30
- }
31
-
32
- /** Timeout configuration for HTTP requests. */
12
+ Http1_1,
13
+ Http2,
14
+ Http3,
15
+ Http3Only,
16
+ Auto,
17
+ }
18
+
33
19
  export interface Timeouts {
34
- /** TCP + TLS/QUIC handshake timeout in seconds */
35
20
  connect?: number;
36
- /** Time-to-first-byte timeout in seconds */
37
21
  ttfb?: number;
38
- /** Maximum time between received bytes in seconds (resets on each chunk) */
39
22
  readIdle?: number;
40
- /** Maximum time between sent bytes in seconds */
41
23
  writeIdle?: number;
42
- /** Absolute deadline for entire request in seconds */
43
24
  total?: number;
44
- /** Time to wait for a pooled connection in seconds */
45
25
  poolAcquire?: number;
46
26
  }
47
27
 
48
- /** HTTP request builder for setting headers and body. */
28
+ export interface WebSocketMessage {
29
+ type: 'text' | 'binary' | 'ping' | 'pong' | 'close';
30
+ text?: string;
31
+ data?: Buffer;
32
+ code?: number;
33
+ reason?: string;
34
+ }
35
+
36
+ export interface WebSocketCloseFrame {
37
+ code?: number;
38
+ reason?: string;
39
+ }
40
+
41
+ export interface H2TunnelEvent {
42
+ type: 'data' | 'endStream' | 'reset' | 'goAway';
43
+ data?: Buffer;
44
+ reason?: string;
45
+ lastStreamId?: number;
46
+ }
47
+
48
+ export interface H3TunnelEvent {
49
+ type: 'data' | 'endStream' | 'reset' | 'goAway';
50
+ data?: Buffer;
51
+ reason?: string;
52
+ lastStreamId?: bigint;
53
+ }
54
+
49
55
  export class RequestBuilder {
50
- /** Add a header to the request. Returns this for chaining. */
51
56
  header(key: string, value: string): this;
52
- /** Set all headers (replaces existing headers). Returns this for chaining. */
53
57
  headers(headers: string[][]): this;
54
- /** Set the request body as bytes. Returns this for chaining. */
55
58
  body(body: Buffer): this;
56
- /** Set the request body as JSON string and add Content-Type header. Returns this for chaining. */
57
59
  json(jsonStr: string): this;
58
- /** Set the request body as form data and add Content-Type header. Returns this for chaining. */
59
60
  form(formStr: string): this;
60
- /** Send the request and return the response. */
61
61
  send(): Promise<Response>;
62
62
  }
63
63
 
64
- /** HTTP response with decompression support. */
65
64
  export class Response {
66
- /** HTTP status code */
67
65
  get status(): number;
68
- /** Response headers as an object */
69
66
  get headers(): Record<string, string>;
70
- /** Get all headers as an array of [key, value] pairs */
71
67
  headersList(): string[][];
72
- /** Get a specific header value by name */
73
68
  getHeader(name: string): string | null;
74
- /** Get the response body as text (with decompression if needed) */
75
69
  text(): string;
76
- /** Get the response body as a Buffer */
77
70
  bytes(): Buffer;
78
- /** Parse the response body as JSON and return as string. Use JSON.parse to convert to an object. */
79
71
  json(): string;
80
- /** HTTP version string */
81
72
  get httpVersion(): string;
82
- /** Effective URL (after redirects) */
83
73
  get effectiveUrl(): string | null;
84
- /** Check if the response status is successful (2xx) */
85
74
  get isSuccess(): boolean;
86
- /** Check if the response is a redirect (3xx) */
87
75
  get isRedirect(): boolean;
88
- /** Get the redirect URL from Location header if present */
89
76
  get redirectUrl(): string | null;
90
- /** Get the Content-Type header value */
91
77
  get contentType(): string | null;
92
78
  }
93
79
 
94
- /** Builder for creating HTTP clients. */
95
80
  export class ClientBuilder {
96
- /** Set the fingerprint profile */
97
81
  fingerprint(profile: FingerprintProfile): this;
98
- /** Set HTTP/2 preference */
99
82
  preferHttp2(prefer: boolean): this;
100
- /** Enable or disable automatic HTTP/3 upgrade via Alt-Svc headers */
83
+ http2PriorKnowledge(enabled: boolean): this;
101
84
  h3Upgrade(enabled: boolean): this;
102
- /** Set timeout configuration */
85
+ cookieStore(enabled: boolean): this;
86
+ cookieJar(jar: CookieJar): this;
103
87
  timeouts(timeouts: Timeouts): this;
104
- /** Use API-optimized timeout defaults */
105
88
  apiTimeouts(): this;
106
- /** Use streaming-optimized timeout defaults */
107
89
  streamingTimeouts(): this;
108
- /** Set total request timeout in seconds */
109
90
  totalTimeout(timeoutSecs: number): this;
110
- /** Set connect timeout in seconds */
111
91
  connectTimeout(timeoutSecs: number): this;
112
- /** Set TTFB (time-to-first-byte) timeout in seconds */
113
92
  ttfbTimeout(timeoutSecs: number): this;
114
- /** Set read idle timeout in seconds */
115
93
  readTimeout(timeoutSecs: number): this;
116
- /** Skip TLS certificate verification for all connections (DANGEROUS - for testing only) */
117
94
  dangerAcceptInvalidCerts(accept: boolean): this;
118
- /** Automatically skip TLS certificate verification for localhost connections */
119
95
  localhostAllowsInvalidCerts(allow: boolean): this;
120
- /** Load root certificates from the operating system's certificate store */
121
96
  withPlatformRoots(enabled: boolean): this;
122
- /** Build the client */
123
97
  build(): Client;
124
98
  }
125
99
 
126
- /** HTTP client with TLS/HTTP2/HTTP3 fingerprint control. */
127
100
  export class Client {
128
- /** Create a GET request builder */
101
+ websocket(url: string): WebSocketBuilder;
102
+ websocketH2(url: string): WebSocketH2Builder;
103
+ websocketH3(url: string): WebSocketH3Builder;
129
104
  get(url: string): RequestBuilder;
130
- /** Create a POST request builder */
131
105
  post(url: string): RequestBuilder;
132
- /** Create a PUT request builder */
133
106
  put(url: string): RequestBuilder;
134
- /** Create a DELETE request builder */
135
107
  delete(url: string): RequestBuilder;
136
- /** Create a PATCH request builder */
137
108
  patch(url: string): RequestBuilder;
138
- /** Create a HEAD request builder */
139
109
  head(url: string): RequestBuilder;
140
- /** Create an OPTIONS request builder */
141
110
  options(url: string): RequestBuilder;
142
- /** Create a request builder for an arbitrary HTTP method */
143
111
  request(method: string, url: string): RequestBuilder;
144
112
  }
145
113
 
146
- /** Cookie jar for manual cookie management. */
147
114
  export class CookieJar {
148
115
  constructor();
149
- /** Get the number of cookies in the jar */
150
116
  get length(): number;
151
- /** Check if the cookie jar is empty */
152
117
  get isEmpty(): boolean;
153
118
  }
154
119
 
155
- /** Create a new client builder */
156
- export function clientBuilder(): ClientBuilder;
120
+ export class WebSocketBuilder {
121
+ header(key: string, value: string): this;
122
+ headers(headers: Record<string, string>): this;
123
+ subprotocol(value: string): this;
124
+ subprotocols(values: string[]): this;
125
+ maxMessageSize(bytes: number): this;
126
+ maxFrameSize(bytes: number): this;
127
+ connectTimeout(timeoutSecs: number): this;
128
+ handshakeTimeout(timeoutSecs: number): this;
129
+ readTimeout(timeoutSecs: number): this;
130
+ writeTimeout(timeoutSecs: number): this;
131
+ connect(): Promise<WebSocket>;
132
+ }
157
133
 
158
- /** Sensible defaults for normal API calls. */
159
- export function timeoutsApiDefaults(): Timeouts;
134
+ export class WebSocket {
135
+ get url(): string;
136
+ get protocol(): string | null;
137
+ send(message: WebSocketMessage): Promise<void>;
138
+ sendText(text: string): Promise<void>;
139
+ sendBinary(data: Buffer): Promise<void>;
140
+ sendPing(data?: Buffer): Promise<void>;
141
+ sendPong(data?: Buffer): Promise<void>;
142
+ next(): Promise<WebSocketMessage>;
143
+ close(frame?: WebSocketCloseFrame): Promise<void>;
144
+ }
160
145
 
161
- /** Sensible defaults for streaming responses. */
162
- export function timeoutsStreamingDefaults(): Timeouts;
146
+ export class WebSocketH2Builder {
147
+ header(key: string, value: string): this;
148
+ headers(headers: string[][]): this;
149
+ subprotocol(value: string): this;
150
+ connectTimeout(timeoutSecs: number): this;
151
+ readTimeout(timeoutSecs: number): this;
152
+ writeTimeout(timeoutSecs: number): this;
153
+ connect(): Promise<WebSocketH2Tunnel>;
154
+ }
155
+
156
+ export class WebSocketH2Tunnel {
157
+ sendBytes(data: Buffer, endStream?: boolean): Promise<void>;
158
+ recvBytes(): Promise<Buffer | null>;
159
+ recvEvent(): Promise<H2TunnelEvent | null>;
160
+ closeSend(): Promise<void>;
161
+ }
162
+
163
+ export class WebSocketH3Builder {
164
+ header(key: string, value: string): this;
165
+ headers(headers: string[][]): this;
166
+ subprotocol(value: string): this;
167
+ connectTimeout(timeoutSecs: number): this;
168
+ readTimeout(timeoutSecs: number): this;
169
+ writeTimeout(timeoutSecs: number): this;
170
+ connect(): Promise<WebSocketH3Tunnel>;
171
+ }
172
+
173
+ export class WebSocketH3Tunnel {
174
+ sendBytes(data: Buffer, fin?: boolean): Promise<void>;
175
+ recvBytes(): Promise<Buffer | null>;
176
+ recvEvent(): Promise<H3TunnelEvent | null>;
177
+ closeSend(): Promise<void>;
178
+ }
163
179
 
164
- export {};
180
+ export const CLOSE_NORMAL: number;
181
+ export const CLOSE_GOING_AWAY: number;
182
+ export const CLOSE_PROTOCOL_ERROR: number;
183
+ export const CLOSE_UNSUPPORTED: number;
184
+ export const CLOSE_NO_STATUS: number;
185
+ export const CLOSE_ABNORMAL: number;
186
+ export const CLOSE_INVALID_PAYLOAD: number;
187
+ export const CLOSE_POLICY_VIOLATION: number;
188
+ export const CLOSE_MESSAGE_TOO_BIG: number;
189
+ export const CLOSE_MANDATORY_EXTENSION: number;
190
+ export const CLOSE_INTERNAL_ERROR: number;
191
+ export const CLOSE_TLS_ERROR: number;
192
+
193
+ export function isValidCloseCode(code: number): boolean;
194
+ export function clientBuilder(): ClientBuilder;
195
+ export function timeoutsApiDefaults(): Timeouts;
196
+ export function timeoutsStreamingDefaults(): Timeouts;
package/index.js CHANGED
@@ -8,7 +8,7 @@
8
8
  const { execSync } = require('node:child_process');
9
9
  const { readFileSync } = require('node:fs');
10
10
 
11
- const PACKAGE_VERSION = '2.2.0';
11
+ const PACKAGE_VERSION = '3.0.0';
12
12
 
13
13
  const targets = {
14
14
  'darwin-x64': {
@@ -131,9 +131,29 @@ module.exports.ClientBuilder = binding.ClientBuilder;
131
131
  module.exports.RequestBuilder = binding.RequestBuilder;
132
132
  module.exports.Response = binding.Response;
133
133
  module.exports.CookieJar = binding.CookieJar;
134
+ module.exports.WebSocketBuilder = binding.WebSocketBuilder;
135
+ module.exports.WebSocket = binding.WebSocket;
136
+ module.exports.WebSocketH2Builder = binding.WebSocketH2Builder;
137
+ module.exports.WebSocketH2Tunnel = binding.WebSocketH2Tunnel;
138
+ module.exports.WebSocketH3Builder = binding.WebSocketH3Builder;
139
+ module.exports.WebSocketH3Tunnel = binding.WebSocketH3Tunnel;
140
+ module.exports.WebSocketCloseFrame = binding.WebSocketCloseFrame;
134
141
  module.exports.FingerprintProfile = binding.FingerprintProfile;
135
142
  module.exports.HttpVersion = binding.HttpVersion;
136
143
  module.exports.Timeouts = binding.Timeouts;
144
+ module.exports.CLOSE_NORMAL = binding.CLOSE_NORMAL;
145
+ module.exports.CLOSE_GOING_AWAY = binding.CLOSE_GOING_AWAY;
146
+ module.exports.CLOSE_PROTOCOL_ERROR = binding.CLOSE_PROTOCOL_ERROR;
147
+ module.exports.CLOSE_UNSUPPORTED = binding.CLOSE_UNSUPPORTED;
148
+ module.exports.CLOSE_NO_STATUS = binding.CLOSE_NO_STATUS;
149
+ module.exports.CLOSE_ABNORMAL = binding.CLOSE_ABNORMAL;
150
+ module.exports.CLOSE_INVALID_PAYLOAD = binding.CLOSE_INVALID_PAYLOAD;
151
+ module.exports.CLOSE_POLICY_VIOLATION = binding.CLOSE_POLICY_VIOLATION;
152
+ module.exports.CLOSE_MESSAGE_TOO_BIG = binding.CLOSE_MESSAGE_TOO_BIG;
153
+ module.exports.CLOSE_MANDATORY_EXTENSION = binding.CLOSE_MANDATORY_EXTENSION;
154
+ module.exports.CLOSE_INTERNAL_ERROR = binding.CLOSE_INTERNAL_ERROR;
155
+ module.exports.CLOSE_TLS_ERROR = binding.CLOSE_TLS_ERROR;
156
+ module.exports.isValidCloseCode = binding.isValidCloseCode;
137
157
  module.exports.clientBuilder = binding.clientBuilder;
138
158
  module.exports.timeoutsApiDefaults = binding.timeoutsApiDefaults;
139
159
  module.exports.timeoutsStreamingDefaults = binding.timeoutsStreamingDefaults;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "specters",
3
- "version": "2.2.0",
4
- "description": "Node.js bindings for Specter HTTP client with TLS/HTTP2/HTTP3 fingerprint control",
3
+ "version": "3.0.0",
4
+ "description": "Node.js bindings for Specter HTTP, WebSocket, HTTP/2, and HTTP/3 tunnel client",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
7
7
  "napi": {
@@ -29,11 +29,16 @@
29
29
  "artifacts": "napi artifacts --output-dir ./artifacts",
30
30
  "test": "jest"
31
31
  },
32
+ "jest": {
33
+ "testMatch": [
34
+ "**/__tests__/*.test.js"
35
+ ]
36
+ },
32
37
  "optionalDependencies": {
33
- "specters-darwin-x64": "2.2.0",
34
- "specters-darwin-arm64": "2.2.0",
35
- "specters-linux-x64-gnu": "2.2.0",
36
- "specters-linux-arm64-gnu": "2.2.0"
38
+ "specters-darwin-x64": "3.0.0",
39
+ "specters-darwin-arm64": "3.0.0",
40
+ "specters-linux-x64-gnu": "3.0.0",
41
+ "specters-linux-arm64-gnu": "3.0.0"
37
42
  },
38
43
  "devDependencies": {
39
44
  "@napi-rs/cli": "^3.6.2",
@@ -55,4 +60,4 @@
55
60
  "url": "https://github.com/jaredboynton/specter.git",
56
61
  "directory": "bindings/node"
57
62
  }
58
- }
63
+ }