mirror of
https://github.com/nexu-io/open-design.git
synced 2026-09-20 06:15:06 +08:00
da7c4ae051
* fix(web): rank Community "Newest" by shipped publishedAt, not local install timestamps
The Community gallery Newest toggle sorted by the installed-record
updatedAt/installedAt. Those are stamped when a machine seeds its own
database, so a fresh install (or an upgrade that re-stamps the catalog)
writes the whole bundled catalog milliseconds apart in folder-walk
order — Newest showed reverse walk order (roughly reverse-alphabetical),
unrelated to publication recency.
Root-cause fix: bundled templates now ship a real publication date.
- packages/contracts: add optional manifest field publishedAt (ISO 8601).
- apps/web sortByNewest: freshness = publishedAt when parseable, else
updatedAt (right signal for user-installed plugins); tie-breaks and
stability unchanged.
- plugins/_official: backfill publishedAt into all 460 bundled manifests
from each folder's first-appearance commit date in upstream git
history.
- e2e: repository-resource guard requiring every bundled manifest to
declare a parseable publishedAt, so the signal cannot silently erode.
- docs: field reference entries in plugins-spec (en + zh-CN).
Issue: https://plane.powerformer.net/open-design/projects/49832a02-3158-4faf-bf2f-d0e39c40c7e6/issues/ca0778af-97f5-4fc0-bc63-739280904979
Generated-By: looper 0.0.0-dev (runner=worker, agent=claude-code)
* worker: 模板用最新筛选,筛选出来的不是最新的
Generated-By: looper 0.0.0-dev (runner=worker, agent=claude-code)
* fix(web): keep shipped appeal order inside same-publishedAt batches
Review follow-up (#5597, nettee): after the freshness tie on an equal
publishedAt, the comparator still consulted local updatedAt/installedAt,
which a fresh install stamps in folder-walk order — so every
same-publication batch (largest: 322 manifests on one catalog date)
re-sorted per machine instead of keeping the shipped visual-appeal
ranking. Same-publishedAt records now preserve their incoming position;
local timestamps break ties only when at least one side has no usable
publishedAt. Adds the requested regression spec pinning
same-publishedAt / different-updatedAt records.
Generated-By: looper 0.0.0-dev (runner=worker, agent=claude-code)
* fix(web): gate publishedAt freshness to bundled records; untrack oversized looper attachment
Review follow-up (#5597, nettee round 2): publishedAt was overriding
updatedAt for every visible plugin, so a user-installed github/local/
marketplace plugin carrying an old manifest publishedAt would be buried
behind bundled templates despite a newer local install. publishedAtMs
now returns null for non-bundled records — "Newest" means install
recency for user installs, catalog publication for bundled ones. Adds
the requested regression (non-bundled, old publishedAt, newer
updatedAt) and clarifies the field docs (en + zh-CN).
CI repair: commit 578f7a9 tracked .looper-attachments/bug13_1.png
(3.8MB bug screenshot), tripping the Static gate 1MiB blob guard and
the Validate workspace aggregate. Untrack it and gitignore
.looper-attachments/ so harness attachments stay out of the repo.
Generated-By: looper 0.0.0-dev (runner=worker, agent=claude-code)
68 lines
2.6 KiB
TypeScript
68 lines
2.6 KiB
TypeScript
// The Community gallery's "Newest" sort ranks bundled templates by the
|
|
// manifest's `publishedAt` (apps/web/src/components/plugins-home/sortOrder.ts).
|
|
// That field is the only recency signal that survives a fresh install: the
|
|
// installed-record timestamps are stamped when a machine seeds its own
|
|
// database, so a first boot stamps the entire bundled catalog milliseconds
|
|
// apart in folder-walk order and "Newest" would degrade back to an order
|
|
// unrelated to publication (#1457).
|
|
//
|
|
// This guard keeps the signal from silently eroding: every bundled plugin
|
|
// manifest must declare a parseable ISO 8601 `publishedAt`. When adding a
|
|
// new template, stamp the authoring time — `date -u +%Y-%m-%dT%H:%M:%SZ` —
|
|
// next to `version` in `open-design.json`. Do not copy the date from a
|
|
// neighboring template: a wrong-but-parseable date places the template at
|
|
// the wrong spot in the Newest shelf, which this check cannot see.
|
|
|
|
import { readdir, readFile } from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
const repoRoot = fileURLToPath(new URL('../../', import.meta.url));
|
|
|
|
async function findManifests(dir: string): Promise<string[]> {
|
|
const out: string[] = [];
|
|
let entries;
|
|
try {
|
|
entries = await readdir(dir, { withFileTypes: true });
|
|
} catch {
|
|
return out;
|
|
}
|
|
for (const entry of entries) {
|
|
const full = path.join(dir, entry.name);
|
|
if (entry.isDirectory()) {
|
|
out.push(...(await findManifests(full)));
|
|
} else if (entry.name === 'open-design.json') {
|
|
out.push(full);
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
describe('bundled plugin manifest publishedAt', () => {
|
|
it('[P2] every bundled manifest declares a parseable publishedAt (keeps the Newest sort meaningful)', async () => {
|
|
const manifests = await findManifests(path.join(repoRoot, 'plugins', '_official'));
|
|
expect(manifests.length).toBeGreaterThan(0);
|
|
|
|
const violations: string[] = [];
|
|
for (const file of manifests) {
|
|
let manifest: Record<string, unknown>;
|
|
try {
|
|
manifest = JSON.parse(await readFile(file, 'utf8')) as Record<string, unknown>;
|
|
} catch (error) {
|
|
throw new Error(`Failed to parse plugin manifest: ${file}`, { cause: error });
|
|
}
|
|
const raw = manifest.publishedAt;
|
|
if (typeof raw !== 'string' || !Number.isFinite(Date.parse(raw))) {
|
|
violations.push(`${path.relative(repoRoot, file)} → publishedAt=${JSON.stringify(raw)}`);
|
|
}
|
|
}
|
|
|
|
expect(
|
|
violations,
|
|
`Bundled manifests missing a parseable publishedAt (stamp the authoring time, see docblock):\n${violations.join('\n')}`,
|
|
).toEqual([]);
|
|
});
|
|
});
|