@nice-code/action 0.4.5 → 0.4.7

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 CHANGED
@@ -154,7 +154,9 @@ export const clientCoord = RuntimeCoordinate.env("frontend");
154
154
  export const serverCoord = RuntimeCoordinate.env("backend");
155
155
 
156
156
  // Transport definitions are plain, reusable objects you construct once.
157
- const serverHttp = new HttpTransport({ url: "https://api.example.com/resolve_action" });
157
+ const serverHttp = HttpTransport.create({
158
+ createRequest: () => ({ url: "https://api.example.com/resolve_action" }),
159
+ });
158
160
 
159
161
  const serverHandler = new ActionExternalClientHandler({
160
162
  runtimeCoordinate: serverCoord,
@@ -218,7 +220,10 @@ function RenameUser() {
218
220
  ```ts
219
221
  import { WebSocketTransport } from "@nice-code/action";
220
222
 
221
- const serverWs = new WebSocketTransport({ url: "wss://api.example.com/resolve_action/ws" });
223
+ const serverWs = WebSocketTransport.create({
224
+ createWebSocket: () => new WebSocket("wss://api.example.com/resolve_action/ws"),
225
+ getTransportCacheKey: () => ["wss://api.example.com/resolve_action/ws"],
226
+ });
222
227
 
223
228
  const serverHandler = new ActionExternalClientHandler({
224
229
  runtimeCoordinate: serverCoord,
@@ -226,12 +231,15 @@ const serverHandler = new ActionExternalClientHandler({
226
231
  }).forDomain(userDomain);
227
232
  ```
228
233
 
229
- The socket is opened lazily and reused across actions to the same endpoint. Multiple transports can
230
- be registered; the runtime picks the best available one (WebSocket preferred for lower latency, HTTP
231
- as fallback). For channels nice-action doesn't model natively, use `CustomTransport`.
234
+ The socket is opened lazily and reused across actions sharing a `getTransportCacheKey`. Multiple
235
+ transports can be registered; the runtime picks the best available one (WebSocket preferred for lower
236
+ latency, HTTP as fallback). For channels nice-action doesn't model natively, use `CustomTransport`.
232
237
 
233
- For advanced cases, `HttpTransport` / `WebSocketTransport` accept functions to derive the URL or
234
- headers per action (e.g. `new HttpTransport({ url: (input) => \`/api/${input.action.id}\` })`).
238
+ Each transport takes a single creation function so you decide how simple or complex it should be —
239
+ derive the request per action straight from the params (e.g.
240
+ `HttpTransport.create({ createRequest: (input) => ({ url: \`/api/${input.action.id}\` }) })`). For full
241
+ control over readiness (support detection, async init), `WebSocketTransport` / `CustomTransport` also
242
+ offer `.createAdvanced({ getTransport })`.
235
243
 
236
244
  ---
237
245
 
@@ -4502,6 +4502,7 @@ var ActionInputAndOutputChip = ({
4502
4502
 
4503
4503
  // src/devtools/browser/components/action_list/ActionEntryRow.tsx
4504
4504
  import { jsxDEV as jsxDEV19, Fragment as Fragment10 } from "react/jsx-dev-runtime";
4505
+ var MAX_GROUP_DOTS = 5;
4505
4506
  function getLatestChipColor(status) {
4506
4507
  if (status === "failed")
4507
4508
  return "failed" /* failed */;
@@ -4822,15 +4823,29 @@ function ActionEntryRow({
4822
4823
  paddingLeft: "4.6em",
4823
4824
  paddingBottom: "2px"
4824
4825
  },
4825
- children: groupEntries.map((e, i) => /* @__PURE__ */ jsxDEV19(GroupDot, {
4826
- entry: e,
4827
- index: i,
4828
- total: groupEntries.length,
4829
- refTime: groupEntries[0].startTime,
4830
- isActive: selectedGroupCuid === e.cuid,
4831
- onSelect: () => onSelectGroupEntry?.(e.cuid)
4832
- }, e.cuid, false, undefined, this))
4833
- }, undefined, false, undefined, this)
4826
+ children: [
4827
+ groupEntries.slice(0, MAX_GROUP_DOTS).map((e, i) => /* @__PURE__ */ jsxDEV19(GroupDot, {
4828
+ entry: e,
4829
+ index: i,
4830
+ total: groupEntries.length,
4831
+ refTime: groupEntries[0].startTime,
4832
+ isActive: selectedGroupCuid === e.cuid,
4833
+ onSelect: () => onSelectGroupEntry?.(e.cuid)
4834
+ }, e.cuid, false, undefined, this)),
4835
+ groupEntries.length > MAX_GROUP_DOTS && /* @__PURE__ */ jsxDEV19("span", {
4836
+ style: {
4837
+ fontSize: "0.7em",
4838
+ opacity: 0.5,
4839
+ flexShrink: 0,
4840
+ lineHeight: 1
4841
+ },
4842
+ children: [
4843
+ "+ ",
4844
+ groupEntries.length - MAX_GROUP_DOTS
4845
+ ]
4846
+ }, undefined, true, undefined, this)
4847
+ ]
4848
+ }, undefined, true, undefined, this)
4834
4849
  ]
4835
4850
  }, undefined, true, undefined, this);
