@adea-ai/plugins 3.0.10 → 4.0.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/README.md +18 -11
- package/dist/agent-plugins-catalog.d.ts +2 -3
- package/dist/agent-plugins-catalog.js +2 -10
- package/dist/agent-plugins-catalog.js.map +1 -1
- package/dist/catalog-store.d.ts +81 -0
- package/dist/catalog-store.js +487 -0
- package/dist/catalog-store.js.map +1 -0
- package/dist/consumer-index.d.ts +1 -1
- package/dist/consumer-index.js +17 -7
- package/dist/consumer-index.js.map +1 -1
- package/dist/display-name.js +3 -2
- package/dist/display-name.js.map +1 -1
- package/dist/harness.d.ts +50 -9
- package/dist/harness.js +190 -147
- package/dist/harness.js.map +1 -1
- package/dist/icon-mirror.d.ts +2 -2
- package/dist/icon-mirror.js +38 -25
- package/dist/icon-mirror.js.map +1 -1
- package/dist/icons.js +5 -27
- package/dist/icons.js.map +1 -1
- package/dist/index.d.ts +9 -3
- package/dist/index.js +103 -88
- package/dist/index.js.map +1 -1
- package/dist/publication.d.ts +8 -11
- package/dist/publication.js +8 -11
- package/dist/publication.js.map +1 -1
- package/dist/schema.d.ts +0 -40
- package/dist/schema.js +0 -36
- package/dist/schema.js.map +1 -1
- package/dist/site-icons.d.ts +2 -0
- package/dist/site-icons.js +46 -9
- package/dist/site-icons.js.map +1 -1
- package/dist/source-digest.d.ts +2 -0
- package/dist/source-digest.js +12 -0
- package/dist/source-digest.js.map +1 -0
- package/dist/sources.js +3 -3
- package/dist/sources.js.map +1 -1
- package/native/build-provenance.txt +5 -5
- package/native/staged-reader-win32-arm64.node +0 -0
- package/native/staged-reader-win32-x64.node +0 -0
- package/package.json +4 -9
- package/src/agent-plugins-catalog.ts +3 -10
- package/src/artifacts.test.ts +80 -0
- package/src/catalog-installation.test.ts +1 -1
- package/src/catalog-store.ts +546 -0
- package/src/consumer-index.test.ts +125 -0
- package/src/consumer-index.ts +23 -8
- package/src/default-head.test.ts +143 -0
- package/src/display-name.test.ts +8 -0
- package/src/display-name.ts +3 -2
- package/src/{agent-plugins-harness.test.ts → harness.test.ts} +1 -1
- package/src/harness.ts +249 -170
- package/src/icon-mirror.ts +36 -23
- package/src/icons.test.ts +5 -0
- package/src/icons.ts +6 -29
- package/src/index.test.ts +272 -40
- package/src/index.ts +131 -94
- package/src/manifest-retry.test.ts +49 -0
- package/src/portable-catalog.test.ts +8 -5
- package/src/publication.ts +8 -11
- package/src/schema.ts +0 -40
- package/src/site-icon-fallback.test.ts +36 -19
- package/src/site-icons.test.ts +104 -0
- package/src/site-icons.ts +47 -9
- package/src/source-config-sync.test.ts +84 -0
- package/src/source-digest.test.ts +101 -0
- package/src/source-digest.ts +14 -0
- package/src/sources-json.test.ts +53 -0
- package/src/sources.ts +3 -3
- package/src/upstream-headers.test.ts +31 -7
- package/src/write-artifacts.test.ts +375 -0
- package/dist/agent-plugins-harness.d.ts +0 -54
- package/dist/agent-plugins-harness.js +0 -212
- package/dist/agent-plugins-harness.js.map +0 -1
- package/src/agent-plugins-harness.ts +0 -281
package/dist/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
import { promises as fs } from 'node:fs';
|
|
4
|
-
import { basename,
|
|
4
|
+
import { basename, join, relative, resolve } from 'node:path';
|
|
5
5
|
import { Readable } from 'node:stream';
|
|
6
|
+
import { setTimeout as sleep } from 'node:timers/promises';
|
|
6
7
|
import { createGunzip } from 'node:zlib';
|
|
7
8
|
import { extract } from 'tar-stream';
|
|
9
|
+
import { publishCatalog } from './catalog-store.js';
|
|
8
10
|
import { CatalogSchema, HarnessSchema, SourcesLockSchema, PluginSchema, } from './schema.js';
|
|
9
11
|
import { canonicalRepositoryUrl, parseJsonDocument, parseMarketplaceManifest, safeRelativePath, } from './sources.js';
|
|
10
12
|
import { buildConsumerIndex, CATALOG_INDEX_ARTIFACT, iconCoverage, } from './consumer-index.js';
|
|
@@ -13,6 +15,9 @@ import { stageIconBytes } from './icon-mirror.js';
|
|
|
13
15
|
import { isVendorHomepage, resolveSiteIcon, } from './site-icons.js';
|
|
14
16
|
import { normalizeDisplayName } from './display-name.js';
|
|
15
17
|
import { immutableAssetUrl } from './publication.js';
|
|
18
|
+
import { sourceDigest as bytesDigest } from './source-digest.js';
|
|
19
|
+
export { bytesDigest };
|
|
20
|
+
export { catalogFingerprint, publishCatalog, withCatalogRead, writeFileAtomically, } from './catalog-store.js';
|
|
16
21
|
/**
|
|
17
22
|
* Marks the catalog format this compiler emits.
|
|
18
23
|
*
|
|
@@ -55,14 +60,6 @@ export function canonicalJson(value) {
|
|
|
55
60
|
export function digest(value) {
|
|
56
61
|
return `sha256:${createHash('sha256').update(canonicalJson(value)).digest('hex')}`;
|
|
57
62
|
}
|
|
58
|
-
export function bytesDigest(files) {
|
|
59
|
-
const hash = createHash('sha256');
|
|
60
|
-
for (const [path, bytes] of [...files.entries()].toSorted(([left], [right]) => left.localeCompare(right))) {
|
|
61
|
-
hash.update(`${path.length}:${path}:${bytes.byteLength}:`);
|
|
62
|
-
hash.update(bytes);
|
|
63
|
-
}
|
|
64
|
-
return `sha256:${hash.digest('hex')}`;
|
|
65
|
-
}
|
|
66
63
|
export function stablePluginId(sourceId, upstreamName) {
|
|
67
64
|
return `plugin:${slug(sourceId)}:${slug(upstreamName)}`;
|
|
68
65
|
}
|
|
@@ -88,17 +85,22 @@ export async function synchronize(input) {
|
|
|
88
85
|
sourceId: config.sourceId,
|
|
89
86
|
commitSha,
|
|
90
87
|
}));
|
|
88
|
+
const resolvedLock = createSourcesLock(resolved);
|
|
91
89
|
const unchanged = input.fromLock === undefined &&
|
|
92
90
|
input.forceRebuild !== true &&
|
|
93
91
|
input.existingLock !== undefined &&
|
|
94
92
|
resolved.length === input.existingLock.sources.length &&
|
|
95
|
-
|
|
96
|
-
const previous = input.existingLock?.sources.find((entry) => entry.sourceId === source.
|
|
97
|
-
if (!previous)
|
|
93
|
+
resolvedLock.sources.every((source) => {
|
|
94
|
+
const previous = input.existingLock?.sources.find((entry) => entry.sourceId === source.sourceId);
|
|
95
|
+
if (!previous || sourceLockChanged(previous, source))
|
|
98
96
|
return false;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
if (!input.existingCatalog)
|
|
98
|
+
return true;
|
|
99
|
+
const priorCatalog = input.existingCatalog.sources.find((entry) => entry.sourceId === source.sourceId);
|
|
100
|
+
const config = resolved.find((entry) => entry.config.sourceId === source.sourceId).config;
|
|
101
|
+
return (!!priorCatalog &&
|
|
102
|
+
priorCatalog.displayName === config.displayName &&
|
|
103
|
+
priorCatalog.trustClassification === config.trustClassification);
|
|
102
104
|
});
|
|
103
105
|
if (unchanged) {
|
|
104
106
|
return {
|
|
@@ -135,7 +137,7 @@ export async function synchronize(input) {
|
|
|
135
137
|
...(input.transformRelease ? { transformRelease: input.transformRelease } : {}),
|
|
136
138
|
});
|
|
137
139
|
const catalog = buildResult.catalog;
|
|
138
|
-
const lock =
|
|
140
|
+
const lock = resolvedLock;
|
|
139
141
|
const artifacts = createArtifacts(orderLeadingPlugins(catalog, input.leading), lock, {
|
|
140
142
|
...(input.leading ? { leading: input.leading } : {}),
|
|
141
143
|
...(input.topCount !== undefined ? { topCount: input.topCount } : {}),
|
|
@@ -179,6 +181,7 @@ export async function buildCatalog(input) {
|
|
|
179
181
|
...(input.productCategories ? { productCategories: input.productCategories } : {}),
|
|
180
182
|
...(input.leading ? { leading: input.leading } : {}),
|
|
181
183
|
...(input.productIconOverrides ? { productIconOverrides: input.productIconOverrides } : {}),
|
|
184
|
+
...(input.iconAssetsDirectory ? { iconAssetsDirectory: input.iconAssetsDirectory } : {}),
|
|
182
185
|
})).catalog;
|
|
183
186
|
}
|
|
184
187
|
async function buildCatalogInternal(input) {
|
|
@@ -583,7 +586,7 @@ async function resolvePluginSource(pluginName, spec, source, resolveExternalRefs
|
|
|
583
586
|
const commitSha = pin?.resolvedCommitSha ??
|
|
584
587
|
spec.sha ??
|
|
585
588
|
(resolveExternalRefs
|
|
586
|
-
? await resolveGitRef(spec.repositoryUrl,
|
|
589
|
+
? await resolveGitRef(spec.repositoryUrl, externalGitRef(spec.ref))
|
|
587
590
|
: source.commitSha);
|
|
588
591
|
return {
|
|
589
592
|
repositoryUrl: canonicalUrl,
|
|
@@ -790,6 +793,15 @@ function findLicense(entry, metadata) {
|
|
|
790
793
|
source: metadata?.license !== undefined ? 'plugin-manifest' : 'marketplace-entry',
|
|
791
794
|
};
|
|
792
795
|
}
|
|
796
|
+
function sourceLockChanged(previous, current) {
|
|
797
|
+
return (previous.repositoryUrl !== current.repositoryUrl ||
|
|
798
|
+
previous.defaultBranch !== current.defaultBranch ||
|
|
799
|
+
previous.manifestPath !== current.manifestPath ||
|
|
800
|
+
previous.marketplaceDialect !== current.marketplaceDialect ||
|
|
801
|
+
previous.resolvedCommitSha !== current.resolvedCommitSha ||
|
|
802
|
+
previous.sourceManifestDigest !== current.sourceManifestDigest ||
|
|
803
|
+
digest(previous.pluginPins) !== digest(current.pluginPins));
|
|
804
|
+
}
|
|
793
805
|
function createSourcesLock(sources) {
|
|
794
806
|
const entries = sources
|
|
795
807
|
.map((source) => ({
|
|
@@ -997,38 +1009,12 @@ export function iconAssets(index) {
|
|
|
997
1009
|
}
|
|
998
1010
|
return [...assets.values()].toSorted((left, right) => left.name.localeCompare(right.name));
|
|
999
1011
|
}
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
await fs.writeFile(join(temporary, name), content, 'utf8');
|
|
1007
|
-
const backup = `${generatedDirectory}.previous`;
|
|
1008
|
-
await fs.rm(backup, { recursive: true, force: true });
|
|
1009
|
-
let movedExisting = false;
|
|
1010
|
-
try {
|
|
1011
|
-
await fs.rename(generatedDirectory, backup);
|
|
1012
|
-
movedExisting = true;
|
|
1013
|
-
}
|
|
1014
|
-
catch (error) {
|
|
1015
|
-
if (error.code !== 'ENOENT')
|
|
1016
|
-
throw error;
|
|
1017
|
-
}
|
|
1018
|
-
try {
|
|
1019
|
-
await fs.rename(temporary, generatedDirectory);
|
|
1020
|
-
}
|
|
1021
|
-
catch (error) {
|
|
1022
|
-
if (movedExisting)
|
|
1023
|
-
await fs.rename(backup, generatedDirectory);
|
|
1024
|
-
throw error;
|
|
1025
|
-
}
|
|
1026
|
-
if (movedExisting)
|
|
1027
|
-
await fs.rm(backup, { recursive: true, force: true });
|
|
1028
|
-
}
|
|
1029
|
-
finally {
|
|
1030
|
-
await fs.rm(temporary, { recursive: true, force: true });
|
|
1031
|
-
}
|
|
1012
|
+
/**
|
|
1013
|
+
* Publishes a verified artifact set to `generatedDirectory` under the output's
|
|
1014
|
+
* coordination lock. See `catalog-store.ts` for the publication contract.
|
|
1015
|
+
*/
|
|
1016
|
+
export async function writeArtifacts(generatedDirectory, artifacts, options = {}) {
|
|
1017
|
+
await publishCatalog(generatedDirectory, artifacts, options);
|
|
1032
1018
|
}
|
|
1033
1019
|
export function verifyArtifacts(artifacts) {
|
|
1034
1020
|
const parsedCatalog = CatalogSchema.parse(JSON.parse(artifacts['catalog.v1.json']));
|
|
@@ -1330,10 +1316,15 @@ function createChangeReport(catalog, lock, existingLock, existingCatalog, skippe
|
|
|
1330
1316
|
? lock.sources
|
|
1331
1317
|
.filter((source) => (() => {
|
|
1332
1318
|
const previous = existingLock.sources.find((candidate) => candidate.sourceId === source.sourceId);
|
|
1319
|
+
const priorCatalog = existingCatalog?.sources.find((entry) => entry.sourceId === source.sourceId);
|
|
1320
|
+
const currentCatalog = catalog.sources.find((entry) => entry.sourceId === source.sourceId);
|
|
1333
1321
|
return (!previous ||
|
|
1334
|
-
previous
|
|
1335
|
-
|
|
1336
|
-
|
|
1322
|
+
sourceLockChanged(previous, source) ||
|
|
1323
|
+
(existingCatalog !== undefined &&
|
|
1324
|
+
(!priorCatalog ||
|
|
1325
|
+
!currentCatalog ||
|
|
1326
|
+
priorCatalog.displayName !== currentCatalog.displayName ||
|
|
1327
|
+
priorCatalog.trustClassification !== currentCatalog.trustClassification)));
|
|
1337
1328
|
})())
|
|
1338
1329
|
.map((source) => source.sourceId)
|
|
1339
1330
|
: lock.sources.map((source) => source.sourceId);
|
|
@@ -1416,7 +1407,10 @@ async function resolvePluginPins(input) {
|
|
|
1416
1407
|
}
|
|
1417
1408
|
const repositoryUrl = canonicalRepositoryUrl(entry.source.repositoryUrl);
|
|
1418
1409
|
const pluginSubdirectory = entry.source.subdirectory;
|
|
1419
|
-
|
|
1410
|
+
// Existing locks describe the prior sync; only explicit replay freezes revisions.
|
|
1411
|
+
const locked = input.fromLock
|
|
1412
|
+
? input.lockedPins?.find((pin) => pin.pluginName === entry.name)
|
|
1413
|
+
: undefined;
|
|
1420
1414
|
if (input.fromLock && !locked)
|
|
1421
1415
|
throw new Error(`SOURCE_LOCK_PLUGIN_PIN_MISSING: ${input.config.sourceId}:${entry.name}`);
|
|
1422
1416
|
if (locked) {
|
|
@@ -1428,13 +1422,16 @@ async function resolvePluginPins(input) {
|
|
|
1428
1422
|
}
|
|
1429
1423
|
const resolvedCommitSha = entry.source.sha ??
|
|
1430
1424
|
(input.resolveExternalRefs
|
|
1431
|
-
? await resolveGitRef(repositoryUrl, entry.source.ref
|
|
1432
|
-
? entry.source.ref
|
|
1433
|
-
: `refs/heads/${entry.source.ref ?? 'main'}`)
|
|
1425
|
+
? await resolveGitRef(repositoryUrl, externalGitRef(entry.source.ref))
|
|
1434
1426
|
: input.sourceCommitSha);
|
|
1435
1427
|
return { pluginName: entry.name, repositoryUrl, pluginSubdirectory, resolvedCommitSha };
|
|
1436
1428
|
});
|
|
1437
|
-
return pins.toSorted((left, right) => left.pluginName.
|
|
1429
|
+
return pins.toSorted((left, right) => left.pluginName < right.pluginName ? -1 : left.pluginName > right.pluginName ? 1 : 0);
|
|
1430
|
+
}
|
|
1431
|
+
function externalGitRef(ref) {
|
|
1432
|
+
if (ref === undefined)
|
|
1433
|
+
return 'HEAD';
|
|
1434
|
+
return ref.startsWith('refs/') ? ref : `refs/heads/${ref}`;
|
|
1438
1435
|
}
|
|
1439
1436
|
async function mapWithConcurrency(values, concurrency, mapper) {
|
|
1440
1437
|
const results = Array.from({ length: values.length });
|
|
@@ -1456,7 +1453,8 @@ async function readFixtureManifest(config, fixtureRoot) {
|
|
|
1456
1453
|
return fs.readFile(join(resolve(fixtureRoot), config.fixturePath, config.manifestPath), 'utf8');
|
|
1457
1454
|
}
|
|
1458
1455
|
async function resolveGitRef(repositoryUrl, ref) {
|
|
1459
|
-
const
|
|
1456
|
+
const peeled = ref.startsWith('refs/tags/') && !ref.endsWith('^{}') ? `${ref}^{}` : undefined;
|
|
1457
|
+
const result = spawn('git', ['ls-remote', canonicalRepositoryUrl(repositoryUrl), ref, ...(peeled ? [peeled] : [])], {
|
|
1460
1458
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
1461
1459
|
});
|
|
1462
1460
|
const [stdout, stderr, exitCode] = await Promise.all([
|
|
@@ -1479,7 +1477,12 @@ async function resolveGitRef(repositoryUrl, ref) {
|
|
|
1479
1477
|
]);
|
|
1480
1478
|
if (exitCode !== 0)
|
|
1481
1479
|
throw new Error(`GIT_REF_LOOKUP_FAILED: ${repositoryUrl}:${ref}:${stderr.trim()}`);
|
|
1482
|
-
const
|
|
1480
|
+
const rows = stdout
|
|
1481
|
+
.trim()
|
|
1482
|
+
.split('\n')
|
|
1483
|
+
.map((line) => line.split(/\s+/));
|
|
1484
|
+
const sha = (peeled && rows.find((row) => row[1] === peeled)?.[0]) ??
|
|
1485
|
+
rows.find((row) => row[1] === ref)?.[0];
|
|
1483
1486
|
if (!sha || !/^[a-f0-9]{40}$/.test(sha))
|
|
1484
1487
|
throw new Error(`GIT_REF_NOT_FOUND: ${repositoryUrl}:${ref}`);
|
|
1485
1488
|
return sha;
|
|
@@ -1503,7 +1506,8 @@ function rawUrl(repositoryUrl, commitSha, path) {
|
|
|
1503
1506
|
return `https://raw.githubusercontent.com${parsed.pathname}/${commitSha}/${path.split('/').map(encodeURIComponent).join('/')}`;
|
|
1504
1507
|
}
|
|
1505
1508
|
async function fetchText(url) {
|
|
1506
|
-
|
|
1509
|
+
// Manifest requests use the same bounded retry policy as the tree and archive.
|
|
1510
|
+
const response = await fetchUpstream(url, { headers: upstreamRequestHeaders(url) });
|
|
1507
1511
|
if (!response.ok)
|
|
1508
1512
|
throw new Error(`HTTP_FETCH_FAILED: ${response.status}:${url}`);
|
|
1509
1513
|
return response.text();
|
|
@@ -1579,9 +1583,6 @@ const UPSTREAM_WEATHER_SKIP_CODES = new Set([
|
|
|
1579
1583
|
'PLUGIN_FILE_UNAVAILABLE',
|
|
1580
1584
|
'PLUGIN_PATH_NOT_FOUND',
|
|
1581
1585
|
]);
|
|
1582
|
-
function sleep(ms) {
|
|
1583
|
-
return new Promise((resolveSleep) => setTimeout(resolveSleep, ms));
|
|
1584
|
-
}
|
|
1585
1586
|
function upstreamRetryDelayMs(attempt, response) {
|
|
1586
1587
|
const retryAfter = response?.headers.get('retry-after');
|
|
1587
1588
|
if (retryAfter !== null && retryAfter !== undefined) {
|
|
@@ -1615,9 +1616,9 @@ async function fetchUpstream(url, init) {
|
|
|
1615
1616
|
response = await fetch(url, init);
|
|
1616
1617
|
}
|
|
1617
1618
|
catch (error) {
|
|
1618
|
-
if (attempt >= UPSTREAM_FETCH_MAX_ATTEMPTS - 1)
|
|
1619
|
+
if (init.signal?.aborted || attempt >= UPSTREAM_FETCH_MAX_ATTEMPTS - 1)
|
|
1619
1620
|
throw error;
|
|
1620
|
-
await sleep(upstreamRetryDelayMs(attempt));
|
|
1621
|
+
await sleep(upstreamRetryDelayMs(attempt), undefined, { signal: init.signal ?? undefined });
|
|
1621
1622
|
attempt += 1;
|
|
1622
1623
|
continue;
|
|
1623
1624
|
}
|
|
@@ -1625,7 +1626,10 @@ async function fetchUpstream(url, init) {
|
|
|
1625
1626
|
!isRetryableUpstreamStatus(response.status, response) ||
|
|
1626
1627
|
attempt >= UPSTREAM_FETCH_MAX_ATTEMPTS - 1)
|
|
1627
1628
|
return response;
|
|
1628
|
-
await
|
|
1629
|
+
await response.body?.cancel().catch(() => { });
|
|
1630
|
+
await sleep(upstreamRetryDelayMs(attempt, response), undefined, {
|
|
1631
|
+
signal: init.signal ?? undefined,
|
|
1632
|
+
});
|
|
1629
1633
|
attempt += 1;
|
|
1630
1634
|
}
|
|
1631
1635
|
}
|
|
@@ -1740,23 +1744,29 @@ export class NetworkSnapshotLoader {
|
|
|
1740
1744
|
if (!owner || !repo)
|
|
1741
1745
|
throw new Error('GIT_REPOSITORY_INVALID');
|
|
1742
1746
|
const key = `${owner}/${repo}@${commitSha}`;
|
|
1743
|
-
let
|
|
1744
|
-
if (!
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1747
|
+
let pendingTree = this.#trees.get(key);
|
|
1748
|
+
if (!pendingTree) {
|
|
1749
|
+
pendingTree = (async () => {
|
|
1750
|
+
const treeUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/${commitSha}?recursive=1`;
|
|
1751
|
+
const response = await fetchUpstream(treeUrl, {
|
|
1752
|
+
headers: upstreamRequestHeaders(treeUrl),
|
|
1753
|
+
});
|
|
1754
|
+
if (!response.ok)
|
|
1755
|
+
throw new PluginSafetyError('PLUGIN_SOURCE_UNAVAILABLE', [`${owner}/${repo}`]);
|
|
1756
|
+
const payload = (await response.json());
|
|
1757
|
+
if (payload.truncated)
|
|
1758
|
+
throw new Error(`GIT_TREE_TRUNCATED: ${owner}/${repo}@${commitSha}`);
|
|
1759
|
+
return (payload.tree ?? []).map((entry) => ({
|
|
1760
|
+
...entry,
|
|
1761
|
+
path: safeRelativePath(entry.path, 'TREE_PATH_INVALID'),
|
|
1762
|
+
}));
|
|
1763
|
+
})().catch((error) => {
|
|
1764
|
+
this.#trees.delete(key);
|
|
1765
|
+
throw error;
|
|
1748
1766
|
});
|
|
1749
|
-
|
|
1750
|
-
throw new PluginSafetyError('PLUGIN_SOURCE_UNAVAILABLE', [`${owner}/${repo}`]);
|
|
1751
|
-
const payload = (await response.json());
|
|
1752
|
-
if (payload.truncated)
|
|
1753
|
-
throw new Error(`GIT_TREE_TRUNCATED: ${owner}/${repo}@${commitSha}`);
|
|
1754
|
-
tree = (payload.tree ?? []).map((entry) => ({
|
|
1755
|
-
...entry,
|
|
1756
|
-
path: safeRelativePath(entry.path, 'TREE_PATH_INVALID'),
|
|
1757
|
-
}));
|
|
1758
|
-
this.#trees.set(key, tree);
|
|
1767
|
+
this.#trees.set(key, pendingTree);
|
|
1759
1768
|
}
|
|
1769
|
+
const tree = await pendingTree;
|
|
1760
1770
|
const prefix = pluginSubdirectory ? `${safeRelativePath(pluginSubdirectory)}/` : '';
|
|
1761
1771
|
const submodules = tree.filter((entry) => entry.path.startsWith(prefix) && (entry.type === 'commit' || entry.mode === '160000'));
|
|
1762
1772
|
if (submodules.length > 0)
|
|
@@ -1799,13 +1809,18 @@ export class NetworkSnapshotLoader {
|
|
|
1799
1809
|
const cached = this.#archives.get(key);
|
|
1800
1810
|
if (cached)
|
|
1801
1811
|
return cached;
|
|
1802
|
-
const
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1812
|
+
const pending = (async () => {
|
|
1813
|
+
const url = `https://api.github.com/repos/${owner}/${repo}/tarball/${commitSha}`;
|
|
1814
|
+
const response = await fetchUpstream(url, { headers: upstreamRequestHeaders(url) });
|
|
1815
|
+
if (!response.ok)
|
|
1816
|
+
throw new PluginSafetyError('PLUGIN_SOURCE_UNAVAILABLE', [`${owner}/${repo}`]);
|
|
1817
|
+
return readArchiveFiles(new Uint8Array(await response.arrayBuffer()), this.#policy);
|
|
1818
|
+
})().catch((error) => {
|
|
1819
|
+
this.#archives.delete(key);
|
|
1820
|
+
throw error;
|
|
1821
|
+
});
|
|
1822
|
+
this.#archives.set(key, pending);
|
|
1823
|
+
return pending;
|
|
1809
1824
|
}
|
|
1810
1825
|
}
|
|
1811
1826
|
export class FixtureSnapshotLoader {
|