@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,145 @@
1
+ import type http from 'http'
2
+ import type {
3
+ Readable,
4
+ Writable,
5
+ } from 'stream'
6
+
7
+ import type {
8
+ FileBox,
9
+ } from './file-box.js'
10
+
11
+ interface Pipeable {
12
+ // pipe: typeof Readable.prototype.pipe,
13
+ pipe<T extends Writable>(destination: T, options?: { end?: boolean; }): T;
14
+ }
15
+
16
+ /**
17
+ * Huan(202002):
18
+ * We need to keep this enum number to be consistent
19
+ * because of toJSON & fromJSON need the same type number across versoins.
20
+ * and gRPC maybe will use those numbers in the future as well.
21
+ */
22
+ enum FileBoxType {
23
+ Unknown = 0,
24
+
25
+ /**
26
+ * 1. toJSON() Serializable
27
+ * - Base64
28
+ * - Url
29
+ * - QRCode
30
+ * - UUID
31
+ *
32
+ * 2. toJSON() NOT Serializable: need to convert to FileBoxType.Base64 before call toJSON()
33
+ * - Buffer
34
+ * - Stream
35
+ * - File
36
+ */
37
+ Base64 = 1,
38
+ Url = 2,
39
+ QRCode = 3,
40
+
41
+ Buffer = 4,
42
+ File = 5,
43
+ Stream = 6,
44
+ Uuid = 7,
45
+ }
46
+
47
+ interface Metadata {
48
+ [key: string]: any,
49
+ }
50
+
51
+ /**
52
+ * URI to the file
53
+ * See:
54
+ * https://nodejs.org/api/fs.html#fs_url_object_support
55
+ * https://danielmiessler.com/study/url-uri/
56
+ *
57
+ * FileType: LOCAL, REMOTE, BUFFER, STREAM
58
+ *
59
+ */
60
+ interface FileBoxOptionsCommon {
61
+ /**
62
+ * File base name: name + ext
63
+ * like: "file.txt"
64
+ */
65
+ name: string
66
+
67
+ /**
68
+ * Can be only set once
69
+ */
70
+ metadata?: Metadata,
71
+
72
+ /**
73
+ * Size
74
+ */
75
+ size?: number
76
+ md5?: string
77
+ }
78
+
79
+ interface FileBoxOptionsFile {
80
+ type : FileBoxType.File
81
+ path : string
82
+ }
83
+ interface FileBoxOptionsUrl {
84
+ type : FileBoxType.Url
85
+ url : string
86
+ headers? : http.OutgoingHttpHeaders
87
+ }
88
+ interface FileBoxOptionsBuffer {
89
+ type : FileBoxType.Buffer
90
+ buffer : Buffer
91
+ }
92
+ interface FileBoxOptionsStream {
93
+ type : FileBoxType.Stream
94
+ stream : Readable
95
+ }
96
+ interface FileBoxOptionsQRCode {
97
+ type : FileBoxType.QRCode,
98
+ qrCode : string,
99
+ }
100
+ interface FileBoxOptionsBase64 {
101
+ type : FileBoxType.Base64,
102
+ base64 : string,
103
+ }
104
+ interface FileBoxOptionsUuid {
105
+ type : FileBoxType.Uuid
106
+ uuid : string
107
+ }
108
+
109
+ type FileBoxOptions = FileBoxOptionsCommon & (
110
+ never
111
+ | FileBoxOptionsBase64
112
+ | FileBoxOptionsBuffer
113
+ | FileBoxOptionsFile
114
+ | FileBoxOptionsQRCode
115
+ | FileBoxOptionsStream
116
+ | FileBoxOptionsUrl
117
+ | FileBoxOptionsUuid
118
+ )
119
+
120
+ type FileBoxJsonObject = FileBoxOptionsCommon & (
121
+ | FileBoxOptionsBase64
122
+ | FileBoxOptionsUrl
123
+ | FileBoxOptionsQRCode
124
+ | FileBoxOptionsUuid
125
+ )
126
+
127
+ type UuidLoader = (this: FileBox, uuid: string) => Promise<Readable>
128
+ type UuidSaver = (this: FileBox, stream: Readable) => Promise<string>
129
+
130
+ export type {
131
+ FileBoxJsonObject,
132
+ FileBoxOptions,
133
+ FileBoxOptionsBase64,
134
+ FileBoxOptionsCommon,
135
+ FileBoxOptionsQRCode,
136
+ FileBoxOptionsUrl,
137
+ FileBoxOptionsUuid,
138
+ Metadata,
139
+ Pipeable,
140
+ UuidSaver,
141
+ UuidLoader,
142
+ }
143
+ export {
144
+ FileBoxType,
145
+ }
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env -S node --no-warnings --loader ts-node/esm
2
+
3
+ // tslint:disable:no-shadowed-variable
4
+ import { test } from 'tstest'
5
+
6
+ import type {
7
+ FileBoxInterface,
8
+ } from './interface.js'
9
+
10
+ import {
11
+ FileBox,
12
+ } from './file-box.js'
13
+
14
+ test('FileBoxInterface', async t => {
15
+ const fileBox: FileBoxInterface = FileBox.fromQRCode('test')
16
+ t.ok(fileBox, 'should be ok with interface')
17
+ })
@@ -0,0 +1,47 @@
1
+ import type {
2
+ Constructor,
3
+ } from 'clone-class'
4
+ import type {
5
+ FileBox,
6
+ } from './file-box.js'
7
+
8
+ interface FileBoxInterface {
9
+ type : FileBox['type']
10
+
11
+ name : FileBox['name']
12
+ size : FileBox['size']
13
+ md5 : FileBox['md5']
14
+ mediaType : FileBox['mediaType']
15
+ metadata : FileBox['metadata']
16
+
17
+ // version: any
18
+ // ready: any
19
+ // syncRemote: any
20
+ // transformBufferToStream: any
21
+ // transformBase64ToStream: any
22
+ // transformFileToStream: any
23
+ // ransformUrlToStream: any
24
+ // transformQRCodeToStream: any
25
+ // transformUrlToStream: any
26
+
27
+ toBase64 : FileBox['toBase64']
28
+ toBuffer : FileBox['toBuffer']
29
+ toDataURL : FileBox['toDataURL']
30
+ toFile : FileBox['toFile']
31
+ toJSON : FileBox['toJSON']
32
+ toQRCode : FileBox['toQRCode']
33
+ toStream : FileBox['toStream']
34
+ toUuid : FileBox['toUuid']
35
+
36
+ pipe: FileBox['pipe']
37
+ }
38
+
39
+ /**
40
+ * Huan(202110): TODO support static methods after TypeScript 4.5: fromXXX()
41
+ */
42
+ type FileBoxConstructor = Constructor<FileBoxInterface>
43
+
44
+ export type {
45
+ FileBoxInterface,
46
+ FileBoxConstructor,
47
+ }
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env -S node --no-warnings --loader ts-node/esm
2
+
3
+ // tslint:disable:no-shadowed-variable
4
+ import { test } from 'tstest'
5
+
6
+ import {
7
+ dataUrlToBase64,
8
+ httpHeaderToFileName,
9
+ httpHeadHeader,
10
+ httpStream,
11
+ streamToBuffer,
12
+ } from './misc.js'
13
+
14
+ test('dataUrl to base64', async t => {
15
+ const base64 = [
16
+ 'R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl',
17
+ '3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
18
+ 'ACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGA',
19
+ 'iqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7',
20
+ ].join('')
21
+ const dataUrl = [
22
+ 'data:image/png;base64,',
23
+ base64,
24
+ ].join('')
25
+
26
+ t.equal(base64, dataUrlToBase64(dataUrl), 'should get base64 from dataUrl')
27
+ })
28
+
29
+ test('httpHeadHeader', async t => {
30
+ const URL = 'https://github.com/huan/file-box/archive/v0.6.tar.gz'
31
+
32
+ const EXPECTED_HEADERS_KEY = 'content-disposition'
33
+ const EXPECTED_HEADERS_VALUE = 'attachment; filename=file-box-0.6.tar.gz'
34
+
35
+ const headers = await httpHeadHeader(URL)
36
+
37
+ t.equal(headers[EXPECTED_HEADERS_KEY], EXPECTED_HEADERS_VALUE, 'should get the headers right')
38
+ })
39
+
40
+ test('httpHeaderToFileName', async t => {
41
+ const HEADERS_QUOTATION_MARK: any = {
42
+ 'content-disposition': 'attachment; filename="db-0.0.19.zip"',
43
+ }
44
+ const HEADERS_NO_QUOTATION_MARK: any = {
45
+ 'content-disposition': 'attachment; filename=db-0.0.19.zip',
46
+ }
47
+ const EXPECTED_FILE_NAME = 'db-0.0.19.zip'
48
+
49
+ let filename = httpHeaderToFileName(HEADERS_QUOTATION_MARK)
50
+ t.equal(filename, EXPECTED_FILE_NAME, 'should get filename with quotation mark')
51
+
52
+ filename = httpHeaderToFileName(HEADERS_NO_QUOTATION_MARK)
53
+ t.equal(filename, EXPECTED_FILE_NAME, 'should get filename with no quotation mark')
54
+ })
55
+
56
+ test('httpStream', async t => {
57
+ const URL = 'https://httpbin.org/headers'
58
+
59
+ const MOL_KEY = 'Mol'
60
+ const MOL_VAL = '42'
61
+
62
+ const headers = {} as { [idx: string]: string }
63
+ headers[MOL_KEY] = MOL_VAL
64
+
65
+ const res = await httpStream(URL, headers)
66
+
67
+ const buffer = await streamToBuffer(res)
68
+ const obj = JSON.parse(buffer.toString())
69
+ t.equal(obj.headers[MOL_KEY], MOL_VAL, 'should send the header right')
70
+ })
package/src/misc.ts ADDED
@@ -0,0 +1,157 @@
1
+ import http from 'http'
2
+ import https from 'https'
3
+ import { URL } from 'url'
4
+ import type stream from 'stream'
5
+
6
+ import { HTTP_TIMEOUT } from './config.js'
7
+
8
+ export function dataUrlToBase64 (dataUrl: string): string {
9
+ const dataList = dataUrl.split(',')
10
+ return dataList[dataList.length - 1]!
11
+ }
12
+
13
+ /**
14
+ * Get http headers for specific `url`
15
+ * follow 302 redirection for max `REDIRECT_TTL` times.
16
+ *
17
+ * @credit https://stackoverflow.com/a/43632171/1123955
18
+ */
19
+ export async function httpHeadHeader (url: string): Promise<http.IncomingHttpHeaders> {
20
+
21
+ let REDIRECT_TTL = 7
22
+
23
+ while (true) {
24
+ if (REDIRECT_TTL-- <= 0) {
25
+ throw new Error(`ttl expired! too many(>${REDIRECT_TTL}) 302 redirection.`)
26
+ }
27
+
28
+ const res = await _headHeader(url)
29
+
30
+ if (!/^3/.test(String(res.statusCode))) {
31
+ return res.headers
32
+ }
33
+
34
+ // console.log('302 found for ' + url)
35
+
36
+ if (!res.headers.location) {
37
+ throw new Error('302 found but no location!')
38
+ }
39
+
40
+ url = res.headers.location
41
+ }
42
+
43
+ async function _headHeader (destUrl: string): Promise<http.IncomingMessage> {
44
+ const parsedUrl = new URL(destUrl)
45
+ const options = {
46
+ method : 'HEAD',
47
+ // method : 'GET',
48
+ }
49
+
50
+ let request: typeof http.request
51
+
52
+ if (parsedUrl.protocol === 'https:') {
53
+ request = https.request
54
+ } else if (parsedUrl.protocol === 'http:') {
55
+ request = http.request
56
+ } else {
57
+ throw new Error('unknown protocol: ' + parsedUrl.protocol)
58
+ }
59
+
60
+ return new Promise<http.IncomingMessage>((resolve, reject) => {
61
+ let res: undefined | http.IncomingMessage
62
+ const req = request(parsedUrl, options, (response) => {
63
+ res = response
64
+ resolve(res)
65
+ })
66
+ .on('error', reject)
67
+ .setTimeout(HTTP_TIMEOUT, () => {
68
+ const e = new Error(`FileBox: Http request timeout (${HTTP_TIMEOUT})!`)
69
+ req.emit('error', e)
70
+ req.destroy()
71
+ })
72
+ .end()
73
+ })
74
+ }
75
+ }
76
+
77
+ export function httpHeaderToFileName (
78
+ headers: http.IncomingHttpHeaders,
79
+ ): null | string {
80
+ const contentDisposition = headers['content-disposition']
81
+
82
+ if (!contentDisposition) {
83
+ return null
84
+ }
85
+
86
+ // 'content-disposition': 'attachment; filename=db-0.0.19.zip'
87
+ const matches = contentDisposition.match(/attachment; filename="?(.+[^"])"?$/i)
88
+
89
+ if (matches && matches[1]) {
90
+ return matches[1]
91
+ }
92
+
93
+ return null
94
+ }
95
+
96
+ export async function httpStream (
97
+ url : string,
98
+ headers : http.OutgoingHttpHeaders = {},
99
+ ): Promise<http.IncomingMessage> {
100
+ const parsedUrl = new URL(url)
101
+
102
+ const protocol = parsedUrl.protocol
103
+
104
+ const options: http.RequestOptions = {}
105
+
106
+ let get: typeof https.get
107
+
108
+ if (!protocol) {
109
+ throw new Error('protocol is empty')
110
+ }
111
+
112
+ if (protocol.match(/^https:/i)) {
113
+ get = https.get
114
+ options.agent = https.globalAgent
115
+ } else if (protocol.match(/^http:/i)) {
116
+ get = http.get
117
+ options.agent = http.globalAgent
118
+ } else {
119
+ throw new Error('protocol unknown: ' + protocol)
120
+ }
121
+
122
+ options.headers = {
123
+ ...headers,
124
+ }
125
+
126
+ return new Promise<http.IncomingMessage>((resolve, reject) => {
127
+ let res: http.IncomingMessage | null = null
128
+ const req = get(parsedUrl, options, (response) => {
129
+ res = response
130
+ resolve(res)
131
+ })
132
+ .on('error', reject)
133
+ .setTimeout(HTTP_TIMEOUT, () => {
134
+ const e = new Error(`FileBox: Http request timeout (${HTTP_TIMEOUT})!`)
135
+ if (res) {
136
+ res.emit('error', e)
137
+ }
138
+ req.emit('error', e)
139
+ req.destroy()
140
+ })
141
+ .end()
142
+ })
143
+ }
144
+
145
+ export async function streamToBuffer (
146
+ stream: stream.Readable,
147
+ ): Promise<Buffer> {
148
+ return new Promise<Buffer>((resolve, reject) => {
149
+ const bufferList: Buffer[] = []
150
+ stream.once('error', reject)
151
+ stream.once('end', () => {
152
+ const fullBuffer = Buffer.concat(bufferList)
153
+ resolve(fullBuffer)
154
+ })
155
+ stream.on('data', buffer => bufferList.push(buffer))
156
+ })
157
+ }
package/src/mod.ts ADDED
@@ -0,0 +1,33 @@
1
+ import {
2
+ FileBoxType,
3
+ } from './file-box.type.js'
4
+ import {
5
+ VERSION,
6
+ } from './config.js'
7
+ import {
8
+ FileBox,
9
+ } from './file-box.js'
10
+ import type {
11
+ FileBoxInterface,
12
+ FileBoxConstructor,
13
+ } from './interface.js'
14
+ import type {
15
+ UuidSaver,
16
+ UuidLoader,
17
+ } from './file-box.type.js'
18
+ import {
19
+ UniformResourceNameRegistry,
20
+ } from './urn-registry/uniform-resource-name-registry.js'
21
+
22
+ export type {
23
+ UuidSaver,
24
+ UuidLoader,
25
+ FileBoxInterface,
26
+ FileBoxConstructor,
27
+ }
28
+ export {
29
+ FileBox,
30
+ FileBoxType,
31
+ UniformResourceNameRegistry,
32
+ VERSION,
33
+ }
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env -S node --no-warnings --loader ts-node/esm
2
+
3
+ /* eslint @typescript-eslint/no-unused-vars:off */
4
+
5
+ import { test } from 'tstest'
6
+
7
+ import {
8
+ PassThrough,
9
+ Readable,
10
+ } from 'stream'
11
+
12
+ import { sizedChunkTransformer } from './sized-chunk-transformer.js'
13
+
14
+ test('chunkerTransformStream()', async t => {
15
+ const DATA_LIST = [
16
+ 'a',
17
+ 'b',
18
+ ]
19
+ const DATA = DATA_LIST.join('')
20
+
21
+ const createStream = () => {
22
+ const stream = new PassThrough()
23
+ stream.end(DATA)
24
+ return stream
25
+ }
26
+
27
+ const getDataList = (stream: Readable) => {
28
+ return new Promise<any[]>(resolve => {
29
+ const list = [] as any[]
30
+ stream.on('end', () => resolve(list))
31
+ stream.on('data', chunk => list.push(chunk))
32
+ })
33
+ }
34
+
35
+ const newStream0 = createStream()
36
+ const dataList0 = await getDataList(newStream0)
37
+
38
+ t.equal(dataList0.length, 1, 'should get 1 chunks')
39
+ t.equal(dataList0[0].toString(), DATA, 'should get data')
40
+
41
+ const newStream1 = createStream().pipe(sizedChunkTransformer(2))
42
+ const dataList1 = await getDataList(newStream1)
43
+
44
+ t.equal(dataList1.length, 1, 'should get 1 chunks')
45
+ t.equal(dataList1[0].toString(), DATA, 'should get data')
46
+
47
+ const newStream2 = createStream().pipe(sizedChunkTransformer(1))
48
+ const dataList2 = await getDataList(newStream2)
49
+
50
+ t.equal(dataList2.length, 2, 'should get 2 chunks')
51
+ t.equal(dataList2.join(''), DATA, 'should get data')
52
+ })
@@ -0,0 +1,49 @@
1
+ /**
2
+ * ChunkerTransformStream, a transform stream to take arbitrary chunk sizes and make them consistent
3
+ * https://codereview.stackexchange.com/q/57492/185709
4
+ */
5
+ import stream from 'stream'
6
+
7
+ /**
8
+ * TCP streaming workload tuning
9
+ * https://www.ibm.com/docs/en/aix/7.2?topic=tuning-tcp-streaming-workload
10
+ */
11
+ const DEFAULT_CHUNK_BYTE = 256 * 1024 // 256 KB
12
+
13
+ /**
14
+ * @param chunkByte The size of the chunks to be created
15
+ * @returns
16
+ */
17
+ function sizedChunkTransformer (chunkByte = DEFAULT_CHUNK_BYTE) {
18
+ let buffer = Buffer.from([])
19
+
20
+ const transform: stream.TransformOptions['transform'] = function (chunk, _, done) {
21
+ buffer = Buffer.concat([ buffer, chunk ])
22
+
23
+ while (buffer.length >= chunkByte) {
24
+ this.push(buffer.slice(0, chunkByte))
25
+ buffer = buffer.slice(chunkByte)
26
+ }
27
+
28
+ done()
29
+ }
30
+
31
+ const flush: stream.TransformOptions['flush'] = function (done) {
32
+ if (buffer.length) {
33
+ this.push(buffer)
34
+ }
35
+ done()
36
+ }
37
+
38
+ const chunker = new stream.Transform({
39
+ flush,
40
+ objectMode: true,
41
+ transform,
42
+ })
43
+
44
+ return chunker
45
+ }
46
+
47
+ export {
48
+ sizedChunkTransformer,
49
+ }
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env -S node --no-warnings --loader ts-node/esm
2
+
3
+ import { test } from 'tstest'
4
+
5
+ import {
6
+ bufferToQrValue,
7
+ qrValueToStream,
8
+ } from './qrcode.js'
9
+
10
+ test('imageBase64ToQrCode()', async t => {
11
+ const QRCODE_IMAGE_BASE64 = [
12
+ 'iVBORw0KGgoAAAANSUhEUgAAAMgAAADIAQMAAACXljzdAAAABlBMVEX///8AAABVwtN+AAAA',
13
+ 'CXBIWXMAAA7EAAAOxAGVKw4bAAAA7klEQVRYw+2WsQ3EIAxFjShSMgKjZLRktIzCCJQpIv7Z',
14
+ 'hCiXO/qzT/wCWXo0X3wbEw0NWVaEKM187KHW2QLZ+AhpXovfQ+J6skEWHELqBa5NEeCwR7iS',
15
+ 'V7BDzuzAiZ9eqn5IWjfWXHf7VCO5tPAM6U9AjSRideyHFn4FiuvDqV5CM9rZXuF2pZmIAjZy',
16
+ 'x4S0MDdBxEmu3TrliPf7iglPvuLlRydfU3P70UweCSK+ZYK0mUg1O4AVcv0/8itGkC7SdiTH',
17
+ '0+Mz19oJZ4NkhhSPbIhQkQGI8u1HJzmzs7p7pzNAru2pJb6z8ykkQ0P/pheK6vjurjf7+wAA',
18
+ 'AABJRU5ErkJggg==',
19
+ ].join('')
20
+ const EXPECTED_QRCODE_TEXT = 'hello, world!'
21
+
22
+ const buf = Buffer.from(QRCODE_IMAGE_BASE64, 'base64')
23
+ const text = await bufferToQrValue(buf)
24
+ t.equal(text, EXPECTED_QRCODE_TEXT, 'should decode qrcode image base64')
25
+ })
26
+
27
+ test('qrValueToStream()', async t => {
28
+ const QRCODE_VALUE = 'hello, world!'
29
+ const EXPECTED_QRCODE_IMAGE_BASE64 = [
30
+ 'iVBORw0KGgoAAAANSUhEUgAAAHQAAAB0CAYAAABUmhYnAAAAAklEQVR4AewaftIAAAKcSURBVO3BQY7',
31
+ 'cQAwEwSxC//9yeo88NSBIsx7TjIg/WGMUa5RijVKsUYo1SrFGKdYoxRqlWKMUa5RijVKsUYo1SrFGKd',
32
+ 'YoxRrl4qEk/CaVkyTcoXKShN+k8kSxRinWKMUa5eJlKm9Kwh0qn6TypiS8qVijFGuUYo1y8WFJuEPlj',
33
+ 'iR0Kl0SOpUuCZ3KHUm4Q+WTijVKsUYp1igXw6n8T4o1SrFGKdYoF8MloVOZrFijFGuUYo1y8WEq3yQJ',
34
+ 'ncoTKt+kWKMUa5RijXLxsiR8M5UuCZ3KSRK+WbFGKdYoxRrl4iGVf5nKicq/pFijFGuUYo0Sf/BAEjq',
35
+ 'VLglvUnkiCZ3KSRLepPJJxRqlWKMUa5SLlyXhROU3JaFT6ZJwonKShCeS0Kk8UaxRijVKsUaJP3hREj',
36
+ 'qVO5JwotIloVO5Iwl3qJwk4Q6VNxVrlGKNUqxRLv6yJJyodEl4Igl3qHRJ6FTuUPmkYo1SrFGKNcrFQ',
37
+ '0k4ScITSehUuiScJKFT6ZLQqXRJuEPljiR0Kk8Ua5RijVKsUS4eUvkmSbhDpUvCHUk4UflNxRqlWKMU',
38
+ 'a5SLh5Lwm1Q6lS4JdyThCZWTJJyovKlYoxRrlGKNcvEylTcl4SQJnUqXhCdUuiScJOFvKtYoxRqlWKN',
39
+ 'cfFgS7lB5k0qXhDuS0Kl0SehUTpLQJaFTeaJYoxRrlGKNcjFcEjqVJ5JwkoROpVP5pGKNUqxRijXKxX',
40
+ '8mCScqXRJOVE6S0Kl8UrFGKdYoxRrl4sNUPknlDpUuCV0SOpWTJHyTYo1SrFGKNcrFy5Lwm5LQqXQqX',
41
+ 'RI6lZMkdConKidJ6FTeVKxRijVKsUaJP1hjFGuUYo1SrFGKNUqxRinWKMUapVijFGuUYo1SrFGKNUqx',
42
+ 'RinWKMUa5Q8Ztu740xD9iQAAAABJRU5ErkJggg==',
43
+ ].join('')
44
+
45
+ const stream = await qrValueToStream(QRCODE_VALUE)
46
+
47
+ const chunks = [] as Buffer[]
48
+ for await (const chunk of stream) {
49
+ chunks.push(chunk as Buffer)
50
+ }
51
+ const buf = Buffer.concat(chunks)
52
+ const base64Text = buf.toString('base64')
53
+
54
+ t.equal(base64Text, EXPECTED_QRCODE_IMAGE_BASE64, 'should encode QR Code value to expected image')
55
+ })
package/src/qrcode.ts ADDED
@@ -0,0 +1,38 @@
1
+ import {
2
+ PassThrough,
3
+ Readable,
4
+ } from 'stream'
5
+
6
+ // The npm package of my best choice for QR code decoding on Angular SPA
7
+ // https://dev.to/j_sakamoto/the-npm-package-of-my-best-choice-for-qr-code-decoding-on-angular-spa-4747?returning-user=true
8
+ import Jimp from 'jimp'
9
+ import jsQR from 'jsqr'
10
+
11
+ /**
12
+ * https://www.npmjs.com/package/qrcode
13
+ * Huan(202002): This module is encode only.
14
+ */
15
+ import { toFileStream } from 'qrcode'
16
+
17
+ export async function bufferToQrValue (buf: Buffer): Promise<string> {
18
+ const image = await Jimp.read(buf)
19
+ const qrCodeImageArray = new Uint8ClampedArray(image.bitmap.data.buffer)
20
+
21
+ const qrCodeResult = jsQR(
22
+ qrCodeImageArray,
23
+ image.bitmap.width,
24
+ image.bitmap.height,
25
+ )
26
+
27
+ if (qrCodeResult) {
28
+ return qrCodeResult.data
29
+ } else {
30
+ throw new Error('bufferToQrcode(buf) fail!')
31
+ }
32
+ }
33
+
34
+ export async function qrValueToStream (value: string): Promise<Readable> {
35
+ const stream = new PassThrough()
36
+ await toFileStream(stream, value) // only support .png for now
37
+ return stream
38
+ }
@@ -0,0 +1 @@
1
+ declare module 'jsqr'
@@ -0,0 +1,7 @@
1
+ import {
2
+ UniformResourceNameRegistry,
3
+ } from './uniform-resource-name-registry.js'
4
+
5
+ export {
6
+ UniformResourceNameRegistry,
7
+ }
@@ -0,0 +1,7 @@
1
+ import * as uuid from 'uuid'
2
+
3
+ const randomUuid = uuid.v4
4
+
5
+ export {
6
+ randomUuid,
7
+ }