@clodocapeo/pulsar-bundle-full 2.0.0 → 3.0.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 +128 -387
- package/dist/spawn.d.ts +14 -5
- package/dist/spawn.d.ts.map +1 -1
- package/dist/spawn.js +232 -45
- package/dist/spawn.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,452 +1,193 @@
|
|
|
1
1
|
# @clodocapeo/pulsar-bundle-full
|
|
2
2
|
|
|
3
|
-
[](./LICENSE)
|
|
5
|
-
[](https://github.com/ZabLaboratory/Pulsar/releases)
|
|
6
|
-
[](https://nodejs.org)
|
|
7
|
-
|
|
8
|
-
The **full** Pulsar bundle: ships `pulsar.exe` + libobs runtime +
|
|
9
|
-
encoders + capture plugins **plus** `obs-browser` (CEF for HTML /
|
|
10
|
-
JS overlays), `obs-text` (native text sources), and `vlc-video`
|
|
11
|
-
(libVLC media playback). Same Node `spawn()` API as
|
|
12
|
-
[`@clodocapeo/pulsar-bundle`](https://www.npmjs.com/package/@clodocapeo/pulsar-bundle).
|
|
13
|
-
|
|
14
|
-
If you don't need browser sources / native text / VLC media, use
|
|
15
|
-
the lighter `@clodocapeo/pulsar-bundle` instead — same API, ~110 MB
|
|
16
|
-
less to download.
|
|
17
|
-
|
|
18
|
-
## Table of contents
|
|
19
|
-
|
|
20
|
-
- [What's in the box](#whats-in-the-box)
|
|
21
|
-
- [Choosing between light and full](#choosing-between-light-and-full)
|
|
22
|
-
- [Install](#install)
|
|
23
|
-
- [Quick start](#quick-start)
|
|
24
|
-
- [Browser / text / VLC sources](#browser--text--vlc-sources)
|
|
25
|
-
- [`spawn()` API](#spawn-api)
|
|
26
|
-
- [Bundling for distribution](#bundling-for-distribution)
|
|
27
|
-
- [CI / offline / mirror](#ci--offline--mirror)
|
|
28
|
-
- [Troubleshooting](#troubleshooting)
|
|
29
|
-
- [Versioning](#versioning)
|
|
30
|
-
- [Compatibility](#compatibility)
|
|
31
|
-
- [Licence](#licence)
|
|
32
|
-
|
|
33
|
-
## What's in the box
|
|
34
|
-
|
|
35
|
-
Everything in the **light** bundle, plus:
|
|
36
|
-
|
|
37
|
-
| Plugin | What it does |
|
|
38
|
-
|---|---|
|
|
39
|
-
| `obs-browser` (Pulsar fork) | HTML / CSS / JS scenes via Chromium Embedded Framework. Real Chromium runtime, GPU-composited, controllable from the host. |
|
|
40
|
-
| `pulsar-browser-page.exe` | The CEF helper executable. Lives next to `libcef.dll` in `obs-plugins/64bit/`. |
|
|
41
|
-
| `obs-text` | Native text sources (`text_gdiplus`). Direct GDI+ rendering — fast, sharp, no DOM overhead. |
|
|
42
|
-
| `text-freetype2` | FreeType-rendered text sources (`text_ft2_source_v2`). Better cross-script support than gdiplus, less Windows-coupled. |
|
|
43
|
-
| `vlc-video` | Local media playback (`vlc_source`) backed by libVLC. Playlists, all the codecs libVLC handles. |
|
|
44
|
-
|
|
45
|
-
### Size
|
|
46
|
-
|
|
47
|
-
| | Compressed | Extracted |
|
|
48
|
-
|---|---|---|
|
|
49
|
-
| Light bundle | ~40 MB | ~100 MB |
|
|
50
|
-
| **Full bundle** | **~150 MB** | **~370 MB** |
|
|
51
|
-
|
|
52
|
-
The ~110 MB inflation is **CEF** — the Chromium runtime obs-browser
|
|
53
|
-
links against. It's what makes browser sources possible.
|
|
54
|
-
|
|
55
|
-
## Choosing between light and full
|
|
56
|
-
|
|
57
|
-
**Use the full bundle when** your scenes need any of:
|
|
58
|
-
|
|
59
|
-
- HTML / CSS / JS overlays composed in a real browser engine
|
|
60
|
-
- Operator-controlled web UIs as broadcast inputs
|
|
61
|
-
- Native text overlays (lower-bargain than rendering text in a browser)
|
|
62
|
-
- Local video file playback via VLC (formats ffmpeg doesn't ship by default)
|
|
63
|
-
- Any obs-studio plugin that depends on the listed plugins
|
|
64
|
-
|
|
65
|
-
**Use [`@clodocapeo/pulsar-bundle`](https://www.npmjs.com/package/@clodocapeo/pulsar-bundle) when** you can express your overlays
|
|
66
|
-
some other way: text rendered in a `Canvas` upstream of `pulsar.exe`,
|
|
67
|
-
overlays drawn into images / video files, scenes composed
|
|
68
|
-
client-side. The lean bundle saves ~110 MB and skips a lot of moving
|
|
69
|
-
parts (CEF subprocess management, GPU process, V8, …).
|
|
70
|
-
|
|
71
|
-
The two packages are **interchangeable from the API point of view**:
|
|
3
|
+
[](https://www.npmjs.com/package/@clodocapeo/pulsar-bundle-full)
|
|
72
4
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
import { spawn } from "@clodocapeo/pulsar-bundle";
|
|
76
|
-
import { spawn } from "@clodocapeo/pulsar-bundle-full";
|
|
77
|
-
```
|
|
5
|
+
The **full Windows x64 Pulsar 3.0.0 runtime**, with CEF/browser, native text,
|
|
6
|
+
VLC module and gated NVIDIA effect module in addition to the light features.
|
|
78
7
|
|
|
79
|
-
|
|
8
|
+
It exposes the same `spawn()` API and matching client as the
|
|
9
|
+
[light bundle](../pulsar-bundle/README.md). That guide is the complete shared
|
|
10
|
+
reference for options, errors, namespace isolation, shutdown, offline installs
|
|
11
|
+
and packaging. This document covers the full variant's additional behavior.
|
|
80
12
|
|
|
81
|
-
## Install
|
|
13
|
+
## Install and first run
|
|
82
14
|
|
|
83
|
-
```
|
|
84
|
-
npm install @clodocapeo/pulsar-bundle-full
|
|
15
|
+
```powershell
|
|
16
|
+
npm install @clodocapeo/pulsar-bundle-full@3.0.0
|
|
85
17
|
```
|
|
86
18
|
|
|
87
|
-
|
|
88
|
-
make `npm install` skip on every other platform.
|
|
89
|
-
|
|
90
|
-
A `postinstall` step downloads
|
|
91
|
-
`pulsar-windows-x64-full-v<VERSION>.zip` from the matching Pulsar
|
|
92
|
-
GitHub Release (~150 MB) and extracts it into
|
|
93
|
-
`node_modules/@clodocapeo/pulsar-bundle-full/binaries/`. Cached: the
|
|
94
|
-
download is skipped if `binaries/.version-stamp` already matches the
|
|
95
|
-
package version.
|
|
96
|
-
|
|
97
|
-
If the download fails (network error, unpublished version, 404), the
|
|
98
|
-
postinstall **soft-fails** with a warning — `npm install` completes,
|
|
99
|
-
the package installs, but `spawn()` will throw a clear error. Same
|
|
100
|
-
behaviour as the light bundle.
|
|
101
|
-
|
|
102
|
-
## Quick start
|
|
103
|
-
|
|
104
|
-
```ts
|
|
19
|
+
```js
|
|
105
20
|
import { spawn } from "@clodocapeo/pulsar-bundle-full";
|
|
106
21
|
|
|
107
|
-
const pulsar = await spawn({
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
console.log(`pulsar booted: libobs ${pulsar.libobsVersion}, ws :${pulsar.port}`);
|
|
116
|
-
|
|
117
|
-
// HTML overlay via browser_source — the headline feature of -full
|
|
118
|
-
await pulsar.client.obs.call("CreateInput", {
|
|
119
|
-
sceneName: "Default",
|
|
120
|
-
inputName: "Overlay",
|
|
121
|
-
inputKind: "browser_source",
|
|
122
|
-
inputSettings: {
|
|
123
|
-
url: "https://example.com/my-overlay",
|
|
124
|
-
width: 1920,
|
|
125
|
-
height: 1080,
|
|
126
|
-
},
|
|
127
|
-
});
|
|
22
|
+
const pulsar = await spawn({ readyTimeoutMs: 60_000 });
|
|
23
|
+
try {
|
|
24
|
+
console.log(await pulsar.client.capabilities.get());
|
|
25
|
+
} finally {
|
|
26
|
+
await pulsar.shutdown();
|
|
27
|
+
}
|
|
28
|
+
```
|
|
128
29
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
});
|
|
134
|
-
await pulsar.client.destinations.start(dest.id);
|
|
30
|
+
Node 18+, ESM and Windows x64 are required. Postinstall downloads
|
|
31
|
+
`pulsar-windows-x64-full-v3.0.0.zip` into `binaries/`.
|
|
32
|
+
A download warning can leave npm installation successful without a usable
|
|
33
|
+
binary; check the artifact before deployment.
|
|
135
34
|
|
|
136
|
-
|
|
35
|
+
## Additional capabilities
|
|
137
36
|
|
|
138
|
-
|
|
139
|
-
|
|
37
|
+
| Family | What is included | What to verify |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| Browser | Pulsar's `browser_source` fork, CEF/helper/resources | Scene URL, rendering path, helper lifetime and actual PGM frames. |
|
|
40
|
+
| Text | GDI+/FreeType native text modules | Registered input kind and installed font. |
|
|
41
|
+
| VLC | `vlc-video` module | Whether libVLC and its codecs are actually available on the host/distribution. |
|
|
42
|
+
| NVIDIA effects | `nv-filters` module | Validated SDK directory/version/models and live capability report. SDK payload is not bundled. |
|
|
140
43
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
[`@clodocapeo/pulsar-client`'s README](https://www.npmjs.com/package/@clodocapeo/pulsar-client)
|
|
144
|
-
— this package re-exports every symbol so a single import gets you
|
|
145
|
-
both the spawn API and the client types.
|
|
44
|
+
Window, display, game and DirectShow capture, WASAPI, encoding, streaming,
|
|
45
|
+
recording, replay and dual-lane control are **not full-only**.
|
|
146
46
|
|
|
147
|
-
|
|
47
|
+
The package does not guarantee NDI, arbitrary OBS plugins, Preview audio/AFV,
|
|
48
|
+
every VLC codec or every GPU. Query actual capability and source registration.
|
|
148
49
|
|
|
149
|
-
|
|
150
|
-
[obs-websocket v5 input kinds](https://github.com/obsproject/obs-websocket/blob/master/docs/generated/protocol.md#getinputkindlist).
|
|
151
|
-
You create them via `pulsar.client.obs.call("CreateInput", {...})`:
|
|
50
|
+
## Browser capture example
|
|
152
51
|
|
|
153
|
-
|
|
52
|
+
With a running `pulsar` handle and your own reachable scene server:
|
|
154
53
|
|
|
155
|
-
```
|
|
156
|
-
await pulsar.client.obs.call("
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
url: "
|
|
54
|
+
```js
|
|
55
|
+
const result = await pulsar.client.obs.call("CallVendorRequest", {
|
|
56
|
+
vendorName: "pulsar-scene",
|
|
57
|
+
requestType: "SetCaptureSource",
|
|
58
|
+
requestData: {
|
|
59
|
+
kind: "browser_source",
|
|
60
|
+
url: "http://127.0.0.1:3000/scene",
|
|
162
61
|
width: 1920,
|
|
163
62
|
height: 1080,
|
|
164
63
|
fps: 60,
|
|
165
|
-
|
|
166
|
-
css: "body { background: transparent; }",
|
|
167
|
-
shutdown: true, // suspend audio when source is hidden
|
|
168
|
-
restart_when_active: false,
|
|
64
|
+
reroute_audio: false,
|
|
169
65
|
},
|
|
170
66
|
});
|
|
171
|
-
|
|
172
|
-
// At runtime, push events / state into the page:
|
|
173
|
-
await pulsar.client.obs.call("CallVendorRequest", {
|
|
174
|
-
vendorName: "obs-browser",
|
|
175
|
-
requestType: "emit_event",
|
|
176
|
-
requestData: {
|
|
177
|
-
event_name: "score_update",
|
|
178
|
-
event_data: { home: 3, away: 2 },
|
|
179
|
-
},
|
|
180
|
-
});
|
|
181
|
-
// In the page: window.addEventListener("obsSourceCustomEvent", ...)
|
|
67
|
+
console.log(result.responseData);
|
|
182
68
|
```
|
|
183
69
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
### Native text sources
|
|
189
|
-
|
|
190
|
-
Two flavours, pick whichever renders better for your locale:
|
|
70
|
+
This uses the **legacy single managed-capture replacement** helper.
|
|
71
|
+
For independent Preview preparation and atomic Takes, use
|
|
72
|
+
[scene-switch v1](../../scripts/contracts/scene_switch_v1/README.md).
|
|
73
|
+
Creating a browser source is not proof that its page renders healthy frames.
|
|
191
74
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
sceneName: "Default",
|
|
196
|
-
inputName: "Title",
|
|
197
|
-
inputKind: "text_gdiplus_v3",
|
|
198
|
-
inputSettings: {
|
|
199
|
-
text: "LIVE",
|
|
200
|
-
font: { face: "Segoe UI", size: 64, flags: 1 },
|
|
201
|
-
color1: 0xFFFFFFFF,
|
|
202
|
-
outline: true,
|
|
203
|
-
outline_color: 0xFF000000,
|
|
204
|
-
},
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
// freetype2: better non-Latin script support
|
|
208
|
-
await pulsar.client.obs.call("CreateInput", {
|
|
209
|
-
sceneName: "Default",
|
|
210
|
-
inputName: "Subtitle",
|
|
211
|
-
inputKind: "text_ft2_source_v2",
|
|
212
|
-
inputSettings: {
|
|
213
|
-
text: "こんにちは",
|
|
214
|
-
font: { face: "Noto Sans CJK JP", size: 48 },
|
|
215
|
-
},
|
|
216
|
-
});
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
### VLC media source (`vlc_source`)
|
|
75
|
+
The managed source pins webpage control to None. The page is content, not
|
|
76
|
+
authorized broadcast-control code. CEF runs without the sandbox SDK in this
|
|
77
|
+
build; the scene server/content is part of the application's trust boundary.
|
|
220
78
|
|
|
221
|
-
|
|
222
|
-
await pulsar.client.obs.call("CreateInput", {
|
|
223
|
-
sceneName: "Default",
|
|
224
|
-
inputName: "Bumper",
|
|
225
|
-
inputKind: "vlc_source",
|
|
226
|
-
inputSettings: {
|
|
227
|
-
playlist: [
|
|
228
|
-
{ value: "C:/path/to/intro.mp4", selected: false, hidden: false },
|
|
229
|
-
{ value: "C:/path/to/sponsor.mkv", selected: false, hidden: false },
|
|
230
|
-
],
|
|
231
|
-
loop: true,
|
|
232
|
-
shuffle: false,
|
|
233
|
-
playback_behavior: "stop_restart",
|
|
234
|
-
},
|
|
235
|
-
});
|
|
236
|
-
```
|
|
79
|
+
### Render and helper layout
|
|
237
80
|
|
|
238
|
-
|
|
239
|
-
|
|
81
|
+
The distribution ships `pulsar-browser.dll` and
|
|
82
|
+
`pulsar-browser-page.exe` next to CEF under `obs-plugins/64bit/`.
|
|
83
|
+
The upstream `obs-browser.dll` is removed to avoid duplicate source
|
|
84
|
+
registration. Preserve all CEF locale/resource files and the helper path.
|
|
240
85
|
|
|
241
|
-
|
|
86
|
+
GPU acceleration is the normal supported hardware rendering path;
|
|
87
|
+
software rendering has separate callback tests. Do not disable GPU globally
|
|
88
|
+
to make an accelerated-rendering test green. A no-physical-GPU CI skip is
|
|
89
|
+
reported as unexecuted hardware proof.
|
|
242
90
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
91
|
+
Callback admission, asynchronous source tasks and browser close are fenced
|
|
92
|
+
before libobs teardown. Do not kill every browser helper by image name;
|
|
93
|
+
multiple Pulsar runtimes may own independent children.
|
|
246
94
|
|
|
247
|
-
|
|
248
|
-
`onLog`)
|
|
249
|
-
- The `SpawnedPulsar` handle (`client`, `child`, `port`,
|
|
250
|
-
`libobsVersion`, `shutdown()`)
|
|
251
|
-
- Boot environment variables (the full `PULSAR_*` matrix)
|
|
252
|
-
- Lifecycle (boot / shutdown / crash recovery)
|
|
95
|
+
## Text and media sources
|
|
253
96
|
|
|
254
|
-
|
|
97
|
+
Use `GetInputKindList` / the capability manifest to select the actual kind.
|
|
98
|
+
Typical source IDs include `text_gdiplus_v2`, `text_ft2_source_v2`,
|
|
99
|
+
`vlc_source` and `ffmpeg_source`, subject to registration.
|
|
255
100
|
|
|
256
|
-
|
|
101
|
+
For text, select a font available on the target machine. For VLC, do not
|
|
102
|
+
assume the presence of the module means a complete libVLC codec distribution
|
|
103
|
+
was shipped. Validate playback on the installed artifact. FFmpeg media source
|
|
104
|
+
is a different input from the FFmpeg recording/muxer output.
|
|
257
105
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
heavy AV scanning.
|
|
106
|
+
When using a local stinger, pass an explicit absolute `PULSAR_STINGER_ASSET`.
|
|
107
|
+
The legacy cwd-relative fallback is not reliable with private runtime cwd.
|
|
108
|
+
Dual-lane Stinger/Fade requires its own opt-in and validates the asset before
|
|
109
|
+
use; missing/invalid input must not be treated as a successful transition.
|
|
263
110
|
|
|
264
|
-
|
|
111
|
+
## Lifecycle
|
|
265
112
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
multi-process architecture; you don't manage it from Node.
|
|
113
|
+
The shared launcher waits for the idle marker, reads this child's protected
|
|
114
|
+
config and authenticates v5. It does not wait for every browser page to finish
|
|
115
|
+
rendering. Observe actual source/Preview readiness separately.
|
|
270
116
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
117
|
+
Finalize active outputs and retain files before calling `shutdown()`.
|
|
118
|
+
The Node helper terminates the child with a bounded fallback; Windows
|
|
119
|
+
termination is not a guarantee of graceful CEF/libobs teardown or MP4
|
|
120
|
+
finalization. The native shutdown-event harness is a different path.
|
|
275
121
|
|
|
276
|
-
|
|
122
|
+
Default generated runtime directories are temporary and may be removed after
|
|
123
|
+
shutdown. Supply persistent recording/log destinations or an explicit
|
|
124
|
+
application-owned runtime directory.
|
|
277
125
|
|
|
278
|
-
|
|
279
|
-
the `asarUnpack` glob and the `cwd` constraint.
|
|
126
|
+
## Packaging Electron
|
|
280
127
|
|
|
281
|
-
|
|
128
|
+
Keep the native tree outside ASAR:
|
|
282
129
|
|
|
283
|
-
```
|
|
130
|
+
```json
|
|
284
131
|
{
|
|
285
132
|
"asar": true,
|
|
286
133
|
"asarUnpack": [
|
|
287
134
|
"node_modules/@clodocapeo/pulsar-bundle-full/binaries/**/*"
|
|
288
|
-
]
|
|
289
|
-
"files": [
|
|
290
|
-
"dist/**/*",
|
|
291
|
-
"node_modules/@clodocapeo/pulsar-bundle-full/**/*"
|
|
292
|
-
],
|
|
293
|
-
"extraResources": []
|
|
135
|
+
]
|
|
294
136
|
}
|
|
295
137
|
```
|
|
296
138
|
|
|
297
|
-
Then in your Electron main process:
|
|
298
|
-
|
|
299
139
|
```ts
|
|
300
140
|
import { app } from "electron";
|
|
301
|
-
import { spawn } from "@clodocapeo/pulsar-bundle-full";
|
|
302
141
|
import { resolve } from "node:path";
|
|
142
|
+
import { spawn } from "@clodocapeo/pulsar-bundle-full";
|
|
303
143
|
|
|
304
144
|
const binariesPath = app.isPackaged
|
|
305
|
-
? resolve(
|
|
306
|
-
|
|
307
|
-
"app.asar.unpacked",
|
|
308
|
-
"node_modules",
|
|
309
|
-
"@clodocapeo",
|
|
310
|
-
"pulsar-bundle-full",
|
|
311
|
-
"binaries",
|
|
312
|
-
)
|
|
145
|
+
? resolve(process.resourcesPath, "app.asar.unpacked", "node_modules",
|
|
146
|
+
"@clodocapeo", "pulsar-bundle-full", "binaries")
|
|
313
147
|
: undefined;
|
|
314
|
-
|
|
315
148
|
const pulsar = await spawn({ binariesPath });
|
|
316
149
|
```
|
|
317
150
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
**around 200 MB** installed (your app code + Electron + Pulsar full
|
|
322
|
-
bundle). If that matters, ship `pulsar-bundle` (light) and
|
|
323
|
-
implement scene composition client-side; if it doesn't, the full
|
|
324
|
-
bundle gives you the most expressive scene model.
|
|
151
|
+
Verify the final installer and deployed bytes. Use actual ZIP/install size
|
|
152
|
+
instead of historical full-bundle estimates. Preserve the full helper/resource
|
|
153
|
+
layout when staging a sidecar runtime.
|
|
325
154
|
|
|
326
|
-
##
|
|
155
|
+
## Offline and mirror installs
|
|
327
156
|
|
|
328
|
-
|
|
157
|
+
`PULSAR_BUNDLE_SKIP_POSTINSTALL=1` skips download.
|
|
158
|
+
`PULSAR_BUNDLE_DOWNLOAD_URL` selects a trusted matching **full** archive.
|
|
329
159
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
160
|
+
Both light and full postinstall scripts read the same override name.
|
|
161
|
+
An override is not automatically scoped to this package in a workspace install.
|
|
162
|
+
Do not send a full ZIP to both installers unintentionally.
|
|
333
163
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
`PULSAR_BUNDLE_DOWNLOAD_URL` overrides the URL for **this package
|
|
339
|
-
only** — the light and full bundles each have their own download URL,
|
|
340
|
-
so a `pulsar-bundle` install in the same workspace fetches its own
|
|
341
|
-
zip from the default GitHub Releases path.
|
|
164
|
+
The capture-test-only `PULSAR_BUNDLE_FULL_BINARIES_PATH` is read by the test
|
|
165
|
+
harness, not by public `spawn()`; applications use `binariesPath`.
|
|
342
166
|
|
|
343
167
|
## Troubleshooting
|
|
344
168
|
|
|
345
|
-
|
|
346
|
-
[light bundle's troubleshooting](https://www.npmjs.com/package/@clodocapeo/pulsar-bundle#troubleshooting):
|
|
347
|
-
|
|
348
|
-
### Browser source shows a blank / black square
|
|
349
|
-
|
|
350
|
-
CEF subprocess failed to start. Common causes:
|
|
351
|
-
|
|
352
|
-
1. **`pulsar-browser-page.exe` missing from `obs-plugins/64bit/`** —
|
|
353
|
-
antivirus may have quarantined it. Restore + whitelist.
|
|
354
|
-
2. **GPU disabled in CEF.** On some virtualised / RDP environments
|
|
355
|
-
CEF can't initialise its GPU process. The `obs-browser` plugin
|
|
356
|
-
logs the failure to stderr — capture it via `onLog` and look for
|
|
357
|
-
`gpu_process_host`.
|
|
358
|
-
3. **URL not reachable.** Check `pulsar.client.obs.call("GetInputSettings", { inputName })`
|
|
359
|
-
matches what you set, and that the URL responds with `200 OK`
|
|
360
|
-
(CEF won't render a 404 page by default).
|
|
361
|
-
|
|
362
|
-
### `Helper process exited with code 63`
|
|
363
|
-
|
|
364
|
-
This is the CEF subprocess crashing on launch. **Almost always**
|
|
365
|
-
means `pulsar-browser-page.exe` is in the wrong directory — it must
|
|
366
|
-
be in `obs-plugins/64bit/` next to `libcef.dll`, not in
|
|
367
|
-
`bin/64bit/`. The full bundle's CMake places it correctly; if you
|
|
368
|
-
re-stage the bundle by hand, preserve that layout.
|
|
369
|
-
|
|
370
|
-
### Audio from a `browser_source` is muffled / out of sync
|
|
371
|
-
|
|
372
|
-
Browser source audio goes through CEF's audio pipeline + libobs's
|
|
373
|
-
WASAPI graph — two A/V resync paths. Mitigations:
|
|
374
|
-
|
|
375
|
-
- Set `restart_when_active: false` so the browser process keeps
|
|
376
|
-
running across scene changes (avoids re-handshake stalls).
|
|
377
|
-
- Pin the page's audio sample rate to 48000 to match libobs's
|
|
378
|
-
audio mix bus.
|
|
379
|
-
- For CEF audio + scene-A-to-scene-B transitions, set the source's
|
|
380
|
-
audio sync offset via the v5 `SetInputAudioSyncOffset` request.
|
|
381
|
-
|
|
382
|
-
### `vlc_source` shows nothing
|
|
383
|
-
|
|
384
|
-
The bundled libVLC is stripped of optional codec support to keep the
|
|
385
|
-
zip size manageable. If your media file plays in standalone VLC but
|
|
386
|
-
not in Pulsar, the codec is probably not in the bundle. Workarounds:
|
|
387
|
-
re-encode to H.264/AAC, or use the FFmpeg muxer source
|
|
388
|
-
(`ffmpeg_source`) which ships with the light bundle anyway.
|
|
389
|
-
|
|
390
|
-
## Versioning
|
|
391
|
-
|
|
392
|
-
Tracks `pulsar-client` and `pulsar.exe` in lockstep — `1.0.0` of this
|
|
393
|
-
package downloads `pulsar-windows-x64-full-v1.0.0.zip` and depends on
|
|
394
|
-
`@clodocapeo/pulsar-client@1.0.0`.
|
|
395
|
-
|
|
396
|
-
Bump light, full, and client together; npm semver resolution rejects
|
|
397
|
-
mixed versions in a workspace.
|
|
398
|
-
|
|
399
|
-
## Compatibility
|
|
400
|
-
|
|
401
|
-
| | |
|
|
402
|
-
|---|---|
|
|
403
|
-
| OS | Windows 10/11 x64 only |
|
|
404
|
-
| Node | ≥ 18 |
|
|
405
|
-
| Module system | ESM only |
|
|
406
|
-
| CEF | bundled — version pinned by upstream `obs-browser` at the recorded SHA |
|
|
407
|
-
| Antivirus | Whitelist the `binaries/` directory; CEF helper exes are common false positives |
|
|
408
|
-
|
|
409
|
-
## Licence
|
|
410
|
-
|
|
411
|
-
[GPL-2.0-or-later](./LICENSE).
|
|
412
|
-
|
|
413
|
-
This package bundles `pulsar.exe`, libobs + Pulsar plugins, CEF, and
|
|
414
|
-
libVLC. The aggregate is distributed under GPL-2.0-or-later;
|
|
415
|
-
individual components retain their upstream notices:
|
|
416
|
-
|
|
417
|
-
| Component | Licence |
|
|
169
|
+
| Symptom | Checks |
|
|
418
170
|
|---|---|
|
|
419
|
-
|
|
|
420
|
-
| CEF
|
|
421
|
-
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
Read [`LICENSE-INVARIANTS.md`](https://github.com/ZabLaboratory/Pulsar/blob/main/LICENSE-INVARIANTS.md)
|
|
443
|
-
on the Pulsar repo for the full contract, then
|
|
444
|
-
[`CONSUMER-AUDIT.md`](https://github.com/ZabLaboratory/Pulsar/blob/main/CONSUMER-AUDIT.md)
|
|
445
|
-
for the empirical checklist your application's CI must enforce.
|
|
446
|
-
|
|
447
|
-
If you only need the typed client without any GPL binary, use
|
|
448
|
-
[`@clodocapeo/pulsar-client`](https://www.npmjs.com/package/@clodocapeo/pulsar-client)
|
|
449
|
-
instead — it's MIT.
|
|
450
|
-
|
|
451
|
-
For the binary without browser sources / CEF / VLC, use
|
|
452
|
-
[`@clodocapeo/pulsar-bundle`](https://www.npmjs.com/package/@clodocapeo/pulsar-bundle).
|
|
171
|
+
| Browser black/frozen | Scene URL/content, source settings, real recorded frames and helper logs. HTTP success alone is insufficient. |
|
|
172
|
+
| Helper launch/exit error | Matching binary dependencies, one browser loader, correct CEF/helper/resource locations; an exit code alone is not a root-cause proof. |
|
|
173
|
+
| CEF GPU unavailable | Actual adapter/driver/session capability; do not relabel software rendering as accelerated proof. |
|
|
174
|
+
| Text missing | Font and registered text kind on the target machine. |
|
|
175
|
+
| VLC source unavailable | Module plus actual libVLC/runtime/codec availability. |
|
|
176
|
+
| Effect filter absent | Validated SDK capability; no SDK/model is bundled. |
|
|
177
|
+
| Recording lost after shutdown | Persistent output path and awaited finalization before child termination. |
|
|
178
|
+
|
|
179
|
+
The [capture/PGM suite](../capture-pgm-compat/README.md) distinguishes black,
|
|
180
|
+
frozen and healthy recordings. The [embedding guide](../../docs/PRISM-EMBEDDING.md)
|
|
181
|
+
covers artifact verification and multi-instance ownership.
|
|
182
|
+
|
|
183
|
+
## Version and license
|
|
184
|
+
|
|
185
|
+
The 3.0.0 package downloads the v3.0.0 full archive and pins client 3.0.0.
|
|
186
|
+
npm may resolve multiple versions; explicit version matching remains the
|
|
187
|
+
integrator's responsibility.
|
|
188
|
+
|
|
189
|
+
The runtime bundle is [GPL-2.0-or-later](LICENSE). Component notices retain
|
|
190
|
+
their original licenses, and the client remains MIT. Read the
|
|
191
|
+
[distribution constraints](../../LICENSE-INVARIANTS.md) and
|
|
192
|
+
[consumer audit](../../CONSUMER-AUDIT.md); this README does not promise a
|
|
193
|
+
legal outcome for an arbitrary non-GPL host.
|
package/dist/spawn.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { type ChildProcess } from "node:child_process";
|
|
2
|
-
import { PulsarClient } from "@clodocapeo/pulsar-client";
|
|
2
|
+
import { PulsarClient, type PulsarPrismLogEvent } from "@clodocapeo/pulsar-client";
|
|
3
3
|
export interface SpawnOptions {
|
|
4
4
|
/** Override the directory containing bin/64bit/pulsar.exe.
|
|
5
5
|
* Default: <package>/binaries (populated by postinstall). */
|
|
6
6
|
binariesPath?: string;
|
|
7
7
|
/** Extra env vars for pulsar.exe. Common ones:
|
|
8
|
+
* PULSAR_RUNTIME_INSTANCE_ID validated identity (auto-generated by spawn)
|
|
9
|
+
* PULSAR_RUNTIME_DIR explicit per-instance working directory
|
|
10
|
+
* PULSAR_LEGACY_ALIAS required | dedicated | off (default opportunistic)
|
|
8
11
|
* PULSAR_CAPTURE_WINDOW "<title>:<class>:<exe>" for window_capture
|
|
9
12
|
* PULSAR_FPS 30 / 60 / etc.
|
|
10
13
|
* PULSAR_VIDEO_BITRATE kbps
|
|
@@ -18,6 +21,8 @@ export interface SpawnOptions {
|
|
|
18
21
|
readyTimeoutMs?: number;
|
|
19
22
|
/** Optional log forwarder. Receives one stdout/stderr line at a time. */
|
|
20
23
|
onLog?: (stream: "stdout" | "stderr", line: string) => void;
|
|
24
|
+
/** Optional structured projection of runtime lifecycle/native log lines. */
|
|
25
|
+
onPrismLog?: (event: PulsarPrismLogEvent) => void;
|
|
21
26
|
/** Test-only escape hatch: spawn a different executable instead of
|
|
22
27
|
* the bundled pulsar.exe. The launched process must still print the
|
|
23
28
|
* ready marker on stdout and create obs-websocket/config.json under
|
|
@@ -41,6 +46,10 @@ export interface SpawnedPulsar {
|
|
|
41
46
|
/** libobs version string parsed from the boot log
|
|
42
47
|
* (e.g. "32.1.2-1-g8c23ba721-pulsar"). */
|
|
43
48
|
libobsVersion: string;
|
|
49
|
+
/** Validated identity of this isolated Pulsar process. */
|
|
50
|
+
runtimeInstanceId: string;
|
|
51
|
+
/** Process-local cwd/config/log/recording namespace. */
|
|
52
|
+
runtimeDir: string;
|
|
44
53
|
/** Disconnect the WS client and terminate pulsar.exe. Resolves once
|
|
45
54
|
* the process has exited. Idempotent. */
|
|
46
55
|
shutdown(): Promise<void>;
|
|
@@ -48,10 +57,10 @@ export interface SpawnedPulsar {
|
|
|
48
57
|
/**
|
|
49
58
|
* Spawn pulsar.exe and return a connected PulsarClient.
|
|
50
59
|
*
|
|
51
|
-
* The
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
60
|
+
* The executable is resolved from the bundle, while each child process is
|
|
61
|
+
* started in its own runtime directory. The native bootstrap resolves OBS
|
|
62
|
+
* plugins from the executable location and uses the runtime directory for
|
|
63
|
+
* config, logs, recordings and namespaced DirectShow mappings.
|
|
55
64
|
*
|
|
56
65
|
* @example
|
|
57
66
|
* const pulsar = await spawn({
|
package/dist/spawn.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["../src/spawn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["../src/spawn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAQ5E,OAAO,EAAE,YAAY,EAAsB,KAAK,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAQvG,MAAM,WAAW,YAAY;IAC3B;kEAC8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;;;;;;OAWG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE7B,oEAAoE;IACpE,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,yEAAyE;IACzE,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAE5D,4EAA4E;IAC5E,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAElD;;;;;oBAKgB;IAChB,aAAa,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CAClD;AAED,MAAM,WAAW,aAAa;IAC5B,qDAAqD;IACrD,MAAM,EAAE,YAAY,CAAC;IAErB;;yDAEqD;IACrD,KAAK,EAAE,YAAY,CAAC;IAEpB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IAEb;+CAC2C;IAC3C,aAAa,EAAE,MAAM,CAAC;IAEtB,0DAA0D;IAC1D,iBAAiB,EAAE,MAAM,CAAC;IAE1B,wDAAwD;IACxD,UAAU,EAAE,MAAM,CAAC;IAEnB;8CAC0C;IAC1C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAUD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,KAAK,CAAC,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,aAAa,CAAC,CAoR3E"}
|
package/dist/spawn.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { spawn as childSpawn } from "node:child_process";
|
|
2
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
-
import {
|
|
2
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { createServer } from "node:net";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
7
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { PulsarClient } from "@clodocapeo/pulsar-client";
|
|
8
|
+
import { PulsarClient, PulsarRuntimeError } from "@clodocapeo/pulsar-client";
|
|
6
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
10
|
const __dirname = dirname(__filename);
|
|
8
11
|
/** Default location of the bundled binaries inside this package. */
|
|
@@ -12,10 +15,10 @@ const IDLE_MARKER = "ready, idling";
|
|
|
12
15
|
/**
|
|
13
16
|
* Spawn pulsar.exe and return a connected PulsarClient.
|
|
14
17
|
*
|
|
15
|
-
* The
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
18
|
+
* The executable is resolved from the bundle, while each child process is
|
|
19
|
+
* started in its own runtime directory. The native bootstrap resolves OBS
|
|
20
|
+
* plugins from the executable location and uses the runtime directory for
|
|
21
|
+
* config, logs, recordings and namespaced DirectShow mappings.
|
|
19
22
|
*
|
|
20
23
|
* @example
|
|
21
24
|
* const pulsar = await spawn({
|
|
@@ -25,9 +28,83 @@ const IDLE_MARKER = "ready, idling";
|
|
|
25
28
|
* await pulsar.shutdown();
|
|
26
29
|
*/
|
|
27
30
|
export async function spawn(opts = {}) {
|
|
31
|
+
const emitRuntimeError = (code, message, details = {}) => {
|
|
32
|
+
opts.onPrismLog?.({
|
|
33
|
+
schemaVersion: 1,
|
|
34
|
+
severity: "error",
|
|
35
|
+
domain: "service",
|
|
36
|
+
source: "pulsar.runtime",
|
|
37
|
+
code,
|
|
38
|
+
message,
|
|
39
|
+
context: { action: "spawn" },
|
|
40
|
+
details,
|
|
41
|
+
});
|
|
42
|
+
};
|
|
28
43
|
const binariesPath = opts.binariesPath ?? DEFAULT_BINARIES;
|
|
29
|
-
const
|
|
30
|
-
|
|
44
|
+
const binaryCwd = join(binariesPath, "bin", "64bit");
|
|
45
|
+
// #243: every child gets an explicit identity and a private cwd. The
|
|
46
|
+
// native bootstrap validates the same identifier and holds an instance
|
|
47
|
+
// lock before touching config, logs, recordings or DirectShow mappings.
|
|
48
|
+
const requestedRuntimeId = opts.env?.PULSAR_RUNTIME_INSTANCE_ID;
|
|
49
|
+
const runtimeInstanceId = requestedRuntimeId || `node-${randomUUID().replace(/-/g, "")}`;
|
|
50
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(runtimeInstanceId)) {
|
|
51
|
+
emitRuntimeError("PULSAR_RUNTIME_ID_INVALID", `Invalid PULSAR_RUNTIME_INSTANCE_ID '${runtimeInstanceId}'.`, {
|
|
52
|
+
runtimeInstanceId,
|
|
53
|
+
});
|
|
54
|
+
throw new PulsarRuntimeError("PULSAR_RUNTIME_ID_INVALID", "PULSAR_RUNTIME_INSTANCE_ID must match [A-Za-z0-9][A-Za-z0-9._-]{0,63}.", { action: "spawn", runtimeInstanceId });
|
|
55
|
+
}
|
|
56
|
+
const requestedRuntimeDir = opts.env?.PULSAR_RUNTIME_DIR;
|
|
57
|
+
const ownsRuntimeDir = requestedRuntimeDir === undefined || requestedRuntimeDir.length === 0;
|
|
58
|
+
const requestedRuntimeRoot = opts.env?.PULSAR_RUNTIME_ROOT ?? process.env.PULSAR_RUNTIME_ROOT;
|
|
59
|
+
const runtimeRoot = resolve(requestedRuntimeRoot || tmpdir());
|
|
60
|
+
let runtimeDir;
|
|
61
|
+
if (!ownsRuntimeDir) {
|
|
62
|
+
runtimeDir = resolve(requestedRuntimeDir);
|
|
63
|
+
mkdirSync(runtimeDir, { recursive: true });
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
mkdirSync(runtimeRoot, { recursive: true });
|
|
67
|
+
runtimeDir = mkdtempSync(join(runtimeRoot, `pulsar-${runtimeInstanceId}-`));
|
|
68
|
+
}
|
|
69
|
+
const configPath = join(runtimeDir, "obs-websocket", "config.json");
|
|
70
|
+
const cleanupRuntimeDir = () => {
|
|
71
|
+
if (!ownsRuntimeDir)
|
|
72
|
+
return;
|
|
73
|
+
const relativeRuntimeDir = relative(runtimeRoot, runtimeDir);
|
|
74
|
+
if (isAbsolute(relativeRuntimeDir) || relativeRuntimeDir.startsWith("..") ||
|
|
75
|
+
!relativeRuntimeDir.startsWith(`pulsar-${runtimeInstanceId}-`))
|
|
76
|
+
return;
|
|
77
|
+
try {
|
|
78
|
+
rmSync(runtimeDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 50 });
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Antivirus/CEF can briefly retain a child file. The path is uniquely
|
|
82
|
+
// generated for this call and can be reaped by the caller later.
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const childEnv = {
|
|
86
|
+
...process.env,
|
|
87
|
+
...opts.env,
|
|
88
|
+
PULSAR_RUNTIME_INSTANCE_ID: runtimeInstanceId,
|
|
89
|
+
PULSAR_RUNTIME_DIR: runtimeDir,
|
|
90
|
+
};
|
|
91
|
+
// The child starts in runtimeDir, so an explicitly relative root would be
|
|
92
|
+
// interpreted differently by native code. Publish the absolute root used
|
|
93
|
+
// by this helper whenever it owns the generated namespace or a caller
|
|
94
|
+
// supplied a root, keeping instance/alias leases on the same state tree.
|
|
95
|
+
if (ownsRuntimeDir || requestedRuntimeRoot)
|
|
96
|
+
childEnv.PULSAR_RUNTIME_ROOT = runtimeRoot;
|
|
97
|
+
// An inherited PULSAR_PORT is not a safe default for a second child. Only
|
|
98
|
+
// an explicit per-spawn value opts out of the free-port allocator.
|
|
99
|
+
if (opts.env?.PULSAR_PORT === undefined || opts.env.PULSAR_PORT === "" || opts.env.PULSAR_PORT === "0") {
|
|
100
|
+
try {
|
|
101
|
+
childEnv.PULSAR_PORT = String(await pickFreePort());
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
cleanupRuntimeDir();
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
31
108
|
// Resolve the executable. Default = bundled pulsar.exe; tests can pass
|
|
32
109
|
// launchCommand to swap in a fake.
|
|
33
110
|
let exe;
|
|
@@ -37,20 +114,52 @@ export async function spawn(opts = {}) {
|
|
|
37
114
|
args = opts.launchCommand.args ?? [];
|
|
38
115
|
}
|
|
39
116
|
else {
|
|
40
|
-
exe = join(
|
|
117
|
+
exe = join(binaryCwd, "pulsar.exe");
|
|
41
118
|
args = [];
|
|
42
119
|
if (!existsSync(exe)) {
|
|
43
|
-
|
|
120
|
+
cleanupRuntimeDir();
|
|
121
|
+
emitRuntimeError("PULSAR_BINARY_UNAVAILABLE", `pulsar.exe not found at ${exe}.`, { binaryPath: exe });
|
|
122
|
+
throw new PulsarRuntimeError("PULSAR_BINARY_UNAVAILABLE", `pulsar.exe not found at ${exe}. ` +
|
|
44
123
|
`Run \`npm install\` to trigger the postinstall download, ` +
|
|
45
|
-
`or set binariesPath to a local checkout
|
|
124
|
+
`or set binariesPath to a local checkout.`, { action: "spawn", binaryPath: exe });
|
|
46
125
|
}
|
|
47
126
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
127
|
+
let child;
|
|
128
|
+
try {
|
|
129
|
+
child = childSpawn(exe, args, {
|
|
130
|
+
cwd: runtimeDir,
|
|
131
|
+
env: childEnv,
|
|
132
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
133
|
+
windowsHide: true,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
cleanupRuntimeDir();
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
// Keep failure cleanup deterministic. In particular, a ready timeout must
|
|
141
|
+
// terminate the child before releasing the generated cwd; otherwise a
|
|
142
|
+
// failed boot can retain the instance lock and leave a partial namespace.
|
|
143
|
+
const terminateChild = async () => {
|
|
144
|
+
if (child.exitCode !== null || child.signalCode !== null)
|
|
145
|
+
return;
|
|
146
|
+
child.kill();
|
|
147
|
+
await new Promise((resolveExit) => {
|
|
148
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
149
|
+
resolveExit();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const forceTimer = setTimeout(() => {
|
|
153
|
+
if (child.exitCode === null && child.signalCode === null)
|
|
154
|
+
child.kill("SIGKILL");
|
|
155
|
+
resolveExit();
|
|
156
|
+
}, 5_000);
|
|
157
|
+
child.once("exit", () => {
|
|
158
|
+
clearTimeout(forceTimer);
|
|
159
|
+
resolveExit();
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
};
|
|
54
163
|
const readyTimeout = opts.readyTimeoutMs ?? 30_000;
|
|
55
164
|
let libobsVersion = "";
|
|
56
165
|
let resolveReady;
|
|
@@ -60,13 +169,30 @@ export async function spawn(opts = {}) {
|
|
|
60
169
|
rejectReady = rej;
|
|
61
170
|
});
|
|
62
171
|
const watchdog = setTimeout(() => {
|
|
63
|
-
|
|
172
|
+
emitRuntimeError("PULSAR_READY_TIMEOUT", `pulsar.exe did not signal ready within ${readyTimeout}ms`, {
|
|
173
|
+
timeoutMs: readyTimeout,
|
|
174
|
+
});
|
|
175
|
+
rejectReady(new PulsarRuntimeError("PULSAR_READY_TIMEOUT", `pulsar.exe did not signal ready within ${readyTimeout}ms`, { action: "spawn", timeoutMs: readyTimeout }));
|
|
64
176
|
}, readyTimeout);
|
|
65
177
|
const handleLine = (stream, line) => {
|
|
66
178
|
if (line.length === 0)
|
|
67
179
|
return;
|
|
180
|
+
// PULSAR_READY carries the WebSocket password. Keep the raw line only
|
|
181
|
+
// inside this boot parser; every application-visible callback receives a
|
|
182
|
+
// redacted copy.
|
|
183
|
+
const publicLine = redactNativeLine(line);
|
|
68
184
|
if (opts.onLog)
|
|
69
|
-
opts.onLog(stream,
|
|
185
|
+
opts.onLog(stream, publicLine);
|
|
186
|
+
opts.onPrismLog?.({
|
|
187
|
+
schemaVersion: 1,
|
|
188
|
+
severity: /error|fail|fatal|refus|declin|unavail/i.test(line) ? "error" : /warn|skip|partial/i.test(line) ? "warning" : "info",
|
|
189
|
+
domain: "service",
|
|
190
|
+
source: "pulsar.process",
|
|
191
|
+
code: "PULSAR_PROCESS_LOG",
|
|
192
|
+
message: publicLine,
|
|
193
|
+
context: { stream },
|
|
194
|
+
details: {},
|
|
195
|
+
});
|
|
70
196
|
// pulsar-headless prints exactly:
|
|
71
197
|
// "pulsar-headless: libobs <version> ready, idling (Ctrl+C to exit)"
|
|
72
198
|
// when boot is complete.
|
|
@@ -81,23 +207,74 @@ export async function spawn(opts = {}) {
|
|
|
81
207
|
attachLineReader(child, "stderr", (l) => handleLine("stderr", l));
|
|
82
208
|
child.on("exit", (code, signal) => {
|
|
83
209
|
clearTimeout(watchdog);
|
|
84
|
-
|
|
210
|
+
emitRuntimeError("PULSAR_PROCESS_EXITED", `pulsar.exe exited prematurely (code=${code}, signal=${signal})`, {
|
|
211
|
+
exitCode: code,
|
|
212
|
+
signal,
|
|
213
|
+
});
|
|
214
|
+
rejectReady(new PulsarRuntimeError("PULSAR_PROCESS_EXITED", `pulsar.exe exited prematurely (code=${code}, signal=${signal})`, { action: "spawn", exitCode: code, signal }));
|
|
215
|
+
cleanupRuntimeDir();
|
|
216
|
+
});
|
|
217
|
+
child.on("error", (error) => {
|
|
218
|
+
clearTimeout(watchdog);
|
|
219
|
+
emitRuntimeError("PULSAR_PROCESS_ERROR", `pulsar.exe process error: ${error.message}`, {
|
|
220
|
+
message: error.message,
|
|
221
|
+
});
|
|
222
|
+
rejectReady(new PulsarRuntimeError("PULSAR_PROCESS_ERROR", `pulsar.exe process error: ${error.message}`, {
|
|
223
|
+
action: "spawn",
|
|
224
|
+
}));
|
|
225
|
+
cleanupRuntimeDir();
|
|
85
226
|
});
|
|
86
|
-
|
|
227
|
+
try {
|
|
228
|
+
await ready;
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
await terminateChild();
|
|
232
|
+
cleanupRuntimeDir();
|
|
233
|
+
throw error;
|
|
234
|
+
}
|
|
87
235
|
// obs-websocket writes its config.json on first run -- after the
|
|
88
236
|
// ready marker, the file is guaranteed to exist with port + password.
|
|
89
237
|
if (!existsSync(configPath)) {
|
|
90
|
-
|
|
91
|
-
|
|
238
|
+
await terminateChild();
|
|
239
|
+
cleanupRuntimeDir();
|
|
240
|
+
emitRuntimeError("PULSAR_CONFIG_MISSING", `obs-websocket config not found at ${configPath} (boot incomplete?)`, {
|
|
241
|
+
configPath,
|
|
242
|
+
});
|
|
243
|
+
throw new PulsarRuntimeError("PULSAR_CONFIG_MISSING", `obs-websocket config not found at ${configPath} (boot incomplete?)`, { action: "spawn" });
|
|
244
|
+
}
|
|
245
|
+
let config;
|
|
246
|
+
try {
|
|
247
|
+
config = JSON.parse(readFileSync(configPath, "utf8"));
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
await terminateChild();
|
|
251
|
+
cleanupRuntimeDir();
|
|
252
|
+
const message = `could not parse obs-websocket config at ${configPath}`;
|
|
253
|
+
emitRuntimeError("PULSAR_CONFIG_INVALID", message, { configPath });
|
|
254
|
+
throw new PulsarRuntimeError("PULSAR_CONFIG_INVALID", message, { action: "spawn" });
|
|
92
255
|
}
|
|
93
|
-
const config = JSON.parse(readFileSync(configPath, "utf8"));
|
|
94
256
|
const port = config.server_port ?? 4455;
|
|
257
|
+
if (!Number.isInteger(port) || port < 1 || port > 65_535) {
|
|
258
|
+
await terminateChild();
|
|
259
|
+
cleanupRuntimeDir();
|
|
260
|
+
const message = `obs-websocket config contains invalid server_port at ${configPath}`;
|
|
261
|
+
emitRuntimeError("PULSAR_CONFIG_INVALID", message, { configPath });
|
|
262
|
+
throw new PulsarRuntimeError("PULSAR_CONFIG_INVALID", message, { action: "spawn" });
|
|
263
|
+
}
|
|
95
264
|
const password = config.server_password;
|
|
96
265
|
const client = new PulsarClient();
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
266
|
+
client.on("prismLog", (event) => opts.onPrismLog?.(event));
|
|
267
|
+
try {
|
|
268
|
+
await client.connect({
|
|
269
|
+
url: `ws://127.0.0.1:${port}`,
|
|
270
|
+
...(password !== undefined ? { password } : {}),
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
await terminateChild();
|
|
275
|
+
cleanupRuntimeDir();
|
|
276
|
+
throw error;
|
|
277
|
+
}
|
|
101
278
|
let shutdownPromise = null;
|
|
102
279
|
const shutdown = async () => {
|
|
103
280
|
if (shutdownPromise)
|
|
@@ -109,25 +286,30 @@ export async function spawn(opts = {}) {
|
|
|
109
286
|
catch {
|
|
110
287
|
// Already closed -- swallow.
|
|
111
288
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
await new Promise((res) => {
|
|
115
|
-
if (child.exitCode !== null || child.signalCode !== null)
|
|
116
|
-
return res();
|
|
117
|
-
child.once("exit", () => res());
|
|
118
|
-
// Safety net: a stuck process gets a 5 s grace period before
|
|
119
|
-
// force-kill so a pending muxer trailer write can land.
|
|
120
|
-
setTimeout(() => {
|
|
121
|
-
if (child.exitCode === null && child.signalCode === null) {
|
|
122
|
-
child.kill("SIGKILL");
|
|
123
|
-
}
|
|
124
|
-
}, 5_000);
|
|
125
|
-
});
|
|
126
|
-
}
|
|
289
|
+
await terminateChild();
|
|
290
|
+
cleanupRuntimeDir();
|
|
127
291
|
})();
|
|
128
292
|
return shutdownPromise;
|
|
129
293
|
};
|
|
130
|
-
return { client, child, port, libobsVersion, shutdown };
|
|
294
|
+
return { client, child, port, libobsVersion, runtimeInstanceId, runtimeDir, shutdown };
|
|
295
|
+
}
|
|
296
|
+
function pickFreePort() {
|
|
297
|
+
return new Promise((resolvePort, rejectPort) => {
|
|
298
|
+
const server = createServer();
|
|
299
|
+
server.once("error", rejectPort);
|
|
300
|
+
server.listen(0, "127.0.0.1", () => {
|
|
301
|
+
const address = server.address();
|
|
302
|
+
const port = typeof address === "object" && address !== null ? address.port : 0;
|
|
303
|
+
server.close((error) => {
|
|
304
|
+
if (error)
|
|
305
|
+
rejectPort(error);
|
|
306
|
+
else if (port > 0)
|
|
307
|
+
resolvePort(port);
|
|
308
|
+
else
|
|
309
|
+
rejectPort(new Error("OS returned no loopback port"));
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
});
|
|
131
313
|
}
|
|
132
314
|
/** Reads `child[stream]` line-by-line and dispatches each completed line. */
|
|
133
315
|
function attachLineReader(child, stream, onLine) {
|
|
@@ -150,4 +332,9 @@ function attachLineReader(child, stream, onLine) {
|
|
|
150
332
|
onLine(buf.replace(/\r$/, ""));
|
|
151
333
|
});
|
|
152
334
|
}
|
|
335
|
+
function redactNativeLine(line) {
|
|
336
|
+
return line
|
|
337
|
+
.replace(/(password|token|secret|stream[-_ ]?key)\s*[:=]\s*[^\s,;]+/gi, "$1=[redacted]")
|
|
338
|
+
.slice(0, 512);
|
|
339
|
+
}
|
|
153
340
|
//# sourceMappingURL=spawn.js.map
|
package/dist/spawn.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spawn.js","sourceRoot":"","sources":["../src/spawn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,UAAU,EAAqB,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAEzD,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,oEAAoE;AACpE,MAAM,gBAAgB,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;AA2D9D,MAAM,YAAY,GAAG,0BAA0B,CAAC;AAChD,MAAM,WAAW,GAAG,eAAe,CAAC;AAEpC;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,OAAqB,EAAE;IACjD,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,gBAAgB,CAAC;IAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,eAAe,EAAE,aAAa,CAAC,CAAC;IAE7D,uEAAuE;IACvE,mCAAmC;IACnC,IAAI,GAAW,CAAC;IAChB,IAAI,IAAc,CAAC;IACnB,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;QACvB,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;QAC7B,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,IAAI,EAAE,CAAC;IACvC,CAAC;SAAM,CAAC;QACN,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;QAC9B,IAAI,GAAG,EAAE,CAAC;QACV,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CACb,2BAA2B,GAAG,IAAI;gBAChC,2DAA2D;gBAC3D,0CAA0C,CAC7C,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE;QAClC,GAAG;QACH,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE;QACpC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;QACjC,WAAW,EAAE,IAAI;KAClB,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,IAAI,CAAC,cAAc,IAAI,MAAM,CAAC;IAEnD,IAAI,aAAa,GAAG,EAAE,CAAC;IACvB,IAAI,YAAyB,CAAC;IAC9B,IAAI,WAAkC,CAAC;IACvC,MAAM,KAAK,GAAG,IAAI,OAAO,CAAO,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAC3C,YAAY,GAAG,GAAG,CAAC;QACnB,WAAW,GAAG,GAAG,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,EAAE;QAC/B,WAAW,CAAC,IAAI,KAAK,CAAC,0CAA0C,YAAY,IAAI,CAAC,CAAC,CAAC;IACrF,CAAC,EAAE,YAAY,CAAC,CAAC;IAEjB,MAAM,UAAU,GAAG,CAAC,MAA2B,EAAE,IAAY,EAAE,EAAE;QAC/D,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC9B,IAAI,IAAI,CAAC,KAAK;YAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACzC,kCAAkC;QAClC,uEAAuE;QACvE,yBAAyB;QACzB,IAAI,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;YACrF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;YAC3E,aAAa,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAC7C,YAAY,CAAC,QAAQ,CAAC,CAAC;YACvB,YAAY,EAAE,CAAC;QACjB,CAAC;IACH,CAAC,CAAC;IAEF,gBAAgB,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;IAClE,gBAAgB,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;IAElE,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;QAChC,YAAY,CAAC,QAAQ,CAAC,CAAC;QACvB,WAAW,CAAC,IAAI,KAAK,CAAC,uCAAuC,IAAI,YAAY,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3F,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,CAAC;IAEZ,iEAAiE;IACjE,sEAAsE;IACtE,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,KAAK,CAAC,IAAI,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,qCAAqC,UAAU,qBAAqB,CAAC,CAAC;IACxF,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAuB,CAAC;IAClF,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,IAAI,IAAI,CAAC;IACxC,MAAM,QAAQ,GAAG,MAAM,CAAC,eAAe,CAAC;IAExC,MAAM,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;IAClC,MAAM,MAAM,CAAC,OAAO,CAAC;QACnB,GAAG,EAAE,kBAAkB,IAAI,EAAE;QAC7B,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChD,CAAC,CAAC;IAEH,IAAI,eAAe,GAAyB,IAAI,CAAC;IACjD,MAAM,QAAQ,GAAG,KAAK,IAAmB,EAAE;QACzC,IAAI,eAAe;YAAE,OAAO,eAAe,CAAC;QAC5C,eAAe,GAAG,CAAC,KAAK,IAAI,EAAE;YAC5B,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACP,6BAA6B;YAC/B,CAAC;YACD,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;gBACzD,KAAK,CAAC,IAAI,EAAE,CAAC;gBACb,MAAM,IAAI,OAAO,CAAO,CAAC,GAAG,EAAE,EAAE;oBAC9B,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI;wBAAE,OAAO,GAAG,EAAE,CAAC;oBACvE,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;oBAChC,6DAA6D;oBAC7D,wDAAwD;oBACxD,UAAU,CAAC,GAAG,EAAE;wBACd,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;4BACzD,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;wBACxB,CAAC;oBACH,CAAC,EAAE,KAAK,CAAC,CAAC;gBACZ,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QACL,OAAO,eAAe,CAAC;IACzB,CAAC,CAAC;IAEF,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC;AAC1D,CAAC;AAED,6EAA6E;AAC7E,SAAS,gBAAgB,CACvB,KAAmB,EACnB,MAA2B,EAC3B,MAA8B;IAE9B,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1B,IAAI,CAAC,GAAG;QAAE,OAAO;IACjB,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IACxB,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;QAC/B,GAAG,IAAI,KAAK,CAAC;QACb,IAAI,GAAG,CAAC;QACR,OAAO,CAAC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAClD,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;YACzB,MAAM,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CAAC;IACH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;QACjB,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
1
|
+
{"version":3,"file":"spawn.js","sourceRoot":"","sources":["../src/spawn.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clodocapeo/pulsar-bundle-full",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Full Pulsar bundle: pulsar.exe + DLLs + browser sources (CEF) + text + VLC. Same Node spawn() API as @clodocapeo/pulsar-bundle.",
|
|
5
5
|
"license": "GPL-2.0-or-later",
|
|
6
6
|
"repository": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"prepublishOnly": "npm run build"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@clodocapeo/pulsar-client": "
|
|
41
|
+
"@clodocapeo/pulsar-client": "3.0.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@msgpack/msgpack": "^3.0.0",
|