From 564eb9dd34ac4496df430a32f089bfd6dd32f5f2 Mon Sep 17 00:00:00 2001 From: eleith Date: Sat, 7 Nov 2026 06:23:49 -0800 Subject: [PATCH 1/2] [web] share asset URL helpers --- apps/web/src/lib/pieces/assets.test.ts | 34 ------------------- apps/web/src/lib/pieces/helpers.ts | 10 ++++-- .../pieces/preview/assemble.server.test.ts | 3 +- apps/web/src/lib/pieces/preview/output-url.ts | 8 ----- apps/web/src/lib/pieces/viewer/assets.ts | 2 +- .../[...path]/preview/[jobId]/+page.svelte | 2 +- .../piece/[...path]/published/+page.svelte | 2 +- .../pieces/[piece]/[slug]/+page.server.ts | 2 +- .../web/pieces/src/asset-urls.test.ts | 34 ++++++++++++++++++- .../web/pieces/src/asset-urls.ts | 7 ++++ packages/web/pieces/src/index.ts | 1 + 11 files changed, 53 insertions(+), 52 deletions(-) delete mode 100644 apps/web/src/lib/pieces/assets.test.ts delete mode 100644 apps/web/src/lib/pieces/preview/output-url.ts rename apps/web/src/lib/pieces/preview/output-url.test.ts => packages/web/pieces/src/asset-urls.test.ts (54%) rename apps/web/src/lib/pieces/assets.ts => packages/web/pieces/src/asset-urls.ts (77%) diff --git a/apps/web/src/lib/pieces/assets.test.ts b/apps/web/src/lib/pieces/assets.test.ts deleted file mode 100644 index fe8371af..00000000 --- a/apps/web/src/lib/pieces/assets.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { expect, test } from 'vitest' -import { encodeAssetPath, assetPathToUrl } from './assets' - -test.each([ - null, - undefined, - '', - ' ', - '../x', - 'a/../x', - '.', - 'a/./x', - '/x', - '//host/x', - 'a//x', - 'https://host/x', - 'javascript:x', - 'images/mark\0.svg', - 'a\\x' -])('invalid recorded path %j remains unlinked', (path) => { - expect(encodeAssetPath(path)).toBeUndefined() - expect(assetPathToUrl(path, '/garden')).toBeUndefined() -}) - -test.each(['', '/garden/', 'https://cdn.test/prefix/?old=1#fragment'])( - 'public URL conversion still preserves base %s and encodes raw segments once', - (base) => { - const path = 'files/雪 #?%2e%2e .pdf' - const encoded = 'files/%E9%9B%AA%20%23%3F%252e%252e%20%3Cfile%3E.pdf' - expect(encodeAssetPath(path)).toBe(encoded) - const prefix = base.startsWith('https:') ? 'https://cdn.test/prefix' : base.replace(/\/$/, '') - expect(assetPathToUrl(path, base)).toBe(`${prefix}/pieces/assets/${encoded}`) - } -) diff --git a/apps/web/src/lib/pieces/helpers.ts b/apps/web/src/lib/pieces/helpers.ts index 89d9a8f4..b9170d26 100644 --- a/apps/web/src/lib/pieces/helpers.ts +++ b/apps/web/src/lib/pieces/helpers.ts @@ -1,8 +1,12 @@ -import { createPieceHelpers, OpengraphImageWidth, OpengraphImageHeight } from '@luzzle/web.pieces' +import { + createPieceHelpers, + OpengraphImageWidth, + OpengraphImageHeight, + assetPathToUrl, + previewOutputUrl +} from '@luzzle/web.pieces' import type { PublicWebPiece, PieceComponentHelpers, PieceIconPalette } from '@luzzle/web.pieces' import { page } from '$app/state' -import { assetPathToUrl } from './assets.js' -import { previewOutputUrl } from './preview/output-url.js' export { OpengraphImageWidth, OpengraphImageHeight } export type { PieceComponentHelpers, PieceIconPalette } diff --git a/apps/web/src/lib/pieces/preview/assemble.server.test.ts b/apps/web/src/lib/pieces/preview/assemble.server.test.ts index 7c939e8d..1725460c 100644 --- a/apps/web/src/lib/pieces/preview/assemble.server.test.ts +++ b/apps/web/src/lib/pieces/preview/assemble.server.test.ts @@ -1,10 +1,9 @@ import { afterEach, expect, test, vi } from 'vitest' import type { TemplateConfig as Config } from '@luzzle/web.templates/config' import type { PreviewResult } from '@luzzle/web.jobs' -import { createPieceHelpers, type PublicWebPieceAsset } from '@luzzle/web.pieces' +import { createPieceHelpers, previewOutputUrl, type PublicWebPieceAsset } from '@luzzle/web.pieces' import { makeConfig } from '#lib/server/config.fixture.js' import { assemblePreview, type PreviewWorkerResult } from './assemble.server.js' -import { previewOutputUrl } from './output-url.js' const pieceConfig: Config['pieces'][number] = { ...makeConfig({ diff --git a/apps/web/src/lib/pieces/preview/output-url.ts b/apps/web/src/lib/pieces/preview/output-url.ts deleted file mode 100644 index 8e1c21a4..00000000 --- a/apps/web/src/lib/pieces/preview/output-url.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { encodeAssetPath } from '#lib/pieces/assets.js' - -export function previewOutputUrl(path: string, job: string): string | undefined { - const encoded = encodeAssetPath(path) - const encodedJob = encodeAssetPath(job) - if (!encoded || !encodedJob || job.includes('/')) return undefined - return `/admin/preview/${encodedJob}/asset/${encoded}` -} diff --git a/apps/web/src/lib/pieces/viewer/assets.ts b/apps/web/src/lib/pieces/viewer/assets.ts index 0f5c9e53..8d02310a 100644 --- a/apps/web/src/lib/pieces/viewer/assets.ts +++ b/apps/web/src/lib/pieces/viewer/assets.ts @@ -1,5 +1,5 @@ import type { PublicWebPieceAsset } from '@luzzle/web.pieces' -import { encodeAssetPath } from '#lib/pieces/assets.js' +import { encodeAssetPath } from '@luzzle/web.pieces' import { transformFamily } from './presentation' export type OutputFile = { diff --git a/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/+page.svelte b/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/+page.svelte index 4e0d2e6a..dc077451 100644 --- a/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/+page.svelte +++ b/apps/web/src/routes/(auth)/admin/piece/[...path]/preview/[jobId]/+page.svelte @@ -1,7 +1,7 @@