@ikalogic/at1000 0.8.0 → 0.10.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 (81) hide show
  1. package/README.md +77 -0
  2. package/build/at1000-6CjGQqmM.cjs +3 -0
  3. package/build/at1000-6CjGQqmM.cjs.map +1 -0
  4. package/build/at1000-C9erttrI.mjs +3 -0
  5. package/build/at1000-C9erttrI.mjs.map +1 -0
  6. package/build/browser.cjs +1 -1
  7. package/build/browser.cjs.map +1 -1
  8. package/build/browser.d.cts +2 -2
  9. package/build/browser.d.mts +2 -2
  10. package/build/browser.mjs +1 -1
  11. package/build/browser.mjs.map +1 -1
  12. package/build/{events-RRtuVpdW.d.cts → events-BVDWI2tr.d.cts} +10 -1
  13. package/build/events-BVDWI2tr.d.cts.map +1 -0
  14. package/build/{events-RRtuVpdW.d.mts → events-BVDWI2tr.d.mts} +10 -1
  15. package/build/events-BVDWI2tr.d.mts.map +1 -0
  16. package/build/events.cjs +1 -1
  17. package/build/events.d.cts +1 -1
  18. package/build/events.d.mts +1 -1
  19. package/build/events.mjs +1 -1
  20. package/build/hmi-Chid1Vvo.d.mts +236 -0
  21. package/build/hmi-Chid1Vvo.d.mts.map +1 -0
  22. package/build/hmi-OyVDyqU-.d.cts +236 -0
  23. package/build/hmi-OyVDyqU-.d.cts.map +1 -0
  24. package/build/hmi.cjs +1 -1
  25. package/build/hmi.cjs.map +1 -1
  26. package/build/hmi.d.cts +2 -2
  27. package/build/hmi.d.mts +2 -2
  28. package/build/hmi.mjs +1 -1
  29. package/build/hmi.mjs.map +1 -1
  30. package/build/{host-TBM8TT7q.d.cts → host-C1Zl1RyR.d.cts} +27 -5
  31. package/build/host-C1Zl1RyR.d.cts.map +1 -0
  32. package/build/{host-BjLI2rbR.d.mts → host-DCfMkBnq.d.mts} +27 -5
  33. package/build/host-DCfMkBnq.d.mts.map +1 -0
  34. package/build/management-BqW9gGfM.mjs +7 -0
  35. package/build/management-BqW9gGfM.mjs.map +1 -0
  36. package/build/management-CIr8us5E.cjs +7 -0
  37. package/build/management-CIr8us5E.cjs.map +1 -0
  38. package/build/management.cjs +1 -1
  39. package/build/management.d.cts +17 -1
  40. package/build/management.d.cts.map +1 -1
  41. package/build/management.d.mts +17 -1
  42. package/build/management.d.mts.map +1 -1
  43. package/build/management.mjs +1 -1
  44. package/build/node.cjs +1 -1
  45. package/build/node.cjs.map +1 -1
  46. package/build/node.d.cts +2 -2
  47. package/build/node.d.mts +2 -2
  48. package/build/node.mjs +1 -1
  49. package/build/node.mjs.map +1 -1
  50. package/build/package-BAite5L5.mjs +2 -0
  51. package/build/package-BAite5L5.mjs.map +1 -0
  52. package/build/{package-BMXokpna.cjs → package-BmyGanoB.cjs} +2 -2
  53. package/build/package-BmyGanoB.cjs.map +1 -0
  54. package/build/{session-qWW9mqWH.d.cts → session-DbV_F9Wz.d.cts} +2 -2
  55. package/build/{session-qWW9mqWH.d.cts.map → session-DbV_F9Wz.d.cts.map} +1 -1
  56. package/build/{session-qWW9mqWH.d.mts → session-DbV_F9Wz.d.mts} +2 -2
  57. package/build/{session-qWW9mqWH.d.mts.map → session-DbV_F9Wz.d.mts.map} +1 -1
  58. package/build/session.cjs +1 -1
  59. package/build/session.d.cts +1 -1
  60. package/build/session.d.mts +1 -1
  61. package/build/session.mjs +1 -1
  62. package/package.json +1 -1
  63. package/build/at1000-CoQUADtx.mjs +0 -3
  64. package/build/at1000-CoQUADtx.mjs.map +0 -1
  65. package/build/at1000-CyFs4TGI.cjs +0 -3
  66. package/build/at1000-CyFs4TGI.cjs.map +0 -1
  67. package/build/events-RRtuVpdW.d.cts.map +0 -1
  68. package/build/events-RRtuVpdW.d.mts.map +0 -1
  69. package/build/hmi-BEMiVKDF.d.cts +0 -151
  70. package/build/hmi-BEMiVKDF.d.cts.map +0 -1
  71. package/build/hmi-BEMiVKDF.d.mts +0 -151
  72. package/build/hmi-BEMiVKDF.d.mts.map +0 -1
  73. package/build/host-BjLI2rbR.d.mts.map +0 -1
  74. package/build/host-TBM8TT7q.d.cts.map +0 -1
  75. package/build/management-CEL202P0.mjs +0 -7
  76. package/build/management-CEL202P0.mjs.map +0 -1
  77. package/build/management-hyOL3JnM.cjs +0 -7
  78. package/build/management-hyOL3JnM.cjs.map +0 -1
  79. package/build/package-BMXokpna.cjs.map +0 -1
  80. package/build/package-D7Y23M8-.mjs +0 -2
  81. package/build/package-D7Y23M8-.mjs.map +0 -1
