Repository navigation
feat(plugins): zip package upload + refactor(web): token convergence and shared primitives - #17
Merged
Merged
Conversation
Plugins can now be uploaded as one .zip carrying manifest.json (the
manifest plus a `package` block), a single entry .mjs and optional
README/CHANGELOG/LICENSE and a PNG/WebP icon.
- providers: plugin-package validates the zip before anything is stored:
central directory parsing (no encryption/zip64/odd methods), path
safety, file whitelist and size/entry/ratio limits, manifest + package
block, entry scan, icon and doc checks. macOS Finder junk is ignored
but still counted toward limits. The scanner accepts esbuild's
`export { x as default }`.
- api: the `package` multipart field installs a zip; the legacy
manifest + file form still works and returns a
PLUGIN_UPLOAD_LEGACY_FORMAT warning. The entry is stored under the
existing key format; the zip and icon are stored alongside. New GET
routes serve the icon, the original package and the docs; the list
payload carries only `docs` flags.
- database: migration 0029 adds package columns to provider_plugins.
- worker: zip rows require the bundle manifest to equal manifest.json
(PLUGIN_MANIFEST_MISMATCH otherwise).
- web-next: the upload dialog takes one zip; installed plugins show the
icon, package metadata, file list, lazily loaded docs and a download.
Spec: MuseCanvas-Connector wiki/plugin-package-spec.md
…token guard The tokens in globals.css were already the single source of truth, but call sites kept re-spelling them: 61 `h-[var(--icon-x)] w-[var(--icon-x)]` pairs, 10 square-control pairs, 31 hand-written transition duration/easing declarations, and 8 admin tables that each re-declared the same table shell. - globals.css: point Tailwind's `--default-transition-*` at `--motion-fast` / `--ease-standard`, so a plain `transition-colors` is token-timed and cannot drift back to the 150ms default. Drop the dead `--shadow-sm/md/lg` aliases (no call site referenced them) and correct `.motion-hover-fade`'s comment: it animates opacity/transform, never colour. - ui/size: `iconSize` / `iconSlot` / `controlSquare` token->utility maps, and all 78 hand-written size pairs replaced with them. Fixes three real violations: stepper's bare `h-8 w-8`, and two `h-6 min-h-6 w-6` in the library that were fighting the button recipe's own min-height token. - ui/table: DataTable / TableFrame / TableHead(Cell) / TableBody / TableRow / TableCell / TableStateRow / TableSkeletonRow, with a `dense` variant for the plugin file manifest. All 8 admin tables migrated; colSpan now derives from one declared `columns` per frame instead of a per-view constant. - ui/section-header: the repeated admin section heading, 4 call sites. - lib/format: one date module, absorbing three private formatDate / formatDateTime copies across account, library and lightbox. - library: split the URL-sync effect so a filter change can no longer write the previous query's page depth, and a restored `?page=N` link survives its walk. - scripts/check-web-tokens.mjs (+ web-checks.yml): fail on undefined tokens, raw colours, off-scale motion and hand-written size pairs. Advisory-only report of tokens with no consumer.
Owner
Author
|
Release acceptance completed before merge: f28798e immutable images deployed; migration history 31 / 0031, Canvas Agent remains disabled. All PR checks and exact-head image publication passed. Isolated real PostgreSQL/production API: 46 checks and core data/config preservation passed. Production proxy boundary: 16 checks; real public homepage/login browser matrix: 8 cases + 4 auth guards, no runtime exceptions/overflow. Protected backups and old application images/source retained. No paid generation, production login, SMTP/S3 writes or user deletion used as smoke tests. Merged without protection bypass; deployment remains digest-pinned, independent of main latest publication. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Plugins can now be uploaded as one zip package instead of a pasted manifest plus a separate
.mjs. The zip carriesmanifest.json(manifest +packageblock), a single entry.mjs, and optional README / CHANGELOG / LICENSE and a PNG/WebP icon.Spec: MuseCanvas-Connector
wiki/plugin-package-spec.md. Author template and pack/check tool: same branch in MuseCanvas-Connector.Changes
providers: new
plugin-package.tsvalidates the zip before anything is stored:central directory parsing; encryption, zip64 and unsupported compression methods are rejected
path safety
file whitelist and size / entry-count / compression-ratio limits
manifest +
packageblock, entry source scan, icon and doc checksmacOS Finder junk (
__MACOSX/,.DS_Store) is ignored but still counted toward limits. The scanner now accepts esbuild'sexport { x as default }.api:
packagemultipart field installs a zip. The legacymanifest+fileupload still works and returns aPLUGIN_UPLOAD_LEGACY_FORMATwarning.The entry is stored under the existing key format, so the worker's fetch path is unchanged. The zip and icon are stored alongside.
New GET routes for the icon, the original package and the docs. The list payload carries only
docsflags.database: migration
0029_provider_plugin_packages.sqladds package columns toprovider_plugins.worker: zip rows require the bundle's manifest to equal
manifest.json(PLUGIN_MANIFEST_MISMATCHotherwise). Legacy rows keep the identity check.web-next:
The upload dialog takes one zip.
Installed plugins show the icon, package metadata, file list, lazily loaded docs (plain text) and a package download.
dependency:
fflateadded to@musecanvas/providers.Verification
pnpm -r --if-present typecheck: pass.Tests, all passing:
| Package | Passing |
|---|---|
| contracts | 41 |
| providers | 208 |
| database | 27 |
| api | 160 |
| worker | 64 |
| integration | 25 |
Integration includes a mocked pack → install → load → submit round trip, plus a manifest-drift rejection.
web-next typecheck was also run against the committed tree alone, without other uncommitted work in the shared worktree.
Not done: no browser or deployed smoke test of the upload UI yet.
Deployment notes
Migration 0029 must run on deploy.
Plugin upload stays behind
ALLOW_PLUGIN_UPLOAD=true.Second commit —
refactor(web): design-token convergence and shared primitivesIndependent of the plugin feature;
apps/web-nextonly. It adds no dependency, changes no APIcontract, touches no schema and adds no migration.
What changed
globals.css— Tailwind's--default-transition-duration/--default-transition-timing-functionnow point at
--motion-fast/--ease-standard, so a plaintransition-colorsis token-timed andcannot drift back to the 150ms default. Removed the dead
--shadow-sm/md/lgaliases and corrected.motion-hover-fade's comment (it animates opacity/transform, never colour). 31 hand-writtentransition declarations collapsed into
transition-colors.ui/size.ts(new) —iconSize/iconSlot/controlSquaremaps, replacing 78 hand-writtenh-[var(--icon-x)] w-[var(--icon-x)]-style pairs. Fixes three real violations:stepper's bareh-8 w-8, and twoh-6 min-h-6 w-6in the library that were fighting the button recipe's ownmin-heighttoken.ui/table.tsx(new) —DataTable/TableFrame/TableHead(Cell)/TableBody/TableRow/TableCell/TableStateRow/TableSkeletonRow, with adensevariant for the plugin filemanifest. All 8 hand-written admin tables migrated;
colSpannow derives from one declaredcolumnsper frame instead of a per-view constant. Nothing outsideui/table.tsxrenders<table>.ui/section-header.tsx(new) — the repeated admin section heading, 4 call sites.lib/format.ts(new) — one date module, absorbing three privateformatDate/formatDateTimecopies in account, library and lightbox.
library — split the URL-sync effect so a filter change can no longer write the previous query's
page depth, while a restored
?page=Nlink still survives its forward walk.scripts/check-web-tokens.mjs+.github/workflows/web-checks.yml(new) — fail on undefinedtokens, raw colours, off-scale motion and hand-written size pairs; advisory-only report of tokens with
no consumer. The guard is a regression gate, not a bug-fix: token references were already clean.
Verification (this commit)
tsc --noEmit,next buildandnode scripts/check-web-tokens.mjs: all exit 0. Compiled-CSSassertions:
transition-duration:var(--motion-fast)present, Tailwind's150msdefault gone, all 10size-[var(--token)]rules emit bothwidthandheight, no--shadow-sm/md/lg.The guard was proven non-vacuous by injecting one violation per rule into a scratch copy → exit 1 with
exact file:line, then removed.
Table migration equivalence: a per-table audit of captions,
th scope,aria-sort,aria-busy,state-row variants and
colSpan, plus an SSR render of the primitives throughreact-dom/server(the rendered class sets match the pre-migration markup).
Deployed for review on 10.7.89.132 (frontend-only image swap)
ghcr.io/nextroad-dev/musecanvas-web-next:sha-66a7b5b@sha256:6a81e22ce9686f8bd7f81aa577d930cd5430a24ae4579d81c1a46fe83391b4fdDeployed with
pull web-next+up -d --no-deps web-next, so onlymusecanvas-web-next-1wasrecreated; the before/after
docker psdiff is exactly that one line. The other 7 containers kepttheir previous start time, which is also the proof that
db-migratenever ran.--no-depsmatters here:web-nextdeclaresdepends_on: api, and without it Compose resolves thewhole stack and would run this branch's migration against production.
Verified:
/api/health/ready200setupComplete:true(same as the pre-deploy baseline) · 8 adminroutes 307 →
/login?from=/admin, no 500 · served chunk and served CSS contain revision-uniquemarkers that are absent from the previous image.
Not verified: every admin screen behind login — the migrated tables have not been rendered
against real data (no credentials here), nor has the interactive library search.
tests/e2e/admin-panel.mjswas deliberately not run: it POSTs 5 invitation codes into theproduction database.
Current production state while this PR is open:
web-next= this branch'ssha-66a7b5b, whileapi/worker/nginxremain onmain'slatest. Migration 0029 is therefore still unapplied —it will run when the backend is next deployed.
Additional full-product release (9ab8464)
Replace Lucide with pinned Phosphor 2.1.10, explicit weights and CSR/SSR-safe imports.
Integrate admin detail/confirmation flows and generation workbench state/cache improvements.
Add owner-scoped Canvas CRUD and confirmation-gated Canvas Agent backend; additive migrations 0030/0031, agent disabled by default.
Preserve standardized image response-body timeout/safety diagnostics; regression tests reproduced failures before the fix.
Verified: root typecheck, 679 tests passed / 2 skipped; provider targeted 57 passed; admin 45 and generation integration 8 passed; frontend production build and 56 fixture UI cases passed.
Icon-only comparable gzip increase accepted by owner; actual network transfer/performance is not implied.
Dev image publication and backed-up deployment validation are in progress. Do not merge until CI and deployment acceptance succeed.
Release validation update
f28798e(CI prerequisite ordering fix only; product sources unchanged from9ab8464).