@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.
Files changed (64) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/lib/classes/alias.js +48 -48
  3. package/lib/classes/asset.js +99 -92
  4. package/lib/classes/author.js +20 -20
  5. package/lib/classes/http-incoming.js +52 -52
  6. package/lib/classes/http-outgoing.js +84 -83
  7. package/lib/classes/meta.js +20 -23
  8. package/lib/classes/package.js +73 -70
  9. package/lib/classes/versions.js +62 -60
  10. package/lib/handlers/alias.delete.js +125 -120
  11. package/lib/handlers/alias.get.js +92 -87
  12. package/lib/handlers/alias.post.js +196 -203
  13. package/lib/handlers/alias.put.js +196 -202
  14. package/lib/handlers/auth.post.js +95 -93
  15. package/lib/handlers/map.get.js +110 -111
  16. package/lib/handlers/map.put.js +256 -231
  17. package/lib/handlers/pkg.get.js +120 -122
  18. package/lib/handlers/pkg.log.js +112 -110
  19. package/lib/handlers/pkg.put.js +223 -213
  20. package/lib/handlers/versions.get.js +92 -101
  21. package/lib/main.js +47 -47
  22. package/lib/multipart/form-field.js +20 -23
  23. package/lib/multipart/form-file.js +22 -24
  24. package/lib/multipart/parser.js +231 -217
  25. package/lib/sinks/mem-entry.js +26 -31
  26. package/lib/sinks/test.js +287 -273
  27. package/lib/utils/defaults.js +11 -11
  28. package/lib/utils/globals.js +5 -5
  29. package/lib/utils/healthcheck.js +131 -108
  30. package/lib/utils/path-builders-fs.js +61 -29
  31. package/lib/utils/path-builders-uri.js +26 -18
  32. package/lib/utils/utils.js +76 -79
  33. package/package.json +22 -17
  34. package/types/classes/alias.d.ts +28 -0
  35. package/types/classes/asset.d.ts +48 -0
  36. package/types/classes/author.d.ts +17 -0
  37. package/types/classes/http-incoming.d.ts +37 -0
  38. package/types/classes/http-outgoing.d.ts +20 -0
  39. package/types/classes/meta.d.ts +17 -0
  40. package/types/classes/package.d.ts +40 -0
  41. package/types/classes/versions.d.ts +28 -0
  42. package/types/handlers/alias.delete.d.ts +33 -0
  43. package/types/handlers/alias.get.d.ts +48 -0
  44. package/types/handlers/alias.post.d.ts +83 -0
  45. package/types/handlers/alias.put.d.ts +83 -0
  46. package/types/handlers/auth.post.d.ts +82 -0
  47. package/types/handlers/map.get.d.ts +51 -0
  48. package/types/handlers/map.put.d.ts +78 -0
  49. package/types/handlers/pkg.get.d.ts +51 -0
  50. package/types/handlers/pkg.log.d.ts +51 -0
  51. package/types/handlers/pkg.put.d.ts +107 -0
  52. package/types/handlers/versions.get.d.ts +48 -0
  53. package/types/main.d.ts +44 -0
  54. package/types/multipart/form-field.d.ts +17 -0
  55. package/types/multipart/form-file.d.ts +17 -0
  56. package/types/multipart/parser.d.ts +52 -0
  57. package/types/sinks/mem-entry.d.ts +15 -0
  58. package/types/sinks/test.d.ts +32 -0
  59. package/types/utils/defaults.d.ts +9 -0
  60. package/types/utils/globals.d.ts +8 -0
  61. package/types/utils/healthcheck.d.ts +24 -0
  62. package/types/utils/path-builders-fs.d.ts +41 -0
  63. package/types/utils/path-builders-uri.d.ts +26 -0
  64. package/types/utils/utils.d.ts +6 -0
package/lib/sinks/test.js CHANGED
@@ -1,281 +1,295 @@
1
- import { Writable, Readable } from 'node:stream';
2
- import { ReadFile } from '@eik/common';
3
- import Metrics from '@metrics/client';
4
- import Sink from '@eik/sink';
5
- import mime from 'mime';
6
- import path from 'node:path';
1
+ import { Writable, Readable } from "node:stream";
2
+ import { ReadFile } from "@eik/common";
3
+ import Metrics from "@metrics/client";
4
+ import Sink from "@eik/sink";
5
+ import mime from "mime";
6
+ import path from "node:path";
7
7
 
