Commit Graph
424 Commits
Author SHA1 Message Date
codingetandClaude 755b3d425d test(drive,ftp,sftp): run conformance over a minimal backing filesystem
Each package ran the suite against a server backed by a full MemoryVFS, so
the servers' fallback paths were never entered. A second run reduces the
server's filesystem to the required operations while leaving the client under
test unchanged; it fails on #177 without the previous commit. Where a server
answers rather than shims — WebDAV 403 for PROPPATCH, FTP 502 and SFTP
SSH_FX_OP_UNSUPPORTED for rename — the run declares that instead of opting
out, which is the decision #179 revisits.

Fixes #181

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 19:53:54 +00:00
codingetandClaude 7ce1a42fbe fix(drive): serve the requested window on a Range without readFileRange
The fallback branch discarded range.start and streamed the file from byte
zero while the response advertised the requested window, so a client asking
for bytes 5-9 got bytes 0-4 and stored them at the wrong offset.
readFileRangeFallback windows the stream and calls the native operation when
there is one.

Fixes #177

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 19:53:52 +00:00
codingetandClaude 99f0efc69c feat(vfs): share the optional-stripping helpers in conformance
withoutOptional and unsupportedOptional replace the local copies in the
fallback tests; the protocol packages need the same reduction to put their
servers' fallback paths under the suite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 19:53:50 +00:00
codingetandCodex c562af8ec3 fix(vfs): reject overlapping copies
CI / format (pull_request) Successful in 2m45s
CI / lint (pull_request) Successful in 3m6s
CI / install (pull_request) Successful in 7m33s
CI / typetest (pull_request) Successful in 2m28s
CI / typecheck (pull_request) Successful in 2m46s
CI / node-tests (pull_request) Successful in 2m52s
CI / browser-tests (pull_request) Successful in 4m32s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-05 17:22:46 +00:00
codingetandClaude b07a3608e9 docs: record the statAndReaddir policy and the issues filed
CI / install (pull_request) Failing after 3m8s
CI / typecheck (pull_request) Skipped
CI / typetest (pull_request) Skipped
CI / node-tests (pull_request) Skipped
CI / browser-tests (pull_request) Skipped
CI / format (pull_request) Successful in 4m58s
CI / lint (pull_request) Successful in 5m9s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 01:47:55 +00:00
codingetandClaude 016e506393 fix(vfs): let the statAndReaddir callsites fall back on unsupported
Every one of these callers needs the listing to answer the request, and the
stat plus readdir it falls back to are required operations, so there is no
reason to stop at an implementation that has the combined call but cannot serve
it for this path. The default policy stops there because for most operations a
fallback is materially different from the native one; for this one it is only
slower.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 01:44:37 +00:00
codingetandClaude b72ee0c9f1 test(vfs): cover the review's untested boundaries
CI / lint (pull_request) Successful in 2m38s
CI / format (pull_request) Successful in 2m43s
CI / install (pull_request) Successful in 8m55s
CI / typetest (pull_request) Successful in 2m40s
CI / typecheck (pull_request) Successful in 2m58s
CI / node-tests (pull_request) Successful in 3m9s
CI / browser-tests (pull_request) Successful in 4m52s
A stream that fails after the native call resolved is deliberately not retried,
and cancelling the fallback's output while a read is in flight must still
release the source. Both were argued from the code rather than demonstrated.
The root path overlaps every other path, so it is rejected at either end of a
copy; that followed from the guard without being stated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 00:34:14 +00:00
codingetandClaude 4387ea9837 docs: record the vfs fallback work
CI / typecheck (pull_request) Canceled after 0s
CI / typetest (pull_request) Canceled after 0s
CI / node-tests (pull_request) Canceled after 0s
CI / browser-tests (pull_request) Canceled after 0s
CI / format (pull_request) Canceled after 1m58s
CI / lint (pull_request) Canceled after 2m4s
CI / install (pull_request) Canceled after 2m10s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 00:13:37 +00:00
codingetandClaude cf485af184 test(vfs): cover shimmed and unsupported conformance variants
An implementation that answers unsupported at runtime was not expressible: the
capability flags only said present or absent, so the suite could not check that
such a rejection carries the right code and leaves the filesystem alone.

