@labacacia/nps-sdk 1.0.0-alpha.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 (311) hide show
  1. package/CONTRIBUTING.md +33 -0
  2. package/LICENSE +170 -0
  3. package/NOTICE +7 -0
  4. package/README.md +153 -0
  5. package/dist/codec-CmHeovTV.d.cts +120 -0
  6. package/dist/codec-CmHeovTV.d.ts +120 -0
  7. package/dist/core/anchor-cache.d.ts +42 -0
  8. package/dist/core/anchor-cache.d.ts.map +1 -0
  9. package/dist/core/anchor-cache.js +104 -0
  10. package/dist/core/anchor-cache.js.map +1 -0
  11. package/dist/core/cache.d.ts +14 -0
  12. package/dist/core/cache.d.ts.map +1 -0
  13. package/dist/core/cache.js +80 -0
  14. package/dist/core/cache.js.map +1 -0
  15. package/dist/core/canonical-json.d.ts +12 -0
  16. package/dist/core/canonical-json.d.ts.map +1 -0
  17. package/dist/core/canonical-json.js +44 -0
  18. package/dist/core/canonical-json.js.map +1 -0
  19. package/dist/core/codec.d.ts +32 -0
  20. package/dist/core/codec.d.ts.map +1 -0
  21. package/dist/core/codec.js +119 -0
  22. package/dist/core/codec.js.map +1 -0
  23. package/dist/core/codecs/index.d.ts +4 -0
  24. package/dist/core/codecs/index.d.ts.map +1 -0
  25. package/dist/core/codecs/index.js +6 -0
  26. package/dist/core/codecs/index.js.map +1 -0
  27. package/dist/core/codecs/ncp-codec.d.ts +39 -0
  28. package/dist/core/codecs/ncp-codec.d.ts.map +1 -0
  29. package/dist/core/codecs/ncp-codec.js +93 -0
  30. package/dist/core/codecs/ncp-codec.js.map +1 -0
  31. package/dist/core/codecs/tier1-json-codec.d.ts +10 -0
  32. package/dist/core/codecs/tier1-json-codec.d.ts.map +1 -0
  33. package/dist/core/codecs/tier1-json-codec.js +28 -0
  34. package/dist/core/codecs/tier1-json-codec.js.map +1 -0
  35. package/dist/core/codecs/tier2-msgpack-codec.d.ts +10 -0
  36. package/dist/core/codecs/tier2-msgpack-codec.d.ts.map +1 -0
  37. package/dist/core/codecs/tier2-msgpack-codec.js +26 -0
  38. package/dist/core/codecs/tier2-msgpack-codec.js.map +1 -0
  39. package/dist/core/crypto-provider.d.ts +31 -0
  40. package/dist/core/crypto-provider.d.ts.map +1 -0
  41. package/dist/core/crypto-provider.js +10 -0
  42. package/dist/core/crypto-provider.js.map +1 -0
  43. package/dist/core/exceptions.d.ts +27 -0
  44. package/dist/core/exceptions.d.ts.map +1 -0
  45. package/dist/core/exceptions.js +52 -0
  46. package/dist/core/exceptions.js.map +1 -0
  47. package/dist/core/frame-header.d.ts +87 -0
  48. package/dist/core/frame-header.d.ts.map +1 -0
  49. package/dist/core/frame-header.js +185 -0
  50. package/dist/core/frame-header.js.map +1 -0
  51. package/dist/core/frame-registry.d.ts +35 -0
  52. package/dist/core/frame-registry.d.ts.map +1 -0
  53. package/dist/core/frame-registry.js +63 -0
  54. package/dist/core/frame-registry.js.map +1 -0
  55. package/dist/core/frames.d.ts +80 -0
  56. package/dist/core/frames.d.ts.map +1 -0
  57. package/dist/core/frames.js +153 -0
  58. package/dist/core/frames.js.map +1 -0
  59. package/dist/core/index.cjs +371 -0
  60. package/dist/core/index.cjs.map +1 -0
  61. package/dist/core/index.d.cts +41 -0
  62. package/dist/core/index.d.ts +9 -0
  63. package/dist/core/index.d.ts.map +1 -0
  64. package/dist/core/index.js +10 -0
  65. package/dist/core/index.js.map +1 -0
  66. package/dist/core/registry.d.ts +11 -0
  67. package/dist/core/registry.d.ts.map +1 -0
  68. package/dist/core/registry.js +17 -0
  69. package/dist/core/registry.js.map +1 -0
  70. package/dist/core/status-codes.d.ts +28 -0
  71. package/dist/core/status-codes.d.ts.map +1 -0
  72. package/dist/core/status-codes.js +38 -0
  73. package/dist/core/status-codes.js.map +1 -0
  74. package/dist/frames-B3qLdl_g.d.cts +77 -0
  75. package/dist/frames-Ff7-ZPUl.d.ts +77 -0
  76. package/dist/index.cjs +1556 -0
  77. package/dist/index.cjs.map +1 -0
  78. package/dist/index.d.cts +21 -0
  79. package/dist/index.d.ts +2 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +10 -0
  82. package/dist/index.js.map +1 -0
  83. package/dist/ncp/frames/anchor-frame.d.ts +29 -0
  84. package/dist/ncp/frames/anchor-frame.d.ts.map +1 -0
  85. package/dist/ncp/frames/anchor-frame.js +54 -0
  86. package/dist/ncp/frames/anchor-frame.js.map +1 -0
  87. package/dist/ncp/frames/caps-frame.d.ts +29 -0
  88. package/dist/ncp/frames/caps-frame.d.ts.map +1 -0
  89. package/dist/ncp/frames/caps-frame.js +29 -0
  90. package/dist/ncp/frames/caps-frame.js.map +1 -0
  91. package/dist/ncp/frames/diff-frame.d.ts +32 -0
  92. package/dist/ncp/frames/diff-frame.d.ts.map +1 -0
  93. package/dist/ncp/frames/diff-frame.js +37 -0
  94. package/dist/ncp/frames/diff-frame.js.map +1 -0
  95. package/dist/ncp/frames/error-frame.d.ts +16 -0
  96. package/dist/ncp/frames/error-frame.d.ts.map +1 -0
  97. package/dist/ncp/frames/error-frame.js +13 -0
  98. package/dist/ncp/frames/error-frame.js.map +1 -0
  99. package/dist/ncp/frames/hello-frame.d.ts +21 -0
  100. package/dist/ncp/frames/hello-frame.d.ts.map +1 -0
  101. package/dist/ncp/frames/hello-frame.js +25 -0
  102. package/dist/ncp/frames/hello-frame.js.map +1 -0
  103. package/dist/ncp/frames/stream-frame.d.ts +16 -0
  104. package/dist/ncp/frames/stream-frame.d.ts.map +1 -0
  105. package/dist/ncp/frames/stream-frame.js +18 -0
  106. package/dist/ncp/frames/stream-frame.js.map +1 -0
  107. package/dist/ncp/frames.d.ts +76 -0
  108. package/dist/ncp/frames.d.ts.map +1 -0
  109. package/dist/ncp/frames.js +147 -0
  110. package/dist/ncp/frames.js.map +1 -0
  111. package/dist/ncp/handshake.d.ts +30 -0
  112. package/dist/ncp/handshake.d.ts.map +1 -0
  113. package/dist/ncp/handshake.js +80 -0
  114. package/dist/ncp/handshake.js.map +1 -0
  115. package/dist/ncp/index.cjs +188 -0
  116. package/dist/ncp/index.cjs.map +1 -0
  117. package/dist/ncp/index.d.cts +6 -0
  118. package/dist/ncp/index.d.ts +11 -0
  119. package/dist/ncp/index.d.ts.map +1 -0
  120. package/dist/ncp/index.js +13 -0
  121. package/dist/ncp/index.js.map +1 -0
  122. package/dist/ncp/ncp-error-codes.d.ts +22 -0
  123. package/dist/ncp/ncp-error-codes.d.ts.map +1 -0
  124. package/dist/ncp/ncp-error-codes.js +32 -0
  125. package/dist/ncp/ncp-error-codes.js.map +1 -0
  126. package/dist/ncp/ncp-patch-format.d.ts +7 -0
  127. package/dist/ncp/ncp-patch-format.d.ts.map +1 -0
  128. package/dist/ncp/ncp-patch-format.js +13 -0
  129. package/dist/ncp/ncp-patch-format.js.map +1 -0
  130. package/dist/ncp/registry.d.ts +3 -0
  131. package/dist/ncp/registry.d.ts.map +1 -0
  132. package/dist/ncp/registry.js +12 -0
  133. package/dist/ncp/registry.js.map +1 -0
  134. package/dist/ncp/stream-manager.d.ts +57 -0
  135. package/dist/ncp/stream-manager.d.ts.map +1 -0
  136. package/dist/ncp/stream-manager.js +163 -0
  137. package/dist/ncp/stream-manager.js.map +1 -0
  138. package/dist/ndp/frames.d.ts +56 -0
  139. package/dist/ndp/frames.d.ts.map +1 -0
  140. package/dist/ndp/frames.js +87 -0
  141. package/dist/ndp/frames.js.map +1 -0
  142. package/dist/ndp/index.cjs +252 -0
  143. package/dist/ndp/index.cjs.map +1 -0
  144. package/dist/ndp/index.d.cts +86 -0
  145. package/dist/ndp/index.d.ts +5 -0
  146. package/dist/ndp/index.d.ts.map +1 -0
  147. package/dist/ndp/index.js +7 -0
  148. package/dist/ndp/index.js.map +1 -0
  149. package/dist/ndp/ndp-registry.d.ts +11 -0
  150. package/dist/ndp/ndp-registry.d.ts.map +1 -0
  151. package/dist/ndp/ndp-registry.js +79 -0
  152. package/dist/ndp/ndp-registry.js.map +1 -0
  153. package/dist/ndp/registry.d.ts +3 -0
  154. package/dist/ndp/registry.d.ts.map +1 -0
  155. package/dist/ndp/registry.js +10 -0
  156. package/dist/ndp/registry.js.map +1 -0
  157. package/dist/ndp/validator.d.ts +18 -0
  158. package/dist/ndp/validator.d.ts.map +1 -0
  159. package/dist/ndp/validator.js +48 -0
  160. package/dist/ndp/validator.js.map +1 -0
  161. package/dist/nip/frames.d.ts +44 -0
  162. package/dist/nip/frames.d.ts.map +1 -0
  163. package/dist/nip/frames.js +81 -0
  164. package/dist/nip/frames.js.map +1 -0
  165. package/dist/nip/identity.d.ts +18 -0
  166. package/dist/nip/identity.d.ts.map +1 -0
  167. package/dist/nip/identity.js +94 -0
  168. package/dist/nip/identity.js.map +1 -0
  169. package/dist/nip/index.cjs +214 -0
  170. package/dist/nip/index.cjs.map +1 -0
  171. package/dist/nip/index.d.cts +65 -0
  172. package/dist/nip/index.d.ts +4 -0
  173. package/dist/nip/index.d.ts.map +1 -0
  174. package/dist/nip/index.js +6 -0
  175. package/dist/nip/index.js.map +1 -0
  176. package/dist/nip/registry.d.ts +3 -0
  177. package/dist/nip/registry.d.ts.map +1 -0
  178. package/dist/nip/registry.js +10 -0
  179. package/dist/nip/registry.js.map +1 -0
  180. package/dist/nop/client.d.ts +34 -0
  181. package/dist/nop/client.d.ts.map +1 -0
  182. package/dist/nop/client.js +90 -0
  183. package/dist/nop/client.js.map +1 -0
  184. package/dist/nop/frames.d.ts +65 -0
  185. package/dist/nop/frames.d.ts.map +1 -0
  186. package/dist/nop/frames.js +148 -0
  187. package/dist/nop/frames.js.map +1 -0
  188. package/dist/nop/index.cjs +762 -0
  189. package/dist/nop/index.cjs.map +1 -0
  190. package/dist/nop/index.d.cts +155 -0
  191. package/dist/nop/index.d.ts +5 -0
  192. package/dist/nop/index.d.ts.map +1 -0
  193. package/dist/nop/index.js +7 -0
  194. package/dist/nop/index.js.map +1 -0
  195. package/dist/nop/models.d.ts +58 -0
  196. package/dist/nop/models.d.ts.map +1 -0
  197. package/dist/nop/models.js +50 -0
  198. package/dist/nop/models.js.map +1 -0
  199. package/dist/nop/nop-types.d.ts +136 -0
  200. package/dist/nop/nop-types.d.ts.map +1 -0
  201. package/dist/nop/nop-types.js +44 -0
  202. package/dist/nop/nop-types.js.map +1 -0
  203. package/dist/nop/registry.d.ts +3 -0
  204. package/dist/nop/registry.d.ts.map +1 -0
  205. package/dist/nop/registry.js +11 -0
  206. package/dist/nop/registry.js.map +1 -0
  207. package/dist/nwp/client.d.ts +22 -0
  208. package/dist/nwp/client.d.ts.map +1 -0
  209. package/dist/nwp/client.js +101 -0
  210. package/dist/nwp/client.js.map +1 -0
  211. package/dist/nwp/frames.d.ts +46 -0
  212. package/dist/nwp/frames.d.ts.map +1 -0
  213. package/dist/nwp/frames.js +81 -0
  214. package/dist/nwp/frames.js.map +1 -0
  215. package/dist/nwp/index.cjs +658 -0
  216. package/dist/nwp/index.cjs.map +1 -0
  217. package/dist/nwp/index.d.cts +65 -0
  218. package/dist/nwp/index.d.ts +4 -0
  219. package/dist/nwp/index.d.ts.map +1 -0
  220. package/dist/nwp/index.js +6 -0
  221. package/dist/nwp/index.js.map +1 -0
  222. package/dist/nwp/registry.d.ts +3 -0
  223. package/dist/nwp/registry.d.ts.map +1 -0
  224. package/dist/nwp/registry.js +9 -0
  225. package/dist/nwp/registry.js.map +1 -0
  226. package/dist/setup.d.ts +10 -0
  227. package/dist/setup.d.ts.map +1 -0
  228. package/dist/setup.js +29 -0
  229. package/dist/setup.js.map +1 -0
  230. package/nip-ca-server/Dockerfile +27 -0
  231. package/nip-ca-server/README.md +45 -0
  232. package/nip-ca-server/db/001_init.sql +25 -0
  233. package/nip-ca-server/docker-compose.yml +29 -0
  234. package/nip-ca-server/package.json +23 -0
  235. package/nip-ca-server/src/ca.ts +155 -0
  236. package/nip-ca-server/src/db.ts +104 -0
  237. package/nip-ca-server/src/index.ts +157 -0
  238. package/nip-ca-server/tsconfig.json +13 -0
  239. package/package.json +47 -0
  240. package/src/core/anchor-cache.ts +129 -0
  241. package/src/core/cache.ts +93 -0
  242. package/src/core/canonical-json.ts +50 -0
  243. package/src/core/codec.ts +158 -0
  244. package/src/core/codecs/index.ts +5 -0
  245. package/src/core/codecs/ncp-codec.ts +170 -0
  246. package/src/core/codecs/tier1-json-codec.ts +33 -0
  247. package/src/core/codecs/tier2-msgpack-codec.ts +30 -0
  248. package/src/core/crypto-provider.ts +47 -0
  249. package/src/core/exceptions.ts +57 -0
  250. package/src/core/frame-header.ts +282 -0
  251. package/src/core/frame-registry.ts +91 -0
  252. package/src/core/frames.ts +183 -0
  253. package/src/core/index.ts +10 -0
  254. package/src/core/registry.ts +28 -0
  255. package/src/core/status-codes.ts +46 -0
  256. package/src/index.ts +10 -0
  257. package/src/ncp/frames/anchor-frame.ts +87 -0
  258. package/src/ncp/frames/caps-frame.ts +59 -0
  259. package/src/ncp/frames/diff-frame.ts +69 -0
  260. package/src/ncp/frames/error-frame.ts +26 -0
  261. package/src/ncp/frames/hello-frame.ts +50 -0
  262. package/src/ncp/frames/stream-frame.ts +35 -0
  263. package/src/ncp/frames.ts +199 -0
  264. package/src/ncp/handshake.ts +95 -0
  265. package/src/ncp/index.ts +12 -0
  266. package/src/ncp/ncp-error-codes.ts +34 -0
  267. package/src/ncp/ncp-patch-format.ts +16 -0
  268. package/src/ncp/registry.ts +14 -0
  269. package/src/ncp/stream-manager.ts +212 -0
  270. package/src/ndp/frames.ts +124 -0
  271. package/src/ndp/index.ts +7 -0
  272. package/src/ndp/ndp-registry.ts +82 -0
  273. package/src/ndp/registry.ts +12 -0
  274. package/src/ndp/validator.ts +64 -0
  275. package/src/nip/frames.ts +106 -0
  276. package/src/nip/identity.ts +113 -0
  277. package/src/nip/index.ts +6 -0
  278. package/src/nip/registry.ts +12 -0
  279. package/src/nop/client.ts +103 -0
  280. package/src/nop/frames.ts +181 -0
  281. package/src/nop/index.ts +7 -0
  282. package/src/nop/models.ts +79 -0
  283. package/src/nop/nop-types.ts +208 -0
  284. package/src/nop/registry.ts +13 -0
  285. package/src/nwp/client.ts +114 -0
  286. package/src/nwp/frames.ts +116 -0
  287. package/src/nwp/index.ts +6 -0
  288. package/src/nwp/registry.ts +11 -0
  289. package/src/setup.ts +32 -0
  290. package/tests/core/anchor-cache.test.ts +242 -0
  291. package/tests/core/codec.test.ts +205 -0
  292. package/tests/core/frame-registry.test.ts +46 -0
  293. package/tests/core.test.ts +327 -0
  294. package/tests/ncp/diff-binary-bitset.test.ts +107 -0
  295. package/tests/ncp/e2e-enc-reject.test.ts +93 -0
  296. package/tests/ncp/err-error-frame.test.ts +152 -0
  297. package/tests/ncp/frames.test.ts +359 -0
  298. package/tests/ncp/framing.test.ts +233 -0
  299. package/tests/ncp/hello-frame.test.ts +122 -0
  300. package/tests/ncp/inline-anchor.test.ts +88 -0
  301. package/tests/ncp/security.test.ts +184 -0
  302. package/tests/ncp/stream-window.test.ts +167 -0
  303. package/tests/ncp/stream.test.ts +242 -0
  304. package/tests/ncp/version-negotiation.test.ts +123 -0
  305. package/tests/ndp.test.ts +271 -0
  306. package/tests/nip.test.ts +184 -0
  307. package/tests/nop.test.ts +344 -0
  308. package/tests/nwp.test.ts +237 -0
  309. package/tsconfig.json +20 -0
  310. package/tsup.config.ts +20 -0
  311. package/vitest.config.ts +10 -0