package/README.md CHANGED
@@ -48,6 +48,83 @@ if (!deviceAddress) {
48
48
  const tester = await AT1000.open(deviceAddress); // takes exclusive access
49
49
  ```
50
50
 
51
+ ## Operator prompts
52
+
53
+ Show a prompt on the LCD and wait for the operator's selection:
54
+
55
+ ```ts
56
+ import { AT1000 } from '@ikalogic/at1000';
57
+ import type { PromptRequestInput } from '@ikalogic/at1000/hmi';
58
+
59
+ const device = await AT1000.open('at1000.local');
60
+ const request: PromptRequestInput = {
61
+ text: 'Measurement failed.\nChoose the next action.',
62
+ choices: [
63
+ { label: 'Retry', color: '#1769AA' },
64
+ 'Skip', // a bare string is a choice with no color
65
+ { label: 'Cancel', color: '#B3261E' },
66
+ ],
67
+ default_index: 2,
68
+ timeout_ms: 60000,
69
+ };
70
+
71
+ const result = await device.hmi.prompt(request);
72
+ if (result.status === 'selected') {
73
+ console.log(result.selection.label, result.selection.index);
74
+ } else {
75
+ console.log(result.status);
76
+ }
77
+ ```
78
+
79
+ Turn the knob to move between choices, then press it to select. The LCD shows one
80
+ choice at a time, without wrapping at the first or last choice. Colors use
81
+ `#RRGGBB`. The default is blue (`#1769AA`), with black or white text chosen for
82
+ contrast. Selecting the choice named “Cancel” returns `selected`, like any other
83
+ choice.
84
+
85
+ A choice is a `{ label, color? }` object, or a bare string that stands for
86
+ `{ label }`. Choices are identified by position, so two choices may share a label.
87
+ `selection` is always present: the choice under the knob while pending, the
88
+ confirmed choice when selected, and the last highlighted choice when the prompt was
89
+ cancelled or timed out.
90
+
91
+ The request accepts 1–32 choices. Text must be nonblank and at most 256 UTF-8 bytes.
92
+ Labels must be nonblank and at most 64 UTF-8 bytes. Text permits line feeds for
93
+ wrapping, but other control characters are rejected. Labels reject all control
94
+ characters. The server also checks displayed text for supported glyphs.
95
+ `default_index` is zero-based, must be below the number of choices, and defaults to
96
+ 0. `timeout_ms` defaults to 60000 and accepts integers from 1000 to 1800000. The
97
+ deadline guards against an abandoned prompt, so the first turn of the knob stops
98
+ it: once the operator is answering, only a selection, a cancellation, or a lost
99
+ session ends the prompt.
100
+
101
+ `prompt()` is the only prompt method, and it returns `selected`, `cancelled`, or
102
+ `timed_out`. Timeout and cancellation are ordinary results. Instead of polling it
103
+ waits on the `hmi.prompt` event, opening the event stream if the application has
104
+ not already, and re-arms a `timeout_ms + 2000 ms` window on every event. A window
105
+ that expires costs a single read before the wait resumes, which covers a stalled or
106
+ reconnecting socket without ever cancelling the prompt. Pass `{ signal }` as the
107
+ second argument to abort locally. The helper attempts cancellation after an abort or
108
+ a transport error, then rejects with the original error. Creation finishes before
109
+ abort cleanup so the helper retains the new prompt ID. If the connection fails, the
110
+ server deadline still applies.
111
+
112
+ Prompts require an active session. While a prompt is pending, another prompt,
113
+ screen changes, resets, and knob reads return HTTP 409. Audio remains available.
114
+ Session changes cancel the prompt. A terminal prompt restores the previous screen
115
+ unless another owner or menu has replaced it. Terminal results remain readable for
116
+ five minutes, limited to the newest 32.
117
+
118
+ The [example](examples/hmi_prompt.ts) in this package runs the same question two
119
+ ways: `minimal` is the two lines a sequence needs, and `typical` uses every request
120
+ field with an exhaustive result switch, the HTTP 409 other HMI writes get while the
121
+ prompt is pending, and a Ctrl+C abort.
122
+
123
+ ```bash
124
+ bun examples/hmi_prompt.ts at1000.local # both
125
+ bun examples/hmi_prompt.ts at1000.local minimal # or one stage
126
+ ```
127
+
51
128
  ## Real-time events
52
129
 
53
130
  Subscribe to live device state changes over a WebSocket. Fetch initial state via
@@ -0,0 +1,3 @@
1
+ const e=require("./rolldown-runtime-BocRIvOZ.cjs"),t=require("./com.cjs"),n=require("./management-CIr8us5E.cjs"),r=require("./gpio.cjs"),i=require("./hmi.cjs"),a=require("./power.cjs"),o=require("./relays.cjs");let s=require("up-fetch"),c=require("valibot");c=e.t(c,1);const l=c.object({active:c.boolean(),kind:c.optional(c.picklist([`remote`,`project`])),label:c.optional(c.string()),project:c.optional(c.string()),since:c.optional(c.number())}),u=c.object({token:c.string()});async function d(e,t){let n=await T(new URL(e).host)(`${e}/session/open`,{method:`POST`,body:JSON.stringify(t),headers:{"content-type":`application/json`},signal:AbortSignal.timeout(3e4)});if(n.status!==404&&n.status!==405)return n.ok||await S(n,`POST`),c.parse(u,await n.json()).token}var f=class{fetch;constructor(e){this.fetch=e}state(){return this.fetch(`/session/state`,{schema:l})}};const p=c.array(c.pipe(c.number(),c.integer(),c.minValue(0),c.maxValue(255))),m={"com.can_config":t.canConfig,"com.rs232_config":t.rs232Config,"com.rs485_config":t.rs485Config,"com.spi_config":t.spiConfig,"com.i2c_config":t.i2cConfig,"com.uart_config":t.uartConfig,"com.can_rx":c.array(t.canFrame),"com.uart_rx":p,"com.rs232_rx":p,"com.rs485_rx":p,"gpio.state":c.union([r.digitalState,r.analogState]),"gpio.config":c.union([r.analogInputConfiguration,r.analogOutputConfiguration,r.digitalInputConfiguration,r.digitalOutputConfiguration,r.openDrainConfiguration]),"gpio.sync":r.syncState,"hmi.text":i.text,"hmi.colors":i.color,"hmi.progress":c.optional(i.progress),"hmi.play":i.playbackConfiguration,"hmi.knob":i.internalKnobEvent,"hmi.prompt":i.promptState,"management.datetime":c.object({epoch_seconds:c.number()}),"management.calibration":n.r,"management.calibration_state":n.i,"management.project":n.s,"management.update_status":n.l,"power.dut":a.dutState,"power.usb":a.usbState,"relays.state":o.relayState,"relays.states":o.states,"session.state":l},h=[`com.reset`,`gpio.reset`,`hmi.clear`,`hmi.reset`,`power.reset`,`relays.reset`],g=c.object({type:c.string(),id:c.optional(c.pipe(c.number(),c.integer())),ts:c.number(),data:c.optional(c.unknown())}),_=5e3,v=(...e)=>console.debug(`[at1000:events]`,...e);var y=class{host;socket;listeners=new Map;stateListeners=new Set;shouldReconnect=!1;reconnectDelayMs=500;reconnectTimer;_connected=!1;connectPromise;constructor(e){this.host=e}get connected(){return this._connected}async connect(e){if(typeof WebSocket>`u`)throw Error(`WebSocket is not available in this runtime (Node >= 22, Bun, or a browser is required)`);if(!this._connected){this.connectPromise??=this.openSocket(e?.timeout??_);try{await this.connectPromise}finally{this.connectPromise=void 0}}}close(){this.shouldReconnect=!1,this.reconnectTimer!==void 0&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0);let e=this.socket;this.socket=void 0,e!==void 0&&e.close(),this.setConnected(!1)}on(e,t){let n=this.listeners.get(e);return n===void 0&&(n=new Set,this.listeners.set(e,n)),n.add(t),()=>{let n=this.listeners.get(e);n!==void 0&&(n.delete(t),n.size===0&&this.listeners.delete(e))}}onStateChange(e){return this.stateListeners.add(e),()=>{this.stateListeners.delete(e)}}openSocket(e){let{promise:t,resolve:r,reject:i}=Promise.withResolvers(),a=`ws://${n.u(this.host)}/v0/events`,o=new WebSocket(a);this.socket=o;let s=!1,c=setTimeout(()=>{s||(s=!0,o.close(),i(Error(`Timed out connecting to ${a}`)))},e);return o.onopen=()=>{this.socket!==o||s||(s=!0,clearTimeout(c),this.shouldReconnect=!0,this.reconnectDelayMs=500,this.setConnected(!0),v(`open`,a),r())},o.onmessage=e=>{this.socket===o&&this.handleMessage(e.data)},o.onerror=()=>{v(`error`,a),this.socket===o&&!s&&(s=!0,clearTimeout(c),i(Error(`Failed to connect to ${a}`)))},o.onclose=()=>{if(this.socket===o){if(v(`close`,a),clearTimeout(c),this.setConnected(!1),!s){s=!0,i(Error(`Connection to ${a} closed before opening`));return}this.shouldReconnect&&this.scheduleReconnect()}},t}scheduleReconnect(){if(!this.shouldReconnect||this.reconnectTimer!==void 0)return;let e=this.reconnectDelayMs;v(`reconnecting in`,e,`ms`),this.reconnectDelayMs=Math.min(this.reconnectDelayMs*2,8e3),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=void 0,this.shouldReconnect&&this.openSocket(_).catch(()=>{})},e)}handleMessage(e){if(typeof e!=`string`){v(`drop: non-string frame`);return}let t;try{t=JSON.parse(e)}catch{v(`drop: invalid JSON`,e);return}v(`recv`,t);let n=c.safeParse(g,t);if(!n.success){v(`drop: malformed envelope`,n.issues);return}let{type:r,id:i,ts:a}=n.output;if(r in m){let e=m[r],t=c.safeParse(e,n.output.data);if(!t.success){v(`drop: payload validation failed for`,r,t.issues);return}let o={type:r,id:i,ts:a,data:t.output};v(`dispatch`,r),this.emitTo(r,o),this.emitTo(`*`,o);return}if(h.includes(r)){let e={type:r,id:i,ts:a};v(`dispatch (dataless)`,r),this.emitTo(r,e),this.emitTo(`*`,e);return}v(`drop: unknown type`,r),this.emitTo(`*`,{type:r,id:i,ts:a,data:n.output.data})}emitTo(e,t){let n=this.listeners.get(e);if(n!==void 0)for(let e of n)try{e(t)}catch{}}setConnected(e){if(this._connected!==e){this._connected=e;for(let t of this.stateListeners)try{t(e)}catch{}}}},b=class extends Error{code;detail;suggestion;path;constructor(e){let t=e.suggestion.replace(/\n/g,`
2
+ `);super(`${e.message} (${e.code})\n Suggestion: ${t}\n Path: ${e.path}`),this.code=e.code,this.detail=e.message,this.suggestion=e.suggestion,this.path=e.path}};function x(e){if(typeof e!=`object`||!e)return!1;let t=e;return[`code`,`message`,`suggestion`,`path`].every(e=>typeof t[e]==`string`)}async function S(e,t){let n,r=e.headers.get(`content-type`);try{n=r?.includes(`application/json`)?await e.json():await e.text()}catch{n=`Failed to parse response body`}throw x(n)?new b(n):Error(`API request to ${t} ${e.url} failed with status ${e.status}: ${typeof n==`string`?n:JSON.stringify(n)}`)}var C=class extends Error{host;method;path;constructor(e,t){super(`The AT1000 took too long to respond.`,{cause:t}),this.name=`TimeoutError`,this.host=e.host,this.method=e.method,this.path=e.path}},w=class extends Error{host;method;path;constructor(e,t){super(`Cannot reach the AT1000. Check that it is powered on and on the same network.`,{cause:t}),this.name=`ConnectionError`,this.host=e.host,this.method=e.method,this.path=e.path}};function T(e){return async(t,n)=>{let r=t instanceof Request?t:new Request(t,n);try{return await fetch(r)}catch(t){let n=t instanceof Error?t.name:void 0,i=r.signal?.reason,a=i==null||typeof i==`object`&&`name`in i&&i.name===`TimeoutError`;if(n===`AbortError`&&!a)throw t;let o={host:e,method:r.method,path:new URL(r.url).pathname};throw n===`TimeoutError`||n===`AbortError`?new C(o,t):new w(o,t)}}}var E=class e{host;info;token;sessionSupported;fetch;com;hmi;gpio;management;power;relays;events;session;constructor(e,c,l,u=!0){this.host=e,this.info=c,this.token=l,this.sessionSupported=u,this.fetch=(0,s.up)(T(e),()=>({baseUrl:`http://${n.u(this.host)}/v0`,timeout:5e3,headers:this.token?{"X-AT1000-Session":this.token}:void 0,parseRejected:(e,t)=>S(e,t.method)})),this.com=new t.ComApi(this.fetch),this.events=new y(this.host),this.hmi=new i.HmiApi(this.fetch,this.events),this.gpio=new r.GpioApi(this.fetch,this.info),this.management=new n.n(this.fetch,this.host),this.power=new a.PowerApi(this.fetch,this.info),this.relays=new o.RelaysApi(this.fetch),this.session=new f(this.fetch)}async reset(){await this.com.reset(),await this.hmi.reset(),await this.gpio.reset(),await this.power.reset(),await this.relays.reset()}static async openBase(t,r,i){if(!t?.trim())throw Error(`No device address provided. Pass a host from findDevices() or findLocalDevice().`);let a=`http://${n.u(t)}/v0`,o=(0,s.up)(T(t),()=>({baseUrl:a,timeout:1e3,parseRejected:(e,t)=>S(e,t.method)})),c=await new n.n(o,t).info();if(r?.readonly)return new e(t,c);let l=await d(a,{kind:i?.kind??`remote`,label:r?.label??i?.defaultLabel,project:i?.project});return l===void 0?new e(t,c,void 0,!1):new e(t,c,l)}};Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,"l",{enumerable:!0,get:function(){return d}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return E}}),Object.defineProperty(exports,"u",{enumerable:!0,get:function(){return l}});
3
+ //# sourceMappingURL=at1000-6CjGQqmM.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"at1000-6CjGQqmM.cjs","names":["v","v","canConfig","rs232Config","rs485Config","spiConfig","i2cConfig","uartConfig","canFrame","digitalState","analogState","analogInputConfiguration","analogOutputConfiguration","digitalInputConfiguration","digitalOutputConfiguration","openDrainConfiguration","syncState","text","color","progress","playbackConfiguration","internalKnobEvent","promptState","calibrationData","calibrationState","projectEvent","updateStatus","dutState","usbState","relayState","states","format_host_for_url","up","format_host_for_url","ComApi","HmiApi","GpioApi","ManagementApi","PowerApi","RelaysApi"],"sources":["../src/api/session.ts","../src/api/events.ts","../src/at1000.ts"],"sourcesContent":["import type { UpFetch } from 'up-fetch';\nimport * as v from 'valibot';\n\nimport { deviceFetch, parseApiError } from '../at1000';\n\nexport const sessionState = v.object({\n active: v.boolean(),\n kind: v.optional(v.picklist(['remote', 'project'])),\n label: v.optional(v.string()),\n project: v.optional(v.string()),\n since: v.optional(v.number()),\n});\nexport type SessionState = v.InferOutput<typeof sessionState>;\n\n/** Body for `POST /v0/session/open`. */\nexport type SessionOpenBody = { kind: 'remote' | 'project'; label?: string; project?: string };\n\nconst openResponse = v.object({ token: v.string() });\n\n/**\n * Opens an exclusive session on the device (last-open-wins). Returns the minted\n * session token, or `undefined` when the server predates the session model.\n *\n * Deliberately uses a direct `fetch` (not the shared up-fetch client) so the\n * response status is inspectable before any structured-error handler throws:\n * pre-session servers answer the unmatched route with 404/405, which we degrade\n * to `undefined` rather than surface as a failure. A 30 s timeout (not the SDK\n * default 5 s) covers a remote-kind open synchronously stopping any running\n * project container; the host is already known-alive from the preceding info\n * probe, so the long timeout never hangs on dead hosts.\n */\nexport async function open_session(\n baseUrl: string,\n body: SessionOpenBody,\n): Promise<string | undefined> {\n const response = await deviceFetch(new URL(baseUrl).host)(`${baseUrl}/session/open`, {\n method: 'POST',\n body: JSON.stringify(body),\n headers: { 'content-type': 'application/json' },\n signal: AbortSignal.timeout(30_000),\n });\n\n if (response.status === 404 || response.status === 405) {\n return undefined; // legacy server without the session model\n }\n if (!response.ok) {\n await parseApiError(response, 'POST');\n }\n\n return v.parse(openResponse, await response.json()).token;\n}\n\n/** @hideconstructor */\nexport class SessionApi {\n constructor(private readonly fetch: UpFetch) {}\n\n /**\n * Reads the current device session state.\n * @returns The active holder (kind/label/project/since) or `{ active: false }`.\n */\n state(): Promise<SessionState> {\n return this.fetch('/session/state', { schema: sessionState });\n }\n}\n","import * as v from 'valibot';\n\nimport { format_host_for_url } from '../host';\nimport {\n canConfig,\n canFrame,\n i2cConfig,\n rs232Config,\n rs485Config,\n spiConfig,\n uartConfig,\n} from './com';\nimport {\n analogInputConfiguration,\n analogOutputConfiguration,\n analogState,\n digitalInputConfiguration,\n digitalOutputConfiguration,\n digitalState,\n openDrainConfiguration,\n syncState,\n} from './gpio';\nimport {\n color,\n internalKnobEvent,\n playbackConfiguration,\n progress,\n promptState,\n text,\n} from './hmi';\nimport { calibrationData, calibrationState, projectEvent, updateStatus } from './management';\nimport { dutState, usbState } from './power';\nimport { relayState, states } from './relays';\nimport { sessionState } from './session';\n\n// Receive-side serial bytes. Distinct from com's `serialData`, whose string->bytes\n// transform is for request bodies; events deliver raw byte arrays.\nconst rxBytes = v.array(v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(255)));\n\n// Schema for the `data` payload of each event, keyed by the wire `type` literal.\nconst eventDataSchemas = {\n 'com.can_config': canConfig,\n 'com.rs232_config': rs232Config,\n 'com.rs485_config': rs485Config,\n 'com.spi_config': spiConfig,\n 'com.i2c_config': i2cConfig,\n 'com.uart_config': uartConfig,\n 'com.can_rx': v.array(canFrame),\n 'com.uart_rx': rxBytes,\n 'com.rs232_rx': rxBytes,\n 'com.rs485_rx': rxBytes,\n 'gpio.state': v.union([digitalState, analogState]),\n 'gpio.config': v.union([\n analogInputConfiguration,\n analogOutputConfiguration,\n digitalInputConfiguration,\n digitalOutputConfiguration,\n openDrainConfiguration,\n ]),\n // Mirrors the glz::meta of gpio::sync_hold_state_t (apps/server/src/api/resources/gpio.hpp).\n 'gpio.sync': syncState,\n 'hmi.text': text,\n 'hmi.colors': color,\n // `data` absent (progress hidden) is accepted by v.optional.\n 'hmi.progress': v.optional(progress),\n 'hmi.play': playbackConfiguration,\n 'hmi.knob': internalKnobEvent,\n 'hmi.prompt': promptState,\n 'management.datetime': v.object({ epoch_seconds: v.number() }),\n 'management.calibration': calibrationData,\n 'management.calibration_state': calibrationState,\n 'management.project': projectEvent,\n 'management.update_status': updateStatus,\n 'power.dut': dutState,\n 'power.usb': usbState,\n 'relays.state': relayState,\n 'relays.states': states,\n 'session.state': sessionState,\n} as const;\n\nconst dataLessEvents = [\n 'com.reset',\n 'gpio.reset',\n 'hmi.clear',\n 'hmi.reset',\n 'power.reset',\n 'relays.reset',\n] as const;\n\nconst envelope = v.object({\n type: v.string(),\n id: v.optional(v.pipe(v.number(), v.integer())),\n ts: v.number(),\n data: v.optional(v.unknown()),\n});\n\ntype EventDataSchemas = typeof eventDataSchemas;\n\n/** Every event `type` literal the server can emit. */\nexport type EventType = keyof EventDataSchemas | (typeof dataLessEvents)[number];\n\ntype PayloadEvent = {\n [K in keyof EventDataSchemas]: {\n type: K;\n id?: number;\n ts: number;\n data: v.InferOutput<EventDataSchemas[K]>;\n };\n}[keyof EventDataSchemas];\n\ntype DataLessEvent = { type: (typeof dataLessEvents)[number]; id?: number; ts: number };\n\n/** A fully-typed device event for a known `type`. */\nexport type DeviceEvent = PayloadEvent | DataLessEvent;\n\n/**\n * Shape delivered to `'*'` listeners. Forward-compatible: a newer server may emit\n * a `type` this SDK does not model, surfaced here with an untyped `data`.\n */\nexport type AnyDeviceEvent = DeviceEvent | {\n type: string;\n id?: number;\n ts: number;\n data?: unknown;\n};\n\ntype EventListener = (event: AnyDeviceEvent) => void;\ntype StateListener = (connected: boolean) => void;\n\nconst reconnectBaseDelayMs = 500;\nconst reconnectMaxDelayMs = 8_000;\nconst defaultConnectTimeoutMs = 5_000;\n\nconst debugLog = (...args: unknown[]): void => console.debug('[at1000:events]', ...args);\n\n/** @hideconstructor */\nexport class EventsApi {\n private socket: WebSocket | undefined;\n private readonly listeners = new Map<string, Set<EventListener>>();\n private readonly stateListeners = new Set<StateListener>();\n private shouldReconnect = false;\n private reconnectDelayMs = reconnectBaseDelayMs;\n private reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n private _connected = false;\n private connectPromise: Promise<void> | undefined;\n\n constructor(private readonly host: string) {}\n\n /** Whether the WebSocket is currently open. */\n get connected(): boolean {\n return this._connected;\n }\n\n /**\n * Open the event stream. Resolves once the socket is open, rejects on failure or\n * after `timeout` ms. A no-op while already connected. Auto-reconnect arms only\n * after the first successful open, so a failed initial `connect()` (e.g. old\n * firmware without `/v0/events`) rejects without scheduling retries.\n */\n async connect(options?: { timeout?: number }): Promise<void> {\n if (typeof WebSocket === 'undefined') {\n throw new Error(\n 'WebSocket is not available in this runtime (Node >= 22, Bun, or a browser is required)',\n );\n }\n if (this._connected) {\n return;\n }\n // Coalesce concurrent opens: the layout holder-banner effect and the\n // projects page both call connect() on the shared per-client socket. Without\n // this a second caller starts a second openSocket() that orphans the first\n // (leaked WS + a connect() that never settles). Reuse the in-flight open.\n this.connectPromise ??= this.openSocket(options?.timeout ?? defaultConnectTimeoutMs);\n try {\n await this.connectPromise;\n } finally {\n this.connectPromise = undefined;\n }\n }\n\n /** Close the stream and cancel any pending reconnect. */\n close(): void {\n this.shouldReconnect = false;\n if (this.reconnectTimer !== undefined) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = undefined;\n }\n const socket = this.socket;\n this.socket = undefined;\n if (socket !== undefined) {\n socket.close();\n }\n this.setConnected(false);\n }\n\n /**\n * Subscribe to an event `type`, or `'*'` for every event. Returns an unsubscribe\n * function. Listeners survive reconnects and are isolated: a throwing listener\n * does not break dispatch to the others.\n */\n on<T extends EventType>(\n type: T,\n listener: (event: Extract<DeviceEvent, { type: T }>) => void,\n ): () => void;\n on(type: '*', listener: (event: AnyDeviceEvent) => void): () => void;\n on(type: string, listener: (event: any) => void): () => void {\n let set = this.listeners.get(type);\n if (set === undefined) {\n set = new Set();\n this.listeners.set(type, set);\n }\n set.add(listener);\n return () => {\n const current = this.listeners.get(type);\n if (current === undefined) {\n return;\n }\n current.delete(listener);\n if (current.size === 0) {\n this.listeners.delete(type);\n }\n };\n }\n\n /** Subscribe to connection state changes. Returns an unsubscribe function. */\n onStateChange(listener: StateListener): () => void {\n this.stateListeners.add(listener);\n return () => {\n this.stateListeners.delete(listener);\n };\n }\n\n private openSocket(timeoutMs: number): Promise<void> {\n const { promise, resolve, reject } = Promise.withResolvers<void>();\n const url = `ws://${format_host_for_url(this.host)}/v0/events`;\n const socket = new WebSocket(url);\n this.socket = socket;\n\n let settled = false;\n const timer = setTimeout(() => {\n if (!settled) {\n settled = true;\n socket.close();\n reject(new Error(`Timed out connecting to ${url}`));\n }\n }, timeoutMs);\n\n socket.onopen = () => {\n if (this.socket !== socket || settled) {\n return;\n }\n settled = true;\n clearTimeout(timer);\n this.shouldReconnect = true;\n this.reconnectDelayMs = reconnectBaseDelayMs;\n this.setConnected(true);\n debugLog('open', url);\n resolve();\n };\n\n socket.onmessage = (event) => {\n if (this.socket === socket) {\n this.handleMessage(event.data);\n }\n };\n\n socket.onerror = () => {\n debugLog('error', url);\n if (this.socket === socket && !settled) {\n settled = true;\n clearTimeout(timer);\n reject(new Error(`Failed to connect to ${url}`));\n }\n };\n\n socket.onclose = () => {\n if (this.socket !== socket) {\n return;\n }\n debugLog('close', url);\n clearTimeout(timer);\n this.setConnected(false);\n if (!settled) {\n settled = true;\n reject(new Error(`Connection to ${url} closed before opening`));\n return;\n }\n if (this.shouldReconnect) {\n this.scheduleReconnect();\n }\n };\n\n return promise;\n }\n\n private scheduleReconnect(): void {\n if (!this.shouldReconnect || this.reconnectTimer !== undefined) {\n return;\n }\n const delay = this.reconnectDelayMs;\n debugLog('reconnecting in', delay, 'ms');\n this.reconnectDelayMs = Math.min(this.reconnectDelayMs * 2, reconnectMaxDelayMs);\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = undefined;\n if (!this.shouldReconnect) {\n return;\n }\n // A failed attempt rejects this promise; onclose then schedules the next retry.\n this.openSocket(defaultConnectTimeoutMs).catch(() => {});\n }, delay);\n }\n\n private handleMessage(data: unknown): void {\n if (typeof data !== 'string') {\n debugLog('drop: non-string frame');\n return;\n }\n let raw: unknown;\n try {\n raw = JSON.parse(data);\n } catch {\n debugLog('drop: invalid JSON', data);\n return;\n }\n\n debugLog('recv', raw);\n const parsed = v.safeParse(envelope, raw);\n if (!parsed.success) {\n debugLog('drop: malformed envelope', parsed.issues);\n return;\n }\n const { type, id, ts } = parsed.output;\n\n if (type in eventDataSchemas) {\n const schema = eventDataSchemas[type as keyof EventDataSchemas] as v.GenericSchema;\n const payload = v.safeParse(schema, parsed.output.data);\n if (!payload.success) {\n debugLog('drop: payload validation failed for', type, payload.issues);\n return;\n }\n const event: AnyDeviceEvent = { type, id, ts, data: payload.output };\n debugLog('dispatch', type);\n this.emitTo(type, event);\n this.emitTo('*', event);\n return;\n }\n\n if ((dataLessEvents as readonly string[]).includes(type)) {\n const event: AnyDeviceEvent = { type, id, ts };\n debugLog('dispatch (dataless)', type);\n this.emitTo(type, event);\n this.emitTo('*', event);\n return;\n }\n\n // Unknown type: deliver the raw envelope to '*' only (server may be newer).\n debugLog('drop: unknown type', type);\n this.emitTo('*', { type, id, ts, data: parsed.output.data });\n }\n\n private emitTo(key: string, event: AnyDeviceEvent): void {\n const set = this.listeners.get(key);\n if (set === undefined) {\n return;\n }\n for (const listener of set) {\n try {\n listener(event);\n } catch {\n // One throwing listener must not break dispatch to the others.\n }\n }\n }\n\n private setConnected(connected: boolean): void {\n if (this._connected === connected) {\n return;\n }\n this._connected = connected;\n for (const listener of this.stateListeners) {\n try {\n listener(connected);\n } catch {\n // Ignore throwing state listeners.\n }\n }\n }\n}\n","import { type UpFetch, up } from 'up-fetch';\n\nimport { ComApi } from './api/com';\nimport { EventsApi } from './api/events';\nimport { GpioApi } from './api/gpio';\nimport { HmiApi } from './api/hmi';\nimport { type Information, ManagementApi } from './api/management';\nimport { PowerApi } from './api/power';\nimport { RelaysApi } from './api/relays';\nimport { SessionApi, open_session } from './api/session';\nimport { format_host_for_url } from './host';\n\n/** Structured error thrown for AT1000 API responses carrying `{ code, message, suggestion, path }`. */\nexport class ApiError extends Error {\n readonly code: string;\n /** Raw human-readable server message (without code/suggestion/path). */\n readonly detail: string;\n readonly suggestion: string;\n readonly path: string;\n\n constructor(body: { code: string; message: string; suggestion: string; path: string }) {\n const formattedSuggestion = body.suggestion.replace(/\\n/g, '\\n ');\n super(\n `${body.message} (${body.code})\\n Suggestion: ${formattedSuggestion}\\n Path: ${body.path}`,\n );\n this.code = body.code;\n this.detail = body.message;\n this.suggestion = body.suggestion;\n this.path = body.path;\n }\n}\n\n/** Type guard for the AT1000 structured error body. */\nfunction isApiErrorBody(\n value: unknown,\n): value is { code: string; message: string; suggestion: string; path: string } {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const record = value as Record<string, unknown>;\n return ['code', 'message', 'suggestion', 'path'].every((key) => typeof record[key] === 'string');\n}\n\n/**\n * Shared rejection handler for every AT1000 up-fetch client. Parses a structured\n * `{ code, message, suggestion, path }` body into an {@link ApiError}, otherwise\n * throws a generic error carrying the HTTP status. Written once so the per-client\n * factory and `open_session` share one code path.\n */\nexport async function parseApiError(response: Response, method: string): Promise<never> {\n let errorData: unknown;\n const contentType = response.headers.get('content-type');\n try {\n errorData = contentType?.includes('application/json')\n ? await response.json()\n : await response.text();\n } catch {\n errorData = 'Failed to parse response body';\n }\n\n if (isApiErrorBody(errorData)) {\n throw new ApiError(errorData);\n }\n\n throw new Error(\n `API request to ${method} ${response.url} failed with status ${response.status}: ${\n typeof errorData === 'string' ? errorData : JSON.stringify(errorData)\n }`,\n );\n}\n\n/** Where a failed request was headed. Attached to a transport error for logs and reports. */\nexport interface RequestTarget {\n /** Device authority, e.g. `192.168.1.23:8080`. */\n host: string;\n method: string;\n /** Request path, e.g. `/v0/management/projects`. */\n path: string;\n}\n\n/** Thrown when the device did not answer a request before the client timeout expired. */\nexport class TimeoutError extends Error {\n readonly host: string;\n readonly method: string;\n readonly path: string;\n\n constructor(target: RequestTarget, cause: unknown) {\n super('The AT1000 took too long to respond.', { cause });\n this.name = 'TimeoutError';\n this.host = target.host;\n this.method = target.method;\n this.path = target.path;\n }\n}\n\n/** Thrown when a request never reached the device: no route, refused socket, or unresolved name. */\nexport class ConnectionError extends Error {\n readonly host: string;\n readonly method: string;\n readonly path: string;\n\n constructor(target: RequestTarget, cause: unknown) {\n super('Cannot reach the AT1000. Check that it is powered on and on the same network.', {\n cause,\n });\n this.name = 'ConnectionError';\n this.host = target.host;\n this.method = target.method;\n this.path = target.path;\n }\n}\n\n/** The `fetch` shape every AT1000 up-fetch client is built on. */\nexport type DeviceFetch = (input: Request | string | URL, init?: RequestInit) => Promise<Response>;\n\n/**\n * Wraps `fetch` so a transport failure carries a message a user can act on.\n * Raw engine messages do not qualify: WebKit rejects an aborted fetch with\n * \"Fetch is aborted\", Node with \"The operation timed out\", and a refused socket\n * surfaces as \"Failed to fetch\". The original error is kept as `cause`, and a\n * caller-supplied abort is rethrown untouched.\n */\nexport function deviceFetch(host: string): DeviceFetch {\n return async (input, init) => {\n const request = input instanceof Request ? input : new Request(input, init);\n try {\n return await fetch(request);\n } catch (error) {\n const name = error instanceof Error ? error.name : undefined;\n // `AbortSignal.timeout` sets a `TimeoutError` reason, `AbortController.abort()`\n // an `AbortError` one. A missing reason is the timeout as well: every SDK request\n // carries one, and no caller passes a signal of its own.\n const reason: unknown = request.signal?.reason;\n const timedOut = reason === undefined || reason === null\n || (typeof reason === 'object' && 'name' in reason && reason.name === 'TimeoutError');\n if (name === 'AbortError' && !timedOut) throw error;\n const target: RequestTarget = {\n host,\n method: request.method,\n path: new URL(request.url).pathname,\n };\n throw name === 'TimeoutError' || name === 'AbortError'\n ? new TimeoutError(target, error)\n : new ConnectionError(target, error);\n }\n };\n}\n\n/** Options for opening a device. */\nexport interface OpenOptions {\n /** Open read-only: skip `session/open`, mint no token, send no session header. */\n readonly?: boolean;\n /** Human-readable holder label shown to other clients and on the front panel. */\n label?: string;\n}\n\n/** @hideconstructor */\nexport class AT1000Base {\n /**\n * IP address or hostname of the AT1000 device\n */\n public readonly host: string;\n /**\n * Device information\n */\n public readonly info: Information;\n /**\n * Session token minted by `open()`, or `undefined` for a read-only or legacy\n * (pre-session) client. Sent as the `X-AT1000-Session` header on every request.\n */\n public readonly token: string | undefined;\n /**\n * `false` only when a pre-session server was detected during an exclusive\n * `open()` (no `/v0/session/*` routes). Read-only opens never probe and report `true`.\n */\n public readonly sessionSupported: boolean;\n\n private fetch: UpFetch;\n /**\n * COM API\n */\n public readonly com: ComApi;\n /**\n * HMI API\n */\n public readonly hmi: HmiApi;\n /**\n * GPIO API\n */\n public readonly gpio: GpioApi;\n /**\n * Management API\n */\n public readonly management: ManagementApi;\n /**\n * Power management API\n */\n public readonly power: PowerApi;\n /**\n * Relays API\n */\n public readonly relays: RelaysApi;\n /**\n * Real-time events API (WebSocket). Call `events.connect()` then `events.on(...)`.\n */\n public readonly events: EventsApi;\n /**\n * Session API. Read the current holder via `session.state()`.\n */\n public readonly session: SessionApi;\n\n constructor(host: string, info: Information, token?: string, sessionSupported = true) {\n this.host = host;\n this.info = info;\n this.token = token;\n this.sessionSupported = sessionSupported;\n\n // 5 s default: every route can queue behind a slower operation already running on\n // the device's container socket, and a request that waits is not a dead device.\n // `management.info()` opts back down to 1 s, since it is the reachability probe.\n this.fetch = up(\n deviceFetch(host),\n () => ({\n baseUrl: `http://${format_host_for_url(this.host)}/v0`,\n timeout: 5000,\n headers: this.token\n ? { 'X-AT1000-Session': this.token }\n : undefined,\n parseRejected: (response, request) => parseApiError(response, request.method),\n }),\n );\n\n this.com = new ComApi(this.fetch);\n this.events = new EventsApi(this.host);\n this.hmi = new HmiApi(this.fetch, this.events);\n this.gpio = new GpioApi(this.fetch, this.info);\n this.management = new ManagementApi(this.fetch, this.host);\n this.power = new PowerApi(this.fetch, this.info);\n this.relays = new RelaysApi(this.fetch);\n this.session = new SessionApi(this.fetch);\n }\n\n async reset() {\n await this.com.reset();\n await this.hmi.reset();\n await this.gpio.reset();\n await this.power.reset();\n await this.relays.reset();\n }\n\n protected static async openBase(\n host: string,\n options?: OpenOptions,\n session?: { kind: 'remote' | 'project'; project?: string; defaultLabel?: string },\n ): Promise<AT1000Base> {\n if (!host?.trim()) {\n throw new Error(\n 'No device address provided. Pass a host from findDevices() or findLocalDevice().',\n );\n }\n const base = `http://${format_host_for_url(host)}/v0`;\n\n // Re-fetch info to validate reachability (bare client, no session header yet).\n const bare = up(\n deviceFetch(host),\n () => ({\n baseUrl: base,\n timeout: 1000,\n parseRejected: (response, request) =>\n parseApiError(response, request.method),\n }),\n );\n const info = await new ManagementApi(bare, host).info();\n\n if (options?.readonly) {\n return new AT1000Base(host, info);\n }\n\n const token = await open_session(base, {\n kind: session?.kind ?? 'remote',\n label: options?.label ?? session?.defaultLabel,\n project: session?.project,\n });\n // `undefined` => pre-session server: keep a working, tokenless client.\n return token === undefined\n ? new AT1000Base(host, info, undefined, false)\n : new AT1000Base(host, info, token);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import{ComApi as e,canConfig as t,canFrame as n,i2cConfig as r,rs232Config as i,rs485Config as a,spiConfig as o,uartConfig as s}from"./com.mjs";import{i as c,l,n as u,r as d,s as f,u as p}from"./management-BqW9gGfM.mjs";import{GpioApi as m,analogInputConfiguration as ee,analogOutputConfiguration as h,analogState as g,digitalInputConfiguration as _,digitalOutputConfiguration as v,digitalState as y,openDrainConfiguration as b,syncState as x}from"./gpio.mjs";import{HmiApi as S,color as C,internalKnobEvent as w,playbackConfiguration as T,progress as E,promptState as D,text as O}from"./hmi.mjs";import{PowerApi as k,dutState as A,usbState as j}from"./power.mjs";import{RelaysApi as M,relayState as N,states as P}from"./relays.mjs";import{up as F}from"up-fetch";import*as I from"valibot";const L=I.object({active:I.boolean(),kind:I.optional(I.picklist([`remote`,`project`])),label:I.optional(I.string()),project:I.optional(I.string()),since:I.optional(I.number())}),R=I.object({token:I.string()});async function z(e,t){let n=await $(new URL(e).host)(`${e}/session/open`,{method:`POST`,body:JSON.stringify(t),headers:{"content-type":`application/json`},signal:AbortSignal.timeout(3e4)});if(n.status!==404&&n.status!==405)return n.ok||await X(n,`POST`),I.parse(R,await n.json()).token}var B=class{fetch;constructor(e){this.fetch=e}state(){return this.fetch(`/session/state`,{schema:L})}};const V=I.array(I.pipe(I.number(),I.integer(),I.minValue(0),I.maxValue(255))),H={"com.can_config":t,"com.rs232_config":i,"com.rs485_config":a,"com.spi_config":o,"com.i2c_config":r,"com.uart_config":s,"com.can_rx":I.array(n),"com.uart_rx":V,"com.rs232_rx":V,"com.rs485_rx":V,"gpio.state":I.union([y,g]),"gpio.config":I.union([ee,h,_,v,b]),"gpio.sync":x,"hmi.text":O,"hmi.colors":C,"hmi.progress":I.optional(E),"hmi.play":T,"hmi.knob":w,"hmi.prompt":D,"management.datetime":I.object({epoch_seconds:I.number()}),"management.calibration":d,"management.calibration_state":c,"management.project":f,"management.update_status":l,"power.dut":A,"power.usb":j,"relays.state":N,"relays.states":P,"session.state":L},U=[`com.reset`,`gpio.reset`,`hmi.clear`,`hmi.reset`,`power.reset`,`relays.reset`],W=I.object({type:I.string(),id:I.optional(I.pipe(I.number(),I.integer())),ts:I.number(),data:I.optional(I.unknown())}),G=5e3,K=(...e)=>console.debug(`[at1000:events]`,...e);var q=class{host;socket;listeners=new Map;stateListeners=new Set;shouldReconnect=!1;reconnectDelayMs=500;reconnectTimer;_connected=!1;connectPromise;constructor(e){this.host=e}get connected(){return this._connected}async connect(e){if(typeof WebSocket>`u`)throw Error(`WebSocket is not available in this runtime (Node >= 22, Bun, or a browser is required)`);if(!this._connected){this.connectPromise??=this.openSocket(e?.timeout??G);try{await this.connectPromise}finally{this.connectPromise=void 0}}}close(){this.shouldReconnect=!1,this.reconnectTimer!==void 0&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0);let e=this.socket;this.socket=void 0,e!==void 0&&e.close(),this.setConnected(!1)}on(e,t){let n=this.listeners.get(e);return n===void 0&&(n=new Set,this.listeners.set(e,n)),n.add(t),()=>{let n=this.listeners.get(e);n!==void 0&&(n.delete(t),n.size===0&&this.listeners.delete(e))}}onStateChange(e){return this.stateListeners.add(e),()=>{this.stateListeners.delete(e)}}openSocket(e){let{promise:t,resolve:n,reject:r}=Promise.withResolvers(),i=`ws://${p(this.host)}/v0/events`,a=new WebSocket(i);this.socket=a;let o=!1,s=setTimeout(()=>{o||(o=!0,a.close(),r(Error(`Timed out connecting to ${i}`)))},e);return a.onopen=()=>{this.socket!==a||o||(o=!0,clearTimeout(s),this.shouldReconnect=!0,this.reconnectDelayMs=500,this.setConnected(!0),K(`open`,i),n())},a.onmessage=e=>{this.socket===a&&this.handleMessage(e.data)},a.onerror=()=>{K(`error`,i),this.socket===a&&!o&&(o=!0,clearTimeout(s),r(Error(`Failed to connect to ${i}`)))},a.onclose=()=>{if(this.socket===a){if(K(`close`,i),clearTimeout(s),this.setConnected(!1),!o){o=!0,r(Error(`Connection to ${i} closed before opening`));return}this.shouldReconnect&&this.scheduleReconnect()}},t}scheduleReconnect(){if(!this.shouldReconnect||this.reconnectTimer!==void 0)return;let e=this.reconnectDelayMs;K(`reconnecting in`,e,`ms`),this.reconnectDelayMs=Math.min(this.reconnectDelayMs*2,8e3),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=void 0,this.shouldReconnect&&this.openSocket(G).catch(()=>{})},e)}handleMessage(e){if(typeof e!=`string`){K(`drop: non-string frame`);return}let t;try{t=JSON.parse(e)}catch{K(`drop: invalid JSON`,e);return}K(`recv`,t);let n=I.safeParse(W,t);if(!n.success){K(`drop: malformed envelope`,n.issues);return}let{type:r,id:i,ts:a}=n.output;if(r in H){let e=H[r],t=I.safeParse(e,n.output.data);if(!t.success){K(`drop: payload validation failed for`,r,t.issues);return}let o={type:r,id:i,ts:a,data:t.output};K(`dispatch`,r),this.emitTo(r,o),this.emitTo(`*`,o);return}if(U.includes(r)){let e={type:r,id:i,ts:a};K(`dispatch (dataless)`,r),this.emitTo(r,e),this.emitTo(`*`,e);return}K(`drop: unknown type`,r),this.emitTo(`*`,{type:r,id:i,ts:a,data:n.output.data})}emitTo(e,t){let n=this.listeners.get(e);if(n!==void 0)for(let e of n)try{e(t)}catch{}}setConnected(e){if(this._connected!==e){this._connected=e;for(let t of this.stateListeners)try{t(e)}catch{}}}},J=class extends Error{code;detail;suggestion;path;constructor(e){let t=e.suggestion.replace(/\n/g,`
2
+ `);super(`${e.message} (${e.code})\n Suggestion: ${t}\n Path: ${e.path}`),this.code=e.code,this.detail=e.message,this.suggestion=e.suggestion,this.path=e.path}};function Y(e){if(typeof e!=`object`||!e)return!1;let t=e;return[`code`,`message`,`suggestion`,`path`].every(e=>typeof t[e]==`string`)}async function X(e,t){let n,r=e.headers.get(`content-type`);try{n=r?.includes(`application/json`)?await e.json():await e.text()}catch{n=`Failed to parse response body`}throw Y(n)?new J(n):Error(`API request to ${t} ${e.url} failed with status ${e.status}: ${typeof n==`string`?n:JSON.stringify(n)}`)}var Z=class extends Error{host;method;path;constructor(e,t){super(`The AT1000 took too long to respond.`,{cause:t}),this.name=`TimeoutError`,this.host=e.host,this.method=e.method,this.path=e.path}},Q=class extends Error{host;method;path;constructor(e,t){super(`Cannot reach the AT1000. Check that it is powered on and on the same network.`,{cause:t}),this.name=`ConnectionError`,this.host=e.host,this.method=e.method,this.path=e.path}};function $(e){return async(t,n)=>{let r=t instanceof Request?t:new Request(t,n);try{return await fetch(r)}catch(t){let n=t instanceof Error?t.name:void 0,i=r.signal?.reason,a=i==null||typeof i==`object`&&`name`in i&&i.name===`TimeoutError`;if(n===`AbortError`&&!a)throw t;let o={host:e,method:r.method,path:new URL(r.url).pathname};throw n===`TimeoutError`||n===`AbortError`?new Z(o,t):new Q(o,t)}}}var te=class t{host;info;token;sessionSupported;fetch;com;hmi;gpio;management;power;relays;events;session;constructor(t,n,r,i=!0){this.host=t,this.info=n,this.token=r,this.sessionSupported=i,this.fetch=F($(t),()=>({baseUrl:`http://${p(this.host)}/v0`,timeout:5e3,headers:this.token?{"X-AT1000-Session":this.token}:void 0,parseRejected:(e,t)=>X(e,t.method)})),this.com=new e(this.fetch),this.events=new q(this.host),this.hmi=new S(this.fetch,this.events),this.gpio=new m(this.fetch,this.info),this.management=new u(this.fetch,this.host),this.power=new k(this.fetch,this.info),this.relays=new M(this.fetch),this.session=new B(this.fetch)}async reset(){await this.com.reset(),await this.hmi.reset(),await this.gpio.reset(),await this.power.reset(),await this.relays.reset()}static async openBase(e,n,r){if(!e?.trim())throw Error(`No device address provided. Pass a host from findDevices() or findLocalDevice().`);let i=`http://${p(e)}/v0`,a=F($(e),()=>({baseUrl:i,timeout:1e3,parseRejected:(e,t)=>X(e,t.method)})),o=await new u(a,e).info();if(n?.readonly)return new t(e,o);let s=await z(i,{kind:r?.kind??`remote`,label:n?.label??r?.defaultLabel,project:r?.project});return s===void 0?new t(e,o,void 0,!1):new t(e,o,s)}};export{$ as a,B as c,Z as i,z as l,J as n,X as o,Q as r,q as s,te as t,L as u};
3
+ //# sourceMappingURL=at1000-C9erttrI.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"at1000-C9erttrI.mjs","names":[],"sources":["../src/api/session.ts","../src/api/events.ts","../src/at1000.ts"],"sourcesContent":["import type { UpFetch } from 'up-fetch';\nimport * as v from 'valibot';\n\nimport { deviceFetch, parseApiError } from '../at1000';\n\nexport const sessionState = v.object({\n active: v.boolean(),\n kind: v.optional(v.picklist(['remote', 'project'])),\n label: v.optional(v.string()),\n project: v.optional(v.string()),\n since: v.optional(v.number()),\n});\nexport type SessionState = v.InferOutput<typeof sessionState>;\n\n/** Body for `POST /v0/session/open`. */\nexport type SessionOpenBody = { kind: 'remote' | 'project'; label?: string; project?: string };\n\nconst openResponse = v.object({ token: v.string() });\n\n/**\n * Opens an exclusive session on the device (last-open-wins). Returns the minted\n * session token, or `undefined` when the server predates the session model.\n *\n * Deliberately uses a direct `fetch` (not the shared up-fetch client) so the\n * response status is inspectable before any structured-error handler throws:\n * pre-session servers answer the unmatched route with 404/405, which we degrade\n * to `undefined` rather than surface as a failure. A 30 s timeout (not the SDK\n * default 5 s) covers a remote-kind open synchronously stopping any running\n * project container; the host is already known-alive from the preceding info\n * probe, so the long timeout never hangs on dead hosts.\n */\nexport async function open_session(\n baseUrl: string,\n body: SessionOpenBody,\n): Promise<string | undefined> {\n const response = await deviceFetch(new URL(baseUrl).host)(`${baseUrl}/session/open`, {\n method: 'POST',\n body: JSON.stringify(body),\n headers: { 'content-type': 'application/json' },\n signal: AbortSignal.timeout(30_000),\n });\n\n if (response.status === 404 || response.status === 405) {\n return undefined; // legacy server without the session model\n }\n if (!response.ok) {\n await parseApiError(response, 'POST');\n }\n\n return v.parse(openResponse, await response.json()).token;\n}\n\n/** @hideconstructor */\nexport class SessionApi {\n constructor(private readonly fetch: UpFetch) {}\n\n /**\n * Reads the current device session state.\n * @returns The active holder (kind/label/project/since) or `{ active: false }`.\n */\n state(): Promise<SessionState> {\n return this.fetch('/session/state', { schema: sessionState });\n }\n}\n","import * as v from 'valibot';\n\nimport { format_host_for_url } from '../host';\nimport {\n canConfig,\n canFrame,\n i2cConfig,\n rs232Config,\n rs485Config,\n spiConfig,\n uartConfig,\n} from './com';\nimport {\n analogInputConfiguration,\n analogOutputConfiguration,\n analogState,\n digitalInputConfiguration,\n digitalOutputConfiguration,\n digitalState,\n openDrainConfiguration,\n syncState,\n} from './gpio';\nimport {\n color,\n internalKnobEvent,\n playbackConfiguration,\n progress,\n promptState,\n text,\n} from './hmi';\nimport { calibrationData, calibrationState, projectEvent, updateStatus } from './management';\nimport { dutState, usbState } from './power';\nimport { relayState, states } from './relays';\nimport { sessionState } from './session';\n\n// Receive-side serial bytes. Distinct from com's `serialData`, whose string->bytes\n// transform is for request bodies; events deliver raw byte arrays.\nconst rxBytes = v.array(v.pipe(v.number(), v.integer(), v.minValue(0), v.maxValue(255)));\n\n// Schema for the `data` payload of each event, keyed by the wire `type` literal.\nconst eventDataSchemas = {\n 'com.can_config': canConfig,\n 'com.rs232_config': rs232Config,\n 'com.rs485_config': rs485Config,\n 'com.spi_config': spiConfig,\n 'com.i2c_config': i2cConfig,\n 'com.uart_config': uartConfig,\n 'com.can_rx': v.array(canFrame),\n 'com.uart_rx': rxBytes,\n 'com.rs232_rx': rxBytes,\n 'com.rs485_rx': rxBytes,\n 'gpio.state': v.union([digitalState, analogState]),\n 'gpio.config': v.union([\n analogInputConfiguration,\n analogOutputConfiguration,\n digitalInputConfiguration,\n digitalOutputConfiguration,\n openDrainConfiguration,\n ]),\n // Mirrors the glz::meta of gpio::sync_hold_state_t (apps/server/src/api/resources/gpio.hpp).\n 'gpio.sync': syncState,\n 'hmi.text': text,\n 'hmi.colors': color,\n // `data` absent (progress hidden) is accepted by v.optional.\n 'hmi.progress': v.optional(progress),\n 'hmi.play': playbackConfiguration,\n 'hmi.knob': internalKnobEvent,\n 'hmi.prompt': promptState,\n 'management.datetime': v.object({ epoch_seconds: v.number() }),\n 'management.calibration': calibrationData,\n 'management.calibration_state': calibrationState,\n 'management.project': projectEvent,\n 'management.update_status': updateStatus,\n 'power.dut': dutState,\n 'power.usb': usbState,\n 'relays.state': relayState,\n 'relays.states': states,\n 'session.state': sessionState,\n} as const;\n\nconst dataLessEvents = [\n 'com.reset',\n 'gpio.reset',\n 'hmi.clear',\n 'hmi.reset',\n 'power.reset',\n 'relays.reset',\n] as const;\n\nconst envelope = v.object({\n type: v.string(),\n id: v.optional(v.pipe(v.number(), v.integer())),\n ts: v.number(),\n data: v.optional(v.unknown()),\n});\n\ntype EventDataSchemas = typeof eventDataSchemas;\n\n/** Every event `type` literal the server can emit. */\nexport type EventType = keyof EventDataSchemas | (typeof dataLessEvents)[number];\n\ntype PayloadEvent = {\n [K in keyof EventDataSchemas]: {\n type: K;\n id?: number;\n ts: number;\n data: v.InferOutput<EventDataSchemas[K]>;\n };\n}[keyof EventDataSchemas];\n\ntype DataLessEvent = { type: (typeof dataLessEvents)[number]; id?: number; ts: number };\n\n/** A fully-typed device event for a known `type`. */\nexport type DeviceEvent = PayloadEvent | DataLessEvent;\n\n/**\n * Shape delivered to `'*'` listeners. Forward-compatible: a newer server may emit\n * a `type` this SDK does not model, surfaced here with an untyped `data`.\n */\nexport type AnyDeviceEvent = DeviceEvent | {\n type: string;\n id?: number;\n ts: number;\n data?: unknown;\n};\n\ntype EventListener = (event: AnyDeviceEvent) => void;\ntype StateListener = (connected: boolean) => void;\n\nconst reconnectBaseDelayMs = 500;\nconst reconnectMaxDelayMs = 8_000;\nconst defaultConnectTimeoutMs = 5_000;\n\nconst debugLog = (...args: unknown[]): void => console.debug('[at1000:events]', ...args);\n\n/** @hideconstructor */\nexport class EventsApi {\n private socket: WebSocket | undefined;\n private readonly listeners = new Map<string, Set<EventListener>>();\n private readonly stateListeners = new Set<StateListener>();\n private shouldReconnect = false;\n private reconnectDelayMs = reconnectBaseDelayMs;\n private reconnectTimer: ReturnType<typeof setTimeout> | undefined;\n private _connected = false;\n private connectPromise: Promise<void> | undefined;\n\n constructor(private readonly host: string) {}\n\n /** Whether the WebSocket is currently open. */\n get connected(): boolean {\n return this._connected;\n }\n\n /**\n * Open the event stream. Resolves once the socket is open, rejects on failure or\n * after `timeout` ms. A no-op while already connected. Auto-reconnect arms only\n * after the first successful open, so a failed initial `connect()` (e.g. old\n * firmware without `/v0/events`) rejects without scheduling retries.\n */\n async connect(options?: { timeout?: number }): Promise<void> {\n if (typeof WebSocket === 'undefined') {\n throw new Error(\n 'WebSocket is not available in this runtime (Node >= 22, Bun, or a browser is required)',\n );\n }\n if (this._connected) {\n return;\n }\n // Coalesce concurrent opens: the layout holder-banner effect and the\n // projects page both call connect() on the shared per-client socket. Without\n // this a second caller starts a second openSocket() that orphans the first\n // (leaked WS + a connect() that never settles). Reuse the in-flight open.\n this.connectPromise ??= this.openSocket(options?.timeout ?? defaultConnectTimeoutMs);\n try {\n await this.connectPromise;\n } finally {\n this.connectPromise = undefined;\n }\n }\n\n /** Close the stream and cancel any pending reconnect. */\n close(): void {\n this.shouldReconnect = false;\n if (this.reconnectTimer !== undefined) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = undefined;\n }\n const socket = this.socket;\n this.socket = undefined;\n if (socket !== undefined) {\n socket.close();\n }\n this.setConnected(false);\n }\n\n /**\n * Subscribe to an event `type`, or `'*'` for every event. Returns an unsubscribe\n * function. Listeners survive reconnects and are isolated: a throwing listener\n * does not break dispatch to the others.\n */\n on<T extends EventType>(\n type: T,\n listener: (event: Extract<DeviceEvent, { type: T }>) => void,\n ): () => void;\n on(type: '*', listener: (event: AnyDeviceEvent) => void): () => void;\n on(type: string, listener: (event: any) => void): () => void {\n let set = this.listeners.get(type);\n if (set === undefined) {\n set = new Set();\n this.listeners.set(type, set);\n }\n set.add(listener);\n return () => {\n const current = this.listeners.get(type);\n if (current === undefined) {\n return;\n }\n current.delete(listener);\n if (current.size === 0) {\n this.listeners.delete(type);\n }\n };\n }\n\n /** Subscribe to connection state changes. Returns an unsubscribe function. */\n onStateChange(listener: StateListener): () => void {\n this.stateListeners.add(listener);\n return () => {\n this.stateListeners.delete(listener);\n };\n }\n\n private openSocket(timeoutMs: number): Promise<void> {\n const { promise, resolve, reject } = Promise.withResolvers<void>();\n const url = `ws://${format_host_for_url(this.host)}/v0/events`;\n const socket = new WebSocket(url);\n this.socket = socket;\n\n let settled = false;\n const timer = setTimeout(() => {\n if (!settled) {\n settled = true;\n socket.close();\n reject(new Error(`Timed out connecting to ${url}`));\n }\n }, timeoutMs);\n\n socket.onopen = () => {\n if (this.socket !== socket || settled) {\n return;\n }\n settled = true;\n clearTimeout(timer);\n this.shouldReconnect = true;\n this.reconnectDelayMs = reconnectBaseDelayMs;\n this.setConnected(true);\n debugLog('open', url);\n resolve();\n };\n\n socket.onmessage = (event) => {\n if (this.socket === socket) {\n this.handleMessage(event.data);\n }\n };\n\n socket.onerror = () => {\n debugLog('error', url);\n if (this.socket === socket && !settled) {\n settled = true;\n clearTimeout(timer);\n reject(new Error(`Failed to connect to ${url}`));\n }\n };\n\n socket.onclose = () => {\n if (this.socket !== socket) {\n return;\n }\n debugLog('close', url);\n clearTimeout(timer);\n this.setConnected(false);\n if (!settled) {\n settled = true;\n reject(new Error(`Connection to ${url} closed before opening`));\n return;\n }\n if (this.shouldReconnect) {\n this.scheduleReconnect();\n }\n };\n\n return promise;\n }\n\n private scheduleReconnect(): void {\n if (!this.shouldReconnect || this.reconnectTimer !== undefined) {\n return;\n }\n const delay = this.reconnectDelayMs;\n debugLog('reconnecting in', delay, 'ms');\n this.reconnectDelayMs = Math.min(this.reconnectDelayMs * 2, reconnectMaxDelayMs);\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = undefined;\n if (!this.shouldReconnect) {\n return;\n }\n // A failed attempt rejects this promise; onclose then schedules the next retry.\n this.openSocket(defaultConnectTimeoutMs).catch(() => {});\n }, delay);\n }\n\n private handleMessage(data: unknown): void {\n if (typeof data !== 'string') {\n debugLog('drop: non-string frame');\n return;\n }\n let raw: unknown;\n try {\n raw = JSON.parse(data);\n } catch {\n debugLog('drop: invalid JSON', data);\n return;\n }\n\n debugLog('recv', raw);\n const parsed = v.safeParse(envelope, raw);\n if (!parsed.success) {\n debugLog('drop: malformed envelope', parsed.issues);\n return;\n }\n const { type, id, ts } = parsed.output;\n\n if (type in eventDataSchemas) {\n const schema = eventDataSchemas[type as keyof EventDataSchemas] as v.GenericSchema;\n const payload = v.safeParse(schema, parsed.output.data);\n if (!payload.success) {\n debugLog('drop: payload validation failed for', type, payload.issues);\n return;\n }\n const event: AnyDeviceEvent = { type, id, ts, data: payload.output };\n debugLog('dispatch', type);\n this.emitTo(type, event);\n this.emitTo('*', event);\n return;\n }\n\n if ((dataLessEvents as readonly string[]).includes(type)) {\n const event: AnyDeviceEvent = { type, id, ts };\n debugLog('dispatch (dataless)', type);\n this.emitTo(type, event);\n this.emitTo('*', event);\n return;\n }\n\n // Unknown type: deliver the raw envelope to '*' only (server may be newer).\n debugLog('drop: unknown type', type);\n this.emitTo('*', { type, id, ts, data: parsed.output.data });\n }\n\n private emitTo(key: string, event: AnyDeviceEvent): void {\n const set = this.listeners.get(key);\n if (set === undefined) {\n return;\n }\n for (const listener of set) {\n try {\n listener(event);\n } catch {\n // One throwing listener must not break dispatch to the others.\n }\n }\n }\n\n private setConnected(connected: boolean): void {\n if (this._connected === connected) {\n return;\n }\n this._connected = connected;\n for (const listener of this.stateListeners) {\n try {\n listener(connected);\n } catch {\n // Ignore throwing state listeners.\n }\n }\n }\n}\n","import { type UpFetch, up } from 'up-fetch';\n\nimport { ComApi } from './api/com';\nimport { EventsApi } from './api/events';\nimport { GpioApi } from './api/gpio';\nimport { HmiApi } from './api/hmi';\nimport { type Information, ManagementApi } from './api/management';\nimport { PowerApi } from './api/power';\nimport { RelaysApi } from './api/relays';\nimport { SessionApi, open_session } from './api/session';\nimport { format_host_for_url } from './host';\n\n/** Structured error thrown for AT1000 API responses carrying `{ code, message, suggestion, path }`. */\nexport class ApiError extends Error {\n readonly code: string;\n /** Raw human-readable server message (without code/suggestion/path). */\n readonly detail: string;\n readonly suggestion: string;\n readonly path: string;\n\n constructor(body: { code: string; message: string; suggestion: string; path: string }) {\n const formattedSuggestion = body.suggestion.replace(/\\n/g, '\\n ');\n super(\n `${body.message} (${body.code})\\n Suggestion: ${formattedSuggestion}\\n Path: ${body.path}`,\n );\n this.code = body.code;\n this.detail = body.message;\n this.suggestion = body.suggestion;\n this.path = body.path;\n }\n}\n\n/** Type guard for the AT1000 structured error body. */\nfunction isApiErrorBody(\n value: unknown,\n): value is { code: string; message: string; suggestion: string; path: string } {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const record = value as Record<string, unknown>;\n return ['code', 'message', 'suggestion', 'path'].every((key) => typeof record[key] === 'string');\n}\n\n/**\n * Shared rejection handler for every AT1000 up-fetch client. Parses a structured\n * `{ code, message, suggestion, path }` body into an {@link ApiError}, otherwise\n * throws a generic error carrying the HTTP status. Written once so the per-client\n * factory and `open_session` share one code path.\n */\nexport async function parseApiError(response: Response, method: string): Promise<never> {\n let errorData: unknown;\n const contentType = response.headers.get('content-type');\n try {\n errorData = contentType?.includes('application/json')\n ? await response.json()\n : await response.text();\n } catch {\n errorData = 'Failed to parse response body';\n }\n\n if (isApiErrorBody(errorData)) {\n throw new ApiError(errorData);\n }\n\n throw new Error(\n `API request to ${method} ${response.url} failed with status ${response.status}: ${\n typeof errorData === 'string' ? errorData : JSON.stringify(errorData)\n }`,\n );\n}\n\n/** Where a failed request was headed. Attached to a transport error for logs and reports. */\nexport interface RequestTarget {\n /** Device authority, e.g. `192.168.1.23:8080`. */\n host: string;\n method: string;\n /** Request path, e.g. `/v0/management/projects`. */\n path: string;\n}\n\n/** Thrown when the device did not answer a request before the client timeout expired. */\nexport class TimeoutError extends Error {\n readonly host: string;\n readonly method: string;\n readonly path: string;\n\n constructor(target: RequestTarget, cause: unknown) {\n super('The AT1000 took too long to respond.', { cause });\n this.name = 'TimeoutError';\n this.host = target.host;\n this.method = target.method;\n this.path = target.path;\n }\n}\n\n/** Thrown when a request never reached the device: no route, refused socket, or unresolved name. */\nexport class ConnectionError extends Error {\n readonly host: string;\n readonly method: string;\n readonly path: string;\n\n constructor(target: RequestTarget, cause: unknown) {\n super('Cannot reach the AT1000. Check that it is powered on and on the same network.', {\n cause,\n });\n this.name = 'ConnectionError';\n this.host = target.host;\n this.method = target.method;\n this.path = target.path;\n }\n}\n\n/** The `fetch` shape every AT1000 up-fetch client is built on. */\nexport type DeviceFetch = (input: Request | string | URL, init?: RequestInit) => Promise<Response>;\n\n/**\n * Wraps `fetch` so a transport failure carries a message a user can act on.\n * Raw engine messages do not qualify: WebKit rejects an aborted fetch with\n * \"Fetch is aborted\", Node with \"The operation timed out\", and a refused socket\n * surfaces as \"Failed to fetch\". The original error is kept as `cause`, and a\n * caller-supplied abort is rethrown untouched.\n */\nexport function deviceFetch(host: string): DeviceFetch {\n return async (input, init) => {\n const request = input instanceof Request ? input : new Request(input, init);\n try {\n return await fetch(request);\n } catch (error) {\n const name = error instanceof Error ? error.name : undefined;\n // `AbortSignal.timeout` sets a `TimeoutError` reason, `AbortController.abort()`\n // an `AbortError` one. A missing reason is the timeout as well: every SDK request\n // carries one, and no caller passes a signal of its own.\n const reason: unknown = request.signal?.reason;\n const timedOut = reason === undefined || reason === null\n || (typeof reason === 'object' && 'name' in reason && reason.name === 'TimeoutError');\n if (name === 'AbortError' && !timedOut) throw error;\n const target: RequestTarget = {\n host,\n method: request.method,\n path: new URL(request.url).pathname,\n };\n throw name === 'TimeoutError' || name === 'AbortError'\n ? new TimeoutError(target, error)\n : new ConnectionError(target, error);\n }\n };\n}\n\n/** Options for opening a device. */\nexport interface OpenOptions {\n /** Open read-only: skip `session/open`, mint no token, send no session header. */\n readonly?: boolean;\n /** Human-readable holder label shown to other clients and on the front panel. */\n label?: string;\n}\n\n/** @hideconstructor */\nexport class AT1000Base {\n /**\n * IP address or hostname of the AT1000 device\n */\n public readonly host: string;\n /**\n * Device information\n */\n public readonly info: Information;\n /**\n * Session token minted by `open()`, or `undefined` for a read-only or legacy\n * (pre-session) client. Sent as the `X-AT1000-Session` header on every request.\n */\n public readonly token: string | undefined;\n /**\n * `false` only when a pre-session server was detected during an exclusive\n * `open()` (no `/v0/session/*` routes). Read-only opens never probe and report `true`.\n */\n public readonly sessionSupported: boolean;\n\n private fetch: UpFetch;\n /**\n * COM API\n */\n public readonly com: ComApi;\n /**\n * HMI API\n */\n public readonly hmi: HmiApi;\n /**\n * GPIO API\n */\n public readonly gpio: GpioApi;\n /**\n * Management API\n */\n public readonly management: ManagementApi;\n /**\n * Power management API\n */\n public readonly power: PowerApi;\n /**\n * Relays API\n */\n public readonly relays: RelaysApi;\n /**\n * Real-time events API (WebSocket). Call `events.connect()` then `events.on(...)`.\n */\n public readonly events: EventsApi;\n /**\n * Session API. Read the current holder via `session.state()`.\n */\n public readonly session: SessionApi;\n\n constructor(host: string, info: Information, token?: string, sessionSupported = true) {\n this.host = host;\n this.info = info;\n this.token = token;\n this.sessionSupported = sessionSupported;\n\n // 5 s default: every route can queue behind a slower operation already running on\n // the device's container socket, and a request that waits is not a dead device.\n // `management.info()` opts back down to 1 s, since it is the reachability probe.\n this.fetch = up(\n deviceFetch(host),\n () => ({\n baseUrl: `http://${format_host_for_url(this.host)}/v0`,\n timeout: 5000,\n headers: this.token\n ? { 'X-AT1000-Session': this.token }\n : undefined,\n parseRejected: (response, request) => parseApiError(response, request.method),\n }),\n );\n\n this.com = new ComApi(this.fetch);\n this.events = new EventsApi(this.host);\n this.hmi = new HmiApi(this.fetch, this.events);\n this.gpio = new GpioApi(this.fetch, this.info);\n this.management = new ManagementApi(this.fetch, this.host);\n this.power = new PowerApi(this.fetch, this.info);\n this.relays = new RelaysApi(this.fetch);\n this.session = new SessionApi(this.fetch);\n }\n\n async reset() {\n await this.com.reset();\n await this.hmi.reset();\n await this.gpio.reset();\n await this.power.reset();\n await this.relays.reset();\n }\n\n protected static async openBase(\n host: string,\n options?: OpenOptions,\n session?: { kind: 'remote' | 'project'; project?: string; defaultLabel?: string },\n ): Promise<AT1000Base> {\n if (!host?.trim()) {\n throw new Error(\n 'No device address provided. Pass a host from findDevices() or findLocalDevice().',\n );\n }\n const base = `http://${format_host_for_url(host)}/v0`;\n\n // Re-fetch info to validate reachability (bare client, no session header yet).\n const bare = up(\n deviceFetch(host),\n () => ({\n baseUrl: base,\n timeout: 1000,\n parseRejected: (response, request) =>\n parseApiError(response, request.method),\n }),\n );\n const info = await new ManagementApi(bare, host).info();\n\n if (options?.readonly) {\n return new AT1000Base(host, info);\n }\n\n const token = await open_session(base, {\n kind: session?.kind ?? 'remote',\n label: options?.label ?? session?.defaultLabel,\n project: session?.project,\n });\n // `undefined` => pre-session server: keep a working, tokenless client.\n return token === undefined\n ? new AT1000Base(host, info, undefined, false)\n : new AT1000Base(host, info, token);\n }\n}\n"],"mappings":"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"}
package/build/browser.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./package-BMXokpna.cjs"),t=require("./management-hyOL3JnM.cjs"),n=require("./at1000-CyFs4TGI.cjs");var r=class extends n.t{static apiVersion=e.t;static isStandalone(){return!1}static async open(e,t){return await n.t.openBase(e,t)}};exports.AT1000=r,exports.ApiError=n.n,exports.format_host_for_url=t.u,exports.host_without_port=t.d;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./package-BmyGanoB.cjs"),t=require("./management-CIr8us5E.cjs"),n=require("./at1000-6CjGQqmM.cjs");var r=class extends n.t{static apiVersion=e.t;static isStandalone(){return!1}static async open(e,t){return await n.t.openBase(e,t)}};exports.AT1000=r,exports.ApiError=n.n,exports.ConnectionError=n.r,exports.TimeoutError=n.i,exports.format_host_for_url=t.u,exports.host_without_port=t.d;
2
2
  //# sourceMappingURL=browser.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser.cjs","names":["AT1000Base","version"],"sources":["../src/browser.ts"],"sourcesContent":["import { version } from '../package.json';\nimport { AT1000Base, type OpenOptions } from './at1000';\n\nexport type { ProjectTemplate } from './api/management';\nexport { ApiError } from './at1000';\nexport type { OpenOptions } from './at1000';\nexport { format_host_for_url, host_without_port } from './host';\n\n/** @hideconstructor */\nexport class AT1000 extends AT1000Base {\n static readonly apiVersion = version;\n\n /** The browser never runs on-device; standalone mode is unavailable. */\n static isStandalone(): boolean {\n return false;\n }\n\n /**\n * Opens a device, taking exclusive (remote-kind) access. The browser entry has\n * no in-container detection and no label default; the server falls back to the\n * client IP for the holder label.\n */\n static async open(host: string, options?: OpenOptions): Promise<AT1000> {\n // AT1000 adds no instance state over AT1000Base; the cast is the create-era convention.\n return (await AT1000Base.openBase(host, options)) as AT1000;\n }\n}\n"],"mappings":"uLASA,IAAa,EAAb,cAA4BA,EAAAA,CAAW,CACrC,OAAgB,WAAaC,EAAAA,EAG7B,OAAO,cAAwB,CAC7B,MAAO,EACT,CAOA,aAAa,KAAK,EAAc,EAAwC,CAEtE,OAAQ,MAAMD,EAAAA,EAAW,SAAS,EAAM,CAAO,CACjD,CACF"}