The fallbacks are then run against the same suite with the optional operations
first removed and then rejecting, which is what shows they can stand in for
them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 00:12:56 +00:00
codingetandClaude a81437430c fix(vfs): honour empty ranges in the drive and ftp clients
Both clients failed the boundary behaviour the conformance suite now requires.
WebDAV answers a range at or past the end of a file with 416, which RFC 9110
defines as unsatisfiable and the client reported as an unexpected status; at
this level the window is simply empty. FTP carries no end bound on REST, so an
end below the start transferred from the start to the end of the file instead
of nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 00:12:43 +00:00
codingetandClaude c8352a319e test(vfs): pin readFileRange boundary behaviour
Serving a byte range needs an empty window and an unanswerable request to be
distinguishable, so a start at or past the end of a file and an end below the
start return an empty stream rather than failing. The suite did not say so, and
NodeVFS passed both bounds to createReadStream, which rejects an inverted range
with a plain RangeError.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 20:00:38 +00:00
codingetandClaude 1660b1df2e refactor(vfs): share the statAndReaddir fallback
The optional operation arrived with three byte-identical copies of the same
fallback, one per call site.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 19:59:04 +00:00
codingetandClaude 4c17366172 feat(vfs): add fallbacks for optional operations
Callers of an optional AsyncVFS operation each had to decide what to do when
an implementation lacked it, so the same fallbacks were written repeatedly and
diverged. withFallbacks presents every optional operation as available, so a
consumer can call it without branching on method presence, and the operations
that cannot be built from the required ones say so with the unsupported code
rather than appearing absent.

The policy chooses when a fallback may stand in. Only an unsupported rejection
ever triggers one: any other failure means the operation was attempted, and
retrying it another way would hide that.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 19:57:24 +00:00
codingetandClaude 3270e373a0 feat(vfs): map native unsupported statuses onto the vfs code
Each client already had a native response meaning the operation does not exist
here, and each discarded it: SFTP OP_UNSUPPORTED and FTP 502 surfaced as bare
protocol errors, WebDAV 501 hit the unexpected-status branch, and SMB2
STATUS_NOT_SUPPORTED reached callers as forbidden, which claims a permission
problem the server never reported.

Without this a server emits the status and its own client throws the meaning
away, so no caller can act on it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 19:53:37 +00:00
codingetandClaude 9ae97a37ad feat(vfs): add an unsupported error code
Optional AsyncVFS operations could only be declared by method presence, which
is global to an instance and cannot express a backend that performs an
operation for some shares, paths or negotiated protocol versions and not
others. Those cases were forced onto forbidden, which claims the caller lacks
permission when the backend said the operation does not exist here.

