@tryaura/aura-testkit 0.2.1 → 0.3.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/dist/index.js +49 -20
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1296,6 +1296,7 @@ async function readBody(response, maxBytes, binary) {
|
|
|
1296
1296
|
status: response.status
|
|
1297
1297
|
} : {
|
|
1298
1298
|
body: "",
|
|
1299
|
+
...responseEtag(response),
|
|
1299
1300
|
kind: "response",
|
|
1300
1301
|
status: response.status
|
|
1301
1302
|
};
|
|
@@ -1321,10 +1322,16 @@ async function readBody(response, maxBytes, binary) {
|
|
|
1321
1322
|
status: response.status
|
|
1322
1323
|
} : {
|
|
1323
1324
|
body: new TextDecoder().decode(body),
|
|
1325
|
+
...responseEtag(response),
|
|
1324
1326
|
kind: "response",
|
|
1325
1327
|
status: response.status
|
|
1326
1328
|
};
|
|
1327
1329
|
}
|
|
1330
|
+
/** The entity tag as an optional field, so absence stays absent rather than `undefined`-valued. */
|
|
1331
|
+
function responseEtag(response) {
|
|
1332
|
+
const etag = response.headers.get("etag");
|
|
1333
|
+
return etag === null ? {} : { etag };
|
|
1334
|
+
}
|
|
1328
1335
|
function concatenate(chunks, length) {
|
|
1329
1336
|
const joined = new Uint8Array(length);
|
|
1330
1337
|
let offset = 0;
|
|
@@ -1345,37 +1352,48 @@ function failureReason(error) {
|
|
|
1345
1352
|
return name === "TimeoutError" || name === "AbortError" ? "timeout" : "network";
|
|
1346
1353
|
}
|
|
1347
1354
|
//#endregion
|
|
1348
|
-
//#region ../core/src/
|
|
1355
|
+
//#region ../core/src/content-hash.ts
|
|
1349
1356
|
/**
|
|
1350
|
-
* Normalizes
|
|
1357
|
+
* Normalizes Markdown to LF and exactly one trailing newline.
|
|
1351
1358
|
*
|
|
1352
|
-
*
|
|
1353
|
-
*
|
|
1359
|
+
* Line endings and trailing blank lines are what a checkout, an editor, or an append seam change
|
|
1360
|
+
* without changing what the text says, so a hash that counted them would report drift on every
|
|
1361
|
+
* machine that rewrote them — and a fragment appended in this form is the same text the hash
|
|
1362
|
+
* covers. Trailing newlines are trimmed by scanning rather than with `/\n+$/`, whose backtracking
|
|
1363
|
+
* is quadratic when a long newline run does not reach the end of the string.
|
|
1354
1364
|
*/
|
|
1355
|
-
function
|
|
1356
|
-
const normalized = content.replace(/\r\n?/
|
|
1365
|
+
function canonicalizeContent(content) {
|
|
1366
|
+
const normalized = content.replace(/\r\n?/gu, "\n");
|
|
1357
1367
|
let end = normalized.length;
|
|
1358
1368
|
while (end > 0 && normalized[end - 1] === "\n") end -= 1;
|
|
1359
1369
|
return `${normalized.slice(0, end)}\n`;
|
|
1360
1370
|
}
|
|
1361
|
-
/**
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1371
|
+
/**
|
|
1372
|
+
* Fingerprints Markdown Aura records but does not own: an installed snippet, a trusted preset.
|
|
1373
|
+
*
|
|
1374
|
+
* One function for both because the two records answer the same question — "is this still the text
|
|
1375
|
+
* the user accepted?" — and two spellings of the canonical form would answer it differently on the
|
|
1376
|
+
* first file whose line endings a checkout rewrote.
|
|
1377
|
+
*/
|
|
1378
|
+
function hashContent(content) {
|
|
1379
|
+
return createHash("sha256").update(canonicalizeContent(content), "utf8").digest("hex");
|
|
1368
1380
|
}
|
|
1369
1381
|
//#endregion
|
|
1370
|
-
//#region ../core/src/preset/repo-
|
|
1382
|
+
//#region ../core/src/preset/repo-content.ts
|
|
1371
1383
|
/**
|
|
1372
|
-
* Hashes repository
|
|
1384
|
+
* Hashes the repository content set for the trust record.
|
|
1373
1385
|
*
|
|
1374
|
-
*
|
|
1375
|
-
*
|
|
1386
|
+
* With no snippet files the hash is exactly the preset file's own content hash, so a repository
|
|
1387
|
+
* that never adds `.aura/snippets` keeps every trust its users already recorded. The first
|
|
1388
|
+
* snippet changes the descriptor shape — and consent is re-asked, which is the point.
|
|
1376
1389
|
*/
|
|
1377
|
-
function
|
|
1378
|
-
return
|
|
1390
|
+
function hashRepoContentSet(presetText, snippets) {
|
|
1391
|
+
if (snippets.length === 0) return hashContent(presetText);
|
|
1392
|
+
return hashContent([
|
|
1393
|
+
"aura-repo-content-v1",
|
|
1394
|
+
`preset ${hashContent(presetText)}`,
|
|
1395
|
+
...[...snippets].sort((left, right) => left.id < right.id ? -1 : 1).map((file) => `snippet ${file.id} ${hashContent(file.text)}`)
|
|
1396
|
+
].join("\n"));
|
|
1379
1397
|
}
|
|
1380
1398
|
//#endregion
|
|
1381
1399
|
//#region src/http.ts
|
|
@@ -1828,7 +1846,7 @@ async function recordTrustedPresets(homeDir, workspaceDir, presets) {
|
|
|
1828
1846
|
throw new Error(`trustWorkspacePreset needs the seeded workspace file: ${relativePath}`);
|
|
1829
1847
|
});
|
|
1830
1848
|
added.push({
|
|
1831
|
-
hash:
|
|
1849
|
+
hash: hashRepoContentSet(content, await seededSnippets(dirname(path))),
|
|
1832
1850
|
path
|
|
1833
1851
|
});
|
|
1834
1852
|
}
|
|
@@ -1844,6 +1862,17 @@ async function recordTrustedPresets(homeDir, workspaceDir, presets) {
|
|
|
1844
1862
|
mode: 384
|
|
1845
1863
|
});
|
|
1846
1864
|
}
|
|
1865
|
+
/** The snippet files beside a seeded preset, in the shape the composite hash consumes. */
|
|
1866
|
+
async function seededSnippets(auraDir) {
|
|
1867
|
+
const root = join(auraDir, "snippets");
|
|
1868
|
+
const names = await readdir(root).catch(() => []);
|
|
1869
|
+
const files = [];
|
|
1870
|
+
for (const name of names.filter((entry) => !entry.startsWith(".") && entry.endsWith(".md"))) files.push({
|
|
1871
|
+
id: `repo/${name.slice(0, -3)}`,
|
|
1872
|
+
text: await readFile(join(root, name), "utf8")
|
|
1873
|
+
});
|
|
1874
|
+
return files;
|
|
1875
|
+
}
|
|
1847
1876
|
function parseSeedManifest(source, path) {
|
|
1848
1877
|
const value = JSON.parse(source);
|
|
1849
1878
|
if (!isRecord(value)) throw new Error(`Seed manifest ${path} must contain a JSON object`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tryaura/aura-testkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Deterministic filesystem seeds and CLI harnesses for Aura plugins and distributions.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"aura",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"access": "public"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@tryaura/aura-cli": "0.
|
|
34
|
-
"@tryaura/aura-sdk": "0.
|
|
33
|
+
"@tryaura/aura-cli": "0.3.1",
|
|
34
|
+
"@tryaura/aura-sdk": "0.3.1"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "24.13.3",
|