@tanstack/db 0.5.5 → 0.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -406,6 +406,16 @@ export class CollectionConfigBuilder<
406
406
  return Array.from(deps)
407
407
  })()
408
408
 
409
+ // Ensure dependent builders are actually scheduled in this context so that
410
+ // dependency edges always point to a real job (or a deduped no-op if already scheduled).
411
+ if (contextId) {
412
+ for (const dep of dependentBuilders) {
413
+ if (typeof dep.scheduleGraphRun === `function`) {
414
+ dep.scheduleGraphRun(undefined, { contextId })
415
+ }
416
+ }
417
+ }
418
+
409
419
  // We intentionally scope deduplication to the builder instance. Each instance
410
420
  // owns caches and compiled pipelines, so sharing work across instances that
411
421
  // merely reuse the same string id would execute the wrong builder's graph.
@@ -451,6 +461,13 @@ export class CollectionConfigBuilder<
451
461
  this.pendingGraphRuns.delete(contextId)
452
462
  }
453
463
 
464
+ /**
465
+ * Returns true if this builder has a pending graph run for the given context.
466
+ */
467
+ hasPendingGraphRun(contextId: SchedulerContextId): boolean {
468
+ return this.pendingGraphRuns.has(contextId)
469
+ }
470
+
454
471
  /**
455
472
  * Executes a pending graph run. Called by the scheduler when dependencies are satisfied.
456
473
  * Clears the pending state BEFORE execution so that any re-schedules during the run
package/src/scheduler.ts CHANGED
@@ -25,6 +25,18 @@ interface SchedulerContextState {
25
25
  completed: Set<unknown>
26
26
  }
27
27
 
28
+ interface PendingAwareJob {
29
+ hasPendingGraphRun: (contextId: SchedulerContextId) => boolean
30
+ }
31
+
32
+ function isPendingAwareJob(dep: any): dep is PendingAwareJob {
33
+ return (
34
+ typeof dep === `object` &&
35
+ dep !== null &&
36
+ typeof dep.hasPendingGraphRun === `function`
37
+ )
38
+ }
39
+
28
40
  /**
29
41
  * Scoped scheduler that coalesces work by context and job.
30
42
  *
@@ -119,7 +131,19 @@ export class Scheduler {
119
131
  if (deps) {
120
132
  ready = true
121
133
  for (const dep of deps) {
122
- if (dep !== jobId && !completed.has(dep)) {
134
+ if (dep === jobId) continue
135
+
136
+ const depHasPending =
137
+ isPendingAwareJob(dep) && dep.hasPendingGraphRun(contextId)
138
+
139
+ // Treat dependencies as blocking if the dep has a pending run in this
140
+ // context or if it's enqueued and not yet complete. If the dep is
141
+ // neither pending nor enqueued, consider it satisfied to avoid deadlocks
142
+ // on lazy sources that never schedule work.
143
+ if (
144
+ (jobs.has(dep) && !completed.has(dep)) ||
145
+ (!jobs.has(dep) && depHasPending)
146
+ ) {
123
147
  ready = false
124
148
  break
125
149
  }