The outbound wire mappings are exhaustive over VFSErrorCode, so each one names
its protocol's own unsupported response.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 19:50:48 +00:00
codingetandCodex 394d720aeb docs(vfs): qualify overwrite replacement
CI / lint (pull_request) Successful in 2m0s
CI / format (pull_request) Successful in 2m6s
CI / install (pull_request) Successful in 7m14s
CI / typetest (pull_request) Successful in 2m5s
CI / node-tests (pull_request) Successful in 2m33s
CI / typecheck (pull_request) Successful in 2m35s
CI / browser-tests (pull_request) Successful in 6m52s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-04 14:39:04 +00:00
codingetandCodex c9ff59bd9a docs(vfs): define overwrite replacement
CI / format (pull_request) Successful in 2m0s
CI / typecheck (pull_request) Canceled after 0s
CI / typetest (pull_request) Canceled after 0s
CI / node-tests (pull_request) Canceled after 0s
CI / browser-tests (pull_request) Canceled after 0s
CI / lint (pull_request) Canceled after 2m12s
CI / install (pull_request) Canceled after 2m28s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-04 14:36:59 +00:00
codingetandCodex e00d55d2af fix(vfs): preserve overwrite destinations
CI / lint (pull_request) Successful in 2m8s
CI / format (pull_request) Successful in 2m8s
CI / install (pull_request) Successful in 6m52s
CI / typetest (pull_request) Successful in 2m12s
CI / typecheck (pull_request) Successful in 2m43s
CI / node-tests (pull_request) Successful in 2m43s
CI / browser-tests (pull_request) Successful in 4m34s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-04 13:50:22 +00:00
codingetandCodex a82ddab3a2 fix(drive): preserve overwrite destination
CI / lint (pull_request) Successful in 2m8s
CI / format (pull_request) Successful in 2m11s
CI / typecheck (pull_request) Canceled after 0s
CI / typetest (pull_request) Canceled after 0s
CI / node-tests (pull_request) Canceled after 0s
CI / browser-tests (pull_request) Canceled after 0s
CI / install (pull_request) Canceled after 6m25s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-04 13:44:07 +00:00
codingetandCodex e1dc165e03 fix(vfs): address combined metadata review
CI / lint (pull_request) Successful in 2m34s
CI / format (pull_request) Successful in 2m37s
CI / typecheck (pull_request) Canceled after 0s
CI / typetest (pull_request) Canceled after 0s
CI / node-tests (pull_request) Canceled after 0s
CI / browser-tests (pull_request) Canceled after 0s
CI / install (pull_request) Canceled after 5m5s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-04 13:39:10 +00:00
codingetandCodex 93b2dff943 refactor(vfs): reuse combined directory metadata
CI / format (pull_request) Successful in 2m21s
CI / lint (pull_request) Successful in 2m35s
CI / install (pull_request) Successful in 9m0s
CI / typetest (pull_request) Successful in 2m35s
CI / node-tests (pull_request) Successful in 3m4s
CI / typecheck (pull_request) Successful in 3m13s
CI / browser-tests (pull_request) Successful in 4m46s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-04 00:51:42 +00:00
codingetandCodex e21ad5f24f feat(vfs): combine stat and directory listing
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-04 00:49:30 +00:00
codingetandCodex dc613b5dfe refactor(taildrive): clarify discovery exports
CI / lint (pull_request) Successful in 2m47s
CI / format (pull_request) Successful in 3m0s
CI / install (pull_request) Successful in 9m49s
CI / typetest (pull_request) Successful in 2m41s
CI / typecheck (pull_request) Successful in 2m59s
CI / node-tests (pull_request) Successful in 3m13s
CI / browser-tests (pull_request) Successful in 5m0s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-03 23:08:54 +00:00
codingetandCodex a60f1b626e fix(taildrive): harden peer probes
CI / format (pull_request) Successful in 1m55s
CI / lint (pull_request) Successful in 2m4s
CI / install (pull_request) Successful in 5m52s
CI / typetest (pull_request) Successful in 2m7s
CI / node-tests (pull_request) Successful in 2m18s
CI / typecheck (pull_request) Successful in 2m20s
CI / browser-tests (pull_request) Successful in 3m47s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-03 01:27:54 +00:00
codingetandCodex a0ad4bf17b feat(taildrive): discover peers with shares
CI / lint (pull_request) Successful in 1m49s
CI / format (pull_request) Successful in 2m0s
CI / typecheck (pull_request) Canceled after 0s
CI / typetest (pull_request) Canceled after 0s
CI / node-tests (pull_request) Canceled after 0s
CI / browser-tests (pull_request) Canceled after 0s
CI / install (pull_request) Canceled after 5m12s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-08-03 01:23:10 +00:00
codingetandClaude ab987f50d0 test(tsconnect): assert drive peers are reported online
CI / format (pull_request) Successful in 2m25s
CI / lint (pull_request) Successful in 2m28s
CI / install (pull_request) Successful in 7m35s
CI / typetest (pull_request) Successful in 2m15s
CI / typecheck (pull_request) Successful in 2m31s
CI / node-tests (pull_request) Successful in 2m32s
CI / browser-tests (pull_request) Successful in 3m56s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 01:39:46 +00:00
codingetandClaude a6b71597ca fix(tsconnect): filter offline and unreachable taildrive peers
CI / format (pull_request) Successful in 2m35s
CI / lint (pull_request) Successful in 2m37s
CI / typecheck (pull_request) Canceled after 0s
CI / typetest (pull_request) Canceled after 0s
CI / node-tests (pull_request) Canceled after 0s
CI / browser-tests (pull_request) Canceled after 0s
CI / install (pull_request) Canceled after 5m55s
listDrivePeers returned every peer holding PeerCapabilityTaildriveSharer,
which is an ACL grant usually given to a whole group or tag, so offline
peers and peers with no reachable peerAPI were included. Bump the
tailscale submodule to the fix and correct the TS doc comments, which
claimed the result was peers exposing a share.

The submodule bump also picks up the removal of the obsolete ssh, fetch
and setExitNodeEnabled wasm bridge APIs, already on the fork's webnet
branch and unused here.

