@shevky/core 0.0.8 → 0.0.9
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/package.json +1 -1
- package/registries/contentRegistry.js +71 -4
- package/scripts/main.js +1 -1
package/package.json
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
1
2
|
import { io as _io, config as _cfg, log as _log } from "@shevky/base";
|
|
2
3
|
import matter from "gray-matter";
|
|
3
4
|
|
|
@@ -334,7 +335,8 @@ export class ContentRegistry {
|
|
|
334
335
|
}
|
|
335
336
|
|
|
336
337
|
/**
|
|
337
|
-
* Adds
|
|
338
|
+
* Adds or replaces content by id+lang.
|
|
339
|
+
* If duplicate payload is same -> ignore, if different -> replace with latest.
|
|
338
340
|
* @param {ContentFile} contentFile
|
|
339
341
|
*/
|
|
340
342
|
#_addUniqueContent(contentFile) {
|
|
@@ -362,18 +364,83 @@ export class ContentRegistry {
|
|
|
362
364
|
return false;
|
|
363
365
|
}
|
|
364
366
|
|
|
365
|
-
const
|
|
367
|
+
const existingIndex = this.#_cache.findIndex((entry) => {
|
|
366
368
|
const existingId = typeof entry.id === "string" ? entry.id.trim() : "";
|
|
367
369
|
const existingLang =
|
|
368
370
|
typeof entry.lang === "string" ? entry.lang.trim() : "";
|
|
369
371
|
return existingId === id && existingLang === lang;
|
|
370
372
|
});
|
|
371
373
|
|
|
372
|
-
if (
|
|
373
|
-
|
|
374
|
+
if (existingIndex !== -1) {
|
|
375
|
+
const currentEntry = this.#_cache[existingIndex];
|
|
376
|
+
const currentHash = this.#_buildContentHash(currentEntry);
|
|
377
|
+
const incomingHash = this.#_buildContentHash(contentFile);
|
|
378
|
+
if (currentHash === incomingHash) {
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
this.#_cache[existingIndex] = contentFile;
|
|
383
|
+
return true;
|
|
374
384
|
}
|
|
375
385
|
|
|
376
386
|
this.#_cache.push(contentFile);
|
|
377
387
|
return true;
|
|
378
388
|
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* @param {ContentFile} contentFile
|
|
392
|
+
*/
|
|
393
|
+
#_buildContentHash(contentFile) {
|
|
394
|
+
const payload = {
|
|
395
|
+
header: contentFile.header?.raw ?? {},
|
|
396
|
+
content: typeof contentFile.content === "string" ? contentFile.content : "",
|
|
397
|
+
isValid: Boolean(contentFile.isValid),
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
return crypto
|
|
401
|
+
.createHash("sha1")
|
|
402
|
+
.update(this.#_stableStringify(payload))
|
|
403
|
+
.digest("hex");
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Stable stringify to avoid key-order differences in hash computation.
|
|
408
|
+
* @param {unknown} value
|
|
409
|
+
* @returns {string}
|
|
410
|
+
*/
|
|
411
|
+
#_stableStringify(value) {
|
|
412
|
+
if (value === undefined) {
|
|
413
|
+
return '"__undefined__"';
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (value === null) {
|
|
417
|
+
return "null";
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (value instanceof Date) {
|
|
421
|
+
return JSON.stringify(value.toISOString());
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (Array.isArray(value)) {
|
|
425
|
+
return `[${value.map((item) => this.#_stableStringify(item)).join(",")}]`;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (typeof value !== "object") {
|
|
429
|
+
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
430
|
+
return JSON.stringify(String(value));
|
|
431
|
+
}
|
|
432
|
+
return JSON.stringify(value);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const entries = Object.keys(value)
|
|
436
|
+
.sort()
|
|
437
|
+
.map(
|
|
438
|
+
(key) =>
|
|
439
|
+
`${JSON.stringify(key)}:${this.#_stableStringify(
|
|
440
|
+
/** @type {Record<string, unknown>} */ (value)[key],
|
|
441
|
+
)}`,
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
return `{${entries.join(",")}}`;
|
|
445
|
+
}
|
|
379
446
|
}
|
package/scripts/main.js
CHANGED