@eik/core 2.3.0 → 2.4.1
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 +14 -0
- package/lib/multipart/parser.js +32 -38
- package/lib/sinks/test.js +57 -0
- package/lib/utils/utils.js +10 -53
- package/package.json +9 -9
- package/types/handlers/alias.post.d.ts +30 -1
- package/types/handlers/alias.put.d.ts +30 -1
- package/types/handlers/auth.post.d.ts +30 -1
- package/types/handlers/pkg.put.d.ts +30 -1
- package/types/multipart/parser.d.ts +30 -1
- package/types/utils/utils.d.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [2.4.1](https://github.com/eik-lib/core/compare/v2.4.0...v2.4.1) (2026-11-12)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **deps:** update dependency tar to v7.5.23 ([#656](https://github.com/eik-lib/core/issues/656)) ([0d678c5](https://github.com/eik-lib/core/commit/0d678c5b2d9386449d9da6cd6d467678bd4297ec))
|
|
7
|
+
|
|
8
|
+
# [2.4.0](https://github.com/eik-lib/core/compare/v2.3.0...v2.4.0) (2026-08-25)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* replace pipeline-based reads and writes with buffer operations ([#649](https://github.com/eik-lib/core/issues/649)) ([ace642f](https://github.com/eik-lib/core/commit/ace642f66ac08abcbda86c51b181bf107df68c5c))
|
|
14
|
+
|
|
1
15
|
# [2.3.0](https://github.com/eik-lib/core/compare/v2.2.3...v2.3.0) (2026-08-24)
|
|
2
16
|
|
|
3
17
|
|
package/lib/multipart/parser.js
CHANGED
|
@@ -345,53 +345,47 @@ const MultipartParser = class MultipartParser {
|
|
|
345
345
|
if (!this._sink) {
|
|
346
346
|
throw new Error("No sink configured");
|
|
347
347
|
}
|
|
348
|
-
const writer = await this._sink.write(assetPath, asset.mimeType, {
|
|
349
|
-
traceId,
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
const integrityStream = ssri.integrityStream({ single: true });
|
|
353
|
-
let hash = "";
|
|
354
|
-
integrityStream.once("integrity", (/** @type {any} */ integrity) => {
|
|
355
|
-
hash = integrity;
|
|
356
|
-
});
|
|
357
348
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
writer.destroy();
|
|
363
|
-
};
|
|
349
|
+
if (signal.aborted) {
|
|
350
|
+
entry.resume();
|
|
351
|
+
throw new Error("Aborted");
|
|
352
|
+
}
|
|
364
353
|
|
|
354
|
+
// Buffer all chunks from the tar entry — no pipeline or stream Transform needed
|
|
355
|
+
const chunks = [];
|
|
356
|
+
for await (const chunk of entry) {
|
|
365
357
|
if (signal.aborted) {
|
|
366
|
-
|
|
367
|
-
reject(new Error("Aborted"));
|
|
368
|
-
return;
|
|
358
|
+
throw new Error("Aborted");
|
|
369
359
|
}
|
|
360
|
+
chunks.push(chunk);
|
|
361
|
+
}
|
|
362
|
+
const buffer = Buffer.concat(chunks);
|
|
370
363
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
signal.removeEventListener("abort", onAbort);
|
|
375
|
-
|
|
376
|
-
if (error) {
|
|
377
|
-
this._log.error(
|
|
378
|
-
`multipart - Failed writing asset to sink - Pathname: ${assetPath} - TraceId: ${traceId}`,
|
|
379
|
-
);
|
|
380
|
-
this._log.trace(error);
|
|
364
|
+
if (signal.aborted) {
|
|
365
|
+
throw new Error("Aborted");
|
|
366
|
+
}
|
|
381
367
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
368
|
+
// Compute integrity hash directly from the buffer
|
|
369
|
+
const integrity = ssri.fromData(buffer, { single: true });
|
|
370
|
+
asset.integrity = integrity.toString();
|
|
385
371
|
|
|
386
|
-
|
|
372
|
+
try {
|
|
373
|
+
await this._sink.writeBuffer(assetPath, asset.mimeType, buffer, {
|
|
374
|
+
traceId,
|
|
375
|
+
});
|
|
376
|
+
} catch (error) {
|
|
377
|
+
this._log.error(
|
|
378
|
+
`multipart - Failed writing asset to sink - Pathname: ${assetPath} - TraceId: ${traceId}`,
|
|
379
|
+
);
|
|
380
|
+
this._log.trace(error);
|
|
381
|
+
throw error;
|
|
382
|
+
}
|
|
387
383
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
384
|
+
this._log.debug(
|
|
385
|
+
`multipart - Successfully wrote asset to sink - Pathname: ${assetPath} - TraceId: ${traceId}`,
|
|
386
|
+
);
|
|
391
387
|
|
|
392
|
-
|
|
393
|
-
});
|
|
394
|
-
});
|
|
388
|
+
return asset;
|
|
395
389
|
}
|
|
396
390
|
};
|
|
397
391
|
export default MultipartParser;
|
package/lib/sinks/test.js
CHANGED
|
@@ -109,6 +109,63 @@ export default class SinkTest extends Sink {
|
|
|
109
109
|
|
|
110
110
|
// Common SINK API
|
|
111
111
|
|
|
112
|
+
/**
|
|
113
|
+
* @param {string} filePath
|
|
114
|
+
* @param {string} contentType
|
|
115
|
+
* @param {Buffer} buffer
|
|
116
|
+
* @param {{ traceId?: string }} [options]
|
|
117
|
+
* @returns {Promise<void>}
|
|
118
|
+
*/
|
|
119
|
+
// eslint-disable-next-line no-unused-vars
|
|
120
|
+
async writeBuffer(filePath, contentType, buffer, options = {}) {
|
|
121
|
+
const operation = "write";
|
|
122
|
+
try {
|
|
123
|
+
Sink.validateFilePath(filePath);
|
|
124
|
+
Sink.validateContentType(contentType);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
this._counter.inc({ labels: { operation } });
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
const pathname = toUrlPathname(path.join(this._rootPath, filePath));
|
|
130
|
+
if (pathname.indexOf(this._rootPath) !== 0) {
|
|
131
|
+
this._counter.inc({ labels: { operation } });
|
|
132
|
+
throw new Error(`Directory traversal - ${filePath}`);
|
|
133
|
+
}
|
|
134
|
+
const entry = new Entry({ mimeType: contentType, payload: [buffer] });
|
|
135
|
+
this._state.set(pathname, entry);
|
|
136
|
+
this._counter.inc({ labels: { success: true, access: true, operation } });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* @param {string} filePath
|
|
141
|
+
* @returns {Promise<Buffer>}
|
|
142
|
+
*/
|
|
143
|
+
async readBuffer(filePath) {
|
|
144
|
+
const operation = "read";
|
|
145
|
+
try {
|
|
146
|
+
Sink.validateFilePath(filePath);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
this._counter.inc({ labels: { operation } });
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
const pathname = toUrlPathname(path.join(this._rootPath, filePath));
|
|
152
|
+
if (pathname.indexOf(this._rootPath) !== 0) {
|
|
153
|
+
this._counter.inc({ labels: { operation } });
|
|
154
|
+
throw new Error(`Directory traversal - ${filePath}`);
|
|
155
|
+
}
|
|
156
|
+
const entry = this._state.get(pathname);
|
|
157
|
+
if (!entry) {
|
|
158
|
+
this._counter.inc({ labels: { access: true, operation } });
|
|
159
|
+
throw new Error(`${filePath} does not exist`);
|
|
160
|
+
}
|
|
161
|
+
this._counter.inc({ labels: { success: true, access: true, operation } });
|
|
162
|
+
return Buffer.concat(
|
|
163
|
+
(entry.payload || []).map((/** @type {any} */ item) =>
|
|
164
|
+
Buffer.isBuffer(item) ? item : Buffer.from(item),
|
|
165
|
+
),
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
112
169
|
/**
|
|
113
170
|
* @param {string} filePath
|
|
114
171
|
* @param {string} contentType
|
package/lib/utils/utils.js
CHANGED
|
@@ -1,39 +1,14 @@
|
|
|
1
|
-
import { Writable,
|
|
1
|
+
import { Writable, pipeline } from "node:stream";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @param {any} sink
|
|
5
5
|
* @param {string} path
|
|
6
6
|
*/
|
|
7
|
-
const readJSON = (sink, path) =>
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
/** @type {any[]} */
|
|
12
|
-
const buffer = [];
|
|
13
|
-
const from = await sink.read(path);
|
|
14
|
-
|
|
15
|
-
const to = new Writable({
|
|
16
|
-
objectMode: false,
|
|
17
|
-
write(chunk, encoding, callback) {
|
|
18
|
-
buffer.push(chunk);
|
|
19
|
-
callback();
|
|
20
|
-
},
|
|
21
|
-
});
|
|
7
|
+
const readJSON = async (sink, path) => {
|
|
8
|
+
const buffer = await sink.readBuffer(path);
|
|
9
|
+
return JSON.parse(buffer.toString("utf8"));
|
|
10
|
+
};
|
|
22
11
|
|
|
23
|
-
pipeline(from.stream, to, (error) => {
|
|
24
|
-
if (error) return reject(error);
|
|
25
|
-
const str = Buffer.concat(buffer).toString("utf8");
|
|
26
|
-
try {
|
|
27
|
-
const obj = JSON.parse(str);
|
|
28
|
-
return resolve(obj);
|
|
29
|
-
} catch (err) {
|
|
30
|
-
return reject(err);
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
} catch (error) {
|
|
34
|
-
reject(error);
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
12
|
/**
|
|
38
13
|
* @param {any} sink
|
|
39
14
|
* @param {string} path
|
|
@@ -46,30 +21,12 @@ const readEikJson = (sink, path) => sink.exist(path);
|
|
|
46
21
|
* @param {any} obj
|
|
47
22
|
* @param {string} contentType
|
|
48
23
|
*/
|
|
49
|
-
const writeJSON = (sink, path, obj, contentType) =>
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const from = new Readable({
|
|
56
|
-
objectMode: false,
|
|
57
|
-
read() {
|
|
58
|
-
this.push(buffer);
|
|
59
|
-
this.push(null);
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
const to = await sink.write(path, contentType);
|
|
24
|
+
const writeJSON = async (sink, path, obj, contentType) => {
|
|
25
|
+
const buffer = Buffer.from(JSON.stringify(obj));
|
|
26
|
+
await sink.writeBuffer(path, contentType, buffer);
|
|
27
|
+
return buffer;
|
|
28
|
+
};
|
|
64
29
|
|
|
65
|
-
pipeline(from, to, (error) => {
|
|
66
|
-
if (error) return reject(error);
|
|
67
|
-
return resolve(buffer);
|
|
68
|
-
});
|
|
69
|
-
} catch (error) {
|
|
70
|
-
reject(error);
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
30
|
/**
|
|
74
31
|
* @param {any} from
|
|
75
32
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eik/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.1",
|
|
4
4
|
"description": "Core server package",
|
|
5
5
|
"main": "lib/main.js",
|
|
6
6
|
"types": "./types/main.d.ts",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"author": "",
|
|
30
30
|
"license": "MIT",
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@eik/sink": "1.
|
|
33
|
-
"@eik/sink-file-system": "2.
|
|
34
|
-
"@eik/sink-memory": "2.
|
|
32
|
+
"@eik/sink": "1.4.0",
|
|
33
|
+
"@eik/sink-file-system": "2.4.0",
|
|
34
|
+
"@eik/sink-memory": "2.4.0",
|
|
35
35
|
"@metrics/client": "2.5.5",
|
|
36
36
|
"abslog": "2.4.4",
|
|
37
37
|
"busboy": "1.6.0",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"original-url": "1.2.3",
|
|
41
41
|
"semver": "7.8.5",
|
|
42
42
|
"ssri": "14.0.0",
|
|
43
|
-
"tar": "7.5.
|
|
43
|
+
"tar": "7.5.23"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@eik/common": "5.2.1",
|
|
@@ -48,12 +48,12 @@
|
|
|
48
48
|
"@eik/prettier-config": "1.0.2",
|
|
49
49
|
"@eik/semantic-release-config": "1.0.19",
|
|
50
50
|
"@eik/typescript-config": "1.0.2",
|
|
51
|
-
"@types/readable-stream": "4.0.
|
|
52
|
-
"eslint": "10.
|
|
51
|
+
"@types/readable-stream": "4.0.25",
|
|
52
|
+
"eslint": "10.9.1",
|
|
53
53
|
"mkdirp": "3.0.1",
|
|
54
|
-
"npm-run-all2": "9.0.
|
|
54
|
+
"npm-run-all2": "9.0.4",
|
|
55
55
|
"prettier": "3.8.3",
|
|
56
|
-
"semantic-release": "25.0.
|
|
56
|
+
"semantic-release": "25.0.9",
|
|
57
57
|
"typescript": "6.0.3"
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -55,7 +55,36 @@ declare const AliasPost: {
|
|
|
55
55
|
incoming: any;
|
|
56
56
|
entry: any;
|
|
57
57
|
signal: AbortSignal;
|
|
58
|
-
}): Promise<
|
|
58
|
+
}): Promise<{
|
|
59
|
+
_mimeType: string;
|
|
60
|
+
_type: string;
|
|
61
|
+
_size: number;
|
|
62
|
+
_integrity: string;
|
|
63
|
+
_pathname: string;
|
|
64
|
+
_version: string;
|
|
65
|
+
_name: string;
|
|
66
|
+
_org: string;
|
|
67
|
+
get integrity(): string;
|
|
68
|
+
set integrity(value: string);
|
|
69
|
+
get pathname(): string;
|
|
70
|
+
get mimeType(): string;
|
|
71
|
+
get version(): string;
|
|
72
|
+
get asset(): string;
|
|
73
|
+
get name(): string;
|
|
74
|
+
get type(): string;
|
|
75
|
+
set type(value: string);
|
|
76
|
+
get size(): number;
|
|
77
|
+
set size(value: number);
|
|
78
|
+
get org(): string;
|
|
79
|
+
toJSON(): {
|
|
80
|
+
integrity: string;
|
|
81
|
+
pathname: string;
|
|
82
|
+
mimeType: string;
|
|
83
|
+
type: string;
|
|
84
|
+
size: number;
|
|
85
|
+
};
|
|
86
|
+
get [Symbol.toStringTag](): string;
|
|
87
|
+
}>;
|
|
59
88
|
get [Symbol.toStringTag](): string;
|
|
60
89
|
};
|
|
61
90
|
get metrics(): Metrics;
|
|
@@ -55,7 +55,36 @@ declare const AliasPut: {
|
|
|
55
55
|
incoming: any;
|
|
56
56
|
entry: any;
|
|
57
57
|
signal: AbortSignal;
|
|
58
|
-
}): Promise<
|
|
58
|
+
}): Promise<{
|
|
59
|
+
_mimeType: string;
|
|
60
|
+
_type: string;
|
|
61
|
+
_size: number;
|
|
62
|
+
_integrity: string;
|
|
63
|
+
_pathname: string;
|
|
64
|
+
_version: string;
|
|
65
|
+
_name: string;
|
|
66
|
+
_org: string;
|
|
67
|
+
get integrity(): string;
|
|
68
|
+
set integrity(value: string);
|
|
69
|
+
get pathname(): string;
|
|
70
|
+
get mimeType(): string;
|
|
71
|
+
get version(): string;
|
|
72
|
+
get asset(): string;
|
|
73
|
+
get name(): string;
|
|
74
|
+
get type(): string;
|
|
75
|
+
set type(value: string);
|
|
76
|
+
get size(): number;
|
|
77
|
+
set size(value: number);
|
|
78
|
+
get org(): string;
|
|
79
|
+
toJSON(): {
|
|
80
|
+
integrity: string;
|
|
81
|
+
pathname: string;
|
|
82
|
+
mimeType: string;
|
|
83
|
+
type: string;
|
|
84
|
+
size: number;
|
|
85
|
+
};
|
|
86
|
+
get [Symbol.toStringTag](): string;
|
|
87
|
+
}>;
|
|
59
88
|
get [Symbol.toStringTag](): string;
|
|
60
89
|
};
|
|
61
90
|
get metrics(): Metrics;
|
|
@@ -55,7 +55,36 @@ declare const AuthPost: {
|
|
|
55
55
|
incoming: any;
|
|
56
56
|
entry: any;
|
|
57
57
|
signal: AbortSignal;
|
|
58
|
-
}): Promise<
|
|
58
|
+
}): Promise<{
|
|
59
|
+
_mimeType: string;
|
|
60
|
+
_type: string;
|
|
61
|
+
_size: number;
|
|
62
|
+
_integrity: string;
|
|
63
|
+
_pathname: string;
|
|
64
|
+
_version: string;
|
|
65
|
+
_name: string;
|
|
66
|
+
_org: string;
|
|
67
|
+
get integrity(): string;
|
|
68
|
+
set integrity(value: string);
|
|
69
|
+
get pathname(): string;
|
|
70
|
+
get mimeType(): string;
|
|
71
|
+
get version(): string;
|
|
72
|
+
get asset(): string;
|
|
73
|
+
get name(): string;
|
|
74
|
+
get type(): string;
|
|
75
|
+
set type(value: string);
|
|
76
|
+
get size(): number;
|
|
77
|
+
set size(value: number);
|
|
78
|
+
get org(): string;
|
|
79
|
+
toJSON(): {
|
|
80
|
+
integrity: string;
|
|
81
|
+
pathname: string;
|
|
82
|
+
mimeType: string;
|
|
83
|
+
type: string;
|
|
84
|
+
size: number;
|
|
85
|
+
};
|
|
86
|
+
get [Symbol.toStringTag](): string;
|
|
87
|
+
}>;
|
|
59
88
|
get [Symbol.toStringTag](): string;
|
|
60
89
|
};
|
|
61
90
|
get metrics(): Metrics;
|
|
@@ -58,7 +58,36 @@ declare const PkgPut: {
|
|
|
58
58
|
incoming: any;
|
|
59
59
|
entry: any;
|
|
60
60
|
signal: AbortSignal;
|
|
61
|
-
}): Promise<
|
|
61
|
+
}): Promise<{
|
|
62
|
+
_mimeType: string;
|
|
63
|
+
_type: string;
|
|
64
|
+
_size: number;
|
|
65
|
+
_integrity: string;
|
|
66
|
+
_pathname: string;
|
|
67
|
+
_version: string;
|
|
68
|
+
_name: string;
|
|
69
|
+
_org: string;
|
|
70
|
+
get integrity(): string;
|
|
71
|
+
set integrity(value: string);
|
|
72
|
+
get pathname(): string;
|
|
73
|
+
get mimeType(): string;
|
|
74
|
+
get version(): string;
|
|
75
|
+
get asset(): string;
|
|
76
|
+
get name(): string;
|
|
77
|
+
get type(): string;
|
|
78
|
+
set type(value: string);
|
|
79
|
+
get size(): number;
|
|
80
|
+
set size(value: number);
|
|
81
|
+
get org(): string;
|
|
82
|
+
toJSON(): {
|
|
83
|
+
integrity: string;
|
|
84
|
+
pathname: string;
|
|
85
|
+
mimeType: string;
|
|
86
|
+
type: string;
|
|
87
|
+
size: number;
|
|
88
|
+
};
|
|
89
|
+
get [Symbol.toStringTag](): string;
|
|
90
|
+
}>;
|
|
62
91
|
get [Symbol.toStringTag](): string;
|
|
63
92
|
};
|
|
64
93
|
get metrics(): Metrics;
|
|
@@ -58,7 +58,36 @@ declare const MultipartParser: {
|
|
|
58
58
|
incoming: any;
|
|
59
59
|
entry: any;
|
|
60
60
|
signal: AbortSignal;
|
|
61
|
-
}): Promise<
|
|
61
|
+
}): Promise<{
|
|
62
|
+
_mimeType: string;
|
|
63
|
+
_type: string;
|
|
64
|
+
_size: number;
|
|
65
|
+
_integrity: string;
|
|
66
|
+
_pathname: string;
|
|
67
|
+
_version: string;
|
|
68
|
+
_name: string;
|
|
69
|
+
_org: string;
|
|
70
|
+
get integrity(): string;
|
|
71
|
+
set integrity(value: string);
|
|
72
|
+
get pathname(): string;
|
|
73
|
+
get mimeType(): string;
|
|
74
|
+
get version(): string;
|
|
75
|
+
get asset(): string;
|
|
76
|
+
get name(): string;
|
|
77
|
+
get type(): string;
|
|
78
|
+
set type(value: string);
|
|
79
|
+
get size(): number;
|
|
80
|
+
set size(value: number);
|
|
81
|
+
get org(): string;
|
|
82
|
+
toJSON(): {
|
|
83
|
+
integrity: string;
|
|
84
|
+
pathname: string;
|
|
85
|
+
mimeType: string;
|
|
86
|
+
type: string;
|
|
87
|
+
size: number;
|
|
88
|
+
};
|
|
89
|
+
get [Symbol.toStringTag](): string;
|
|
90
|
+
}>;
|
|
62
91
|
get [Symbol.toStringTag](): string;
|
|
63
92
|
};
|
|
64
93
|
};
|
package/types/utils/utils.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export function readJSON(sink: any, path: string): Promise<any>;
|
|
|
9
9
|
* @param {any} obj
|
|
10
10
|
* @param {string} contentType
|
|
11
11
|
*/
|
|
12
|
-
export function writeJSON(sink: any, path: string, obj: any, contentType: string): Promise<
|
|
12
|
+
export function writeJSON(sink: any, path: string, obj: any, contentType: string): Promise<Buffer<ArrayBuffer>>;
|
|
13
13
|
/**
|
|
14
14
|
* @param {any} from
|
|
15
15
|
*/
|