Closes #143

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 01:34:07 +00:00
codinget f4131462af build(turbo): fix turbo cache with submodules
CI / format (pull_request) Successful in 2m39s
CI / lint (pull_request) Successful in 2m55s
CI / install (pull_request) Successful in 7m57s
CI / typetest (pull_request) Successful in 2m4s
CI / node-tests (pull_request) Successful in 2m40s
CI / typecheck (pull_request) Successful in 2m44s
CI / browser-tests (pull_request) Successful in 4m13s
2026-08-01 00:19:50 +00:00
webnet-actions f766c853d9 chore(tsconnect): update CA bundle 2026-07-31 21:15:18 +00:00
codingetandCodex 1287baf203 ci: paginate CA updater PR discovery
CI / format (pull_request) Successful in 2m16s
CI / lint (pull_request) Successful in 2m36s
CI / install (pull_request) Successful in 9m51s
CI / typetest (pull_request) Successful in 2m40s
CI / typecheck (pull_request) Successful in 3m5s
CI / node-tests (pull_request) Successful in 3m17s
CI / browser-tests (pull_request) Successful in 4m59s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-07-31 20:52:07 +00:00
codingetandCodex 5ac5f08378 ci: schedule tsconnect CA bundle refresh
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-07-31 20:52:07 +00:00
codingetandCodex d28697eeee fix(tsconnect): publish only used build outputs
CI / lint (pull_request) Successful in 2m49s
CI / format (pull_request) Successful in 2m54s
CI / install (pull_request) Successful in 7m37s
CI / typetest (pull_request) Successful in 2m2s
CI / node-tests (pull_request) Successful in 2m25s
CI / typecheck (pull_request) Successful in 2m35s
CI / browser-tests (pull_request) Successful in 3m55s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-07-31 22:35:18 +02:00
codingetandClaude 962ed20219 docs: drop the duplicated #147 disclosure entry
CI / format (pull_request) Successful in 2m43s
CI / lint (pull_request) Successful in 2m47s
CI / install (pull_request) Successful in 9m49s
CI / typecheck (pull_request) Successful in 2m46s
CI / typetest (pull_request) Successful in 2m43s
CI / node-tests (pull_request) Successful in 2m21s
CI / browser-tests (pull_request) Successful in 4m23s
The union merge driver kept both the pre- and post-rebase versions of the
tsconnect shutdown entry; the older one predates the dead-handle change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 16:49:20 +00:00
codingetandClaude 279733fc01 test: run Go runtime shutdowns in-process again
CI / lint (pull_request) Successful in 3m58s
CI / format (pull_request) Successful in 2m55s
CI / install (pull_request) Successful in 13m11s
CI / typecheck (pull_request) Successful in 3m35s
CI / typetest (pull_request) Successful in 1m30s
CI / node-tests (pull_request) Successful in 1m34s
CI / browser-tests (pull_request) Successful in 3m59s
The child-process probes, the missing shutdown() in the tsconnect
integration teardown, and the *.probe.ts tsconfig excludes all existed
only to keep the late "Go program has already exited" errors away from
node:test. That bug is fixed, so drop the workarounds; the restored
in-process shutdowns are the regression test for it.

Refs #147, #152

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 00:39:07 +00:00
codingetandClaude 342b996828 fix(tsconnect): treat handles from an exited runtime as closed
CI / lint (pull_request) Successful in 2m34s
CI / format (pull_request) Successful in 2m34s
CI / install (pull_request) Successful in 7m2s
CI / typetest (pull_request) Successful in 1m59s
CI / node-tests (pull_request) Successful in 2m24s
CI / typecheck (pull_request) Successful in 2m31s
CI / browser-tests (pull_request) Successful in 3m53s
With _resume no longer throwing after exit, a call into a dead Go handle
resolves with undefined instead of failing — a read loop would spin on it
rather than break out. Conn, PacketConn and TCPListener now report closed
once their runtime has exited, so those calls raise ClosedError and
close() is a no-op.

Refs #147

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 00:38:58 +00:00
codingetandClaude 90f262e301 fix(tsconnect): drop the Go runtime's pending work on exit
CI / format (pull_request) Successful in 3m27s
CI / lint (pull_request) Successful in 3m27s
CI / install (pull_request) Successful in 11m17s
CI / typetest (pull_request) Successful in 2m11s
CI / node-tests (pull_request) Successful in 3m8s
CI / typecheck (pull_request) Successful in 3m24s
CI / browser-tests (pull_request) Successful in 5m56s
wasm_exec.js keeps the Go scheduler's setTimeout armed and every js.FuncOf
callback registered across program exit, and _resume throws "Go program has
already exited" when they fire. After a clean shutdown() that surfaces as
uncaught errors and unhandled rejections seconds later: a scheduler timeout,
DERP WebSocket close listeners, and a fetch continuation.