1
+ {"version":3,"file":"browser.cjs","names":["AT1000Base","version"],"sources":["../src/browser.ts"],"sourcesContent":["import { version } from '../package.json';\nimport { AT1000Base, type OpenOptions } from './at1000';\n\nexport type { ProjectTemplate } from './api/management';\nexport { ApiError, ConnectionError, TimeoutError } from './at1000';\nexport type { OpenOptions } from './at1000';\nexport { format_host_for_url, host_without_port } from './host';\n\n/** @hideconstructor */\nexport class AT1000 extends AT1000Base {\n static readonly apiVersion = version;\n\n /** The browser never runs on-device; standalone mode is unavailable. */\n static isStandalone(): boolean {\n return false;\n }\n\n /**\n * Opens a device, taking exclusive (remote-kind) access. The browser entry has\n * no in-container detection and no label default; the server falls back to the\n * client IP for the holder label.\n */\n static async open(host: string, options?: OpenOptions): Promise<AT1000> {\n // AT1000 adds no instance state over AT1000Base; the cast is the create-era convention.\n return (await AT1000Base.openBase(host, options)) as AT1000;\n }\n}\n"],"mappings":"uLASA,IAAa,EAAb,cAA4BA,EAAAA,CAAW,CACrC,OAAgB,WAAaC,EAAAA,EAG7B,OAAO,cAAwB,CAC7B,MAAO,EACT,CAOA,aAAa,KAAK,EAAc,EAAwC,CAEtE,OAAQ,MAAMD,EAAAA,EAAW,SAAS,EAAM,CAAO,CACjD,CACF"}
@@ -1,5 +1,5 @@
1
1
  import { ProjectTemplate } from "./management.cjs";
