goke 6.4.0 → 6.5.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/src/goke.ts CHANGED
@@ -10,12 +10,13 @@
10
10
  * - Utility functions: string helpers, bracket parsing, dot-prop access
11
11
  */
12
12
 
13
- import pc from 'picocolors'
13
+ import pc from './picocolors.js'
14
14
  import mri from "./mri.js"
15
15
  import { GokeError, coerceBySchema, extractJsonSchema, extractSchemaMetadata, isStandardSchema } from "./coerce.js"
16
16
  import type { StandardJSONSchemaV1 } from "./coerce.js"
17
17
  import { createJustBashCommand as createJustBashCommandBridge } from './just-bash.js'
18
- import { EventEmitter, openInBrowser, process } from '#runtime'
18
+ import type { GokeFs } from './goke-fs.js'
19
+ import { EventEmitter, fs as runtimeFs, openInBrowser, process } from '#runtime'
19
20
 
20
21
  // ─── Node.js platform constants ───
21
22
 
@@ -210,7 +211,8 @@ const getFileName = (input: string) => {
210
211
  const isPromiseLike = (value: unknown): value is PromiseLike<unknown> =>
211
212
  value != null
212
213
  && (typeof value === 'object' || typeof value === 'function')
213
- && typeof (value as any).then === 'function'
214
+ && 'then' in value
215
+ && typeof value.then === 'function'
214
216
 
215
217
  const camelcaseOptionName = (name: string) => {
216
218
  // Camelcase the option name
@@ -813,6 +815,9 @@ interface GokeConsole {
813
815
 
814
816
  interface GokeProcess {
815
817
  argv: string[]
818
+ cwd: string
819
+ env: Record<string, string | undefined>
820
+ stdin: string
816
821
  stdout: GokeOutputStream
817
822
  stderr: GokeOutputStream
818
823
  exit(code: number): never | void
@@ -820,6 +825,7 @@ interface GokeProcess {
820
825
 
821
826
  interface GokeExecutionContext {
822
827
  console: GokeConsole
828
+ fs: GokeFs
823
829
  process: GokeProcess
824
830
  }
825
831
 
@@ -837,6 +843,14 @@ class GokeProcessExit extends Error {
837
843
  * Options for configuring a Goke CLI instance.
838
844
  */
839
845
  interface GokeOptions {
846
+ /** Custom cwd value exposed through the injected process context. */
847
+ cwd?: string
848
+ /** Custom environment exposed through the injected process context. */
849
+ env?: Record<string, string | undefined>
850
+ /** Custom fs implementation. Defaults to node:fs/promises in Node runtimes. */
851
+ fs?: GokeFs
852
+ /** Custom stdin content exposed through the injected process context. */
853
+ stdin?: string
840
854
  /** Custom stdout stream. Defaults to process.stdout */
841
855
  stdout?: GokeOutputStream
842
856
  /** Custom stderr stream. Defaults to process.stderr */
@@ -930,6 +944,14 @@ class Goke<Opts extends Record<string, any> = {}> extends EventEmitter {
930
944
  showHelpOnExit?: boolean
931
945
  showVersionOnExit?: boolean
932
946
 
947
+ /** Working directory exposed through the injected process context. */
948
+ readonly cwd?: string
949
+ /** Environment exposed through the injected process context. */
950
+ readonly env?: Record<string, string | undefined>
951
+ /** Output stream for normal output (help, version, etc.) */
952
+ readonly fs: GokeFs
953
+ /** Standard input exposed through the injected process context. */
954
+ readonly stdin?: string
933
955
  /** Output stream for normal output (help, version, etc.) */
934
956
  readonly stdout: GokeOutputStream
935
957
  /** Output stream for error output */
@@ -955,6 +977,10 @@ class Goke<Opts extends Record<string, any> = {}> extends EventEmitter {
955
977
  this.rawArgs = []
956
978
  this.args = []
957
979
  this.options = {}
980
+ this.cwd = options?.cwd
981
+ this.env = options?.env
982
+ this.fs = options?.fs ?? runtimeFs
983
+ this.stdin = options?.stdin
958
984
  this.stdout = options?.stdout ?? process.stdout
959
985
  this.stderr = options?.stderr ?? process.stderr
960
986
  this.console = createConsole(this.stdout, this.stderr)
@@ -967,6 +993,10 @@ class Goke<Opts extends Record<string, any> = {}> extends EventEmitter {
967
993
 
968
994
  clone(options?: GokeOptions) {
969
995
  const cloned = new Goke<Opts>(this.name, {
996
+ cwd: options?.cwd ?? this.cwd,
997
+ env: options?.env ?? this.env,
998
+ fs: options?.fs ?? this.fs,
999
+ stdin: options?.stdin ?? this.stdin,
970
1000
  stdout: options?.stdout ?? this.stdout,
971
1001
  stderr: options?.stderr ?? this.stderr,
972
1002
  argv: options?.argv ?? this.#defaultArgv,
@@ -995,8 +1025,12 @@ class Goke<Opts extends Record<string, any> = {}> extends EventEmitter {
995
1025
  private createExecutionContext(argv = this.rawArgs): GokeExecutionContext {
996
1026
  return {
997
1027
  console: this.console,
1028
+ fs: this.fs,
998
1029
  process: {
999
1030
  argv,
1031
+ cwd: this.cwd ?? process.cwd(),
1032
+ env: this.env ?? process.env,
1033
+ stdin: this.stdin ?? '',
1000
1034
  stdout: this.stdout,
1001
1035
  stderr: this.stderr,
1002
1036
  exit: (code: number) => {
@@ -1045,7 +1079,8 @@ class Goke<Opts extends Record<string, any> = {}> extends EventEmitter {
1045
1079
  >(rawName: RawName, schema: S): Goke<Opts & OptionEntry<RawName, S>>
1046
1080
  option(rawName: string, descriptionOrSchema?: string | StandardJSONSchemaV1): this
1047
1081
  option(rawName: string, descriptionOrSchema?: string | StandardJSONSchemaV1): any {
1048
- this.globalCommand.option(rawName, descriptionOrSchema as any)
1082
+ const option = new Option(rawName, descriptionOrSchema)
1083
+ this.globalCommand.options.push(option)
1049
1084
  return this
1050
1085
  }
1051
1086
 
@@ -1568,6 +1603,6 @@ class Goke<Opts extends Record<string, any> = {}> extends EventEmitter {
1568
1603
 
1569
1604
  // ─── Exports ───
1570
1605
 
1571
- export type { GokeOutputStream, GokeConsole, GokeOptions, GokeProcess, GokeExecutionContext }
1606
+ export type { GokeOutputStream, GokeConsole, GokeOptions, GokeProcess, GokeExecutionContext, GokeFs }
1572
1607
  export { createConsole, Command, GokeProcessExit, openInBrowser }
1573
1608
  export default Goke
package/src/index.ts CHANGED
@@ -11,6 +11,6 @@ const goke = (name = '', options?: GokeOptions) => new Goke(name, options)
11
11
  export default goke
12
12
  export { goke, Goke, Command }
13
13
  export { createConsole, GokeProcessExit, openInBrowser } from "./goke.js"
14
- export type { GokeOutputStream, GokeConsole, GokeExecutionContext, GokeOptions, GokeProcess } from "./goke.js"
14
+ export type { GokeOutputStream, GokeConsole, GokeExecutionContext, GokeFs, GokeOptions, GokeProcess } from "./goke.js"
15
15
  export type { StandardTypedV1, StandardJSONSchemaV1, JsonSchema } from "./coerce.js"
16
16
  export { GokeError, coerceBySchema, extractJsonSchema, wrapJsonSchema, isStandardSchema, extractSchemaMetadata } from "./coerce.js"
package/src/just-bash.ts CHANGED
@@ -6,8 +6,13 @@
6
6
  * - https://github.com/vercel-labs/just-bash/blob/main/src/types.ts
7
7
  */
8
8
 
9
+ import { Buffer } from 'node:buffer'
10
+ import { fileURLToPath } from 'node:url'
11
+ import type { PathLike } from 'node:fs'
12
+ import type { CommandContext, IFileSystem } from 'just-bash'
9
13
  import Goke, { GokeProcessExit } from './goke.js'
10
14
  import type { GokeOutputStream } from './goke.js'
15
+ import type { GokeFs, GokeFsEncodingOption, GokeFsFileContent } from './goke-fs.js'
11
16
 
12
17
  interface JustBashExecResult {
13
18
  stdout: string
@@ -18,9 +23,12 @@ interface JustBashExecResult {
18
23
  interface JustBashCommand {
19
24
  name: string
20
25
  trusted: true
21
- execute(args: string[]): Promise<JustBashExecResult>
26
+ execute(args: string[], context?: JustBashExecutionContext): Promise<JustBashExecResult>
22
27
  }
23
28
 
29
+ type JustBashExecutionContext = Pick<CommandContext, 'cwd' | 'env' | 'fs' | 'stdin'>
30
+ type JustBashEncoding = 'utf8' | 'utf-8' | 'ascii' | 'binary' | 'base64' | 'hex' | 'latin1'
31
+
24
32
  function createTextCaptureStream(): GokeOutputStream & { readonly text: string } {
25
33
  const chunks: string[] = []
26
34
  return {
@@ -33,6 +41,179 @@ function createTextCaptureStream(): GokeOutputStream & { readonly text: string }
33
41
  }
34
42
  }
35
43
 
44
+ const resolveJustBashPath = (fs: IFileSystem, cwd: string, path: PathLike) => {
45
+ if (path instanceof URL) {
46
+ return fileURLToPath(path)
47
+ }
48
+ return fs.resolvePath(cwd, path.toString())
49
+ }
50
+
51
+ const getEncoding = (options?: GokeFsEncodingOption) => {
52
+ if (typeof options === 'string' || options == null) {
53
+ return options
54
+ }
55
+ return options.encoding
56
+ }
57
+
58
+ const toJustBashEncoding = (encoding?: BufferEncoding | null): JustBashEncoding | null | undefined => {
59
+ if (encoding == null) {
60
+ return encoding
61
+ }
62
+
63
+ switch (encoding) {
64
+ case 'utf8':
65
+ case 'utf-8':
66
+ case 'ascii':
67
+ case 'binary':
68
+ case 'base64':
69
+ case 'hex':
70
+ case 'latin1':
71
+ return encoding
72
+ default:
73
+ throw new Error(`Encoding ${encoding} is not supported by the JustBash fs adapter`)
74
+ }
75
+ }
76
+
77
+ const toJustBashContent = (content: GokeFsFileContent) => {
78
+ if (typeof content === 'string' || content instanceof Uint8Array) {
79
+ return content
80
+ }
81
+ return new Uint8Array(content.buffer, content.byteOffset, content.byteLength)
82
+ }
83
+
84
+ const toDate = (value: Date | string | number) => {
85
+ const date = value instanceof Date ? value : new Date(value)
86
+ if (Number.isNaN(date.getTime())) {
87
+ throw new Error(`Invalid time value: ${String(value)}`)
88
+ }
89
+ return date
90
+ }
91
+
92
+ function createJustBashEnvProxy(env: Map<string, string>): Record<string, string | undefined> {
93
+ return new Proxy(Object.create(null) as Record<string, string | undefined>, {
94
+ deleteProperty(_target, property) {
95
+ if (typeof property === 'string') {
96
+ env.delete(property)
97
+ }
98
+ return true
99
+ },
100
+ get(_target, property) {
101
+ if (typeof property !== 'string') return undefined
102
+ return env.get(property)
103
+ },
104
+ getOwnPropertyDescriptor(_target, property) {
105
+ if (typeof property !== 'string') return undefined
106
+ const value = env.get(property)
107
+ if (value === undefined) return undefined
108
+ return {
109
+ configurable: true,
110
+ enumerable: true,
111
+ value,
112
+ writable: true,
113
+ }
114
+ },
115
+ has(_target, property) {
116
+ return typeof property === 'string' && env.has(property)
117
+ },
118
+ ownKeys() {
119
+ return [...env.keys()]
120
+ },
121
+ set(_target, property, value) {
122
+ if (typeof property === 'string') {
123
+ if (value === undefined) {
124
+ env.delete(property)
125
+ } else {
126
+ env.set(property, String(value))
127
+ }
128
+ }
129
+ return true
130
+ },
131
+ })
132
+ }
133
+
134
+ function createJustBashFs(fs: IFileSystem, cwd: string): GokeFs {
135
+ const readFile: GokeFs['readFile'] = async (path, options) => {
136
+ const resolvedPath = resolveJustBashPath(fs, cwd, path)
137
+ const encoding = toJustBashEncoding(getEncoding(options))
138
+ if (encoding == null) {
139
+ return Buffer.from(await fs.readFileBuffer(resolvedPath))
140
+ }
141
+ return fs.readFile(resolvedPath, encoding)
142
+ }
143
+
144
+ const writeFile: GokeFs['writeFile'] = async (path, content, options) => {
145
+ const resolvedPath = resolveJustBashPath(fs, cwd, path)
146
+ const encoding = toJustBashEncoding(getEncoding(options)) ?? undefined
147
+ await fs.writeFile(resolvedPath, toJustBashContent(content), encoding)
148
+ }
149
+
150
+ const appendFile: GokeFs['appendFile'] = async (path, content, options) => {
151
+ const resolvedPath = resolveJustBashPath(fs, cwd, path)
152
+ const encoding = toJustBashEncoding(getEncoding(options)) ?? undefined
153
+ await fs.appendFile(resolvedPath, toJustBashContent(content), encoding)
154
+ }
155
+
156
+ const mkdir: GokeFs['mkdir'] = async (path, options) => {
157
+ await fs.mkdir(resolveJustBashPath(fs, cwd, path), { recursive: typeof options === 'object' ? options.recursive : undefined })
158
+ return undefined
159
+ }
160
+
161
+ const rm: GokeFs['rm'] = async (path, options) => {
162
+ await fs.rm(resolveJustBashPath(fs, cwd, path), {
163
+ recursive: options?.recursive,
164
+ force: options?.force,
165
+ })
166
+ }
167
+
168
+ const rename: GokeFs['rename'] = async (oldPath, newPath) => {
169
+ await fs.mv(resolveJustBashPath(fs, cwd, oldPath), resolveJustBashPath(fs, cwd, newPath))
170
+ }
171
+
172
+ const copyFile: GokeFs['copyFile'] = async (src, dest) => {
173
+ await fs.cp(resolveJustBashPath(fs, cwd, src), resolveJustBashPath(fs, cwd, dest))
174
+ }
175
+
176
+ const chmod: GokeFs['chmod'] = async (path, mode) => {
177
+ await fs.chmod(resolveJustBashPath(fs, cwd, path), Number(mode))
178
+ }
179
+
180
+ const link: GokeFs['link'] = async (existingPath, newPath) => {
181
+ await fs.link(resolveJustBashPath(fs, cwd, existingPath), resolveJustBashPath(fs, cwd, newPath))
182
+ }
183
+
184
+ const readlink: GokeFs['readlink'] = async (path) => {
185
+ return fs.readlink(resolveJustBashPath(fs, cwd, path))
186
+ }
187
+
188
+ const realpath: GokeFs['realpath'] = async (path) => {
189
+ return fs.realpath(resolveJustBashPath(fs, cwd, path))
190
+ }
191
+
192
+ const symlink: GokeFs['symlink'] = async (target, path) => {
193
+ await fs.symlink(target.toString(), resolveJustBashPath(fs, cwd, path))
194
+ }
195
+
196
+ const utimes: GokeFs['utimes'] = async (path, atime, mtime) => {
197
+ await fs.utimes(resolveJustBashPath(fs, cwd, path), toDate(atime), toDate(mtime))
198
+ }
199
+
200
+ return {
201
+ appendFile,
202
+ chmod,
203
+ copyFile,
204
+ link,
205
+ mkdir,
206
+ readFile,
207
+ readlink,
208
+ realpath,
209
+ rename,
210
+ rm,
211
+ symlink,
212
+ utimes,
213
+ writeFile,
214
+ }
215
+ }
216
+
36
217
  export function createJustBashCommand(
37
218
  cli: Goke<any>,
38
219
  options?: { name?: string }
@@ -50,11 +231,15 @@ export function createJustBashCommand(
50
231
  return {
51
232
  name,
52
233
  trusted: true,
53
- async execute(args: string[]) {
234
+ async execute(args: string[], context?: JustBashExecutionContext) {
54
235
  const stdout = createTextCaptureStream()
55
236
  const stderr = createTextCaptureStream()
56
237
  const argv = ['node', name, ...args]
57
238
  const cloned = cli.clone({
239
+ cwd: context?.cwd,
240
+ env: context ? createJustBashEnvProxy(context.env) : cli.env,
241
+ fs: context ? createJustBashFs(context.fs, context.cwd) : cli.fs,
242
+ stdin: context?.stdin,
58
243
  stdout,
59
244
  stderr,
60
245
  argv,
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Vendored from picocolors by Alexey Raspopov (MIT license).
3
+ * Source: https://github.com/alexeyraspopov/picocolors/blob/main/picocolors.js
4
+ */
5
+
6
+ import { process } from '#runtime'
7
+
8
+ type Formatter = (input: unknown) => string
9
+
10
+ interface PicoColors {
11
+ isColorSupported: boolean
12
+ reset: Formatter
13
+ bold: Formatter
14
+ dim: Formatter
15
+ italic: Formatter
16
+ underline: Formatter
17
+ inverse: Formatter
18
+ hidden: Formatter
19
+ strikethrough: Formatter
20
+ black: Formatter
21
+ red: Formatter
22
+ green: Formatter
23
+ yellow: Formatter
24
+ blue: Formatter
25
+ magenta: Formatter
26
+ cyan: Formatter
27
+ white: Formatter
28
+ gray: Formatter
29
+ bgBlack: Formatter
30
+ bgRed: Formatter
31
+ bgGreen: Formatter
32
+ bgYellow: Formatter
33
+ bgBlue: Formatter
34
+ bgMagenta: Formatter
35
+ bgCyan: Formatter
36
+ bgWhite: Formatter
37
+ blackBright: Formatter
38
+ redBright: Formatter
39
+ greenBright: Formatter
40
+ yellowBright: Formatter
41
+ blueBright: Formatter
42
+ magentaBright: Formatter
43
+ cyanBright: Formatter
44
+ whiteBright: Formatter
45
+ bgBlackBright: Formatter
46
+ bgRedBright: Formatter
47
+ bgGreenBright: Formatter
48
+ bgYellowBright: Formatter
49
+ bgBlueBright: Formatter
50
+ bgMagentaBright: Formatter
51
+ bgCyanBright: Formatter
52
+ bgWhiteBright: Formatter
53
+ }
54
+
55
+ const argv = process.argv || []
56
+ const env = process.env || {}
57
+ const isColorSupported =
58
+ !(!!env.NO_COLOR || argv.includes('--no-color'))
59
+ && (
60
+ !!env.FORCE_COLOR
61
+ || argv.includes('--color')
62
+ || process.platform === 'win32'
63
+ || (process.stdout.isTTY && env.TERM !== 'dumb')
64
+ || !!env.CI
65
+ )
66
+
67
+ const replaceClose = (string: string, close: string, replace: string, index: number) => {
68
+ let result = ''
69
+ let cursor = 0
70
+
71
+ do {
72
+ result += string.substring(cursor, index) + replace
73
+ cursor = index + close.length
74
+ index = string.indexOf(close, cursor)
75
+ } while (~index)
76
+
77
+ return result + string.substring(cursor)
78
+ }
79
+
80
+ const formatter = (open: string, close: string, replace = open): Formatter =>
81
+ (input) => {
82
+ const string = String(input)
83
+ const index = string.indexOf(close, open.length)
84
+ return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close
85
+ }
86
+
87
+ const createColors = (enabled = isColorSupported): PicoColors => {
88
+ const f = enabled ? formatter : () => String
89
+
90
+ return {
91
+ isColorSupported: enabled,
92
+ reset: f('\x1b[0m', '\x1b[0m'),
93
+ bold: f('\x1b[1m', '\x1b[22m', '\x1b[22m\x1b[1m'),
94
+ dim: f('\x1b[2m', '\x1b[22m', '\x1b[22m\x1b[2m'),
95
+ italic: f('\x1b[3m', '\x1b[23m'),
96
+ underline: f('\x1b[4m', '\x1b[24m'),
97
+ inverse: f('\x1b[7m', '\x1b[27m'),
98
+ hidden: f('\x1b[8m', '\x1b[28m'),
99
+ strikethrough: f('\x1b[9m', '\x1b[29m'),
100
+ black: f('\x1b[30m', '\x1b[39m'),
101
+ red: f('\x1b[31m', '\x1b[39m'),
102
+ green: f('\x1b[32m', '\x1b[39m'),
103
+ yellow: f('\x1b[33m', '\x1b[39m'),
104
+ blue: f('\x1b[34m', '\x1b[39m'),
105
+ magenta: f('\x1b[35m', '\x1b[39m'),
106
+ cyan: f('\x1b[36m', '\x1b[39m'),
107
+ white: f('\x1b[37m', '\x1b[39m'),
108
+ gray: f('\x1b[90m', '\x1b[39m'),
109
+ bgBlack: f('\x1b[40m', '\x1b[49m'),
110
+ bgRed: f('\x1b[41m', '\x1b[49m'),
111
+ bgGreen: f('\x1b[42m', '\x1b[49m'),
112
+ bgYellow: f('\x1b[43m', '\x1b[49m'),
113
+ bgBlue: f('\x1b[44m', '\x1b[49m'),
114
+ bgMagenta: f('\x1b[45m', '\x1b[49m'),
115
+ bgCyan: f('\x1b[46m', '\x1b[49m'),
116
+ bgWhite: f('\x1b[47m', '\x1b[49m'),
117
+ blackBright: f('\x1b[90m', '\x1b[39m'),
118
+ redBright: f('\x1b[91m', '\x1b[39m'),
119
+ greenBright: f('\x1b[92m', '\x1b[39m'),
120
+ yellowBright: f('\x1b[93m', '\x1b[39m'),
121
+ blueBright: f('\x1b[94m', '\x1b[39m'),
122
+ magentaBright: f('\x1b[95m', '\x1b[39m'),
123
+ cyanBright: f('\x1b[96m', '\x1b[39m'),
124
+ whiteBright: f('\x1b[97m', '\x1b[39m'),
125
+ bgBlackBright: f('\x1b[100m', '\x1b[49m'),
126
+ bgRedBright: f('\x1b[101m', '\x1b[49m'),
127
+ bgGreenBright: f('\x1b[102m', '\x1b[49m'),
128
+ bgYellowBright: f('\x1b[103m', '\x1b[49m'),
129
+ bgBlueBright: f('\x1b[104m', '\x1b[49m'),
130
+ bgMagentaBright: f('\x1b[105m', '\x1b[49m'),
131
+ bgCyanBright: f('\x1b[106m', '\x1b[49m'),
132
+ bgWhiteBright: f('\x1b[107m', '\x1b[49m'),
133
+ }
134
+ }
135
+
136
+ const pc = createColors()
137
+
138
+ export { createColors }
139
+ export type { PicoColors }
140
+ export default pc
@@ -2,6 +2,8 @@
2
2
  * Browser-safe runtime stubs for goke core.
3
3
  */
4
4
 
5
+ import type { GokeFs } from './goke-fs.js'
6
+
5
7
  type Listener = (...args: any[]) => void
6
8
 
7
9
  class EventEmitter {
@@ -41,6 +43,10 @@ const createOutputStream = () => ({
41
43
  const process = {
42
44
  argv: [] as string[],
43
45
  arch: 'browser',
46
+ cwd() {
47
+ return '/'
48
+ },
49
+ env: Object.create(null) as Record<string, string | undefined>,
44
50
  platform: 'browser',
45
51
  version: 'browser',
46
52
  stdout: createOutputStream(),
@@ -52,8 +58,36 @@ const process = {
52
58
  },
53
59
  }
54
60
 
61
+ function createBrowserFsError(methodName: string) {
62
+ return new Error(
63
+ `fs.${methodName}() is not available in the browser runtime. Pass a custom fs implementation to goke(...).`
64
+ )
65
+ }
66
+
67
+ function createUnsupportedFsMethod<T>(methodName: string): T {
68
+ return (async () => {
69
+ throw createBrowserFsError(methodName)
70
+ }) as T
71
+ }
72
+
73
+ const fs: GokeFs = {
74
+ appendFile: createUnsupportedFsMethod<GokeFs['appendFile']>('appendFile'),
75
+ chmod: createUnsupportedFsMethod<GokeFs['chmod']>('chmod'),
76
+ copyFile: createUnsupportedFsMethod<GokeFs['copyFile']>('copyFile'),
77
+ link: createUnsupportedFsMethod<GokeFs['link']>('link'),
78
+ mkdir: createUnsupportedFsMethod<GokeFs['mkdir']>('mkdir'),
79
+ readFile: createUnsupportedFsMethod<GokeFs['readFile']>('readFile'),
80
+ readlink: createUnsupportedFsMethod<GokeFs['readlink']>('readlink'),
81
+ realpath: createUnsupportedFsMethod<GokeFs['realpath']>('realpath'),
82
+ rename: createUnsupportedFsMethod<GokeFs['rename']>('rename'),
83
+ rm: createUnsupportedFsMethod<GokeFs['rm']>('rm'),
84
+ symlink: createUnsupportedFsMethod<GokeFs['symlink']>('symlink'),
85
+ utimes: createUnsupportedFsMethod<GokeFs['utimes']>('utimes'),
86
+ writeFile: createUnsupportedFsMethod<GokeFs['writeFile']>('writeFile'),
87
+ }
88
+
55
89
  function openInBrowser(_url: string): void {
56
90
  // Browser builds should decide how to surface URLs themselves.
57
91
  }
58
92
 
59
- export { EventEmitter, openInBrowser, process }
93
+ export { EventEmitter, fs, openInBrowser, process }
@@ -4,8 +4,11 @@
4
4
 
5
5
  import { execSync } from 'child_process'
6
6
  import { EventEmitter } from 'events'
7
+ import * as nodeFs from 'node:fs/promises'
8
+ import type { GokeFs } from './goke-fs.js'
7
9
 
8
10
  const process = globalThis.process
11
+ const fs: GokeFs = nodeFs
9
12
 
10
13
  function openInBrowser(url: string): void {
11
14
  if (!process.stdout.isTTY) {
@@ -26,4 +29,4 @@ function openInBrowser(url: string): void {
26
29
  }
27
30
  }
28
31
 
29
- export { EventEmitter, openInBrowser, process }
32
+ export { EventEmitter, fs, openInBrowser, process }