8
- import Entry from './mem-entry.js';
8
+ import Entry from "./mem-entry.js";
9
9
 
10
- const DEFAULT_ROOT_PATH = '/eik';
10
+ const DEFAULT_ROOT_PATH = "/eik";
11
11
 
12
12
  /**
13
13
  * @deprecated Use eik/sink-memory or implement your own. This class will be removed in a future version of core.
14
14
  */
15
15
  export default class SinkTest extends Sink {
16
- constructor({ rootPath = DEFAULT_ROOT_PATH } = {}) {
17
- super();
18
- this._rootPath = rootPath;
19
- this._metrics = new Metrics();
20
- this._state = new Map();
21
-
22
- this._counter = this._metrics.counter({
23
- name: 'eik_core_sink_test',
24
- description: 'Counter measuring access to the in memory test storage sink',
25
- labels: {
26
- operation: 'n/a',
27
- success: false,
28
- access: false,
29
- },
30
- });
31
-
32
- this._writeDelayResolve = () => -1;
33
- this._writeDelayChunks = () => -1;
34
- }
35
-
36
- get metrics() {
37
- return this._metrics;
38
- }
39
-
40
- set(filePath, payload) {
41
- const pathname = path.join(this._rootPath, filePath);
42
- const mimeType = mime.getType(pathname) || 'application/octet-stream';
43
-
44
- let entry;
45
-
46
- if (Array.isArray(payload)) {
47
- entry = new Entry({ mimeType, payload, });
48
- } else {
49
- entry = new Entry({ mimeType, payload: [payload], });
50
- }
51
-
52
- this._state.set(pathname, entry);
53
- }
54
-
55
- get(filePath) {
56
- const pathname = path.join(this._rootPath, filePath);
57
- if (this._state.has(pathname)) {
58
- const entry = this._state.get(pathname);
59
- return entry.payload.join('');
60
- }
61
- return null;
62
- }
63
-
64
- dump() {
65
- return Array.from(this._state.entries());
66
- }
67
-
68
- load(items) {
69
- if (!Array.isArray(items)) {
70
- throw new Error('Argument "items" must be an Array');
71
- }
72
- this._state = new Map(items);
73
- }
74
-
75
- set writeDelayResolve(fn) {
76
- if (typeof fn !== "function") {
77
- throw new TypeError('Value must be a function');
78
- }
79
- this._writeDelayResolve = fn;
80
- }
81
-
82
- set writeDelayChunks(fn) {
83
- if (typeof fn !== "function") {
84
- throw new TypeError('Value must be a function');
85
- }
86
- this._writeDelayChunks = fn;
87
- }
88
-
89
- // Common SINK API
90
-
91
- write(filePath, contentType) {
92
- return new Promise((resolve, reject) => {
93
- const operation = 'write';
94
-
95
- try {
96
- super.constructor.validateFilePath(filePath);
97
- super.constructor.validateContentType(contentType);
98
- } catch (error) {
99
- this._counter.inc({ labels: { operation } });
100
- reject(error);
101
- return;
102
- }
103
-
104
- const pathname = path.join(this._rootPath, filePath);
105
-
106
- if (pathname.indexOf(this._rootPath) !== 0) {
107
- this._counter.inc({ labels: { operation } });
108
- reject(new Error(`Directory traversal - ${filePath}`));
109
- return;
110
- }
111
-
112
- const chunkDelay = this._writeDelayChunks;
113
- const payload = [];
114
- let count = 0;
115
- const stream = new Writable({
116
- write(chunk, encoding, cb) {
117
- const timeout = chunkDelay(count);
118
- count += 1;
119
-
120
- if (timeout < 0) {
121
- payload.push(chunk);
122
- cb();
123
- } else {
124
- setTimeout(() => {
125
- payload.push(chunk);
126
- cb();
127
- }, timeout);
128
- }
129
- },
130
- });
131
-
132
- stream.on('finish', () => {
133
- const entry = new Entry({
134
- mimeType: contentType,
135
- payload
136
- });
137
-
138
- this._state.set(pathname, entry);
139
-
140
- this._counter.inc({ labels: {
141
- success: true,
142
- access: true,
143
- operation
144
- } });
145
- });
146
-
147
- const resolveDelay = this._writeDelayResolve();
148
- if (resolveDelay < 0) {
149
- resolve(stream);
150
- } else {
151
- setTimeout(() => {
152
- resolve(stream);
153
- }, resolveDelay);
154
- }
155
- });
156
- }
157
-
158
- read(filePath) {
159
- return new Promise((resolve, reject) => {
160
- const operation = 'read';
161
-
162
- try {
163
- super.constructor.validateFilePath(filePath);
164
- } catch (error) {
165
- this._counter.inc({ labels: { operation } });
166
- reject(error);
167
- return;
168
- }
169
-
170
- const pathname = path.join(this._rootPath, filePath);
171
-
172
- if (pathname.indexOf(this._rootPath) !== 0) {
173
- this._counter.inc({ labels: { operation } });
174
- reject(new Error(`Directory traversal - ${filePath}`));
175
- return;
176
- }
177
-
178
- const entry = this._state.get(pathname);
179
- const payload = entry.payload || [];
180
- const file = new ReadFile({
181
- mimeType: entry.mimeType,
182
- etag: entry.hash,
183
- });
184
-
185
- file.stream = new Readable({
186
- read() {
187
- payload.forEach(item => {
188
- this.push(item);
189
- });
190
- this.push(null);
191
- },
192
- });
193
-
194
- file.stream.on('end', () => {
195
- this._counter.inc({ labels: {
196
- success: true,
197
- access: true,
198
- operation
199
- } });
200
- });
201
-
202
- resolve(file);
203
- });
204
- }
205
-
206
- delete(filePath) {
207
- return new Promise((resolve, reject) => {
208
- const operation = 'delete';
209
-
210
- try {
211
- super.constructor.validateFilePath(filePath);
212
- } catch (error) {
213
- this._counter.inc({ labels: { operation } });
214
- reject(error);
215
- return;
216
- }
217
-
218
- const pathname = path.join(this._rootPath, filePath);
219
-
220
- if (pathname.indexOf(this._rootPath) !== 0) {
221
- this._counter.inc({ labels: { operation } });
222
- reject(new Error(`Directory traversal - ${filePath}`));
223
- return;
224
- }
225
-
226
- // Delete recursively
227
- Array.from(this._state.keys()).forEach((key) => {
228
- if (key.startsWith(pathname)) {
229
- this._state.delete(key);
230
- }
231
- });
232
-
233
- this._counter.inc({ labels: {
234
- success: true,
235
- access: true,
236
- operation
237
- } });
238
-
239
- resolve();
240
- });
241
- }
242
-
243
- exist(filePath) {
244
- return new Promise((resolve, reject) => {
245
- const operation = 'exist';
246
-
247
- try {
248
- super.constructor.validateFilePath(filePath);
249
- } catch (error) {
250
- this._counter.inc({ labels: { operation } });
251
- reject(error);
252
- return;
253
- }
254
-
255
- const pathname = path.join(this._rootPath, filePath);
256
-
257
- if (pathname.indexOf(this._rootPath) !== 0) {
258
- this._counter.inc({ labels: { operation } });
259
- reject(new Error(`Directory traversal - ${filePath}`));
260
- return;
261
- }
262
-
263
- this._counter.inc({ labels: {
264
- success: true,
265
- access: true,
266
- operation
267
- } });
268
-
269
- if (this._state.has(pathname)) {
270
- resolve();
271
- return;
272
- }
273
- reject(new Error('File does not exist'));
274
- });
275
- }
276
-
277
-
278
- get [Symbol.toStringTag]() {
279
- return 'SinkTest';
280
- }
16
+ constructor({ rootPath = DEFAULT_ROOT_PATH } = {}) {
17
+ super();
18
+ this._rootPath = rootPath;
19
+ this._metrics = new Metrics();
20
+ this._state = new Map();
21
+
22
+ this._counter = this._metrics.counter({
23
+ name: "eik_core_sink_test",
24
+ description:
25
+ "Counter measuring access to the in memory test storage sink",
26
+ labels: {
27
+ operation: "n/a",
28
+ success: false,
29
+ access: false,
30
+ },
31
+ });
32
+
33
+ this._writeDelayResolve = (a = -1) => a;
34
+ this._writeDelayChunks = (a = -1) => a;
35
+ }
36
+
37
+ get metrics() {
38
+ return this._metrics;
39
+ }
40
+
41
+ set(filePath, payload) {
42
+ const pathname = path.join(this._rootPath, filePath);
43
+ const mimeType = mime.getType(pathname) || "application/octet-stream";
44
+
45
+ let entry;
46
+
47
+ if (Array.isArray(payload)) {
48
+ entry = new Entry({ mimeType, payload });
49
+ } else {
50
+ entry = new Entry({ mimeType, payload: [payload] });
51
+ }
52
+
53
+ this._state.set(pathname, entry);
54
+ }
55
+
56
+ get(filePath) {
57
+ const pathname = path.join(this._rootPath, filePath);
58
+ if (this._state.has(pathname)) {
59
+ const entry = this._state.get(pathname);
60
+ return entry.payload.join("");
61
+ }
62
+ return null;
63
+ }
64
+
65
+ dump() {
66
+ return Array.from(this._state.entries());
67
+ }
68
+
69
+ load(items) {
70
+ if (!Array.isArray(items)) {
71
+ throw new Error('Argument "items" must be an Array');
72
+ }
73
+ this._state = new Map(items);
74
+ }
75
+
76
+ /**
77
+ * @param {(count: number) => number} fn
78
+ */
79
+ set writeDelayResolve(fn) {
80
+ if (typeof fn !== "function") {
81
+ throw new TypeError("Value must be a function");
82
+ }
83
+ this._writeDelayResolve = fn;
84
+ }
85
+
86
+ /**
87
+ * @param {(count: number) => number} fn
88
+ */
89
+ set writeDelayChunks(fn) {
90
+ if (typeof fn !== "function") {
91
+ throw new TypeError("Value must be a function");
92
+ }
93
+ this._writeDelayChunks = fn;
94
+ }
95
+
96
+ // Common SINK API
97
+
98
+ write(filePath, contentType) {
99
+ return new Promise((resolve, reject) => {
100
+ const operation = "write";
101
+
102
+ try {
103
+ Sink.validateFilePath(filePath);
104
+ Sink.validateContentType(contentType);
105
+ } catch (error) {
106
+ this._counter.inc({ labels: { operation } });
107
+ reject(error);
108
+ return;
109
+ }
110
+
111
+ const pathname = path.join(this._rootPath, filePath);
112
+
113
+ if (pathname.indexOf(this._rootPath) !== 0) {
114
+ this._counter.inc({ labels: { operation } });
115
+ reject(new Error(`Directory traversal - ${filePath}`));
116
+ return;
117
+ }
118
+
119
+ const chunkDelay = this._writeDelayChunks;
120
+ const payload = [];
121
+ let count = 0;
122
+ const stream = new Writable({
123
+ write(chunk, encoding, cb) {
124
+ const timeout = chunkDelay(count);
125
+ count += 1;
126
+
127
+ if (timeout < 0) {
128
+ payload.push(chunk);
129
+ cb();
130
+ } else {
131
+ setTimeout(() => {
132
+ payload.push(chunk);
133
+ cb();
134
+ }, timeout);
135
+ }
136
+ },
137
+ });
138
+
139
+ stream.on("finish", () => {
140
+ const entry = new Entry({
141
+ mimeType: contentType,
142
+ payload,
143
+ });
144
+
145
+ this._state.set(pathname, entry);
146
+
147
+ this._counter.inc({
148
+ labels: {
149
+ success: true,
150
+ access: true,
151
+ operation,
152
+ },
153
+ });
154
+ });
155
+
156
+ const resolveDelay = this._writeDelayResolve();
157
+ if (resolveDelay < 0) {
158
+ resolve(stream);
159
+ } else {
160
+ setTimeout(() => {
161
+ resolve(stream);
162
+ }, resolveDelay);
163
+ }
164
+ });
165
+ }
166
+
167
+ read(filePath) {
168
+ return new Promise((resolve, reject) => {
169
+ const operation = "read";
170
+
171
+ try {
172
+ Sink.validateFilePath(filePath);
173
+ } catch (error) {
174
+ this._counter.inc({ labels: { operation } });
175
+ reject(error);
176
+ return;
177
+ }
178
+
179
+ const pathname = path.join(this._rootPath, filePath);
180
+
181
+ if (pathname.indexOf(this._rootPath) !== 0) {
182
+ this._counter.inc({ labels: { operation } });
183
+ reject(new Error(`Directory traversal - ${filePath}`));
184
+ return;
185
+ }
186
+
187
+ const entry = this._state.get(pathname);
188
+ const payload = entry.payload || [];
189
+ const file = new ReadFile({
190
+ mimeType: entry.mimeType,
191
+ etag: entry.hash,
192
+ });
193
+
194
+ file.stream = new Readable({
195
+ read() {
196
+ payload.forEach((item) => {
197
+ this.push(item);
198
+ });
199
+ this.push(null);
200
+ },
201
+ });
202
+
203
+ file.stream.on("end", () => {
204
+ this._counter.inc({
205
+ labels: {
206
+ success: true,
207
+ access: true,
208
+ operation,
209
+ },
210
+ });
211
+ });
212
+
213
+ resolve(file);
214
+ });
215
+ }
216
+
217
+ delete(filePath) {
218
+ return new Promise((resolve, reject) => {
219
+ const operation = "delete";
220
+
221
+ try {
222
+ Sink.validateFilePath(filePath);
223
+ } catch (error) {
224
+ this._counter.inc({ labels: { operation } });
225
+ reject(error);
226
+ return;
227
+ }
228
+
229
+ const pathname = path.join(this._rootPath, filePath);
230
+
231
+ if (pathname.indexOf(this._rootPath) !== 0) {
232
+ this._counter.inc({ labels: { operation } });
233
+ reject(new Error(`Directory traversal - ${filePath}`));
234
+ return;
235
+ }
236
+
237
+ // Delete recursively
238
+ Array.from(this._state.keys()).forEach((key) => {
239
+ if (key.startsWith(pathname)) {
240
+ this._state.delete(key);
241
+ }
242
+ });
243
+
244
+ this._counter.inc({
245
+ labels: {
246
+ success: true,
247
+ access: true,
248
+ operation,
249
+ },
250
+ });
251
+
252
+ resolve();
253
+ });
254
+ }
255
+
256
+ exist(filePath) {
257
+ return new Promise((resolve, reject) => {
258
+ const operation = "exist";
259
+
260
+ try {
261
+ Sink.validateFilePath(filePath);
262
+ } catch (error) {
263
+ this._counter.inc({ labels: { operation } });
264
+ reject(error);
265
+ return;
266
+ }
267
+
268
+ const pathname = path.join(this._rootPath, filePath);
269
+
270
+ if (pathname.indexOf(this._rootPath) !== 0) {
271
+ this._counter.inc({ labels: { operation } });
272
+ reject(new Error(`Directory traversal - ${filePath}`));
273
+ return;
274
+ }
275
+
276
+ this._counter.inc({
277
+ labels: {
278
+ success: true,
279
+ access: true,
280
+ operation,
281
+ },
282
+ });
283
+
284
+ if (this._state.has(pathname)) {
285
+ resolve();
286
+ return;
287
+ }
288
+ reject(new Error("File does not exist"));
289
+ });
290
+ }
291
+
292
+ get [Symbol.toStringTag]() {
293
+ return "SinkTest";
294
+ }
281
295
  }
