@gjsify/fs 0.42.0 → 0.43.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.
- package/lib/esm/fs-watcher.js +1 -1
- package/lib/esm/promises.js +1 -1
- package/lib/esm/sync.js +1 -1
- package/lib/esm/watch-tree.js +1 -0
- package/lib/types/sync.d.ts +5 -2
- package/lib/types/watch-backend.gjs.spec.d.ts +2 -0
- package/lib/types/watch-fanout.gjs.spec.d.ts +2 -0
- package/lib/types/watch-recursive.spec.d.ts +2 -0
- package/lib/types/watch-tree.d.ts +98 -0
- package/package.json +10 -10
package/lib/esm/fs-watcher.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./_virtual/_rolldown/runtime.js";import{normalizePath as e}from"./utils.js";import t
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{normalizePath as e}from"./utils.js";import{WatchTree as t}from"./watch-tree.js";import n from"@girs/glib-2.0";import{EventEmitter as r}from"node:events";const i=new WeakMap;var FSWatcher=class extends r{constructor(r,a,o){super(),(!a||typeof a!=`object`)&&(a={persistent:!0});let s=a.persistent!==!1,c=new t(e(r),a.recursive===!0,(e,t)=>{this.emit(`change`,e,t)},e=>{this.emit(`error`,e)}),l=null;s&&(l=n.timeout_add(n.PRIORITY_LOW,2147483647,()=>n.SOURCE_CONTINUE)),i.set(this,{persistent:s,closed:!1,sourceId:l,tree:c}),o&&this.on(`change`,o)}close(){let e=i.get(this);e.closed||(e.closed=!0,e.tree.close(),e.sourceId!==null&&(n.source_remove(e.sourceId),e.sourceId=null))}ref(){let e=i.get(this);return!e.persistent&&!e.closed&&(e.persistent=!0,e.sourceId=n.timeout_add(n.PRIORITY_LOW,2147483647,()=>n.SOURCE_CONTINUE)),this}unref(){let e=i.get(this);return e.persistent&&(e.persistent=!1,e.sourceId!==null&&(n.source_remove(e.sourceId),e.sourceId=null)),this}};async function*watchAsync(n,r){let i=r?.signal;if(i?.aborted)return;let a=e(n),o=[],s=[],c=!1,l=null;function enqueue(e){c||(s.length>0?s.shift().resolve({value:e,done:!1}):o.push(e))}function terminate(){if(!c)for(c=!0;s.length>0;)s.shift().resolve({value:void 0,done:!0})}let u;try{u=new t(a,r?.recursive===!0,(e,t)=>{enqueue({eventType:e,filename:t})},e=>{l=e,terminate()})}catch{return}let abortHandler=()=>terminate();i?.addEventListener(`abort`,abortHandler);try{for(;!c;){if(o.length>0){yield o.shift();continue}let e=await new Promise(e=>{s.push({resolve:e})});if(e.done)break;yield e.value}if(l!==null)throw l}finally{i?.removeEventListener(`abort`,abortHandler),u.close()}}export{FSWatcher,FSWatcher as default,watchAsync};
|
package/lib/esm/promises.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{__exportAll as e}from"./_virtual/_rolldown/runtime.js";import{decode as t,encodeUint8Array as n,getEncodingFromOptions as r}from"./encoding.js";import{normalizePath as i}from"./utils.js";import{
|
|
1
|
+
import{__exportAll as e}from"./_virtual/_rolldown/runtime.js";import{decode as t,encodeUint8Array as n,getEncodingFromOptions as r}from"./encoding.js";import{normalizePath as i}from"./utils.js";import{createNodeError as a}from"./errors.js";import{watchAsync as o}from"./fs-watcher.js";import{lchmodAsync as s,lchownAsync as c,lutimesAsync as l,utimesAsync as u}from"./utimes.js";import{fchmodAsync as d,fchownAsync as f,fdatasyncAsync as p,fstatAsync as m,fsyncAsync as h,ftruncateAsync as g,futimesAsync as _,openAsBlob as v,readvAsync as y,writevAsync as b}from"./fd-ops.js";import{STAT_ATTRIBUTES as x,statsFrom as S}from"./stats.js";import{FileHandle as C}from"./file-handle.js";import{accessSync as w,appendFileSync as T,chmodSync as E,chownSync as D,copyFileSync as O,linkSync as k,mkdirSync as A,mkdtempAt as j,readdirSync as M,readlinkSync as N,realpathSync as P,renameSync as F,truncateSync as I,writeFileSync as L}from"./sync.js";import{cpAsync as R}from"./cp.js";import{opendirAsync as z}from"./dir.js";import{globAsync as B}from"./glob.js";import{statfsAsync as V}from"./statfs.js";import H from"@girs/glib-2.0";import U from"@girs/gio-2.0";import{join as W}from"node:path";var G=e({access:()=>access,appendFile:()=>appendFile,chmod:()=>chmod,chown:()=>chown,copyFile:()=>copyFile,cp:()=>R,default:()=>K,fchmod:()=>d,fchown:()=>f,fdatasync:()=>p,fstat:()=>m,fsync:()=>h,ftruncate:()=>g,futimes:()=>_,glob:()=>B,lchmod:()=>s,lchown:()=>c,link:()=>link,lstat:()=>lstat,lutimes:()=>l,mkdir:()=>mkdir,mkdtemp:()=>mkdtemp,open:()=>open,openAsBlob:()=>v,opendir:()=>z,readFile:()=>readFile,readdir:()=>readdir,readlink:()=>readlink,readv:()=>y,realpath:()=>realpath,rename:()=>rename,rm:()=>rm,rmdir:()=>rmdir,stat:()=>stat,statfs:()=>V,symlink:()=>symlink,truncate:()=>truncate,unlink:()=>unlink,utimes:()=>u,watch:()=>o,write:()=>write,writeFile:()=>writeFile,writev:()=>b});async function mkdir(e,t){return A(i(e),t)}async function readFile(e,t={encoding:null,flag:`r`}){if(e instanceof C||typeof e==`number`){let i=C.getInstance(e,`read`);return n(r(t,`buffer`),i._readToEndSync())}let o=i(e),s=U.File.new_for_path(o),c,l;try{[c,l]=await new Promise((e,t)=>{s.load_contents_async(null,(n,r)=>{try{e(s.load_contents_finish(r))}catch(e){t(e)}})})}catch(e){throw a(e,`open`,o)}if(!c)throw a(Error(`failed to read file`),`open`,o);return n(r(t,`buffer`),l)}async function mkdtemp(e,n){let i=r(n);return t(j(e),i)}async function writeFile(e,t,n){L(e,typeof t==`string`||t instanceof Uint8Array?t:String(t),n??null)}async function rmdir(e,t){let n=i(e),r=U.File.new_for_path(n);if((await new Promise((e,t)=>{r.query_info_async(`standard::type`,U.FileQueryInfoFlags.NONE,H.PRIORITY_DEFAULT,null,(i,o)=>{try{e(r.query_info_finish(o))}catch(e){t(a(e,`rmdir`,n))}})})).get_file_type()!==U.FileType.DIRECTORY)throw a(Object.assign(Error(),{code:4}),`rmdir`,n);if((await new Promise((e,t)=>{r.enumerate_children_async(`standard::name`,U.FileQueryInfoFlags.NONE,H.PRIORITY_DEFAULT,null,(i,o)=>{try{e(r.enumerate_children_finish(o))}catch(e){t(a(e,`rmdir`,n))}})})).next_file(null)!==null)throw a(Object.assign(Error(),{code:5}),`rmdir`,n);await new Promise((e,t)=>{r.delete_async(H.PRIORITY_DEFAULT,null,(i,o)=>{try{r.delete_finish(o),e()}catch(e){t(a(e,`rmdir`,n))}})})}async function unlink(e){let t=i(e),n=U.File.new_for_path(t);await new Promise((e,r)=>{n.delete_async(H.PRIORITY_DEFAULT,null,(i,o)=>{try{n.delete_finish(o),e()}catch(e){r(a(e,`unlink`,t))}})})}async function open(e,t,n){return new C({path:e,flags:t,mode:n})}async function write(e,t,n,r,i){return typeof t==`string`?_writeStr(e,t,n,r):_writeBuf(e,t,n,r,i)}async function _writeBuf(e,t,n,r,i){return{bytesWritten:(await C.getInstance(e,`write`).write(t,n,r,i)).bytesWritten,buffer:t}}async function _writeStr(e,t,n,r){return{bytesWritten:(await C.getInstance(e,`write`).write(t,n,r)).bytesWritten,buffer:t}}function queryInfoAsync(e,t,n,r){let o=i(e);return new Promise((e,i)=>{let s=U.File.new_for_path(o);s.query_info_async(x,t,H.PRIORITY_DEFAULT,null,(t,c)=>{try{e(S(s.query_info_finish(c),o,n,r?.bigint))}catch(e){i(a(e,n,o))}})})}async function stat(e,t){return queryInfoAsync(e,U.FileQueryInfoFlags.NONE,`stat`,t)}async function lstat(e,t){return queryInfoAsync(e,U.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,`lstat`,t)}async function readdir(e,t){try{return M(e,t)}catch(t){throw a(t,`scandir`,e)}}async function realpath(e){return P(e)}async function symlink(e,t,n){let r=i(t),o=i(e);return new Promise((e,t)=>{let n=U.File.new_for_path(r);n.make_symbolic_link_async(o,H.PRIORITY_DEFAULT,null,(i,s)=>{try{n.make_symbolic_link_finish(s),e()}catch(e){t(a(e,`symlink`,o,r))}})})}async function rm(e,t){let n=i(e),r=U.File.new_for_path(n),o=t?.recursive||!1,s=t?.force||!1,c;try{c=r.query_file_type(U.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,null)}catch(t){if(s)return;throw a(t,`rm`,e)}if(c===U.FileType.SYMBOLIC_LINK){await new Promise((t,n)=>{r.delete_async(H.PRIORITY_DEFAULT,null,(i,o)=>{try{r.delete_finish(o),t()}catch(r){if(s){t();return}n(a(r,`rm`,e))}})});return}if(c===U.FileType.DIRECTORY){let r=await readdir(e,{withFileTypes:!0});if(!o&&r.length)throw a(Object.assign(Error(),{code:5}),`rm`,e);for(let e of r)typeof e!=`string`&&await rm(W(n,e.name),t)}await new Promise((t,n)=>{r.delete_async(H.PRIORITY_DEFAULT,null,(i,o)=>{try{r.delete_finish(o),t()}catch(r){if(s){t();return}n(a(r,`rm`,e))}})})}async function rename(e,t){F(e,t)}async function copyFile(e,t,n){O(e,t,n)}async function access(e,t){w(e,t)}async function appendFile(e,t,n){T(e,t,n)}async function readlink(e,t){return N(e,t)}async function truncate(e,t){I(e,t)}async function chmod(e,t){E(e,t)}async function chown(e,t,n){D(e,t,n)}async function link(e,t){k(e,t)}var K={readFile,mkdir,mkdtemp,realpath,readdir,writeFile,rmdir,unlink,open,write,rm,lstat,symlink,stat,rename,copyFile,access,appendFile,readlink,truncate,chmod,chown,link,cp:R,opendir:z,glob:B,watch:o,statfs:V,utimes:u,lutimes:l,lchown:c,lchmod:s,fstat:m,ftruncate:g,fdatasync:p,fsync:h,fchmod:d,fchown:f,futimes:_,readv:y,writev:b,openAsBlob:v};export{access,appendFile,chmod,chown,copyFile,R as cp,K as default,d as fchmod,f as fchown,p as fdatasync,m as fstat,h as fsync,g as ftruncate,_ as futimes,B as glob,s as lchmod,c as lchown,link,lstat,l as lutimes,mkdir,mkdtemp,open,v as openAsBlob,z as opendir,G as promises_exports,readFile,readdir,readlink,y as readv,realpath,rename,rm,rmdir,stat,V as statfs,symlink,truncate,unlink,u as utimes,o as watch,write,writeFile,b as writev};
|
package/lib/esm/sync.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"./_virtual/_rolldown/runtime.js";import{decode as e,encodeUint8Array as t,getEncodingFromOptions as n}from"./encoding.js";import{normalizePath as r,randomName as i}from"./utils.js";import{
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{decode as e,encodeUint8Array as t,getEncodingFromOptions as n}from"./encoding.js";import{normalizePath as r,randomName as i}from"./utils.js";import{createNodeError as a,isNotFoundError as o}from"./errors.js";import{FSWatcher as s}from"./fs-watcher.js";import{isStdFd as c,readStdFdAll as l,writeStdFdSync as u}from"./std-fd.js";import{normalizeMode as d}from"./posix-flags.js";import{Dirent as f}from"./dirent.js";import{classifyMkdirFailure as p,fsError as m}from"./fd-io.js";import{STAT_ATTRIBUTES as h,statsFrom as g}from"./stats.js";import{FileHandle as _}from"./file-handle.js";import v from"@girs/glib-2.0";import y from"@girs/gio-2.0";import{join as b}from"node:path";import{Buffer as x}from"node:buffer";import{existsSync as S}from"@gjsify/utils";import{isDarwin as C}from"@gjsify/utils/core";function statSync(e,t){let n=r(e);try{return g(y.File.new_for_path(n).query_info(h,y.FileQueryInfoFlags.NONE,null),n,`stat`,t?.bigint)}catch(e){if(t?.throwIfNoEntry===!1&&o(e))return;throw a(e,`stat`,n)}}function lstatSync(e,t){let n=r(e);try{return g(y.File.new_for_path(n).query_info(h,y.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,null),n,`lstat`,t?.bigint)}catch(e){if(t?.throwIfNoEntry===!1&&o(e))return;throw a(e,`lstat`,n)}}function readdirSync(e,t){let n=r(e),i=y.File.new_for_path(n).enumerate_children(`standard::name,standard::type`,y.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,null),a=[];try{let e=i.next_file(null);for(;e!==null;)a.push({name:e.get_name(),type:e.get_file_type()}),e=i.next_file(null)}finally{try{i.close(null)}catch{}}let o=[];for(let e of a){let r=b(n,e.name);if(t?.withFileTypes?o.push(new f(r,e.name,e.type)):o.push(e.name),t?.recursive&&e.type===y.FileType.DIRECTORY){let n=readdirSync(r,t);for(let t of n)typeof t==`string`?o.push(b(e.name,t)):o.push(t)}}return o}function realpathSync(e){let t=r(e);return resolveEveryComponent(y.File.new_for_path(t),{left:40},t)}function resolveEveryComponent(e,t,n){let r=e.get_parent();return expandSymlinks(r===null?e:y.File.new_for_path(resolveEveryComponent(r,t,n)).get_child(e.get_basename()),t,n)}function expandSymlinks(e,t,n){let r=e;for(;;){let e=r.query_info(`standard::is-symlink,standard::symlink-target`,y.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,null);if(!e.get_is_symlink())return r.get_path();if(--t.left<0)throw Error(`ELOOP: too many levels of symbolic links, realpath '${n}'`);let i=e.get_symlink_target(),a=r.get_parent(),o=v.path_is_absolute(i)?y.File.new_for_path(i):(a??r).resolve_relative_path(i);r=y.File.new_for_path(resolveEveryComponent(o,t,n))}}realpathSync.native=realpathSync;function symlinkSync(e,t,n){let i=r(t),a=r(e);y.File.new_for_path(i).make_symbolic_link(a,null)}function readFileSync(e,i={encoding:null,flag:`r`}){if(e instanceof _&&(e=e.fd),typeof e==`number`&&c(e))return t(n(i,`buffer`),l(e));if(typeof e==`number`)return t(n(i,`buffer`),_.getInstance(e,`read`)._readToEndSync());let o=r(e),s=y.File.new_for_path(o);try{let[e,r]=s.load_contents(null);if(!e)throw a(Error(`failed to read file`),`read`,o);return t(n(i,`buffer`),r)}catch(e){throw e.code&&typeof e.code==`string`?e:a(e,`read`,o)}}function mkdirSync(e,t){let n=!1,i;t!=null&&(typeof t==`object`?(t.recursive&&(n=t.recursive),t.mode!==void 0&&(i=t.mode)):i=t),e=r(e);let a=d(i,511);if(n)return mkdirSyncRecursive(e,a);mkdirWithMode(e,a)}function mkdirWithMode(e,t){if(v.mkdir(e,t)!==0)throw p(e)}function mkdirSyncRecursive(e,t){if(v.mkdir(e,t)===0)return e;let n=p(e);if(n.code===`EEXIST`){if(v.file_test(e,v.FileTest.IS_DIR))return;throw n}if(n.code!==`ENOENT`)throw n;let r=b(e,`..`),i=y.File.new_for_path(r).get_path();if(i===e)throw n;let a=mkdirSyncRecursive(i,t);return mkdirWithMode(e,t),a??e}function rmdirSync(e,t){let n=r(e),i=y.File.new_for_path(n);try{if(i.query_info(`standard::type`,y.FileQueryInfoFlags.NONE,null).get_file_type()!==y.FileType.DIRECTORY)throw a(Object.assign(Error(),{code:4}),`rmdir`,n);if(i.enumerate_children(`standard::name`,y.FileQueryInfoFlags.NONE,null).next_file(null)!==null)throw a(Object.assign(Error(),{code:5}),`rmdir`,n);i.delete(null)}catch(e){throw e.code&&typeof e.code==`string`?e:a(e,`rmdir`,n)}}function unlinkSync(e){let t=r(e),n=y.File.new_for_path(t);try{n.delete(null)}catch(e){throw a(e,`unlink`,t)}}function writeWholeFile(e,t,i,a){let o=n(i,`utf8`),s=typeof i==`object`&&i?i:{},l=typeof t==`string`?x.from(t,o||`utf8`):t;if(typeof e==`number`||e instanceof _){if(typeof e==`number`&&c(e)){u(e,l);return}_.getInstance(e,`write`)._writeSync(l,null);return}let d=new _({path:r(e),flags:s.flag??a,mode:s.mode??438});try{d._writeSync(l,null)}finally{d._closeSync()}}function writeFileSync(e,t,n){writeWholeFile(e,t,n??null,`w`)}function renameSync(e,t){let n=r(e),i=r(t),o=y.File.new_for_path(n),s=y.File.new_for_path(i);try{o.move(s,y.FileCopyFlags.OVERWRITE,null,null)}catch(e){throw a(e,`rename`,n,i)}}function copyFileSync(e,t,n){let i=r(e),o=r(t),s=y.File.new_for_path(i),c=y.File.new_for_path(o),l=y.FileCopyFlags.NONE;n&&!(n&1)?l=y.FileCopyFlags.OVERWRITE:n||(l=y.FileCopyFlags.OVERWRITE);try{s.copy(c,l,null,null)}catch(e){throw a(e,`copyfile`,i,o)}stripPrivilegeBitsAfterCopy(c,i,o)}function stripPrivilegeBitsAfterCopy(e,t,n){try{let t=e.query_info(`unix::mode,standard::size`,y.FileQueryInfoFlags.NONE,null);if(t.get_size()===0)return;let n=t.get_attribute_uint32(`unix::mode`),r=n&-2049;if((C()||n&8)&&(r&=-1025),r===n)return;e.set_attribute_uint32(`unix::mode`,r,y.FileQueryInfoFlags.NONE,null)}catch(e){throw a(e,`copyfile`,t,n)}}function accessSync(e,t){let n=r(e),i=y.File.new_for_path(n);try{let e=i.query_info(`access::*`,y.FileQueryInfoFlags.NONE,null);if(t!==void 0&&t!==0){let r={code:14,message:`permission denied, access '${n}'`};if(t&4&&!e.get_attribute_boolean(`access::can-read`)||t&2&&!e.get_attribute_boolean(`access::can-write`)||t&1&&!e.get_attribute_boolean(`access::can-execute`))throw a(r,`access`,n)}}catch(e){throw e.code&&typeof e.code==`string`?e:a(e,`access`,n)}}function appendFileSync(e,t,n){writeWholeFile(e,t,n??null,`a`)}function readlinkSync(e,t){let n=r(e),i=y.File.new_for_path(n);try{let e=i.query_info(`standard::symlink-target`,y.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,null).get_symlink_target();if(!e)throw Object.assign(Error(`EINVAL: invalid argument, readlink '${n}'`),{code:`EINVAL`,errno:-22,syscall:`readlink`,path:n});return(typeof t==`string`?t:t?.encoding)===`buffer`?x.from(e):e}catch(e){throw typeof e.code==`string`?e:a(e,`readlink`,n)}}function linkSync(e,t){let n=r(e),i=r(t);if(y.File.new_for_path(n).query_file_type(y.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,null)===y.FileType.UNKNOWN)throw Object.assign(Error(`ENOENT: no such file or directory, link '${n}' -> '${i}'`),{code:`ENOENT`,errno:-2,syscall:`link`,path:n,dest:i});if(y.File.new_for_path(i).query_file_type(y.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,null)!==y.FileType.UNKNOWN)throw Object.assign(Error(`EEXIST: file already exists, link '${n}' -> '${i}'`),{code:`EEXIST`,errno:-17,syscall:`link`,path:n,dest:i});let a=``;try{let[,,e,t]=v.spawn_sync(null,[`ln`,`--`,n,i],null,v.SpawnFlags.SEARCH_PATH,null);if(a=e?new TextDecoder().decode(e).trim():``,!v.spawn_check_wait_status(t))throw Error(`ln exited non-zero (${t})`);return}catch(e){throw a===``&&e instanceof Error&&(a=e.message),Object.assign(Error(`EPERM: operation not permitted, link '${n}' -> '${i}'`+(a?` (${a})`:``)),{code:`EPERM`,errno:-1,syscall:`link`,path:n,dest:i})}}function truncateSync(e,t){let n=r(e),i=y.File.new_for_path(n);try{let e=i.open_readwrite(null);try{e.get_output_stream().truncate(Math.max(0,t??0),null)}finally{e.close(null)}}catch(e){throw a(e,`truncate`,n)}}function chmodSync(e,t){let n=r(e),i=d(t);try{y.File.new_for_path(n).set_attribute_uint32(`unix::mode`,i,y.FileQueryInfoFlags.NONE,null)}catch(e){throw a(e,`chmod`,n)}}function chownSync(e,t,n){let i=r(e);try{let e=y.File.new_for_path(i),r=new y.FileInfo;t!==-1&&r.set_attribute_uint32(`unix::uid`,t),n!==-1&&r.set_attribute_uint32(`unix::gid`,n),e.set_attributes_from_info(r,y.FileQueryInfoFlags.NONE,null)}catch(e){throw a(e,`chown`,i)}}function watch(e,t,n){return typeof t==`function`&&(n=t,t=void 0),new s(r(e),t,n)}function openSync(e,t,n){return new _({path:e,flags:t,mode:n})}function mkdtempSync(t,r){let i=n(r),a=mkdtempAt(t);return i===`buffer`?x.from(a):e(a,i)}function mkdtempAt(e){for(let t=0;t<64;t++){let t=e+i();if(v.mkdir(t,448)===0)return t;let n=p(t);if(n.code!==`EEXIST`)throw n.syscall=`mkdtemp`,n}throw m(`EEXIST`,`mkdtemp`,e+`XXXXXX`)}function rmSync(e,t){let n=r(e),i=y.File.new_for_path(n),s=t?.recursive||!1,c=t?.force||!1,l;try{l=i.query_file_type(y.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,null)}catch(t){if(c&&o(t))return;throw a(t,`rm`,e)}if(l===y.FileType.SYMBOLIC_LINK){try{i.delete(null)}catch(t){if(c&&o(t))return;throw a(t,`rm`,e)}return}if(l===y.FileType.DIRECTORY){let r=readdirSync(e);if(!s&&r.length)throw a(Object.assign(Error(),{code:5}),`rm`,e);for(let e of r)rmSync(b(n,e),t)}try{i.delete(null)}catch(t){if(c&&o(t))return;throw a(t,`rm`,e)}}export{accessSync,appendFileSync,chmodSync,chownSync,copyFileSync,S as existsSync,linkSync,lstatSync,mkdirSync,mkdtempAt,mkdtempSync,openSync,readFileSync,readdirSync,readlinkSync,realpathSync,renameSync,rmSync,rmdirSync,statSync,symlinkSync,truncateSync,unlinkSync,watch,writeFileSync};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import{isNotFoundError as e}from"./errors.js";import t from"@girs/gio-2.0";import{basename as n,join as r,relative as i,sep as a}from"node:path";import{isDarwin as o}from"@gjsify/utils/core";function gioEventToNodeType(e){switch(e){case t.FileMonitorEvent.CHANGED:case t.FileMonitorEvent.CHANGES_DONE_HINT:return`change`;case t.FileMonitorEvent.DELETED:case t.FileMonitorEvent.CREATED:case t.FileMonitorEvent.RENAMED:case t.FileMonitorEvent.MOVED_IN:case t.FileMonitorEvent.MOVED_OUT:return`rename`;default:return null}}var WatchTree=class{_monitors=new Map;_fileMonitors=new Map;_closed=!1;_root;_recursive;_fanOut;_budget;_budgetReported=!1;_onEvent;_onError;constructor(e,n,r,i,a){this._recursive=n,this._onEvent=r,this._onError=i,this._fanOut=a?.fanOut??o(),this._budget=a?.budget??(this._fanOut?4096:1/0);let s=t.File.new_for_path(e);this._root=s.get_path()??e,this._open(this._root,s),s.query_file_type(t.FileQueryInfoFlags.NONE,null)===t.FileType.DIRECTORY&&(n||this._fanOut)&&this._descend(this._root,!1)}get monitorCount(){return this._monitors.size+this._fileMonitors.size}close(){this._closed=!0;for(let e of this._monitors.values())e.cancel();this._monitors.clear();for(let e of this._fileMonitors.values())e.cancel();this._fileMonitors.clear()}_open(e,n){let r=n.monitor(t.FileMonitorFlags.NONE,null);r.connect(`changed`,(e,t,n,r)=>{this._dispatch(t,r)}),this._monitors.set(e,r)}_watchFile(n){if(this._closed||this._fileMonitors.has(n)||!this._admit(n))return;let r;try{r=t.File.new_for_path(n).monitor(t.FileMonitorFlags.NONE,null)}catch(t){e(t)||this._onError(t);return}r.connect(`changed`,(e,t,n,r)=>{if(gioEventToNodeType(r)!==`change`)return;let i=t.get_path();i!==null&&this._emit(`change`,i)}),this._fileMonitors.set(n,r)}_admit(e){if(this.monitorCount<this._budget)return!0;if(this._budgetReported)return!1;this._budgetReported=!0;let t=Error(`fs.watch: this watch holds its whole budget of ${this._budget} monitors and stopped at ${e}. Anything under ${this._root} that is not already attached is NOT being watched, and no further event will arrive for it. The budget is finite because this host needs a monitor per FILE rather than per directory, and unbounded growth of a shared process resource surfaces as an unrelated allocation failing elsewhere, in a trace that never mentions fs.watch. Watch a narrower directory.`);return t.code=`ERR_FS_WATCH_MONITOR_BUDGET`,t.path=e,this._onError(t),!1}_dispatch(e,n){let r=e.get_path();if(r===null)return;let i=gioEventToNodeType(n);if(i!==null&&this._emit(i,r),!(!this._recursive&&!this._fanOut))switch(n){case t.FileMonitorEvent.CREATED:case t.FileMonitorEvent.MOVED_IN:{let n=e.query_file_type(t.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,null);n===t.FileType.DIRECTORY?this._recursive&&this._addSubtree(r,!0):this._fanOut&&n===t.FileType.REGULAR&&this._watchFile(r);break}case t.FileMonitorEvent.DELETED:case t.FileMonitorEvent.MOVED_OUT:this._dropSubtree(r);break}}_emit(e,t){let r=i(this._root,t);this._onEvent(e,r===``?n(t):r)}_addSubtree(n,r){if(!(this._closed||this._monitors.has(n))&&this._admit(n)){try{this._open(n,t.File.new_for_path(n))}catch(t){e(t)||this._onError(t);return}this._descend(n,r)}}_descend(e,t){for(let n of this._children(e)){if(this._closed)return;if(n.isDirectory){if(!this._recursive)continue;t&&this._emit(`rename`,n.path),this._addSubtree(n.path,t)}else t&&this._emit(`rename`,n.path),this._fanOut&&n.isFile&&this._watchFile(n.path)}}_dropSubtree(e){this._fileMonitors.get(e)?.cancel(),this._fileMonitors.delete(e);let t=e+a;for(let[e,n]of this._fileMonitors)e.startsWith(t)&&(n.cancel(),this._fileMonitors.delete(e));for(let[n,r]of this._monitors)n!==e&&!n.startsWith(t)||(r.cancel(),this._monitors.delete(n))}_children(n){let i;try{i=t.File.new_for_path(n).enumerate_children(`standard::name,standard::type`,t.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,null)}catch(t){return e(t)||this._onError(t),[]}let a=[];try{let e=i.next_file(null);for(;e!==null;){let o=e.get_file_type();a.push({path:r(n,e.get_name()),isDirectory:o===t.FileType.DIRECTORY,isFile:o===t.FileType.REGULAR}),e=i.next_file(null)}}catch(t){e(t)||this._onError(t)}finally{try{i.close(null)}catch{}}return a}};export{WatchTree};
|
package/lib/types/sync.d.ts
CHANGED
|
@@ -72,11 +72,14 @@ export declare function linkSync(existingPath: PathLike, newPath: PathLike): voi
|
|
|
72
72
|
export declare function truncateSync(path: PathLike, len?: number): void;
|
|
73
73
|
export declare function chmodSync(path: PathLike, mode: Mode): void;
|
|
74
74
|
export declare function chownSync(path: PathLike, uid: number, gid: number): void;
|
|
75
|
-
|
|
75
|
+
type WatchListener = (eventType: string, filename: string | null) => void;
|
|
76
|
+
type WatchOpts = {
|
|
76
77
|
persistent?: boolean;
|
|
77
78
|
recursive?: boolean;
|
|
78
79
|
encoding?: string;
|
|
79
|
-
}
|
|
80
|
+
};
|
|
81
|
+
export declare function watch(filename: PathLike, listener?: WatchListener): FSWatcher;
|
|
82
|
+
export declare function watch(filename: PathLike, options?: WatchOpts, listener?: WatchListener): FSWatcher;
|
|
80
83
|
export declare function openSync(path: PathLike, flags?: OpenFlags | number, mode?: Mode): FileHandle;
|
|
81
84
|
/**
|
|
82
85
|
* Create a unique temporary directory: six random characters appended to `prefix`.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/** The two event names Node's `fs.watch` listener signature discriminates on. */
|
|
2
|
+
export type WatchEventType = 'rename' | 'change';
|
|
3
|
+
/**
|
|
4
|
+
* The two host-derived decisions, made overridable so their own behaviour can be
|
|
5
|
+
* exercised anywhere.
|
|
6
|
+
*
|
|
7
|
+
* Not configuration: nothing outside this package's own specs passes either one.
|
|
8
|
+
* They exist because both defaults are decided by the HOST, and a mechanism that
|
|
9
|
+
* only runs on the one host CI has least of is a mechanism measured once. With
|
|
10
|
+
* `fanOut` forced on, a Linux run exercises attach, re-attach and disposal of the
|
|
11
|
+
* per-file monitors — the half of `close()` that a darwin-only path would leave
|
|
12
|
+
* to a single leg; with `budget` set low, every run exercises the refusal and the
|
|
13
|
+
* error it raises.
|
|
14
|
+
*/
|
|
15
|
+
export interface WatchTreeOverrides {
|
|
16
|
+
/** Give every FILE its own monitor, not only every directory (see the header). */
|
|
17
|
+
fanOut?: boolean;
|
|
18
|
+
/** How many monitors this watch may hold before it refuses, loudly. */
|
|
19
|
+
budget?: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Every `Gio.FileMonitor` behind ONE `fs.watch()` call, and the only place they are
|
|
23
|
+
* created, replaced and disposed.
|
|
24
|
+
*
|
|
25
|
+
* Both entry points delegate here — `FSWatcher` (`fs.watch`) and `watchAsync`
|
|
26
|
+
* (`fs.promises.watch`) — so the non-recursive case is not a second code path that
|
|
27
|
+
* can drift from the recursive one: `recursive: false` is this class holding
|
|
28
|
+
* exactly one directory monitor, plus the per-file fan-out where the host needs it.
|
|
29
|
+
*/
|
|
30
|
+
export declare class WatchTree {
|
|
31
|
+
/** One monitor per watched directory, keyed by its absolute path. */
|
|
32
|
+
private _monitors;
|
|
33
|
+
/** The fan-out: one monitor per watched FILE. Empty where the host needs none. */
|
|
34
|
+
private _fileMonitors;
|
|
35
|
+
private _closed;
|
|
36
|
+
private _root;
|
|
37
|
+
private _recursive;
|
|
38
|
+
/** Does a directory monitor on this host miss writes to its children? (header) */
|
|
39
|
+
private _fanOut;
|
|
40
|
+
private _budget;
|
|
41
|
+
/** The budget is announced ONCE — see `_refuseAttach`. */
|
|
42
|
+
private _budgetReported;
|
|
43
|
+
private _onEvent;
|
|
44
|
+
private _onError;
|
|
45
|
+
constructor(rootPath: string, recursive: boolean, onEvent: (eventType: WatchEventType, filename: string) => void, onError: (err: unknown) => void, overrides?: WatchTreeOverrides);
|
|
46
|
+
/**
|
|
47
|
+
* How many monitors this watch holds — what the budget counts, and what
|
|
48
|
+
* `close()` has to bring back to zero.
|
|
49
|
+
*/
|
|
50
|
+
get monitorCount(): number;
|
|
51
|
+
close(): void;
|
|
52
|
+
/** Create the monitor for `path` and wire it up. Throws what `g_file_monitor()` throws. */
|
|
53
|
+
private _open;
|
|
54
|
+
/**
|
|
55
|
+
* Attach the fan-out monitor for one FILE.
|
|
56
|
+
*
|
|
57
|
+
* It reports ONLY modifications, because a create and a delete are the half a
|
|
58
|
+
* directory monitor does deliver on every backend measured here — mapping them
|
|
59
|
+
* a second time would emit each of them twice.
|
|
60
|
+
*
|
|
61
|
+
* An editor's atomic same-name replace was the case that looked like it might
|
|
62
|
+
* need more: the path keeps its name and gains a new inode, so a directory
|
|
63
|
+
* monitor has no name change to report and a monitor bound to the old inode
|
|
64
|
+
* would go DELETED and stop. Measured on both hosts rather than reasoned about
|
|
65
|
+
* (`watch-backend.gjs.spec.ts`): BOTH shapes report it, and the file monitor
|
|
66
|
+
* follows the PATH rather than the inode — DELETED, CREATED and
|
|
67
|
+
* CHANGES_DONE_HINT all arrive on it, and it keeps working afterwards. So the
|
|
68
|
+
* last of those maps to 'change' here and no re-arm is needed.
|
|
69
|
+
*/
|
|
70
|
+
private _watchFile;
|
|
71
|
+
/**
|
|
72
|
+
* Is there budget left for one more monitor? Refuses LOUDLY when there is not.
|
|
73
|
+
*
|
|
74
|
+
* Announced once per watch, not once per path: past the limit every remaining
|
|
75
|
+
* entry raises the same fact, and a storm of them is another way to be unread.
|
|
76
|
+
* The one error that is raised says the watch is incomplete from here on, which
|
|
77
|
+
* is what a consumer has to act on.
|
|
78
|
+
*/
|
|
79
|
+
private _admit;
|
|
80
|
+
private _dispatch;
|
|
81
|
+
private _emit;
|
|
82
|
+
/**
|
|
83
|
+
* Start watching `dir` and everything below it, announcing what is found when
|
|
84
|
+
* `announce` says this subtree is NEW.
|
|
85
|
+
*
|
|
86
|
+
* The announcement is not decoration: a directory can be created AND filled
|
|
87
|
+
* before this monitor attaches (`mkdir -p src/components && cp Button.tsx
|
|
88
|
+
* src/components/`), and those entries have no CREATED event of their own left to
|
|
89
|
+
* arrive on. Node closes the same window the same way, in `#watchFolder`. The
|
|
90
|
+
* startup walk passes `false` all the way down, because nothing that already
|
|
91
|
+
* existed when the watch began is news.
|
|
92
|
+
*/
|
|
93
|
+
private _addSubtree;
|
|
94
|
+
private _descend;
|
|
95
|
+
/** Drop the monitors for `path`, whether it is a watched file or a whole subtree. */
|
|
96
|
+
private _dropSubtree;
|
|
97
|
+
private _children;
|
|
98
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gjsify/fs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.43.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,9 +66,9 @@
|
|
|
66
66
|
"fs"
|
|
67
67
|
],
|
|
68
68
|
"devDependencies": {
|
|
69
|
-
"@gjsify/cli": "^0.
|
|
70
|
-
"@gjsify/unit": "^0.
|
|
71
|
-
"@gjsify/web-streams": "^0.
|
|
69
|
+
"@gjsify/cli": "^0.43.0",
|
|
70
|
+
"@gjsify/unit": "^0.43.0",
|
|
71
|
+
"@gjsify/web-streams": "^0.43.0",
|
|
72
72
|
"@types/node": "^25.9.2",
|
|
73
73
|
"typescript": "^6.0.3"
|
|
74
74
|
},
|
|
@@ -76,11 +76,11 @@
|
|
|
76
76
|
"@girs/gio-2.0": "^4.1.0",
|
|
77
77
|
"@girs/giounix-2.0": "^4.1.0",
|
|
78
78
|
"@girs/glib-2.0": "^4.1.0",
|
|
79
|
-
"@gjsify/buffer": "^0.
|
|
80
|
-
"@gjsify/events": "^0.
|
|
81
|
-
"@gjsify/stream": "^0.
|
|
82
|
-
"@gjsify/url": "^0.
|
|
83
|
-
"@gjsify/utils": "^0.
|
|
79
|
+
"@gjsify/buffer": "^0.43.0",
|
|
80
|
+
"@gjsify/events": "^0.43.0",
|
|
81
|
+
"@gjsify/stream": "^0.43.0",
|
|
82
|
+
"@gjsify/url": "^0.43.0",
|
|
83
|
+
"@gjsify/utils": "^0.43.0"
|
|
84
84
|
},
|
|
85
85
|
"gjsify": {
|
|
86
86
|
"runtimes": {
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"win32": "none"
|
|
96
96
|
},
|
|
97
97
|
"osNotes": {
|
|
98
|
-
"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. copyFileSync additionally branches on isDarwin(): the kernel write-time privilege strip is not portable — Linux's should_remove_suid() keeps S_ISGID without S_IXGRP, XNU strips it either way (measured on both macOS legs, 2644 -> 0644 vs 2644 -> 2644). Node inherits that for free because the kernel does the stripping; we spell it.",
|
|
98
|
+
"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. copyFileSync additionally branches on isDarwin(): the kernel write-time privilege strip is not portable — Linux's should_remove_suid() keeps S_ISGID without S_IXGRP, XNU strips it either way (measured on both macOS legs, 2644 -> 0644 vs 2644 -> 2644). Node inherits that for free because the kernel does the stripping; we spell it. watch-tree.ts branches on isDarwin() a second time, for fs.watch: a Gio DIRECTORY monitor here reports a child being created or deleted but never a write landing inside an existing child (measured through a raw Gio.FileMonitor on both macOS legs; the probe is watch-backend.gjs.spec.ts and stays in the suite), so every watched FILE is given a monitor of its own. That is not a recursion feature -- a flat fs.watch(dir, cb) needs it too, or a rewrite of dir/config.json produces no event at all. The residual gap, and why this stays partial: the fan-out grows one monitor per FILE where Node's FSEvents stream costs one subscription for the whole tree, so it is BOUNDED -- a watch over a tree larger than the budget raises ERR_FS_WATCH_MONITOR_BUDGET naming the limit and the path it stopped at, and is incomplete from there on. What one monitor costs the kernel here is NOT established: the macOS legs report a soft RLIMIT_NOFILE of 1048575 and a descriptor delta of zero across 27 monitors, so the budget bounds growth without claiming to ration descriptors.",
|
|
99
99
|
"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."
|
|
100
100
|
},
|
|
101
101
|
"tier": 1
|