2
- import { a as OpenOptions, i as ApiError, n as host_without_port, r as AT1000Base, t as format_host_for_url } from "./host-TBM8TT7q.cjs";
2
+ import { a as ConnectionError, i as ApiError, n as host_without_port, o as OpenOptions, r as AT1000Base, s as TimeoutError, t as format_host_for_url } from "./host-C1Zl1RyR.cjs";
3
3
  //#region src/browser.d.ts
4
4
  /** @hideconstructor */
5
5
  export declare class AT1000 extends AT1000Base {
@@ -14,5 +14,5 @@ export declare class AT1000 extends AT1000Base {
14
14
  static open(host: string, options?: OpenOptions): Promise<AT1000>;
15
15
  }
16
16
  //#endregion
17
- export { ApiError, type OpenOptions, type ProjectTemplate, format_host_for_url, host_without_port };
17
+ export { ApiError, ConnectionError, type OpenOptions, type ProjectTemplate, TimeoutError, format_host_for_url, host_without_port };
18
18
  //# sourceMappingURL=browser.d.cts.map
@@ -1,5 +1,5 @@
1
1
  import { ProjectTemplate } from "./management.mjs";
2
- import { a as OpenOptions, i as ApiError, n as host_without_port, r as AT1000Base, t as format_host_for_url } from "./host-BjLI2rbR.mjs";
2
+ import { a as ConnectionError, i as ApiError, n as host_without_port, o as OpenOptions, r as AT1000Base, s as TimeoutError, t as format_host_for_url } from "./host-DCfMkBnq.mjs";
3
3
  //#region src/browser.d.ts
4
4
  /** @hideconstructor */
5
5
  export declare class AT1000 extends AT1000Base {
@@ -14,5 +14,5 @@ export declare class AT1000 extends AT1000Base {
14
14
  static open(host: string, options?: OpenOptions): Promise<AT1000>;
15
15
  }
16
16
  //#endregion
17
- export { ApiError, type OpenOptions, type ProjectTemplate, format_host_for_url, host_without_port };
17
+ export { ApiError, ConnectionError, type OpenOptions, type ProjectTemplate, TimeoutError, format_host_for_url, host_without_port };
18
18
  //# sourceMappingURL=browser.d.mts.map
package/build/browser.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{t as e}from"./package-D7Y23M8-.mjs";import{d as t,u as n}from"./management-CEL202P0.mjs";import{n as r,t as i}from"./at1000-CoQUADtx.mjs";var a=class extends i{static apiVersion=e;static isStandalone(){return!1}static async open(e,t){return await i.openBase(e,t)}};export{a as AT1000,r as ApiError,n as format_host_for_url,t as host_without_port};
1
+ import{t as e}from"./package-BAite5L5.mjs";import{d as t,u as n}from"./management-BqW9gGfM.mjs";import{i as r,n as i,r as a,t as o}from"./at1000-C9erttrI.mjs";var s=class extends o{static apiVersion=e;static isStandalone(){return!1}static async open(e,t){return await o.openBase(e,t)}};export{s as AT1000,i as ApiError,a as ConnectionError,r as TimeoutError,n as format_host_for_url,t as host_without_port};
2
2
  //# sourceMappingURL=browser.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser.mjs","names":[],"sources":["../src/browser.ts"],"sourcesContent":["import { version } from '../package.json';\nimport { AT1000Base, type OpenOptions } from './at1000';\n\nexport type { ProjectTemplate } from './api/management';\nexport { ApiError } from './at1000';\nexport type { OpenOptions } from './at1000';\nexport { format_host_for_url, host_without_port } from './host';\n\n/** @hideconstructor */\nexport class AT1000 extends AT1000Base {\n static readonly apiVersion = version;\n\n /** The browser never runs on-device; standalone mode is unavailable. */\n static isStandalone(): boolean {\n return false;\n }\n\n /**\n * Opens a device, taking exclusive (remote-kind) access. The browser entry has\n * no in-container detection and no label default; the server falls back to the\n * client IP for the holder label.\n */\n static async open(host: string, options?: OpenOptions): Promise<AT1000> {\n // AT1000 adds no instance state over AT1000Base; the cast is the create-era convention.\n return (await AT1000Base.openBase(host, options)) as AT1000;\n }\n}\n"],"mappings":"iJASA,IAAa,EAAb,cAA4B,CAAW,CACrC,OAAgB,WAAa,EAG7B,OAAO,cAAwB,CAC7B,MAAO,EACT,CAOA,aAAa,KAAK,EAAc,EAAwC,CAEtE,OAAQ,MAAM,EAAW,SAAS,EAAM,CAAO,CACjD,CACF"}
1
+ {"version":3,"file":"browser.mjs","names":[],"sources":["../src/browser.ts"],"sourcesContent":["import { version } from '../package.json';\nimport { AT1000Base, type OpenOptions } from './at1000';\n\nexport type { ProjectTemplate } from './api/management';\nexport { ApiError, ConnectionError, TimeoutError } from './at1000';\nexport type { OpenOptions } from './at1000';\nexport { format_host_for_url, host_without_port } from './host';\n\n/** @hideconstructor */\nexport class AT1000 extends AT1000Base {\n static readonly apiVersion = version;\n\n /** The browser never runs on-device; standalone mode is unavailable. */\n static isStandalone(): boolean {\n return false;\n }\n\n /**\n * Opens a device, taking exclusive (remote-kind) access. The browser entry has\n * no in-container detection and no label default; the server falls back to the\n * client IP for the holder label.\n */\n static async open(host: string, options?: OpenOptions): Promise<AT1000> {\n // AT1000 adds no instance state over AT1000Base; the cast is the create-era convention.\n return (await AT1000Base.openBase(host, options)) as AT1000;\n }\n}\n"],"mappings":"+JASA,IAAa,EAAb,cAA4B,CAAW,CACrC,OAAgB,WAAa,EAG7B,OAAO,cAAwB,CAC7B,MAAO,EACT,CAOA,aAAa,KAAK,EAAc,EAAwC,CAEtE,OAAQ,MAAM,EAAW,SAAS,EAAM,CAAO,CACjD,CACF"}
@@ -111,6 +111,15 @@ declare const eventDataSchemas: {
111
111
  readonly button_held: v.BooleanSchema<undefined>;
112
112
  readonly rotation_delta: v.NumberSchema<undefined>;
113
113
  }, undefined>;
114
+ readonly 'hmi.prompt': v.ObjectSchema<{
115
+ readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
116
+ readonly status: v.PicklistSchema<["pending", "selected", "cancelled", "timed_out"], undefined>;
117
+ readonly selection: v.ObjectSchema<{
118
+ readonly label: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MaxBytesAction<string, 64, undefined>, v.RegexAction<string, "Choice labels must not be blank">, v.CheckAction<string, "Choice labels must not contain control characters">]>;
119
+ readonly index: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>, v.MaxValueAction<number, 31, undefined>]>;
120
+ }, undefined>;
121
+ readonly reason: v.OptionalSchema<v.PicklistSchema<["client_cancelled", "session_ended", "panel_exit", "display_replaced"], undefined>, undefined>;
122
+ }, undefined>;
114
123
  readonly 'management.datetime': v.ObjectSchema<{
115
124
  readonly epoch_seconds: v.NumberSchema<undefined>;
116
125
  }, undefined>;
