@bun-native/runtime 0.33.0 → 0.33.1
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 +1 -1
- package/bun-native-source.json +12 -12
- package/package.json +2 -2
- package/src/exit-diagnostic.ts +19 -11
- package/src/index.ts +38 -10
- package/src/startup-trace.ts +14 -4
package/README.md
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
|
|
3
3
|
Bun Native host process lifecycle and bridge runtime.
|
|
4
4
|
|
|
5
|
-
Part of [Bun Native](https://github.com/0xPlayerOne/bun-native), an experimental developer preview. Version 0.33.
|
|
5
|
+
Part of [Bun Native](https://github.com/0xPlayerOne/bun-native), an experimental developer preview. Version 0.33.1 was built from [c56fdd675365](https://github.com/0xPlayerOne/bun-native/tree/c56fdd6753653a10144a4167c1d3dfb3c38a0647) and published from GitHub Actions with npm provenance. Use the Bun revision pinned in that commit's toolchain.json. Start with the [getting-started guide](https://github.com/0xPlayerOne/bun-native/blob/c56fdd6753653a10144a4167c1d3dfb3c38a0647/docs/getting-started.md); `bun-native host install` fetches the matching release host. The license notice is in LICENSE.
|
package/bun-native-source.json
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"commit": "
|
|
4
|
-
"tree": "
|
|
3
|
+
"commit": "c56fdd6753653a10144a4167c1d3dfb3c38a0647",
|
|
4
|
+
"tree": "c6e81b3f9d041e11dd0413b5fdd7a50910383854",
|
|
5
5
|
"files": {
|
|
6
6
|
"package.json": {
|
|
7
|
-
"bytes":
|
|
8
|
-
"sha256": "
|
|
7
|
+
"bytes": 2115,
|
|
8
|
+
"sha256": "caf866d56f3cd7530d5735ecbf62512e9e22526bc44c75a4b48a18c26352c0b8"
|
|
9
9
|
},
|
|
10
10
|
"packages/runtime/package.json": {
|
|
11
11
|
"bytes": 195,
|
|
12
12
|
"sha256": "30d8e6684ee94e266b3bc63d151f718a5d0a8804b2155c11f74de3e7189af63a"
|
|
13
13
|
},
|
|
14
14
|
"packages/runtime/src/exit-diagnostic.ts": {
|
|
15
|
-
"bytes":
|
|
16
|
-
"sha256": "
|
|
15
|
+
"bytes": 5059,
|
|
16
|
+
"sha256": "0c93ad077ad4cfcdf32a8092da1aff6cdc0f65322feadf538768439d67b11e06"
|
|
17
17
|
},
|
|
18
18
|
"packages/runtime/src/index.ts": {
|
|
19
|
-
"bytes":
|
|
20
|
-
"sha256": "
|
|
19
|
+
"bytes": 39579,
|
|
20
|
+
"sha256": "a5f20057a2baa2117176a59c29e65438904f3758bfed3e29e981b55758e93adb"
|
|
21
21
|
},
|
|
22
22
|
"packages/runtime/src/installation.ts": {
|
|
23
23
|
"bytes": 1871,
|
|
24
24
|
"sha256": "a89c677fad56ace6839bda86891fafa088f3a37e0248c313a72d7918a0538a10"
|
|
25
25
|
},
|
|
26
26
|
"packages/runtime/src/startup-trace.ts": {
|
|
27
|
-
"bytes":
|
|
28
|
-
"sha256": "
|
|
27
|
+
"bytes": 3345,
|
|
28
|
+
"sha256": "19efe954374e1cd486277bc8df144d8dd711cffd12e2888d14021c871de840d6"
|
|
29
29
|
},
|
|
30
30
|
"LICENSE": {
|
|
31
31
|
"bytes": 1075,
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
"toolchain": {
|
|
36
|
-
"bytes":
|
|
37
|
-
"sha256": "
|
|
36
|
+
"bytes": 3234,
|
|
37
|
+
"sha256": "9f4d9362cb4cc38c0f4e705295789a0cbfec46ffdd007e0541b9441a7c3003a7"
|
|
38
38
|
},
|
|
39
39
|
"dependencyLock": {
|
|
40
40
|
"bytes": 28357,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bun-native/runtime",
|
|
3
|
-
"version": "0.33.
|
|
3
|
+
"version": "0.33.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": [
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"exports": "./src/index.ts",
|
|
16
16
|
"description": "Bun Native host process lifecycle and bridge runtime.",
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@bun-native/protocol": "0.33.
|
|
18
|
+
"@bun-native/protocol": "0.33.1"
|
|
19
19
|
},
|
|
20
20
|
"repository": {
|
|
21
21
|
"type": "git",
|
package/src/exit-diagnostic.ts
CHANGED
|
@@ -46,10 +46,7 @@ function escapeRegExp(text: string): string {
|
|
|
46
46
|
* Removes inherited environment values, assignments and credential-shaped
|
|
47
47
|
* tokens from native output. Over-redaction is acceptable; leaks are not.
|
|
48
48
|
*/
|
|
49
|
-
function
|
|
50
|
-
text: string,
|
|
51
|
-
environment: Readonly<Record<string, string | undefined>>
|
|
52
|
-
): string {
|
|
49
|
+
function redact(text: string, environment: Readonly<Record<string, string | undefined>>): string {
|
|
53
50
|
let redacted = text.replace(/\p{Cc}/gu, (control) =>
|
|
54
51
|
control === '\n' || control === '\t' ? control : ''
|
|
55
52
|
)
|
|
@@ -72,6 +69,22 @@ function redactDiagnostic(
|
|
|
72
69
|
.replace(/\b[A-Za-z0-9+_-]{32,}={0,2}/g, REDACTED)
|
|
73
70
|
}
|
|
74
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Captured native stderr, safe to log or record. A clipped first line can hold
|
|
74
|
+
* the end of a secret that no longer matches its full environment value, so it
|
|
75
|
+
* is dropped before redaction.
|
|
76
|
+
*/
|
|
77
|
+
export function redactDiagnostic(
|
|
78
|
+
stderr: string,
|
|
79
|
+
clipped: boolean,
|
|
80
|
+
environment: Readonly<Record<string, string | undefined>>
|
|
81
|
+
): string {
|
|
82
|
+
return redact(
|
|
83
|
+
clipped ? stderr.slice(stderr.indexOf('\n') + 1 || stderr.length) : stderr,
|
|
84
|
+
environment
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
75
88
|
export function exitDiagnostic(input: {
|
|
76
89
|
exitCode: number | null
|
|
77
90
|
signal: string | null
|
|
@@ -88,17 +101,12 @@ export function exitDiagnostic(input: {
|
|
|
88
101
|
panicReport?: string | null
|
|
89
102
|
environment: Readonly<Record<string, string | undefined>>
|
|
90
103
|
}): Readonly<NativeHostExitDiagnostic> {
|
|
91
|
-
|
|
92
|
-
// its full environment value, so it is dropped before redaction.
|
|
93
|
-
const stderr = input.stderrClipped
|
|
94
|
-
? input.stderr.slice(input.stderr.indexOf('\n') + 1 || input.stderr.length)
|
|
95
|
-
: input.stderr
|
|
96
|
-
const redacted = redactDiagnostic(stderr, input.environment)
|
|
104
|
+
const redacted = redactDiagnostic(input.stderr, input.stderrClipped, input.environment)
|
|
97
105
|
let panic = PANIC_LOCATION.exec(redacted)
|
|
98
106
|
// The kept report leads the tail only when the captured stderr lost it.
|
|
99
107
|
const report =
|
|
100
108
|
!panic && input.panicReport
|
|
101
|
-
?
|
|
109
|
+
? redact(input.panicReport, input.environment)
|
|
102
110
|
.trimEnd()
|
|
103
111
|
.slice(0, MAX_DIAGNOSTIC_TAIL / 2)
|
|
104
112
|
: null
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
processArguments,
|
|
7
7
|
encodeFrame,
|
|
8
8
|
FrameDecoder,
|
|
9
|
+
MAX_EVENT_FRAME_BYTES,
|
|
9
10
|
parseCommand,
|
|
10
11
|
parseEvent,
|
|
11
12
|
PROTOCOL_VERSION,
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
type ServicePolicy,
|
|
19
20
|
type Capability,
|
|
20
21
|
type FileAccess,
|
|
22
|
+
type HostEvent,
|
|
21
23
|
} from '@bun-native/protocol'
|
|
22
24
|
import { isAbsolute } from 'node:path'
|
|
23
25
|
import type { ReadableStreamDefaultReader } from 'node:stream/web'
|
|
@@ -31,6 +33,7 @@ export {
|
|
|
31
33
|
type NativeHostExitReason,
|
|
32
34
|
} from './exit-diagnostic.ts'
|
|
33
35
|
export { installedRelease, reportHealthy, type InstalledRelease } from './installation.ts'
|
|
36
|
+
export type { ApplicationEvent, HostEvent, WindowEvent } from '@bun-native/protocol'
|
|
34
37
|
|
|
35
38
|
type WithoutId<T> = T extends { id: number } ? Omit<T, 'id'> : never
|
|
36
39
|
export type WindowCommand = WithoutId<
|
|
@@ -71,10 +74,6 @@ export type WindowCommand = WithoutId<
|
|
|
71
74
|
}
|
|
72
75
|
>
|
|
73
76
|
>
|
|
74
|
-
export type WindowEvent = Extract<NativeEvent, { sequence: number; window_id: number }>
|
|
75
|
-
/** Sequenced events owned by the application rather than one window. */
|
|
76
|
-
export type ApplicationEvent = Extract<NativeEvent, { type: 'app_menu_item' }>
|
|
77
|
-
export type HostEvent = WindowEvent | ApplicationEvent
|
|
78
77
|
export type HostResponse = Extract<
|
|
79
78
|
NativeEvent,
|
|
80
79
|
{
|
|
@@ -351,6 +350,8 @@ export class NativeHost implements AsyncIterable<HostEvent> {
|
|
|
351
350
|
private readonly events: HostEvent[] = []
|
|
352
351
|
private readonly finished: Promise<void>
|
|
353
352
|
private waiting: WaitingReader | undefined
|
|
353
|
+
/** Queued events the consumer has taken; progress while reading is paused. */
|
|
354
|
+
private taken = 0
|
|
354
355
|
private failure: Error | undefined
|
|
355
356
|
private phase: 'starting' | 'running' | 'stopping' | 'closed' = 'starting'
|
|
356
357
|
private ready = false
|
|
@@ -428,6 +429,11 @@ export class NativeHost implements AsyncIterable<HostEvent> {
|
|
|
428
429
|
}
|
|
429
430
|
}
|
|
430
431
|
|
|
432
|
+
/** Whether the transport is still open (false once it failed or shut down). */
|
|
433
|
+
get running(): boolean {
|
|
434
|
+
return this.phase === 'running'
|
|
435
|
+
}
|
|
436
|
+
|
|
431
437
|
get limits(): Readonly<Limits> {
|
|
432
438
|
if (!this.negotiatedLimits) throw new ProtocolError('Native limits are not negotiated')
|
|
433
439
|
return this.negotiatedLimits
|
|
@@ -574,7 +580,10 @@ export class NativeHost implements AsyncIterable<HostEvent> {
|
|
|
574
580
|
next: () => {
|
|
575
581
|
if (this.failure) return Promise.reject(this.failure)
|
|
576
582
|
const event = this.events.shift()
|
|
577
|
-
if (event)
|
|
583
|
+
if (event) {
|
|
584
|
+
this.taken += 1
|
|
585
|
+
return Promise.resolve({ done: false, value: event })
|
|
586
|
+
}
|
|
578
587
|
if (this.phase === 'closed') return Promise.resolve({ done: true, value: undefined })
|
|
579
588
|
if (this.waiting)
|
|
580
589
|
return Promise.reject(new Error('Concurrent event reads are not supported'))
|
|
@@ -660,12 +669,13 @@ export class NativeHost implements AsyncIterable<HostEvent> {
|
|
|
660
669
|
}
|
|
661
670
|
|
|
662
671
|
private async readStdout(): Promise<void> {
|
|
663
|
-
const decoder = new FrameDecoder()
|
|
672
|
+
const decoder = new FrameDecoder(MAX_EVENT_FRAME_BYTES)
|
|
664
673
|
try {
|
|
665
674
|
for (;;) {
|
|
666
675
|
const { done, value } = await this.stdout.read()
|
|
667
676
|
if (done) break
|
|
668
677
|
for (const raw of decoder.push(value)) this.receive(parseEvent(raw))
|
|
678
|
+
if (this.events.length >= MAX_QUEUED_EVENTS) await this.drainEvents()
|
|
669
679
|
}
|
|
670
680
|
decoder.finish()
|
|
671
681
|
if (
|
|
@@ -679,6 +689,21 @@ export class NativeHost implements AsyncIterable<HostEvent> {
|
|
|
679
689
|
}
|
|
680
690
|
}
|
|
681
691
|
|
|
692
|
+
/**
|
|
693
|
+
* One pipe read can coalesce more frames than the cap while the consumer is
|
|
694
|
+
* keeping up. Over the cap, reading pauses for macrotask turns while the
|
|
695
|
+
* consumer takes events, and fails only a turn in which it took none, so
|
|
696
|
+
* queued events stay bounded by the cap plus one read.
|
|
697
|
+
*/
|
|
698
|
+
private async drainEvents(): Promise<void> {
|
|
699
|
+
while (this.events.length >= MAX_QUEUED_EVENTS) {
|
|
700
|
+
const taken = this.taken
|
|
701
|
+
await new Promise<void>((resolve) => setImmediate(resolve))
|
|
702
|
+
if (this.phase === 'closed') return
|
|
703
|
+
if (this.taken === taken) throw new ProtocolError('Native event limit exceeded')
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
682
707
|
private async readStderr(): Promise<void> {
|
|
683
708
|
const decoder = new TextDecoder()
|
|
684
709
|
try {
|
|
@@ -771,7 +796,12 @@ export class NativeHost implements AsyncIterable<HostEvent> {
|
|
|
771
796
|
clearTimeout(drainDeadline)
|
|
772
797
|
this.cancelOutput()
|
|
773
798
|
await Promise.allSettled(streams)
|
|
774
|
-
this.startupTrace?.finished(
|
|
799
|
+
this.startupTrace?.finished(
|
|
800
|
+
this.process.exitCode,
|
|
801
|
+
this.stderrTail,
|
|
802
|
+
this.stderrClipped,
|
|
803
|
+
this.process.pid
|
|
804
|
+
)
|
|
775
805
|
}
|
|
776
806
|
}
|
|
777
807
|
|
|
@@ -864,8 +894,6 @@ export class NativeHost implements AsyncIterable<HostEvent> {
|
|
|
864
894
|
this.waiting.resolve({ done: false, value: event })
|
|
865
895
|
this.waiting = undefined
|
|
866
896
|
} else {
|
|
867
|
-
if (this.events.length >= MAX_QUEUED_EVENTS)
|
|
868
|
-
throw new ProtocolError('Native event limit exceeded')
|
|
869
897
|
this.events.push(event)
|
|
870
898
|
}
|
|
871
899
|
}
|
|
@@ -1016,7 +1044,7 @@ export class NativeHost implements AsyncIterable<HostEvent> {
|
|
|
1016
1044
|
private fail(cause: unknown): void {
|
|
1017
1045
|
if (this.failure) return
|
|
1018
1046
|
this.failure = cause instanceof Error ? cause : new Error(String(cause))
|
|
1019
|
-
this.startupTrace?.failure(this.failure, this.stderrTail, this.process.pid)
|
|
1047
|
+
this.startupTrace?.failure(this.failure, this.stderrTail, this.stderrClipped, this.process.pid)
|
|
1020
1048
|
this.phase = 'closed'
|
|
1021
1049
|
for (const request of this.pending.values()) {
|
|
1022
1050
|
clearTimeout(request.timer)
|
package/src/startup-trace.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Command, NativeEvent } from '@bun-native/protocol'
|
|
2
2
|
import { appendFileSync } from 'node:fs'
|
|
3
3
|
import { isAbsolute } from 'node:path'
|
|
4
|
+
import { redactDiagnostic } from './exit-diagnostic.ts'
|
|
4
5
|
|
|
5
6
|
const MAX_RECORDS = 256
|
|
6
7
|
|
|
@@ -51,18 +52,27 @@ export class StartupTrace {
|
|
|
51
52
|
})
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
/** `stderr` is the bounded capture; it is recorded redacted, like exit diagnostics. */
|
|
56
|
+
failure(error: Error, stderr: string, clipped: boolean, hostPid: number): void {
|
|
55
57
|
this.write(
|
|
56
58
|
'transport_failed',
|
|
57
|
-
{
|
|
59
|
+
{
|
|
60
|
+
host_pid: hostPid,
|
|
61
|
+
error: error.message.slice(-1_024),
|
|
62
|
+
stderr_tail: redactDiagnostic(stderr, clipped, process.env),
|
|
63
|
+
},
|
|
58
64
|
true
|
|
59
65
|
)
|
|
60
66
|
}
|
|
61
67
|
|
|
62
|
-
finished(exitCode: number | null, stderr: string, hostPid: number): void {
|
|
68
|
+
finished(exitCode: number | null, stderr: string, clipped: boolean, hostPid: number): void {
|
|
63
69
|
this.write(
|
|
64
70
|
'transport_finished',
|
|
65
|
-
{
|
|
71
|
+
{
|
|
72
|
+
host_pid: hostPid,
|
|
73
|
+
exit_code: exitCode,
|
|
74
|
+
stderr_tail: redactDiagnostic(stderr, clipped, process.env),
|
|
75
|
+
},
|
|
66
76
|
true
|
|
67
77
|
)
|
|
68
78
|
}
|