Skip to content

feat: multi-pattern glob support for observers and queries; v12.1.0#49

Merged
flyingrobots merged 5 commits intomainfrom
release/v12.1.0
Feb 27, 2026
Merged

feat: multi-pattern glob support for observers and queries; v12.1.0#49
flyingrobots merged 5 commits intomainfrom
release/v12.1.0

Conversation

@flyingrobots
Copy link
Member

@flyingrobots flyingrobots commented Feb 26, 2026

Centralizes glob matching logic into a new matchGlob utility and adds support for array-based multi-prefix matching in graph.observer(), query().match(), translationCost(), and watch(). Includes version bump to v12.1.0 and updated tests.

Summary by CodeRabbit

  • New Features

    • watch now accepts either a single pattern or an array of patterns, enabling monitoring of multiple patterns in one call.
  • Bug Fixes / Validation

    • Validation tightened: provided poll intervals must be numeric and at least 1000ms.
  • Documentation

    • Public API docs updated to reflect the expanded pattern type for relevant observers and query helpers.

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

Warning

Rate limit exceeded

@flyingrobots has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 20 minutes and 0 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 235544c and 7808f2d.

📒 Files selected for processing (6)
  • src/domain/services/TranslationCost.js
  • src/domain/warp/query.methods.js
  • src/domain/warp/subscribe.methods.js
  • test/unit/domain/WarpGraph.watch.test.js
  • test/unit/domain/services/ObserverView.test.js
  • test/unit/domain/services/TranslationCost.test.js
📝 Walkthrough

Walkthrough

The watch API now accepts a string or an array of strings for pattern matching. The internal custom matcher was replaced with a matchGlob utility and runtime validation was added for pattern type and poll (if provided, must be number >= 1000). Type declarations and JSDoc were updated.

Changes

Cohort / File(s) Summary
Type Definitions
index.d.ts
Updated WarpGraph.watch() signature to accept pattern: string | string[].
Watch Method Implementation
src/domain/warp/subscribe.methods.js
Replaced bespoke pattern-matching logic with matchGlob(pattern, nodeId); extended watch() to accept string or string[]; added runtime validation (isValidPattern) and poll >= 1000 check; updated JSDoc/comments.
Query JSDoc Updates
src/domain/warp/query.methods.js
Doc annotations updated: config.match, configA.match, and configB.match documented as string | string[] (no runtime behavior change).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 I nibble patterns, string or rows,
Glob hops in where the matcher goes,
Validation keeps my garden neat,
Polls tick steady, paws so fleet,
Watch leaps forward — carrot treat! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding multi-pattern glob support for observers and queries, which aligns with the core functionality changes across all modified files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch release/v12.1.0

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.

@github-actions
Copy link

Release Preflight

  • package version: 12.1.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v12.1.0, release workflow will publish.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/domain/warp/subscribe.methods.js (1)

111-113: ⚠️ Potential issue | 🟡 Minor

Update @throws documentation to reflect array support.

The @throws annotation on line 111 says "If pattern is not a string" but the validation now accepts arrays of strings. The error message on line 136 is correct, but the JSDoc is stale.

📝 Suggested fix
- * `@throws` {Error} If pattern is not a string
+ * `@throws` {Error} If pattern is not a string or array of strings
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/domain/warp/subscribe.methods.js` around lines 111 - 113, Update the
JSDoc `@throws` line that currently reads "If pattern is not a string" to reflect
that the function accepts either a string or an array of strings (e.g., "If
pattern is not a string or an array of strings"); locate the JSDoc above the
subscribe-related function in src/domain/warp/subscribe.methods.js (the same
block that documents the throws for onChange and poll) and change the wording so
it matches the runtime validation/error message emitted around the validation at
line ~136.
🧹 Nitpick comments (3)
src/domain/warp/query.methods.js (1)

334-339: JSDoc type annotations are inconsistent with the actual implementation.

The JSDoc for translationCost parameters still shows {string} for configA.match and configB.match, but the implementation (via computeTranslationCost) accepts string | string[]. Consider updating for consistency:

  * `@param` {Object} configA - Observer configuration for A
- * `@param` {string} configA.match - Glob pattern for visible nodes
+ * `@param` {string|string[]} configA.match - Glob pattern(s) for visible nodes
  * `@param` {string[]} [configA.expose] - Property keys to include
  * `@param` {string[]} [configA.redact] - Property keys to exclude
  * `@param` {Object} configB - Observer configuration for B
- * `@param` {string} configB.match - Glob pattern for visible nodes
+ * `@param` {string|string[]} configB.match - Glob pattern(s) for visible nodes
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/domain/warp/query.methods.js` around lines 334 - 339, The JSDoc for
translationCost is inaccurate: update the types for configA.match and
configB.match (and any related param docs like in the config objects) from
{string} to {string|string[]} to match the implementation in
computeTranslationCost; ensure any occurrences in the JSDoc for translationCost
and its configA/configB parameter descriptions reflect the union type and mirror
computeTranslationCost’s accepted input shape (including optional expose/redact
arrays).
src/domain/utils/matchGlob.js (1)

