fix(api): expose legacy zip artifact aliases
Some checks are pending
CI / types-build (push) Waiting to run
CI / static (push) Waiting to run
CI / unit (push) Waiting to run
CI / packages (push) Waiting to run
CI / e2e-http (push) Waiting to run
CI / playwright-smoke (push) Waiting to run
Security Gate: Secret Scanning / Scan for Verified Secrets (push) Waiting to run
Some checks are pending
CI / types-build (push) Waiting to run
CI / static (push) Waiting to run
CI / unit (push) Waiting to run
CI / packages (push) Waiting to run
CI / e2e-http (push) Waiting to run
CI / playwright-smoke (push) Waiting to run
Security Gate: Secret Scanning / Scan for Verified Secrets (push) Waiting to run
Expose legacy ZIP resolver compatibility aliases without confusing publish-time content hashes for downloaded archive integrity.
This commit is contained in:
parent
59fc54ff64
commit
199e6a0cdf
@ -3775,6 +3775,142 @@ describe("httpApiV1 handlers", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("package artifact endpoint exposes legacy zip resolver compatibility aliases", async () => {
|
||||
const runQuery = vi.fn(async (_query: unknown, args: Record<string, unknown>) => {
|
||||
if ("name" in args && !("version" in args)) {
|
||||
return {
|
||||
package: {
|
||||
_id: "packages:demo-plugin",
|
||||
name: "demo-plugin",
|
||||
displayName: "Demo Plugin",
|
||||
family: "code-plugin",
|
||||
tags: { latest: "packageReleases:1" },
|
||||
latestReleaseId: "packageReleases:1",
|
||||
channel: "community",
|
||||
isOfficial: false,
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
},
|
||||
latestRelease: null,
|
||||
owner: { _id: "publishers:demo", handle: "demo" },
|
||||
};
|
||||
}
|
||||
if ("name" in args && "version" in args) {
|
||||
return {
|
||||
package: {
|
||||
_id: "packages:demo-plugin",
|
||||
name: "demo-plugin",
|
||||
displayName: "Demo Plugin",
|
||||
family: "code-plugin",
|
||||
},
|
||||
version: {
|
||||
_id: "packageReleases:1",
|
||||
packageId: "packages:demo-plugin",
|
||||
version: "1.0.0",
|
||||
createdAt: 1,
|
||||
changelog: "Initial release",
|
||||
distTags: ["latest"],
|
||||
files: [],
|
||||
artifactKind: "legacy-zip",
|
||||
integritySha256: "a".repeat(64),
|
||||
sha256hash: "b".repeat(64),
|
||||
},
|
||||
};
|
||||
}
|
||||
return null;
|
||||
});
|
||||
const runMutation = vi.fn().mockResolvedValue(okRate());
|
||||
|
||||
const response = await __handlers.packagesGetRouterV1Handler(
|
||||
makeCtx({ runQuery, runMutation }),
|
||||
new Request("https://example.com/api/v1/packages/demo-plugin/versions/1.0.0/artifact"),
|
||||
);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
await expect(response.json()).resolves.toMatchObject({
|
||||
package: { name: "demo-plugin" },
|
||||
version: "1.0.0",
|
||||
artifact: {
|
||||
kind: "legacy-zip",
|
||||
sha256: "b".repeat(64),
|
||||
format: "zip",
|
||||
source: "clawhub",
|
||||
artifactKind: "legacy-zip",
|
||||
artifactSha256: "b".repeat(64),
|
||||
packageName: "demo-plugin",
|
||||
version: "1.0.0",
|
||||
downloadUrl: "https://example.com/api/v1/packages/demo-plugin/download?version=1.0.0",
|
||||
legacyDownloadUrl: "https://example.com/api/v1/packages/demo-plugin/download?version=1.0.0",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("package artifact endpoint omits legacy zip archive aliases when archive hash is missing", async () => {
|
||||
const runQuery = vi.fn(async (_query: unknown, args: Record<string, unknown>) => {
|
||||
if ("name" in args && !("version" in args)) {
|
||||
return {
|
||||
package: {
|
||||
_id: "packages:demo-plugin",
|
||||
name: "demo-plugin",
|
||||
displayName: "Demo Plugin",
|
||||
family: "code-plugin",
|
||||
tags: { latest: "packageReleases:1" },
|
||||
latestReleaseId: "packageReleases:1",
|
||||
channel: "community",
|
||||
isOfficial: false,
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
},
|
||||
latestRelease: null,
|
||||
owner: { _id: "publishers:demo", handle: "demo" },
|
||||
};
|
||||
}
|
||||
if ("name" in args && "version" in args) {
|
||||
return {
|
||||
package: {
|
||||
_id: "packages:demo-plugin",
|
||||
name: "demo-plugin",
|
||||
displayName: "Demo Plugin",
|
||||
family: "code-plugin",
|
||||
},
|
||||
version: {
|
||||
_id: "packageReleases:1",
|
||||
packageId: "packages:demo-plugin",
|
||||
version: "1.0.0",
|
||||
createdAt: 1,
|
||||
changelog: "Initial release",
|
||||
distTags: ["latest"],
|
||||
files: [],
|
||||
artifactKind: "legacy-zip",
|
||||
integritySha256: "a".repeat(64),
|
||||
},
|
||||
};
|
||||
}
|
||||
return null;
|
||||
});
|
||||
const runMutation = vi.fn().mockResolvedValue(okRate());
|
||||
|
||||
const response = await __handlers.packagesGetRouterV1Handler(
|
||||
makeCtx({ runQuery, runMutation }),
|
||||
new Request("https://example.com/api/v1/packages/demo-plugin/versions/1.0.0/artifact"),
|
||||
);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
const body = await response.json();
|
||||
expect(body).toMatchObject({
|
||||
artifact: {
|
||||
kind: "legacy-zip",
|
||||
format: "zip",
|
||||
source: "clawhub",
|
||||
artifactKind: "legacy-zip",
|
||||
packageName: "demo-plugin",
|
||||
version: "1.0.0",
|
||||
},
|
||||
});
|
||||
expect(body.artifact).not.toHaveProperty("sha256");
|
||||
expect(body.artifact).not.toHaveProperty("artifactSha256");
|
||||
});
|
||||
|
||||
it("package artifact endpoint accepts split scoped package paths", async () => {
|
||||
const runQuery = vi.fn(async (_query: unknown, args: Record<string, unknown>) => {
|
||||
if ("name" in args && !("version" in args)) {
|
||||
|
||||
@ -364,7 +364,7 @@ function getReleaseSecurityBlock(release: ReleaseLike) {
|
||||
return getPackageDownloadSecurityBlock(release);
|
||||
}
|
||||
|
||||
function toReleaseArtifact(release: ReleaseLike) {
|
||||
function toReleaseArtifact(release: ReleaseLike, packageName?: string) {
|
||||
if (release.artifactKind === "npm-pack") {
|
||||
return {
|
||||
kind: "npm-pack" as const,
|
||||
@ -378,10 +378,16 @@ function toReleaseArtifact(release: ReleaseLike) {
|
||||
npmFileCount: release.npmFileCount,
|
||||
};
|
||||
}
|
||||
const sha256 = release.sha256hash;
|
||||
return {
|
||||
kind: "legacy-zip" as const,
|
||||
sha256: release.integritySha256 ?? release.sha256hash,
|
||||
...(sha256 ? { sha256 } : {}),
|
||||
format: "zip",
|
||||
source: "clawhub" as const,
|
||||
artifactKind: "legacy-zip" as const,
|
||||
...(sha256 ? { artifactSha256: sha256 } : {}),
|
||||
packageName,
|
||||
version: release.version,
|
||||
};
|
||||
}
|
||||
|
||||
@ -2347,7 +2353,7 @@ export async function packagesGetRouterV1Handler(ctx: ActionCtx, request: Reques
|
||||
},
|
||||
version: release.version,
|
||||
artifact: {
|
||||
...toReleaseArtifact(release),
|
||||
...toReleaseArtifact(release, publicPackage!.name),
|
||||
...releaseArtifactUrls(request, publicPackage!.name, release),
|
||||
},
|
||||
},
|
||||
@ -2427,7 +2433,7 @@ export async function packagesGetRouterV1Handler(ctx: ActionCtx, request: Reques
|
||||
compatibility: result.version.compatibility ?? null,
|
||||
capabilities: result.version.capabilities ?? null,
|
||||
verification: result.version.verification ?? null,
|
||||
artifact: toReleaseArtifact(result.version),
|
||||
artifact: toReleaseArtifact(result.version, result.package.name),
|
||||
sha256hash: result.version.sha256hash ?? null,
|
||||
vtAnalysis: result.version.vtAnalysis ?? null,
|
||||
llmAnalysis: result.version.llmAnalysis ?? null,
|
||||
@ -2669,7 +2675,7 @@ export async function npmMirrorGetHandler(ctx: ActionCtx, request: Request) {
|
||||
|
||||
const versions = Object.fromEntries(
|
||||
releases.map((release) => {
|
||||
const artifact = toReleaseArtifact(release);
|
||||
const artifact = toReleaseArtifact(release, detail.package!.name);
|
||||
const urls = releaseArtifactUrls(request, detail.package!.name, release);
|
||||
return [
|
||||
release.version,
|
||||
|
||||
@ -103,6 +103,11 @@ export const PackageArtifactSummarySchema = type({
|
||||
npmTarballName: "string?",
|
||||
npmUnpackedSize: "number?",
|
||||
npmFileCount: "number?",
|
||||
source: '"clawhub"?',
|
||||
artifactKind: PackageArtifactKindSchema.optional(),
|
||||
artifactSha256: "string?",
|
||||
packageName: "string?",
|
||||
version: "string?",
|
||||
});
|
||||
export type PackageArtifactSummary = (typeof PackageArtifactSummarySchema)[inferred];
|
||||
|
||||
@ -315,6 +320,11 @@ export const ApiV1PackageArtifactResponseSchema = type({
|
||||
downloadUrl: "string",
|
||||
tarballUrl: "string?",
|
||||
legacyDownloadUrl: "string?",
|
||||
source: '"clawhub"?',
|
||||
artifactKind: PackageArtifactKindSchema.optional(),
|
||||
artifactSha256: "string?",
|
||||
packageName: "string?",
|
||||
version: "string?",
|
||||
}),
|
||||
});
|
||||
export type ApiV1PackageArtifactResponse = (typeof ApiV1PackageArtifactResponseSchema)[inferred];
|
||||
|
||||
20
packages/schema/dist/packages.d.ts
vendored
20
packages/schema/dist/packages.d.ts
vendored
@ -79,6 +79,11 @@ export declare const PackageArtifactSummarySchema: import("arktype/internal/vari
|
||||
npmTarballName?: string | undefined;
|
||||
npmUnpackedSize?: number | undefined;
|
||||
npmFileCount?: number | undefined;
|
||||
source?: "clawhub" | undefined;
|
||||
artifactKind?: "legacy-zip" | "npm-pack" | undefined;
|
||||
artifactSha256?: string | undefined;
|
||||
packageName?: string | undefined;
|
||||
version?: string | undefined;
|
||||
}, {}>;
|
||||
export type PackageArtifactSummary = (typeof PackageArtifactSummarySchema)[inferred];
|
||||
export declare const PackagePublishArtifactSchema: import("arktype/internal/variants/object.ts").ObjectType<{
|
||||
@ -331,6 +336,11 @@ export declare const ApiV1PackageResponseSchema: import("arktype/internal/varian
|
||||
npmTarballName?: string | undefined;
|
||||
npmUnpackedSize?: number | undefined;
|
||||
npmFileCount?: number | undefined;
|
||||
source?: "clawhub" | undefined;
|
||||
artifactKind?: "legacy-zip" | "npm-pack" | undefined;
|
||||
artifactSha256?: string | undefined;
|
||||
packageName?: string | undefined;
|
||||
version?: string | undefined;
|
||||
} | null | undefined;
|
||||
stats?: {
|
||||
downloads: number;
|
||||
@ -414,6 +424,11 @@ export declare const ApiV1PackageVersionResponseSchema: import("arktype/internal
|
||||
npmTarballName?: string | undefined;
|
||||
npmUnpackedSize?: number | undefined;
|
||||
npmFileCount?: number | undefined;
|
||||
source?: "clawhub" | undefined;
|
||||
artifactKind?: "legacy-zip" | "npm-pack" | undefined;
|
||||
artifactSha256?: string | undefined;
|
||||
packageName?: string | undefined;
|
||||
version?: string | undefined;
|
||||
} | null | undefined;
|
||||
sha256hash?: string | null | undefined;
|
||||
vtAnalysis?: {
|
||||
@ -477,6 +492,11 @@ export declare const ApiV1PackageArtifactResponseSchema: import("arktype/interna
|
||||
npmFileCount?: number | undefined;
|
||||
tarballUrl?: string | undefined;
|
||||
legacyDownloadUrl?: string | undefined;
|
||||
source?: "clawhub" | undefined;
|
||||
artifactKind?: "legacy-zip" | "npm-pack" | undefined;
|
||||
artifactSha256?: string | undefined;
|
||||
packageName?: string | undefined;
|
||||
version?: string | undefined;
|
||||
};
|
||||
}, {}>;
|
||||
export type ApiV1PackageArtifactResponse = (typeof ApiV1PackageArtifactResponseSchema)[inferred];
|
||||
|
||||
10
packages/schema/dist/packages.js
vendored
10
packages/schema/dist/packages.js
vendored
@ -64,6 +64,11 @@ export const PackageArtifactSummarySchema = type({
|
||||
npmTarballName: "string?",
|
||||
npmUnpackedSize: "number?",
|
||||
npmFileCount: "number?",
|
||||
source: '"clawhub"?',
|
||||
artifactKind: PackageArtifactKindSchema.optional(),
|
||||
artifactSha256: "string?",
|
||||
packageName: "string?",
|
||||
version: "string?",
|
||||
});
|
||||
export const PackagePublishArtifactSchema = type({
|
||||
kind: '"npm-pack"',
|
||||
@ -249,6 +254,11 @@ export const ApiV1PackageArtifactResponseSchema = type({
|
||||
downloadUrl: "string",
|
||||
tarballUrl: "string?",
|
||||
legacyDownloadUrl: "string?",
|
||||
source: '"clawhub"?',
|
||||
artifactKind: PackageArtifactKindSchema.optional(),
|
||||
artifactSha256: "string?",
|
||||
packageName: "string?",
|
||||
version: "string?",
|
||||
}),
|
||||
});
|
||||
export const PackageReleaseModerationRequestSchema = type({
|
||||
|
||||
2
packages/schema/dist/packages.js.map
vendored
2
packages/schema/dist/packages.js.map
vendored
File diff suppressed because one or more lines are too long
@ -103,6 +103,11 @@ export const PackageArtifactSummarySchema = type({
|
||||
npmTarballName: "string?",
|
||||
npmUnpackedSize: "number?",
|
||||
npmFileCount: "number?",
|
||||
source: '"clawhub"?',
|
||||
artifactKind: PackageArtifactKindSchema.optional(),
|
||||
artifactSha256: "string?",
|
||||
packageName: "string?",
|
||||
version: "string?",
|
||||
});
|
||||
export type PackageArtifactSummary = (typeof PackageArtifactSummarySchema)[inferred];
|
||||
|
||||
@ -321,6 +326,11 @@ export const ApiV1PackageArtifactResponseSchema = type({
|
||||
downloadUrl: "string",
|
||||
tarballUrl: "string?",
|
||||
legacyDownloadUrl: "string?",
|
||||
source: '"clawhub"?',
|
||||
artifactKind: PackageArtifactKindSchema.optional(),
|
||||
artifactSha256: "string?",
|
||||
packageName: "string?",
|
||||
version: "string?",
|
||||
}),
|
||||
});
|
||||
export type ApiV1PackageArtifactResponse = (typeof ApiV1PackageArtifactResponseSchema)[inferred];
|
||||
|
||||
Loading…
Reference in New Issue
Block a user