[{"data":1,"prerenderedAt":8545},["ShallowReactive",2],{"search-sections-edamame":3,"nav-edamame":888,"content-tree-edamame":931,"footer-resources":950,"content-/v1.0.3/cookbook/llm-integration":3703,"surround-/v1.0.3/cookbook/llm-integration":8542},[4,10,14,20,25,30,35,41,46,51,56,61,64,69,74,79,84,89,94,99,104,108,113,118,123,127,132,137,142,147,152,157,162,167,172,177,182,187,192,197,202,207,212,217,221,225,230,234,239,244,249,254,259,264,269,273,277,281,286,291,296,301,306,311,316,321,326,331,336,341,346,351,356,361,366,371,376,381,386,391,396,401,406,411,416,421,426,431,436,441,444,449,454,459,463,467,472,477,482,487,492,497,502,507,512,517,522,526,531,535,540,545,550,555,560,565,570,575,580,585,590,594,598,603,607,612,617,622,627,632,636,641,646,651,656,661,665,669,675,680,685,690,695,700,705,709,714,719,724,729,734,739,744,748,753,758,763,768,773,778,783,787,792,796,800,805,809,813,817,821,825,829,834,839,844,849,854,859,864,869,874,879,884],{"id":5,"title":6,"titles":7,"content":8,"level":9},"/v1.0.3/overview","Overview",[],"Statement-driven query exec for Go applications",1,{"id":11,"title":6,"titles":12,"content":13,"level":9},"/v1.0.3/overview#overview",[],"Database operations in Go often mean choosing between raw SQL and heavy ORMs. Edamame offers a third path: a statement-driven query exec that stays out of your way while providing type-safe, declarative query definitions. // Define your model\ntype User struct {\n    ID    int    `db:\"id\" type:\"integer\" constraints:\"primarykey\"`\n    Email string `db:\"email\" type:\"text\" constraints:\"notnull,unique\"`\n    Name  string `db:\"name\" type:\"text\"`\n    Age   *int   `db:\"age\" type:\"integer\"`\n}\n\n// Define statements as package-level variables\nvar (\n    QueryAll = edamame.NewQueryStatement(\"query-all\", \"Query all users\", edamame.QuerySpec{})\n\n    SelectByID = edamame.NewSelectStatement(\"select-by-id\", \"Select user by ID\", edamame.SelectSpec{\n        Where: []edamame.ConditionSpec{{Field: \"id\", Operator: \"=\", Param: \"id\"}},\n    })\n\n    DeleteByID = edamame.NewDeleteStatement(\"delete-by-id\", \"Delete user by ID\", edamame.DeleteSpec{\n        Where: []edamame.ConditionSpec{{Field: \"id\", Operator: \"=\", Param: \"id\"}},\n    })\n\n    Adults = edamame.NewQueryStatement(\"adults\", \"Find users over a minimum age\", edamame.QuerySpec{\n        Where: []edamame.ConditionSpec{\n            {Field: \"age\", Operator: \">=\", Param: \"min_age\"},\n        },\n        OrderBy: []edamame.OrderBySpec{\n            {Field: \"name\", Direction: \"asc\"},\n        },\n    })\n)\n\n// Create a exec and execute\nexec, err := edamame.New[User](db, \"users\", renderer)\n\nusers, err := exec.ExecQuery(ctx, QueryAll, nil)\nuser, err := exec.ExecSelect(ctx, SelectByID, map[string]any{\"id\": 123})\ninserted, err := exec.ExecInsert(ctx, &user)\ndeleted, err := exec.ExecDelete(ctx, DeleteByID, map[string]any{\"id\": 123})\nadults, err := exec.ExecQuery(ctx, Adults, map[string]any{\"min_age\": 18}) Type-safe, injection-protected, declarative.",{"id":15,"title":16,"titles":17,"content":18,"level":19},"/v1.0.3/overview#architecture","Architecture",[6],"┌─────────────────────────────────────────────────────────────┐\n│                         Edamame                             │\n│                                                             │\n│  ┌─────────────────────────────────────────────────────┐    │\n│  │                   Executor[T]                        │    │\n│  │                                                     │    │\n│  │  ┌───────────────────────────────────────────────┐  │    │\n│  │  │              Statement Types                   │  │    │\n│  │  │                                                │  │    │\n│  │  │  QueryStatement  SelectStatement               │  │    │\n│  │  │  UpdateStatement DeleteStatement               │  │    │\n│  │  │  AggregateStatement                            │  │    │\n│  │  └────────────────────┬───────────────────────────┘  │    │\n│  │                       │                              │    │\n│  │                 ┌─────▼─────┐                        │    │\n│  │                 │    Soy    │                        │    │\n│  │                 │ (Builder) │                        │    │\n│  │                 └─────┬─────┘                        │    │\n│  │                       │                              │    │\n│  └───────────────────────┼──────────────────────────────┘    │\n│                          │                                   │\n│                    ┌─────▼─────┐                             │\n│                    │   sqlx    │                             │\n│                    │    DB     │                             │\n│                    └───────────┘                             │\n└─────────────────────────────────────────────────────────────┘ Edamame provides typed statements over soy's query builder. Each statement type encapsulates a declarative spec that maps to a parameterized SQL builder.",2,{"id":21,"title":22,"titles":23,"content":24,"level":19},"/v1.0.3/overview#philosophy","Philosophy",[6],"Edamame bridges two worlds: the declarative simplicity of specs and the type safety of Go generics. Define what you want, get SQL that's validated at build time and parameterized at runtime. // Define statements as package-level variables\nvar ActiveByRole = edamame.NewQueryStatement(\"active-by-role\", \"Find active users by role\", edamame.QuerySpec{\n    Where: []edamame.ConditionSpec{\n        {Field: \"active\", Operator: \"=\", Param: \"active\"},\n        {Field: \"role\", Operator: \"=\", Param: \"role\"},\n    },\n})\n\n// In your API handler\nusers, err := exec.ExecQuery(ctx, ActiveByRole, map[string]any{\n    \"active\": true,\n    \"role\":   \"admin\",\n}) Typed statements, compile-time safety, no magic strings.",{"id":26,"title":27,"titles":28,"content":29,"level":19},"/v1.0.3/overview#statement-types","Statement Types",[6],"Edamame provides five statement types for different operations: Statement TypeDescriptionQueryStatementMulti-record retrieval with filteringSelectStatementSingle-record retrievalUpdateStatementTargeted updates with SET/WHEREDeleteStatementConditional deletion with WHEREAggregateStatementCOUNT, SUM, AVG, MIN, MAX operations Each statement type accepts a spec that defines the query behavior: QuerySpec - Filtering, sorting, pagination, grouping, locking SelectSpec - Single-record filtering with optional locking UpdateSpec - SET clauses and WHERE conditions DeleteSpec - WHERE conditions for targeted deletion AggregateSpec - Field to aggregate and optional filtering",{"id":31,"title":32,"titles":33,"content":34,"level":19},"/v1.0.3/overview#priorities","Priorities",[6],"",{"id":36,"title":37,"titles":38,"content":39,"level":40},"/v1.0.3/overview#type-safety","Type Safety",[6,32],"Fields, operators, and params are validated at query build time. No runtime SQL injection, no magic strings. // Spec-based: field names validated against model metadata\nvar Adults = edamame.NewQueryStatement(\"adults\", \"Find adults\", edamame.QuerySpec{\n    Where: []edamame.ConditionSpec{\n        {Field: \"age\", Operator: \">=\", Param: \"min_age\"},\n    },\n})\n\n// Execution: params bound safely via sqlx\nusers, err := exec.ExecQuery(ctx, Adults, map[string]any{\n    \"min_age\": 18,  // Parameterized, never interpolated\n})",3,{"id":42,"title":43,"titles":44,"content":45,"level":40},"/v1.0.3/overview#compile-time-guarantees","Compile-Time Guarantees",[6,32],"Statements are typed. Pass a QueryStatement to ExecQuery, a SelectStatement to ExecSelect. The compiler catches mismatches. // Compiler ensures correct statement types\nusers, err := exec.ExecQuery(ctx, QueryAll, nil)       // QueryStatement\nuser, err := exec.ExecSelect(ctx, SelectByID, params)  // SelectStatement\ncount, err := exec.ExecAggregate(ctx, CountAll, nil)   // AggregateStatement",{"id":47,"title":48,"titles":49,"content":50,"level":40},"/v1.0.3/overview#security","Security",[6,32],"All SQL generation flows through soy's validated builder: Field names validated against model metadataOperators validated against allowlistAll values bound as parameters, never interpolatedNo raw SQL construction from user input",{"id":52,"title":53,"titles":54,"content":55,"level":40},"/v1.0.3/overview#performance","Performance",[6,32],"Lazy initialization - Builders created on demandMinimal allocations - Spec-to-builder conversion is lightweightBatch operations - Insert, update, delete batches in single transactions html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"id":57,"title":58,"titles":59,"content":60,"level":9},"/v1.0.3/learn/quickstart","Quickstart",[],"Get started with edamame in minutes",{"id":62,"title":58,"titles":63,"content":34,"level":9},"/v1.0.3/learn/quickstart#quickstart",[],{"id":65,"title":66,"titles":67,"content":68,"level":19},"/v1.0.3/learn/quickstart#requirements","Requirements",[58],"Go 1.24 or later.",{"id":70,"title":71,"titles":72,"content":73,"level":19},"/v1.0.3/learn/quickstart#installation","Installation",[58],"go get github.com/zoobz-io/edamame",{"id":75,"title":76,"titles":77,"content":78,"level":19},"/v1.0.3/learn/quickstart#basic-usage","Basic Usage",[58],"package main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"log\"\n\n    \"github.com/jmoiron/sqlx\"\n    _ \"github.com/lib/pq\" // or mariadb, sqlite3, mssql driver\n    \"github.com/zoobz-io/astql/pkg/postgres\" // or mariadb, sqlite, mssql\n    \"github.com/zoobz-io/edamame\"\n)\n\n// Define your model with struct tags\ntype User struct {\n    ID    int    `db:\"id\" type:\"integer\" constraints:\"primarykey\"`\n    Email string `db:\"email\" type:\"text\" constraints:\"notnull,unique\"`\n    Name  string `db:\"name\" type:\"text\"`\n    Age   *int   `db:\"age\" type:\"integer\"`\n}\n\n// Define statements as package-level variables\nvar (\n    QueryAll = edamame.NewQueryStatement(\"query-all\", \"Query all users\", edamame.QuerySpec{})\n\n    SelectByID = edamame.NewSelectStatement(\"select-by-id\", \"Select user by ID\", edamame.SelectSpec{\n        Where: []edamame.ConditionSpec{{Field: \"id\", Operator: \"=\", Param: \"id\"}},\n    })\n\n    CountAll = edamame.NewAggregateStatement(\"count-all\", \"Count all users\", edamame.AggCount, edamame.AggregateSpec{})\n)\n\nfunc main() {\n    // Connect to database (PostgreSQL shown; MariaDB, SQLite, SQL Server also supported)\n    db, err := sqlx.Connect(\"postgres\", \"postgres://user:pass@localhost/mydb?sslmode=disable\")\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    // Create exec\n    exec, err := edamame.New[User](db, \"users\", postgres.New())\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    ctx := context.Background()\n\n    // Query all users\n    users, err := exec.ExecQuery(ctx, QueryAll, nil)\n    if err != nil {\n        log.Fatal(err)\n    }\n    fmt.Printf(\"Found %d users\\n\", len(users))\n\n    // Select user by ID\n    user, err := exec.ExecSelect(ctx, SelectByID, map[string]any{\"id\": 1})\n    if err != nil {\n        log.Fatal(err)\n    }\n    fmt.Printf(\"User: %s\\n\", user.Name)\n\n    // Insert a new user\n    age := 25\n    newUser := &User{\n        Email: \"alice@example.com\",\n        Name:  \"Alice\",\n        Age:   &age,\n    }\n    inserted, err := exec.ExecInsert(ctx, newUser)\n    if err != nil {\n        log.Fatal(err)\n    }\n    fmt.Printf(\"Inserted user ID: %d\\n\", inserted.ID)\n\n    // Count users\n    count, err := exec.ExecAggregate(ctx, CountAll, nil)\n    if err != nil {\n        log.Fatal(err)\n    }\n    fmt.Printf(\"Total users: %.0f\\n\", count)\n}",{"id":80,"title":81,"titles":82,"content":83,"level":19},"/v1.0.3/learn/quickstart#whats-happening","What's Happening",[58],"New[User](db, \"users\", postgres.New()) creates a exec for the User type bound to the \"users\" table with the PostgreSQL rendererStatements are defined as package-level variables with unique names and descriptionsExecQuery returns all records matching the statement's specExecSelect returns a single record (or error if not found)ExecInsert inserts a record and returns it with generated fields (like ID)ExecAggregate runs an aggregate function and returns the result",{"id":85,"title":86,"titles":87,"content":88,"level":19},"/v1.0.3/learn/quickstart#defining-custom-statements","Defining Custom Statements",[58],"// Define statements for your domain\nvar (\n    // Query for active adult users\n    ActiveAdults = edamame.NewQueryStatement(\"active-adults\", \"Find active users above minimum age\", edamame.QuerySpec{\n        Where: []edamame.ConditionSpec{\n            {Field: \"active\", Operator: \"=\", Param: \"active\"},\n            {Field: \"age\", Operator: \">=\", Param: \"min_age\"},\n        },\n        OrderBy: []edamame.OrderBySpec{\n            {Field: \"name\", Direction: \"asc\"},\n        },\n    })\n\n    // Update user name\n    UpdateName = edamame.NewUpdateStatement(\"update-name\", \"Update user name by ID\", edamame.UpdateSpec{\n        Set:   map[string]string{\"name\": \"new_name\"},\n        Where: []edamame.ConditionSpec{{Field: \"id\", Operator: \"=\", Param: \"id\"}},\n    })\n\n    // Delete inactive users\n    DeleteInactive = edamame.NewDeleteStatement(\"delete-inactive\", \"Delete inactive users\", edamame.DeleteSpec{\n        Where: []edamame.ConditionSpec{{Field: \"active\", Operator: \"=\", Param: \"active\"}},\n    })\n\n    // Sum of ages\n    SumAges = edamame.NewAggregateStatement(\"sum-ages\", \"Sum all user ages\", edamame.AggSum, edamame.AggregateSpec{\n        Field: \"age\",\n    })\n)\n\n// Use the statements\nusers, err := exec.ExecQuery(ctx, ActiveAdults, map[string]any{\n    \"active\":  true,\n    \"min_age\": 18,\n})\n\naffected, err := exec.ExecUpdate(ctx, UpdateName, map[string]any{\n    \"id\":       123,\n    \"new_name\": \"New Name\",\n})\n\ndeleted, err := exec.ExecDelete(ctx, DeleteInactive, map[string]any{\n    \"active\": false,\n})\n\ntotal, err := exec.ExecAggregate(ctx, SumAges, nil)",{"id":90,"title":91,"titles":92,"content":93,"level":19},"/v1.0.3/learn/quickstart#statement-parameters","Statement Parameters",[58],"Statements automatically derive their parameters from the spec. You can inspect them: // Check statement parameters\nfor _, param := range ActiveAdults.Params() {\n    fmt.Printf(\"Param: %s (type: %s, required: %v)\\n\", param.Name, param.Type, param.Required)\n}\n// Output:\n// Param: active (type: any, required: true)\n// Param: min_age (type: any, required: true)",{"id":95,"title":96,"titles":97,"content":98,"level":19},"/v1.0.3/learn/quickstart#next-steps","Next Steps",[58],"Core Concepts - Understand factories, statements, and specsStatement Guide - Define custom queries, updates, and moreTesting - Test your database operations html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}",{"id":100,"title":101,"titles":102,"content":103,"level":9},"/v1.0.3/learn/concepts","Core Concepts",[],"Executors, statements, and specs - the building blocks of edamame",{"id":105,"title":101,"titles":106,"content":107,"level":9},"/v1.0.3/learn/concepts#core-concepts",[],"Edamame has three primitives: executors, statements, and specs. Understanding these unlocks the full API.",{"id":109,"title":110,"titles":111,"content":112,"level":19},"/v1.0.3/learn/concepts#executor","Executor",[101],"An executor is the execution context for a single model type. It wraps soy and provides methods to execute typed statements. exec, err := edamame.New[User](db, \"users\", renderer) The executor: Wraps a soy instance for SQL buildingProvides execution methods that accept typed statementsSupports transactions via *Tx method variants",{"id":114,"title":115,"titles":116,"content":117,"level":40},"/v1.0.3/learn/concepts#struct-tags","Struct Tags",[101,110],"Edamame uses struct tags to understand your model: type User struct {\n    ID    int    `db:\"id\" type:\"integer\" constraints:\"primarykey\"`\n    Email string `db:\"email\" type:\"text\" constraints:\"notnull,unique\"`\n    Name  string `db:\"name\" type:\"text\"`\n    Age   *int   `db:\"age\" type:\"integer\"`\n} TagPurposeExampledbColumn namedb:\"user_id\"typeSQL typetype:\"text\", type:\"integer\"constraintsColumn constraintsconstraints:\"primarykey,notnull\"",{"id":119,"title":120,"titles":121,"content":122,"level":19},"/v1.0.3/learn/concepts#statements","Statements",[101],"A statement is a typed, named database operation. Define statements as package-level variables: var (\n    QueryAll = edamame.NewQueryStatement(\"query-all\", \"Query all users\", edamame.QuerySpec{})\n\n    SelectByID = edamame.NewSelectStatement(\"select-by-id\", \"Select user by ID\", edamame.SelectSpec{\n        Where: []edamame.ConditionSpec{{Field: \"id\", Operator: \"=\", Param: \"id\"}},\n    })\n) Each statement has: Name - Human-readable identifierDescription - What the statement doesID - Auto-generated UUID for uniquenessSpec - Declarative definition of the operationParams - Required parameters (auto-derived from spec)Tags - Optional metadata for categorization",{"id":124,"title":27,"titles":125,"content":126,"level":40},"/v1.0.3/learn/concepts#statement-types",[101,120],"TypeConstructorReturnsUse CaseQueryStatementNewQueryStatement[]*TMulti-record retrievalSelectStatementNewSelectStatement*TSingle-record retrievalUpdateStatementNewUpdateStatement*TModify and return recordDeleteStatementNewDeleteStatementint64Remove records, return countAggregateStatementNewAggregateStatementfloat64COUNT, SUM, AVG, MIN, MAX",{"id":128,"title":129,"titles":130,"content":131,"level":40},"/v1.0.3/learn/concepts#defining-statements","Defining Statements",[101,120],"// Query statement\nvar ByStatus = edamame.NewQueryStatement(\"by-status\", \"Find users by status\", edamame.QuerySpec{\n    Where: []edamame.ConditionSpec{\n        {Field: \"status\", Operator: \"=\", Param: \"status\"},\n    },\n}, \"user\", \"filter\") // Optional tags\n\n// Select statement\nvar ByEmail = edamame.NewSelectStatement(\"by-email\", \"Find user by email\", edamame.SelectSpec{\n    Where: []edamame.ConditionSpec{\n        {Field: \"email\", Operator: \"=\", Param: \"email\"},\n    },\n})\n\n// Update statement\nvar Activate = edamame.NewUpdateStatement(\"activate\", \"Activate a user by ID\", edamame.UpdateSpec{\n    Set: map[string]string{\"active\": \"active\"},\n    Where: []edamame.ConditionSpec{\n        {Field: \"id\", Operator: \"=\", Param: \"id\"},\n    },\n})\n\n// Delete statement\nvar DeleteByID = edamame.NewDeleteStatement(\"delete-by-id\", \"Delete user by ID\", edamame.DeleteSpec{\n    Where: []edamame.ConditionSpec{\n        {Field: \"id\", Operator: \"=\", Param: \"id\"},\n    },\n})\n\n// Aggregate statement\nvar AvgAge = edamame.NewAggregateStatement(\"avg-age\", \"Average age of all users\", edamame.AggAvg, edamame.AggregateSpec{\n    Field: \"age\",\n})",{"id":133,"title":134,"titles":135,"content":136,"level":40},"/v1.0.3/learn/concepts#statement-metadata","Statement Metadata",[101,120],"Inspect statement properties: fmt.Println(ByStatus.Name())        // \"by-status\"\nfmt.Println(ByStatus.Description()) // \"Find users by status\"\nfmt.Println(ByStatus.ID())          // UUID\nfmt.Println(ByStatus.Tags())        // [\"user\", \"filter\"]\n\nfor _, p := range ByStatus.Params() {\n    fmt.Printf(\"Param: %s (type: %s, required: %v)\\n\", p.Name, p.Type, p.Required)\n}",{"id":138,"title":139,"titles":140,"content":141,"level":19},"/v1.0.3/learn/concepts#specs","Specs",[101],"A spec is a declarative definition of a database operation. Specs are pure data—no SQL strings, no builder calls.",{"id":143,"title":144,"titles":145,"content":146,"level":40},"/v1.0.3/learn/concepts#queryspec","QuerySpec",[101,139],"For multi-record retrieval: spec := edamame.QuerySpec{\n    Fields:     []string{\"id\", \"name\", \"email\"},  // SELECT columns (empty = all)\n    Where:      []edamame.ConditionSpec{...},     // WHERE clauses\n    OrderBy:    []edamame.OrderBySpec{...},       // ORDER BY clauses\n    GroupBy:    []string{\"status\"},               // GROUP BY columns\n    Having:     []edamame.ConditionSpec{...},     // HAVING clauses\n    Limit:      &limit,                           // LIMIT\n    Offset:     &offset,                          // OFFSET\n    Distinct:   true,                             // SELECT DISTINCT\n    ForLocking: \"update\",                         // FOR UPDATE/SHARE\n}",{"id":148,"title":149,"titles":150,"content":151,"level":40},"/v1.0.3/learn/concepts#selectspec","SelectSpec",[101,139],"For single-record retrieval (same structure as QuerySpec): spec := edamame.SelectSpec{\n    Fields:     []string{\"id\", \"name\"},\n    Where:      []edamame.ConditionSpec{...},\n    ForLocking: \"share\",\n}",{"id":153,"title":154,"titles":155,"content":156,"level":40},"/v1.0.3/learn/concepts#updatespec","UpdateSpec",[101,139],"For modifications: spec := edamame.UpdateSpec{\n    Set: map[string]string{\n        \"name\":   \"new_name\",    // field -> param mapping\n        \"status\": \"new_status\",\n    },\n    Where: []edamame.ConditionSpec{...},\n}",{"id":158,"title":159,"titles":160,"content":161,"level":40},"/v1.0.3/learn/concepts#deletespec","DeleteSpec",[101,139],"For deletions: spec := edamame.DeleteSpec{\n    Where: []edamame.ConditionSpec{...},\n}",{"id":163,"title":164,"titles":165,"content":166,"level":40},"/v1.0.3/learn/concepts#aggregatespec","AggregateSpec",[101,139],"For aggregate functions: spec := edamame.AggregateSpec{\n    Field: \"age\",                           // Field to aggregate\n    Where: []edamame.ConditionSpec{...},    // Optional filter\n} Use with AggCount, AggSum, AggAvg, AggMin, or AggMax.",{"id":168,"title":169,"titles":170,"content":171,"level":19},"/v1.0.3/learn/concepts#conditions","Conditions",[101],"Conditions define WHERE clauses. They can be simple or grouped.",{"id":173,"title":174,"titles":175,"content":176,"level":40},"/v1.0.3/learn/concepts#simple-conditions","Simple Conditions",[101,169],"cond := edamame.ConditionSpec{\n    Field:    \"age\",\n    Operator: \">=\",\n    Param:    \"min_age\",\n}",{"id":178,"title":179,"titles":180,"content":181,"level":40},"/v1.0.3/learn/concepts#null-conditions","NULL Conditions",[101,169],"// IS NULL\ncond := edamame.ConditionSpec{\n    Field:    \"deleted_at\",\n    IsNull:   true,\n    Operator: \"IS NULL\",\n}\n\n// IS NOT NULL\ncond := edamame.ConditionSpec{\n    Field:    \"email\",\n    IsNull:   true,\n    Operator: \"IS NOT NULL\",\n}",{"id":183,"title":184,"titles":185,"content":186,"level":40},"/v1.0.3/learn/concepts#grouped-conditions-or","Grouped Conditions (OR)",[101,169],"cond := edamame.ConditionSpec{\n    Logic: \"OR\",\n    Group: []edamame.ConditionSpec{\n        {Field: \"status\", Operator: \"=\", Param: \"status1\"},\n        {Field: \"status\", Operator: \"=\", Param: \"status2\"},\n    },\n}",{"id":188,"title":189,"titles":190,"content":191,"level":40},"/v1.0.3/learn/concepts#supported-operators","Supported Operators",[101,169],"OperatorDescription=Equal!=, \u003C>Not equal\u003C, \u003C=Less than>, >=Greater thanLIKE, ILIKEPattern matchingINValue in listIS NULLNULL checkIS NOT NULLNOT NULL check",{"id":193,"title":194,"titles":195,"content":196,"level":19},"/v1.0.3/learn/concepts#ordering","Ordering",[101],"order := edamame.OrderBySpec{\n    Field:     \"created_at\",\n    Direction: \"desc\",      // \"asc\" or \"desc\"\n    Nulls:     \"last\",      // \"first\" or \"last\" (optional)\n}",{"id":198,"title":199,"titles":200,"content":201,"level":40},"/v1.0.3/learn/concepts#expression-based-ordering","Expression-Based Ordering",[101,194],"For vector similarity or computed distances: order := edamame.OrderBySpec{\n    Field:     \"embedding\",\n    Operator:  \"\u003C->\",           // pgvector distance operator\n    Param:     \"query_vector\",\n    Direction: \"asc\",\n}",{"id":203,"title":204,"titles":205,"content":206,"level":19},"/v1.0.3/learn/concepts#execution","Execution",[101],"Execute statements with params: // Query (multiple records)\nusers, err := exec.ExecQuery(ctx, ByStatus, map[string]any{\n    \"status\": \"active\",\n})\n\n// Select (single record)\nuser, err := exec.ExecSelect(ctx, ByEmail, map[string]any{\n    \"email\": \"alice@example.com\",\n})\n\n// Update\nupdated, err := exec.ExecUpdate(ctx, Activate, map[string]any{\n    \"id\":     123,\n    \"active\": true,\n})\n\n// Delete\ncount, err := exec.ExecDelete(ctx, DeleteByID, map[string]any{\n    \"id\": 123,\n})\n\n// Aggregate\navg, err := exec.ExecAggregate(ctx, AvgAge, nil)\n\n// Insert (no statement needed)\ninserted, err := exec.ExecInsert(ctx, &user)",{"id":208,"title":209,"titles":210,"content":211,"level":40},"/v1.0.3/learn/concepts#transaction-support","Transaction Support",[101,204],"All execution methods have *Tx variants: tx, err := db.BeginTxx(ctx, nil)\n\nuser, err := exec.ExecSelectTx(ctx, tx, SelectByID, params)\n_, err = exec.ExecUpdateTx(ctx, tx, Activate, params)\n\ntx.Commit()",{"id":213,"title":214,"titles":215,"content":216,"level":40},"/v1.0.3/learn/concepts#batch-operations","Batch Operations",[101,204],"// Batch insert\ncount, err := exec.ExecInsertBatch(ctx, users)\n\n// Batch update\ncount, err := exec.ExecUpdateBatch(ctx, Activate, []map[string]any{\n    {\"id\": 1, \"active\": true},\n    {\"id\": 2, \"active\": true},\n}) html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}",{"id":218,"title":16,"titles":219,"content":220,"level":9},"/v1.0.3/learn/architecture",[],"How edamame works with soy for SQL generation",{"id":222,"title":16,"titles":223,"content":224,"level":9},"/v1.0.3/learn/architecture#architecture",[],"Edamame is a semantic layer over soy. Understanding this relationship helps you use both effectively.",{"id":226,"title":227,"titles":228,"content":229,"level":19},"/v1.0.3/learn/architecture#layer-diagram","Layer Diagram",[16],"┌─────────────────────────────────────────────────────────────┐\n│                      Your Application                       │\n│                                                             │\n│   exec.ExecQuery(ctx, ByStatus, params)                     │\n└─────────────────────────┬───────────────────────────────────┘\n                          │\n┌─────────────────────────▼───────────────────────────────────┐\n│                         Edamame                             │\n│                                                             │\n│  ┌──────────────┐  Typed statements                         │\n│  │ Executor[T]  │  Spec → Builder conversion                │\n│  │              │  Event emission (capitan)                 │\n│  └──────┬───────┘                                           │\n│         │                                                   │\n│  ┌──────▼───────────────────────────────────────────────┐   │\n│  │                  Statement Types                      │   │\n│  │  QueryStatement  SelectStatement  UpdateStatement     │   │\n│  │  DeleteStatement  AggregateStatement                  │   │\n│  └──────────────────────────────────────────────────────┘   │\n└─────────┬───────────────────────────────────────────────────┘\n          │\n┌─────────▼───────────────────────────────────────────────────┐\n│                          Soy                                │\n│                                                             │\n│  Query builder API                                          │\n│  Field validation (via ASTQL)                               │\n│  Parameterized SQL generation                               │\n│  Render() → {SQL, Params}                                   │\n└─────────┬───────────────────────────────────────────────────┘\n          │\n┌─────────▼───────────────────────────────────────────────────┐\n│                          sqlx                               │\n│                                                             │\n│  Connection pooling                                         │\n│  Named parameter binding                                    │\n│  Struct scanning                                            │\n└─────────────────────────────────────────────────────────────┘",{"id":231,"title":232,"titles":233,"content":34,"level":19},"/v1.0.3/learn/architecture#responsibilities","Responsibilities",[16],{"id":235,"title":236,"titles":237,"content":238,"level":40},"/v1.0.3/learn/architecture#edamame","Edamame",[16,232],"Typed statements - Compile-time safe query definitionsSpec-to-builder conversion - Transform declarative specs into soy buildersExecution wrappers - Convenient Exec* methods with paramsEvents - Emit executor lifecycle events via capitan",{"id":240,"title":241,"titles":242,"content":243,"level":40},"/v1.0.3/learn/architecture#soy","Soy",[16,232],"Query building - Fluent API for constructing SQLField validation - Validate field names against model metadataOperator validation - Ensure operators are safe and validSQL generation - Render builders to parameterized SQLExecution - Execute queries via sqlx",{"id":245,"title":246,"titles":247,"content":248,"level":40},"/v1.0.3/learn/architecture#when-to-use-each","When to Use Each",[16,232],"TaskUseDefine named operationsStatement typesExecute named operationsexec.Exec* methodsAd-hoc queriesexec.Soy().Query()...Custom SQL constructionSoy builder API",{"id":250,"title":251,"titles":252,"content":253,"level":19},"/v1.0.3/learn/architecture#spec-to-builder-flow","Spec-to-Builder Flow",[16],"When you call exec.ExecQuery(ctx, ByStatus, params): Extract spec - Statement contains the spec internallyConvert - Spec is converted to a soy builder:\n// QuerySpec → soy.Query[T]\nbuilder := exec.queryFromSpec(stmt.spec)\nRender - Builder generates SQL:\nresult, err := builder.Render()\n// result.SQL = \"SELECT ... FROM users WHERE status = $1\"\n// result.Params = []any{\"active\"}\nExecute - sqlx runs the parameterized query:\nrows, err := db.QueryxContext(ctx, result.SQL, result.Params...)\nScan - Results are scanned into structs",{"id":255,"title":256,"titles":257,"content":258,"level":19},"/v1.0.3/learn/architecture#security-model","Security Model",[16],"SQL injection protection flows through the entire stack: Edamame - Specs are data, not SQL stringsSoy - Field names validated against model metadataSoy - Operators validated against allowlistSoy - All values become bound parameterssqlx - Parameters passed separately from SQL // User input\nparams := map[string]any{\"status\": userInput}\n\n// Never interpolated into SQL\nexec.ExecQuery(ctx, ByStatus, params)\n\n// Becomes:\n// SQL: \"SELECT ... WHERE status = $1\"\n// Args: [userInput]  // Bound separately",{"id":260,"title":261,"titles":262,"content":263,"level":19},"/v1.0.3/learn/architecture#event-integration","Event Integration",[16],"Edamame emits events via capitan for observability: SignalWhenFieldsExecutorCreatedExecutor initializedtable Hook for monitoring: capitan.Hook(edamame.ExecutorCreated, func(ctx context.Context, e *capitan.Event) {\n    table, _ := edamame.KeyTable.From(e)\n    log.Printf(\"Executor created for table: %s\", table)\n})",{"id":265,"title":266,"titles":267,"content":268,"level":19},"/v1.0.3/learn/architecture#direct-soy-access","Direct Soy Access",[16],"For operations not covered by statements, access soy directly: // Get the underlying soy instance\ns := exec.Soy()\n\n// Use soy's fluent API\nusers, err := s.Query().\n    Where(\"age\", \">=\", \"min_age\").\n    Where(\"status\", \"=\", \"status\").\n    OrderBy(\"name\", \"asc\").\n    Limit(10).\n    Exec(ctx, params) This bypasses statement types but still benefits from: Field validationParameterized queriesType-safe results",{"id":270,"title":37,"titles":271,"content":272,"level":19},"/v1.0.3/learn/architecture#type-safety",[16],"Statements provide compile-time guarantees: // Compiler enforces correct statement types\nusers, err := exec.ExecQuery(ctx, QueryAll, nil)       // Must be QueryStatement\nuser, err := exec.ExecSelect(ctx, SelectByID, params)  // Must be SelectStatement\ncount, err := exec.ExecAggregate(ctx, CountAll, nil)   // Must be AggregateStatement\n\n// This won't compile:\n// exec.ExecQuery(ctx, SelectByID, nil)  // SelectStatement not allowed No magic strings, no runtime lookup failures. html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}",{"id":274,"title":120,"titles":275,"content":276,"level":9},"/v1.0.3/guides/statements",[],"Defining and using typed database statements",{"id":278,"title":120,"titles":279,"content":280,"level":9},"/v1.0.3/guides/statements#statements",[],"This guide covers defining typed statements for your database operations.",{"id":282,"title":283,"titles":284,"content":285,"level":19},"/v1.0.3/guides/statements#queries","Queries",[120],"Queries return multiple records. Use for lists, search results, and filtered collections.",{"id":287,"title":288,"titles":289,"content":290,"level":40},"/v1.0.3/guides/statements#basic-query","Basic Query",[120,283],"var ActiveUsers = edamame.NewQueryStatement(\"active-users\", \"Find all active users\", edamame.QuerySpec{\n    Where: []edamame.ConditionSpec{\n        {Field: \"active\", Operator: \"=\", Param: \"active\"},\n    },\n})\n\n// Usage\nusers, err := exec.ExecQuery(ctx, ActiveUsers, map[string]any{\n    \"active\": true,\n})",{"id":292,"title":293,"titles":294,"content":295,"level":40},"/v1.0.3/guides/statements#with-ordering-and-pagination","With Ordering and Pagination",[120,283],"limit := 20\noffset := 0\n\nvar RecentUsers = edamame.NewQueryStatement(\"recent-users\", \"Get users ordered by creation date\", edamame.QuerySpec{\n    OrderBy: []edamame.OrderBySpec{\n        {Field: \"created_at\", Direction: \"desc\"},\n    },\n    Limit:  &limit,\n    Offset: &offset,\n})",{"id":297,"title":298,"titles":299,"content":300,"level":40},"/v1.0.3/guides/statements#with-field-selection","With Field Selection",[120,283],"var UserNames = edamame.NewQueryStatement(\"user-names\", \"Get user names only\", edamame.QuerySpec{\n    Fields: []string{\"id\", \"name\"},  // Only select these columns\n})",{"id":302,"title":303,"titles":304,"content":305,"level":40},"/v1.0.3/guides/statements#with-grouping","With Grouping",[120,283],"var UsersByRole = edamame.NewQueryStatement(\"users-by-role\", \"Group users by role\", edamame.QuerySpec{\n    Fields:  []string{\"role\", \"COUNT(*) as count\"},\n    GroupBy: []string{\"role\"},\n})",{"id":307,"title":308,"titles":309,"content":310,"level":40},"/v1.0.3/guides/statements#having-with-aggregates","HAVING with Aggregates",[120,283],"Use HavingAgg for aggregate conditions in HAVING clauses: var PopularRoles = edamame.NewQueryStatement(\"popular-roles\", \"Roles with minimum user count\", edamame.QuerySpec{\n    Fields:  []string{\"role\"},\n    GroupBy: []string{\"role\"},\n    HavingAgg: []edamame.HavingAggSpec{\n        {Func: \"count\", Field: \"*\", Operator: \">=\", Param: \"min_count\"},\n    },\n})\n\n// Generates: SELECT role FROM users GROUP BY role HAVING COUNT(*) >= $1 Multiple aggregate conditions: var HighValueRoles = edamame.NewQueryStatement(\"high-value-roles\", \"Roles with high balance and count\", edamame.QuerySpec{\n    Fields:  []string{\"role\"},\n    GroupBy: []string{\"role\"},\n    HavingAgg: []edamame.HavingAggSpec{\n        {Func: \"count\", Field: \"*\", Operator: \">=\", Param: \"min_count\"},\n        {Func: \"sum\", Field: \"balance\", Operator: \">=\", Param: \"min_total\"},\n    },\n})",{"id":312,"title":313,"titles":314,"content":315,"level":40},"/v1.0.3/guides/statements#distinct-on-postgresql","DISTINCT ON (PostgreSQL)",[120,283],"Use DistinctOn for PostgreSQL's DISTINCT ON clause: var LatestPerUser = edamame.NewQueryStatement(\"latest-per-user\", \"Latest record per user\", edamame.QuerySpec{\n    DistinctOn: []string{\"user_id\"},\n    OrderBy: []edamame.OrderBySpec{\n        {Field: \"user_id\", Direction: \"asc\"},\n        {Field: \"created_at\", Direction: \"desc\"},\n    },\n})\n\n// Generates: SELECT DISTINCT ON (user_id) * FROM ... ORDER BY user_id ASC, created_at DESC",{"id":317,"title":318,"titles":319,"content":320,"level":40},"/v1.0.3/guides/statements#complex-conditions","Complex Conditions",[120,283],"var FilteredUsers = edamame.NewQueryStatement(\"filtered-users\", \"Filter users by age and role\", edamame.QuerySpec{\n    Where: []edamame.ConditionSpec{\n        {Field: \"age\", Operator: \">=\", Param: \"min_age\"},\n        {Field: \"age\", Operator: \"\u003C=\", Param: \"max_age\"},\n        {\n            Logic: \"OR\",\n            Group: []edamame.ConditionSpec{\n                {Field: \"role\", Operator: \"=\", Param: \"role1\"},\n                {Field: \"role\", Operator: \"=\", Param: \"role2\"},\n            },\n        },\n    },\n})\n\n// Generates: WHERE age >= $1 AND age \u003C= $2 AND (role = $3 OR role = $4)",{"id":322,"title":323,"titles":324,"content":325,"level":40},"/v1.0.3/guides/statements#between-conditions","BETWEEN Conditions",[120,283],"var UsersInAgeRange = edamame.NewQueryStatement(\"users-in-age-range\", \"Users in age range\", edamame.QuerySpec{\n    Where: []edamame.ConditionSpec{\n        {Field: \"age\", Between: true, LowParam: \"min_age\", HighParam: \"max_age\"},\n    },\n})\n\n// Generates: WHERE age BETWEEN $1 AND $2\n\n// NOT BETWEEN\nvar UsersOutsideRange = edamame.NewQueryStatement(\"users-outside-range\", \"Users outside age range\", edamame.QuerySpec{\n    Where: []edamame.ConditionSpec{\n        {Field: \"age\", NotBetween: true, LowParam: \"min_age\", HighParam: \"max_age\"},\n    },\n})\n\n// Generates: WHERE age NOT BETWEEN $1 AND $2",{"id":327,"title":328,"titles":329,"content":330,"level":40},"/v1.0.3/guides/statements#field-to-field-comparisons","Field-to-Field Comparisons",[120,283],"Compare two columns directly without parameters: var ModifiedAfterCreated = edamame.NewQueryStatement(\"modified-after-created\", \"Records updated after creation\", edamame.QuerySpec{\n    Where: []edamame.ConditionSpec{\n        {Field: \"updated_at\", Operator: \">\", RightField: \"created_at\"},\n    },\n})\n\n// Generates: WHERE updated_at > created_at",{"id":332,"title":333,"titles":334,"content":335,"level":40},"/v1.0.3/guides/statements#parameterized-pagination","Parameterized Pagination",[120,283],"Use parameter-driven limits and offsets for flexible pagination: var PaginatedUsers = edamame.NewQueryStatement(\"paginated-users\", \"Paginated user list\", edamame.QuerySpec{\n    LimitParam:  \"page_size\",\n    OffsetParam: \"offset\",\n    OrderBy: []edamame.OrderBySpec{\n        {Field: \"created_at\", Direction: \"desc\"},\n    },\n})\n\n// Usage\nusers, err := exec.ExecQuery(ctx, PaginatedUsers, map[string]any{\n    \"page_size\": 20,\n    \"offset\":    40,  // Page 3\n})",{"id":337,"title":338,"titles":339,"content":340,"level":40},"/v1.0.3/guides/statements#select-expressions","Select Expressions",[120,283],"Add computed columns using SQL functions: var UsersWithComputed = edamame.NewQueryStatement(\"users-with-computed\", \"Users with computed columns\", edamame.QuerySpec{\n    Fields: []string{\"id\", \"name\"},\n    SelectExprs: []edamame.SelectExprSpec{\n        {Func: \"upper\", Field: \"name\", Alias: \"upper_name\"},\n        {Func: \"length\", Field: \"email\", Alias: \"email_length\"},\n        {Func: \"count_star\", Alias: \"total\"},\n        {Func: \"now\", Alias: \"query_time\"},\n    },\n    GroupBy: []string{\"id\", \"name\"},\n}) Available functions include: upper, lower, length, trim, concat, abs, ceil, floor, round, now, current_date, cast, count, sum, avg, min, max, coalesce, nullif.",{"id":342,"title":343,"titles":344,"content":345,"level":40},"/v1.0.3/guides/statements#with-row-locking","With Row Locking",[120,283],"var ForUpdate = edamame.NewQueryStatement(\"for-update\", \"Query with row lock\", edamame.QuerySpec{\n    Where:      []edamame.ConditionSpec{{Field: \"status\", Operator: \"=\", Param: \"status\"}},\n    ForLocking: \"update\",  // FOR UPDATE\n}) Locking options: \"update\", \"no_key_update\", \"share\", \"key_share\"",{"id":347,"title":348,"titles":349,"content":350,"level":19},"/v1.0.3/guides/statements#selects","Selects",[120],"Selects return a single record. Use for lookups by unique identifier.",{"id":352,"title":353,"titles":354,"content":355,"level":40},"/v1.0.3/guides/statements#by-unique-field","By Unique Field",[120,348],"var ByEmail = edamame.NewSelectStatement(\"by-email\", \"Find user by email address\", edamame.SelectSpec{\n    Where: []edamame.ConditionSpec{\n        {Field: \"email\", Operator: \"=\", Param: \"email\"},\n    },\n})\n\n// Usage - returns error if not found\nuser, err := exec.ExecSelect(ctx, ByEmail, map[string]any{\n    \"email\": \"alice@example.com\",\n})",{"id":357,"title":358,"titles":359,"content":360,"level":40},"/v1.0.3/guides/statements#with-locking","With Locking",[120,348],"var ForShare = edamame.NewSelectStatement(\"for-share\", \"Select with shared lock\", edamame.SelectSpec{\n    Where:      []edamame.ConditionSpec{{Field: \"id\", Operator: \"=\", Param: \"id\"}},\n    ForLocking: \"share\",  // FOR SHARE\n})",{"id":362,"title":363,"titles":364,"content":365,"level":19},"/v1.0.3/guides/statements#updates","Updates",[120],"Updates modify records and return the updated row.",{"id":367,"title":368,"titles":369,"content":370,"level":40},"/v1.0.3/guides/statements#single-field-update","Single Field Update",[120,363],"var Activate = edamame.NewUpdateStatement(\"activate\", \"Activate a user by ID\", edamame.UpdateSpec{\n    Set: map[string]string{\n        \"active\": \"active\",  // field -> param\n    },\n    Where: []edamame.ConditionSpec{\n        {Field: \"id\", Operator: \"=\", Param: \"id\"},\n    },\n})\n\n// Usage\nupdated, err := exec.ExecUpdate(ctx, Activate, map[string]any{\n    \"id\":     123,\n    \"active\": true,\n})",{"id":372,"title":373,"titles":374,"content":375,"level":40},"/v1.0.3/guides/statements#multi-field-update","Multi-Field Update",[120,363],"var UpdateProfile = edamame.NewUpdateStatement(\"update-profile\", \"Update user profile\", edamame.UpdateSpec{\n    Set: map[string]string{\n        \"name\":  \"new_name\",\n        \"email\": \"new_email\",\n        \"bio\":   \"new_bio\",\n    },\n    Where: []edamame.ConditionSpec{\n        {Field: \"id\", Operator: \"=\", Param: \"id\"},\n    },\n})",{"id":377,"title":378,"titles":379,"content":380,"level":40},"/v1.0.3/guides/statements#batch-updates","Batch Updates",[120,363],"// Execute same update with different params\ncount, err := exec.ExecUpdateBatch(ctx, Activate, []map[string]any{\n    {\"id\": 1, \"active\": true},\n    {\"id\": 2, \"active\": true},\n    {\"id\": 3, \"active\": false},\n})",{"id":382,"title":383,"titles":384,"content":385,"level":19},"/v1.0.3/guides/statements#deletes","Deletes",[120],"Deletes remove records and return the count of deleted rows.",{"id":387,"title":388,"titles":389,"content":390,"level":40},"/v1.0.3/guides/statements#by-single-field","By Single Field",[120,383],"var ByStatus = edamame.NewDeleteStatement(\"by-status\", \"Delete all users with given status\", edamame.DeleteSpec{\n    Where: []edamame.ConditionSpec{\n        {Field: \"status\", Operator: \"=\", Param: \"status\"},\n    },\n})\n\n// Usage\ncount, err := exec.ExecDelete(ctx, ByStatus, map[string]any{\n    \"status\": \"inactive\",\n})",{"id":392,"title":393,"titles":394,"content":395,"level":40},"/v1.0.3/guides/statements#with-multiple-conditions","With Multiple Conditions",[120,383],"var ExpiredSessions = edamame.NewDeleteStatement(\"expired-sessions\", \"Delete expired sessions\", edamame.DeleteSpec{\n    Where: []edamame.ConditionSpec{\n        {Field: \"expires_at\", Operator: \"\u003C\", Param: \"now\"},\n        {Field: \"active\", Operator: \"=\", Param: \"active\"},\n    },\n})",{"id":397,"title":398,"titles":399,"content":400,"level":19},"/v1.0.3/guides/statements#aggregates","Aggregates",[120],"Aggregates compute values across records.",{"id":402,"title":403,"titles":404,"content":405,"level":40},"/v1.0.3/guides/statements#count","Count",[120,398],"var CountActive = edamame.NewAggregateStatement(\"count-active\", \"Count active users\", edamame.AggCount, edamame.AggregateSpec{\n    Where: []edamame.ConditionSpec{\n        {Field: \"active\", Operator: \"=\", Param: \"active\"},\n    },\n})\n\ncount, err := exec.ExecAggregate(ctx, CountActive, map[string]any{\n    \"active\": true,\n})",{"id":407,"title":408,"titles":409,"content":410,"level":40},"/v1.0.3/guides/statements#sum-avg-min-max","Sum, Avg, Min, Max",[120,398],"// Sum\nvar TotalBalance = edamame.NewAggregateStatement(\"total-balance\", \"Total balance\", edamame.AggSum, edamame.AggregateSpec{\n    Field: \"balance\",\n})\n\n// Average\nvar AvgAge = edamame.NewAggregateStatement(\"avg-age\", \"Average age\", edamame.AggAvg, edamame.AggregateSpec{\n    Field: \"age\",\n})\n\n// Min/Max\nvar Youngest = edamame.NewAggregateStatement(\"youngest\", \"Youngest user\", edamame.AggMin, edamame.AggregateSpec{\n    Field: \"age\",\n})",{"id":412,"title":413,"titles":414,"content":415,"level":19},"/v1.0.3/guides/statements#inserts","Inserts",[120],"Inserts don't use statements - they're driven by struct fields: // Single insert\ninserted, err := exec.ExecInsert(ctx, &user)\n\n// Batch insert\ncount, err := exec.ExecInsertBatch(ctx, users)",{"id":417,"title":418,"titles":419,"content":420,"level":40},"/v1.0.3/guides/statements#with-conflict-handling","With Conflict Handling",[120,413],"For upsert patterns, use the underlying soy API: s := exec.Soy()\n\n// ON CONFLICT DO NOTHING\nresult, err := s.Insert().\n    OnConflictDoNothing(\"email\").\n    Exec(ctx, &user)\n\n// ON CONFLICT DO UPDATE\nresult, err := s.Insert().\n    OnConflict(\"email\").\n    DoUpdate(map[string]string{\"name\": \"name\"}).\n    Exec(ctx, &user)",{"id":422,"title":423,"titles":424,"content":425,"level":19},"/v1.0.3/guides/statements#compound-queries","Compound Queries",[120],"Compound queries combine multiple SELECT statements using set operations.",{"id":427,"title":428,"titles":429,"content":430,"level":40},"/v1.0.3/guides/statements#union","UNION",[120,423],"spec := edamame.CompoundQuerySpec{\n    Base: edamame.QuerySpec{\n        Where: []edamame.ConditionSpec{\n            {Field: \"role\", Operator: \"=\", Param: \"role1\"},\n        },\n    },\n    Operands: []edamame.CompoundOperand{\n        {\n            Operation: \"union\",\n            Query: edamame.QuerySpec{\n                Where: []edamame.ConditionSpec{\n                    {Field: \"role\", Operator: \"=\", Param: \"role2\"},\n                },\n            },\n        },\n    },\n    OrderBy: []edamame.OrderBySpec{\n        {Field: \"name\", Direction: \"asc\"},\n    },\n}\n\nusers, err := exec.ExecCompound(ctx, spec, map[string]any{\n    \"role1\": \"admin\",\n    \"role2\": \"moderator\",\n})",{"id":432,"title":433,"titles":434,"content":435,"level":40},"/v1.0.3/guides/statements#available-operations","Available Operations",[120,423],"OperationDescriptionunionCombine results, remove duplicatesunion_allCombine results, keep duplicatesintersectOnly rows in both queriesintersect_allIntersection with duplicatesexceptRows in first but not secondexcept_allExcept with duplicates",{"id":437,"title":438,"titles":439,"content":440,"level":40},"/v1.0.3/guides/statements#rendering-for-inspection","Rendering for Inspection",[120,423],"sql, err := exec.RenderCompound(spec)\n// SELECT ... WHERE role = $1 UNION SELECT ... WHERE role = $2 ORDER BY name ASC",{"id":442,"title":134,"titles":443,"content":136,"level":19},"/v1.0.3/guides/statements#statement-metadata",[120],{"id":445,"title":446,"titles":447,"content":448,"level":19},"/v1.0.3/guides/statements#tags","Tags",[120],"Statements support optional tags for categorization: var ByRole = edamame.NewQueryStatement(\"by-role\", \"Find users by role\", edamame.QuerySpec{\n    Where: []edamame.ConditionSpec{\n        {Field: \"role\", Operator: \"=\", Param: \"role\"},\n    },\n}, \"user\", \"filter\", \"security\")  // Tags as variadic args",{"id":450,"title":451,"titles":452,"content":453,"level":19},"/v1.0.3/guides/statements#parameter-derivation","Parameter Derivation",[120],"Params are automatically derived from specs: var Example = edamame.NewQueryStatement(\"example\", \"Example query\", edamame.QuerySpec{\n    Where: []edamame.ConditionSpec{\n        {Field: \"age\", Operator: \">=\", Param: \"min_age\"},\n        {Field: \"status\", Operator: \"=\", Param: \"status\"},\n    },\n})\n\n// Params auto-derived:\n// - min_age (required)\n// - status (required)\n\nfor _, p := range Example.Params() {\n    fmt.Printf(\"Param: %s (required: %v)\\n\", p.Name, p.Required)\n} html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}",{"id":455,"title":456,"titles":457,"content":458,"level":9},"/v1.0.3/guides/testing","Testing",[],"Testing strategies for edamame-based applications",{"id":460,"title":456,"titles":461,"content":462,"level":9},"/v1.0.3/guides/testing#testing",[],"Edamame provides testing utilities in the github.com/zoobz-io/edamame/testing package.",{"id":464,"title":465,"titles":466,"content":34,"level":19},"/v1.0.3/guides/testing#test-helpers","Test Helpers",[456],{"id":468,"title":469,"titles":470,"content":471,"level":40},"/v1.0.3/guides/testing#querycapture","QueryCapture",[456,465],"Capture rendered SQL for verification: import (\n    edamametesting \"github.com/zoobz-io/edamame/testing\"\n    \"github.com/zoobz-io/astql/pkg/postgres\"\n)\n\nfunc TestQueryRendering(t *testing.T) {\n    capture := edamametesting.NewQueryCapture()\n\n    exec, _ := edamame.New[User](nil, \"users\", postgres.New())\n\n    // Define a statement\n    var ByStatus = edamame.NewQueryStatement(\"by-status\", \"Find by status\", edamame.QuerySpec{\n        Where: []edamame.ConditionSpec{{Field: \"status\", Operator: \"=\", Param: \"status\"}},\n    })\n\n    // Get the builder and render\n    q, _ := exec.Query(ByStatus)\n    result, _ := q.Render()\n\n    capture.CaptureQuery(\"by-status\", \"query\", result.SQL, nil)\n\n    // Verify\n    if capture.Count() != 1 {\n        t.Errorf(\"expected 1 query, got %d\", capture.Count())\n    }\n\n    last := capture.Last()\n    if last.Type != \"query\" {\n        t.Errorf(\"expected type 'query', got %q\", last.Type)\n    }\n}",{"id":473,"title":474,"titles":475,"content":476,"level":40},"/v1.0.3/guides/testing#executoreventcapture","ExecutorEventCapture",[456,465],"Capture executor creation events via capitan: func TestExecutorEvents(t *testing.T) {\n    c := capitan.New(capitan.WithSyncMode())\n    defer c.Shutdown()\n\n    capture := edamametesting.NewExecutorEventCapture()\n    c.Hook(edamame.ExecutorCreated, capture.Handler())\n\n    exec, _ := edamame.New[User](nil, \"users\", postgres.New())\n\n    // Verify event captured\n    if capture.Count() != 1 {\n        t.Error(\"expected executor created event\")\n    }\n\n    tables := capture.Tables()\n    if tables[0].Table != \"users\" {\n        t.Errorf(\"expected table 'users', got %q\", tables[0].Table)\n    }\n}",{"id":478,"title":479,"titles":480,"content":481,"level":40},"/v1.0.3/guides/testing#parambuilder","ParamBuilder",[456,465],"Build test parameters fluently: func TestWithParams(t *testing.T) {\n    params := edamametesting.NewParamBuilder().\n        Set(\"id\", 123).\n        Set(\"status\", \"active\").\n        Set(\"limit\", 10).\n        Build()\n\n    var Filtered = edamame.NewQueryStatement(\"filtered\", \"Filtered query\", edamame.QuerySpec{\n        Where: []edamame.ConditionSpec{{Field: \"status\", Operator: \"=\", Param: \"status\"}},\n    })\n\n    // Use in tests\n    users, err := exec.ExecQuery(ctx, Filtered, params)\n}",{"id":483,"title":484,"titles":485,"content":486,"level":19},"/v1.0.3/guides/testing#unit-testing-without-database","Unit Testing Without Database",[456],"Test statement creation and specs without a database: func TestStatements(t *testing.T) {\n    // nil db is valid for testing statements\n    exec, err := edamame.New[User](nil, \"users\", postgres.New())\n    if err != nil {\n        t.Fatal(err)\n    }\n\n    // Define statements\n    var QueryAll = edamame.NewQueryStatement(\"query-all\", \"Query all\", edamame.QuerySpec{})\n\n    var ByStatus = edamame.NewQueryStatement(\"by-status\", \"Find by status\", edamame.QuerySpec{\n        Where: []edamame.ConditionSpec{\n            {Field: \"status\", Operator: \"=\", Param: \"status\"},\n        },\n    })\n\n    // Test statement metadata\n    if ByStatus.Name() != \"by-status\" {\n        t.Errorf(\"expected name 'by-status', got %q\", ByStatus.Name())\n    }\n\n    // Test params derived correctly\n    params := ByStatus.Params()\n    if len(params) != 1 || params[0].Name != \"status\" {\n        t.Error(\"expected 1 param named 'status'\")\n    }\n\n    // Test builder creation\n    q, err := exec.Query(ByStatus)\n    if err != nil {\n        t.Fatal(err)\n    }\n\n    // Test rendering\n    result, err := q.Render()\n    if err != nil {\n        t.Fatal(err)\n    }\n\n    if result.SQL == \"\" {\n        t.Error(\"empty SQL rendered\")\n    }\n}",{"id":488,"title":489,"titles":490,"content":491,"level":19},"/v1.0.3/guides/testing#testing-sql-rendering","Testing SQL Rendering",[456],"Verify generated SQL without executing: func TestSQLRendering(t *testing.T) {\n    exec, _ := edamame.New[User](nil, \"users\", postgres.New())\n\n    var Adults = edamame.NewQueryStatement(\"adults\", \"Find adults\", edamame.QuerySpec{\n        Where: []edamame.ConditionSpec{\n            {Field: \"age\", Operator: \">=\", Param: \"min_age\"},\n        },\n        OrderBy: []edamame.OrderBySpec{\n            {Field: \"name\", Direction: \"asc\"},\n        },\n    })\n\n    q, _ := exec.Query(Adults)\n    result, err := q.Render()\n    if err != nil {\n        t.Fatal(err)\n    }\n\n    // Verify SQL structure\n    if !strings.Contains(result.SQL, \"WHERE\") {\n        t.Error(\"SQL missing WHERE clause\")\n    }\n    if !strings.Contains(result.SQL, \"ORDER BY\") {\n        t.Error(\"SQL missing ORDER BY clause\")\n    }\n}",{"id":493,"title":494,"titles":495,"content":496,"level":19},"/v1.0.3/guides/testing#integration-testing-with-testcontainers","Integration Testing with Testcontainers",[456],"For database integration tests, use testcontainers: //go:build integration\n\npackage integration\n\nimport (\n    \"context\"\n    \"testing\"\n\n    \"github.com/testcontainers/testcontainers-go\"\n    \"github.com/testcontainers/testcontainers-go/wait\"\n)\n\nfunc TestWithPostgres(t *testing.T) {\n    ctx := context.Background()\n\n    // Start PostgreSQL container\n    req := testcontainers.ContainerRequest{\n        Image:        \"postgres:16-alpine\",\n        ExposedPorts: []string{\"5432/tcp\"},\n        WaitingFor:   wait.ForLog(\"database system is ready to accept connections\"),\n        Env: map[string]string{\n            \"POSTGRES_USER\":     \"test\",\n            \"POSTGRES_PASSWORD\": \"test\",\n            \"POSTGRES_DB\":       \"testdb\",\n        },\n    }\n\n    container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{\n        ContainerRequest: req,\n        Started:          true,\n    })\n    if err != nil {\n        t.Fatal(err)\n    }\n    defer container.Terminate(ctx)\n\n    // Get connection details\n    host, _ := container.Host(ctx)\n    port, _ := container.MappedPort(ctx, \"5432\")\n\n    // Connect and test\n    dsn := fmt.Sprintf(\"host=%s port=%s user=test password=test dbname=testdb sslmode=disable\", host, port.Port())\n    db, err := sqlx.Connect(\"postgres\", dsn)\n    if err != nil {\n        t.Fatal(err)\n    }\n\n    // Create table\n    db.ExecContext(ctx, `CREATE TABLE users (\n        id SERIAL PRIMARY KEY,\n        email TEXT NOT NULL UNIQUE,\n        name TEXT,\n        age INTEGER\n    )`)\n\n    // Test executor\n    exec, _ := edamame.New[User](db, \"users\", postgres.New())\n\n    age := 25\n    user := &User{Email: \"test@example.com\", Name: \"Test\", Age: &age}\n    inserted, err := exec.ExecInsert(ctx, user)\n    if err != nil {\n        t.Fatal(err)\n    }\n\n    if inserted.ID == 0 {\n        t.Error(\"expected non-zero ID\")\n    }\n} Run integration tests: go test -tags=integration ./testing/integration/...",{"id":498,"title":499,"titles":500,"content":501,"level":19},"/v1.0.3/guides/testing#benchmarking","Benchmarking",[456],"The testing/benchmarks package provides performance benchmarks: func BenchmarkQueryBuilding(b *testing.B) {\n    exec, _ := edamame.New[User](nil, \"users\", postgres.New())\n\n    var QueryAll = edamame.NewQueryStatement(\"query-all\", \"Query all\", edamame.QuerySpec{})\n\n    b.ResetTimer()\n    b.ReportAllocs()\n\n    for i := 0; i \u003C b.N; i++ {\n        _, _ = exec.Query(QueryAll)\n    }\n} Run benchmarks: go test ./testing/benchmarks/... -bench=. -benchmem",{"id":503,"title":504,"titles":505,"content":506,"level":19},"/v1.0.3/guides/testing#testing-events","Testing Events",[456],"Verify capitan events are emitted correctly: func TestExecutorEmitsCreatedEvent(t *testing.T) {\n    c := capitan.New(capitan.WithSyncMode())\n    defer c.Shutdown()\n\n    capture := edamametesting.NewExecutorEventCapture()\n    c.Hook(edamame.ExecutorCreated, capture.Handler())\n\n    _, _ = edamame.New[User](nil, \"users\", postgres.New())\n\n    if capture.Count() != 1 {\n        t.Errorf(\"expected 1 executor created event, got %d\", capture.Count())\n    }\n\n    tables := capture.Tables()\n    if tables[0].Table != \"users\" {\n        t.Errorf(\"expected table 'users', got %q\", tables[0].Table)\n    }\n}",{"id":508,"title":509,"titles":510,"content":511,"level":19},"/v1.0.3/guides/testing#testing-transaction-behavior","Testing Transaction Behavior",[456],"func TestTransaction(t *testing.T) {\n    // ... setup db and exec ...\n\n    var QueryAll = edamame.NewQueryStatement(\"query-all\", \"Query all\", edamame.QuerySpec{})\n    var SelectByID = edamame.NewSelectStatement(\"select-by-id\", \"Select by ID\", edamame.SelectSpec{\n        Where: []edamame.ConditionSpec{{Field: \"id\", Operator: \"=\", Param: \"id\"}},\n    })\n\n    tx, _ := db.BeginTxx(ctx, nil)\n\n    // Insert in transaction\n    user := &User{Email: \"tx@test.com\", Name: \"TxTest\"}\n    inserted, err := exec.ExecInsertTx(ctx, tx, user)\n    if err != nil {\n        tx.Rollback()\n        t.Fatal(err)\n    }\n\n    // Verify visible in transaction\n    users, _ := exec.ExecQueryTx(ctx, tx, QueryAll, nil)\n    if len(users) != 1 {\n        t.Error(\"expected 1 user in transaction\")\n    }\n\n    // Rollback\n    tx.Rollback()\n\n    // Verify not visible after rollback\n    users, _ = exec.ExecQuery(ctx, QueryAll, nil)\n    if len(users) != 0 {\n        t.Error(\"expected 0 users after rollback\")\n    }\n}",{"id":513,"title":514,"titles":515,"content":516,"level":19},"/v1.0.3/guides/testing#async-event-testing","Async Event Testing",[456],"Use WaitForCount for async event verification: func TestAsyncEvents(t *testing.T) {\n    c := capitan.New()  // async mode\n    defer c.Shutdown()\n\n    capture := edamametesting.NewExecutorEventCapture()\n    c.Hook(edamame.ExecutorCreated, capture.Handler())\n\n    _, _ = edamame.New[User](nil, \"users\", postgres.New())\n\n    // Wait for async event processing\n    if !capture.WaitForCount(1, 500*time.Millisecond) {\n        t.Error(\"timed out waiting for event\")\n    }\n} html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}",{"id":518,"title":519,"titles":520,"content":521,"level":9},"/v1.0.3/cookbook/llm-integration","LLM Integration",[],"Using edamame statements with AI assistants",{"id":523,"title":519,"titles":524,"content":525,"level":9},"/v1.0.3/cookbook/llm-integration#llm-integration",[],"Edamame's typed statements with self-describing metadata make it ideal for AI-assisted database operations.",{"id":527,"title":528,"titles":529,"content":530,"level":19},"/v1.0.3/cookbook/llm-integration#the-pattern","The Pattern",[519],"Define statements with descriptive names and descriptionsCollect statement metadata into a registrySerialize registry as JSON for LLM contextLLM selects statement by name and provides paramsExecute statement with LLM-provided inputs User Query → LLM (with statement metadata) → Statement Name + Params → Edamame → Results",{"id":532,"title":129,"titles":533,"content":534,"level":19},"/v1.0.3/cookbook/llm-integration#defining-statements",[519],"Statements are self-describing with name, description, params, and tags: var (\n    QueryAll = edamame.NewQueryStatement(\"query-all\", \"Query all users\", edamame.QuerySpec{})\n\n    ByRole = edamame.NewQueryStatement(\"by-role\", \"Find users by role\", edamame.QuerySpec{\n        Where: []edamame.ConditionSpec{\n            {Field: \"role\", Operator: \"=\", Param: \"role\"},\n        },\n    }, \"filter\", \"security\")\n\n    ActiveAdults = edamame.NewQueryStatement(\"active-adults\", \"Find active users over 18\", edamame.QuerySpec{\n        Where: []edamame.ConditionSpec{\n            {Field: \"active\", Operator: \"=\", Param: \"active\"},\n            {Field: \"age\", Operator: \">=\", Param: \"min_age\"},\n        },\n    }, \"filter\")\n\n    SelectByID = edamame.NewSelectStatement(\"select-by-id\", \"Select a single user by ID\", edamame.SelectSpec{\n        Where: []edamame.ConditionSpec{\n            {Field: \"id\", Operator: \"=\", Param: \"id\"},\n        },\n    })\n\n    CountAll = edamame.NewAggregateStatement(\"count-all\", \"Count all users\", edamame.AggCount, edamame.AggregateSpec{})\n)",{"id":536,"title":537,"titles":538,"content":539,"level":19},"/v1.0.3/cookbook/llm-integration#building-a-statement-registry","Building a Statement Registry",[519],"Create a registry to collect statements for LLM consumption: type StatementInfo struct {\n    Name        string      `json:\"name\"`\n    Description string      `json:\"description\"`\n    Type        string      `json:\"type\"`\n    Params      []ParamInfo `json:\"params\"`\n    Tags        []string    `json:\"tags,omitempty\"`\n}\n\ntype ParamInfo struct {\n    Name     string `json:\"name\"`\n    Type     string `json:\"type\"`\n    Required bool   `json:\"required\"`\n}\n\ntype StatementRegistry struct {\n    Table      string          `json:\"table\"`\n    Queries    []StatementInfo `json:\"queries,omitempty\"`\n    Selects    []StatementInfo `json:\"selects,omitempty\"`\n    Updates    []StatementInfo `json:\"updates,omitempty\"`\n    Deletes    []StatementInfo `json:\"deletes,omitempty\"`\n    Aggregates []StatementInfo `json:\"aggregates,omitempty\"`\n}\n\nfunc NewRegistry(table string) *StatementRegistry {\n    return &StatementRegistry{Table: table}\n}\n\nfunc (r *StatementRegistry) AddQuery(stmt edamame.QueryStatement) {\n    r.Queries = append(r.Queries, toStatementInfo(stmt.Name(), stmt.Description(), \"query\", stmt.Params(), stmt.Tags()))\n}\n\nfunc (r *StatementRegistry) AddSelect(stmt edamame.SelectStatement) {\n    r.Selects = append(r.Selects, toStatementInfo(stmt.Name(), stmt.Description(), \"select\", stmt.Params(), stmt.Tags()))\n}\n\nfunc (r *StatementRegistry) AddAggregate(stmt edamame.AggregateStatement) {\n    r.Aggregates = append(r.Aggregates, toStatementInfo(stmt.Name(), stmt.Description(), \"aggregate\", stmt.Params(), stmt.Tags()))\n}\n\nfunc toStatementInfo(name, desc, typ string, params []edamame.ParamSpec, tags []string) StatementInfo {\n    info := StatementInfo{\n        Name:        name,\n        Description: desc,\n        Type:        typ,\n        Tags:        tags,\n    }\n    for _, p := range params {\n        info.Params = append(info.Params, ParamInfo{\n            Name:     p.Name,\n            Type:     p.Type,\n            Required: p.Required,\n        })\n    }\n    return info\n}\n\nfunc (r *StatementRegistry) JSON() (string, error) {\n    data, err := json.MarshalIndent(r, \"\", \"  \")\n    return string(data), err\n}",{"id":541,"title":542,"titles":543,"content":544,"level":19},"/v1.0.3/cookbook/llm-integration#exporting-for-llm-context","Exporting for LLM Context",[519],"// Build registry\nregistry := NewRegistry(\"users\")\nregistry.AddQuery(QueryAll)\nregistry.AddQuery(ByRole)\nregistry.AddQuery(ActiveAdults)\nregistry.AddSelect(SelectByID)\nregistry.AddAggregate(CountAll)\n\n// Export as JSON\njson, _ := registry.JSON() Example output: {\n  \"table\": \"users\",\n  \"queries\": [\n    {\n      \"name\": \"query-all\",\n      \"description\": \"Query all users\",\n      \"type\": \"query\",\n      \"params\": []\n    },\n    {\n      \"name\": \"by-role\",\n      \"description\": \"Find users by role\",\n      \"type\": \"query\",\n      \"params\": [\n        {\"name\": \"role\", \"type\": \"any\", \"required\": true}\n      ],\n      \"tags\": [\"filter\", \"security\"]\n    },\n    {\n      \"name\": \"active-adults\",\n      \"description\": \"Find active users over 18\",\n      \"type\": \"query\",\n      \"params\": [\n        {\"name\": \"active\", \"type\": \"any\", \"required\": true},\n        {\"name\": \"min_age\", \"type\": \"any\", \"required\": true}\n      ],\n      \"tags\": [\"filter\"]\n    }\n  ],\n  \"selects\": [\n    {\n      \"name\": \"select-by-id\",\n      \"description\": \"Select a single user by ID\",\n      \"type\": \"select\",\n      \"params\": [\n        {\"name\": \"id\", \"type\": \"any\", \"required\": true}\n      ]\n    }\n  ],\n  \"aggregates\": [\n    {\n      \"name\": \"count-all\",\n      \"description\": \"Count all users\",\n      \"type\": \"aggregate\",\n      \"params\": []\n    }\n  ]\n}",{"id":546,"title":547,"titles":548,"content":549,"level":19},"/v1.0.3/cookbook/llm-integration#system-prompt-design","System Prompt Design",[519],"Provide statement metadata in your LLM system prompt: You are a database assistant. You have access to the following operations:\n\n{registry_json}\n\nWhen the user asks a question about data:\n1. Identify the appropriate operation\n2. Extract required parameters from the user's request\n3. Respond with JSON: {\"statement\": \"name\", \"type\": \"query|select|...\", \"params\": {...}}\n\nExamples:\n- \"How many users are there?\" → {\"statement\": \"count-all\", \"type\": \"aggregate\", \"params\": {}}\n- \"Find admins\" → {\"statement\": \"by-role\", \"type\": \"query\", \"params\": {\"role\": \"admin\"}}\n- \"Get user 123\" → {\"statement\": \"select-by-id\", \"type\": \"select\", \"params\": {\"id\": 123}}",{"id":551,"title":552,"titles":553,"content":554,"level":19},"/v1.0.3/cookbook/llm-integration#executing-llm-responses","Executing LLM Responses",[519],"Create a dispatcher that maps statement names to actual statements: type LLMResponse struct {\n    Statement string         `json:\"statement\"`\n    Type      string         `json:\"type\"`\n    Params    map[string]any `json:\"params\"`\n}\n\ntype StatementDispatcher struct {\n    exec       *edamame.Executor[User]\n    queries    map[string]edamame.QueryStatement\n    selects    map[string]edamame.SelectStatement\n    aggregates map[string]edamame.AggregateStatement\n}\n\nfunc NewDispatcher(exec *edamame.Executor[User]) *StatementDispatcher {\n    return &StatementDispatcher{\n        exec:       exec,\n        queries:    make(map[string]edamame.QueryStatement),\n        selects:    make(map[string]edamame.SelectStatement),\n        aggregates: make(map[string]edamame.AggregateStatement),\n    }\n}\n\nfunc (d *StatementDispatcher) RegisterQuery(stmt edamame.QueryStatement) {\n    d.queries[stmt.Name()] = stmt\n}\n\nfunc (d *StatementDispatcher) RegisterSelect(stmt edamame.SelectStatement) {\n    d.selects[stmt.Name()] = stmt\n}\n\nfunc (d *StatementDispatcher) RegisterAggregate(stmt edamame.AggregateStatement) {\n    d.aggregates[stmt.Name()] = stmt\n}\n\nfunc (d *StatementDispatcher) Execute(ctx context.Context, resp LLMResponse) (any, error) {\n    switch resp.Type {\n    case \"query\":\n        stmt, ok := d.queries[resp.Statement]\n        if !ok {\n            return nil, fmt.Errorf(\"unknown query: %s\", resp.Statement)\n        }\n        return d.exec.ExecQuery(ctx, stmt, resp.Params)\n    case \"select\":\n        stmt, ok := d.selects[resp.Statement]\n        if !ok {\n            return nil, fmt.Errorf(\"unknown select: %s\", resp.Statement)\n        }\n        return d.exec.ExecSelect(ctx, stmt, resp.Params)\n    case \"aggregate\":\n        stmt, ok := d.aggregates[resp.Statement]\n        if !ok {\n            return nil, fmt.Errorf(\"unknown aggregate: %s\", resp.Statement)\n        }\n        return d.exec.ExecAggregate(ctx, stmt, resp.Params)\n    default:\n        return nil, fmt.Errorf(\"unknown type: %s\", resp.Type)\n    }\n}",{"id":556,"title":557,"titles":558,"content":559,"level":19},"/v1.0.3/cookbook/llm-integration#complete-setup","Complete Setup",[519],"// Create executor\nexec, _ := edamame.New[User](db, \"users\", postgres.New())\n\n// Create dispatcher and register statements\ndispatcher := NewDispatcher(exec)\ndispatcher.RegisterQuery(QueryAll)\ndispatcher.RegisterQuery(ByRole)\ndispatcher.RegisterQuery(ActiveAdults)\ndispatcher.RegisterSelect(SelectByID)\ndispatcher.RegisterAggregate(CountAll)\n\n// Build registry for LLM context\nregistry := NewRegistry(\"users\")\nregistry.AddQuery(QueryAll)\nregistry.AddQuery(ByRole)\nregistry.AddQuery(ActiveAdults)\nregistry.AddSelect(SelectByID)\nregistry.AddAggregate(CountAll)\n\nllmContext, _ := registry.JSON()",{"id":561,"title":562,"titles":563,"content":564,"level":19},"/v1.0.3/cookbook/llm-integration#validation","Validation",[519],"Validate LLM responses before execution: func (d *StatementDispatcher) Validate(resp LLMResponse) error {\n    switch resp.Type {\n    case \"query\":\n        stmt, ok := d.queries[resp.Statement]\n        if !ok {\n            return fmt.Errorf(\"unknown query: %s\", resp.Statement)\n        }\n        return validateParams(stmt.Params(), resp.Params)\n    case \"select\":\n        stmt, ok := d.selects[resp.Statement]\n        if !ok {\n            return fmt.Errorf(\"unknown select: %s\", resp.Statement)\n        }\n        return validateParams(stmt.Params(), resp.Params)\n    case \"aggregate\":\n        stmt, ok := d.aggregates[resp.Statement]\n        if !ok {\n            return fmt.Errorf(\"unknown aggregate: %s\", resp.Statement)\n        }\n        return validateParams(stmt.Params(), resp.Params)\n    default:\n        return fmt.Errorf(\"unknown type: %s\", resp.Type)\n    }\n}\n\nfunc validateParams(specs []edamame.ParamSpec, provided map[string]any) error {\n    for _, spec := range specs {\n        if spec.Required {\n            if _, ok := provided[spec.Name]; !ok {\n                return fmt.Errorf(\"missing required param: %s\", spec.Name)\n            }\n        }\n    }\n    return nil\n}",{"id":566,"title":567,"titles":568,"content":569,"level":19},"/v1.0.3/cookbook/llm-integration#rate-limiting","Rate Limiting",[519],"Protect against LLM-driven query floods: type RateLimitedDispatcher struct {\n    dispatcher *StatementDispatcher\n    limiter    *rate.Limiter\n}\n\nfunc NewRateLimitedDispatcher(dispatcher *StatementDispatcher, rps float64) *RateLimitedDispatcher {\n    return &RateLimitedDispatcher{\n        dispatcher: dispatcher,\n        limiter:    rate.NewLimiter(rate.Limit(rps), 10),\n    }\n}\n\nfunc (d *RateLimitedDispatcher) Execute(ctx context.Context, resp LLMResponse) (any, error) {\n    if err := d.limiter.Wait(ctx); err != nil {\n        return nil, err\n    }\n    return d.dispatcher.Execute(ctx, resp)\n}",{"id":571,"title":572,"titles":573,"content":574,"level":19},"/v1.0.3/cookbook/llm-integration#audit-logging","Audit Logging",[519],"Log LLM-driven operations: func (d *StatementDispatcher) ExecuteWithAudit(ctx context.Context, resp LLMResponse, userID string) (any, error) {\n    start := time.Now()\n\n    result, err := d.Execute(ctx, resp)\n\n    log.Printf(\"LLM execution: user=%s statement=%s type=%s params=%v duration=%v error=%v\",\n        userID,\n        resp.Statement,\n        resp.Type,\n        resp.Params,\n        time.Since(start),\n        err,\n    )\n\n    return result, err\n}",{"id":576,"title":577,"titles":578,"content":579,"level":19},"/v1.0.3/cookbook/llm-integration#example-chat-interface","Example: Chat Interface",[519],"Complete example with a simple chat interface: func HandleChat(ctx context.Context, dispatcher *StatementDispatcher, registry *StatementRegistry, llm LLMClient, userMessage string) string {\n    // 1. Get statement metadata\n    specs, _ := registry.JSON()\n\n    // 2. Build prompt\n    prompt := fmt.Sprintf(`You are a database assistant. Available operations:\n%s\n\nUser: %s\n\nRespond with JSON: {\"statement\": \"...\", \"type\": \"...\", \"params\": {...}}\nOr respond with {\"error\": \"...\"} if the request cannot be fulfilled.`, specs, userMessage)\n\n    // 3. Get LLM response\n    llmResp, err := llm.Complete(ctx, prompt)\n    if err != nil {\n        return \"I couldn't process that request.\"\n    }\n\n    // 4. Parse response\n    var resp LLMResponse\n    if err := json.Unmarshal([]byte(llmResp), &resp); err != nil {\n        return \"I couldn't understand how to query the database.\"\n    }\n\n    // 5. Validate\n    if err := dispatcher.Validate(resp); err != nil {\n        return fmt.Sprintf(\"Invalid request: %v\", err)\n    }\n\n    // 6. Execute\n    result, err := dispatcher.Execute(ctx, resp)\n    if err != nil {\n        return fmt.Sprintf(\"Query failed: %v\", err)\n    }\n\n    // 7. Format response\n    return formatResult(result)\n}",{"id":581,"title":582,"titles":583,"content":584,"level":19},"/v1.0.3/cookbook/llm-integration#security-considerations","Security Considerations",[519],"Validate all LLM outputs before executionUse parameterized queries (edamame handles this)Limit exposed statements to what's safeRate limit LLM-driven operationsAudit log all executionsNever expose raw SQL generation to LLMs html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}",{"id":586,"title":587,"titles":588,"content":589,"level":9},"/v1.0.3/reference/api","API Reference",[],"Complete API reference for the edamame package",{"id":591,"title":587,"titles":592,"content":593,"level":9},"/v1.0.3/reference/api#api-reference",[],"Complete API reference for the github.com/zoobz-io/edamame package.",{"id":595,"title":110,"titles":596,"content":597,"level":19},"/v1.0.3/reference/api#executor",[587],"The main execution context for a model type.",{"id":599,"title":600,"titles":601,"content":602,"level":40},"/v1.0.3/reference/api#new","New",[587,110],"func New[T any](db *sqlx.DB, tableName string, renderer astql.Renderer) (*Executor[T], error) Creates a new executor for type T bound to the given table with the specified SQL renderer. The db parameter can be nil for testing statement rendering without database access. Supported renderers via astql: github.com/zoobz-io/astql/pkg/postgres - PostgreSQLgithub.com/zoobz-io/astql/pkg/mariadb - MariaDBgithub.com/zoobz-io/astql/pkg/sqlite - SQLitegithub.com/zoobz-io/astql/pkg/mssql - SQL Server import \"github.com/zoobz-io/astql/pkg/postgres\" // or mariadb, sqlite, mssql\n\nexec, err := edamame.New[User](db, \"users\", postgres.New())",{"id":604,"title":605,"titles":606,"content":34,"level":19},"/v1.0.3/reference/api#statement-constructors","Statement Constructors",[587],{"id":608,"title":609,"titles":610,"content":611,"level":40},"/v1.0.3/reference/api#newquerystatement","NewQueryStatement",[587,605],"func NewQueryStatement(name, description string, spec QuerySpec, tags ...string) QueryStatement Creates a typed query statement for multi-record retrieval. Parameters are automatically derived from the spec.",{"id":613,"title":614,"titles":615,"content":616,"level":40},"/v1.0.3/reference/api#newselectstatement","NewSelectStatement",[587,605],"func NewSelectStatement(name, description string, spec SelectSpec, tags ...string) SelectStatement Creates a typed select statement for single-record retrieval.",{"id":618,"title":619,"titles":620,"content":621,"level":40},"/v1.0.3/reference/api#newupdatestatement","NewUpdateStatement",[587,605],"func NewUpdateStatement(name, description string, spec UpdateSpec, tags ...string) UpdateStatement Creates a typed update statement for modifications.",{"id":623,"title":624,"titles":625,"content":626,"level":40},"/v1.0.3/reference/api#newdeletestatement","NewDeleteStatement",[587,605],"func NewDeleteStatement(name, description string, spec DeleteSpec, tags ...string) DeleteStatement Creates a typed delete statement for deletions.",{"id":628,"title":629,"titles":630,"content":631,"level":40},"/v1.0.3/reference/api#newaggregatestatement","NewAggregateStatement",[587,605],"func NewAggregateStatement(name, description string, fn AggregateFunc, spec AggregateSpec, tags ...string) AggregateStatement Creates a typed aggregate statement for COUNT, SUM, AVG, MIN, MAX operations.",{"id":633,"title":27,"titles":634,"content":635,"level":19},"/v1.0.3/reference/api#statement-types",[587],"All statement types share common methods: func (s Statement) ID() uuid.UUID      // Unique identifier\nfunc (s Statement) Name() string       // Human-readable name\nfunc (s Statement) Description() string // What the statement does\nfunc (s Statement) Params() []ParamSpec // Required parameters\nfunc (s Statement) Tags() []string     // Optional categorization tags",{"id":637,"title":638,"titles":639,"content":640,"level":40},"/v1.0.3/reference/api#querystatement","QueryStatement",[587,27],"For multi-record retrieval operations.",{"id":642,"title":643,"titles":644,"content":645,"level":40},"/v1.0.3/reference/api#selectstatement","SelectStatement",[587,27],"For single-record retrieval operations.",{"id":647,"title":648,"titles":649,"content":650,"level":40},"/v1.0.3/reference/api#updatestatement","UpdateStatement",[587,27],"For modification operations.",{"id":652,"title":653,"titles":654,"content":655,"level":40},"/v1.0.3/reference/api#deletestatement","DeleteStatement",[587,27],"For deletion operations.",{"id":657,"title":658,"titles":659,"content":660,"level":40},"/v1.0.3/reference/api#aggregatestatement","AggregateStatement",[587,27],"For aggregate operations (COUNT, SUM, AVG, MIN, MAX).",{"id":662,"title":663,"titles":664,"content":34,"level":19},"/v1.0.3/reference/api#executor-methods","Executor Methods",[587],{"id":666,"title":667,"titles":668,"content":34,"level":40},"/v1.0.3/reference/api#builder-access","Builder Access",[587,663],{"id":670,"title":671,"titles":672,"content":673,"level":674},"/v1.0.3/reference/api#query","Query",[587,663,667],"func (e *Executor[T]) Query(stmt QueryStatement) (*soy.Query[T], error) Returns a soy Query builder for the statement.",4,{"id":676,"title":677,"titles":678,"content":679,"level":674},"/v1.0.3/reference/api#select","Select",[587,663,667],"func (e *Executor[T]) Select(stmt SelectStatement) (*soy.Select[T], error) Returns a soy Select builder for the statement.",{"id":681,"title":682,"titles":683,"content":684,"level":674},"/v1.0.3/reference/api#update","Update",[587,663,667],"func (e *Executor[T]) Update(stmt UpdateStatement) (*soy.Update[T], error) Returns a soy Update builder for the statement.",{"id":686,"title":687,"titles":688,"content":689,"level":674},"/v1.0.3/reference/api#delete","Delete",[587,663,667],"func (e *Executor[T]) Delete(stmt DeleteStatement) (*soy.Delete[T], error) Returns a soy Delete builder for the statement.",{"id":691,"title":692,"titles":693,"content":694,"level":674},"/v1.0.3/reference/api#aggregate","Aggregate",[587,663,667],"func (e *Executor[T]) Aggregate(stmt AggregateStatement) *soy.Aggregate[T] Returns a soy Aggregate builder for the statement.",{"id":696,"title":697,"titles":698,"content":699,"level":674},"/v1.0.3/reference/api#insert","Insert",[587,663,667],"func (e *Executor[T]) Insert() *soy.Create[T] Returns a soy Create builder for inserts.",{"id":701,"title":702,"titles":703,"content":704,"level":674},"/v1.0.3/reference/api#compound","Compound",[587,663,667],"func (e *Executor[T]) Compound(spec CompoundQuerySpec) (*soy.Compound[T], error) Returns a soy Compound builder for set operations (UNION, INTERSECT, EXCEPT).",{"id":706,"title":707,"titles":708,"content":34,"level":40},"/v1.0.3/reference/api#execution-methods","Execution Methods",[587,663],{"id":710,"title":711,"titles":712,"content":713,"level":674},"/v1.0.3/reference/api#execquery-execquerytx","ExecQuery / ExecQueryTx",[587,663,707],"func (e *Executor[T]) ExecQuery(ctx context.Context, stmt QueryStatement, params map[string]any) ([]*T, error)\nfunc (e *Executor[T]) ExecQueryTx(ctx context.Context, tx *sqlx.Tx, stmt QueryStatement, params map[string]any) ([]*T, error) Executes a query statement, returning multiple records.",{"id":715,"title":716,"titles":717,"content":718,"level":674},"/v1.0.3/reference/api#execselect-execselecttx","ExecSelect / ExecSelectTx",[587,663,707],"func (e *Executor[T]) ExecSelect(ctx context.Context, stmt SelectStatement, params map[string]any) (*T, error)\nfunc (e *Executor[T]) ExecSelectTx(ctx context.Context, tx *sqlx.Tx, stmt SelectStatement, params map[string]any) (*T, error) Executes a select statement, returning a single record.",{"id":720,"title":721,"titles":722,"content":723,"level":674},"/v1.0.3/reference/api#execupdate-execupdatetx","ExecUpdate / ExecUpdateTx",[587,663,707],"func (e *Executor[T]) ExecUpdate(ctx context.Context, stmt UpdateStatement, params map[string]any) (*T, error)\nfunc (e *Executor[T]) ExecUpdateTx(ctx context.Context, tx *sqlx.Tx, stmt UpdateStatement, params map[string]any) (*T, error) Executes an update statement, returning the updated record.",{"id":725,"title":726,"titles":727,"content":728,"level":674},"/v1.0.3/reference/api#execdelete-execdeletetx","ExecDelete / ExecDeleteTx",[587,663,707],"func (e *Executor[T]) ExecDelete(ctx context.Context, stmt DeleteStatement, params map[string]any) (int64, error)\nfunc (e *Executor[T]) ExecDeleteTx(ctx context.Context, tx *sqlx.Tx, stmt DeleteStatement, params map[string]any) (int64, error) Executes a delete statement, returning the count of deleted rows.",{"id":730,"title":731,"titles":732,"content":733,"level":674},"/v1.0.3/reference/api#execaggregate-execaggregatetx","ExecAggregate / ExecAggregateTx",[587,663,707],"func (e *Executor[T]) ExecAggregate(ctx context.Context, stmt AggregateStatement, params map[string]any) (float64, error)\nfunc (e *Executor[T]) ExecAggregateTx(ctx context.Context, tx *sqlx.Tx, stmt AggregateStatement, params map[string]any) (float64, error) Executes an aggregate statement, returning the result.",{"id":735,"title":736,"titles":737,"content":738,"level":674},"/v1.0.3/reference/api#execinsert-execinserttx","ExecInsert / ExecInsertTx",[587,663,707],"func (e *Executor[T]) ExecInsert(ctx context.Context, record *T) (*T, error)\nfunc (e *Executor[T]) ExecInsertTx(ctx context.Context, tx *sqlx.Tx, record *T) (*T, error) Inserts a record, returning it with generated fields populated.",{"id":740,"title":741,"titles":742,"content":743,"level":674},"/v1.0.3/reference/api#execcompound-execcompoundtx","ExecCompound / ExecCompoundTx",[587,663,707],"func (e *Executor[T]) ExecCompound(ctx context.Context, spec CompoundQuerySpec, params map[string]any) ([]*T, error)\nfunc (e *Executor[T]) ExecCompoundTx(ctx context.Context, tx *sqlx.Tx, spec CompoundQuerySpec, params map[string]any) ([]*T, error) Executes a compound query (UNION, INTERSECT, EXCEPT), returning multiple records.",{"id":745,"title":746,"titles":747,"content":34,"level":40},"/v1.0.3/reference/api#batch-execution","Batch Execution",[587,663],{"id":749,"title":750,"titles":751,"content":752,"level":674},"/v1.0.3/reference/api#execinsertbatch-execinsertbatchtx","ExecInsertBatch / ExecInsertBatchTx",[587,663,746],"func (e *Executor[T]) ExecInsertBatch(ctx context.Context, records []*T) (int64, error)\nfunc (e *Executor[T]) ExecInsertBatchTx(ctx context.Context, tx *sqlx.Tx, records []*T) (int64, error) Inserts multiple records, returning the count.",{"id":754,"title":755,"titles":756,"content":757,"level":674},"/v1.0.3/reference/api#execupdatebatch-execupdatebatchtx","ExecUpdateBatch / ExecUpdateBatchTx",[587,663,746],"func (e *Executor[T]) ExecUpdateBatch(ctx context.Context, stmt UpdateStatement, batchParams []map[string]any) (int64, error)\nfunc (e *Executor[T]) ExecUpdateBatchTx(ctx context.Context, tx *sqlx.Tx, stmt UpdateStatement, batchParams []map[string]any) (int64, error) Executes an update statement with multiple parameter sets.",{"id":759,"title":760,"titles":761,"content":762,"level":674},"/v1.0.3/reference/api#execdeletebatch-execdeletebatchtx","ExecDeleteBatch / ExecDeleteBatchTx",[587,663,746],"func (e *Executor[T]) ExecDeleteBatch(ctx context.Context, stmt DeleteStatement, batchParams []map[string]any) (int64, error)\nfunc (e *Executor[T]) ExecDeleteBatchTx(ctx context.Context, tx *sqlx.Tx, stmt DeleteStatement, batchParams []map[string]any) (int64, error) Executes a delete statement with multiple parameter sets.",{"id":764,"title":765,"titles":766,"content":767,"level":40},"/v1.0.3/reference/api#type-erased-execution-atom","Type-Erased Execution (Atom)",[587,663],"These methods return results as atom.Atom types, enabling type-erased execution where the concrete type T is not known at consumption time. Useful for dynamic query handling and LLM-driven database operations.",{"id":769,"title":770,"titles":771,"content":772,"level":674},"/v1.0.3/reference/api#execqueryatom","ExecQueryAtom",[587,663,765],"func (e *Executor[T]) ExecQueryAtom(ctx context.Context, stmt QueryStatement, params map[string]any) ([]*atom.Atom, error) Executes a query statement and returns results as Atoms.",{"id":774,"title":775,"titles":776,"content":777,"level":674},"/v1.0.3/reference/api#execselectatom","ExecSelectAtom",[587,663,765],"func (e *Executor[T]) ExecSelectAtom(ctx context.Context, stmt SelectStatement, params map[string]any) (*atom.Atom, error) Executes a select statement and returns the result as an Atom.",{"id":779,"title":780,"titles":781,"content":782,"level":674},"/v1.0.3/reference/api#execinsertatom","ExecInsertAtom",[587,663,765],"func (e *Executor[T]) ExecInsertAtom(ctx context.Context, params map[string]any) (*atom.Atom, error) Executes an insert using parameter map and returns the result as an Atom.",{"id":784,"title":785,"titles":786,"content":34,"level":40},"/v1.0.3/reference/api#rendering","Rendering",[587,663],{"id":788,"title":789,"titles":790,"content":791,"level":674},"/v1.0.3/reference/api#rendercompound","RenderCompound",[587,663,785],"func (e *Executor[T]) RenderCompound(spec CompoundQuerySpec) (string, error) Renders a compound query to SQL for inspection or debugging.",{"id":793,"title":794,"titles":795,"content":34,"level":40},"/v1.0.3/reference/api#other","Other",[587,663],{"id":797,"title":241,"titles":798,"content":799,"level":674},"/v1.0.3/reference/api#soy",[587,663,794],"func (e *Executor[T]) Soy() *soy.Soy[T] Returns the underlying soy instance for direct builder access.",{"id":801,"title":802,"titles":803,"content":804,"level":674},"/v1.0.3/reference/api#tablename","TableName",[587,663,794],"func (e *Executor[T]) TableName() string Returns the table name.",{"id":806,"title":807,"titles":808,"content":34,"level":19},"/v1.0.3/reference/api#spec-types","Spec Types",[587],{"id":810,"title":144,"titles":811,"content":812,"level":40},"/v1.0.3/reference/api#queryspec",[587,807],"type QuerySpec struct {\n    Fields      []string\n    SelectExprs []SelectExprSpec  // Expression-based SELECT (functions, aggregates)\n    Where       []ConditionSpec\n    OrderBy     []OrderBySpec\n    GroupBy     []string\n    Having      []ConditionSpec\n    HavingAgg   []HavingAggSpec\n    Limit       *int\n    LimitParam  string            // Parameterized LIMIT\n    Offset      *int\n    OffsetParam string            // Parameterized OFFSET\n    Distinct    bool\n    DistinctOn  []string\n    ForLocking  string\n}",{"id":814,"title":149,"titles":815,"content":816,"level":40},"/v1.0.3/reference/api#selectspec",[587,807],"type SelectSpec struct {\n    Fields      []string\n    SelectExprs []SelectExprSpec  // Expression-based SELECT (functions, aggregates)\n    Where       []ConditionSpec\n    OrderBy     []OrderBySpec\n    GroupBy     []string\n    Having      []ConditionSpec\n    HavingAgg   []HavingAggSpec\n    Limit       *int\n    LimitParam  string            // Parameterized LIMIT\n    Offset      *int\n    OffsetParam string            // Parameterized OFFSET\n    Distinct    bool\n    DistinctOn  []string\n    ForLocking  string\n}",{"id":818,"title":154,"titles":819,"content":820,"level":40},"/v1.0.3/reference/api#updatespec",[587,807],"type UpdateSpec struct {\n    Set   map[string]string  // field -> param\n    Where []ConditionSpec\n}",{"id":822,"title":159,"titles":823,"content":824,"level":40},"/v1.0.3/reference/api#deletespec",[587,807],"type DeleteSpec struct {\n    Where []ConditionSpec\n}",{"id":826,"title":164,"titles":827,"content":828,"level":40},"/v1.0.3/reference/api#aggregatespec",[587,807],"type AggregateSpec struct {\n    Field string\n    Where []ConditionSpec\n}",{"id":830,"title":831,"titles":832,"content":833,"level":40},"/v1.0.3/reference/api#conditionspec","ConditionSpec",[587,807],"type ConditionSpec struct {\n    Field      string\n    Operator   string\n    Param      string\n    IsNull     bool\n    Logic      string           // \"AND\" or \"OR\" for groups\n    Group      []ConditionSpec  // Nested conditions\n    Between    bool             // Use BETWEEN with LowParam/HighParam\n    NotBetween bool             // Use NOT BETWEEN with LowParam/HighParam\n    LowParam   string           // Lower bound param for BETWEEN\n    HighParam  string           // Upper bound param for BETWEEN\n    RightField string           // For field-to-field comparisons (WHERE a.field = b.field)\n}",{"id":835,"title":836,"titles":837,"content":838,"level":674},"/v1.0.3/reference/api#helper-methods","Helper Methods",[587,807,831],"func (c ConditionSpec) IsGroup() bool           // Returns true if this is a grouped condition\nfunc (c ConditionSpec) IsBetween() bool         // Returns true if Between is set\nfunc (c ConditionSpec) IsNotBetween() bool      // Returns true if NotBetween is set\nfunc (c ConditionSpec) IsFieldComparison() bool // Returns true if RightField is set",{"id":840,"title":841,"titles":842,"content":843,"level":40},"/v1.0.3/reference/api#orderbyspec","OrderBySpec",[587,807],"type OrderBySpec struct {\n    Field     string\n    Direction string  // \"asc\" or \"desc\"\n    Nulls     string  // \"first\" or \"last\"\n    Operator  string  // For expressions (e.g., \"\u003C->\")\n    Param     string  // For expression parameters\n}",{"id":845,"title":846,"titles":847,"content":848,"level":40},"/v1.0.3/reference/api#paramspec","ParamSpec",[587,807],"type ParamSpec struct {\n    Name        string\n    Type        string\n    Required    bool\n    Default     any\n    Description string\n}",{"id":850,"title":851,"titles":852,"content":853,"level":40},"/v1.0.3/reference/api#selectexprspec","SelectExprSpec",[587,807],"Defines expression-based SELECT columns (functions, aggregates, casts). type SelectExprSpec struct {\n    Func     string          // Function name (see supported functions below)\n    Field    string          // Primary field for single-field functions\n    Fields   []string        // Multiple fields for multi-field functions (concat)\n    Params   []string        // Additional parameters (substring positions, power exponent)\n    CastType string          // Target type for cast operations\n    Filter   *ConditionSpec  // Filter clause for aggregate functions\n    Alias    string          // Required: column alias in result\n} Supported Functions: CategoryFunctionsStringupper, lower, length, trim, ltrim, rtrim, substring, replace, concatMathabs, ceil, floor, round, sqrt, powerDate/Timenow, current_date, current_time, current_timestampTypecastAggregatecount, count_star, count_distinct, sum, avg, min, maxConditionalcoalesce, nullif",{"id":855,"title":856,"titles":857,"content":858,"level":40},"/v1.0.3/reference/api#havingaggspec","HavingAggSpec",[587,807],"Defines aggregate conditions for HAVING clauses. type HavingAggSpec struct {\n    Func     string  // Aggregate function: \"count\", \"sum\", \"avg\", \"min\", \"max\"\n    Field    string  // Field to aggregate\n    Operator string  // Comparison operator\n    Param    string  // Parameter name for comparison value\n}",{"id":860,"title":861,"titles":862,"content":863,"level":40},"/v1.0.3/reference/api#compoundqueryspec","CompoundQuerySpec",[587,807],"Defines compound queries using set operations (UNION, INTERSECT, EXCEPT). type CompoundQuerySpec struct {\n    Base     QuerySpec         // The base query\n    Operands []CompoundOperand // Set operations with additional queries\n    OrderBy  []OrderBySpec     // Final ORDER BY (applies to combined result)\n    Limit    *int              // Final LIMIT\n    Offset   *int              // Final OFFSET\n}",{"id":865,"title":866,"titles":867,"content":868,"level":40},"/v1.0.3/reference/api#compoundoperand","CompoundOperand",[587,807],"Defines a set operation in a compound query. type CompoundOperand struct {\n    Operation string    // \"union\", \"union_all\", \"intersect\", \"intersect_all\", \"except\", \"except_all\"\n    Query     QuerySpec // The query to combine\n}",{"id":870,"title":871,"titles":872,"content":873,"level":19},"/v1.0.3/reference/api#aggregatefunc","AggregateFunc",[587],"type AggregateFunc string\n\nconst (\n    AggCount AggregateFunc = \"COUNT\"\n    AggSum   AggregateFunc = \"SUM\"\n    AggAvg   AggregateFunc = \"AVG\"\n    AggMin   AggregateFunc = \"MIN\"\n    AggMax   AggregateFunc = \"MAX\"\n)",{"id":875,"title":876,"titles":877,"content":878,"level":19},"/v1.0.3/reference/api#event-keys","Event Keys",[587],"var (\n    KeyTable    = capitan.NewStringKey(\"table\")\n    KeyError    = capitan.NewStringKey(\"error\")\n    KeyDuration = capitan.NewDurationKey(\"duration\")\n)",{"id":880,"title":881,"titles":882,"content":883,"level":19},"/v1.0.3/reference/api#signals","Signals",[587],"var (\n    ExecutorCreated = capitan.NewSignal(\"edamame.executor.created\", \"Executor instance created\")\n) Hook for monitoring: capitan.Hook(edamame.ExecutorCreated, func(ctx context.Context, e *capitan.Event) {\n    table, _ := edamame.KeyTable.From(e)\n    log.Printf(\"Executor created for table: %s\", table)\n})",{"id":885,"title":115,"titles":886,"content":887,"level":19},"/v1.0.3/reference/api#struct-tags",[587],"Edamame uses struct tags to understand your model: type User struct {\n    ID    int    `db:\"id\" type:\"integer\" constraints:\"primarykey\"`\n    Email string `db:\"email\" type:\"text\" constraints:\"notnull,unique\"`\n    Name  string `db:\"name\" type:\"text\"`\n    Age   *int   `db:\"age\" type:\"integer\"`\n} TagPurposeExampledbColumn namedb:\"user_id\"typeSQL typetype:\"text\", type:\"integer\"constraintsColumn constraintsconstraints:\"primarykey,notnull\" html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sfm-E, html code.shiki .sfm-E{--shiki-default:var(--shiki-variable)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}",[889],{"title":890,"path":891,"stem":892,"children":893,"page":907},"V103","/v1.0.3","v1.0.3",[894,896,908,917,924],{"title":6,"path":5,"stem":895,"description":8},"v1.0.3/1.overview",{"title":897,"path":898,"stem":899,"children":900,"page":907},"Learn","/v1.0.3/learn","v1.0.3/2.learn",[901,903,905],{"title":58,"path":57,"stem":902,"description":60},"v1.0.3/2.learn/1.quickstart",{"title":101,"path":100,"stem":904,"description":103},"v1.0.3/2.learn/2.concepts",{"title":16,"path":218,"stem":906,"description":220},"v1.0.3/2.learn/3.architecture",false,{"title":909,"path":910,"stem":911,"children":912,"page":907},"Guides","/v1.0.3/guides","v1.0.3/3.guides",[913,915],{"title":120,"path":274,"stem":914,"description":276},"v1.0.3/3.guides/1.statements",{"title":456,"path":455,"stem":916,"description":458},"v1.0.3/3.guides/2.testing",{"title":918,"path":919,"stem":920,"children":921,"page":907},"Cookbook","/v1.0.3/cookbook","v1.0.3/4.cookbook",[922],{"title":519,"path":518,"stem":923,"description":521},"v1.0.3/4.cookbook/1.llm-integration",{"title":925,"path":926,"stem":927,"children":928,"page":907},"Reference","/v1.0.3/reference","v1.0.3/5.reference",[929],{"title":587,"path":586,"stem":930,"description":589},"v1.0.3/5.reference/1.api",[932],{"title":890,"path":891,"stem":892,"children":933,"page":907},[934,935,940,944,947],{"title":6,"path":5,"stem":895},{"title":897,"path":898,"stem":899,"children":936,"page":907},[937,938,939],{"title":58,"path":57,"stem":902},{"title":101,"path":100,"stem":904},{"title":16,"path":218,"stem":906},{"title":909,"path":910,"stem":911,"children":941,"page":907},[942,943],{"title":120,"path":274,"stem":914},{"title":456,"path":455,"stem":916},{"title":918,"path":919,"stem":920,"children":945,"page":907},[946],{"title":519,"path":518,"stem":923},{"title":925,"path":926,"stem":927,"children":948,"page":907},[949],{"title":587,"path":586,"stem":930},[951,2753,3194],{"id":952,"title":953,"body":954,"description":34,"extension":2746,"icon":2747,"meta":2748,"navigation":1117,"path":2749,"seo":2750,"stem":2751,"__hash__":2752},"resources/readme.md","README",{"type":955,"value":956,"toc":2731},"minimark",[957,961,1029,1032,1035,1040,1043,1488,1491,1495,1512,1520,1524,2344,2348,2444,2448,2491,2495,2501,2504,2650,2653,2657,2665,2669,2683,2686,2697,2700,2706,2710,2718,2721,2727],[958,959,960],"h1",{"id":960},"edamame",[962,963,964,975,983,991,999,1007,1014,1021],"p",{},[965,966,970],"a",{"href":967,"rel":968},"https://github.com/zoobz-io/edamame/actions/workflows/ci.yml",[969],"nofollow",[971,972],"img",{"alt":973,"src":974},"CI Status","https://github.com/zoobz-io/edamame/workflows/CI/badge.svg",[965,976,979],{"href":977,"rel":978},"https://codecov.io/gh/zoobz-io/edamame",[969],[971,980],{"alt":981,"src":982},"codecov","https://codecov.io/gh/zoobz-io/edamame/graph/badge.svg?branch=main",[965,984,987],{"href":985,"rel":986},"https://goreportcard.com/report/github.com/zoobz-io/edamame",[969],[971,988],{"alt":989,"src":990},"Go Report Card","https://goreportcard.com/badge/github.com/zoobz-io/edamame",[965,992,995],{"href":993,"rel":994},"https://github.com/zoobz-io/edamame/security/code-scanning",[969],[971,996],{"alt":997,"src":998},"CodeQL","https://github.com/zoobz-io/edamame/workflows/CodeQL/badge.svg",[965,1000,1003],{"href":1001,"rel":1002},"https://pkg.go.dev/github.com/zoobz-io/edamame",[969],[971,1004],{"alt":1005,"src":1006},"Go Reference","https://pkg.go.dev/badge/github.com/zoobz-io/edamame.svg",[965,1008,1010],{"href":1009},"LICENSE",[971,1011],{"alt":1012,"src":1013},"License","https://img.shields.io/github/license/zoobz-io/edamame",[965,1015,1017],{"href":1016},"go.mod",[971,1018],{"alt":1019,"src":1020},"Go Version","https://img.shields.io/github/go-mod/go-version/zoobz-io/edamame",[965,1022,1025],{"href":1023,"rel":1024},"https://github.com/zoobz-io/edamame/releases",[969],[971,1026],{"alt":1027,"src":1028},"Release","https://img.shields.io/github/v/release/zoobz-io/edamame",[962,1030,1031],{},"Statement-driven query exec for Go.",[962,1033,1034],{},"Define database queries as typed statements, execute them without magic strings.",[1036,1037,1039],"h2",{"id":1038},"queries-as-data","Queries as Data",[962,1041,1042],{},"Edamame treats queries as specs—pure data structures wrapped in typed statements.",[1044,1045,1049],"pre",{"className":1046,"code":1047,"language":1048,"meta":34,"style":34},"language-go shiki shiki-themes","// Define statements as package-level variables\nvar (\n    QueryAll = edamame.NewQueryStatement(\"query-all\", \"Query all users\", edamame.QuerySpec{})\n\n    ByStatus = edamame.NewQueryStatement(\"by-status\", \"Query users by status\", edamame.QuerySpec{\n        Where:   []edamame.ConditionSpec{{Field: \"status\", Operator: \"=\", Param: \"status\"}},\n        OrderBy: []edamame.OrderBySpec{{Field: \"created_at\", Direction: \"desc\"}},\n        Limit:   ptr(50),\n    })\n\n    SelectByID = edamame.NewSelectStatement(\"select-by-id\", \"Select user by ID\", edamame.SelectSpec{\n        Where: []edamame.ConditionSpec{{Field: \"id\", Operator: \"=\", Param: \"id\"}},\n    })\n)\n\n// Execute with type safety\nusers, _ := exec.ExecQuery(ctx, ByStatus, map[string]any{\"status\": \"active\"})\nuser, _ := exec.ExecSelect(ctx, SelectByID, map[string]any{\"id\": 123})\n","go",[1050,1051,1052,1060,1070,1113,1119,1154,1206,1244,1264,1270,1275,1309,1351,1356,1362,1367,1373,1436],"code",{"__ignoreMap":34},[1053,1054,1056],"span",{"class":1055,"line":9},"line",[1053,1057,1059],{"class":1058},"sLkEo","// Define statements as package-level variables\n",[1053,1061,1062,1066],{"class":1055,"line":19},[1053,1063,1065],{"class":1064},"sUt3r","var",[1053,1067,1069],{"class":1068},"sq5bi"," (\n",[1053,1071,1072,1076,1079,1082,1085,1088,1091,1095,1098,1101,1103,1106,1108,1110],{"class":1055,"line":40},[1053,1073,1075],{"class":1074},"sh8_p","    QueryAll",[1053,1077,1078],{"class":1074}," =",[1053,1080,1081],{"class":1074}," edamame",[1053,1083,1084],{"class":1068},".",[1053,1086,609],{"class":1087},"s5klm",[1053,1089,1090],{"class":1068},"(",[1053,1092,1094],{"class":1093},"sxAnc","\"query-all\"",[1053,1096,1097],{"class":1068},",",[1053,1099,1100],{"class":1093}," \"Query all users\"",[1053,1102,1097],{"class":1068},[1053,1104,1081],{"class":1105},"sYBwO",[1053,1107,1084],{"class":1068},[1053,1109,144],{"class":1105},[1053,1111,1112],{"class":1068},"{})\n",[1053,1114,1115],{"class":1055,"line":674},[1053,1116,1118],{"emptyLinePlaceholder":1117},true,"\n",[1053,1120,1122,1125,1127,1129,1131,1133,1135,1138,1140,1143,1145,1147,1149,1151],{"class":1055,"line":1121},5,[1053,1123,1124],{"class":1074},"    ByStatus",[1053,1126,1078],{"class":1074},[1053,1128,1081],{"class":1074},[1053,1130,1084],{"class":1068},[1053,1132,609],{"class":1087},[1053,1134,1090],{"class":1068},[1053,1136,1137],{"class":1093},"\"by-status\"",[1053,1139,1097],{"class":1068},[1053,1141,1142],{"class":1093}," \"Query users by status\"",[1053,1144,1097],{"class":1068},[1053,1146,1081],{"class":1105},[1053,1148,1084],{"class":1068},[1053,1150,144],{"class":1105},[1053,1152,1153],{"class":1068},"{\n",[1053,1155,1157,1161,1164,1167,1169,1171,1173,1176,1179,1181,1184,1186,1189,1191,1194,1196,1199,1201,1203],{"class":1055,"line":1156},6,[1053,1158,1160],{"class":1159},"sBGCq","        Where",[1053,1162,1163],{"class":1068},":",[1053,1165,1166],{"class":1068},"   []",[1053,1168,960],{"class":1105},[1053,1170,1084],{"class":1068},[1053,1172,831],{"class":1105},[1053,1174,1175],{"class":1068},"{{",[1053,1177,1178],{"class":1159},"Field",[1053,1180,1163],{"class":1068},[1053,1182,1183],{"class":1093}," \"status\"",[1053,1185,1097],{"class":1068},[1053,1187,1188],{"class":1159}," Operator",[1053,1190,1163],{"class":1068},[1053,1192,1193],{"class":1093}," \"=\"",[1053,1195,1097],{"class":1068},[1053,1197,1198],{"class":1159}," Param",[1053,1200,1163],{"class":1068},[1053,1202,1183],{"class":1093},[1053,1204,1205],{"class":1068},"}},\n",[1053,1207,1209,1212,1214,1217,1219,1221,1223,1225,1227,1229,1232,1234,1237,1239,1242],{"class":1055,"line":1208},7,[1053,1210,1211],{"class":1159},"        OrderBy",[1053,1213,1163],{"class":1068},[1053,1215,1216],{"class":1068}," []",[1053,1218,960],{"class":1105},[1053,1220,1084],{"class":1068},[1053,1222,841],{"class":1105},[1053,1224,1175],{"class":1068},[1053,1226,1178],{"class":1159},[1053,1228,1163],{"class":1068},[1053,1230,1231],{"class":1093}," \"created_at\"",[1053,1233,1097],{"class":1068},[1053,1235,1236],{"class":1159}," Direction",[1053,1238,1163],{"class":1068},[1053,1240,1241],{"class":1093}," \"desc\"",[1053,1243,1205],{"class":1068},[1053,1245,1247,1250,1252,1255,1257,1261],{"class":1055,"line":1246},8,[1053,1248,1249],{"class":1159},"        Limit",[1053,1251,1163],{"class":1068},[1053,1253,1254],{"class":1087},"   ptr",[1053,1256,1090],{"class":1068},[1053,1258,1260],{"class":1259},"sMAmT","50",[1053,1262,1263],{"class":1068},"),\n",[1053,1265,1267],{"class":1055,"line":1266},9,[1053,1268,1269],{"class":1068},"    })\n",[1053,1271,1273],{"class":1055,"line":1272},10,[1053,1274,1118],{"emptyLinePlaceholder":1117},[1053,1276,1278,1281,1283,1285,1287,1289,1291,1294,1296,1299,1301,1303,1305,1307],{"class":1055,"line":1277},11,[1053,1279,1280],{"class":1074},"    SelectByID",[1053,1282,1078],{"class":1074},[1053,1284,1081],{"class":1074},[1053,1286,1084],{"class":1068},[1053,1288,614],{"class":1087},[1053,1290,1090],{"class":1068},[1053,1292,1293],{"class":1093},"\"select-by-id\"",[1053,1295,1097],{"class":1068},[1053,1297,1298],{"class":1093}," \"Select user by ID\"",[1053,1300,1097],{"class":1068},[1053,1302,1081],{"class":1105},[1053,1304,1084],{"class":1068},[1053,1306,149],{"class":1105},[1053,1308,1153],{"class":1068},[1053,1310,1312,1314,1316,1318,1320,1322,1324,1326,1328,1330,1333,1335,1337,1339,1341,1343,1345,1347,1349],{"class":1055,"line":1311},12,[1053,1313,1160],{"class":1159},[1053,1315,1163],{"class":1068},[1053,1317,1216],{"class":1068},[1053,1319,960],{"class":1105},[1053,1321,1084],{"class":1068},[1053,1323,831],{"class":1105},[1053,1325,1175],{"class":1068},[1053,1327,1178],{"class":1159},[1053,1329,1163],{"class":1068},[1053,1331,1332],{"class":1093}," \"id\"",[1053,1334,1097],{"class":1068},[1053,1336,1188],{"class":1159},[1053,1338,1163],{"class":1068},[1053,1340,1193],{"class":1093},[1053,1342,1097],{"class":1068},[1053,1344,1198],{"class":1159},[1053,1346,1163],{"class":1068},[1053,1348,1332],{"class":1093},[1053,1350,1205],{"class":1068},[1053,1352,1354],{"class":1055,"line":1353},13,[1053,1355,1269],{"class":1068},[1053,1357,1359],{"class":1055,"line":1358},14,[1053,1360,1361],{"class":1068},")\n",[1053,1363,1365],{"class":1055,"line":1364},15,[1053,1366,1118],{"emptyLinePlaceholder":1117},[1053,1368,1370],{"class":1055,"line":1369},16,[1053,1371,1372],{"class":1058},"// Execute with type safety\n",[1053,1374,1376,1379,1381,1384,1387,1390,1392,1395,1397,1400,1402,1405,1407,1410,1413,1416,1419,1422,1425,1428,1430,1433],{"class":1055,"line":1375},17,[1053,1377,1378],{"class":1074},"users",[1053,1380,1097],{"class":1068},[1053,1382,1383],{"class":1074}," _",[1053,1385,1386],{"class":1074}," :=",[1053,1388,1389],{"class":1074}," exec",[1053,1391,1084],{"class":1068},[1053,1393,1394],{"class":1087},"ExecQuery",[1053,1396,1090],{"class":1068},[1053,1398,1399],{"class":1074},"ctx",[1053,1401,1097],{"class":1068},[1053,1403,1404],{"class":1074}," ByStatus",[1053,1406,1097],{"class":1068},[1053,1408,1409],{"class":1064}," map",[1053,1411,1412],{"class":1068},"[",[1053,1414,1415],{"class":1105},"string",[1053,1417,1418],{"class":1068},"]",[1053,1420,1421],{"class":1105},"any",[1053,1423,1424],{"class":1068},"{",[1053,1426,1427],{"class":1093},"\"status\"",[1053,1429,1163],{"class":1068},[1053,1431,1432],{"class":1093}," \"active\"",[1053,1434,1435],{"class":1068},"})\n",[1053,1437,1439,1442,1444,1446,1448,1450,1452,1455,1457,1459,1461,1464,1466,1468,1470,1472,1474,1476,1478,1481,1483,1486],{"class":1055,"line":1438},18,[1053,1440,1441],{"class":1074},"user",[1053,1443,1097],{"class":1068},[1053,1445,1383],{"class":1074},[1053,1447,1386],{"class":1074},[1053,1449,1389],{"class":1074},[1053,1451,1084],{"class":1068},[1053,1453,1454],{"class":1087},"ExecSelect",[1053,1456,1090],{"class":1068},[1053,1458,1399],{"class":1074},[1053,1460,1097],{"class":1068},[1053,1462,1463],{"class":1074}," SelectByID",[1053,1465,1097],{"class":1068},[1053,1467,1409],{"class":1064},[1053,1469,1412],{"class":1068},[1053,1471,1415],{"class":1105},[1053,1473,1418],{"class":1068},[1053,1475,1421],{"class":1105},[1053,1477,1424],{"class":1068},[1053,1479,1480],{"class":1093},"\"id\"",[1053,1482,1163],{"class":1068},[1053,1484,1485],{"class":1259}," 123",[1053,1487,1435],{"class":1068},[962,1489,1490],{},"Type-safe. No magic strings. Compile-time guarantees.",[1036,1492,1494],{"id":1493},"install","Install",[1044,1496,1500],{"className":1497,"code":1498,"language":1499,"meta":34,"style":34},"language-bash shiki shiki-themes","go get github.com/zoobz-io/edamame\n","bash",[1050,1501,1502],{"__ignoreMap":34},[1053,1503,1504,1506,1509],{"class":1055,"line":9},[1053,1505,1048],{"class":1087},[1053,1507,1508],{"class":1093}," get",[1053,1510,1511],{"class":1093}," github.com/zoobz-io/edamame\n",[962,1513,1514,1515,1084],{},"Requires Go 1.24+. Supports PostgreSQL, MariaDB, SQLite, and SQL Server via ",[965,1516,1519],{"href":1517,"rel":1518},"https://github.com/zoobz-io/astql",[969],"astql",[1036,1521,1523],{"id":1522},"quick-start","Quick Start",[1044,1525,1527],{"className":1046,"code":1526,"language":1048,"meta":34,"style":34},"package main\n\nimport (\n    \"context\"\n    \"fmt\"\n\n    \"github.com/jmoiron/sqlx\"\n    _ \"github.com/lib/pq\" // or mariadb, sqlite3, mssql driver\n    \"github.com/zoobz-io/astql/pkg/postgres\" // or mariadb, sqlite, mssql\n    \"github.com/zoobz-io/edamame\"\n)\n\ntype User struct {\n    ID     int    `db:\"id\" type:\"integer\" constraints:\"primarykey\"`\n    Email  string `db:\"email\" type:\"text\" constraints:\"notnull,unique\"`\n    Name   string `db:\"name\" type:\"text\"`\n    Status string `db:\"status\" type:\"text\"`\n}\n\n// Define statements\nvar (\n    QueryAll = edamame.NewQueryStatement(\"query-all\", \"Query all users\", edamame.QuerySpec{})\n\n    SelectByID = edamame.NewSelectStatement(\"select-by-id\", \"Select user by ID\", edamame.SelectSpec{\n        Where: []edamame.ConditionSpec{{Field: \"id\", Operator: \"=\", Param: \"id\"}},\n    })\n\n    CountAll = edamame.NewAggregateStatement(\"count-all\", \"Count all users\", edamame.AggCount, edamame.AggregateSpec{})\n\n    ActiveUsers = edamame.NewQueryStatement(\"active\", \"Query active users\", edamame.QuerySpec{\n        Where: []edamame.ConditionSpec{\n            {Field: \"status\", Operator: \"=\", Param: \"status\"},\n        },\n    })\n)\n\nfunc main() {\n    db, _ := sqlx.Connect(\"postgres\", \"postgres://localhost/mydb?sslmode=disable\")\n    ctx := context.Background()\n\n    // Create exec\n    exec, _ := edamame.New[User](db, \"users\", postgres.New())\n\n    // Execute statements\n    users, _ := exec.ExecQuery(ctx, QueryAll, nil)\n    user, _ := exec.ExecSelect(ctx, SelectByID, map[string]any{\"id\": 1})\n    count, _ := exec.ExecAggregate(ctx, CountAll, nil)\n    active, _ := exec.ExecQuery(ctx, ActiveUsers, map[string]any{\"status\": \"active\"})\n\n    fmt.Printf(\"%d users, user #1: %s, %.0f total, %d active\\n\", len(users), user.Name, count, len(active))\n}\n",[1050,1528,1529,1537,1541,1549,1554,1559,1563,1568,1580,1588,1593,1597,1601,1615,1626,1637,1648,1659,1664,1669,1675,1682,1713,1718,1749,1790,1795,1800,1843,1848,1882,1899,1930,1936,1941,1946,1951,1965,1997,2016,2021,2027,2073,2078,2084,2118,2167,2201,2250,2255,2339],{"__ignoreMap":34},[1053,1530,1531,1534],{"class":1055,"line":9},[1053,1532,1533],{"class":1064},"package",[1053,1535,1536],{"class":1105}," main\n",[1053,1538,1539],{"class":1055,"line":19},[1053,1540,1118],{"emptyLinePlaceholder":1117},[1053,1542,1543,1546],{"class":1055,"line":40},[1053,1544,1545],{"class":1064},"import",[1053,1547,1069],{"class":1548},"soy-K",[1053,1550,1551],{"class":1055,"line":674},[1053,1552,1553],{"class":1093},"    \"context\"\n",[1053,1555,1556],{"class":1055,"line":1121},[1053,1557,1558],{"class":1093},"    \"fmt\"\n",[1053,1560,1561],{"class":1055,"line":1156},[1053,1562,1118],{"emptyLinePlaceholder":1117},[1053,1564,1565],{"class":1055,"line":1208},[1053,1566,1567],{"class":1093},"    \"github.com/jmoiron/sqlx\"\n",[1053,1569,1570,1574,1577],{"class":1055,"line":1246},[1053,1571,1573],{"class":1572},"sSYET","    _",[1053,1575,1576],{"class":1093}," \"github.com/lib/pq\"",[1053,1578,1579],{"class":1058}," // or mariadb, sqlite3, mssql driver\n",[1053,1581,1582,1585],{"class":1055,"line":1266},[1053,1583,1584],{"class":1093},"    \"github.com/zoobz-io/astql/pkg/postgres\"",[1053,1586,1587],{"class":1058}," // or mariadb, sqlite, mssql\n",[1053,1589,1590],{"class":1055,"line":1272},[1053,1591,1592],{"class":1093},"    \"github.com/zoobz-io/edamame\"\n",[1053,1594,1595],{"class":1055,"line":1277},[1053,1596,1361],{"class":1548},[1053,1598,1599],{"class":1055,"line":1311},[1053,1600,1118],{"emptyLinePlaceholder":1117},[1053,1602,1603,1606,1609,1612],{"class":1055,"line":1353},[1053,1604,1605],{"class":1064},"type",[1053,1607,1608],{"class":1105}," User",[1053,1610,1611],{"class":1064}," struct",[1053,1613,1614],{"class":1068}," {\n",[1053,1616,1617,1620,1623],{"class":1055,"line":1358},[1053,1618,1619],{"class":1159},"    ID",[1053,1621,1622],{"class":1105},"     int",[1053,1624,1625],{"class":1093},"    `db:\"id\" type:\"integer\" constraints:\"primarykey\"`\n",[1053,1627,1628,1631,1634],{"class":1055,"line":1364},[1053,1629,1630],{"class":1159},"    Email",[1053,1632,1633],{"class":1105},"  string",[1053,1635,1636],{"class":1093}," `db:\"email\" type:\"text\" constraints:\"notnull,unique\"`\n",[1053,1638,1639,1642,1645],{"class":1055,"line":1369},[1053,1640,1641],{"class":1159},"    Name",[1053,1643,1644],{"class":1105},"   string",[1053,1646,1647],{"class":1093}," `db:\"name\" type:\"text\"`\n",[1053,1649,1650,1653,1656],{"class":1055,"line":1375},[1053,1651,1652],{"class":1159},"    Status",[1053,1654,1655],{"class":1105}," string",[1053,1657,1658],{"class":1093}," `db:\"status\" type:\"text\"`\n",[1053,1660,1661],{"class":1055,"line":1438},[1053,1662,1663],{"class":1068},"}\n",[1053,1665,1667],{"class":1055,"line":1666},19,[1053,1668,1118],{"emptyLinePlaceholder":1117},[1053,1670,1672],{"class":1055,"line":1671},20,[1053,1673,1674],{"class":1058},"// Define statements\n",[1053,1676,1678,1680],{"class":1055,"line":1677},21,[1053,1679,1065],{"class":1064},[1053,1681,1069],{"class":1068},[1053,1683,1685,1687,1689,1691,1693,1695,1697,1699,1701,1703,1705,1707,1709,1711],{"class":1055,"line":1684},22,[1053,1686,1075],{"class":1074},[1053,1688,1078],{"class":1074},[1053,1690,1081],{"class":1074},[1053,1692,1084],{"class":1068},[1053,1694,609],{"class":1087},[1053,1696,1090],{"class":1068},[1053,1698,1094],{"class":1093},[1053,1700,1097],{"class":1068},[1053,1702,1100],{"class":1093},[1053,1704,1097],{"class":1068},[1053,1706,1081],{"class":1105},[1053,1708,1084],{"class":1068},[1053,1710,144],{"class":1105},[1053,1712,1112],{"class":1068},[1053,1714,1716],{"class":1055,"line":1715},23,[1053,1717,1118],{"emptyLinePlaceholder":1117},[1053,1719,1721,1723,1725,1727,1729,1731,1733,1735,1737,1739,1741,1743,1745,1747],{"class":1055,"line":1720},24,[1053,1722,1280],{"class":1074},[1053,1724,1078],{"class":1074},[1053,1726,1081],{"class":1074},[1053,1728,1084],{"class":1068},[1053,1730,614],{"class":1087},[1053,1732,1090],{"class":1068},[1053,1734,1293],{"class":1093},[1053,1736,1097],{"class":1068},[1053,1738,1298],{"class":1093},[1053,1740,1097],{"class":1068},[1053,1742,1081],{"class":1105},[1053,1744,1084],{"class":1068},[1053,1746,149],{"class":1105},[1053,1748,1153],{"class":1068},[1053,1750,1752,1754,1756,1758,1760,1762,1764,1766,1768,1770,1772,1774,1776,1778,1780,1782,1784,1786,1788],{"class":1055,"line":1751},25,[1053,1753,1160],{"class":1159},[1053,1755,1163],{"class":1068},[1053,1757,1216],{"class":1068},[1053,1759,960],{"class":1105},[1053,1761,1084],{"class":1068},[1053,1763,831],{"class":1105},[1053,1765,1175],{"class":1068},[1053,1767,1178],{"class":1159},[1053,1769,1163],{"class":1068},[1053,1771,1332],{"class":1093},[1053,1773,1097],{"class":1068},[1053,1775,1188],{"class":1159},[1053,1777,1163],{"class":1068},[1053,1779,1193],{"class":1093},[1053,1781,1097],{"class":1068},[1053,1783,1198],{"class":1159},[1053,1785,1163],{"class":1068},[1053,1787,1332],{"class":1093},[1053,1789,1205],{"class":1068},[1053,1791,1793],{"class":1055,"line":1792},26,[1053,1794,1269],{"class":1068},[1053,1796,1798],{"class":1055,"line":1797},27,[1053,1799,1118],{"emptyLinePlaceholder":1117},[1053,1801,1803,1806,1808,1810,1812,1814,1816,1819,1821,1824,1826,1828,1830,1833,1835,1837,1839,1841],{"class":1055,"line":1802},28,[1053,1804,1805],{"class":1074},"    CountAll",[1053,1807,1078],{"class":1074},[1053,1809,1081],{"class":1074},[1053,1811,1084],{"class":1068},[1053,1813,629],{"class":1087},[1053,1815,1090],{"class":1068},[1053,1817,1818],{"class":1093},"\"count-all\"",[1053,1820,1097],{"class":1068},[1053,1822,1823],{"class":1093}," \"Count all users\"",[1053,1825,1097],{"class":1068},[1053,1827,1081],{"class":1074},[1053,1829,1084],{"class":1068},[1053,1831,1832],{"class":1074},"AggCount",[1053,1834,1097],{"class":1068},[1053,1836,1081],{"class":1105},[1053,1838,1084],{"class":1068},[1053,1840,164],{"class":1105},[1053,1842,1112],{"class":1068},[1053,1844,1846],{"class":1055,"line":1845},29,[1053,1847,1118],{"emptyLinePlaceholder":1117},[1053,1849,1851,1854,1856,1858,1860,1862,1864,1867,1869,1872,1874,1876,1878,1880],{"class":1055,"line":1850},30,[1053,1852,1853],{"class":1074},"    ActiveUsers",[1053,1855,1078],{"class":1074},[1053,1857,1081],{"class":1074},[1053,1859,1084],{"class":1068},[1053,1861,609],{"class":1087},[1053,1863,1090],{"class":1068},[1053,1865,1866],{"class":1093},"\"active\"",[1053,1868,1097],{"class":1068},[1053,1870,1871],{"class":1093}," \"Query active users\"",[1053,1873,1097],{"class":1068},[1053,1875,1081],{"class":1105},[1053,1877,1084],{"class":1068},[1053,1879,144],{"class":1105},[1053,1881,1153],{"class":1068},[1053,1883,1885,1887,1889,1891,1893,1895,1897],{"class":1055,"line":1884},31,[1053,1886,1160],{"class":1159},[1053,1888,1163],{"class":1068},[1053,1890,1216],{"class":1068},[1053,1892,960],{"class":1105},[1053,1894,1084],{"class":1068},[1053,1896,831],{"class":1105},[1053,1898,1153],{"class":1068},[1053,1900,1902,1905,1907,1909,1911,1913,1915,1917,1919,1921,1923,1925,1927],{"class":1055,"line":1901},32,[1053,1903,1904],{"class":1068},"            {",[1053,1906,1178],{"class":1159},[1053,1908,1163],{"class":1068},[1053,1910,1183],{"class":1093},[1053,1912,1097],{"class":1068},[1053,1914,1188],{"class":1159},[1053,1916,1163],{"class":1068},[1053,1918,1193],{"class":1093},[1053,1920,1097],{"class":1068},[1053,1922,1198],{"class":1159},[1053,1924,1163],{"class":1068},[1053,1926,1183],{"class":1093},[1053,1928,1929],{"class":1068},"},\n",[1053,1931,1933],{"class":1055,"line":1932},33,[1053,1934,1935],{"class":1068},"        },\n",[1053,1937,1939],{"class":1055,"line":1938},34,[1053,1940,1269],{"class":1068},[1053,1942,1944],{"class":1055,"line":1943},35,[1053,1945,1361],{"class":1068},[1053,1947,1949],{"class":1055,"line":1948},36,[1053,1950,1118],{"emptyLinePlaceholder":1117},[1053,1952,1954,1957,1960,1963],{"class":1055,"line":1953},37,[1053,1955,1956],{"class":1064},"func",[1053,1958,1959],{"class":1087}," main",[1053,1961,1962],{"class":1068},"()",[1053,1964,1614],{"class":1068},[1053,1966,1968,1971,1973,1975,1977,1980,1982,1985,1987,1990,1992,1995],{"class":1055,"line":1967},38,[1053,1969,1970],{"class":1074},"    db",[1053,1972,1097],{"class":1068},[1053,1974,1383],{"class":1074},[1053,1976,1386],{"class":1074},[1053,1978,1979],{"class":1074}," sqlx",[1053,1981,1084],{"class":1068},[1053,1983,1984],{"class":1087},"Connect",[1053,1986,1090],{"class":1068},[1053,1988,1989],{"class":1093},"\"postgres\"",[1053,1991,1097],{"class":1068},[1053,1993,1994],{"class":1093}," \"postgres://localhost/mydb?sslmode=disable\"",[1053,1996,1361],{"class":1068},[1053,1998,2000,2003,2005,2008,2010,2013],{"class":1055,"line":1999},39,[1053,2001,2002],{"class":1074},"    ctx",[1053,2004,1386],{"class":1074},[1053,2006,2007],{"class":1074}," context",[1053,2009,1084],{"class":1068},[1053,2011,2012],{"class":1087},"Background",[1053,2014,2015],{"class":1068},"()\n",[1053,2017,2019],{"class":1055,"line":2018},40,[1053,2020,1118],{"emptyLinePlaceholder":1117},[1053,2022,2024],{"class":1055,"line":2023},41,[1053,2025,2026],{"class":1058},"    // Create exec\n",[1053,2028,2030,2033,2035,2037,2039,2041,2043,2045,2047,2050,2053,2056,2058,2061,2063,2066,2068,2070],{"class":1055,"line":2029},42,[1053,2031,2032],{"class":1074},"    exec",[1053,2034,1097],{"class":1068},[1053,2036,1383],{"class":1074},[1053,2038,1386],{"class":1074},[1053,2040,1081],{"class":1074},[1053,2042,1084],{"class":1068},[1053,2044,600],{"class":1087},[1053,2046,1412],{"class":1068},[1053,2048,2049],{"class":1105},"User",[1053,2051,2052],{"class":1068},"](",[1053,2054,2055],{"class":1074},"db",[1053,2057,1097],{"class":1068},[1053,2059,2060],{"class":1093}," \"users\"",[1053,2062,1097],{"class":1068},[1053,2064,2065],{"class":1074}," postgres",[1053,2067,1084],{"class":1068},[1053,2069,600],{"class":1087},[1053,2071,2072],{"class":1068},"())\n",[1053,2074,2076],{"class":1055,"line":2075},43,[1053,2077,1118],{"emptyLinePlaceholder":1117},[1053,2079,2081],{"class":1055,"line":2080},44,[1053,2082,2083],{"class":1058},"    // Execute statements\n",[1053,2085,2087,2090,2092,2094,2096,2098,2100,2102,2104,2106,2108,2111,2113,2116],{"class":1055,"line":2086},45,[1053,2088,2089],{"class":1074},"    users",[1053,2091,1097],{"class":1068},[1053,2093,1383],{"class":1074},[1053,2095,1386],{"class":1074},[1053,2097,1389],{"class":1074},[1053,2099,1084],{"class":1068},[1053,2101,1394],{"class":1087},[1053,2103,1090],{"class":1068},[1053,2105,1399],{"class":1074},[1053,2107,1097],{"class":1068},[1053,2109,2110],{"class":1074}," QueryAll",[1053,2112,1097],{"class":1068},[1053,2114,2115],{"class":1064}," nil",[1053,2117,1361],{"class":1068},[1053,2119,2121,2124,2126,2128,2130,2132,2134,2136,2138,2140,2142,2144,2146,2148,2150,2152,2154,2156,2158,2160,2162,2165],{"class":1055,"line":2120},46,[1053,2122,2123],{"class":1074},"    user",[1053,2125,1097],{"class":1068},[1053,2127,1383],{"class":1074},[1053,2129,1386],{"class":1074},[1053,2131,1389],{"class":1074},[1053,2133,1084],{"class":1068},[1053,2135,1454],{"class":1087},[1053,2137,1090],{"class":1068},[1053,2139,1399],{"class":1074},[1053,2141,1097],{"class":1068},[1053,2143,1463],{"class":1074},[1053,2145,1097],{"class":1068},[1053,2147,1409],{"class":1064},[1053,2149,1412],{"class":1068},[1053,2151,1415],{"class":1105},[1053,2153,1418],{"class":1068},[1053,2155,1421],{"class":1105},[1053,2157,1424],{"class":1068},[1053,2159,1480],{"class":1093},[1053,2161,1163],{"class":1068},[1053,2163,2164],{"class":1259}," 1",[1053,2166,1435],{"class":1068},[1053,2168,2170,2173,2175,2177,2179,2181,2183,2186,2188,2190,2192,2195,2197,2199],{"class":1055,"line":2169},47,[1053,2171,2172],{"class":1074},"    count",[1053,2174,1097],{"class":1068},[1053,2176,1383],{"class":1074},[1053,2178,1386],{"class":1074},[1053,2180,1389],{"class":1074},[1053,2182,1084],{"class":1068},[1053,2184,2185],{"class":1087},"ExecAggregate",[1053,2187,1090],{"class":1068},[1053,2189,1399],{"class":1074},[1053,2191,1097],{"class":1068},[1053,2193,2194],{"class":1074}," CountAll",[1053,2196,1097],{"class":1068},[1053,2198,2115],{"class":1064},[1053,2200,1361],{"class":1068},[1053,2202,2204,2207,2209,2211,2213,2215,2217,2219,2221,2223,2225,2228,2230,2232,2234,2236,2238,2240,2242,2244,2246,2248],{"class":1055,"line":2203},48,[1053,2205,2206],{"class":1074},"    active",[1053,2208,1097],{"class":1068},[1053,2210,1383],{"class":1074},[1053,2212,1386],{"class":1074},[1053,2214,1389],{"class":1074},[1053,2216,1084],{"class":1068},[1053,2218,1394],{"class":1087},[1053,2220,1090],{"class":1068},[1053,2222,1399],{"class":1074},[1053,2224,1097],{"class":1068},[1053,2226,2227],{"class":1074}," ActiveUsers",[1053,2229,1097],{"class":1068},[1053,2231,1409],{"class":1064},[1053,2233,1412],{"class":1068},[1053,2235,1415],{"class":1105},[1053,2237,1418],{"class":1068},[1053,2239,1421],{"class":1105},[1053,2241,1424],{"class":1068},[1053,2243,1427],{"class":1093},[1053,2245,1163],{"class":1068},[1053,2247,1432],{"class":1093},[1053,2249,1435],{"class":1068},[1053,2251,2253],{"class":1055,"line":2252},49,[1053,2254,1118],{"emptyLinePlaceholder":1117},[1053,2256,2258,2261,2263,2266,2268,2271,2275,2278,2281,2284,2287,2290,2292,2295,2299,2301,2303,2307,2309,2311,2314,2317,2319,2322,2324,2327,2329,2331,2333,2336],{"class":1055,"line":2257},50,[1053,2259,2260],{"class":1074},"    fmt",[1053,2262,1084],{"class":1068},[1053,2264,2265],{"class":1087},"Printf",[1053,2267,1090],{"class":1068},[1053,2269,2270],{"class":1093},"\"",[1053,2272,2274],{"class":2273},"scyPU","%d",[1053,2276,2277],{"class":1093}," users, user #1: ",[1053,2279,2280],{"class":2273},"%s",[1053,2282,2283],{"class":1093},", ",[1053,2285,2286],{"class":2273},"%.0f",[1053,2288,2289],{"class":1093}," total, ",[1053,2291,2274],{"class":2273},[1053,2293,2294],{"class":1093}," active",[1053,2296,2298],{"class":2297},"suWN2","\\n",[1053,2300,2270],{"class":1093},[1053,2302,1097],{"class":1068},[1053,2304,2306],{"class":2305},"skxcq"," len",[1053,2308,1090],{"class":1068},[1053,2310,1378],{"class":1074},[1053,2312,2313],{"class":1068},"),",[1053,2315,2316],{"class":1074}," user",[1053,2318,1084],{"class":1068},[1053,2320,2321],{"class":1074},"Name",[1053,2323,1097],{"class":1068},[1053,2325,2326],{"class":1074}," count",[1053,2328,1097],{"class":1068},[1053,2330,2306],{"class":2305},[1053,2332,1090],{"class":1068},[1053,2334,2335],{"class":1074},"active",[1053,2337,2338],{"class":1068},"))\n",[1053,2340,2342],{"class":1055,"line":2341},51,[1053,2343,1663],{"class":1068},[1036,2345,2347],{"id":2346},"capabilities","Capabilities",[2349,2350,2351,2367],"table",{},[2352,2353,2354],"thead",{},[2355,2356,2357,2361,2364],"tr",{},[2358,2359,2360],"th",{},"Feature",[2358,2362,2363],{},"Description",[2358,2365,2366],{},"Docs",[2368,2369,2370,2383,2401,2417,2430],"tbody",{},[2355,2371,2372,2375,2378],{},[2373,2374,27],"td",{},[2373,2376,2377],{},"Query, Select, Aggregate, Insert, Update, Delete",[2373,2379,2380],{},[965,2381,120],{"href":2382},"docs/guides/statements",[2355,2384,2385,2388,2395],{},[2373,2386,2387],{},"Generic Executor",[2373,2389,2390,2391,2394],{},"Type-safe ",[1050,2392,2393],{},"Executor[T]"," with compile-time checking",[2373,2396,2397],{},[965,2398,2400],{"href":2399},"docs/learn/concepts","Concepts",[2355,2402,2403,2406,2412],{},[2373,2404,2405],{},"Multi-Dialect Support",[2373,2407,2408,2409],{},"PostgreSQL, MariaDB, SQLite, SQL Server via ",[965,2410,1519],{"href":1517,"rel":2411},[969],[2373,2413,2414],{},[965,2415,58],{"href":2416},"docs/learn/quickstart",[2355,2418,2419,2422,2425],{},[2373,2420,2421],{},"Declarative Specs",[2373,2423,2424],{},"Queries as pure data structures",[2373,2426,2427],{},[965,2428,16],{"href":2429},"docs/learn/architecture",[2355,2431,2432,2435,2438],{},[2373,2433,2434],{},"Thread-Safe Execution",[2373,2436,2437],{},"Concurrent access, no shared mutable state",[2373,2439,2440],{},[965,2441,2443],{"href":2442},"docs/reference/api","API",[1036,2445,2447],{"id":2446},"why-edamame","Why edamame?",[2449,2450,2451,2467,2473,2479,2485],"ul",{},[2452,2453,2454,2458,2459,2461,2462],"li",{},[2455,2456,2457],"strong",{},"Type-safe"," — Generic ",[1050,2460,2393],{}," with compile-time safety via ",[965,2463,2466],{"href":2464,"rel":2465},"https://github.com/zoobz-io/soy",[969],"soy",[2452,2468,2469,2472],{},[2455,2470,2471],{},"No magic strings"," — Typed statements, not string keys",[2452,2474,2475,2478],{},[2455,2476,2477],{},"Declarative"," — Specs are data, statements wrap them with identity",[2452,2480,2481,2484],{},[2455,2482,2483],{},"Compile-time guarantees"," — Pass wrong statement type? Compiler catches it",[2452,2486,2487,2490],{},[2455,2488,2489],{},"Thread-safe"," — Concurrent execution, no shared mutable state",[1036,2492,2494],{"id":2493},"structural-query-safety","Structural Query Safety",[962,2496,2497,2498,1084],{},"Edamame enables a pattern: ",[2455,2499,2500],{},"define statements once, execute them anywhere",[962,2502,2503],{},"Your query logic lives in typed statements as package-level variables. The executor consumes them with full type safety. No string interpolation, no runtime query building, no SQL injection vectors.",[1044,2505,2507],{"className":1046,"code":2506,"language":1048,"meta":34,"style":34},"// In your repository package\nvar FindActive = edamame.NewQueryStatement(\"find-active\", \"Find active users\", edamame.QuerySpec{\n    Where: []edamame.ConditionSpec{{Field: \"status\", Operator: \"=\", Param: \"status\"}},\n})\n\n// In your service layer\nusers, err := exec.ExecQuery(ctx, FindActive, map[string]any{\"status\": \"active\"})\n",[1050,2508,2509,2514,2549,2590,2594,2598,2603],{"__ignoreMap":34},[1053,2510,2511],{"class":1055,"line":9},[1053,2512,2513],{"class":1058},"// In your repository package\n",[1053,2515,2516,2518,2521,2523,2525,2527,2529,2531,2534,2536,2539,2541,2543,2545,2547],{"class":1055,"line":19},[1053,2517,1065],{"class":1064},[1053,2519,2520],{"class":1074}," FindActive",[1053,2522,1078],{"class":1074},[1053,2524,1081],{"class":1074},[1053,2526,1084],{"class":1068},[1053,2528,609],{"class":1087},[1053,2530,1090],{"class":1068},[1053,2532,2533],{"class":1093},"\"find-active\"",[1053,2535,1097],{"class":1068},[1053,2537,2538],{"class":1093}," \"Find active users\"",[1053,2540,1097],{"class":1068},[1053,2542,1081],{"class":1105},[1053,2544,1084],{"class":1068},[1053,2546,144],{"class":1105},[1053,2548,1153],{"class":1068},[1053,2550,2551,2554,2556,2558,2560,2562,2564,2566,2568,2570,2572,2574,2576,2578,2580,2582,2584,2586,2588],{"class":1055,"line":40},[1053,2552,2553],{"class":1159},"    Where",[1053,2555,1163],{"class":1068},[1053,2557,1216],{"class":1068},[1053,2559,960],{"class":1105},[1053,2561,1084],{"class":1068},[1053,2563,831],{"class":1105},[1053,2565,1175],{"class":1068},[1053,2567,1178],{"class":1159},[1053,2569,1163],{"class":1068},[1053,2571,1183],{"class":1093},[1053,2573,1097],{"class":1068},[1053,2575,1188],{"class":1159},[1053,2577,1163],{"class":1068},[1053,2579,1193],{"class":1093},[1053,2581,1097],{"class":1068},[1053,2583,1198],{"class":1159},[1053,2585,1163],{"class":1068},[1053,2587,1183],{"class":1093},[1053,2589,1205],{"class":1068},[1053,2591,2592],{"class":1055,"line":674},[1053,2593,1435],{"class":1068},[1053,2595,2596],{"class":1055,"line":1121},[1053,2597,1118],{"emptyLinePlaceholder":1117},[1053,2599,2600],{"class":1055,"line":1156},[1053,2601,2602],{"class":1058},"// In your service layer\n",[1053,2604,2605,2607,2609,2612,2614,2616,2618,2620,2622,2624,2626,2628,2630,2632,2634,2636,2638,2640,2642,2644,2646,2648],{"class":1055,"line":1208},[1053,2606,1378],{"class":1074},[1053,2608,1097],{"class":1068},[1053,2610,2611],{"class":1074}," err",[1053,2613,1386],{"class":1074},[1053,2615,1389],{"class":1074},[1053,2617,1084],{"class":1068},[1053,2619,1394],{"class":1087},[1053,2621,1090],{"class":1068},[1053,2623,1399],{"class":1074},[1053,2625,1097],{"class":1068},[1053,2627,2520],{"class":1074},[1053,2629,1097],{"class":1068},[1053,2631,1409],{"class":1064},[1053,2633,1412],{"class":1068},[1053,2635,1415],{"class":1105},[1053,2637,1418],{"class":1068},[1053,2639,1421],{"class":1105},[1053,2641,1424],{"class":1068},[1053,2643,1427],{"class":1093},[1053,2645,1163],{"class":1068},[1053,2647,1432],{"class":1093},[1053,2649,1435],{"class":1068},[962,2651,2652],{},"Statements are the single source of truth. The database dialect handles rendering. Your code stays clean.",[1036,2654,2656],{"id":2655},"documentation","Documentation",[2449,2658,2659],{},[2452,2660,2661,2664],{},[965,2662,6],{"href":2663},"docs/overview"," — Design philosophy and architecture",[2666,2667,897],"h3",{"id":2668},"learn",[2449,2670,2671,2675,2679],{},[2452,2672,2673],{},[965,2674,58],{"href":2416},[2452,2676,2677],{},[965,2678,101],{"href":2399},[2452,2680,2681],{},[965,2682,16],{"href":2429},[2666,2684,909],{"id":2685},"guides",[2449,2687,2688,2692],{},[2452,2689,2690],{},[965,2691,120],{"href":2382},[2452,2693,2694],{},[965,2695,456],{"href":2696},"docs/guides/testing",[2666,2698,925],{"id":2699},"reference",[2449,2701,2702],{},[2452,2703,2704],{},[965,2705,587],{"href":2442},[1036,2707,2709],{"id":2708},"contributing","Contributing",[962,2711,2712,2713,2717],{},"See ",[965,2714,2716],{"href":2715},"CONTRIBUTING","CONTRIBUTING.md"," for guidelines.",[1036,2719,1012],{"id":2720},"license",[962,2722,2723,2724,2726],{},"MIT License — see ",[965,2725,1009],{"href":1009}," for details.",[2728,2729,2730],"style",{},"html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}",{"title":34,"searchDepth":19,"depth":19,"links":2732},[2733,2734,2735,2736,2737,2738,2739,2744,2745],{"id":1038,"depth":19,"text":1039},{"id":1493,"depth":19,"text":1494},{"id":1522,"depth":19,"text":1523},{"id":2346,"depth":19,"text":2347},{"id":2446,"depth":19,"text":2447},{"id":2493,"depth":19,"text":2494},{"id":2655,"depth":19,"text":2656,"children":2740},[2741,2742,2743],{"id":2668,"depth":40,"text":897},{"id":2685,"depth":40,"text":909},{"id":2699,"depth":40,"text":925},{"id":2708,"depth":19,"text":2709},{"id":2720,"depth":19,"text":1012},"md","book-open",{},"/readme",{"title":953,"description":34},"readme","BscYQNVE6yaJgKSiVGtWPnquY2UTEYnFwgQPUaLtbJU",{"id":2754,"title":48,"body":2755,"description":34,"extension":2746,"icon":3188,"meta":3189,"navigation":1117,"path":3190,"seo":3191,"stem":3192,"__hash__":3193},"resources/security.md",{"type":955,"value":2756,"toc":3174},[2757,2761,2765,2768,2807,2811,2814,2818,2823,2826,2865,2869,2872,2921,2925,2951,2955,2958,2962,2965,3061,3065,3068,3099,3103,3106,3131,3135,3149,3153,3156,3162,3165,3171],[958,2758,2760],{"id":2759},"security-policy","Security Policy",[1036,2762,2764],{"id":2763},"supported-versions","Supported Versions",[962,2766,2767],{},"We release patches for security vulnerabilities. Which versions are eligible for receiving such patches depends on the CVSS v3.0 Rating:",[2349,2769,2770,2783],{},[2352,2771,2772],{},[2355,2773,2774,2777,2780],{},[2358,2775,2776],{},"Version",[2358,2778,2779],{},"Supported",[2358,2781,2782],{},"Status",[2368,2784,2785,2796],{},[2355,2786,2787,2790,2793],{},[2373,2788,2789],{},"latest",[2373,2791,2792],{},"✅",[2373,2794,2795],{},"Active development",[2355,2797,2798,2801,2804],{},[2373,2799,2800],{},"\u003C latest",[2373,2802,2803],{},"❌",[2373,2805,2806],{},"Security fixes only for critical issues",[1036,2808,2810],{"id":2809},"reporting-a-vulnerability","Reporting a Vulnerability",[962,2812,2813],{},"We take the security of edamame seriously. If you have discovered a security vulnerability in this project, please report it responsibly.",[2666,2815,2817],{"id":2816},"how-to-report","How to Report",[962,2819,2820],{},[2455,2821,2822],{},"Please DO NOT report security vulnerabilities through public GitHub issues.",[962,2824,2825],{},"Instead, please report them via one of the following methods:",[2827,2828,2829,2852],"ol",{},[2452,2830,2831,2834,2835],{},[2455,2832,2833],{},"GitHub Security Advisories"," (Preferred)",[2449,2836,2837,2846,2849],{},[2452,2838,2839,2840,2845],{},"Go to the ",[965,2841,2844],{"href":2842,"rel":2843},"https://github.com/zoobz-io/edamame/security",[969],"Security tab"," of this repository",[2452,2847,2848],{},"Click \"Report a vulnerability\"",[2452,2850,2851],{},"Fill out the form with details about the vulnerability",[2452,2853,2854,2857],{},[2455,2855,2856],{},"Email",[2449,2858,2859,2862],{},[2452,2860,2861],{},"Send details to the repository maintainer through GitHub profile contact information",[2452,2863,2864],{},"Use PGP encryption if possible for sensitive details",[2666,2866,2868],{"id":2867},"what-to-include","What to Include",[962,2870,2871],{},"Please include the following information (as much as you can provide) to help us better understand the nature and scope of the possible issue:",[2449,2873,2874,2880,2886,2892,2898,2903,2909,2915],{},[2452,2875,2876,2879],{},[2455,2877,2878],{},"Type of issue"," (e.g., SQL injection, race condition, information disclosure, etc.)",[2452,2881,2882,2885],{},[2455,2883,2884],{},"Full paths of source file(s)"," related to the manifestation of the issue",[2452,2887,2888,2891],{},[2455,2889,2890],{},"The location of the affected source code"," (tag/branch/commit or direct URL)",[2452,2893,2894,2897],{},[2455,2895,2896],{},"Any special configuration required"," to reproduce the issue",[2452,2899,2900,2897],{},[2455,2901,2902],{},"Step-by-step instructions",[2452,2904,2905,2908],{},[2455,2906,2907],{},"Proof-of-concept or exploit code"," (if possible)",[2452,2910,2911,2914],{},[2455,2912,2913],{},"Impact of the issue",", including how an attacker might exploit the issue",[2452,2916,2917,2920],{},[2455,2918,2919],{},"Your name and affiliation"," (optional)",[2666,2922,2924],{"id":2923},"what-to-expect","What to Expect",[2449,2926,2927,2933,2939,2945],{},[2452,2928,2929,2932],{},[2455,2930,2931],{},"Acknowledgment",": We will acknowledge receipt of your vulnerability report within 48 hours",[2452,2934,2935,2938],{},[2455,2936,2937],{},"Initial Assessment",": Within 7 days, we will provide an initial assessment of the report",[2452,2940,2941,2944],{},[2455,2942,2943],{},"Resolution Timeline",": We aim to resolve critical issues within 30 days",[2452,2946,2947,2950],{},[2455,2948,2949],{},"Disclosure",": We will coordinate with you on the disclosure timeline",[2666,2952,2954],{"id":2953},"preferred-languages","Preferred Languages",[962,2956,2957],{},"We prefer all communications to be in English.",[1036,2959,2961],{"id":2960},"security-best-practices","Security Best Practices",[962,2963,2964],{},"When using edamame in your applications, we recommend:",[2827,2966,2967,2988,3007,3026,3045],{},[2452,2968,2969,2972],{},[2455,2970,2971],{},"Keep Dependencies Updated",[1044,2973,2975],{"className":1497,"code":2974,"language":1499,"meta":34,"style":34},"go get -u github.com/zoobz-io/edamame\n",[1050,2976,2977],{"__ignoreMap":34},[1053,2978,2979,2981,2983,2986],{"class":1055,"line":9},[1053,2980,1048],{"class":1087},[1053,2982,1508],{"class":1093},[1053,2984,2985],{"class":1064}," -u",[1053,2987,1511],{"class":1093},[2452,2989,2990,2993],{},[2455,2991,2992],{},"Database Security",[2449,2994,2995,2998,3001,3004],{},[2452,2996,2997],{},"Use least-privilege database accounts",[2452,2999,3000],{},"Never expose factory specs publicly without authentication",[2452,3002,3003],{},"Validate all user input before passing to capabilities",[2452,3005,3006],{},"Use transactions for multi-step operations",[2452,3008,3009,3012],{},[2455,3010,3011],{},"LLM Integration Security",[2449,3013,3014,3017,3020,3023],{},[2452,3015,3016],{},"Always validate LLM-generated capability names and params",[2452,3018,3019],{},"Rate limit LLM-driven database operations",[2452,3021,3022],{},"Audit log all LLM-executed queries",[2452,3024,3025],{},"Never expose raw SQL generation to external systems",[2452,3027,3028,3031],{},[2455,3029,3030],{},"Capability Management",[2449,3032,3033,3036,3039,3042],{},[2452,3034,3035],{},"Only expose capabilities that are safe for your use case",[2452,3037,3038],{},"Use explicit params with validation",[2452,3040,3041],{},"Avoid overly permissive WHERE clauses",[2452,3043,3044],{},"Review auto-derived params before deployment",[2452,3046,3047,3050],{},[2455,3048,3049],{},"Error Handling",[2449,3051,3052,3055,3058],{},[2452,3053,3054],{},"Implement proper error handling",[2452,3056,3057],{},"Don't expose internal errors to users",[2452,3059,3060],{},"Log errors appropriately",[1036,3062,3064],{"id":3063},"security-features","Security Features",[962,3066,3067],{},"edamame includes several built-in security features:",[2449,3069,3070,3076,3081,3087,3093],{},[2452,3071,3072,3075],{},[2455,3073,3074],{},"Parameterized Queries",": All queries use parameterized SQL via sqlx, preventing SQL injection",[2452,3077,3078,3080],{},[2455,3079,37],{},": Generic types prevent type confusion",[2452,3082,3083,3086],{},[2455,3084,3085],{},"Spec Validation",": Capability specs are validated before execution",[2452,3088,3089,3092],{},[2455,3090,3091],{},"No Raw SQL",": Users define specs, not raw SQL strings",[2452,3094,3095,3098],{},[2455,3096,3097],{},"Introspection Only",": Spec export is read-only, doesn't expose credentials",[1036,3100,3102],{"id":3101},"automated-security-scanning","Automated Security Scanning",[962,3104,3105],{},"This project uses:",[2449,3107,3108,3113,3119,3125],{},[2452,3109,3110,3112],{},[2455,3111,997],{},": GitHub's semantic code analysis for security vulnerabilities",[2452,3114,3115,3118],{},[2455,3116,3117],{},"gosec",": Go security checker for common vulnerabilities",[2452,3120,3121,3124],{},[2455,3122,3123],{},"golangci-lint",": Static analysis including security linters (sqlclosecheck, noctx, bodyclose)",[2452,3126,3127,3130],{},[2455,3128,3129],{},"Codecov",": Coverage tracking to ensure security-critical code is tested",[1036,3132,3134],{"id":3133},"vulnerability-disclosure-policy","Vulnerability Disclosure Policy",[2449,3136,3137,3140,3143,3146],{},[2452,3138,3139],{},"Security vulnerabilities will be disclosed via GitHub Security Advisories",[2452,3141,3142],{},"We follow a 90-day disclosure timeline for non-critical issues",[2452,3144,3145],{},"Critical vulnerabilities may be disclosed sooner after patches are available",[2452,3147,3148],{},"We will credit reporters who follow responsible disclosure practices",[1036,3150,3152],{"id":3151},"credits","Credits",[962,3154,3155],{},"We thank the following individuals for responsibly disclosing security issues:",[962,3157,3158],{},[3159,3160,3161],"em",{},"This list is currently empty. Be the first to help improve our security!",[3163,3164],"hr",{},[962,3166,3167,3170],{},[2455,3168,3169],{},"Last Updated",": 2025-12-17",[2728,3172,3173],{},"html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":34,"searchDepth":19,"depth":19,"links":3175},[3176,3177,3183,3184,3185,3186,3187],{"id":2763,"depth":19,"text":2764},{"id":2809,"depth":19,"text":2810,"children":3178},[3179,3180,3181,3182],{"id":2816,"depth":40,"text":2817},{"id":2867,"depth":40,"text":2868},{"id":2923,"depth":40,"text":2924},{"id":2953,"depth":40,"text":2954},{"id":2960,"depth":19,"text":2961},{"id":3063,"depth":19,"text":3064},{"id":3101,"depth":19,"text":3102},{"id":3133,"depth":19,"text":3134},{"id":3151,"depth":19,"text":3152},"shield",{},"/security",{"title":48,"description":34},"security","Sx8ErY-iTzxHLX7iGbih6ydeebS0veDo30qsuQcZLEI",{"id":3195,"title":2709,"body":3196,"description":3204,"extension":2746,"icon":1050,"meta":3699,"navigation":1117,"path":3700,"seo":3701,"stem":2708,"__hash__":3702},"resources/contributing.md",{"type":955,"value":3197,"toc":3675},[3198,3202,3205,3209,3212,3216,3260,3264,3268,3286,3289,3312,3314,3328,3332,3336,3350,3354,3365,3369,3372,3386,3390,3418,3421,3424,3437,3440,3452,3455,3467,3470,3482,3485,3497,3501,3509,3513,3516,3560,3564,3568,3571,3582,3585,3599,3603,3630,3636,3640,3643,3654,3658,3669,3672],[958,3199,3201],{"id":3200},"contributing-to-edamame","Contributing to edamame",[962,3203,3204],{},"Thank you for your interest in contributing to edamame! This guide will help you get started.",[1036,3206,3208],{"id":3207},"code-of-conduct","Code of Conduct",[962,3210,3211],{},"By participating in this project, you agree to maintain a respectful and inclusive environment for all contributors.",[1036,3213,3215],{"id":3214},"getting-started","Getting Started",[2827,3217,3218,3221,3227,3233,3236,3242,3248,3251,3257],{},[2452,3219,3220],{},"Fork the repository",[2452,3222,3223,3224],{},"Clone your fork: ",[1050,3225,3226],{},"git clone https://github.com/yourusername/edamame.git",[2452,3228,3229,3230],{},"Create a feature branch: ",[1050,3231,3232],{},"git checkout -b feature/your-feature-name",[2452,3234,3235],{},"Make your changes",[2452,3237,3238,3239],{},"Run tests: ",[1050,3240,3241],{},"make test",[2452,3243,3244,3245],{},"Run linters: ",[1050,3246,3247],{},"make lint",[2452,3249,3250],{},"Commit your changes with a descriptive message",[2452,3252,3253,3254],{},"Push to your fork: ",[1050,3255,3256],{},"git push origin feature/your-feature-name",[2452,3258,3259],{},"Create a Pull Request",[1036,3261,3263],{"id":3262},"development-guidelines","Development Guidelines",[2666,3265,3267],{"id":3266},"code-style","Code Style",[2449,3269,3270,3273,3280,3283],{},[2452,3271,3272],{},"Follow standard Go conventions",[2452,3274,3275,3276,3279],{},"Run ",[1050,3277,3278],{},"go fmt"," before committing",[2452,3281,3282],{},"Add comments for exported functions and types",[2452,3284,3285],{},"Keep functions small and focused",[2666,3287,456],{"id":3288},"testing",[2449,3290,3291,3294,3299,3302,3305],{},[2452,3292,3293],{},"Write tests for new functionality",[2452,3295,3296,3297],{},"Ensure all tests pass: ",[1050,3298,3241],{},[2452,3300,3301],{},"Maintain 1:1 file-to-test ratio",[2452,3303,3304],{},"Aim for 80%+ test coverage",[2452,3306,3307,3308,3311],{},"Use the ",[1050,3309,3310],{},"testing/"," package helpers for capability testing",[2666,3313,2656],{"id":2655},[2449,3315,3316,3319,3322,3325],{},[2452,3317,3318],{},"Update README.md for API changes",[2452,3320,3321],{},"Add comments to all exported types",[2452,3323,3324],{},"Keep doc comments clear and concise",[2452,3326,3327],{},"Update docs/ for significant changes",[1036,3329,3331],{"id":3330},"types-of-contributions","Types of Contributions",[2666,3333,3335],{"id":3334},"bug-reports","Bug Reports",[2449,3337,3338,3341,3344,3347],{},[2452,3339,3340],{},"Use GitHub Issues",[2452,3342,3343],{},"Include minimal reproduction code",[2452,3345,3346],{},"Describe expected vs actual behavior",[2452,3348,3349],{},"Include Go version and OS",[2666,3351,3353],{"id":3352},"feature-requests","Feature Requests",[2449,3355,3356,3359,3362],{},[2452,3357,3358],{},"Open an issue for discussion first",[2452,3360,3361],{},"Explain the use case",[2452,3363,3364],{},"Consider backwards compatibility",[2666,3366,3368],{"id":3367},"code-contributions","Code Contributions",[962,3370,3371],{},"All contributions should:",[2449,3373,3374,3377,3380,3383],{},[2452,3375,3376],{},"Include comprehensive tests",[2452,3378,3379],{},"Pass linter checks",[2452,3381,3382],{},"Maintain existing code style",[2452,3384,3385],{},"Update documentation as needed",[1036,3387,3389],{"id":3388},"pull-request-process","Pull Request Process",[2827,3391,3392,3398,3403,3408,3413],{},[2452,3393,3394,3397],{},[2455,3395,3396],{},"Keep PRs focused"," - One feature/fix per PR",[2452,3399,3400],{},[2455,3401,3402],{},"Write descriptive commit messages",[2452,3404,3405],{},[2455,3406,3407],{},"Update tests and documentation",[2452,3409,3410],{},[2455,3411,3412],{},"Ensure CI passes",[2452,3414,3415],{},[2455,3416,3417],{},"Respond to review feedback",[1036,3419,456],{"id":3420},"testing-1",[962,3422,3423],{},"Run the full test suite:",[1044,3425,3427],{"className":1497,"code":3426,"language":1499,"meta":34,"style":34},"make test\n",[1050,3428,3429],{"__ignoreMap":34},[1053,3430,3431,3434],{"class":1055,"line":9},[1053,3432,3433],{"class":1087},"make",[1053,3435,3436],{"class":1093}," test\n",[962,3438,3439],{},"Run with coverage:",[1044,3441,3443],{"className":1497,"code":3442,"language":1499,"meta":34,"style":34},"make coverage\n",[1050,3444,3445],{"__ignoreMap":34},[1053,3446,3447,3449],{"class":1055,"line":9},[1053,3448,3433],{"class":1087},[1053,3450,3451],{"class":1093}," coverage\n",[962,3453,3454],{},"Run linters:",[1044,3456,3458],{"className":1497,"code":3457,"language":1499,"meta":34,"style":34},"make lint\n",[1050,3459,3460],{"__ignoreMap":34},[1053,3461,3462,3464],{"class":1055,"line":9},[1053,3463,3433],{"class":1087},[1053,3465,3466],{"class":1093}," lint\n",[962,3468,3469],{},"Run integration tests (requires Docker):",[1044,3471,3473],{"className":1497,"code":3472,"language":1499,"meta":34,"style":34},"make test-integration\n",[1050,3474,3475],{"__ignoreMap":34},[1053,3476,3477,3479],{"class":1055,"line":9},[1053,3478,3433],{"class":1087},[1053,3480,3481],{"class":1093}," test-integration\n",[962,3483,3484],{},"Run full CI simulation:",[1044,3486,3488],{"className":1497,"code":3487,"language":1499,"meta":34,"style":34},"make ci\n",[1050,3489,3490],{"__ignoreMap":34},[1053,3491,3492,3494],{"class":1055,"line":9},[1053,3493,3433],{"class":1087},[1053,3495,3496],{"class":1093}," ci\n",[1036,3498,3500],{"id":3499},"project-structure","Project Structure",[1044,3502,3507],{"className":3503,"code":3505,"language":3506},[3504],"language-text","edamame/\n├── *.go              # Core library files\n├── *_test.go         # Tests (1:1 with source files)\n├── testing/          # Test helpers and benchmarks\n│   ├── helpers.go    # Test utilities\n│   ├── benchmarks/   # Performance benchmarks\n│   └── integration/  # Database integration tests\n├── docs/             # Documentation\n├── .github/          # GitHub workflows and templates\n├── README.md         # Project documentation\n└── Makefile          # Build and test commands\n","text",[1050,3508,3505],{"__ignoreMap":34},[1036,3510,3512],{"id":3511},"commit-messages","Commit Messages",[962,3514,3515],{},"Follow conventional commits:",[2449,3517,3518,3524,3530,3536,3542,3548,3554],{},[2452,3519,3520,3523],{},[1050,3521,3522],{},"feat:"," New feature",[2452,3525,3526,3529],{},[1050,3527,3528],{},"fix:"," Bug fix",[2452,3531,3532,3535],{},[1050,3533,3534],{},"docs:"," Documentation changes",[2452,3537,3538,3541],{},[1050,3539,3540],{},"test:"," Test additions/changes",[2452,3543,3544,3547],{},[1050,3545,3546],{},"refactor:"," Code refactoring",[2452,3549,3550,3553],{},[1050,3551,3552],{},"perf:"," Performance improvements",[2452,3555,3556,3559],{},[1050,3557,3558],{},"chore:"," Maintenance tasks",[1036,3561,3563],{"id":3562},"release-process","Release Process",[2666,3565,3567],{"id":3566},"automated-releases","Automated Releases",[962,3569,3570],{},"This project uses automated release versioning. To create a release:",[2827,3572,3573,3576,3579],{},[2452,3574,3575],{},"Go to Actions → Release → Run workflow",[2452,3577,3578],{},"Leave \"Version override\" empty for automatic version inference",[2452,3580,3581],{},"Click \"Run workflow\"",[962,3583,3584],{},"The system will:",[2449,3586,3587,3590,3593,3596],{},[2452,3588,3589],{},"Automatically determine the next version from conventional commits",[2452,3591,3592],{},"Create a git tag",[2452,3594,3595],{},"Generate release notes via GoReleaser",[2452,3597,3598],{},"Publish the release to GitHub",[2666,3600,3602],{"id":3601},"commit-conventions-for-versioning","Commit Conventions for Versioning",[2449,3604,3605,3610,3615,3621],{},[2452,3606,3607,3609],{},[1050,3608,3522],{}," new features (minor version: 1.2.0 → 1.3.0)",[2452,3611,3612,3614],{},[1050,3613,3528],{}," bug fixes (patch version: 1.2.0 → 1.2.1)",[2452,3616,3617,3620],{},[1050,3618,3619],{},"feat!:"," breaking changes (major version: 1.2.0 → 2.0.0)",[2452,3622,3623,2283,3625,2283,3627,3629],{},[1050,3624,3534],{},[1050,3626,3540],{},[1050,3628,3558],{}," no version change",[962,3631,3632,3633],{},"Example: ",[1050,3634,3635],{},"feat(factory): add batch delete support",[2666,3637,3639],{"id":3638},"version-preview-on-pull-requests","Version Preview on Pull Requests",[962,3641,3642],{},"Every PR automatically shows the next version that will be created:",[2449,3644,3645,3648,3651],{},[2452,3646,3647],{},"Check PR comments for \"Version Preview\"",[2452,3649,3650],{},"Updates automatically as you add commits",[2452,3652,3653],{},"Helps verify your commits have the intended effect",[1036,3655,3657],{"id":3656},"questions","Questions?",[2449,3659,3660,3663,3666],{},[2452,3661,3662],{},"Open an issue for questions",[2452,3664,3665],{},"Check existing issues first",[2452,3667,3668],{},"Be patient and respectful",[962,3670,3671],{},"Thank you for contributing to edamame!",[2728,3673,3674],{},"html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":34,"searchDepth":19,"depth":19,"links":3676},[3677,3678,3679,3684,3689,3690,3691,3692,3693,3698],{"id":3207,"depth":19,"text":3208},{"id":3214,"depth":19,"text":3215},{"id":3262,"depth":19,"text":3263,"children":3680},[3681,3682,3683],{"id":3266,"depth":40,"text":3267},{"id":3288,"depth":40,"text":456},{"id":2655,"depth":40,"text":2656},{"id":3330,"depth":19,"text":3331,"children":3685},[3686,3687,3688],{"id":3334,"depth":40,"text":3335},{"id":3352,"depth":40,"text":3353},{"id":3367,"depth":40,"text":3368},{"id":3388,"depth":19,"text":3389},{"id":3420,"depth":19,"text":456},{"id":3499,"depth":19,"text":3500},{"id":3511,"depth":19,"text":3512},{"id":3562,"depth":19,"text":3563,"children":3694},[3695,3696,3697],{"id":3566,"depth":40,"text":3567},{"id":3601,"depth":40,"text":3602},{"id":3638,"depth":40,"text":3639},{"id":3656,"depth":19,"text":3657},{},"/contributing",{"title":2709,"description":3204},"QcMmj_qs3DqAuZIWSIkA2CW6kAKXQPr0ftDXK0qwA9k",{"id":3704,"title":519,"author":3705,"body":3706,"description":521,"extension":2746,"meta":8532,"navigation":1117,"path":518,"published":8533,"readtime":8534,"seo":8535,"stem":923,"tags":8536,"updated":8540,"__hash__":8541},"edamame/v1.0.3/4.cookbook/1.llm-integration.md","zoobzio",{"type":955,"value":3707,"toc":8518},[3708,3711,3713,3716,3733,3739,3742,3745,4144,4147,4150,5077,5080,5214,5217,5677,5680,5683,5689,5692,5695,6659,6662,6923,6926,6929,7481,7484,7487,7765,7768,7771,8014,8017,8020,8474,8477,8515],[958,3709,519],{"id":3710},"llm-integration",[962,3712,525],{},[1036,3714,528],{"id":3715},"the-pattern",[2827,3717,3718,3721,3724,3727,3730],{},[2452,3719,3720],{},"Define statements with descriptive names and descriptions",[2452,3722,3723],{},"Collect statement metadata into a registry",[2452,3725,3726],{},"Serialize registry as JSON for LLM context",[2452,3728,3729],{},"LLM selects statement by name and provides params",[2452,3731,3732],{},"Execute statement with LLM-provided inputs",[1044,3734,3737],{"className":3735,"code":3736,"language":3506},[3504],"User Query → LLM (with statement metadata) → Statement Name + Params → Edamame → Results\n",[1050,3738,3736],{"__ignoreMap":34},[1036,3740,129],{"id":3741},"defining-statements",[962,3743,3744],{},"Statements are self-describing with name, description, params, and tags:",[1044,3746,3748],{"className":1046,"code":3747,"language":1048,"meta":34,"style":34},"var (\n    QueryAll = edamame.NewQueryStatement(\"query-all\", \"Query all users\", edamame.QuerySpec{})\n\n    ByRole = edamame.NewQueryStatement(\"by-role\", \"Find users by role\", edamame.QuerySpec{\n        Where: []edamame.ConditionSpec{\n            {Field: \"role\", Operator: \"=\", Param: \"role\"},\n        },\n    }, \"filter\", \"security\")\n\n    ActiveAdults = edamame.NewQueryStatement(\"active-adults\", \"Find active users over 18\", edamame.QuerySpec{\n        Where: []edamame.ConditionSpec{\n            {Field: \"active\", Operator: \"=\", Param: \"active\"},\n            {Field: \"age\", Operator: \">=\", Param: \"min_age\"},\n        },\n    }, \"filter\")\n\n    SelectByID = edamame.NewSelectStatement(\"select-by-id\", \"Select a single user by ID\", edamame.SelectSpec{\n        Where: []edamame.ConditionSpec{\n            {Field: \"id\", Operator: \"=\", Param: \"id\"},\n        },\n    })\n\n    CountAll = edamame.NewAggregateStatement(\"count-all\", \"Count all users\", edamame.AggCount, edamame.AggregateSpec{})\n)\n",[1050,3749,3750,3756,3786,3790,3823,3839,3868,3872,3887,3891,3924,3940,3968,3999,4003,4011,4015,4046,4062,4090,4094,4098,4102,4140],{"__ignoreMap":34},[1053,3751,3752,3754],{"class":1055,"line":9},[1053,3753,1065],{"class":1064},[1053,3755,1069],{"class":1068},[1053,3757,3758,3760,3762,3764,3766,3768,3770,3772,3774,3776,3778,3780,3782,3784],{"class":1055,"line":19},[1053,3759,1075],{"class":1074},[1053,3761,1078],{"class":1074},[1053,3763,1081],{"class":1074},[1053,3765,1084],{"class":1068},[1053,3767,609],{"class":1087},[1053,3769,1090],{"class":1068},[1053,3771,1094],{"class":1093},[1053,3773,1097],{"class":1068},[1053,3775,1100],{"class":1093},[1053,3777,1097],{"class":1068},[1053,3779,1081],{"class":1105},[1053,3781,1084],{"class":1068},[1053,3783,144],{"class":1105},[1053,3785,1112],{"class":1068},[1053,3787,3788],{"class":1055,"line":40},[1053,3789,1118],{"emptyLinePlaceholder":1117},[1053,3791,3792,3795,3797,3799,3801,3803,3805,3808,3810,3813,3815,3817,3819,3821],{"class":1055,"line":674},[1053,3793,3794],{"class":1074},"    ByRole",[1053,3796,1078],{"class":1074},[1053,3798,1081],{"class":1074},[1053,3800,1084],{"class":1068},[1053,3802,609],{"class":1087},[1053,3804,1090],{"class":1068},[1053,3806,3807],{"class":1093},"\"by-role\"",[1053,3809,1097],{"class":1068},[1053,3811,3812],{"class":1093}," \"Find users by role\"",[1053,3814,1097],{"class":1068},[1053,3816,1081],{"class":1105},[1053,3818,1084],{"class":1068},[1053,3820,144],{"class":1105},[1053,3822,1153],{"class":1068},[1053,3824,3825,3827,3829,3831,3833,3835,3837],{"class":1055,"line":1121},[1053,3826,1160],{"class":1159},[1053,3828,1163],{"class":1068},[1053,3830,1216],{"class":1068},[1053,3832,960],{"class":1105},[1053,3834,1084],{"class":1068},[1053,3836,831],{"class":1105},[1053,3838,1153],{"class":1068},[1053,3840,3841,3843,3845,3847,3850,3852,3854,3856,3858,3860,3862,3864,3866],{"class":1055,"line":1156},[1053,3842,1904],{"class":1068},[1053,3844,1178],{"class":1159},[1053,3846,1163],{"class":1068},[1053,3848,3849],{"class":1093}," \"role\"",[1053,3851,1097],{"class":1068},[1053,3853,1188],{"class":1159},[1053,3855,1163],{"class":1068},[1053,3857,1193],{"class":1093},[1053,3859,1097],{"class":1068},[1053,3861,1198],{"class":1159},[1053,3863,1163],{"class":1068},[1053,3865,3849],{"class":1093},[1053,3867,1929],{"class":1068},[1053,3869,3870],{"class":1055,"line":1208},[1053,3871,1935],{"class":1068},[1053,3873,3874,3877,3880,3882,3885],{"class":1055,"line":1246},[1053,3875,3876],{"class":1068},"    },",[1053,3878,3879],{"class":1093}," \"filter\"",[1053,3881,1097],{"class":1068},[1053,3883,3884],{"class":1093}," \"security\"",[1053,3886,1361],{"class":1068},[1053,3888,3889],{"class":1055,"line":1266},[1053,3890,1118],{"emptyLinePlaceholder":1117},[1053,3892,3893,3896,3898,3900,3902,3904,3906,3909,3911,3914,3916,3918,3920,3922],{"class":1055,"line":1272},[1053,3894,3895],{"class":1074},"    ActiveAdults",[1053,3897,1078],{"class":1074},[1053,3899,1081],{"class":1074},[1053,3901,1084],{"class":1068},[1053,3903,609],{"class":1087},[1053,3905,1090],{"class":1068},[1053,3907,3908],{"class":1093},"\"active-adults\"",[1053,3910,1097],{"class":1068},[1053,3912,3913],{"class":1093}," \"Find active users over 18\"",[1053,3915,1097],{"class":1068},[1053,3917,1081],{"class":1105},[1053,3919,1084],{"class":1068},[1053,3921,144],{"class":1105},[1053,3923,1153],{"class":1068},[1053,3925,3926,3928,3930,3932,3934,3936,3938],{"class":1055,"line":1277},[1053,3927,1160],{"class":1159},[1053,3929,1163],{"class":1068},[1053,3931,1216],{"class":1068},[1053,3933,960],{"class":1105},[1053,3935,1084],{"class":1068},[1053,3937,831],{"class":1105},[1053,3939,1153],{"class":1068},[1053,3941,3942,3944,3946,3948,3950,3952,3954,3956,3958,3960,3962,3964,3966],{"class":1055,"line":1311},[1053,3943,1904],{"class":1068},[1053,3945,1178],{"class":1159},[1053,3947,1163],{"class":1068},[1053,3949,1432],{"class":1093},[1053,3951,1097],{"class":1068},[1053,3953,1188],{"class":1159},[1053,3955,1163],{"class":1068},[1053,3957,1193],{"class":1093},[1053,3959,1097],{"class":1068},[1053,3961,1198],{"class":1159},[1053,3963,1163],{"class":1068},[1053,3965,1432],{"class":1093},[1053,3967,1929],{"class":1068},[1053,3969,3970,3972,3974,3976,3979,3981,3983,3985,3988,3990,3992,3994,3997],{"class":1055,"line":1353},[1053,3971,1904],{"class":1068},[1053,3973,1178],{"class":1159},[1053,3975,1163],{"class":1068},[1053,3977,3978],{"class":1093}," \"age\"",[1053,3980,1097],{"class":1068},[1053,3982,1188],{"class":1159},[1053,3984,1163],{"class":1068},[1053,3986,3987],{"class":1093}," \">=\"",[1053,3989,1097],{"class":1068},[1053,3991,1198],{"class":1159},[1053,3993,1163],{"class":1068},[1053,3995,3996],{"class":1093}," \"min_age\"",[1053,3998,1929],{"class":1068},[1053,4000,4001],{"class":1055,"line":1358},[1053,4002,1935],{"class":1068},[1053,4004,4005,4007,4009],{"class":1055,"line":1364},[1053,4006,3876],{"class":1068},[1053,4008,3879],{"class":1093},[1053,4010,1361],{"class":1068},[1053,4012,4013],{"class":1055,"line":1369},[1053,4014,1118],{"emptyLinePlaceholder":1117},[1053,4016,4017,4019,4021,4023,4025,4027,4029,4031,4033,4036,4038,4040,4042,4044],{"class":1055,"line":1375},[1053,4018,1280],{"class":1074},[1053,4020,1078],{"class":1074},[1053,4022,1081],{"class":1074},[1053,4024,1084],{"class":1068},[1053,4026,614],{"class":1087},[1053,4028,1090],{"class":1068},[1053,4030,1293],{"class":1093},[1053,4032,1097],{"class":1068},[1053,4034,4035],{"class":1093}," \"Select a single user by ID\"",[1053,4037,1097],{"class":1068},[1053,4039,1081],{"class":1105},[1053,4041,1084],{"class":1068},[1053,4043,149],{"class":1105},[1053,4045,1153],{"class":1068},[1053,4047,4048,4050,4052,4054,4056,4058,4060],{"class":1055,"line":1438},[1053,4049,1160],{"class":1159},[1053,4051,1163],{"class":1068},[1053,4053,1216],{"class":1068},[1053,4055,960],{"class":1105},[1053,4057,1084],{"class":1068},[1053,4059,831],{"class":1105},[1053,4061,1153],{"class":1068},[1053,4063,4064,4066,4068,4070,4072,4074,4076,4078,4080,4082,4084,4086,4088],{"class":1055,"line":1666},[1053,4065,1904],{"class":1068},[1053,4067,1178],{"class":1159},[1053,4069,1163],{"class":1068},[1053,4071,1332],{"class":1093},[1053,4073,1097],{"class":1068},[1053,4075,1188],{"class":1159},[1053,4077,1163],{"class":1068},[1053,4079,1193],{"class":1093},[1053,4081,1097],{"class":1068},[1053,4083,1198],{"class":1159},[1053,4085,1163],{"class":1068},[1053,4087,1332],{"class":1093},[1053,4089,1929],{"class":1068},[1053,4091,4092],{"class":1055,"line":1671},[1053,4093,1935],{"class":1068},[1053,4095,4096],{"class":1055,"line":1677},[1053,4097,1269],{"class":1068},[1053,4099,4100],{"class":1055,"line":1684},[1053,4101,1118],{"emptyLinePlaceholder":1117},[1053,4103,4104,4106,4108,4110,4112,4114,4116,4118,4120,4122,4124,4126,4128,4130,4132,4134,4136,4138],{"class":1055,"line":1715},[1053,4105,1805],{"class":1074},[1053,4107,1078],{"class":1074},[1053,4109,1081],{"class":1074},[1053,4111,1084],{"class":1068},[1053,4113,629],{"class":1087},[1053,4115,1090],{"class":1068},[1053,4117,1818],{"class":1093},[1053,4119,1097],{"class":1068},[1053,4121,1823],{"class":1093},[1053,4123,1097],{"class":1068},[1053,4125,1081],{"class":1074},[1053,4127,1084],{"class":1068},[1053,4129,1832],{"class":1074},[1053,4131,1097],{"class":1068},[1053,4133,1081],{"class":1105},[1053,4135,1084],{"class":1068},[1053,4137,164],{"class":1105},[1053,4139,1112],{"class":1068},[1053,4141,4142],{"class":1055,"line":1720},[1053,4143,1361],{"class":1068},[1036,4145,537],{"id":4146},"building-a-statement-registry",[962,4148,4149],{},"Create a registry to collect statements for LLM consumption:",[1044,4151,4153],{"className":1046,"code":4152,"language":1048,"meta":34,"style":34},"type StatementInfo struct {\n    Name        string      `json:\"name\"`\n    Description string      `json:\"description\"`\n    Type        string      `json:\"type\"`\n    Params      []ParamInfo `json:\"params\"`\n    Tags        []string    `json:\"tags,omitempty\"`\n}\n\ntype ParamInfo struct {\n    Name     string `json:\"name\"`\n    Type     string `json:\"type\"`\n    Required bool   `json:\"required\"`\n}\n\ntype StatementRegistry struct {\n    Table      string          `json:\"table\"`\n    Queries    []StatementInfo `json:\"queries,omitempty\"`\n    Selects    []StatementInfo `json:\"selects,omitempty\"`\n    Updates    []StatementInfo `json:\"updates,omitempty\"`\n    Deletes    []StatementInfo `json:\"deletes,omitempty\"`\n    Aggregates []StatementInfo `json:\"aggregates,omitempty\"`\n}\n\nfunc NewRegistry(table string) *StatementRegistry {\n    return &StatementRegistry{Table: table}\n}\n\nfunc (r *StatementRegistry) AddQuery(stmt edamame.QueryStatement) {\n    r.Queries = append(r.Queries, toStatementInfo(stmt.Name(), stmt.Description(), \"query\", stmt.Params(), stmt.Tags()))\n}\n\nfunc (r *StatementRegistry) AddSelect(stmt edamame.SelectStatement) {\n    r.Selects = append(r.Selects, toStatementInfo(stmt.Name(), stmt.Description(), \"select\", stmt.Params(), stmt.Tags()))\n}\n\nfunc (r *StatementRegistry) AddAggregate(stmt edamame.AggregateStatement) {\n    r.Aggregates = append(r.Aggregates, toStatementInfo(stmt.Name(), stmt.Description(), \"aggregate\", stmt.Params(), stmt.Tags()))\n}\n\nfunc toStatementInfo(name, desc, typ string, params []edamame.ParamSpec, tags []string) StatementInfo {\n    info := StatementInfo{\n        Name:        name,\n        Description: desc,\n        Type:        typ,\n        Tags:        tags,\n    }\n    for _, p := range params {\n        info.Params = append(info.Params, ParamInfo{\n            Name:     p.Name,\n            Type:     p.Type,\n            Required: p.Required,\n        })\n    }\n    return info\n}\n\nfunc (r *StatementRegistry) JSON() (string, error) {\n    data, err := json.MarshalIndent(r, \"\", \"  \")\n    return string(data), err\n}\n",[1050,4154,4155,4166,4176,4186,4196,4210,4223,4227,4231,4242,4252,4261,4272,4276,4280,4291,4302,4316,4328,4340,4352,4364,4368,4372,4397,4419,4423,4427,4462,4533,4537,4541,4572,4635,4639,4643,4674,4737,4741,4745,4796,4807,4820,4831,4843,4855,4860,4881,4909,4925,4941,4957,4963,4968,4976,4981,4986,5019,5055,5072],{"__ignoreMap":34},[1053,4156,4157,4159,4162,4164],{"class":1055,"line":9},[1053,4158,1605],{"class":1064},[1053,4160,4161],{"class":1105}," StatementInfo",[1053,4163,1611],{"class":1064},[1053,4165,1614],{"class":1068},[1053,4167,4168,4170,4173],{"class":1055,"line":19},[1053,4169,1641],{"class":1159},[1053,4171,4172],{"class":1105},"        string",[1053,4174,4175],{"class":1093},"      `json:\"name\"`\n",[1053,4177,4178,4181,4183],{"class":1055,"line":40},[1053,4179,4180],{"class":1159},"    Description",[1053,4182,1655],{"class":1105},[1053,4184,4185],{"class":1093},"      `json:\"description\"`\n",[1053,4187,4188,4191,4193],{"class":1055,"line":674},[1053,4189,4190],{"class":1159},"    Type",[1053,4192,4172],{"class":1105},[1053,4194,4195],{"class":1093},"      `json:\"type\"`\n",[1053,4197,4198,4201,4204,4207],{"class":1055,"line":1121},[1053,4199,4200],{"class":1159},"    Params",[1053,4202,4203],{"class":1068},"      []",[1053,4205,4206],{"class":1105},"ParamInfo",[1053,4208,4209],{"class":1093}," `json:\"params\"`\n",[1053,4211,4212,4215,4218,4220],{"class":1055,"line":1156},[1053,4213,4214],{"class":1159},"    Tags",[1053,4216,4217],{"class":1068},"        []",[1053,4219,1415],{"class":1105},[1053,4221,4222],{"class":1093},"    `json:\"tags,omitempty\"`\n",[1053,4224,4225],{"class":1055,"line":1208},[1053,4226,1663],{"class":1068},[1053,4228,4229],{"class":1055,"line":1246},[1053,4230,1118],{"emptyLinePlaceholder":1117},[1053,4232,4233,4235,4238,4240],{"class":1055,"line":1266},[1053,4234,1605],{"class":1064},[1053,4236,4237],{"class":1105}," ParamInfo",[1053,4239,1611],{"class":1064},[1053,4241,1614],{"class":1068},[1053,4243,4244,4246,4249],{"class":1055,"line":1272},[1053,4245,1641],{"class":1159},[1053,4247,4248],{"class":1105},"     string",[1053,4250,4251],{"class":1093}," `json:\"name\"`\n",[1053,4253,4254,4256,4258],{"class":1055,"line":1277},[1053,4255,4190],{"class":1159},[1053,4257,4248],{"class":1105},[1053,4259,4260],{"class":1093}," `json:\"type\"`\n",[1053,4262,4263,4266,4269],{"class":1055,"line":1311},[1053,4264,4265],{"class":1159},"    Required",[1053,4267,4268],{"class":1105}," bool",[1053,4270,4271],{"class":1093},"   `json:\"required\"`\n",[1053,4273,4274],{"class":1055,"line":1353},[1053,4275,1663],{"class":1068},[1053,4277,4278],{"class":1055,"line":1358},[1053,4279,1118],{"emptyLinePlaceholder":1117},[1053,4281,4282,4284,4287,4289],{"class":1055,"line":1364},[1053,4283,1605],{"class":1064},[1053,4285,4286],{"class":1105}," StatementRegistry",[1053,4288,1611],{"class":1064},[1053,4290,1614],{"class":1068},[1053,4292,4293,4296,4299],{"class":1055,"line":1369},[1053,4294,4295],{"class":1159},"    Table",[1053,4297,4298],{"class":1105},"      string",[1053,4300,4301],{"class":1093},"          `json:\"table\"`\n",[1053,4303,4304,4307,4310,4313],{"class":1055,"line":1375},[1053,4305,4306],{"class":1159},"    Queries",[1053,4308,4309],{"class":1068},"    []",[1053,4311,4312],{"class":1105},"StatementInfo",[1053,4314,4315],{"class":1093}," `json:\"queries,omitempty\"`\n",[1053,4317,4318,4321,4323,4325],{"class":1055,"line":1438},[1053,4319,4320],{"class":1159},"    Selects",[1053,4322,4309],{"class":1068},[1053,4324,4312],{"class":1105},[1053,4326,4327],{"class":1093}," `json:\"selects,omitempty\"`\n",[1053,4329,4330,4333,4335,4337],{"class":1055,"line":1666},[1053,4331,4332],{"class":1159},"    Updates",[1053,4334,4309],{"class":1068},[1053,4336,4312],{"class":1105},[1053,4338,4339],{"class":1093}," `json:\"updates,omitempty\"`\n",[1053,4341,4342,4345,4347,4349],{"class":1055,"line":1671},[1053,4343,4344],{"class":1159},"    Deletes",[1053,4346,4309],{"class":1068},[1053,4348,4312],{"class":1105},[1053,4350,4351],{"class":1093}," `json:\"deletes,omitempty\"`\n",[1053,4353,4354,4357,4359,4361],{"class":1055,"line":1677},[1053,4355,4356],{"class":1159},"    Aggregates",[1053,4358,1216],{"class":1068},[1053,4360,4312],{"class":1105},[1053,4362,4363],{"class":1093}," `json:\"aggregates,omitempty\"`\n",[1053,4365,4366],{"class":1055,"line":1684},[1053,4367,1663],{"class":1068},[1053,4369,4370],{"class":1055,"line":1715},[1053,4371,1118],{"emptyLinePlaceholder":1117},[1053,4373,4374,4376,4379,4381,4383,4385,4388,4392,4395],{"class":1055,"line":1720},[1053,4375,1956],{"class":1064},[1053,4377,4378],{"class":1087}," NewRegistry",[1053,4380,1090],{"class":1068},[1053,4382,2349],{"class":1572},[1053,4384,1655],{"class":1105},[1053,4386,4387],{"class":1068},")",[1053,4389,4391],{"class":4390},"sW3Qg"," *",[1053,4393,4394],{"class":1105},"StatementRegistry",[1053,4396,1614],{"class":1068},[1053,4398,4399,4402,4405,4407,4409,4412,4414,4417],{"class":1055,"line":1751},[1053,4400,4401],{"class":4390},"    return",[1053,4403,4404],{"class":4390}," &",[1053,4406,4394],{"class":1105},[1053,4408,1424],{"class":1068},[1053,4410,4411],{"class":1159},"Table",[1053,4413,1163],{"class":1068},[1053,4415,4416],{"class":1074}," table",[1053,4418,1663],{"class":1068},[1053,4420,4421],{"class":1055,"line":1792},[1053,4422,1663],{"class":1068},[1053,4424,4425],{"class":1055,"line":1797},[1053,4426,1118],{"emptyLinePlaceholder":1117},[1053,4428,4429,4431,4434,4437,4440,4442,4444,4447,4449,4452,4454,4456,4458,4460],{"class":1055,"line":1802},[1053,4430,1956],{"class":1064},[1053,4432,4433],{"class":1068}," (",[1053,4435,4436],{"class":1572},"r ",[1053,4438,4439],{"class":4390},"*",[1053,4441,4394],{"class":1105},[1053,4443,4387],{"class":1068},[1053,4445,4446],{"class":1087}," AddQuery",[1053,4448,1090],{"class":1068},[1053,4450,4451],{"class":1572},"stmt",[1053,4453,1081],{"class":1105},[1053,4455,1084],{"class":1068},[1053,4457,638],{"class":1105},[1053,4459,4387],{"class":1068},[1053,4461,1614],{"class":1068},[1053,4463,4464,4467,4469,4471,4473,4476,4478,4481,4483,4485,4487,4490,4492,4494,4496,4498,4501,4504,4506,4508,4510,4513,4515,4517,4519,4522,4524,4526,4528,4530],{"class":1055,"line":1845},[1053,4465,4466],{"class":1074},"    r",[1053,4468,1084],{"class":1068},[1053,4470,283],{"class":1074},[1053,4472,1078],{"class":1074},[1053,4474,4475],{"class":2305}," append",[1053,4477,1090],{"class":1068},[1053,4479,4480],{"class":1074},"r",[1053,4482,1084],{"class":1068},[1053,4484,283],{"class":1074},[1053,4486,1097],{"class":1068},[1053,4488,4489],{"class":1087}," toStatementInfo",[1053,4491,1090],{"class":1068},[1053,4493,4451],{"class":1074},[1053,4495,1084],{"class":1068},[1053,4497,2321],{"class":1087},[1053,4499,4500],{"class":1068},"(),",[1053,4502,4503],{"class":1074}," stmt",[1053,4505,1084],{"class":1068},[1053,4507,2363],{"class":1087},[1053,4509,4500],{"class":1068},[1053,4511,4512],{"class":1093}," \"query\"",[1053,4514,1097],{"class":1068},[1053,4516,4503],{"class":1074},[1053,4518,1084],{"class":1068},[1053,4520,4521],{"class":1087},"Params",[1053,4523,4500],{"class":1068},[1053,4525,4503],{"class":1074},[1053,4527,1084],{"class":1068},[1053,4529,446],{"class":1087},[1053,4531,4532],{"class":1068},"()))\n",[1053,4534,4535],{"class":1055,"line":1850},[1053,4536,1663],{"class":1068},[1053,4538,4539],{"class":1055,"line":1884},[1053,4540,1118],{"emptyLinePlaceholder":1117},[1053,4542,4543,4545,4547,4549,4551,4553,4555,4558,4560,4562,4564,4566,4568,4570],{"class":1055,"line":1901},[1053,4544,1956],{"class":1064},[1053,4546,4433],{"class":1068},[1053,4548,4436],{"class":1572},[1053,4550,4439],{"class":4390},[1053,4552,4394],{"class":1105},[1053,4554,4387],{"class":1068},[1053,4556,4557],{"class":1087}," AddSelect",[1053,4559,1090],{"class":1068},[1053,4561,4451],{"class":1572},[1053,4563,1081],{"class":1105},[1053,4565,1084],{"class":1068},[1053,4567,643],{"class":1105},[1053,4569,4387],{"class":1068},[1053,4571,1614],{"class":1068},[1053,4573,4574,4576,4578,4580,4582,4584,4586,4588,4590,4592,4594,4596,4598,4600,4602,4604,4606,4608,4610,4612,4614,4617,4619,4621,4623,4625,4627,4629,4631,4633],{"class":1055,"line":1932},[1053,4575,4466],{"class":1074},[1053,4577,1084],{"class":1068},[1053,4579,348],{"class":1074},[1053,4581,1078],{"class":1074},[1053,4583,4475],{"class":2305},[1053,4585,1090],{"class":1068},[1053,4587,4480],{"class":1074},[1053,4589,1084],{"class":1068},[1053,4591,348],{"class":1074},[1053,4593,1097],{"class":1068},[1053,4595,4489],{"class":1087},[1053,4597,1090],{"class":1068},[1053,4599,4451],{"class":1074},[1053,4601,1084],{"class":1068},[1053,4603,2321],{"class":1087},[1053,4605,4500],{"class":1068},[1053,4607,4503],{"class":1074},[1053,4609,1084],{"class":1068},[1053,4611,2363],{"class":1087},[1053,4613,4500],{"class":1068},[1053,4615,4616],{"class":1093}," \"select\"",[1053,4618,1097],{"class":1068},[1053,4620,4503],{"class":1074},[1053,4622,1084],{"class":1068},[1053,4624,4521],{"class":1087},[1053,4626,4500],{"class":1068},[1053,4628,4503],{"class":1074},[1053,4630,1084],{"class":1068},[1053,4632,446],{"class":1087},[1053,4634,4532],{"class":1068},[1053,4636,4637],{"class":1055,"line":1938},[1053,4638,1663],{"class":1068},[1053,4640,4641],{"class":1055,"line":1943},[1053,4642,1118],{"emptyLinePlaceholder":1117},[1053,4644,4645,4647,4649,4651,4653,4655,4657,4660,4662,4664,4666,4668,4670,4672],{"class":1055,"line":1948},[1053,4646,1956],{"class":1064},[1053,4648,4433],{"class":1068},[1053,4650,4436],{"class":1572},[1053,4652,4439],{"class":4390},[1053,4654,4394],{"class":1105},[1053,4656,4387],{"class":1068},[1053,4658,4659],{"class":1087}," AddAggregate",[1053,4661,1090],{"class":1068},[1053,4663,4451],{"class":1572},[1053,4665,1081],{"class":1105},[1053,4667,1084],{"class":1068},[1053,4669,658],{"class":1105},[1053,4671,4387],{"class":1068},[1053,4673,1614],{"class":1068},[1053,4675,4676,4678,4680,4682,4684,4686,4688,4690,4692,4694,4696,4698,4700,4702,4704,4706,4708,4710,4712,4714,4716,4719,4721,4723,4725,4727,4729,4731,4733,4735],{"class":1055,"line":1953},[1053,4677,4466],{"class":1074},[1053,4679,1084],{"class":1068},[1053,4681,398],{"class":1074},[1053,4683,1078],{"class":1074},[1053,4685,4475],{"class":2305},[1053,4687,1090],{"class":1068},[1053,4689,4480],{"class":1074},[1053,4691,1084],{"class":1068},[1053,4693,398],{"class":1074},[1053,4695,1097],{"class":1068},[1053,4697,4489],{"class":1087},[1053,4699,1090],{"class":1068},[1053,4701,4451],{"class":1074},[1053,4703,1084],{"class":1068},[1053,4705,2321],{"class":1087},[1053,4707,4500],{"class":1068},[1053,4709,4503],{"class":1074},[1053,4711,1084],{"class":1068},[1053,4713,2363],{"class":1087},[1053,4715,4500],{"class":1068},[1053,4717,4718],{"class":1093}," \"aggregate\"",[1053,4720,1097],{"class":1068},[1053,4722,4503],{"class":1074},[1053,4724,1084],{"class":1068},[1053,4726,4521],{"class":1087},[1053,4728,4500],{"class":1068},[1053,4730,4503],{"class":1074},[1053,4732,1084],{"class":1068},[1053,4734,446],{"class":1087},[1053,4736,4532],{"class":1068},[1053,4738,4739],{"class":1055,"line":1967},[1053,4740,1663],{"class":1068},[1053,4742,4743],{"class":1055,"line":1999},[1053,4744,1118],{"emptyLinePlaceholder":1117},[1053,4746,4747,4749,4751,4753,4756,4758,4761,4763,4766,4768,4770,4773,4775,4777,4779,4781,4783,4786,4788,4790,4792,4794],{"class":1055,"line":2018},[1053,4748,1956],{"class":1064},[1053,4750,4489],{"class":1087},[1053,4752,1090],{"class":1068},[1053,4754,4755],{"class":1572},"name",[1053,4757,1097],{"class":1068},[1053,4759,4760],{"class":1572}," desc",[1053,4762,1097],{"class":1068},[1053,4764,4765],{"class":1572}," typ",[1053,4767,1655],{"class":1105},[1053,4769,1097],{"class":1068},[1053,4771,4772],{"class":1572}," params",[1053,4774,1216],{"class":1068},[1053,4776,960],{"class":1105},[1053,4778,1084],{"class":1068},[1053,4780,846],{"class":1105},[1053,4782,1097],{"class":1068},[1053,4784,4785],{"class":1572}," tags",[1053,4787,1216],{"class":1068},[1053,4789,1415],{"class":1105},[1053,4791,4387],{"class":1068},[1053,4793,4161],{"class":1105},[1053,4795,1614],{"class":1068},[1053,4797,4798,4801,4803,4805],{"class":1055,"line":2023},[1053,4799,4800],{"class":1074},"    info",[1053,4802,1386],{"class":1074},[1053,4804,4161],{"class":1105},[1053,4806,1153],{"class":1068},[1053,4808,4809,4812,4814,4817],{"class":1055,"line":2029},[1053,4810,4811],{"class":1159},"        Name",[1053,4813,1163],{"class":1068},[1053,4815,4816],{"class":1074},"        name",[1053,4818,4819],{"class":1068},",\n",[1053,4821,4822,4825,4827,4829],{"class":1055,"line":2075},[1053,4823,4824],{"class":1159},"        Description",[1053,4826,1163],{"class":1068},[1053,4828,4760],{"class":1074},[1053,4830,4819],{"class":1068},[1053,4832,4833,4836,4838,4841],{"class":1055,"line":2080},[1053,4834,4835],{"class":1159},"        Type",[1053,4837,1163],{"class":1068},[1053,4839,4840],{"class":1074},"        typ",[1053,4842,4819],{"class":1068},[1053,4844,4845,4848,4850,4853],{"class":1055,"line":2086},[1053,4846,4847],{"class":1159},"        Tags",[1053,4849,1163],{"class":1068},[1053,4851,4852],{"class":1074},"        tags",[1053,4854,4819],{"class":1068},[1053,4856,4857],{"class":1055,"line":2120},[1053,4858,4859],{"class":1068},"    }\n",[1053,4861,4862,4865,4867,4869,4872,4874,4877,4879],{"class":1055,"line":2169},[1053,4863,4864],{"class":4390},"    for",[1053,4866,1383],{"class":1074},[1053,4868,1097],{"class":1068},[1053,4870,4871],{"class":1074}," p",[1053,4873,1386],{"class":1074},[1053,4875,4876],{"class":4390}," range",[1053,4878,4772],{"class":1074},[1053,4880,1614],{"class":1068},[1053,4882,4883,4886,4888,4890,4892,4894,4896,4899,4901,4903,4905,4907],{"class":1055,"line":2203},[1053,4884,4885],{"class":1074},"        info",[1053,4887,1084],{"class":1068},[1053,4889,4521],{"class":1074},[1053,4891,1078],{"class":1074},[1053,4893,4475],{"class":2305},[1053,4895,1090],{"class":1068},[1053,4897,4898],{"class":1074},"info",[1053,4900,1084],{"class":1068},[1053,4902,4521],{"class":1074},[1053,4904,1097],{"class":1068},[1053,4906,4237],{"class":1105},[1053,4908,1153],{"class":1068},[1053,4910,4911,4914,4916,4919,4921,4923],{"class":1055,"line":2252},[1053,4912,4913],{"class":1159},"            Name",[1053,4915,1163],{"class":1068},[1053,4917,4918],{"class":1074},"     p",[1053,4920,1084],{"class":1068},[1053,4922,2321],{"class":1074},[1053,4924,4819],{"class":1068},[1053,4926,4927,4930,4932,4934,4936,4939],{"class":1055,"line":2257},[1053,4928,4929],{"class":1159},"            Type",[1053,4931,1163],{"class":1068},[1053,4933,4918],{"class":1074},[1053,4935,1084],{"class":1068},[1053,4937,4938],{"class":1074},"Type",[1053,4940,4819],{"class":1068},[1053,4942,4943,4946,4948,4950,4952,4955],{"class":1055,"line":2341},[1053,4944,4945],{"class":1159},"            Required",[1053,4947,1163],{"class":1068},[1053,4949,4871],{"class":1074},[1053,4951,1084],{"class":1068},[1053,4953,4954],{"class":1074},"Required",[1053,4956,4819],{"class":1068},[1053,4958,4960],{"class":1055,"line":4959},52,[1053,4961,4962],{"class":1068},"        })\n",[1053,4964,4966],{"class":1055,"line":4965},53,[1053,4967,4859],{"class":1068},[1053,4969,4971,4973],{"class":1055,"line":4970},54,[1053,4972,4401],{"class":4390},[1053,4974,4975],{"class":1074}," info\n",[1053,4977,4979],{"class":1055,"line":4978},55,[1053,4980,1663],{"class":1068},[1053,4982,4984],{"class":1055,"line":4983},56,[1053,4985,1118],{"emptyLinePlaceholder":1117},[1053,4987,4989,4991,4993,4995,4997,4999,5001,5004,5006,5008,5010,5012,5015,5017],{"class":1055,"line":4988},57,[1053,4990,1956],{"class":1064},[1053,4992,4433],{"class":1068},[1053,4994,4436],{"class":1572},[1053,4996,4439],{"class":4390},[1053,4998,4394],{"class":1105},[1053,5000,4387],{"class":1068},[1053,5002,5003],{"class":1087}," JSON",[1053,5005,1962],{"class":1068},[1053,5007,4433],{"class":1068},[1053,5009,1415],{"class":1105},[1053,5011,1097],{"class":1068},[1053,5013,5014],{"class":1105}," error",[1053,5016,4387],{"class":1068},[1053,5018,1614],{"class":1068},[1053,5020,5022,5025,5027,5029,5031,5034,5036,5039,5041,5043,5045,5048,5050,5053],{"class":1055,"line":5021},58,[1053,5023,5024],{"class":1074},"    data",[1053,5026,1097],{"class":1068},[1053,5028,2611],{"class":1074},[1053,5030,1386],{"class":1074},[1053,5032,5033],{"class":1074}," json",[1053,5035,1084],{"class":1068},[1053,5037,5038],{"class":1087},"MarshalIndent",[1053,5040,1090],{"class":1068},[1053,5042,4480],{"class":1074},[1053,5044,1097],{"class":1068},[1053,5046,5047],{"class":1093}," \"\"",[1053,5049,1097],{"class":1068},[1053,5051,5052],{"class":1093}," \"  \"",[1053,5054,1361],{"class":1068},[1053,5056,5058,5060,5062,5064,5067,5069],{"class":1055,"line":5057},59,[1053,5059,4401],{"class":4390},[1053,5061,1655],{"class":1105},[1053,5063,1090],{"class":1068},[1053,5065,5066],{"class":1074},"data",[1053,5068,2313],{"class":1068},[1053,5070,5071],{"class":1074}," err\n",[1053,5073,5075],{"class":1055,"line":5074},60,[1053,5076,1663],{"class":1068},[1036,5078,542],{"id":5079},"exporting-for-llm-context",[1044,5081,5083],{"className":1046,"code":5082,"language":1048,"meta":34,"style":34},"// Build registry\nregistry := NewRegistry(\"users\")\nregistry.AddQuery(QueryAll)\nregistry.AddQuery(ByRole)\nregistry.AddQuery(ActiveAdults)\nregistry.AddSelect(SelectByID)\nregistry.AddAggregate(CountAll)\n\n// Export as JSON\njson, _ := registry.JSON()\n",[1050,5084,5085,5090,5106,5122,5137,5152,5168,5184,5188,5193],{"__ignoreMap":34},[1053,5086,5087],{"class":1055,"line":9},[1053,5088,5089],{"class":1058},"// Build registry\n",[1053,5091,5092,5095,5097,5099,5101,5104],{"class":1055,"line":19},[1053,5093,5094],{"class":1074},"registry",[1053,5096,1386],{"class":1074},[1053,5098,4378],{"class":1087},[1053,5100,1090],{"class":1068},[1053,5102,5103],{"class":1093},"\"users\"",[1053,5105,1361],{"class":1068},[1053,5107,5108,5110,5112,5115,5117,5120],{"class":1055,"line":40},[1053,5109,5094],{"class":1074},[1053,5111,1084],{"class":1068},[1053,5113,5114],{"class":1087},"AddQuery",[1053,5116,1090],{"class":1068},[1053,5118,5119],{"class":1074},"QueryAll",[1053,5121,1361],{"class":1068},[1053,5123,5124,5126,5128,5130,5132,5135],{"class":1055,"line":674},[1053,5125,5094],{"class":1074},[1053,5127,1084],{"class":1068},[1053,5129,5114],{"class":1087},[1053,5131,1090],{"class":1068},[1053,5133,5134],{"class":1074},"ByRole",[1053,5136,1361],{"class":1068},[1053,5138,5139,5141,5143,5145,5147,5150],{"class":1055,"line":1121},[1053,5140,5094],{"class":1074},[1053,5142,1084],{"class":1068},[1053,5144,5114],{"class":1087},[1053,5146,1090],{"class":1068},[1053,5148,5149],{"class":1074},"ActiveAdults",[1053,5151,1361],{"class":1068},[1053,5153,5154,5156,5158,5161,5163,5166],{"class":1055,"line":1156},[1053,5155,5094],{"class":1074},[1053,5157,1084],{"class":1068},[1053,5159,5160],{"class":1087},"AddSelect",[1053,5162,1090],{"class":1068},[1053,5164,5165],{"class":1074},"SelectByID",[1053,5167,1361],{"class":1068},[1053,5169,5170,5172,5174,5177,5179,5182],{"class":1055,"line":1208},[1053,5171,5094],{"class":1074},[1053,5173,1084],{"class":1068},[1053,5175,5176],{"class":1087},"AddAggregate",[1053,5178,1090],{"class":1068},[1053,5180,5181],{"class":1074},"CountAll",[1053,5183,1361],{"class":1068},[1053,5185,5186],{"class":1055,"line":1246},[1053,5187,1118],{"emptyLinePlaceholder":1117},[1053,5189,5190],{"class":1055,"line":1266},[1053,5191,5192],{"class":1058},"// Export as JSON\n",[1053,5194,5195,5198,5200,5202,5204,5207,5209,5212],{"class":1055,"line":1272},[1053,5196,5197],{"class":1074},"json",[1053,5199,1097],{"class":1068},[1053,5201,1383],{"class":1074},[1053,5203,1386],{"class":1074},[1053,5205,5206],{"class":1074}," registry",[1053,5208,1084],{"class":1068},[1053,5210,5211],{"class":1087},"JSON",[1053,5213,2015],{"class":1068},[962,5215,5216],{},"Example output:",[1044,5218,5221],{"className":5219,"code":5220,"language":5197,"meta":34,"style":34},"language-json shiki shiki-themes","{\n  \"table\": \"users\",\n  \"queries\": [\n    {\n      \"name\": \"query-all\",\n      \"description\": \"Query all users\",\n      \"type\": \"query\",\n      \"params\": []\n    },\n    {\n      \"name\": \"by-role\",\n      \"description\": \"Find users by role\",\n      \"type\": \"query\",\n      \"params\": [\n        {\"name\": \"role\", \"type\": \"any\", \"required\": true}\n      ],\n      \"tags\": [\"filter\", \"security\"]\n    },\n    {\n      \"name\": \"active-adults\",\n      \"description\": \"Find active users over 18\",\n      \"type\": \"query\",\n      \"params\": [\n        {\"name\": \"active\", \"type\": \"any\", \"required\": true},\n        {\"name\": \"min_age\", \"type\": \"any\", \"required\": true}\n      ],\n      \"tags\": [\"filter\"]\n    }\n  ],\n  \"selects\": [\n    {\n      \"name\": \"select-by-id\",\n      \"description\": \"Select a single user by ID\",\n      \"type\": \"select\",\n      \"params\": [\n        {\"name\": \"id\", \"type\": \"any\", \"required\": true}\n      ]\n    }\n  ],\n  \"aggregates\": [\n    {\n      \"name\": \"count-all\",\n      \"description\": \"Count all users\",\n      \"type\": \"aggregate\",\n      \"params\": []\n    }\n  ]\n}\n",[1050,5222,5223,5227,5239,5247,5252,5263,5275,5287,5295,5300,5304,5314,5325,5335,5341,5376,5381,5400,5404,5408,5418,5429,5439,5445,5473,5502,5506,5516,5520,5525,5532,5536,5546,5557,5568,5574,5602,5607,5611,5615,5622,5626,5636,5647,5658,5664,5668,5673],{"__ignoreMap":34},[1053,5224,5225],{"class":1055,"line":9},[1053,5226,1153],{"class":1548},[1053,5228,5229,5232,5235,5237],{"class":1055,"line":19},[1053,5230,5231],{"class":1572},"  \"table\"",[1053,5233,5234],{"class":1548},": ",[1053,5236,5103],{"class":1093},[1053,5238,4819],{"class":1548},[1053,5240,5241,5244],{"class":1055,"line":40},[1053,5242,5243],{"class":1572},"  \"queries\"",[1053,5245,5246],{"class":1548},": [\n",[1053,5248,5249],{"class":1055,"line":674},[1053,5250,5251],{"class":1548},"    {\n",[1053,5253,5254,5257,5259,5261],{"class":1055,"line":1121},[1053,5255,5256],{"class":1572},"      \"name\"",[1053,5258,5234],{"class":1548},[1053,5260,1094],{"class":1093},[1053,5262,4819],{"class":1548},[1053,5264,5265,5268,5270,5273],{"class":1055,"line":1156},[1053,5266,5267],{"class":1572},"      \"description\"",[1053,5269,5234],{"class":1548},[1053,5271,5272],{"class":1093},"\"Query all users\"",[1053,5274,4819],{"class":1548},[1053,5276,5277,5280,5282,5285],{"class":1055,"line":1208},[1053,5278,5279],{"class":1572},"      \"type\"",[1053,5281,5234],{"class":1548},[1053,5283,5284],{"class":1093},"\"query\"",[1053,5286,4819],{"class":1548},[1053,5288,5289,5292],{"class":1055,"line":1246},[1053,5290,5291],{"class":1572},"      \"params\"",[1053,5293,5294],{"class":1548},": []\n",[1053,5296,5297],{"class":1055,"line":1266},[1053,5298,5299],{"class":1548},"    },\n",[1053,5301,5302],{"class":1055,"line":1272},[1053,5303,5251],{"class":1548},[1053,5305,5306,5308,5310,5312],{"class":1055,"line":1277},[1053,5307,5256],{"class":1572},[1053,5309,5234],{"class":1548},[1053,5311,3807],{"class":1093},[1053,5313,4819],{"class":1548},[1053,5315,5316,5318,5320,5323],{"class":1055,"line":1311},[1053,5317,5267],{"class":1572},[1053,5319,5234],{"class":1548},[1053,5321,5322],{"class":1093},"\"Find users by role\"",[1053,5324,4819],{"class":1548},[1053,5326,5327,5329,5331,5333],{"class":1055,"line":1353},[1053,5328,5279],{"class":1572},[1053,5330,5234],{"class":1548},[1053,5332,5284],{"class":1093},[1053,5334,4819],{"class":1548},[1053,5336,5337,5339],{"class":1055,"line":1358},[1053,5338,5291],{"class":1572},[1053,5340,5246],{"class":1548},[1053,5342,5343,5346,5349,5351,5354,5356,5359,5361,5364,5366,5369,5371,5374],{"class":1055,"line":1364},[1053,5344,5345],{"class":1548},"        {",[1053,5347,5348],{"class":1572},"\"name\"",[1053,5350,5234],{"class":1548},[1053,5352,5353],{"class":1093},"\"role\"",[1053,5355,2283],{"class":1548},[1053,5357,5358],{"class":1572},"\"type\"",[1053,5360,5234],{"class":1548},[1053,5362,5363],{"class":1093},"\"any\"",[1053,5365,2283],{"class":1548},[1053,5367,5368],{"class":1572},"\"required\"",[1053,5370,5234],{"class":1548},[1053,5372,5373],{"class":1064},"true",[1053,5375,1663],{"class":1548},[1053,5377,5378],{"class":1055,"line":1369},[1053,5379,5380],{"class":1548},"      ],\n",[1053,5382,5383,5386,5389,5392,5394,5397],{"class":1055,"line":1375},[1053,5384,5385],{"class":1572},"      \"tags\"",[1053,5387,5388],{"class":1548},": [",[1053,5390,5391],{"class":1093},"\"filter\"",[1053,5393,2283],{"class":1548},[1053,5395,5396],{"class":1093},"\"security\"",[1053,5398,5399],{"class":1548},"]\n",[1053,5401,5402],{"class":1055,"line":1438},[1053,5403,5299],{"class":1548},[1053,5405,5406],{"class":1055,"line":1666},[1053,5407,5251],{"class":1548},[1053,5409,5410,5412,5414,5416],{"class":1055,"line":1671},[1053,5411,5256],{"class":1572},[1053,5413,5234],{"class":1548},[1053,5415,3908],{"class":1093},[1053,5417,4819],{"class":1548},[1053,5419,5420,5422,5424,5427],{"class":1055,"line":1677},[1053,5421,5267],{"class":1572},[1053,5423,5234],{"class":1548},[1053,5425,5426],{"class":1093},"\"Find active users over 18\"",[1053,5428,4819],{"class":1548},[1053,5430,5431,5433,5435,5437],{"class":1055,"line":1684},[1053,5432,5279],{"class":1572},[1053,5434,5234],{"class":1548},[1053,5436,5284],{"class":1093},[1053,5438,4819],{"class":1548},[1053,5440,5441,5443],{"class":1055,"line":1715},[1053,5442,5291],{"class":1572},[1053,5444,5246],{"class":1548},[1053,5446,5447,5449,5451,5453,5455,5457,5459,5461,5463,5465,5467,5469,5471],{"class":1055,"line":1720},[1053,5448,5345],{"class":1548},[1053,5450,5348],{"class":1572},[1053,5452,5234],{"class":1548},[1053,5454,1866],{"class":1093},[1053,5456,2283],{"class":1548},[1053,5458,5358],{"class":1572},[1053,5460,5234],{"class":1548},[1053,5462,5363],{"class":1093},[1053,5464,2283],{"class":1548},[1053,5466,5368],{"class":1572},[1053,5468,5234],{"class":1548},[1053,5470,5373],{"class":1064},[1053,5472,1929],{"class":1548},[1053,5474,5475,5477,5479,5481,5484,5486,5488,5490,5492,5494,5496,5498,5500],{"class":1055,"line":1751},[1053,5476,5345],{"class":1548},[1053,5478,5348],{"class":1572},[1053,5480,5234],{"class":1548},[1053,5482,5483],{"class":1093},"\"min_age\"",[1053,5485,2283],{"class":1548},[1053,5487,5358],{"class":1572},[1053,5489,5234],{"class":1548},[1053,5491,5363],{"class":1093},[1053,5493,2283],{"class":1548},[1053,5495,5368],{"class":1572},[1053,5497,5234],{"class":1548},[1053,5499,5373],{"class":1064},[1053,5501,1663],{"class":1548},[1053,5503,5504],{"class":1055,"line":1792},[1053,5505,5380],{"class":1548},[1053,5507,5508,5510,5512,5514],{"class":1055,"line":1797},[1053,5509,5385],{"class":1572},[1053,5511,5388],{"class":1548},[1053,5513,5391],{"class":1093},[1053,5515,5399],{"class":1548},[1053,5517,5518],{"class":1055,"line":1802},[1053,5519,4859],{"class":1548},[1053,5521,5522],{"class":1055,"line":1845},[1053,5523,5524],{"class":1548},"  ],\n",[1053,5526,5527,5530],{"class":1055,"line":1850},[1053,5528,5529],{"class":1572},"  \"selects\"",[1053,5531,5246],{"class":1548},[1053,5533,5534],{"class":1055,"line":1884},[1053,5535,5251],{"class":1548},[1053,5537,5538,5540,5542,5544],{"class":1055,"line":1901},[1053,5539,5256],{"class":1572},[1053,5541,5234],{"class":1548},[1053,5543,1293],{"class":1093},[1053,5545,4819],{"class":1548},[1053,5547,5548,5550,5552,5555],{"class":1055,"line":1932},[1053,5549,5267],{"class":1572},[1053,5551,5234],{"class":1548},[1053,5553,5554],{"class":1093},"\"Select a single user by ID\"",[1053,5556,4819],{"class":1548},[1053,5558,5559,5561,5563,5566],{"class":1055,"line":1938},[1053,5560,5279],{"class":1572},[1053,5562,5234],{"class":1548},[1053,5564,5565],{"class":1093},"\"select\"",[1053,5567,4819],{"class":1548},[1053,5569,5570,5572],{"class":1055,"line":1943},[1053,5571,5291],{"class":1572},[1053,5573,5246],{"class":1548},[1053,5575,5576,5578,5580,5582,5584,5586,5588,5590,5592,5594,5596,5598,5600],{"class":1055,"line":1948},[1053,5577,5345],{"class":1548},[1053,5579,5348],{"class":1572},[1053,5581,5234],{"class":1548},[1053,5583,1480],{"class":1093},[1053,5585,2283],{"class":1548},[1053,5587,5358],{"class":1572},[1053,5589,5234],{"class":1548},[1053,5591,5363],{"class":1093},[1053,5593,2283],{"class":1548},[1053,5595,5368],{"class":1572},[1053,5597,5234],{"class":1548},[1053,5599,5373],{"class":1064},[1053,5601,1663],{"class":1548},[1053,5603,5604],{"class":1055,"line":1953},[1053,5605,5606],{"class":1548},"      ]\n",[1053,5608,5609],{"class":1055,"line":1967},[1053,5610,4859],{"class":1548},[1053,5612,5613],{"class":1055,"line":1999},[1053,5614,5524],{"class":1548},[1053,5616,5617,5620],{"class":1055,"line":2018},[1053,5618,5619],{"class":1572},"  \"aggregates\"",[1053,5621,5246],{"class":1548},[1053,5623,5624],{"class":1055,"line":2023},[1053,5625,5251],{"class":1548},[1053,5627,5628,5630,5632,5634],{"class":1055,"line":2029},[1053,5629,5256],{"class":1572},[1053,5631,5234],{"class":1548},[1053,5633,1818],{"class":1093},[1053,5635,4819],{"class":1548},[1053,5637,5638,5640,5642,5645],{"class":1055,"line":2075},[1053,5639,5267],{"class":1572},[1053,5641,5234],{"class":1548},[1053,5643,5644],{"class":1093},"\"Count all users\"",[1053,5646,4819],{"class":1548},[1053,5648,5649,5651,5653,5656],{"class":1055,"line":2080},[1053,5650,5279],{"class":1572},[1053,5652,5234],{"class":1548},[1053,5654,5655],{"class":1093},"\"aggregate\"",[1053,5657,4819],{"class":1548},[1053,5659,5660,5662],{"class":1055,"line":2086},[1053,5661,5291],{"class":1572},[1053,5663,5294],{"class":1548},[1053,5665,5666],{"class":1055,"line":2120},[1053,5667,4859],{"class":1548},[1053,5669,5670],{"class":1055,"line":2169},[1053,5671,5672],{"class":1548},"  ]\n",[1053,5674,5675],{"class":1055,"line":2203},[1053,5676,1663],{"class":1548},[1036,5678,547],{"id":5679},"system-prompt-design",[962,5681,5682],{},"Provide statement metadata in your LLM system prompt:",[1044,5684,5687],{"className":5685,"code":5686,"language":3506},[3504],"You are a database assistant. You have access to the following operations:\n\n{registry_json}\n\nWhen the user asks a question about data:\n1. Identify the appropriate operation\n2. Extract required parameters from the user's request\n3. Respond with JSON: {\"statement\": \"name\", \"type\": \"query|select|...\", \"params\": {...}}\n\nExamples:\n- \"How many users are there?\" → {\"statement\": \"count-all\", \"type\": \"aggregate\", \"params\": {}}\n- \"Find admins\" → {\"statement\": \"by-role\", \"type\": \"query\", \"params\": {\"role\": \"admin\"}}\n- \"Get user 123\" → {\"statement\": \"select-by-id\", \"type\": \"select\", \"params\": {\"id\": 123}}\n",[1050,5688,5686],{"__ignoreMap":34},[1036,5690,552],{"id":5691},"executing-llm-responses",[962,5693,5694],{},"Create a dispatcher that maps statement names to actual statements:",[1044,5696,5698],{"className":1046,"code":5697,"language":1048,"meta":34,"style":34},"type LLMResponse struct {\n    Statement string         `json:\"statement\"`\n    Type      string         `json:\"type\"`\n    Params    map[string]any `json:\"params\"`\n}\n\ntype StatementDispatcher struct {\n    exec       *edamame.Executor[User]\n    queries    map[string]edamame.QueryStatement\n    selects    map[string]edamame.SelectStatement\n    aggregates map[string]edamame.AggregateStatement\n}\n\nfunc NewDispatcher(exec *edamame.Executor[User]) *StatementDispatcher {\n    return &StatementDispatcher{\n        exec:       exec,\n        queries:    make(map[string]edamame.QueryStatement),\n        selects:    make(map[string]edamame.SelectStatement),\n        aggregates: make(map[string]edamame.AggregateStatement),\n    }\n}\n\nfunc (d *StatementDispatcher) RegisterQuery(stmt edamame.QueryStatement) {\n    d.queries[stmt.Name()] = stmt\n}\n\nfunc (d *StatementDispatcher) RegisterSelect(stmt edamame.SelectStatement) {\n    d.selects[stmt.Name()] = stmt\n}\n\nfunc (d *StatementDispatcher) RegisterAggregate(stmt edamame.AggregateStatement) {\n    d.aggregates[stmt.Name()] = stmt\n}\n\nfunc (d *StatementDispatcher) Execute(ctx context.Context, resp LLMResponse) (any, error) {\n    switch resp.Type {\n    case \"query\":\n        stmt, ok := d.queries[resp.Statement]\n        if !ok {\n            return nil, fmt.Errorf(\"unknown query: %s\", resp.Statement)\n        }\n        return d.exec.ExecQuery(ctx, stmt, resp.Params)\n    case \"select\":\n        stmt, ok := d.selects[resp.Statement]\n        if !ok {\n            return nil, fmt.Errorf(\"unknown select: %s\", resp.Statement)\n        }\n        return d.exec.ExecSelect(ctx, stmt, resp.Params)\n    case \"aggregate\":\n        stmt, ok := d.aggregates[resp.Statement]\n        if !ok {\n            return nil, fmt.Errorf(\"unknown aggregate: %s\", resp.Statement)\n        }\n        return d.exec.ExecAggregate(ctx, stmt, resp.Params)\n    default:\n        return nil, fmt.Errorf(\"unknown type: %s\", resp.Type)\n    }\n}\n",[1050,5699,5700,5711,5721,5730,5747,5751,5755,5766,5785,5805,5825,5845,5849,5853,5887,5897,5909,5938,5965,5993,5997,6001,6005,6037,6063,6067,6071,6102,6125,6129,6133,6164,6187,6191,6195,6244,6257,6267,6298,6311,6347,6352,6385,6393,6419,6429,6462,6466,6498,6506,6532,6542,6575,6579,6611,6618,6651,6655],{"__ignoreMap":34},[1053,5701,5702,5704,5707,5709],{"class":1055,"line":9},[1053,5703,1605],{"class":1064},[1053,5705,5706],{"class":1105}," LLMResponse",[1053,5708,1611],{"class":1064},[1053,5710,1614],{"class":1068},[1053,5712,5713,5716,5718],{"class":1055,"line":19},[1053,5714,5715],{"class":1159},"    Statement",[1053,5717,1655],{"class":1105},[1053,5719,5720],{"class":1093},"         `json:\"statement\"`\n",[1053,5722,5723,5725,5727],{"class":1055,"line":40},[1053,5724,4190],{"class":1159},[1053,5726,4298],{"class":1105},[1053,5728,5729],{"class":1093},"         `json:\"type\"`\n",[1053,5731,5732,5734,5737,5739,5741,5743,5745],{"class":1055,"line":674},[1053,5733,4200],{"class":1159},[1053,5735,5736],{"class":1064},"    map",[1053,5738,1412],{"class":1068},[1053,5740,1415],{"class":1105},[1053,5742,1418],{"class":1068},[1053,5744,1421],{"class":1105},[1053,5746,4209],{"class":1093},[1053,5748,5749],{"class":1055,"line":1121},[1053,5750,1663],{"class":1068},[1053,5752,5753],{"class":1055,"line":1156},[1053,5754,1118],{"emptyLinePlaceholder":1117},[1053,5756,5757,5759,5762,5764],{"class":1055,"line":1208},[1053,5758,1605],{"class":1064},[1053,5760,5761],{"class":1105}," StatementDispatcher",[1053,5763,1611],{"class":1064},[1053,5765,1614],{"class":1068},[1053,5767,5768,5770,5773,5775,5777,5779,5781,5783],{"class":1055,"line":1246},[1053,5769,2032],{"class":1159},[1053,5771,5772],{"class":4390},"       *",[1053,5774,960],{"class":1105},[1053,5776,1084],{"class":1068},[1053,5778,110],{"class":1105},[1053,5780,1412],{"class":1068},[1053,5782,2049],{"class":1105},[1053,5784,5399],{"class":1068},[1053,5786,5787,5790,5792,5794,5796,5798,5800,5802],{"class":1055,"line":1266},[1053,5788,5789],{"class":1159},"    queries",[1053,5791,5736],{"class":1064},[1053,5793,1412],{"class":1068},[1053,5795,1415],{"class":1105},[1053,5797,1418],{"class":1068},[1053,5799,960],{"class":1105},[1053,5801,1084],{"class":1068},[1053,5803,5804],{"class":1105},"QueryStatement\n",[1053,5806,5807,5810,5812,5814,5816,5818,5820,5822],{"class":1055,"line":1272},[1053,5808,5809],{"class":1159},"    selects",[1053,5811,5736],{"class":1064},[1053,5813,1412],{"class":1068},[1053,5815,1415],{"class":1105},[1053,5817,1418],{"class":1068},[1053,5819,960],{"class":1105},[1053,5821,1084],{"class":1068},[1053,5823,5824],{"class":1105},"SelectStatement\n",[1053,5826,5827,5830,5832,5834,5836,5838,5840,5842],{"class":1055,"line":1277},[1053,5828,5829],{"class":1159},"    aggregates",[1053,5831,1409],{"class":1064},[1053,5833,1412],{"class":1068},[1053,5835,1415],{"class":1105},[1053,5837,1418],{"class":1068},[1053,5839,960],{"class":1105},[1053,5841,1084],{"class":1068},[1053,5843,5844],{"class":1105},"AggregateStatement\n",[1053,5846,5847],{"class":1055,"line":1311},[1053,5848,1663],{"class":1068},[1053,5850,5851],{"class":1055,"line":1353},[1053,5852,1118],{"emptyLinePlaceholder":1117},[1053,5854,5855,5857,5860,5862,5865,5867,5869,5871,5873,5875,5877,5880,5882,5885],{"class":1055,"line":1358},[1053,5856,1956],{"class":1064},[1053,5858,5859],{"class":1087}," NewDispatcher",[1053,5861,1090],{"class":1068},[1053,5863,5864],{"class":1572},"exec",[1053,5866,4391],{"class":4390},[1053,5868,960],{"class":1105},[1053,5870,1084],{"class":1068},[1053,5872,110],{"class":1105},[1053,5874,1412],{"class":1068},[1053,5876,2049],{"class":1105},[1053,5878,5879],{"class":1068},"])",[1053,5881,4391],{"class":4390},[1053,5883,5884],{"class":1105},"StatementDispatcher",[1053,5886,1614],{"class":1068},[1053,5888,5889,5891,5893,5895],{"class":1055,"line":1364},[1053,5890,4401],{"class":4390},[1053,5892,4404],{"class":4390},[1053,5894,5884],{"class":1105},[1053,5896,1153],{"class":1068},[1053,5898,5899,5902,5904,5907],{"class":1055,"line":1369},[1053,5900,5901],{"class":1159},"        exec",[1053,5903,1163],{"class":1068},[1053,5905,5906],{"class":1074},"       exec",[1053,5908,4819],{"class":1068},[1053,5910,5911,5914,5916,5919,5921,5924,5926,5928,5930,5932,5934,5936],{"class":1055,"line":1375},[1053,5912,5913],{"class":1159},"        queries",[1053,5915,1163],{"class":1068},[1053,5917,5918],{"class":2305},"    make",[1053,5920,1090],{"class":1068},[1053,5922,5923],{"class":1064},"map",[1053,5925,1412],{"class":1068},[1053,5927,1415],{"class":1105},[1053,5929,1418],{"class":1068},[1053,5931,960],{"class":1105},[1053,5933,1084],{"class":1068},[1053,5935,638],{"class":1105},[1053,5937,1263],{"class":1068},[1053,5939,5940,5943,5945,5947,5949,5951,5953,5955,5957,5959,5961,5963],{"class":1055,"line":1438},[1053,5941,5942],{"class":1159},"        selects",[1053,5944,1163],{"class":1068},[1053,5946,5918],{"class":2305},[1053,5948,1090],{"class":1068},[1053,5950,5923],{"class":1064},[1053,5952,1412],{"class":1068},[1053,5954,1415],{"class":1105},[1053,5956,1418],{"class":1068},[1053,5958,960],{"class":1105},[1053,5960,1084],{"class":1068},[1053,5962,643],{"class":1105},[1053,5964,1263],{"class":1068},[1053,5966,5967,5970,5972,5975,5977,5979,5981,5983,5985,5987,5989,5991],{"class":1055,"line":1666},[1053,5968,5969],{"class":1159},"        aggregates",[1053,5971,1163],{"class":1068},[1053,5973,5974],{"class":2305}," make",[1053,5976,1090],{"class":1068},[1053,5978,5923],{"class":1064},[1053,5980,1412],{"class":1068},[1053,5982,1415],{"class":1105},[1053,5984,1418],{"class":1068},[1053,5986,960],{"class":1105},[1053,5988,1084],{"class":1068},[1053,5990,658],{"class":1105},[1053,5992,1263],{"class":1068},[1053,5994,5995],{"class":1055,"line":1671},[1053,5996,4859],{"class":1068},[1053,5998,5999],{"class":1055,"line":1677},[1053,6000,1663],{"class":1068},[1053,6002,6003],{"class":1055,"line":1684},[1053,6004,1118],{"emptyLinePlaceholder":1117},[1053,6006,6007,6009,6011,6014,6016,6018,6020,6023,6025,6027,6029,6031,6033,6035],{"class":1055,"line":1715},[1053,6008,1956],{"class":1064},[1053,6010,4433],{"class":1068},[1053,6012,6013],{"class":1572},"d ",[1053,6015,4439],{"class":4390},[1053,6017,5884],{"class":1105},[1053,6019,4387],{"class":1068},[1053,6021,6022],{"class":1087}," RegisterQuery",[1053,6024,1090],{"class":1068},[1053,6026,4451],{"class":1572},[1053,6028,1081],{"class":1105},[1053,6030,1084],{"class":1068},[1053,6032,638],{"class":1105},[1053,6034,4387],{"class":1068},[1053,6036,1614],{"class":1068},[1053,6038,6039,6042,6044,6047,6049,6051,6053,6055,6058,6060],{"class":1055,"line":1720},[1053,6040,6041],{"class":1074},"    d",[1053,6043,1084],{"class":1068},[1053,6045,6046],{"class":1074},"queries",[1053,6048,1412],{"class":1068},[1053,6050,4451],{"class":1074},[1053,6052,1084],{"class":1068},[1053,6054,2321],{"class":1087},[1053,6056,6057],{"class":1068},"()]",[1053,6059,1078],{"class":1074},[1053,6061,6062],{"class":1074}," stmt\n",[1053,6064,6065],{"class":1055,"line":1751},[1053,6066,1663],{"class":1068},[1053,6068,6069],{"class":1055,"line":1792},[1053,6070,1118],{"emptyLinePlaceholder":1117},[1053,6072,6073,6075,6077,6079,6081,6083,6085,6088,6090,6092,6094,6096,6098,6100],{"class":1055,"line":1797},[1053,6074,1956],{"class":1064},[1053,6076,4433],{"class":1068},[1053,6078,6013],{"class":1572},[1053,6080,4439],{"class":4390},[1053,6082,5884],{"class":1105},[1053,6084,4387],{"class":1068},[1053,6086,6087],{"class":1087}," RegisterSelect",[1053,6089,1090],{"class":1068},[1053,6091,4451],{"class":1572},[1053,6093,1081],{"class":1105},[1053,6095,1084],{"class":1068},[1053,6097,643],{"class":1105},[1053,6099,4387],{"class":1068},[1053,6101,1614],{"class":1068},[1053,6103,6104,6106,6108,6111,6113,6115,6117,6119,6121,6123],{"class":1055,"line":1802},[1053,6105,6041],{"class":1074},[1053,6107,1084],{"class":1068},[1053,6109,6110],{"class":1074},"selects",[1053,6112,1412],{"class":1068},[1053,6114,4451],{"class":1074},[1053,6116,1084],{"class":1068},[1053,6118,2321],{"class":1087},[1053,6120,6057],{"class":1068},[1053,6122,1078],{"class":1074},[1053,6124,6062],{"class":1074},[1053,6126,6127],{"class":1055,"line":1845},[1053,6128,1663],{"class":1068},[1053,6130,6131],{"class":1055,"line":1850},[1053,6132,1118],{"emptyLinePlaceholder":1117},[1053,6134,6135,6137,6139,6141,6143,6145,6147,6150,6152,6154,6156,6158,6160,6162],{"class":1055,"line":1884},[1053,6136,1956],{"class":1064},[1053,6138,4433],{"class":1068},[1053,6140,6013],{"class":1572},[1053,6142,4439],{"class":4390},[1053,6144,5884],{"class":1105},[1053,6146,4387],{"class":1068},[1053,6148,6149],{"class":1087}," RegisterAggregate",[1053,6151,1090],{"class":1068},[1053,6153,4451],{"class":1572},[1053,6155,1081],{"class":1105},[1053,6157,1084],{"class":1068},[1053,6159,658],{"class":1105},[1053,6161,4387],{"class":1068},[1053,6163,1614],{"class":1068},[1053,6165,6166,6168,6170,6173,6175,6177,6179,6181,6183,6185],{"class":1055,"line":1901},[1053,6167,6041],{"class":1074},[1053,6169,1084],{"class":1068},[1053,6171,6172],{"class":1074},"aggregates",[1053,6174,1412],{"class":1068},[1053,6176,4451],{"class":1074},[1053,6178,1084],{"class":1068},[1053,6180,2321],{"class":1087},[1053,6182,6057],{"class":1068},[1053,6184,1078],{"class":1074},[1053,6186,6062],{"class":1074},[1053,6188,6189],{"class":1055,"line":1932},[1053,6190,1663],{"class":1068},[1053,6192,6193],{"class":1055,"line":1938},[1053,6194,1118],{"emptyLinePlaceholder":1117},[1053,6196,6197,6199,6201,6203,6205,6207,6209,6212,6214,6216,6218,6220,6223,6225,6228,6230,6232,6234,6236,6238,6240,6242],{"class":1055,"line":1943},[1053,6198,1956],{"class":1064},[1053,6200,4433],{"class":1068},[1053,6202,6013],{"class":1572},[1053,6204,4439],{"class":4390},[1053,6206,5884],{"class":1105},[1053,6208,4387],{"class":1068},[1053,6210,6211],{"class":1087}," Execute",[1053,6213,1090],{"class":1068},[1053,6215,1399],{"class":1572},[1053,6217,2007],{"class":1105},[1053,6219,1084],{"class":1068},[1053,6221,6222],{"class":1105},"Context",[1053,6224,1097],{"class":1068},[1053,6226,6227],{"class":1572}," resp",[1053,6229,5706],{"class":1105},[1053,6231,4387],{"class":1068},[1053,6233,4433],{"class":1068},[1053,6235,1421],{"class":1105},[1053,6237,1097],{"class":1068},[1053,6239,5014],{"class":1105},[1053,6241,4387],{"class":1068},[1053,6243,1614],{"class":1068},[1053,6245,6246,6249,6251,6253,6255],{"class":1055,"line":1948},[1053,6247,6248],{"class":4390},"    switch",[1053,6250,6227],{"class":1074},[1053,6252,1084],{"class":1068},[1053,6254,4938],{"class":1074},[1053,6256,1614],{"class":1068},[1053,6258,6259,6262,6264],{"class":1055,"line":1953},[1053,6260,6261],{"class":4390},"    case",[1053,6263,4512],{"class":1093},[1053,6265,6266],{"class":1068},":\n",[1053,6268,6269,6272,6274,6277,6279,6282,6284,6286,6288,6291,6293,6296],{"class":1055,"line":1967},[1053,6270,6271],{"class":1074},"        stmt",[1053,6273,1097],{"class":1068},[1053,6275,6276],{"class":1074}," ok",[1053,6278,1386],{"class":1074},[1053,6280,6281],{"class":1074}," d",[1053,6283,1084],{"class":1068},[1053,6285,6046],{"class":1074},[1053,6287,1412],{"class":1068},[1053,6289,6290],{"class":1074},"resp",[1053,6292,1084],{"class":1068},[1053,6294,6295],{"class":1074},"Statement",[1053,6297,5399],{"class":1068},[1053,6299,6300,6303,6306,6309],{"class":1055,"line":1999},[1053,6301,6302],{"class":4390},"        if",[1053,6304,6305],{"class":4390}," !",[1053,6307,6308],{"class":1074},"ok",[1053,6310,1614],{"class":1068},[1053,6312,6313,6316,6318,6320,6323,6325,6328,6330,6333,6335,6337,6339,6341,6343,6345],{"class":1055,"line":2018},[1053,6314,6315],{"class":4390},"            return",[1053,6317,2115],{"class":1064},[1053,6319,1097],{"class":1068},[1053,6321,6322],{"class":1074}," fmt",[1053,6324,1084],{"class":1068},[1053,6326,6327],{"class":1087},"Errorf",[1053,6329,1090],{"class":1068},[1053,6331,6332],{"class":1093},"\"unknown query: ",[1053,6334,2280],{"class":2273},[1053,6336,2270],{"class":1093},[1053,6338,1097],{"class":1068},[1053,6340,6227],{"class":1074},[1053,6342,1084],{"class":1068},[1053,6344,6295],{"class":1074},[1053,6346,1361],{"class":1068},[1053,6348,6349],{"class":1055,"line":2023},[1053,6350,6351],{"class":1068},"        }\n",[1053,6353,6354,6357,6359,6361,6363,6365,6367,6369,6371,6373,6375,6377,6379,6381,6383],{"class":1055,"line":2029},[1053,6355,6356],{"class":4390},"        return",[1053,6358,6281],{"class":1074},[1053,6360,1084],{"class":1068},[1053,6362,5864],{"class":1074},[1053,6364,1084],{"class":1068},[1053,6366,1394],{"class":1087},[1053,6368,1090],{"class":1068},[1053,6370,1399],{"class":1074},[1053,6372,1097],{"class":1068},[1053,6374,4503],{"class":1074},[1053,6376,1097],{"class":1068},[1053,6378,6227],{"class":1074},[1053,6380,1084],{"class":1068},[1053,6382,4521],{"class":1074},[1053,6384,1361],{"class":1068},[1053,6386,6387,6389,6391],{"class":1055,"line":2075},[1053,6388,6261],{"class":4390},[1053,6390,4616],{"class":1093},[1053,6392,6266],{"class":1068},[1053,6394,6395,6397,6399,6401,6403,6405,6407,6409,6411,6413,6415,6417],{"class":1055,"line":2080},[1053,6396,6271],{"class":1074},[1053,6398,1097],{"class":1068},[1053,6400,6276],{"class":1074},[1053,6402,1386],{"class":1074},[1053,6404,6281],{"class":1074},[1053,6406,1084],{"class":1068},[1053,6408,6110],{"class":1074},[1053,6410,1412],{"class":1068},[1053,6412,6290],{"class":1074},[1053,6414,1084],{"class":1068},[1053,6416,6295],{"class":1074},[1053,6418,5399],{"class":1068},[1053,6420,6421,6423,6425,6427],{"class":1055,"line":2086},[1053,6422,6302],{"class":4390},[1053,6424,6305],{"class":4390},[1053,6426,6308],{"class":1074},[1053,6428,1614],{"class":1068},[1053,6430,6431,6433,6435,6437,6439,6441,6443,6445,6448,6450,6452,6454,6456,6458,6460],{"class":1055,"line":2120},[1053,6432,6315],{"class":4390},[1053,6434,2115],{"class":1064},[1053,6436,1097],{"class":1068},[1053,6438,6322],{"class":1074},[1053,6440,1084],{"class":1068},[1053,6442,6327],{"class":1087},[1053,6444,1090],{"class":1068},[1053,6446,6447],{"class":1093},"\"unknown select: ",[1053,6449,2280],{"class":2273},[1053,6451,2270],{"class":1093},[1053,6453,1097],{"class":1068},[1053,6455,6227],{"class":1074},[1053,6457,1084],{"class":1068},[1053,6459,6295],{"class":1074},[1053,6461,1361],{"class":1068},[1053,6463,6464],{"class":1055,"line":2169},[1053,6465,6351],{"class":1068},[1053,6467,6468,6470,6472,6474,6476,6478,6480,6482,6484,6486,6488,6490,6492,6494,6496],{"class":1055,"line":2203},[1053,6469,6356],{"class":4390},[1053,6471,6281],{"class":1074},[1053,6473,1084],{"class":1068},[1053,6475,5864],{"class":1074},[1053,6477,1084],{"class":1068},[1053,6479,1454],{"class":1087},[1053,6481,1090],{"class":1068},[1053,6483,1399],{"class":1074},[1053,6485,1097],{"class":1068},[1053,6487,4503],{"class":1074},[1053,6489,1097],{"class":1068},[1053,6491,6227],{"class":1074},[1053,6493,1084],{"class":1068},[1053,6495,4521],{"class":1074},[1053,6497,1361],{"class":1068},[1053,6499,6500,6502,6504],{"class":1055,"line":2252},[1053,6501,6261],{"class":4390},[1053,6503,4718],{"class":1093},[1053,6505,6266],{"class":1068},[1053,6507,6508,6510,6512,6514,6516,6518,6520,6522,6524,6526,6528,6530],{"class":1055,"line":2257},[1053,6509,6271],{"class":1074},[1053,6511,1097],{"class":1068},[1053,6513,6276],{"class":1074},[1053,6515,1386],{"class":1074},[1053,6517,6281],{"class":1074},[1053,6519,1084],{"class":1068},[1053,6521,6172],{"class":1074},[1053,6523,1412],{"class":1068},[1053,6525,6290],{"class":1074},[1053,6527,1084],{"class":1068},[1053,6529,6295],{"class":1074},[1053,6531,5399],{"class":1068},[1053,6533,6534,6536,6538,6540],{"class":1055,"line":2341},[1053,6535,6302],{"class":4390},[1053,6537,6305],{"class":4390},[1053,6539,6308],{"class":1074},[1053,6541,1614],{"class":1068},[1053,6543,6544,6546,6548,6550,6552,6554,6556,6558,6561,6563,6565,6567,6569,6571,6573],{"class":1055,"line":4959},[1053,6545,6315],{"class":4390},[1053,6547,2115],{"class":1064},[1053,6549,1097],{"class":1068},[1053,6551,6322],{"class":1074},[1053,6553,1084],{"class":1068},[1053,6555,6327],{"class":1087},[1053,6557,1090],{"class":1068},[1053,6559,6560],{"class":1093},"\"unknown aggregate: ",[1053,6562,2280],{"class":2273},[1053,6564,2270],{"class":1093},[1053,6566,1097],{"class":1068},[1053,6568,6227],{"class":1074},[1053,6570,1084],{"class":1068},[1053,6572,6295],{"class":1074},[1053,6574,1361],{"class":1068},[1053,6576,6577],{"class":1055,"line":4965},[1053,6578,6351],{"class":1068},[1053,6580,6581,6583,6585,6587,6589,6591,6593,6595,6597,6599,6601,6603,6605,6607,6609],{"class":1055,"line":4970},[1053,6582,6356],{"class":4390},[1053,6584,6281],{"class":1074},[1053,6586,1084],{"class":1068},[1053,6588,5864],{"class":1074},[1053,6590,1084],{"class":1068},[1053,6592,2185],{"class":1087},[1053,6594,1090],{"class":1068},[1053,6596,1399],{"class":1074},[1053,6598,1097],{"class":1068},[1053,6600,4503],{"class":1074},[1053,6602,1097],{"class":1068},[1053,6604,6227],{"class":1074},[1053,6606,1084],{"class":1068},[1053,6608,4521],{"class":1074},[1053,6610,1361],{"class":1068},[1053,6612,6613,6616],{"class":1055,"line":4978},[1053,6614,6615],{"class":4390},"    default",[1053,6617,6266],{"class":1068},[1053,6619,6620,6622,6624,6626,6628,6630,6632,6634,6637,6639,6641,6643,6645,6647,6649],{"class":1055,"line":4983},[1053,6621,6356],{"class":4390},[1053,6623,2115],{"class":1064},[1053,6625,1097],{"class":1068},[1053,6627,6322],{"class":1074},[1053,6629,1084],{"class":1068},[1053,6631,6327],{"class":1087},[1053,6633,1090],{"class":1068},[1053,6635,6636],{"class":1093},"\"unknown type: ",[1053,6638,2280],{"class":2273},[1053,6640,2270],{"class":1093},[1053,6642,1097],{"class":1068},[1053,6644,6227],{"class":1074},[1053,6646,1084],{"class":1068},[1053,6648,4938],{"class":1074},[1053,6650,1361],{"class":1068},[1053,6652,6653],{"class":1055,"line":4988},[1053,6654,4859],{"class":1068},[1053,6656,6657],{"class":1055,"line":5021},[1053,6658,1663],{"class":1068},[1036,6660,557],{"id":6661},"complete-setup",[1044,6663,6665],{"className":1046,"code":6664,"language":1048,"meta":34,"style":34},"// Create executor\nexec, _ := edamame.New[User](db, \"users\", postgres.New())\n\n// Create dispatcher and register statements\ndispatcher := NewDispatcher(exec)\ndispatcher.RegisterQuery(QueryAll)\ndispatcher.RegisterQuery(ByRole)\ndispatcher.RegisterQuery(ActiveAdults)\ndispatcher.RegisterSelect(SelectByID)\ndispatcher.RegisterAggregate(CountAll)\n\n// Build registry for LLM context\nregistry := NewRegistry(\"users\")\nregistry.AddQuery(QueryAll)\nregistry.AddQuery(ByRole)\nregistry.AddQuery(ActiveAdults)\nregistry.AddSelect(SelectByID)\nregistry.AddAggregate(CountAll)\n\nllmContext, _ := registry.JSON()\n",[1050,6666,6667,6672,6710,6714,6719,6734,6749,6763,6777,6792,6807,6811,6816,6830,6844,6858,6872,6886,6900,6904],{"__ignoreMap":34},[1053,6668,6669],{"class":1055,"line":9},[1053,6670,6671],{"class":1058},"// Create executor\n",[1053,6673,6674,6676,6678,6680,6682,6684,6686,6688,6690,6692,6694,6696,6698,6700,6702,6704,6706,6708],{"class":1055,"line":19},[1053,6675,5864],{"class":1074},[1053,6677,1097],{"class":1068},[1053,6679,1383],{"class":1074},[1053,6681,1386],{"class":1074},[1053,6683,1081],{"class":1074},[1053,6685,1084],{"class":1068},[1053,6687,600],{"class":1087},[1053,6689,1412],{"class":1068},[1053,6691,2049],{"class":1105},[1053,6693,2052],{"class":1068},[1053,6695,2055],{"class":1074},[1053,6697,1097],{"class":1068},[1053,6699,2060],{"class":1093},[1053,6701,1097],{"class":1068},[1053,6703,2065],{"class":1074},[1053,6705,1084],{"class":1068},[1053,6707,600],{"class":1087},[1053,6709,2072],{"class":1068},[1053,6711,6712],{"class":1055,"line":40},[1053,6713,1118],{"emptyLinePlaceholder":1117},[1053,6715,6716],{"class":1055,"line":674},[1053,6717,6718],{"class":1058},"// Create dispatcher and register statements\n",[1053,6720,6721,6724,6726,6728,6730,6732],{"class":1055,"line":1121},[1053,6722,6723],{"class":1074},"dispatcher",[1053,6725,1386],{"class":1074},[1053,6727,5859],{"class":1087},[1053,6729,1090],{"class":1068},[1053,6731,5864],{"class":1074},[1053,6733,1361],{"class":1068},[1053,6735,6736,6738,6740,6743,6745,6747],{"class":1055,"line":1156},[1053,6737,6723],{"class":1074},[1053,6739,1084],{"class":1068},[1053,6741,6742],{"class":1087},"RegisterQuery",[1053,6744,1090],{"class":1068},[1053,6746,5119],{"class":1074},[1053,6748,1361],{"class":1068},[1053,6750,6751,6753,6755,6757,6759,6761],{"class":1055,"line":1208},[1053,6752,6723],{"class":1074},[1053,6754,1084],{"class":1068},[1053,6756,6742],{"class":1087},[1053,6758,1090],{"class":1068},[1053,6760,5134],{"class":1074},[1053,6762,1361],{"class":1068},[1053,6764,6765,6767,6769,6771,6773,6775],{"class":1055,"line":1246},[1053,6766,6723],{"class":1074},[1053,6768,1084],{"class":1068},[1053,6770,6742],{"class":1087},[1053,6772,1090],{"class":1068},[1053,6774,5149],{"class":1074},[1053,6776,1361],{"class":1068},[1053,6778,6779,6781,6783,6786,6788,6790],{"class":1055,"line":1266},[1053,6780,6723],{"class":1074},[1053,6782,1084],{"class":1068},[1053,6784,6785],{"class":1087},"RegisterSelect",[1053,6787,1090],{"class":1068},[1053,6789,5165],{"class":1074},[1053,6791,1361],{"class":1068},[1053,6793,6794,6796,6798,6801,6803,6805],{"class":1055,"line":1272},[1053,6795,6723],{"class":1074},[1053,6797,1084],{"class":1068},[1053,6799,6800],{"class":1087},"RegisterAggregate",[1053,6802,1090],{"class":1068},[1053,6804,5181],{"class":1074},[1053,6806,1361],{"class":1068},[1053,6808,6809],{"class":1055,"line":1277},[1053,6810,1118],{"emptyLinePlaceholder":1117},[1053,6812,6813],{"class":1055,"line":1311},[1053,6814,6815],{"class":1058},"// Build registry for LLM context\n",[1053,6817,6818,6820,6822,6824,6826,6828],{"class":1055,"line":1353},[1053,6819,5094],{"class":1074},[1053,6821,1386],{"class":1074},[1053,6823,4378],{"class":1087},[1053,6825,1090],{"class":1068},[1053,6827,5103],{"class":1093},[1053,6829,1361],{"class":1068},[1053,6831,6832,6834,6836,6838,6840,6842],{"class":1055,"line":1358},[1053,6833,5094],{"class":1074},[1053,6835,1084],{"class":1068},[1053,6837,5114],{"class":1087},[1053,6839,1090],{"class":1068},[1053,6841,5119],{"class":1074},[1053,6843,1361],{"class":1068},[1053,6845,6846,6848,6850,6852,6854,6856],{"class":1055,"line":1364},[1053,6847,5094],{"class":1074},[1053,6849,1084],{"class":1068},[1053,6851,5114],{"class":1087},[1053,6853,1090],{"class":1068},[1053,6855,5134],{"class":1074},[1053,6857,1361],{"class":1068},[1053,6859,6860,6862,6864,6866,6868,6870],{"class":1055,"line":1369},[1053,6861,5094],{"class":1074},[1053,6863,1084],{"class":1068},[1053,6865,5114],{"class":1087},[1053,6867,1090],{"class":1068},[1053,6869,5149],{"class":1074},[1053,6871,1361],{"class":1068},[1053,6873,6874,6876,6878,6880,6882,6884],{"class":1055,"line":1375},[1053,6875,5094],{"class":1074},[1053,6877,1084],{"class":1068},[1053,6879,5160],{"class":1087},[1053,6881,1090],{"class":1068},[1053,6883,5165],{"class":1074},[1053,6885,1361],{"class":1068},[1053,6887,6888,6890,6892,6894,6896,6898],{"class":1055,"line":1438},[1053,6889,5094],{"class":1074},[1053,6891,1084],{"class":1068},[1053,6893,5176],{"class":1087},[1053,6895,1090],{"class":1068},[1053,6897,5181],{"class":1074},[1053,6899,1361],{"class":1068},[1053,6901,6902],{"class":1055,"line":1666},[1053,6903,1118],{"emptyLinePlaceholder":1117},[1053,6905,6906,6909,6911,6913,6915,6917,6919,6921],{"class":1055,"line":1671},[1053,6907,6908],{"class":1074},"llmContext",[1053,6910,1097],{"class":1068},[1053,6912,1383],{"class":1074},[1053,6914,1386],{"class":1074},[1053,6916,5206],{"class":1074},[1053,6918,1084],{"class":1068},[1053,6920,5211],{"class":1087},[1053,6922,2015],{"class":1068},[1036,6924,562],{"id":6925},"validation",[962,6927,6928],{},"Validate LLM responses before execution:",[1044,6930,6932],{"className":1046,"code":6931,"language":1048,"meta":34,"style":34},"func (d *StatementDispatcher) Validate(resp LLMResponse) error {\n    switch resp.Type {\n    case \"query\":\n        stmt, ok := d.queries[resp.Statement]\n        if !ok {\n            return fmt.Errorf(\"unknown query: %s\", resp.Statement)\n        }\n        return validateParams(stmt.Params(), resp.Params)\n    case \"select\":\n        stmt, ok := d.selects[resp.Statement]\n        if !ok {\n            return fmt.Errorf(\"unknown select: %s\", resp.Statement)\n        }\n        return validateParams(stmt.Params(), resp.Params)\n    case \"aggregate\":\n        stmt, ok := d.aggregates[resp.Statement]\n        if !ok {\n            return fmt.Errorf(\"unknown aggregate: %s\", resp.Statement)\n        }\n        return validateParams(stmt.Params(), resp.Params)\n    default:\n        return fmt.Errorf(\"unknown type: %s\", resp.Type)\n    }\n}\n\nfunc validateParams(specs []edamame.ParamSpec, provided map[string]any) error {\n    for _, spec := range specs {\n        if spec.Required {\n            if _, ok := provided[spec.Name]; !ok {\n                return fmt.Errorf(\"missing required param: %s\", spec.Name)\n            }\n        }\n    }\n    return nil\n}\n",[1050,6933,6934,6963,6975,6983,7009,7019,7047,7051,7076,7084,7110,7120,7148,7152,7176,7184,7210,7220,7248,7252,7276,7282,7310,7314,7318,7322,7362,7382,7394,7427,7457,7462,7466,7470,7477],{"__ignoreMap":34},[1053,6935,6936,6938,6940,6942,6944,6946,6948,6951,6953,6955,6957,6959,6961],{"class":1055,"line":9},[1053,6937,1956],{"class":1064},[1053,6939,4433],{"class":1068},[1053,6941,6013],{"class":1572},[1053,6943,4439],{"class":4390},[1053,6945,5884],{"class":1105},[1053,6947,4387],{"class":1068},[1053,6949,6950],{"class":1087}," Validate",[1053,6952,1090],{"class":1068},[1053,6954,6290],{"class":1572},[1053,6956,5706],{"class":1105},[1053,6958,4387],{"class":1068},[1053,6960,5014],{"class":1105},[1053,6962,1614],{"class":1068},[1053,6964,6965,6967,6969,6971,6973],{"class":1055,"line":19},[1053,6966,6248],{"class":4390},[1053,6968,6227],{"class":1074},[1053,6970,1084],{"class":1068},[1053,6972,4938],{"class":1074},[1053,6974,1614],{"class":1068},[1053,6976,6977,6979,6981],{"class":1055,"line":40},[1053,6978,6261],{"class":4390},[1053,6980,4512],{"class":1093},[1053,6982,6266],{"class":1068},[1053,6984,6985,6987,6989,6991,6993,6995,6997,6999,7001,7003,7005,7007],{"class":1055,"line":674},[1053,6986,6271],{"class":1074},[1053,6988,1097],{"class":1068},[1053,6990,6276],{"class":1074},[1053,6992,1386],{"class":1074},[1053,6994,6281],{"class":1074},[1053,6996,1084],{"class":1068},[1053,6998,6046],{"class":1074},[1053,7000,1412],{"class":1068},[1053,7002,6290],{"class":1074},[1053,7004,1084],{"class":1068},[1053,7006,6295],{"class":1074},[1053,7008,5399],{"class":1068},[1053,7010,7011,7013,7015,7017],{"class":1055,"line":1121},[1053,7012,6302],{"class":4390},[1053,7014,6305],{"class":4390},[1053,7016,6308],{"class":1074},[1053,7018,1614],{"class":1068},[1053,7020,7021,7023,7025,7027,7029,7031,7033,7035,7037,7039,7041,7043,7045],{"class":1055,"line":1156},[1053,7022,6315],{"class":4390},[1053,7024,6322],{"class":1074},[1053,7026,1084],{"class":1068},[1053,7028,6327],{"class":1087},[1053,7030,1090],{"class":1068},[1053,7032,6332],{"class":1093},[1053,7034,2280],{"class":2273},[1053,7036,2270],{"class":1093},[1053,7038,1097],{"class":1068},[1053,7040,6227],{"class":1074},[1053,7042,1084],{"class":1068},[1053,7044,6295],{"class":1074},[1053,7046,1361],{"class":1068},[1053,7048,7049],{"class":1055,"line":1208},[1053,7050,6351],{"class":1068},[1053,7052,7053,7055,7058,7060,7062,7064,7066,7068,7070,7072,7074],{"class":1055,"line":1246},[1053,7054,6356],{"class":4390},[1053,7056,7057],{"class":1087}," validateParams",[1053,7059,1090],{"class":1068},[1053,7061,4451],{"class":1074},[1053,7063,1084],{"class":1068},[1053,7065,4521],{"class":1087},[1053,7067,4500],{"class":1068},[1053,7069,6227],{"class":1074},[1053,7071,1084],{"class":1068},[1053,7073,4521],{"class":1074},[1053,7075,1361],{"class":1068},[1053,7077,7078,7080,7082],{"class":1055,"line":1266},[1053,7079,6261],{"class":4390},[1053,7081,4616],{"class":1093},[1053,7083,6266],{"class":1068},[1053,7085,7086,7088,7090,7092,7094,7096,7098,7100,7102,7104,7106,7108],{"class":1055,"line":1272},[1053,7087,6271],{"class":1074},[1053,7089,1097],{"class":1068},[1053,7091,6276],{"class":1074},[1053,7093,1386],{"class":1074},[1053,7095,6281],{"class":1074},[1053,7097,1084],{"class":1068},[1053,7099,6110],{"class":1074},[1053,7101,1412],{"class":1068},[1053,7103,6290],{"class":1074},[1053,7105,1084],{"class":1068},[1053,7107,6295],{"class":1074},[1053,7109,5399],{"class":1068},[1053,7111,7112,7114,7116,7118],{"class":1055,"line":1277},[1053,7113,6302],{"class":4390},[1053,7115,6305],{"class":4390},[1053,7117,6308],{"class":1074},[1053,7119,1614],{"class":1068},[1053,7121,7122,7124,7126,7128,7130,7132,7134,7136,7138,7140,7142,7144,7146],{"class":1055,"line":1311},[1053,7123,6315],{"class":4390},[1053,7125,6322],{"class":1074},[1053,7127,1084],{"class":1068},[1053,7129,6327],{"class":1087},[1053,7131,1090],{"class":1068},[1053,7133,6447],{"class":1093},[1053,7135,2280],{"class":2273},[1053,7137,2270],{"class":1093},[1053,7139,1097],{"class":1068},[1053,7141,6227],{"class":1074},[1053,7143,1084],{"class":1068},[1053,7145,6295],{"class":1074},[1053,7147,1361],{"class":1068},[1053,7149,7150],{"class":1055,"line":1353},[1053,7151,6351],{"class":1068},[1053,7153,7154,7156,7158,7160,7162,7164,7166,7168,7170,7172,7174],{"class":1055,"line":1358},[1053,7155,6356],{"class":4390},[1053,7157,7057],{"class":1087},[1053,7159,1090],{"class":1068},[1053,7161,4451],{"class":1074},[1053,7163,1084],{"class":1068},[1053,7165,4521],{"class":1087},[1053,7167,4500],{"class":1068},[1053,7169,6227],{"class":1074},[1053,7171,1084],{"class":1068},[1053,7173,4521],{"class":1074},[1053,7175,1361],{"class":1068},[1053,7177,7178,7180,7182],{"class":1055,"line":1364},[1053,7179,6261],{"class":4390},[1053,7181,4718],{"class":1093},[1053,7183,6266],{"class":1068},[1053,7185,7186,7188,7190,7192,7194,7196,7198,7200,7202,7204,7206,7208],{"class":1055,"line":1369},[1053,7187,6271],{"class":1074},[1053,7189,1097],{"class":1068},[1053,7191,6276],{"class":1074},[1053,7193,1386],{"class":1074},[1053,7195,6281],{"class":1074},[1053,7197,1084],{"class":1068},[1053,7199,6172],{"class":1074},[1053,7201,1412],{"class":1068},[1053,7203,6290],{"class":1074},[1053,7205,1084],{"class":1068},[1053,7207,6295],{"class":1074},[1053,7209,5399],{"class":1068},[1053,7211,7212,7214,7216,7218],{"class":1055,"line":1375},[1053,7213,6302],{"class":4390},[1053,7215,6305],{"class":4390},[1053,7217,6308],{"class":1074},[1053,7219,1614],{"class":1068},[1053,7221,7222,7224,7226,7228,7230,7232,7234,7236,7238,7240,7242,7244,7246],{"class":1055,"line":1438},[1053,7223,6315],{"class":4390},[1053,7225,6322],{"class":1074},[1053,7227,1084],{"class":1068},[1053,7229,6327],{"class":1087},[1053,7231,1090],{"class":1068},[1053,7233,6560],{"class":1093},[1053,7235,2280],{"class":2273},[1053,7237,2270],{"class":1093},[1053,7239,1097],{"class":1068},[1053,7241,6227],{"class":1074},[1053,7243,1084],{"class":1068},[1053,7245,6295],{"class":1074},[1053,7247,1361],{"class":1068},[1053,7249,7250],{"class":1055,"line":1666},[1053,7251,6351],{"class":1068},[1053,7253,7254,7256,7258,7260,7262,7264,7266,7268,7270,7272,7274],{"class":1055,"line":1671},[1053,7255,6356],{"class":4390},[1053,7257,7057],{"class":1087},[1053,7259,1090],{"class":1068},[1053,7261,4451],{"class":1074},[1053,7263,1084],{"class":1068},[1053,7265,4521],{"class":1087},[1053,7267,4500],{"class":1068},[1053,7269,6227],{"class":1074},[1053,7271,1084],{"class":1068},[1053,7273,4521],{"class":1074},[1053,7275,1361],{"class":1068},[1053,7277,7278,7280],{"class":1055,"line":1677},[1053,7279,6615],{"class":4390},[1053,7281,6266],{"class":1068},[1053,7283,7284,7286,7288,7290,7292,7294,7296,7298,7300,7302,7304,7306,7308],{"class":1055,"line":1684},[1053,7285,6356],{"class":4390},[1053,7287,6322],{"class":1074},[1053,7289,1084],{"class":1068},[1053,7291,6327],{"class":1087},[1053,7293,1090],{"class":1068},[1053,7295,6636],{"class":1093},[1053,7297,2280],{"class":2273},[1053,7299,2270],{"class":1093},[1053,7301,1097],{"class":1068},[1053,7303,6227],{"class":1074},[1053,7305,1084],{"class":1068},[1053,7307,4938],{"class":1074},[1053,7309,1361],{"class":1068},[1053,7311,7312],{"class":1055,"line":1715},[1053,7313,4859],{"class":1068},[1053,7315,7316],{"class":1055,"line":1720},[1053,7317,1663],{"class":1068},[1053,7319,7320],{"class":1055,"line":1751},[1053,7321,1118],{"emptyLinePlaceholder":1117},[1053,7323,7324,7326,7328,7330,7333,7335,7337,7339,7341,7343,7346,7348,7350,7352,7354,7356,7358,7360],{"class":1055,"line":1792},[1053,7325,1956],{"class":1064},[1053,7327,7057],{"class":1087},[1053,7329,1090],{"class":1068},[1053,7331,7332],{"class":1572},"specs",[1053,7334,1216],{"class":1068},[1053,7336,960],{"class":1105},[1053,7338,1084],{"class":1068},[1053,7340,846],{"class":1105},[1053,7342,1097],{"class":1068},[1053,7344,7345],{"class":1572}," provided",[1053,7347,1409],{"class":1064},[1053,7349,1412],{"class":1068},[1053,7351,1415],{"class":1105},[1053,7353,1418],{"class":1068},[1053,7355,1421],{"class":1105},[1053,7357,4387],{"class":1068},[1053,7359,5014],{"class":1105},[1053,7361,1614],{"class":1068},[1053,7363,7364,7366,7368,7370,7373,7375,7377,7380],{"class":1055,"line":1797},[1053,7365,4864],{"class":4390},[1053,7367,1383],{"class":1074},[1053,7369,1097],{"class":1068},[1053,7371,7372],{"class":1074}," spec",[1053,7374,1386],{"class":1074},[1053,7376,4876],{"class":4390},[1053,7378,7379],{"class":1074}," specs",[1053,7381,1614],{"class":1068},[1053,7383,7384,7386,7388,7390,7392],{"class":1055,"line":1802},[1053,7385,6302],{"class":4390},[1053,7387,7372],{"class":1074},[1053,7389,1084],{"class":1068},[1053,7391,4954],{"class":1074},[1053,7393,1614],{"class":1068},[1053,7395,7396,7399,7401,7403,7405,7407,7409,7411,7414,7416,7418,7421,7423,7425],{"class":1055,"line":1845},[1053,7397,7398],{"class":4390},"            if",[1053,7400,1383],{"class":1074},[1053,7402,1097],{"class":1068},[1053,7404,6276],{"class":1074},[1053,7406,1386],{"class":1074},[1053,7408,7345],{"class":1074},[1053,7410,1412],{"class":1068},[1053,7412,7413],{"class":1074},"spec",[1053,7415,1084],{"class":1068},[1053,7417,2321],{"class":1074},[1053,7419,7420],{"class":1068},"];",[1053,7422,6305],{"class":4390},[1053,7424,6308],{"class":1074},[1053,7426,1614],{"class":1068},[1053,7428,7429,7432,7434,7436,7438,7440,7443,7445,7447,7449,7451,7453,7455],{"class":1055,"line":1850},[1053,7430,7431],{"class":4390},"                return",[1053,7433,6322],{"class":1074},[1053,7435,1084],{"class":1068},[1053,7437,6327],{"class":1087},[1053,7439,1090],{"class":1068},[1053,7441,7442],{"class":1093},"\"missing required param: ",[1053,7444,2280],{"class":2273},[1053,7446,2270],{"class":1093},[1053,7448,1097],{"class":1068},[1053,7450,7372],{"class":1074},[1053,7452,1084],{"class":1068},[1053,7454,2321],{"class":1074},[1053,7456,1361],{"class":1068},[1053,7458,7459],{"class":1055,"line":1884},[1053,7460,7461],{"class":1068},"            }\n",[1053,7463,7464],{"class":1055,"line":1901},[1053,7465,6351],{"class":1068},[1053,7467,7468],{"class":1055,"line":1932},[1053,7469,4859],{"class":1068},[1053,7471,7472,7474],{"class":1055,"line":1938},[1053,7473,4401],{"class":4390},[1053,7475,7476],{"class":1064}," nil\n",[1053,7478,7479],{"class":1055,"line":1943},[1053,7480,1663],{"class":1068},[1036,7482,567],{"id":7483},"rate-limiting",[962,7485,7486],{},"Protect against LLM-driven query floods:",[1044,7488,7490],{"className":1046,"code":7489,"language":1048,"meta":34,"style":34},"type RateLimitedDispatcher struct {\n    dispatcher *StatementDispatcher\n    limiter    *rate.Limiter\n}\n\nfunc NewRateLimitedDispatcher(dispatcher *StatementDispatcher, rps float64) *RateLimitedDispatcher {\n    return &RateLimitedDispatcher{\n        dispatcher: dispatcher,\n        limiter:    rate.NewLimiter(rate.Limit(rps), 10),\n    }\n}\n\nfunc (d *RateLimitedDispatcher) Execute(ctx context.Context, resp LLMResponse) (any, error) {\n    if err := d.limiter.Wait(ctx); err != nil {\n        return nil, err\n    }\n    return d.dispatcher.Execute(ctx, resp)\n}\n",[1050,7491,7492,7503,7513,7529,7533,7537,7569,7579,7591,7627,7631,7635,7639,7685,7722,7732,7736,7761],{"__ignoreMap":34},[1053,7493,7494,7496,7499,7501],{"class":1055,"line":9},[1053,7495,1605],{"class":1064},[1053,7497,7498],{"class":1105}," RateLimitedDispatcher",[1053,7500,1611],{"class":1064},[1053,7502,1614],{"class":1068},[1053,7504,7505,7508,7510],{"class":1055,"line":19},[1053,7506,7507],{"class":1159},"    dispatcher",[1053,7509,4391],{"class":4390},[1053,7511,7512],{"class":1105},"StatementDispatcher\n",[1053,7514,7515,7518,7521,7524,7526],{"class":1055,"line":40},[1053,7516,7517],{"class":1159},"    limiter",[1053,7519,7520],{"class":4390},"    *",[1053,7522,7523],{"class":1105},"rate",[1053,7525,1084],{"class":1068},[1053,7527,7528],{"class":1105},"Limiter\n",[1053,7530,7531],{"class":1055,"line":674},[1053,7532,1663],{"class":1068},[1053,7534,7535],{"class":1055,"line":1121},[1053,7536,1118],{"emptyLinePlaceholder":1117},[1053,7538,7539,7541,7544,7546,7548,7550,7552,7554,7557,7560,7562,7564,7567],{"class":1055,"line":1156},[1053,7540,1956],{"class":1064},[1053,7542,7543],{"class":1087}," NewRateLimitedDispatcher",[1053,7545,1090],{"class":1068},[1053,7547,6723],{"class":1572},[1053,7549,4391],{"class":4390},[1053,7551,5884],{"class":1105},[1053,7553,1097],{"class":1068},[1053,7555,7556],{"class":1572}," rps",[1053,7558,7559],{"class":1105}," float64",[1053,7561,4387],{"class":1068},[1053,7563,4391],{"class":4390},[1053,7565,7566],{"class":1105},"RateLimitedDispatcher",[1053,7568,1614],{"class":1068},[1053,7570,7571,7573,7575,7577],{"class":1055,"line":1208},[1053,7572,4401],{"class":4390},[1053,7574,4404],{"class":4390},[1053,7576,7566],{"class":1105},[1053,7578,1153],{"class":1068},[1053,7580,7581,7584,7586,7589],{"class":1055,"line":1246},[1053,7582,7583],{"class":1159},"        dispatcher",[1053,7585,1163],{"class":1068},[1053,7587,7588],{"class":1074}," dispatcher",[1053,7590,4819],{"class":1068},[1053,7592,7593,7596,7598,7601,7603,7606,7608,7610,7612,7615,7617,7620,7622,7625],{"class":1055,"line":1266},[1053,7594,7595],{"class":1159},"        limiter",[1053,7597,1163],{"class":1068},[1053,7599,7600],{"class":1074},"    rate",[1053,7602,1084],{"class":1068},[1053,7604,7605],{"class":1087},"NewLimiter",[1053,7607,1090],{"class":1068},[1053,7609,7523],{"class":1074},[1053,7611,1084],{"class":1068},[1053,7613,7614],{"class":1087},"Limit",[1053,7616,1090],{"class":1068},[1053,7618,7619],{"class":1074},"rps",[1053,7621,2313],{"class":1068},[1053,7623,7624],{"class":1259}," 10",[1053,7626,1263],{"class":1068},[1053,7628,7629],{"class":1055,"line":1272},[1053,7630,4859],{"class":1068},[1053,7632,7633],{"class":1055,"line":1277},[1053,7634,1663],{"class":1068},[1053,7636,7637],{"class":1055,"line":1311},[1053,7638,1118],{"emptyLinePlaceholder":1117},[1053,7640,7641,7643,7645,7647,7649,7651,7653,7655,7657,7659,7661,7663,7665,7667,7669,7671,7673,7675,7677,7679,7681,7683],{"class":1055,"line":1353},[1053,7642,1956],{"class":1064},[1053,7644,4433],{"class":1068},[1053,7646,6013],{"class":1572},[1053,7648,4439],{"class":4390},[1053,7650,7566],{"class":1105},[1053,7652,4387],{"class":1068},[1053,7654,6211],{"class":1087},[1053,7656,1090],{"class":1068},[1053,7658,1399],{"class":1572},[1053,7660,2007],{"class":1105},[1053,7662,1084],{"class":1068},[1053,7664,6222],{"class":1105},[1053,7666,1097],{"class":1068},[1053,7668,6227],{"class":1572},[1053,7670,5706],{"class":1105},[1053,7672,4387],{"class":1068},[1053,7674,4433],{"class":1068},[1053,7676,1421],{"class":1105},[1053,7678,1097],{"class":1068},[1053,7680,5014],{"class":1105},[1053,7682,4387],{"class":1068},[1053,7684,1614],{"class":1068},[1053,7686,7687,7690,7692,7694,7696,7698,7701,7703,7706,7708,7710,7713,7715,7718,7720],{"class":1055,"line":1358},[1053,7688,7689],{"class":4390},"    if",[1053,7691,2611],{"class":1074},[1053,7693,1386],{"class":1074},[1053,7695,6281],{"class":1074},[1053,7697,1084],{"class":1068},[1053,7699,7700],{"class":1074},"limiter",[1053,7702,1084],{"class":1068},[1053,7704,7705],{"class":1087},"Wait",[1053,7707,1090],{"class":1068},[1053,7709,1399],{"class":1074},[1053,7711,7712],{"class":1068},");",[1053,7714,2611],{"class":1074},[1053,7716,7717],{"class":4390}," !=",[1053,7719,2115],{"class":1064},[1053,7721,1614],{"class":1068},[1053,7723,7724,7726,7728,7730],{"class":1055,"line":1364},[1053,7725,6356],{"class":4390},[1053,7727,2115],{"class":1064},[1053,7729,1097],{"class":1068},[1053,7731,5071],{"class":1074},[1053,7733,7734],{"class":1055,"line":1369},[1053,7735,4859],{"class":1068},[1053,7737,7738,7740,7742,7744,7746,7748,7751,7753,7755,7757,7759],{"class":1055,"line":1375},[1053,7739,4401],{"class":4390},[1053,7741,6281],{"class":1074},[1053,7743,1084],{"class":1068},[1053,7745,6723],{"class":1074},[1053,7747,1084],{"class":1068},[1053,7749,7750],{"class":1087},"Execute",[1053,7752,1090],{"class":1068},[1053,7754,1399],{"class":1074},[1053,7756,1097],{"class":1068},[1053,7758,6227],{"class":1074},[1053,7760,1361],{"class":1068},[1053,7762,7763],{"class":1055,"line":1438},[1053,7764,1663],{"class":1068},[1036,7766,572],{"id":7767},"audit-logging",[962,7769,7770],{},"Log LLM-driven operations:",[1044,7772,7774],{"className":1046,"code":7773,"language":1048,"meta":34,"style":34},"func (d *StatementDispatcher) ExecuteWithAudit(ctx context.Context, resp LLMResponse, userID string) (any, error) {\n    start := time.Now()\n\n    result, err := d.Execute(ctx, resp)\n\n    log.Printf(\"LLM execution: user=%s statement=%s type=%s params=%v duration=%v error=%v\",\n        userID,\n        resp.Statement,\n        resp.Type,\n        resp.Params,\n        time.Since(start),\n        err,\n    )\n\n    return result, err\n}\n",[1050,7775,7776,7830,7847,7851,7878,7882,7928,7935,7946,7956,7966,7983,7990,7995,7999,8010],{"__ignoreMap":34},[1053,7777,7778,7780,7782,7784,7786,7788,7790,7793,7795,7797,7799,7801,7803,7805,7807,7809,7811,7814,7816,7818,7820,7822,7824,7826,7828],{"class":1055,"line":9},[1053,7779,1956],{"class":1064},[1053,7781,4433],{"class":1068},[1053,7783,6013],{"class":1572},[1053,7785,4439],{"class":4390},[1053,7787,5884],{"class":1105},[1053,7789,4387],{"class":1068},[1053,7791,7792],{"class":1087}," ExecuteWithAudit",[1053,7794,1090],{"class":1068},[1053,7796,1399],{"class":1572},[1053,7798,2007],{"class":1105},[1053,7800,1084],{"class":1068},[1053,7802,6222],{"class":1105},[1053,7804,1097],{"class":1068},[1053,7806,6227],{"class":1572},[1053,7808,5706],{"class":1105},[1053,7810,1097],{"class":1068},[1053,7812,7813],{"class":1572}," userID",[1053,7815,1655],{"class":1105},[1053,7817,4387],{"class":1068},[1053,7819,4433],{"class":1068},[1053,7821,1421],{"class":1105},[1053,7823,1097],{"class":1068},[1053,7825,5014],{"class":1105},[1053,7827,4387],{"class":1068},[1053,7829,1614],{"class":1068},[1053,7831,7832,7835,7837,7840,7842,7845],{"class":1055,"line":19},[1053,7833,7834],{"class":1074},"    start",[1053,7836,1386],{"class":1074},[1053,7838,7839],{"class":1074}," time",[1053,7841,1084],{"class":1068},[1053,7843,7844],{"class":1087},"Now",[1053,7846,2015],{"class":1068},[1053,7848,7849],{"class":1055,"line":40},[1053,7850,1118],{"emptyLinePlaceholder":1117},[1053,7852,7853,7856,7858,7860,7862,7864,7866,7868,7870,7872,7874,7876],{"class":1055,"line":674},[1053,7854,7855],{"class":1074},"    result",[1053,7857,1097],{"class":1068},[1053,7859,2611],{"class":1074},[1053,7861,1386],{"class":1074},[1053,7863,6281],{"class":1074},[1053,7865,1084],{"class":1068},[1053,7867,7750],{"class":1087},[1053,7869,1090],{"class":1068},[1053,7871,1399],{"class":1074},[1053,7873,1097],{"class":1068},[1053,7875,6227],{"class":1074},[1053,7877,1361],{"class":1068},[1053,7879,7880],{"class":1055,"line":1121},[1053,7881,1118],{"emptyLinePlaceholder":1117},[1053,7883,7884,7887,7889,7891,7893,7896,7898,7901,7903,7906,7908,7911,7914,7917,7919,7922,7924,7926],{"class":1055,"line":1156},[1053,7885,7886],{"class":1074},"    log",[1053,7888,1084],{"class":1068},[1053,7890,2265],{"class":1087},[1053,7892,1090],{"class":1068},[1053,7894,7895],{"class":1093},"\"LLM execution: user=",[1053,7897,2280],{"class":2273},[1053,7899,7900],{"class":1093}," statement=",[1053,7902,2280],{"class":2273},[1053,7904,7905],{"class":1093}," type=",[1053,7907,2280],{"class":2273},[1053,7909,7910],{"class":1093}," params=",[1053,7912,7913],{"class":2273},"%v",[1053,7915,7916],{"class":1093}," duration=",[1053,7918,7913],{"class":2273},[1053,7920,7921],{"class":1093}," error=",[1053,7923,7913],{"class":2273},[1053,7925,2270],{"class":1093},[1053,7927,4819],{"class":1068},[1053,7929,7930,7933],{"class":1055,"line":1208},[1053,7931,7932],{"class":1074},"        userID",[1053,7934,4819],{"class":1068},[1053,7936,7937,7940,7942,7944],{"class":1055,"line":1246},[1053,7938,7939],{"class":1074},"        resp",[1053,7941,1084],{"class":1068},[1053,7943,6295],{"class":1074},[1053,7945,4819],{"class":1068},[1053,7947,7948,7950,7952,7954],{"class":1055,"line":1266},[1053,7949,7939],{"class":1074},[1053,7951,1084],{"class":1068},[1053,7953,4938],{"class":1074},[1053,7955,4819],{"class":1068},[1053,7957,7958,7960,7962,7964],{"class":1055,"line":1272},[1053,7959,7939],{"class":1074},[1053,7961,1084],{"class":1068},[1053,7963,4521],{"class":1074},[1053,7965,4819],{"class":1068},[1053,7967,7968,7971,7973,7976,7978,7981],{"class":1055,"line":1277},[1053,7969,7970],{"class":1074},"        time",[1053,7972,1084],{"class":1068},[1053,7974,7975],{"class":1087},"Since",[1053,7977,1090],{"class":1068},[1053,7979,7980],{"class":1074},"start",[1053,7982,1263],{"class":1068},[1053,7984,7985,7988],{"class":1055,"line":1311},[1053,7986,7987],{"class":1074},"        err",[1053,7989,4819],{"class":1068},[1053,7991,7992],{"class":1055,"line":1353},[1053,7993,7994],{"class":1068},"    )\n",[1053,7996,7997],{"class":1055,"line":1358},[1053,7998,1118],{"emptyLinePlaceholder":1117},[1053,8000,8001,8003,8006,8008],{"class":1055,"line":1364},[1053,8002,4401],{"class":4390},[1053,8004,8005],{"class":1074}," result",[1053,8007,1097],{"class":1068},[1053,8009,5071],{"class":1074},[1053,8011,8012],{"class":1055,"line":1369},[1053,8013,1663],{"class":1068},[1036,8015,577],{"id":8016},"example-chat-interface",[962,8018,8019],{},"Complete example with a simple chat interface:",[1044,8021,8023],{"className":1046,"code":8022,"language":1048,"meta":34,"style":34},"func HandleChat(ctx context.Context, dispatcher *StatementDispatcher, registry *StatementRegistry, llm LLMClient, userMessage string) string {\n    // 1. Get statement metadata\n    specs, _ := registry.JSON()\n\n    // 2. Build prompt\n    prompt := fmt.Sprintf(`You are a database assistant. Available operations:\n%s\n\nUser: %s\n\nRespond with JSON: {\"statement\": \"...\", \"type\": \"...\", \"params\": {...}}\nOr respond with {\"error\": \"...\"} if the request cannot be fulfilled.`, specs, userMessage)\n\n    // 3. Get LLM response\n    llmResp, err := llm.Complete(ctx, prompt)\n    if err != nil {\n        return \"I couldn't process that request.\"\n    }\n\n    // 4. Parse response\n    var resp LLMResponse\n    if err := json.Unmarshal([]byte(llmResp), &resp); err != nil {\n        return \"I couldn't understand how to query the database.\"\n    }\n\n    // 5. Validate\n    if err := dispatcher.Validate(resp); err != nil {\n        return fmt.Sprintf(\"Invalid request: %v\", err)\n    }\n\n    // 6. Execute\n    result, err := dispatcher.Execute(ctx, resp)\n    if err != nil {\n        return fmt.Sprintf(\"Query failed: %v\", err)\n    }\n\n    // 7. Format response\n    return formatResult(result)\n}\n",[1050,8024,8025,8079,8084,8103,8107,8112,8131,8136,8140,8147,8151,8156,8171,8175,8180,8209,8221,8228,8232,8236,8241,8251,8293,8300,8304,8308,8313,8342,8367,8371,8375,8380,8406,8418,8443,8447,8451,8456,8470],{"__ignoreMap":34},[1053,8026,8027,8029,8032,8034,8036,8038,8040,8042,8044,8046,8048,8050,8052,8054,8056,8058,8060,8063,8066,8068,8071,8073,8075,8077],{"class":1055,"line":9},[1053,8028,1956],{"class":1064},[1053,8030,8031],{"class":1087}," HandleChat",[1053,8033,1090],{"class":1068},[1053,8035,1399],{"class":1572},[1053,8037,2007],{"class":1105},[1053,8039,1084],{"class":1068},[1053,8041,6222],{"class":1105},[1053,8043,1097],{"class":1068},[1053,8045,7588],{"class":1572},[1053,8047,4391],{"class":4390},[1053,8049,5884],{"class":1105},[1053,8051,1097],{"class":1068},[1053,8053,5206],{"class":1572},[1053,8055,4391],{"class":4390},[1053,8057,4394],{"class":1105},[1053,8059,1097],{"class":1068},[1053,8061,8062],{"class":1572}," llm",[1053,8064,8065],{"class":1105}," LLMClient",[1053,8067,1097],{"class":1068},[1053,8069,8070],{"class":1572}," userMessage",[1053,8072,1655],{"class":1105},[1053,8074,4387],{"class":1068},[1053,8076,1655],{"class":1105},[1053,8078,1614],{"class":1068},[1053,8080,8081],{"class":1055,"line":19},[1053,8082,8083],{"class":1058},"    // 1. Get statement metadata\n",[1053,8085,8086,8089,8091,8093,8095,8097,8099,8101],{"class":1055,"line":40},[1053,8087,8088],{"class":1074},"    specs",[1053,8090,1097],{"class":1068},[1053,8092,1383],{"class":1074},[1053,8094,1386],{"class":1074},[1053,8096,5206],{"class":1074},[1053,8098,1084],{"class":1068},[1053,8100,5211],{"class":1087},[1053,8102,2015],{"class":1068},[1053,8104,8105],{"class":1055,"line":674},[1053,8106,1118],{"emptyLinePlaceholder":1117},[1053,8108,8109],{"class":1055,"line":1121},[1053,8110,8111],{"class":1058},"    // 2. Build prompt\n",[1053,8113,8114,8117,8119,8121,8123,8126,8128],{"class":1055,"line":1156},[1053,8115,8116],{"class":1074},"    prompt",[1053,8118,1386],{"class":1074},[1053,8120,6322],{"class":1074},[1053,8122,1084],{"class":1068},[1053,8124,8125],{"class":1087},"Sprintf",[1053,8127,1090],{"class":1068},[1053,8129,8130],{"class":1093},"`You are a database assistant. Available operations:\n",[1053,8132,8133],{"class":1055,"line":1208},[1053,8134,8135],{"class":2273},"%s\n",[1053,8137,8138],{"class":1055,"line":1246},[1053,8139,1118],{"emptyLinePlaceholder":1117},[1053,8141,8142,8145],{"class":1055,"line":1266},[1053,8143,8144],{"class":1093},"User: ",[1053,8146,8135],{"class":2273},[1053,8148,8149],{"class":1055,"line":1272},[1053,8150,1118],{"emptyLinePlaceholder":1117},[1053,8152,8153],{"class":1055,"line":1277},[1053,8154,8155],{"class":1093},"Respond with JSON: {\"statement\": \"...\", \"type\": \"...\", \"params\": {...}}\n",[1053,8157,8158,8161,8163,8165,8167,8169],{"class":1055,"line":1311},[1053,8159,8160],{"class":1093},"Or respond with {\"error\": \"...\"} if the request cannot be fulfilled.`",[1053,8162,1097],{"class":1068},[1053,8164,7379],{"class":1074},[1053,8166,1097],{"class":1068},[1053,8168,8070],{"class":1074},[1053,8170,1361],{"class":1068},[1053,8172,8173],{"class":1055,"line":1353},[1053,8174,1118],{"emptyLinePlaceholder":1117},[1053,8176,8177],{"class":1055,"line":1358},[1053,8178,8179],{"class":1058},"    // 3. Get LLM response\n",[1053,8181,8182,8185,8187,8189,8191,8193,8195,8198,8200,8202,8204,8207],{"class":1055,"line":1364},[1053,8183,8184],{"class":1074},"    llmResp",[1053,8186,1097],{"class":1068},[1053,8188,2611],{"class":1074},[1053,8190,1386],{"class":1074},[1053,8192,8062],{"class":1074},[1053,8194,1084],{"class":1068},[1053,8196,8197],{"class":1087},"Complete",[1053,8199,1090],{"class":1068},[1053,8201,1399],{"class":1074},[1053,8203,1097],{"class":1068},[1053,8205,8206],{"class":1074}," prompt",[1053,8208,1361],{"class":1068},[1053,8210,8211,8213,8215,8217,8219],{"class":1055,"line":1369},[1053,8212,7689],{"class":4390},[1053,8214,2611],{"class":1074},[1053,8216,7717],{"class":4390},[1053,8218,2115],{"class":1064},[1053,8220,1614],{"class":1068},[1053,8222,8223,8225],{"class":1055,"line":1375},[1053,8224,6356],{"class":4390},[1053,8226,8227],{"class":1093}," \"I couldn't process that request.\"\n",[1053,8229,8230],{"class":1055,"line":1438},[1053,8231,4859],{"class":1068},[1053,8233,8234],{"class":1055,"line":1666},[1053,8235,1118],{"emptyLinePlaceholder":1117},[1053,8237,8238],{"class":1055,"line":1671},[1053,8239,8240],{"class":1058},"    // 4. Parse response\n",[1053,8242,8243,8246,8248],{"class":1055,"line":1677},[1053,8244,8245],{"class":1064},"    var",[1053,8247,6227],{"class":1074},[1053,8249,8250],{"class":1105}," LLMResponse\n",[1053,8252,8253,8255,8257,8259,8261,8263,8266,8269,8272,8274,8277,8279,8281,8283,8285,8287,8289,8291],{"class":1055,"line":1684},[1053,8254,7689],{"class":4390},[1053,8256,2611],{"class":1074},[1053,8258,1386],{"class":1074},[1053,8260,5033],{"class":1074},[1053,8262,1084],{"class":1068},[1053,8264,8265],{"class":1087},"Unmarshal",[1053,8267,8268],{"class":1068},"([]",[1053,8270,8271],{"class":1105},"byte",[1053,8273,1090],{"class":1068},[1053,8275,8276],{"class":1074},"llmResp",[1053,8278,2313],{"class":1068},[1053,8280,4404],{"class":4390},[1053,8282,6290],{"class":1074},[1053,8284,7712],{"class":1068},[1053,8286,2611],{"class":1074},[1053,8288,7717],{"class":4390},[1053,8290,2115],{"class":1064},[1053,8292,1614],{"class":1068},[1053,8294,8295,8297],{"class":1055,"line":1715},[1053,8296,6356],{"class":4390},[1053,8298,8299],{"class":1093}," \"I couldn't understand how to query the database.\"\n",[1053,8301,8302],{"class":1055,"line":1720},[1053,8303,4859],{"class":1068},[1053,8305,8306],{"class":1055,"line":1751},[1053,8307,1118],{"emptyLinePlaceholder":1117},[1053,8309,8310],{"class":1055,"line":1792},[1053,8311,8312],{"class":1058},"    // 5. Validate\n",[1053,8314,8315,8317,8319,8321,8323,8325,8328,8330,8332,8334,8336,8338,8340],{"class":1055,"line":1797},[1053,8316,7689],{"class":4390},[1053,8318,2611],{"class":1074},[1053,8320,1386],{"class":1074},[1053,8322,7588],{"class":1074},[1053,8324,1084],{"class":1068},[1053,8326,8327],{"class":1087},"Validate",[1053,8329,1090],{"class":1068},[1053,8331,6290],{"class":1074},[1053,8333,7712],{"class":1068},[1053,8335,2611],{"class":1074},[1053,8337,7717],{"class":4390},[1053,8339,2115],{"class":1064},[1053,8341,1614],{"class":1068},[1053,8343,8344,8346,8348,8350,8352,8354,8357,8359,8361,8363,8365],{"class":1055,"line":1802},[1053,8345,6356],{"class":4390},[1053,8347,6322],{"class":1074},[1053,8349,1084],{"class":1068},[1053,8351,8125],{"class":1087},[1053,8353,1090],{"class":1068},[1053,8355,8356],{"class":1093},"\"Invalid request: ",[1053,8358,7913],{"class":2273},[1053,8360,2270],{"class":1093},[1053,8362,1097],{"class":1068},[1053,8364,2611],{"class":1074},[1053,8366,1361],{"class":1068},[1053,8368,8369],{"class":1055,"line":1845},[1053,8370,4859],{"class":1068},[1053,8372,8373],{"class":1055,"line":1850},[1053,8374,1118],{"emptyLinePlaceholder":1117},[1053,8376,8377],{"class":1055,"line":1884},[1053,8378,8379],{"class":1058},"    // 6. Execute\n",[1053,8381,8382,8384,8386,8388,8390,8392,8394,8396,8398,8400,8402,8404],{"class":1055,"line":1901},[1053,8383,7855],{"class":1074},[1053,8385,1097],{"class":1068},[1053,8387,2611],{"class":1074},[1053,8389,1386],{"class":1074},[1053,8391,7588],{"class":1074},[1053,8393,1084],{"class":1068},[1053,8395,7750],{"class":1087},[1053,8397,1090],{"class":1068},[1053,8399,1399],{"class":1074},[1053,8401,1097],{"class":1068},[1053,8403,6227],{"class":1074},[1053,8405,1361],{"class":1068},[1053,8407,8408,8410,8412,8414,8416],{"class":1055,"line":1932},[1053,8409,7689],{"class":4390},[1053,8411,2611],{"class":1074},[1053,8413,7717],{"class":4390},[1053,8415,2115],{"class":1064},[1053,8417,1614],{"class":1068},[1053,8419,8420,8422,8424,8426,8428,8430,8433,8435,8437,8439,8441],{"class":1055,"line":1938},[1053,8421,6356],{"class":4390},[1053,8423,6322],{"class":1074},[1053,8425,1084],{"class":1068},[1053,8427,8125],{"class":1087},[1053,8429,1090],{"class":1068},[1053,8431,8432],{"class":1093},"\"Query failed: ",[1053,8434,7913],{"class":2273},[1053,8436,2270],{"class":1093},[1053,8438,1097],{"class":1068},[1053,8440,2611],{"class":1074},[1053,8442,1361],{"class":1068},[1053,8444,8445],{"class":1055,"line":1943},[1053,8446,4859],{"class":1068},[1053,8448,8449],{"class":1055,"line":1948},[1053,8450,1118],{"emptyLinePlaceholder":1117},[1053,8452,8453],{"class":1055,"line":1953},[1053,8454,8455],{"class":1058},"    // 7. Format response\n",[1053,8457,8458,8460,8463,8465,8468],{"class":1055,"line":1967},[1053,8459,4401],{"class":4390},[1053,8461,8462],{"class":1087}," formatResult",[1053,8464,1090],{"class":1068},[1053,8466,8467],{"class":1074},"result",[1053,8469,1361],{"class":1068},[1053,8471,8472],{"class":1055,"line":1999},[1053,8473,1663],{"class":1068},[1036,8475,582],{"id":8476},"security-considerations",[2449,8478,8479,8485,8491,8497,8503,8509],{},[2452,8480,8481,8484],{},[2455,8482,8483],{},"Validate all LLM outputs"," before execution",[2452,8486,8487,8490],{},[2455,8488,8489],{},"Use parameterized queries"," (edamame handles this)",[2452,8492,8493,8496],{},[2455,8494,8495],{},"Limit exposed statements"," to what's safe",[2452,8498,8499,8502],{},[2455,8500,8501],{},"Rate limit"," LLM-driven operations",[2452,8504,8505,8508],{},[2455,8506,8507],{},"Audit log"," all executions",[2452,8510,8511,8514],{},[2455,8512,8513],{},"Never expose"," raw SQL generation to LLMs",[2728,8516,8517],{},"html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}",{"title":34,"searchDepth":19,"depth":19,"links":8519},[8520,8521,8522,8523,8524,8525,8526,8527,8528,8529,8530,8531],{"id":3715,"depth":19,"text":528},{"id":3741,"depth":19,"text":129},{"id":4146,"depth":19,"text":537},{"id":5079,"depth":19,"text":542},{"id":5679,"depth":19,"text":547},{"id":5691,"depth":19,"text":552},{"id":6661,"depth":19,"text":557},{"id":6925,"depth":19,"text":562},{"id":7483,"depth":19,"text":567},{"id":7767,"depth":19,"text":572},{"id":8016,"depth":19,"text":577},{"id":8476,"depth":19,"text":582},{},"2025-12-17T00:00:00.000Z",null,{"title":519,"description":521},[8537,8538,8539,120],"LLM","AI","Integration","2026-01-04T00:00:00.000Z","ZOyPasILXqigSyBJtaAL8FwjE_6Lorz4zvxYVlb1Ihw",[8543,8544],{"title":456,"path":455,"stem":916,"description":458,"children":-1},{"title":587,"path":586,"stem":930,"description":589,"children":-1},1776270501873]