Skip to content
Meeko
Type to search the guides and the API reference. Open it from any page with / or Ctrl K; move with the arrow keys and open a result with Enter.
Get started
On this page

1.x stability policy

Starting with 1.0.0, @meekojs/meeko follows Semantic Versioning. This page defines what that promise covers, what falls outside it, how an API is removed and which environments run Meeko. Changes in each version are recorded in the CHANGELOG; upgrading from 0.1.0 is explained in the 1.0 migration guide, from 1.0 to 1.1 in Upgrade from 1.0 to 1.1, from 1.1 to 1.2 in Upgrade from 1.1 to 1.2, from 1.2 to 1.3 in Upgrade from 1.2 to 1.3, from 1.3 to 1.4 in Upgrade from 1.3 to 1.4, from 1.4 to 1.5 in Upgrade from 1.4 to 1.5, from 1.5 to 1.6 in Upgrade from 1.5 to 1.6, from 1.6 to 1.7 in Upgrade from 1.6 to 1.7, and from 1.7 to 1.8 in Upgrade from 1.7 to 1.8.

Version May include
Patch (1.0.x) Compatible fixes and documentation.
Minor (1.x.0) New APIs, options, diagnostics and conventions that do not break existing code, and deprecations with their replacements.
Major (2.0.0) Incompatible changes and removal of deprecated features.

Prereleases (1.1.0-rc.1) are published under npm's next dist-tag and are outside the promise until their final release.

A single package#

Compiler, runtime, framework, adapters, CLI and language server are published together in @meekojs/meeko, with a single version. During 1.x they will not be split into packages requiring coordination.

Compiler-generated code (compiled .meeko modules, entries under .meeko/ and meeko build builds) imports @meekojs/meeko/internal, and both must have exactly the same version. That is why meeko init and meeko init-framework pin the exact version in the generated package.json. When upgrading Meeko, recompile or rebuild: do not mix code generated by one version with another version's runtime, even if both are 1.x.

Public API#

The following only changes incompatibly in a major release.

Package entry points#

The subpaths declared in package.json exports and all symbols and types they export, whether or not described in this guide, api.md, framework.md, devtools.md and editors.md; their exact reference is the published .d.ts declarations. Low-level functions for custom hosts (for example matchRoute, createNavigator, createSocketHub, dispatchApiRoute, dispatchRpc or createRequestContext) are also part of the contract:

