Skip to content

Add integration name tracking for collection collision detection#274

Open
Fryuni wants to merge 1 commit into
mainfrom
claude/improve-collision-reporting-dzEgZ
Open

Add integration name tracking for collection collision detection#274
Fryuni wants to merge 1 commit into
mainfrom
claude/improve-collision-reporting-dzEgZ

Conversation

@Fryuni

@Fryuni Fryuni commented Mar 2, 2026

Copy link
Copy Markdown
Owner

Summary

This PR enhances collection injection error reporting by tracking which integration injected each collection, enabling more informative error messages when collections collide or are overridden.

Key Changes

  • Virtual module generation: Refactored the injector plugin to generate runtime code that tracks collection sources alongside the collections themselves. The generated code now:

    • Imports collections from each entrypoint
    • Maintains a collectionSources map to record which integration owns each collection
    • Detects inter-integration collisions at import time and throws descriptive errors
  • Runtime error reporting: Updated injectCollections() to use the tracked integration names in error messages:

    • Reports the specific integration name when a project collection overrides an injected one
    • Reports the specific integration name when extending the wrong injected collection
    • Falls back to generic "an integration" message when integration name is unavailable
  • Type system updates:

    • Added InjectedCollectionEntry interface to track both entrypoint and integration name
    • Added integrationName optional field to InjectCollectionOptions
    • Exported collectionSources from the virtual injector module
  • Utility enhancement: The injectCollections() utility now automatically populates integrationName from the integration's logger label if not explicitly provided

  • Test coverage: Added tests verifying:

    • Integration names appear in collision error messages
    • Generic fallback message when integration name is unavailable
    • Integration names reported when extending wrong collections

Implementation Details

The collision detection happens at two levels:

  1. Virtual module import time: Inter-integration collisions are caught when the generated module is imported, preventing conflicting integrations from both injecting the same collection
  2. Runtime: Project-level overrides are caught when injectCollections() is called, with proper attribution to the original integration

This approach ensures early detection of conflicts while maintaining clear error messages that help developers identify the source of the problem.

https://claude.ai/code/session_016xmmzrXmsoxx2d1EJuVAhK

Summary by CodeRabbit

Improvements

  • Content collection injection error messages now clearly identify which integration is causing conflicts or validation issues, improving the debugging experience.
  • Collision detection for injected collections has been enhanced to explicitly report the integration responsible for conflicts in error messages.
  • Better integration attribution in error reporting helps developers quickly resolve content collection injection issues.

…ting with

Track integration names alongside collection entrypoints so error messages
identify which integration injected a conflicting collection. Also detect
inter-integration collisions when two different integrations inject the
same collection key.

Closes #97

https://claude.ai/code/session_016xmmzrXmsoxx2d1EJuVAhK
@vercel

vercel Bot commented Mar 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
inox-tools Ready Ready Preview Mar 2, 2026 4:06am

@coderabbitai

coderabbitai Bot commented Mar 2, 2026

Copy link
Copy Markdown

Walkthrough

This PR enhances integration collision detection for content collection injection. It tracks integration names alongside collection entrypoints, detects and reports collisions with specific owner information, and improves error messaging to indicate which integrations are conflicting when multiple integrations attempt to inject the same collection keys.

Changes

Cohort / File(s) Summary
Documentation
AGENTS.md
Updated anti-pattern note describing actual collision detection behavior: integration names are now reported in collision errors, and inter-integration collisions are detected at virtual module import time.
Type Definitions
packages/content-utils/src/integration/state.ts, packages/content-utils/virtual.d.ts
Added InjectedCollectionEntry interface with entrypoint and optional integrationName fields; updated IntegrationState.injectedCollectionsEntrypoints to use new interface type; added collectionSources export type declaration.
Public API
packages/content-utils/src/integration/index.ts
Added optional integrationName?: string property to InjectCollectionOptions to track which integration is providing collections.
Integration Utilities
packages/content-utils/src/integration/utilities.ts
Updated injectCollections to merge options and ensure integrationName is populated from provided value or falls back to logger label.
Virtual Module Generation
packages/content-utils/src/integration/injectorPlugin.ts
Refactored RESOLVED_INJECTOR_VIRTUAL_MODULE load path to dynamically import per-entrypoint collections with aliases, introduce collectionSources tracking object mapping keys to owner integrations, and implement collision detection that throws errors when multiple integrations inject the same key.
Runtime Error Handling
packages/content-utils/src/runtime/injector.ts
Added getOwnerLabel() helper function; updated error messages to reference specific integration owners using collectionSources; removed obsolete collision detection placeholder comments.
Tests
packages/content-utils/tests/injector-runtime.test.ts
Added comprehensive test cases validating integration-name-based error messages for collection overrides and extensions, including tests with explicit and missing integration names.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • florian-lefebvre
  • BryceRussell
  • OliverSpeir

Poem

🐰 Collections now know who brought them to the party,
With names tracked through virtual modules, oh so hearty!
When collisions occur, the owner's revealed,
No more mystery—the truth is sealed!
The integration's name is no longer concealed! 📦✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add integration name tracking for collection collision detection' directly and accurately summarizes the main objective of the changeset, which is to enhance collection injection error reporting by tracking which integration injected each collection.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/improve-collision-reporting-dzEgZ

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new

