@eik/core 2.1.71 → 2.1.73

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 CHANGED
@@ -1,3 +1,17 @@
1
+ ## [2.1.73](https://github.com/eik-lib/core/compare/v2.1.72...v2.1.73) (2026-08-14)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * write eik.json server-side as the final committed marker for package uploads ([#632](https://github.com/eik-lib/core/issues/632)) ([4461f76](https://github.com/eik-lib/core/commit/4461f76881886992db70848d42c7d6a199397c8f)), closes [#4](https://github.com/eik-lib/core/issues/4) [#5](https://github.com/eik-lib/core/issues/5)
7
+
8
+ ## [2.1.72](https://github.com/eik-lib/core/compare/v2.1.71...v2.1.72) (2026-08-14)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * upgrade size-limit log from info to warn and include package details ([#631](https://github.com/eik-lib/core/issues/631)) ([e324b5e](https://github.com/eik-lib/core/commit/e324b5e6cab52c788535d3c54b888e0514e40546))
14
+
1
15
  ## [2.1.71](https://github.com/eik-lib/core/compare/v2.1.70...v2.1.71) (2026-08-13)
2
16
 
3
17
 
@@ -82,19 +82,22 @@ const PkgPut = class PkgPut {
82
82
  .parse(incoming)
83
83
  .then((result) => {
84
84
  const pkg = new Package(incoming);
85
+ /** @type {Record<string,any>|null} */
86
+ let eikJson = null;
85
87
  result.forEach((/** @type {any} */ obj) => {
86
88
  if (obj.constructor.name === "FormField") {
87
89
  pkg.setMeta(obj);
88
90
  }
89
91
  if (obj.constructor.name === "FormFile") {
92
+ eikJson = obj.eikJson;
90
93
  obj.value.forEach((/** @type {any} */ o) => {
91
94
  pkg.setAsset(o);
92
95
  });
93
96
  }
94
97
  });
95
- return pkg;
98
+ return { pkg, eikJson };
96
99
  })
97
- .then(async (pkg) => {
100
+ .then(async ({ pkg, eikJson }) => {
98
101
  const path = createFilePathToPackage(pkg);
99
102
  await writeJSON(this._sink, path, pkg, "application/json");
100
103
 
@@ -102,7 +105,7 @@ const PkgPut = class PkgPut {
102
105
  `pkg:put - Successfully wrote package meta file to sink - Pathname: ${path}`,
103
106
  );
104
107
 
105
- resolve(pkg);
108
+ resolve({ pkg, eikJson });
106
109
  })
107
110
  .catch((error) => {
108
111
  reject(error);
@@ -110,6 +113,29 @@ const PkgPut = class PkgPut {
110
113
  });
111
114
  }
112
115
 
116
+ /**
117
+ * Writes eik.json to the sink as the final committed marker for a version.
118
+ * Merges any fields from the client's eik.json (captured from the tar) with
119
+ * server-authoritative values, ensuring the file is always server-written
120
+ * regardless of what the client's tar contained.
121
+ *
122
+ * @param {any} incoming
123
+ * @param {Record<string,any>|null} capturedEikJson
124
+ */
125
+ async _writeEikJson(incoming, capturedEikJson) {
126
+ const content = {
127
+ ...capturedEikJson,
128
+ name: incoming.name,
129
+ version: incoming.version,
130
+ type: incoming.type,
131
+ };
132
+ const eikJsonPath = createFilePathToEikJson(incoming);
133
+ await writeJSON(this._sink, eikJsonPath, content, "application/json");
134
+ this._log.info(
135
+ `pkg:put - Successfully wrote eik.json to sink - Pathname: ${eikJsonPath}`,
136
+ );
137
+ }
138
+
113
139
  /**
114
140
  * @param {any} incoming
115
141
  */
@@ -226,7 +252,7 @@ const PkgPut = class PkgPut {
226
252
  }
227
253
 
228
254
  const versions = await this._readVersions(incoming);
229
- const pkg = await this._parser(incoming);
255
+ const { pkg, eikJson } = await this._parser(incoming);
230
256
  versions.setVersion(pVersion, pkg.integrity);
231
257
 
232
258
  try {
@@ -238,6 +264,15 @@ const PkgPut = class PkgPut {
238
264
  throw e;
239
265
  }
240
266
 
267
+ try {
268
+ await this._writeEikJson(incoming, eikJson);
269
+ // eslint-disable-next-line no-unused-vars
270
+ } catch (error) {
271
+ const e = new HttpError.BadGateway();
272
+ end({ labels: { success: false, status: e.status, type } });
273
+ throw e;
274
+ }
275
+
241
276
  const outgoing = new HttpOutgoing();
242
277
  outgoing.cacheControl = this._cacheControl || "";
243
278
  outgoing.statusCode = 303;
@@ -1,12 +1,13 @@
1
1
  const FormFile = class FormFile {
2
2
  /**
3
- * @param {{ value?: any[], name?: string }} [options]
3
+ * @param {{ value?: any[], name?: string, eikJson?: Record<string,any>|null }} [options]
4
4
  */
5
- constructor({ value = [], name = "" } = {}) {
5
+ constructor({ value = [], name = "", eikJson = null } = {}) {
6
6
  if (!Array.isArray(value))
7
7
  throw new TypeError('The argument "value" must be of type Array');
8
8
  this._value = value;
9
9
  this._name = name;
10
+ this._eikJson = eikJson;
10
11
  }
11
12
 
12
13
  get value() {
@@ -17,6 +18,11 @@ const FormFile = class FormFile {
17
18
  return this._name;
18
19
  }
19
20
 
21
+ /** @returns {Record<string,any>|null} */
22
+ get eikJson() {
23
+ return this._eikJson;
24
+ }
25
+
20
26
  toJSON() {
21
27
  return {
22
28
  value: this.value,
@@ -1,4 +1,5 @@
1
1
  import { pipeline } from "node:stream";
2
+ import path from "node:path";
2
3
  import HttpError from "http-errors";
3
4
  import Busboy from "busboy";
4
5
  import abslog from "abslog";
@@ -148,10 +149,12 @@ const MultipartParser = class MultipartParser {
148
149
  const ac = new AbortController();
149
150
  /** @type {Promise<any>[]} */
150
151
  const queue = [];
152
+ /** @type {Record<string,any>|null} */
153
+ let capturedEikJson = null;
151
154
 
152
155
  file.once("limit", () => {
153
- this._log.info(
154
- `multipart - Uploaded package exceeds legal file size limit - Field: ${fieldname} - Filename: ${filename}`,
156
+ this._log.warn(
157
+ `multipart - Uploaded package exceeds the max allowed file size - Field: ${fieldname} - Filename: ${filename} - Name: ${incoming.name} - Version: ${incoming.version} - Type: ${incoming.type} - Limit: ${this._pkgMaxFileSize} bytes`,
155
158
  );
156
159
  ac.abort();
157
160
  file.emit("error", new HttpError.PayloadTooLarge());
@@ -166,6 +169,30 @@ const MultipartParser = class MultipartParser {
166
169
  entry.resume();
167
170
  return;
168
171
  }
172
+
173
+ // eik.json is captured in memory and written server-side
174
+ // after the upload is fully committed, so that it can serve
175
+ // as a reliable committed marker independent of what the
176
+ // client includes in the tar.
177
+ if (path.normalize(entry.path) === "eik.json") {
178
+ /** @type {Buffer[]} */
179
+ const chunks = [];
180
+ entry.on("data", (/** @type {Buffer} */ chunk) => {
181
+ chunks.push(chunk);
182
+ });
183
+ entry.once("end", () => {
184
+ try {
185
+ capturedEikJson = JSON.parse(
186
+ Buffer.concat(chunks).toString("utf8"),
187
+ );
188
+ } catch {
189
+ // Malformed eik.json in tar — proceed without it.
190
+ }
191
+ });
192
+ entry.once("error", () => {});
193
+ return;
194
+ }
195
+
169
196
  const persist = this._persistFile({
170
197
  incoming,
171
198
  entry,
@@ -205,7 +232,11 @@ const MultipartParser = class MultipartParser {
205
232
 
206
233
  Promise.all(queue)
207
234
  .then((result) => {
208
- const formFile = new FormFile({ name: fieldname, value: result });
235
+ const formFile = new FormFile({
236
+ name: fieldname,
237
+ value: result,
238
+ eikJson: capturedEikJson,
239
+ });
209
240
  resolve(formFile);
210
241
  })
211
242
  .catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eik/core",
3
- "version": "2.1.71",
3
+ "version": "2.1.73",
4
4
  "description": "Core server package",
5
5
  "main": "lib/main.js",
6
6
  "types": "./types/main.d.ts",
@@ -66,6 +66,16 @@ declare const PkgPut: {
66
66
  * @param {any} incoming
67
67
  */
68
68
  _parser(incoming: any): Promise<any>;
69
+ /**
70
+ * Writes eik.json to the sink as the final committed marker for a version.
71
+ * Merges any fields from the client's eik.json (captured from the tar) with
72
+ * server-authoritative values, ensuring the file is always server-written
73
+ * regardless of what the client's tar contained.
74
+ *
75
+ * @param {any} incoming
76
+ * @param {Record<string,any>|null} capturedEikJson
77
+ */
78
+ _writeEikJson(incoming: any, capturedEikJson: Record<string, any> | null): Promise<void>;
69
79
  /**
70
80
  * @param {any} incoming
71
81
  */
@@ -1,13 +1,17 @@
1
1
  export default FormFile;
2
2
  declare const FormFile: {
3
- new ({ value, name }?: {
3
+ new ({ value, name, eikJson }?: {
4
4
  value?: any[];
5
5
  name?: string;
6
+ eikJson?: Record<string, any> | null;
6
7
  }): {
7
8
  _value: any[];
8
9
  _name: string;
10
+ _eikJson: Record<string, any> | null;
9
11
  get value(): any[];
10
12
  get name(): string;
13
+ /** @returns {Record<string,any>|null} */
14
+ get eikJson(): Record<string, any> | null;
11
15
  toJSON(): {
12
16
  value: any[];
13
17
  name: string;