@@ -357,4 +366,4 @@ declare class EventsApi {
357
366
  }
358
367
  //#endregion
359
368
  export { EventsApi as i, DeviceEvent as n, EventType as r, AnyDeviceEvent as t };
360
- //# sourceMappingURL=events-RRtuVpdW.d.cts.map
369
+ //# sourceMappingURL=events-BVDWI2tr.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events-BVDWI2tr.d.cts","names":[],"sources":["../src/api/events.ts"],"mappings":";;cAwCM;WACJ,kBAAgB,EAAA;;;;;;;;;;WAChB,oBAAkB,EAAA;;;;;;;WAClB,oBAAkB,EAAA;;;;;;;;WAClB,kBAAgB,EAAA;;;;;;WAChB,kBAAgB,EAAA;;;;;WAChB,mBAAiB,EAAA;;;;;;;;WACjB,cAAY,EAAA,YAAA,EAAA;;;;;;;;WACZ,eAAa,EAAA,YAAA,EAAA,yBAAA,EAAA,yBAAA,EAAA,kCAAA,EAAA,sCAAA,EAAA;WACb,gBAAc,EAAA,YAAA,EAAA,yBAAA,EAAA,yBAAA,EAAA,kCAAA,EAAA,sCAAA,EAAA;WACd,gBAAc,EAAA,YAAA,EAAA,yBAAA,EAAA,yBAAA,EAAA,kCAAA,EAAA,sCAAA,EAAA;WACd,cAAY,EAAA,aAAA,EAAA;;;;;WACZ,eAAa,EAAA,aAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;WAQb,aAAW,EAAA;;;WACX,YAAU,EAAA;;;WACV,cAAY,EAAA;;;;;WAEZ,gBAAc,EAAA,eAAA,EAAA;;;WACd,YAAU,EAAA;;;;WACV,YAAU,EAAA;;;;;WACV,cAAY,EAAA;;;;;;;;;WACZ,uBAAqB,EAAA;;;WACrB,0BAAwB,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WACxB,gCAA8B,EAAA;;;;;;WAC9B,sBAAoB,EAAA,yBAAA,EAAA;;;;;;;;;;;;;WACpB,4BAA0B,EAAA;;;;;;;WAC1B,aAAW,EAAA;;;;;;WACX,aAAW,KAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WACX,gBAAc,EAAA;;;WACd,iBAAe,EAAA,YAAA,EAAA;WACf,iBAAe,EAAA;;;;;;;;cAGX;KAgBD,0BAA0B;;KAGnB,kBAAkB,2BAA2B;KAEpD,kBACF,WAAW;EACV,MAAM;EACN;EACA;EACA,MAAM,EAAE,YAAY,iBAAiB;WAEjC;KAEH;EAAkB,cAAc;EAAyB;EAAa;;;KAG/D,cAAc,eAAe;;;;;KAM7B,iBAAiB;EAC3B;EACA;EACA;EACA;;KAIG,iBAAiB;;cAST;mBAUkB;UATrB;mBACS;mBACA;UACT;UACA;UACA;UACA;UACA;EAER,YAA6B;;MAGzB;;;;;;;EAUE,QAAQ;IAAY;MAAqB;;EAsB/C;;;;;;EAmBA,GAAG,UAAU,WACX,MAAM,GACN,WAAW,OAAO,QAAQ;IAAe,MAAM;;EAEjD,GAAG,WAAW,WAAW,OAAO;;EAqBhC,cAAc,UAAU;UAOhB;UA+DA;UAiBA;UAgDA;UAcA"}
@@ -111,6 +111,15 @@ declare const eventDataSchemas: {
111
111
  readonly button_held: v.BooleanSchema<undefined>;
112
112
  readonly rotation_delta: v.NumberSchema<undefined>;
113
113
  }, undefined>;