pkg-pr-new Bot commented Mar 2, 2026

Copy link
Copy Markdown

Open in StackBlitz

@inox-tools/aik-mod

npm i https://pkg.pr.new/Fryuni/inox-tools/@inox-tools/aik-mod@274

@inox-tools/aik-route-config

npm i https://pkg.pr.new/Fryuni/inox-tools/@inox-tools/aik-route-config@274

@inox-tools/astro-tests

npm i https://pkg.pr.new/Fryuni/inox-tools/@inox-tools/astro-tests@274

@inox-tools/astro-when

npm i https://pkg.pr.new/Fryuni/inox-tools/@inox-tools/astro-when@274

@inox-tools/content-utils

npm i https://pkg.pr.new/Fryuni/inox-tools/@inox-tools/content-utils@274

@inox-tools/custom-routing

npm i https://pkg.pr.new/Fryuni/inox-tools/@inox-tools/custom-routing@274

@inox-tools/cut-short

npm i https://pkg.pr.new/Fryuni/inox-tools/@inox-tools/cut-short@274

@inox-tools/inline-mod

npm i https://pkg.pr.new/Fryuni/inox-tools/@inox-tools/inline-mod@274

@inox-tools/modular-station

npm i https://pkg.pr.new/Fryuni/inox-tools/@inox-tools/modular-station@274

@inox-tools/portal-gun

npm i https://pkg.pr.new/Fryuni/inox-tools/@inox-tools/portal-gun@274

@inox-tools/request-nanostores

npm i https://pkg.pr.new/Fryuni/inox-tools/@inox-tools/request-nanostores@274

@inox-tools/request-state

npm i https://pkg.pr.new/Fryuni/inox-tools/@inox-tools/request-state@274

@inox-tools/runtime-logger

npm i https://pkg.pr.new/Fryuni/inox-tools/@inox-tools/runtime-logger@274

@inox-tools/server-islands

npm i https://pkg.pr.new/Fryuni/inox-tools/@inox-tools/server-islands@274

@inox-tools/sitemap-ext

npm i https://pkg.pr.new/Fryuni/inox-tools/@inox-tools/sitemap-ext@274

@inox-tools/star-warp

npm i https://pkg.pr.new/Fryuni/inox-tools/@inox-tools/star-warp@274

@inox-tools/utils

npm i https://pkg.pr.new/Fryuni/inox-tools/@inox-tools/utils@274

@inox-tools/velox-luna

npm i https://pkg.pr.new/Fryuni/inox-tools/@inox-tools/velox-luna@274

commit: e19d0d8

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/content-utils/src/integration/injectorPlugin.ts`:
- Around line 49-52: The generated import injects raw entrypoint strings into
single-quoted code, which can break or allow injection when entrypoint contains
quotes/escapes; in injectorPlugin.ts where you build import lines (the loop over
entrypoints and the lines.push that creates `import {collections as
__collections${i}} from '...';`), replace the raw specifier insertion with an
escaped specifier using JSON.stringify(entrypoints[i].entrypoint) so the module
specifier is properly quoted/escaped before being concatenated into the
generated code.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9ecddc9 and c9f8b64.

📒 Files selected for processing (8)
  • AGENTS.md
  • packages/content-utils/src/integration/index.ts
  • packages/content-utils/src/integration/injectorPlugin.ts
  • packages/content-utils/src/integration/state.ts
  • packages/content-utils/src/integration/utilities.ts
  • packages/content-utils/src/runtime/injector.ts
  • packages/content-utils/tests/injector-runtime.test.ts
  • packages/content-utils/virtual.d.ts

Comment on lines +49 to +52
for (let i = 0; i < entrypoints.length; i++) {
lines.push(
`import {collections as __collections${i}} from '${entrypoints[i].entrypoint}';`
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Escape entrypoint specifiers in generated import statements.

Line 51 injects a raw module specifier into single-quoted code. If the entrypoint contains quotes or escapes,
the generated module can break (or be injection-prone). Use JSON.stringify(...) for the specifier.

🔧 Proposed fix
-						lines.push(
-							`import {collections as __collections${i}} from '${entrypoints[i].entrypoint}';`
-						);
+						lines.push(
+							`import { collections as __collections${i} } from ${JSON.stringify(entrypoints[i].entrypoint)};`
+						);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (let i = 0; i < entrypoints.length; i++) {
lines.push(
`import {collections as __collections${i}} from '${entrypoints[i].entrypoint}';`
);
for (let i = 0; i < entrypoints.length; i++) {
lines.push(
`import { collections as __collections${i} } from ${JSON.stringify(entrypoints[i].entrypoint)};`
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/content-utils/src/integration/injectorPlugin.ts` around lines 49 -
52, The generated import injects raw entrypoint strings into single-quoted code,
which can break or allow injection when entrypoint contains quotes/escapes; in
injectorPlugin.ts where you build import lines (the loop over entrypoints and
the lines.push that creates `import {collections as __collections${i}} from
'...';`), replace the raw specifier insertion with an escaped specifier using
JSON.stringify(entrypoints[i].entrypoint) so the module specifier is properly
quoted/escaped before being concatenated into the generated code.

