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"in1.3),PageRender,DeferredStatus,DevtoolsReadKind,DevtoolsEventKind, aSocketClient's state (which gains"unauthorized"in1.1) or error codes. An exhaustiveswitchover 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 byuseRoute()(gainsisActivein1.5),DataResourceandLoadedResource, fromuseApiand resources (gainrefreshingin1.5), orForm(gainstrySubmit,commitandfieldin1.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 withPartial<RouteState>or anas RouteStateassertion 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 (withscopedand:global(...)), and on pages<script server lang="ts">(since1.1), with its two forms: route options and top-level code whoseexport constdeclarations are page data, with the rule deciding when a block has top-level code. Since1.1,<script lang="ts">is optional and accepts thegenericattribute with the component's type parameters, which is not allowed on pages, layouts,app.meekoorerror.meeko. - The
defineProps<Props>()macro, with or without defaults, and since1.1thedefineEmits<T>(),defineExpose(value),defineModel<T>(name?, options?),defineSnippets<T>()anduseElementRef("name")macros. - The
{expression},on:event,bind:name,if:when,each:of/each:as/each:key/each:indexand<component is>directives. Since1.1:each:keyis optional and without it the key is the index;on:eventon<component>and component tags is equivalent to theonEventprop;- 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 passestrue), withis,ref,html, hyphenated names and names containing:without being directives reserved (since1.8, exceptaria-*anddata-*); - the
ref:namedirective.ref="x"andbind:refretain their1.0behavior; modelandmodel:nameon component tags,modelon<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; htmlandmodelare 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 giveMEEK001, and a self-closed table or list element followed by its own closing tag (<td /></td>) givesMEEK008(see Elements without content). - Since
1.4,if:whenon<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 withto("body"or"#id", the latter with a letter followed by ASCII letters, digits,-or_),bind:to,disabledandbind:disabled, and theMeekoTeleportPropsandMeekoTeleportTargettypes from@meekojs/meeko/framework. The content's destination in server HTML (inside theidelement, after its content, or after the application and before Meeko scripts withbody), ordering of multiple teleports to the same target, retaining the declaring component's scope, moving the same nodes whendisabledchanges 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 publicTagNametype; a non-global attribute is written only while the current tag supports it according to Meeko HTML data (notypeon<a>or<area>). When the tag changes, children move to the new element with their state. Thebind="obj"directive writes an object as element attributes or component props, withouton*keys; explicit attributes win andclassandstyleare combined.bindremains a literal attribute on custom elements and is no longer a static prop. - Since
1.4,defineSnippets<T>accepts a parameter tuple orSnippet<Args>in each key ofT, counting asArgs, soTcan derive from another component's snippet map withOmit,Pickor&while preserving parameters and optionality. A tuple declaration retains the same type. - Since
1.8,<snippet>nameaccepts, besides an identifier, identifier-character segments joined by hyphens (/^[A-Za-z_$][\w$]*(?:-[\w$]+)*$/, such ascell-email), anddefineSnippets<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 thatmeeko checkand the editor check these snippets in the parent with the type inferred from its props (nonexistent name, each parameter's type and number ofparams), 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, withthemeinmeeko.config.ts, the built-in<MeekoThemeToggle>tag (deprecated since1.8, removed in2.0; useUIThemeTogglefrom@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">withdata-meeko-theme-toggle, themode="cycle"<button>, icon<span data-meeko-theme-icon>elements,role="radiogroup"and itsrole="radio"buttons witharia-checkedanddata-meeko-theme-optioninmode="select"), the<html>data-theme-choiceattribute with the choice, the<head>rule showing the chosen icon, and thePOST /_meeko/themeendpoint with the framework.md rules (valid themes, same-siteOrigin,303only 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 withbind:for,reserve,messages,class,id), snippets (defaultwithrefreshing,pendinganderrorwitherrorandretry), states described in framework.md, narrowing offordata in its content, and application-styled markup: a<div>withdata-meeko-await(pending,errororready),aria-busy="true"while loading or refreshing,data-meeko-refreshingduring refresh and, withreserve,data-meeko-reserveanddata-meeko-reserve-min. It is contractual that reservation appliesmin-block-sizeonly inpending, withoutstyleattributes, and that persistence lives insessionStorage; 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 since1.8, removed in2.0; useUIPopoverandUISelectfrom@meekojs/ui) (see Built-in components): their props, snippets (trigger,anchorand content;option,value,group,emptyandloading), model, events (togglewith{ open },change) and exposed members (show,hide,toggle); application-styled markup (data-meeko-popoveranddata-meeko-popover-trigger,data-meeko-selectwithdata-open,data-multiple,data-disabledanddata-loading,role="option"options withdata-active,data-selectedanddata-disabled); documented ARIA roles and attributes; documented keyboard behavior; thename<input type="hidden">elements, and not usingstyle="…"attributes. Themeekocascade layer rules,--meeko-popover-*custom properties, default text and ID shapes are implementation details and may change in a minor. - Since
1.8, a staticclassorstylealongsidebind:classorbind:styleon 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 dynamicstyledeclarations follow and override static ones. If the dynamic value isundefined,null,falseor empty, static text stays as-is. Previously SSR wrote the attribute twice and hydration failed. - Since
1.8, lowercasearia-*anddata-*, static or withbind:, on a component tag or a<component is>that can only be a component pass a prop with that name (previouslyMEEK032);data-meeko*and uppercase names still produceMEEK032. This only makes previously uncompilable code valid. - Since
1.8, when the script writesdefineEmitsevents (a type literal or localinterfaceortypewithoutextendsor parameters),propsdoes not enumerate their callbacks (onSave) inObject.keys,in, spreading or rest destructuring; reading them (props.onSave) andemitdo not change. Thepropstype 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: oneif:whenregion 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 gaveMEEK112(not a tag), andiswith a component value or HTML tags is unchanged. An unknown name or a value alongside names givesMEEK132. Region and marker distribution is an implementation detail.resolveComponent("MeekoLink")and theMeekoLinkentry inComponentRegistry(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:viewBoxwritesviewBox, notviewbox); previously, the lowercase attribute had no effect on SVG and hydration did not match. - Since
1.8,<MeekoLink>with a route-unionbind:toacceptsbind:paramswith parameters of any of them, or none, andbind:searchwithundefined; checking for a single route is unchanged. - Since
1.8,definePropscan be destructured in itsconstdeclaration (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;...restis a read-only reactive view of the remaining props, excludingdefineEmitshandlers. Defaults (in both forms) can read top-level constants using only literals and imports. This is additive: it only makes code previously producingMEEK015orTS2304valid. Default types are computed per variant of a props union, so a default for a prop one variant declaresneverno longer allows it in that variant for the parent. - Since
1.8, with a props union, a literaldefinePropsdefault (kind = "a", in the pattern or argument) makes its prop optional for the parent only in variants accepting that value, inComponentProps,.d.meeko.tsdeclarations and the editor; a nonliteral default stays optional in all variants and givesMEEK131if one requiring the prop does not accept it. This is a fix: previously a parent could omit any variant's discriminant (<Child b="hello" />withkind = "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 byscripts/html-data.ts): tags and attributes accepted by<component is>andbind="obj"are a superset of1.7's. The publicAttributeValues<Tag, Name>andAttributeValueOf<Tag, Name>types (from@meekojs/meeko), andElementAttributes<Tag>values, suggest standard values without restricting acceptance; a literal outside them gives warningMEEK122.hiddenwith nonempty text ("until-found") writes that text, and standard boolean attributes not treated as such by1.7are written empty. - Setup rules since
1.1:useElementRef,useApi,useRpcQueryanduseRpcMutationonly at the top level; top-levelawaitonly onuseApi,useRpcQuery,useElementRef,useSocketanduseSocketClient; splitting setup at the first browserawait, whose continuation runs only in the browser and accepts expression and control flow statements. - Each compiled module's shape:
ComponentProps,ComponentExposed,ComponentModelsandComponentSnippets(since1.1),css(with the Meeko Vite plugin, since1.8it depends on the graph: in the SSR graph —meeko dev, Node build, prerendering,staticand Cloudflare— and themeeko devclient, 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 before1.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 withouttest.css: true, it is also compiler output. Withcompile()withoutstyleModule, it is always compiler output),renderWithState,renderToString,hydrateandhydrateScope, whose disposal function carriesexposed. In a component withgeneric, types and functions carry its type parameters. ThehasDataAwait,__meekoChildren,__meekoReactiveProps(since1.2),__meekoChannelsand__meekoTagflags 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 fromaddStylesheetanddocumenthead), 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. Thedata-vite-dev-idattributemeeko devadds 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-serverbinary, with the same transports asmeeko lsp; - exit code
1on errors; - diagnostic codes (
MEEK0xx,MEEK1xxandMEEK_*; see Diagnostic codes); - the
{ code, message, filename, location }shape ofmeeko 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.tswithdefineMeekoConfig:extends,modules,imports.serverDirs, theserverdiscriminated union (node,bun,denoandcloudflare, with documented fields, and since1.3static, withcontentSecurityPolicy,headersandheaderFiles) and, since1.2,css.scopeAttribute,layers,document(langandhead) and module option keys (meta.configKey; since1.4, a built-inMeekoConfigkey makes loading fail, see Approved exceptions). Since1.3,site,sitemap(trueor{ exclude }),robots(trueor{ allow, disallow }) anddocument.seo(trueor{ canonical, ogUrl, ogTitle, ogDescription, origin }), with the tags, files and priorities described in framework.md,dev.serverLogs,imports.appDirsandimports.sharedDirs, the latter two with the scope and path rules of Auto-import folders. Since1.4,theme(trueor{ 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). Since1.2,moduleslistsdefineMeekoModulemodules (a module, package, path or[módulo, opciones]);extendsis an alias forlayers. Since1.8, the documented form is the package name or path, with options under itsmeta.configKey: it is contractual that a package resolves through its./modulesubpath if exported, otherwise its root; that.meeko/types/config.d.tsimports types frommodulesmodules and.meeko/tsconfig.config.jsonincludesmeeko.config.tswith them (not their exact text); that a dependency'spackage.jsonkeywordmeeko-moduleoffers it inmodulescompletion, and that two modules with the samemeta.configKeyare 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, beforeDocumentOptions.headHTML, and the CSRF meta follows<meta charset>and default tags (viewport and icon) not replaced by the application. TheuseHeadpriority and deduplication described in framework.md are stable. app/:app.meeko,error.meeko,pages/,layouts/default.meeko,middleware/,components/,composables/,utils/,stores/andstyles/main.css;index.meeko,[id].meeko,[...slug].meekopages and(group)groups, with+layout.meeko,+error.meekoand route options in<script server>or*.route.tssidecars;- since
1.5, named layoutslayouts/<nombre>.meeko(including subfolders:layouts/admin/panel.meekois"admin/panel"), selected by pages throughlayoutindefinePage, and+group.tsfiles inpagesfolders, with literalexport default defineGroup({ layout, layoutProps }); - since
1.6, named middleware: amiddleware/file withdefineRouteMiddleware(fn, { global: false }), named by its path without.tsor each part's numeric ordering prefix (10-auth.tsis"auth"), requested by routes throughmiddleware(a literal array of names, orfalse) indefinePageanddefineGroup. The order (global then route middleware, from outer+group.tsfiles to the page, without repetition) and a missing or global name stopping discovery are contractual; - since
1.6, server route middleware:middleware/*.server.tswhose default export isdefineServerRouteMiddleware(...)(any other default export, or none, stops discovery), global or named with the same naming rule (auth.server.tsis"auth", and namedauth.tsandauth.server.tsare 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 onrender: "client"or"prerender"pages, and requesting a named one on those pages stops discovery.
server/:api/(<nombre>.<método>.ts),rpc/,socket/,middleware/andutils/;server/runtime.ts, whose default export isdefineServerRuntime(...).
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 themeeko.config.tsaliasoption (a prefix ending in/with a project folder orfalse). The precedence of projecttsconfig.jsonpathsand Viteresolve.aliasover them, and apackage.jsonimportswith the same#prefix winning and disabling the alias, are contractual. The shape ofpathsin generatedtsconfigfiles is not contractual. - Since
1.2,app/plugins/*.tsin 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.tsand.shared.tssuffixes, and*.test.ts,*.spec.tsand*.d.tsexclusions. Since1.6, anapp/middleware/*.server.ts, ignored until1.5, is server route middleware (see Approved exceptions). - The
api,rpc,socket,useSocket,useState,hrefandprovideErrorHandlerauto-imports, and since1.1onMounted,useApi,useRpcQueryanduseRpcMutation, since1.2useRoute,useForm,useStorage,useApiMutation,useHeadanduseSeoMeta, since1.4useTheme, since1.5useId(from@meekojs/meeko),useLayout,defineGroupandClientErrorandisClientError(only in components andapp/modules, not inserver/,*.server.tsor route files), since1.6defineServerRouteMiddleware(in*.server.tsand allserver/, like server definitions), and since1.7useNavigation,createRootanddefer(the latter in server code:<script server>, including inside route options,*.route.tssidecars,server/and*.server.ts) (a custom export with one of these names takes precedence over the framework's). Since1.4, each auto-imported component's types: its tag name (theapp/componentsandaddComponentsDirnaming rule, prefix included) followed byProps,Snippets,ModelsandExposed(AppCardProps…), identical to its module'sComponentProps,ComponentSnippets,ComponentModelsandComponentExposed, includingnull, 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,redirectandnotFoundcan be used without imports, and since1.2so canuseHeadanduseSeoMeta. - The
MEEKO_HOST,MEEKO_PORT(withPORTas a permanent fallback),MEEKO_PUBLIC_ORIGIN,MEEKO_HMR_PORTenvironment variables and, since1.2,MEEKO_REDIS_URL,MEEKO_REDIS_PREFIX,MEEKO_SESSION_TTL,MEEKO_SESSION_SAMESITEandMEEKO_TRUST_PROXY, and since1.8thecloudflarevalue ofMEEKO_TRUST_PROXYandMEEKO_TRUSTED_PROXIES(see Cloudflare Tunnel and Traefik), and theMEEKO_prefix, reserved for variables Meeko reads.HOSTandPUBLIC_ORIGINare deprecated since1.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.htmland<ruta>/index.meeko.jsonper page,404.html,sitemap.xmlandrobots.txtat the root; since1.7, asitemap.xml<url>may carry<lastmod>,<changefreq>and<priority>(in that order, after<loc>), in all adapters;_headers(/*block) and_meeko/headers.conf(oneadd_header <Nombre> "<valor>" always;line per header), which conflict with a same-namedpublic/file.
Also stable in Cloudflare:
- the
MEEKO_SOCKETbinding; - the
meeko-socket-v1migration, which is not renamed; - the reservation of
public/_meeko/; wrangler.jsoncfields 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, withrender,head(titleand, since1.1,description) anddeferred:- 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 isapplication/meeko+jsonor, on a"stream"route,application/meeko+ndjson(payload on the first line and a{ "deferred", "value" | "error" }value on each subsequent line); since1.3, thestaticadapter client requests it as the<ruta>/index.meeko.jsonfile, withoutaccept; - since
1.3, a document with anerror: truepayload hydratesapp/error.meekoat any URL, including one matching a route (a static host's404.html); - with
app/error.meeko, a URL without a route responds404with anerror: truepayload, withoutrouteand with{ status, message }props; - since
1.1, the optionalresourcesfield carries data fromawait useApi(...)andawait 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 withoutresourcesremains valid; - since
1.5, the optionallayoutPropsfield carries props used by the server to render the page's named layout (from its+group.tsfiles, itslayoutPropsand anything written withuseLayout()), and the route manifest (RouteRecord,ErrorPageRecord,.meeko/manifests/routes.jsonand that ofvirtual:meeko-manifest/client) carries the optionallayoutfield ({ name, file, props? }). A payload or manifest without them remains valid. Since1.6, the route manifest gains the optionalmiddlewarefield, with the route's named middleware files in execution order, and the optionalserverMiddlewarefield, with the full server middleware chain, global then named (thevirtual:meeko-manifest/clientmanifest does not carry it); a manifest without them remains valid: its route executes global isomorphic middleware and no server middleware. - since
1.6, the optionalurlfield carries the URL the document was rendered for (pathnameandsearch, without origin or fragment); the browser hydratesuseRoute()with it and then takes its own. A payload withouturlremains valid and hydrates with the browser URL.
- the document carries it in
- Streaming. The
<script type="application/json" data-meeko-deferred="N">blocks, direct children of<body>after#meeko-payload. - Endpoints.
/api/...according toserver/apifiles,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; since1.2, a400for invalid input may addissues: [{ path, message }], andrateLimitresponds429RATE_LIMITEDwithRetry-Afterin seconds. Since1.2,retrySafemutations andapi.*andrpc.*calls withidempotencyKeysend theIdempotency-Keyheader with a UUID as a Structured Fields string ("…"), and the server exposes it validated inRequestContext.idempotencyKey. - Native forms. Since
1.7, an endpoint withformacceptsapplication/x-www-form-urlencodedandmultipart/form-datawith reservedreturnand_csrffields, and responds303 See Other(except403INVALID_ORIGINorINVALID_CSRF, with the usual error body). The newINVALID_FORMandFILES_NOT_ALLOWED(400) codes reach the page in the result. - Headers:
x-meeko-csrfon mutations;x-meeko-csrf-tokenandx-request-idon responses;cache-control: no-storeandx-accel-buffering: noon streams;- since
1.7, on a page withcache: weakETag(W/"…"),Cache-Control: public, max-age=…(withno-transformon a stream),Vary: Accept, Cookieand304forIf-None-Match.
- Meta and cookie. The
meeko-csrf-tokenmeta and session cookie, defaulting to__Host-meeko.sid(ormeeko.sidwithoutSecure) and configured withcookieName. Since1.4, withtheme, the theme cookie (meeko-themeby default,theme.cookie) andlocalStoragekey (theme.storageKey) store the theme name orsystemas plain text. Since1.7, the native forms' single-use cookie is named__Host-meeko-form(meeko-formwithout HTTPS), isHttpOnlyandSameSite=Laxand lasts 60 seconds; its contents and thesharedStatemeeko:form:<ruta>key carrying it to the browser are internal: only server and browser of the same version interpret them. - Theme. Since
1.4, withtheme, server-rendered documents carryVary: Cookie, Sec-CH-Prefers-Color-Scheme,Accept-CH: Sec-CH-Prefers-Color-Schemeand, when the choice issystem,Critical-CH: Sec-CH-Prefers-Color-Scheme(withoutclientHints, onlyVary: Cookie). A page payload reading the theme carries what the server rendered in thesharedStatemeeko:themekey. - Socket.
- Documented close codes:
1003,1008,1009,1011and1013, and since1.14401(session rejected) and4403(channel authorization rejected). - Handshake responses:
400,403INVALID_ORIGIN,404and HTTP limit responses. Since1.1, a session (401) or channel authorization (403) rejection completes the upgrade and immediately closes with4401or4403, with a fixed reason and without calling handlers. See framework.md.
- Documented close codes:
- Redis:
- the
<prefix>socket:<canal>:<sala>channel with the{ v: 1, event, payload }envelope; - the
<prefix>revokedrevocation channel; - since
1.2,rateLimitcounters in<prefix>ratelimit:<resumen>(the route and subject digest is not contractual); - session records written by
RedisSessionStoreandSqlSessionStore; - the
D1SessionDriver.schema()reference schema and, since1.7, that ofD1SessionDriver.rateLimitSchema(); - since
1.7, SQLrateLimitcounter columns (key,window_ends_atandcount) in the framework.md PostgreSQL reference schema. The counter key from whichkeyis derived is not contractual: if it changes, counters restart.
- the
- Prerendering. The
prerender/manifest.jsonindex ({ version: 1, pages }); since1.3, each page has the optionalscriptHashesfield, the CSP'sha256-…'sources of its inline scripts, and since1.4styleHashes, 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), withprotocolVersion: 1. - Its
snapshot,subscribe,select,highlight,setBreakpointandsetSignalmethods. - 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,DevtoolsInstancehas the optionalsignalSitesfield (DevtoolsSignalSitetype), with each signal's declaration. It is a field added within protocol 1: a hook or snapshot without it remains valid.
- The
meeko.config.tsdevtoolsoption (since1.8, additive).devtools: boolean | { panel?: boolean; position?: "bottom-right" | "bottom-left" | "top-right" | "top-left" }(MeekoDevtoolsConfigtype) andmountDevtoolsPanelposition(DevtoolsPanelPositiontype). Defaults tofalsethroughout1.x: without the option, nothing changes. The panel exists only inmeeko dev; never reachingmeeko build,meeko startor a prerendered page is part of the contract. The panel's appearance,Alt+Shift+Mshortcut andlocalStoragemeeko:devtools-panelkey are outside the contract and may change in a minor.- Language server.
- Starting with
meeko lsp --stdioormeeko-language-server. - The
trusted,projectContractsanddiagnosticsDelayinitializationOptions. - The standard LSP capabilities it advertises. A minor may add capabilities and quick fixes.
- Starting with
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. Since1.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-scopetsconfigfiles, intermediate builds andchunks/. - The contents of
.meeko/styles/main.css, the processed global stylesheetmeeko devwrites 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.
- Everything under
- The editor bridge. The live-value protocol between browser,
meeko devand editor (HMRmeeko:live*events,.meeko/dev/editor-bridge-<puerto>.jsondescriptors, one per development server, the/__meeko/editor-bridgeroute and its messages, including thealiasfield) 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 devadds to forward server console output (since1.3):<script type="application/json" data-meeko-server-logs>blocks, thedevServerLogsfield of navigation payloads, the HMRmeeko:server-logsevent, its/@id/__x00__meeko:server-logs*modules and the serialized value shape. They exist only in development, never inmeeko buildormeeko start, and may change in any version. The stable part is thedev.serverLogsoption. See framework.md. - Custom language server extensions. The
meeko/executionZonesLSP request and Meeko's custom semantic token modifiers (signal,computed,resourceandreactive;modificationis 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#
- 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.
- Deprecation is announced in the CHANGELOG, documentation and with
@deprecatedin JSDoc so the editor marks it. - Where feasible, deprecated use also emits a warning:
- a
meeko checkor language server warning for syntax and conventions; - a development console warning, once per process, for runtime APIs.
- a
- 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.