nervur 0.21.4 → 0.22.0

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 (339) hide show
  1. package/README.md +215 -158
  2. package/dist/browser.js +4 -3
  3. package/dist/cli.js +16 -0
  4. package/dist/{being → core/being}/being.js +10 -0
  5. package/dist/{being → core/being}/faculty.js +5 -2
  6. package/dist/{being → core/being}/index.d.ts +1 -1
  7. package/dist/{being → core/being}/index.js +1 -1
  8. package/dist/{being → core/being}/kind.d.ts +2 -0
  9. package/dist/{being → core/being}/kind.js +6 -0
  10. package/dist/{being → core/being}/types.js +1 -1
  11. package/dist/{browser → core/browser}/index.d.ts +6 -5
  12. package/dist/{browser → core/browser}/index.js +10 -10
  13. package/dist/{browser → core/browser}/worker.d.ts +2 -3
  14. package/dist/{browser → core/browser}/worker.js +8 -11
  15. package/dist/{cli → core/cli}/command.d.ts +4 -4
  16. package/dist/{cli → core/cli}/command.js +116 -66
  17. package/dist/{cli → core/cli}/edge.js +4 -2
  18. package/dist/core/cli/harbor.d.ts +25 -0
  19. package/dist/{cli → core/cli}/harbor.js +45 -33
  20. package/dist/{contract → core/contract}/index.d.ts +6 -5
  21. package/dist/{contract → core/contract}/index.js +8 -9
  22. package/dist/core/contract/link.d.ts +4 -0
  23. package/dist/core/contract/link.js +45 -0
  24. package/dist/{crypto → core/crypto}/ed25519.js +22 -14
  25. package/dist/core/crypto/subtle.d.ts +3 -0
  26. package/dist/core/crypto/subtle.js +11 -0
  27. package/dist/core/crypto/x25519.js +24 -0
  28. package/dist/core/edge/index.d.ts +164 -0
  29. package/dist/core/edge/index.js +739 -0
  30. package/dist/core/edge/kit.d.ts +2 -0
  31. package/dist/core/edge/kit.js +2 -0
  32. package/dist/core/git/http.d.ts +6 -0
  33. package/dist/core/git/http.js +102 -0
  34. package/dist/core/git/index.d.ts +5 -0
  35. package/dist/core/git/index.js +10 -0
  36. package/dist/core/git/object.d.ts +40 -0
  37. package/dist/core/git/object.js +103 -0
  38. package/dist/core/git/pack.d.ts +9 -0
  39. package/dist/core/git/pack.js +171 -0
  40. package/dist/core/git/store.d.ts +23 -0
  41. package/dist/core/git/store.js +120 -0
  42. package/dist/core/git/zlib.d.ts +6 -0
  43. package/dist/core/git/zlib.js +210 -0
  44. package/dist/core/harbor/carrying.d.ts +51 -0
  45. package/dist/core/harbor/carrying.js +69 -0
  46. package/dist/core/harbor/catalogue.d.ts +33 -0
  47. package/dist/core/harbor/catalogue.js +365 -0
  48. package/dist/core/harbor/dna.d.ts +13 -0
  49. package/dist/core/harbor/dna.js +120 -0
  50. package/dist/{harbor → core/harbor}/dock.d.ts +22 -13
  51. package/dist/core/harbor/dock.js +381 -0
  52. package/dist/core/harbor/harbor.d.ts +16 -0
  53. package/dist/core/harbor/harbor.js +438 -0
  54. package/dist/core/harbor/index.d.ts +11 -0
  55. package/dist/{harbor → core/harbor}/index.js +5 -5
  56. package/dist/core/harbor/memory.d.ts +10 -0
  57. package/dist/core/harbor/memory.js +25 -0
  58. package/dist/{harbor → core/harbor}/package.d.ts +0 -1
  59. package/dist/{harbor → core/harbor}/package.js +9 -9
  60. package/dist/core/harbor/probe.d.ts +22 -0
  61. package/dist/core/harbor/probe.js +15 -0
  62. package/dist/core/harbor/registry.d.ts +15 -0
  63. package/dist/{harbor → core/harbor}/registry.js +26 -10
  64. package/dist/core/harbor/root-line.d.ts +7 -0
  65. package/dist/core/harbor/root-line.js +9 -0
  66. package/dist/{harbor → core/harbor}/terrain.d.ts +9 -6
  67. package/dist/{harbor → core/harbor}/terrain.js +5 -4
  68. package/dist/core/http/index.d.ts +2 -0
  69. package/dist/{http → core/http}/index.js +3 -5
  70. package/dist/{line → core/line}/answer.d.ts +2 -2
  71. package/dist/{line → core/line}/frame.d.ts +0 -5
  72. package/dist/{line → core/line}/frame.js +0 -4
  73. package/dist/core/line/ground.js +7 -0
  74. package/dist/core/line/index.d.ts +4 -0
  75. package/dist/core/line/index.js +8 -0
  76. package/dist/core/line/web.d.ts +12 -0
  77. package/dist/{line → core/line}/web.js +13 -77
  78. package/dist/{node → core/node}/index.d.ts +2 -15
  79. package/dist/core/node/index.js +124 -0
  80. package/dist/{pointer → core/pointer}/bodies.d.ts +7 -5
  81. package/dist/core/pointer/bodies.js +118 -0
  82. package/dist/core/pointer/index.d.ts +2 -0
  83. package/dist/{pointer → core/pointer}/index.js +1 -1
  84. package/dist/{pointer → core/pointer}/world.d.ts +13 -9
  85. package/dist/{pointer → core/pointer}/world.js +31 -16
  86. package/dist/{proof → core/proof}/contracts.d.ts +6 -2
  87. package/dist/{proof → core/proof}/contracts.js +11 -10
  88. package/dist/core/proof/index.d.ts +3 -0
  89. package/dist/{proof → core/proof}/index.js +2 -2
  90. package/dist/core/proof/law.d.ts +25 -0
  91. package/dist/core/proof/law.js +715 -0
  92. package/dist/{tcp → core/tcp}/index.d.ts +7 -4
  93. package/dist/{tcp → core/tcp}/index.js +11 -62
  94. package/dist/{ward/ward.d.ts → core/ward/house.d.ts} +24 -10
  95. package/dist/core/ward/house.js +473 -0
  96. package/dist/core/ward/index.d.ts +5 -0
  97. package/dist/core/ward/index.js +7 -0
  98. package/dist/{ward → core/ward}/partition.d.ts +1 -0
  99. package/dist/{ward → core/ward}/stance.d.ts +2 -2
  100. package/dist/{ward → core/ward}/stance.js +5 -2
  101. package/dist/{ward/ward-being.d.ts → core/ward/ward.d.ts} +14 -6
  102. package/dist/core/ward/ward.js +140 -0
  103. package/dist/core.d.ts +10 -0
  104. package/dist/core.js +19 -0
  105. package/dist/defaults/dock/index.d.ts +4 -0
  106. package/dist/defaults/dock/index.js +11 -0
  107. package/dist/defaults/index.d.ts +5 -0
  108. package/dist/defaults/index.js +10 -0
  109. package/dist/defaults/memory/index.d.ts +12 -0
  110. package/dist/defaults/memory/index.js +39 -0
  111. package/dist/defaults/tcp/index.d.ts +16 -0
  112. package/dist/defaults/tcp/index.js +82 -0
  113. package/dist/defaults/ward/index.d.ts +4 -0
  114. package/dist/defaults/ward/index.js +11 -0
  115. package/dist/defaults/web/index.d.ts +15 -0
  116. package/dist/defaults/web/index.js +90 -0
  117. package/dist/edge.js +4 -3
  118. package/dist/index.d.ts +1 -8
  119. package/dist/index.js +13 -18
  120. package/dist/kit.d.ts +4 -0
  121. package/dist/kit.js +12 -0
  122. package/dist/node.js +4 -3
  123. package/package.json +23 -18
  124. package/src/browser.ts +4 -3
  125. package/src/cli.ts +15 -0
  126. package/src/{being → core/being}/being.ts +4 -0
  127. package/src/{being → core/being}/faculty.ts +5 -2
  128. package/src/{being → core/being}/index.ts +1 -1
  129. package/src/{being → core/being}/kind.ts +7 -0
  130. package/src/{being → core/being}/types.ts +1 -1
  131. package/src/{browser → core/browser}/index.ts +16 -13
  132. package/src/{browser → core/browser}/worker.ts +11 -14
  133. package/src/{cli → core/cli}/command.ts +113 -66
  134. package/src/{cli → core/cli}/edge.ts +4 -2
  135. package/src/{cli → core/cli}/harbor.ts +48 -31
  136. package/src/{contract → core/contract}/index.ts +14 -14
  137. package/src/core/contract/link.ts +58 -0
  138. package/src/{crypto → core/crypto}/ed25519.ts +22 -14
  139. package/src/core/crypto/subtle.ts +13 -0
  140. package/src/core/crypto/x25519.ts +23 -0
  141. package/src/core/edge/index.ts +865 -0
  142. package/src/core/git/http.ts +103 -0
  143. package/src/core/git/index.ts +10 -0
  144. package/src/core/git/object.ts +116 -0
  145. package/src/core/git/pack.ts +147 -0
  146. package/src/core/git/store.ts +122 -0
  147. package/src/core/git/zlib.ts +197 -0
  148. package/src/core/harbor/carrying.ts +124 -0
  149. package/src/core/harbor/catalogue.ts +386 -0
  150. package/src/core/harbor/dna.ts +116 -0
  151. package/src/core/harbor/dock.ts +420 -0
  152. package/src/core/harbor/harbor.ts +433 -0
  153. package/src/core/harbor/index.ts +14 -0
  154. package/src/core/harbor/memory.ts +31 -0
  155. package/src/{harbor → core/harbor}/package.ts +15 -15
  156. package/src/core/harbor/probe.ts +39 -0
  157. package/src/{harbor → core/harbor}/registry.ts +28 -11
  158. package/src/core/harbor/root-line.ts +19 -0
  159. package/src/{harbor → core/harbor}/terrain.ts +14 -7
  160. package/src/{http → core/http}/index.ts +5 -8
  161. package/src/{line → core/line}/answer.ts +3 -2
  162. package/src/{line → core/line}/frame.ts +0 -5
  163. package/src/core/line/ground.ts +18 -0
  164. package/src/core/line/index.ts +8 -0
  165. package/src/{line → core/line}/web.ts +1 -105
  166. package/src/core/node/index.ts +139 -0
  167. package/src/core/pointer/bodies.ts +122 -0
  168. package/src/core/pointer/index.ts +4 -0
  169. package/src/{pointer → core/pointer}/world.ts +45 -21
  170. package/src/{proof → core/proof}/contracts.ts +18 -13
  171. package/src/{proof → core/proof}/index.ts +3 -3
  172. package/src/core/proof/law.ts +742 -0
  173. package/src/{stand → core/stand}/stand.ts +2 -2
  174. package/src/{tcp → core/tcp}/index.ts +17 -74
  175. package/src/core/ward/house.ts +524 -0
  176. package/src/core/ward/index.ts +7 -0
  177. package/src/{ward → core/ward}/partition.ts +5 -1
  178. package/src/{ward → core/ward}/stance.ts +5 -2
  179. package/src/core/ward/ward.ts +166 -0
  180. package/src/core.ts +55 -0
  181. package/src/defaults/dock/index.ts +12 -0
  182. package/src/defaults/index.ts +10 -0
  183. package/src/defaults/memory/index.ts +50 -0
  184. package/src/defaults/tcp/index.ts +89 -0
  185. package/src/defaults/ward/index.ts +12 -0
  186. package/src/defaults/web/index.ts +97 -0
  187. package/src/edge.ts +4 -3
  188. package/src/index.ts +13 -50
  189. package/src/kit.ts +15 -0
  190. package/src/node.ts +4 -3
  191. package/dist/cli/harbor.d.ts +0 -16
  192. package/dist/cli/main.js +0 -14
  193. package/dist/crypto/subtle.d.ts +0 -10
  194. package/dist/crypto/subtle.js +0 -29
  195. package/dist/crypto/x25519.js +0 -21
  196. package/dist/edge/index.d.ts +0 -81
  197. package/dist/edge/index.js +0 -400
  198. package/dist/harbor/carrying.d.ts +0 -17
  199. package/dist/harbor/carrying.js +0 -38
  200. package/dist/harbor/catalogue.d.ts +0 -28
  201. package/dist/harbor/catalogue.js +0 -109
  202. package/dist/harbor/dock.js +0 -236
  203. package/dist/harbor/harbor.d.ts +0 -45
  204. package/dist/harbor/harbor.js +0 -347
  205. package/dist/harbor/index.d.ts +0 -11
  206. package/dist/harbor/probe.d.ts +0 -18
  207. package/dist/harbor/probe.js +0 -15
  208. package/dist/harbor/registry.d.ts +0 -10
  209. package/dist/harbor/relay.d.ts +0 -15
  210. package/dist/harbor/relay.js +0 -406
  211. package/dist/harbor/root-line.d.ts +0 -9
  212. package/dist/harbor/root-line.js +0 -38
  213. package/dist/harbor/store.d.ts +0 -13
  214. package/dist/harbor/store.js +0 -53
  215. package/dist/http/index.d.ts +0 -4
  216. package/dist/line/ground.js +0 -9
  217. package/dist/line/index.d.ts +0 -5
  218. package/dist/line/index.js +0 -9
  219. package/dist/line/push.d.ts +0 -7
  220. package/dist/line/push.js +0 -92
  221. package/dist/line/web.d.ts +0 -30
  222. package/dist/node/index.js +0 -236
  223. package/dist/pointer/bodies.js +0 -80
  224. package/dist/pointer/index.d.ts +0 -2
  225. package/dist/proof/index.d.ts +0 -3
  226. package/dist/proof/law.d.ts +0 -90
  227. package/dist/proof/law.js +0 -591
  228. package/dist/tcp/frame.d.ts +0 -25
  229. package/dist/tcp/frame.js +0 -64
  230. package/dist/ward/index.d.ts +0 -6
  231. package/dist/ward/index.js +0 -8
  232. package/dist/ward/pilot.d.ts +0 -8
  233. package/dist/ward/pilot.js +0 -50
  234. package/dist/ward/ward-being.js +0 -82
  235. package/dist/ward/ward.js +0 -290
  236. package/src/cli/main.ts +0 -13
  237. package/src/crypto/subtle.ts +0 -33
  238. package/src/crypto/x25519.ts +0 -21
  239. package/src/edge/index.ts +0 -490
  240. package/src/harbor/carrying.ts +0 -56
  241. package/src/harbor/catalogue.ts +0 -134
  242. package/src/harbor/dock.ts +0 -265
  243. package/src/harbor/harbor.ts +0 -381
  244. package/src/harbor/index.ts +0 -14
  245. package/src/harbor/probe.ts +0 -34
  246. package/src/harbor/relay.ts +0 -418
  247. package/src/harbor/root-line.ts +0 -39
  248. package/src/harbor/store.ts +0 -69
  249. package/src/line/ground.ts +0 -19
  250. package/src/line/index.ts +0 -9
  251. package/src/line/push.ts +0 -112
  252. package/src/node/index.ts +0 -241
  253. package/src/pointer/bodies.ts +0 -82
  254. package/src/pointer/index.ts +0 -4
  255. package/src/proof/law.ts +0 -591
  256. package/src/ward/index.ts +0 -8
  257. package/src/ward/pilot.ts +0 -50
  258. package/src/ward/ward-being.ts +0 -103
  259. package/src/ward/ward.ts +0 -328
  260. /package/dist/{cli/main.d.ts → cli.d.ts} +0 -0
  261. /package/dist/{being → core/being}/being.d.ts +0 -0
  262. /package/dist/{being → core/being}/digest.d.ts +0 -0
  263. /package/dist/{being → core/being}/digest.js +0 -0
  264. /package/dist/{being → core/being}/faculty.d.ts +0 -0
  265. /package/dist/{being → core/being}/types.d.ts +0 -0
  266. /package/dist/{being → core/being}/words.d.ts +0 -0
  267. /package/dist/{being → core/being}/words.js +0 -0
  268. /package/dist/{cli → core/cli}/edge.d.ts +0 -0
  269. /package/dist/{crypto → core/crypto}/aes.d.ts +0 -0
  270. /package/dist/{crypto → core/crypto}/aes.js +0 -0
  271. /package/dist/{crypto → core/crypto}/bytes.d.ts +0 -0
  272. /package/dist/{crypto → core/crypto}/bytes.js +0 -0
  273. /package/dist/{crypto → core/crypto}/ed25519.d.ts +0 -0
  274. /package/dist/{crypto → core/crypto}/hash.d.ts +0 -0
  275. /package/dist/{crypto → core/crypto}/hash.js +0 -0
  276. /package/dist/{crypto → core/crypto}/index.d.ts +0 -0
  277. /package/dist/{crypto → core/crypto}/index.js +0 -0
  278. /package/dist/{crypto → core/crypto}/json.d.ts +0 -0
  279. /package/dist/{crypto → core/crypto}/json.js +0 -0
  280. /package/dist/{crypto → core/crypto}/mlkem.d.ts +0 -0
  281. /package/dist/{crypto → core/crypto}/mlkem.js +0 -0
  282. /package/dist/{crypto → core/crypto}/x25519.d.ts +0 -0
  283. /package/dist/{folder → core/folder}/index.d.ts +0 -0
  284. /package/dist/{folder → core/folder}/index.js +0 -0
  285. /package/dist/{http → core/http}/websocket.d.ts +0 -0
  286. /package/dist/{http → core/http}/websocket.js +0 -0
  287. /package/dist/{line → core/line}/answer.js +0 -0
  288. /package/dist/{line → core/line}/ground.d.ts +0 -0
  289. /package/dist/{proof → core/proof}/expect.d.ts +0 -0
  290. /package/dist/{proof → core/proof}/expect.js +0 -0
  291. /package/dist/{quo → core/quo}/address.d.ts +0 -0
  292. /package/dist/{quo → core/quo}/address.js +0 -0
  293. /package/dist/{quo → core/quo}/door.d.ts +0 -0
  294. /package/dist/{quo → core/quo}/door.js +0 -0
  295. /package/dist/{quo → core/quo}/index.d.ts +0 -0
  296. /package/dist/{quo → core/quo}/index.js +0 -0
  297. /package/dist/{quo → core/quo}/invitation.d.ts +0 -0
  298. /package/dist/{quo → core/quo}/invitation.js +0 -0
  299. /package/dist/{quo → core/quo}/keys.d.ts +0 -0
  300. /package/dist/{quo → core/quo}/keys.js +0 -0
  301. /package/dist/{quo → core/quo}/payload.d.ts +0 -0
  302. /package/dist/{quo → core/quo}/payload.js +0 -0
  303. /package/dist/{quo → core/quo}/relations.d.ts +0 -0
  304. /package/dist/{quo → core/quo}/relations.js +0 -0
  305. /package/dist/{quo → core/quo}/reply.d.ts +0 -0
  306. /package/dist/{quo → core/quo}/reply.js +0 -0
  307. /package/dist/{quo → core/quo}/seal.d.ts +0 -0
  308. /package/dist/{quo → core/quo}/seal.js +0 -0
  309. /package/dist/{quo → core/quo}/standing.d.ts +0 -0
  310. /package/dist/{quo → core/quo}/standing.js +0 -0
  311. /package/dist/{ward → core/ward}/allowance.d.ts +0 -0
  312. /package/dist/{ward → core/ward}/allowance.js +0 -0
  313. /package/dist/{ward → core/ward}/cells.d.ts +0 -0
  314. /package/dist/{ward → core/ward}/cells.js +0 -0
  315. /package/dist/{ward → core/ward}/partition.js +0 -0
  316. /package/src/{being → core/being}/digest.ts +0 -0
  317. /package/src/{being → core/being}/words.ts +0 -0
  318. /package/src/{crypto → core/crypto}/aes.ts +0 -0
  319. /package/src/{crypto → core/crypto}/bytes.ts +0 -0
  320. /package/src/{crypto → core/crypto}/hash.ts +0 -0
  321. /package/src/{crypto → core/crypto}/index.ts +0 -0
  322. /package/src/{crypto → core/crypto}/json.ts +0 -0
  323. /package/src/{crypto → core/crypto}/mlkem.ts +0 -0
  324. /package/src/{folder → core/folder}/index.ts +0 -0
  325. /package/src/{http → core/http}/websocket.ts +0 -0
  326. /package/src/{proof → core/proof}/expect.ts +0 -0
  327. /package/src/{quo → core/quo}/address.ts +0 -0
  328. /package/src/{quo → core/quo}/door.ts +0 -0
  329. /package/src/{quo → core/quo}/index.ts +0 -0
  330. /package/src/{quo → core/quo}/invitation.ts +0 -0
  331. /package/src/{quo → core/quo}/keys.ts +0 -0
  332. /package/src/{quo → core/quo}/payload.ts +0 -0
  333. /package/src/{quo → core/quo}/relations.ts +0 -0
  334. /package/src/{quo → core/quo}/reply.ts +0 -0
  335. /package/src/{quo → core/quo}/seal.ts +0 -0
  336. /package/src/{quo → core/quo}/standing.ts +0 -0
  337. /package/src/{stand → core/stand}/main.ts +0 -0
  338. /package/src/{ward → core/ward}/allowance.ts +0 -0
  339. /package/src/{ward → core/ward}/cells.ts +0 -0