initIPN now clears the scheduled timeouts from runtime.wasmExit and makes
_resume a no-op once the runtime has exited.

Fixes #147

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 23:51:40 +00:00
codingetandCodex ad0d5259ab test(tsconnect-worker): isolate Go runtime shutdowns
CI / lint (pull_request) Successful in 2m24s
CI / format (pull_request) Successful in 2m32s
CI / install (pull_request) Successful in 7m2s
CI / typetest (pull_request) Successful in 1m54s
CI / node-tests (pull_request) Successful in 2m8s
CI / typecheck (pull_request) Successful in 2m19s
CI / browser-tests (pull_request) Successful in 3m37s
Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
2026-07-30 00:38:22 +02:00
codinget afbc809240 chore: remove deprecated APIs: setExitNodeEnabled, ping(disco), fetch, ssh
CI / format (pull_request) Successful in 2m41s
CI / lint (pull_request) Successful in 2m46s
CI / install (pull_request) Successful in 10m45s
CI / typetest (pull_request) Successful in 2m49s
CI / typecheck (pull_request) Successful in 3m2s
CI / node-tests (pull_request) Successful in 3m10s
CI / browser-tests (pull_request) Successful in 6m13s
2026-07-29 21:38:42 +00:00
codinget 7f6a2bbc22 chore: switch to the rebased tailscale build
CI / lint (pull_request) Successful in 1m52s
CI / format (pull_request) Successful in 2m3s
CI / install (pull_request) Successful in 6m22s
CI / typetest (pull_request) Successful in 2m0s
CI / typecheck (pull_request) Successful in 2m15s
CI / node-tests (pull_request) Successful in 2m17s
CI / browser-tests (pull_request) Successful in 3m29s
2026-07-29 20:15:25 +00:00
codingetandClaude ca91c68717 docs: record tsconnect integration test coverage in AI_CHANGES
CI / format (pull_request) Successful in 1m58s
CI / lint (pull_request) Successful in 2m0s
CI / install (pull_request) Successful in 5m9s
CI / typetest (pull_request) Successful in 1m38s
CI / typecheck (pull_request) Successful in 1m54s
CI / node-tests (pull_request) Successful in 2m0s
CI / browser-tests (pull_request) Successful in 3m14s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 23:51:26 +00:00
codingetandClaude fddeb7f5ba docs: document the new integration test env variables
CI / format (pull_request) Successful in 2m4s
CI / lint (pull_request) Successful in 2m9s
CI / install (pull_request) Successful in 5m28s
CI / typetest (pull_request) Successful in 1m46s
CI / node-tests (pull_request) Successful in 1m55s
CI / typecheck (pull_request) Successful in 2m5s
CI / browser-tests (pull_request) Successful in 3m19s
TSCONNECT_TEST_USER_AUTH_KEY and TSCONNECT_TEST_EXIT_NODE_ID gate the Taildrop
and exit node tests; both are optional and the suite is green without them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 23:21:18 +00:00
codingetandClaude 8edecd479b test(tsconnect): cover UDP, ICMP, TLS, taildrop and shutdown against headscale
CI / lint (pull_request) Successful in 2m6s
CI / format (pull_request) Successful in 1m59s
CI / install (pull_request) Successful in 5m37s
CI / typetest (pull_request) Successful in 1m49s
CI / node-tests (pull_request) Successful in 2m4s
CI / typecheck (pull_request) Successful in 2m4s
CI / browser-tests (pull_request) Successful in 3m18s
The integration suite only exercised a fraction of IpnClient: reaching Running,
localAPI, service advertisement and a TCP listen/dial pair. Everything else was
either unit-tested against mocks (serveDrive, listDrivePeers, upgradeTls) or
only ever checked by hand.

Adds real two-node coverage for:

- UDP listen/writeTo/readFrom, both directions
- listenICMP lifecycle plus an echo request/reply round-trip
- listenTLS + dialTLS with a self-signed CA, the negative case where the cert
  does not chain to the supplied CA, and upgradeTls on a live TCP conn
- taildrop end-to-end: listFileTargets, sendFile, waitingFiles, openWaitingFile
  and deleteWaitingFile
