@lexho111/plainblog 0.6.11 → 0.6.13
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/ArrayList.cpuprofile +1 -0
- package/Article.js +26 -46
- package/BinarySearchTree.cpuprofile +1 -0
- package/Blog.js +99 -64
- package/FlameChartProfile.cpuprofile +1 -0
- package/FlameChartProfile2.cpuprofile +1 -0
- package/FlameChartProfile3.cpuprofile +1 -0
- package/StandaloneProfile.cpuprofile +1 -0
- package/build-scripts.js +4 -3
- package/index.js +12 -1
- package/model/FileAdapter.js +1 -0
- package/model/FileModel.js +13 -1
- package/model/SequelizeAdapter.js +2 -1
- package/model/datastructures/BinarySearchTree.js +2 -1
- package/model/datastructures/BinarySearchTreeHashMap.js +2 -1
- package/package.json +2 -1
- package/profile-bst.js +73 -0
- package/public/scripts.min.js +2 -2
- package/public/styles.min.css +2 -2
- package/src/fetchData.js +55 -0
- package/Blog - Kopie.js +0 -979
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"nodes":[{"id":1,"callFrame":{"functionName":"(root)","scriptId":"0","url":"","lineNumber":-1,"columnNumber":-1},"hitCount":0,"children":[2]},{"id":2,"callFrame":{"functionName":"processTicksAndRejections","scriptId":"31","url":"node:internal/process/task_queues","lineNumber":70,"columnNumber":34},"hitCount":0,"children":[3]},{"id":3,"callFrame":{"functionName":"runSearch","scriptId":"165","url":"file:///C:/Users/alexh/Documents/plainblog/package/profile-bst.js","lineNumber":11,"columnNumber":24},"hitCount":2,"children":[4,6,7],"positionTicks":[{"line":38,"ticks":2}]},{"id":4,"callFrame":{"functionName":"","scriptId":"165","url":"file:///C:/Users/alexh/Documents/plainblog/package/profile-bst.js","lineNumber":31,"columnNumber":20},"hitCount":0,"children":[5]},{"id":5,"callFrame":{"functionName":"post","scriptId":"108","url":"node:inspector","lineNumber":114,"columnNumber":6},"hitCount":0},{"id":6,"callFrame":{"functionName":"getRange","scriptId":"167","url":"file:///C:/Users/alexh/Documents/plainblog/package/model/datastructures/ArrayList.js","lineNumber":64,"columnNumber":16},"hitCount":9,"positionTicks":[{"line":83,"ticks":4},{"line":83,"ticks":1},{"line":85,"ticks":3},{"line":83,"ticks":1}]},{"id":7,"callFrame":{"functionName":"","scriptId":"165","url":"file:///C:/Users/alexh/Documents/plainblog/package/profile-bst.js","lineNumber":39,"columnNumber":20},"hitCount":0,"children":[8]},{"id":8,"callFrame":{"functionName":"post","scriptId":"108","url":"node:inspector","lineNumber":114,"columnNumber":6},"hitCount":0,"children":[9]},{"id":9,"callFrame":{"functionName":"dispatch","scriptId":"0","url":"","lineNumber":-1,"columnNumber":-1},"hitCount":1,"positionTicks":[{"line":139,"ticks":1}]}],"startTime":1715759778061,"endTime":1715763735963,"samples":[5,6,3,6,6,6,6,6,6,6,6,6,6,3,9],"timeDeltas":[3939197,1797,1542,1458,1498,1489,1511,506,1007,1507,1835,1295,853,497,1509]}
|
package/Article.js
CHANGED
|
@@ -24,20 +24,41 @@ export default class Article {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
getContentVeryShort() {
|
|
27
|
-
if (!this.content) return;
|
|
28
|
-
if (this.content.length < 50) return this.
|
|
27
|
+
if (!this.content) return "";
|
|
28
|
+
if (this.content.length < 50) return this.content;
|
|
29
29
|
let contentShort = this.content.substring(0, 50);
|
|
30
30
|
contentShort += "...";
|
|
31
31
|
return contentShort;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
getContentShort() {
|
|
35
|
-
if (this.content
|
|
35
|
+
if (!this.content) return "";
|
|
36
|
+
if (this.content.length < 400) return this.content;
|
|
36
37
|
let contentShort = this.content.substring(0, 400);
|
|
37
38
|
contentShort += "...";
|
|
38
39
|
return contentShort;
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Returns a JSON-serializable representation of the article.
|
|
44
|
+
* This method is automatically called by JSON.stringify().
|
|
45
|
+
*/
|
|
46
|
+
toJSON() {
|
|
47
|
+
// Return a plain object with the desired properties
|
|
48
|
+
let date = new Date();
|
|
49
|
+
try {
|
|
50
|
+
date = new Date(this.createdAt).toISOString();
|
|
51
|
+
} catch (err) {
|
|
52
|
+
console.error(err);
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
id: this.id,
|
|
56
|
+
title: this.title,
|
|
57
|
+
content: this.content,
|
|
58
|
+
createdAt: date,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
41
62
|
getFormattedDate() {
|
|
42
63
|
const date = new Date(this.createdAt);
|
|
43
64
|
const year = date.getFullYear();
|
|
@@ -51,48 +72,8 @@ export default class Article {
|
|
|
51
72
|
toHTML(loggedin = false) {
|
|
52
73
|
if (this.content == null) return "";
|
|
53
74
|
const moreButton = this.content.length > 400 ? `<a href="#">more</a>` : "";
|
|
54
|
-
const buttons = loggedin
|
|
55
|
-
? `<div class="buttons"><div id="editButton${this.id}" class="btn edit">edit</div><div id="deleteButton${this.id}" class="btn delete">delete</div><div id="somethingelseButton${this.id}" class="btn light">something else</div></div>`
|
|
56
|
-
: "";
|
|
57
|
-
const script = loggedin
|
|
58
|
-
? `<script>
|
|
59
|
-
if (typeof window.handleAction !== 'function') {
|
|
60
|
-
window.handleAction = function(action, id) {
|
|
61
|
-
if (action === 'delete') {
|
|
62
|
-
if (confirm("Delete this article?")) {
|
|
63
|
-
fetch("/api/articles?id=" + id, { method: "DELETE" })
|
|
64
|
-
.then(res => { if(res.ok) window.location.reload(); else alert("Error: " + res.statusText); });
|
|
65
|
-
}
|
|
66
|
-
} else if (action === 'edit') {
|
|
67
|
-
const title = prompt("New Title");
|
|
68
|
-
const content = prompt("New Content");
|
|
69
|
-
if (title && content) {
|
|
70
|
-
fetch("/api/articles?id=" + id, {
|
|
71
|
-
method: "PUT",
|
|
72
|
-
body: JSON.stringify({ title, content })
|
|
73
|
-
}).then(res => { if(res.ok) window.location.reload(); else alert("Error: " + res.statusText); });
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
function clickListener(button) {
|
|
79
|
-
const mybutton = document.getElementById(button);
|
|
80
|
-
const action = button.includes("edit") ? "edit" : "delete";
|
|
81
|
-
|
|
82
|
-
// Mouse/Touch support
|
|
83
|
-
mybutton.addEventListener('click', () => window.handleAction(action, ${this.id}));
|
|
84
|
-
|
|
85
|
-
// Keyboard support
|
|
86
|
-
mybutton.addEventListener('keydown', (event) => {
|
|
87
|
-
if (event.key === 'Enter' || event.key === ' ') {
|
|
88
|
-
event.preventDefault(); // Prevents page scrolling on Space
|
|
89
|
-
window.handleAction(action, ${this.id});
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
clickListener("editButton${this.id}");
|
|
94
|
-
clickListener("deleteButton${this.id}");
|
|
95
|
-
</script>`
|
|
75
|
+
const buttons = loggedin // prettier-ignore
|
|
76
|
+
? `<div class="buttons"><div id="editButton${this.id}" class="btn edit" role="button" tabindex="0" data-action="edit" data-id="${this.id}">edit</div><div id="deleteButton${this.id}" class="btn delete" role="button" tabindex="0" data-action="delete" data-id="${this.id}">delete</div><div id="somethingelseButton${this.id}" class="btn light">something else</div></div>`
|
|
96
77
|
: "";
|
|
97
78
|
// prettier-ignore
|
|
98
79
|
return `<article data-id="${this.id}" data-date="${this.createdAt}">
|
|
@@ -101,7 +82,6 @@ clickListener("deleteButton${this.id}");
|
|
|
101
82
|
<span class="datetime">${this.getFormattedDate()}</span>
|
|
102
83
|
<p>${this.getContentShort()}</p>
|
|
103
84
|
${moreButton}
|
|
104
|
-
${script}
|
|
105
85
|
</article>`;
|
|
106
86
|
}
|
|
107
87
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"nodes":[{"id":1,"callFrame":{"functionName":"(root)","scriptId":"0","url":"","lineNumber":-1,"columnNumber":-1},"hitCount":0,"children":[2]},{"id":2,"callFrame":{"functionName":"processTicksAndRejections","scriptId":"31","url":"node:internal/process/task_queues","lineNumber":70,"columnNumber":34},"hitCount":0,"children":[3]},{"id":3,"callFrame":{"functionName":"runSearch","scriptId":"165","url":"file:///C:/Users/alexh/Documents/plainblog/package/profile-bst.js","lineNumber":11,"columnNumber":24},"hitCount":0,"children":[4,6]},{"id":4,"callFrame":{"functionName":"","scriptId":"165","url":"file:///C:/Users/alexh/Documents/plainblog/package/profile-bst.js","lineNumber":31,"columnNumber":20},"hitCount":0,"children":[5]},{"id":5,"callFrame":{"functionName":"post","scriptId":"108","url":"node:inspector","lineNumber":114,"columnNumber":6},"hitCount":0},{"id":6,"callFrame":{"functionName":"","scriptId":"165","url":"file:///C:/Users/alexh/Documents/plainblog/package/profile-bst.js","lineNumber":39,"columnNumber":20},"hitCount":0,"children":[7]},{"id":7,"callFrame":{"functionName":"post","scriptId":"108","url":"node:inspector","lineNumber":114,"columnNumber":6},"hitCount":0,"children":[8]},{"id":8,"callFrame":{"functionName":"dispatch","scriptId":"0","url":"","lineNumber":-1,"columnNumber":-1},"hitCount":1,"positionTicks":[{"line":139,"ticks":1}]}],"startTime":1715784018183,"endTime":1715790062379,"samples":[5,8],"timeDeltas":[6035769,7757]}
|
package/Blog.js
CHANGED
|
@@ -6,6 +6,7 @@ import process from "node:process";
|
|
|
6
6
|
import path from "path";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
8
|
import pkg from "./package.json" with { type: "json" };
|
|
9
|
+
import { Buffer } from "node:buffer";
|
|
9
10
|
import Article from "./Article.js";
|
|
10
11
|
import DatabaseModel from "./model/DatabaseModel.js";
|
|
11
12
|
import { fetchData, postData } from "./model/APIModel.js";
|
|
@@ -56,7 +57,7 @@ export default class Blog {
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
/** @returns a json representation of the blog */
|
|
59
|
-
|
|
60
|
+
toJSON() {
|
|
60
61
|
const serverInfo = this.#server
|
|
61
62
|
? {
|
|
62
63
|
listening: this.#server.listening,
|
|
@@ -80,6 +81,11 @@ export default class Blog {
|
|
|
80
81
|
return JSON.parse(JSON.stringify(json)); // make json read-only
|
|
81
82
|
}
|
|
82
83
|
|
|
84
|
+
/** @returns a json representation of the blog */
|
|
85
|
+
json() {
|
|
86
|
+
return this.toJSON();
|
|
87
|
+
}
|
|
88
|
+
|
|
83
89
|
// Private fields
|
|
84
90
|
#version = null;
|
|
85
91
|
#server = null;
|
|
@@ -171,19 +177,29 @@ export default class Blog {
|
|
|
171
177
|
|
|
172
178
|
async #readBody(req) {
|
|
173
179
|
return new Promise((resolve, reject) => {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
req.on("
|
|
177
|
-
|
|
180
|
+
const chunks = [];
|
|
181
|
+
|
|
182
|
+
req.on("data", (chunk) => {
|
|
183
|
+
chunks.push(chunk);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
req.on("end", () => {
|
|
187
|
+
const data = Buffer.concat(chunks).toString();
|
|
188
|
+
resolve(data);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
req.on("error", (err) => {
|
|
192
|
+
reject(err);
|
|
193
|
+
});
|
|
178
194
|
});
|
|
179
195
|
}
|
|
180
196
|
|
|
181
197
|
async #handleLogin(req, res) {
|
|
182
198
|
debug("handle login");
|
|
183
199
|
const body = await new Promise((resolve, reject) => {
|
|
184
|
-
|
|
185
|
-
req.on("data", (chunk) => (
|
|
186
|
-
req.on("end", () => resolve(
|
|
200
|
+
const chunks = [];
|
|
201
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
202
|
+
req.on("end", () => resolve(Buffer.concat(chunks).toString()));
|
|
187
203
|
req.on("error", reject);
|
|
188
204
|
});
|
|
189
205
|
const params = new URLSearchParams(body);
|
|
@@ -263,7 +279,9 @@ export default class Blog {
|
|
|
263
279
|
);
|
|
264
280
|
if (match) publicHash = match[1];
|
|
265
281
|
})
|
|
266
|
-
.catch((err) =>
|
|
282
|
+
.catch((err) => {
|
|
283
|
+
if (err.code !== "ENOENT") console.error(err);
|
|
284
|
+
}), // public/styles.min.css doesn't exist, will be created.
|
|
267
285
|
fs.promises
|
|
268
286
|
.readFile(srcStylePath, "utf8")
|
|
269
287
|
.then((content) => {
|
|
@@ -307,14 +325,14 @@ export default class Blog {
|
|
|
307
325
|
console.log("external API");
|
|
308
326
|
await this.#loadFromAPI();
|
|
309
327
|
} else {
|
|
310
|
-
|
|
328
|
+
debug(`database: ${this.database.type}`);
|
|
311
329
|
if (!this.#databaseModel) {
|
|
312
330
|
if (this.database.type === "file") {
|
|
313
331
|
const adapter = new FileAdapter(this.database);
|
|
314
332
|
this.#databaseModel = new DatabaseModel(adapter);
|
|
315
333
|
}
|
|
316
334
|
}
|
|
317
|
-
|
|
335
|
+
debug(`connected to database`);
|
|
318
336
|
await this.#databaseModel.initialize();
|
|
319
337
|
this.#articles.setDatabase(this.#databaseModel);
|
|
320
338
|
const [dbTitle, dbArticles] = await Promise.all([
|
|
@@ -364,16 +382,21 @@ export default class Blog {
|
|
|
364
382
|
async postArticle(newArticle) {
|
|
365
383
|
try {
|
|
366
384
|
// Save the new article to the database via the ApiServer
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
const
|
|
374
|
-
|
|
385
|
+
if (this.#isExternalAPI) {
|
|
386
|
+
await postData(this.#apiUrl, newArticle);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// The article is already saved to the DB, just add it to the in-memory cache.
|
|
390
|
+
// Ensure it's an Article instance.
|
|
391
|
+
const article = new Article(
|
|
392
|
+
newArticle.id,
|
|
393
|
+
newArticle.title,
|
|
394
|
+
newArticle.content,
|
|
395
|
+
newArticle.createdAt,
|
|
396
|
+
);
|
|
397
|
+
|
|
375
398
|
// Add the article to the local list for immediate display
|
|
376
|
-
this.#articles.insert(
|
|
399
|
+
this.#articles.insert(article);
|
|
377
400
|
// remove sample entries
|
|
378
401
|
this.#articles.remove(1); // "Sample Entry #1"
|
|
379
402
|
this.#articles.remove(2); // "Sample Entry #2"
|
|
@@ -471,8 +494,8 @@ export default class Blog {
|
|
|
471
494
|
|
|
472
495
|
console.log("New Article Data:", articleData);
|
|
473
496
|
// local
|
|
474
|
-
await this.#databaseModel.save(articleData);
|
|
475
|
-
this.postArticle(
|
|
497
|
+
const savedArticle = await this.#databaseModel.save(articleData);
|
|
498
|
+
this.postArticle(savedArticle);
|
|
476
499
|
// external
|
|
477
500
|
|
|
478
501
|
// Success response
|
|
@@ -696,7 +719,10 @@ export default class Blog {
|
|
|
696
719
|
//console.log(this.#articles.getAllArticles());
|
|
697
720
|
const article = this.#articles.get(id);
|
|
698
721
|
if (article) {
|
|
699
|
-
res.writeHead(200, {
|
|
722
|
+
res.writeHead(200, {
|
|
723
|
+
"Content-Type": "application/json",
|
|
724
|
+
"Cache-Control": "public, max-age=60", // Cache for 60 seconds
|
|
725
|
+
});
|
|
700
726
|
res.end(JSON.stringify(article));
|
|
701
727
|
} else {
|
|
702
728
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
@@ -742,19 +768,27 @@ export default class Blog {
|
|
|
742
768
|
return;
|
|
743
769
|
}
|
|
744
770
|
const body = await new Promise((resolve, reject) => {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
req.on("
|
|
771
|
+
const chunks = [];
|
|
772
|
+
|
|
773
|
+
req.on("data", (chunk) => {
|
|
774
|
+
chunks.push(chunk);
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
req.on("end", () => {
|
|
778
|
+
const result = Buffer.concat(chunks).toString();
|
|
779
|
+
resolve(result);
|
|
780
|
+
});
|
|
781
|
+
|
|
748
782
|
req.on("error", reject);
|
|
749
783
|
});
|
|
750
784
|
const newArticle = JSON.parse(body);
|
|
751
785
|
debug("new article: %s", newArticle.title);
|
|
752
786
|
// local
|
|
753
|
-
await this.#databaseModel.save(newArticle);
|
|
754
|
-
this.postArticle(
|
|
787
|
+
const savedArticle = await this.#databaseModel.save(newArticle);
|
|
788
|
+
this.postArticle(savedArticle);
|
|
755
789
|
// external
|
|
756
790
|
res.writeHead(201, { "Content-Type": "application/json" });
|
|
757
|
-
res.end(JSON.stringify(
|
|
791
|
+
res.end(JSON.stringify(savedArticle));
|
|
758
792
|
} else if (req.method === "DELETE") {
|
|
759
793
|
debug("DELETE an article");
|
|
760
794
|
const match = pathname.match(/^\/api\/articles\/(\d+)$/);
|
|
@@ -786,10 +820,20 @@ export default class Blog {
|
|
|
786
820
|
if (pathname === "/api/articles" || match) {
|
|
787
821
|
const id = match ? match[1] : url.searchParams.get("id");
|
|
788
822
|
debug("PUT article id: %d", id);
|
|
789
|
-
const body = await new Promise((resolve) => {
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
req.on("
|
|
823
|
+
const body = await new Promise((resolve, reject) => {
|
|
824
|
+
const chunks = [];
|
|
825
|
+
|
|
826
|
+
req.on("data", (chunk) => {
|
|
827
|
+
// Speichert die Buffer-Referenzen direkt, ohne String-Konvertierung
|
|
828
|
+
chunks.push(chunk);
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
req.on("end", () => {
|
|
832
|
+
const data = Buffer.concat(chunks).toString();
|
|
833
|
+
resolve(data);
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
req.on("error", reject);
|
|
793
837
|
});
|
|
794
838
|
const { title, content } = JSON.parse(body);
|
|
795
839
|
this.#articles.update(parseInt(id), title, content);
|
|
@@ -820,10 +864,9 @@ export default class Blog {
|
|
|
820
864
|
|
|
821
865
|
/** render this blog content to valid html */
|
|
822
866
|
async toHTML(loggedin) {
|
|
823
|
-
const
|
|
824
|
-
const articles_after = articles.slice(0, 50);
|
|
867
|
+
const articles_after = await this.#articles.findAll(null, null, 50);
|
|
825
868
|
// prettier-ignore
|
|
826
|
-
debug("
|
|
869
|
+
debug("fetched %d articles", articles_after.length);
|
|
827
870
|
const data = {
|
|
828
871
|
title: this.title,
|
|
829
872
|
articles: articles_after,
|
|
@@ -843,13 +886,15 @@ export default class Blog {
|
|
|
843
886
|
//debug("typeof data.articles: %o", typeof data.articles);
|
|
844
887
|
//debug("typeof data.articles: %O", data.articles);
|
|
845
888
|
const article = data.articles[0];
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
889
|
+
if (article) {
|
|
890
|
+
debug("first article: ");
|
|
891
|
+
debug(
|
|
892
|
+
"%d %s %s...",
|
|
893
|
+
article.id,
|
|
894
|
+
article.title,
|
|
895
|
+
article.getContentVeryShort(),
|
|
896
|
+
);
|
|
897
|
+
}
|
|
853
898
|
const html = formatHTML(data);
|
|
854
899
|
if (validate(html)) return html;
|
|
855
900
|
throw new Error("Error. Invalid HTML!");
|
|
@@ -860,7 +905,7 @@ export default class Blog {
|
|
|
860
905
|
* @param {string[]} files - Array of css/scss file paths to process.
|
|
861
906
|
*/
|
|
862
907
|
async #processStylesheets(files) {
|
|
863
|
-
|
|
908
|
+
debug("process stylesheets");
|
|
864
909
|
|
|
865
910
|
// Normalize input to array (handles string or array)
|
|
866
911
|
// "file1.css" --> ["file1.css"]
|
|
@@ -886,16 +931,11 @@ export default class Blog {
|
|
|
886
931
|
);
|
|
887
932
|
|
|
888
933
|
// compute hash
|
|
889
|
-
const
|
|
890
|
-
|
|
891
|
-
.update(
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
crypto.createHash("sha256").update(f.content).digest("hex"),
|
|
895
|
-
)
|
|
896
|
-
.join(""),
|
|
897
|
-
)
|
|
898
|
-
.digest("hex");
|
|
934
|
+
const hash = crypto.createHash("sha256");
|
|
935
|
+
for (const file of fileData) {
|
|
936
|
+
hash.update(file.content);
|
|
937
|
+
}
|
|
938
|
+
const currentHash = hash.digest("hex");
|
|
899
939
|
|
|
900
940
|
// check if hash matches
|
|
901
941
|
if (currentHash !== this.#stylesHash && this.compilestyle) {
|
|
@@ -938,16 +978,11 @@ export default class Blog {
|
|
|
938
978
|
}),
|
|
939
979
|
);
|
|
940
980
|
|
|
941
|
-
const
|
|
942
|
-
|
|
943
|
-
.update(
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
crypto.createHash("sha256").update(f.content).digest("hex"),
|
|
947
|
-
)
|
|
948
|
-
.join(""),
|
|
949
|
-
)
|
|
950
|
-
.digest("hex");
|
|
981
|
+
const hash = crypto.createHash("sha256");
|
|
982
|
+
for (const file of fileData) {
|
|
983
|
+
hash.update(file.content);
|
|
984
|
+
}
|
|
985
|
+
const currentHash = hash.digest("hex");
|
|
951
986
|
|
|
952
987
|
if (!this.#scriptsHash) {
|
|
953
988
|
try {
|