Entry Contents
@meekojs/meeko Reactivity (signal, computed, effect, batch, untrack, createRoot, onCleanup), lifecycle (onMounted, since 1.1), context (createContext, provide, inject), provideErrorHandler, serializeJson, trustHtml (since 1.3), useId (since 1.5), isTooLargeToAnimate (since 1.8; the exact limit is not a contract) and their types, including TrustedHtml (since 1.3), TagName and ElementAttributes (since 1.5, the types of tags accepted by <component is> and bind="…" attributes) and component and DevTools protocol types. Since 1.5, useId guarantees the same ID in server rendering and hydration of the same version, unique per instance, with the document.idPrefix prefix; the exact ID shape is not contractual.
@meekojs/meeko/framework Configuration, routes, pages, defer, state (useState and, since 1.2, useStorage with its StorageOptions and StorageArea types), navigation (useNavigation and, since 1.2, useRoute with its RouteState type, which gains isActive in 1.5), forms (useForm and its Form type, since 1.2; since 1.5, the onSuccess and focusInvalid options, Form members trySubmit, commit and field, FormSubmission and FormField types, and revalidation on every change of fields with schema errors; since 1.7, the action option and Form member csrfToken), document head (useHead, useSeoMeta and the HeadInput, HeadMeta, HeadLink, HeadScript, HeadStyle, SeoMetaInput, DocumentConfig and DocumentHead types, since 1.2, DocumentSeo, since 1.3, and DocumentConfig.idPrefix, since 1.5), configuration types SitemapConfig, RobotsConfig and StaticHeaderFile (since 1.3), the sitemapEntries option of definePage and its SitemapEntry and SitemapEntriesContext types (since 1.7), themes (since 1.8, useThemeToggle, ThemeToggleState and ThemeToggleSystem; useTheme and the ThemeConfig, ThemeState, ThemeName, ThemeChoice, GeneratedTheme, ThemeToggleMessages and ThemeToggleSnippets types, since 1.4), built-in tag prop types (MeekoLinkProps, MeekoOutletProps and MeekoThemeToggleProps, since 1.4; MeekoLinkProps gains match in 1.5, MeekoAwaitProps, with AwaitResource, AwaitMessages and MeekoAwaitSnippets, since 1.5; and MeekoPopoverProps and MeekoSelectProps, since 1.5, with PopoverPlacement, PopoverMode, PopoverRole, PopoverTrigger, PopoverToggleEvent, PopoverSnippets, PopoverExposed, SelectModel, SelectOptionState, SelectFilter, SelectFilterContext, SelectSnippets and SelectField) and RouteId, the ID of any application route (since 1.4), named layouts (useLayout, defineGroup and the LayoutName, LayoutProps, LayoutHandle, LayoutPropsInput, PageLayout, PageLayoutOptions, GroupDefinition, GeneratedLayouts, GeneratedPageLayouts and NamedLayoutRecord types, since 1.5), auto-imports, route middleware (since 1.6, the second { global: false } argument of defineRouteMiddleware, the middleware option of definePage and defineGroup, the middleware field of RouteRecord, the loadNamedMiddleware option of ClientAppOptions and FrameworkHandlerOptions and the RouteMiddlewareName, RouteMiddlewareOptions and GeneratedRouteMiddleware types; and server route middleware: defineServerRouteMiddleware, its ServerRouteMiddleware and ServerRouteMiddlewareContext types, the serverMiddleware field of RouteRecord, with the route's full chain (global server middleware followed by named middleware), and the loadServerMiddleware(file) option of FrameworkHandlerOptions), API, RPC and Socket clients (createSocketClient, createSocketRegistry, useSocketChannel, useSocketClient since 1.1 and its deprecated alias useSocket), isClientError for these clients' errors (since 1.3), data composable factories (createUseApi, createUseRpcQuery, createUseRpcMutation and their types, since 1.1; createUseApiMutation, retrySafe options and their ApiMutation, ApiMutationInput, ApiMutationContract, UseApiMutation, MutationOptions, MutationAttempt, MutationFailure, RetrySafeOptions, RetrySafeMutation and UncertainMutation types, since 1.2; refreshing and the keepPrevious option with its DataResourceOptions type, since 1.5; the debounceMs and enabled options, since 1.7) and the top-level <script server> code API (usePage, redirect, notFound and the PageRequest, PageRequestOf, PageBodyHead, ServerData, JsonShape and SerializableExport types, since 1.1). Since 1.2, the module and plugin system: defineMeekoModule with its types (MeekoModule, MeekoModuleDefinition, MeekoModuleMeta, MeekoModuleEntry, MeekoModuleContext, MeekoHooks, MeekoHookable, ModuleAutoImport, ModulePlugin and ModuleServerHandler; since 1.8, MeekoModuleName and the extensible MeekoModuleNames interface) and definePlugin with its types (MeekoPlugin, PluginApp, PluginErrorHandler, PluginErrorInfo and PluginRegistration). The members of the setup meeko object, hook names and contexts, and documented module and plugin execution order are contractual; a minor may add members, hooks and context fields. Since 1.7, the cache option of definePage (PageDefinition.cache, and cache?: never in ClientPageDefinition) with its PageCache, PageCacheContext and PageMemoryCache types, and the ServerHeaders and ServerResponseConfig configuration types; the documented SSR page cache rules are contractual: production only, never with a session or Set-Cookie, middleware before 304, and a weak ETag including theme, build and response type (its exact value is not contractual).
@meekojs/meeko/vite Vite meeko() plugin.
@meekojs/meeko/server Portable server core: createServerApp, defineServerRuntime, API/RPC contracts, sessions and stores, middleware, Socket and its brokers, createSocketUpgradeHandler and OpenAPI. Since 1.2, module augmentation of the MeekoSession interface, typing RequestContext.session (SessionData type), is also contractual. Since 1.7, AppServices, GeneratedServices and ServerRuntimeServices: the type of RequestContext.services (and services of ServerRouteMiddlewareContext) when no other is specified is AppServices, inferred by .meeko/types/services.d.ts from the default export of server/runtime.ts; an explicit type (RequestContext<MisServicios>) or any still takes precedence. Since 1.5, the third { message, issues } argument of ApiError, its issues property and the ApiErrorOptions and ApiErrorIssue types: issues of an ApiError with a 4xx status travel in API and RPC responses with the same shape as those of a 400 (at most 20), never with a 5xx. Since 1.7, the form option of defineApiRoute and its ApiFormOptions type (redirect, maxBytes, files and echo), with documented native form behavior: accepted body types, reserved return and _csrf fields, text conversion rules, no submitted value returned without echo, 303 See Other on success and error, 403 without redirection for Origin or CSRF, and mandatory Origin on every native submission. Since 1.8, default Origin checking on every API and RPC mutation, with or without a session (403 INVALID_ORIGIN if missing, null or disallowed; origin: "required" makes this explicit), and the per-route origin: "none" escape hatch, disabling it only for requests without a session, including native forms; requests with a session still require Origin and the CSRF token. createLocalCalls sends the application origin as Origin, like SSR clients (see Approved exceptions). Since 1.8, the root field of ServerRuntimeContext (and root option of createServerApp): the absolute project root in meeko dev; in Node, Bun and Deno outputs, the folder three levels above the output, unless specified by MEEKO_ROOT; undefined in Workers and when a custom host does not pass it. Since 1.3, contentSecurityPolicy and its ContentSecurityPolicy type (since 1.4, with the third styleHashes argument), and process failure policy: ServerRuntime.processErrors (ProcessErrorPolicy type), handleProcessErrors (ProcessErrorOptions type) and the optional third argument of ServerErrorObserver (ServerErrorInfo type); documented defaults are contractual (a rejection is reported and the server continues; an exception is reported and exits with code 1). Since 1.7, the contentSecurityPolicy, headers and buildId fields of AppManifest, scriptHashes and styleHashes of PrerenderedPage, cache of PageRoute, and the documented CSP rules for server adapters (hashes on a prerendered page or one with cache, only for inline code Meeko writes outside #app and teleports; per-response nonce in other SSR; default-src 'none'; frame-ancestors 'none' in JSON; nonce or hashes in script-src-elem and style-src-elem if present in the policy, and no nonce or hash in a directive with 'unsafe-inline' unless it has 'strict-dynamic'). Since 1.7, clientAddress(request, env?), with the same MEEKO_TRUST_PROXY policy as context.clientAddress and rateLimit, the optional SqlSessionDriver.rateLimit(key, windowMs, now) method with its SqlRateLimitRow type and SqlSessionStore.rateLimit, present only when the driver implements it; the fixed window, key being the SHA-256 (32 bytes) of the counter key and, without SqlSessionDriver.rateLimit, counters remaining in each instance's memory with a production warning are contractual. Since 1.8, with MEEKO_TRUST_PROXY=cloudflare, the CF-Connecting-IP IP (or True-Client-IP if missing) only when the connection and every X-Forwarded-For hop come from MEEKO_TRUSTED_PROXIES networks (by default loopback, RFC 1918 and fc00::/7); otherwise, the first outside hop or connection IP.
@meekojs/meeko/node Node adapter: serveNode, toNodeListener, createNodeAssetReader, attachNodeWebSocketServer and createNodeSocketUpgradeHandler.
@meekojs/meeko/password Since 1.2: hashPassword, verifyPassword and needsRehash, with node:crypto scrypt (Node, Bun and Deno). The scrypt$<N>$<r>$<p>$<sal>$<hash> format is contractual: a minor may increase default parameters, and verifyPassword always accepts previously written hashes.
@meekojs/meeko/bun Bun adapter: createBunServer and createBunAssetReader.
@meekojs/meeko/deno Deno adapter: serveDeno and createDenoAssetReader.
@meekojs/meeko/cloudflare Cloudflare Workers adapter: createWorker, createSocketRoomClass, DurableObjectSocketPubSub, socketRoomName, D1SessionDriver, createCloudflareAssetReader, createCloudflarePrerenderSource and their structural types. Since 1.7, the D1SessionDriver rateLimitTable option, through which it implements rateLimit, and D1SessionDriver.rateLimitSchema().
@meekojs/meeko/build Build tooling for Node: discovery, configuration, prerendering and buildProject. Since 1.3, inlineScriptHashes and its InlineScriptReport type (since 1.4, also inlineStyleHashes and styleHashes in PrerenderIndex), and the SitemapConfig, RobotsConfig and StaticHeaderFile configuration types, the same as those exported by @meekojs/meeko/framework. Since 1.5, discoverLayouts, the layouts option of discoverRoutes, composeFrameworkRoutes and the new second argument of discoverErrorPage, and the DiscoverErrorPageOptions, StaticRouteOptions and StaticGroupOptions types. Since 1.6, discoverRouteMiddleware with its RouteMiddlewareDiscovery type (with serverGlobal) and the middleware field of StaticRouteOptions and StaticGroupOptions. Since 1.7, the ServerHeaders and ServerResponseConfig configuration types (server contentSecurityPolicy and headers in node, bun, deno and cloudflare), the same as those exported by @meekojs/meeko/framework.
@meekojs/meeko/devtools-ui Integrated DevTools panel (mountDevtoolsPanel).
@meekojs/meeko/prettier Prettier plugin for .meeko (since 1.1): the meeko parser and the meekoIndentSections, meekoPreserveArrayBreaks (since 1.3; also replaces Prettier's estree printer) and meekoSortClasses options (since 1.3, with off, stylesheet and alphabetical values). Parser and option names are stable; exact formatting output may improve in a minor, like Prettier's.
@meekojs/meeko/postcss PostCSS meekoUtilities() plugin (since 1.2): the @utilities "<ruta>" [layer(<nombre>)] at-rule, defineUtilities, the rule, atRule and css helpers and their types, with UtilityAtRuleDeclarations since 1.3 and the autoImports option since 1.4. Runs only in Node, in development and during build. Exact generated CSS formatting (spaces, line breaks) may change in a minor.

tests/package-boundaries.test.ts pins the exact list of root exports and package.json subpaths.

Types are part of the contract. There are two nuances:

  • A union Meeko returns to the application may gain members in a minor release: for example ErrorSource (which gains "uncaught" and "rejection" in 1.3), PageRender, DeferredStatus, DevtoolsReadKind, DevtoolsEventKind, a SocketClient's state (which gains "unauthorized" in 1.1) or error codes. An exhaustive switch over these must have a default branch. A union the application passes to Meeko is not narrowed in 1.x.
  • An interface for an object Meeko returns to the application may gain members in a minor release: for example RouteState, returned by useRoute() (gains isActive in 1.5), DataResource and LoadedResource, from useApi and resources (gain refreshing in 1.5), or Form (gains trySubmit, commit and field in 1.5). Read and destructure them normally, but do not implement them by hand (const route: RouteState = { … }), because a minor may add a required member. In a test, a partial double with Partial<RouteState> or an as RouteState assertion still compiles. Interfaces the application passes to Meeko (options, configuration, contracts) only gain optional members.
  • A type fix rejecting code that already failed at runtime may ship in a minor or patch release, with a CHANGELOG entry.

Approved exceptions#

Changes rejecting runtime-valid code that ship in a minor release by explicit decision, each with its migration:

Version Change Reason and migration
1.1 meeko check and the editor check each component tag's props (<Etiqueta> or <component is>) only against the child's declarations: an unknown prop is an error even alongside valid props. In 1.0, an extra prop passed checking and was ignored at runtime, so it did not fail: this change is not a runtime bug fix but an accepted exception to detect typos and obsolete props. Simply delete the prop or declare it in the child; runtime behavior does not change. See Upgrade from 1.0 to 1.1.
1.1 html="..." and model="..." on a standard HTML element (<div>, <input>…) are directives: the value is an expression, and unsupported uses produce MEEK095 and MEEK065. On custom elements (hyphenated tags, such as <x-markdown html="intro">) they remain literal HTML attributes, as in 1.0. In 1.0 they were written as literal attributes, but html and model are not valid attributes of any standard HTML element, so browsers ignored them. If a stylesheet or script read that attribute, write it as data-html/data-model or with bind:html="'texto'", which remains an attribute.
1.1 The Socket handshake no longer responds 401 or 403 to session or channel authorization rejection: Meeko adapters upgrade and immediately close with 4401 or 4403. A disallowed Origin still receives HTTP 403. In createSocketUpgradeHandler, the reject action of SocketUpgrade gains the optional close field with code and reason. The browser's WebSocket does not expose a rejected handshake's HTTP status, so the client retried forever. A custom client expecting HTTP 401/403 must treat 4401/4403 closures as definitive rejection; Meeko's client already does. The union gains no members, so a custom adapter written for 1.0 keeps working and responds with HTTP status until it handles close. See Upgrade from 1.0 to 1.1.
1.2 A compiled child receiving new props stays mounted and only updates bindings reading the prop: setup does not rerun, so a value copying a prop (const { label } = props, props.title.toUpperCase() or the input to useApi(ruta, { query: { q: props.q } })) keeps its initial value, and writing to props throws TypeError. Components with a top-level data await are still remounted. Rebuilding the child replaced the <input> the user was typing in, with its focus, cursor and composition, and the snippets it had received. Read the prop where used ({props.label}), derive with computed(() => ...) or pass a function to useApi/useRpcQuery; to change a parent value, use an event or model. See Reactive props and Upgrade from 1.1 to 1.2.
1.2 context.session.create() in a POST, PUT, PATCH or DELETE request rejects with 403 INVALID_ORIGIN if its Origin is missing or disallowed, unless the route declares origin: "none". Security fix (login CSRF): until 1.1, a login route without its own check accepting a form or other body without a CORS preflight allowed another site to log the victim's browser into an attacker's account, and origin: "required" did not exist. This cannot be fixed compatibly, so the vulnerability exception in the deprecation policy applies: the minimum change, with an explicit escape hatch. Browsers already send Origin in these requests, so web applications do not change; a script or native app must send Origin or the route must declare origin: "none", documented as unsafe. An application behind a TLS-terminating proxy without MEEKO_PUBLIC_ORIGIN (or publicOrigin) sees login rejected with 403, because the server receives http:// and the browser sends an https:// Origin; that same application already failed in 1.1 on every authenticated mutation, which checks the same Origin, so it did not work before and the fix is the same: set MEEKO_PUBLIC_ORIGIN to the public origin. See framework.md and Upgrade from 1.1 to 1.2.
1.2 On a template HTML or SVG element, event.currentTarget in on:evento has the tag element's type (HTMLInputElement on <input>) and event has the HTMLElementEventMap (or SVGElementEventMap) type for that name. An explicit assertion to an unrelated element, such as event.currentTarget as HTMLSelectElement on an <input>, is now a type error (TS2352) in meeko check, meeko build and the editor. This is a type-only fix with no runtime change: generated code and the currentTarget value are the same as in 1.1. Until 1.1, currentTarget was a generic EventTarget, so any assertion passed, including one naming another element and reading a property the actual element lacks (selectedIndex on an <input>, which gives undefined): the new error identifies that bug. The element cannot be typed without rejecting those assertions. Migration: remove the assertion, since the type is already the element's (event.currentTarget.value), or correct it to the tag's element. Component tags do not change.
1.3 UtilityDeclarations, the rule() and atRule() declarations from @meekojs/meeko/postcss, use csstype types instead of accepting any key with a string or number: a property csstype does not know (backgroundColr), a numeric length or time other than 0 (padding: 4) and, for properties accepting only keywords (position, boxSizing…), a string outside that set or a widened string (const base = { position: "absolute" }) are type errors. atRule() accepts descriptors csstype knows through the new UtilityAtRuleDeclarations. This is a type-only fix with no runtime change: the plugin generates the same CSS as in 1.2. Almost everything now rejected already failed in the browser, which silently drops unknown properties or padding: 4; the rest (a recent property or descriptor csstype does not yet include, or a widened string) is the accepted cost of name completion and checking. Migration: correct the name, write the unit ("4px"), annotate the object with UtilityDeclarations or use as const, and write anything unknown to csstype with css`…`. Dynamic keys (Object.fromEntries, Record<string, string>), --* properties, kebab-case keys, fallback arrays and !important still compile. With the [property: string] index signature removed, reading a UtilityDeclarations object with a string key (decls[key] inside a loop over Object.keys(decls)) produces TS7053 with noImplicitAny (included in strict); migration: type the key as keyof UtilityDeclarations (decls[key as keyof UtilityDeclarations]) or use Object.entries(decls) to iterate. See framework.md and Upgrade from 1.2 to 1.3.
1.4 A module whose meta.configKey is a built-in MeekoConfig key (layers, extends, modules, imports, server, css, document, dev, site, sitemap, robots or theme) makes configuration loading fail with a message requesting another key. With theme, non-theme options (theme: { initial: "dark" }) fail with unknown field, and the message points to that conflict. Since 1.4, theme is Meeko's light/dark theme option, and the 1.3 guide's module example used configKey: "theme". That module would read Meeko's theme options as its own, its MeekoConfig.theme augmentation conflicts with Meeko's type (TS2717) and its own options under theme would activate Meeko's theme or fail validation: there is no compatible way for them to coexist. Give the module another key (for example, acmeTheme), move its options there in meeko.config.ts and rename its MeekoConfig augmentation; a module without configKey, or with a key not belonging to Meeko, does not change. See Upgrade from 1.3 to 1.4.
1.5 bind="..." (without a name) on a standard HTML element or component tag is the bulk attributes directive: the value is an expression. Until 1.4, it was a literal bind HTML attribute or, on a component, the static bind prop with the text. This syntax best fits bind:nombre, and bind is not an HTML attribute: on an element, it could only write an attribute browsers ignore. Migration: write the bind prop or attribute with bind:bind="'texto'". Custom elements do not change. See Upgrade from 1.4 to 1.5.
1.5 alias is a built-in MeekoConfig key: a module with meta.configKey: "alias" makes configuration loading fail, like other Meeko keys since 1.4. Since 1.5, alias configures Meeko's path aliases; module options under that key would be read as aliases and its MeekoConfig.alias augmentation would conflict with Meeko's type. Migration: give the module another key and move its options there in meeko.config.ts; see Upgrade from 1.4 to 1.5.
1.6 app/middleware/*.server.ts files, ignored by route middleware discovery until 1.5, are server route middleware. A file without a default export, or with one that is not a defineServerRouteMiddleware(...) call (a helper such as export default function parseCookies…), stops meeko dev, meeko build and meeko check naming the file. The suffix fits other conventions (.server.ts is server-only in app/plugins and imports), and without checking, a helper living there would execute on every page request as global server middleware. Migration: move helpers to server/utils (auto-imported in server code) or rename them without .server. See Upgrade from 1.5 to 1.6.
1.6 A spread in page options (definePage({ ...comun, render: "ssr" })) that may carry middleware (an imported object, a call result or a file constant containing middleware) stops discovery if middleware is not explicitly declared after the last spread. Meeko reads route options without executing the file, so a spread's middleware did not reach the manifest and the page was served without it, even though types accepted it. Migration: write middleware after the last spread, with names or middleware: [] if none are added. A spread of a same-file constant without middleware remains valid. See Upgrade from 1.5 to 1.6.
1.7 With TypeScript server/runtime.ts, RequestContext.services (without an explicit type) is AppServices, the inferred services type, instead of unknown. A direct assertion to an incompatible type, such as context.services as Otro, is now a type error (TS2352) in meeko check, meeko build and the editor. This is a type-only fix with no runtime change: the services object is the same as in 1.6. Typing context.services without assertions requires comparing it with its actual type, and an assertion to a type that does not fit the return of server/runtime.ts is exactly the error this change identifies. Migration: remove the assertion (the type is already that of server/runtime.ts), correct the type or, if intentional, write as unknown as Otro or RequestContext<Otro>. A context with other services, such as one created in a test with createRequestContext(request, { db: falso }, session), remains accepted where a RequestContext is expected. See Upgrade from 1.6 to 1.7.
1.7 Assigning computed(...).value throws TypeError. TypeScript already rejected it (ReadonlySignal), but in untyped JavaScript or after an assertion, writing replaced the computed value until the next recomputation, so the interface showed a value not derived from its signals. This is the same behavior as writing to props since 1.2. Migration: change the signals the computed depends on or use a signal if the value is written manually. See Upgrade from 1.6 to 1.7.
1.7 PageInputError.name is "PageInputError"; previously it was "Error". Aligns it with ClientError and ApiError, whose names identify the error in logs and error.name. Only code comparing error.name === "Error" to recognize it changes; instanceof PageInputError, code and the message are unchanged. Migration: use instanceof PageInputError or error.code. See Upgrade from 1.6 to 1.7.
1.8 Every mutation checks Origin by default, with or without a session: POST, PUT, PATCH and DELETE requests to defineApiRoute and all defineRpc calls respond 403 INVALID_ORIGIN if Origin is missing, null or neither the application's (publicOrigin or MEEKO_PUBLIC_ORIGIN) nor one of SessionManager.allowedOrigins, unless the route declares origin: "none". Sec-Fetch-Site and Referer do not replace Origin. origin: "required" is equivalent to the default. Security fix (CSRF): until 1.7, an anonymous mutation was checked only with origin: "required", so a route accepting a form or JSON sent as text/plain, without a CORS preflight, could execute from another site. This cannot be fixed compatibly, so the vulnerability exception in the deprecation policy applies: the minimum change, with the explicit per-route origin: "none" escape hatch, which already existed and is documented as unsafe. With "none", a request without a session needs no Origin, including a native form (a form-encoded webhook, which received 403 until 1.7), and one with a session still requires Origin and the CSRF token. createLocalCalls sends the application's origin, like SSR clients, so a local mutation from a page requested with GET does not receive 403. Browsers already send Origin in these requests, and SSR api.* and rpc.* clients send the request origin, so web applications do not change; a webhook, public API or script without Origin must declare origin: "none" (with another proof, such as a signature) or send an allowed Origin. See framework.md and Upgrade from 1.7 to 1.8.
1.8 meeko check (and meeko build, which runs it) always checks meeko.config.ts types with those of the modules in modules; until 1.7, it did so only when configuration loading failed. So a module key is typed without imports (for example, i18n: { defaultLocale: 1 } is TS2322), as required by the documented modules form. No runtime change: configuration that loads keeps loading. A previously unnoticed type error (such as process.env without @types/node installed) makes checking fail; simply fix it. See Upgrade from 1.7 to 1.8.
1.8 devtools is a built-in MeekoConfig key: a module with meta.configKey: "devtools" makes configuration loading fail, like other Meeko keys since 1.4. Since 1.8, devtools mounts the Meeko DevTools panel in meeko dev; a module's options under that key would be read as DevTools options. Migration: give the module another key and move its options there in meeko.config.ts.
1.8 <Name>Props, the auto-imported type of each component, no longer includes the callbacks of defineEmits, and <Name>Emits is new. Without this change, a wrapper of a component with events could not be written. Only types are affected: const props: BaseButtonProps = { onClick } gives TS2353 and BaseButtonProps["onClick"], TS2339. ComponentProps<"BaseButton"> and the module's ComponentProps do not change, nor do SSR and hydration. A package published before 1.8 does not need republishing: its declaration does not export the new types, so its <Name>Props remains its ComponentProps, with the callbacks, and it has no <Name>Emits. Migration: Component types.

.meeko format and generated module#

  • The <script lang="ts">, <template> and <style> sections (with scoped and :global(...)), and on pages <script server lang="ts"> (since 1.1), with its two forms: route options and top-level code whose export const declarations are page data, with the rule deciding when a block has top-level code. Since 1.1, <script lang="ts"> is optional and accepts the generic attribute with the component's type parameters, which is not allowed on pages, layouts, app.meeko or error.meeko.
  • The defineProps<Props>() macro, with or without defaults, and since 1.1 the defineEmits<T>(), defineExpose(value), defineModel<T>(name?, options?), defineSnippets<T>() and useElementRef("name") macros.
  • The {expression}, on:event, bind:name, if:when, each:of/each:as/each:key/each:index and <component is> directives. Since 1.1:
    • each:key is optional and without it the key is the index;
    • on:event on <component> and component tags is equivalent to the onEvent prop;
    • component tags with uppercase names (<AppButton />), whose content consists of snippets;
    • static props on components (variant="primary" passes a text literal and an attribute without a value passes true), with is, ref, html, hyphenated names and names containing : without being directives reserved (since 1.8, except aria-* and data-*);
    • the ref:name directive. ref="x" and bind:ref retain their 1.0 behavior;
    • model and model:name on component tags, model on <input>, <textarea> and <select>, and the <snippet name params> and <render snippet args /> elements;
    • the html="expression" directive, inserting unescaped HTML, normalized to fit the element and never executing its <script> elements when inserted in the browser;
    • html and model are directives only on standard HTML elements; on custom elements (hyphenated tags) they are literal attributes;
    • <component is="..." /> is self-closing, like <Etiqueta />.
  • Since 1.8, a non-void HTML element without content can self-close with /> (<div />, <textarea />, <td />, <my-element />) and is equivalent to writing its closing tag: subsequent nodes are siblings, and SSR writes <div></div>. <template>, <script>, <style>, <html>, <head>, <body> and unknown names still give MEEK001, and a self-closed table or list element followed by its own closing tag (<td /></td>) gives MEEK008 (see Elements without content).
  • Since 1.4, if:when on <snippet>: the parent passes that snippet only while the condition is true, and only if the child declares it optional.
  • Since 1.5, the built-in <MeekoTeleport> tag with to ("body" or "#id", the latter with a letter followed by ASCII letters, digits, - or _), bind:to, disabled and bind:disabled, and the MeekoTeleportProps and MeekoTeleportTarget types from @meekojs/meeko/framework. The content's destination in server HTML (inside the id element, after its content, or after the application and before Meeko scripts with body), ordering of multiple teleports to the same target, retaining the declaring component's scope, moving the same nodes when disabled changes and behavior with a missing target described in framework.md are contractual. Comments marking content are internal.
  • Since 1.5, <component is> accepts an HTML tag (a literal, an expression whose type is a union of tags, or a union of tags and components) and content: children with a tag, snippets with a component. Supported tags are the public TagName type; a non-global attribute is written only while the current tag supports it according to Meeko HTML data (no type on <a> or <area>). When the tag changes, children move to the new element with their state. The bind="obj" directive writes an object as element attributes or component props, without on* keys; explicit attributes win and class and style are combined. bind remains a literal attribute on custom elements and is no longer a static prop.
  • Since 1.4, defineSnippets<T> accepts a parameter tuple or Snippet<Args> in each key of T, counting as Args, so T can derive from another component's snippet map with Omit, Pick or & while preserving parameters and optionality. A tuple declaration retains the same type.
  • Since 1.8, <snippet> name accepts, besides an identifier, identifier-character segments joined by hyphens (/^[A-Za-z_$][\w$]*(?:-[\w$]+)*$/, such as cell-email), and defineSnippets<T> accepts keys from a template literal mapped type over the component's type parameters ({ [K in keyof Row & string as `cell-${K}`]?: [row: Row, value: Row[K]] }). It is contractual that meeko check and the editor check these snippets in the parent with the type inferred from its props (nonexistent name, each parameter's type and number of params), and that within the component, an intersection with a mapped type retains its fixed keys' types (snippets.cell). The type of an access by computed name inside the component is excluded: TypeScript does not resolve it over a type parameter.
  • Since 1.4, with theme in meeko.config.ts, the built-in <MeekoThemeToggle> tag (deprecated since 1.8, removed in 2.0; use UIThemeToggle from @meekojs/ui): its props (mode, themes, labels, messages, class, id), one snippet per choice, application-styled markup (the root <form method="post" action="/_meeko/theme"> with data-meeko-theme-toggle, the mode="cycle" <button>, icon <span data-meeko-theme-icon> elements, role="radiogroup" and its role="radio" buttons with aria-checked and data-meeko-theme-option in mode="select"), the <html> data-theme-choice attribute with the choice, the <head> rule showing the chosen icon, and the POST /_meeko/theme endpoint with the framework.md rules (valid themes, same-site Origin, 303 only to a same-origin path). Default text and the rule's CSS may change in a minor.
  • Since 1.5, the built-in <MeekoAwait> tag: its props (for, as an expression or with bind:for, reserve, messages, class, id), snippets (default with refreshing, pending and error with error and retry), states described in framework.md, narrowing of for data in its content, and application-styled markup: a <div> with data-meeko-await (pending, error or ready), aria-busy="true" while loading or refreshing, data-meeko-refreshing during refresh and, with reserve, data-meeko-reserve and data-meeko-reserve-min. It is contractual that reservation applies min-block-size only in pending, without style attributes, and that persistence lives in sessionStorage; each zone's key, stored format, <head> script text, default text and number of remembered zones may change in a minor.
  • Since 1.5, the built-in <MeekoPopover> and <MeekoSelect> tags (deprecated since 1.8, removed in 2.0; use UIPopover and UISelect from @meekojs/ui) (see Built-in components): their props, snippets (trigger, anchor and content; option, value, group, empty and loading), model, events (toggle with { open }, change) and exposed members (show, hide, toggle); application-styled markup (data-meeko-popover and data-meeko-popover-trigger, data-meeko-select with data-open, data-multiple, data-disabled and data-loading, role="option" options with data-active, data-selected and data-disabled); documented ARIA roles and attributes; documented keyboard behavior; the name <input type="hidden"> elements, and not using style="…" attributes. The meeko cascade layer rules, --meeko-popover-* custom properties, default text and ID shapes are implementation details and may change in a minor.
  • Since 1.8, a static class or style alongside bind:class or bind:style on the same element (or on a <component is> that can only be a tag) writes a single attribute in SSR, hydration and updates: dynamic classes follow static classes, and dynamic style declarations follow and override static ones. If the dynamic value is undefined, null, false or empty, static text stays as-is. Previously SSR wrote the attribute twice and hydration failed.
  • Since 1.8, lowercase aria-* and data-*, static or with bind:, on a component tag or a <component is> that can only be a component pass a prop with that name (previously MEEK032); data-meeko* and uppercase names still produce MEEK032. This only makes previously uncompilable code valid.
  • Since 1.8, when the script writes defineEmits events (a type literal or local interface or type without extends or parameters), props does not enumerate their callbacks (onSave) in Object.keys, in, spreading or rest destructuring; reading them (props.onSave) and emit do not change. The props type never declared them, so typed code did not enumerate them intentionally.
  • Since 1.8, a PascalCase text literal in <component is> (is="to ? 'MeekoLink' : 'a'", 'UICard') names <MeekoLink> or an imported, auto-imported or script-declared component, which the compiler resolves like its tag: one if:when region per name, whose condition is the ?: path to its literal, and another for HTML tags; each attribute goes only to branches supporting it. HTML tags are lowercase. This is additive: previously a PascalCase literal gave MEEK112 (not a tag), and is with a component value or HTML tags is unchanged. An unknown name or a value alongside names gives MEEK132. Region and marker distribution is an implementation detail. resolveComponent("MeekoLink") and the MeekoLink entry in ComponentRegistry (unless the project has a component with that name) load <MeekoLink> as a component from @meekojs/meeko/builtins/link.
  • Since 1.8, bind: on an SVG or MathML element writes the name with standard casing, like the static attribute (bind:viewBox writes viewBox, not viewbox); previously, the lowercase attribute had no effect on SVG and hydration did not match.
  • Since 1.8, <MeekoLink> with a route-union bind:to accepts bind:params with parameters of any of them, or none, and bind:search with undefined; checking for a single route is unchanged.
  • Since 1.8, defineProps can be destructured in its const declaration (const { label, size = "md", class: className, ...rest } = defineProps<Props>()): each name reads its prop where used and is reactive in the script and template, except where a closer scope declares the same name; pattern defaults are equivalent to the defaults argument; ...rest is a read-only reactive view of the remaining props, excluding defineEmits handlers. Defaults (in both forms) can read top-level constants using only literals and imports. This is additive: it only makes code previously producing MEEK015 or TS2304 valid. Default types are computed per variant of a props union, so a default for a prop one variant declares never no longer allows it in that variant for the parent.
  • Since 1.8, with a props union, a literal defineProps default (kind = "a", in the pattern or argument) makes its prop optional for the parent only in variants accepting that value, in ComponentProps, .d.meeko.ts declarations and the editor; a nonliteral default stays optional in all variants and gives MEEK131 if one requiring the prop does not accept it. This is a fix: previously a parent could omit any variant's discriminant (<Child b="hello" /> with kind = "a"), the component received { kind: "a", b: "hello" }, which matches no variant, and failed at runtime. That parent stops compiling; to migrate, pass the discriminant (kind="b"). Unions without a discriminant default and non-union props are unchanged.
  • Since 1.8, Meeko HTML data comes from the standard (src/shared/html-data, generated by scripts/html-data.ts): tags and attributes accepted by <component is> and bind="obj" are a superset of 1.7's. The public AttributeValues<Tag, Name> and AttributeValueOf<Tag, Name> types (from @meekojs/meeko), and ElementAttributes<Tag> values, suggest standard values without restricting acceptance; a literal outside them gives warning MEEK122. hidden with nonempty text ("until-found") writes that text, and standard boolean attributes not treated as such by 1.7 are written empty.
  • Setup rules since 1.1: useElementRef, useApi, useRpcQuery and useRpcMutation only at the top level; top-level await only on useApi, useRpcQuery, useElementRef, useSocket and useSocketClient; splitting setup at the first browser await, whose continuation runs only in the browser and accepts expression and control flow statements.
  • Each compiled module's shape: ComponentProps, ComponentExposed, ComponentModels and ComponentSnippets (since 1.1), css (with the Meeko Vite plugin, since 1.8 it depends on the graph: in the SSR graph —meeko dev, Node build, prerendering, static and Cloudflare— and the meeko dev client, it is <style> CSS already processed by project PostCSS and CSS plugins, the same CSS embedded by SSR and prerendering and reapplied by HMR; in the production client bundle it is compiler output, as before 1.8, because no Meeko code reads it there and the stylesheet Vite bundles already applies processed CSS. In a host that does not process CSS and resolves that module as an empty string, such as Vitest without test.css: true, it is also compiler output. With compile() without styleModule, it is always compiler output), renderWithState, renderToString, hydrate and hydrateScope, whose disposal function carries exposed. In a component with generic, types and functions carry its type parameters. The hasDataAwait, __meekoChildren, __meekoReactiveProps (since 1.2), __meekoChannels and __meekoTag flags are internal.
  • Since 1.8, placement of inline SSR and prerender <style> elements: at the end of <head>, after global stylesheets (app/styles/main.css, those from addStylesheet and document head), where the client later adds component CSS, so first render and hydrated page have the same cascade; previously they preceded global stylesheets and a global rule of equal specificity won only until hydration. The error page for a server failure, which is not hydrated, also embeds its CSS. The data-vite-dev-id attribute meeko dev adds to those <style> elements for Vite to adopt them is an implementation detail.
  • The Component<Props> type.

Generated JavaScript is not public; see Outside the contract.

CLI#

The commands and options shown by meeko --help:

Command Options
meeko dev [directorio]
meeko check [directorio | componente.meeko] --json (only for a component)
meeko build [directorio] --adapter <node|bun|deno|cloudflare|static> o --adapter=<nombre> (static, since 1.3)
meeko start [directorio]
meeko preview [directorio] --adapter <node|bun|deno|cloudflare|static> (static, since 1.3)
meeko check-app <directorio>, meeko routes <directorio>, meeko prerender <directorio>
meeko compile <componente.meeko> <salida.ts>, meeko types <componente.meeko>
meeko init <directorio>, meeko init-framework <directorio>
meeko clean [directorio] (since 1.1) --all, --dry-run. The options and what each mode deletes are contractual; exact output text is not.
meeko auth hash (since 1.2) No options. Reads the password without echo in a terminal (twice) or from stdin in a pipe, and writes only the hash line to stdout.
meeko lsp --stdio, --node-ipc, --socket=<puerto> o --pipe=<nombre>
meeko --help (-h), meeko --version (-v)

Also part of the contract:

  • the meeko-language-server binary, with the same transports as meeko lsp;
  • exit code 1 on errors;
  • diagnostic codes (MEEK0xx, MEEK1xx and MEEK_*; see Diagnostic codes);
  • the { code, message, filename, location } shape of meeko check --json.

Message text may improve in any version; since 1.1 it is in Spanish. A minor may add diagnostics for code that already failed at runtime or violated a documented contract, and may stop emitting one when the code it flagged becomes valid. A retired code is not reused with another meaning.

Diagnostic codes#

Code Topic
MEEK001 Invalid component HTML.
MEEK002 Top-level content other than <script>, <template> or <style>, including a component tag outside sections.
MEEK003, MEEK004 Section without a closing tag, or duplicate section.
MEEK005 Retired in 1.1. Flagged a missing <script lang="ts">, which becomes optional.
MEEK006 <script> or <script server> without lang="ts".
MEEK007 Missing <template>.
MEEK008 Invalid template HTML, including a closing component tag without an opening one and, since 1.8, <input> or another <select> inside a <select>.
MEEK009, MEEK010 Empty or unclosed template expression.
MEEK011 Invalid event name.
MEEK012 Unsupported dynamic attribute or directive.
MEEK013 <script> syntax error.
MEEK014, MEEK015 More than one variable per statement, destructuring of a useElementRef result or an unsupported defineProps destructuring form: nested, array, computed key or let (since 1.8; from 1.2 to 1.7, any defineProps destructuring; previously, any destructuring).
MEEK016, MEEK017 Duplicate defineProps, outside its own declaration or without a type argument.
MEEK018 Unsupported top-level statement in <script> or the browser continuation: a value export, class, enum, namespace, import = or standalone return, throw, break or continue. Since 1.2, synchronous setup accepts any expression and control flow statement, like the continuation.
MEEK019 Invalid :global(...) in <style scoped>.
MEEK020 Template expression syntax error.
MEEK021, MEEK022 if:when and each:of on the same node; invalid or duplicate each:as or each:index.
MEEK023 Retired in 1.1. Flagged a list without each:key, which becomes optional.
MEEK024 Incomplete template directive.
MEEK030, MEEK031 <component> without is, or with content. Since 1.5, <component is> accepts content and MEEK031 flags only content in a void tag (img, input, hr…): a compilation error for an is literal and a meeko check and editor type error if it comes from the is type.
MEEK032 Component attribute that is not a prop: is on a tag, ref="x", html and names with hyphens or : that are not directives. Since 1.1, any other identifier-named attribute is a static prop instead of this error. Since 1.5, on a <component is> whose expression may yield a tag, these attributes belong to the element and, if the is type accepts a component, produce the MEEK112 type error.
MEEK033, MEEK034 Component import that is not a relative .meeko path (since 1.5, or a path alias), or does not exist.
MEEK040 to MEEK042 defineProps defaults; MEEK042 also flags a prop destructured twice.
MEEK043, MEEK044 <script server> with a value, or outside a page.
MEEK045 to MEEK048 defineEmits, and on:x alongside bind:onX (or the same prop twice).
MEEK049 Retired in 1.1. Flagged content in a component tag, which becomes its snippets.
MEEK050, MEEK051 defineExpose.
MEEK052, MEEK053 The ref: directive and duplicate names.
MEEK054 to MEEK058 Top-level composables, await and the browser continuation; MEEK056, useElementRef arguments.
MEEK059 to MEEK062 defineSnippets, <snippet> and <render>.
MEEK063 to MEEK066 defineModel, model on form controls and model/model:name on components, with their prop and event.
MEEK070 to MEEK076 Top-level <script server> code.
MEEK080 await useElementRef(...) on a component without defineExpose.
MEEK090 The same prop as static text and with bind: or on:.
MEEK095 The html directive: children, void or text elements, <template>, model or an empty value.
MEEK100 The generic attribute: invalid list, modifiers, reserved or duplicate names, unsupported location and script types using its parameters.
MEEK101 Warning (since 1.2): a props read executed only once by setup; since 1.8, also a name destructured from defineProps or its ...rest.
MEEK104, MEEK105 Since 1.2, <MeekoOutlet /> outside app.meeko and layouts, or with attributes or content, and a <MeekoLink> without to, with href or with params/search without bind:; since 1.5, also a static match other than "exact" or "prefix".
MEEK106 Warning (since 1.2): a project component named MeekoLink or MeekoOutlet (since 1.4, also MeekoThemeToggle; since 1.5, MeekoAwait, MeekoTeleport, MeekoPopover and MeekoSelect) replaces Meeko's built-in tag.
MEEK107 Warning (since 1.3): the html="..." value is a string (or any) without the TrustedHtml brand from trustHtml. Becomes an error in 2.0; see Deprecated APIs.
MEEK108 Since 1.3, an API unavailable where used: in a component's <script> or template (which also execute in the browser), a server auto-import (defineApiRoute, defineRpc, defineServerMiddleware, defineSocketChannel, defineServerRuntime, ApiError or one from server/utils), a value import from @meekojs/meeko/server or /cloudflare, or an import from server/ or a *.server.* module; in <script server>, component macros (defineProps, defineEmits, defineExpose, defineModel, defineSnippets, useElementRef) and onMounted. All this already failed during build or runtime; see framework.md. Since 1.5, also an import through #server/ (or a custom alias to a server/ folder) in a component, an app/ .ts or shared/, even with import type, and a server/ import through ~/; see Alias scopes.
MEEK109 Warning (since 1.3): in a component's <script> or template, definePage, which has no effect there, or a value import from @meekojs/meeko/node, /bun, /deno, /password or /build, whose functions work only on the server. Becomes an error in 2.0; see Deprecated APIs.
MEEK110 Since 1.4, type error: if:when on a <snippet> filling a required child snippet.
MEEK111 Since 1.4, <MeekoThemeToggle> without theme in meeko.config.ts (in meeko dev, meeko build, meeko check, the editor and, if known only by executing configuration, during rendering).
MEEK112 Since 1.5, <component is> with an HTML tag: a tag it cannot render (script, iframe, unknown or inside <svg>), an arbitrary string, an attribute supported by none of the possible tags, model with tags, or an attribute valid only on a tag (or only on a component) when is accepts the other type. A compilation error for an is literal and a meeko check and editor type error in other cases; at runtime, a disallowed value throws TypeError before writing HTML. Since 1.8, with a text that arrives at runtime and may name a component, the message says what to do: a literal in the template, the imported module or resolveComponent.
MEEK113 Since 1.5, bind="obj": without a value, on <template>, an on* or data-meeko* key, a key that is not a tag attribute or component prop. Invalid keys are type errors; at runtime, an on* key throws TypeError.
MEEK114 Since 1.5, <MeekoTeleport> without to, with a static to other than "body" or "#id", with a valued disabled, with a repeated attribute (to and bind:to) or with an unsupported attribute.
MEEK115 Warning (since 1.5): a +group.ts outside app/pages, where it configures nothing.
MEEK116 Since 1.5, <MeekoAwait> without for or with an empty for.
MEEK117 Warning (since 1.5): a page or layout renders unawaited useApi or useRpcQuery data in an each:of without handling loading (<MeekoAwait>, pending, refreshing or an if:when on its data), causing layout shifts when data arrives.
MEEK118 Since 1.5, an import through @/ (or a custom alias to an app/ folder) in shared/ or a *.shared.ts, which cannot see app/; see Alias scopes.
MEEK119 Warning (since 1.7): an each:* or if:* attribute not read by any node directive (each:by alongside each:of, if:else alongside if:when) and written as a literal HTML attribute. In 1.x, generated HTML does not change.
MEEK120 Warning: the local props interface is not named Props; rename it to Props. See defineProps.
MEEK121 Warning (since 1.8): use of <MeekoPopover>, <MeekoSelect> or <MeekoThemeToggle>, deprecated and removed in 2.0. Use UIPopover, UISelect or UIThemeToggle from @meekojs/ui.
MEEK122 Warning (since 1.8): an HTML attribute value is a text literal outside the standard's accepted values for that tag (<button type="sumbit">). Becomes an error in 2.0; see Attribute values.
MEEK123 Since 1.8, a defineProps default (in the argument or destructuring) reads something depending on setup: a prop, signal, let variable, script function, a constant whose value calls a function, or the setup function's arguments, new.target or super. Variables declared by the default itself do not count. Literal constants and imports are valid; previously, any script variable gave TS2304.
MEEK124 Since 1.8, defineProps with defaults in both destructuring and the argument.
MEEK125 Since 1.8, assignment (=, ++, destructuring) or delete of a name destructured from defineProps, which is a read-only prop.
MEEK126 Warning (since 1.8) from meeko dev, meeko build, meeko check and the editor: a modules entry importing the module (modules: [ui]) or using [módulo, opciones]. Indicates the replacement string and key (meta.configKey). Both forms keep working in 1.x and are removed in 2.0; see Deprecated APIs.
MEEK127 Warning (since 1.8) when loading modules: a meeko.config.ts key that is the meta.configKey of an installed module absent from modules (i18n: { … } without '@meekojs/i18n'). Meeko ignores it, as through 1.7; the warning names the missing entry.
MEEK128 Warning (since 1.8) from meeko check and the editor: an onCleanup also executing on the server (at the top level of <script>, in an effect or in a script function called by setup, such as a composable) whose cleanup uses a browser-only API (window, document, cancelAnimationFrame, ResizeObserver…) without checking first (typeof window, import.meta.env.SSR). The editor offers Move to onMounted. Becomes an error in 2.0; see onCleanup.
MEEK129 Warning (since 1.8) from meeko check and the editor: onCleanup after an await in its own async function (onMounted(async () => { await …; onCleanup(…) })), where no active root remains. Becomes an error in 2.0; see onCleanup.
MEEK130 Warning (since 1.8) from project meeko check: onCleanup during evaluation of an app/, shared/ or server/ module (at its top level, outside a function), which throws on import. Becomes an error in 2.0; see onCleanup.
MEEK131 Since 1.8, meeko check and the editor: a nonliteral defineProps default on a prop that a variant of a props union requires with a type not accepting that value (a discriminant). A literal default makes the prop optional only in variants accepting it; a nonliteral one would make it optional in all. Previously, the parent could omit any variant's discriminant.
MEEK132 Since 1.8, compilation, meeko check and the editor: in <component is> with quoted component names, a PascalCase literal other than 'MeekoLink' or an imported, auto-imported or script-declared component (when project auto-imports are known), 'MeekoOutlet' or 'MeekoTeleport', or a nonliteral is result alongside names. See Component names in is.
MEEK133 Warning (since 1.8) from meeko check, meeko build and the editor: the same attribute as static text and with bind: (id="a" bind:id="b"), except class and style, which are combined. bind: applies, in any order, and the text is ignored; before, one order produced Hydration mismatch. See bind="obj".
MEEK134 Warning (since 1.8) from meeko check, meeko build and the editor: the previous name of a Folder/Index.meeko (<ButtonIndex>, ButtonIndexProps or resolveComponent("ButtonIndex")), which now takes the name of its folder. It keeps working in 1.x and is removed in 2.0; see A folder with Index.meeko.
MEEK135 Warning (since 1.8) from meeko check, meeko build and the editor, on a Folder/Index.meeko whose new name another file already has (Button.meeko): it keeps <ButtonIndex>, its only name. Also on a nested Index whose new name is the old name of another one (Button/Index/Index.meeko, with Button/Index.meeko): it keeps <ButtonIndexIndex>. The warning says which file keeps each name and suggests renaming or moving one of them; see A folder with Index.meeko.
MEEK136 Since 1.8, meeko check and the editor: the initial value of a top-level signal of the script or of a useState contains a component module, which cannot travel to the browser with the state. It reads the value, not the type of the signal, and a union with a member without a module does not count. At runtime, the server render TypeError carries the same code.
MEEK137 Since 1.8, meeko check and the editor: a type declaration of the script uses typeof of a setup declaration, which does not exist at module level, where types are declared. Compilation is not affected.
MEEK_PAGE_PROPS, MEEK_LAYOUT_PROPS, MEEK_ERROR_PROPS Page, layout and error page prop contracts.
MEEK_PAGE_LAYOUT_PROPS Since 1.5, the layoutProps of a +group.ts or a page inheriting its layout do not match that layout's props.
MEEK_AUTOIMPORT, MEEK_CHECK, MEEK_PROJECT_CHECK Language server: ambiguous or unavailable auto-import, component checking failure and project contract checking failure.

Codes from MEEK043 onward are new in 1.1. TypeScript errors keep their TSxxxx code, which may change when TypeScript or the shape of the code it checks is updated: in 1.1, a missing required prop on a component tag gives TS2345 instead of TS2769.

The TypeScript version included in the package may be updated in a minor release. If this changes meeko check diagnostics, the CHANGELOG states it.

Project conventions#

  • meeko.config.ts with defineMeekoConfig: extends, modules, imports.serverDirs, the server discriminated union (node, bun, deno and cloudflare, with documented fields, and since 1.3 static, with contentSecurityPolicy, headers and headerFiles) and, since 1.2, css.scopeAttribute, layers, document (lang and head) and module option keys (meta.configKey; since 1.4, a built-in MeekoConfig key makes loading fail, see Approved exceptions). Since 1.3, site, sitemap (true or { exclude }), robots (true or { allow, disallow }) and document.seo (true or { canonical, ogUrl, ogTitle, ogDescription, origin }), with the tags, files and priorities described in framework.md, dev.serverLogs, imports.appDirs and imports.sharedDirs, the latter two with the scope and path rules of Auto-import folders. Since 1.4, theme (true or { themes, default, system, darkThemes, attribute, colorScheme, cookie, storageKey, clientHints }), with Light/dark theme without flicker behavior: the <html> attribute or class, the inline <head> script and its read order (cookie, localStorage, default value). Since 1.2, modules lists defineMeekoModule modules (a module, package, path or [módulo, opciones]); extends is an alias for layers. Since 1.8, the documented form is the package name or path, with options under its meta.configKey: it is contractual that a package resolves through its ./module subpath if exported, otherwise its root; that .meeko/types/config.d.ts imports types from modules modules and .meeko/tsconfig.config.json includes meeko.config.ts with them (not their exact text); that a dependency's package.json keyword meeko-module offers it in modules completion, and that two modules with the same meta.configKey are an error (previously the same options were read twice).
  • Server-rendered HTML document: since 1.2, the title and tags managed by Meeko go between <!--meeko:head--> and <!--/meeko:head--> comments, before DocumentOptions.head HTML, and the CSRF meta follows <meta charset> and default tags (viewport and icon) not replaced by the application. The useHead priority and deduplication described in framework.md are stable.
  • app/:
    • app.meeko, error.meeko, pages/, layouts/default.meeko, middleware/, components/, composables/, utils/, stores/ and styles/main.css;
    • index.meeko, [id].meeko, [...slug].meeko pages and (group) groups, with +layout.meeko, +error.meeko and route options in <script server> or *.route.ts sidecars;
    • since 1.5, named layouts layouts/<nombre>.meeko (including subfolders: layouts/admin/panel.meeko is "admin/panel"), selected by pages through layout in definePage, and +group.ts files in pages folders, with literal export default defineGroup({ layout, layoutProps });
    • since 1.6, named middleware: a middleware/ file with defineRouteMiddleware(fn, { global: false }), named by its path without .ts or each part's numeric ordering prefix (10-auth.ts is "auth"), requested by routes through middleware (a literal array of names, or false) in definePage and defineGroup. The order (global then route middleware, from outer +group.ts files to the page, without repetition) and a missing or global name stopping discovery are contractual;
    • since 1.6, server route middleware: middleware/*.server.ts whose default export is defineServerRouteMiddleware(...) (any other default export, or none, stops discovery), global or named with the same naming rule (auth.server.ts is "auth", and named auth.ts and auth.server.ts are duplicates). It is contractual that it never reaches the browser, runs on the server after all isomorphic middleware (global then named, then server middleware in the same order), in SSR and each navigation's data request, does not run on render: "client" or "prerender" pages, and requesting a named one on those pages stops discovery.
  • server/:
    • api/ (<nombre>.<método>.ts), rpc/, socket/, middleware/ and utils/;
    • server/runtime.ts, whose default export is defineServerRuntime(...).
  • shared/, public/, layers and modules.
  • Since 1.5, the path aliases ~/ (project root), @/ (app/), #shared/ (shared/) and #server/ (server/, server code only), their scopes, lookup in the project followed by each layer, and the meeko.config.ts alias option (a prefix ending in / with a project folder or false). The precedence of project tsconfig.json paths and Vite resolve.alias over them, and a package.json imports with the same # prefix winning and disabling the alias, are contractual. The shape of paths in generated tsconfig files is not contractual.
  • Since 1.2, app/plugins/*.ts in the application and its layers, with .client.ts (browser only, before hydration) and .server.ts (server only, once per page request) suffixes.
  • The .client.ts, .server.ts and .shared.ts suffixes, and *.test.ts, *.spec.ts and *.d.ts exclusions. Since 1.6, an app/middleware/*.server.ts, ignored until 1.5, is server route middleware (see Approved exceptions).
  • The api, rpc, socket, useSocket, useState, href and provideErrorHandler auto-imports, and since 1.1 onMounted, useApi, useRpcQuery and useRpcMutation, since 1.2 useRoute, useForm, useStorage, useApiMutation, useHead and useSeoMeta, since 1.4 useTheme, since 1.5 useId (from @meekojs/meeko), useLayout, defineGroup and ClientError and isClientError (only in components and app/ modules, not in server/, *.server.ts or route files), since 1.6 defineServerRouteMiddleware (in *.server.ts and all server/, like server definitions), and since 1.7 useNavigation, createRoot and defer (the latter in server code: <script server>, including inside route options, *.route.ts sidecars, server/ and *.server.ts) (a custom export with one of these names takes precedence over the framework's). Since 1.4, each auto-imported component's types: its tag name (the app/components and addComponentsDir naming rule, prefix included) followed by Props, Snippets, Models and Exposed (AppCardProps…), identical to its module's ComponentProps, ComponentSnippets, ComponentModels and ComponentExposed, including null, with the component's scope; a project auto-import with the same name takes precedence (see Each component's types). In top-level <script server> code, usePage, redirect and notFound can be used without imports, and since 1.2 so can useHead and useSeoMeta.
  • The MEEKO_HOST, MEEKO_PORT (with PORT as a permanent fallback), MEEKO_PUBLIC_ORIGIN, MEEKO_HMR_PORT environment variables and, since 1.2, MEEKO_REDIS_URL, MEEKO_REDIS_PREFIX, MEEKO_SESSION_TTL, MEEKO_SESSION_SAMESITE and MEEKO_TRUST_PROXY, and since 1.8 the cloudflare value of MEEKO_TRUST_PROXY and MEEKO_TRUSTED_PROXIES (see Cloudflare Tunnel and Traefik), and the MEEKO_ prefix, reserved for variables Meeko reads. HOST and PUBLIC_ORIGIN are deprecated since 1.2.

Build outputs#

Adapter Folder Runs with
Node .meeko/output/node/ index.mjs, through meeko start or node
Bun .meeko/output/bun/ index.js
Deno .meeko/output/deno/ main.js
Cloudflare Workers .meeko/output/cloudflare/ worker.js and wrangler.jsonc
Static site (since 1.3) .meeko/output/static/ Any static hosting, or meeko preview

Also stable in the static site:

  • <ruta>/index.html and <ruta>/index.meeko.json per page, 404.html, sitemap.xml and robots.txt at the root; since 1.7, a sitemap.xml <url> may carry <lastmod>, <changefreq> and <priority> (in that order, after <loc>), in all adapters;
  • _headers (/* block) and _meeko/headers.conf (one add_header <Nombre> "<valor>" always; line per header), which conflict with a same-named public/ file.

Also stable in Cloudflare:

  • the MEEKO_SOCKET binding;
  • the meeko-socket-v1 migration, which is not renamed;
  • the reservation of public/_meeko/;
  • wrangler.jsonc fields belonging to Meeko.

The contents of chunks/, hashed filenames and the rest of .meeko/ are not public.

Network and persisted formats#

These formats support external clients, proxies, CSP and rolling deployments:

  • Page payload. Version v: 2, with render, head (title and, since 1.1, description) and deferred:
    • the document carries it in <script id="meeko-payload" type="application/json">, inside HTML with <div id="app">;
    • navigation requests it with accept: application/meeko+json, and the response is application/meeko+json or, on a "stream" route, application/meeko+ndjson (payload on the first line and a { "deferred", "value" | "error" } value on each subsequent line); since 1.3, the static adapter client requests it as the <ruta>/index.meeko.json file, without accept;
    • since 1.3, a document with an error: true payload hydrates app/error.meeko at any URL, including one matching a route (a static host's 404.html);
    • with app/error.meeko, a URL without a route responds 404 with an error: true payload, without route and with { status, message } props;
    • since 1.1, the optional resources field carries data from await useApi(...) and await useRpcQuery(...) read by rendering, as an object keyed by request. Those keys' format is internal and may change in any version: only server and browser of the same version interpret it. A payload without resources remains valid;
    • since 1.5, the optional layoutProps field carries props used by the server to render the page's named layout (from its +group.ts files, its layoutProps and anything written with useLayout()), and the route manifest (RouteRecord, ErrorPageRecord, .meeko/manifests/routes.json and that of virtual:meeko-manifest/client) carries the optional layout field ({ name, file, props? }). A payload or manifest without them remains valid. Since 1.6, the route manifest gains the optional middleware field, with the route's named middleware files in execution order, and the optional serverMiddleware field, with the full server middleware chain, global then named (the virtual:meeko-manifest/client manifest does not carry it); a manifest without them remains valid: its route executes global isomorphic middleware and no server middleware.
    • since 1.6, the optional url field carries the URL the document was rendered for (pathname and search, without origin or fragment); the browser hydrates useRoute() with it and then takes its own. A payload without url remains valid and hydrates with the browser URL.
  • Streaming. The <script type="application/json" data-meeko-deferred="N"> blocks, direct children of <body> after #meeko-payload.
  • Endpoints. /api/... according to server/api files, POST /_meeko/rpc/<ruta> with a JSON body and /_meeko/socket/<canal>?room=<sala>, whose text messages are { event, payload }. Errors have the { code, status, requestId } shape; since 1.2, a 400 for invalid input may add issues: [{ path, message }], and rateLimit responds 429 RATE_LIMITED with Retry-After in seconds. Since 1.2, retrySafe mutations and api.* and rpc.* calls with idempotencyKey send the Idempotency-Key header with a UUID as a Structured Fields string ("…"), and the server exposes it validated in RequestContext.idempotencyKey.
  • Native forms. Since 1.7, an endpoint with form accepts application/x-www-form-urlencoded and multipart/form-data with reserved return and _csrf fields, and responds 303 See Other (except 403 INVALID_ORIGIN or INVALID_CSRF, with the usual error body). The new INVALID_FORM and FILES_NOT_ALLOWED (400) codes reach the page in the result.
  • Headers:
    • x-meeko-csrf on mutations;
    • x-meeko-csrf-token and x-request-id on responses;
    • cache-control: no-store and x-accel-buffering: no on streams;
    • since 1.7, on a page with cache: weak ETag (W/"…"), Cache-Control: public, max-age=… (with no-transform on a stream), Vary: Accept, Cookie and 304 for If-None-Match.
  • Meta and cookie. The meeko-csrf-token meta and session cookie, defaulting to __Host-meeko.sid (or meeko.sid without Secure) and configured with cookieName. Since 1.4, with theme, the theme cookie (meeko-theme by default, theme.cookie) and localStorage key (theme.storageKey) store the theme name or system as plain text. Since 1.7, the native forms' single-use cookie is named __Host-meeko-form (meeko-form without HTTPS), is HttpOnly and SameSite=Lax and lasts 60 seconds; its contents and the sharedState meeko:form:<ruta> key carrying it to the browser are internal: only server and browser of the same version interpret them.
  • Theme. Since 1.4, with theme, server-rendered documents carry Vary: Cookie, Sec-CH-Prefers-Color-Scheme, Accept-CH: Sec-CH-Prefers-Color-Scheme and, when the choice is system, Critical-CH: Sec-CH-Prefers-Color-Scheme (without clientHints, only Vary: Cookie). A page payload reading the theme carries what the server rendered in the sharedState meeko:theme key.
  • Socket.
    • Documented close codes: 1003, 1008, 1009, 1011 and 1013, and since 1.1 4401 (session rejected) and 4403 (channel authorization rejected).
    • Handshake responses: 400, 403 INVALID_ORIGIN, 404 and HTTP limit responses. Since 1.1, a session (401) or channel authorization (403) rejection completes the upgrade and immediately closes with 4401 or 4403, with a fixed reason and without calling handlers. See framework.md.
  • Redis:
    • the <prefix>socket:<canal>:<sala> channel with the { v: 1, event, payload } envelope;
    • the <prefix>revoked revocation channel;
    • since 1.2, rateLimit counters in <prefix>ratelimit:<resumen> (the route and subject digest is not contractual);
    • session records written by RedisSessionStore and SqlSessionStore;
    • the D1SessionDriver.schema() reference schema and, since 1.7, that of D1SessionDriver.rateLimitSchema();
    • since 1.7, SQL rateLimit counter columns (key, window_ends_at and count) in the framework.md PostgreSQL reference schema. The counter key from which key is derived is not contractual: if it changes, counters restart.
  • Prerendering. The prerender/manifest.json index ({ version: 1, pages }); since 1.3, each page has the optional scriptHashes field, the CSP 'sha256-…' sources of its inline scripts, and since 1.4 styleHashes, those of its inline <style> elements.

A minor may only add optional fields to these formats. It must accept anything written by any earlier 1.x and does not change a format's version. During a rolling deployment, instances of two minors may coexist with the same Redis or database. An application's browser and server must come from the same build; a payload with another version is rejected with an explicit message.

DevTools and editor#

  • DevTools protocol 1.
    • The window.__MEEKO_DEVTOOLS__ hook (development only), with protocolVersion: 1.
    • Its snapshot, subscribe, select, highlight, setBreakpoint and setSignal methods.
    • Root Devtools* types.
    • A minor may add fields or event types. Moving to another protocol version requires a major. The Chrome extension maintains its own version number.
    • Since 1.1, DevtoolsInstance has the optional signalSites field (DevtoolsSignalSite type), with each signal's declaration. It is a field added within protocol 1: a hook or snapshot without it remains valid.
  • meeko.config.ts devtools option (since 1.8, additive). devtools: boolean | { panel?: boolean; position?: "bottom-right" | "bottom-left" | "top-right" | "top-left" } (MeekoDevtoolsConfig type) and mountDevtoolsPanel position (DevtoolsPanelPosition type). Defaults to false throughout 1.x: without the option, nothing changes. The panel exists only in meeko dev; never reaching meeko build, meeko start or a prerendered page is part of the contract. The panel's appearance, Alt+Shift+M shortcut and localStorage meeko:devtools-panel key are outside the contract and may change in a minor.
  • Language server.
    • Starting with meeko lsp --stdio or meeko-language-server.
    • The trusted, projectContracts and diagnosticsDelay initializationOptions.
    • The standard LSP capabilities it advertises. A minor may add capabilities and quick fixes.

Outside the contract#

  • @meekojs/meeko/internal. This is the contract between compiler and runtime. It is guaranteed only within the same exact version and may change in any patch. Do not import it manually. Since 1.5, the same applies to @meekojs/meeko/builtins/*, the compiled <MeekoThemeToggle>, <MeekoAwait>, <MeekoPopover> and <MeekoSelect> modules the compiler imports into components using those tags.
  • Generated code and files.
    • Everything under .meeko/: types, manifests, entries, per-scope tsconfig files, intermediate builds and chunks/.
    • The contents of .meeko/styles/main.css, the processed global stylesheet meeko dev writes for the editor: it follows what Vite and PostCSS generate in development and may change in any version. Its path does remain throughout 1.x, so editor configuration naming it (css.styleSheets) keeps working. See framework.md.
    • Vite virtual modules (virtual:meeko-*).
    • JavaScript emitted by the compiler.
    • A custom entry importing these modules must pin the exact Meeko version.
  • The editor bridge. The live-value protocol between browser, meeko dev and editor (HMR meeko:live* events, .meeko/dev/editor-bridge-<puerto>.json descriptors, one per development server, the /__meeko/editor-bridge route and its messages, including the alias field) is development-only and outside the public contract: it may change in any version. The VS Code extension ignores a descriptor with another protocol version. See devtools.md.
  • Server logs in the browser. What meeko dev adds to forward server console output (since 1.3): <script type="application/json" data-meeko-server-logs> blocks, the devServerLogs field of navigation payloads, the HMR meeko:server-logs event, its /@id/__x00__meeko:server-logs* modules and the serialized value shape. They exist only in development, never in meeko build or meeko start, and may change in any version. The stable part is the dev.serverLogs option. See framework.md.
  • Custom language server extensions. The meeko/executionZones LSP request and Meeko's custom semantic token modifiers (signal, computed, resource and reactive; modification is standard LSP) are internal to the VS Code extension, published alongside the server. They are not public contract: they may change or disappear in a minor. Another LSP client may use them but must tolerate those changes. See editors.md.
  • Presentation. Error and diagnostic message text, DevTools panel HTML and styles, editor CodeLens, hover and zone titles, console output and log formatting.
  • The repository. Examples, tests, benchmarks and scripts are not published to npm.
  • Performance. Performance and size figures, which may improve or vary between versions.

Deprecation#

  1. A public API is deprecated only in a minor release and always with a documented replacement, unless the functionality disappears completely; in that case, the reason is explained.
  2. Deprecation is announced in the CHANGELOG, documentation and with @deprecated in JSDoc so the editor marks it.
  3. Where feasible, deprecated use also emits a warning:
    • a meeko check or language server warning for syntax and conventions;
    • a development console warning, once per process, for runtime APIs.
  4. Deprecated features keep working throughout 1.x and are only removed in the next major, whose migration guide covers them.

Deprecated APIs#

The theme system (theme, cookie, <head> script and icon rule, useTheme, useThemeToggle and POST /_meeko/theme) remains in core. Only the three tags are removed; the 1.8 guide explains their replacements.

API Since Replacement Removed in
<MeekoPopover> (warning MEEK121) 1.8 <UIPopover> from @meekojs/ui 0.2, with its module enabled 2.0
<MeekoSelect> (warning MEEK121) 1.8 <UISelect> from @meekojs/ui 0.2, with its module enabled 2.0
<MeekoThemeToggle> (warning MEEK121) 1.8 <UIThemeToggle> from @meekojs/ui 0.2, with its module enabled 2.0
The name <FolderIndex> of a Folder/Index.meeko and its types (warning MEEK134) 1.8 <Folder> and its types 2.0
useSocket(url, contract, options) from @meekojs/meeko/framework 1.1 useSocketClient, with the same signature and types 2.0
byId segments of api.* and ApiClient (api.users.byId.get({ params: { id } })) 1.2 Calling the dynamic segment: api.users({ id }).get() 2.0
An imported module (modules: [ui]) or [módulo, opciones] in meeko.config.ts modules (warning MEEK126) 1.8 The package name or path (modules: ['@meekojs/ui']) and options under its meta.configKey (ui: { … }) 2.0
Layer folders (with app/pages and no module) listed in meeko.config.ts modules 1.2 The same string in layers 2.0
HOST and PUBLIC_ORIGIN environment variables 1.2 MEEKO_HOST and MEEKO_PUBLIC_ORIGIN. PORT is not deprecated 2.0
html="..." with an unbranded string (warning MEEK107) 1.3 A TrustedHtml: your own or sanitized HTML, branded with trustHtml(…) where produced; for text, {expr} 2.0, as a meeko check error
A literal outside an HTML attribute's values (warning MEEK122) 1.8 One of the standard's values (AttributeValues<Tag, Name>), or a computed string 2.0, as a meeko check error
definePage in a component's <script> or template, and value imports from @meekojs/meeko/node, /bun, /deno, /password or /build there (warning MEEK109) 1.3 Route options in <script server> or a *.route.ts sidecar; server code in <script server> or server/, with import type for types 2.0, as a meeko check and build error
onCleanup with browser APIs in code also running in SSR, after an await or during module evaluation (warnings MEEK128, MEEK129 and MEEK130) 1.8 Cleanup inside onMounted (or guarded with typeof window), registered before the first await, and inside a composable or createRoot 2.0, as a meeko check error

The useSocket alias is the same function as useSocketClient, so it emits no console warning: the editor marks it through @deprecated. This does not affect the generated clients' useSocket("canal", opciones) auto-import, which is not deprecated. See framework.md.

byId segments construct the same requests as calling the segment and also emit no console warning: the editor strikes through each method called through them. The manifest's clientKey, createLocalCalls().api("users.byId", ...) and useApi do not change and are not deprecated. See framework.md.

An imported module or [módulo, opciones] in modules still loads the same way, with inline options merged after config[meta.configKey]; meeko dev, meeko build and meeko check write MEEK126 once per process and entry, and the editor marks it in meeko.config.*. A module without meta.configKey only receives options through [módulo, opciones]: its author must declare one before 2.0. @meekojs/i18n/module (and @meekojs/meeko/i18n/module) are not deprecated: they are the subpath resolved by '@meekojs/i18n'; only importing them in meeko.config.ts is deprecated. See Upgrade from 1.7 to 1.8.

A layer folder listed in modules still applies as a layer, in the same position as before, and meeko dev, meeko build and meeko check write a warning once per process. extends is not deprecated: it is an alias for layers. See framework.md.

MEEK128, MEEK129 and MEEK130 are only meeko check and editor warnings: in 1.x, they do not change SSR, hydration or generated code, and meeko check does not fail because of them. MEEK128 resolves names with the TypeScript checker (a local window variable is not the global) and does not warn if cleanup, or its registration, is guarded by typeof <global> or import.meta.env.SSR, or inside onMounted, an event handler or the continuation after the first browser await. In 2.0, all three will be errors.

MEEK122 is only a meeko check, meeko build and editor warning: in 1.x, it does not change SSR, hydration or generated code, and the value is written the same way. In 2.0, a literal outside AttributeValues<Tag, Name> in an attribute with a closed set of values will be an error; a computed string will remain accepted.

MEEK107 is only a meeko check, meeko build and language server warning: in 1.x, it does not change SSR, hydration or generated code, and an unbranded string is still inserted the same way. In 2.0, the html value will have to be assignable to TrustedHtml | null | undefined and the warning will become an error. Text literal types (html="'<br>'") remain accepted. See api.md.

MEEK109 is only a meeko check, meeko build and language server warning, and generated code does not change in 1.x. These uses compile and load in the browser, but do nothing (definePage outside <script server> and sidecars does not configure the route) or fail when called there (adapter, /password and /build functions need Node, Bun or Deno). What already failed before 1.3 is directly the MEEK108 error: a server auto-import did not resolve in the browser bundle, @meekojs/meeko/server and /cloudflare cannot load there (they create a node:async_hooks AsyncLocalStorage), a server/ import stopped meeko build, and macros and onMounted do not exist outside <script>. See framework.md.

HOST and PUBLIC_ORIGIN are read only when MEEKO_HOST or MEEKO_PUBLIC_ORIGIN is missing, and then meeko dev, start, preview and meeko build outputs write a warning once per process naming the new variable. See framework.md.

A vulnerability that cannot be fixed compatibly is the only exception. In that case, the minimum necessary change may ship in a patch or minor release, with its explanation and migration in the CHANGELOG.

Planned changes for 2.0#

Besides removing deprecated features, 2.0 will change these defaults. In 1.x, you can enable each early with its option.

Behavior Option Since Default in 1.x Default in 2.0
Elements receiving the <style scoped> attribute css.scopeAttribute in meeko.config.ts (scopeAttribute in compile()) 1.2 "all": all template elements "matched": only those a scoped selector can match

With "matched", a class or attribute the script adds through the DOM API to an element without ref: or on: stops receiving scoped styles if that element was left without the attribute. See api.md.

Supported environments#

Environment Version
Node.js >=24 (package.json engines). Runs the CLI, build, prerendering, Vite, language server and Node adapter. CI uses Node 24 on Windows and Ubuntu.
Bun Bun 1.x, with meeko build --adapter bun output. The conformance suite runs in CI with Bun 1.4.4.
Deno Deno 2.x, with meeko build --adapter deno output. The conformance suite runs in CI with Deno 2.9.8.
Cloudflare Workers wrangler.jsonc defaults to compatibility date 2026-10-19, configurable with server.compatibilityDate, and the nodejs_als flag (nodejs_compat also works). The conformance suite runs with workerd 1.20261019.1 (Wrangler 4.152.0). Socket requires Durable Objects, and sessions require D1 or Redis through the Upstash REST API.
TypeScript The package includes TypeScript 7.0.2 for meeko check and types. Applications check declarations with TypeScript 7.
Vite Optional peer ^8.3.5.

The table's Bun, Deno and workerd versions are the tested ones. They may be updated in a minor release and the CHANGELOG states it. Dropping Node 24 support requires a major.

Browsers#

The runtime is published compiled to ES2022, without polyfills. It uses, among others, Object.hasOwn, Array.prototype.at, MutationObserver, fetch with ReadableStream bodies (navigation to "stream" routes) and WebSocket (Socket). An application's client bundle uses Vite 8's default target, baseline-widely-available. Browsers meeting that target are supported.

E2E tests run with Chrome and Playwright's Chromium, Firefox and WebKit. Playwright WebKit is not equivalent to testing real Safari.

Security#

Report vulnerabilities privately through the Security tab of meekojs/meeko, never in a public issue. SECURITY.md details supported versions, what to include in the report and response timelines.

ESLint#

During Meeko 1.x, defineEslint sorts only imports by default, with perfectionist/sort-imports and perfectionist/sort-named-imports, warn level, autofix and ascending natural order. In 2.0, those two rules will become error. perfectionist: true also enables the recommended-natural set except sort-classes and sort-modules; that list is decided by the Perfectionist version the plugin pins. You can disable them all with perfectionist: false, turn one off with off or require error through rules; see Sort with Perfectionist, which also lists the cases where --fix changes behavior. Warnings can block CI using --max-warnings 0.

Was this page helpful?

Add a comment

It is sent when you choose “Yes” or “No”. Only the page, your answer and the comment are stored: no cookies and no personal data.

0 of 2000 characters

Edit on GitHub: docs/stability.md