@tsdoctor/snapshot 0.2.0 → 0.2.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/content-hash.js +82 -11
- package/index.d.ts +8 -3
- package/package.json +1 -1
package/content-hash.js
CHANGED
|
@@ -49,14 +49,89 @@ function hashContent(content) {
|
|
|
49
49
|
const normalized = normalizeContent(content);
|
|
50
50
|
return createHash("sha256").update(normalized).digest("hex");
|
|
51
51
|
}
|
|
52
|
+
const TIMESTAMP_KEYS = /* @__PURE__ */ new Set([
|
|
53
|
+
"publishedTime",
|
|
54
|
+
"modifiedTime",
|
|
55
|
+
"article:published_time",
|
|
56
|
+
"article:modified_time"
|
|
57
|
+
]);
|
|
58
|
+
const JSON_LD_DATE_KEYS = /* @__PURE__ */ new Set([
|
|
59
|
+
"datePublished",
|
|
60
|
+
"dateModified",
|
|
61
|
+
"uploadDate"
|
|
62
|
+
]);
|
|
63
|
+
const JSON_LD_BODY_KEYS = /* @__PURE__ */ new Set([
|
|
64
|
+
"children",
|
|
65
|
+
"innerHTML",
|
|
66
|
+
"textContent"
|
|
67
|
+
]);
|
|
68
|
+
function isRecord(value) {
|
|
69
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Strips a JSON-LD script body of its date fields.
|
|
73
|
+
*
|
|
74
|
+
* @remarks
|
|
75
|
+
* The body arrives as a string, so it must be parsed before its dates can be
|
|
76
|
+
* removed. A body that does not parse as JSON is returned unchanged rather
|
|
77
|
+
* than throwing — an unparseable body is still content worth hashing.
|
|
78
|
+
*/
|
|
79
|
+
function stripJsonLdBody(body) {
|
|
80
|
+
let parsed;
|
|
81
|
+
try {
|
|
82
|
+
parsed = JSON.parse(body);
|
|
83
|
+
} catch {
|
|
84
|
+
return body;
|
|
85
|
+
}
|
|
86
|
+
return JSON.stringify(stripTimestamps(parsed));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Recursively removes timestamp-valued entries from a frontmatter value.
|
|
90
|
+
*
|
|
91
|
+
* @remarks
|
|
92
|
+
* Timestamps appear in two shapes. In the meta-pair form the value lives in a
|
|
93
|
+
* `content` field whose sibling `property`/`name` names a timestamp
|
|
94
|
+
* (`article:published_time`, `article:modified_time`). In the JSON-LD form it
|
|
95
|
+
* is an object key (`datePublished`, `dateModified`) inside a script body.
|
|
96
|
+
* Both are stripped; everything else survives so that an `og:image`,
|
|
97
|
+
* `og:description`, canonical `href` or JSON-LD version change is visible to
|
|
98
|
+
* change detection.
|
|
99
|
+
*
|
|
100
|
+
* The walk must be recursive: `head` is an array of `[tagName, attrs]` pairs,
|
|
101
|
+
* so a shallow pass would see nothing.
|
|
102
|
+
*/
|
|
103
|
+
function stripTimestamps(value) {
|
|
104
|
+
if (Array.isArray(value)) return value.map(stripTimestamps);
|
|
105
|
+
if (!isRecord(value)) return value;
|
|
106
|
+
const property = value["property"] ?? value["name"];
|
|
107
|
+
const isTimestampTag = typeof property === "string" && TIMESTAMP_KEYS.has(property);
|
|
108
|
+
const isJsonLd = value["type"] === "application/ld+json";
|
|
109
|
+
const result = {};
|
|
110
|
+
for (const key of Object.keys(value).sort()) {
|
|
111
|
+
if (JSON_LD_DATE_KEYS.has(key)) continue;
|
|
112
|
+
if (isTimestampTag && key === "content") continue;
|
|
113
|
+
const entry = value[key];
|
|
114
|
+
if (isJsonLd && JSON_LD_BODY_KEYS.has(key) && typeof entry === "string") {
|
|
115
|
+
result[key] = stripJsonLdBody(entry);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
result[key] = stripTimestamps(entry);
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
52
122
|
/**
|
|
53
123
|
* Generates a SHA-256 hash of frontmatter fields.
|
|
54
124
|
*
|
|
55
125
|
* @remarks
|
|
56
|
-
* Excludes
|
|
126
|
+
* Excludes the top-level timestamp fields (`publishedTime`, `modifiedTime`,
|
|
57
127
|
* `article:published_time`, `article:modified_time`) to prevent circular
|
|
58
|
-
* dependencies in change detection
|
|
59
|
-
*
|
|
128
|
+
* dependencies in change detection, and strips timestamp-valued entries
|
|
129
|
+
* recursively from every remaining value — including the `head` array's meta
|
|
130
|
+
* pairs and the date fields inside a JSON-LD script body. Everything else in
|
|
131
|
+
* `head` participates in the hash, so an `og:image`, `og:description` or
|
|
132
|
+
* canonical URL change marks the page modified. Keys are sorted
|
|
133
|
+
* alphabetically before hashing to ensure consistent results regardless of
|
|
134
|
+
* object key order.
|
|
60
135
|
*
|
|
61
136
|
* @param frontmatter - The frontmatter object to hash
|
|
62
137
|
* @returns Hexadecimal SHA-256 hash string
|
|
@@ -75,15 +150,11 @@ function hashContent(content) {
|
|
|
75
150
|
*/
|
|
76
151
|
function hashFrontmatter(frontmatter) {
|
|
77
152
|
const filtered = {};
|
|
78
|
-
for (const
|
|
79
|
-
if (key
|
|
80
|
-
filtered[key] =
|
|
153
|
+
for (const key of Object.keys(frontmatter).sort()) {
|
|
154
|
+
if (TIMESTAMP_KEYS.has(key)) continue;
|
|
155
|
+
filtered[key] = stripTimestamps(frontmatter[key]);
|
|
81
156
|
}
|
|
82
|
-
const
|
|
83
|
-
acc[key] = filtered[key];
|
|
84
|
-
return acc;
|
|
85
|
-
}, {});
|
|
86
|
-
const json = JSON.stringify(sorted);
|
|
157
|
+
const json = JSON.stringify(filtered);
|
|
87
158
|
return createHash("sha256").update(json).digest("hex");
|
|
88
159
|
}
|
|
89
160
|
|
package/index.d.ts
CHANGED
|
@@ -48,10 +48,15 @@ declare function hashContent(content: string): string;
|
|
|
48
48
|
* Generates a SHA-256 hash of frontmatter fields.
|
|
49
49
|
*
|
|
50
50
|
* @remarks
|
|
51
|
-
* Excludes
|
|
51
|
+
* Excludes the top-level timestamp fields (`publishedTime`, `modifiedTime`,
|
|
52
52
|
* `article:published_time`, `article:modified_time`) to prevent circular
|
|
53
|
-
* dependencies in change detection
|
|
54
|
-
*
|
|
53
|
+
* dependencies in change detection, and strips timestamp-valued entries
|
|
54
|
+
* recursively from every remaining value — including the `head` array's meta
|
|
55
|
+
* pairs and the date fields inside a JSON-LD script body. Everything else in
|
|
56
|
+
* `head` participates in the hash, so an `og:image`, `og:description` or
|
|
57
|
+
* canonical URL change marks the page modified. Keys are sorted
|
|
58
|
+
* alphabetically before hashing to ensure consistent results regardless of
|
|
59
|
+
* object key order.
|
|
55
60
|
*
|
|
56
61
|
* @param frontmatter - The frontmatter object to hash
|
|
57
62
|
* @returns Hexadecimal SHA-256 hash string
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsdoctor/snapshot",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Incremental-build snapshot tracking for static documentation pipelines: a schema-versioned SQLite store of per-file content hashes and timestamps, built on @effected/store, plus the pure SHA-256 content-hashing helpers that feed it.",
|
|
6
6
|
"keywords": [
|