spec-3nweb-server 1.8.5 → 1.8.7

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 (340) hide show
  1. package/.gitlab-ci-linux.yml +50 -0
  2. package/.gitlab-ci-mac.yml +29 -0
  3. package/.gitlab-ci-windows.yml +29 -0
  4. package/.gitlab-ci.yml +63 -0
  5. package/build/3nstorage/resources/store.d.ts +2 -2
  6. package/build/3nstorage/resources/store.js +1 -1
  7. package/build/3nstorage/resources/users.d.ts +3 -3
  8. package/build/3nstorage/resources/users.js +1 -1
  9. package/build/3nstorage/resources/utils.d.ts +6 -0
  10. package/build/3nstorage/resources/utils.js +41 -0
  11. package/build/3nstorage/routes/owner/archive-obj-version.js +8 -10
  12. package/build/3nstorage/routes/owner/cancel-trans.js +5 -4
  13. package/build/3nstorage/routes/owner/delete-obj.js +7 -8
  14. package/build/3nstorage/routes/owner/get-archived-obj-ver.js +11 -13
  15. package/build/3nstorage/routes/owner/get-current-obj.js +7 -6
  16. package/build/3nstorage/routes/owner/get-obj-status.js +8 -8
  17. package/build/3nstorage/routes/owner/put-current-obj.js +25 -55
  18. package/build/3nstorage/routes/owner/session-params.js +2 -0
  19. package/build/asmail/resources/utils.d.ts +6 -0
  20. package/build/asmail/resources/utils.js +36 -0
  21. package/build/asmail/routes/delivery/finalize-delivery.js +3 -2
  22. package/build/asmail/routes/delivery/pre-flight.js +9 -26
  23. package/build/asmail/routes/delivery/provide-recipient-pubkey.js +4 -3
  24. package/build/asmail/routes/delivery/put-metadata.js +13 -21
  25. package/build/asmail/routes/delivery/put-obj.js +19 -25
  26. package/build/asmail/routes/delivery/restart-session.d.ts +4 -8
  27. package/build/asmail/routes/delivery/restart-session.js +10 -23
  28. package/build/asmail/routes/delivery/sender-authorization.js +5 -5
  29. package/build/asmail/routes/delivery/start-session.js +9 -24
  30. package/build/asmail/routes/retrieval/get-message-meta.js +4 -7
  31. package/build/asmail/routes/retrieval/get-message-obj.js +10 -7
  32. package/build/asmail/routes/retrieval/list-messages.js +4 -4
  33. package/build/asmail/routes/retrieval/remove-message.js +3 -2
  34. package/build/mock/tls-options.js +46 -44
  35. package/build/tests/admin-proto/signup.d.ts +1 -0
  36. package/build/tests/admin-proto/signup.js +235 -0
  37. package/build/tests/jasmine.d.ts +1 -0
  38. package/build/tests/jasmine.js +32 -0
  39. package/build/tests/libs-for-tests/3nstorage.d.ts +22 -0
  40. package/build/tests/libs-for-tests/3nstorage.js +123 -0
  41. package/build/tests/libs-for-tests/asmail.d.ts +22 -0
  42. package/build/tests/libs-for-tests/asmail.js +179 -0
  43. package/build/tests/libs-for-tests/assert.d.ts +1 -0
  44. package/build/tests/libs-for-tests/assert.js +26 -0
  45. package/build/tests/libs-for-tests/async-jasmine.d.ts +7 -0
  46. package/build/tests/libs-for-tests/async-jasmine.js +77 -0
  47. package/build/tests/libs-for-tests/bytes-equal.d.ts +1 -0
  48. package/build/tests/libs-for-tests/bytes-equal.js +34 -0
  49. package/build/tests/libs-for-tests/json-copy.d.ts +1 -0
  50. package/build/tests/libs-for-tests/json-copy.js +45 -0
  51. package/build/tests/libs-for-tests/json-equal.d.ts +4 -0
  52. package/build/tests/libs-for-tests/json-equal.js +67 -0
  53. package/build/tests/libs-for-tests/mailerid.d.ts +13 -0
  54. package/build/tests/libs-for-tests/mailerid.js +114 -0
  55. package/build/tests/libs-for-tests/pkl.d.ts +28 -0
  56. package/build/tests/libs-for-tests/pkl.js +89 -0
  57. package/build/tests/libs-for-tests/server-components/3nstorage.d.ts +20 -0
  58. package/build/tests/libs-for-tests/server-components/3nstorage.js +200 -0
  59. package/build/tests/libs-for-tests/server-components/admin.d.ts +6 -0
  60. package/build/tests/libs-for-tests/server-components/admin.js +43 -0
  61. package/build/tests/libs-for-tests/server-components/asmail.d.ts +16 -0
  62. package/build/tests/libs-for-tests/server-components/asmail.js +143 -0
  63. package/build/tests/libs-for-tests/server-components/component.d.ts +13 -0
  64. package/build/tests/libs-for-tests/server-components/component.js +77 -0
  65. package/build/tests/libs-for-tests/server-components/mailerid.d.ts +10 -0
  66. package/build/tests/libs-for-tests/server-components/mailerid.js +103 -0
  67. package/build/tests/libs-for-tests/server-runner.d.ts +15 -0
  68. package/build/tests/libs-for-tests/server-runner.js +69 -0
  69. package/build/tests/libs-for-tests/spec-assembly.d.ts +6 -0
  70. package/build/tests/libs-for-tests/spec-assembly.js +55 -0
  71. package/build/tests/libs-for-tests/ws-utils.d.ts +3 -0
  72. package/build/tests/libs-for-tests/ws-utils.js +59 -0
  73. package/build/tests/libs-for-tests/xhr-utils.d.ts +61 -0
  74. package/build/tests/libs-for-tests/xhr-utils.js +154 -0
  75. package/build/tests/protocols/3nstorage/owner/specs/archive-obj-ver.d.ts +2 -0
  76. package/build/tests/protocols/3nstorage/owner/specs/archive-obj-ver.js +84 -0
  77. package/build/tests/protocols/3nstorage/owner/specs/archive-root-ver.d.ts +2 -0
  78. package/build/tests/protocols/3nstorage/owner/specs/archive-root-ver.js +78 -0
  79. package/build/tests/protocols/3nstorage/owner/specs/delete-archive-obj-ver.d.ts +2 -0
  80. package/build/tests/protocols/3nstorage/owner/specs/delete-archive-obj-ver.js +83 -0
  81. package/build/tests/protocols/3nstorage/owner/specs/delete-archive-root-ver.d.ts +2 -0
  82. package/build/tests/protocols/3nstorage/owner/specs/delete-archive-root-ver.js +77 -0
  83. package/build/tests/protocols/3nstorage/owner/specs/delete-obj.d.ts +2 -0
  84. package/build/tests/protocols/3nstorage/owner/specs/delete-obj.js +75 -0
  85. package/build/tests/protocols/3nstorage/owner/specs/events.d.ts +2 -0
  86. package/build/tests/protocols/3nstorage/owner/specs/events.js +91 -0
  87. package/build/tests/protocols/3nstorage/owner/specs/get-current-obj.d.ts +2 -0
  88. package/build/tests/protocols/3nstorage/owner/specs/get-current-obj.js +269 -0
  89. package/build/tests/protocols/3nstorage/owner/specs/get-current-root.d.ts +2 -0
  90. package/build/tests/protocols/3nstorage/owner/specs/get-current-root.js +262 -0
  91. package/build/tests/protocols/3nstorage/owner/specs/get-obj-status.d.ts +2 -0
  92. package/build/tests/protocols/3nstorage/owner/specs/get-obj-status.js +79 -0
  93. package/build/tests/protocols/3nstorage/owner/specs/get-root-status.d.ts +2 -0
  94. package/build/tests/protocols/3nstorage/owner/specs/get-root-status.js +72 -0
  95. package/build/tests/protocols/3nstorage/owner/specs/put-current-obj.d.ts +2 -0
  96. package/build/tests/protocols/3nstorage/owner/specs/put-current-obj.js +287 -0
  97. package/build/tests/protocols/3nstorage/owner/specs/put-current-root.d.ts +2 -0
  98. package/build/tests/protocols/3nstorage/owner/specs/put-current-root.js +274 -0
  99. package/build/tests/protocols/3nstorage/owner/specs/trans-cancel-root.d.ts +2 -0
  100. package/build/tests/protocols/3nstorage/owner/specs/trans-cancel-root.js +73 -0
  101. package/build/tests/protocols/3nstorage/owner/specs/trans-cancel.d.ts +2 -0
  102. package/build/tests/protocols/3nstorage/owner/specs/trans-cancel.js +72 -0
  103. package/build/tests/protocols/3nstorage/owner/test-utils.d.ts +14 -0
  104. package/build/tests/protocols/3nstorage/owner/test-utils.js +54 -0
  105. package/build/tests/protocols/3nstorage/owner.d.ts +1 -0
  106. package/build/tests/protocols/3nstorage/owner.js +138 -0
  107. package/build/tests/protocols/asmail/config.d.ts +1 -0
  108. package/build/tests/protocols/asmail/config.js +117 -0
  109. package/build/tests/protocols/asmail/delivery/specs/finalize-delivery.d.ts +2 -0
  110. package/build/tests/protocols/asmail/delivery/specs/finalize-delivery.js +60 -0
  111. package/build/tests/protocols/asmail/delivery/specs/provide-recipient-pubkey.d.ts +2 -0
  112. package/build/tests/protocols/asmail/delivery/specs/provide-recipient-pubkey.js +68 -0
  113. package/build/tests/protocols/asmail/delivery/specs/put-metadata.d.ts +2 -0
  114. package/build/tests/protocols/asmail/delivery/specs/put-metadata.js +83 -0
  115. package/build/tests/protocols/asmail/delivery/specs/put-obj.d.ts +2 -0
  116. package/build/tests/protocols/asmail/delivery/specs/put-obj.js +123 -0
  117. package/build/tests/protocols/asmail/delivery/specs/restart-session.d.ts +2 -0
  118. package/build/tests/protocols/asmail/delivery/specs/restart-session.js +79 -0
  119. package/build/tests/protocols/asmail/delivery/specs/start-session.d.ts +2 -0
  120. package/build/tests/protocols/asmail/delivery/specs/start-session.js +64 -0
  121. package/build/tests/protocols/asmail/delivery/test-utils.d.ts +16 -0
  122. package/build/tests/protocols/asmail/delivery/test-utils.js +46 -0
  123. package/build/tests/protocols/asmail/delivery.d.ts +1 -0
  124. package/build/tests/protocols/asmail/delivery.js +46 -0
  125. package/build/tests/protocols/asmail/retrieval/specs/events.d.ts +2 -0
  126. package/build/tests/protocols/asmail/retrieval/specs/events.js +62 -0
  127. package/build/tests/protocols/asmail/retrieval/specs/get-message-meta.d.ts +2 -0
  128. package/build/tests/protocols/asmail/retrieval/specs/get-message-meta.js +70 -0
  129. package/build/tests/protocols/asmail/retrieval/specs/get-message-obj.d.ts +2 -0
  130. package/build/tests/protocols/asmail/retrieval/specs/get-message-obj.js +166 -0
  131. package/build/tests/protocols/asmail/retrieval/specs/list-messages.d.ts +2 -0
  132. package/build/tests/protocols/asmail/retrieval/specs/list-messages.js +96 -0
  133. package/build/tests/protocols/asmail/retrieval/specs/remove-message.d.ts +2 -0
  134. package/build/tests/protocols/asmail/retrieval/specs/remove-message.js +66 -0
  135. package/build/tests/protocols/asmail/retrieval/test-utils.d.ts +17 -0
  136. package/build/tests/protocols/asmail/retrieval/test-utils.js +46 -0
  137. package/build/tests/protocols/asmail/retrieval.d.ts +1 -0
  138. package/build/tests/protocols/asmail/retrieval.js +92 -0
  139. package/build/tests/protocols/mailerid.d.ts +1 -0
  140. package/build/tests/protocols/mailerid.js +210 -0
  141. package/build/tests/shared-checks/check-mid-login.d.ts +2 -0
  142. package/build/tests/shared-checks/check-mid-login.js +131 -0
  143. package/build/tests/shared-checks/requests.d.ts +20 -0
  144. package/build/tests/shared-checks/requests.js +123 -0
  145. package/package.json +1 -1
  146. package/packing/create-deb.sh +57 -0
  147. package/packing/deb/lib/systemd/system/3nweb.service +11 -0
  148. package/packing/deb/postinst +44 -0
  149. package/packing/deb/usr/share/doc/3nweb-server/conf-for-postinstall.yaml +19 -0
  150. package/packing/deb/usr/share/doc/3nweb-server/copyright +22 -0
  151. package/publish-task.sh +10 -0
  152. package/push-to-github.sh +105 -0
  153. package/ts-code/3nstorage/3nstorage-app.ts +99 -0
  154. package/ts-code/3nstorage/owner.ts +178 -0
  155. package/ts-code/3nstorage/resources/sessions.ts +37 -0
  156. package/ts-code/3nstorage/resources/store.ts +896 -0
  157. package/ts-code/3nstorage/resources/users.ts +258 -0
  158. package/ts-code/3nstorage/resources/utils.ts +45 -0
  159. package/ts-code/3nstorage/routes/owner/archive-obj-version.ts +71 -0
  160. package/ts-code/3nstorage/routes/owner/cancel-trans.ts +60 -0
  161. package/ts-code/3nstorage/routes/owner/delete-obj.ts +99 -0
  162. package/ts-code/3nstorage/routes/owner/get-archived-obj-ver.ts +109 -0
  163. package/ts-code/3nstorage/routes/owner/get-current-obj.ts +124 -0
  164. package/ts-code/3nstorage/routes/owner/get-obj-status.ts +55 -0
  165. package/ts-code/3nstorage/routes/owner/param-getter.ts +51 -0
  166. package/ts-code/3nstorage/routes/owner/param-setter.ts +50 -0
  167. package/ts-code/3nstorage/routes/owner/put-current-obj.ts +257 -0
  168. package/ts-code/3nstorage/routes/owner/session-params.ts +50 -0
  169. package/ts-code/3nstorage/shared.ts +43 -0
  170. package/ts-code/asmail/asmail-app.ts +91 -0
  171. package/ts-code/asmail/config.ts +96 -0
  172. package/ts-code/asmail/delivery.ts +94 -0
  173. package/ts-code/asmail/resources/delivery-sessions.ts +118 -0
  174. package/ts-code/asmail/resources/inbox.ts +696 -0
  175. package/ts-code/asmail/resources/recipients.ts +495 -0
  176. package/ts-code/asmail/resources/sessions.ts +37 -0
  177. package/ts-code/asmail/resources/utils.ts +41 -0
  178. package/ts-code/asmail/retrieval.ts +96 -0
  179. package/ts-code/asmail/routes/config/param-getter.ts +49 -0
  180. package/ts-code/asmail/routes/config/param-setter.ts +49 -0
  181. package/ts-code/asmail/routes/delivery/finalize-delivery.ts +51 -0
  182. package/ts-code/asmail/routes/delivery/pre-flight.ts +118 -0
  183. package/ts-code/asmail/routes/delivery/provide-recipient-pubkey.ts +58 -0
  184. package/ts-code/asmail/routes/delivery/put-metadata.ts +85 -0
  185. package/ts-code/asmail/routes/delivery/put-obj.ts +152 -0
  186. package/ts-code/asmail/routes/delivery/restart-session.ts +117 -0
  187. package/ts-code/asmail/routes/delivery/sender-authorization.ts +81 -0
  188. package/ts-code/asmail/routes/delivery/start-session.ts +136 -0
  189. package/ts-code/asmail/routes/retrieval/get-message-meta.ts +50 -0
  190. package/ts-code/asmail/routes/retrieval/get-message-obj.ts +106 -0
  191. package/ts-code/asmail/routes/retrieval/list-messages.ts +68 -0
  192. package/ts-code/asmail/routes/retrieval/remove-message.ts +48 -0
  193. package/ts-code/cli/index.ts +82 -0
  194. package/ts-code/cli/run-cmd.ts +81 -0
  195. package/ts-code/cli/show-sample-config.ts +78 -0
  196. package/ts-code/cli/signup-cmd.ts +229 -0
  197. package/ts-code/cli/user-cmd.ts +68 -0
  198. package/ts-code/cli/utils.ts +105 -0
  199. package/ts-code/config/default-confs.ts +22 -0
  200. package/ts-code/config/from-yaml.ts +35 -0
  201. package/ts-code/config/letsencrypt.ts +77 -0
  202. package/ts-code/config/signup.ts +137 -0
  203. package/ts-code/lib-common/admin-api/signup.ts +120 -0
  204. package/ts-code/lib-common/assert.ts +22 -0
  205. package/ts-code/lib-common/async-fs-node.ts +656 -0
  206. package/ts-code/lib-common/buffer-utils.ts +124 -0
  207. package/ts-code/lib-common/byte-streaming/common.ts +166 -0
  208. package/ts-code/lib-common/bytes-equal.ts +25 -0
  209. package/ts-code/lib-common/canonical-address.ts +92 -0
  210. package/ts-code/lib-common/exceptions/error.ts +44 -0
  211. package/ts-code/lib-common/exceptions/file.ts +84 -0
  212. package/ts-code/lib-common/exceptions/http.ts +69 -0
  213. package/ts-code/lib-common/ipc/generic-ipc.ts +750 -0
  214. package/ts-code/lib-common/ipc/ws-ipc.ts +117 -0
  215. package/ts-code/lib-common/json-equal.ts +55 -0
  216. package/ts-code/lib-common/json-utils.ts +130 -0
  217. package/ts-code/lib-common/jwkeys.ts +246 -0
  218. package/ts-code/lib-common/map-of-sets.ts +60 -0
  219. package/ts-code/lib-common/mid-sigs-NaCl-Ed.ts +677 -0
  220. package/ts-code/lib-common/objs-on-disk/file-layout.ts +480 -0
  221. package/ts-code/lib-common/objs-on-disk/obj-file.ts +325 -0
  222. package/ts-code/lib-common/objs-on-disk/utils.ts +201 -0
  223. package/ts-code/lib-common/objs-on-disk/v1-obj-file-format.ts +329 -0
  224. package/ts-code/lib-common/processes.ts +501 -0
  225. package/ts-code/lib-common/random-node.ts +79 -0
  226. package/ts-code/lib-common/service-api/3nstorage/owner.ts +559 -0
  227. package/ts-code/lib-common/service-api/3nstorage/root-route.ts +22 -0
  228. package/ts-code/lib-common/service-api/asmail/config.ts +144 -0
  229. package/ts-code/lib-common/service-api/asmail/delivery.ts +250 -0
  230. package/ts-code/lib-common/service-api/asmail/retrieval.ts +238 -0
  231. package/ts-code/lib-common/service-api/asmail/root-route.ts +22 -0
  232. package/ts-code/lib-common/service-api/ipc-api.ts +44 -0
  233. package/ts-code/lib-common/service-api/locker.ts +44 -0
  234. package/ts-code/lib-common/service-api/mailer-id/login.ts +80 -0
  235. package/ts-code/lib-common/service-api/mailer-id/provisioning.ts +64 -0
  236. package/ts-code/lib-common/service-api/mailer-id/root-route.ts +25 -0
  237. package/ts-code/lib-common/service-api/pub-key-login.ts +75 -0
  238. package/ts-code/lib-common/service-api/web-socket-base.ts +87 -0
  239. package/ts-code/lib-common/session-encryptor.ts +54 -0
  240. package/ts-code/lib-common/time-window-cache.ts +74 -0
  241. package/ts-code/lib-common/transferable.d.ts +30 -0
  242. package/ts-code/lib-server/conf-util.ts +35 -0
  243. package/ts-code/lib-server/mid-access.ts +45 -0
  244. package/ts-code/lib-server/middleware/body-parsers.ts +248 -0
  245. package/ts-code/lib-server/middleware/error-handler.ts +79 -0
  246. package/ts-code/lib-server/pkl-access.ts +46 -0
  247. package/ts-code/lib-server/resources/dns.ts +151 -0
  248. package/ts-code/lib-server/resources/mailerid-authorizer.ts +115 -0
  249. package/ts-code/lib-server/resources/mem-backed-sessions-factory.ts +132 -0
  250. package/ts-code/lib-server/resources/server-data-folders.ts +160 -0
  251. package/ts-code/lib-server/resources/server-key-for-pkl-challenge.ts +41 -0
  252. package/ts-code/lib-server/resources/sessions.ts +232 -0
  253. package/ts-code/lib-server/resources/user-files.ts +147 -0
  254. package/ts-code/lib-server/routes/pub-key-login/complete-exchange.ts +63 -0
  255. package/ts-code/lib-server/routes/pub-key-login/start-exchange.ts +189 -0
  256. package/ts-code/lib-server/routes/sessions/close.ts +27 -0
  257. package/ts-code/lib-server/routes/sessions/mid-auth.ts +77 -0
  258. package/ts-code/lib-server/routes/sessions/start.ts +116 -0
  259. package/ts-code/lib-server/web-sockets/app.ts +339 -0
  260. package/ts-code/lib-server/web-sockets/server-events.ts +63 -0
  261. package/ts-code/lib.ts +29 -0
  262. package/ts-code/locker/locker-app.ts +60 -0
  263. package/ts-code/locker/resources/lockers.ts +17 -0
  264. package/ts-code/locker/resources/sessions.ts +37 -0
  265. package/ts-code/locker/routes/create-locker.ts +47 -0
  266. package/ts-code/mailerId/mailerId-app.ts +107 -0
  267. package/ts-code/mailerId/resources/certifier.ts +181 -0
  268. package/ts-code/mailerId/resources/sessions.ts +44 -0
  269. package/ts-code/mailerId/resources/users.ts +88 -0
  270. package/ts-code/mailerId/routes/certify.ts +68 -0
  271. package/ts-code/main.ts +304 -0
  272. package/ts-code/mock/dns.ts +106 -0
  273. package/ts-code/mock/mock-as-child-proc.ts +130 -0
  274. package/ts-code/mock/node-child-ipc.ts +69 -0
  275. package/ts-code/mock/run-in-proc.ts +111 -0
  276. package/ts-code/mock/tls-options.ts +73 -0
  277. package/ts-code/services.ts +138 -0
  278. package/ts-code/signup/resources/users.ts +429 -0
  279. package/ts-code/signup/routes/add.ts +65 -0
  280. package/ts-code/signup/routes/get-available-addresses.ts +49 -0
  281. package/ts-code/signup/routes/get-available-domains.ts +45 -0
  282. package/ts-code/signup/signup-app.ts +54 -0
  283. package/ts-code/signup/signup-tokens.ts +148 -0
  284. package/ts-code/tests/admin-proto/signup.ts +277 -0
  285. package/ts-code/tests/jasmine.ts +37 -0
  286. package/ts-code/tests/libs-for-tests/3nstorage.ts +144 -0
  287. package/ts-code/tests/libs-for-tests/asmail.ts +220 -0
  288. package/ts-code/tests/libs-for-tests/assert.ts +22 -0
  289. package/ts-code/tests/libs-for-tests/async-jasmine.ts +89 -0
  290. package/ts-code/tests/libs-for-tests/bytes-equal.ts +25 -0
  291. package/ts-code/tests/libs-for-tests/json-copy.ts +38 -0
  292. package/ts-code/tests/libs-for-tests/json-equal.ts +55 -0
  293. package/ts-code/tests/libs-for-tests/mailerid.ts +123 -0
  294. package/ts-code/tests/libs-for-tests/pkl.ts +127 -0
  295. package/ts-code/tests/libs-for-tests/server-components/3nstorage.ts +206 -0
  296. package/ts-code/tests/libs-for-tests/server-components/admin.ts +49 -0
  297. package/ts-code/tests/libs-for-tests/server-components/asmail.ts +152 -0
  298. package/ts-code/tests/libs-for-tests/server-components/component.ts +90 -0
  299. package/ts-code/tests/libs-for-tests/server-components/mailerid.ts +114 -0
  300. package/ts-code/tests/libs-for-tests/server-runner.ts +82 -0
  301. package/ts-code/tests/libs-for-tests/spec-assembly.ts +60 -0
  302. package/ts-code/tests/libs-for-tests/ws-utils.ts +61 -0
  303. package/ts-code/tests/libs-for-tests/xhr-utils.ts +179 -0
  304. package/ts-code/tests/protocols/3nstorage/owner/specs/archive-obj-ver.ts +100 -0
  305. package/ts-code/tests/protocols/3nstorage/owner/specs/archive-root-ver.ts +91 -0
  306. package/ts-code/tests/protocols/3nstorage/owner/specs/delete-archive-obj-ver.ts +99 -0
  307. package/ts-code/tests/protocols/3nstorage/owner/specs/delete-archive-root-ver.ts +90 -0
  308. package/ts-code/tests/protocols/3nstorage/owner/specs/delete-obj.ts +84 -0
  309. package/ts-code/tests/protocols/3nstorage/owner/specs/events.ts +125 -0
  310. package/ts-code/tests/protocols/3nstorage/owner/specs/get-current-obj.ts +298 -0
  311. package/ts-code/tests/protocols/3nstorage/owner/specs/get-current-root.ts +290 -0
  312. package/ts-code/tests/protocols/3nstorage/owner/specs/get-obj-status.ts +88 -0
  313. package/ts-code/tests/protocols/3nstorage/owner/specs/get-root-status.ts +80 -0
  314. package/ts-code/tests/protocols/3nstorage/owner/specs/put-current-obj.ts +366 -0
  315. package/ts-code/tests/protocols/3nstorage/owner/specs/put-current-root.ts +329 -0
  316. package/ts-code/tests/protocols/3nstorage/owner/specs/trans-cancel-root.ts +86 -0
  317. package/ts-code/tests/protocols/3nstorage/owner/specs/trans-cancel.ts +86 -0
  318. package/ts-code/tests/protocols/3nstorage/owner/test-utils.ts +72 -0
  319. package/ts-code/tests/protocols/3nstorage/owner.ts +198 -0
  320. package/ts-code/tests/protocols/asmail/config.ts +172 -0
  321. package/ts-code/tests/protocols/asmail/delivery/specs/finalize-delivery.ts +69 -0
  322. package/ts-code/tests/protocols/asmail/delivery/specs/provide-recipient-pubkey.ts +88 -0
  323. package/ts-code/tests/protocols/asmail/delivery/specs/put-metadata.ts +102 -0
  324. package/ts-code/tests/protocols/asmail/delivery/specs/put-obj.ts +156 -0
  325. package/ts-code/tests/protocols/asmail/delivery/specs/restart-session.ts +97 -0
  326. package/ts-code/tests/protocols/asmail/delivery/specs/start-session.ts +79 -0
  327. package/ts-code/tests/protocols/asmail/delivery/test-utils.ts +64 -0
  328. package/ts-code/tests/protocols/asmail/delivery.ts +56 -0
  329. package/ts-code/tests/protocols/asmail/retrieval/specs/events.ts +78 -0
  330. package/ts-code/tests/protocols/asmail/retrieval/specs/get-message-meta.ts +80 -0
  331. package/ts-code/tests/protocols/asmail/retrieval/specs/get-message-obj.ts +197 -0
  332. package/ts-code/tests/protocols/asmail/retrieval/specs/list-messages.ts +110 -0
  333. package/ts-code/tests/protocols/asmail/retrieval/specs/remove-message.ts +74 -0
  334. package/ts-code/tests/protocols/asmail/retrieval/test-utils.ts +60 -0
  335. package/ts-code/tests/protocols/asmail/retrieval.ts +115 -0
  336. package/ts-code/tests/protocols/mailerid.ts +271 -0
  337. package/ts-code/tests/shared-checks/check-mid-login.ts +164 -0
  338. package/ts-code/tests/shared-checks/requests.ts +135 -0
  339. package/ts-code/tsconfig.json +11 -0
  340. package/ts-code/web3n.d.ts +1068 -0
