Skip to content

Commit 4056a38

Browse files
committed
fix(create): make the tarball manifest probe best-effort
A probe failure (download, size cap, integrity, parse) on a normal @scope/create package without a manifest must not block the passthrough path that previously handled it — degrade to 'no manifest' instead. Schema errors stay loud: validateManifest runs outside the try, so a manifest that is present but malformed still throws.
1 parent 4517135 commit 4056a38

2 files changed

Lines changed: 32 additions & 3 deletions

File tree

packages/cli/src/create/__tests__/org-manifest.spec.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,23 @@ describe('readOrgManifest', () => {
258258
expect(spy).toHaveBeenCalledTimes(1);
259259
});
260260

261+
it('treats a tarball probe failure as "no manifest" so passthrough still works', async () => {
262+
// A normal @scope/create package (no manifest anywhere) whose tarball
263+
// cannot be probed — e.g. a download error — must not turn into a hard
264+
// failure; `null` lets the caller fall through to the passthrough path.
265+
vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
266+
const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;
267+
if (url.endsWith('.tgz')) {
268+
throw new Error('network unreachable');
269+
}
270+
return new Response(
271+
JSON.stringify(packument(undefined, { dist: { tarball: TARBALL_URL } })),
272+
{ status: 200 },
273+
);
274+
});
275+
expect(await readOrgManifest('@your-org')).toBeNull();
276+
});
277+
261278
it('returns null when createConfig.templates is an empty array', async () => {
262279
mockFetchJson(packument([]));
263280
expect(await readOrgManifest('@your-org')).toBeNull();

packages/cli/src/create/org-manifest.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -339,10 +339,22 @@ export async function readOrgManifest(
339339
}
340340
let templates = validateManifest(meta, packageName);
341341
if (!templates && meta.createConfig === undefined && meta.dist?.tarball) {
342-
// `createConfig` absent (not merely empty) → the registry stripped it from
343-
// the version metadata; recover it from the published tarball. See
342+
// `createConfig` absent (not merely empty) → either the registry stripped
343+
// it from the version metadata, or the package simply has no manifest.
344+
// Probe the published tarball to tell the two apart. See
344345
// `readPackageJsonFromTarball` for the full rationale.
345-
const packageJson = await readPackageJsonFromTarball(meta.dist.tarball, meta.dist.integrity);
346+
//
347+
// The probe is best-effort: a download/size/integrity/parse failure must
348+
// not block normal `@scope/create` packages that never had a manifest
349+
// from reaching the passthrough path, so it degrades to "no manifest".
350+
// A manifest that IS present but malformed still throws below —
351+
// `validateManifest` runs outside the try so schema errors stay loud.
352+
let packageJson: unknown = null;
353+
try {
354+
packageJson = await readPackageJsonFromTarball(meta.dist.tarball, meta.dist.integrity);
355+
} catch {
356+
packageJson = null;
357+
}
346358
templates = validateManifest(packageJson, packageName);
347359
}
348360
if (!templates) {

0 commit comments

Comments
 (0)