mirror of
https://github.com/nexu-io/open-design.git
synced 2026-08-03 06:05:05 +08:00
* fix(packaging): serve baked plugin previews in the packaged app The packaged gallery fell back to live, GPU-expensive iframes instead of the baked hover-pan clips, because of two gaps that only bite the packaged layout: 1. The baked-preview manifest (data/plugin-previews/manifest.json) was never bundled — it's absent from the packaged resource trees. The clips live on R2, but without the manifest the daemon has no plugin -> clip mapping. 2. Even bundled, the daemon resolved the manifest dir from PROJECT_ROOT. For the prebundled sidecar (Resources/app/prebundled/daemon) PROJECT_ROOT resolves to Resources/app — two levels up — which has no data/. The bundled resources actually live under OD_RESOURCE_ROOT (Resources/open-design), where every other tree (skills, plugins, design-systems…) is resolved from. Fix both: add data/plugin-previews to BUNDLED_RESOURCE_TREES, and resolve PLUGIN_PREVIEWS_DIR against DAEMON_RESOURCE_ROOT like the other resource dirs (explicit OD_PLUGIN_PREVIEWS_DIR override + the dev PROJECT_ROOT layout still win). Verified end-to-end: a freshly built+installed mac app's packaged daemon now returns bakedPreview blocks for 126 plugins (R2 URLs) instead of none. * test(tools-pack): assert the plugin-preview manifest is bundled * test(daemon): cover packaged plugin-previews dir resolution from OD_RESOURCE_ROOT Address review: extract resolveDaemonPluginPreviewsDir and prove it resolves the bundled manifest under the resource root in the packaged layout (and falls back to PROJECT_ROOT in dev). Also seed data/plugin-previews in the win-resources and mac packager fixtures so copyBundledResourceTrees' new tree doesn't ENOENT. * fix(daemon): resolve OD_PLUGIN_PREVIEWS_DIR override from the injected env Address review: the override branch delegated to resolvePluginPreviewsDir, which re-reads process.env — so an injected env was ignored and the override path was untestable. Resolve it directly from env.OD_PLUGIN_PREVIEWS_DIR (absolute passthrough, relative against projectRoot) and add an override test. * fix(tools-pack): include plugin-previews in the Windows resource-tree cache key Address review: adding data/plugin-previews to the bundled tree didn't feed the Windows resource-tree cache key, so a builder with an existing cache entry would reuse the old bundle without manifest.json. Hash data/plugin-previews into the key, bump the cache schema version, and add a regression proving a manifest.json change forces a cache miss. --------- Co-authored-by: audit <a@b.c>
141 lines
5.1 KiB
TypeScript
141 lines
5.1 KiB
TypeScript
import path from 'node:path';
|
|
import { describe, expect, it } from 'vitest';
|
|
import {
|
|
resolveDaemonCliPath,
|
|
resolveDaemonPluginPreviewsDir,
|
|
resolveDaemonResourceRoot,
|
|
resolveProjectRoot,
|
|
} from '../src/server.js';
|
|
|
|
describe('resolveProjectRoot', () => {
|
|
it('resolves the repository root from the source daemon directory', () => {
|
|
const root = path.resolve(import.meta.dirname, '../../..');
|
|
|
|
expect(resolveProjectRoot(path.join(root, 'apps', 'daemon'))).toBe(root);
|
|
});
|
|
|
|
it('resolves the repository root from the live TypeScript source directory', () => {
|
|
const root = path.resolve(import.meta.dirname, '../../..');
|
|
|
|
expect(resolveProjectRoot(path.join(root, 'apps', 'daemon', 'src'))).toBe(root);
|
|
});
|
|
|
|
it('resolves the repository root from the compiled daemon dist directory', () => {
|
|
const root = path.resolve(import.meta.dirname, '../../..');
|
|
|
|
expect(resolveProjectRoot(path.join(root, 'apps', 'daemon', 'dist'))).toBe(root);
|
|
});
|
|
|
|
it('resolves the repository root from the daemon src directory (tsx entry)', () => {
|
|
const root = path.resolve(import.meta.dirname, '../../..');
|
|
|
|
expect(resolveProjectRoot(path.join(root, 'apps', 'daemon', 'src'))).toBe(root);
|
|
});
|
|
});
|
|
|
|
describe('resolveDaemonCliPath', () => {
|
|
it('resolves the od CLI from the daemon package root', () => {
|
|
const packageRoot = path.resolve(import.meta.dirname, '..');
|
|
|
|
expect(resolveDaemonCliPath()).toBe(path.join(packageRoot, 'dist', 'cli.js'));
|
|
});
|
|
|
|
it('uses the packaged daemon CLI path override before package resolution', () => {
|
|
expect(resolveDaemonCliPath({ OD_DAEMON_CLI_PATH: '/app/prebundled/daemon-cli.mjs' })).toBe(
|
|
'/app/prebundled/daemon-cli.mjs',
|
|
);
|
|
});
|
|
|
|
it('uses OD_BIN as a fallback override for bundled wrapper invocations', () => {
|
|
expect(resolveDaemonCliPath({ OD_BIN: '/app/prebundled/daemon-cli.mjs' })).toBe(
|
|
'/app/prebundled/daemon-cli.mjs',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('resolveDaemonResourceRoot', () => {
|
|
it('allows resource roots under an explicit safe base', () => {
|
|
const safeBase = path.resolve(import.meta.dirname, '..', 'fixtures', 'resources');
|
|
const configured = path.join(safeBase, 'packaged');
|
|
|
|
expect(resolveDaemonResourceRoot({ configured, safeBases: [safeBase] })).toBe(configured);
|
|
});
|
|
|
|
it('allows a resource root equal to an explicit safe base', () => {
|
|
const safeBase = path.resolve(import.meta.dirname, '..', 'fixtures', 'resources');
|
|
|
|
expect(resolveDaemonResourceRoot({ configured: safeBase, safeBases: [safeBase] })).toBe(safeBase);
|
|
});
|
|
|
|
it('allows packaged launcher payload resources under the installation root', () => {
|
|
const installationRoot = path.resolve(import.meta.dirname, '..', 'fixtures', 'installation');
|
|
const configured = path.join(
|
|
installationRoot,
|
|
'launcher',
|
|
'channels',
|
|
'beta',
|
|
'namespaces',
|
|
'release-beta',
|
|
'versions',
|
|
'0.10.0-beta.15',
|
|
'payload',
|
|
'Open Design Beta.app',
|
|
'Contents',
|
|
'Resources',
|
|
'open-design',
|
|
);
|
|
|
|
expect(resolveDaemonResourceRoot({ configured, safeBases: [installationRoot] })).toBe(configured);
|
|
});
|
|
|
|
it('rejects resource roots outside the safe bases', () => {
|
|
const safeBase = path.resolve(import.meta.dirname, '..', 'fixtures', 'resources');
|
|
const configured = path.resolve(import.meta.dirname, '..', 'fixtures-other', 'resources');
|
|
|
|
expect(() => resolveDaemonResourceRoot({ configured, safeBases: [safeBase] })).toThrow(
|
|
/OD_RESOURCE_ROOT must be under/,
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('resolveDaemonPluginPreviewsDir', () => {
|
|
it('resolves under the resource root in the packaged layout', () => {
|
|
// Packaged: the prebundled daemon's PROJECT_ROOT is Resources/app (no data/),
|
|
// but the bundled manifest lives under OD_RESOURCE_ROOT (Resources/open-design).
|
|
const resourceRoot = '/Applications/Open Design.app/Contents/Resources/open-design';
|
|
const projectRoot = '/Applications/Open Design.app/Contents/Resources/app';
|
|
|
|
expect(
|
|
resolveDaemonPluginPreviewsDir({ env: {}, resourceRoot, projectRoot }),
|
|
).toBe(path.join(resourceRoot, 'data', 'plugin-previews'));
|
|
});
|
|
|
|
it('falls back to the project root in the dev layout (no resource root)', () => {
|
|
const projectRoot = path.resolve(import.meta.dirname, '../../..');
|
|
|
|
expect(
|
|
resolveDaemonPluginPreviewsDir({ env: {}, resourceRoot: undefined, projectRoot }),
|
|
).toBe(path.join(projectRoot, 'data', 'plugin-previews'));
|
|
});
|
|
|
|
it('honors an OD_PLUGIN_PREVIEWS_DIR override from the injected env', () => {
|
|
const projectRoot = '/repo';
|
|
|
|
// Absolute override passes through; a relative one resolves against projectRoot.
|
|
expect(
|
|
resolveDaemonPluginPreviewsDir({
|
|
env: { OD_PLUGIN_PREVIEWS_DIR: '/abs/previews' },
|
|
resourceRoot: '/res/open-design',
|
|
projectRoot,
|
|
}),
|
|
).toBe('/abs/previews');
|
|
expect(
|
|
resolveDaemonPluginPreviewsDir({
|
|
env: { OD_PLUGIN_PREVIEWS_DIR: 'rel/previews' },
|
|
resourceRoot: '/res/open-design',
|
|
projectRoot,
|
|
}),
|
|
).toBe(path.join(projectRoot, 'rel', 'previews'));
|
|
});
|
|
});
|