localpreview 0.2.5 → 0.2.6

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.
@@ -1 +1 @@
1
- {"version":3,"file":"cli-ui.d.ts","sourceRoot":"","sources":["../src/cli-ui.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAkB,MAAM,wBAAwB,CAAC;AAIlF,eAAO,MAAM,eAAe,6BAA6B,CAAC;AAE1D,eAAO,MAAM,kBAAkB,GAAI,MAAK,MAAM,CAAC,UAAwB,KAAG,OAGzE,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,KAAG,MAQ/C,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,KAAG,MAQhD,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,SAAS,MAAM,KAAG,MAM9C,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,KAAG,MACT,CAAC;AAE/C,eAAO,MAAM,qBAAqB,GAAI,QAAQ,MAAM,EAAE,MAAM,MAAM,KAAG,MASpE,CAAC;AAEF,eAAO,MAAM,6BAA6B,GAAI,QAAQ,MAAM,EAAE,WAAW,MAAM,KAAG,MAUjF,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,QAAQ,MAAM,EAAE,MAAM,MAAM,EAAE,YAAY,MAAM,KAAG,MAgBvF,CAAC;AAEF,eAAO,MAAM,4BAA4B,GACvC,QAAQ,MAAM,EACd,WAAW,MAAM,EACjB,YAAY,MAAM,KACjB,MAiBF,CAAC;AAIF,eAAO,MAAM,2BAA2B,GAAI,SAAS;IACnD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B,KAAG,MAiBH,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,SAAS,MAAM,KAAG,MASlE,CAAC;AAEF,eAAO,MAAM,8BAA8B,GAAI,SAAS;IACtD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,KAAG,MAaH,CAAC;AAKF,eAAO,MAAM,4BAA4B,QAAO,MAA0C,CAAC;AAE3F,eAAO,MAAM,yBAAyB,QAAO,MACkD,CAAC;AAEhG,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC,CAAC;AAiDF,eAAO,MAAM,gBAAgB,GAAI,UAAS,iBAAsB,KAAG,MASlE,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAO,MAuBjC,CAAC;AAEL,eAAO,MAAM,eAAe,GAAI,UAAS,iBAAsB,KAAG,MA+BjE,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,UAAS,iBAAsB,KAAG,MA+BhE,CAAC;AAaF,eAAO,MAAM,uBAAuB,GAAI,UAAU,mBAAmB,KAAG,MAkEvE,CAAC;AAwCF,eAAO,MAAM,gBAAgB,GAAI,aAAa,MAAM,GAAG,SAAS,KAAG,MA0BlE,CAAC"}
1
+ {"version":3,"file":"cli-ui.d.ts","sourceRoot":"","sources":["../src/cli-ui.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAkB,MAAM,wBAAwB,CAAC;AAIlF,eAAO,MAAM,eAAe,6BAA6B,CAAC;AAE1D,eAAO,MAAM,kBAAkB,GAAI,MAAK,MAAM,CAAC,UAAwB,KAAG,OAGzE,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,KAAG,MAQ/C,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,KAAG,MAQhD,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,SAAS,MAAM,KAAG,MAM9C,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,KAAG,MACT,CAAC;AAE/C,eAAO,MAAM,qBAAqB,GAAI,QAAQ,MAAM,EAAE,MAAM,MAAM,KAAG,MASpE,CAAC;AAEF,eAAO,MAAM,6BAA6B,GAAI,QAAQ,MAAM,EAAE,WAAW,MAAM,KAAG,MAUjF,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,QAAQ,MAAM,EAAE,MAAM,MAAM,EAAE,YAAY,MAAM,KAAG,MAgBvF,CAAC;AAEF,eAAO,MAAM,4BAA4B,GACvC,QAAQ,MAAM,EACd,WAAW,MAAM,EACjB,YAAY,MAAM,KACjB,MAiBF,CAAC;AAIF,eAAO,MAAM,2BAA2B,GAAI,SAAS;IACnD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B,KAAG,MAiBH,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,SAAS,MAAM,KAAG,MASlE,CAAC;AAEF,eAAO,MAAM,8BAA8B,GAAI,SAAS;IACtD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,KAAG,MAaH,CAAC;AAKF,eAAO,MAAM,4BAA4B,QAAO,MAA0C,CAAC;AAE3F,eAAO,MAAM,yBAAyB,QAAO,MACkD,CAAC;AAEhG,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC,CAAC;AAiDF,eAAO,MAAM,gBAAgB,GAAI,UAAS,iBAAsB,KAAG,MASlE,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAO,MAuBjC,CAAC;AAEL,eAAO,MAAM,eAAe,GAAI,UAAS,iBAAsB,KAAG,MA+BjE,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,UAAS,iBAAsB,KAAG,MA+BhE,CAAC;AAcF,eAAO,MAAM,uBAAuB,GAAI,UAAU,mBAAmB,KAAG,MAkEvE,CAAC;AAyCF,eAAO,MAAM,gBAAgB,GAAI,aAAa,MAAM,GAAG,SAAS,KAAG,MA0BlE,CAAC"}
package/dist/cli-ui.js CHANGED
@@ -238,6 +238,7 @@ export const renderListHelp = (options = {}) => {
238
238
  };
239
239
  const LIST_COLUMNS = [
240
240
  "kind",
241
+ "state",
241
242
  "subdomain",
242
243
  "tunnel",
243
244
  "age",
@@ -289,6 +290,7 @@ export const formatListTunnelsOutput = (response) => {
289
290
  };
290
291
  const formatListTunnelRow = (item) => [
291
292
  formatListItemKind(item.kind),
293
+ item.status ?? "-",
292
294
  item.subdomain ?? "-",
293
295
  formatShortId(item.tunnelId),
294
296
  formatCompactAge(item.activeForMs),
@@ -1 +1 @@
1
- {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AAQA,OAAO,EAAW,MAAM,EAAiB,MAAM,QAAQ,CAAC;AAkBxD,OAAO,EAAE,aAAa,EAAgB,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAwChF,eAAO,MAAM,MAAM,GACjB,MAAM,aAAa,CAAC,MAAM,CAAC,KAC1B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,GAAG,aAAa,CAmBrD,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,MAAM,aAAa,CAAC,MAAM,CAAC,KAAG,aAAa,CAAC,MAAM,CAC1C,CAAC;AAE1C,eAAO,MAAM,wBAAwB,GACnC,MAAM,aAAa,CAAC,MAAM,CAAC,EAC3B,QAAQ,OAAO,KACd,aAAa,CAAC,MAAM,CAAmE,CAAC"}
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AAQA,OAAO,EAAW,MAAM,EAAiB,MAAM,QAAQ,CAAC;AAuBxD,OAAO,EAAE,aAAa,EAAgB,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAyChF,eAAO,MAAM,MAAM,GACjB,MAAM,aAAa,CAAC,MAAM,CAAC,KAC1B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,GAAG,aAAa,CAmBrD,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,MAAM,aAAa,CAAC,MAAM,CAAC,KAAG,aAAa,CAAC,MAAM,CAC1C,CAAC;AAE1C,eAAO,MAAM,wBAAwB,GACnC,MAAM,aAAa,CAAC,MAAM,CAAC,EAC3B,QAAQ,OAAO,KACd,aAAa,CAAC,MAAM,CAAmE,CAAC"}
package/dist/command.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { formatCaptureOrigin, parseCaptureHostPort, parseTarget, validateRequestedSubdomain, } from "@localpreview/protocol";
2
2
  import { Console, Effect, Layer, Option } from "effect";
3
- import { adminTokenRequiredMessage, formatStatus, formatWarning, formatListTunnelsOutput, renderCleanHelp, renderConnectHelp, renderGlobalHelp, renderListHelp, removedAdminTokenFlagMessage, } from "./cli-ui.js";
4
- import { CliConfig, CliConfigLive, LOCAL_CONTROL_PLANE_URL, normalizeControlPlaneUrl } from "./config.js";
3
+ import { adminTokenRequiredMessage, formatWarning, formatListTunnelsOutput, renderCleanHelp, renderConnectHelp, renderGlobalHelp, renderListHelp, removedAdminTokenFlagMessage, } from "./cli-ui.js";
4
+ import { CliConfig, CliConfigLive, LOCAL_CONTROL_PLANE_PUBLIC_HOST, LOCAL_CONTROL_PLANE_URL, normalizeControlPlaneUrl, } from "./config.js";
5
5
  import { ControlPlaneClient, ControlPlaneClientLive, } from "./control-plane.js";
6
6
  import { CliUsageError, errorMessage } from "./errors.js";
7
7
  import { LocalProxyLive } from "./local-proxy.js";
@@ -178,6 +178,7 @@ const parseConnectArgs = (argv) => {
178
178
  config: {
179
179
  captures,
180
180
  controlPlane: toOption(controlPlane),
181
+ localMode: usedLocalControlPlane,
181
182
  requestedName: toOption(requestedName),
182
183
  target,
183
184
  },
@@ -468,9 +469,10 @@ const runConnect = (config, legacy, originalArgv) => Effect.scoped(Effect.gen(fu
468
469
  const controlPlane = yield* ControlPlaneClient;
469
470
  const relay = yield* RelayClient;
470
471
  const cliConfig = yield* CliConfig;
471
- const tunnel = yield* Effect.acquireRelease(controlPlane.createTunnel(cliConfig.controlPlaneUrl, requestedSubdomain === undefined ? {} : { requestedSubdomain }), (tunnel) => closeTunnelBestEffort(controlPlane, cliConfig.controlPlaneUrl, tunnel));
472
- yield* Console.log(formatStatus(`Tunnel ready: ${tunnel.publicUrl}`));
473
- yield* Console.log(formatStatus(`Forwarding to ${target.target.protocol}://${target.target.hostname}:${target.target.port}`));
472
+ const tunnel = yield* Effect.acquireRelease(controlPlane.createTunnel(cliConfig.controlPlaneUrl, {
473
+ ...(config.localMode ? { publicHost: LOCAL_CONTROL_PLANE_PUBLIC_HOST } : {}),
474
+ ...(requestedSubdomain === undefined ? {} : { requestedSubdomain }),
475
+ }), (tunnel) => closeTunnelBestEffort(controlPlane, cliConfig.controlPlaneUrl, tunnel));
474
476
  if (config.captures.length > 0) {
475
477
  const origins = config.captures.map((capture) => formatCaptureOrigin(capture)).join(", ");
476
478
  yield* Console.error(formatWarning(`Captured local backends (${origins}) are exposed through this preview URL. Anyone with the link can reach them.`));
package/dist/config.d.ts CHANGED
@@ -4,6 +4,7 @@ export type CliConfigShape = {
4
4
  readonly controlPlaneUrl: string;
5
5
  readonly maxInFlightRequests: number;
6
6
  readonly relayConnectTimeoutMs: number;
7
+ readonly relayReadyTimeoutMs: number;
7
8
  readonly requestBodyLimitBytes: number;
8
9
  readonly requestTimeoutMs: number;
9
10
  readonly responseBodyLimitBytes: number;
@@ -13,7 +14,8 @@ declare const CliConfig_base: Context.ServiceClass<CliConfig, "CliConfig", CliCo
13
14
  export declare class CliConfig extends CliConfig_base {
14
15
  }
15
16
  export declare const PUBLIC_CONTROL_PLANE_URL = "https://localpreview.dev";
16
- export declare const LOCAL_CONTROL_PLANE_URL = "http://localhost:3000";
17
+ export declare const LOCAL_CONTROL_PLANE_URL = "http://127.0.0.1:3000";
18
+ export declare const LOCAL_CONTROL_PLANE_PUBLIC_HOST = "localhost:3000";
17
19
  export type NormalizeControlPlaneUrlResult = {
18
20
  readonly ok: true;
19
21
  readonly url: string;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAEhD,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;CACzC,CAAC;;AAEF,qBAAa,SAAU,SAAQ,cAAyD;CAAG;AAE3F,eAAO,MAAM,wBAAwB,6BAA6B,CAAC;AACnE,eAAO,MAAM,uBAAuB,0BAA0B,CAAC;AAE/D,MAAM,MAAM,8BAA8B,GACtC;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB,GACD;IACE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;CACpB,CAAC;AAEN,gFAAgF;AAChF,eAAO,MAAM,wBAAwB,GAAI,OAAO,MAAM,KAAG,8BA4DxD,CAAC;AA2BF,eAAO,MAAM,aAAa,GAAI,OAAO;IACnC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC,KAAG,cAkBH,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,OAAO;IACnC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC,yCAAmD,CAAC;AAWrD,eAAO,MAAM,aAAa,GAAI,OAAO;IACnC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC,KAAG,MAAM,CAAC,MAAM,CAAC,cAAc,CAA4C,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAEhD,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;CACzC,CAAC;;AAEF,qBAAa,SAAU,SAAQ,cAAyD;CAAG;AAE3F,eAAO,MAAM,wBAAwB,6BAA6B,CAAC;AACnE,eAAO,MAAM,uBAAuB,0BAA0B,CAAC;AAC/D,eAAO,MAAM,+BAA+B,mBAAmB,CAAC;AAEhE,MAAM,MAAM,8BAA8B,GACtC;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB,GACD;IACE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;CACpB,CAAC;AAEN,gFAAgF;AAChF,eAAO,MAAM,wBAAwB,GAAI,OAAO,MAAM,KAAG,8BA4DxD,CAAC;AA2BF,eAAO,MAAM,aAAa,GAAI,OAAO;IACnC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC,KAAG,cAmBH,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,OAAO;IACnC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC,yCAAmD,CAAC;AAWrD,eAAO,MAAM,aAAa,GAAI,OAAO;IACnC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC,KAAG,MAAM,CAAC,MAAM,CAAC,cAAc,CAA4C,CAAC"}
package/dist/config.js CHANGED
@@ -3,7 +3,8 @@ import { Context, Effect, Layer } from "effect";
3
3
  export class CliConfig extends Context.Service()("CliConfig") {
4
4
  }
5
5
  export const PUBLIC_CONTROL_PLANE_URL = LOCALPREVIEW_PUBLIC_ORIGIN;
6
- export const LOCAL_CONTROL_PLANE_URL = "http://localhost:3000";
6
+ export const LOCAL_CONTROL_PLANE_URL = "http://127.0.0.1:3000";
7
+ export const LOCAL_CONTROL_PLANE_PUBLIC_HOST = "localhost:3000";
7
8
  /** Normalizes and validates a control-plane base URL from `--control-plane`. */
8
9
  export const normalizeControlPlaneUrl = (value) => {
9
10
  let parsed;
@@ -84,6 +85,7 @@ export const makeCliConfig = (input) => {
84
85
  controlPlaneUrl,
85
86
  maxInFlightRequests: readNumber(env.LOCALPREVIEW_MAX_IN_FLIGHT_REQUESTS, 100),
86
87
  relayConnectTimeoutMs: readNumber(env.LOCALPREVIEW_RELAY_CONNECT_TIMEOUT_MS, 10_000),
88
+ relayReadyTimeoutMs: readNumber(env.LOCALPREVIEW_RELAY_READY_TIMEOUT_MS, 60_000),
87
89
  requestBodyLimitBytes: readNumber(env.LOCALPREVIEW_REQUEST_BODY_LIMIT_BYTES, 10 * 1024 * 1024),
88
90
  requestTimeoutMs: readNumber(env.LOCALPREVIEW_REQUEST_TIMEOUT_MS, 30_000),
89
91
  responseBodyLimitBytes: readNumber(env.LOCALPREVIEW_RESPONSE_BODY_LIMIT_BYTES, 50 * 1024 * 1024),
@@ -1,4 +1,4 @@
1
- import { type CreateTunnelResponse, type ListTunnelsResponse } from "@localpreview/protocol";
1
+ import { type CreateTunnelRequest, type CreateTunnelResponse, type ListTunnelsResponse } from "@localpreview/protocol";
2
2
  import { Context, Effect, Layer } from "effect";
3
3
  import { ControlPlaneError } from "./errors.js";
4
4
  export type ControlPlaneClientShape = {
@@ -12,9 +12,7 @@ export type ControlPlaneClientShape = {
12
12
  readonly subdomain: string;
13
13
  }) => Effect.Effect<CleanSubdomainResponse, ControlPlaneError>;
14
14
  readonly closeTunnel: (controlPlaneUrl: string, tunnel: Pick<CreateTunnelResponse, "clientToken" | "tunnelId">) => Effect.Effect<void, ControlPlaneError>;
15
- readonly createTunnel: (controlPlaneUrl: string, body: {
16
- readonly requestedSubdomain?: string;
17
- }) => Effect.Effect<CreateTunnelResponse, ControlPlaneError>;
15
+ readonly createTunnel: (controlPlaneUrl: string, body: CreateTunnelRequest) => Effect.Effect<CreateTunnelResponse, ControlPlaneError>;
18
16
  readonly listTunnels: (controlPlaneUrl: string, input: {
19
17
  readonly adminToken: string;
20
18
  readonly limit: number;
@@ -47,9 +45,7 @@ declare const ControlPlaneClient_base: Context.ServiceClass<ControlPlaneClient,
47
45
  export declare class ControlPlaneClient extends ControlPlaneClient_base {
48
46
  }
49
47
  export declare const ControlPlaneClientLive: Layer.Layer<ControlPlaneClient, never, never>;
50
- export declare const createTunnel: (controlPlaneUrl: string, body: {
51
- readonly requestedSubdomain?: string;
52
- }) => Promise<CreateTunnelResponse>;
48
+ export declare const createTunnel: (controlPlaneUrl: string, body: CreateTunnelRequest) => Promise<CreateTunnelResponse>;
53
49
  export declare const closeTunnel: (controlPlaneUrl: string, tunnel: Pick<CreateTunnelResponse, "clientToken" | "tunnelId">) => Promise<void>;
54
50
  export declare const cleanSubdomain: (controlPlaneUrl: string, input: {
55
51
  readonly adminToken: string;
@@ -1 +1 @@
1
- {"version":3,"file":"control-plane.d.ts","sourceRoot":"","sources":["../src/control-plane.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"control-plane.d.ts","sourceRoot":"","sources":["../src/control-plane.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { LOCALPREVIEW_ADMIN_TOKEN_HEADER, LOCALPREVIEW_ERROR_CODES, LOCALPREVIEW_PROTOCOL_VERSION, LOCALPREVIEW_RELAY_SNAPSHOT_VERSION, } from "@localpreview/protocol";
2
2
  import { Context, Effect, Layer, Schedule } from "effect";
3
+ import { LOCAL_CONTROL_PLANE_URL } from "./config.js";
3
4
  import { ControlPlaneError } from "./errors.js";
4
5
  export class ControlPlaneClient extends Context.Service()("ControlPlaneClient") {
5
6
  }
@@ -20,8 +21,9 @@ export const cleanAllSubdomains = (controlPlaneUrl, input) => Effect.runPromise(
20
21
  export const listTunnels = (controlPlaneUrl, input) => Effect.runPromise(listTunnelsEffect(controlPlaneUrl, input));
21
22
  const createTunnelEffect = (controlPlaneUrl, body) => Effect.promise(async () => {
22
23
  let response;
24
+ const createTunnelUrl = new URL("/api/tunnels", controlPlaneUrl);
23
25
  try {
24
- response = await fetch(new URL("/api/tunnels", controlPlaneUrl), {
26
+ response = await fetch(createTunnelUrl, {
25
27
  body: JSON.stringify(body),
26
28
  headers: {
27
29
  "content-type": "application/json",
@@ -33,7 +35,7 @@ const createTunnelEffect = (controlPlaneUrl, body) => Effect.promise(async () =>
33
35
  throw new ControlPlaneError({
34
36
  message: [
35
37
  `Could not reach localpreview control-plane at ${controlPlaneUrl}.`,
36
- 'Use "-l" or "--local" as shorthand for --control-plane http://localhost:3000.',
38
+ `Use "-l" or "--local" as shorthand for --control-plane ${LOCAL_CONTROL_PLANE_URL}.`,
37
39
  ].join("\n"),
38
40
  retryable: false,
39
41
  });
@@ -1,5 +1,5 @@
1
- import type { CaptureTarget, CreateTunnelResponse, TunnelTarget } from "@localpreview/protocol";
2
- import { Context, Effect, Layer } from "effect";
1
+ import { type CaptureTarget, type CreateTunnelResponse, type TunnelTarget } from "@localpreview/protocol";
2
+ import { Context, Deferred, Effect, Layer } from "effect";
3
3
  import { CliConfig } from "./config.js";
4
4
  import { RelayConnectionError, RelayProtocolError } from "./errors.js";
5
5
  import { LocalProxy } from "./local-proxy.js";
@@ -13,7 +13,12 @@ export type RelaySessionMetadata = {
13
13
  declare const RelayClient_base: Context.ServiceClass<RelayClient, "RelayClient", RelayClientShape>;
14
14
  export declare class RelayClient extends RelayClient_base {
15
15
  }
16
- export declare const RelayClientLive: Layer.Layer<RelayClient, never, LocalProxy | CliConfig>;
16
+ export declare const RelayClientLive: Layer.Layer<RelayClient, never, CliConfig | LocalProxy>;
17
+ export declare const waitForTunnelReady: (input: {
18
+ readonly ready: Deferred.Deferred<void, RelayConnectionError>;
19
+ readonly terminatedBeforeReady: Deferred.Deferred<void, RelayConnectionError>;
20
+ readonly timeoutMs: number;
21
+ }) => Effect.Effect<void, RelayConnectionError>;
17
22
  export declare const isRetryableCloseCode: (code: number) => boolean;
18
23
  export {};
19
24
  //# sourceMappingURL=relay-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"relay-client.d.ts","sourceRoot":"","sources":["../src/relay-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAChG,OAAO,EAAW,OAAO,EAAY,MAAM,EAAE,KAAK,EAAY,MAAM,QAAQ,CAAC;AAG7E,OAAO,EAAE,SAAS,EAAuB,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAgB,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,UAAU,EAAwB,MAAM,kBAAkB,CAAC;AAEpE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,eAAe,EAAE,CACxB,MAAM,EAAE,oBAAoB,EAC5B,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,EACtC,eAAe,EAAE,oBAAoB,KAClC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,oBAAoB,GAAG,kBAAkB,CAAC,CAAC;CACrE,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC9C,CAAC;;AAEF,qBAAa,WAAY,SAAQ,gBAA+D;CAAG;AAEnG,eAAO,MAAM,eAAe,yDAU3B,CAAC;AAwMF,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,KAAG,OAAwB,CAAC"}
1
+ {"version":3,"file":"relay-client.d.ts","sourceRoot":"","sources":["../src/relay-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,aAAa,EAAE,KAAK,oBAAoB,EAAE,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACpI,OAAO,EAAW,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAY,MAAM,QAAQ,CAAC;AAG7E,OAAO,EAAE,SAAS,EAAuB,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAgB,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,UAAU,EAAwB,MAAM,kBAAkB,CAAC;AAEpE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,eAAe,EAAE,CACxB,MAAM,EAAE,oBAAoB,EAC5B,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,aAAa,CAAC,aAAa,CAAC,EACtC,eAAe,EAAE,oBAAoB,KAClC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,oBAAoB,GAAG,kBAAkB,CAAC,CAAC;CACrE,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC9C,CAAC;;AAEF,qBAAa,WAAY,SAAQ,gBAA+D;CAAG;AAEnG,eAAO,MAAM,eAAe,yDAU3B,CAAC;AA0OF,eAAO,MAAM,kBAAkB,GAAI,OAAO;IACxC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC;IAC9D,QAAQ,CAAC,qBAAqB,EAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC;IAC9E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,oBAAoB,CAczC,CAAC;AAEJ,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,KAAG,OAAwB,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { decodeServerRelayMessage } from "@localpreview/protocol";
1
2
  import { Console, Context, Deferred, Effect, Layer, Schedule } from "effect";
2
3
  import WebSocket from "ws";
3
4
  import { formatRelayReconnect, formatStatus } from "./cli-ui.js";
@@ -33,6 +34,9 @@ const serveOnce = (config, localProxy, tunnel, target, captures, sessionMetadata
33
34
  target,
34
35
  };
35
36
  const done = yield* Deferred.make();
37
+ const ready = yield* Deferred.make();
38
+ const terminatedBeforeReady = yield* Deferred.make();
39
+ let tunnelReady = false;
36
40
  const handleSignal = () => {
37
41
  socket.close(1000, "Interrupted");
38
42
  Effect.runFork(Deferred.succeed(done, undefined));
@@ -42,6 +46,14 @@ const serveOnce = (config, localProxy, tunnel, target, captures, sessionMetadata
42
46
  process.once("SIGINT", handleSignal);
43
47
  process.once("SIGTERM", handleSignal);
44
48
  socket.on("message", (data) => {
49
+ const decoded = decodeServerRelayMessage(data.toString());
50
+ if (decoded.ok &&
51
+ decoded.message.type === "tunnel-ready" &&
52
+ decoded.message.tunnelId === tunnel.tunnelId) {
53
+ tunnelReady = true;
54
+ Effect.runFork(Deferred.succeed(ready, undefined));
55
+ return;
56
+ }
45
57
  const effect = localProxy.handleMessage(socket, session, data.toString()).pipe(Effect.catch((error) => Effect.gen(function* () {
46
58
  socket.close(1002, error.message);
47
59
  yield* Deferred.fail(done, error);
@@ -49,19 +61,23 @@ const serveOnce = (config, localProxy, tunnel, target, captures, sessionMetadata
49
61
  Effect.runFork(effect);
50
62
  });
51
63
  socket.on("error", (error) => {
52
- Effect.runFork(Deferred.fail(done, new RelayConnectionError({
64
+ const relayError = new RelayConnectionError({
53
65
  message: error.message,
54
- })));
66
+ });
67
+ Effect.runFork(Deferred.fail(done, relayError).pipe(Effect.andThen(Deferred.fail(terminatedBeforeReady, relayError))));
55
68
  });
56
69
  socket.on("close", (code, reason) => {
70
+ const relayError = new RelayConnectionError({
71
+ message: tunnelReady
72
+ ? `Relay connection closed (${code}): ${reason.toString()}`
73
+ : `Relay connection closed before tunnel-ready (${code}): ${reason.toString()}`,
74
+ retryable: isRetryableCloseCode(code),
75
+ });
57
76
  if (code === 1000 || code === 1001) {
58
- Effect.runFork(Deferred.succeed(done, undefined));
77
+ Effect.runFork(Deferred.fail(terminatedBeforeReady, relayError).pipe(Effect.andThen(Deferred.succeed(done, undefined))));
59
78
  return;
60
79
  }
61
- Effect.runFork(Deferred.fail(done, new RelayConnectionError({
62
- message: `Relay connection closed (${code}): ${reason.toString()}`,
63
- retryable: isRetryableCloseCode(code),
64
- })));
80
+ Effect.runFork(Deferred.fail(done, relayError).pipe(Effect.andThen(Deferred.fail(terminatedBeforeReady, relayError))));
65
81
  });
66
82
  }), () => Effect.sync(() => {
67
83
  process.off("SIGINT", handleSignal);
@@ -70,6 +86,13 @@ const serveOnce = (config, localProxy, tunnel, target, captures, sessionMetadata
70
86
  socket.close(1000, "Closing");
71
87
  }
72
88
  }));
89
+ yield* waitForTunnelReady({
90
+ ready,
91
+ terminatedBeforeReady,
92
+ timeoutMs: config.relayReadyTimeoutMs,
93
+ });
94
+ yield* Console.log(formatStatus(`Tunnel ready: ${tunnel.publicUrl}`));
95
+ yield* Console.log(formatStatus(`Forwarding to ${target.protocol}://${target.hostname}:${target.port}`));
73
96
  yield* Deferred.await(done);
74
97
  }));
75
98
  });
@@ -120,4 +143,10 @@ const openSocket = (tunnel) => Effect.callback((resume) => {
120
143
  }).pipe(Effect.mapError((error) => new RelayConnectionError({
121
144
  message: errorMessage(error),
122
145
  })));
146
+ export const waitForTunnelReady = (input) => Effect.raceFirst(Deferred.await(input.ready), Deferred.await(input.terminatedBeforeReady)).pipe(Effect.timeoutOrElse({
147
+ duration: `${input.timeoutMs} millis`,
148
+ orElse: () => Effect.fail(new RelayConnectionError({
149
+ message: "Timed out waiting for relay tunnel-ready acknowledgement.",
150
+ })),
151
+ }));
123
152
  export const isRetryableCloseCode = (code) => code === 1006;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "localpreview",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "bin": {
5
5
  "localpreview": "./dist/index.js"
6
6
  },
@@ -10,28 +10,26 @@
10
10
  "README.md"
11
11
  ],
12
12
  "types": "./dist/index.d.ts",
13
- "scripts": {
14
- "build": "tsc -p tsconfig.build.json",
15
- "dev": "tsx src/index.ts",
16
- "lint": "oxlint .",
17
- "prepack": "pnpm build",
18
- "prepublishOnly": "pnpm test && pnpm typecheck && pnpm build",
19
- "test": "vitest run --passWithNoTests",
20
- "typecheck": "tsc -p tsconfig.json --noEmit"
21
- },
22
13
  "dependencies": {
23
14
  "@effect/platform-node": "beta",
24
- "@localpreview/protocol": "workspace:*",
25
15
  "effect": "beta",
26
16
  "picocolors": "^1.1.1",
27
- "ws": "latest"
17
+ "ws": "latest",
18
+ "@localpreview/protocol": "0.2.3"
28
19
  },
29
20
  "devDependencies": {
30
- "@effect/vitest": "catalog:",
31
- "@types/node": "catalog:",
32
- "@types/ws": "catalog:",
33
- "tsx": "catalog:",
34
- "typescript": "catalog:",
35
- "vitest": "catalog:"
21
+ "@effect/vitest": "beta",
22
+ "@types/node": "latest",
23
+ "@types/ws": "latest",
24
+ "tsx": "latest",
25
+ "typescript": "latest",
26
+ "vitest": "latest"
27
+ },
28
+ "scripts": {
29
+ "build": "tsc -p tsconfig.build.json",
30
+ "dev": "tsx src/index.ts",
31
+ "lint": "oxlint .",
32
+ "test": "vitest run --passWithNoTests",
33
+ "typecheck": "tsc -p tsconfig.json --noEmit"
36
34
  }
37
- }
35
+ }