@nice-code/action 0.7.0 → 0.8.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.
Files changed (39) hide show
  1. package/build/devtools/browser/index.js +590 -1246
  2. package/build/devtools/server/index.js +7 -1
  3. package/build/index.js +1173 -972
  4. package/build/types/ActionRuntime/ActionRuntime.d.ts +23 -1
  5. package/build/types/ActionRuntime/Handler/ExternalClient/Transport/Transport.types.d.ts +6 -0
  6. package/build/types/ActionRuntime/Handler/ExternalClient/Transport/WebSocket/WebSocketConnection.d.ts +2 -1
  7. package/build/types/ActionRuntime/Handler/Server/ActionServerHandler.d.ts +57 -0
  8. package/build/types/ActionRuntime/Handler/Server/WsConnectionStateStore.d.ts +61 -0
  9. package/build/types/ActionRuntime/Handler/Server/createActionFetchHandler.d.ts +40 -0
  10. package/build/types/devtools/browser/NiceActionDevtools.d.ts +1 -1
  11. package/build/types/devtools/browser/components/ActionErrorDisplay.d.ts +1 -1
  12. package/build/types/devtools/browser/components/CallStackSection.d.ts +1 -1
  13. package/build/types/devtools/browser/components/ChildDispatchChips.d.ts +1 -1
  14. package/build/types/devtools/browser/components/Chip.d.ts +1 -1
  15. package/build/types/devtools/browser/components/DetailSection.d.ts +1 -1
  16. package/build/types/devtools/browser/components/DomainChip.d.ts +1 -1
  17. package/build/types/devtools/browser/components/HandlerChips.d.ts +1 -1
  18. package/build/types/devtools/browser/components/Icon.d.ts +1 -1
  19. package/build/types/devtools/browser/components/MetaSection.d.ts +1 -1
  20. package/build/types/devtools/browser/components/NiceErrorDisplay.d.ts +2 -2
  21. package/build/types/devtools/browser/components/OriginChip.d.ts +1 -1
  22. package/build/types/devtools/browser/components/RoutingSection.d.ts +1 -1
  23. package/build/types/devtools/browser/components/RunningTimer.d.ts +2 -2
  24. package/build/types/devtools/browser/components/SectionLabel.d.ts +1 -4
  25. package/build/types/devtools/browser/components/StackTraceSection.d.ts +1 -1
  26. package/build/types/devtools/browser/components/Tooltip.d.ts +1 -24
  27. package/build/types/devtools/browser/components/action_detail/ActionDetailPanel.d.ts +1 -1
  28. package/build/types/devtools/browser/components/action_list/ActionEntryRow.d.ts +1 -1
  29. package/build/types/devtools/browser/components/action_list/ActionInputAndOutputChip.d.ts +1 -1
  30. package/build/types/devtools/browser/components/action_list/ActionList.d.ts +1 -1
  31. package/build/types/devtools/browser/components/action_list/IoTooltipContent.d.ts +1 -4
  32. package/build/types/devtools/browser/components/utils.d.ts +1 -3
  33. package/build/types/devtools/core/ActionDevtools.types.d.ts +1 -1
  34. package/build/types/devtools/core/ActionDevtoolsCore.d.ts +4 -1
  35. package/build/types/devtools/core/devtools_colors.d.ts +1 -26
  36. package/build/types/index.d.ts +3 -1
  37. package/package.json +33 -29
  38. package/build/types/devtools/browser/components/PanelChrome.d.ts +0 -41
  39. package/build/types/devtools/browser/devtools_dock.d.ts +0 -54
