@aigne/ash 0.0.1 → 0.0.2-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ast.d.cts +9 -1
- package/dist/ast.d.cts.map +1 -1
- package/dist/ast.d.mts +9 -1
- package/dist/ast.d.mts.map +1 -1
- package/dist/compiler.cjs +6 -0
- package/dist/compiler.d.cts.map +1 -1
- package/dist/compiler.d.mts.map +1 -1
- package/dist/compiler.mjs +6 -0
- package/dist/compiler.mjs.map +1 -1
- package/dist/parser.cjs +28 -0
- package/dist/parser.d.cts.map +1 -1
- package/dist/parser.d.mts.map +1 -1
- package/dist/parser.mjs +28 -0
- package/dist/parser.mjs.map +1 -1
- package/dist/type-checker.cjs +6 -0
- package/dist/type-checker.d.cts.map +1 -1
- package/dist/type-checker.d.mts.map +1 -1
- package/dist/type-checker.mjs +6 -0
- package/dist/type-checker.mjs.map +1 -1
- package/package.json +5 -1
- package/DESIGN.md +0 -41
- package/src/ai-dev-loop/ash-run-result.test.ts +0 -113
- package/src/ai-dev-loop/ash-run-result.ts +0 -46
- package/src/ai-dev-loop/ash-typed-error.test.ts +0 -136
- package/src/ai-dev-loop/ash-typed-error.ts +0 -50
- package/src/ai-dev-loop/ash-validate.test.ts +0 -54
- package/src/ai-dev-loop/ash-validate.ts +0 -34
- package/src/ai-dev-loop/dev-loop.test.ts +0 -364
- package/src/ai-dev-loop/dev-loop.ts +0 -156
- package/src/ai-dev-loop/dry-run.test.ts +0 -107
- package/src/ai-dev-loop/e2e-multi-fix.test.ts +0 -473
- package/src/ai-dev-loop/e2e.test.ts +0 -324
- package/src/ai-dev-loop/index.ts +0 -15
- package/src/ai-dev-loop/invariants.test.ts +0 -253
- package/src/ai-dev-loop/live-mode.test.ts +0 -63
- package/src/ai-dev-loop/live-mode.ts +0 -33
- package/src/ai-dev-loop/meta-tools.test.ts +0 -120
- package/src/ai-dev-loop/meta-tools.ts +0 -142
- package/src/ai-dev-loop/structured-runner.test.ts +0 -159
- package/src/ai-dev-loop/structured-runner.ts +0 -209
- package/src/ai-dev-loop/system-prompt.test.ts +0 -102
- package/src/ai-dev-loop/system-prompt.ts +0 -81
- package/src/ast.ts +0 -186
- package/src/compiler.test.ts +0 -2933
- package/src/compiler.ts +0 -1103
- package/src/e2e.test.ts +0 -552
- package/src/index.ts +0 -16
- package/src/lexer.test.ts +0 -538
- package/src/lexer.ts +0 -222
- package/src/parser.test.ts +0 -1024
- package/src/parser.ts +0 -835
- package/src/reference.test.ts +0 -166
- package/src/reference.ts +0 -125
- package/src/template.test.ts +0 -210
- package/src/template.ts +0 -139
- package/src/type-checker.test.ts +0 -1494
- package/src/type-checker.ts +0 -785
- package/tsconfig.json +0 -9
- package/tsdown.config.ts +0 -12
package/dist/parser.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.mjs","names":[],"sources":["../src/parser.ts"],"sourcesContent":["import type { Token, TokenType } from \"./lexer.js\";\nimport type { Program, JobDeclaration, PipelineStage, Annotation, WhereClause, QueryCondition, TopLevelStatement, LetStatement, CountExpression, GroupByExpression, ActionExpression, Expression, MapExpression, RouteExpression, RouteBranch, LookupExpression, ParamDeclaration, TriggerDeclaration } from \"./ast.js\";\n\n/** Annotation names that are script-level: propagate to all jobs when declared before any statement. */\nconst SCRIPT_LEVEL_ANNOTATIONS = new Set([\"caps\", \"budget\"]);\n\n/** Merge script-level annotations with job-specific ones. Job overrides script-level by name. */\nfunction mergeAnnotations(script: Annotation[], job: Annotation[]): Annotation[] {\n if (script.length === 0) return job;\n if (job.length === 0) return [...script];\n const jobNames = new Set(job.map(a => a.name));\n return [...script.filter(a => !jobNames.has(a.name)), ...job];\n}\n\nexport class AshParser {\n private tokens: Token[] = [];\n private pos = 0;\n\n private letNames = new Set<string>();\n private paramNames = new Set<string>();\n private static readonly RESERVED_KEYWORDS = new Set([\"job\", \"find\", \"where\", \"map\", \"save\", \"publish\", \"tee\", \"fanout\", \"input\", \"output\", \"let\", \"action\", \"route\", \"lookup\", \"param\", \"on\"]);\n\n parse(tokens: Token[]): Program {\n this.tokens = tokens;\n this.pos = 0;\n this.letNames = new Set();\n this.paramNames = new Set();\n const statements: TopLevelStatement[] = [];\n\n this.skipNewlines();\n\n // Collect initial annotations (before any statement).\n // Split into script-level (caps, budget → propagate to all jobs) and first-job-specific.\n const initialAnnotations = this.parseAnnotations();\n const scriptAnnotations = initialAnnotations.filter(a => SCRIPT_LEVEL_ANNOTATIONS.has(a.name));\n const firstJobOnly = initialAnnotations.filter(a => !SCRIPT_LEVEL_ANNOTATIONS.has(a.name));\n let firstStatement = true;\n\n while (!this.isAtEnd()) {\n const jobAnnotations = this.parseAnnotations();\n if (this.isAtEnd()) break;\n\n const t = this.peek();\n if (t.type === \"JOB\") {\n const extra = firstStatement ? [...firstJobOnly, ...jobAnnotations] : jobAnnotations;\n const merged = mergeAnnotations(scriptAnnotations, extra);\n firstStatement = false;\n statements.push(this.parseJob(merged));\n } else if (t.type === \"OUTPUT\" && jobAnnotations.length === 0) {\n firstStatement = false;\n this.advance(); // output\n const msg = this.expect(\"STRING\", \"Expected string after 'output'\");\n statements.push({ kind: \"output\", message: msg.value });\n } else if (t.type === \"LET\" && jobAnnotations.length === 0) {\n firstStatement = false;\n statements.push(this.parseLet());\n } else if (t.type === \"PARAM\" && jobAnnotations.length === 0) {\n firstStatement = false;\n statements.push(this.parseParam());\n } else {\n throw new Error(`Expected 'job', 'output', 'let', or 'param' at top level, got ${t.type} at line ${t.line}, column ${t.column}`);\n }\n this.skipNewlines();\n }\n\n const jobs = statements.filter((s): s is JobDeclaration => s.kind === \"job\");\n return { statements, jobs };\n }\n\n private parseParam(): ParamDeclaration {\n const paramToken = this.advance(); // param\n if (this.isAtEnd()) {\n throw new Error(`Expected parameter name after 'param' at line ${paramToken.line}, column ${paramToken.column}`);\n }\n const nameToken = this.peek();\n if (nameToken.type !== \"IDENTIFIER\") {\n throw new Error(`Cannot use reserved keyword '${nameToken.value}' as param name at line ${nameToken.line}, column ${nameToken.column}`);\n }\n this.advance();\n if (AshParser.RESERVED_KEYWORDS.has(nameToken.value)) {\n throw new Error(`Cannot use reserved keyword '${nameToken.value}' as param name at line ${nameToken.line}, column ${nameToken.column}`);\n }\n if (this.paramNames.has(nameToken.value)) {\n throw new Error(`Duplicate param name '${nameToken.value}' at line ${nameToken.line}, column ${nameToken.column}`);\n }\n if (this.letNames.has(nameToken.value)) {\n throw new Error(`Param '${nameToken.value}' conflicts with existing let variable at line ${nameToken.line}, column ${nameToken.column}`);\n }\n this.expect(\"ASSIGN\", \"Expected '=' after param name\");\n const valToken = this.advance();\n let defaultValue: string | number;\n if (valToken.type === \"NUMBER\") {\n defaultValue = parseFloat(valToken.value);\n } else if (valToken.type === \"STRING\") {\n defaultValue = valToken.value;\n } else if (valToken.type === \"PATH\") {\n defaultValue = valToken.value;\n } else if (valToken.type === \"IDENTIFIER\") {\n defaultValue = valToken.value;\n } else {\n throw new Error(`Expected default value after '=' at line ${valToken.line}, column ${valToken.column}`);\n }\n this.paramNames.add(nameToken.value);\n return { kind: \"param\", name: nameToken.value, defaultValue };\n }\n\n private static readonly PIPELINE_STARTERS = new Set([\"FIND\", \"ACTION\", \"LOOKUP\", \"INPUT\"]);\n\n private parseLet(): LetStatement {\n const letToken = this.advance(); // let\n if (this.isAtEnd() || this.peek().type !== \"IDENTIFIER\") {\n throw new Error(`Expected variable name after 'let' at line ${letToken.line}, column ${letToken.column}`);\n }\n const nameToken = this.advance();\n if (AshParser.RESERVED_KEYWORDS.has(nameToken.value)) {\n throw new Error(`Cannot use reserved keyword '${nameToken.value}' as variable name at line ${nameToken.line}, column ${nameToken.column}`);\n }\n if (this.letNames.has(nameToken.value)) {\n throw new Error(`Duplicate variable name '${nameToken.value}' at line ${nameToken.line}, column ${nameToken.column}`);\n }\n if (this.paramNames.has(nameToken.value)) {\n throw new Error(`Let variable '${nameToken.value}' conflicts with existing param at line ${nameToken.line}, column ${nameToken.column}`);\n }\n this.expect(\"ASSIGN\", \"Expected '=' after variable name\");\n\n // Check if the value is a pipeline (starts with find, action, etc.)\n if (!this.isAtEnd() && AshParser.PIPELINE_STARTERS.has(this.peek().type)) {\n const pipeline = this.parseLetPipeline();\n this.letNames.add(nameToken.value);\n return { kind: \"let\", name: nameToken.value, value: 0, pipeline };\n }\n\n const valToken = this.advance();\n let value: string | number;\n if (valToken.type === \"NUMBER\") {\n value = parseFloat(valToken.value);\n } else if (valToken.type === \"STRING\") {\n value = valToken.value;\n } else if (valToken.type === \"IDENTIFIER\") {\n value = valToken.value;\n } else {\n throw new Error(`Expected value after '=' at line ${valToken.line}, column ${valToken.column}`);\n }\n this.letNames.add(nameToken.value);\n return { kind: \"let\", name: nameToken.value, value };\n }\n\n private parseLetPipeline(): PipelineStage[] {\n // Parse pipeline stages until NEWLINE, EOF, or JOB/LET/OUTPUT (top-level)\n const stages: PipelineStage[] = [];\n while (!this.isAtEnd()) {\n const t = this.peek();\n // Stop at newline or top-level keywords\n if (t.type === \"NEWLINE\" || t.type === \"JOB\" || t.type === \"LET\" || t.type === \"OUTPUT\" || t.type === \"AT\") break;\n\n if (t.type === \"PIPE\") {\n this.advance(); // |\n continue;\n }\n\n stages.push(this.parseStage());\n }\n return stages;\n }\n\n private parseAnnotations(): Annotation[] {\n const annotations: Annotation[] = [];\n while (!this.isAtEnd() && this.peek().type === \"AT\") {\n const at = this.advance(); // @\n if (this.isAtEnd() || this.peek().type !== \"IDENTIFIER\") {\n throw new Error(`Expected annotation name after '@' at line ${at.line}, column ${at.column}`);\n }\n const nameToken = this.advance();\n const args: string[] = [];\n\n if (!this.isAtEnd() && this.peek().type === \"LPAREN\") {\n this.advance(); // (\n while (!this.isAtEnd() && this.peek().type !== \"RPAREN\") {\n const arg = this.advance();\n args.push(arg.value);\n if (!this.isAtEnd() && this.peek().type === \"COMMA\") {\n this.advance();\n }\n }\n if (this.isAtEnd()) {\n throw new Error(`Unclosed annotation parenthesis at line ${at.line}, column ${at.column}`);\n }\n this.advance(); // )\n }\n\n annotations.push({ name: nameToken.value, args, line: at.line, column: at.column });\n this.skipNewlines();\n }\n return annotations;\n }\n\n private parseJob(annotations: Annotation[]): JobDeclaration {\n this.advance(); // 'job'\n this.skipNewlines();\n\n if (this.isAtEnd()) {\n throw new Error(\"Expected job name\");\n }\n const nameToken = this.advance();\n if (AshParser.RESERVED_KEYWORDS.has(nameToken.value)) {\n throw new Error(`Cannot use reserved keyword '${nameToken.value}' as job name at line ${nameToken.line}, column ${nameToken.column}`);\n }\n this.skipNewlines();\n\n // Optional trigger: `on /path:event` or `on cron(\"expr\")`\n let trigger: TriggerDeclaration | undefined;\n if (!this.isAtEnd() && this.peek().type === \"ON\") {\n const onToken = this.advance(); // on\n\n if (!this.isAtEnd() && this.peek().type === \"IDENTIFIER\" && this.peek().value === \"cron\") {\n // Cron trigger: on cron(\"*/5 * * * *\")\n this.advance(); // cron\n if (this.isAtEnd() || this.peek().type !== \"LPAREN\") {\n throw new Error(`Expected '(' after 'cron' at line ${onToken.line}, column ${onToken.column}`);\n }\n this.advance(); // (\n if (this.isAtEnd() || this.peek().type !== \"STRING\") {\n throw new Error(`Expected cron expression string after 'cron(' at line ${onToken.line}, column ${onToken.column}`);\n }\n const exprToken = this.advance();\n if (this.isAtEnd() || this.peek().type !== \"RPAREN\") {\n throw new Error(`Expected ')' after cron expression at line ${onToken.line}, column ${onToken.column}`);\n }\n this.advance(); // )\n trigger = { kind: \"cron\", expression: exprToken.value };\n } else {\n // Event trigger: on /path:event\n if (this.isAtEnd() || this.peek().type !== \"PATH\") {\n throw new Error(`Expected path after 'on' at line ${onToken.line}, column ${onToken.column}`);\n }\n const pathToken = this.advance();\n if (this.isAtEnd() || this.peek().type !== \"COLON\") {\n throw new Error(`Expected ':event' after trigger path at line ${pathToken.line}, column ${pathToken.column}`);\n }\n this.advance(); // :\n if (this.isAtEnd() || this.peek().type !== \"IDENTIFIER\") {\n throw new Error(`Expected event name after ':' at line ${pathToken.line}, column ${pathToken.column}`);\n }\n const eventToken = this.advance();\n trigger = { kind: \"event\", path: pathToken.value, event: eventToken.value };\n }\n this.skipNewlines();\n }\n\n if (this.isAtEnd() || this.peek().type !== \"LBRACE\") {\n const t = this.isAtEnd() ? nameToken : this.peek();\n throw new Error(`Expected '{' after job name at line ${t.line}, column ${t.column}`);\n }\n this.advance(); // {\n this.skipNewlines();\n\n const pipeline = this.parsePipeline();\n this.skipNewlines();\n\n if (this.isAtEnd() || this.peek().type !== \"RBRACE\") {\n throw new Error(\"Expected '}' to close job body\");\n }\n this.advance(); // }\n\n return { kind: \"job\", name: nameToken.value, annotations, pipeline, trigger };\n }\n\n private parsePipeline(): PipelineStage[] {\n const stages: PipelineStage[] = [];\n\n while (!this.isAtEnd() && this.peek().type !== \"RBRACE\") {\n if (this.peek().type === \"PIPE\") {\n if (stages.length === 0) {\n const t = this.peek();\n throw new Error(`Unexpected PIPE at line ${t.line}, column ${t.column}`);\n }\n this.advance(); // |\n this.skipNewlines();\n if (this.isAtEnd() || this.peek().type === \"RBRACE\") {\n throw new Error(\"Unexpected end after pipe operator\");\n }\n }\n\n this.skipNewlines();\n if (this.isAtEnd() || this.peek().type === \"RBRACE\") break;\n\n const stage = this.parseStage();\n stages.push(stage);\n this.skipNewlines();\n }\n\n return stages;\n }\n\n private parseStage(): PipelineStage {\n const t = this.peek();\n\n switch (t.type) {\n case \"FIND\": {\n this.advance();\n const path = this.expect(\"PATH\", \"Expected path after 'find'\");\n // Check for inline where: `find /path where condition`\n let query: QueryCondition | undefined;\n if (!this.isAtEnd() && this.peek().type === \"WHERE\") {\n this.advance(); // consume WHERE\n const where = this.parseWhere();\n query = { field: where.left, op: where.op, value: where.right };\n }\n return { kind: \"find\", path: path.value, query };\n }\n case \"WHERE\": {\n this.advance();\n return this.parseWhere();\n }\n case \"MAP\": {\n this.advance();\n return this.parseMapBody();\n }\n case \"SAVE\": {\n this.advance();\n const path = this.expect(\"PATH\", \"Expected path after 'save'\");\n return { kind: \"save\", path: path.value };\n }\n case \"PUBLISH\": {\n this.advance();\n const path = this.expect(\"PATH\", \"Expected path after 'publish'\");\n return { kind: \"publish\", path: path.value };\n }\n case \"TEE\": {\n this.advance();\n const path = this.expect(\"PATH\", \"Expected path after 'tee'\");\n return { kind: \"tee\", path: path.value };\n }\n case \"FANOUT\": {\n this.advance();\n return this.parseFanout();\n }\n case \"OUTPUT\": {\n this.advance();\n // Try parsing as expression (supports field access, binary, string literals)\n const expr = this.parseExpression();\n if (expr.kind === \"literal\" && typeof expr.value === \"string\") {\n // Backward compatible: string literal → use message field\n return { kind: \"output\", message: expr.value };\n }\n // Expression mode: evaluate per stream item at runtime\n return { kind: \"output\", message: \"\", expression: expr };\n }\n case \"INPUT\": {\n this.advance();\n const prompt = this.expect(\"STRING\", \"Expected string after 'input'\");\n return { kind: \"input\", prompt: prompt.value };\n }\n case \"COUNT\": {\n this.advance();\n return { kind: \"count\" } as CountExpression;\n }\n case \"GROUP_BY\": {\n this.advance();\n if (this.isAtEnd() || this.peek().type === \"PIPE\" || this.peek().type === \"RBRACE\" || this.peek().type === \"NEWLINE\") {\n throw new Error(\"Expected field name after 'group-by'\");\n }\n const field = this.parseFieldAccess();\n return { kind: \"group-by\", field } as GroupByExpression;\n }\n case \"ACTION\": {\n return this.parseAction();\n }\n case \"ROUTE\": {\n return this.parseRoute();\n }\n case \"LOOKUP\": {\n return this.parseLookup();\n }\n default:\n throw new Error(`Unexpected token ${t.type} \"${t.value}\" at line ${t.line}, column ${t.column}`);\n }\n }\n\n private parseWhere(): WhereClause {\n const left = this.parseFieldAccess();\n const opToken = this.advance();\n const opMap: Record<string, WhereClause[\"op\"]> = {\n \"==\": \"==\", \"!=\": \"!=\", \">\": \">\", \"<\": \"<\", \">=\": \">=\", \"<=\": \"<=\",\n };\n const op = opMap[opToken.value];\n if (!op) {\n throw new Error(`Expected comparison operator, got ${opToken.type} at line ${opToken.line}, column ${opToken.column}`);\n }\n\n let right: string | number;\n // Check for $variable reference\n if (!this.isAtEnd() && this.peek().type === \"DOLLAR\") {\n this.advance(); // $\n const varName = this.advance();\n right = \"$\" + varName.value;\n } else {\n const rightToken = this.advance();\n if (rightToken.type === \"NUMBER\") {\n right = parseFloat(rightToken.value);\n } else if (rightToken.type === \"STRING\") {\n right = rightToken.value;\n } else if (rightToken.type === \"IDENTIFIER\" || rightToken.type === \"PATH\") {\n let val = rightToken.value;\n while (!this.isAtEnd() && this.peek().type === \"DOT\") {\n this.advance();\n const next = this.advance();\n val += \".\" + next.value;\n }\n right = val;\n } else {\n right = rightToken.value;\n }\n }\n\n return { kind: \"where\", left, op, right };\n }\n\n private parseFieldAccess(): string {\n const first = this.advance();\n let field = first.value;\n while (!this.isAtEnd() && this.peek().type === \"DOT\") {\n this.advance(); // .\n const next = this.advance();\n field += \".\" + next.value;\n }\n return field;\n }\n\n private parseMapBody(): MapExpression {\n // map without braces: either simple field or expression\n if (this.isAtEnd() || this.peek().type === \"PIPE\" || this.peek().type === \"RBRACE\" || this.peek().type === \"NEWLINE\") {\n throw new Error(\"Expected field or expression after 'map'\");\n }\n\n if (this.peek().type !== \"LBRACE\") {\n // map field or map expr (no braces)\n // Try to parse as expression; if it's just a field access, use backward-compat field mode\n const expr = this.parseExpression();\n if (expr.kind === \"field_access\") {\n return { kind: \"map\", field: expr.path };\n }\n return { kind: \"map\", field: \"\", expression: expr };\n }\n\n // map { ... }\n this.advance(); // {\n this.skipNewlines();\n\n // Detect mode: peek ahead to see if first pair uses COLON (expression) or not (legacy)\n if (this.isAtEnd() || this.peek().type === \"RBRACE\") {\n this.advance(); // }\n return { kind: \"map\", field: \"\" };\n }\n\n // Save position to peek ahead\n const savedPos = this.pos;\n // Skip the first key token\n this.advance();\n const hasColon = !this.isAtEnd() && this.peek().type === \"COLON\";\n this.pos = savedPos; // restore\n\n if (hasColon) {\n // Expression mode: map { key: expr, ... }\n // Use Object.create(null) to prevent __proto__ pollution\n const exprMappings: Record<string, Expression> = Object.create(null);\n const seenExprKeys = new Set<string>();\n while (!this.isAtEnd() && this.peek().type !== \"RBRACE\") {\n const keyToken = this.expect(\"IDENTIFIER\", \"Expected key in map expression\");\n if (seenExprKeys.has(keyToken.value)) {\n throw new Error(`Duplicate map key '${keyToken.value}' at line ${keyToken.line}, column ${keyToken.column}`);\n }\n seenExprKeys.add(keyToken.value);\n this.expect(\"COLON\", \"Expected ':' after key in map expression\");\n const expr = this.parseExpression();\n exprMappings[keyToken.value] = expr;\n this.skipNewlines();\n if (!this.isAtEnd() && this.peek().type === \"COMMA\") {\n this.advance();\n this.skipNewlines();\n }\n }\n if (this.isAtEnd()) throw new Error(\"Expected '}' to close map expression\");\n this.advance(); // }\n return { kind: \"map\", field: \"\", exprMappings };\n }\n\n // Legacy mode: map { outputKey inputField, outputKey inputField }\n // Use Object.create(null) to prevent __proto__ pollution\n const mappings: Record<string, string> = Object.create(null);\n const seenMapKeys = new Set<string>();\n while (!this.isAtEnd() && this.peek().type !== \"RBRACE\") {\n const key = this.advance();\n if (seenMapKeys.has(key.value)) {\n throw new Error(`Duplicate map key '${key.value}' at line ${key.line}, column ${key.column}`);\n }\n seenMapKeys.add(key.value);\n const field = this.advance();\n mappings[key.value] = field.value;\n this.skipNewlines();\n if (!this.isAtEnd() && this.peek().type === \"COMMA\") {\n this.advance();\n this.skipNewlines();\n }\n }\n if (this.isAtEnd()) throw new Error(\"Expected '}' to close map object\");\n this.advance(); // }\n const firstKey = Object.keys(mappings)[0] ?? \"\";\n return { kind: \"map\", field: firstKey, mappings };\n }\n\n // ── Expression parsing (recursive descent with precedence) ──\n // expr = term (('+' | '-') term)*\n // term = factor (('*' | '/') factor)*\n // factor = '(' expr ')' | $var | literal | fieldAccess\n\n private parseExpression(): Expression {\n let left = this.parseTerm();\n while (!this.isAtEnd() && (this.peek().type === \"PLUS\" || this.peek().type === \"MINUS\")) {\n const opToken = this.advance();\n const right = this.parseTerm();\n left = { kind: \"binary\", op: opToken.value as \"+\" | \"-\", left, right };\n }\n return left;\n }\n\n private parseTerm(): Expression {\n let left = this.parseFactor();\n while (!this.isAtEnd() && (this.peek().type === \"STAR\" || this.peek().type === \"SLASH\")) {\n const opToken = this.advance();\n const right = this.parseFactor();\n left = { kind: \"binary\", op: opToken.value as \"*\" | \"/\", left, right };\n }\n return left;\n }\n\n private parseFactor(): Expression {\n if (this.isAtEnd()) {\n throw new Error(\"Expected expression\");\n }\n const t = this.peek();\n\n // Parenthesized expression\n if (t.type === \"LPAREN\") {\n this.advance(); // (\n const expr = this.parseExpression();\n this.expect(\"RPAREN\", \"Expected ')' after expression\");\n return expr;\n }\n\n // Variable reference: $name\n if (t.type === \"DOLLAR\") {\n this.advance(); // $\n const nameToken = this.expect(\"IDENTIFIER\", \"Expected variable name after '$'\");\n return { kind: \"var_ref\", name: nameToken.value };\n }\n\n // String literal\n if (t.type === \"STRING\") {\n this.advance();\n return { kind: \"literal\", value: t.value };\n }\n\n // Number literal\n if (t.type === \"NUMBER\") {\n this.advance();\n return { kind: \"literal\", value: parseFloat(t.value) };\n }\n\n // Field access: identifier (possibly dotted)\n if (t.type === \"IDENTIFIER\") {\n let field = this.advance().value;\n while (!this.isAtEnd() && this.peek().type === \"DOT\") {\n this.advance(); // .\n const next = this.advance();\n field += \".\" + next.value;\n }\n return { kind: \"field_access\", path: field };\n }\n\n throw new Error(`Unexpected token in expression: ${t.type} \"${t.value}\" at line ${t.line}, column ${t.column}`);\n }\n\n private parseAction(): ActionExpression {\n const actionToken = this.advance(); // action\n if (this.isAtEnd()) {\n throw new Error(`Expected path or action name after 'action' at line ${actionToken.line}, column ${actionToken.column}`);\n }\n\n const nextToken = this.peek();\n let path: string;\n let relative: boolean | undefined;\n\n if (nextToken.type === \"PATH\") {\n // Absolute action: action /tesla/.actions/honk\n path = this.advance().value;\n } else if (nextToken.type === \"IDENTIFIER\") {\n // Relative action: action turn_off\n path = this.advance().value;\n relative = true;\n } else {\n throw new Error(`Expected path or action name after 'action' at line ${actionToken.line}, column ${actionToken.column}`);\n }\n\n let params: Record<string, unknown> | undefined;\n\n // Optional params: { key: value, ... }\n if (!this.isAtEnd() && this.peek().type === \"LBRACE\") {\n this.advance(); // {\n params = {};\n this.skipNewlines();\n while (!this.isAtEnd() && this.peek().type !== \"RBRACE\") {\n const keyToken = this.expect(\"IDENTIFIER\", \"Expected parameter name in action params\");\n this.expect(\"COLON\", \"Expected ':' after parameter name\");\n if (this.isAtEnd()) {\n throw new Error(`Expected value after ':' at line ${keyToken.line}, column ${keyToken.column}`);\n }\n // Check for json() wrapper\n if (this.peek().type === \"IDENTIFIER\" && this.peek().value === \"json\") {\n const jsonToken = this.advance(); // consume \"json\"\n this.expect(\"LPAREN\", `Expected '(' after json at line ${jsonToken.line}, column ${jsonToken.column}`);\n params[keyToken.value] = this.parseJsonValue();\n this.expect(\"RPAREN\", \"Expected ')' to close json()\");\n } else {\n const valToken = this.advance();\n if (valToken.type === \"STRING\") {\n params[keyToken.value] = valToken.value;\n } else if (valToken.type === \"NUMBER\") {\n params[keyToken.value] = parseFloat(valToken.value);\n } else {\n throw new Error(`Expected string or number value for parameter '${keyToken.value}' at line ${valToken.line}, column ${valToken.column}`);\n }\n }\n this.skipNewlines();\n if (!this.isAtEnd() && this.peek().type === \"COMMA\") {\n this.advance();\n this.skipNewlines();\n }\n }\n if (this.isAtEnd()) throw new Error(\"Expected '}' to close action params\");\n this.advance(); // }\n }\n\n return { kind: \"action\", path, relative, params };\n }\n\n private parseRoute(): RouteExpression {\n const routeToken = this.advance(); // route\n if (this.isAtEnd() || (this.peek().type !== \"IDENTIFIER\" && this.peek().type !== \"DOT\")) {\n throw new Error(`Expected field name after 'route' at line ${routeToken.line}, column ${routeToken.column}`);\n }\n const field = this.parseFieldAccess();\n this.expect(\"LBRACE\", \"Expected '{' after route field\");\n this.skipNewlines();\n\n const branches: RouteBranch[] = [];\n let fallback: string | undefined;\n\n while (!this.isAtEnd() && this.peek().type !== \"RBRACE\") {\n // Each branch: \"value\" -> job target OR _ -> job target\n let value: string;\n if (this.peek().type === \"STRING\") {\n value = this.advance().value;\n } else if (this.peek().type === \"IDENTIFIER\" && this.peek().value === \"_\") {\n value = \"_\";\n this.advance();\n } else {\n throw new Error(`Expected string value or '_' in route branch at line ${this.peek().line}, column ${this.peek().column}`);\n }\n\n this.expect(\"ARROW\", \"Expected '->' in route branch\");\n this.expect(\"JOB\", \"Expected 'job' keyword in route branch target\");\n const targetToken = this.advance(); // job name\n\n if (value === \"_\") {\n fallback = targetToken.value;\n } else {\n branches.push({ value, targetJob: targetToken.value });\n }\n\n this.skipNewlines();\n if (!this.isAtEnd() && this.peek().type === \"COMMA\") {\n this.advance();\n this.skipNewlines();\n }\n }\n\n if (branches.length === 0 && !fallback) {\n throw new Error(`Route must have at least one branch at line ${routeToken.line}, column ${routeToken.column}`);\n }\n\n if (this.isAtEnd()) throw new Error(\"Expected '}' to close route\");\n this.advance(); // }\n\n return { kind: \"route\", field, branches, fallback };\n }\n\n private parseLookup(): LookupExpression {\n const lookupToken = this.advance(); // lookup\n if (this.isAtEnd() || this.peek().type !== \"PATH\") {\n throw new Error(`Expected path after 'lookup' at line ${lookupToken.line}, column ${lookupToken.column}`);\n }\n const pathToken = this.advance();\n // Expect \"on\" keyword\n if (this.isAtEnd() || this.peek().type !== \"ON\") {\n throw new Error(`Expected 'on' keyword after lookup path at line ${lookupToken.line}, column ${lookupToken.column}`);\n }\n this.advance(); // on\n const joinKey = this.parseFieldAccess();\n return { kind: \"lookup\", path: pathToken.value, joinKey };\n }\n\n private parseFanout(): PipelineStage {\n this.expect(\"LBRACE\", \"Expected '{' after 'fanout'\");\n const branches: PipelineStage[][] = [];\n\n this.skipNewlines();\n while (!this.isAtEnd() && this.peek().type !== \"RBRACE\") {\n // Each branch is a pipeline until COMMA or RBRACE\n const branch: PipelineStage[] = [];\n while (!this.isAtEnd() && this.peek().type !== \"COMMA\" && this.peek().type !== \"RBRACE\" && this.peek().type !== \"NEWLINE\") {\n if (this.peek().type === \"PIPE\") {\n this.advance();\n }\n branch.push(this.parseStage());\n }\n branches.push(branch);\n this.skipNewlines();\n if (!this.isAtEnd() && this.peek().type === \"COMMA\") {\n this.advance();\n this.skipNewlines();\n }\n }\n\n if (this.isAtEnd()) throw new Error(\"Expected '}' to close fanout\");\n this.advance(); // }\n\n return { kind: \"fanout\", branches };\n }\n\n /** Recursive descent JSON value parser using existing lexer tokens. */\n private parseJsonValue(): unknown {\n if (this.isAtEnd()) throw new Error(\"Expected JSON value (unexpected EOF)\");\n const t = this.peek();\n\n // Object: { ... }\n if (t.type === \"LBRACE\") {\n this.advance(); // {\n const obj: Record<string, unknown> = {};\n this.skipNewlines();\n while (!this.isAtEnd() && this.peek().type !== \"RBRACE\") {\n const keyToken = this.advance();\n if (keyToken.type !== \"STRING\" && keyToken.type !== \"IDENTIFIER\") {\n throw new Error(`Expected string key in JSON object at line ${keyToken.line}, column ${keyToken.column}`);\n }\n this.expect(\"COLON\", \"Expected ':' after key in JSON object\");\n obj[keyToken.value] = this.parseJsonValue();\n this.skipNewlines();\n if (!this.isAtEnd() && this.peek().type === \"COMMA\") {\n this.advance();\n this.skipNewlines();\n }\n }\n if (this.isAtEnd()) throw new Error(\"Expected '}' to close JSON object\");\n this.advance(); // }\n return obj;\n }\n\n // Array: [ ... ]\n if (t.type === \"LBRACKET\") {\n this.advance(); // [\n const arr: unknown[] = [];\n this.skipNewlines();\n while (!this.isAtEnd() && this.peek().type !== \"RBRACKET\") {\n arr.push(this.parseJsonValue());\n this.skipNewlines();\n if (!this.isAtEnd() && this.peek().type === \"COMMA\") {\n this.advance();\n this.skipNewlines();\n }\n }\n if (this.isAtEnd()) throw new Error(\"Expected ']' to close JSON array\");\n this.advance(); // ]\n return arr;\n }\n\n // String\n if (t.type === \"STRING\") {\n this.advance();\n return t.value;\n }\n\n // Number\n if (t.type === \"NUMBER\") {\n this.advance();\n return parseFloat(t.value);\n }\n\n // Boolean / null: true, false, null are lexed as IDENTIFIER\n if (t.type === \"IDENTIFIER\") {\n if (t.value === \"true\") { this.advance(); return true; }\n if (t.value === \"false\") { this.advance(); return false; }\n if (t.value === \"null\") { this.advance(); return null; }\n }\n\n throw new Error(`Unexpected token in json(): ${t.type} \"${t.value}\" at line ${t.line}, column ${t.column}`);\n }\n\n private peek(): Token {\n return this.tokens[this.pos];\n }\n\n private advance(): Token {\n return this.tokens[this.pos++];\n }\n\n private expect(type: TokenType, message: string): Token {\n if (this.isAtEnd()) throw new Error(message + \" (unexpected EOF)\");\n const t = this.peek();\n if (t.type !== type) {\n throw new Error(`${message}, got ${t.type} at line ${t.line}, column ${t.column}`);\n }\n return this.advance();\n }\n\n private isAtEnd(): boolean {\n return this.pos >= this.tokens.length || this.tokens[this.pos].type === \"EOF\";\n }\n\n private skipNewlines(): void {\n while (!this.isAtEnd() && this.peek().type === \"NEWLINE\") {\n this.pos++;\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"parser.mjs","names":[],"sources":["../src/parser.ts"],"sourcesContent":["import type { Token, TokenType } from \"./lexer.js\";\nimport type { Program, JobDeclaration, PipelineStage, Annotation, WhereClause, QueryCondition, TopLevelStatement, LetStatement, CountExpression, GroupByExpression, ActionExpression, Expression, MapExpression, RouteExpression, RouteBranch, LookupExpression, ParamDeclaration, TriggerDeclaration } from \"./ast.js\";\n\n/** Annotation names that are script-level: propagate to all jobs when declared before any statement. */\nconst SCRIPT_LEVEL_ANNOTATIONS = new Set([\"caps\", \"budget\"]);\n\n/** Merge script-level annotations with job-specific ones. Job overrides script-level by name. */\nfunction mergeAnnotations(script: Annotation[], job: Annotation[]): Annotation[] {\n if (script.length === 0) return job;\n if (job.length === 0) return [...script];\n const jobNames = new Set(job.map(a => a.name));\n return [...script.filter(a => !jobNames.has(a.name)), ...job];\n}\n\nexport class AshParser {\n private tokens: Token[] = [];\n private pos = 0;\n\n private letNames = new Set<string>();\n private paramNames = new Set<string>();\n private static readonly RESERVED_KEYWORDS = new Set([\"job\", \"find\", \"where\", \"map\", \"save\", \"publish\", \"tee\", \"fanout\", \"input\", \"output\", \"let\", \"action\", \"route\", \"lookup\", \"param\", \"on\"]);\n\n parse(tokens: Token[]): Program {\n this.tokens = tokens;\n this.pos = 0;\n this.letNames = new Set();\n this.paramNames = new Set();\n const statements: TopLevelStatement[] = [];\n\n this.skipNewlines();\n\n // Collect initial annotations (before any statement).\n // Split into script-level (caps, budget → propagate to all jobs) and first-job-specific.\n const initialAnnotations = this.parseAnnotations();\n const scriptAnnotations = initialAnnotations.filter(a => SCRIPT_LEVEL_ANNOTATIONS.has(a.name));\n const firstJobOnly = initialAnnotations.filter(a => !SCRIPT_LEVEL_ANNOTATIONS.has(a.name));\n let firstStatement = true;\n\n while (!this.isAtEnd()) {\n const jobAnnotations = this.parseAnnotations();\n if (this.isAtEnd()) break;\n\n const t = this.peek();\n if (t.type === \"JOB\") {\n const extra = firstStatement ? [...firstJobOnly, ...jobAnnotations] : jobAnnotations;\n const merged = mergeAnnotations(scriptAnnotations, extra);\n firstStatement = false;\n statements.push(this.parseJob(merged));\n } else if (t.type === \"OUTPUT\" && jobAnnotations.length === 0) {\n firstStatement = false;\n this.advance(); // output\n const msg = this.expect(\"STRING\", \"Expected string after 'output'\");\n statements.push({ kind: \"output\", message: msg.value });\n } else if (t.type === \"LET\" && jobAnnotations.length === 0) {\n firstStatement = false;\n statements.push(this.parseLet());\n } else if (t.type === \"PARAM\" && jobAnnotations.length === 0) {\n firstStatement = false;\n statements.push(this.parseParam());\n } else {\n throw new Error(`Expected 'job', 'output', 'let', or 'param' at top level, got ${t.type} at line ${t.line}, column ${t.column}`);\n }\n this.skipNewlines();\n }\n\n const jobs = statements.filter((s): s is JobDeclaration => s.kind === \"job\");\n return { statements, jobs };\n }\n\n private parseParam(): ParamDeclaration {\n const paramToken = this.advance(); // param\n if (this.isAtEnd()) {\n throw new Error(`Expected parameter name after 'param' at line ${paramToken.line}, column ${paramToken.column}`);\n }\n const nameToken = this.peek();\n if (nameToken.type !== \"IDENTIFIER\") {\n throw new Error(`Cannot use reserved keyword '${nameToken.value}' as param name at line ${nameToken.line}, column ${nameToken.column}`);\n }\n this.advance();\n if (AshParser.RESERVED_KEYWORDS.has(nameToken.value)) {\n throw new Error(`Cannot use reserved keyword '${nameToken.value}' as param name at line ${nameToken.line}, column ${nameToken.column}`);\n }\n if (this.paramNames.has(nameToken.value)) {\n throw new Error(`Duplicate param name '${nameToken.value}' at line ${nameToken.line}, column ${nameToken.column}`);\n }\n if (this.letNames.has(nameToken.value)) {\n throw new Error(`Param '${nameToken.value}' conflicts with existing let variable at line ${nameToken.line}, column ${nameToken.column}`);\n }\n this.expect(\"ASSIGN\", \"Expected '=' after param name\");\n const valToken = this.advance();\n let defaultValue: string | number;\n if (valToken.type === \"NUMBER\") {\n defaultValue = parseFloat(valToken.value);\n } else if (valToken.type === \"STRING\") {\n defaultValue = valToken.value;\n } else if (valToken.type === \"PATH\") {\n defaultValue = valToken.value;\n } else if (valToken.type === \"IDENTIFIER\") {\n defaultValue = valToken.value;\n } else {\n throw new Error(`Expected default value after '=' at line ${valToken.line}, column ${valToken.column}`);\n }\n this.paramNames.add(nameToken.value);\n return { kind: \"param\", name: nameToken.value, defaultValue };\n }\n\n private static readonly PIPELINE_STARTERS = new Set([\"FIND\", \"ACTION\", \"LOOKUP\", \"INPUT\"]);\n\n private parseLet(): LetStatement {\n const letToken = this.advance(); // let\n if (this.isAtEnd() || this.peek().type !== \"IDENTIFIER\") {\n throw new Error(`Expected variable name after 'let' at line ${letToken.line}, column ${letToken.column}`);\n }\n const nameToken = this.advance();\n if (AshParser.RESERVED_KEYWORDS.has(nameToken.value)) {\n throw new Error(`Cannot use reserved keyword '${nameToken.value}' as variable name at line ${nameToken.line}, column ${nameToken.column}`);\n }\n if (this.letNames.has(nameToken.value)) {\n throw new Error(`Duplicate variable name '${nameToken.value}' at line ${nameToken.line}, column ${nameToken.column}`);\n }\n if (this.paramNames.has(nameToken.value)) {\n throw new Error(`Let variable '${nameToken.value}' conflicts with existing param at line ${nameToken.line}, column ${nameToken.column}`);\n }\n this.expect(\"ASSIGN\", \"Expected '=' after variable name\");\n\n // Check if the value is a pipeline (starts with find, action, etc.)\n if (!this.isAtEnd() && AshParser.PIPELINE_STARTERS.has(this.peek().type)) {\n const pipeline = this.parseLetPipeline();\n this.letNames.add(nameToken.value);\n return { kind: \"let\", name: nameToken.value, value: 0, pipeline };\n }\n\n const valToken = this.advance();\n let value: string | number;\n if (valToken.type === \"NUMBER\") {\n value = parseFloat(valToken.value);\n } else if (valToken.type === \"STRING\") {\n value = valToken.value;\n } else if (valToken.type === \"IDENTIFIER\") {\n value = valToken.value;\n } else {\n throw new Error(`Expected value after '=' at line ${valToken.line}, column ${valToken.column}`);\n }\n this.letNames.add(nameToken.value);\n return { kind: \"let\", name: nameToken.value, value };\n }\n\n private parseLetPipeline(): PipelineStage[] {\n // Parse pipeline stages until NEWLINE, EOF, or JOB/LET/OUTPUT (top-level)\n const stages: PipelineStage[] = [];\n while (!this.isAtEnd()) {\n const t = this.peek();\n // Stop at newline or top-level keywords\n if (t.type === \"NEWLINE\" || t.type === \"JOB\" || t.type === \"LET\" || t.type === \"OUTPUT\" || t.type === \"AT\") break;\n\n if (t.type === \"PIPE\") {\n this.advance(); // |\n continue;\n }\n\n stages.push(this.parseStage());\n }\n return stages;\n }\n\n private parseAnnotations(): Annotation[] {\n const annotations: Annotation[] = [];\n while (!this.isAtEnd() && this.peek().type === \"AT\") {\n const at = this.advance(); // @\n if (this.isAtEnd() || this.peek().type !== \"IDENTIFIER\") {\n throw new Error(`Expected annotation name after '@' at line ${at.line}, column ${at.column}`);\n }\n const nameToken = this.advance();\n const args: string[] = [];\n\n if (!this.isAtEnd() && this.peek().type === \"LPAREN\") {\n this.advance(); // (\n while (!this.isAtEnd() && this.peek().type !== \"RPAREN\") {\n const arg = this.advance();\n args.push(arg.value);\n if (!this.isAtEnd() && this.peek().type === \"COMMA\") {\n this.advance();\n }\n }\n if (this.isAtEnd()) {\n throw new Error(`Unclosed annotation parenthesis at line ${at.line}, column ${at.column}`);\n }\n this.advance(); // )\n }\n\n annotations.push({ name: nameToken.value, args, line: at.line, column: at.column });\n this.skipNewlines();\n }\n return annotations;\n }\n\n private parseJob(annotations: Annotation[]): JobDeclaration {\n this.advance(); // 'job'\n this.skipNewlines();\n\n if (this.isAtEnd()) {\n throw new Error(\"Expected job name\");\n }\n const nameToken = this.advance();\n if (AshParser.RESERVED_KEYWORDS.has(nameToken.value)) {\n throw new Error(`Cannot use reserved keyword '${nameToken.value}' as job name at line ${nameToken.line}, column ${nameToken.column}`);\n }\n this.skipNewlines();\n\n // Optional trigger: `on /path:event` or `on cron(\"expr\")`\n let trigger: TriggerDeclaration | undefined;\n if (!this.isAtEnd() && this.peek().type === \"ON\") {\n const onToken = this.advance(); // on\n\n if (!this.isAtEnd() && this.peek().type === \"IDENTIFIER\" && this.peek().value === \"cron\") {\n // Cron trigger: on cron(\"*/5 * * * *\")\n this.advance(); // cron\n if (this.isAtEnd() || this.peek().type !== \"LPAREN\") {\n throw new Error(`Expected '(' after 'cron' at line ${onToken.line}, column ${onToken.column}`);\n }\n this.advance(); // (\n if (this.isAtEnd() || this.peek().type !== \"STRING\") {\n throw new Error(`Expected cron expression string after 'cron(' at line ${onToken.line}, column ${onToken.column}`);\n }\n const exprToken = this.advance();\n if (this.isAtEnd() || this.peek().type !== \"RPAREN\") {\n throw new Error(`Expected ')' after cron expression at line ${onToken.line}, column ${onToken.column}`);\n }\n this.advance(); // )\n trigger = { kind: \"cron\", expression: exprToken.value };\n } else {\n // Event trigger: on /path:event\n if (this.isAtEnd() || this.peek().type !== \"PATH\") {\n throw new Error(`Expected path after 'on' at line ${onToken.line}, column ${onToken.column}`);\n }\n const pathToken = this.advance();\n if (this.isAtEnd() || this.peek().type !== \"COLON\") {\n throw new Error(`Expected ':event' after trigger path at line ${pathToken.line}, column ${pathToken.column}`);\n }\n this.advance(); // :\n if (this.isAtEnd() || this.peek().type !== \"IDENTIFIER\") {\n throw new Error(`Expected event name after ':' at line ${pathToken.line}, column ${pathToken.column}`);\n }\n const eventToken = this.advance();\n trigger = { kind: \"event\", path: pathToken.value, event: eventToken.value };\n }\n this.skipNewlines();\n }\n\n if (this.isAtEnd() || this.peek().type !== \"LBRACE\") {\n const t = this.isAtEnd() ? nameToken : this.peek();\n throw new Error(`Expected '{' after job name at line ${t.line}, column ${t.column}`);\n }\n this.advance(); // {\n this.skipNewlines();\n\n const pipeline = this.parsePipeline();\n this.skipNewlines();\n\n if (this.isAtEnd() || this.peek().type !== \"RBRACE\") {\n throw new Error(\"Expected '}' to close job body\");\n }\n this.advance(); // }\n\n return { kind: \"job\", name: nameToken.value, annotations, pipeline, trigger };\n }\n\n private parsePipeline(): PipelineStage[] {\n const stages: PipelineStage[] = [];\n\n while (!this.isAtEnd() && this.peek().type !== \"RBRACE\") {\n if (this.peek().type === \"PIPE\") {\n if (stages.length === 0) {\n const t = this.peek();\n throw new Error(`Unexpected PIPE at line ${t.line}, column ${t.column}`);\n }\n this.advance(); // |\n this.skipNewlines();\n if (this.isAtEnd() || this.peek().type === \"RBRACE\") {\n throw new Error(\"Unexpected end after pipe operator\");\n }\n }\n\n this.skipNewlines();\n if (this.isAtEnd() || this.peek().type === \"RBRACE\") break;\n\n const stage = this.parseStage();\n stages.push(stage);\n this.skipNewlines();\n }\n\n return stages;\n }\n\n private parseStage(): PipelineStage {\n const t = this.peek();\n\n switch (t.type) {\n case \"FIND\": {\n this.advance();\n const path = this.expect(\"PATH\", \"Expected path after 'find'\");\n // Check for inline where: `find /path where condition`\n let query: QueryCondition | undefined;\n if (!this.isAtEnd() && this.peek().type === \"WHERE\") {\n this.advance(); // consume WHERE\n const where = this.parseWhere();\n query = { field: where.left, op: where.op, value: where.right };\n }\n return { kind: \"find\", path: path.value, query };\n }\n case \"WHERE\": {\n this.advance();\n return this.parseWhere();\n }\n case \"MAP\": {\n this.advance();\n return this.parseMapBody();\n }\n case \"SAVE\": {\n this.advance();\n const path = this.expect(\"PATH\", \"Expected path after 'save'\");\n return { kind: \"save\", path: path.value };\n }\n case \"PUBLISH\": {\n this.advance();\n const path = this.expect(\"PATH\", \"Expected path after 'publish'\");\n return { kind: \"publish\", path: path.value };\n }\n case \"TEE\": {\n this.advance();\n const path = this.expect(\"PATH\", \"Expected path after 'tee'\");\n return { kind: \"tee\", path: path.value };\n }\n case \"FANOUT\": {\n this.advance();\n return this.parseFanout();\n }\n case \"OUTPUT\": {\n this.advance();\n // Try parsing as expression (supports field access, binary, string literals)\n const expr = this.parseExpression();\n if (expr.kind === \"literal\" && typeof expr.value === \"string\") {\n // Backward compatible: string literal → use message field\n return { kind: \"output\", message: expr.value };\n }\n // Expression mode: evaluate per stream item at runtime\n return { kind: \"output\", message: \"\", expression: expr };\n }\n case \"INPUT\": {\n this.advance();\n const prompt = this.expect(\"STRING\", \"Expected string after 'input'\");\n return { kind: \"input\", prompt: prompt.value };\n }\n case \"COUNT\": {\n this.advance();\n return { kind: \"count\" } as CountExpression;\n }\n case \"GROUP_BY\": {\n this.advance();\n if (this.isAtEnd() || this.peek().type === \"PIPE\" || this.peek().type === \"RBRACE\" || this.peek().type === \"NEWLINE\") {\n throw new Error(\"Expected field name after 'group-by'\");\n }\n const field = this.parseFieldAccess();\n return { kind: \"group-by\", field } as GroupByExpression;\n }\n case \"ACTION\": {\n return this.parseAction();\n }\n case \"ROUTE\": {\n return this.parseRoute();\n }\n case \"LOOKUP\": {\n return this.parseLookup();\n }\n default:\n throw new Error(`Unexpected token ${t.type} \"${t.value}\" at line ${t.line}, column ${t.column}`);\n }\n }\n\n private parseWhere(): WhereClause {\n const left = this.parseFieldAccess();\n const opToken = this.advance();\n const opMap: Record<string, WhereClause[\"op\"]> = {\n \"==\": \"==\", \"!=\": \"!=\", \">\": \">\", \"<\": \"<\", \">=\": \">=\", \"<=\": \"<=\",\n };\n const op = opMap[opToken.value];\n if (!op) {\n throw new Error(`Expected comparison operator, got ${opToken.type} at line ${opToken.line}, column ${opToken.column}`);\n }\n\n let right: string | number;\n // Check for $variable reference\n if (!this.isAtEnd() && this.peek().type === \"DOLLAR\") {\n this.advance(); // $\n const varName = this.advance();\n right = \"$\" + varName.value;\n } else {\n const rightToken = this.advance();\n if (rightToken.type === \"NUMBER\") {\n right = parseFloat(rightToken.value);\n } else if (rightToken.type === \"STRING\") {\n right = rightToken.value;\n } else if (rightToken.type === \"IDENTIFIER\" || rightToken.type === \"PATH\") {\n let val = rightToken.value;\n while (!this.isAtEnd() && this.peek().type === \"DOT\") {\n this.advance();\n const next = this.advance();\n val += \".\" + next.value;\n }\n right = val;\n } else {\n right = rightToken.value;\n }\n }\n\n return { kind: \"where\", left, op, right };\n }\n\n private parseFieldAccess(): string {\n const first = this.advance();\n let field = first.value;\n while (!this.isAtEnd() && this.peek().type === \"DOT\") {\n this.advance(); // .\n const next = this.advance();\n field += \".\" + next.value;\n }\n return field;\n }\n\n private parseMapBody(): MapExpression {\n // map without braces: either simple field or expression\n if (this.isAtEnd() || this.peek().type === \"PIPE\" || this.peek().type === \"RBRACE\" || this.peek().type === \"NEWLINE\") {\n throw new Error(\"Expected field or expression after 'map'\");\n }\n\n if (this.peek().type !== \"LBRACE\") {\n // map field or map expr (no braces)\n // Try to parse as expression; if it's just a field access, use backward-compat field mode\n const expr = this.parseExpression();\n if (expr.kind === \"field_access\") {\n return { kind: \"map\", field: expr.path };\n }\n return { kind: \"map\", field: \"\", expression: expr };\n }\n\n // map { ... }\n this.advance(); // {\n this.skipNewlines();\n\n // Detect mode: peek ahead to see if first pair uses COLON (expression) or not (legacy)\n if (this.isAtEnd() || this.peek().type === \"RBRACE\") {\n this.advance(); // }\n return { kind: \"map\", field: \"\" };\n }\n\n // Save position to peek ahead\n const savedPos = this.pos;\n // Skip the first key token\n this.advance();\n const hasColon = !this.isAtEnd() && this.peek().type === \"COLON\";\n this.pos = savedPos; // restore\n\n if (hasColon) {\n // Expression mode: map { key: expr, ... }\n // Use Object.create(null) to prevent __proto__ pollution\n const exprMappings: Record<string, Expression> = Object.create(null);\n const seenExprKeys = new Set<string>();\n while (!this.isAtEnd() && this.peek().type !== \"RBRACE\") {\n const keyToken = this.expect(\"IDENTIFIER\", \"Expected key in map expression\");\n if (seenExprKeys.has(keyToken.value)) {\n throw new Error(`Duplicate map key '${keyToken.value}' at line ${keyToken.line}, column ${keyToken.column}`);\n }\n seenExprKeys.add(keyToken.value);\n this.expect(\"COLON\", \"Expected ':' after key in map expression\");\n const expr = this.parseExpression();\n exprMappings[keyToken.value] = expr;\n this.skipNewlines();\n if (!this.isAtEnd() && this.peek().type === \"COMMA\") {\n this.advance();\n this.skipNewlines();\n }\n }\n if (this.isAtEnd()) throw new Error(\"Expected '}' to close map expression\");\n this.advance(); // }\n return { kind: \"map\", field: \"\", exprMappings };\n }\n\n // Legacy mode: map { outputKey inputField, outputKey inputField }\n // Use Object.create(null) to prevent __proto__ pollution\n const mappings: Record<string, string> = Object.create(null);\n const seenMapKeys = new Set<string>();\n while (!this.isAtEnd() && this.peek().type !== \"RBRACE\") {\n const key = this.advance();\n if (seenMapKeys.has(key.value)) {\n throw new Error(`Duplicate map key '${key.value}' at line ${key.line}, column ${key.column}`);\n }\n seenMapKeys.add(key.value);\n const field = this.advance();\n mappings[key.value] = field.value;\n this.skipNewlines();\n if (!this.isAtEnd() && this.peek().type === \"COMMA\") {\n this.advance();\n this.skipNewlines();\n }\n }\n if (this.isAtEnd()) throw new Error(\"Expected '}' to close map object\");\n this.advance(); // }\n const firstKey = Object.keys(mappings)[0] ?? \"\";\n return { kind: \"map\", field: firstKey, mappings };\n }\n\n // ── Expression parsing (recursive descent with precedence) ──\n // expr = term (('+' | '-') term)*\n // term = factor (('*' | '/') factor)*\n // factor = '(' expr ')' | $var | literal | fieldAccess\n\n private parseExpression(): Expression {\n let left = this.parseTerm();\n while (!this.isAtEnd() && (this.peek().type === \"PLUS\" || this.peek().type === \"MINUS\")) {\n const opToken = this.advance();\n const right = this.parseTerm();\n left = { kind: \"binary\", op: opToken.value as \"+\" | \"-\", left, right };\n }\n return left;\n }\n\n private parseTerm(): Expression {\n let left = this.parseFactor();\n while (!this.isAtEnd() && (this.peek().type === \"STAR\" || this.peek().type === \"SLASH\")) {\n const opToken = this.advance();\n const right = this.parseFactor();\n left = { kind: \"binary\", op: opToken.value as \"*\" | \"/\", left, right };\n }\n return left;\n }\n\n private parseFactor(): Expression {\n if (this.isAtEnd()) {\n throw new Error(\"Expected expression\");\n }\n const t = this.peek();\n\n // Parenthesized expression\n if (t.type === \"LPAREN\") {\n this.advance(); // (\n const expr = this.parseExpression();\n this.expect(\"RPAREN\", \"Expected ')' after expression\");\n return expr;\n }\n\n // Variable reference: $name\n if (t.type === \"DOLLAR\") {\n this.advance(); // $\n const nameToken = this.expect(\"IDENTIFIER\", \"Expected variable name after '$'\");\n return { kind: \"var_ref\", name: nameToken.value };\n }\n\n // String literal\n if (t.type === \"STRING\") {\n this.advance();\n return { kind: \"literal\", value: t.value };\n }\n\n // Number literal\n if (t.type === \"NUMBER\") {\n this.advance();\n return { kind: \"literal\", value: parseFloat(t.value) };\n }\n\n // Array literal: [expr, expr, ...]\n if (t.type === \"LBRACKET\") {\n this.advance(); // [\n const elements: Expression[] = [];\n while (!this.isAtEnd() && this.peek().type !== \"RBRACKET\") {\n elements.push(this.parseExpression());\n if (!this.isAtEnd() && this.peek().type === \"COMMA\") {\n this.advance(); // ,\n }\n }\n this.expect(\"RBRACKET\", \"Expected ']' after array literal\");\n return { kind: \"array_literal\", elements };\n }\n\n // Object literal: { key: expr, key: expr, ... }\n if (t.type === \"LBRACE\") {\n this.advance(); // {\n const properties: Record<string, Expression> = {};\n while (!this.isAtEnd() && this.peek().type !== \"RBRACE\") {\n const keyToken = this.expect(\"IDENTIFIER\", \"Expected property name in object literal\");\n this.expect(\"COLON\", \"Expected ':' after property name\");\n properties[keyToken.value] = this.parseExpression();\n if (!this.isAtEnd() && this.peek().type === \"COMMA\") {\n this.advance(); // ,\n }\n }\n this.expect(\"RBRACE\", \"Expected '}' after object literal\");\n return { kind: \"object_literal\", properties };\n }\n\n // Field access: identifier (possibly dotted)\n if (t.type === \"IDENTIFIER\") {\n let field = this.advance().value;\n while (!this.isAtEnd() && this.peek().type === \"DOT\") {\n this.advance(); // .\n const next = this.advance();\n field += \".\" + next.value;\n }\n return { kind: \"field_access\", path: field };\n }\n\n throw new Error(`Unexpected token in expression: ${t.type} \"${t.value}\" at line ${t.line}, column ${t.column}`);\n }\n\n private parseAction(): ActionExpression {\n const actionToken = this.advance(); // action\n if (this.isAtEnd()) {\n throw new Error(`Expected path or action name after 'action' at line ${actionToken.line}, column ${actionToken.column}`);\n }\n\n const nextToken = this.peek();\n let path: string;\n let relative: boolean | undefined;\n\n if (nextToken.type === \"PATH\") {\n // Absolute action: action /tesla/.actions/honk\n path = this.advance().value;\n } else if (nextToken.type === \"IDENTIFIER\") {\n // Relative action: action turn_off\n path = this.advance().value;\n relative = true;\n } else {\n throw new Error(`Expected path or action name after 'action' at line ${actionToken.line}, column ${actionToken.column}`);\n }\n\n let params: Record<string, unknown> | undefined;\n\n // Optional params: { key: value, ... }\n if (!this.isAtEnd() && this.peek().type === \"LBRACE\") {\n this.advance(); // {\n params = {};\n this.skipNewlines();\n while (!this.isAtEnd() && this.peek().type !== \"RBRACE\") {\n const keyToken = this.expect(\"IDENTIFIER\", \"Expected parameter name in action params\");\n this.expect(\"COLON\", \"Expected ':' after parameter name\");\n if (this.isAtEnd()) {\n throw new Error(`Expected value after ':' at line ${keyToken.line}, column ${keyToken.column}`);\n }\n // Check for json() wrapper\n if (this.peek().type === \"IDENTIFIER\" && this.peek().value === \"json\") {\n const jsonToken = this.advance(); // consume \"json\"\n this.expect(\"LPAREN\", `Expected '(' after json at line ${jsonToken.line}, column ${jsonToken.column}`);\n params[keyToken.value] = this.parseJsonValue();\n this.expect(\"RPAREN\", \"Expected ')' to close json()\");\n } else {\n const valToken = this.advance();\n if (valToken.type === \"STRING\") {\n params[keyToken.value] = valToken.value;\n } else if (valToken.type === \"NUMBER\") {\n params[keyToken.value] = parseFloat(valToken.value);\n } else {\n throw new Error(`Expected string or number value for parameter '${keyToken.value}' at line ${valToken.line}, column ${valToken.column}`);\n }\n }\n this.skipNewlines();\n if (!this.isAtEnd() && this.peek().type === \"COMMA\") {\n this.advance();\n this.skipNewlines();\n }\n }\n if (this.isAtEnd()) throw new Error(\"Expected '}' to close action params\");\n this.advance(); // }\n }\n\n return { kind: \"action\", path, relative, params };\n }\n\n private parseRoute(): RouteExpression {\n const routeToken = this.advance(); // route\n if (this.isAtEnd() || (this.peek().type !== \"IDENTIFIER\" && this.peek().type !== \"DOT\")) {\n throw new Error(`Expected field name after 'route' at line ${routeToken.line}, column ${routeToken.column}`);\n }\n const field = this.parseFieldAccess();\n this.expect(\"LBRACE\", \"Expected '{' after route field\");\n this.skipNewlines();\n\n const branches: RouteBranch[] = [];\n let fallback: string | undefined;\n\n while (!this.isAtEnd() && this.peek().type !== \"RBRACE\") {\n // Each branch: \"value\" -> job target OR _ -> job target\n let value: string;\n if (this.peek().type === \"STRING\") {\n value = this.advance().value;\n } else if (this.peek().type === \"IDENTIFIER\" && this.peek().value === \"_\") {\n value = \"_\";\n this.advance();\n } else {\n throw new Error(`Expected string value or '_' in route branch at line ${this.peek().line}, column ${this.peek().column}`);\n }\n\n this.expect(\"ARROW\", \"Expected '->' in route branch\");\n this.expect(\"JOB\", \"Expected 'job' keyword in route branch target\");\n const targetToken = this.advance(); // job name\n\n if (value === \"_\") {\n fallback = targetToken.value;\n } else {\n branches.push({ value, targetJob: targetToken.value });\n }\n\n this.skipNewlines();\n if (!this.isAtEnd() && this.peek().type === \"COMMA\") {\n this.advance();\n this.skipNewlines();\n }\n }\n\n if (branches.length === 0 && !fallback) {\n throw new Error(`Route must have at least one branch at line ${routeToken.line}, column ${routeToken.column}`);\n }\n\n if (this.isAtEnd()) throw new Error(\"Expected '}' to close route\");\n this.advance(); // }\n\n return { kind: \"route\", field, branches, fallback };\n }\n\n private parseLookup(): LookupExpression {\n const lookupToken = this.advance(); // lookup\n if (this.isAtEnd() || this.peek().type !== \"PATH\") {\n throw new Error(`Expected path after 'lookup' at line ${lookupToken.line}, column ${lookupToken.column}`);\n }\n const pathToken = this.advance();\n // Expect \"on\" keyword\n if (this.isAtEnd() || this.peek().type !== \"ON\") {\n throw new Error(`Expected 'on' keyword after lookup path at line ${lookupToken.line}, column ${lookupToken.column}`);\n }\n this.advance(); // on\n const joinKey = this.parseFieldAccess();\n return { kind: \"lookup\", path: pathToken.value, joinKey };\n }\n\n private parseFanout(): PipelineStage {\n this.expect(\"LBRACE\", \"Expected '{' after 'fanout'\");\n const branches: PipelineStage[][] = [];\n\n this.skipNewlines();\n while (!this.isAtEnd() && this.peek().type !== \"RBRACE\") {\n // Each branch is a pipeline until COMMA or RBRACE\n const branch: PipelineStage[] = [];\n while (!this.isAtEnd() && this.peek().type !== \"COMMA\" && this.peek().type !== \"RBRACE\" && this.peek().type !== \"NEWLINE\") {\n if (this.peek().type === \"PIPE\") {\n this.advance();\n }\n branch.push(this.parseStage());\n }\n branches.push(branch);\n this.skipNewlines();\n if (!this.isAtEnd() && this.peek().type === \"COMMA\") {\n this.advance();\n this.skipNewlines();\n }\n }\n\n if (this.isAtEnd()) throw new Error(\"Expected '}' to close fanout\");\n this.advance(); // }\n\n return { kind: \"fanout\", branches };\n }\n\n /** Recursive descent JSON value parser using existing lexer tokens. */\n private parseJsonValue(): unknown {\n if (this.isAtEnd()) throw new Error(\"Expected JSON value (unexpected EOF)\");\n const t = this.peek();\n\n // Object: { ... }\n if (t.type === \"LBRACE\") {\n this.advance(); // {\n const obj: Record<string, unknown> = {};\n this.skipNewlines();\n while (!this.isAtEnd() && this.peek().type !== \"RBRACE\") {\n const keyToken = this.advance();\n if (keyToken.type !== \"STRING\" && keyToken.type !== \"IDENTIFIER\") {\n throw new Error(`Expected string key in JSON object at line ${keyToken.line}, column ${keyToken.column}`);\n }\n this.expect(\"COLON\", \"Expected ':' after key in JSON object\");\n obj[keyToken.value] = this.parseJsonValue();\n this.skipNewlines();\n if (!this.isAtEnd() && this.peek().type === \"COMMA\") {\n this.advance();\n this.skipNewlines();\n }\n }\n if (this.isAtEnd()) throw new Error(\"Expected '}' to close JSON object\");\n this.advance(); // }\n return obj;\n }\n\n // Array: [ ... ]\n if (t.type === \"LBRACKET\") {\n this.advance(); // [\n const arr: unknown[] = [];\n this.skipNewlines();\n while (!this.isAtEnd() && this.peek().type !== \"RBRACKET\") {\n arr.push(this.parseJsonValue());\n this.skipNewlines();\n if (!this.isAtEnd() && this.peek().type === \"COMMA\") {\n this.advance();\n this.skipNewlines();\n }\n }\n if (this.isAtEnd()) throw new Error(\"Expected ']' to close JSON array\");\n this.advance(); // ]\n return arr;\n }\n\n // String\n if (t.type === \"STRING\") {\n this.advance();\n return t.value;\n }\n\n // Number\n if (t.type === \"NUMBER\") {\n this.advance();\n return parseFloat(t.value);\n }\n\n // Boolean / null: true, false, null are lexed as IDENTIFIER\n if (t.type === \"IDENTIFIER\") {\n if (t.value === \"true\") { this.advance(); return true; }\n if (t.value === \"false\") { this.advance(); return false; }\n if (t.value === \"null\") { this.advance(); return null; }\n }\n\n throw new Error(`Unexpected token in json(): ${t.type} \"${t.value}\" at line ${t.line}, column ${t.column}`);\n }\n\n private peek(): Token {\n return this.tokens[this.pos];\n }\n\n private advance(): Token {\n return this.tokens[this.pos++];\n }\n\n private expect(type: TokenType, message: string): Token {\n if (this.isAtEnd()) throw new Error(message + \" (unexpected EOF)\");\n const t = this.peek();\n if (t.type !== type) {\n throw new Error(`${message}, got ${t.type} at line ${t.line}, column ${t.column}`);\n }\n return this.advance();\n }\n\n private isAtEnd(): boolean {\n return this.pos >= this.tokens.length || this.tokens[this.pos].type === \"EOF\";\n }\n\n private skipNewlines(): void {\n while (!this.isAtEnd() && this.peek().type === \"NEWLINE\") {\n this.pos++;\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/type-checker.cjs
CHANGED
|
@@ -269,6 +269,8 @@ function checkDivisionRisk(expr, jobName, errors) {
|
|
|
269
269
|
checkDivisionRisk(expr.left, jobName, errors);
|
|
270
270
|
checkDivisionRisk(expr.right, jobName, errors);
|
|
271
271
|
}
|
|
272
|
+
if (expr.kind === "array_literal") for (const el of expr.elements) checkDivisionRisk(el, jobName, errors);
|
|
273
|
+
if (expr.kind === "object_literal") for (const val of Object.values(expr.properties)) checkDivisionRisk(val, jobName, errors);
|
|
272
274
|
}
|
|
273
275
|
function collectJsReservedFields(expr, reserved, out) {
|
|
274
276
|
if (expr.kind === "field_access" && reserved.has(expr.path)) out.push(expr.path);
|
|
@@ -276,6 +278,8 @@ function collectJsReservedFields(expr, reserved, out) {
|
|
|
276
278
|
collectJsReservedFields(expr.left, reserved, out);
|
|
277
279
|
collectJsReservedFields(expr.right, reserved, out);
|
|
278
280
|
}
|
|
281
|
+
if (expr.kind === "array_literal") for (const el of expr.elements) collectJsReservedFields(el, reserved, out);
|
|
282
|
+
if (expr.kind === "object_literal") for (const val of Object.values(expr.properties)) collectJsReservedFields(val, reserved, out);
|
|
279
283
|
}
|
|
280
284
|
function classifyField(field, proto, jsReserved, protoOut, jsOut) {
|
|
281
285
|
if (proto.has(field)) protoOut.push(field);
|
|
@@ -343,6 +347,8 @@ function checkExpressionDivZero(stage, errors) {
|
|
|
343
347
|
function hasLiteralDivByZero(expr) {
|
|
344
348
|
if (expr.kind === "binary" && expr.op === "/" && expr.right.kind === "literal" && expr.right.value === 0) return true;
|
|
345
349
|
if (expr.kind === "binary") return hasLiteralDivByZero(expr.left) || hasLiteralDivByZero(expr.right);
|
|
350
|
+
if (expr.kind === "array_literal") return expr.elements.some(hasLiteralDivByZero);
|
|
351
|
+
if (expr.kind === "object_literal") return Object.values(expr.properties).some(hasLiteralDivByZero);
|
|
346
352
|
return false;
|
|
347
353
|
}
|
|
348
354
|
function checkRouteDag(program, errors) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type-checker.d.cts","names":[],"sources":["../src/type-checker.ts"],"mappings":";;;KAIY,cAAA;AAAA,UAgCK,aAAA;EACf,IAAA,EAAM,cAAA;EACN,QAAA;EACA,OAAA;EACA,IAAA;EACA,MAAA;EACA,KAAA;EACA,UAAA;AAAA;AAAA,KAGU,UAAA;AAAA,UAQK,SAAA;EACf,OAAA;EACA,KAAA;EACA,QAAA,GAAW,UAAA;EACX,MAAA,GAAS,UAAA;AAAA;AAAA,UAGM,YAAA;EACf,OAAA;EACA,IAAA;EACA,QAAA;AAAA;AAAA,iBAoBc,kBAAA,CAAmB,MAAA,EAAQ,aAAA,KAAkB,SAAA;AAAA,iBA4B7C,uBAAA,CAAwB,OAAA,EAAS,OAAA,GAAU,YAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"type-checker.d.cts","names":[],"sources":["../src/type-checker.ts"],"mappings":";;;KAIY,cAAA;AAAA,UAgCK,aAAA;EACf,IAAA,EAAM,cAAA;EACN,QAAA;EACA,OAAA;EACA,IAAA;EACA,MAAA;EACA,KAAA;EACA,UAAA;AAAA;AAAA,KAGU,UAAA;AAAA,UAQK,SAAA;EACf,OAAA;EACA,KAAA;EACA,QAAA,GAAW,UAAA;EACX,MAAA,GAAS,UAAA;AAAA;AAAA,UAGM,YAAA;EACf,OAAA;EACA,IAAA;EACA,QAAA;AAAA;AAAA,iBAoBc,kBAAA,CAAmB,MAAA,EAAQ,aAAA,KAAkB,SAAA;AAAA,iBA4B7C,uBAAA,CAAwB,OAAA,EAAS,OAAA,GAAU,YAAA;AAAA,iBA0iB3C,gBAAA,CAAiB,GAAA,EAAK,cAAA,GAAiB,YAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type-checker.d.mts","names":[],"sources":["../src/type-checker.ts"],"mappings":";;;KAIY,cAAA;AAAA,UAgCK,aAAA;EACf,IAAA,EAAM,cAAA;EACN,QAAA;EACA,OAAA;EACA,IAAA;EACA,MAAA;EACA,KAAA;EACA,UAAA;AAAA;AAAA,KAGU,UAAA;AAAA,UAQK,SAAA;EACf,OAAA;EACA,KAAA;EACA,QAAA,GAAW,UAAA;EACX,MAAA,GAAS,UAAA;AAAA;AAAA,UAGM,YAAA;EACf,OAAA;EACA,IAAA;EACA,QAAA;AAAA;AAAA,iBAoBc,kBAAA,CAAmB,MAAA,EAAQ,aAAA,KAAkB,SAAA;AAAA,iBA4B7C,uBAAA,CAAwB,OAAA,EAAS,OAAA,GAAU,YAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"type-checker.d.mts","names":[],"sources":["../src/type-checker.ts"],"mappings":";;;KAIY,cAAA;AAAA,UAgCK,aAAA;EACf,IAAA,EAAM,cAAA;EACN,QAAA;EACA,OAAA;EACA,IAAA;EACA,MAAA;EACA,KAAA;EACA,UAAA;AAAA;AAAA,KAGU,UAAA;AAAA,UAQK,SAAA;EACf,OAAA;EACA,KAAA;EACA,QAAA,GAAW,UAAA;EACX,MAAA,GAAS,UAAA;AAAA;AAAA,UAGM,YAAA;EACf,OAAA;EACA,IAAA;EACA,QAAA;AAAA;AAAA,iBAoBc,kBAAA,CAAmB,MAAA,EAAQ,aAAA,KAAkB,SAAA;AAAA,iBA4B7C,uBAAA,CAAwB,OAAA,EAAS,OAAA,GAAU,YAAA;AAAA,iBA0iB3C,gBAAA,CAAiB,GAAA,EAAK,cAAA,GAAiB,YAAA"}
|
package/dist/type-checker.mjs
CHANGED
|
@@ -268,6 +268,8 @@ function checkDivisionRisk(expr, jobName, errors) {
|
|
|
268
268
|
checkDivisionRisk(expr.left, jobName, errors);
|
|
269
269
|
checkDivisionRisk(expr.right, jobName, errors);
|
|
270
270
|
}
|
|
271
|
+
if (expr.kind === "array_literal") for (const el of expr.elements) checkDivisionRisk(el, jobName, errors);
|
|
272
|
+
if (expr.kind === "object_literal") for (const val of Object.values(expr.properties)) checkDivisionRisk(val, jobName, errors);
|
|
271
273
|
}
|
|
272
274
|
function collectJsReservedFields(expr, reserved, out) {
|
|
273
275
|
if (expr.kind === "field_access" && reserved.has(expr.path)) out.push(expr.path);
|
|
@@ -275,6 +277,8 @@ function collectJsReservedFields(expr, reserved, out) {
|
|
|
275
277
|
collectJsReservedFields(expr.left, reserved, out);
|
|
276
278
|
collectJsReservedFields(expr.right, reserved, out);
|
|
277
279
|
}
|
|
280
|
+
if (expr.kind === "array_literal") for (const el of expr.elements) collectJsReservedFields(el, reserved, out);
|
|
281
|
+
if (expr.kind === "object_literal") for (const val of Object.values(expr.properties)) collectJsReservedFields(val, reserved, out);
|
|
278
282
|
}
|
|
279
283
|
function classifyField(field, proto, jsReserved, protoOut, jsOut) {
|
|
280
284
|
if (proto.has(field)) protoOut.push(field);
|
|
@@ -342,6 +346,8 @@ function checkExpressionDivZero(stage, errors) {
|
|
|
342
346
|
function hasLiteralDivByZero(expr) {
|
|
343
347
|
if (expr.kind === "binary" && expr.op === "/" && expr.right.kind === "literal" && expr.right.value === 0) return true;
|
|
344
348
|
if (expr.kind === "binary") return hasLiteralDivByZero(expr.left) || hasLiteralDivByZero(expr.right);
|
|
349
|
+
if (expr.kind === "array_literal") return expr.elements.some(hasLiteralDivByZero);
|
|
350
|
+
if (expr.kind === "object_literal") return Object.values(expr.properties).some(hasLiteralDivByZero);
|
|
345
351
|
return false;
|
|
346
352
|
}
|
|
347
353
|
function checkRouteDag(program, errors) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type-checker.mjs","names":[],"sources":["../src/type-checker.ts"],"sourcesContent":["import type { Program, PipelineStage, JobDeclaration, Expression } from \"./ast.js\";\n\n// ── AshDiagnostic ──\n\nexport type DiagnosticCode =\n | \"ASH_SYNTAX_UNTERMINATED\"\n | \"ASH_SYNTAX_UNEXPECTED\"\n | \"ASH_UNKNOWN_COMMAND\"\n | \"ASH_TYPE_MISMATCH\"\n | \"ASH_UNDEFINED_VAR\"\n | \"ASH_DUPLICATE_VAR\"\n | \"ASH_DUPLICATE_JOB\"\n | \"ASH_ANNOTATION_INVALID\"\n | \"ASH_ANNOTATION_CONFLICT\"\n | \"ASH_READONLY_VIOLATION\"\n | \"ASH_CAP_DENIED\"\n | \"ASH_PATH_TRAVERSAL\"\n | \"ASH_EMPTY_JOB\"\n | \"ASH_DIV_ZERO\"\n | \"ASH_EXCESSIVE_RETRY\"\n | \"ASH_EVENT_LOOP\"\n | \"ASH_SELF_RECURSION\"\n | \"ASH_DIVISION_RISK\"\n | \"ASH_MIXED_SECURITY\"\n | \"ASH_JS_GLOBAL_FIELD\"\n | \"ASH_LET_WRITE\"\n | \"ASH_LET_PRE_APPROVAL\"\n | \"ASH_PARAM_WRITE_GATE\"\n | \"ASH_UNCAPPED_ACTION\"\n | \"ASH_ACTION_AMPLIFICATION\"\n | \"ASH_CROSS_PROVIDER_ACTION\"\n | \"ASH_RELATIVE_ACTION_NO_FIND\"\n | \"ASH_UNCAPPED_WRITE\"\n | \"ASH_BUDGET_EXCESSIVE\"\n;\n\nexport interface AshDiagnostic {\n code: DiagnosticCode;\n severity?: \"error\" | \"warning\";\n message: string;\n line?: number;\n column?: number;\n stage?: number;\n suggestion?: string[];\n}\n\nexport type StreamType = \"object_stream\" | \"single_object\" | \"none\";\n\nexport interface CommandManifest {\n name: string;\n stdin: StreamType;\n stdout: StreamType;\n}\n\nexport interface TypeError {\n message: string;\n stage: number;\n expected?: StreamType;\n actual?: StreamType;\n}\n\nexport interface CompileError {\n message: string;\n kind: \"prohibited\" | \"unknown_command\";\n severity?: \"error\" | \"warning\";\n}\n\nconst BUILTIN_MANIFESTS: Record<string, CommandManifest> = {\n find: { name: \"find\", stdin: \"none\", stdout: \"object_stream\" },\n where: { name: \"where\", stdin: \"object_stream\", stdout: \"object_stream\" },\n map: { name: \"map\", stdin: \"object_stream\", stdout: \"object_stream\" },\n save: { name: \"save\", stdin: \"object_stream\", stdout: \"none\" },\n publish: { name: \"publish\", stdin: \"object_stream\", stdout: \"none\" },\n tee: { name: \"tee\", stdin: \"object_stream\", stdout: \"object_stream\" },\n fanout: { name: \"fanout\", stdin: \"object_stream\", stdout: \"object_stream\" },\n output: { name: \"output\", stdin: \"object_stream\", stdout: \"object_stream\" },\n input: { name: \"input\", stdin: \"none\", stdout: \"object_stream\" },\n count: { name: \"count\", stdin: \"object_stream\", stdout: \"object_stream\" },\n \"group-by\": { name: \"group-by\", stdin: \"object_stream\", stdout: \"object_stream\" },\n action: { name: \"action\", stdin: \"object_stream\", stdout: \"object_stream\" },\n route: { name: \"route\", stdin: \"object_stream\", stdout: \"none\" },\n lookup: { name: \"lookup\", stdin: \"object_stream\", stdout: \"object_stream\" },\n};\n\nexport function checkPipelineTypes(stages: PipelineStage[]): TypeError[] {\n const errors: TypeError[] = [];\n if (stages.length <= 1) return errors;\n\n for (let i = 1; i < stages.length; i++) {\n const prev = stages[i - 1];\n const curr = stages[i];\n const prevManifest = BUILTIN_MANIFESTS[prev.kind];\n const currManifest = BUILTIN_MANIFESTS[curr.kind];\n\n if (!prevManifest || !currManifest) {\n errors.push({ message: `Unknown command at stage ${i}`, stage: i, kind: \"unknown_command\" } as any);\n continue;\n }\n\n if (prevManifest.stdout === \"none\" && currManifest.stdin === \"object_stream\") {\n errors.push({\n message: `Type mismatch at stage ${i}: ${prev.kind} outputs 'none' but ${curr.kind} expects 'object_stream'`,\n stage: i,\n expected: currManifest.stdin,\n actual: prevManifest.stdout,\n });\n }\n }\n\n return errors;\n}\n\nexport function checkProhibitedPatterns(program: Program): CompileError[] {\n const errors: CompileError[] = [];\n const jobNames = new Set(program.jobs.map(j => j.name));\n\n // 1. Duplicate job names + job name length\n const seenJobs = new Set<string>();\n for (const job of program.jobs) {\n if (seenJobs.has(job.name)) {\n errors.push({\n message: `Duplicate job name '${job.name}' — each job must have a unique name`,\n kind: \"prohibited\",\n });\n }\n if (job.name.length > 64) {\n errors.push({\n message: `Job name '${job.name.slice(0, 20)}...' exceeds 64 characters — keep job names concise`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n seenJobs.add(job.name);\n }\n\n // Program job count limit\n if (program.jobs.length > 50) {\n errors.push({\n message: `Program has ${program.jobs.length} jobs — programs with over 50 jobs may indicate over-complexity`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n\n for (const job of program.jobs) {\n // 5. Empty job → warning\n if (job.pipeline.length === 0) {\n errors.push({\n message: `Job '${job.name}' has empty pipeline — did you forget to add stages?`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n\n // Pipeline depth limit\n if (job.pipeline.length > 30) {\n errors.push({\n message: `Job '${job.name}' has ${job.pipeline.length} stages — pipelines over 30 stages may indicate over-complexity`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n\n for (const stage of job.pipeline) {\n // Check for prohibited patterns in stage names/kinds\n if (!Object.hasOwn(BUILTIN_MANIFESTS, stage.kind)) {\n errors.push({\n message: `Unknown command '${stage.kind}' — not registered in manifest`,\n kind: \"unknown_command\",\n });\n }\n // 4. Path traversal warning + $ in paths\n if (\"path\" in stage && typeof (stage as any).path === \"string\") {\n const path = (stage as any).path as string;\n if (path.includes(\"..\")) {\n errors.push({\n message: `Path '${path}' contains '..' — path traversal is not allowed`,\n kind: \"prohibited\",\n });\n }\n // Allow ${template.field} patterns in paths; block bare $ (e.g., $evil)\n const pathWithoutTemplates = path.replace(/\\$\\{[a-zA-Z_][a-zA-Z0-9_.]*\\}/g, \"\");\n if (pathWithoutTemplates.includes(\"$\")) {\n errors.push({\n message: `Path '${path}' contains '$' — variable interpolation in paths is not allowed`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n }\n // 6. Division by zero in literal expressions\n checkExpressionDivZero(stage, errors);\n // Route: validate branch targets exist + require fallback\n if (stage.kind === \"route\") {\n for (const branch of stage.branches) {\n if (!jobNames.has(branch.targetJob)) {\n errors.push({\n message: `Route target job '${branch.targetJob}' not found in program`,\n kind: \"prohibited\",\n });\n }\n }\n if (stage.fallback && !jobNames.has(stage.fallback)) {\n errors.push({\n message: `Route fallback job '${stage.fallback}' not found in program`,\n kind: \"prohibited\",\n });\n }\n if (!stage.fallback) {\n errors.push({\n message: `Job '${job.name}': route has no default branch — unmatched items are silently dropped, add '_ -> fallback_job' to handle unexpected values`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n }\n }\n }\n\n // Fanout nesting depth validation + duplicate write path detection\n for (const job of program.jobs) {\n checkFanoutDepth(job.pipeline, 0, errors);\n checkFanoutDuplicateWrites(job, errors);\n }\n\n // Tee chain limit: too many tee stages in one pipeline = data amplification risk\n for (const job of program.jobs) {\n const teeCount = job.pipeline.filter(s => s.kind === \"tee\").length;\n if (teeCount > 5) {\n errors.push({\n message: `Job '${job.name}' has ${teeCount} tee stages — more than 5 tee stages risks data amplification`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n }\n\n // Route DAG validation: check for cycles\n checkRouteDag(program, errors);\n\n // Trigger validation: trigger declarations are metadata-only\n for (const job of program.jobs) {\n if (job.trigger) {\n if (job.trigger.kind === \"event\") {\n if (!job.trigger.path || !job.trigger.event) {\n errors.push({\n message: `Job '${job.name}' has incomplete trigger declaration`,\n kind: \"prohibited\",\n });\n }\n } else if (job.trigger.kind === \"cron\") {\n if (!job.trigger.expression) {\n errors.push({\n message: `Job '${job.name}' has empty cron expression`,\n kind: \"prohibited\",\n });\n } else {\n // Basic cron field count validation (5, 6, or 7 fields expected)\n const fields = job.trigger.expression.trim().split(/\\s+/);\n if (fields.length < 5 || fields.length > 7) {\n errors.push({\n message: `Job '${job.name}' has invalid cron expression \"${job.trigger.expression}\" — expected 5-7 fields, got ${fields.length}`,\n kind: \"prohibited\",\n });\n }\n }\n }\n }\n }\n\n // Event loop detection: on /path:event + save/tee to same path (event triggers only)\n for (const job of program.jobs) {\n if (job.trigger?.kind === \"event\") {\n const triggerPath = job.trigger.path;\n for (const stage of job.pipeline) {\n if ((stage.kind === \"save\" || stage.kind === \"tee\") && stage.path === triggerPath) {\n errors.push({\n message: `Job '${job.name}' writes to its own trigger path '${triggerPath}' — this creates an event loop`,\n kind: \"prohibited\",\n });\n }\n }\n }\n }\n\n // Self-referencing action: action /.actions/* → potential recursion\n // Uses provider-relative path (/.actions/) since the absolute mount point varies.\n // Dangerous: /.actions/run (arbitrary code), /.actions/exec (script exec).\n // Allowed: /.actions/agent-run (budget-constrained LLM loop, not arbitrary code).\n const SELF_EXEC_BLOCKED = new Set([\"/.actions/run\", \"/.actions/exec\"]);\n for (const job of program.jobs) {\n for (const stage of job.pipeline) {\n if (stage.kind === \"action\" && SELF_EXEC_BLOCKED.has(stage.path)) {\n errors.push({\n message: `Job '${job.name}' calls action '${stage.path}' — self-referencing ASH execution risks recursion and bypasses compile-time checks`,\n kind: \"prohibited\",\n });\n }\n }\n }\n\n // Proto-pollution fields — ERROR (these can modify object prototype chain)\n const PROTO_POLLUTION_FIELDS = new Set([\n \"constructor\", \"__proto__\", \"prototype\",\n \"__defineGetter__\", \"__defineSetter__\",\n ]);\n // JS reserved identifiers — WARNING (runtime risk if eval-like constructs used)\n const JS_RESERVED_FIELDS = new Set([\n ...PROTO_POLLUTION_FIELDS,\n \"toString\", \"valueOf\", \"hasOwnProperty\",\n \"class\", \"return\", \"delete\", \"void\", \"typeof\", \"new\",\n ]);\n for (const job of program.jobs) {\n for (const stage of job.pipeline) {\n const protoFields: string[] = []; // error-level\n const jsFields: string[] = []; // warning-level\n if (stage.kind === \"map\") {\n if (stage.field) classifyField(stage.field, PROTO_POLLUTION_FIELDS, JS_RESERVED_FIELDS, protoFields, jsFields);\n if (stage.expression) collectJsReservedFields(stage.expression, JS_RESERVED_FIELDS, jsFields);\n if (stage.exprMappings) {\n // Check both KEYS and VALUES of map expressions\n for (const key of Object.keys(stage.exprMappings)) {\n classifyField(key, PROTO_POLLUTION_FIELDS, JS_RESERVED_FIELDS, protoFields, jsFields);\n }\n for (const expr of Object.values(stage.exprMappings)) {\n collectJsReservedFields(expr, JS_RESERVED_FIELDS, jsFields);\n }\n }\n if (stage.mappings) {\n for (const key of Object.keys(stage.mappings)) {\n classifyField(key, PROTO_POLLUTION_FIELDS, JS_RESERVED_FIELDS, protoFields, jsFields);\n }\n }\n // Map duplicate key detection\n if (stage.exprMappings) {\n checkMapDuplicateKeys(stage.exprMappings, job.name, errors);\n }\n if (stage.mappings) {\n checkMapDuplicateKeys(stage.mappings, job.name, errors);\n }\n }\n if (stage.kind === \"where\" && JS_RESERVED_FIELDS.has(stage.left)) {\n classifyField(stage.left, PROTO_POLLUTION_FIELDS, JS_RESERVED_FIELDS, protoFields, jsFields);\n }\n // Proto-pollution fields → error (blocks compilation)\n for (const field of protoFields) {\n errors.push({\n message: `Job '${job.name}': field '${field}' is a prototype pollution vector — not allowed as map key or field name`,\n kind: \"prohibited\",\n });\n }\n // Other JS reserved fields → warning\n for (const field of jsFields) {\n errors.push({\n message: `Job '${job.name}': field '${field}' is a JavaScript reserved identifier — may expose runtime internals`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n }\n }\n\n // Division risk: map expressions with field / field (could produce NaN)\n for (const job of program.jobs) {\n for (const stage of job.pipeline) {\n if (stage.kind === \"map\") {\n if (stage.expression) {\n checkDivisionRisk(stage.expression, job.name, errors);\n }\n if (stage.exprMappings) {\n for (const expr of Object.values(stage.exprMappings)) {\n checkDivisionRisk(expr, job.name, errors);\n }\n }\n }\n }\n }\n\n return errors;\n}\n\nfunction checkDivisionRisk(expr: Expression, jobName: string, errors: CompileError[]): void {\n if (expr.kind === \"binary\" && expr.op === \"/\") {\n // If the divisor is a field reference (not a literal), it could be zero → NaN risk\n if (expr.right.kind === \"field_access\") {\n errors.push({\n message: `Job '${jobName}': division by field '${expr.right.path}' may produce NaN/Infinity if zero — consider adding a where clause to filter`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n }\n // Recurse into sub-expressions\n if (expr.kind === \"binary\") {\n checkDivisionRisk(expr.left, jobName, errors);\n checkDivisionRisk(expr.right, jobName, errors);\n }\n}\n\nfunction collectJsReservedFields(expr: Expression, reserved: Set<string>, out: string[]): void {\n if (expr.kind === \"field_access\" && reserved.has(expr.path)) out.push(expr.path);\n if (expr.kind === \"binary\") {\n collectJsReservedFields(expr.left, reserved, out);\n collectJsReservedFields(expr.right, reserved, out);\n }\n}\n\nfunction classifyField(\n field: string,\n proto: Set<string>,\n jsReserved: Set<string>,\n protoOut: string[],\n jsOut: string[],\n): void {\n if (proto.has(field)) {\n protoOut.push(field);\n } else if (jsReserved.has(field)) {\n jsOut.push(field);\n }\n}\n\nfunction checkMapDuplicateKeys(obj: Record<string, unknown>, jobName: string, errors: CompileError[]): void {\n // Object.create(null) maps already deduplicate at parser level (last-write-wins).\n // But we track keys during parsing — duplicates are caught at parse time.\n // This is a defense-in-depth check for any keys that slip through.\n const seen = new Set<string>();\n for (const key of Object.keys(obj)) {\n if (seen.has(key)) {\n errors.push({\n message: `Job '${jobName}': map has duplicate key '${key}' — last value wins silently, potential data confusion`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n seen.add(key);\n }\n}\n\nfunction checkFanoutDepth(stages: PipelineStage[], depth: number, errors: CompileError[]): void {\n for (const stage of stages) {\n if (stage.kind === \"fanout\") {\n if (stage.branches.length > 10) {\n errors.push({\n message: `Fanout has ${stage.branches.length} branches — more than 10 branches may cause excessive parallelism`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n if (depth + 1 > 3) {\n errors.push({\n message: `Fanout nesting depth exceeds 3 levels — deeply nested fanout may indicate over-complexity`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n return;\n }\n for (const branch of stage.branches) {\n checkFanoutDepth(branch, depth + 1, errors);\n }\n }\n }\n}\n\nfunction checkFanoutDuplicateWrites(job: JobDeclaration, errors: CompileError[]): void {\n for (const stage of job.pipeline) {\n if (stage.kind !== \"fanout\") continue;\n // Collect write paths from each branch\n const writePaths = new Map<string, number>(); // path → branch count\n for (const branch of stage.branches) {\n for (const s of branch) {\n if ((s.kind === \"save\" || s.kind === \"tee\") && \"path\" in s) {\n const path = (s as any).path as string;\n writePaths.set(path, (writePaths.get(path) ?? 0) + 1);\n }\n }\n }\n for (const [path, count] of writePaths) {\n if (count > 1) {\n errors.push({\n message: `Job '${job.name}': fanout has ${count} branches writing to '${path}' — last write wins silently, audit trail lost`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n }\n }\n}\n\nfunction checkExpressionDivZero(stage: PipelineStage, errors: CompileError[]): void {\n if (stage.kind !== \"map\") return;\n if (stage.exprMappings) {\n for (const expr of Object.values(stage.exprMappings)) {\n if (hasLiteralDivByZero(expr)) {\n errors.push({ message: \"Expression contains literal division by zero\", kind: \"prohibited\", severity: \"warning\" });\n }\n }\n }\n if (stage.expression && hasLiteralDivByZero(stage.expression)) {\n errors.push({ message: \"Expression contains literal division by zero\", kind: \"prohibited\", severity: \"warning\" });\n }\n}\n\nfunction hasLiteralDivByZero(expr: Expression): boolean {\n if (expr.kind === \"binary\" && expr.op === \"/\" && expr.right.kind === \"literal\" && expr.right.value === 0) {\n return true;\n }\n if (expr.kind === \"binary\") {\n return hasLiteralDivByZero(expr.left) || hasLiteralDivByZero(expr.right);\n }\n return false;\n}\n\nfunction checkRouteDag(program: Program, errors: CompileError[]): void {\n // Build adjacency: job -> [target jobs via route]\n const graph = new Map<string, Set<string>>();\n for (const job of program.jobs) {\n const targets = new Set<string>();\n for (const stage of job.pipeline) {\n if (stage.kind === \"route\") {\n for (const branch of stage.branches) targets.add(branch.targetJob);\n if (stage.fallback) targets.add(stage.fallback);\n }\n }\n if (targets.size > 0) graph.set(job.name, targets);\n }\n\n // DFS cycle detection\n const visited = new Set<string>();\n const inStack = new Set<string>();\n\n function dfs(node: string): boolean {\n if (inStack.has(node)) return true; // cycle\n if (visited.has(node)) return false;\n visited.add(node);\n inStack.add(node);\n const neighbors = graph.get(node);\n if (neighbors) {\n for (const n of neighbors) {\n if (dfs(n)) {\n errors.push({\n message: `Route cycle detected: job '${node}' → job '${n}' forms a cycle`,\n kind: \"prohibited\",\n });\n return true;\n }\n }\n }\n inStack.delete(node);\n return false;\n }\n\n for (const node of graph.keys()) {\n dfs(node);\n }\n}\n\nconst ANNOTATION_VALIDATORS: Record<string, (args: string[]) => string | null> = {\n approval: (args) => {\n if (args.length === 0) return null;\n const valid = new Set([\"human\", \"auto\"]);\n if (!valid.has(args[0])) return `@approval accepts 'human' or 'auto', got '${args[0]}'`;\n return null;\n },\n readonly: () => null,\n retry: (args) => {\n if (args.length === 0) return null;\n const n = Number(args[0]);\n if (isNaN(n) || !Number.isInteger(n) || n < 0) return `@retry requires a non-negative integer, got '${args[0]}'`;\n return null;\n },\n timeout: (args) => {\n if (args.length === 0) return null;\n const n = Number(args[0]);\n if (isNaN(n) || n <= 0) return `@timeout requires a positive number, got '${args[0]}'`;\n if (n > 3600000) return `@timeout(${args[0]}) exceeds hard limit of 3600000ms (1 hour) — reduce timeout`;\n return null;\n },\n on_error: (args) => {\n if (args.length === 0) return `@on_error requires a strategy: skip, fail, or save <path>`;\n const valid = new Set([\"skip\", \"fail\", \"save\"]);\n if (!valid.has(args[0])) return `@on_error accepts 'skip', 'fail', or 'save', got '${args[0]}'`;\n return null;\n },\n budget: (args) => {\n if (args.length === 0) return `@budget requires at least one limit: @budget(actions 50, records 10000)`;\n if (args.length % 2 !== 0) return `@budget requires dimension/value pairs — got odd number of args`;\n const validDims = new Set([\"actions\", \"writes\", \"records\", \"tokens\", \"cost\"]);\n for (let i = 0; i < args.length; i += 2) {\n if (!validDims.has(args[i])) return `@budget dimension must be one of: ${[...validDims].join(\", \")} — got '${args[i]}'`;\n const n = Number(args[i + 1]);\n if (isNaN(n) || n <= 0) return `@budget value must be a positive number, got '${args[i + 1]}'`;\n }\n return null;\n },\n caps: (args) => {\n if (args.length === 0) return `@caps requires at least one capability: @caps(read /path/*, write /out/*)`;\n if (args.length % 2 !== 0) return `@caps requires operation/path pairs — got odd number of args`;\n const validOps = new Set([\"read\", \"write\", \"exec\"]);\n for (let i = 0; i < args.length; i += 2) {\n if (!validOps.has(args[i])) return `@caps operation must be 'read', 'write', or 'exec', got '${args[i]}'`;\n if (!args[i + 1].startsWith(\"/\")) return `@caps path must start with '/', got '${args[i + 1]}'`;\n }\n return null;\n },\n};\n\nexport const BUILTIN_COMMAND_NAMES = Object.keys(BUILTIN_MANIFESTS);\n\nexport function typeErrorsToDiagnostics(errors: TypeError[]): AshDiagnostic[] {\n return errors.map(e => ({\n code: \"ASH_TYPE_MISMATCH\" as DiagnosticCode,\n message: e.message,\n stage: e.stage,\n }));\n}\n\nexport function compileErrorsToDiagnostics(errors: CompileError[]): AshDiagnostic[] {\n return errors.map(e => ({\n code: (e.kind === \"unknown_command\" ? \"ASH_UNKNOWN_COMMAND\" : \"ASH_ANNOTATION_INVALID\") as DiagnosticCode,\n severity: e.severity,\n message: e.message,\n suggestion: e.kind === \"unknown_command\" ? BUILTIN_COMMAND_NAMES : undefined,\n }));\n}\n\nexport function annotationErrorsToDiagnostics(errors: CompileError[]): AshDiagnostic[] {\n return errors.map(e => ({\n code: \"ASH_ANNOTATION_INVALID\" as DiagnosticCode,\n severity: e.severity,\n message: e.message,\n }));\n}\n\nexport function parseSyntaxError(errorMessage: string): AshDiagnostic {\n const isUnterminated = /unterminated/i.test(errorMessage);\n const isDuplicate = /duplicate variable/i.test(errorMessage);\n const lineMatch = errorMessage.match(/line\\s+(\\d+)/i);\n const colMatch = errorMessage.match(/column\\s+(\\d+)/i);\n // Strip \"Error:\" prefix if present\n const cleanMsg = errorMessage.replace(/^Error:\\s*/i, \"\");\n return {\n code: isDuplicate ? \"ASH_DUPLICATE_VAR\" : isUnterminated ? \"ASH_SYNTAX_UNTERMINATED\" : \"ASH_SYNTAX_UNEXPECTED\",\n message: cleanMsg,\n line: lineMatch ? Number(lineMatch[1]) : undefined,\n column: colMatch ? Number(colMatch[1]) : undefined,\n };\n}\n\nexport function checkAnnotations(job: JobDeclaration): CompileError[] {\n const errors: CompileError[] = [];\n\n for (const ann of job.annotations) {\n const validator = Object.hasOwn(ANNOTATION_VALIDATORS, ann.name) ? ANNOTATION_VALIDATORS[ann.name] : undefined;\n if (!validator) {\n errors.push({\n message: `Unknown annotation '@${ann.name}' — not in allowed set`,\n kind: \"prohibited\",\n });\n continue;\n }\n const err = validator(ann.args);\n if (err) {\n errors.push({ message: err, kind: \"prohibited\" });\n }\n }\n\n // 2. Duplicate annotation detection — last-wins is an audit deception vector\n const annCounts = new Map<string, number>();\n for (const ann of job.annotations) {\n annCounts.set(ann.name, (annCounts.get(ann.name) ?? 0) + 1);\n }\n for (const [name, count] of annCounts) {\n if (count > 1) {\n errors.push({\n message: `Job '${job.name}': @${name} appears ${count} times — last value wins silently, potential audit deception`,\n kind: \"prohibited\",\n });\n }\n }\n\n // 3. @retry hard limit (error, not warning)\n const retryAnns = job.annotations.filter(a => a.name === \"retry\");\n for (const ann of retryAnns) {\n if (ann.args.length > 0) {\n const n = Number(ann.args[0]);\n if (!isNaN(n) && n > 100) {\n errors.push({ message: `@retry(${n}) exceeds hard limit of 100 — reduce retry count`, kind: \"prohibited\" });\n }\n }\n }\n\n // 4. @approval conflict: human and auto on same job\n const approvalAnns = job.annotations.filter(a => a.name === \"approval\");\n if (approvalAnns.length > 1) {\n const values = approvalAnns.map(a => a.args[0]).filter(Boolean);\n if (values.includes(\"human\") && values.includes(\"auto\")) {\n errors.push({ message: `@approval conflict: 'human' and 'auto' cannot both be set on job '${job.name}'`, kind: \"prohibited\" });\n }\n }\n\n return errors;\n}\n\n// ── @caps: capability-based path security ──\n\nexport interface CapEntry {\n op: \"read\" | \"write\" | \"exec\";\n pathGlob: string;\n}\n\nexport function parseCaps(ann: { args: string[] }): CapEntry[] {\n const caps: CapEntry[] = [];\n for (let i = 0; i < ann.args.length; i += 2) {\n caps.push({ op: ann.args[i] as CapEntry[\"op\"], pathGlob: ann.args[i + 1] });\n }\n return caps;\n}\n\nexport function capMatches(pathGlob: string, actual: string): boolean {\n if (pathGlob === actual) return true;\n if (pathGlob.endsWith(\"/*\")) {\n const prefix = pathGlob.slice(0, -1); // \"/ha/sensors/\" (keep trailing /)\n return actual.startsWith(prefix) || actual === pathGlob.slice(0, -2); // exact parent match\n }\n if (pathGlob.endsWith(\"*\")) {\n return actual.startsWith(pathGlob.slice(0, -1));\n }\n return false;\n}\n\nexport function hasCapFor(caps: CapEntry[], op: CapEntry[\"op\"], path: string): boolean {\n return caps.some(c => c.op === op && capMatches(c.pathGlob, path));\n}\n\nconst STAGE_OP_MAP: Record<string, CapEntry[\"op\"]> = {\n find: \"read\",\n lookup: \"read\",\n save: \"write\",\n publish: \"write\",\n tee: \"write\",\n action: \"exec\",\n};\n\nfunction checkStagesCaps(stages: PipelineStage[], caps: CapEntry[], diagnostics: AshDiagnostic[]): void {\n for (const stage of stages) {\n // Skip static caps check for relative actions (path resolved at runtime, caps checked per-exec)\n if (stage.kind === \"action\" && stage.relative) continue;\n // Skip static caps check for template paths (resolved at runtime, caps checked post-resolution)\n if (\"path\" in stage && typeof (stage as any).path === \"string\" && (stage as any).path.includes(\"${\")) continue;\n\n const op = Object.hasOwn(STAGE_OP_MAP, stage.kind) ? STAGE_OP_MAP[stage.kind] : undefined;\n if (op && \"path\" in stage && typeof (stage as any).path === \"string\") {\n const path = (stage as any).path as string;\n if (!hasCapFor(caps, op, path)) {\n diagnostics.push({\n code: \"ASH_CAP_DENIED\",\n message: `@caps denied: '${stage.kind} ${path}' requires '${op}' capability for '${path}'`,\n });\n }\n }\n if (stage.kind === \"fanout\") {\n for (const branch of stage.branches) {\n checkStagesCaps(branch, caps, diagnostics);\n }\n }\n }\n}\n\nexport function checkJobCaps(job: JobDeclaration): AshDiagnostic[] {\n const capsAnns = job.annotations.filter(a => a.name === \"caps\");\n if (capsAnns.length === 0) return [];\n\n const diagnostics: AshDiagnostic[] = [];\n const allCaps: CapEntry[] = [];\n for (const ann of capsAnns) {\n if (ann.args.length > 0 && ann.args.length % 2 === 0) {\n allCaps.push(...parseCaps(ann));\n }\n }\n\n if (allCaps.length === 0) return diagnostics;\n\n checkStagesCaps(job.pipeline, allCaps, diagnostics);\n return diagnostics;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"type-checker.mjs","names":[],"sources":["../src/type-checker.ts"],"sourcesContent":["import type { Program, PipelineStage, JobDeclaration, Expression } from \"./ast.js\";\n\n// ── AshDiagnostic ──\n\nexport type DiagnosticCode =\n | \"ASH_SYNTAX_UNTERMINATED\"\n | \"ASH_SYNTAX_UNEXPECTED\"\n | \"ASH_UNKNOWN_COMMAND\"\n | \"ASH_TYPE_MISMATCH\"\n | \"ASH_UNDEFINED_VAR\"\n | \"ASH_DUPLICATE_VAR\"\n | \"ASH_DUPLICATE_JOB\"\n | \"ASH_ANNOTATION_INVALID\"\n | \"ASH_ANNOTATION_CONFLICT\"\n | \"ASH_READONLY_VIOLATION\"\n | \"ASH_CAP_DENIED\"\n | \"ASH_PATH_TRAVERSAL\"\n | \"ASH_EMPTY_JOB\"\n | \"ASH_DIV_ZERO\"\n | \"ASH_EXCESSIVE_RETRY\"\n | \"ASH_EVENT_LOOP\"\n | \"ASH_SELF_RECURSION\"\n | \"ASH_DIVISION_RISK\"\n | \"ASH_MIXED_SECURITY\"\n | \"ASH_JS_GLOBAL_FIELD\"\n | \"ASH_LET_WRITE\"\n | \"ASH_LET_PRE_APPROVAL\"\n | \"ASH_PARAM_WRITE_GATE\"\n | \"ASH_UNCAPPED_ACTION\"\n | \"ASH_ACTION_AMPLIFICATION\"\n | \"ASH_CROSS_PROVIDER_ACTION\"\n | \"ASH_RELATIVE_ACTION_NO_FIND\"\n | \"ASH_UNCAPPED_WRITE\"\n | \"ASH_BUDGET_EXCESSIVE\"\n;\n\nexport interface AshDiagnostic {\n code: DiagnosticCode;\n severity?: \"error\" | \"warning\";\n message: string;\n line?: number;\n column?: number;\n stage?: number;\n suggestion?: string[];\n}\n\nexport type StreamType = \"object_stream\" | \"single_object\" | \"none\";\n\nexport interface CommandManifest {\n name: string;\n stdin: StreamType;\n stdout: StreamType;\n}\n\nexport interface TypeError {\n message: string;\n stage: number;\n expected?: StreamType;\n actual?: StreamType;\n}\n\nexport interface CompileError {\n message: string;\n kind: \"prohibited\" | \"unknown_command\";\n severity?: \"error\" | \"warning\";\n}\n\nconst BUILTIN_MANIFESTS: Record<string, CommandManifest> = {\n find: { name: \"find\", stdin: \"none\", stdout: \"object_stream\" },\n where: { name: \"where\", stdin: \"object_stream\", stdout: \"object_stream\" },\n map: { name: \"map\", stdin: \"object_stream\", stdout: \"object_stream\" },\n save: { name: \"save\", stdin: \"object_stream\", stdout: \"none\" },\n publish: { name: \"publish\", stdin: \"object_stream\", stdout: \"none\" },\n tee: { name: \"tee\", stdin: \"object_stream\", stdout: \"object_stream\" },\n fanout: { name: \"fanout\", stdin: \"object_stream\", stdout: \"object_stream\" },\n output: { name: \"output\", stdin: \"object_stream\", stdout: \"object_stream\" },\n input: { name: \"input\", stdin: \"none\", stdout: \"object_stream\" },\n count: { name: \"count\", stdin: \"object_stream\", stdout: \"object_stream\" },\n \"group-by\": { name: \"group-by\", stdin: \"object_stream\", stdout: \"object_stream\" },\n action: { name: \"action\", stdin: \"object_stream\", stdout: \"object_stream\" },\n route: { name: \"route\", stdin: \"object_stream\", stdout: \"none\" },\n lookup: { name: \"lookup\", stdin: \"object_stream\", stdout: \"object_stream\" },\n};\n\nexport function checkPipelineTypes(stages: PipelineStage[]): TypeError[] {\n const errors: TypeError[] = [];\n if (stages.length <= 1) return errors;\n\n for (let i = 1; i < stages.length; i++) {\n const prev = stages[i - 1];\n const curr = stages[i];\n const prevManifest = BUILTIN_MANIFESTS[prev.kind];\n const currManifest = BUILTIN_MANIFESTS[curr.kind];\n\n if (!prevManifest || !currManifest) {\n errors.push({ message: `Unknown command at stage ${i}`, stage: i, kind: \"unknown_command\" } as any);\n continue;\n }\n\n if (prevManifest.stdout === \"none\" && currManifest.stdin === \"object_stream\") {\n errors.push({\n message: `Type mismatch at stage ${i}: ${prev.kind} outputs 'none' but ${curr.kind} expects 'object_stream'`,\n stage: i,\n expected: currManifest.stdin,\n actual: prevManifest.stdout,\n });\n }\n }\n\n return errors;\n}\n\nexport function checkProhibitedPatterns(program: Program): CompileError[] {\n const errors: CompileError[] = [];\n const jobNames = new Set(program.jobs.map(j => j.name));\n\n // 1. Duplicate job names + job name length\n const seenJobs = new Set<string>();\n for (const job of program.jobs) {\n if (seenJobs.has(job.name)) {\n errors.push({\n message: `Duplicate job name '${job.name}' — each job must have a unique name`,\n kind: \"prohibited\",\n });\n }\n if (job.name.length > 64) {\n errors.push({\n message: `Job name '${job.name.slice(0, 20)}...' exceeds 64 characters — keep job names concise`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n seenJobs.add(job.name);\n }\n\n // Program job count limit\n if (program.jobs.length > 50) {\n errors.push({\n message: `Program has ${program.jobs.length} jobs — programs with over 50 jobs may indicate over-complexity`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n\n for (const job of program.jobs) {\n // 5. Empty job → warning\n if (job.pipeline.length === 0) {\n errors.push({\n message: `Job '${job.name}' has empty pipeline — did you forget to add stages?`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n\n // Pipeline depth limit\n if (job.pipeline.length > 30) {\n errors.push({\n message: `Job '${job.name}' has ${job.pipeline.length} stages — pipelines over 30 stages may indicate over-complexity`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n\n for (const stage of job.pipeline) {\n // Check for prohibited patterns in stage names/kinds\n if (!Object.hasOwn(BUILTIN_MANIFESTS, stage.kind)) {\n errors.push({\n message: `Unknown command '${stage.kind}' — not registered in manifest`,\n kind: \"unknown_command\",\n });\n }\n // 4. Path traversal warning + $ in paths\n if (\"path\" in stage && typeof (stage as any).path === \"string\") {\n const path = (stage as any).path as string;\n if (path.includes(\"..\")) {\n errors.push({\n message: `Path '${path}' contains '..' — path traversal is not allowed`,\n kind: \"prohibited\",\n });\n }\n // Allow ${template.field} patterns in paths; block bare $ (e.g., $evil)\n const pathWithoutTemplates = path.replace(/\\$\\{[a-zA-Z_][a-zA-Z0-9_.]*\\}/g, \"\");\n if (pathWithoutTemplates.includes(\"$\")) {\n errors.push({\n message: `Path '${path}' contains '$' — variable interpolation in paths is not allowed`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n }\n // 6. Division by zero in literal expressions\n checkExpressionDivZero(stage, errors);\n // Route: validate branch targets exist + require fallback\n if (stage.kind === \"route\") {\n for (const branch of stage.branches) {\n if (!jobNames.has(branch.targetJob)) {\n errors.push({\n message: `Route target job '${branch.targetJob}' not found in program`,\n kind: \"prohibited\",\n });\n }\n }\n if (stage.fallback && !jobNames.has(stage.fallback)) {\n errors.push({\n message: `Route fallback job '${stage.fallback}' not found in program`,\n kind: \"prohibited\",\n });\n }\n if (!stage.fallback) {\n errors.push({\n message: `Job '${job.name}': route has no default branch — unmatched items are silently dropped, add '_ -> fallback_job' to handle unexpected values`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n }\n }\n }\n\n // Fanout nesting depth validation + duplicate write path detection\n for (const job of program.jobs) {\n checkFanoutDepth(job.pipeline, 0, errors);\n checkFanoutDuplicateWrites(job, errors);\n }\n\n // Tee chain limit: too many tee stages in one pipeline = data amplification risk\n for (const job of program.jobs) {\n const teeCount = job.pipeline.filter(s => s.kind === \"tee\").length;\n if (teeCount > 5) {\n errors.push({\n message: `Job '${job.name}' has ${teeCount} tee stages — more than 5 tee stages risks data amplification`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n }\n\n // Route DAG validation: check for cycles\n checkRouteDag(program, errors);\n\n // Trigger validation: trigger declarations are metadata-only\n for (const job of program.jobs) {\n if (job.trigger) {\n if (job.trigger.kind === \"event\") {\n if (!job.trigger.path || !job.trigger.event) {\n errors.push({\n message: `Job '${job.name}' has incomplete trigger declaration`,\n kind: \"prohibited\",\n });\n }\n } else if (job.trigger.kind === \"cron\") {\n if (!job.trigger.expression) {\n errors.push({\n message: `Job '${job.name}' has empty cron expression`,\n kind: \"prohibited\",\n });\n } else {\n // Basic cron field count validation (5, 6, or 7 fields expected)\n const fields = job.trigger.expression.trim().split(/\\s+/);\n if (fields.length < 5 || fields.length > 7) {\n errors.push({\n message: `Job '${job.name}' has invalid cron expression \"${job.trigger.expression}\" — expected 5-7 fields, got ${fields.length}`,\n kind: \"prohibited\",\n });\n }\n }\n }\n }\n }\n\n // Event loop detection: on /path:event + save/tee to same path (event triggers only)\n for (const job of program.jobs) {\n if (job.trigger?.kind === \"event\") {\n const triggerPath = job.trigger.path;\n for (const stage of job.pipeline) {\n if ((stage.kind === \"save\" || stage.kind === \"tee\") && stage.path === triggerPath) {\n errors.push({\n message: `Job '${job.name}' writes to its own trigger path '${triggerPath}' — this creates an event loop`,\n kind: \"prohibited\",\n });\n }\n }\n }\n }\n\n // Self-referencing action: action /.actions/* → potential recursion\n // Uses provider-relative path (/.actions/) since the absolute mount point varies.\n // Dangerous: /.actions/run (arbitrary code), /.actions/exec (script exec).\n // Allowed: /.actions/agent-run (budget-constrained LLM loop, not arbitrary code).\n const SELF_EXEC_BLOCKED = new Set([\"/.actions/run\", \"/.actions/exec\"]);\n for (const job of program.jobs) {\n for (const stage of job.pipeline) {\n if (stage.kind === \"action\" && SELF_EXEC_BLOCKED.has(stage.path)) {\n errors.push({\n message: `Job '${job.name}' calls action '${stage.path}' — self-referencing ASH execution risks recursion and bypasses compile-time checks`,\n kind: \"prohibited\",\n });\n }\n }\n }\n\n // Proto-pollution fields — ERROR (these can modify object prototype chain)\n const PROTO_POLLUTION_FIELDS = new Set([\n \"constructor\", \"__proto__\", \"prototype\",\n \"__defineGetter__\", \"__defineSetter__\",\n ]);\n // JS reserved identifiers — WARNING (runtime risk if eval-like constructs used)\n const JS_RESERVED_FIELDS = new Set([\n ...PROTO_POLLUTION_FIELDS,\n \"toString\", \"valueOf\", \"hasOwnProperty\",\n \"class\", \"return\", \"delete\", \"void\", \"typeof\", \"new\",\n ]);\n for (const job of program.jobs) {\n for (const stage of job.pipeline) {\n const protoFields: string[] = []; // error-level\n const jsFields: string[] = []; // warning-level\n if (stage.kind === \"map\") {\n if (stage.field) classifyField(stage.field, PROTO_POLLUTION_FIELDS, JS_RESERVED_FIELDS, protoFields, jsFields);\n if (stage.expression) collectJsReservedFields(stage.expression, JS_RESERVED_FIELDS, jsFields);\n if (stage.exprMappings) {\n // Check both KEYS and VALUES of map expressions\n for (const key of Object.keys(stage.exprMappings)) {\n classifyField(key, PROTO_POLLUTION_FIELDS, JS_RESERVED_FIELDS, protoFields, jsFields);\n }\n for (const expr of Object.values(stage.exprMappings)) {\n collectJsReservedFields(expr, JS_RESERVED_FIELDS, jsFields);\n }\n }\n if (stage.mappings) {\n for (const key of Object.keys(stage.mappings)) {\n classifyField(key, PROTO_POLLUTION_FIELDS, JS_RESERVED_FIELDS, protoFields, jsFields);\n }\n }\n // Map duplicate key detection\n if (stage.exprMappings) {\n checkMapDuplicateKeys(stage.exprMappings, job.name, errors);\n }\n if (stage.mappings) {\n checkMapDuplicateKeys(stage.mappings, job.name, errors);\n }\n }\n if (stage.kind === \"where\" && JS_RESERVED_FIELDS.has(stage.left)) {\n classifyField(stage.left, PROTO_POLLUTION_FIELDS, JS_RESERVED_FIELDS, protoFields, jsFields);\n }\n // Proto-pollution fields → error (blocks compilation)\n for (const field of protoFields) {\n errors.push({\n message: `Job '${job.name}': field '${field}' is a prototype pollution vector — not allowed as map key or field name`,\n kind: \"prohibited\",\n });\n }\n // Other JS reserved fields → warning\n for (const field of jsFields) {\n errors.push({\n message: `Job '${job.name}': field '${field}' is a JavaScript reserved identifier — may expose runtime internals`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n }\n }\n\n // Division risk: map expressions with field / field (could produce NaN)\n for (const job of program.jobs) {\n for (const stage of job.pipeline) {\n if (stage.kind === \"map\") {\n if (stage.expression) {\n checkDivisionRisk(stage.expression, job.name, errors);\n }\n if (stage.exprMappings) {\n for (const expr of Object.values(stage.exprMappings)) {\n checkDivisionRisk(expr, job.name, errors);\n }\n }\n }\n }\n }\n\n return errors;\n}\n\nfunction checkDivisionRisk(expr: Expression, jobName: string, errors: CompileError[]): void {\n if (expr.kind === \"binary\" && expr.op === \"/\") {\n // If the divisor is a field reference (not a literal), it could be zero → NaN risk\n if (expr.right.kind === \"field_access\") {\n errors.push({\n message: `Job '${jobName}': division by field '${expr.right.path}' may produce NaN/Infinity if zero — consider adding a where clause to filter`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n }\n // Recurse into sub-expressions\n if (expr.kind === \"binary\") {\n checkDivisionRisk(expr.left, jobName, errors);\n checkDivisionRisk(expr.right, jobName, errors);\n }\n if (expr.kind === \"array_literal\") {\n for (const el of expr.elements) checkDivisionRisk(el, jobName, errors);\n }\n if (expr.kind === \"object_literal\") {\n for (const val of Object.values(expr.properties)) checkDivisionRisk(val, jobName, errors);\n }\n}\n\nfunction collectJsReservedFields(expr: Expression, reserved: Set<string>, out: string[]): void {\n if (expr.kind === \"field_access\" && reserved.has(expr.path)) out.push(expr.path);\n if (expr.kind === \"binary\") {\n collectJsReservedFields(expr.left, reserved, out);\n collectJsReservedFields(expr.right, reserved, out);\n }\n if (expr.kind === \"array_literal\") {\n for (const el of expr.elements) collectJsReservedFields(el, reserved, out);\n }\n if (expr.kind === \"object_literal\") {\n for (const val of Object.values(expr.properties)) collectJsReservedFields(val, reserved, out);\n }\n}\n\nfunction classifyField(\n field: string,\n proto: Set<string>,\n jsReserved: Set<string>,\n protoOut: string[],\n jsOut: string[],\n): void {\n if (proto.has(field)) {\n protoOut.push(field);\n } else if (jsReserved.has(field)) {\n jsOut.push(field);\n }\n}\n\nfunction checkMapDuplicateKeys(obj: Record<string, unknown>, jobName: string, errors: CompileError[]): void {\n // Object.create(null) maps already deduplicate at parser level (last-write-wins).\n // But we track keys during parsing — duplicates are caught at parse time.\n // This is a defense-in-depth check for any keys that slip through.\n const seen = new Set<string>();\n for (const key of Object.keys(obj)) {\n if (seen.has(key)) {\n errors.push({\n message: `Job '${jobName}': map has duplicate key '${key}' — last value wins silently, potential data confusion`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n seen.add(key);\n }\n}\n\nfunction checkFanoutDepth(stages: PipelineStage[], depth: number, errors: CompileError[]): void {\n for (const stage of stages) {\n if (stage.kind === \"fanout\") {\n if (stage.branches.length > 10) {\n errors.push({\n message: `Fanout has ${stage.branches.length} branches — more than 10 branches may cause excessive parallelism`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n if (depth + 1 > 3) {\n errors.push({\n message: `Fanout nesting depth exceeds 3 levels — deeply nested fanout may indicate over-complexity`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n return;\n }\n for (const branch of stage.branches) {\n checkFanoutDepth(branch, depth + 1, errors);\n }\n }\n }\n}\n\nfunction checkFanoutDuplicateWrites(job: JobDeclaration, errors: CompileError[]): void {\n for (const stage of job.pipeline) {\n if (stage.kind !== \"fanout\") continue;\n // Collect write paths from each branch\n const writePaths = new Map<string, number>(); // path → branch count\n for (const branch of stage.branches) {\n for (const s of branch) {\n if ((s.kind === \"save\" || s.kind === \"tee\") && \"path\" in s) {\n const path = (s as any).path as string;\n writePaths.set(path, (writePaths.get(path) ?? 0) + 1);\n }\n }\n }\n for (const [path, count] of writePaths) {\n if (count > 1) {\n errors.push({\n message: `Job '${job.name}': fanout has ${count} branches writing to '${path}' — last write wins silently, audit trail lost`,\n kind: \"prohibited\",\n severity: \"warning\",\n });\n }\n }\n }\n}\n\nfunction checkExpressionDivZero(stage: PipelineStage, errors: CompileError[]): void {\n if (stage.kind !== \"map\") return;\n if (stage.exprMappings) {\n for (const expr of Object.values(stage.exprMappings)) {\n if (hasLiteralDivByZero(expr)) {\n errors.push({ message: \"Expression contains literal division by zero\", kind: \"prohibited\", severity: \"warning\" });\n }\n }\n }\n if (stage.expression && hasLiteralDivByZero(stage.expression)) {\n errors.push({ message: \"Expression contains literal division by zero\", kind: \"prohibited\", severity: \"warning\" });\n }\n}\n\nfunction hasLiteralDivByZero(expr: Expression): boolean {\n if (expr.kind === \"binary\" && expr.op === \"/\" && expr.right.kind === \"literal\" && expr.right.value === 0) {\n return true;\n }\n if (expr.kind === \"binary\") {\n return hasLiteralDivByZero(expr.left) || hasLiteralDivByZero(expr.right);\n }\n if (expr.kind === \"array_literal\") {\n return expr.elements.some(hasLiteralDivByZero);\n }\n if (expr.kind === \"object_literal\") {\n return Object.values(expr.properties).some(hasLiteralDivByZero);\n }\n return false;\n}\n\nfunction checkRouteDag(program: Program, errors: CompileError[]): void {\n // Build adjacency: job -> [target jobs via route]\n const graph = new Map<string, Set<string>>();\n for (const job of program.jobs) {\n const targets = new Set<string>();\n for (const stage of job.pipeline) {\n if (stage.kind === \"route\") {\n for (const branch of stage.branches) targets.add(branch.targetJob);\n if (stage.fallback) targets.add(stage.fallback);\n }\n }\n if (targets.size > 0) graph.set(job.name, targets);\n }\n\n // DFS cycle detection\n const visited = new Set<string>();\n const inStack = new Set<string>();\n\n function dfs(node: string): boolean {\n if (inStack.has(node)) return true; // cycle\n if (visited.has(node)) return false;\n visited.add(node);\n inStack.add(node);\n const neighbors = graph.get(node);\n if (neighbors) {\n for (const n of neighbors) {\n if (dfs(n)) {\n errors.push({\n message: `Route cycle detected: job '${node}' → job '${n}' forms a cycle`,\n kind: \"prohibited\",\n });\n return true;\n }\n }\n }\n inStack.delete(node);\n return false;\n }\n\n for (const node of graph.keys()) {\n dfs(node);\n }\n}\n\nconst ANNOTATION_VALIDATORS: Record<string, (args: string[]) => string | null> = {\n approval: (args) => {\n if (args.length === 0) return null;\n const valid = new Set([\"human\", \"auto\"]);\n if (!valid.has(args[0])) return `@approval accepts 'human' or 'auto', got '${args[0]}'`;\n return null;\n },\n readonly: () => null,\n retry: (args) => {\n if (args.length === 0) return null;\n const n = Number(args[0]);\n if (isNaN(n) || !Number.isInteger(n) || n < 0) return `@retry requires a non-negative integer, got '${args[0]}'`;\n return null;\n },\n timeout: (args) => {\n if (args.length === 0) return null;\n const n = Number(args[0]);\n if (isNaN(n) || n <= 0) return `@timeout requires a positive number, got '${args[0]}'`;\n if (n > 3600000) return `@timeout(${args[0]}) exceeds hard limit of 3600000ms (1 hour) — reduce timeout`;\n return null;\n },\n on_error: (args) => {\n if (args.length === 0) return `@on_error requires a strategy: skip, fail, or save <path>`;\n const valid = new Set([\"skip\", \"fail\", \"save\"]);\n if (!valid.has(args[0])) return `@on_error accepts 'skip', 'fail', or 'save', got '${args[0]}'`;\n return null;\n },\n budget: (args) => {\n if (args.length === 0) return `@budget requires at least one limit: @budget(actions 50, records 10000)`;\n if (args.length % 2 !== 0) return `@budget requires dimension/value pairs — got odd number of args`;\n const validDims = new Set([\"actions\", \"writes\", \"records\", \"tokens\", \"cost\"]);\n for (let i = 0; i < args.length; i += 2) {\n if (!validDims.has(args[i])) return `@budget dimension must be one of: ${[...validDims].join(\", \")} — got '${args[i]}'`;\n const n = Number(args[i + 1]);\n if (isNaN(n) || n <= 0) return `@budget value must be a positive number, got '${args[i + 1]}'`;\n }\n return null;\n },\n caps: (args) => {\n if (args.length === 0) return `@caps requires at least one capability: @caps(read /path/*, write /out/*)`;\n if (args.length % 2 !== 0) return `@caps requires operation/path pairs — got odd number of args`;\n const validOps = new Set([\"read\", \"write\", \"exec\"]);\n for (let i = 0; i < args.length; i += 2) {\n if (!validOps.has(args[i])) return `@caps operation must be 'read', 'write', or 'exec', got '${args[i]}'`;\n if (!args[i + 1].startsWith(\"/\")) return `@caps path must start with '/', got '${args[i + 1]}'`;\n }\n return null;\n },\n};\n\nexport const BUILTIN_COMMAND_NAMES = Object.keys(BUILTIN_MANIFESTS);\n\nexport function typeErrorsToDiagnostics(errors: TypeError[]): AshDiagnostic[] {\n return errors.map(e => ({\n code: \"ASH_TYPE_MISMATCH\" as DiagnosticCode,\n message: e.message,\n stage: e.stage,\n }));\n}\n\nexport function compileErrorsToDiagnostics(errors: CompileError[]): AshDiagnostic[] {\n return errors.map(e => ({\n code: (e.kind === \"unknown_command\" ? \"ASH_UNKNOWN_COMMAND\" : \"ASH_ANNOTATION_INVALID\") as DiagnosticCode,\n severity: e.severity,\n message: e.message,\n suggestion: e.kind === \"unknown_command\" ? BUILTIN_COMMAND_NAMES : undefined,\n }));\n}\n\nexport function annotationErrorsToDiagnostics(errors: CompileError[]): AshDiagnostic[] {\n return errors.map(e => ({\n code: \"ASH_ANNOTATION_INVALID\" as DiagnosticCode,\n severity: e.severity,\n message: e.message,\n }));\n}\n\nexport function parseSyntaxError(errorMessage: string): AshDiagnostic {\n const isUnterminated = /unterminated/i.test(errorMessage);\n const isDuplicate = /duplicate variable/i.test(errorMessage);\n const lineMatch = errorMessage.match(/line\\s+(\\d+)/i);\n const colMatch = errorMessage.match(/column\\s+(\\d+)/i);\n // Strip \"Error:\" prefix if present\n const cleanMsg = errorMessage.replace(/^Error:\\s*/i, \"\");\n return {\n code: isDuplicate ? \"ASH_DUPLICATE_VAR\" : isUnterminated ? \"ASH_SYNTAX_UNTERMINATED\" : \"ASH_SYNTAX_UNEXPECTED\",\n message: cleanMsg,\n line: lineMatch ? Number(lineMatch[1]) : undefined,\n column: colMatch ? Number(colMatch[1]) : undefined,\n };\n}\n\nexport function checkAnnotations(job: JobDeclaration): CompileError[] {\n const errors: CompileError[] = [];\n\n for (const ann of job.annotations) {\n const validator = Object.hasOwn(ANNOTATION_VALIDATORS, ann.name) ? ANNOTATION_VALIDATORS[ann.name] : undefined;\n if (!validator) {\n errors.push({\n message: `Unknown annotation '@${ann.name}' — not in allowed set`,\n kind: \"prohibited\",\n });\n continue;\n }\n const err = validator(ann.args);\n if (err) {\n errors.push({ message: err, kind: \"prohibited\" });\n }\n }\n\n // 2. Duplicate annotation detection — last-wins is an audit deception vector\n const annCounts = new Map<string, number>();\n for (const ann of job.annotations) {\n annCounts.set(ann.name, (annCounts.get(ann.name) ?? 0) + 1);\n }\n for (const [name, count] of annCounts) {\n if (count > 1) {\n errors.push({\n message: `Job '${job.name}': @${name} appears ${count} times — last value wins silently, potential audit deception`,\n kind: \"prohibited\",\n });\n }\n }\n\n // 3. @retry hard limit (error, not warning)\n const retryAnns = job.annotations.filter(a => a.name === \"retry\");\n for (const ann of retryAnns) {\n if (ann.args.length > 0) {\n const n = Number(ann.args[0]);\n if (!isNaN(n) && n > 100) {\n errors.push({ message: `@retry(${n}) exceeds hard limit of 100 — reduce retry count`, kind: \"prohibited\" });\n }\n }\n }\n\n // 4. @approval conflict: human and auto on same job\n const approvalAnns = job.annotations.filter(a => a.name === \"approval\");\n if (approvalAnns.length > 1) {\n const values = approvalAnns.map(a => a.args[0]).filter(Boolean);\n if (values.includes(\"human\") && values.includes(\"auto\")) {\n errors.push({ message: `@approval conflict: 'human' and 'auto' cannot both be set on job '${job.name}'`, kind: \"prohibited\" });\n }\n }\n\n return errors;\n}\n\n// ── @caps: capability-based path security ──\n\nexport interface CapEntry {\n op: \"read\" | \"write\" | \"exec\";\n pathGlob: string;\n}\n\nexport function parseCaps(ann: { args: string[] }): CapEntry[] {\n const caps: CapEntry[] = [];\n for (let i = 0; i < ann.args.length; i += 2) {\n caps.push({ op: ann.args[i] as CapEntry[\"op\"], pathGlob: ann.args[i + 1] });\n }\n return caps;\n}\n\nexport function capMatches(pathGlob: string, actual: string): boolean {\n if (pathGlob === actual) return true;\n if (pathGlob.endsWith(\"/*\")) {\n const prefix = pathGlob.slice(0, -1); // \"/ha/sensors/\" (keep trailing /)\n return actual.startsWith(prefix) || actual === pathGlob.slice(0, -2); // exact parent match\n }\n if (pathGlob.endsWith(\"*\")) {\n return actual.startsWith(pathGlob.slice(0, -1));\n }\n return false;\n}\n\nexport function hasCapFor(caps: CapEntry[], op: CapEntry[\"op\"], path: string): boolean {\n return caps.some(c => c.op === op && capMatches(c.pathGlob, path));\n}\n\nconst STAGE_OP_MAP: Record<string, CapEntry[\"op\"]> = {\n find: \"read\",\n lookup: \"read\",\n save: \"write\",\n publish: \"write\",\n tee: \"write\",\n action: \"exec\",\n};\n\nfunction checkStagesCaps(stages: PipelineStage[], caps: CapEntry[], diagnostics: AshDiagnostic[]): void {\n for (const stage of stages) {\n // Skip static caps check for relative actions (path resolved at runtime, caps checked per-exec)\n if (stage.kind === \"action\" && stage.relative) continue;\n // Skip static caps check for template paths (resolved at runtime, caps checked post-resolution)\n if (\"path\" in stage && typeof (stage as any).path === \"string\" && (stage as any).path.includes(\"${\")) continue;\n\n const op = Object.hasOwn(STAGE_OP_MAP, stage.kind) ? STAGE_OP_MAP[stage.kind] : undefined;\n if (op && \"path\" in stage && typeof (stage as any).path === \"string\") {\n const path = (stage as any).path as string;\n if (!hasCapFor(caps, op, path)) {\n diagnostics.push({\n code: \"ASH_CAP_DENIED\",\n message: `@caps denied: '${stage.kind} ${path}' requires '${op}' capability for '${path}'`,\n });\n }\n }\n if (stage.kind === \"fanout\") {\n for (const branch of stage.branches) {\n checkStagesCaps(branch, caps, diagnostics);\n }\n }\n }\n}\n\nexport function checkJobCaps(job: JobDeclaration): AshDiagnostic[] {\n const capsAnns = job.annotations.filter(a => a.name === \"caps\");\n if (capsAnns.length === 0) return [];\n\n const diagnostics: AshDiagnostic[] = [];\n const allCaps: CapEntry[] = [];\n for (const ann of capsAnns) {\n if (ann.args.length > 0 && ann.args.length % 2 === 0) {\n allCaps.push(...parseCaps(ann));\n }\n }\n\n if (allCaps.length === 0) return diagnostics;\n\n checkStagesCaps(job.pipeline, allCaps, diagnostics);\n return diagnostics;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
package/DESIGN.md
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# ASH — Design
|
|
2
|
-
|
|
3
|
-
## What ASH Is
|
|
4
|
-
|
|
5
|
-
ASH (Agentic Shell) is a **pipeline intermediate representation** designed for
|
|
6
|
-
agents and agentic workflows. It compiles declarative multi-stage pipelines
|
|
7
|
-
into executable jobs that run against any world interface (AFS, AOS namespace,
|
|
8
|
-
or custom implementations).
|
|
9
|
-
|
|
10
|
-
```
|
|
11
|
-
find /world/users | where active == true | map { name, role } | save /world/active
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## What ASH Is Not
|
|
15
|
-
|
|
16
|
-
ASH is **not** an interactive shell for humans. It does not handle:
|
|
17
|
-
|
|
18
|
-
- Prompts or REPL
|
|
19
|
-
- Command history
|
|
20
|
-
- Current working directory (cwd)
|
|
21
|
-
- Session management
|
|
22
|
-
- TUI / interactive UI
|
|
23
|
-
- Tab completion
|
|
24
|
-
|
|
25
|
-
## Consumers
|
|
26
|
-
|
|
27
|
-
| Consumer | Interface |
|
|
28
|
-
|----------|-----------|
|
|
29
|
-
| AFS exec | `afs exec script.ash` — runs against AFS world |
|
|
30
|
-
| AOS runtime | Loads `.ash` as program entrypoint |
|
|
31
|
-
| Agent runtime | Calls `compileSource()` / `CompiledProgram.run()` directly |
|
|
32
|
-
| Console | Detects `|` in input → sends full line to ASH compiler |
|
|
33
|
-
|
|
34
|
-
## Relationship to AFS
|
|
35
|
-
|
|
36
|
-
```
|
|
37
|
-
ASH = Pipeline DSL (Agent / Advanced User)
|
|
38
|
-
AFS = World (read/write/publish interface)
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
ASH operates on any `WorldInterface` — AFS is the primary but not sole target.
|