@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/processes.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ProtocolError } from './framing.ts'
|
|
2
2
|
import { parseChunk, parseFilePath, parseRootId, parseRootPath } from './files.ts'
|
|
3
|
-
import { id, integer, keys, object } from './values.ts'
|
|
3
|
+
import { fields, id, integer, keys, object } from './values.ts'
|
|
4
4
|
|
|
5
5
|
export const MAX_PROCESS_EXECUTABLES = 8
|
|
6
6
|
export const MAX_PROCESSES = 8
|
|
@@ -48,9 +48,11 @@ const LIMIT_BOUNDS = {
|
|
|
48
48
|
open_files: [MIN_PROCESS_OPEN_FILES, MAX_PROCESS_OPEN_FILES],
|
|
49
49
|
} as const
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
const PROCESS_LIMITS_KEYS = fields(...LIMIT_NAMES)
|
|
52
|
+
|
|
53
|
+
function parseProcessLimits(value: unknown): ProcessLimits {
|
|
52
54
|
const data = object(value)
|
|
53
|
-
keys(data,
|
|
55
|
+
keys(data, PROCESS_LIMITS_KEYS)
|
|
54
56
|
const limits: ProcessLimits = {}
|
|
55
57
|
for (const name of LIMIT_NAMES)
|
|
56
58
|
if (name in data) {
|
|
@@ -103,9 +105,11 @@ export type ProcessSignal = (typeof PROCESS_SIGNALS)[number]
|
|
|
103
105
|
export type ProcessStream = 'stdout' | 'stderr' | 'terminal'
|
|
104
106
|
export type ProcessExit = { code: number } | { signal: number }
|
|
105
107
|
|
|
108
|
+
const EXECUTABLE_KEYS = fields('id', 'executable', 'limits')
|
|
109
|
+
|
|
106
110
|
function executable(value: unknown): ProcessExecutable {
|
|
107
111
|
const data = object(value)
|
|
108
|
-
keys(data,
|
|
112
|
+
keys(data, EXECUTABLE_KEYS)
|
|
109
113
|
const path = parseRootPath(data['executable'])
|
|
110
114
|
// `/` and `X:\` are the only normalized paths ending in a separator.
|
|
111
115
|
if (path.endsWith('/') || path.endsWith('\\'))
|
|
@@ -169,9 +173,11 @@ function plain(value: unknown, limit: number, field: string): string {
|
|
|
169
173
|
return value
|
|
170
174
|
}
|
|
171
175
|
|
|
176
|
+
const TERMINAL_SIZE_KEYS = fields('columns', 'rows')
|
|
177
|
+
|
|
172
178
|
export function parseTerminalSize(value: unknown): TerminalSize {
|
|
173
179
|
const data = object(value)
|
|
174
|
-
keys(data,
|
|
180
|
+
keys(data, TERMINAL_SIZE_KEYS)
|
|
175
181
|
return {
|
|
176
182
|
columns: integer(data['columns'], 'terminal columns', 1, MAX_TERMINAL_DIMENSION),
|
|
177
183
|
rows: integer(data['rows'], 'terminal rows', 1, MAX_TERMINAL_DIMENSION),
|
|
@@ -186,9 +192,12 @@ function parseEnvName(value: string): string {
|
|
|
186
192
|
return value
|
|
187
193
|
}
|
|
188
194
|
|
|
195
|
+
const PROCESS_LAUNCH_KEYS = fields('executable', 'args', 'env', 'cwd', 'terminal')
|
|
196
|
+
const PROCESS_CWD_KEYS = fields('grant_id', 'path')
|
|
197
|
+
|
|
189
198
|
export function parseProcessLaunch(value: unknown): ProcessLaunch {
|
|
190
199
|
const data = object(value)
|
|
191
|
-
keys(data,
|
|
200
|
+
keys(data, PROCESS_LAUNCH_KEYS)
|
|
192
201
|
const args = data['args']
|
|
193
202
|
if (!Array.isArray(args) || args.length > MAX_PROCESS_ARGS)
|
|
194
203
|
throw new ProtocolError('Process arguments must be an array of at most ' + MAX_PROCESS_ARGS)
|
|
@@ -216,7 +225,7 @@ export function parseProcessLaunch(value: unknown): ProcessLaunch {
|
|
|
216
225
|
}
|
|
217
226
|
if ('cwd' in data) {
|
|
218
227
|
const cwd = object(data['cwd'])
|
|
219
|
-
keys(cwd,
|
|
228
|
+
keys(cwd, PROCESS_CWD_KEYS)
|
|
220
229
|
launch.cwd = { grant_id: id(cwd['grant_id']), path: parseFilePath(cwd['path']) }
|
|
221
230
|
}
|
|
222
231
|
if ('terminal' in data) launch.terminal = parseTerminalSize(data['terminal'])
|
|
@@ -241,9 +250,11 @@ export function parseProcessOutput(value: unknown): string {
|
|
|
241
250
|
return data
|
|
242
251
|
}
|
|
243
252
|
|
|
253
|
+
const PROCESS_EXIT_KEYS = fields('code', 'signal')
|
|
254
|
+
|
|
244
255
|
export function parseProcessExit(value: unknown): ProcessExit {
|
|
245
256
|
const data = object(value)
|
|
246
|
-
keys(data,
|
|
257
|
+
keys(data, PROCESS_EXIT_KEYS)
|
|
247
258
|
if ('code' in data && !('signal' in data))
|
|
248
259
|
return { code: integer(data['code'], 'exit code', 0, 255) }
|
|
249
260
|
if ('signal' in data && !('code' in data))
|
package/src/services.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ProtocolError } from './framing.ts'
|
|
2
|
-
import { keys, number, object, string } from './values.ts'
|
|
2
|
+
import { fields, keys, number, object, string } from './values.ts'
|
|
3
3
|
import { parseFilesystemRoots, type FilesystemRoot } from './files.ts'
|
|
4
4
|
import { parseProcessExecutables, type ProcessExecutable } from './processes.ts'
|
|
5
5
|
|
|
@@ -53,9 +53,11 @@ export interface ShortcutModifiers {
|
|
|
53
53
|
platform: boolean
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
const SHORTCUT_MODIFIERS_KEYS = fields('control', 'alt', 'shift', 'platform')
|
|
57
|
+
|
|
56
58
|
export function parseShortcutModifiers(value: unknown): ShortcutModifiers {
|
|
57
59
|
const data = object(value)
|
|
58
|
-
keys(data,
|
|
60
|
+
keys(data, SHORTCUT_MODIFIERS_KEYS)
|
|
59
61
|
const flag = (key: keyof ShortcutModifiers) => {
|
|
60
62
|
if (typeof data[key] !== 'boolean')
|
|
61
63
|
throw new ProtocolError('Global shortcut modifiers must state each modifier explicitly')
|
|
@@ -103,13 +105,16 @@ export function parseNotificationDismissal(value: unknown): NotificationDismissa
|
|
|
103
105
|
|
|
104
106
|
export type ClipboardText = { kind: 'empty' } | { kind: 'text'; text: string }
|
|
105
107
|
|
|
108
|
+
const CLIPBOARD_TEXT_EMPTY_KEYS = fields('kind')
|
|
109
|
+
const CLIPBOARD_TEXT_READ_KEYS = fields('kind', 'text')
|
|
110
|
+
|
|
106
111
|
export function parseClipboardText(value: unknown): ClipboardText {
|
|
107
112
|
const data = object(value)
|
|
108
113
|
if (data['kind'] === 'empty') {
|
|
109
|
-
keys(data,
|
|
114
|
+
keys(data, CLIPBOARD_TEXT_EMPTY_KEYS)
|
|
110
115
|
return { kind: 'empty' }
|
|
111
116
|
}
|
|
112
|
-
keys(data,
|
|
117
|
+
keys(data, CLIPBOARD_TEXT_READ_KEYS)
|
|
113
118
|
if (data['kind'] !== 'text') throw new ProtocolError('Unknown clipboard text result')
|
|
114
119
|
return { kind: 'text', text: string(data['text'], 'clipboard text', MAX_CLIPBOARD_BYTES) }
|
|
115
120
|
}
|
|
@@ -132,9 +137,11 @@ export interface DisplaySnapshot {
|
|
|
132
137
|
|
|
133
138
|
const DISPLAY_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
|
|
134
139
|
|
|
140
|
+
const DISPLAY_BOUNDS_KEYS = fields('x', 'y', 'width', 'height')
|
|
141
|
+
|
|
135
142
|
function displayBounds(value: unknown, name: string): DisplayBounds {
|
|
136
143
|
const bounds = object(value)
|
|
137
|
-
keys(bounds,
|
|
144
|
+
keys(bounds, DISPLAY_BOUNDS_KEYS)
|
|
138
145
|
return {
|
|
139
146
|
x: number(bounds['x'], name + ' x', -1_048_576, 1_048_576),
|
|
140
147
|
y: number(bounds['y'], name + ' y', -1_048_576, 1_048_576),
|
|
@@ -143,21 +150,23 @@ function displayBounds(value: unknown, name: string): DisplayBounds {
|
|
|
143
150
|
}
|
|
144
151
|
}
|
|
145
152
|
|
|
153
|
+
const SERVICE_POLICY_KEYS = fields(
|
|
154
|
+
'window_display',
|
|
155
|
+
'clipboard_read',
|
|
156
|
+
'clipboard_write',
|
|
157
|
+
'global_shortcuts',
|
|
158
|
+
'notifications',
|
|
159
|
+
'drag_drop',
|
|
160
|
+
'filesystem_roots',
|
|
161
|
+
'processes',
|
|
162
|
+
'file_dialogs',
|
|
163
|
+
'app_menu',
|
|
164
|
+
'task_dialogs'
|
|
165
|
+
)
|
|
166
|
+
|
|
146
167
|
export function parseServicePolicy(value: unknown): ServicePolicy {
|
|
147
168
|
const data = object(value)
|
|
148
|
-
keys(data,
|
|
149
|
-
'window_display',
|
|
150
|
-
'clipboard_read',
|
|
151
|
-
'clipboard_write',
|
|
152
|
-
'global_shortcuts',
|
|
153
|
-
'notifications',
|
|
154
|
-
'drag_drop',
|
|
155
|
-
'filesystem_roots',
|
|
156
|
-
'processes',
|
|
157
|
-
'file_dialogs',
|
|
158
|
-
'app_menu',
|
|
159
|
-
'task_dialogs',
|
|
160
|
-
])
|
|
169
|
+
keys(data, SERVICE_POLICY_KEYS)
|
|
161
170
|
const grant = (key: keyof ServicePolicy) => {
|
|
162
171
|
if (typeof data[key] !== 'boolean')
|
|
163
172
|
throw new ProtocolError('Native service policy must explicitly grant or deny each operation')
|
|
@@ -178,9 +187,11 @@ export function parseServicePolicy(value: unknown): ServicePolicy {
|
|
|
178
187
|
}
|
|
179
188
|
}
|
|
180
189
|
|
|
190
|
+
const DISPLAY_SNAPSHOT_KEYS = fields('bounds', 'scale_factor', 'visible_bounds', 'display_uuid')
|
|
191
|
+
|
|
181
192
|
export function parseDisplaySnapshot(value: unknown): DisplaySnapshot {
|
|
182
193
|
const data = object(value)
|
|
183
|
-
keys(data,
|
|
194
|
+
keys(data, DISPLAY_SNAPSHOT_KEYS)
|
|
184
195
|
const snapshot: DisplaySnapshot = {
|
|
185
196
|
bounds: displayBounds(data['bounds'], 'display'),
|
|
186
197
|
scale_factor: number(data['scale_factor'], 'display scale factor', 0.125, 16),
|
package/src/values.ts
CHANGED
|
@@ -10,9 +10,15 @@ export function object(value: unknown): Record<string, unknown> {
|
|
|
10
10
|
return value as Record<string, unknown>
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
/** A message's accepted property names, hoisted to module scope so validation allocates nothing. */
|
|
14
|
+
export function fields(...names: readonly string[]): ReadonlySet<string> {
|
|
15
|
+
return new Set(names)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Reject own enumerable properties outside `allowed`; inherited keys are ignored. */
|
|
19
|
+
export function keys(value: Record<string, unknown>, allowed: ReadonlySet<string>): void {
|
|
14
20
|
for (const key of Object.keys(value))
|
|
15
|
-
if (!allowed.
|
|
21
|
+
if (!allowed.has(key)) throw new ProtocolError('Unknown message property: ' + key)
|
|
16
22
|
}
|
|
17
23
|
|
|
18
24
|
export function number(value: unknown, field: string, min: number, max: number): number {
|