{"version":3,"file":"artifact_tool_forbids_artifact_constructor.mjs","names":[],"sources":["../../../src/eslint/rules/artifact_tool_forbids_artifact_constructor.ts"],"sourcesContent":["/**\n * @module @nhtio/adk/eslint/rules/artifact_tool_forbids_artifact_constructor\n *\n * Flags `new ArtifactTool({ … })` whose options literal sets an `artifactConstructor`.\n *\n * Why: an `ArtifactTool` is the tool that answers queries AGAINST a spooled artifact — it must not\n * itself return a `SpooledArtifact`, or the result would be re-wrapped into another artifact whose\n * query tools are themselves `ArtifactTool`s, and so on without end. The base `RawTool` accepts an\n * `artifactConstructor` (the subclass used to wrap a tool's bytes), but `ArtifactTool` explicitly\n * FORBIDS it (`artifactConstructor: validator.any().forbidden()` in its schema). Supplying one is a\n * recursion footgun the constructor rejects at runtime; this rule surfaces it at the construction\n * site so it fails lint.\n *\n * Opt-out:\n *   // eslint-disable-next-line adk/artifact-tool-forbids-artifact-constructor -- <reason>\n */\n\nimport { createRule } from './common'\n\nimport type { TSESTree } from '@typescript-eslint/utils'\n\nconst literalKey = (prop: TSESTree.Property): string | undefined => {\n  if (prop.computed) return undefined\n  if (prop.key.type === 'Identifier') return prop.key.name\n  if (prop.key.type === 'Literal' && typeof prop.key.value === 'string') return prop.key.value\n  return undefined\n}\n\nconst isUndefinedValue = (node: TSESTree.Node): boolean =>\n  node.type === 'Identifier' && node.name === 'undefined'\n\n/** ESLint rule: flags `new ArtifactTool({ … })` that sets the forbidden `artifactConstructor` option. */\nconst artifactToolForbidsArtifactConstructorRule = createRule({\n  name: 'artifact-tool-forbids-artifact-constructor',\n  meta: {\n    type: 'problem',\n    docs: {\n      description:\n        'An `ArtifactTool` must not declare `artifactConstructor` — it answers queries against an artifact and cannot itself return one (infinite re-wrapping). The ArtifactTool schema forbids the field at runtime; this surfaces it at the construction site.',\n    },\n    schema: [],\n    messages: {\n      forbidArtifactConstructor:\n        '`new ArtifactTool` must not set `artifactConstructor` — an artifact-query tool cannot itself return a SpooledArtifact (it would re-wrap forever). The ArtifactTool schema forbids this field. Remove it. Opt out with an eslint-disable-next-line adk/artifact-tool-forbids-artifact-constructor comment + reason.',\n    },\n  },\n  defaultOptions: [],\n  create(context) {\n    return {\n      NewExpression(node: TSESTree.NewExpression) {\n        if (node.callee.type !== 'Identifier' || node.callee.name !== 'ArtifactTool') return\n        const arg = node.arguments[0]\n        if (!arg || arg.type !== 'ObjectExpression') return\n\n        for (const p of arg.properties) {\n          if (p.type === 'SpreadElement') continue\n          if (literalKey(p) === 'artifactConstructor' && !isUndefinedValue(p.value)) {\n            context.report({ node: p, messageId: 'forbidArtifactConstructor' })\n          }\n        }\n      },\n    }\n  },\n})\n\nexport default artifactToolForbidsArtifactConstructorRule\n"],"mappings":";;;;;;;;;;;;;;;;;;AAqBA,IAAM,cAAc,SAAgD;CAClE,IAAI,KAAK,UAAU,OAAO,KAAA;CAC1B,IAAI,KAAK,IAAI,SAAS,cAAc,OAAO,KAAK,IAAI;CACpD,IAAI,KAAK,IAAI,SAAS,aAAa,OAAO,KAAK,IAAI,UAAU,UAAU,OAAO,KAAK,IAAI;AAEzF;AAEA,IAAM,oBAAoB,SACxB,KAAK,SAAS,gBAAgB,KAAK,SAAS;;AAG9C,IAAM,6CAA6C,WAAW;CAC5D,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aACE,0PACJ;EACA,QAAQ,CAAC;EACT,UAAU,EACR,2BACE,qTACJ;CACF;CACA,gBAAgB,CAAC;CACjB,OAAO,SAAS;EACd,OAAO,EACL,cAAc,MAA8B;GAC1C,IAAI,KAAK,OAAO,SAAS,gBAAgB,KAAK,OAAO,SAAS,gBAAgB;GAC9E,MAAM,MAAM,KAAK,UAAU;GAC3B,IAAI,CAAC,OAAO,IAAI,SAAS,oBAAoB;GAE7C,KAAK,MAAM,KAAK,IAAI,YAAY;IAC9B,IAAI,EAAE,SAAS,iBAAiB;IAChC,IAAI,WAAW,CAAC,MAAM,yBAAyB,CAAC,iBAAiB,EAAE,KAAK,GACtE,QAAQ,OAAO;KAAE,MAAM;KAAG,WAAW;IAA4B,CAAC;GAEtE;EACF,EACF;CACF;AACF,CAAC"}