@robota-sdk/agent-file-authority 3.0.0-beta.82 → 3.0.0-beta.83

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/CHANGELOG.md ADDED
@@ -0,0 +1,42 @@
1
+ # @robota-sdk/agent-file-authority
2
+
3
+ ## 3.0.0-beta.83
4
+
5
+ ### Patch Changes
6
+
7
+ - 57280bf: Every published package now declares `"engines": { "node": ">=22.12.0" }`. Before, 27 of the 38
8
+ packages declared no floor (`agent-core`, `agent-tools` and every provider among them),
9
+ `agent-session` and `agent-file-authority` declared `>=20.19.0`, and the other nine declared
10
+ `>=22.0.0`, so a consumer on Node 20 saw at most a warning from a transitive dependency.
11
+
12
+ Why 22.12: `agent-cli` and `agent-ui-terminal` need Node 22 through `ink` 7, and the CommonJS entries
13
+ of `agent-tools` and its dependents, `agent-transport`/`node` and its dependents, and
14
+ `agent-ui-terminal` `require()` ESM-only dependencies (`p-limit`, `jose`, `chalk`), which Node 22
15
+ supports unflagged only from 22.12. `engines` is advisory unless the consumer enables `engine-strict`.
16
+
17
+ No code changes: `tsdown` now reads `node22.12.0` as its build target from the field.
18
+
19
+ - 18c0d5c: Every published package now exports `./package.json`, so `require('<package>/package.json')` and
20
+ `import('<package>/package.json', { with: { type: 'json' } })` work instead of failing with
21
+ `ERR_PACKAGE_PATH_NOT_EXPORTED`, and each tarball now ships the package's `CHANGELOG.md`.
22
+
23
+ ## 3.0.0-beta.82
24
+
25
+ ## 3.0.0-beta.81
26
+
27
+ ## 3.0.0-beta.80
28
+
29
+ ### Minor Changes
30
+
31
+ - fcb0da3: PAYLOAD-2153: make external-payload replay stable across Linux, macOS, and Windows.
32
+
33
+ - Add the domain-free `@robota-sdk/agent-file-authority` leaf with bounded, root-relative reads over retained native handles and a typed, path-safe refusal taxonomy.
34
+ - Route session replay and framework project reads through the shared authority while preserving their existing domain-specific budgets, integrity checks, and error mappings.
35
+ - Expose the canonical safe session-id predicate through the framework facade so CLI exact-session lookup stays within the SDK package boundary.
36
+ - Package the pinned native bridge in clean-installed Node CLI archives and exact-host standalone Bun binaries, refusing unsupported or mismatched targets before artifact mutation.
37
+
38
+ ## 3.0.0-beta.79
39
+
40
+ ### Minor Changes
41
+
42
+ - Add the initial stable root-relative file-read authority.
package/README.md CHANGED
@@ -17,6 +17,6 @@ budgets are explicit `StableFileAuthorityError` results. Raw descriptors, Window
17
17
  paths, and pathname fallbacks are never exposed.
18
18
 
19
19
  The qualified native targets are Linux x64/arm64, macOS x64/arm64, and Windows x64 on Node.js
20
- 20.19 or newer. Other targets fail closed with `UNSUPPORTED_BACKEND`.
20
+ 22.12 or newer. Other targets fail closed with `UNSUPPORTED_BACKEND`.
21
21
 
