Saltar al contenido
Meeko
Escribe para buscar en las guías y en la referencia de la API. Ábrela desde cualquier página con / o Ctrl K; muévete con las flechas y entra con Intro.
Empezar
En esta página

Política de estabilidad 1.x

A partir de 1.0.0, @meekojs/meeko sigue Semantic Versioning. Esta página define qué cubre esa promesa, qué queda fuera, cómo se retira una API y en qué entornos se ejecuta Meeko. Los cambios de cada versión se registran en el CHANGELOG; la actualización desde 0.1.0 se explica en la guía de migración a 1.0, la de 1.0 a 1.1, en Actualizar de 1.0 a 1.1, la de 1.1 a 1.2, en Actualizar de 1.1 a 1.2, la de 1.2 a 1.3, en Actualizar de 1.2 a 1.3, la de 1.3 a 1.4, en Actualizar de 1.3 a 1.4, la de 1.4 a 1.5, en Actualizar de 1.4 a 1.5, la de 1.5 a 1.6, en Actualizar de 1.5 a 1.6, la de 1.6 a 1.7, en Actualizar de 1.6 a 1.7, y la de 1.7 a 1.8, en Actualizar de 1.7 a 1.8.

Versión Puede incluir
Patch (1.0.x) Correcciones compatibles y documentación.
Minor (1.x.0) API, opciones, diagnósticos y convenciones nuevas que no rompen código existente, y deprecaciones con su reemplazo.
Major (2.0.0) Cambios incompatibles y retirada de lo deprecado.

Las prereleases (1.1.0-rc.1) se publican en el dist-tag next de npm y no forman parte de la promesa hasta su versión final.

Un solo paquete#

Compilador, runtime, framework, adaptadores, CLI y servidor de lenguaje se publican juntos en @meekojs/meeko, con una única versión. Durante 1.x no se separarán en paquetes que haya que coordinar.

El código que genera el compilador (módulos .meeko compilados, entradas bajo .meeko/ y builds de meeko build) importa @meekojs/meeko/internal, y ambos deben tener exactamente la misma versión. Por eso meeko init y meeko init-framework fijan la versión exacta en el package.json generado. Al actualizar Meeko, vuelva a compilar o construir: no mezcle código generado por una versión con el runtime de otra, aunque ambas sean 1.x.

API pública#

Lo que se enumera a continuación sólo cambia de forma incompatible en una versión major.

Entradas del paquete#

Las subrutas declaradas en exports de package.json y todos los símbolos y tipos que exportan, estén o no descritos en esta guía, api.md, framework.md, devtools.md y editors.md; su referencia exacta son las declaraciones .d.ts publicadas. Las funciones de bajo nivel para hosts propios (por ejemplo matchRoute, createNavigator, createSocketHub, dispatchApiRoute, dispatchRpc o createRequestContext) también forman parte del contrato:

