Full deep audit of the puter.js SDK for TypeScript drift and documentation drift against the implementation. Every API family was re-derived from scratch (impl + types + docs read in full).
- Commit:
2c0a1b9bbbbb8ddce52d5afc1b4d7afc8b1fb961 (2c0a1b9)
- Date: 2026-07-06
- Sources of truth: impl
src/puter-js/src/modules/ (+ src/puter-js/src/index.js, and src/gui/src/IPC.js for the UI family) · types src/puter-js/types/ · docs src/docs/src/
Legend
🟡 types (impl/docs have it, types don't/wrong) · 🔵 docs (impl/types have it, docs don't/wrong) · 🔴 both · 🐛 impl-bug (advertised param the impl ignores, or phantom typed method) · 🆕/🗑 manifest-drift
🎯 Highest-impact shortlist (cross-family)
- 🐛 KV
list(pattern, false) silently ignores the pattern — a form the types explicitly sanction; returns the entire keyspace instead of the filtered subset. KV.js:618
- 🐛 KV
incr(key, 0) / decr(key, 0) off-by-one — explicit 0 is coerced to the default 1. KV.js:323,353
- 🔴 KV
MAX_VALUE_SIZE documented as 400 KB but is 399 * 1024 (399 KB) — wrong in 6 places across types + docs. KV.js:36
- 🐛
FSItem.move() / FSItem.copy() advertised params silently ignored — overwrite/newName/autoRename are typed & accepted but positionally mis-mapped into the wrong fs.move/fs.copy argument slots. FSItem.js:89-94
- 🔴
FSItem.isDir vs impl isDirectory — the documented/typed property does not exist on the instance; every consumer reading item.isDir gets undefined. FSItem.js:17
- 🐛 UI
notify type & duration silently dropped for apps — two prominently documented options that do nothing in the real Puter desktop (IPC never forwards them). IPC.js:283-290
- 🐛 UI
socialShare(url) throws in the GUI — documented "options optional", but the IPC handler dereferences event.data.options.left unconditionally. IPC.js:329
- 🐛 Networking
PSocket.write ArrayBuffer branch throws — calls data.write(...) on an ArrayBuffer; ArrayBuffer input is typed/documented as supported but crashes. PSocket.js:74-75
- 🔴 Auth
getGlobalUsage shipping with zero types and zero docs. Auth.js:338
- 🔴 Perms
requestReadAppRootDir / requestWriteAppRootDir missing from types + docs — and they carry live impl bugs (hardcoded read access; undefined-variable ReferenceError). Perms.js:333,346
- 🐛 Hosting
create('subdomain') single-arg overload untyped — real, explicitly-coded form fails typecheck (types require dirPath). Hosting.js:50
- 🔴 AI
chat streaming error & image chunk types — real emitted chunk types missing from both ChatResponseChunk and the object doc; the error chunk especially matters since errors arrive as a chunk, not a throw. ai.d.ts:108
🆕/🗑 Manifest-drift (disk vs Expected manifest)
| File |
Status |
Tag |
src/docs/src/Objects/ttsengine.md |
On disk, not in manifest Objects list (backs AI/txt2speech.listEngines) |
🆕 |
src/docs/src/Objects/ttsvoice.md |
On disk, not in manifest Objects list (backs AI/txt2speech.listVoices) |
🆕 |
Everything else on disk matches the manifest exactly (all impl modules, FS ops, networking, UI components, types, and docs families/pages present as expected). No removed/renamed files.
AI
| Function |
Item |
Issue |
Tag |
impl ref |
chat |
provider option |
Read by impl & typed, but undocumented in chat.md options list |
🔵 |
AI.js:800-802 |
chat |
driver option |
Read by impl (maps to provider) & typed, but undocumented |
🔵 |
AI.js:812-814 |
chat |
context_management option |
In impl PARAMS_TO_PASS & types; chat.md documents only compaction |
🔵 |
AI.js:819 |
chat |
response option |
In impl PARAMS_TO_PASS & typed; undocumented (borderline, loosely typed) |
🔵 |
AI.js:819 |
chat |
vision option |
ChatOptions.vision?: boolean typed, but impl never reads a user-supplied vision (only sets it internally) — value silently dropped |
🐛 |
ai.d.ts:36 / AI.js:733 |
chat (stream) |
"error" chunk |
Documented (chat.md:227,295-297 with .message) but absent from ChatResponseChunk.type enum & chatresponsechunk.md |
🔴 |
ai.d.ts:108 |
chat (stream) |
"image" chunk |
Documented (chat.md:377-384, part.image) but absent from type enum, image field & chatresponsechunk.md |
🔴 |
ai.d.ts:108 |
chat |
message.images typing |
ChatMessage.images non-optional but only present on image-gen responses → should be images? |
🟡 |
ai.d.ts:14 |
chat |
image_url.thoughtSignature |
Passed/read in multi-turn image editing but ImageContent doesn't model it |
🟡 |
ai.d.ts:3-6 |
ChatResponse |
choices field |
Typed choices?: unknown; not in chatresponse.md (borderline) |
🔵 |
ai.d.ts:93 |
speech2speech |
voiceId alias |
Impl normalizes voiceId→voice; missing from types & docs |
🔴 |
AI.js:416 |
speech2speech |
modelId alias |
Impl normalizes modelId→model; missing from types & docs |
🔴 |
AI.js:417 |
speech2speech |
outputFormat alias |
Impl normalizes →output_format; missing from types & docs |
🔴 |
AI.js:418 |
speech2speech |
voiceSettings alias |
Impl normalizes →voice_settings; missing from types & docs |
🔴 |
AI.js:419 |
speech2speech |
fileFormat alias |
Impl normalizes →file_format; missing from types & docs |
🔴 |
AI.js:420 |
speech2speech |
removeBackgroundNoise alias |
Impl normalizes →remove_background_noise; not in types; docs use it in an example but options list documents only snake_case |
🔴 |
AI.js:421-423 |
speech2speech |
optimizeStreamingLatency alias |
Impl normalizes →optimize_streaming_latency; missing from types & docs |
🔴 |
AI.js:424-426 |
speech2speech |
enableLogging alias |
Impl normalizes →enable_logging; missing from types & docs |
🔴 |
AI.js:427-429 |
txt2vid |
duration alias |
Impl accepts duration→seconds, typed, but txt2vid.md documents only seconds |
🔵 |
AI.js:979-981 |
txt2vid |
driver option |
Read by impl & typed; undocumented |
🔵 |
AI.js:988 |
txt2img |
driver option |
Impl uses options.driver (not provider) for service override; typed but txt2img.md documents provider only |
🔵 |
AI.js:909 |
txt2img |
service option |
Typed service?: string but impl never reads it (only driver) — possible phantom |
🐛 (low-conf) |
ai.d.ts:182 |
txt2img |
nano-banana / nano-banana-pro model aliases |
Impl remaps to Gemini ids; not in options table nor types |
🔴 |
AI.js:900-907 |
txt2speech.listVoices |
string-arg overload |
Impl & types accept a bare engine string; doc Syntax block omits the form |
🔵 |
AI.js:651-652 |
FileSystem (FS)
| Function |
Item |
Issue |
Tag |
impl ref |
FSItem |
isDirectory vs isDir |
Impl exposes isDirectory; types & docs call it isDir — property does not exist |
🔴 |
FSItem.js:17 |
FSItem.move() |
overwrite, newName params |
Passed positionally to fs.move, which expects arg[2]=options object → both ignored |
🐛 |
FSItem.js:89-90 / move.js:13-21 |
FSItem.copy() |
autoRename, overwrite params |
Same mis-map: arg[2] expected as options object; both ignored |
🐛 |
FSItem.js:93-94 / copy.js:12-24 |
FSItem |
move(), copy() methods |
Implemented + typed but absent from fsitem.md methods list |
🔵 |
FSItem.js:89,93 |
read() |
cache option |
Impl reads options.cache; missing from ReadOptions and read.md |
🔴 |
read.js:46 |
readdir() |
no_subdomains |
Impl reads/sends it; not in ReaddirOptions |
🟡 |
readdir.js:59,128 |
readdir() |
consistency, no_thumbs, no_assocs, no_subdomains |
Impl-read; readdir.md documents only path,uid |
🔵 |
readdir.js:44,57-60 |
readdir() |
readdir(path, options) overload |
Docs advertise it, but positional branch treats arg[1] as success callback — options mis-handled |
🐛 |
readdir.js:18-25 / readdir.md:14 |
stat() |
returnWorkers alias |
Impl reads returnSubdomains || returnWorkers; alias missing from StatOptions |
🟡 |
stat.js:54,126 |
stat() |
consistency |
Typed but not in stat.md options |
🔵 |
stat.js:31 |
mkdir() |
rename alias |
Impl reads rename || dedupeName; alias typed but not in mkdir.md |
🔵 |
mkdir.js:51 |
mkdir() |
recursive alias |
Impl reads recursive || createMissingParents; typed but not in mkdir.md |
🔵 |
mkdir.js:54 |
mkdir() |
shortcutTo |
Impl sends shortcut_to; typed but not in mkdir.md |
🔵 |
mkdir.js:52 |
move() |
dedupeName option |
Docs advertise it, but move.js never reads any dedupe key nor sends dedupe_name — phantom |
🐛 |
move.js:59-67 / move.md:34 |
move() |
newName option |
Impl supports newName/new_name; move.md omits it |
🔵 |
move.js:63 |
copy() |
dedupeName in positional form |
3-arg positional branch never extracts dedupeName/dedupe_name from arg[2]; only copy(options) form works |
🐛 |
copy.js:12-24,56 |
upload() |
generateThumbnails, thumbnailGenerator, thumbnail |
Impl reads all three; none in UploadOptions |
🟡 |
upload.js:571,573,690 |
upload() |
thumbnail options, appUID, shortcutTo, createFileParent, … |
upload.md documents only overwrite,dedupeName,createMissingParents |
🔵 |
upload.js:571,737,1311 |
revokeReadURL() |
method existence |
Wired on puter.fs but absent from FS types class and no doc |
🔴 |
revokeReadUrl.js / index.js:44 |
readdirSubdomains() |
method existence |
Wired on puter.fs but absent from FS types class and no doc |
🔴 |
readdirSubdomains.js / index.js:46 |
sign() |
doc existence |
Typed (SignResult) & implemented, but no FS/sign doc page |
🔵 |
sign.js / index.js:42 |
space() |
params |
space.md says "Parameters: None", but impl accepts (success, error) and an options object; example calls puter.space() not puter.fs.space() |
🔵 |
space.js:3-16 |
FSItem |
uid/uuid, readURL/writeURL/metadataURL |
Set by constructor & (URLs) present in types, but omitted from fsitem.md attributes |
🔵 |
FSItem.js:5-11 |
FSItem.rename() |
uid vs path |
(low-conf) Calls fs.rename(this.uid, ...); rename.js else-branch treats arg[0] as path, so uid may be sent as a path |
🐛 (low-conf) |
FSItem.js:86 / rename.js:12-18 |
KV
| Function |
Item |
Issue |
Tag |
impl ref |
MAX_VALUE_SIZE |
Constant value |
Impl 399 * 1024 (399 KB); types comment & every value-size mention say 400 KB |
🔴 |
KV.js:36 |
set |
value max size |
"Maximum value size is 400 KB" — wrong, 399 KB |
🔴 |
KV.js:36 / kv.d.ts:128 / set.md:29,37 |
set (batch) |
KVSetItem.value / KVSetObject.value |
Both say 400 KB — wrong |
🟡 |
kv.d.ts:17,26 |
MAX_VALUE_SIZE prop |
Types doc comment |
(400 KB) — wrong |
🟡 |
kv.d.ts:122 |
incr |
amount = 0 |
!amountOrMap treats explicit 0 as falsy → increments by 1 |
🐛 |
KV.js:323 |
decr |
amount = 0 |
Same bug: decr(key, 0) decrements by 1 |
🐛 |
KV.js:353 |
list |
list(pattern, false) |
Types allow it, but pattern only captured when arg[1]===true → returns ALL keys unfiltered |
🐛 |
KV.js:618-627 |
list |
list(true, optConfig) |
Typed to return KVPair[], but impl returns keys only when a config 2nd arg follows |
🐛 / 🟡 |
KV.js:606 |
get |
object-form overload |
Impl accepts get({ key, optConfig }); no object overload in types |
🟡 |
KV.js:256 |
del |
object-form overload |
Impl accepts del({ key, optConfig }); no object overload in types |
🟡 |
KV.js:540 |
incr |
object-form overload |
Impl accepts incr({ key, pathAndAmountMap, optConfig }); no object overload |
🟡 |
KV.js:308 |
decr |
object-form overload |
Impl accepts decr({ key, pathAndAmountMap, optConfig }); no object overload |
🟡 |
KV.js:338 |
clear |
Docs |
Public alias for flush (typed) but no docs entry / not in KV.md functions |
🔵 |
KV.js:676 |
optConfig |
Docs |
{ appUuid } public option in all typed signatures; never mentioned in any KV doc |
🔵 |
KV.js:118 |
Verified correct: MAX_KEY_SIZE = 1024 ("1 KB") everywhere; add default 1 (correctly handles add(key, 0)); KVPair/KVListPage object docs vs interfaces.
Drivers
| Function |
Item |
Issue |
Tag |
impl ref |
call |
2-arg overload |
Impl supports call(iface, parameters) (method defaults to iface) — shown in the impl's own doc comment — but types declare only 3-arg & 4-arg forms |
🟡 |
Drivers.js:262 |
OS
| Function |
Item |
Issue |
Tag |
impl ref |
setAuthToken / setAPIOrigin |
Types |
Exist in impl & typed in drivers.d.ts, but os.d.ts omits them (borderline internal plumbing) |
🟡 |
OS.js:26,37 |
UI
| Function |
Item |
Issue |
Tag |
impl ref |
alert |
body_icon / icon options |
Typed & documented, and UIAlert supports body_icon, but the ALERT IPC handler forwards only message/buttons/type — dropped for apps |
🐛 |
IPC.js:230-238 |
notify |
type option |
Typed & documented, but showNotification IPC never forwards it to the GUI UINotification |
🐛 |
IPC.js:283-290 |
notify |
duration option |
Typed & documented ("Defaults to 5000"), but never forwarded by the IPC handler |
🐛 |
IPC.js:283-290 |
socialShare |
options optional |
Docs & types mark options optional, but IPC dereferences event.data.options.left unconditionally → socialShare(url) throws in the GUI |
🐛 |
IPC.js:329 |
socialShare |
return value |
Impl returns a promise via #postMessageWithCallback, but IPC never posts back original_msg_id → promise never resolves |
🟡 |
UI.js:729 |
showFontPicker |
defaultFont option |
Typed & honored by standalone fallback, but GUI path ignores it (reads only options.default) |
🐛 |
UIWindowFontPicker.js:47,58 / IPC.js:1350 |
showFontPicker |
default alias |
GUI & standalone both read default; FontPickerOptions omits it |
🟡 |
UIWindowFontPicker.js:58 |
showColorPicker |
defaultValue alias |
GUI reads defaultValue ?? defaultColor ?? default; ColorPickerOptions declares only defaultColor |
🟡 |
UIWindowColorPicker.js:77 |
showColorPicker |
default alias |
Same line reads default; not in ColorPickerOptions |
🟡 |
UIWindowColorPicker.js:77 |
showColorPicker |
Parameters section |
showColorPicker.md has NO Parameters section — neither positional nor options documented |
🔵 |
showColorPicker.md:9-14 |
prompt |
options / defaultValue |
Typed & read by IPC (defaultValue), but prompt.md stops at prompt(message, placeholder) |
🔵 |
prompt.md:9-14 |
setMenuItemIcon |
doc file |
Typed & impl, no docs / absent from UI.md index |
🔵 |
UI.js:1125 |
setMenuItemIconActive |
doc file |
Typed & impl, no docs |
🔵 |
UI.js:1129 |
setMenuItemChecked |
doc file |
Typed & impl, no docs |
🔵 |
UI.js:1133 |
requestUpgrade |
doc file |
Typed & impl, no docs |
🔵 |
UI.js:892 |
getEntriesFromDataTransferItems |
doc file |
Typed & impl, undocumented (helper) |
🔵 |
UI.js:1173 |
contextMenu |
theme option |
Typed (standalone-only), not in contextMenu.md |
🔵 |
UI.js:1147 |
setWindowPosition/setWindowX/setWindowY |
clamping |
Docs say "positive number" but IPC clamps x to [0, innerWidth-100], y to [taskbar_height, innerHeight-100]; undocumented |
🔵 |
IPC.js:1076-1106,1136-1194 |
launchApp |
app_name alias / callback / pseudonym form |
Typed & impl accept app_name, callback, and name#(as)pseudonym; launchApp.md documents none |
🔵 |
UI.js:1309-1327 |
Note: disableMenuItem/enableMenuItem are app-facing counterparts to the typed setMenuItem* trio but are neither typed nor documented (inconsistency). No phantom typed methods. createWindow handle return {id}, window-size clamping, and all event names (localeChanged/themeChanged/connection, onItemsOpened/onLaunchedWithItems/onWindowClose) match.
Workers
| Function |
Item |
Issue |
Tag |
impl ref |
getLoggingHandle |
method existence |
In impl & types but absent from all docs |
🔵 |
Workers.js:137 |
getLoggingHandle |
events / return |
Types expose only close+onLog; impl also dispatches a 'log' MessageEvent and defines start/cancel (ReadableStream) — untyped |
🟡 |
Workers.js:161-174 |
create |
workerName validity |
Docs/types say name may contain letters etc.; impl silently lowercases it (toLocaleLowerCase()) |
🔵 |
Workers.js:41 |
get / delete |
workerName |
Same silent lowercasing on lookup; undocumented |
🔵 |
Workers.js:100,115 |
exec |
x-puter-no-auth header |
Impl honors this header to suppress the auto-injected puter-auth token; undocumented escape hatch |
🔵 |
Workers.js:70-73 |
create |
Return errors |
WorkerDeployment.errors typed/documented as populated, but the success (only returning) path omits it |
🟡 |
Workers.js:50-56 |
router.md / types.md describe the deployed-worker runtime (not the SDK call surface) — internally consistent, nothing to map.
Hosting
| Function |
Item |
Issue |
Tag |
impl ref |
create |
single-arg overload |
Impl supports create('subdomain'), but types require dirPath and docs list only create(subdomain, dirPath)/create(options) |
🔴 |
Hosting.js:50-58 |
create |
options root_dir optionality |
Impl passes object through, accepts { subdomain } alone; types & docs mark root_dir required |
🔴 |
Hosting.js:76-78 |
update |
dirPath optionality |
Impl allows update('subdomain') → sends root_dir: null (disconnects dir); typed/documented as required, behavior undocumented |
🔴 |
Hosting.js:99 |
create/update/get/delete |
subdomain normalization |
All normalize xxx.puter.site/xxx.puter.com first arg to bare label; undocumented |
🔵 |
Hosting.js:53,64,90,113,129 |
list |
return filtering |
Impl filters out workers.puter.* subdomains; docs/types say "all subdomains" |
🔵 |
Hosting.js:44 |
Subdomain object doc matches the interface.
Networking
| Function |
Item |
Issue |
Tag |
impl ref |
PSocket.write |
ArrayBuffer branch |
data.write(...) calls a non-existent method on the ArrayBuffer → TypeError; the data.resize discriminator only exists on resizable ArrayBuffers, so plain ArrayBuffers fall to else → "Invalid data type". ArrayBuffer is typed & documented as supported but unusable |
🐛 (+🔴) |
PSocket.js:74-75 |
| socket events |
'drain' event |
Declared in the event list (PSocket.js:16) and SocketEvent union but never emitted; no on('drain') overload; docs omit it — phantom |
🟡 |
PSocket.js:16 |
puter.net.generateWispV1URL |
doc existence |
In impl & types but documented nowhere |
🔵 |
index.js:627 |
socket.write |
callback param |
Impl & types include write(data, callback); Socket.md/TLSSocket.md document only data |
🔵 |
PSocket.js:70 |
Emitted events otherwise match: open/data(Uint8Array)/close(boolean)/error(Error); PTLS tlsopen/tlsdata/tlsclose/error.
Peer
| Function |
Item |
Issue |
Tag |
impl ref |
PuterPeerConnection 'error' event |
payload type |
Type declares error: string, but impl always dispatches an Error object (and docs example uses event.error?.message) |
🟡 |
Peer.js:33-39,213,237,286 |
PuterPeerServer.message |
method existence |
Types declare public message(data), but only a private #message exists — phantom |
🟡 |
Peer.js:104 |
PuterPeerServer.close |
method existence |
Public close() in impl, absent from types and serve.md |
🔴 |
Peer.js:170 |
PuterPeerConnection.setRemoteDescription / addIceCandidate |
return shape |
async (return Promise<void>) in impl but typed as void |
🟡 |
Peer.js:327,331 |
AppConnection |
response field |
Typed readonly response?; AppConnection.md documents only usesSDK |
🔵 |
UI.js (AppConnection) |
Peer options (iceServers, forceRelay + default) match; connection/message/open/close events align.
Apps
| Function |
Item |
Issue |
Tag |
impl ref |
create |
title optionality |
Doc labels title "(required)" then says it defaults to name — contradictory; title is optional (types correct) |
🔵 |
Apps.js:90 |
create |
indexURL requiredness |
Doc says "created with no index page" if omitted, but impl throws 'Index URL is required' |
🔵 |
Apps.js:135-143 |
checkName |
doc existence |
In impl & types (apps.d.ts:195), no doc page / absent from Apps.md functions |
🔵 |
Apps.js:206 |
getDeveloperProfile |
doc existence |
In impl & types (apps.d.ts:196-197), no doc |
🔵 |
Apps.js:232 |
App / CreateAppResult object docs match their interfaces.
Auth
| Function |
Item |
Issue |
Tag |
impl ref |
getGlobalUsage |
existence |
Public method (hits /metering/globalUsage) absent from types AND docs entirely |
🔴 |
Auth.js:338 |
getMonthlyUsage |
nav/index |
Doc page exists but not listed in Auth.md functions |
🔵 |
Auth.js:258 |
getDetailedAppUsage |
nav/index |
Doc page exists but not listed in Auth.md functions |
🔵 |
Auth.js:296 |
whoami |
doc existence |
In types (auth.d.ts:119) but no doc page |
🔵 |
Auth.js:211 |
User object |
feature_flags, hasDevAccountAccess, otp |
In the User interface but not documented in user.md |
🔵 |
auth.d.ts:15,16,20 |
SignInResult / MonthlyUsage / DetailedAppUsage object docs match; signIn options & rejections match.
Perms
| Function |
Item |
Issue |
Tag |
impl ref |
requestReadAppRootDir |
existence |
Public request* method (takes app_uid) missing from both types and docs |
🔴 |
Perms.js:333 |
requestWriteAppRootDir |
existence |
Public request* method missing from both types and docs |
🔴 |
Perms.js:346 |
All 14 documented request* methods agree impl↔types↔docs on return shape. requestPermission deprecated alias and grant/revoke/group-mgmt are out of documented scope.
Utils (top-level, src/index.js)
| Function |
Item |
Issue |
Tag |
impl ref |
exit |
doc existence |
exit(statusCode?) in impl & types but entirely undocumented (no Utils/exit.md, not in Utils.md) |
🔵 |
index.js:1040 |
print |
escapeHTML option |
In impl & typed, but print.md documents only code |
🔵 |
index.js:1258,1267 |
print |
variadic form |
print(...args) prints every arg; docs & types show only a single text param |
🔴 |
index.js:1251,1264 |
env |
return values |
env.md lists only app/web/gui; impl also returns web-worker/service-worker/nodejs (types correct) |
🔵 |
index.js:357-388 |
on / off |
existence |
Public event API puter.on/off (e.g. auth.reauth_required) in impl but not declared in puter.d.ts |
🟡 |
index.js:972,979 |
randName default separator '-', appID (value, not getter), and return shapes all consistent.
🐛 impl-bug bucket (code fixes, not doc/type edits)
These advertise a param/option the impl silently ignores, declare a phantom method, or crash on documented input.
AI
chat — vision?: boolean typed but a user-supplied vision is never read (only set internally); passing { vision: true } has no effect. AI.js:733
txt2img — service?: string typed but impl reads only options.driver; service survives only via passthrough (likely phantom). ai.d.ts:182
FileSystem
FSItem.move(dest, overwrite, newName) — booleans passed positionally to fs.move, whose positional branch expects arg[2]=options object → overwrite/newName dropped. FSItem.js:89-90
FSItem.copy(dest, autoRename, overwrite) — same mis-map; autoRename/overwrite dropped (overwrite lands in the success callback slot). FSItem.js:93-94
move({ dedupeName }) — advertised in move.md but move.js never reads any dedupe key nor sends dedupe_name (phantom option). move.js:59-67
copy(source, dest, { dedupeName }) — 3-arg positional branch never extracts dedupeName/dedupe_name; only copy(options) honors it. copy.js:12-24,56
readdir(path, options) — documented form; positional branch binds arg[1] to success, so an options object there is lost. readdir.js:18-25
- (low-conf)
FSItem.rename(newName) — calls fs.rename(this.uid, ...) but rename.js else-branch stores arg[0] as path, so the uid is treated as a filesystem path. FSItem.js:86 / rename.js:12-18
KV
incr(key, 0) / decr(key, 0) — explicit 0 coerced to default 1 via !amountOrMap. KV.js:323,353
list(pattern, false[, optConfig]) — pattern only captured when 2nd arg is true; explicit false drops the pattern → returns all keys. KV.js:618-627
list(true, optConfig) — returnValues not set true when a config object follows → returns bare keys despite typed KVPair[]. KV.js:606
UI
alert — options.body_icon / options.icon never forwarded to the GUI. IPC.js:230-238
notify — options.type never forwarded to the GUI. IPC.js:283-290
notify — options.duration never forwarded to the GUI. IPC.js:283-290
socialShare(url) — crashes (TypeError) when options omitted despite being optional; and its returned promise never resolves (no original_msg_id reply). IPC.js:329,325-397
showFontPicker({ defaultFont }) — typed/standalone-honored option ignored by the GUI, which reads only options.default. UIWindowFontPicker.js:47,58
Workers
delete — dead/duplicated error branch: nested identical if (!driverResult.result) constructs new Error("Worker doesn't exist") but never throws it → the message is never surfaced. Workers.js:119-123
getLoggingHandle — driver args dropped: make_driver_method([], ...) called with two positional args and no arg_defs, so puter.authToken/workerName are interpreted as success/error callbacks and never sent → logging URL request goes out with no auth token or worker name. Workers.js:138
getLoggingHandle — unbound close: logStreamObject.close = socket.close assigns unbound; handle.close() throws an illegal-invocation TypeError (the non-enumerable cancel does it correctly). Workers.js:174
Networking
PSocket.write — ArrayBuffer branch calls data.write(...) on an ArrayBuffer (no such method) → TypeError; non-resizable ArrayBuffers never reach it and throw "Invalid data type" instead. PSocket.js:74-75 (same fragile data.resize detection at PTLS.js:102)
Peer
PuterPeerServer.message() — typed public method does not exist (only private #message) → phantom; TS compiles, runtime fails. Peer.js:104
Perms
#requestAppRootDir(access, app_uid) — hardcodes access: 'read' in the request body, ignoring the parameter → requestWriteAppRootDir requests read access, not write. Perms.js:361
#requestAppRootDir — references undefined variable app_or_uuid (should be app_uid) → ReferenceError if that branch is reached. Perms.js:367
requestReadAppRootDir error message typo: 'parameter app_uid must be a strinkg'. Perms.js:356
getDeveloperProfile — duplicated option parsing + double-Promise nesting (outer executor's return ignored); works only via the success ?? resUpper path (latent bug / dead code). Apps.js:232-268
Generated by an automated deep-audit routine. Each finding re-derived from impl + types + docs at commit 2c0a1b9. False-positive filters applied per the audit spec (internal _-suffixed methods, cosmetic param-name diffs, intentionally-unknown provider payloads, internal plumbing).
Full deep audit of the puter.js SDK for TypeScript drift and documentation drift against the implementation. Every API family was re-derived from scratch (impl + types + docs read in full).
2c0a1b9bbbbb8ddce52d5afc1b4d7afc8b1fb961(2c0a1b9)src/puter-js/src/modules/(+src/puter-js/src/index.js, andsrc/gui/src/IPC.jsfor the UI family) · typessrc/puter-js/types/· docssrc/docs/src/Legend
🟡 types (impl/docs have it, types don't/wrong) · 🔵 docs (impl/types have it, docs don't/wrong) · 🔴 both · 🐛 impl-bug (advertised param the impl ignores, or phantom typed method) · 🆕/🗑 manifest-drift
🎯 Highest-impact shortlist (cross-family)
list(pattern, false)silently ignores the pattern — a form the types explicitly sanction; returns the entire keyspace instead of the filtered subset.KV.js:618incr(key, 0)/decr(key, 0)off-by-one — explicit0is coerced to the default1.KV.js:323,353MAX_VALUE_SIZEdocumented as 400 KB but is399 * 1024(399 KB) — wrong in 6 places across types + docs.KV.js:36FSItem.move()/FSItem.copy()advertised params silently ignored —overwrite/newName/autoRenameare typed & accepted but positionally mis-mapped into the wrongfs.move/fs.copyargument slots.FSItem.js:89-94FSItem.isDirvs implisDirectory— the documented/typed property does not exist on the instance; every consumer readingitem.isDirgetsundefined.FSItem.js:17notifytype&durationsilently dropped for apps — two prominently documented options that do nothing in the real Puter desktop (IPC never forwards them).IPC.js:283-290socialShare(url)throws in the GUI — documented "options optional", but the IPC handler dereferencesevent.data.options.leftunconditionally.IPC.js:329PSocket.writeArrayBuffer branch throws — callsdata.write(...)on an ArrayBuffer; ArrayBuffer input is typed/documented as supported but crashes.PSocket.js:74-75getGlobalUsageshipping with zero types and zero docs.Auth.js:338requestReadAppRootDir/requestWriteAppRootDirmissing from types + docs — and they carry live impl bugs (hardcodedreadaccess; undefined-variableReferenceError).Perms.js:333,346create('subdomain')single-arg overload untyped — real, explicitly-coded form fails typecheck (types requiredirPath).Hosting.js:50chatstreamingerror&imagechunk types — real emitted chunk types missing from bothChatResponseChunkand the object doc; theerrorchunk especially matters since errors arrive as a chunk, not a throw.ai.d.ts:108🆕/🗑 Manifest-drift (disk vs Expected manifest)
src/docs/src/Objects/ttsengine.mdAI/txt2speech.listEngines)src/docs/src/Objects/ttsvoice.mdAI/txt2speech.listVoices)Everything else on disk matches the manifest exactly (all impl modules, FS ops, networking, UI components, types, and docs families/pages present as expected). No removed/renamed files.
AI
chatprovideroptionchatdriveroptionprovider) & typed, but undocumentedchatcontext_managementoptioncompactionchatresponseoptionchatvisionoptionChatOptions.vision?: booleantyped, but impl never reads a user-suppliedvision(only sets it internally) — value silently droppedchat(stream)"error"chunk.message) but absent fromChatResponseChunk.typeenum & chatresponsechunk.mdchat(stream)"image"chunkpart.image) but absent from type enum,imagefield & chatresponsechunk.mdchatmessage.imagestypingChatMessage.imagesnon-optional but only present on image-gen responses → should beimages?chatimage_url.thoughtSignatureImageContentdoesn't model itChatResponsechoicesfieldchoices?: unknown; not in chatresponse.md (borderline)speech2speechvoiceIdaliasvoiceId→voice; missing from types & docsspeech2speechmodelIdaliasmodelId→model; missing from types & docsspeech2speechoutputFormataliasoutput_format; missing from types & docsspeech2speechvoiceSettingsaliasvoice_settings; missing from types & docsspeech2speechfileFormataliasfile_format; missing from types & docsspeech2speechremoveBackgroundNoisealiasremove_background_noise; not in types; docs use it in an example but options list documents only snake_casespeech2speechoptimizeStreamingLatencyaliasoptimize_streaming_latency; missing from types & docsspeech2speechenableLoggingaliasenable_logging; missing from types & docstxt2viddurationaliasduration→seconds, typed, but txt2vid.md documents onlysecondstxt2viddriveroptiontxt2imgdriveroptionoptions.driver(notprovider) for service override; typed but txt2img.md documentsprovideronlytxt2imgserviceoptionservice?: stringbut impl never reads it (onlydriver) — possible phantomtxt2imgnano-banana/nano-banana-promodel aliasestxt2speech.listVoicesenginestring; doc Syntax block omits the formFileSystem (FS)
FSItemisDirectoryvsisDirisDirectory; types & docs call itisDir— property does not existFSItem.move()overwrite,newNameparamsfs.move, which expects arg[2]=options object → both ignoredFSItem.copy()autoRename,overwriteparamsFSItemmove(),copy()methodsread()cacheoptionoptions.cache; missing fromReadOptionsand read.mdreaddir()no_subdomainsReaddirOptionsreaddir()consistency,no_thumbs,no_assocs,no_subdomainspath,uidreaddir()readdir(path, options)overloadsuccesscallback — options mis-handledstat()returnWorkersaliasreturnSubdomains || returnWorkers; alias missing fromStatOptionsstat()consistencymkdir()renamealiasrename || dedupeName; alias typed but not in mkdir.mdmkdir()recursivealiasrecursive || createMissingParents; typed but not in mkdir.mdmkdir()shortcutToshortcut_to; typed but not in mkdir.mdmove()dedupeNameoptiondedupe_name— phantommove()newNameoptionnewName/new_name; move.md omits itcopy()dedupeNamein positional formdedupeName/dedupe_namefrom arg[2]; onlycopy(options)form worksupload()generateThumbnails,thumbnailGenerator,thumbnailUploadOptionsupload()appUID,shortcutTo,createFileParent, …overwrite,dedupeName,createMissingParentsrevokeReadURL()puter.fsbut absent fromFStypes class and no docreaddirSubdomains()puter.fsbut absent fromFStypes class and no docsign()SignResult) & implemented, but no FS/sign doc pagespace()(success, error)and an options object; example callsputer.space()notputer.fs.space()FSItemuid/uuid,readURL/writeURL/metadataURLFSItem.rename()uidvspathfs.rename(this.uid, ...); rename.js else-branch treats arg[0] aspath, so uid may be sent as a pathKV
MAX_VALUE_SIZE399 * 1024(399 KB); types comment & every value-size mention say 400 KBsetvaluemax sizeset(batch)KVSetItem.value/KVSetObject.valueMAX_VALUE_SIZEprop(400 KB)— wrongincramount = 0!amountOrMaptreats explicit0as falsy → increments by 1decramount = 0decr(key, 0)decrements by 1listlist(pattern, false)listlist(true, optConfig)KVPair[], but impl returns keys only when a config 2nd arg followsgetget({ key, optConfig }); no object overload in typesdeldel({ key, optConfig }); no object overload in typesincrincr({ key, pathAndAmountMap, optConfig }); no object overloaddecrdecr({ key, pathAndAmountMap, optConfig }); no object overloadclearflush(typed) but no docs entry / not in KV.md functionsoptConfig{ appUuid }public option in all typed signatures; never mentioned in any KV docVerified correct:
MAX_KEY_SIZE = 1024("1 KB") everywhere;adddefault1(correctly handlesadd(key, 0));KVPair/KVListPageobject docs vs interfaces.Drivers
callcall(iface, parameters)(method defaults to iface) — shown in the impl's own doc comment — but types declare only 3-arg & 4-arg formsOS
setAuthToken/setAPIOriginos.d.tsomits them (borderline internal plumbing)UI
alertbody_icon/iconoptionsUIAlertsupportsbody_icon, but the ALERT IPC handler forwards onlymessage/buttons/type— dropped for appsnotifytypeoptionshowNotificationIPC never forwards it to the GUIUINotificationnotifydurationoptionsocialShareoptionsoptional, but IPC dereferencesevent.data.options.leftunconditionally →socialShare(url)throws in the GUIsocialShare#postMessageWithCallback, but IPC never posts backoriginal_msg_id→ promise never resolvesshowFontPickerdefaultFontoptionoptions.default)showFontPickerdefaultaliasdefault;FontPickerOptionsomits itshowColorPickerdefaultValuealiasdefaultValue ?? defaultColor ?? default;ColorPickerOptionsdeclares onlydefaultColorshowColorPickerdefaultaliasdefault; not inColorPickerOptionsshowColorPickerpromptoptions/defaultValuedefaultValue), but prompt.md stops atprompt(message, placeholder)setMenuItemIconsetMenuItemIconActivesetMenuItemCheckedrequestUpgradegetEntriesFromDataTransferItemscontextMenuthemeoptionsetWindowPosition/setWindowX/setWindowY[0, innerWidth-100], y to[taskbar_height, innerHeight-100]; undocumentedlaunchAppapp_namealias /callback/ pseudonym formapp_name,callback, andname#(as)pseudonym; launchApp.md documents noneNote:
disableMenuItem/enableMenuItemare app-facing counterparts to the typedsetMenuItem*trio but are neither typed nor documented (inconsistency). No phantom typed methods.createWindowhandle return{id}, window-size clamping, and all event names (localeChanged/themeChanged/connection,onItemsOpened/onLaunchedWithItems/onWindowClose) match.Workers
getLoggingHandlegetLoggingHandleclose+onLog; impl also dispatches a'log'MessageEvent and definesstart/cancel(ReadableStream) — untypedcreatetoLocaleLowerCase())get/deleteexecx-puter-no-authheaderputer-authtoken; undocumented escape hatchcreateerrorsWorkerDeployment.errorstyped/documented as populated, but the success (only returning) path omits itrouter.md / types.md describe the deployed-worker runtime (not the SDK call surface) — internally consistent, nothing to map.
Hosting
createcreate('subdomain'), but types requiredirPathand docs list onlycreate(subdomain, dirPath)/create(options)createroot_diroptionality{ subdomain }alone; types & docs markroot_dirrequiredupdatedirPathoptionalityupdate('subdomain')→ sendsroot_dir: null(disconnects dir); typed/documented as required, behavior undocumentedcreate/update/get/deletexxx.puter.site/xxx.puter.comfirst arg to bare label; undocumentedlistworkers.puter.*subdomains; docs/types say "all subdomains"Subdomain object doc matches the interface.
Networking
PSocket.writedata.write(...)calls a non-existent method on the ArrayBuffer → TypeError; thedata.resizediscriminator only exists on resizable ArrayBuffers, so plain ArrayBuffers fall toelse→ "Invalid data type". ArrayBuffer is typed & documented as supported but unusable'drain'eventSocketEventunion but never emitted; noon('drain')overload; docs omit it — phantomputer.net.generateWispV1URLsocket.writecallbackparamwrite(data, callback); Socket.md/TLSSocket.md document onlydataEmitted events otherwise match:
open/data(Uint8Array)/close(boolean)/error(Error); PTLStlsopen/tlsdata/tlsclose/error.Peer
PuterPeerConnection'error'eventerror: string, but impl always dispatches anErrorobject (and docs example usesevent.error?.message)PuterPeerServer.messagemessage(data), but only a private#messageexists — phantomPuterPeerServer.closeclose()in impl, absent from types and serve.mdPuterPeerConnection.setRemoteDescription/addIceCandidateasync(returnPromise<void>) in impl but typed asvoidAppConnectionresponsefieldreadonly response?; AppConnection.md documents onlyusesSDKPeer options (
iceServers,forceRelay+ default) match;connection/message/open/closeevents align.Apps
createtitleoptionalitytitle"(required)" then says it defaults toname— contradictory; title is optional (types correct)createindexURLrequiredness'Index URL is required'checkNamegetDeveloperProfileApp / CreateAppResult object docs match their interfaces.
Auth
getGlobalUsage/metering/globalUsage) absent from types AND docs entirelygetMonthlyUsagegetDetailedAppUsagewhoamiUserobjectfeature_flags,hasDevAccountAccess,otpSignInResult / MonthlyUsage / DetailedAppUsage object docs match; signIn options & rejections match.
Perms
requestReadAppRootDirrequest*method (takesapp_uid) missing from both types and docsrequestWriteAppRootDirrequest*method missing from both types and docsAll 14 documented
request*methods agree impl↔types↔docs on return shape.requestPermissiondeprecated alias and grant/revoke/group-mgmt are out of documented scope.Utils (top-level,
src/index.js)exitexit(statusCode?)in impl & types but entirely undocumented (no Utils/exit.md, not in Utils.md)printescapeHTMLoptioncodeprintprint(...args)prints every arg; docs & types show only a singletextparamenvapp/web/gui; impl also returnsweb-worker/service-worker/nodejs(types correct)on/offputer.on/off(e.g.auth.reauth_required) in impl but not declared in puter.d.tsrandNamedefault separator'-',appID(value, not getter), and return shapes all consistent.🐛 impl-bug bucket (code fixes, not doc/type edits)
These advertise a param/option the impl silently ignores, declare a phantom method, or crash on documented input.
AI
chat—vision?: booleantyped but a user-suppliedvisionis never read (only set internally); passing{ vision: true }has no effect.AI.js:733txt2img—service?: stringtyped but impl reads onlyoptions.driver;servicesurvives only via passthrough (likely phantom).ai.d.ts:182FileSystem
FSItem.move(dest, overwrite, newName)— booleans passed positionally tofs.move, whose positional branch expects arg[2]=options object →overwrite/newNamedropped.FSItem.js:89-90FSItem.copy(dest, autoRename, overwrite)— same mis-map;autoRename/overwritedropped (overwritelands in thesuccesscallback slot).FSItem.js:93-94move({ dedupeName })— advertised in move.md but move.js never reads any dedupe key nor sendsdedupe_name(phantom option).move.js:59-67copy(source, dest, { dedupeName })— 3-arg positional branch never extractsdedupeName/dedupe_name; onlycopy(options)honors it.copy.js:12-24,56readdir(path, options)— documented form; positional branch binds arg[1] tosuccess, so an options object there is lost.readdir.js:18-25FSItem.rename(newName)— callsfs.rename(this.uid, ...)but rename.js else-branch stores arg[0] aspath, so the uid is treated as a filesystem path.FSItem.js:86 / rename.js:12-18KV
incr(key, 0)/decr(key, 0)— explicit0coerced to default1via!amountOrMap.KV.js:323,353list(pattern, false[, optConfig])— pattern only captured when 2nd arg istrue; explicitfalsedrops the pattern → returns all keys.KV.js:618-627list(true, optConfig)—returnValuesnot set true when a config object follows → returns bare keys despite typedKVPair[].KV.js:606UI
alert—options.body_icon/options.iconnever forwarded to the GUI.IPC.js:230-238notify—options.typenever forwarded to the GUI.IPC.js:283-290notify—options.durationnever forwarded to the GUI.IPC.js:283-290socialShare(url)— crashes (TypeError) whenoptionsomitted despite being optional; and its returned promise never resolves (nooriginal_msg_idreply).IPC.js:329,325-397showFontPicker({ defaultFont })— typed/standalone-honored option ignored by the GUI, which reads onlyoptions.default.UIWindowFontPicker.js:47,58Workers
delete— dead/duplicated error branch: nested identicalif (!driverResult.result)constructsnew Error("Worker doesn't exist")but never throws it → the message is never surfaced.Workers.js:119-123getLoggingHandle— driver args dropped:make_driver_method([], ...)called with two positional args and no arg_defs, soputer.authToken/workerNameare interpreted as success/error callbacks and never sent → logging URL request goes out with no auth token or worker name.Workers.js:138getLoggingHandle— unboundclose:logStreamObject.close = socket.closeassigns unbound;handle.close()throws an illegal-invocationTypeError(the non-enumerablecanceldoes it correctly).Workers.js:174Networking
PSocket.write— ArrayBuffer branch callsdata.write(...)on an ArrayBuffer (no such method) → TypeError; non-resizable ArrayBuffers never reach it and throw "Invalid data type" instead.PSocket.js:74-75(same fragiledata.resizedetection atPTLS.js:102)Peer
PuterPeerServer.message()— typed public method does not exist (only private#message) → phantom; TS compiles, runtime fails.Peer.js:104Perms
#requestAppRootDir(access, app_uid)— hardcodesaccess: 'read'in the request body, ignoring the parameter →requestWriteAppRootDirrequests read access, not write.Perms.js:361#requestAppRootDir— references undefined variableapp_or_uuid(should beapp_uid) →ReferenceErrorif that branch is reached.Perms.js:367requestReadAppRootDirerror message typo:'parameter app_uid must be a strinkg'.Perms.js:356getDeveloperProfile— duplicated option parsing + double-Promise nesting (outer executor's return ignored); works only via thesuccess ?? resUpperpath (latent bug / dead code).Apps.js:232-268Generated by an automated deep-audit routine. Each finding re-derived from impl + types + docs at commit
2c0a1b9. False-positive filters applied per the audit spec (internal_-suffixed methods, cosmetic param-name diffs, intentionally-unknownprovider payloads, internal plumbing).