@needmoretruth/nmts-cli 0.17.2 → 0.34.1

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.
Files changed (419) hide show
  1. package/AGENTS.md +124 -516
  2. package/CHANGELOG.md +98 -0
  3. package/README.ko.md +374 -467
  4. package/README.md +384 -465
  5. package/dist/account-proof.d.ts +29 -0
  6. package/dist/account-proof.js +6 -6
  7. package/dist/account.d.ts +18 -0
  8. package/dist/account.js +5 -5
  9. package/dist/agent-host.d.ts +71 -0
  10. package/dist/agent-host.js +169 -0
  11. package/dist/api-advice.d.ts +2 -0
  12. package/dist/api-advice.js +210 -0
  13. package/dist/api-key.d.ts +79 -0
  14. package/dist/api-key.js +7 -7
  15. package/dist/api-refusal.d.ts +14 -0
  16. package/dist/api-refusal.js +8 -0
  17. package/dist/api.d.ts +114 -0
  18. package/dist/api.js +59 -162
  19. package/dist/args.d.ts +160 -0
  20. package/dist/args.js +118 -30
  21. package/dist/artifact-about.d.ts +49 -0
  22. package/dist/autonomy.d.ts +39 -0
  23. package/dist/autonomy.js +110 -32
  24. package/dist/code-access.d.ts +29 -0
  25. package/dist/code-access.js +3 -3
  26. package/dist/code-vault.d.ts +38 -0
  27. package/dist/code-vault.js +8 -8
  28. package/dist/collision.d.ts +67 -0
  29. package/dist/collision.js +1 -1
  30. package/dist/commands/accept-terms.d.ts +11 -0
  31. package/dist/commands/accept-terms.js +106 -0
  32. package/dist/commands/balance.d.ts +7 -0
  33. package/dist/commands/balance.js +54 -1
  34. package/dist/commands/create-code-file.d.ts +24 -0
  35. package/dist/commands/create-code-file.js +93 -0
  36. package/dist/commands/create-link.d.ts +10 -0
  37. package/dist/commands/create-link.js +232 -0
  38. package/dist/commands/create.d.ts +20 -0
  39. package/dist/commands/create.js +43 -102
  40. package/dist/commands/credits.d.ts +9 -0
  41. package/dist/commands/credits.js +110 -0
  42. package/dist/commands/delete-account.d.ts +11 -0
  43. package/dist/commands/delete-account.js +63 -0
  44. package/dist/commands/deposit.d.ts +7 -0
  45. package/dist/commands/deposit.js +85 -0
  46. package/dist/commands/devices.d.ts +12 -0
  47. package/dist/commands/devices.js +144 -0
  48. package/dist/commands/documents.d.ts +35 -0
  49. package/dist/commands/documents.js +212 -0
  50. package/dist/commands/env.d.ts +5 -0
  51. package/dist/commands/env.js +10 -2
  52. package/dist/commands/erase.d.ts +13 -0
  53. package/dist/commands/erase.js +193 -0
  54. package/dist/commands/expiring.d.ts +19 -0
  55. package/dist/commands/extend.d.ts +33 -0
  56. package/dist/commands/extend.js +54 -15
  57. package/dist/commands/get.d.ts +18 -0
  58. package/dist/commands/key-manage.d.ts +11 -0
  59. package/dist/commands/key-manage.js +162 -0
  60. package/dist/commands/key.d.ts +28 -0
  61. package/dist/commands/key.js +249 -0
  62. package/dist/commands/kit.d.ts +11 -0
  63. package/dist/commands/kit.js +7 -7
  64. package/dist/commands/listfile.d.ts +13 -0
  65. package/dist/commands/listfile.js +12 -5
  66. package/dist/commands/login.d.ts +18 -0
  67. package/dist/commands/login.js +19 -19
  68. package/dist/commands/logout.d.ts +1 -0
  69. package/dist/commands/logout.js +1 -1
  70. package/dist/commands/losses.d.ts +12 -0
  71. package/dist/commands/losses.js +180 -0
  72. package/dist/commands/ls.d.ts +23 -0
  73. package/dist/commands/marks-dispatch.d.ts +2 -0
  74. package/dist/commands/marks-dispatch.js +51 -0
  75. package/dist/commands/marks.d.ts +46 -0
  76. package/dist/commands/marks.js +80 -0
  77. package/dist/commands/mcp.d.ts +24 -0
  78. package/dist/commands/mcp.js +70 -5
  79. package/dist/commands/mode.d.ts +8 -0
  80. package/dist/commands/mode.js +60 -24
  81. package/dist/commands/on-collision.d.ts +6 -0
  82. package/dist/commands/organise.d.ts +46 -0
  83. package/dist/commands/padding.d.ts +7 -0
  84. package/dist/commands/padding.js +114 -0
  85. package/dist/commands/public-code.d.ts +9 -0
  86. package/dist/commands/public-code.js +8 -8
  87. package/dist/commands/pull.d.ts +27 -0
  88. package/dist/commands/push-wallet.d.ts +19 -0
  89. package/dist/commands/push-wallet.js +69 -0
  90. package/dist/commands/push.d.ts +47 -0
  91. package/dist/commands/push.js +37 -7
  92. package/dist/commands/put-wallet.d.ts +77 -0
  93. package/dist/commands/put-wallet.js +316 -0
  94. package/dist/commands/put.d.ts +56 -0
  95. package/dist/commands/put.js +53 -6
  96. package/dist/commands/rebuild.d.ts +18 -0
  97. package/dist/commands/rebuild.js +108 -10
  98. package/dist/commands/receive.d.ts +24 -0
  99. package/dist/commands/recovery-dispatch.d.ts +2 -0
  100. package/dist/commands/recovery-dispatch.js +34 -0
  101. package/dist/commands/recovery-list.d.ts +11 -0
  102. package/dist/commands/recovery-list.js +6 -6
  103. package/dist/commands/recovery.d.ts +25 -0
  104. package/dist/commands/recovery.js +2 -2
  105. package/dist/commands/rollback.d.ts +9 -0
  106. package/dist/commands/rollback.js +118 -0
  107. package/dist/commands/s3.d.ts +23 -0
  108. package/dist/commands/s3.js +6 -6
  109. package/dist/commands/settings.d.ts +5 -0
  110. package/dist/commands/settings.js +21 -6
  111. package/dist/commands/share-dispatch.d.ts +2 -0
  112. package/dist/commands/share-dispatch.js +46 -0
  113. package/dist/commands/share.d.ts +14 -0
  114. package/dist/commands/share.js +13 -3
  115. package/dist/commands/shares-sent.d.ts +18 -0
  116. package/dist/commands/shares-sent.js +95 -0
  117. package/dist/commands/support.d.ts +24 -0
  118. package/dist/commands/support.js +321 -0
  119. package/dist/commands/sweep.d.ts +11 -0
  120. package/dist/commands/tip.d.ts +11 -0
  121. package/dist/commands/tip.js +86 -0
  122. package/dist/commands/trash.d.ts +17 -0
  123. package/dist/commands/trash.js +1 -1
  124. package/dist/commands/trial.d.ts +7 -0
  125. package/dist/commands/trial.js +16 -18
  126. package/dist/commands/unlock.d.ts +16 -0
  127. package/dist/commands/unlock.js +159 -0
  128. package/dist/commands/update.d.ts +42 -0
  129. package/dist/commands/update.js +1 -1
  130. package/dist/commands/usage.d.ts +8 -0
  131. package/dist/commands/verify.d.ts +18 -0
  132. package/dist/commands/verify.js +10 -6
  133. package/dist/commands/wallet-activity.d.ts +26 -0
  134. package/dist/commands/wallet-activity.js +117 -0
  135. package/dist/commands/wallet-donate.d.ts +26 -0
  136. package/dist/commands/wallet-donate.js +163 -0
  137. package/dist/commands/wallet-hall.d.ts +35 -0
  138. package/dist/commands/wallet-hall.js +214 -0
  139. package/dist/commands/wallet-send.d.ts +22 -0
  140. package/dist/commands/wallet-send.js +153 -0
  141. package/dist/commands/wallet-storage-ops.d.ts +24 -0
  142. package/dist/commands/wallet-storage-ops.js +215 -0
  143. package/dist/commands/wallet-storage.d.ts +18 -0
  144. package/dist/commands/wallet-storage.js +95 -0
  145. package/dist/commands/wallet-swap.d.ts +32 -0
  146. package/dist/commands/wallet-swap.js +287 -0
  147. package/dist/commands/wallet.d.ts +36 -0
  148. package/dist/commands/wallet.js +57 -15
  149. package/dist/commands/whoami.d.ts +10 -0
  150. package/dist/commands/whoami.js +24 -6
  151. package/dist/consent.d.ts +140 -0
  152. package/dist/consent.js +89 -30
  153. package/dist/credentials.d.ts +146 -0
  154. package/dist/credentials.js +10 -10
  155. package/dist/crypto-surface.d.ts +197 -0
  156. package/dist/crypto.d.ts +81 -0
  157. package/dist/crypto.js +13 -0
  158. package/dist/deposit.d.ts +25 -0
  159. package/dist/deposit.js +78 -0
  160. package/dist/download-part.d.ts +40 -0
  161. package/dist/download-sink.d.ts +76 -0
  162. package/dist/download.d.ts +79 -0
  163. package/dist/download.js +3 -3
  164. package/dist/drive-paths.d.ts +73 -0
  165. package/dist/environment.d.ts +62 -0
  166. package/dist/environment.js +29 -9
  167. package/dist/errors.d.ts +22 -0
  168. package/dist/errors.js +3 -3
  169. package/dist/exit.d.ts +44 -0
  170. package/dist/expiry.d.ts +58 -0
  171. package/dist/extend-budget.d.ts +35 -0
  172. package/dist/extend-budget.js +81 -0
  173. package/dist/extend-chain.d.ts +43 -0
  174. package/dist/extend-chain.js +40 -3
  175. package/dist/extend-plan.d.ts +148 -0
  176. package/dist/extend-plan.js +1 -1
  177. package/dist/gate.d.ts +18 -0
  178. package/dist/gate.js +100 -0
  179. package/dist/guards.d.ts +2 -0
  180. package/dist/help.d.ts +20 -0
  181. package/dist/help.js +199 -33
  182. package/dist/human-check.d.ts +23 -0
  183. package/dist/index.d.ts +42 -0
  184. package/dist/index.js +49 -0
  185. package/dist/item-trash.d.ts +1 -0
  186. package/dist/kit-file.d.ts +56 -0
  187. package/dist/kit-file.js +10 -10
  188. package/dist/list-file.d.ts +88 -0
  189. package/dist/list-file.js +90 -7
  190. package/dist/list-view-find.d.ts +15 -0
  191. package/dist/list-view-order.d.ts +32 -0
  192. package/dist/main.d.ts +12 -0
  193. package/dist/main.js +112 -113
  194. package/dist/manifest-chunk-cache.d.ts +14 -0
  195. package/dist/manifest-chunk-cache.js +103 -0
  196. package/dist/manifest-chunk-flow.d.ts +81 -0
  197. package/dist/manifest-chunk-flow.js +307 -0
  198. package/dist/manifest-create.d.ts +23 -0
  199. package/dist/manifest-create.js +26 -30
  200. package/dist/manifest-write.d.ts +161 -0
  201. package/dist/manifest-write.js +42 -33
  202. package/dist/manifest.d.ts +82 -0
  203. package/dist/manifest.js +69 -21
  204. package/dist/mark-render.d.ts +26 -0
  205. package/dist/mcp-args.d.ts +17 -0
  206. package/dist/mcp-ask.d.ts +43 -0
  207. package/dist/mcp-ask.js +100 -0
  208. package/dist/mcp-tools/context.d.ts +46 -0
  209. package/dist/mcp-tools/credits.d.ts +3 -0
  210. package/dist/mcp-tools/credits.js +61 -0
  211. package/dist/mcp-tools/files.d.ts +3 -0
  212. package/dist/mcp-tools/files.js +34 -0
  213. package/dist/mcp-tools/organise.d.ts +3 -0
  214. package/dist/mcp-tools/organise.js +82 -2
  215. package/dist/mcp-tools/reads.d.ts +3 -0
  216. package/dist/mcp-tools/reads.js +132 -4
  217. package/dist/mcp-tools/share.d.ts +3 -0
  218. package/dist/mcp-tools/share.js +26 -7
  219. package/dist/mcp-tools/support.d.ts +3 -0
  220. package/dist/mcp-tools/support.js +140 -0
  221. package/dist/mcp-tools/tiers.d.ts +31 -0
  222. package/dist/mcp-tools/tiers.js +147 -0
  223. package/dist/mcp.d.ts +80 -0
  224. package/dist/mcp.js +110 -9
  225. package/dist/net-retry.d.ts +38 -0
  226. package/dist/network.d.ts +10 -0
  227. package/dist/notice.d.ts +7 -0
  228. package/dist/notice.js +7 -7
  229. package/dist/product.d.ts +29 -0
  230. package/dist/product.js +1 -1
  231. package/dist/progress.d.ts +37 -0
  232. package/dist/prompt.d.ts +31 -0
  233. package/dist/prompt.js +1 -1
  234. package/dist/rebuild-key-check.d.ts +26 -0
  235. package/dist/rebuild-key-check.js +90 -0
  236. package/dist/rebuild.d.ts +87 -0
  237. package/dist/rebuild.js +26 -4
  238. package/dist/recovery-assemble.d.ts +40 -0
  239. package/dist/recovery-assemble.js +1 -1
  240. package/dist/recovery-build.d.ts +40 -0
  241. package/dist/recovery-map-file.d.ts +53 -0
  242. package/dist/recovery-map-file.js +4 -4
  243. package/dist/recovery-map.d.ts +197 -0
  244. package/dist/recovery-release.d.ts +104 -0
  245. package/dist/recovery-seq.d.ts +10 -0
  246. package/dist/recovery-source.d.ts +50 -0
  247. package/dist/redact.d.ts +70 -0
  248. package/dist/redact.js +212 -0
  249. package/dist/registration.d.ts +30 -0
  250. package/dist/registration.js +5 -5
  251. package/dist/risk.d.ts +380 -0
  252. package/dist/risk.js +206 -0
  253. package/dist/run-log.d.ts +78 -0
  254. package/dist/run-log.js +301 -0
  255. package/dist/s3/listing.d.ts +47 -0
  256. package/dist/s3/multipart.d.ts +21 -0
  257. package/dist/s3/response-sink.d.ts +8 -0
  258. package/dist/s3/same-file.d.ts +54 -0
  259. package/dist/s3/server.d.ts +58 -0
  260. package/dist/s3/server.js +2 -2
  261. package/dist/s3/sigv4.d.ts +46 -0
  262. package/dist/s3/sigv4.js +1 -1
  263. package/dist/s3/staging.d.ts +10 -0
  264. package/dist/s3/xml.d.ts +28 -0
  265. package/dist/safe-path.d.ts +41 -0
  266. package/dist/seal.d.ts +120 -0
  267. package/dist/secret-reader.d.ts +28 -0
  268. package/dist/server.d.ts +9 -0
  269. package/dist/session.d.ts +32 -0
  270. package/dist/session.js +1 -1
  271. package/dist/setup-questions.d.ts +7 -0
  272. package/dist/share.d.ts +108 -0
  273. package/dist/share.js +1 -1
  274. package/dist/shared/lib/crypto/size-padding.d.ts +55 -0
  275. package/dist/shared/lib/crypto/size-padding.js +5 -0
  276. package/dist/shared/lib/drive/manifest-chunks.d.ts +95 -0
  277. package/dist/shared/lib/drive/manifest-chunks.js +252 -0
  278. package/dist/shared/lib/drive/manifest-codec.d.ts +214 -0
  279. package/dist/shared/lib/drive/manifest-codec.js +7 -7
  280. package/dist/shared/lib/drive/manifest-index.d.ts +98 -0
  281. package/dist/shared/lib/drive/manifest-ops.d.ts +167 -0
  282. package/dist/shared/lib/drive/manifest-ops.js +9 -4
  283. package/dist/shared/lib/drive/manifest-pack.d.ts +65 -0
  284. package/dist/shared/lib/drive/manifest-pack.js +287 -0
  285. package/dist/shared/lib/drive/manifest-settings.d.ts +113 -0
  286. package/dist/shared/lib/drive/manifest-settings.js +87 -6
  287. package/dist/shared/lib/drive/name-conflict.d.ts +76 -0
  288. package/dist/shared/lib/drive/rebuild-verify.d.ts +73 -0
  289. package/dist/shared/lib/drive/rebuild-verify.js +112 -0
  290. package/dist/shared/lib/drive/unique-name.d.ts +9 -0
  291. package/dist/shared/lib/drive/zstd.d.ts +27 -0
  292. package/dist/shared/lib/drive/zstd.js +74 -0
  293. package/dist/shared/lib/extend/epochs.d.ts +205 -0
  294. package/dist/shared/lib/net/retry-budget.d.ts +79 -0
  295. package/dist/shared/lib/share/shared-file-info.d.ts +21 -0
  296. package/dist/shared/lib/storage-control/chain.d.ts +57 -0
  297. package/dist/shared/lib/storage-control/chain.js +82 -0
  298. package/dist/shared/lib/storage-control/plan.d.ts +40 -0
  299. package/dist/shared/lib/storage-control/plan.js +72 -0
  300. package/dist/shared/lib/storage-control/reuse.d.ts +72 -0
  301. package/dist/shared/lib/storage-control/reuse.js +95 -0
  302. package/dist/shared/lib/storage-network.d.ts +25 -0
  303. package/dist/shared/lib/upload/part-plan.d.ts +24 -0
  304. package/dist/shared/lib/wallet/activity.d.ts +138 -0
  305. package/dist/shared/lib/wallet/activity.js +208 -0
  306. package/dist/shared/lib/wallet/send-rules.d.ts +68 -0
  307. package/dist/shared/lib/wallet/send-rules.js +116 -0
  308. package/dist/shared/lib/wallet/swap-rules.d.ts +115 -0
  309. package/dist/shared/lib/wallet/swap-rules.js +199 -0
  310. package/dist/shared/lib/wallet/tip.d.ts +14 -0
  311. package/dist/shared/lib/wallet/tip.js +34 -0
  312. package/dist/shared/lib/wallet/venue-ids.d.ts +23 -0
  313. package/dist/shared/lib/wallet/venue-ids.js +53 -0
  314. package/dist/standing-tip.d.ts +24 -0
  315. package/dist/standing-tip.js +52 -0
  316. package/dist/stdout.d.ts +38 -0
  317. package/dist/storage-control-chain.d.ts +38 -0
  318. package/dist/storage-control-chain.js +78 -0
  319. package/dist/sui-rpc.d.ts +15 -0
  320. package/dist/sui-rpc.js +59 -0
  321. package/dist/support-attach.d.ts +24 -0
  322. package/dist/support-attach.js +72 -0
  323. package/dist/support-copy.d.ts +42 -0
  324. package/dist/support-copy.js +109 -0
  325. package/dist/support-wire.d.ts +49 -0
  326. package/dist/support-wire.js +194 -0
  327. package/dist/trash-sweep.d.ts +55 -0
  328. package/dist/units.d.ts +2 -0
  329. package/dist/update-check.d.ts +50 -0
  330. package/dist/update-source.d.ts +63 -0
  331. package/dist/upload-api.d.ts +3 -0
  332. package/dist/upload-file.d.ts +97 -0
  333. package/dist/upload-file.js +3 -2
  334. package/dist/upload-price.d.ts +50 -0
  335. package/dist/upload-steps.d.ts +26 -0
  336. package/dist/upload-steps.js +29 -9
  337. package/dist/upload-store.d.ts +182 -0
  338. package/dist/upload-wallet-chain.d.ts +54 -0
  339. package/dist/upload-wallet-chain.js +203 -0
  340. package/dist/upload-wallet-plan.d.ts +128 -0
  341. package/dist/upload-wallet-plan.js +184 -0
  342. package/dist/upload-wallet.d.ts +24 -0
  343. package/dist/upload-wallet.js +208 -0
  344. package/dist/upload-wire.d.ts +244 -0
  345. package/dist/upload.d.ts +18 -0
  346. package/dist/upload.js +10 -0
  347. package/dist/usage-report.d.ts +35 -0
  348. package/dist/wallet-activity-chain.d.ts +5 -0
  349. package/dist/wallet-activity-chain.js +31 -0
  350. package/dist/wallet-chain.d.ts +19 -0
  351. package/dist/wallet-chain.js +2 -2
  352. package/dist/wallet-grant.d.ts +50 -0
  353. package/dist/wallet-grant.js +218 -0
  354. package/dist/wallet-send-chain.d.ts +24 -0
  355. package/dist/wallet-send-chain.js +49 -0
  356. package/dist/wallet-sign.d.ts +133 -0
  357. package/dist/wallet-sign.js +317 -0
  358. package/dist/wallet-storage-chain.d.ts +3 -0
  359. package/dist/wallet-storage-chain.js +20 -0
  360. package/dist/wallet-swap-chain.d.ts +52 -0
  361. package/dist/wallet-swap-chain.js +186 -0
  362. package/dist/wallet-swap-quote.d.ts +22 -0
  363. package/dist/wallet-swap-quote.js +126 -0
  364. package/dist/wallet.d.ts +135 -0
  365. package/dist/wallet.js +5 -5
  366. package/dist/walrus-write.d.ts +56 -0
  367. package/dist/walrus-write.js +5 -4
  368. package/dist/walrus.d.ts +86 -0
  369. package/dist/walrus.js +48 -10
  370. package/dist/zstd-node.d.ts +11 -0
  371. package/dist/zstd-node.js +71 -0
  372. package/docs/commands/accept-terms.md +15 -0
  373. package/docs/commands/create.md +17 -0
  374. package/docs/commands/credits.md +34 -0
  375. package/docs/commands/delete-account.md +14 -0
  376. package/docs/commands/deposit.md +27 -0
  377. package/docs/commands/devices.md +9 -0
  378. package/docs/commands/documents.md +10 -0
  379. package/docs/commands/env.md +21 -0
  380. package/docs/commands/extend.md +27 -0
  381. package/docs/commands/folders.md +10 -0
  382. package/docs/commands/get.md +14 -0
  383. package/docs/commands/key.md +14 -0
  384. package/docs/commands/login.md +23 -0
  385. package/docs/commands/logout.md +11 -0
  386. package/docs/commands/losses.md +13 -0
  387. package/docs/commands/ls.md +16 -0
  388. package/docs/commands/marks.md +9 -0
  389. package/docs/commands/mcp.md +33 -0
  390. package/docs/commands/mode.md +29 -0
  391. package/docs/commands/on-collision.md +13 -0
  392. package/docs/commands/padding.md +13 -0
  393. package/docs/commands/public-code.md +9 -0
  394. package/docs/commands/pull.md +9 -0
  395. package/docs/commands/push.md +12 -0
  396. package/docs/commands/put.md +36 -0
  397. package/docs/commands/rebuild.md +13 -0
  398. package/docs/commands/recovery.md +18 -0
  399. package/docs/commands/s3.md +17 -0
  400. package/docs/commands/share.md +15 -0
  401. package/docs/commands/support.md +16 -0
  402. package/docs/commands/tip.md +21 -0
  403. package/docs/commands/trash.md +25 -0
  404. package/docs/commands/trial.md +12 -0
  405. package/docs/commands/unlock.md +25 -0
  406. package/docs/commands/update.md +14 -0
  407. package/docs/commands/usage.md +14 -0
  408. package/docs/commands/verify.md +18 -0
  409. package/docs/commands/wallet.md +67 -0
  410. package/docs/commands/whoami.md +12 -0
  411. package/package.json +67 -8
  412. package/server.json +28 -0
  413. package/vendor/nmts-crypto/nmts_crypto_wasm.d.ts +23 -2
  414. package/vendor/nmts-crypto/nmts_crypto_wasm.js +44 -2
  415. package/vendor/nmts-crypto/nmts_crypto_wasm_bg.wasm +0 -0
  416. package/vendor/nmts-crypto/nmts_crypto_wasm_bg.wasm.d.ts +1 -0
  417. package/vendor/nmts-crypto/package.json +1 -1
  418. package/dist/commands/consent.js +0 -77
  419. package/dist/extend-sign.js +0 -130
