@labacacia/nps-sdk 1.0.0-alpha.6 → 1.0.0-alpha.8

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 (369) hide show
  1. package/CHANGELOG.cn.md +126 -0
  2. package/CHANGELOG.md +135 -0
  3. package/README.cn.md +9 -2
  4. package/README.md +9 -2
  5. package/dist/core/anchor-cache.d.ts +42 -0
  6. package/dist/core/anchor-cache.d.ts.map +1 -0
  7. package/dist/core/anchor-cache.js +104 -0
  8. package/dist/core/anchor-cache.js.map +1 -0
  9. package/dist/core/cache.d.ts +14 -0
  10. package/dist/core/cache.d.ts.map +1 -0
  11. package/dist/core/cache.js +80 -0
  12. package/dist/core/cache.js.map +1 -0
  13. package/dist/core/canonical-json.d.ts +12 -0
  14. package/dist/core/canonical-json.d.ts.map +1 -0
  15. package/dist/core/canonical-json.js +44 -0
  16. package/dist/core/canonical-json.js.map +1 -0
  17. package/dist/core/codec.d.ts +32 -0
  18. package/dist/core/codec.d.ts.map +1 -0
  19. package/dist/core/codec.js +119 -0
  20. package/dist/core/codec.js.map +1 -0
  21. package/dist/core/codecs/index.d.ts +4 -0
  22. package/dist/core/codecs/index.d.ts.map +1 -0
  23. package/{src/core/codecs/index.ts → dist/core/codecs/index.js} +1 -0
  24. package/dist/core/codecs/index.js.map +1 -0
  25. package/dist/core/codecs/ncp-codec.d.ts +39 -0
  26. package/dist/core/codecs/ncp-codec.d.ts.map +1 -0
  27. package/dist/core/codecs/ncp-codec.js +93 -0
  28. package/dist/core/codecs/ncp-codec.js.map +1 -0
  29. package/dist/core/codecs/tier1-json-codec.d.ts +10 -0
  30. package/dist/core/codecs/tier1-json-codec.d.ts.map +1 -0
  31. package/{src/core/codecs/tier1-json-codec.ts → dist/core/codecs/tier1-json-codec.js} +11 -16
  32. package/dist/core/codecs/tier1-json-codec.js.map +1 -0
  33. package/dist/core/codecs/tier2-msgpack-codec.d.ts +10 -0
  34. package/dist/core/codecs/tier2-msgpack-codec.d.ts.map +1 -0
  35. package/{src/core/codecs/tier2-msgpack-codec.ts → dist/core/codecs/tier2-msgpack-codec.js} +10 -14
  36. package/dist/core/codecs/tier2-msgpack-codec.js.map +1 -0
  37. package/dist/core/crypto-provider.d.ts +31 -0
  38. package/dist/core/crypto-provider.d.ts.map +1 -0
  39. package/dist/core/crypto-provider.js +10 -0
  40. package/dist/core/crypto-provider.js.map +1 -0
  41. package/dist/core/exceptions.d.ts +27 -0
  42. package/dist/core/exceptions.d.ts.map +1 -0
  43. package/dist/core/exceptions.js +52 -0
  44. package/dist/core/exceptions.js.map +1 -0
  45. package/dist/core/frame-header.d.ts +87 -0
  46. package/dist/core/frame-header.d.ts.map +1 -0
  47. package/dist/core/frame-header.js +185 -0
  48. package/dist/core/frame-header.js.map +1 -0
  49. package/dist/core/frame-registry.d.ts +35 -0
  50. package/dist/core/frame-registry.d.ts.map +1 -0
  51. package/dist/core/frame-registry.js +63 -0
  52. package/dist/core/frame-registry.js.map +1 -0
  53. package/dist/core/frames.d.ts +82 -0
  54. package/dist/core/frames.d.ts.map +1 -0
  55. package/dist/core/frames.js +155 -0
  56. package/dist/core/frames.js.map +1 -0
  57. package/dist/core/index.d.ts +11 -0
  58. package/dist/core/index.d.ts.map +1 -0
  59. package/{src/core/index.ts → dist/core/index.js} +3 -23
  60. package/dist/core/index.js.map +1 -0
  61. package/dist/core/registry.d.ts +11 -0
  62. package/dist/core/registry.d.ts.map +1 -0
  63. package/dist/core/registry.js +17 -0
  64. package/dist/core/registry.js.map +1 -0
  65. package/dist/core/status-codes.d.ts +29 -0
  66. package/dist/core/status-codes.d.ts.map +1 -0
  67. package/dist/core/status-codes.js +39 -0
  68. package/dist/core/status-codes.js.map +1 -0
  69. package/dist/index.d.ts +2 -0
  70. package/dist/index.d.ts.map +1 -0
  71. package/{src/index.ts → dist/index.js} +1 -1
  72. package/dist/index.js.map +1 -0
  73. package/dist/ncp/frames/anchor-frame.d.ts +29 -0
  74. package/dist/ncp/frames/anchor-frame.d.ts.map +1 -0
  75. package/dist/ncp/frames/anchor-frame.js +54 -0
  76. package/dist/ncp/frames/anchor-frame.js.map +1 -0
  77. package/dist/ncp/frames/caps-frame.d.ts +29 -0
  78. package/dist/ncp/frames/caps-frame.d.ts.map +1 -0
  79. package/dist/ncp/frames/caps-frame.js +29 -0
  80. package/dist/ncp/frames/caps-frame.js.map +1 -0
  81. package/dist/ncp/frames/diff-frame.d.ts +32 -0
  82. package/dist/ncp/frames/diff-frame.d.ts.map +1 -0
  83. package/dist/ncp/frames/diff-frame.js +37 -0
  84. package/dist/ncp/frames/diff-frame.js.map +1 -0
  85. package/dist/ncp/frames/error-frame.d.ts +16 -0
  86. package/dist/ncp/frames/error-frame.d.ts.map +1 -0
  87. package/dist/ncp/frames/error-frame.js +13 -0
  88. package/dist/ncp/frames/error-frame.js.map +1 -0
  89. package/dist/ncp/frames/hello-frame.d.ts +21 -0
  90. package/dist/ncp/frames/hello-frame.d.ts.map +1 -0
  91. package/dist/ncp/frames/hello-frame.js +25 -0
  92. package/dist/ncp/frames/hello-frame.js.map +1 -0
  93. package/dist/ncp/frames/stream-frame.d.ts +16 -0
  94. package/dist/ncp/frames/stream-frame.d.ts.map +1 -0
  95. package/dist/ncp/frames/stream-frame.js +18 -0
  96. package/dist/ncp/frames/stream-frame.js.map +1 -0
  97. package/dist/ncp/frames.d.ts +94 -0
  98. package/dist/ncp/frames.d.ts.map +1 -0
  99. package/dist/ncp/frames.js +192 -0
  100. package/dist/ncp/frames.js.map +1 -0
  101. package/dist/ncp/handshake.d.ts +30 -0
  102. package/dist/ncp/handshake.d.ts.map +1 -0
  103. package/dist/ncp/handshake.js +80 -0
  104. package/dist/ncp/handshake.js.map +1 -0
  105. package/dist/ncp/index.d.ts +12 -0
  106. package/dist/ncp/index.d.ts.map +1 -0
  107. package/{src/ncp/index.ts → dist/ncp/index.js} +1 -0
  108. package/dist/ncp/index.js.map +1 -0
  109. package/dist/ncp/ncp-error-codes.d.ts +23 -0
  110. package/dist/ncp/ncp-error-codes.d.ts.map +1 -0
  111. package/dist/ncp/ncp-error-codes.js +34 -0
  112. package/dist/ncp/ncp-error-codes.js.map +1 -0
  113. package/dist/ncp/ncp-patch-format.d.ts +7 -0
  114. package/dist/ncp/ncp-patch-format.d.ts.map +1 -0
  115. package/dist/ncp/ncp-patch-format.js +13 -0
  116. package/dist/ncp/ncp-patch-format.js.map +1 -0
  117. package/dist/ncp/preamble.d.ts +47 -0
  118. package/dist/ncp/preamble.d.ts.map +1 -0
  119. package/dist/ncp/preamble.js +74 -0
  120. package/dist/ncp/preamble.js.map +1 -0
  121. package/dist/ncp/registry.d.ts +3 -0
  122. package/dist/ncp/registry.d.ts.map +1 -0
  123. package/dist/ncp/registry.js +13 -0
  124. package/dist/ncp/registry.js.map +1 -0
  125. package/dist/ncp/stream-manager.d.ts +57 -0
  126. package/dist/ncp/stream-manager.d.ts.map +1 -0
  127. package/dist/ncp/stream-manager.js +163 -0
  128. package/dist/ncp/stream-manager.js.map +1 -0
  129. package/dist/ndp/dns-txt.d.ts +35 -0
  130. package/dist/ndp/dns-txt.d.ts.map +1 -0
  131. package/dist/ndp/dns-txt.js +67 -0
  132. package/dist/ndp/dns-txt.js.map +1 -0
  133. package/dist/ndp/frames.d.ts +56 -0
  134. package/dist/ndp/frames.d.ts.map +1 -0
  135. package/dist/ndp/frames.js +87 -0
  136. package/dist/ndp/frames.js.map +1 -0
  137. package/dist/ndp/index.d.ts +6 -0
  138. package/dist/ndp/index.d.ts.map +1 -0
  139. package/{src/ndp/index.ts → dist/ndp/index.js} +1 -1
  140. package/dist/ndp/index.js.map +1 -0
  141. package/dist/ndp/ndp-registry.d.ts +13 -0
  142. package/dist/ndp/ndp-registry.d.ts.map +1 -0
  143. package/dist/ndp/ndp-registry.js +104 -0
  144. package/dist/ndp/ndp-registry.js.map +1 -0
  145. package/dist/ndp/registry.d.ts +3 -0
  146. package/dist/ndp/registry.d.ts.map +1 -0
  147. package/dist/ndp/registry.js +10 -0
  148. package/dist/ndp/registry.js.map +1 -0
  149. package/dist/ndp/validator.d.ts +18 -0
  150. package/dist/ndp/validator.d.ts.map +1 -0
  151. package/dist/ndp/validator.js +48 -0
  152. package/dist/ndp/validator.js.map +1 -0
  153. package/dist/nip/acme/client.d.ts +31 -0
  154. package/dist/nip/acme/client.d.ts.map +1 -0
  155. package/dist/nip/acme/client.js +136 -0
  156. package/dist/nip/acme/client.js.map +1 -0
  157. package/dist/nip/acme/index.d.ts +6 -0
  158. package/dist/nip/acme/index.d.ts.map +1 -0
  159. package/{src/nip/acme/index.ts → dist/nip/acme/index.js} +1 -1
  160. package/dist/nip/acme/index.js.map +1 -0
  161. package/dist/nip/acme/jws.d.ts +31 -0
  162. package/dist/nip/acme/jws.d.ts.map +1 -0
  163. package/dist/nip/acme/jws.js +76 -0
  164. package/dist/nip/acme/jws.js.map +1 -0
  165. package/dist/nip/acme/messages.d.ts +71 -0
  166. package/dist/nip/acme/messages.d.ts.map +1 -0
  167. package/dist/nip/acme/messages.js +4 -0
  168. package/dist/nip/acme/messages.js.map +1 -0
  169. package/dist/nip/acme/server.d.ts +41 -0
  170. package/dist/nip/acme/server.d.ts.map +1 -0
  171. package/dist/nip/acme/server.js +458 -0
  172. package/dist/nip/acme/server.js.map +1 -0
  173. package/dist/nip/acme/wire.d.ts +19 -0
  174. package/dist/nip/acme/wire.d.ts.map +1 -0
  175. package/dist/nip/acme/wire.js +21 -0
  176. package/dist/nip/acme/wire.js.map +1 -0
  177. package/dist/nip/assurance-level.d.ts +19 -0
  178. package/dist/nip/assurance-level.d.ts.map +1 -0
  179. package/dist/nip/assurance-level.js +38 -0
  180. package/dist/nip/assurance-level.js.map +1 -0
  181. package/dist/nip/cert-format.d.ts +5 -0
  182. package/dist/nip/cert-format.d.ts.map +1 -0
  183. package/dist/nip/cert-format.js +6 -0
  184. package/dist/nip/cert-format.js.map +1 -0
  185. package/dist/nip/error-codes.d.ts +25 -0
  186. package/dist/nip/error-codes.d.ts.map +1 -0
  187. package/{src/nip/error-codes.ts → dist/nip/error-codes.js} +19 -25
  188. package/dist/nip/error-codes.js.map +1 -0
  189. package/dist/nip/frames.d.ts +53 -0
  190. package/dist/nip/frames.d.ts.map +1 -0
  191. package/dist/nip/frames.js +106 -0
  192. package/dist/nip/frames.js.map +1 -0
  193. package/dist/nip/identity.d.ts +18 -0
  194. package/dist/nip/identity.d.ts.map +1 -0
  195. package/dist/nip/identity.js +94 -0
  196. package/dist/nip/identity.js.map +1 -0
  197. package/dist/nip/index.d.ts +11 -0
  198. package/dist/nip/index.d.ts.map +1 -0
  199. package/{src/nip/index.ts → dist/nip/index.js} +3 -2
  200. package/dist/nip/index.js.map +1 -0
  201. package/dist/nip/registry.d.ts +3 -0
  202. package/dist/nip/registry.d.ts.map +1 -0
  203. package/dist/nip/registry.js +10 -0
  204. package/dist/nip/registry.js.map +1 -0
  205. package/dist/nip/reputation-client.d.ts +116 -0
  206. package/dist/nip/reputation-client.d.ts.map +1 -0
  207. package/dist/nip/reputation-client.js +261 -0
  208. package/dist/nip/reputation-client.js.map +1 -0
  209. package/dist/nip/verifier.d.ts +23 -0
  210. package/dist/nip/verifier.d.ts.map +1 -0
  211. package/dist/nip/verifier.js +90 -0
  212. package/dist/nip/verifier.js.map +1 -0
  213. package/dist/nip/x509/builder.d.ts +35 -0
  214. package/dist/nip/x509/builder.d.ts.map +1 -0
  215. package/dist/nip/x509/builder.js +59 -0
  216. package/dist/nip/x509/builder.js.map +1 -0
  217. package/dist/nip/x509/index.d.ts +4 -0
  218. package/dist/nip/x509/index.d.ts.map +1 -0
  219. package/{src/nip/x509/index.ts → dist/nip/x509/index.js} +1 -1
  220. package/dist/nip/x509/index.js.map +1 -0
  221. package/dist/nip/x509/oids.d.ts +16 -0
  222. package/dist/nip/x509/oids.d.ts.map +1 -0
  223. package/{src/nip/x509/oids.ts → dist/nip/x509/oids.js} +5 -10
  224. package/dist/nip/x509/oids.js.map +1 -0
  225. package/dist/nip/x509/verifier.d.ts +26 -0
  226. package/dist/nip/x509/verifier.d.ts.map +1 -0
  227. package/dist/nip/x509/verifier.js +171 -0
  228. package/dist/nip/x509/verifier.js.map +1 -0
  229. package/dist/nop/client.d.ts +34 -0
  230. package/dist/nop/client.d.ts.map +1 -0
  231. package/dist/nop/client.js +90 -0
  232. package/dist/nop/client.js.map +1 -0
  233. package/dist/nop/frames.d.ts +65 -0
  234. package/dist/nop/frames.d.ts.map +1 -0
  235. package/dist/nop/frames.js +148 -0
  236. package/dist/nop/frames.js.map +1 -0
  237. package/dist/nop/index.d.ts +5 -0
  238. package/dist/nop/index.d.ts.map +1 -0
  239. package/{src/nop/index.ts → dist/nop/index.js} +1 -1
  240. package/dist/nop/index.js.map +1 -0
  241. package/dist/nop/models.d.ts +58 -0
  242. package/dist/nop/models.d.ts.map +1 -0
  243. package/dist/nop/models.js +50 -0
  244. package/dist/nop/models.js.map +1 -0
  245. package/dist/nop/nop-types.d.ts +136 -0
  246. package/dist/nop/nop-types.d.ts.map +1 -0
  247. package/dist/nop/nop-types.js +44 -0
  248. package/dist/nop/nop-types.js.map +1 -0
  249. package/dist/nop/registry.d.ts +3 -0
  250. package/dist/nop/registry.d.ts.map +1 -0
  251. package/dist/nop/registry.js +11 -0
  252. package/dist/nop/registry.js.map +1 -0
  253. package/dist/nwp/anchor-client.d.ts +109 -0
  254. package/dist/nwp/anchor-client.d.ts.map +1 -0
  255. package/dist/nwp/anchor-client.js +279 -0
  256. package/dist/nwp/anchor-client.js.map +1 -0
  257. package/dist/nwp/client.d.ts +28 -0
  258. package/dist/nwp/client.d.ts.map +1 -0
  259. package/dist/nwp/client.js +142 -0
  260. package/dist/nwp/client.js.map +1 -0
  261. package/dist/nwp/frames.d.ts +107 -0
  262. package/dist/nwp/frames.d.ts.map +1 -0
  263. package/dist/nwp/frames.js +198 -0
  264. package/dist/nwp/frames.js.map +1 -0
  265. package/dist/nwp/index.d.ts +6 -0
  266. package/dist/nwp/index.d.ts.map +1 -0
  267. package/{src/nwp/index.ts → dist/nwp/index.js} +3 -1
  268. package/dist/nwp/index.js.map +1 -0
  269. package/dist/nwp/manifest.d.ts +69 -0
  270. package/dist/nwp/manifest.d.ts.map +1 -0
  271. package/dist/nwp/manifest.js +4 -0
  272. package/dist/nwp/manifest.js.map +1 -0
  273. package/dist/nwp/registry.d.ts +3 -0
  274. package/dist/nwp/registry.d.ts.map +1 -0
  275. package/dist/nwp/registry.js +10 -0
  276. package/dist/nwp/registry.js.map +1 -0
  277. package/dist/setup.d.ts +10 -0
  278. package/dist/setup.d.ts.map +1 -0
  279. package/{src/setup.ts → dist/setup.js} +13 -16
  280. package/dist/setup.js.map +1 -0
  281. package/package.json +12 -1
  282. package/CONTRIBUTING.cn.md +0 -35
  283. package/CONTRIBUTING.md +0 -35
  284. package/nip-ca-server/Dockerfile +0 -27
  285. package/nip-ca-server/README.md +0 -45
  286. package/nip-ca-server/db/001_init.sql +0 -25
  287. package/nip-ca-server/docker-compose.yml +0 -29
  288. package/nip-ca-server/package.json +0 -23
  289. package/nip-ca-server/src/ca.ts +0 -155
  290. package/nip-ca-server/src/db.ts +0 -104
  291. package/nip-ca-server/src/index.ts +0 -157
  292. package/nip-ca-server/tsconfig.json +0 -13
  293. package/src/core/anchor-cache.ts +0 -129
  294. package/src/core/cache.ts +0 -93
  295. package/src/core/canonical-json.ts +0 -50
  296. package/src/core/codec.ts +0 -158
  297. package/src/core/codecs/ncp-codec.ts +0 -170
  298. package/src/core/crypto-provider.ts +0 -47
  299. package/src/core/exceptions.ts +0 -57
  300. package/src/core/frame-header.ts +0 -282
  301. package/src/core/frame-registry.ts +0 -91
  302. package/src/core/frames.ts +0 -184
  303. package/src/core/registry.ts +0 -28
  304. package/src/core/status-codes.ts +0 -47
  305. package/src/ncp/frames/anchor-frame.ts +0 -87
  306. package/src/ncp/frames/caps-frame.ts +0 -59
  307. package/src/ncp/frames/diff-frame.ts +0 -69
  308. package/src/ncp/frames/error-frame.ts +0 -26
  309. package/src/ncp/frames/hello-frame.ts +0 -50
  310. package/src/ncp/frames/stream-frame.ts +0 -35
  311. package/src/ncp/frames.ts +0 -251
  312. package/src/ncp/handshake.ts +0 -95
  313. package/src/ncp/ncp-error-codes.ts +0 -36
  314. package/src/ncp/ncp-patch-format.ts +0 -16
  315. package/src/ncp/preamble.ts +0 -79
  316. package/src/ncp/registry.ts +0 -15
  317. package/src/ncp/stream-manager.ts +0 -212
  318. package/src/ndp/dns-txt.ts +0 -86
  319. package/src/ndp/frames.ts +0 -124
  320. package/src/ndp/ndp-registry.ts +0 -116
  321. package/src/ndp/registry.ts +0 -12
  322. package/src/ndp/validator.ts +0 -64
  323. package/src/nip/acme/client.ts +0 -185
  324. package/src/nip/acme/jws.ts +0 -109
  325. package/src/nip/acme/messages.ts +0 -85
  326. package/src/nip/acme/server.ts +0 -480
  327. package/src/nip/acme/wire.ts +0 -24
  328. package/src/nip/assurance-level.ts +0 -40
  329. package/src/nip/cert-format.ts +0 -9
  330. package/src/nip/frames.ts +0 -138
  331. package/src/nip/identity.ts +0 -113
  332. package/src/nip/registry.ts +0 -12
  333. package/src/nip/verifier.ts +0 -122
  334. package/src/nip/x509/builder.ts +0 -91
  335. package/src/nip/x509/verifier.ts +0 -214
  336. package/src/nop/client.ts +0 -103
  337. package/src/nop/frames.ts +0 -181
  338. package/src/nop/models.ts +0 -79
  339. package/src/nop/nop-types.ts +0 -208
  340. package/src/nop/registry.ts +0 -13
  341. package/src/nwp/client.ts +0 -114
  342. package/src/nwp/frames.ts +0 -116
  343. package/src/nwp/registry.ts +0 -11
  344. package/tests/_rfc0002-keys.ts +0 -57
  345. package/tests/core/anchor-cache.test.ts +0 -242
  346. package/tests/core/codec.test.ts +0 -205
  347. package/tests/core/frame-registry.test.ts +0 -46
  348. package/tests/core.test.ts +0 -327
  349. package/tests/ncp/diff-binary-bitset.test.ts +0 -107
  350. package/tests/ncp/e2e-enc-reject.test.ts +0 -93
  351. package/tests/ncp/err-error-frame.test.ts +0 -152
  352. package/tests/ncp/frames.test.ts +0 -359
  353. package/tests/ncp/framing.test.ts +0 -233
  354. package/tests/ncp/hello-frame.test.ts +0 -122
  355. package/tests/ncp/inline-anchor.test.ts +0 -88
  356. package/tests/ncp/preamble.test.ts +0 -93
  357. package/tests/ncp/security.test.ts +0 -184
  358. package/tests/ncp/stream-window.test.ts +0 -167
  359. package/tests/ncp/stream.test.ts +0 -242
  360. package/tests/ncp/version-negotiation.test.ts +0 -123
  361. package/tests/ndp.test.ts +0 -377
  362. package/tests/nip-acme-agent01.test.ts +0 -192
  363. package/tests/nip-x509.test.ts +0 -280
  364. package/tests/nip.test.ts +0 -184
  365. package/tests/nop.test.ts +0 -344
  366. package/tests/nwp.test.ts +0 -237
  367. package/tsconfig.json +0 -20
  368. package/tsup.config.ts +0 -20
  369. package/vitest.config.ts +0 -10