package/src/line/push.ts DELETED
@@ -1,112 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // A ring with no content, as Web Push sends it: RFC 8030 with no payload,
3
- // so nothing is encrypted and nothing is said, and RFC 8292's VAPID, so
4
- // the push service knows who rings. It runs on fetch and Web Crypto, in
5
- // every engine. The device a ring wakes asks its relay client to `ring`.
6
- //
7
- // subject { subject } the contact a push service reaches, a mailto:
8
- // or https: URI, the owner's to set
9
- // key the key a device subscribes with, as
10
- // PushManager takes it
11
- // ring { endpoint } a ring to that endpoint: `rung`, `gone` where
12
- // the push service has let it go, or an error
13
- //
14
- // The signing key is drawn at the first `key` or `ring` and kept in the
15
- // faculty's cells, sealed with the rest of the harbor.
16
- import type { AskSpec, JsonObject } from '../being/index.ts';
17
- import { concat, utf8 } from '../crypto/index.ts';
18
- import { Push, type GroundFaculty } from '../harbor/index.ts';
19
- import { ownerAsk } from '../ward/index.ts';
20
-
21
- export const WEB_PUSH = 'org.nervur.web-push';
22
- // How long a push service holds a ring for a device, in seconds.
23
- export const PUSH_TTL = 60;
24
- // How long a VAPID token stands, in seconds, inside RFC 8292's day.
25
- export const VAPID_LIFE = 43_200;
26
-
27
- type Vapid = { d: string; x: string; y: string };
28
- const EC = { name: 'ECDSA', namedCurve: 'P-256' } as const;
29
-
30
- export const base64url = (bytes: Uint8Array): string => {
31
- let s = '';
32
- for (const b of bytes) s += String.fromCharCode(b);
33
- return btoa(s).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
34
- };
35
- const unbase64url = (text: string): Uint8Array => Uint8Array.from(atob(text.replace(/-/g, '+').replace(/_/g, '/')), (c) => c.charCodeAt(0));
36
- const local = (host: string): boolean => host === '127.0.0.1' || host === 'localhost' || host === '[::1]';
37
-
38
- // A push endpoint: an https URL, or plain http to this machine alone.
39
- export const pushEndpoint = (value: unknown): URL | null => {
40
- if (typeof value !== 'string') return null;
41
- try {
42
- const url = new URL(value);
43
- return url.protocol === 'https:' || (url.protocol === 'http:' && local(url.hostname)) ? url : null;
44
- } catch {
45
- return null;
46
- }
47
- };
48
- const isSubject = (value: unknown): value is string => typeof value === 'string' && /^(mailto:.+|https:\/\/.+)$/.test(value);
49
-
50
- export const webPush: GroundFaculty = () =>
51
- class WebPush extends Push {
52
- static override readonly kind: string = WEB_PUSH;
53
- static override asks: Record<string, AskSpec> = {
54
- subject: ownerAsk('the contact a push service reaches, a mailto: or https: URI', { required: ['subject'] }),
55
- key: { description: 'the key a device subscribes to pushes with', input: { type: 'object' } },
56
- ring: { description: 'a ring with no content to a push endpoint', input: { type: 'object', required: ['endpoint'] } },
57
- };
58
-
59
- #drawing: Promise<Vapid> | undefined;
60
-
61
- subject(args: JsonObject): JsonObject {
62
- if (!isSubject(args.subject)) return { error: 'subject is a mailto: or https: URI' };
63
- this.cells.subject = args.subject;
64
- return { subject: args.subject };
65
- }
66
-
67
- async key(): Promise<JsonObject> {
68
- return { key: WebPush.#public(await this.#vapid()) };
69
- }
70
-
71
- async ring(args: JsonObject): Promise<JsonObject> {
72
- const endpoint = pushEndpoint(args.endpoint);
73
- if (endpoint === null) return { error: 'endpoint is an https URL' };
74
- const sub = this.cells.subject;
75
- if (!isSubject(sub)) return { error: 'no subject is set' };
76
- const vapid = await this.#vapid();
77
- const signing = await crypto.subtle.importKey('jwk', { kty: 'EC', crv: 'P-256', ...vapid }, EC, false, ['sign']);
78
- const part = (value: JsonObject) => base64url(utf8(JSON.stringify(value)));
79
- const unsigned = `${part({ typ: 'JWT', alg: 'ES256' })}.${part({ aud: endpoint.origin, exp: Math.floor(Date.now() / 1000) + VAPID_LIFE, sub })}`;
80
- const signature = new Uint8Array(await crypto.subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, signing, new Uint8Array(utf8(unsigned))));
81
- let status: number;
82
- try {
83
- const response = await fetch(endpoint, {
84
- method: 'POST',
85
- headers: { TTL: String(PUSH_TTL), Urgency: 'high', 'Content-Length': '0', Authorization: `vapid t=${unsigned}.${base64url(signature)}, k=${WebPush.#public(vapid)}` },
86
- });
87
- status = response.status;
88
- await response.body?.cancel();
89
- } catch {
90
- return { error: 'the push service was not reached' };
91
- }
92
- if (status === 404 || status === 410) return { gone: true };
93
- return status >= 200 && status < 300 ? { rung: true } : { error: `the push service answered ${String(status)}` };
94
- }
95
-
96
- static #public({ x, y }: Vapid): string {
97
- return base64url(concat(Uint8Array.of(4), unbase64url(x), unbase64url(y)));
98
- }
99
-
100
- #vapid(): Promise<Vapid> {
101
- const kept = this.cells.vapid as Vapid | undefined;
102
- if (kept) return Promise.resolve(kept);
103
- this.#drawing ??= (async () => {
104
- const pair = await crypto.subtle.generateKey(EC, true, ['sign']);
105
- const { d, x, y } = await crypto.subtle.exportKey('jwk', pair.privateKey);
106
- const vapid = { d: d!, x: x!, y: y! };
107
- this.cells.vapid = vapid;
108
- return vapid;
109
- })();
110
- return this.#drawing;
111
- }
112
- };
package/src/node/index.ts DELETED
@@ -1,241 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // `nervur/node`: the Node ground. A harbor keeps its seed and sealed
3
- // memory in a folder, finds its code among the modules it is handed and
4
- // then in files, speaks TCP from genesis and the web and Web Push where
5
- // the root opens them, and serves the root line on a local socket, so the
6
- // `nervur` command reaches it while it stands.
7
- //
8
- // <dir>/seed the harbor's seed
9
- // <dir>/<place>/ its sealed memory
10
- // <dir>/root.sock the root line while the harbor is served, open to
11
- // this user alone, or in the temporary folder for a
12
- // folder too deep for a socket path
13
- //
14
- // One run holds a folder: a terrain refuses a folder another run serves.
15
- import { createHash } from 'node:crypto';
16
- import { chmod, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
17
- import { createRequire } from 'node:module';
18
- import { connect, createServer, type Server } from 'node:net';
19
- import { homedir, tmpdir } from 'node:os';
20
- import { dirname, join, resolve } from 'node:path';
21
- import { pathToFileURL } from 'node:url';
22
- import type { JsonObject } from '../being/index.ts';
23
- import { Loader, type Loaded, type Module } from '../contract/index.ts';
24
- import { FolderCustody, FolderMemory } from '../folder/index.ts';
25
- import { rootLine, type GroundProbe, type Harbor, type RootRequest } from '../harbor/index.ts';
26
- import { webGround } from '../http/index.ts';
27
- import { webPush } from '../line/index.ts';
28
- import { CryptoEntropy, HeldLoader, PointerTerrain } from '../pointer/index.ts';
29
- import { TCP_OPENING, tcpFaculty } from '../tcp/index.ts';
30
-
31
- export const NODE = 'node';
32
-
33
- // Where a harbor of this user lives unless named: $NERVUR_DIR, then
34
- // ~/.nervur.
35
- export const harborDir = (env: Record<string, string | undefined> = process.env): string => env.NERVUR_DIR ?? join(homedir(), '.nervur');
36
-
37
- // A socket path has a hundred bytes or so on every system, so a folder too
38
- // deep for one puts its socket in this user's temporary folder, under a
39
- // name drawn from the folder's own path.
40
- const SOCKET_PATH = 100;
41
- export const socketOf = (dir: string): string => {
42
- const beside = join(resolve(dir), 'root.sock');
43
- if (Buffer.byteLength(beside) <= SOCKET_PATH) return beside;
44
- return join(tmpdir(), `nervur-${createHash('sha256').update(resolve(dir)).digest('hex').slice(0, 32)}.sock`);
45
- };
46
-
47
- // A specifier that names a file rather than a package.
48
- export const isFile = (from: string): boolean => /^(\.{1,2}[\\/]|[\\/]|[A-Za-z]:[\\/])/.test(from) || /\.(m?[jt]s|c[jt]s)$/.test(from);
49
-
50
- // Code from a file or an installed package: a module that exports
51
- // `module`, `version` and `classes`, found again by where it was found, a
52
- // file by its absolute path and a package by its name, resolved from each
53
- // folder of `bases` in order. What is not there, or exports no module, is
54
- // none. Code that is there and does not import throws Node's reason, so
55
- // whoever added it learns why; an ES module read as CommonJS says which
56
- // field its package.json lacks.
57
- export class ImportLoader extends Loader {
58
- readonly #bases: readonly string[];
59
- constructor(bases: readonly string[] = [process.cwd()]) {
60
- super();
61
- this.#bases = bases;
62
- }
63
-
64
- async load(from: string): Promise<Loaded | null> {
65
- const found = isFile(from) ? resolve(from) : this.#package(from);
66
- if (found === null) return null;
67
- let exported: { module?: unknown; version?: unknown; classes?: unknown };
68
- try {
69
- exported = (await import(pathToFileURL(found).href)) as typeof exported;
70
- } catch (e) {
71
- const said = (e as Error).message;
72
- // The name itself is nowhere, as against code there that needs
73
- // something that is not.
74
- if ((e as { code?: string }).code === 'ERR_MODULE_NOT_FOUND' && said.includes(`'${found}'`)) return null;
75
- const commonjs = e instanceof SyntaxError && /\b(export|import)\b/.test(said);
76
- throw new Error(`${found} did not load: ${said}${commonjs ? '; it is an ES module read as CommonJS, so set "type": "module" in its package.json' : ''}`, { cause: e });
77
- }
78
- const { module, version, classes } = exported;
79
- if (typeof module !== 'string' || typeof version !== 'string' || !Array.isArray(classes)) return null;
80
- return { module, version, classes, from: isFile(from) ? found : from };
81
- }
82
-
83
- #package(name: string): string | null {
84
- for (const base of this.#bases) {
85
- try {
86
- return createRequire(join(resolve(base), 'noop.js')).resolve(name);
87
- } catch {
88
- // Not installed from here; the next base may hold it.
89
- }
90
- }
91
- return null;
92
- }
93
- }
94
-
95
- // A module by its id, the name it declares, so the catalogue's cells name
96
- // code the same way on every ground. `add` may name a module by its id, a
97
- // file, or an installed package, and the ground works out which. The
98
- // modules a harbor is handed are found by their id first. Where else an id
99
- // is found, a file's path or a package's name, is this ground's own
100
- // knowledge, kept beside the harbor in `<dir>/modules.json` and never in
101
- // the sealed package, since another ground finds the same id in its own
102
- // bundle. Packages resolve from where the command runs, then from the
103
- // harbor's folder.
104
- export class NodeLoader extends ImportLoader {
105
- readonly #held: HeldLoader;
106
- readonly #map: string | undefined;
107
- constructor(modules: readonly Module[] = [], dir?: string) {
108
- super(dir === undefined ? [process.cwd()] : [process.cwd(), dir]);
109
- this.#held = new HeldLoader(modules);
110
- this.#map = dir === undefined ? undefined : join(dir, MODULES);
111
- }
112
-
113
- override async load(from: string): Promise<Loaded | null> {
114
- const held = await this.#held.load(from);
115
- if (held) return held;
116
- const known = await this.#known();
117
- const where = known[from];
118
- if (where !== undefined) {
119
- const loaded = await super.load(where);
120
- return loaded?.module === from ? { ...loaded, from } : null;
121
- }
122
- const loaded = await super.load(from);
123
- if (!loaded) return null;
124
- if (this.#map && known[loaded.module] !== loaded.from) await this.#keep({ ...known, [loaded.module]: loaded.from });
125
- return { ...loaded, from: loaded.module };
126
- }
127
-
128
- async #known(): Promise<Record<string, string>> {
129
- if (!this.#map) return {};
130
- try {
131
- return JSON.parse(await readFile(this.#map, 'utf8')) as Record<string, string>;
132
- } catch {
133
- return {};
134
- }
135
- }
136
-
137
- async #keep(files: Record<string, string>): Promise<void> {
138
- await mkdir(dirname(this.#map!), { recursive: true, mode: 0o700 });
139
- await writeFile(`${this.#map!}.new`, `${JSON.stringify(files, null, 2)}\n`, { mode: 0o600 });
140
- await rename(`${this.#map!}.new`, this.#map!);
141
- }
142
- }
143
-
144
- // Where a Node harbor's module ids find their files.
145
- export const MODULES = 'modules.json';
146
-
147
- // One root request over a served harbor's socket, or undefined where none
148
- // is served.
149
- export const askServed = (dir: string, request: RootRequest): Promise<JsonObject | undefined> =>
150
- new Promise((done, fail) => {
151
- const socket = connect(socketOf(dir));
152
- let text = '';
153
- socket.once('connect', () => socket.write(`${JSON.stringify(request)}\n`));
154
- socket.on('data', (chunk: Buffer) => {
155
- text += chunk.toString('utf8');
156
- const end = text.indexOf('\n');
157
- if (end < 0) return;
158
- socket.end();
159
- done(JSON.parse(text.slice(0, end)) as JsonObject);
160
- });
161
- socket.once('error', (e: NodeJS.ErrnoException) => (e.code === 'ENOENT' || e.code === 'ECONNREFUSED' ? done(undefined) : fail(e)));
162
- });
163
-
164
- export type NodeParts = {
165
- readonly dir: string;
166
- readonly modules?: readonly Module[];
167
- // Whether its carriers wake, and whether it serves the root line.
168
- readonly wakes?: boolean;
169
- readonly serves?: boolean;
170
- };
171
-
172
- export class NodeTerrain extends PointerTerrain {
173
- readonly dir: string;
174
- readonly serves: boolean;
175
- #server: Server | undefined;
176
-
177
- constructor(parts: NodeParts) {
178
- const entropy = new CryptoEntropy();
179
- super({
180
- entropy,
181
- loader: new NodeLoader(parts.modules, parts.dir),
182
- custody: new FolderCustody(parts.dir, entropy),
183
- memory: new FolderMemory(parts.dir),
184
- faculties: [tcpFaculty, webGround, webPush],
185
- opens: [TCP_OPENING],
186
- wakes: parts.wakes ?? true,
187
- });
188
- this.dir = parts.dir;
189
- this.serves = parts.serves ?? true;
190
- }
191
-
192
- override async claim(): Promise<void> {
193
- if ((await askServed(this.dir, {})) !== undefined) throw new Error(`a harbor is already served from ${this.dir}`);
194
- }
195
-
196
- // The root line on the socket. A socket file nobody answers on is a
197
- // stale one, and is replaced.
198
- override async stand(harbor: Harbor): Promise<void> {
199
- if (!this.serves) return;
200
- const root = socketOf(this.dir);
201
- await rm(root, { force: true });
202
- const server = createServer((socket) => {
203
- let text = '';
204
- socket.on('data', (chunk: Buffer) => {
205
- text += chunk.toString('utf8');
206
- for (let end = text.indexOf('\n'); end >= 0; end = text.indexOf('\n')) {
207
- const line = text.slice(0, end);
208
- text = text.slice(end + 1);
209
- let value: unknown;
210
- try {
211
- value = JSON.parse(line);
212
- } catch {
213
- value = undefined;
214
- }
215
- void rootLine(harbor, value)
216
- .catch(() => ({ error: 'the harbor threw' }))
217
- .then((answer) => socket.write(`${JSON.stringify(answer)}\n`));
218
- }
219
- });
220
- socket.on('error', () => socket.destroy());
221
- });
222
- await new Promise<void>((done) => server.listen(root, done));
223
- await chmod(root, 0o600);
224
- this.#server = server;
225
- }
226
-
227
- override async release(): Promise<void> {
228
- const server = this.#server;
229
- if (!server) return;
230
- this.#server = undefined;
231
- await new Promise((done) => server.close(done));
232
- await rm(socketOf(this.dir), { force: true });
233
- }
234
- }
235
-
236
- // Node, and the engines that answer as Node does.
237
- export const nodeGround: GroundProbe = {
238
- name: NODE,
239
- fits: () => typeof process !== 'undefined' && typeof process.versions?.node === 'string',
240
- terrain: ({ modules, where }) => new NodeTerrain({ dir: where ?? harborDir(), ...(modules ? { modules } : {}) }),
241
- };
@@ -1,82 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // The pointer terrain's bodies: each contract fulfilled in one process,
3
- // with nothing beyond JavaScript.
4
- import { Clock, Custody, Entropy, Loader, Memory, type Loaded, type Module } from '../contract/index.ts';
5
-
6
- export class CryptoEntropy extends Entropy {
7
- draw(length: number): Uint8Array {
8
- return globalThis.crypto.getRandomValues(new Uint8Array(length));
9
- }
10
- }
11
-
12
- export class SystemClock extends Clock {
13
- now(): number {
14
- return Date.now();
15
- }
16
- wait(ms: number): { done: Promise<void>; cancel(): void } {
17
- let timer: ReturnType<typeof setTimeout> | undefined;
18
- const done = new Promise<void>((resolve) => (timer = setTimeout(resolve, ms)));
19
- return { done, cancel: () => clearTimeout(timer) };
20
- }
21
- }
22
-
23
- // The harbor's seed, drawn once and held by this object.
24
- export class HeldCustody extends Custody {
25
- readonly #seed: Uint8Array;
26
- constructor(entropy: Entropy) {
27
- super();
28
- this.#seed = entropy.draw(32);
29
- }
30
- seed(): Promise<Uint8Array> {
31
- return Promise.resolve(this.#seed.slice());
32
- }
33
- }
34
-
35
- // Modules handed to this object, found by the name each declares: the
36
- // bundle a ground with no disk to load from carries, an edge's or a
37
- // browser's. A path, a package or any other name is no module of it.
38
- export class HeldLoader extends Loader {
39
- readonly #modules = new Map<string, Module>();
40
- constructor(modules: readonly Module[] = []) {
41
- super();
42
- for (const m of modules) this.#modules.set(m.module, m);
43
- }
44
- load(from: string): Promise<Loaded | null> {
45
- const m = this.#modules.get(from);
46
- return Promise.resolve(m ? { module: m.module, version: m.version, classes: m.classes, from } : null);
47
- }
48
- override missing(from: string): string {
49
- const carried = [...this.#modules.keys()];
50
- return `${from} is no module this bundle carries${carried.length === 0 ? ', and it carries none' : `: it carries ${carried.join(', ')}`}`;
51
- }
52
- }
53
-
54
- // Entries that live as long as this object, copied in and out, which is
55
- // what a restart in one process finds.
56
- export class VolatileMemory extends Memory {
57
- readonly #places = new Map<string, Map<string, Uint8Array>>();
58
- read(place: string): Promise<Map<string, Uint8Array>> {
59
- const entries = this.#places.get(place) ?? new Map<string, Uint8Array>();
60
- return Promise.resolve(new Map([...entries].map(([name, bytes]) => [name, bytes.slice()])));
61
- }
62
- // One object holds it, so a keep is whole by the run of one turn.
63
- write(place: string, entries: ReadonlyMap<string, Uint8Array | null>): Promise<void> {
64
- let kept = this.#places.get(place);
65
- if (!kept) this.#places.set(place, (kept = new Map()));
66
- for (const [name, bytes] of entries) {
67
- if (bytes === null) kept.delete(name);
68
- else kept.set(name, bytes.slice());
69
- }
70
- if (kept.size === 0) this.#places.delete(place);
71
- return Promise.resolve();
72
- }
73
-
74
- places(): Promise<string[]> {
75
- return Promise.resolve([...this.#places.keys()]);
76
- }
77
-
78
- forget(place: string): Promise<void> {
79
- this.#places.delete(place);
80
- return Promise.resolve();
81
- }
82
- }
@@ -1,4 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // The pointer dock: a whole world with `nervur` alone.
3
- export { CryptoEntropy, HeldCustody, HeldLoader, SystemClock, VolatileMemory } from './bodies.ts';
4
- export { NoCarrier, PointerCarrier, PointerTerrain, World, type PointerParts } from './world.ts';