@aigne/ash 0.0.1

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.
Files changed (146) hide show
  1. package/DESIGN.md +41 -0
  2. package/dist/ai-dev-loop/ash-run-result.cjs +12 -0
  3. package/dist/ai-dev-loop/ash-run-result.d.cts +28 -0
  4. package/dist/ai-dev-loop/ash-run-result.d.cts.map +1 -0
  5. package/dist/ai-dev-loop/ash-run-result.d.mts +28 -0
  6. package/dist/ai-dev-loop/ash-run-result.d.mts.map +1 -0
  7. package/dist/ai-dev-loop/ash-run-result.mjs +11 -0
  8. package/dist/ai-dev-loop/ash-run-result.mjs.map +1 -0
  9. package/dist/ai-dev-loop/ash-typed-error.cjs +51 -0
  10. package/dist/ai-dev-loop/ash-typed-error.d.cts +54 -0
  11. package/dist/ai-dev-loop/ash-typed-error.d.cts.map +1 -0
  12. package/dist/ai-dev-loop/ash-typed-error.d.mts +54 -0
  13. package/dist/ai-dev-loop/ash-typed-error.d.mts.map +1 -0
  14. package/dist/ai-dev-loop/ash-typed-error.mjs +50 -0
  15. package/dist/ai-dev-loop/ash-typed-error.mjs.map +1 -0
  16. package/dist/ai-dev-loop/ash-validate.cjs +27 -0
  17. package/dist/ai-dev-loop/ash-validate.d.cts +7 -0
  18. package/dist/ai-dev-loop/ash-validate.d.cts.map +1 -0
  19. package/dist/ai-dev-loop/ash-validate.d.mts +7 -0
  20. package/dist/ai-dev-loop/ash-validate.d.mts.map +1 -0
  21. package/dist/ai-dev-loop/ash-validate.mjs +28 -0
  22. package/dist/ai-dev-loop/ash-validate.mjs.map +1 -0
  23. package/dist/ai-dev-loop/dev-loop.cjs +134 -0
  24. package/dist/ai-dev-loop/dev-loop.d.cts +28 -0
  25. package/dist/ai-dev-loop/dev-loop.d.cts.map +1 -0
  26. package/dist/ai-dev-loop/dev-loop.d.mts +28 -0
  27. package/dist/ai-dev-loop/dev-loop.d.mts.map +1 -0
  28. package/dist/ai-dev-loop/dev-loop.mjs +135 -0
  29. package/dist/ai-dev-loop/dev-loop.mjs.map +1 -0
  30. package/dist/ai-dev-loop/index.cjs +24 -0
  31. package/dist/ai-dev-loop/index.d.cts +9 -0
  32. package/dist/ai-dev-loop/index.d.mts +9 -0
  33. package/dist/ai-dev-loop/index.mjs +10 -0
  34. package/dist/ai-dev-loop/live-mode.cjs +17 -0
  35. package/dist/ai-dev-loop/live-mode.d.cts +24 -0
  36. package/dist/ai-dev-loop/live-mode.d.cts.map +1 -0
  37. package/dist/ai-dev-loop/live-mode.d.mts +24 -0
  38. package/dist/ai-dev-loop/live-mode.d.mts.map +1 -0
  39. package/dist/ai-dev-loop/live-mode.mjs +17 -0
  40. package/dist/ai-dev-loop/live-mode.mjs.map +1 -0
  41. package/dist/ai-dev-loop/meta-tools.cjs +123 -0
  42. package/dist/ai-dev-loop/meta-tools.d.cts +24 -0
  43. package/dist/ai-dev-loop/meta-tools.d.cts.map +1 -0
  44. package/dist/ai-dev-loop/meta-tools.d.mts +24 -0
  45. package/dist/ai-dev-loop/meta-tools.d.mts.map +1 -0
  46. package/dist/ai-dev-loop/meta-tools.mjs +120 -0
  47. package/dist/ai-dev-loop/meta-tools.mjs.map +1 -0
  48. package/dist/ai-dev-loop/structured-runner.cjs +154 -0
  49. package/dist/ai-dev-loop/structured-runner.d.cts +12 -0
  50. package/dist/ai-dev-loop/structured-runner.d.cts.map +1 -0
  51. package/dist/ai-dev-loop/structured-runner.d.mts +12 -0
  52. package/dist/ai-dev-loop/structured-runner.d.mts.map +1 -0
  53. package/dist/ai-dev-loop/structured-runner.mjs +155 -0
  54. package/dist/ai-dev-loop/structured-runner.mjs.map +1 -0
  55. package/dist/ai-dev-loop/system-prompt.cjs +55 -0
  56. package/dist/ai-dev-loop/system-prompt.d.cts +20 -0
  57. package/dist/ai-dev-loop/system-prompt.d.cts.map +1 -0
  58. package/dist/ai-dev-loop/system-prompt.d.mts +20 -0
  59. package/dist/ai-dev-loop/system-prompt.d.mts.map +1 -0
  60. package/dist/ai-dev-loop/system-prompt.mjs +54 -0
  61. package/dist/ai-dev-loop/system-prompt.mjs.map +1 -0
  62. package/dist/ast.d.cts +140 -0
  63. package/dist/ast.d.cts.map +1 -0
  64. package/dist/ast.d.mts +140 -0
  65. package/dist/ast.d.mts.map +1 -0
  66. package/dist/compiler.cjs +802 -0
  67. package/dist/compiler.d.cts +103 -0
  68. package/dist/compiler.d.cts.map +1 -0
  69. package/dist/compiler.d.mts +103 -0
  70. package/dist/compiler.d.mts.map +1 -0
  71. package/dist/compiler.mjs +802 -0
  72. package/dist/compiler.mjs.map +1 -0
  73. package/dist/index.cjs +14 -0
  74. package/dist/index.d.cts +7 -0
  75. package/dist/index.d.mts +7 -0
  76. package/dist/index.mjs +7 -0
  77. package/dist/lexer.cjs +451 -0
  78. package/dist/lexer.d.cts +14 -0
  79. package/dist/lexer.d.cts.map +1 -0
  80. package/dist/lexer.d.mts +14 -0
  81. package/dist/lexer.d.mts.map +1 -0
  82. package/dist/lexer.mjs +451 -0
  83. package/dist/lexer.mjs.map +1 -0
  84. package/dist/parser.cjs +734 -0
  85. package/dist/parser.d.cts +40 -0
  86. package/dist/parser.d.cts.map +1 -0
  87. package/dist/parser.d.mts +40 -0
  88. package/dist/parser.d.mts.map +1 -0
  89. package/dist/parser.mjs +734 -0
  90. package/dist/parser.mjs.map +1 -0
  91. package/dist/reference.cjs +130 -0
  92. package/dist/reference.d.cts +11 -0
  93. package/dist/reference.d.cts.map +1 -0
  94. package/dist/reference.d.mts +11 -0
  95. package/dist/reference.d.mts.map +1 -0
  96. package/dist/reference.mjs +130 -0
  97. package/dist/reference.mjs.map +1 -0
  98. package/dist/template.cjs +85 -0
  99. package/dist/template.mjs +84 -0
  100. package/dist/template.mjs.map +1 -0
  101. package/dist/type-checker.cjs +582 -0
  102. package/dist/type-checker.d.cts +31 -0
  103. package/dist/type-checker.d.cts.map +1 -0
  104. package/dist/type-checker.d.mts +31 -0
  105. package/dist/type-checker.d.mts.map +1 -0
  106. package/dist/type-checker.mjs +573 -0
  107. package/dist/type-checker.mjs.map +1 -0
  108. package/package.json +29 -0
  109. package/src/ai-dev-loop/ash-run-result.test.ts +113 -0
  110. package/src/ai-dev-loop/ash-run-result.ts +46 -0
  111. package/src/ai-dev-loop/ash-typed-error.test.ts +136 -0
  112. package/src/ai-dev-loop/ash-typed-error.ts +50 -0
  113. package/src/ai-dev-loop/ash-validate.test.ts +54 -0
  114. package/src/ai-dev-loop/ash-validate.ts +34 -0
  115. package/src/ai-dev-loop/dev-loop.test.ts +364 -0
  116. package/src/ai-dev-loop/dev-loop.ts +156 -0
  117. package/src/ai-dev-loop/dry-run.test.ts +107 -0
  118. package/src/ai-dev-loop/e2e-multi-fix.test.ts +473 -0
  119. package/src/ai-dev-loop/e2e.test.ts +324 -0
  120. package/src/ai-dev-loop/index.ts +15 -0
  121. package/src/ai-dev-loop/invariants.test.ts +253 -0
  122. package/src/ai-dev-loop/live-mode.test.ts +63 -0
  123. package/src/ai-dev-loop/live-mode.ts +33 -0
  124. package/src/ai-dev-loop/meta-tools.test.ts +120 -0
  125. package/src/ai-dev-loop/meta-tools.ts +142 -0
  126. package/src/ai-dev-loop/structured-runner.test.ts +159 -0
  127. package/src/ai-dev-loop/structured-runner.ts +209 -0
  128. package/src/ai-dev-loop/system-prompt.test.ts +102 -0
  129. package/src/ai-dev-loop/system-prompt.ts +81 -0
  130. package/src/ast.ts +186 -0
  131. package/src/compiler.test.ts +2933 -0
  132. package/src/compiler.ts +1103 -0
  133. package/src/e2e.test.ts +552 -0
  134. package/src/index.ts +16 -0
  135. package/src/lexer.test.ts +538 -0
  136. package/src/lexer.ts +222 -0
  137. package/src/parser.test.ts +1024 -0
  138. package/src/parser.ts +835 -0
  139. package/src/reference.test.ts +166 -0
  140. package/src/reference.ts +125 -0
  141. package/src/template.test.ts +210 -0
  142. package/src/template.ts +139 -0
  143. package/src/type-checker.test.ts +1494 -0
  144. package/src/type-checker.ts +785 -0
  145. package/tsconfig.json +9 -0
  146. package/tsdown.config.ts +12 -0