@@ -1,212 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // Copyright (c) 2026 LabAcacia / INNO LOTUS PTY LTD
3
- //
4
- // StreamManager — Concurrent stream tracking, sequence validation, flow control
5
- // NPS-1 §4.3, §7.3
6
-
7
- import { NcpError } from "../core/frame-header.js";
8
- import { NCP_ERROR_CODES } from "./ncp-error-codes.js";
9
- import type { StreamFrame } from "./frames/stream-frame.js";
10
-
11
- interface ActiveStream {
12
- streamId: string;
13
- expectedSeq: number;
14
- chunks: unknown[][];
15
- completed: boolean;
16
- errorCode?: string;
17
- }
18
-
19
- interface OutgoingStream {
20
- streamId: string;
21
- remainingWindow: number | undefined; // undefined = no flow control
22
- paused: boolean;
23
- }
24
-
25
- /**
26
- * Manages concurrent StreamFrame streams.
27
- *
28
- * - Tracks active streams by stream_id
29
- * - Validates sequential seq numbers
30
- * - Enforces max concurrent stream limit (NPS-1 §7.3, default 32)
31
- * - Detects early termination via error_code
32
- * - Enforces window-based flow control on outgoing streams (NCP-S-07–11)
33
- */
34
- export class StreamManager {
35
- private readonly streams = new Map<string, ActiveStream>();
36
- private readonly outgoing = new Map<string, OutgoingStream>();
37
- private readonly maxConcurrent: number;
38
-
39
- constructor(options?: { maxConcurrent?: number }) {
40
- this.maxConcurrent = options?.maxConcurrent ?? 32;
41
- }
42
-
43
- /**
44
- * Receive a StreamFrame chunk.
45
- *
46
- * @returns true if stream is complete (is_last=true or error_code set).
47
- * @throws {NcpError} NCP-STREAM-LIMIT-EXCEEDED if too many concurrent streams.
48
- * @throws {NcpError} NCP-STREAM-NOT-FOUND if frame.seq > 0 for a stream that was never opened.
49
- * @throws {NcpError} NPS-CLIENT-CONFLICT if the stream_id was already completed (stream-id reuse; see test_cases NCP-S-04).
50
- * @throws {NcpError} NCP-STREAM-SEQ-GAP if sequence number is not expected.
51
- */
52
- receive(frame: StreamFrame): boolean {
53
- let stream = this.streams.get(frame.stream_id);
54
-
55
- if (!stream) {
56
- // A stream is opened only by seq=0. Any other seq on an unknown stream_id
57
- // means the opener was never seen (NCP-S-13: unknown stream_id).
58
- if (frame.seq !== 0) {
59
- throw new NcpError(
60
- NCP_ERROR_CODES.NCP_STREAM_NOT_FOUND,
61
- `Unknown stream_id ${frame.stream_id} — first frame must have seq=0`,
62
- );
63
- }
64
-
65
- // New stream — check concurrent limit
66
- if (this.streams.size >= this.maxConcurrent) {
67
- throw new NcpError(
68
- NCP_ERROR_CODES.NCP_STREAM_LIMIT_EXCEEDED,
69
- `Max concurrent streams (${this.maxConcurrent}) exceeded`,
70
- );
71
- }
72
-
73
- stream = {
74
- streamId: frame.stream_id,
75
- expectedSeq: 0,
76
- chunks: [],
77
- completed: false,
78
- };
79
- this.streams.set(frame.stream_id, stream);
80
- }
81
-
82
- // Reject writes to completed streams. Per test_cases NCP-S-04, the spec does
83
- // not assign a dedicated NCP code for stream-id reuse; interim mapping uses
84
- // the NPS-level NPS-CLIENT-CONFLICT until a spec-side code is added.
85
- if (stream.completed) {
86
- throw new NcpError(
87
- "NPS-CLIENT-CONFLICT",
88
- `Stream ${frame.stream_id} is already completed — cannot reuse stream_id`,
89
- );
90
- }
91
-
92
- // Sequence validation
93
- if (frame.seq !== stream.expectedSeq) {
94
- // Duplicate detection — same seq as last accepted
95
- if (frame.seq < stream.expectedSeq) {
96
- // Ignore duplicate (idempotent)
97
- return false;
98
- }
99
- throw new NcpError(
100
- NCP_ERROR_CODES.NCP_STREAM_SEQ_GAP,
101
- `Expected seq ${stream.expectedSeq}, got ${frame.seq} on stream ${frame.stream_id}`,
102
- );
103
- }
104
-
105
- stream.chunks.push(frame.data);
106
- stream.expectedSeq = frame.seq + 1;
107
-
108
- // Early termination via error_code
109
- if (frame.error_code) {
110
- stream.completed = true;
111
- stream.errorCode = frame.error_code;
112
- return true;
113
- }
114
-
115
- // Normal completion
116
- if (frame.is_last) {
117
- stream.completed = true;
118
- return true;
119
- }
120
-
121
- return false;
122
- }
123
-
124
- /**
125
- * Send a StreamFrame on an outgoing stream, enforcing window-based flow control.
126
- *
127
- * - seq=0 with window_size initialises remainingWindow (no decrement for opening frame).
128
- * - Subsequent sends decrement remainingWindow when flow control is active.
129
- * - Throws NCP-STREAM-WINDOW-OVERFLOW when remainingWindow === 0.
130
- *
131
- * @throws {NcpError} NCP-STREAM-WINDOW-OVERFLOW if window is exhausted.
132
- */
133
- send(frame: StreamFrame): void {
134
- let out = this.outgoing.get(frame.stream_id);
135
-
136
- if (!out) {
137
- out = {
138
- streamId: frame.stream_id,
139
- remainingWindow: undefined,
140
- paused: false,
141
- };
142
- this.outgoing.set(frame.stream_id, out);
143
- }
144
-
145
- // Opening frame: initialise window from window_size if provided.
146
- if (frame.seq === 0 && frame.window_size !== undefined) {
147
- out.remainingWindow = frame.window_size;
148
- return; // opening frame does not consume a window slot
149
- }
150
-
151
- // Flow control check for subsequent frames.
152
- if (out.remainingWindow !== undefined) {
153
- if (out.remainingWindow === 0) {
154
- throw new NcpError(
155
- NCP_ERROR_CODES.NCP_STREAM_WINDOW_OVERFLOW,
156
- `Window exhausted on stream ${frame.stream_id}`,
157
- );
158
- }
159
- out.remainingWindow -= 1;
160
- }
161
- }
162
-
163
- /**
164
- * Update the send window for a stream.
165
- *
166
- * Called when a reverse-direction StreamFrame arrives with data=[] and window_size set.
167
- * Replaces remainingWindow with new_size. Sets paused=true when new_size === 0.
168
- */
169
- updateWindow(streamId: string, newSize: number): void {
170
- let out = this.outgoing.get(streamId);
171
- if (!out) {
172
- out = {
173
- streamId,
174
- remainingWindow: newSize,
175
- paused: newSize === 0,
176
- };
177
- this.outgoing.set(streamId, out);
178
- return;
179
- }
180
- out.remainingWindow = newSize;
181
- out.paused = newSize === 0;
182
- }
183
-
184
- /**
185
- * Returns true when the outgoing stream is paused (window=0 was received).
186
- * Resumes (returns false) once a non-zero window update arrives.
187
- */
188
- isPaused(streamId: string): boolean {
189
- return this.outgoing.get(streamId)?.paused ?? false;
190
- }
191
-
192
- /** Get reassembled data for a completed stream. */
193
- getData(streamId: string): unknown[] | null {
194
- const stream = this.streams.get(streamId);
195
- if (!stream || !stream.completed) return null;
196
- return stream.chunks.flat();
197
- }
198
-
199
- /** Get error code if stream terminated with error. */
200
- getError(streamId: string): string | undefined {
201
- return this.streams.get(streamId)?.errorCode;
202
- }
203
-
204
- /** Number of active (non-completed) streams. */
205
- get activeCount(): number {
206
- let count = 0;
207
- for (const s of this.streams.values()) {
208
- if (!s.completed) count++;
209
- }
210
- return count;
211
- }
212
- }
@@ -1,86 +0,0 @@
1
- // Copyright 2026 INNO LOTUS PTY LTD
2
- // SPDX-License-Identifier: Apache-2.0
3
-
4
- import type { NdpResolveResult } from "./frames.js";
5
-
6
- export const DNS_TXT_DEFAULT_TTL = 300;
7
-
8
- /**
9
- * Extract the hostname from an NWP target URI.
10
- * e.g. "nwp://api.example.com/products" → "api.example.com"
11
- */
12
- export function extractHostFromTarget(target: string): string | undefined {
13
- if (!target.startsWith("nwp://")) return undefined;
14
- const rest = target.slice("nwp://".length);
15
- const slashIdx = rest.indexOf("/");
16
- const host = slashIdx === -1 ? rest : rest.slice(0, slashIdx);
17
- return host.length > 0 ? host : undefined;
18
- }
19
-
20
- /**
21
- * Parse one TXT record (array of string chunks) into an NdpResolveResult.
22
- *
23
- * Expected format (chunks joined with a single space):
24
- * v=nps1 type=<type> port=<port> nid=<nid> fp=sha256:<fingerprint>
25
- *
26
- * Rules:
27
- * - `v` MUST be present and equal to "nps1"
28
- * - `nid` MUST be present
29
- * - `port` defaults to 17433 when absent
30
- * - `fp` is mapped to certFingerprint
31
- */
32
- export function parseNpsTxtRecord(
33
- parts: string[],
34
- host: string,
35
- ): NdpResolveResult | undefined {
36
- const joined = parts.join(" ");
37
- const kv = new Map<string, string>();
38
-
39
- for (const token of joined.split(/\s+/)) {
40
- const eq = token.indexOf("=");
41
- if (eq === -1) continue;
42
- const key = token.slice(0, eq);
43
- const val = token.slice(eq + 1);
44
- kv.set(key, val);
45
- }
46
-
47
- if (kv.get("v") !== "nps1") return undefined;
48
-
49
- const nid = kv.get("nid");
50
- if (!nid) return undefined;
51
-
52
- const rawPort = kv.get("port");
53
- const port = rawPort !== undefined ? Number(rawPort) : 17433;
54
- const fp = kv.get("fp");
55
-
56
- const result: NdpResolveResult = {
57
- host,
58
- port,
59
- ttl: DNS_TXT_DEFAULT_TTL,
60
- };
61
-
62
- if (fp !== undefined) {
63
- result.certFingerprint = fp;
64
- }
65
-
66
- return result;
67
- }
68
-
69
- /**
70
- * Abstraction over DNS TXT lookups.
71
- * The default implementation uses Node's `dns.promises`; pass a mock in tests.
72
- */
73
- export interface DnsTxtLookup {
74
- resolveTxt(hostname: string): Promise<string[][]>;
75
- }
76
-
77
- /**
78
- * System DNS TXT resolver backed by Node's built-in `dns.promises`.
79
- * The dynamic `import()` keeps browser bundles from failing at parse time.
80
- */
81
- export class SystemDnsTxtLookup implements DnsTxtLookup {
82
- async resolveTxt(hostname: string): Promise<string[][]> {
83
- const { promises: dns } = await import("node:dns");
84
- return dns.resolveTxt(hostname);
85
- }
86
- }
package/src/ndp/frames.ts DELETED
@@ -1,124 +0,0 @@
1
- // Copyright 2026 INNO LOTUS PTY LTD
2
- // SPDX-License-Identifier: Apache-2.0
3
-
4
- import { EncodingTier, FrameType } from "../core/frames.js";
5
- import type { NpsFrame } from "../core/codec.js";
6
-
7
- export interface NdpAddress {
8
- host: string;
9
- port: number;
10
- protocol: string;
11
- }
12
-
13
- export interface NdpGraphNode {
14
- nid: string;
15
- addresses: readonly NdpAddress[];
16
- capabilities: readonly string[];
17
- nodeType?: string;
18
- }
19
-
20
- export interface NdpResolveResult {
21
- host: string;
22
- port: number;
23
- ttl: number;
24
- certFingerprint?: string;
25
- }
26
-
27
- export class AnnounceFrame implements NpsFrame {
28
- readonly frameType = FrameType.ANNOUNCE;
29
- readonly preferredTier = EncodingTier.MSGPACK;
30
-
31
- constructor(
32
- public readonly nid: string,
33
- public readonly addresses: readonly NdpAddress[],
34
- public readonly capabilities: readonly string[],
35
- public readonly ttl: number,
36
- public readonly timestamp: string,
37
- public readonly signature: string,
38
- public readonly nodeType?: string,
39
- ) {}
40
-
41
- unsignedDict(): Record<string, unknown> {
42
- return {
43
- nid: this.nid,
44
- addresses: this.addresses,
45
- capabilities: this.capabilities,
46
- ttl: this.ttl,
47
- timestamp: this.timestamp,
48
- node_type: this.nodeType ?? null,
49
- };
50
- }
51
-
52
- toDict(): Record<string, unknown> {
53
- return { ...this.unsignedDict(), signature: this.signature };
54
- }
55
-
56
- static fromDict(data: Record<string, unknown>): AnnounceFrame {
57
- return new AnnounceFrame(
58
- data["nid"] as string,
59
- data["addresses"] as NdpAddress[],
60
- data["capabilities"] as string[],
61
- data["ttl"] as number,
62
- data["timestamp"] as string,
63
- data["signature"] as string,
64
- (data["node_type"] as string | null) ?? undefined,
65
- );
66
- }
67
- }
68
-
69
- export class ResolveFrame implements NpsFrame {
70
- readonly frameType = FrameType.RESOLVE;
71
- readonly preferredTier = EncodingTier.MSGPACK;
72
-
73
- constructor(
74
- public readonly target: string,
75
- public readonly requesterNid?: string,
76
- public readonly resolved?: NdpResolveResult,
77
- ) {}
78
-
79
- toDict(): Record<string, unknown> {
80
- return {
81
- target: this.target,
82
- requester_nid: this.requesterNid ?? null,
83
- resolved: this.resolved ?? null,
84
- };
85
- }
86
-
87
- static fromDict(data: Record<string, unknown>): ResolveFrame {
88
- return new ResolveFrame(
89
- data["target"] as string,
90
- (data["requester_nid"] as string | null) ?? undefined,
91
- (data["resolved"] as NdpResolveResult | null) ?? undefined,
92
- );
93
- }
94
- }
95
-
96
- export class GraphFrame implements NpsFrame {
97
- readonly frameType = FrameType.GRAPH;
98
- readonly preferredTier = EncodingTier.MSGPACK;
99
-
100
- constructor(
101
- public readonly seq: number,
102
- public readonly initialSync: boolean,
103
- public readonly nodes?: readonly NdpGraphNode[],
104
- public readonly patch?: readonly Record<string, unknown>[],
105
- ) {}
106
-
107
- toDict(): Record<string, unknown> {
108
- return {
109
- seq: this.seq,
110
- initial_sync: this.initialSync,
111
- nodes: this.nodes ?? null,
112
- patch: this.patch ?? null,
113
- };
114
- }
115
-
116
- static fromDict(data: Record<string, unknown>): GraphFrame {
117
- return new GraphFrame(
118
- data["seq"] as number,
119
- data["initial_sync"] as boolean,
120
- (data["nodes"] as NdpGraphNode[] | null) ?? undefined,
121
- (data["patch"] as Record<string, unknown>[] | null) ?? undefined,
122
- );
123
- }
124
- }
@@ -1,116 +0,0 @@
1
- // Copyright 2026 INNO LOTUS PTY LTD
2
- // SPDX-License-Identifier: Apache-2.0
3
-
4
- import type { AnnounceFrame, NdpResolveResult } from "./frames.js";
5
- import {
6
- extractHostFromTarget,
7
- parseNpsTxtRecord,
8
- SystemDnsTxtLookup,
9
- type DnsTxtLookup,
10
- } from "./dns-txt.js";
11
-
12
- interface RegistryEntry {
13
- frame: AnnounceFrame;
14
- expiresAt: number;
15
- }
16
-
17
- export class InMemoryNdpRegistry {
18
- private readonly _store = new Map<string, RegistryEntry>();
19
-
20
- // Replaceable for testing
21
- clock: () => number = () => Date.now();
22
-
23
- announce(frame: AnnounceFrame): void {
24
- const expiresAt = this.clock() + frame.ttl * 1000;
25
- if (frame.ttl === 0) {
26
- this._store.delete(frame.nid);
27
- return;
28
- }
29
- this._store.set(frame.nid, { frame, expiresAt });
30
- }
31
-
32
- getByNid(nid: string): AnnounceFrame | undefined {
33
- const entry = this._store.get(nid);
34
- if (entry === undefined) return undefined;
35
- if (this.clock() > entry.expiresAt) {
36
- this._store.delete(nid);
37
- return undefined;
38
- }
39
- return entry.frame;
40
- }
41
-
42
- resolve(target: string): NdpResolveResult | undefined {
43
- for (const [nid, entry] of this._store) {
44
- if (this.clock() > entry.expiresAt) { this._store.delete(nid); continue; }
45
- if (!InMemoryNdpRegistry.nwpTargetMatchesNid(nid, target)) continue;
46
- const addr = entry.frame.addresses[0];
47
- if (addr === undefined) continue;
48
- return { host: addr.host, port: addr.port, ttl: entry.frame.ttl };
49
- }
50
- return undefined;
51
- }
52
-
53
- getAll(): AnnounceFrame[] {
54
- const now = this.clock();
55
- const result: AnnounceFrame[] = [];
56
- for (const [nid, entry] of this._store) {
57
- if (now > entry.expiresAt) { this._store.delete(nid); continue; }
58
- result.push(entry.frame);
59
- }
60
- return result;
61
- }
62
-
63
- async resolveWithDns(
64
- target: string,
65
- resolver: DnsTxtLookup = new SystemDnsTxtLookup(),
66
- ): Promise<NdpResolveResult | undefined> {
67
- // 1. Try in-memory registry first
68
- const cached = this.resolve(target);
69
- if (cached !== undefined) return cached;
70
-
71
- // 2. Extract hostname and fall back to DNS TXT lookup
72
- const host = extractHostFromTarget(target);
73
- if (host === undefined) return undefined;
74
-
75
- const txtHost = `_nps-node.${host}`;
76
- let records: string[][];
77
- try {
78
- records = await resolver.resolveTxt(txtHost);
79
- } catch {
80
- return undefined;
81
- }
82
-
83
- for (const record of records) {
84
- const result = parseNpsTxtRecord(record, host);
85
- if (result !== undefined) return result;
86
- }
87
-
88
- return undefined;
89
- }
90
-
91
- static nwpTargetMatchesNid(nid: string, target: string): boolean {
92
- // NID: urn:nps:node:{authority}:{path-segment}
93
- // target: nwp://{authority}/{path}
94
- const nidParts = nid.split(":");
95
- if (nidParts.length < 5 || nidParts[0] !== "urn" || nidParts[1] !== "nps" || nidParts[2] !== "node") {
96
- return false;
97
- }
98
- if (!target.startsWith("nwp://")) return false;
99
-
100
- const nidAuthority = nidParts[3]!;
101
- const nidPath = nidParts[4]!;
102
- const rest = target.slice("nwp://".length);
103
- const slashIdx = rest.indexOf("/");
104
- if (slashIdx === -1) return false;
105
-
106
- const urlAuthority = rest.slice(0, slashIdx);
107
- const urlPath = rest.slice(slashIdx + 1); // without leading slash
108
-
109
- if (urlAuthority !== nidAuthority) return false;
110
-
111
- // nidPath must be a prefix of urlPath at a segment boundary
112
- if (urlPath === nidPath) return true;
113
- if (urlPath.startsWith(nidPath + "/")) return true;
114
- return false;
115
- }
116
- }
@@ -1,12 +0,0 @@
1
- // Copyright 2026 INNO LOTUS PTY LTD
2
- // SPDX-License-Identifier: Apache-2.0
3
-
4
- import { FrameRegistry } from "../core/registry.js";
5
- import { FrameType } from "../core/frames.js";
6
- import { AnnounceFrame, GraphFrame, ResolveFrame } from "./frames.js";
7
-
8
- export function registerNdpFrames(registry: FrameRegistry): void {
9
- registry.register(FrameType.ANNOUNCE, AnnounceFrame);
10
- registry.register(FrameType.RESOLVE, ResolveFrame);
11
- registry.register(FrameType.GRAPH, GraphFrame);
12
- }
@@ -1,64 +0,0 @@
1
- // Copyright 2026 INNO LOTUS PTY LTD
2
- // SPDX-License-Identifier: Apache-2.0
3
-
4
- import * as ed25519 from "@noble/ed25519";
5
- import { sha512 } from "@noble/hashes/sha512";
6
- import type { AnnounceFrame } from "./frames.js";
7
-
8
- ed25519.etc.sha512Sync = (...m) => sha512(ed25519.etc.concatBytes(...m));
9
-
10
- export interface NdpAnnounceResult {
11
- isValid: boolean;
12
- errorCode?: string;
13
- message?: string;
14
- }
15
-
16
- export const NdpAnnounceResult = {
17
- ok: (): NdpAnnounceResult => ({ isValid: true }),
18
- fail: (errorCode: string, message: string): NdpAnnounceResult => ({ isValid: false, errorCode, message }),
19
- };
20
-
21
- export class NdpAnnounceValidator {
22
- private readonly _keys = new Map<string, string>(); // nid → "ed25519:<hex>"
23
-
24
- registerPublicKey(nid: string, encodedPubKey: string): void {
25
- this._keys.set(nid, encodedPubKey);
26
- }
27
-
28
- removePublicKey(nid: string): void {
29
- this._keys.delete(nid);
30
- }
31
-
32
- get knownPublicKeys(): ReadonlyMap<string, string> {
33
- return this._keys;
34
- }
35
-
36
- validate(frame: AnnounceFrame): NdpAnnounceResult {
37
- const encoded = this._keys.get(frame.nid);
38
- if (encoded === undefined) {
39
- return NdpAnnounceResult.fail("NDP-ANNOUNCE-NID-MISMATCH", `No public key registered for NID: ${frame.nid}`);
40
- }
41
-
42
- try {
43
- const prefix = "ed25519:";
44
- const pubHex = encoded.startsWith(prefix) ? encoded.slice(prefix.length) : encoded;
45
- const pubKey = Buffer.from(pubHex, "hex");
46
-
47
- const sig = frame.signature;
48
- if (!sig.startsWith(prefix)) {
49
- return NdpAnnounceResult.fail("NDP-ANNOUNCE-SIG-INVALID", "Signature must start with 'ed25519:'");
50
- }
51
- const sigBytes = Buffer.from(sig.slice(prefix.length), "base64");
52
-
53
- const unsigned = frame.unsignedDict();
54
- const canonical = JSON.stringify(unsigned, Object.keys(unsigned).sort());
55
- const message = new TextEncoder().encode(canonical);
56
-
57
- const valid = ed25519.verify(sigBytes, message, pubKey);
58
- if (!valid) return NdpAnnounceResult.fail("NDP-ANNOUNCE-SIG-INVALID", "Ed25519 signature verification failed.");
59
- return NdpAnnounceResult.ok();
60
- } catch {
61
- return NdpAnnounceResult.fail("NDP-ANNOUNCE-SIG-INVALID", "Ed25519 signature verification failed.");
62
- }
63
- }
64
- }