@nice-code/action 0.1.1 → 0.1.2

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
@@ -3393,6 +3393,7 @@ var EErrId_NiceTransport;
3393
3393
  ((EErrId_NiceTransport2) => {
3394
3394
  EErrId_NiceTransport2["timeout"] = "timeout";
3395
3395
  EErrId_NiceTransport2["not_found"] = "not_found";
3396
+ EErrId_NiceTransport2["not_available"] = "not_available";
3396
3397
  EErrId_NiceTransport2["initialization_failed"] = "initialization_failed";
3397
3398
  EErrId_NiceTransport2["send_failed"] = "send_failed";
3398
3399
  EErrId_NiceTransport2["invalid_action_response"] = "invalid_action_response";
@@ -3406,6 +3407,9 @@ var err_nice_transport = err_nice_connect.createChildDomain({
3406
3407
  ["not_found" /* not_found */]: err({
3407
3408
  message: ({ actionId, routeKey, tag }) => `No connected transport found for action "${actionId}"${routeKey ? ` with route key "${routeKey}"` : ``}${tag ? ` and action tag "${tag}"` : ""}.`
3408
3409
  }),
3410
+ ["not_available" /* not_available */]: err({
3411
+ message: ({ transportCount }) => `${transportCount} Transport(s) found but were filtered out via filterUsage().`
3412
+ }),
3409
3413
  ["initialization_failed" /* initialization_failed */]: err({
3410
3414
  message: ({ actionId, routeKey, tag }) => `Transports found for action "${actionId}"${routeKey ? ` with route key "${routeKey}"` : ""}${tag ? ` and action tag "${tag}"` : ""}, but none are ready.`
3411
3415
  }),
@@ -3487,13 +3491,21 @@ class Transport {
3487
3491
  def;
3488
3492
  type;
3489
3493
  requestResolvers = new Map;
3494
+ _filterUsage;
3490
3495
  constructor(def) {
3491
3496
  this.def = def;
3492
3497
  this.type = def.type;
3498
+ this._filterUsage = def.filterUsage;
3493
3499
  }
3494
3500
  get status() {
3495
3501
  return this._status;
3496
3502
  }
3503
+ filterUsage(primed) {
3504
+ if (this._filterUsage == null) {
3505
+ return true;
3506
+ }
3507
+ return this._filterUsage(primed);
3508
+ }
3497
3509
  checkAndPrepare() {
3498
3510
  return this._status;
3499
3511
  }
@@ -3533,6 +3545,7 @@ var ETransportType;
3533
3545
  ((ETransportType2) => {
3534
3546
  ETransportType2["ws"] = "ws";
3535
3547
  ETransportType2["http"] = "http";
3548
+ ETransportType2["custom"] = "custom";
3536
3549
  })(ETransportType ||= {});
3537
3550
  var ETransportStatus;
3538
3551
  ((ETransportStatus2) => {
@@ -3621,6 +3634,7 @@ var err_nice_transport_ws = err_nice_transport.createChildDomain({
3621
3634
  class TransportWebSocket extends Transport {
3622
3635
  websocket;
3623
3636
  _status = { status: "uninitialized" /* uninitialized */ };
3637
+ _customMessageSerde;
3624
3638
  constructor(def) {
3625
3639
  super(def);
3626
3640
  }
@@ -3642,21 +3656,24 @@ class TransportWebSocket extends Transport {
3642
3656
  waitForInitialization
3643
3657
  };
3644
3658
  }
3645
- handleMessage(data) {
3659
+ handlePureActionResponseMessage(message) {
3646
3660
  let json;
3647
3661
  try {
3648
- json = JSON.parse(data);
3662
+ json = JSON.parse(message);
3649
3663
  } catch {
3650
3664
  return;
3651
3665
  }
3652
3666
  if (!isActionResponseJsonObject(json)) {
3653
3667
  return;
3654
3668
  }
3655
- const pending = this.requestResolvers.get(json.cuid);
3669
+ return json;
3670
+ }
3671
+ handleResponse(response) {
3672
+ const pending = this.requestResolvers.get(response.cuid);
3656
3673
  if (pending == null) {
3657
3674
  return;
3658
3675
  }
3659
- this.respond(pending.primed.coreAction.actionDomain.hydrateResponse(json));
3676
+ this.respond(pending.primed.coreAction.actionDomain.hydrateResponse(response));
3660
3677
  }
3661
3678
  rejectPendingWebSocketRequests(error) {
3662
3679
  for (const [, pending] of this.requestResolvers) {
@@ -3676,7 +3693,9 @@ class TransportWebSocket extends Transport {
3676
3693
  }
3677
3694
  };
3678
3695
  try {
3679
- this.websocket = await this.def.createWebSocket();
3696
+ const { ws, customMessageSerde } = await this.def.createWebSocket();
3697
+ this.websocket = ws;
3698
+ this._customMessageSerde = customMessageSerde;
3680
3699
  } catch (e2) {
3681
3700
  console.error("Failed to create WebSocket:", e2);
3682
3701
  const error = err_nice_transport_ws.fromId("ws_create_failed" /* ws_create_failed */, {
@@ -3701,7 +3720,10 @@ class TransportWebSocket extends Transport {
3701
3720
  });
3702
3721
  this.websocket.addEventListener("message", (event) => {
3703
3722
  if (typeof event.data === "string") {
3704
- this.handleMessage(event.data);
3723
+ const response = this._customMessageSerde?.deserialize?.(event.data) ?? this.handlePureActionResponseMessage(event.data);
3724
+ if (response) {
3725
+ this.handleResponse(response);
3726
+ }
3705
3727
  }
3706
3728
  });
3707
3729
  this.websocket.addEventListener("close", (event) => {
@@ -3773,7 +3795,23 @@ class ConnectionConfig {
3773
3795
  async dispatch(primed, defaultTimeout) {
3774
3796
  const timeout = this.config.defaultTimeout ?? defaultTimeout;
3775
3797
  const initializingWaiters = [];
3798
+ const unavailableTransports = [];
3776
3799
  for (const transport of this._transports) {
3800
+ const isAvailable = transport.filterUsage(primed);
3801
+ if (isAvailable instanceof Promise) {
3802
+ try {
3803
+ if (!await isAvailable) {
3804
+ unavailableTransports.push(transport);
3805
+ continue;
3806
+ }
3807
+ } catch {
3808
+ unavailableTransports.push(transport);
3809
+ continue;
3810
+ }
3811
+ } else if (!isAvailable) {
3812
+ unavailableTransports.push(transport);
3813
+ continue;
3814
+ }
3777
3815
  const statusInfo = transport.checkAndPrepare();
3778
3816
  if (statusInfo.status === "ready" /* ready */) {
3779
3817
  return transport.makeRequest(primed, timeout);
@@ -3788,6 +3826,11 @@ class ConnectionConfig {
3788
3826
  }
3789
3827
  }
3790
3828
  if (initializingWaiters.length === 0) {
3829
+ if (unavailableTransports.length > 0) {
3830
+ throw err_nice_transport.fromId("not_available" /* not_available */, {
3831
+ transportCount: unavailableTransports.length
3832
+ });
3833
+ }
3791
3834
  throw err_nice_transport.fromId("not_found" /* not_found */, {
3792
3835
  actionId: primed.id
3793
3836
  });
@@ -1,6 +1,5 @@
1
1
  import type { NiceActionSchema } from "../ActionSchema/NiceActionSchema";
2
2
  import type { INiceActionErrorDeclaration, TTransportedValue } from "../ActionSchema/NiceActionSchema.types";
3
- export type MaybePromise<T> = T | Promise<T>;
4
3
  export type TPossibleDomainId = string;
5
4
  export type TPossibleDomainIdList = [TPossibleDomainId, ...TPossibleDomainId[]];
6
5
  export type TNiceActionDomainSchema = Record<string, NiceActionSchema<TTransportedValue<any, any>, TTransportedValue<any, any>, readonly INiceActionErrorDeclaration<any, any>[]>>;
@@ -1,3 +1,4 @@
1
+ import type { MaybePromise } from "../../..";
1
2
  import type { NiceActionPrimed } from "../../../NiceAction/NiceActionPrimed";
2
3
  import type { NiceActionResponse } from "../../../NiceAction/NiceActionResponse";
3
4
  import { type ITransportPendingRequest, type TActionTransportDef, type TTransportStatusInfo } from "./Transport.types";
@@ -6,8 +7,10 @@ export declare abstract class Transport<DEF extends TActionTransportDef> {
6
7
  readonly type: DEF["type"];
7
8
  readonly requestResolvers: Map<string, ITransportPendingRequest>;
8
9
  protected abstract _status: TTransportStatusInfo;
10
+ protected _filterUsage?: (primed: NiceActionPrimed<any>) => MaybePromise<boolean>;
9
11
  constructor(def: DEF);
10
12
  get status(): TTransportStatusInfo;
13
+ filterUsage(primed: NiceActionPrimed<any>): MaybePromise<boolean>;
11
14
  checkAndPrepare(): TTransportStatusInfo;
12
15
  protected abstract send(primed: NiceActionPrimed<any>): Promise<void>;
13
16
  abstract disconnect(): void;
@@ -1,10 +1,12 @@
1
- import type { NiceError } from "@nice-code/error";
1
+ import type { MaybePromise, NiceError } from "@nice-code/error";
2
+ import type { INiceActionPrimed_JsonObject, TNiceActionResponse_JsonObject } from "../../../NiceAction/NiceAction.types";
2
3
  import type { NiceActionPrimed } from "../../../NiceAction/NiceActionPrimed";
3
4
  import type { NiceActionResponse } from "../../../NiceAction/NiceActionResponse";
4
5
  import type { Transport } from "./Transport";
5
6
  export declare enum ETransportType {
6
7
  ws = "ws",
7
- http = "http"
8
+ http = "http",
9
+ custom = "custom"
8
10
  }
9
11
  export declare enum ETransportStatus {
10
12
  uninitialized = "uninitialized",
@@ -31,16 +33,34 @@ export type TTransportStatusInfo = ITransportStatusInfo_Base<ETransportStatus.un
31
33
  export interface IActionTransport_Base {
32
34
  /** Per-transport timeout override (ms) */
33
35
  timeout?: number;
36
+ filterUsage?: (primed: NiceActionPrimed<any>) => MaybePromise<boolean>;
37
+ }
38
+ export interface ICustomWebsocketMessageSerde {
39
+ serialize?: (primedJson: INiceActionPrimed_JsonObject<any>) => string;
40
+ deserialize?: (message: string) => TNiceActionResponse_JsonObject<any>;
34
41
  }
35
42
  export interface IActionTransportDef_Ws extends IActionTransport_Base {
36
43
  type: ETransportType.ws;
37
- createWebSocket: () => Promise<WebSocket>;
44
+ createWebSocket: () => Promise<{
45
+ ws: WebSocket;
46
+ customMessageSerde?: ICustomWebsocketMessageSerde;
47
+ }>;
38
48
  }
39
49
  export interface IActionTransportDef_Http extends IActionTransport_Base {
40
50
  type: ETransportType.http;
41
51
  url: string;
42
52
  }
43
- export type TActionTransportDef = IActionTransportDef_Ws | IActionTransportDef_Http;
53
+ export interface ICustomActionTransport {
54
+ checkAndPrepare: () => TTransportStatusInfo;
55
+ handleAction: (primed: NiceActionPrimed<any>, onResponse: (response: NiceActionResponse<any>) => void) => Promise<void>;
56
+ onDisconnect: () => void;
57
+ }
58
+ export interface IActionTransportDef_Custom extends IActionTransport_Base {
59
+ type: ETransportType.custom;
60
+ initialStatus: TTransportStatusInfo;
61
+ createTransport: () => ICustomActionTransport;
62
+ }
63
+ export type TActionTransportDef = IActionTransportDef_Ws | IActionTransportDef_Http | IActionTransportDef_Custom;
44
64
  export interface ITransportPendingRequest {
45
65
  type: ETransportType;
46
66
  resolve: (response: NiceActionResponse<any>) => void;
@@ -0,0 +1,12 @@
1
+ import type { NiceActionPrimed } from "../../../NiceAction/NiceActionPrimed";
2
+ import { Transport } from "./Transport";
3
+ import { type IActionTransportDef_Custom, type TTransportStatusInfo } from "./Transport.types";
4
+ export declare class TransportCustom extends Transport<IActionTransportDef_Custom> {
5
+ readonly abortControllers: Map<string, AbortController>;
6
+ protected _status: TTransportStatusInfo;
7
+ private _customTransport;
8
+ constructor(def: IActionTransportDef_Custom);
9
+ checkAndPrepare(): TTransportStatusInfo;
10
+ send(primed: NiceActionPrimed<any>): Promise<void>;
11
+ disconnect(): void;
12
+ }
@@ -4,10 +4,12 @@ import { type IActionTransportDef_Ws, type TTransportStatusInfo } from "./Transp
4
4
  export declare class TransportWebSocket extends Transport<IActionTransportDef_Ws> {
5
5
  websocket?: WebSocket;
6
6
  protected _status: TTransportStatusInfo;
7
+ private _customMessageSerde?;
7
8
  constructor(def: IActionTransportDef_Ws);
8
9
  checkAndPrepare(): TTransportStatusInfo;
9
10
  private startInitializing;
10
- private handleMessage;
11
+ private handlePureActionResponseMessage;
12
+ private handleResponse;
11
13
  private rejectPendingWebSocketRequests;
12
14
  private _connect;
13
15
  protected send(primed: NiceActionPrimed<any>): Promise<void>;
@@ -1,6 +1,7 @@
1
1
  export declare enum EErrId_NiceTransport {
2
2
  timeout = "timeout",
3
3
  not_found = "not_found",
4
+ not_available = "not_available",
4
5
  initialization_failed = "initialization_failed",
5
6
  send_failed = "send_failed",
6
7
  invalid_action_response = "invalid_action_response"
@@ -17,6 +18,9 @@ export declare const err_nice_transport: import("@nice-code/error").NiceErrorDom
17
18
  routeKey?: string;
18
19
  tag?: string;
19
20
  }, import("@nice-code/error").JSONSerializableValue>;
21
+ not_available: import("@nice-code/error").INiceErrorIdMetadata<{
22
+ transportCount: number;
23
+ }, import("@nice-code/error").JSONSerializableValue>;
20
24
  initialization_failed: import("@nice-code/error").INiceErrorIdMetadata<{
21
25
  actionId: string;
22
26
  routeKey?: string;
@@ -1,8 +1,9 @@
1
- import type { INiceActionDomain, MaybePromise, TInferOutputFromSchema } from "../../ActionDomain/NiceActionDomain.types";
1
+ import type { INiceActionDomain, TInferOutputFromSchema } from "../../ActionDomain/NiceActionDomain.types";
2
2
  import type { INiceAction, TNiceActionResponse_JsonObject } from "../../NiceAction/NiceAction.types";
3
3
  import type { TDistributedDomainActions } from "../../NiceAction/NiceActionCombined.types";
4
4
  import type { NiceActionPrimed } from "../../NiceAction/NiceActionPrimed";
5
5
  import type { NiceActionResponse } from "../../NiceAction/NiceActionResponse";
6
+ import type { MaybePromise } from "../../utils/maybePromise";
6
7
  import type { IRuntimeEnvironmentMeta } from "../ActionRuntimeEnvironment.types";
7
8
  export type TAtLeastOne<T extends object> = {
8
9
  [K in keyof T]-?: Required<Pick<T, K>> & Partial<Omit<T, K>>;
@@ -1,4 +1,5 @@
1
- import type { INiceActionDomain, MaybePromise } from "../../ActionDomain/NiceActionDomain.types";
1
+ import type { INiceActionDomain } from "../../ActionDomain/NiceActionDomain.types";
2
+ import type { MaybePromise } from "../../utils/maybePromise";
2
3
  import type { INiceAction } from "../NiceAction.types";
3
4
  import type { TNarrowActionType } from "../NiceActionCombined.types";
4
5
  type TMatchHandler<A extends INiceAction<any>> = (action: A) => MaybePromise<void>;
@@ -1,6 +1,6 @@
1
1
  export { createActionRootDomain } from "./ActionDomain/helpers/createRootActionDomain";
2
2
  export { NiceActionDomain } from "./ActionDomain/NiceActionDomain";
3
- export type { INiceActionDomain, INiceActionDomainChildOptions, INiceActionRootDomain, MaybePromise, TDomainActionId, TInferInputFromSchema, TInferOutputFromSchema, TNiceActionDomainChildDef, TNiceActionDomainSchema, TPossibleDomainId, TPossibleDomainIdList, } from "./ActionDomain/NiceActionDomain.types";
3
+ export type { INiceActionDomain, INiceActionDomainChildOptions, INiceActionRootDomain, TDomainActionId, TInferInputFromSchema, TInferOutputFromSchema, TNiceActionDomainChildDef, TNiceActionDomainSchema, TPossibleDomainId, TPossibleDomainIdList, } from "./ActionDomain/NiceActionDomain.types";
4
4
  export { NiceActionRootDomain } from "./ActionDomain/RootDomain/NiceActionRootDomain";
5
5
  export { ActionConnect } from "./ActionRuntimeEnvironment/ActionConnect/ActionConnect";
6
6
  export * from "./ActionRuntimeEnvironment/ActionConnect/ActionConnect.types";
@@ -30,3 +30,4 @@ export { NiceActionPrimed } from "./NiceAction/NiceActionPrimed";
30
30
  export { NiceActionResponse } from "./NiceAction/NiceActionResponse";
31
31
  export * from "./utils/isActionResponseJsonObject";
32
32
  export * from "./utils/isPrimedActionJsonObject";
33
+ export * from "./utils/maybePromise";
@@ -0,0 +1 @@
1
+ export type MaybePromise<T> = T | Promise<T>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nice-code/action",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -32,8 +32,8 @@
32
32
  "build-types": "tsc --project tsconfig.build.json"
33
33
  },
34
34
  "dependencies": {
35
- "@nice-code/error": "0.1.1",
36
- "@nice-code/common-errors": "0.1.1",
35
+ "@nice-code/error": "0.1.2",
36
+ "@nice-code/common-errors": "0.1.2",
37
37
  "@standard-schema/spec": "^1.1.0",
38
38
  "http-status-codes": "^2.3.0",
39
39
  "nanoid": "^5.1.9",