@@ -14,6 +14,23 @@ export const TEXT_SCALE_MIN_PCT = 80;
14
14
  export const TEXT_SCALE_MAX_PCT = 160;
15
15
  /** Follow the device. Not written to the wire — absence is the only spelling of it. */
16
16
  export const TEXT_SCALE_DEFAULT_PCT = 100;
17
+ /** The most a standing tip can be: the whole payment. Above the dial's 10 % it is typed and confirmed. */
18
+ export const TIP_TENTHS_MAX = 1000;
19
+ /**
20
+ * The deposit range this format carries: whole credits, 0 to 64.
21
+ *
22
+ * ⚠ The SERVER's own ceiling rides on the account view (`deposit_max`) and is what the payment
23
+ * screen holds the chosen figure inside. This pair is the format's bound, so a value written by
24
+ * some other build is read back only when it is one this build can also write.
25
+ */
26
+ export const DEPOSIT_MAX_CREDITS = 64;
27
+ /** What an account's deposit is when nobody chose. Not written to the wire — absence spells it. */
28
+ export const DEPOSIT_DEFAULT_CREDITS = DEPOSIT_MAX_CREDITS;
29
+ /** The default deposit in force for an account, in credits. Absence is the full deposit, not 0. */
30
+ export function depositDefaultOf(settings) {
31
+ const stored = settings?.depositDefault;
32
+ return typeof stored === "number" ? stored : DEPOSIT_DEFAULT_CREDITS;
33
+ }
17
34
  /** Settings → wire, or null when every field is at its default (then nothing is written). */
