@jini-ai/core 0.4.4 → 0.4.5

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @jini-ai/core
2
2
 
3
+ ## 0.4.5 — 2026-10-13
4
+
5
+ - `FetchQueryCache`: a `replace()` that lands after a read's transport resolves but before publication now wins; arbitration and publication share one continuation.
6
+
3
7
  ## 0.4.4 — 2026-10-12
4
8
 
5
9
  - New `./records` subpath exports `isRecord` and `isNonArrayRecord` without loading the core barrel; the root and `registration-kit` keep re-exporting both.
@@ -91,20 +91,26 @@ export class FetchQueryCache {
91
91
  this.publish(entry, { ...entry.state, isFetching: false });
92
92
  return this.runOnline({ run });
93
93
  }).then(value => {
94
- const data = replacement === entry.replacement ? value : entry.state.data;
95
94
  // Undefined is never successful query data, including an initial or background read.
96
- if (data === undefined)
95
+ // Thrown here so the rejection path below handles it (a replacement still supersedes it).
96
+ if (value === undefined)
97
97
  throw new Error(`${JSON.stringify(entry.key)} data is undefined`);
98
- return data;
99
- }).then(data => {
98
+ return value;
99
+ }).then(value => {
100
100
  // Only the current request may publish or clear invalidation. An older transport
101
101
  // need not support abort: its callers follow the replacement request instead.
102
102
  if (entry.pending !== pending)
103
103
  return (entry.pending ?? entry.state.data);
104
+ // Arbitration and publication share this continuation: a microtask between them would
105
+ // let an older result overwrite a replacement accepted after arbitration.
106
+ if (replacement !== entry.replacement) {
107
+ this.publish(entry, { ...entry.state, isFetching: false });
108
+ return entry.state.data;
109
+ }
104
110
  entry.updatedAt = this.clock.nowMs();
105
111
  entry.invalidated = false;
106
- this.publish(entry, { data, error: null, status: 'success', isFetching: false });
107
- return data;
112
+ this.publish(entry, { data: value, error: null, status: 'success', isFetching: false });
113
+ return value;
108
114
  }, error => {
109
115
  if (entry.pending !== pending)
110
116
  return (entry.pending ?? entry.state.data);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jini-ai/core",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
4
4
  "description": "The Jini kernel: typed DI tokens, pack composition and lifecycle, the tool registry, redaction, and origin/API-token auth primitives. Zero dependencies.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {