{
    "version": "1.10.1",
    "name": "No.JS",
    "tagline": "The HTML-First Reactive Framework",
    "description": "Build dynamic, reactive web applications using nothing but HTML attributes. No build step. No virtual DOM. No transpiler. No JSX. Just HTML.",
    "dependencies": "Zero",
    "cdn": "https://cdn.no-js.dev/",
    "github": "https://github.com/ErickXavier/no-js",
    "website": "https://no-js.dev/",
    "lsp": "https://lsp.no-js.dev/",
    "api": {
        "config": {
            "description": "Configure NoJS settings",
            "syntax": "NoJS.config(options)",
            "options": {
                "debug": "boolean — enable debug logging",
                "baseApiUrl": "string — default API base URL",
                "router": {
                    "useHash": "boolean — use hash-based routing (default: false)",
                    "base": "string — base path for the app (default: '/')",
                    "scrollBehavior": "string — scroll on navigate: 'top' (default), 'smooth', or 'preserve'",
                    "templates": "string — directory for file-based route templates (default: 'pages')",
                    "ext": "string — file extension for auto-resolved route templates (default: '.tpl')",
                    "focusBehavior": "string — 'none' (default) or 'auto' — moves focus to new content after SPA navigation for accessible navigation"
                },
                "headers": "object — default request headers",
                "timeout": "number — request timeout in milliseconds (default: 10000)",
                "retries": "number — automatic retries on failed requests (default: 0)",
                "retryDelay": "number — delay between retries in ms (default: 1000)",
                "credentials": "string — fetch credentials mode: 'same-origin' (default), 'include', 'omit'",
                "csrf": "object — CSRF token configuration ({ token, header })",
                "cache": "object — cache settings ({ strategy: 'none'|'memory'|'local'|'session', ttl: 300000 })",
                "templates": "object — template loading settings ({ cache: true })",
                "sanitize": "boolean — sanitize HTML in bind-html (default: true)",
                "dangerouslyDisableSanitize": "boolean — explicit opt-out of HTML sanitization (default: false)",
                "exprCacheSize": "number — maximum expression cache entries using LRU eviction (default: 500)",
                "maxEventListeners": "number — maximum event listeners per element (default: 100)",
                "devtools": "boolean — enable browser devtools panel (default: false)",
                "sanitizeHtml": "function|null — custom HTML sanitizer function, overrides built-in DOMParser-based sanitizer (default: null)",
                "stores": "object — define global stores at config time",
                "i18n": "object — i18n settings (see NoJS.i18n())"
            }
        },
        "init": {
            "description": "Initialize NoJS on a root element",
            "syntax": "NoJS.init(root?)",
            "notes": "Called automatically by CDN script. Only needed for npm/ESM usage. Returns a Promise that resolves when initialization is complete (including plugin init hooks). Fires plugins:ready event."
        },
        "directive": {
            "description": "Register a custom directive",
            "syntax": "NoJS.directive(name, handler)",
            "example": "NoJS.directive('tooltip', {\n  init(el, attrName, value) {\n    el.title = value;\n    el.style.cursor = 'help';\n  }\n});"
        },
        "filter": {
            "description": "Register a custom filter",
            "syntax": "NoJS.filter(name, fn)",
            "example": "NoJS.filter('initials', (name) => {\n  return name.split(' ').map(w => w[0]).join('').toUpperCase();\n});"
        },
        "validator": {
            "description": "Register a custom validator",
            "syntax": "NoJS.validator(name, fn)",
            "example": "NoJS.validator('phone', (value) => {\n  return /^\\+?[\\d\\s\\-()]{7,15}$/.test(value);\n});"
        },
        "i18n": {
            "description": "Configure internationalization",
            "syntax": "NoJS.i18n(options)",
            "options": {
                "loadPath": "string — URL pattern for locale files, e.g. 'locales/{locale}/{ns}.json'",
                "ns": "string[] — namespaces to load",
                "defaultLocale": "string — default locale",
                "fallbackLocale": "string — fallback locale",
                "persist": "boolean — persist locale to localStorage",
                "detectBrowser": "boolean — detect browser language",
                "locales": "object — inline locale definitions"
            }
        },
        "on": {
            "description": "Subscribe to global event bus",
            "syntax": "const unsub = NoJS.on(event, callback)",
            "notes": "Returns an unsubscribe function"
        },
        "interceptor": {
            "description": "Register request/response interceptors for fetch directives. Supports async functions. When registered inside a plugin, interceptors are tagged with the plugin name and receive redacted responses (sensitive headers/URLs stripped) unless the plugin is trusted.",
            "syntax": "NoJS.interceptor(type, fn)",
            "example": "NoJS.interceptor('request', (url, opts) => {\n  opts.headers['Authorization'] = 'Bearer ' + token;\n  return opts;\n});",
            "notes": "Request interceptors can return sentinel objects: { [NoJS.CANCEL]: true } to abort, { [NoJS.RESPOND]: data } to short-circuit. Response interceptors can return { [NoJS.REPLACE]: data } to replace response data. Each interceptor has a 5-second timeout."
        },
        "use": {
            "description": "Register a plugin with the framework",
            "syntax": "NoJS.use(plugin, options?)",
            "example": "NoJS.use({\n  name: 'auth',\n  install(NoJS, options) {\n    NoJS.interceptor('request', (url, opts) => {\n      opts.headers['Authorization'] = 'Bearer ' + token;\n      return opts;\n    });\n  }\n});",
            "notes": "Plugin object: { name, version?, capabilities?, install(NoJS, options), init?(NoJS), dispose?(NoJS) }. Named functions accepted as shorthand. Duplicate names rejected. Options: { trusted: true } grants unredacted header access."
        },
        "global": {
            "description": "Register a reactive global variable accessible as $name in all expressions",
            "syntax": "NoJS.global(name, value)",
            "example": "NoJS.global('apiVersion', '2.1');\n// Use in HTML: <span bind=\"$apiVersion\"></span>",
            "notes": "Name must start with a letter, contain only [a-zA-Z0-9_]. Reserved names (store, route, router, etc.) are rejected. Functions, eval, and Function references are rejected. Objects are sanitized (prototype keys stripped)."
        },
        "dispose": {
            "description": "Tear down the entire NoJS instance",
            "syntax": "await NoJS.dispose()",
            "notes": "Disposes plugins in reverse order (3s timeout each), clears interceptors, globals, stores, event bus, and router. Returns a Promise. After dispose, NoJS.init() can be called again."
        },
        "CANCEL": {
            "description": "Read-only Symbol sentinel for request interceptors — cancels the request",
            "syntax": "return { [NoJS.CANCEL]: true }",
            "notes": "Throws a DOMException with name 'AbortError'. Handled by the error template if present."
        },
        "RESPOND": {
            "description": "Read-only Symbol sentinel for request interceptors — short-circuits with mock data",
            "syntax": "return { [NoJS.RESPOND]: mockData }",
            "notes": "The value becomes the parsed response data, bypassing the network request."
        },
        "REPLACE": {
            "description": "Read-only Symbol sentinel for response interceptors — replaces response data",
            "syntax": "return { [NoJS.REPLACE]: transformedData }",
            "notes": "The value replaces the entire response data."
        },
        "store": {
            "description": "Access global stores",
            "syntax": "NoJS.store",
            "notes": "Returns the _stores object. Access individual stores: NoJS.store.auth"
        },
        "notify": {
            "description": "Notify all store watchers after external JavaScript mutations to NoJS.store",
            "syntax": "NoJS.notify()",
            "example": "NoJS.store.cart.items.push(newItem);\nNoJS.notify(); // UI updates"
        },
        "router": {
            "description": "Access the router instance",
            "syntax": "NoJS.router",
            "notes": "Provides push(), replace(), back(), forward(). push() and replace() return Promises."
        },
        "locale": {
            "description": "Get or set the current locale",
            "syntax": "NoJS.locale = 'pt'"
        },
        "baseApiUrl": {
            "description": "Get or set the API base URL",
            "syntax": "NoJS.baseApiUrl = 'https://api.example.com'"
        },
        "version": {
            "description": "Current NoJS version",
            "syntax": "NoJS.version",
            "value": "1.10.1",
            "note": "In template expressions, use window.NoJS.version (NoJS is not on the expression evaluator's allow-list)"
        }
    },
    "utilities": {
        "createContext": "Create a reactive context manually",
        "evaluate": "Evaluate an expression string in a context",
        "findContext": "Find the nearest reactive context for a DOM element",
        "processTree": "Process a DOM subtree to activate directives",
        "resolve": "Resolve a property path from a context"
    },
    "quickStart": "<!DOCTYPE html>\n<html>\n<head>\n  <script src=\"https://cdn.no-js.dev/\"></script>\n</head>\n<body base=\"https://jsonplaceholder.typicode.com\">\n  <div get=\"/users\" as=\"users\">\n    <div each=\"user in users\">\n      <h2 bind=\"user.name\"></h2>\n      <p bind=\"user.email\"></p>\n    </div>\n  </div>\n</body>\n</html>"
}