@reddb-io/client 1.9.1 → 1.10.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/index.browser.d.ts +7 -6
- package/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/core/url.js +37 -1
- package/src/index.browser.js +23 -0
- package/src/redwire-core.js +1144 -0
- package/src/redwire-ws.js +169 -0
- package/src/redwire.js +62 -1128
package/index.browser.d.ts
CHANGED
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
* - It omits `splitNdjson()` (a `node:stream` `Transform`), which has no
|
|
14
14
|
* browser counterpart.
|
|
15
15
|
*
|
|
16
|
-
* `connect()`
|
|
17
|
-
* `red(s)://`, and `pg`
|
|
18
|
-
* `'BROWSER_TRANSPORT_UNSUPPORTED'`, and
|
|
19
|
-
* `EmbeddedNotSupported`.
|
|
16
|
+
* `connect()` reaches `http(s)://` and `red+wss://` (RedWire-over-binary-
|
|
17
|
+
* WebSocket, #937) from a browser; `grpc(s)://`, `red(s)://`, and `pg`
|
|
18
|
+
* throw `RedDBError` with code `'BROWSER_TRANSPORT_UNSUPPORTED'`, and
|
|
19
|
+
* embedded URIs throw `EmbeddedNotSupported`.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
export type AuthOptions =
|
|
@@ -422,7 +422,8 @@ export class RedDB {
|
|
|
422
422
|
/**
|
|
423
423
|
* Connect to a remote RedDB instance from a browser.
|
|
424
424
|
*
|
|
425
|
-
*
|
|
425
|
+
* `http://host:port`, `https://host:port`, and `red+wss://host:port`
|
|
426
|
+
* (RedWire-over-binary-WebSocket, #937) are reachable from a browser
|
|
426
427
|
* sandbox. `grpc(s)://`, `red(s)://`, and `pg` throw `RedDBError` with code
|
|
427
428
|
* `'BROWSER_TRANSPORT_UNSUPPORTED'`. Embedded URIs (`memory://`, `memory:`,
|
|
428
429
|
* `file:///path`, `red:///`, `red://:memory[:]`) throw `EmbeddedNotSupported`.
|
|
@@ -436,7 +437,7 @@ export function login(
|
|
|
436
437
|
): Promise<LoginResult>
|
|
437
438
|
|
|
438
439
|
export interface ParsedUri {
|
|
439
|
-
kind: 'embedded' | 'http' | 'https' | 'red' | 'reds' | 'grpc' | 'grpcs' | 'pg'
|
|
440
|
+
kind: 'embedded' | 'http' | 'https' | 'red' | 'reds' | 'redwss' | 'grpc' | 'grpcs' | 'pg'
|
|
440
441
|
host?: string
|
|
441
442
|
port?: number
|
|
442
443
|
path?: string
|
package/index.d.ts
CHANGED
|
@@ -443,7 +443,7 @@ export function login(
|
|
|
443
443
|
): Promise<LoginResult>
|
|
444
444
|
|
|
445
445
|
export interface ParsedUri {
|
|
446
|
-
kind: 'embedded' | 'http' | 'https' | 'red' | 'reds' | 'grpc' | 'grpcs' | 'pg'
|
|
446
|
+
kind: 'embedded' | 'http' | 'https' | 'red' | 'reds' | 'redwss' | 'grpc' | 'grpcs' | 'pg'
|
|
447
447
|
host?: string
|
|
448
448
|
port?: number
|
|
449
449
|
path?: string
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reddb-io/client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.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' | 'grpc' | 'grpcs' | 'pg'} kind
|
|
23
|
+
* @property {'embedded' | 'http' | 'https' | 'red' | 'reds' | 'redwss' | 'grpc' | 'grpcs' | 'pg'} kind
|
|
24
24
|
* @property {string} [host]
|
|
25
25
|
* @property {number} [port]
|
|
26
26
|
* @property {string} [path] // for embedded `file://`-equivalent
|
|
@@ -47,12 +47,48 @@ export function parseUri(uri) {
|
|
|
47
47
|
"connect() requires a URI string (e.g. 'red://localhost:5050' or 'red:///data.rdb')",
|
|
48
48
|
)
|
|
49
49
|
}
|
|
50
|
+
if (uri.startsWith('red+wss://')) {
|
|
51
|
+
return parseRedWssUrl(uri)
|
|
52
|
+
}
|
|
50
53
|
if (uri.startsWith('red://') || uri === 'red:' || uri === 'red:/') {
|
|
51
54
|
return parseRedUrl(uri)
|
|
52
55
|
}
|
|
53
56
|
return parseLegacyUrl(uri)
|
|
54
57
|
}
|
|
55
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Parse `red+wss://[user:pass@]host:port[?token=...]` — RedWire framing
|
|
61
|
+
* tunneled over a binary WebSocket (the browser transport; #937, ADR
|
|
62
|
+
* 0036). Resolves to a `wss://host:port/redwire` endpoint downstream.
|
|
63
|
+
* Default port is 443 (the TLS edge).
|
|
64
|
+
*/
|
|
65
|
+
export function parseRedWssUrl(uri) {
|
|
66
|
+
let parsed
|
|
67
|
+
try {
|
|
68
|
+
// Borrow the URL parser via an `https://` authority so user / pass /
|
|
69
|
+
// host / port / query all decode with the standard rules.
|
|
70
|
+
parsed = new URL('https://' + uri.slice('red+wss://'.length))
|
|
71
|
+
} catch (err) {
|
|
72
|
+
throw new RedDBError('UNPARSEABLE_URI', `failed to parse '${uri}': ${err.message}`)
|
|
73
|
+
}
|
|
74
|
+
if (!parsed.hostname) {
|
|
75
|
+
throw new TypeError(`invalid red+wss:// URI: missing host in '${uri}'`)
|
|
76
|
+
}
|
|
77
|
+
const params = parsed.searchParams
|
|
78
|
+
return {
|
|
79
|
+
kind: 'redwss',
|
|
80
|
+
host: parsed.hostname,
|
|
81
|
+
port: parsed.port ? Number(parsed.port) : 443,
|
|
82
|
+
username: parsed.username ? decodeURIComponent(parsed.username) : undefined,
|
|
83
|
+
password: parsed.password ? decodeURIComponent(parsed.password) : undefined,
|
|
84
|
+
token: params.get('token') ?? undefined,
|
|
85
|
+
apiKey: params.get('apiKey') ?? params.get('api_key') ?? undefined,
|
|
86
|
+
loginUrl: params.get('loginUrl') ?? params.get('login_url') ?? undefined,
|
|
87
|
+
params,
|
|
88
|
+
originalUri: uri,
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
56
92
|
/**
|
|
57
93
|
* Parse a `red://` URL.
|
|
58
94
|
*
|
package/src/index.browser.js
CHANGED
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
mergeAuthFromUri,
|
|
41
41
|
} from './core/index.js'
|
|
42
42
|
import { HttpRpcClient } from './http.js'
|
|
43
|
+
import { connectRedwireWs, REDWIRE_WS_PATH } from './redwire-ws.js'
|
|
43
44
|
import { createSelectStream, createInputStream } from './streaming-web.js'
|
|
44
45
|
|
|
45
46
|
export { RedDBError, EmbeddedNotSupported, EMBEDDED_REJECTION_MESSAGE, isEmbeddedUri }
|
|
@@ -122,6 +123,28 @@ export async function connect(uri, options = {}) {
|
|
|
122
123
|
return new RedDB(client)
|
|
123
124
|
}
|
|
124
125
|
|
|
126
|
+
// RedWire-over-binary-WebSocket (#937, ADR 0036): the browser speaks the
|
|
127
|
+
// same multiplexed binary protocol as the native drivers, tunneled over
|
|
128
|
+
// a WSS the sandbox can open. The TLS edge enforces the Origin allowlist
|
|
129
|
+
// and WSS-only on its side; here we just open the socket and run the
|
|
130
|
+
// standard RedWire handshake over it.
|
|
131
|
+
if (parsed.kind === 'redwss') {
|
|
132
|
+
const merged = mergeAuthFromUri(parsed, options.auth)
|
|
133
|
+
let token = merged.token
|
|
134
|
+
if (!token && merged.username && merged.password) {
|
|
135
|
+
const loginUrl = merged.loginUrl ?? `https://${parsed.host}:${parsed.port}/auth/login`
|
|
136
|
+
const session = await login(loginUrl, {
|
|
137
|
+
username: merged.username,
|
|
138
|
+
password: merged.password,
|
|
139
|
+
})
|
|
140
|
+
token = session.token
|
|
141
|
+
}
|
|
142
|
+
const auth = token ? { kind: 'bearer', token } : { kind: 'anonymous' }
|
|
143
|
+
const url = `wss://${parsed.host}:${parsed.port}${REDWIRE_WS_PATH}`
|
|
144
|
+
const client = await connectRedwireWs({ url, auth })
|
|
145
|
+
return new RedDB(client)
|
|
146
|
+
}
|
|
147
|
+
|
|
125
148
|
if (
|
|
126
149
|
parsed.kind === 'grpc'
|
|
127
150
|
|| parsed.kind === 'grpcs'
|