refactor(host): rename the directory-picker dialog backend and kind to native
The browse interaction also presents a dialog (the in-app modal), so 'dialog' failed to discriminate the two capability kinds; 'native' names where the chooser runs. Package directory-picker-dialog -> directory-picker-native, kind 'dialog' -> 'native', with every seam/gateway/client/doc reference updated and the seam Agent Note's naming rationale rewritten to match.
This commit is contained in:
6
packages/host/directory-picker-native/README.i18n.yaml
Normal file
6
packages/host/directory-picker-native/README.i18n.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/host/directory-picker-native/README.md
|
||||
README.md: 8e9d6c7c558a6b33c2a37d504c571fd3eda579bb
|
||||
README.zh.md: 68f23698ff0c1a5ee4456a1f121dcf244f09c72b
|
||||
17
packages/host/directory-picker-native/README.md
Normal file
17
packages/host/directory-picker-native/README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# @deepseek-ai/dsh-host-directory-picker-native
|
||||
|
||||
English | [中文](README.zh.md)
|
||||
|
||||
The **native-OS-chooser backend** of the [directory-picker seam](../directory-picker/README.md): `NativeDirectoryPicker` registers `ctx.directoryPicker` with the `native` capability, whose `pick(signal)` opens one native chooser per call and resolves the chosen absolute path (`null` on cancel). Platform tools run without a shell: `osascript` on macOS, an STA PowerShell `FolderBrowserDialog` on Windows, and Zenity with a KDialog fallback on Linux; the caller's abort terminates the native process. Only viable when the operator sits at the host's display — remote deployments compose [`-browse`](../directory-picker-browse/README.md) instead. The command boundary (`DirectoryPickerRunner`) and platform facts are injectable for deterministic tests.
|
||||
|
||||
## Model Experience
|
||||
|
||||
None, as the backend serves the GUI host's directory selection; nothing here reaches a model request.
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
None; this package neither assembles nor sends a provider request.
|
||||
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
- **Linux requires desktop tooling** — with neither Zenity nor KDialog installed, `pick` rejects with an actionable error; it does not fall back to a typed-path prompt (the browse backend is that fallback at the composition level).
|
||||
17
packages/host/directory-picker-native/README.zh.md
Normal file
17
packages/host/directory-picker-native/README.zh.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# @deepseek-ai/dsh-host-directory-picker-native
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
[目录选择 seam](../directory-picker/README.md) 的**原生 OS 选择器后端**:`NativeDirectoryPicker` 以 `native` 能力注册 `ctx.directoryPicker`,其 `pick(signal)` 每次调用打开一个原生选择器并解析出所选绝对路径(取消时为 `null`)。平台工具不经 shell 调用:macOS 使用 `osascript`,Windows 使用以 STA 模式运行的 PowerShell `FolderBrowserDialog`,Linux 使用 Zenity 并以 KDialog 回退;调用方的中止信号会终止原生进程。只有操作者坐在宿主屏幕前时才可用——远程部署应组合 [`-browse`](../directory-picker-browse/README.md)。命令边界(`DirectoryPickerRunner`)与平台事实可注入,便于确定性测试。
|
||||
|
||||
## 模型体验
|
||||
|
||||
无。该后端服务于 GUI 宿主的目录选择;这里没有任何内容进入模型请求。
|
||||
|
||||
#### KV 缓存影响
|
||||
|
||||
无;该包既不组装也不发送提供方请求。
|
||||
|
||||
## 已知限制与延期工作
|
||||
|
||||
- **Linux 依赖桌面工具**——Zenity 与 KDialog 均未安装时,`pick` 以包含解决建议的错误拒绝;它不会回退为手输路径提示(组合层面的回退是 browse 后端)。
|
||||
40
packages/host/directory-picker-native/package.json
Normal file
40
packages/host/directory-picker-native/package.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "@deepseek-ai/dsh-host-directory-picker-native",
|
||||
"description": "Native-OS-chooser backend of the directory-picker seam for the DeepSeek Harness web GUI host",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/types/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./lib/types/index.d.ts",
|
||||
"default": "./lib/index.js"
|
||||
},
|
||||
"./invariant": {
|
||||
"types": "./lib/types/invariant.d.ts",
|
||||
"default": "./lib/invariant.js"
|
||||
},
|
||||
"./src/*": "./src/*",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
"lib/invariant.js",
|
||||
"lib/types/**/*.d.ts",
|
||||
"lib/types/**/*.d.ts.map",
|
||||
"src"
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@deepseek-ai/dsh-host-directory-picker": "workspace:^"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-invariants": "^0.0.1",
|
||||
"cordis": "^4.0.0-rc.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"cordis": "^4.0.0-rc.7"
|
||||
}
|
||||
}
|
||||
33
packages/host/directory-picker-native/src/index.ts
Normal file
33
packages/host/directory-picker-native/src/index.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Native backend of the directory-picker seam: registers `ctx.directoryPicker`
|
||||
* with the `native` capability, opening one native OS chooser on the host
|
||||
* display per pick (macOS `osascript`, Windows STA PowerShell
|
||||
* `FolderBrowserDialog`, Linux Zenity with a KDialog fallback). Only viable
|
||||
* when the operator sits at the host's screen; remote deployments compose the
|
||||
* browse backend instead.
|
||||
* @module @deepseek-ai/dsh-host-directory-picker-native
|
||||
*/
|
||||
|
||||
import { DirectoryPicker } from '@deepseek-ai/dsh-host-directory-picker'
|
||||
import type { DirectoryPickerCapability } from '@deepseek-ai/dsh-host-directory-picker'
|
||||
import { pickNativeDirectory } from './native-picker.ts'
|
||||
|
||||
export type { DirectoryPickerInternals, DirectoryPickerRunner } from './native-picker.ts'
|
||||
export { pickNativeDirectory } from './native-picker.ts'
|
||||
|
||||
/** The `ctx.directoryPicker` native implementation (stable capability object per service life). */
|
||||
export default class NativeDirectoryPicker extends DirectoryPicker {
|
||||
private readonly nativeCapability: DirectoryPickerCapability = {
|
||||
kind: 'native',
|
||||
/* v8 ignore next -- pure forward to pickNativeDirectory (its spec owns behavior); invoking here opens a real chooser. */
|
||||
pick: signal => pickNativeDirectory(signal),
|
||||
}
|
||||
|
||||
/**
|
||||
* The native interaction capability.
|
||||
* @returns the stable `native` capability object.
|
||||
*/
|
||||
capability(): DirectoryPickerCapability {
|
||||
return this.nativeCapability
|
||||
}
|
||||
}
|
||||
25
packages/host/directory-picker-native/src/invariant.ts
Normal file
25
packages/host/directory-picker-native/src/invariant.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Package-owned invariant companion for the native directory-picker backend.
|
||||
* @module @deepseek-ai/dsh-host-directory-picker-native/invariant
|
||||
*/
|
||||
|
||||
import type { Context } from 'cordis'
|
||||
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
|
||||
|
||||
const PACKAGE_NAME = '@deepseek-ai/dsh-host-directory-picker-native'
|
||||
|
||||
/** Cordis companion plugin name. */
|
||||
export const name = 'host-directory-picker-native-invariant'
|
||||
/** Service required before the companion can reserve package ownership. */
|
||||
export const inject = ['invariants']
|
||||
|
||||
/** No runtime invariant: each pick is one stateless subprocess round trip; the chooser outcome is only the returned path. */
|
||||
const install: InvariantInstaller = () => {}
|
||||
|
||||
/**
|
||||
* Register the native directory-picker invariant companion.
|
||||
* @param ctx - Cordis context carrying the invariant service.
|
||||
* @returns the installed registration's disposer after setup succeeds.
|
||||
*/
|
||||
export const apply = (ctx: Context): Promise<() => void> =>
|
||||
Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install))
|
||||
136
packages/host/directory-picker-native/src/native-picker.ts
Normal file
136
packages/host/directory-picker-native/src/native-picker.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
/** Cross-platform native single-directory chooser behind the dialog backend's capability. */
|
||||
|
||||
import { execFile } from 'node:child_process'
|
||||
|
||||
/** Testable command boundary; native implementations never invoke a shell. */
|
||||
export type DirectoryPickerRunner = (
|
||||
command: string,
|
||||
args: readonly string[],
|
||||
signal: AbortSignal,
|
||||
) => Promise<{ stdout: string; stderr: string }>
|
||||
|
||||
/** Injectable platform facts for deterministic adapter tests. */
|
||||
export interface DirectoryPickerInternals {
|
||||
platform?: NodeJS.Platform
|
||||
run?: DirectoryPickerRunner
|
||||
}
|
||||
|
||||
const runCommand: DirectoryPickerRunner = (command, args, signal) =>
|
||||
new Promise((resolve, reject) => {
|
||||
execFile(
|
||||
command,
|
||||
[...args],
|
||||
{ encoding: 'utf8', signal, windowsHide: true },
|
||||
(error, stdout, stderr) => {
|
||||
if (error !== null) {
|
||||
const failure = Object.assign(new Error(error.message, { cause: error }), {
|
||||
code: error.code,
|
||||
stdout,
|
||||
stderr,
|
||||
})
|
||||
reject(failure)
|
||||
return
|
||||
}
|
||||
resolve({ stdout, stderr })
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
function outputPath(stdout: string): string | null {
|
||||
const path = stdout.replace(/[\r\n]+$/, '')
|
||||
return path === '' ? null : path
|
||||
}
|
||||
|
||||
function errorCode(error: unknown): string | number | undefined {
|
||||
if (typeof error !== 'object' || error === null || !('code' in error)) return undefined
|
||||
const code = (error as { code?: unknown }).code
|
||||
return typeof code === 'string' || typeof code === 'number' ? code : undefined
|
||||
}
|
||||
|
||||
function errorStderr(error: unknown): string {
|
||||
if (typeof error !== 'object' || error === null || !('stderr' in error)) return ''
|
||||
const stderr = (error as { stderr?: unknown }).stderr
|
||||
return typeof stderr === 'string' ? stderr : ''
|
||||
}
|
||||
|
||||
function isMissingCommand(error: unknown): boolean {
|
||||
return errorCode(error) === 'ENOENT'
|
||||
}
|
||||
|
||||
function rethrowIfAborted(signal: AbortSignal, error: unknown): void {
|
||||
if (signal.aborted) throw error
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the platform directory picker.
|
||||
* @param signal - caller/connection lifetime; abort terminates the native command.
|
||||
* @param internals - platform and runner seam for deterministic tests.
|
||||
* @returns the selected path, or null when the user cancels.
|
||||
*/
|
||||
export async function pickNativeDirectory(
|
||||
signal: AbortSignal,
|
||||
internals: DirectoryPickerInternals = {},
|
||||
): Promise<string | null> {
|
||||
const platform = internals.platform ?? process.platform
|
||||
const run = internals.run ?? runCommand
|
||||
|
||||
if (platform === 'darwin') {
|
||||
try {
|
||||
const result = await run('osascript', [
|
||||
'-e', 'set selectedFolder to choose folder with prompt "Select Workspace Directory"',
|
||||
'-e', 'POSIX path of selectedFolder',
|
||||
], signal)
|
||||
return outputPath(result.stdout)
|
||||
} catch (error: unknown) {
|
||||
if (!signal.aborted && errorCode(error) === 1
|
||||
&& /(?:User canceled|-128)/i.test(errorStderr(error))) return null
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
if (platform === 'win32') {
|
||||
const script = [
|
||||
"$ErrorActionPreference = 'Stop'",
|
||||
'Add-Type -AssemblyName System.Windows.Forms',
|
||||
'$dialog = New-Object System.Windows.Forms.FolderBrowserDialog',
|
||||
"$dialog.Description = 'Select Workspace Directory'",
|
||||
'$dialog.ShowNewFolderButton = $true',
|
||||
'$result = $dialog.ShowDialog()',
|
||||
'if ($result -eq [System.Windows.Forms.DialogResult]::OK) {',
|
||||
' [Console]::OutputEncoding = [System.Text.Encoding]::UTF8',
|
||||
' [Console]::WriteLine($dialog.SelectedPath)',
|
||||
'}',
|
||||
].join('; ')
|
||||
const result = await run('powershell.exe', ['-NoProfile', '-STA', '-Command', script], signal)
|
||||
return outputPath(result.stdout)
|
||||
}
|
||||
|
||||
if (platform === 'linux') {
|
||||
try {
|
||||
const result = await run('zenity', [
|
||||
'--file-selection', '--directory', '--title=Select Workspace Directory',
|
||||
], signal)
|
||||
return outputPath(result.stdout)
|
||||
} catch (error: unknown) {
|
||||
rethrowIfAborted(signal, error)
|
||||
if (errorCode(error) === 1) return null
|
||||
if (!isMissingCommand(error)) throw error
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await run('kdialog', [
|
||||
'--getexistingdirectory', '.', '--title', 'Select Workspace Directory',
|
||||
], signal)
|
||||
return outputPath(result.stdout)
|
||||
} catch (error: unknown) {
|
||||
rethrowIfAborted(signal, error)
|
||||
if (errorCode(error) === 1) return null
|
||||
if (isMissingCommand(error)) {
|
||||
throw new Error('no supported native directory picker found (install zenity or kdialog)')
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`native directory picker is unsupported on ${platform}`)
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
type ExecFileCallback = (
|
||||
error: (Error & { code?: string | number }) | null,
|
||||
stdout: string,
|
||||
stderr: string,
|
||||
) => void
|
||||
type ExecFileMock = (
|
||||
command: string,
|
||||
args: readonly string[],
|
||||
options: { encoding: string; signal: AbortSignal; windowsHide: boolean },
|
||||
callback: ExecFileCallback,
|
||||
) => void
|
||||
|
||||
const { execFileMock } = vi.hoisted(() => ({ execFileMock: vi.fn<ExecFileMock>() }))
|
||||
|
||||
vi.mock('node:child_process', () => ({ execFile: execFileMock }))
|
||||
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { pickNativeDirectory, type DirectoryPickerRunner } from '../src/native-picker.ts'
|
||||
|
||||
function failure(code: string | number, stderr = ''): Error {
|
||||
return Object.assign(new Error(`command failed: ${String(code)}`), { code, stderr })
|
||||
}
|
||||
|
||||
const signal = () => new AbortController().signal
|
||||
|
||||
describe('native directory picker', () => {
|
||||
it('uses the macOS folder chooser and maps user cancellation to null', async () => {
|
||||
const run = vi.fn<DirectoryPickerRunner>(async () => ({ stdout: '/Users/test/project/\n', stderr: '' }))
|
||||
await expect(pickNativeDirectory(signal(), { platform: 'darwin', run })).resolves.toBe('/Users/test/project/')
|
||||
expect(run).toHaveBeenCalledWith('osascript', expect.arrayContaining(['POSIX path of selectedFolder']), expect.any(AbortSignal))
|
||||
|
||||
run.mockRejectedValueOnce(failure(1, 'execution error: User canceled. (-128)'))
|
||||
await expect(pickNativeDirectory(signal(), { platform: 'darwin', run })).resolves.toBeNull()
|
||||
|
||||
run.mockRejectedValueOnce(failure(2, 'permission denied'))
|
||||
await expect(pickNativeDirectory(signal(), { platform: 'darwin', run })).rejects.toThrow('command failed')
|
||||
})
|
||||
|
||||
it.each([
|
||||
['a primitive error', 'failed'],
|
||||
['an invalid code type', { code: true }],
|
||||
['a missing stderr property', { code: 1 }],
|
||||
['a non-string stderr property', { code: 1, stderr: 42 }],
|
||||
])('does not mistake %s for macOS cancellation', async (_label, reason) => {
|
||||
const run = vi.fn<DirectoryPickerRunner>(async () => { throw reason })
|
||||
await expect(pickNativeDirectory(signal(), { platform: 'darwin', run })).rejects.toBe(reason)
|
||||
})
|
||||
|
||||
it('uses the Windows STA folder dialog and maps empty output to cancellation', async () => {
|
||||
const run = vi.fn<DirectoryPickerRunner>(async () => ({ stdout: 'C:\\work\\project\r\n', stderr: '' }))
|
||||
await expect(pickNativeDirectory(signal(), { platform: 'win32', run })).resolves.toBe('C:\\work\\project')
|
||||
expect(run).toHaveBeenCalledWith(
|
||||
'powershell.exe',
|
||||
expect.arrayContaining(['-NoProfile', '-STA', '-Command']),
|
||||
expect.any(AbortSignal),
|
||||
)
|
||||
expect(run.mock.calls[0]?.[1].at(-1)).toContain("$ErrorActionPreference = 'Stop'")
|
||||
run.mockResolvedValueOnce({ stdout: '', stderr: '' })
|
||||
await expect(pickNativeDirectory(signal(), { platform: 'win32', run })).resolves.toBeNull()
|
||||
run.mockRejectedValueOnce(failure(1, 'Add-Type failed'))
|
||||
await expect(pickNativeDirectory(signal(), { platform: 'win32', run })).rejects.toThrow('command failed')
|
||||
})
|
||||
|
||||
it('runs the default command adapter without a shell and preserves command failures', async () => {
|
||||
execFileMock.mockImplementationOnce((_command, _args, _options, callback) => {
|
||||
callback(null, 'C:\\work\\default\r\n', '')
|
||||
})
|
||||
await expect(pickNativeDirectory(signal(), { platform: 'win32' })).resolves.toBe('C:\\work\\default')
|
||||
const [command, args, options] = execFileMock.mock.calls[0]!
|
||||
expect(command).toBe('powershell.exe')
|
||||
expect(args).toEqual(expect.arrayContaining(['-NoProfile', '-STA', '-Command']))
|
||||
expect(options.encoding).toBe('utf8')
|
||||
expect(options.windowsHide).toBe(true)
|
||||
expect(options.signal).toBeInstanceOf(AbortSignal)
|
||||
|
||||
const commandError = Object.assign(new Error('powershell failed'), { code: 7 })
|
||||
execFileMock.mockImplementationOnce((_command, _args, _options, callback) => {
|
||||
callback(commandError, 'partial output', 'failure details')
|
||||
})
|
||||
await expect(pickNativeDirectory(signal(), { platform: 'win32' })).rejects.toMatchObject({
|
||||
message: 'powershell failed', cause: commandError, code: 7,
|
||||
stdout: 'partial output', stderr: 'failure details',
|
||||
})
|
||||
})
|
||||
|
||||
it('uses the current process platform when no platform override is supplied', async () => {
|
||||
const run = vi.fn<DirectoryPickerRunner>(async () => ({ stdout: '/default/platform\n', stderr: '' }))
|
||||
await expect(pickNativeDirectory(signal(), { run })).resolves.toBe('/default/platform')
|
||||
})
|
||||
|
||||
it('uses Zenity on Linux and falls back to KDialog only when Zenity is missing', async () => {
|
||||
const run = vi.fn<DirectoryPickerRunner>()
|
||||
.mockRejectedValueOnce(failure('ENOENT'))
|
||||
.mockResolvedValueOnce({ stdout: '/home/test/project\n', stderr: '' })
|
||||
await expect(pickNativeDirectory(signal(), { platform: 'linux', run })).resolves.toBe('/home/test/project')
|
||||
expect(run.mock.calls.map(call => call[0])).toEqual(['zenity', 'kdialog'])
|
||||
|
||||
const zenity = vi.fn<DirectoryPickerRunner>(async () => ({ stdout: '/home/test/direct\n', stderr: '' }))
|
||||
await expect(pickNativeDirectory(signal(), { platform: 'linux', run: zenity }))
|
||||
.resolves.toBe('/home/test/direct')
|
||||
expect(zenity).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('maps Linux cancellation to null and reports a missing desktop picker', async () => {
|
||||
const cancelled = vi.fn<DirectoryPickerRunner>(async () => { throw failure(1) })
|
||||
await expect(pickNativeDirectory(signal(), { platform: 'linux', run: cancelled })).resolves.toBeNull()
|
||||
|
||||
const missing = vi.fn<DirectoryPickerRunner>(async () => { throw failure('ENOENT') })
|
||||
await expect(pickNativeDirectory(signal(), { platform: 'linux', run: missing }))
|
||||
.rejects.toThrow('install zenity or kdialog')
|
||||
|
||||
const kdialogCancelled = vi.fn<DirectoryPickerRunner>()
|
||||
.mockRejectedValueOnce(failure('ENOENT'))
|
||||
.mockRejectedValueOnce(failure(1))
|
||||
await expect(pickNativeDirectory(signal(), { platform: 'linux', run: kdialogCancelled }))
|
||||
.resolves.toBeNull()
|
||||
|
||||
const zenityFailed = vi.fn<DirectoryPickerRunner>(async () => { throw failure(2) })
|
||||
await expect(pickNativeDirectory(signal(), { platform: 'linux', run: zenityFailed }))
|
||||
.rejects.toThrow('command failed')
|
||||
|
||||
const kdialogFailed = vi.fn<DirectoryPickerRunner>()
|
||||
.mockRejectedValueOnce(failure('ENOENT'))
|
||||
.mockRejectedValueOnce(failure(2))
|
||||
await expect(pickNativeDirectory(signal(), { platform: 'linux', run: kdialogFailed }))
|
||||
.rejects.toThrow('command failed')
|
||||
})
|
||||
|
||||
it('does not convert caller aborts into user cancellation', async () => {
|
||||
const abort = new AbortController()
|
||||
abort.abort(new Error('closed'))
|
||||
const run = vi.fn<DirectoryPickerRunner>(async () => { throw failure('ABORT_ERR') })
|
||||
await expect(pickNativeDirectory(abort.signal, { platform: 'linux', run })).rejects.toThrow('command failed')
|
||||
})
|
||||
|
||||
it('reports unsupported platforms', async () => {
|
||||
await expect(pickNativeDirectory(signal(), { platform: 'aix' })).rejects.toThrow('unsupported on aix')
|
||||
})
|
||||
})
|
||||
21
packages/host/directory-picker-native/tests/service.spec.ts
Normal file
21
packages/host/directory-picker-native/tests/service.spec.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/** Registration/capability behavior of the native backend (the seam's cordis half). */
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import NativeDirectoryPicker from '../src/index.ts'
|
||||
|
||||
describe('NativeDirectoryPicker', () => {
|
||||
it('registers ctx.directoryPicker with a stable native capability and leaves with its fiber', async () => {
|
||||
const ctx = new Context()
|
||||
const fiber = ctx.plugin(NativeDirectoryPicker)
|
||||
await fiber.await()
|
||||
const picker = ctx.get('directoryPicker')
|
||||
expect(picker).toBeInstanceOf(NativeDirectoryPicker)
|
||||
const capability = picker!.capability()
|
||||
expect(capability.kind).toBe('native')
|
||||
// Stability: consumers may capture the capability object across calls.
|
||||
expect(picker!.capability()).toBe(capability)
|
||||
await fiber.dispose()
|
||||
expect(ctx.get('directoryPicker')).toBeUndefined()
|
||||
})
|
||||
})
|
||||
24
packages/host/directory-picker-native/tsconfig.json
Normal file
24
packages/host/directory-picker-native/tsconfig.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "lib/types"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../../vendor/cosmokit"
|
||||
},
|
||||
{
|
||||
"path": "../../../vendor/cordis"
|
||||
},
|
||||
{
|
||||
"path": "../directory-picker"
|
||||
},
|
||||
{
|
||||
"path": "../../support/invariants"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user