react-native-onyx 3.0.109 → 3.0.111
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.
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { CollectionKeyBase, KeyValueMapping, OnyxCollection, OnyxKey, OnyxValue } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Listener fired when an exact key's value changes.
|
|
4
|
+
*/
|
|
5
|
+
type Listener<TKey extends OnyxKey = OnyxKey> = (value: OnyxValue<TKey>, key: TKey) => void;
|
|
6
|
+
/**
|
|
7
|
+
* OnyxSubscriptionManager is a registry for Onyx subscriptions.
|
|
8
|
+
* Subscriptions are stored in `keyListeners`, a flat map keyed by OnyxKey.
|
|
9
|
+
* Subscribers are notified on a per-key basis:
|
|
10
|
+
* - `notifyKey` for individual keys or collection members
|
|
11
|
+
* - `notifyCollection` for batch updates to collections
|
|
12
|
+
*/
|
|
13
|
+
declare class OnyxSubscriptionManager {
|
|
14
|
+
private keyListeners;
|
|
15
|
+
constructor();
|
|
16
|
+
/**
|
|
17
|
+
* Returns the frozen collection object for collection
|
|
18
|
+
* keys, the cached value for single keys, or `undefined` if not in cache.
|
|
19
|
+
*/
|
|
20
|
+
getState<TKey extends OnyxKey>(key: TKey): OnyxValue<TKey>;
|
|
21
|
+
/**
|
|
22
|
+
* Subscribe to an exact key. For a collection root key this is "collection mode": the
|
|
23
|
+
* listener fires with the frozen collection object whenever any member changes. For a
|
|
24
|
+
* collection member key or a regular key, the listener fires when that key's value changes.
|
|
25
|
+
*
|
|
26
|
+
* Returns an unsubscribe function.
|
|
27
|
+
*/
|
|
28
|
+
subscribe<TKey extends OnyxKey>(key: TKey, listener: Listener<TKey>): () => void;
|
|
29
|
+
/**
|
|
30
|
+
* Notify of a single-key write.
|
|
31
|
+
*
|
|
32
|
+
* Dispatch:
|
|
33
|
+
* 1. keyListeners.get(key): exact-key subscribers (always fires).
|
|
34
|
+
* 2. If key is a collection member, keyListeners.get(collectionKey): collection
|
|
35
|
+
* listeners for the parent collection.
|
|
36
|
+
*/
|
|
37
|
+
notifyKey<TKey extends OnyxKey>(key: TKey, value: OnyxValue<TKey>): void;
|
|
38
|
+
/**
|
|
39
|
+
* Notify of a collection-level batch update.
|
|
40
|
+
*
|
|
41
|
+
* Dispatch:
|
|
42
|
+
* 1. keyListeners.get(collectionKey): fires once with the new collection object.
|
|
43
|
+
* 2. keyListeners.get(memberKey): fires per changed member whose value differs from
|
|
44
|
+
* the previous, preserving ref-equality on unchanged members.
|
|
45
|
+
*/
|
|
46
|
+
notifyCollection<TKey extends CollectionKeyBase>(collectionKey: TKey, partialCollection: OnyxCollection<KeyValueMapping[TKey]>, partialPreviousCollection?: OnyxCollection<KeyValueMapping[TKey]>): void;
|
|
47
|
+
/**
|
|
48
|
+
* Wipe all subscriptions. Used by tests and `Onyx.clear()` follow-on.
|
|
49
|
+
*/
|
|
50
|
+
clearAll(): void;
|
|
51
|
+
/**
|
|
52
|
+
* True if there are any subscribers for the given key (exact or parent collection).
|
|
53
|
+
*/
|
|
54
|
+
hasListenersForKey(key: OnyxKey): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Runs a listener, catching and logging any throw so one failing listener can't stop the rest.
|
|
57
|
+
*/
|
|
58
|
+
private safeInvoke;
|
|
59
|
+
}
|
|
60
|
+
declare const onyxSubscriptionManager: OnyxSubscriptionManager;
|
|
61
|
+
export default onyxSubscriptionManager;
|
|
62
|
+
export type { Listener };
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
const Logger = __importStar(require("./Logger"));
|
|
40
|
+
const OnyxCache_1 = __importDefault(require("./OnyxCache"));
|
|
41
|
+
const OnyxKeys_1 = __importDefault(require("./OnyxKeys"));
|
|
42
|
+
/**
|
|
43
|
+
* OnyxSubscriptionManager is a registry for Onyx subscriptions.
|
|
44
|
+
* Subscriptions are stored in `keyListeners`, a flat map keyed by OnyxKey.
|
|
45
|
+
* Subscribers are notified on a per-key basis:
|
|
46
|
+
* - `notifyKey` for individual keys or collection members
|
|
47
|
+
* - `notifyCollection` for batch updates to collections
|
|
48
|
+
*/
|
|
49
|
+
class OnyxSubscriptionManager {
|
|
50
|
+
constructor() {
|
|
51
|
+
this.keyListeners = new Map();
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Returns the frozen collection object for collection
|
|
55
|
+
* keys, the cached value for single keys, or `undefined` if not in cache.
|
|
56
|
+
*/
|
|
57
|
+
getState(key) {
|
|
58
|
+
if (OnyxKeys_1.default.isCollectionKey(key)) {
|
|
59
|
+
return OnyxCache_1.default.getCollectionData(key);
|
|
60
|
+
}
|
|
61
|
+
return OnyxCache_1.default.get(key);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Subscribe to an exact key. For a collection root key this is "collection mode": the
|
|
65
|
+
* listener fires with the frozen collection object whenever any member changes. For a
|
|
66
|
+
* collection member key or a regular key, the listener fires when that key's value changes.
|
|
67
|
+
*
|
|
68
|
+
* Returns an unsubscribe function.
|
|
69
|
+
*/
|
|
70
|
+
subscribe(key, listener) {
|
|
71
|
+
let listeners = this.keyListeners.get(key);
|
|
72
|
+
if (!listeners) {
|
|
73
|
+
listeners = new Set();
|
|
74
|
+
this.keyListeners.set(key, listeners);
|
|
75
|
+
}
|
|
76
|
+
listeners.add(listener);
|
|
77
|
+
return () => {
|
|
78
|
+
const set = this.keyListeners.get(key);
|
|
79
|
+
if (!set) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
set.delete(listener);
|
|
83
|
+
if (set.size === 0) {
|
|
84
|
+
this.keyListeners.delete(key);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Notify of a single-key write.
|
|
90
|
+
*
|
|
91
|
+
* Dispatch:
|
|
92
|
+
* 1. keyListeners.get(key): exact-key subscribers (always fires).
|
|
93
|
+
* 2. If key is a collection member, keyListeners.get(collectionKey): collection
|
|
94
|
+
* listeners for the parent collection.
|
|
95
|
+
*/
|
|
96
|
+
notifyKey(key, value) {
|
|
97
|
+
// 1. Exact-key listeners
|
|
98
|
+
const exact = this.keyListeners.get(key);
|
|
99
|
+
if (exact && exact.size > 0) {
|
|
100
|
+
for (const listener of [...exact]) {
|
|
101
|
+
this.safeInvoke(() => listener(value, key), key);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// 2. Collection-level routing. Only fires when the write is to a member key.
|
|
105
|
+
// Direct writes to a collection root (e.g. `Onyx.merge(COLLECTION_KEY, ...)`) are an
|
|
106
|
+
// unsupported anti-pattern; treat them as opaque single-key writes.
|
|
107
|
+
const collectionKey = OnyxKeys_1.default.getCollectionKey(key);
|
|
108
|
+
const isCollectionMemberWrite = collectionKey !== undefined && collectionKey !== key;
|
|
109
|
+
if (isCollectionMemberWrite) {
|
|
110
|
+
const collectionListeners = this.keyListeners.get(collectionKey);
|
|
111
|
+
if (collectionListeners && collectionListeners.size > 0) {
|
|
112
|
+
const collectionData = OnyxCache_1.default.getCollectionData(collectionKey);
|
|
113
|
+
for (const listener of [...collectionListeners]) {
|
|
114
|
+
this.safeInvoke(() => listener(collectionData, collectionKey), collectionKey);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Notify of a collection-level batch update.
|
|
121
|
+
*
|
|
122
|
+
* Dispatch:
|
|
123
|
+
* 1. keyListeners.get(collectionKey): fires once with the new collection object.
|
|
124
|
+
* 2. keyListeners.get(memberKey): fires per changed member whose value differs from
|
|
125
|
+
* the previous, preserving ref-equality on unchanged members.
|
|
126
|
+
*/
|
|
127
|
+
notifyCollection(collectionKey, partialCollection, partialPreviousCollection) {
|
|
128
|
+
const changedKeys = Object.keys(partialCollection !== null && partialCollection !== void 0 ? partialCollection : {});
|
|
129
|
+
if (changedKeys.length === 0) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
// Read the merged collection once. `cache.getCollectionData()` returns the post-merge
|
|
133
|
+
// frozen object, which is what listeners should see (not the raw `partialCollection`
|
|
134
|
+
// input, which is just the delta and lacks fields preserved during merge).
|
|
135
|
+
const collectionData = OnyxCache_1.default.getCollectionData(collectionKey);
|
|
136
|
+
// 1. Collection listeners fire once with the new collection object.
|
|
137
|
+
const collectionListeners = this.keyListeners.get(collectionKey);
|
|
138
|
+
if (collectionListeners && collectionListeners.size > 0) {
|
|
139
|
+
for (const listener of [...collectionListeners]) {
|
|
140
|
+
this.safeInvoke(() => listener(collectionData, collectionKey), collectionKey);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// 2. Exact-member subscribers fire per changed key (skip if ref unchanged vs previous).
|
|
144
|
+
// Only treat a member as unchanged when `previous` actually carries it: when the
|
|
145
|
+
// previous collection is omitted, a removed member reads `undefined` on both sides
|
|
146
|
+
// and would otherwise be skipped even though it changed.
|
|
147
|
+
for (const memberKey of changedKeys) {
|
|
148
|
+
const value = collectionData === null || collectionData === void 0 ? void 0 : collectionData[memberKey];
|
|
149
|
+
const prev = partialPreviousCollection === null || partialPreviousCollection === void 0 ? void 0 : partialPreviousCollection[memberKey];
|
|
150
|
+
if (partialPreviousCollection && Object.prototype.hasOwnProperty.call(partialPreviousCollection, memberKey) && value === prev) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const exact = this.keyListeners.get(memberKey);
|
|
154
|
+
if (!exact || exact.size === 0) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
for (const listener of [...exact]) {
|
|
158
|
+
this.safeInvoke(() => listener(value, memberKey), memberKey);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Wipe all subscriptions. Used by tests and `Onyx.clear()` follow-on.
|
|
164
|
+
*/
|
|
165
|
+
clearAll() {
|
|
166
|
+
this.keyListeners.clear();
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* True if there are any subscribers for the given key (exact or parent collection).
|
|
170
|
+
*/
|
|
171
|
+
hasListenersForKey(key) {
|
|
172
|
+
var _a, _b, _c, _d;
|
|
173
|
+
if (((_b = (_a = this.keyListeners.get(key)) === null || _a === void 0 ? void 0 : _a.size) !== null && _b !== void 0 ? _b : 0) > 0) {
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
const collectionKey = OnyxKeys_1.default.getCollectionKey(key);
|
|
177
|
+
if (collectionKey && collectionKey !== key && ((_d = (_c = this.keyListeners.get(collectionKey)) === null || _c === void 0 ? void 0 : _c.size) !== null && _d !== void 0 ? _d : 0) > 0) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Runs a listener, catching and logging any throw so one failing listener can't stop the rest.
|
|
184
|
+
*/
|
|
185
|
+
safeInvoke(fn, contextKey) {
|
|
186
|
+
try {
|
|
187
|
+
fn();
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
Logger.logAlert(`[OnyxSubscriptionManager] Listener threw an error for key '${contextKey}': ${error}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const onyxSubscriptionManager = new OnyxSubscriptionManager();
|
|
195
|
+
exports.default = onyxSubscriptionManager;
|
|
@@ -34,7 +34,7 @@ function classifyIDBError(error) {
|
|
|
34
34
|
name.includes('aborterror') ||
|
|
35
35
|
message.includes('connection to indexed database server lost') ||
|
|
36
36
|
message.includes('connection is closing') ||
|
|
37
|
-
|
|
37
|
+
message.includes('without an in-progress transaction') ||
|
|
38
38
|
message.includes('idb write transaction aborted without an error')) {
|
|
39
39
|
return errors_1.StorageErrorClass.TRANSIENT;
|
|
40
40
|
}
|