@statelyai/inspect
Advanced tools
Comparing version 0.2.2 to 0.2.3
# @statelyai/inspect | ||
## 0.2.3 | ||
### Patch Changes | ||
- [#12](https://github.com/statelyai/inspect/pull/12) [`c878733`](https://github.com/statelyai/inspect/commit/c8787338e100f45649b14eae49f3eddacefd7df9) Thanks [@mellson](https://github.com/mellson)! - Adds `createSkyInspector`, which allows you to inspect machines in Node or the browser. The inspection will send the events to a server backend through websockets and allows you to open and share a live inspection URL. | ||
## 0.2.2 | ||
@@ -4,0 +10,0 @@ |
@@ -98,22 +98,2 @@ import { InspectionEvent, Snapshot, AnyActorRef, AnyEventObject, Observer, Subscribable } from 'xstate'; | ||
interface WebSocketInspectorOptions extends InspectorOptions { | ||
url: string; | ||
} | ||
declare class WebSocketAdapter implements Adapter { | ||
private ws; | ||
private status; | ||
private deferredEvents; | ||
private options; | ||
constructor(options?: WebSocketInspectorOptions); | ||
start(): void; | ||
stop(): void; | ||
send(inspectionEvent: StatelyInspectionEvent): void; | ||
} | ||
declare function createWebSocketInspector(options?: WebSocketInspectorOptions): Inspector<WebSocketAdapter>; | ||
interface WebSocketReceiver extends Subscribable<StatelyInspectionEvent> { | ||
} | ||
declare function createWebSocketReceiver(options?: { | ||
server: string; | ||
}): WebSocketReceiver; | ||
interface BrowserReceiver extends Subscribable<StatelyInspectionEvent> { | ||
@@ -126,2 +106,5 @@ } | ||
} | ||
interface OptionalBrowserInspectorOptions { | ||
send?: Adapter['send']; | ||
} | ||
/** | ||
@@ -131,3 +114,3 @@ * Creates a browser-based inspector that sends events to a remote inspector window. | ||
*/ | ||
declare function createBrowserInspector(options?: BrowserInspectorOptions): Inspector<BrowserAdapter>; | ||
declare function createBrowserInspector(options?: BrowserInspectorOptions & OptionalBrowserInspectorOptions): Inspector<BrowserAdapter>; | ||
interface BrowserReceiverOptions { | ||
@@ -142,7 +125,7 @@ window?: Window; | ||
declare class BrowserAdapter implements Adapter { | ||
options: Required<BrowserInspectorOptions>; | ||
options: Required<BrowserInspectorOptions> & OptionalBrowserInspectorOptions; | ||
private status; | ||
private deferredEvents; | ||
targetWindow: Window | null; | ||
constructor(options: Required<BrowserInspectorOptions>); | ||
constructor(options: Required<BrowserInspectorOptions> & OptionalBrowserInspectorOptions); | ||
start(): void; | ||
@@ -153,2 +136,27 @@ stop(): void; | ||
export { StatelyActorEvent, StatelyEventEvent, StatelyInspectionEvent, StatelySnapshotEvent, createBrowserInspector, createBrowserReceiver, createInspector, createWebSocketInspector, createWebSocketReceiver }; | ||
declare function createSkyInspector(options?: { | ||
apiKey?: string; | ||
onerror?: (error: Error) => void; | ||
} & InspectorOptions): ReturnType<typeof createInspector>; | ||
interface WebSocketInspectorOptions extends InspectorOptions { | ||
url: string; | ||
} | ||
declare class WebSocketAdapter implements Adapter { | ||
private ws; | ||
private status; | ||
private deferredEvents; | ||
private options; | ||
constructor(options?: WebSocketInspectorOptions); | ||
start(): void; | ||
stop(): void; | ||
send(inspectionEvent: StatelyInspectionEvent): void; | ||
} | ||
declare function createWebSocketInspector(options?: WebSocketInspectorOptions): Inspector<WebSocketAdapter>; | ||
interface WebSocketReceiver extends Subscribable<StatelyInspectionEvent> { | ||
} | ||
declare function createWebSocketReceiver(options?: { | ||
server: string; | ||
}): WebSocketReceiver; | ||
export { StatelyActorEvent, StatelyEventEvent, StatelyInspectionEvent, StatelySnapshotEvent, createBrowserInspector, createBrowserReceiver, createInspector, createSkyInspector, createWebSocketInspector, createWebSocketReceiver }; |
@@ -36,2 +36,3 @@ "use strict"; | ||
createInspector: () => createInspector, | ||
createSkyInspector: () => createSkyInspector, | ||
createWebSocketInspector: () => createWebSocketInspector, | ||
@@ -42,2 +43,6 @@ createWebSocketReceiver: () => createWebSocketReceiver | ||
// src/browser.ts | ||
var import_fast_safe_stringify = __toESM(require("fast-safe-stringify")); | ||
var import_xstate = require("xstate"); | ||
// src/utils.ts | ||
@@ -50,2 +55,3 @@ function toEventObject(event) { | ||
} | ||
var isNode = typeof process !== "undefined" && typeof process.versions?.node !== "undefined" && typeof document === "undefined"; | ||
@@ -58,2 +64,3 @@ // package.json | ||
"@types/jsdom": "^21.1.6", | ||
"@types/uuid": "^9.0.8", | ||
jsdom: "^23.0.0", | ||
@@ -66,3 +73,3 @@ tsup: "^7.2.0", | ||
name: "@statelyai/inspect", | ||
version: "0.2.2", | ||
version: "0.2.3", | ||
description: "Inspection utilities for state, actors, workflows, and state machines.", | ||
@@ -74,3 +81,7 @@ main: "dist/index.js", | ||
dependencies: { | ||
"fast-safe-stringify": "^2.1.1", | ||
"isomorphic-ws": "^5.0.0", | ||
partysocket: "^0.0.25", | ||
superjson: "^1", | ||
uuid: "^9.0.1", | ||
"safe-stable-stringify": "^2.4.3" | ||
@@ -88,3 +99,4 @@ }, | ||
release: "changeset publish", | ||
version: "changeset version" | ||
version: "changeset version", | ||
dev: "yarn build && ./scripts/dev.sh" | ||
}, | ||
@@ -261,104 +273,2 @@ publishConfig: { | ||
// src/webSocket.ts | ||
var import_isomorphic_ws = __toESM(require("isomorphic-ws")); | ||
var import_safe_stable_stringify2 = __toESM(require("safe-stable-stringify")); | ||
var import_xstate = require("xstate"); | ||
var WebSocketAdapter = class { | ||
ws; | ||
status = "closed"; | ||
deferredEvents = []; | ||
options; | ||
constructor(options) { | ||
this.options = { | ||
filter: () => true, | ||
serialize: (inspectionEvent) => JSON.parse((0, import_safe_stable_stringify2.default)(inspectionEvent)), | ||
autoStart: true, | ||
url: "ws://localhost:8080", | ||
...options | ||
}; | ||
} | ||
start() { | ||
const start = () => { | ||
this.ws = new import_isomorphic_ws.default(this.options.url); | ||
this.ws.onopen = () => { | ||
console.log("websocket open"); | ||
this.status = "open"; | ||
this.deferredEvents.forEach((inspectionEvent) => { | ||
const serializedEvent = this.options.serialize(inspectionEvent); | ||
this.ws.send((0, import_safe_stable_stringify2.default)(serializedEvent)); | ||
}); | ||
}; | ||
this.ws.onclose = () => { | ||
console.log("websocket closed"); | ||
}; | ||
this.ws.onerror = async (event) => { | ||
console.error("websocket error", event); | ||
await new Promise((res) => setTimeout(res, 5e3)); | ||
console.warn("restarting"); | ||
start(); | ||
}; | ||
this.ws.onmessage = (event) => { | ||
if (typeof event.data !== "string") { | ||
return; | ||
} | ||
console.log("message", event.data); | ||
}; | ||
}; | ||
start(); | ||
} | ||
stop() { | ||
this.ws.close(); | ||
this.status = "closed"; | ||
} | ||
send(inspectionEvent) { | ||
if (this.status === "open") { | ||
this.ws.send((0, import_safe_stable_stringify2.default)(inspectionEvent)); | ||
} else { | ||
this.deferredEvents.push(inspectionEvent); | ||
} | ||
} | ||
}; | ||
function createWebSocketInspector(options) { | ||
const adapter = new WebSocketAdapter(options); | ||
const inspector = createInspector(adapter, options); | ||
return inspector; | ||
} | ||
function createWebSocketReceiver(options) { | ||
const resolvedOptions = { | ||
server: "ws://localhost:8080", | ||
...options | ||
}; | ||
const observers = /* @__PURE__ */ new Set(); | ||
const ws = new import_isomorphic_ws.default(resolvedOptions.server); | ||
ws.onopen = () => { | ||
console.log("websocket open"); | ||
ws.onmessage = (event) => { | ||
if (typeof event.data !== "string") { | ||
return; | ||
} | ||
console.log("message", event.data); | ||
const eventData = JSON.parse(event.data); | ||
observers.forEach((observer) => { | ||
observer.next?.(eventData); | ||
}); | ||
}; | ||
}; | ||
const receiver = { | ||
subscribe(observerOrFn) { | ||
const observer = (0, import_xstate.toObserver)(observerOrFn); | ||
observers.add(observer); | ||
return { | ||
unsubscribe() { | ||
observers.delete(observer); | ||
} | ||
}; | ||
} | ||
}; | ||
return receiver; | ||
} | ||
// src/browser.ts | ||
var import_xstate2 = require("xstate"); | ||
var import_safe_stable_stringify3 = __toESM(require("safe-stable-stringify")); | ||
// src/useless.ts | ||
@@ -393,3 +303,3 @@ var UselessAdapter = class { | ||
filter: () => true, | ||
serialize: (inspectionEvent) => JSON.parse((0, import_safe_stable_stringify3.default)(inspectionEvent)), | ||
serialize: (inspectionEvent) => JSON.parse((0, import_fast_safe_stringify.default)(inspectionEvent)), | ||
autoStart: true, | ||
@@ -427,3 +337,3 @@ iframe: null, | ||
subscribe(observerOrFn) { | ||
const observer = (0, import_xstate2.toObserver)(observerOrFn); | ||
const observer = (0, import_xstate.toObserver)(observerOrFn); | ||
observers.add(observer); | ||
@@ -481,3 +391,5 @@ return { | ||
} | ||
if (this.status === "connected") { | ||
if (this.options.send) { | ||
this.options.send(event); | ||
} else if (this.status === "connected") { | ||
const serializedEvent = this.options.serialize(event); | ||
@@ -489,2 +401,145 @@ this.targetWindow?.postMessage(serializedEvent, "*"); | ||
}; | ||
// src/createSkyInspector.ts | ||
var import_partysocket = __toESM(require("partysocket")); | ||
var import_superjson = require("superjson"); | ||
var import_uuid = require("uuid"); | ||
var isDevMode = false; | ||
function createSkyInspector(options = {}) { | ||
const { host, apiBaseURL } = { | ||
host: isDevMode ? "localhost:1999" : "stately-sky-beta.mellson.partykit.dev", | ||
apiBaseURL: isDevMode ? "http://localhost:3000/registry/api/sky" : "https://stately.ai/registry/api/sky" | ||
}; | ||
const server = apiBaseURL.replace("/api/sky", ""); | ||
const { apiKey, onerror, ...inspectorOptions } = options; | ||
const sessionId = (0, import_uuid.v4)(); | ||
const room = `inspect-${sessionId}`; | ||
const socket = new import_partysocket.default({ | ||
host, | ||
room, | ||
WebSocket: isNode ? require("isomorphic-ws") : void 0 | ||
}); | ||
const liveInspectUrl = `${server}/inspect/${sessionId}`; | ||
socket.onerror = onerror ?? console.error; | ||
socket.onopen = () => { | ||
console.log("Connected to Sky, link to your live inspect session:"); | ||
console.log(liveInspectUrl); | ||
}; | ||
if (isNode) { | ||
return createInspector({ | ||
...inspectorOptions, | ||
send(event) { | ||
const skyEvent = apiKey ? { apiKey, ...event } : event; | ||
socket.send((0, import_superjson.stringify)(skyEvent)); | ||
} | ||
}); | ||
} else { | ||
return createBrowserInspector({ | ||
...inspectorOptions, | ||
url: liveInspectUrl, | ||
send(event) { | ||
const skyEvent = apiKey ? { apiKey, ...event } : event; | ||
socket.send((0, import_superjson.stringify)(skyEvent)); | ||
} | ||
}); | ||
} | ||
} | ||
// src/webSocket.ts | ||
var import_isomorphic_ws = __toESM(require("isomorphic-ws")); | ||
var import_safe_stable_stringify2 = __toESM(require("safe-stable-stringify")); | ||
var import_xstate2 = require("xstate"); | ||
var WebSocketAdapter = class { | ||
ws; | ||
status = "closed"; | ||
deferredEvents = []; | ||
options; | ||
constructor(options) { | ||
this.options = { | ||
filter: () => true, | ||
serialize: (inspectionEvent) => JSON.parse((0, import_safe_stable_stringify2.default)(inspectionEvent)), | ||
autoStart: true, | ||
url: "ws://localhost:8080", | ||
...options | ||
}; | ||
} | ||
start() { | ||
const start = () => { | ||
this.ws = new import_isomorphic_ws.default(this.options.url); | ||
this.ws.onopen = () => { | ||
console.log("websocket open"); | ||
this.status = "open"; | ||
this.deferredEvents.forEach((inspectionEvent) => { | ||
const serializedEvent = this.options.serialize(inspectionEvent); | ||
this.ws.send((0, import_safe_stable_stringify2.default)(serializedEvent)); | ||
}); | ||
}; | ||
this.ws.onclose = () => { | ||
console.log("websocket closed"); | ||
}; | ||
this.ws.onerror = async (event) => { | ||
console.error("websocket error", event); | ||
await new Promise((res) => setTimeout(res, 5e3)); | ||
console.warn("restarting"); | ||
start(); | ||
}; | ||
this.ws.onmessage = (event) => { | ||
if (typeof event.data !== "string") { | ||
return; | ||
} | ||
console.log("message", event.data); | ||
}; | ||
}; | ||
start(); | ||
} | ||
stop() { | ||
this.ws.close(); | ||
this.status = "closed"; | ||
} | ||
send(inspectionEvent) { | ||
if (this.status === "open") { | ||
this.ws.send((0, import_safe_stable_stringify2.default)(inspectionEvent)); | ||
} else { | ||
this.deferredEvents.push(inspectionEvent); | ||
} | ||
} | ||
}; | ||
function createWebSocketInspector(options) { | ||
const adapter = new WebSocketAdapter(options); | ||
const inspector = createInspector(adapter, options); | ||
return inspector; | ||
} | ||
function createWebSocketReceiver(options) { | ||
const resolvedOptions = { | ||
server: "ws://localhost:8080", | ||
...options | ||
}; | ||
const observers = /* @__PURE__ */ new Set(); | ||
const ws = new import_isomorphic_ws.default(resolvedOptions.server); | ||
ws.onopen = () => { | ||
console.log("websocket open"); | ||
ws.onmessage = (event) => { | ||
if (typeof event.data !== "string") { | ||
return; | ||
} | ||
console.log("message", event.data); | ||
const eventData = JSON.parse(event.data); | ||
observers.forEach((observer) => { | ||
observer.next?.(eventData); | ||
}); | ||
}; | ||
}; | ||
const receiver = { | ||
subscribe(observerOrFn) { | ||
const observer = (0, import_xstate2.toObserver)(observerOrFn); | ||
observers.add(observer); | ||
return { | ||
unsubscribe() { | ||
observers.delete(observer); | ||
} | ||
}; | ||
} | ||
}; | ||
return receiver; | ||
} | ||
// Annotate the CommonJS export names for ESM import in node: | ||
@@ -495,4 +550,5 @@ 0 && (module.exports = { | ||
createInspector, | ||
createSkyInspector, | ||
createWebSocketInspector, | ||
createWebSocketReceiver | ||
}); |
@@ -6,2 +6,3 @@ { | ||
"@types/jsdom": "^21.1.6", | ||
"@types/uuid": "^9.0.8", | ||
"jsdom": "^23.0.0", | ||
@@ -14,3 +15,3 @@ "tsup": "^7.2.0", | ||
"name": "@statelyai/inspect", | ||
"version": "0.2.2", | ||
"version": "0.2.3", | ||
"description": "Inspection utilities for state, actors, workflows, and state machines.", | ||
@@ -22,3 +23,7 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"fast-safe-stringify": "^2.1.1", | ||
"isomorphic-ws": "^5.0.0", | ||
"partysocket": "^0.0.25", | ||
"superjson": "^1", | ||
"uuid": "^9.0.1", | ||
"safe-stable-stringify": "^2.4.3" | ||
@@ -39,4 +44,5 @@ }, | ||
"release": "changeset publish", | ||
"version": "changeset version" | ||
"version": "changeset version", | ||
"dev": "yarn build && ./scripts/dev.sh" | ||
} | ||
} |
@@ -0,5 +1,5 @@ | ||
import safeStringify from 'fast-safe-stringify'; | ||
import { AnyEventObject, Observer, Subscribable, toObserver } from 'xstate'; | ||
import { InspectorOptions, createInspector } from './createInspector'; | ||
import { Adapter, Inspector, StatelyInspectionEvent } from './types'; | ||
import { InspectorOptions, createInspector } from './createInspector'; | ||
import safeStringify from 'safe-stable-stringify'; | ||
import { UselessAdapter } from './useless'; | ||
@@ -36,2 +36,6 @@ | ||
interface OptionalBrowserInspectorOptions { | ||
send?: Adapter['send']; | ||
} | ||
/** | ||
@@ -42,3 +46,3 @@ * Creates a browser-based inspector that sends events to a remote inspector window. | ||
export function createBrowserInspector( | ||
options?: BrowserInspectorOptions | ||
options?: BrowserInspectorOptions & OptionalBrowserInspectorOptions | ||
): Inspector<BrowserAdapter> { | ||
@@ -62,3 +66,4 @@ const resolvedWindow = | ||
window: resolvedWindow, | ||
} satisfies Required<BrowserInspectorOptions>; | ||
} satisfies Required<BrowserInspectorOptions> & | ||
OptionalBrowserInspectorOptions; | ||
const adapter = new BrowserAdapter(resolvedOptions); | ||
@@ -143,3 +148,6 @@ const inspector = createInspector(adapter, resolvedOptions); | ||
constructor(public options: Required<BrowserInspectorOptions>) {} | ||
constructor( | ||
public options: Required<BrowserInspectorOptions> & | ||
OptionalBrowserInspectorOptions | ||
) {} | ||
public start() { | ||
@@ -180,8 +188,11 @@ this.targetWindow = this.options.iframe | ||
if (this.status === 'connected') { | ||
if (this.options.send) { | ||
this.options.send(event); | ||
} else if (this.status === 'connected') { | ||
const serializedEvent = this.options.serialize(event); | ||
this.targetWindow?.postMessage(serializedEvent, '*'); | ||
} | ||
this.deferredEvents.push(event); | ||
} | ||
} |
@@ -0,9 +1,10 @@ | ||
export { createBrowserInspector, createBrowserReceiver } from './browser'; | ||
export { createInspector } from './createInspector'; | ||
export { createWebSocketInspector, createWebSocketReceiver } from './webSocket'; | ||
export { createBrowserInspector, createBrowserReceiver } from './browser'; | ||
export { createSkyInspector } from './createSkyInspector'; | ||
export type { | ||
StatelyActorEvent, | ||
StatelyEventEvent, | ||
StatelyInspectionEvent, | ||
StatelyEventEvent, | ||
StatelySnapshotEvent, | ||
} from './types'; | ||
export { createWebSocketInspector, createWebSocketReceiver } from './webSocket'; |
@@ -1,2 +0,2 @@ | ||
import type { AnyEventObject, AnyActorRef } from 'xstate'; | ||
import type { AnyActorRef, AnyEventObject } from 'xstate'; | ||
@@ -19,1 +19,6 @@ export function toEventObject(event: AnyEventObject | string): AnyEventObject { | ||
} | ||
export const isNode = | ||
typeof process !== 'undefined' && | ||
typeof process.versions?.node !== 'undefined' && | ||
typeof document === 'undefined'; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
73715
25
1843
7
9
+ Addedfast-safe-stringify@^2.1.1
+ Addedpartysocket@^0.0.25
+ Addedsuperjson@^1
+ Addeduuid@^9.0.1
+ Addedcopy-anything@3.0.5(transitive)
+ Addedevent-target-shim@6.0.2(transitive)
+ Addedfast-safe-stringify@2.1.1(transitive)
+ Addedis-what@4.1.16(transitive)
+ Addedpartysocket@0.0.25(transitive)
+ Addedsuperjson@1.13.3(transitive)
+ Addeduuid@9.0.1(transitive)