package/build/index.js CHANGED
@@ -689,7 +689,7 @@ var isActionPayload_Result_JsonObject = (obj) => {
689
689
 
690
690
  // src/ActionRuntime/ActionRuntime.ts
691
691
  import { castNiceError } from "@nice-code/error";
692
- import { nanoid as nanoid2 } from "nanoid";
692
+ import { nanoid as nanoid4 } from "nanoid";
693
693
 
694
694
  // src/utils/getAssumedRuntimeEnvironment.ts
695
695
  import { runtime } from "std-env";
@@ -715,6 +715,21 @@ function isActionPayload_Any_JsonObject(obj) {
715
715
  return isActionPayload_Request_JsonObject(obj) || isActionPayload_Result_JsonObject(obj) || isActionPayload_Progress_JsonObject(obj);
716
716
  }
717
717
 
718
+ // src/ActionRuntime/Handler/ExternalClient/ActionExternalClientHandler.ts
719
+ import { nanoid as nanoid3 } from "nanoid";
720
+
721
+ // src/ActionRuntime/HandlerCallStack.ts
722
+ var _stack = [];
723
+ function pushHandlerCuid(cuid) {
724
+ _stack.push(cuid);
725
+ }
726
+ function popHandlerCuid() {
727
+ _stack.pop();
728
+ }
729
+ function peekHandlerCuid() {
730
+ return _stack[_stack.length - 1];
731
+ }
732
+
718
733
  // src/ActionRuntime/ActionDomainManager.ts
719
734
  class ActionDomainManager {
720
735
  _domains = new Map;
@@ -899,693 +914,753 @@ class ActionRouter {
899
914
  }
900
915
  }
901
916
 
902
- // src/ActionRuntime/ActionRuntime.ts
903
- class ActionRuntime {
904
- _coordinate;
905
- timeCreated;
906
- runtimeInfo = getAssumedRuntimeInfo();
907
- actionRouter;
908
- _pendingRunningActions = new Map;
909
- _registeredExternalHandlers = [];
910
- _applied = false;
911
- static getDefault() {
912
- return getDefaultActionRuntime();
913
- }
914
- constructor(coordinate) {
915
- this._coordinate = coordinate.specifyIfUnset({
916
- insId: nanoid2(14)
917
- });
918
- this.timeCreated = Date.now();
919
- this.actionRouter = new ActionRouter({
920
- contextType: "runtime_to_handler" /* runtime_to_handler */,
921
- runtime: this
922
- });
923
- }
924
- get coordinate() {
925
- return this._coordinate;
917
+ // src/ActionRuntime/Handler/ActionHandler.ts
918
+ import { nanoid as nanoid2 } from "nanoid";
919
+
920
+ class ActionHandler {
921
+ cuid;
922
+ constructor() {
923
+ this.cuid = nanoid2();
926
924
  }
927
- specifyRuntimeCoordinate(specifics) {
928
- if (specifics.envId != null && this._coordinate.envId !== specifics.envId) {
929
- throw err_nice_action.fromId("not_implemented" /* not_implemented */, {
930
- label: `updating RuntimeCoordinate with a different "envId" ("${this._coordinate.envId}" → "${specifics.envId}")`
931
- });
932
- }
933
- this._coordinate = this._coordinate.specify(specifics);
934
- this.apply();
925
+ getActionRouter() {
926
+ return this.actionRouter;
935
927
  }
936
- registerRunningAction(ra) {
937
- this._pendingRunningActions.set(ra.cuid, ra);
938
- ra.addUpdateListeners([
939
- (update) => {
940
- if (update.type === "finished" /* finished */) {
941
- this._pendingRunningActions.delete(ra.cuid);
942
- }
943
- }
944
- ]);
928
+ }
929
+
930
+ // src/ActionRuntime/Handler/ExternalClient/Transport/err_nice_transport.ts
931
+ import { err as err2 } from "@nice-code/error";
932
+
933
+ // src/ActionRuntime/Handler/ExternalClient/err_nice_external_client.ts
934
+ var err_nice_external_client = err_nice_action.createChildDomain({
935
+ domain: "err_nice_external_client",
936
+ schema: {}
937
+ });
938
+
939
+ // src/ActionRuntime/Handler/ExternalClient/Transport/err_nice_transport.ts
940
+ var EErrId_NiceTransport;
941
+ ((EErrId_NiceTransport2) => {
942
+ EErrId_NiceTransport2["timeout"] = "timeout";
943
+ EErrId_NiceTransport2["not_found"] = "not_found";
944
+ EErrId_NiceTransport2["unsupported"] = "unsupported";
945
+ EErrId_NiceTransport2["initialization_failed"] = "initialization_failed";
946
+ EErrId_NiceTransport2["send_failed"] = "send_failed";
947
+ EErrId_NiceTransport2["invalid_action_response"] = "invalid_action_response";
948
+ })(EErrId_NiceTransport ||= {});
949
+ var err_nice_transport = err_nice_external_client.createChildDomain({
950
+ domain: "err_nice_transport",
951
+ schema: {
952
+ ["timeout" /* timeout */]: err2({
953
+ message: ({ timeout }) => `ActionConnect transport timed out after ${timeout}ms.`
954
+ }),
955
+ ["not_found" /* not_found */]: err2({
956
+ message: ({ actionId }) => `No connected transport found for action "${actionId}".`
957
+ }),
958
+ ["unsupported" /* unsupported */]: err2({
959
+ message: ({ transportTypes }) => `${transportTypes.length} Transport(s) [${transportTypes.join(", ")}] found but returned "unsupported" status.`
960
+ }),
961
+ ["initialization_failed" /* initialization_failed */]: err2({
962
+ message: ({ actionId }) => `Transports found for action "${actionId}", but none are ready.`
963
+ }),
964
+ ["send_failed" /* send_failed */]: err2({
965
+ message: ({ actionId, httpStatusCode, message }) => `Failed to send action "${actionId}" [${httpStatusCode ?? "Unknown status"}]: ${message ?? "Unknown error"}.`,
966
+ httpStatusCode: ({ httpStatusCode }) => httpStatusCode ?? 500
967
+ }),
968
+ ["invalid_action_response" /* invalid_action_response */]: err2({
969
+ message: ({ actionId }) => `Invalid action response JSON structure for action "${actionId}"`
970
+ })
945
971
  }
946
- resolveIncomingActionPayload(json) {
947
- if (json.type === "request" /* request */) {
948
- this.handleActionPayloadWire(json).catch((err2) => {
949
- console.error(`[ActionRuntime] Incoming action [${json.domain}:${json.id}:${json.form}:${json.type}] unhandled:`, err2);
950
- });
951
- return;
952
- }
953
- this._pendingRunningActions.get(json.context.cuid)?._resolveFromJson(json);
972
+ });
973
+
974
+ // src/ActionRuntime/Handler/ExternalClient/Transport/Transport.types.ts
975
+ var ETransportType;
976
+ ((ETransportType2) => {
977
+ ETransportType2["ws"] = "ws";
978
+ ETransportType2["http"] = "http";
979
+ ETransportType2["custom"] = "custom";
980
+ })(ETransportType ||= {});
981
+ var ETransportStatus;
982
+ ((ETransportStatus2) => {
983
+ ETransportStatus2["uninitialized"] = "uninitialized";
984
+ ETransportStatus2["unsupported"] = "unsupported";
985
+ ETransportStatus2["initializing"] = "initializing";
986
+ ETransportStatus2["ready"] = "ready";
987
+ ETransportStatus2["failed"] = "failed";
988
+ })(ETransportStatus ||= {});
989
+
990
+ // src/ActionRuntime/Handler/ExternalClient/Transport/ConnectionTransportManager.ts
991
+ class ConnectionTransportManager {
992
+ _cache;
993
+ _transports = [];
994
+ constructor(_cache) {
995
+ this._cache = _cache;
954
996
  }
955
- async handleActionPayloadWire(wire) {
956
- let action;
957
- if (isActionPayload_Any_JsonObject(wire)) {
958
- const domain = this.actionRouter.domainManager.getActionDomainOrThrow(wire);
959
- action = domain.hydrateAnyAction(wire);
960
- }
961
- if (action == null) {
962
- throw err_nice_action.fromId("wire_not_action_data" /* wire_not_action_data */);
963
- }
964
- return this.handleActionPayload(action);
997
+ addTransport(transport) {
998
+ this._transports.push(transport);
965
999
  }
966
- async handleActionPayload(action, options) {
967
- if (action.type === "request" /* request */) {
968
- const observers = action.context._domain._collectActionObservers();
969
- let handlerForAction;
970
- try {
971
- handlerForAction = this.getHandlerForActionOrThrow(action, options);
972
- } catch (err2) {
973
- const runningAction2 = new RunningAction({
974
- context: action.context,
975
- request: action
976
- });
977
- runningAction2.addUpdateListeners(observers);
978
- runningAction2._completeWithResult(action.errorResult(castNiceError(err2)));
979
- return runningAction2;
980
- }
981
- const runningAction = await handlerForAction.handleActionRequest(action, {
982
- ...options,
983
- targetLocalRuntime: this
984
- });
985
- runningAction.addUpdateListeners(observers);
986
- this._trySetupReturnDispatch(runningAction);
987
- return runningAction;
988
- }
989
- throw err_nice_action.fromId("not_implemented" /* not_implemented */, {
990
- label: `Handling incoming action payloads of type "${action.type}"`
991
- });
1000
+ getPreferredTransport() {
1001
+ return this._transports[0];
992
1002
  }
993
- _getHandlerForAction(action, options) {
994
- const handlers = this.actionRouter.getRouteDataEntriesForAction(action);
995
- const targetExternalClient = options?.targetExternalClient;
996
- const possibleHandlers = handlers.filter((handler2) => {
997
- if (handler2.handlerType === "external" /* external */) {
998
- if (targetExternalClient && !targetExternalClient.isSameFor(handler2.externalClient).id) {
999
- return false;
1003
+ async getReadyTransport(routeActionParams) {
1004
+ const action = routeActionParams.action;
1005
+ const candidates = [];
1006
+ const unavailableTransports = [];
1007
+ for (const transport of this._transports) {
1008
+ const cacheKey = transport.getCacheKey(routeActionParams);
1009
+ if (cacheKey != null) {
1010
+ const cached = this._cache.get(cacheKey);
1011
+ if (cached != null) {
1012
+ if (cached instanceof Promise) {
1013
+ candidates.push(cached);
1014
+ continue;
1015
+ }
1016
+ candidates.push(Promise.resolve({ ...cached, transport }));
1017
+ break;
1000
1018
  }
1001
- return true;
1002
- }
1003
- if (targetExternalClient != null) {
1004
- return false;
1005
1019
  }
1006
- if (action.type === "request" /* request */) {
1007
- return true;
1020
+ const statusInfo = transport.getTransport(routeActionParams);
1021
+ if (statusInfo.status === "ready" /* ready */) {
1022
+ const readyData = statusInfo.readyData;
1023
+ if (cacheKey != null) {
1024
+ const entry = { methods: readyData, transport };
1025
+ this._cache.set(cacheKey, entry);
1026
+ readyData.addOnDisconnectListener?.(() => {
1027
+ if (this._cache.get(cacheKey) === entry)
1028
+ this._cache.delete(cacheKey);
1029
+ });
1030
+ }
1031
+ candidates.push(Promise.resolve({ methods: readyData, transport }));
1032
+ break;
1008
1033
  }
1009
- return false;
1010
- });
1011
- if (possibleHandlers.length === 0) {
1012
- return;
1013
- }
1014
- const scoringExternalClient = targetExternalClient ?? RuntimeCoordinate.unknown;
1015
- let handlerScore = -1;
1016
- let handler;
1017
- for (const possibleHandler of possibleHandlers) {
1018
- if (possibleHandler.handlerType === "local" /* local */ && handler == null) {
1019
- return possibleHandler;
1034
+ if (statusInfo.status === "unsupported" /* unsupported */) {
1035
+ unavailableTransports.push(transport);
1036
+ continue;
1020
1037
  }
1021
- if (possibleHandler.handlerType === "external" /* external */) {
1022
- const score = scoringExternalClient.similarityLevel(possibleHandler.externalClient);
1023
- if (score > handlerScore) {
1024
- handlerScore = score;
1025
- handler = possibleHandler;
1038
+ if (statusInfo.status === "initializing" /* initializing */) {
1039
+ const promise = statusInfo.initializationPromise.then((info) => {
1040
+ if (info.status === "failed" /* failed */) {
1041
+ throw info.error;
1042
+ }
1043
+ if (info.status === "unsupported" /* unsupported */) {
1044
+ throw err_nice_transport.fromId("unsupported" /* unsupported */, {
1045
+ transportTypes: [transport.type]
1046
+ });
1047
+ }
1048
+ const readyData = info.readyData;
1049
+ const entry = { methods: readyData, transport };
1050
+ if (cacheKey != null) {
1051
+ if (this._cache.get(cacheKey) === promise) {
1052
+ this._cache.set(cacheKey, entry);
1053
+ readyData.addOnDisconnectListener?.(() => {
1054
+ if (this._cache.get(cacheKey) === entry)
1055
+ this._cache.delete(cacheKey);
1056
+ });
1057
+ } else {
1058
+ readyData.disconnect?.();
1059
+ }
1060
+ }
1061
+ return entry;
1062
+ }).catch((e) => {
1063
+ if (cacheKey != null && this._cache.get(cacheKey) === promise) {
1064
+ this._cache.delete(cacheKey);
1065
+ }
1066
+ throw e;
1067
+ });
1068
+ if (cacheKey != null) {
1069
+ this._cache.set(cacheKey, promise);
1026
1070
  }
1071
+ candidates.push(promise);
1027
1072
  }
1028
1073
  }
1029
- return handler;
1030
- }
1031
- getHandlerForActionOrThrow(action, options) {
1032
- const handler = this._getHandlerForAction(action, options);
1033
- if (handler == null) {
1034
- throw err_nice_action.fromId("no_action_execution_handler" /* no_action_execution_handler */, {
1035
- actionId: action.id,
1036
- domain: action.domain,
1037
- specifiedClient: options?.targetExternalClient
1038
- });
1039
- }
1040
- return handler;
1041
- }
1042
- addHandlers(handlers) {
1043
- for (const handler of handlers) {
1044
- if (handler.handlerType === "external" /* external */) {
1045
- handler._setIncomingActionDataListener((json) => this.resolveIncomingActionPayload(json));
1046
- this._registeredExternalHandlers.push(handler);
1047
- }
1048
- const handlerRouter = handler.getActionRouter();
1049
- this.actionRouter.addDomainsFromOther(handlerRouter);
1050
- if (this._applied) {
1051
- this.apply();
1052
- }
1053
- for (const key of handlerRouter.getRegisteredKeys()) {
1054
- const alreadyRegistered = this.actionRouter.getForKey(key).some((h) => h.cuid === handler.cuid);
1055
- if (!alreadyRegistered) {
1056
- this.actionRouter.addForKey(key, handler);
1057
- }
1074
+ if (candidates.length === 0) {
1075
+ if (unavailableTransports.length > 0) {
1076
+ throw err_nice_transport.fromId("unsupported" /* unsupported */, {
1077
+ transportTypes: unavailableTransports.map((t) => t.type)
1078
+ });
1058
1079
  }
1080
+ throw err_nice_transport.fromId("not_found" /* not_found */, {
1081
+ actionId: action.id
1082
+ });
1059
1083
  }
1060
- return this;
1061
- }
1062
- applyRuntimeForDomain(domain) {
1063
- const rootDomain = domain.rootDomain;
1064
- if (!rootDomain._hasRuntime(this)) {
1065
- rootDomain._registerRuntime(this);
1066
- }
1067
- }
1068
- apply() {
1069
- this._applied = true;
1070
- for (const domain of this.actionRouter.getDomains()) {
1071
- this.applyRuntimeForDomain(domain);
1072
- }
1073
- return this;
1074
- }
1075
- getReturnHandlerForOrigin(originClient) {
1076
- if (originClient.envId === UNSET_RUNTIME_ENV_ID)
1077
- return;
1078
- let bestScore = -1;
1079
- let bestHandler;
1080
- for (const handler of this._registeredExternalHandlers) {
1081
- const score = originClient.similarityLevel(handler.externalClient);
1082
- if (score > bestScore) {
1083
- bestScore = score;
1084
- bestHandler = handler;
1084
+ let lastError;
1085
+ for (const candidate of candidates) {
1086
+ try {
1087
+ return await candidate;
1088
+ } catch (e) {
1089
+ lastError = e;
1085
1090
  }
1086
1091
  }
1087
- return bestScore > 0 ? bestHandler : undefined;
1088
- }
1089
- resetRuntime() {
1090
- for (const ra of this._pendingRunningActions.values()) {
1091
- ra._abort(err_nice_action.fromId("runtime_reset" /* runtime_reset */));
1092
- }
1093
- for (const handler of this._registeredExternalHandlers) {
1094
- handler.clearTransportCache();
1095
- }
1096
- }
1097
- _trySetupReturnDispatch(runningAction) {
1098
- const originClient = runningAction.context.originClient;
1099
- if (originClient.envId === UNSET_RUNTIME_ENV_ID || originClient.isSameFor(this._coordinate).id) {
1100
- return;
1101
- }
1102
- runningAction.addUpdateListeners([
1103
- (update) => {
1104
- if (update.type === "finished" /* finished */ && update.finishType === "success" /* success */) {
1105
- const returnHandler = this.getReturnHandlerForOrigin(originClient);
1106
- returnHandler?.sendReturnPayload(update.response, { targetLocalRuntime: this }).catch(() => {});
1107
- }
1108
- }
1109
- ]);
1110
- }
1111
- }
1112
- var runtimeState = {
1113
- defaultLocalRuntime: undefined,
1114
- assumedRuntimeInfo: undefined
1115
- };
1116
- function getDefaultActionRuntime() {
1117
- if (runtimeState.assumedRuntimeInfo == null) {
1118
- runtimeState.assumedRuntimeInfo = getAssumedRuntimeInfo();
1119
- }
1120
- if (runtimeState.defaultLocalRuntime == null) {
1121
- runtimeState.defaultLocalRuntime = new ActionRuntime(RuntimeCoordinate.unknown.specify({
1122
- perId: `${runtimeState.assumedRuntimeInfo?.runtimeName ?? "unknown"}-runtime`
1123
- }));
1092
+ throw err_nice_transport.fromId("initialization_failed" /* initialization_failed */, {
1093
+ actionId: action.id
1094
+ }).withOriginError(lastError);
1124
1095
  }
1125
- return runtimeState.defaultLocalRuntime;
1126
- }
1127
-
1128
- // src/ActionRuntime/HandlerCallStack.ts
1129
- var _stack = [];
1130
- function pushHandlerCuid(cuid) {
1131
- _stack.push(cuid);
1132
- }
1133
- function popHandlerCuid() {
1134
- _stack.pop();
1135
1096
  }
1136
- function peekHandlerCuid() {
1137
- return _stack[_stack.length - 1];
1138
- }
1139
-
1140
- // src/ActionRuntime/Handler/ActionHandler.ts
1141
- import { nanoid as nanoid3 } from "nanoid";
1142
1097
 
1143
- class ActionHandler {
1098
+ // src/ActionRuntime/Handler/ExternalClient/ActionExternalClientHandler.ts
1099
+ class ActionExternalClientHandler extends ActionHandler {
1100
+ externalClient;
1101
+ handlerType = "external" /* external */;
1144
1102
  cuid;
1145
- constructor() {
1146
- this.cuid = nanoid3();
1147
- }
1148
- getActionRouter() {
1149
- return this.actionRouter;
1150
- }
1151
- }
1152
-
1153
- // src/ActionRuntime/Handler/Local/ActionLocalHandler.ts
1154
- class ActionLocalHandler extends ActionHandler {
1155
- handlerType = "local" /* local */;
1103
+ _defaultTimeout;
1104
+ _transportCache = new Map;
1105
+ transportManager = new ConnectionTransportManager(this._transportCache);
1106
+ _incomingActionDataListeners = [];
1156
1107
  actionRouter = new ActionRouter({
1157
1108
  contextType: "handler_route" /* handler_route */,
1158
1109
  handler: this
1159
1110
  });
1160
- constructor() {
1111
+ constructor({
1112
+ runtimeCoordinate: externalClientSpecifier,
1113
+ transports,
1114
+ defaultTimeout
1115
+ }) {
1161
1116
  super();
1117
+ this.externalClient = externalClientSpecifier;
1118
+ this.cuid = nanoid3();
1119
+ this._defaultTimeout = defaultTimeout ?? DEFAULT_TRANSPORT_TIMEOUT;
1120
+ for (const transport of transports) {
1121
+ const connection = transport._createConnection({
1122
+ resolvers: {
1123
+ onIncomingActionDataJson: (json) => {
1124
+ for (const l of this._incomingActionDataListeners)
1125
+ l(json);
1126
+ }
1127
+ }
1128
+ });
1129
+ connection.definition = transport;
1130
+ this.transportManager.addTransport(connection);
1131
+ }
1162
1132
  }
1163
- forDomain(domain, handler) {
1164
- this.actionRouter.forDomain(domain, handler);
1133
+ forDomain(domain) {
1134
+ this.actionRouter.forDomain(domain, true);
1165
1135
  return this;
1166
1136
  }
1167
- forAction(action, handler) {
1168
- this.actionRouter.forAction(action, handler);
1137
+ forAction(action) {
1138
+ this.actionRouter.forAction(action, true);
1169
1139
  return this;
1170
1140
  }
1171
- forActionIds(domain, ids, handler) {
1172
- this.actionRouter.forActionIds(domain, ids, handler);
1141
+ forActionIds(domain, ids) {
1142
+ this.actionRouter.forActionIds(domain, ids, true);
1173
1143
  return this;
1174
1144
  }
1175
- forDomainActionCases(domain, cases) {
1176
- this.actionRouter.forDomainActionCases(domain, cases);
1177
- return this;
1145
+ _setIncomingActionDataListener(listener) {
1146
+ this._incomingActionDataListeners.push(listener);
1178
1147
  }
1179
1148
  async handleActionRequest(action, config) {
1180
- const targetLocalRuntime = config?.targetLocalRuntime ?? ActionRuntime.getDefault();
1181
- const handler = this.actionRouter.getRouteDataForActionOrThrow(action, {
1182
- targetLocalRuntime
1183
- });
1184
- action.context.addRouteItem({
1185
- runtime: targetLocalRuntime.coordinate,
1186
- handler: this.toHandlerRouteItem(),
1149
+ const localRuntime = config?.targetLocalRuntime ?? ActionRuntime.getDefault();
1150
+ const localClient = localRuntime.coordinate;
1151
+ const incomingTimeout = config?.timeout ?? this._defaultTimeout;
1152
+ const parentCuid = peekHandlerCuid();
1153
+ const callSite = action._callSite ?? new Error().stack;
1154
+ const routeParams = {
1155
+ action,
1156
+ localClient,
1157
+ externalClient: this.externalClient
1158
+ };
1159
+ const preferredTransport = this.transportManager.getPreferredTransport();
1160
+ const routeItem = preferredTransport != null ? {
1161
+ runtime: localClient,
1162
+ handler: this.toHandlerRouteItem(preferredTransport, routeParams),
1187
1163
  time: Date.now()
1188
- });
1164
+ } : undefined;
1165
+ if (routeItem != null)
1166
+ action.context.addRouteItem(routeItem);
1189
1167
  const runningAction = new RunningAction({
1190
1168
  context: action.context,
1191
1169
  request: action,
1192
- parentCuid: peekHandlerCuid(),
1193
- callSite: action._callSite ?? new Error().stack
1194
- });
1195
- this._handleRunningAction(handler, runningAction).catch((err2) => {
1196
- if (err2 instanceof NiceError) {
1197
- runningAction._completeWithResult(action.errorResult(err2));
1198
- } else if (isNiceErrorObject(err2)) {
1199
- runningAction._completeWithResult(action.errorResult(castNiceError2(err2)));
1200
- } else {
1201
- runningAction._abort(err2);
1202
- }
1170
+ parentCuid,
1171
+ callSite
1203
1172
  });
1173
+ localRuntime.registerRunningAction(runningAction);
1174
+ this._dispatchWhenTransportReady(runningAction, routeParams, routeItem, incomingTimeout);
1204
1175
  return runningAction;
1205
1176
  }
1206
- async _handleRunningAction(handler, runningAction) {
1207
- const state = runningAction.state;
1208
- if (state.result != null) {
1209
- return;
1210
- }
1211
- await Promise.resolve();
1212
- pushHandlerCuid(runningAction.cuid);
1177
+ async _dispatchWhenTransportReady(runningAction, routeParams, routeItem, incomingTimeout) {
1178
+ const action = routeParams.action;
1213
1179
  try {
1214
- const rawResult = await handler(state.request);
1215
- let result;
1216
- if (rawResult instanceof ActionPayload_Result) {
1217
- result = rawResult;
1218
- } else if (rawResult != null && isActionPayload_Result_JsonObject(rawResult)) {
1219
- const domain = this.actionRouter.domainManager.getActionDomainOrThrow(state.request);
1220
- result = domain.hydrateResultPayload(rawResult);
1180
+ const { methods, transport } = await this.transportManager.getReadyTransport(routeParams);
1181
+ const handlerRouteItem = this.toHandlerRouteItem(transport, routeParams);
1182
+ if (routeItem != null) {
1183
+ routeItem.handler = handlerRouteItem;
1184
+ routeItem.time = Date.now();
1221
1185
  } else {
1222
- result = state.request.successResult(rawResult);
1186
+ action.context.addRouteItem({
1187
+ runtime: routeParams.localClient,
1188
+ handler: handlerRouteItem,
1189
+ time: Date.now()
1190
+ });
1223
1191
  }
1224
- runningAction._completeWithResult(result);
1225
- } finally {
1226
- popHandlerCuid();
1192
+ const sendInput = {
1193
+ ...routeParams,
1194
+ runningAction,
1195
+ timeout: incomingTimeout
1196
+ };
1197
+ if (action.type === "request" /* request */ && methods.updateRunConfig != null) {
1198
+ const runConfig = methods.updateRunConfig(sendInput);
1199
+ sendInput.timeout = runConfig?.timeout ?? incomingTimeout;
1200
+ }
1201
+ methods.sendActionData(sendInput);
1202
+ } catch (err3) {
1203
+ runningAction._abort(err3);
1227
1204
  }
1228
1205
  }
1229
- async handlePayloadWireOrThrow(wire, config) {
1230
- const hydratedAction = this.actionRouter.domainManager.hydrateActionPayload(wire);
1231
- if (!(hydratedAction instanceof ActionPayload_Request)) {
1232
- throw err_nice_action.fromId("wire_action_not_payload" /* wire_action_not_payload */, {
1233
- domain: hydratedAction.domain,
1234
- actionId: hydratedAction.id,
1235
- actionState: hydratedAction.type ?? hydratedAction.form
1206
+ async sendReturnPayload(payload, config) {
1207
+ const localClient = config.targetLocalRuntime.coordinate;
1208
+ try {
1209
+ const { methods } = await this.transportManager.getReadyTransport({
1210
+ action: payload,
1211
+ localClient,
1212
+ externalClient: this.externalClient
1236
1213
  });
1214
+ if (methods.sendReturnData == null)
1215
+ return false;
1216
+ methods.sendReturnData(payload, { localClient, externalClient: this.externalClient });
1217
+ return true;
1218
+ } catch {
1219
+ return false;
1237
1220
  }
1238
- return await this.handleActionRequest(hydratedAction, config);
1239
1221
  }
1240
1222
  toJsonObject() {
1241
1223
  return {
1242
- type: this.handlerType
1224
+ type: this.handlerType,
1225
+ client: this.externalClient
1243
1226
  };
1244
1227
  }
1245
- toHandlerRouteItem() {
1228
+ toHandlerRouteItem(transport, input) {
1246
1229
  return {
1247
- type: this.handlerType
1248
- };
1249
- }
1250
- }
1251
- var createLocalHandler = () => {
1252
- return new ActionLocalHandler;
1253
- };
1254
-
1255
- // src/utils/isAction_Context_JsonObject.ts
1256
- var isAction_Context_JsonObject = (obj) => {
1257
- return isAction_Base_JsonObject(obj) && obj.form === "context" /* context */;
1258
- };
1259
-
1260
- // src/utils/isAction_Core_JsonObject.ts
1261
- var isAction_Core_JsonObject = (obj) => {
1262
- return isAction_Base_JsonObject(obj) && obj.form === "core" /* core */;
1263
- };
1264
-
1265
- // src/utils/isAction_Any_JsonObject.ts
1266
- function isAction_Any_JsonObject(obj) {
1267
- return isActionPayload_Any_JsonObject(obj) || isAction_Context_JsonObject(obj) || isAction_Core_JsonObject(obj);
1268
- }
1269
-
1270
- // src/utils/assertIsActionJson.ts
1271
- function assertIsActionJson(obj) {
1272
- if (!isAction_Any_JsonObject(obj)) {
1273
- throw err_nice_action.fromId("wire_not_action_data" /* wire_not_action_data */);
1274
- }
1275
- }
1276
-
1277
- // src/utils/isAction_Any_Instance.ts
1278
- function isAction_Any_Instance(value) {
1279
- return value instanceof ActionCore || value instanceof ActionPayload || value instanceof ActionContext;
1280
- }
1281
-
1282
- // src/ActionDefinition/Domain/ActionDomainBase.ts
1283
- class ActionDomainBase {
1284
- domain;
1285
- allDomains;
1286
- actionSchema;
1287
- _listeners = [];
1288
- constructor(definition) {
1289
- this.domain = definition.domain;
1290
- this.allDomains = definition.allDomains;
1291
- this.actionSchema = definition.actionSchema;
1292
- }
1293
- addActionListener(listener) {
1294
- this._listeners.push(listener);
1295
- return () => {
1296
- this._listeners = this._listeners.filter((l) => l !== listener);
1230
+ type: this.handlerType,
1231
+ client: this.externalClient,
1232
+ transOrd: transport.transOrd,
1233
+ transType: transport.type,
1234
+ transInfo: transport.getRouteInfo(input)
1297
1235
  };
1298
1236
  }
1299
- _getActionObservers() {
1300
- return this._listeners;
1237
+ clearTransportCache() {
1238
+ for (const entry of this._transportCache.values()) {
1239
+ if (entry instanceof Promise) {
1240
+ entry.then((ready) => ready.methods.disconnect?.()).catch(() => {});
1241
+ } else {
1242
+ entry.methods.disconnect?.();
1243
+ }
1244
+ }
1245
+ this._transportCache.clear();
1301
1246
  }
1302
1247
  }
1248
+ var createExternalClientHandler = (config) => {
1249
+ return new ActionExternalClientHandler(config);
1250
+ };
1303
1251
 
1304
- // src/ActionDefinition/Domain/ActionDomain.ts
1305
- class ActionDomain extends ActionDomainBase {
1306
- _rootDomain;
1307
- _actionMap;
1308
- constructor(definition, {
1309
- rootDomain
1310
- }) {
1311
- super(definition);
1312
- this._rootDomain = rootDomain;
1313
- this._actionMap = this.createActionMap();
1314
- }
1315
- get rootDomain() {
1316
- return this._rootDomain;
1252
+ // src/ActionRuntime/ActionRuntime.ts
1253
+ class ActionRuntime {
1254
+ _coordinate;
1255
+ timeCreated;
1256
+ runtimeInfo = getAssumedRuntimeInfo();
1257
+ actionRouter;
1258
+ _pendingRunningActions = new Map;
1259
+ _registeredExternalHandlers = [];
1260
+ _applied = false;
1261
+ static getDefault() {
1262
+ return getDefaultActionRuntime();
1317
1263
  }
1318
- _collectActionObservers() {
1319
- return [...this._rootDomain._getActionObservers(), ...this._getActionObservers()];
1264
+ constructor(coordinate) {
1265
+ this._coordinate = coordinate.specifyIfUnset({
1266
+ insId: nanoid4(14)
1267
+ });
1268
+ this.timeCreated = Date.now();
1269
+ this.actionRouter = new ActionRouter({
1270
+ contextType: "runtime_to_handler" /* runtime_to_handler */,
1271
+ runtime: this
1272
+ });
1320
1273
  }
1321
- _registerRuntime(runtime2) {
1322
- this._rootDomain._registerRuntime(runtime2);
1274
+ get coordinate() {
1275
+ return this._coordinate;
1323
1276
  }
1324
- createChildDomain(subDomainDef) {
1325
- if (this.allDomains.includes(subDomainDef.domain)) {
1326
- throw err_nice_action.fromId("domain_already_exists_in_hierarchy" /* domain_already_exists_in_hierarchy */, {
1327
- domain: subDomainDef.domain,
1328
- allParentDomains: this.allDomains,
1329
- parentDomain: this.domain
1277
+ specifyRuntimeCoordinate(specifics) {
1278
+ if (specifics.envId != null && this._coordinate.envId !== specifics.envId) {
1279
+ throw err_nice_action.fromId("not_implemented" /* not_implemented */, {
1280
+ label: `updating RuntimeCoordinate with a different "envId" ("${this._coordinate.envId}" → "${specifics.envId}")`
1330
1281
  });
1331
1282
  }
1332
- return new ActionDomain({
1333
- allDomains: [...this.allDomains, subDomainDef.domain],
1334
- domain: subDomainDef.domain,
1335
- actionSchema: subDomainDef.actions
1336
- }, { rootDomain: this._rootDomain });
1337
- }
1338
- get action() {
1339
- return this._actionMap;
1283
+ this._coordinate = this._coordinate.specify(specifics);
1284
+ this.apply();
1340
1285
  }
1341
- actionsMap() {
1342
- return this._actionMap;
1286
+ registerRunningAction(ra) {
1287
+ this._pendingRunningActions.set(ra.cuid, ra);
1288
+ ra.addUpdateListeners([
1289
+ (update) => {
1290
+ if (update.type === "finished" /* finished */) {
1291
+ this._pendingRunningActions.delete(ra.cuid);
1292
+ }
1293
+ }
1294
+ ]);
1343
1295
  }
1344
- actionForId(id) {
1345
- const actionSchema = this.actionSchema[id];
1346
- if (!actionSchema) {
1347
- throw err_nice_action.fromId("action_id_not_in_domain" /* action_id_not_in_domain */, {
1348
- domain: this.domain,
1349
- actionId: id
1296
+ resolveIncomingActionPayload(json) {
1297
+ if (json.type === "request" /* request */) {
1298
+ this.handleActionPayloadWire(json).catch((err3) => {
1299
+ console.error(`[ActionRuntime] Incoming action [${json.domain}:${json.id}:${json.form}:${json.type}] unhandled:`, err3);
1350
1300
  });
1301
+ return;
1351
1302
  }
1352
- return new ActionCore(this, id);
1353
- }
1354
- wrapAsPartialLocalHandler(wrappedActionExecutor) {
1355
- const _handler = new ActionLocalHandler;
1356
- const executor = wrappedActionExecutor;
1357
- for (const actionKey in wrappedActionExecutor) {
1358
- if (!this.actionSchema[actionKey]) {
1359
- continue;
1360
- }
1361
- _handler.forAction(this.actionForId(actionKey), (request) => executor[request.id](request.input));
1362
- }
1363
- return _handler;
1364
- }
1365
- wrapAsLocalHandler(wrappedActionExecutor) {
1366
- const _handler = new ActionLocalHandler;
1367
- const executor = wrappedActionExecutor;
1368
- return _handler.forDomain(this, (request) => executor[request.id](request.input));
1369
- }
1370
- hydrateContext(id, contextData) {
1371
- return new ActionContext(this, id, {
1372
- timeCreated: contextData.timeCreated,
1373
- cuid: contextData.cuid,
1374
- routing: contextData.routing.map((item) => {
1375
- return {
1376
- runtime: new RuntimeCoordinate(item.runtime),
1377
- handler: item.handler,
1378
- time: item.time
1379
- };
1380
- }),
1381
- originClient: contextData.originClient ? new RuntimeCoordinate(contextData.originClient) : RuntimeCoordinate.unknown
1382
- });
1383
- }
1384
- isDomainAction(action) {
1385
- return isAction_Any_Instance(action) && action.domain === this.domain;
1303
+ this._pendingRunningActions.get(json.context.cuid)?._resolveFromJson(json);
1386
1304
  }
1387
- hydrateRequestPayload(serialized) {
1388
- if (serialized.type !== "request" /* request */) {
1389
- throw err_nice_action.fromId("hydration_action_state_mismatch" /* hydration_action_state_mismatch */, {
1390
- expected: "request" /* request */,
1391
- received: serialized.type
1392
- });
1305
+ async handleActionPayloadWire(wire) {
1306
+ let action;
1307
+ if (isActionPayload_Any_JsonObject(wire)) {
1308
+ const domain = this.actionRouter.domainManager.getActionDomainOrThrow(wire);
1309
+ action = domain.hydrateAnyAction(wire);
1393
1310
  }
1394
- if (serialized.domain !== this.domain) {
1395
- throw err_nice_action.fromId("hydration_domain_mismatch" /* hydration_domain_mismatch */, {
1396
- expected: this.domain,
1397
- received: serialized.domain
1398
- });
1311
+ if (action == null) {
1312
+ throw err_nice_action.fromId("wire_not_action_data" /* wire_not_action_data */);
1399
1313
  }
1400
- const id = serialized.id;
1401
- if (!this.actionSchema[id]) {
1402
- throw err_nice_action.fromId("hydration_action_id_not_found" /* hydration_action_id_not_found */, {
1403
- domain: this.domain,
1404
- actionId: serialized.id
1314
+ return this.handleActionPayload(action);
1315
+ }
1316
+ async handleActionPayload(action, options) {
1317
+ if (action.type === "request" /* request */) {
1318
+ const observers = action.context._domain._collectActionObservers();
1319
+ let handlerForAction;
1320
+ try {
1321
+ handlerForAction = this.getHandlerForActionOrThrow(action, options);
1322
+ } catch (err3) {
1323
+ const runningAction2 = new RunningAction({
1324
+ context: action.context,
1325
+ request: action
1326
+ });
1327
+ runningAction2.addUpdateListeners(observers);
1328
+ runningAction2._completeWithResult(action.errorResult(castNiceError(err3)));
1329
+ return runningAction2;
1330
+ }
1331
+ const runningAction = await handlerForAction.handleActionRequest(action, {
1332
+ ...options,
1333
+ targetLocalRuntime: this
1405
1334
  });
1335
+ runningAction.addUpdateListeners(observers);
1336
+ this._trySetupReturnDispatch(runningAction);
1337
+ return runningAction;
1406
1338
  }
1407
- const contextAction = this.hydrateContext(id, serialized.context);
1408
- return new ActionPayload_Request({ context: contextAction }, contextAction.deserializeInput(serialized.input), {
1409
- time: serialized.time
1339
+ throw err_nice_action.fromId("not_implemented" /* not_implemented */, {
1340
+ label: `Handling incoming action payloads of type "${action.type}"`
1410
1341
  });
1411
1342
  }
1412
- hydrateResultPayload(serialized) {
1413
- if (serialized.type !== "result" /* result */) {
1414
- throw err_nice_action.fromId("hydration_action_state_mismatch" /* hydration_action_state_mismatch */, {
1415
- expected: "result" /* result */,
1416
- received: serialized.type
1417
- });
1418
- }
1419
- if (serialized.domain !== this.domain) {
1420
- throw err_nice_action.fromId("hydration_domain_mismatch" /* hydration_domain_mismatch */, {
1421
- expected: this.domain,
1422
- received: serialized.domain
1423
- });
1424
- }
1425
- const id = serialized.id;
1426
- if (!this.actionSchema[id]) {
1427
- throw err_nice_action.fromId("hydration_action_id_not_found" /* hydration_action_id_not_found */, {
1428
- domain: this.domain,
1429
- actionId: serialized.id
1430
- });
1431
- }
1432
- const contextAction = this.hydrateContext(id, serialized.context);
1433
- const result = serialized.result.ok ? {
1434
- ok: true,
1435
- output: contextAction.schema.deserializeOutput(serialized.result.output)
1436
- } : serialized.result;
1437
- return new ActionPayload_Result({ context: contextAction }, result, {
1438
- time: serialized.time
1439
- });
1440
- }
1441
- hydrateAnyAction(actionJson) {
1442
- assertIsActionJson(actionJson);
1443
- if (actionJson.form === "data" /* data */) {
1444
- if (actionJson.type === "request" /* request */) {
1445
- return this.hydrateRequestPayload(actionJson);
1343
+ _getHandlerForAction(action, options) {
1344
+ const handlers = this.actionRouter.getRouteDataEntriesForAction(action);
1345
+ const targetExternalClient = options?.targetExternalClient;
1346
+ const possibleHandlers = handlers.filter((handler2) => {
1347
+ if (handler2.handlerType === "external" /* external */) {
1348
+ if (targetExternalClient && !targetExternalClient.isSameFor(handler2.externalClient).id) {
1349
+ return false;
1350
+ }
1351
+ return true;
1446
1352
  }
1447
- if (actionJson.type === "result" /* result */) {
1448
- return this.hydrateResultPayload(actionJson);
1353
+ if (targetExternalClient != null) {
1354
+ return false;
1449
1355
  }
1450
- }
1451
- return this.actionForId(actionJson.id);
1452
- }
1453
- async runAction(request, options) {
1454
- const allListeners = [
1455
- ...options?.listeners ?? [],
1456
- ...this._listeners
1457
- ];
1458
- return this._rootDomain._runAction(request, {
1459
- ...options,
1460
- listeners: allListeners
1356
+ if (action.type === "request" /* request */) {
1357
+ return true;
1358
+ }
1359
+ return false;
1461
1360
  });
1462
- }
1463
- createActionMap() {
1464
- const map = {};
1465
- for (const id in this.actionSchema) {
1466
- map[id] = new ActionCore(this, id);
1361
+ if (possibleHandlers.length === 0) {
1362
+ return;
1467
1363
  }
1468
- return map;
1469
- }
1470
- }
1471
- // src/ActionRuntime/ActionRuntimeManager.ts
1472
- class ActionRuntimeManager {
1473
- _runtimes = new Map;
1474
- _preferredRuntimeClientId = null;
1475
- _context;
1476
- constructor(context) {
1477
- this._context = context ?? {};
1364
+ const scoringExternalClient = targetExternalClient ?? RuntimeCoordinate.unknown;
1365
+ let handlerScore = -1;
1366
+ let handler;
1367
+ for (const possibleHandler of possibleHandlers) {
1368
+ if (possibleHandler.handlerType === "local" /* local */ && handler == null) {
1369
+ return possibleHandler;
1370
+ }
1371
+ if (possibleHandler.handlerType === "external" /* external */) {
1372
+ const score = scoringExternalClient.similarityLevel(possibleHandler.externalClient);
1373
+ if (score > handlerScore) {
1374
+ handlerScore = score;
1375
+ handler = possibleHandler;
1376
+ }
1377
+ }
1378
+ }
1379
+ return handler;
1478
1380
  }
1479
- registerRuntime(runtime2) {
1480
- const runtimeId = runtime2.coordinate.stringId;
1481
- if (this._runtimes.has(runtimeId)) {
1482
- throw err_nice_action.fromId("client_runtime_already_registered" /* client_runtime_already_registered */, {
1483
- context: this._context,
1484
- client: runtime2.coordinate
1381
+ getHandlerForActionOrThrow(action, options) {
1382
+ const handler = this._getHandlerForAction(action, options);
1383
+ if (handler == null) {
1384
+ throw err_nice_action.fromId("no_action_execution_handler" /* no_action_execution_handler */, {
1385
+ actionId: action.id,
1386
+ domain: action.domain,
1387
+ specifiedClient: options?.targetExternalClient
1485
1388
  });
1486
1389
  }
1487
- for (const id of runtime2.coordinate.toStringIds()) {
1488
- if (this._runtimes.has(id)) {
1489
- continue;
1490
- }
1491
- this._runtimes.set(id, runtime2);
1492
- }
1390
+ return handler;
1493
1391
  }
1494
- getRuntimeAndHandlerForAction(action, options, throwOnIssue) {
1495
- const localRuntime = options?.targetLocalRuntime;
1496
- if (localRuntime != null) {
1497
- const runtime2 = throwOnIssue ? this.getBestRuntimeOrThrow(options?.targetLocalRuntime?.coordinate) : this.getBestRuntime(options?.targetLocalRuntime?.coordinate);
1498
- if (runtime2 == null) {
1499
- return;
1392
+ addHandlers(handlers) {
1393
+ for (const handler of handlers) {
1394
+ if (handler.handlerType === "external" /* external */) {
1395
+ handler._setIncomingActionDataListener((json) => this.resolveIncomingActionPayload(json));
1396
+ this._registeredExternalHandlers.push(handler);
1500
1397
  }
1501
- const handler = runtime2._getHandlerForAction(action, options);
1502
- if (handler != null) {
1503
- return { handler, runtime: runtime2 };
1398
+ const handlerRouter = handler.getActionRouter();
1399
+ this.actionRouter.addDomainsFromOther(handlerRouter);
1400
+ if (this._applied) {
1401
+ this.apply();
1504
1402
  }
1505
- if (throwOnIssue) {
1506
- throw err_nice_action.fromId("no_action_execution_handler" /* no_action_execution_handler */, {
1507
- domain: action.domain,
1508
- actionId: action.id,
1509
- specifiedClient: localRuntime.coordinate
1510
- });
1403
+ for (const key of handlerRouter.getRegisteredKeys()) {
1404
+ const alreadyRegistered = this.actionRouter.getForKey(key).some((h) => h.cuid === handler.cuid);
1405
+ if (!alreadyRegistered) {
1406
+ this.actionRouter.addForKey(key, handler);
1407
+ }
1511
1408
  }
1512
1409
  }
1513
- for (const runtime2 of this._runtimes.values()) {
1514
- const handler = runtime2._getHandlerForAction(action);
1515
- if (handler) {
1516
- return { handler, runtime: runtime2 };
1517
- }
1410
+ return this;
1411
+ }
1412
+ connectTo(externalCoordinate, options) {
1413
+ const handler = new ActionExternalClientHandler({
1414
+ runtimeCoordinate: externalCoordinate,
1415
+ transports: options.transports,
1416
+ defaultTimeout: options.defaultTimeout
1417
+ });
1418
+ for (const domain of options.domains ?? []) {
1419
+ handler.forDomain(domain);
1518
1420
  }
1519
- if (throwOnIssue) {
1520
- throw err_nice_action.fromId("no_action_execution_handler" /* no_action_execution_handler */, {
1521
- domain: action.domain,
1522
- actionId: action.id,
1523
- specifiedClient: options?.targetLocalRuntime?.coordinate
1524
- });
1421
+ for (const action of options.actions ?? []) {
1422
+ handler.forAction(action);
1525
1423
  }
1424
+ this.addHandlers([handler, ...options.localHandlers ?? []]);
1425
+ this.apply();
1426
+ return handler;
1526
1427
  }
1527
- getRuntimeAndHandlerForActionOrThrow(action, options) {
1528
- return this.getRuntimeAndHandlerForAction(action, options, true);
1428
+ applyRuntimeForDomain(domain) {
1429
+ const rootDomain = domain.rootDomain;
1430
+ if (!rootDomain._hasRuntime(this)) {
1431
+ rootDomain._registerRuntime(this);
1432
+ }
1529
1433
  }
1530
- setPreferredRuntime(runtime2) {
1531
- const runtimeId = runtime2.coordinate.stringId;
1532
- this._preferredRuntimeClientId = runtimeId;
1434
+ apply() {
1435
+ this._applied = true;
1436
+ for (const domain of this.actionRouter.getDomains()) {
1437
+ this.applyRuntimeForDomain(domain);
1438
+ }
1439
+ return this;
1533
1440
  }
1534
- getPreferredRuntime() {
1535
- if (this._preferredRuntimeClientId) {
1536
- const runtime2 = this._runtimes.get(this._preferredRuntimeClientId);
1537
- if (runtime2) {
1538
- return runtime2;
1441
+ getReturnHandlerForOrigin(originClient) {
1442
+ if (originClient.envId === UNSET_RUNTIME_ENV_ID)
1443
+ return;
1444
+ let bestScore = -1;
1445
+ let bestHandler;
1446
+ for (const handler of this._registeredExternalHandlers) {
1447
+ const score = originClient.similarityLevel(handler.externalClient);
1448
+ if (score > bestScore) {
1449
+ bestScore = score;
1450
+ bestHandler = handler;
1539
1451
  }
1540
1452
  }
1541
- return this._runtimes.values().next().value;
1453
+ return bestScore > 0 ? bestHandler : undefined;
1542
1454
  }
1543
- getBestRuntimeForSpecifier(clientSpecifier) {
1544
- const actionClient = new RuntimeCoordinate(clientSpecifier);
1545
- const ids = actionClient.toStringIds();
1546
- for (const id of ids) {
1547
- const runtime2 = this._runtimes.get(id);
1548
- if (runtime2) {
1549
- return runtime2;
1550
- }
1455
+ resetRuntime() {
1456
+ for (const ra of this._pendingRunningActions.values()) {
1457
+ ra._abort(err_nice_action.fromId("runtime_reset" /* runtime_reset */));
1458
+ }
1459
+ for (const handler of this._registeredExternalHandlers) {
1460
+ handler.clearTransportCache();
1551
1461
  }
1552
1462
  }
1553
- getBestRuntime(clientSpecifier) {
1554
- return clientSpecifier != null ? this.getBestRuntimeForSpecifier(clientSpecifier) : this.getPreferredRuntime();
1463
+ _trySetupReturnDispatch(runningAction) {
1464
+ const originClient = runningAction.context.originClient;
1465
+ if (originClient.envId === UNSET_RUNTIME_ENV_ID || originClient.isSameFor(this._coordinate).id) {
1466
+ return;
1467
+ }
1468
+ runningAction.addUpdateListeners([
1469
+ (update) => {
1470
+ if (update.type === "finished" /* finished */ && update.finishType === "success" /* success */) {
1471
+ const returnHandler = this.getReturnHandlerForOrigin(originClient);
1472
+ returnHandler?.sendReturnPayload(update.response, { targetLocalRuntime: this }).catch(() => {});
1473
+ }
1474
+ }
1475
+ ]);
1555
1476
  }
1556
- hasRuntime(runtime2) {
1557
- return this._runtimes.has(runtime2.coordinate.stringId);
1477
+ }
1478
+ var runtimeState = {
1479
+ defaultLocalRuntime: undefined,
1480
+ assumedRuntimeInfo: undefined
1481
+ };
1482
+ function getDefaultActionRuntime() {
1483
+ if (runtimeState.assumedRuntimeInfo == null) {
1484
+ runtimeState.assumedRuntimeInfo = getAssumedRuntimeInfo();
1558
1485
  }
1559
- getBestRuntimeOrThrow(specifier) {
1560
- const runtime2 = this.getBestRuntime(specifier);
1561
- if (!runtime2) {
1562
- if (specifier == null) {
1563
- throw err_nice_action.fromId("no_client_runtimes_registered" /* no_client_runtimes_registered */, {
1564
- context: this._context
1565
- });
1566
- }
1567
- throw err_nice_action.fromId("client_runtime_not_registered" /* client_runtime_not_registered */, {
1568
- context: this._context,
1569
- clientStringId: runtimeCoordinateToStringIds(specifier)[0]
1570
- });
1571
- }
1572
- return runtime2;
1486
+ if (runtimeState.defaultLocalRuntime == null) {
1487
+ runtimeState.defaultLocalRuntime = new ActionRuntime(RuntimeCoordinate.unknown.specify({
1488
+ perId: `${runtimeState.assumedRuntimeInfo?.runtimeName ?? "unknown"}-runtime`
1489
+ }));
1573
1490
  }
1491
+ return runtimeState.defaultLocalRuntime;
1574
1492
  }
1575
1493
 
1576
- // src/ActionDefinition/Domain/ActionRootDomain.ts
1577
- class ActionRootDomain extends ActionDomainBase {
1578
- domainDefinition;
1579
- _actionRuntimeManager;
1580
- constructor(domainDefinition) {
1581
- const domainId = domainDefinition.domain;
1582
- super({
1583
- domain: domainId,
1584
- allDomains: [domainId],
1585
- actionSchema: {}
1494
+ // src/ActionRuntime/Handler/Local/ActionLocalHandler.ts
1495
+ class ActionLocalHandler extends ActionHandler {
1496
+ handlerType = "local" /* local */;
1497
+ actionRouter = new ActionRouter({
1498
+ contextType: "handler_route" /* handler_route */,
1499
+ handler: this
1500
+ });
1501
+ constructor() {
1502
+ super();
1503
+ }
1504
+ forDomain(domain, handler) {
1505
+ this.actionRouter.forDomain(domain, handler);
1506
+ return this;
1507
+ }
1508
+ forAction(action, handler) {
1509
+ this.actionRouter.forAction(action, handler);
1510
+ return this;
1511
+ }
1512
+ forActionIds(domain, ids, handler) {
1513
+ this.actionRouter.forActionIds(domain, ids, handler);
1514
+ return this;
1515
+ }
1516
+ forDomainActionCases(domain, cases) {
1517
+ this.actionRouter.forDomainActionCases(domain, cases);
1518
+ return this;
1519
+ }
1520
+ async handleActionRequest(action, config) {
1521
+ const targetLocalRuntime = config?.targetLocalRuntime ?? ActionRuntime.getDefault();
1522
+ const handler = this.actionRouter.getRouteDataForActionOrThrow(action, {
1523
+ targetLocalRuntime
1586
1524
  });
1587
- this.domainDefinition = domainDefinition;
1588
- this._actionRuntimeManager = new ActionRuntimeManager({ domain: domainId });
1525
+ action.context.addRouteItem({
1526
+ runtime: targetLocalRuntime.coordinate,
1527
+ handler: this.toHandlerRouteItem(),
1528
+ time: Date.now()
1529
+ });
1530
+ const runningAction = new RunningAction({
1531
+ context: action.context,
1532
+ request: action,
1533
+ parentCuid: peekHandlerCuid(),
1534
+ callSite: action._callSite ?? new Error().stack
1535
+ });
1536
+ this._handleRunningAction(handler, runningAction).catch((err3) => {
1537
+ if (err3 instanceof NiceError) {
1538
+ runningAction._completeWithResult(action.errorResult(err3));
1539
+ } else if (isNiceErrorObject(err3)) {
1540
+ runningAction._completeWithResult(action.errorResult(castNiceError2(err3)));
1541
+ } else {
1542
+ runningAction._abort(err3);
1543
+ }
1544
+ });
1545
+ return runningAction;
1546
+ }
1547
+ async _handleRunningAction(handler, runningAction) {
1548
+ const state = runningAction.state;
1549
+ if (state.result != null) {
1550
+ return;
1551
+ }
1552
+ await Promise.resolve();
1553
+ pushHandlerCuid(runningAction.cuid);
1554
+ try {
1555
+ const rawResult = await handler(state.request);
1556
+ let result;
1557
+ if (rawResult instanceof ActionPayload_Result) {
1558
+ result = rawResult;
1559
+ } else if (rawResult != null && isActionPayload_Result_JsonObject(rawResult)) {
1560
+ const domain = this.actionRouter.domainManager.getActionDomainOrThrow(state.request);
1561
+ result = domain.hydrateResultPayload(rawResult);
1562
+ } else {
1563
+ result = state.request.successResult(rawResult);
1564
+ }
1565
+ runningAction._completeWithResult(result);
1566
+ } finally {
1567
+ popHandlerCuid();
1568
+ }
1569
+ }
1570
+ async handlePayloadWireOrThrow(wire, config) {
1571
+ const hydratedAction = this.actionRouter.domainManager.hydrateActionPayload(wire);
1572
+ if (!(hydratedAction instanceof ActionPayload_Request)) {
1573
+ throw err_nice_action.fromId("wire_action_not_payload" /* wire_action_not_payload */, {
1574
+ domain: hydratedAction.domain,
1575
+ actionId: hydratedAction.id,
1576
+ actionState: hydratedAction.type ?? hydratedAction.form
1577
+ });
1578
+ }
1579
+ return await this.handleActionRequest(hydratedAction, config);
1580
+ }
1581
+ toJsonObject() {
1582
+ return {
1583
+ type: this.handlerType
1584
+ };
1585
+ }
1586
+ toHandlerRouteItem() {
1587
+ return {
1588
+ type: this.handlerType
1589
+ };
1590
+ }
1591
+ }
1592
+ var createLocalHandler = () => {
1593
+ return new ActionLocalHandler;
1594
+ };
1595
+
1596
+ // src/utils/isAction_Context_JsonObject.ts
1597
+ var isAction_Context_JsonObject = (obj) => {
1598
+ return isAction_Base_JsonObject(obj) && obj.form === "context" /* context */;
1599
+ };
1600
+
1601
+ // src/utils/isAction_Core_JsonObject.ts
1602
+ var isAction_Core_JsonObject = (obj) => {
1603
+ return isAction_Base_JsonObject(obj) && obj.form === "core" /* core */;
1604
+ };
1605
+
1606
+ // src/utils/isAction_Any_JsonObject.ts
1607
+ function isAction_Any_JsonObject(obj) {
1608
+ return isActionPayload_Any_JsonObject(obj) || isAction_Context_JsonObject(obj) || isAction_Core_JsonObject(obj);
1609
+ }
1610
+
1611
+ // src/utils/assertIsActionJson.ts
1612
+ function assertIsActionJson(obj) {
1613
+ if (!isAction_Any_JsonObject(obj)) {
1614
+ throw err_nice_action.fromId("wire_not_action_data" /* wire_not_action_data */);
1615
+ }
1616
+ }
1617
+
1618
+ // src/utils/isAction_Any_Instance.ts
1619
+ function isAction_Any_Instance(value) {
1620
+ return value instanceof ActionCore || value instanceof ActionPayload || value instanceof ActionContext;
1621
+ }
1622
+
1623
+ // src/ActionDefinition/Domain/ActionDomainBase.ts
1624
+ class ActionDomainBase {
1625
+ domain;
1626
+ allDomains;
1627
+ actionSchema;
1628
+ _listeners = [];
1629
+ constructor(definition) {
1630
+ this.domain = definition.domain;
1631
+ this.allDomains = definition.allDomains;
1632
+ this.actionSchema = definition.actionSchema;
1633
+ }
1634
+ addActionListener(listener) {
1635
+ this._listeners.push(listener);
1636
+ return () => {
1637
+ this._listeners = this._listeners.filter((l) => l !== listener);
1638
+ };
1639
+ }
1640
+ _getActionObservers() {
1641
+ return this._listeners;
1642
+ }
1643
+ }
1644
+
1645
+ // src/ActionDefinition/Domain/ActionDomain.ts
1646
+ class ActionDomain extends ActionDomainBase {
1647
+ _rootDomain;
1648
+ _actionMap;
1649
+ constructor(definition, {
1650
+ rootDomain
1651
+ }) {
1652
+ super(definition);
1653
+ this._rootDomain = rootDomain;
1654
+ this._actionMap = this.createActionMap();
1655
+ }
1656
+ get rootDomain() {
1657
+ return this._rootDomain;
1658
+ }
1659
+ _collectActionObservers() {
1660
+ return [...this._rootDomain._getActionObservers(), ...this._getActionObservers()];
1661
+ }
1662
+ _registerRuntime(runtime2) {
1663
+ this._rootDomain._registerRuntime(runtime2);
1589
1664
  }
1590
1665
  createChildDomain(subDomainDef) {
1591
1666
  if (this.allDomains.includes(subDomainDef.domain)) {
@@ -1599,440 +1674,402 @@ class ActionRootDomain extends ActionDomainBase {
1599
1674
  allDomains: [...this.allDomains, subDomainDef.domain],
1600
1675
  domain: subDomainDef.domain,
1601
1676
  actionSchema: subDomainDef.actions
1602
- }, { rootDomain: this });
1603
- }
1604
- _registerRuntime(runtime2) {
1605
- this._actionRuntimeManager.registerRuntime(runtime2);
1677
+ }, { rootDomain: this._rootDomain });
1606
1678
  }
1607
- _hasRuntime(runtime2) {
1608
- return this._actionRuntimeManager.hasRuntime(runtime2);
1679
+ get action() {
1680
+ return this._actionMap;
1609
1681
  }
1610
- getRuntime(clientSpecifier) {
1611
- return this._actionRuntimeManager.getBestRuntimeForSpecifier(clientSpecifier);
1682
+ actionsMap() {
1683
+ return this._actionMap;
1612
1684
  }
1613
- async _runAction(actionPayload, options) {
1614
- const allListeners = [...this._listeners, ...options?.listeners ?? []];
1615
- let handlerAndRuntime;
1616
- try {
1617
- handlerAndRuntime = this._actionRuntimeManager.getRuntimeAndHandlerForActionOrThrow(actionPayload, options);
1618
- } catch (err2) {
1619
- const runningAction2 = new RunningAction({
1620
- context: actionPayload.context,
1621
- request: actionPayload,
1622
- callSite: actionPayload._callSite
1685
+ actionForId(id) {
1686
+ const actionSchema = this.actionSchema[id];
1687
+ if (!actionSchema) {
1688
+ throw err_nice_action.fromId("action_id_not_in_domain" /* action_id_not_in_domain */, {
1689
+ domain: this.domain,
1690
+ actionId: id
1623
1691
  });
1624
- runningAction2.addUpdateListeners(allListeners);
1625
- runningAction2._failWithError(err2);
1626
- throw err2;
1627
1692
  }
1628
- const { handler, runtime: runtime2 } = handlerAndRuntime;
1629
- actionPayload.context._setOriginClient(runtime2.coordinate);
1630
- const runningAction = await handler.handleActionRequest(actionPayload, {
1631
- targetLocalRuntime: runtime2
1632
- });
1633
- runningAction.addUpdateListeners(allListeners);
1634
- return runningAction;
1693
+ return new ActionCore(this, id);
1635
1694
  }
1636
- }
1637
- // src/ActionDefinition/Domain/helpers/createRootActionDomain.ts
1638
- var createActionRootDomain = (definition) => {
1639
- return new ActionRootDomain(definition);
1640
- };
1641
- // src/ActionDefinition/Schema/ActionSchema.ts
1642
- import { extractMessageFromStandardSchema } from "@nice-code/common-errors";
1643
- class ActionSchema {
1644
- _errorDeclarations = [];
1645
- inputOptions;
1646
- outputOptions;
1647
- get inputSchema() {
1648
- return this.inputOptions?.schema;
1695
+ wrapAsPartialLocalHandler(wrappedActionExecutor) {
1696
+ const _handler = new ActionLocalHandler;
1697
+ const executor = wrappedActionExecutor;
1698
+ for (const actionKey in wrappedActionExecutor) {
1699
+ if (!this.actionSchema[actionKey]) {
1700
+ continue;
1701
+ }
1702
+ _handler.forAction(this.actionForId(actionKey), (request) => executor[request.id](request.input));
1703
+ }
1704
+ return _handler;
1649
1705
  }
1650
- get outputSchema() {
1651
- return this.outputOptions?.schema;
1706
+ wrapAsLocalHandler(wrappedActionExecutor) {
1707
+ const _handler = new ActionLocalHandler;
1708
+ const executor = wrappedActionExecutor;
1709
+ return _handler.forDomain(this, (request) => executor[request.id](request.input));
1652
1710
  }
1653
- input(options) {
1654
- this.inputOptions = options;
1655
- return this;
1711
+ hydrateContext(id, contextData) {
1712
+ return new ActionContext(this, id, {
1713
+ timeCreated: contextData.timeCreated,
1714
+ cuid: contextData.cuid,
1715
+ routing: contextData.routing.map((item) => {
1716
+ return {
1717
+ runtime: new RuntimeCoordinate(item.runtime),
1718
+ handler: item.handler,
1719
+ time: item.time
1720
+ };
1721
+ }),
1722
+ originClient: contextData.originClient ? new RuntimeCoordinate(contextData.originClient) : RuntimeCoordinate.unknown
1723
+ });
1656
1724
  }
1657
- output(options) {
1658
- this.outputOptions = options;
1659
- return this;
1725
+ isDomainAction(action) {
1726
+ return isAction_Any_Instance(action) && action.domain === this.domain;
1660
1727
  }
1661
- throws(domain, ids) {
1662
- this._errorDeclarations.push({ _domain: domain, _ids: ids });
1663
- return this;
1664
- }
1665
- serializeInput(rawInput) {
1666
- if (this.inputOptions?.serialization) {
1667
- return this.inputOptions.serialization.serialize(rawInput);
1668
- }
1669
- return rawInput;
1670
- }
1671
- deserializeInput(serialized) {
1672
- if (this.inputOptions?.serialization) {
1673
- return this.inputOptions.serialization.deserialize(serialized);
1674
- }
1675
- return serialized;
1676
- }
1677
- validateInput(value, meta) {
1678
- if (this.inputOptions?.schema == null) {
1679
- return value;
1728
+ hydrateRequestPayload(serialized) {
1729
+ if (serialized.type !== "request" /* request */) {
1730
+ throw err_nice_action.fromId("hydration_action_state_mismatch" /* hydration_action_state_mismatch */, {
1731
+ expected: "request" /* request */,
1732
+ received: serialized.type
1733
+ });
1680
1734
  }
1681
- const result = this.inputOptions.schema["~standard"].validate(value);
1682
- if (result instanceof Promise) {
1683
- throw err_nice_action.fromId("action_input_validation_promise" /* action_input_validation_promise */, {
1684
- domain: meta.domain,
1685
- actionId: meta.actionId
1735
+ if (serialized.domain !== this.domain) {
1736
+ throw err_nice_action.fromId("hydration_domain_mismatch" /* hydration_domain_mismatch */, {
1737
+ expected: this.domain,
1738
+ received: serialized.domain
1686
1739
  });
1687
1740
  }
1688
- if (result.issues != null) {
1689
- throw err_nice_action.fromId("action_input_validation_failed" /* action_input_validation_failed */, {
1690
- domain: meta.domain,
1691
- actionId: meta.actionId,
1692
- validationMessage: extractMessageFromStandardSchema(result)
1741
+ const id = serialized.id;
1742
+ if (!this.actionSchema[id]) {
1743
+ throw err_nice_action.fromId("hydration_action_id_not_found" /* hydration_action_id_not_found */, {
1744
+ domain: this.domain,
1745
+ actionId: serialized.id
1693
1746
  });
1694
1747
  }
1695
- return result.value;
1748
+ const contextAction = this.hydrateContext(id, serialized.context);
1749
+ return new ActionPayload_Request({ context: contextAction }, contextAction.deserializeInput(serialized.input), {
1750
+ time: serialized.time
1751
+ });
1696
1752
  }
1697
- validateOutput(value, meta) {
1698
- if (this.outputOptions?.schema == null) {
1699
- return value;
1753
+ hydrateResultPayload(serialized) {
1754
+ if (serialized.type !== "result" /* result */) {
1755
+ throw err_nice_action.fromId("hydration_action_state_mismatch" /* hydration_action_state_mismatch */, {
1756
+ expected: "result" /* result */,
1757
+ received: serialized.type
1758
+ });
1700
1759
  }
1701
- const result = this.outputOptions.schema["~standard"].validate(value);
1702
- if (result instanceof Promise) {
1703
- throw err_nice_action.fromId("action_output_validation_promise" /* action_output_validation_promise */, {
1704
- domain: meta.domain,
1705
- actionId: meta.actionId
1760
+ if (serialized.domain !== this.domain) {
1761
+ throw err_nice_action.fromId("hydration_domain_mismatch" /* hydration_domain_mismatch */, {
1762
+ expected: this.domain,
1763
+ received: serialized.domain
1706
1764
  });
1707
1765
  }
1708
- if (result.issues != null) {
1709
- throw err_nice_action.fromId("action_output_validation_failed" /* action_output_validation_failed */, {
1710
- domain: meta.domain,
1711
- actionId: meta.actionId,
1712
- validationMessage: extractMessageFromStandardSchema(result)
1766
+ const id = serialized.id;
1767
+ if (!this.actionSchema[id]) {
1768
+ throw err_nice_action.fromId("hydration_action_id_not_found" /* hydration_action_id_not_found */, {
1769
+ domain: this.domain,
1770
+ actionId: serialized.id
1713
1771
  });
1714
1772
  }
1715
- return result.value;
1773
+ const contextAction = this.hydrateContext(id, serialized.context);
1774
+ const result = serialized.result.ok ? {
1775
+ ok: true,
1776
+ output: contextAction.schema.deserializeOutput(serialized.result.output)
1777
+ } : serialized.result;
1778
+ return new ActionPayload_Result({ context: contextAction }, result, {
1779
+ time: serialized.time
1780
+ });
1716
1781
  }
1717
- serializeOutput(rawOutput) {
1718
- if (this.outputOptions?.serialization) {
1719
- return this.outputOptions.serialization.serialize(rawOutput);
1782
+ hydrateAnyAction(actionJson) {
1783
+ assertIsActionJson(actionJson);
1784
+ if (actionJson.form === "data" /* data */) {
1785
+ if (actionJson.type === "request" /* request */) {
1786
+ return this.hydrateRequestPayload(actionJson);
1787
+ }
1788
+ if (actionJson.type === "result" /* result */) {
1789
+ return this.hydrateResultPayload(actionJson);
1790
+ }
1720
1791
  }
1721
- return rawOutput;
1792
+ return this.actionForId(actionJson.id);
1722
1793
  }
1723
- deserializeOutput(serialized) {
1724
- if (this.outputOptions?.serialization) {
1725
- return this.outputOptions.serialization.deserialize(serialized);
1794
+ async runAction(request, options) {
1795
+ const allListeners = [
1796
+ ...options?.listeners ?? [],
1797
+ ...this._listeners
1798
+ ];
1799
+ return this._rootDomain._runAction(request, {
1800
+ ...options,
1801
+ listeners: allListeners
1802
+ });
1803
+ }
1804
+ createActionMap() {
1805
+ const map = {};
1806
+ for (const id in this.actionSchema) {
1807
+ map[id] = new ActionCore(this, id);
1726
1808
  }
1727
- return serialized;
1809
+ return map;
1728
1810
  }
1729
1811
  }
1730
- var actionSchema = () => {
1731
- return new ActionSchema;
1732
- };
1733
- // src/ActionRuntime/Handler/ExternalClient/ActionExternalClientHandler.ts
1734
- import { nanoid as nanoid4 } from "nanoid";
1735
-
1736
- // src/ActionRuntime/Handler/ExternalClient/Transport/err_nice_transport.ts
1737
- import { err as err2 } from "@nice-code/error";
1738
-
1739
- // src/ActionRuntime/Handler/ExternalClient/err_nice_external_client.ts
1740
- var err_nice_external_client = err_nice_action.createChildDomain({
1741
- domain: "err_nice_external_client",
1742
- schema: {}
1743
- });
1744
-
1745
- // src/ActionRuntime/Handler/ExternalClient/Transport/err_nice_transport.ts
1746
- var EErrId_NiceTransport;
1747
- ((EErrId_NiceTransport2) => {
1748
- EErrId_NiceTransport2["timeout"] = "timeout";
1749
- EErrId_NiceTransport2["not_found"] = "not_found";
1750
- EErrId_NiceTransport2["unsupported"] = "unsupported";
1751
- EErrId_NiceTransport2["initialization_failed"] = "initialization_failed";
1752
- EErrId_NiceTransport2["send_failed"] = "send_failed";
1753
- EErrId_NiceTransport2["invalid_action_response"] = "invalid_action_response";
1754
- })(EErrId_NiceTransport ||= {});
1755
- var err_nice_transport = err_nice_external_client.createChildDomain({
1756
- domain: "err_nice_transport",
1757
- schema: {
1758
- ["timeout" /* timeout */]: err2({
1759
- message: ({ timeout }) => `ActionConnect transport timed out after ${timeout}ms.`
1760
- }),
1761
- ["not_found" /* not_found */]: err2({
1762
- message: ({ actionId }) => `No connected transport found for action "${actionId}".`
1763
- }),
1764
- ["unsupported" /* unsupported */]: err2({
1765
- message: ({ transportTypes }) => `${transportTypes.length} Transport(s) [${transportTypes.join(", ")}] found but returned "unsupported" status.`
1766
- }),
1767
- ["initialization_failed" /* initialization_failed */]: err2({
1768
- message: ({ actionId }) => `Transports found for action "${actionId}", but none are ready.`
1769
- }),
1770
- ["send_failed" /* send_failed */]: err2({
1771
- message: ({ actionId, httpStatusCode, message }) => `Failed to send action "${actionId}" [${httpStatusCode ?? "Unknown status"}]: ${message ?? "Unknown error"}.`,
1772
- httpStatusCode: ({ httpStatusCode }) => httpStatusCode ?? 500
1773
- }),
1774
- ["invalid_action_response" /* invalid_action_response */]: err2({
1775
- message: ({ actionId }) => `Invalid action response JSON structure for action "${actionId}"`
1776
- })
1777
- }
1778
- });
1779
-
1780
- // src/ActionRuntime/Handler/ExternalClient/Transport/Transport.types.ts
1781
- var ETransportType;
1782
- ((ETransportType2) => {
1783
- ETransportType2["ws"] = "ws";
1784
- ETransportType2["http"] = "http";
1785
- ETransportType2["custom"] = "custom";
1786
- })(ETransportType ||= {});
1787
- var ETransportStatus;
1788
- ((ETransportStatus2) => {
1789
- ETransportStatus2["uninitialized"] = "uninitialized";
1790
- ETransportStatus2["unsupported"] = "unsupported";
1791
- ETransportStatus2["initializing"] = "initializing";
1792
- ETransportStatus2["ready"] = "ready";
1793
- ETransportStatus2["failed"] = "failed";
1794
- })(ETransportStatus ||= {});
1795
-
1796
- // src/ActionRuntime/Handler/ExternalClient/Transport/ConnectionTransportManager.ts
1797
- class ConnectionTransportManager {
1798
- _cache;
1799
- _transports = [];
1800
- constructor(_cache) {
1801
- this._cache = _cache;
1802
- }
1803
- addTransport(transport) {
1804
- this._transports.push(transport);
1805
- }
1806
- getPreferredTransport() {
1807
- return this._transports[0];
1812
+ // src/ActionRuntime/ActionRuntimeManager.ts
1813
+ class ActionRuntimeManager {
1814
+ _runtimes = new Map;
1815
+ _preferredRuntimeClientId = null;
1816
+ _context;
1817
+ constructor(context) {
1818
+ this._context = context ?? {};
1808
1819
  }
1809
- async getReadyTransport(routeActionParams) {
1810
- const action = routeActionParams.action;
1811
- const candidates = [];
1812
- const unavailableTransports = [];
1813
- for (const transport of this._transports) {
1814
- const cacheKey = transport.getCacheKey(routeActionParams);
1815
- if (cacheKey != null) {
1816
- const cached = this._cache.get(cacheKey);
1817
- if (cached != null) {
1818
- if (cached instanceof Promise) {
1819
- candidates.push(cached);
1820
- continue;
1821
- }
1822
- candidates.push(Promise.resolve({ ...cached, transport }));
1823
- break;
1824
- }
1820
+ registerRuntime(runtime2) {
1821
+ const runtimeId = runtime2.coordinate.stringId;
1822
+ if (this._runtimes.has(runtimeId)) {
1823
+ throw err_nice_action.fromId("client_runtime_already_registered" /* client_runtime_already_registered */, {
1824
+ context: this._context,
1825
+ client: runtime2.coordinate
1826
+ });
1827
+ }
1828
+ for (const id of runtime2.coordinate.toStringIds()) {
1829
+ if (this._runtimes.has(id)) {
1830
+ continue;
1825
1831
  }
1826
- const statusInfo = transport.getTransport(routeActionParams);
1827
- if (statusInfo.status === "ready" /* ready */) {
1828
- const readyData = statusInfo.readyData;
1829
- if (cacheKey != null) {
1830
- this._cache.set(cacheKey, { methods: readyData, transport });
1831
- readyData.addOnDisconnectListener?.(() => this._cache.delete(cacheKey));
1832
- }
1833
- candidates.push(Promise.resolve({ methods: readyData, transport }));
1834
- break;
1832
+ this._runtimes.set(id, runtime2);
1833
+ }
1834
+ }
1835
+ getRuntimeAndHandlerForAction(action, options, throwOnIssue) {
1836
+ const localRuntime = options?.targetLocalRuntime;
1837
+ if (localRuntime != null) {
1838
+ const runtime2 = throwOnIssue ? this.getBestRuntimeOrThrow(options?.targetLocalRuntime?.coordinate) : this.getBestRuntime(options?.targetLocalRuntime?.coordinate);
1839
+ if (runtime2 == null) {
1840
+ return;
1835
1841
  }
1836
- if (statusInfo.status === "unsupported" /* unsupported */) {
1837
- unavailableTransports.push(transport);
1838
- continue;
1842
+ const handler = runtime2._getHandlerForAction(action, options);
1843
+ if (handler != null) {
1844
+ return { handler, runtime: runtime2 };
1839
1845
  }
1840
- if (statusInfo.status === "initializing" /* initializing */) {
1841
- const promise = statusInfo.initializationPromise.then((info) => {
1842
- if (info.status === "failed" /* failed */) {
1843
- throw info.error;
1844
- }
1845
- if (info.status === "unsupported" /* unsupported */) {
1846
- throw err_nice_transport.fromId("unsupported" /* unsupported */, {
1847
- transportTypes: [transport.type]
1848
- });
1849
- }
1850
- const readyData = info.readyData;
1851
- if (cacheKey != null) {
1852
- this._cache.set(cacheKey, { methods: readyData, transport });
1853
- readyData.addOnDisconnectListener?.(() => this._cache.delete(cacheKey));
1854
- }
1855
- return { methods: readyData, transport };
1856
- }).catch((e) => {
1857
- if (cacheKey != null)
1858
- this._cache.delete(cacheKey);
1859
- throw e;
1846
+ if (throwOnIssue) {
1847
+ throw err_nice_action.fromId("no_action_execution_handler" /* no_action_execution_handler */, {
1848
+ domain: action.domain,
1849
+ actionId: action.id,
1850
+ specifiedClient: localRuntime.coordinate
1860
1851
  });
1861
- if (cacheKey != null) {
1862
- this._cache.set(cacheKey, promise);
1863
- }
1864
- candidates.push(promise);
1865
1852
  }
1866
1853
  }
1867
- if (candidates.length === 0) {
1868
- if (unavailableTransports.length > 0) {
1869
- throw err_nice_transport.fromId("unsupported" /* unsupported */, {
1870
- transportTypes: unavailableTransports.map((t) => t.type)
1871
- });
1854
+ for (const runtime2 of this._runtimes.values()) {
1855
+ const handler = runtime2._getHandlerForAction(action);
1856
+ if (handler) {
1857
+ return { handler, runtime: runtime2 };
1872
1858
  }
1873
- throw err_nice_transport.fromId("not_found" /* not_found */, {
1874
- actionId: action.id
1859
+ }
1860
+ if (throwOnIssue) {
1861
+ throw err_nice_action.fromId("no_action_execution_handler" /* no_action_execution_handler */, {
1862
+ domain: action.domain,
1863
+ actionId: action.id,
1864
+ specifiedClient: options?.targetLocalRuntime?.coordinate
1875
1865
  });
1876
1866
  }
1877
- let lastError;
1878
- for (const candidate of candidates) {
1879
- try {
1880
- return await candidate;
1881
- } catch (e) {
1882
- lastError = e;
1867
+ }
1868
+ getRuntimeAndHandlerForActionOrThrow(action, options) {
1869
+ return this.getRuntimeAndHandlerForAction(action, options, true);
1870
+ }
1871
+ setPreferredRuntime(runtime2) {
1872
+ const runtimeId = runtime2.coordinate.stringId;
1873
+ this._preferredRuntimeClientId = runtimeId;
1874
+ }
1875
+ getPreferredRuntime() {
1876
+ if (this._preferredRuntimeClientId) {
1877
+ const runtime2 = this._runtimes.get(this._preferredRuntimeClientId);
1878
+ if (runtime2) {
1879
+ return runtime2;
1883
1880
  }
1884
1881
  }
1885
- throw err_nice_transport.fromId("initialization_failed" /* initialization_failed */, {
1886
- actionId: action.id
1887
- }).withOriginError(lastError);
1882
+ return this._runtimes.values().next().value;
1883
+ }
1884
+ getBestRuntimeForSpecifier(clientSpecifier) {
1885
+ const actionClient = new RuntimeCoordinate(clientSpecifier);
1886
+ const ids = actionClient.toStringIds();
1887
+ for (const id of ids) {
1888
+ const runtime2 = this._runtimes.get(id);
1889
+ if (runtime2) {
1890
+ return runtime2;
1891
+ }
1892
+ }
1893
+ }
1894
+ getBestRuntime(clientSpecifier) {
1895
+ return clientSpecifier != null ? this.getBestRuntimeForSpecifier(clientSpecifier) : this.getPreferredRuntime();
1896
+ }
1897
+ hasRuntime(runtime2) {
1898
+ return this._runtimes.has(runtime2.coordinate.stringId);
1899
+ }
1900
+ getBestRuntimeOrThrow(specifier) {
1901
+ const runtime2 = this.getBestRuntime(specifier);
1902
+ if (!runtime2) {
1903
+ if (specifier == null) {
1904
+ throw err_nice_action.fromId("no_client_runtimes_registered" /* no_client_runtimes_registered */, {
1905
+ context: this._context
1906
+ });
1907
+ }
1908
+ throw err_nice_action.fromId("client_runtime_not_registered" /* client_runtime_not_registered */, {
1909
+ context: this._context,
1910
+ clientStringId: runtimeCoordinateToStringIds(specifier)[0]
1911
+ });
1912
+ }
1913
+ return runtime2;
1888
1914
  }
1889
1915
  }
1890
1916
 
1891
- // src/ActionRuntime/Handler/ExternalClient/ActionExternalClientHandler.ts
1892
- class ActionExternalClientHandler extends ActionHandler {
1893
- externalClient;
1894
- handlerType = "external" /* external */;
1895
- cuid;
1896
- _defaultTimeout;
1897
- _transportCache = new Map;
1898
- transportManager = new ConnectionTransportManager(this._transportCache);
1899
- _incomingActionDataListeners = [];
1900
- actionRouter = new ActionRouter({
1901
- contextType: "handler_route" /* handler_route */,
1902
- handler: this
1903
- });
1904
- constructor({
1905
- runtimeCoordinate: externalClientSpecifier,
1906
- transports,
1907
- defaultTimeout
1908
- }) {
1909
- super();
1910
- this.externalClient = externalClientSpecifier;
1911
- this.cuid = nanoid4();
1912
- this._defaultTimeout = defaultTimeout ?? DEFAULT_TRANSPORT_TIMEOUT;
1913
- for (const transport of transports) {
1914
- const connection = transport._createConnection({
1915
- resolvers: {
1916
- onIncomingActionDataJson: (json) => {
1917
- for (const l of this._incomingActionDataListeners)
1918
- l(json);
1919
- }
1920
- }
1917
+ // src/ActionDefinition/Domain/ActionRootDomain.ts
1918
+ class ActionRootDomain extends ActionDomainBase {
1919
+ domainDefinition;
1920
+ _actionRuntimeManager;
1921
+ constructor(domainDefinition) {
1922
+ const domainId = domainDefinition.domain;
1923
+ super({
1924
+ domain: domainId,
1925
+ allDomains: [domainId],
1926
+ actionSchema: {}
1927
+ });
1928
+ this.domainDefinition = domainDefinition;
1929
+ this._actionRuntimeManager = new ActionRuntimeManager({ domain: domainId });
1930
+ }
1931
+ createChildDomain(subDomainDef) {
1932
+ if (this.allDomains.includes(subDomainDef.domain)) {
1933
+ throw err_nice_action.fromId("domain_already_exists_in_hierarchy" /* domain_already_exists_in_hierarchy */, {
1934
+ domain: subDomainDef.domain,
1935
+ allParentDomains: this.allDomains,
1936
+ parentDomain: this.domain
1937
+ });
1938
+ }
1939
+ return new ActionDomain({
1940
+ allDomains: [...this.allDomains, subDomainDef.domain],
1941
+ domain: subDomainDef.domain,
1942
+ actionSchema: subDomainDef.actions
1943
+ }, { rootDomain: this });
1944
+ }
1945
+ _registerRuntime(runtime2) {
1946
+ this._actionRuntimeManager.registerRuntime(runtime2);
1947
+ }
1948
+ _hasRuntime(runtime2) {
1949
+ return this._actionRuntimeManager.hasRuntime(runtime2);
1950
+ }
1951
+ getRuntime(clientSpecifier) {
1952
+ return this._actionRuntimeManager.getBestRuntimeForSpecifier(clientSpecifier);
1953
+ }
1954
+ async _runAction(actionPayload, options) {
1955
+ const allListeners = [...this._listeners, ...options?.listeners ?? []];
1956
+ let handlerAndRuntime;
1957
+ try {
1958
+ handlerAndRuntime = this._actionRuntimeManager.getRuntimeAndHandlerForActionOrThrow(actionPayload, options);
1959
+ } catch (err3) {
1960
+ const runningAction2 = new RunningAction({
1961
+ context: actionPayload.context,
1962
+ request: actionPayload,
1963
+ callSite: actionPayload._callSite
1921
1964
  });
1922
- connection.definition = transport;
1923
- this.transportManager.addTransport(connection);
1965
+ runningAction2.addUpdateListeners(allListeners);
1966
+ runningAction2._failWithError(err3);
1967
+ throw err3;
1924
1968
  }
1969
+ const { handler, runtime: runtime2 } = handlerAndRuntime;
1970
+ actionPayload.context._setOriginClient(runtime2.coordinate);
1971
+ const runningAction = await handler.handleActionRequest(actionPayload, {
1972
+ targetLocalRuntime: runtime2
1973
+ });
1974
+ runningAction.addUpdateListeners(allListeners);
1975
+ return runningAction;
1925
1976
  }
1926
- forDomain(domain) {
1927
- this.actionRouter.forDomain(domain, true);
1928
- return this;
1977
+ }
1978
+ // src/ActionDefinition/Domain/helpers/createRootActionDomain.ts
1979
+ var createActionRootDomain = (definition) => {
1980
+ return new ActionRootDomain(definition);
1981
+ };
1982
+ // src/ActionDefinition/Schema/ActionSchema.ts
1983
+ import { extractMessageFromStandardSchema } from "@nice-code/common-errors";
1984
+ class ActionSchema {
1985
+ _errorDeclarations = [];
1986
+ inputOptions;
1987
+ outputOptions;
1988
+ get inputSchema() {
1989
+ return this.inputOptions?.schema;
1929
1990
  }
1930
- forAction(action) {
1931
- this.actionRouter.forAction(action, true);
1991
+ get outputSchema() {
1992
+ return this.outputOptions?.schema;
1993
+ }
1994
+ input(options) {
1995
+ this.inputOptions = options;
1932
1996
  return this;
1933
1997
  }
1934
- forActionIds(domain, ids) {
1935
- this.actionRouter.forActionIds(domain, ids, true);
1998
+ output(options) {
1999
+ this.outputOptions = options;
1936
2000
  return this;
1937
2001
  }
1938
- _setIncomingActionDataListener(listener) {
1939
- this._incomingActionDataListeners.push(listener);
2002
+ throws(domain, ids) {
2003
+ this._errorDeclarations.push({ _domain: domain, _ids: ids });
2004
+ return this;
1940
2005
  }
1941
- async handleActionRequest(action, config) {
1942
- const localRuntime = config?.targetLocalRuntime ?? ActionRuntime.getDefault();
1943
- const localClient = localRuntime.coordinate;
1944
- const incomingTimeout = config?.timeout ?? this._defaultTimeout;
1945
- const parentCuid = peekHandlerCuid();
1946
- const callSite = action._callSite ?? new Error().stack;
1947
- const routeParams = {
1948
- action,
1949
- localClient,
1950
- externalClient: this.externalClient
1951
- };
1952
- const preferredTransport = this.transportManager.getPreferredTransport();
1953
- const routeItem = preferredTransport != null ? {
1954
- runtime: localClient,
1955
- handler: this.toHandlerRouteItem(preferredTransport, routeParams),
1956
- time: Date.now()
1957
- } : undefined;
1958
- if (routeItem != null)
1959
- action.context.addRouteItem(routeItem);
1960
- const runningAction = new RunningAction({
1961
- context: action.context,
1962
- request: action,
1963
- parentCuid,
1964
- callSite
1965
- });
1966
- localRuntime.registerRunningAction(runningAction);
1967
- this._dispatchWhenTransportReady(runningAction, routeParams, routeItem, incomingTimeout);
1968
- return runningAction;
2006
+ serializeInput(rawInput) {
2007
+ if (this.inputOptions?.serialization) {
2008
+ return this.inputOptions.serialization.serialize(rawInput);
2009
+ }
2010
+ return rawInput;
1969
2011
  }
1970
- async _dispatchWhenTransportReady(runningAction, routeParams, routeItem, incomingTimeout) {
1971
- const action = routeParams.action;
1972
- try {
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);
1995
- } catch (err3) {
1996
- runningAction._abort(err3);
2012
+ deserializeInput(serialized) {
2013
+ if (this.inputOptions?.serialization) {
2014
+ return this.inputOptions.serialization.deserialize(serialized);
1997
2015
  }
2016
+ return serialized;
1998
2017
  }
1999
- async sendReturnPayload(payload, config) {
2000
- const localClient = config.targetLocalRuntime.coordinate;
2001
- try {
2002
- const { methods } = await this.transportManager.getReadyTransport({
2003
- action: payload,
2004
- localClient,
2005
- externalClient: this.externalClient
2018
+ validateInput(value, meta) {
2019
+ if (this.inputOptions?.schema == null) {
2020
+ return value;
2021
+ }
2022
+ const result = this.inputOptions.schema["~standard"].validate(value);
2023
+ if (result instanceof Promise) {
2024
+ throw err_nice_action.fromId("action_input_validation_promise" /* action_input_validation_promise */, {
2025
+ domain: meta.domain,
2026
+ actionId: meta.actionId
2027
+ });
2028
+ }
2029
+ if (result.issues != null) {
2030
+ throw err_nice_action.fromId("action_input_validation_failed" /* action_input_validation_failed */, {
2031
+ domain: meta.domain,
2032
+ actionId: meta.actionId,
2033
+ validationMessage: extractMessageFromStandardSchema(result)
2006
2034
  });
2007
- if (methods.sendReturnData == null)
2008
- return false;
2009
- methods.sendReturnData(payload, { localClient, externalClient: this.externalClient });
2010
- return true;
2011
- } catch {
2012
- return false;
2013
2035
  }
2036
+ return result.value;
2014
2037
  }
2015
- toJsonObject() {
2016
- return {
2017
- type: this.handlerType,
2018
- client: this.externalClient
2019
- };
2038
+ validateOutput(value, meta) {
2039
+ if (this.outputOptions?.schema == null) {
2040
+ return value;
2041
+ }
2042
+ const result = this.outputOptions.schema["~standard"].validate(value);
2043
+ if (result instanceof Promise) {
2044
+ throw err_nice_action.fromId("action_output_validation_promise" /* action_output_validation_promise */, {
2045
+ domain: meta.domain,
2046
+ actionId: meta.actionId
2047
+ });
2048
+ }
2049
+ if (result.issues != null) {
2050
+ throw err_nice_action.fromId("action_output_validation_failed" /* action_output_validation_failed */, {
2051
+ domain: meta.domain,
2052
+ actionId: meta.actionId,
2053
+ validationMessage: extractMessageFromStandardSchema(result)
2054
+ });
2055
+ }
2056
+ return result.value;
2020
2057
  }
2021
- toHandlerRouteItem(transport, input) {
2022
- return {
2023
- type: this.handlerType,
2024
- client: this.externalClient,
2025
- transOrd: transport.transOrd,
2026
- transType: transport.type,
2027
- transInfo: transport.getRouteInfo(input)
2028
- };
2058
+ serializeOutput(rawOutput) {
2059
+ if (this.outputOptions?.serialization) {
2060
+ return this.outputOptions.serialization.serialize(rawOutput);
2061
+ }
2062
+ return rawOutput;
2029
2063
  }
2030
- clearTransportCache() {
2031
- this._transportCache.clear();
2064
+ deserializeOutput(serialized) {
2065
+ if (this.outputOptions?.serialization) {
2066
+ return this.outputOptions.serialization.deserialize(serialized);
2067
+ }
2068
+ return serialized;
2032
2069
  }
2033
2070
  }
2034
- var createExternalClientHandler = (config) => {
2035
- return new ActionExternalClientHandler(config);
2071
+ var actionSchema = () => {
2072
+ return new ActionSchema;
2036
2073
  };
2037
2074
  // src/ActionRuntime/Handler/ExternalClient/Transport/Transport.ts
2038
2075
  class Transport {
@@ -3009,8 +3046,8 @@ var HANDSHAKE_TIMEOUT_MS = 15000;
3009
3046
 
3010
3047
  class WebSocketConnection extends TransportConnection {
3011
3048
  resolvers;
3012
- _abortSet = new Set;
3013
3049
  _liveSocketUrl;
3050
+ _intentionalCloses = new WeakSet;
3014
3051
  constructor(def, resolvers) {
3015
3052
  super({ ...def, type: "ws" /* ws */ });
3016
3053
  this.resolvers = resolvers ?? createUnsetTransportResolvers("ws" /* ws */);
@@ -3092,20 +3129,22 @@ class WebSocketConnection extends TransportConnection {
3092
3129
  _finalizeTransportMethods(wsData) {
3093
3130
  const ws = wsData.ws;
3094
3131
  const disconnectListeners = [];
3132
+ const abortSet = new Set;
3095
3133
  this._captureSocketUrl(ws);
3096
- this._attachLifecycle(ws, disconnectListeners);
3134
+ this._attachLifecycle(ws, disconnectListeners, abortSet);
3097
3135
  ws.addEventListener("message", async (event) => {
3098
3136
  const frame = await this._normalizeFrame(event.data);
3099
3137
  if (frame !== undefined)
3100
3138
  await this._handleIncomingActionFrame(frame, wsData, undefined);
3101
3139
  });
3102
- return this._buildSendMethods(ws, wsData, undefined, disconnectListeners);
3140
+ return this._buildSendMethods(ws, wsData, undefined, disconnectListeners, abortSet);
3103
3141
  }
3104
3142
  async _finalizeSecureMethods(wsData) {
3105
3143
  const ws = wsData.ws;
3106
3144
  const disconnectListeners = [];
3145
+ const abortSet = new Set;
3107
3146
  this._captureSocketUrl(ws);
3108
- this._attachLifecycle(ws, disconnectListeners);
3147
+ this._attachLifecycle(ws, disconnectListeners, abortSet);
3109
3148
  let active = false;
3110
3149
  let crypto;
3111
3150
  const handshakeQueue = [];
@@ -3149,7 +3188,7 @@ class WebSocketConnection extends TransportConnection {
3149
3188
  for (const frame of pendingActionFrames)
3150
3189
  await this._handleIncomingActionFrame(frame, wsData, crypto);
3151
3190
  pendingActionFrames.length = 0;
3152
- return this._buildSendMethods(ws, wsData, crypto, disconnectListeners);
3191
+ return this._buildSendMethods(ws, wsData, crypto, disconnectListeners, abortSet);
3153
3192
  }
3154
3193
  async _runClientHandshake(ws, secure, nextHandshakeMessage) {
3155
3194
  await secure.link.initialize();
@@ -3178,20 +3217,31 @@ class WebSocketConnection extends TransportConnection {
3178
3217
  const result = await handshake.onAccept(accept);
3179
3218
  return result.securityLevel === "encrypted" /* encrypted */ ? createActionFrameCrypto({ link: secure.link, linkedClientId: result.linkedClientId }) : undefined;
3180
3219
  }
3181
- _buildSendMethods(ws, wsData, crypto, disconnectListeners) {
3220
+ _buildSendMethods(ws, wsData, crypto, disconnectListeners, abortSet) {
3182
3221
  let sendChain = Promise.resolve();
3183
3222
  const enqueueSend = (frame) => {
3223
+ if (ws.readyState !== WebSocket.OPEN)
3224
+ return;
3184
3225
  if (crypto == null) {
3185
3226
  sendFrame(ws, frame);
3186
3227
  return;
3187
3228
  }
3188
3229
  const bytes = toFrameBytes(frame);
3189
- sendChain = sendChain.then(() => crypto.encryptFrame(bytes)).then((encrypted) => sendFrame(ws, encrypted)).catch((err4) => console.error("[ws] failed to encrypt/send frame", err4));
3230
+ sendChain = sendChain.then(() => crypto.encryptFrame(bytes)).then((encrypted) => {
3231
+ if (ws.readyState === WebSocket.OPEN)
3232
+ sendFrame(ws, encrypted);
3233
+ }).catch((err4) => console.error("[ws] failed to encrypt/send frame", err4));
3190
3234
  };
3191
3235
  const sendActionData = (inputs) => {
3192
3236
  const { action, runningAction, timeout } = inputs;
3237
+ if (ws.readyState !== WebSocket.OPEN) {
3238
+ if (action.type === "request" /* request */) {
3239
+ runningAction._abort(err_nice_transport_ws.fromId("ws_disconnected" /* ws_disconnected */));
3240
+ }
3241
+ return;
3242
+ }
3193
3243
  if (action.type === "request" /* request */) {
3194
- this._abortSet.add(runningAction);
3244
+ abortSet.add(runningAction);
3195
3245
  const timeoutId = setTimeout(() => {
3196
3246
  runningAction._abort(err_nice_transport.fromId("timeout" /* timeout */, { timeout }));
3197
3247
  }, timeout);
@@ -3199,7 +3249,7 @@ class WebSocketConnection extends TransportConnection {
3199
3249
  (update) => {
3200
3250
  if (update.type === "finished" /* finished */) {
3201
3251
  clearTimeout(timeoutId);
3202
- this._abortSet.delete(runningAction);
3252
+ abortSet.delete(runningAction);
3203
3253
  }
3204
3254
  }
3205
3255
  ]);
@@ -3212,6 +3262,12 @@ class WebSocketConnection extends TransportConnection {
3212
3262
  addOnDisconnectListener: (cb) => {
3213
3263
  disconnectListeners.push(cb);
3214
3264
  },
3265
+ disconnect: () => {
3266
+ this._intentionalCloses.add(ws);
3267
+ try {
3268
+ ws.close();
3269
+ } catch {}
3270
+ },
3215
3271
  sendReturnData: (payload, clients) => {
3216
3272
  const formatted = clients != null ? wsData.formatMessage?.outgoing({ action: payload, ...clients }) : undefined;
3217
3273
  enqueueSend(formatted ?? JSON.stringify(payload.toJsonObject()));
@@ -3246,24 +3302,25 @@ class WebSocketConnection extends TransportConnection {
3246
3302
  if (ws.url != null && ws.url !== "")
3247
3303
  this._liveSocketUrl = ws.url;
3248
3304
  }
3249
- _attachLifecycle(ws, disconnectListeners) {
3305
+ _attachLifecycle(ws, disconnectListeners, abortSet) {
3250
3306
  ws.addEventListener("close", (event) => {
3251
- console.error("WebSocket closed:", event);
3307
+ if (!this._intentionalCloses.has(ws))
3308
+ console.error("WebSocket closed:", event);
3252
3309
  for (const cb of disconnectListeners)
3253
3310
  cb();
3254
- this._abortAll(err_nice_transport_ws.fromId("ws_disconnected" /* ws_disconnected */));
3311
+ this._abortAll(abortSet, err_nice_transport_ws.fromId("ws_disconnected" /* ws_disconnected */));
3255
3312
  });
3256
3313
  ws.addEventListener("error", (event) => {
3257
3314
  console.error("WebSocket error:", event);
3258
3315
  for (const cb of disconnectListeners)
3259
3316
  cb();
3260
- this._abortAll(err_nice_transport_ws.fromId("ws_error" /* ws_error */, {
3317
+ this._abortAll(abortSet, err_nice_transport_ws.fromId("ws_error" /* ws_error */, {
3261
3318
  originalError: event instanceof Error ? event : undefined
3262
3319
  }));
3263
3320
  });
3264
3321
  }
3265
- _abortAll(error) {
3266
- const snapshot = [...this._abortSet];
3322
+ _abortAll(abortSet, error) {
3323
+ const snapshot = [...abortSet];
3267
3324
  for (const ra of snapshot) {
3268
3325
  ra._abort(error);
3269
3326
  }
@@ -3354,11 +3411,71 @@ function createSecureWebSocketTransport(options) {
3354
3411
  }
3355
3412
  });
3356
3413
  }
3414
+ // src/ActionRuntime/Handler/Server/WsConnectionStateStore.ts
3415
+ class WsConnectionStateStore {
3416
+ options;
3417
+ constructor(options) {
3418
+ this.options = options;
3419
+ }
3420
+ get(connection) {
3421
+ return this._readAttachment(connection).app ?? null;
3422
+ }
3423
+ set(connection, app) {
3424
+ const existing = this._readAttachment(connection);
3425
+ this.options.write(connection, { app, binding: existing.binding });
3426
+ }
3427
+ clearApp(connection) {
3428
+ const existing = this._readAttachment(connection);
3429
+ this.options.write(connection, { binding: existing.binding });
3430
+ }
3431
+ entries() {
3432
+ return this.options.getConnections().map((connection) => [connection, this._readAttachment(connection).app ?? null]);
3433
+ }
3434
+ _persistBinding(connection, binding) {
3435
+ const existing = this._readAttachment(connection);
3436
+ this.options.write(connection, { app: existing.app, binding });
3437
+ }
3438
+ _readBinding(connection) {
3439
+ return this._readAttachment(connection).binding;
3440
+ }
3441
+ _readAttachment(connection) {
3442
+ try {
3443
+ const raw = this.options.read(connection);
3444
+ if (typeof raw !== "object" || raw === null)
3445
+ return {};
3446
+ const attachment = raw;
3447
+ const result = {};
3448
+ if (attachment.binding != null)
3449
+ result.binding = attachment.binding;
3450
+ if (attachment.app !== undefined) {
3451
+ const app = this._validateApp(attachment.app);
3452
+ if (app !== undefined)
3453
+ result.app = app;
3454
+ }
3455
+ return result;
3456
+ } catch {
3457
+ return {};
3458
+ }
3459
+ }
3460
+ _validateApp(value) {
3461
+ const schema = this.options.schema;
3462
+ if (schema == null)
3463
+ return value;
3464
+ const result = schema["~standard"].validate(value);
3465
+ if (result instanceof Promise)
3466
+ return;
3467
+ if (result.issues != null)
3468
+ return;
3469
+ return result.value;
3470
+ }
3471
+ }
3472
+
3357
3473
  // src/ActionRuntime/Handler/Server/ActionServerHandler.ts
3358
3474
  class ActionServerHandler extends ActionExternalClientHandler {
3359
3475
  _formatMessage;
3360
3476
  _createFormatMessage;
3361
3477
  _send;
3478
+ _runtime;
3362
3479
  _serverTimeout;
3363
3480
  _onConnectionBound;
3364
3481
  _incomingListeners = [];
@@ -3381,6 +3498,7 @@ class ActionServerHandler extends ActionExternalClientHandler {
3381
3498
  this._formatMessage = options.formatMessage;
3382
3499
  this._createFormatMessage = options.createFormatMessage;
3383
3500
  this._send = options.send;
3501
+ this._runtime = options.runtime;
3384
3502
  this._serverTimeout = options.defaultTimeout ?? DEFAULT_TRANSPORT_TIMEOUT;
3385
3503
  this._onConnectionBound = options.onConnectionBound;
3386
3504
  this._security = options.security;
@@ -3409,6 +3527,16 @@ class ActionServerHandler extends ActionExternalClientHandler {
3409
3527
  setOnConnectionBound(onConnectionBound) {
3410
3528
  this._onConnectionBound = onConnectionBound;
3411
3529
  }
3530
+ createConnectionState(options) {
3531
+ const store = new WsConnectionStateStore(options);
3532
+ this.setOnConnectionBound((connection, binding) => store._persistBinding(connection, binding));
3533
+ for (const connection of options.getConnections()) {
3534
+ const binding = store._readBinding(connection);
3535
+ if (binding != null)
3536
+ this.rehydrateConnection(connection, binding);
3537
+ }
3538
+ return store;
3539
+ }
3412
3540
  receive(connection, frame) {
3413
3541
  if (this._security == null || !this._handshakeMode) {
3414
3542
  this._receivePlain(connection, frame);
@@ -3583,6 +3711,41 @@ class ActionServerHandler extends ActionExternalClientHandler {
3583
3711
  const connection = this._resolveConnection(target);
3584
3712
  return this._dispatch(runtime2, connection, request, options?.timeout);
3585
3713
  }
3714
+ forConnectionDomainCases(domain, cases) {
3715
+ const handler = new ActionLocalHandler;
3716
+ const executor = cases;
3717
+ const wrapped = {};
3718
+ const wrappedRecord = wrapped;
3719
+ for (const id in cases) {
3720
+ const caseFn = executor[id];
3721
+ if (caseFn == null)
3722
+ continue;
3723
+ wrappedRecord[id] = (request) => caseFn(request, this.getConnectionForClient(request.context.originClient));
3724
+ }
3725
+ return handler.forDomainActionCases(domain, wrapped);
3726
+ }
3727
+ broadcast(makeRequest, options) {
3728
+ const runtime2 = options?.runtime ?? this._runtime;
3729
+ if (runtime2 == null) {
3730
+ throw err_nice_transport.fromId("not_found" /* not_found */, {
3731
+ actionId: "server-handler-runtime (construct with `runtime` or pass `options.runtime`)"
3732
+ });
3733
+ }
3734
+ for (const connection of this._clientByConn.keys()) {
3735
+ if (options?.except != null && connection === options.except)
3736
+ continue;
3737
+ if (options?.where != null && !options.where(connection))
3738
+ continue;
3739
+ try {
3740
+ this.pushToClient(runtime2, connection, makeRequest(), { timeout: options?.timeout });
3741
+ } catch (error) {
3742
+ if (options?.onError != null)
3743
+ options.onError(error, connection);
3744
+ else
3745
+ console.error("[ws-server] broadcast push failed", error);
3746
+ }
3747
+ }
3748
+ }
3586
3749
  async sendReturnPayload(payload, config) {
3587
3750
  const connection = this._connByClient.get(payload.context.originClient.stringId);
3588
3751
  if (connection == null)
@@ -3671,6 +3834,41 @@ class ActionServerHandler extends ActionExternalClientHandler {
3671
3834
  var createServerHandler = (options) => {
3672
3835
  return new ActionServerHandler(options);
3673
3836
  };
3837
+ // src/ActionRuntime/Handler/Server/createActionFetchHandler.ts
3838
+ var DEFAULT_CORS_HEADERS = {
3839
+ "Access-Control-Allow-Origin": "*",
3840
+ "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
3841
+ "Access-Control-Allow-Headers": "Content-Type",
3842
+ "Access-Control-Max-Age": "86400"
3843
+ };
3844
+ function createActionFetchHandler(runtime2, options = {}) {
3845
+ const corsHeaders = options.cors === false ? {} : options.cors ?? DEFAULT_CORS_HEADERS;
3846
+ const isActionPath = options.isActionPath ?? ((url) => url.pathname.endsWith("/action"));
3847
+ const isWebSocketPath = options.isWebSocketPath ?? ((url) => url.pathname.endsWith("/ws"));
3848
+ const withCors = (response) => {
3849
+ if (options.cors === false)
3850
+ return response;
3851
+ const headers = new Headers(response.headers);
3852
+ for (const [key, value] of Object.entries(corsHeaders))
3853
+ headers.set(key, value);
3854
+ return new Response(response.body, { status: response.status, headers });
3855
+ };
3856
+ return async (request) => {
3857
+ if (request.method === "OPTIONS") {
3858
+ return withCors(new Response(null, { status: 204 }));
3859
+ }
3860
+ const url = new URL(request.url);
3861
+ if (options.onWebSocketUpgrade != null && request.headers.get("Upgrade") === "websocket" && isWebSocketPath(url)) {
3862
+ return options.onWebSocketUpgrade(request, url);
3863
+ }
3864
+ if (request.method === "POST" && isActionPath(url)) {
3865
+ const running = await runtime2.handleActionPayloadWire(await request.json());
3866
+ const result = await running.waitForResultPayload();
3867
+ return withCors(result.toHttpResponse({ useErrorStatus: options.useErrorStatus }));
3868
+ }
3869
+ return withCors(new Response("Not found", { status: 404 }));
3870
+ };
3871
+ }
3674
3872
  // src/ActionRuntime/Handler/Server/createSecureActionServer.ts
3675
3873
  import { ClientCryptoKeyLink as ClientCryptoKeyLink2 } from "@nice-code/util";
3676
3874
  var DEFAULT_SERVER_SECURITY_LEVELS = [
@@ -3684,6 +3882,7 @@ function createSecureActionServerHandler(options) {
3684
3882
  clientEnv: options.clientEnv,
3685
3883
  createFormatMessage: options.channel.createCodec,
3686
3884
  send: options.send,
3885
+ runtime: options.runtime,
3687
3886
  defaultTimeout: options.defaultTimeout,
3688
3887
  security: {
3689
3888
  securityLevel: options.securityLevel ?? DEFAULT_SERVER_SECURITY_LEVELS,
@@ -3734,7 +3933,9 @@ export {
3734
3933
  createBinaryWsAdapter,
3735
3934
  createActionRootDomain,
3736
3935
  createActionFrameCrypto,
3936
+ createActionFetchHandler,
3737
3937
  actionSchema,
3938
+ WsConnectionStateStore,
3738
3939
  WebSocketTransport,
3739
3940
  Transport,
3740
3941
  RuntimeCoordinate,