@gjsify/fs 0.30.0 → 0.32.0

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.
@@ -33,7 +33,7 @@ export declare function readdirSync(path: PathLike, options?: {
33
33
  */
34
34
  export declare function realpathSync(path: PathLike): string;
35
35
  export declare function symlinkSync(target: PathLike, path: PathLike, _type?: 'file' | 'dir' | 'junction'): void;
36
- export declare function readFileSync(path: PathLike | number, options?: {
36
+ export declare function readFileSync(path: PathLike | number | FileHandle, options?: {
37
37
  encoding?: string | null;
38
38
  flag?: string;
39
39
  } | string | null): string | Buffer<ArrayBuffer>;
@@ -74,15 +74,17 @@ export declare function mkdirSync(path: PathLike, options?: Mode | MakeDirectory
74
74
  */
75
75
  export declare function rmdirSync(path: PathLike, _options?: RmDirOptions): void;
76
76
  export declare function unlinkSync(path: PathLike): void;
77
- export declare function writeFileSync(path: PathLike, data: string | Uint8Array): void;
77
+ /** The `{ encoding, mode, flag }` bag `writeFileSync` / `appendFileSync` accept. */
78
+ type WriteFileOptions = {
79
+ encoding?: string | null;
80
+ mode?: Mode;
81
+ flag?: OpenFlags | string;
82
+ } | string | null;
83
+ export declare function writeFileSync(path: PathLike | number | FileHandle, data: string | Uint8Array, options?: WriteFileOptions): void;
78
84
  export declare function renameSync(oldPath: PathLike, newPath: PathLike): void;
79
85
  export declare function copyFileSync(src: PathLike, dest: PathLike, mode?: number): void;
80
86
  export declare function accessSync(path: PathLike, mode?: number): void;
81
- export declare function appendFileSync(path: PathLike, data: string | Uint8Array, _options?: {
82
- encoding?: string;
83
- mode?: number;
84
- flag?: string;
85
- } | string): void;
87
+ export declare function appendFileSync(path: PathLike | number | FileHandle, data: string | Uint8Array, options?: WriteFileOptions): void;
86
88
  export declare function readlinkSync(path: PathLike, options?: {
87
89
  encoding?: string;
88
90
  } | string): string | Buffer;
@@ -119,6 +121,23 @@ export declare function mkdtempSync(prefix: string, options: BufferEncodingOptio
119
121
  * @param options The encoding (or an object specifying the encoding), used as the encoding of the result. If not provided, `'utf8'` is used.
120
122
  */
121
123
  export declare function mkdtempSync(prefix: string, options?: EncodingOption): string | Buffer;
124
+ /**
125
+ * `mkdtemp(3)`: claim a unique name and create it 0700, atomically.
126
+ *
127
+ * TWO defects lived in the four lines this replaces. It asked `mkdirSync` for
128
+ * `mode: 0o777` — inert while `mode` was ignored, and a WORLD-READABLE
129
+ * directory the moment `mode` started working, from the one API whose whole
130
+ * purpose is a private scratch space. And it picked the name with
131
+ * `while (existsSync(path))`, a test-then-create whose `existsSync` was
132
+ * `Gio.File.query_exists()` — true for a dangling symlink, and racy against
133
+ * any other process regardless. `mkdir(2)` decides both questions in one
134
+ * syscall: it fails EEXIST if the name is taken, so the loop retries instead
135
+ * of guessing.
136
+ *
137
+ * Both entry points call THIS, so `mkdtempSync` and `promises.mkdtemp` cannot
138
+ * disagree on the mode again — they already did, by 0o077.
139
+ */
140
+ export declare function mkdtempAt(prefix: string): string;
122
141
  /**
123
142
  * Synchronously removes files and directories (modeled on the standard POSIX `rm`utility). Returns `undefined`.
124
143
  * @since v14.14.0
@@ -1,4 +1,3 @@
1
1
  import type { PathLike } from 'node:fs';
2
2
  export declare function normalizePath(path: PathLike): string;
3
3
  export declare function randomName(): string;
4
- export declare function tempDirPath(prefix: string): string;
@@ -8,6 +8,10 @@ export declare function lutimesAsync(path: PathLike, atime: TimeLike, mtime: Tim
8
8
  export declare function lchownSync(path: PathLike, uid: number, gid: number): void;
9
9
  export declare function lchown(path: PathLike, uid: number, gid: number, callback: (err: NodeJS.ErrnoException | null) => void): void;
10
10
  export declare function lchownAsync(path: PathLike, uid: number, gid: number): Promise<void>;
11
- export declare function lchmodSync(_path: PathLike, _mode: number): void;
12
- export declare function lchmod(_path: PathLike, _mode: number, callback: (err: NodeJS.ErrnoException | null) => void): void;
11
+ export declare const lchmodSync: ((path: PathLike, mode: number) => void) | undefined;
12
+ export declare const lchmod: ((path: PathLike, mode: number, callback: (err: NodeJS.ErrnoException | null) => void) => void) | undefined;
13
+ /**
14
+ * The one spelling Node defines on every platform — and it throws there too, so
15
+ * this is the only lchmod surface that keeps a body.
16
+ */
13
17
  export declare function lchmodAsync(_path: PathLike, _mode: number): Promise<void>;
@@ -1,7 +1,36 @@
1
1
  import { Writable } from 'node:stream';
2
2
  import type { OpenFlags } from './types/index.js';
3
- import type { PathLike, WriteStream as IWriteStream } from 'node:fs';
3
+ import type { Mode, PathLike, WriteStream as IWriteStream } from 'node:fs';
4
4
  import type { CreateWriteStreamOptions } from 'node:fs/promises';
5
+ /**
6
+ * What `fs.createWriteStream()` accepts, as opposed to
7
+ * `filehandle.createWriteStream()`.
8
+ *
9
+ * `CreateWriteStreamOptions` is the fs/promises shape and carries only
10
+ * `encoding`/`autoClose`/`emitClose`/`start`/`highWaterMark` — which is
11
+ * precisely why `flags`, `mode` and `fd` were never read here: they are not in
12
+ * the type the constructor was written against.
13
+ */
14
+ export type WriteStreamOptions = CreateWriteStreamOptions & {
15
+ flags?: OpenFlags | string;
16
+ mode?: Mode;
17
+ fd?: number | null;
18
+ /**
19
+ * @internal Set only by {@link FileHandle.createWriteStream}.
20
+ *
21
+ * A descriptor that came from a `FileHandle` is closed through the HANDLE
22
+ * in Node, and a handle's close is IDEMPOTENT — so a stream that finishes
23
+ * after the caller already closed the handle reports nothing (measured
24
+ * against v24.15.0: `errs=[]`). A raw `fd` handed to
25
+ * `createWriteStream(p, {fd})` is a real `close(2)`, and Node DOES surface
26
+ * its EBADF as an `'error'`. The two look identical from inside this class,
27
+ * which is why they are told apart here rather than both swallowed — or, as
28
+ * before this flag existed, both reported: that turned the ordinary
29
+ * `fh.createWriteStream(); …; await fh.close()` sequence into an
30
+ * `'error'` nobody listens for.
31
+ */
32
+ fdFromHandle?: boolean;
33
+ };
5
34
  declare const kIsPerformingIO: unique symbol;
6
35
  export declare function toPathIfFileURL(fileURLOrPath: string | Buffer | URL): string;
7
36
  export declare class WriteStream extends Writable implements IWriteStream {
@@ -33,12 +62,24 @@ export declare class WriteStream extends Writable implements IWriteStream {
33
62
  fd: number | null;
34
63
  flags: OpenFlags;
35
64
  mode: number;
36
- pos: number;
65
+ /**
66
+ * The offset the next chunk is written at, or `undefined` to ride the
67
+ * descriptor's own cursor — which is what Node does unless `start` is given.
68
+ *
69
+ * This was `pos = 0`, i.e. ALWAYS defined, so every chunk carried an
70
+ * explicit position. That is why a `{flags: 'a'}` stream could only ever
71
+ * rewrite the file from byte 0, and why `{start: n}` was silently ignored.
72
+ */
73
+ pos: number | undefined;
74
+ /** Whether this stream owns the descriptor and must close it. */
75
+ private _autoClose;
76
+ /** Whether the descriptor came from a FileHandle — see `fdFromHandle`. */
77
+ private _fdFromHandle;
37
78
  [kIsPerformingIO]: boolean;
38
- constructor(path: PathLike, opts?: CreateWriteStreamOptions);
79
+ constructor(path: PathLike, opts?: WriteStreamOptions);
39
80
  _construct(callback: (err?: Error) => void): void;
40
81
  _write(data: Buffer, _encoding: string, cb: (err?: Error | null) => void): void;
41
82
  _destroy(err: Error, cb: (err?: Error | null) => void): void;
42
83
  }
43
- export declare function createWriteStream(path: string | Buffer, opts: CreateWriteStreamOptions): WriteStream;
84
+ export declare function createWriteStream(path: string | Buffer, opts: WriteStreamOptions): WriteStream;
44
85
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/fs",
3
- "version": "0.30.0",
3
+ "version": "0.32.0",
4
4
  "description": "Node.js fs module for Gjs",
5
5
  "module": "lib/esm/index.js",
6
6
  "types": "lib/types/index.d.ts",
@@ -66,8 +66,8 @@
66
66
  "fs"
67
67
  ],
68
68
  "devDependencies": {
69
- "@gjsify/cli": "^0.30.0",
70
- "@gjsify/unit": "^0.30.0",
69
+ "@gjsify/cli": "^0.32.0",
70
+ "@gjsify/unit": "^0.32.0",
71
71
  "@types/node": "^25.9.2",
72
72
  "typescript": "^6.0.3"
73
73
  },
@@ -75,11 +75,11 @@
75
75
  "@girs/gio-2.0": "^4.1.0",
76
76
  "@girs/giounix-2.0": "^4.1.0",
77
77
  "@girs/glib-2.0": "^4.1.0",
78
- "@gjsify/buffer": "^0.30.0",
79
- "@gjsify/events": "^0.30.0",
80
- "@gjsify/stream": "^0.30.0",
81
- "@gjsify/url": "^0.30.0",
82
- "@gjsify/utils": "^0.30.0"
78
+ "@gjsify/buffer": "^0.32.0",
79
+ "@gjsify/events": "^0.32.0",
80
+ "@gjsify/stream": "^0.32.0",
81
+ "@gjsify/url": "^0.32.0",
82
+ "@gjsify/utils": "^0.32.0"
83
83
  },
84
84
  "gjsify": {
85
85
  "runtimes": {
@@ -88,6 +88,15 @@
88
88
  "browser": "partial",
89
89
  "nativescript": "none"
90
90
  },
91
+ "os": {
92
+ "linux": "supported",
93
+ "darwin": "partial",
94
+ "win32": "none"
95
+ },
96
+ "osNotes": {
97
+ "darwin": "The descriptor path is measured on Linux only. fstat/fchmod/ftruncate and the cursor read-back go through /proc/self/fd and /proc/self/fdinfo, which darwin does not have; fd-io.ts falls back to acting on the PATH there, and tellFd() to sizeOfFd(). Both fallbacks are gated on the measured CAN_PROC_FD capability rather than assumed, but no darwin host has run them — shipping an unmeasured darwin path is how the first cut of this work shipped a broken umask probe. The O_* table in posix-flags.ts carries darwin values (O_CREAT 0x200 collides with Linux O_TRUNC, which is why it is host-keyed at all) and those are equally unexercised: prebuilds.yml runs no darwin leg for this package.",
98
+ "win32": "Not attempted. The implementation is POSIX open(2)/mkdir(2) semantics with real O_CREAT|O_EXCL, an fd-owned cursor and mode-at-creation; win32 has no procfs, no POSIX mode bits and different open flags. The WIN32 branch in posix-flags.ts exists so the table is total, not because the package works there — the fs specs already carry `it.failing({ when: isWin32() })` rows saying the same."
99
+ },
91
100
  "tier": 1
92
101
  },
93
102
  "license": "MIT",
@@ -1 +0,0 @@
1
- import"./_virtual/_rolldown/runtime.js";import e from"@girs/glib-2.0";import{createGLibFileError as t}from"@gjsify/utils/core";function resolveIOMode(e){if(e==null)return`r`;if(typeof e==`number`){let t=(e&2)!=0,n=(e&1)!=0,r=(e&1024)!=0,i=(e&512)!=0;return t?i?`w+`:`r+`:n?r?`a`:`w`:`r`}switch(e){case`ax`:case`wx`:return`w`;case`ax+`:case`wx+`:return`w+`;case`as`:case`rs+`:return`r+`;case`as+`:return`a+`;default:return e}}function shouldCreate(e){return typeof e==`number`?(e&64)!=0:typeof e==`string`?e===`w`||e===`w+`||e===`wx`||e===`wx+`||e===`a`||e===`a+`||e===`ax`||e===`ax+`||e===`as`||e===`as+`:!1}function openIOChannel(t,n,r){try{return e.IOChannel.new_file(t,n)}catch(i){if(r&&n===`r+`&&i?.code===e.FileError.NOENT)return e.file_set_contents(t,new Uint8Array),e.IOChannel.new_file(t,n);throw i}}function mapOpenError(e,n){return t(e,`open`,{path:n})}export{mapOpenError,openIOChannel,resolveIOMode,shouldCreate};
@@ -1,25 +0,0 @@
1
- import GLib from '@girs/glib-2.0';
2
- import type { OpenFlags } from './types/index.js';
3
- /** fopen(3)-style IOChannel mode. */
4
- export type IOMode = 'r' | 'r+' | 'w' | 'w+' | 'a' | 'a+';
5
- /**
6
- * Convert open flags (Node.js string or POSIX numeric) to a GLib.IOChannel mode.
7
- * IOChannel.new_file() takes fopen(3) modes: 'r', 'r+', 'w', 'w+', 'a', 'a+'.
8
- */
9
- export declare function resolveIOMode(flags: OpenFlags | number | undefined): IOMode;
10
- /** Whether the open flags include `O_CREAT` (create-if-missing semantics). */
11
- export declare function shouldCreate(flags: OpenFlags | number | undefined): boolean;
12
- /**
13
- * Open the file with the given IOChannel mode. When the flags request
14
- * create-if-missing + read/write without truncation (numeric O_CREAT | O_RDWR,
15
- * which maps to IOChannel 'r+' — a mode that requires the file to exist), we
16
- * catch the ENOENT and create an empty file, then retry. This avoids a TOCTOU
17
- * existence check and keeps the common "file exists" path to a single syscall.
18
- */
19
- export declare function openIOChannel(path: string, mode: IOMode, creat: boolean): GLib.IOChannel;
20
- /**
21
- * Wrap a `GLib.IOChannel.new_file()` failure as a Node `ErrnoException`.
22
- * GLib's IOChannel throws GLib.FileError (not Gio.IOErrorEnum) on this path,
23
- * so we route through `createGLibFileError` to get the right errno + code.
24
- */
25
- export declare function mapOpenError(err: unknown, path: string): NodeJS.ErrnoException;