22
22
  See [the package specification](./docs/SPEC.md) for the contract and security boundaries.
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("node:path"),l=require("node:fs"),u=require("koffi");u=s(u,1);const d={INVALID_PATH:`The requested root-relative file operation is invalid.`,UNSAFE_ENTRY:`The requested entry cannot be opened without following an unsafe object.`,UNSUPPORTED_BACKEND:`This host cannot provide stable root-relative file reads.`,AUTHORITY_CLOSED:`The stable rooted file authority is closed.`,ROOT_CHANGED:`The retained root identity changed during the operation.`,FILE_CHANGED:`The file identity or contents changed during the bounded read.`,OVER_BUDGET:`The file exceeds the supplied byte budget.`,HOST_IO:`The host could not complete the stable root-relative file operation.`};var f=class extends Error{code;context;constructor(e,t){super(d[e]),this.name=`StableFileAuthorityError`,this.code=e,this.context=Object.freeze({...t})}};function p(e,t,n={}){return new f(e,{operation:t,...n})}function m(e){return{device:e.dev,inode:e.ino,mode:e.mode}}function h(e){return{...m(e),size:e.size,modified:e.mtimeNs,changed:e.ctimeNs}}function g(e,t){return e.device===t.device&&e.inode===t.inode&&e.mode===t.mode}function _(e,t){return g(e,t)&&e.size===t.size&&e.modified===t.modified&&e.changed===t.changed}function v(e){for(let[t,n]of Object.entries(u.os.errno))if(n===e)return t;return`ERRNO_${e}`}function y(e){let t=l.constants.O_CLOEXEC??(process.platform===`darwin`?16777216:524288);return l.constants.O_RDONLY|l.constants.O_NOFOLLOW|l.constants.O_NONBLOCK|t|(e?l.constants.O_DIRECTORY:0)}function b(e,t){try{return(0,l.fstatSync)(e,{bigint:!0})}catch(e){let n=x(typeof e==`object`?e:null);throw p(`HOST_IO`,t,n===void 0?{}:{hostCode:n})}}function x(e){if(!(e===null||!(`code`in e)))return typeof e.code==`string`?e.code:void 0}var S=class{openAt;rootIdentity;rootDescriptor;constructor(e){try{let t=u.load(process.platform===`darwin`?`/usr/lib/libSystem.B.dylib`:`libc.so.6`),n=t.func(`int open(const char *path, int flags)`);this.openAt=t.func(`int openat(int fd, const char *path, int flags)`);let r=n(e,y(!0));r<0&&this.throwOpenFailure(u.errno(),`open-root`),this.rootDescriptor=r;let i=b(r,`inspect-root`);if(!i.isDirectory())throw p(`UNSAFE_ENTRY`,`open-root`);this.rootIdentity=m(i)}catch(e){throw this.rootDescriptor!==void 0&&(this.closeDescriptor(this.rootDescriptor),this.rootDescriptor=void 0),e instanceof f?e:p(`UNSUPPORTED_BACKEND`,`load-posix-backend`)}}readBytes(e,t,n){let r=this.rootDescriptor;if(r===void 0)throw p(`AUTHORITY_CLOSED`,`read`);this.assertRootIdentity(r);let i=r,a=!1,o;try{for(let t=0;t<e.length;t+=1){let r=t===e.length-1,s=this.openAt(i,e[t],y(!r));if(s<0){let e=u.errno();if(e===u.os.errno.ENOENT)return;this.throwOpenFailure(e,r?`open-file`:`open-directory`,t)}if(r){o=s;break}if(!b(s,`inspect-directory`).isDirectory())throw this.closeDescriptor(s),p(`UNSAFE_ENTRY`,`inspect-directory`,{segmentIndex:t});a&&this.closeDescriptor(i),i=s,a=!0,n.afterDirectoryOpened?.(t)}if(o===void 0)throw p(`HOST_IO`,`open-file`);let s=this.readFile(o,t,n);return this.assertRootIdentity(r),s}catch(e){if(e instanceof f)throw e;let t=x(typeof e==`object`?e:null);throw p(`HOST_IO`,`read`,t===void 0?{}:{hostCode:t})}finally{this.closeOptionalDescriptor(o),this.closeOptionalDescriptor(a?i:void 0)}}close(){let e=this.rootDescriptor;e!==void 0&&(this.rootDescriptor=void 0,this.closeDescriptor(e))}readFile(e,t,n){let r=b(e,`inspect-file`);if(!r.isFile())throw p(`UNSAFE_ENTRY`,`inspect-file`);if(r.size>BigInt(t))throw p(`OVER_BUDGET`,`read-file`);let i=Number(r.size),a;try{a=Buffer.alloc(i)}catch{throw p(`HOST_IO`,`allocate-read-buffer`)}let o=0;for(;o<i;){let t=(0,l.readSync)(e,a,o,Math.min(1048576,i-o),o);if(t===0)break;o+=t,n.afterReadChunk?.(o)}let s=(0,l.readSync)(e,Buffer.alloc(1),0,1,i)!==0,c=b(e,`reinspect-file`);if(s&&i>=t)throw p(`OVER_BUDGET`,`read-file`);if(o!==i||s||!_(h(r),h(c)))throw p(`FILE_CHANGED`,`read-file`);return a}assertRootIdentity(e){let t=b(e,`inspect-root`);if(!t.isDirectory()||!g(this.rootIdentity,m(t)))throw p(`ROOT_CHANGED`,`inspect-root`)}throwOpenFailure(e,t,n){let r=v(e),i=n===void 0?{hostCode:r}:{hostCode:r,segmentIndex:n};throw e===u.os.errno.ELOOP||e===u.os.errno.ENOTDIR||e===u.os.errno.EISDIR?p(`UNSAFE_ENTRY`,t,i):p(`HOST_IO`,t,i)}closeDescriptor(e){try{(0,l.closeSync)(e)}catch{}}closeOptionalDescriptor(e){e!==void 0&&this.closeDescriptor(e)}};function C(){let e=u.pointer(`RobotaStableFileHandle`,u.opaque()),t=u.struct(`RobotaUnicodeString`,{Length:`uint16_t`,MaximumLength:`uint16_t`,Buffer:u.pointer(`char16_t`)});return{handleType:e,unicodeStringType:t,objectAttributesType:u.struct(`RobotaObjectAttributes`,{Length:`uint32_t`,RootDirectory:e,ObjectName:u.pointer(t),Attributes:`uint32_t`,SecurityDescriptor:`void *`,SecurityQualityOfService:`void *`}),ioStatusBlockType:u.struct(`RobotaIoStatusBlock`,{Status:`intptr_t`,Information:`uintptr_t`}),attributeTagInfoType:u.struct(`RobotaFileAttributeTagInfo`,{FileAttributes:`uint32_t`,ReparseTag:`uint32_t`}),fileIdInfoType:u.struct(`RobotaFileIdInfo`,{VolumeSerialNumber:`uint64_t`,FileId:u.array(`uint8_t`,16,`Typed`)}),standardInfoType:u.struct(`RobotaFileStandardInfo`,{AllocationSize:`int64_t`,EndOfFile:`int64_t`,NumberOfLinks:`uint32_t`,DeletePending:`uint8_t`,Directory:`uint8_t`})}}function w(e,t){return{createFileW:e.func(`CreateFileW`,t.handleType,[u.pointer(`char16_t`),`uint32_t`,`uint32_t`,`void *`,`uint32_t`,`uint32_t`,t.handleType]),closeHandle:e.func(`CloseHandle`,`bool`,[t.handleType]),...T(e,t),getFileSizeEx:e.func(`GetFileSizeEx`,`bool`,[t.handleType,u.out(u.pointer(`int64_t`))]),readFile:e.func(`ReadFile`,`bool`,[t.handleType,u.out(u.pointer(`uint8_t`)),`uint32_t`,u.out(u.pointer(`uint32_t`)),`void *`]),getLastError:e.func(`GetLastError`,`uint32_t`,[])}}function T(e,t){let n=n=>e.func(`GetFileInformationByHandleEx`,`bool`,[t.handleType,`int32_t`,u.out(u.pointer(n)),`uint32_t`]);return{getAttributeTagInfo:n(t.attributeTagInfoType),getFileIdInfo:n(t.fileIdInfoType),getStandardInfo:n(t.standardInfoType)}}function E(e,t){return{ntCreateFile:e.func(`NtCreateFile`,`int32_t`,[u.out(u.pointer(t.handleType)),`uint32_t`,u.pointer(t.objectAttributesType),u.out(u.pointer(t.ioStatusBlockType)),`void *`,`uint32_t`,`uint32_t`,`uint32_t`,`uint32_t`,`void *`,`uint32_t`]),ntClose:e.func(`NtClose`,`int32_t`,[t.handleType])}}let D;function O(){if(D!==void 0)return D;let e=C();return D={...e,...w(u.load(`kernel32.dll`),e),...E(u.load(`ntdll.dll`),e)},D}const k=BigInt(`0`);function A(e){if(e instanceof Uint8Array||Array.isArray(e))return Buffer.from(e).toString(`hex`);throw p(`UNSUPPORTED_BACKEND`,`decode-file-identity`)}function j(e){return`WIN32_${e.getLastError()}`}function M(e,t,n){let r={};if(!e.getFileIdInfo(t,18,r,u.sizeof(e.fileIdInfoType)))throw p(`UNSUPPORTED_BACKEND`,n,{hostCode:j(e)});return{volume:BigInt(r.VolumeSerialNumber??k),fileId:A(r.FileId)}}function N(e,t,n,r){let i=F(e,t);if(i.size>BigInt(n))throw p(`OVER_BUDGET`,`read-file`);let a=Number(i.size),o;try{o=Buffer.alloc(a)}catch{throw p(`HOST_IO`,`allocate-read-buffer`)}let s=P(e,t,o,r),c=[0];if(!e.readFile(t,Buffer.alloc(1),1,c,null))throw p(`HOST_IO`,`probe-file`,{hostCode:j(e)});let l=F(e,t);if(c[0]!==0&&a>=n)throw p(`OVER_BUDGET`,`read-file`);if(!I(i,l,s,a,c[0]))throw p(`FILE_CHANGED`,`read-file`);return o}function P(e,t,n,r){let i=0;for(;i<n.length;){let a=Math.min(1048576,n.length-i),o=[0];if(!e.readFile(t,n.subarray(i,i+a),a,o,null))throw p(`HOST_IO`,`read-file`,{hostCode:j(e)});if(o[0]===0)break;i+=o[0],r.afterReadChunk?.(i)}return i}function F(e,t){let n=M(e,t,`inspect-file`),r=[k];if(!e.getFileSizeEx(t,r))throw p(`HOST_IO`,`inspect-file`,{hostCode:j(e)});return{...n,size:BigInt(r[0])}}function I(e,t,n,r,i){return n===r&&i===0&&e.volume===t.volume&&e.fileId===t.fileId&&e.size===t.size}const L=new Set([3221225487,3221225524,3221225530]),R=new Set([2147483693,3221225658,3221225731,3221226105,3221226763]);function z(e){let t=e>>>0;return{kind:L.has(t)?`missing`:R.has(t)?`unsafe`:`host-io`,hostCode:`NTSTATUS_0x${t.toString(16)}`}}const B=BigInt(`-1`);function V(e){return e===null||BigInt.asIntN(64,e)===B}var H=class{api;constructor(){try{this.api=O()}catch{throw p(`UNSUPPORTED_BACKEND`,`load-windows-backend`)}}openRoot(e){let t=this.api.createFileW(e,2147483648,7,null,3,35651584,null);if(V(t))throw p(`HOST_IO`,`open-root`,{hostCode:this.lastErrorCode()});try{return this.assertSafeHandle(t,!0,`inspect-root`),t}catch(e){throw this.close(t),e}}openRelative(e,t,n,r){let{handle:i,status:a}=this.issueRelativeOpen(e,t,n);if(a>=0){if(V(i))throw p(`HOST_IO`,`open-relative`);try{return this.assertSafeHandle(i,!n,n?`inspect-file`:`inspect-directory`,r),i}catch(e){throw this.close(i),e}}V(i)||this.close(i);let o=z(a);if(o.kind===`missing`)return;let s={segmentIndex:r,hostCode:o.hostCode};throw o.kind===`unsafe`?p(`UNSAFE_ENTRY`,n?`open-file`:`open-directory`,s):p(`HOST_IO`,n?`open-file`:`open-directory`,s)}readFile(e,t,n){return N(this.api,e,t,n)}queryIdentity(e,t){return M(this.api,e,t)}close(e){try{if(this.api.ntClose(e)>=0)return}catch{}try{this.api.closeHandle(e)}catch{}}issueRelativeOpen(e,t,n){let r=u.alloc(this.api.unicodeStringType,1);try{u.encode(r,this.api.unicodeStringType,{Length:Buffer.byteLength(t,`utf16le`),MaximumLength:Buffer.byteLength(`${t}\0`,`utf16le`),Buffer:t});let i={Length:u.sizeof(this.api.objectAttributesType),RootDirectory:e,ObjectName:r,Attributes:4160,SecurityDescriptor:null,SecurityQualityOfService:null},a=[null],o={Status:0,Information:0},s=n?5:7,c=this.api.ntCreateFile(a,1048705,i,o,null,0,s,1,(n?64:1)|2097184,null,0);return{handle:a[0],status:c}}finally{u.free(r)}}assertSafeHandle(e,t,n,r){let i={},a={};if(!this.api.getAttributeTagInfo(e,9,i,u.sizeof(this.api.attributeTagInfoType))||!this.api.getStandardInfo(e,1,a,u.sizeof(this.api.standardInfoType)))throw p(`HOST_IO`,n,{hostCode:this.lastErrorCode()});if((i.FileAttributes??0)&1024||!!a.Directory!==t)throw p(`UNSAFE_ENTRY`,n,r===void 0?{}:{segmentIndex:r})}lastErrorCode(){return j(this.api)}},U=class{native=new H;rootIdentity;rootHandle;constructor(e){try{let t=this.native.openRoot(e);this.rootHandle=t,this.rootIdentity=this.native.queryIdentity(t,`inspect-root`)}catch(e){throw this.rootHandle!==void 0&&(this.native.close(this.rootHandle),this.rootHandle=void 0),e instanceof f?e:p(`UNSUPPORTED_BACKEND`,`load-windows-backend`)}}readBytes(e,t,n){let r=this.rootHandle;if(r===void 0)throw p(`AUTHORITY_CLOSED`,`read`);this.assertRootIdentity(r);let i=r,a=!1,o;try{for(let t=0;t<e.length;t+=1){let r=t===e.length-1,s=this.native.openRelative(i,e[t],r,t);if(s===void 0)return;if(r){o=s;break}a&&this.native.close(i),i=s,a=!0,n.afterDirectoryOpened?.(t)}if(o===void 0)throw p(`HOST_IO`,`open-file`);let s=this.native.readFile(o,t,n);return this.assertRootIdentity(r),s}catch(e){throw e instanceof f?e:p(`HOST_IO`,`read`)}finally{o!==void 0&&this.native.close(o),a&&this.native.close(i)}}close(){let e=this.rootHandle;e!==void 0&&(this.rootHandle=void 0,this.native.close(e))}assertRootIdentity(e){let t=this.native.queryIdentity(e,`inspect-root`);if(t.volume!==this.rootIdentity.volume||t.fileId!==this.rootIdentity.fileId)throw p(`ROOT_CHANGED`,`inspect-root`)}};const W=new Set([`darwin-arm64`,`darwin-x64`,`linux-arm64`,`linux-x64`,`win32-x64`]),G=new FinalizationRegistry(e=>{e.close()});function K(e){if(typeof e!=`string`||e.trim().length===0||e.includes(`\0`))throw p(`INVALID_PATH`,`validate-root`);return(0,c.resolve)(e)}function q(e,t){if(!Array.isArray(e)||e.length===0)throw p(`INVALID_PATH`,`validate-segments`);for(let t=0;t<e.length;t+=1){let n=e[t];if(typeof n!=`string`||n.length===0||n===`.`||n===`..`||n.includes(`\0`)||n.includes(`/`)||n.includes(`\\`))throw p(`INVALID_PATH`,`validate-segments`,{segmentIndex:t})}if(!Number.isSafeInteger(t)||t<0)throw p(`INVALID_PATH`,`validate-budget`)}function J(e){let t=`${process.platform}-${process.arch}`;if(!W.has(t))throw p(`UNSUPPORTED_BACKEND`,`select-backend`,{hostCode:t});return process.platform===`win32`?new U(e):new S(e)}var Y=class{hooks;backend;finalizerToken={};constructor(e,t){this.hooks=t;let n=J(K(e));this.backend=n,G.register(this,n,this.finalizerToken)}readBytes(e,t){let n=this.backend;if(n===void 0)throw p(`AUTHORITY_CLOSED`,`read`);q(e,t);try{return n.readBytes(e,t,this.hooks)}catch(e){throw e instanceof f?e:p(`HOST_IO`,`read`)}}close(){let e=this.backend;if(e!==void 0){this.backend=void 0,G.unregister(this.finalizerToken);try{e.close()}catch(e){throw e instanceof f?e:p(`HOST_IO`,`close`)}}}[Symbol.dispose](){this.close()}};function X(e){return new Y(e,{})}exports.StableFileAuthorityError=f,exports.createStableRootedFileReader=X;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("node:path"),l=require("node:fs"),u=require("koffi");u=s(u,1);const d={INVALID_PATH:`The requested root-relative file operation is invalid.`,UNSAFE_ENTRY:`The requested entry cannot be opened without following an unsafe object.`,UNSUPPORTED_BACKEND:`This host cannot provide stable root-relative file reads.`,AUTHORITY_CLOSED:`The stable rooted file authority is closed.`,ROOT_CHANGED:`The retained root identity changed during the operation.`,FILE_CHANGED:`The file identity or contents changed during the bounded read.`,OVER_BUDGET:`The file exceeds the supplied byte budget.`,HOST_IO:`The host could not complete the stable root-relative file operation.`};var f=class extends Error{code;context;constructor(e,t){super(d[e]),this.name=`StableFileAuthorityError`,this.code=e,this.context=Object.freeze({...t})}};function p(e,t,n={}){return new f(e,{operation:t,...n})}function m(e){return{device:e.dev,inode:e.ino,mode:e.mode}}function h(e){return{...m(e),size:e.size,modified:e.mtimeNs,changed:e.ctimeNs}}function g(e,t){return e.device===t.device&&e.inode===t.inode&&e.mode===t.mode}function _(e,t){return g(e,t)&&e.size===t.size&&e.modified===t.modified&&e.changed===t.changed}function v(e){for(let[t,n]of Object.entries(u.os.errno))if(n===e)return t;return`ERRNO_${e}`}function y(e){let t=l.constants.O_CLOEXEC??(process.platform===`darwin`?16777216:524288);return l.constants.O_RDONLY|l.constants.O_NOFOLLOW|l.constants.O_NONBLOCK|t|(e?l.constants.O_DIRECTORY:0)}function b(e,t){try{return(0,l.fstatSync)(e,{bigint:!0})}catch(e){let n=x(typeof e==`object`?e:null);throw p(`HOST_IO`,t,n===void 0?{}:{hostCode:n})}}function x(e){if(!(e===null||!(`code`in e)))return typeof e.code==`string`?e.code:void 0}let S;const C=[];function w(){if(!S){let e=u.load(process.platform===`darwin`?`/usr/lib/libSystem.B.dylib`:`libc.so.6`);C.push(e),S={library:e,open:e.func(`int open(const char *path, int flags)`),openAt:e.func(`int openat(int fd, const char *path, int flags)`)}}return S}var T=class{openAt;rootIdentity;rootDescriptor;constructor(e){try{let{open:t,openAt:n}=w();this.openAt=n;let r=t(e,y(!0));r<0&&this.throwOpenFailure(u.errno(),`open-root`),this.rootDescriptor=r;let i=b(r,`inspect-root`);if(!i.isDirectory())throw p(`UNSAFE_ENTRY`,`open-root`);this.rootIdentity=m(i)}catch(e){throw this.rootDescriptor!==void 0&&(this.closeDescriptor(this.rootDescriptor),this.rootDescriptor=void 0),e instanceof f?e:p(`UNSUPPORTED_BACKEND`,`load-posix-backend`)}}readBytes(e,t,n){let r=this.rootDescriptor;if(r===void 0)throw p(`AUTHORITY_CLOSED`,`read`);this.assertRootIdentity(r);let i=r,a=!1,o;try{for(let t=0;t<e.length;t+=1){let r=t===e.length-1,s=this.openAt(i,e[t],y(!r));if(s<0){let e=u.errno();if(e===u.os.errno.ENOENT)return;this.throwOpenFailure(e,r?`open-file`:`open-directory`,t)}if(r){o=s;break}if(!b(s,`inspect-directory`).isDirectory())throw this.closeDescriptor(s),p(`UNSAFE_ENTRY`,`inspect-directory`,{segmentIndex:t});a&&this.closeDescriptor(i),i=s,a=!0,n.afterDirectoryOpened?.(t)}if(o===void 0)throw p(`HOST_IO`,`open-file`);let s=this.readFile(o,t,n);return this.assertRootIdentity(r),s}catch(e){if(e instanceof f)throw e;let t=x(typeof e==`object`?e:null);throw p(`HOST_IO`,`read`,t===void 0?{}:{hostCode:t})}finally{this.closeOptionalDescriptor(o),this.closeOptionalDescriptor(a?i:void 0)}}close(){let e=this.rootDescriptor;e!==void 0&&(this.rootDescriptor=void 0,this.closeDescriptor(e))}readFile(e,t,n){let r=b(e,`inspect-file`);if(!r.isFile())throw p(`UNSAFE_ENTRY`,`inspect-file`);if(r.size>BigInt(t))throw p(`OVER_BUDGET`,`read-file`);let i=Number(r.size),a;try{a=Buffer.alloc(i)}catch{throw p(`HOST_IO`,`allocate-read-buffer`)}let o=0;for(;o<i;){let t=(0,l.readSync)(e,a,o,Math.min(1048576,i-o),o);if(t===0)break;o+=t,n.afterReadChunk?.(o)}let s=(0,l.readSync)(e,Buffer.alloc(1),0,1,i)!==0,c=b(e,`reinspect-file`);if(s&&i>=t)throw p(`OVER_BUDGET`,`read-file`);if(o!==i||s||!_(h(r),h(c)))throw p(`FILE_CHANGED`,`read-file`);return a}assertRootIdentity(e){let t=b(e,`inspect-root`);if(!t.isDirectory()||!g(this.rootIdentity,m(t)))throw p(`ROOT_CHANGED`,`inspect-root`)}throwOpenFailure(e,t,n){let r=v(e),i=n===void 0?{hostCode:r}:{hostCode:r,segmentIndex:n};throw e===u.os.errno.ELOOP||e===u.os.errno.ENOTDIR||e===u.os.errno.EISDIR?p(`UNSAFE_ENTRY`,t,i):p(`HOST_IO`,t,i)}closeDescriptor(e){try{(0,l.closeSync)(e)}catch{}}closeOptionalDescriptor(e){e!==void 0&&this.closeDescriptor(e)}};function E(){let e=u.pointer(`RobotaStableFileHandle`,u.opaque()),t=u.struct(`RobotaUnicodeString`,{Length:`uint16_t`,MaximumLength:`uint16_t`,Buffer:u.pointer(`char16_t`)});return{handleType:e,unicodeStringType:t,objectAttributesType:u.struct(`RobotaObjectAttributes`,{Length:`uint32_t`,RootDirectory:e,ObjectName:u.pointer(t),Attributes:`uint32_t`,SecurityDescriptor:`void *`,SecurityQualityOfService:`void *`}),ioStatusBlockType:u.struct(`RobotaIoStatusBlock`,{Status:`intptr_t`,Information:`uintptr_t`}),attributeTagInfoType:u.struct(`RobotaFileAttributeTagInfo`,{FileAttributes:`uint32_t`,ReparseTag:`uint32_t`}),fileIdInfoType:u.struct(`RobotaFileIdInfo`,{VolumeSerialNumber:`uint64_t`,FileId:u.array(`uint8_t`,16,`Typed`)}),standardInfoType:u.struct(`RobotaFileStandardInfo`,{AllocationSize:`int64_t`,EndOfFile:`int64_t`,NumberOfLinks:`uint32_t`,DeletePending:`uint8_t`,Directory:`uint8_t`})}}function D(e,t){return{createFileW:e.func(`CreateFileW`,t.handleType,[u.pointer(`char16_t`),`uint32_t`,`uint32_t`,`void *`,`uint32_t`,`uint32_t`,t.handleType]),closeHandle:e.func(`CloseHandle`,`bool`,[t.handleType]),...O(e,t),getFileSizeEx:e.func(`GetFileSizeEx`,`bool`,[t.handleType,u.out(u.pointer(`int64_t`))]),readFile:e.func(`ReadFile`,`bool`,[t.handleType,u.out(u.pointer(`uint8_t`)),`uint32_t`,u.out(u.pointer(`uint32_t`)),`void *`]),getLastError:e.func(`GetLastError`,`uint32_t`,[])}}function O(e,t){let n=n=>e.func(`GetFileInformationByHandleEx`,`bool`,[t.handleType,`int32_t`,u.out(u.pointer(n)),`uint32_t`]);return{getAttributeTagInfo:n(t.attributeTagInfoType),getFileIdInfo:n(t.fileIdInfoType),getStandardInfo:n(t.standardInfoType)}}function k(e,t){return{ntCreateFile:e.func(`NtCreateFile`,`int32_t`,[u.out(u.pointer(t.handleType)),`uint32_t`,u.pointer(t.objectAttributesType),u.out(u.pointer(t.ioStatusBlockType)),`void *`,`uint32_t`,`uint32_t`,`uint32_t`,`uint32_t`,`void *`,`uint32_t`]),ntClose:e.func(`NtClose`,`int32_t`,[t.handleType])}}let A;const j=[];function M(){if(A!==void 0)return A;let e=E(),t=u.load(`kernel32.dll`);j.push(t);let n=u.load(`ntdll.dll`);return j.push(n),A={...e,...D(t,e),...k(n,e)},A}const N=BigInt(`0`);function P(e){if(e instanceof Uint8Array||Array.isArray(e))return Buffer.from(e).toString(`hex`);throw p(`UNSUPPORTED_BACKEND`,`decode-file-identity`)}function F(e){return`WIN32_${e.getLastError()}`}function I(e,t,n){let r={};if(!e.getFileIdInfo(t,18,r,u.sizeof(e.fileIdInfoType)))throw p(`UNSUPPORTED_BACKEND`,n,{hostCode:F(e)});return{volume:BigInt(r.VolumeSerialNumber??N),fileId:P(r.FileId)}}function L(e,t,n,r){let i=z(e,t);if(i.size>BigInt(n))throw p(`OVER_BUDGET`,`read-file`);let a=Number(i.size),o;try{o=Buffer.alloc(a)}catch{throw p(`HOST_IO`,`allocate-read-buffer`)}let s=R(e,t,o,r),c=[0];if(!e.readFile(t,Buffer.alloc(1),1,c,null))throw p(`HOST_IO`,`probe-file`,{hostCode:F(e)});let l=z(e,t);if(c[0]!==0&&a>=n)throw p(`OVER_BUDGET`,`read-file`);if(!B(i,l,s,a,c[0]))throw p(`FILE_CHANGED`,`read-file`);return o}function R(e,t,n,r){let i=0;for(;i<n.length;){let a=Math.min(1048576,n.length-i),o=[0];if(!e.readFile(t,n.subarray(i,i+a),a,o,null))throw p(`HOST_IO`,`read-file`,{hostCode:F(e)});if(o[0]===0)break;i+=o[0],r.afterReadChunk?.(i)}return i}function z(e,t){let n=I(e,t,`inspect-file`),r=[N];if(!e.getFileSizeEx(t,r))throw p(`HOST_IO`,`inspect-file`,{hostCode:F(e)});return{...n,size:BigInt(r[0])}}function B(e,t,n,r,i){return n===r&&i===0&&e.volume===t.volume&&e.fileId===t.fileId&&e.size===t.size}const V=new Set([3221225487,3221225524,3221225530]),H=new Set([2147483693,3221225658,3221225731,3221226105,3221226763]);function U(e){let t=e>>>0;return{kind:V.has(t)?`missing`:H.has(t)?`unsafe`:`host-io`,hostCode:`NTSTATUS_0x${t.toString(16)}`}}const W=BigInt(`-1`);function G(e){return e===null||BigInt.asIntN(64,e)===W}var K=class{api;constructor(){try{this.api=M()}catch{throw p(`UNSUPPORTED_BACKEND`,`load-windows-backend`)}}openRoot(e){let t=this.api.createFileW(e,2147483648,7,null,3,35651584,null);if(G(t))throw p(`HOST_IO`,`open-root`,{hostCode:this.lastErrorCode()});try{return this.assertSafeHandle(t,!0,`inspect-root`),t}catch(e){throw this.close(t),e}}openRelative(e,t,n,r){let{handle:i,status:a}=this.issueRelativeOpen(e,t,n);if(a>=0){if(G(i))throw p(`HOST_IO`,`open-relative`);try{return this.assertSafeHandle(i,!n,n?`inspect-file`:`inspect-directory`,r),i}catch(e){throw this.close(i),e}}G(i)||this.close(i);let o=U(a);if(o.kind===`missing`)return;let s={segmentIndex:r,hostCode:o.hostCode};throw o.kind===`unsafe`?p(`UNSAFE_ENTRY`,n?`open-file`:`open-directory`,s):p(`HOST_IO`,n?`open-file`:`open-directory`,s)}readFile(e,t,n){return L(this.api,e,t,n)}queryIdentity(e,t){return I(this.api,e,t)}close(e){try{if(this.api.ntClose(e)>=0)return}catch{}try{this.api.closeHandle(e)}catch{}}issueRelativeOpen(e,t,n){let r=u.alloc(this.api.unicodeStringType,1);try{u.encode(r,this.api.unicodeStringType,{Length:Buffer.byteLength(t,`utf16le`),MaximumLength:Buffer.byteLength(`${t}\0`,`utf16le`),Buffer:t});let i={Length:u.sizeof(this.api.objectAttributesType),RootDirectory:e,ObjectName:r,Attributes:4160,SecurityDescriptor:null,SecurityQualityOfService:null},a=[null],o={Status:0,Information:0},s=n?5:7,c=this.api.ntCreateFile(a,1048705,i,o,null,0,s,1,(n?64:1)|2097184,null,0);return{handle:a[0],status:c}}finally{u.free(r)}}assertSafeHandle(e,t,n,r){let i={},a={};if(!this.api.getAttributeTagInfo(e,9,i,u.sizeof(this.api.attributeTagInfoType))||!this.api.getStandardInfo(e,1,a,u.sizeof(this.api.standardInfoType)))throw p(`HOST_IO`,n,{hostCode:this.lastErrorCode()});if((i.FileAttributes??0)&1024||!!a.Directory!==t)throw p(`UNSAFE_ENTRY`,n,r===void 0?{}:{segmentIndex:r})}lastErrorCode(){return F(this.api)}},q=class{native=new K;rootIdentity;rootHandle;constructor(e){try{let t=this.native.openRoot(e);this.rootHandle=t,this.rootIdentity=this.native.queryIdentity(t,`inspect-root`)}catch(e){throw this.rootHandle!==void 0&&(this.native.close(this.rootHandle),this.rootHandle=void 0),e instanceof f?e:p(`UNSUPPORTED_BACKEND`,`load-windows-backend`)}}readBytes(e,t,n){let r=this.rootHandle;if(r===void 0)throw p(`AUTHORITY_CLOSED`,`read`);this.assertRootIdentity(r);let i=r,a=!1,o;try{for(let t=0;t<e.length;t+=1){let r=t===e.length-1,s=this.native.openRelative(i,e[t],r,t);if(s===void 0)return;if(r){o=s;break}a&&this.native.close(i),i=s,a=!0,n.afterDirectoryOpened?.(t)}if(o===void 0)throw p(`HOST_IO`,`open-file`);let s=this.native.readFile(o,t,n);return this.assertRootIdentity(r),s}catch(e){throw e instanceof f?e:p(`HOST_IO`,`read`)}finally{o!==void 0&&this.native.close(o),a&&this.native.close(i)}}close(){let e=this.rootHandle;e!==void 0&&(this.rootHandle=void 0,this.native.close(e))}assertRootIdentity(e){let t=this.native.queryIdentity(e,`inspect-root`);if(t.volume!==this.rootIdentity.volume||t.fileId!==this.rootIdentity.fileId)throw p(`ROOT_CHANGED`,`inspect-root`)}};const J=new Set([`darwin-arm64`,`darwin-x64`,`linux-arm64`,`linux-x64`,`win32-x64`]),Y=new FinalizationRegistry(e=>{e.close()});function X(e){if(typeof e!=`string`||e.trim().length===0||e.includes(`\0`))throw p(`INVALID_PATH`,`validate-root`);return(0,c.resolve)(e)}function Z(e,t){if(!Array.isArray(e)||e.length===0)throw p(`INVALID_PATH`,`validate-segments`);for(let t=0;t<e.length;t+=1){let n=e[t];if(typeof n!=`string`||n.length===0||n===`.`||n===`..`||n.includes(`\0`)||n.includes(`/`)||n.includes(`\\`))throw p(`INVALID_PATH`,`validate-segments`,{segmentIndex:t})}if(!Number.isSafeInteger(t)||t<0)throw p(`INVALID_PATH`,`validate-budget`)}function Q(e){let t=`${process.platform}-${process.arch}`;if(!J.has(t))throw p(`UNSUPPORTED_BACKEND`,`select-backend`,{hostCode:t});return process.platform===`win32`?new q(e):new T(e)}var $=class{hooks;backend;finalizerToken={};constructor(e,t){this.hooks=t;let n=Q(X(e));this.backend=n,Y.register(this,n,this.finalizerToken)}readBytes(e,t){let n=this.backend;if(n===void 0)throw p(`AUTHORITY_CLOSED`,`read`);Z(e,t);try{return n.readBytes(e,t,this.hooks)}catch(e){throw e instanceof f?e:p(`HOST_IO`,`read`)}}close(){let e=this.backend;if(e!==void 0){this.backend=void 0,Y.unregister(this.finalizerToken);try{e.close()}catch(e){throw e instanceof f?e:p(`HOST_IO`,`close`)}}}[Symbol.dispose](){this.close()}};function ee(e){return new $(e,{})}exports.StableFileAuthorityError=f,exports.createStableRootedFileReader=ee;
@@ -1,2 +1,2 @@
1
- import{resolve as e}from"node:path";import{closeSync as t,constants as n,fstatSync as r,readSync as i}from"node:fs";import*as a from"koffi";const o={INVALID_PATH:`The requested root-relative file operation is invalid.`,UNSAFE_ENTRY:`The requested entry cannot be opened without following an unsafe object.`,UNSUPPORTED_BACKEND:`This host cannot provide stable root-relative file reads.`,AUTHORITY_CLOSED:`The stable rooted file authority is closed.`,ROOT_CHANGED:`The retained root identity changed during the operation.`,FILE_CHANGED:`The file identity or contents changed during the bounded read.`,OVER_BUDGET:`The file exceeds the supplied byte budget.`,HOST_IO:`The host could not complete the stable root-relative file operation.`};var s=class extends Error{code;context;constructor(e,t){super(o[e]),this.name=`StableFileAuthorityError`,this.code=e,this.context=Object.freeze({...t})}};function c(e,t,n={}){return new s(e,{operation:t,...n})}function l(e){return{device:e.dev,inode:e.ino,mode:e.mode}}function u(e){return{...l(e),size:e.size,modified:e.mtimeNs,changed:e.ctimeNs}}function d(e,t){return e.device===t.device&&e.inode===t.inode&&e.mode===t.mode}function f(e,t){return d(e,t)&&e.size===t.size&&e.modified===t.modified&&e.changed===t.changed}function p(e){for(let[t,n]of Object.entries(a.os.errno))if(n===e)return t;return`ERRNO_${e}`}function m(e){let t=n.O_CLOEXEC??(process.platform===`darwin`?16777216:524288);return n.O_RDONLY|n.O_NOFOLLOW|n.O_NONBLOCK|t|(e?n.O_DIRECTORY:0)}function h(e,t){try{return r(e,{bigint:!0})}catch(e){let n=g(typeof e==`object`?e:null);throw c(`HOST_IO`,t,n===void 0?{}:{hostCode:n})}}function g(e){if(!(e===null||!(`code`in e)))return typeof e.code==`string`?e.code:void 0}var _=class{openAt;rootIdentity;rootDescriptor;constructor(e){try{let t=a.load(process.platform===`darwin`?`/usr/lib/libSystem.B.dylib`:`libc.so.6`),n=t.func(`int open(const char *path, int flags)`);this.openAt=t.func(`int openat(int fd, const char *path, int flags)`);let r=n(e,m(!0));r<0&&this.throwOpenFailure(a.errno(),`open-root`),this.rootDescriptor=r;let i=h(r,`inspect-root`);if(!i.isDirectory())throw c(`UNSAFE_ENTRY`,`open-root`);this.rootIdentity=l(i)}catch(e){throw this.rootDescriptor!==void 0&&(this.closeDescriptor(this.rootDescriptor),this.rootDescriptor=void 0),e instanceof s?e:c(`UNSUPPORTED_BACKEND`,`load-posix-backend`)}}readBytes(e,t,n){let r=this.rootDescriptor;if(r===void 0)throw c(`AUTHORITY_CLOSED`,`read`);this.assertRootIdentity(r);let i=r,o=!1,l;try{for(let t=0;t<e.length;t+=1){let r=t===e.length-1,s=this.openAt(i,e[t],m(!r));if(s<0){let e=a.errno();if(e===a.os.errno.ENOENT)return;this.throwOpenFailure(e,r?`open-file`:`open-directory`,t)}if(r){l=s;break}if(!h(s,`inspect-directory`).isDirectory())throw this.closeDescriptor(s),c(`UNSAFE_ENTRY`,`inspect-directory`,{segmentIndex:t});o&&this.closeDescriptor(i),i=s,o=!0,n.afterDirectoryOpened?.(t)}if(l===void 0)throw c(`HOST_IO`,`open-file`);let s=this.readFile(l,t,n);return this.assertRootIdentity(r),s}catch(e){if(e instanceof s)throw e;let t=g(typeof e==`object`?e:null);throw c(`HOST_IO`,`read`,t===void 0?{}:{hostCode:t})}finally{this.closeOptionalDescriptor(l),this.closeOptionalDescriptor(o?i:void 0)}}close(){let e=this.rootDescriptor;e!==void 0&&(this.rootDescriptor=void 0,this.closeDescriptor(e))}readFile(e,t,n){let r=h(e,`inspect-file`);if(!r.isFile())throw c(`UNSAFE_ENTRY`,`inspect-file`);if(r.size>BigInt(t))throw c(`OVER_BUDGET`,`read-file`);let a=Number(r.size),o;try{o=Buffer.alloc(a)}catch{throw c(`HOST_IO`,`allocate-read-buffer`)}let s=0;for(;s<a;){let t=i(e,o,s,Math.min(1048576,a-s),s);if(t===0)break;s+=t,n.afterReadChunk?.(s)}let l=i(e,Buffer.alloc(1),0,1,a)!==0,d=h(e,`reinspect-file`);if(l&&a>=t)throw c(`OVER_BUDGET`,`read-file`);if(s!==a||l||!f(u(r),u(d)))throw c(`FILE_CHANGED`,`read-file`);return o}assertRootIdentity(e){let t=h(e,`inspect-root`);if(!t.isDirectory()||!d(this.rootIdentity,l(t)))throw c(`ROOT_CHANGED`,`inspect-root`)}throwOpenFailure(e,t,n){let r=p(e),i=n===void 0?{hostCode:r}:{hostCode:r,segmentIndex:n};throw e===a.os.errno.ELOOP||e===a.os.errno.ENOTDIR||e===a.os.errno.EISDIR?c(`UNSAFE_ENTRY`,t,i):c(`HOST_IO`,t,i)}closeDescriptor(e){try{t(e)}catch{}}closeOptionalDescriptor(e){e!==void 0&&this.closeDescriptor(e)}};function v(){let e=a.pointer(`RobotaStableFileHandle`,a.opaque()),t=a.struct(`RobotaUnicodeString`,{Length:`uint16_t`,MaximumLength:`uint16_t`,Buffer:a.pointer(`char16_t`)});return{handleType:e,unicodeStringType:t,objectAttributesType:a.struct(`RobotaObjectAttributes`,{Length:`uint32_t`,RootDirectory:e,ObjectName:a.pointer(t),Attributes:`uint32_t`,SecurityDescriptor:`void *`,SecurityQualityOfService:`void *`}),ioStatusBlockType:a.struct(`RobotaIoStatusBlock`,{Status:`intptr_t`,Information:`uintptr_t`}),attributeTagInfoType:a.struct(`RobotaFileAttributeTagInfo`,{FileAttributes:`uint32_t`,ReparseTag:`uint32_t`}),fileIdInfoType:a.struct(`RobotaFileIdInfo`,{VolumeSerialNumber:`uint64_t`,FileId:a.array(`uint8_t`,16,`Typed`)}),standardInfoType:a.struct(`RobotaFileStandardInfo`,{AllocationSize:`int64_t`,EndOfFile:`int64_t`,NumberOfLinks:`uint32_t`,DeletePending:`uint8_t`,Directory:`uint8_t`})}}function y(e,t){return{createFileW:e.func(`CreateFileW`,t.handleType,[a.pointer(`char16_t`),`uint32_t`,`uint32_t`,`void *`,`uint32_t`,`uint32_t`,t.handleType]),closeHandle:e.func(`CloseHandle`,`bool`,[t.handleType]),...b(e,t),getFileSizeEx:e.func(`GetFileSizeEx`,`bool`,[t.handleType,a.out(a.pointer(`int64_t`))]),readFile:e.func(`ReadFile`,`bool`,[t.handleType,a.out(a.pointer(`uint8_t`)),`uint32_t`,a.out(a.pointer(`uint32_t`)),`void *`]),getLastError:e.func(`GetLastError`,`uint32_t`,[])}}function b(e,t){let n=n=>e.func(`GetFileInformationByHandleEx`,`bool`,[t.handleType,`int32_t`,a.out(a.pointer(n)),`uint32_t`]);return{getAttributeTagInfo:n(t.attributeTagInfoType),getFileIdInfo:n(t.fileIdInfoType),getStandardInfo:n(t.standardInfoType)}}function x(e,t){return{ntCreateFile:e.func(`NtCreateFile`,`int32_t`,[a.out(a.pointer(t.handleType)),`uint32_t`,a.pointer(t.objectAttributesType),a.out(a.pointer(t.ioStatusBlockType)),`void *`,`uint32_t`,`uint32_t`,`uint32_t`,`uint32_t`,`void *`,`uint32_t`]),ntClose:e.func(`NtClose`,`int32_t`,[t.handleType])}}let S;function C(){if(S!==void 0)return S;let e=v();return S={...e,...y(a.load(`kernel32.dll`),e),...x(a.load(`ntdll.dll`),e)},S}const w=BigInt(`0`);function T(e){if(e instanceof Uint8Array||Array.isArray(e))return Buffer.from(e).toString(`hex`);throw c(`UNSUPPORTED_BACKEND`,`decode-file-identity`)}function E(e){return`WIN32_${e.getLastError()}`}function D(e,t,n){let r={};if(!e.getFileIdInfo(t,18,r,a.sizeof(e.fileIdInfoType)))throw c(`UNSUPPORTED_BACKEND`,n,{hostCode:E(e)});return{volume:BigInt(r.VolumeSerialNumber??w),fileId:T(r.FileId)}}function O(e,t,n,r){let i=A(e,t);if(i.size>BigInt(n))throw c(`OVER_BUDGET`,`read-file`);let a=Number(i.size),o;try{o=Buffer.alloc(a)}catch{throw c(`HOST_IO`,`allocate-read-buffer`)}let s=k(e,t,o,r),l=[0];if(!e.readFile(t,Buffer.alloc(1),1,l,null))throw c(`HOST_IO`,`probe-file`,{hostCode:E(e)});let u=A(e,t);if(l[0]!==0&&a>=n)throw c(`OVER_BUDGET`,`read-file`);if(!j(i,u,s,a,l[0]))throw c(`FILE_CHANGED`,`read-file`);return o}function k(e,t,n,r){let i=0;for(;i<n.length;){let a=Math.min(1048576,n.length-i),o=[0];if(!e.readFile(t,n.subarray(i,i+a),a,o,null))throw c(`HOST_IO`,`read-file`,{hostCode:E(e)});if(o[0]===0)break;i+=o[0],r.afterReadChunk?.(i)}return i}function A(e,t){let n=D(e,t,`inspect-file`),r=[w];if(!e.getFileSizeEx(t,r))throw c(`HOST_IO`,`inspect-file`,{hostCode:E(e)});return{...n,size:BigInt(r[0])}}function j(e,t,n,r,i){return n===r&&i===0&&e.volume===t.volume&&e.fileId===t.fileId&&e.size===t.size}const M=new Set([3221225487,3221225524,3221225530]),N=new Set([2147483693,3221225658,3221225731,3221226105,3221226763]);function P(e){let t=e>>>0;return{kind:M.has(t)?`missing`:N.has(t)?`unsafe`:`host-io`,hostCode:`NTSTATUS_0x${t.toString(16)}`}}const F=BigInt(`-1`);function I(e){return e===null||BigInt.asIntN(64,e)===F}var L=class{api;constructor(){try{this.api=C()}catch{throw c(`UNSUPPORTED_BACKEND`,`load-windows-backend`)}}openRoot(e){let t=this.api.createFileW(e,2147483648,7,null,3,35651584,null);if(I(t))throw c(`HOST_IO`,`open-root`,{hostCode:this.lastErrorCode()});try{return this.assertSafeHandle(t,!0,`inspect-root`),t}catch(e){throw this.close(t),e}}openRelative(e,t,n,r){let{handle:i,status:a}=this.issueRelativeOpen(e,t,n);if(a>=0){if(I(i))throw c(`HOST_IO`,`open-relative`);try{return this.assertSafeHandle(i,!n,n?`inspect-file`:`inspect-directory`,r),i}catch(e){throw this.close(i),e}}I(i)||this.close(i);let o=P(a);if(o.kind===`missing`)return;let s={segmentIndex:r,hostCode:o.hostCode};throw o.kind===`unsafe`?c(`UNSAFE_ENTRY`,n?`open-file`:`open-directory`,s):c(`HOST_IO`,n?`open-file`:`open-directory`,s)}readFile(e,t,n){return O(this.api,e,t,n)}queryIdentity(e,t){return D(this.api,e,t)}close(e){try{if(this.api.ntClose(e)>=0)return}catch{}try{this.api.closeHandle(e)}catch{}}issueRelativeOpen(e,t,n){let r=a.alloc(this.api.unicodeStringType,1);try{a.encode(r,this.api.unicodeStringType,{Length:Buffer.byteLength(t,`utf16le`),MaximumLength:Buffer.byteLength(`${t}\0`,`utf16le`),Buffer:t});let i={Length:a.sizeof(this.api.objectAttributesType),RootDirectory:e,ObjectName:r,Attributes:4160,SecurityDescriptor:null,SecurityQualityOfService:null},o=[null],s={Status:0,Information:0},c=n?5:7,l=this.api.ntCreateFile(o,1048705,i,s,null,0,c,1,(n?64:1)|2097184,null,0);return{handle:o[0],status:l}}finally{a.free(r)}}assertSafeHandle(e,t,n,r){let i={},o={};if(!this.api.getAttributeTagInfo(e,9,i,a.sizeof(this.api.attributeTagInfoType))||!this.api.getStandardInfo(e,1,o,a.sizeof(this.api.standardInfoType)))throw c(`HOST_IO`,n,{hostCode:this.lastErrorCode()});if((i.FileAttributes??0)&1024||!!o.Directory!==t)throw c(`UNSAFE_ENTRY`,n,r===void 0?{}:{segmentIndex:r})}lastErrorCode(){return E(this.api)}},R=class{native=new L;rootIdentity;rootHandle;constructor(e){try{let t=this.native.openRoot(e);this.rootHandle=t,this.rootIdentity=this.native.queryIdentity(t,`inspect-root`)}catch(e){throw this.rootHandle!==void 0&&(this.native.close(this.rootHandle),this.rootHandle=void 0),e instanceof s?e:c(`UNSUPPORTED_BACKEND`,`load-windows-backend`)}}readBytes(e,t,n){let r=this.rootHandle;if(r===void 0)throw c(`AUTHORITY_CLOSED`,`read`);this.assertRootIdentity(r);let i=r,a=!1,o;try{for(let t=0;t<e.length;t+=1){let r=t===e.length-1,s=this.native.openRelative(i,e[t],r,t);if(s===void 0)return;if(r){o=s;break}a&&this.native.close(i),i=s,a=!0,n.afterDirectoryOpened?.(t)}if(o===void 0)throw c(`HOST_IO`,`open-file`);let s=this.native.readFile(o,t,n);return this.assertRootIdentity(r),s}catch(e){throw e instanceof s?e:c(`HOST_IO`,`read`)}finally{o!==void 0&&this.native.close(o),a&&this.native.close(i)}}close(){let e=this.rootHandle;e!==void 0&&(this.rootHandle=void 0,this.native.close(e))}assertRootIdentity(e){let t=this.native.queryIdentity(e,`inspect-root`);if(t.volume!==this.rootIdentity.volume||t.fileId!==this.rootIdentity.fileId)throw c(`ROOT_CHANGED`,`inspect-root`)}};const z=new Set([`darwin-arm64`,`darwin-x64`,`linux-arm64`,`linux-x64`,`win32-x64`]),B=new FinalizationRegistry(e=>{e.close()});function V(t){if(typeof t!=`string`||t.trim().length===0||t.includes(`\0`))throw c(`INVALID_PATH`,`validate-root`);return e(t)}function H(e,t){if(!Array.isArray(e)||e.length===0)throw c(`INVALID_PATH`,`validate-segments`);for(let t=0;t<e.length;t+=1){let n=e[t];if(typeof n!=`string`||n.length===0||n===`.`||n===`..`||n.includes(`\0`)||n.includes(`/`)||n.includes(`\\`))throw c(`INVALID_PATH`,`validate-segments`,{segmentIndex:t})}if(!Number.isSafeInteger(t)||t<0)throw c(`INVALID_PATH`,`validate-budget`)}function U(e){let t=`${process.platform}-${process.arch}`;if(!z.has(t))throw c(`UNSUPPORTED_BACKEND`,`select-backend`,{hostCode:t});return process.platform===`win32`?new R(e):new _(e)}var W=class{hooks;backend;finalizerToken={};constructor(e,t){this.hooks=t;let n=U(V(e));this.backend=n,B.register(this,n,this.finalizerToken)}readBytes(e,t){let n=this.backend;if(n===void 0)throw c(`AUTHORITY_CLOSED`,`read`);H(e,t);try{return n.readBytes(e,t,this.hooks)}catch(e){throw e instanceof s?e:c(`HOST_IO`,`read`)}}close(){let e=this.backend;if(e!==void 0){this.backend=void 0,B.unregister(this.finalizerToken);try{e.close()}catch(e){throw e instanceof s?e:c(`HOST_IO`,`close`)}}}[Symbol.dispose](){this.close()}};function G(e){return new W(e,{})}export{s as StableFileAuthorityError,G as createStableRootedFileReader};
1
+ import{resolve as e}from"node:path";import{closeSync as t,constants as n,fstatSync as r,readSync as i}from"node:fs";import*as a from"koffi";const o={INVALID_PATH:`The requested root-relative file operation is invalid.`,UNSAFE_ENTRY:`The requested entry cannot be opened without following an unsafe object.`,UNSUPPORTED_BACKEND:`This host cannot provide stable root-relative file reads.`,AUTHORITY_CLOSED:`The stable rooted file authority is closed.`,ROOT_CHANGED:`The retained root identity changed during the operation.`,FILE_CHANGED:`The file identity or contents changed during the bounded read.`,OVER_BUDGET:`The file exceeds the supplied byte budget.`,HOST_IO:`The host could not complete the stable root-relative file operation.`};var s=class extends Error{code;context;constructor(e,t){super(o[e]),this.name=`StableFileAuthorityError`,this.code=e,this.context=Object.freeze({...t})}};function c(e,t,n={}){return new s(e,{operation:t,...n})}function l(e){return{device:e.dev,inode:e.ino,mode:e.mode}}function u(e){return{...l(e),size:e.size,modified:e.mtimeNs,changed:e.ctimeNs}}function d(e,t){return e.device===t.device&&e.inode===t.inode&&e.mode===t.mode}function f(e,t){return d(e,t)&&e.size===t.size&&e.modified===t.modified&&e.changed===t.changed}function p(e){for(let[t,n]of Object.entries(a.os.errno))if(n===e)return t;return`ERRNO_${e}`}function m(e){let t=n.O_CLOEXEC??(process.platform===`darwin`?16777216:524288);return n.O_RDONLY|n.O_NOFOLLOW|n.O_NONBLOCK|t|(e?n.O_DIRECTORY:0)}function h(e,t){try{return r(e,{bigint:!0})}catch(e){let n=g(typeof e==`object`?e:null);throw c(`HOST_IO`,t,n===void 0?{}:{hostCode:n})}}function g(e){if(!(e===null||!(`code`in e)))return typeof e.code==`string`?e.code:void 0}let _;const v=[];function y(){if(!_){let e=a.load(process.platform===`darwin`?`/usr/lib/libSystem.B.dylib`:`libc.so.6`);v.push(e),_={library:e,open:e.func(`int open(const char *path, int flags)`),openAt:e.func(`int openat(int fd, const char *path, int flags)`)}}return _}var b=class{openAt;rootIdentity;rootDescriptor;constructor(e){try{let{open:t,openAt:n}=y();this.openAt=n;let r=t(e,m(!0));r<0&&this.throwOpenFailure(a.errno(),`open-root`),this.rootDescriptor=r;let i=h(r,`inspect-root`);if(!i.isDirectory())throw c(`UNSAFE_ENTRY`,`open-root`);this.rootIdentity=l(i)}catch(e){throw this.rootDescriptor!==void 0&&(this.closeDescriptor(this.rootDescriptor),this.rootDescriptor=void 0),e instanceof s?e:c(`UNSUPPORTED_BACKEND`,`load-posix-backend`)}}readBytes(e,t,n){let r=this.rootDescriptor;if(r===void 0)throw c(`AUTHORITY_CLOSED`,`read`);this.assertRootIdentity(r);let i=r,o=!1,l;try{for(let t=0;t<e.length;t+=1){let r=t===e.length-1,s=this.openAt(i,e[t],m(!r));if(s<0){let e=a.errno();if(e===a.os.errno.ENOENT)return;this.throwOpenFailure(e,r?`open-file`:`open-directory`,t)}if(r){l=s;break}if(!h(s,`inspect-directory`).isDirectory())throw this.closeDescriptor(s),c(`UNSAFE_ENTRY`,`inspect-directory`,{segmentIndex:t});o&&this.closeDescriptor(i),i=s,o=!0,n.afterDirectoryOpened?.(t)}if(l===void 0)throw c(`HOST_IO`,`open-file`);let s=this.readFile(l,t,n);return this.assertRootIdentity(r),s}catch(e){if(e instanceof s)throw e;let t=g(typeof e==`object`?e:null);throw c(`HOST_IO`,`read`,t===void 0?{}:{hostCode:t})}finally{this.closeOptionalDescriptor(l),this.closeOptionalDescriptor(o?i:void 0)}}close(){let e=this.rootDescriptor;e!==void 0&&(this.rootDescriptor=void 0,this.closeDescriptor(e))}readFile(e,t,n){let r=h(e,`inspect-file`);if(!r.isFile())throw c(`UNSAFE_ENTRY`,`inspect-file`);if(r.size>BigInt(t))throw c(`OVER_BUDGET`,`read-file`);let a=Number(r.size),o;try{o=Buffer.alloc(a)}catch{throw c(`HOST_IO`,`allocate-read-buffer`)}let s=0;for(;s<a;){let t=i(e,o,s,Math.min(1048576,a-s),s);if(t===0)break;s+=t,n.afterReadChunk?.(s)}let l=i(e,Buffer.alloc(1),0,1,a)!==0,d=h(e,`reinspect-file`);if(l&&a>=t)throw c(`OVER_BUDGET`,`read-file`);if(s!==a||l||!f(u(r),u(d)))throw c(`FILE_CHANGED`,`read-file`);return o}assertRootIdentity(e){let t=h(e,`inspect-root`);if(!t.isDirectory()||!d(this.rootIdentity,l(t)))throw c(`ROOT_CHANGED`,`inspect-root`)}throwOpenFailure(e,t,n){let r=p(e),i=n===void 0?{hostCode:r}:{hostCode:r,segmentIndex:n};throw e===a.os.errno.ELOOP||e===a.os.errno.ENOTDIR||e===a.os.errno.EISDIR?c(`UNSAFE_ENTRY`,t,i):c(`HOST_IO`,t,i)}closeDescriptor(e){try{t(e)}catch{}}closeOptionalDescriptor(e){e!==void 0&&this.closeDescriptor(e)}};function x(){let e=a.pointer(`RobotaStableFileHandle`,a.opaque()),t=a.struct(`RobotaUnicodeString`,{Length:`uint16_t`,MaximumLength:`uint16_t`,Buffer:a.pointer(`char16_t`)});return{handleType:e,unicodeStringType:t,objectAttributesType:a.struct(`RobotaObjectAttributes`,{Length:`uint32_t`,RootDirectory:e,ObjectName:a.pointer(t),Attributes:`uint32_t`,SecurityDescriptor:`void *`,SecurityQualityOfService:`void *`}),ioStatusBlockType:a.struct(`RobotaIoStatusBlock`,{Status:`intptr_t`,Information:`uintptr_t`}),attributeTagInfoType:a.struct(`RobotaFileAttributeTagInfo`,{FileAttributes:`uint32_t`,ReparseTag:`uint32_t`}),fileIdInfoType:a.struct(`RobotaFileIdInfo`,{VolumeSerialNumber:`uint64_t`,FileId:a.array(`uint8_t`,16,`Typed`)}),standardInfoType:a.struct(`RobotaFileStandardInfo`,{AllocationSize:`int64_t`,EndOfFile:`int64_t`,NumberOfLinks:`uint32_t`,DeletePending:`uint8_t`,Directory:`uint8_t`})}}function S(e,t){return{createFileW:e.func(`CreateFileW`,t.handleType,[a.pointer(`char16_t`),`uint32_t`,`uint32_t`,`void *`,`uint32_t`,`uint32_t`,t.handleType]),closeHandle:e.func(`CloseHandle`,`bool`,[t.handleType]),...C(e,t),getFileSizeEx:e.func(`GetFileSizeEx`,`bool`,[t.handleType,a.out(a.pointer(`int64_t`))]),readFile:e.func(`ReadFile`,`bool`,[t.handleType,a.out(a.pointer(`uint8_t`)),`uint32_t`,a.out(a.pointer(`uint32_t`)),`void *`]),getLastError:e.func(`GetLastError`,`uint32_t`,[])}}function C(e,t){let n=n=>e.func(`GetFileInformationByHandleEx`,`bool`,[t.handleType,`int32_t`,a.out(a.pointer(n)),`uint32_t`]);return{getAttributeTagInfo:n(t.attributeTagInfoType),getFileIdInfo:n(t.fileIdInfoType),getStandardInfo:n(t.standardInfoType)}}function w(e,t){return{ntCreateFile:e.func(`NtCreateFile`,`int32_t`,[a.out(a.pointer(t.handleType)),`uint32_t`,a.pointer(t.objectAttributesType),a.out(a.pointer(t.ioStatusBlockType)),`void *`,`uint32_t`,`uint32_t`,`uint32_t`,`uint32_t`,`void *`,`uint32_t`]),ntClose:e.func(`NtClose`,`int32_t`,[t.handleType])}}let T;const E=[];function D(){if(T!==void 0)return T;let e=x(),t=a.load(`kernel32.dll`);E.push(t);let n=a.load(`ntdll.dll`);return E.push(n),T={...e,...S(t,e),...w(n,e)},T}const O=BigInt(`0`);function k(e){if(e instanceof Uint8Array||Array.isArray(e))return Buffer.from(e).toString(`hex`);throw c(`UNSUPPORTED_BACKEND`,`decode-file-identity`)}function A(e){return`WIN32_${e.getLastError()}`}function j(e,t,n){let r={};if(!e.getFileIdInfo(t,18,r,a.sizeof(e.fileIdInfoType)))throw c(`UNSUPPORTED_BACKEND`,n,{hostCode:A(e)});return{volume:BigInt(r.VolumeSerialNumber??O),fileId:k(r.FileId)}}function M(e,t,n,r){let i=P(e,t);if(i.size>BigInt(n))throw c(`OVER_BUDGET`,`read-file`);let a=Number(i.size),o;try{o=Buffer.alloc(a)}catch{throw c(`HOST_IO`,`allocate-read-buffer`)}let s=N(e,t,o,r),l=[0];if(!e.readFile(t,Buffer.alloc(1),1,l,null))throw c(`HOST_IO`,`probe-file`,{hostCode:A(e)});let u=P(e,t);if(l[0]!==0&&a>=n)throw c(`OVER_BUDGET`,`read-file`);if(!F(i,u,s,a,l[0]))throw c(`FILE_CHANGED`,`read-file`);return o}function N(e,t,n,r){let i=0;for(;i<n.length;){let a=Math.min(1048576,n.length-i),o=[0];if(!e.readFile(t,n.subarray(i,i+a),a,o,null))throw c(`HOST_IO`,`read-file`,{hostCode:A(e)});if(o[0]===0)break;i+=o[0],r.afterReadChunk?.(i)}return i}function P(e,t){let n=j(e,t,`inspect-file`),r=[O];if(!e.getFileSizeEx(t,r))throw c(`HOST_IO`,`inspect-file`,{hostCode:A(e)});return{...n,size:BigInt(r[0])}}function F(e,t,n,r,i){return n===r&&i===0&&e.volume===t.volume&&e.fileId===t.fileId&&e.size===t.size}const I=new Set([3221225487,3221225524,3221225530]),L=new Set([2147483693,3221225658,3221225731,3221226105,3221226763]);function R(e){let t=e>>>0;return{kind:I.has(t)?`missing`:L.has(t)?`unsafe`:`host-io`,hostCode:`NTSTATUS_0x${t.toString(16)}`}}const z=BigInt(`-1`);function B(e){return e===null||BigInt.asIntN(64,e)===z}var V=class{api;constructor(){try{this.api=D()}catch{throw c(`UNSUPPORTED_BACKEND`,`load-windows-backend`)}}openRoot(e){let t=this.api.createFileW(e,2147483648,7,null,3,35651584,null);if(B(t))throw c(`HOST_IO`,`open-root`,{hostCode:this.lastErrorCode()});try{return this.assertSafeHandle(t,!0,`inspect-root`),t}catch(e){throw this.close(t),e}}openRelative(e,t,n,r){let{handle:i,status:a}=this.issueRelativeOpen(e,t,n);if(a>=0){if(B(i))throw c(`HOST_IO`,`open-relative`);try{return this.assertSafeHandle(i,!n,n?`inspect-file`:`inspect-directory`,r),i}catch(e){throw this.close(i),e}}B(i)||this.close(i);let o=R(a);if(o.kind===`missing`)return;let s={segmentIndex:r,hostCode:o.hostCode};throw o.kind===`unsafe`?c(`UNSAFE_ENTRY`,n?`open-file`:`open-directory`,s):c(`HOST_IO`,n?`open-file`:`open-directory`,s)}readFile(e,t,n){return M(this.api,e,t,n)}queryIdentity(e,t){return j(this.api,e,t)}close(e){try{if(this.api.ntClose(e)>=0)return}catch{}try{this.api.closeHandle(e)}catch{}}issueRelativeOpen(e,t,n){let r=a.alloc(this.api.unicodeStringType,1);try{a.encode(r,this.api.unicodeStringType,{Length:Buffer.byteLength(t,`utf16le`),MaximumLength:Buffer.byteLength(`${t}\0`,`utf16le`),Buffer:t});let i={Length:a.sizeof(this.api.objectAttributesType),RootDirectory:e,ObjectName:r,Attributes:4160,SecurityDescriptor:null,SecurityQualityOfService:null},o=[null],s={Status:0,Information:0},c=n?5:7,l=this.api.ntCreateFile(o,1048705,i,s,null,0,c,1,(n?64:1)|2097184,null,0);return{handle:o[0],status:l}}finally{a.free(r)}}assertSafeHandle(e,t,n,r){let i={},o={};if(!this.api.getAttributeTagInfo(e,9,i,a.sizeof(this.api.attributeTagInfoType))||!this.api.getStandardInfo(e,1,o,a.sizeof(this.api.standardInfoType)))throw c(`HOST_IO`,n,{hostCode:this.lastErrorCode()});if((i.FileAttributes??0)&1024||!!o.Directory!==t)throw c(`UNSAFE_ENTRY`,n,r===void 0?{}:{segmentIndex:r})}lastErrorCode(){return A(this.api)}},H=class{native=new V;rootIdentity;rootHandle;constructor(e){try{let t=this.native.openRoot(e);this.rootHandle=t,this.rootIdentity=this.native.queryIdentity(t,`inspect-root`)}catch(e){throw this.rootHandle!==void 0&&(this.native.close(this.rootHandle),this.rootHandle=void 0),e instanceof s?e:c(`UNSUPPORTED_BACKEND`,`load-windows-backend`)}}readBytes(e,t,n){let r=this.rootHandle;if(r===void 0)throw c(`AUTHORITY_CLOSED`,`read`);this.assertRootIdentity(r);let i=r,a=!1,o;try{for(let t=0;t<e.length;t+=1){let r=t===e.length-1,s=this.native.openRelative(i,e[t],r,t);if(s===void 0)return;if(r){o=s;break}a&&this.native.close(i),i=s,a=!0,n.afterDirectoryOpened?.(t)}if(o===void 0)throw c(`HOST_IO`,`open-file`);let s=this.native.readFile(o,t,n);return this.assertRootIdentity(r),s}catch(e){throw e instanceof s?e:c(`HOST_IO`,`read`)}finally{o!==void 0&&this.native.close(o),a&&this.native.close(i)}}close(){let e=this.rootHandle;e!==void 0&&(this.rootHandle=void 0,this.native.close(e))}assertRootIdentity(e){let t=this.native.queryIdentity(e,`inspect-root`);if(t.volume!==this.rootIdentity.volume||t.fileId!==this.rootIdentity.fileId)throw c(`ROOT_CHANGED`,`inspect-root`)}};const U=new Set([`darwin-arm64`,`darwin-x64`,`linux-arm64`,`linux-x64`,`win32-x64`]),W=new FinalizationRegistry(e=>{e.close()});function G(t){if(typeof t!=`string`||t.trim().length===0||t.includes(`\0`))throw c(`INVALID_PATH`,`validate-root`);return e(t)}function K(e,t){if(!Array.isArray(e)||e.length===0)throw c(`INVALID_PATH`,`validate-segments`);for(let t=0;t<e.length;t+=1){let n=e[t];if(typeof n!=`string`||n.length===0||n===`.`||n===`..`||n.includes(`\0`)||n.includes(`/`)||n.includes(`\\`))throw c(`INVALID_PATH`,`validate-segments`,{segmentIndex:t})}if(!Number.isSafeInteger(t)||t<0)throw c(`INVALID_PATH`,`validate-budget`)}function q(e){let t=`${process.platform}-${process.arch}`;if(!U.has(t))throw c(`UNSUPPORTED_BACKEND`,`select-backend`,{hostCode:t});return process.platform===`win32`?new H(e):new b(e)}var J=class{hooks;backend;finalizerToken={};constructor(e,t){this.hooks=t;let n=q(G(e));this.backend=n,W.register(this,n,this.finalizerToken)}readBytes(e,t){let n=this.backend;if(n===void 0)throw c(`AUTHORITY_CLOSED`,`read`);K(e,t);try{return n.readBytes(e,t,this.hooks)}catch(e){throw e instanceof s?e:c(`HOST_IO`,`read`)}}close(){let e=this.backend;if(e!==void 0){this.backend=void 0,W.unregister(this.finalizerToken);try{e.close()}catch(e){throw e instanceof s?e:c(`HOST_IO`,`close`)}}}[Symbol.dispose](){this.close()}};function Y(e){return new J(e,{})}export{s as StableFileAuthorityError,Y as createStableRootedFileReader};
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["CHUNK_BYTES"],"sources":["../../src/contracts.ts","../../src/posix-backend.ts","../../src/windows-native-api.ts","../../src/windows-native-read.ts","../../src/windows-native-status.ts","../../src/windows-native-authority.ts","../../src/windows-backend.ts","../../src/stable-rooted-file-reader.ts"],"sourcesContent":["export type TStableFileAuthorityErrorCode =\n | 'INVALID_PATH'\n | 'UNSAFE_ENTRY'\n | 'UNSUPPORTED_BACKEND'\n | 'AUTHORITY_CLOSED'\n | 'ROOT_CHANGED'\n | 'FILE_CHANGED'\n | 'OVER_BUDGET'\n | 'HOST_IO';\n\nexport interface IStableFileAuthorityErrorContext {\n readonly operation: string;\n readonly segmentIndex?: number;\n readonly hostCode?: string;\n}\n\nconst ERROR_MESSAGES: Readonly<Record<TStableFileAuthorityErrorCode, string>> = {\n INVALID_PATH: 'The requested root-relative file operation is invalid.',\n UNSAFE_ENTRY: 'The requested entry cannot be opened without following an unsafe object.',\n UNSUPPORTED_BACKEND: 'This host cannot provide stable root-relative file reads.',\n AUTHORITY_CLOSED: 'The stable rooted file authority is closed.',\n ROOT_CHANGED: 'The retained root identity changed during the operation.',\n FILE_CHANGED: 'The file identity or contents changed during the bounded read.',\n OVER_BUDGET: 'The file exceeds the supplied byte budget.',\n HOST_IO: 'The host could not complete the stable root-relative file operation.',\n};\n\n/** A stable, secret-free failure from a rooted file authority. */\nexport class StableFileAuthorityError extends Error {\n readonly code: TStableFileAuthorityErrorCode;\n readonly context: Readonly<IStableFileAuthorityErrorContext>;\n\n constructor(code: TStableFileAuthorityErrorCode, context: IStableFileAuthorityErrorContext) {\n super(ERROR_MESSAGES[code]);\n this.name = 'StableFileAuthorityError';\n this.code = code;\n this.context = Object.freeze({ ...context });\n }\n}\n\n/** Opaque authority for bounded regular-file reads beneath one retained directory identity. */\nexport interface IStableRootedFileReader {\n readBytes(relativeSegments: readonly string[], maxBytes: number): Uint8Array | undefined;\n close(): void;\n [Symbol.dispose](): void;\n}\n\nexport function authorityError(\n code: TStableFileAuthorityErrorCode,\n operation: string,\n details: Omit<IStableFileAuthorityErrorContext, 'operation'> = {},\n): StableFileAuthorityError {\n return new StableFileAuthorityError(code, { operation, ...details });\n}\n","import { closeSync, constants, fstatSync, readSync, type BigIntStats } from 'node:fs';\n\nimport * as koffi from 'koffi';\n\nimport { authorityError, StableFileAuthorityError } from './contracts.js';\n\nimport type { IFileAuthorityTestHooks, IStableFileHostBackend } from './host-backend.js';\n\ninterface IPosixIdentity {\n readonly device: bigint;\n readonly inode: bigint;\n readonly mode: bigint;\n}\n\ninterface IPosixFileSnapshot extends IPosixIdentity {\n readonly size: bigint;\n readonly modified: bigint;\n readonly changed: bigint;\n}\n\ntype TOpen = (path: string, flags: number) => number;\ntype TOpenAt = (directory: number, path: string, flags: number) => number;\n\nconst CHUNK_BYTES = 1_048_576;\nconst DARWIN_CLOSE_ON_EXEC = 0x01000000;\nconst LINUX_CLOSE_ON_EXEC = 0x00080000;\n\nfunction identity(metadata: BigIntStats): IPosixIdentity {\n return { device: metadata.dev, inode: metadata.ino, mode: metadata.mode };\n}\n\nfunction snapshot(metadata: BigIntStats): IPosixFileSnapshot {\n return {\n ...identity(metadata),\n size: metadata.size,\n modified: metadata.mtimeNs,\n changed: metadata.ctimeNs,\n };\n}\n\nfunction sameIdentity(left: IPosixIdentity, right: IPosixIdentity): boolean {\n return left.device === right.device && left.inode === right.inode && left.mode === right.mode;\n}\n\nfunction sameSnapshot(left: IPosixFileSnapshot, right: IPosixFileSnapshot): boolean {\n return (\n sameIdentity(left, right) &&\n left.size === right.size &&\n left.modified === right.modified &&\n left.changed === right.changed\n );\n}\n\nfunction hostCode(errno: number): string {\n for (const [name, value] of Object.entries(koffi.os.errno)) {\n if (value === errno) return name;\n }\n return `ERRNO_${errno}`;\n}\n\nfunction flags(directory: boolean): number {\n const closeOnExec =\n (constants as typeof constants & { readonly O_CLOEXEC?: number }).O_CLOEXEC ??\n (process.platform === 'darwin' ? DARWIN_CLOSE_ON_EXEC : LINUX_CLOSE_ON_EXEC);\n return (\n constants.O_RDONLY |\n constants.O_NOFOLLOW |\n constants.O_NONBLOCK |\n closeOnExec |\n (directory ? constants.O_DIRECTORY : 0)\n );\n}\n\nfunction safeFstat(descriptor: number, operation: string): BigIntStats {\n try {\n return fstatSync(descriptor, { bigint: true });\n } catch (error) {\n const code = nodeErrorCode(typeof error === 'object' ? error : null);\n throw authorityError('HOST_IO', operation, code === undefined ? {} : { hostCode: code });\n }\n}\n\nfunction nodeErrorCode(error: object | null): string | undefined {\n if (error === null || !('code' in error)) return undefined;\n return typeof error.code === 'string' ? error.code : undefined;\n}\n\n/** POSIX implementation; descriptors never leave this module. */\nexport class PosixStableFileHostBackend implements IStableFileHostBackend {\n private readonly openAt: TOpenAt;\n private readonly rootIdentity: IPosixIdentity;\n private rootDescriptor: number | undefined;\n\n constructor(rootDirectory: string) {\n try {\n const libc = koffi.load(\n process.platform === 'darwin' ? '/usr/lib/libSystem.B.dylib' : 'libc.so.6',\n );\n const open = libc.func('int open(const char *path, int flags)') as TOpen;\n this.openAt = libc.func('int openat(int fd, const char *path, int flags)') as TOpenAt;\n const descriptor = open(rootDirectory, flags(true));\n if (descriptor < 0) this.throwOpenFailure(koffi.errno(), 'open-root');\n this.rootDescriptor = descriptor;\n const metadata = safeFstat(descriptor, 'inspect-root');\n if (!metadata.isDirectory()) throw authorityError('UNSAFE_ENTRY', 'open-root');\n this.rootIdentity = identity(metadata);\n } catch (error) {\n if (this.rootDescriptor !== undefined) {\n this.closeDescriptor(this.rootDescriptor);\n this.rootDescriptor = undefined;\n }\n if (error instanceof StableFileAuthorityError) throw error;\n throw authorityError('UNSUPPORTED_BACKEND', 'load-posix-backend');\n }\n }\n\n readBytes(\n relativeSegments: readonly string[],\n maxBytes: number,\n hooks: IFileAuthorityTestHooks,\n ): Uint8Array | undefined {\n const rootDescriptor = this.rootDescriptor;\n if (rootDescriptor === undefined) throw authorityError('AUTHORITY_CLOSED', 'read');\n this.assertRootIdentity(rootDescriptor);\n\n let parentDescriptor = rootDescriptor;\n let ownsParent = false;\n let fileDescriptor: number | undefined;\n try {\n for (let index = 0; index < relativeSegments.length; index += 1) {\n const final = index === relativeSegments.length - 1;\n const descriptor = this.openAt(parentDescriptor, relativeSegments[index], flags(!final));\n if (descriptor < 0) {\n const errno = koffi.errno();\n if (errno === koffi.os.errno.ENOENT) return undefined;\n this.throwOpenFailure(errno, final ? 'open-file' : 'open-directory', index);\n }\n\n if (final) {\n fileDescriptor = descriptor;\n break;\n }\n\n const metadata = safeFstat(descriptor, 'inspect-directory');\n if (!metadata.isDirectory()) {\n this.closeDescriptor(descriptor);\n throw authorityError('UNSAFE_ENTRY', 'inspect-directory', { segmentIndex: index });\n }\n if (ownsParent) this.closeDescriptor(parentDescriptor);\n parentDescriptor = descriptor;\n ownsParent = true;\n hooks.afterDirectoryOpened?.(index);\n }\n\n if (fileDescriptor === undefined) throw authorityError('HOST_IO', 'open-file');\n const bytes = this.readFile(fileDescriptor, maxBytes, hooks);\n this.assertRootIdentity(rootDescriptor);\n return bytes;\n } catch (error) {\n if (error instanceof StableFileAuthorityError) throw error;\n const code = nodeErrorCode(typeof error === 'object' ? error : null);\n throw authorityError('HOST_IO', 'read', code === undefined ? {} : { hostCode: code });\n } finally {\n this.closeOptionalDescriptor(fileDescriptor);\n this.closeOptionalDescriptor(ownsParent ? parentDescriptor : undefined);\n }\n }\n\n close(): void {\n const descriptor = this.rootDescriptor;\n if (descriptor === undefined) return;\n this.rootDescriptor = undefined;\n this.closeDescriptor(descriptor);\n }\n\n private readFile(\n descriptor: number,\n maxBytes: number,\n hooks: IFileAuthorityTestHooks,\n ): Uint8Array {\n const beforeMetadata = safeFstat(descriptor, 'inspect-file');\n if (!beforeMetadata.isFile()) throw authorityError('UNSAFE_ENTRY', 'inspect-file');\n if (beforeMetadata.size > BigInt(maxBytes)) throw authorityError('OVER_BUDGET', 'read-file');\n\n const expectedBytes = Number(beforeMetadata.size);\n let bytes: Buffer;\n try {\n bytes = Buffer.alloc(expectedBytes);\n } catch {\n throw authorityError('HOST_IO', 'allocate-read-buffer');\n }\n\n let offset = 0;\n while (offset < expectedBytes) {\n const count = readSync(\n descriptor,\n bytes,\n offset,\n Math.min(CHUNK_BYTES, expectedBytes - offset),\n offset,\n );\n if (count === 0) break;\n offset += count;\n hooks.afterReadChunk?.(offset);\n }\n\n const probe = Buffer.alloc(1);\n const grew = readSync(descriptor, probe, 0, 1, expectedBytes) !== 0;\n const afterMetadata = safeFstat(descriptor, 'reinspect-file');\n if (grew && expectedBytes >= maxBytes) throw authorityError('OVER_BUDGET', 'read-file');\n if (\n offset !== expectedBytes ||\n grew ||\n !sameSnapshot(snapshot(beforeMetadata), snapshot(afterMetadata))\n ) {\n throw authorityError('FILE_CHANGED', 'read-file');\n }\n return bytes;\n }\n\n private assertRootIdentity(descriptor: number): void {\n const metadata = safeFstat(descriptor, 'inspect-root');\n if (!metadata.isDirectory() || !sameIdentity(this.rootIdentity, identity(metadata))) {\n throw authorityError('ROOT_CHANGED', 'inspect-root');\n }\n }\n\n private throwOpenFailure(errno: number, operation: string, segmentIndex?: number): never {\n const code = hostCode(errno);\n const details =\n segmentIndex === undefined ? { hostCode: code } : { hostCode: code, segmentIndex };\n if (\n errno === koffi.os.errno.ELOOP ||\n errno === koffi.os.errno.ENOTDIR ||\n errno === koffi.os.errno.EISDIR\n ) {\n throw authorityError('UNSAFE_ENTRY', operation, details);\n }\n throw authorityError('HOST_IO', operation, details);\n }\n\n private closeDescriptor(descriptor: number): void {\n try {\n closeSync(descriptor);\n } catch {\n // allow-fallback: descriptor close failures cannot restore authority or make a later read safe\n }\n }\n\n private closeOptionalDescriptor(descriptor: number | undefined): void {\n if (descriptor !== undefined) this.closeDescriptor(descriptor);\n }\n}\n","import * as koffi from 'koffi';\n\nexport type TNativeHandle = bigint | null;\n\nexport interface IWindowsAttributeTagInfo {\n FileAttributes?: number;\n ReparseTag?: number;\n}\n\nexport interface IWindowsFileIdInfo {\n VolumeSerialNumber?: bigint | number;\n FileId?: Uint8Array | number[];\n}\n\nexport interface IWindowsStandardInfo {\n Directory?: number;\n}\n\nexport interface IWindowsObjectAttributes {\n Length: number;\n RootDirectory: TNativeHandle;\n ObjectName: object;\n Attributes: number;\n SecurityDescriptor: null;\n SecurityQualityOfService: null;\n}\n\nexport interface IWindowsIoStatus {\n Status: number;\n Information: number;\n}\n\ntype TCreateFileW = (\n path: string,\n desiredAccess: number,\n shareMode: number,\n securityAttributes: null,\n creationDisposition: number,\n flagsAndAttributes: number,\n templateFile: TNativeHandle,\n) => TNativeHandle;\n\ntype TGetInformation = (\n handle: TNativeHandle,\n informationClass: number,\n output: object,\n outputBytes: number,\n) => boolean;\n\ntype TNtCreateFile = (\n output: TNativeHandle[],\n desiredAccess: number,\n objectAttributes: IWindowsObjectAttributes,\n ioStatus: IWindowsIoStatus,\n allocationSize: null,\n fileAttributes: number,\n shareAccess: number,\n createDisposition: number,\n createOptions: number,\n extendedAttributes: null,\n extendedAttributesBytes: number,\n) => number;\n\nexport interface IWindowsApi {\n readonly handleType: koffi.TypeObject;\n readonly unicodeStringType: koffi.TypeObject;\n readonly objectAttributesType: koffi.TypeObject;\n readonly attributeTagInfoType: koffi.TypeObject;\n readonly fileIdInfoType: koffi.TypeObject;\n readonly standardInfoType: koffi.TypeObject;\n readonly createFileW: TCreateFileW;\n readonly closeHandle: (handle: TNativeHandle) => boolean;\n readonly getAttributeTagInfo: TGetInformation;\n readonly getFileIdInfo: TGetInformation;\n readonly getStandardInfo: TGetInformation;\n readonly getFileSizeEx: (handle: TNativeHandle, size: bigint[]) => boolean;\n readonly readFile: (\n handle: TNativeHandle,\n buffer: Uint8Array,\n bytesToRead: number,\n bytesRead: number[],\n overlapped: null,\n ) => boolean;\n readonly getLastError: () => number;\n readonly ntCreateFile: TNtCreateFile;\n readonly ntClose: (handle: TNativeHandle) => number;\n}\n\ninterface IWindowsTypes {\n readonly handleType: koffi.TypeObject;\n readonly unicodeStringType: koffi.TypeObject;\n readonly objectAttributesType: koffi.TypeObject;\n readonly ioStatusBlockType: koffi.TypeObject;\n readonly attributeTagInfoType: koffi.TypeObject;\n readonly fileIdInfoType: koffi.TypeObject;\n readonly standardInfoType: koffi.TypeObject;\n}\n\nconst FILE_ID_BYTES = 16;\n\nfunction defineWindowsTypes(): IWindowsTypes {\n const handleType = koffi.pointer('RobotaStableFileHandle', koffi.opaque());\n const unicodeStringType = koffi.struct('RobotaUnicodeString', {\n Length: 'uint16_t',\n MaximumLength: 'uint16_t',\n Buffer: koffi.pointer('char16_t'),\n });\n const objectAttributesType = koffi.struct('RobotaObjectAttributes', {\n Length: 'uint32_t',\n RootDirectory: handleType,\n ObjectName: koffi.pointer(unicodeStringType),\n Attributes: 'uint32_t',\n SecurityDescriptor: 'void *',\n SecurityQualityOfService: 'void *',\n });\n return {\n handleType,\n unicodeStringType,\n objectAttributesType,\n ioStatusBlockType: koffi.struct('RobotaIoStatusBlock', {\n Status: 'intptr_t',\n Information: 'uintptr_t',\n }),\n attributeTagInfoType: koffi.struct('RobotaFileAttributeTagInfo', {\n FileAttributes: 'uint32_t',\n ReparseTag: 'uint32_t',\n }),\n fileIdInfoType: koffi.struct('RobotaFileIdInfo', {\n VolumeSerialNumber: 'uint64_t',\n FileId: koffi.array('uint8_t', FILE_ID_BYTES, 'Typed'),\n }),\n standardInfoType: koffi.struct('RobotaFileStandardInfo', {\n AllocationSize: 'int64_t',\n EndOfFile: 'int64_t',\n NumberOfLinks: 'uint32_t',\n DeletePending: 'uint8_t',\n Directory: 'uint8_t',\n }),\n };\n}\n\nfunction bindKernel32(\n library: koffi.LibraryHandle,\n types: IWindowsTypes,\n): Pick<\n IWindowsApi,\n | 'createFileW'\n | 'closeHandle'\n | 'getAttributeTagInfo'\n | 'getFileIdInfo'\n | 'getStandardInfo'\n | 'getFileSizeEx'\n | 'readFile'\n | 'getLastError'\n> {\n return {\n createFileW: library.func('CreateFileW', types.handleType, [\n koffi.pointer('char16_t'),\n 'uint32_t',\n 'uint32_t',\n 'void *',\n 'uint32_t',\n 'uint32_t',\n types.handleType,\n ]) as TCreateFileW,\n closeHandle: library.func('CloseHandle', 'bool', [types.handleType]),\n ...bindInformationFunctions(library, types),\n getFileSizeEx: library.func('GetFileSizeEx', 'bool', [\n types.handleType,\n koffi.out(koffi.pointer('int64_t')),\n ]),\n readFile: library.func('ReadFile', 'bool', [\n types.handleType,\n koffi.out(koffi.pointer('uint8_t')),\n 'uint32_t',\n koffi.out(koffi.pointer('uint32_t')),\n 'void *',\n ]),\n getLastError: library.func('GetLastError', 'uint32_t', []),\n };\n}\n\nfunction bindInformationFunctions(\n library: koffi.LibraryHandle,\n types: IWindowsTypes,\n): Pick<IWindowsApi, 'getAttributeTagInfo' | 'getFileIdInfo' | 'getStandardInfo'> {\n const bind = (outputType: koffi.TypeObject): TGetInformation =>\n library.func('GetFileInformationByHandleEx', 'bool', [\n types.handleType,\n 'int32_t',\n koffi.out(koffi.pointer(outputType)),\n 'uint32_t',\n ]) as TGetInformation;\n return {\n getAttributeTagInfo: bind(types.attributeTagInfoType),\n getFileIdInfo: bind(types.fileIdInfoType),\n getStandardInfo: bind(types.standardInfoType),\n };\n}\n\nfunction bindNtDll(\n library: koffi.LibraryHandle,\n types: IWindowsTypes,\n): Pick<IWindowsApi, 'ntCreateFile' | 'ntClose'> {\n return {\n ntCreateFile: library.func('NtCreateFile', 'int32_t', [\n koffi.out(koffi.pointer(types.handleType)),\n 'uint32_t',\n koffi.pointer(types.objectAttributesType),\n koffi.out(koffi.pointer(types.ioStatusBlockType)),\n 'void *',\n 'uint32_t',\n 'uint32_t',\n 'uint32_t',\n 'uint32_t',\n 'void *',\n 'uint32_t',\n ]) as TNtCreateFile,\n ntClose: library.func('NtClose', 'int32_t', [types.handleType]),\n };\n}\n\nlet cachedApi: IWindowsApi | undefined;\n\nexport function getWindowsApi(): IWindowsApi {\n if (cachedApi !== undefined) return cachedApi;\n const types = defineWindowsTypes();\n cachedApi = {\n ...types,\n ...bindKernel32(koffi.load('kernel32.dll'), types),\n ...bindNtDll(koffi.load('ntdll.dll'), types),\n };\n return cachedApi;\n}\n","import * as koffi from 'koffi';\n\nimport { authorityError } from './contracts.js';\n\nimport type { IFileAuthorityTestHooks } from './host-backend.js';\nimport type { IWindowsApi, IWindowsFileIdInfo, TNativeHandle } from './windows-native-api.js';\n\nexport interface IWindowsIdentity {\n readonly volume: bigint;\n readonly fileId: string;\n}\n\ninterface IWindowsFileSnapshot extends IWindowsIdentity {\n readonly size: bigint;\n}\n\nconst FILE_ID_INFO_CLASS = 18;\nconst CHUNK_BYTES = 1_048_576;\nconst ZERO_SIZE = BigInt('0');\n\nfunction bytesToHex(value: Uint8Array | number[] | undefined): string {\n if (value instanceof Uint8Array) return Buffer.from(value).toString('hex');\n if (Array.isArray(value)) return Buffer.from(value).toString('hex');\n throw authorityError('UNSUPPORTED_BACKEND', 'decode-file-identity');\n}\n\nexport function windowsLastErrorCode(api: IWindowsApi): string {\n return `WIN32_${api.getLastError()}`;\n}\n\nexport function queryWindowsIdentity(\n api: IWindowsApi,\n handle: TNativeHandle,\n operation: string,\n): IWindowsIdentity {\n const info: IWindowsFileIdInfo = {};\n if (!api.getFileIdInfo(handle, FILE_ID_INFO_CLASS, info, koffi.sizeof(api.fileIdInfoType))) {\n throw authorityError('UNSUPPORTED_BACKEND', operation, {\n hostCode: windowsLastErrorCode(api),\n });\n }\n return {\n volume: BigInt(info.VolumeSerialNumber ?? ZERO_SIZE),\n fileId: bytesToHex(info.FileId),\n };\n}\n\nexport function readWindowsFile(\n api: IWindowsApi,\n handle: TNativeHandle,\n maxBytes: number,\n hooks: IFileAuthorityTestHooks,\n): Uint8Array {\n const before = queryFileSnapshot(api, handle);\n if (before.size > BigInt(maxBytes)) throw authorityError('OVER_BUDGET', 'read-file');\n const expectedBytes = Number(before.size);\n let bytes: Buffer;\n try {\n bytes = Buffer.alloc(expectedBytes);\n } catch {\n throw authorityError('HOST_IO', 'allocate-read-buffer');\n }\n\n const offset = readExpectedBytes(api, handle, bytes, hooks);\n const probeCount = [0];\n if (!api.readFile(handle, Buffer.alloc(1), 1, probeCount, null)) {\n throw authorityError('HOST_IO', 'probe-file', { hostCode: windowsLastErrorCode(api) });\n }\n const after = queryFileSnapshot(api, handle);\n if (probeCount[0] !== 0 && expectedBytes >= maxBytes) {\n throw authorityError('OVER_BUDGET', 'read-file');\n }\n if (!isStableRead(before, after, offset, expectedBytes, probeCount[0])) {\n throw authorityError('FILE_CHANGED', 'read-file');\n }\n return bytes;\n}\n\nfunction readExpectedBytes(\n api: IWindowsApi,\n handle: TNativeHandle,\n bytes: Buffer,\n hooks: IFileAuthorityTestHooks,\n): number {\n let offset = 0;\n while (offset < bytes.length) {\n const length = Math.min(CHUNK_BYTES, bytes.length - offset);\n const count = [0];\n if (!api.readFile(handle, bytes.subarray(offset, offset + length), length, count, null)) {\n throw authorityError('HOST_IO', 'read-file', { hostCode: windowsLastErrorCode(api) });\n }\n if (count[0] === 0) break;\n offset += count[0];\n hooks.afterReadChunk?.(offset);\n }\n return offset;\n}\n\nfunction queryFileSnapshot(api: IWindowsApi, handle: TNativeHandle): IWindowsFileSnapshot {\n const identity = queryWindowsIdentity(api, handle, 'inspect-file');\n const size = [ZERO_SIZE];\n if (!api.getFileSizeEx(handle, size)) {\n throw authorityError('HOST_IO', 'inspect-file', { hostCode: windowsLastErrorCode(api) });\n }\n return { ...identity, size: BigInt(size[0]) };\n}\n\nfunction isStableRead(\n before: IWindowsFileSnapshot,\n after: IWindowsFileSnapshot,\n actualBytes: number,\n expectedBytes: number,\n probeBytes: number,\n): boolean {\n return (\n actualBytes === expectedBytes &&\n probeBytes === 0 &&\n before.volume === after.volume &&\n before.fileId === after.fileId &&\n before.size === after.size\n );\n}\n","type TNtOpenFailureKind = 'missing' | 'unsafe' | 'host-io';\n\nexport interface INtOpenFailure {\n readonly kind: TNtOpenFailureKind;\n readonly hostCode: string;\n}\n\nconst HEX_RADIX = 16;\nconst STATUS_NO_SUCH_FILE = 0xc000000f;\nconst STATUS_OBJECT_NAME_NOT_FOUND = 0xc0000034;\nconst STATUS_OBJECT_PATH_NOT_FOUND = 0xc000003a;\nconst STATUS_STOPPED_ON_SYMLINK = 0x8000002d;\nconst STATUS_FILE_IS_A_DIRECTORY = 0xc00000ba;\nconst STATUS_NOT_A_DIRECTORY = 0xc0000103;\nconst STATUS_REPARSE_POINT_ENCOUNTERED = 0xc0000279;\nconst STATUS_IO_REPARSE_TAG_NOT_HANDLED = 0xc000050b;\n\nconst MISSING_STATUSES = new Set([\n STATUS_NO_SUCH_FILE,\n STATUS_OBJECT_NAME_NOT_FOUND,\n STATUS_OBJECT_PATH_NOT_FOUND,\n]);\nconst UNSAFE_STATUSES = new Set([\n STATUS_STOPPED_ON_SYMLINK,\n STATUS_FILE_IS_A_DIRECTORY,\n STATUS_NOT_A_DIRECTORY,\n STATUS_REPARSE_POINT_ENCOUNTERED,\n STATUS_IO_REPARSE_TAG_NOT_HANDLED,\n]);\n\nexport function classifyNtOpenFailure(status: number): INtOpenFailure {\n const normalized = status >>> 0;\n return {\n kind: MISSING_STATUSES.has(normalized)\n ? 'missing'\n : UNSAFE_STATUSES.has(normalized)\n ? 'unsafe'\n : 'host-io',\n hostCode: `NTSTATUS_0x${normalized.toString(HEX_RADIX)}`,\n };\n}\n","import * as koffi from 'koffi';\n\nimport { authorityError } from './contracts.js';\nimport {\n getWindowsApi,\n type IWindowsApi,\n type IWindowsAttributeTagInfo,\n type IWindowsIoStatus,\n type IWindowsObjectAttributes,\n type IWindowsStandardInfo,\n type TNativeHandle,\n} from './windows-native-api.js';\nimport {\n queryWindowsIdentity,\n readWindowsFile,\n windowsLastErrorCode,\n type IWindowsIdentity,\n} from './windows-native-read.js';\nimport { classifyNtOpenFailure } from './windows-native-status.js';\n\nimport type { IFileAuthorityTestHooks } from './host-backend.js';\n\nexport type { IWindowsIdentity } from './windows-native-read.js';\n\nconst GENERIC_READ = 0x80000000;\nconst FILE_READ_DATA = 0x0001;\nconst FILE_LIST_DIRECTORY = 0x0001;\nconst FILE_READ_ATTRIBUTES = 0x0080;\nconst SYNCHRONIZE = 0x00100000;\nconst FILE_SHARE_READ = 0x00000001;\nconst FILE_SHARE_WRITE = 0x00000002;\nconst FILE_SHARE_DELETE = 0x00000004;\nconst OPEN_EXISTING = 3;\nconst FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400;\nconst FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;\nconst FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;\nconst FILE_OPEN = 1;\nconst FILE_DIRECTORY_FILE = 0x00000001;\nconst FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020;\nconst FILE_NON_DIRECTORY_FILE = 0x00000040;\nconst FILE_OPEN_REPARSE_POINT = 0x00200000;\nconst OBJ_CASE_INSENSITIVE = 0x00000040;\nconst OBJ_DONT_REPARSE = 0x00001000;\nconst FILE_STANDARD_INFO_CLASS = 1;\nconst FILE_ATTRIBUTE_TAG_INFO_CLASS = 9;\nconst HANDLE_WIDTH_BITS = 64;\nconst INVALID_HANDLE_VALUE = BigInt('-1');\n\nfunction isInvalidHandle(handle: TNativeHandle): boolean {\n return handle === null || BigInt.asIntN(HANDLE_WIDTH_BITS, handle) === INVALID_HANDLE_VALUE;\n}\n\n/** Raw Windows handle operations. Native HANDLE and Koffi values stay inside this module. */\nexport class WindowsNativeAuthority {\n private readonly api: IWindowsApi;\n\n constructor() {\n try {\n this.api = getWindowsApi();\n } catch {\n throw authorityError('UNSUPPORTED_BACKEND', 'load-windows-backend');\n }\n }\n\n openRoot(rootDirectory: string): TNativeHandle {\n const handle = this.api.createFileW(\n rootDirectory,\n GENERIC_READ,\n FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,\n null,\n OPEN_EXISTING,\n FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,\n null,\n );\n if (isInvalidHandle(handle)) {\n throw authorityError('HOST_IO', 'open-root', { hostCode: this.lastErrorCode() });\n }\n try {\n this.assertSafeHandle(handle, true, 'inspect-root');\n return handle;\n } catch (error) {\n this.close(handle);\n throw error;\n }\n }\n\n openRelative(\n parentHandle: TNativeHandle,\n segment: string,\n final: boolean,\n segmentIndex: number,\n ): TNativeHandle | undefined {\n const { handle, status } = this.issueRelativeOpen(parentHandle, segment, final);\n if (status >= 0) {\n if (isInvalidHandle(handle)) throw authorityError('HOST_IO', 'open-relative');\n try {\n this.assertSafeHandle(\n handle,\n !final,\n final ? 'inspect-file' : 'inspect-directory',\n segmentIndex,\n );\n return handle;\n } catch (error) {\n this.close(handle);\n throw error;\n }\n }\n if (!isInvalidHandle(handle)) this.close(handle);\n const failure = classifyNtOpenFailure(status);\n if (failure.kind === 'missing') return undefined;\n const details = { segmentIndex, hostCode: failure.hostCode };\n if (failure.kind === 'unsafe') {\n throw authorityError('UNSAFE_ENTRY', final ? 'open-file' : 'open-directory', details);\n }\n throw authorityError('HOST_IO', final ? 'open-file' : 'open-directory', details);\n }\n\n readFile(handle: TNativeHandle, maxBytes: number, hooks: IFileAuthorityTestHooks): Uint8Array {\n return readWindowsFile(this.api, handle, maxBytes, hooks);\n }\n\n queryIdentity(handle: TNativeHandle, operation: string): IWindowsIdentity {\n return queryWindowsIdentity(this.api, handle, operation);\n }\n\n close(handle: TNativeHandle): void {\n try {\n if (this.api.ntClose(handle) >= 0) return;\n } catch {\n // allow-fallback: CloseHandle is the native cleanup fallback after NtClose itself throws\n }\n try {\n this.api.closeHandle(handle);\n } catch {\n // allow-fallback: close failure cannot restore authority or make a later read safe\n }\n }\n\n private issueRelativeOpen(\n parentHandle: TNativeHandle,\n segment: string,\n final: boolean,\n ): { handle: TNativeHandle; status: number } {\n const unicodeName = koffi.alloc(this.api.unicodeStringType, 1);\n try {\n koffi.encode(unicodeName, this.api.unicodeStringType, {\n Length: Buffer.byteLength(segment, 'utf16le'),\n MaximumLength: Buffer.byteLength(`${segment}\\0`, 'utf16le'),\n Buffer: segment,\n });\n const attributes: IWindowsObjectAttributes = {\n Length: koffi.sizeof(this.api.objectAttributesType),\n RootDirectory: parentHandle,\n ObjectName: unicodeName,\n Attributes: OBJ_CASE_INSENSITIVE | OBJ_DONT_REPARSE,\n SecurityDescriptor: null,\n SecurityQualityOfService: null,\n };\n const output: TNativeHandle[] = [null];\n const ioStatus: IWindowsIoStatus = { Status: 0, Information: 0 };\n // A final read cannot verify same-size in-place rewrites from identity and size alone.\n // Deny write sharing for that handle so existing and new writers cannot race the read.\n const shareAccess = final\n ? FILE_SHARE_READ | FILE_SHARE_DELETE\n : FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;\n const status = this.api.ntCreateFile(\n output,\n (final ? FILE_READ_DATA : FILE_LIST_DIRECTORY) | FILE_READ_ATTRIBUTES | SYNCHRONIZE,\n attributes,\n ioStatus,\n null,\n 0,\n shareAccess,\n FILE_OPEN,\n (final ? FILE_NON_DIRECTORY_FILE : FILE_DIRECTORY_FILE) |\n FILE_SYNCHRONOUS_IO_NONALERT |\n FILE_OPEN_REPARSE_POINT,\n null,\n 0,\n );\n return { handle: output[0], status };\n } finally {\n koffi.free(unicodeName);\n }\n }\n\n private assertSafeHandle(\n handle: TNativeHandle,\n directory: boolean,\n operation: string,\n segmentIndex?: number,\n ): void {\n const attributes: IWindowsAttributeTagInfo = {};\n const standard: IWindowsStandardInfo = {};\n if (\n !this.api.getAttributeTagInfo(\n handle,\n FILE_ATTRIBUTE_TAG_INFO_CLASS,\n attributes,\n koffi.sizeof(this.api.attributeTagInfoType),\n ) ||\n !this.api.getStandardInfo(\n handle,\n FILE_STANDARD_INFO_CLASS,\n standard,\n koffi.sizeof(this.api.standardInfoType),\n )\n ) {\n throw authorityError('HOST_IO', operation, { hostCode: this.lastErrorCode() });\n }\n const unsafe =\n ((attributes.FileAttributes ?? 0) & FILE_ATTRIBUTE_REPARSE_POINT) !== 0 ||\n Boolean(standard.Directory) !== directory;\n if (unsafe) {\n throw authorityError(\n 'UNSAFE_ENTRY',\n operation,\n segmentIndex === undefined ? {} : { segmentIndex },\n );\n }\n }\n\n private lastErrorCode(): string {\n return windowsLastErrorCode(this.api);\n }\n}\n","import { authorityError, StableFileAuthorityError } from './contracts.js';\nimport { WindowsNativeAuthority, type IWindowsIdentity } from './windows-native-authority.js';\n\nimport type { IFileAuthorityTestHooks, IStableFileHostBackend } from './host-backend.js';\nimport type { TNativeHandle } from './windows-native-api.js';\n\n/** Windows implementation; native HANDLE values never leave the native authority module. */\nexport class WindowsStableFileHostBackend implements IStableFileHostBackend {\n private readonly native = new WindowsNativeAuthority();\n private readonly rootIdentity: IWindowsIdentity;\n private rootHandle: TNativeHandle | undefined;\n\n constructor(rootDirectory: string) {\n try {\n const handle = this.native.openRoot(rootDirectory);\n this.rootHandle = handle;\n this.rootIdentity = this.native.queryIdentity(handle, 'inspect-root');\n } catch (error) {\n if (this.rootHandle !== undefined) {\n this.native.close(this.rootHandle);\n this.rootHandle = undefined;\n }\n if (error instanceof StableFileAuthorityError) throw error;\n throw authorityError('UNSUPPORTED_BACKEND', 'load-windows-backend');\n }\n }\n\n readBytes(\n relativeSegments: readonly string[],\n maxBytes: number,\n hooks: IFileAuthorityTestHooks,\n ): Uint8Array | undefined {\n const rootHandle = this.rootHandle;\n if (rootHandle === undefined) throw authorityError('AUTHORITY_CLOSED', 'read');\n this.assertRootIdentity(rootHandle);\n\n let parentHandle = rootHandle;\n let ownsParent = false;\n let fileHandle: TNativeHandle | undefined;\n try {\n for (let index = 0; index < relativeSegments.length; index += 1) {\n const final = index === relativeSegments.length - 1;\n const handle = this.native.openRelative(\n parentHandle,\n relativeSegments[index],\n final,\n index,\n );\n if (handle === undefined) return undefined;\n if (final) {\n fileHandle = handle;\n break;\n }\n if (ownsParent) this.native.close(parentHandle);\n parentHandle = handle;\n ownsParent = true;\n hooks.afterDirectoryOpened?.(index);\n }\n\n if (fileHandle === undefined) throw authorityError('HOST_IO', 'open-file');\n const bytes = this.native.readFile(fileHandle, maxBytes, hooks);\n this.assertRootIdentity(rootHandle);\n return bytes;\n } catch (error) {\n if (error instanceof StableFileAuthorityError) throw error;\n throw authorityError('HOST_IO', 'read');\n } finally {\n if (fileHandle !== undefined) this.native.close(fileHandle);\n if (ownsParent) this.native.close(parentHandle);\n }\n }\n\n close(): void {\n const handle = this.rootHandle;\n if (handle === undefined) return;\n this.rootHandle = undefined;\n this.native.close(handle);\n }\n\n private assertRootIdentity(handle: TNativeHandle): void {\n const current = this.native.queryIdentity(handle, 'inspect-root');\n if (\n current.volume !== this.rootIdentity.volume ||\n current.fileId !== this.rootIdentity.fileId\n ) {\n throw authorityError('ROOT_CHANGED', 'inspect-root');\n }\n }\n}\n","import { resolve } from 'node:path';\n\nimport {\n authorityError,\n StableFileAuthorityError,\n type IStableRootedFileReader,\n} from './contracts.js';\nimport { PosixStableFileHostBackend } from './posix-backend.js';\nimport { WindowsStableFileHostBackend } from './windows-backend.js';\n\nimport type { IFileAuthorityTestHooks, IStableFileHostBackend } from './host-backend.js';\n\nexport type { IFileAuthorityTestHooks } from './host-backend.js';\n\nconst SUPPORTED_HOSTS = new Set([\n 'darwin-arm64',\n 'darwin-x64',\n 'linux-arm64',\n 'linux-x64',\n 'win32-x64',\n]);\n\nconst finalizer = new FinalizationRegistry<IStableFileHostBackend>((backend) => {\n backend.close();\n});\n\nfunction validateRoot(rootDirectory: string): string {\n if (\n typeof rootDirectory !== 'string' ||\n rootDirectory.trim().length === 0 ||\n rootDirectory.includes('\\0')\n ) {\n throw authorityError('INVALID_PATH', 'validate-root');\n }\n return resolve(rootDirectory);\n}\n\nfunction validateReadInput(relativeSegments: readonly string[], maxBytes: number): void {\n if (!Array.isArray(relativeSegments) || relativeSegments.length === 0) {\n throw authorityError('INVALID_PATH', 'validate-segments');\n }\n for (let index = 0; index < relativeSegments.length; index += 1) {\n const segment = relativeSegments[index];\n if (\n typeof segment !== 'string' ||\n segment.length === 0 ||\n segment === '.' ||\n segment === '..' ||\n segment.includes('\\0') ||\n segment.includes('/') ||\n segment.includes('\\\\')\n ) {\n throw authorityError('INVALID_PATH', 'validate-segments', { segmentIndex: index });\n }\n }\n if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {\n throw authorityError('INVALID_PATH', 'validate-budget');\n }\n}\n\nfunction createHostBackend(rootDirectory: string): IStableFileHostBackend {\n const host = `${process.platform}-${process.arch}`;\n if (!SUPPORTED_HOSTS.has(host)) {\n throw authorityError('UNSUPPORTED_BACKEND', 'select-backend', { hostCode: host });\n }\n if (process.platform === 'win32') return new WindowsStableFileHostBackend(rootDirectory);\n return new PosixStableFileHostBackend(rootDirectory);\n}\n\nclass StableRootedFileReader implements IStableRootedFileReader {\n private backend: IStableFileHostBackend | undefined;\n private readonly finalizerToken = {};\n\n constructor(\n rootDirectory: string,\n private readonly hooks: IFileAuthorityTestHooks,\n ) {\n const backend = createHostBackend(validateRoot(rootDirectory));\n this.backend = backend;\n finalizer.register(this, backend, this.finalizerToken);\n }\n\n readBytes(relativeSegments: readonly string[], maxBytes: number): Uint8Array | undefined {\n const backend = this.backend;\n if (backend === undefined) throw authorityError('AUTHORITY_CLOSED', 'read');\n validateReadInput(relativeSegments, maxBytes);\n try {\n return backend.readBytes(relativeSegments, maxBytes, this.hooks);\n } catch (error) {\n if (error instanceof StableFileAuthorityError) throw error;\n throw authorityError('HOST_IO', 'read');\n }\n }\n\n close(): void {\n const backend = this.backend;\n if (backend === undefined) return;\n this.backend = undefined;\n finalizer.unregister(this.finalizerToken);\n try {\n backend.close();\n } catch (error) {\n if (error instanceof StableFileAuthorityError) throw error;\n throw authorityError('HOST_IO', 'close');\n }\n }\n\n [Symbol.dispose](): void {\n this.close();\n }\n}\n\n/** Create a stable, opaque read authority rooted at the currently opened directory identity. */\nexport function createStableRootedFileReader(rootDirectory: string): IStableRootedFileReader {\n return new StableRootedFileReader(rootDirectory, {});\n}\n\n/** Internal-only deterministic seams used by native replacement tests. */\nexport function createStableRootedFileReaderForTest(\n rootDirectory: string,\n hooks: IFileAuthorityTestHooks,\n): IStableRootedFileReader {\n return new StableRootedFileReader(rootDirectory, hooks);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["CHUNK_BYTES","retainedLibraries"],"sources":["../../src/contracts.ts","../../src/posix-backend.ts","../../src/windows-native-api.ts","../../src/windows-native-read.ts","../../src/windows-native-status.ts","../../src/windows-native-authority.ts","../../src/windows-backend.ts","../../src/stable-rooted-file-reader.ts"],"sourcesContent":["export type TStableFileAuthorityErrorCode =\n | 'INVALID_PATH'\n | 'UNSAFE_ENTRY'\n | 'UNSUPPORTED_BACKEND'\n | 'AUTHORITY_CLOSED'\n | 'ROOT_CHANGED'\n | 'FILE_CHANGED'\n | 'OVER_BUDGET'\n | 'HOST_IO';\n\nexport interface IStableFileAuthorityErrorContext {\n readonly operation: string;\n readonly segmentIndex?: number;\n readonly hostCode?: string;\n}\n\nconst ERROR_MESSAGES: Readonly<Record<TStableFileAuthorityErrorCode, string>> = {\n INVALID_PATH: 'The requested root-relative file operation is invalid.',\n UNSAFE_ENTRY: 'The requested entry cannot be opened without following an unsafe object.',\n UNSUPPORTED_BACKEND: 'This host cannot provide stable root-relative file reads.',\n AUTHORITY_CLOSED: 'The stable rooted file authority is closed.',\n ROOT_CHANGED: 'The retained root identity changed during the operation.',\n FILE_CHANGED: 'The file identity or contents changed during the bounded read.',\n OVER_BUDGET: 'The file exceeds the supplied byte budget.',\n HOST_IO: 'The host could not complete the stable root-relative file operation.',\n};\n\n/** A stable, secret-free failure from a rooted file authority. */\nexport class StableFileAuthorityError extends Error {\n readonly code: TStableFileAuthorityErrorCode;\n readonly context: Readonly<IStableFileAuthorityErrorContext>;\n\n constructor(code: TStableFileAuthorityErrorCode, context: IStableFileAuthorityErrorContext) {\n super(ERROR_MESSAGES[code]);\n this.name = 'StableFileAuthorityError';\n this.code = code;\n this.context = Object.freeze({ ...context });\n }\n}\n\n/** Opaque authority for bounded regular-file reads beneath one retained directory identity. */\nexport interface IStableRootedFileReader {\n readBytes(relativeSegments: readonly string[], maxBytes: number): Uint8Array | undefined;\n close(): void;\n [Symbol.dispose](): void;\n}\n\nexport function authorityError(\n code: TStableFileAuthorityErrorCode,\n operation: string,\n details: Omit<IStableFileAuthorityErrorContext, 'operation'> = {},\n): StableFileAuthorityError {\n return new StableFileAuthorityError(code, { operation, ...details });\n}\n","import { closeSync, constants, fstatSync, readSync, type BigIntStats } from 'node:fs';\n\nimport * as koffi from 'koffi';\n\nimport { authorityError, StableFileAuthorityError } from './contracts.js';\n\nimport type { IFileAuthorityTestHooks, IStableFileHostBackend } from './host-backend.js';\n\ninterface IPosixIdentity {\n readonly device: bigint;\n readonly inode: bigint;\n readonly mode: bigint;\n}\n\ninterface IPosixFileSnapshot extends IPosixIdentity {\n readonly size: bigint;\n readonly modified: bigint;\n readonly changed: bigint;\n}\n\ntype TOpen = (path: string, flags: number) => number;\ntype TOpenAt = (directory: number, path: string, flags: number) => number;\n\nconst CHUNK_BYTES = 1_048_576;\nconst DARWIN_CLOSE_ON_EXEC = 0x01000000;\nconst LINUX_CLOSE_ON_EXEC = 0x00080000;\n\nfunction identity(metadata: BigIntStats): IPosixIdentity {\n return { device: metadata.dev, inode: metadata.ino, mode: metadata.mode };\n}\n\nfunction snapshot(metadata: BigIntStats): IPosixFileSnapshot {\n return {\n ...identity(metadata),\n size: metadata.size,\n modified: metadata.mtimeNs,\n changed: metadata.ctimeNs,\n };\n}\n\nfunction sameIdentity(left: IPosixIdentity, right: IPosixIdentity): boolean {\n return left.device === right.device && left.inode === right.inode && left.mode === right.mode;\n}\n\nfunction sameSnapshot(left: IPosixFileSnapshot, right: IPosixFileSnapshot): boolean {\n return (\n sameIdentity(left, right) &&\n left.size === right.size &&\n left.modified === right.modified &&\n left.changed === right.changed\n );\n}\n\nfunction hostCode(errno: number): string {\n for (const [name, value] of Object.entries(koffi.os.errno)) {\n if (value === errno) return name;\n }\n return `ERRNO_${errno}`;\n}\n\nfunction flags(directory: boolean): number {\n const closeOnExec =\n (constants as typeof constants & { readonly O_CLOEXEC?: number }).O_CLOEXEC ??\n (process.platform === 'darwin' ? DARWIN_CLOSE_ON_EXEC : LINUX_CLOSE_ON_EXEC);\n return (\n constants.O_RDONLY |\n constants.O_NOFOLLOW |\n constants.O_NONBLOCK |\n closeOnExec |\n (directory ? constants.O_DIRECTORY : 0)\n );\n}\n\nfunction safeFstat(descriptor: number, operation: string): BigIntStats {\n try {\n return fstatSync(descriptor, { bigint: true });\n } catch (error) {\n const code = nodeErrorCode(typeof error === 'object' ? error : null);\n throw authorityError('HOST_IO', operation, code === undefined ? {} : { hostCode: code });\n }\n}\n\nfunction nodeErrorCode(error: object | null): string | undefined {\n if (error === null || !('code' in error)) return undefined;\n return typeof error.code === 'string' ? error.code : undefined;\n}\n\nlet libc: { library: unknown; open: TOpen; openAt: TOpenAt } | undefined;\n// Held as soon as it loads, even if declaring a function below then throws.\nconst retainedLibraries: unknown[] = [];\n\n/**\n * libc is loaded once per process and the library handle itself is kept alive with its functions.\n * Bun aborts when Koffi's finalizer for a collected library handle runs (\"Finalizer is calling a\n * function that may affect GC state\"), so no handle may ever become garbage.\n */\nfunction libcFunctions(): { open: TOpen; openAt: TOpenAt } {\n if (!libc) {\n const library = koffi.load(\n process.platform === 'darwin' ? '/usr/lib/libSystem.B.dylib' : 'libc.so.6',\n );\n retainedLibraries.push(library);\n libc = {\n library,\n open: library.func('int open(const char *path, int flags)') as TOpen,\n openAt: library.func('int openat(int fd, const char *path, int flags)') as TOpenAt,\n };\n }\n return libc;\n}\n\n/** POSIX implementation; descriptors never leave this module. */\nexport class PosixStableFileHostBackend implements IStableFileHostBackend {\n private readonly openAt: TOpenAt;\n private readonly rootIdentity: IPosixIdentity;\n private rootDescriptor: number | undefined;\n\n constructor(rootDirectory: string) {\n try {\n const { open, openAt } = libcFunctions();\n this.openAt = openAt;\n const descriptor = open(rootDirectory, flags(true));\n if (descriptor < 0) this.throwOpenFailure(koffi.errno(), 'open-root');\n this.rootDescriptor = descriptor;\n const metadata = safeFstat(descriptor, 'inspect-root');\n if (!metadata.isDirectory()) throw authorityError('UNSAFE_ENTRY', 'open-root');\n this.rootIdentity = identity(metadata);\n } catch (error) {\n if (this.rootDescriptor !== undefined) {\n this.closeDescriptor(this.rootDescriptor);\n this.rootDescriptor = undefined;\n }\n if (error instanceof StableFileAuthorityError) throw error;\n throw authorityError('UNSUPPORTED_BACKEND', 'load-posix-backend');\n }\n }\n\n readBytes(\n relativeSegments: readonly string[],\n maxBytes: number,\n hooks: IFileAuthorityTestHooks,\n ): Uint8Array | undefined {\n const rootDescriptor = this.rootDescriptor;\n if (rootDescriptor === undefined) throw authorityError('AUTHORITY_CLOSED', 'read');\n this.assertRootIdentity(rootDescriptor);\n\n let parentDescriptor = rootDescriptor;\n let ownsParent = false;\n let fileDescriptor: number | undefined;\n try {\n for (let index = 0; index < relativeSegments.length; index += 1) {\n const final = index === relativeSegments.length - 1;\n const descriptor = this.openAt(parentDescriptor, relativeSegments[index], flags(!final));\n if (descriptor < 0) {\n const errno = koffi.errno();\n if (errno === koffi.os.errno.ENOENT) return undefined;\n this.throwOpenFailure(errno, final ? 'open-file' : 'open-directory', index);\n }\n\n if (final) {\n fileDescriptor = descriptor;\n break;\n }\n\n const metadata = safeFstat(descriptor, 'inspect-directory');\n if (!metadata.isDirectory()) {\n this.closeDescriptor(descriptor);\n throw authorityError('UNSAFE_ENTRY', 'inspect-directory', { segmentIndex: index });\n }\n if (ownsParent) this.closeDescriptor(parentDescriptor);\n parentDescriptor = descriptor;\n ownsParent = true;\n hooks.afterDirectoryOpened?.(index);\n }\n\n if (fileDescriptor === undefined) throw authorityError('HOST_IO', 'open-file');\n const bytes = this.readFile(fileDescriptor, maxBytes, hooks);\n this.assertRootIdentity(rootDescriptor);\n return bytes;\n } catch (error) {\n if (error instanceof StableFileAuthorityError) throw error;\n const code = nodeErrorCode(typeof error === 'object' ? error : null);\n throw authorityError('HOST_IO', 'read', code === undefined ? {} : { hostCode: code });\n } finally {\n this.closeOptionalDescriptor(fileDescriptor);\n this.closeOptionalDescriptor(ownsParent ? parentDescriptor : undefined);\n }\n }\n\n close(): void {\n const descriptor = this.rootDescriptor;\n if (descriptor === undefined) return;\n this.rootDescriptor = undefined;\n this.closeDescriptor(descriptor);\n }\n\n private readFile(\n descriptor: number,\n maxBytes: number,\n hooks: IFileAuthorityTestHooks,\n ): Uint8Array {\n const beforeMetadata = safeFstat(descriptor, 'inspect-file');\n if (!beforeMetadata.isFile()) throw authorityError('UNSAFE_ENTRY', 'inspect-file');\n if (beforeMetadata.size > BigInt(maxBytes)) throw authorityError('OVER_BUDGET', 'read-file');\n\n const expectedBytes = Number(beforeMetadata.size);\n let bytes: Buffer;\n try {\n bytes = Buffer.alloc(expectedBytes);\n } catch {\n throw authorityError('HOST_IO', 'allocate-read-buffer');\n }\n\n let offset = 0;\n while (offset < expectedBytes) {\n const count = readSync(\n descriptor,\n bytes,\n offset,\n Math.min(CHUNK_BYTES, expectedBytes - offset),\n offset,\n );\n if (count === 0) break;\n offset += count;\n hooks.afterReadChunk?.(offset);\n }\n\n const probe = Buffer.alloc(1);\n const grew = readSync(descriptor, probe, 0, 1, expectedBytes) !== 0;\n const afterMetadata = safeFstat(descriptor, 'reinspect-file');\n if (grew && expectedBytes >= maxBytes) throw authorityError('OVER_BUDGET', 'read-file');\n if (\n offset !== expectedBytes ||\n grew ||\n !sameSnapshot(snapshot(beforeMetadata), snapshot(afterMetadata))\n ) {\n throw authorityError('FILE_CHANGED', 'read-file');\n }\n return bytes;\n }\n\n private assertRootIdentity(descriptor: number): void {\n const metadata = safeFstat(descriptor, 'inspect-root');\n if (!metadata.isDirectory() || !sameIdentity(this.rootIdentity, identity(metadata))) {\n throw authorityError('ROOT_CHANGED', 'inspect-root');\n }\n }\n\n private throwOpenFailure(errno: number, operation: string, segmentIndex?: number): never {\n const code = hostCode(errno);\n const details =\n segmentIndex === undefined ? { hostCode: code } : { hostCode: code, segmentIndex };\n if (\n errno === koffi.os.errno.ELOOP ||\n errno === koffi.os.errno.ENOTDIR ||\n errno === koffi.os.errno.EISDIR\n ) {\n throw authorityError('UNSAFE_ENTRY', operation, details);\n }\n throw authorityError('HOST_IO', operation, details);\n }\n\n private closeDescriptor(descriptor: number): void {\n try {\n closeSync(descriptor);\n } catch {\n // allow-fallback: descriptor close failures cannot restore authority or make a later read safe\n }\n }\n\n private closeOptionalDescriptor(descriptor: number | undefined): void {\n if (descriptor !== undefined) this.closeDescriptor(descriptor);\n }\n}\n","import * as koffi from 'koffi';\n\nexport type TNativeHandle = bigint | null;\n\nexport interface IWindowsAttributeTagInfo {\n FileAttributes?: number;\n ReparseTag?: number;\n}\n\nexport interface IWindowsFileIdInfo {\n VolumeSerialNumber?: bigint | number;\n FileId?: Uint8Array | number[];\n}\n\nexport interface IWindowsStandardInfo {\n Directory?: number;\n}\n\nexport interface IWindowsObjectAttributes {\n Length: number;\n RootDirectory: TNativeHandle;\n ObjectName: object;\n Attributes: number;\n SecurityDescriptor: null;\n SecurityQualityOfService: null;\n}\n\nexport interface IWindowsIoStatus {\n Status: number;\n Information: number;\n}\n\ntype TCreateFileW = (\n path: string,\n desiredAccess: number,\n shareMode: number,\n securityAttributes: null,\n creationDisposition: number,\n flagsAndAttributes: number,\n templateFile: TNativeHandle,\n) => TNativeHandle;\n\ntype TGetInformation = (\n handle: TNativeHandle,\n informationClass: number,\n output: object,\n outputBytes: number,\n) => boolean;\n\ntype TNtCreateFile = (\n output: TNativeHandle[],\n desiredAccess: number,\n objectAttributes: IWindowsObjectAttributes,\n ioStatus: IWindowsIoStatus,\n allocationSize: null,\n fileAttributes: number,\n shareAccess: number,\n createDisposition: number,\n createOptions: number,\n extendedAttributes: null,\n extendedAttributesBytes: number,\n) => number;\n\nexport interface IWindowsApi {\n readonly handleType: koffi.TypeObject;\n readonly unicodeStringType: koffi.TypeObject;\n readonly objectAttributesType: koffi.TypeObject;\n readonly attributeTagInfoType: koffi.TypeObject;\n readonly fileIdInfoType: koffi.TypeObject;\n readonly standardInfoType: koffi.TypeObject;\n readonly createFileW: TCreateFileW;\n readonly closeHandle: (handle: TNativeHandle) => boolean;\n readonly getAttributeTagInfo: TGetInformation;\n readonly getFileIdInfo: TGetInformation;\n readonly getStandardInfo: TGetInformation;\n readonly getFileSizeEx: (handle: TNativeHandle, size: bigint[]) => boolean;\n readonly readFile: (\n handle: TNativeHandle,\n buffer: Uint8Array,\n bytesToRead: number,\n bytesRead: number[],\n overlapped: null,\n ) => boolean;\n readonly getLastError: () => number;\n readonly ntCreateFile: TNtCreateFile;\n readonly ntClose: (handle: TNativeHandle) => number;\n}\n\ninterface IWindowsTypes {\n readonly handleType: koffi.TypeObject;\n readonly unicodeStringType: koffi.TypeObject;\n readonly objectAttributesType: koffi.TypeObject;\n readonly ioStatusBlockType: koffi.TypeObject;\n readonly attributeTagInfoType: koffi.TypeObject;\n readonly fileIdInfoType: koffi.TypeObject;\n readonly standardInfoType: koffi.TypeObject;\n}\n\nconst FILE_ID_BYTES = 16;\n\nfunction defineWindowsTypes(): IWindowsTypes {\n const handleType = koffi.pointer('RobotaStableFileHandle', koffi.opaque());\n const unicodeStringType = koffi.struct('RobotaUnicodeString', {\n Length: 'uint16_t',\n MaximumLength: 'uint16_t',\n Buffer: koffi.pointer('char16_t'),\n });\n const objectAttributesType = koffi.struct('RobotaObjectAttributes', {\n Length: 'uint32_t',\n RootDirectory: handleType,\n ObjectName: koffi.pointer(unicodeStringType),\n Attributes: 'uint32_t',\n SecurityDescriptor: 'void *',\n SecurityQualityOfService: 'void *',\n });\n return {\n handleType,\n unicodeStringType,\n objectAttributesType,\n ioStatusBlockType: koffi.struct('RobotaIoStatusBlock', {\n Status: 'intptr_t',\n Information: 'uintptr_t',\n }),\n attributeTagInfoType: koffi.struct('RobotaFileAttributeTagInfo', {\n FileAttributes: 'uint32_t',\n ReparseTag: 'uint32_t',\n }),\n fileIdInfoType: koffi.struct('RobotaFileIdInfo', {\n VolumeSerialNumber: 'uint64_t',\n FileId: koffi.array('uint8_t', FILE_ID_BYTES, 'Typed'),\n }),\n standardInfoType: koffi.struct('RobotaFileStandardInfo', {\n AllocationSize: 'int64_t',\n EndOfFile: 'int64_t',\n NumberOfLinks: 'uint32_t',\n DeletePending: 'uint8_t',\n Directory: 'uint8_t',\n }),\n };\n}\n\nfunction bindKernel32(\n library: koffi.LibraryHandle,\n types: IWindowsTypes,\n): Pick<\n IWindowsApi,\n | 'createFileW'\n | 'closeHandle'\n | 'getAttributeTagInfo'\n | 'getFileIdInfo'\n | 'getStandardInfo'\n | 'getFileSizeEx'\n | 'readFile'\n | 'getLastError'\n> {\n return {\n createFileW: library.func('CreateFileW', types.handleType, [\n koffi.pointer('char16_t'),\n 'uint32_t',\n 'uint32_t',\n 'void *',\n 'uint32_t',\n 'uint32_t',\n types.handleType,\n ]) as TCreateFileW,\n closeHandle: library.func('CloseHandle', 'bool', [types.handleType]),\n ...bindInformationFunctions(library, types),\n getFileSizeEx: library.func('GetFileSizeEx', 'bool', [\n types.handleType,\n koffi.out(koffi.pointer('int64_t')),\n ]),\n readFile: library.func('ReadFile', 'bool', [\n types.handleType,\n koffi.out(koffi.pointer('uint8_t')),\n 'uint32_t',\n koffi.out(koffi.pointer('uint32_t')),\n 'void *',\n ]),\n getLastError: library.func('GetLastError', 'uint32_t', []),\n };\n}\n\nfunction bindInformationFunctions(\n library: koffi.LibraryHandle,\n types: IWindowsTypes,\n): Pick<IWindowsApi, 'getAttributeTagInfo' | 'getFileIdInfo' | 'getStandardInfo'> {\n const bind = (outputType: koffi.TypeObject): TGetInformation =>\n library.func('GetFileInformationByHandleEx', 'bool', [\n types.handleType,\n 'int32_t',\n koffi.out(koffi.pointer(outputType)),\n 'uint32_t',\n ]) as TGetInformation;\n return {\n getAttributeTagInfo: bind(types.attributeTagInfoType),\n getFileIdInfo: bind(types.fileIdInfoType),\n getStandardInfo: bind(types.standardInfoType),\n };\n}\n\nfunction bindNtDll(\n library: koffi.LibraryHandle,\n types: IWindowsTypes,\n): Pick<IWindowsApi, 'ntCreateFile' | 'ntClose'> {\n return {\n ntCreateFile: library.func('NtCreateFile', 'int32_t', [\n koffi.out(koffi.pointer(types.handleType)),\n 'uint32_t',\n koffi.pointer(types.objectAttributesType),\n koffi.out(koffi.pointer(types.ioStatusBlockType)),\n 'void *',\n 'uint32_t',\n 'uint32_t',\n 'uint32_t',\n 'uint32_t',\n 'void *',\n 'uint32_t',\n ]) as TNtCreateFile,\n ntClose: library.func('NtClose', 'int32_t', [types.handleType]),\n };\n}\n\nlet cachedApi: IWindowsApi | undefined;\n/**\n * The library handles stay referenced for the life of the process: Bun aborts when Koffi's\n * finalizer for a collected library handle runs, so a handle must never become garbage.\n */\nconst retainedLibraries: unknown[] = [];\n\nexport function getWindowsApi(): IWindowsApi {\n if (cachedApi !== undefined) return cachedApi;\n const types = defineWindowsTypes();\n const kernel32 = koffi.load('kernel32.dll');\n retainedLibraries.push(kernel32);\n const ntdll = koffi.load('ntdll.dll');\n retainedLibraries.push(ntdll);\n cachedApi = {\n ...types,\n ...bindKernel32(kernel32, types),\n ...bindNtDll(ntdll, types),\n };\n return cachedApi;\n}\n","import * as koffi from 'koffi';\n\nimport { authorityError } from './contracts.js';\n\nimport type { IFileAuthorityTestHooks } from './host-backend.js';\nimport type { IWindowsApi, IWindowsFileIdInfo, TNativeHandle } from './windows-native-api.js';\n\nexport interface IWindowsIdentity {\n readonly volume: bigint;\n readonly fileId: string;\n}\n\ninterface IWindowsFileSnapshot extends IWindowsIdentity {\n readonly size: bigint;\n}\n\nconst FILE_ID_INFO_CLASS = 18;\nconst CHUNK_BYTES = 1_048_576;\nconst ZERO_SIZE = BigInt('0');\n\nfunction bytesToHex(value: Uint8Array | number[] | undefined): string {\n if (value instanceof Uint8Array) return Buffer.from(value).toString('hex');\n if (Array.isArray(value)) return Buffer.from(value).toString('hex');\n throw authorityError('UNSUPPORTED_BACKEND', 'decode-file-identity');\n}\n\nexport function windowsLastErrorCode(api: IWindowsApi): string {\n return `WIN32_${api.getLastError()}`;\n}\n\nexport function queryWindowsIdentity(\n api: IWindowsApi,\n handle: TNativeHandle,\n operation: string,\n): IWindowsIdentity {\n const info: IWindowsFileIdInfo = {};\n if (!api.getFileIdInfo(handle, FILE_ID_INFO_CLASS, info, koffi.sizeof(api.fileIdInfoType))) {\n throw authorityError('UNSUPPORTED_BACKEND', operation, {\n hostCode: windowsLastErrorCode(api),\n });\n }\n return {\n volume: BigInt(info.VolumeSerialNumber ?? ZERO_SIZE),\n fileId: bytesToHex(info.FileId),\n };\n}\n\nexport function readWindowsFile(\n api: IWindowsApi,\n handle: TNativeHandle,\n maxBytes: number,\n hooks: IFileAuthorityTestHooks,\n): Uint8Array {\n const before = queryFileSnapshot(api, handle);\n if (before.size > BigInt(maxBytes)) throw authorityError('OVER_BUDGET', 'read-file');\n const expectedBytes = Number(before.size);\n let bytes: Buffer;\n try {\n bytes = Buffer.alloc(expectedBytes);\n } catch {\n throw authorityError('HOST_IO', 'allocate-read-buffer');\n }\n\n const offset = readExpectedBytes(api, handle, bytes, hooks);\n const probeCount = [0];\n if (!api.readFile(handle, Buffer.alloc(1), 1, probeCount, null)) {\n throw authorityError('HOST_IO', 'probe-file', { hostCode: windowsLastErrorCode(api) });\n }\n const after = queryFileSnapshot(api, handle);\n if (probeCount[0] !== 0 && expectedBytes >= maxBytes) {\n throw authorityError('OVER_BUDGET', 'read-file');\n }\n if (!isStableRead(before, after, offset, expectedBytes, probeCount[0])) {\n throw authorityError('FILE_CHANGED', 'read-file');\n }\n return bytes;\n}\n\nfunction readExpectedBytes(\n api: IWindowsApi,\n handle: TNativeHandle,\n bytes: Buffer,\n hooks: IFileAuthorityTestHooks,\n): number {\n let offset = 0;\n while (offset < bytes.length) {\n const length = Math.min(CHUNK_BYTES, bytes.length - offset);\n const count = [0];\n if (!api.readFile(handle, bytes.subarray(offset, offset + length), length, count, null)) {\n throw authorityError('HOST_IO', 'read-file', { hostCode: windowsLastErrorCode(api) });\n }\n if (count[0] === 0) break;\n offset += count[0];\n hooks.afterReadChunk?.(offset);\n }\n return offset;\n}\n\nfunction queryFileSnapshot(api: IWindowsApi, handle: TNativeHandle): IWindowsFileSnapshot {\n const identity = queryWindowsIdentity(api, handle, 'inspect-file');\n const size = [ZERO_SIZE];\n if (!api.getFileSizeEx(handle, size)) {\n throw authorityError('HOST_IO', 'inspect-file', { hostCode: windowsLastErrorCode(api) });\n }\n return { ...identity, size: BigInt(size[0]) };\n}\n\nfunction isStableRead(\n before: IWindowsFileSnapshot,\n after: IWindowsFileSnapshot,\n actualBytes: number,\n expectedBytes: number,\n probeBytes: number,\n): boolean {\n return (\n actualBytes === expectedBytes &&\n probeBytes === 0 &&\n before.volume === after.volume &&\n before.fileId === after.fileId &&\n before.size === after.size\n );\n}\n","type TNtOpenFailureKind = 'missing' | 'unsafe' | 'host-io';\n\nexport interface INtOpenFailure {\n readonly kind: TNtOpenFailureKind;\n readonly hostCode: string;\n}\n\nconst HEX_RADIX = 16;\nconst STATUS_NO_SUCH_FILE = 0xc000000f;\nconst STATUS_OBJECT_NAME_NOT_FOUND = 0xc0000034;\nconst STATUS_OBJECT_PATH_NOT_FOUND = 0xc000003a;\nconst STATUS_STOPPED_ON_SYMLINK = 0x8000002d;\nconst STATUS_FILE_IS_A_DIRECTORY = 0xc00000ba;\nconst STATUS_NOT_A_DIRECTORY = 0xc0000103;\nconst STATUS_REPARSE_POINT_ENCOUNTERED = 0xc0000279;\nconst STATUS_IO_REPARSE_TAG_NOT_HANDLED = 0xc000050b;\n\nconst MISSING_STATUSES = new Set([\n STATUS_NO_SUCH_FILE,\n STATUS_OBJECT_NAME_NOT_FOUND,\n STATUS_OBJECT_PATH_NOT_FOUND,\n]);\nconst UNSAFE_STATUSES = new Set([\n STATUS_STOPPED_ON_SYMLINK,\n STATUS_FILE_IS_A_DIRECTORY,\n STATUS_NOT_A_DIRECTORY,\n STATUS_REPARSE_POINT_ENCOUNTERED,\n STATUS_IO_REPARSE_TAG_NOT_HANDLED,\n]);\n\nexport function classifyNtOpenFailure(status: number): INtOpenFailure {\n const normalized = status >>> 0;\n return {\n kind: MISSING_STATUSES.has(normalized)\n ? 'missing'\n : UNSAFE_STATUSES.has(normalized)\n ? 'unsafe'\n : 'host-io',\n hostCode: `NTSTATUS_0x${normalized.toString(HEX_RADIX)}`,\n };\n}\n","import * as koffi from 'koffi';\n\nimport { authorityError } from './contracts.js';\nimport {\n getWindowsApi,\n type IWindowsApi,\n type IWindowsAttributeTagInfo,\n type IWindowsIoStatus,\n type IWindowsObjectAttributes,\n type IWindowsStandardInfo,\n type TNativeHandle,\n} from './windows-native-api.js';\nimport {\n queryWindowsIdentity,\n readWindowsFile,\n windowsLastErrorCode,\n type IWindowsIdentity,\n} from './windows-native-read.js';\nimport { classifyNtOpenFailure } from './windows-native-status.js';\n\nimport type { IFileAuthorityTestHooks } from './host-backend.js';\n\nexport type { IWindowsIdentity } from './windows-native-read.js';\n\nconst GENERIC_READ = 0x80000000;\nconst FILE_READ_DATA = 0x0001;\nconst FILE_LIST_DIRECTORY = 0x0001;\nconst FILE_READ_ATTRIBUTES = 0x0080;\nconst SYNCHRONIZE = 0x00100000;\nconst FILE_SHARE_READ = 0x00000001;\nconst FILE_SHARE_WRITE = 0x00000002;\nconst FILE_SHARE_DELETE = 0x00000004;\nconst OPEN_EXISTING = 3;\nconst FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400;\nconst FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;\nconst FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;\nconst FILE_OPEN = 1;\nconst FILE_DIRECTORY_FILE = 0x00000001;\nconst FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020;\nconst FILE_NON_DIRECTORY_FILE = 0x00000040;\nconst FILE_OPEN_REPARSE_POINT = 0x00200000;\nconst OBJ_CASE_INSENSITIVE = 0x00000040;\nconst OBJ_DONT_REPARSE = 0x00001000;\nconst FILE_STANDARD_INFO_CLASS = 1;\nconst FILE_ATTRIBUTE_TAG_INFO_CLASS = 9;\nconst HANDLE_WIDTH_BITS = 64;\nconst INVALID_HANDLE_VALUE = BigInt('-1');\n\nfunction isInvalidHandle(handle: TNativeHandle): boolean {\n return handle === null || BigInt.asIntN(HANDLE_WIDTH_BITS, handle) === INVALID_HANDLE_VALUE;\n}\n\n/** Raw Windows handle operations. Native HANDLE and Koffi values stay inside this module. */\nexport class WindowsNativeAuthority {\n private readonly api: IWindowsApi;\n\n constructor() {\n try {\n this.api = getWindowsApi();\n } catch {\n throw authorityError('UNSUPPORTED_BACKEND', 'load-windows-backend');\n }\n }\n\n openRoot(rootDirectory: string): TNativeHandle {\n const handle = this.api.createFileW(\n rootDirectory,\n GENERIC_READ,\n FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,\n null,\n OPEN_EXISTING,\n FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,\n null,\n );\n if (isInvalidHandle(handle)) {\n throw authorityError('HOST_IO', 'open-root', { hostCode: this.lastErrorCode() });\n }\n try {\n this.assertSafeHandle(handle, true, 'inspect-root');\n return handle;\n } catch (error) {\n this.close(handle);\n throw error;\n }\n }\n\n openRelative(\n parentHandle: TNativeHandle,\n segment: string,\n final: boolean,\n segmentIndex: number,\n ): TNativeHandle | undefined {\n const { handle, status } = this.issueRelativeOpen(parentHandle, segment, final);\n if (status >= 0) {\n if (isInvalidHandle(handle)) throw authorityError('HOST_IO', 'open-relative');\n try {\n this.assertSafeHandle(\n handle,\n !final,\n final ? 'inspect-file' : 'inspect-directory',\n segmentIndex,\n );\n return handle;\n } catch (error) {\n this.close(handle);\n throw error;\n }\n }\n if (!isInvalidHandle(handle)) this.close(handle);\n const failure = classifyNtOpenFailure(status);\n if (failure.kind === 'missing') return undefined;\n const details = { segmentIndex, hostCode: failure.hostCode };\n if (failure.kind === 'unsafe') {\n throw authorityError('UNSAFE_ENTRY', final ? 'open-file' : 'open-directory', details);\n }\n throw authorityError('HOST_IO', final ? 'open-file' : 'open-directory', details);\n }\n\n readFile(handle: TNativeHandle, maxBytes: number, hooks: IFileAuthorityTestHooks): Uint8Array {\n return readWindowsFile(this.api, handle, maxBytes, hooks);\n }\n\n queryIdentity(handle: TNativeHandle, operation: string): IWindowsIdentity {\n return queryWindowsIdentity(this.api, handle, operation);\n }\n\n close(handle: TNativeHandle): void {\n try {\n if (this.api.ntClose(handle) >= 0) return;\n } catch {\n // allow-fallback: CloseHandle is the native cleanup fallback after NtClose itself throws\n }\n try {\n this.api.closeHandle(handle);\n } catch {\n // allow-fallback: close failure cannot restore authority or make a later read safe\n }\n }\n\n private issueRelativeOpen(\n parentHandle: TNativeHandle,\n segment: string,\n final: boolean,\n ): { handle: TNativeHandle; status: number } {\n const unicodeName = koffi.alloc(this.api.unicodeStringType, 1);\n try {\n koffi.encode(unicodeName, this.api.unicodeStringType, {\n Length: Buffer.byteLength(segment, 'utf16le'),\n MaximumLength: Buffer.byteLength(`${segment}\\0`, 'utf16le'),\n Buffer: segment,\n });\n const attributes: IWindowsObjectAttributes = {\n Length: koffi.sizeof(this.api.objectAttributesType),\n RootDirectory: parentHandle,\n ObjectName: unicodeName,\n Attributes: OBJ_CASE_INSENSITIVE | OBJ_DONT_REPARSE,\n SecurityDescriptor: null,\n SecurityQualityOfService: null,\n };\n const output: TNativeHandle[] = [null];\n const ioStatus: IWindowsIoStatus = { Status: 0, Information: 0 };\n // A final read cannot verify same-size in-place rewrites from identity and size alone.\n // Deny write sharing for that handle so existing and new writers cannot race the read.\n const shareAccess = final\n ? FILE_SHARE_READ | FILE_SHARE_DELETE\n : FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;\n const status = this.api.ntCreateFile(\n output,\n (final ? FILE_READ_DATA : FILE_LIST_DIRECTORY) | FILE_READ_ATTRIBUTES | SYNCHRONIZE,\n attributes,\n ioStatus,\n null,\n 0,\n shareAccess,\n FILE_OPEN,\n (final ? FILE_NON_DIRECTORY_FILE : FILE_DIRECTORY_FILE) |\n FILE_SYNCHRONOUS_IO_NONALERT |\n FILE_OPEN_REPARSE_POINT,\n null,\n 0,\n );\n return { handle: output[0], status };\n } finally {\n koffi.free(unicodeName);\n }\n }\n\n private assertSafeHandle(\n handle: TNativeHandle,\n directory: boolean,\n operation: string,\n segmentIndex?: number,\n ): void {\n const attributes: IWindowsAttributeTagInfo = {};\n const standard: IWindowsStandardInfo = {};\n if (\n !this.api.getAttributeTagInfo(\n handle,\n FILE_ATTRIBUTE_TAG_INFO_CLASS,\n attributes,\n koffi.sizeof(this.api.attributeTagInfoType),\n ) ||\n !this.api.getStandardInfo(\n handle,\n FILE_STANDARD_INFO_CLASS,\n standard,\n koffi.sizeof(this.api.standardInfoType),\n )\n ) {\n throw authorityError('HOST_IO', operation, { hostCode: this.lastErrorCode() });\n }\n const unsafe =\n ((attributes.FileAttributes ?? 0) & FILE_ATTRIBUTE_REPARSE_POINT) !== 0 ||\n Boolean(standard.Directory) !== directory;\n if (unsafe) {\n throw authorityError(\n 'UNSAFE_ENTRY',\n operation,\n segmentIndex === undefined ? {} : { segmentIndex },\n );\n }\n }\n\n private lastErrorCode(): string {\n return windowsLastErrorCode(this.api);\n }\n}\n","import { authorityError, StableFileAuthorityError } from './contracts.js';\nimport { WindowsNativeAuthority, type IWindowsIdentity } from './windows-native-authority.js';\n\nimport type { IFileAuthorityTestHooks, IStableFileHostBackend } from './host-backend.js';\nimport type { TNativeHandle } from './windows-native-api.js';\n\n/** Windows implementation; native HANDLE values never leave the native authority module. */\nexport class WindowsStableFileHostBackend implements IStableFileHostBackend {\n private readonly native = new WindowsNativeAuthority();\n private readonly rootIdentity: IWindowsIdentity;\n private rootHandle: TNativeHandle | undefined;\n\n constructor(rootDirectory: string) {\n try {\n const handle = this.native.openRoot(rootDirectory);\n this.rootHandle = handle;\n this.rootIdentity = this.native.queryIdentity(handle, 'inspect-root');\n } catch (error) {\n if (this.rootHandle !== undefined) {\n this.native.close(this.rootHandle);\n this.rootHandle = undefined;\n }\n if (error instanceof StableFileAuthorityError) throw error;\n throw authorityError('UNSUPPORTED_BACKEND', 'load-windows-backend');\n }\n }\n\n readBytes(\n relativeSegments: readonly string[],\n maxBytes: number,\n hooks: IFileAuthorityTestHooks,\n ): Uint8Array | undefined {\n const rootHandle = this.rootHandle;\n if (rootHandle === undefined) throw authorityError('AUTHORITY_CLOSED', 'read');\n this.assertRootIdentity(rootHandle);\n\n let parentHandle = rootHandle;\n let ownsParent = false;\n let fileHandle: TNativeHandle | undefined;\n try {\n for (let index = 0; index < relativeSegments.length; index += 1) {\n const final = index === relativeSegments.length - 1;\n const handle = this.native.openRelative(\n parentHandle,\n relativeSegments[index],\n final,\n index,\n );\n if (handle === undefined) return undefined;\n if (final) {\n fileHandle = handle;\n break;\n }\n if (ownsParent) this.native.close(parentHandle);\n parentHandle = handle;\n ownsParent = true;\n hooks.afterDirectoryOpened?.(index);\n }\n\n if (fileHandle === undefined) throw authorityError('HOST_IO', 'open-file');\n const bytes = this.native.readFile(fileHandle, maxBytes, hooks);\n this.assertRootIdentity(rootHandle);\n return bytes;\n } catch (error) {\n if (error instanceof StableFileAuthorityError) throw error;\n throw authorityError('HOST_IO', 'read');\n } finally {\n if (fileHandle !== undefined) this.native.close(fileHandle);\n if (ownsParent) this.native.close(parentHandle);\n }\n }\n\n close(): void {\n const handle = this.rootHandle;\n if (handle === undefined) return;\n this.rootHandle = undefined;\n this.native.close(handle);\n }\n\n private assertRootIdentity(handle: TNativeHandle): void {\n const current = this.native.queryIdentity(handle, 'inspect-root');\n if (\n current.volume !== this.rootIdentity.volume ||\n current.fileId !== this.rootIdentity.fileId\n ) {\n throw authorityError('ROOT_CHANGED', 'inspect-root');\n }\n }\n}\n","import { resolve } from 'node:path';\n\nimport {\n authorityError,\n StableFileAuthorityError,\n type IStableRootedFileReader,\n} from './contracts.js';\nimport { PosixStableFileHostBackend } from './posix-backend.js';\nimport { WindowsStableFileHostBackend } from './windows-backend.js';\n\nimport type { IFileAuthorityTestHooks, IStableFileHostBackend } from './host-backend.js';\n\nexport type { IFileAuthorityTestHooks } from './host-backend.js';\n\nconst SUPPORTED_HOSTS = new Set([\n 'darwin-arm64',\n 'darwin-x64',\n 'linux-arm64',\n 'linux-x64',\n 'win32-x64',\n]);\n\nconst finalizer = new FinalizationRegistry<IStableFileHostBackend>((backend) => {\n backend.close();\n});\n\nfunction validateRoot(rootDirectory: string): string {\n if (\n typeof rootDirectory !== 'string' ||\n rootDirectory.trim().length === 0 ||\n rootDirectory.includes('\\0')\n ) {\n throw authorityError('INVALID_PATH', 'validate-root');\n }\n return resolve(rootDirectory);\n}\n\nfunction validateReadInput(relativeSegments: readonly string[], maxBytes: number): void {\n if (!Array.isArray(relativeSegments) || relativeSegments.length === 0) {\n throw authorityError('INVALID_PATH', 'validate-segments');\n }\n for (let index = 0; index < relativeSegments.length; index += 1) {\n const segment = relativeSegments[index];\n if (\n typeof segment !== 'string' ||\n segment.length === 0 ||\n segment === '.' ||\n segment === '..' ||\n segment.includes('\\0') ||\n segment.includes('/') ||\n segment.includes('\\\\')\n ) {\n throw authorityError('INVALID_PATH', 'validate-segments', { segmentIndex: index });\n }\n }\n if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {\n throw authorityError('INVALID_PATH', 'validate-budget');\n }\n}\n\nfunction createHostBackend(rootDirectory: string): IStableFileHostBackend {\n const host = `${process.platform}-${process.arch}`;\n if (!SUPPORTED_HOSTS.has(host)) {\n throw authorityError('UNSUPPORTED_BACKEND', 'select-backend', { hostCode: host });\n }\n if (process.platform === 'win32') return new WindowsStableFileHostBackend(rootDirectory);\n return new PosixStableFileHostBackend(rootDirectory);\n}\n\nclass StableRootedFileReader implements IStableRootedFileReader {\n private backend: IStableFileHostBackend | undefined;\n private readonly finalizerToken = {};\n\n constructor(\n rootDirectory: string,\n private readonly hooks: IFileAuthorityTestHooks,\n ) {\n const backend = createHostBackend(validateRoot(rootDirectory));\n this.backend = backend;\n finalizer.register(this, backend, this.finalizerToken);\n }\n\n readBytes(relativeSegments: readonly string[], maxBytes: number): Uint8Array | undefined {\n const backend = this.backend;\n if (backend === undefined) throw authorityError('AUTHORITY_CLOSED', 'read');\n validateReadInput(relativeSegments, maxBytes);\n try {\n return backend.readBytes(relativeSegments, maxBytes, this.hooks);\n } catch (error) {\n if (error instanceof StableFileAuthorityError) throw error;\n throw authorityError('HOST_IO', 'read');\n }\n }\n\n close(): void {\n const backend = this.backend;\n if (backend === undefined) return;\n this.backend = undefined;\n finalizer.unregister(this.finalizerToken);\n try {\n backend.close();\n } catch (error) {\n if (error instanceof StableFileAuthorityError) throw error;\n throw authorityError('HOST_IO', 'close');\n }\n }\n\n [Symbol.dispose](): void {\n this.close();\n }\n}\n\n/** Create a stable, opaque read authority rooted at the currently opened directory identity. */\nexport function createStableRootedFileReader(rootDirectory: string): IStableRootedFileReader {\n return new StableRootedFileReader(rootDirectory, {});\n}\n\n/** Internal-only deterministic seams used by native replacement tests. */\nexport function createStableRootedFileReaderForTest(\n rootDirectory: string,\n hooks: IFileAuthorityTestHooks,\n): IStableRootedFileReader {\n return new StableRootedFileReader(rootDirectory, hooks);\n}\n"],"mappings":"4IAgBA,MAAM,EAA0E,CAC9E,aAAc,yDACd,aAAc,2EACd,oBAAqB,4DACrB,iBAAkB,8CAClB,aAAc,2DACd,aAAc,iEACd,YAAa,6CACb,QAAS,sEACX,EAGA,IAAa,EAAb,cAA8C,KAAM,CAClD,KACA,QAEA,YAAY,EAAqC,EAA2C,CAC1F,MAAM,EAAe,EAAK,EAC1B,KAAK,KAAO,2BACZ,KAAK,KAAO,EACZ,KAAK,QAAU,OAAO,OAAO,CAAE,GAAG,CAAQ,CAAC,CAC7C,CACF,EASA,SAAgB,EACd,EACA,EACA,EAA+D,CAAC,EACtC,CAC1B,OAAO,IAAI,EAAyB,EAAM,CAAE,YAAW,GAAG,CAAQ,CAAC,CACrE,CC1BA,SAAS,EAAS,EAAuC,CACvD,MAAO,CAAE,OAAQ,EAAS,IAAK,MAAO,EAAS,IAAK,KAAM,EAAS,IAAK,CAC1E,CAEA,SAAS,EAAS,EAA2C,CAC3D,MAAO,CACL,GAAG,EAAS,CAAQ,EACpB,KAAM,EAAS,KACf,SAAU,EAAS,QACnB,QAAS,EAAS,OACpB,CACF,CAEA,SAAS,EAAa,EAAsB,EAAgC,CAC1E,OAAO,EAAK,SAAW,EAAM,QAAU,EAAK,QAAU,EAAM,OAAS,EAAK,OAAS,EAAM,IAC3F,CAEA,SAAS,EAAa,EAA0B,EAAoC,CAClF,OACE,EAAa,EAAM,CAAK,GACxB,EAAK,OAAS,EAAM,MACpB,EAAK,WAAa,EAAM,UACxB,EAAK,UAAY,EAAM,OAE3B,CAEA,SAAS,EAAS,EAAuB,CACvC,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,EAAM,GAAG,KAAK,EACvD,GAAI,IAAU,EAAO,OAAO,EAE9B,MAAO,SAAS,GAClB,CAEA,SAAS,EAAM,EAA4B,CACzC,IAAM,EACH,EAAiE,YACjE,QAAQ,WAAa,SAAW,SAAuB,QAC1D,OACE,EAAU,SACV,EAAU,WACV,EAAU,WACV,GACC,EAAY,EAAU,YAAc,EAEzC,CAEA,SAAS,EAAU,EAAoB,EAAgC,CACrE,GAAI,CACF,OAAO,EAAU,EAAY,CAAE,OAAQ,EAAK,CAAC,CAC/C,OAAS,EAAO,CACd,IAAM,EAAO,EAAc,OAAO,GAAU,SAAW,EAAQ,IAAI,EACnE,MAAM,EAAe,UAAW,EAAW,IAAS,IAAA,GAAY,CAAC,EAAI,CAAE,SAAU,CAAK,CAAC,CACzF,CACF,CAEA,SAAS,EAAc,EAA0C,CAC3D,SAAU,MAAQ,EAAE,SAAU,IAClC,OAAO,OAAO,EAAM,MAAS,SAAW,EAAM,KAAO,IAAA,EACvD,CAEA,IAAI,EAEJ,MAAMC,EAA+B,CAAC,EAOtC,SAAS,GAAkD,CACzD,GAAI,CAAC,EAAM,CACT,IAAM,EAAU,EAAM,KACpB,QAAQ,WAAa,SAAW,6BAA+B,WACjE,EACA,EAAkB,KAAK,CAAO,EAC9B,EAAO,CACL,UACA,KAAM,EAAQ,KAAK,uCAAuC,EAC1D,OAAQ,EAAQ,KAAK,iDAAiD,CACxE,CACF,CACA,OAAO,CACT,CAGA,IAAa,EAAb,KAA0E,CACxE,OACA,aACA,eAEA,YAAY,EAAuB,CACjC,GAAI,CACF,GAAM,CAAE,OAAM,UAAW,EAAc,EACvC,KAAK,OAAS,EACd,IAAM,EAAa,EAAK,EAAe,EAAM,EAAI,CAAC,EAC9C,EAAa,GAAG,KAAK,iBAAiB,EAAM,MAAM,EAAG,WAAW,EACpE,KAAK,eAAiB,EACtB,IAAM,EAAW,EAAU,EAAY,cAAc,EACrD,GAAI,CAAC,EAAS,YAAY,EAAG,MAAM,EAAe,eAAgB,WAAW,EAC7E,KAAK,aAAe,EAAS,CAAQ,CACvC,OAAS,EAAO,CAMd,MALI,KAAK,iBAAmB,IAAA,KAC1B,KAAK,gBAAgB,KAAK,cAAc,EACxC,KAAK,eAAiB,IAAA,IAEpB,aAAiB,EAAgC,EAC/C,EAAe,sBAAuB,oBAAoB,CAClE,CACF,CAEA,UACE,EACA,EACA,EACwB,CACxB,IAAM,EAAiB,KAAK,eAC5B,GAAI,IAAmB,IAAA,GAAW,MAAM,EAAe,mBAAoB,MAAM,EACjF,KAAK,mBAAmB,CAAc,EAEtC,IAAI,EAAmB,EACnB,EAAa,GACb,EACJ,GAAI,CACF,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAiB,OAAQ,GAAS,EAAG,CAC/D,IAAM,EAAQ,IAAU,EAAiB,OAAS,EAC5C,EAAa,KAAK,OAAO,EAAkB,EAAiB,GAAQ,EAAM,CAAC,CAAK,CAAC,EACvF,GAAI,EAAa,EAAG,CAClB,IAAM,EAAQ,EAAM,MAAM,EAC1B,GAAI,IAAU,EAAM,GAAG,MAAM,OAAQ,OACrC,KAAK,iBAAiB,EAAO,EAAQ,YAAc,iBAAkB,CAAK,CAC5E,CAEA,GAAI,EAAO,CACT,EAAiB,EACjB,KACF,CAGA,GAAI,CADa,EAAU,EAAY,mBAC3B,CAAC,CAAC,YAAY,EAExB,MADA,KAAK,gBAAgB,CAAU,EACzB,EAAe,eAAgB,oBAAqB,CAAE,aAAc,CAAM,CAAC,EAE/E,GAAY,KAAK,gBAAgB,CAAgB,EACrD,EAAmB,EACnB,EAAa,GACb,EAAM,uBAAuB,CAAK,CACpC,CAEA,GAAI,IAAmB,IAAA,GAAW,MAAM,EAAe,UAAW,WAAW,EAC7E,IAAM,EAAQ,KAAK,SAAS,EAAgB,EAAU,CAAK,EAE3D,OADA,KAAK,mBAAmB,CAAc,EAC/B,CACT,OAAS,EAAO,CACd,GAAI,aAAiB,EAA0B,MAAM,EACrD,IAAM,EAAO,EAAc,OAAO,GAAU,SAAW,EAAQ,IAAI,EACnE,MAAM,EAAe,UAAW,OAAQ,IAAS,IAAA,GAAY,CAAC,EAAI,CAAE,SAAU,CAAK,CAAC,CACtF,QAAU,CACR,KAAK,wBAAwB,CAAc,EAC3C,KAAK,wBAAwB,EAAa,EAAmB,IAAA,EAAS,CACxE,CACF,CAEA,OAAc,CACZ,IAAM,EAAa,KAAK,eACpB,IAAe,IAAA,KACnB,KAAK,eAAiB,IAAA,GACtB,KAAK,gBAAgB,CAAU,EACjC,CAEA,SACE,EACA,EACA,EACY,CACZ,IAAM,EAAiB,EAAU,EAAY,cAAc,EAC3D,GAAI,CAAC,EAAe,OAAO,EAAG,MAAM,EAAe,eAAgB,cAAc,EACjF,GAAI,EAAe,KAAO,OAAO,CAAQ,EAAG,MAAM,EAAe,cAAe,WAAW,EAE3F,IAAM,EAAgB,OAAO,EAAe,IAAI,EAC5C,EACJ,GAAI,CACF,EAAQ,OAAO,MAAM,CAAa,CACpC,MAAQ,CACN,MAAM,EAAe,UAAW,sBAAsB,CACxD,CAEA,IAAI,EAAS,EACb,KAAO,EAAS,GAAe,CAC7B,IAAM,EAAQ,EACZ,EACA,EACA,EACA,KAAK,IAAID,QAAa,EAAgB,CAAM,EAC5C,CACF,EACA,GAAI,IAAU,EAAG,MACjB,GAAU,EACV,EAAM,iBAAiB,CAAM,CAC/B,CAGA,IAAM,EAAO,EAAS,EADR,OAAO,MAAM,CACW,EAAG,EAAG,EAAG,CAAa,IAAM,EAC5D,EAAgB,EAAU,EAAY,gBAAgB,EAC5D,GAAI,GAAQ,GAAiB,EAAU,MAAM,EAAe,cAAe,WAAW,EACtF,GACE,IAAW,GACX,GACA,CAAC,EAAa,EAAS,CAAc,EAAG,EAAS,CAAa,CAAC,EAE/D,MAAM,EAAe,eAAgB,WAAW,EAElD,OAAO,CACT,CAEA,mBAA2B,EAA0B,CACnD,IAAM,EAAW,EAAU,EAAY,cAAc,EACrD,GAAI,CAAC,EAAS,YAAY,GAAK,CAAC,EAAa,KAAK,aAAc,EAAS,CAAQ,CAAC,EAChF,MAAM,EAAe,eAAgB,cAAc,CAEvD,CAEA,iBAAyB,EAAe,EAAmB,EAA8B,CACvF,IAAM,EAAO,EAAS,CAAK,EACrB,EACJ,IAAiB,IAAA,GAAY,CAAE,SAAU,CAAK,EAAI,CAAE,SAAU,EAAM,cAAa,EAQnF,MANE,IAAU,EAAM,GAAG,MAAM,OACzB,IAAU,EAAM,GAAG,MAAM,SACzB,IAAU,EAAM,GAAG,MAAM,OAEnB,EAAe,eAAgB,EAAW,CAAO,EAEnD,EAAe,UAAW,EAAW,CAAO,CACpD,CAEA,gBAAwB,EAA0B,CAChD,GAAI,CACF,EAAU,CAAU,CACtB,MAAQ,CAER,CACF,CAEA,wBAAgC,EAAsC,CAChE,IAAe,IAAA,IAAW,KAAK,gBAAgB,CAAU,CAC/D,CACF,EC7KA,SAAS,GAAoC,CAC3C,IAAM,EAAa,EAAM,QAAQ,yBAA0B,EAAM,OAAO,CAAC,EACnE,EAAoB,EAAM,OAAO,sBAAuB,CAC5D,OAAQ,WACR,cAAe,WACf,OAAQ,EAAM,QAAQ,UAAU,CAClC,CAAC,EASD,MAAO,CACL,aACA,oBACA,qBAX2B,EAAM,OAAO,yBAA0B,CAClE,OAAQ,WACR,cAAe,EACf,WAAY,EAAM,QAAQ,CAAiB,EAC3C,WAAY,WACZ,mBAAoB,SACpB,yBAA0B,QAC5B,CAIqB,EACnB,kBAAmB,EAAM,OAAO,sBAAuB,CACrD,OAAQ,WACR,YAAa,WACf,CAAC,EACD,qBAAsB,EAAM,OAAO,6BAA8B,CAC/D,eAAgB,WAChB,WAAY,UACd,CAAC,EACD,eAAgB,EAAM,OAAO,mBAAoB,CAC/C,mBAAoB,WACpB,OAAQ,EAAM,MAAM,UAAW,GAAe,OAAO,CACvD,CAAC,EACD,iBAAkB,EAAM,OAAO,yBAA0B,CACvD,eAAgB,UAChB,UAAW,UACX,cAAe,WACf,cAAe,UACf,UAAW,SACb,CAAC,CACH,CACF,CAEA,SAAS,EACP,EACA,EAWA,CACA,MAAO,CACL,YAAa,EAAQ,KAAK,cAAe,EAAM,WAAY,CACzD,EAAM,QAAQ,UAAU,EACxB,WACA,WACA,SACA,WACA,WACA,EAAM,UACR,CAAC,EACD,YAAa,EAAQ,KAAK,cAAe,OAAQ,CAAC,EAAM,UAAU,CAAC,EACnE,GAAG,EAAyB,EAAS,CAAK,EAC1C,cAAe,EAAQ,KAAK,gBAAiB,OAAQ,CACnD,EAAM,WACN,EAAM,IAAI,EAAM,QAAQ,SAAS,CAAC,CACpC,CAAC,EACD,SAAU,EAAQ,KAAK,WAAY,OAAQ,CACzC,EAAM,WACN,EAAM,IAAI,EAAM,QAAQ,SAAS,CAAC,EAClC,WACA,EAAM,IAAI,EAAM,QAAQ,UAAU,CAAC,EACnC,QACF,CAAC,EACD,aAAc,EAAQ,KAAK,eAAgB,WAAY,CAAC,CAAC,CAC3D,CACF,CAEA,SAAS,EACP,EACA,EACgF,CAChF,IAAM,EAAQ,GACZ,EAAQ,KAAK,+BAAgC,OAAQ,CACnD,EAAM,WACN,UACA,EAAM,IAAI,EAAM,QAAQ,CAAU,CAAC,EACnC,UACF,CAAC,EACH,MAAO,CACL,oBAAqB,EAAK,EAAM,oBAAoB,EACpD,cAAe,EAAK,EAAM,cAAc,EACxC,gBAAiB,EAAK,EAAM,gBAAgB,CAC9C,CACF,CAEA,SAAS,EACP,EACA,EAC+C,CAC/C,MAAO,CACL,aAAc,EAAQ,KAAK,eAAgB,UAAW,CACpD,EAAM,IAAI,EAAM,QAAQ,EAAM,UAAU,CAAC,EACzC,WACA,EAAM,QAAQ,EAAM,oBAAoB,EACxC,EAAM,IAAI,EAAM,QAAQ,EAAM,iBAAiB,CAAC,EAChD,SACA,WACA,WACA,WACA,WACA,SACA,UACF,CAAC,EACD,QAAS,EAAQ,KAAK,UAAW,UAAW,CAAC,EAAM,UAAU,CAAC,CAChE,CACF,CAEA,IAAI,EAKJ,MAAM,EAA+B,CAAC,EAEtC,SAAgB,GAA6B,CAC3C,GAAI,IAAc,IAAA,GAAW,OAAO,EACpC,IAAM,EAAQ,EAAmB,EAC3B,EAAW,EAAM,KAAK,cAAc,EAC1C,EAAkB,KAAK,CAAQ,EAC/B,IAAM,EAAQ,EAAM,KAAK,WAAW,EAOpC,OANA,EAAkB,KAAK,CAAK,EAC5B,EAAY,CACV,GAAG,EACH,GAAG,EAAa,EAAU,CAAK,EAC/B,GAAG,EAAU,EAAO,CAAK,CAC3B,EACO,CACT,CClOA,MAEM,EAAY,OAAO,GAAG,EAE5B,SAAS,EAAW,EAAkD,CAEpE,GADI,aAAiB,YACjB,MAAM,QAAQ,CAAK,EAAG,OAAO,OAAO,KAAK,CAAK,CAAC,CAAC,SAAS,KAAK,EAClE,MAAM,EAAe,sBAAuB,sBAAsB,CACpE,CAEA,SAAgB,EAAqB,EAA0B,CAC7D,MAAO,SAAS,EAAI,aAAa,GACnC,CAEA,SAAgB,EACd,EACA,EACA,EACkB,CAClB,IAAM,EAA2B,CAAC,EAClC,GAAI,CAAC,EAAI,cAAc,EAAQ,GAAoB,EAAM,EAAM,OAAO,EAAI,cAAc,CAAC,EACvF,MAAM,EAAe,sBAAuB,EAAW,CACrD,SAAU,EAAqB,CAAG,CACpC,CAAC,EAEH,MAAO,CACL,OAAQ,OAAO,EAAK,oBAAsB,CAAS,EACnD,OAAQ,EAAW,EAAK,MAAM,CAChC,CACF,CAEA,SAAgB,EACd,EACA,EACA,EACA,EACY,CACZ,IAAM,EAAS,EAAkB,EAAK,CAAM,EAC5C,GAAI,EAAO,KAAO,OAAO,CAAQ,EAAG,MAAM,EAAe,cAAe,WAAW,EACnF,IAAM,EAAgB,OAAO,EAAO,IAAI,EACpC,EACJ,GAAI,CACF,EAAQ,OAAO,MAAM,CAAa,CACpC,MAAQ,CACN,MAAM,EAAe,UAAW,sBAAsB,CACxD,CAEA,IAAM,EAAS,EAAkB,EAAK,EAAQ,EAAO,CAAK,EACpD,EAAa,CAAC,CAAC,EACrB,GAAI,CAAC,EAAI,SAAS,EAAQ,OAAO,MAAM,CAAC,EAAG,EAAG,EAAY,IAAI,EAC5D,MAAM,EAAe,UAAW,aAAc,CAAE,SAAU,EAAqB,CAAG,CAAE,CAAC,EAEvF,IAAM,EAAQ,EAAkB,EAAK,CAAM,EAC3C,GAAI,EAAW,KAAO,GAAK,GAAiB,EAC1C,MAAM,EAAe,cAAe,WAAW,EAEjD,GAAI,CAAC,EAAa,EAAQ,EAAO,EAAQ,EAAe,EAAW,EAAE,EACnE,MAAM,EAAe,eAAgB,WAAW,EAElD,OAAO,CACT,CAEA,SAAS,EACP,EACA,EACA,EACA,EACQ,CACR,IAAI,EAAS,EACb,KAAO,EAAS,EAAM,QAAQ,CAC5B,IAAM,EAAS,KAAK,IAAI,QAAa,EAAM,OAAS,CAAM,EACpD,EAAQ,CAAC,CAAC,EAChB,GAAI,CAAC,EAAI,SAAS,EAAQ,EAAM,SAAS,EAAQ,EAAS,CAAM,EAAG,EAAQ,EAAO,IAAI,EACpF,MAAM,EAAe,UAAW,YAAa,CAAE,SAAU,EAAqB,CAAG,CAAE,CAAC,EAEtF,GAAI,EAAM,KAAO,EAAG,MACpB,GAAU,EAAM,GAChB,EAAM,iBAAiB,CAAM,CAC/B,CACA,OAAO,CACT,CAEA,SAAS,EAAkB,EAAkB,EAA6C,CACxF,IAAM,EAAW,EAAqB,EAAK,EAAQ,cAAc,EAC3D,EAAO,CAAC,CAAS,EACvB,GAAI,CAAC,EAAI,cAAc,EAAQ,CAAI,EACjC,MAAM,EAAe,UAAW,eAAgB,CAAE,SAAU,EAAqB,CAAG,CAAE,CAAC,EAEzF,MAAO,CAAE,GAAG,EAAU,KAAM,OAAO,EAAK,EAAE,CAAE,CAC9C,CAEA,SAAS,EACP,EACA,EACA,EACA,EACA,EACS,CACT,OACE,IAAgB,GAChB,IAAe,GACf,EAAO,SAAW,EAAM,QACxB,EAAO,SAAW,EAAM,QACxB,EAAO,OAAS,EAAM,IAE1B,CClHA,MAUM,EAAmB,IAAI,IAAI,CAC/B,WACA,WACA,UACF,CAAC,EACK,EAAkB,IAAI,IAAI,CAC9B,WACA,WACA,WACA,WACA,UACF,CAAC,EAED,SAAgB,EAAsB,EAAgC,CACpE,IAAM,EAAa,IAAW,EAC9B,MAAO,CACL,KAAM,EAAiB,IAAI,CAAU,EACjC,UACA,EAAgB,IAAI,CAAU,EAC5B,SACA,UACN,SAAU,cAAc,EAAW,SAAS,EAAS,GACvD,CACF,CChBA,MAsBM,EAAuB,OAAO,IAAI,EAExC,SAAS,EAAgB,EAAgC,CACvD,OAAO,IAAW,MAAQ,OAAO,OAAO,GAAmB,CAAM,IAAM,CACzE,CAGA,IAAa,EAAb,KAAoC,CAClC,IAEA,aAAc,CACZ,GAAI,CACF,KAAK,IAAM,EAAc,CAC3B,MAAQ,CACN,MAAM,EAAe,sBAAuB,sBAAsB,CACpE,CACF,CAEA,SAAS,EAAsC,CAC7C,IAAM,EAAS,KAAK,IAAI,YACtB,EACA,WACA,EACA,KACA,EACA,SACA,IACF,EACA,GAAI,EAAgB,CAAM,EACxB,MAAM,EAAe,UAAW,YAAa,CAAE,SAAU,KAAK,cAAc,CAAE,CAAC,EAEjF,GAAI,CAEF,OADA,KAAK,iBAAiB,EAAQ,GAAM,cAAc,EAC3C,CACT,OAAS,EAAO,CAEd,MADA,KAAK,MAAM,CAAM,EACX,CACR,CACF,CAEA,aACE,EACA,EACA,EACA,EAC2B,CAC3B,GAAM,CAAE,SAAQ,UAAW,KAAK,kBAAkB,EAAc,EAAS,CAAK,EAC9E,GAAI,GAAU,EAAG,CACf,GAAI,EAAgB,CAAM,EAAG,MAAM,EAAe,UAAW,eAAe,EAC5E,GAAI,CAOF,OANA,KAAK,iBACH,EACA,CAAC,EACD,EAAQ,eAAiB,oBACzB,CACF,EACO,CACT,OAAS,EAAO,CAEd,MADA,KAAK,MAAM,CAAM,EACX,CACR,CACF,CACK,EAAgB,CAAM,GAAG,KAAK,MAAM,CAAM,EAC/C,IAAM,EAAU,EAAsB,CAAM,EAC5C,GAAI,EAAQ,OAAS,UAAW,OAChC,IAAM,EAAU,CAAE,eAAc,SAAU,EAAQ,QAAS,EAI3D,MAHI,EAAQ,OAAS,SACb,EAAe,eAAgB,EAAQ,YAAc,iBAAkB,CAAO,EAEhF,EAAe,UAAW,EAAQ,YAAc,iBAAkB,CAAO,CACjF,CAEA,SAAS,EAAuB,EAAkB,EAA4C,CAC5F,OAAO,EAAgB,KAAK,IAAK,EAAQ,EAAU,CAAK,CAC1D,CAEA,cAAc,EAAuB,EAAqC,CACxE,OAAO,EAAqB,KAAK,IAAK,EAAQ,CAAS,CACzD,CAEA,MAAM,EAA6B,CACjC,GAAI,CACF,GAAI,KAAK,IAAI,QAAQ,CAAM,GAAK,EAAG,MACrC,MAAQ,CAER,CACA,GAAI,CACF,KAAK,IAAI,YAAY,CAAM,CAC7B,MAAQ,CAER,CACF,CAEA,kBACE,EACA,EACA,EAC2C,CAC3C,IAAM,EAAc,EAAM,MAAM,KAAK,IAAI,kBAAmB,CAAC,EAC7D,GAAI,CACF,EAAM,OAAO,EAAa,KAAK,IAAI,kBAAmB,CACpD,OAAQ,OAAO,WAAW,EAAS,SAAS,EAC5C,cAAe,OAAO,WAAW,GAAG,EAAQ,IAAK,SAAS,EAC1D,OAAQ,CACV,CAAC,EACD,IAAM,EAAuC,CAC3C,OAAQ,EAAM,OAAO,KAAK,IAAI,oBAAoB,EAClD,cAAe,EACf,WAAY,EACZ,WAAY,KACZ,mBAAoB,KACpB,yBAA0B,IAC5B,EACM,EAA0B,CAAC,IAAI,EAC/B,EAA6B,CAAE,OAAQ,EAAG,YAAa,CAAE,EAGzD,EAAc,EAChB,EACA,EACE,EAAS,KAAK,IAAI,aACtB,EAAA,QAEA,EACA,EACA,KACA,EACA,EACA,GACC,EAAQ,GAA0B,GACjC,QAEF,KACA,CACF,EACA,MAAO,CAAE,OAAQ,EAAO,GAAI,QAAO,CACrC,QAAU,CACR,EAAM,KAAK,CAAW,CACxB,CACF,CAEA,iBACE,EACA,EACA,EACA,EACM,CACN,IAAM,EAAuC,CAAC,EACxC,EAAiC,CAAC,EACxC,GACE,CAAC,KAAK,IAAI,oBACR,EACA,EACA,EACA,EAAM,OAAO,KAAK,IAAI,oBAAoB,CAC5C,GACA,CAAC,KAAK,IAAI,gBACR,EACA,EACA,EACA,EAAM,OAAO,KAAK,IAAI,gBAAgB,CACxC,EAEA,MAAM,EAAe,UAAW,EAAW,CAAE,SAAU,KAAK,cAAc,CAAE,CAAC,EAK/E,IAFI,EAAW,gBAAkB,GAAK,MACpC,EAAQ,EAAS,YAAe,EAEhC,MAAM,EACJ,eACA,EACA,IAAiB,IAAA,GAAY,CAAC,EAAI,CAAE,cAAa,CACnD,CAEJ,CAEA,eAAgC,CAC9B,OAAO,EAAqB,KAAK,GAAG,CACtC,CACF,EC3Na,EAAb,KAA4E,CAC1E,OAA0B,IAAI,EAC9B,aACA,WAEA,YAAY,EAAuB,CACjC,GAAI,CACF,IAAM,EAAS,KAAK,OAAO,SAAS,CAAa,EACjD,KAAK,WAAa,EAClB,KAAK,aAAe,KAAK,OAAO,cAAc,EAAQ,cAAc,CACtE,OAAS,EAAO,CAMd,MALI,KAAK,aAAe,IAAA,KACtB,KAAK,OAAO,MAAM,KAAK,UAAU,EACjC,KAAK,WAAa,IAAA,IAEhB,aAAiB,EAAgC,EAC/C,EAAe,sBAAuB,sBAAsB,CACpE,CACF,CAEA,UACE,EACA,EACA,EACwB,CACxB,IAAM,EAAa,KAAK,WACxB,GAAI,IAAe,IAAA,GAAW,MAAM,EAAe,mBAAoB,MAAM,EAC7E,KAAK,mBAAmB,CAAU,EAElC,IAAI,EAAe,EACf,EAAa,GACb,EACJ,GAAI,CACF,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAiB,OAAQ,GAAS,EAAG,CAC/D,IAAM,EAAQ,IAAU,EAAiB,OAAS,EAC5C,EAAS,KAAK,OAAO,aACzB,EACA,EAAiB,GACjB,EACA,CACF,EACA,GAAI,IAAW,IAAA,GAAW,OAC1B,GAAI,EAAO,CACT,EAAa,EACb,KACF,CACI,GAAY,KAAK,OAAO,MAAM,CAAY,EAC9C,EAAe,EACf,EAAa,GACb,EAAM,uBAAuB,CAAK,CACpC,CAEA,GAAI,IAAe,IAAA,GAAW,MAAM,EAAe,UAAW,WAAW,EACzE,IAAM,EAAQ,KAAK,OAAO,SAAS,EAAY,EAAU,CAAK,EAE9D,OADA,KAAK,mBAAmB,CAAU,EAC3B,CACT,OAAS,EAAO,CAEd,MADI,aAAiB,EAAgC,EAC/C,EAAe,UAAW,MAAM,CACxC,QAAU,CACJ,IAAe,IAAA,IAAW,KAAK,OAAO,MAAM,CAAU,EACtD,GAAY,KAAK,OAAO,MAAM,CAAY,CAChD,CACF,CAEA,OAAc,CACZ,IAAM,EAAS,KAAK,WAChB,IAAW,IAAA,KACf,KAAK,WAAa,IAAA,GAClB,KAAK,OAAO,MAAM,CAAM,EAC1B,CAEA,mBAA2B,EAA6B,CACtD,IAAM,EAAU,KAAK,OAAO,cAAc,EAAQ,cAAc,EAChE,GACE,EAAQ,SAAW,KAAK,aAAa,QACrC,EAAQ,SAAW,KAAK,aAAa,OAErC,MAAM,EAAe,eAAgB,cAAc,CAEvD,CACF,EC1EA,MAAM,EAAkB,IAAI,IAAI,CAC9B,eACA,aACA,cACA,YACA,WACF,CAAC,EAEK,EAAY,IAAI,qBAA8C,GAAY,CAC9E,EAAQ,MAAM,CAChB,CAAC,EAED,SAAS,EAAa,EAA+B,CACnD,GACE,OAAO,GAAkB,UACzB,EAAc,KAAK,CAAC,CAAC,SAAW,GAChC,EAAc,SAAS,IAAI,EAE3B,MAAM,EAAe,eAAgB,eAAe,EAEtD,OAAO,EAAQ,CAAa,CAC9B,CAEA,SAAS,EAAkB,EAAqC,EAAwB,CACtF,GAAI,CAAC,MAAM,QAAQ,CAAgB,GAAK,EAAiB,SAAW,EAClE,MAAM,EAAe,eAAgB,mBAAmB,EAE1D,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAiB,OAAQ,GAAS,EAAG,CAC/D,IAAM,EAAU,EAAiB,GACjC,GACE,OAAO,GAAY,UACnB,EAAQ,SAAW,GACnB,IAAY,KACZ,IAAY,MACZ,EAAQ,SAAS,IAAI,GACrB,EAAQ,SAAS,GAAG,GACpB,EAAQ,SAAS,IAAI,EAErB,MAAM,EAAe,eAAgB,oBAAqB,CAAE,aAAc,CAAM,CAAC,CAErF,CACA,GAAI,CAAC,OAAO,cAAc,CAAQ,GAAK,EAAW,EAChD,MAAM,EAAe,eAAgB,iBAAiB,CAE1D,CAEA,SAAS,EAAkB,EAA+C,CACxE,IAAM,EAAO,GAAG,QAAQ,SAAS,GAAG,QAAQ,OAC5C,GAAI,CAAC,EAAgB,IAAI,CAAI,EAC3B,MAAM,EAAe,sBAAuB,iBAAkB,CAAE,SAAU,CAAK,CAAC,EAGlF,OADI,QAAQ,WAAa,QAAgB,IAAI,EAA6B,CAAa,EAChF,IAAI,EAA2B,CAAa,CACrD,CAEA,IAAM,EAAN,KAAgE,CAM3C,MALnB,QACA,eAAkC,CAAC,EAEnC,YACE,EACA,EACA,CADiB,KAAA,MAAA,EAEjB,IAAM,EAAU,EAAkB,EAAa,CAAa,CAAC,EAC7D,KAAK,QAAU,EACf,EAAU,SAAS,KAAM,EAAS,KAAK,cAAc,CACvD,CAEA,UAAU,EAAqC,EAA0C,CACvF,IAAM,EAAU,KAAK,QACrB,GAAI,IAAY,IAAA,GAAW,MAAM,EAAe,mBAAoB,MAAM,EAC1E,EAAkB,EAAkB,CAAQ,EAC5C,GAAI,CACF,OAAO,EAAQ,UAAU,EAAkB,EAAU,KAAK,KAAK,CACjE,OAAS,EAAO,CAEd,MADI,aAAiB,EAAgC,EAC/C,EAAe,UAAW,MAAM,CACxC,CACF,CAEA,OAAc,CACZ,IAAM,EAAU,KAAK,QACjB,OAAY,IAAA,GAEhB,CADA,KAAK,QAAU,IAAA,GACf,EAAU,WAAW,KAAK,cAAc,EACxC,GAAI,CACF,EAAQ,MAAM,CAChB,OAAS,EAAO,CAEd,MADI,aAAiB,EAAgC,EAC/C,EAAe,UAAW,OAAO,CACzC,CANwC,CAO1C,CAEA,CAAC,OAAO,UAAiB,CACvB,KAAK,MAAM,CACb,CACF,EAGA,SAAgB,EAA6B,EAAgD,CAC3F,OAAO,IAAI,EAAuB,EAAe,CAAC,CAAC,CACrD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robota-sdk/agent-file-authority",
3
- "version": "3.0.0-beta.82",
3
+ "version": "3.0.0-beta.83",
4
4
  "description": "Stable root-relative file read authority for Node.js",
5
5
  "type": "module",
6
6
  "main": "dist/node/index.js",
@@ -28,7 +28,8 @@
28
28
  "default": "./dist/node/index.cjs"
29
29
  }
