@juzi/file-box 1.7.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 (210) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +613 -0
  3. package/dist/cjs/package.json +3 -0
  4. package/dist/cjs/src/config.d.ts +4 -0
  5. package/dist/cjs/src/config.d.ts.map +1 -0
  6. package/dist/cjs/src/config.js +9 -0
  7. package/dist/cjs/src/config.js.map +1 -0
  8. package/dist/cjs/src/file-box.d.ts +209 -0
  9. package/dist/cjs/src/file-box.d.ts.map +1 -0
  10. package/dist/cjs/src/file-box.js +804 -0
  11. package/dist/cjs/src/file-box.js.map +1 -0
  12. package/dist/cjs/src/file-box.spec.d.ts +14 -0
  13. package/dist/cjs/src/file-box.spec.d.ts.map +1 -0
  14. package/dist/cjs/src/file-box.spec.js +393 -0
  15. package/dist/cjs/src/file-box.spec.js.map +1 -0
  16. package/dist/cjs/src/file-box.type.d.ts +103 -0
  17. package/dist/cjs/src/file-box.type.d.ts.map +1 -0
  18. package/dist/cjs/src/file-box.type.js +34 -0
  19. package/dist/cjs/src/file-box.type.js.map +1 -0
  20. package/dist/cjs/src/interface.d.ts +25 -0
  21. package/dist/cjs/src/interface.d.ts.map +1 -0
  22. package/dist/cjs/src/interface.js +3 -0
  23. package/dist/cjs/src/interface.js.map +1 -0
  24. package/dist/cjs/src/interface.spec.d.ts +3 -0
  25. package/dist/cjs/src/interface.spec.d.ts.map +1 -0
  26. package/dist/cjs/src/interface.spec.js +11 -0
  27. package/dist/cjs/src/interface.spec.js.map +1 -0
  28. package/dist/cjs/src/misc.d.ts +17 -0
  29. package/dist/cjs/src/misc.d.ts.map +1 -0
  30. package/dist/cjs/src/misc.js +137 -0
  31. package/dist/cjs/src/misc.js.map +1 -0
  32. package/dist/cjs/src/misc.spec.d.ts +3 -0
  33. package/dist/cjs/src/misc.spec.d.ts.map +1 -0
  34. package/dist/cjs/src/misc.spec.js +51 -0
  35. package/dist/cjs/src/misc.spec.js.map +1 -0
  36. package/dist/cjs/src/mod.d.ts +9 -0
  37. package/dist/cjs/src/mod.d.ts.map +1 -0
  38. package/dist/cjs/src/mod.js +12 -0
  39. package/dist/cjs/src/mod.js.map +1 -0
  40. package/dist/cjs/src/pure-functions/sized-chunk-transformer.d.ts +13 -0
  41. package/dist/cjs/src/pure-functions/sized-chunk-transformer.d.ts.map +1 -0
  42. package/dist/cjs/src/pure-functions/sized-chunk-transformer.js +45 -0
  43. package/dist/cjs/src/pure-functions/sized-chunk-transformer.js.map +1 -0
  44. package/dist/cjs/src/pure-functions/sized-chunk-transformer.spec.d.ts +3 -0
  45. package/dist/cjs/src/pure-functions/sized-chunk-transformer.spec.d.ts.map +1 -0
  46. package/dist/cjs/src/pure-functions/sized-chunk-transformer.spec.js +39 -0
  47. package/dist/cjs/src/pure-functions/sized-chunk-transformer.spec.js.map +1 -0
  48. package/dist/cjs/src/qrcode.d.ts +6 -0
  49. package/dist/cjs/src/qrcode.d.ts.map +1 -0
  50. package/dist/cjs/src/qrcode.js +35 -0
  51. package/dist/cjs/src/qrcode.js.map +1 -0
  52. package/dist/cjs/src/qrcode.spec.d.ts +3 -0
  53. package/dist/cjs/src/qrcode.spec.d.ts.map +1 -0
  54. package/dist/cjs/src/qrcode.spec.js +47 -0
  55. package/dist/cjs/src/qrcode.spec.js.map +1 -0
  56. package/dist/cjs/src/urn-registry/mod.d.ts +3 -0
  57. package/dist/cjs/src/urn-registry/mod.d.ts.map +1 -0
  58. package/dist/cjs/src/urn-registry/mod.js +6 -0
  59. package/dist/cjs/src/urn-registry/mod.js.map +1 -0
  60. package/dist/cjs/src/urn-registry/random-uuid.d.ts +4 -0
  61. package/dist/cjs/src/urn-registry/random-uuid.d.ts.map +1 -0
  62. package/dist/cjs/src/urn-registry/random-uuid.js +30 -0
  63. package/dist/cjs/src/urn-registry/random-uuid.js.map +1 -0
  64. package/dist/cjs/src/urn-registry/uniform-resource-name-registry.d.ts +76 -0
  65. package/dist/cjs/src/urn-registry/uniform-resource-name-registry.d.ts.map +1 -0
  66. package/dist/cjs/src/urn-registry/uniform-resource-name-registry.js +259 -0
  67. package/dist/cjs/src/urn-registry/uniform-resource-name-registry.js.map +1 -0
  68. package/dist/cjs/src/urn-registry/uniform-resource-name-registry.spec.d.ts +3 -0
  69. package/dist/cjs/src/urn-registry/uniform-resource-name-registry.spec.d.ts.map +1 -0
  70. package/dist/cjs/src/urn-registry/uniform-resource-name-registry.spec.js +81 -0
  71. package/dist/cjs/src/urn-registry/uniform-resource-name-registry.spec.js.map +1 -0
  72. package/dist/cjs/src/urn-registry/uuid-to-big-int.d.ts +8 -0
  73. package/dist/cjs/src/urn-registry/uuid-to-big-int.d.ts.map +1 -0
  74. package/dist/cjs/src/urn-registry/uuid-to-big-int.js +16 -0
  75. package/dist/cjs/src/urn-registry/uuid-to-big-int.js.map +1 -0
  76. package/dist/cjs/src/urn-registry/uuid-to-big-int.spec.d.ts +3 -0
  77. package/dist/cjs/src/urn-registry/uuid-to-big-int.spec.d.ts.map +1 -0
  78. package/dist/cjs/src/urn-registry/uuid-to-big-int.spec.js +17 -0
  79. package/dist/cjs/src/urn-registry/uuid-to-big-int.spec.js.map +1 -0
  80. package/dist/cjs/src/version.d.ts +5 -0
  81. package/dist/cjs/src/version.d.ts.map +1 -0
  82. package/dist/cjs/src/version.js +8 -0
  83. package/dist/cjs/src/version.js.map +1 -0
  84. package/dist/cjs/src/version.spec.d.ts +3 -0
  85. package/dist/cjs/src/version.spec.d.ts.map +1 -0
  86. package/dist/cjs/src/version.spec.js +9 -0
  87. package/dist/cjs/src/version.spec.js.map +1 -0
  88. package/dist/cjs/tests/integration.spec.d.ts +3 -0
  89. package/dist/cjs/tests/integration.spec.d.ts.map +1 -0
  90. package/dist/cjs/tests/integration.spec.js +8 -0
  91. package/dist/cjs/tests/integration.spec.js.map +1 -0
  92. package/dist/cjs/tests/network-timeout.spec.d.ts +3 -0
  93. package/dist/cjs/tests/network-timeout.spec.d.ts.map +1 -0
  94. package/dist/cjs/tests/network-timeout.spec.js +121 -0
  95. package/dist/cjs/tests/network-timeout.spec.js.map +1 -0
  96. package/dist/esm/src/config.d.ts +4 -0
  97. package/dist/esm/src/config.d.ts.map +1 -0
  98. package/dist/esm/src/config.js +5 -0
  99. package/dist/esm/src/config.js.map +1 -0
  100. package/dist/esm/src/file-box.d.ts +209 -0
  101. package/dist/esm/src/file-box.d.ts.map +1 -0
  102. package/dist/esm/src/file-box.js +775 -0
  103. package/dist/esm/src/file-box.js.map +1 -0
  104. package/dist/esm/src/file-box.spec.d.ts +14 -0
  105. package/dist/esm/src/file-box.spec.d.ts.map +1 -0
  106. package/dist/esm/src/file-box.spec.js +386 -0
  107. package/dist/esm/src/file-box.spec.js.map +1 -0
  108. package/dist/esm/src/file-box.type.d.ts +103 -0
  109. package/dist/esm/src/file-box.type.d.ts.map +1 -0
  110. package/dist/esm/src/file-box.type.js +31 -0
  111. package/dist/esm/src/file-box.type.js.map +1 -0
  112. package/dist/esm/src/interface.d.ts +25 -0
  113. package/dist/esm/src/interface.d.ts.map +1 -0
  114. package/dist/esm/src/interface.js +2 -0
  115. package/dist/esm/src/interface.js.map +1 -0
  116. package/dist/esm/src/interface.spec.d.ts +3 -0
  117. package/dist/esm/src/interface.spec.d.ts.map +1 -0
  118. package/dist/esm/src/interface.spec.js +9 -0
  119. package/dist/esm/src/interface.spec.js.map +1 -0
  120. package/dist/esm/src/misc.d.ts +17 -0
  121. package/dist/esm/src/misc.d.ts.map +1 -0
  122. package/dist/esm/src/misc.js +126 -0
  123. package/dist/esm/src/misc.js.map +1 -0
  124. package/dist/esm/src/misc.spec.d.ts +3 -0
  125. package/dist/esm/src/misc.spec.d.ts.map +1 -0
  126. package/dist/esm/src/misc.spec.js +49 -0
  127. package/dist/esm/src/misc.spec.js.map +1 -0
  128. package/dist/esm/src/mod.d.ts +9 -0
  129. package/dist/esm/src/mod.d.ts.map +1 -0
  130. package/dist/esm/src/mod.js +6 -0
  131. package/dist/esm/src/mod.js.map +1 -0
  132. package/dist/esm/src/pure-functions/sized-chunk-transformer.d.ts +13 -0
  133. package/dist/esm/src/pure-functions/sized-chunk-transformer.d.ts.map +1 -0
  134. package/dist/esm/src/pure-functions/sized-chunk-transformer.js +39 -0
  135. package/dist/esm/src/pure-functions/sized-chunk-transformer.js.map +1 -0
  136. package/dist/esm/src/pure-functions/sized-chunk-transformer.spec.d.ts +3 -0
  137. package/dist/esm/src/pure-functions/sized-chunk-transformer.spec.d.ts.map +1 -0
  138. package/dist/esm/src/pure-functions/sized-chunk-transformer.spec.js +37 -0
  139. package/dist/esm/src/pure-functions/sized-chunk-transformer.spec.js.map +1 -0
  140. package/dist/esm/src/qrcode.d.ts +6 -0
  141. package/dist/esm/src/qrcode.d.ts.map +1 -0
  142. package/dist/esm/src/qrcode.js +27 -0
  143. package/dist/esm/src/qrcode.js.map +1 -0
  144. package/dist/esm/src/qrcode.spec.d.ts +3 -0
  145. package/dist/esm/src/qrcode.spec.d.ts.map +1 -0
  146. package/dist/esm/src/qrcode.spec.js +45 -0
  147. package/dist/esm/src/qrcode.spec.js.map +1 -0
  148. package/dist/esm/src/urn-registry/mod.d.ts +3 -0
  149. package/dist/esm/src/urn-registry/mod.d.ts.map +1 -0
  150. package/dist/esm/src/urn-registry/mod.js +3 -0
  151. package/dist/esm/src/urn-registry/mod.js.map +1 -0
  152. package/dist/esm/src/urn-registry/random-uuid.d.ts +4 -0
  153. package/dist/esm/src/urn-registry/random-uuid.d.ts.map +1 -0
  154. package/dist/esm/src/urn-registry/random-uuid.js +4 -0
  155. package/dist/esm/src/urn-registry/random-uuid.js.map +1 -0
  156. package/dist/esm/src/urn-registry/uniform-resource-name-registry.d.ts +76 -0
  157. package/dist/esm/src/urn-registry/uniform-resource-name-registry.d.ts.map +1 -0
  158. package/dist/esm/src/urn-registry/uniform-resource-name-registry.js +253 -0
  159. package/dist/esm/src/urn-registry/uniform-resource-name-registry.js.map +1 -0
  160. package/dist/esm/src/urn-registry/uniform-resource-name-registry.spec.d.ts +3 -0
  161. package/dist/esm/src/urn-registry/uniform-resource-name-registry.spec.d.ts.map +1 -0
  162. package/dist/esm/src/urn-registry/uniform-resource-name-registry.spec.js +79 -0
  163. package/dist/esm/src/urn-registry/uniform-resource-name-registry.spec.js.map +1 -0
  164. package/dist/esm/src/urn-registry/uuid-to-big-int.d.ts +8 -0
  165. package/dist/esm/src/urn-registry/uuid-to-big-int.d.ts.map +1 -0
  166. package/dist/esm/src/urn-registry/uuid-to-big-int.js +13 -0
  167. package/dist/esm/src/urn-registry/uuid-to-big-int.js.map +1 -0
  168. package/dist/esm/src/urn-registry/uuid-to-big-int.spec.d.ts +3 -0
  169. package/dist/esm/src/urn-registry/uuid-to-big-int.spec.d.ts.map +1 -0
  170. package/dist/esm/src/urn-registry/uuid-to-big-int.spec.js +15 -0
  171. package/dist/esm/src/urn-registry/uuid-to-big-int.spec.js.map +1 -0
  172. package/dist/esm/src/version.d.ts +5 -0
  173. package/dist/esm/src/version.d.ts.map +1 -0
  174. package/dist/esm/src/version.js +5 -0
  175. package/dist/esm/src/version.js.map +1 -0
  176. package/dist/esm/src/version.spec.d.ts +3 -0
  177. package/dist/esm/src/version.spec.d.ts.map +1 -0
  178. package/dist/esm/src/version.spec.js +7 -0
  179. package/dist/esm/src/version.spec.js.map +1 -0
  180. package/dist/esm/tests/integration.spec.d.ts +3 -0
  181. package/dist/esm/tests/integration.spec.d.ts.map +1 -0
  182. package/dist/esm/tests/integration.spec.js +6 -0
  183. package/dist/esm/tests/integration.spec.js.map +1 -0
  184. package/dist/esm/tests/network-timeout.spec.d.ts +3 -0
  185. package/dist/esm/tests/network-timeout.spec.d.ts.map +1 -0
  186. package/dist/esm/tests/network-timeout.spec.js +119 -0
  187. package/dist/esm/tests/network-timeout.spec.js.map +1 -0
  188. package/package.json +85 -0
  189. package/src/config.ts +5 -0
  190. package/src/file-box.spec.ts +480 -0
  191. package/src/file-box.ts +1042 -0
  192. package/src/file-box.type.ts +145 -0
  193. package/src/interface.spec.ts +17 -0
  194. package/src/interface.ts +47 -0
  195. package/src/misc.spec.ts +70 -0
  196. package/src/misc.ts +157 -0
  197. package/src/mod.ts +33 -0
  198. package/src/pure-functions/sized-chunk-transformer.spec.ts +52 -0
  199. package/src/pure-functions/sized-chunk-transformer.ts +49 -0
  200. package/src/qrcode.spec.ts +55 -0
  201. package/src/qrcode.ts +38 -0
  202. package/src/typings.d.ts +1 -0
  203. package/src/urn-registry/mod.ts +7 -0
  204. package/src/urn-registry/random-uuid.ts +7 -0
  205. package/src/urn-registry/uniform-resource-name-registry.spec.ts +109 -0
  206. package/src/urn-registry/uniform-resource-name-registry.ts +342 -0
  207. package/src/urn-registry/uuid-to-big-int.spec.ts +19 -0
  208. package/src/urn-registry/uuid-to-big-int.ts +16 -0
  209. package/src/version.spec.ts +9 -0
  210. package/src/version.ts +4 -0
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env -S node --no-warnings --loader ts-node/esm
2
+
3
+ import {
4
+ test,
5
+ sinon,
6
+ } from 'tstest'
7
+
8
+ import { FileBox } from '../file-box.js'
9
+
10
+ import { UniformResourceNameRegistry } from './uniform-resource-name-registry.js'
11
+
12
+ test('UniformResourceNameRegistry class', async t => {
13
+ const QRCODE = 'test qrcode'
14
+
15
+ const urnRegistry = new UniformResourceNameRegistry()
16
+ await urnRegistry.init()
17
+
18
+ const fileBox = FileBox.fromQRCode(QRCODE)
19
+ const stream = await fileBox.toStream()
20
+
21
+ const uuid = await urnRegistry.save(stream)
22
+
23
+ const stream2 = await urnRegistry.load(uuid)
24
+ t.ok(stream2, 'should load stream')
25
+
26
+ const fileBox2 = FileBox.fromStream(stream2!, 'test')
27
+ const qrcode = await fileBox2.toQRCode()
28
+
29
+ t.equal(qrcode, QRCODE, 'should get back the qrcode data')
30
+
31
+ await t.resolves(() => urnRegistry.load(uuid), 'should not reject when load a UUID again')
32
+
33
+ await urnRegistry.destroy()
34
+ })
35
+
36
+ test('expireMilliseconds: in time', async t => {
37
+ const expireMilliseconds = 3
38
+ const urnRegistry = new UniformResourceNameRegistry({
39
+ expireMilliseconds,
40
+ })
41
+ await urnRegistry.init()
42
+
43
+ const uuid = await urnRegistry.save(await FileBox.fromQRCode('qr').toStream())
44
+ await new Promise(resolve => setTimeout(resolve, 1))
45
+ await t.resolves(() => urnRegistry.load(uuid), `should not expire after 1ms (with ${expireMilliseconds}ms expire)`)
46
+
47
+ await urnRegistry.destroy()
48
+ })
49
+
50
+ test('expireMilliseconds: time out', async t => {
51
+ const sandbox = sinon.createSandbox({
52
+ useFakeTimers: true,
53
+ })
54
+
55
+ const expireMilliseconds = 10 * 60 * 1000 // 10 minute
56
+ const urnRegistry = new UniformResourceNameRegistry({
57
+ expireMilliseconds,
58
+ })
59
+ await urnRegistry.init()
60
+
61
+ /**
62
+ * Time: 0
63
+ */
64
+ const uuid1 = await urnRegistry.save(await FileBox.fromQRCode('qr').toStream())
65
+ await t.resolves(() => urnRegistry.load(uuid1), 'should load uuid1 successfully')
66
+
67
+ /**
68
+ * Time: 5
69
+ */
70
+ await sandbox.clock.tickAsync(5 * 60 * 1000)
71
+ const uuid2 = await urnRegistry.save(await FileBox.fromQRCode('qr2').toStream())
72
+ await t.resolves(() => urnRegistry.load(uuid1), 'should load uuid1 successfully after 5 minutes')
73
+ await t.resolves(() => urnRegistry.load(uuid2), 'should load uuid2 successfully')
74
+
75
+ /**
76
+ * Time: 11
77
+ */
78
+ await sandbox.clock.tickAsync(6 * 60 * 1000)
79
+ await t.rejects(() => urnRegistry.load(uuid1), 'should load uuid1 fail because it is expired')
80
+ await t.resolves(() => urnRegistry.load(uuid2), 'should load uuid2 successfully 6 minutes after it has been saved')
81
+
82
+ /**
83
+ * Time 20
84
+ */
85
+ await sandbox.clock.tickAsync(9 * 60 * 1000)
86
+ await t.rejects(() => urnRegistry.load(uuid2), 'should load uuid2 fail because it is expired')
87
+
88
+ await urnRegistry.destroy()
89
+ sandbox.restore()
90
+ })
91
+
92
+ test('URN FileBox helper smoke testing', async t => {
93
+ const QRCODE = 'test qrcode'
94
+
95
+ const urnRegistry = new UniformResourceNameRegistry()
96
+
97
+ const UUIDFileBox = urnRegistry.getFileBox()
98
+
99
+ const uuid = await UUIDFileBox
100
+ .fromQRCode(QRCODE)
101
+ .toUuid()
102
+
103
+ const qrcode = await UUIDFileBox
104
+ .fromUuid(uuid)
105
+ .toQRCode()
106
+
107
+ t.equal(qrcode, QRCODE, 'should get back the qrcode data')
108
+ urnRegistry.destroy()
109
+ })
@@ -0,0 +1,342 @@
1
+ /**
2
+ * Huan(202110): Assignment and Resolution of Uniform Resource Names
3
+ * https://datatracker.ietf.org/wg/urn/about/
4
+ */
5
+
6
+ /**
7
+ * RFC 4122: A Universally Unique IDentifier (UUID) URN Namespace
8
+ * ------------------------------------------------------------
9
+ * This specification defines a Uniform Resource Name namespace for
10
+ * UUIDs (Universally Unique IDentifier), also known as GUIDs (Globally
11
+ * Unique IDentifier). A UUID is 128 bits long, and can guarantee
12
+ * uniqueness across space and time. UUIDs were originally used in the
13
+ * Apollo Network Computing System and later in the Open Software
14
+ * Foundation's (OSF) Distributed Computing Environment (DCE), and then
15
+ * in Microsoft Windows platforms.
16
+ *
17
+ * The information here is meant to be a concise guide for those wishing
18
+ * to implement services using UUIDs as URNs. Nothing in this document
19
+ * should be construed to override the DCE standards that defined UUIDs.
20
+ */
21
+
22
+ /**
23
+ * RFC 2141: Uniform Resource Names (URNs) Syntax
24
+ * ----------------------------------------------
25
+ * Uniform Resource Names (URNs) are intended to serve as persistent,
26
+ * location-independent, resource identifiers. This document sets
27
+ * forward the canonical syntax for URNs. A discussion of both existing
28
+ * legacy and new namespaces and requirements for URN presentation and
29
+ * transmission are presented. Finally, there is a discussion of URN
30
+ * equivalence and how to determine it.
31
+ */
32
+ import fs from 'fs'
33
+ import os from 'os'
34
+ import path from 'path'
35
+
36
+ import type { Readable } from 'stream'
37
+
38
+ import { instanceToClass } from 'clone-class'
39
+ import { log } from 'brolog'
40
+
41
+ import {
42
+ FileBox,
43
+ } from '../file-box.js'
44
+
45
+ import {
46
+ randomUuid,
47
+ } from './random-uuid.js'
48
+
49
+ /**
50
+ * A UUID will be only keep for a certain time.
51
+ */
52
+ const DEFAULT_UUID_EXPIRE_MINUTES = 30
53
+ const DEFAULT_UUID_PURGE_INTERVAL_MINUTES = 1
54
+
55
+ interface UniformResourceNameRegistryOptions {
56
+ expireMilliseconds? : number,
57
+ storeDir? : string,
58
+ }
59
+
60
+ class UniformResourceNameRegistry {
61
+
62
+ protected static processExitMap = new WeakMap<
63
+ UniformResourceNameRegistry,
64
+ Function
65
+ >()
66
+
67
+ /**
68
+ * The directory that store all UUID files
69
+ */
70
+ protected storeDir: string
71
+
72
+ protected expireMilliseconds: number
73
+
74
+ /**
75
+ * Key: expiretime
76
+ * Value: the array of UUID that will expires after the `expiretime` (Key)
77
+ */
78
+ protected uuidExpiringTable: Map<
79
+ number,
80
+ string[]
81
+ >
82
+
83
+ protected purgerTimer?: ReturnType<typeof setInterval>
84
+
85
+ constructor (
86
+ options: UniformResourceNameRegistryOptions = {},
87
+ ) {
88
+ log.verbose('UniformResourceNameRegistry', 'constructor("%s")', JSON.stringify(options))
89
+
90
+ this.uuidExpiringTable = new Map()
91
+
92
+ this.expireMilliseconds = options.expireMilliseconds ?? (DEFAULT_UUID_EXPIRE_MINUTES * 60 * 1000 * 1000)
93
+ this.storeDir = options.storeDir || path.join(
94
+ os.tmpdir(),
95
+ 'file-box-urn-registry.' + String(process.pid),
96
+ )
97
+ }
98
+
99
+ /**
100
+ * Return a FileBox Interface with the current URN Registry for conience
101
+ */
102
+ getFileBox (): typeof FileBox {
103
+ this.init()
104
+
105
+ class UUIDFileBox extends FileBox {}
106
+ UUIDFileBox.setUuidLoader(this.load.bind(this))
107
+ UUIDFileBox.setUuidSaver(this.save.bind(this))
108
+ return UUIDFileBox
109
+ }
110
+
111
+ /**
112
+ * init the UUID registry
113
+ *
114
+ * must be called before use.
115
+ */
116
+ init () {
117
+ log.verbose('UniformResourceNameRegistry', 'init()')
118
+
119
+ try {
120
+ const stat = fs.statSync(this.storeDir)
121
+ if (!stat.isDirectory()) {
122
+ throw new Error(this.storeDir + ' is Not a directory')
123
+ }
124
+ } catch (e) {
125
+ if ((e as NodeJS.ErrnoException).code === 'ENOENT') {
126
+ fs.mkdirSync(this.storeDir, { recursive: true })
127
+ } else {
128
+ throw e
129
+ }
130
+ }
131
+
132
+ if (!this.purgerTimer) {
133
+ this.addProcessExitListener()
134
+
135
+ this.purgerTimer = setInterval(
136
+ () => this.purgeExpiredUuid(),
137
+ DEFAULT_UUID_PURGE_INTERVAL_MINUTES * 60 * 1000,
138
+ )
139
+ }
140
+
141
+ }
142
+
143
+ protected purgeExpiredUuid () {
144
+ log.verbose('UniformResourceNameRegistry', 'purgeExpiredUuid()')
145
+ const expireTimeList = [ ...this.uuidExpiringTable.keys() ]
146
+ .sort((a, b) => Number(a) - Number(b))
147
+
148
+ for (const expireTime of expireTimeList) {
149
+ if (Date.now() < expireTime) {
150
+ // The earliest expire time is in the future
151
+ break
152
+ }
153
+
154
+ const uuidList = this.uuidExpiringTable.get(expireTime) || []
155
+ this.uuidExpiringTable.delete(expireTime)
156
+
157
+ for (const uuid of uuidList) {
158
+ this.purge(uuid).catch(console.error)
159
+ }
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Clean up by calling this.destroy() before process exit
165
+ */
166
+ protected addProcessExitListener () {
167
+ log.verbose('UniformResourceNameRegistry', 'addProcessExitListener()')
168
+
169
+ const Klass = instanceToClass(this, UniformResourceNameRegistry)
170
+
171
+ /**
172
+ * If we have already registered the listener, do nothing.
173
+ */
174
+ if (Klass.processExitMap.has(this)) {
175
+ return
176
+ }
177
+
178
+ const destroyCallback = () => this.destroy()
179
+
180
+ process.addListener('exit', destroyCallback)
181
+ Klass.processExitMap.set(
182
+ this,
183
+ () => process.removeListener('exit', destroyCallback),
184
+ )
185
+ }
186
+
187
+ protected uuidFileName (uuid: string): string {
188
+ return path.join(
189
+ this.storeDir,
190
+ uuid + '.dat',
191
+ )
192
+ }
193
+
194
+ /**
195
+ * @deprecated use `load()` instead
196
+ */
197
+ async resolve (uuid: string): Promise<Readable> {
198
+ log.warn('UniformResourceNameRegistry', 'resolve() is deprecated: use `load()` instead.\n%s', new Error().stack)
199
+ return this.load(uuid)
200
+ }
201
+
202
+ /**
203
+ * `resolve()` can only be used once.
204
+ * after resolve(), the UUID will be not exist any more
205
+ */
206
+ async load (uuid: string): Promise<Readable> {
207
+ log.verbose('UniformResourceNameRegistry', 'load(%s)', uuid)
208
+
209
+ const filename = this.uuidFileName(uuid)
210
+ const stream = fs.createReadStream(filename)
211
+
212
+ await new Promise<void>((resolve, reject) => {
213
+ stream.on('ready', resolve)
214
+ stream.on('error', reject)
215
+ })
216
+
217
+ return stream
218
+ }
219
+
220
+ /**
221
+ * @deprecated use `save()` instead
222
+ */
223
+ async register (stream: Readable): Promise<string> {
224
+ log.verbose('UniformResourceNameRegistry', 'register() deprecated: use save() instead.\n%s', new Error().stack)
225
+ return this.save(stream)
226
+ }
227
+
228
+ /**
229
+ * Save the `Readable` stream and return a random UUID
230
+ * The UUID will be expired after MAX_KEEP_MINUTES
231
+ */
232
+ async save (stream: Readable): Promise<string> {
233
+ log.verbose('UniformResourceNameRegistry', 'save(stream)')
234
+
235
+ const uuid = randomUuid()
236
+
237
+ const fileStream = fs.createWriteStream(this.uuidFileName(uuid))
238
+ const future = new Promise<void>((resolve, reject) => {
239
+ stream.on('end', resolve)
240
+ stream.on('error', reject)
241
+ fileStream.on('error', reject)
242
+ })
243
+ stream.pipe(fileStream)
244
+ await future
245
+
246
+ this.addToExpiringTable(uuid)
247
+
248
+ return uuid
249
+ }
250
+
251
+ /**
252
+ * Set a timer to execute delete callback after `expireMilliseconds`
253
+ */
254
+ protected addToExpiringTable (uuid: string): void {
255
+ log.verbose('UniformResourceNameRegistry', 'addToExpiringTable(%s)', uuid)
256
+
257
+ const expireTime = Date.now() + this.expireMilliseconds
258
+ const expireTimeInterval = DEFAULT_UUID_PURGE_INTERVAL_MINUTES * 60 * 1000
259
+
260
+ // https://stackoverflow.com/a/22687090
261
+ const expireTimeNearestMinute = Math.ceil(expireTime / expireTimeInterval) * expireTimeInterval
262
+
263
+ const uuidList = this.uuidExpiringTable.get(expireTime) || []
264
+ uuidList.push(uuid)
265
+ this.uuidExpiringTable.set(expireTimeNearestMinute, uuidList)
266
+
267
+ log.silly('UniformResourceNameRegistry', 'addToExpiringTable() uuidList.length = %s, expireTime = %s',
268
+ uuidList.length,
269
+ expireTimeNearestMinute,
270
+ )
271
+ }
272
+
273
+ protected async purge (uuid: string): Promise<void> {
274
+ log.verbose('UniformResourceNameRegistry', 'purge(%s)', uuid)
275
+
276
+ const file = this.uuidFileName(uuid)
277
+ try {
278
+ await fs.promises.unlink(file)
279
+ log.silly('UniformResourceNameRegistry', 'purge() %s', file)
280
+ } catch (e) {
281
+ log.warn('UniformResourceNameRegistry', 'purge() rejection:', (e as Error).message)
282
+ }
283
+ }
284
+
285
+ /**
286
+ * destroy the urn registry.
287
+ *
288
+ * This function will be called automatically at `process.on(exit)`
289
+ * however, it till need to be called before the program ends
290
+ * because there have some timers in eventloop task list
291
+ */
292
+ destroy () {
293
+ log.verbose('UniformResourceNameRegistry', 'destroy() %s UUIDs left',
294
+ [ ...this.uuidExpiringTable.values() ].flat().length,
295
+ )
296
+
297
+ if (this.purgerTimer) {
298
+ log.verbose('UniformResourceNameRegistry', 'destroy() clearing purger timer ...')
299
+ clearInterval(this.purgerTimer)
300
+ this.purgerTimer = undefined
301
+ }
302
+
303
+ const Klass = instanceToClass(this, UniformResourceNameRegistry)
304
+
305
+ /**
306
+ * Remove process exit listener
307
+ */
308
+ if (Klass.processExitMap.has(this)) {
309
+ log.verbose('UniformResourceNameRegistry', 'destroy() remove process `exit` listener ...')
310
+ const fn = Klass.processExitMap.get(this)
311
+ Klass.processExitMap.delete(this)
312
+ fn && fn()
313
+ }
314
+
315
+ /**
316
+ * Clean up all the files
317
+ */
318
+ log.verbose('UniformResourceNameRegistry', 'destroy() fs.rmSync(%s) ...', this.storeDir)
319
+ try {
320
+ /**
321
+ * Huan(202110):
322
+ * Check for the `this.uuidDir` exist or not
323
+ * when we are running unit tests, we might instanciate multiple UniformResourceNameRegistry
324
+ * which will cause the `this.destroy()` to be registered multiple times
325
+ */
326
+ fs.statSync(this.storeDir)
327
+
328
+ fs.rmSync(this.storeDir, { recursive: true })
329
+ log.verbose('UniformResourceNameRegistry', 'destroy() fs.rmSync(%s) done', this.storeDir)
330
+
331
+ } catch (e) {
332
+ if ((e as NodeJS.ErrnoException).code === 'ENOENT') {
333
+ log.verbose('UniformResourceNameRegistry', 'destroy() %s not exist', this.storeDir)
334
+ return
335
+ }
336
+ log.warn('UniformResourceNameRegistry', 'destroy() fs.rmSync(%s) exception: %s', (e as Error).message)
337
+ }
338
+ }
339
+
340
+ }
341
+
342
+ export { UniformResourceNameRegistry }
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env -S node --no-warnings --loader ts-node/esm
2
+
3
+ import { test } from 'tstest'
4
+
5
+ import { uuidToBigInt } from './uuid-to-big-int.js'
6
+
7
+ test('uuidToBigInt() Nil UUID', async t => {
8
+ const uuid = '00000000-0000-0000-0000-000000000000'
9
+ const bigInt = uuidToBigInt(uuid)
10
+ t.equal(bigInt.toString(), '0', 'should get zero for a Nil UUID')
11
+ })
12
+
13
+ test('uuidToBigInt() Nil UUID', async t => {
14
+ const UUID = '00bd7c03-a690-48d3-a1fe-1314574e4fc1'
15
+ const EXPECTED = BigInt('0x00bd7c03a69048d3a1fe1314574e4fc1')
16
+
17
+ const bigInt = uuidToBigInt(UUID)
18
+ t.equal(bigInt, EXPECTED, 'should convert UUID right')
19
+ })
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Convert UUID to BigInt
3
+ * @param uuid
4
+ * @returns BigInt
5
+ */
6
+
7
+ function uuidToBigInt (uuid: string): BigInt {
8
+ // credit: https://stackoverflow.com/a/58014300/1123955
9
+ const hexBytes = `0x${uuid.replace(/-/g, '')}`
10
+ const bigInteger = BigInt(hexBytes)
11
+ return bigInteger
12
+ }
13
+
14
+ export {
15
+ uuidToBigInt,
16
+ }
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env -S node --no-warnings --loader ts-node/esm
2
+
3
+ import { test } from 'tstest'
4
+
5
+ import { VERSION } from './version.js'
6
+
7
+ test('Make sure the VERSION is fresh in source code', async t => {
8
+ t.equal(VERSION, '0.0.0', 'version should be 0.0.0 in source code, only updated before publish to NPM')
9
+ })
package/src/version.ts ADDED
@@ -0,0 +1,4 @@
1
+ /**
2
+ * This file was auto generated from scripts/generate-version.sh
3
+ */
4
+ export const VERSION: string = '1.7.0'