1-2: Consider adding a cache size limit to prevent unbounded memory growth.

The globRegexCache grows indefinitely. If patterns are user-controlled or dynamic, this could lead to memory leaks. For a library with long-running processes, consider adding an LRU eviction strategy or a maximum cache size.

That said, for typical usage with a fixed set of patterns (e.g., observer configs), the current implementation is likely fine.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/domain/utils/matchGlob.js` around lines 1 - 2, The module-level Map
globRegexCache currently grows without bound; add a bounded cache policy (e.g.,
LRU or max-size eviction) around accesses that insert into globRegexCache so
that when inserting a new compiled RegExp you evict the least-recently-used or
oldest entry once a configured MAX_CACHE_SIZE is exceeded; update the code paths
that read/insert into globRegexCache so they mark entries as recently used (or
move them to the front) and make MAX_CACHE_SIZE configurable and documented.
src/domain/services/ObserverView.js (1)

167-182: Consider adding validation for config.match in constructor.

The ObserverView constructor accepts config.match without validating it's a string or string array. While the caller (graph.observer()) likely validates this, adding defensive validation here would prevent subtle bugs if the class is instantiated directly.

This is a minor suggestion since the internal matchGlob utility handles non-string patterns gracefully (returns false), but explicit validation would provide clearer error messages at construction time rather than silent mismatches.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/domain/services/ObserverView.js` around lines 167 - 182, Add defensive
validation in the ObserverView constructor for config.match: check that
config.match is either a string or an array of strings (use typeof and
Array.isArray with element type checks) before assigning to this._matchPattern,
and throw a clear TypeError if it is missing or of the wrong type; update the
constructor (where this._matchPattern is assigned) to perform this validation so
direct instantiation of ObserverView fails fast with a helpful message.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CHANGELOG.md`:
- Line 17: The version header for 12.0.0 in CHANGELOG.md uses "### [12.0.0] —
2026-02-25" which is an h3 and inconsistent with the rest of the changelog;
change that header to "## [12.0.0] — 2026-02-25" so it matches the h2 format
used by other release sections and conforms to Keep a Changelog.

---

Outside diff comments:
In `@src/domain/warp/subscribe.methods.js`:
- Around line 111-113: Update the JSDoc `@throws` line that currently reads "If
pattern is not a string" to reflect that the function accepts either a string or
an array of strings (e.g., "If pattern is not a string or an array of strings");
locate the JSDoc above the subscribe-related function in
src/domain/warp/subscribe.methods.js (the same block that documents the throws
for onChange and poll) and change the wording so it matches the runtime
validation/error message emitted around the validation at line ~136.

---

Nitpick comments:
In `@src/domain/services/ObserverView.js`:
- Around line 167-182: Add defensive validation in the ObserverView constructor
for config.match: check that config.match is either a string or an array of
strings (use typeof and Array.isArray with element type checks) before assigning
to this._matchPattern, and throw a clear TypeError if it is missing or of the
wrong type; update the constructor (where this._matchPattern is assigned) to
perform this validation so direct instantiation of ObserverView fails fast with
a helpful message.

In `@src/domain/utils/matchGlob.js`:
- Around line 1-2: The module-level Map globRegexCache currently grows without
bound; add a bounded cache policy (e.g., LRU or max-size eviction) around
accesses that insert into globRegexCache so that when inserting a new compiled
RegExp you evict the least-recently-used or oldest entry once a configured
MAX_CACHE_SIZE is exceeded; update the code paths that read/insert into
globRegexCache so they mark entries as recently used (or move them to the front)
and make MAX_CACHE_SIZE configurable and documented.

In `@src/domain/warp/query.methods.js`:
- Around line 334-339: The JSDoc for translationCost is inaccurate: update the
types for configA.match and configB.match (and any related param docs like in
the config objects) from {string} to {string|string[]} to match the
implementation in computeTranslationCost; ensure any occurrences in the JSDoc
for translationCost and its configA/configB parameter descriptions reflect the
union type and mirror computeTranslationCost’s accepted input shape (including
optional expose/redact arrays).

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f752387 and 6010c46.

📒 Files selected for processing (12)
  • CHANGELOG.md
  • index.d.ts
  • package.json
  • src/domain/services/ObserverView.js
  • src/domain/services/QueryBuilder.js
  • src/domain/services/TranslationCost.js
  • src/domain/utils/matchGlob.js
  • src/domain/warp/query.methods.js
  • src/domain/warp/subscribe.methods.js
  • test/unit/domain/WarpGraph.queryBuilder.test.js
  • test/unit/domain/services/ObserverView.test.js
  • test/unit/domain/services/TranslationCost.test.js

@flyingrobots
Copy link
Member Author

@coderabbitai review please 🥕

@coderabbitai
Copy link

coderabbitai bot commented Feb 27, 2026

@flyingrobots I'll review the changes for you! 🐰

This PR introduces multi-pattern glob support across the codebase with a centralized matchGlob utility. I'll analyze the implementation, type safety, test coverage, and overall architecture.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Take main's strict-mode type annotations (@param {unknown}) over
branch's inline @type casts. Fix stale @throws JSDoc in watch().
@github-actions
Copy link

Release Preflight

  • package version: 12.2.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v12.2.0, release workflow will publish.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/domain/warp/subscribe.methods.js (1)

141-143: ⚠️ Potential issue | 🟠 Major

Harden poll validation for non-finite numbers and avoid truthiness gating.

NaN and Infinity both pass typeof poll === 'number', and since NaN < 1000 and Infinity < 1000 both evaluate to false, the validation at line 142 doesn't catch them. Then line 189's if (poll) causes NaN to be silently skipped (falsy) and Infinity to pass through to setInterval (truthy), creating unexpected behavior. Use Number.isFinite() to reject non-finite numbers and gate on poll !== undefined instead of truthiness.

Suggested fix
   if (poll !== undefined) {
-    if (typeof poll !== 'number' || poll < 1000) {
+    if (typeof poll !== 'number' || !Number.isFinite(poll) || poll < 1000) {
       throw new Error('poll must be a number >= 1000');
     }
   }
@@
-  if (poll) {
+  if (poll !== undefined) {
     pollIntervalId = setInterval(() => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/domain/warp/subscribe.methods.js` around lines 141 - 143, The poll
