@eik/core 1.3.53 → 1.3.55
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/alias.js +48 -48
- package/lib/classes/asset.js +99 -92
- package/lib/classes/author.js +20 -20
- package/lib/classes/http-incoming.js +52 -52
- package/lib/classes/http-outgoing.js +84 -83
- package/lib/classes/meta.js +20 -23
- package/lib/classes/package.js +73 -70
- package/lib/classes/versions.js +62 -60
- package/lib/handlers/alias.delete.js +125 -120
- package/lib/handlers/alias.get.js +92 -87
- package/lib/handlers/alias.post.js +196 -203
- package/lib/handlers/alias.put.js +196 -202
- package/lib/handlers/auth.post.js +95 -93
- package/lib/handlers/map.get.js +110 -111
- package/lib/handlers/map.put.js +256 -231
- package/lib/handlers/pkg.get.js +120 -122
- package/lib/handlers/pkg.log.js +112 -110
- package/lib/handlers/pkg.put.js +223 -213
- package/lib/handlers/versions.get.js +92 -101
- package/lib/main.js +47 -47
- package/lib/multipart/form-field.js +20 -23
- package/lib/multipart/form-file.js +22 -24
- package/lib/multipart/parser.js +231 -217
- package/lib/sinks/mem-entry.js +26 -31
- package/lib/sinks/test.js +287 -273
- package/lib/utils/defaults.js +11 -11
- package/lib/utils/globals.js +5 -5
- package/lib/utils/healthcheck.js +131 -108
- package/lib/utils/path-builders-fs.js +61 -29
- package/lib/utils/path-builders-uri.js +26 -18
- package/lib/utils/utils.js +76 -79
- package/package.json +22 -17
- package/types/classes/alias.d.ts +28 -0
- package/types/classes/asset.d.ts +48 -0
- package/types/classes/author.d.ts +17 -0
- package/types/classes/http-incoming.d.ts +37 -0
- package/types/classes/http-outgoing.d.ts +20 -0
- package/types/classes/meta.d.ts +17 -0
- package/types/classes/package.d.ts +40 -0
- package/types/classes/versions.d.ts +28 -0
- package/types/handlers/alias.delete.d.ts +33 -0
- package/types/handlers/alias.get.d.ts +48 -0
- package/types/handlers/alias.post.d.ts +83 -0
- package/types/handlers/alias.put.d.ts +83 -0
- package/types/handlers/auth.post.d.ts +82 -0
- package/types/handlers/map.get.d.ts +51 -0
- package/types/handlers/map.put.d.ts +78 -0
- package/types/handlers/pkg.get.d.ts +51 -0
- package/types/handlers/pkg.log.d.ts +51 -0
- package/types/handlers/pkg.put.d.ts +107 -0
- package/types/handlers/versions.get.d.ts +48 -0
- package/types/main.d.ts +44 -0
- package/types/multipart/form-field.d.ts +17 -0
- package/types/multipart/form-file.d.ts +17 -0
- package/types/multipart/parser.d.ts +52 -0
- package/types/sinks/mem-entry.d.ts +15 -0
- package/types/sinks/test.d.ts +32 -0
- package/types/utils/defaults.d.ts +9 -0
- package/types/utils/globals.d.ts +8 -0
- package/types/utils/healthcheck.d.ts +24 -0
- package/types/utils/path-builders-fs.d.ts +41 -0
- package/types/utils/path-builders-uri.d.ts +26 -0
- package/types/utils/utils.d.ts +6 -0
package/lib/multipart/parser.js
CHANGED
|
@@ -1,220 +1,234 @@
|
|
|
1
|
-
import { pipeline } from
|
|
2
|
-
import HttpError from
|
|
3
|
-
import Busboy from
|
|
4
|
-
import abslog from
|
|
5
|
-
import ssri from
|
|
6
|
-
import tar from
|
|
7
|
-
|
|
8
|
-
import { createFilePathToAsset } from
|
|
9
|
-
import FormField from
|
|
10
|
-
import FormFile from
|
|
11
|
-
import Asset from
|
|
1
|
+
import { pipeline } from "node:stream";
|
|
2
|
+
import HttpError from "http-errors";
|
|
3
|
+
import Busboy from "busboy";
|
|
4
|
+
import abslog from "abslog";
|
|
5
|
+
import ssri from "ssri";
|
|
6
|
+
import tar from "tar";
|
|
7
|
+
|
|
8
|
+
import { createFilePathToAsset } from "../utils/path-builders-fs.js";
|
|
9
|
+
import FormField from "./form-field.js";
|
|
10
|
+
import FormFile from "./form-file.js";
|
|
11
|
+
import Asset from "../classes/asset.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {object} MultipartParserOptions
|
|
15
|
+
* @property {number} [pkgMaxFileSize=10000000]
|
|
16
|
+
* @property {string[]} [legalFields]
|
|
17
|
+
* @property {string[]} [legalFiles]
|
|
18
|
+
* @property {import("@eik/sink").default} [sink]
|
|
19
|
+
* @property {import("abslog").AbstractLoggerOptions} [logger]
|
|
20
|
+
*/
|
|
12
21
|
|
|
13
22
|
const MultipartParser = class MultipartParser {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
23
|
+
/**
|
|
24
|
+
* @param {MultipartParserOptions} options
|
|
25
|
+
*/
|
|
26
|
+
constructor({ pkgMaxFileSize, legalFields, legalFiles, logger, sink } = {}) {
|
|
27
|
+
this._pkgMaxFileSize = pkgMaxFileSize;
|
|
28
|
+
this._legalFields = legalFields || [];
|
|
29
|
+
this._legalFiles = legalFiles || [];
|
|
30
|
+
this._sink = sink;
|
|
31
|
+
this._log = abslog(logger);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get [Symbol.toStringTag]() {
|
|
35
|
+
return "MultipartParser";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
parse(incoming) {
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
const queue = [];
|
|
41
|
+
|
|
42
|
+
const busboy = Busboy({
|
|
43
|
+
headers: incoming.headers,
|
|
44
|
+
limits: {
|
|
45
|
+
fileSize: this._pkgMaxFileSize,
|
|
46
|
+
fields: this._legalFields.length,
|
|
47
|
+
files: this._legalFiles.length,
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
busboy.on("field", (name, value) => {
|
|
52
|
+
if (!this._legalFields.includes(name.toLowerCase())) {
|
|
53
|
+
busboy.emit("error", new HttpError.BadRequest());
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
queue.push(
|
|
58
|
+
this._handleField({
|
|
59
|
+
value,
|
|
60
|
+
name,
|
|
61
|
+
}),
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
busboy.on("file", (fieldname, file, filename) => {
|
|
66
|
+
if (!this._legalFiles.includes(fieldname.toLowerCase())) {
|
|
67
|
+
busboy.emit("error", new HttpError.BadRequest());
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
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);
|
|
81
|
+
})
|
|
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
|
+
});
|
|
90
|
+
|
|
91
|
+
busboy.once("error", (error) => {
|
|
92
|
+
reject(error);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
busboy.once("close", () => {
|
|
96
|
+
Promise.all(queue)
|
|
97
|
+
.then((items) => {
|
|
98
|
+
resolve(items);
|
|
99
|
+
})
|
|
100
|
+
.catch((error) => {
|
|
101
|
+
reject(error);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// If incoming.request is handeled by pipeline, it will close
|
|
106
|
+
// to early for the http framework to handle it. Let the
|
|
107
|
+
// http framework handle closing incoming.request
|
|
108
|
+
incoming.request.pipe(busboy);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
_handleField({ name, value }) {
|
|
113
|
+
this._log.info(
|
|
114
|
+
`multipart - Input field added - Name: ${name} - Value: ${value}`,
|
|
115
|
+
);
|
|
116
|
+
return new FormField({ name, value });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
_handleFile({ fieldname, file, filename, incoming }) {
|
|
120
|
+
return new Promise((resolve, reject) => {
|
|
121
|
+
this._log.info(
|
|
122
|
+
`multipart - Start extracting package - Field: ${fieldname} - Filename: ${filename}`,
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const queue = [];
|
|
126
|
+
|
|
127
|
+
file.once("limit", () => {
|
|
128
|
+
this._log.info(
|
|
129
|
+
`multipart - Uploaded package exceeds legal file size limit - Field: ${fieldname} - Filename: ${filename}`,
|
|
130
|
+
);
|
|
131
|
+
file.emit("error", new HttpError.PayloadTooLarge());
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const extract = new tar.Parse({
|
|
135
|
+
strict: true,
|
|
136
|
+
onentry: (entry) => {
|
|
137
|
+
// Entries not supported must be thrown
|
|
138
|
+
// away for extraction to continue
|
|
139
|
+
if (entry.type.toLowerCase() !== "file") {
|
|
140
|
+
entry.resume();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
queue.push(this._persistFile({ incoming, entry }));
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
pipeline(file, extract, (error) => {
|
|
148
|
+
if (error) {
|
|
149
|
+
this._log.info(
|
|
150
|
+
`multipart - Uploaded package could not be extracted properly - Field: ${fieldname} - Filename: ${filename}`,
|
|
151
|
+
);
|
|
152
|
+
this._log.trace(error);
|
|
153
|
+
|
|
154
|
+
switch (error.code) {
|
|
155
|
+
case "TAR_BAD_ARCHIVE":
|
|
156
|
+
case "TAR_ABORT":
|
|
157
|
+
reject(new HttpError.UnsupportedMediaType());
|
|
158
|
+
break;
|
|
159
|
+
case "TAR_ENTRY_UNSUPPORTED":
|
|
160
|
+
case "TAR_ENTRY_INVALID":
|
|
161
|
+
case "TAR_ENTRY_ERROR":
|
|
162
|
+
reject(new HttpError.UnprocessableEntity());
|
|
163
|
+
break;
|
|
164
|
+
default:
|
|
165
|
+
reject(error);
|
|
166
|
+
}
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
Promise.all(queue)
|
|
171
|
+
.then((result) => {
|
|
172
|
+
const formFile = new FormFile({ name: fieldname, value: result });
|
|
173
|
+
resolve(formFile);
|
|
174
|
+
})
|
|
175
|
+
.catch((err) => {
|
|
176
|
+
reject(err);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
_persistFile({ incoming, entry }) {
|
|
183
|
+
// eslint-disable-next-line no-async-promise-executor
|
|
184
|
+
return new Promise(async (resolve, reject) => {
|
|
185
|
+
const asset = new Asset({
|
|
186
|
+
pathname: entry.path,
|
|
187
|
+
version: incoming.version,
|
|
188
|
+
name: incoming.name,
|
|
189
|
+
type: incoming.type,
|
|
190
|
+
org: incoming.org,
|
|
191
|
+
});
|
|
192
|
+
asset.size = entry.size;
|
|
193
|
+
|
|
194
|
+
const path = createFilePathToAsset(asset);
|
|
195
|
+
|
|
196
|
+
this._log.info(
|
|
197
|
+
`multipart - Start writing asset to sink - Pathname: ${path}`,
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
const writer = await this._sink.write(path, asset.mimeType);
|
|
202
|
+
|
|
203
|
+
const integrityStream = ssri.integrityStream({ single: true });
|
|
204
|
+
let hash = "";
|
|
205
|
+
integrityStream.once("integrity", (integrity) => {
|
|
206
|
+
hash = integrity;
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
pipeline(entry, integrityStream, writer, (error) => {
|
|
210
|
+
if (error) {
|
|
211
|
+
this._log.error(
|
|
212
|
+
`multipart - Failed writing asset to sink - Pathname: ${path}`,
|
|
213
|
+
);
|
|
214
|
+
this._log.trace(error);
|
|
215
|
+
|
|
216
|
+
reject(error);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
asset.integrity = hash.toString();
|
|
221
|
+
|
|
222
|
+
this._log.info(
|
|
223
|
+
`multipart - Successfully wrote asset to sink - Pathname: ${path}`,
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
resolve(asset);
|
|
227
|
+
});
|
|
228
|
+
} catch (error) {
|
|
229
|
+
reject(error);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
};
|
|
220
234
|
export default MultipartParser;
|
package/lib/sinks/mem-entry.js
CHANGED
|
@@ -1,40 +1,35 @@
|
|
|
1
|
-
import crypto from
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
2
|
|
|
3
3
|
const Entry = class Entry {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
this._mimeType = mimeType;
|
|
9
|
-
this._payload = payload;
|
|
10
|
-
this._hash = '';
|
|
4
|
+
constructor({ mimeType = "application/octet-stream", payload = [] } = {}) {
|
|
5
|
+
this._mimeType = mimeType;
|
|
6
|
+
this._payload = payload;
|
|
7
|
+
this._hash = "";
|
|
11
8
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
}
|
|
9
|
+
if (Array.isArray(payload)) {
|
|
10
|
+
const hash = crypto.createHash("sha512");
|
|
11
|
+
payload.forEach((buffer) => {
|
|
12
|
+
hash.update(buffer.toString());
|
|
13
|
+
});
|
|
14
|
+
this._hash = `sha512-${hash.digest("base64")}`;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
22
17
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
get mimeType() {
|
|
19
|
+
return this._mimeType;
|
|
20
|
+
}
|
|
26
21
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
get payload() {
|
|
23
|
+
return this._payload;
|
|
24
|
+
}
|
|
30
25
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
get hash() {
|
|
27
|
+
return this._hash;
|
|
28
|
+
}
|
|
34
29
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
30
|
+
get [Symbol.toStringTag]() {
|
|
31
|
+
return "Entry";
|
|
32
|
+
}
|
|
33
|
+
};
|
|
39
34
|
|
|
40
35
|
export default Entry;
|