@@ -0,0 +1,896 @@
1
+ /*
2
+ Copyright (C) 2015 - 2017, 2019 - 2020, 2022, 2025 - 2026 3NSoft Inc.
3
+
4
+ This program is free software: you can redistribute it and/or modify it under
5
+ the terms of the GNU General Public License as published by the Free Software
6
+ Foundation, either version 3 of the License, or (at your option) any later
7
+ version.
8
+
9
+ This program is distributed in the hope that it will be useful, but
10
+ WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
+ See the GNU General Public License for more details.
13
+
14
+ You should have received a copy of the GNU General Public License along with
15
+ this program. If not, see <http://www.gnu.org/licenses/>.
16
+ */
17
+
18
+ /**
19
+ * Everything in this module is assumed to be inside of a storage reliance set.
20
+ *
21
+ * Store files are laid out on disk in the following way:
22
+ * (a) store is just a folder with stuff inside;
23
+ * (b) main store folder contains following folders:
24
+ * (b.1) root - is afolder for root object;
25
+ * (b.2) objects - is a folder for non-root object folders;
26
+ * (b.3) transactions - is a folder with non-root object transactions;
27
+ * (b.4) params - is a folder for information files about this storage;
28
+ * (c) object folder's name is object's id;
29
+ * (d) object folder contains:
30
+ * (d.1) status - is a json file with information about an object;
31
+ * (d.2) N. - is a file with an N'th version object's either diff or whole
32
+ * object file;
33
+ * (e) transactions folder (for root and non-root) contains:
34
+ * (e.1) transaction - is a json file with current transaction's info;
35
+ * this file is present only for duration of a transaction,
36
+ * and also acts as a transaction lock;
37
+ * (e.2) new - is a new version, still in transaction, either diff or whole
38
+ * object file;
39
+ */
40
+
41
+ import * as fs from '../../lib-common/async-fs-node';
42
+ import { Readable } from 'stream';
43
+ import { PutObjFirstQueryOpts, PutObjSecondQueryOpts } from '../../lib-common/service-api/3nstorage/owner';
44
+ import { stringOfB64UrlSafeChars } from '../../lib-common/random-node';
45
+ import { UserFiles, SC as ufSC, ObjReader } from '../../lib-server/resources/user-files';
46
+ import { DiffInfo, events, ObjStatus } from '../../lib-common/service-api/3nstorage/owner';
47
+ import { streamToObjFile, diffToLayout, chunksInOrderedStream, makeObjPipe, GetObjFile } from '../../lib-common/objs-on-disk/utils';
48
+ import { TimeWindowCache } from '../../lib-common/time-window-cache';
49
+ import { ObjVersionFile } from '../../lib-common/objs-on-disk/obj-file';
50
+ import { errWithCause } from '../../lib-common/exceptions/error';
51
+ import { join } from 'path';
52
+ import { NamedProcs } from '../../lib-common/processes';
53
+
54
+ export { DiffInfo } from '../../lib-common/service-api/3nstorage/owner';
55
+ export { ObjPipe, ObjReader } from '../../lib-server/resources/user-files';
56
+
57
+ export type StorageEventsSink = (
58
+ userId: string, channel: events.EventNameType, event: events.AllTypes
59
+ ) => void;
60
+
61
+ export const SC = {
62
+ USER_UNKNOWN: ufSC.USER_UNKNOWN,
63
+ OBJ_EXIST: 'obj-already-exist',
64
+ OBJ_UNKNOWN: 'obj-unknown',
65
+ OBJ_VER_UNKNOWN: 'obj-ver-unknown',
66
+ OBJ_VER_EXIST: 'obj-ver-already-exist',
67
+ CONCURRENT_TRANSACTION: "concurrent-transactions",
68
+ TRANSACTION_UNKNOWN: "transactions-unknown",
69
+ NOT_ENOUGH_SPACE: "not-enough-space",
70
+ OBJ_FILE_INCOMPLETE: 'obj-file-incomplete',
71
+ };
72
+ Object.freeze(SC);
73
+
74
+ export interface MismatchedObjVerException {
75
+ type: 'mismatched-obj-ver';
76
+ current_version: number;
77
+ }
78
+
79
+ export function makeMismatchedObjVerException(current_version: number):
80
+ MismatchedObjVerException {
81
+ return {
82
+ type: 'mismatched-obj-ver',
83
+ current_version
84
+ };
85
+ }
86
+
87
+ export interface ObjStatusInfo {
88
+ state: 'new' | 'current' | 'archived';
89
+ currentVersion?: number;
90
+ archivedVersions?: number[];
91
+ }
92
+
93
+ const STATUS_FILE = 'status';
94
+
95
+ export interface TransactionParams {
96
+ isNewObj?: boolean;
97
+ version: number;
98
+ baseVersion?: number;
99
+ }
100
+
101
+ export interface TransactionInfo extends TransactionParams {
102
+ transactionType: 'write' | 'remove' | 'archive';
103
+ transactionId: string;
104
+ }
105
+
106
+ interface SpaceInfo {
107
+ free: number;
108
+ used: number;
109
+ }
110
+
111
+ export interface StorageParams {
112
+ "key-deriv": any;
113
+ }
114
+
115
+ // XXX move this space-tracker into user-files file. One instance for the whole
116
+ // server should do the job.
117
+ class SpaceTracker {
118
+
119
+ private space: {
120
+ [userId: string]: SpaceInfo;
121
+ } = {};
122
+
123
+ constructor() {
124
+ Object.freeze(this);
125
+ }
126
+
127
+ private async diskUsed(path: string, runNum = 0): Promise<number> {
128
+
129
+ // XXX use calculated value, updating it from time to time with du <folder>. while on windows du might be in linux console!
130
+
131
+ return 0;
132
+ }
133
+
134
+ /**
135
+ * @param store
136
+ * @return a promise, resolvable to space info object.
137
+ */
138
+ private async updateSpaceInfo(store: Store): Promise<SpaceInfo> {
139
+ const usedSpace = await this.diskUsed(store.path);
140
+ const quota = await store.getSpaceQuota();
141
+ return {
142
+ free: Math.max(0, quota-usedSpace),
143
+ used: usedSpace
144
+ };
145
+ }
146
+
147
+ async change(store: Store, delta: number): Promise<void> {
148
+ let s = this.space[store.userId];
149
+ if (!s) {
150
+ s = await this.updateSpaceInfo(store);
151
+ }
152
+ if ((delta > 0) && ((s.free - delta) < 0)) {
153
+ throw SC.NOT_ENOUGH_SPACE;
154
+ }
155
+ s.free -= delta;
156
+ s.used += delta;
157
+ }
158
+
159
+ reset(userId: string): void {
160
+ delete this.space[userId];
161
+ }
162
+
163
+ }
164
+ Object.freeze(SpaceTracker.prototype);
165
+ Object.freeze(SpaceTracker);
166
+
167
+
168
+ const spaceTracker = new SpaceTracker();
169
+
170
+ const SINGLE_BYTE_BUF = Buffer.alloc(1);
171
+ SINGLE_BYTE_BUF[0] = 0;
172
+
173
+
174
+ export class Store extends UserFiles<StorageParams> {
175
+
176
+ private objVerFiles = new ObjVerFiles(30*1000);
177
+
178
+ private statuses = new ObjStatuses(30*1000, this.path);
179
+
180
+ private transactions = new ObjTransactions(
181
+ 30*1000, this.statuses, this.path, this.objVerFiles.uncache
182
+ );
183
+
184
+ constructor(userId: string, path: string,
185
+ private storageEventsSink: StorageEventsSink,
186
+ writeBufferSize?: string|number, readBufferSize?: string|number
187
+ ) {
188
+ super(userId, path, writeBufferSize, readBufferSize);
189
+ Object.freeze(this);
190
+ }
191
+
192
+ static async make(
193
+ userFolder: string, userId: string, storageEventsSink: StorageEventsSink,
194
+ writeBufferSize?: string|number, readBufferSize?: string|number
195
+ ): Promise<Store> {
196
+ const path = join(userFolder, 'store');
197
+ const store = new Store(userId, path, storageEventsSink,
198
+ writeBufferSize, readBufferSize);
199
+ await store.ensureUserExistsOnDisk();
200
+ return store;
201
+ }
202
+
203
+ /**
204
+ * This method starts object saving transaction, saves bytes from a stream,
205
+ * and, if upload is complete in one request, closes transaction.
206
+ * Returned promise resolves to a transaction id, if transaction is not
207
+ * complete, and to undefined, otherwise.
208
+ * @param objId is a string object id for non-root objects, and null for
209
+ * root object.
210
+ * @param bytes is a stream of bytes that has object header, and some or all
211
+ * segment bytes, following header.
212
+ * @param byteLen is an expected length of a given byte stream.
213
+ * @param opts
214
+ */
215
+ async startSavingObjNewVersion(
216
+ objId: string|null, diff: DiffInfo|undefined,
217
+ bytes: Readable, byteLen: number, opts: PutObjFirstQueryOpts
218
+ ): Promise<string|undefined> {
219
+ if (byteLen > 0) {
220
+ await spaceTracker.change(this, byteLen);
221
+ }
222
+
223
+ const params: TransactionParams = {
224
+ version: opts.ver,
225
+ baseVersion: (diff ? diff.baseVersion : undefined)
226
+ };
227
+ if (opts.ver === 1) {
228
+ params.isNewObj = true;
229
+ }
230
+ const trans = await this.transactions.startNew(objId, params);
231
+
232
+ let file: ObjVersionFile;
233
+ try {
234
+ const filePath = this.transactions.objFileWritingPath(objId);
235
+ file = await this.objVerFiles.forNewFile(
236
+ objId, trans.version, filePath);
237
+ if (diff) {
238
+ await file.setSegsLayout(diffToLayout(diff), false);
239
+ }
240
+
241
+ const chunks = chunksInOrderedStream(byteLen, opts.header, 0);
242
+ await streamToObjFile(file, chunks, bytes, this.fileWritingBufferSize);
243
+
244
+ if (opts.last) {
245
+ if (!file.isFileComplete()) { throw SC.OBJ_FILE_INCOMPLETE; }
246
+ await this.transactions.complete(objId, trans, file);
247
+ this.storageEventsSink(
248
+ this.userId,
249
+ events.objChanged.EVENT_NAME,
250
+ { objId, newVer: opts.ver }
251
+ );
252
+ } else {
253
+ return trans.transactionId;
254
+ }
255
+
256
+ } catch (err) {
257
+ await spaceTracker.change(this, -byteLen);
258
+ await this.transactions.cancel(objId, trans.transactionId).catch(noop);
259
+ throw err;
260
+ }
261
+ }
262
+
263
+ /**
264
+ * This method continues object saving transaction. If this request is the
265
+ * last, it closes transaction.
266
+ * Returned promise resolves to a transaction id, if transaction is not
267
+ * complete, and to undefined, if it is closed.
268
+ * @param objId is a string object id for non-root objects, and null for
269
+ * root object.
270
+ * @param bytes is a stream of bytes that has optional diff info, object
271
+ * header, and some or all segment bytes in this order.
272
+ * @param byteLen is an expected length of a given byte stream.
273
+ * @param opts
274
+ */
275
+ async continueSavingObjNewVersion(
276
+ objId: string|null, bytes: Readable, byteLen: number,
277
+ opts: PutObjSecondQueryOpts
278
+ ): Promise<string|undefined> {
279
+ const trans = await this.transactions.get(objId, opts.trans);
280
+
281
+ try {
282
+ const filePath = this.transactions.objFileWritingPath(objId);
283
+ const file = await this.objVerFiles.forExistingFile(
284
+ objId, trans.version, filePath);
285
+
286
+ if (byteLen > 0) {
287
+ await spaceTracker.change(this, byteLen);
288
+ const chunks = chunksInOrderedStream(byteLen, undefined, opts.ofs);
289
+ await streamToObjFile(
290
+ file, chunks, bytes, this.fileWritingBufferSize);
291
+ }
292
+
293
+ if (opts.last) {
294
+ if (!file.isFileComplete()) { throw SC.OBJ_FILE_INCOMPLETE; }
295
+ await this.transactions.complete(objId, trans, file);
296
+ this.storageEventsSink(
297
+ this.userId,
298
+ events.objChanged.EVENT_NAME,
299
+ { objId, newVer: trans.version }
300
+ );
301
+ } else {
302
+ return opts.trans;
303
+ }
304
+ } catch (err) {
305
+ await spaceTracker.change(this, -byteLen);
306
+ await this.transactions.cancel(objId, trans.transactionId).catch(() => {});
307
+ throw err;
308
+ }
309
+ }
310
+
311
+ async cancelTransaction(
312
+ objId: string|null, transactionId?: string
313
+ ): Promise<void> {
314
+ await this.transactions.cancel(objId, transactionId);
315
+ }
316
+
317
+ /**
318
+ * This method promises to return object reader with a corresponding version
319
+ * number.
320
+ * @param objId is a string object id for non-root objects, and null for
321
+ * root object.
322
+ * @param header is a boolean flag, which true value indicates that header
323
+ * bytes should be present.
324
+ * @param segsOffset is a read starting point in segments. If header is
325
+ * present, this offset must be zero.
326
+ * @param segsLimit is a maximum number of segment bytes to read. Undefined
327
+ * indicates that all bytes can be read.
328
+ */
329
+ async getCurrentObj(
330
+ objId: string|null, header: boolean, segsOffset: number,
331
+ segsLimit: number|undefined
332
+ ): Promise<{ reader: ObjReader; version: number; }> {
333
+ const status = await this.statuses.get(objId);
334
+ const version = status.currentVersion;
335
+ if (typeof version !== 'number') { throw SC.OBJ_UNKNOWN; }
336
+ const reader = await this.makeObjReader(
337
+ objId, version, header, segsOffset, segsLimit);
338
+ return { reader, version };
339
+ }
340
+
341
+ /**
342
+ * This method promises to return object reader.
343
+ * @param objId is a string object id for non-root objects, and null for
344
+ * root object.
345
+ * @param version is object's archived version number
346
+ * @param header is a boolean flag, which true value indicates that header
347
+ * bytes should be present.
348
+ * @param segsOffset is a read starting point in segments. If header is
349
+ * present, this offset must be zero.
350
+ * @param segsLimit is a maximum number of segment bytes to read. Undefined
351
+ * indicates that all bytes can be read.
352
+ */
353
+ async getArchivedObjVersion(
354
+ objId: string|null, version: number,
355
+ header: boolean, segsOffset: number, segsLimit: number|undefined
356
+ ): Promise<ObjReader> {
357
+ const status = await this.statuses.get(objId);
358
+ if (!status.archivedVersions) { throw SC.OBJ_UNKNOWN; }
359
+ if (status.archivedVersions.indexOf(version) < 0) {
360
+ throw SC.OBJ_UNKNOWN; }
361
+ const reader = await this.makeObjReader(
362
+ objId, version, header, segsOffset, segsLimit);
363
+ return reader;
364
+ }
365
+
366
+ private async makeObjReader(
367
+ objId: string|null, version: number,
368
+ header: boolean, segsOfs: number, segsLimit: number|undefined
369
+ ): Promise<ObjReader> {
370
+ const file = await this.getObjFile(objId, version);
371
+
372
+ // find total segments length
373
+ const segsLen = file.getTotalSegsLen();
374
+
375
+ // contain boundary parameters offsetIntoSegs and len for segment bytes
376
+ if (segsLen < segsOfs) {
377
+ segsOfs = segsLen;
378
+ }
379
+ let segBytesToRead: number;
380
+ if (segsLimit === undefined) {
381
+ segBytesToRead = segsLen - segsOfs;
382
+ } else if ((segsOfs+segsLimit) >= segsLen) {
383
+ segBytesToRead = segsLen - segsOfs;
384
+ } else {
385
+ segBytesToRead = segsLimit;
386
+ }
387
+
388
+ // construct reader
389
+ let reader: ObjReader;
390
+ if (header) {
391
+ const headerLen = file.getHeaderLen()!;
392
+ reader = {
393
+ len: (segBytesToRead + headerLen),
394
+ segsLen,
395
+ headerLen,
396
+ pipe: makeObjPipe(
397
+ file, true, segsOfs, segBytesToRead, objId, this.getObjFile)
398
+ };
399
+ } else {
400
+ reader = {
401
+ len: segBytesToRead,
402
+ segsLen,
403
+ pipe: makeObjPipe(
404
+ file, false, segsOfs, segBytesToRead, objId, this.getObjFile)
405
+ };
406
+ }
407
+ Object.freeze(reader);
408
+
409
+ return reader;
410
+ }
411
+
412
+ private getObjFile: GetObjFile = async (
413
+ objId: string|null, version: number
414
+ ): Promise<ObjVersionFile> => {
415
+ const filePath = this.statuses.objFileReadingPath(objId, version);
416
+ return await this.objVerFiles.forExistingFile(
417
+ objId, version, filePath).catch((exc: fs.FileException) => {
418
+ if (exc.notFound) { throw SC.OBJ_UNKNOWN; }
419
+ throw exc;
420
+ });
421
+ };
422
+
423
+ async deleteCurrentObjVer(
424
+ objId: string, version: number|null = null
425
+ ): Promise<void> {
426
+ if (!objId) { throw new Error('Root object is not removable.'); }
427
+ const status = await this.statuses.get(objId);
428
+ if (status.state !== 'current') { throw SC.OBJ_UNKNOWN; }
429
+ if (version && (version !== status.currentVersion)) {
430
+ throw SC.OBJ_VER_UNKNOWN;
431
+ }
432
+
433
+ // XXX need to put removal transaction, closing it in a finally clause
434
+
435
+ const arch = status.archivedVersions;
436
+ if (!Array.isArray(arch) || (arch.length === 0)) {
437
+ // remove everything related to this object
438
+ await this.statuses.deleteWithObj(objId);
439
+ } else {
440
+ // set status and remove version if it isn't archived
441
+ version = status.currentVersion!;
442
+ delete status.currentVersion;
443
+ status.state = 'archived';
444
+ await this.statuses.set(objId, status);
445
+ if (arch.indexOf(version) < 0) {
446
+ await this.rmObjFiles(objId, version);
447
+ }
448
+ }
449
+
450
+ this.storageEventsSink(
451
+ this.userId,
452
+ events.objRemoved.EVENT_NAME,
453
+ { objId }
454
+ );
455
+ }
456
+
457
+ async deleteArchivedObjVer(
458
+ objId: string|null, version: number
459
+ ): Promise<void> {
460
+ const status = await this.statuses.get(objId);
461
+ const arch = status.archivedVersions;
462
+
463
+ // XXX need to put removal transaction, closing it in a finally clause
464
+
465
+ if (!Array.isArray(arch)) { throw SC.OBJ_VER_UNKNOWN; }
466
+ const indInArch = arch.indexOf(version);
467
+ if (indInArch < 0) { throw SC.OBJ_VER_UNKNOWN; }
468
+ arch.splice(indInArch, 1);
469
+ if (arch.length === 0) {
470
+ delete status.archivedVersions;
471
+ }
472
+ await this.statuses.set(objId, status);
473
+ if (!status.archivedVersions && !status.currentVersion) {
474
+ await this.rmObjFiles(objId!, version);
475
+ } else if (status.currentVersion !== version) {
476
+ await this.statuses.deleteWithObj(objId);
477
+ }
478
+
479
+ this.storageEventsSink(
480
+ this.userId,
481
+ events.objArchivedVersionRemoved.EVENT_NAME,
482
+ { objId, archivedVer: version }
483
+ );
484
+ }
485
+
486
+ /**
487
+ * @param objId is a string object id for non-root objects, and null for
488
+ * root object.
489
+ * @param version identifies exact version of files for removal.
490
+ * @return a promise, resolvable when version files are removed.
491
+ */
492
+ private async rmObjFiles(objId: string, version: number): Promise<void> {
493
+ const objFolder = this.statuses.objFolder(objId);
494
+ let objFiles = await fs.readdir(objFolder)
495
+ .catch((exc: fs.FileException) => {
496
+ if (exc.notFound) { throw SC.OBJ_UNKNOWN; }
497
+ else { throw exc; }
498
+ });
499
+ const verPart = toFName(version);
500
+ objFiles = objFiles.filter(fName => fName.startsWith(verPart));
501
+ if (objFiles.length === 0) { throw SC.OBJ_VER_UNKNOWN; }
502
+ for (const fName of objFiles) {
503
+ await fs.unlink(join(objFolder, fName)).catch(() => {});
504
+ }
505
+ }
506
+
507
+ // XXX need both, archiving and removal transactions for atomic locking
508
+ // should these be like empty lock files instead of saving folders,
509
+ // with code distiguishing between the two, while concurrent transaction
510
+ // only need fs.stat for a given name, or a fail of creating lock
511
+ // file/folder.
512
+
513
+ async archiveCurrentObjVersion(
514
+ objId: string|null, version: number
515
+ ): Promise<void> {
516
+ // XXX need to put archiving transaction, closing it in a finally clause
517
+ // - make in memory transactions for archival and removal, or chaining(?)
518
+
519
+ const status = await this.statuses.get(objId);
520
+ if (status.state !== 'current') { throw SC.OBJ_UNKNOWN; }
521
+ if (status.currentVersion !== version) {
522
+ throw makeMismatchedObjVerException(status.currentVersion!);
523
+ }
524
+ const arch = status.archivedVersions;
525
+ if (Array.isArray(arch)) {
526
+ arch.push(status.currentVersion);
527
+ } else {
528
+ status.archivedVersions = [ status.currentVersion ];
529
+ }
530
+ await this.statuses.set(objId, status);
531
+ this.storageEventsSink(
532
+ this.userId,
533
+ events.objVersionArchived.EVENT_NAME,
534
+ { objId, archivedVer: version }
535
+ );
536
+ }
537
+
538
+ async getObjStatus(objId: string|null): Promise<ObjStatus> {
539
+ const status = await this.statuses.get(objId);
540
+ return {
541
+ current: status.currentVersion,
542
+ archived: status.archivedVersions
543
+ };
544
+ }
545
+
546
+ async setKeyDerivParams(
547
+ params: any, setDefault: boolean
548
+ ): Promise<boolean> {
549
+ if (setDefault) {
550
+ params = {};
551
+ } else if ((typeof params !== 'object') || Array.isArray(params)) {
552
+ return false;
553
+ }
554
+ await this.setParam('key-deriv', params);
555
+ return true;
556
+ }
557
+
558
+ }
559
+ Object.freeze(Store.prototype);
560
+ Object.freeze(Store);
561
+
562
+
563
+ class ObjVerFiles {
564
+
565
+ private cache: TimeWindowCache<string, ObjVersionFile>;
566
+
567
+ constructor(cachePeriodMillis: number) {
568
+ this.cache = new TimeWindowCache(cachePeriodMillis);
569
+ Object.freeze(this);
570
+ }
571
+
572
+ async forNewFile(
573
+ objId: string|null, version: number, path: string
574
+ ): Promise<ObjVersionFile> {
575
+ const cacheId = this.cacheIdFor(objId, version);
576
+ const objFile = await ObjVersionFile.createNew(path)
577
+ .catch((exc: fs.FileException) => {
578
+ if (exc.alreadyExists) { throw SC.OBJ_VER_EXIST; }
579
+ throw exc;
580
+ });
581
+ this.cache.set(cacheId, objFile);
582
+ return objFile;
583
+ }
584
+
585
+ async forExistingFile(
586
+ objId: string|null, version: number, path?: string
587
+ ): Promise<ObjVersionFile> {
588
+ const cacheId = this.cacheIdFor(objId, version);
589
+ let objFile = this.cache.get(cacheId);
590
+ if (!objFile) {
591
+ if (!path) { throw new Error(
592
+ `Path to file is not given, while instance of obj version file is not found in cache.`); }
593
+ objFile = await ObjVersionFile.forExisting(path)
594
+ .catch((exc: fs.FileException) => {
595
+ if (exc.notFound) { throw SC.OBJ_VER_UNKNOWN; }
596
+ throw exc;
597
+ });
598
+ this.cache.set(cacheId, objFile);
599
+ }
600
+ return objFile;
601
+ }
602
+
603
+ private cacheIdFor(objId: string|null, version: number): string {
604
+ return `${objId}:${version}`;
605
+ }
606
+
607
+ uncache = (objId: string|null, version: number): void => {
608
+ this.cache.delete(this.cacheIdFor(objId, version));
609
+ };
610
+
611
+ }
612
+ Object.freeze(ObjVerFiles.prototype);
613
+ Object.freeze(ObjVerFiles);
614
+
615
+
616
+ class ObjStatuses {
617
+
618
+ private cache: TimeWindowCache<string|null, ObjStatusInfo>;
619
+ private saveProcs = new NamedProcs();
620
+
621
+ constructor(
622
+ cachePeriodMillis: number,
623
+ public path: string
624
+ ) {
625
+ this.cache = new TimeWindowCache(cachePeriodMillis);
626
+ Object.freeze(this);
627
+ }
628
+
629
+ objFolder(objId: string|null): string {
630
+ return (objId ?
631
+ join(this.path, 'objects', objId) :
632
+ join(this.path, 'root'));
633
+ }
634
+
635
+ async get(objId: string|null): Promise<ObjStatusInfo> {
636
+ let status = this.cache.get(objId);
637
+ if (!status) {
638
+ status = await this.fromFile(objId);
639
+ this.cache.set(objId, status);
640
+ }
641
+ return status;
642
+ }
643
+
644
+ private async fromFile(objId: string|null): Promise<ObjStatusInfo> {
645
+ const filePath = join(this.objFolder(objId), STATUS_FILE);
646
+ const str = await fs.readFile(filePath, { encoding: 'utf8', flag: 'r' }).catch((exc: fs.FileException) => {
647
+ if (exc.notFound) { throw SC.OBJ_UNKNOWN; }
648
+ throw exc;
649
+ });
650
+ try {
651
+ return JSON.parse(str) as ObjStatusInfo;
652
+ } catch (err) {
653
+ throw errWithCause(err, `Can't parse content of obj status file`);
654
+ }
655
+ }
656
+
657
+ async set(objId: string|null, status: ObjStatusInfo): Promise<void> {
658
+ this.cache.set(objId, status);
659
+ await this.saveToFile(objId, status)
660
+ }
661
+
662
+ private async saveToFile(
663
+ objId: string|null, status: ObjStatusInfo
664
+ ): Promise<void> {
665
+ const filePath = join(this.objFolder(objId), STATUS_FILE);
666
+ const str = JSON.stringify(status);
667
+ const procId = (objId ? objId : 'null');
668
+ await this.saveProcs.startOrChain(procId, () => fs.writeFile(
669
+ filePath, str, { encoding: 'utf8', flag: 'w' }));
670
+ }
671
+
672
+ async deleteWithObj(objId: string|null): Promise<void> {
673
+ this.cache.delete(objId);
674
+ if (objId === null) {
675
+ await fs.unlink(join(this.objFolder(null), STATUS_FILE))
676
+ .catch(() => {});
677
+ } else {
678
+ const objFolder = this.objFolder(objId);
679
+ await fs.rmDirWithContent(objFolder).catch(() => {});
680
+ }
681
+ }
682
+
683
+ async makeNewObj(objId: string|null): Promise<void> {
684
+ if (objId === null) {
685
+ await this.get(objId).then(
686
+ () => { throw SC.OBJ_EXIST; },
687
+ (err) => { if (err !== SC.OBJ_UNKNOWN) { throw err; } });
688
+ } else {
689
+ // creation of folder ensures that id is unique
690
+ const objFolder = this.objFolder(objId);
691
+ await fs.mkdir(objFolder).catch((err: fs.FileException) => {
692
+ if (err.alreadyExists) { throw SC.OBJ_EXIST; }
693
+ else { throw err; }
694
+ });
695
+ }
696
+ await this.set(objId, {
697
+ state: 'new'
698
+ });
699
+ }
700
+
701
+ objFileReadingPath(objId: string|null, version: number): string {
702
+ return join(this.objFolder(objId), toFName(version));
703
+ }
704
+
705
+ }
706
+ Object.freeze(ObjStatuses.prototype);
707
+ Object.freeze(ObjStatuses);
708
+
709
+
710
+ class ObjTransactions {
711
+
712
+ private cache: TimeWindowCache<string|null, TransactionInfo>;
713
+ private saveProcs = new NamedProcs();
714
+
715
+ constructor(
716
+ cachePeriodMillis: number,
717
+ private statuses: ObjStatuses,
718
+ private path: string,
719
+ private uncacheFile: ObjVerFiles['uncache']
720
+ ) {
721
+ this.cache = new TimeWindowCache(cachePeriodMillis);
722
+ Object.freeze(this);
723
+ }
724
+
725
+ private transactionFolder(objId: string|null): string {
726
+ return (objId ?
727
+ join(this.path, 'transactions', objId) :
728
+ join(this.path, 'root', 'transaction'));
729
+ }
730
+
731
+ async get(
732
+ objId: string|null, transactionId?: string
733
+ ): Promise<TransactionInfo> {
734
+ let isInCache = true;
735
+ let trans = this.cache.get(objId);
736
+ if (!trans) {
737
+ isInCache = false;
738
+ trans = await this.fromFile(objId);
739
+ }
740
+ if (transactionId && (trans.transactionId !== transactionId)) {
741
+ throw SC.TRANSACTION_UNKNOWN;
742
+ }
743
+ if (isInCache) {
744
+ this.cache.set(objId, trans);
745
+ }
746
+ return trans;
747
+ }
748
+
749
+ private transactionFilePath(objId: string|null): string {
750
+ return join(this.transactionFolder(objId), 'transaction');
751
+ }
752
+
753
+ private async fromFile(objId: string|null): Promise<TransactionInfo> {
754
+ const filePath = this.transactionFilePath(objId);
755
+ const str = await fs.readFile(filePath, { encoding: 'utf8', flag: 'r' })
756
+ .catch(async (exc: fs.FileException) => {
757
+ if (exc.notFound) {
758
+ // it may be due to obj missing, in which case the following
759
+ // throws respective error
760
+ await this.statuses.get(objId);
761
+ // or just missing transaction file
762
+ throw SC.TRANSACTION_UNKNOWN;
763
+ }
764
+ throw exc;
765
+ });
766
+ try {
767
+ return JSON.parse(str) as TransactionInfo;
768
+ } catch (err) {
769
+ throw errWithCause(err, `Can't parse content of obj transaction file`);
770
+ }
771
+ }
772
+
773
+ async set(objId: string|null, status: TransactionInfo): Promise<void> {
774
+ this.cache.set(objId, status);
775
+ await this.saveToFile(objId, status)
776
+ }
777
+
778
+ private async saveToFile(
779
+ objId: string|null, transaction: TransactionInfo
780
+ ): Promise<void> {
781
+ const filePath = this.transactionFilePath(objId);
782
+ const str = JSON.stringify(transaction);
783
+ const procId = (objId ? objId : 'null');
784
+ await this.saveProcs.startOrChain(procId, () => fs.writeFile(
785
+ filePath, str, { encoding: 'utf8', flag: 'w' }));
786
+ }
787
+
788
+ objFileWritingPath(objId: string|null): string {
789
+ return join(this.transactionFolder(objId), 'new');
790
+ }
791
+
792
+ async startNew(
793
+ objId: string|null, reqTrans: TransactionParams
794
+ ): Promise<TransactionInfo> {
795
+ const trans = reqTrans as TransactionInfo;
796
+ trans.transactionId = await stringOfB64UrlSafeChars(10);
797
+ trans.transactionType = 'write';
798
+ await this.makeTransactionFolder(objId);
799
+ await this.set(objId, trans);
800
+ try {
801
+
802
+ // check object status
803
+ if (trans.isNewObj) {
804
+ await this.statuses.makeNewObj(objId);
805
+ } else {
806
+ // get current version, and check new one against it
807
+ const status = await this.statuses.get(objId);
808
+ if (status.state !== 'current') {
809
+ throw SC.OBJ_UNKNOWN;
810
+ } else if (trans.version !== (status.currentVersion! + 1)) {
811
+ throw makeMismatchedObjVerException(status.currentVersion!);
812
+ }
813
+ }
814
+
815
+ } catch (err) {
816
+ await this.cancel(objId, trans.transactionId).catch(() => {});
817
+ throw err;
818
+ }
819
+ return trans;
820
+ }
821
+
822
+ private async makeTransactionFolder(objId: string|null): Promise<void> {
823
+ try {
824
+ await fs.mkdir(this.transactionFolder(objId));
825
+ } catch (err) {
826
+ if ((<fs.FileException> err).alreadyExists) {
827
+ throw SC.CONCURRENT_TRANSACTION;
828
+ }
829
+ throw err;
830
+ }
831
+ }
832
+
833
+ async cancel(objId: string|null, transactionId?: string): Promise<void> {
834
+
835
+ // XXX implement assurance that canceling without transaction id
836
+ // happens only after a certain timeout, i.e. as a method to
837
+ // remove deadlock, when time determines that there is a deadlock
838
+
839
+ const trans = await this.get(objId).catch(
840
+ async (exc: string) => {
841
+ if (exc === SC.TRANSACTION_UNKNOWN) { return null; }
842
+ else { throw exc; }
843
+ });
844
+ if (trans && trans.isNewObj) {
845
+ this.statuses.deleteWithObj(objId);
846
+ }
847
+ this.cache.delete(objId);
848
+ const transFolder = this.transactionFolder(objId);
849
+ await fs.rmDirWithContent(transFolder).catch(() => {});
850
+ }
851
+
852
+ async complete(
853
+ objId: string|null, trans: TransactionInfo, newVerFile: ObjVersionFile
854
+ ): Promise<void> {
855
+ this.cache.delete(objId);
856
+ // move new file from transaction folder to obj's one
857
+ const newVerPath = this.statuses.objFileReadingPath(objId, trans.version);
858
+ await newVerFile.moveFile(newVerPath);
859
+ // update object status
860
+ const status = await this.statuses.get(objId);
861
+ if (status.state === 'current') {
862
+ // if needed, remove previous version, right after exposing a new one
863
+ const verToDel = (status.archivedVersions &&
864
+ (status.archivedVersions.indexOf(
865
+ status.currentVersion as number) >= 0)) ?
866
+ undefined : status.currentVersion;
867
+ status.currentVersion = trans.version;
868
+ await this.statuses.set(objId, status);
869
+ if ((verToDel !== undefined) && (trans.baseVersion !== verToDel)) {
870
+ this.uncacheFile(objId, verToDel);
871
+ const rmPath = this.statuses.objFileReadingPath(objId, verToDel);
872
+ await fs.unlink(rmPath);
873
+ }
874
+ } else if (status.state === 'new') {
875
+ status.state = 'current';
876
+ status.currentVersion = trans.version;
877
+ await this.statuses.set(objId, status);
878
+ } else {
879
+ throw new Error(`Object ${objId} has unexpected for transaction completion state: ${status.state}`);
880
+ }
881
+ await fs.rmDirWithContent(this.transactionFolder(objId)).catch(() => {});
882
+ }
883
+
884
+ }
885
+ Object.freeze(ObjStatuses.prototype);
886
+ Object.freeze(ObjStatuses);
887
+
888
+
889
+ function toFName(version: number): string {
890
+ return `${version}.v`;
891
+ }
892
+
893
+ function noop () {}
894
+
895
+
896
+ Object.freeze(exports);