@reddb-io/client 1.15.0 → 1.16.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 +8 -0
- package/index.browser.d.ts +7 -5
- package/index.d.ts +2 -1
- package/package.json +1 -1
- package/src/core/url.js +32 -12
- package/src/index.browser.js +12 -9
- package/src/redwire-ws.js +4 -4
package/README.md
CHANGED
|
@@ -147,6 +147,14 @@ The client follows the SDK Helper Spec for the shared JS/TS surface:
|
|
|
147
147
|
| `grpcs://` | gRPC over TLS | 55555 |
|
|
148
148
|
| `http://` | HTTP JSON | 5000 |
|
|
149
149
|
| `https://` | HTTPS JSON | 55555 |
|
|
150
|
+
| `ws://` | RedWire over WebSocket | 80 |
|
|
151
|
+
| `wss://` | RedWire over WebSocket + TLS | 443 |
|
|
152
|
+
| `red+ws://` | RedWire over WebSocket | 80 |
|
|
153
|
+
| `red+wss://` | RedWire over WebSocket + TLS | 443 |
|
|
154
|
+
|
|
155
|
+
Browser bundles cannot open raw TCP, so use `wss://host` (or `ws://host`
|
|
156
|
+
for plaintext development origins) when connecting directly from browser
|
|
157
|
+
JavaScript.
|
|
150
158
|
|
|
151
159
|
## Rejected URI schemes
|
|
152
160
|
|
package/index.browser.d.ts
CHANGED
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
* - It omits `splitNdjson()` (a `node:stream` `Transform`), which has no
|
|
14
14
|
* browser counterpart.
|
|
15
15
|
*
|
|
16
|
-
* `connect()` reaches `http(s)
|
|
17
|
-
* WebSocket, #937) from a browser; `grpc(s)://`,
|
|
16
|
+
* `connect()` reaches `http(s)://`, `ws(s)://`, and `red+ws(s)://`
|
|
17
|
+
* (RedWire-over-binary-WebSocket, #937) from a browser; `grpc(s)://`,
|
|
18
|
+
* `red(s)://`, and `pg`
|
|
18
19
|
* throw `RedDBError` with code `'BROWSER_TRANSPORT_UNSUPPORTED'`, and
|
|
19
20
|
* embedded URIs throw `EmbeddedNotSupported`.
|
|
20
21
|
*/
|
|
@@ -422,8 +423,8 @@ export class RedDB {
|
|
|
422
423
|
/**
|
|
423
424
|
* Connect to a remote RedDB instance from a browser.
|
|
424
425
|
*
|
|
425
|
-
* `http://host:port`, `https://host:port`,
|
|
426
|
-
* (RedWire-over-binary-WebSocket, #937) are reachable from a browser
|
|
426
|
+
* `http://host:port`, `https://host:port`, `ws(s)://host:port`, and
|
|
427
|
+
* `red+ws(s)://host:port` (RedWire-over-binary-WebSocket, #937) are reachable from a browser
|
|
427
428
|
* sandbox. `grpc(s)://`, `red(s)://`, and `pg` throw `RedDBError` with code
|
|
428
429
|
* `'BROWSER_TRANSPORT_UNSUPPORTED'`. Embedded URIs (`memory://`, `memory:`,
|
|
429
430
|
* `file:///path`, `red:///`, `red://:memory[:]`) throw `EmbeddedNotSupported`.
|
|
@@ -437,9 +438,10 @@ export function login(
|
|
|
437
438
|
): Promise<LoginResult>
|
|
438
439
|
|
|
439
440
|
export interface ParsedUri {
|
|
440
|
-
kind: 'embedded' | 'http' | 'https' | 'red' | 'reds' | 'redwss' | 'grpc' | 'grpcs' | 'pg'
|
|
441
|
+
kind: 'embedded' | 'http' | 'https' | 'red' | 'reds' | 'redws' | 'redwss' | 'grpc' | 'grpcs' | 'pg'
|
|
441
442
|
host?: string
|
|
442
443
|
port?: number
|
|
444
|
+
tls?: boolean
|
|
443
445
|
path?: string
|
|
444
446
|
username?: string
|
|
445
447
|
password?: string
|
package/index.d.ts
CHANGED
|
@@ -443,9 +443,10 @@ export function login(
|
|
|
443
443
|
): Promise<LoginResult>
|
|
444
444
|
|
|
445
445
|
export interface ParsedUri {
|
|
446
|
-
kind: 'embedded' | 'http' | 'https' | 'red' | 'reds' | 'redwss' | 'grpc' | 'grpcs' | 'pg'
|
|
446
|
+
kind: 'embedded' | 'http' | 'https' | 'red' | 'reds' | 'redws' | 'redwss' | 'grpc' | 'grpcs' | 'pg'
|
|
447
447
|
host?: string
|
|
448
448
|
port?: number
|
|
449
|
+
tls?: boolean
|
|
449
450
|
path?: string
|
|
450
451
|
username?: string
|
|
451
452
|
password?: string
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reddb-io/client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "Thin remote-only RedDB driver. Downloads the `red_client` binary on install. Speaks RedWire/gRPC/HTTP. Embedded URIs (memory://, file://, red:///path) are rejected — use @reddb-io/sdk for those.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
package/src/core/url.js
CHANGED
|
@@ -20,7 +20,7 @@ import { RedDBError } from './errors.js'
|
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* @typedef {object} ParsedUri
|
|
23
|
-
* @property {'embedded' | 'http' | 'https' | 'red' | 'reds' | 'redwss' | 'grpc' | 'grpcs' | 'pg'} kind
|
|
23
|
+
* @property {'embedded' | 'http' | 'https' | 'red' | 'reds' | 'redws' | 'redwss' | 'grpc' | 'grpcs' | 'pg'} kind
|
|
24
24
|
* @property {string} [host]
|
|
25
25
|
* @property {number} [port]
|
|
26
26
|
* @property {string} [path] // for embedded `file://`-equivalent
|
|
@@ -35,8 +35,8 @@ import { RedDBError } from './errors.js'
|
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Parse any URI string into a normalised `ParsedUri`.
|
|
38
|
-
* Accepts `red://`, `memory://`, `file://`, `grpc://`
|
|
39
|
-
* three for backwards compat).
|
|
38
|
+
* Accepts `red://`, `ws(s)://`, `memory://`, `file://`, `grpc://`
|
|
39
|
+
* (the latter three for backwards compat).
|
|
40
40
|
*
|
|
41
41
|
* @param {string} uri
|
|
42
42
|
* @returns {ParsedUri}
|
|
@@ -48,7 +48,16 @@ export function parseUri(uri) {
|
|
|
48
48
|
)
|
|
49
49
|
}
|
|
50
50
|
if (uri.startsWith('red+wss://')) {
|
|
51
|
-
return
|
|
51
|
+
return parseRedWebSocketUrl(uri, 'red+wss')
|
|
52
|
+
}
|
|
53
|
+
if (uri.startsWith('red+ws://')) {
|
|
54
|
+
return parseRedWebSocketUrl(uri, 'red+ws')
|
|
55
|
+
}
|
|
56
|
+
if (uri.startsWith('wss://')) {
|
|
57
|
+
return parseRedWebSocketUrl(uri, 'wss')
|
|
58
|
+
}
|
|
59
|
+
if (uri.startsWith('ws://')) {
|
|
60
|
+
return parseRedWebSocketUrl(uri, 'ws')
|
|
52
61
|
}
|
|
53
62
|
if (uri.startsWith('red://') || uri === 'red:' || uri === 'red:/') {
|
|
54
63
|
return parseRedUrl(uri)
|
|
@@ -57,28 +66,35 @@ export function parseUri(uri) {
|
|
|
57
66
|
}
|
|
58
67
|
|
|
59
68
|
/**
|
|
60
|
-
* Parse `
|
|
69
|
+
* Parse `ws(s)://[user:pass@]host:port[?token=...]` — RedWire framing
|
|
61
70
|
* tunneled over a binary WebSocket (the browser transport; #937, ADR
|
|
62
|
-
* 0036). Resolves to a `
|
|
63
|
-
*
|
|
71
|
+
* 0036). Resolves to a `ws(s)://host:port/redwire` endpoint downstream.
|
|
72
|
+
* TLS defaults to port 443; plaintext defaults to port 80.
|
|
64
73
|
*/
|
|
65
74
|
export function parseRedWssUrl(uri) {
|
|
75
|
+
return parseRedWebSocketUrl(uri, 'red+wss')
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function parseRedWebSocketUrl(uri, scheme) {
|
|
79
|
+
const tls = scheme === 'red+wss' || scheme === 'wss'
|
|
80
|
+
const prefix = `${scheme}://`
|
|
66
81
|
let parsed
|
|
67
82
|
try {
|
|
68
83
|
// Borrow the URL parser via an `https://` authority so user / pass /
|
|
69
84
|
// host / port / query all decode with the standard rules.
|
|
70
|
-
parsed = new URL('https://' + uri.slice(
|
|
85
|
+
parsed = new URL('https://' + uri.slice(prefix.length))
|
|
71
86
|
} catch (err) {
|
|
72
87
|
throw new RedDBError('UNPARSEABLE_URI', `failed to parse '${uri}': ${err.message}`)
|
|
73
88
|
}
|
|
74
89
|
if (!parsed.hostname) {
|
|
75
|
-
throw new TypeError(`invalid
|
|
90
|
+
throw new TypeError(`invalid ${prefix} URI: missing host in '${uri}'`)
|
|
76
91
|
}
|
|
77
92
|
const params = parsed.searchParams
|
|
78
93
|
return {
|
|
79
|
-
kind: 'redwss',
|
|
94
|
+
kind: tls ? 'redwss' : 'redws',
|
|
80
95
|
host: parsed.hostname,
|
|
81
|
-
port: parsed.port ? Number(parsed.port) : 443,
|
|
96
|
+
port: parsed.port ? Number(parsed.port) : (tls ? 443 : 80),
|
|
97
|
+
tls,
|
|
82
98
|
username: parsed.username ? decodeURIComponent(parsed.username) : undefined,
|
|
83
99
|
password: parsed.password ? decodeURIComponent(parsed.password) : undefined,
|
|
84
100
|
token: params.get('token') ?? undefined,
|
|
@@ -239,7 +255,7 @@ export function parseLegacyUrl(uri) {
|
|
|
239
255
|
}
|
|
240
256
|
throw new RedDBError(
|
|
241
257
|
'UNSUPPORTED_SCHEME',
|
|
242
|
-
`unsupported URI: '${uri}'. Use 'red://...' or one of memory://, file://, grpc://, http(s)://`,
|
|
258
|
+
`unsupported URI: '${uri}'. Use 'red://...' or one of memory://, file://, grpc://, http(s)://, ws(s)://`,
|
|
243
259
|
)
|
|
244
260
|
}
|
|
245
261
|
|
|
@@ -280,6 +296,10 @@ function defaultPortFor(kind) {
|
|
|
280
296
|
case 'reds':
|
|
281
297
|
case 'redwire':
|
|
282
298
|
return 5050
|
|
299
|
+
case 'redws':
|
|
300
|
+
return 80
|
|
301
|
+
case 'redwss':
|
|
302
|
+
return 443
|
|
283
303
|
case 'grpc':
|
|
284
304
|
return 55055
|
|
285
305
|
case 'grpcs':
|
package/src/index.browser.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* - 'http://host:port' — HTTP JSON-RPC over `fetch`
|
|
9
9
|
* - 'https://host:port' — HTTPS JSON-RPC over `fetch`
|
|
10
|
+
* - 'ws(s)://host:port' — RedWire over binary WebSocket
|
|
10
11
|
*
|
|
11
12
|
* Streaming is the Web-streams implementation (`./streaming-web.js`), so the
|
|
12
13
|
* full transport-agnostic surface works client-side: query, execute, insert,
|
|
@@ -72,18 +73,18 @@ function browserTransportError(scheme) {
|
|
|
72
73
|
`'${scheme}' connections are not available in the browser: the `
|
|
73
74
|
+ `browser sandbox exposes no raw TCP socket (red://, reds://, pg) or `
|
|
74
75
|
+ `HTTP/2 client (grpc://, grpcs://) to JavaScript. Connect to an `
|
|
75
|
-
+ `HTTP(S) endpoint instead — e.g.
|
|
76
|
-
+ `'
|
|
77
|
-
+ `
|
|
78
|
-
+ `
|
|
76
|
+
+ `HTTP(S) endpoint or RedWire WebSocket endpoint instead — e.g. `
|
|
77
|
+
+ `'http://host:port' / 'https://host:port' / 'wss://host' — by running `
|
|
78
|
+
+ `RedDB's HTTP JSON-RPC listener, its WebSocket edge, or an HTTP gateway `
|
|
79
|
+
+ `in front of the server.`,
|
|
79
80
|
)
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
/**
|
|
83
84
|
* Connect to a remote RedDB instance from a browser.
|
|
84
85
|
*
|
|
85
|
-
* @param {string} uri Connection URI.
|
|
86
|
-
* browser; other schemes raise `BROWSER_TRANSPORT_UNSUPPORTED`.
|
|
86
|
+
* @param {string} uri Connection URI. `http(s)://` and `ws(s)://` are
|
|
87
|
+
* reachable from a browser; other schemes raise `BROWSER_TRANSPORT_UNSUPPORTED`.
|
|
87
88
|
* @param {object} [options]
|
|
88
89
|
* @param {object} [options.auth] Authentication credentials.
|
|
89
90
|
* @param {string} [options.auth.token] Bearer / API-key token.
|
|
@@ -128,11 +129,12 @@ export async function connect(uri, options = {}) {
|
|
|
128
129
|
// a WSS the sandbox can open. The TLS edge enforces the Origin allowlist
|
|
129
130
|
// and WSS-only on its side; here we just open the socket and run the
|
|
130
131
|
// standard RedWire handshake over it.
|
|
131
|
-
if (parsed.kind === 'redwss') {
|
|
132
|
+
if (parsed.kind === 'redws' || parsed.kind === 'redwss') {
|
|
132
133
|
const merged = mergeAuthFromUri(parsed, options.auth)
|
|
133
134
|
let token = merged.token
|
|
134
135
|
if (!token && merged.username && merged.password) {
|
|
135
|
-
const
|
|
136
|
+
const httpScheme = parsed.tls === false ? 'http' : 'https'
|
|
137
|
+
const loginUrl = merged.loginUrl ?? `${httpScheme}://${parsed.host}:${parsed.port}/auth/login`
|
|
136
138
|
const session = await login(loginUrl, {
|
|
137
139
|
username: merged.username,
|
|
138
140
|
password: merged.password,
|
|
@@ -140,7 +142,8 @@ export async function connect(uri, options = {}) {
|
|
|
140
142
|
token = session.token
|
|
141
143
|
}
|
|
142
144
|
const auth = token ? { kind: 'bearer', token } : { kind: 'anonymous' }
|
|
143
|
-
const
|
|
145
|
+
const wsScheme = parsed.tls === false ? 'ws' : 'wss'
|
|
146
|
+
const url = `${wsScheme}://${parsed.host}:${parsed.port}${REDWIRE_WS_PATH}`
|
|
144
147
|
const client = await connectRedwireWs({ url, auth })
|
|
145
148
|
return new RedDB(client)
|
|
146
149
|
}
|
package/src/redwire-ws.js
CHANGED
|
@@ -128,7 +128,7 @@ function waitOpen(ws) {
|
|
|
128
128
|
* Open a RedWire connection over a binary WebSocket.
|
|
129
129
|
*
|
|
130
130
|
* @param {object} opts
|
|
131
|
-
* @param {string} [opts.url] `
|
|
131
|
+
* @param {string} [opts.url] `ws(s)://host:port/redwire` endpoint.
|
|
132
132
|
* @param {{ kind: 'anonymous' } | { kind: 'bearer', token: string }} [opts.auth]
|
|
133
133
|
* @param {string} [opts.clientName]
|
|
134
134
|
* @param {string} [opts.subprotocol] Override the advertised subprotocol.
|
|
@@ -153,10 +153,10 @@ export async function connectRedwireWs(opts = {}) {
|
|
|
153
153
|
'no global WebSocket in this runtime; pass opts.WebSocketImpl',
|
|
154
154
|
)
|
|
155
155
|
}
|
|
156
|
-
if (typeof url !== 'string' || !url.startsWith('wss://')) {
|
|
156
|
+
if (typeof url !== 'string' || !(url.startsWith('wss://') || url.startsWith('ws://'))) {
|
|
157
157
|
throw new RedDBError(
|
|
158
|
-
'
|
|
159
|
-
`redwire websocket requires a wss:// url, got '${url}'`,
|
|
158
|
+
'WEBSOCKET_URL_REQUIRED',
|
|
159
|
+
`redwire websocket requires a ws:// or wss:// url, got '${url}'`,
|
|
160
160
|
)
|
|
161
161
|
}
|
|
162
162
|
|