# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

```bash
# Install dependencies (always run first, takes ~2s)
npm install

# Run tests (1041 tests, ~45s - required for CI)
npm test

# Run specific test
npm test -- -g "test name"

# Lint code (required for CI, ~1s)
npm run lint

# Generate TextMate language definition (optional)
npm run tm
```

**Important:** Tests take ~45 seconds. Set adequate timeouts (60+ seconds) for test commands.

## Architecture Overview

This is a Language Server Protocol (LSP) implementation for XanoScript, built on Chevrotain parser generator. The architecture follows: **Lexer → Parser → Language Server Features**.

### Core Components

**Lexer (`lexer/`)**: Token definitions and lexical analysis

- `tokens.js` - Main token registry
- `lexer.js` - Chevrotain lexer implementation
- Domain tokens: `api.js`, `db.js`, `cloud.js`, `function.js`, etc.
- `utils.js` - Token creation utilities (`createToken`, `createTokenByName`)

**Parser (`parser/`)**: Grammar rules and parsing logic

- `base_parser.js` - Core XanoBaseParser extending Chevrotain
- Main parsers: `query_parser.js`, `function_parser.js`, `task_parser.js`, `api_group_parser.js`, `table_parser.js`, `workflow_test_parser.js`, `table_trigger_parser.js`
- `attributes/` - Field attributes (description, disabled, sensitive)
- `clauses/` - Language blocks (stack, input, response, security, auth, cache, history, index, schema, test)
- `definitions/` - Type and column definitions
- `functions/` - Built-in functions organized by domain (api, array, cloud, controls, db, debug, expect, math, object, redis, time, util, variable)
- `generic/` - Reusable parsing components

**Language Server (`server.js` + feature directories)**:

- `onCompletion/` - Auto-completion logic
- `onDidChangeContent/` - Live diagnostics and error reporting
- `onHover/` - Documentation and hover information
- `onSemanticCheck/` - Syntax highlighting token generation

### XanoScript Object Types

Primary constructs parsed by dedicated parsers:

- **query** - API endpoints with HTTP verbs, input validation, processing logic, responses
- **function** - Reusable logic blocks with testing capabilities
- **task** - Scheduled operations with cron-like triggers
- **api_group** - Collections of related API endpoints
- **table** - Database schema definitions with column types
- **workflow_test** - Test definitions for workflows
- **table_trigger** - Database table trigger definitions

## Adding New Features

### New Top-Level Parser (e.g., `run.job`, `run.service`)

Follow this step-by-step process:

**1. Create lexer tokens (`lexer/my_feature.js`)**

```javascript
import { Identifier } from "./identifier.js";
import { createTokenByName } from "./utils.js";

export const MyToken = createTokenByName("my_keyword", {
  longer_alt: Identifier,
  categories: [Identifier],
});

export const MyFeatureTokens = [MyToken];

export function mapTokenToType(token) {
  switch (token) {
    case MyToken.name:
      return "keyword";
    default:
      return null;
  }
}
```

**2. Register tokens in `lexer/tokens.js`**

- Import tokens and mapper at the top
- Add `...MyFeatureTokens` to `allTokens` array
- Add `mapMyFeatureTokenToType` to `tokenMappers` array

**3. Create parser (`parser/my_feature_parser.js`)**

```javascript
import { StringLiteral } from "../lexer/literal.js";
import { MyToken } from "../lexer/my_feature.js";
import { Identifier, NewlineToken } from "../lexer/tokens.js";

export function myFeatureDeclaration($) {
  return () => {
    $.sectionStack.push("myFeatureDeclaration");
    $.SUBRULE($.optionalCommentBlockFn);

    const parent = $.CONSUME(MyToken);
    $.OR([
      { ALT: () => $.CONSUME(StringLiteral) },
      { ALT: () => $.CONSUME(Identifier) },
    ]);

    // Use schemaParseAttributeFn for body with declarative schema
    $.SUBRULE($.schemaParseAttributeFn, {
      ARGS: [
        parent,
        {
          required_attr: "[string]",
          "optional_attr?": "[boolean]",
          "nested?": {
            name: "[string]",
            "value?": { "[string]": "[constant]" },
          },
          "array_of_strings?": ["[string]"],
        },
      ],
    });

    $.MANY2(() => $.CONSUME2(NewlineToken));
    $.sectionStack.pop();
  };
}
```

**4. Register parser in `parser/register.js`**

```javascript
import { myFeatureDeclaration } from "./my_feature_parser.js";
// In register function:
$.myFeatureDeclaration = $.RULE(
  "myFeatureDeclaration",
  myFeatureDeclaration($)
);
```

**5. Add scheme detection in `utils.js`**

```javascript
const schemeByFirstWord = {
  // ... existing entries
  my_feature: "my_feature",
};
```

**6. Route scheme in `parser/parser.js`**

```javascript
case "my_feature":
  parser.myFeatureDeclaration();
  return parser;
```

**7. Write tests (`parser/my_feature_parser.spec.js`)**

```javascript
import { expect } from "chai";
import { describe, it } from "mocha";
import { xanoscriptParser } from "./parser.js";

describe("my_feature", () => {
  it("should parse a basic my_feature", () => {
    const parser = xanoscriptParser(`my_feature "name" {
      required_attr = "value"
    }`);
    expect(parser.errors).to.be.empty;
  });
});
```

### Schema Definition Types (for `schemaParseAttributeFn`)

| Schema                         | Description                        | Example                   |
| ------------------------------ | ---------------------------------- | ------------------------- |
| `"[string]"`                   | String literal                     | `"hello"`                 |
| `"[number]"`                   | Number literal                     | `123`                     |
| `"[boolean]"`                  | Boolean value                      | `true` / `false`          |
| `"[constant]"`                 | Value expression (no variables)    | `"text"`, `123`, `{a: 1}` |
| `"[expression]"`               | Any expression including variables | `$var`, `$input.name`     |
| `["[string]"]`                 | Array of strings                   | `["a", "b"]`              |
| `{ "[string]": "[constant]" }` | Object with string keys            | `{key: "value"}`          |
| `"attr?"`                      | Optional attribute                 | May be omitted            |
| `"!attr"`                      | Can be disabled                    | `!attr = value`           |

### New Keyword/Function

1. Add token definition in appropriate `lexer/` file
2. Create function implementation in `parser/functions/[domain]/`
3. Register in parent clause or parser
4. Add comprehensive tests in corresponding `.spec.js` file

### Token Creation Pattern

```javascript
export const MyToken = createTokenByName("keyword", {
  longer_alt: Identifier,
  categories: [Identifier],
});
```

### Parser Rule Pattern

```javascript
myRule = this.RULE("myRule", () => {
  this.CONSUME(MyToken);
  this.SUBRULE(this.otherRule);
});
```

## Testing Guidelines

- Tests use Mocha + Chai framework
- 171 test files with `.spec.js` naming convention
- Test structure mirrors source structure
- Real XanoScript examples in `valid_sources/` directories
- Tests are explicit and straightforward - avoid dynamic test generation
- CI requires all tests to pass

### Test Pattern

```javascript
function parse(inputText) {
  const lexResult = lexDocument(inputText);
  const parser = new Parser();
  parser.input = lexResult.tokens;
  parser.myRule();
  return parser;
}
```

## Development Notes

- **Node.js 20+ required** for ES module support
- **No compilation step** - pure JavaScript ES modules
- Use `import`/`export`, not `require()`
- Follow existing code conventions when adding features
- Check neighboring files for patterns and conventions
- Never assume a library is available - check `package.json` first
- 3 low-severity npm audit warnings are known and acceptable
