Source: lib/core/diagnostics/catalogue.js

/**
 * Structured diagnostic catalogue mapping stable error and warning codes
 * to detailed descriptions, causes, remedies, and documentation links.
 */
export const DIAGNOSTIC_CATALOGUE = {
  // Compiler Diagnostics (AVX_C01 - AVX_C06)
  AVX_C01: {
    code: 'AVX_C01',
    name: 'CompilerDistCreationFailed',
    severity: 'error',
    category: 'compiler',
    summary: 'The compiler could not create the build output (dist) directory.',
    causes: [
      'The process lacks write permission for the configured dist path.',
      'A non-directory file already exists at the dist location.'
    ],
    remedies: [
      'Ensure the project directory is writable by the build process.',
      'Remove or rename any file blocking the dist path, then rebuild.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#avx-c01-compiler-dist-creation-failed'
  },
  AVX_C02: {
    code: 'AVX_C02',
    name: 'CompilerSrcDirMissing',
    severity: 'error',
    category: 'compiler',
    summary: 'The project source directory (src) does not exist.',
    causes: [
      'avenx build or avenx check was run outside a scaffolded project.',
      'srcDir in avenx.config.json points at a missing folder.'
    ],
    remedies: [
      'Run avenx init to create a project skeleton.',
      'Create the configured source directory or correct srcDir.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#src-directory-missing'
  },
  AVX_C03: {
    code: 'AVX_C03',
    name: 'CompilerDuplicateComponentName',
    severity: 'error',
    category: 'compiler',
    summary: 'Two or more component files compile to the same class name.',
    causes: [
      'Component class names are derived from file names, so identically named files collide when bundled.'
    ],
    remedies: [
      'Rename or move one of the conflicting files so each produces a distinct class name.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#component-name-conflict'
  },
  AVX_C04: {
    code: 'AVX_C04',
    name: 'CompilerContractStaticViolation',
    severity: 'error',
    category: 'compiler',
    summary: 'A node or component marked with the static contract contains dynamic content.',
    causes: [
      'Interpolations, bindings, or other dynamic expressions appear under a static contract.'
    ],
    remedies: [
      'Remove the dynamic expression, or drop the static contract from that subtree.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#avx-c04-static-contract-violation'
  },
  AVX_C05: {
    code: 'AVX_C05',
    name: 'CompilerContractIsolatedViolation',
    severity: 'error',
    category: 'compiler',
    summary: 'An isolated component reaches outside its isolation boundary.',
    causes: [
      'The template or script accesses $bridges, $parent, or other external scope while isolated.'
    ],
    remedies: [
      'Remove the external access, or stop declaring the isolated contract on that component.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#avx-c05-isolated-contract-violation'
  },
  AVX_C06: {
    code: 'AVX_C06',
    name: 'CompilerContractInvalidDeclaration',
    severity: 'error',
    category: 'compiler',
    summary: 'A <contract /> declaration is unknown or malformed.',
    causes: [
      'An unrecognized contract name or invalid contract attribute syntax was used.'
    ],
    remedies: [
      'Use a supported contract name and check the contract declaration syntax in the docs.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#avx-c06-invalid-contract-declaration'
  },

  // Runtime Diagnostics (AVX_R01 - AVX_R18)
  AVX_R01: {
    code: 'AVX_R01',
    name: 'ComponentMountFailure',
    severity: 'error',
    category: 'runtime',
    summary: 'The target DOM element for component mounting was not found.',
    causes: [
      'The selector passed to app.mount() does not exist in the DOM when called.'
    ],
    remedies: [
      'Ensure the selector exists in index.html before mounting or call after DOMContentLoaded.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#avx-r01-mount-target-not-found'
  },
  AVX_R08: {
    code: 'AVX_R08',
    name: 'UncaughtRenderError',
    severity: 'error',
    category: 'runtime',
    summary: 'An unhandled exception occurred during component render cycle.',
    causes: [
      'Accessing properties of undefined/null during reactive re-rendering.'
    ],
    remedies: [
      'Use optional chaining or default values for nullable reactive properties.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#template-interpolation-failure'
  },
  AVX_R18: {
    code: 'AVX_R18',
    name: 'ReactivityLoopDetected',
    severity: 'error',
    category: 'runtime',
    summary: 'A circular reactive update loop exceeded the maximum update depth limit.',
    causes: [
      'An action or effect synchronously mutates state that triggers itself continuously.'
    ],
    remedies: [
      'Break recursive mutations or add termination conditions to reactive watchers.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#avx-r18-reactive-deadlock-detected'
  },

  // Warning Diagnostics (AVX_W01 - AVX_W35)
  AVX_W01: {
    code: 'AVX_W01',
    name: 'CompilerBundleSizeExceeded',
    severity: 'warning',
    category: 'compiler',
    summary: 'A compiled JavaScript or CSS asset exceeded the configured bundle size budget.',
    causes: [
      'Large third-party dependencies are included in the application bundle.',
      'Unused code or assets are bundled unnecessarily.',
      'Bundle size limits are configured too aggressively for the project.'
    ],
    remedies: [
      'Review generated assets and split or remove large dependencies.',
      'Raise build.bundleBudget in avenx.config.json if the size is expected.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#avx-w01-compiler-bundle-size-exceeded'
  },
  AVX_W03: {
    code: 'AVX_W03',
    name: 'CompilerUndeclaredReference',
    severity: 'warning',
    category: 'compiler',
    summary: 'A template references a variable or method that is not declared on the component.',
    causes: [
      'A typo in a variable or method name.',
      'Referencing state, computed, actions, or bridges that were never declared.'
    ],
    remedies: [
      'Verify the identifier spelling against state, computed, actions, and bridges.',
      'Update the template after renaming a declaration.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#avx-w03-compiler-undeclared-reference'
  },
  AVX_W29: {
    code: 'AVX_W29',
    name: 'CompilerCircularDependency',
    severity: 'warning',
    category: 'compiler',
    summary: 'The compiler detected a circular dependency in the component import graph.',
    causes: [
      'Two components import each other directly.',
      'A longer import chain loops back to an earlier component.'
    ],
    remedies: [
      'Remove imports that close a cycle.',
      'Extract shared code into a module both sides can import without looping.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#avx-w29-compiler-circular-dependency'
  },
  AVX_W35: {
    code: 'AVX_W35',
    name: 'CompilerDeadlockParseFailed',
    severity: 'warning',
    category: 'compiler',
    summary: 'The compiler could not parse a <@deadlock> tag or its attributes.',
    causes: [
      'Malformed or unclosed <@deadlock> / <@fallback> tags.',
      'Invalid attribute values on a <@deadlock> boundary.'
    ],
    remedies: [
      'Fix tag nesting and quoted attributes on the <@deadlock> block.',
      'Confirm nested <@fallback as="..."> is closed with </@fallback>.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#avx-w35-compiler-deadlock-parse-failed'
  },

  // Atlas diagnostics (AVX_W40 - AVX_W41)
  AVX_W40: {
    code: 'AVX_W40',
    name: 'AtlasUnreadState',
    severity: 'warning',
    category: 'compiler',
    summary: 'A declared state key is not read anywhere Atlas can see.',
    causes: [
      'The state was written but the code meant to render or derive from it was never added.',
      'The last reader was deleted and the declaration outlived it.',
      'The state is superseded by a computed or a bridge getter that reads something else.'
    ],
    remedies: [
      'Run `avenx impact <owner>.<key>` to see every relationship Atlas did find before deleting anything.',
      'Delete the declaration and its writers if the value really is dead.',
      'Add the missing read if the warning has caught a half-finished change.',
      'Silence it for one project with `"warnings": { "AVX_W40": "off" }` in avenx.config.json.'
    ],
    docsUrl: 'https://avenx-js.com/core-concepts/atlas#avx-w40-unread-state'
  },
  AVX_W41: {
    code: 'AVX_W41',
    name: 'AtlasUnreachableAction',
    severity: 'warning',
    category: 'compiler',
    summary: 'An action has no call site on any invocation surface Atlas models.',
    causes: [
      'The template handler that called it was renamed or removed.',
      'The action is called from code Atlas does not model, such as a bridge setup() or an imported helper module.',
      'It is dead code left behind by a refactor.'
    ],
    remedies: [
      'Run `avenx why <owner>.<action>` to confirm nothing reaches it.',
      'Wire up the missing @event handler or caller.',
      'Delete the action if it really is unreachable.',
      'Lifecycle actions the runtime calls by name (onMount, onUnmount, and the rest) are already exempt; if yours is invoked another way, silence the code with `"warnings": { "AVX_W41": "off" }`.'
    ],
    docsUrl: 'https://avenx-js.com/core-concepts/atlas#avx-w41-unreachable-action'
  },

  // Rewind diagnostics (AVX_W42 - AVX_W44, AVX_R29)
  AVX_W42: {
    code: 'AVX_W42',
    name: 'TransactionUnbounded',
    severity: 'warning',
    category: 'compiler',
    summary: "An atomic action's write set could not be resolved completely.",
    causes: [
      'The action reaches state through a computed member, a spread, or an identifier Atlas could not resolve.',
      'The action writes state from inside a promise continuation, which runs outside the transaction and is therefore not journaled.',
      'The action calls into a module Atlas does not model, so what that call writes is unknown here.'
    ],
    remedies: [
      'The rewind itself is unaffected: the journal observes the reactive proxies, not this analysis. What is incomplete is the report.',
      'Move optimistic writes ahead of the promise the action returns, so they sit inside the transaction.',
      'Run `avenx why <owner>.<action>` to see which relationships Atlas did resolve.',
      'Silence it for one project with `"warnings": { "AVX_W42": "off" }` in avenx.config.json.'
    ],
    docsUrl: 'https://avenx-js.com/core-concepts/rewind#avx-w42-the-write-set-is-incomplete'
  },
  AVX_W43: {
    code: 'AVX_W43',
    name: 'TransactionIrreversible',
    severity: 'warning',
    category: 'compiler',
    summary: 'An atomic action performs an effect that a rewind cannot undo.',
    causes: [
      'The action emits a bridge event, writes to localStorage or sessionStorage, touches the DOM directly, or starts a timer.',
      'The action performs a request whose result it neither returns nor stores, so the transaction cannot tie its outcome to the rewind.'
    ],
    remedies: [
      'Move the effect after the transaction, where it runs only once the writes have committed.',
      'Return the promise whose rejection should trigger the rewind, so it becomes the transaction outcome rather than a loose effect.',
      'Accept it: state is still restored, and the listed effects are what a rewind will leave behind.',
      'Silence it for one project with `"warnings": { "AVX_W43": "off" }` in avenx.config.json.'
    ],
    docsUrl: 'https://avenx-js.com/core-concepts/rewind#avx-w43-an-effect-a-rewind-cannot-undo'
  },
  AVX_W44: {
    code: 'AVX_W44',
    name: 'TransactionOverlap',
    severity: 'warning',
    category: 'compiler',
    summary: 'Two atomic actions write the same state and may be in flight at the same time.',
    causes: [
      'Two optimistic updates target one value — the classic double-click on a counter or a like button.',
      'An action and its inverse both write the key they toggle.'
    ],
    remedies: [
      'Guard the second invocation while the first is in flight, e.g. with a busy flag the template disables the control on.',
      'Nothing may be wrong: the default "safe" conflict policy refuses to discard a newer value and reports AVX_R29 instead.',
      'Set onConflict="force" on the action when the transaction really is the authority on that value.',
      'Silence it for one project with `"warnings": { "AVX_W44": "off" }` in avenx.config.json.'
    ],
    docsUrl: 'https://avenx-js.com/core-concepts/rewind#avx-w44-two-transactions-writing-the-same-state'
  },
  AVX_W46: {
    code: 'AVX_W46',
    name: 'UnresolvedComponentReference',
    severity: 'warning',
    category: 'compiler',
    summary: 'A PascalCase template tag resolves to no registered component, built-in tag, or known HTML/SVG element.',
    causes: [
      'The component name is misspelled — the single most common template mistake, e.g. <UserCrad /> for <UserCard />.',
      'The component file was never created, or was renamed and a call site was missed.',
      'The tag was meant to be a plain HTML element but was written in PascalCase.'
    ],
    remedies: [
      'Fix the spelling — the warning names the closest registered component when one is near enough.',
      'Create the component file (e.g. "UserCard.component.js") so the name resolves.',
      'Use a lowercase HTML element, or a dash-containing custom element (e.g. "my-widget") which is never flagged.',
      'If the component is registered at runtime via app.register(), silence the code with "warnings": { "AVX_W46": "off" } in avenx.config.json.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#avx-w46-unresolved-component-reference'
  },
  AVX_R29: {
    code: 'AVX_R29',
    name: 'TransactionRewindFailed',
    severity: 'error',
    category: 'runtime',
    summary: 'A rewind could not restore every path the transaction journaled.',
    causes: [
      'Another transaction, or ordinary code, wrote the same path after this transaction did. The "safe" policy will not overwrite a value it did not write.',
      'A collection grew past `rewind.maxSnapshotItems`, so no savepoint was kept for it.',
      'A setter on the restored path threw while the value was being put back.'
    ],
    remedies: [
      'Read the report: it names each path, the value the transaction wrote, and the value found instead.',
      'Check the build output for AVX_W44 — an overlap between two atomic actions is the usual cause.',
      'Raise `rewind.maxSnapshotItems` in avenx.config.json if a large collection was the reason.',
      'Set onConflict="force" on the action if this transaction should win regardless.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#avx-r29-transaction-rewind-failed'
  },

  // Trace diagnostics (AVX_R25 - AVX_R28)
  AVX_C23: {
    code: 'AVX_C23',
    name: 'CompilerMissingRuntimeCapability',
    severity: 'error',
    category: 'compiler',
    summary: 'The build linked a runtime capability and the bundle it produced does not contain it.',
    causes: [
      'The same runtime file was reached by two different paths and bundled twice, so the registry the capability installs itself into was split.',
      'avenx-core is reached through a symlink -- npm link, a file: dependency, a pnpm store or a workspace -- and resolution did not canonicalise it.',
      'A code generator emitted the module that installs the capability without the import that runs it.'
    ],
    remedies: [
      'Rebuild from a clean dist/ directory.',
      'Reinstall dependencies so avenx-core resolves to a single location.',
      'This is a compiler defect rather than a mistake in your source. Nothing was written, so the previous build is intact; please report it with the template that triggered it.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#avx-c23-compiler-missing-runtime-capability'
  },
  AVX_R33: {
    code: 'AVX_R33',
    name: 'ComponentRenderAborted',
    severity: 'error',
    category: 'runtime',
    summary: 'A component failed to render, nothing handled the error, and the component is showing nothing.',
    causes: [
      'An expression, computed value or lifecycle hook threw while the component was rendering.',
      'The component needs a rendering engine this bundle does not carry (see AVX_R34).',
      'The application registers no error handler, so there was nowhere else for the error to go.'
    ],
    remedies: [
      'Fix the cause printed under "cause:" -- it is the original error, with its stack.',
      'Handle it where it belongs: an onErrorCaptured hook on the component or an ancestor, an <@errorBoundary> around the part that can fail, or app.onError() for the whole application.',
      'This message is what an unhandled render failure looks like. It is not raised once something claims the error, so adding a handler silences it by design.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#avx-r33-component-render-aborted'
  },
  AVX_R34: {
    code: 'AVX_R34',
    name: 'RendererUnavailable',
    severity: 'error',
    category: 'runtime',
    summary: 'A component needs a rendering engine that is not present in this bundle.',
    causes: [
      'The component was compiled by a different build than the one that linked this bundle.',
      'The string renderer was linked but its registry was split by the same file being bundled twice.',
      'dist/ holds a mixture of artifacts from more than one build.'
    ],
    remedies: [
      'Delete dist/ and rebuild.',
      'Run "avenx build" and check that AVX_W47 is reported for the component named in the message -- that warning is what links the renderer.',
      'A build that reports AVX_W47 and still produces this is a compiler defect worth filing.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#avx-r34-renderer-unavailable'
  },
  AVX_R25: {
    code: 'AVX_R25',
    name: 'TraceUnreadable',
    severity: 'error',
    category: 'runtime',
    summary: 'A trace file could not be read by this version of Avenx.',
    causes: [
      'The trace was produced by a newer avenx-core than the one reading it.',
      'The file is not a trace, or was truncated while being written.'
    ],
    remedies: [
      'Upgrade avenx-core to a version that understands this trace format version.',
      'Re-record the session with `avenx serve --trace`.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#avx-r25-traceunreadable'
  },
  AVX_R26: {
    code: 'AVX_R26',
    name: 'TraceNotDeterministic',
    severity: 'error',
    category: 'runtime',
    summary: 'A best-effort trace was replayed without explicitly accepting that it may not reproduce.',
    causes: [
      'The recording detected something replay cannot reproduce: an unattributed state write, a polling resource, a value that could not be serialized, or a redacted input.',
      'The recording buffer filled up and dropped its oldest nodes.'
    ],
    remedies: [
      'Run `avenx trace view <id>` to see which reasons were recorded.',
      'Remove the source of non-determinism — move timer-driven state changes into an action, or drop pollInterval — and record again.',
      'Pass { allowBestEffort: true } to replay() to run it anyway; the result reports what diverged instead of claiming a pass.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#avx-r26-tracenotdeterministic'
  },
  AVX_R27: {
    code: 'AVX_R27',
    name: 'TraceReplayDiverged',
    severity: 'error',
    category: 'runtime',
    summary: 'Replaying a trace produced different state or DOM changes than the recording.',
    causes: [
      'Application code changed since the trace was recorded — which is exactly what a regression test is for.',
      'Something outside the sandbox boundary took part in the original run: a bridge reading Date.now(), a timer, or a request made outside a <resource>.',
      'The recorded event target could not be found in the replayed DOM.'
    ],
    remedies: [
      'Read the divergence report: it names the step and the first recorded and replayed operation that differ.',
      'If the change was intended, re-record the trace and re-export the test.',
      'If it was not, the divergence is the bug the trace was meant to catch.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#avx-r27-tracereplaydiverged'
  },
  AVX_R28: {
    code: 'AVX_R28',
    name: 'TraceReplayFailed',
    severity: 'error',
    category: 'runtime',
    summary: 'A replay could not be set up.',
    causes: [
      'replay() was called without a mount() option.'
    ],
    remedies: [
      'Pass a mount() function that constructs and mounts the application, and returns the context your assertions need.'
    ],
    docsUrl: 'https://avenx-js.com/troubleshooting/errors#avx-r28-tracereplayfailed'
  },
};

/**
 * Normalizes input code string to standard format (e.g. 'c01', 'avx_c01' -> 'AVX_C01').
 * @param {string} code
 * @returns {string}
 */
export function normalizeCode(code = '') {
  const clean = code.trim().toUpperCase();
  if (clean.startsWith('AVX_')) return clean;
  if (clean.startsWith('AVX')) return `AVX_${clean.slice(3)}`;
  return `AVX_${clean}`;
}

/**
 * Looks up an entry from the catalogue.
 * @param {string} code
 * @returns {object|null}
 */
export function getDiagnostic(code) {
  const normalized = normalizeCode(code);
  return DIAGNOSTIC_CATALOGUE[normalized] || null;
}

/**
 * Suggests near matches for an unknown code.
 * @param {string} inputCode
 * @returns {string[]}
 */
export function suggestCodes(inputCode) {
  const normalized = normalizeCode(inputCode);
  return Object.keys(DIAGNOSTIC_CATALOGUE).filter((k) => {
    return (
      k.includes(normalized) ||
      k.replace('AVX_', '').includes(normalized.replace('AVX_', ''))
    );
  });
}