114
+ readonly 'hmi.prompt': v.ObjectSchema<{
115
+ readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
116
+ readonly status: v.PicklistSchema<["pending", "selected", "cancelled", "timed_out"], undefined>;
117
+ readonly selection: v.ObjectSchema<{
118
+ readonly label: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MaxBytesAction<string, 64, undefined>, v.RegexAction<string, "Choice labels must not be blank">, v.CheckAction<string, "Choice labels must not contain control characters">]>;
119
+ readonly index: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>, v.MaxValueAction<number, 31, undefined>]>;
120
+ }, undefined>;
121
+ readonly reason: v.OptionalSchema<v.PicklistSchema<["client_cancelled", "session_ended", "panel_exit", "display_replaced"], undefined>, undefined>;
122
+ }, undefined>;
114
123
  readonly 'management.datetime': v.ObjectSchema<{
115
124
  readonly epoch_seconds: v.NumberSchema<undefined>;
116
125
  }, undefined>;
@@ -357,4 +366,4 @@ declare class EventsApi {
357
366
  }
358
367
  //#endregion
359
368
  export { EventsApi as i, DeviceEvent as n, EventType as r, AnyDeviceEvent as t };
360
- //# sourceMappingURL=events-RRtuVpdW.d.mts.map
369
+ //# sourceMappingURL=events-BVDWI2tr.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events-BVDWI2tr.d.mts","names":[],"sources":["../src/api/events.ts"],"mappings":";;cAwCM;WACJ,kBAAgB,EAAA;;;;;;;;;;WAChB,oBAAkB,EAAA;;;;;;;WAClB,oBAAkB,EAAA;;;;;;;;WAClB,kBAAgB,EAAA;;;;;;WAChB,kBAAgB,EAAA;;;;;WAChB,mBAAiB,EAAA;;;;;;;;WACjB,cAAY,EAAA,YAAA,EAAA;;;;;;;;WACZ,eAAa,EAAA,YAAA,EAAA,yBAAA,EAAA,yBAAA,EAAA,kCAAA,EAAA,sCAAA,EAAA;WACb,gBAAc,EAAA,YAAA,EAAA,yBAAA,EAAA,yBAAA,EAAA,kCAAA,EAAA,sCAAA,EAAA;WACd,gBAAc,EAAA,YAAA,EAAA,yBAAA,EAAA,yBAAA,EAAA,kCAAA,EAAA,sCAAA,EAAA;WACd,cAAY,EAAA,aAAA,EAAA;;;;;WACZ,eAAa,EAAA,aAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;WAQb,aAAW,EAAA;;;WACX,YAAU,EAAA;;;WACV,cAAY,EAAA;;;;;WAEZ,gBAAc,EAAA,eAAA,EAAA;;;WACd,YAAU,EAAA;;;;WACV,YAAU,EAAA;;;;;WACV,cAAY,EAAA;;;;;;;;;WACZ,uBAAqB,EAAA;;;WACrB,0BAAwB,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WACxB,gCAA8B,EAAA;;;;;;WAC9B,sBAAoB,EAAA,yBAAA,EAAA;;;;;;;;;;;;;WACpB,4BAA0B,EAAA;;;;;;;WAC1B,aAAW,EAAA;;;;;;WACX,aAAW,KAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WACX,gBAAc,EAAA;;;WACd,iBAAe,EAAA,YAAA,EAAA;WACf,iBAAe,EAAA;;;;;;;;cAGX;KAgBD,0BAA0B;;KAGnB,kBAAkB,2BAA2B;KAEpD,kBACF,WAAW;EACV,MAAM;EACN;EACA;EACA,MAAM,EAAE,YAAY,iBAAiB;WAEjC;KAEH;EAAkB,cAAc;EAAyB;EAAa;;;KAG/D,cAAc,eAAe;;;;;KAM7B,iBAAiB;EAC3B;EACA;EACA;EACA;;KAIG,iBAAiB;;cAST;mBAUkB;UATrB;mBACS;mBACA;UACT;UACA;UACA;UACA;UACA;EAER,YAA6B;;MAGzB;;;;;;;EAUE,QAAQ;IAAY;MAAqB;;EAsB/C;;;;;;EAmBA,GAAG,UAAU,WACX,MAAM,GACN,WAAW,OAAO,QAAQ;IAAe,MAAM;;EAEjD,GAAG,WAAW,WAAW,OAAO;;EAqBhC,cAAc,UAAU;UAOhB;UA+DA;UAiBA;UAgDA;UAcA"}
package/build/events.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./at1000-CyFs4TGI.cjs");exports.EventsApi=e.i;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./at1000-6CjGQqmM.cjs");exports.EventsApi=e.s;
@@ -1,2 +1,2 @@
1
- import { i as EventsApi, n as DeviceEvent, r as EventType, t as AnyDeviceEvent } from "./events-RRtuVpdW.cjs";
1
+ import { i as EventsApi, n as DeviceEvent, r as EventType, t as AnyDeviceEvent } from "./events-BVDWI2tr.cjs";
2
2
  export { AnyDeviceEvent, DeviceEvent, EventType, EventsApi };
@@ -1,2 +1,2 @@
1
- import { i as EventsApi, n as DeviceEvent, r as EventType, t as AnyDeviceEvent } from "./events-RRtuVpdW.mjs";
1
+ import { i as EventsApi, n as DeviceEvent, r as EventType, t as AnyDeviceEvent } from "./events-BVDWI2tr.mjs";
2
2
  export { AnyDeviceEvent, DeviceEvent, EventType, EventsApi };
package/build/events.mjs CHANGED
@@ -1 +1 @@
1
- import{i as e}from"./at1000-CoQUADtx.mjs";export{e as EventsApi};
1
+ import{s as e}from"./at1000-C9erttrI.mjs";export{e as EventsApi};