@localpreview/protocol 0.1.0 → 0.2.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 +161 -0
- package/dist/capture.d.ts +37 -0
- package/dist/capture.d.ts.map +1 -0
- package/dist/capture.js +97 -0
- package/dist/errors.d.ts +34 -4
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +47 -12
- package/dist/headers.d.ts +16 -7
- package/dist/headers.d.ts.map +1 -1
- package/dist/headers.js +9 -12
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/messages.d.ts +33 -114
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +25 -14
- package/dist/public-domain.d.ts +2 -0
- package/dist/public-domain.d.ts.map +1 -1
- package/dist/public-domain.js +2 -0
- package/dist/subdomain.d.ts +9 -1
- package/dist/subdomain.d.ts.map +1 -1
- package/dist/subdomain.js +7 -0
- package/dist/target.d.ts +10 -1
- package/dist/target.d.ts.map +1 -1
- package/dist/target.js +7 -0
- package/dist/tunnel.d.ts +2 -18
- package/dist/tunnel.d.ts.map +1 -1
- package/dist/tunnel.js +1 -7
- package/dist/version.d.ts +17 -2
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +14 -1
- package/package.json +11 -9
package/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# @localpreview/protocol
|
|
2
|
+
|
|
3
|
+
Shared protocol contract for LocalPreview packages.
|
|
4
|
+
|
|
5
|
+
This package contains the stable shapes and helpers used by the CLI,
|
|
6
|
+
control-plane, and relay. Keep runtime-specific code out of this package; it
|
|
7
|
+
should stay small, dependency-light, and safe to import from every LocalPreview
|
|
8
|
+
runtime.
|
|
9
|
+
|
|
10
|
+
## What Lives Here
|
|
11
|
+
|
|
12
|
+
- Public LocalPreview origin constants.
|
|
13
|
+
- Tunnel creation and relay registration response/request shapes.
|
|
14
|
+
- Relay WebSocket message encoding and validation.
|
|
15
|
+
- Shared error codes and error response shape.
|
|
16
|
+
- Header filtering helpers for proxy boundaries.
|
|
17
|
+
- Target and requested subdomain validation.
|
|
18
|
+
- Protocol and relay snapshot version constants.
|
|
19
|
+
|
|
20
|
+
## Relay Message Flow
|
|
21
|
+
|
|
22
|
+
The relay protocol multiplexes browser HTTP requests over the CLI WebSocket.
|
|
23
|
+
Messages are JSON strings and every message belongs to a `requestId`.
|
|
24
|
+
|
|
25
|
+
```txt
|
|
26
|
+
browser request
|
|
27
|
+
-> relay sends request-start/request-chunk/request-end to CLI
|
|
28
|
+
-> CLI forwards to local target
|
|
29
|
+
-> CLI sends response-start/response-chunk/response-end to relay
|
|
30
|
+
-> relay streams response back to browser
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Server-to-client messages are decoded with `decodeServerRelayMessage`.
|
|
34
|
+
Client-to-server messages are decoded with `decodeClientRelayMessage`.
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import {
|
|
38
|
+
decodeServerRelayMessage,
|
|
39
|
+
encodeRelayMessage,
|
|
40
|
+
type ServerMessage,
|
|
41
|
+
} from "@localpreview/protocol";
|
|
42
|
+
|
|
43
|
+
const message: ServerMessage = {
|
|
44
|
+
type: "request-start",
|
|
45
|
+
requestId: "req_1",
|
|
46
|
+
method: "GET",
|
|
47
|
+
path: "/hello",
|
|
48
|
+
headers: [["host", "example.test"]],
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const encoded = encodeRelayMessage(message);
|
|
52
|
+
const decoded = decodeServerRelayMessage(encoded);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Decoders return `{ ok: true, message }` or `{ ok: false, error }`. They do not
|
|
56
|
+
throw on invalid JSON or invalid message shapes.
|
|
57
|
+
|
|
58
|
+
Request and response body chunks are sent as base64 strings in `chunkBase64`.
|
|
59
|
+
Headers are represented as ordered `[name, value]` pairs rather than an object
|
|
60
|
+
map so duplicate headers such as `set-cookie` are preserved.
|
|
61
|
+
|
|
62
|
+
## Targets
|
|
63
|
+
|
|
64
|
+
`parseTarget` normalizes CLI target input into a `TunnelTarget`.
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { parseTarget } from "@localpreview/protocol";
|
|
68
|
+
|
|
69
|
+
parseTarget("4000");
|
|
70
|
+
// { ok: true, target: { protocol: "http", hostname: "127.0.0.1", port: 4000 } }
|
|
71
|
+
|
|
72
|
+
parseTarget("https://localhost:4000");
|
|
73
|
+
// { ok: true, target: { protocol: "https", hostname: "localhost", port: 4000 } }
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Rules:
|
|
77
|
+
|
|
78
|
+
- A bare port means `http://127.0.0.1:<port>`.
|
|
79
|
+
- URL targets default to `http` when no protocol is provided.
|
|
80
|
+
- URL targets must use `http` or `https`.
|
|
81
|
+
- URL targets must include an explicit port.
|
|
82
|
+
- Ports must be integers from `1` to `65535`.
|
|
83
|
+
|
|
84
|
+
## Requested Subdomains
|
|
85
|
+
|
|
86
|
+
`validateRequestedSubdomain` trims and lowercases user input, rejects reserved
|
|
87
|
+
names, and enforces the public subdomain format.
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { validateRequestedSubdomain } from "@localpreview/protocol";
|
|
91
|
+
|
|
92
|
+
validateRequestedSubdomain("Proyecto-API");
|
|
93
|
+
// { valid: true, subdomain: "proyecto-api" }
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Valid requested subdomains:
|
|
97
|
+
|
|
98
|
+
- Start and end with a lowercase letter or number after normalization.
|
|
99
|
+
- May contain lowercase letters, numbers, and hyphens.
|
|
100
|
+
- Must not use reserved names such as `api`, `app`, `auth`, `dashboard`, or
|
|
101
|
+
`www`.
|
|
102
|
+
|
|
103
|
+
## Headers
|
|
104
|
+
|
|
105
|
+
Proxy boundaries should remove hop-by-hop headers and internal LocalPreview
|
|
106
|
+
headers before forwarding requests across trust boundaries.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import {
|
|
110
|
+
filterEndToEndHeaderPairs,
|
|
111
|
+
filterInternalLocalPreviewHeaderPairs,
|
|
112
|
+
} from "@localpreview/protocol";
|
|
113
|
+
|
|
114
|
+
const publicHeaders = filterInternalLocalPreviewHeaderPairs(
|
|
115
|
+
filterEndToEndHeaderPairs(headers),
|
|
116
|
+
);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Use `flattenHeaderPairs` when an HTTP adapter expects alternating
|
|
120
|
+
`name, value, name, value` entries.
|
|
121
|
+
|
|
122
|
+
## Errors
|
|
123
|
+
|
|
124
|
+
`LOCALPREVIEW_ERROR_CODES` is the shared code registry for CLI, control-plane,
|
|
125
|
+
and relay failures. API responses should use the `LocalPreviewError` shape so
|
|
126
|
+
clients can branch on `error.code` instead of parsing messages.
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
import { LOCALPREVIEW_ERROR_CODES, makeLocalPreviewError } from "@localpreview/protocol";
|
|
130
|
+
|
|
131
|
+
const error = makeLocalPreviewError({
|
|
132
|
+
code: LOCALPREVIEW_ERROR_CODES.TUNNEL_NOT_FOUND,
|
|
133
|
+
message: "Tunnel not found.",
|
|
134
|
+
tunnelId: "tun_123",
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Versioning
|
|
139
|
+
|
|
140
|
+
`LOCALPREVIEW_PROTOCOL_VERSION` identifies the wire and HTTP contract expected
|
|
141
|
+
by all LocalPreview packages.
|
|
142
|
+
|
|
143
|
+
`LOCALPREVIEW_RELAY_SNAPSHOT_VERSION` identifies the relay snapshot build
|
|
144
|
+
contract used by production sandboxes.
|
|
145
|
+
|
|
146
|
+
Change `LOCALPREVIEW_PROTOCOL_VERSION` when a deployed CLI, control-plane, or
|
|
147
|
+
relay would no longer understand the same request/response/message shapes.
|
|
148
|
+
Change `LOCALPREVIEW_RELAY_SNAPSHOT_VERSION` when the relay snapshot must be
|
|
149
|
+
rebuilt or promoted even if the protocol version stays compatible.
|
|
150
|
+
|
|
151
|
+
See `../../docs/relay-snapshot.md` for the production snapshot runbook.
|
|
152
|
+
|
|
153
|
+
## Development
|
|
154
|
+
|
|
155
|
+
From the repo root:
|
|
156
|
+
|
|
157
|
+
```sh
|
|
158
|
+
pnpm --filter @localpreview/protocol test
|
|
159
|
+
pnpm --filter @localpreview/protocol typecheck
|
|
160
|
+
pnpm --filter @localpreview/protocol build
|
|
161
|
+
```
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { TunnelTarget } from "./target.js";
|
|
2
|
+
/** Local backend endpoint registered via `--capture host:port`. */
|
|
3
|
+
export type CaptureTarget = {
|
|
4
|
+
readonly hostname: string;
|
|
5
|
+
readonly port: number;
|
|
6
|
+
};
|
|
7
|
+
type ParseCaptureResult = {
|
|
8
|
+
readonly capture: CaptureTarget;
|
|
9
|
+
readonly ok: true;
|
|
10
|
+
} | {
|
|
11
|
+
readonly message: string;
|
|
12
|
+
readonly ok: false;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Returns true when `hostname` is an allowed loopback host for capture.
|
|
16
|
+
*
|
|
17
|
+
* Allowed values: `localhost`, `127.0.0.1`, `[::1]` / `::1`, and `*.localhost`.
|
|
18
|
+
*/
|
|
19
|
+
export declare const isLoopbackHost: (hostname: string) => boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Parses `--capture host:port` input into a normalized capture target.
|
|
22
|
+
*
|
|
23
|
+
* The host must be loopback-only. Port must be between 1 and 65535.
|
|
24
|
+
*/
|
|
25
|
+
export declare const parseCaptureHostPort: (input: string) => ParseCaptureResult;
|
|
26
|
+
/** Canonicalizes loopback hostnames for stable allowlist matching. */
|
|
27
|
+
export declare const normalizeLoopbackHostname: (hostname: string) => string;
|
|
28
|
+
/** Returns true when the capture matches the given target host and port. */
|
|
29
|
+
export declare const captureMatchesTarget: (capture: CaptureTarget, hostname: string, port: number) => boolean;
|
|
30
|
+
/** Finds an allowlisted capture for the given host and port. */
|
|
31
|
+
export declare const findCapture: (captures: ReadonlyArray<CaptureTarget>, hostname: string, port: number) => CaptureTarget | undefined;
|
|
32
|
+
/** Formats a capture target as `host:port` for CLI output. */
|
|
33
|
+
export declare const formatCaptureOrigin: (capture: CaptureTarget) => string;
|
|
34
|
+
/** Converts a capture target into a tunnel target using HTTP by default. */
|
|
35
|
+
export declare const captureToTunnelTarget: (capture: CaptureTarget, protocol: TunnelTarget["protocol"]) => TunnelTarget;
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.d.ts","sourceRoot":"","sources":["../src/capture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,mEAAmE;AACnE,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,KAAK,kBAAkB,GACnB;IACE,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;CACnB,GACD;IACE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;CACpB,CAAC;AAEN;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,UAAU,MAAM,KAAG,OAYjD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO,MAAM,KAAG,kBAiCpD,CAAC;AAEF,sEAAsE;AACtE,eAAO,MAAM,yBAAyB,GAAI,UAAU,MAAM,KAAG,MAQ5D,CAAC;AAEF,4EAA4E;AAC5E,eAAO,MAAM,oBAAoB,GAC/B,SAAS,aAAa,EACtB,UAAU,MAAM,EAChB,MAAM,MAAM,KACX,OAEoB,CAAC;AAExB,gEAAgE;AAChE,eAAO,MAAM,WAAW,GACtB,UAAU,aAAa,CAAC,aAAa,CAAC,EACtC,UAAU,MAAM,EAChB,MAAM,MAAM,KACX,aAAa,GAAG,SACwD,CAAC;AAE5E,8DAA8D;AAC9D,eAAO,MAAM,mBAAmB,GAAI,SAAS,aAAa,KAAG,MACtB,CAAC;AAExC,4EAA4E;AAC5E,eAAO,MAAM,qBAAqB,GAChC,SAAS,aAAa,EACtB,UAAU,YAAY,CAAC,UAAU,CAAC,KACjC,YAID,CAAC"}
|
package/dist/capture.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns true when `hostname` is an allowed loopback host for capture.
|
|
3
|
+
*
|
|
4
|
+
* Allowed values: `localhost`, `127.0.0.1`, `[::1]` / `::1`, and `*.localhost`.
|
|
5
|
+
*/
|
|
6
|
+
export const isLoopbackHost = (hostname) => {
|
|
7
|
+
const lower = hostname.toLowerCase();
|
|
8
|
+
if (lower === "localhost" || lower === "127.0.0.1") {
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
if (lower === "[::1]" || lower === "::1") {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
return lower.endsWith(".localhost");
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Parses `--capture host:port` input into a normalized capture target.
|
|
18
|
+
*
|
|
19
|
+
* The host must be loopback-only. Port must be between 1 and 65535.
|
|
20
|
+
*/
|
|
21
|
+
export const parseCaptureHostPort = (input) => {
|
|
22
|
+
const trimmed = input.trim();
|
|
23
|
+
if (trimmed.length === 0) {
|
|
24
|
+
return { ok: false, message: "Capture must be host:port, like localhost:4000." };
|
|
25
|
+
}
|
|
26
|
+
const parsed = splitHostPort(trimmed);
|
|
27
|
+
if (!parsed.ok) {
|
|
28
|
+
return parsed;
|
|
29
|
+
}
|
|
30
|
+
const { hostname, port } = parsed;
|
|
31
|
+
if (!isLoopbackHost(hostname)) {
|
|
32
|
+
return {
|
|
33
|
+
ok: false,
|
|
34
|
+
message: `Capture host "${hostname}" must be loopback (localhost, 127.0.0.1, [::1], or *.localhost).`,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (!Number.isInteger(port) || port < 1 || port > 65_535) {
|
|
38
|
+
return { ok: false, message: "Capture port must be between 1 and 65535." };
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
ok: true,
|
|
42
|
+
capture: {
|
|
43
|
+
hostname: normalizeLoopbackHostname(hostname),
|
|
44
|
+
port,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
/** Canonicalizes loopback hostnames for stable allowlist matching. */
|
|
49
|
+
export const normalizeLoopbackHostname = (hostname) => {
|
|
50
|
+
const lower = hostname.toLowerCase();
|
|
51
|
+
if (lower === "::1") {
|
|
52
|
+
return "[::1]";
|
|
53
|
+
}
|
|
54
|
+
return lower;
|
|
55
|
+
};
|
|
56
|
+
/** Returns true when the capture matches the given target host and port. */
|
|
57
|
+
export const captureMatchesTarget = (capture, hostname, port) => normalizeLoopbackHostname(hostname) === normalizeLoopbackHostname(capture.hostname) &&
|
|
58
|
+
capture.port === port;
|
|
59
|
+
/** Finds an allowlisted capture for the given host and port. */
|
|
60
|
+
export const findCapture = (captures, hostname, port) => captures.find((capture) => captureMatchesTarget(capture, hostname, port));
|
|
61
|
+
/** Formats a capture target as `host:port` for CLI output. */
|
|
62
|
+
export const formatCaptureOrigin = (capture) => `${capture.hostname}:${capture.port}`;
|
|
63
|
+
/** Converts a capture target into a tunnel target using HTTP by default. */
|
|
64
|
+
export const captureToTunnelTarget = (capture, protocol) => ({
|
|
65
|
+
hostname: capture.hostname,
|
|
66
|
+
port: capture.port,
|
|
67
|
+
protocol,
|
|
68
|
+
});
|
|
69
|
+
const splitHostPort = (input) => {
|
|
70
|
+
if (input.startsWith("[")) {
|
|
71
|
+
const closingBracket = input.indexOf("]");
|
|
72
|
+
if (closingBracket === -1 || input[closingBracket + 1] !== ":") {
|
|
73
|
+
return { ok: false, message: "Capture must be host:port, like [::1]:4000." };
|
|
74
|
+
}
|
|
75
|
+
const hostname = input.slice(0, closingBracket + 1);
|
|
76
|
+
const portText = input.slice(closingBracket + 2);
|
|
77
|
+
if (portText.length === 0) {
|
|
78
|
+
return { ok: false, message: "Capture must include a port." };
|
|
79
|
+
}
|
|
80
|
+
const port = Number(portText);
|
|
81
|
+
if (!/^\d+$/.test(portText)) {
|
|
82
|
+
return { ok: false, message: "Capture port must be a number." };
|
|
83
|
+
}
|
|
84
|
+
return { ok: true, hostname, port };
|
|
85
|
+
}
|
|
86
|
+
const separator = input.lastIndexOf(":");
|
|
87
|
+
if (separator <= 0 || separator === input.length - 1) {
|
|
88
|
+
return { ok: false, message: "Capture must be host:port, like localhost:4000." };
|
|
89
|
+
}
|
|
90
|
+
const hostname = input.slice(0, separator);
|
|
91
|
+
const portText = input.slice(separator + 1);
|
|
92
|
+
const port = Number(portText);
|
|
93
|
+
if (!/^\d+$/.test(portText)) {
|
|
94
|
+
return { ok: false, message: "Capture port must be a number." };
|
|
95
|
+
}
|
|
96
|
+
return { ok: true, hostname, port };
|
|
97
|
+
};
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,13 +1,43 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
readonly
|
|
2
|
+
/** Shared machine-readable error codes used across LocalPreview packages. */
|
|
3
|
+
export declare const LOCALPREVIEW_ERROR_CODES: {
|
|
4
|
+
readonly BODY_TOO_LARGE: "BODY_TOO_LARGE";
|
|
5
|
+
readonly BULK_CLEANUP_REQUIRES_FORCE: "BULK_CLEANUP_REQUIRES_FORCE";
|
|
6
|
+
readonly CLEANUP_DISABLED: "CLEANUP_DISABLED";
|
|
7
|
+
readonly CONFIG_ERROR: "CONFIG_ERROR";
|
|
8
|
+
readonly INTERNAL_ERROR: "INTERNAL_ERROR";
|
|
9
|
+
readonly INVALID_JSON: "INVALID_JSON";
|
|
10
|
+
readonly INVALID_SUBDOMAIN: "INVALID_SUBDOMAIN";
|
|
11
|
+
readonly RELAY_HEALTH_CHECK_FAILED: "RELAY_HEALTH_CHECK_FAILED";
|
|
12
|
+
readonly RELAY_REGISTRATION_FAILED: "RELAY_REGISTRATION_FAILED";
|
|
13
|
+
readonly REQUEST_FAILED: "REQUEST_FAILED";
|
|
14
|
+
readonly REQUEST_TIMEOUT: "REQUEST_TIMEOUT";
|
|
15
|
+
readonly SANDBOX_CREATE_FAILED: "SANDBOX_CREATE_FAILED";
|
|
16
|
+
readonly SANDBOX_STOP_FAILED: "SANDBOX_STOP_FAILED";
|
|
17
|
+
readonly STORE_FAILED: "STORE_FAILED";
|
|
18
|
+
readonly SUBDOMAIN_ACTIVE: "SUBDOMAIN_ACTIVE";
|
|
19
|
+
readonly SUBDOMAIN_TAKEN: "SUBDOMAIN_TAKEN";
|
|
20
|
+
readonly TUNNEL_HOST_NOT_FOUND: "TUNNEL_HOST_NOT_FOUND";
|
|
21
|
+
readonly TUNNEL_NOT_CONNECTED: "TUNNEL_NOT_CONNECTED";
|
|
22
|
+
readonly TUNNEL_NOT_FOUND: "TUNNEL_NOT_FOUND";
|
|
23
|
+
readonly UNAUTHORIZED: "UNAUTHORIZED";
|
|
24
|
+
};
|
|
25
|
+
declare const LocalPreviewErrorSchema: Schema.Struct<{
|
|
26
|
+
readonly code: Schema.Literals<readonly ["BODY_TOO_LARGE", "BULK_CLEANUP_REQUIRES_FORCE", "CLEANUP_DISABLED", "CONFIG_ERROR", "INTERNAL_ERROR", "INVALID_JSON", "INVALID_SUBDOMAIN", "RELAY_HEALTH_CHECK_FAILED", "RELAY_REGISTRATION_FAILED", "REQUEST_FAILED", "REQUEST_TIMEOUT", "SANDBOX_CREATE_FAILED", "SANDBOX_STOP_FAILED", "STORE_FAILED", "SUBDOMAIN_ACTIVE", "SUBDOMAIN_TAKEN", "TUNNEL_HOST_NOT_FOUND", "TUNNEL_NOT_CONNECTED", "TUNNEL_NOT_FOUND", "UNAUTHORIZED"]>;
|
|
6
27
|
readonly message: Schema.String;
|
|
7
28
|
readonly requestId: Schema.optional<Schema.String>;
|
|
8
29
|
readonly subdomain: Schema.optional<Schema.String>;
|
|
30
|
+
readonly tunnelId: Schema.optional<Schema.String>;
|
|
9
31
|
readonly limitBytes: Schema.optional<Schema.Number>;
|
|
10
32
|
}>;
|
|
33
|
+
/**
|
|
34
|
+
* Shared API error shape.
|
|
35
|
+
*
|
|
36
|
+
* Callers should branch on `code` and treat `message` as human-readable context.
|
|
37
|
+
* Optional fields add request-specific identifiers or limits when available.
|
|
38
|
+
*/
|
|
11
39
|
export type LocalPreviewError = Schema.Schema.Type<typeof LocalPreviewErrorSchema>;
|
|
40
|
+
/** Preserves literal error-code inference when constructing LocalPreview errors. */
|
|
12
41
|
export declare const makeLocalPreviewError: (error: LocalPreviewError) => LocalPreviewError;
|
|
42
|
+
export {};
|
|
13
43
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,6EAA6E;AAC7E,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;CAqB3B,CAAC;AAyBX,QAAA,MAAM,uBAAuB;;;;;;;EAO3B,CAAC;AAEH;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAEnF,oFAAoF;AACpF,eAAO,MAAM,qBAAqB,GAAI,OAAO,iBAAiB,KAAG,iBAA0B,CAAC"}
|
package/dist/errors.js
CHANGED
|
@@ -1,21 +1,56 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
2
|
+
/** Shared machine-readable error codes used across LocalPreview packages. */
|
|
3
|
+
export const LOCALPREVIEW_ERROR_CODES = {
|
|
4
|
+
BODY_TOO_LARGE: "BODY_TOO_LARGE",
|
|
5
|
+
BULK_CLEANUP_REQUIRES_FORCE: "BULK_CLEANUP_REQUIRES_FORCE",
|
|
6
|
+
CLEANUP_DISABLED: "CLEANUP_DISABLED",
|
|
7
|
+
CONFIG_ERROR: "CONFIG_ERROR",
|
|
8
|
+
INTERNAL_ERROR: "INTERNAL_ERROR",
|
|
9
|
+
INVALID_JSON: "INVALID_JSON",
|
|
10
|
+
INVALID_SUBDOMAIN: "INVALID_SUBDOMAIN",
|
|
11
|
+
RELAY_HEALTH_CHECK_FAILED: "RELAY_HEALTH_CHECK_FAILED",
|
|
12
|
+
RELAY_REGISTRATION_FAILED: "RELAY_REGISTRATION_FAILED",
|
|
13
|
+
REQUEST_FAILED: "REQUEST_FAILED",
|
|
14
|
+
REQUEST_TIMEOUT: "REQUEST_TIMEOUT",
|
|
15
|
+
SANDBOX_CREATE_FAILED: "SANDBOX_CREATE_FAILED",
|
|
16
|
+
SANDBOX_STOP_FAILED: "SANDBOX_STOP_FAILED",
|
|
17
|
+
STORE_FAILED: "STORE_FAILED",
|
|
18
|
+
SUBDOMAIN_ACTIVE: "SUBDOMAIN_ACTIVE",
|
|
19
|
+
SUBDOMAIN_TAKEN: "SUBDOMAIN_TAKEN",
|
|
20
|
+
TUNNEL_HOST_NOT_FOUND: "TUNNEL_HOST_NOT_FOUND",
|
|
21
|
+
TUNNEL_NOT_CONNECTED: "TUNNEL_NOT_CONNECTED",
|
|
22
|
+
TUNNEL_NOT_FOUND: "TUNNEL_NOT_FOUND",
|
|
23
|
+
UNAUTHORIZED: "UNAUTHORIZED",
|
|
24
|
+
};
|
|
25
|
+
const ErrorCodeSchema = Schema.Literals([
|
|
26
|
+
LOCALPREVIEW_ERROR_CODES.BODY_TOO_LARGE,
|
|
27
|
+
LOCALPREVIEW_ERROR_CODES.BULK_CLEANUP_REQUIRES_FORCE,
|
|
28
|
+
LOCALPREVIEW_ERROR_CODES.CLEANUP_DISABLED,
|
|
29
|
+
LOCALPREVIEW_ERROR_CODES.CONFIG_ERROR,
|
|
30
|
+
LOCALPREVIEW_ERROR_CODES.INTERNAL_ERROR,
|
|
31
|
+
LOCALPREVIEW_ERROR_CODES.INVALID_JSON,
|
|
32
|
+
LOCALPREVIEW_ERROR_CODES.INVALID_SUBDOMAIN,
|
|
33
|
+
LOCALPREVIEW_ERROR_CODES.RELAY_HEALTH_CHECK_FAILED,
|
|
34
|
+
LOCALPREVIEW_ERROR_CODES.RELAY_REGISTRATION_FAILED,
|
|
35
|
+
LOCALPREVIEW_ERROR_CODES.REQUEST_FAILED,
|
|
36
|
+
LOCALPREVIEW_ERROR_CODES.REQUEST_TIMEOUT,
|
|
37
|
+
LOCALPREVIEW_ERROR_CODES.SANDBOX_CREATE_FAILED,
|
|
38
|
+
LOCALPREVIEW_ERROR_CODES.SANDBOX_STOP_FAILED,
|
|
39
|
+
LOCALPREVIEW_ERROR_CODES.STORE_FAILED,
|
|
40
|
+
LOCALPREVIEW_ERROR_CODES.SUBDOMAIN_ACTIVE,
|
|
41
|
+
LOCALPREVIEW_ERROR_CODES.SUBDOMAIN_TAKEN,
|
|
42
|
+
LOCALPREVIEW_ERROR_CODES.TUNNEL_HOST_NOT_FOUND,
|
|
43
|
+
LOCALPREVIEW_ERROR_CODES.TUNNEL_NOT_CONNECTED,
|
|
44
|
+
LOCALPREVIEW_ERROR_CODES.TUNNEL_NOT_FOUND,
|
|
45
|
+
LOCALPREVIEW_ERROR_CODES.UNAUTHORIZED,
|
|
13
46
|
]);
|
|
14
|
-
|
|
47
|
+
const LocalPreviewErrorSchema = Schema.Struct({
|
|
15
48
|
code: ErrorCodeSchema,
|
|
16
49
|
message: Schema.String,
|
|
17
50
|
requestId: Schema.optional(Schema.String),
|
|
18
51
|
subdomain: Schema.optional(Schema.String),
|
|
52
|
+
tunnelId: Schema.optional(Schema.String),
|
|
19
53
|
limitBytes: Schema.optional(Schema.Number),
|
|
20
54
|
});
|
|
55
|
+
/** Preserves literal error-code inference when constructing LocalPreview errors. */
|
|
21
56
|
export const makeLocalPreviewError = (error) => error;
|
package/dist/headers.d.ts
CHANGED
|
@@ -1,19 +1,28 @@
|
|
|
1
|
+
/** Header used to authenticate administrative control-plane operations. */
|
|
1
2
|
export declare const LOCALPREVIEW_ADMIN_TOKEN_HEADER = "x-localpreview-admin-token";
|
|
3
|
+
/** Header used by trusted proxies to preserve the original public host. */
|
|
2
4
|
export declare const LOCALPREVIEW_FORWARDED_HOST_HEADER = "x-localpreview-forwarded-host";
|
|
5
|
+
/** Header used by trusted proxies to preserve the original public protocol. */
|
|
3
6
|
export declare const LOCALPREVIEW_FORWARDED_PROTO_HEADER = "x-localpreview-forwarded-proto";
|
|
7
|
+
/** Header used to authenticate relay-to-control-plane proxy requests. */
|
|
4
8
|
export declare const LOCALPREVIEW_PROXY_TOKEN_HEADER = "x-localpreview-proxy-token";
|
|
5
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Ordered HTTP header pair.
|
|
11
|
+
*
|
|
12
|
+
* Headers are represented as pairs instead of object maps so duplicate headers,
|
|
13
|
+
* especially `set-cookie`, survive relay and proxy boundaries.
|
|
14
|
+
*/
|
|
6
15
|
export type HeaderPair = readonly [name: string, value: string];
|
|
16
|
+
/** Ordered HTTP header list that may contain duplicate header names. */
|
|
7
17
|
export type HeaderPairs = ReadonlyArray<HeaderPair>;
|
|
18
|
+
/** Returns true when a header must not be forwarded across proxy hops. */
|
|
8
19
|
export declare const isHopByHopHeader: (name: string) => boolean;
|
|
20
|
+
/** Returns true when a header is internal LocalPreview control metadata. */
|
|
9
21
|
export declare const isInternalLocalPreviewHeader: (name: string) => boolean;
|
|
10
|
-
|
|
11
|
-
export declare const filterInternalLocalPreviewHeaders: (headers: HeaderMap) => HeaderMap;
|
|
22
|
+
/** Removes hop-by-hop headers while preserving original header order. */
|
|
12
23
|
export declare const filterEndToEndHeaderPairs: (headers: HeaderPairs) => HeaderPairs;
|
|
24
|
+
/** Removes LocalPreview internal headers before forwarding to untrusted targets. */
|
|
13
25
|
export declare const filterInternalLocalPreviewHeaderPairs: (headers: HeaderPairs) => HeaderPairs;
|
|
14
|
-
|
|
15
|
-
export declare const headerMapToPairs: (headers: HeaderMap) => HeaderPairs;
|
|
26
|
+
/** Converts header pairs to alternating name/value entries for HTTP adapters. */
|
|
16
27
|
export declare const flattenHeaderPairs: (headers: HeaderPairs) => Array<string>;
|
|
17
|
-
export declare const headersToMap: (headers: Headers) => HeaderMap;
|
|
18
|
-
export declare const recordToHeaders: (headers: HeaderMap) => Headers;
|
|
19
28
|
//# sourceMappingURL=headers.d.ts.map
|
package/dist/headers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../src/headers.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,+BAA+B,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../src/headers.ts"],"names":[],"mappings":"AAWA,2EAA2E;AAC3E,eAAO,MAAM,+BAA+B,+BAA+B,CAAC;AAE5E,2EAA2E;AAC3E,eAAO,MAAM,kCAAkC,kCAAkC,CAAC;AAElF,+EAA+E;AAC/E,eAAO,MAAM,mCAAmC,mCAAmC,CAAC;AAEpF,yEAAyE;AACzE,eAAO,MAAM,+BAA+B,+BAA+B,CAAC;AAS5E;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;AAEhE,wEAAwE;AACxE,MAAM,MAAM,WAAW,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;AAEpD,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,KAAG,OAAkD,CAAC;AAEnG,4EAA4E;AAC5E,eAAO,MAAM,4BAA4B,GAAI,MAAM,MAAM,KAAG,OACP,CAAC;AAEtD,yEAAyE;AACzE,eAAO,MAAM,yBAAyB,GAAI,SAAS,WAAW,KAAG,WACZ,CAAC;AAEtD,oFAAoF;AACpF,eAAO,MAAM,qCAAqC,GAAI,SAAS,WAAW,KAAG,WACZ,CAAC;AAElE,iFAAiF;AACjF,eAAO,MAAM,kBAAkB,GAAI,SAAS,WAAW,KAAG,KAAK,CAAC,MAAM,CACnB,CAAC"}
|
package/dist/headers.js
CHANGED
|
@@ -8,9 +8,13 @@ const hopByHopHeaders = new Set([
|
|
|
8
8
|
"transfer-encoding",
|
|
9
9
|
"upgrade",
|
|
10
10
|
]);
|
|
11
|
+
/** Header used to authenticate administrative control-plane operations. */
|
|
11
12
|
export const LOCALPREVIEW_ADMIN_TOKEN_HEADER = "x-localpreview-admin-token";
|
|
13
|
+
/** Header used by trusted proxies to preserve the original public host. */
|
|
12
14
|
export const LOCALPREVIEW_FORWARDED_HOST_HEADER = "x-localpreview-forwarded-host";
|
|
15
|
+
/** Header used by trusted proxies to preserve the original public protocol. */
|
|
13
16
|
export const LOCALPREVIEW_FORWARDED_PROTO_HEADER = "x-localpreview-forwarded-proto";
|
|
17
|
+
/** Header used to authenticate relay-to-control-plane proxy requests. */
|
|
14
18
|
export const LOCALPREVIEW_PROXY_TOKEN_HEADER = "x-localpreview-proxy-token";
|
|
15
19
|
const internalLocalPreviewHeaders = new Set([
|
|
16
20
|
LOCALPREVIEW_ADMIN_TOKEN_HEADER,
|
|
@@ -18,20 +22,13 @@ const internalLocalPreviewHeaders = new Set([
|
|
|
18
22
|
LOCALPREVIEW_FORWARDED_PROTO_HEADER,
|
|
19
23
|
LOCALPREVIEW_PROXY_TOKEN_HEADER,
|
|
20
24
|
]);
|
|
25
|
+
/** Returns true when a header must not be forwarded across proxy hops. */
|
|
21
26
|
export const isHopByHopHeader = (name) => hopByHopHeaders.has(name.toLowerCase());
|
|
27
|
+
/** Returns true when a header is internal LocalPreview control metadata. */
|
|
22
28
|
export const isInternalLocalPreviewHeader = (name) => internalLocalPreviewHeaders.has(name.toLowerCase());
|
|
23
|
-
|
|
24
|
-
export const filterInternalLocalPreviewHeaders = (headers) => Object.fromEntries(Object.entries(headers).filter(([name]) => !isInternalLocalPreviewHeader(name)));
|
|
29
|
+
/** Removes hop-by-hop headers while preserving original header order. */
|
|
25
30
|
export const filterEndToEndHeaderPairs = (headers) => headers.filter(([name]) => !isHopByHopHeader(name));
|
|
31
|
+
/** Removes LocalPreview internal headers before forwarding to untrusted targets. */
|
|
26
32
|
export const filterInternalLocalPreviewHeaderPairs = (headers) => headers.filter(([name]) => !isInternalLocalPreviewHeader(name));
|
|
27
|
-
|
|
28
|
-
export const headerMapToPairs = (headers) => Object.entries(headers);
|
|
33
|
+
/** Converts header pairs to alternating name/value entries for HTTP adapters. */
|
|
29
34
|
export const flattenHeaderPairs = (headers) => headers.flatMap(([name, value]) => [name, value]);
|
|
30
|
-
export const headersToMap = (headers) => Object.fromEntries(headers.entries());
|
|
31
|
-
export const recordToHeaders = (headers) => {
|
|
32
|
-
const result = new Headers();
|
|
33
|
-
for (const [name, value] of Object.entries(headers)) {
|
|
34
|
-
result.set(name, value);
|
|
35
|
-
}
|
|
36
|
-
return result;
|
|
37
|
-
};
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/messages.d.ts
CHANGED
|
@@ -1,114 +1,14 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
2
|
import type { HeaderPairs } from "./headers.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
export declare const RequestChunkMessageSchema: Schema.Struct<{
|
|
13
|
-
readonly chunkBase64: Schema.String;
|
|
14
|
-
readonly requestId: Schema.String;
|
|
15
|
-
readonly type: Schema.Literal<"request-chunk">;
|
|
16
|
-
}>;
|
|
17
|
-
export declare const RequestEndMessageSchema: Schema.Struct<{
|
|
18
|
-
readonly requestId: Schema.String;
|
|
19
|
-
readonly type: Schema.Literal<"request-end">;
|
|
20
|
-
}>;
|
|
21
|
-
export declare const CancelMessageSchema: Schema.Struct<{
|
|
22
|
-
readonly requestId: Schema.String;
|
|
23
|
-
readonly type: Schema.Literal<"cancel">;
|
|
24
|
-
}>;
|
|
25
|
-
export declare const ResponseStartMessageSchema: Schema.Struct<{
|
|
26
|
-
readonly headers: Schema.$Array<Schema.Tuple<readonly [Schema.String, Schema.String]>>;
|
|
27
|
-
readonly requestId: Schema.String;
|
|
28
|
-
readonly status: Schema.Number;
|
|
29
|
-
readonly type: Schema.Literal<"response-start">;
|
|
30
|
-
}>;
|
|
31
|
-
export declare const ResponseChunkMessageSchema: Schema.Struct<{
|
|
32
|
-
readonly chunkBase64: Schema.String;
|
|
33
|
-
readonly requestId: Schema.String;
|
|
34
|
-
readonly type: Schema.Literal<"response-chunk">;
|
|
35
|
-
}>;
|
|
36
|
-
export declare const ResponseEndMessageSchema: Schema.Struct<{
|
|
37
|
-
readonly requestId: Schema.String;
|
|
38
|
-
readonly type: Schema.Literal<"response-end">;
|
|
39
|
-
}>;
|
|
40
|
-
export declare const ResponseErrorMessageSchema: Schema.Struct<{
|
|
41
|
-
readonly message: Schema.String;
|
|
42
|
-
readonly requestId: Schema.String;
|
|
43
|
-
readonly type: Schema.Literal<"response-error">;
|
|
44
|
-
}>;
|
|
45
|
-
export declare const ServerMessageSchema: Schema.Union<readonly [Schema.Struct<{
|
|
46
|
-
readonly headers: Schema.$Array<Schema.Tuple<readonly [Schema.String, Schema.String]>>;
|
|
47
|
-
readonly method: Schema.String;
|
|
48
|
-
readonly path: Schema.String;
|
|
49
|
-
readonly requestId: Schema.String;
|
|
50
|
-
readonly type: Schema.Literal<"request-start">;
|
|
51
|
-
}>, Schema.Struct<{
|
|
52
|
-
readonly chunkBase64: Schema.String;
|
|
53
|
-
readonly requestId: Schema.String;
|
|
54
|
-
readonly type: Schema.Literal<"request-chunk">;
|
|
55
|
-
}>, Schema.Struct<{
|
|
56
|
-
readonly requestId: Schema.String;
|
|
57
|
-
readonly type: Schema.Literal<"request-end">;
|
|
58
|
-
}>, Schema.Struct<{
|
|
59
|
-
readonly requestId: Schema.String;
|
|
60
|
-
readonly type: Schema.Literal<"cancel">;
|
|
61
|
-
}>]>;
|
|
62
|
-
export declare const ClientMessageSchema: Schema.Union<readonly [Schema.Struct<{
|
|
63
|
-
readonly headers: Schema.$Array<Schema.Tuple<readonly [Schema.String, Schema.String]>>;
|
|
64
|
-
readonly requestId: Schema.String;
|
|
65
|
-
readonly status: Schema.Number;
|
|
66
|
-
readonly type: Schema.Literal<"response-start">;
|
|
67
|
-
}>, Schema.Struct<{
|
|
68
|
-
readonly chunkBase64: Schema.String;
|
|
69
|
-
readonly requestId: Schema.String;
|
|
70
|
-
readonly type: Schema.Literal<"response-chunk">;
|
|
71
|
-
}>, Schema.Struct<{
|
|
72
|
-
readonly requestId: Schema.String;
|
|
73
|
-
readonly type: Schema.Literal<"response-end">;
|
|
74
|
-
}>, Schema.Struct<{
|
|
75
|
-
readonly message: Schema.String;
|
|
76
|
-
readonly requestId: Schema.String;
|
|
77
|
-
readonly type: Schema.Literal<"response-error">;
|
|
78
|
-
}>]>;
|
|
79
|
-
export declare const RelayMessageSchema: Schema.Union<readonly [Schema.Union<readonly [Schema.Struct<{
|
|
80
|
-
readonly headers: Schema.$Array<Schema.Tuple<readonly [Schema.String, Schema.String]>>;
|
|
81
|
-
readonly method: Schema.String;
|
|
82
|
-
readonly path: Schema.String;
|
|
83
|
-
readonly requestId: Schema.String;
|
|
84
|
-
readonly type: Schema.Literal<"request-start">;
|
|
85
|
-
}>, Schema.Struct<{
|
|
86
|
-
readonly chunkBase64: Schema.String;
|
|
87
|
-
readonly requestId: Schema.String;
|
|
88
|
-
readonly type: Schema.Literal<"request-chunk">;
|
|
89
|
-
}>, Schema.Struct<{
|
|
90
|
-
readonly requestId: Schema.String;
|
|
91
|
-
readonly type: Schema.Literal<"request-end">;
|
|
92
|
-
}>, Schema.Struct<{
|
|
93
|
-
readonly requestId: Schema.String;
|
|
94
|
-
readonly type: Schema.Literal<"cancel">;
|
|
95
|
-
}>]>, Schema.Union<readonly [Schema.Struct<{
|
|
96
|
-
readonly headers: Schema.$Array<Schema.Tuple<readonly [Schema.String, Schema.String]>>;
|
|
97
|
-
readonly requestId: Schema.String;
|
|
98
|
-
readonly status: Schema.Number;
|
|
99
|
-
readonly type: Schema.Literal<"response-start">;
|
|
100
|
-
}>, Schema.Struct<{
|
|
101
|
-
readonly chunkBase64: Schema.String;
|
|
102
|
-
readonly requestId: Schema.String;
|
|
103
|
-
readonly type: Schema.Literal<"response-chunk">;
|
|
104
|
-
}>, Schema.Struct<{
|
|
105
|
-
readonly requestId: Schema.String;
|
|
106
|
-
readonly type: Schema.Literal<"response-end">;
|
|
107
|
-
}>, Schema.Struct<{
|
|
108
|
-
readonly message: Schema.String;
|
|
109
|
-
readonly requestId: Schema.String;
|
|
110
|
-
readonly type: Schema.Literal<"response-error">;
|
|
111
|
-
}>]>]>;
|
|
3
|
+
declare const RequestIdSchema: Schema.String;
|
|
4
|
+
type RequestId = Schema.Schema.Type<typeof RequestIdSchema>;
|
|
5
|
+
/**
|
|
6
|
+
* Relay messages sent by the relay server to the CLI client.
|
|
7
|
+
*
|
|
8
|
+
* These messages describe the inbound browser request that the CLI must forward
|
|
9
|
+
* to the local target. Body chunks are base64 strings so the wire format can
|
|
10
|
+
* stay JSON while preserving arbitrary bytes.
|
|
11
|
+
*/
|
|
112
12
|
export type ServerMessage = {
|
|
113
13
|
readonly type: "request-start";
|
|
114
14
|
readonly requestId: RequestId;
|
|
@@ -126,7 +26,13 @@ export type ServerMessage = {
|
|
|
126
26
|
readonly type: "cancel";
|
|
127
27
|
readonly requestId: RequestId;
|
|
128
28
|
};
|
|
129
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Relay messages sent by the CLI client back to the relay server.
|
|
31
|
+
*
|
|
32
|
+
* These messages describe the local target response, stream response bytes, or
|
|
33
|
+
* report a request-scoped forwarding failure.
|
|
34
|
+
*/
|
|
35
|
+
type ClientMessage = {
|
|
130
36
|
readonly type: "response-start";
|
|
131
37
|
readonly requestId: RequestId;
|
|
132
38
|
readonly status: number;
|
|
@@ -143,17 +49,30 @@ export type ClientMessage = {
|
|
|
143
49
|
readonly requestId: RequestId;
|
|
144
50
|
readonly message: string;
|
|
145
51
|
};
|
|
146
|
-
|
|
52
|
+
type RelayMessage = ServerMessage | ClientMessage;
|
|
53
|
+
/** Encodes a relay message as the JSON string sent over the WebSocket. */
|
|
147
54
|
export declare const encodeRelayMessage: (message: RelayMessage) => string;
|
|
148
|
-
|
|
149
|
-
export type RelayMessageDecodeResult<A> = {
|
|
55
|
+
type RelayMessageDecodeResult<A> = {
|
|
150
56
|
readonly ok: true;
|
|
151
57
|
readonly message: A;
|
|
152
58
|
} | {
|
|
153
59
|
readonly error: unknown;
|
|
154
60
|
readonly ok: false;
|
|
155
61
|
};
|
|
62
|
+
/**
|
|
63
|
+
* Decodes a relay-to-CLI WebSocket message.
|
|
64
|
+
*
|
|
65
|
+
* Returns a result object instead of throwing for invalid JSON or invalid
|
|
66
|
+
* message shapes, which lets callers close or ignore malformed connections
|
|
67
|
+
* using their own error policy.
|
|
68
|
+
*/
|
|
156
69
|
export declare const decodeServerRelayMessage: (input: string) => RelayMessageDecodeResult<ServerMessage>;
|
|
70
|
+
/**
|
|
71
|
+
* Decodes a CLI-to-relay WebSocket message.
|
|
72
|
+
*
|
|
73
|
+
* Response headers must be ordered pairs, not collapsed maps, so duplicate
|
|
74
|
+
* response headers remain representable.
|
|
75
|
+
*/
|
|
157
76
|
export declare const decodeClientRelayMessage: (input: string) => RelayMessageDecodeResult<ClientMessage>;
|
|
158
|
-
export
|
|
77
|
+
export {};
|
|
159
78
|
//# sourceMappingURL=messages.d.ts.map
|
package/dist/messages.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,QAAA,MAAM,eAAe,eAAgB,CAAC;AACtC,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,eAAe,CAAC,CAAC;AAkE5D;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GACrB;IACE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;CAC/B,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;CAC/B,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;CAC/B,CAAC;AAEN;;;;;GAKG;AACH,KAAK,aAAa,GACd;IACE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;CAC/B,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;CAC/B,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEN,KAAK,YAAY,GAAG,aAAa,GAAG,aAAa,CAAC;AAElD,0EAA0E;AAC1E,eAAO,MAAM,kBAAkB,GAAI,SAAS,YAAY,KAAG,MAAiC,CAAC;AAE7F,KAAK,wBAAwB,CAAC,CAAC,IAC3B;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;CACrB,GACD;IACE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;CACpB,CAAC;AAEN;;;;;;GAMG;AACH,eAAO,MAAM,wBAAwB,GAAI,OAAO,MAAM,KAAG,wBAAwB,CAAC,aAAa,CACjD,CAAC;AAE/C;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,GAAI,OAAO,MAAM,KAAG,wBAAwB,CAAC,aAAa,CACjD,CAAC"}
|
package/dist/messages.js
CHANGED
|
@@ -1,64 +1,75 @@
|
|
|
1
1
|
import { Result, Schema } from "effect";
|
|
2
|
-
|
|
2
|
+
const RequestIdSchema = Schema.String;
|
|
3
3
|
const HeaderPairsSchema = Schema.Array(Schema.Tuple([Schema.String, Schema.String]));
|
|
4
|
-
|
|
4
|
+
const RequestStartMessageSchema = Schema.Struct({
|
|
5
5
|
headers: HeaderPairsSchema,
|
|
6
6
|
method: Schema.String,
|
|
7
7
|
path: Schema.String,
|
|
8
8
|
requestId: RequestIdSchema,
|
|
9
9
|
type: Schema.Literal("request-start"),
|
|
10
10
|
});
|
|
11
|
-
|
|
11
|
+
const RequestChunkMessageSchema = Schema.Struct({
|
|
12
12
|
chunkBase64: Schema.String,
|
|
13
13
|
requestId: RequestIdSchema,
|
|
14
14
|
type: Schema.Literal("request-chunk"),
|
|
15
15
|
});
|
|
16
|
-
|
|
16
|
+
const RequestEndMessageSchema = Schema.Struct({
|
|
17
17
|
requestId: RequestIdSchema,
|
|
18
18
|
type: Schema.Literal("request-end"),
|
|
19
19
|
});
|
|
20
|
-
|
|
20
|
+
const CancelMessageSchema = Schema.Struct({
|
|
21
21
|
requestId: RequestIdSchema,
|
|
22
22
|
type: Schema.Literal("cancel"),
|
|
23
23
|
});
|
|
24
|
-
|
|
24
|
+
const ResponseStartMessageSchema = Schema.Struct({
|
|
25
25
|
headers: HeaderPairsSchema,
|
|
26
26
|
requestId: RequestIdSchema,
|
|
27
27
|
status: Schema.Number,
|
|
28
28
|
type: Schema.Literal("response-start"),
|
|
29
29
|
});
|
|
30
|
-
|
|
30
|
+
const ResponseChunkMessageSchema = Schema.Struct({
|
|
31
31
|
chunkBase64: Schema.String,
|
|
32
32
|
requestId: RequestIdSchema,
|
|
33
33
|
type: Schema.Literal("response-chunk"),
|
|
34
34
|
});
|
|
35
|
-
|
|
35
|
+
const ResponseEndMessageSchema = Schema.Struct({
|
|
36
36
|
requestId: RequestIdSchema,
|
|
37
37
|
type: Schema.Literal("response-end"),
|
|
38
38
|
});
|
|
39
|
-
|
|
39
|
+
const ResponseErrorMessageSchema = Schema.Struct({
|
|
40
40
|
message: Schema.String,
|
|
41
41
|
requestId: RequestIdSchema,
|
|
42
42
|
type: Schema.Literal("response-error"),
|
|
43
43
|
});
|
|
44
|
-
|
|
44
|
+
const ServerMessageSchema = Schema.Union([
|
|
45
45
|
RequestStartMessageSchema,
|
|
46
46
|
RequestChunkMessageSchema,
|
|
47
47
|
RequestEndMessageSchema,
|
|
48
48
|
CancelMessageSchema,
|
|
49
49
|
]);
|
|
50
|
-
|
|
50
|
+
const ClientMessageSchema = Schema.Union([
|
|
51
51
|
ResponseStartMessageSchema,
|
|
52
52
|
ResponseChunkMessageSchema,
|
|
53
53
|
ResponseEndMessageSchema,
|
|
54
54
|
ResponseErrorMessageSchema,
|
|
55
55
|
]);
|
|
56
|
-
|
|
56
|
+
/** Encodes a relay message as the JSON string sent over the WebSocket. */
|
|
57
57
|
export const encodeRelayMessage = (message) => JSON.stringify(message);
|
|
58
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Decodes a relay-to-CLI WebSocket message.
|
|
60
|
+
*
|
|
61
|
+
* Returns a result object instead of throwing for invalid JSON or invalid
|
|
62
|
+
* message shapes, which lets callers close or ignore malformed connections
|
|
63
|
+
* using their own error policy.
|
|
64
|
+
*/
|
|
59
65
|
export const decodeServerRelayMessage = (input) => decodeWithSchema(input, ServerMessageSchema);
|
|
66
|
+
/**
|
|
67
|
+
* Decodes a CLI-to-relay WebSocket message.
|
|
68
|
+
*
|
|
69
|
+
* Response headers must be ordered pairs, not collapsed maps, so duplicate
|
|
70
|
+
* response headers remain representable.
|
|
71
|
+
*/
|
|
60
72
|
export const decodeClientRelayMessage = (input) => decodeWithSchema(input, ClientMessageSchema);
|
|
61
|
-
export const decodeTypedRelayMessage = (input) => decodeWithSchema(input, RelayMessageSchema);
|
|
62
73
|
const decodeWithSchema = (input, schema) => {
|
|
63
74
|
try {
|
|
64
75
|
const result = Schema.decodeUnknownResult(schema)(JSON.parse(input));
|
package/dist/public-domain.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** Canonical public domain used for production LocalPreview URLs. */
|
|
1
2
|
export declare const LOCALPREVIEW_PUBLIC_DOMAIN = "localpreview.dev";
|
|
3
|
+
/** HTTPS origin for the production LocalPreview control-plane. */
|
|
2
4
|
export declare const LOCALPREVIEW_PUBLIC_ORIGIN = "https://localpreview.dev";
|
|
3
5
|
//# sourceMappingURL=public-domain.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-domain.d.ts","sourceRoot":"","sources":["../src/public-domain.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,0BAA0B,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"public-domain.d.ts","sourceRoot":"","sources":["../src/public-domain.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,eAAO,MAAM,0BAA0B,qBAAqB,CAAC;AAE7D,kEAAkE;AAClE,eAAO,MAAM,0BAA0B,6BAA0C,CAAC"}
|
package/dist/public-domain.js
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
+
/** Canonical public domain used for production LocalPreview URLs. */
|
|
1
2
|
export const LOCALPREVIEW_PUBLIC_DOMAIN = "localpreview.dev";
|
|
3
|
+
/** HTTPS origin for the production LocalPreview control-plane. */
|
|
2
4
|
export const LOCALPREVIEW_PUBLIC_ORIGIN = `https://${LOCALPREVIEW_PUBLIC_DOMAIN}`;
|
package/dist/subdomain.d.ts
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
type SubdomainValidationResult = {
|
|
2
2
|
readonly valid: true;
|
|
3
3
|
readonly subdomain: string;
|
|
4
4
|
} | {
|
|
5
5
|
readonly valid: false;
|
|
6
6
|
readonly reason: "invalid-format" | "reserved";
|
|
7
7
|
};
|
|
8
|
+
/**
|
|
9
|
+
* Normalizes and validates a user-requested public subdomain.
|
|
10
|
+
*
|
|
11
|
+
* The returned `subdomain` is trimmed and lowercased. Reserved product routes
|
|
12
|
+
* and names outside the public subdomain format are rejected with separate
|
|
13
|
+
* reasons so callers can produce specific user-facing errors.
|
|
14
|
+
*/
|
|
8
15
|
export declare const validateRequestedSubdomain: (input: string) => SubdomainValidationResult;
|
|
16
|
+
export {};
|
|
9
17
|
//# sourceMappingURL=subdomain.d.ts.map
|
package/dist/subdomain.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subdomain.d.ts","sourceRoot":"","sources":["../src/subdomain.ts"],"names":[],"mappings":"AAaA,
|
|
1
|
+
{"version":3,"file":"subdomain.d.ts","sourceRoot":"","sources":["../src/subdomain.ts"],"names":[],"mappings":"AAaA,KAAK,yBAAyB,GAC1B;IACE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,GACD;IACE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU,CAAC;CAChD,CAAC;AAEN;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,GAAI,OAAO,MAAM,KAAG,yBAY1D,CAAC"}
|
package/dist/subdomain.js
CHANGED
|
@@ -10,6 +10,13 @@ const reservedSubdomains = new Set([
|
|
|
10
10
|
"status",
|
|
11
11
|
"www",
|
|
12
12
|
]);
|
|
13
|
+
/**
|
|
14
|
+
* Normalizes and validates a user-requested public subdomain.
|
|
15
|
+
*
|
|
16
|
+
* The returned `subdomain` is trimmed and lowercased. Reserved product routes
|
|
17
|
+
* and names outside the public subdomain format are rejected with separate
|
|
18
|
+
* reasons so callers can produce specific user-facing errors.
|
|
19
|
+
*/
|
|
13
20
|
export const validateRequestedSubdomain = (input) => {
|
|
14
21
|
const subdomain = input.trim().toLowerCase();
|
|
15
22
|
if (reservedSubdomains.has(subdomain)) {
|
package/dist/target.d.ts
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
|
+
/** Local HTTP(S) endpoint that the CLI forwards tunnel traffic to. */
|
|
1
2
|
export type TunnelTarget = {
|
|
2
3
|
readonly protocol: "http" | "https";
|
|
3
4
|
readonly hostname: string;
|
|
4
5
|
readonly port: number;
|
|
5
6
|
};
|
|
6
|
-
|
|
7
|
+
type ParseTargetResult = {
|
|
7
8
|
readonly ok: true;
|
|
8
9
|
readonly target: TunnelTarget;
|
|
9
10
|
} | {
|
|
10
11
|
readonly ok: false;
|
|
11
12
|
readonly message: string;
|
|
12
13
|
};
|
|
14
|
+
/**
|
|
15
|
+
* Parses CLI target input into a normalized tunnel target.
|
|
16
|
+
*
|
|
17
|
+
* A bare port is treated as `http://127.0.0.1:<port>`. URL-like inputs default
|
|
18
|
+
* to `http` when no protocol is provided, but must include an explicit port and
|
|
19
|
+
* use either `http` or `https`.
|
|
20
|
+
*/
|
|
13
21
|
export declare const parseTarget: (input: string) => ParseTargetResult;
|
|
22
|
+
export {};
|
|
14
23
|
//# sourceMappingURL=target.d.ts.map
|
package/dist/target.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"target.d.ts","sourceRoot":"","sources":["../src/target.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,
|
|
1
|
+
{"version":3,"file":"target.d.ts","sourceRoot":"","sources":["../src/target.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,KAAK,iBAAiB,GAClB;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;CAC/B,GACD;IACE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEN;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,KAAG,iBA4B3C,CAAC"}
|
package/dist/target.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses CLI target input into a normalized tunnel target.
|
|
3
|
+
*
|
|
4
|
+
* A bare port is treated as `http://127.0.0.1:<port>`. URL-like inputs default
|
|
5
|
+
* to `http` when no protocol is provided, but must include an explicit port and
|
|
6
|
+
* use either `http` or `https`.
|
|
7
|
+
*/
|
|
1
8
|
export const parseTarget = (input) => {
|
|
2
9
|
const trimmed = input.trim();
|
|
3
10
|
if (/^\d+$/.test(trimmed)) {
|
package/dist/tunnel.d.ts
CHANGED
|
@@ -1,21 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const TunnelStatusSchema: Schema.Literals<readonly ["starting", "connected", "disconnected", "expired"]>;
|
|
3
|
-
export type TunnelStatus = Schema.Schema.Type<typeof TunnelStatusSchema>;
|
|
4
|
-
export type TunnelRecord = {
|
|
5
|
-
readonly tunnelId: string;
|
|
6
|
-
readonly subdomain: string;
|
|
7
|
-
readonly relayHttpUrl: string;
|
|
8
|
-
readonly relayWsUrl: string;
|
|
9
|
-
readonly proxyToken: string;
|
|
10
|
-
readonly relayProvider?: "local" | "vercel";
|
|
11
|
-
readonly status: TunnelStatus;
|
|
12
|
-
readonly sandboxId?: string;
|
|
13
|
-
readonly createdAt: string;
|
|
14
|
-
readonly expiresAt: string;
|
|
15
|
-
};
|
|
16
|
-
export type CreateTunnelRequest = {
|
|
17
|
-
readonly requestedSubdomain?: string;
|
|
18
|
-
};
|
|
1
|
+
/** Response returned by the control-plane after creating a tunnel session. */
|
|
19
2
|
export type CreateTunnelResponse = {
|
|
20
3
|
readonly tunnelId: string;
|
|
21
4
|
readonly subdomain: string;
|
|
@@ -26,6 +9,7 @@ export type CreateTunnelResponse = {
|
|
|
26
9
|
readonly protocolVersion: string;
|
|
27
10
|
readonly relaySnapshotVersion: string;
|
|
28
11
|
};
|
|
12
|
+
/** Admin request used by the control-plane to register a tunnel with a relay. */
|
|
29
13
|
export type RegisterRelayTunnelRequest = {
|
|
30
14
|
readonly tunnelId: string;
|
|
31
15
|
readonly clientToken: string;
|
package/dist/tunnel.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tunnel.d.ts","sourceRoot":"","sources":["../src/tunnel.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"tunnel.d.ts","sourceRoot":"","sources":["../src/tunnel.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;CACvC,CAAC;AAEF,iFAAiF;AACjF,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B,CAAC"}
|
package/dist/tunnel.js
CHANGED
package/dist/version.d.ts
CHANGED
|
@@ -1,20 +1,35 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
|
+
/**
|
|
3
|
+
* Wire and HTTP contract version expected by CLI, control-plane, and relay.
|
|
4
|
+
*
|
|
5
|
+
* Change this when a deployed component would no longer understand the same
|
|
6
|
+
* public request, response, or relay message shapes.
|
|
7
|
+
*/
|
|
2
8
|
export declare const LOCALPREVIEW_PROTOCOL_VERSION = "1";
|
|
9
|
+
/**
|
|
10
|
+
* Production relay snapshot contract version.
|
|
11
|
+
*
|
|
12
|
+
* Change this when the relay snapshot must be rebuilt or promoted even if the
|
|
13
|
+
* shared protocol remains compatible.
|
|
14
|
+
*/
|
|
3
15
|
export declare const LOCALPREVIEW_RELAY_SNAPSHOT_VERSION = "2026-05-25.1";
|
|
4
|
-
|
|
16
|
+
declare const RelayHealthResponseSchema: Schema.Struct<{
|
|
5
17
|
readonly ok: Schema.Literal<true>;
|
|
6
18
|
readonly protocolVersion: Schema.String;
|
|
7
19
|
readonly relaySnapshotVersion: Schema.String;
|
|
8
20
|
readonly service: Schema.String;
|
|
9
21
|
readonly tunnels: Schema.Number;
|
|
10
22
|
}>;
|
|
23
|
+
/** Health response emitted by relay instances and checked by snapshot tooling. */
|
|
11
24
|
export type RelayHealthResponse = Schema.Schema.Type<typeof RelayHealthResponseSchema>;
|
|
12
|
-
|
|
25
|
+
type RelayHealthDecodeResult = {
|
|
13
26
|
readonly health: RelayHealthResponse;
|
|
14
27
|
readonly ok: true;
|
|
15
28
|
} | {
|
|
16
29
|
readonly error: unknown;
|
|
17
30
|
readonly ok: false;
|
|
18
31
|
};
|
|
32
|
+
/** Validates an unknown relay `/health` response without throwing. */
|
|
19
33
|
export declare const decodeRelayHealthResponse: (input: unknown) => RelayHealthDecodeResult;
|
|
34
|
+
export {};
|
|
20
35
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,eAAO,MAAM,6BAA6B,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,MAAM,CAAC;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,mCAAmC,iBAAiB,CAAC;AAElE,QAAA,MAAM,yBAAyB;;;;;;EAM7B,CAAC;AAEH,kFAAkF;AAClF,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAEvF,KAAK,uBAAuB,GACxB;IACE,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;CACnB,GACD;IACE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;CACpB,CAAC;AAEN,sEAAsE;AACtE,eAAO,MAAM,yBAAyB,GAAI,OAAO,OAAO,KAAG,uBAQ1D,CAAC"}
|
package/dist/version.js
CHANGED
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
import { Result, Schema } from "effect";
|
|
2
|
+
/**
|
|
3
|
+
* Wire and HTTP contract version expected by CLI, control-plane, and relay.
|
|
4
|
+
*
|
|
5
|
+
* Change this when a deployed component would no longer understand the same
|
|
6
|
+
* public request, response, or relay message shapes.
|
|
7
|
+
*/
|
|
2
8
|
export const LOCALPREVIEW_PROTOCOL_VERSION = "1";
|
|
9
|
+
/**
|
|
10
|
+
* Production relay snapshot contract version.
|
|
11
|
+
*
|
|
12
|
+
* Change this when the relay snapshot must be rebuilt or promoted even if the
|
|
13
|
+
* shared protocol remains compatible.
|
|
14
|
+
*/
|
|
3
15
|
export const LOCALPREVIEW_RELAY_SNAPSHOT_VERSION = "2026-05-25.1";
|
|
4
|
-
|
|
16
|
+
const RelayHealthResponseSchema = Schema.Struct({
|
|
5
17
|
ok: Schema.Literal(true),
|
|
6
18
|
protocolVersion: Schema.String,
|
|
7
19
|
relaySnapshotVersion: Schema.String,
|
|
8
20
|
service: Schema.String,
|
|
9
21
|
tunnels: Schema.Number,
|
|
10
22
|
});
|
|
23
|
+
/** Validates an unknown relay `/health` response without throwing. */
|
|
11
24
|
export const decodeRelayHealthResponse = (input) => {
|
|
12
25
|
const result = Schema.decodeUnknownResult(RelayHealthResponseSchema)(input);
|
|
13
26
|
if (Result.isSuccess(result)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@localpreview/protocol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -16,18 +16,20 @@
|
|
|
16
16
|
"publishConfig": {
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
|
-
"dependencies": {
|
|
20
|
-
"effect": "beta"
|
|
21
|
-
},
|
|
22
|
-
"devDependencies": {
|
|
23
|
-
"typescript": "latest",
|
|
24
|
-
"vitest": "latest"
|
|
25
|
-
},
|
|
26
19
|
"scripts": {
|
|
27
20
|
"build": "tsc -p tsconfig.build.json",
|
|
28
21
|
"dev": "tsc -p tsconfig.build.json --watch",
|
|
29
22
|
"lint": "oxlint .",
|
|
23
|
+
"prepack": "pnpm build",
|
|
24
|
+
"prepublishOnly": "pnpm test && pnpm typecheck && pnpm build",
|
|
30
25
|
"test": "vitest run",
|
|
31
26
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"effect": "catalog:"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"typescript": "catalog:",
|
|
33
|
+
"vitest": "catalog:"
|
|
32
34
|
}
|
|
33
|
-
}
|
|
35
|
+
}
|