@nice-code/action 0.6.1 → 0.6.3

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/build/index.js CHANGED
@@ -965,6 +965,7 @@ class ActionRuntime {
965
965
  }
966
966
  async handleActionPayload(action, options) {
967
967
  if (action.type === "request" /* request */) {
968
+ const observers = action.context._domain._collectActionObservers();
968
969
  let handlerForAction;
969
970
  try {
970
971
  handlerForAction = this.getHandlerForActionOrThrow(action, options);
@@ -973,6 +974,7 @@ class ActionRuntime {
973
974
  context: action.context,
974
975
  request: action
975
976
  });
977
+ runningAction2.addUpdateListeners(observers);
976
978
  runningAction2._completeWithResult(action.errorResult(castNiceError(err2)));
977
979
  return runningAction2;
978
980
  }
@@ -980,6 +982,7 @@ class ActionRuntime {
980
982
  ...options,
981
983
  targetLocalRuntime: this
982
984
  });
985
+ runningAction.addUpdateListeners(observers);
983
986
  this._trySetupReturnDispatch(runningAction);
984
987
  return runningAction;
985
988
  }
@@ -1293,6 +1296,9 @@ class ActionDomainBase {
1293
1296
  this._listeners = this._listeners.filter((l) => l !== listener);
1294
1297
  };
1295
1298
  }
1299
+ _getActionObservers() {
1300
+ return this._listeners;
1301
+ }
1296
1302
  }
1297
1303
 
1298
1304
  // src/ActionDefinition/Domain/ActionDomain.ts
