@eik/core 2.1.46 → 2.1.47
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.
- package/CHANGELOG.md +7 -0
- package/lib/classes/http-incoming.js +4 -0
- package/lib/classes/http-outgoing.js +4 -0
- package/lib/classes/package.js +8 -0
- package/lib/classes/versions.js +16 -2
- package/lib/handlers/alias.delete.js +13 -4
- package/lib/handlers/alias.get-v2.js +22 -10
- package/lib/handlers/alias.get.js +10 -1
- package/lib/handlers/alias.post.js +19 -4
- package/lib/handlers/alias.put.js +19 -4
- package/lib/handlers/auth.post.js +7 -1
- package/lib/handlers/map.get.js +20 -10
- package/lib/handlers/map.put.js +38 -13
- package/lib/handlers/pkg.get.js +22 -10
- package/lib/handlers/pkg.log.js +21 -10
- package/lib/handlers/pkg.put.js +26 -4
- package/lib/handlers/versions.get.js +20 -10
- package/lib/multipart/form-file.js +3 -0
- package/lib/multipart/parser.js +65 -37
- package/lib/sinks/mem-entry.js +3 -0
- package/lib/sinks/test.js +40 -10
- package/lib/utils/healthcheck.js +16 -2
- package/lib/utils/utils.js +25 -0
- package/package.json +16 -20
- package/types/classes/alias.d.ts +4 -4
- package/types/classes/asset.d.ts +5 -5
- package/types/classes/author.d.ts +2 -2
- package/types/classes/http-incoming.d.ts +4 -4
- package/types/classes/http-outgoing.d.ts +7 -3
- package/types/classes/meta.d.ts +2 -2
- package/types/classes/package.d.ts +136 -11
- package/types/classes/versions.d.ts +14 -4
- package/types/handlers/alias.delete.d.ts +13 -6
- package/types/handlers/alias.get-v2.d.ts +16 -9
- package/types/handlers/alias.get.d.ts +15 -8
- package/types/handlers/alias.post.d.ts +24 -11
- package/types/handlers/alias.put.d.ts +24 -11
- package/types/handlers/auth.post.d.ts +15 -9
- package/types/handlers/map.get.d.ts +14 -9
- package/types/handlers/map.put.d.ts +32 -13
- package/types/handlers/pkg.get.d.ts +16 -9
- package/types/handlers/pkg.log.d.ts +15 -9
- package/types/handlers/pkg.put.d.ts +35 -15
- package/types/handlers/versions.get.d.ts +14 -9
- package/types/main.d.ts +2 -0
- package/types/multipart/form-field.d.ts +2 -2
- package/types/multipart/parser.d.ts +18 -6
- package/types/sinks/test.d.ts +26 -13
- package/types/utils/healthcheck.d.ts +6 -4
- package/types/utils/path-builders-fs.d.ts +27 -27
- package/types/utils/path-builders-uri.d.ts +16 -16
- package/types/utils/utils.d.ts +26 -3
package/lib/handlers/pkg.log.js
CHANGED
|
@@ -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(
|
|
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
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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}`);
|
package/lib/handlers/pkg.put.js
CHANGED
|
@@ -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(
|
|
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(
|
|
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
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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}`);
|
package/lib/multipart/parser.js
CHANGED
|
@@ -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(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
queue.push(
|
|
64
|
+
this._handleField({
|
|
65
|
+
value,
|
|
66
|
+
name,
|
|
67
|
+
}),
|
|
68
|
+
);
|
|
69
|
+
},
|
|
70
|
+
);
|
|
64
71
|
|
|
65
|
-
busboy.on(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
|
package/lib/sinks/mem-entry.js
CHANGED
|
@@ -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
|
-
|
|
36
|
-
this._writeDelayResolve = (
|
|
37
|
-
|
|
38
|
-
this._writeDelayChunks = (
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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";
|
package/lib/utils/healthcheck.js
CHANGED
|
@@ -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
|
|
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,
|
|
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
|
|
package/lib/utils/utils.js
CHANGED
|
@@ -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);
|