tce_packagestce_packagesEvery published version is one row of tce_packages (App\Models\TcePackage): namespace,
name, kind (element | library | sdk), description, major/minor/patch,
width/height, extra. Its code zip and icon are attachments (relation: code /
relation: icon), reusing the site's polymorphic attachment mechanism unchanged. Declared
dependencies are rows of tce_package_dependencies, pointing at the dependency's row, with
optional required_major/minor/patch constraints.
The pre-v3 tobidot_elements table is read-only reference material for porting. Nothing in v3
— the registry, the generator, the loader, the routes — reads from it, and there is no
migration; a legacy element appears in v3 only when it is deliberately ported and uploaded.
| Kind | Zip contents | Produced by |
|---|---|---|
element, library |
dist/index.js, dist/index.global.js, copied public/ files |
npm run pack (release/<name>-<version>.zip), or publish.py in the v3 workspace |
sdk |
package.json + src/**/*.ts |
publish.py |
One top-level folder in the zip is flattened on extraction, so zipping dist/ as a folder or
zipping its contents both work.
| Path | Who | Declares dependencies? | One request? |
|---|---|---|---|
POST /api/v1/tce-packages |
script / CI | yes | yes |
MCP tool upload_tce_package |
an agent, publish.py, tce:import-v3-packages |
yes | yes |
| Nova admin | a human | by hand | no — row, then two attachments |
All three hand their input to one service (TcePackageService::upload), so the rules below hold
for every path.
POST /api/v1/tce-packages (auth:sanctum, multipart/form-data)
| Field | Required | Notes |
|---|---|---|
namespace |
yes | The publisher (Naming). |
name |
yes | The package name; elements and libraries must match the tag-name pattern. |
zip |
yes | See What Gets Uploaded. Large projects: Assets & Build. |
version |
no | major.minor.patch. Omitted: the previous patch + 1. |
kind |
no | element | library | sdk. Omitted: the previous kind, else element. |
description |
no | Omitted: carried over. |
icon |
no | SVG or PNG. Omitted: carried over. |
width, height |
no | Default window size. Omitted: carried over. |
dependencies[i][namespace], dependencies[i][name] |
no | Declared dependencies. Omitted: carried over; an empty list clears. |
The response is the published version as the registry lists it. A README generated by the
generator contains the exact curl call for its project.
The site's MCP server exposes upload_tce_package with the same fields (zip_base64,
icon_base64 + icon_file_name instead of files) and list_tce_packages to inspect the
registry. The v3 workspace's publish.py uses it, reading everything from each package's
manifest; php artisan tce:import-v3-packages does the same from a staging folder
inside the app container.
The Nova TcePackage resource creates the row by hand (namespace, name, version, kind,
description, size, dependencies), then the Upload Attachment action attaches the code zip
(relation: code) and the icon (relation: icon) in two steps. Nothing auto-increments the
version here.
Version resolution is keyed on (namespace, name):
version given, no prior upload for the pair → 0.0.1.version given, prior upload exists → same major.minor, patch + 1.version given → must be strictly greater than the pair's latest; the same or an older number
is rejected. A bad release is fixed by a new patch, never by re-uploading its number.Dependencies are declared by identifier and resolved to the dependency's latest uploaded version at upload time. A dependency that was never uploaded rejects the whole upload, naming every missing identifier — so packages are published dependencies first, and a published version can never be missing part of its dependency list. The declared list is what the embed pages and the package manager use to include libraries before the packages that need them.
GET /api/public/tce-packages (no auth)
Every element and library version that has a code attachment, grouped by namespace/name
(two publishers using the same name never share a group). SDK packages are build-time only and
are not listed.
{
"version": 1,
"packages": {
"tobidot/tce-notepad": [
{
"namespace": "tobidot", "name": "tce-notepad", "kind": "element",
"description": "A simple text editor for .txt and .md files.",
"major": 0, "minor": 2, "patch": 0,
"root": "https://…/storage/tce-packages/<uuid>",
"content": "https://…/storage/tce-packages/<uuid>/index.js",
"icon": "https://…/storage/media/<uuid>/icon.svg",
"width": 480, "height": 360,
"extra": [], "created_at": "2026-10-17T18:45:35.000000Z",
"dependencies": [{ "identifier": { "namespace": "tobidot", "name": "dll-tce-file-system" } }]
}
]
}
}
A version entry is TcePackageResource; the client-side schema is registry_response_schema
in @tce-v3/common, and every consumer reads the endpoint through
fetch_json(url, registry_response_schema). The two change together.
| Route | Serves |
|---|---|
GET /tce-elements/{namespace}/{name} |
the latest version's IIFE, prefixed with its content root (Integration) |
GET /tce-elements/{namespace}/{name}@{x}.{y}.{z} |
exactly that version, immutable |
GET /tce-elements/{name} |
the same, in the site's default namespace |
GET /tce/{name} |
fullscreen page of the latest element of that name, default namespace |
GET /tce-packages/{id}/view |
fullscreen page of one specific upload |
GET /, GET /v3 |
the desktop (with / without the site header) |
GET /api/public/tce-packages |
the registry response above |
GET /documentation/{path} |
this documentation as raw markdown, for tce-documentation |
The embed route answers 404 for a version without an IIFE build and for SDK packages.
publish.py / the manifest in the workspace and npm run pack + REST for a
standalone project; never zip by hand what the tooling zips./tce/{name}, not an {id}, unless you want to pin a version.TcePackageResource and registry_response_schema together.tobidot_elements from anything in v3.