{"version":3,"sources":["../src/react/index.ts","../src/react/useProperty.ts","../src/resource/reactive.ts","../src/react/useResource.ts","../src/react/useResourceEvent.ts"],"sourcesContent":["// Optional React bindings for `esiur` (import from `\"esiur/react\"`).\r\n//\r\n// This entry point is the *only* place in the package that imports `react` —\r\n// the core library (`\"esiur\"`) has no React dependency at all, and building\r\n// it never touches this file. `react` is a peer dependency here, matching\r\n// the `ws` peer-dependency pattern already used for the optional Node\r\n// WebSocket fallback.\r\nexport { useProperty } from \"./useProperty.js\";\r\nexport { useResource } from \"./useResource.js\";\r\nexport { useResourceEvent } from \"./useResourceEvent.js\";\r\n","import { useCallback, useSyncExternalStore } from \"react\";\r\nimport { readProperty, subscribeToProperty } from \"../resource/reactive.js\";\r\n\r\n/**\r\n * Subscribe to one exported property on a local or remote resource. The\r\n * component re-renders whenever the property changes — locally via\r\n * `Instance.propertyModified`, or remotely via a `PropertyModified`\r\n * notification pushed by the peer and applied to the `EpResource` proxy.\r\n *\r\n * `resource` may be `undefined` (e.g. while a remote attach is still in\r\n * flight); the hook simply returns `undefined` until it's supplied.\r\n *\r\n * @example\r\n * ```tsx\r\n * function StatusBadge({ device }: { device: unknown }) {\r\n *   const status = useProperty<string>(device, \"status\");\r\n *   return <span>{status ?? \"…\"}</span>;\r\n * }\r\n * ```\r\n */\r\nexport function useProperty<T = unknown>(resource: unknown, propertyName: string): T | undefined {\r\n  const subscribe = useCallback(\r\n    (onStoreChange: () => void) => subscribeToProperty(resource, propertyName, onStoreChange),\r\n    [resource, propertyName],\r\n  );\r\n  const getSnapshot = useCallback(\r\n    () => readProperty<T>(resource, propertyName),\r\n    [resource, propertyName],\r\n  );\r\n  return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\r\n}\r\n","/**\r\n * Framework-agnostic adapter over the property/event-change notifications\r\n * already fired by {@link Instance} (local resources) and {@link EpResource}\r\n * (remote resource proxies from `EpConnection.attach()`/`.get()`). Depends\r\n * on neither — it duck-types over whatever `EventHandler`-shaped\r\n * `propertyModified`/`eventOccurred` it finds, so it works for both without\r\n * importing either class (avoiding a dependency on the protocol layer from\r\n * here, and keeping this file usable as the basis for *any* UI binding, not\r\n * just the `esiur/react` one built on top of it).\r\n */\r\n\r\ninterface EventHandlerLike {\r\n  add(handler: (value: unknown) => void): unknown;\r\n  remove(handler: (value: unknown) => void): unknown;\r\n}\r\n\r\nfunction isEventHandlerLike(value: unknown): value is EventHandlerLike {\r\n  const v = value as EventHandlerLike | undefined;\r\n  return !!v && typeof v.add === \"function\" && typeof v.remove === \"function\";\r\n}\r\n\r\n/** A normalized property-change notification, regardless of source shape. */\r\nexport interface PropertyChangeEvent {\r\n  name: string;\r\n  value: unknown;\r\n}\r\n\r\nfunction normalizePropertyChange(raw: unknown): PropertyChangeEvent | undefined {\r\n  // `EpResource.propertyModified` fires `{ name, index, value, age?, date? }`;\r\n  // `Instance.propertyModified` fires `{ resource, property: { name }, value, age }`.\r\n  const r = raw as { name?: unknown; property?: { name?: unknown }; value?: unknown } | undefined;\r\n  const name = typeof r?.name === \"string\" ? r.name : r?.property?.name;\r\n  return typeof name === \"string\" ? { name, value: r?.value } : undefined;\r\n}\r\n\r\nfunction findPropertyModifiedHandler(resource: unknown): EventHandlerLike | undefined {\r\n  const r = resource as Record<string, unknown> | undefined;\r\n  if (!r) return undefined;\r\n  // Remote proxy (or a raw EpResource): `propertyModified` is a real own\r\n  // property on the underlying instance, so `Proxy`'s `get` trap in\r\n  // `EpResource.createProxy` passes it through unchanged.\r\n  if (isEventHandlerLike(r.propertyModified)) return r.propertyModified as EventHandlerLike;\r\n  // Local resource: the notifier lives on `.instance`, not the resource itself.\r\n  const instance = r.instance as Record<string, unknown> | undefined;\r\n  if (instance && isEventHandlerLike(instance.propertyModified))\r\n    return instance.propertyModified as EventHandlerLike;\r\n  return undefined;\r\n}\r\n\r\n/**\r\n * Subscribe to every property change on `resource` (local or remote).\r\n * Returns an unsubscribe function; a no-op if `resource` exposes no\r\n * recognizable change notifier.\r\n */\r\nexport function subscribeToResource(\r\n  resource: unknown,\r\n  onChange: (event: PropertyChangeEvent) => void,\r\n): () => void {\r\n  const handler = findPropertyModifiedHandler(resource);\r\n  if (!handler) return () => {};\r\n\r\n  const wrapped = (raw: unknown): void => {\r\n    const event = normalizePropertyChange(raw);\r\n    if (event) onChange(event);\r\n  };\r\n  handler.add(wrapped);\r\n  return () => handler.remove(wrapped);\r\n}\r\n\r\n/** Subscribe to a single named property's changes. */\r\nexport function subscribeToProperty(\r\n  resource: unknown,\r\n  propertyName: string,\r\n  onChange: (value: unknown) => void,\r\n): () => void {\r\n  return subscribeToResource(resource, (event) => {\r\n    if (event.name === propertyName) onChange(event.value);\r\n  });\r\n}\r\n\r\n/** Read a property's current value directly off a resource/proxy. */\r\nexport function readProperty<T = unknown>(resource: unknown, propertyName: string): T | undefined {\r\n  if (resource == null) return undefined;\r\n  return (resource as Record<string, unknown>)[propertyName] as T | undefined;\r\n}\r\n\r\ninterface PropertyLike {\r\n  name: string;\r\n}\r\ninterface TypeDefLike {\r\n  properties: readonly PropertyLike[];\r\n}\r\n\r\nfunction isTypeDefLike(value: unknown): value is TypeDefLike {\r\n  return !!value && Array.isArray((value as TypeDefLike).properties);\r\n}\r\n\r\nfunction findTypeDef(resource: unknown): TypeDefLike | undefined {\r\n  const r = resource as Record<string, unknown> | undefined;\r\n  if (!r) return undefined;\r\n  // Remote proxy: `typeDef` is a real own property (same pass-through as above).\r\n  if (isTypeDefLike(r.typeDef)) return r.typeDef as TypeDefLike;\r\n  // Local resource: the TypeDef lives on `.instance.definition`.\r\n  const instance = r.instance as Record<string, unknown> | undefined;\r\n  if (instance && isTypeDefLike(instance.definition)) return instance.definition as TypeDefLike;\r\n  return undefined;\r\n}\r\n\r\n/** Build a plain snapshot object of every exported property's current value. */\r\nexport function snapshotProperties<T extends Record<string, unknown> = Record<string, unknown>>(\r\n  resource: unknown,\r\n): T {\r\n  const typeDef = findTypeDef(resource);\r\n  const out: Record<string, unknown> = {};\r\n  if (typeDef) for (const p of typeDef.properties) out[p.name] = readProperty(resource, p.name);\r\n  return out as T;\r\n}\r\n\r\nfunction findEventOccurredHandler(resource: unknown): EventHandlerLike | undefined {\r\n  const r = resource as Record<string, unknown> | undefined;\r\n  return r && isEventHandlerLike(r.eventOccurred) ? (r.eventOccurred as EventHandlerLike) : undefined;\r\n}\r\n\r\ninterface ListenerLike {\r\n  listen(handler: (value: unknown) => void): unknown;\r\n  unlisten(handler: (value: unknown) => void): unknown;\r\n}\r\n\r\nfunction isListenerLike(value: unknown): value is ListenerLike {\r\n  const v = value as ListenerLike | undefined;\r\n  return !!v && typeof v.listen === \"function\" && typeof v.unlisten === \"function\";\r\n}\r\n\r\n/**\r\n * Subscribe to one named exported event, local or remote. Remote proxies\r\n * fire everything through the single `eventOccurred` notifier (filtered\r\n * here by name); local resources expose each event as its own\r\n * {@link EventSource} field with `listen`/`unlisten`.\r\n */\r\nexport function subscribeToResourceEvent(\r\n  resource: unknown,\r\n  eventName: string,\r\n  onEvent: (value: unknown) => void,\r\n): () => void {\r\n  const remoteHandler = findEventOccurredHandler(resource);\r\n  if (remoteHandler) {\r\n    const wrapped = (raw: unknown): void => {\r\n      const event = normalizePropertyChange(raw);\r\n      if (event && event.name === eventName) onEvent(event.value);\r\n    };\r\n    remoteHandler.add(wrapped);\r\n    return () => remoteHandler.remove(wrapped);\r\n  }\r\n\r\n  const source = (resource as Record<string, unknown> | undefined)?.[eventName];\r\n  if (isListenerLike(source)) {\r\n    const wrapped = (value: unknown): void => onEvent(value);\r\n    source.listen(wrapped);\r\n    return () => source.unlisten(wrapped);\r\n  }\r\n\r\n  return () => {};\r\n}\r\n","import { useRef, useSyncExternalStore } from \"react\";\r\nimport { snapshotProperties, subscribeToResource } from \"../resource/reactive.js\";\r\n\r\n/**\r\n * A cached, reference-stable snapshot of a resource's exported properties,\r\n * rebuilt only when a change notification actually fires — required for\r\n * `useSyncExternalStore`, whose `getSnapshot` must return the same\r\n * reference between renders unless the store actually changed, or React\r\n * will treat every render as a change and loop.\r\n */\r\nclass ResourceSnapshotStore<T extends Record<string, unknown>> {\r\n  private value: T;\r\n\r\n  constructor(private readonly resource: unknown) {\r\n    this.value = snapshotProperties<T>(resource);\r\n  }\r\n\r\n  subscribe = (onStoreChange: () => void): (() => void) => {\r\n    return subscribeToResource(this.resource, () => {\r\n      this.value = snapshotProperties<T>(this.resource);\r\n      onStoreChange();\r\n    });\r\n  };\r\n\r\n  getSnapshot = (): T => this.value;\r\n}\r\n\r\n/**\r\n * Subscribe to *every* exported property on a local or remote resource at\r\n * once, re-rendering on any change and returning a plain snapshot object\r\n * (`{ [propertyName]: value }`) — handy for spreading/destructuring several\r\n * properties without a `useProperty` call each.\r\n *\r\n * @example\r\n * ```tsx\r\n * function DeviceCard({ device }: { device: unknown }) {\r\n *   const { status, level } = useResource<{ status: string; level: number }>(device);\r\n *   return <div>{status} — {level}%</div>;\r\n * }\r\n * ```\r\n */\r\nexport function useResource<T extends Record<string, unknown> = Record<string, unknown>>(\r\n  resource: unknown,\r\n): T {\r\n  const ref = useRef<{ resource: unknown; store: ResourceSnapshotStore<T> } | null>(null);\r\n  if (!ref.current || ref.current.resource !== resource)\r\n    ref.current = { resource, store: new ResourceSnapshotStore<T>(resource) };\r\n\r\n  const { store } = ref.current;\r\n  return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);\r\n}\r\n","import { useEffect } from \"react\";\r\nimport { subscribeToResourceEvent } from \"../resource/reactive.js\";\r\n\r\n/**\r\n * Run `handler` whenever a named exported event occurs on a local or remote\r\n * resource — e.g. a server-pushed toast/log line rather than a stored\r\n * property. Unlike {@link useProperty}/{@link useResource} this doesn't\r\n * cause a re-render by itself; call `setState` (or similar) inside\r\n * `handler` if the event should update the UI.\r\n *\r\n * @example\r\n * ```tsx\r\n * function Log({ device }: { device: unknown }) {\r\n *   const [lines, setLines] = useState<string[]>([]);\r\n *   useResourceEvent<string>(device, \"message\", (line) =>\r\n *     setLines((prev) => [...prev, line]),\r\n *   );\r\n *   return <ul>{lines.map((l, i) => <li key={i}>{l}</li>)}</ul>;\r\n * }\r\n * ```\r\n */\r\nexport function useResourceEvent<T = unknown>(\r\n  resource: unknown,\r\n  eventName: string,\r\n  handler: (value: T) => void,\r\n): void {\r\n  useEffect(\r\n    () => subscribeToResourceEvent(resource, eventName, handler as (value: unknown) => void),\r\n    [resource, eventName, handler],\r\n  );\r\n}\r\n"],"mappings":"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","names":["import_react","import_react"]}