@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,775 @@
1
+ /**
2
+ * File Box
3
+ * https://github.com/huan/file-box
4
+ *
5
+ * 2018 Huan LI <zixia@zixia.net>
6
+ */
7
+ /* eslint no-use-before-define: off */
8
+ import * as FS from 'fs';
9
+ import * as PATH from 'path';
10
+ import * as URL from 'url';
11
+ import mime from 'mime';
12
+ import { PassThrough, } from 'stream';
13
+ import { instanceToClass, looseInstanceOfClass, interfaceOfClass, } from 'clone-class';
14
+ import { VERSION, } from './config.js';
15
+ import { FileBoxType, } from './file-box.type.js';
16
+ import { dataUrlToBase64, httpHeaderToFileName, httpHeadHeader, httpStream, streamToBuffer, } from './misc.js';
17
+ import { bufferToQrValue, qrValueToStream, } from './qrcode.js';
18
+ import { sizedChunkTransformer, } from './pure-functions/sized-chunk-transformer.js';
19
+ const EMPTY_META_DATA = Object.freeze({});
20
+ const UNKNOWN_SIZE = -1;
21
+ let interfaceOfFileBox = (_) => false;
22
+ let looseInstanceOfFileBox = (_) => false;
23
+ class FileBox {
24
+ /**
25
+ *
26
+ * Static Properties
27
+ *
28
+ */
29
+ static version = VERSION;
30
+ /**
31
+ * Symbol.hasInstance: instanceof
32
+ *
33
+ * @link https://www.keithcirkel.co.uk/metaprogramming-in-es6-symbols/
34
+ */
35
+ static [Symbol.hasInstance](lho) {
36
+ return this.validInterface(lho);
37
+ }
38
+ /**
39
+ * Check if obj satisfy FileBox interface
40
+ */
41
+ static valid(target) {
42
+ return this.validInstance(target) || this.validInterface(target);
43
+ }
44
+ /**
45
+ * Check if obj satisfy FileBox interface
46
+ */
47
+ static validInterface(target) {
48
+ return interfaceOfFileBox(target);
49
+ }
50
+ /**
51
+ * loose check instance of FileBox
52
+ */
53
+ static validInstance(target) {
54
+ return looseInstanceOfFileBox(target);
55
+ }
56
+ /**
57
+ * fromUrl()
58
+ */
59
+ static fromUrl(url, nameOrOptions, headers) {
60
+ let name;
61
+ let size;
62
+ let md5;
63
+ if (typeof nameOrOptions === 'object') {
64
+ headers = nameOrOptions.headers;
65
+ name = nameOrOptions.name;
66
+ size = nameOrOptions.size;
67
+ md5 = nameOrOptions.md5;
68
+ }
69
+ else {
70
+ name = nameOrOptions;
71
+ }
72
+ if (!name) {
73
+ const parsedUrl = new URL.URL(url);
74
+ name = parsedUrl.pathname;
75
+ }
76
+ const options = {
77
+ headers,
78
+ md5,
79
+ name,
80
+ size,
81
+ type: FileBoxType.Url,
82
+ url,
83
+ };
84
+ return new this(options);
85
+ }
86
+ /**
87
+ * Alias for `FileBox.fromFile()`
88
+ *
89
+ * @alias fromFile
90
+ */
91
+ static fromFile(path, name, md5) {
92
+ if (!name) {
93
+ name = PATH.parse(path).base;
94
+ }
95
+ const options = {
96
+ md5,
97
+ name,
98
+ path,
99
+ type: FileBoxType.File,
100
+ };
101
+ return new this(options);
102
+ }
103
+ /**
104
+ * TODO: add `FileBoxStreamOptions` with `size` support (@huan, 202111)
105
+ */
106
+ static fromStream(stream, name, md5) {
107
+ const options = {
108
+ md5,
109
+ name: name || 'stream.dat',
110
+ stream,
111
+ type: FileBoxType.Stream,
112
+ };
113
+ return new this(options);
114
+ }
115
+ static fromBuffer(buffer, name, md5) {
116
+ const options = {
117
+ buffer,
118
+ md5,
119
+ name: name || 'buffer.dat',
120
+ type: FileBoxType.Buffer,
121
+ };
122
+ return new this(options);
123
+ }
124
+ /**
125
+ * @param base64
126
+ * @param name the file name of the base64 data
127
+ */
128
+ static fromBase64(base64, name, md5) {
129
+ const options = {
130
+ base64,
131
+ md5,
132
+ name: name || 'base64.dat',
133
+ type: FileBoxType.Base64,
134
+ };
135
+ return new this(options);
136
+ }
137
+ /**
138
+ * dataURL: `data:image/png;base64,${base64Text}`,
139
+ */
140
+ static fromDataURL(dataUrl, name, md5) {
141
+ return this.fromBase64(dataUrlToBase64(dataUrl), name || 'data-url.dat', md5);
142
+ }
143
+ /**
144
+ *
145
+ * @param qrCode the value of the QR Code. For example: `https://github.com`
146
+ */
147
+ static fromQRCode(qrCode, md5) {
148
+ const options = {
149
+ md5,
150
+ name: 'qrcode.png',
151
+ qrCode,
152
+ type: FileBoxType.QRCode,
153
+ };
154
+ return new this(options);
155
+ }
156
+ static uuidToStream;
157
+ static uuidFromStream;
158
+ /**
159
+ * @param uuid the UUID of the file. For example: `6f88b03c-1237-4f46-8db2-98ef23200551`
160
+ * @param name the name of the file. For example: `video.mp4`
161
+ */
162
+ static fromUuid(uuid, nameOrOptions) {
163
+ let name;
164
+ let size;
165
+ let md5;
166
+ if (typeof nameOrOptions === 'object') {
167
+ name = nameOrOptions.name;
168
+ size = nameOrOptions.size;
169
+ md5 = nameOrOptions.md5;
170
+ }
171
+ else {
172
+ name = nameOrOptions;
173
+ }
174
+ const options = {
175
+ md5,
176
+ name: name || `${uuid}.dat`,
177
+ size,
178
+ type: FileBoxType.Uuid,
179
+ uuid,
180
+ };
181
+ return new this(options);
182
+ }
183
+ /**
184
+ * UUID Type FielBox Loader
185
+ */
186
+ static setUuidLoader(loader) {
187
+ if (Object.prototype.hasOwnProperty.call(this, 'uuidToStream')) {
188
+ throw new Error('this FileBox has been set resolver before, can not set twice');
189
+ }
190
+ this.uuidToStream = loader;
191
+ }
192
+ /**
193
+ * UUID Type FielBox Saver
194
+ */
195
+ static setUuidSaver(saver) {
196
+ if (Object.prototype.hasOwnProperty.call(this, 'uuidFromStream')) {
197
+ throw new Error('this FileBox has been set register before, can not set twice');
198
+ }
199
+ this.uuidFromStream = saver;
200
+ }
201
+ /**
202
+ *
203
+ * @static
204
+ * @param {(FileBoxJsonObject | string)} obj
205
+ * @returns {FileBox}
206
+ */
207
+ static fromJSON(obj) {
208
+ if (typeof obj === 'string') {
209
+ obj = JSON.parse(obj);
210
+ }
211
+ /**
212
+ * Huan(202111): compatible with old FileBox.toJSON() key: `boxType`
213
+ * this is a breaking change made by v1.0
214
+ *
215
+ * convert `obj.boxType` to `obj.type`
216
+ * (will be removed after Dec 31, 2022)
217
+ */
218
+ if (!obj.type && 'boxType' in obj) {
219
+ obj.type = obj['boxType'];
220
+ }
221
+ let fileBox;
222
+ switch (obj.type) {
223
+ case FileBoxType.Base64:
224
+ fileBox = this.fromBase64(obj.base64, obj.name, obj.md5);
225
+ break;
226
+ case FileBoxType.Url:
227
+ fileBox = this.fromUrl(obj.url, {
228
+ md5: obj.md5,
229
+ name: obj.name,
230
+ size: obj.size,
231
+ });
232
+ break;
233
+ case FileBoxType.QRCode:
234
+ fileBox = this.fromQRCode(obj.qrCode, obj.md5);
235
+ break;
236
+ case FileBoxType.Uuid:
237
+ fileBox = this.fromUuid(obj.uuid, {
238
+ md5: obj.md5,
239
+ name: obj.name,
240
+ size: obj.size,
241
+ });
242
+ break;
243
+ default:
244
+ throw new Error(`unknown filebox json object{type}: ${JSON.stringify(obj)}`);
245
+ }
246
+ if (obj.metadata) {
247
+ fileBox.metadata = obj.metadata;
248
+ }
249
+ return fileBox;
250
+ }
251
+ /**
252
+ *
253
+ * Instance Properties
254
+ *
255
+ */
256
+ version = VERSION;
257
+ /**
258
+ * We are using a getter for `type` is because
259
+ * getter name can be enumurated by the `Object.hasOwnProperties()`*
260
+ * but property name can not.
261
+ *
262
+ * * required by `validInterface()`
263
+ */
264
+ _type;
265
+ get type() { return this._type; }
266
+ /**
267
+ * the Content-Length of the file
268
+ * `SIZE_UNKNOWN(-1)` means unknown
269
+ *
270
+ * @example
271
+ * ```ts
272
+ * const fileBox = FileBox.fromUrl('http://example.com/image.png')
273
+ * await fileBox.ready()
274
+ * console.log(fileBox.size)
275
+ * // > 102400 <- this is the size of the remote image.png
276
+ * ```
277
+ */
278
+ _size;
279
+ get size() {
280
+ if (this._size) {
281
+ return this._size;
282
+ }
283
+ return UNKNOWN_SIZE;
284
+ }
285
+ /**
286
+ * File MD5 Sum
287
+ */
288
+ md5;
289
+ /**
290
+ * @deprecated: use `mediaType` instead. will be removed after Dec 31, 2022
291
+ */
292
+ mimeType = 'application/unknown';
293
+ /**
294
+ * (Internet) Media Type is the proper technical term of `MIME Type`
295
+ * @see https://stackoverflow.com/a/9277778/1123955
296
+ *
297
+ * @example 'text/plain'
298
+ */
299
+ _mediaType;
300
+ get mediaType() {
301
+ if (this._mediaType) {
302
+ return this._mediaType;
303
+ }
304
+ return 'application/unknown';
305
+ }
306
+ _name;
307
+ get name() {
308
+ return this._name;
309
+ }
310
+ _metadata;
311
+ get metadata() {
312
+ if (this._metadata) {
313
+ return this._metadata;
314
+ }
315
+ return EMPTY_META_DATA;
316
+ }
317
+ set metadata(data) {
318
+ if (this._metadata) {
319
+ throw new Error('metadata can not be modified after set');
320
+ }
321
+ this._metadata = { ...data };
322
+ Object.freeze(this._metadata);
323
+ }
324
+ /**
325
+ * Lazy load data: (can be serialized to JSON)
326
+ * Do not read file to Buffer until there's a consumer.
327
+ */
328
+ base64;
329
+ remoteUrl;
330
+ qrCode;
331
+ uuid;
332
+ /**
333
+ * Can not be serialized to JSON
334
+ */
335
+ buffer;
336
+ localPath;
337
+ stream;
338
+ headers;
339
+ constructor(options) {
340
+ // Only keep `basename` in this.name
341
+ this._name = PATH.basename(options.name);
342
+ this._type = options.type;
343
+ /**
344
+ * Unknown file type MIME: `'application/unknown'`
345
+ * @see https://stackoverflow.com/a/6080707/1123955
346
+ */
347
+ this._mediaType = mime.getType(this.name) ?? undefined;
348
+ this.md5 = options.md5;
349
+ switch (options.type) {
350
+ case FileBoxType.Buffer:
351
+ this.buffer = options.buffer;
352
+ this._size = options.buffer.length;
353
+ break;
354
+ case FileBoxType.File:
355
+ if (!options.path) {
356
+ throw new Error('no path');
357
+ }
358
+ this.localPath = options.path;
359
+ this._size = FS.statSync(this.localPath).size;
360
+ break;
361
+ case FileBoxType.Url:
362
+ if (!options.url) {
363
+ throw new Error('no url');
364
+ }
365
+ this.remoteUrl = options.url;
366
+ if (options.headers) {
367
+ this.headers = options.headers;
368
+ }
369
+ if (options.size) {
370
+ this._size = options.size;
371
+ }
372
+ else {
373
+ /**
374
+ * Add a background task to fetch remote file name & size
375
+ *
376
+ * TODO: how to improve it?
377
+ */
378
+ // this.syncUrlMetadata().catch(console.error)
379
+ }
380
+ break;
381
+ case FileBoxType.Stream:
382
+ this.stream = options.stream;
383
+ if (options.size) {
384
+ this._size = options.size;
385
+ }
386
+ break;
387
+ case FileBoxType.QRCode:
388
+ if (!options.qrCode) {
389
+ throw new Error('no QR Code');
390
+ }
391
+ this.qrCode = options.qrCode;
392
+ break;
393
+ case FileBoxType.Base64:
394
+ if (!options.base64) {
395
+ throw new Error('no Base64 data');
396
+ }
397
+ this.base64 = options.base64;
398
+ this._size = Buffer.byteLength(options.base64, 'base64');
399
+ break;
400
+ case FileBoxType.Uuid:
401
+ if (!options.uuid) {
402
+ throw new Error('no UUID data');
403
+ }
404
+ this.uuid = options.uuid;
405
+ if (options.size) {
406
+ this._size = options.size;
407
+ }
408
+ break;
409
+ default:
410
+ throw new Error(`unknown options(type): ${JSON.stringify(options)}`);
411
+ }
412
+ }
413
+ async ready() {
414
+ switch (this.type) {
415
+ case FileBoxType.Url:
416
+ await this._syncUrlMetadata();
417
+ break;
418
+ case FileBoxType.QRCode:
419
+ if (this.size === UNKNOWN_SIZE) {
420
+ this._size = (await this.toBuffer()).length;
421
+ }
422
+ break;
423
+ default:
424
+ break;
425
+ }
426
+ }
427
+ /**
428
+ * @todo use http.get/gets instead of Request
429
+ */
430
+ async _syncUrlMetadata() {
431
+ /**
432
+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
433
+ * > Content-Disposition: attachment; filename="cool.html"
434
+ */
435
+ if (this.type !== FileBoxType.Url) {
436
+ throw new Error('type is not Url');
437
+ }
438
+ if (!this.remoteUrl) {
439
+ throw new Error('no url');
440
+ }
441
+ const headers = await httpHeadHeader(this.remoteUrl);
442
+ const httpFilename = httpHeaderToFileName(headers);
443
+ if (httpFilename) {
444
+ this._name = httpFilename;
445
+ }
446
+ if (!this.name) {
447
+ throw new Error('NONAME');
448
+ }
449
+ const httpMediaType = headers['content-type'] || (httpFilename && mime.getType(httpFilename));
450
+ if (httpMediaType) {
451
+ this._mediaType = httpMediaType;
452
+ }
453
+ if (headers['content-length']) {
454
+ this._size = Number(headers['content-length']);
455
+ }
456
+ }
457
+ /**
458
+ *
459
+ * toXXX methods
460
+ *
461
+ */
462
+ toString() {
463
+ return [
464
+ 'FileBox#',
465
+ FileBoxType[this.type],
466
+ '<',
467
+ this.name,
468
+ '>',
469
+ ].join('');
470
+ }
471
+ toJSON() {
472
+ const objCommon = {
473
+ md5: this.md5,
474
+ metadata: this.metadata,
475
+ name: this.name,
476
+ };
477
+ if (typeof this.size !== 'undefined') {
478
+ objCommon.size = this.size;
479
+ }
480
+ let obj;
481
+ switch (this.type) {
482
+ case FileBoxType.Url: {
483
+ if (!this.remoteUrl) {
484
+ throw new Error('no url');
485
+ }
486
+ const objUrl = {
487
+ headers: this.headers,
488
+ type: FileBoxType.Url,
489
+ url: this.remoteUrl,
490
+ };
491
+ obj = {
492
+ ...objCommon,
493
+ ...objUrl,
494
+ };
495
+ break;
496
+ }
497
+ case FileBoxType.QRCode: {
498
+ if (!this.qrCode) {
499
+ throw new Error('no qr code');
500
+ }
501
+ const objQRCode = {
502
+ qrCode: this.qrCode,
503
+ type: FileBoxType.QRCode,
504
+ };
505
+ obj = {
506
+ ...objCommon,
507
+ ...objQRCode,
508
+ };
509
+ break;
510
+ }
511
+ case FileBoxType.Base64: {
512
+ if (!this.base64) {
513
+ throw new Error('no base64 data');
514
+ }
515
+ const objBase64 = {
516
+ base64: this.base64,
517
+ type: FileBoxType.Base64,
518
+ };
519
+ obj = {
520
+ ...objCommon,
521
+ ...objBase64,
522
+ };
523
+ break;
524
+ }
525
+ case FileBoxType.Uuid: {
526
+ if (!this.uuid) {
527
+ throw new Error('no uuid data');
528
+ }
529
+ const objUuid = {
530
+ type: FileBoxType.Uuid,
531
+ uuid: this.uuid,
532
+ };
533
+ obj = {
534
+ ...objCommon,
535
+ ...objUuid,
536
+ };
537
+ break;
538
+ }
539
+ default:
540
+ void this.type;
541
+ throw new Error('FileBox.toJSON() can only work on limited FileBoxType(s). See: <https://github.com/huan/file-box/issues/25>');
542
+ }
543
+ /**
544
+ * Huan(202111): compatible with old FileBox.toJSON() key: `boxType`
545
+ * this is a breaking change made by v1.0
546
+ *
547
+ * save `obj.type` a copy to `obj.boxType`
548
+ * (will be removed after Dec 31, 2022)
549
+ */
550
+ obj['boxType'] = obj.type;
551
+ return obj;
552
+ }
553
+ async toStream() {
554
+ let stream;
555
+ switch (this.type) {
556
+ case FileBoxType.Buffer:
557
+ stream = this._transformBufferToStream();
558
+ break;
559
+ case FileBoxType.File:
560
+ stream = this._transformFileToStream();
561
+ break;
562
+ case FileBoxType.Url:
563
+ stream = await this._transformUrlToStream();
564
+ break;
565
+ case FileBoxType.Stream:
566
+ if (!this.stream) {
567
+ throw new Error('no stream');
568
+ }
569
+ /**
570
+ * Huan(202109): the stream.destroyed will not be `true`
571
+ * when we have read all the data
572
+ * after we change some code.
573
+ * The reason is unbase64 : this.base64,
574
+ type : FileBoxType.Base64,known... so we change to check `readable`
575
+ */
576
+ if (!this.stream.readable) {
577
+ throw new Error('The stream is not readable. Maybe has already been consumed, and now it was drained. See: https://github.com/huan/file-box/issues/50');
578
+ }
579
+ stream = this.stream;
580
+ break;
581
+ case FileBoxType.QRCode:
582
+ if (!this.qrCode) {
583
+ throw new Error('no QR Code');
584
+ }
585
+ stream = await this._transformQRCodeToStream();
586
+ break;
587
+ case FileBoxType.Base64:
588
+ if (!this.base64) {
589
+ throw new Error('no base64 data');
590
+ }
591
+ stream = this._transformBase64ToStream();
592
+ break;
593
+ case FileBoxType.Uuid: {
594
+ if (!this.uuid) {
595
+ throw new Error('no uuid data');
596
+ }
597
+ const FileBoxKlass = instanceToClass(this, FileBox);
598
+ if (!FileBoxKlass.uuidToStream) {
599
+ throw new Error('need to call FileBox.setUuidLoader() to set UUID loader first.');
600
+ }
601
+ stream = await FileBoxKlass.uuidToStream.call(this, this.uuid);
602
+ break;
603
+ }
604
+ default:
605
+ throw new Error('not supported FileBoxType: ' + FileBoxType[this.type]);
606
+ }
607
+ return stream;
608
+ }
609
+ /**
610
+ * https://stackoverflow.com/a/16044400/1123955
611
+ */
612
+ _transformBufferToStream(buffer) {
613
+ const bufferStream = new PassThrough();
614
+ bufferStream.end(buffer || this.buffer);
615
+ /**
616
+ * Use small `chunks` with `toStream()` #44
617
+ * https://github.com/huan/file-box/issues/44
618
+ */
619
+ return bufferStream.pipe(sizedChunkTransformer());
620
+ }
621
+ _transformBase64ToStream() {
622
+ if (!this.base64) {
623
+ throw new Error('no base64 data');
624
+ }
625
+ const buffer = Buffer.from(this.base64, 'base64');
626
+ return this._transformBufferToStream(buffer);
627
+ }
628
+ _transformFileToStream() {
629
+ if (!this.localPath) {
630
+ throw new Error('no url(path)');
631
+ }
632
+ return FS.createReadStream(this.localPath);
633
+ }
634
+ async _transformUrlToStream() {
635
+ return new Promise((resolve, reject) => {
636
+ if (this.remoteUrl) {
637
+ httpStream(this.remoteUrl, this.headers)
638
+ .then(resolve)
639
+ .catch(reject);
640
+ }
641
+ else {
642
+ reject(new Error('no url'));
643
+ }
644
+ });
645
+ }
646
+ async _transformQRCodeToStream() {
647
+ if (!this.qrCode) {
648
+ throw new Error('no QR Code Value found');
649
+ }
650
+ const stream = qrValueToStream(this.qrCode);
651
+ return stream;
652
+ }
653
+ /**
654
+ * save file
655
+ *
656
+ * @param filePath save file
657
+ */
658
+ async toFile(filePath, overwrite = false) {
659
+ if (this.type === FileBoxType.Url) {
660
+ if (!this.mediaType || !this.name) {
661
+ await this._syncUrlMetadata();
662
+ }
663
+ }
664
+ const fullFilePath = PATH.resolve(filePath || this.name);
665
+ const exist = FS.existsSync(fullFilePath);
666
+ if (exist && !overwrite) {
667
+ throw new Error(`FileBox.toFile(${fullFilePath}): file exist. use FileBox.toFile(${fullFilePath}, true) to force overwrite.`);
668
+ }
669
+ const writeStream = FS.createWriteStream(fullFilePath);
670
+ /**
671
+ * Huan(202109): make sure the file can be opened for writting
672
+ * before we pipe the stream to it
673
+ */
674
+ await new Promise((resolve, reject) => writeStream
675
+ .once('open', resolve)
676
+ .once('error', reject));
677
+ /**
678
+ * Start pipe
679
+ */
680
+ await new Promise((resolve, reject) => {
681
+ writeStream
682
+ .once('close', resolve)
683
+ .once('error', reject);
684
+ this.pipe(writeStream);
685
+ });
686
+ }
687
+ async toBase64() {
688
+ if (this.type === FileBoxType.Base64) {
689
+ if (!this.base64) {
690
+ throw new Error('no base64 data');
691
+ }
692
+ return this.base64;
693
+ }
694
+ const buffer = await this.toBuffer();
695
+ return buffer.toString('base64');
696
+ }
697
+ /**
698
+ * dataUrl: `data:image/png;base64,${base64Text}',
699
+ */
700
+ async toDataURL() {
701
+ const base64Text = await this.toBase64();
702
+ if (!this.mediaType) {
703
+ throw new Error('no mediaType found');
704
+ }
705
+ const dataUrl = [
706
+ 'data:',
707
+ this.mediaType,
708
+ ';base64,',
709
+ base64Text,
710
+ ].join('');
711
+ return dataUrl;
712
+ }
713
+ async toBuffer() {
714
+ if (this.type === FileBoxType.Buffer) {
715
+ if (!this.buffer) {
716
+ throw new Error('no buffer!');
717
+ }
718
+ return this.buffer;
719
+ }
720
+ const stream = new PassThrough();
721
+ this.pipe(stream);
722
+ const buffer = await streamToBuffer(stream);
723
+ return buffer;
724
+ }
725
+ async toQRCode() {
726
+ if (this.type === FileBoxType.QRCode) {
727
+ if (!this.qrCode) {
728
+ throw new Error('no QR Code!');
729
+ }
730
+ return this.qrCode;
731
+ }
732
+ const buf = await this.toBuffer();
733
+ const qrValue = await bufferToQrValue(buf);
734
+ return qrValue;
735
+ }
736
+ async toUuid() {
737
+ if (this.type === FileBoxType.Uuid) {
738
+ if (!this.uuid) {
739
+ throw new Error('no uuid found for a UUID type file box!');
740
+ }
741
+ return this.uuid;
742
+ }
743
+ const FileBoxKlass = instanceToClass(this, FileBox);
744
+ if (!FileBoxKlass.uuidFromStream) {
745
+ throw new Error('need to use FileBox.setUuidSaver() before dealing with UUID');
746
+ }
747
+ const stream = new PassThrough();
748
+ this.pipe(stream);
749
+ return FileBoxKlass.uuidFromStream.call(this, stream);
750
+ }
751
+ /**
752
+ *
753
+ * toXXX methods END
754
+ *
755
+ */
756
+ pipe(destination) {
757
+ this.toStream()
758
+ .then(stream => {
759
+ stream.on('error', e => {
760
+ destination.emit('error', e);
761
+ });
762
+ return stream.pipe(destination);
763
+ })
764
+ .catch(e => destination.emit('error', e));
765
+ return destination;
766
+ }
767
+ }
768
+ /**
769
+ * Huan(202110): lazy initialize `interfaceOfClass(FileBox)`
770
+ * because we only can reference a class after its declaration
771
+ */
772
+ interfaceOfFileBox = interfaceOfClass(FileBox)();
773
+ looseInstanceOfFileBox = looseInstanceOfClass(FileBox);
774
+ export { FileBox, };
775
+ //# sourceMappingURL=file-box.js.map