@@ -1309,6 +1315,9 @@ class ActionDomain extends ActionDomainBase {
1309
1315
  get rootDomain() {
1310
1316
  return this._rootDomain;
1311
1317
  }
1318
+ _collectActionObservers() {
1319
+ return [...this._rootDomain._getActionObservers(), ...this._getActionObservers()];
1320
+ }
1312
1321
  _registerRuntime(runtime2) {
1313
1322
  this._rootDomain._registerRuntime(runtime2);
1314
1323
  }
@@ -1794,20 +1803,24 @@ class ConnectionTransportManager {
1794
1803
  addTransport(transport) {
1795
1804
  this._transports.push(transport);
1796
1805
  }
1806
+ getPreferredTransport() {
1807
+ return this._transports[0];
1808
+ }
1797
1809
  async getReadyTransport(routeActionParams) {
1798
- const initializingWaiters = [];
1799
- const unavailableTransports = [];
1800
1810
  const action = routeActionParams.action;
1811
+ const candidates = [];
1812
+ const unavailableTransports = [];
1801
1813
  for (const transport of this._transports) {
1802
1814
  const cacheKey = transport.getCacheKey(routeActionParams);
1803
1815
  if (cacheKey != null) {
1804
1816
  const cached = this._cache.get(cacheKey);
1805
1817
  if (cached != null) {
1806
1818
  if (cached instanceof Promise) {
1807
- initializingWaiters.push(cached);
1819
+ candidates.push(cached);
1808
1820
  continue;
1809
1821
  }
1810
- return { ...cached, transport };
1822
+ candidates.push(Promise.resolve({ ...cached, transport }));
1823
+ break;
1811
1824
  }
1812
1825
  }
1813
1826
  const statusInfo = transport.getTransport(routeActionParams);
@@ -1817,7 +1830,8 @@ class ConnectionTransportManager {
1817
1830
  this._cache.set(cacheKey, { methods: readyData, transport });
1818
1831
  readyData.addOnDisconnectListener?.(() => this._cache.delete(cacheKey));
1819
1832
  }
1820
- return { methods: readyData, transport };
1833
+ candidates.push(Promise.resolve({ methods: readyData, transport }));
1834
+ break;
1821
1835
  }
1822
1836
  if (statusInfo.status === "unsupported" /* unsupported */) {
1823
1837
  unavailableTransports.push(transport);
@@ -1847,10 +1861,10 @@ class ConnectionTransportManager {
1847
1861
  if (cacheKey != null) {
1848
1862
  this._cache.set(cacheKey, promise);
1849
1863
  }
1850
- initializingWaiters.push(promise);
1864
+ candidates.push(promise);
1851
1865
  }
1852
1866
  }
1853
- if (initializingWaiters.length === 0) {
1867
+ if (candidates.length === 0) {
1854
1868
  if (unavailableTransports.length > 0) {
1855
1869
  throw err_nice_transport.fromId("unsupported" /* unsupported */, {
1856
1870
  transportTypes: unavailableTransports.map((t) => t.type)
@@ -1860,13 +1874,17 @@ class ConnectionTransportManager {
1860
1874
  actionId: action.id
1861
1875
  });
1862
1876
  }
1863
- try {
1864
- return await Promise.any(initializingWaiters).then();
1865
- } catch (e) {
1866
- throw err_nice_transport.fromId("initialization_failed" /* initialization_failed */, {
1867
- actionId: action.id
1868
- }).withOriginError(e);
1877
+ let lastError;
1878
+ for (const candidate of candidates) {
1879
+ try {
1880
+ return await candidate;
1881
+ } catch (e) {
1882
+ lastError = e;
1883
+ }
1869
1884
  }
1885
+ throw err_nice_transport.fromId("initialization_failed" /* initialization_failed */, {
1886
+ actionId: action.id
1887
+ }).withOriginError(lastError);
1870
1888
  }
1871
1889
  }
1872
1890
 
@@ -1926,20 +1944,19 @@ class ActionExternalClientHandler extends ActionHandler {
1926
1944
  const incomingTimeout = config?.timeout ?? this._defaultTimeout;
1927
1945
  const parentCuid = peekHandlerCuid();
1928
1946
  const callSite = action._callSite ?? new Error().stack;
1929
- const { methods, transport } = await this.transportManager.getReadyTransport({
1947
+ const routeParams = {
1930
1948
  action,
1931
1949
  localClient,
1932
1950
  externalClient: this.externalClient
1933
- });
1934
- action.context.addRouteItem({
1951
+ };
1952
+ const preferredTransport = this.transportManager.getPreferredTransport();
1953
+ const routeItem = preferredTransport != null ? {
1935
1954
  runtime: localClient,
1936
- handler: this.toHandlerRouteItem(transport, {
1937
- action,
1938
- localClient,
1939
- externalClient: this.externalClient
1940
- }),
1955
+ handler: this.toHandlerRouteItem(preferredTransport, routeParams),
1941
1956
  time: Date.now()
1942
- });
1957
+ } : undefined;
1958
+ if (routeItem != null)
1959
+ action.context.addRouteItem(routeItem);
1943
1960
  const runningAction = new RunningAction({
1944
1961
  context: action.context,
1945
1962
  request: action,
@@ -1947,23 +1964,37 @@ class ActionExternalClientHandler extends ActionHandler {
1947
1964
  callSite
1948
1965
  });
1949
1966
  localRuntime.registerRunningAction(runningAction);
1950
- const routeActionParams = {
1951
- action,
1952
- runningAction,
1953
- localClient,
1954
- externalClient: this.externalClient,
1955
- timeout: incomingTimeout
1956
- };
1957
- if (action.type === "request" /* request */ && methods.updateRunConfig != null) {
1958
- const runConfig = methods.updateRunConfig(routeActionParams);
1959
- routeActionParams.timeout = runConfig?.timeout ?? incomingTimeout;
1960
- }
1967
+ this._dispatchWhenTransportReady(runningAction, routeParams, routeItem, incomingTimeout);
1968
+ return runningAction;
1969
+ }
1970
+ async _dispatchWhenTransportReady(runningAction, routeParams, routeItem, incomingTimeout) {
1971
+ const action = routeParams.action;
1961
1972
  try {
1962
- methods.sendActionData(routeActionParams);
1973
+ const { methods, transport } = await this.transportManager.getReadyTransport(routeParams);
1974
+ const handlerRouteItem = this.toHandlerRouteItem(transport, routeParams);
1975
+ if (routeItem != null) {
1976
+ routeItem.handler = handlerRouteItem;
1977
+ routeItem.time = Date.now();
1978
+ } else {
1979
+ action.context.addRouteItem({
1980
+ runtime: routeParams.localClient,
1981
+ handler: handlerRouteItem,
1982
+ time: Date.now()
1983
+ });
1984
+ }
1985
+ const sendInput = {
1986
+ ...routeParams,
1987
+ runningAction,
1988
+ timeout: incomingTimeout
1989
+ };
1990
+ if (action.type === "request" /* request */ && methods.updateRunConfig != null) {
1991
+ const runConfig = methods.updateRunConfig(sendInput);
1992
+ sendInput.timeout = runConfig?.timeout ?? incomingTimeout;
1993
+ }
1994
+ methods.sendActionData(sendInput);
1963
1995
  } catch (err3) {
1964
1996
  runningAction._abort(err3);
1965
1997
  }
1966
- return runningAction;
1967
1998
  }
1968
1999
  async sendReturnPayload(payload, config) {
1969
2000
  const localClient = config.targetLocalRuntime.coordinate;
@@ -27,6 +27,7 @@ export declare class ActionExternalClientHandler extends ActionHandler<EActionHa
27
27
  forActionIds<ACT_DOM extends IActionDomain, IDS extends ReadonlyArray<keyof ACT_DOM["actionSchema"] & string>>(domain: ActionDomain<ACT_DOM>, ids: IDS): this;
28
28
  _setIncomingActionDataListener(listener: (json: TActionPayload_Any_JsonObject<any, any>) => void): void;
29
29
  handleActionRequest<DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(action: ActionPayload_Request<DOM, ID>, config?: IHandleActionOptions): Promise<RunningAction<DOM, ID>>;
30
+ private _dispatchWhenTransportReady;
30
31
  /**
31
32
  * Dispatch a result or progress payload directly back to the external client via the best
32
33
  * available bidirectional transport (WebSocket / Custom). Used for return-path routing when the
@@ -5,5 +5,12 @@ export declare class ConnectionTransportManager {
5
5
  private _transports;
6
6
  constructor(_cache: TTransportCache);
7
7
  addTransport(transport: TransportConnection): void;
8
+ /**
9
+ * The highest-priority transport (first declared). Used to label an action's route *before* the
10
+ * transport has finished connecting — so a still-connecting action shows its (expected) destination
11
+ * instead of an "unknown" hop. {@link getReadyTransport} still decides the real winner, and the
12
+ * caller corrects the hop if a lower-priority transport ends up serving the action.
13
+ */
14
+ getPreferredTransport(): TransportConnection | undefined;
8
15
  getReadyTransport(routeActionParams: ITransportRouteActionParams): Promise<IActionTransportReady>;
9
16
  }
@@ -0,0 +1,15 @@
1
+ import { ESize } from "../ui_util/size";
2
+ /**
3
+ * Marks an action that was received from another runtime (a backend push, or an action relayed from
4
+ * another client) rather than dispatched locally. The chip shows the origin's `envId`; the tooltip
5
+ * carries the full coordinate. Render it only when {@link getInboundOrigin} returns a value.
6
+ */
7
+ export declare function OriginChip({ origin, size, subtle, }: {
8
+ origin: {
9
+ envId: string;
10
+ perId?: string;
11
+ insId?: string;
12
+ };
13
+ size?: ESize;
14
+ subtle?: boolean;
15
+ }): import("react/jsx-runtime").JSX.Element;
@@ -5,6 +5,17 @@ export declare const STATUS_COLOR: Record<TDevtoolsActionStatus, string>;
5
5
  export declare const STATUS_THING: Record<TDevtoolsActionStatus, ESemanticThing>;
6
6
  export declare const STATUS_SYMBOL: Record<TDevtoolsActionStatus, string>;
7
7
  export declare const STATUS_ICON: Record<TDevtoolsActionStatus, LucideIcon>;
8
+ /**
9
+ * The runtime an action *originated* on, when that differs from the runtime that handled it — i.e. an
10
+ * inbound action received over a transport (a backend push, or an action relayed from another client),
11
+ * rather than one dispatched locally. Returns `null` for locally-originated actions (where the origin
12
+ * matches the handling runtime) and for actions with no known origin.
13
+ */
14
+ export declare function getInboundOrigin(entry: IDevtoolsActionEntry): {
15
+ envId: string;
16
+ perId?: string;
17
+ insId?: string;
18
+ } | null;
8
19
  export declare function safeStringify(value: unknown, indent?: number): string;
9
20
  export declare function formatRelativeAge(ms: number): string;
10
21
  export declare function formatDuration(entry: IDevtoolsActionEntry): string | null;
@@ -54,6 +54,7 @@ export declare enum ESemanticThing {
54
54
  domain = "domain",
55
55
  handler_local = "handler_local",
56
56
  handler_external = "handler_external",
57
+ origin = "origin",// runtime an action was received from (inbound / pushed actions)
57
58
  age = "age",// non-latest relative-age chip
58
59
  io_input = "io_input",
59
60
  io_output = "io_output",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nice-code/action",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
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.6.1",
48
- "@nice-code/error": "0.6.1",
49
- "@nice-code/util": "0.6.1",
47
+ "@nice-code/common-errors": "0.6.3",
48
+ "@nice-code/error": "0.6.3",
49
+ "@nice-code/util": "0.6.3",
50
50
  "@standard-schema/spec": "^1.1.0",
51
51
  "@tanstack/react-virtual": "^3.13.26",
52
52
  "http-status-codes": "^2.3.0",