Comment on lines +69 to +83
lines.push(' if (__key in injectedCollections) {');
lines.push(' const __prevOwner = collectionSources[__key];');
lines.push(` const __curOwner = ${nameExpr};`);
lines.push(
' if (__prevOwner !== undefined && __curOwner !== undefined && __prevOwner !== __curOwner) {'
);
lines.push(
' throw new Error(`Content collection "${__key}" is injected by both "${__prevOwner}" and "${__curOwner}". Only one integration may inject a given collection key.`);'
);
lines.push(' }');
lines.push(' }');
lines.push(' injectedCollections[__key] = __value;');
lines.push(
` if (${nameExpr} !== undefined) collectionSources[__key] = ${nameExpr};`
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Collision detection misses conflicts when one integration name is unavailable.

At Line 73, duplicates only throw when both owners are defined and different.
That allows unnamed-vs-named collisions to silently overwrite injectedCollections (Line 80), defeating early collision detection and potentially leaving stale owner attribution.

🛠️ Proposed fix (owner-id based collision check)
 					lines.push('');
 					lines.push('export const collectionSources = {};');
 					lines.push('export const injectedCollections = {};');
+					lines.push('const __collectionOwners = {};');
@@
 						lines.push(
 							`for (const [__key, __value] of Object.entries(__collections${i})) {`
 						);
 						lines.push('  if (__key in injectedCollections) {');
 						lines.push('    const __prevOwner = collectionSources[__key];');
 						lines.push(`    const __curOwner = ${nameExpr};`);
-						lines.push(
-							'    if (__prevOwner !== undefined && __curOwner !== undefined && __prevOwner !== __curOwner) {'
-						);
+						lines.push('    const __prevOwnerId = __collectionOwners[__key];');
+						lines.push(`    const __curOwnerId = ${JSON.stringify(entrypoints[i].entrypoint)};`);
+						lines.push('    if (__prevOwnerId !== __curOwnerId) {');
+						lines.push(
+							'      const __prevLabel = __prevOwner !== undefined ? `"${__prevOwner}"` : "an integration";'
+						);
+						lines.push(
+							'      const __curLabel = __curOwner !== undefined ? `"${__curOwner}"` : "an integration";'
+						);
 						lines.push(
-							'      throw new Error(`Content collection "${__key}" is injected by both "${__prevOwner}" and "${__curOwner}". Only one integration may inject a given collection key.`);'
+							'      throw new Error(`Content collection "${__key}" is injected by both ${__prevLabel} and ${__curLabel}. Only one integration may inject a given collection key.`);'
 						);
 						lines.push('    }');
 						lines.push('  }');
 						lines.push('  injectedCollections[__key] = __value;');
+						lines.push(`  __collectionOwners[__key] = ${JSON.stringify(entrypoints[i].entrypoint)};`);
 						lines.push(
 							`  if (${nameExpr} !== undefined) collectionSources[__key] = ${nameExpr};`
 						);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
lines.push(' if (__key in injectedCollections) {');
lines.push(' const __prevOwner = collectionSources[__key];');
lines.push(` const __curOwner = ${nameExpr};`);
lines.push(
' if (__prevOwner !== undefined && __curOwner !== undefined && __prevOwner !== __curOwner) {'
);
lines.push(
' throw new Error(`Content collection "${__key}" is injected by both "${__prevOwner}" and "${__curOwner}". Only one integration may inject a given collection key.`);'
);
lines.push(' }');
lines.push(' }');
lines.push(' injectedCollections[__key] = __value;');
lines.push(
` if (${nameExpr} !== undefined) collectionSources[__key] = ${nameExpr};`
);
lines.push(' if (__key in injectedCollections) {');
lines.push(' const __prevOwner = collectionSources[__key];');
lines.push(` const __curOwner = ${nameExpr};`);
lines.push(' const __prevOwnerId = __collectionOwners[__key];');
lines.push(` const __curOwnerId = ${JSON.stringify(entrypoints[i].entrypoint)};`);
lines.push(' if (__prevOwnerId !== __curOwnerId) {');
lines.push(
' const __prevLabel = __prevOwner !== undefined ? `"${__prevOwner}"` : "an integration";'
);
lines.push(
' const __curLabel = __curOwner !== undefined ? `"${__curOwner}"` : "an integration";'
);
lines.push(
' throw new Error(`Content collection "${__key}" is injected by both ${__prevLabel} and ${__curLabel}. Only one integration may inject a given collection key.`);'
);
lines.push(' }');
lines.push(' }');
lines.push(' injectedCollections[__key] = __value;');
lines.push(` __collectionOwners[__key] = ${JSON.stringify(entrypoints[i].entrypoint)};`);
lines.push(
` if (${nameExpr} !== undefined) collectionSources[__key] = ${nameExpr};`
);

@Fryuni Fryuni left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants