From 1ef6d24285bfaf430fb4e6d3f8271c02fdedd61d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zieli=C5=84ski?= Date: Thu, 13 Nov 2025 14:59:30 +0100 Subject: [PATCH 1/2] [Blueprints v1] Rewrite github.com/owner/repo/raw URLs --- .../blueprints/src/lib/v1/resources.spec.ts | 11 +++++++++++ .../playground/blueprints/src/lib/v1/resources.ts | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/playground/blueprints/src/lib/v1/resources.spec.ts b/packages/playground/blueprints/src/lib/v1/resources.spec.ts index 2f0bf34b82..3eceae1996 100644 --- a/packages/playground/blueprints/src/lib/v1/resources.spec.ts +++ b/packages/playground/blueprints/src/lib/v1/resources.spec.ts @@ -30,6 +30,17 @@ describe('UrlResource', () => { 'https://raw.githubusercontent.com/WordPress/wordpress-develop/trunk/src/wp-includes/version.php' ); }); + + it('should translate github.com raw URLs into raw.githubusercontent.com URLs', () => { + const resource = new UrlResource({ + resource: 'url', + url: 'https://github.com/adamziel/blueprints/raw/f49382e89099806a8eede4feba41a9a7ab89bcfe/blueprints%2Fbeta-rc%2Fblueprint.json', + caption: 'Example', + }); + expect(resource.getURL()).toBe( + 'https://raw.githubusercontent.com/adamziel/blueprints/f49382e89099806a8eede4feba41a9a7ab89bcfe/blueprints%2Fbeta-rc%2Fblueprint.json' + ); + }); }); describe('GitDirectoryResource', () => { diff --git a/packages/playground/blueprints/src/lib/v1/resources.ts b/packages/playground/blueprints/src/lib/v1/resources.ts index 675ca531f5..c20c06b1c7 100644 --- a/packages/playground/blueprints/src/lib/v1/resources.ts +++ b/packages/playground/blueprints/src/lib/v1/resources.ts @@ -529,7 +529,7 @@ export class UrlResource extends FetchResource { */ if (this.resource.url.startsWith('https://github.com/')) { const match = this.resource.url.match( - /^https:\/\/github\.com\/(?[^/]+)\/(?[^/]+)\/blob\/(?[^/]+)\/(?.+[^/])$/ + /^https:\/\/github\.com\/(?[^/]+)\/(?[^/]+)\/(?:blob|raw)\/(?[^/]+)\/(?.+[^/])$/ ); if (match?.groups) { this.resource = { From c8acdcc7eeaa8d64254a2451f6dfe5c92e3fb308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zieli=C5=84ski?= Date: Thu, 13 Nov 2025 15:18:40 +0100 Subject: [PATCH 2/2] Replace github URLs via ?blueprint-url --- .../playground/website/builder/builder.ts | 9 ++++-- .../state/url/resolve-blueprint-from-url.ts | 30 ++++++++++++++++--- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/packages/playground/website/builder/builder.ts b/packages/playground/website/builder/builder.ts index 307afddd89..1b993da81d 100644 --- a/packages/playground/website/builder/builder.ts +++ b/packages/playground/website/builder/builder.ts @@ -877,11 +877,14 @@ async function initializeBlueprint() { try { const blueprintUrl = new URL(urlParams.get('blueprint-url')); if (blueprintUrl.hostname === 'github.com') { - blueprintUrl.pathname = blueprintUrl.pathname.replace( - /^\/([^/]+)\/([^/]+)\/blob/, + const rewrittenPath = blueprintUrl.pathname.replace( + /^\/([^/]+)\/([^/]+)\/(?:blob|raw)\//, '/$1/$2/' ); - blueprintUrl.hostname = 'raw.githubusercontent.com'; + if (rewrittenPath !== blueprintUrl.pathname) { + blueprintUrl.pathname = rewrittenPath; + blueprintUrl.hostname = 'raw.githubusercontent.com'; + } } console.log('blueprintUrl.toString()', blueprintUrl.toString()); const response = await fetch(blueprintUrl.toString()); diff --git a/packages/playground/website/src/lib/state/url/resolve-blueprint-from-url.ts b/packages/playground/website/src/lib/state/url/resolve-blueprint-from-url.ts index dc23b5259b..2fc0b4500d 100644 --- a/packages/playground/website/src/lib/state/url/resolve-blueprint-from-url.ts +++ b/packages/playground/website/src/lib/state/url/resolve-blueprint-from-url.ts @@ -30,6 +30,29 @@ export type ResolvedBlueprint = { source: BlueprintSource; }; +const githubBlobOrRawPathPattern = /^\/([^/]+)\/([^/]+)\/(?:blob|raw)\//; + +function normalizeBlueprintUrl(remoteUrl: string): string { + try { + const parsedUrl = new URL(remoteUrl); + if (parsedUrl.hostname !== 'github.com') { + return remoteUrl; + } + const rewrittenPath = parsedUrl.pathname.replace( + githubBlobOrRawPathPattern, + '/$1/$2/' + ); + if (rewrittenPath === parsedUrl.pathname) { + return remoteUrl; + } + parsedUrl.pathname = rewrittenPath; + parsedUrl.hostname = 'raw.githubusercontent.com'; + return parsedUrl.toString(); + } catch { + return remoteUrl; + } +} + export async function resolveBlueprintFromURL( url: URL, defaultBlueprint?: string @@ -59,13 +82,12 @@ export async function resolveBlueprintFromURL( * Support passing blueprints via query parameter, e.g.: * ?blueprint-url=https://example.com/blueprint.json */ + const blueprintUrl = normalizeBlueprintUrl(query.get('blueprint-url')!); return { - blueprint: await resolveRemoteBlueprint( - query.get('blueprint-url')! - ), + blueprint: await resolveRemoteBlueprint(blueprintUrl), source: { type: 'remote-url', - url: query.get('blueprint-url')!, + url: blueprintUrl, }, }; } else if (fragment.length) {