validation is too weak: update the check around the poll variable in the
subscribe flow (where poll is currently validated and later used in setInterval)
to reject non-finite numbers and avoid truthiness gating; specifically, replace
the typeof/less-than check with Number.isFinite(poll) and ensure the runtime
branch uses poll !== undefined (not if (poll)) before calling setInterval, and
update the thrown error message to state "poll must be a finite number >= 1000".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/domain/warp/subscribe.methods.js`:
- Around line 134-137: The current isValidPattern check accepts empty or sparse
arrays (e.g., [] or [ , ]) which creates no-op watchers; update validation
around pattern (used in isValidPattern and the subsequent throw) to reject empty
arrays and arrays with holes by requiring Array.isArray(pattern) &&
pattern.length > 0 && Object.keys(pattern).length === pattern.length &&
pattern.every(i => typeof i === 'string'), so only non-empty, dense arrays of
strings (or a string) are allowed.

---

Outside diff comments:
In `@src/domain/warp/subscribe.methods.js`:
- Around line 141-143: The poll validation is too weak: update the check around
the poll variable in the subscribe flow (where poll is currently validated and
later used in setInterval) to reject non-finite numbers and avoid truthiness
gating; specifically, replace the typeof/less-than check with
Number.isFinite(poll) and ensure the runtime branch uses poll !== undefined (not
if (poll)) before calling setInterval, and update the thrown error message to
state "poll must be a finite number >= 1000".

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6010c46 and 1f69f75.

📒 Files selected for processing (2)
  • index.d.ts
  • src/domain/warp/subscribe.methods.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • index.d.ts

@github-actions
Copy link

Release Preflight

  • package version: 12.2.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v12.2.0, release workflow will publish.

- Reject empty arrays in match pattern validators (observer, watch,
  translationCost) — prevents no-op watchers
- Reject NaN/Infinity in poll validation via Number.isFinite()
- Replace truthiness gate `if (poll)` with `if (poll !== undefined)`
- Add tests for NaN, Infinity, and empty array edge cases
@flyingrobots
Copy link
Member Author

@coderabbitai review please 🥕

@coderabbitai
Copy link

coderabbitai bot commented Feb 27, 2026

@flyingrobots I'll review the changes right away! 🐰

This PR introduces multi-pattern glob support across the codebase with a centralized matchGlob utility. I'll analyze the implementation, type safety, test coverage, and overall architecture.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions
Copy link

Release Preflight

  • package version: 12.2.0
  • prerelease: false
  • npm dist-tag on release: latest
  • npm pack dry-run: passed
  • jsr publish dry-run: passed

If you tag this commit as v12.2.0, release workflow will publish.

@flyingrobots flyingrobots merged commit e281725 into main Feb 27, 2026
7 checks passed
@flyingrobots flyingrobots deleted the release/v12.1.0 branch February 27, 2026 10:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant