@nice-code/action 0.2.11 → 0.2.12

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.
@@ -0,0 +1,232 @@
1
+ // src/ActionDefinition/Action/RunningAction.types.ts
2
+ var ERunningActionState;
3
+ ((ERunningActionState2) => {
4
+ ERunningActionState2["running"] = "running";
5
+ ERunningActionState2["completed"] = "completed";
6
+ })(ERunningActionState ||= {});
7
+ var ERunningActionUpdateType;
8
+ ((ERunningActionUpdateType2) => {
9
+ ERunningActionUpdateType2["started"] = "started";
10
+ ERunningActionUpdateType2["progress"] = "progress";
11
+ ERunningActionUpdateType2["finished"] = "finished";
12
+ })(ERunningActionUpdateType ||= {});
13
+ var ERunningActionFinishedType;
14
+ ((ERunningActionFinishedType2) => {
15
+ ERunningActionFinishedType2["aborted"] = "aborted";
16
+ ERunningActionFinishedType2["failed"] = "failed";
17
+ ERunningActionFinishedType2["success"] = "success";
18
+ })(ERunningActionFinishedType ||= {});
19
+
20
+ // src/devtools/server/NiceActionServerDevtools.ts
21
+ class ActionServerDevtools {
22
+ _options;
23
+ _inFlight = new Map;
24
+ constructor(options = {}) {
25
+ const defaultEnabled = typeof process !== "undefined" ? true : true;
26
+ this._options = {
27
+ logger: options.logger ?? defaultConsoleLogger,
28
+ format: options.format ?? "pretty",
29
+ logPayloads: options.logPayloads ?? true,
30
+ enabled: options.enabled ?? defaultEnabled
31
+ };
32
+ }
33
+ attachToDomain(domain) {
34
+ if (!this._options.enabled) {
35
+ return () => {};
36
+ }
37
+ return domain.addActionListener((update) => {
38
+ const { runningAction, type, time } = update;
39
+ const actionPath = [...runningAction.allDomains, runningAction.id].join(".");
40
+ if (type === "started" /* started */) {
41
+ this._inFlight.set(runningAction.cuid, { startTime: time });
42
+ this._log("started", actionPath, runningAction.cuid, {
43
+ ...this._options.logPayloads ? { input: runningAction.state?.request?.input } : {}
44
+ });
45
+ } else if (type === "progress" /* progress */) {
46
+ this._log("progress", actionPath, runningAction.cuid, { progress: update.progress });
47
+ } else if (type === "finished" /* finished */) {
48
+ const timing = this._inFlight.get(runningAction.cuid);
49
+ const duration = timing != null ? time - timing.startTime : undefined;
50
+ this._inFlight.delete(runningAction.cuid);
51
+ const finishType = update.finishType;
52
+ if (finishType === "success" /* success */) {
53
+ this._log("success", actionPath, runningAction.cuid, {
54
+ ...duration != null ? { duration: `${duration}ms` } : {},
55
+ ...this._options.logPayloads ? { output: update.response?.result?.output } : {}
56
+ });
57
+ } else if (finishType === "failed" /* failed */) {
58
+ this._log("failed", actionPath, runningAction.cuid, {
59
+ ...duration != null ? { duration: `${duration}ms` } : {},
60
+ error: serializeError(update.error)
61
+ });
62
+ } else {
63
+ this._log("aborted", actionPath, runningAction.cuid, {
64
+ ...duration != null ? { duration: `${duration}ms` } : {},
65
+ ...update.reason != null ? { reason: String(update.reason) } : {}
66
+ });
67
+ }
68
+ }
69
+ });
70
+ }
71
+ _log(event, actionPath, cuid, data) {
72
+ const { logger, format } = this._options;
73
+ if (format === "json") {
74
+ logger(JSON.stringify({ time: new Date().toISOString(), event, action: actionPath, cuid, ...data }));
75
+ return;
76
+ }
77
+ const prefix = PRETTY_PREFIX[event] ?? `[${event}]`;
78
+ const suffix = Object.keys(data).length > 0 ? ` ${formatPrettyData(data)}` : "";
79
+ logger(`${prefix} ${actionPath} cuid=${cuid}${suffix}`);
80
+ }
81
+ }
82
+ var PRETTY_PREFIX = {
83
+ started: "[nice-action] ►",
84
+ progress: "[nice-action] ",
85
+ success: "[nice-action] ✓",
86
+ failed: "[nice-action] ✗",
87
+ aborted: "[nice-action] ○"
88
+ };
89
+ function formatPrettyData(data) {
90
+ return Object.entries(data).map(([k, v]) => `${k}=${safeStringify(v)}`).join(" ");
91
+ }
92
+ function safeStringify(value) {
93
+ if (value === undefined)
94
+ return "undefined";
95
+ if (value === null)
96
+ return "null";
97
+ if (typeof value === "string")
98
+ return `"${value}"`;
99
+ try {
100
+ return JSON.stringify(value);
101
+ } catch {
102
+ return String(value);
103
+ }
104
+ }
105
+ function serializeError(err) {
106
+ if (err == null)
107
+ return err;
108
+ if (err instanceof Error)
109
+ return { message: err.message, name: err.name, stack: err.stack };
110
+ if (typeof err === "object") {
111
+ try {
112
+ return JSON.parse(JSON.stringify(err));
113
+ } catch {
114
+ return String(err);
115
+ }
116
+ }
117
+ return err;
118
+ }
119
+ function defaultConsoleLogger(message) {
120
+ console.log(message);
121
+ }
122
+ // src/devtools/core/ActionDevtoolsCore.ts
123
+ function extractRouting(context) {
124
+ return (context?.routing ?? []).map((item) => {
125
+ const handler = item.handler;
126
+ const isExternal = handler?.type === "external";
127
+ return {
128
+ runtime: {
129
+ envId: item.runtime?.envId ?? "unknown",
130
+ perId: item.runtime?.perId,
131
+ insId: item.runtime?.insId
132
+ },
133
+ handlerType: isExternal ? "external" : "local",
134
+ handlerClient: isExternal && handler.client != null ? {
135
+ envId: handler.client.envId ?? "unknown",
136
+ perId: handler.client.perId,
137
+ insId: handler.client.insId
138
+ } : undefined,
139
+ transport: isExternal ? handler.transType : undefined,
140
+ time: item.time
141
+ };
142
+ });
143
+ }
144
+ function extractMeta(context) {
145
+ return {
146
+ timeCreated: context?.timeCreated ?? Date.now(),
147
+ originClient: {
148
+ envId: context?.originClient?.envId ?? "unknown",
149
+ perId: context?.originClient?.perId,
150
+ insId: context?.originClient?.insId
151
+ },
152
+ routing: extractRouting(context)
153
+ };
154
+ }
155
+
156
+ class ActionDevtoolsCore {
157
+ _entries = [];
158
+ _listeners = new Set;
159
+ _maxEntries;
160
+ constructor(options = {}) {
161
+ this._maxEntries = options.maxEntries ?? 100;
162
+ }
163
+ attachToDomain(domain) {
164
+ return domain.addActionListener((update) => {
165
+ const { runningAction, type, time } = update;
166
+ if (type === "started" /* started */) {
167
+ const entry = {
168
+ cuid: runningAction.cuid,
169
+ actionId: runningAction.id,
170
+ domain: runningAction.domain,
171
+ allDomains: [...runningAction.allDomains],
172
+ status: "running",
173
+ startTime: time,
174
+ input: runningAction.state?.request?.input,
175
+ progressUpdates: [],
176
+ meta: extractMeta(runningAction.context)
177
+ };
178
+ this._entries = [entry, ...this._entries].slice(0, this._maxEntries);
179
+ this._notify();
180
+ } else if (type === "progress" /* progress */) {
181
+ this._updateEntry(runningAction.cuid, (e) => ({
182
+ ...e,
183
+ progressUpdates: [...e.progressUpdates, update.progress]
184
+ }));
185
+ } else if (type === "finished" /* finished */) {
186
+ this._updateEntry(runningAction.cuid, (e) => {
187
+ const finishedRoutingContext = update.response?.context ?? runningAction.context;
188
+ const base = {
189
+ ...e,
190
+ endTime: time,
191
+ meta: { ...e.meta, routing: extractRouting(finishedRoutingContext) }
192
+ };
193
+ const finishType = update.finishType;
194
+ if (finishType === "success" /* success */) {
195
+ return { ...base, status: "success", output: update.response?.result?.output };
196
+ }
197
+ if (finishType === "failed" /* failed */) {
198
+ return { ...base, status: "failed", error: update.error };
199
+ }
200
+ return { ...base, status: "aborted", abortReason: update.reason };
201
+ });
202
+ }
203
+ });
204
+ }
205
+ getEntries() {
206
+ return this._entries;
207
+ }
208
+ subscribe(listener) {
209
+ this._listeners.add(listener);
210
+ listener(this._entries);
211
+ return () => {
212
+ this._listeners.delete(listener);
213
+ };
214
+ }
215
+ clear() {
216
+ this._entries = [];
217
+ this._notify();
218
+ }
219
+ _updateEntry(cuid, updater) {
220
+ this._entries = this._entries.map((e) => e.cuid === cuid ? updater(e) : e);
221
+ this._notify();
222
+ }
223
+ _notify() {
224
+ const snapshot = this._entries;
225
+ for (const listener of this._listeners)
226
+ listener(snapshot);
227
+ }
228
+ }
229
+ export {
230
+ ActionServerDevtools,
231
+ ActionDevtoolsCore
232
+ };
package/build/index.js CHANGED
@@ -37,19 +37,19 @@ class RuntimeCoordinate {
37
37
  this.perId = perId;
38
38
  this.insId = insId;
39
39
  }
