@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,237 @@
1
+ // Copyright 2026 INNO LOTUS PTY LTD
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { describe, expect, it, vi, beforeEach } from "vitest";
5
+ import { QueryFrame, ActionFrame, asyncActionResponseFromDict } from "../src/nwp/frames.js";
6
+ import { NwpClient } from "../src/nwp/client.js";
7
+ import { createDefaultRegistry } from "../src/setup.js";
8
+ import { NpsFrameCodec, EncodingTier } from "../src/core/index.js";
9
+ import { AnchorFrame, CapsFrame, StreamFrame } from "../src/ncp/frames.js";
10
+
11
+ // ── QueryFrame ────────────────────────────────────────────────────────────────
12
+
13
+ describe("QueryFrame", () => {
14
+ it("round-trips via toDict / fromDict", () => {
15
+ const f = new QueryFrame(
16
+ "sha256:" + "a".repeat(64),
17
+ { name: { $eq: "Alice" } },
18
+ 10,
19
+ 0,
20
+ [{ field: "name", dir: "asc" }],
21
+ ["id", "name"],
22
+ { vector: [0.1, 0.2], topK: 5 },
23
+ 2,
24
+ );
25
+ const back = QueryFrame.fromDict(f.toDict());
26
+ expect(back.anchorRef).toBe(f.anchorRef);
27
+ expect(back.limit).toBe(10);
28
+ expect(back.offset).toBe(0);
29
+ expect(back.orderBy?.[0]?.field).toBe("name");
30
+ expect(back.fields?.[1]).toBe("name");
31
+ expect(back.vectorSearch?.topK).toBe(5);
32
+ expect(back.depth).toBe(2);
33
+ });
34
+
35
+ it("handles all-optional constructor (empty query)", () => {
36
+ const f = new QueryFrame();
37
+ const back = QueryFrame.fromDict(f.toDict());
38
+ expect(back.anchorRef).toBeUndefined();
39
+ expect(back.limit).toBeUndefined();
40
+ });
41
+ });
42
+
43
+ // ── ActionFrame ───────────────────────────────────────────────────────────────
44
+
45
+ describe("ActionFrame", () => {
46
+ it("round-trips via toDict / fromDict", () => {
47
+ const f = new ActionFrame(
48
+ "search",
49
+ { q: "test" },
50
+ true,
51
+ "idem-key-1",
52
+ 5000,
53
+ );
54
+ const back = ActionFrame.fromDict(f.toDict());
55
+ expect(back.actionId).toBe("search");
56
+ expect(back.params?.["q"]).toBe("test");
57
+ expect(back.async_).toBe(true);
58
+ expect(back.idempotencyKey).toBe("idem-key-1");
59
+ expect(back.timeoutMs).toBe(5000);
60
+ });
61
+
62
+ it("defaults async to false when not provided", () => {
63
+ const f = new ActionFrame("do-thing");
64
+ const back = ActionFrame.fromDict(f.toDict());
65
+ expect(back.async_).toBeFalsy();
66
+ });
67
+ });
68
+
69
+ // ── asyncActionResponseFromDict ───────────────────────────────────────────────
70
+
71
+ describe("asyncActionResponseFromDict", () => {
72
+ it("parses full response", () => {
73
+ const r = asyncActionResponseFromDict({ task_id: "t1", status: "accepted", poll_url: "/poll/t1" });
74
+ expect(r.taskId).toBe("t1");
75
+ expect(r.status).toBe("accepted");
76
+ expect(r.pollUrl).toBe("/poll/t1");
77
+ });
78
+
79
+ it("omits pollUrl when null", () => {
80
+ const r = asyncActionResponseFromDict({ task_id: "t2", status: "accepted", poll_url: null });
81
+ expect(r.pollUrl).toBeUndefined();
82
+ });
83
+ });
84
+
85
+ // ── NwpClient ─────────────────────────────────────────────────────────────────
86
+
87
+ describe("NwpClient", () => {
88
+ const registry = createDefaultRegistry();
89
+ const codec = new NpsFrameCodec(registry);
90
+ const aid = "sha256:" + "b".repeat(64);
91
+ const schema = { fields: [{ name: "id", type: "uint64" }] };
92
+
93
+ beforeEach(() => { vi.restoreAllMocks(); });
94
+
95
+ it("sendAnchor — POSTs and resolves on 200", async () => {
96
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true }));
97
+ const client = new NwpClient("http://node.test");
98
+ await expect(client.sendAnchor(new AnchorFrame(aid, schema))).resolves.toBeUndefined();
99
+ expect(vi.mocked(fetch)).toHaveBeenCalledWith(
100
+ "http://node.test/anchor",
101
+ expect.objectContaining({ method: "POST" }),
102
+ );
103
+ });
104
+
105
+ it("sendAnchor — throws on non-2xx", async () => {
106
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: false, status: 503 }));
107
+ const client = new NwpClient("http://node.test");
108
+ await expect(client.sendAnchor(new AnchorFrame(aid, schema))).rejects.toThrow("503");
109
+ });
110
+
111
+ it("query — decodes CapsFrame response", async () => {
112
+ const capsFrame = new CapsFrame(aid, 1, [{ id: 42 }]);
113
+ const wire = codec.encode(capsFrame);
114
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
115
+ ok: true,
116
+ arrayBuffer: () => Promise.resolve(wire.buffer),
117
+ }));
118
+ const client = new NwpClient("http://node.test");
119
+ const result = await client.query(new QueryFrame(aid));
120
+ expect(result).toBeInstanceOf(CapsFrame);
121
+ expect(result.count).toBe(1);
122
+ });
123
+
124
+ it("query — throws when response is not CapsFrame", async () => {
125
+ const anchorFrame = new AnchorFrame(aid, schema);
126
+ const wire = codec.encode(anchorFrame);
127
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
128
+ ok: true,
129
+ arrayBuffer: () => Promise.resolve(wire.buffer),
130
+ }));
131
+ const client = new NwpClient("http://node.test");
132
+ await expect(client.query(new QueryFrame(aid))).rejects.toThrow(TypeError);
133
+ });
134
+
135
+ it("query — throws on non-2xx", async () => {
136
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: false, status: 404 }));
137
+ const client = new NwpClient("http://node.test");
138
+ await expect(client.query(new QueryFrame(aid))).rejects.toThrow("404");
139
+ });
140
+
141
+ it("invoke — sync NPS frame response", async () => {
142
+ const capsFrame = new CapsFrame(aid, 0, []);
143
+ const wire = codec.encode(capsFrame);
144
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
145
+ ok: true,
146
+ headers: { get: () => "application/x-nps-frame" },
147
+ arrayBuffer: () => Promise.resolve(wire.buffer),
148
+ }));
149
+ const client = new NwpClient("http://node.test");
150
+ const result = await client.invoke(new ActionFrame("list"));
151
+ expect(result).toBeInstanceOf(CapsFrame);
152
+ });
153
+
154
+ it("invoke — async response returns AsyncActionResponse", async () => {
155
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
156
+ ok: true,
157
+ json: () => Promise.resolve({ task_id: "t99", status: "queued" }),
158
+ }));
159
+ const client = new NwpClient("http://node.test");
160
+ const result = await client.invoke(new ActionFrame("heavy", {}, true)) as { taskId: string };
161
+ expect(result.taskId).toBe("t99");
162
+ });
163
+
164
+ it("invoke — sync JSON response", async () => {
165
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
166
+ ok: true,
167
+ headers: { get: () => "application/json" },
168
+ json: () => Promise.resolve({ result: "ok" }),
169
+ }));
170
+ const client = new NwpClient("http://node.test");
171
+ const result = await client.invoke(new ActionFrame("ping")) as { result: string };
172
+ expect(result.result).toBe("ok");
173
+ });
174
+
175
+ it("invoke — throws on non-2xx", async () => {
176
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: false, status: 500 }));
177
+ const client = new NwpClient("http://node.test");
178
+ await expect(client.invoke(new ActionFrame("ping"))).rejects.toThrow("500");
179
+ });
180
+
181
+ it("stream — yields StreamFrames and stops at isLast", async () => {
182
+ const s0 = new StreamFrame("s1", 0, false, [{ id: 1 }]);
183
+ const s1 = new StreamFrame("s1", 1, true, [{ id: 2 }]);
184
+ const w0 = codec.encode(s0);
185
+ const w1 = codec.encode(s1);
186
+
187
+ async function* mockBody() { yield w0; yield w1; }
188
+
189
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true, body: mockBody() }));
190
+ const client = new NwpClient("http://node.test");
191
+ const frames: StreamFrame[] = [];
192
+ for await (const f of client.stream(new QueryFrame(aid))) {
193
+ frames.push(f);
194
+ }
195
+ expect(frames).toHaveLength(2);
196
+ expect(frames[0]!.seq).toBe(0);
197
+ expect(frames[1]!.isLast).toBe(true);
198
+ });
199
+
200
+ it("stream — throws when chunk is not a StreamFrame", async () => {
201
+ const anchor = new AnchorFrame(aid, schema);
202
+ const wire = codec.encode(anchor);
203
+ async function* mockBody() { yield wire; }
204
+
205
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true, body: mockBody() }));
206
+ const client = new NwpClient("http://node.test");
207
+ await expect(async () => {
208
+ for await (const _ of client.stream(new QueryFrame(aid))) { /* consume */ }
209
+ }).rejects.toThrow(TypeError);
210
+ });
211
+
212
+ it("stream — returns immediately when body is null", async () => {
213
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true, body: null }));
214
+ const client = new NwpClient("http://node.test");
215
+ const frames: StreamFrame[] = [];
216
+ for await (const f of client.stream(new QueryFrame(aid))) frames.push(f);
217
+ expect(frames).toHaveLength(0);
218
+ });
219
+
220
+ it("stream — throws on non-2xx", async () => {
221
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: false, status: 400 }));
222
+ const client = new NwpClient("http://node.test");
223
+ await expect(async () => {
224
+ for await (const _ of client.stream(new QueryFrame(aid))) { /* consume */ }
225
+ }).rejects.toThrow("400");
226
+ });
227
+
228
+ it("trailing slash stripped from baseUrl", async () => {
229
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true }));
230
+ const client = new NwpClient("http://node.test/");
231
+ await client.sendAnchor(new AnchorFrame(aid, schema));
232
+ expect(vi.mocked(fetch)).toHaveBeenCalledWith(
233
+ "http://node.test/anchor",
234
+ expect.anything(),
235
+ );
236
+ });
237
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "strict": true,
4
+ "target": "ES2022",
5
+ "module": "NodeNext",
6
+ "moduleResolution": "NodeNext",
7
+ "declaration": true,
8
+ "declarationMap": true,
9
+ "sourceMap": true,
10
+ "outDir": "dist",
11
+ "rootDir": "src",
12
+ "esModuleInterop": true,
13
+ "skipLibCheck": true,
14
+ "forceConsistentCasingInFileNames": true,
15
+ "resolveJsonModule": true,
16
+ "isolatedModules": true
17
+ },
18
+ "include": ["src/**/*.ts"],
19
+ "exclude": ["node_modules", "dist", "tests"]
20
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,20 @@
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig({
4
+ entry: {
5
+ index: "src/index.ts",
6
+ "core/index": "src/core/index.ts",
7
+ "ncp/index": "src/ncp/index.ts",
8
+ "nwp/index": "src/nwp/index.ts",
9
+ "nip/index": "src/nip/index.ts",
10
+ "ndp/index": "src/ndp/index.ts",
11
+ "nop/index": "src/nop/index.ts",
12
+ },
13
+ format: ["esm", "cjs"],
14
+ dts: true,
15
+ sourcemap: true,
16
+ clean: true,
17
+ splitting: false,
18
+ treeshake: true,
19
+ target: "es2022",
20
+ });
@@ -0,0 +1,10 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) 2026 LabAcacia / INNO LOTUS PTY LTD
3
+
4
+ import { defineConfig } from "vitest/config";
5
+
6
+ export default defineConfig({
7
+ test: {
8
+ include: ["tests/**/*.test.ts"],
9
+ },
10
+ });