@@ -1,16 +1,16 @@
1
- import path from 'node:path';
2
- import os from 'node:os';
1
+ import path from "node:path";
2
+ import os from "node:os";
3
3
 
4
4
  const config = {
5
- authKey: 'change_me',
6
- pkgMaxFileSize: 10000000,
7
- mapMaxFileSize: 1000000,
8
- sinkFsRootPath: path.join(os.tmpdir(), '/eik-files'),
9
- etag: true,
10
- organizations: [
11
- ['localhost', 'local'],
12
- ['127.0.0.1', 'local'],
13
- ],
5
+ authKey: "change_me",
6
+ pkgMaxFileSize: 10000000,
7
+ mapMaxFileSize: 1000000,
8
+ sinkFsRootPath: path.join(os.tmpdir(), "/eik-files"),
9
+ etag: true,
10
+ organizations: /** @type {Array<[string, string]>} */ ([
11
+ ["localhost", "local"],
12
+ ["127.0.0.1", "local"],
13
+ ]),
14
14
  };
15
15
 
16
16
  export default config;
@@ -1,8 +1,8 @@
1
1
  const globals = {
2
- BASE_AUTHENTICATION: 'auth',
3
- BASE_IMPORT_MAPS: 'map',
4
- BASE_PACKAGES: 'pkg',
5
- BASE_NPM: 'npm',
6
- ROOT: '/',
2
+ BASE_AUTHENTICATION: "auth",
3
+ BASE_IMPORT_MAPS: "map",
4
+ BASE_PACKAGES: "pkg",
5
+ BASE_NPM: "npm",
6
+ ROOT: "/",
7
7
  };
8
8
  export default globals;