30
30
  }
31
- }
31
+ },
32
+ "./package.json": "./package.json"
32
33
  },
33
34
  "repository": {
34
35
  "type": "git",
@@ -39,11 +40,12 @@
39
40
  "url": "https://github.com/woojubb/robota/issues"
40
41
  },
41
42
  "engines": {
42
- "node": ">=20.19.0"
43
+ "node": ">=22.12.0"
43
44
  },
44
45
  "files": [
45
46
  "dist",
46
- "src"
47
+ "src",
48
+ "CHANGELOG.md"
47
49
  ],
48
50
  "dependencies": {
49
51
  "koffi": "3.3.1"
@@ -0,0 +1,30 @@
1
+ import { mkdtempSync, rmSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+
5
+ import { afterEach, describe, expect, it, vi } from 'vitest';
6
+
7
+ // Count native library loads while keeping the real Koffi behavior.
8
+ const load = vi.hoisted(() => vi.fn());
9
+ vi.mock('koffi', async () => {
10
+ const actual = await vi.importActual<typeof import('koffi')>('koffi');
11
+ load.mockImplementation((...args: Parameters<typeof actual.load>) => actual.load(...args));
12
+ return { ...actual, load };
13
+ });
14
+
15
+ const roots: string[] = [];
16
+ afterEach(() => {
17
+ for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true });
18
+ });
19
+
20
+ describe.runIf(process.platform !== 'win32')('POSIX native library retention', () => {
21
+ it('loads libc once per process, so no library handle is ever left for the garbage collector', async () => {
22
+ const { PosixStableFileHostBackend } = await import('../posix-backend.js');
23
+ for (let index = 0; index < 3; index += 1) {
24
+ const root = mkdtempSync(join(tmpdir(), 'robota-posix-retention-'));
25
+ roots.push(root);
26
+ new PosixStableFileHostBackend(root).close();
27
+ }
28
+ expect(load).toHaveBeenCalledTimes(1);
29
+ });
30
+ });
@@ -85,6 +85,30 @@ function nodeErrorCode(error: object | null): string | undefined {
85
85
  return typeof error.code === 'string' ? error.code : undefined;
86
86
  }
