{
    "count": 1393,
    "items": [
        {
            "title": "1. Introduction",
            "page": "/guide/introduction",
            "anchor": "c1",
            "snippet": "Phlo is an integrated platform with its own full-stack language. You write .phlo source files; Phlo transpiles them to PHP, CSS and JavaScript you can open and "
        },
        {
            "title": "1.1. Philosophy",
            "page": "/guide/introduction",
            "anchor": "c1.1",
            "snippet": "Source first: you work in .phlo, not in generated PHP, CSS or JavaScript. A small runtime: the web entrypoint loads /opt/phlo/phlo.php and starts the app with p"
        },
        {
            "title": "1.2. Installation",
            "page": "/guide/introduction",
            "anchor": "c1.2",
            "snippet": "Phlo requires PHP 8.3 or higher; the CLI build runs on the same PHP. For production, FrankenPHP(https://frankenphp.dev) is the recommended runtime (built-in web"
        },
        {
            "title": "1.3. Project structure",
            "page": "/guide/introduction",
            "anchor": "c1.3",
            "snippet": "A typical app: php/, www/app.js, www/app.css and release/ are build output. Only change them through the source and rebuild."
        },
        {
            "title": "1.4. Entrypoint",
            "page": "/guide/introduction",
            "anchor": "c1.4",
            "snippet": "Dev entrypoint in www/app.php: Release entrypoint in release/www/app.php:"
        },
        {
            "title": "1.5. Build",
            "page": "/guide/introduction",
            "anchor": "c1.5",
            "snippet": "In development every request rebuilds changed sources automatically, so a browser reload is all a change needs. The CLI gives the same build explicitly, for scr"
        },
        {
            "title": "1.6. Web server",
            "page": "/guide/introduction",
            "anchor": "c1.6",
            "snippet": "The web server points to www/ for dev and to release/www/ for stage/production. Unknown paths are rewritten to app.php. For FrankenPHP (recommended), a single C"
        },
        {
            "title": "2. Configuration",
            "page": "/guide/configuration",
            "anchor": "c2",
            "snippet": "data/app.json describes the build: which resources are loaded, where release output goes, and which resources belong only in release or only in dev."
        },
        {
            "title": "2.1. Minimal configuration",
            "page": "/guide/configuration",
            "anchor": "c2.1",
            "snippet": "Resources refer to the Phlo runtime catalog. That is framework code, not a place for app-specific files. You write app code as .phlo in the app path; only gener"
        },
        {
            "title": "2.2. Resources",
            "page": "/guide/configuration",
            "anchor": "c2.2",
            "snippet": "Commonly used resources: Resource Purpose --- --- security/creds Credentials from env and ini files security/security Security headers security/token Token gene"
        },
        {
            "title": "2.3. Dev exclude",
            "page": "/guide/configuration",
            "anchor": "c2.3",
            "snippet": "In a local dev build you often want to leave out certain tracking and realtime resources: This applies to the dev build. The release build does not use this exc"
        },
        {
            "title": "2.4. Release",
            "page": "/guide/configuration",
            "anchor": "c2.4",
            "snippet": "The short form is enough: Phlo then writes release PHP to release/ and web assets to release/www/."
        },
        {
            "title": "2.5. Paths",
            "page": "/guide/configuration",
            "anchor": "c2.5",
            "snippet": "%app/ refers to the app path from phloapp(...). Keep path configuration in www/app.php and release/www/app.php as much as possible, so data/app.json stays about"
        },
        {
            "title": "2.6. Namespaces and bundles",
            "page": "/guide/configuration",
            "anchor": "c2.6",
            "snippet": "Every <style and <script block transpiles into a per-namespace bundle. ns=docs lands in www/docs.css and www/docs.js, ns=app,docs in both bundles, and blocks wi"
        },
        {
            "title": "2.7. Generated output",
            "page": "/guide/configuration",
            "anchor": "c2.7",
            "snippet": "Do not edit these files by hand: Change the .phlo source (any dev request rebuilds it; build::run does the same from the CLI), check with build::lint, and then "
        },
        {
            "title": "2.8. Credentials",
            "page": "/guide/configuration",
            "anchor": "c2.8",
            "snippet": "The security/creds resource resolves secrets (API keys, database logins, webhook tokens) and exposes them as %creds-.... It reads from two sources, so the same "
        },
        {
            "title": "3. Runtime config",
            "page": "/guide/runtime-config",
            "anchor": "c3",
            "snippet": "data/app.json describes the build, what gets transpiled. This chapter covers the runtime, what happens on each request. That config lives in www/app.php (and, f"
        },
        {
            "title": "3.1. Identity and host",
            "page": "/guide/runtime-config",
            "anchor": "c3.1",
            "snippet": "Key Default Purpose --- --- --- id none Free-form name for the app, used by the Phlo Control Center, the Phlo Dashboard and logs host null Canonical host used b"
        },
        {
            "title": "3.2. Modes: `build`, `debug`, `auth`, `thread`",
            "page": "/guide/runtime-config",
            "anchor": "c3.2",
            "snippet": "Key Default Effect --- --- --- build false Enables the build/lint/reflect CLI and lets Phlo detect and rebuild changed sources per request debug false Loads deb"
        },
        {
            "title": "3.3. Paths",
            "page": "/guide/runtime-config",
            "anchor": "c3.3",
            "snippet": "Only app is required. The rest falls back to subdirectories of app: Key Default Intended for --- --- --- app (required) App root data <app/data/ Config (app.jso"
        },
        {
            "title": "3.4. Control Center and WebSocket",
            "page": "/guide/runtime-config",
            "anchor": "c3.4",
            "snippet": "Key Default Effect --- --- --- control 'phlo' with build+debug, otherwise false URL prefix the control UI lives under. For example 'beheer' → /beheer; false = o"
        },
        {
            "title": "3.5. Composer autoload",
            "page": "/guide/runtime-config",
            "anchor": "c3.5",
            "snippet": "Want to use PHP packages from vendor/? Provide the path: Phlo then registers a lazy autoloader that only loads <composer/vendor/autoload.php when an unknown cla"
        },
        {
            "title": "3.6. Trace and CLI",
            "page": "/guide/runtime-config",
            "anchor": "c3.6",
            "snippet": "Key Default Effect --- --- --- trace false Enables trace mode, see the Trace chapter cli 'php-zts' (if ZTS) or 'php' Path to the PHP binary Phlo uses for subpro"
        },
        {
            "title": "3.7. Worker mode rules",
            "page": "/guide/runtime-config",
            "anchor": "c3.7",
            "snippet": "thread: true puts Phlo in long-running FrankenPHP worker mode. The runtime stays in memory between requests. Three rules you need to know: 1. No die() or exit()"
        },
        {
            "title": "3.8. Custom keys: your own constants",
            "page": "/guide/runtime-config",
            "anchor": "c3.8",
            "snippet": "Every extra named argument you pass to phloapp() automatically becomes a PHP constant. That is the way to declare app-wide paths or feature flags: Directly usab"
        },
        {
            "title": "3.9. Example: dev and release side by side",
            "page": "/guide/runtime-config",
            "anchor": "c3.9",
            "snippet": "Dev has build, debug, auth, the Control Center and trace. Release has worker mode (thread) and points the webroot/php output to release/. data, composer, langs "
        },
        {
            "title": "3.10. The request and response objects",
            "page": "/guide/runtime-config",
            "anchor": "c3.10",
            "snippet": "Two runtime objects carry every request. %req (read) and %res (write) are always available. %req computed properties: Property Contains --- --- %req-method HTTP"
        },
        {
            "title": "4. Syntax & Structure",
            "page": "/guide/language",
            "anchor": "c4",
            "snippet": "Phlo transpiles .phlo source files to plain PHP classes and generated assets. The syntax is PHP-like, but without semicolons at the end of statements."
        },
        {
            "title": "4.1. File structure",
            "page": "/guide/language",
            "anchor": "c4.1",
            "snippet": "A .phlo file can contain top-level controller code and nodes: route function prop static method view <style <script Example:"
        },
        {
            "title": "4.2. Statements and the line parser",
            "page": "/guide/language",
            "anchor": "c4.2",
            "snippet": "Statements end at a line break, not at a semicolon: This works because the transpiler appends a ; to every line and only removes it where the line clearly conti"
        },
        {
            "title": "4.3. Controller code",
            "page": "/guide/language",
            "anchor": "c4.3",
            "snippet": "Top-level code that is not a node becomes controller code of the generated class. That code runs when the instance is first retrieved. Use controller code for l"
        },
        {
            "title": "4.4. Instances",
            "page": "/guide/language",
            "anchor": "c4.4",
            "snippet": "Use %name to retrieve a Phlo object through the instance manager: The instance is created lazily and then reused within the request. Lesson. The transpiler rewr"
        },
        {
            "title": "4.5. Props",
            "page": "/guide/language",
            "anchor": "c4.5",
            "snippet": "Static prop: Computed prop: Computed props are generated as getters and can be read as a property:"
        },
        {
            "title": "4.6. Methods",
            "page": "/guide/language",
            "anchor": "c4.6",
            "snippet": "Single-line method: Multiline method:"
        },
        {
            "title": "4.7. Functions",
            "page": "/guide/language",
            "anchor": "c4.7",
            "snippet": "You define global functions with function: Use this sparingly. For app code a regular app class is usually better; only framework-wide helpers belong in a runti"
        },
        {
            "title": "4.8. Statics",
            "page": "/guide/language",
            "anchor": "c4.8",
            "snippet": "Static value: Static method: Call:"
        },
        {
            "title": "4.9. Strings and operators",
            "page": "/guide/language",
            "anchor": "c4.9",
            "snippet": "Strings and operators follow PHP: Use void for an empty string when that makes the intent clear."
        },
        {
            "title": "4.10. Named arguments",
            "page": "/guide/language",
            "anchor": "c4.10",
            "snippet": "Named arguments work as in PHP and keep calls readable:"
        },
        {
            "title": "4.11. Error handling and JSON responses",
            "page": "/guide/language",
            "anchor": "c4.11",
            "snippet": "error('message', $code = 500) aborts a request with a status. It throws (no return needed), and the engine renders it to fit the request: - an async/SPA request"
        },
        {
            "title": "5. Routing",
            "page": "/guide/routing",
            "anchor": "c5",
            "snippet": "Routing in Phlo maps a space-separated path + HTTP method to a target (usually a method). Routes from all .phlo files are collected; the router is activated wit"
        },
        {
            "title": "5.1. Basic form",
            "page": "/guide/routing",
            "anchor": "c5.1",
            "snippet": "Paths are written with spaces (no / in the route definition). Target: a direct call or statement block. Example: QUERY (RFC 10008) is a safe, idempotent read th"
        },
        {
            "title": "5.2. sync / async / both",
            "page": "/guide/routing",
            "anchor": "c5.2",
            "snippet": "Keyword Behavior ------------ ---------------------------------------------- (omitted) Sync only (regular HTTP) async Async only (requests from a Phlo frontend)"
        },
        {
            "title": "5.3. Variables",
            "page": "/guide/routing",
            "anchor": "c5.3",
            "snippet": "Phlo parses every path segment. Segments that start with $ are variables with extra capabilities: 4.3.1 Required (passed to the target) 4.3.2 Optional presence "
        },
        {
            "title": "5.4. Payload check with `@`",
            "page": "/guide/routing",
            "anchor": "c5.4",
            "snippet": "You specify exact body keys with a single @ and a comma-separated list. The router compares this 1-to-1 with the keys from %payload (exact set; order as supplie"
        },
        {
            "title": "5.5. Targets",
            "page": "/guide/routing",
            "anchor": "c5.5",
            "snippet": "Local method External class method (static) Static calls: parentheses required, even without arguments. Pass path variables explicitly. What a route may return "
        },
        {
            "title": "5.6. Activating the router",
            "page": "/guide/routing",
            "anchor": "c5.6",
            "snippet": "Routes are only matched after: Place this call, for example, in app.phlo (or another central controller) after your app initialization and before a fallback for"
        },
        {
            "title": "5.7. Recommended structure",
            "page": "/guide/routing",
            "anchor": "c5.7",
            "snippet": "Put routes at the top of each file. Keep path and method name logically aligned: Always pass variables in the target. Use both only when an endpoint deliberatel"
        },
        {
            "title": "6. Views",
            "page": "/guide/views",
            "anchor": "c6",
            "snippet": "Views live directly in .phlo files. A view transpiles to a PHP method that returns HTML. You render a view with view(...). One rule before everything else: a bl"
        },
        {
            "title": "6.1. Declaration",
            "page": "/guide/views",
            "anchor": "c6.1",
            "snippet": "Anonymous view: Named view: View with arguments: Calling it:"
        },
        {
            "title": "6.2. Multiline blocks",
            "page": "/guide/views",
            "anchor": "c6.2",
            "snippet": "A multiline view runs until an empty line. So don't put empty lines in the middle of view HTML."
        },
        {
            "title": "6.3. HTML shorthand",
            "page": "/guide/views",
            "anchor": "c6.3",
            "snippet": "Phlo supports compact id/class shorthand: This becomes: A trailing slash makes a tag self-closing in the source, and Phlo turns it into a normal open/close tag."
        },
        {
            "title": "6.4. Text and variables",
            "page": "/guide/views",
            "anchor": "c6.4",
            "snippet": "You can use plain variables and simple properties directly in text: For method calls, chained access, or expressions, use {{ ... }}: {( ... )} exists as a short"
        },
        {
            "title": "6.5. Translatable view text",
            "page": "/guide/views",
            "anchor": "c6.5",
            "snippet": "For static translatable text, use the language shorthand: With arguments: Use the shorthand for this; it is shorter and shows at a glance which source language "
        },
        {
            "title": "6.6. Attributes",
            "page": "/guide/views",
            "anchor": "c6.6",
            "snippet": "Attribute values without spaces or variables can go unquoted: With variables or expressions, use quotes: Attribute values interpolate $var, $this-prop and %inst"
        },
        {
            "title": "6.7. Control flow",
            "page": "/guide/views",
            "anchor": "c6.7",
            "snippet": "Use control-flow tags on their own lines: With if:"
        },
        {
            "title": "6.8. Rendering",
            "page": "/guide/views",
            "anchor": "c6.8",
            "snippet": "view(...) builds and renders the response, but it does not stop PHP execution. Return it from a route guard or let the routine end immediately afterwards. Build"
        },
        {
            "title": "6.9. Apply commands",
            "page": "/guide/views",
            "anchor": "c6.9",
            "snippet": "apply() accepts named arguments where each key is a DOM mutation or UI action. The runtime core provides the basics; resources can register extra commands via a"
        },
        {
            "title": "7. CSS",
            "page": "/guide/css",
            "anchor": "c7",
            "snippet": "Phlo uses a compact, semicolon-free CSS syntax inside <style blocks. You write rules with colons as separators: selector: property: value nested: A: B: property"
        },
        {
            "title": "7.1. `<style>` block",
            "page": "/guide/css",
            "anchor": "c7.1",
            "snippet": "Output (conceptually): A <style block can target one or more bundle namespaces with ns=: <style ns=docs transpiles into www/docs.css, <style ns=app,docs into bo"
        },
        {
            "title": "7.2. Chains & groups",
            "page": "/guide/css",
            "anchor": "c7.2",
            "snippet": "Chain with colons; group with commas, and the full context is applied to each item. Context: body Targets: h1 and p (with the glued :first-letter) The backslash"
        },
        {
            "title": "7.3. Media queries inside a selector",
            "page": "/guide/css",
            "anchor": "c7.3",
            "snippet": "You may write @media (…) inside the selector block; Phlo moves it to the right place and keeps the selector context: Output:"
        },
        {
            "title": "7.4. Variables",
            "page": "/guide/css",
            "anchor": "c7.4",
            "snippet": "Phlo supports CSS variables via $names. You can define variables in :root, or at any other level, but :root is the usual place for global theming. Output 👉 Phlo"
        },
        {
            "title": "7.5. Dynamic variables",
            "page": "/guide/css",
            "anchor": "c7.5",
            "snippet": "Phlo's frontend engine includes the DOM/CSS.var library, which lets you read and update defined $variables in CSS directly from JavaScript, via the global app.v"
        },
        {
            "title": "7.6. Full example",
            "page": "/guide/css",
            "anchor": "c7.6",
            "snippet": "Input Output"
        },
        {
            "title": "7.7. Best practices",
            "page": "/guide/css",
            "anchor": "c7.7",
            "snippet": "Use $variables for colors, spacing, and fonts; this makes theming and dark/light modes easy. Define global theme variables in :root. Use selector chains and gro"
        },
        {
            "title": "7.8. Icon sprites",
            "page": "/guide/css",
            "anchor": "c7.8",
            "snippet": "Point icons in data/app.json at one or more folders of PNG files and the build composes them into a single www/icons.png sprite plus the CSS to use them: Naming"
        },
        {
            "title": "8. ORM",
            "page": "/guide/orm",
            "anchor": "c8",
            "snippet": "Phlo ships with a powerful built-in ORM that lets you define database tables as classes. Models can be defined quickly via columns or in full via a declarative "
        },
        {
            "title": "8.1. Basics",
            "page": "/guide/orm",
            "anchor": "c8.1",
            "snippet": "An ORM model is a .phlo file with: @ class: the name of the model (and table) @ extends: model static table and columns or schema Optional: relations (parent, c"
        },
        {
            "title": "8.2. Defining models",
            "page": "/guide/orm",
            "anchor": "c8.2",
            "snippet": "7.2.1 Flat with columns (quick and lightweight) Use columns for simple tables: --- 7.2.2 With schema and field(...) (rich and declarative) With schema you defin"
        },
        {
            "title": "8.3. CRUD",
            "page": "/guide/orm",
            "anchor": "c8.3",
            "snippet": "Fetching: Creating: Editing and saving: Deleting: record(...) → single record (or null) records(...) → array of records (class instances) create(...) → insert +"
        },
        {
            "title": "8.4. Relational navigation",
            "page": "/guide/orm",
            "anchor": "c8.4",
            "snippet": "Relations are available as properties: Type Declaration Usage ------ -------------- ------------------ parent type: parent $shipment-user child type: child $use"
        },
        {
            "title": "8.5. Instance dynamics",
            "page": "/guide/orm",
            "anchor": "c8.5",
            "snippet": "Every record is a real instance of your model class. You can use props, methods and views to add virtual fields, computations or representations: Usage: Using a"
        },
        {
            "title": "8.6. Filtering and queries",
            "page": "/guide/orm",
            "anchor": "c8.6",
            "snippet": "All query methods accept named arguments and SQL-like filters: Supported: where, order, group, joins, caching and schema-aware columns."
        },
        {
            "title": "8.7. Caching and performance",
            "page": "/guide/orm",
            "anchor": "c8.7",
            "snippet": "The ORM uses internal buffers (objRecords, objLoaded) for relational lookups and optional APCu caching via: Or a number of seconds: Records and relations are lo"
        },
        {
            "title": "8.8. Multiple engines",
            "page": "/guide/orm",
            "anchor": "c8.8",
            "snippet": "Phlo is database-agnostic: a model resolves its engine from static DB, and there is no implicit default. Set the engine once for the whole app by injecting it o"
        },
        {
            "title": "8.9. Opt-in features: audit, validation, custom PK",
            "page": "/guide/orm",
            "anchor": "c8.9",
            "snippet": "An @ extends: model gives you CRUD + identity map + relations out of the box. Three additional opt-ins are enabled per model with a static flag. 8.9.1 Audit log"
        },
        {
            "title": "8.10. Function overview",
            "page": "/guide/orm",
            "anchor": "c8.10",
            "snippet": "Function / Property Type Description ------------------------------- ----------- --------------------------------- record(...) static Fetches 1 record (or null)"
        },
        {
            "title": "8.11. Best practices",
            "page": "/guide/orm",
            "anchor": "c8.11",
            "snippet": "Use columns for quick, simple models. Use schema for rich definitions and CMS integration. Define a view for string representations. Use props for virtual field"
        },
        {
            "title": "9. Instance Management",
            "page": "/guide/instances",
            "anchor": "c9",
            "snippet": "Phlo uses its own instance manager to initialize and reuse objects efficiently and predictably. This system determines when controller code runs, how instances "
        },
        {
            "title": "9.1. The basics",
            "page": "/guide/instances",
            "anchor": "c9.1",
            "snippet": "When you define a .phlo file, the build phase turns it into a class. Every call to an object via %name goes through the instance manager (phlo() in /phlo/phlo.p"
        },
        {
            "title": "9.2. Controller code",
            "page": "/guide/instances",
            "anchor": "c9.2",
            "snippet": "All code in a .phlo file that does not belong to route, prop, static, method, function, view, <style or <script is controller code. This code runs after instant"
        },
        {
            "title": "9.3. The role of `__handle()`: instance identity",
            "page": "/guide/instances",
            "anchor": "c9.3",
            "snippet": "handle() is an optional static you define yourself to control the identity of instances. It is interwoven with construct: the transpiler grafts your constructor"
        },
        {
            "title": "9.4. Lazy initialization",
            "page": "/guide/instances",
            "anchor": "c9.4",
            "snippet": "Because controller code runs only after construction, instances can reference each other without triggering unwanted recursive creation. Example: a.phlo: b.phlo"
        },
        {
            "title": "9.5. The obj base class: powertools",
            "page": "/guide/instances",
            "anchor": "c9.5",
            "snippet": "Every transpiled class extends obj, and obj is more than get/set. These are the tools you reach for when a class needs to behave dynamically. The ad-hoc value o"
        },
        {
            "title": "9.6. Best practices",
            "page": "/guide/instances",
            "anchor": "c9.6",
            "snippet": "Use controller code for initial setup, not for request-dependent logic. Place controller code at the top or directly below props for readability. construct capt"
        },
        {
            "title": "10. Tooling & CLI",
            "page": "/guide/tooling",
            "anchor": "c10",
            "snippet": "Phlo apps have a CLI layer for build, lint, release and reflection. Always use it through the app's dev entrypoint."
        },
        {
            "title": "10.1. Build commands",
            "page": "/guide/tooling",
            "anchor": "c10.1",
            "snippet": "build::lint should return an empty array: If lint reports errors, fix the .phlo source and build again. Never patch the generated PHP. Command Returns --- --- b"
        },
        {
            "title": "10.2. Reflect commands",
            "page": "/guide/tooling",
            "anchor": "c10.2",
            "snippet": "Reflection helps you understand an app before you change it: These commands return JSON and are intended for development environments with build: true. The same"
        },
        {
            "title": "10.3. General CLI dispatch",
            "page": "/guide/tooling",
            "anchor": "c10.3",
            "snippet": "build:: and reflect:: are just two examples of a more general mechanism. Phlo's CLI can call any static, method or function in your app: Three patterns: Pattern"
        },
        {
            "title": "10.4. Debug helpers",
            "page": "/guide/tooling",
            "anchor": "c10.4",
            "snippet": "With debug: true in phloapp(...), Phlo activates a set of helpers for inspection during dev. In production they are inert. Helper Purpose Behavior --- --- --- d"
        },
        {
            "title": "10.5. Workflow",
            "page": "/guide/tooling",
            "anchor": "c10.5",
            "snippet": "In a dev environment the Phlo Control Center (at /phlo) puts this whole loop in the browser: source, build, release and errors, with every file one click away: "
        },
        {
            "title": "10.6. Dev, stage and production",
            "page": "/guide/tooling",
            "anchor": "c10.6",
            "snippet": "Dev typically has: In build+debug mode, the built-in control UI lives at /phlo by default; use control: 'path' in phloapp(...) to pick a different path. Stage/p"
        },
        {
            "title": "10.7. HEAD and async",
            "page": "/guide/tooling",
            "anchor": "c10.7",
            "snippet": "The current runtime supports the normal HTTP methods, including HEAD. Async requests are handled by the frontend resource and use the same routes as sync reques"
        },
        {
            "title": "11. Translations",
            "page": "/guide/translations",
            "anchor": "c11",
            "snippet": "Phlo uses the lang resource for multilingual view text and translation. In views, you write static text preferably with the compact language shorthand. You writ"
        },
        {
            "title": "11.1. View shorthand",
            "page": "/guide/translations",
            "anchor": "c11.1",
            "snippet": "The standard form for static translatable text in a view is: The language code before the colon is the source language of the text. If %app-lang is the same lan"
        },
        {
            "title": "11.2. Helpers in code",
            "page": "/guide/translations",
            "anchor": "c11.2",
            "snippet": "The current lang resource provides global helper functions for Dutch and English: These helpers are useful in methods, props or controller code. In views, the s"
        },
        {
            "title": "11.3. Active language",
            "page": "/guide/translations",
            "anchor": "c11.3",
            "snippet": "The active language lives on %app-lang. A route can set it before the view is rendered: In links you can use %lang as an object value to display or process the "
        },
        {
            "title": "11.4. Translation cache",
            "page": "/guide/translations",
            "anchor": "c11.4",
            "snippet": "Translations are loaded per language from langs/ via %INI(%app-lang, langs). Missing entries are translated asynchronously based on their hash and read from the"
        },
        {
            "title": "11.5. Translation instructions",
            "page": "/guide/translations",
            "anchor": "c11.5",
            "snippet": "The lang resource feeds an AI translator, and you can steer it. The instructions property is extra context the translator receives with every translation: your "
        },
        {
            "title": "11.6. Best practices",
            "page": "/guide/translations",
            "anchor": "c11.6",
            "snippet": "Use {nl: ...} and {en: ...} in views for static text; use nl() / en() for anything with arguments. Use nl() and en() in PHP/Phlo code outside views. Set %app-la"
        },
        {
            "title": "12. Advanced",
            "page": "/guide/advanced",
            "anchor": "c12",
            "snippet": "Phlo stays deliberately modular. You can keep an app small and activate only the resources it needs, or combine multiple source paths and resource groups."
        },
        {
            "title": "12.1. App code and runtime resources",
            "page": "/guide/advanced",
            "anchor": "c12.1",
            "snippet": "App-specific code belongs in the app itself. Do not put it in /opt/phlo/resources/. /opt/phlo/resources/ is the Phlo runtime catalog: framework-wide resources t"
        },
        {
            "title": "12.2. Multiple source paths",
            "page": "/guide/advanced",
            "anchor": "c12.2",
            "snippet": "Keep the default simple: app source in the app path. Only add extra paths when a codebase genuinely needs to be shared. Path choices should stay predictable: ap"
        },
        {
            "title": "12.3. Integrating with existing PHP",
            "page": "/guide/advanced",
            "anchor": "c12.3",
            "snippet": "Use Phlo alongside existing PHP by loading the runtime and making the Phlo entrypoint responsible only for the routes the app handles. Existing static files kee"
        },
        {
            "title": "12.4. Security and visitors",
            "page": "/guide/advanced",
            "anchor": "c12.4",
            "snippet": "For public sites, the usual baseline is: For local dev you can exclude tracking: The visitors resource tracks engagement, not just hits. A small heartbeat scrip"
        },
        {
            "title": "12.5. Cookiewall: GDPR consent",
            "page": "/guide/advanced",
            "anchor": "c12.5",
            "snippet": "DOM/cookiewall is a built-in, subtle consent banner. Activate it in 3 steps: 1. Resource in data/app.json: 2. Banner in your layout: The banner only appears whe"
        },
        {
            "title": "12.5.1. Captcha",
            "page": "/guide/advanced",
            "anchor": "c12.5.1",
            "snippet": "security/captcha is a self-contained slider-puzzle captcha: no external service and no third-party scripts. The server picks a secret gap position, renders the "
        },
        {
            "title": "12.5.2. Social login",
            "page": "/guide/advanced",
            "anchor": "c12.5.2",
            "snippet": "security/social adds \"Sign in with Google\" (and Microsoft and Apple) on top of security/OAuth2: it builds the authorize URL and turns the callback code into a v"
        },
        {
            "title": "12.6. Worker mode",
            "page": "/guide/advanced",
            "anchor": "c12.6",
            "snippet": "In the classic request lifecycle, request state gets a fresh boundary for every request. With thread: true in phloapp(...), the runtime stays in memory between "
        },
        {
            "title": "12.7. Modifying resources without forking",
            "page": "/guide/advanced",
            "anchor": "c12.7",
            "snippet": "Sometimes you want a shared resource to behave just slightly differently in one app, without copying or changing that resource. From any .phlo file, you can inj"
        },
        {
            "title": "12.8. File metadata: the complete @ reference",
            "page": "/guide/advanced",
            "anchor": "c12.8",
            "snippet": "Every .phlo file can open with @ key: value lines. Any key is stored as file metadata; these have engine or tooling meaning: Key Effect --- --- @ class: Overrid"
        },
        {
            "title": "12.9. Best practices",
            "page": "/guide/advanced",
            "anchor": "c12.9",
            "snippet": "Keep entrypoints explicit; avoid hidden configuration. Let release output come from build::release. Never put credentials in source files. Use reflection to ver"
        },
        {
            "title": "13. Appendices",
            "page": "/guide/appendices",
            "anchor": "c13",
            "snippet": "These appendices give compact examples that match the production release."
        },
        {
            "title": "13.1. Basic route with a view",
            "page": "/guide/appendices",
            "anchor": "c13.1",
            "snippet": ""
        },
        {
            "title": "13.2. Route with a variable",
            "page": "/guide/appendices",
            "anchor": "c13.2",
            "snippet": ""
        },
        {
            "title": "13.3. Async form",
            "page": "/guide/appendices",
            "anchor": "c13.3",
            "snippet": ""
        },
        {
            "title": "13.4. Minimal app.json",
            "page": "/guide/appendices",
            "anchor": "c13.4",
            "snippet": ""
        },
        {
            "title": "13.5. Dev entrypoint",
            "page": "/guide/appendices",
            "anchor": "c13.5",
            "snippet": ""
        },
        {
            "title": "13.6. Release entrypoint",
            "page": "/guide/appendices",
            "anchor": "c13.6",
            "snippet": ""
        },
        {
            "title": "14. WebSocket",
            "page": "/guide/websocket",
            "anchor": "c14",
            "snippet": "Realtime in Phlo runs through Phlo Realtime, the WebSocket server built into the Phlo Daemon(/guide/daemon). One Node process owns the socket connections across"
        },
        {
            "title": "14.1. Streaming first",
            "page": "/guide/websocket",
            "anchor": "c14.1",
            "snippet": "Not everything realtime needs a socket. For one-way updates over a single request, chunk() (the chunk resource) opens a streaming response and flushes each call"
        },
        {
            "title": "14.2. What Phlo Realtime is",
            "page": "/guide/websocket",
            "anchor": "c14.2",
            "snippet": "Phlo Realtime is the WebSocket layer of the daemon (built on the ws library), not a separate process. The single daemon on port 3001 serves the whole stack: it "
        },
        {
            "title": "14.3. Installation",
            "page": "/guide/websocket",
            "anchor": "c14.3",
            "snippet": "The daemon is one Node service that lives outside the Phlo framework. Run it, point your reverse proxy at it, and that is the whole realtime setup. It takes a p"
        },
        {
            "title": "14.4. App hooks",
            "page": "/guide/websocket",
            "anchor": "c14.4",
            "snippet": "In your app source you define four functions; Phlo's websocket resource calls them if they exist. Put them in a file like app.ws.phlo: do not name the file webs"
        },
        {
            "title": "14.5. Auth flow",
            "page": "/guide/websocket",
            "anchor": "c14.5",
            "snippet": "The daemon authenticates at the handshake, before it accepts the socket: 1. The browser opens wss://<host/websocket. The cookies for that origin, including a to"
        },
        {
            "title": "14.6. Broadcasting from PHP",
            "page": "/guide/websocket",
            "anchor": "c14.6",
            "snippet": "wsCast() is a regular function (resource wsCast). It does a POST to the daemon's internal /message bridge, which pushes it on to the right sockets. Argument Def"
        },
        {
            "title": "14.7. Client side",
            "page": "/guide/websocket",
            "anchor": "c14.7",
            "snippet": "The client itself does nothing special. Add DOM/websocket to your resources in data/app.json: DOM/websocket injects a script that: - connects automatically to w"
        },
        {
            "title": "14.8. Mini example: presence",
            "page": "/guide/websocket",
            "anchor": "c14.8",
            "snippet": "Show \"who is online\" without polling. The server keeps no state; APCu counts sockets per host. On a PHP restart the cache empties by itself, which is fine, beca"
        },
        {
            "title": "14.9. Known limitations",
            "page": "/guide/websocket",
            "anchor": "c14.9",
            "snippet": "One-shot mode costs a PHP startup per event. Fine for inbox, presence and notifications; for high-frequency telemetry run the host as a release build so the dae"
        },
        {
            "title": "15. Tasks",
            "page": "/guide/tasks",
            "anchor": "c15",
            "snippet": "Phlo has a built-in cross-app task runner. Something triggers tasks::run every minute, a system cron entry or the Phlo Daemon(/guide/daemon); the tasks resource"
        },
        {
            "title": "15.1. Setup",
            "page": "/guide/tasks",
            "anchor": "c15.1",
            "snippet": "Three steps. 1. Activate the resource in data/app.json: 2. Describe your tasks in app.phlo: 3. Trigger tasks::run every minute. With the Phlo Daemon(/guide/daem"
        },
        {
            "title": "15.2. Schedule",
            "page": "/guide/tasks",
            "anchor": "c15.2",
            "snippet": "Pick exactly one scheduling key per task: Key Format Example --- --- --- every: PHP-readable duration string 'minute', '5 minutes', '2 hours', '1 day' daily: 'H"
        },
        {
            "title": "15.3. Callable (`do:`)",
            "page": "/guide/tasks",
            "anchor": "c15.3",
            "snippet": "The do: field accepts three forms: Type Example Is called as --- --- --- Closure fn() = external::pull() directly 'Class::method' 'account::cleanup' account::cl"
        },
        {
            "title": "15.4. State on disk (`data/tasks/`)",
            "page": "/guide/tasks",
            "anchor": "c15.4",
            "snippet": "tasks::run creates data/tasks/ automatically and guards each task with three files: File Contents When --- --- --- <name.last raw unix timestamp Per successful "
        },
        {
            "title": "15.5. Error flow",
            "page": "/guide/tasks",
            "anchor": "c15.5",
            "snippet": "No try/catch in tasks::run. A Throwable bubbles up to Phlo's framework exception handler and writes to data/errors.json, just like build errors do. The Phlo Con"
        },
        {
            "title": "15.6. Phlo Control Center",
            "page": "/guide/tasks",
            "anchor": "c15.6",
            "snippet": "The Phlo Control Center detects data/tasks/ automatically: A Tasks tab appears in the nav (only if the directory exists), right after Home. Per task: schedule ("
        },
        {
            "title": "15.7. Example",
            "page": "/guide/tasks",
            "anchor": "c15.7",
            "snippet": "Cron entry: Every minute tasks::run is invoked, sees that heartbeat is every: 'minute' and lastRun < 60s ago, and runs app::heartbeat(). The files data/tasks/he"
        },
        {
            "title": "15.8. Scheduling without cron",
            "page": "/guide/tasks",
            "anchor": "c15.8",
            "snippet": "If the Phlo Daemon(/guide/daemon) is running, it runs tasks::run for you every minute, so you do not need the per-app cron entry. Every app in the daemon's host"
        },
        {
            "title": "16. Daemon",
            "page": "/guide/daemon",
            "anchor": "c16",
            "snippet": "The Phlo Daemon is an optional Node sidecar (phlo-daemon.js): a generic engine that dispatches any Phlo target to a pool of persistent workers. Core Phlo works "
        },
        {
            "title": "16.1. What it is, and why it is optional",
            "page": "/guide/daemon",
            "anchor": "c16.1",
            "snippet": "Every routine in your app is already callable as a one-shot CLI process (php www/app.php <target args..., see the Tooling chapter). That is how async helpers, t"
        },
        {
            "title": "16.2. The worker protocol",
            "page": "/guide/daemon",
            "anchor": "c16.2",
            "snippet": "Each worker runs php <app.php phloserve, boots the app once, then answers newline-delimited JSON on stdin, one request in flight per worker (concurrency equals "
        },
        {
            "title": "16.3. The HTTP API",
            "page": "/guide/daemon",
            "anchor": "c16.3",
            "snippet": "The daemon binds 127.0.0.1 by default (local only; gate it at the network boundary). POST /dispatch takes {app, target, args?, stream?, async?}: Key Used by Mea"
        },
        {
            "title": "16.4. Configuration",
            "page": "/guide/daemon",
            "anchor": "c16.4",
            "snippet": "The daemon takes three arguments: a port, the PHP binary, and the host map. There is no pool sizing - the pool scales on demand. Argument Default Meaning --- --"
        },
        {
            "title": "16.5. Runtime helpers on the pool",
            "page": "/guide/daemon",
            "anchor": "c16.5",
            "snippet": "The async helpers all keep their one-shot subprocess behaviour by default and switch to the daemon pool when the app sets the optional daemon constant: With the"
        },
        {
            "title": "16.6. Scheduling: the cron alternative",
            "page": "/guide/daemon",
            "anchor": "c16.6",
            "snippet": "Every app in the host map gets the cron-replacing minute tick: the daemon runs tasks::run on each served app once a minute, the first run a minute after boot. T"
        },
        {
            "title": "16.7. Consumers",
            "page": "/guide/daemon",
            "anchor": "c16.7",
            "snippet": "Consumer Relationship --- --- Phlo Realtime The daemon's built-in WebSocket server; owns the sockets and runs the websocket::{auth,connect,receive,close} hooks "
        },
        {
            "title": "16.8. When to run it",
            "page": "/guide/daemon",
            "anchor": "c16.8",
            "snippet": "Run the daemon when an app needs WebSockets, when it schedules tasks without cron, or when a hot path fans out into many app calls per request. A small site tha"
        },
        {
            "title": "17. SEO",
            "page": "/guide/seo",
            "anchor": "c17",
            "snippet": "The seo resource centralises the search and sharing metadata an app needs: sitemap.xml, robots.txt, hreflang alternates, the canonical link, and a <head block o"
        },
        {
            "title": "17.1. Activation",
            "page": "/guide/seo",
            "anchor": "c17.1",
            "snippet": "Add the resource (it needs output): That alone serves two routes: Route Output --- --- GET /sitemap.xml A multilingual sitemap built from %app-pages and %app-la"
        },
        {
            "title": "17.2. The sitemap and hreflang",
            "page": "/guide/seo",
            "anchor": "c17.2",
            "snippet": "The sitemap iterates %app-pages and, for each page, emits an hreflang alternate per entry in %app-langs plus an x-default. Localised paths come from %app-slugs "
        },
        {
            "title": "17.3. The head block",
            "page": "/guide/seo",
            "anchor": "c17.3",
            "snippet": "%seo-head renders the conventional metadata set, all derived from existing props: - <meta name=description (only when a description is present); - Open Graph: o"
        },
        {
            "title": "17.4. robots.txt and the indexable constant",
            "page": "/guide/seo",
            "anchor": "c17.4",
            "snippet": "robots.txt is generated, and it is safe by default. Unless the app sets the indexable constant to a truthy value, the resource serves: So dev, stage and any non"
        },
        {
            "title": "17.5. Per-app and per-page overrides",
            "page": "/guide/seo",
            "anchor": "c17.5",
            "snippet": "The defaults cover most apps. Override the rest with the cross-class injection idiom (see the Advanced chapter), which sets the prop at build time: twitterCard "
        },
        {
            "title": "18. AI",
            "page": "/guide/ai",
            "anchor": "c18",
            "snippet": "Phlo bundles a number of AI providers (OpenAI, Claude, Gemini, DeepSeek, Grok) behind a single facade. You pick a model, Phlo picks the right engine. Streaming "
        },
        {
            "title": "18.1. Resources",
            "page": "/guide/ai",
            "anchor": "c18.1",
            "snippet": "Add to data/app.json: One file per provider. AI/AI is the facade, which routes to the right engine based on the model: Model contains Engine --- --- gpt-, o1-, "
        },
        {
            "title": "18.2. A single answer",
            "page": "/guide/ai",
            "anchor": "c18.2",
            "snippet": "Short question, one answer: Or even shorter, via the answer helper: answer() is built into AI/answer. It makes one call with a low temperature and returns only "
        },
        {
            "title": "18.3. Streaming to the DOM",
            "page": "/guide/ai",
            "anchor": "c18.3",
            "snippet": "This is where Phlo's apply() protocol really shines. An async route that writes token by token into an element: Set %res-streaming = true and every apply() flus"
        },
        {
            "title": "18.4. Tools (function calling)",
            "page": "/guide/ai",
            "anchor": "c18.4",
            "snippet": "Tool calls come back under $res-tools as an array of {name, args}. Phlo's facade normalizes the provider differences."
        },
        {
            "title": "18.5. Vision",
            "page": "/guide/ai",
            "anchor": "c18.5",
            "snippet": "Works with OpenAI, Claude, Gemini and Grok."
        },
        {
            "title": "18.6. Embeddings",
            "page": "/guide/ai",
            "anchor": "c18.6",
            "snippet": "The default model is provider-specific. For OpenAI it is text-embedding-3-small."
        },
        {
            "title": "18.7. Transcribe",
            "page": "/guide/ai",
            "anchor": "c18.7",
            "snippet": ""
        },
        {
            "title": "18.8. Safety",
            "page": "/guide/ai",
            "anchor": "c18.8",
            "snippet": "AI calls are expensive and non-deterministic. Cache aggressively, %apcu for session scope, JSONDB for longer TTLs. Filter user input before you put it in a prom"
        },
        {
            "title": "19. Trace",
            "page": "/guide/trace",
            "anchor": "c19",
            "snippet": "Phlo's trace mode is a runtime instrumentation layer that logs every call to a generated method, prop getter, static or native function with timing and argument"
        },
        {
            "title": "19.1. What trace does",
            "page": "/guide/trace",
            "anchor": "c19.1",
            "snippet": "With trace on, Phlo's transpiler injects one line at the top of every generated method: And Phlo loads functions.trace.php (a generated variant of functions.php"
        },
        {
            "title": "19.2. Enabling",
            "page": "/guide/trace",
            "anchor": "c19.2",
            "snippet": "In your dev entrypoint: The next build (any dev request, or build::run) injects the trace() calls; from then on every call is logged. Trace output goes to data/"
        },
        {
            "title": "19.3. What a trace contains",
            "page": "/guide/trace",
            "anchor": "c19.3",
            "snippet": "One JSON file per request with: Field Contents --- --- id Date-time + random suffix, also the file name path, method, route Request context ts, ms, count Start "
        },
        {
            "title": "19.4. Argument snipping",
            "page": "/guide/trace",
            "anchor": "c19.4",
            "snippet": "Full argument values would make the trace unreadable. Phlo snips: Type Becomes --- --- null, bool, int, float unchanged string 200 characters ... truncated at 2"
        },
        {
            "title": "19.5. Reading via the Phlo Control Center",
            "page": "/guide/trace",
            "anchor": "c19.5",
            "snippet": "The Phlo Control Center has a Trace tab that reads data/trace/index.json. The most recent trace is at the top; a selectbox opens older ones. Per trace you see a"
        },
        {
            "title": "19.6. Maintenance: `build::traceShadow`",
            "page": "/guide/trace",
            "anchor": "c19.6",
            "snippet": "functions.trace.php is a generated file. Whenever you add something to or change something in functions.php, regenerate it: This parses functions.php and inject"
        },
        {
            "title": "19.7. When to use it, when not",
            "page": "/guide/trace",
            "anchor": "c19.7",
            "snippet": "Do: - A request is slow \"somewhere\" and you want to know where the time goes - Exploring an unfamiliar app: sequence shows you the actual path through the code "
        },
        {
            "title": "20. For AI Agents",
            "page": "/guide/agents",
            "anchor": "c20",
            "snippet": "Phlo is built to be driven by AI agents, not just read by them. Every app exposes three CLI layers that let an agent work without prior knowledge of the codebas"
        },
        {
            "title": "20.1. The triad: understand, execute, build",
            "page": "/guide/agents",
            "anchor": "c20.1",
            "snippet": "Three layers, three planes. Reach for them in this order. Layer Plane Use it to --- --- --- reflect:: Static Understand structure without running anything: rout"
        },
        {
            "title": "20.2. Recommended workflow",
            "page": "/guide/agents",
            "anchor": "c20.2",
            "snippet": "1. Orient. reflect::context for a one-call snapshot: identity, route/view counts, loaded packages, recent errors. Then reflect::compactRoutes. 2. Explore. refle"
        },
        {
            "title": "20.3. reflect:: in practice",
            "page": "/guide/agents",
            "anchor": "c20.3",
            "snippet": "Output is JSON. reflect::context reads data/app.md, the human- and agent-written scratchpad that orients you on intent and status. See the Tooling chapter for t"
        },
        {
            "title": "20.4. phlo_eval in practice",
            "page": "/guide/agents",
            "anchor": "c20.4",
            "snippet": "phloeval '<phlo source' transpiles a string of Phlo statements and runs them in the live app. It is the runtime complement to reflection: where reflect:: reads "
        },
        {
            "title": "20.5. Power and danger",
            "page": "/guide/agents",
            "anchor": "c20.5",
            "snippet": "phloeval runs arbitrary code with full app privileges against the live app. In one call it can read password hashes, API keys and personal data, and in another "
        },
        {
            "title": "20.6. build:: in practice",
            "page": "/guide/agents",
            "anchor": "c20.6",
            "snippet": "Lint reports parse errors in the transpiled PHP. Always fix the .phlo source and rebuild; never patch the generated PHP. See the Tooling chapter for the full co"
        },
        {
            "title": "21. Philosophy",
            "page": "/guide/philosophy",
            "anchor": "c21",
            "snippet": "This chapter explains why Phlo is built the way it is: the deliberate trade-offs behind the parser, the transpiler, the runtime and the tooling. The short versi"
        },
        {
            "title": "21.1. One system, four layers",
            "page": "/guide/philosophy",
            "anchor": "c21.1",
            "snippet": "Phlo is not a library you add to a stack; it is the stack. Four layers share one design: The language: custom .phlo syntax that transpiles to readable PHP, CSS "
        },
        {
            "title": "21.2. A line-based parser, no AST",
            "page": "/guide/philosophy",
            "anchor": "c21.2",
            "snippet": "Phlo reads source line by line: a statement ends at a line ending, a node header opens a block, a blank line closes a view. There is no tokenizer building an ab"
        },
        {
            "title": "21.3. Transpiles to readable PHP",
            "page": "/guide/philosophy",
            "anchor": "c21.3",
            "snippet": ".phlo transpiles to plain PHP classes: one class per file, a header naming the source, and a per-class sourcemap recording PHP line to .phlo line. You can alway"
        },
        {
            "title": "21.4. The `obj` magic base class",
            "page": "/guide/philosophy",
            "anchor": "c21.4",
            "snippet": "Every transpiled class extends obj: arbitrary data via get/set, bound closures, and computed properties written as name() methods, called without parentheses an"
        },
        {
            "title": "21.5. `phlo()` as a tiny service registry",
            "page": "/guide/philosophy",
            "anchor": "c21.5",
            "snippet": "phlo('MySQL') returns a shared instance; in .phlo source, %MySQL transpiles to exactly that call. Each class can implement handle() to decide its own identity ("
        },
        {
            "title": "21.6. Rebuild on request in development",
            "page": "/guide/philosophy",
            "anchor": "c21.6",
            "snippet": "With build: true, Phlo checks whether any source changed and rebuilds before handling the request, throttled so the check is cheap in a hot loop. The edit-refre"
        },
        {
            "title": "21.7. Zero dependencies",
            "page": "/guide/philosophy",
            "anchor": "c21.7",
            "snippet": "The engine ships its own CSS transpiler, JS minifier, icon-sprite builder and SPA runtime, plus more than 150 bundled resources instead of a package tree. Compo"
        },
        {
            "title": "21.8. Self-hosted ownership",
            "page": "/guide/philosophy",
            "anchor": "c21.8",
            "snippet": "A Phlo app is a directory of files on a server you control. It runs as one FrankenPHP process, stays in memory in worker mode, and answers requests without a pe"
        },
        {
            "title": "21.9. Agent-first by design",
            "page": "/guide/philosophy",
            "anchor": "c21.9",
            "snippet": "SKILL.md is a complete language and build reference written so an AI agent can work without prior knowledge; the reflect:: CLI exposes routes, views, the parsed"
        },
        {
            "title": "21.10. What Phlo is not",
            "page": "/guide/philosophy",
            "anchor": "c21.10",
            "snippet": "Honesty about the boundaries: A young ecosystem. No marketplace of plugins, no Stack Overflow archive. The bundled resources and the guide are the ecosystem. Op"
        },
        {
            "title": "22. Performance",
            "page": "/guide/performance",
            "anchor": "c22",
            "snippet": "A Phlo app that follows the conventions in this guide starts from a strong Lighthouse position, because the architecture already does most of what the audits me"
        },
        {
            "title": "22.1. What the platform already does",
            "page": "/guide/performance",
            "anchor": "c22.1",
            "snippet": "Every view() response is complete server-rendered HTML. The first paint is real content, there is no hydration step and no client-side rendering to wait for, so"
        },
        {
            "title": "22.2. Server time: worker mode",
            "page": "/guide/performance",
            "anchor": "c22.2",
            "snippet": "Time to First Byte is the one Lighthouse metric that lives entirely on your server. In production, run the release build in worker mode (thread in the entry): t"
        },
        {
            "title": "22.3. The engine is not the cost",
            "page": "/guide/performance",
            "anchor": "c22.3",
            "snippet": "Phlo is a transpiler, not a runtime framework, and that is where the speed comes from. Your .phlo sources become plain PHP at build time: a route compiles to a "
        },
        {
            "title": "22.4. Images",
            "page": "/guide/performance",
            "anchor": "c22.4",
            "snippet": "Images decide most real-world LCP scores, and they stay your responsibility. The bundled img resource resizes, crops and converts with GD, so serve scaled files"
        },
        {
            "title": "22.5. Navigation: the SPA without the framework bill",
            "page": "/guide/performance",
            "anchor": "c22.5",
            "snippet": "With the DOM/link resource (or app.get() from your own script), an in-app navigation is one XHR returning apply() commands: the server sends only the DOM change"
        },
        {
            "title": "22.6. The other three audits",
            "page": "/guide/performance",
            "anchor": "c22.6",
            "snippet": "Accessibility is largely markup, and Phlo hands you the markup unwrapped: no generated wrappers, no synthetic buttons. Use the semantic element (<button, <nav, "
        },
        {
            "title": "22.7. Measuring honestly",
            "page": "/guide/performance",
            "anchor": "c22.7",
            "snippet": "Measure the release build on the production host, in a private window, against the public URL. Development mode skews every number: build: true may rebuild duri"
        },
        {
            "title": "23. Deployment & CI/CD",
            "page": "/guide/deployment",
            "anchor": "c23",
            "snippet": "A Phlo app is static once it is built. build::release transpiles your .phlo sources into plain PHP, CSS and JS under a release/ directory (see the Tooling chapt"
        },
        {
            "title": "23.1. The release build",
            "page": "/guide/deployment",
            "anchor": "c23.1",
            "snippet": "The unit you deploy is the release/ tree, produced by: It runs the release hooks, transpiles every .phlo to release/.php, and writes the asset bundles to releas"
        },
        {
            "title": "23.2. Continuous integration",
            "page": "/guide/deployment",
            "anchor": "c23.2",
            "snippet": "Run CI on every push and pull request. A Phlo CI job does four things: 1. Check out your app and the Phlo engine according to the chosen policy, and record both"
        },
        {
            "title": "23.3. The deploy gate",
            "page": "/guide/deployment",
            "anchor": "c23.3",
            "snippet": "Deployment is a separate, manually triggered job (a workflowdispatch on GitHub Actions, or your provider's equivalent). The manual trigger is the approval step:"
        },
        {
            "title": "23.4. A safe deploy on the server",
            "page": "/guide/deployment",
            "anchor": "c23.4",
            "snippet": "Ship the artifact over SSH to a single restricted command on the server; never hand CI a shell. A minimal safe-deploy receiver: 1. Unpacks the incoming release/"
        },
        {
            "title": "23.5. Recording what is live",
            "page": "/guide/deployment",
            "anchor": "c23.5",
            "snippet": "CI writes a small build.json into the release artifact, recording the app commit, resolved engine commit and build timestamp. The receiver verifies it and copie"
        },
        {
            "title": "23.6. Automate the mechanics, not the decision",
            "page": "/guide/deployment",
            "anchor": "c23.6",
            "snippet": "Everything above automates the safe parts: building, testing, shipping identical bytes, the atomic swap, the healthcheck, the rollback. What it deliberately lea"
        },
        {
            "title": "24. PWA & native apps",
            "page": "/guide/mobile",
            "anchor": "c24",
            "snippet": "A Phlo app is already a website. The same build ships as an installable Progressive Web App with no extra tooling, and the same web output can be wrapped in a t"
        },
        {
            "title": "24.1. The manifest",
            "page": "/guide/mobile",
            "anchor": "c24.1",
            "snippet": "A PWA needs a manifest describing the app: name, icons, how it launches. Phlo ships a manifest resource that owns the route and the serving; your app only decla"
        },
        {
            "title": "24.2. The service worker",
            "page": "/guide/mobile",
            "anchor": "c24.2",
            "snippet": "Installable also means offline-capable. Serve the worker itself through a route so its cache name can carry the app version, invalidating old caches on every de"
        },
        {
            "title": "24.3. What Capacitor adds",
            "page": "/guide/mobile",
            "anchor": "c24.3",
            "snippet": "A PWA covers install-to-home-screen, offline shell, and push if you add it, all from the same deploy. Capacitor(https://capacitorjs.com) covers what a browser t"
        },
        {
            "title": "24.4. One app, three shapes",
            "page": "/guide/mobile",
            "anchor": "c24.4",
            "snippet": "A Phlo app is a website first. It becomes installable with a manifest and a service worker, no build step changes. It becomes a store listing with Capacitor, no"
        },
        {
            "title": "25. Integrations",
            "page": "/guide/integrations",
            "anchor": "c25",
            "snippet": "Most apps eventually talk to something outside themselves: a chat service, a calendar, an accounting back-end, a card terminal, a payment processor. Phlo ships "
        },
        {
            "title": "25.1. Connectors",
            "page": "/guide/integrations",
            "anchor": "c25.1",
            "snippet": "A connector is a small class over one third-party REST API. Every connector extends Connector, reads its own credentials section, and returns the same result sh"
        },
        {
            "title": "25.2. Payments",
            "page": "/guide/integrations",
            "anchor": "c25.2",
            "snippet": "Stripe The Stripe resource is a thin layer over the official stripe/stripe-php SDK (add it with Composer). Call Stripe::boot() once with your secret key, then u"
        },
        {
            "title": "1. Setup",
            "page": "/learn/setup",
            "anchor": "c1",
            "snippet": "In this tutorial you build Phlo Poll: a small poll app that asks \"Which stack wins?\". You start from an empty folder and end with a styled, multilingual, realti"
        },
        {
            "title": "1.1. Install with Docker",
            "page": "/learn/setup",
            "anchor": "c1.1",
            "snippet": "The fastest way to run Phlo is the official Docker image. It contains PHP, the FrankenPHP web server, and the Phlo engine at /phlo. Scaffold a new app into ./ap"
        },
        {
            "title": "1.2. Start the server",
            "page": "/learn/setup",
            "anchor": "c1.2",
            "snippet": "Run the same image as a web server: Open http://localhost in your browser. You see the placeholder home page with the app name and your one-line purpose. That p"
        },
        {
            "title": "1.3. Your first route",
            "page": "/learn/setup",
            "anchor": "c1.3",
            "snippet": "Every .phlo file transpiles to exactly one PHP class, named after the file. Create app/poll.phlo (next to app.phlo) with one line: Three things to notice: Route"
        },
        {
            "title": "1.4. Build and check",
            "page": "/learn/setup",
            "anchor": "c1.4",
            "snippet": "In development (build: true) Phlo rebuilds changed sources on every request, so reloading the browser is usually enough. The CLI gives you the same build explic"
        },
        {
            "title": "2. Views",
            "page": "/learn/views",
            "anchor": "c2",
            "snippet": "Views live directly in your .phlo files and transpile to PHP methods that return HTML. In this chapter you replace the hello route with the first real poll page"
        },
        {
            "title": "2.1. From route to view",
            "page": "/learn/views",
            "anchor": "c2.1",
            "snippet": "Replace the contents of poll.phlo: Top to bottom: prop question is a static property on the poll class. The route maps GET /poll to the home method. home calls "
        },
        {
            "title": "2.2. Dot shorthand",
            "page": "/learn/views",
            "anchor": "c2.2",
            "snippet": "Look at the opening tag again: Phlo expands id and .class directly on the tag name: You can stack classes (<div.card.wide) and a trailing slash self-closes a ta"
        },
        {
            "title": "2.3. A blank line closes the view",
            "page": "/learn/views",
            "anchor": "c2.3",
            "snippet": "A view block ends at the first blank line. Everything after that blank line is controller code again. This is by design: it is how you put several views in one "
        },
        {
            "title": "2.4. Variables, expressions, and composition",
            "page": "/learn/views",
            "anchor": "c2.4",
            "snippet": "Inside a view you have three levels of expression: $var and $this-prop for plain values {{ expr }} for method calls and anything with arguments { expr } for the"
        },
        {
            "title": "3. CSS",
            "page": "/learn/css",
            "anchor": "c3",
            "snippet": "Phlo has its own compact CSS syntax: no semicolons, no braces required for single rules, and $variables that transpile to CSS custom properties. In this chapter"
        },
        {
            "title": "3.1. A style file",
            "page": "/learn/css",
            "anchor": "c3.1",
            "snippet": "Style can live in any .phlo file, but a dedicated file keeps things tidy. Create poll.style.phlo: Every <style block transpiles into a bundle. Without an ns= at"
        },
        {
            "title": "3.2. One declaration per line",
            "page": "/learn/css",
            "anchor": "c3.2",
            "snippet": "The CSS parser treats every line as one complete declaration, and the colon does double duty as both selector nesting and property separator: h1: margin: 0 0 24"
        },
        {
            "title": "3.3. Theme variables",
            "page": "/learn/css",
            "anchor": "c3.3",
            "snippet": "$name in a style block becomes the CSS custom property --name, and every place you use it becomes var(--name). Define the theme once in :root and use it everywh"
        },
        {
            "title": "3.4. Check the output",
            "page": "/learn/css",
            "anchor": "c3.4",
            "snippet": "Look at what the transpiler produced: You see regular CSS: :root { --text: f4f4f5; ... }, button { background: var(--primary); ... }. Phlo writes the semicolons"
        },
        {
            "title": "4. Data",
            "page": "/learn/data",
            "anchor": "c4",
            "snippet": "The poll needs storage. Phlo's ORM defines a table as a class, and for a tutorial app the JSON file driver is perfect: no database server, no credentials, one J"
        },
        {
            "title": "4.1. The model file",
            "page": "/learn/data",
            "anchor": "c4.1",
            "snippet": "Create type.poll.phlo: Line by line: @ extends: model makes this a model class. The file name type.poll.phlo gives the class its name, typepoll (dots become und"
        },
        {
            "title": "4.2. Switch on the database resources",
            "page": "/learn/data",
            "anchor": "c4.2",
            "snippet": "Resources are opt-in. Open data/app.json and list what the model layer needs: DB/model is the ORM, DB/JSONDB the file driver, DB/JSON.result its result wrapper,"
        },
        {
            "title": "4.3. Seed the options",
            "page": "/learn/data",
            "anchor": "c4.3",
            "snippet": "The poll should create its own options on first run. In poll.phlo, replace the options prop and the home method with: typepoll::records() fetches all records; i"
        },
        {
            "title": "4.4. Render the records",
            "page": "/learn/data",
            "anchor": "c4.4",
            "snippet": "Now replace the static buttons with records, and add a results section. The full poll.phlo view section: The share method turns a vote count into a percentage; "
        },
        {
            "title": "5. Voting",
            "page": "/learn/voting",
            "anchor": "c5",
            "snippet": "A poll without votes is a list. In this chapter every option becomes a form, a POST route receives the vote, the count goes up, and a redirect re-renders the pa"
        },
        {
            "title": "5.1. The vote forms",
            "page": "/learn/voting",
            "anchor": "c5.1",
            "snippet": "In poll.phlo, turn each button in the choices view into a tiny form: Each form posts to its own URL: /poll/vote/1, /poll/vote/2, and so on. The id travels in th"
        },
        {
            "title": "5.2. The POST route",
            "page": "/learn/voting",
            "anchor": "c5.2",
            "snippet": "Add the route to poll.phlo, near the GET route: Read it top to bottom: route POST poll vote $id matches POST /poll/vote/<id. A $segment in the path is a variabl"
        },
        {
            "title": "5.3. Reading a payload instead",
            "page": "/learn/voting",
            "anchor": "c5.3",
            "snippet": "The id in the path is one style. The other is a request body, which you read through %payload. First enable the resource in data/app.json: A payload-based versi"
        },
        {
            "title": "5.4. What you have now",
            "page": "/learn/voting",
            "anchor": "c5.4",
            "snippet": "The build keeps itself current on every request; run the checks explicitly if you want the confirmation: Both settle at . You have a complete, working poll: ser"
        },
        {
            "title": "6. Async",
            "page": "/learn/async",
            "anchor": "c6",
            "snippet": "Now make voting instant. Phlo's frontend runtime intercepts forms and links marked async, posts them in the background, and applies the server's DOM commands to"
        },
        {
            "title": "6.1. Switch on the frontend",
            "page": "/learn/async",
            "anchor": "c6.1",
            "snippet": "Add the form resource to data/app.json: The core frontend runtime is generated in www/app.js, which view() includes, and DOM/form teaches it to submit forms. Th"
        },
        {
            "title": "6.2. Routes have a sync side and an async side",
            "page": "/learn/async",
            "anchor": "c6.2",
            "snippet": "Every route is sync-only unless you say otherwise: no modifier: regular HTTP requests only async: requests from the Phlo frontend only both: both Change the vot"
        },
        {
            "title": "6.3. Mark the form as async",
            "page": "/learn/async",
            "anchor": "c6.3",
            "snippet": "The frontend only intercepts elements with the async class. One character in the choices view: <form.async ... is the dot shorthand for class=\"async\". The same "
        },
        {
            "title": "6.4. What apply() actually sends",
            "page": "/learn/async",
            "anchor": "c6.4",
            "snippet": "apply() returns JSON commands that the frontend executes against the DOM. Watch it happen: outer replaces the element's outerHTML; the server rendered the resul"
        },
        {
            "title": "7. Translations",
            "page": "/learn/translations",
            "anchor": "c7",
            "snippet": "Phlo treats translation as a rendering concern: you write view text in your source language, mark it, and the lang resource serves the right language at request"
        },
        {
            "title": "7.1. Source-language markers",
            "page": "/learn/translations",
            "anchor": "c7.1",
            "snippet": "Static view text gets a marker keyed by the language the text is written in. This app's source language is English, so the key is en: {en: ...} transpiles to a "
        },
        {
            "title": "7.2. Configure the language layer",
            "page": "/learn/translations",
            "anchor": "c7.2",
            "snippet": "Three small pieces of configuration. First, the resources in data/app.json (the lang resource brings cookie detection, INI file caching, and AI-backed translati"
        },
        {
            "title": "7.3. A /nl route prefix",
            "page": "/learn/translations",
            "anchor": "c7.3",
            "snippet": "The active language lives on %app-lang. Set it from the URL with a second route in poll.phlo: $lang:nl is a value-list segment: it only matches when the first p"
        },
        {
            "title": "7.4. The translation cache",
            "page": "/learn/translations",
            "anchor": "c7.4",
            "snippet": "Translations live in langs/<lang.ini, one line per string, keyed by a hash of the source text. When a marked string has no entry for the active language, the pa"
        },
        {
            "title": "8. Realtime",
            "page": "/learn/realtime",
            "anchor": "c8",
            "snippet": "One gap remains: when someone else votes, your tab does not move. In this final chapter the server pushes results to every open browser through the Phlo Daemon,"
        },
        {
            "title": "8.1. The Phlo Daemon, the optional Node service",
            "page": "/learn/realtime",
            "anchor": "c8.1",
            "snippet": "WebSockets in Phlo run through the Phlo Daemon: one Node process that owns the sockets and runs your PHP hooks. It serves all your apps on one port and routes c"
        },
        {
            "title": "8.2. The hooks",
            "page": "/learn/realtime",
            "anchor": "c8.2",
            "snippet": "Your app reacts to socket events through plain functions. Create poll.ws.phlo: wsConnect runs after the handshake; return true to accept. wsReceive runs for eve"
        },
        {
            "title": "8.3. Broadcast on vote",
            "page": "/learn/realtime",
            "anchor": "c8.3",
            "snippet": "wsCast() posts to Phlo Realtime, which pushes to connected browsers. The payload is the same command language as apply(). One line in the vote route: wsTarget: "
        },
        {
            "title": "8.4. Ship it",
            "page": "/learn/realtime",
            "anchor": "c8.4",
            "snippet": "Time to produce a deployable build. Add a release target to data/app.json: Then build it: build::release produces a minified production build: PHP into release/"
        },
        {
            "title": "8.5. Where next",
            "page": "/learn/realtime",
            "anchor": "c8.5",
            "snippet": "You built a full-stack app in eight short files: a model, a controller, a style sheet, a hooks file, and four config touches. Routing, views, CSS, ORM, async DO"
        },
        {
            "title": "1. CMS overview",
            "page": "/cms/overview",
            "anchor": "c1",
            "snippet": "The Phlo CMS turns a model schema into a complete admin interface. You declare what your data looks like; the CMS generates the navigation, list views, record v"
        },
        {
            "title": "1.1. What it generates",
            "page": "/cms/overview",
            "anchor": "c1.1",
            "snippet": "From a handful of models, each declaring a schema, the CMS produces: a sidebar built from your menu, a list view per model (with counts, child links and paginat"
        },
        {
            "title": "1.2. How an app declares itself",
            "page": "/cms/overview",
            "anchor": "c1.2",
            "snippet": "An app mounts the CMS with two declarations: which models exist, and how they appear in the sidebar. The CMS framework is loaded as a resource path; your models"
        },
        {
            "title": "1.3. The demo",
            "page": "/cms/overview",
            "anchor": "c1.3",
            "snippet": "The phlo-demo-cms repository is a blog on SQLite: five models, every relation type, image uploads and a Faker seeder, a complete app you can copy and re-seed in"
        },
        {
            "title": "1.4. Generate a starting model",
            "page": "/cms/overview",
            "anchor": "c1.4",
            "snippet": "Picking a database, a table name and a list of fields by hand is most of a schema. The CMS builder(/cms-builder) turns that choice into a ready model file: pick"
        },
        {
            "title": "2. Schemas and fields",
            "page": "/cms/schema",
            "anchor": "c2",
            "snippet": "!A create form rendered straight from the schema(/illustrations/cms-create.png) A model declares its shape with a schema: a map of column name to a field(...) o"
        },
        {
            "title": "2.1. Field types",
            "page": "/cms/schema",
            "anchor": "c2.1",
            "snippet": "Type Stores / renders --- --- text a string (set multiline: true for a textarea) email, password, number, date, datetime typed inputs bool a checkbox select, mu"
        },
        {
            "title": "2.2. Common field options",
            "page": "/cms/schema",
            "anchor": "c2.2",
            "snippet": "title: the label shown in the admin. required: true: validated on write. list: false / record: false: hide the field from the list or record view. length, defau"
        },
        {
            "title": "2.3. The field() helper",
            "page": "/cms/schema",
            "anchor": "c2.3",
            "snippet": "field(type: 'x', ...) is shorthand: it resolves to the fieldx resource with your arguments. Loading a field type is just listing fields/x in your resources, so "
        },
        {
            "title": "3. Relations",
            "page": "/cms/relations",
            "anchor": "c3",
            "snippet": "The CMS models three relation types, all declared as fields and all derived automatically from the schema. Type Meaning Example --- --- --- parent belongs-to-on"
        },
        {
            "title": "3.1. Parent and child",
            "page": "/cms/relations",
            "anchor": "c3.1",
            "snippet": "A parent field stores the related row's id and resolves to the related model when you read it: The column convention matters. The foreign-key column is named af"
        },
        {
            "title": "3.2. Many-to-many",
            "page": "/cms/relations",
            "anchor": "c3.2",
            "snippet": "A many field uses a join table: The join table's columns default to the two short names: articletopic(article, topic). By default the related rows show read-onl"
        },
        {
            "title": "3.3. Reading relations",
            "page": "/cms/relations",
            "anchor": "c3.3",
            "snippet": "Once declared, relations are plain property reads: $article-author is the author model, $article-author-name its name, and child counts (12 Comments) appear aut"
        },
        {
            "title": "4. The admin and API",
            "page": "/cms/admin",
            "anchor": "c4",
            "snippet": "With models and a menu declared, the CMS serves a full admin and a matching REST API."
        },
        {
            "title": "4.1. URLs",
            "page": "/cms/admin",
            "anchor": "c4.1",
            "snippet": "Path Shows --- --- /articles the list for a model (the menu uri) /article/5 one record and its relations /create/article the create form /change/article/5 the e"
        },
        {
            "title": "4.2. The write API",
            "page": "/cms/admin",
            "anchor": "c4.2",
            "snippet": "Every model is writable over POST, PUT, PATCH and DELETE, including nested child operations; reads are the rendered list and record pages. Writes are CSRF-prote"
        },
        {
            "title": "4.3. Look and feel",
            "page": "/cms/admin",
            "anchor": "c4.3",
            "snippet": "The CMS ships themes and transitions as resources. An app picks defaults with prop theme and prop trans, and users can switch at runtime. Because the whole admi"
        },
        {
            "title": "4.4. Auth is optional",
            "page": "/cms/admin",
            "anchor": "c4.4",
            "snippet": "The CMS does not force authentication. The production Dashboard layers auth on top (login, roles, per-module permissions); a small internal tool, like the demo,"
        },
        {
            "title": "4.5. Dashboard widgets",
            "page": "/cms/admin",
            "anchor": "c4.5",
            "snippet": "The CMS home is not just a menu. Every model can pin widgets on it, so the dashboard opens as a live overview of the data behind it. A widget is a static objWid"
        },
        {
            "title": "5. Uploads",
            "page": "/cms/uploads",
            "anchor": "c5",
            "snippet": "The image and file field types handle uploads end to end: storage, tokenised URLs and, for images, thumbnails."
        },
        {
            "title": "5.1. The image field",
            "page": "/cms/uploads",
            "anchor": "c5.1",
            "snippet": "An image field stores two columns: the filename (cover) and a token (covertoken). On upload it writes the original and a scaled thumbnail, and renders the thumb"
        },
        {
            "title": "5.2. Where files go",
            "page": "/cms/uploads",
            "anchor": "c5.2",
            "snippet": "The app entrypoint declares the storage roots: Files are served back through a tokenised route, so URLs are not guessable from the row id. Keep data/uploads/ ou"
        },
        {
            "title": "5.3. Schema columns",
            "page": "/cms/uploads",
            "anchor": "c5.3",
            "snippet": "When you hand-write the table (as the SQLite demo does), remember an image field is two columns: The demo's seed.phlo creates exactly these columns alongside th"
        },
        {
            "title": "6. The example app",
            "page": "/cms/example",
            "anchor": "c6",
            "snippet": "The phlo-demo-cms repository is a blog/magazine on SQLite, built to be copied."
        },
        {
            "title": "6.1. What is in it",
            "page": "/cms/example",
            "anchor": "c6.1",
            "snippet": "Seven models, among them article, author, category and topic. Every relation type: parent (author, category), many (topics), child (comments). Image uploads on "
        },
        {
            "title": "6.2. One-command setup",
            "page": "/cms/example",
            "anchor": "c6.2",
            "snippet": "The repo ships with a ready-made dataset (data/cms.db plus the uploaded covers, avatars and attachments), so there is no build or seed step: clone and serve. To"
        },
        {
            "title": "6.3. Make it yours",
            "page": "/cms/example",
            "anchor": "c6.3",
            "snippet": "Copy the repository, replace the models in modules/, update %app-models and %app-menu, and adjust the CREATE TABLE statements in seed.phlo to match. Remember th"
        },
        {
            "title": "7. Insights",
            "page": "/cms/insights",
            "anchor": "c7",
            "snippet": "The CMS dashboard can answer questions about your data in plain language. The CMS.dashboard.bi module turns a question like \"articles about travel from last mon"
        },
        {
            "title": "7.1. How it works",
            "page": "/cms/insights",
            "anchor": "c7.1",
            "snippet": "The module hands the AI exactly two things: your question and the schema of your models (names and columns). The AI answers with a JSON description of a search "
        },
        {
            "title": "7.2. Setup",
            "page": "/cms/insights",
            "anchor": "c7.2",
            "snippet": "1. List CMS.dashboard.bi in resources. 2. Select it as the dashboard type: phloapp(..., dashboard: 'bi'). 3. Configure an AI provider: the module talks to %AI, "
        },
        {
            "title": "7.3. Voice",
            "page": "/cms/insights",
            "anchor": "c7.3",
            "snippet": "The search field carries a microphone button: dictate the question and it lands in the same flow. Speech input uses the browser's built-in speech recognition (C"
        },
        {
            "title": "1. Phlo Realtime overview",
            "page": "/websocket/overview",
            "anchor": "c1",
            "snippet": "Phlo Realtime is the realtime layer of the Phlo platform. It lets a Phlo app push to connected browsers over WebSockets without you leaving the Phlo language: y"
        },
        {
            "title": "1.1. The model",
            "page": "/websocket/overview",
            "anchor": "c1.1",
            "snippet": "Phlo Realtime is the WebSocket layer built into the Phlo Daemon: the one daemon process owns the socket connections. When a client connects, authenticates, send"
        },
        {
            "title": "1.2. When to use it",
            "page": "/websocket/overview",
            "anchor": "c1.2",
            "snippet": "Use Phlo Realtime when the browser needs to be told about something it did not ask for: live counters, presence, chat, notifications, dashboards, collaborative "
        },
        {
            "title": "1.3. What you wire",
            "page": "/websocket/overview",
            "anchor": "c1.3",
            "snippet": "A daemon: port in your app entrypoint and the DOM/websocket resource for the browser client. The body class wss, which is the switch that tells the client to op"
        },
        {
            "title": "2. The four hooks",
            "page": "/websocket/hooks",
            "anchor": "c2",
            "snippet": "Phlo Realtime calls one of four functions in your app for each socket lifecycle event. You implement only the ones you need."
        },
        {
            "title": "2.1. The lifecycle",
            "page": "/websocket/hooks",
            "anchor": "c2.1",
            "snippet": "Hook Called when Notes --- --- --- wsAuth at the handshake, before the socket is accepted validate the token; raise an error to refuse wsConnect right after the"
        },
        {
            "title": "2.2. The ws-prefixed arguments",
            "page": "/websocket/hooks",
            "anchor": "c2.2",
            "snippet": "Every hook receives connection context under ws-prefixed names so they never collide with your own payload keys: $wsHost and $wsPort: the host and daemon port t"
        },
        {
            "title": "2.3. State lives in the database",
            "page": "/websocket/hooks",
            "anchor": "c2.3",
            "snippet": "Because handlers are short-lived, anything you want to remember between events (who is online, the last message id) goes in a model, exactly like the rest of yo"
        },
        {
            "title": "3. Broadcasting with wsCast",
            "page": "/websocket/broadcasting",
            "anchor": "c3",
            "snippet": "wsCast is how a handler pushes to clients. It posts a message to Phlo Realtime, which fans it out to the targeted sockets, and each browser applies it through t"
        },
        {
            "title": "3.1. Targets",
            "page": "/websocket/broadcasting",
            "anchor": "c3.1",
            "snippet": "wsTarget: 'all' broadcasts to every connected socket. wsTarget: 'socket:'.$wsSocket sends to one socket. Token-scoped targeting lets you reach every socket for "
        },
        {
            "title": "3.2. Leaving the sender out",
            "page": "/websocket/broadcasting",
            "anchor": "c3.2",
            "snippet": "A token identifies a principal, not a connection. One user with two tabs, or one till with a customer display beside it, is one token holding several sockets. T"
        },
        {
            "title": "3.3. wsCast can fail; guard it",
            "page": "/websocket/broadcasting",
            "anchor": "c3.3",
            "snippet": "wsCast makes an HTTP call to Phlo Realtime. If Phlo Realtime is not running, it throws. Wrap it so the rest of your handler (and your plain async path) keeps wo"
        },
        {
            "title": "3.4. The browser client",
            "page": "/websocket/broadcasting",
            "anchor": "c3.4",
            "snippet": "Loading the DOM/websocket resource ships a small client. It opens a socket only when the page body has the class wss, reconnects automatically, and applies inco"
        },
        {
            "title": "3.5. Mirroring state between screens",
            "page": "/websocket/broadcasting",
            "anchor": "c3.5",
            "snippet": "With DOM/store loaded, a client can publish part of its own state instead of hand-rolling messages. Declare the path once: Every change under that path now leav"
        },
        {
            "title": "4. Socket monitoring",
            "page": "/websocket/monitoring",
            "anchor": "c4",
            "snippet": "Because every connection passes through your hooks, you can see and manage sockets with ordinary Phlo models, no special tooling required."
        },
        {
            "title": "4.1. Presence",
            "page": "/websocket/monitoring",
            "anchor": "c4.1",
            "snippet": "Track who is connected by writing a row in wsConnect and deleting it in wsClose: The live count is then just count(typepresence::records()), which you can broad"
        },
        {
            "title": "4.2. A connect/disconnect feed",
            "page": "/websocket/monitoring",
            "anchor": "c4.2",
            "snippet": "Append every lifecycle event to a log model and you have an audit trail and a live feed. The demo's /monitor page renders the log and updates the online count a"
        },
        {
            "title": "4.3. Health",
            "page": "/websocket/monitoring",
            "anchor": "c4.3",
            "snippet": "A GET /health on the daemon (e.g. 127.0.0.1:3001) reports both the connection side and the worker pools in one place, since they are one process: sockets counts"
        },
        {
            "title": "4.4. Limitations",
            "page": "/websocket/monitoring",
            "anchor": "c4.4",
            "snippet": "Phlo Realtime is the daemon's built-in WebSocket layer: one process owns the sockets and runs the PHP. In one-shot mode (a build: true app) the daemon boots a h"
        },
        {
            "title": "5. The example app",
            "page": "/websocket/example",
            "anchor": "c5",
            "snippet": "The phlo-demo-poll repository is the realtime showcase: the Poll from the tutorial(/learn) with Phlo Realtime built in. One repo holds both the plain async poll"
        },
        {
            "title": "5.1. From async poll to realtime",
            "page": "/websocket/example",
            "anchor": "c5.1",
            "snippet": "The Poll tutorial builds a poll that updates the page without a reload using a plain async form. Its final step hands off to Phlo Realtime: the same vote now al"
        },
        {
            "title": "5.2. What to read",
            "page": "/websocket/example",
            "anchor": "c5.2",
            "snippet": "app.ws.phlo: all four hooks, the guarded cast, presence and the event log. poll.phlo: the vote route adds one cast(...) and an online badge to the tutorial's ve"
        },
        {
            "title": "5.3. Run it yourself",
            "page": "/websocket/example",
            "anchor": "c5.3",
            "snippet": "Clone the repository, point a host at www/, and start the Phlo Daemon and Phlo Realtime. The first request transpiles the sources; without the realtime services"
        },
        {
            "title": "1. Phlo WhatsApp overview",
            "page": "/whatsapp/overview",
            "anchor": "c1",
            "snippet": "Phlo WhatsApp is the WhatsApp layer of the Phlo platform. It lets a Phlo app send and receive WhatsApp messages through a small gateway, while your application "
        },
        {
            "title": "1.1. The gateway model",
            "page": "/whatsapp/overview",
            "anchor": "c1.1",
            "snippet": "Phlo WhatsApp runs a gateway process (built on whatsapp-web.js) for each WhatsApp number. The gateway owns the connection to WhatsApp and exposes a small, secre"
        },
        {
            "title": "1.2. The two directions",
            "page": "/whatsapp/overview",
            "anchor": "c1.2",
            "snippet": "Direction You write Carries --- --- --- Receive a POST route, secret-checked inbound messages Send %WhatsApp(url, secret)-... text, media, polls, locations"
        },
        {
            "title": "1.3. What you wire",
            "page": "/whatsapp/overview",
            "anchor": "c1.3",
            "snippet": "The WhatsApp resource (it requires HTTP). security/creds so the gateway URL and secret come from data/creds.ini or the environment, never from code. A webhook r"
        },
        {
            "title": "2. Sending messages",
            "page": "/whatsapp/sending",
            "anchor": "c2",
            "snippet": "Construct the client from your gateway's URL and secret, then call a method per message type. A contact is a WhatsApp id like 31612345678@c.us (individual) or a"
        },
        {
            "title": "2.1. The resource methods",
            "page": "/whatsapp/sending",
            "anchor": "c2.1",
            "snippet": "Method Sends --- --- text($to, $text) a text message image($to, $file, $text) an image with optional caption document($to, $file, $text) a file as a document au"
        },
        {
            "title": "2.2. Sends can fail; expect it",
            "page": "/whatsapp/sending",
            "anchor": "c2.2",
            "snippet": "Every call crosses the network to the gateway, which may be offline or unlinked. Wrap sends so a failure becomes a clean message to the user instead of an error"
        },
        {
            "title": "3. Receiving messages",
            "page": "/whatsapp/receiving",
            "anchor": "c3",
            "snippet": "The gateway delivers each inbound message by POSTing to a webhook route in your app. You verify the shared secret, then do whatever you like with the message."
        },
        {
            "title": "3.1. The webhook route",
            "page": "/whatsapp/receiving",
            "anchor": "c3.1",
            "snippet": "Two details matter: Use hashequals for the secret check, never ==, to avoid timing leaks. Read the raw body with php://input; the gateway sends JSON, not a form"
        },
        {
            "title": "3.2. Acting on a message",
            "page": "/whatsapp/receiving",
            "anchor": "c3.2",
            "snippet": "Inside the webhook you have the full app available. Log it (as above), auto-reply with the WhatsApp resource, route it to a human, or fan it out over Phlo Realt"
        },
        {
            "title": "4. Credentials and shielding",
            "page": "/whatsapp/credentials",
            "anchor": "c4",
            "snippet": "Phlo WhatsApp touches a real phone number and a real account session. The rule for the whole platform is simple: no secrets and no numbers in the repository, ev"
        },
        {
            "title": "4.1. Where secrets live",
            "page": "/whatsapp/credentials",
            "anchor": "c4.1",
            "snippet": "security/creds resolves credentials from, in order, data/creds.ini and environment variables, and wraps them so they do not print by accident: The same values c"
        },
        {
            "title": "4.2. What to gitignore",
            "page": "/whatsapp/credentials",
            "anchor": "c4.2",
            "snippet": "Path Why --- --- data/creds.ini the gateway URL and secret .env environment secrets .wwebjsauth/, .wwebjscache/ the linked WhatsApp session data/received.json, "
        },
        {
            "title": "4.3. Inert without credentials",
            "page": "/whatsapp/credentials",
            "anchor": "c4.3",
            "snippet": "Design the app so it is safe with no credentials: if the secret is empty, sends become a \"not configured\" toast and the webhook rejects every request. The demo "
        },
        {
            "title": "5. The example app",
            "page": "/whatsapp/example",
            "anchor": "c5",
            "snippet": "The phlo-demo-whatsapp repository is a single app that shows both directions and ships with no secrets."
        },
        {
            "title": "5.1. What it does",
            "page": "/whatsapp/example",
            "anchor": "c5.1",
            "snippet": "Send panels for text, image, poll and location, each one route calling one WhatsApp method. A status check that asks the gateway whether it is online. A /receiv"
        },
        {
            "title": "5.2. Running it for real",
            "page": "/whatsapp/example",
            "anchor": "c5.2",
            "snippet": "1. Run a Phlo WhatsApp gateway linked to your own number. 2. Copy data/creds.ini.example to data/creds.ini and fill in the gateway URL and secret. 3. Set the ga"
        },
        {
            "title": "1. Dashboard overview",
            "page": "/dashboard/overview",
            "anchor": "c1",
            "snippet": "The Phlo Dashboard is the operations layer of the platform: a single control center for a fleet of Phlo apps and the servers they run on. It is itself a Phlo ap"
        },
        {
            "title": "1.1. What it is for",
            "page": "/dashboard/overview",
            "anchor": "c1.1",
            "snippet": "Where the CMS manages one app's content, the Dashboard manages the operation: which apps and hosts exist, who may touch them, what they are doing right now, and"
        },
        {
            "title": "1.2. The modules",
            "page": "/dashboard/overview",
            "anchor": "c1.2",
            "snippet": "Module Purpose --- --- Overview fleet status at a glance Database browse and edit MySQL and SQLite databases Domains / Sites the hosts and apps under management"
        },
        {
            "title": "1.3. Reference, not a demo",
            "page": "/dashboard/overview",
            "anchor": "c1.3",
            "snippet": "The Dashboard is an operational tool tied to real servers and accounts, so there is no public demo. This mini-manual is concepts and reference. The other three "
        },
        {
            "title": "2. Database admin",
            "page": "/dashboard/database",
            "anchor": "c2",
            "snippet": "The Dashboard can browse and edit the databases behind your apps without a separate tool."
        },
        {
            "title": "2.1. MySQL and SQLite",
            "page": "/dashboard/database",
            "anchor": "c2.1",
            "snippet": "Both drivers the platform ships are supported: the shared MySQL database that backs most production apps, and the SQLite files individual apps use (the CMS demo"
        },
        {
            "title": "2.2. Why it is built in",
            "page": "/dashboard/database",
            "anchor": "c2.2",
            "snippet": "Because every Phlo model is schema-driven, the Dashboard renders database tables with the same machinery the CMS uses for content. Database admin is therefore n"
        },
        {
            "title": "2.3. Safety",
            "page": "/dashboard/database",
            "anchor": "c2.3",
            "snippet": "Database access is an admin capability. Scope it with the permission system (next chapters) so only trusted users reach it, and prefer per-app SQLite files for "
        },
        {
            "title": "3. Notifications and subscriptions",
            "page": "/dashboard/notifications",
            "anchor": "c3",
            "snippet": "The Dashboard is the hub apps push events to, and the place users choose what reaches them."
        },
        {
            "title": "3.1. Notifications",
            "page": "/dashboard/notifications",
            "anchor": "c3.1",
            "snippet": "Any app can POST an event to the Dashboard's central inbox using a shared secret. Logged-in users see a server-scoped inbox of these events. This gives a fleet "
        },
        {
            "title": "3.2. Subscriptions",
            "page": "/dashboard/notifications",
            "anchor": "c3.2",
            "snippet": "Subscriptions decide delivery: a user can subscribe to a class of events and receive them out-of-band, for example forwarded to WhatsApp through a Phlo WhatsApp"
        },
        {
            "title": "3.3. The pattern",
            "page": "/dashboard/notifications",
            "anchor": "c3.3",
            "snippet": "Apps stay simple: they fire a one-line, best-effort POST and move on. The Dashboard owns fan-out, history and delivery. This is the same \"thin app, smart hub\" s"
        },
        {
            "title": "4. Users, roles and permissions",
            "page": "/dashboard/users",
            "anchor": "c4",
            "snippet": "The Dashboard is multi-user. Access is controlled by role and by two scoping axes: which servers and which modules a user may use."
        },
        {
            "title": "4.1. Roles",
            "page": "/dashboard/users",
            "anchor": "c4.1",
            "snippet": "Admin: full access to every module and server. User: access only to the modules and servers they are granted."
        },
        {
            "title": "4.2. Scoping",
            "page": "/dashboard/users",
            "anchor": "c4.2",
            "snippet": "A non-admin user carries two allow-lists: Scope Controls --- --- allowed servers which hosts in the fleet they can act on allowed modules which Dashboard module"
        },
        {
            "title": "4.3. Authentication",
            "page": "/dashboard/users",
            "anchor": "c4.3",
            "snippet": "Login uses bcrypt password hashing, and the fleet can verify a user against a shared secret so one identity works across sub-dashboards. Because auth is a resou"
        },
        {
            "title": "5. Monitoring: WhatsApp and visitors",
            "page": "/dashboard/monitoring",
            "anchor": "c5",
            "snippet": "Two of the Dashboard's modules are about watching things in production."
        },
        {
            "title": "5.1. WhatsApp monitoring",
            "page": "/dashboard/monitoring",
            "anchor": "c5.1",
            "snippet": "The WhatsApp module discovers the Phlo WhatsApp gateways running on the fleet and reports their state: linked or not, online or offline, and the login QR when a"
        },
        {
            "title": "5.2. Visitor analytics",
            "page": "/dashboard/monitoring",
            "anchor": "c5.2",
            "snippet": "The Visitors module is privacy-friendly analytics built into the platform: page views, visitor counts and real engagement time without third-party trackers, sto"
        },
        {
            "title": "5.3. One control center",
            "page": "/dashboard/monitoring",
            "anchor": "c5.3",
            "snippet": "Put together, the Dashboard is the operations layer over everything the other manuals describe: the CMS(/cms) for content, Phlo Realtime(/websocket) for realtim"
        },
        {
            "title": "1. Daemon overview",
            "page": "/daemon/overview",
            "anchor": "c1",
            "snippet": "The Phlo Daemon is an optional Node sidecar (phlo-daemon.js) that runs any Phlo target on a pool of resident workers. It is a generic engine: it knows nothing a"
        },
        {
            "title": "1.1. Three ways Phlo runs code",
            "page": "/daemon/overview",
            "anchor": "c1.1",
            "snippet": "Every routine in a Phlo app is a callable target (Class::method, object.method or a bare function). There are three ways it actually executes: Mode What happens"
        },
        {
            "title": "1.2. Why it is optional",
            "page": "/daemon/overview",
            "anchor": "c1.2",
            "snippet": "The one-shot path means nothing hard depends on the daemon. Async helpers fall back to a subprocess; tasks fall back to cron; an app that only serves HTTP never"
        },
        {
            "title": "1.3. What you wire",
            "page": "/daemon/overview",
            "anchor": "c1.3",
            "snippet": "A small config file that requires phlo-daemon.js with the port, the PHP binary and the host→app map: every app the daemon serves, driving dispatch, websockets a"
        },
        {
            "title": "2. The worker pool",
            "page": "/daemon/workers",
            "anchor": "c2",
            "snippet": "The daemon keeps a pool of resident PHP workers per app. Each worker boots the app once and then answers many calls, removing the per-call boot that the one-sho"
        },
        {
            "title": "2.1. The worker protocol",
            "page": "/daemon/workers",
            "anchor": "c2.1",
            "snippet": "A worker runs php <app.php phloserve, boots the app, and then reads newline-delimited JSON on stdin, one request in flight at a time: Concurrency equals the poo"
        },
        {
            "title": "2.2. Per-call reset",
            "page": "/daemon/workers",
            "anchor": "c2.2",
            "snippet": "Between calls a worker resets state (phlo('tech/reset'), session close, GC), exactly like the FrankenPHP worker loop, so one call never leaks into the next. Wri"
        },
        {
            "title": "2.3. Dynamic sizing, recycle",
            "page": "/daemon/workers",
            "anchor": "c2.3",
            "snippet": "The pool sizes itself: it spawns a worker when a call arrives and none is free, up to a global cap of one less than the core count, and reaps a worker once it s"
        },
        {
            "title": "2.4. Health",
            "page": "/daemon/workers",
            "anchor": "c2.4",
            "snippet": "GET /health on the daemon reports the live worker total against the cap, each pool keyed by app path, the connected sockets per host, and the configured hosts: "
        },
        {
            "title": "3. The dispatch API",
            "page": "/daemon/dispatch",
            "anchor": "c3",
            "snippet": "The daemon's main endpoint is POST /dispatch. It binds 127.0.0.1 by default, so it is local-only; gate it at the network boundary."
        },
        {
            "title": "3.1. Routing: by app path",
            "page": "/daemon/dispatch",
            "anchor": "c3.1",
            "snippet": "A dispatch carries {app, target, args?, stream?, async?}: the absolute .../app.php path tells the daemon which app to run, and each app's pool is keyed by that "
        },
        {
            "title": "3.2. Sync, async, stream",
            "page": "/daemon/dispatch",
            "anchor": "c3.2",
            "snippet": "The same endpoint answers in three shapes: Request Response --- --- default {status:\"ok\", result} once the call returns async: true 202 {status:\"ok\", queued:tru"
        },
        {
            "title": "3.3. What comes back",
            "page": "/daemon/dispatch",
            "anchor": "c3.3",
            "snippet": "The pool returns the target's real return value, typed (a boolean stays a boolean). The one-shot fallback returns the process's stdout as a string, so a consume"
        },
        {
            "title": "4. Runtime helpers and scheduling",
            "page": "/daemon/runtime",
            "anchor": "c4",
            "snippet": "Two consumers ride the daemon without you calling /dispatch yourself: the async runtime helpers, and the scheduler."
        },
        {
            "title": "4.1. The runtime helpers",
            "page": "/daemon/runtime",
            "anchor": "c4.1",
            "snippet": "Phlo's helpers for running a target out of band all use the pool when the app opts in, and fall back to a one-shot subprocess otherwise: Helper Does --- --- phl"
        },
        {
            "title": "4.2. Scheduling without cron",
            "page": "/daemon/runtime",
            "anchor": "c4.2",
            "snippet": "The daemon runs tasks::run every minute on every app in its host map, which replaces the per-app cron entry behind the tasks(/guide/tasks) resource. There is no"
        },
        {
            "title": "5. The WebSocket layer",
            "page": "/daemon/websocket",
            "anchor": "c5",
            "snippet": "Phlo Realtime is the daemon's built-in WebSocket server: the daemon owns the sockets and runs your PHP hooks in the same process. This is how that fits together"
        },
        {
            "title": "5.1. In-process, not a separate service",
            "page": "/daemon/websocket",
            "anchor": "c5.1",
            "snippet": "The WebSocket server (the ws library) lives inside the daemon. It holds the connections, the client registry and the /message broadcast bridge, and runs no PHP "
        },
        {
            "title": "5.2. The event flow",
            "page": "/daemon/websocket",
            "anchor": "c5.2",
            "snippet": "Socket event Dispatched as Notes --- --- --- handshake websocket::auth Runs before the socket is accepted; an explicit false (or a thrown error) refuses it. A m"
        },
        {
            "title": "5.3. Other relationships",
            "page": "/daemon/websocket",
            "anchor": "c5.3",
            "snippet": "The runtime helpers dispatch by app path over HTTP when the daemon constant is set (see the previous chapter). The scheduler is built into the daemon (previous "
        },
        {
            "title": "5.4. Running it",
            "page": "/daemon/websocket",
            "anchor": "c5.4",
            "snippet": "The daemon is one process. Run it under a supervisor: It binds to localhost; terminate wss:// at your reverse proxy and forward /websocket to the daemon's port."
        },
        {
            "title": "1. Presentation overview",
            "page": "/presentation/overview",
            "anchor": "c1",
            "snippet": "Phlo Presentation is an audio-timeline editor for narrated presentations and short videos. You layer images and background video over an audio track, and everyt"
        },
        {
            "title": "1.1. The player is an engine resource",
            "page": "/presentation/overview",
            "anchor": "c1.1",
            "snippet": "The thing that plays a presentation is not this app. PresentationPlayer is a Phlo engine resource (DOM/presentation): a small, dependency-free class that takes "
        },
        {
            "title": "1.2. What it does",
            "page": "/presentation/overview",
            "anchor": "c1.2",
            "snippet": "Capability What it means --- --- Audio-timeline editor Drag and resize images on a stage, scrub the timeline, preview live. Transitions and camera moves A share"
        },
        {
            "title": "1.3. What you need",
            "page": "/presentation/overview",
            "anchor": "c1.3",
            "snippet": "Presentation is a Phlo app that leans on four engine resources (DOM/presentation, DOM/ffmpeg, DOM/recorder, AI/OpenAI) and a couple of server tools. On the serv"
        },
        {
            "title": "2. The editor",
            "page": "/presentation/editor",
            "anchor": "c2",
            "snippet": "The editor is one stage, one timeline and one inspector. You add media, place it on the stage, and it plays against the audio."
        },
        {
            "title": "2.1. The media library",
            "page": "/presentation/editor",
            "anchor": "c2.1",
            "snippet": "Everything starts in the media library on the left: images, video and audio. You import files, or capture them in place with the recording studio. Each item is "
        },
        {
            "title": "2.2. The recording studio",
            "page": "/presentation/editor",
            "anchor": "c2.2",
            "snippet": "You do not have to bring media. The studio captures straight into the library: the microphone, for narration, a screen, tab or window, with the tab audio and th"
        },
        {
            "title": "2.3. The stage and the timeline",
            "page": "/presentation/editor",
            "anchor": "c2.3",
            "snippet": "You drag an item onto the stage and size it where it should sit, and you set when it appears and leaves on the timeline. Scrubbing the timeline moves the audio "
        },
        {
            "title": "2.4. Transitions and camera moves",
            "page": "/presentation/editor",
            "anchor": "c2.4",
            "snippet": "There are two kinds of motion, both without keyframes: Transitions run once, on entry and exit: fade, zoom, wipe, cards and more, from a shared set. during anim"
        },
        {
            "title": "3. Subtitles and dubbing",
            "page": "/presentation/subtitles",
            "anchor": "c3",
            "snippet": "The audio track is not just the clock; it is text waiting to happen. Presentation transcribes it, translates it, and can re-voice it."
        },
        {
            "title": "3.1. Transcription",
            "page": "/presentation/subtitles",
            "anchor": "c3.1",
            "snippet": "Whisper transcribes the audio into timed segments, with the source language detected automatically. The result is a subtitle track locked to the audio clock and"
        },
        {
            "title": "3.2. Translation",
            "page": "/presentation/subtitles",
            "anchor": "c3.2",
            "snippet": "From the transcript, a GPT call translates the subtitles into another language while preserving the timings. Each language becomes its own SRT and its own track"
        },
        {
            "title": "3.3. AI dubbing",
            "page": "/presentation/subtitles",
            "anchor": "c3.3",
            "snippet": "Subtitles are read; dubbing is heard. Presentation groups the transcript by sentence and generates TTS narration in a preset voice, placed back on the transcrip"
        },
        {
            "title": "3.4. Language alternatives everywhere",
            "page": "/presentation/subtitles",
            "anchor": "c3.4",
            "snippet": "Language is not only the audio. Every media item can carry per-type language alternatives: a different image, a different video or a different caption per langu"
        },
        {
            "title": "4. The AI director",
            "page": "/presentation/director",
            "anchor": "c4",
            "snippet": "The editor assumes you know the video you want. The director is for when you do not: you have a pile of media and a sentence."
        },
        {
            "title": "4.1. From a prompt to a storyboard",
            "page": "/presentation/director",
            "anchor": "c4.1",
            "snippet": "You describe the video in one line and pick a tempo and shot-length preset. First every item in your media library is captioned by a vision model, so the direct"
        },
        {
            "title": "4.2. From a storyboard to a timeline",
            "page": "/presentation/director",
            "anchor": "c4.2",
            "snippet": "The storyboard is not the output; it is the plan. A deterministic compiler turns it into a full presentation: the media placed on the stage and the timeline, ca"
        },
        {
            "title": "4.3. Refine, do not regenerate",
            "page": "/presentation/director",
            "anchor": "c4.3",
            "snippet": "Because the director produces an ordinary timeline, you refine it by hand: nudge a timing, swap a clip, change a transition, rewrite a line. The AI gets you to "
        },
        {
            "title": "5. Exporting",
            "page": "/presentation/export",
            "anchor": "c5",
            "snippet": "A presentation is a presentation.json payload plus its media. There are two ways to take it out of the editor, and both are driven by the same player."
        },
        {
            "title": "5.1. The standalone web page",
            "page": "/presentation/export",
            "anchor": "c5.1",
            "snippet": "The first export is a self-contained zip: the player inlined, your media alongside it and the payload. It works offline and even from file://, so you can email "
        },
        {
            "title": "5.2. The client-side MP4 render",
            "page": "/presentation/export",
            "anchor": "c5.2",
            "snippet": "The second export is a real MP4, rendered entirely in the browser. The same PresentationPlayer drives a deterministic canvas renderer, so every frame of the exp"
        },
        {
            "title": "5.3. Preview equals export",
            "page": "/presentation/export",
            "anchor": "c5.3",
            "snippet": "The reason both paths can be trusted is the single player. The editor preview, the standalone page and the MP4 renderer all replay the same payload on the same "
        },
        {
            "title": "%cookies",
            "page": "/manual/core",
            "anchor": "obj/cookies",
            "snippet": "Cookies data object",
            "tags": "cookies session browser web"
        },
        {
            "title": "%cookies->controller",
            "page": "/manual/core",
            "anchor": "obj/cookies/controller",
            "snippet": ""
        },
        {
            "title": "%cookies->lifetimeDays",
            "page": "/manual/core",
            "anchor": "obj/cookies/lifetimeDays",
            "snippet": ""
        },
        {
            "title": "%cookies->objSet($key, $value, array $options = [])",
            "page": "/manual/core",
            "anchor": "obj/cookies/objSet",
            "snippet": ""
        },
        {
            "title": "%cookies->__unset($key)",
            "page": "/manual/core",
            "anchor": "obj/cookies/__unset",
            "snippet": ""
        },
        {
            "title": "%lang",
            "page": "/manual/core",
            "anchor": "obj/lang",
            "snippet": "Language and translation resource",
            "tags": "lang translation i18n locale ai"
        },
        {
            "title": "%lang->nl($text, ...$args)",
            "page": "/manual/core",
            "anchor": "obj/lang/nl",
            "snippet": ""
        },
        {
            "title": "%lang->en($text, ...$args)",
            "page": "/manual/core",
            "anchor": "obj/lang/en",
            "snippet": ""
        },
        {
            "title": "%lang->asyncBatch($from, $to, $json)",
            "page": "/manual/core",
            "anchor": "obj/lang/asyncBatch",
            "snippet": ""
        },
        {
            "title": "%lang->view",
            "page": "/manual/core",
            "anchor": "obj/lang/view",
            "snippet": ""
        },
        {
            "title": "%lang->model",
            "page": "/manual/core",
            "anchor": "obj/lang/model",
            "snippet": ""
        },
        {
            "title": "%lang->instructions",
            "page": "/manual/core",
            "anchor": "obj/lang/instructions",
            "snippet": ""
        },
        {
            "title": "%lang->fileCache",
            "page": "/manual/core",
            "anchor": "obj/lang/fileCache",
            "snippet": ""
        },
        {
            "title": "%lang->file($lang)",
            "page": "/manual/core",
            "anchor": "obj/lang/file",
            "snippet": ""
        },
        {
            "title": "%lang->escape($value)",
            "page": "/manual/core",
            "anchor": "obj/lang/escape",
            "snippet": ""
        },
        {
            "title": "%lang->unescape($value)",
            "page": "/manual/core",
            "anchor": "obj/lang/unescape",
            "snippet": ""
        },
        {
            "title": "%lang->lineValue($line, $eq)",
            "page": "/manual/core",
            "anchor": "obj/lang/lineValue",
            "snippet": ""
        },
        {
            "title": "%lang->readAll($file)",
            "page": "/manual/core",
            "anchor": "obj/lang/readAll",
            "snippet": ""
        },
        {
            "title": "%lang->search($file, $hash)",
            "page": "/manual/core",
            "anchor": "obj/lang/search",
            "snippet": "Finds one line with a binary search over the raw bytes of the translation file."
        },
        {
            "title": "%lang->lookup($hash)",
            "page": "/manual/core",
            "anchor": "obj/lang/lookup",
            "snippet": ""
        },
        {
            "title": "%lang->save($lang, $pairs)",
            "page": "/manual/core",
            "anchor": "obj/lang/save",
            "snippet": "Writes the whole file under a temporary name and renames it into place."
        },
        {
            "title": "%lang->transContext",
            "page": "/manual/core",
            "anchor": "obj/lang/transContext",
            "snippet": ""
        },
        {
            "title": "%lang->browser",
            "page": "/manual/core",
            "anchor": "obj/lang/browser",
            "snippet": ""
        },
        {
            "title": "%lang->cookie",
            "page": "/manual/core",
            "anchor": "obj/lang/cookie",
            "snippet": ""
        },
        {
            "title": "%lang->detect($text, $fallback = 'en')",
            "page": "/manual/core",
            "anchor": "obj/lang/detect",
            "snippet": ""
        },
        {
            "title": "%lang->hash($from, $text)",
            "page": "/manual/core",
            "anchor": "obj/lang/hash",
            "snippet": ""
        },
        {
            "title": "%lang->translation($from, $text, ...$args)",
            "page": "/manual/core",
            "anchor": "obj/lang/translation",
            "snippet": ""
        },
        {
            "title": "%lang->translate($from, $to, $text)",
            "page": "/manual/core",
            "anchor": "obj/lang/translate",
            "snippet": ""
        },
        {
            "title": "%lang->translateBatch($from, $to, $texts)",
            "page": "/manual/core",
            "anchor": "obj/lang/translateBatch",
            "snippet": ""
        },
        {
            "title": "%lastmod",
            "page": "/manual/core",
            "anchor": "obj/lastmod",
            "snippet": "Build-time stamp of when each page's source last changed, read at runtime for sitemap lastmod and for showing a date to the reader",
            "tags": "seo sitemap lastmod build stamp"
        },
        {
            "title": "%lastmod->file($dir = null)",
            "page": "/manual/core",
            "anchor": "obj/lastmod/file",
            "snippet": "Where the map lives, beside the generated PHP so it travels with a release."
        },
        {
            "title": "%lastmod->sources",
            "page": "/manual/core",
            "anchor": "obj/lastmod/sources",
            "snippet": "Sources the convention cannot find, as a uri to file path map."
        },
        {
            "title": "%lastmod->resolve($uri)",
            "page": "/manual/core",
            "anchor": "obj/lastmod/resolve",
            "snippet": "The file most likely to render this page."
        },
        {
            "title": "%lastmod->day($file)",
            "page": "/manual/core",
            "anchor": "obj/lastmod/day",
            "snippet": ""
        },
        {
            "title": "%lastmod->uriOf($page)",
            "page": "/manual/core",
            "anchor": "obj/lastmod/uriOf",
            "snippet": ""
        },
        {
            "title": "%lastmod->stamp($dir = null)",
            "page": "/manual/core",
            "anchor": "obj/lastmod/stamp",
            "snippet": ""
        },
        {
            "title": "%lastmod->map",
            "page": "/manual/core",
            "anchor": "obj/lastmod/map",
            "snippet": ""
        },
        {
            "title": "%lastmod->for($uri)",
            "page": "/manual/core",
            "anchor": "obj/lastmod/for",
            "snippet": ""
        },
        {
            "title": "%manifest",
            "page": "/manual/core",
            "anchor": "obj/manifest",
            "snippet": "PWA web app manifest: declare the body, get the manifest.json route, head link and correct serving",
            "tags": "manifest pwa webmanifest install standalone"
        },
        {
            "title": "%manifest->GETManifest",
            "page": "/manual/core",
            "anchor": "obj/manifest/GETManifest",
            "snippet": ""
        },
        {
            "title": "%manifest->body",
            "page": "/manual/core",
            "anchor": "obj/manifest/body",
            "snippet": ""
        },
        {
            "title": "%manifest->maxAge",
            "page": "/manual/core",
            "anchor": "obj/manifest/maxAge",
            "snippet": ""
        },
        {
            "title": "%manifest->encode($body)",
            "page": "/manual/core",
            "anchor": "obj/manifest/encode",
            "snippet": ""
        },
        {
            "title": "%manifest->output($body, $maxAge = null)",
            "page": "/manual/core",
            "anchor": "obj/manifest/output",
            "snippet": ""
        },
        {
            "title": "%manifest->view",
            "page": "/manual/core",
            "anchor": "obj/manifest/view",
            "snippet": ""
        },
        {
            "title": "%manifest->head",
            "page": "/manual/core",
            "anchor": "obj/manifest/head",
            "snippet": ""
        },
        {
            "title": "%manual",
            "page": "/manual/core",
            "anchor": "obj/manual",
            "snippet": "Self-writing manual at /manual: app description, source reflection and recent commits, plus an optional AI summary",
            "tags": "manual docs reflection ai"
        },
        {
            "title": "%manual->instruction",
            "page": "/manual/core",
            "anchor": "obj/manual/instruction",
            "snippet": ""
        },
        {
            "title": "%manual->translate",
            "page": "/manual/core",
            "anchor": "obj/manual/translate",
            "snippet": ""
        },
        {
            "title": "%manual->labels",
            "page": "/manual/core",
            "anchor": "obj/manual/labels",
            "snippet": ""
        },
        {
            "title": "%manual->label($key)",
            "page": "/manual/core",
            "anchor": "obj/manual/label",
            "snippet": ""
        },
        {
            "title": "%manual->appInfo",
            "page": "/manual/core",
            "anchor": "obj/manual/appInfo",
            "snippet": ""
        },
        {
            "title": "%manual->commits(string $path, int $limit = 12)",
            "page": "/manual/core",
            "anchor": "obj/manual/commits",
            "snippet": ""
        },
        {
            "title": "%manual->repos",
            "page": "/manual/core",
            "anchor": "obj/manual/repos",
            "snippet": ""
        },
        {
            "title": "%manual->commitList(int $limit = 20)",
            "page": "/manual/core",
            "anchor": "obj/manual/commitList",
            "snippet": ""
        },
        {
            "title": "%manual->head",
            "page": "/manual/core",
            "anchor": "obj/manual/head",
            "snippet": ""
        },
        {
            "title": "%manual->routes",
            "page": "/manual/core",
            "anchor": "obj/manual/routes",
            "snippet": ""
        },
        {
            "title": "%manual->routeGroups",
            "page": "/manual/core",
            "anchor": "obj/manual/routeGroups",
            "snippet": ""
        },
        {
            "title": "%manual->nodesByFile",
            "page": "/manual/core",
            "anchor": "obj/manual/nodesByFile",
            "snippet": ""
        },
        {
            "title": "%manual->fileRow(string $file)",
            "page": "/manual/core",
            "anchor": "obj/manual/fileRow",
            "snippet": ""
        },
        {
            "title": "%manual->sources",
            "page": "/manual/core",
            "anchor": "obj/manual/sources",
            "snippet": ""
        },
        {
            "title": "%manual->dirLabel(string $dir)",
            "page": "/manual/core",
            "anchor": "obj/manual/dirLabel",
            "snippet": ""
        },
        {
            "title": "%manual->cacheFile",
            "page": "/manual/core",
            "anchor": "obj/manual/cacheFile",
            "snippet": ""
        },
        {
            "title": "%manual->cached",
            "page": "/manual/core",
            "anchor": "obj/manual/cached",
            "snippet": ""
        },
        {
            "title": "%manual->configured",
            "page": "/manual/core",
            "anchor": "obj/manual/configured",
            "snippet": ""
        },
        {
            "title": "%manual->appName",
            "page": "/manual/core",
            "anchor": "obj/manual/appName",
            "snippet": ""
        },
        {
            "title": "%manual->model",
            "page": "/manual/core",
            "anchor": "obj/manual/model",
            "snippet": ""
        },
        {
            "title": "%manual->summary",
            "page": "/manual/core",
            "anchor": "obj/manual/summary",
            "snippet": ""
        },
        {
            "title": "%manual->pageFile",
            "page": "/manual/core",
            "anchor": "obj/manual/pageFile",
            "snippet": ""
        },
        {
            "title": "%manual->store(string $body, string $page)",
            "page": "/manual/core",
            "anchor": "obj/manual/store",
            "snippet": ""
        },
        {
            "title": "%manual->mdBlocks(string $md)",
            "page": "/manual/core",
            "anchor": "obj/manual/mdBlocks",
            "snippet": ""
        },
        {
            "title": "%manual->mdMarker(string $line)",
            "page": "/manual/core",
            "anchor": "obj/manual/mdMarker",
            "snippet": ""
        },
        {
            "title": "%manual->mdBreaks(?obj $here, obj $marker)",
            "page": "/manual/core",
            "anchor": "obj/manual/mdBreaks",
            "snippet": ""
        },
        {
            "title": "%manual->mdPara(array &$para, array &$blocks)",
            "page": "/manual/core",
            "anchor": "obj/manual/mdPara",
            "snippet": ""
        },
        {
            "title": "%manual->mdCells(string $line)",
            "page": "/manual/core",
            "anchor": "obj/manual/mdCells",
            "snippet": ""
        },
        {
            "title": "%manual->mdItems(array $lines, int $indent)",
            "page": "/manual/core",
            "anchor": "obj/manual/mdItems",
            "snippet": ""
        },
        {
            "title": "%manual->mdInline(string $text)",
            "page": "/manual/core",
            "anchor": "obj/manual/mdInline",
            "snippet": ""
        },
        {
            "title": "%manual->mdFormat(string $text)",
            "page": "/manual/core",
            "anchor": "obj/manual/mdFormat",
            "snippet": ""
        },
        {
            "title": "%manual->BothGETManual",
            "page": "/manual/core",
            "anchor": "obj/manual/BothGETManual",
            "snippet": ""
        },
        {
            "title": "%manual->page",
            "page": "/manual/core",
            "anchor": "obj/manual/page",
            "snippet": ""
        },
        {
            "title": "%manual->summaryBlock",
            "page": "/manual/core",
            "anchor": "obj/manual/summaryBlock",
            "snippet": ""
        },
        {
            "title": "%manual->summaryText($row)",
            "page": "/manual/core",
            "anchor": "obj/manual/summaryText",
            "snippet": ""
        },
        {
            "title": "%manual->infoBlock",
            "page": "/manual/core",
            "anchor": "obj/manual/infoBlock",
            "snippet": ""
        },
        {
            "title": "%manual->infoText(array $blocks)",
            "page": "/manual/core",
            "anchor": "obj/manual/infoText",
            "snippet": ""
        },
        {
            "title": "%manual->mdBlock($block)",
            "page": "/manual/core",
            "anchor": "obj/manual/mdBlock",
            "snippet": ""
        },
        {
            "title": "%manual->mdList($block)",
            "page": "/manual/core",
            "anchor": "obj/manual/mdList",
            "snippet": ""
        },
        {
            "title": "%manual->mdBullets(array $items)",
            "page": "/manual/core",
            "anchor": "obj/manual/mdBullets",
            "snippet": ""
        },
        {
            "title": "%manual->mdOrdered(array $items)",
            "page": "/manual/core",
            "anchor": "obj/manual/mdOrdered",
            "snippet": ""
        },
        {
            "title": "%manual->mdItem($item)",
            "page": "/manual/core",
            "anchor": "obj/manual/mdItem",
            "snippet": ""
        },
        {
            "title": "%manual->mdBody($item)",
            "page": "/manual/core",
            "anchor": "obj/manual/mdBody",
            "snippet": ""
        },
        {
            "title": "%manual->mdTable($block)",
            "page": "/manual/core",
            "anchor": "obj/manual/mdTable",
            "snippet": ""
        },
        {
            "title": "%manual->filesBlock",
            "page": "/manual/core",
            "anchor": "obj/manual/filesBlock",
            "snippet": ""
        },
        {
            "title": "%manual->filesTable(array $groups)",
            "page": "/manual/core",
            "anchor": "obj/manual/filesTable",
            "snippet": ""
        },
        {
            "title": "%manual->fileNodes($file)",
            "page": "/manual/core",
            "anchor": "obj/manual/fileNodes",
            "snippet": ""
        },
        {
            "title": "%manual->routesBlock",
            "page": "/manual/core",
            "anchor": "obj/manual/routesBlock",
            "snippet": ""
        },
        {
            "title": "%manual->routesTable(array $groups)",
            "page": "/manual/core",
            "anchor": "obj/manual/routesTable",
            "snippet": ""
        },
        {
            "title": "%manual->routeRow(array $route)",
            "page": "/manual/core",
            "anchor": "obj/manual/routeRow",
            "snippet": ""
        },
        {
            "title": "%manual->routeLine(string $method, string $path, string $summary)",
            "page": "/manual/core",
            "anchor": "obj/manual/routeLine",
            "snippet": ""
        },
        {
            "title": "%manual->commitsBlock",
            "page": "/manual/core",
            "anchor": "obj/manual/commitsBlock",
            "snippet": ""
        },
        {
            "title": "%manual->commitsList(array $commits)",
            "page": "/manual/core",
            "anchor": "obj/manual/commitsList",
            "snippet": ""
        },
        {
            "title": "%payload",
            "page": "/manual/core",
            "anchor": "obj/payload",
            "snippet": "POST, PUT, PATCH, QUERY and file-upload data object",
            "tags": "payload request upload post put patch query"
        },
        {
            "title": "%payload->controller",
            "page": "/manual/core",
            "anchor": "obj/payload/controller",
            "snippet": ""
        },
        {
            "title": "%seo",
            "page": "/manual/core",
            "anchor": "obj/seo",
            "snippet": "Multilingual SEO: sitemap.xml, robots.txt, hreflang + head meta (description/OG/Twitter/canonical)",
            "tags": "seo sitemap robots hreflang opengraph multilingual"
        },
        {
            "title": "%seo->GETSitemap",
            "page": "/manual/core",
            "anchor": "obj/seo/GETSitemap",
            "snippet": ""
        },
        {
            "title": "%seo->GETRobots",
            "page": "/manual/core",
            "anchor": "obj/seo/GETRobots",
            "snippet": ""
        },
        {
            "title": "%seo->robots",
            "page": "/manual/core",
            "anchor": "obj/seo/robots",
            "snippet": ""
        },
        {
            "title": "%seo->intl($uri)",
            "page": "/manual/core",
            "anchor": "obj/seo/intl",
            "snippet": ""
        },
        {
            "title": "%seo->uri($page)",
            "page": "/manual/core",
            "anchor": "obj/seo/uri",
            "snippet": "A sitemap entry is a uri string, or an object carrying that uri plus more."
        },
        {
            "title": "%seo->field($page, $key)",
            "page": "/manual/core",
            "anchor": "obj/seo/field",
            "snippet": ""
        },
        {
            "title": "%seo->lastmod($page)",
            "page": "/manual/core",
            "anchor": "obj/seo/lastmod",
            "snippet": "The date for one entry, or nothing at all when it does not parse."
        },
        {
            "title": "%seo->locale",
            "page": "/manual/core",
            "anchor": "obj/seo/locale",
            "snippet": ""
        },
        {
            "title": "%seo->ogTitle",
            "page": "/manual/core",
            "anchor": "obj/seo/ogTitle",
            "snippet": ""
        },
        {
            "title": "%seo->ogDescr",
            "page": "/manual/core",
            "anchor": "obj/seo/ogDescr",
            "snippet": ""
        },
        {
            "title": "%seo->ogImageFile",
            "page": "/manual/core",
            "anchor": "obj/seo/ogImageFile",
            "snippet": ""
        },
        {
            "title": "%seo->ogImage",
            "page": "/manual/core",
            "anchor": "obj/seo/ogImage",
            "snippet": ""
        },
        {
            "title": "%seo->canonical",
            "page": "/manual/core",
            "anchor": "obj/seo/canonical",
            "snippet": ""
        },
        {
            "title": "%seo->ogType",
            "page": "/manual/core",
            "anchor": "obj/seo/ogType",
            "snippet": ""
        },
        {
            "title": "%seo->sitemapPages",
            "page": "/manual/core",
            "anchor": "obj/seo/sitemapPages",
            "snippet": ""
        },
        {
            "title": "%seo->sitemapLangs",
            "page": "/manual/core",
            "anchor": "obj/seo/sitemapLangs",
            "snippet": ""
        },
        {
            "title": "%seo->siteName",
            "page": "/manual/core",
            "anchor": "obj/seo/siteName",
            "snippet": ""
        },
        {
            "title": "%seo->twitterCard",
            "page": "/manual/core",
            "anchor": "obj/seo/twitterCard",
            "snippet": ""
        },
        {
            "title": "%seo->structuredData",
            "page": "/manual/core",
            "anchor": "obj/seo/structuredData",
            "snippet": "Schema.org data for this page, as '@type' plus what only your app knows."
        },
        {
            "title": "%seo->schemaData",
            "page": "/manual/core",
            "anchor": "obj/seo/schemaData",
            "snippet": ""
        },
        {
            "title": "%seo->noIndex",
            "page": "/manual/core",
            "anchor": "obj/seo/noIndex",
            "snippet": ""
        },
        {
            "title": "%seo->view",
            "page": "/manual/core",
            "anchor": "obj/seo/view",
            "snippet": ""
        },
        {
            "title": "%seo->page($page)",
            "page": "/manual/core",
            "anchor": "obj/seo/page",
            "snippet": ""
        },
        {
            "title": "%seo->xlink($lang, $uri)",
            "page": "/manual/core",
            "anchor": "obj/seo/xlink",
            "snippet": ""
        },
        {
            "title": "%seo->link($lang, $uri)",
            "page": "/manual/core",
            "anchor": "obj/seo/link",
            "snippet": ""
        },
        {
            "title": "%seo->head",
            "page": "/manual/core",
            "anchor": "obj/seo/head",
            "snippet": ""
        },
        {
            "title": "%session",
            "page": "/manual/core",
            "anchor": "obj/session",
            "snippet": "Session data object",
            "tags": "session web state"
        },
        {
            "title": "%session->options",
            "page": "/manual/core",
            "anchor": "obj/session/options",
            "snippet": ""
        },
        {
            "title": "%session->controller",
            "page": "/manual/core",
            "anchor": "obj/session/controller",
            "snippet": ""
        },
        {
            "title": "%session->__set($key, $value)",
            "page": "/manual/core",
            "anchor": "obj/session/__set",
            "snippet": ""
        },
        {
            "title": "%session->__unset($key)",
            "page": "/manual/core",
            "anchor": "obj/session/__unset",
            "snippet": ""
        },
        {
            "title": "%session->__isset($key)",
            "page": "/manual/core",
            "anchor": "obj/session/__isset",
            "snippet": ""
        },
        {
            "title": "%session->objRegenerateId($deleteOld = true)",
            "page": "/manual/core",
            "anchor": "obj/session/objRegenerateId",
            "snippet": ""
        },
        {
            "title": "%stream",
            "page": "/manual/core",
            "anchor": "obj/stream",
            "snippet": "Raw data stream beside the JSON command channel: stream() emits text or binary chunks under any content type, app.stream() consumes them via fetch dispatching on the response type",
            "tags": "stream binary raw data download"
        },
        {
            "title": "%stream->stream($data = null, string $type = 'application/octet-stream', ?string $name = null)",
            "page": "/manual/core",
            "anchor": "obj/stream/stream",
            "snippet": ""
        },
        {
            "title": "%tasks",
            "page": "/manual/core",
            "anchor": "obj/tasks",
            "snippet": "Cron runner for %app->tasks. One cron entry per app triggers this every minute.",
            "tags": "cron schedule tasks scheduler"
        },
        {
            "title": "%tasks->dir",
            "page": "/manual/core",
            "anchor": "obj/tasks/dir",
            "snippet": ""
        },
        {
            "title": "%tasks->run",
            "page": "/manual/core",
            "anchor": "obj/tasks/run",
            "snippet": ""
        },
        {
            "title": "%tasks->saveRun($name, $do, $schedule, $return)",
            "page": "/manual/core",
            "anchor": "obj/tasks/saveRun",
            "snippet": ""
        },
        {
            "title": "%tasks->due($name, $task, $now)",
            "page": "/manual/core",
            "anchor": "obj/tasks/due",
            "snippet": "daily and weekly match the exact minute the task names."
        },
        {
            "title": "%tasks->fire($do)",
            "page": "/manual/core",
            "anchor": "obj/tasks/fire",
            "snippet": ""
        },
        {
            "title": "%tasks->lastRun($name)",
            "page": "/manual/core",
            "anchor": "obj/tasks/lastRun",
            "snippet": ""
        },
        {
            "title": "%tasks->markRun($name, $ts)",
            "page": "/manual/core",
            "anchor": "obj/tasks/markRun",
            "snippet": ""
        },
        {
            "title": "%tasks->lock($name)",
            "page": "/manual/core",
            "anchor": "obj/tasks/lock",
            "snippet": ""
        },
        {
            "title": "%tasks->unlock($name)",
            "page": "/manual/core",
            "anchor": "obj/tasks/unlock",
            "snippet": ""
        },
        {
            "title": "%useragent",
            "page": "/manual/core",
            "anchor": "obj/useragent",
            "snippet": "User agent information",
            "tags": "useragent browser os device web"
        },
        {
            "title": "%useragent->source",
            "page": "/manual/core",
            "anchor": "obj/useragent/source",
            "snippet": ""
        },
        {
            "title": "%useragent->os",
            "page": "/manual/core",
            "anchor": "obj/useragent/os",
            "snippet": ""
        },
        {
            "title": "%useragent->osV",
            "page": "/manual/core",
            "anchor": "obj/useragent/osV",
            "snippet": ""
        },
        {
            "title": "%useragent->osFull",
            "page": "/manual/core",
            "anchor": "obj/useragent/osFull",
            "snippet": ""
        },
        {
            "title": "%useragent->name",
            "page": "/manual/core",
            "anchor": "obj/useragent/name",
            "snippet": ""
        },
        {
            "title": "%useragent->version",
            "page": "/manual/core",
            "anchor": "obj/useragent/version",
            "snippet": ""
        },
        {
            "title": "%useragent->full",
            "page": "/manual/core",
            "anchor": "obj/useragent/full",
            "snippet": ""
        },
        {
            "title": "%useragent->device",
            "page": "/manual/core",
            "anchor": "obj/useragent/device",
            "snippet": ""
        },
        {
            "title": "%visitors",
            "page": "/manual/core",
            "anchor": "obj/visitors",
            "snippet": "Visitor tracking via heartbeat",
            "tags": "visitors analytics heartbeat tracking"
        },
        {
            "title": "%visitors->table",
            "page": "/manual/core",
            "anchor": "obj/visitors/table",
            "snippet": ""
        },
        {
            "title": "%visitors->columns",
            "page": "/manual/core",
            "anchor": "obj/visitors/columns",
            "snippet": ""
        },
        {
            "title": "%visitors->history",
            "page": "/manual/core",
            "anchor": "obj/visitors/history",
            "snippet": ""
        },
        {
            "title": "%visitors->online",
            "page": "/manual/core",
            "anchor": "obj/visitors/online",
            "snippet": ""
        },
        {
            "title": "%visitors->lastHour",
            "page": "/manual/core",
            "anchor": "obj/visitors/lastHour",
            "snippet": ""
        },
        {
            "title": "%visitors->isBot(?string $ua)",
            "page": "/manual/core",
            "anchor": "obj/visitors/isBot",
            "snippet": ""
        },
        {
            "title": "%visitors->parseReferrer(string $url)",
            "page": "/manual/core",
            "anchor": "obj/visitors/parseReferrer",
            "snippet": ""
        },
        {
            "title": "%visitors->PUTHeartbeatNVLUWHAPRCSPpPs",
            "page": "/manual/core",
            "anchor": "obj/visitors/PUTHeartbeatNVLUWHAPRCSPpPs",
            "snippet": ""
        },
        {
            "title": "%websocket",
            "page": "/manual/core",
            "anchor": "obj/websocket",
            "snippet": "Server-side WebSocket handler via phloWS",
            "tags": "websocket realtime ws server"
        },
        {
            "title": "%websocket->connect($wsHost, $wsToken, $wsSocket)",
            "page": "/manual/core",
            "anchor": "obj/websocket/connect",
            "snippet": ""
        },
        {
            "title": "%websocket->auth($wsHost, $wsToken, $wsSocket)",
            "page": "/manual/core",
            "anchor": "obj/websocket/auth",
            "snippet": ""
        },
        {
            "title": "%websocket->receive($wsHost, $wsToken, $wsSocket, $data)",
            "page": "/manual/core",
            "anchor": "obj/websocket/receive",
            "snippet": ""
        },
        {
            "title": "%websocket->close($wsHost, $wsToken, $wsSocket)",
            "page": "/manual/core",
            "anchor": "obj/websocket/close",
            "snippet": ""
        },
        {
            "title": "%WhatsApp",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp",
            "snippet": "WhatsApp client for phloWA using whatsapp-web.js",
            "tags": "whatsapp messaging api"
        },
        {
            "title": "%WhatsApp->__construct(public string $url, public string $secret)",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/__construct",
            "snippet": ""
        },
        {
            "title": "%WhatsApp->channel($channel)",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/channel",
            "snippet": ""
        },
        {
            "title": "%WhatsApp->number($contact)",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/number",
            "snippet": ""
        },
        {
            "title": "%WhatsApp->isGroup($contact)",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/isGroup",
            "snippet": ""
        },
        {
            "title": "%WhatsApp->status",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/status",
            "snippet": ""
        },
        {
            "title": "%WhatsApp->health",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/health",
            "snippet": ""
        },
        {
            "title": "%WhatsApp->qr",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/qr",
            "snippet": ""
        },
        {
            "title": "%WhatsApp->disconnect",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/disconnect",
            "snippet": ""
        },
        {
            "title": "%WhatsApp->read($chat)",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/read",
            "snippet": ""
        },
        {
            "title": "%WhatsApp->reaction($msg, $emoji)",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/reaction",
            "snippet": ""
        },
        {
            "title": "%WhatsApp->text($to, $text)",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/text",
            "snippet": ""
        },
        {
            "title": "%WhatsApp->image($to, file $file, $text = void)",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/image",
            "snippet": ""
        },
        {
            "title": "%WhatsApp->location($to, $lat, $lon, $text)",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/location",
            "snippet": ""
        },
        {
            "title": "%WhatsApp->document($to, file $file, $text = void)",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/document",
            "snippet": ""
        },
        {
            "title": "%WhatsApp->audio($to, file $file)",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/audio",
            "snippet": ""
        },
        {
            "title": "%WhatsApp->voice($to, file $file)",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/voice",
            "snippet": ""
        },
        {
            "title": "%WhatsApp->poll($to, $name, array $options, bool $multi = false)",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/poll",
            "snippet": ""
        },
        {
            "title": "%WhatsApp->startTyping($to)",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/startTyping",
            "snippet": ""
        },
        {
            "title": "%WhatsApp->stopTyping($to)",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/stopTyping",
            "snippet": ""
        },
        {
            "title": "%WhatsApp->request($action, ...$data)",
            "page": "/manual/core",
            "anchor": "obj/WhatsApp/request",
            "snippet": ""
        },
        {
            "title": "active(bool $cond, string $classList = void):string",
            "page": "/manual/core",
            "anchor": "function/Core/active",
            "snippet": "Build active class attribute for UI state",
            "tags": "active class ui view html"
        },
        {
            "title": "age(int $time):int",
            "page": "/manual/core",
            "anchor": "function/Core/age",
            "snippet": "Get age in seconds since a given timestamp",
            "tags": "age time timestamp"
        },
        {
            "title": "age_human(int $age):string",
            "page": "/manual/core",
            "anchor": "function/Core/age_human",
            "snippet": "Convert age in seconds to human readable text",
            "tags": "age human time format"
        },
        {
            "title": "apcu($key, $cb, int $duration = 3600, bool $log = true)",
            "page": "/manual/core",
            "anchor": "function/Core/apcu",
            "snippet": "Cache callback results in APCu",
            "tags": "cache apcu performance"
        },
        {
            "title": "await(...$jobs):array",
            "page": "/manual/core",
            "anchor": "function/Core/await",
            "snippet": "Run app targets in parallel, via the daemon pool or one-shot CLI processes",
            "tags": "await parallel cli process daemon"
        },
        {
            "title": "button(...$args):string",
            "page": "/manual/core",
            "anchor": "function/Core/button",
            "snippet": "DOM form tags for button, input, select and textarea",
            "tags": "form tags html view"
        },
        {
            "title": "camel(string $text):string",
            "page": "/manual/core",
            "anchor": "function/Core/camel",
            "snippet": "Convert text to camelCase",
            "tags": "camelcase string format"
        },
        {
            "title": "chunk(...$cmds):void",
            "page": "/manual/core",
            "anchor": "function/Core/chunk",
            "snippet": "Stream JSON chunks over CLI or Server-Sent Events",
            "tags": "chunk stream sse cli async"
        },
        {
            "title": "create(iterable $items, \\Closure $keyCb, ?\\Closure $valueCb = null):array",
            "page": "/manual/core",
            "anchor": "function/Core/create",
            "snippet": "Create associative array from iterable using callbacks",
            "tags": "create array iterable callback"
        },
        {
            "title": "en($text, ...$args):string",
            "page": "/manual/core",
            "anchor": "function/Core/en",
            "snippet": "Language and translation resource",
            "tags": "lang translation i18n locale ai"
        },
        {
            "title": "exec_stream(string $cmd, ?int $timeoutSec = 0):Generator",
            "page": "/manual/core",
            "anchor": "function/Core/exec_stream",
            "snippet": "Stream shell command output via yielding",
            "tags": "stream shell cli process yield"
        },
        {
            "title": "HTTP(string $url, array $headers = [], bool $JSON = false, $POST = null, $PUT = null, $PATCH = null, $QUERY = null, bool $DELETE = false, string|bool|null $agent = null, string|bool $cookies = false, int $timeout = 15, &$response = null)",
            "page": "/manual/core",
            "anchor": "function/Core/HTTP",
            "snippet": "HTTP request helper via cURL",
            "tags": "http curl request api"
        },
        {
            "title": "input(...$args):string",
            "page": "/manual/core",
            "anchor": "function/Core/input",
            "snippet": "DOM form tags for button, input, select and textarea",
            "tags": "form tags html view"
        },
        {
            "title": "n8n($webhook, ?array $data = null, $test = false)",
            "page": "/manual/core",
            "anchor": "function/Core/n8n",
            "snippet": "Call n8n webhook endpoint",
            "tags": "n8n webhook http automation"
        },
        {
            "title": "nl($text, ...$args):string",
            "page": "/manual/core",
            "anchor": "function/Core/nl",
            "snippet": "Language and translation resource",
            "tags": "lang translation i18n locale ai"
        },
        {
            "title": "notify(string $title, string $body = void, string $type = 'info', string $level = 'info', ?string $user = null):void",
            "page": "/manual/core",
            "anchor": "function/Core/notify",
            "snippet": "Notification to the central hub: POST to [notify].url (secret header) via Phlo's HTTP() function. No-op without [notify] config.",
            "tags": ""
        },
        {
            "title": "phlo(?string $phloName = null, ...$args):mixed",
            "page": "/manual/core",
            "anchor": "function/Core/phlo",
            "snippet": "",
            "tags": ""
        },
        {
            "title": "phlo_app(...$args):void",
            "page": "/manual/core",
            "anchor": "function/Core/phlo_app",
            "snippet": "",
            "tags": ""
        },
        {
            "title": "phlo_async(string $cb, ...$args):bool",
            "page": "/manual/core",
            "anchor": "function/Core/phlo_async",
            "snippet": "Run an app target in the background, via the daemon pool or a one-shot CLI process",
            "tags": "async cli process background app daemon"
        },
        {
            "title": "phlo_cli(array $args):void",
            "page": "/manual/core",
            "anchor": "function/Core/phlo_cli",
            "snippet": "",
            "tags": ""
        },
        {
            "title": "phlo_dispatch(string $target, array $args = []):mixed",
            "page": "/manual/core",
            "anchor": "function/Core/phlo_dispatch",
            "snippet": "",
            "tags": ""
        },
        {
            "title": "phlo_exception(Throwable $e):void",
            "page": "/manual/core",
            "anchor": "function/Core/phlo_exception",
            "snippet": "",
            "tags": ""
        },
        {
            "title": "phlo_exists(string $obj):bool",
            "page": "/manual/core",
            "anchor": "function/Core/phlo_exists",
            "snippet": "Check if compiled Phlo class exists",
            "tags": "phlo exists class build runtime"
        },
        {
            "title": "phlo_load(bool $http):void",
            "page": "/manual/core",
            "anchor": "function/Core/phlo_load",
            "snippet": "",
            "tags": ""
        },
        {
            "title": "phlo_serve():void",
            "page": "/manual/core",
            "anchor": "function/Core/phlo_serve",
            "snippet": "",
            "tags": ""
        },
        {
            "title": "phlo_stream(string $cb, ...$args):Generator",
            "page": "/manual/core",
            "anchor": "function/Core/phlo_stream",
            "snippet": "Stream an app target's output line by line, via the daemon pool or a one-shot CLI process",
            "tags": "stream phlo cli process yield daemon"
        },
        {
            "title": "phlo_sync(string $cb, ...$args)",
            "page": "/manual/core",
            "anchor": "function/Core/phlo_sync",
            "snippet": "Run an app target synchronously, via the daemon pool or a one-shot CLI process",
            "tags": "sync cli process app daemon"
        },
        {
            "title": "phlo_thread():void",
            "page": "/manual/core",
            "anchor": "function/Core/phlo_thread",
            "snippet": "",
            "tags": ""
        },
        {
            "title": "select(...$args):string",
            "page": "/manual/core",
            "anchor": "function/Core/select",
            "snippet": "DOM form tags for button, input, select and textarea",
            "tags": "form tags html view"
        },
        {
            "title": "setting(?string $key = null, $value = null):mixed",
            "page": "/manual/core",
            "anchor": "function/Core/setting",
            "snippet": "Persistent app settings in data/settings.json: setting() lists everything, setting(key) reads one value or null, setting(key, value) writes",
            "tags": "settings config storage json"
        },
        {
            "title": "slug(string $text):string",
            "page": "/manual/core",
            "anchor": "function/Core/slug",
            "snippet": "Convert text to URL slug",
            "tags": "slug url string format"
        },
        {
            "title": "stream($data = null, string $type = 'application/octet-stream', ?string $name = null):void",
            "page": "/manual/core",
            "anchor": "function/Core/stream",
            "snippet": "Raw data stream beside the JSON command channel: stream() emits text or binary chunks under any content type, app.stream() consumes them via fetch dispatching on the response type",
            "tags": "stream binary raw data download"
        },
        {
            "title": "tag(string $tagName, ?string $inner = null, ...$args):string",
            "page": "/manual/core",
            "anchor": "function/Core/tag",
            "snippet": "Generate HTML tag string with attributes",
            "tags": "tag html render view"
        },
        {
            "title": "textarea(...$args):string",
            "page": "/manual/core",
            "anchor": "function/Core/textarea",
            "snippet": "DOM form tags for button, input, select and textarea",
            "tags": "form tags html view"
        },
        {
            "title": "time_human(?int $time = null):string",
            "page": "/manual/core",
            "anchor": "function/Core/time_human",
            "snippet": "Convert timestamp age to human label",
            "tags": "time human age format"
        },
        {
            "title": "wsCast($wsTarget = 'all', $wsHost = host, $wsPort = daemon, $wsExcept = void, ...$data)",
            "page": "/manual/core",
            "anchor": "function/Core/wsCast",
            "snippet": "Broadcast a message to WebSocket clients via the daemon's cast bridge",
            "tags": "websocket cast realtime http daemon"
        },
        {
            "title": "%AI",
            "page": "/manual/ai",
            "anchor": "obj/AI",
            "snippet": "Unified AI facade with engine auto-detect",
            "tags": "ai facade llm streaming tools embeddings"
        },
        {
            "title": "%AI->model",
            "page": "/manual/ai",
            "anchor": "obj/AI/model",
            "snippet": ""
        },
        {
            "title": "%AI->engines",
            "page": "/manual/ai",
            "anchor": "obj/AI/engines",
            "snippet": ""
        },
        {
            "title": "%AI->http(string $url, array $headers, bool $json = true, mixed $post = null)",
            "page": "/manual/ai",
            "anchor": "obj/AI/http",
            "snippet": ""
        },
        {
            "title": "%AI->resolve(...$args)",
            "page": "/manual/ai",
            "anchor": "obj/AI/resolve",
            "snippet": ""
        },
        {
            "title": "%AI->chat(...$args)",
            "page": "/manual/ai",
            "anchor": "obj/AI/chat",
            "snippet": ""
        },
        {
            "title": "%AI->stream(...$args)",
            "page": "/manual/ai",
            "anchor": "obj/AI/stream",
            "snippet": ""
        },
        {
            "title": "%AI->embedding(...$args)",
            "page": "/manual/ai",
            "anchor": "obj/AI/embedding",
            "snippet": ""
        },
        {
            "title": "%AI->vision(...$args)",
            "page": "/manual/ai",
            "anchor": "obj/AI/vision",
            "snippet": ""
        },
        {
            "title": "%AI->transcribe(...$args)",
            "page": "/manual/ai",
            "anchor": "obj/AI/transcribe",
            "snippet": ""
        },
        {
            "title": "%Claude",
            "page": "/manual/ai",
            "anchor": "obj/Claude",
            "snippet": "Anthropic Claude API",
            "tags": "ai claude anthropic chat vision embeddings"
        },
        {
            "title": "%Claude->model",
            "page": "/manual/ai",
            "anchor": "obj/Claude/model",
            "snippet": ""
        },
        {
            "title": "%Claude->context(...$args)",
            "page": "/manual/ai",
            "anchor": "obj/Claude/context",
            "snippet": ""
        },
        {
            "title": "%Claude->tool($tool)",
            "page": "/manual/ai",
            "anchor": "obj/Claude/tool",
            "snippet": ""
        },
        {
            "title": "%Claude->embedding($input, $model = 'text-embedding-3-small')",
            "page": "/manual/ai",
            "anchor": "obj/Claude/embedding",
            "snippet": ""
        },
        {
            "title": "%Claude->vision($text, $image, $stream = false, ...$args)",
            "page": "/manual/ai",
            "anchor": "obj/Claude/vision",
            "snippet": ""
        },
        {
            "title": "%Claude->chat(...$args)",
            "page": "/manual/ai",
            "anchor": "obj/Claude/chat",
            "snippet": ""
        },
        {
            "title": "%Claude->parseSSE(string $url, array $headers, array $payload)",
            "page": "/manual/ai",
            "anchor": "obj/Claude/parseSSE",
            "snippet": ""
        },
        {
            "title": "%Claude->stream(...$args)",
            "page": "/manual/ai",
            "anchor": "obj/Claude/stream",
            "snippet": ""
        },
        {
            "title": "%Claude->request($uri, ...$args)",
            "page": "/manual/ai",
            "anchor": "obj/Claude/request",
            "snippet": ""
        },
        {
            "title": "%DeepSeek",
            "page": "/manual/ai",
            "anchor": "obj/DeepSeek",
            "snippet": "DeepSeek API (OpenAI-compatible, extends OpenAI)",
            "tags": "ai deepseek chat embeddings"
        },
        {
            "title": "%DeepSeek->model",
            "page": "/manual/ai",
            "anchor": "obj/DeepSeek/model",
            "snippet": ""
        },
        {
            "title": "%DeepSeek->endpoint",
            "page": "/manual/ai",
            "anchor": "obj/DeepSeek/endpoint",
            "snippet": ""
        },
        {
            "title": "%DeepSeek->cred",
            "page": "/manual/ai",
            "anchor": "obj/DeepSeek/cred",
            "snippet": ""
        },
        {
            "title": "%DeepSeek->label",
            "page": "/manual/ai",
            "anchor": "obj/DeepSeek/label",
            "snippet": ""
        },
        {
            "title": "%DeepSeek->embedding($input, $model = 'text-embedding-3-small')",
            "page": "/manual/ai",
            "anchor": "obj/DeepSeek/embedding",
            "snippet": ""
        },
        {
            "title": "%Gemini",
            "page": "/manual/ai",
            "anchor": "obj/Gemini",
            "snippet": "Google Gemini API",
            "tags": "ai gemini google chat vision embeddings"
        },
        {
            "title": "%Gemini->model",
            "page": "/manual/ai",
            "anchor": "obj/Gemini/model",
            "snippet": ""
        },
        {
            "title": "%Gemini->endpoint",
            "page": "/manual/ai",
            "anchor": "obj/Gemini/endpoint",
            "snippet": ""
        },
        {
            "title": "%Gemini->context(...$args)",
            "page": "/manual/ai",
            "anchor": "obj/Gemini/context",
            "snippet": ""
        },
        {
            "title": "%Gemini->tool($tool)",
            "page": "/manual/ai",
            "anchor": "obj/Gemini/tool",
            "snippet": ""
        },
        {
            "title": "%Gemini->embedding($input, $model = 'text-embedding-004')",
            "page": "/manual/ai",
            "anchor": "obj/Gemini/embedding",
            "snippet": ""
        },
        {
            "title": "%Gemini->vision($text, $image, $stream = false, ...$args)",
            "page": "/manual/ai",
            "anchor": "obj/Gemini/vision",
            "snippet": ""
        },
        {
            "title": "%Gemini->chat(...$args)",
            "page": "/manual/ai",
            "anchor": "obj/Gemini/chat",
            "snippet": ""
        },
        {
            "title": "%Gemini->parseSSE(string $url, array $headers, array $payload)",
            "page": "/manual/ai",
            "anchor": "obj/Gemini/parseSSE",
            "snippet": ""
        },
        {
            "title": "%Gemini->stream(...$args)",
            "page": "/manual/ai",
            "anchor": "obj/Gemini/stream",
            "snippet": ""
        },
        {
            "title": "%Gemini->request($path, ...$args)",
            "page": "/manual/ai",
            "anchor": "obj/Gemini/request",
            "snippet": ""
        },
        {
            "title": "%Grok",
            "page": "/manual/ai",
            "anchor": "obj/Grok",
            "snippet": "xAI Grok API (OpenAI-compatible, extends OpenAI)",
            "tags": "ai grok xai chat vision embeddings"
        },
        {
            "title": "%Grok->model",
            "page": "/manual/ai",
            "anchor": "obj/Grok/model",
            "snippet": ""
        },
        {
            "title": "%Grok->endpoint",
            "page": "/manual/ai",
            "anchor": "obj/Grok/endpoint",
            "snippet": ""
        },
        {
            "title": "%Grok->cred",
            "page": "/manual/ai",
            "anchor": "obj/Grok/cred",
            "snippet": ""
        },
        {
            "title": "%Grok->label",
            "page": "/manual/ai",
            "anchor": "obj/Grok/label",
            "snippet": ""
        },
        {
            "title": "%Grok->embedding($input, $model = 'text-embedding-3-small')",
            "page": "/manual/ai",
            "anchor": "obj/Grok/embedding",
            "snippet": ""
        },
        {
            "title": "%OpenAI",
            "page": "/manual/ai",
            "anchor": "obj/OpenAI",
            "snippet": "Basic OpenAI functions",
            "tags": "ai openai llm chat embeddings audio vision"
        },
        {
            "title": "%OpenAI->model",
            "page": "/manual/ai",
            "anchor": "obj/OpenAI/model",
            "snippet": ""
        },
        {
            "title": "%OpenAI->endpoint",
            "page": "/manual/ai",
            "anchor": "obj/OpenAI/endpoint",
            "snippet": ""
        },
        {
            "title": "%OpenAI->cred",
            "page": "/manual/ai",
            "anchor": "obj/OpenAI/cred",
            "snippet": ""
        },
        {
            "title": "%OpenAI->label",
            "page": "/manual/ai",
            "anchor": "obj/OpenAI/label",
            "snippet": ""
        },
        {
            "title": "%OpenAI->voices",
            "page": "/manual/ai",
            "anchor": "obj/OpenAI/voices",
            "snippet": ""
        },
        {
            "title": "%OpenAI->context(...$args)",
            "page": "/manual/ai",
            "anchor": "obj/OpenAI/context",
            "snippet": ""
        },
        {
            "title": "%OpenAI->tool($tool)",
            "page": "/manual/ai",
            "anchor": "obj/OpenAI/tool",
            "snippet": ""
        },
        {
            "title": "%OpenAI->chat(...$args)",
            "page": "/manual/ai",
            "anchor": "obj/OpenAI/chat",
            "snippet": ""
        },
        {
            "title": "%OpenAI->embedding($input, $model = 'text-embedding-3-small')",
            "page": "/manual/ai",
            "anchor": "obj/OpenAI/embedding",
            "snippet": ""
        },
        {
            "title": "%OpenAI->parseSSE(string $url, array $headers, array $payload)",
            "page": "/manual/ai",
            "anchor": "obj/OpenAI/parseSSE",
            "snippet": ""
        },
        {
            "title": "%OpenAI->stream(...$args)",
            "page": "/manual/ai",
            "anchor": "obj/OpenAI/stream",
            "snippet": ""
        },
        {
            "title": "%OpenAI->transcribe($file, $model = 'whisper-1', ...$args)",
            "page": "/manual/ai",
            "anchor": "obj/OpenAI/transcribe",
            "snippet": ""
        },
        {
            "title": "%OpenAI->vision($text, $image, $stream = false, ...$args)",
            "page": "/manual/ai",
            "anchor": "obj/OpenAI/vision",
            "snippet": ""
        },
        {
            "title": "%OpenAI->request($uri, $JSON = true, $token = null, ...$args)",
            "page": "/manual/ai",
            "anchor": "obj/OpenAI/request",
            "snippet": ""
        },
        {
            "title": "answer($question, ...$options):?string",
            "page": "/manual/ai",
            "anchor": "function/AI/answer",
            "snippet": "Simple AI answering helper",
            "tags": "ai answer question llm"
        },
        {
            "title": "%Connector",
            "page": "/manual/connectors",
            "anchor": "obj/Connector",
            "snippet": "Base class for API connectors: credentials, JSON requests, retries, pagination and a normalized result contract",
            "tags": "api connector http rest base"
        },
        {
            "title": "%Connector->section",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/section",
            "snippet": ""
        },
        {
            "title": "%Connector->api",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/api",
            "snippet": ""
        },
        {
            "title": "%Connector->__construct(?array $config = null)",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/__construct",
            "snippet": ""
        },
        {
            "title": "%Connector->make(?array $config = null)",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/make",
            "snippet": ""
        },
        {
            "title": "%Connector->base",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/base",
            "snippet": ""
        },
        {
            "title": "%Connector->headers",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/headers",
            "snippet": ""
        },
        {
            "title": "%Connector->fields",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/fields",
            "snippet": ""
        },
        {
            "title": "%Connector->configured(...$keys)",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/configured",
            "snippet": ""
        },
        {
            "title": "%Connector->missing(...$keys)",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/missing",
            "snippet": ""
        },
        {
            "title": "%Connector->bearer($token)",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/bearer",
            "snippet": ""
        },
        {
            "title": "%Connector->basic($user, $pass)",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/basic",
            "snippet": ""
        },
        {
            "title": "%Connector->build(string $method, string $url, ?array $query = null, array $headers = [], mixed $json = null, mixed $form = null)",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/build",
            "snippet": ""
        },
        {
            "title": "%Connector->ok($data, int $status = 200)",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/ok",
            "snippet": ""
        },
        {
            "title": "%Connector->fail($error, int $status = 0)",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/fail",
            "snippet": ""
        },
        {
            "title": "%Connector->errorMessage($data, string $raw, int $status)",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/errorMessage",
            "snippet": ""
        },
        {
            "title": "%Connector->parse($raw, int $status = 200)",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/parse",
            "snippet": ""
        },
        {
            "title": "%Connector->retryable($method, int $status)",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/retryable",
            "snippet": ""
        },
        {
            "title": "%Connector->backoff(int $attempt, $response)",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/backoff",
            "snippet": ""
        },
        {
            "title": "%Connector->dispatch(array $req)",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/dispatch",
            "snippet": "Retries only what retryable() allows, and waits as long as the server asked for."
        },
        {
            "title": "%Connector->request(string $method, string $url, ?array $query = null, array $headers = [], mixed $json = null, mixed $form = null)",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/request",
            "snippet": ""
        },
        {
            "title": "%Connector->get(string $url, ?array $query = null, array $headers = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/get",
            "snippet": ""
        },
        {
            "title": "%Connector->post(string $url, mixed $json = null, array $headers = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/post",
            "snippet": ""
        },
        {
            "title": "%Connector->put(string $url, mixed $json = null, array $headers = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/put",
            "snippet": ""
        },
        {
            "title": "%Connector->patch(string $url, mixed $json = null, array $headers = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/patch",
            "snippet": ""
        },
        {
            "title": "%Connector->query(string $url, mixed $json = null, array $headers = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/query",
            "snippet": ""
        },
        {
            "title": "%Connector->del(string $url, array $headers = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/del",
            "snippet": ""
        },
        {
            "title": "%Connector->form(string $url, array $fields, array $headers = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/form",
            "snippet": ""
        },
        {
            "title": "%Connector->paginate(string $url, callable $extract, ?array $query = null, string $param = 'page', int $start = 1, int $max = 0)",
            "page": "/manual/connectors",
            "anchor": "obj/Connector/paginate",
            "snippet": ""
        },
        {
            "title": "%EBoekhouden",
            "page": "/manual/connectors",
            "anchor": "obj/EBoekhouden",
            "snippet": "e-Boekhouden.nl connector: session auth from an API token, relations and sales invoices",
            "tags": "eboekhouden accounting invoices relations connector"
        },
        {
            "title": "%EBoekhouden->section",
            "page": "/manual/connectors",
            "anchor": "obj/EBoekhouden/section",
            "snippet": ""
        },
        {
            "title": "%EBoekhouden->api",
            "page": "/manual/connectors",
            "anchor": "obj/EBoekhouden/api",
            "snippet": ""
        },
        {
            "title": "%EBoekhouden->sessionToken",
            "page": "/manual/connectors",
            "anchor": "obj/EBoekhouden/sessionToken",
            "snippet": ""
        },
        {
            "title": "%EBoekhouden->headers",
            "page": "/manual/connectors",
            "anchor": "obj/EBoekhouden/headers",
            "snippet": ""
        },
        {
            "title": "%EBoekhouden->fields",
            "page": "/manual/connectors",
            "anchor": "obj/EBoekhouden/fields",
            "snippet": ""
        },
        {
            "title": "%EBoekhouden->session",
            "page": "/manual/connectors",
            "anchor": "obj/EBoekhouden/session",
            "snippet": "Returns the session token, fetching one on first use."
        },
        {
            "title": "%EBoekhouden->guard",
            "page": "/manual/connectors",
            "anchor": "obj/EBoekhouden/guard",
            "snippet": ""
        },
        {
            "title": "%EBoekhouden->relations(array $query = [])",
            "page": "/manual/connectors",
            "anchor": "obj/EBoekhouden/relations",
            "snippet": ""
        },
        {
            "title": "%EBoekhouden->createRelation(array $relation)",
            "page": "/manual/connectors",
            "anchor": "obj/EBoekhouden/createRelation",
            "snippet": ""
        },
        {
            "title": "%EBoekhouden->invoices(array $query = [])",
            "page": "/manual/connectors",
            "anchor": "obj/EBoekhouden/invoices",
            "snippet": ""
        },
        {
            "title": "%EBoekhouden->createInvoice(array $invoice)",
            "page": "/manual/connectors",
            "anchor": "obj/EBoekhouden/createInvoice",
            "snippet": ""
        },
        {
            "title": "%ExactOnline",
            "page": "/manual/connectors",
            "anchor": "obj/ExactOnline",
            "snippet": "Exact Online connector (OAuth2): read sales invoices and accounts, create sales invoices",
            "tags": "exact exactonline accounting invoices oauth connector"
        },
        {
            "title": "%ExactOnline->section",
            "page": "/manual/connectors",
            "anchor": "obj/ExactOnline/section",
            "snippet": ""
        },
        {
            "title": "%ExactOnline->tokenUrl",
            "page": "/manual/connectors",
            "anchor": "obj/ExactOnline/tokenUrl",
            "snippet": ""
        },
        {
            "title": "%ExactOnline->base",
            "page": "/manual/connectors",
            "anchor": "obj/ExactOnline/base",
            "snippet": ""
        },
        {
            "title": "%ExactOnline->fields",
            "page": "/manual/connectors",
            "anchor": "obj/ExactOnline/fields",
            "snippet": ""
        },
        {
            "title": "%ExactOnline->guard",
            "page": "/manual/connectors",
            "anchor": "obj/ExactOnline/guard",
            "snippet": ""
        },
        {
            "title": "%ExactOnline->invoices(array $query = [])",
            "page": "/manual/connectors",
            "anchor": "obj/ExactOnline/invoices",
            "snippet": ""
        },
        {
            "title": "%ExactOnline->accounts(array $query = [])",
            "page": "/manual/connectors",
            "anchor": "obj/ExactOnline/accounts",
            "snippet": ""
        },
        {
            "title": "%ExactOnline->createInvoice(array $invoice)",
            "page": "/manual/connectors",
            "anchor": "obj/ExactOnline/createInvoice",
            "snippet": ""
        },
        {
            "title": "%GoogleCalendar",
            "page": "/manual/connectors",
            "anchor": "obj/GoogleCalendar",
            "snippet": "Google Calendar connector (OAuth2): read events and create events",
            "tags": "google calendar events oauth connector"
        },
        {
            "title": "%GoogleCalendar->section",
            "page": "/manual/connectors",
            "anchor": "obj/GoogleCalendar/section",
            "snippet": ""
        },
        {
            "title": "%GoogleCalendar->tokenUrl",
            "page": "/manual/connectors",
            "anchor": "obj/GoogleCalendar/tokenUrl",
            "snippet": ""
        },
        {
            "title": "%GoogleCalendar->base",
            "page": "/manual/connectors",
            "anchor": "obj/GoogleCalendar/base",
            "snippet": ""
        },
        {
            "title": "%GoogleCalendar->fields",
            "page": "/manual/connectors",
            "anchor": "obj/GoogleCalendar/fields",
            "snippet": ""
        },
        {
            "title": "%GoogleCalendar->guard",
            "page": "/manual/connectors",
            "anchor": "obj/GoogleCalendar/guard",
            "snippet": ""
        },
        {
            "title": "%GoogleCalendar->events(string $calendarId = 'primary', array $query = [])",
            "page": "/manual/connectors",
            "anchor": "obj/GoogleCalendar/events",
            "snippet": ""
        },
        {
            "title": "%GoogleCalendar->createEvent(array $event, string $calendarId = 'primary')",
            "page": "/manual/connectors",
            "anchor": "obj/GoogleCalendar/createEvent",
            "snippet": ""
        },
        {
            "title": "%GoogleSheets",
            "page": "/manual/connectors",
            "anchor": "obj/GoogleSheets",
            "snippet": "Google Sheets connector (OAuth2): read ranges and append rows",
            "tags": "google sheets spreadsheet oauth connector"
        },
        {
            "title": "%GoogleSheets->section",
            "page": "/manual/connectors",
            "anchor": "obj/GoogleSheets/section",
            "snippet": ""
        },
        {
            "title": "%GoogleSheets->tokenUrl",
            "page": "/manual/connectors",
            "anchor": "obj/GoogleSheets/tokenUrl",
            "snippet": ""
        },
        {
            "title": "%GoogleSheets->base",
            "page": "/manual/connectors",
            "anchor": "obj/GoogleSheets/base",
            "snippet": ""
        },
        {
            "title": "%GoogleSheets->fields",
            "page": "/manual/connectors",
            "anchor": "obj/GoogleSheets/fields",
            "snippet": ""
        },
        {
            "title": "%GoogleSheets->guard",
            "page": "/manual/connectors",
            "anchor": "obj/GoogleSheets/guard",
            "snippet": ""
        },
        {
            "title": "%GoogleSheets->values($spreadsheetId, string $range)",
            "page": "/manual/connectors",
            "anchor": "obj/GoogleSheets/values",
            "snippet": ""
        },
        {
            "title": "%GoogleSheets->append($spreadsheetId, string $range, array $rows, string $valueInputOption = 'USER_ENTERED')",
            "page": "/manual/connectors",
            "anchor": "obj/GoogleSheets/append",
            "snippet": ""
        },
        {
            "title": "%Lightspeed",
            "page": "/manual/connectors",
            "anchor": "obj/Lightspeed",
            "snippet": "Lightspeed Retail (V3) connector: read customers and sales, create customers",
            "tags": "lightspeed webshop retail pos customers connector"
        },
        {
            "title": "%Lightspeed->section",
            "page": "/manual/connectors",
            "anchor": "obj/Lightspeed/section",
            "snippet": ""
        },
        {
            "title": "%Lightspeed->base",
            "page": "/manual/connectors",
            "anchor": "obj/Lightspeed/base",
            "snippet": ""
        },
        {
            "title": "%Lightspeed->headers",
            "page": "/manual/connectors",
            "anchor": "obj/Lightspeed/headers",
            "snippet": ""
        },
        {
            "title": "%Lightspeed->fields",
            "page": "/manual/connectors",
            "anchor": "obj/Lightspeed/fields",
            "snippet": ""
        },
        {
            "title": "%Lightspeed->customers(array $query = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Lightspeed/customers",
            "snippet": ""
        },
        {
            "title": "%Lightspeed->findCustomer($participant)",
            "page": "/manual/connectors",
            "anchor": "obj/Lightspeed/findCustomer",
            "snippet": ""
        },
        {
            "title": "%Lightspeed->customer($id)",
            "page": "/manual/connectors",
            "anchor": "obj/Lightspeed/customer",
            "snippet": ""
        },
        {
            "title": "%Lightspeed->sales(array $query = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Lightspeed/sales",
            "snippet": ""
        },
        {
            "title": "%Lightspeed->createCustomer(array $customer)",
            "page": "/manual/connectors",
            "anchor": "obj/Lightspeed/createCustomer",
            "snippet": ""
        },
        {
            "title": "%MessageBird",
            "page": "/manual/connectors",
            "anchor": "obj/MessageBird",
            "snippet": "MessageBird connector: send SMS",
            "tags": "messagebird sms messaging connector"
        },
        {
            "title": "%MessageBird->section",
            "page": "/manual/connectors",
            "anchor": "obj/MessageBird/section",
            "snippet": ""
        },
        {
            "title": "%MessageBird->base",
            "page": "/manual/connectors",
            "anchor": "obj/MessageBird/base",
            "snippet": ""
        },
        {
            "title": "%MessageBird->headers",
            "page": "/manual/connectors",
            "anchor": "obj/MessageBird/headers",
            "snippet": ""
        },
        {
            "title": "%MessageBird->fields",
            "page": "/manual/connectors",
            "anchor": "obj/MessageBird/fields",
            "snippet": ""
        },
        {
            "title": "%MessageBird->errorMessage($data, string $raw, int $status)",
            "page": "/manual/connectors",
            "anchor": "obj/MessageBird/errorMessage",
            "snippet": ""
        },
        {
            "title": "%MessageBird->sms($to, $body, array $extra = [])",
            "page": "/manual/connectors",
            "anchor": "obj/MessageBird/sms",
            "snippet": ""
        },
        {
            "title": "%MicrosoftGraph",
            "page": "/manual/connectors",
            "anchor": "obj/MicrosoftGraph",
            "snippet": "Microsoft Graph connector (app-only client credentials): read users and calendars, send mail, create events",
            "tags": "microsoft graph office365 calendar mail connector"
        },
        {
            "title": "%MicrosoftGraph->section",
            "page": "/manual/connectors",
            "anchor": "obj/MicrosoftGraph/section",
            "snippet": ""
        },
        {
            "title": "%MicrosoftGraph->base",
            "page": "/manual/connectors",
            "anchor": "obj/MicrosoftGraph/base",
            "snippet": ""
        },
        {
            "title": "%MicrosoftGraph->headers",
            "page": "/manual/connectors",
            "anchor": "obj/MicrosoftGraph/headers",
            "snippet": ""
        },
        {
            "title": "%MicrosoftGraph->fields",
            "page": "/manual/connectors",
            "anchor": "obj/MicrosoftGraph/fields",
            "snippet": ""
        },
        {
            "title": "%MicrosoftGraph->token",
            "page": "/manual/connectors",
            "anchor": "obj/MicrosoftGraph/token",
            "snippet": ""
        },
        {
            "title": "%MicrosoftGraph->fetchToken",
            "page": "/manual/connectors",
            "anchor": "obj/MicrosoftGraph/fetchToken",
            "snippet": "Caches the app-only token in APCu until a minute before it expires."
        },
        {
            "title": "%MicrosoftGraph->mailbox($user = null)",
            "page": "/manual/connectors",
            "anchor": "obj/MicrosoftGraph/mailbox",
            "snippet": ""
        },
        {
            "title": "%MicrosoftGraph->users(array $query = [])",
            "page": "/manual/connectors",
            "anchor": "obj/MicrosoftGraph/users",
            "snippet": ""
        },
        {
            "title": "%MicrosoftGraph->user($id)",
            "page": "/manual/connectors",
            "anchor": "obj/MicrosoftGraph/user",
            "snippet": ""
        },
        {
            "title": "%MicrosoftGraph->events($user = null, array $query = [])",
            "page": "/manual/connectors",
            "anchor": "obj/MicrosoftGraph/events",
            "snippet": ""
        },
        {
            "title": "%MicrosoftGraph->sendMail($message, $user = null, bool $save = true)",
            "page": "/manual/connectors",
            "anchor": "obj/MicrosoftGraph/sendMail",
            "snippet": ""
        },
        {
            "title": "%MicrosoftGraph->createEvent(array $event, $user = null)",
            "page": "/manual/connectors",
            "anchor": "obj/MicrosoftGraph/createEvent",
            "snippet": ""
        },
        {
            "title": "%Moneybird",
            "page": "/manual/connectors",
            "anchor": "obj/Moneybird",
            "snippet": "Moneybird connector: read contacts and invoices, create sales invoices",
            "tags": "moneybird accounting invoices contacts connector"
        },
        {
            "title": "%Moneybird->section",
            "page": "/manual/connectors",
            "anchor": "obj/Moneybird/section",
            "snippet": ""
        },
        {
            "title": "%Moneybird->base",
            "page": "/manual/connectors",
            "anchor": "obj/Moneybird/base",
            "snippet": ""
        },
        {
            "title": "%Moneybird->headers",
            "page": "/manual/connectors",
            "anchor": "obj/Moneybird/headers",
            "snippet": ""
        },
        {
            "title": "%Moneybird->fields",
            "page": "/manual/connectors",
            "anchor": "obj/Moneybird/fields",
            "snippet": ""
        },
        {
            "title": "%Moneybird->contacts(array $query = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Moneybird/contacts",
            "snippet": ""
        },
        {
            "title": "%Moneybird->findContact($query)",
            "page": "/manual/connectors",
            "anchor": "obj/Moneybird/findContact",
            "snippet": ""
        },
        {
            "title": "%Moneybird->contact($id)",
            "page": "/manual/connectors",
            "anchor": "obj/Moneybird/contact",
            "snippet": ""
        },
        {
            "title": "%Moneybird->invoices(array $query = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Moneybird/invoices",
            "snippet": ""
        },
        {
            "title": "%Moneybird->createContact(array $contact)",
            "page": "/manual/connectors",
            "anchor": "obj/Moneybird/createContact",
            "snippet": ""
        },
        {
            "title": "%Moneybird->createInvoice(array $invoice)",
            "page": "/manual/connectors",
            "anchor": "obj/Moneybird/createInvoice",
            "snippet": ""
        },
        {
            "title": "%OAuthConnector",
            "page": "/manual/connectors",
            "anchor": "obj/OAuthConnector",
            "snippet": "Base class for OAuth2 connectors: stored, auto-refreshed bearer access tokens via TokenStore, on the OAuth2 primitive",
            "tags": "oauth oauth2 connector base token refresh"
        },
        {
            "title": "%OAuthConnector->tokenUrl",
            "page": "/manual/connectors",
            "anchor": "obj/OAuthConnector/tokenUrl",
            "snippet": ""
        },
        {
            "title": "%OAuthConnector->oauthKey",
            "page": "/manual/connectors",
            "anchor": "obj/OAuthConnector/oauthKey",
            "snippet": ""
        },
        {
            "title": "%OAuthConnector->token",
            "page": "/manual/connectors",
            "anchor": "obj/OAuthConnector/token",
            "snippet": ""
        },
        {
            "title": "%OAuthConnector->headers",
            "page": "/manual/connectors",
            "anchor": "obj/OAuthConnector/headers",
            "snippet": ""
        },
        {
            "title": "%OAuthConnector->authed",
            "page": "/manual/connectors",
            "anchor": "obj/OAuthConnector/authed",
            "snippet": ""
        },
        {
            "title": "%Resend",
            "page": "/manual/connectors",
            "anchor": "obj/Resend",
            "snippet": "Resend connector: send transactional email via the HTTP API",
            "tags": "resend email transactional messaging connector"
        },
        {
            "title": "%Resend->section",
            "page": "/manual/connectors",
            "anchor": "obj/Resend/section",
            "snippet": ""
        },
        {
            "title": "%Resend->base",
            "page": "/manual/connectors",
            "anchor": "obj/Resend/base",
            "snippet": ""
        },
        {
            "title": "%Resend->headers",
            "page": "/manual/connectors",
            "anchor": "obj/Resend/headers",
            "snippet": ""
        },
        {
            "title": "%Resend->fields",
            "page": "/manual/connectors",
            "anchor": "obj/Resend/fields",
            "snippet": ""
        },
        {
            "title": "%Resend->send($to, $subject, $html = void, array $extra = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Resend/send",
            "snippet": ""
        },
        {
            "title": "%Shopify",
            "page": "/manual/connectors",
            "anchor": "obj/Shopify",
            "snippet": "Shopify Admin API connector: read customers, orders and products; create draft orders and products; update inventory",
            "tags": "shopify webshop ecommerce orders products connector"
        },
        {
            "title": "%Shopify->section",
            "page": "/manual/connectors",
            "anchor": "obj/Shopify/section",
            "snippet": ""
        },
        {
            "title": "%Shopify->base",
            "page": "/manual/connectors",
            "anchor": "obj/Shopify/base",
            "snippet": ""
        },
        {
            "title": "%Shopify->headers",
            "page": "/manual/connectors",
            "anchor": "obj/Shopify/headers",
            "snippet": ""
        },
        {
            "title": "%Shopify->fields",
            "page": "/manual/connectors",
            "anchor": "obj/Shopify/fields",
            "snippet": ""
        },
        {
            "title": "%Shopify->customers(array $query = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Shopify/customers",
            "snippet": ""
        },
        {
            "title": "%Shopify->searchCustomers($query, int $limit = 10)",
            "page": "/manual/connectors",
            "anchor": "obj/Shopify/searchCustomers",
            "snippet": ""
        },
        {
            "title": "%Shopify->customer($id)",
            "page": "/manual/connectors",
            "anchor": "obj/Shopify/customer",
            "snippet": ""
        },
        {
            "title": "%Shopify->orders(array $query = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Shopify/orders",
            "snippet": ""
        },
        {
            "title": "%Shopify->products(array $query = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Shopify/products",
            "snippet": ""
        },
        {
            "title": "%Shopify->createDraftOrder(array $order)",
            "page": "/manual/connectors",
            "anchor": "obj/Shopify/createDraftOrder",
            "snippet": ""
        },
        {
            "title": "%Shopify->createProduct(array $product)",
            "page": "/manual/connectors",
            "anchor": "obj/Shopify/createProduct",
            "snippet": ""
        },
        {
            "title": "%Shopify->setInventory($inventoryItemId, $locationId, int $available)",
            "page": "/manual/connectors",
            "anchor": "obj/Shopify/setInventory",
            "snippet": ""
        },
        {
            "title": "%Slack",
            "page": "/manual/connectors",
            "anchor": "obj/Slack",
            "snippet": "Slack connector: post messages, read channel history and list channels",
            "tags": "slack messaging chat connector"
        },
        {
            "title": "%Slack->section",
            "page": "/manual/connectors",
            "anchor": "obj/Slack/section",
            "snippet": ""
        },
        {
            "title": "%Slack->api",
            "page": "/manual/connectors",
            "anchor": "obj/Slack/api",
            "snippet": ""
        },
        {
            "title": "%Slack->headers",
            "page": "/manual/connectors",
            "anchor": "obj/Slack/headers",
            "snippet": ""
        },
        {
            "title": "%Slack->fields",
            "page": "/manual/connectors",
            "anchor": "obj/Slack/fields",
            "snippet": ""
        },
        {
            "title": "%Slack->result(obj $res)",
            "page": "/manual/connectors",
            "anchor": "obj/Slack/result",
            "snippet": ""
        },
        {
            "title": "%Slack->send($channel, $text, array $extra = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Slack/send",
            "snippet": ""
        },
        {
            "title": "%Slack->history($channel, int $limit = 20)",
            "page": "/manual/connectors",
            "anchor": "obj/Slack/history",
            "snippet": ""
        },
        {
            "title": "%Slack->channels(int $limit = 100, string $types = 'public_channel')",
            "page": "/manual/connectors",
            "anchor": "obj/Slack/channels",
            "snippet": ""
        },
        {
            "title": "%Telegram",
            "page": "/manual/connectors",
            "anchor": "obj/Telegram",
            "snippet": "Telegram Bot API connector: send messages, photos and documents; poll updates",
            "tags": "telegram bot messaging chat connector"
        },
        {
            "title": "%Telegram->section",
            "page": "/manual/connectors",
            "anchor": "obj/Telegram/section",
            "snippet": ""
        },
        {
            "title": "%Telegram->base",
            "page": "/manual/connectors",
            "anchor": "obj/Telegram/base",
            "snippet": ""
        },
        {
            "title": "%Telegram->fields",
            "page": "/manual/connectors",
            "anchor": "obj/Telegram/fields",
            "snippet": ""
        },
        {
            "title": "%Telegram->result(obj $res)",
            "page": "/manual/connectors",
            "anchor": "obj/Telegram/result",
            "snippet": ""
        },
        {
            "title": "%Telegram->send($chatId, $text, array $extra = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Telegram/send",
            "snippet": ""
        },
        {
            "title": "%Telegram->photo($chatId, $photo, $caption = void, array $extra = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Telegram/photo",
            "snippet": ""
        },
        {
            "title": "%Telegram->document($chatId, $document, $caption = void, array $extra = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Telegram/document",
            "snippet": ""
        },
        {
            "title": "%Telegram->updates(int $offset = 0, int $limit = 100)",
            "page": "/manual/connectors",
            "anchor": "obj/Telegram/updates",
            "snippet": ""
        },
        {
            "title": "%TokenStore",
            "page": "/manual/connectors",
            "anchor": "obj/TokenStore",
            "snippet": "Persisted OAuth2 token store with automatic refresh via the OAuth2 resource",
            "tags": "oauth oauth2 token refresh store credentials"
        },
        {
            "title": "%TokenStore->path($key)",
            "page": "/manual/connectors",
            "anchor": "obj/TokenStore/path",
            "snippet": ""
        },
        {
            "title": "%TokenStore->read($key)",
            "page": "/manual/connectors",
            "anchor": "obj/TokenStore/read",
            "snippet": ""
        },
        {
            "title": "%TokenStore->write($key, array $token)",
            "page": "/manual/connectors",
            "anchor": "obj/TokenStore/write",
            "snippet": ""
        },
        {
            "title": "%TokenStore->valid(array $token)",
            "page": "/manual/connectors",
            "anchor": "obj/TokenStore/valid",
            "snippet": ""
        },
        {
            "title": "%TokenStore->store($res, $refresh)",
            "page": "/manual/connectors",
            "anchor": "obj/TokenStore/store",
            "snippet": ""
        },
        {
            "title": "%TokenStore->lock($key)",
            "page": "/manual/connectors",
            "anchor": "obj/TokenStore/lock",
            "snippet": "Takes an exclusive lock around one key's refresh cycle."
        },
        {
            "title": "%TokenStore->access($key, $tokenUrl, $clientId, $clientSecret, array $seed = [])",
            "page": "/manual/connectors",
            "anchor": "obj/TokenStore/access",
            "snippet": ""
        },
        {
            "title": "%Twilio",
            "page": "/manual/connectors",
            "anchor": "obj/Twilio",
            "snippet": "Twilio connector: send SMS and read message status",
            "tags": "twilio sms messaging connector"
        },
        {
            "title": "%Twilio->section",
            "page": "/manual/connectors",
            "anchor": "obj/Twilio/section",
            "snippet": ""
        },
        {
            "title": "%Twilio->base",
            "page": "/manual/connectors",
            "anchor": "obj/Twilio/base",
            "snippet": ""
        },
        {
            "title": "%Twilio->headers",
            "page": "/manual/connectors",
            "anchor": "obj/Twilio/headers",
            "snippet": ""
        },
        {
            "title": "%Twilio->fields",
            "page": "/manual/connectors",
            "anchor": "obj/Twilio/fields",
            "snippet": ""
        },
        {
            "title": "%Twilio->sms($to, $body, array $extra = [])",
            "page": "/manual/connectors",
            "anchor": "obj/Twilio/sms",
            "snippet": ""
        },
        {
            "title": "%Twilio->message($sid)",
            "page": "/manual/connectors",
            "anchor": "obj/Twilio/message",
            "snippet": ""
        },
        {
            "title": "%DB",
            "page": "/manual/db",
            "anchor": "obj/DB",
            "snippet": "Database engine class",
            "tags": "database pdo sql"
        },
        {
            "title": "%DB->PDO",
            "page": "/manual/db",
            "anchor": "obj/DB/PDO",
            "snippet": ""
        },
        {
            "title": "%DB->fieldQuotes",
            "page": "/manual/db",
            "anchor": "obj/DB/fieldQuotes",
            "snippet": ""
        },
        {
            "title": "%DB->savepoint",
            "page": "/manual/db",
            "anchor": "obj/DB/savepoint",
            "snippet": ""
        },
        {
            "title": "%DB->insertIgnore",
            "page": "/manual/db",
            "anchor": "obj/DB/insertIgnore",
            "snippet": ""
        },
        {
            "title": "%DB->insertOnConflict",
            "page": "/manual/db",
            "anchor": "obj/DB/insertOnConflict",
            "snippet": ""
        },
        {
            "title": "%DB->load(string $table, string $columns = '*', string $where = void, string $joins = void, string $group = void, string $limit = void, string $order = void, ...$args)",
            "page": "/manual/db",
            "anchor": "obj/DB/load",
            "snippet": ""
        },
        {
            "title": "%DB->query($query, ...$args)",
            "page": "/manual/db",
            "anchor": "obj/DB/query",
            "snippet": ""
        },
        {
            "title": "%DB->queryRun($query, $args, $retry)",
            "page": "/manual/db",
            "anchor": "obj/DB/queryRun",
            "snippet": "Runs a statement, retrying once when the connection was lost."
        },
        {
            "title": "%DB->goneAway($e)",
            "page": "/manual/db",
            "anchor": "obj/DB/goneAway",
            "snippet": ""
        },
        {
            "title": "%DB->column(...$args)",
            "page": "/manual/db",
            "anchor": "obj/DB/column",
            "snippet": ""
        },
        {
            "title": "%DB->item(...$args)",
            "page": "/manual/db",
            "anchor": "obj/DB/item",
            "snippet": ""
        },
        {
            "title": "%DB->pair(...$args)",
            "page": "/manual/db",
            "anchor": "obj/DB/pair",
            "snippet": ""
        },
        {
            "title": "%DB->group(...$args)",
            "page": "/manual/db",
            "anchor": "obj/DB/group",
            "snippet": ""
        },
        {
            "title": "%DB->records(...$args)",
            "page": "/manual/db",
            "anchor": "obj/DB/records",
            "snippet": ""
        },
        {
            "title": "%DB->rows(...$args)",
            "page": "/manual/db",
            "anchor": "obj/DB/rows",
            "snippet": ""
        },
        {
            "title": "%DB->record(...$args)",
            "page": "/manual/db",
            "anchor": "obj/DB/record",
            "snippet": ""
        },
        {
            "title": "%DB->quoteList(array $ids)",
            "page": "/manual/db",
            "anchor": "obj/DB/quoteList",
            "snippet": ""
        },
        {
            "title": "%DB->quoteId($id)",
            "page": "/manual/db",
            "anchor": "obj/DB/quoteId",
            "snippet": "Safely quotes a SQL identifier such as a column or table name."
        },
        {
            "title": "%DB->create(string $table, ...$data)",
            "page": "/manual/db",
            "anchor": "obj/DB/create",
            "snippet": ""
        },
        {
            "title": "%DB->lastId",
            "page": "/manual/db",
            "anchor": "obj/DB/lastId",
            "snippet": ""
        },
        {
            "title": "%DB->change(string $table, string $where, ...$data)",
            "page": "/manual/db",
            "anchor": "obj/DB/change",
            "snippet": ""
        },
        {
            "title": "%DB->delete(string $table, string $where, ...$args)",
            "page": "/manual/db",
            "anchor": "obj/DB/delete",
            "snippet": ""
        },
        {
            "title": "%DB->begin",
            "page": "/manual/db",
            "anchor": "obj/DB/begin",
            "snippet": ""
        },
        {
            "title": "%DB->commit",
            "page": "/manual/db",
            "anchor": "obj/DB/commit",
            "snippet": ""
        },
        {
            "title": "%DB->rollback",
            "page": "/manual/db",
            "anchor": "obj/DB/rollback",
            "snippet": ""
        },
        {
            "title": "%DB->transaction($callback)",
            "page": "/manual/db",
            "anchor": "obj/DB/transaction",
            "snippet": ""
        },
        {
            "title": "%JSONDB",
            "page": "/manual/db",
            "anchor": "obj/JSONDB",
            "snippet": "JSON file database driver. One JSONDB instance = one JSON file = one model table. No joins, no transactions, no schema introspection.",
            "tags": "json database file storage"
        },
        {
            "title": "%JSONDB->__handle",
            "page": "/manual/db",
            "anchor": "obj/JSONDB/__handle",
            "snippet": ""
        },
        {
            "title": "%JSONDB->__construct(private string $file)",
            "page": "/manual/db",
            "anchor": "obj/JSONDB/__construct",
            "snippet": ""
        },
        {
            "title": "%JSONDB->PDO",
            "page": "/manual/db",
            "anchor": "obj/JSONDB/PDO",
            "snippet": ""
        },
        {
            "title": "%JSONDB->fieldQuotes",
            "page": "/manual/db",
            "anchor": "obj/JSONDB/fieldQuotes",
            "snippet": ""
        },
        {
            "title": "%JSONDB->lastInsertedId",
            "page": "/manual/db",
            "anchor": "obj/JSONDB/lastInsertedId",
            "snippet": ""
        },
        {
            "title": "%JSONDB->quoteList(array $ids)",
            "page": "/manual/db",
            "anchor": "obj/JSONDB/quoteList",
            "snippet": "Builds the quoted id list an IN clause expects."
        },
        {
            "title": "%JSONDB->objRead",
            "page": "/manual/db",
            "anchor": "obj/JSONDB/objRead",
            "snippet": ""
        },
        {
            "title": "%JSONDB->objWrite(array $data)",
            "page": "/manual/db",
            "anchor": "obj/JSONDB/objWrite",
            "snippet": ""
        },
        {
            "title": "%JSONDB->objNextId(array $data)",
            "page": "/manual/db",
            "anchor": "obj/JSONDB/objNextId",
            "snippet": ""
        },
        {
            "title": "%JSONDB->objFilter(array $data, string $where = void, ...$args)",
            "page": "/manual/db",
            "anchor": "obj/JSONDB/objFilter",
            "snippet": ""
        },
        {
            "title": "%JSONDB->objSelect(string $where = void, string $limit = void, string $order = void, ...$args)",
            "page": "/manual/db",
            "anchor": "obj/JSONDB/objSelect",
            "snippet": ""
        },
        {
            "title": "%JSONDB->create(string $table, ...$data)",
            "page": "/manual/db",
            "anchor": "obj/JSONDB/create",
            "snippet": "ignore compares ids loosely, so the string \"7\" and the number 7 count as one row."
        },
        {
            "title": "%JSONDB->change(string $table, string $where, ...$data)",
            "page": "/manual/db",
            "anchor": "obj/JSONDB/change",
            "snippet": ""
        },
        {
            "title": "%JSONDB->delete(string $table, string $where, ...$args)",
            "page": "/manual/db",
            "anchor": "obj/JSONDB/delete",
            "snippet": ""
        },
        {
            "title": "%JSONDB->load(string $table, string $columns = '*', string $where = void, string $joins = void, string $group = void, string $limit = void, string $order = void, ...$args)",
            "page": "/manual/db",
            "anchor": "obj/JSONDB/load",
            "snippet": ""
        },
        {
            "title": "%JSONDB->query($query, ...$args)",
            "page": "/manual/db",
            "anchor": "obj/JSONDB/query",
            "snippet": ""
        },
        {
            "title": "%JSONDB->begin",
            "page": "/manual/db",
            "anchor": "obj/JSONDB/begin",
            "snippet": ""
        },
        {
            "title": "%JSONDB->commit",
            "page": "/manual/db",
            "anchor": "obj/JSONDB/commit",
            "snippet": ""
        },
        {
            "title": "%JSONDB->rollback",
            "page": "/manual/db",
            "anchor": "obj/JSONDB/rollback",
            "snippet": ""
        },
        {
            "title": "%JSON_result",
            "page": "/manual/db",
            "anchor": "obj/JSON_result",
            "snippet": "Minimal PDOStatement-like wrapper for JSONDB result arrays",
            "tags": "json database result"
        },
        {
            "title": "%JSON_result->__handle",
            "page": "/manual/db",
            "anchor": "obj/JSON_result/__handle",
            "snippet": ""
        },
        {
            "title": "%JSON_result->data",
            "page": "/manual/db",
            "anchor": "obj/JSON_result/data",
            "snippet": ""
        },
        {
            "title": "%JSON_result->__construct(array $data)",
            "page": "/manual/db",
            "anchor": "obj/JSON_result/__construct",
            "snippet": ""
        },
        {
            "title": "%JSON_result->fetchAll($mode = 2)",
            "page": "/manual/db",
            "anchor": "obj/JSON_result/fetchAll",
            "snippet": ""
        },
        {
            "title": "%JSON_result->fetchObject($class = 'obj')",
            "page": "/manual/db",
            "anchor": "obj/JSON_result/fetchObject",
            "snippet": ""
        },
        {
            "title": "%JSON_result->fetch($mode = 2)",
            "page": "/manual/db",
            "anchor": "obj/JSON_result/fetch",
            "snippet": ""
        },
        {
            "title": "%JSON_result->fetchColumn($col = 0)",
            "page": "/manual/db",
            "anchor": "obj/JSON_result/fetchColumn",
            "snippet": ""
        },
        {
            "title": "%JSON_result->rowCount",
            "page": "/manual/db",
            "anchor": "obj/JSON_result/rowCount",
            "snippet": ""
        },
        {
            "title": "%model",
            "page": "/manual/db",
            "anchor": "obj/model",
            "snippet": "Phlo ORM class with unified columns and schema",
            "tags": "orm model database records schema"
        },
        {
            "title": "%model->DB",
            "page": "/manual/db",
            "anchor": "obj/model/DB",
            "snippet": ""
        },
        {
            "title": "%model->objCache",
            "page": "/manual/db",
            "anchor": "obj/model/objCache",
            "snippet": ""
        },
        {
            "title": "%model->objRecordLimit",
            "page": "/manual/db",
            "anchor": "obj/model/objRecordLimit",
            "snippet": ""
        },
        {
            "title": "%model->objAudit",
            "page": "/manual/db",
            "anchor": "obj/model/objAudit",
            "snippet": ""
        },
        {
            "title": "%model->objValidate",
            "page": "/manual/db",
            "anchor": "obj/model/objValidate",
            "snippet": ""
        },
        {
            "title": "%model->idColumn",
            "page": "/manual/db",
            "anchor": "obj/model/idColumn",
            "snippet": ""
        },
        {
            "title": "%model->idType",
            "page": "/manual/db",
            "anchor": "obj/model/idType",
            "snippet": ""
        },
        {
            "title": "%model->canView",
            "page": "/manual/db",
            "anchor": "obj/model/canView",
            "snippet": ""
        },
        {
            "title": "%model->canCreate",
            "page": "/manual/db",
            "anchor": "obj/model/canCreate",
            "snippet": ""
        },
        {
            "title": "%model->canChange",
            "page": "/manual/db",
            "anchor": "obj/model/canChange",
            "snippet": ""
        },
        {
            "title": "%model->canDelete",
            "page": "/manual/db",
            "anchor": "obj/model/canDelete",
            "snippet": ""
        },
        {
            "title": "%model->state",
            "page": "/manual/db",
            "anchor": "obj/model/state",
            "snippet": ""
        },
        {
            "title": "%model->columns",
            "page": "/manual/db",
            "anchor": "obj/model/columns",
            "snippet": ""
        },
        {
            "title": "%model->_columns",
            "page": "/manual/db",
            "anchor": "obj/model/_columns",
            "snippet": ""
        },
        {
            "title": "%model->fields",
            "page": "/manual/db",
            "anchor": "obj/model/fields",
            "snippet": ""
        },
        {
            "title": "%model->_fields",
            "page": "/manual/db",
            "anchor": "obj/model/_fields",
            "snippet": ""
        },
        {
            "title": "%model->field($name)",
            "page": "/manual/db",
            "anchor": "obj/model/field",
            "snippet": ""
        },
        {
            "title": "%model->create(...$args)",
            "page": "/manual/db",
            "anchor": "obj/model/create",
            "snippet": ""
        },
        {
            "title": "%model->objCreateCommit($record, $pk)",
            "page": "/manual/db",
            "anchor": "obj/model/objCreateCommit",
            "snippet": ""
        },
        {
            "title": "%model->objRunValidation($data)",
            "page": "/manual/db",
            "anchor": "obj/model/objRunValidation",
            "snippet": ""
        },
        {
            "title": "%model->objErrors",
            "page": "/manual/db",
            "anchor": "obj/model/objErrors",
            "snippet": ""
        },
        {
            "title": "%model->createRecord(...$args)",
            "page": "/manual/db",
            "anchor": "obj/model/createRecord",
            "snippet": ""
        },
        {
            "title": "%model->change($where, ...$args)",
            "page": "/manual/db",
            "anchor": "obj/model/change",
            "snippet": ""
        },
        {
            "title": "%model->delete($where, ...$args)",
            "page": "/manual/db",
            "anchor": "obj/model/delete",
            "snippet": ""
        },
        {
            "title": "%model->objDeleteCommit($where, $args, $records)",
            "page": "/manual/db",
            "anchor": "obj/model/objDeleteCommit",
            "snippet": ""
        },
        {
            "title": "%model->objLogChange($where, ...$args)",
            "page": "/manual/db",
            "anchor": "obj/model/objLogChange",
            "snippet": ""
        },
        {
            "title": "%model->objSave",
            "page": "/manual/db",
            "anchor": "obj/model/objSave",
            "snippet": ""
        },
        {
            "title": "%model->objSaveCreate($pk, $pkValue)",
            "page": "/manual/db",
            "anchor": "obj/model/objSaveCreate",
            "snippet": ""
        },
        {
            "title": "%model->transaction($callback)",
            "page": "/manual/db",
            "anchor": "obj/model/transaction",
            "snippet": ""
        },
        {
            "title": "%model->query",
            "page": "/manual/db",
            "anchor": "obj/model/query",
            "snippet": ""
        },
        {
            "title": "%model->column(...$args)",
            "page": "/manual/db",
            "anchor": "obj/model/column",
            "snippet": ""
        },
        {
            "title": "%model->item(...$args)",
            "page": "/manual/db",
            "anchor": "obj/model/item",
            "snippet": ""
        },
        {
            "title": "%model->pair(...$args)",
            "page": "/manual/db",
            "anchor": "obj/model/pair",
            "snippet": ""
        },
        {
            "title": "%model->records(...$args)",
            "page": "/manual/db",
            "anchor": "obj/model/records",
            "snippet": ""
        },
        {
            "title": "%model->recordCount(...$args)",
            "page": "/manual/db",
            "anchor": "obj/model/recordCount",
            "snippet": ""
        },
        {
            "title": "%model->record(...$args)",
            "page": "/manual/db",
            "anchor": "obj/model/record",
            "snippet": ""
        },
        {
            "title": "%model->recordsLoad($args, $fetch, $fetchMode, $saveRelations = false)",
            "page": "/manual/db",
            "anchor": "obj/model/recordsLoad",
            "snippet": "Loads records and keeps a full record read on the request-local state for relation reuse."
        },
        {
            "title": "%model->objRel($key)",
            "page": "/manual/db",
            "anchor": "obj/model/objRel",
            "snippet": ""
        },
        {
            "title": "%model->objState",
            "page": "/manual/db",
            "anchor": "obj/model/objState",
            "snippet": ""
        },
        {
            "title": "%model->objGet($key)",
            "page": "/manual/db",
            "anchor": "obj/model/objGet",
            "snippet": ""
        },
        {
            "title": "%model->objIn($ids, $db = null)",
            "page": "/manual/db",
            "anchor": "obj/model/objIn",
            "snippet": ""
        },
        {
            "title": "%model->objMirror(string $bucket, $key)",
            "page": "/manual/db",
            "anchor": "obj/model/objMirror",
            "snippet": "Mirrors a freshly loaded relation onto a stale reference to the same record."
        },
        {
            "title": "%model->getParent($key)",
            "page": "/manual/db",
            "anchor": "obj/model/getParent",
            "snippet": ""
        },
        {
            "title": "%model->getChildren($key)",
            "page": "/manual/db",
            "anchor": "obj/model/getChildren",
            "snippet": ""
        },
        {
            "title": "%model->getMany($key)",
            "page": "/manual/db",
            "anchor": "obj/model/getMany",
            "snippet": ""
        },
        {
            "title": "%model->getCount($key)",
            "page": "/manual/db",
            "anchor": "obj/model/getCount",
            "snippet": ""
        },
        {
            "title": "%model->getLast($key)",
            "page": "/manual/db",
            "anchor": "obj/model/getLast",
            "snippet": ""
        },
        {
            "title": "%model->objResolveClass($name)",
            "page": "/manual/db",
            "anchor": "obj/model/objResolveClass",
            "snippet": ""
        },
        {
            "title": "%model->objShortName($class = null)",
            "page": "/manual/db",
            "anchor": "obj/model/objShortName",
            "snippet": ""
        },
        {
            "title": "%model->objParents",
            "page": "/manual/db",
            "anchor": "obj/model/objParents",
            "snippet": ""
        },
        {
            "title": "%model->objChildren",
            "page": "/manual/db",
            "anchor": "obj/model/objChildren",
            "snippet": ""
        },
        {
            "title": "%model->objMany",
            "page": "/manual/db",
            "anchor": "obj/model/objMany",
            "snippet": ""
        },
        {
            "title": "%MySQL",
            "page": "/manual/db",
            "anchor": "obj/MySQL",
            "snippet": "MySQL handler via DB class",
            "tags": "mysql pdo database sql"
        },
        {
            "title": "%MySQL->PDO",
            "page": "/manual/db",
            "anchor": "obj/MySQL/PDO",
            "snippet": ""
        },
        {
            "title": "%PostgreSQL",
            "page": "/manual/db",
            "anchor": "obj/PostgreSQL",
            "snippet": "PostgreSQL resource",
            "tags": "postgresql pdo database sql"
        },
        {
            "title": "%PostgreSQL->PDO",
            "page": "/manual/db",
            "anchor": "obj/PostgreSQL/PDO",
            "snippet": ""
        },
        {
            "title": "%PostgreSQL->fieldQuotes",
            "page": "/manual/db",
            "anchor": "obj/PostgreSQL/fieldQuotes",
            "snippet": ""
        },
        {
            "title": "%PostgreSQL->insertIgnore",
            "page": "/manual/db",
            "anchor": "obj/PostgreSQL/insertIgnore",
            "snippet": ""
        },
        {
            "title": "%PostgreSQL->insertOnConflict",
            "page": "/manual/db",
            "anchor": "obj/PostgreSQL/insertOnConflict",
            "snippet": ""
        },
        {
            "title": "%PostgreSQL->lastId",
            "page": "/manual/db",
            "anchor": "obj/PostgreSQL/lastId",
            "snippet": "Asks for the last id inside a savepoint, so a failed lastval() cannot abort the transaction."
        },
        {
            "title": "%Qdrant",
            "page": "/manual/db",
            "anchor": "obj/Qdrant",
            "snippet": "Embeddings resource with Qdrant",
            "tags": "qdrant embeddings vector search ai"
        },
        {
            "title": "%Qdrant->get(string $input, ?string $model = null)",
            "page": "/manual/db",
            "anchor": "obj/Qdrant/get",
            "snippet": ""
        },
        {
            "title": "%Qdrant->collections",
            "page": "/manual/db",
            "anchor": "obj/Qdrant/collections",
            "snippet": ""
        },
        {
            "title": "%Qdrant->create($collection, $size = 1536, $distance = 'Cosine')",
            "page": "/manual/db",
            "anchor": "obj/Qdrant/create",
            "snippet": ""
        },
        {
            "title": "%Qdrant->upsert($collection, $id, $input, ...$payload)",
            "page": "/manual/db",
            "anchor": "obj/Qdrant/upsert",
            "snippet": ""
        },
        {
            "title": "%Qdrant->delete($collection, ...$ids)",
            "page": "/manual/db",
            "anchor": "obj/Qdrant/delete",
            "snippet": ""
        },
        {
            "title": "%Qdrant->search($collection, $input = null, $top = 100)",
            "page": "/manual/db",
            "anchor": "obj/Qdrant/search",
            "snippet": ""
        },
        {
            "title": "%Qdrant->drop($collection)",
            "page": "/manual/db",
            "anchor": "obj/Qdrant/drop",
            "snippet": ""
        },
        {
            "title": "%Qdrant->request($uri, ...$data)",
            "page": "/manual/db",
            "anchor": "obj/Qdrant/request",
            "snippet": ""
        },
        {
            "title": "%query",
            "page": "/manual/db",
            "anchor": "obj/query",
            "snippet": "Fluent query builder for Phlo ORM",
            "tags": "query builder orm database sql"
        },
        {
            "title": "%query->class",
            "page": "/manual/db",
            "anchor": "obj/query/class",
            "snippet": ""
        },
        {
            "title": "%query->conditions",
            "page": "/manual/db",
            "anchor": "obj/query/conditions",
            "snippet": ""
        },
        {
            "title": "%query->bindings",
            "page": "/manual/db",
            "anchor": "obj/query/bindings",
            "snippet": ""
        },
        {
            "title": "%query->orderBy",
            "page": "/manual/db",
            "anchor": "obj/query/orderBy",
            "snippet": ""
        },
        {
            "title": "%query->limitVal",
            "page": "/manual/db",
            "anchor": "obj/query/limitVal",
            "snippet": ""
        },
        {
            "title": "%query->offsetVal",
            "page": "/manual/db",
            "anchor": "obj/query/offsetVal",
            "snippet": ""
        },
        {
            "title": "%query->fq",
            "page": "/manual/db",
            "anchor": "obj/query/fq",
            "snippet": ""
        },
        {
            "title": "%query->q($column)",
            "page": "/manual/db",
            "anchor": "obj/query/q",
            "snippet": ""
        },
        {
            "title": "%query->eq($column, $value)",
            "page": "/manual/db",
            "anchor": "obj/query/eq",
            "snippet": ""
        },
        {
            "title": "%query->neq($column, $value)",
            "page": "/manual/db",
            "anchor": "obj/query/neq",
            "snippet": ""
        },
        {
            "title": "%query->gt($column, $value)",
            "page": "/manual/db",
            "anchor": "obj/query/gt",
            "snippet": ""
        },
        {
            "title": "%query->gte($column, $value)",
            "page": "/manual/db",
            "anchor": "obj/query/gte",
            "snippet": ""
        },
        {
            "title": "%query->lt($column, $value)",
            "page": "/manual/db",
            "anchor": "obj/query/lt",
            "snippet": ""
        },
        {
            "title": "%query->lte($column, $value)",
            "page": "/manual/db",
            "anchor": "obj/query/lte",
            "snippet": ""
        },
        {
            "title": "%query->like($column, $value)",
            "page": "/manual/db",
            "anchor": "obj/query/like",
            "snippet": ""
        },
        {
            "title": "%query->in($column, array $values)",
            "page": "/manual/db",
            "anchor": "obj/query/in",
            "snippet": ""
        },
        {
            "title": "%query->isNull($column)",
            "page": "/manual/db",
            "anchor": "obj/query/isNull",
            "snippet": ""
        },
        {
            "title": "%query->notNull($column)",
            "page": "/manual/db",
            "anchor": "obj/query/notNull",
            "snippet": ""
        },
        {
            "title": "%query->between($column, $min, $max)",
            "page": "/manual/db",
            "anchor": "obj/query/between",
            "snippet": ""
        },
        {
            "title": "%query->raw($sql, ...$bindings)",
            "page": "/manual/db",
            "anchor": "obj/query/raw",
            "snippet": ""
        },
        {
            "title": "%query->where($condition, ...$values)",
            "page": "/manual/db",
            "anchor": "obj/query/where",
            "snippet": ""
        },
        {
            "title": "%query->order($order)",
            "page": "/manual/db",
            "anchor": "obj/query/order",
            "snippet": ""
        },
        {
            "title": "%query->limit($limit)",
            "page": "/manual/db",
            "anchor": "obj/query/limit",
            "snippet": ""
        },
        {
            "title": "%query->offset($offset)",
            "page": "/manual/db",
            "anchor": "obj/query/offset",
            "snippet": ""
        },
        {
            "title": "%query->build",
            "page": "/manual/db",
            "anchor": "obj/query/build",
            "snippet": ""
        },
        {
            "title": "%query->records",
            "page": "/manual/db",
            "anchor": "obj/query/records",
            "snippet": ""
        },
        {
            "title": "%query->record",
            "page": "/manual/db",
            "anchor": "obj/query/record",
            "snippet": ""
        },
        {
            "title": "%query->column",
            "page": "/manual/db",
            "anchor": "obj/query/column",
            "snippet": ""
        },
        {
            "title": "%query->item",
            "page": "/manual/db",
            "anchor": "obj/query/item",
            "snippet": ""
        },
        {
            "title": "%query->count",
            "page": "/manual/db",
            "anchor": "obj/query/count",
            "snippet": ""
        },
        {
            "title": "%query->delete",
            "page": "/manual/db",
            "anchor": "obj/query/delete",
            "snippet": ""
        },
        {
            "title": "%SQLite",
            "page": "/manual/db",
            "anchor": "obj/SQLite",
            "snippet": "SQLite resource",
            "tags": "sqlite pdo database sql"
        },
        {
            "title": "%SQLite->__handle",
            "page": "/manual/db",
            "anchor": "obj/SQLite/__handle",
            "snippet": ""
        },
        {
            "title": "%SQLite->__construct(private string $file)",
            "page": "/manual/db",
            "anchor": "obj/SQLite/__construct",
            "snippet": ""
        },
        {
            "title": "%SQLite->PDO",
            "page": "/manual/db",
            "anchor": "obj/SQLite/PDO",
            "snippet": ""
        },
        {
            "title": "%SQLite->insertIgnore",
            "page": "/manual/db",
            "anchor": "obj/SQLite/insertIgnore",
            "snippet": ""
        },
        {
            "title": "%charts",
            "page": "/manual/dom",
            "anchor": "obj/charts",
            "snippet": "Lightweight dependency-free SVG charts: sparkline, bars and donut. Use charts::spark/bars/donut.",
            "tags": "chart svg sparkline bars donut visualization"
        },
        {
            "title": "%charts->spark($values, $color = '#888', $w = 240, $h = 48, $label = null)",
            "page": "/manual/dom",
            "anchor": "obj/charts/spark",
            "snippet": ""
        },
        {
            "title": "%charts->bars($values, $color = '#888', $w = 240, $h = 48, $label = null)",
            "page": "/manual/dom",
            "anchor": "obj/charts/bars",
            "snippet": ""
        },
        {
            "title": "%charts->donut($parts, $colors = null, $size = 120)",
            "page": "/manual/dom",
            "anchor": "obj/charts/donut",
            "snippet": ""
        },
        {
            "title": "%cookiewall",
            "page": "/manual/dom",
            "anchor": "obj/cookiewall",
            "snippet": "Subtle GDPR cookie-consent banner. English by default; auto-translates when the lang system (en()) is loaded. Override prop labels for a fixed language, or prop translate to force it on/off.",
            "tags": "gdpr consent cookies privacy"
        },
        {
            "title": "%cookiewall->__handle",
            "page": "/manual/dom",
            "anchor": "obj/cookiewall/__handle",
            "snippet": ""
        },
        {
            "title": "%cookiewall->choice",
            "page": "/manual/dom",
            "anchor": "obj/cookiewall/choice",
            "snippet": ""
        },
        {
            "title": "%cookiewall->hasChosen",
            "page": "/manual/dom",
            "anchor": "obj/cookiewall/hasChosen",
            "snippet": ""
        },
        {
            "title": "%cookiewall->canTrack",
            "page": "/manual/dom",
            "anchor": "obj/cookiewall/canTrack",
            "snippet": ""
        },
        {
            "title": "%cookiewall->canAnalytics",
            "page": "/manual/dom",
            "anchor": "obj/cookiewall/canAnalytics",
            "snippet": ""
        },
        {
            "title": "%cookiewall->translate",
            "page": "/manual/dom",
            "anchor": "obj/cookiewall/translate",
            "snippet": ""
        },
        {
            "title": "%cookiewall->labels",
            "page": "/manual/dom",
            "anchor": "obj/cookiewall/labels",
            "snippet": ""
        },
        {
            "title": "%cookiewall->label($key)",
            "page": "/manual/dom",
            "anchor": "obj/cookiewall/label",
            "snippet": ""
        },
        {
            "title": "%cookiewall->AsyncPOSTCookiewallAcceptAll",
            "page": "/manual/dom",
            "anchor": "obj/cookiewall/AsyncPOSTCookiewallAcceptAll",
            "snippet": ""
        },
        {
            "title": "%cookiewall->AsyncPOSTCookiewallAcceptEssential",
            "page": "/manual/dom",
            "anchor": "obj/cookiewall/AsyncPOSTCookiewallAcceptEssential",
            "snippet": ""
        },
        {
            "title": "%cookiewall->banner",
            "page": "/manual/dom",
            "anchor": "obj/cookiewall/banner",
            "snippet": ""
        },
        {
            "title": "%CSS_fixes",
            "page": "/manual/dom",
            "anchor": "obj/CSS_fixes",
            "snippet": "Single Page App basic CSS boilerplate fixes",
            "tags": "css fixes boilerplate reset"
        },
        {
            "title": "%CSS_var",
            "page": "/manual/dom",
            "anchor": "obj/CSS_var",
            "snippet": "CSS variable proxy via app.var",
            "tags": "css variables app.var frontend"
        },
        {
            "title": "%datatags",
            "page": "/manual/dom",
            "anchor": "obj/datatags",
            "snippet": "Single Page App datatag plugin",
            "tags": "dom datatag dataset spa events"
        },
        {
            "title": "%dialog",
            "page": "/manual/dom",
            "anchor": "obj/dialog",
            "snippet": "Single Page App dialog resource",
            "tags": "dom dialog modal confirm prompt alert"
        },
        {
            "title": "%exists",
            "page": "/manual/dom",
            "anchor": "obj/exists",
            "snippet": "onExist helper for dynamic SPA elements",
            "tags": "dom onexist spa lifecycle"
        },
        {
            "title": "%ffmpeg",
            "page": "/manual/dom",
            "anchor": "obj/ffmpeg",
            "snippet": "ffmpeg-wasm for the DOM: encode a canvas timeline to MP4, decode source frames via WebCodecs (seek fallback), transcode/run arbitrary ffmpeg. Exposes the ready singleton `ffmpeg` (and class `Ffmpeg`).",
            "tags": "video ffmpeg wasm canvas encode transcode webcodecs mp4 render"
        },
        {
            "title": "%form",
            "page": "/manual/dom",
            "anchor": "obj/form",
            "snippet": "Single Page App form handler and input state saver",
            "tags": "dom form input state spa"
        },
        {
            "title": "%image_resizer",
            "page": "/manual/dom",
            "anchor": "obj/image_resizer",
            "snippet": "Client-side file upload image resizer",
            "tags": "dom image resize upload canvas"
        },
        {
            "title": "%keyboard",
            "page": "/manual/dom",
            "anchor": "obj/keyboard",
            "snippet": "On-screen keyboard for touch devices, with selectable layout",
            "tags": "dom keyboard onscreen touch input frontend"
        },
        {
            "title": "%link",
            "page": "/manual/dom",
            "anchor": "obj/link",
            "snippet": "Single Page App async link handler",
            "tags": "dom link async navigation spa"
        },
        {
            "title": "%markdown",
            "page": "/manual/dom",
            "anchor": "obj/markdown",
            "snippet": "Client-side markdown parser",
            "tags": "dom markdown parser frontend"
        },
        {
            "title": "%numpad",
            "page": "/manual/dom",
            "anchor": "obj/numpad",
            "snippet": "On-screen numeric keypad for touch input, bound to a field",
            "tags": "dom numpad keypad touch input pos frontend"
        },
        {
            "title": "%presentation",
            "page": "/manual/dom",
            "anchor": "obj/presentation",
            "snippet": "Timeline presentation player for the DOM: timed image/video layers, transitions, subtitles and language alternatives from a presentation.json payload. Exposes the class `PresentationPlayer`; each transition carries its own pp-* CSS animation and the matching canvas curve for deterministic export rendering. boot() wires every .pp-embed, from an inline JSON script child or a data-src payload URL. A document keymap steers the fullscreen, focused or only player: space/k toggles, arrows seek and set volume, m mutes, c toggles subtitles, f fullscreen, home/end jump.",
            "tags": "presentation player timeline audio video subtitles transitions canvas render keyboard"
        },
        {
            "title": "%recorder",
            "page": "/manual/dom",
            "anchor": "obj/recorder",
            "snippet": "Record the screen, a camera/mic or a canvas via MediaRecorder -> Blob. Optionally transcodes to MP4 with the DOM/ffmpeg resource when it is loaded. Exposes the ready singleton `recorder` (and class `Recorder`).",
            "tags": "video recorder mediarecorder screen capture getdisplaymedia webcam canvas"
        },
        {
            "title": "%shorthands",
            "page": "/manual/dom",
            "anchor": "obj/shorthands",
            "snippet": "onChange, onClick and onInput event shorthands",
            "tags": "dom events shorthand frontend"
        },
        {
            "title": "%store",
            "page": "/manual/dom",
            "anchor": "obj/store",
            "snippet": "Stateful binding engine",
            "tags": "dom store binding state signals calc reactive each persist websocket sync"
        },
        {
            "title": "%template",
            "page": "/manual/dom",
            "anchor": "obj/template",
            "snippet": "Single Page App client-side templating",
            "tags": "dom template spa frontend render"
        },
        {
            "title": "%timestamps",
            "page": "/manual/dom",
            "anchor": "obj/timestamps",
            "snippet": "DOM live timestamps",
            "tags": "dom timestamps time live frontend"
        },
        {
            "title": "%toasts",
            "page": "/manual/dom",
            "anchor": "obj/toasts",
            "snippet": "Simple toast resource",
            "tags": "dom toast notification frontend"
        },
        {
            "title": "%visible",
            "page": "/manual/dom",
            "anchor": "obj/visible",
            "snippet": "onVisible and onVisibleIn helpers for DOM visibility",
            "tags": "dom visible intersection observer frontend"
        },
        {
            "title": "%websocket",
            "page": "/manual/dom",
            "anchor": "obj/websocket",
            "snippet": "Client-side WebSocket handler",
            "tags": "websocket realtime frontend dom"
        },
        {
            "title": "%field",
            "page": "/manual/fields",
            "anchor": "obj/field",
            "snippet": "Base ORM field",
            "tags": "field orm"
        },
        {
            "title": "%field->field($type, ...$args)",
            "page": "/manual/fields",
            "anchor": "obj/field/field",
            "snippet": ""
        },
        {
            "title": "%field->__handle",
            "page": "/manual/fields",
            "anchor": "obj/field/__handle",
            "snippet": ""
        },
        {
            "title": "%field->title",
            "page": "/manual/fields",
            "anchor": "obj/field/title",
            "snippet": ""
        },
        {
            "title": "%field->input($record)",
            "page": "/manual/fields",
            "anchor": "obj/field/input",
            "snippet": ""
        },
        {
            "title": "%field->label($record)",
            "page": "/manual/fields",
            "anchor": "obj/field/label",
            "snippet": ""
        },
        {
            "title": "%field->objColumns",
            "page": "/manual/fields",
            "anchor": "obj/field/objColumns",
            "snippet": ""
        },
        {
            "title": "%field->objValidate($value)",
            "page": "/manual/fields",
            "anchor": "obj/field/objValidate",
            "snippet": ""
        },
        {
            "title": "%field_bool",
            "page": "/manual/fields",
            "anchor": "obj/field_bool",
            "snippet": "Boolean field",
            "tags": "field boolean input"
        },
        {
            "title": "%field_bool->true",
            "page": "/manual/fields",
            "anchor": "obj/field_bool/true",
            "snippet": ""
        },
        {
            "title": "%field_bool->false",
            "page": "/manual/fields",
            "anchor": "obj/field_bool/false",
            "snippet": ""
        },
        {
            "title": "%field_bool->label($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_bool/label",
            "snippet": ""
        },
        {
            "title": "%field_bool->input($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_bool/input",
            "snippet": ""
        },
        {
            "title": "%field_bool->parse($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_bool/parse",
            "snippet": ""
        },
        {
            "title": "%field_bool->nullable",
            "page": "/manual/fields",
            "anchor": "obj/field_bool/nullable",
            "snippet": ""
        },
        {
            "title": "%field_bool->objColumns",
            "page": "/manual/fields",
            "anchor": "obj/field_bool/objColumns",
            "snippet": ""
        },
        {
            "title": "%field_child",
            "page": "/manual/fields",
            "anchor": "obj/field_child",
            "snippet": "Child relation field",
            "tags": "field relation child"
        },
        {
            "title": "%field_child->list",
            "page": "/manual/fields",
            "anchor": "obj/field_child/list",
            "snippet": ""
        },
        {
            "title": "%field_child->change",
            "page": "/manual/fields",
            "anchor": "obj/field_child/change",
            "snippet": ""
        },
        {
            "title": "%field_child->create",
            "page": "/manual/fields",
            "anchor": "obj/field_child/create",
            "snippet": ""
        },
        {
            "title": "%field_child->record",
            "page": "/manual/fields",
            "anchor": "obj/field_child/record",
            "snippet": ""
        },
        {
            "title": "%field_child->count($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_child/count",
            "snippet": ""
        },
        {
            "title": "%field_child->last($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_child/last",
            "snippet": ""
        },
        {
            "title": "%field_child->label($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_child/label",
            "snippet": ""
        },
        {
            "title": "%field_child->input($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_child/input",
            "snippet": ""
        },
        {
            "title": "%field_child->link($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_child/link",
            "snippet": ""
        },
        {
            "title": "%field_child->objKey($parentModel)",
            "page": "/manual/fields",
            "anchor": "obj/field_child/objKey",
            "snippet": ""
        },
        {
            "title": "%field_child->objOwns($record, $parentId, $parentModel)",
            "page": "/manual/fields",
            "anchor": "obj/field_child/objOwns",
            "snippet": "Does $record belong to parent $parentId via this relation? (direct foreign key)."
        },
        {
            "title": "%field_date",
            "page": "/manual/fields",
            "anchor": "obj/field_date",
            "snippet": "Date field",
            "tags": "field date"
        },
        {
            "title": "%field_date->handle",
            "page": "/manual/fields",
            "anchor": "obj/field_date/handle",
            "snippet": ""
        },
        {
            "title": "%field_date->format",
            "page": "/manual/fields",
            "anchor": "obj/field_date/format",
            "snippet": ""
        },
        {
            "title": "%field_date->months",
            "page": "/manual/fields",
            "anchor": "obj/field_date/months",
            "snippet": ""
        },
        {
            "title": "%field_date->label($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_date/label",
            "snippet": ""
        },
        {
            "title": "%field_date->objColumns",
            "page": "/manual/fields",
            "anchor": "obj/field_date/objColumns",
            "snippet": ""
        },
        {
            "title": "%field_datetime",
            "page": "/manual/fields",
            "anchor": "obj/field_datetime",
            "snippet": "Date-time field",
            "tags": "field datetime"
        },
        {
            "title": "%field_datetime->handle",
            "page": "/manual/fields",
            "anchor": "obj/field_datetime/handle",
            "snippet": ""
        },
        {
            "title": "%field_datetime->change",
            "page": "/manual/fields",
            "anchor": "obj/field_datetime/change",
            "snippet": ""
        },
        {
            "title": "%field_datetime->create",
            "page": "/manual/fields",
            "anchor": "obj/field_datetime/create",
            "snippet": ""
        },
        {
            "title": "%field_datetime->label($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_datetime/label",
            "snippet": ""
        },
        {
            "title": "%field_datetime->labelIconClass($value)",
            "page": "/manual/fields",
            "anchor": "obj/field_datetime/labelIconClass",
            "snippet": ""
        },
        {
            "title": "%field_datetime->input($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_datetime/input",
            "snippet": ""
        },
        {
            "title": "%field_datetime->parse($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_datetime/parse",
            "snippet": ""
        },
        {
            "title": "%field_datetime->objColumns",
            "page": "/manual/fields",
            "anchor": "obj/field_datetime/objColumns",
            "snippet": ""
        },
        {
            "title": "%field_email",
            "page": "/manual/fields",
            "anchor": "obj/field_email",
            "snippet": "Email field",
            "tags": "field email"
        },
        {
            "title": "%field_email->label($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_email/label",
            "snippet": ""
        },
        {
            "title": "%field_file",
            "page": "/manual/fields",
            "anchor": "obj/field_file",
            "snippet": "File field",
            "tags": "field file upload"
        },
        {
            "title": "%field_file->canDelete",
            "page": "/manual/fields",
            "anchor": "obj/field_file/canDelete",
            "snippet": ""
        },
        {
            "title": "%field_file->delete",
            "page": "/manual/fields",
            "anchor": "obj/field_file/delete",
            "snippet": ""
        },
        {
            "title": "%field_file->path",
            "page": "/manual/fields",
            "anchor": "obj/field_file/path",
            "snippet": ""
        },
        {
            "title": "%field_file->uri",
            "page": "/manual/fields",
            "anchor": "obj/field_file/uri",
            "snippet": ""
        },
        {
            "title": "%field_file->length",
            "page": "/manual/fields",
            "anchor": "obj/field_file/length",
            "snippet": ""
        },
        {
            "title": "%field_file->accept",
            "page": "/manual/fields",
            "anchor": "obj/field_file/accept",
            "snippet": ""
        },
        {
            "title": "%field_file->label($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_file/label",
            "snippet": ""
        },
        {
            "title": "%field_file->input($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_file/input",
            "snippet": ""
        },
        {
            "title": "%field_file->parse($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_file/parse",
            "snippet": ""
        },
        {
            "title": "%field_file->read($record, $path = null)",
            "page": "/manual/fields",
            "anchor": "obj/field_file/read",
            "snippet": ""
        },
        {
            "title": "%field_file->write($file)",
            "page": "/manual/fields",
            "anchor": "obj/field_file/write",
            "snippet": ""
        },
        {
            "title": "%field_file->writePath($file, $path = null)",
            "page": "/manual/fields",
            "anchor": "obj/field_file/writePath",
            "snippet": ""
        },
        {
            "title": "%field_file->objColumns",
            "page": "/manual/fields",
            "anchor": "obj/field_file/objColumns",
            "snippet": ""
        },
        {
            "title": "%field_image",
            "page": "/manual/fields",
            "anchor": "obj/field_image",
            "snippet": "Image field",
            "tags": "field image upload"
        },
        {
            "title": "%field_image->delete",
            "page": "/manual/fields",
            "anchor": "obj/field_image/delete",
            "snippet": ""
        },
        {
            "title": "%field_image->uri",
            "page": "/manual/fields",
            "anchor": "obj/field_image/uri",
            "snippet": ""
        },
        {
            "title": "%field_image->path",
            "page": "/manual/fields",
            "anchor": "obj/field_image/path",
            "snippet": ""
        },
        {
            "title": "%field_image->thumbPath",
            "page": "/manual/fields",
            "anchor": "obj/field_image/thumbPath",
            "snippet": ""
        },
        {
            "title": "%field_image->thumbSize",
            "page": "/manual/fields",
            "anchor": "obj/field_image/thumbSize",
            "snippet": ""
        },
        {
            "title": "%field_image->thumbUri",
            "page": "/manual/fields",
            "anchor": "obj/field_image/thumbUri",
            "snippet": ""
        },
        {
            "title": "%field_image->placeholder",
            "page": "/manual/fields",
            "anchor": "obj/field_image/placeholder",
            "snippet": ""
        },
        {
            "title": "%field_image->record",
            "page": "/manual/fields",
            "anchor": "obj/field_image/record",
            "snippet": ""
        },
        {
            "title": "%field_image->label($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_image/label",
            "snippet": ""
        },
        {
            "title": "%field_image->preview($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_image/preview",
            "snippet": ""
        },
        {
            "title": "%field_image->input($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_image/input",
            "snippet": ""
        },
        {
            "title": "%field_image->write($file)",
            "page": "/manual/fields",
            "anchor": "obj/field_image/write",
            "snippet": ""
        },
        {
            "title": "%field_image->objColumns",
            "page": "/manual/fields",
            "anchor": "obj/field_image/objColumns",
            "snippet": ""
        },
        {
            "title": "%field_many",
            "page": "/manual/fields",
            "anchor": "obj/field_many",
            "snippet": "Many-to-many relation field",
            "tags": "field relation many"
        },
        {
            "title": "%field_many->list",
            "page": "/manual/fields",
            "anchor": "obj/field_many/list",
            "snippet": ""
        },
        {
            "title": "%field_many->record",
            "page": "/manual/fields",
            "anchor": "obj/field_many/record",
            "snippet": ""
        },
        {
            "title": "%field_many->create",
            "page": "/manual/fields",
            "anchor": "obj/field_many/create",
            "snippet": ""
        },
        {
            "title": "%field_many->change",
            "page": "/manual/fields",
            "anchor": "obj/field_many/change",
            "snippet": ""
        },
        {
            "title": "%field_many->count($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_many/count",
            "snippet": ""
        },
        {
            "title": "%field_many->label($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_many/label",
            "snippet": ""
        },
        {
            "title": "%field_many->link($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_many/link",
            "snippet": ""
        },
        {
            "title": "%field_many->input($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_many/input",
            "snippet": ""
        },
        {
            "title": "%field_many->sync($model, $parentId)",
            "page": "/manual/fields",
            "anchor": "obj/field_many/sync",
            "snippet": ""
        },
        {
            "title": "%field_many->objOwns($record, $parentId, $parentModel)",
            "page": "/manual/fields",
            "anchor": "obj/field_many/objOwns",
            "snippet": ""
        },
        {
            "title": "%field_many->objColumns",
            "page": "/manual/fields",
            "anchor": "obj/field_many/objColumns",
            "snippet": ""
        },
        {
            "title": "%field_multiselect",
            "page": "/manual/fields",
            "anchor": "obj/field_multiselect",
            "snippet": "Multi-select via checkboxes; stores the choice as CSV in one hidden field (no save change needed).",
            "tags": "field select multi"
        },
        {
            "title": "%field_multiselect->label($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_multiselect/label",
            "snippet": ""
        },
        {
            "title": "%field_multiselect->input($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_multiselect/input",
            "snippet": ""
        },
        {
            "title": "%field_multiselect->objColumns",
            "page": "/manual/fields",
            "anchor": "obj/field_multiselect/objColumns",
            "snippet": ""
        },
        {
            "title": "%field_number",
            "page": "/manual/fields",
            "anchor": "obj/field_number",
            "snippet": "Number field",
            "tags": "field number"
        },
        {
            "title": "%field_number->decimals",
            "page": "/manual/fields",
            "anchor": "obj/field_number/decimals",
            "snippet": ""
        },
        {
            "title": "%field_number->length",
            "page": "/manual/fields",
            "anchor": "obj/field_number/length",
            "snippet": ""
        },
        {
            "title": "%field_number->min",
            "page": "/manual/fields",
            "anchor": "obj/field_number/min",
            "snippet": ""
        },
        {
            "title": "%field_number->label($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_number/label",
            "snippet": ""
        },
        {
            "title": "%field_number->input($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_number/input",
            "snippet": ""
        },
        {
            "title": "%field_number->parse($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_number/parse",
            "snippet": ""
        },
        {
            "title": "%field_number->objColumns",
            "page": "/manual/fields",
            "anchor": "obj/field_number/objColumns",
            "snippet": ""
        },
        {
            "title": "%field_parent",
            "page": "/manual/fields",
            "anchor": "obj/field_parent",
            "snippet": "Parent relation field",
            "tags": "field relation parent"
        },
        {
            "title": "%field_parent->label($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_parent/label",
            "snippet": ""
        },
        {
            "title": "%field_parent->input($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_parent/input",
            "snippet": ""
        },
        {
            "title": "%field_parent->link($record, $content = null)",
            "page": "/manual/fields",
            "anchor": "obj/field_parent/link",
            "snippet": ""
        },
        {
            "title": "%field_parent->options",
            "page": "/manual/fields",
            "anchor": "obj/field_parent/options",
            "snippet": ""
        },
        {
            "title": "%field_parent->objColumns",
            "page": "/manual/fields",
            "anchor": "obj/field_parent/objColumns",
            "snippet": ""
        },
        {
            "title": "%field_password",
            "page": "/manual/fields",
            "anchor": "obj/field_password",
            "snippet": "Password field",
            "tags": "field password"
        },
        {
            "title": "%field_password->list",
            "page": "/manual/fields",
            "anchor": "obj/field_password/list",
            "snippet": ""
        },
        {
            "title": "%field_password->required",
            "page": "/manual/fields",
            "anchor": "obj/field_password/required",
            "snippet": ""
        },
        {
            "title": "%field_password->minlength",
            "page": "/manual/fields",
            "anchor": "obj/field_password/minlength",
            "snippet": ""
        },
        {
            "title": "%field_password->placeholder",
            "page": "/manual/fields",
            "anchor": "obj/field_password/placeholder",
            "snippet": ""
        },
        {
            "title": "%field_password->input($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_password/input",
            "snippet": ""
        },
        {
            "title": "%field_password->label($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_password/label",
            "snippet": ""
        },
        {
            "title": "%field_password->parse($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_password/parse",
            "snippet": ""
        },
        {
            "title": "%field_password->objColumns",
            "page": "/manual/fields",
            "anchor": "obj/field_password/objColumns",
            "snippet": ""
        },
        {
            "title": "%field_price",
            "page": "/manual/fields",
            "anchor": "obj/field_price",
            "snippet": "Price field",
            "tags": "field price money"
        },
        {
            "title": "%field_price->decimals",
            "page": "/manual/fields",
            "anchor": "obj/field_price/decimals",
            "snippet": ""
        },
        {
            "title": "%field_select",
            "page": "/manual/fields",
            "anchor": "obj/field_select",
            "snippet": "Select field",
            "tags": "field select"
        },
        {
            "title": "%field_select->input($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_select/input",
            "snippet": ""
        },
        {
            "title": "%field_select->objColumns",
            "page": "/manual/fields",
            "anchor": "obj/field_select/objColumns",
            "snippet": ""
        },
        {
            "title": "%field_text",
            "page": "/manual/fields",
            "anchor": "obj/field_text",
            "snippet": "Text field",
            "tags": "field text"
        },
        {
            "title": "%field_text->length",
            "page": "/manual/fields",
            "anchor": "obj/field_text/length",
            "snippet": ""
        },
        {
            "title": "%field_text->multiline",
            "page": "/manual/fields",
            "anchor": "obj/field_text/multiline",
            "snippet": ""
        },
        {
            "title": "%field_text->label($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_text/label",
            "snippet": ""
        },
        {
            "title": "%field_text->input($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_text/input",
            "snippet": ""
        },
        {
            "title": "%field_text->inputField($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_text/inputField",
            "snippet": ""
        },
        {
            "title": "%field_text->inputMulti($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_text/inputMulti",
            "snippet": ""
        },
        {
            "title": "%field_text->objColumns",
            "page": "/manual/fields",
            "anchor": "obj/field_text/objColumns",
            "snippet": ""
        },
        {
            "title": "%field_token",
            "page": "/manual/fields",
            "anchor": "obj/field_token",
            "snippet": "Token field",
            "tags": "field token"
        },
        {
            "title": "%field_token->length",
            "page": "/manual/fields",
            "anchor": "obj/field_token/length",
            "snippet": ""
        },
        {
            "title": "%field_token->default",
            "page": "/manual/fields",
            "anchor": "obj/field_token/default",
            "snippet": ""
        },
        {
            "title": "%field_token->create",
            "page": "/manual/fields",
            "anchor": "obj/field_token/create",
            "snippet": ""
        },
        {
            "title": "%field_token->change",
            "page": "/manual/fields",
            "anchor": "obj/field_token/change",
            "snippet": ""
        },
        {
            "title": "%field_token->search",
            "page": "/manual/fields",
            "anchor": "obj/field_token/search",
            "snippet": ""
        },
        {
            "title": "%field_token->handle",
            "page": "/manual/fields",
            "anchor": "obj/field_token/handle",
            "snippet": ""
        },
        {
            "title": "%field_token->label($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_token/label",
            "snippet": ""
        },
        {
            "title": "%field_token->parse($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_token/parse",
            "snippet": ""
        },
        {
            "title": "%field_token->objColumns",
            "page": "/manual/fields",
            "anchor": "obj/field_token/objColumns",
            "snippet": ""
        },
        {
            "title": "%field_virtual",
            "page": "/manual/fields",
            "anchor": "obj/field_virtual",
            "snippet": "Virtual field",
            "tags": "field virtual"
        },
        {
            "title": "%field_virtual->create",
            "page": "/manual/fields",
            "anchor": "obj/field_virtual/create",
            "snippet": ""
        },
        {
            "title": "%field_virtual->change",
            "page": "/manual/fields",
            "anchor": "obj/field_virtual/change",
            "snippet": ""
        },
        {
            "title": "%field_virtual->objColumns",
            "page": "/manual/fields",
            "anchor": "obj/field_virtual/objColumns",
            "snippet": ""
        },
        {
            "title": "%field_wysiwyg",
            "page": "/manual/fields",
            "anchor": "obj/field_wysiwyg",
            "snippet": "WYSIWYG field",
            "tags": "field wysiwyg editor"
        },
        {
            "title": "%field_wysiwyg->input($record)",
            "page": "/manual/fields",
            "anchor": "obj/field_wysiwyg/input",
            "snippet": ""
        },
        {
            "title": "%field_wysiwyg->objColumns",
            "page": "/manual/fields",
            "anchor": "obj/field_wysiwyg/objColumns",
            "snippet": ""
        },
        {
            "title": "field($type, ...$args):field",
            "page": "/manual/fields",
            "anchor": "function/fields/field",
            "snippet": "Base ORM field",
            "tags": "field orm"
        },
        {
            "title": "%CSV",
            "page": "/manual/files",
            "anchor": "obj/CSV",
            "snippet": "CSV reader resource",
            "tags": "file csv reader import"
        },
        {
            "title": "%CSV->__handle",
            "page": "/manual/files",
            "anchor": "obj/CSV/__handle",
            "snippet": ""
        },
        {
            "title": "%CSV->__construct(string $filename, ?string $path = null)",
            "page": "/manual/files",
            "anchor": "obj/CSV/__construct",
            "snippet": ""
        },
        {
            "title": "%CSV->objFile",
            "page": "/manual/files",
            "anchor": "obj/CSV/objFile",
            "snippet": ""
        },
        {
            "title": "%CSV->objRead",
            "page": "/manual/files",
            "anchor": "obj/CSV/objRead",
            "snippet": ""
        },
        {
            "title": "%DOCX",
            "page": "/manual/files",
            "anchor": "obj/DOCX",
            "snippet": "DOCX reader resource",
            "tags": "file docx word reader"
        },
        {
            "title": "%DOCX->__construct(string $file)",
            "page": "/manual/files",
            "anchor": "obj/DOCX/__construct",
            "snippet": ""
        },
        {
            "title": "%DOCX->toText(string $file)",
            "page": "/manual/files",
            "anchor": "obj/DOCX/toText",
            "snippet": ""
        },
        {
            "title": "%file",
            "page": "/manual/files",
            "anchor": "obj/file",
            "snippet": "File resource",
            "tags": "file filesystem io"
        },
        {
            "title": "%file->__handle",
            "page": "/manual/files",
            "anchor": "obj/file/__handle",
            "snippet": ""
        },
        {
            "title": "%file->__construct(public string $file, ?string $name = null, $contents = null, ...$args)",
            "page": "/manual/files",
            "anchor": "obj/file/__construct",
            "snippet": ""
        },
        {
            "title": "%file->append(string $data)",
            "page": "/manual/files",
            "anchor": "obj/file/append",
            "snippet": ""
        },
        {
            "title": "%file->basename",
            "page": "/manual/files",
            "anchor": "obj/file/basename",
            "snippet": ""
        },
        {
            "title": "%file->base64",
            "page": "/manual/files",
            "anchor": "obj/file/base64",
            "snippet": ""
        },
        {
            "title": "%file->contents",
            "page": "/manual/files",
            "anchor": "obj/file/contents",
            "snippet": ""
        },
        {
            "title": "%file->contentsINI(bool $parse = true)",
            "page": "/manual/files",
            "anchor": "obj/file/contentsINI",
            "snippet": ""
        },
        {
            "title": "%file->contentsJSON($assoc = null)",
            "page": "/manual/files",
            "anchor": "obj/file/contentsJSON",
            "snippet": ""
        },
        {
            "title": "%file->copy($to)",
            "page": "/manual/files",
            "anchor": "obj/file/copy",
            "snippet": ""
        },
        {
            "title": "%file->created",
            "page": "/manual/files",
            "anchor": "obj/file/created",
            "snippet": ""
        },
        {
            "title": "%file->createdAge",
            "page": "/manual/files",
            "anchor": "obj/file/createdAge",
            "snippet": ""
        },
        {
            "title": "%file->createdHuman",
            "page": "/manual/files",
            "anchor": "obj/file/createdHuman",
            "snippet": ""
        },
        {
            "title": "%file->curl($type = null, $filename = null)",
            "page": "/manual/files",
            "anchor": "obj/file/curl",
            "snippet": ""
        },
        {
            "title": "%file->delete",
            "page": "/manual/files",
            "anchor": "obj/file/delete",
            "snippet": ""
        },
        {
            "title": "%file->exists",
            "page": "/manual/files",
            "anchor": "obj/file/exists",
            "snippet": ""
        },
        {
            "title": "%file->ext",
            "page": "/manual/files",
            "anchor": "obj/file/ext",
            "snippet": ""
        },
        {
            "title": "%file->filename",
            "page": "/manual/files",
            "anchor": "obj/file/filename",
            "snippet": ""
        },
        {
            "title": "%file->getLine",
            "page": "/manual/files",
            "anchor": "obj/file/getLine",
            "snippet": ""
        },
        {
            "title": "%file->getLength(int $length)",
            "page": "/manual/files",
            "anchor": "obj/file/getLength",
            "snippet": ""
        },
        {
            "title": "%file->is(string $file)",
            "page": "/manual/files",
            "anchor": "obj/file/is",
            "snippet": ""
        },
        {
            "title": "%file->md5",
            "page": "/manual/files",
            "anchor": "obj/file/md5",
            "snippet": ""
        },
        {
            "title": "%file->mime",
            "page": "/manual/files",
            "anchor": "obj/file/mime",
            "snippet": ""
        },
        {
            "title": "%file->modified",
            "page": "/manual/files",
            "anchor": "obj/file/modified",
            "snippet": ""
        },
        {
            "title": "%file->modifiedAge",
            "page": "/manual/files",
            "anchor": "obj/file/modifiedAge",
            "snippet": ""
        },
        {
            "title": "%file->modifiedHuman",
            "page": "/manual/files",
            "anchor": "obj/file/modifiedHuman",
            "snippet": ""
        },
        {
            "title": "%file->move($to)",
            "page": "/manual/files",
            "anchor": "obj/file/move",
            "snippet": ""
        },
        {
            "title": "%file->name",
            "page": "/manual/files",
            "anchor": "obj/file/name",
            "snippet": ""
        },
        {
            "title": "%file->output($download = false)",
            "page": "/manual/files",
            "anchor": "obj/file/output",
            "snippet": ""
        },
        {
            "title": "%file->path",
            "page": "/manual/files",
            "anchor": "obj/file/path",
            "snippet": ""
        },
        {
            "title": "%file->pathRel",
            "page": "/manual/files",
            "anchor": "obj/file/pathRel",
            "snippet": ""
        },
        {
            "title": "%file->pointer",
            "page": "/manual/files",
            "anchor": "obj/file/pointer",
            "snippet": ""
        },
        {
            "title": "%file->readable",
            "page": "/manual/files",
            "anchor": "obj/file/readable",
            "snippet": ""
        },
        {
            "title": "%file->src",
            "page": "/manual/files",
            "anchor": "obj/file/src",
            "snippet": ""
        },
        {
            "title": "%file->size",
            "page": "/manual/files",
            "anchor": "obj/file/size",
            "snippet": ""
        },
        {
            "title": "%file->sizeHuman(int $precision = 0)",
            "page": "/manual/files",
            "anchor": "obj/file/sizeHuman",
            "snippet": ""
        },
        {
            "title": "%file->sha1",
            "page": "/manual/files",
            "anchor": "obj/file/sha1",
            "snippet": ""
        },
        {
            "title": "%file->shortenTo(int $length)",
            "page": "/manual/files",
            "anchor": "obj/file/shortenTo",
            "snippet": ""
        },
        {
            "title": "%file->title",
            "page": "/manual/files",
            "anchor": "obj/file/title",
            "snippet": ""
        },
        {
            "title": "%file->token($length = 20)",
            "page": "/manual/files",
            "anchor": "obj/file/token",
            "snippet": ""
        },
        {
            "title": "%file->type",
            "page": "/manual/files",
            "anchor": "obj/file/type",
            "snippet": ""
        },
        {
            "title": "%file->touch",
            "page": "/manual/files",
            "anchor": "obj/file/touch",
            "snippet": ""
        },
        {
            "title": "%file->writable",
            "page": "/manual/files",
            "anchor": "obj/file/writable",
            "snippet": ""
        },
        {
            "title": "%file->writeINI($data, bool $deleteEmpty = false)",
            "page": "/manual/files",
            "anchor": "obj/file/writeINI",
            "snippet": ""
        },
        {
            "title": "%file->writeJSON($data, bool $deleteEmpty = false)",
            "page": "/manual/files",
            "anchor": "obj/file/writeJSON",
            "snippet": ""
        },
        {
            "title": "%file->writeJSONplain($data, bool $deleteEmpty = false)",
            "page": "/manual/files",
            "anchor": "obj/file/writeJSONplain",
            "snippet": ""
        },
        {
            "title": "%file->write(string $data, bool $deleteEmpty = false)",
            "page": "/manual/files",
            "anchor": "obj/file/write",
            "snippet": ""
        },
        {
            "title": "%file->objInfo",
            "page": "/manual/files",
            "anchor": "obj/file/objInfo",
            "snippet": ""
        },
        {
            "title": "%img",
            "page": "/manual/files",
            "anchor": "obj/img",
            "snippet": "GD image resource",
            "tags": "image gd file graphics"
        },
        {
            "title": "%img->detect($data)",
            "page": "/manual/files",
            "anchor": "obj/img/detect",
            "snippet": ""
        },
        {
            "title": "%img->__handle",
            "page": "/manual/files",
            "anchor": "obj/img/__handle",
            "snippet": ""
        },
        {
            "title": "%img->__construct(public string $file)",
            "page": "/manual/files",
            "anchor": "obj/img/__construct",
            "snippet": ""
        },
        {
            "title": "%img->src",
            "page": "/manual/files",
            "anchor": "obj/img/src",
            "snippet": ""
        },
        {
            "title": "%img->width",
            "page": "/manual/files",
            "anchor": "obj/img/width",
            "snippet": ""
        },
        {
            "title": "%img->height",
            "page": "/manual/files",
            "anchor": "obj/img/height",
            "snippet": ""
        },
        {
            "title": "%img->scale($width = null, $height = null, $crop = false)",
            "page": "/manual/files",
            "anchor": "obj/img/scale",
            "snippet": ""
        },
        {
            "title": "%img->ext($file = null)",
            "page": "/manual/files",
            "anchor": "obj/img/ext",
            "snippet": ""
        },
        {
            "title": "%img->source($format = null)",
            "page": "/manual/files",
            "anchor": "obj/img/source",
            "snippet": ""
        },
        {
            "title": "%img->save($file = null)",
            "page": "/manual/files",
            "anchor": "obj/img/save",
            "snippet": ""
        },
        {
            "title": "%img->write($format = null, $file = null)",
            "page": "/manual/files",
            "anchor": "obj/img/write",
            "snippet": ""
        },
        {
            "title": "%INI",
            "page": "/manual/files",
            "anchor": "obj/INI",
            "snippet": "Generic INI resource",
            "tags": "file ini config parser"
        },
        {
            "title": "%INI->objFile",
            "page": "/manual/files",
            "anchor": "obj/INI/objFile",
            "snippet": ""
        },
        {
            "title": "%INI->__handle",
            "page": "/manual/files",
            "anchor": "obj/INI/__handle",
            "snippet": ""
        },
        {
            "title": "%INI->__construct(string $filename, ?string $path = null, bool $parse = true)",
            "page": "/manual/files",
            "anchor": "obj/INI/__construct",
            "snippet": ""
        },
        {
            "title": "%INI->objRead($parse = true)",
            "page": "/manual/files",
            "anchor": "obj/INI/objRead",
            "snippet": ""
        },
        {
            "title": "%INI->objWrite",
            "page": "/manual/files",
            "anchor": "obj/INI/objWrite",
            "snippet": ""
        },
        {
            "title": "%INI->__destruct",
            "page": "/manual/files",
            "anchor": "obj/INI/__destruct",
            "snippet": ""
        },
        {
            "title": "%JSON",
            "page": "/manual/files",
            "anchor": "obj/JSON",
            "snippet": "Generic JSON resource",
            "tags": "file json storage parser"
        },
        {
            "title": "%JSON->__handle",
            "page": "/manual/files",
            "anchor": "obj/JSON/__handle",
            "snippet": ""
        },
        {
            "title": "%JSON->__construct(string $filename, ?string $path = null, $assoc = null)",
            "page": "/manual/files",
            "anchor": "obj/JSON/__construct",
            "snippet": ""
        },
        {
            "title": "%JSON->objFile",
            "page": "/manual/files",
            "anchor": "obj/JSON/objFile",
            "snippet": ""
        },
        {
            "title": "%JSON->objTouch",
            "page": "/manual/files",
            "anchor": "obj/JSON/objTouch",
            "snippet": ""
        },
        {
            "title": "%JSON->objRead($assoc = null)",
            "page": "/manual/files",
            "anchor": "obj/JSON/objRead",
            "snippet": ""
        },
        {
            "title": "%JSON->objWrite($data, $flags = null)",
            "page": "/manual/files",
            "anchor": "obj/JSON/objWrite",
            "snippet": ""
        },
        {
            "title": "%JSON->__destruct",
            "page": "/manual/files",
            "anchor": "obj/JSON/__destruct",
            "snippet": ""
        },
        {
            "title": "%PDF",
            "page": "/manual/files",
            "anchor": "obj/PDF",
            "snippet": "PDF generator and reader",
            "tags": "file pdf reader generator"
        },
        {
            "title": "%PDF->toText(string $file)",
            "page": "/manual/files",
            "anchor": "obj/PDF/toText",
            "snippet": ""
        },
        {
            "title": "%PDF->title",
            "page": "/manual/files",
            "anchor": "obj/PDF/title",
            "snippet": ""
        },
        {
            "title": "%PDF->author",
            "page": "/manual/files",
            "anchor": "obj/PDF/author",
            "snippet": ""
        },
        {
            "title": "%PDF->subject",
            "page": "/manual/files",
            "anchor": "obj/PDF/subject",
            "snippet": ""
        },
        {
            "title": "%PDF->keywords",
            "page": "/manual/files",
            "anchor": "obj/PDF/keywords",
            "snippet": ""
        },
        {
            "title": "%PDF->creator",
            "page": "/manual/files",
            "anchor": "obj/PDF/creator",
            "snippet": ""
        },
        {
            "title": "%PDF->filename",
            "page": "/manual/files",
            "anchor": "obj/PDF/filename",
            "snippet": ""
        },
        {
            "title": "%PDF->mode",
            "page": "/manual/files",
            "anchor": "obj/PDF/mode",
            "snippet": ""
        },
        {
            "title": "%PDF->fromHTML($HTML)",
            "page": "/manual/files",
            "anchor": "obj/PDF/fromHTML",
            "snippet": ""
        },
        {
            "title": "%UBL",
            "page": "/manual/files",
            "anchor": "obj/UBL",
            "snippet": "UBL 2.1 invoice XML (PEPPOL BIS Billing 3.0) from a normalized invoice structure. Use UBL::invoice($data).",
            "tags": "ubl peppol invoice xml e-invoicing export"
        },
        {
            "title": "%UBL->invoice(array $data)",
            "page": "/manual/files",
            "anchor": "obj/UBL/invoice",
            "snippet": ""
        },
        {
            "title": "%UBL->xmlLine($idx, $line, $qty, $net, $rate, $currency = 'EUR')",
            "page": "/manual/files",
            "anchor": "obj/UBL/xmlLine",
            "snippet": ""
        },
        {
            "title": "%UBL->partyXml($wrapper, $name, $info, $vatNumber)",
            "page": "/manual/files",
            "anchor": "obj/UBL/partyXml",
            "snippet": ""
        },
        {
            "title": "%UBL->n($v)",
            "page": "/manual/files",
            "anchor": "obj/UBL/n",
            "snippet": ""
        },
        {
            "title": "%UBL->esc($v)",
            "page": "/manual/files",
            "anchor": "obj/UBL/esc",
            "snippet": ""
        },
        {
            "title": "%XLSX",
            "page": "/manual/files",
            "anchor": "obj/XLSX",
            "snippet": "XLSX reader resource",
            "tags": "file xlsx excel reader"
        },
        {
            "title": "%XLSX->__construct(string $file)",
            "page": "/manual/files",
            "anchor": "obj/XLSX/__construct",
            "snippet": "Reads the sheets straight out of the zip with regular expressions instead of an XML parser."
        },
        {
            "title": "%Stripe",
            "page": "/manual/payments",
            "anchor": "obj/Stripe",
            "snippet": "Thin Stripe wrappers: Checkout, Billing Portal, prices, customers, subscriptions and webhook verification. Call Stripe::boot($secret) first. Requires the Stripe PHP SDK (composer: stripe/stripe-php).",
            "tags": "stripe payments checkout subscription billing webhook"
        },
        {
            "title": "%Stripe->boot($secret, $version = '2025-09-30.clover')",
            "page": "/manual/payments",
            "anchor": "obj/Stripe/boot",
            "snippet": ""
        },
        {
            "title": "%Stripe->price($lookupKey)",
            "page": "/manual/payments",
            "anchor": "obj/Stripe/price",
            "snippet": ""
        },
        {
            "title": "%Stripe->customer($id)",
            "page": "/manual/payments",
            "anchor": "obj/Stripe/customer",
            "snippet": ""
        },
        {
            "title": "%Stripe->createCustomer(array $data)",
            "page": "/manual/payments",
            "anchor": "obj/Stripe/createCustomer",
            "snippet": ""
        },
        {
            "title": "%Stripe->checkout(array $params)",
            "page": "/manual/payments",
            "anchor": "obj/Stripe/checkout",
            "snippet": ""
        },
        {
            "title": "%Stripe->portal($customerId, $returnUrl)",
            "page": "/manual/payments",
            "anchor": "obj/Stripe/portal",
            "snippet": ""
        },
        {
            "title": "%Stripe->verifyWebhook($payload, $sigHeader, $secret)",
            "page": "/manual/payments",
            "anchor": "obj/Stripe/verifyWebhook",
            "snippet": ""
        },
        {
            "title": "%Stripe->subscriptions($customerId, $status = 'all', $limit = 10)",
            "page": "/manual/payments",
            "anchor": "obj/Stripe/subscriptions",
            "snippet": ""
        },
        {
            "title": "%Stripe->subscription($id)",
            "page": "/manual/payments",
            "anchor": "obj/Stripe/subscription",
            "snippet": ""
        },
        {
            "title": "%Stripe->product($id)",
            "page": "/manual/payments",
            "anchor": "obj/Stripe/product",
            "snippet": ""
        },
        {
            "title": "%SumUp",
            "page": "/manual/payments",
            "anchor": "obj/SumUp",
            "snippet": "SumUp connector: card-present checkouts on paired Solo readers via the Cloud API, transaction lookup and history",
            "tags": "sumup payments terminal reader card-present checkout connector"
        },
        {
            "title": "%SumUp->section",
            "page": "/manual/payments",
            "anchor": "obj/SumUp/section",
            "snippet": ""
        },
        {
            "title": "%SumUp->api",
            "page": "/manual/payments",
            "anchor": "obj/SumUp/api",
            "snippet": ""
        },
        {
            "title": "%SumUp->headers",
            "page": "/manual/payments",
            "anchor": "obj/SumUp/headers",
            "snippet": ""
        },
        {
            "title": "%SumUp->fields",
            "page": "/manual/payments",
            "anchor": "obj/SumUp/fields",
            "snippet": ""
        },
        {
            "title": "%SumUp->merchant",
            "page": "/manual/payments",
            "anchor": "obj/SumUp/merchant",
            "snippet": ""
        },
        {
            "title": "%SumUp->readers",
            "page": "/manual/payments",
            "anchor": "obj/SumUp/readers",
            "snippet": "The merchant's paired readers."
        },
        {
            "title": "%SumUp->reader(?string $readerId = null)",
            "page": "/manual/payments",
            "anchor": "obj/SumUp/reader",
            "snippet": ""
        },
        {
            "title": "%SumUp->createReaderCheckout(int $amountMinor, string $currency = 'EUR', ?string $readerId = null, ?string $description = null, ?string $returnUrl = null)",
            "page": "/manual/payments",
            "anchor": "obj/SumUp/createReaderCheckout",
            "snippet": "Start a card-present checkout on a reader. The amount is in minor units (cents)."
        },
        {
            "title": "%SumUp->terminateReaderCheckout(?string $readerId = null)",
            "page": "/manual/payments",
            "anchor": "obj/SumUp/terminateReaderCheckout",
            "snippet": "Stops the active checkout on a reader."
        },
        {
            "title": "%SumUp->transaction(string $clientTransactionId)",
            "page": "/manual/payments",
            "anchor": "obj/SumUp/transaction",
            "snippet": "Looks up a transaction by the client transaction id a checkout returned."
        },
        {
            "title": "%SumUp->transactions(array $query = [])",
            "page": "/manual/payments",
            "anchor": "obj/SumUp/transactions",
            "snippet": ""
        },
        {
            "title": "%audit",
            "page": "/manual/security",
            "anchor": "obj/audit",
            "snippet": "Audit log for model mutations (opt-in via static idColumn/objAudit). Schema: resources/security/audit.sql",
            "tags": "audit log compliance traceability"
        },
        {
            "title": "%audit->log($model, $action, $before = [], $after = [], $exclude = [])",
            "page": "/manual/security",
            "anchor": "obj/audit/log",
            "snippet": ""
        },
        {
            "title": "%audit->diff($before, $after)",
            "page": "/manual/security",
            "anchor": "obj/audit/diff",
            "snippet": ""
        },
        {
            "title": "%audit->history($model, $recordId, $limit = 50)",
            "page": "/manual/security",
            "anchor": "obj/audit/history",
            "snippet": ""
        },
        {
            "title": "%audit->byUser($model, $userId, $fromTs = 0, $limit = 100)",
            "page": "/manual/security",
            "anchor": "obj/audit/byUser",
            "snippet": ""
        },
        {
            "title": "%audit->purge($model, $olderThanSeconds = 31536000)",
            "page": "/manual/security",
            "anchor": "obj/audit/purge",
            "snippet": ""
        },
        {
            "title": "%captcha",
            "page": "/manual/security",
            "anchor": "obj/captcha",
            "snippet": "Self-contained interactive slider-puzzle captcha (no external service). The server picks a secret gap position and renders the background plus a loose piece with GD; the client drags the piece into place. verify() checks the end position plus human drag behaviour (time, path, variation). Single-use and session-bound; the gap position never leaves the server.",
            "tags": "captcha spam bot human-verification security"
        },
        {
            "title": "%captcha->W",
            "page": "/manual/security",
            "anchor": "obj/captcha/W",
            "snippet": ""
        },
        {
            "title": "%captcha->H",
            "page": "/manual/security",
            "anchor": "obj/captcha/H",
            "snippet": ""
        },
        {
            "title": "%captcha->P",
            "page": "/manual/security",
            "anchor": "obj/captcha/P",
            "snippet": ""
        },
        {
            "title": "%captcha->tol",
            "page": "/manual/security",
            "anchor": "obj/captcha/tol",
            "snippet": ""
        },
        {
            "title": "%captcha->ttl",
            "page": "/manual/security",
            "anchor": "obj/captcha/ttl",
            "snippet": ""
        },
        {
            "title": "%captcha->issue",
            "page": "/manual/security",
            "anchor": "obj/captcha/issue",
            "snippet": ""
        },
        {
            "title": "%captcha->images($gapX, $gapY)",
            "page": "/manual/security",
            "anchor": "obj/captcha/images",
            "snippet": ""
        },
        {
            "title": "%captcha->verify($x, $telemetry)",
            "page": "/manual/security",
            "anchor": "obj/captcha/verify",
            "snippet": "Judges the drag as much as where it ended."
        },
        {
            "title": "%captcha->consume",
            "page": "/manual/security",
            "anchor": "obj/captcha/consume",
            "snippet": ""
        },
        {
            "title": "%captcha->widget",
            "page": "/manual/security",
            "anchor": "obj/captcha/widget",
            "snippet": ""
        },
        {
            "title": "%captcha->field($bg, $piece, $gapY, $w, $h, $p)",
            "page": "/manual/security",
            "anchor": "obj/captcha/field",
            "snippet": ""
        },
        {
            "title": "%creds",
            "page": "/manual/security",
            "anchor": "obj/creds",
            "snippet": "Credentials resolver from env and ini sources",
            "tags": "credentials env ini secrets configuration"
        },
        {
            "title": "%creds->__construct(?array $values = null)",
            "page": "/manual/security",
            "anchor": "obj/creds/__construct",
            "snippet": ""
        },
        {
            "title": "%creds->resolve",
            "page": "/manual/security",
            "anchor": "obj/creds/resolve",
            "snippet": ""
        },
        {
            "title": "%creds->loadINI(string $file)",
            "page": "/manual/security",
            "anchor": "obj/creds/loadINI",
            "snippet": ""
        },
        {
            "title": "%creds->envValues(bool $hostScoped = false)",
            "page": "/manual/security",
            "anchor": "obj/creds/envValues",
            "snippet": ""
        },
        {
            "title": "%creds->hostKey",
            "page": "/manual/security",
            "anchor": "obj/creds/hostKey",
            "snippet": ""
        },
        {
            "title": "%creds->envAssign(array &$target, array $parts, string $value)",
            "page": "/manual/security",
            "anchor": "obj/creds/envAssign",
            "snippet": ""
        },
        {
            "title": "%creds->merge(array &$base, array $add)",
            "page": "/manual/security",
            "anchor": "obj/creds/merge",
            "snippet": ""
        },
        {
            "title": "%creds->objGet($key)",
            "page": "/manual/security",
            "anchor": "obj/creds/objGet",
            "snippet": ""
        },
        {
            "title": "%creds->objInfo",
            "page": "/manual/security",
            "anchor": "obj/creds/objInfo",
            "snippet": ""
        },
        {
            "title": "%CSRF",
            "page": "/manual/security",
            "anchor": "obj/CSRF",
            "snippet": "Rotating async CSRF protection for Phlo requests",
            "tags": "csrf security async forms"
        },
        {
            "title": "%CSRF->view",
            "page": "/manual/security",
            "anchor": "obj/CSRF/view",
            "snippet": ""
        },
        {
            "title": "%CSRF->token",
            "page": "/manual/security",
            "anchor": "obj/CSRF/token",
            "snippet": ""
        },
        {
            "title": "%CSRF->verify",
            "page": "/manual/security",
            "anchor": "obj/CSRF/verify",
            "snippet": ""
        },
        {
            "title": "%CSRF->update",
            "page": "/manual/security",
            "anchor": "obj/CSRF/update",
            "snippet": ""
        },
        {
            "title": "%JWT",
            "page": "/manual/security",
            "anchor": "obj/JWT",
            "snippet": "Sign and verify compact HS256 JSON Web Tokens (RFC 7519), secure by default",
            "tags": "jwt jws hs256 token auth security"
        },
        {
            "title": "%JWT->__construct(public string $secret, public string $issuer = void, public int $leeway = 30)",
            "page": "/manual/security",
            "anchor": "obj/JWT/__construct",
            "snippet": ""
        },
        {
            "title": "%JWT->sign(array $claims, int $ttl = 3600)",
            "page": "/manual/security",
            "anchor": "obj/JWT/sign",
            "snippet": ""
        },
        {
            "title": "%JWT->verify(string $token)",
            "page": "/manual/security",
            "anchor": "obj/JWT/verify",
            "snippet": ""
        },
        {
            "title": "%JWT->sig(string $body)",
            "page": "/manual/security",
            "anchor": "obj/JWT/sig",
            "snippet": ""
        },
        {
            "title": "%JWT->encode($data)",
            "page": "/manual/security",
            "anchor": "obj/JWT/encode",
            "snippet": ""
        },
        {
            "title": "%JWT->decode(string $data)",
            "page": "/manual/security",
            "anchor": "obj/JWT/decode",
            "snippet": ""
        },
        {
            "title": "%OAuth2",
            "page": "/manual/security",
            "anchor": "obj/OAuth2",
            "snippet": "Stateless OAuth2 client: build the authorize URL and exchange/refresh tokens. Token storage and config are the caller's responsibility. The protocol primitive under TokenStore and OAuthConnector.",
            "tags": "oauth oauth2 token authorization refresh authentication"
        },
        {
            "title": "%OAuth2->authorizeUrl($endpoint, array $params)",
            "page": "/manual/security",
            "anchor": "obj/OAuth2/authorizeUrl",
            "snippet": ""
        },
        {
            "title": "%OAuth2->token($tokenUrl, $clientId, $clientSecret, $grantType, array $extra = [])",
            "page": "/manual/security",
            "anchor": "obj/OAuth2/token",
            "snippet": ""
        },
        {
            "title": "%OAuth2->exchangeCode($tokenUrl, $clientId, $clientSecret, $code, $redirectUri = null, array $extra = [])",
            "page": "/manual/security",
            "anchor": "obj/OAuth2/exchangeCode",
            "snippet": ""
        },
        {
            "title": "%OAuth2->refresh($tokenUrl, $clientId, $clientSecret, $refreshToken, array $extra = [])",
            "page": "/manual/security",
            "anchor": "obj/OAuth2/refresh",
            "snippet": ""
        },
        {
            "title": "%rate",
            "page": "/manual/security",
            "anchor": "obj/rate",
            "snippet": "Rate-limit (fixed window) on the rate_limit table. Schema: resources/security/rate.sql",
            "tags": "rate limit throttle abuse"
        },
        {
            "title": "%rate->check($key, $limit, $windowSeconds, $storage = 'db')",
            "page": "/manual/security",
            "anchor": "obj/rate/check",
            "snippet": ""
        },
        {
            "title": "%rate->checkApcu($key, $limit, $windowSeconds)",
            "page": "/manual/security",
            "anchor": "obj/rate/checkApcu",
            "snippet": ""
        },
        {
            "title": "%rate->status($key, $limit, $windowSeconds)",
            "page": "/manual/security",
            "anchor": "obj/rate/status",
            "snippet": ""
        },
        {
            "title": "%rate->reset($key)",
            "page": "/manual/security",
            "anchor": "obj/rate/reset",
            "snippet": ""
        },
        {
            "title": "%rate->purge($olderThanSeconds = 604800)",
            "page": "/manual/security",
            "anchor": "obj/rate/purge",
            "snippet": ""
        },
        {
            "title": "%security",
            "page": "/manual/security",
            "anchor": "obj/security",
            "snippet": "Generic security resource",
            "tags": "security csp nonce headers"
        },
        {
            "title": "%security->whitelist",
            "page": "/manual/security",
            "anchor": "obj/security/whitelist",
            "snippet": ""
        },
        {
            "title": "%security->sources",
            "page": "/manual/security",
            "anchor": "obj/security/sources",
            "snippet": ""
        },
        {
            "title": "%security->setNonce",
            "page": "/manual/security",
            "anchor": "obj/security/setNonce",
            "snippet": ""
        },
        {
            "title": "%security->frameProtect($mode = 'DENY')",
            "page": "/manual/security",
            "anchor": "obj/security/frameProtect",
            "snippet": ""
        },
        {
            "title": "%security->frameWhitelist",
            "page": "/manual/security",
            "anchor": "obj/security/frameWhitelist",
            "snippet": ""
        },
        {
            "title": "%security->sourceList",
            "page": "/manual/security",
            "anchor": "obj/security/sourceList",
            "snippet": "Extra origins the app loads content from (images, media, fetch), e.g. a CDN; full origins, applied verbatim."
        },
        {
            "title": "%security->strict",
            "page": "/manual/security",
            "anchor": "obj/security/strict",
            "snippet": ""
        },
        {
            "title": "%security->basic",
            "page": "/manual/security",
            "anchor": "obj/security/basic",
            "snippet": "Under debug, basic/marketing relax script-src to 'unsafe-inline' so the inline debug console runs."
        },
        {
            "title": "%security->marketing",
            "page": "/manual/security",
            "anchor": "obj/security/marketing",
            "snippet": ""
        },
        {
            "title": "%security->api",
            "page": "/manual/security",
            "anchor": "obj/security/api",
            "snippet": ""
        },
        {
            "title": "%security->base",
            "page": "/manual/security",
            "anchor": "obj/security/base",
            "snippet": ""
        },
        {
            "title": "%social",
            "page": "/manual/security",
            "anchor": "obj/social",
            "snippet": "Reusable social login (OIDC) on top of OAuth2: build the authorize URL and turn a callback code into a verified profile. Google, Microsoft and Apple. No user, session or route handling - that is the caller's responsibility.",
            "tags": "oauth oidc social login google microsoft apple authentication"
        },
        {
            "title": "%social->providers",
            "page": "/manual/security",
            "anchor": "obj/social/providers",
            "snippet": ""
        },
        {
            "title": "%social->config($provider)",
            "page": "/manual/security",
            "anchor": "obj/social/config",
            "snippet": ""
        },
        {
            "title": "%social->configured($provider)",
            "page": "/manual/security",
            "anchor": "obj/social/configured",
            "snippet": ""
        },
        {
            "title": "%social->authUrl($provider, $state, $nonce = void)",
            "page": "/manual/security",
            "anchor": "obj/social/authUrl",
            "snippet": ""
        },
        {
            "title": "%social->profile($provider, $code, $nonce = void)",
            "page": "/manual/security",
            "anchor": "obj/social/profile",
            "snippet": ""
        },
        {
            "title": "%social->decodeIdToken($jwt)",
            "page": "/manual/security",
            "anchor": "obj/social/decodeIdToken",
            "snippet": ""
        },
        {
            "title": "%social->algs",
            "page": "/manual/security",
            "anchor": "obj/social/algs",
            "snippet": ""
        },
        {
            "title": "%social->verifySignature($provider, $jwt)",
            "page": "/manual/security",
            "anchor": "obj/social/verifySignature",
            "snippet": ""
        },
        {
            "title": "%social->jwks($provider)",
            "page": "/manual/security",
            "anchor": "obj/social/jwks",
            "snippet": ""
        },
        {
            "title": "%social->key($provider, $kid)",
            "page": "/manual/security",
            "anchor": "obj/social/key",
            "snippet": ""
        },
        {
            "title": "%social->jwkToPem(array $jwk)",
            "page": "/manual/security",
            "anchor": "obj/social/jwkToPem",
            "snippet": ""
        },
        {
            "title": "%social->der($tag, $content)",
            "page": "/manual/security",
            "anchor": "obj/social/der",
            "snippet": ""
        },
        {
            "title": "%social->derInt($bytes)",
            "page": "/manual/security",
            "anchor": "obj/social/derInt",
            "snippet": ""
        },
        {
            "title": "%social->b64urlDecode($data)",
            "page": "/manual/security",
            "anchor": "obj/social/b64urlDecode",
            "snippet": ""
        },
        {
            "title": "%social->verifyClaims($provider, array $cfg, array $claims, $nonce = void)",
            "page": "/manual/security",
            "anchor": "obj/social/verifyClaims",
            "snippet": ""
        },
        {
            "title": "%social->verifyIssuer($provider, array $claims)",
            "page": "/manual/security",
            "anchor": "obj/social/verifyIssuer",
            "snippet": ""
        },
        {
            "title": "%social->normalize($provider, array $claims)",
            "page": "/manual/security",
            "anchor": "obj/social/normalize",
            "snippet": ""
        },
        {
            "title": "%social->b64url($data)",
            "page": "/manual/security",
            "anchor": "obj/social/b64url",
            "snippet": ""
        },
        {
            "title": "%social->appleSecret",
            "page": "/manual/security",
            "anchor": "obj/social/appleSecret",
            "snippet": ""
        },
        {
            "title": "%social->derToJose($der)",
            "page": "/manual/security",
            "anchor": "obj/social/derToJose",
            "snippet": ""
        },
        {
            "title": "%social->pad32($x)",
            "page": "/manual/security",
            "anchor": "obj/social/pad32",
            "snippet": ""
        },
        {
            "title": "decrypt($encrypted, $key):string|false",
            "page": "/manual/security",
            "anchor": "function/security/decrypt",
            "snippet": "Encrypt and decrypt secretbox payloads using a key",
            "tags": "encrypt decrypt encryption sodium secretbox crypto"
        },
        {
            "title": "encrypt($data, $key):string",
            "page": "/manual/security",
            "anchor": "function/security/encrypt",
            "snippet": "Encrypt and decrypt secretbox payloads using a key",
            "tags": "encrypt decrypt encryption sodium secretbox crypto"
        },
        {
            "title": "token(int $length = 8, ?string $input = null):string",
            "page": "/manual/security",
            "anchor": "function/security/token",
            "snippet": "Generate deterministic or random lowercase token",
            "tags": "token random deterministic security"
        }
    ]
}