18
35
  export function settingsToWire(s) {
19
36
  if (!s)
@@ -28,9 +45,28 @@ export function settingsToWire(s) {
28
45
  s.textScalePct <= TEXT_SCALE_MAX_PCT) {
29
46
  w.tx = Math.round(s.textScalePct);
30
47
  }
31
- if (s.paddingMode === "pow2")
32
- w.pd = "pow2";
33
- return w.dm !== undefined || w.tx !== undefined || w.pd !== undefined ? w : null;
48
+ if (s.paddingMode === "pow2" || s.paddingMode === "none")
49
+ w.pd = s.paddingMode;
50
+ if (typeof s.depositDefault === "number" &&
51
+ Number.isInteger(s.depositDefault) &&
52
+ s.depositDefault >= 0 &&
53
+ s.depositDefault < DEPOSIT_MAX_CREDITS) {
54
+ w.dd = s.depositDefault;
55
+ }
56
+ if (typeof s.tipTenths === "number" && Number.isInteger(s.tipTenths) && s.tipTenths > 0 && s.tipTenths <= TIP_TENTHS_MAX) {
57
+ w.tp = s.tipTenths;
58
+ }
59
+ if (typeof s.tipConsentAt === "number" && Number.isFinite(s.tipConsentAt) && s.tipConsentAt > 0) {
60
+ w.tc = Math.round(s.tipConsentAt);
61
+ }
62
+ return w.dm !== undefined ||
63
+ w.tx !== undefined ||
64
+ w.pd !== undefined ||
65
+ w.dd !== undefined ||
66
+ w.tp !== undefined ||
67
+ w.tc !== undefined
68
+ ? w
69
+ : null;
34
70
  }
