@bun-native/protocol 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 +30 -30
- package/package.json +1 -1
- package/src/app-menu.ts +20 -12
- package/src/dialogs.ts +21 -6
- package/src/drops.ts +4 -2
- package/src/elements.ts +76 -47
- package/src/files.ts +26 -12
- package/src/framing.ts +33 -9
- package/src/index.ts +10 -24
- package/src/message-dialogs.ts +22 -16
- package/src/messages.ts +181 -79
- package/src/processes.ts +19 -8
- package/src/services.ts +30 -19
- package/src/values.ts +8 -2
package/src/framing.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Maximum command frame (runtime to host) in UTF-8 JSON bytes, excluding the
|
|
3
|
+
* newline. One transaction is one batch, so this bounds a full-window mount.
|
|
4
|
+
*/
|
|
5
|
+
export const MAX_FRAME_BYTES = 16_777_216
|
|
6
|
+
/** Maximum event frame (host to runtime): one text value or service result. */
|
|
7
|
+
export const MAX_EVENT_FRAME_BYTES = 65_536
|
|
8
|
+
/** Decoder capacity kept between frames; a larger frame borrows memory while it is read. */
|
|
9
|
+
const RETAINED_FRAME_BYTES = 65_536
|
|
3
10
|
|
|
4
11
|
export class ProtocolError extends Error {
|
|
5
12
|
override readonly name = 'ProtocolError'
|
|
@@ -24,17 +31,35 @@ export function encodeFrame(value: unknown): Uint8Array {
|
|
|
24
31
|
}
|
|
25
32
|
|
|
26
33
|
/**
|
|
27
|
-
* Incremental, bounded JSON-lines decoder for a native process pipe.
|
|
34
|
+
* Incremental, bounded JSON-lines decoder for a native process pipe. `limit`
|
|
35
|
+
* is the largest frame payload it accepts: the runtime reads events with
|
|
36
|
+
* `MAX_EVENT_FRAME_BYTES`; a peer reading commands keeps the default.
|
|
28
37
|
* A protocol error poisons the decoder; the caller must terminate that session.
|
|
29
38
|
* Successful finish closes the decoder and is idempotent.
|
|
30
39
|
*/
|
|
31
40
|
export class FrameDecoder {
|
|
32
|
-
private
|
|
41
|
+
private buffer = new Uint8Array(0)
|
|
33
42
|
// Preserve a leading BOM so JSON.parse rejects it, matching Rust serde_json.
|
|
34
43
|
private readonly utf8 = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true })
|
|
35
44
|
private length = 0
|
|
36
45
|
private state: 'open' | 'finished' | 'failed' = 'open'
|
|
37
46
|
|
|
47
|
+
constructor(private readonly limit: number = MAX_FRAME_BYTES) {}
|
|
48
|
+
|
|
49
|
+
/** Append a partial frame, growing the buffer geometrically up to the limit. */
|
|
50
|
+
private append(bytes: Uint8Array): void {
|
|
51
|
+
const length = this.length + bytes.length
|
|
52
|
+
if (length > this.buffer.length) {
|
|
53
|
+
const grown = new Uint8Array(
|
|
54
|
+
Math.min(this.limit, Math.max(length, 2 * this.buffer.length, 4_096))
|
|
55
|
+
)
|
|
56
|
+
grown.set(this.buffer.subarray(0, this.length))
|
|
57
|
+
this.buffer = grown
|
|
58
|
+
}
|
|
59
|
+
this.buffer.set(bytes, this.length)
|
|
60
|
+
this.length = length
|
|
61
|
+
}
|
|
62
|
+
|
|
38
63
|
push(chunk: Uint8Array): unknown[] {
|
|
39
64
|
if (this.state !== 'open')
|
|
40
65
|
throw new ProtocolError(
|
|
@@ -49,8 +74,7 @@ export class FrameDecoder {
|
|
|
49
74
|
const newline = chunk.indexOf(10, start)
|
|
50
75
|
const end = newline === -1 ? chunk.length : newline
|
|
51
76
|
const size = end - start
|
|
52
|
-
if (this.length + size >
|
|
53
|
-
throw new ProtocolError('Frame exceeds byte limit')
|
|
77
|
+
if (this.length + size > this.limit) throw new ProtocolError('Frame exceeds byte limit')
|
|
54
78
|
if (newline !== -1 && this.length === 0) {
|
|
55
79
|
// A frame wholly inside this chunk decodes in place, without a copy.
|
|
56
80
|
if (size === 0) throw new ProtocolError('Empty frame')
|
|
@@ -58,12 +82,12 @@ export class FrameDecoder {
|
|
|
58
82
|
start = newline + 1
|
|
59
83
|
continue
|
|
60
84
|
}
|
|
61
|
-
this.
|
|
62
|
-
this.length += size
|
|
85
|
+
this.append(chunk.subarray(start, end))
|
|
63
86
|
if (newline === -1) break
|
|
64
87
|
const json = this.utf8.decode(this.buffer.subarray(0, this.length))
|
|
65
|
-
frames.push(JSON.parse(json) as unknown)
|
|
66
88
|
this.length = 0
|
|
89
|
+
if (this.buffer.length > RETAINED_FRAME_BYTES) this.buffer = new Uint8Array(0)
|
|
90
|
+
frames.push(JSON.parse(json) as unknown)
|
|
67
91
|
start = newline + 1
|
|
68
92
|
}
|
|
69
93
|
} catch (error) {
|
package/src/index.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {
|
|
2
|
+
encodeFrame,
|
|
3
|
+
FrameDecoder,
|
|
4
|
+
MAX_EVENT_FRAME_BYTES,
|
|
5
|
+
MAX_FRAME_BYTES,
|
|
6
|
+
ProtocolError,
|
|
7
|
+
} from './framing.ts'
|
|
2
8
|
export {
|
|
3
9
|
DECORATION_FIELDS,
|
|
4
|
-
MAX_ACCESSIBLE_SET_SIZE,
|
|
5
10
|
MAX_OVERLAY_DEPTH,
|
|
6
11
|
parseElement,
|
|
7
12
|
SPLIT_DIVIDER,
|
|
8
13
|
type Element,
|
|
9
|
-
type OverlayElement,
|
|
10
|
-
type AccessibleLabel,
|
|
11
|
-
type BoxAccessibility,
|
|
12
|
-
type OverlayKeyboard,
|
|
13
|
-
type SplitStyle,
|
|
14
14
|
type Style,
|
|
15
15
|
} from './elements.ts'
|
|
16
16
|
export { MAX_TEXT_BYTES } from './values.ts'
|
|
@@ -26,12 +26,10 @@ export {
|
|
|
26
26
|
parseFilePath,
|
|
27
27
|
parseFilesystemRoots,
|
|
28
28
|
parseRootId,
|
|
29
|
-
type DirectoryEntry,
|
|
30
29
|
type FileAccess,
|
|
31
30
|
type FileKind,
|
|
32
31
|
type FileOperation,
|
|
33
32
|
type FileResult,
|
|
34
|
-
type FilesystemRoot,
|
|
35
33
|
type WriteMode,
|
|
36
34
|
} from './files.ts'
|
|
37
35
|
export {
|
|
@@ -42,14 +40,11 @@ export {
|
|
|
42
40
|
MAX_PROCESSES,
|
|
43
41
|
parseProcessExecutables,
|
|
44
42
|
parseProcessLaunch,
|
|
45
|
-
parseProcessLimits,
|
|
46
43
|
parseProcessLimitsArgument,
|
|
47
44
|
PROCESS_SIGNALS,
|
|
48
45
|
processArguments,
|
|
49
46
|
processLimitsArgument,
|
|
50
47
|
processesFromArguments,
|
|
51
|
-
type ProcessExecutable,
|
|
52
|
-
type ProcessLimits,
|
|
53
48
|
type ProcessExit,
|
|
54
49
|
type ProcessLaunch,
|
|
55
50
|
type ProcessSignal,
|
|
@@ -58,19 +53,12 @@ export {
|
|
|
58
53
|
} from './processes.ts'
|
|
59
54
|
export {
|
|
60
55
|
APP_MENU_KEYS,
|
|
61
|
-
APP_MENU_NAMED_KEYS,
|
|
62
|
-
APP_MENU_PUNCTUATION_KEYS,
|
|
63
56
|
MAX_APP_MENU_DEPTH,
|
|
64
57
|
MAX_APP_MENU_ENTRIES,
|
|
65
|
-
MAX_APP_MENU_ITEM_ID_BYTES,
|
|
66
|
-
MAX_APP_MENU_LABEL_BYTES,
|
|
67
|
-
MAX_APP_MENUS,
|
|
68
58
|
parseAppMenus,
|
|
69
59
|
parseContextMenuItems,
|
|
70
60
|
type AppMenu,
|
|
71
61
|
type AppMenuEntry,
|
|
72
|
-
type MenuShortcut,
|
|
73
|
-
type MenuShortcutModifiers,
|
|
74
62
|
} from './app-menu.ts'
|
|
75
63
|
export {
|
|
76
64
|
FILE_DIALOG_MODES,
|
|
@@ -88,7 +76,6 @@ export {
|
|
|
88
76
|
type FileDialogMode,
|
|
89
77
|
type FileDialogOptions,
|
|
90
78
|
type FileDialogResult,
|
|
91
|
-
type FileFilter,
|
|
92
79
|
} from './dialogs.ts'
|
|
93
80
|
export {
|
|
94
81
|
MAX_MESSAGE_DIALOG_BUTTONS,
|
|
@@ -97,8 +84,6 @@ export {
|
|
|
97
84
|
MAX_MESSAGE_DIALOG_LABEL_BYTES,
|
|
98
85
|
MAX_MESSAGE_DIALOG_MESSAGE_BYTES,
|
|
99
86
|
MAX_MESSAGE_DIALOG_TITLE_BYTES,
|
|
100
|
-
MAX_MESSAGE_DIALOGS,
|
|
101
|
-
parseMessageDialogOptions,
|
|
102
87
|
type MessageDialogButton,
|
|
103
88
|
type MessageDialogOptions,
|
|
104
89
|
type MessageDialogResult,
|
|
@@ -109,8 +94,6 @@ export {
|
|
|
109
94
|
MAX_DROP_ITEMS,
|
|
110
95
|
MAX_DROP_NAME_BYTES,
|
|
111
96
|
MAX_DROP_TARGETS,
|
|
112
|
-
type DragState,
|
|
113
|
-
type DroppedItem,
|
|
114
97
|
type DropRejection,
|
|
115
98
|
} from './drops.ts'
|
|
116
99
|
export {
|
|
@@ -138,7 +121,10 @@ export {
|
|
|
138
121
|
PROTOCOL_VERSION,
|
|
139
122
|
REQUIRED_CAPABILITIES,
|
|
140
123
|
type Command,
|
|
124
|
+
type ApplicationEvent,
|
|
125
|
+
type HostEvent,
|
|
141
126
|
type NativeEvent,
|
|
127
|
+
type WindowEvent,
|
|
142
128
|
type Operation,
|
|
143
129
|
type Limits,
|
|
144
130
|
type ErrorCode,
|
package/src/message-dialogs.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { ProtocolError } from './framing.ts'
|
|
2
|
-
import { keys, object, string } from './values.ts'
|
|
2
|
+
import { fields, keys, object, string } from './values.ts'
|
|
3
3
|
|
|
4
|
-
/** Live OS message dialogs per session. Each window owns at most one at a time. */
|
|
5
|
-
export const MAX_MESSAGE_DIALOGS = 16
|
|
6
4
|
export const MAX_MESSAGE_DIALOG_BUTTONS = 4
|
|
7
5
|
export const MAX_MESSAGE_DIALOG_TITLE_BYTES = 256
|
|
8
6
|
export const MAX_MESSAGE_DIALOG_MESSAGE_BYTES = 2_048
|
|
@@ -39,9 +37,11 @@ function text(value: unknown, field: string, limit: number, block: boolean): str
|
|
|
39
37
|
return result
|
|
40
38
|
}
|
|
41
39
|
|
|
40
|
+
const BUTTON_KEYS = fields('id', 'label')
|
|
41
|
+
|
|
42
42
|
function button(value: unknown): MessageDialogButton {
|
|
43
43
|
const data = object(value)
|
|
44
|
-
keys(data,
|
|
44
|
+
keys(data, BUTTON_KEYS)
|
|
45
45
|
return {
|
|
46
46
|
id: text(data['id'], 'message dialog button id', MAX_MESSAGE_DIALOG_ID_BYTES, false),
|
|
47
47
|
label: text(
|
|
@@ -53,17 +53,19 @@ function button(value: unknown): MessageDialogButton {
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
const MESSAGE_DIALOG_OPTIONS_KEYS = fields(
|
|
57
|
+
'title',
|
|
58
|
+
'message',
|
|
59
|
+
'detail',
|
|
60
|
+
'buttons',
|
|
61
|
+
'default_button',
|
|
62
|
+
'cancel_button',
|
|
63
|
+
'dismissible'
|
|
64
|
+
)
|
|
65
|
+
|
|
56
66
|
export function parseMessageDialogOptions(value: unknown): MessageDialogOptions {
|
|
57
67
|
const data = object(value)
|
|
58
|
-
keys(data,
|
|
59
|
-
'title',
|
|
60
|
-
'message',
|
|
61
|
-
'detail',
|
|
62
|
-
'buttons',
|
|
63
|
-
'default_button',
|
|
64
|
-
'cancel_button',
|
|
65
|
-
'dismissible',
|
|
66
|
-
])
|
|
68
|
+
keys(data, MESSAGE_DIALOG_OPTIONS_KEYS)
|
|
67
69
|
const list = data['buttons']
|
|
68
70
|
if (!Array.isArray(list) || list.length < 1 || list.length > MAX_MESSAGE_DIALOG_BUTTONS)
|
|
69
71
|
throw new ProtocolError('A message dialog needs between 1 and 4 buttons')
|
|
@@ -104,20 +106,24 @@ export function parseMessageDialogOptions(value: unknown): MessageDialogOptions
|
|
|
104
106
|
return options
|
|
105
107
|
}
|
|
106
108
|
|
|
109
|
+
const MESSAGE_DIALOG_RESULT_BUTTON_KEYS = fields('kind', 'id')
|
|
110
|
+
const MESSAGE_DIALOG_RESULT_DISMISSED_KEYS = fields('kind')
|
|
111
|
+
const MESSAGE_DIALOG_RESULT_FAILED_KEYS = fields('kind')
|
|
112
|
+
|
|
107
113
|
export function parseMessageDialogResult(value: unknown): MessageDialogResult {
|
|
108
114
|
const data = object(value)
|
|
109
115
|
switch (data['kind']) {
|
|
110
116
|
case 'button':
|
|
111
|
-
keys(data,
|
|
117
|
+
keys(data, MESSAGE_DIALOG_RESULT_BUTTON_KEYS)
|
|
112
118
|
return {
|
|
113
119
|
kind: 'button',
|
|
114
120
|
id: text(data['id'], 'message dialog result id', MAX_MESSAGE_DIALOG_ID_BYTES, false),
|
|
115
121
|
}
|
|
116
122
|
case 'dismissed':
|
|
117
|
-
keys(data,
|
|
123
|
+
keys(data, MESSAGE_DIALOG_RESULT_DISMISSED_KEYS)
|
|
118
124
|
return { kind: 'dismissed' }
|
|
119
125
|
case 'failed':
|
|
120
|
-
keys(data,
|
|
126
|
+
keys(data, MESSAGE_DIALOG_RESULT_FAILED_KEYS)
|
|
121
127
|
return { kind: 'failed' }
|
|
122
128
|
default:
|
|
123
129
|
throw new ProtocolError('Unknown message dialog result')
|