-
Notifications
You must be signed in to change notification settings - Fork 354
feat(live-query): pollingInterval
#8793
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Caution Review failedThe pull request is closed. 📝 WalkthroughSummary by CodeRabbit
WalkthroughThis PR implements polling-based invalidation for the Live Query plugin, complementing the existing mutation-based approach. The feature adds an optional Changes
Sequence DiagramsequenceDiagram
participant Config as Configuration
participant Plugin as Live Query Plugin
participant Timer as Interval Timer
participant PubSub as Mesh PubSub
participant Subscriber as Subscriber
Config->>Plugin: Load LiveQueryInvalidation config
alt pollingInterval provided
Plugin->>Timer: Schedule setInterval(pollingInterval)
loop Every pollingInterval ms
Timer->>Plugin: Trigger invalidation check
Plugin->>PubSub: Publish invalidation paths
PubSub->>Subscriber: Push updated data
end
else field provided (existing behavior)
Plugin->>Plugin: Track mutation factories by field
Plugin->>PubSub: Subscribe to mutation results
Note over PubSub: On mutation match
PubSub->>Subscriber: Push updated data
end
Plugin->>Plugin: On destroy: clear timers & unsubscribe
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (4 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (9)
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. Comment |
Apollo Federation Subgraph Compatibility Results
Learn more: |
🚀 Snapshot Release (
|
| Package | Version | Info |
|---|---|---|
@graphql-mesh/cache-cfw-kv |
0.105.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/cache-file |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/cache-inmemory-lru |
0.8.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/cache-localforage |
0.105.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/cache-redis |
0.105.1-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/cache-upstash-redis |
0.1.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/compose-cli |
1.5.2-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/fusion-composition |
0.8.19-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/include |
0.3.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/apollo-link |
0.106.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/cli |
0.100.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/config |
0.108.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/graphql |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/grpc |
0.108.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/json-schema |
0.109.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/mongoose |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/mysql |
0.105.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/neo4j |
0.107.13-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/odata |
0.106.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/openapi |
0.109.22-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/postgraphile |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/raml |
0.109.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/soap |
0.107.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/supergraph |
0.10.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/thrift |
0.106.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/tuql |
0.105.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/http |
0.106.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/merger-bare |
0.105.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/merger-stitching |
0.105.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/migrate-config-cli |
1.7.2-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/runtime |
0.106.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/store |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transform-cache |
0.105.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transform-encapsulate |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transform-extend |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transform-federation |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transform-filter-schema |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transform-hive |
0.104.17-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transform-hoist-field |
0.105.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transform-naming-convention |
0.105.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transform-prefix |
0.105.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transform-prune |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transform-rate-limit |
0.105.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transform-rename |
0.105.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transform-replace-field |
0.105.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transform-resolvers-composition |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transform-transfer-schema |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transform-type-merging |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/types |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/urql-exchange |
0.106.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/utils |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@omnigraph/json-schema |
0.109.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@omnigraph/mysql |
0.9.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@omnigraph/neo4j |
0.11.13-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@omnigraph/odata |
0.2.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@omnigraph/openapi |
0.109.22-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@omnigraph/raml |
0.109.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@omnigraph/soap |
0.107.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@omnigraph/sqlite |
0.8.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@omnigraph/thrift |
0.9.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/plugin-deduplicate-request |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/plugin-hive |
0.104.17-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/plugin-http-cache |
0.105.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/plugin-http-details-extensions |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/plugin-jit |
0.2.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/plugin-live-query |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/plugin-mock |
0.105.17-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/plugin-newrelic |
0.104.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/plugin-operation-field-permissions |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/plugin-operation-headers |
1.4.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/plugin-rate-limit |
0.105.3-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/plugin-response-cache |
0.104.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/plugin-snapshot |
0.104.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/plugin-statsd |
0.104.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transport-grpc |
0.3.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transport-mysql |
0.9.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transport-neo4j |
0.10.13-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transport-odata |
0.2.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transport-rest |
0.9.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transport-soap |
0.10.16-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transport-sqlite |
0.9.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
@graphql-mesh/transport-thrift |
0.9.15-alpha-20251105205224-348b2d91bcf32f0c52abafc3535a9a638e0a1eb8 |
npm ↗︎ unpkg ↗︎ |
💻 Website PreviewThe latest changes are available as preview in: https://d8cfb014.graphql-mesh.pages.dev |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (10)
.changeset/@graphql-mesh_plugin-live-query-8793-dependencies.md (1)
5-5: Fix markdownlint MD007 (unordered list indentation).Unindent the bullet to top level. While here, small copy edit for clarity.
--- "@graphql-mesh/plugin-live-query": patch --- -dependencies updates: - - Added dependency [`@whatwg-node/disposablestack@^0.0.6` ↗︎](https://www.npmjs.com/package/@whatwg-node/disposablestack/v/0.0.6) (to `dependencies`) +Dependency updates: +- Added dependency [`@whatwg-node/disposablestack@^0.0.6` ↗︎](https://www.npmjs.com/package/@whatwg-node/disposablestack/v/0.0.6) (to `dependencies`)packages/legacy/types/src/config-schema.json (4)
2464-2476: Update description to reflect union (mutation or polling).Current text implies only mutation-based invalidation.
Apply:
- "description": "Invalidate a query or queries when a specific operation is done without an error (Any of: LiveQueryInvalidationByMutation, LiveQueryInvalidationByPolling)" + "description": "Invalidate queries either after a mutation completes without error or on a fixed polling interval (Any of: LiveQueryInvalidationByMutation, LiveQueryInvalidationByPolling)"
2504-2516: Grammar: “affect” not “effect”.Fix description for clarity.
- "description": "Path to the operation that could effect it. In a form: Mutation.something. Note that wildcard is not supported in this field." + "description": "Path to the operation that could affect it. In a form: Mutation.something. Note that wildcard is not supported in this field."
2519-2535: Constrain and document pollingInterval; describe invalidate targets.Add units and a sane minimum to prevent accidental hot loops; clarify target format.
"LiveQueryInvalidationByPolling": { "additionalProperties": false, "type": "object", "title": "LiveQueryInvalidationByPolling", "properties": { "pollingInterval": { - "type": "integer" + "type": "integer", + "minimum": 1, + "description": "Polling interval in milliseconds (must be >= 1)." }, "invalidate": { "type": "array", "items": { "type": "string" }, - "additionalItems": false + "additionalItems": false, + "description": "Schema coordinates to invalidate on each poll (e.g. \"Query.products\")." } }, "required": ["pollingInterval", "invalidate"] },
2536-2536: Back-compat alias for renamed type.If external tooling referenced
#/definitions/LiveQueryInvalidation, keep a deprecated alias to avoid breakage.}, + "LiveQueryInvalidation": { + "description": "Deprecated alias. Use LiveQueryInvalidationByMutation.", + "allOf": [{ "$ref": "#/definitions/LiveQueryInvalidationByMutation" }] + }, "LiveQueryIndexBy": {website/src/generated-markdown/LiveQueryInvalidationByPolling.generated.md (1)
2-3: Clarify units in docs (milliseconds).Reflect schema units to reduce confusion.
-* `pollingInterval` (type: `Int`, required) +* `pollingInterval` (type: `Int`, required) — interval in milliseconds * `invalidate` (type: `Array of String`, required)Note: this file is generated—please update the JSON schema descriptions and re-generate.
website/src/generated-markdown/LiveQueryInvalidationByMutation.generated.md (1)
2-3: Grammar: “affect” not “effect”.Consistency with schema text.
-* `field` (type: `String`, required) - Path to the operation that could effect it. In a form: Mutation.something. Note that wildcard is not supported in this field. +* `field` (type: `String`, required) - Path to the operation that could affect it. In a form: Mutation.something. Note that wildcard is not supported in this field.Note: update the schema description so regenerated docs pick this up.
packages/plugins/live-query/yaml-config.graphql (2)
43-49: Doc fix: “affect”, not “effect”.Small wording tweak for clarity.
Apply:
- Path to the operation that could effect it. In a form: Mutation.something. Note that wildcard is not supported in this field. + Path to the operation that could affect it. Use the form "Mutation.something". Wildcards are not supported.
51-60: Clarify semantics and constraints for polling invalidation.Explicitly require a positive interval and pluralize the description of coordinates.
Apply:
- """ - Polling interval in milliseconds - """ + """ + Polling interval in milliseconds. Must be a positive integer (> 0). + """ ... - """ - Schema coordinate of the query to be polled - """ - invalidate: [String!]! + """ + Schema coordinates of the queries to invalidate on each interval (e.g., "Query.products"). + """ + invalidate: [String!]!Also consider stating that the list must be non-empty to avoid no-op timers.
packages/plugins/live-query/src/useInvalidateByResult.ts (1)
42-47: Unsubscribedestroylistener on dispose, not only whendestroyfires.Eliminates a latent subscription leak if consumers dispose without publishing
destroy.Apply:
- const id = pubsub.subscribe('destroy', () => { + let destroySubId: number | undefined; + destroySubId = pubsub.subscribe('destroy', () => { for (const timer of timers) { clearInterval(timer); } - pubsub.unsubscribe(id); + if (destroySubId != null) { + pubsub.unsubscribe(destroySubId); + destroySubId = undefined; + } }); ... - [DisposableSymbols.dispose]() { - for (const timer of timers) { - clearInterval(timer); - } - }, + [DisposableSymbols.dispose]() { + for (const timer of timers) { + clearInterval(timer); + } + if (destroySubId != null) { + pubsub.unsubscribe(destroySubId); + destroySubId = undefined; + } + },Also applies to: 86-90
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (11)
.changeset/@graphql-mesh_plugin-live-query-8793-dependencies.md(1 hunks).changeset/social-camels-mate.md(1 hunks)packages/legacy/types/src/config-schema.json(3 hunks)packages/legacy/types/src/config.ts(2 hunks)packages/plugins/live-query/package.json(1 hunks)packages/plugins/live-query/src/useInvalidateByResult.ts(3 hunks)packages/plugins/live-query/yaml-config.graphql(1 hunks)website/src/generated-markdown/LiveQueryIndexBy.generated.md(1 hunks)website/src/generated-markdown/LiveQueryInvalidationByMutation.generated.md(1 hunks)website/src/generated-markdown/LiveQueryInvalidationByPolling.generated.md(1 hunks)website/src/pages/docs/plugins/live-queries.mdx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/plugins/live-query/src/useInvalidateByResult.ts (4)
packages/legacy/types/src/pubsub.ts (4)
MeshPubSub(14-24)HivePubSub(6-6)toMeshPubSub(164-175)DisposableSymbols(135-137)packages/legacy/types/src/config.ts (2)
LiveQueryInvalidationByMutation(2187-2193)LiveQueryInvalidationByPolling(2194-2197)packages/cache/inmemory-lru/src/index.ts (1)
DisposableSymbols(68-74)packages/cache/redis/src/index.ts (1)
DisposableSymbols(132-134)
🪛 markdownlint-cli2 (0.18.1)
.changeset/@graphql-mesh_plugin-live-query-8793-dependencies.md
5-5: Unordered list indentation
Expected: 0; Actual: 2
(MD007, ul-indent)
🔇 Additional comments (6)
.changeset/@graphql-mesh_plugin-live-query-8793-dependencies.md (1)
5-5: Sanity check the dependency range and module format.^0.0.6 effectively pins to 0.0.6 only; confirm that’s intended, and that the package’s module format (ESM/CJS) aligns with how the plugin imports it.
website/src/generated-markdown/LiveQueryIndexBy.generated.md (1)
2-3: LGTM. Clear and consistent with schema.packages/legacy/types/src/config.ts (1)
2156-2159: No action required — JSDoc already includes pollingpackages/legacy/types/src/config.ts (≈ lines 2156–2159) already documents LiveQueryInvalidationByMutation and LiveQueryInvalidationByPolling, so regeneration is not required.
Likely an incorrect or invalid review comment.
packages/plugins/live-query/yaml-config.graphql (2)
62-65: LGTM: annotateLiveQueryIndexBywith@md.Public docs visibility improvement looks good.
41-41: Config mismatch: examples useinvalidateByPollingbut schema exposesinvalidations(union).Repo search returned no matches for
invalidateByPolling/invalidations— unable to verify. Verify where polling is configured and either:
- expose
invalidateByPollingon LiveQueryConfig, or- update docs/examples to use the
invalidationsunion shape (see packages/plugins/live-query/yaml-config.graphql:41).Re-run locally: rg -n --glob '!/node_modules/' -S -e '\binvalidateByPolling\b' -e '\binvalidations\b' -e 'LiveQueryConfig' -e 'LiveQueryInvalidation'
packages/plugins/live-query/src/useInvalidateByResult.ts (1)
25-25: Nit: timer type is fine; keep it.
Set<ReturnType<typeof setInterval>>is portable across Node/DOM. No change needed.
| params.invalidations.forEach(liveQueryInvalidation => { | ||
| const rawInvalidationPaths = liveQueryInvalidation.invalidate; | ||
| const factories = rawInvalidationPaths.map(rawInvalidationPath => | ||
| getInterpolatedStringFactory(rawInvalidationPath), | ||
| ); | ||
| liveQueryInvalidationFactoryMap.set(liveQueryInvalidation.field, factories); | ||
| if ('pollingInterval' in liveQueryInvalidation) { | ||
| timers.add( | ||
| setInterval(() => { | ||
| pubsub.publish('live-query:invalidate', liveQueryInvalidation.invalidate); | ||
| }, liveQueryInvalidation.pollingInterval), | ||
| ); | ||
| } else if ('field' in liveQueryInvalidation) { | ||
| liveQueryInvalidationFactoryMap.set(liveQueryInvalidation.field, factories); | ||
| } | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don’t overwrite same‑field invalidations; validate interval; avoid unnecessary factory creation.
- Current
setoverwrites earlier entries for the samefield. Merge factories instead. - Reject non‑positive
pollingIntervalto prevent hot loops. - Only build factories for field‑based rules.
Apply:
- params.invalidations.forEach(liveQueryInvalidation => {
- const rawInvalidationPaths = liveQueryInvalidation.invalidate;
- const factories = rawInvalidationPaths.map(rawInvalidationPath =>
- getInterpolatedStringFactory(rawInvalidationPath),
- );
- if ('pollingInterval' in liveQueryInvalidation) {
- timers.add(
- setInterval(() => {
- pubsub.publish('live-query:invalidate', liveQueryInvalidation.invalidate);
- }, liveQueryInvalidation.pollingInterval),
- );
- } else if ('field' in liveQueryInvalidation) {
- liveQueryInvalidationFactoryMap.set(liveQueryInvalidation.field, factories);
- }
- });
+ for (const inv of params.invalidations) {
+ if ('pollingInterval' in inv) {
+ if (inv.pollingInterval <= 0) {
+ throw new Error('pollingInterval must be a positive integer (> 0).');
+ }
+ if (inv.invalidate.length === 0) {
+ params.logger.warn('Skipping polling invalidation with empty "invalidate" list.');
+ continue;
+ }
+ timers.add(
+ setInterval(() => {
+ pubsub.publish('live-query:invalidate', inv.invalidate);
+ }, inv.pollingInterval),
+ );
+ } else if ('field' in inv) {
+ const factories = inv.invalidate.map(p => getInterpolatedStringFactory(p));
+ const existing = liveQueryInvalidationFactoryMap.get(inv.field) ?? [];
+ existing.push(...factories);
+ liveQueryInvalidationFactoryMap.set(inv.field, existing);
+ }
+ }📝 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.
| params.invalidations.forEach(liveQueryInvalidation => { | |
| const rawInvalidationPaths = liveQueryInvalidation.invalidate; | |
| const factories = rawInvalidationPaths.map(rawInvalidationPath => | |
| getInterpolatedStringFactory(rawInvalidationPath), | |
| ); | |
| liveQueryInvalidationFactoryMap.set(liveQueryInvalidation.field, factories); | |
| if ('pollingInterval' in liveQueryInvalidation) { | |
| timers.add( | |
| setInterval(() => { | |
| pubsub.publish('live-query:invalidate', liveQueryInvalidation.invalidate); | |
| }, liveQueryInvalidation.pollingInterval), | |
| ); | |
| } else if ('field' in liveQueryInvalidation) { | |
| liveQueryInvalidationFactoryMap.set(liveQueryInvalidation.field, factories); | |
| } | |
| }); | |
| for (const inv of params.invalidations) { | |
| if ('pollingInterval' in inv) { | |
| if (inv.pollingInterval <= 0) { | |
| throw new Error('pollingInterval must be a positive integer (> 0).'); | |
| } | |
| if (inv.invalidate.length === 0) { | |
| params.logger.warn('Skipping polling invalidation with empty "invalidate" list.'); | |
| continue; | |
| } | |
| timers.add( | |
| setInterval(() => { | |
| pubsub.publish('live-query:invalidate', inv.invalidate); | |
| }, inv.pollingInterval), | |
| ); | |
| } else if ('field' in inv) { | |
| const factories = inv.invalidate.map(p => getInterpolatedStringFactory(p)); | |
| const existing = liveQueryInvalidationFactoryMap.get(inv.field) ?? []; | |
| existing.push(...factories); | |
| liveQueryInvalidationFactoryMap.set(inv.field, existing); | |
| } | |
| } |
🤖 Prompt for AI Agents
In packages/plugins/live-query/src/useInvalidateByResult.ts around lines 27 to
41, the current loop always creates factories for every invalidation, overwrites
entries in liveQueryInvalidationFactoryMap when multiple rules target the same
field, and accepts non‑positive pollingInterval values; change it so that
factories are only created for field‑based rules (do not call
getInterpolatedStringFactory for polling‑type invalidations), when a rule has a
'field' merge the new factories with any existing array in
liveQueryInvalidationFactoryMap instead of replacing it, and when a rule has a
'pollingInterval' validate that pollingInterval is a positive number (reject or
throw if <= 0) before creating the timer; ensure timers.add only receives the
setInterval id and preserve existing behavior for publishing invalidation
values.
|
Hey @ardatan I think I found an issue.
Repro:
Expected outcome:
Please let me know if you need any more information from my side. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (3)
packages/plugins/live-query/src/useInvalidateByResult.ts (3)
12-12: Import theDisposabletype explicitly.The return type at line 26 uses
Disposable, but onlyDisposableSymbolsis imported. Add an explicit type import to ensure type safety.Apply:
-import { DisposableSymbols } from '@whatwg-node/disposablestack'; +import { DisposableSymbols, type Disposable } from '@whatwg-node/disposablestack';
29-29: Guard againstpubsubbeing undefined.The
toMeshPubSubfunction may returnundefined, but the result is used without a guard. Add a check to prevent runtime errors.Apply:
const pubsub = toMeshPubSub(params.pubsub); +if (!pubsub) { + throw new Error('Live Query plugin requires a valid pubsub instance.'); +}
30-49: Multiple issues with invalidation processing.Several problems exist in this loop:
- Factories are created for all invalidations (line 32-34) but only used for mutation-based ones
- No validation that
pollingIntervalis positive- Using
set()at line 47 overwrites previous entries for the same field instead of merging- No check for empty
invalidatearraysApply:
- params.invalidations.forEach(liveQueryInvalidation => { - const rawInvalidationPaths = liveQueryInvalidation.invalidate; - const factories = rawInvalidationPaths.map(rawInvalidationPath => - getInterpolatedStringFactory(rawInvalidationPath), - ); - - // Set up polling-based invalidation if pollingInterval is provided - if ('pollingInterval' in liveQueryInvalidation && liveQueryInvalidation.pollingInterval) { - timers.add( - setInterval(() => { - pubsub.publish('live-query:invalidate', liveQueryInvalidation.invalidate); - }, liveQueryInvalidation.pollingInterval), - ); - } - - // Set up mutation-based invalidation if field is provided - if ('field' in liveQueryInvalidation && liveQueryInvalidation.field) { - liveQueryInvalidationFactoryMap.set(liveQueryInvalidation.field, factories); - } - }); + for (const inv of params.invalidations) { + if (inv.invalidate.length === 0) { + params.logger.warn('Skipping invalidation with empty "invalidate" list.'); + continue; + } + + // Set up polling-based invalidation if pollingInterval is provided + if ('pollingInterval' in inv) { + if (inv.pollingInterval <= 0) { + throw new Error('pollingInterval must be a positive integer (> 0).'); + } + timers.add( + setInterval(() => { + pubsub.publish('live-query:invalidate', inv.invalidate); + }, inv.pollingInterval), + ); + } else if ('field' in inv) { + // Set up mutation-based invalidation - only create factories when needed + const factories = inv.invalidate.map(p => getInterpolatedStringFactory(p)); + const existing = liveQueryInvalidationFactoryMap.get(inv.field) ?? []; + existing.push(...factories); + liveQueryInvalidationFactoryMap.set(inv.field, existing); + } + }
🧹 Nitpick comments (1)
packages/plugins/live-query/src/useInvalidateByResult.ts (1)
14-18: Simplify the hybrid type definition.The intersection with
Partial<>types doesn't add meaningful constraints since all values satisfy a partial. The type can be simplified to a plain union.Apply:
-// Hybrid type that supports both polling and mutation-based invalidation -type LiveQueryInvalidationHybrid = ( - | YamlConfig.LiveQueryInvalidationByMutation - | YamlConfig.LiveQueryInvalidationByPolling -) & Partial<YamlConfig.LiveQueryInvalidationByMutation> & Partial<YamlConfig.LiveQueryInvalidationByPolling>; +// Union type that supports both polling and mutation-based invalidation +type LiveQueryInvalidationHybrid = + | YamlConfig.LiveQueryInvalidationByMutation + | YamlConfig.LiveQueryInvalidationByPolling;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
examples/v1-next/integrations/fastify/supergraph.graphql(2 hunks)packages/legacy/types/src/config-schema.json(3 hunks)packages/legacy/types/src/config.ts(2 hunks)packages/plugins/live-query/src/useInvalidateByResult.ts(3 hunks)website/src/generated-markdown/LiveQueryInvalidationByPolling.generated.md(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- website/src/generated-markdown/LiveQueryInvalidationByPolling.generated.md
- examples/v1-next/integrations/fastify/supergraph.graphql
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-22T17:11:35.160Z
Learnt from: enisdenjo
Repo: ardatan/graphql-mesh PR: 8716
File: packages/legacy/types/src/pubsub.ts:64-79
Timestamp: 2025-08-22T17:11:35.160Z
Learning: In the GraphQL Mesh codebase, when implementing MeshPubSub interfaces, the optional `options` parameter in the subscribe method may be intentionally omitted if it's unused and irrelevant for the specific implementation, even if it causes a minor TypeScript interface mismatch.
Applied to files:
packages/plugins/live-query/src/useInvalidateByResult.ts
🧬 Code graph analysis (1)
packages/plugins/live-query/src/useInvalidateByResult.ts (4)
packages/legacy/types/src/config.ts (2)
LiveQueryInvalidationByMutation(2187-2193)LiveQueryInvalidationByPolling(2194-2203)packages/legacy/types/src/pubsub.ts (4)
MeshPubSub(14-24)HivePubSub(6-6)toMeshPubSub(164-175)DisposableSymbols(135-137)packages/cache/inmemory-lru/src/index.ts (1)
DisposableSymbols(68-74)packages/cache/redis/src/index.ts (1)
DisposableSymbols(132-134)
🔇 Additional comments (2)
packages/legacy/types/src/config-schema.json (1)
2464-2537: LGTM! Schema correctly models the union of invalidation types.The JSON schema properly uses
anyOfto represent the union of mutation-based and polling-based invalidations. Both types have clear descriptions and appropriate required fields.packages/legacy/types/src/config.ts (1)
2154-2203: LGTM! TypeScript types correctly reflect the schema changes.The union type for
invalidationsproperly models both mutation-based and polling-based strategies. The interface definitions are clear and type-safe.
1ebcc9c to
19ebc11
Compare
|
Hey @ardatan I just tested it with 0.104.15-alpha-20251103000320-0eb10bb65fedaf33990c08ccc9ba69fe2dd6d6a5 and I am able to validate both polling interval and mutation based invalidation work simultaneously! |
…neous support for polling interval and mutation based invalidation. (#8913)
0eb10bb to
348b2d9
Compare
Closes #8757
Ref GW-506
Support polling in an interval by milliseconds