wstp-node 0.4.6 → 0.6.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.
package/README.md CHANGED
@@ -30,6 +30,7 @@ const { WstpSession, WstpReader, setDiagHandler } = require('./build/Release/wst
30
30
  - [`createSubsession(kernelPath?)`](#createsubsessionkernelpath) — spawn an independent parallel kernel session
31
31
  - [`close()`](#close) — gracefully shut down the kernel and free resources
32
32
  - [`isOpen` / `isDialogOpen`](#isopen--isdialogopen) — read-only status flags
33
+ - [Dynamic eval API](#dynamic-eval-api) — register expressions for automatic periodic evaluation
33
34
  5. [`WstpReader` — kernel-pushed side channel](#wstpreader)
34
35
  6. [`setDiagHandler(fn)`](#setdiaghandlerfn)
35
36
  5. [Usage examples](#usage-examples)
@@ -97,7 +98,7 @@ The script automatically locates the WSTP SDK inside the default Wolfram install
97
98
  node test.js
98
99
  ```
99
100
 
100
- Expected last line: `All 36 tests passed.`
101
+ Expected last line: `All 52 tests passed.`
101
102
 
102
103
  A more comprehensive suite (both modes + In/Out + comparison) lives in `tmp/tests_all.js`:
103
104
 
@@ -105,7 +106,7 @@ A more comprehensive suite (both modes + In/Out + comparison) lives in `tmp/test
105
106
  node tmp/tests_all.js
106
107
  ```
107
108
 
108
- Expected last line: `All 56 tests passed.`
109
+ Expected last line: `All 41 tests passed.`
109
110
 
110
111
  ### 5. Quick smoke test
111
112
 
@@ -487,6 +488,35 @@ session.isDialogOpen: boolean // true while inside a Dialog[] subsession
487
488
 
488
489
  ---
489
490
 
491
+ ### Dynamic eval API
492
+
493
+ Register Wolfram Language expressions for automatic periodic evaluation during
494
+ cell computations. A `RunScheduledTask` in the kernel periodically calls
495
+ `Dialog[]`; the C++ layer intercepts each `BEGINDLGPKT`, evaluates all
496
+ registered expressions inline, stores the results, and closes the dialog.
497
+
498
+ | Method | Description |
499
+ |--------|-------------|
500
+ | `registerDynamic(id, expr)` | Register (or upsert) an expression for periodic evaluation |
501
+ | `unregisterDynamic(id)` | Remove one registration by id |
502
+ | `clearDynamicRegistry()` | Remove all registrations and clear results buffer |
503
+ | `getDynamicResults()` | Return `Record<string, DynResult>` and clear buffer |
504
+ | `setDynamicInterval(ms)` | Set the ScheduledTask period (0 = off) |
505
+ | `setDynAutoMode(auto)` | `true` (default) = C++ handles dialogs; `false` = legacy JS path |
506
+ | `dynamicActive` | Read-only: `true` when registry is non-empty and interval > 0 |
507
+
508
+ ```ts
509
+ interface DynResult {
510
+ value: string; // string-form result
511
+ timestamp: number; // Unix timestamp (ms)
512
+ error?: string; // set if evaluation failed
513
+ }
514
+ ```
515
+
516
+ See [API.md](API.md#dynamic-eval-api) for full documentation.
517
+
518
+ ---
519
+
490
520
  ## `WstpReader`
491
521
 
492
522
  A reader that **connects to** a named WSTP link created by the kernel (via `LinkCreate`)
Binary file
package/index.d.ts CHANGED
@@ -77,6 +77,37 @@ export interface EvalOptions {
77
77
  * @param level The nesting depth that just closed.
78
78
  */
79
79
  onDialogEnd?: (level: number) => void;
80
+ /**
81
+ * When `true`, any `BEGINDLGPKT` received during a non-interactive
82
+ * evaluation is automatically closed in C++ without informing the JS
83
+ * layer. Use for `VsCodeRender`, handler-install, and `sub()` calls
84
+ * that must never block on a Dialog[] (prevents Pattern C deadlocks).
85
+ */
86
+ rejectDialog?: boolean;
87
+ }
88
+
89
+ /**
90
+ * One captured Dynamic[] result returned by `getDynamicResults()`.
91
+ */
92
+ export interface DynResult {
93
+ /** String-form result returned by the kernel for this expression. */
94
+ value: string;
95
+ /** Unix timestamp (ms) when this result was evaluated. */
96
+ timestamp: number;
97
+ /** Set if evaluation failed (e.g. timeout, `$Failed`). */
98
+ error?: string;
99
+ }
100
+
101
+ /**
102
+ * Optional options for subWhenIdle().
103
+ */
104
+ export interface SubWhenIdleOptions {
105
+ /**
106
+ * Maximum number of milliseconds to wait in the queue before the
107
+ * returned Promise rejects with a timeout error.
108
+ * Omit or set to 0 for no timeout (wait indefinitely).
109
+ */
110
+ timeout?: number;
80
111
  }
81
112
 
82
113
  /**
@@ -202,6 +233,36 @@ export class WstpSession {
202
233
  */
203
234
  sub(expr: string): Promise<WExpr>;
204
235
 
236
+ /**
237
+ * Queue a background evaluation that runs only when the kernel is truly idle.
238
+ *
239
+ * Unlike `sub()`, which runs *before* any queued `evaluate()` calls,
240
+ * `subWhenIdle()` runs only *after* ALL pending `evaluate()` and `sub()`
241
+ * calls have completed. This makes it safe for background queries
242
+ * (e.g. global-symbol coloring, auto-complete) that must not compete with
243
+ * active cell evaluations.
244
+ *
245
+ * If the kernel is idle at call time the query starts immediately;
246
+ * otherwise it is queued and executes as soon as the kernel becomes
247
+ * fully idle again. Multiple `subWhenIdle()` calls are executed FIFO.
248
+ *
249
+ * If the session is closed while the request is still queued the Promise
250
+ * rejects with `"Session is closed"`.
251
+ *
252
+ * @param expr Wolfram Language expression string to evaluate.
253
+ * @param opts Optional: `{ timeout?: number }` — ms before the Promise
254
+ * rejects with `"subWhenIdle: timeout"` if the kernel has
255
+ * not become idle within that window.
256
+ * @returns Promise that resolves with the WExpr result,
257
+ * identical in shape to the value returned by `sub()`.
258
+ *
259
+ * @example
260
+ * session.subWhenIdle('Names["Global`*"]').then(result => {
261
+ * // safe background query — never races with evaluate()
262
+ * });
263
+ */
264
+ subWhenIdle(expr: string, opts?: SubWhenIdleOptions): Promise<WExpr>;
265
+
205
266
  /**
206
267
  * Launch an independent child kernel as a new WstpSession.
207
268
  *
@@ -220,6 +281,79 @@ export class WstpSession {
220
281
 
221
282
  /** True while the link is open and the kernel is running. */
222
283
  readonly isOpen: boolean;
284
+
285
+ /**
286
+ * The OS process ID of the WolframKernel child process.
287
+ *
288
+ * Useful for external monitoring or force-terminating a stale kernel
289
+ * after a restart. Returns `0` if the PID could not be determined
290
+ * at session-construction time (rare, non-fatal fallback).
291
+ *
292
+ * The PID is captured once during `new WstpSession()` and does not
293
+ * change; it remains set even after `close()` so callers can reference
294
+ * it in cleanup paths.
295
+ */
296
+ readonly kernelPid: number;
297
+
298
+ // ── Dynamic eval API ────────────────────────────────────────────────────
299
+
300
+ /**
301
+ * Register (or update) a Wolfram Language expression to be evaluated
302
+ * automatically every time the kernel enters a Dialog[] interrupt.
303
+ *
304
+ * @param id Unique identifier for this registration (e.g. `"x"`).
305
+ * @param expr Wolfram Language expression string (e.g. `"ToString[x]"`).
306
+ */
307
+ registerDynamic(id: string, expr: string): void;
308
+
309
+ /**
310
+ * Remove a previously registered Dynamic expression by id.
311
+ * Has no effect if the id was never registered.
312
+ */
313
+ unregisterDynamic(id: string): void;
314
+
315
+ /** Remove all registered Dynamic expressions. */
316
+ clearDynamicRegistry(): void;
317
+
318
+ /**
319
+ * Consume and return all results accumulated since the last call.
320
+ *
321
+ * Each key is a registration `id`; each value is the most recent
322
+ * `DynResult` for that expression. Calling this clears the internal
323
+ * buffer so subsequent calls return only new results.
324
+ *
325
+ * @returns A plain object mapping id → DynResult.
326
+ */
327
+ getDynamicResults(): Record<string, DynResult>;
328
+
329
+ /**
330
+ * Set the auto-interrupt period (in ms) for the Dynamic timer thread.
331
+ *
332
+ * The background thread sends `WSInterruptMessage` to the kernel every
333
+ * `ms` milliseconds while an evaluation is in progress and the registry
334
+ * is non-empty. Set to `0` to disable.
335
+ *
336
+ * @param ms Interval in milliseconds (0 = off).
337
+ */
338
+ setDynamicInterval(ms: number): void;
339
+
340
+ /**
341
+ * Switch the Dialog[] handling mode.
342
+ *
343
+ * - `true` (default): C++ intercepts every `BEGINDLGPKT` and evaluates
344
+ * all registered expressions inline — no JS round-trip required.
345
+ * - `false`: Falls back to the legacy JS callback path
346
+ * (`onDialogBegin` / `dialogEval` / `exitDialog`).
347
+ *
348
+ * @param auto `true` to enable automatic C++ handling, `false` for legacy.
349
+ */
350
+ setDynAutoMode(auto: boolean): void;
351
+
352
+ /**
353
+ * `true` when the Dynamic registry is non-empty **and** the timer
354
+ * interval is greater than zero (i.e. auto-interrupts are active).
355
+ */
356
+ readonly dynamicActive: boolean;
223
357
  }
224
358
 
225
359
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wstp-node",
3
- "version": "0.4.6",
3
+ "version": "0.6.0",
4
4
  "description": "Native Node.js addon for Wolfram/Mathematica WSTP — kernel sessions with evaluation queue, streaming Print/messages, Dialog subsessions, and side-channel WstpReader",
5
5
  "main": "build/Release/wstp.node",
6
6
  "types": "index.d.ts",