87
87
 
88
+ let libc: { library: unknown; open: TOpen; openAt: TOpenAt } | undefined;
89
+ // Held as soon as it loads, even if declaring a function below then throws.
90
+ const retainedLibraries: unknown[] = [];
91
+
92
+ /**
93
+ * libc is loaded once per process and the library handle itself is kept alive with its functions.
94
+ * Bun aborts when Koffi's finalizer for a collected library handle runs ("Finalizer is calling a
95
+ * function that may affect GC state"), so no handle may ever become garbage.
96
+ */
97
+ function libcFunctions(): { open: TOpen; openAt: TOpenAt } {
98
+ if (!libc) {
99
+ const library = koffi.load(
100
+ process.platform === 'darwin' ? '/usr/lib/libSystem.B.dylib' : 'libc.so.6',
101
+ );
102
+ retainedLibraries.push(library);
103
+ libc = {
104
+ library,
105
+ open: library.func('int open(const char *path, int flags)') as TOpen,
106
+ openAt: library.func('int openat(int fd, const char *path, int flags)') as TOpenAt,
107
+ };
108
+ }
109
+ return libc;
110
+ }
111
+
88
112
  /** POSIX implementation; descriptors never leave this module. */
89
113
  export class PosixStableFileHostBackend implements IStableFileHostBackend {
90
114
  private readonly openAt: TOpenAt;
@@ -93,11 +117,8 @@ export class PosixStableFileHostBackend implements IStableFileHostBackend {
93
117
 
94
118
  constructor(rootDirectory: string) {
95
119
  try {
96
- const libc = koffi.load(
97
- process.platform === 'darwin' ? '/usr/lib/libSystem.B.dylib' : 'libc.so.6',
98
- );
99
- const open = libc.func('int open(const char *path, int flags)') as TOpen;
100
- this.openAt = libc.func('int openat(int fd, const char *path, int flags)') as TOpenAt;
120
+ const { open, openAt } = libcFunctions();
121
+ this.openAt = openAt;
101
122
  const descriptor = open(rootDirectory, flags(true));
102
123
  if (descriptor < 0) this.throwOpenFailure(koffi.errno(), 'open-root');
103
124
  this.rootDescriptor = descriptor;
@@ -221,14 +221,23 @@ function bindNtDll(
221
221
  }
222
222
 
223
223
  let cachedApi: IWindowsApi | undefined;
224
+ /**
225
+ * The library handles stay referenced for the life of the process: Bun aborts when Koffi's
226
+ * finalizer for a collected library handle runs, so a handle must never become garbage.
227
+ */
228
+ const retainedLibraries: unknown[] = [];
224
229
 
225
230
  export function getWindowsApi(): IWindowsApi {
226
231
  if (cachedApi !== undefined) return cachedApi;
227
232
  const types = defineWindowsTypes();
233
+ const kernel32 = koffi.load('kernel32.dll');
234
+ retainedLibraries.push(kernel32);
235
+ const ntdll = koffi.load('ntdll.dll');
236
+ retainedLibraries.push(ntdll);
228
237
  cachedApi = {
229
238
  ...types,
230
- ...bindKernel32(koffi.load('kernel32.dll'), types),
231
- ...bindNtDll(koffi.load('ntdll.dll'), types),
239
+ ...bindKernel32(kernel32, types),
240
+ ...bindNtDll(ntdll, types),
232
241
  };
233
242
  return cachedApi;
234
243
  }