@@ -0,0 +1,104 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 LabAcacia / INNO LOTUS PTY LTD
3
+ //
4
+ // AnchorCache — Schema cache with TTL, LRU eviction, poison detection
5
+ // NPS-1 §5.3, §7.2, §9
6
+ import { NcpError } from "./frame-header.js";
7
+ /**
8
+ * AnchorFrame cache with:
9
+ * - TTL-based expiry (NPS-1 §5.3)
10
+ * - LRU eviction at maxSize (NPS-1 §9, default 1000)
11
+ * - Anchor poisoning detection (NPS-1 §7.2)
12
+ */
13
+ export class AnchorCache {
14
+ cache = new Map();
15
+ maxSize;
16
+ getNow;
17
+ constructor(options) {
18
+ this.maxSize = options?.maxSize ?? 1000;
19
+ this.getNow = options?.getNow ?? (() => Date.now());
20
+ }
21
+ /**
22
+ * Cache an AnchorFrame.
23
+ *
24
+ * - ttl=0: frame is valid but not cached (NPS-1 §4.1)
25
+ * - Same anchor_id + same schema: idempotent (no-op)
26
+ * - Same anchor_id + different schema: NCP-ANCHOR-ID-MISMATCH (poison detection)
27
+ *
28
+ * @throws {NcpError} NCP-ANCHOR-ID-MISMATCH on anchor poisoning.
29
+ */
30
+ set(frame) {
31
+ // ttl=0 means use once, don't cache
32
+ if (frame.ttl === 0)
33
+ return;
34
+ const existing = this.cache.get(frame.anchor_id);
35
+ if (existing) {
36
+ // Poison detection: same ID, different schema
37
+ const existingJson = JSON.stringify(existing.frame.schema);
38
+ const newJson = JSON.stringify(frame.schema);
39
+ if (existingJson !== newJson) {
40
+ throw new NcpError("NCP-ANCHOR-ID-MISMATCH", `Anchor poisoning detected: anchor_id ${frame.anchor_id} received with different schema`);
41
+ }
42
+ // Same schema — idempotent, update access time
43
+ existing.lastAccessed = this.getNow();
44
+ return;
45
+ }
46
+ // LRU eviction if at capacity
47
+ if (this.cache.size >= this.maxSize) {
48
+ this.evictLru();
49
+ }
50
+ const ttlMs = (frame.ttl ?? 3600) * 1000;
51
+ this.cache.set(frame.anchor_id, {
52
+ frame,
53
+ expiresAt: this.getNow() + ttlMs,
54
+ lastAccessed: this.getNow(),
55
+ });
56
+ }
57
+ /**
58
+ * Get a cached AnchorFrame by anchor_id.
59
+ *
60
+ * @returns The cached frame, or null if not found or expired.
61
+ */
62
+ get(anchorId) {
63
+ const entry = this.cache.get(anchorId);
64
+ if (!entry)
65
+ return null;
66
+ // Check TTL expiry
67
+ if (this.getNow() >= entry.expiresAt) {
68
+ this.cache.delete(anchorId);
69
+ return null;
70
+ }
71
+ entry.lastAccessed = this.getNow();
72
+ return entry.frame;
73
+ }
74
+ /**
75
+ * Get a cached AnchorFrame, throwing if not found.
76
+ * @throws {NcpError} NCP-ANCHOR-NOT-FOUND if not in cache or expired.
77
+ */
78
+ getRequired(anchorId) {
79
+ const frame = this.get(anchorId);
80
+ if (!frame) {
81
+ throw new NcpError("NCP-ANCHOR-NOT-FOUND", `Schema anchor ${anchorId} not found in cache`);
82
+ }
83
+ return frame;
84
+ }
85
+ /** Current cache size. */
86
+ get size() {
87
+ return this.cache.size;
88
+ }
89
+ /** Evict the least recently accessed entry. */
90
+ evictLru() {
91
+ let oldestKey;
92
+ let oldestTime = Infinity;
93
+ for (const [key, entry] of this.cache) {
94
+ if (entry.lastAccessed < oldestTime) {
95
+ oldestTime = entry.lastAccessed;
96
+ oldestKey = key;
97
+ }
98
+ }
99
+ if (oldestKey) {
100
+ this.cache.delete(oldestKey);
101
+ }
102
+ }
103
+ }
104
+ //# sourceMappingURL=anchor-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchor-cache.js","sourceRoot":"","sources":["../../src/core/anchor-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { AnchorFrame, FrameSchema } from "../ncp/frames.js";
2
+ export declare class AnchorFrameCache {
3
+ private readonly _store;
4
+ clock: () => number;
5
+ set(frame: AnchorFrame): string;
6
+ get(anchorId: string): AnchorFrame | undefined;
7
+ getRequired(anchorId: string): AnchorFrame;
8
+ invalidate(anchorId: string): void;
9
+ get size(): number;
10
+ static computeAnchorId(schema: FrameSchema): string;
11
+ private _evictExpired;
12
+ private static _schemasEqual;
13
+ }
14
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/core/cache.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEjE,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgE;IAGvF,KAAK,EAAE,MAAM,MAAM,CAAoB;IAIvC,GAAG,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM;IAmB/B,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAU9C,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW;IAM1C,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAIlC,IAAI,IAAI,IAAI,MAAM,CAGjB;IAID,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM;IAiBnD,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,MAAM,CAAC,aAAa;CAG7B"}
@@ -0,0 +1,80 @@
1
+ // Copyright 2026 INNO LOTUS PTY LTD
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * AnchorFrameCache — in-process cache for AnchorFrame instances (NPS-1 §4.1).
5
+ */
6
+ import { createHash } from "node:crypto";
7
+ import { NpsAnchorNotFoundError, NpsAnchorPoisonError } from "./exceptions.js";
8
+ export class AnchorFrameCache {
9
+ _store = new Map();
10
+ // Allow clock injection for testing
11
+ clock = () => Date.now();
12
+ // ── Public API ────────────────────────────────────────────────────────────
13
+ set(frame) {
14
+ const anchorId = frame.anchorId.startsWith("sha256:")
15
+ ? frame.anchorId
16
+ : AnchorFrameCache.computeAnchorId(frame.schema);
17
+ const existing = this._store.get(anchorId);
18
+ if (existing !== undefined && this.clock() < existing.expiresAt) {
19
+ if (!AnchorFrameCache._schemasEqual(existing.frame.schema, frame.schema)) {
20
+ throw new NpsAnchorPoisonError(anchorId);
21
+ }
22
+ // Same schema — idempotent; refresh TTL below
23
+ }
24
+ const ttlMs = (frame.ttl ?? 3600) * 1000;
25
+ const expiresAt = this.clock() + ttlMs;
26
+ this._store.set(anchorId, { frame, expiresAt });
27
+ return anchorId;
28
+ }
29
+ get(anchorId) {
30
+ const entry = this._store.get(anchorId);
31
+ if (entry === undefined)
32
+ return undefined;
33
+ if (this.clock() > entry.expiresAt) {
34
+ this._store.delete(anchorId);
35
+ return undefined;
36
+ }
37
+ return entry.frame;
38
+ }
39
+ getRequired(anchorId) {
40
+ const frame = this.get(anchorId);
41
+ if (frame === undefined)
42
+ throw new NpsAnchorNotFoundError(anchorId);
43
+ return frame;
44
+ }
45
+ invalidate(anchorId) {
46
+ this._store.delete(anchorId);
47
+ }
48
+ get size() {
49
+ this._evictExpired();
50
+ return this._store.size;
51
+ }
52
+ // ── Static helpers ────────────────────────────────────────────────────────
53
+ static computeAnchorId(schema) {
54
+ const sorted = [...schema.fields]
55
+ .map((f) => {
56
+ const obj = { name: f.name, type: f.type };
57
+ if (f.semantic !== undefined)
58
+ obj["semantic"] = f.semantic;
59
+ if (f.nullable !== undefined)
60
+ obj["nullable"] = f.nullable;
61
+ return obj;
62
+ })
63
+ .sort((a, b) => String(a["name"]).localeCompare(String(b["name"])));
64
+ const canonical = JSON.stringify(sorted);
65
+ const digest = createHash("sha256").update(canonical, "utf8").digest("hex");
66
+ return `sha256:${digest}`;
67
+ }
68
+ // ── Private ───────────────────────────────────────────────────────────────
69
+ _evictExpired() {
70
+ const now = this.clock();
71
+ for (const [k, entry] of this._store) {
72
+ if (now > entry.expiresAt)
73
+ this._store.delete(k);
74
+ }
75
+ }
76
+ static _schemasEqual(a, b) {
77
+ return AnchorFrameCache.computeAnchorId(a) === AnchorFrameCache.computeAnchorId(b);
78
+ }
79
+ }
80
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/core/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * JCS (RFC 8785) canonical JSON stringify.
3
+ * Used for AnchorFrame anchor_id computation.
4
+ */
5
+ export declare function jcsStringify(obj: unknown): string;
6
+ /**
7
+ * Sorted-key JSON stringify — matches Python's
8
+ * `json.dumps(obj, sort_keys=True, separators=(",",":"))`.
9
+ * Used for NIP signing.
10
+ */
11
+ export declare function sortKeysStringify(obj: unknown): string;
12
+ //# sourceMappingURL=canonical-json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical-json.d.ts","sourceRoot":"","sources":["../../src/core/canonical-json.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAMjD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAEtD"}
@@ -0,0 +1,44 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 LabAcacia / INNO LOTUS PTY LTD
3
+ //
4
+ // Canonical JSON helpers — two distinct serialisation paths used in NPS.
5
+ //
6
+ // AnchorFrame uses JCS (RFC 8785) for anchor_id hashing → jcsStringify.
7
+ // NIP signing uses Python-compatible sorted-key JSON → sortKeysStringify.
8
+ import canonicalizeImport from "canonicalize";
9
+ // `canonicalize` is a CJS module whose default export resolves to a namespace
10
+ // under NodeNext. Cast once to the call signature its .d.ts promises.
11
+ const canonicalize = canonicalizeImport;
12
+ /**
13
+ * JCS (RFC 8785) canonical JSON stringify.
14
+ * Used for AnchorFrame anchor_id computation.
15
+ */
16
+ export function jcsStringify(obj) {
17
+ const result = canonicalize(obj);
18
+ if (result === undefined) {
19
+ throw new Error("canonicalize returned undefined for input");
20
+ }
21
+ return result;
22
+ }
23
+ /**
24
+ * Sorted-key JSON stringify — matches Python's
25
+ * `json.dumps(obj, sort_keys=True, separators=(",",":"))`.
26
+ * Used for NIP signing.
27
+ */
28
+ export function sortKeysStringify(obj) {
29
+ return JSON.stringify(sortKeys(obj));
30
+ }
31
+ function sortKeys(value) {
32
+ if (Array.isArray(value)) {
33
+ return value.map(sortKeys);
34
+ }
35
+ if (value !== null && typeof value === "object") {
36
+ const sorted = {};
37
+ for (const key of Object.keys(value).sort()) {
38
+ sorted[key] = sortKeys(value[key]);
39
+ }
40
+ return sorted;
41
+ }
42
+ return value;
43
+ }
44
+ //# sourceMappingURL=canonical-json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical-json.js","sourceRoot":"","sources":["../../src/core/canonical-json.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,oDAAoD;AACpD,EAAE;AACF,yEAAyE;AACzE,EAAE;AACF,wEAAwE;AACxE,0EAA0E;AAE1E,OAAO,kBAAkB,MAAM,cAAc,CAAC;AAE9C,8EAA8E;AAC9E,sEAAsE;AACtE,MAAM,YAAY,GAAG,kBAEE,CAAC;AAExB;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAY;IAC5C,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAe,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YACtD,MAAM,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAE,KAAiC,CAAC,GAAG,CAAC,CAAC,CAAC;QAClE,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { EncodingTier, FrameHeader, FrameType } from "./frames.js";
2
+ import type { FrameRegistry } from "./registry.js";
3
+ export interface NpsFrame {
4
+ readonly frameType: FrameType;
5
+ readonly preferredTier: EncodingTier;
6
+ toDict(): Record<string, unknown>;
7
+ }
8
+ export declare class Tier1JsonCodec {
9
+ encode(frame: NpsFrame): Uint8Array;
10
+ decode(frameType: FrameType, payload: Uint8Array, registry: FrameRegistry): NpsFrame;
11
+ }
12
+ export declare class Tier2MsgPackCodec {
13
+ encode(frame: NpsFrame): Uint8Array;
14
+ decode(frameType: FrameType, payload: Uint8Array, registry: FrameRegistry): NpsFrame;
15
+ }
16
+ export declare class NpsFrameCodec {
17
+ private readonly _registry;
18
+ private readonly _maxPayload;
19
+ private readonly _json;
20
+ private readonly _msgpack;
21
+ constructor(registry: FrameRegistry, options?: {
22
+ maxPayload?: number;
23
+ });
24
+ encode(frame: NpsFrame, options?: {
25
+ overrideTier?: EncodingTier;
26
+ }): Uint8Array;
27
+ decode(wire: Uint8Array): NpsFrame;
28
+ static peekHeader(wire: Uint8Array): FrameHeader;
29
+ private _buildFlags;
30
+ private _selectCodec;
31
+ }
32
+ //# sourceMappingURL=codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../../src/core/codec.ts"],"names":[],"mappings":"AAUA,OAAO,EAEL,YAAY,EAEZ,WAAW,EACX,SAAS,EACV,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAInD,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,YAAY,CAAC;IACrC,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAID,qBAAa,cAAc;IACzB,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,UAAU;IAWnC,MAAM,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,aAAa,GAAG,QAAQ;CAYrF;AAID,qBAAa,iBAAiB;IAC5B,MAAM,CAAC,KAAK,EAAE,QAAQ,GAAG,UAAU;IAUnC,MAAM,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,aAAa,GAAG,QAAQ;CAWrF;AAID,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAC1C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2B;IACjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA2B;gBAExC,QAAQ,EAAE,aAAa,EAAE,OAAO,GAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAO;IAO1E,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,GAAE;QAAE,YAAY,CAAC,EAAE,YAAY,CAAA;KAAO,GAAG,UAAU;IAiClF,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ;IAOlC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW;IAMhD,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,YAAY;CAKrB"}
@@ -0,0 +1,119 @@
1
+ // Copyright 2026 INNO LOTUS PTY LTD
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * NPS frame codec: Tier-1 (JSON) and Tier-2 (MsgPack) encode/decode,
5
+ * plus the top-level NpsFrameCodec dispatcher.
6
+ */
7
+ import * as msgpack from "@msgpack/msgpack";
8
+ import { NpsCodecError } from "./exceptions.js";
9
+ import { DEFAULT_MAX_PAYLOAD, EncodingTier, FrameFlags, FrameHeader, } from "./frames.js";
10
+ // ── Tier-1 JSON codec ─────────────────────────────────────────────────────────
11
+ export class Tier1JsonCodec {
12
+ encode(frame) {
13
+ try {
14
+ const json = JSON.stringify(frame.toDict());
15
+ return new TextEncoder().encode(json);
16
+ }
17
+ catch (err) {
18
+ throw new NpsCodecError(`Tier-1 JSON encode failed for 0x${frame.frameType.toString(16).padStart(2, "0")}: ${String(err)}`);
19
+ }
20
+ }
21
+ decode(frameType, payload, registry) {
22
+ const cls = registry.resolve(frameType);
23
+ try {
24
+ const text = new TextDecoder().decode(payload);
25
+ const data = JSON.parse(text);
26
+ return cls.fromDict(data);
27
+ }
28
+ catch (err) {
29
+ throw new NpsCodecError(`Tier-1 JSON decode failed for 0x${frameType.toString(16).padStart(2, "0")}: ${String(err)}`);
30
+ }
31
+ }
32
+ }
33
+ // ── Tier-2 MsgPack codec ──────────────────────────────────────────────────────
34
+ export class Tier2MsgPackCodec {
35
+ encode(frame) {
36
+ try {
37
+ return msgpack.encode(frame.toDict());
38
+ }
39
+ catch (err) {
40
+ throw new NpsCodecError(`Tier-2 MsgPack encode failed for 0x${frame.frameType.toString(16).padStart(2, "0")}: ${String(err)}`);
41
+ }
42
+ }
43
+ decode(frameType, payload, registry) {
44
+ const cls = registry.resolve(frameType);
45
+ try {
46
+ const data = msgpack.decode(payload);
47
+ return cls.fromDict(data);
48
+ }
49
+ catch (err) {
50
+ throw new NpsCodecError(`Tier-2 MsgPack decode failed for 0x${frameType.toString(16).padStart(2, "0")}: ${String(err)}`);
51
+ }
52
+ }
53
+ }
54
+ // ── NpsFrameCodec (dispatcher) ────────────────────────────────────────────────
55
+ export class NpsFrameCodec {
56
+ _registry;
57
+ _maxPayload;
58
+ _json = new Tier1JsonCodec();
59
+ _msgpack = new Tier2MsgPackCodec();
60
+ constructor(registry, options = {}) {
61
+ this._registry = registry;
62
+ this._maxPayload = options.maxPayload ?? DEFAULT_MAX_PAYLOAD;
63
+ }
64
+ // ── Encode ────────────────────────────────────────────────────────────────
65
+ encode(frame, options = {}) {
66
+ const tier = options.overrideTier ?? frame.preferredTier;
67
+ const tierCodec = this._selectCodec(tier);
68
+ let payload;
69
+ try {
70
+ payload = tierCodec.encode(frame);
71
+ }
72
+ catch (err) {
73
+ if (err instanceof NpsCodecError)
74
+ throw err;
75
+ throw new NpsCodecError(`Encode failed for 0x${frame.frameType.toString(16)}.`);
76
+ }
77
+ if (payload.length > this._maxPayload) {
78
+ throw new NpsCodecError(`Encoded payload for 0x${frame.frameType.toString(16).padStart(2, "0")} exceeds max_frame_payload` +
79
+ ` (${payload.length} bytes > ${this._maxPayload}). Use StreamFrame (0x03) for large payloads.`);
80
+ }
81
+ const useExt = payload.length > DEFAULT_MAX_PAYLOAD;
82
+ let flags = this._buildFlags(frame, tier);
83
+ if (useExt)
84
+ flags |= FrameFlags.EXT;
85
+ const header = new FrameHeader(frame.frameType, flags, payload.length);
86
+ const headerBytes = header.toBytes();
87
+ const wire = new Uint8Array(headerBytes.length + payload.length);
88
+ wire.set(headerBytes, 0);
89
+ wire.set(payload, headerBytes.length);
90
+ return wire;
91
+ }
92
+ // ── Decode ────────────────────────────────────────────────────────────────
93
+ decode(wire) {
94
+ const header = FrameHeader.parse(wire);
95
+ const payload = wire.slice(header.headerSize, header.headerSize + header.payloadLength);
96
+ const codec = this._selectCodec(header.encodingTier);
97
+ return codec.decode(header.frameType, payload, this._registry);
98
+ }
99
+ static peekHeader(wire) {
100
+ return FrameHeader.parse(wire);
101
+ }
102
+ // ── Private ───────────────────────────────────────────────────────────────
103
+ _buildFlags(frame, tier) {
104
+ let flags = tier === EncodingTier.JSON ? FrameFlags.TIER1_JSON : FrameFlags.TIER2_MSGPACK;
105
+ const isStreamFrame = "isLast" in frame;
106
+ const isFinal = !isStreamFrame || frame.isLast;
107
+ if (isFinal)
108
+ flags |= FrameFlags.FINAL;
109
+ return flags;
110
+ }
111
+ _selectCodec(tier) {
112
+ if (tier === EncodingTier.JSON)
113
+ return this._json;
114
+ if (tier === EncodingTier.MSGPACK)
115
+ return this._msgpack;
116
+ throw new NpsCodecError(`Unsupported encoding tier: 0x${tier.toString(16).padStart(2, "0")}.`);
117
+ }
118
+ }
119
+ //# sourceMappingURL=codec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.js","sourceRoot":"","sources":["../../src/core/codec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from "./ncp-codec.js";
2
+ export { encodeJson, decodeJson } from "./tier1-json-codec.js";
3
+ export { encodeMsgPack, decodeMsgPack } from "./tier2-msgpack-codec.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/codecs/index.ts"],"names":[],"mappings":"AAEA,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 LabAcacia / INNO LOTUS PTY LTD
3
+ export * from "./ncp-codec.js";
4
+ export { encodeJson, decodeJson } from "./tier1-json-codec.js";
5
+ export { encodeMsgPack, decodeMsgPack } from "./tier2-msgpack-codec.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/codecs/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,oDAAoD;AACpD,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { EncodingTier, type FrameHeader } from "../frame-header.js";
2
+ export interface DecodeResult {
3
+ /** Parsed frame header. */
4
+ header: FrameHeader;
5
+ /** Decoded payload object. */
6
+ payload: unknown;
7
+ /** Total bytes consumed (header + payload). */
8
+ bytesConsumed: number;
9
+ }
10
+ export interface EncodeOptions {
11
+ /** Frame type byte. */
12
+ frameType: number;
13
+ /** Encoding tier (default: JSON). */
14
+ tier?: EncodingTier;
15
+ /** Set FINAL flag. */
16
+ final?: boolean;
17
+ /** Set ENC flag. */
18
+ encrypted?: boolean;
19
+ /** Use extended header (for payloads > 64KB). */
20
+ extended?: boolean;
21
+ }
22
+ export interface CodecOptions {
23
+ /** Maximum frame payload in bytes (negotiated via CapsFrame). Default: 65535. */
24
+ maxFramePayload?: number;
25
+ }
26
+ /**
27
+ * Decode a complete NCP frame from a buffer.
28
+ *
29
+ * @returns Decoded header + payload + bytes consumed.
30
+ * @throws {NcpError} NCP-ENCODING-UNSUPPORTED for reserved tiers.
31
+ * @throws {NcpError} NCP-FRAME-PAYLOAD-TOO-LARGE if payload exceeds max.
32
+ * @throws {NcpError} NCP-FRAME-INCOMPLETE if buffer doesn't have enough data.
33
+ */
34
+ export declare function decodeFrame(buffer: Uint8Array, options?: CodecOptions): DecodeResult;
35
+ /**
36
+ * Encode a payload into a complete NCP frame (header + payload bytes).
37
+ */
38
+ export declare function encodeFrame(payload: unknown, options: EncodeOptions): Uint8Array;
39
+ //# sourceMappingURL=ncp-codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ncp-codec.d.ts","sourceRoot":"","sources":["../../../src/core/codecs/ncp-codec.ts"],"names":[],"mappings":"AAMA,OAAO,EAIL,YAAY,EAGZ,KAAK,WAAW,EACjB,MAAM,oBAAoB,CAAC;AAQ5B,MAAM,WAAW,YAAY;IAC3B,2BAA2B;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,8BAA8B;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,+CAA+C;IAC/C,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,uBAAuB;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,sBAAsB;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,oBAAoB;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,iFAAiF;IACjF,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAMD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,UAAU,EAClB,OAAO,CAAC,EAAE,YAAY,GACrB,YAAY,CA8Cd;AAMD;;GAEG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,aAAa,GACrB,UAAU,CA+CZ"}
@@ -0,0 +1,93 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 LabAcacia / INNO LOTUS PTY LTD
3
+ //
4
+ // NCP Codec — Top-level encode/decode dispatcher
5
+ // Routes to Tier-1 JSON or Tier-2 MsgPack based on frame header flags.
6
+ import { parseFrameHeader, writeFrameHeader, buildFlags, EncodingTier, NcpError, DEFAULT_MAX_PAYLOAD, } from "../frame-header.js";
7
+ import { encodeJson, decodeJson } from "./tier1-json-codec.js";
8
+ import { encodeMsgPack, decodeMsgPack } from "./tier2-msgpack-codec.js";
9
+ // ---------------------------------------------------------------------------
10
+ // Decode
11
+ // ---------------------------------------------------------------------------
12
+ /**
13
+ * Decode a complete NCP frame from a buffer.
14
+ *
15
+ * @returns Decoded header + payload + bytes consumed.
16
+ * @throws {NcpError} NCP-ENCODING-UNSUPPORTED for reserved tiers.
17
+ * @throws {NcpError} NCP-FRAME-PAYLOAD-TOO-LARGE if payload exceeds max.
18
+ * @throws {NcpError} NCP-FRAME-INCOMPLETE if buffer doesn't have enough data.
19
+ */
20
+ export function decodeFrame(buffer, options) {
21
+ const header = parseFrameHeader(buffer);
22
+ const maxPayload = options?.maxFramePayload ?? DEFAULT_MAX_PAYLOAD;
23
+ // NCP-F-09: Validate against negotiated max_frame_payload
24
+ if (header.payloadLength > maxPayload) {
25
+ throw new NcpError("NCP-FRAME-PAYLOAD-TOO-LARGE", `Payload ${header.payloadLength} exceeds max_frame_payload ${maxPayload}`);
26
+ }
27
+ const totalSize = header.headerSize + header.payloadLength;
28
+ // NCP-F-05: Buffer underrun — not enough data for payload
29
+ if (buffer.length < totalSize) {
30
+ throw new NcpError("NCP-FRAME-INCOMPLETE", `Buffer has ${buffer.length} bytes but frame needs ${totalSize}`);
31
+ }
32
+ // Extract payload bytes
33
+ const payloadBytes = buffer.subarray(header.headerSize, header.headerSize + header.payloadLength);
34
+ // Route to codec by tier
35
+ let payload;
36
+ switch (header.tier) {
37
+ case EncodingTier.Json:
38
+ payload = decodeJson(payloadBytes);
39
+ break;
40
+ case EncodingTier.MsgPack:
41
+ payload = decodeMsgPack(payloadBytes);
42
+ break;
43
+ default:
44
+ // NCP-E-05: Reserved tiers (0x02, 0x03)
45
+ throw new NcpError("NCP-ENCODING-UNSUPPORTED", `Unsupported encoding tier: 0x${header.tier.toString(16).padStart(2, "0")}`);
46
+ }
47
+ return { header, payload, bytesConsumed: totalSize };
48
+ }
49
+ // ---------------------------------------------------------------------------
50
+ // Encode
51
+ // ---------------------------------------------------------------------------
52
+ /**
53
+ * Encode a payload into a complete NCP frame (header + payload bytes).
54
+ */
55
+ export function encodeFrame(payload, options) {
56
+ const tier = options.tier ?? EncodingTier.Json;
57
+ // Encode payload
58
+ let payloadBytes;
59
+ switch (tier) {
60
+ case EncodingTier.Json:
61
+ payloadBytes = encodeJson(payload);
62
+ break;
63
+ case EncodingTier.MsgPack:
64
+ payloadBytes = encodeMsgPack(payload);
65
+ break;
66
+ default:
67
+ throw new NcpError("NCP-ENCODING-UNSUPPORTED", `Unsupported encoding tier for encode: ${tier}`);
68
+ }
69
+ // Determine if extended header is needed
70
+ const needsExtended = options.extended || payloadBytes.length > DEFAULT_MAX_PAYLOAD;
71
+ const flags = buildFlags({
72
+ tier,
73
+ final: options.final,
74
+ encrypted: options.encrypted,
75
+ extended: needsExtended,
76
+ });
77
+ const header = {
78
+ frameType: options.frameType,
79
+ flags,
80
+ payloadLength: payloadBytes.length,
81
+ tier,
82
+ isFinal: options.final ?? false,
83
+ isEncrypted: options.encrypted ?? false,
84
+ isExtended: needsExtended,
85
+ headerSize: needsExtended ? 8 : 4,
86
+ };
87
+ // Write frame
88
+ const frame = new Uint8Array(header.headerSize + payloadBytes.length);
89
+ writeFrameHeader(header, frame);
90
+ frame.set(payloadBytes, header.headerSize);
91
+ return frame;
92
+ }
93
+ //# sourceMappingURL=ncp-codec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ncp-codec.js","sourceRoot":"","sources":["../../../src/core/codecs/ncp-codec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Encode a frame payload to Tier-1 JSON bytes.
3
+ */
4
+ export declare function encodeJson(payload: unknown): Uint8Array;
5
+ /**
6
+ * Decode Tier-1 JSON bytes to a parsed object.
7
+ * @throws {NcpError} NPS-CLIENT-BAD-FRAME on malformed JSON.
8
+ */
9
+ export declare function decodeJson(bytes: Uint8Array): unknown;
10
+ //# sourceMappingURL=tier1-json-codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tier1-json-codec.d.ts","sourceRoot":"","sources":["../../../src/core/codecs/tier1-json-codec.ts"],"names":[],"mappings":"AAWA;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,CAEvD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAUrD"}