@eik/core 2.1.46 → 2.1.48

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 (52) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/lib/classes/http-incoming.js +4 -0
  3. package/lib/classes/http-outgoing.js +4 -0
  4. package/lib/classes/package.js +8 -0
  5. package/lib/classes/versions.js +16 -2
  6. package/lib/handlers/alias.delete.js +13 -4
  7. package/lib/handlers/alias.get-v2.js +22 -10
  8. package/lib/handlers/alias.get.js +10 -1
  9. package/lib/handlers/alias.post.js +19 -4
  10. package/lib/handlers/alias.put.js +19 -4
  11. package/lib/handlers/auth.post.js +7 -1
  12. package/lib/handlers/map.get.js +20 -10
  13. package/lib/handlers/map.put.js +38 -13
  14. package/lib/handlers/pkg.get.js +22 -10
  15. package/lib/handlers/pkg.log.js +21 -10
  16. package/lib/handlers/pkg.put.js +26 -4
  17. package/lib/handlers/versions.get.js +20 -10
  18. package/lib/multipart/form-file.js +3 -0
  19. package/lib/multipart/parser.js +65 -37
  20. package/lib/sinks/mem-entry.js +3 -0
  21. package/lib/sinks/test.js +40 -10
  22. package/lib/utils/healthcheck.js +16 -2
  23. package/lib/utils/utils.js +25 -0
  24. package/package.json +17 -22
  25. package/types/classes/alias.d.ts +4 -4
  26. package/types/classes/asset.d.ts +5 -5
  27. package/types/classes/author.d.ts +2 -2
  28. package/types/classes/http-incoming.d.ts +4 -4
  29. package/types/classes/http-outgoing.d.ts +7 -3
  30. package/types/classes/meta.d.ts +2 -2
  31. package/types/classes/package.d.ts +136 -11
  32. package/types/classes/versions.d.ts +14 -4
  33. package/types/handlers/alias.delete.d.ts +13 -6
  34. package/types/handlers/alias.get-v2.d.ts +16 -9
  35. package/types/handlers/alias.get.d.ts +15 -8
  36. package/types/handlers/alias.post.d.ts +24 -11
  37. package/types/handlers/alias.put.d.ts +24 -11
  38. package/types/handlers/auth.post.d.ts +15 -9
  39. package/types/handlers/map.get.d.ts +14 -9
  40. package/types/handlers/map.put.d.ts +32 -13
  41. package/types/handlers/pkg.get.d.ts +16 -9
  42. package/types/handlers/pkg.log.d.ts +15 -9
  43. package/types/handlers/pkg.put.d.ts +35 -15
  44. package/types/handlers/versions.get.d.ts +14 -9
  45. package/types/main.d.ts +2 -0
  46. package/types/multipart/form-field.d.ts +2 -2
  47. package/types/multipart/parser.d.ts +18 -6
  48. package/types/sinks/test.d.ts +26 -13
  49. package/types/utils/healthcheck.d.ts +6 -4
  50. package/types/utils/path-builders-fs.d.ts +27 -27
  51. package/types/utils/path-builders-uri.d.ts +16 -16
  52. package/types/utils/utils.d.ts +26 -3
@@ -46,6 +46,12 @@ const PkgLog = class PkgLog {
46
46
  return this._metrics;
47
47
  }
48
48
 