Entrada Contenido
@meekojs/meeko Reactividad (signal, computed, effect, batch, untrack, createRoot, onCleanup), ciclo de vida (onMounted, desde 1.1), contexto (createContext, provide, inject), provideErrorHandler, serializeJson, trustHtml (desde 1.3), useId (desde 1.5), isTooLargeToAnimate (desde 1.8; el límite exacto no es contrato) y sus tipos, incluidos TrustedHtml (desde 1.3), TagName y ElementAttributes (desde 1.5, los tipos de las etiquetas que acepta <component is> y de los atributos de bind="…") y los de componentes y del protocolo DevTools. Desde 1.5, useId garantiza el mismo ID en el render del servidor y en la hidratación de la misma versión, único por instancia, con el prefijo de document.idPrefix; la forma concreta del ID no es contrato.
@meekojs/meeko/framework Configuración, rutas, páginas, defer, estado (useState y, desde 1.2, useStorage con sus tipos StorageOptions y StorageArea), navegación (useNavigation y, desde 1.2, useRoute con su tipo RouteState, que gana el método isActive en 1.5), formularios (useForm y su tipo Form, desde 1.2; desde 1.5, las opciones onSuccess y focusInvalid, los miembros trySubmit, commit y field de Form, los tipos FormSubmission y FormField, y la revalidación de los campos con error del schema en cada cambio; desde 1.7, la opción action y el miembro csrfToken de Form), head del documento (useHead, useSeoMeta y los tipos HeadInput, HeadMeta, HeadLink, HeadScript, HeadStyle, SeoMetaInput, DocumentConfig y DocumentHead, desde 1.2, DocumentSeo, desde 1.3, y DocumentConfig.idPrefix, desde 1.5), los tipos de configuración SitemapConfig, RobotsConfig y StaticHeaderFile (desde 1.3), la opción sitemapEntries de definePage y sus tipos SitemapEntry y SitemapEntriesContext (desde 1.7), el tema (desde 1.8, useThemeToggle, ThemeToggleState y ThemeToggleSystem; useTheme y los tipos ThemeConfig, ThemeState, ThemeName, ThemeChoice, GeneratedTheme, ThemeToggleMessages y ThemeToggleSnippets, desde 1.4), los tipos de las props de las etiquetas propias (MeekoLinkProps, MeekoOutletProps y MeekoThemeToggleProps, desde 1.4; MeekoLinkProps gana match en 1.5, MeekoAwaitProps, con AwaitResource, AwaitMessages y MeekoAwaitSnippets, desde 1.5; y MeekoPopoverProps y MeekoSelectProps, desde 1.5, con PopoverPlacement, PopoverMode, PopoverRole, PopoverTrigger, PopoverToggleEvent, PopoverSnippets, PopoverExposed, SelectModel, SelectOptionState, SelectFilter, SelectFilterContext, SelectSnippets y SelectField) y RouteId, el ID de cualquier ruta de la aplicación (desde 1.4), los layouts con nombre (useLayout, defineGroup y los tipos LayoutName, LayoutProps, LayoutHandle, LayoutPropsInput, PageLayout, PageLayoutOptions, GroupDefinition, GeneratedLayouts, GeneratedPageLayouts y NamedLayoutRecord, desde 1.5), autoimportados, middleware de rutas (desde 1.6, el segundo argumento { global: false } de defineRouteMiddleware, la opción middleware de definePage y de defineGroup, el campo middleware de RouteRecord, la opción loadNamedMiddleware de ClientAppOptions y de FrameworkHandlerOptions y los tipos RouteMiddlewareName, RouteMiddlewareOptions y GeneratedRouteMiddleware; y el middleware de ruta de servidor: defineServerRouteMiddleware, sus tipos ServerRouteMiddleware y ServerRouteMiddlewareContext, el campo serverMiddleware de RouteRecord, con la cadena completa de la ruta (los globales de servidor y después los con nombre), y la opción loadServerMiddleware(file) de FrameworkHandlerOptions), clientes API, RPC y Socket (createSocketClient, createSocketRegistry, useSocketChannel, useSocketClient desde 1.1 y su alias obsoleto useSocket), isClientError para los errores de esos clientes (desde 1.3), las fábricas de los composables de datos (createUseApi, createUseRpcQuery, createUseRpcMutation y sus tipos, desde 1.1; createUseApiMutation, las opciones retrySafe y sus tipos ApiMutation, ApiMutationInput, ApiMutationContract, UseApiMutation, MutationOptions, MutationAttempt, MutationFailure, RetrySafeOptions, RetrySafeMutation y UncertainMutation, desde 1.2; refreshing y la opción keepPrevious con su tipo DataResourceOptions, desde 1.5; las opciones debounceMs y enabled, desde 1.7) y la API del código de nivel superior de <script server> (usePage, redirect, notFound y los tipos PageRequest, PageRequestOf, PageBodyHead, ServerData, JsonShape y SerializableExport, desde 1.1). Desde 1.2, el sistema de módulos y plugins: defineMeekoModule con sus tipos (MeekoModule, MeekoModuleDefinition, MeekoModuleMeta, MeekoModuleEntry, MeekoModuleContext, MeekoHooks, MeekoHookable, ModuleAutoImport, ModulePlugin y ModuleServerHandler; desde 1.8, MeekoModuleName y la interfaz ampliable MeekoModuleNames) y definePlugin con los suyos (MeekoPlugin, PluginApp, PluginErrorHandler, PluginErrorInfo y PluginRegistration). Son contrato los miembros del objeto meeko de setup, los nombres de los hooks y sus contextos, y el orden de ejecución documentado de módulos y plugins; un minor puede añadir miembros, hooks y campos de contexto. Desde 1.7, la opción cache de definePage (PageDefinition.cache, y cache?: never en ClientPageDefinition) con sus tipos PageCache, PageCacheContext y PageMemoryCache, y los tipos de configuración ServerHeaders y ServerResponseConfig; son contrato las reglas documentadas de la caché de páginas SSR: sólo en producción, nunca con sesión ni con Set-Cookie, el middleware antes del 304, y un ETag débil que incluye el tema, el build y el tipo de respuesta (su valor concreto no es contrato).
@meekojs/meeko/vite Plugin meeko() de Vite.
@meekojs/meeko/server Núcleo de servidor portable: createServerApp, defineServerRuntime, contratos API/RPC, sesiones y stores, middleware, Socket y sus brokers, createSocketUpgradeHandler y OpenAPI. Desde 1.2, la aumentación de módulo de la interfaz MeekoSession, que tipa RequestContext.session (tipo SessionData), también es contrato. Desde 1.7, AppServices, GeneratedServices y ServerRuntimeServices: el tipo de RequestContext.services (y de services de ServerRouteMiddlewareContext) cuando no se indica otro es AppServices, que .meeko/types/services.d.ts infiere del export por defecto de server/runtime.ts; un tipo escrito (RequestContext<MisServicios>) o any siguen ganando. Desde 1.5, el tercer argumento { message, issues } de ApiError, su propiedad issues y los tipos ApiErrorOptions y ApiErrorIssue: los issues de un ApiError con estado 4xx viajan en la respuesta de API y RPC con la forma de los de un 400 (como mucho 20) y nunca con un 5xx. Desde 1.7, la opción form de defineApiRoute y su tipo ApiFormOptions (redirect, maxBytes, files y echo), con el comportamiento documentado de los formularios nativos: los tipos de cuerpo que acepta, los campos reservados return y _csrf, las reglas de conversión del texto, que sin echo no vuelva ningún valor enviado, el 303 See Other del éxito y del error, el 403 sin redirección por Origin o CSRF y el Origin obligatorio en todo envío nativo. Desde 1.8, la comprobación de Origin por defecto en toda mutación de API y RPC, con o sin sesión (403 INVALID_ORIGIN si falta, es null o no está permitido; origin: "required" lo deja explícito), y la salida origin: "none" por ruta, que la desactiva sólo en las peticiones sin sesión, también en un formulario nativo; con sesión se siguen exigiendo Origin y el token CSRF. createLocalCalls envía el origen de la aplicación como Origin, como los clientes del SSR (véase Excepciones aprobadas). Desde 1.8, el campo root de ServerRuntimeContext (y la opción root de createServerApp): la raíz absoluta del proyecto en meeko dev; en las salidas de Node, Bun y Deno, la carpeta tres niveles por encima de la salida, salvo que MEEKO_ROOT la indique; undefined en Workers y cuando un host propio no la pasa. Desde 1.3, contentSecurityPolicy y su tipo ContentSecurityPolicy (desde 1.4, con el tercer argumento styleHashes), y la política de fallos del proceso: processErrors de ServerRuntime (tipo ProcessErrorPolicy), handleProcessErrors (tipo ProcessErrorOptions) y el tercer argumento opcional de ServerErrorObserver (tipo ServerErrorInfo); son contrato los valores por defecto documentados (un rechazo se informa y el servidor sigue; una excepción se informa y cierra con código 1). Desde 1.7, los campos contentSecurityPolicy, headers y buildId de AppManifest, scriptHashes y styleHashes de PrerenderedPage, cache de PageRoute, y las reglas documentadas de la CSP de los adaptadores con servidor (hashes en una página prerenderizada o con cache, sólo del código en línea que escribe Meeko fuera de #app y de los teleports; nonce por respuesta en el resto del SSR; default-src 'none'; frame-ancestors 'none' en el JSON; el nonce o los hashes en script-src-elem y style-src-elem si la política las tiene, y ningún nonce ni hash en una directiva con 'unsafe-inline' salvo que tenga 'strict-dynamic'). Desde 1.7, clientAddress(request, env?), con la misma política de MEEKO_TRUST_PROXY que context.clientAddress y rateLimit, el método opcional SqlSessionDriver.rateLimit(key, windowMs, now) con su tipo SqlRateLimitRow y SqlSessionStore.rateLimit, que sólo existe cuando el driver lo implementa; son contrato la ventana fija, que key sea el SHA-256 (32 bytes) de la clave del contador y que, sin SqlSessionDriver.rateLimit, los contadores sigan en la memoria de cada instancia con un aviso en producción. Desde 1.8, con MEEKO_TRUST_PROXY=cloudflare, la IP de CF-Connecting-IP (o de True-Client-IP si falta) sólo cuando la conexión y cada salto de X-Forwarded-For vienen de las redes de MEEKO_TRUSTED_PROXIES (por defecto, loopback, RFC 1918 y fc00::/7); si no, el primer salto de fuera o la IP de la conexión.
@meekojs/meeko/node Adaptador Node: serveNode, toNodeListener, createNodeAssetReader, attachNodeWebSocketServer y createNodeSocketUpgradeHandler.
@meekojs/meeko/password Desde 1.2: hashPassword, verifyPassword y needsRehash, con scrypt de node:crypto (Node, Bun y Deno). Es contrato el formato scrypt$<N>$<r>$<p>$<sal>$<hash>: un minor puede subir los parámetros por defecto, y verifyPassword acepta siempre los hashes escritos antes.
@meekojs/meeko/bun Adaptador Bun: createBunServer y createBunAssetReader.
@meekojs/meeko/deno Adaptador Deno: serveDeno y createDenoAssetReader.
@meekojs/meeko/cloudflare Adaptador Cloudflare Workers: createWorker, createSocketRoomClass, DurableObjectSocketPubSub, socketRoomName, D1SessionDriver, createCloudflareAssetReader, createCloudflarePrerenderSource y sus tipos estructurales. Desde 1.7, la opción rateLimitTable de D1SessionDriver, con la que implementa rateLimit, y D1SessionDriver.rateLimitSchema().
@meekojs/meeko/build Herramientas de build para Node: descubrimiento, configuración, prerender y buildProject. Desde 1.3, inlineScriptHashes y su tipo InlineScriptReport (desde 1.4, también inlineStyleHashes y styleHashes en PrerenderIndex), y los tipos de configuración SitemapConfig, RobotsConfig y StaticHeaderFile, los mismos que exporta @meekojs/meeko/framework. Desde 1.5, discoverLayouts, la opción layouts de discoverRoutes, de composeFrameworkRoutes y del nuevo segundo argumento de discoverErrorPage, y los tipos DiscoverErrorPageOptions, StaticRouteOptions y StaticGroupOptions. Desde 1.6, discoverRouteMiddleware con su tipo RouteMiddlewareDiscovery (con serverGlobal) y el campo middleware de StaticRouteOptions y StaticGroupOptions. Desde 1.7, los tipos de configuración ServerHeaders y ServerResponseConfig (contentSecurityPolicy y headers de server en node, bun, deno y cloudflare), los mismos que exporta @meekojs/meeko/framework.
@meekojs/meeko/devtools-ui Panel DevTools integrado (mountDevtoolsPanel).
@meekojs/meeko/prettier Plugin de Prettier para .meeko (desde 1.1): el parser meeko y las opciones meekoIndentSections, meekoPreserveArrayBreaks (desde 1.3; también sustituye el printer estree de Prettier) y meekoSortClasses (desde 1.3, con los valores off, stylesheet y alphabetical). El nombre del parser y de las opciones son estables; la salida exacta del formato puede mejorar en un minor, como la de Prettier.
@meekojs/meeko/postcss Plugin de PostCSS meekoUtilities() (desde 1.2): la at-rule @utilities "<ruta>" [layer(<nombre>)], defineUtilities, los helpers rule, atRule y css y sus tipos, con UtilityAtRuleDeclarations desde 1.3 y la opción autoImports desde 1.4. Sólo se ejecuta en Node, en desarrollo y en el build. El formato exacto del CSS generado (espacios, saltos de línea) puede cambiar en un minor.

tests/package-boundaries.test.ts fija la lista exacta de exports de la raíz y de las subrutas de package.json.

Los tipos forman parte del contrato. Hay dos matices:

  • Una unión que Meeko entrega a la aplicación puede ganar miembros en un minor: por ejemplo ErrorSource (que gana "uncaught" y "rejection" en 1.3), PageRender, DeferredStatus, DevtoolsReadKind, DevtoolsEventKind, el estado de un SocketClient (que gana "unauthorized" en 1.1) o los códigos de error. Un switch exhaustivo sobre ellas debe tener una rama por defecto. Una unión que la aplicación pasa a Meeko no se estrecha en 1.x.
  • Una interfaz de un objeto que Meeko entrega a la aplicación puede ganar miembros en un minor: por ejemplo RouteState, que devuelve useRoute() (gana isActive en 1.5), DataResource y LoadedResource, de useApi y los recursos (ganan refreshing en 1.5), o Form (gana trySubmit, commit y field en 1.5). Léalas y desestructúrelas con normalidad, pero no las implemente a mano (const route: RouteState = { … }), porque un minor puede añadir un miembro obligatorio. En una prueba, un doble parcial con Partial<RouteState> o una conversión as RouteState sigue compilando. Las interfaces que la aplicación pasa a Meeko (opciones, configuración, contratos) sólo ganan miembros opcionales.
  • Una corrección de tipos que rechaza código que ya fallaba en ejecución puede publicarse en un minor o un patch, con su entrada en el CHANGELOG.

Excepciones aprobadas#

Cambios que rechazan código válido en ejecución y que se publican en un minor por decisión explícita, cada uno con su migración:

Versión Cambio Motivo y migración
1.1 meeko check y el editor comprueban las props de cada etiqueta de componente (<Etiqueta> o <component is>) sólo contra las que declara el hijo: una prop desconocida es un error aunque la acompañen props válidas. En 1.0, una prop sobrante pasaba la comprobación y en ejecución se ignoraba, así que no fallaba: el cambio no es una corrección de un fallo en ejecución, sino una excepción aceptada para detectar errores de escritura y props obsoletas. Basta con borrar la prop o declararla en el hijo; el comportamiento en ejecución no cambia. Véase Actualizar de 1.0 a 1.1.
1.1 html="..." y model="..." en un elemento HTML estándar (<div>, <input>…) son directivas: el valor es una expresión, y los usos no admitidos dan MEEK095 y MEEK065. En los elementos personalizados (etiqueta con guion, como <x-markdown html="intro">) siguen siendo atributos HTML literales, como en 1.0. En 1.0 se escribían como atributos literales, pero html y model no son atributos válidos de ningún elemento HTML estándar, así que el navegador los ignoraba. Si una hoja de estilos o un script leía ese atributo, escríbalo como data-html/data-model o con bind:html="'texto'", que sigue siendo un atributo.
1.1 El handshake de Socket ya no responde 401 ni 403 a un rechazo de sesión o de autorización del canal: los adaptadores de Meeko hacen el upgrade y cierran en seguida con 4401 o 4403. El Origin no permitido sigue respondiendo 403 por HTTP. En createSocketUpgradeHandler, la acción reject de SocketUpgrade gana el campo opcional close con el código y el motivo. El WebSocket del navegador no expone el estado HTTP de un handshake rechazado, así que el cliente reintentaba sin fin. Un cliente propio que esperaba el 401/403 HTTP debe tratar los cierres 4401/4403 como rechazo definitivo; el cliente de Meeko ya lo hace. La unión no gana miembros, de modo que un adaptador propio escrito para 1.0 sigue funcionando y responde status por HTTP mientras no atienda close. Véase Actualizar de 1.0 a 1.1.
1.2 Un hijo compilado que recibe props nuevas sigue montado y sólo actualiza los bindings que leen la prop: el setup no se vuelve a ejecutar, así que un valor que copia una prop (const { label } = props, props.title.toUpperCase() o la entrada de useApi(ruta, { query: { q: props.q } })) conserva el valor inicial, y escribir en props lanza TypeError. Los componentes con un await de datos en el nivel superior se siguen montando de nuevo. Reconstruir el hijo reemplazaba el <input> que el usuario estaba escribiendo, con su foco, su cursor y su composición, y los snippets que había recibido. Lea la prop donde se usa ({props.label}), derive con computed(() => ...) o pase una función a useApi/useRpcQuery; para cambiar un valor del padre, use un evento o un modelo. Véase Props reactivas y Actualizar de 1.1 a 1.2.
1.2 context.session.create() en una petición POST, PUT, PATCH o DELETE rechaza con 403 INVALID_ORIGIN si su Origin falta o no está permitido, salvo que la ruta declare origin: "none". Corrección de seguridad (login CSRF): hasta 1.1, una ruta de inicio de sesión sin comprobación propia que aceptara un formulario u otro cuerpo sin preflight de CORS dejaba que otra web iniciara sesión en el navegador de la víctima con una cuenta del atacante, y la opción origin: "required" no existía. No se puede corregir de forma compatible, así que se aplica la excepción de vulnerabilidades de la política de deprecación: el cambio mínimo, con una salida explícita. Los navegadores ya envían Origin en esas peticiones, así que las aplicaciones web no cambian; un script o una app nativa debe enviar Origin o la ruta declarar origin: "none", documentado como inseguro. Una aplicación detrás de un proxy que termina TLS y sin MEEKO_PUBLIC_ORIGIN (ni publicOrigin) ve su inicio de sesión rechazado con 403, porque el servidor recibe http:// y el navegador envía un Origin https://; esa misma aplicación ya fallaba en 1.1 en toda mutación autenticada, que comprueba el mismo Origin, así que no funcionaba antes y la corrección es la misma: fijar MEEKO_PUBLIC_ORIGIN con el origen público. Véase framework.md y Actualizar de 1.1 a 1.2.
1.2 En un elemento HTML o SVG de la plantilla, event.currentTarget de on:evento tiene el tipo del elemento de la etiqueta (HTMLInputElement en <input>) y event, el de HTMLElementEventMap (o SVGElementEventMap) para ese nombre. Una conversión explícita a un elemento no relacionado, como event.currentTarget as HTMLSelectElement en un <input>, es ahora un error de tipos (TS2352) en meeko check, meeko build y el editor. Es una corrección sólo de tipos, sin cambio en ejecución: el código generado y el valor de currentTarget son los mismos que en 1.1. Hasta 1.1, currentTarget era un EventTarget genérico, así que cualquier conversión pasaba, incluida la que nombra otro elemento y lee una propiedad que el elemento real no tiene (selectedIndex en un <input>, que da undefined): el error que ahora aparece señala ese fallo. No se puede tipar el elemento sin rechazar esas conversiones. Migración: quitar la conversión, porque el tipo ya es el del elemento (event.currentTarget.value), o corregirla al elemento de la etiqueta. Las etiquetas de componente no cambian.
1.3 UtilityDeclarations, las declaraciones de rule() y atRule() de @meekojs/meeko/postcss, se tipan con csstype en lugar de aceptar cualquier clave con una cadena o un número: una propiedad que csstype no conoce (backgroundColr), una longitud o un tiempo numéricos distintos de 0 (padding: 4) y, en las propiedades que sólo admiten palabras clave (position, boxSizing…), una cadena que no es una de ellas o un string sin literal (const base = { position: "absolute" }) son errores de tipos. atRule() acepta los descriptores que csstype conoce mediante el nuevo UtilityAtRuleDeclarations. Es una corrección sólo de tipos, sin cambio en ejecución: el plugin genera el mismo CSS que en 1.2. Casi todo lo que ahora se rechaza ya fallaba en el navegador, que descarta en silencio una propiedad desconocida o padding: 4; el resto (una propiedad o un descriptor recientes que csstype aún no incluye, o un string ensanchado) es el coste aceptado de autocompletar y comprobar los nombres. Migración: corregir el nombre, escribir la unidad ("4px"), anotar el objeto con UtilityDeclarations o usar as const, y escribir con css`…` lo que csstype no conozca. Las claves dinámicas (Object.fromEntries, Record<string, string>), las propiedades --*, las claves en kebab-case, los arrays de respaldo y !important siguen compilando. Al desaparecer la firma de índice [property: string], leer un objeto UtilityDeclarations con una clave string (decls[key] dentro de un for sobre Object.keys(decls)) es el error TS7053 con noImplicitAny (incluido en strict); migración: tipar la clave como keyof UtilityDeclarations (decls[key as keyof UtilityDeclarations]) o, para iterar, usar Object.entries(decls). Véase framework.md y Actualizar de 1.2 a 1.3.
1.4 Un módulo cuyo meta.configKey es una clave propia de MeekoConfig (layers, extends, modules, imports, server, css, document, dev, site, sitemap, robots o theme) hace fallar la carga de la configuración con un mensaje que pide otra clave. Con theme, unas opciones que no son las del tema (theme: { initial: "dark" }) fallan con unknown field, y el mensaje apunta a ese conflicto. Desde 1.4, theme es la opción del tema claro/oscuro de Meeko, y el ejemplo de módulos de la guía de 1.3 usaba configKey: "theme". Ese módulo leería como sus opciones las del tema de Meeko, su aumentación MeekoConfig.theme choca con el tipo de Meeko (TS2717) y unas opciones suyas en theme activarían el tema de Meeko o no pasarían su validación: no hay forma compatible de que convivan. Basta con dar al módulo otra clave (por ejemplo, acmeTheme), mover sus opciones a ella en meeko.config.ts y renombrar su aumentación de MeekoConfig; un módulo sin configKey, o con una clave que no es de Meeko, no cambia. Véase Actualizar de 1.3 a 1.4.
1.5 bind="..." (sin nombre) en un elemento HTML estándar o en una etiqueta de componente es la directiva de atributos en bloque: el valor es una expresión. Hasta 1.4 era un atributo HTML literal bind o, en un componente, la prop estática bind con el texto. Es la sintaxis que mejor encaja con bind:nombre, y bind no es un atributo HTML: en un elemento sólo podía escribir un atributo que el navegador ignora. Migración: escriba la prop o el atributo bind con bind:bind="'texto'". Los elementos personalizados no cambian. Véase Actualizar de 1.4 a 1.5.
1.5 alias es una clave propia de MeekoConfig: un módulo con meta.configKey: "alias" hace fallar la carga de la configuración, como las demás claves de Meeko desde 1.4. Desde 1.5, alias configura los aliases de ruta de Meeko; las opciones de un módulo en esa clave se leerían como aliases y su aumentación de MeekoConfig.alias chocaría con el tipo de Meeko. Migración: dé al módulo otra clave y mueva sus opciones a ella en meeko.config.ts; véase Actualizar de 1.4 a 1.5.
1.6 Los archivos app/middleware/*.server.ts, que hasta 1.5 el descubrimiento de middleware de ruta ignoraba, son middleware de ruta de servidor. Uno sin export por defecto, o con un export por defecto que no es una llamada a defineServerRouteMiddleware(...) (un helper como export default function parseCookies…), detiene meeko dev, meeko build y meeko check con el archivo. Es el sufijo que encaja con el resto de convenciones (.server.ts es sólo servidor en app/plugins y en los imports), y sin la comprobación un helper que viviera allí se ejecutaría en cada petición de página como middleware global de servidor. Migración: mueva los helpers a server/utils (se autoimportan en el código de servidor) o renómbrelos sin .server. Véase Actualizar de 1.5 a 1.6.
1.6 Un spread en las opciones de una página (definePage({ ...comun, render: "ssr" })) que puede llevar middleware (un objeto importado, el resultado de una llamada o una constante del archivo que tiene middleware) detiene el descubrimiento si middleware no se declara de forma explícita después del último spread. Meeko lee las opciones de ruta sin ejecutar el archivo, así que el middleware de un spread no llegaba al manifiesto y la página se servía sin él, aunque los tipos lo aceptaran. Migración: escriba middleware después del último spread, con los nombres o con middleware: [] si no añade ninguno. El spread de una constante del mismo archivo sin middleware sigue valiendo. Véase Actualizar de 1.5 a 1.6.
1.7 Con server/runtime.ts en TypeScript, RequestContext.services (sin tipo escrito) es AppServices, el tipo inferido de sus servicios, en lugar de unknown. Una conversión directa a un tipo incompatible, como context.services as Otro, es ahora un error de tipos (TS2352) en meeko check, meeko build y el editor. Es una corrección sólo de tipos, sin cambio en ejecución: el objeto services es el mismo que en 1.6. Tipar context.services sin conversiones exige compararlo con su tipo real, y una conversión a un tipo que no encaja con lo que devuelve server/runtime.ts es justo el error que el cambio señala. Migración: quitar la conversión (el tipo ya es el de server/runtime.ts), corregir el tipo o, si la conversión es intencionada, escribir as unknown as Otro o RequestContext<Otro>. Un contexto con otros servicios, como el que crea un test con createRequestContext(request, { db: falso }, session), se sigue aceptando donde se espera un RequestContext. Véase Actualizar de 1.6 a 1.7.
1.7 Asignar computed(...).value lanza TypeError. TypeScript ya lo rechazaba (ReadonlySignal), pero en JavaScript sin tipos, o tras una conversión, la escritura sustituía el valor derivado hasta el siguiente recálculo, de modo que la interfaz mostraba un valor que no salía de sus señales. Es el mismo comportamiento que una escritura en las props desde 1.2. Migración: cambiar las señales de las que depende el computed o usar un signal si el valor se escribe a mano. Véase Actualizar de 1.6 a 1.7.
1.7 PageInputError.name es "PageInputError"; antes era "Error". Lo alinea con ClientError y ApiError, cuyos nombres sirven para reconocer el error en registros y en error.name. Sólo cambia quien comparaba error.name === "Error" para reconocerlo; instanceof PageInputError, code y el mensaje no cambian. Migración: usar instanceof PageInputError o error.code. Véase Actualizar de 1.6 a 1.7.
1.8 Toda mutación comprueba Origin por defecto, con o sin sesión: los POST, PUT, PATCH y DELETE de defineApiRoute y todas las llamadas de defineRpc responden 403 INVALID_ORIGIN si su Origin falta, es null o no es el de la aplicación (publicOrigin o MEEKO_PUBLIC_ORIGIN) ni uno de SessionManager.allowedOrigins, salvo que la ruta declare origin: "none". Sec-Fetch-Site y Referer no sustituyen a Origin. origin: "required" equivale al valor por defecto. Corrección de seguridad (CSRF): hasta 1.7, una mutación anónima sólo se comprobaba con origin: "required", así que una ruta que aceptara un formulario o JSON enviado como text/plain, sin preflight de CORS, podía ejecutarse desde otra web. No se puede corregir de forma compatible, así que se aplica la excepción de vulnerabilidades de la política de deprecación: el cambio mínimo, con la salida explícita origin: "none" por ruta, que ya existía y queda documentada como insegura. Con "none", una petición sin sesión no necesita Origin, también en un formulario nativo (un webhook form-encoded, que hasta 1.7 recibía 403), y una con sesión sigue exigiendo Origin y el token CSRF. createLocalCalls envía el origen de la aplicación, como los clientes del SSR, así que una mutación local desde una página pedida con GET no recibe 403. Los navegadores ya envían Origin en esas peticiones y los clientes api.* y rpc.* del SSR envían el de la petición, así que las aplicaciones web no cambian; un webhook, una API pública o un script sin Origin debe declarar origin: "none" (con otra prueba, como una firma) o enviar un Origin permitido. Véase framework.md y Actualizar de 1.7 a 1.8.
1.8 meeko check (y meeko build, que lo ejecuta) comprueba siempre los tipos de meeko.config.ts, con los de los módulos de modules; hasta 1.7 sólo lo hacía cuando la carga de la configuración fallaba. Para que la clave de un módulo quede tipada sin imports (por ejemplo, i18n: { defaultLocale: 1 } es TS2322), como pide la forma documentada de modules. No cambia nada en ejecución: una configuración que carga sigue cargando. Un error de tipos que antes pasaba inadvertido (como process.env sin @types/node instalado) hace fallar la comprobación; basta con corregirlo. Véase Actualizar de 1.7 a 1.8.
1.8 devtools es una clave propia de MeekoConfig: un módulo con meta.configKey: "devtools" hace fallar la carga de la configuración, como las demás claves de Meeko desde 1.4. Desde 1.8, devtools monta el panel de Meeko DevTools en meeko dev; las opciones de un módulo en esa clave se leerían como las de DevTools. Migración: dé al módulo otra clave y mueva sus opciones a ella en meeko.config.ts.
1.8 <Nombre>Props, el tipo autoimportado de cada componente, ya no incluye los callbacks de defineEmits, y <Nombre>Emits es nuevo. Sin este cambio no se podía escribir un envoltorio de un componente con eventos. Sólo afecta a los tipos: const props: BaseButtonProps = { onClick } da TS2353 y BaseButtonProps["onClick"], TS2339. ComponentProps<"BaseButton"> y el ComponentProps del módulo no cambian, ni tampoco el SSR y la hidratación. Un paquete publicado antes de 1.8 no hay que republicarlo: su declaración no exporta los tipos nuevos, así que su <Nombre>Props sigue siendo su ComponentProps, con los callbacks, y no tiene <Nombre>Emits. Migración: Tipos de un componente.

Formato .meeko y módulo generado#

  • Las secciones <script lang="ts">, <template> y <style> (con scoped y :global(...)), y en las páginas <script server lang="ts"> (desde 1.1), con sus dos formas: las opciones de ruta y el código de nivel superior cuyos export const son los datos de la página, con la regla que decide cuándo un bloque tiene código de nivel superior. Desde 1.1, el <script lang="ts"> es opcional y admite el atributo generic con los parámetros de tipo del componente, que no se permite en páginas, layouts, app.meeko ni error.meeko.
  • La macro defineProps<Props>(), con o sin defaults, y desde 1.1 las macros defineEmits<T>(), defineExpose(value), defineModel<T>(name?, options?), defineSnippets<T>() y useElementRef("name").
  • Las directivas {expression}, on:event, bind:name, if:when, each:of/each:as/each:key/each:index y <component is>. Desde 1.1:
    • each:key es opcional y sin él la clave es el índice;
    • on:event en <component> y en las etiquetas de componente equivale a la prop onEvent;
    • las etiquetas de componente con nombre en mayúscula (<AppButton />), cuyo contenido son snippets;
    • las props estáticas en componentes (variant="primary" pasa un literal de texto y un atributo sin valor, true), con is, ref, html, los nombres con guiones y los que llevan : sin ser directivas reservados (desde 1.8, salvo aria-* y data-*);
    • la directiva ref:name. ref="x" y bind:ref conservan su comportamiento de 1.0;
    • model y model:name en las etiquetas de componente, model en <input>, <textarea> y <select>, y los elementos <snippet name params> y <render snippet args />;
    • la directiva html="expression", que inserta HTML sin escapar, normalizado para que encaje en el elemento y sin ejecutar nunca sus <script> al insertarlo en el navegador;
    • html y model son directivas sólo en los elementos HTML estándar; en los elementos personalizados (etiqueta con guion) son atributos literales;
    • <component is="..." /> se cierra solo, como <Etiqueta />.
  • Desde 1.8, un elemento HTML no vacío sin contenido puede cerrarse con /> (<div />, <textarea />, <td />, <my-element />) y equivale a escribirlo con su etiqueta de cierre: los nodos que siguen son sus hermanos y el SSR escribe <div></div>. <template>, <script>, <style>, <html>, <head>, <body> y los nombres desconocidos siguen dando MEEK001, y un elemento de tabla o de lista autocerrado seguido de su propia etiqueta de cierre (<td /></td>), MEEK008 (véase Elementos sin contenido).
  • Desde 1.4, if:when en <snippet>: el padre pasa ese snippet sólo mientras la condición es verdadera, y sólo si el hijo lo declara opcional.
  • Desde 1.5, la etiqueta propia <MeekoTeleport> con to ("body" o "#id", este último con una letra seguida de letras ASCII, dígitos, - o _), bind:to, disabled y bind:disabled, y los tipos MeekoTeleportProps y MeekoTeleportTarget de @meekojs/meeko/framework. Son contrato el destino del contenido en el HTML del servidor (dentro del elemento del id, tras su contenido, o tras la aplicación y antes de los scripts de Meeko con body), el orden de varios teleports al mismo destino, que conserve el ámbito del componente que lo declara, el movimiento de los mismos nodos al cambiar disabled y el comportamiento con un destino inexistente descrito en framework.md. Los comentarios que marcan el contenido son internos.
  • Desde 1.5, <component is> acepta una etiqueta HTML (un literal, una expresión cuyo tipo es una unión de etiquetas, o una unión de etiquetas y componentes) y contenido: hijos con una etiqueta, snippets con un componente. Las etiquetas admitidas son el tipo público TagName; un atributo que no es global sólo se escribe mientras la etiqueta del momento lo admite según los datos HTML de Meeko (sin type en <a> ni en <area>). Al cambiar la etiqueta, los hijos se mueven al elemento nuevo con su estado. La directiva bind="obj" escribe un objeto como atributos de un elemento o como props de un componente, sin claves on*; los atributos explícitos ganan y class y style se combinan. bind sigue siendo un atributo literal en los elementos personalizados y ya no es una prop estática.
  • Desde 1.4, defineSnippets<T> acepta en cada clave de T una tupla de parámetros o un Snippet<Args>, que cuenta como Args, de modo que T puede derivar del mapa de snippets de otro componente con Omit, Pick o & conservando parámetros y opcionalidad. Una declaración con tuplas conserva el mismo tipo.
  • Desde 1.8, el name de <snippet> admite, además de un identificador, segmentos de caracteres de identificador unidos por guiones (/^[A-Za-z_$][\w$]*(?:-[\w$]+)*$/, como cell-email), y defineSnippets<T> acepta claves que salen de un tipo mapeado con template literal sobre los parámetros de tipo del componente ({ [K in keyof Row & string as `cell-${K}`]?: [row: Row, value: Row[K]] }). Son contrato que meeko check y el editor comprueban esos snippets en el padre con el tipo que infieren sus props (nombre inexistente, tipo de cada parámetro y número de params) y que, en el propio componente, una intersección con un tipo mapeado conserva el tipo de sus claves fijas (snippets.cell). El tipo de un acceso por nombre calculado dentro del componente queda fuera: TypeScript no lo resuelve sobre un parámetro de tipo.
  • Desde 1.4, con theme en meeko.config.ts, la etiqueta propia <MeekoThemeToggle> (obsoleta desde 1.8, se retira en 2.0; usa UIThemeToggle de @meekojs/ui): sus props (mode, themes, labels, messages, class, id), un snippet por elección, el marcado que estiliza la aplicación (el <form method="post" action="/_meeko/theme"> raíz con data-meeko-theme-toggle, el <button> de mode="cycle", los <span data-meeko-theme-icon> de los iconos, el role="radiogroup" y sus botones role="radio" con aria-checked y data-meeko-theme-option de mode="select"), el atributo data-theme-choice de <html> con la elección, la regla de <head> que muestra el icono de la elección, y el endpoint POST /_meeko/theme con las reglas de framework.md (temas válidos, Origin del propio sitio, 303 sólo a una ruta del mismo origen). Los textos por defecto y el CSS de la regla pueden cambiar en un minor.
  • Desde 1.5, la etiqueta propia <MeekoAwait>: sus props (for, como expresión o con bind:for, reserve, messages, class, id), sus snippets (default con refreshing, pending y error con error y retry), los estados descritos en framework.md, el estrechamiento de los datos de for en su contenido, y el marcado que estiliza la aplicación: un <div> con data-meeko-await (pending, error o ready), aria-busy="true" mientras carga o recarga, data-meeko-refreshing durante una recarga y, con reserve, data-meeko-reserve y data-meeko-reserve-min. Son contrato que la reserva sólo aplica min-block-size en pending, sin atributos style, y que la memoria vive en sessionStorage; la clave de cada zona, el formato guardado, el texto del script de <head>, los textos por defecto y el número de zonas recordadas pueden cambiar en un minor.
  • Desde 1.5, las etiquetas propias <MeekoPopover> y <MeekoSelect> (obsoletas desde 1.8, se retiran en 2.0; usa UIPopover y UISelect de @meekojs/ui) (véase Componentes integrados): sus props, sus snippets (trigger, anchor y el contenido; option, value, group, empty y loading), su modelo, sus eventos (toggle con { open }, change) y lo expuesto (show, hide, toggle); el marcado que estiliza la aplicación (data-meeko-popover y data-meeko-popover-trigger, data-meeko-select con data-open, data-multiple, data-disabled y data-loading, las opciones role="option" con data-active, data-selected y data-disabled); los roles y atributos ARIA documentados; el teclado documentado; los <input type="hidden"> de name, y que no usen atributos style="…". Son detalle de implementación, y pueden cambiar en un minor, las reglas de la capa de cascada meeko, las propiedades personalizadas --meeko-popover-*, los textos por defecto y la forma de los IDs.
  • Desde 1.8, un class o un style estático junto a bind:class o bind:style en el mismo elemento (o en un <component is> que sólo puede ser etiqueta) escribe un único atributo en SSR, al hidratar y al actualizar: las clases dinámicas van tras las estáticas, y las declaraciones dinámicas de style tras las estáticas, que pierden. Si el valor dinámico es undefined, null, false o está vacío, queda el texto estático tal cual. Antes, el SSR escribía el atributo dos veces y la hidratación fallaba.
  • Desde 1.8, aria-* y data-* en minúsculas, estáticos o con bind:, en una etiqueta de componente o en un <component is> que sólo puede ser componente, pasan una prop con ese nombre (antes, MEEK032); data-meeko* y las mayúsculas siguen siendo MEEK032. Sólo convierte en válido código que no compilaba.
  • Desde 1.8, cuando el script escribe los eventos de defineEmits (un tipo literal, o una interface o un type locales sin extends ni parámetros), props no enumera sus callbacks (onSave) en Object.keys, in, la propagación ni la desestructuración con resto; leerlos (props.onSave) y emit no cambian. El tipo de props nunca los declaraba, así que el código tipado no los enumeraba a propósito.
  • Desde 1.8, un literal de texto en PascalCase en <component is> (is="to ? 'MeekoLink' : 'a'", 'UICard') nombra <MeekoLink> o un componente importado, autoimportado o declarado en el script, que el compilador resuelve como su etiqueta: una región if:when por nombre, cuya condición es el camino de ?: hasta su literal, y otra para las etiquetas HTML; cada atributo va sólo a las ramas que lo admiten. Las etiquetas HTML se escriben en minúscula. Es aditivo: antes, un literal en PascalCase era MEEK112 (no es una etiqueta), y is con un valor de componente o con etiquetas HTML no cambia. Un nombre desconocido o un valor junto a nombres es MEEK132. El reparto de regiones y marcadores es detalle de implementación. resolveComponent("MeekoLink") y la entrada MeekoLink de ComponentRegistry (salvo que el proyecto tenga un componente con ese nombre) cargan <MeekoLink> como componente desde @meekojs/meeko/builtins/link.
  • Desde 1.8, bind: en un elemento SVG o MathML escribe el nombre con el caso del estándar, como el atributo estático (bind:viewBox escribe viewBox, no viewbox); antes, el atributo en minúsculas no tenía efecto en el SVG y la hidratación no coincidía.
  • Desde 1.8, <MeekoLink> con bind:to de tipo unión de rutas acepta bind:params con los parámetros de alguna de ellas, o ninguno, y bind:search con undefined; con una sola ruta la comprobación no cambia.
  • Desde 1.8, defineProps puede desestructurarse en su declaración const (const { label, size = "md", class: className, ...rest } = defineProps<Props>()): cada nombre lee su prop donde se usa y es reactivo en el script y en la plantilla, salvo donde un ámbito más cercano declara el mismo nombre; los defaults del patrón equivalen al argumento de defaults; ...rest es una vista de sólo lectura y reactiva de las demás props, sin los manejadores de defineEmits. Los defaults (en las dos formas) pueden leer constantes de nivel superior que sólo usan literales e imports. Es aditivo: sólo convierte en válido código que antes era MEEK015 o TS2304. Los tipos de los defaults se calculan por variante de una unión de props, así que un default de una prop que una variante declara never ya no la permite en esa variante para el padre.
  • Desde 1.8, con una unión de props, un valor por defecto literal de defineProps (kind = "a", en el patrón o en el argumento) hace opcional su prop para el padre sólo en las variantes que admiten ese valor, en ComponentProps, las declaraciones .d.meeko.ts y el editor; un default que no es literal sigue siendo opcional en todas las variantes y es MEEK131 si alguna que exige la prop no lo admite. Es una corrección: hasta ahora, un padre podía omitir el discriminante de cualquier variante (<Child b="hello" /> con kind = "a"), el componente recibía { kind: "a", b: "hello" }, que no es ninguna variante, y fallaba en ejecución. Ese padre deja de compilar; para migrar, pasa el discriminante (kind="b"). Las uniones sin default en el discriminante y las props que no son uniones no cambian.
  • Desde 1.8, los datos HTML de Meeko salen del estándar (src/shared/html-data, generados por scripts/html-data.ts): las etiquetas y atributos que acepta <component is> y bind="obj" son un superconjunto de los de 1.7. Los tipos públicos AttributeValues<Tag, Name> y AttributeValueOf<Tag, Name> (de @meekojs/meeko), y los valores de ElementAttributes<Tag>, sugieren los valores del estándar sin restringir lo que se acepta; un literal fuera de ellos es el aviso MEEK122. hidden con un texto no vacío ("until-found") escribe ese texto, y los atributos booleanos del estándar que 1.7 no trataba como tales se escriben vacíos.
  • Las reglas del setup desde 1.1: useElementRef, useApi, useRpcQuery y useRpcMutation sólo en el nivel superior; await de nivel superior sólo sobre useApi, useRpcQuery, useElementRef, useSocket y useSocketClient; la división del setup en el primer await de navegador, cuya continuación sólo se ejecuta en el navegador y admite sentencias de expresión y de control de flujo.
  • La forma de cada módulo compilado: ComponentProps, ComponentExposed, ComponentModels y ComponentSnippets (desde 1.1), css (con el plugin de Vite de Meeko, desde 1.8 depende del grafo: en el grafo SSR —meeko dev, el build de Node, el prerender, static y Cloudflare— y en el cliente de meeko dev, es el CSS del <style> ya procesado por el PostCSS y los plugins CSS del proyecto, el mismo que incrustan el SSR y el prerender y que reaplica la HMR; en el bundle de cliente de producción es la salida del compilador, como antes de 1.8, porque ahí ningún código de Meeko lo lee y la hoja que Vite empaqueta ya aplica el CSS procesado. En un host que no procesa CSS y resuelve ese módulo como una cadena vacía, como Vitest sin test.css: true, también es la salida del compilador. Con compile() sin la opción styleModule, siempre es la salida del compilador), renderWithState, renderToString, hydrate y hydrateScope, cuya función de liberación lleva exposed. En un componente con generic, los tipos y las funciones llevan sus parámetros de tipo. Las marcas hasDataAwait, __meekoChildren, __meekoReactiveProps (desde 1.2), __meekoChannels y __meekoTag son internas.
  • Desde 1.8, dónde van los <style> en línea del SSR y del prerender: al final del <head>, después de las hojas globales (app/styles/main.css, las de addStylesheet y el head de document), donde el cliente añade después el CSS de los componentes, de modo que el primer pintado y la página hidratada tienen la misma cascada; antes iban delante de las hojas globales y una regla global de igual especificidad ganaba sólo hasta hidratar. La página de error de un fallo del servidor, que no se hidrata, también incrusta su CSS. El atributo data-vite-dev-id que meeko dev añade a esos <style> para que Vite los adopte es detalle de implementación.
  • El tipo Component<Props>.

El código JavaScript generado no es público; véase Fuera del contrato.

CLI#

Los comandos y opciones que muestra meeko --help:

Comando Opciones
meeko dev [directorio]
meeko check [directorio | componente.meeko] --json (sólo para un componente)
meeko build [directorio] --adapter <node|bun|deno|cloudflare|static> o --adapter=<nombre> (static, desde 1.3)
meeko start [directorio]
meeko preview [directorio] --adapter <node|bun|deno|cloudflare|static> (static, desde 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] (desde 1.1) --all, --dry-run. Son contrato las opciones y qué se borra en cada modo; el texto exacto de la salida no.
meeko auth hash (desde 1.2) Sin opciones. Lee la contraseña sin eco en una terminal (dos veces) o de stdin en una tubería, y escribe en stdout sólo la línea del hash.
meeko lsp --stdio, --node-ipc, --socket=<puerto> o --pipe=<nombre>
meeko --help (-h), meeko --version (-v)

También forman parte del contrato:

  • el bin meeko-language-server, con los mismos transportes que meeko lsp;
  • el código de salida 1 ante errores;
  • los códigos de diagnóstico (MEEK0xx, MEEK1xx y MEEK_*; véase Códigos de diagnóstico);
  • la forma { code, message, filename, location } de meeko check --json.

El texto de los mensajes puede mejorar en cualquier versión; desde 1.1 está en español. Un minor puede añadir diagnósticos para código que ya fallaba en ejecución o violaba un contrato documentado, y puede dejar de emitir uno cuando el código que señalaba pasa a ser válido. Un código retirado no se reutiliza con otro significado.

Códigos de diagnóstico#

Código Tema
MEEK001 HTML del componente no válido.
MEEK002 Contenido de nivel superior que no es <script>, <template> ni <style>, incluida una etiqueta de componente fuera de las secciones.
MEEK003, MEEK004 Sección sin etiqueta de cierre o duplicada.
MEEK005 Retirado en 1.1. Señalaba la falta de <script lang="ts">, que pasa a ser opcional.
MEEK006 <script> o <script server> sin lang="ts".
MEEK007 Falta <template>.
MEEK008 HTML de la plantilla no válido, incluido un cierre de etiqueta de componente sin apertura y, desde 1.8, <input> u otro <select> dentro de un <select>.
MEEK009, MEEK010 Expresión de plantilla vacía o sin cerrar.
MEEK011 Nombre de evento no válido.
MEEK012 Atributo dinámico o directiva no admitidos.
MEEK013 Error de sintaxis del <script>.
MEEK014, MEEK015 Más de una variable por sentencia, o una desestructuración del resultado de useElementRef o una forma no admitida de desestructurar defineProps: anidada, de array, con clave calculada o con let (desde 1.8; de 1.2 a 1.7, cualquier desestructuración de defineProps; antes, cualquier desestructuración).
MEEK016, MEEK017 defineProps repetido o fuera de su propia declaración, o sin argumento de tipo.
MEEK018 Sentencia de nivel superior no admitida en el <script> o en la continuación de navegador: un export de valor, una clase, un enum, un namespace, import = o un return, throw, break o continue suelto. Desde 1.2 el setup síncrono admite cualquier sentencia de expresión y control de flujo, como la continuación.
MEEK019 :global(...) no válido en <style scoped>.
MEEK020 Error de sintaxis de una expresión de la plantilla.
MEEK021, MEEK022 if:when y each:of en el mismo nodo; each:as o each:index no válidos o repetidos.
MEEK023 Retirado en 1.1. Señalaba una lista sin each:key, que pasa a ser opcional.
MEEK024 Directiva de plantilla incompleta.
MEEK030, MEEK031 <component> sin is, o con contenido. Desde 1.5, <component is> admite contenido y MEEK031 sólo señala contenido en una etiqueta vacía (img, input, hr…): un error de compilación si es un literal de is y un error de tipos de meeko check y del editor si viene del tipo de is.
MEEK032 Atributo de componente que no es una prop: is en una etiqueta, ref="x", html y los nombres con guiones o con : que no son directivas. Desde 1.1, cualquier otro atributo con nombre de identificador es una prop estática en lugar de este error. Desde 1.5, en un <component is> cuya expresión puede dar una etiqueta, esos atributos son del elemento y, si el tipo de is admite un componente, el error de tipos MEEK112.
MEEK033, MEEK034 Import de componente que no es una ruta .meeko relativa (desde 1.5, ni un alias de ruta), o que no existe.
MEEK040 a MEEK042 Valores por defecto de defineProps; MEEK042 también señala una prop desestructurada dos veces.
MEEK043, MEEK044 <script server> con valor, o fuera de una página.
MEEK045 a MEEK048 defineEmits, y on:x junto a bind:onX (o la misma prop dos veces).
MEEK049 Retirado en 1.1. Señalaba contenido en una etiqueta de componente, que pasa a ser sus snippets.
MEEK050, MEEK051 defineExpose.
MEEK052, MEEK053 La directiva ref: y los nombres repetidos.
MEEK054 a MEEK058 Composables de nivel superior, await y la continuación de navegador; MEEK056, los argumentos de useElementRef.
MEEK059 a MEEK062 defineSnippets, <snippet> y <render>.
MEEK063 a MEEK066 defineModel, model en controles de formulario y model/model:name en componentes, con su prop y su evento.
MEEK070 a MEEK076 Código de nivel superior de <script server>.
MEEK080 await useElementRef(...) sobre un componente sin defineExpose.
MEEK090 La misma prop como texto estático y con bind: u on:.
MEEK095 La directiva html: hijos, elementos vacíos o de texto, <template>, model o valor vacío.
MEEK100 El atributo generic: lista no válida, modificadores, nombres reservados o repetidos, ubicación no admitida y tipos del script que usan sus parámetros.
MEEK101 Aviso (desde 1.2): una lectura de props que el setup ejecuta una sola vez; desde 1.8, también de un nombre desestructurado de defineProps o de su ...rest.
MEEK104, MEEK105 Desde 1.2, <MeekoOutlet /> fuera de app.meeko y los layouts o con atributos o contenido, y un <MeekoLink> sin to, con href o con params/search sin bind:; desde 1.5, también un match estático que no es "exact" ni "prefix".
MEEK106 Aviso (desde 1.2): un componente del proyecto llamado MeekoLink o MeekoOutlet (desde 1.4, también MeekoThemeToggle; desde 1.5, MeekoAwait, MeekoTeleport, MeekoPopover y MeekoSelect) sustituye a la etiqueta propia de Meeko.
MEEK107 Aviso (desde 1.3): el valor de html="..." es un string (o any) sin la marca TrustedHtml de trustHtml. Pasará a ser un error en 2.0; véase APIs deprecadas.
MEEK108 Desde 1.3, una API que no existe donde se usa: en el <script> o la plantilla de un componente (que también se ejecutan en el navegador), un autoimport de servidor (defineApiRoute, defineRpc, defineServerMiddleware, defineSocketChannel, defineServerRuntime, ApiError o uno de server/utils), un import de valores de @meekojs/meeko/server o /cloudflare, o un import de server/ o de un módulo *.server.*; en <script server>, las macros del componente (defineProps, defineEmits, defineExpose, defineModel, defineSnippets, useElementRef) y onMounted. Todo eso ya fallaba en el build o en ejecución; véase framework.md. Desde 1.5, también un import por #server/ (o por un alias propio de una carpeta de server/) en un componente, en un .ts de app/ o en shared/, aunque sea import type, y un import de server/ por ~/; véase Ámbitos de los aliases.
MEEK109 Aviso (desde 1.3): en el <script> o la plantilla de un componente, definePage, que ahí no tiene efecto, o un import de valores de @meekojs/meeko/node, /bun, /deno, /password o /build, cuyas funciones sólo funcionan en el servidor. Pasará a ser un error en 2.0; véase APIs deprecadas.
MEEK110 Desde 1.4, error de tipos: if:when en un <snippet> que llena un snippet obligatorio del hijo.
MEEK111 Desde 1.4, <MeekoThemeToggle> sin theme en meeko.config.ts (en meeko dev, meeko build, meeko check, el editor y, si sólo ejecutando la configuración se sabe, al renderizar).
MEEK112 Desde 1.5, <component is> con una etiqueta HTML: una etiqueta que no puede renderizar (script, iframe, desconocida o dentro de <svg>), un string cualquiera, un atributo que no admite ninguna de las etiquetas posibles, model con etiquetas, o un atributo que sólo vale con una etiqueta (o sólo con un componente) cuando is admite el otro tipo. Es un error de compilación para un literal de is y un error de tipos de meeko check y del editor en los demás casos; en ejecución, un valor no permitido lanza TypeError antes de escribir HTML. Desde 1.8, con un texto que llega en ejecución y puede nombrar un componente, el mensaje dice qué hacer: un literal en la plantilla, el módulo importado o resolveComponent.
MEEK113 Desde 1.5, bind="obj": sin valor, en <template>, una clave on* o data-meeko*, una clave que no es un atributo de la etiqueta o una prop del componente. Las claves no válidas son errores de tipos; en ejecución, una clave on* lanza TypeError.
MEEK114 Desde 1.5, <MeekoTeleport> sin to, con un to estático que no es "body" ni "#id", con disabled con valor, con un atributo repetido (to y bind:to) o con un atributo que no admite.
MEEK115 Aviso (desde 1.5): un +group.ts fuera de app/pages, donde no configura nada.
MEEK116 Desde 1.5, <MeekoAwait> sin for o con for vacío.
MEEK117 Aviso (desde 1.5): una página o un layout pinta en un each:of los datos de un useApi o un useRpcQuery sin await sin ocuparse de su carga (<MeekoAwait>, pending, refreshing o un if:when sobre sus datos), lo que hace saltar el diseño al llegar los datos.
MEEK118 Desde 1.5, un import por @/ (o por un alias propio de una carpeta de app/) en shared/ o en un *.shared.ts, que no ve app/; véase Ámbitos de los aliases.
MEEK119 Aviso (desde 1.7): un atributo each:* o if:* que no lee ninguna directiva del nodo (each:by junto a each:of, if:else junto a if:when) y se escribe como atributo HTML literal. En 1.x no cambia el HTML generado.
MEEK120 Aviso: la interfaz local de props no se llama Props; renómbrala a Props. Véase defineProps.
MEEK121 Aviso (desde 1.8): uso de <MeekoPopover>, <MeekoSelect> o <MeekoThemeToggle>, obsoletos y con retirada en 2.0. Usa UIPopover, UISelect o UIThemeToggle de @meekojs/ui.
MEEK122 Aviso (desde 1.8): el valor de un atributo HTML es un literal de texto que no está entre los que el estándar admite para esa etiqueta (<button type="sumbit">). Será un error en 2.0; véase Valores de los atributos.
MEEK123 Desde 1.8, un valor por defecto de defineProps (en el argumento o en la desestructuración) lee algo que depende del setup: una prop, una señal, una variable let, una función del script, una constante cuyo valor llama a una función, o arguments, new.target o super de la función del setup. Las variables que declara el propio default no cuentan. Las constantes de literales e imports son válidas; antes, cualquier variable del script daba TS2304.
MEEK124 Desde 1.8, defineProps con valores por defecto en la desestructuración y en el argumento a la vez.
MEEK125 Desde 1.8, una asignación (=, ++, una desestructuración) o un delete de un nombre desestructurado de defineProps, que es una prop de sólo lectura.
MEEK126 Aviso (desde 1.8) de meeko dev, meeko build, meeko check y el editor: una entrada de modules que importa el módulo (modules: [ui]) o usa [módulo, opciones]. Indica la cadena y la clave (meta.configKey) que la sustituyen. Ambas formas siguen funcionando en 1.x y se retiran en 2.0; véase APIs deprecadas.
MEEK127 Aviso (desde 1.8) al cargar los módulos: una clave de meeko.config.ts que es la meta.configKey de un módulo instalado que no está en modules (i18n: { … } sin '@meekojs/i18n'). Meeko la ignora, como hasta 1.7; el aviso nombra la entrada que falta.
MEEK128 Aviso (desde 1.8) de meeko check y el editor: un onCleanup que también se ejecuta en el servidor (en el nivel superior del <script>, en un effect o en una función del script que el setup llama, como un composable) y cuya limpieza usa una API que sólo existe en el navegador (window, document, cancelAnimationFrame, ResizeObserver…) sin comprobarlo antes (typeof window, import.meta.env.SSR). El editor ofrece «Mover a onMounted». Será un error en 2.0; véase onCleanup.
MEEK129 Aviso (desde 1.8) de meeko check y el editor: onCleanup después de un await de su propia función async (onMounted(async () => { await …; onCleanup(…) })), donde ya no hay raíz activa. Será un error en 2.0; véase onCleanup.
MEEK130 Aviso (desde 1.8) de meeko check de un proyecto: onCleanup al evaluar un módulo de app/, shared/ o server/ (en su nivel superior, fuera de una función), que lanza al importarlo. Será un error en 2.0; véase onCleanup.
MEEK131 Desde 1.8, meeko check y el editor: un valor por defecto de defineProps que no es un literal, en una prop que alguna variante de una unión de props exige con un tipo que no admite ese valor (un discriminante). Un default literal hace opcional la prop sólo en las variantes que lo admiten; uno que no es literal la haría opcional en todas. Antes, el padre podía omitir el discriminante de cualquier variante.
MEEK132 Desde 1.8, compilación, meeko check y el editor: en <component is> con nombres de componente entre comillas, un literal en PascalCase que no es 'MeekoLink' ni un componente importado, autoimportado o declarado en el script (cuando se conocen los autoimports del proyecto), 'MeekoOutlet' o 'MeekoTeleport', o un resultado de is que no es un literal junto a nombres. Véase Nombres de componente en is.
MEEK133 Aviso (desde 1.8) de meeko check, meeko build y el editor: el mismo atributo como texto estático y con bind: (id="a" bind:id="b"), salvo class y style, que se combinan. Vale bind:, en cualquier orden, y el texto se ignora; antes, un orden daba Hydration mismatch. Véase bind="obj".
MEEK134 Aviso (desde 1.8) de meeko check, meeko build y el editor: el nombre anterior de un Carpeta/Index.meeko (<ButtonIndex>, ButtonIndexProps o resolveComponent("ButtonIndex")), que ahora se llama como su carpeta. Sigue funcionando en 1.x y se retira en 2.0; véase Una carpeta con Index.meeko.
MEEK135 Aviso (desde 1.8) de meeko check, meeko build y el editor, en un Carpeta/Index.meeko cuyo nombre nuevo ya tiene otro archivo (Button.meeko): conserva <ButtonIndex>, su único nombre. También en un Index anidado cuyo nombre nuevo es el anterior de otro (Button/Index/Index.meeko, con Button/Index.meeko): conserva <ButtonIndexIndex>. El aviso dice qué archivo conserva cada nombre y sugiere renombrar o mover uno de los dos; véase Una carpeta con Index.meeko.
MEEK136 Desde 1.8, meeko check y el editor: el valor inicial de un signal del nivel superior del script o de un useState contiene un módulo de componente, que no puede viajar al navegador con el estado. Mira el valor, no el tipo de la señal, y una unión con un miembro sin módulo no cuenta. En ejecución, el TypeError del render de servidor lleva el mismo código.
MEEK137 Desde 1.8, meeko check y el editor: una declaración de tipo del script usa typeof de una declaración del setup, que no existe a nivel de módulo, donde se declaran los tipos. No afecta a la compilación.
MEEK_PAGE_PROPS, MEEK_LAYOUT_PROPS, MEEK_ERROR_PROPS Contratos de props de páginas, layouts y páginas de error.
MEEK_PAGE_LAYOUT_PROPS Desde 1.5, las layoutProps de un +group.ts o de una página que hereda su layout no coinciden con las props de ese layout.
MEEK_AUTOIMPORT, MEEK_CHECK, MEEK_PROJECT_CHECK Servidor de lenguaje: autoimport ambiguo o no disponible, fallo al comprobar el componente y fallo al comprobar los contratos del proyecto.

Los códigos a partir de MEEK043 son nuevos en 1.1. Los errores de TypeScript conservan su código TSxxxx, que puede cambiar al actualizar TypeScript o la forma del código que comprueba: en 1.1, una prop obligatoria que falta en una etiqueta de componente da TS2345 en lugar de TS2769.

La versión de TypeScript que incluye el paquete puede actualizarse en un minor. Si eso cambia los diagnósticos de meeko check, el CHANGELOG lo indica.

Convenciones de proyecto#

  • meeko.config.ts con defineMeekoConfig: extends, modules, imports.serverDirs, la unión discriminada server (node, bun, deno y cloudflare, con sus campos documentados, y desde 1.3 static, con contentSecurityPolicy, headers y headerFiles) y, desde 1.2, css.scopeAttribute, layers, document (lang y head) y las claves de opciones de los módulos (meta.configKey; desde 1.4, una clave propia de MeekoConfig hace fallar la carga, véase Excepciones aprobadas). Desde 1.3, site, sitemap (true o { exclude }), robots (true o { allow, disallow }) y document.seo (true o { canonical, ogUrl, ogTitle, ogDescription, origin }), con las etiquetas, archivos y prioridades descritos en framework.md, dev.serverLogs, imports.appDirs e imports.sharedDirs, estas dos con el ámbito y las reglas de rutas de Carpetas de autoimports. Desde 1.4, theme (true o { themes, default, system, darkThemes, attribute, colorScheme, cookie, storageKey, clientHints }), con el comportamiento de Tema claro/oscuro sin parpadeo: el atributo o la clase de <html>, el script en línea de <head> y su orden de lectura (cookie, localStorage, valor por defecto). Desde 1.2, modules lista módulos de defineMeekoModule (un módulo, un paquete, una ruta o [módulo, opciones]); extends es un alias de layers. Desde 1.8, la forma documentada es el nombre del paquete o la ruta, con las opciones en la clave de su meta.configKey: es contrato que un paquete se resuelva por su subpath ./module si lo exporta y, si no, por su raíz; que .meeko/types/config.d.ts importe los tipos de los módulos de modules y que .meeko/tsconfig.config.json incluya meeko.config.ts con ellos (su texto concreto no); que la palabra clave meeko-module del package.json de una dependencia la ofrezca en el completado de modules, y que dos módulos con la misma meta.configKey sean un error (antes se leían las mismas opciones dos veces).
  • El documento HTML que renderiza el servidor: desde 1.2, el título y las etiquetas que gestiona Meeko van entre los comentarios <!--meeko:head--> y <!--/meeko:head-->, antes del HTML de DocumentOptions.head, y la meta CSRF sigue a <meta charset> y a las etiquetas por defecto (viewport e icono) que la aplicación no sustituye. La prioridad y la deduplicación de useHead descritas en framework.md son estables.
  • app/:
    • app.meeko, error.meeko, pages/, layouts/default.meeko, middleware/, components/, composables/, utils/, stores/ y styles/main.css;
    • páginas index.meeko, [id].meeko, [...slug].meeko y grupos (group), con +layout.meeko, +error.meeko y las opciones de ruta en <script server> o en sidecars *.route.ts;
    • desde 1.5, los layouts con nombre layouts/<nombre>.meeko (también en subcarpetas: layouts/admin/panel.meeko es "admin/panel"), que las páginas eligen con layout en definePage, y los archivos +group.ts de las carpetas de pages, con export default defineGroup({ layout, layoutProps }) en literales;
    • desde 1.6, el middleware con nombre: un archivo de middleware/ con defineRouteMiddleware(fn, { global: false }), cuyo nombre es su ruta sin .ts ni el prefijo numérico de orden de cada parte (10-auth.ts es "auth"), que las rutas piden con middleware (un array literal de nombres, o false) en definePage y en defineGroup. Son contrato el orden (los globales y después los de la ruta, de los +group.ts exteriores a la página, sin repetir) y que un nombre que no existe o que es global detenga el descubrimiento;
    • desde 1.6, el middleware de ruta de servidor: middleware/*.server.ts cuyo export por defecto es defineServerRouteMiddleware(...) (cualquier otro export por defecto, o ninguno, detiene el descubrimiento), global o con nombre con la misma regla de nombres (auth.server.ts es "auth", y auth.ts y auth.server.ts con nombre son un duplicado). Son contrato que nunca llegue al navegador, que se ejecute en el servidor después de todo el middleware isomórfico (globales y luego con nombre, y después los de servidor en el mismo orden), en SSR y en la petición de datos de cada navegación, que no se ejecute en las páginas render: "client" ni "prerender", y que pedir uno con nombre en ellas detenga el descubrimiento.
  • server/:
    • api/ (<nombre>.<método>.ts), rpc/, socket/, middleware/ y utils/;
    • server/runtime.ts, cuyo export por defecto es defineServerRuntime(...).
  • shared/, public/, layers y módulos.
  • Desde 1.5, los aliases de ruta ~/ (la raíz del proyecto), @/ (app/), #shared/ (shared/) y #server/ (server/, sólo en el código de servidor), sus ámbitos, su búsqueda en el proyecto y después en cada layer, y la opción alias de meeko.config.ts (un prefijo terminado en / con una carpeta del proyecto o false). Son contrato la prioridad de los paths del tsconfig.json del proyecto y de un resolve.alias de Vite sobre ellos, y que un imports de package.json con el mismo prefijo # gane y desactive el alias. La forma de los paths en los tsconfig generados no es contrato.
  • Desde 1.2, app/plugins/*.ts en la aplicación y sus layers, con los sufijos .client.ts (sólo navegador, antes de hidratar) y .server.ts (sólo servidor, una vez por petición de página).
  • Los sufijos .client.ts, .server.ts y .shared.ts, y las exclusiones *.test.ts, *.spec.ts y *.d.ts. Desde 1.6, un app/middleware/*.server.ts, que hasta 1.5 se ignoraba, es middleware de ruta de servidor (véase Excepciones aprobadas).
  • Los autoimports api, rpc, socket, useSocket, useState, href y provideErrorHandler, y desde 1.1 onMounted, useApi, useRpcQuery y useRpcMutation, y desde 1.2 useRoute, useForm, useStorage, useApiMutation, useHead y useSeoMeta, y desde 1.4 useTheme, y desde 1.5 useId (de @meekojs/meeko), useLayout, defineGroup y ClientError e isClientError (sólo en los componentes y en los módulos de app/, no en server/, *.server.ts ni los archivos de ruta), y desde 1.6 defineServerRouteMiddleware (en los *.server.ts y en todo server/, como las definiciones de servidor), y desde 1.7 useNavigation, createRoot y defer (este en el código de servidor: <script server>, también dentro de las opciones de ruta, los sidecars *.route.ts, server/ y los *.server.ts) (un export propio con uno de esos nombres tiene prioridad sobre el del framework). Desde 1.4, los tipos de cada componente autoimportado: su nombre de etiqueta (la regla de nombres de app/components y de addComponentsDir, prefijo incluido) seguido de Props, Snippets, Models y Exposed (AppCardProps…), iguales a ComponentProps, ComponentSnippets, ComponentModels y ComponentExposed de su módulo, null incluido, con el ámbito del componente; un autoimport del proyecto con el mismo nombre tiene prioridad (véase Tipos de cada componente). En el código de nivel superior de <script server>, usePage, redirect y notFound se pueden usar sin import, y desde 1.2 también useHead y useSeoMeta.
  • Las variables de entorno MEEKO_HOST, MEEKO_PORT (con PORT como respaldo permanente), MEEKO_PUBLIC_ORIGIN, MEEKO_HMR_PORT y, desde 1.2, MEEKO_REDIS_URL, MEEKO_REDIS_PREFIX, MEEKO_SESSION_TTL, MEEKO_SESSION_SAMESITE y MEEKO_TRUST_PROXY, y desde 1.8 el valor cloudflare de MEEKO_TRUST_PROXY y MEEKO_TRUSTED_PROXIES (véase Cloudflare Tunnel y Traefik), y el prefijo MEEKO_, reservado para las variables que lee Meeko. HOST y PUBLIC_ORIGIN están obsoletas desde 1.2.

Salidas de build#

Adaptador Carpeta Se ejecuta con
Node .meeko/output/node/ index.mjs, mediante meeko start o node
Bun .meeko/output/bun/ index.js
Deno .meeko/output/deno/ main.js
Cloudflare Workers .meeko/output/cloudflare/ worker.js y wrangler.jsonc
Sitio estático (desde 1.3) .meeko/output/static/ Cualquier hosting estático, o meeko preview

En el sitio estático también son estables:

  • <ruta>/index.html y <ruta>/index.meeko.json por página, 404.html, sitemap.xml y robots.txt en la raíz; desde 1.7, una <url> de sitemap.xml puede llevar <lastmod>, <changefreq> y <priority> (en ese orden, tras <loc>), en todos los adaptadores;
  • _headers (bloque /*) y _meeko/headers.conf (una línea add_header <Nombre> "<valor>" always; por cabecera), con los que choca un archivo de public/ del mismo nombre.

En Cloudflare también son estables:

  • el binding MEEKO_SOCKET;
  • la migración meeko-socket-v1, que no se renombra;
  • la reserva de public/_meeko/;
  • los campos de wrangler.jsonc que pertenecen a Meeko.

El contenido de chunks/, los nombres de archivos con hash y el resto de .meeko/ no son públicos.

Formatos de red y persistidos#

Estos formatos permiten clientes externos, proxies, CSP y despliegues graduales:

  • Payload de página. Versión v: 2, con render, head (title y, desde 1.1, description) y deferred:
    • el documento lo lleva en <script id="meeko-payload" type="application/json">, dentro de un HTML con <div id="app">;
    • la navegación lo pide con accept: application/meeko+json, y la respuesta es application/meeko+json o, en una ruta "stream", application/meeko+ndjson (el payload en la primera línea y un valor { "deferred", "value" | "error" } en cada línea siguiente); desde 1.3, el cliente del adaptador static lo pide como el archivo <ruta>/index.meeko.json, sin accept;
    • desde 1.3, un documento con un payload error: true hidrata app/error.meeko en cualquier URL, también en una que coincide con una ruta (el 404.html de un hosting estático);
    • con app/error.meeko, una URL sin ruta responde 404 con un payload error: true, sin route y con props { status, message };
    • desde 1.1, el campo opcional resources lleva los datos de los await useApi(...) y await useRpcQuery(...) que leyó el render, como objeto por clave de petición. El formato de esas claves es interno y puede cambiar en cualquier versión: sólo el servidor y el navegador de la misma versión lo interpretan. Un payload sin resources sigue siendo válido;
    • desde 1.5, el campo opcional layoutProps lleva las props con que el servidor renderizó el layout con nombre de la página (las de sus +group.ts, sus layoutProps y lo que escribió con useLayout()), y el manifiesto de rutas (RouteRecord, ErrorPageRecord, .meeko/manifests/routes.json y el de virtual:meeko-manifest/client) el campo opcional layout ({ name, file, props? }). Un payload o un manifiesto sin ellos siguen siendo válidos. Desde 1.6, el manifiesto de rutas gana el campo opcional middleware, con los archivos de los middleware con nombre de la ruta en orden de ejecución, y el campo opcional serverMiddleware, con la cadena completa de los de servidor, los globales y después los con nombre (el manifiesto de virtual:meeko-manifest/client no lo lleva); un manifiesto sin ellos sigue siendo válido: su ruta ejecuta los globales isomórficos y ningún middleware de servidor.
    • desde 1.6, el campo opcional url lleva la URL para la que se renderizó el documento (pathname y search, sin origen ni fragmento); el navegador hidrata useRoute() con ella y después toma la suya. Un payload sin url sigue siendo válido e hidrata con la URL del navegador.
  • Streaming. Los bloques <script type="application/json" data-meeko-deferred="N">, hijos directos de <body> y posteriores a #meeko-payload.
  • Endpoints. /api/... según los archivos de server/api, POST /_meeko/rpc/<ruta> con cuerpo JSON y /_meeko/socket/<canal>?room=<sala>, cuyos mensajes de texto son { event, payload }. Los errores tienen la forma { code, status, requestId }; desde 1.2, un 400 por entrada no válida puede añadir issues: [{ path, message }], y rateLimit responde 429 RATE_LIMITED con Retry-After en segundos. Desde 1.2, las mutaciones retrySafe y las llamadas de api.* y rpc.* con idempotencyKey envían la cabecera Idempotency-Key con un UUID como cadena de Structured Fields ("…"), y el servidor la expone validada en RequestContext.idempotencyKey.
  • Formularios nativos. Desde 1.7, un endpoint con form acepta application/x-www-form-urlencoded y multipart/form-data con los campos reservados return y _csrf, y responde 303 See Other (salvo 403 INVALID_ORIGIN o INVALID_CSRF, con el cuerpo de error de siempre). Los nuevos códigos INVALID_FORM y FILES_NOT_ALLOWED (400) llegan a la página en el resultado.
  • Cabeceras:
    • x-meeko-csrf en las mutaciones;
    • x-meeko-csrf-token y x-request-id en las respuestas;
    • cache-control: no-store y x-accel-buffering: no en los streams;
    • desde 1.7, en una página con cache: ETag débil (W/"…"), Cache-Control: public, max-age=… (con no-transform en un stream), Vary: Accept, Cookie y 304 ante If-None-Match.
  • Meta y cookie. La meta meeko-csrf-token y la cookie de sesión, que por defecto es __Host-meeko.sid (o meeko.sid sin Secure) y se configura con cookieName. Desde 1.4, con theme, la cookie del tema (meeko-theme por defecto, theme.cookie) y la clave de localStorage (theme.storageKey) guardan el nombre del tema o system en texto plano. Desde 1.7, la cookie de un solo uso de los formularios nativos se llama __Host-meeko-form (meeko-form sin HTTPS), es HttpOnly y SameSite=Lax y dura 60 segundos; su contenido y la clave meeko:form:<ruta> de sharedState que lo lleva al navegador son internos: sólo los interpretan el servidor y el navegador de la misma versión.
  • Tema. Desde 1.4, con theme, los documentos que renderiza el servidor llevan Vary: Cookie, Sec-CH-Prefers-Color-Scheme, Accept-CH: Sec-CH-Prefers-Color-Scheme y, cuando la elección es system, Critical-CH: Sec-CH-Prefers-Color-Scheme (sin clientHints, sólo Vary: Cookie). El payload de una página que lee el tema lleva lo que renderizó el servidor en la clave meeko:theme de sharedState.
  • Socket.
    • Los códigos de cierre documentados: 1003, 1008, 1009, 1011 y 1013, y desde 1.1 4401 (sesión rechazada) y 4403 (autorización del canal rechazada).
    • Las respuestas del handshake: 400, 403 INVALID_ORIGIN, 404 y las de los límites por HTTP. Desde 1.1, un rechazo de sesión (401) o de autorización del canal (403) completa el upgrade y cierra en seguida con 4401 o 4403, con un motivo fijo y sin llamar a los handlers. Véase framework.md.
  • Redis:
    • el canal <prefix>socket:<canal>:<sala> con el sobre { v: 1, event, payload };
    • el canal de revocaciones <prefix>revoked;
    • desde 1.2, los contadores de rateLimit en <prefix>ratelimit:<resumen> (el resumen de la ruta y el sujeto no es contrato);
    • los registros de sesión que escriben RedisSessionStore y SqlSessionStore;
    • el esquema de referencia de D1SessionDriver.schema() y, desde 1.7, el de D1SessionDriver.rateLimitSchema();
    • desde 1.7, las columnas de los contadores de rateLimit en SQL (key, window_ends_at y count) del esquema de referencia de PostgreSQL de framework.md. La clave del contador de la que sale key no es contrato: si cambia, los contadores vuelven a empezar.
  • Prerender. El índice prerender/manifest.json ({ version: 1, pages }); desde 1.3, cada página tiene el campo opcional scriptHashes, las fuentes CSP 'sha256-…' de sus scripts en línea, y desde 1.4 styleHashes, las de sus <style> en línea.

Un minor sólo puede añadir campos opcionales a estos formatos. Debe aceptar lo que escribió cualquier 1.x anterior y no cambia la versión de un formato. Durante un despliegue gradual pueden convivir instancias de dos minors con el mismo Redis o la misma base. El navegador y el servidor de una misma aplicación deben salir del mismo build; un payload con otra versión se rechaza con un mensaje explícito.

DevTools y editor#

  • Protocolo DevTools 1.
    • El hook window.__MEEKO_DEVTOOLS__ (sólo en desarrollo), con protocolVersion: 1.
    • Sus métodos snapshot, subscribe, select, highlight, setBreakpoint y setSignal.
    • Los tipos Devtools* de la raíz.
    • Un minor puede añadir campos o tipos de evento. Pasar a otra versión del protocolo requiere una major. La extensión de Chrome mantiene su propio número de versión.
    • Desde 1.1, DevtoolsInstance tiene el campo opcional signalSites (tipo DevtoolsSignalSite), con la declaración de cada señal. Es un campo añadido dentro del protocolo 1: un hook o una captura sin él siguen siendo válidos.
  • Opción devtools de meeko.config.ts (desde 1.8, aditiva). devtools: boolean | { panel?: boolean; position?: "bottom-right" | "bottom-left" | "top-right" | "top-left" } (tipo MeekoDevtoolsConfig) y position de mountDevtoolsPanel (tipo DevtoolsPanelPosition). Por defecto, false en toda la serie 1.x: sin la opción nada cambia. El panel sólo existe en meeko dev; que nunca llegue a meeko build, meeko start ni a una página prerenderizada forma parte del contrato. El aspecto del panel, el atajo Alt+Mayús+M y la clave meeko:devtools-panel de localStorage no forman parte del contrato y pueden cambiar en un minor.
  • Servidor de lenguaje.
    • El arranque con meeko lsp --stdio o meeko-language-server.
    • Las initializationOptions trusted, projectContracts y diagnosticsDelay.
    • Las capacidades LSP estándar que anuncia. Un minor puede añadir capacidades y acciones rápidas.

Fuera del contrato#

  • @meekojs/meeko/internal. Es el contrato entre el compilador y su runtime. Sólo está garantizado dentro de la misma versión exacta y puede cambiar en cualquier patch. No lo importe a mano. Lo mismo vale, desde 1.5, para @meekojs/meeko/builtins/*, los módulos compilados de <MeekoThemeToggle>, <MeekoAwait>, <MeekoPopover> y <MeekoSelect> que el compilador importa en los componentes que usan esas etiquetas.
  • El código y los archivos generados.
    • Lo que hay bajo .meeko/: tipos, manifiestos, entradas, tsconfig por ámbito, builds intermedios y chunks/.
    • El contenido de .meeko/styles/main.css, la hoja global procesada que escribe meeko dev para el editor: sigue a lo que Vite y PostCSS generan en desarrollo y puede cambiar en cualquier versión. Su ruta sí se mantiene durante 1.x, para que la configuración del editor que la nombra (css.styleSheets) siga funcionando. Véase framework.md.
    • Los módulos virtuales de Vite (virtual:meeko-*).
    • El JavaScript que emite el compilador.
    • Una entrada propia que importe esos módulos debe fijar la versión exacta de Meeko.
  • El puente del editor. El protocolo de valores en vivo entre el navegador, meeko dev y el editor (eventos meeko:live* de HMR, los descriptores .meeko/dev/editor-bridge-<puerto>.json, uno por servidor de desarrollo, la ruta /__meeko/editor-bridge y sus mensajes, incluido el campo alias) es sólo de desarrollo y no forma parte del contrato público: puede cambiar en cualquier versión. La extensión de VS Code ignora un descriptor con otra versión del protocolo. Véase devtools.md.
  • Los logs del servidor en el navegador. Lo que meeko dev añade para reenviar la consola del servidor (desde 1.3): los bloques <script type="application/json" data-meeko-server-logs>, el campo devServerLogs de los payloads de navegación, el evento meeko:server-logs de HMR, sus módulos /@id/__x00__meeko:server-logs* y la forma serializada de los valores. Sólo existen en desarrollo, nunca en meeko build ni en meeko start, y pueden cambiar en cualquier versión. Lo estable es la opción dev.serverLogs. Véase framework.md.
  • Las extensiones propias del servidor de lenguaje. La petición LSP meeko/executionZones y los modificadores de token semántico propios de Meeko (signal, computed, resource y reactive; modification es el estándar de LSP) son internos de la extensión de VS Code, que se publica junto al servidor. No son contrato público: pueden cambiar o desaparecer en un minor. Otro cliente LSP puede usarlos, pero debe tolerar esos cambios. Véase editors.md.
  • La presentación. El texto de mensajes de error y diagnósticos, el HTML y los estilos de los paneles DevTools, los títulos de CodeLens, hovers y zonas del editor, la salida de consola y el formato de los logs.
  • El repositorio. Los ejemplos, pruebas, benchmarks y scripts no se publican en npm.
  • El rendimiento. Las cifras de rendimiento y tamaño, que pueden mejorar o variar entre versiones.

Deprecación#

  1. Una API pública sólo se depreca en un minor y siempre con un reemplazo documentado, salvo que la funcionalidad desaparezca por completo; en ese caso se explica el motivo.
  2. La deprecación se anuncia en el CHANGELOG, en la documentación y con @deprecated en el JSDoc, para que el editor la marque.
  3. Donde es viable, el uso deprecado emite además un aviso:
    • un aviso de meeko check o del servidor de lenguaje para sintaxis y convenciones;
    • un aviso de consola en desarrollo, una vez por proceso, para APIs de runtime.
  4. Lo deprecado sigue funcionando durante toda la serie 1.x y sólo se retira en la siguiente major, cuya guía de migración lo recoge.

APIs deprecadas#

El sistema de temas (theme, cookie, script y regla de iconos de <head>, useTheme, useThemeToggle y POST /_meeko/theme) permanece en core. Sólo se retiran las tres etiquetas; la guía de 1.8 explica su reemplazo.

API Desde Reemplazo Se retira en
<MeekoPopover> (aviso MEEK121) 1.8 <UIPopover> de @meekojs/ui 0.2, con su módulo activo 2.0
<MeekoSelect> (aviso MEEK121) 1.8 <UISelect> de @meekojs/ui 0.2, con su módulo activo 2.0
<MeekoThemeToggle> (aviso MEEK121) 1.8 <UIThemeToggle> de @meekojs/ui 0.2, con su módulo activo 2.0
El nombre <CarpetaIndex> de un Carpeta/Index.meeko y sus tipos (aviso MEEK134) 1.8 <Carpeta> y sus tipos 2.0
useSocket(url, contract, options) de @meekojs/meeko/framework 1.1 useSocketClient, con la misma firma y los mismos tipos 2.0
Segmentos byId de api.* y de ApiClient (api.users.byId.get({ params: { id } })) 1.2 La llamada del segmento dinámico: api.users({ id }).get() 2.0
Un módulo importado (modules: [ui]) o [módulo, opciones] en modules de meeko.config.ts (aviso MEEK126) 1.8 El nombre del paquete o la ruta (modules: ['@meekojs/ui']) y las opciones en la clave de su meta.configKey (ui: { … }) 2.0
Carpetas de layer (con app/pages y sin módulo) listadas en modules de meeko.config.ts 1.2 La misma cadena en layers 2.0
Variables de entorno HOST y PUBLIC_ORIGIN 1.2 MEEKO_HOST y MEEKO_PUBLIC_ORIGIN. PORT no está obsoleta 2.0
html="..." con un string sin marcar (aviso MEEK107) 1.3 Un TrustedHtml: el HTML propio o saneado, marcado con trustHtml(…) donde se produce; para texto, {expr} 2.0, como error de meeko check
Un literal fuera de los valores de un atributo HTML (aviso MEEK122) 1.8 Uno de los valores del estándar (AttributeValues<Tag, Name>), o un string calculado 2.0, como error de meeko check
definePage en el <script> o la plantilla de un componente, e imports de valores de @meekojs/meeko/node, /bun, /deno, /password o /build allí (aviso MEEK109) 1.3 Las opciones de ruta en <script server> o en un sidecar *.route.ts; el código de servidor en <script server> o en server/, con import type para los tipos 2.0, como error de meeko check y del build
onCleanup con APIs del navegador en código que también corre en SSR, tras un await o al evaluar un módulo (avisos MEEK128, MEEK129 y MEEK130) 1.8 La limpieza dentro de onMounted (o protegida con typeof window), registrada antes del primer await, y dentro de un composable o de createRoot 2.0, como error de meeko check

El alias useSocket es la misma función que useSocketClient, así que no emite aviso de consola: el editor lo marca por su @deprecated. No afecta al autoimport useSocket("canal", opciones) de los clientes generados, que no está deprecado. Véase framework.md.

Los segmentos byId construyen las mismas peticiones que la llamada del segmento y tampoco emiten aviso de consola: el editor tacha cada método que se llama a través de ellos. La clave clientKey del manifiesto, createLocalCalls().api("users.byId", ...) y useApi no cambian ni están deprecados. Véase framework.md.

Un módulo importado o [módulo, opciones] en modules se sigue cargando igual, con las opciones en línea combinadas después de config[meta.configKey]; meeko dev, meeko build y meeko check escriben MEEK126 una vez por proceso y entrada, y el editor lo marca en meeko.config.*. Un módulo sin meta.configKey sólo recibe opciones con [módulo, opciones]: su autor debe declarar una antes de 2.0. @meekojs/i18n/module (y @meekojs/meeko/i18n/module) no están deprecados: son el subpath que resuelve '@meekojs/i18n'; sólo lo está importarlos en meeko.config.ts. Véase Actualizar de 1.7 a 1.8.

Una carpeta de layer listada en modules se sigue aplicando como layer, en la misma posición que antes, y meeko dev, meeko build y meeko check escriben un aviso una vez por proceso. extends no está deprecado: es un alias de layers. Véase framework.md.

MEEK128, MEEK129 y MEEK130 son sólo avisos de meeko check y del editor: en 1.x no cambian el SSR, la hidratación ni el código generado, y meeko check no falla por ellos. MEEK128 resuelve los nombres con el checker de TypeScript (una variable local window no es la global) y no avisa si la limpieza, o su registro, está protegida con typeof <global> o import.meta.env.SSR, ni dentro de onMounted, de un manejador de evento o de la continuación tras el primer await de navegador. En 2.0 los tres serán errores.

MEEK122 es sólo un aviso de meeko check, meeko build y el editor: en 1.x no cambia el SSR, la hidratación ni el código generado, y el valor se escribe igual. En 2.0, un literal fuera de AttributeValues<Tag, Name> en un atributo con valores cerrados será un error; un string calculado seguirá admitido.

MEEK107 es sólo un aviso de meeko check, meeko build y el servidor de lenguaje: en 1.x no cambia el SSR, la hidratación ni el código generado, y un string sin marcar se sigue insertando igual. En 2.0, el valor de html tendrá que ser asignable a TrustedHtml | null | undefined y el aviso pasará a ser un error. Los tipos literales de texto (html="'<br>'") siguen admitidos. Véase api.md.

MEEK109 es sólo un aviso de meeko check, meeko build y el servidor de lenguaje, y en 1.x el código generado no cambia. Esos usos compilan y se cargan en el navegador, pero no hacen nada (definePage fuera de <script server> y de los sidecars no configura la ruta) o fallan al llamarlos allí (las funciones de los adaptadores, de /password y de /build necesitan Node, Bun o Deno). Lo que ya fallaba antes de 1.3 es el error MEEK108 directamente: un autoimport de servidor no se resolvía en el bundle del navegador, @meekojs/meeko/server y /cloudflare no se pueden cargar allí (crean un AsyncLocalStorage de node:async_hooks), un import de server/ detenía meeko build, y las macros y onMounted no existen fuera del <script>. Véase framework.md.

HOST y PUBLIC_ORIGIN sólo se leen cuando falta MEEKO_HOST o MEEKO_PUBLIC_ORIGIN, y entonces meeko dev, start, preview y las salidas de meeko build escriben un aviso una vez por proceso que nombra la variable nueva. Véase framework.md.

Una vulnerabilidad que no se pueda corregir de forma compatible es la única excepción. En ese caso, el cambio mínimo necesario puede publicarse en un patch o un minor, con su explicación y migración en el CHANGELOG.

Cambios previstos para 2.0#

Además de retirar lo deprecado, 2.0 cambiará estos valores por defecto. En 1.x cada uno se puede activar antes con su opción.

Comportamiento Opción Desde Por defecto en 1.x Por defecto en 2.0
Elementos que reciben el atributo de <style scoped> css.scopeAttribute de meeko.config.ts (scopeAttribute de compile()) 1.2 "all": todos los de la plantilla "matched": sólo los que algún selector con ámbito puede seleccionar

Con "matched", una clase o un atributo que el script añade con la API del DOM a un elemento sin ref: ni on: deja de recibir los estilos con ámbito si ese elemento se quedó sin el atributo. Véase api.md.

Entornos soportados#

Entorno Versión
Node.js >=24 (engines de package.json). Ejecuta la CLI, el build, el prerender, Vite, el servidor de lenguaje y el adaptador Node. CI usa Node 24 en Windows y Ubuntu.
Bun Bun 1.x, con la salida meeko build --adapter bun. La suite de conformidad se ejecuta en CI con Bun 1.4.4.
Deno Deno 2.x, con la salida meeko build --adapter deno. La suite de conformidad se ejecuta en CI con Deno 2.9.8.
Cloudflare Workers wrangler.jsonc usa por defecto la fecha de compatibilidad 2026-10-19, configurable con server.compatibilityDate, y el flag nodejs_als (también vale nodejs_compat). La suite de conformidad se ejecuta con workerd 1.20261019.1 (Wrangler 4.152.0). Socket requiere Durable Objects, y las sesiones D1 o Redis por la API REST de Upstash.
TypeScript El paquete incluye TypeScript 7.0.2 para meeko check y los tipos. Las aplicaciones comprueban sus declaraciones con TypeScript 7.
Vite Peer opcional ^8.3.5.

Las versiones de Bun, Deno y workerd de la tabla son las que se prueban. Pueden actualizarse en un minor y el CHANGELOG lo indica. Dejar de admitir Node 24 requiere una major.

El runtime se publica compilado a ES2022, sin polyfills. Usa, entre otros, Object.hasOwn, Array.prototype.at, MutationObserver, fetch con cuerpos ReadableStream (navegación a rutas "stream") y WebSocket (Socket). El bundle cliente de una aplicación usa el objetivo por defecto de Vite 8, baseline-widely-available. Se admiten los navegadores que cumplen ese objetivo.

Las pruebas E2E se ejecutan con Chrome y con Chromium, Firefox y WebKit de Playwright. WebKit de Playwright no equivale a una prueba en Safari real.

Seguridad#

Las vulnerabilidades se informan de forma privada desde la pestaña Security de meekojs/meeko, nunca en un issue abierto. SECURITY.md detalla las versiones con soporte, qué incluir en el informe y los plazos de respuesta.

ESLint#

Durante Meeko 1.x, defineEslint ordena por defecto sólo los imports, con perfectionist/sort-imports y perfectionist/sort-named-imports, nivel warn, autofix y orden natural ascendente. En 2.0, esas dos reglas pasarán a error. perfectionist: true activa además el conjunto recommended-natural salvo sort-classes y sort-modules; esa lista la decide la versión de Perfectionist que fija el plugin. Puedes desactivarlas todas con perfectionist: false, apagar una con off o exigir error mediante rules; véase Ordenar con Perfectionist, que también lista los casos en los que --fix cambia el comportamiento. Los avisos pueden bloquear un CI que use --max-warnings 0.

¿Te sirvió esta página?

Añadir un comentario

Se envía al elegir «Sí» o «No». Solo se guardan la página, tu respuesta y el comentario: ni cookies ni datos tuyos.

0 de 2000 caracteres

Editar en GitHub: docs/stability.md