4836
4851
  }
package/build/index.js CHANGED
@@ -1993,7 +1993,7 @@ class ActionExternalClientHandler extends ActionHandler {
1993
1993
  client: this.externalClient,
1994
1994
  transOrd: transport.transOrd,
1995
1995
  transType: transport.type,
1996
- transInfo: transport.definition?.getRouteInfo(input)
1996
+ transInfo: transport.getRouteInfo(input)
1997
1997
  };
1998
1998
  }
1999
1999
  clearTransportCache() {
@@ -2053,6 +2053,9 @@ class TransportConnection {
2053
2053
  this.type = def.type;
2054
2054
  this.initialized = def.initialize();
2055
2055
  }
2056
+ getRouteInfo(input) {
2057
+ return this.definition?.getRouteInfo(input);
2058
+ }
2056
2059
  _getCacheKey(input) {
2057
2060
  const parts = this.initialized.getTransportCacheKey?.(input);
2058
2061
  if (parts == null)
@@ -2121,20 +2124,32 @@ class CustomTransport extends Transport {
2121
2124
  super();
2122
2125
  this.options = options;
2123
2126
  }
2127
+ static create(options) {
2128
+ return new CustomTransport({ ...options, mode: "send" });
2129
+ }
2130
+ static createAdvanced(options) {
2131
+ return new CustomTransport({ ...options, mode: "advanced" });
2132
+ }
2124
2133
  _createConnection(_ctx) {
2125
2134
  const options = this.options;
2135
+ let getTransport;
2136
+ if (options.mode === "advanced") {
2137
+ getTransport = options.getTransport;
2138
+ } else {
2139
+ getTransport = () => ({
2140
+ status: "ready" /* ready */,
2141
+ readyData: {
2142
+ sendActionData: options.sendActionData,
2143
+ sendReturnData: options.sendReturnData,
2144
+ updateRunConfig: options.updateRunConfig,
2145
+ closeTransport: options.closeTransport ?? (() => {})
2146
+ }
2147
+ });
2148
+ }
2126
2149
  return new CustomConnection({
2127
2150
  initialize: () => ({
2128
2151
  getTransportCacheKey: options.getTransportCacheKey,
2129
- getTransport: options.getTransport ?? (() => ({
2130
- status: "ready" /* ready */,
2131
- readyData: {
2132
- sendActionData: options.sendActionData,
2133
- sendReturnData: options.sendReturnData,
2134
- updateRunConfig: options.updateRunConfig,
2135
- closeTransport: options.closeTransport ?? (() => {})
2136
- }
2137
- }))
2152
+ getTransport
2138
2153
  })
2139
2154
  });
2140
2155
  }
@@ -2282,9 +2297,6 @@ class HttpConnection extends TransportConnection {
2282
2297
  }
2283
2298
 
2284
2299
  // src/ActionRuntime/Handler/ExternalClient/Transport/Http/HttpTransport.ts
2285
- function resolveMaybe(value, input) {
2286
- return typeof value === "function" ? value(input) : value;
2287
- }
2288
2300
  function shortPath(url) {
2289
2301
  try {
2290
2302
  return new URL(url).pathname || url;
@@ -2300,13 +2312,8 @@ class HttpTransport extends Transport {
2300
2312
  super();
2301
2313
  this.options = options;
2302
2314
  }
2303
- _resolveRequest(input) {
2304
- if (this.options.createRequest != null)
2305
- return this.options.createRequest(input);
2306
- return {
2307
- url: resolveMaybe(this.options.url, input),
2308
- headers: this.options.headers != null ? resolveMaybe(this.options.headers, input) : undefined
2309
- };
2315
+ static create(options) {
2316
+ return new HttpTransport(options);
2310
2317
  }
2311
2318
  _createConnection(_ctx) {
2312
2319
  return new HttpConnection({
@@ -2315,7 +2322,7 @@ class HttpTransport extends Transport {
2315
2322
  getTransport: () => ({
2316
2323
  status: "ready" /* ready */,
2317
2324
  readyData: {
2318
- createRequest: (input) => this._resolveRequest(input),
2325
+ createRequest: this.options.createRequest,
2319
2326
  updateRunConfig: this.options.updateRunConfig
2320
2327
  }
2321
2328
  })
@@ -2323,7 +2330,7 @@ class HttpTransport extends Transport {
2323
2330
  });
2324
2331
  }
2325
2332
  getRouteInfo(input) {
2326
- const { url } = this._resolveRequest(input);
2333
+ const { url } = this.options.createRequest(input);
2327
2334
  return {
2328
2335
  type: "http" /* http */,
2329
2336
  method: "POST",
@@ -2339,10 +2346,21 @@ var createUnsetTransportResolvers = (type) => ({
2339
2346
  }
2340
2347
  });
2341
2348
 
2349
+ // src/ActionRuntime/Handler/ExternalClient/Transport/WebSocket/ws_util.ts
2350
+ function shortWs(url) {
2351
+ try {
2352
+ const u = new URL(url);
2353
+ return `${u.host}${u.pathname}`;
2354
+ } catch {
2355
+ return url;
2356
+ }
2357
+ }
2358
+
2342
2359
  // src/ActionRuntime/Handler/ExternalClient/Transport/WebSocket/WebSocketConnection.ts
2343
2360
  class WebSocketConnection extends TransportConnection {
2344
2361
  resolvers;
2345
2362
  _abortSet = new Set;
2363
+ _liveSocketUrl;
2346
2364
  constructor(def, resolvers) {
2347
2365
  super({ ...def, type: "ws" /* ws */ });
2348
2366
  this.resolvers = resolvers ?? createUnsetTransportResolvers("ws" /* ws */);
@@ -2403,9 +2421,22 @@ class WebSocketConnection extends TransportConnection {
2403
2421
  readyData: this._finalizeTransportMethods(transportStatusInfo.readyData)
2404
2422
  };
2405
2423
  }
2424
+ getRouteInfo(input) {
2425
+ const base = this.definition?.getRouteInfo(input);
2426
+ if (base?.url != null || this._liveSocketUrl == null)
2427
+ return base;
2428
+ return {
2429
+ type: "ws" /* ws */,
2430
+ ...base,
2431
+ url: this._liveSocketUrl,
2432
+ summary: `ws ${shortWs(this._liveSocketUrl)}`
2433
+ };
2434
+ }
2406
2435
  _finalizeTransportMethods(wsData) {
2407
2436
  const ws = wsData.ws;
2408
2437
  const disconnectListeners = [];
2438
+ if (ws.url != null && ws.url !== "")
2439
+ this._liveSocketUrl = ws.url;
2409
2440
  const sendActionData = (inputs) => {
2410
2441
  const { action, runningAction, timeout } = inputs;
2411
2442
  if (action.type === "request" /* request */) {
@@ -2475,18 +2506,6 @@ class WebSocketConnection extends TransportConnection {
2475
2506
  }
2476
2507
 
2477
2508
  // src/ActionRuntime/Handler/ExternalClient/Transport/WebSocket/WebSocketTransport.ts
2478
- function resolveMaybe2(value, input) {
2479
- return typeof value === "function" ? value(input) : value;
2480
- }
2481
- function shortWs(url) {
2482
- try {
2483
- const u = new URL(url);
2484
- return `${u.host}${u.pathname}`;
2485
- } catch {
2486
- return url;
2487
- }
2488
- }
2489
-
2490
2509
  class WebSocketTransport extends Transport {
2491
2510
  options;
2492
2511
  type = "ws" /* ws */;
@@ -2494,32 +2513,40 @@ class WebSocketTransport extends Transport {
2494
2513
  super();
2495
2514
  this.options = options;
2496
2515
  }
2497
- _createSocket(input) {
2498
- if (this.options.createWebSocket != null)
2499
- return this.options.createWebSocket(input);
2500
- return new WebSocket(resolveMaybe2(this.options.url, input));
2516
+ static create(options) {
2517
+ return new WebSocketTransport({ ...options, mode: "socket" });
2518
+ }
2519
+ static createAdvanced(options) {
2520
+ return new WebSocketTransport({ ...options, mode: "advanced" });
2501
2521
  }
2502
2522
  _createConnection(ctx) {
2523
+ const options = this.options;
2524
+ let getTransport;
2525
+ if (options.mode === "advanced") {
2526
+ getTransport = options.getTransport;
2527
+ } else {
2528
+ getTransport = (input) => ({
2529
+ status: "ready" /* ready */,
2530
+ readyData: {
2531
+ ws: options.createWebSocket(input),
2532
+ formatMessage: options.formatMessage,
2533
+ updateRunConfig: options.updateRunConfig
2534
+ }
2535
+ });
2536
+ }
2503
2537
  return new WebSocketConnection({
2504
2538
  initialize: () => ({
2505
- getTransportCacheKey: this.options.getTransportCacheKey ?? ((input) => [resolveMaybe2(this.options.url, input)]),
2506
- getTransport: (input) => ({
2507
- status: "ready" /* ready */,
2508
- readyData: {
2509
- ws: this._createSocket(input),
2510
- formatMessage: this.options.formatMessage,
2511
- updateRunConfig: this.options.updateRunConfig
2512
- }
2513
- })
2539
+ getTransportCacheKey: options.getTransportCacheKey,
2540
+ getTransport
2514
2541
  })
2515
2542
  }, ctx.resolvers);
2516
2543
  }
2517
2544
  getRouteInfo(input) {
2518
- const url = resolveMaybe2(this.options.url, input);
2545
+ if (this.options.getRouteInfo != null)
2546
+ return this.options.getRouteInfo(input);
2519
2547
  return {
2520
2548
  type: "ws" /* ws */,
2521
- url,
2522
- summary: `ws ${shortWs(url)}`
2549
+ summary: "ws"
2523
2550
  };
2524
2551
  }
2525
2552
  }
@@ -1,5 +1,5 @@
1
- import { TransportConnection } from "../TransportConnection";
2
1
  import { ETransportType, type IActionTransportReadyData_Methods, type ITransportRouteActionParams } from "../Transport.types";
2
+ import { TransportConnection } from "../TransportConnection";
3
3
  import type { IActionTransportDef_Custom, IActionTransportInitialized_Custom, IActionTransportReadyData_Custom, TActionTransportDef_Custom_NoType } from "./TransportCustom.types";
4
4
  export declare class CustomConnection extends TransportConnection<ETransportType.custom, ITransportRouteActionParams, IActionTransportReadyData_Custom, IActionTransportInitialized_Custom, IActionTransportDef_Custom> {
5
5
  constructor(def: TActionTransportDef_Custom_NoType);
@@ -1,33 +1,43 @@
1
- import { Transport, type ITransportConnectionContext } from "../Transport";
1
+ import { type ITransportConnectionContext, Transport } from "../Transport";
2
2
  import { ETransportType, type ITransportMethod_SendActionData_Input, type ITransportRouteActionParams, type ITransportRouteInfo, type TSendReturnDataMethod, type TUpdateActionRunConfig } from "../Transport.types";
3
3
  import { CustomConnection } from "./CustomConnection";
4
4
  import type { IActionTransportInitialized_Custom } from "./TransportCustom.types";
5
- export interface ICustomTransportOptions {
6
- /** Send a request/progress payload to the external client. */
7
- sendActionData: (input: ITransportMethod_SendActionData_Input) => void;
8
- /** Optional return-path dispatch for bidirectional channels. */
9
- sendReturnData?: TSendReturnDataMethod;
10
- closeTransport?: (input?: ITransportRouteActionParams) => void;
5
+ interface ICustomTransportSharedOptions {
11
6
  updateRunConfig?: TUpdateActionRunConfig;
12
7
  getTransportCacheKey?: (input: ITransportRouteActionParams) => string[];
13
- /**
14
- * Advanced escape hatch — full control over readiness/initialization. When provided, the simple
15
- * `sendActionData` / `sendReturnData` / `closeTransport` fields are ignored.
16
- */
17
- getTransport?: IActionTransportInitialized_Custom["getTransport"];
18
8
  /** Short label shown in the devtools chip (defaults to "custom"). */
19
9
  label?: string;
20
10
  /** Override the devtools route info for a specific action. */
21
11
  getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
22
12
  }
13
+ export interface ICustomTransportSendOptions extends ICustomTransportSharedOptions {
14
+ /** Send a request/progress payload to the external client. */
15
+ sendActionData: (input: ITransportMethod_SendActionData_Input) => void;
16
+ /** Optional return-path dispatch for bidirectional channels. */
17
+ sendReturnData?: TSendReturnDataMethod;
18
+ closeTransport?: (input?: ITransportRouteActionParams) => void;
19
+ }
20
+ export interface ICustomTransportAdvancedOptions extends ICustomTransportSharedOptions {
21
+ /** Full control over readiness/initialization for the custom channel. */
22
+ getTransport: IActionTransportInitialized_Custom["getTransport"];
23
+ }
24
+ export type TCustomTransportOptions = (ICustomTransportSendOptions & {
25
+ mode: "send";
26
+ }) | (ICustomTransportAdvancedOptions & {
27
+ mode: "advanced";
28
+ });
23
29
  /**
24
- * Reusable custom transport definition for channels nice-action doesn't model natively. Provide
25
- * `sendActionData` for the simple case, or `getTransport` for full control over the lifecycle.
30
+ * Reusable custom transport definition for channels nice-action doesn't model natively. Create one
31
+ * with `CustomTransport.create({ sendActionData })` for the simple case, or
32
+ * `CustomTransport.createAdvanced({ getTransport })` for full control over the lifecycle.
26
33
  */
27
34
  export declare class CustomTransport extends Transport<ETransportType.custom> {
28
35
  private readonly options;
29
36
  readonly type = ETransportType.custom;
30
- constructor(options: ICustomTransportOptions);
37
+ constructor(options: TCustomTransportOptions);
38
+ static create(options: ICustomTransportSendOptions): CustomTransport;
39
+ static createAdvanced(options: ICustomTransportAdvancedOptions): CustomTransport;
31
40
  _createConnection(_ctx: ITransportConnectionContext): CustomConnection;
32
41
  getRouteInfo(input: ITransportRouteActionParams): ITransportRouteInfo;
33
42
  }
43
+ export {};
@@ -1,5 +1,5 @@
1
- import { TransportConnection } from "../TransportConnection";
2
1
  import { ETransportType, type IActionTransportReadyData_Methods, type ITransportDispatchAction, type ITransportRouteActionParams } from "../Transport.types";
2
+ import { TransportConnection } from "../TransportConnection";
3
3
  import type { IActionTransportDef_Http, IActionTransportInitialized_Http, IActionTransportReadyData_Http, IActionTransportReadyParams_Http } from "./TransportHttp.types";
4
4
  export declare class HttpConnection extends TransportConnection<ETransportType.http, ITransportRouteActionParams, IActionTransportReadyData_Http, IActionTransportInitialized_Http, IActionTransportDef_Http> {
5
5
  constructor(def: Omit<IActionTransportDef_Http, "type">);
@@ -1,28 +1,26 @@
1
- import { Transport, type ITransportConnectionContext } from "../Transport";
1
+ import { type ITransportConnectionContext, Transport } from "../Transport";
2
2
  import { ETransportType, type ITransportRouteActionParams, type ITransportRouteInfo, type TUpdateActionRunConfig } from "../Transport.types";
3
3
  import { HttpConnection } from "./HttpConnection";
4
4
  import type { IHttpRequestParams } from "./TransportHttp.types";
5
- type TMaybeResolved<T> = T | ((input: ITransportRouteActionParams) => T);
6
5
  export interface IHttpTransportOptions {
7
- /** Endpoint URL, either static or derived per action. */
8
- url: TMaybeResolved<string>;
9
- /** Optional extra headers, either static or derived per action. */
10
- headers?: TMaybeResolved<Record<string, string>>;
11
- /** Advanced escape hatch — full control over the request. Overrides `url` / `headers`. */
12
- createRequest?: (input: ITransportRouteActionParams) => IHttpRequestParams;
6
+ /**
7
+ * Build the HTTP request for an action. Return a static request for the simple case, or derive the
8
+ * url / headers / body from the action params for full control.
9
+ */
10
+ createRequest: (input: ITransportRouteActionParams) => IHttpRequestParams;
13
11
  updateRunConfig?: TUpdateActionRunConfig;
14
12
  getTransportCacheKey?: (input: ITransportRouteActionParams) => string[];
15
13
  }
16
14
  /**
17
- * Reusable HTTP transport definition. Common case is just `new HttpTransport({ url })`; pass
18
- * functions for `url` / `headers` to derive them per action, or `createRequest` for full control.
15
+ * Reusable HTTP transport definition. Create one with `HttpTransport.create({ createRequest })` — the
16
+ * single `createRequest` function lets you keep it simple (`() => ({ url })`) or derive the request
17
+ * per action.
19
18
  */
20
19
  export declare class HttpTransport extends Transport<ETransportType.http> {
21
20
  private readonly options;
22
21
  readonly type = ETransportType.http;
23
22
  constructor(options: IHttpTransportOptions);
24
- private _resolveRequest;
23
+ static create(options: IHttpTransportOptions): HttpTransport;
25
24
  _createConnection(_ctx: ITransportConnectionContext): HttpConnection;
26
25
  getRouteInfo(input: ITransportRouteActionParams): ITransportRouteInfo;
27
26
  }
28
- export {};
@@ -9,8 +9,9 @@ export interface ITransportConnectionContext {
9
9
  resolvers?: IActionTransportResolvers;
10
10
  }
11
11
  /**
12
- * Reusable transport definition. Devs construct these (`new HttpTransport({ url })`,
13
- * `new WebSocketTransport({ url })`, …) and pass them to an `ActionExternalClientHandler`. A single
12
+ * Reusable transport definition. Devs construct these (`HttpTransport.create({ createRequest })`,
13
+ * `WebSocketTransport.create({ createWebSocket })`, …) and pass them to an
14
+ * `ActionExternalClientHandler`. A single
14
15
  * definition can be shared across multiple handlers — each handler builds its own live
15
16
  * {@link TransportConnection} via {@link TransportConnection._createConnection}.
16
17
  */
@@ -1,5 +1,5 @@
1
1
  import type { Transport } from "./Transport";
2
- import { type ETransportType, type IActionTransportDef, type IActionTransportInitialized, type IActionTransportReadyData_Base, type IActionTransportReadyData_Methods, type ITransportRouteActionParams, type TTransportStatusInfo } from "./Transport.types";
2
+ import { type ETransportType, type IActionTransportDef, type IActionTransportInitialized, type IActionTransportReadyData_Base, type IActionTransportReadyData_Methods, type ITransportRouteActionParams, type ITransportRouteInfo, type TTransportStatusInfo } from "./Transport.types";
3
3
  /**
4
4
  * Live, per-handler transport runtime built from a reusable {@link Transport} definition. Holds the
5
5
  * connection-scoped state (ordinal, initialized config, sockets / abort sets) that must not be shared
@@ -13,6 +13,12 @@ export declare abstract class TransportConnection<T extends ETransportType = ETr
13
13
  /** Backref to the public definition that created this connection (used for devtools route info). */
14
14
  definition?: Transport<T>;
15
15
  constructor(def: DEF);
16
+ /**
17
+ * Devtools route info for an action routed through this live connection. Defaults to the stateless
18
+ * {@link definition}'s info; connections override to enrich it from live state (e.g. the actual
19
+ * resolved socket URL) when the definition couldn't resolve it on its own.
20
+ */
21
+ getRouteInfo(input: RP): ITransportRouteInfo | undefined;
16
22
  protected abstract _finalizeTransportMethods(inputs: RD): IActionTransportReadyData_Methods;
17
23
  protected _getCacheKey(input: RP): string | null;
18
24
  getCacheKey(input: RP): string | null;
@@ -1,12 +1,15 @@
1
+ import { ETransportType, type IActionTransportReadyData_Methods, type IActionTransportResolvers, type ITransportRouteActionParams, type ITransportRouteInfo, type TTransportStatusInfo } from "../Transport.types";
1
2
  import { TransportConnection } from "../TransportConnection";
2
- import { ETransportType, type IActionTransportReadyData_Methods, type IActionTransportResolvers, type ITransportRouteActionParams, type TTransportStatusInfo } from "../Transport.types";
3
3
  import type { IActionTransportDef_Ws, IActionTransportInitialized_Ws, IActionTransportReadyData_Ws } from "./TransportWebSocket.types";
4
4
  export declare class WebSocketConnection extends TransportConnection<ETransportType.ws, ITransportRouteActionParams, IActionTransportReadyData_Ws, IActionTransportInitialized_Ws, IActionTransportDef_Ws> {
5
5
  private resolvers;
6
6
  private _abortSet;
7
+ /** URL of the most recently resolved live socket — surfaced to devtools when the definition can't. */
8
+ private _liveSocketUrl?;
7
9
  constructor(def: Omit<IActionTransportDef_Ws, "type">, resolvers?: IActionTransportResolvers);
8
10
  protected _getCacheKey(_input: ITransportRouteActionParams): string;
9
11
  protected _processTransportStatus(input: ITransportRouteActionParams): TTransportStatusInfo<IActionTransportReadyData_Methods>;
12
+ getRouteInfo(input: ITransportRouteActionParams): ITransportRouteInfo | undefined;
10
13
  _finalizeTransportMethods(wsData: IActionTransportReadyData_Ws): IActionTransportReadyData_Methods;
11
14
  private _parseActionMessage;
12
15
  private _abortAll;
@@ -1,31 +1,48 @@
1
- import { Transport, type ITransportConnectionContext } from "../Transport";
1
+ import { type ITransportConnectionContext, Transport } from "../Transport";
2
2
  import { ETransportType, type ITransportRouteActionParams, type ITransportRouteInfo, type TUpdateActionRunConfig } from "../Transport.types";
3
- import type { IActionTransportReadyData_Ws } from "./TransportWebSocket.types";
3
+ import type { IActionTransportInitialized_Ws, IActionTransportReadyData_Ws } from "./TransportWebSocket.types";
4
4
  import { WebSocketConnection } from "./WebSocketConnection";
5
- type TMaybeResolved<T> = T | ((input: ITransportRouteActionParams) => T);
6
- export interface IWebSocketTransportOptions {
7
- /** WebSocket endpoint URL, either static or derived per action. */
8
- url: TMaybeResolved<string>;
9
- /** Advanced escape hatch — provide your own socket (e.g. with sub-protocols). Overrides `url`. */
10
- createWebSocket?: (input: ITransportRouteActionParams) => WebSocket;
5
+ interface IWebSocketTransportSharedOptions {
11
6
  /** Custom (de)serialization of action payloads on the wire. */
12
7
  formatMessage?: IActionTransportReadyData_Ws["formatMessage"];
13
8
  updateRunConfig?: TUpdateActionRunConfig;
14
9
  /**
15
- * Keys that identify a reusable socket. Defaults to the resolved url, so a single socket is shared
16
- * across actions to the same endpoint instead of opening one per action.
10
+ * Keys that identify a reusable socket, so a single socket is shared across actions to the same
11
+ * endpoint instead of opening one per action.
17
12
  */
18
13
  getTransportCacheKey?: (input: ITransportRouteActionParams) => string[];
14
+ /** Override the devtools route info for a specific action. */
15
+ getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
19
16
  }
17
+ export interface IWebSocketTransportSocketOptions extends IWebSocketTransportSharedOptions {
18
+ /** Open (or reuse) the WebSocket for an action — keep it simple or derive it per action. */
19
+ createWebSocket: (input: ITransportRouteActionParams) => WebSocket;
20
+ }
21
+ export interface IWebSocketTransportAdvancedOptions extends IWebSocketTransportSharedOptions {
22
+ /**
23
+ * Full control over readiness/initialization. Use this for contextual support detection (return
24
+ * `{ status: ETransportStatus.unsupported }` when the socket shouldn't be used) or async url
25
+ * building (return `{ status: ETransportStatus.initializing, initializationPromise }` that resolves
26
+ * to a `ready` socket).
27
+ */
28
+ getTransport: IActionTransportInitialized_Ws["getTransport"];
29
+ }
30
+ export type TWebSocketTransportOptions = (IWebSocketTransportSocketOptions & {
31
+ mode: "socket";
32
+ }) | (IWebSocketTransportAdvancedOptions & {
33
+ mode: "advanced";
34
+ });
20
35
  /**
21
- * Reusable WebSocket transport definition. Common case is just `new WebSocketTransport({ url })`. The
22
- * underlying socket is cached (by url, by default) and reused across actions to the same endpoint.
36
+ * Reusable WebSocket transport definition. Create one with `WebSocketTransport.create({ createWebSocket })`
37
+ * for the common case, or `WebSocketTransport.createAdvanced({ getTransport })` for full control over
38
+ * readiness. The underlying socket is cached (via `getTransportCacheKey`) and reused across actions.
23
39
  */
24
40
  export declare class WebSocketTransport extends Transport<ETransportType.ws> {
25
41
  private readonly options;
26
42
  readonly type = ETransportType.ws;
27
- constructor(options: IWebSocketTransportOptions);
28
- private _createSocket;
43
+ constructor(options: TWebSocketTransportOptions);
44
+ static create(options: IWebSocketTransportSocketOptions): WebSocketTransport;
45
+ static createAdvanced(options: IWebSocketTransportAdvancedOptions): WebSocketTransport;
29
46
  _createConnection(ctx: ITransportConnectionContext): WebSocketConnection;
30
47
  getRouteInfo(input: ITransportRouteActionParams): ITransportRouteInfo;
31
48
  }
@@ -0,0 +1,2 @@
1
+ /** Compact a WebSocket URL to `host/pathname` for devtools display, falling back to the raw url. */
2
+ export declare function shortWs(url: string): string;
@@ -22,7 +22,7 @@ export * from "./ActionRuntime/Handler/ExternalClient/Transport/err_nice_transpo
22
22
  export { HttpTransport, type IHttpTransportOptions, } from "./ActionRuntime/Handler/ExternalClient/Transport/Http/HttpTransport";
23
23
  export { type ITransportConnectionContext, Transport, } from "./ActionRuntime/Handler/ExternalClient/Transport/Transport";
24
24
  export * from "./ActionRuntime/Handler/ExternalClient/Transport/Transport.types";
25
- export { type IWebSocketTransportOptions, WebSocketTransport, } from "./ActionRuntime/Handler/ExternalClient/Transport/WebSocket/WebSocketTransport";
25
+ export { type IWebSocketTransportAdvancedOptions, type IWebSocketTransportSocketOptions, type TWebSocketTransportOptions, WebSocketTransport, } from "./ActionRuntime/Handler/ExternalClient/Transport/WebSocket/WebSocketTransport";
26
26
  export { ActionLocalHandler, createLocalHandler, } from "./ActionRuntime/Handler/Local/ActionLocalHandler";
27
27
  export * from "./ActionRuntime/RuntimeCoordinate";
28
28
  export { EErrId_NiceAction, err_nice_action } from "./errors/err_nice_action";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nice-code/action",
3
- "version": "0.4.5",
3
+ "version": "0.4.7",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -44,9 +44,9 @@
44
44
  "build-types": "tsc --project tsconfig.build.json"
45
45
  },
46
46
  "dependencies": {
47
- "@nice-code/common-errors": "0.4.5",
48
- "@nice-code/error": "0.4.5",
49
- "@nice-code/util": "0.4.5",
47
+ "@nice-code/common-errors": "0.4.7",
48
+ "@nice-code/error": "0.4.7",
49
+ "@nice-code/util": "0.4.7",
50
50
  "@standard-schema/spec": "^1.1.0",
51
51
  "@tanstack/react-virtual": "^3.13.26",
52
52
  "http-status-codes": "^2.3.0",