@@ -0,0 +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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"}
@@ -0,0 +1,130 @@
1
+
2
+ //#region src/reference.ts
3
+ /**
4
+ * ASH language reference — designed for LLM consumption.
5
+ *
6
+ * Exported as a string constant so any consumer (AFS provider, AI dev loop,
7
+ * CLI help) can use the same single source of truth.
8
+ */
9
+ const ASH_REFERENCE = `# ASH Language Reference
10
+
11
+ ASH is a deterministic pipeline DSL. No imports, no eval, no control flow — compiler-safe by design.
12
+
13
+ ## Structure
14
+
15
+ \`\`\`ash
16
+ # Comments start with #
17
+ param threshold = 70
18
+
19
+ let total = find /data/users | count
20
+
21
+ output "Starting ETL"
22
+
23
+ @caps(read /data/* write /data/*)
24
+ job extract {
25
+ find /data/users where active == true
26
+ | where score > $threshold
27
+ | map { fullName: name + " " + surname, score: score * 2 + $bonus }
28
+ | tee /data/backup
29
+ | save /data/clean
30
+ }
31
+ \`\`\`
32
+
33
+ **Top-level statements**: \`param\`, \`let\`, \`output\`, \`job\`
34
+ **Pipes**: stages separated by \`|\`
35
+
36
+ ## Commands
37
+
38
+ | Command | Input | Output | Description |
39
+ |---------|-------|--------|-------------|
40
+ | \`find /path\` | none | object_stream | Read records from path. Each record contains \`path\`, \`name\`, \`kind\` + provider-specific content fields (e.g. \`battery_level\`, \`state\`, \`label\`). |
41
+ | \`find /path where f == v\` | none | object_stream | Read with inline filter (query pushdown). Same output schema as \`find\`. |
42
+ | \`where field op value\` | object_stream | object_stream | Filter records (\`==\` \`!=\` \`>\` \`<\` \`>=\` \`<=\`) |
43
+ | \`map field\` | object_stream | object_stream | Extract single field |
44
+ | \`map { out1 in1, out2 in2 }\` | object_stream | object_stream | Object transform (rename/reshape) |
45
+ | \`map { key: expr }\` | object_stream | object_stream | Expression transform (\`score: score * 2\`) |
46
+ | \`save /path\` | object_stream | none | Write records to path (terminal) |
47
+ | \`publish /topic\` | object_stream | none | Publish records to topic (terminal) |
48
+ | \`tee /path\` | object_stream | object_stream | Side-write copy, stream continues |
49
+ | \`count\` | object_stream | object_stream | Produce \`{ count: N }\` → schema: \`{ count: number }\` |
50
+ | \`group-by field\` | object_stream | object_stream | Produce \`[{ key, items }]\` → schema: \`{ key: T, items: T[] }\` |
51
+ | \`output "msg"\` | object_stream | object_stream | Emit text message, pass-through |
52
+ | \`output expr\` | object_stream | object_stream | Evaluate expression per record, emit as text, pass-through |
53
+ | \`input "prompt"\` | none | object_stream | Prompt for input → schema: \`{ prompt: string, response: string }\` |
54
+ | \`fanout { branch1, branch2 }\` | object_stream | object_stream | Split stream to parallel branches |
55
+ | \`action /path\` | object_stream | object_stream | Execute external action via \`world.exec\` |
56
+ | \`route field { "v1" -> job j1, ... }\` | object_stream | none | Dispatch items by field value to named jobs |
57
+ | \`lookup /path on key\` | object_stream | object_stream | Left-join with data at path on key field |
58
+
59
+ **Type rule**: each stage's output type must match the next stage's input type. \`save\`, \`publish\`, and \`route\` output \`none\` — nothing can follow them in a pipeline.
60
+
61
+ ## Params & Variables
62
+
63
+ \`\`\`ash
64
+ param min_score = 70
65
+ let bonus = 10
66
+ let total = find /data/users | count
67
+ job q { find /data/users | where score > $min_score | map { adjusted: score * 2 + $bonus } | save /data/out }
68
+ \`\`\`
69
+
70
+ \`param\` declares a named default that callers can override at execution time.
71
+ \`let\` binds a name to a literal or a pipeline result. Reference with \`$name\` in where clauses and expressions.
72
+
73
+ ## Expressions
74
+
75
+ Arithmetic: \`score * 2 + $bonus\`, \`price - discount\`
76
+ String concat: \`name + " " + surname\`
77
+ Usable in \`map { key: expr }\` and runtime \`let\`.
78
+
79
+ ## Triggers
80
+
81
+ \`\`\`ash
82
+ job ingest on /data/raw:created { find /data/raw | save /data/clean }
83
+ job ticker on cron("*/5 * * * *") { find /metrics | save /snapshots }
84
+ \`\`\`
85
+
86
+ \`on /path:event\` — runs when the named event fires on the given path.
87
+ \`on cron("expr")\` — runs on a cron schedule. Standard 5-field cron expression (minute hour day-of-month month day-of-week).
88
+
89
+ ## Annotations
90
+
91
+ Annotations decorate jobs with metadata. Place them on lines before \`job\`:
92
+
93
+ | Annotation | Description |
94
+ |------------|-------------|
95
+ | \`@approval(human)\` | Require human approval before execution |
96
+ | \`@approval(auto)\` | Auto-approved |
97
+ | \`@retry(N)\` | Retry up to N times on failure |
98
+ | \`@timeout(ms)\` | Timeout in milliseconds |
99
+ | \`@readonly\` | Job must not contain \`save\` or \`publish\` |
100
+ | \`@on_error(strategy)\` | Error handling: \`skip\` (continue), \`save /path\` (write errors), \`fail\` (default) |
101
+ | \`@caps(op /path, ...)\` | Capability declaration: restricts which paths the job can access |
102
+ | \`@budget(dim limit, ...)\` | Runtime resource limits per job execution |
103
+
104
+ \`\`\`ash
105
+ @caps(read /data/*, write /out/*, exec /api/enrich)
106
+ @on_error(skip)
107
+ @retry(3)
108
+ job resilient { find /data/raw | action /api/enrich | save /out/enriched }
109
+ \`\`\`
110
+
111
+ \`@caps\` operations: \`read\` (find, lookup), \`write\` (save, publish, tee), \`exec\` (action).
112
+ Paths support \`*\` wildcard: \`/data/*\` matches \`/data/users\`, \`/data/orders/active\`.
113
+ Compiler statically verifies all stage paths are within declared caps.
114
+
115
+ \`@budget\` dimensions: \`actions\` (exec calls), \`writes\` (save/publish/tee), \`records\` (stream items), \`tokens\` (LLM consumption), \`cost\` (dollar cost).
116
+ Runtime throws when any dimension exceeds its limit. Combine with \`@on_error(skip)\` for soft enforcement.
117
+
118
+ \`\`\`ash
119
+ @budget(actions 50, records 10000)
120
+ @caps(read /data/*, write /out/*, exec /api/enrich)
121
+ job bounded { find /data/raw | action /api/enrich | save /out/enriched }
122
+ \`\`\`
123
+
124
+ ## Paths
125
+
126
+ All paths start with \`/\`. Convention: \`/domain/collection\` (e.g. \`/world/users\`, \`/data/output\`).
127
+ `;
128
+
129
+ //#endregion
130
+ exports.ASH_REFERENCE = ASH_REFERENCE;
@@ -0,0 +1,11 @@
1
+ //#region src/reference.d.ts
2
+ /**
3
+ * ASH language reference — designed for LLM consumption.
4
+ *
5
+ * Exported as a string constant so any consumer (AFS provider, AI dev loop,
6
+ * CLI help) can use the same single source of truth.
7
+ */
8
+ declare const ASH_REFERENCE = "# ASH Language Reference\n\nASH is a deterministic pipeline DSL. No imports, no eval, no control flow \u2014 compiler-safe by design.\n\n## Structure\n\n```ash\n# Comments start with #\nparam threshold = 70\n\nlet total = find /data/users | count\n\noutput \"Starting ETL\"\n\n@caps(read /data/* write /data/*)\njob extract {\n find /data/users where active == true\n | where score > $threshold\n | map { fullName: name + \" \" + surname, score: score * 2 + $bonus }\n | tee /data/backup\n | save /data/clean\n}\n```\n\n**Top-level statements**: `param`, `let`, `output`, `job`\n**Pipes**: stages separated by `|`\n\n## Commands\n\n| Command | Input | Output | Description |\n|---------|-------|--------|-------------|\n| `find /path` | none | object_stream | Read records from path. Each record contains `path`, `name`, `kind` + provider-specific content fields (e.g. `battery_level`, `state`, `label`). |\n| `find /path where f == v` | none | object_stream | Read with inline filter (query pushdown). Same output schema as `find`. |\n| `where field op value` | object_stream | object_stream | Filter records (`==` `!=` `>` `<` `>=` `<=`) |\n| `map field` | object_stream | object_stream | Extract single field |\n| `map { out1 in1, out2 in2 }` | object_stream | object_stream | Object transform (rename/reshape) |\n| `map { key: expr }` | object_stream | object_stream | Expression transform (`score: score * 2`) |\n| `save /path` | object_stream | none | Write records to path (terminal) |\n| `publish /topic` | object_stream | none | Publish records to topic (terminal) |\n| `tee /path` | object_stream | object_stream | Side-write copy, stream continues |\n| `count` | object_stream | object_stream | Produce `{ count: N }` \u2192 schema: `{ count: number }` |\n| `group-by field` | object_stream | object_stream | Produce `[{ key, items }]` \u2192 schema: `{ key: T, items: T[] }` |\n| `output \"msg\"` | object_stream | object_stream | Emit text message, pass-through |\n| `output expr` | object_stream | object_stream | Evaluate expression per record, emit as text, pass-through |\n| `input \"prompt\"` | none | object_stream | Prompt for input \u2192 schema: `{ prompt: string, response: string }` |\n| `fanout { branch1, branch2 }` | object_stream | object_stream | Split stream to parallel branches |\n| `action /path` | object_stream | object_stream | Execute external action via `world.exec` |\n| `route field { \"v1\" -> job j1, ... }` | object_stream | none | Dispatch items by field value to named jobs |\n| `lookup /path on key` | object_stream | object_stream | Left-join with data at path on key field |\n\n**Type rule**: each stage's output type must match the next stage's input type. `save`, `publish`, and `route` output `none` \u2014 nothing can follow them in a pipeline.\n\n## Params & Variables\n\n```ash\nparam min_score = 70\nlet bonus = 10\nlet total = find /data/users | count\njob q { find /data/users | where score > $min_score | map { adjusted: score * 2 + $bonus } | save /data/out }\n```\n\n`param` declares a named default that callers can override at execution time.\n`let` binds a name to a literal or a pipeline result. Reference with `$name` in where clauses and expressions.\n\n## Expressions\n\nArithmetic: `score * 2 + $bonus`, `price - discount`\nString concat: `name + \" \" + surname`\nUsable in `map { key: expr }` and runtime `let`.\n\n## Triggers\n\n```ash\njob ingest on /data/raw:created { find /data/raw | save /data/clean }\njob ticker on cron(\"*/5 * * * *\") { find /metrics | save /snapshots }\n```\n\n`on /path:event` \u2014 runs when the named event fires on the given path.\n`on cron(\"expr\")` \u2014 runs on a cron schedule. Standard 5-field cron expression (minute hour day-of-month month day-of-week).\n\n## Annotations\n\nAnnotations decorate jobs with metadata. Place them on lines before `job`:\n\n| Annotation | Description |\n|------------|-------------|\n| `@approval(human)` | Require human approval before execution |\n| `@approval(auto)` | Auto-approved |\n| `@retry(N)` | Retry up to N times on failure |\n| `@timeout(ms)` | Timeout in milliseconds |\n| `@readonly` | Job must not contain `save` or `publish` |\n| `@on_error(strategy)` | Error handling: `skip` (continue), `save /path` (write errors), `fail` (default) |\n| `@caps(op /path, ...)` | Capability declaration: restricts which paths the job can access |\n| `@budget(dim limit, ...)` | Runtime resource limits per job execution |\n\n```ash\n@caps(read /data/*, write /out/*, exec /api/enrich)\n@on_error(skip)\n@retry(3)\njob resilient { find /data/raw | action /api/enrich | save /out/enriched }\n```\n\n`@caps` operations: `read` (find, lookup), `write` (save, publish, tee), `exec` (action).\nPaths support `*` wildcard: `/data/*` matches `/data/users`, `/data/orders/active`.\nCompiler statically verifies all stage paths are within declared caps.\n\n`@budget` dimensions: `actions` (exec calls), `writes` (save/publish/tee), `records` (stream items), `tokens` (LLM consumption), `cost` (dollar cost).\nRuntime throws when any dimension exceeds its limit. Combine with `@on_error(skip)` for soft enforcement.\n\n```ash\n@budget(actions 50, records 10000)\n@caps(read /data/*, write /out/*, exec /api/enrich)\njob bounded { find /data/raw | action /api/enrich | save /out/enriched }\n```\n\n## Paths\n\nAll paths start with `/`. Convention: `/domain/collection` (e.g. `/world/users`, `/data/output`).\n";
9
+ //#endregion
10
+ export { ASH_REFERENCE };
11
+ //# sourceMappingURL=reference.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference.d.cts","names":[],"sources":["../src/reference.ts"],"mappings":";;AAMA;;;;;cAAa,aAAA"}
@@ -0,0 +1,11 @@
1
+ //#region src/reference.d.ts
2
+ /**
3
+ * ASH language reference — designed for LLM consumption.
4
+ *
5
+ * Exported as a string constant so any consumer (AFS provider, AI dev loop,
6
+ * CLI help) can use the same single source of truth.
7
+ */
8
+ declare const ASH_REFERENCE = "# ASH Language Reference\n\nASH is a deterministic pipeline DSL. No imports, no eval, no control flow \u2014 compiler-safe by design.\n\n## Structure\n\n```ash\n# Comments start with #\nparam threshold = 70\n\nlet total = find /data/users | count\n\noutput \"Starting ETL\"\n\n@caps(read /data/* write /data/*)\njob extract {\n find /data/users where active == true\n | where score > $threshold\n | map { fullName: name + \" \" + surname, score: score * 2 + $bonus }\n | tee /data/backup\n | save /data/clean\n}\n```\n\n**Top-level statements**: `param`, `let`, `output`, `job`\n**Pipes**: stages separated by `|`\n\n## Commands\n\n| Command | Input | Output | Description |\n|---------|-------|--------|-------------|\n| `find /path` | none | object_stream | Read records from path. Each record contains `path`, `name`, `kind` + provider-specific content fields (e.g. `battery_level`, `state`, `label`). |\n| `find /path where f == v` | none | object_stream | Read with inline filter (query pushdown). Same output schema as `find`. |\n| `where field op value` | object_stream | object_stream | Filter records (`==` `!=` `>` `<` `>=` `<=`) |\n| `map field` | object_stream | object_stream | Extract single field |\n| `map { out1 in1, out2 in2 }` | object_stream | object_stream | Object transform (rename/reshape) |\n| `map { key: expr }` | object_stream | object_stream | Expression transform (`score: score * 2`) |\n| `save /path` | object_stream | none | Write records to path (terminal) |\n| `publish /topic` | object_stream | none | Publish records to topic (terminal) |\n| `tee /path` | object_stream | object_stream | Side-write copy, stream continues |\n| `count` | object_stream | object_stream | Produce `{ count: N }` \u2192 schema: `{ count: number }` |\n| `group-by field` | object_stream | object_stream | Produce `[{ key, items }]` \u2192 schema: `{ key: T, items: T[] }` |\n| `output \"msg\"` | object_stream | object_stream | Emit text message, pass-through |\n| `output expr` | object_stream | object_stream | Evaluate expression per record, emit as text, pass-through |\n| `input \"prompt\"` | none | object_stream | Prompt for input \u2192 schema: `{ prompt: string, response: string }` |\n| `fanout { branch1, branch2 }` | object_stream | object_stream | Split stream to parallel branches |\n| `action /path` | object_stream | object_stream | Execute external action via `world.exec` |\n| `route field { \"v1\" -> job j1, ... }` | object_stream | none | Dispatch items by field value to named jobs |\n| `lookup /path on key` | object_stream | object_stream | Left-join with data at path on key field |\n\n**Type rule**: each stage's output type must match the next stage's input type. `save`, `publish`, and `route` output `none` \u2014 nothing can follow them in a pipeline.\n\n## Params & Variables\n\n```ash\nparam min_score = 70\nlet bonus = 10\nlet total = find /data/users | count\njob q { find /data/users | where score > $min_score | map { adjusted: score * 2 + $bonus } | save /data/out }\n```\n\n`param` declares a named default that callers can override at execution time.\n`let` binds a name to a literal or a pipeline result. Reference with `$name` in where clauses and expressions.\n\n## Expressions\n\nArithmetic: `score * 2 + $bonus`, `price - discount`\nString concat: `name + \" \" + surname`\nUsable in `map { key: expr }` and runtime `let`.\n\n## Triggers\n\n```ash\njob ingest on /data/raw:created { find /data/raw | save /data/clean }\njob ticker on cron(\"*/5 * * * *\") { find /metrics | save /snapshots }\n```\n\n`on /path:event` \u2014 runs when the named event fires on the given path.\n`on cron(\"expr\")` \u2014 runs on a cron schedule. Standard 5-field cron expression (minute hour day-of-month month day-of-week).\n\n## Annotations\n\nAnnotations decorate jobs with metadata. Place them on lines before `job`:\n\n| Annotation | Description |\n|------------|-------------|\n| `@approval(human)` | Require human approval before execution |\n| `@approval(auto)` | Auto-approved |\n| `@retry(N)` | Retry up to N times on failure |\n| `@timeout(ms)` | Timeout in milliseconds |\n| `@readonly` | Job must not contain `save` or `publish` |\n| `@on_error(strategy)` | Error handling: `skip` (continue), `save /path` (write errors), `fail` (default) |\n| `@caps(op /path, ...)` | Capability declaration: restricts which paths the job can access |\n| `@budget(dim limit, ...)` | Runtime resource limits per job execution |\n\n```ash\n@caps(read /data/*, write /out/*, exec /api/enrich)\n@on_error(skip)\n@retry(3)\njob resilient { find /data/raw | action /api/enrich | save /out/enriched }\n```\n\n`@caps` operations: `read` (find, lookup), `write` (save, publish, tee), `exec` (action).\nPaths support `*` wildcard: `/data/*` matches `/data/users`, `/data/orders/active`.\nCompiler statically verifies all stage paths are within declared caps.\n\n`@budget` dimensions: `actions` (exec calls), `writes` (save/publish/tee), `records` (stream items), `tokens` (LLM consumption), `cost` (dollar cost).\nRuntime throws when any dimension exceeds its limit. Combine with `@on_error(skip)` for soft enforcement.\n\n```ash\n@budget(actions 50, records 10000)\n@caps(read /data/*, write /out/*, exec /api/enrich)\njob bounded { find /data/raw | action /api/enrich | save /out/enriched }\n```\n\n## Paths\n\nAll paths start with `/`. Convention: `/domain/collection` (e.g. `/world/users`, `/data/output`).\n";
9
+ //#endregion
10
+ export { ASH_REFERENCE };
11
+ //# sourceMappingURL=reference.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference.d.mts","names":[],"sources":["../src/reference.ts"],"mappings":";;AAMA;;;;;cAAa,aAAA"}
@@ -0,0 +1,130 @@
1
+ //#region src/reference.ts
2
+ /**
3
+ * ASH language reference — designed for LLM consumption.
4
+ *
5
+ * Exported as a string constant so any consumer (AFS provider, AI dev loop,
6
+ * CLI help) can use the same single source of truth.
7
+ */
8
+ const ASH_REFERENCE = `# ASH Language Reference
9
+
10
+ ASH is a deterministic pipeline DSL. No imports, no eval, no control flow — compiler-safe by design.
11
+
12
+ ## Structure
13
+
14
+ \`\`\`ash
15
+ # Comments start with #
16
+ param threshold = 70
17
+
18
+ let total = find /data/users | count
19
+
20
+ output "Starting ETL"
21
+
22
+ @caps(read /data/* write /data/*)
23
+ job extract {
24
+ find /data/users where active == true
25
+ | where score > $threshold
26
+ | map { fullName: name + " " + surname, score: score * 2 + $bonus }
27
+ | tee /data/backup
28
+ | save /data/clean
29
+ }
30
+ \`\`\`
31
+
32
+ **Top-level statements**: \`param\`, \`let\`, \`output\`, \`job\`
33
+ **Pipes**: stages separated by \`|\`
34
+
35
+ ## Commands
36
+
37
+ | Command | Input | Output | Description |
38
+ |---------|-------|--------|-------------|
39
+ | \`find /path\` | none | object_stream | Read records from path. Each record contains \`path\`, \`name\`, \`kind\` + provider-specific content fields (e.g. \`battery_level\`, \`state\`, \`label\`). |
40
+ | \`find /path where f == v\` | none | object_stream | Read with inline filter (query pushdown). Same output schema as \`find\`. |
41
+ | \`where field op value\` | object_stream | object_stream | Filter records (\`==\` \`!=\` \`>\` \`<\` \`>=\` \`<=\`) |
42
+ | \`map field\` | object_stream | object_stream | Extract single field |
43
+ | \`map { out1 in1, out2 in2 }\` | object_stream | object_stream | Object transform (rename/reshape) |
44
+ | \`map { key: expr }\` | object_stream | object_stream | Expression transform (\`score: score * 2\`) |
45
+ | \`save /path\` | object_stream | none | Write records to path (terminal) |
46
+ | \`publish /topic\` | object_stream | none | Publish records to topic (terminal) |
47
+ | \`tee /path\` | object_stream | object_stream | Side-write copy, stream continues |
48
+ | \`count\` | object_stream | object_stream | Produce \`{ count: N }\` → schema: \`{ count: number }\` |
49
+ | \`group-by field\` | object_stream | object_stream | Produce \`[{ key, items }]\` → schema: \`{ key: T, items: T[] }\` |
50
+ | \`output "msg"\` | object_stream | object_stream | Emit text message, pass-through |
51
+ | \`output expr\` | object_stream | object_stream | Evaluate expression per record, emit as text, pass-through |
52
+ | \`input "prompt"\` | none | object_stream | Prompt for input → schema: \`{ prompt: string, response: string }\` |
53
+ | \`fanout { branch1, branch2 }\` | object_stream | object_stream | Split stream to parallel branches |
54
+ | \`action /path\` | object_stream | object_stream | Execute external action via \`world.exec\` |
55
+ | \`route field { "v1" -> job j1, ... }\` | object_stream | none | Dispatch items by field value to named jobs |
56
+ | \`lookup /path on key\` | object_stream | object_stream | Left-join with data at path on key field |
57
+
58
+ **Type rule**: each stage's output type must match the next stage's input type. \`save\`, \`publish\`, and \`route\` output \`none\` — nothing can follow them in a pipeline.
59
+
60
+ ## Params & Variables
61
+
62
+ \`\`\`ash
63
+ param min_score = 70
64
+ let bonus = 10
65
+ let total = find /data/users | count
66
+ job q { find /data/users | where score > $min_score | map { adjusted: score * 2 + $bonus } | save /data/out }
67
+ \`\`\`
68
+
69
+ \`param\` declares a named default that callers can override at execution time.
70
+ \`let\` binds a name to a literal or a pipeline result. Reference with \`$name\` in where clauses and expressions.
71
+
72
+ ## Expressions
73
+
74
+ Arithmetic: \`score * 2 + $bonus\`, \`price - discount\`
75
+ String concat: \`name + " " + surname\`
76
+ Usable in \`map { key: expr }\` and runtime \`let\`.
77
+
78
+ ## Triggers
79
+
80
+ \`\`\`ash
81
+ job ingest on /data/raw:created { find /data/raw | save /data/clean }
82
+ job ticker on cron("*/5 * * * *") { find /metrics | save /snapshots }
83
+ \`\`\`
84
+
85
+ \`on /path:event\` — runs when the named event fires on the given path.
86
+ \`on cron("expr")\` — runs on a cron schedule. Standard 5-field cron expression (minute hour day-of-month month day-of-week).
87
+
88
+ ## Annotations
89
+
90
+ Annotations decorate jobs with metadata. Place them on lines before \`job\`:
91
+
92
+ | Annotation | Description |
93
+ |------------|-------------|
94
+ | \`@approval(human)\` | Require human approval before execution |
95
+ | \`@approval(auto)\` | Auto-approved |
96
+ | \`@retry(N)\` | Retry up to N times on failure |
97
+ | \`@timeout(ms)\` | Timeout in milliseconds |
98
+ | \`@readonly\` | Job must not contain \`save\` or \`publish\` |
99
+ | \`@on_error(strategy)\` | Error handling: \`skip\` (continue), \`save /path\` (write errors), \`fail\` (default) |
100
+ | \`@caps(op /path, ...)\` | Capability declaration: restricts which paths the job can access |
101
+ | \`@budget(dim limit, ...)\` | Runtime resource limits per job execution |
102
+
103
+ \`\`\`ash
104
+ @caps(read /data/*, write /out/*, exec /api/enrich)
105
+ @on_error(skip)
106
+ @retry(3)
107
+ job resilient { find /data/raw | action /api/enrich | save /out/enriched }
108
+ \`\`\`
109
+
110
+ \`@caps\` operations: \`read\` (find, lookup), \`write\` (save, publish, tee), \`exec\` (action).
111
+ Paths support \`*\` wildcard: \`/data/*\` matches \`/data/users\`, \`/data/orders/active\`.
112
+ Compiler statically verifies all stage paths are within declared caps.
113
+
114
+ \`@budget\` dimensions: \`actions\` (exec calls), \`writes\` (save/publish/tee), \`records\` (stream items), \`tokens\` (LLM consumption), \`cost\` (dollar cost).
115
+ Runtime throws when any dimension exceeds its limit. Combine with \`@on_error(skip)\` for soft enforcement.
116
+
117
+ \`\`\`ash
118
+ @budget(actions 50, records 10000)
119
+ @caps(read /data/*, write /out/*, exec /api/enrich)
120
+ job bounded { find /data/raw | action /api/enrich | save /out/enriched }
121
+ \`\`\`
122
+
123
+ ## Paths
124
+
125
+ All paths start with \`/\`. Convention: \`/domain/collection\` (e.g. \`/world/users\`, \`/data/output\`).
126
+ `;
127
+
128
+ //#endregion
129
+ export { ASH_REFERENCE };
130
+ //# sourceMappingURL=reference.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference.mjs","names":[],"sources":["../src/reference.ts"],"sourcesContent":["/**\n * ASH language reference — designed for LLM consumption.\n *\n * Exported as a string constant so any consumer (AFS provider, AI dev loop,\n * CLI help) can use the same single source of truth.\n */\nexport const ASH_REFERENCE = `# ASH Language Reference\n\nASH is a deterministic pipeline DSL. No imports, no eval, no control flow — compiler-safe by design.\n\n## Structure\n\n\\`\\`\\`ash\n# Comments start with #\nparam threshold = 70\n\nlet total = find /data/users | count\n\noutput \"Starting ETL\"\n\n@caps(read /data/* write /data/*)\njob extract {\n find /data/users where active == true\n | where score > $threshold\n | map { fullName: name + \" \" + surname, score: score * 2 + $bonus }\n | tee /data/backup\n | save /data/clean\n}\n\\`\\`\\`\n\n**Top-level statements**: \\`param\\`, \\`let\\`, \\`output\\`, \\`job\\`\n**Pipes**: stages separated by \\`|\\`\n\n## Commands\n\n| Command | Input | Output | Description |\n|---------|-------|--------|-------------|\n| \\`find /path\\` | none | object_stream | Read records from path. Each record contains \\`path\\`, \\`name\\`, \\`kind\\` + provider-specific content fields (e.g. \\`battery_level\\`, \\`state\\`, \\`label\\`). |\n| \\`find /path where f == v\\` | none | object_stream | Read with inline filter (query pushdown). Same output schema as \\`find\\`. |\n| \\`where field op value\\` | object_stream | object_stream | Filter records (\\`==\\` \\`!=\\` \\`>\\` \\`<\\` \\`>=\\` \\`<=\\`) |\n| \\`map field\\` | object_stream | object_stream | Extract single field |\n| \\`map { out1 in1, out2 in2 }\\` | object_stream | object_stream | Object transform (rename/reshape) |\n| \\`map { key: expr }\\` | object_stream | object_stream | Expression transform (\\`score: score * 2\\`) |\n| \\`save /path\\` | object_stream | none | Write records to path (terminal) |\n| \\`publish /topic\\` | object_stream | none | Publish records to topic (terminal) |\n| \\`tee /path\\` | object_stream | object_stream | Side-write copy, stream continues |\n| \\`count\\` | object_stream | object_stream | Produce \\`{ count: N }\\` → schema: \\`{ count: number }\\` |\n| \\`group-by field\\` | object_stream | object_stream | Produce \\`[{ key, items }]\\` → schema: \\`{ key: T, items: T[] }\\` |\n| \\`output \"msg\"\\` | object_stream | object_stream | Emit text message, pass-through |\n| \\`output expr\\` | object_stream | object_stream | Evaluate expression per record, emit as text, pass-through |\n| \\`input \"prompt\"\\` | none | object_stream | Prompt for input → schema: \\`{ prompt: string, response: string }\\` |\n| \\`fanout { branch1, branch2 }\\` | object_stream | object_stream | Split stream to parallel branches |\n| \\`action /path\\` | object_stream | object_stream | Execute external action via \\`world.exec\\` |\n| \\`route field { \"v1\" -> job j1, ... }\\` | object_stream | none | Dispatch items by field value to named jobs |\n| \\`lookup /path on key\\` | object_stream | object_stream | Left-join with data at path on key field |\n\n**Type rule**: each stage's output type must match the next stage's input type. \\`save\\`, \\`publish\\`, and \\`route\\` output \\`none\\` — nothing can follow them in a pipeline.\n\n## Params & Variables\n\n\\`\\`\\`ash\nparam min_score = 70\nlet bonus = 10\nlet total = find /data/users | count\njob q { find /data/users | where score > $min_score | map { adjusted: score * 2 + $bonus } | save /data/out }\n\\`\\`\\`\n\n\\`param\\` declares a named default that callers can override at execution time.\n\\`let\\` binds a name to a literal or a pipeline result. Reference with \\`$name\\` in where clauses and expressions.\n\n## Expressions\n\nArithmetic: \\`score * 2 + $bonus\\`, \\`price - discount\\`\nString concat: \\`name + \" \" + surname\\`\nUsable in \\`map { key: expr }\\` and runtime \\`let\\`.\n\n## Triggers\n\n\\`\\`\\`ash\njob ingest on /data/raw:created { find /data/raw | save /data/clean }\njob ticker on cron(\"*/5 * * * *\") { find /metrics | save /snapshots }\n\\`\\`\\`\n\n\\`on /path:event\\` — runs when the named event fires on the given path.\n\\`on cron(\"expr\")\\` — runs on a cron schedule. Standard 5-field cron expression (minute hour day-of-month month day-of-week).\n\n## Annotations\n\nAnnotations decorate jobs with metadata. Place them on lines before \\`job\\`:\n\n| Annotation | Description |\n|------------|-------------|\n| \\`@approval(human)\\` | Require human approval before execution |\n| \\`@approval(auto)\\` | Auto-approved |\n| \\`@retry(N)\\` | Retry up to N times on failure |\n| \\`@timeout(ms)\\` | Timeout in milliseconds |\n| \\`@readonly\\` | Job must not contain \\`save\\` or \\`publish\\` |\n| \\`@on_error(strategy)\\` | Error handling: \\`skip\\` (continue), \\`save /path\\` (write errors), \\`fail\\` (default) |\n| \\`@caps(op /path, ...)\\` | Capability declaration: restricts which paths the job can access |\n| \\`@budget(dim limit, ...)\\` | Runtime resource limits per job execution |\n\n\\`\\`\\`ash\n@caps(read /data/*, write /out/*, exec /api/enrich)\n@on_error(skip)\n@retry(3)\njob resilient { find /data/raw | action /api/enrich | save /out/enriched }\n\\`\\`\\`\n\n\\`@caps\\` operations: \\`read\\` (find, lookup), \\`write\\` (save, publish, tee), \\`exec\\` (action).\nPaths support \\`*\\` wildcard: \\`/data/*\\` matches \\`/data/users\\`, \\`/data/orders/active\\`.\nCompiler statically verifies all stage paths are within declared caps.\n\n\\`@budget\\` dimensions: \\`actions\\` (exec calls), \\`writes\\` (save/publish/tee), \\`records\\` (stream items), \\`tokens\\` (LLM consumption), \\`cost\\` (dollar cost).\nRuntime throws when any dimension exceeds its limit. Combine with \\`@on_error(skip)\\` for soft enforcement.\n\n\\`\\`\\`ash\n@budget(actions 50, records 10000)\n@caps(read /data/*, write /out/*, exec /api/enrich)\njob bounded { find /data/raw | action /api/enrich | save /out/enriched }\n\\`\\`\\`\n\n## Paths\n\nAll paths start with \\`/\\`. Convention: \\`/domain/collection\\` (e.g. \\`/world/users\\`, \\`/data/output\\`).\n`;\n"],"mappings":";;;;;;;AAMA,MAAa,gBAAgB"}
@@ -0,0 +1,85 @@
1
+
2
+ //#region src/template.ts
3
+ /**
4
+ * ASH Template Parameter Resolution
5
+ *
6
+ * Resolves ${field} references in action parameters and paths
7
+ * against the current stream record.
8
+ */
9
+ const MAX_RESOLVED_SIZE = 65536;
10
+ const TEMPLATE_RE = /(?<!\\)\$\{([^}]+)\}/g;
11
+ const WHOLE_VALUE_RE = /^\$\{([^}]+)\}$/;
12
+ /**
13
+ * Access a nested value via dot-separated path.
14
+ * Supports object fields and array indices (numeric keys).
15
+ *
16
+ * getNestedValue({data: {id: 42}}, "data.id") → 42
17
+ * getNestedValue({items: [{name: "a"}]}, "items.0.name") → "a"
18
+ */
19
+ function getNestedValue(obj, path) {
20
+ const keys = path.split(".");
21
+ let current = obj;
22
+ for (const key of keys) {
23
+ if (current === null || current === void 0) return void 0;
24
+ if (typeof current !== "object") return void 0;
25
+ current = current[key];
26
+ }
27
+ return current;
28
+ }
29
+ /**
30
+ * Resolve a single template value against a stream record.
31
+ *
32
+ * - Non-string values pass through unchanged
33
+ * - Whole-value "${field}" preserves the field's type
34
+ * - Mixed "text ${field}" stringifies substitutions (JSON.stringify for objects)
35
+ * - Escaped \${...} produces literal ${...}
36
+ * - Missing fields resolve to ""
37
+ * - Resolved strings > 64KB throw
38
+ */
39
+ function resolveTemplate(value, record) {
40
+ if (typeof value !== "string") return value;
41
+ const wholeMatch = value.match(WHOLE_VALUE_RE);
42
+ if (wholeMatch) {
43
+ const resolved = getNestedValue(record, wholeMatch[1]);
44
+ if (resolved === void 0 || resolved === null) return "";
45
+ if (typeof resolved === "string" && resolved.length > MAX_RESOLVED_SIZE) throw new Error(`Template resolution error: resolved value exceeds 64KB limit (${resolved.length} bytes)`);
46
+ return resolved;
47
+ }
48
+ const final = value.replace(TEMPLATE_RE, (_, field) => {
49
+ const val = getNestedValue(record, field);
50
+ if (val === void 0 || val === null) return "";
51
+ if (typeof val === "object") return JSON.stringify(val);
52
+ return String(val);
53
+ }).replace(/\\\$/g, "$");
54
+ if (final.length > MAX_RESOLVED_SIZE) throw new Error(`Template resolution error: resolved value exceeds 64KB limit (${final.length} bytes)`);
55
+ return final;
56
+ }
57
+ /**
58
+ * Resolve all template references in an action's parameters.
59
+ */
60
+ function resolveActionParams(params, record) {
61
+ const resolved = {};
62
+ for (const [key, value] of Object.entries(params)) resolved[key] = resolveTemplate(value, record);
63
+ return resolved;
64
+ }
65
+ /**
66
+ * Resolve template references in a path string.
67
+ * Always returns a string. Validates the resolved path:
68
+ * - No empty segments (//)
69
+ * - No path traversal (..)
70
+ */
71
+ function resolveTemplatePath(path, record) {
72
+ if (!path.includes("${")) return path;
73
+ const final = path.replace(TEMPLATE_RE, (_, field) => {
74
+ const val = getNestedValue(record, field);
75
+ if (val === void 0 || val === null) return "";
76
+ return String(val);
77
+ }).replace(/\\\$/g, "$");
78
+ if (final.includes("//")) throw new Error(`Template path resolution error: resolved path '${final}' contains empty segment (//). Check that template fields are not empty.`);
79
+ if (final.split("/").some((seg) => seg === "..")) throw new Error(`Template path resolution error: resolved path '${final}' contains path traversal (..)`);
80
+ return final;
81
+ }
82
+
83
+ //#endregion
84
+ exports.resolveActionParams = resolveActionParams;
85
+ exports.resolveTemplatePath = resolveTemplatePath;
@@ -0,0 +1,84 @@
1
+ //#region src/template.ts
2
+ /**
3
+ * ASH Template Parameter Resolution
4
+ *
5
+ * Resolves ${field} references in action parameters and paths
6
+ * against the current stream record.
7
+ */
8
+ const MAX_RESOLVED_SIZE = 65536;
9
+ const TEMPLATE_RE = /(?<!\\)\$\{([^}]+)\}/g;
10
+ const WHOLE_VALUE_RE = /^\$\{([^}]+)\}$/;
11
+ /**
12
+ * Access a nested value via dot-separated path.
13
+ * Supports object fields and array indices (numeric keys).
14
+ *
15
+ * getNestedValue({data: {id: 42}}, "data.id") → 42
16
+ * getNestedValue({items: [{name: "a"}]}, "items.0.name") → "a"
17
+ */
18
+ function getNestedValue(obj, path) {
19
+ const keys = path.split(".");
20
+ let current = obj;
21
+ for (const key of keys) {
22
+ if (current === null || current === void 0) return void 0;
23
+ if (typeof current !== "object") return void 0;
24
+ current = current[key];
25
+ }
26
+ return current;
27
+ }
28
+ /**
29
+ * Resolve a single template value against a stream record.
30
+ *
31
+ * - Non-string values pass through unchanged
32
+ * - Whole-value "${field}" preserves the field's type
33
+ * - Mixed "text ${field}" stringifies substitutions (JSON.stringify for objects)
34
+ * - Escaped \${...} produces literal ${...}
35
+ * - Missing fields resolve to ""
36
+ * - Resolved strings > 64KB throw
37
+ */
38
+ function resolveTemplate(value, record) {
39
+ if (typeof value !== "string") return value;
40
+ const wholeMatch = value.match(WHOLE_VALUE_RE);
41
+ if (wholeMatch) {
42
+ const resolved = getNestedValue(record, wholeMatch[1]);
43
+ if (resolved === void 0 || resolved === null) return "";
44
+ if (typeof resolved === "string" && resolved.length > MAX_RESOLVED_SIZE) throw new Error(`Template resolution error: resolved value exceeds 64KB limit (${resolved.length} bytes)`);
45
+ return resolved;
46
+ }
47
+ const final = value.replace(TEMPLATE_RE, (_, field) => {
48
+ const val = getNestedValue(record, field);
49
+ if (val === void 0 || val === null) return "";
50
+ if (typeof val === "object") return JSON.stringify(val);
51
+ return String(val);
52
+ }).replace(/\\\$/g, "$");
53
+ if (final.length > MAX_RESOLVED_SIZE) throw new Error(`Template resolution error: resolved value exceeds 64KB limit (${final.length} bytes)`);
54
+ return final;
55
+ }
56
+ /**
57
+ * Resolve all template references in an action's parameters.
58
+ */
59
+ function resolveActionParams(params, record) {
60
+ const resolved = {};
61
+ for (const [key, value] of Object.entries(params)) resolved[key] = resolveTemplate(value, record);
62
+ return resolved;
63
+ }
64
+ /**
65
+ * Resolve template references in a path string.
66
+ * Always returns a string. Validates the resolved path:
67
+ * - No empty segments (//)
68
+ * - No path traversal (..)
69
+ */
70
+ function resolveTemplatePath(path, record) {
71
+ if (!path.includes("${")) return path;
72
+ const final = path.replace(TEMPLATE_RE, (_, field) => {
73
+ const val = getNestedValue(record, field);
74
+ if (val === void 0 || val === null) return "";
75
+ return String(val);
76
+ }).replace(/\\\$/g, "$");
77
+ if (final.includes("//")) throw new Error(`Template path resolution error: resolved path '${final}' contains empty segment (//). Check that template fields are not empty.`);
78
+ if (final.split("/").some((seg) => seg === "..")) throw new Error(`Template path resolution error: resolved path '${final}' contains path traversal (..)`);
79
+ return final;
80
+ }
81
+
82
+ //#endregion
83
+ export { resolveActionParams, resolveTemplatePath };
84
+ //# sourceMappingURL=template.mjs.map