35
71
  /**
36
72
  * Wire → settings, dropping anything unusable. A text scale outside the bounds is DROPPED, not
@@ -45,6 +81,9 @@ export function settingsFromWire(w) {
45
81
  const dm = Reflect.get(w, "dm");
46
82
  const tx = Reflect.get(w, "tx");
47
83
  const pd = Reflect.get(w, "pd");
84
+ const dd = Reflect.get(w, "dd");
85
+ const tp = Reflect.get(w, "tp");
86
+ const tc = Reflect.get(w, "tc");
48
87
  const s = {};
49
88
  if (dm === 1)
50
89
  s.developerMode = true;
@@ -57,9 +96,51 @@ export function settingsFromWire(w) {
57
96
  }
58
97
  // An unknown rule is DROPPED, not guessed at: padding a file by a rule this build does not know
59
98
  // would give it a size no reader here can undo. Falling back to the default is always readable.
60
- if (pd === "pow2")
61
- s.paddingMode = "pow2";
62
- return s.developerMode !== undefined || s.textScalePct !== undefined || s.paddingMode !== undefined
99
+ if (pd === "pow2" || pd === "none")
100
+ s.paddingMode = pd;
101
+ // A deposit outside the range is DROPPED, not clamped: holding back a number some other build
102
+ // miswrote is worse than holding back the full deposit, which is what every account starts at.
103
+ if (typeof dd === "number" && Number.isInteger(dd) && dd >= 0 && dd < DEPOSIT_MAX_CREDITS) {
104
+ s.depositDefault = dd;
105
+ }
106
+ // A tip outside the bounds is DROPPED, not clamped: sending a share some other build miswrote is
107
+ // worse than sending nothing, which is always what 0 means.
108
+ if (typeof tp === "number" && Number.isInteger(tp) && tp > 0 && tp <= TIP_TENTHS_MAX)
109
+ s.tipTenths = tp;
110
+ if (typeof tc === "number" && Number.isFinite(tc) && tc > 0)
111
+ s.tipConsentAt = Math.round(tc);
112
+ return s.developerMode !== undefined ||
113
+ s.textScalePct !== undefined ||
114
+ s.paddingMode !== undefined ||
115
+ s.depositDefault !== undefined ||
116
+ s.tipTenths !== undefined ||
117
+ s.tipConsentAt !== undefined
63
118
  ? s
64
119
  : undefined;
65
120
  }
121
+ /** Folds a deposit patch into a settings copy: out-of-range is clamped, the full deposit clears. */
122
+ export function applyDepositPatch(next, depositDefault) {
123
+ if (depositDefault === undefined || !Number.isFinite(depositDefault))
124
+ return;
125
+ const credits = Math.round(Math.min(DEPOSIT_MAX_CREDITS, Math.max(0, depositDefault)));
126
+ if (credits === DEPOSIT_DEFAULT_CREDITS)
127
+ delete next.depositDefault;
128
+ else
129
+ next.depositDefault = credits;
130
+ }
131
+ /** Folds a tip patch into a settings copy: 0 clears, above the cap is capped, fractions are rounded. */
132
+ export function applyTipPatch(next, tipTenths, tipConsentAt) {
133
+ if (tipTenths !== undefined && Number.isFinite(tipTenths)) {
134
+ const t = Math.round(Math.min(TIP_TENTHS_MAX, Math.max(0, tipTenths)));
135
+ if (t === 0)
136
+ delete next.tipTenths;
137
+ else
138
+ next.tipTenths = t;
139
+ }
140
+ if (tipConsentAt !== undefined && Number.isFinite(tipConsentAt)) {
141
+ if (tipConsentAt <= 0)
142
+ delete next.tipConsentAt;
143
+ else
144
+ next.tipConsentAt = Math.round(tipConsentAt);
145
+ }
146
+ }
@@ -0,0 +1,76 @@
1
+ /** What to do with one name that is already in use. */
2
+ export type ConflictChoice = "rename" | "overwrite";
3
+ /** One file whose desired name is already taken. */
4
+ export interface Conflict {
5
+ /** Index into the batch handed to `findConflicts`, so the caller can match its own record. */
6
+ readonly at: number;
7
+ /** The name that is already in use. */
8
+ readonly name: string;
9
+ /** The folder it collides in — null is the root. */
10
+ readonly parentId: string | null;
11
+ }
12
+ /** A conflict as it is put to whoever answers, with the answer already worked out for one branch. */
13
+ export interface Asked extends Conflict {
14
+ /**
15
+ * The name renaming would actually produce, at this point in the batch.
16
+ *
17
+ * ⛔ THE NAME, NOT "A NUMBER GETS ADDED". A screen that cannot say what the file will be called
18
+ * is asking somebody to choose between one known outcome and one unknown one. And it has to
19
+ * come from this walk rather than be guessed alongside it, or the second of three collisions
20
+ * is shown `(2)` and stored as `(3)`.
21
+ */
22
+ readonly renamedTo: string;
23
+ /** How many conflicts are still to come after this one. 0 means this is the last. */
24
+ readonly remaining: number;
25
+ }
26
+ /** One file on its way in, as much of it as this file needs. */
27
+ export interface Incoming {
28
+ readonly name: string;
29
+ readonly parentId: string | null;
30
+ }
31
+ /**
32
+ * Which of these collide, in batch order.
33
+ *
34
+ * ⛔ THE TAKEN SET GROWS AS THIS WALKS. Two files called `report.pdf` in one drop collide with each
35
+ * other, not only with the drive — and a caller that asked only about the drive would give both
36
+ * the same name. So a name that has been handed out here counts as taken from then on, and the
37
+ * second one is reported as a conflict too.
38
+ */
39
+ export declare function findConflicts(batch: readonly Incoming[], takenIn: (parentId: string | null) => ReadonlySet<string>): Conflict[];
40
+ /** What one file ended up as, after the choices were applied. */
41
+ export interface Settled {
42
+ /** The name it will be stored under. */
43
+ readonly name: string;
44
+ readonly parentId: string | null;
45
+ /**
46
+ * The name it replaces in that folder, when the choice was to overwrite.
47
+ *
48
+ * ⚠ The NAME, not an id: this file does not know the drive's identifiers, and the caller that
49
+ * does is the one that has to find and destroy the old record.
50
+ */
51
+ readonly replaces?: string;
52
+ }
53
+ /**
54
+ * Apply the choices and hand back what each file becomes.
55
+ *
56
+ * `choiceFor` is asked only about names that actually collide, one at a time and in batch order, so
57
+ * a screen can put the question to a person and this walk waits. Anything it is not asked about
58
+ * keeps its name. A batch answer ("do this for all") is the caller returning the same value from
59
+ * then on without asking again — this file does not need to know that happened.
60
+ *
61
+ * ⛔ A RENAME CONSUMES THE NAME IT WAS GIVEN, an overwrite does not. Two files called `report.pdf`
62
+ * both overwriting would otherwise be two writes to one name — the second wins and the first is
63
+ * lost with nothing said. So the second one is renamed regardless of the choice, and the caller
64
+ * can see that because the name it gets back is not the name it asked for.
65
+ */
66
+ export declare function settle(batch: readonly Incoming[], takenIn: (parentId: string | null) => ReadonlySet<string>, choiceFor: (conflict: Asked) => ConflictChoice | Promise<ConflictChoice>,
67
+ /**
68
+ * Can the thing holding this name be replaced at all?
69
+ *
70
+ * ⛔ A FOLDER CAN HOLD THE NAME. Offering "overwrite" then would offer to delete a folder and
71
+ * everything under it in order to store one file, from a dialog that names a file. Nothing
72
+ * asks; those are renamed, which is what happened before anything was asked at all.
73
+ * ⚠ Left out means everything is replaceable, which is right for callers whose names are all
74
+ * files (the S3 gateway has no folders).
75
+ */
76
+ overwritable?: (conflict: Conflict) => boolean): Promise<Settled[]>;
@@ -0,0 +1,73 @@
1
+ /** Bytes of one sealed part that decide the question (NCF-3 §4.1 header, §4.2 commitment). */
2
+ export declare const NCF3_HEADER_BYTES = 72;
3
+ /**
4
+ * How many of these reads are in flight at once.
5
+ *
6
+ * Small on purpose: it is one tiny range request per file against the public aggregators, and a
7
+ * rebuild is not a download — going wider would spend an account's whole read budget on 72-byte
8
+ * requests and make the progress line lie about what is happening.
9
+ */
10
+ export declare const REBUILD_VERIFY_CONCURRENCY = 4;
11
+ /** Why one pair could not be shown to belong together. */
12
+ export type UnverifiedReason =
13
+ /** The server's row carries no wrapped key at all — nothing to pair, and nothing to check. */
14
+ "no-key"
15
+ /** The row names no stored part, so there is no header this key could be tried against. */
16
+ | "no-parts"
17
+ /** The header could not be read (network, aggregator, an unknown storage network). */
18
+ | "unreadable"
19
+ /** The header was read and this key does not open it — the pair is WRONG, not merely unproven. */
20
+ | "wrong-key";
21
+ /** One item whose key was not shown to belong to it. */
22
+ export interface UnverifiedPair {
23
+ readonly id: string;
24
+ readonly reason: UnverifiedReason;
25
+ }
26
+ /** What one attempt to open a row's first part header came back with. */
27
+ export type PairVerdict = {
28
+ readonly ok: true;
29
+ } | {
30
+ readonly ok: false;
31
+ readonly reason: UnverifiedReason;
32
+ };
33
+ /** The least a row must expose to be checked. */
34
+ export interface RebuildRow {
35
+ readonly id: string;
36
+ /** The wrapped file key, absent on a row committed before the server kept one. */
37
+ readonly dekWrapped?: string | undefined;
38
+ }
39
+ export interface VerifyPairingsInput<T extends RebuildRow> {
40
+ readonly rows: readonly T[];
41
+ /**
42
+ * Read this row's FIRST sealed part's header prefix and try to open it with the row's own key.
43
+ *
44
+ * ⛔ IT MUST NOT THROW FOR AN ORDINARY FAILURE — a verdict is the answer, and `unreadable` is a
45
+ * verdict. A throw is still handled (as `unreadable`, never as a pass) so that a bug on the
46
+ * caller's side cannot turn into a list full of keys nobody checked.
47
+ */
48
+ openFirstPartHeader(row: T): Promise<PairVerdict>;
49
+ /** In-flight reads. Defaults to REBUILD_VERIFY_CONCURRENCY; anything below 1 is raised to 1. */
50
+ readonly concurrency?: number;
51
+ /** Ticks as answers arrive, so a large account is not a silent wait. */
52
+ onProgress?: (checked: number, total: number) => void;
53
+ }
54
+ export interface PairingVerdicts {
55
+ /** Ids whose key was shown to open their own first part. Only these may be written with a key. */
56
+ readonly verified: ReadonlySet<string>;
57
+ /** Everything else, in the order the rows were given, each with why. */
58
+ readonly unverified: readonly UnverifiedPair[];
59
+ }
60
+ /**
61
+ * Decide, for every row, whether its key belongs to it.
62
+ *
63
+ * A row with no key is answered here rather than by the caller's reader: there is nothing to
64
+ * fetch, and spending a request to learn that would be a request per keyless file.
65
+ */
66
+ export declare function verifyKeyPairings<T extends RebuildRow>(input: VerifyPairingsInput<T>): Promise<PairingVerdicts>;
67
+ /**
68
+ * True when this row's key may be written into the rebuilt list.
69
+ *
70
+ * Both rebuild paths ask this one question rather than each testing the set themselves, because
71
+ * "which way round is the set" is exactly the kind of thing two copies get differently.
72
+ */
73
+ export declare function mayCarryKey(id: string, verdicts: PairingVerdicts): boolean;
@@ -0,0 +1,112 @@
1
+ // Checking that a rebuilt file list pairs each KEY with the file it actually opens. ⚠ PUBLISHED —
2
+ // copied byte-for-byte into the `nmts` command-line package; keep comments self-contained English.
3
+ //
4
+ // ⛔ WHY THIS EXISTS. A rebuild takes each server row's wrapped file key (`dek_wrapped`) and pairs
5
+ // it with the item as the server presents it. The wrapped key is sealed under a FIXED domain
6
+ // separator (`nmts/v3/dek-wrap`, NCF-3 §2.2) and NOT under the item's id, so every one of an
7
+ // account's wrapped keys opens under the same account key — which means a server that handed
8
+ // back row A's key beside row B's id would produce a list whose pairs are wrong, and every
9
+ // check on this side would pass. The mistake is invisible because rebuilt names are
10
+ // placeholders, and it is then SEALED into the list and carried into the recovery list.
11
+ //
12
+ // ⛔ WHY NOTHING FALSE IS EVER READ, AND WHY THAT IS NOT ENOUGH. A part decrypted with the wrong
13
+ // key fails at its authentication tag, so a wrong pair is a file that will not open — never
14
+ // wrong bytes. What the wrong pair does is PERSIST: it is written into the sealed list, it
15
+ // survives every later edit, and the day somebody tries to open the file is years after the
16
+ // day the pairing could still have been worked out from what the server held.
17
+ //
18
+ // ⭐ THE CHECK IS ONE HEADER, NOT ONE FILE. NCF-3 §4.1 puts a 72-byte plaintext header in front of
19
+ // every sealed part, and §4.2 puts a key commitment in it — `HKDF(ikm = DEK, salt = nonce_prefix,
20
+ // info = "nmts/v3/stream-commit" || header[0..40])` — which the decryptor recomputes and
21
+ // compares IN CONSTANT TIME BEFORE ANY CHUNK IS DECRYPTED. So opening a part's header with a
22
+ // key either succeeds or does not, and neither answer needs a single byte of ciphertext, a tag,
23
+ // or a chunk. That is what makes verifying a whole account affordable: one 72-byte ranged read
24
+ // per file, on the read path the download engine already uses.
25
+ //
26
+ // ⛔ A PAIR THAT DOES NOT OPEN IS NOT WRITTEN WITH A KEY. It keeps its entry — the row, the size
27
+ // and the dates are real, and an entry is what says the file was there — but the key stays off
28
+ // it, because a key written beside the wrong file is a claim this side cannot make. The count
29
+ // and the reasons come back to the caller so a screen or a command can say which is which.
30
+ //
31
+ // ⛔ EVERY UNKNOWN ANSWER IS "UNVERIFIED", never "verified". A read that timed out, an aggregator
32
+ // that is down, a part row that is missing: none of them is evidence that the key belongs to
33
+ // the file, and treating them as evidence is exactly how the defect this file closes came back.
34
+ /** Bytes of one sealed part that decide the question (NCF-3 §4.1 header, §4.2 commitment). */
35
+ export const NCF3_HEADER_BYTES = 72;
36
+ /**
37
+ * How many of these reads are in flight at once.
38
+ *
39
+ * Small on purpose: it is one tiny range request per file against the public aggregators, and a
40
+ * rebuild is not a download — going wider would spend an account's whole read budget on 72-byte
41
+ * requests and make the progress line lie about what is happening.
42
+ */
43
+ export const REBUILD_VERIFY_CONCURRENCY = 4;
44
+ /**
45
+ * Decide, for every row, whether its key belongs to it.
46
+ *
47
+ * A row with no key is answered here rather than by the caller's reader: there is nothing to
48
+ * fetch, and spending a request to learn that would be a request per keyless file.
49
+ */
50
+ export async function verifyKeyPairings(input) {
51
+ const { rows, openFirstPartHeader, onProgress } = input;
52
+ const total = rows.length;
53
+ // Answers are parked BY POSITION and read back in order at the end: the pool finishes them in
54
+ // whatever order the network allows, and a list of problems in network order is a list whose
55
+ // order changes every run.
56
+ const verdicts = new Array(total);
57
+ let checked = 0;
58
+ // The same bounded runner the relay pool probes with: a shared cursor and N workers walking it,
59
+ // so at most N reads are open and nothing is queued that will not be started.
60
+ let next = 0;
61
+ const worker = async () => {
62
+ while (next < total) {
63
+ const at = next;
64
+ next += 1;
65
+ const row = rows[at];
66
+ if (row === undefined)
67
+ continue;
68
+ verdicts[at] =
69
+ row.dekWrapped === undefined || row.dekWrapped === ""
70
+ ? { ok: false, reason: "no-key" }
71
+ : await attempt(openFirstPartHeader, row);
72
+ checked += 1;
73
+ onProgress?.(checked, total);
74
+ }
75
+ };
76
+ const workers = Math.min(Math.max(input.concurrency ?? REBUILD_VERIFY_CONCURRENCY, 1), total);
77
+ await Promise.all(Array.from({ length: workers }, () => worker()));
78
+ const verified = new Set();
79
+ const unverified = [];
80
+ for (let at = 0; at < total; at += 1) {
81
+ const row = rows[at];
82
+ if (row === undefined)
83
+ continue;
84
+ const verdict = verdicts[at];
85
+ // An absent verdict cannot happen while the loop above covers every position, and if it ever
86
+ // did it would mean "not checked" — which is `unverified`, because the alternative is a key
87
+ // written on the strength of a read nobody made.
88
+ if (verdict !== undefined && verdict.ok)
89
+ verified.add(row.id);
90
+ else
91
+ unverified.push({ id: row.id, reason: verdict === undefined ? "unreadable" : verdict.reason });
92
+ }
93
+ return { verified, unverified };
94
+ }
95
+ /** Run one attempt, turning a thrown reader into the verdict it should have returned. */
96
+ async function attempt(open, row) {
97
+ try {
98
+ return await open(row);
99
+ }
100
+ catch {
101
+ return { ok: false, reason: "unreadable" };
102
+ }
103
+ }
104
+ /**
105
+ * True when this row's key may be written into the rebuilt list.
106
+ *
107
+ * Both rebuild paths ask this one question rather than each testing the set themselves, because
108
+ * "which way round is the set" is exactly the kind of thing two copies get differently.
109
+ */
110
+ export function mayCarryKey(id, verdicts) {
111
+ return verdicts.verified.has(id);
112
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * A name not present in `taken`.
3
+ *
4
+ * Returns `desired` untouched when it is free. Otherwise inserts ` (n)` before the extension,
5
+ * starting at 2, until it finds a free one — matching desktop behaviour so nobody has to learn a
6
+ * new convention. `taken` is not mutated; callers uploading several files at once must add each
7
+ * returned name themselves, or a batch of identical names would all resolve to the same `(2)`.
8
+ */
9
+ export declare function uniqueFileName(desired: string, taken: ReadonlySet<string>): string;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * What a platform must provide to write (and read) flag 0x02.
3
+ *
4
+ * `decompress` is given the largest plaintext the caller will accept. It exists so an encoder can
5
+ * refuse a frame that CLAIMS to expand past that bound before it allocates for it — a sealed
6
+ * document is authenticated, but a build that meets a corrupt or hostile one must still not be
7
+ * asked for a gigabyte of memory first.
8
+ */
9
+ export interface ZstdCodec {
10
+ compress(bytes: Uint8Array, level: number): Uint8Array | Promise<Uint8Array>;
11
+ decompress(bytes: Uint8Array, maxOut: number): Uint8Array | Promise<Uint8Array>;
12
+ }
13
+ /** The level a writer asks for. See the measurement in this file's header before changing it. */
14
+ export declare const ZSTD_LEVEL = 6;
15
+ /** Hand this build its zstd encoder, or `null` to take it away again (tests do the second). */
16
+ export declare function setZstdCodec(codec: ZstdCodec | null): void;
17
+ /** The registered encoder, or null when this build has none. */
18
+ export declare function zstdCodec(): ZstdCodec | null;
19
+ /**
20
+ * The size the frame SAYS it expands to, or null when the frame does not declare one.
21
+ *
22
+ * Reads only the frame header described by RFC 8878, which is at most 14 bytes, so it costs
23
+ * nothing and — this is the point — it runs BEFORE any buffer is allocated for the output. NCF-3
24
+ * §6.3.4 requires our writers to emit the content size for exactly this reason; a frame without
25
+ * one is not ours and is refused by the caller rather than guessed at.
26
+ */
27
+ export declare function zstdContentSize(frame: Uint8Array): number | null;
@@ -0,0 +1,74 @@
1
+ // The zstd seam for the chunked file list (NCF-3 §6.3.4, flag 0x02). ⚠ PUBLISHED —
2
+ // copied byte-for-byte into the `nmts` command-line package; keep comments self-contained English.
3
+ //
4
+ // WHY A SEAM AND NOT AN IMPORT. The browser gets zstd from a WebAssembly build it has to fetch,
5
+ // and the command line gets it from Node's own `zlib`. Neither belongs in a module the two
6
+ // share, so this file holds the SHAPE of an encoder and the register that finds one, and each
7
+ // platform hands its own in. A build that registers nothing still reads and writes lists: the
8
+ // writer falls back to gzip (§6.3.4), and only a document that already says 0x02 needs a
9
+ // decoder — which is a refusal the reader names, never an empty drive.
10
+ //
11
+ // LEVEL 6, MEASURED 2026-09-06 on a synthetic list of 100,000 entries shaped like a real drive
12
+ // (2,000 folders, 98,000 files, random 104-byte wrapped-key and content-hash envelopes,
13
+ // realistic names), compressed as one CHUNK_PLAIN_MAX-sized chunk payload (3,899,634 bytes,
14
+ // 10,330 entries) with @bokuweb/zstd-wasm under Node 24:
15
+ //
16
+ // level 1 → 2,409,818 B (61.8 %) · 8.2 ms/MiB
17
+ // level 3 → 2,357,562 B (60.5 %) · 10.3 ms/MiB
18
+ // level 6 → 2,321,378 B (59.5 %) · 32.9 ms/MiB ← smallest, and well under 100 ms/MiB
19
+ //
20
+ // ⚠ The ranking is not a law of zstd — it is a fact about THIS data shape, and it inverts
21
+ // further up: on the same kind of payload, level 10 was measured producing LARGER output than
22
+ // level 3, in three separate zstd builds. So the level is a measurement, never a guess:
23
+ // re-measure on real chunks before moving this number, and do not reason about it.
24
+ /** The level a writer asks for. See the measurement in this file's header before changing it. */
25
+ export const ZSTD_LEVEL = 6;
26
+ let registered = null;
27
+ /** Hand this build its zstd encoder, or `null` to take it away again (tests do the second). */
28
+ export function setZstdCodec(codec) {
29
+ registered = codec;
30
+ }
31
+ /** The registered encoder, or null when this build has none. */
32
+ export function zstdCodec() {
33
+ return registered;
34
+ }
35
+ /** Magic number every zstd frame starts with (RFC 8878 §3.1.1), little-endian on the wire. */
36
+ const MAGIC = [0x28, 0xb5, 0x2f, 0xfd];
37
+ /**
38
+ * The size the frame SAYS it expands to, or null when the frame does not declare one.
39
+ *
40
+ * Reads only the frame header described by RFC 8878, which is at most 14 bytes, so it costs
41
+ * nothing and — this is the point — it runs BEFORE any buffer is allocated for the output. NCF-3
42
+ * §6.3.4 requires our writers to emit the content size for exactly this reason; a frame without
43
+ * one is not ours and is refused by the caller rather than guessed at.
44
+ */
45
+ export function zstdContentSize(frame) {
46
+ if (frame.length < 6)
47
+ return null;
48
+ for (let i = 0; i < MAGIC.length; i += 1) {
49
+ if (frame[i] !== MAGIC[i])
50
+ return null;
51
+ }
52
+ const descriptor = frame[4] ?? 0;
53
+ const fcsFlag = descriptor >> 6;
54
+ const singleSegment = (descriptor >> 5) & 1;
55
+ const dictIdFlag = descriptor & 3;
56
+ // Field widths, straight from the frame-header descriptor table in RFC 8878: the content-size
57
+ // field is absent when the flag is 0 unless the frame is one segment, in which case it is a
58
+ // single byte.
59
+ const fcsSize = fcsFlag === 0 ? singleSegment : 1 << fcsFlag;
60
+ if (fcsSize === 0)
61
+ return null;
62
+ const dictIdSize = dictIdFlag === 0 ? 0 : 1 << (dictIdFlag - 1);
63
+ const at = 5 + (singleSegment === 1 ? 0 : 1) + dictIdSize;
64
+ if (frame.length < at + fcsSize)
65
+ return null;
66
+ let value = 0;
67
+ for (let i = fcsSize - 1; i >= 0; i -= 1) {
68
+ // Built as a float rather than shifted: an 8-byte field overflows 32-bit shifts, and the only
69
+ // use of this number is a comparison against a bound in the low millions.
70
+ value = value * 256 + (frame[at + i] ?? 0);
71
+ }
72
+ // A two-byte field is stored with 256 subtracted — the one irregular width in that table.
73
+ return fcsSize === 2 ? value + 256 : value;
74
+ }