This is the current-line (0.41.x stable and later) five-package product truth. ADR-0113 authorizes the breaking collapse from the earlier implementation graph.
OpenElement = Web Components-native, static-first application framework
authoring modes = Basic Element standalone + full application
| Package | Responsibility | Supported public interface |
|---|---|---|
@openelement/element |
JSX, Custom Elements, DSD, hydration, signals and component runtime contracts | root, jsx-runtime, jsx-dev-runtime, build-utils, sanitize |
@openelement/app |
Pages, routes, loaders, actions, islands and normalized request semantics | root, model, spa, preact |
@openelement/adapter-vite |
Vite, content, SSG, generated data, Hono and Nitro build/deploy implementation | root, nitro-mount, cli/build, cli/start, sitemap |
@openelement/create |
Version-coherent starter generation and consumer lifecycle | CLI binary (root) |
@openelement/ui |
Optional, reusable and dogfood-proven Web Component primitives | root and retained primitive subpaths |
Responsibility wording follows STACK_CONTRACT.md,
the source of truth for the five-package responsibility table.
Application authors should normally learn element, app, adapter-vite,
and create; ui is optional.
- Hydration — element-level: how and when a component's client JavaScript
is loaded (
load,idle,visible,only; seeHYDRATION_CONTRACT.md). - Upgrade — island-level: the moment a server-rendered custom element is defined and its instance takes over the existing markup.
- Activation — framework takeover: marker activation, event binding and state restoration performed by the runtime after upgrade.
The Element/App root surface exposes the compiled-class authoring model: the
OpenElement base class plus the @element/@property compile-time
decorator intrinsics (experimental, #1209 — see the v0.44 experimental table
below). The v0.43 functional element-authoring helper, its alpha-only layout
alias, and the signal control-flow list factory were removed in v0.44 with no
alias; the before/after mapping lives in
v0.44.0-MIGRATION.md. Show stays internal
(jsx-runtime) — open an issue if a public consumer surface is needed. Keyed
list reconciliation is compiler-owned in v0.44: the compiler emits list
Regions for keyed collections, with the v1 grammar boundaries recorded in the
migration guide (ADR-0124 Consequences, #915).
The machine-readable map below is compared against each package's exports
field by deno task package-surface:check; any drift fails the gate. A second
machine-readable block (package-export-classes) in the Beta.1 stability
section below classifies every named export of every subpath; the same gate
compares it against source.
"Internal but importable" subpaths stay reachable for build adapters,
generated code and optional integrations, but they carry no compatibility
promise and are not application-authoring surface.
@openelement/element/build-utils(alpha.17): build-time helpers (transformIslandSource,formatJson,pathToTagName,normalizeSeparators,insertBeforeBodyClose,normalizeRoutePatternForURLPattern,SsrRenderError,createRuntimeAdapter,composeFetchMiddlewareand the runtime handler types) for build adapters. They were removed from the element root export; application code must not import them.- The
open-element-renderandopen-element-hydrationmodules are internal-only hydration implementation modules (seeHYDRATION_CONTRACT.md); their subpath exports were removed in alpha.19. The module files remain inside the package for internal relative imports only. - The branded types
SafeHtmlandUnsafeHtmland the internalStyleSheetRuletype are no longer exported from the element root (alpha.18 release notes already claimed their removal; alpha.19 makes it true). Their declarations stay in the internal protocol files. - The element root no longer carries
export type *seams (alpha.19); the public type surface is an explicit export list inpackages/element/src/index.ts. - v0.43.1 ownership correction (ADR-0137, #1097): adapter-specific blog,
navigation, i18n and build-context contracts are exported by
@openelement/adapter-vite, not@openelement/element. Migrate imports ofOpenElementBlogOptions,OpenElementNavSection,OpenElementHeaderNavLink,OpenElementI18nContextOptions, andOpenElementBuildContextLiketo the adapter root. Package roots route supported implementations through named public modules and never name aninternal/module specifier. @openelement/app/i18nis the optional locale-expansion integration point.- App's router implementation (
internal/router) is not exported; the router types (RouteConfig,RouterInstance,RouterMode) were removed from the app root export in alpha.17 — SPA consumers derive the instance fromReturnType<typeof defineApp>and the options fromParameters<typeof defineApp>[0]. @openelement/adapter-viteinternal subpaths (app-vite,build-context,head-injection,i18n-plugin,plugin,generated-data-resolver,plugin-mdx,route-manifest,cli/build-client,cli/build-ssg) were pruned at the 0.41.0 freeze (ADR-0119): they had zero consumer specifiers — the build pipeline and generated code import only relatively or through the supported root,nitro-mount,cli/build,cli/startandsitemapsubpaths.cli/startcarries a--mode=start|previewflag (alpha.13, ADR-0123 item 4): the former standalonecli/previewsubpath merged into it as preview mode. The module files remain inside the package for internal relative imports only.- The adapter root owns the content-collection authoring surface:
CollectionOptions,CollectionSchema,createCollectionPlugin,loadCollectionData, andwriteCollectionDataModule. Normal applications configure named collections throughopenElement({ content: { collections } });content.blogremains the compatible blog alias and emits the unchanged_generated-blog-data.tsmodule contract. @openelement/uisupported subpaths:open-badge,open-button,open-callout,open-card,open-code-block,open-dialog,open-dropdown,open-input,open-props-tokens,open-tabs,open-theme-toggle.@openelement/ui/open-props-tokens.jsis a resolver-compatibility alias ofopen-props-tokens.
Every named export of every published subpath carries exactly one stability
class. The machine-readable package-export-classes block below is compared
against the real export surface by deno task package-surface:check, which
enumerates exports through the same TypeScript module resolution as the
public-interface snapshot; an unclassified export, a stale classification or a
classified name missing from the prose fails the gate.
- stable-candidate — supported application-authoring, third-party component or deployment-integration contract intended to reach 1.0 unchanged. Entries that are also frozen (ADR-0119, ADR-0122) still require a major-version ADR to change.
- experimental — admitted but explicitly unsettled; may change or move without a major-version bump while its tracking issue is open.
- internal-importable — reachable at a supported subpath because generated code, build adapters or sibling OpenElement packages import it; carries no application-authoring compatibility promise.
- compatibility-only — retained solely for backward compatibility; new code must not adopt it.
- deprecated — scheduled for removal. No export currently carries this class.
| Subpath | Class | Exports |
|---|---|---|
| root | compatibility-only | OpenElementRouteKind, OpenElementRouteNode |
| experimental | DANGEROUS_KEYS, element, injectPropsSafe, isDangerousKey, property |
|
| internal-importable | AppShellConfig, assertValidTagName, collectPublicProps, CompatibilityClassification, CompatibilityTier, ComponentLayer, createLogger, deepGetElementById, ensureDeepFragmentNavigation, ensurePreHydrationClickCapture, formatError, FrameworkOptions, isValidTagName, renderDsd, RenderDsdOptions, RenderOutput, RouteEntry, SpecialFileType, SsrAdmissionDecision, wrapInDocument |
|
| stable-candidate | Action, ACTION_FETCH_HEADER, ActionContext, ActionResult, computed, consumeContext, Context, createContext, effect, ERROR_PREFIX, ErrorBoundary, ErrorTelemetryHook, escapeAttr, escapeHtml, HYDRATION_STRATEGIES, HydrationStrategy, IslandOptions, isSafeAttributeName, Loader, LoaderContext, LocalePath, Middleware, OpenElement, OpenElementAttribute, OpenElementCssPart, OpenElementDeclaration, OpenElementError, OpenElementEvent, OpenElementPackageManifest, OpenElementSlot, PROBLEM_JSON_MEDIA_TYPE, ProblemDetails, provideContext, RenderError, reportError, ServerRouteContext, ServerRouteMetadata, setErrorTelemetryHook, signal, Signal, SpaAction, SpaActionContext, SpaLoader, SpaLoaderContext, StyleSheet, StyleSheetLike, trustedHtml, TrustedHtml |
|
jsx-runtime |
stable-candidate | Fragment, jsx, JSX, jsxs |
jsx-dev-runtime |
stable-candidate | Fragment, JSX, jsxDEV |
sanitize |
stable-candidate | isSafeUrl, sanitizeHtml, SanitizeOptions |
build-utils |
internal-importable | composeFetchMiddleware, createRuntimeAdapter, formatJson, insertBeforeBodyClose, normalizeRoutePatternForURLPattern, normalizeSeparators, OpenElementRequestHandler, pathToTagName, RuntimeContext, SsrRenderError, transformIslandSource |
| Subpath | Class | Exports |
|---|---|---|
| root | internal-importable | ActionOutcome, classifyActionResult, projectPageProps |
| stable-candidate | Action, ACTION_FETCH_HEADER, ActionContext, ActionResult, createRequestContext, CreateRequestContextOptions, defineApp, defineIslandConfig, definePage, fail, isActionFailure, IslandConfig, IslandDeliveryStrategy, isOpenElementNotFound, isOpenElementRedirect, Loader, LoaderContext, notFound, OpenElementActionFailure, OpenElementNotFound, OpenElementPageDescriptor, OpenElementRedirect, OpenElementRequestContext, PageComponentConstructor, PageErrorProjector, PagePropsContext, PagePropsProjector, PROBLEM_JSON_MEDIA_TYPE, ProblemDetails, redirect, ServerRouteContext, ServerRouteMetadata, SpaAction, SpaActionContext, SpaAppInstance, SpaLoader, SpaLoaderContext |
|
model |
stable-candidate | createRequestContext, CreateRequestContextOptions, OpenElementRequestContext |
spa |
stable-candidate | defineApp, SpaAppInstance |
i18n |
internal-importable | loadI18nData, LocalePath, normalizeLocalePath, OpenElementI18nOptions |
preact |
stable-candidate | definePreactIsland, PreactIslandConstructor, PreactIslandOptions |
| Subpath | Class | Exports |
|---|---|---|
| root | internal-importable | ArtifactInfo, buildIslandChunkMap, BuildManifest, buildSpeculationRulesJson, extractCustomElementTags, FrameworkOptions, generateIslandManifests, injectClientScript, injectCspMeta, injectSpeculationRules, injectViewTransitionMeta, OpenElementBuildContext, printBuildManifest, scanClientBuild, scanSSGOutput, SpeculationRulesOptions, SsgBehaviorOptions, writeIslandManifests |
| stable-candidate | buildApp, buildHeadExtras, CollectionEntry, CollectionFieldDefinition, CollectionFieldType, CollectionOptions, CollectionSchema, CollectionSchemaContext, CollectionSchemaResult, createCollectionPlugin, default, generateSitemap, HeadExtrasResult, loadCollectionData, mdxPlugin, openElement, OpenElementBlogOptions, OpenElementBuildContextLike, OpenElementHeaderNavLink, OpenElementI18nContextOptions, OpenElementNavSection, OpenElementOptions, OpenMdxPluginOptions, openPipeline, OpenPipelineConfig, writeCollectionDataModule |
|
nitro-mount |
stable-candidate | createOpenElementNitroHandler, NitroRequestEvent, OpenElementNitroMountOptions |
sitemap |
stable-candidate | generateSitemap, SitemapOptions |
cli/build |
— | CLI entry module; no importable exports |
cli/start |
internal-importable | extractServeMode |
| Subpath | Class | Exports |
|---|---|---|
| root | — | CLI entry module; no importable exports |
| Subpath | Class | Exports |
|---|---|---|
| root | stable-candidate | manifest, OpenBadge, OpenButton, OpenCallout, OpenCard, OpenCodeBlock, OpenDialog, OpenDropdown, OpenInput, openPropsRootSheet, openPropsTokenSheet, OpenTabs, OpenThemeToggle, registerOpenUi |
open-badge |
stable-candidate | OpenBadge |
open-button |
stable-candidate | OpenButton |
open-callout |
stable-candidate | OpenCallout |
open-card |
stable-candidate | OpenCard |
open-code-block |
stable-candidate | OpenCodeBlock |
open-dialog |
stable-candidate | OpenDialog |
open-dropdown |
stable-candidate | OpenDropdown |
open-input |
stable-candidate | OpenInput |
open-tabs |
stable-candidate | OpenTabs |
open-theme-toggle |
stable-candidate | OpenThemeToggle |
open-props-tokens |
stable-candidate | openPropsRootSheet, openPropsTokenSheet |
open-props-tokens.js |
compatibility-only | openPropsRootSheet, openPropsTokenSheet |
The adapter root default export is the openPipeline alias. cli/build, |
||
cli/start and the create root are CLI entry modules: cli/start exports |
||
extractServeMode for CLI tests only, and the other two export nothing |
||
importable. @openelement/app/i18n stays the optional locale-expansion |
||
| integration point with no compatibility promise. |
Where physical source lives in a different package than the canonical semantic
owner (registry of record:
SEMANTIC_OWNERSHIP.md), the tables above classify
the export at its physical location. The split entries, each with its
compatibility reason and removal condition (migration is deliberately deferred
because the physical declarations sit on ADR-0122 frozen paths):
| Export group | Semantic owner | Physical source | Compatibility reason | Removal condition |
|---|---|---|---|---|
Action/loader protocol: Action, ActionContext, ActionResult, Loader, LoaderContext, ProblemDetails, ServerRouteContext, ServerRouteMetadata, SpaAction, SpaActionContext, SpaLoader, SpaLoaderContext, ACTION_FETCH_HEADER, PROBLEM_JSON_MEDIA_TYPE |
App (loader/action/outcome authoring semantics, #1206) | @openelement/element root; declarations in element internal/protocol/data.ts |
The retired protocol package collapse placed the declarations in element; the 0.42 freeze (ADR-0122) froze both the element-root re-export and the file. The app root re-exports the same names so application authors import from the semantic owner | Physical move to App requires a major-version ADR amending ADR-0122 |
Build/framework contract types: FrameworkOptions, RouteEntry, AppShellConfig, CompatibilityClassification, CompatibilityTier, ComponentLayer, SpecialFileType |
Adapter Vite (build and deploy contracts) | @openelement/element root; declarations in element internal/protocol/framework.ts |
Same protocol collapse; consumed only by the adapter, generated code and sibling packages, hence classified internal-importable | Physical move to the adapter requires a major-version ADR amending ADR-0122 |
Route-tree metadata: OpenElementRouteKind, OpenElementRouteNode |
App route model | @openelement/element root; declarations in element internal/protocol/app-model.ts |
Frozen protocol metadata retained on the element root with zero in-repo consumers, hence compatibility-only | Removable at the next major version under the ADR-0122 amendment gate |
Frozen at 0.41.0: the element authoring helper, definePage, buildApp, the five-package
graph, the supported subpaths above, and the static/SPA semantics of
defineApp as shipped — file routes, tagName route elements, island
configuration (ssr/dsd/hydrate), DSD output, and the SPA-mode
loader/action chain.
The 0.41 freeze did not cover request-time data, forms, sessions, cache
semantics, the @openelement/ui stable scope, or anything marked internal in
the map above. Request-time data and form semantics were later frozen by
ADR-0122; framework session/cache capabilities remain unassigned. Any
post-freeze change to a frozen surface requires a major-version ADR.
Frozen-surface change on the alpha line (ADR-0127, #920): element's
IslandOptions.strategy was renamed to hydrate in 0.42.0-alpha.16,
matching the app-side defineIslandConfig({ hydrate }) name; the old name
was deleted with no alias. Migration: rename the option at each
island-definition call site (strategy → hydrate).
Frozen wording narrowed on the alpha line (ADR-0128, #960): "tagName route
elements" now means two distinct things by route kind. On a route whose
default export is definePage(...), the tagName export names the content
element only — the page class always registers under the route-path-derived
fallback tag (0.42.0-alpha.17). Plain element routes (no definePage) are
unaffected: their tagName export remains the registration tag.
-
definePage({ renderIntent: { mode } }):'static'(default, prerendered) /'dynamic'(per-request; the'auto'alias was removed in alpha.13, #609) rendering modes;'dynamic'routes render per request through the generateddist/server/index.js(0.42.0-alpha.1). The formerrenderIntent.revalidatefield and all ISR semantics were removed in v0.44 (issue #1217): no ISR manifest is emitted and no route-level cache revalidation exists. Any future ISR capability must be re-earned from real evidence (#1221). -
Route-module
loader/action/actionsexports with the ADR-0120 protocol:fail(status, data)422 re-render, 303 PRG, named actions viaformaction='?/name'(0.42.0-alpha.2). -
isActionFailure(value)type guard and theOpenElementActionFailure<T>class (app root): the structured failure carrier constructed byfail()and recognized on both the request-time and SPA action chains. -
ActionResult/ACTION_FETCH_HEADERwire types and thedata-open-enhance/data-open-preserve/data-open-regionenhancement attributes with morph-based continuity (0.42.0-alpha.3). Fetch-channel error outcomes are RFC 9457ProblemDetailsanswered asapplication/problem+json(PROBLEM_JSON_MEDIA_TYPE) since 0.42.0-alpha.13 (#863, ADR-0123 addendum item 13). -
alpha.13 additions (ADR-0123 train):
Middleware(element root) andcomposeFetchMiddleware(./build-utils) — the WinterCG-shaped(request, next) => Responsemiddleware contract wired throughmiddleware.use(#858);cli/previewmerged intocli/start --mode=preview(#859);PageRenderingModenarrowed to'static' | 'dynamic'(#609).
| Export (element root) | Class | Purpose | Removal/move condition |
|---|---|---|---|
isDangerousKey, injectPropsSafe, DANGEROUS_KEYS |
experimental (#1214) | The one canonical prototype-pollution guard shared by page projection (SPA bootstrap, projectPageProps) and adapter codegen, which serializes DANGEROUS_KEYS into generated server runtimes at build time |
Kept at the element root at the B1.2 freeze (#1223); may move to a dedicated security subpath before 1.0 |
element, property |
experimental (#1209) | Compile-time-only decorator intrinsics: the compiler admits them by binding provenance (a runtime named import from @openelement/element) and erases them from generated code; evaluated without the compiler (unit tests, config evaluation) they are inert no-ops, carrying no runtime semantics and acting as no second recognizer |
Kept at the element root at the B1.2 freeze (#1223); may move to a dedicated authoring subpath before 1.0 |
v0.44 (ADR-0143) also re-shapes existing entries without adding names:
definePage takes the compiled page element class as its first argument
(definePage(CompiledPageClass, { route?, head?, renderIntent?, props?, error? }))
— the object-descriptor form with a render function field is removed, and
the render-scope loader/action data hooks with it; island authoring is a
single-module compiled class plus the scanned defineIslandConfig export.
The full mapping is in v0.44.0-MIGRATION.md.
The following alpha implementation packages are absorbed and are not supported
consumer imports: @openelement/core, @openelement/signal,
@openelement/router, @openelement/protocol, @openelement/content, and
@openelement/ssg.
Earlier removed product experiments and adapters remain historical only:
@openelement/i18n, @openelement/rpc, @openelement/hub, @openelement/cem,
@openelement/compat-check, @openelement/adapter-lit,
@openelement/adapter-react, @openelement/adapter-vanilla,
@openelement/runtime, and @openelement/style-sheet.
- Element owns the browser/runtime implementation and runtime contracts.
@preact/signals-coreis Element's internal signal engine, not a consumer OpenElement package surface.- App owns routing and application semantics; its router is internal.
- Adapter Vite owns content, static generation, deployment and build contracts.
- Create templates and current docs may import only retained product packages.
- Runtime-free packages (
element,app,ui) contain no Deno or Node host API in their public execution paths (gated bydeno task deno-api:check). - Build packages (
adapter-vite,create) run on the Deno host and may use host APIs behind their public build interfaces (#966: the CLI and template builder useDeno.*directly — that is in-contract; the scaffolding entry isdeno run). What must stay host-agnostic is their output: generated artifacts (dist/server,serve.mjs, the client entries) run on Node/Deno/Bun and may use only Web/ES globals plusnode:builtins. - Alpha internal packages and subpaths have no compatibility promise.
The package export map, generated resolver table, subpath inventory and
export stability classification above are checked together by
deno task package-surface:check. Historical
ADR and release evidence retain their original package names; they are not
current usage documentation.