40
- specify(newInputs) {
40
+ specify(specifics) {
41
41
  return new RuntimeCoordinate({
42
42
  envId: this.envId,
43
43
  perId: this.perId,
44
44
  insId: this.insId,
45
- ...newInputs
45
+ ...specifics
46
46
  });
47
47
  }
48
- specifyIfUnset(newInputs) {
48
+ specifyIfUnset(specifics) {
49
49
  return new RuntimeCoordinate({
50
50
  envId: this.envId,
51
- perId: this.perId ?? newInputs.perId,
52
- insId: this.insId ?? newInputs.insId
51
+ perId: this.perId ?? specifics.perId,
52
+ insId: this.insId ?? specifics.insId
53
53
  });
54
54
  }
55
55
  toJsonObject() {
@@ -864,13 +864,13 @@ class ActionRuntime {
864
864
  get coordinate() {
865
865
  return this._coordinate;
866
866
  }
867
- updateRuntimeCoordinate(newCoordinate) {
868
- if (this._coordinate.envId !== newCoordinate.envId) {
867
+ specifyRuntimeCoordinate(specifics) {
868
+ if (specifics.envId != null && this._coordinate.envId !== specifics.envId) {
869
869
  throw err_nice_action.fromId("not_implemented" /* not_implemented */, {
870
- label: `updating RuntimeCoordinate with a different "envId" ("${this._coordinate.envId}" → "${newCoordinate.envId}")`
870
+ label: `updating RuntimeCoordinate with a different "envId" ("${this._coordinate.envId}" → "${specifics.envId}")`
871
871
  });
872
872
  }
873
- this._coordinate = newCoordinate;
873
+ this._coordinate = this._coordinate.specify(specifics);
874
874
  this.apply();
875
875
  }
876
876
  registerRunningAction(ra) {
@@ -5,7 +5,7 @@ import type { IActionDomain } from "../ActionDefinition/Domain/ActionDomain.type
5
5
  import type { IRuntimeMeta, TActionRuntimeHandler } from "./ActionRuntime.types";
6
6
  import { type IHandleActionOptions, type TActionHandler } from "./Handler/ActionHandler.types";
7
7
  import type { ActionExternalClientHandler } from "./Handler/ExternalClient/ActionExternalClientHandler";
8
- import { RuntimeCoordinate } from "./RuntimeCoordinate";
8
+ import { type IRuntimeCoordinateSpecifics, RuntimeCoordinate } from "./RuntimeCoordinate";
9
9
  export declare class ActionRuntime {
10
10
  private _coordinate;
11
11
  readonly timeCreated: number;
@@ -17,7 +17,9 @@ export declare class ActionRuntime {
17
17
  static getDefault(): ActionRuntime;
18
18
  constructor(coordinate: RuntimeCoordinate);
19
19
  get coordinate(): RuntimeCoordinate;
20
- updateRuntimeCoordinate(newCoordinate: RuntimeCoordinate): void;
20
+ specifyRuntimeCoordinate(specifics: IRuntimeCoordinateSpecifics & {
21
+ envId?: string;
22
+ }): void;
21
23
  registerRunningAction(ra: RunningAction<any, any>): void;
22
24
  resolveIncomingActionPayload(json: TActionPayload_Any_JsonObject<any, any>): void;
23
25
  /**
@@ -1,8 +1,4 @@
1
- export interface IRuntimeCoordinate {
2
- /**
3
- * A static runtime environment identifier (e.g. "web_app_v1", "backend_analytics")
4
- */
5
- envId: string;
1
+ export interface IRuntimeCoordinateSpecifics {
6
2
  /**
7
3
  * A unique and persistent client ID (should stay the same between runtime instance reloads)
8
4
  */
@@ -13,6 +9,12 @@ export interface IRuntimeCoordinate {
13
9
  */
14
10
  insId?: string;
15
11
  }
12
+ export interface IRuntimeCoordinate extends IRuntimeCoordinateSpecifics {
13
+ /**
14
+ * A static runtime environment identifier (e.g. "web_app_v1", "backend_analytics")
15
+ */
16
+ envId: string;
17
+ }
16
18
  export type TRuntimeCoordinateEnvId = `envId[${string}]`;
17
19
  export type TRuntimeCoordinateStringId = `${TRuntimeCoordinateEnvId}perId[${string | "_"}]:insId[${string | "_"}]`;
18
20
  export interface IRuntimeFullCoordinates extends Required<IRuntimeCoordinate> {
@@ -25,8 +27,8 @@ export declare class RuntimeCoordinate implements IRuntimeCoordinate {
25
27
  static env(envId: string): RuntimeCoordinate;
26
28
  withPersistentId(perId: string): RuntimeCoordinate;
27
29
  constructor({ envId, perId, insId }: IRuntimeCoordinate);
28
- specify(newInputs: Omit<IRuntimeCoordinate, "envId">): RuntimeCoordinate;
29
- specifyIfUnset(newInputs: Omit<IRuntimeCoordinate, "envId">): RuntimeCoordinate;
30
+ specify(specifics: IRuntimeCoordinateSpecifics): RuntimeCoordinate;
31
+ specifyIfUnset(specifics: IRuntimeCoordinateSpecifics): RuntimeCoordinate;
30
32
  toJsonObject(): IRuntimeCoordinate;
31
33
  isExactlySame(other: IRuntimeCoordinate): boolean;
32
34
  isSameFor(other: IRuntimeCoordinate): {
@@ -0,0 +1,8 @@
1
+ import type { TDevtoolsPosition } from "../core/ActionDevtools.types";
2
+ import type { ActionDevtoolsCore } from "../core/ActionDevtoolsCore";
3
+ export interface INiceActionDevtoolsProps {
4
+ core: ActionDevtoolsCore;
5
+ position?: TDevtoolsPosition;
6
+ initialOpen?: boolean;
7
+ }
8
+ export declare function NiceActionDevtools(props: INiceActionDevtoolsProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,3 @@
1
+ export type { IDevtoolsActionEntry, IDevtoolsObservableDomain, TDevtoolsActionStatus, TDevtoolsListener, } from "../core/ActionDevtools.types";
2
+ export { ActionDevtoolsCore, type IActionDevtoolsCoreOptions } from "../core/ActionDevtoolsCore";
3
+ export { type INiceActionDevtoolsProps, NiceActionDevtools } from "./NiceActionDevtools";
@@ -0,0 +1,47 @@
1
+ import type { TActionProgress } from "../../ActionDefinition/Action/Payload/ActionPayload.types";
2
+ export type TDevtoolsActionStatus = "running" | "success" | "failed" | "aborted";
3
+ export type TDevtoolsPosition = "bottom-right" | "bottom-left" | "top-right" | "top-left" | "dock-bottom" | "dock-top" | "dock-left" | "dock-right";
4
+ export interface IDevtoolsRouteItem {
5
+ runtime: {
6
+ envId: string;
7
+ perId?: string;
8
+ insId?: string;
9
+ };
10
+ handlerType: "local" | "external";
11
+ handlerClient?: {
12
+ envId: string;
13
+ perId?: string;
14
+ insId?: string;
15
+ };
16
+ transport?: string;
17
+ time: number;
18
+ }
19
+ export interface IDevtoolsActionMeta {
20
+ timeCreated: number;
21
+ originClient: {
22
+ envId: string;
23
+ perId?: string;
24
+ insId?: string;
25
+ };
26
+ routing: IDevtoolsRouteItem[];
27
+ }
28
+ export interface IDevtoolsActionEntry {
29
+ cuid: string;
30
+ actionId: string;
31
+ domain: string;
32
+ allDomains: string[];
33
+ status: TDevtoolsActionStatus;
34
+ startTime: number;
35
+ endTime?: number;
36
+ input: unknown;
37
+ output?: unknown;
38
+ error?: unknown;
39
+ abortReason?: unknown;
40
+ progressUpdates: TActionProgress[];
41
+ meta: IDevtoolsActionMeta;
42
+ }
43
+ export type TDevtoolsListener = (entries: readonly IDevtoolsActionEntry[]) => void;
44
+ export interface IDevtoolsObservableDomain {
45
+ domain: string;
46
+ addActionListener(listener: (update: any) => void): () => void;
47
+ }
@@ -0,0 +1,16 @@
1
+ import type { IDevtoolsActionEntry, IDevtoolsObservableDomain, TDevtoolsListener } from "./ActionDevtools.types";
2
+ export interface IActionDevtoolsCoreOptions {
3
+ maxEntries?: number;
4
+ }
5
+ export declare class ActionDevtoolsCore {
6
+ private _entries;
7
+ private _listeners;
8
+ private readonly _maxEntries;
9
+ constructor(options?: IActionDevtoolsCoreOptions);
10
+ attachToDomain(domain: IDevtoolsObservableDomain): () => void;
11
+ getEntries(): readonly IDevtoolsActionEntry[];
12
+ subscribe(listener: TDevtoolsListener): () => void;
13
+ clear(): void;
14
+ private _updateEntry;
15
+ private _notify;
16
+ }
@@ -0,0 +1,30 @@
1
+ import type { IDevtoolsObservableDomain } from "../core/ActionDevtools.types";
2
+ export type TServerDevtoolsLogFn = (message: string, data?: Record<string, unknown>) => void;
3
+ export interface IActionServerDevtoolsOptions {
4
+ /**
5
+ * Custom logger function. Defaults to console.log / console.error.
6
+ */
7
+ logger?: TServerDevtoolsLogFn;
8
+ /**
9
+ * Output format.
10
+ * - "pretty": human-readable lines (default)
11
+ * - "json": newline-delimited JSON for log aggregators
12
+ */
13
+ format?: "pretty" | "json";
14
+ /**
15
+ * Whether to log action input/output payloads.
16
+ * Disable if payloads may contain sensitive data. Defaults to true.
17
+ */
18
+ logPayloads?: boolean;
19
+ /**
20
+ * Whether devtools are active. Defaults to process.env.NODE_ENV !== "production".
21
+ */
22
+ enabled?: boolean;
23
+ }
24
+ export declare class ActionServerDevtools {
25
+ private readonly _options;
26
+ private readonly _inFlight;
27
+ constructor(options?: IActionServerDevtoolsOptions);
28
+ attachToDomain(domain: IDevtoolsObservableDomain): () => void;
29
+ private _log;
30
+ }
@@ -0,0 +1,3 @@
1
+ export { ActionServerDevtools, type IActionServerDevtoolsOptions, type TServerDevtoolsLogFn, } from "./NiceActionServerDevtools";
2
+ export { ActionDevtoolsCore, type IActionDevtoolsCoreOptions } from "../core/ActionDevtoolsCore";
3
+ export type { IDevtoolsActionEntry, IDevtoolsObservableDomain, TDevtoolsActionStatus, TDevtoolsListener, } from "../core/ActionDevtools.types";
@@ -24,7 +24,7 @@ export type { TActionTransportDef } from "./ActionRuntime/Handler/ExternalClient
24
24
  export * from "./ActionRuntime/Handler/ExternalClient/Transport/Transport.types";
25
25
  export * from "./ActionRuntime/Handler/ExternalClient/Transport/WebSocket/TransportWebSocket";
26
26
  export { ActionLocalHandler, createLocalHandler, } from "./ActionRuntime/Handler/Local/ActionLocalHandler";
27
- export { type IRuntimeCoordinate, RuntimeCoordinate, } from "./ActionRuntime/RuntimeCoordinate";
27
+ export * from "./ActionRuntime/RuntimeCoordinate";
28
28
  export { EErrId_NiceAction, err_nice_action } from "./errors/err_nice_action";
29
29
  export { isActionPayload_Any_JsonObject } from "./utils/isActionPayload_Any_JsonObject";
30
30
  export * from "./utils/isActionPayload_Request_JsonObject";
package/package.json CHANGED
@@ -1,16 +1,28 @@
1
1
  {
2
2
  "name": "@nice-code/action",
3
- "version": "0.2.11",
3
+ "version": "0.2.12",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": {
8
+ "source": "./src/index.ts",
8
9
  "types": "./build/types/index.d.ts",
9
10
  "import": "./build/index.js"
10
11
  },
11
12
  "./react-query": {
13
+ "source": "./src/react-query/index.ts",
12
14
  "types": "./build/types/react-query/index.d.ts",
13
15
  "import": "./build/react-query/index.js"
16
+ },
17
+ "./devtools/browser": {
18
+ "source": "./src/devtools/browser/index.ts",
19
+ "types": "./build/types/devtools/browser/index.d.ts",
20
+ "import": "./build/devtools/browser/index.js"
21
+ },
22
+ "./devtools/server": {
23
+ "source": "./src/devtools/server/index.ts",
24
+ "types": "./build/types/devtools/server/index.d.ts",
25
+ "import": "./build/devtools/server/index.js"
14
26
  }
15
27
  },
16
28
  "files": [
@@ -32,15 +44,17 @@
32
44
  "build-types": "tsc --project tsconfig.build.json"
33
45
  },
34
46
  "dependencies": {
35
- "@nice-code/error": "0.2.11",
36
- "@nice-code/common-errors": "0.2.11",
47
+ "@nice-code/common-errors": "0.2.12",
48
+ "@nice-code/error": "0.2.12",
37
49
  "@standard-schema/spec": "^1.1.0",
50
+ "@tanstack/react-virtual": "^3.13.26",
38
51
  "http-status-codes": "^2.3.0",
39
52
  "nanoid": "^5.1.9",
40
53
  "std-env": "^4.1.0"
41
54
  },
42
55
  "devDependencies": {
43
56
  "@tanstack/react-query": "^5.100.3",
57
+ "@types/react": "^19.0.0",
44
58
  "msw": "^2.13.6"
45
59
  },
46
60
  "peerDependencies": {