49
+ /**
50
+ * @param {any} req
51
+ * @param {string} type
52
+ * @param {string} name
53
+ * @param {string} version
54
+ */
49
55
  async handler(req, type, name, version) {
50
56
  const end = this._histogram.timer();
51
57
 
@@ -57,7 +63,9 @@ const PkgLog = class PkgLog {
57
63
  validators.name(pName);
58
64
  validators.type(type);
59
65
  } catch (error) {
60
- this._log.debug(`pkg:log - Validation failed - ${error.message}`);
66
+ this._log.debug(
67
+ `pkg:log - Validation failed - ${error instanceof Error ? error.message : String(error)}`,
68
+ );
61
69
  const e = new HttpError.NotFound();
62
70
  end({ labels: { success: false, status: e.status, type } });
63
71
  throw e;
@@ -83,7 +91,7 @@ const PkgLog = class PkgLog {
83
91
  });
84
92
 
85
93
  try {
86
- const file = await this._sink.read(path);
94
+ const file = await (this._sink && this._sink.read(path));
87
95
  const outgoing = new HttpOutgoing();
88
96
  outgoing.cacheControl = this._cacheControl;
89
97
  outgoing.mimeType = "application/json";
@@ -99,14 +107,17 @@ const PkgLog = class PkgLog {
99
107
  outgoing.statusCode = 200;
100
108
  outgoing.stream = file.stream;
101
109
 
102
- outgoing.stream.on("error", (err) => {
103
- this._log.info(`pkg:log - File stream error - ${err}`);
104
- end({ labels: { success: false, status: 503, type } });
105
- });
106
-
107
- outgoing.stream.on("end", () => {
108
- end({ labels: { status: outgoing.statusCode, type } });
109
- });
110
+ const outgoingStream = outgoing.stream;
111
+ if (outgoingStream) {
112
+ outgoingStream.on("error", (err) => {
113
+ this._log.info(`pkg:log - File stream error - ${err}`);
114
+ end({ labels: { success: false, status: 503, type } });
115
+ });
116
+
117
+ outgoingStream.on("end", () => {
118
+ end({ labels: { status: outgoing.statusCode, type } });
119
+ });
120
+ }
110
121
  }
111
122
 
112
123
  this._log.debug(`pkg:log - Package log found - Pathname: ${path}`);
@@ -73,18 +73,21 @@ const PkgPut = class PkgPut {
73
73
  return this._metrics;
74
74
  }
75
75
 
76
+ /**
77
+ * @param {any} incoming
78
+ */
76
79
  async _parser(incoming) {
77
80
  return new Promise((resolve, reject) => {
78
81
  this._multipart
79
82
  .parse(incoming)
80
83
  .then((result) => {
81
84
  const pkg = new Package(incoming);
82
- result.forEach((obj) => {
85
+ result.forEach((/** @type {any} */ obj) => {
83
86
  if (obj.constructor.name === "FormField") {
84
87
  pkg.setMeta(obj);
85
88
  }
86
89
  if (obj.constructor.name === "FormFile") {
87
- obj.value.forEach((o) => {
90
+ obj.value.forEach((/** @type {any} */ o) => {
88
91
  pkg.setAsset(o);
89
92
  });
90
93
  }
@@ -107,6 +110,9 @@ const PkgPut = class PkgPut {
107
110
  });
108
111
  }
109
112
 
113
+ /**
114
+ * @param {any} incoming
115
+ */
110
116
  async _readVersions(incoming) {
111
117
  const path = createFilePathToVersion(incoming);
112
118
  let versions;
@@ -127,6 +133,9 @@ const PkgPut = class PkgPut {
127
133
  return versions;
128
134
  }
129
135
 
136
+ /**
137
+ * @param {any} incoming
138
+ */
130
139
  async _readVersion(incoming) {
131
140
  const path = createFilePathToEikJson(incoming);
132
141
  try {
@@ -145,6 +154,10 @@ const PkgPut = class PkgPut {
145
154
  }
146
155
  }
147
156
 
157
+ /**
158
+ * @param {any} incoming
159
+ * @param {any} versions
160
+ */
148
161
  async _writeVersions(incoming, versions) {
149
162
  const path = createFilePathToVersion(incoming);
150
163
  await writeJSON(this._sink, path, versions, "application/json");
@@ -153,6 +166,13 @@ const PkgPut = class PkgPut {
153
166
  );
154
167
  }
155
168
 
169
+ /**
170
+ * @param {any} req
171
+ * @param {any} user
172
+ * @param {string} type
173
+ * @param {string} name
174
+ * @param {string} version
175
+ */
156
176
  async handler(req, user, type, name, version) {
157
177
  const end = this._histogram.timer();
158
178
 
@@ -164,7 +184,9 @@ const PkgPut = class PkgPut {
164
184
  validators.name(pName);
165
185
  validators.type(type);
166
186
  } catch (error) {
167
- this._log.info(`pkg:put - Validation failed - ${error.message}`);
187
+ this._log.info(
188
+ `pkg:put - Validation failed - ${error instanceof Error ? error.message : String(error)}`,
189
+ );
168
190
  const e = new HttpError.BadRequest();
169
191
  end({ labels: { success: false, status: e.status } });
170
192
  throw e;
@@ -217,7 +239,7 @@ const PkgPut = class PkgPut {
217
239
  }
218
240
 
219
241
  const outgoing = new HttpOutgoing();
220
- outgoing.cacheControl = this._cacheControl;
242
+ outgoing.cacheControl = this._cacheControl || "";
221
243
  outgoing.statusCode = 303;
222
244
  outgoing.location = createURIPathToPkgLog(pkg);
223
245
 
@@ -46,6 +46,11 @@ const VersionsGet = class VersionsGet {
46
46
  return this._metrics;
47
47
  }
48
48
 
49
+ /**
50
+ * @param {any} req
51
+ * @param {string} type
52
+ * @param {string} name
53
+ */
49
54
  async handler(req, type, name) {
50
55
  const end = this._histogram.timer();
51
56
 
@@ -55,7 +60,9 @@ const VersionsGet = class VersionsGet {
55
60
  validators.name(pName);
56
61
  validators.type(type);
57
62
  } catch (error) {
58
- this._log.debug(`pkg:latest - Validation failed - ${error.message}`);
63
+ this._log.debug(
64
+ `pkg:latest - Validation failed - ${error instanceof Error ? error.message : String(error)}`,
65
+ );
59
66
  const e = new HttpError.NotFound();
60
67
  end({ labels: { success: false, status: e.status } });
61
68
  throw e;
@@ -75,7 +82,7 @@ const VersionsGet = class VersionsGet {
75
82
  const path = createFilePathToVersion({ org, type, name: pName });
76
83
 
77
84
  try {
78
- const file = await this._sink.read(path);
85
+ const file = await (this._sink && this._sink.read(path));
79
86
  const outgoing = new HttpOutgoing();
80
87
  outgoing.cacheControl = this._cacheControl;
81
88
  outgoing.mimeType = "application/json";
@@ -91,14 +98,17 @@ const VersionsGet = class VersionsGet {
91
98
  outgoing.statusCode = 200;
92
99
  outgoing.stream = file.stream;
93
100
 
94
- outgoing.stream.on("error", (err) => {
95
- this._log.info(`pkg:latest - File stream error - ${err}`);
96
- end({ labels: { success: false, status: 503, type } });
97
- });
98
-
99
- outgoing.stream.on("end", () => {
100
- end({ labels: { status: outgoing.statusCode, type } });
101
- });
101
+ const outgoingStream = outgoing.stream;
102
+ if (outgoingStream) {
103
+ outgoingStream.on("error", (err) => {
104
+ this._log.info(`pkg:latest - File stream error - ${err}`);
105
+ end({ labels: { success: false, status: 503, type } });
106
+ });
107
+
108
+ outgoingStream.on("end", () => {
109
+ end({ labels: { status: outgoing.statusCode, type } });
110
+ });
111
+ }
102
112
  }
103
113
 
104
114
  this._log.debug(`pkg:latest - Package log found - Pathname: ${path}`);
@@ -1,4 +1,7 @@
1
1
  const FormFile = class FormFile {
2
+ /**
3
+ * @param {{ value?: any[], name?: string }} [options]
4
+ */
2
5
  constructor({ value = [], name = "" } = {}) {
3
6
  if (!Array.isArray(value))
4
7
  throw new TypeError('The argument "value" must be of type Array');
@@ -35,8 +35,12 @@ const MultipartParser = class MultipartParser {
35
35
  return "MultipartParser";
36
36
  }
37
37
 
38
+ /**
39
+ * @param {any} incoming
40
+ */
38
41
  parse(incoming) {
39
42
  return new Promise((resolve, reject) => {
43
+ /** @type {any[]} */
40
44
  const queue = [];
41
45
 
42
46
  const busboy = Busboy({
@@ -48,47 +52,57 @@ const MultipartParser = class MultipartParser {
48
52
  },
49
53
  });
50
54
 
51
- busboy.on("field", (name, value) => {
52
- if (!this._legalFields.includes(name.toLowerCase())) {
53
- busboy.emit("error", new HttpError.BadRequest());
54
- return;
55
- }
55
+ busboy.on(
56
+ "field",
57
+ (/** @type {any} */ name, /** @type {any} */ value) => {
58
+ if (!this._legalFields.includes(name.toLowerCase())) {
59
+ busboy.emit("error", new HttpError.BadRequest());
60
+ return;
61
+ }
56
62
 
57
- queue.push(
58
- this._handleField({
59
- value,
60
- name,
61
- }),
62
- );
63
- });
63
+ queue.push(
64
+ this._handleField({
65
+ value,
66
+ name,
67
+ }),
68
+ );
69
+ },
70
+ );
64
71
 
65
- busboy.on("file", (fieldname, file, filename) => {
66
- if (!this._legalFiles.includes(fieldname.toLowerCase())) {
67
- busboy.emit("error", new HttpError.BadRequest());
68
- return;
69
- }
72
+ busboy.on(
73
+ "file",
74
+ (
75
+ /** @type {any} */ fieldname,
76
+ /** @type {any} */ file,
77
+ /** @type {any} */ filename,
78
+ ) => {
79
+ if (!this._legalFiles.includes(fieldname.toLowerCase())) {
80
+ busboy.emit("error", new HttpError.BadRequest());
81
+ return;
82
+ }
70
83
 
71
- queue.push(
72
- new Promise((done) => {
73
- this._handleFile({
74
- fieldname,
75
- file,
76
- filename,
77
- incoming,
78
- })
79
- .then((item) => {
80
- done(item);
84
+ queue.push(
85
+ new Promise((done) => {
86
+ this._handleFile({
87
+ fieldname,
88
+ file,
89
+ filename,
90
+ incoming,
81
91
  })
82
- .catch((error) => {
83
- // Emit an error on busboy instead of rejecting
84
- // This will break and terminate the stream stright away
85
- busboy.emit("error", error);
86
- });
87
- }),
88
- );
89
- });
92
+ .then((item) => {
93
+ done(item);
94
+ })
95
+ .catch((error) => {
96
+ // Emit an error on busboy instead of rejecting
97
+ // This will break and terminate the stream stright away
98
+ busboy.emit("error", error);
99
+ });
100
+ }),
101
+ );
102
+ },
103
+ );
90
104
 
91
- busboy.once("error", (error) => {
105
+ busboy.once("error", (/** @type {any} */ error) => {
92
106
  reject(error);
93
107
  });
94
108
 
@@ -109,6 +123,9 @@ const MultipartParser = class MultipartParser {
109
123
  });
110
124
  }
111
125
 
126
+ /**
127
+ * @param {{ name: any, value: any }} options
128
+ */
112
129
  _handleField({ name, value }) {
113
130
  this._log.info(
114
131
  `multipart - Input field added - Name: ${name} - Value: ${value}`,
@@ -116,12 +133,16 @@ const MultipartParser = class MultipartParser {
116
133
  return new FormField({ name, value });
117
134
  }
118
135
 
136
+ /**
137
+ * @param {{ fieldname: any, file: any, filename: any, incoming: any }} options
138
+ */
119
139
  _handleFile({ fieldname, file, filename, incoming }) {
120
140
  return new Promise((resolve, reject) => {
121
141
  this._log.info(
122
142
  `multipart - Start extracting package - Field: ${fieldname} - Filename: ${filename}`,
123
143
  );
124
144
 
145
+ /** @type {Promise<any>[]} */
125
146
  const queue = [];
126
147
 
127
148
  file.once("limit", () => {
@@ -179,6 +200,9 @@ const MultipartParser = class MultipartParser {
179
200
  });
180
201
  }
181
202
 
203
+ /**
204
+ * @param {{ incoming: any, entry: any }} options
205
+ */
182
206
  _persistFile({ incoming, entry }) {
183
207
  // eslint-disable-next-line no-async-promise-executor
184
208
  return new Promise(async (resolve, reject) => {
@@ -198,11 +222,15 @@ const MultipartParser = class MultipartParser {
198
222
  );
199
223
 
200
224
  try {
225
+ if (!this._sink) {
226
+ reject(new Error("No sink configured"));
227
+ return;
228
+ }
201
229
  const writer = await this._sink.write(path, asset.mimeType);
202
230
 
203
231
  const integrityStream = ssri.integrityStream({ single: true });
204
232
  let hash = "";
205
- integrityStream.once("integrity", (integrity) => {
233
+ integrityStream.once("integrity", (/** @type {any} */ integrity) => {
206
234
  hash = integrity;
207
235
  });
208
236
 
@@ -1,6 +1,9 @@
1
1
  import crypto from "node:crypto";
2
2
 
3
3
  const Entry = class Entry {
4
+ /**
5
+ * @param {{ mimeType?: string, payload?: any[] }} [options]
6
+ */
4
7
  constructor({ mimeType = "application/octet-stream", payload = [] } = {}) {
5
8
  this._mimeType = mimeType;
6
9
  this._payload = payload;
package/lib/sinks/test.js CHANGED
@@ -32,16 +32,20 @@ export default class SinkTest extends Sink {
32
32
  },
33
33
  });
34
34
 
35
- // eslint-disable-next-line no-unused-vars
36
- this._writeDelayResolve = (a) => -1;
37
- // eslint-disable-next-line no-unused-vars
38
- this._writeDelayChunks = (a) => -1;
35
+ /** @type {(count?: number) => number} */
36
+ this._writeDelayResolve = () => -1;
37
+ /** @type {(count?: number) => number} */
38
+ this._writeDelayChunks = () => -1;
39
39
  }
40
40
 
41
41
  get metrics() {
42
42
  return this._metrics;
43
43
  }
44
44
 
45
+ /**
46
+ * @param {string} filePath
47
+ * @param {any} payload
48
+ */
45
49
  set(filePath, payload) {
46
50
  const pathname = toUrlPathname(path.join(this._rootPath, filePath));
47
51
  const mimeType = mime.getType(pathname) || "application/octet-stream";
@@ -57,6 +61,9 @@ export default class SinkTest extends Sink {
57
61
  this._state.set(pathname, entry);
58
62
  }
59
63
 
64
+ /**
65
+ * @param {string} filePath
66
+ */
60
67
  get(filePath) {
61
68
  const pathname = toUrlPathname(path.join(this._rootPath, filePath));
62
69
  if (this._state.has(pathname)) {
@@ -67,9 +74,12 @@ export default class SinkTest extends Sink {
67
74
  }
68
75
 
69
76
  dump() {
70
- return Array.from(this._state.entries());
77
+ return [...this._state.entries()];
71
78
  }
72
79
 
80
+ /**
81
+ * @param {any} items
82
+ */
73
83
  load(items) {
74
84
  if (!Array.isArray(items)) {
75
85
  throw new Error('Argument "items" must be an Array');
@@ -78,7 +88,7 @@ export default class SinkTest extends Sink {
78
88
  }
79
89
 
80
90
  /**
81
- * @param {(count: number) => number} fn
91
+ * @param {(count?: number) => number} fn
82
92
  */
83
93
  set writeDelayResolve(fn) {
84
94
  if (typeof fn !== "function") {
@@ -88,7 +98,7 @@ export default class SinkTest extends Sink {
88
98
  }
89
99
 
90
100
  /**
91
- * @param {(count: number) => number} fn
101
+ * @param {(count?: number) => number} fn
92
102
  */
93
103
  set writeDelayChunks(fn) {
94
104
  if (typeof fn !== "function") {
@@ -99,6 +109,10 @@ export default class SinkTest extends Sink {
99
109
 
100
110
  // Common SINK API
101
111
 
112
+ /**
113
+ * @param {string} filePath
114
+ * @param {string} contentType
115
+ */
102
116
  write(filePath, contentType) {
103
117
  return new Promise((resolve, reject) => {
104
118
  const operation = "write";
@@ -121,6 +135,7 @@ export default class SinkTest extends Sink {
121
135
  }
122
136
 
123
137
  const chunkDelay = this._writeDelayChunks;
138
+ /** @type {any[]} */
124
139
  const payload = [];
125
140
  let count = 0;
126
141
  const stream = new Writable({
@@ -168,6 +183,9 @@ export default class SinkTest extends Sink {
168
183
  });
169
184
  }
170
185
 
186
+ /**
187
+ * @param {string} filePath
188
+ */
171
189
  read(filePath) {
172
190
  return new Promise((resolve, reject) => {
173
191
  const operation = "read";
@@ -189,6 +207,10 @@ export default class SinkTest extends Sink {
189
207
  }
190
208
 
191
209
  const entry = this._state.get(pathname);
210
+ if (!entry) {
211
+ reject(new Error(`${filePath} does not exist`));
212
+ return;
213
+ }
192
214
  const payload = entry.payload || [];
193
215
  const file = new ReadFile({
194
216
  mimeType: entry.mimeType,
@@ -200,13 +222,13 @@ export default class SinkTest extends Sink {
200
222
  read() {
201
223
  if (readDelay) {
202
224
  setTimeout(() => {
203
- payload.forEach((item) => {
225
+ payload.forEach((/** @type {any} */ item) => {
204
226
  this.push(item);
205
227
  });
206
228
  this.push(null);
207
229
  }, readDelay);
208
230
  } else {
209
- payload.forEach((item) => {
231
+ payload.forEach((/** @type {any} */ item) => {
210
232
  this.push(item);
211
233
  });
212
234
  this.push(null);
@@ -228,6 +250,10 @@ export default class SinkTest extends Sink {
228
250
  });
229
251
  }
230
252
 
253
+ /**
254
+ * @param {string} filePath
255
+ * @returns {Promise<void>}
256
+ */
231
257
  delete(filePath) {
232
258
  return new Promise((resolve, reject) => {
233
259
  const operation = "delete";
@@ -249,7 +275,7 @@ export default class SinkTest extends Sink {
249
275
  }
250
276
 
251
277
  // Delete recursively
252
- Array.from(this._state.keys()).forEach((key) => {
278
+ [...this._state.keys()].forEach((key) => {
253
279
  if (key.startsWith(pathname)) {
254
280
  this._state.delete(key);
255
281
  }
@@ -267,6 +293,10 @@ export default class SinkTest extends Sink {
267
293
  });
268
294
  }
269
295
 
296
+ /**
297
+ * @param {string} filePath
298
+ * @returns {Promise<void>}
299
+ */
270
300
  exist(filePath) {
271
301
  return new Promise((resolve, reject) => {
272
302
  const operation = "exist";
@@ -1,9 +1,11 @@
1
1
  import { Writable, pipeline } from "node:stream";
2
2
  import { URL } from "node:url";
3
3
  import abslog from "abslog";
4
- import slug from "unique-slug";
4
+ import { randomBytes } from "node:crypto";
5
5
  import fs from "node:fs";
6
6
 
7
+ const slug = () => randomBytes(4).toString("hex");
8
+
7
9
  const fileReader = (file = "../../README.md") =>
8
10
  fs.createReadStream(new URL(file, import.meta.url));
9
11
 
@@ -23,8 +25,13 @@ const HealthCheck = class HealthCheck {
23
25
  this._log = abslog(logger);
24
26
  }
25
27
 
28
+ /** @returns {Promise<void>} */
26
29
  _write() {
27
30
  return new Promise((resolve, reject) => {
31
+ if (!this._sink) {
32
+ reject(new Error("No sink configured"));
33
+ return;
34
+ }
28
35
  this._sink
29
36
  .write(this._name, "text/plain")
30
37
  .then((destination) => {
@@ -40,15 +47,20 @@ const HealthCheck = class HealthCheck {
40
47
  });
41
48
  }
42
49
 
50
+ /** @returns {Promise<void>} */
43
51
  _read() {
44
52
  return new Promise((resolve, reject) => {
53
+ if (!this._sink) {
54
+ reject(new Error("No sink configured"));
55
+ return;
56
+ }
45
57
  this._sink
46
58
  .read(this._name)
47
59
  .then((source) => {
48
60
  const buffer = [];
49
61
  const destination = new Writable({
50
62
  objectMode: false,
51
- write(chunk, encoding, callback) {
63
+ write(chunk, _encoding, callback) {
52
64
  buffer.push(chunk);
53
65
  callback();
54
66
  },
@@ -66,10 +78,12 @@ const HealthCheck = class HealthCheck {
66
78
  }
67
79
 
68
80
  _delete() {
81
+ if (!this._sink) throw new Error("No sink configured");
69
82
  return this._sink.delete(this._name);
70
83
  }
71
84
 
72
85
  _exist() {
86
+ if (!this._sink) throw new Error("No sink configured");
73
87
  return this._sink.exist(this._name);
74
88
  }
75
89
 
@@ -1,9 +1,14 @@
1
1
  import { Writable, Readable, pipeline } from "node:stream";
2
2
 
3
+ /**
4
+ * @param {any} sink
5
+ * @param {string} path
6
+ */
3
7
  const readJSON = (sink, path) =>
4
8
  // eslint-disable-next-line no-async-promise-executor
5
9
  new Promise(async (resolve, reject) => {
6
10
  try {
11
+ /** @type {any[]} */
7
12
  const buffer = [];
8
13
  const from = await sink.read(path);
9
14
 
@@ -29,8 +34,18 @@ const readJSON = (sink, path) =>
29
34
  reject(error);
30
35
  }
31
36
  });
37
+ /**
38
+ * @param {any} sink
39
+ * @param {string} path
40
+ */
32
41
  const readEikJson = (sink, path) => sink.exist(path);
33
42
 
43
+ /**
44
+ * @param {any} sink
45
+ * @param {string} path
46
+ * @param {any} obj
47
+ * @param {string} contentType
48
+ */
34
49
  const writeJSON = (sink, path, obj, contentType) =>
35
50
  // eslint-disable-next-line no-async-promise-executor
36
51
  new Promise(async (resolve, reject) => {
@@ -55,8 +70,12 @@ const writeJSON = (sink, path, obj, contentType) =>
55
70
  reject(error);
56
71
  }
57
72
  });
73
+ /**
74
+ * @param {any} from
75
+ */
58
76
  const streamCollector = (from) =>
59
77
  new Promise((resolve, reject) => {
78
+ /** @type {any[]} */
60
79
  const buffer = [];
61
80
  const to = new Writable({
62
81
  write(chunk, encoding, cb) {
@@ -71,12 +90,18 @@ const streamCollector = (from) =>
71
90
  });
72
91
  });
73
92
 
93
+ /**
94
+ * @param {any} stat
95
+ */
74
96
  const etagFromFsStat = (stat) => {
75
97
  const mtime = stat.mtime.getTime().toString(16);
76
98
  const size = stat.size.toString(16);
77
99
  return `W/"${size}-${mtime}"`;
78
100
  };
79
101
 
102
+ /**
103
+ * @param {any} value
104
+ */
80
105
  const decodeUriComponent = (value) => {
81
106
  if (value === null || value === undefined) return value;
82
107
  return decodeURIComponent(value);