- serveDrive/listDrivePeers over a real netmap
- suggestExitNode, and setExitNode gated on TSCONNECT_TEST_EXIT_NODE_ID

listenTLS works here because it takes the cert and key directly rather than
going through ACME, so it does not need a control plane that issues certs. Only
Funnel needs that, and it stays manual.

Taildrop requires both ends to be untagged: canPutFile wants IsSelfUntagged()
or an ACL-granted PeerCapabilityFileSharingSend, and the shared auth key
registers nodes as tag:test. Those tests run their own pair from
TSCONNECT_TEST_USER_AUTH_KEY and skip when it is unset.

shutdown() runs in a child process (src/shutdown.probe.ts): it exits the whole
Go runtime, and the runtime leaves scheduler timeouts behind that throw "Go
program has already exited" afterwards, which node:test attributes to whichever
hook created them. For the same reason the shared suite no longer calls
shutdown() in its teardown. See webnet/webnet#147.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 23:00:49 +00:00
codingetandClaude 0172018cc6 ci: bound the Node heap for CI jobs
CI / lint (pull_request) Successful in 1m23s
CI / format (pull_request) Successful in 1m26s
CI / install (pull_request) Successful in 4m58s
CI / typetest (pull_request) Successful in 1m40s
CI / node-tests (pull_request) Successful in 1m58s
CI / typecheck (pull_request) Successful in 1m59s
CI / browser-tests (pull_request) Successful in 3m9s
Closes #136

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:24:45 +00:00
codingetandClaude 1196469156 test(vfs): require reads to leave the file intact
The truncation this branch fixed was only caught incidentally, by the
cancellation test happening to re-read afterwards. Reading through a default
reader does not reproduce it either: the entry is detached only once a consumer
transfers the chunk's buffer, which is what postMessage to a worker does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:58:45 +02:00
codingetandClaude 4ebe30ddc5 docs: record the conformance review round in AI_CHANGES
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:58:45 +02:00
codingetandClaude 5ca35f59ab fix: address conformance review findings
The suite gained no coverage of readFileRange's error contract when the
per-package tests for it were removed, and none of copy replacing an existing
directory, which NodeVFS merged into instead.

SMB2 copy could recurse without bound when the destination was inside the
source, and could not overwrite a destination of the other type.

The DAV client read every 405 as is-a-directory; RFC 4918 only gives it that
meaning for GET and PUT, so any other method now keeps a generic error.

The FTP server relayed the backing filesystem's prose as 550 text, leaving the
client to guess the code from wording that MemoryVFS never produced. It now
sends conventional wording per code, so the client no longer needs to accept
not-found in place of forbidden.

SFTP marks the VFS code it embeds in SSH_FX_FAILURE messages, so a third-party
server's prose can no longer be decoded as one of ours.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:58:45 +02:00
codingetandClaude 11f907cae5 docs: record the AsyncVFS conformance suite in AI_CHANGES
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:58:45 +02:00
codingetandClaude e573e22078 test(drive): run the AsyncVFS conformance suite
The suite caught four client bugs: copy() and move() defaulted to overwriting
the destination, delete() only checked for a non-empty directory when
recursive was explicitly false, readdir() of a file returned an empty listing
instead of rejecting, and setProps() merged rather than replaced.

The server reported is-a-directory as 409, which the client read back as
already-exists; RFC 4918 calls for 405 on a collection, which is what the GET
handler already returns.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:58:45 +02:00
codingetandClaude 8c6617dcb0 fix(vfs): copy file contents out of MemoryVFS on read
readFile enqueued the entry's own array, and a consumer reading through a byte
stream transfers the chunk's buffer, detaching it. Reading a file over WebDAV
and cancelling the stream truncated the stored file to zero bytes.

The conformance suite compared payloads with assert.deepEqual, so this surfaced
as node building a diff between a 128 KiB array and an empty one, which
exhausts memory and takes the process down before the failure is reported.
Compare byte ranges directly instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:58:45 +02:00
codingetandClaude 38a98c3155 test(smb2): run the AsyncVFS conformance suite
The suite caught two client bugs: delete() accepted the share root, and copy()
ignored opts.overwrite so it always clobbered the destination, and could not
copy directories.

The mock server also mis-called #respond on two missing-parent paths, ignored
CreateOptions.DIRECTORY_FILE / NON_DIRECTORY_FILE, and ignored ReplaceIfExists
on rename, so it never produced the errors a real server would.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:58:45 +02:00