@signalium/query 1.0.12 → 1.0.14
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 +20 -0
- package/dist/cjs/development/index.js +2985 -0
- package/dist/cjs/development/index.js.map +1 -0
- package/dist/cjs/development/react/index.js +52 -0
- package/dist/cjs/development/react/index.js.map +1 -0
- package/dist/cjs/{stores/shared.js → development/shared-5acOO-tS.js} +11 -14
- package/dist/cjs/development/shared-5acOO-tS.js.map +1 -0
- package/dist/cjs/development/stores/async.js +304 -0
- package/dist/cjs/development/stores/async.js.map +1 -0
- package/dist/cjs/development/stores/sync.js +214 -0
- package/dist/cjs/development/stores/sync.js.map +1 -0
- package/dist/cjs/production/index.js +2985 -0
- package/dist/cjs/production/index.js.map +1 -0
- package/dist/cjs/production/package.json +3 -0
- package/dist/cjs/production/react/index.js +52 -0
- package/dist/cjs/production/react/index.js.map +1 -0
- package/dist/cjs/production/shared-5acOO-tS.js +20 -0
- package/dist/cjs/production/shared-5acOO-tS.js.map +1 -0
- package/dist/cjs/production/stores/async.js +304 -0
- package/dist/cjs/production/stores/async.js.map +1 -0
- package/dist/cjs/production/stores/sync.js +214 -0
- package/dist/cjs/production/stores/sync.js.map +1 -0
- package/dist/esm/EntityMap.d.ts +22 -0
- package/dist/esm/EntityMap.d.ts.map +1 -1
- package/dist/esm/MutationResult.d.ts +43 -0
- package/dist/esm/MutationResult.d.ts.map +1 -0
- package/dist/esm/QueryClient.d.ts +31 -2
- package/dist/esm/QueryClient.d.ts.map +1 -1
- package/dist/esm/development/index.js +2985 -0
- package/dist/esm/development/index.js.map +1 -0
- package/dist/esm/development/react/index.js +52 -0
- package/dist/esm/development/react/index.js.map +1 -0
- package/dist/esm/development/shared-Br5plsKm.js +21 -0
- package/dist/esm/development/shared-Br5plsKm.js.map +1 -0
- package/dist/esm/development/stores/async.js +304 -0
- package/dist/esm/development/stores/async.js.map +1 -0
- package/dist/esm/development/stores/sync.js +214 -0
- package/dist/esm/development/stores/sync.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/mutation.d.ts +82 -0
- package/dist/esm/mutation.d.ts.map +1 -0
- package/dist/esm/parseEntities.d.ts.map +1 -1
- package/dist/esm/production/index.js +2985 -0
- package/dist/esm/production/index.js.map +1 -0
- package/dist/esm/production/react/index.js +52 -0
- package/dist/esm/production/react/index.js.map +1 -0
- package/dist/esm/production/shared-Br5plsKm.js +21 -0
- package/dist/esm/production/shared-Br5plsKm.js.map +1 -0
- package/dist/esm/production/stores/async.js +304 -0
- package/dist/esm/production/stores/async.js.map +1 -0
- package/dist/esm/production/stores/sync.js +214 -0
- package/dist/esm/production/stores/sync.js.map +1 -0
- package/dist/esm/proxy.d.ts +6 -5
- package/dist/esm/proxy.d.ts.map +1 -1
- package/dist/esm/query.d.ts +3 -1
- package/dist/esm/query.d.ts.map +1 -1
- package/dist/esm/typeDefs.d.ts +5 -3
- package/dist/esm/typeDefs.d.ts.map +1 -1
- package/dist/esm/types.d.ts +72 -5
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/utils.d.ts +35 -0
- package/dist/esm/utils.d.ts.map +1 -1
- package/package.json +55 -21
- package/stores/async.js +1 -1
- package/stores/sync.js +1 -1
- package/dist/cjs/EntityMap.js +0 -103
- package/dist/cjs/EntityMap.js.map +0 -1
- package/dist/cjs/MemoryEvictionManager.js +0 -56
- package/dist/cjs/MemoryEvictionManager.js.map +0 -1
- package/dist/cjs/NetworkManager.js +0 -125
- package/dist/cjs/NetworkManager.js.map +0 -1
- package/dist/cjs/QueryClient.js +0 -162
- package/dist/cjs/QueryClient.js.map +0 -1
- package/dist/cjs/QueryResult.js +0 -920
- package/dist/cjs/QueryResult.js.map +0 -1
- package/dist/cjs/RefetchManager.js +0 -88
- package/dist/cjs/RefetchManager.js.map +0 -1
- package/dist/cjs/errors.js +0 -129
- package/dist/cjs/errors.js.map +0 -1
- package/dist/cjs/index.js +0 -43
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/parseEntities.js +0 -142
- package/dist/cjs/parseEntities.js.map +0 -1
- package/dist/cjs/pathInterpolator.js +0 -69
- package/dist/cjs/pathInterpolator.js.map +0 -1
- package/dist/cjs/proxy.js +0 -263
- package/dist/cjs/proxy.js.map +0 -1
- package/dist/cjs/query.js +0 -184
- package/dist/cjs/query.js.map +0 -1
- package/dist/cjs/react/index.js +0 -6
- package/dist/cjs/react/index.js.map +0 -1
- package/dist/cjs/react/use-query.js +0 -56
- package/dist/cjs/react/use-query.js.map +0 -1
- package/dist/cjs/stores/async.js +0 -344
- package/dist/cjs/stores/async.js.map +0 -1
- package/dist/cjs/stores/shared.js.map +0 -1
- package/dist/cjs/stores/sync.js +0 -240
- package/dist/cjs/stores/sync.js.map +0 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +0 -1
- package/dist/cjs/type-utils.js +0 -3
- package/dist/cjs/type-utils.js.map +0 -1
- package/dist/cjs/typeDefs.js +0 -620
- package/dist/cjs/typeDefs.js.map +0 -1
- package/dist/cjs/types.js +0 -33
- package/dist/cjs/types.js.map +0 -1
- package/dist/cjs/utils.js +0 -23
- package/dist/cjs/utils.js.map +0 -1
- package/dist/esm/EntityMap.js +0 -99
- package/dist/esm/EntityMap.js.map +0 -1
- package/dist/esm/MemoryEvictionManager.js +0 -51
- package/dist/esm/MemoryEvictionManager.js.map +0 -1
- package/dist/esm/NetworkManager.js +0 -120
- package/dist/esm/NetworkManager.js.map +0 -1
- package/dist/esm/QueryClient.js +0 -154
- package/dist/esm/QueryClient.js.map +0 -1
- package/dist/esm/QueryResult.js +0 -916
- package/dist/esm/QueryResult.js.map +0 -1
- package/dist/esm/RefetchManager.js +0 -83
- package/dist/esm/RefetchManager.js.map +0 -1
- package/dist/esm/errors.js +0 -125
- package/dist/esm/errors.js.map +0 -1
- package/dist/esm/index.js +0 -8
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/parseEntities.js +0 -135
- package/dist/esm/parseEntities.js.map +0 -1
- package/dist/esm/pathInterpolator.js +0 -66
- package/dist/esm/pathInterpolator.js.map +0 -1
- package/dist/esm/proxy.js +0 -254
- package/dist/esm/proxy.js.map +0 -1
- package/dist/esm/query.js +0 -177
- package/dist/esm/query.js.map +0 -1
- package/dist/esm/react/index.js +0 -2
- package/dist/esm/react/index.js.map +0 -1
- package/dist/esm/react/use-query.js +0 -53
- package/dist/esm/react/use-query.js.map +0 -1
- package/dist/esm/stores/async.js +0 -340
- package/dist/esm/stores/async.js.map +0 -1
- package/dist/esm/stores/shared.js +0 -13
- package/dist/esm/stores/shared.js.map +0 -1
- package/dist/esm/stores/sync.js +0 -234
- package/dist/esm/stores/sync.js.map +0 -1
- package/dist/esm/type-utils.js +0 -2
- package/dist/esm/type-utils.js.map +0 -1
- package/dist/esm/typeDefs.js +0 -606
- package/dist/esm/typeDefs.js.map +0 -1
- package/dist/esm/types.js +0 -30
- package/dist/esm/types.js.map +0 -1
- package/dist/esm/utils.js +0 -20
- package/dist/esm/utils.js.map +0 -1
- /package/dist/cjs/{package.json → development/package.json} +0 -0
|
@@ -0,0 +1,2985 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const signalium = require("signalium");
|
|
4
|
+
const utils = require("signalium/utils");
|
|
5
|
+
var RefetchInterval = /* @__PURE__ */ ((RefetchInterval2) => {
|
|
6
|
+
RefetchInterval2[RefetchInterval2["Every1Second"] = 1e3] = "Every1Second";
|
|
7
|
+
RefetchInterval2[RefetchInterval2["Every5Seconds"] = 5e3] = "Every5Seconds";
|
|
8
|
+
RefetchInterval2[RefetchInterval2["Every10Seconds"] = 1e4] = "Every10Seconds";
|
|
9
|
+
RefetchInterval2[RefetchInterval2["Every30Seconds"] = 3e4] = "Every30Seconds";
|
|
10
|
+
RefetchInterval2[RefetchInterval2["Every1Minute"] = 6e4] = "Every1Minute";
|
|
11
|
+
RefetchInterval2[RefetchInterval2["Every5Minutes"] = 3e5] = "Every5Minutes";
|
|
12
|
+
return RefetchInterval2;
|
|
13
|
+
})(RefetchInterval || {});
|
|
14
|
+
var NetworkMode = /* @__PURE__ */ ((NetworkMode2) => {
|
|
15
|
+
NetworkMode2["Always"] = "always";
|
|
16
|
+
NetworkMode2["Online"] = "online";
|
|
17
|
+
NetworkMode2["OfflineFirst"] = "offlineFirst";
|
|
18
|
+
return NetworkMode2;
|
|
19
|
+
})(NetworkMode || {});
|
|
20
|
+
var Mask = /* @__PURE__ */ ((Mask2) => {
|
|
21
|
+
Mask2[Mask2["UNDEFINED"] = 1] = "UNDEFINED";
|
|
22
|
+
Mask2[Mask2["NULL"] = 2] = "NULL";
|
|
23
|
+
Mask2[Mask2["NUMBER"] = 4] = "NUMBER";
|
|
24
|
+
Mask2[Mask2["STRING"] = 8] = "STRING";
|
|
25
|
+
Mask2[Mask2["BOOLEAN"] = 16] = "BOOLEAN";
|
|
26
|
+
Mask2[Mask2["OBJECT"] = 32] = "OBJECT";
|
|
27
|
+
Mask2[Mask2["ARRAY"] = 64] = "ARRAY";
|
|
28
|
+
Mask2[Mask2["ID"] = 128] = "ID";
|
|
29
|
+
Mask2[Mask2["RECORD"] = 256] = "RECORD";
|
|
30
|
+
Mask2[Mask2["UNION"] = 512] = "UNION";
|
|
31
|
+
Mask2[Mask2["ENTITY"] = 1024] = "ENTITY";
|
|
32
|
+
Mask2[Mask2["HAS_SUB_ENTITY"] = 2048] = "HAS_SUB_ENTITY";
|
|
33
|
+
Mask2[Mask2["HAS_NUMBER_FORMAT"] = 4096] = "HAS_NUMBER_FORMAT";
|
|
34
|
+
Mask2[Mask2["HAS_STRING_FORMAT"] = 8192] = "HAS_STRING_FORMAT";
|
|
35
|
+
Mask2[Mask2["PARSE_RESULT"] = 16384] = "PARSE_RESULT";
|
|
36
|
+
return Mask2;
|
|
37
|
+
})(Mask || {});
|
|
38
|
+
const ARRAY_KEY = Symbol("array");
|
|
39
|
+
const RECORD_KEY = Symbol("record");
|
|
40
|
+
const entries$2 = Object.entries;
|
|
41
|
+
const isArray$2 = Array.isArray;
|
|
42
|
+
class ValidatorDef {
|
|
43
|
+
kind;
|
|
44
|
+
mask;
|
|
45
|
+
_optional;
|
|
46
|
+
_nullable;
|
|
47
|
+
_nullish;
|
|
48
|
+
_shapeKey = void 0;
|
|
49
|
+
_shape;
|
|
50
|
+
subEntityPaths = void 0;
|
|
51
|
+
typenameField = void 0;
|
|
52
|
+
typenameValue = void 0;
|
|
53
|
+
idField = void 0;
|
|
54
|
+
values = void 0;
|
|
55
|
+
/**
|
|
56
|
+
* Lazy factory function for creating entity methods.
|
|
57
|
+
* Evaluated once during reifyShape() and cached in _methods.
|
|
58
|
+
*/
|
|
59
|
+
_methodsFactory = void 0;
|
|
60
|
+
/**
|
|
61
|
+
* Cached methods object from evaluating _methodsFactory.
|
|
62
|
+
* Populated during reifyShape() - the same methods object is shared across all proxies,
|
|
63
|
+
* but each proxy binds its own reactive method wrappers.
|
|
64
|
+
*/
|
|
65
|
+
_methods = void 0;
|
|
66
|
+
/**
|
|
67
|
+
* Entity configuration including stream options.
|
|
68
|
+
*/
|
|
69
|
+
_entityConfig = void 0;
|
|
70
|
+
constructor(kind, mask, shape, values = void 0) {
|
|
71
|
+
this.kind = kind;
|
|
72
|
+
this.mask = mask;
|
|
73
|
+
this._shape = shape;
|
|
74
|
+
this.values = values;
|
|
75
|
+
}
|
|
76
|
+
static cloneWith(def, mask, values = void 0) {
|
|
77
|
+
const newDef = new ValidatorDef(def.kind, mask | def.mask, def._shape, values);
|
|
78
|
+
newDef.subEntityPaths = def.subEntityPaths;
|
|
79
|
+
newDef.values = def.values;
|
|
80
|
+
newDef.typenameField = def.typenameField;
|
|
81
|
+
newDef.typenameValue = def.typenameValue;
|
|
82
|
+
newDef.idField = def.idField;
|
|
83
|
+
newDef._methodsFactory = def._methodsFactory;
|
|
84
|
+
newDef._methods = def._methods;
|
|
85
|
+
newDef._entityConfig = def._entityConfig;
|
|
86
|
+
return newDef;
|
|
87
|
+
}
|
|
88
|
+
reifyShape() {
|
|
89
|
+
if (this._shapeKey === void 0) {
|
|
90
|
+
switch (this.kind) {
|
|
91
|
+
case 5: {
|
|
92
|
+
const shape = this._shape();
|
|
93
|
+
this._shape = reifyObjectShape(this, shape);
|
|
94
|
+
if (this._methodsFactory && !this._methods) {
|
|
95
|
+
this._methods = this._methodsFactory();
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case 0:
|
|
100
|
+
this._shape = reifyObjectShape(this, this._shape);
|
|
101
|
+
break;
|
|
102
|
+
case 1:
|
|
103
|
+
this._shape = reifyUnionShape(this, this._shape);
|
|
104
|
+
break;
|
|
105
|
+
case 3:
|
|
106
|
+
case 4:
|
|
107
|
+
case 6: {
|
|
108
|
+
const shape = this._shape;
|
|
109
|
+
let shapeKey;
|
|
110
|
+
if (shape instanceof ValidatorDef) {
|
|
111
|
+
shapeKey = shape.shapeKey;
|
|
112
|
+
if (shape.mask & (Mask.ENTITY | Mask.HAS_SUB_ENTITY)) {
|
|
113
|
+
this.mask |= Mask.HAS_SUB_ENTITY;
|
|
114
|
+
}
|
|
115
|
+
} else if (shape instanceof CaseInsensitiveSet) {
|
|
116
|
+
shapeKey = utils.hashValue(Array.from(shape));
|
|
117
|
+
} else {
|
|
118
|
+
shapeKey = utils.hashValue(shape);
|
|
119
|
+
}
|
|
120
|
+
this._shapeKey = utils.hashValue([this.kind, this.mask, this.values, shapeKey]);
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
get shape() {
|
|
127
|
+
this.reifyShape();
|
|
128
|
+
return this._shape;
|
|
129
|
+
}
|
|
130
|
+
get methods() {
|
|
131
|
+
this.reifyShape();
|
|
132
|
+
return this._methods;
|
|
133
|
+
}
|
|
134
|
+
get shapeKey() {
|
|
135
|
+
this.reifyShape();
|
|
136
|
+
return this._shapeKey;
|
|
137
|
+
}
|
|
138
|
+
set shapeKey(key) {
|
|
139
|
+
this._shapeKey = key >>> 0;
|
|
140
|
+
}
|
|
141
|
+
get optional() {
|
|
142
|
+
if (this._optional === void 0) {
|
|
143
|
+
this._optional = ValidatorDef.cloneWith(this, Mask.UNDEFINED);
|
|
144
|
+
}
|
|
145
|
+
return this._optional;
|
|
146
|
+
}
|
|
147
|
+
get nullable() {
|
|
148
|
+
if (this._nullable === void 0) {
|
|
149
|
+
this._nullable = ValidatorDef.cloneWith(this, Mask.NULL);
|
|
150
|
+
}
|
|
151
|
+
return this._nullable;
|
|
152
|
+
}
|
|
153
|
+
get nullish() {
|
|
154
|
+
if (this._nullish === void 0) {
|
|
155
|
+
this._nullish = ValidatorDef.cloneWith(this, Mask.UNDEFINED | Mask.NULL);
|
|
156
|
+
}
|
|
157
|
+
return this._nullish;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Creates a new ValidatorDef that extends this one with additional fields and optional methods.
|
|
161
|
+
* Only valid for ENTITY and OBJECT types.
|
|
162
|
+
* Prevents overriding of existing fields including id and typename.
|
|
163
|
+
*
|
|
164
|
+
* For entities, accepts a function that returns the new fields (lazy reification).
|
|
165
|
+
* For objects, accepts the new fields directly.
|
|
166
|
+
*
|
|
167
|
+
* @param newFieldsOrGetter - New fields to add (lazy function for entities, direct object for objects)
|
|
168
|
+
* @param newMethodsGetter - Optional lazy factory returning new methods to add (entities only, merged with existing)
|
|
169
|
+
*/
|
|
170
|
+
extend(newFieldsOrGetter, newMethodsGetter) {
|
|
171
|
+
if (this.kind !== 5 && this.kind !== 0) {
|
|
172
|
+
throw new Error("extend() can only be called on Entity or Object types");
|
|
173
|
+
}
|
|
174
|
+
if (this.kind === 5) {
|
|
175
|
+
const newFieldsGetter = newFieldsOrGetter;
|
|
176
|
+
const parentMethodsFactory = this._methodsFactory;
|
|
177
|
+
const extendedDef = new ValidatorDef(5, this.mask, () => {
|
|
178
|
+
const existingShape = this.shape;
|
|
179
|
+
const newFields = newFieldsGetter();
|
|
180
|
+
for (const key of Object.keys(newFields)) {
|
|
181
|
+
if (key in existingShape) {
|
|
182
|
+
throw new Error(`Cannot extend: field '${key}' already exists in type definition`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return { ...existingShape, ...newFields };
|
|
186
|
+
});
|
|
187
|
+
if (parentMethodsFactory || newMethodsGetter) {
|
|
188
|
+
extendedDef._methodsFactory = () => {
|
|
189
|
+
const parentMethods = parentMethodsFactory ? parentMethodsFactory() : {};
|
|
190
|
+
const newMethods = newMethodsGetter ? newMethodsGetter() : {};
|
|
191
|
+
return { ...parentMethods, ...newMethods };
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
extendedDef._entityConfig = this._entityConfig;
|
|
195
|
+
return extendedDef;
|
|
196
|
+
} else {
|
|
197
|
+
this.reifyShape();
|
|
198
|
+
const existingShape = this._shape;
|
|
199
|
+
const newFields = newFieldsOrGetter;
|
|
200
|
+
for (const key of Object.keys(newFields)) {
|
|
201
|
+
if (key in existingShape) {
|
|
202
|
+
throw new Error(`Cannot extend: field '${key}' already exists in type definition`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return new ValidatorDef(0, this.mask, { ...existingShape, ...newFields });
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
class CaseInsensitiveSet extends Set {
|
|
210
|
+
lowercaseMap;
|
|
211
|
+
// lowercase -> canonical (strings only)
|
|
212
|
+
constructor(values) {
|
|
213
|
+
super(values);
|
|
214
|
+
this.lowercaseMap = /* @__PURE__ */ new Map();
|
|
215
|
+
for (const value of values) {
|
|
216
|
+
if (typeof value === "string") {
|
|
217
|
+
const lowercase = value.toLowerCase();
|
|
218
|
+
const existing = this.lowercaseMap.get(lowercase);
|
|
219
|
+
if (existing !== void 0) {
|
|
220
|
+
throw new Error(
|
|
221
|
+
`Case-insensitive enum cannot have multiple values with the same lowercase form: '${existing}' and '${value}' both become '${lowercase}'`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
this.lowercaseMap.set(lowercase, value);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Check if a value exists in the set (case-insensitively for strings).
|
|
230
|
+
* Used for backwards compatibility with Set-based checks.
|
|
231
|
+
*/
|
|
232
|
+
has(value) {
|
|
233
|
+
return this.get(value) !== void 0;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Get the canonical value for a given input.
|
|
237
|
+
* For strings, performs case-insensitive lookup and returns the canonical casing.
|
|
238
|
+
* For numbers/booleans, performs exact match.
|
|
239
|
+
* Returns undefined if no match is found.
|
|
240
|
+
*/
|
|
241
|
+
get(value) {
|
|
242
|
+
if (typeof value === "string") {
|
|
243
|
+
return this.lowercaseMap.get(value.toLowerCase());
|
|
244
|
+
}
|
|
245
|
+
if (super.has(value)) {
|
|
246
|
+
return value;
|
|
247
|
+
}
|
|
248
|
+
return void 0;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function defineArray(shape) {
|
|
252
|
+
return new ValidatorDef(3, Mask.ARRAY, shape);
|
|
253
|
+
}
|
|
254
|
+
function defineRecord(shape) {
|
|
255
|
+
return new ValidatorDef(4, Mask.RECORD | Mask.OBJECT, shape);
|
|
256
|
+
}
|
|
257
|
+
function defineParseResult(innerType) {
|
|
258
|
+
return new ValidatorDef(
|
|
259
|
+
6,
|
|
260
|
+
Mask.PARSE_RESULT,
|
|
261
|
+
innerType
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
function defineObject(shape) {
|
|
265
|
+
return new ValidatorDef(0, Mask.OBJECT, shape);
|
|
266
|
+
}
|
|
267
|
+
function defineUnion(...types) {
|
|
268
|
+
let mask = 0;
|
|
269
|
+
let definition;
|
|
270
|
+
let shape;
|
|
271
|
+
let values;
|
|
272
|
+
for (const type of types) {
|
|
273
|
+
if (typeof type === "number") {
|
|
274
|
+
mask |= type;
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (type instanceof Set) {
|
|
278
|
+
if (values === void 0) {
|
|
279
|
+
values = new Set(type);
|
|
280
|
+
} else {
|
|
281
|
+
for (const val of type) {
|
|
282
|
+
values.add(val);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
if (definition === void 0) {
|
|
288
|
+
definition = type;
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
if (shape === void 0) {
|
|
292
|
+
shape = [definition];
|
|
293
|
+
}
|
|
294
|
+
shape.push(type);
|
|
295
|
+
}
|
|
296
|
+
if (definition === void 0) {
|
|
297
|
+
if (values === void 0) {
|
|
298
|
+
return mask;
|
|
299
|
+
}
|
|
300
|
+
if (mask === 0) {
|
|
301
|
+
return values;
|
|
302
|
+
}
|
|
303
|
+
return new ValidatorDef(2, mask | Mask.UNION, void 0, values);
|
|
304
|
+
}
|
|
305
|
+
if (shape === void 0) {
|
|
306
|
+
return ValidatorDef.cloneWith(definition, mask | Mask.UNION, values);
|
|
307
|
+
}
|
|
308
|
+
return new ValidatorDef(1, mask | Mask.UNION, shape, values);
|
|
309
|
+
}
|
|
310
|
+
function defineNullish(type) {
|
|
311
|
+
if (typeof type === "number") {
|
|
312
|
+
return type | Mask.UNDEFINED | Mask.NULL;
|
|
313
|
+
}
|
|
314
|
+
if (type instanceof Set) {
|
|
315
|
+
return defineUnion(type, Mask.UNDEFINED, Mask.NULL);
|
|
316
|
+
}
|
|
317
|
+
return type.nullish;
|
|
318
|
+
}
|
|
319
|
+
function defineOptional(type) {
|
|
320
|
+
if (typeof type === "number") {
|
|
321
|
+
return type | Mask.UNDEFINED;
|
|
322
|
+
}
|
|
323
|
+
if (type instanceof Set) {
|
|
324
|
+
return defineUnion(type, Mask.UNDEFINED);
|
|
325
|
+
}
|
|
326
|
+
return type.optional;
|
|
327
|
+
}
|
|
328
|
+
function defineNullable(type) {
|
|
329
|
+
if (typeof type === "number") {
|
|
330
|
+
return type | Mask.NULL;
|
|
331
|
+
}
|
|
332
|
+
if (type instanceof Set) {
|
|
333
|
+
return defineUnion(type, Mask.NULL);
|
|
334
|
+
}
|
|
335
|
+
return type.nullable;
|
|
336
|
+
}
|
|
337
|
+
function reifyObjectShape(def, shape) {
|
|
338
|
+
let shapeKey = utils.hashValue([def.mask, def.values]);
|
|
339
|
+
for (const [key, value] of entries$2(shape)) {
|
|
340
|
+
switch (typeof value) {
|
|
341
|
+
case "number":
|
|
342
|
+
if ((value & Mask.ID) !== 0) {
|
|
343
|
+
if (def.idField !== void 0) {
|
|
344
|
+
throw new Error(`Duplicate id field: ${key}`);
|
|
345
|
+
}
|
|
346
|
+
def.idField = key;
|
|
347
|
+
}
|
|
348
|
+
shapeKey += utils.hashValue(key) ^ value;
|
|
349
|
+
break;
|
|
350
|
+
case "string":
|
|
351
|
+
if (def.typenameField !== void 0 && def.typenameField !== key) {
|
|
352
|
+
throw new Error(`Duplicate typename field: ${key}`);
|
|
353
|
+
}
|
|
354
|
+
def.typenameField = key;
|
|
355
|
+
def.typenameValue = value;
|
|
356
|
+
shapeKey += utils.hashValue(key) ^ utils.hashValue(value);
|
|
357
|
+
break;
|
|
358
|
+
case "object":
|
|
359
|
+
if (value instanceof CaseInsensitiveSet) {
|
|
360
|
+
shapeKey ^= utils.hashValue(key) ^ utils.hashValue(Array.from(value));
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
if (value instanceof Set) {
|
|
364
|
+
shapeKey ^= utils.hashValue(key) ^ utils.hashValue(value);
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
shapeKey += utils.hashValue(key) ^ value.shapeKey;
|
|
368
|
+
if (value.mask & (Mask.ENTITY | Mask.HAS_SUB_ENTITY)) {
|
|
369
|
+
def.mask |= Mask.HAS_SUB_ENTITY;
|
|
370
|
+
if (def.subEntityPaths === void 0) {
|
|
371
|
+
def.subEntityPaths = key;
|
|
372
|
+
} else if (isArray$2(def.subEntityPaths)) {
|
|
373
|
+
def.subEntityPaths.push(key);
|
|
374
|
+
} else {
|
|
375
|
+
def.subEntityPaths = [def.subEntityPaths, key];
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
def.shapeKey = shapeKey >>> 0;
|
|
382
|
+
return shape;
|
|
383
|
+
}
|
|
384
|
+
function reifyUnionShape(def, defs) {
|
|
385
|
+
let mask = def.mask;
|
|
386
|
+
let shape = /* @__PURE__ */ Object.create(null);
|
|
387
|
+
let unionTypenameField;
|
|
388
|
+
let shapeKey = utils.hashValue([mask, def.values]);
|
|
389
|
+
for (const nestedDef of defs) {
|
|
390
|
+
const nestedMask = nestedDef.mask;
|
|
391
|
+
mask |= nestedMask;
|
|
392
|
+
shapeKey += nestedDef.shapeKey;
|
|
393
|
+
if ((nestedMask & Mask.UNION) !== 0) {
|
|
394
|
+
const nestedUnion = nestedDef;
|
|
395
|
+
if (nestedUnion.typenameField !== void 0) {
|
|
396
|
+
if (unionTypenameField !== void 0 && unionTypenameField !== nestedUnion.typenameField) {
|
|
397
|
+
throw new Error(
|
|
398
|
+
`Union typename field conflict: Cannot merge unions with different typename fields ('${unionTypenameField}' vs '${nestedUnion.typenameField}')`
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
unionTypenameField = nestedUnion.typenameField;
|
|
402
|
+
}
|
|
403
|
+
const nestedShape = nestedUnion.shape;
|
|
404
|
+
if (nestedShape !== void 0) {
|
|
405
|
+
for (const key of [...Object.keys(nestedShape), ARRAY_KEY, RECORD_KEY]) {
|
|
406
|
+
const value = nestedShape[key];
|
|
407
|
+
if (shape[key] !== void 0 && shape[key] !== value) {
|
|
408
|
+
throw new Error(
|
|
409
|
+
`Union merge conflict: Duplicate typename value '${String(key)}' found when merging nested unions (${String(shape[key])} vs ${String(value)})`
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
shape[key] = value;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
} else if ((nestedMask & Mask.ARRAY) !== 0) {
|
|
416
|
+
if (shape[ARRAY_KEY] !== void 0) {
|
|
417
|
+
throw new Error("Array shape already defined");
|
|
418
|
+
}
|
|
419
|
+
shape[ARRAY_KEY] = nestedDef.shape;
|
|
420
|
+
} else if ((nestedMask & Mask.RECORD) !== 0) {
|
|
421
|
+
if (shape[RECORD_KEY] !== void 0) {
|
|
422
|
+
throw new Error("Record shape already defined");
|
|
423
|
+
}
|
|
424
|
+
shape[RECORD_KEY] = nestedDef.shape;
|
|
425
|
+
} else {
|
|
426
|
+
const typenameField = nestedDef.typenameField;
|
|
427
|
+
const typename = nestedDef.typenameValue;
|
|
428
|
+
if (typename === void 0) {
|
|
429
|
+
throw new Error(
|
|
430
|
+
"Object definitions must have a typename to be in a union with other objects, records, or arrays"
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
if (unionTypenameField !== void 0 && typenameField !== unionTypenameField) {
|
|
434
|
+
throw new Error("Object definitions must have the same typename field to be in the same union");
|
|
435
|
+
}
|
|
436
|
+
unionTypenameField = typenameField;
|
|
437
|
+
shape[typename] = nestedDef;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
def.typenameField = unionTypenameField;
|
|
441
|
+
def.shapeKey = shapeKey >>> 0;
|
|
442
|
+
def.mask = mask;
|
|
443
|
+
return shape;
|
|
444
|
+
}
|
|
445
|
+
function defineTypename(value) {
|
|
446
|
+
return value;
|
|
447
|
+
}
|
|
448
|
+
function defineConst(value) {
|
|
449
|
+
return /* @__PURE__ */ new Set([value]);
|
|
450
|
+
}
|
|
451
|
+
const defineEnum = ((...values) => {
|
|
452
|
+
return new Set(values);
|
|
453
|
+
});
|
|
454
|
+
defineEnum.caseInsensitive = (...values) => {
|
|
455
|
+
return new CaseInsensitiveSet(values);
|
|
456
|
+
};
|
|
457
|
+
const FORMAT_MASK_SHIFT = 16;
|
|
458
|
+
let nextFormatId = 0;
|
|
459
|
+
const FORMAT_PARSERS = [];
|
|
460
|
+
const FORMAT_MAP = /* @__PURE__ */ new Map();
|
|
461
|
+
const FORMAT_ID_TO_NAME = /* @__PURE__ */ new Map();
|
|
462
|
+
function defineFormatted(format) {
|
|
463
|
+
const mask = FORMAT_MAP.get(format);
|
|
464
|
+
if (mask === void 0) {
|
|
465
|
+
throw new Error(`Format ${format} not registered`);
|
|
466
|
+
}
|
|
467
|
+
return mask;
|
|
468
|
+
}
|
|
469
|
+
function getFormat(mask) {
|
|
470
|
+
const formatId = mask >> FORMAT_MASK_SHIFT;
|
|
471
|
+
return FORMAT_PARSERS[formatId];
|
|
472
|
+
}
|
|
473
|
+
function getFormatName(mask) {
|
|
474
|
+
const formatId = mask >> FORMAT_MASK_SHIFT;
|
|
475
|
+
return FORMAT_ID_TO_NAME.get(formatId);
|
|
476
|
+
}
|
|
477
|
+
function registerFormat(name, type, parse, serialize) {
|
|
478
|
+
const maskId = nextFormatId++;
|
|
479
|
+
FORMAT_PARSERS[maskId] = parse;
|
|
480
|
+
FORMAT_ID_TO_NAME.set(maskId, name);
|
|
481
|
+
const shiftedId = maskId << FORMAT_MASK_SHIFT;
|
|
482
|
+
const formatMask = type === Mask.STRING ? Mask.HAS_STRING_FORMAT : Mask.HAS_NUMBER_FORMAT;
|
|
483
|
+
const mask = shiftedId | type | formatMask;
|
|
484
|
+
FORMAT_MAP.set(name, mask);
|
|
485
|
+
}
|
|
486
|
+
registerFormat(
|
|
487
|
+
"date",
|
|
488
|
+
Mask.STRING,
|
|
489
|
+
(value) => {
|
|
490
|
+
const match = value.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
491
|
+
if (!match) {
|
|
492
|
+
throw new Error(`Invalid date string: ${value}. Expected YYYY-MM-DD format.`);
|
|
493
|
+
}
|
|
494
|
+
const [, year, month, day] = match;
|
|
495
|
+
const date = new Date(Date.UTC(parseInt(year, 10), parseInt(month, 10) - 1, parseInt(day, 10)));
|
|
496
|
+
if (isNaN(date.getTime())) {
|
|
497
|
+
throw new Error(`Invalid date string: ${value}`);
|
|
498
|
+
}
|
|
499
|
+
return date;
|
|
500
|
+
}
|
|
501
|
+
);
|
|
502
|
+
registerFormat(
|
|
503
|
+
"date-time",
|
|
504
|
+
Mask.STRING,
|
|
505
|
+
(value) => {
|
|
506
|
+
const date = new Date(value);
|
|
507
|
+
if (isNaN(date.getTime())) {
|
|
508
|
+
throw new Error(`Invalid date-time string: ${value}`);
|
|
509
|
+
}
|
|
510
|
+
return date;
|
|
511
|
+
}
|
|
512
|
+
);
|
|
513
|
+
function entity(shape, methods, config) {
|
|
514
|
+
const def = new ValidatorDef(
|
|
515
|
+
5,
|
|
516
|
+
// The mask should be OBJECT | ENTITY so that values match when compared
|
|
517
|
+
Mask.ENTITY | Mask.OBJECT,
|
|
518
|
+
shape
|
|
519
|
+
);
|
|
520
|
+
if (methods) {
|
|
521
|
+
def._methodsFactory = methods;
|
|
522
|
+
}
|
|
523
|
+
if (config) {
|
|
524
|
+
def._entityConfig = config;
|
|
525
|
+
}
|
|
526
|
+
return def;
|
|
527
|
+
}
|
|
528
|
+
const t = {
|
|
529
|
+
format: defineFormatted,
|
|
530
|
+
typename: defineTypename,
|
|
531
|
+
const: defineConst,
|
|
532
|
+
enum: defineEnum,
|
|
533
|
+
id: Mask.ID | Mask.STRING | Mask.NUMBER,
|
|
534
|
+
string: Mask.STRING,
|
|
535
|
+
number: Mask.NUMBER,
|
|
536
|
+
boolean: Mask.BOOLEAN,
|
|
537
|
+
null: Mask.NULL,
|
|
538
|
+
undefined: Mask.UNDEFINED,
|
|
539
|
+
array: defineArray,
|
|
540
|
+
object: defineObject,
|
|
541
|
+
record: defineRecord,
|
|
542
|
+
union: defineUnion,
|
|
543
|
+
nullish: defineNullish,
|
|
544
|
+
optional: defineOptional,
|
|
545
|
+
nullable: defineNullable,
|
|
546
|
+
result: defineParseResult
|
|
547
|
+
};
|
|
548
|
+
function typeToString(type) {
|
|
549
|
+
if (type instanceof CaseInsensitiveSet) {
|
|
550
|
+
const values = Array.from(type).map((v) => typeof v === "string" ? `"${v}"` : String(v));
|
|
551
|
+
return values.join(" | ");
|
|
552
|
+
}
|
|
553
|
+
if (type instanceof Set) {
|
|
554
|
+
const values = Array.from(type).map((v) => typeof v === "string" ? `"${v}"` : String(v));
|
|
555
|
+
return values.join(" | ");
|
|
556
|
+
}
|
|
557
|
+
if (typeof type === "string") {
|
|
558
|
+
return `"${type}"`;
|
|
559
|
+
}
|
|
560
|
+
if (typeof type === "boolean") {
|
|
561
|
+
return String(type);
|
|
562
|
+
}
|
|
563
|
+
if (typeof type === "number") {
|
|
564
|
+
const hasFormat = (type & (Mask.HAS_STRING_FORMAT | Mask.HAS_NUMBER_FORMAT)) !== 0;
|
|
565
|
+
if (hasFormat) {
|
|
566
|
+
const formatName = getFormatName(type);
|
|
567
|
+
if (formatName) {
|
|
568
|
+
return `"${formatName}"`;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
const types = [];
|
|
572
|
+
if (type & Mask.UNDEFINED) types.push("undefined");
|
|
573
|
+
if (type & Mask.NULL) types.push("null");
|
|
574
|
+
if (type & Mask.NUMBER) types.push("number");
|
|
575
|
+
if (type & Mask.STRING) types.push("string");
|
|
576
|
+
if (type & Mask.BOOLEAN) types.push("boolean");
|
|
577
|
+
if (type & Mask.OBJECT) types.push("object");
|
|
578
|
+
if (type & Mask.ARRAY) types.push("array");
|
|
579
|
+
if (types.length === 0) {
|
|
580
|
+
return "unknown";
|
|
581
|
+
}
|
|
582
|
+
return types.length === 1 ? types[0] : types.join(" | ");
|
|
583
|
+
}
|
|
584
|
+
let mask = type.mask;
|
|
585
|
+
if (mask & Mask.UNION) {
|
|
586
|
+
const unionType = type;
|
|
587
|
+
const parts = [];
|
|
588
|
+
if (unionType.values !== void 0 && unionType.values.size > 0) {
|
|
589
|
+
for (const val of unionType.values) {
|
|
590
|
+
const valStr = typeof val === "string" ? `"${val}"` : String(val);
|
|
591
|
+
parts.push(valStr);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
if (unionType.shape !== void 0) {
|
|
595
|
+
if (unionType.shape[ARRAY_KEY] !== void 0) {
|
|
596
|
+
parts.push(`Array<${typeToString(unionType.shape[ARRAY_KEY])}>`);
|
|
597
|
+
}
|
|
598
|
+
if (unionType.shape[RECORD_KEY] !== void 0) {
|
|
599
|
+
parts.push(`Record<string, ${typeToString(unionType.shape[RECORD_KEY])}>`);
|
|
600
|
+
}
|
|
601
|
+
for (const [key, value] of Object.entries(unionType.shape)) {
|
|
602
|
+
if (key !== ARRAY_KEY && key !== RECORD_KEY) {
|
|
603
|
+
parts.push(key);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
mask = unionType.mask;
|
|
608
|
+
const hasFormat = (mask & (Mask.HAS_STRING_FORMAT | Mask.HAS_NUMBER_FORMAT)) !== 0;
|
|
609
|
+
if (hasFormat) {
|
|
610
|
+
const formatName = getFormatName(mask);
|
|
611
|
+
if (formatName) {
|
|
612
|
+
parts.push(`"${formatName}"`);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
if (mask & Mask.UNDEFINED) parts.push("undefined");
|
|
616
|
+
if (mask & Mask.NULL) parts.push("null");
|
|
617
|
+
if (mask & Mask.NUMBER) parts.push("number");
|
|
618
|
+
if (mask & Mask.STRING) parts.push("string");
|
|
619
|
+
if (mask & Mask.BOOLEAN) parts.push("boolean");
|
|
620
|
+
if (parts.length === 0) {
|
|
621
|
+
return "union";
|
|
622
|
+
}
|
|
623
|
+
return parts.join(" | ");
|
|
624
|
+
}
|
|
625
|
+
if (mask & Mask.ENTITY) {
|
|
626
|
+
return `Entity<${type.typenameValue}>`;
|
|
627
|
+
}
|
|
628
|
+
if (mask & Mask.ARRAY) {
|
|
629
|
+
const shape = type.shape;
|
|
630
|
+
return `Array<${typeToString(shape)}>`;
|
|
631
|
+
}
|
|
632
|
+
if (mask & Mask.RECORD) {
|
|
633
|
+
const shape = type.shape;
|
|
634
|
+
return `Record<string, ${typeToString(shape)}>`;
|
|
635
|
+
}
|
|
636
|
+
if (mask & Mask.OBJECT) {
|
|
637
|
+
const typename = type.typenameValue;
|
|
638
|
+
return typename ? `Object<${typename}>` : "object";
|
|
639
|
+
}
|
|
640
|
+
return "unknown";
|
|
641
|
+
}
|
|
642
|
+
function typeError(path, expectedType, value) {
|
|
643
|
+
return new TypeError(
|
|
644
|
+
`Validation error at ${path}: expected ${typeToString(expectedType)}, got ${typeof value === "object" ? value === null ? "null" : Array.isArray(value) ? "array" : "object" : typeof value}`
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
const isArray$1 = Array.isArray;
|
|
648
|
+
function typeMaskOf(value) {
|
|
649
|
+
if (value === null) return Mask.NULL;
|
|
650
|
+
switch (typeof value) {
|
|
651
|
+
case "number":
|
|
652
|
+
return Mask.NUMBER;
|
|
653
|
+
case "string":
|
|
654
|
+
return Mask.STRING;
|
|
655
|
+
case "boolean":
|
|
656
|
+
return Mask.BOOLEAN;
|
|
657
|
+
case "undefined":
|
|
658
|
+
return Mask.UNDEFINED;
|
|
659
|
+
case "object":
|
|
660
|
+
return isArray$1(value) ? Mask.ARRAY : Mask.OBJECT;
|
|
661
|
+
default:
|
|
662
|
+
throw new Error(`Invalid type: ${typeof value}`);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
function draft(entity2) {
|
|
666
|
+
return deepClone$1(entity2);
|
|
667
|
+
}
|
|
668
|
+
function deepClone$1(value) {
|
|
669
|
+
if (value === null || typeof value !== "object") {
|
|
670
|
+
return value;
|
|
671
|
+
}
|
|
672
|
+
if (isArray$1(value)) {
|
|
673
|
+
return value.map((item) => deepClone$1(item));
|
|
674
|
+
}
|
|
675
|
+
if (value instanceof Date) {
|
|
676
|
+
return new Date(value.getTime());
|
|
677
|
+
}
|
|
678
|
+
if (value instanceof Map) {
|
|
679
|
+
const clonedMap = /* @__PURE__ */ new Map();
|
|
680
|
+
for (const [k, v] of value) {
|
|
681
|
+
clonedMap.set(deepClone$1(k), deepClone$1(v));
|
|
682
|
+
}
|
|
683
|
+
return clonedMap;
|
|
684
|
+
}
|
|
685
|
+
if (value instanceof Set) {
|
|
686
|
+
const clonedSet = /* @__PURE__ */ new Set();
|
|
687
|
+
for (const v of value) {
|
|
688
|
+
clonedSet.add(deepClone$1(v));
|
|
689
|
+
}
|
|
690
|
+
return clonedSet;
|
|
691
|
+
}
|
|
692
|
+
const result = {};
|
|
693
|
+
for (const key of Object.keys(value)) {
|
|
694
|
+
result[key] = deepClone$1(value[key]);
|
|
695
|
+
}
|
|
696
|
+
return result;
|
|
697
|
+
}
|
|
698
|
+
const noopWarn = () => {
|
|
699
|
+
};
|
|
700
|
+
const entries$1 = Object.entries;
|
|
701
|
+
const isArray = Array.isArray;
|
|
702
|
+
const PROXY_ID = /* @__PURE__ */ new WeakMap();
|
|
703
|
+
function parseUnionValue(valueType, value, unionDef, path, warn = noopWarn) {
|
|
704
|
+
if (valueType === Mask.ARRAY) {
|
|
705
|
+
const shape = unionDef.shape[ARRAY_KEY];
|
|
706
|
+
if (shape === void 0 || typeof shape === "number") {
|
|
707
|
+
return value;
|
|
708
|
+
}
|
|
709
|
+
return parseArrayValue(value, shape, path, warn);
|
|
710
|
+
} else {
|
|
711
|
+
const typenameField = unionDef.typenameField;
|
|
712
|
+
const typename = typenameField ? value[typenameField] : void 0;
|
|
713
|
+
if (typename === void 0 || typeof typename !== "string") {
|
|
714
|
+
const recordShape = unionDef.shape[RECORD_KEY];
|
|
715
|
+
if (recordShape === void 0 || typeof recordShape === "number") {
|
|
716
|
+
throw new Error(
|
|
717
|
+
`Typename field '${typenameField}' is required for union discrimination but was not found in the data`
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
return parseRecordValue(value, recordShape, path, warn);
|
|
721
|
+
}
|
|
722
|
+
const matchingDef = unionDef.shape[typename];
|
|
723
|
+
if (matchingDef === void 0 || typeof matchingDef === "number") {
|
|
724
|
+
throw new Error(`Unknown typename '${typename}' in union`);
|
|
725
|
+
}
|
|
726
|
+
return parseObjectValue(value, matchingDef, path, warn);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
function parseArrayValue(array, arrayShape, path, warn = noopWarn) {
|
|
730
|
+
const result = [];
|
|
731
|
+
for (let i = 0; i < array.length; i++) {
|
|
732
|
+
try {
|
|
733
|
+
result.push(parseValue(array[i], arrayShape, `${path}[${i}]`, false, warn));
|
|
734
|
+
} catch (e) {
|
|
735
|
+
warn("Failed to parse array item, filtering out", {
|
|
736
|
+
index: i,
|
|
737
|
+
value: array[i],
|
|
738
|
+
error: e instanceof Error ? e.message : String(e)
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
return result;
|
|
743
|
+
}
|
|
744
|
+
function parseRecordValue(record, recordShape, path, warn = noopWarn) {
|
|
745
|
+
for (const [key, value] of entries$1(record)) {
|
|
746
|
+
record[key] = parseValue(value, recordShape, `${path}["${key}"]`, false, warn);
|
|
747
|
+
}
|
|
748
|
+
return record;
|
|
749
|
+
}
|
|
750
|
+
function parseObjectValue(object, objectShape, path, warn = noopWarn) {
|
|
751
|
+
if (PROXY_ID.has(object)) {
|
|
752
|
+
return object;
|
|
753
|
+
}
|
|
754
|
+
const shape = objectShape.shape;
|
|
755
|
+
for (const [key, propShape] of entries$1(shape)) {
|
|
756
|
+
object[key] = parseValue(object[key], propShape, `${path}.${key}`, false, warn);
|
|
757
|
+
}
|
|
758
|
+
return object;
|
|
759
|
+
}
|
|
760
|
+
function parseValue(value, propDef, path, skipFallbacks = false, warn = noopWarn) {
|
|
761
|
+
if (propDef instanceof CaseInsensitiveSet) {
|
|
762
|
+
const canonical = propDef.get(value);
|
|
763
|
+
if (canonical === void 0) {
|
|
764
|
+
throw typeError(path, propDef, value);
|
|
765
|
+
}
|
|
766
|
+
return canonical;
|
|
767
|
+
}
|
|
768
|
+
if (propDef instanceof Set) {
|
|
769
|
+
if (!propDef.has(value)) {
|
|
770
|
+
throw typeError(path, propDef, value);
|
|
771
|
+
}
|
|
772
|
+
return value;
|
|
773
|
+
}
|
|
774
|
+
switch (typeof propDef) {
|
|
775
|
+
case "string":
|
|
776
|
+
if (value === void 0 || value === null) {
|
|
777
|
+
return propDef;
|
|
778
|
+
}
|
|
779
|
+
if (value !== propDef) {
|
|
780
|
+
throw typeError(path, propDef, value);
|
|
781
|
+
}
|
|
782
|
+
return value;
|
|
783
|
+
// handle primitives
|
|
784
|
+
case "number": {
|
|
785
|
+
let valueType = typeMaskOf(value);
|
|
786
|
+
if ((propDef & valueType) === 0) {
|
|
787
|
+
if (!skipFallbacks && (propDef & Mask.UNDEFINED) !== 0) {
|
|
788
|
+
warn("Invalid value for optional type, defaulting to undefined", { value, path });
|
|
789
|
+
return void 0;
|
|
790
|
+
}
|
|
791
|
+
throw typeError(path, propDef, value);
|
|
792
|
+
}
|
|
793
|
+
if ((propDef & (Mask.HAS_STRING_FORMAT | Mask.HAS_NUMBER_FORMAT)) !== 0) {
|
|
794
|
+
try {
|
|
795
|
+
return getFormat(propDef)(value);
|
|
796
|
+
} catch (e) {
|
|
797
|
+
if (!skipFallbacks && (propDef & Mask.UNDEFINED) !== 0) {
|
|
798
|
+
warn("Invalid formatted value for optional type, defaulting to undefined", {
|
|
799
|
+
value,
|
|
800
|
+
path,
|
|
801
|
+
error: e instanceof Error ? e.message : String(e)
|
|
802
|
+
});
|
|
803
|
+
return void 0;
|
|
804
|
+
}
|
|
805
|
+
throw e;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
return value;
|
|
809
|
+
}
|
|
810
|
+
// handle complex objects
|
|
811
|
+
default: {
|
|
812
|
+
let valueType = typeMaskOf(value);
|
|
813
|
+
const propMask = propDef.mask;
|
|
814
|
+
if ((propMask & Mask.PARSE_RESULT) !== 0) {
|
|
815
|
+
try {
|
|
816
|
+
const innerResult = parseValue(value, propDef.shape, path, true, warn);
|
|
817
|
+
return { success: true, value: innerResult };
|
|
818
|
+
} catch (e) {
|
|
819
|
+
return { success: false, error: e instanceof Error ? e : new Error(String(e)) };
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
if ((propMask & valueType) === 0 && !propDef.values?.has(value)) {
|
|
823
|
+
if (!skipFallbacks && (propMask & Mask.UNDEFINED) !== 0) {
|
|
824
|
+
warn("Invalid value for optional type, defaulting to undefined", { value, path });
|
|
825
|
+
return void 0;
|
|
826
|
+
}
|
|
827
|
+
throw typeError(path, propMask, value);
|
|
828
|
+
}
|
|
829
|
+
if (valueType < Mask.OBJECT) {
|
|
830
|
+
if ((propMask & (Mask.HAS_STRING_FORMAT | Mask.HAS_NUMBER_FORMAT)) !== 0) {
|
|
831
|
+
try {
|
|
832
|
+
return getFormat(propMask)(value);
|
|
833
|
+
} catch (e) {
|
|
834
|
+
if (!skipFallbacks && (propMask & Mask.UNDEFINED) !== 0) {
|
|
835
|
+
warn("Invalid formatted value for optional type, defaulting to undefined", {
|
|
836
|
+
value,
|
|
837
|
+
path,
|
|
838
|
+
error: e instanceof Error ? e.message : String(e)
|
|
839
|
+
});
|
|
840
|
+
return void 0;
|
|
841
|
+
}
|
|
842
|
+
throw e;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
return value;
|
|
846
|
+
}
|
|
847
|
+
if ((valueType & Mask.UNION) !== 0) {
|
|
848
|
+
return parseUnionValue(
|
|
849
|
+
valueType,
|
|
850
|
+
value,
|
|
851
|
+
propDef,
|
|
852
|
+
path,
|
|
853
|
+
warn
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
if (valueType === Mask.ARRAY) {
|
|
857
|
+
return parseArrayValue(value, propDef.shape, path, warn);
|
|
858
|
+
}
|
|
859
|
+
return parseObjectValue(value, propDef, path, warn);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
function mergeValues(target, update) {
|
|
864
|
+
for (const [key, value] of entries$1(update)) {
|
|
865
|
+
const targetValue = target[key];
|
|
866
|
+
if (typeof value === "object" && value !== null && !isArray(value) && !PROXY_ID.has(value) && typeof targetValue === "object" && targetValue !== null && !isArray(targetValue) && !PROXY_ID.has(targetValue)) {
|
|
867
|
+
mergeValues(targetValue, value);
|
|
868
|
+
} else {
|
|
869
|
+
target[key] = value;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
return target;
|
|
873
|
+
}
|
|
874
|
+
const CustomNodeInspect = Symbol.for("nodejs.util.inspect.custom");
|
|
875
|
+
function createEntityProxy(id, entityRecord, def, entityRelay, scopeOwner, warn = noopWarn, desc) {
|
|
876
|
+
const shape = def.shape;
|
|
877
|
+
const methods = def.methods;
|
|
878
|
+
const wrappedMethods = /* @__PURE__ */ new Map();
|
|
879
|
+
const toJSON = () => ({
|
|
880
|
+
__entityRef: id
|
|
881
|
+
});
|
|
882
|
+
let proxy;
|
|
883
|
+
const handler = {
|
|
884
|
+
get(target, prop) {
|
|
885
|
+
if (prop === "toJSON") {
|
|
886
|
+
return toJSON;
|
|
887
|
+
}
|
|
888
|
+
const { data, cache, notifier } = entityRecord;
|
|
889
|
+
entityRelay?.value;
|
|
890
|
+
notifier.consume();
|
|
891
|
+
if (cache.has(prop)) {
|
|
892
|
+
return cache.get(prop);
|
|
893
|
+
}
|
|
894
|
+
if (methods && typeof prop === "string" && prop in methods) {
|
|
895
|
+
let wrapped = wrappedMethods.get(prop);
|
|
896
|
+
if (!wrapped) {
|
|
897
|
+
wrapped = signalium.reactiveMethod(proxy, methods[prop].bind(proxy));
|
|
898
|
+
wrappedMethods.set(prop, wrapped);
|
|
899
|
+
}
|
|
900
|
+
return wrapped;
|
|
901
|
+
}
|
|
902
|
+
const value = data[prop];
|
|
903
|
+
const propDef = shape[prop];
|
|
904
|
+
if (!Object.hasOwnProperty.call(shape, prop)) {
|
|
905
|
+
return value;
|
|
906
|
+
}
|
|
907
|
+
const parsed = parseValue(value, propDef, `[[${desc}]].${prop}`, false, warn);
|
|
908
|
+
cache.set(prop, parsed);
|
|
909
|
+
return parsed;
|
|
910
|
+
},
|
|
911
|
+
has(target, prop) {
|
|
912
|
+
if (methods && typeof prop === "string" && prop in methods) {
|
|
913
|
+
return true;
|
|
914
|
+
}
|
|
915
|
+
return prop in shape;
|
|
916
|
+
},
|
|
917
|
+
ownKeys(target) {
|
|
918
|
+
const keys = Object.keys(shape);
|
|
919
|
+
const typenameField = def.typenameField;
|
|
920
|
+
if (typenameField && !keys.includes(typenameField)) {
|
|
921
|
+
keys.push(typenameField);
|
|
922
|
+
}
|
|
923
|
+
if (methods) {
|
|
924
|
+
for (const methodKey of Object.keys(methods)) {
|
|
925
|
+
if (!keys.includes(methodKey)) {
|
|
926
|
+
keys.push(methodKey);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
return keys;
|
|
931
|
+
},
|
|
932
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
933
|
+
const typenameField = def.typenameField;
|
|
934
|
+
if (prop in shape || prop === typenameField) {
|
|
935
|
+
return {
|
|
936
|
+
enumerable: true,
|
|
937
|
+
configurable: true
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
if (methods && typeof prop === "string" && prop in methods) {
|
|
941
|
+
return {
|
|
942
|
+
enumerable: false,
|
|
943
|
+
configurable: true
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
return void 0;
|
|
947
|
+
}
|
|
948
|
+
};
|
|
949
|
+
proxy = new Proxy(
|
|
950
|
+
{
|
|
951
|
+
[CustomNodeInspect]: () => {
|
|
952
|
+
return Object.keys(shape).reduce(
|
|
953
|
+
(acc, key) => {
|
|
954
|
+
acc[key] = proxy[key];
|
|
955
|
+
return acc;
|
|
956
|
+
},
|
|
957
|
+
{}
|
|
958
|
+
);
|
|
959
|
+
}
|
|
960
|
+
},
|
|
961
|
+
handler
|
|
962
|
+
);
|
|
963
|
+
PROXY_ID.set(proxy, id);
|
|
964
|
+
signalium.setScopeOwner(proxy, scopeOwner);
|
|
965
|
+
return proxy;
|
|
966
|
+
}
|
|
967
|
+
function getProxyId(object) {
|
|
968
|
+
return PROXY_ID.get(object);
|
|
969
|
+
}
|
|
970
|
+
function deepClone(value) {
|
|
971
|
+
if (value === null || typeof value !== "object") {
|
|
972
|
+
return value;
|
|
973
|
+
}
|
|
974
|
+
if (Array.isArray(value)) {
|
|
975
|
+
return value.map((item) => deepClone(item));
|
|
976
|
+
}
|
|
977
|
+
if (value instanceof Date) {
|
|
978
|
+
return new Date(value.getTime());
|
|
979
|
+
}
|
|
980
|
+
const cloned = {};
|
|
981
|
+
for (const key of Object.keys(value)) {
|
|
982
|
+
cloned[key] = deepClone(value[key]);
|
|
983
|
+
}
|
|
984
|
+
return cloned;
|
|
985
|
+
}
|
|
986
|
+
class EntityStore {
|
|
987
|
+
map = /* @__PURE__ */ new Map();
|
|
988
|
+
queryClient;
|
|
989
|
+
/**
|
|
990
|
+
* Tracks pending optimistic updates by entity key.
|
|
991
|
+
* Each entity can only have one pending update at a time (throws if concurrent).
|
|
992
|
+
*/
|
|
993
|
+
pendingOptimisticUpdates = /* @__PURE__ */ new Map();
|
|
994
|
+
constructor(queryClient) {
|
|
995
|
+
this.queryClient = queryClient;
|
|
996
|
+
}
|
|
997
|
+
hasEntity(key) {
|
|
998
|
+
return this.map.has(key);
|
|
999
|
+
}
|
|
1000
|
+
getEntity(key) {
|
|
1001
|
+
return this.map.get(key);
|
|
1002
|
+
}
|
|
1003
|
+
getNestedEntityRefIds(key, refIds) {
|
|
1004
|
+
const record = this.getEntity(key);
|
|
1005
|
+
if (record === void 0) {
|
|
1006
|
+
throw new Error(`Entity ${key} not found when getting nested entity ref ids`);
|
|
1007
|
+
}
|
|
1008
|
+
refIds.add(key);
|
|
1009
|
+
record.notifier.consume();
|
|
1010
|
+
if (record.entityRefs !== void 0) {
|
|
1011
|
+
for (const ref of record.entityRefs) {
|
|
1012
|
+
this.getNestedEntityRefIds(ref, refIds);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
return refIds;
|
|
1016
|
+
}
|
|
1017
|
+
hydratePreloadedEntity(key, shape) {
|
|
1018
|
+
const record = this.getEntity(key);
|
|
1019
|
+
if (record === void 0) {
|
|
1020
|
+
throw new Error(`Entity ${key} not found`);
|
|
1021
|
+
}
|
|
1022
|
+
record.proxy = this.createEntityProxy(record, shape);
|
|
1023
|
+
return record;
|
|
1024
|
+
}
|
|
1025
|
+
setPreloadedEntity(key, data) {
|
|
1026
|
+
const record = {
|
|
1027
|
+
key,
|
|
1028
|
+
data,
|
|
1029
|
+
notifier: signalium.notifier(),
|
|
1030
|
+
cache: /* @__PURE__ */ new Map(),
|
|
1031
|
+
id: void 0,
|
|
1032
|
+
proxy: void 0,
|
|
1033
|
+
entityRefs: void 0
|
|
1034
|
+
};
|
|
1035
|
+
this.map.set(key, record);
|
|
1036
|
+
return record;
|
|
1037
|
+
}
|
|
1038
|
+
setEntity(key, obj, shape, entityRefs) {
|
|
1039
|
+
let record = this.map.get(key);
|
|
1040
|
+
if (record === void 0) {
|
|
1041
|
+
record = this.setPreloadedEntity(key, obj);
|
|
1042
|
+
record.proxy = this.createEntityProxy(record, shape);
|
|
1043
|
+
} else {
|
|
1044
|
+
record.data = mergeValues(record.data, obj);
|
|
1045
|
+
record.notifier.notify();
|
|
1046
|
+
record.cache.clear();
|
|
1047
|
+
}
|
|
1048
|
+
record.entityRefs = entityRefs;
|
|
1049
|
+
return record;
|
|
1050
|
+
}
|
|
1051
|
+
// ======================================================
|
|
1052
|
+
// Optimistic Update Tracking
|
|
1053
|
+
// ======================================================
|
|
1054
|
+
/**
|
|
1055
|
+
* Register a pending optimistic update for an entity.
|
|
1056
|
+
* Snapshots the current state and applies the optimistic update.
|
|
1057
|
+
*
|
|
1058
|
+
* @throws Error if the entity already has a pending optimistic update
|
|
1059
|
+
*/
|
|
1060
|
+
registerOptimisticUpdate(entityKey, fields) {
|
|
1061
|
+
if (this.pendingOptimisticUpdates.has(entityKey)) {
|
|
1062
|
+
throw new Error(
|
|
1063
|
+
`Cannot apply optimistic update: entity ${entityKey} already has a pending optimistic update from another mutation.`
|
|
1064
|
+
);
|
|
1065
|
+
}
|
|
1066
|
+
const record = this.map.get(entityKey);
|
|
1067
|
+
if (!record) {
|
|
1068
|
+
this.pendingOptimisticUpdates.set(entityKey, { snapshot: {} });
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
const snapshot = deepClone(record.data);
|
|
1072
|
+
this.pendingOptimisticUpdates.set(entityKey, { snapshot });
|
|
1073
|
+
record.data = mergeValues(record.data, fields);
|
|
1074
|
+
record.cache.clear();
|
|
1075
|
+
queueMicrotask(() => {
|
|
1076
|
+
record.notifier.notify();
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* Revert the optimistic update for an entity, restoring its snapshot.
|
|
1081
|
+
* Called when a mutation fails.
|
|
1082
|
+
*/
|
|
1083
|
+
revertOptimisticUpdate(entityKey) {
|
|
1084
|
+
const pending = this.pendingOptimisticUpdates.get(entityKey);
|
|
1085
|
+
if (!pending) {
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
const record = this.map.get(entityKey);
|
|
1089
|
+
if (record && Object.keys(pending.snapshot).length > 0) {
|
|
1090
|
+
record.data = pending.snapshot;
|
|
1091
|
+
record.cache.clear();
|
|
1092
|
+
queueMicrotask(() => {
|
|
1093
|
+
record.notifier.notify();
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
this.pendingOptimisticUpdates.delete(entityKey);
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Clear the optimistic update for an entity without reverting.
|
|
1100
|
+
* Called when a mutation succeeds (the optimistic update is now confirmed).
|
|
1101
|
+
*/
|
|
1102
|
+
clearOptimisticUpdates(entityKey) {
|
|
1103
|
+
this.pendingOptimisticUpdates.delete(entityKey);
|
|
1104
|
+
}
|
|
1105
|
+
createEntityProxy(record, shape) {
|
|
1106
|
+
const idField = shape.idField;
|
|
1107
|
+
if (idField === void 0) {
|
|
1108
|
+
throw new Error(`Entity id field is required ${shape.typenameValue}`);
|
|
1109
|
+
}
|
|
1110
|
+
const id = record.data[idField];
|
|
1111
|
+
if (typeof id !== "string" && typeof id !== "number") {
|
|
1112
|
+
console.log(record.data);
|
|
1113
|
+
throw new Error(`Entity id must be string or number: ${shape.typenameValue}`);
|
|
1114
|
+
}
|
|
1115
|
+
record.id = id;
|
|
1116
|
+
let entityRelay;
|
|
1117
|
+
const entityConfig = shape._entityConfig;
|
|
1118
|
+
if (entityConfig?.stream) {
|
|
1119
|
+
entityRelay = signalium.relay((state) => {
|
|
1120
|
+
const context = this.queryClient.getContext();
|
|
1121
|
+
const onUpdate = (update) => {
|
|
1122
|
+
const currentValue = record.data;
|
|
1123
|
+
const merged = mergeValues(currentValue, update);
|
|
1124
|
+
record.data = merged;
|
|
1125
|
+
record.notifier.notify();
|
|
1126
|
+
record.cache.clear();
|
|
1127
|
+
};
|
|
1128
|
+
const unsubscribe = entityConfig.stream.subscribe(context, id, onUpdate);
|
|
1129
|
+
state.value = record.proxy;
|
|
1130
|
+
return unsubscribe;
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
const warn = this.queryClient.getContext().log?.warn;
|
|
1134
|
+
return createEntityProxy(record.key, record, shape, entityRelay, this.queryClient, warn);
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
class NetworkManager {
|
|
1138
|
+
onlineSignal;
|
|
1139
|
+
manualOverride = void 0;
|
|
1140
|
+
eventListenersAttached = false;
|
|
1141
|
+
constructor(initialStatus) {
|
|
1142
|
+
const initialOnlineStatus = initialStatus ?? this.detectOnlineStatus();
|
|
1143
|
+
this.onlineSignal = signalium.signal(initialOnlineStatus);
|
|
1144
|
+
if (this.canAttachListeners()) {
|
|
1145
|
+
this.attachEventListeners();
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
/**
|
|
1149
|
+
* Returns true if the network is currently online
|
|
1150
|
+
*/
|
|
1151
|
+
get isOnline() {
|
|
1152
|
+
if (this.manualOverride !== void 0) {
|
|
1153
|
+
return this.manualOverride;
|
|
1154
|
+
}
|
|
1155
|
+
return this.onlineSignal.value;
|
|
1156
|
+
}
|
|
1157
|
+
/**
|
|
1158
|
+
* Manually set the network status (useful for testing)
|
|
1159
|
+
*/
|
|
1160
|
+
setNetworkStatus(online) {
|
|
1161
|
+
this.manualOverride = online;
|
|
1162
|
+
this.onlineSignal.value = online;
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Clear manual override and return to automatic detection
|
|
1166
|
+
*/
|
|
1167
|
+
clearManualOverride() {
|
|
1168
|
+
this.manualOverride = void 0;
|
|
1169
|
+
this.onlineSignal.value = this.detectOnlineStatus();
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Get the reactive signal for online status
|
|
1173
|
+
* This allows reactive functions to depend on network status
|
|
1174
|
+
*/
|
|
1175
|
+
getOnlineSignal() {
|
|
1176
|
+
return this.onlineSignal;
|
|
1177
|
+
}
|
|
1178
|
+
/**
|
|
1179
|
+
* Detect current online status from the environment
|
|
1180
|
+
*/
|
|
1181
|
+
detectOnlineStatus() {
|
|
1182
|
+
if (typeof navigator !== "undefined" && "onLine" in navigator) {
|
|
1183
|
+
return navigator.onLine;
|
|
1184
|
+
}
|
|
1185
|
+
return true;
|
|
1186
|
+
}
|
|
1187
|
+
/**
|
|
1188
|
+
* Check if we can attach event listeners (browser or React Native)
|
|
1189
|
+
*/
|
|
1190
|
+
canAttachListeners() {
|
|
1191
|
+
return typeof window !== "undefined" && typeof window.addEventListener === "function";
|
|
1192
|
+
}
|
|
1193
|
+
/**
|
|
1194
|
+
* Attach event listeners for online/offline events
|
|
1195
|
+
*/
|
|
1196
|
+
attachEventListeners() {
|
|
1197
|
+
if (this.eventListenersAttached) {
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
const handleOnline = () => {
|
|
1201
|
+
if (this.manualOverride === void 0) {
|
|
1202
|
+
this.onlineSignal.value = true;
|
|
1203
|
+
}
|
|
1204
|
+
};
|
|
1205
|
+
const handleOffline = () => {
|
|
1206
|
+
if (this.manualOverride === void 0) {
|
|
1207
|
+
this.onlineSignal.value = false;
|
|
1208
|
+
}
|
|
1209
|
+
};
|
|
1210
|
+
window.addEventListener("online", handleOnline);
|
|
1211
|
+
window.addEventListener("offline", handleOffline);
|
|
1212
|
+
this.eventListenersAttached = true;
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
class NoOpNetworkManager {
|
|
1216
|
+
static onlineSignal = signalium.signal(true);
|
|
1217
|
+
get isOnline() {
|
|
1218
|
+
return true;
|
|
1219
|
+
}
|
|
1220
|
+
setNetworkStatus(_online) {
|
|
1221
|
+
}
|
|
1222
|
+
clearManualOverride() {
|
|
1223
|
+
}
|
|
1224
|
+
getOnlineSignal() {
|
|
1225
|
+
return NoOpNetworkManager.onlineSignal;
|
|
1226
|
+
}
|
|
1227
|
+
destroy() {
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
const defaultNetworkManager = new NetworkManager();
|
|
1231
|
+
const NetworkManagerContext = signalium.context(defaultNetworkManager);
|
|
1232
|
+
const entries = Object.entries;
|
|
1233
|
+
function parseUnionEntities(valueType, value, unionDef, queryClient, entityRefs) {
|
|
1234
|
+
if (valueType === Mask.ARRAY) {
|
|
1235
|
+
const shape = unionDef.shape[ARRAY_KEY];
|
|
1236
|
+
if (shape === void 0 || typeof shape === "number") {
|
|
1237
|
+
return value;
|
|
1238
|
+
}
|
|
1239
|
+
return parseArrayEntities(
|
|
1240
|
+
value,
|
|
1241
|
+
{ mask: Mask.ARRAY, shape, values: void 0 },
|
|
1242
|
+
queryClient,
|
|
1243
|
+
entityRefs
|
|
1244
|
+
);
|
|
1245
|
+
} else {
|
|
1246
|
+
const typenameField = unionDef.typenameField;
|
|
1247
|
+
const typename = typenameField ? value[typenameField] : void 0;
|
|
1248
|
+
if (typename === void 0 || typeof typename !== "string") {
|
|
1249
|
+
const recordShape = unionDef.shape[RECORD_KEY];
|
|
1250
|
+
if (recordShape === void 0 || typeof recordShape === "number") {
|
|
1251
|
+
throw new Error(
|
|
1252
|
+
`Typename field '${typenameField}' is required for union discrimination but was not found in the data`
|
|
1253
|
+
);
|
|
1254
|
+
}
|
|
1255
|
+
return parseRecordEntities(
|
|
1256
|
+
value,
|
|
1257
|
+
recordShape,
|
|
1258
|
+
queryClient,
|
|
1259
|
+
entityRefs
|
|
1260
|
+
);
|
|
1261
|
+
}
|
|
1262
|
+
const matchingDef = unionDef.shape[typename];
|
|
1263
|
+
if (matchingDef === void 0 || typeof matchingDef === "number") {
|
|
1264
|
+
throw new Error(`Unknown typename '${typename}' in union`);
|
|
1265
|
+
}
|
|
1266
|
+
return parseObjectEntities(
|
|
1267
|
+
value,
|
|
1268
|
+
matchingDef,
|
|
1269
|
+
queryClient,
|
|
1270
|
+
entityRefs
|
|
1271
|
+
);
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
function parseArrayEntities(array, arrayShape, queryClient, entityRefs) {
|
|
1275
|
+
const result = [];
|
|
1276
|
+
for (let i = 0; i < array.length; i++) {
|
|
1277
|
+
try {
|
|
1278
|
+
result.push(parseEntities(array[i], arrayShape, queryClient, entityRefs));
|
|
1279
|
+
} catch (e) {
|
|
1280
|
+
queryClient.getContext().log?.warn?.("Failed to parse array item, filtering out", {
|
|
1281
|
+
index: i,
|
|
1282
|
+
value: array[i],
|
|
1283
|
+
error: e instanceof Error ? e.message : String(e)
|
|
1284
|
+
});
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
return result;
|
|
1288
|
+
}
|
|
1289
|
+
function parseRecordEntities(record, recordShape, queryClient, entityRefs) {
|
|
1290
|
+
if (typeof recordShape === "number") {
|
|
1291
|
+
return record;
|
|
1292
|
+
}
|
|
1293
|
+
for (const [key, value] of entries(record)) {
|
|
1294
|
+
record[key] = parseEntities(value, recordShape, queryClient, entityRefs);
|
|
1295
|
+
}
|
|
1296
|
+
return record;
|
|
1297
|
+
}
|
|
1298
|
+
function parseObjectEntities(obj, objectShape, queryClient, entityRefs) {
|
|
1299
|
+
const entityRefId = obj.__entityRef;
|
|
1300
|
+
if (typeof entityRefId === "number") {
|
|
1301
|
+
return queryClient.hydrateEntity(entityRefId, objectShape).proxy;
|
|
1302
|
+
}
|
|
1303
|
+
const { mask } = objectShape;
|
|
1304
|
+
const childRefs = mask & Mask.ENTITY ? /* @__PURE__ */ new Set() : entityRefs;
|
|
1305
|
+
const shape = objectShape.shape;
|
|
1306
|
+
const subEntityPaths = objectShape.subEntityPaths;
|
|
1307
|
+
if (subEntityPaths !== void 0) {
|
|
1308
|
+
if (typeof subEntityPaths === "string") {
|
|
1309
|
+
const propDef = shape[subEntityPaths];
|
|
1310
|
+
obj[subEntityPaths] = parseEntities(obj[subEntityPaths], propDef, queryClient, childRefs);
|
|
1311
|
+
} else {
|
|
1312
|
+
for (const path of subEntityPaths) {
|
|
1313
|
+
const propDef = shape[path];
|
|
1314
|
+
obj[path] = parseEntities(obj[path], propDef, queryClient, childRefs);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
if (mask & Mask.ENTITY) {
|
|
1319
|
+
const entityDef = objectShape;
|
|
1320
|
+
const typename = entityDef.typenameValue;
|
|
1321
|
+
const id = obj[entityDef.idField];
|
|
1322
|
+
if (id === void 0) {
|
|
1323
|
+
throw new Error(`Entity id is required: ${typename}`);
|
|
1324
|
+
}
|
|
1325
|
+
const desc = `${typename}:${id}`;
|
|
1326
|
+
const key = utils.hashValue(desc);
|
|
1327
|
+
if (entityRefs !== void 0) {
|
|
1328
|
+
entityRefs.add(key);
|
|
1329
|
+
}
|
|
1330
|
+
return queryClient.saveEntity(key, obj, entityDef, childRefs).proxy;
|
|
1331
|
+
}
|
|
1332
|
+
const warn = queryClient.getContext().log?.warn;
|
|
1333
|
+
for (const [key, propDef] of entries(shape)) {
|
|
1334
|
+
if (subEntityPaths !== void 0) {
|
|
1335
|
+
if (typeof subEntityPaths === "string" ? key === subEntityPaths : subEntityPaths.includes(key)) {
|
|
1336
|
+
continue;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
obj[key] = parseValue(
|
|
1340
|
+
obj[key],
|
|
1341
|
+
propDef,
|
|
1342
|
+
`${objectShape.typenameValue ?? "object"}.${key}`,
|
|
1343
|
+
false,
|
|
1344
|
+
warn
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
return obj;
|
|
1348
|
+
}
|
|
1349
|
+
function parseEntities(value, def, queryClient, entityRefs) {
|
|
1350
|
+
const valueType = typeMaskOf(value);
|
|
1351
|
+
const defType = def.mask;
|
|
1352
|
+
if ((defType & Mask.PARSE_RESULT) !== 0) {
|
|
1353
|
+
try {
|
|
1354
|
+
const innerResult = parseEntities(
|
|
1355
|
+
value,
|
|
1356
|
+
def.shape,
|
|
1357
|
+
queryClient,
|
|
1358
|
+
entityRefs
|
|
1359
|
+
);
|
|
1360
|
+
return { success: true, value: innerResult };
|
|
1361
|
+
} catch (e) {
|
|
1362
|
+
return { success: false, error: e instanceof Error ? e : new Error(String(e)) };
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
if (valueType < Mask.OBJECT || (defType & valueType) === 0) {
|
|
1366
|
+
return value;
|
|
1367
|
+
}
|
|
1368
|
+
if ((defType & Mask.UNION) !== 0) {
|
|
1369
|
+
return parseUnionEntities(
|
|
1370
|
+
valueType,
|
|
1371
|
+
value,
|
|
1372
|
+
def,
|
|
1373
|
+
queryClient,
|
|
1374
|
+
entityRefs
|
|
1375
|
+
);
|
|
1376
|
+
}
|
|
1377
|
+
if (valueType === Mask.ARRAY) {
|
|
1378
|
+
return parseArrayEntities(value, def.shape, queryClient, entityRefs);
|
|
1379
|
+
}
|
|
1380
|
+
if ((defType & Mask.RECORD) !== 0) {
|
|
1381
|
+
return parseRecordEntities(
|
|
1382
|
+
value,
|
|
1383
|
+
def.shape,
|
|
1384
|
+
queryClient,
|
|
1385
|
+
entityRefs
|
|
1386
|
+
);
|
|
1387
|
+
}
|
|
1388
|
+
return parseObjectEntities(value, def, queryClient, entityRefs);
|
|
1389
|
+
}
|
|
1390
|
+
class QueryResultExtra {
|
|
1391
|
+
_streamOrphansNotifier = void 0;
|
|
1392
|
+
_streamOrphans = void 0;
|
|
1393
|
+
_optimisticInsertsNotifier = void 0;
|
|
1394
|
+
_optimisticInserts = void 0;
|
|
1395
|
+
onChanged;
|
|
1396
|
+
constructor(onChanged) {
|
|
1397
|
+
this.onChanged = onChanged;
|
|
1398
|
+
}
|
|
1399
|
+
get streamOrphansNotifier() {
|
|
1400
|
+
return this._streamOrphansNotifier ?? (this._streamOrphansNotifier = signalium.notifier());
|
|
1401
|
+
}
|
|
1402
|
+
get optimisticInsertsNotifier() {
|
|
1403
|
+
return this._optimisticInsertsNotifier ?? (this._optimisticInsertsNotifier = signalium.notifier());
|
|
1404
|
+
}
|
|
1405
|
+
get streamOrphans() {
|
|
1406
|
+
return this._streamOrphans ?? (this._streamOrphans = /* @__PURE__ */ new Set());
|
|
1407
|
+
}
|
|
1408
|
+
get optimisticInserts() {
|
|
1409
|
+
return this._optimisticInserts ?? (this._optimisticInserts = /* @__PURE__ */ new Set());
|
|
1410
|
+
}
|
|
1411
|
+
/**
|
|
1412
|
+
* Returns the QueryExtra object for public API consumption.
|
|
1413
|
+
* Consumes the notifiers to establish reactive tracking.
|
|
1414
|
+
*/
|
|
1415
|
+
getExtra() {
|
|
1416
|
+
this.streamOrphansNotifier.consume();
|
|
1417
|
+
this.optimisticInsertsNotifier.consume();
|
|
1418
|
+
return {
|
|
1419
|
+
streamOrphans: this.streamOrphans,
|
|
1420
|
+
optimisticInserts: this.optimisticInserts
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
/**
|
|
1424
|
+
* Add a stream orphan entity.
|
|
1425
|
+
* Returns true if the orphan was added (not a duplicate).
|
|
1426
|
+
*/
|
|
1427
|
+
addStreamOrphan(entity2) {
|
|
1428
|
+
const orphans = this.streamOrphans;
|
|
1429
|
+
const sizeBefore = orphans.size;
|
|
1430
|
+
orphans.add(entity2);
|
|
1431
|
+
if (orphans.size !== sizeBefore) {
|
|
1432
|
+
this.streamOrphansNotifier.notify();
|
|
1433
|
+
const proxyId = getProxyId(entity2);
|
|
1434
|
+
if (proxyId !== void 0) {
|
|
1435
|
+
this.removeOptimisticInsertById(proxyId);
|
|
1436
|
+
}
|
|
1437
|
+
this.onChanged();
|
|
1438
|
+
return true;
|
|
1439
|
+
}
|
|
1440
|
+
return false;
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Add an optimistic insert entity.
|
|
1444
|
+
* Returns true if the insert was added (not a duplicate).
|
|
1445
|
+
*/
|
|
1446
|
+
addOptimisticInsert(entity2) {
|
|
1447
|
+
const inserts = this.optimisticInserts;
|
|
1448
|
+
const sizeBefore = inserts.size;
|
|
1449
|
+
inserts.add(entity2);
|
|
1450
|
+
if (inserts.size !== sizeBefore) {
|
|
1451
|
+
this.optimisticInsertsNotifier.notify();
|
|
1452
|
+
this.onChanged();
|
|
1453
|
+
return true;
|
|
1454
|
+
}
|
|
1455
|
+
return false;
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Remove an optimistic insert by its entity.
|
|
1459
|
+
* Returns true if the insert was removed.
|
|
1460
|
+
*/
|
|
1461
|
+
removeOptimisticInsert(entity2) {
|
|
1462
|
+
const proxyId = getProxyId(entity2);
|
|
1463
|
+
if (proxyId === void 0) {
|
|
1464
|
+
return false;
|
|
1465
|
+
}
|
|
1466
|
+
return this.removeOptimisticInsertById(proxyId);
|
|
1467
|
+
}
|
|
1468
|
+
/**
|
|
1469
|
+
* Remove an optimistic insert by proxy ID.
|
|
1470
|
+
*/
|
|
1471
|
+
removeOptimisticInsertById(proxyId) {
|
|
1472
|
+
const inserts = this._optimisticInserts;
|
|
1473
|
+
if (inserts === void 0 || inserts.size === 0) {
|
|
1474
|
+
return false;
|
|
1475
|
+
}
|
|
1476
|
+
for (const existing of inserts) {
|
|
1477
|
+
if (getProxyId(existing) === proxyId) {
|
|
1478
|
+
inserts.delete(existing);
|
|
1479
|
+
this.optimisticInsertsNotifier.notify();
|
|
1480
|
+
this.onChanged();
|
|
1481
|
+
return true;
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
return false;
|
|
1485
|
+
}
|
|
1486
|
+
/**
|
|
1487
|
+
* Check if a proxy ID exists in stream orphans.
|
|
1488
|
+
*/
|
|
1489
|
+
hasOrphanWithId(proxyId) {
|
|
1490
|
+
const orphans = this._streamOrphans;
|
|
1491
|
+
if (orphans === void 0) {
|
|
1492
|
+
return false;
|
|
1493
|
+
}
|
|
1494
|
+
for (const orphan of orphans) {
|
|
1495
|
+
if (getProxyId(orphan) === proxyId) {
|
|
1496
|
+
return true;
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
return false;
|
|
1500
|
+
}
|
|
1501
|
+
/**
|
|
1502
|
+
* Reconcile orphans and optimistic inserts against the main response entity refs.
|
|
1503
|
+
* Removes any that now exist in the main response.
|
|
1504
|
+
*/
|
|
1505
|
+
reconcile(allRefIds) {
|
|
1506
|
+
const orphans = this._streamOrphans;
|
|
1507
|
+
if (orphans !== void 0 && orphans.size > 0) {
|
|
1508
|
+
let orphansChanged = false;
|
|
1509
|
+
for (const orphan of orphans) {
|
|
1510
|
+
const entityRefId = getProxyId(orphan);
|
|
1511
|
+
if (entityRefId !== void 0 && allRefIds.has(entityRefId)) {
|
|
1512
|
+
orphans.delete(orphan);
|
|
1513
|
+
orphansChanged = true;
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
if (orphansChanged) {
|
|
1517
|
+
this.streamOrphansNotifier.notify();
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
const inserts = this._optimisticInserts;
|
|
1521
|
+
if (inserts !== void 0 && inserts.size > 0) {
|
|
1522
|
+
let insertsChanged = false;
|
|
1523
|
+
for (const insert of inserts) {
|
|
1524
|
+
const entityRefId = getProxyId(insert);
|
|
1525
|
+
if (entityRefId !== void 0) {
|
|
1526
|
+
if (allRefIds.has(entityRefId)) {
|
|
1527
|
+
inserts.delete(insert);
|
|
1528
|
+
insertsChanged = true;
|
|
1529
|
+
} else if (orphans !== void 0 && orphans.has(insert)) {
|
|
1530
|
+
inserts.delete(insert);
|
|
1531
|
+
insertsChanged = true;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
if (insertsChanged) {
|
|
1536
|
+
this.optimisticInsertsNotifier.notify();
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
/**
|
|
1541
|
+
* Clear all stream orphans and optimistic inserts.
|
|
1542
|
+
* Called on refetch.
|
|
1543
|
+
*/
|
|
1544
|
+
clear() {
|
|
1545
|
+
let changed = false;
|
|
1546
|
+
if (this._streamOrphans !== void 0 && this._streamOrphans.size > 0) {
|
|
1547
|
+
this._streamOrphans = void 0;
|
|
1548
|
+
this.streamOrphansNotifier.notify();
|
|
1549
|
+
changed = true;
|
|
1550
|
+
}
|
|
1551
|
+
if (this._optimisticInserts !== void 0 && this._optimisticInserts.size > 0) {
|
|
1552
|
+
this._optimisticInserts = void 0;
|
|
1553
|
+
this.optimisticInsertsNotifier.notify();
|
|
1554
|
+
changed = true;
|
|
1555
|
+
}
|
|
1556
|
+
if (changed) {
|
|
1557
|
+
this.onChanged();
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
/**
|
|
1561
|
+
* Load extra data from cached values.
|
|
1562
|
+
*/
|
|
1563
|
+
loadFromCache(cachedExtra, queryClient, streamShape, optimisticInsertsShape) {
|
|
1564
|
+
if (cachedExtra.streamOrphanRefs && cachedExtra.streamOrphanRefs.length > 0 && streamShape) {
|
|
1565
|
+
const orphans = this.streamOrphans;
|
|
1566
|
+
for (const refId of cachedExtra.streamOrphanRefs) {
|
|
1567
|
+
const entityRecord = queryClient.hydrateEntity(refId, streamShape);
|
|
1568
|
+
orphans.add(entityRecord.proxy);
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
if (cachedExtra.optimisticInsertRefs && cachedExtra.optimisticInsertRefs.length > 0 && optimisticInsertsShape) {
|
|
1572
|
+
const inserts = this.optimisticInserts;
|
|
1573
|
+
for (const refId of cachedExtra.optimisticInsertRefs) {
|
|
1574
|
+
const entityRecord = queryClient.hydrateEntity(refId, optimisticInsertsShape);
|
|
1575
|
+
inserts.add(entityRecord.proxy);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
/**
|
|
1580
|
+
* Get extra data for persistence (converts Sets to arrays of entity ref IDs).
|
|
1581
|
+
*/
|
|
1582
|
+
getForPersistence() {
|
|
1583
|
+
const orphans = this._streamOrphans;
|
|
1584
|
+
const inserts = this._optimisticInserts;
|
|
1585
|
+
if ((orphans === void 0 || orphans.size === 0) && (inserts === void 0 || inserts.size === 0)) {
|
|
1586
|
+
return void 0;
|
|
1587
|
+
}
|
|
1588
|
+
const extra = {};
|
|
1589
|
+
if (orphans !== void 0 && orphans.size > 0) {
|
|
1590
|
+
extra.streamOrphanRefs = [];
|
|
1591
|
+
for (const orphan of orphans) {
|
|
1592
|
+
const refId = getProxyId(orphan);
|
|
1593
|
+
if (refId !== void 0) {
|
|
1594
|
+
extra.streamOrphanRefs.push(refId);
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
if (inserts !== void 0 && inserts.size > 0) {
|
|
1599
|
+
extra.optimisticInsertRefs = [];
|
|
1600
|
+
for (const insert of inserts) {
|
|
1601
|
+
const refId = getProxyId(insert);
|
|
1602
|
+
if (refId !== void 0) {
|
|
1603
|
+
extra.optimisticInsertRefs.push(refId);
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
return extra;
|
|
1608
|
+
}
|
|
1609
|
+
/**
|
|
1610
|
+
* Check if there's any extra data.
|
|
1611
|
+
*/
|
|
1612
|
+
get hasData() {
|
|
1613
|
+
return this._streamOrphans !== void 0 && this._streamOrphans.size > 0 || this._optimisticInserts !== void 0 && this._optimisticInserts.size > 0;
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
class QueryResultImpl {
|
|
1617
|
+
def;
|
|
1618
|
+
queryKey;
|
|
1619
|
+
// Instance key (includes Signal identity)
|
|
1620
|
+
storageKey = -1;
|
|
1621
|
+
// Storage key (extracted values only)
|
|
1622
|
+
queryClient;
|
|
1623
|
+
initialized = false;
|
|
1624
|
+
isRefetchingSignal = signalium.signal(false);
|
|
1625
|
+
isFetchingMoreSignal = signalium.signal(false);
|
|
1626
|
+
updatedAt = void 0;
|
|
1627
|
+
params = void 0;
|
|
1628
|
+
refIds = void 0;
|
|
1629
|
+
allNestedRefIdsSignal = void 0;
|
|
1630
|
+
refetchPromise = void 0;
|
|
1631
|
+
fetchMorePromise = void 0;
|
|
1632
|
+
unsubscribe = void 0;
|
|
1633
|
+
relay;
|
|
1634
|
+
_relayState = void 0;
|
|
1635
|
+
wasPaused = false;
|
|
1636
|
+
currentParams = void 0;
|
|
1637
|
+
debounceTimer = void 0;
|
|
1638
|
+
get relayState() {
|
|
1639
|
+
const relayState = this._relayState;
|
|
1640
|
+
if (!relayState) {
|
|
1641
|
+
throw new Error("Relay state not initialized");
|
|
1642
|
+
}
|
|
1643
|
+
return relayState;
|
|
1644
|
+
}
|
|
1645
|
+
_extra = void 0;
|
|
1646
|
+
get extraData() {
|
|
1647
|
+
return this._extra ?? (this._extra = new QueryResultExtra(() => this.persistExtraData()));
|
|
1648
|
+
}
|
|
1649
|
+
_nextPageParams = void 0;
|
|
1650
|
+
get nextPageParams() {
|
|
1651
|
+
if (this.def.type !== QueryType.InfiniteQuery) {
|
|
1652
|
+
return null;
|
|
1653
|
+
}
|
|
1654
|
+
let params = this._nextPageParams;
|
|
1655
|
+
const value = this.relayState.value;
|
|
1656
|
+
if (params === void 0 && value !== void 0) {
|
|
1657
|
+
if (!Array.isArray(value)) {
|
|
1658
|
+
throw new Error("Query result is not an array, this is a bug");
|
|
1659
|
+
}
|
|
1660
|
+
const infiniteDef = this.def;
|
|
1661
|
+
const nextParams = infiniteDef.pagination?.getNextPageParams?.(value[value.length - 1]);
|
|
1662
|
+
if (nextParams === void 0) {
|
|
1663
|
+
params = null;
|
|
1664
|
+
} else {
|
|
1665
|
+
let hasDefinedParams = false;
|
|
1666
|
+
const clonedParams = { ...this.currentParams };
|
|
1667
|
+
for (const [key, value2] of Object.entries(nextParams)) {
|
|
1668
|
+
if (value2 !== void 0 && value2 !== null) {
|
|
1669
|
+
clonedParams[key] = value2;
|
|
1670
|
+
hasDefinedParams = true;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
this._nextPageParams = params = hasDefinedParams ? clonedParams : null;
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
return params ?? null;
|
|
1677
|
+
}
|
|
1678
|
+
constructor(def, queryClient, queryKey, params) {
|
|
1679
|
+
utils.setReactivePromise(this);
|
|
1680
|
+
this.def = def;
|
|
1681
|
+
this.queryClient = queryClient;
|
|
1682
|
+
this.queryKey = queryKey;
|
|
1683
|
+
this.params = params;
|
|
1684
|
+
this.relay = signalium.relay((state) => {
|
|
1685
|
+
this._relayState = state;
|
|
1686
|
+
this.currentParams = extractParamsForKey(this.params);
|
|
1687
|
+
this.storageKey = queryKeyFor(this.def, this.currentParams);
|
|
1688
|
+
this.queryClient.activateQuery(this);
|
|
1689
|
+
const isPaused = this.isPaused;
|
|
1690
|
+
this.wasPaused = isPaused;
|
|
1691
|
+
if (this.initialized) {
|
|
1692
|
+
if (!isPaused) {
|
|
1693
|
+
if (this.def.type === QueryType.Stream || this.def.stream) {
|
|
1694
|
+
this.setupSubscription();
|
|
1695
|
+
}
|
|
1696
|
+
if (this.def.type !== QueryType.Stream && this.isStale) {
|
|
1697
|
+
this.refetch();
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
} else {
|
|
1701
|
+
this.initialize();
|
|
1702
|
+
}
|
|
1703
|
+
const deactivate = () => {
|
|
1704
|
+
clearTimeout(this.debounceTimer);
|
|
1705
|
+
this.debounceTimer = void 0;
|
|
1706
|
+
this.unsubscribe?.();
|
|
1707
|
+
this.unsubscribe = void 0;
|
|
1708
|
+
if (this.def.type !== QueryType.Stream && this.def.cache?.refetchInterval) {
|
|
1709
|
+
this.queryClient.refetchManager.removeQuery(this);
|
|
1710
|
+
}
|
|
1711
|
+
this.queryClient.memoryEvictionManager.scheduleEviction(this.queryKey);
|
|
1712
|
+
};
|
|
1713
|
+
return {
|
|
1714
|
+
update: () => {
|
|
1715
|
+
const { wasPaused, isPaused: isPaused2 } = this;
|
|
1716
|
+
this.wasPaused = isPaused2;
|
|
1717
|
+
if (isPaused2) {
|
|
1718
|
+
deactivate();
|
|
1719
|
+
return;
|
|
1720
|
+
}
|
|
1721
|
+
const newExtractedParams = extractParamsForKey(this.params);
|
|
1722
|
+
const newStorageKey = queryKeyFor(this.def, newExtractedParams);
|
|
1723
|
+
const paramsDidChange = newStorageKey !== this.storageKey;
|
|
1724
|
+
if (paramsDidChange) {
|
|
1725
|
+
this.params = newExtractedParams;
|
|
1726
|
+
this.storageKey = newStorageKey;
|
|
1727
|
+
}
|
|
1728
|
+
if (wasPaused) {
|
|
1729
|
+
this.queryClient.activateQuery(this);
|
|
1730
|
+
if (this.def.type !== QueryType.Stream) {
|
|
1731
|
+
const refreshStaleOnReconnect = this.def.cache?.refreshStaleOnReconnect ?? true;
|
|
1732
|
+
if (refreshStaleOnReconnect && this.isStale) {
|
|
1733
|
+
state.setPromise(this.runQuery(this.currentParams, true));
|
|
1734
|
+
}
|
|
1735
|
+
} else {
|
|
1736
|
+
this.setupSubscription();
|
|
1737
|
+
}
|
|
1738
|
+
} else if (paramsDidChange) {
|
|
1739
|
+
if (this.def.type !== QueryType.Stream) {
|
|
1740
|
+
this.debouncedRefetch();
|
|
1741
|
+
} else {
|
|
1742
|
+
this.setupSubscription();
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
},
|
|
1746
|
+
deactivate
|
|
1747
|
+
};
|
|
1748
|
+
});
|
|
1749
|
+
}
|
|
1750
|
+
// ======================================================
|
|
1751
|
+
// ReactivePromise properties
|
|
1752
|
+
// =====================================================
|
|
1753
|
+
get value() {
|
|
1754
|
+
return this.relay.value;
|
|
1755
|
+
}
|
|
1756
|
+
get error() {
|
|
1757
|
+
return this.relay.error;
|
|
1758
|
+
}
|
|
1759
|
+
get isPending() {
|
|
1760
|
+
return this.relay.isPending;
|
|
1761
|
+
}
|
|
1762
|
+
get isRejected() {
|
|
1763
|
+
return this.relay.isRejected;
|
|
1764
|
+
}
|
|
1765
|
+
get isResolved() {
|
|
1766
|
+
return this.relay.isResolved;
|
|
1767
|
+
}
|
|
1768
|
+
get isSettled() {
|
|
1769
|
+
return this.relay.isSettled;
|
|
1770
|
+
}
|
|
1771
|
+
get isReady() {
|
|
1772
|
+
return this.relay.isReady;
|
|
1773
|
+
}
|
|
1774
|
+
// TODO: Intimate APIs needed for `useReactive`, this is a code smell and
|
|
1775
|
+
// we should find a better way to entangle these more generically
|
|
1776
|
+
get _version() {
|
|
1777
|
+
return this.relay._version;
|
|
1778
|
+
}
|
|
1779
|
+
get _signal() {
|
|
1780
|
+
return this.relay._signal;
|
|
1781
|
+
}
|
|
1782
|
+
get _flags() {
|
|
1783
|
+
return this.relay._flags;
|
|
1784
|
+
}
|
|
1785
|
+
// Forward Promise methods
|
|
1786
|
+
then(onfulfilled, onrejected) {
|
|
1787
|
+
return this.relay.then(onfulfilled, onrejected);
|
|
1788
|
+
}
|
|
1789
|
+
catch(onrejected) {
|
|
1790
|
+
return this.relay.catch(onrejected);
|
|
1791
|
+
}
|
|
1792
|
+
finally(onfinally) {
|
|
1793
|
+
return this.relay.finally(onfinally);
|
|
1794
|
+
}
|
|
1795
|
+
get [Symbol.toStringTag]() {
|
|
1796
|
+
return "QueryResult";
|
|
1797
|
+
}
|
|
1798
|
+
// ======================================================
|
|
1799
|
+
// Internal fetch methods
|
|
1800
|
+
// ======================================================
|
|
1801
|
+
getAllEntityRefs() {
|
|
1802
|
+
let allNestedRefIdsSignal = this.allNestedRefIdsSignal;
|
|
1803
|
+
if (!allNestedRefIdsSignal) {
|
|
1804
|
+
const queryClient = this.queryClient;
|
|
1805
|
+
this.allNestedRefIdsSignal = allNestedRefIdsSignal = signalium.reactiveSignal(() => {
|
|
1806
|
+
this.relay.value;
|
|
1807
|
+
const allRefIds = /* @__PURE__ */ new Set();
|
|
1808
|
+
if (this.refIds !== void 0) {
|
|
1809
|
+
for (const refId of this.refIds) {
|
|
1810
|
+
queryClient.getNestedEntityRefIds(refId, allRefIds);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
this.extraData.reconcile(allRefIds);
|
|
1814
|
+
return allRefIds;
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1817
|
+
return allNestedRefIdsSignal.value;
|
|
1818
|
+
}
|
|
1819
|
+
/**
|
|
1820
|
+
* Initialize the query by loading from cache and fetching if stale
|
|
1821
|
+
*/
|
|
1822
|
+
async initialize() {
|
|
1823
|
+
const state = this.relayState;
|
|
1824
|
+
this.initialized = true;
|
|
1825
|
+
let cached;
|
|
1826
|
+
try {
|
|
1827
|
+
cached = await this.queryClient.loadCachedQuery(this.def, this.storageKey);
|
|
1828
|
+
if (cached !== void 0) {
|
|
1829
|
+
this.updatedAt = cached.updatedAt;
|
|
1830
|
+
this.refIds = cached.refIds;
|
|
1831
|
+
if (cached.extra) {
|
|
1832
|
+
const def = this.def;
|
|
1833
|
+
this.extraData.loadFromCache(
|
|
1834
|
+
cached.extra,
|
|
1835
|
+
this.queryClient,
|
|
1836
|
+
def.stream?.shape,
|
|
1837
|
+
def.optimisticInserts?.shape
|
|
1838
|
+
);
|
|
1839
|
+
}
|
|
1840
|
+
const shape = this.def.shape;
|
|
1841
|
+
state.value = shape instanceof ValidatorDef ? parseEntities(cached.value, shape, this.queryClient, /* @__PURE__ */ new Set()) : parseValue(cached.value, shape, this.def.id);
|
|
1842
|
+
}
|
|
1843
|
+
} catch (error) {
|
|
1844
|
+
this.queryClient.deleteCachedQuery(this.storageKey);
|
|
1845
|
+
this.queryClient.getContext().log?.warn?.("Failed to initialize query, the query cache may be corrupted or invalid", error);
|
|
1846
|
+
}
|
|
1847
|
+
if (this.isPaused) {
|
|
1848
|
+
return;
|
|
1849
|
+
}
|
|
1850
|
+
try {
|
|
1851
|
+
if (this.def.type === QueryType.Stream || this.def.stream) {
|
|
1852
|
+
this.setupSubscription();
|
|
1853
|
+
}
|
|
1854
|
+
if (this.def.type !== QueryType.Stream) {
|
|
1855
|
+
if (cached !== void 0) {
|
|
1856
|
+
if (this.isStale) {
|
|
1857
|
+
if (this.def.debounce !== void 0 && this.def.debounce > 0) {
|
|
1858
|
+
this.debouncedRefetch();
|
|
1859
|
+
} else {
|
|
1860
|
+
this.refetch();
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
} else {
|
|
1864
|
+
state.setPromise(this.runQuery(this.currentParams, true));
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
} catch (error) {
|
|
1868
|
+
state.setError(error);
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
/**
|
|
1872
|
+
* Handle stream updates. This method handles both StreamQuery and Query/InfiniteQuery with stream options.
|
|
1873
|
+
* - For StreamQuery: directly updates the relay state with the entity
|
|
1874
|
+
* - For Query/InfiniteQuery with stream: updates entities in response or adds to orphans
|
|
1875
|
+
*/
|
|
1876
|
+
setupSubscription() {
|
|
1877
|
+
this.unsubscribe?.();
|
|
1878
|
+
let subscribeFn;
|
|
1879
|
+
let shapeDef;
|
|
1880
|
+
if (this.def.type === QueryType.Stream) {
|
|
1881
|
+
shapeDef = this.def.shape;
|
|
1882
|
+
subscribeFn = this.def.subscribeFn;
|
|
1883
|
+
} else {
|
|
1884
|
+
const stream = this.def.stream;
|
|
1885
|
+
if (!stream) {
|
|
1886
|
+
return;
|
|
1887
|
+
}
|
|
1888
|
+
shapeDef = stream.shape;
|
|
1889
|
+
subscribeFn = stream.subscribeFn;
|
|
1890
|
+
}
|
|
1891
|
+
const extractedParams = this.currentParams;
|
|
1892
|
+
this.unsubscribe = subscribeFn(this.queryClient.getContext(), extractedParams, (update) => {
|
|
1893
|
+
const parsedData = parseObjectEntities(update, shapeDef, this.queryClient);
|
|
1894
|
+
if (this.def.type === QueryType.Stream) {
|
|
1895
|
+
this.relayState.value = parsedData;
|
|
1896
|
+
this.updatedAt = Date.now();
|
|
1897
|
+
this.queryClient.saveQueryData(this.def, this.storageKey, parsedData, this.updatedAt);
|
|
1898
|
+
} else {
|
|
1899
|
+
const allRefIds = this.getAllEntityRefs();
|
|
1900
|
+
const proxyId = getProxyId(parsedData);
|
|
1901
|
+
if (proxyId !== void 0 && !allRefIds.has(proxyId)) {
|
|
1902
|
+
this.extraData.addStreamOrphan(parsedData);
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
/**
|
|
1908
|
+
* Fetches fresh data, updates the cache, and updates updatedAt timestamp
|
|
1909
|
+
*/
|
|
1910
|
+
async runQuery(params, reset = false) {
|
|
1911
|
+
if (this.isPaused) {
|
|
1912
|
+
throw new Error("Query is paused due to network status");
|
|
1913
|
+
}
|
|
1914
|
+
const { retries, retryDelay } = this.getRetryConfig();
|
|
1915
|
+
let lastError;
|
|
1916
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
1917
|
+
try {
|
|
1918
|
+
const queryDef = this.def;
|
|
1919
|
+
const freshData = await queryDef.fetchFn(this.queryClient.getContext(), params);
|
|
1920
|
+
let entityRefs;
|
|
1921
|
+
const isInfinite = this.def.type === QueryType.InfiniteQuery;
|
|
1922
|
+
if (isInfinite && !reset && this.refIds !== void 0) {
|
|
1923
|
+
entityRefs = this.refIds;
|
|
1924
|
+
} else {
|
|
1925
|
+
entityRefs = this.refIds = /* @__PURE__ */ new Set();
|
|
1926
|
+
}
|
|
1927
|
+
const shape = this.def.shape;
|
|
1928
|
+
const parsedData = shape instanceof ValidatorDef ? parseEntities(freshData, shape, this.queryClient, entityRefs) : parseValue(freshData, shape, this.def.id);
|
|
1929
|
+
let queryData;
|
|
1930
|
+
if (isInfinite) {
|
|
1931
|
+
const prevQueryData = this.relayState.value;
|
|
1932
|
+
queryData = reset || prevQueryData === void 0 ? [parsedData] : [...prevQueryData, parsedData];
|
|
1933
|
+
} else {
|
|
1934
|
+
queryData = parsedData;
|
|
1935
|
+
}
|
|
1936
|
+
let updatedAt;
|
|
1937
|
+
if (reset) {
|
|
1938
|
+
updatedAt = this.updatedAt = Date.now();
|
|
1939
|
+
} else {
|
|
1940
|
+
updatedAt = this.updatedAt ??= Date.now();
|
|
1941
|
+
}
|
|
1942
|
+
this._nextPageParams = void 0;
|
|
1943
|
+
this.queryClient.saveQueryData(
|
|
1944
|
+
this.def,
|
|
1945
|
+
this.storageKey,
|
|
1946
|
+
queryData,
|
|
1947
|
+
updatedAt,
|
|
1948
|
+
entityRefs,
|
|
1949
|
+
this.getExtraForPersistence()
|
|
1950
|
+
);
|
|
1951
|
+
this.updatedAt = Date.now();
|
|
1952
|
+
return queryData;
|
|
1953
|
+
} catch (error) {
|
|
1954
|
+
lastError = error;
|
|
1955
|
+
if (attempt >= retries) {
|
|
1956
|
+
throw error;
|
|
1957
|
+
}
|
|
1958
|
+
const delay = retryDelay(attempt);
|
|
1959
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1960
|
+
if (this.isPaused) {
|
|
1961
|
+
throw new Error("Query is paused due to network status");
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
throw lastError;
|
|
1966
|
+
}
|
|
1967
|
+
// ======================================================
|
|
1968
|
+
// Private debounce methods
|
|
1969
|
+
// ======================================================
|
|
1970
|
+
/**
|
|
1971
|
+
* Triggers a debounced refetch. If debounce is configured, delays the fetch.
|
|
1972
|
+
* Otherwise, calls refetch immediately.
|
|
1973
|
+
*/
|
|
1974
|
+
debouncedRefetch() {
|
|
1975
|
+
const debounce = this.def.debounce;
|
|
1976
|
+
if (debounce === void 0) {
|
|
1977
|
+
this.refetch();
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1980
|
+
clearTimeout(this.debounceTimer);
|
|
1981
|
+
this.debounceTimer = setTimeout(() => {
|
|
1982
|
+
this.debounceTimer = void 0;
|
|
1983
|
+
this.refetch();
|
|
1984
|
+
}, debounce);
|
|
1985
|
+
}
|
|
1986
|
+
// ======================================================
|
|
1987
|
+
// Public methods
|
|
1988
|
+
// ======================================================
|
|
1989
|
+
refetch = () => {
|
|
1990
|
+
if (this.def.type === QueryType.Stream) {
|
|
1991
|
+
throw new Error("Cannot refetch a stream query");
|
|
1992
|
+
}
|
|
1993
|
+
if (this.fetchMorePromise) {
|
|
1994
|
+
throw new Error("Query is fetching more, cannot refetch");
|
|
1995
|
+
}
|
|
1996
|
+
if (this.refetchPromise) {
|
|
1997
|
+
return this.refetchPromise;
|
|
1998
|
+
}
|
|
1999
|
+
clearTimeout(this.debounceTimer);
|
|
2000
|
+
this.debounceTimer = void 0;
|
|
2001
|
+
this._nextPageParams = void 0;
|
|
2002
|
+
this.isRefetchingSignal.value = true;
|
|
2003
|
+
this._version.update((v) => v + 1);
|
|
2004
|
+
const promise = this.runQuery(this.currentParams, true).then((result) => {
|
|
2005
|
+
this.relayState.value = result;
|
|
2006
|
+
if (this._extra !== void 0) {
|
|
2007
|
+
this._extra.clear();
|
|
2008
|
+
}
|
|
2009
|
+
return result;
|
|
2010
|
+
}).catch((error) => {
|
|
2011
|
+
this.relayState.setError(error);
|
|
2012
|
+
return Promise.reject(error);
|
|
2013
|
+
}).finally(() => {
|
|
2014
|
+
this._version.update((v) => v + 1);
|
|
2015
|
+
this.isRefetchingSignal.value = false;
|
|
2016
|
+
this.refetchPromise = void 0;
|
|
2017
|
+
});
|
|
2018
|
+
this.refetchPromise = promise;
|
|
2019
|
+
return promise;
|
|
2020
|
+
};
|
|
2021
|
+
fetchNextPage = () => {
|
|
2022
|
+
if (this.def.type === QueryType.Stream) {
|
|
2023
|
+
throw new Error("Cannot fetch next page on a stream query");
|
|
2024
|
+
}
|
|
2025
|
+
if (this.refetchPromise) {
|
|
2026
|
+
return Promise.reject(new Error("Query is refetching, cannot fetch next page"));
|
|
2027
|
+
}
|
|
2028
|
+
if (this.fetchMorePromise) {
|
|
2029
|
+
return this.fetchMorePromise;
|
|
2030
|
+
}
|
|
2031
|
+
const nextPageParams = this.nextPageParams;
|
|
2032
|
+
if (!nextPageParams) {
|
|
2033
|
+
return Promise.reject(new Error("No next page params"));
|
|
2034
|
+
}
|
|
2035
|
+
this.isFetchingMoreSignal.value = true;
|
|
2036
|
+
this._version.update((v) => v + 1);
|
|
2037
|
+
const promise = this.runQuery(nextPageParams, false).then((result) => {
|
|
2038
|
+
this.relayState.value = result;
|
|
2039
|
+
return result;
|
|
2040
|
+
}).catch((error) => {
|
|
2041
|
+
this.relayState.setError(error);
|
|
2042
|
+
return Promise.reject(error);
|
|
2043
|
+
}).finally(() => {
|
|
2044
|
+
this._version.update((v) => v + 1);
|
|
2045
|
+
this.isFetchingMoreSignal.value = false;
|
|
2046
|
+
this.fetchMorePromise = void 0;
|
|
2047
|
+
});
|
|
2048
|
+
this.fetchMorePromise = promise;
|
|
2049
|
+
return promise;
|
|
2050
|
+
};
|
|
2051
|
+
// ======================================================
|
|
2052
|
+
// Public properties
|
|
2053
|
+
// ======================================================
|
|
2054
|
+
get isRefetching() {
|
|
2055
|
+
return this.isRefetchingSignal.value;
|
|
2056
|
+
}
|
|
2057
|
+
get isFetchingMore() {
|
|
2058
|
+
return this.isFetchingMoreSignal.value;
|
|
2059
|
+
}
|
|
2060
|
+
get isFetching() {
|
|
2061
|
+
return this.relay.isPending || this.isRefetching || this.isFetchingMore;
|
|
2062
|
+
}
|
|
2063
|
+
get hasNextPage() {
|
|
2064
|
+
return this.nextPageParams !== null;
|
|
2065
|
+
}
|
|
2066
|
+
get extra() {
|
|
2067
|
+
this.getAllEntityRefs();
|
|
2068
|
+
return this.extraData.getExtra();
|
|
2069
|
+
}
|
|
2070
|
+
/**
|
|
2071
|
+
* Persist the current extra data to the store
|
|
2072
|
+
*/
|
|
2073
|
+
persistExtraData() {
|
|
2074
|
+
if (this.updatedAt === void 0) {
|
|
2075
|
+
return;
|
|
2076
|
+
}
|
|
2077
|
+
const extra = this._extra?.getForPersistence();
|
|
2078
|
+
this.queryClient.saveQueryData(
|
|
2079
|
+
this.def,
|
|
2080
|
+
this.storageKey,
|
|
2081
|
+
this.relayState.value,
|
|
2082
|
+
this.updatedAt,
|
|
2083
|
+
this.refIds,
|
|
2084
|
+
extra
|
|
2085
|
+
);
|
|
2086
|
+
}
|
|
2087
|
+
/**
|
|
2088
|
+
* Get extra data for persistence (converts Sets to arrays of entity ref IDs)
|
|
2089
|
+
*/
|
|
2090
|
+
getExtraForPersistence() {
|
|
2091
|
+
return this._extra?.getForPersistence();
|
|
2092
|
+
}
|
|
2093
|
+
/**
|
|
2094
|
+
* Add an optimistic insert to the query result.
|
|
2095
|
+
* The insert will be automatically removed when:
|
|
2096
|
+
* - The entity appears in a refetched response
|
|
2097
|
+
* - The entity appears as a stream orphan
|
|
2098
|
+
* - refetch() is called
|
|
2099
|
+
*/
|
|
2100
|
+
addOptimisticInsert(insert) {
|
|
2101
|
+
const def = this.def;
|
|
2102
|
+
const optimisticInsertsConfig = def.optimisticInserts;
|
|
2103
|
+
if (optimisticInsertsConfig === void 0) {
|
|
2104
|
+
throw new Error(
|
|
2105
|
+
"Query does not have optimisticInserts configured. Add optimisticInserts: { type: YourEntity } to the query definition."
|
|
2106
|
+
);
|
|
2107
|
+
}
|
|
2108
|
+
let proxyId = getProxyId(insert);
|
|
2109
|
+
let parsedInsert = insert;
|
|
2110
|
+
if (proxyId === void 0) {
|
|
2111
|
+
parsedInsert = parseObjectEntities(insert, optimisticInsertsConfig.shape, this.queryClient);
|
|
2112
|
+
proxyId = getProxyId(parsedInsert);
|
|
2113
|
+
if (proxyId === void 0) {
|
|
2114
|
+
throw new Error("Optimistic insert must be or produce an entity proxy");
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
const allRefIds = this.getAllEntityRefs();
|
|
2118
|
+
if (allRefIds.has(proxyId)) {
|
|
2119
|
+
return;
|
|
2120
|
+
}
|
|
2121
|
+
if (this.extraData.hasOrphanWithId(proxyId)) {
|
|
2122
|
+
return;
|
|
2123
|
+
}
|
|
2124
|
+
this.extraData.addOptimisticInsert(parsedInsert);
|
|
2125
|
+
}
|
|
2126
|
+
/**
|
|
2127
|
+
* Remove an optimistic insert from the query result.
|
|
2128
|
+
* This is a no-op if the insert has already been removed.
|
|
2129
|
+
*/
|
|
2130
|
+
removeOptimisticInsert(insert) {
|
|
2131
|
+
this.extraData.removeOptimisticInsert(insert);
|
|
2132
|
+
}
|
|
2133
|
+
get isStale() {
|
|
2134
|
+
if (this.def.type === QueryType.Stream) {
|
|
2135
|
+
return false;
|
|
2136
|
+
}
|
|
2137
|
+
if (this.updatedAt === void 0) {
|
|
2138
|
+
return true;
|
|
2139
|
+
}
|
|
2140
|
+
const staleTime = this.def.cache?.staleTime ?? 0;
|
|
2141
|
+
return Date.now() - this.updatedAt >= staleTime;
|
|
2142
|
+
}
|
|
2143
|
+
get isPaused() {
|
|
2144
|
+
if (this.def.type === QueryType.Stream) {
|
|
2145
|
+
return false;
|
|
2146
|
+
}
|
|
2147
|
+
const networkMode = this.def.cache?.networkMode ?? NetworkMode.Online;
|
|
2148
|
+
const networkManager = this.queryClient.networkManager;
|
|
2149
|
+
const isOnline = networkManager.getOnlineSignal().value;
|
|
2150
|
+
switch (networkMode) {
|
|
2151
|
+
case NetworkMode.Always:
|
|
2152
|
+
return false;
|
|
2153
|
+
case NetworkMode.Online:
|
|
2154
|
+
return !isOnline;
|
|
2155
|
+
case NetworkMode.OfflineFirst:
|
|
2156
|
+
return !isOnline && this.updatedAt === void 0;
|
|
2157
|
+
default:
|
|
2158
|
+
return false;
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
getRetryConfig() {
|
|
2162
|
+
if (this.def.type === QueryType.Stream) {
|
|
2163
|
+
return { retries: 0, retryDelay: () => 0 };
|
|
2164
|
+
}
|
|
2165
|
+
const retryOption = this.def.cache?.retry;
|
|
2166
|
+
const isServer = this.queryClient.isServer;
|
|
2167
|
+
let retries;
|
|
2168
|
+
let retryDelay;
|
|
2169
|
+
if (retryOption === false) {
|
|
2170
|
+
retries = 0;
|
|
2171
|
+
} else if (retryOption === void 0) {
|
|
2172
|
+
retries = isServer ? 0 : 3;
|
|
2173
|
+
} else if (typeof retryOption === "number") {
|
|
2174
|
+
retries = retryOption;
|
|
2175
|
+
} else {
|
|
2176
|
+
retries = retryOption.retries;
|
|
2177
|
+
}
|
|
2178
|
+
if (typeof retryOption === "object" && retryOption.retryDelay) {
|
|
2179
|
+
retryDelay = retryOption.retryDelay;
|
|
2180
|
+
} else {
|
|
2181
|
+
retryDelay = (attempt) => 1e3 * Math.pow(2, attempt);
|
|
2182
|
+
}
|
|
2183
|
+
return { retries, retryDelay };
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
class MutationResultImpl {
|
|
2187
|
+
def;
|
|
2188
|
+
queryClient;
|
|
2189
|
+
// The underlying ReactiveTask that handles all state management
|
|
2190
|
+
_task;
|
|
2191
|
+
// Track which entity keys we've registered optimistic updates for
|
|
2192
|
+
// EntityMap handles the actual snapshots
|
|
2193
|
+
_pendingOptimisticKeys = /* @__PURE__ */ new Set();
|
|
2194
|
+
constructor(def, queryClient) {
|
|
2195
|
+
this.def = def;
|
|
2196
|
+
this.queryClient = queryClient;
|
|
2197
|
+
this._task = this.createTask();
|
|
2198
|
+
}
|
|
2199
|
+
createTask() {
|
|
2200
|
+
return signalium.task(async (request) => {
|
|
2201
|
+
try {
|
|
2202
|
+
const response = await this.executeWithRetry(request);
|
|
2203
|
+
const parsedResponse = this.parseAndUpdateEntities(response);
|
|
2204
|
+
this.clearOptimisticUpdates();
|
|
2205
|
+
return parsedResponse;
|
|
2206
|
+
} catch (error) {
|
|
2207
|
+
this.revertOptimisticUpdates();
|
|
2208
|
+
throw error;
|
|
2209
|
+
}
|
|
2210
|
+
});
|
|
2211
|
+
}
|
|
2212
|
+
// ======================================================
|
|
2213
|
+
// Delegated ReactivePromise properties
|
|
2214
|
+
// ======================================================
|
|
2215
|
+
get value() {
|
|
2216
|
+
return this._task.value;
|
|
2217
|
+
}
|
|
2218
|
+
get error() {
|
|
2219
|
+
return this._task.error;
|
|
2220
|
+
}
|
|
2221
|
+
get isPending() {
|
|
2222
|
+
return this._task.isPending;
|
|
2223
|
+
}
|
|
2224
|
+
get isRejected() {
|
|
2225
|
+
return this._task.isRejected;
|
|
2226
|
+
}
|
|
2227
|
+
get isResolved() {
|
|
2228
|
+
return this._task.isResolved;
|
|
2229
|
+
}
|
|
2230
|
+
get isSettled() {
|
|
2231
|
+
return this._task.isSettled;
|
|
2232
|
+
}
|
|
2233
|
+
get isReady() {
|
|
2234
|
+
return this._task.isReady;
|
|
2235
|
+
}
|
|
2236
|
+
// ======================================================
|
|
2237
|
+
// Promise interface (delegated)
|
|
2238
|
+
// ======================================================
|
|
2239
|
+
then(onfulfilled, onrejected) {
|
|
2240
|
+
return this._task.then(onfulfilled, onrejected);
|
|
2241
|
+
}
|
|
2242
|
+
catch(onrejected) {
|
|
2243
|
+
return this._task.catch(onrejected);
|
|
2244
|
+
}
|
|
2245
|
+
finally(onfinally) {
|
|
2246
|
+
return this._task.finally(onfinally);
|
|
2247
|
+
}
|
|
2248
|
+
get [Symbol.toStringTag]() {
|
|
2249
|
+
return "MutationResult";
|
|
2250
|
+
}
|
|
2251
|
+
// ======================================================
|
|
2252
|
+
// Mutation execution
|
|
2253
|
+
// ======================================================
|
|
2254
|
+
run = (request) => {
|
|
2255
|
+
if (this.def.optimisticUpdates) {
|
|
2256
|
+
this.applyOptimisticUpdates(request);
|
|
2257
|
+
}
|
|
2258
|
+
this._task.run(request);
|
|
2259
|
+
return this;
|
|
2260
|
+
};
|
|
2261
|
+
reset = () => {
|
|
2262
|
+
this.revertOptimisticUpdates();
|
|
2263
|
+
this._task = this.createTask();
|
|
2264
|
+
};
|
|
2265
|
+
// ======================================================
|
|
2266
|
+
// Optimistic updates
|
|
2267
|
+
// ======================================================
|
|
2268
|
+
applyOptimisticUpdates(request) {
|
|
2269
|
+
this._pendingOptimisticKeys.clear();
|
|
2270
|
+
const requestShape = this.def.requestShape;
|
|
2271
|
+
if (!(requestShape instanceof ValidatorDef)) {
|
|
2272
|
+
return;
|
|
2273
|
+
}
|
|
2274
|
+
this.findAndUpdateEntities(request, requestShape);
|
|
2275
|
+
}
|
|
2276
|
+
/**
|
|
2277
|
+
* Recursively walks the data according to its shape, finding and updating all entities.
|
|
2278
|
+
*/
|
|
2279
|
+
findAndUpdateEntities(value, def) {
|
|
2280
|
+
const valueType = typeMaskOf(value);
|
|
2281
|
+
const defType = def.mask;
|
|
2282
|
+
if (valueType < Mask.OBJECT || (defType & valueType) === 0) {
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
2285
|
+
if ((defType & Mask.UNION) !== 0) {
|
|
2286
|
+
const unionDef = def;
|
|
2287
|
+
if (valueType === Mask.ARRAY) {
|
|
2288
|
+
const arrayShape = unionDef.shape["[]"];
|
|
2289
|
+
if (arrayShape && typeof arrayShape !== "number") {
|
|
2290
|
+
this.findAndUpdateEntitiesInArray(value, arrayShape);
|
|
2291
|
+
}
|
|
2292
|
+
} else {
|
|
2293
|
+
const typenameField = unionDef.typenameField;
|
|
2294
|
+
const typename = typenameField ? value[typenameField] : void 0;
|
|
2295
|
+
if (typename && typeof typename === "string") {
|
|
2296
|
+
const matchingDef = unionDef.shape[typename];
|
|
2297
|
+
if (matchingDef && typeof matchingDef !== "number") {
|
|
2298
|
+
this.findAndUpdateEntitiesInObject(value, matchingDef);
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
return;
|
|
2303
|
+
}
|
|
2304
|
+
if (valueType === Mask.ARRAY) {
|
|
2305
|
+
const arrayShape = def.shape;
|
|
2306
|
+
if (arrayShape && typeof arrayShape !== "number") {
|
|
2307
|
+
this.findAndUpdateEntitiesInArray(value, arrayShape);
|
|
2308
|
+
}
|
|
2309
|
+
return;
|
|
2310
|
+
}
|
|
2311
|
+
if ((defType & Mask.RECORD) !== 0) {
|
|
2312
|
+
const recordShape = def.shape;
|
|
2313
|
+
if (recordShape && typeof recordShape !== "number") {
|
|
2314
|
+
for (const item of Object.values(value)) {
|
|
2315
|
+
this.findAndUpdateEntities(item, recordShape);
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
return;
|
|
2319
|
+
}
|
|
2320
|
+
this.findAndUpdateEntitiesInObject(value, def);
|
|
2321
|
+
}
|
|
2322
|
+
findAndUpdateEntitiesInArray(array, shape) {
|
|
2323
|
+
for (const item of array) {
|
|
2324
|
+
this.findAndUpdateEntities(item, shape);
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
findAndUpdateEntitiesInObject(obj, def) {
|
|
2328
|
+
const { mask } = def;
|
|
2329
|
+
if (mask & Mask.ENTITY) {
|
|
2330
|
+
const entityDef = def;
|
|
2331
|
+
const idField = entityDef.idField;
|
|
2332
|
+
const entityId = obj[idField];
|
|
2333
|
+
if (entityId !== void 0) {
|
|
2334
|
+
const typename = entityDef.typenameValue;
|
|
2335
|
+
const entityKey = utils.hashValue(`${typename}:${entityId}`);
|
|
2336
|
+
this.queryClient.registerOptimisticUpdate(entityKey, obj);
|
|
2337
|
+
this._pendingOptimisticKeys.add(entityKey);
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
const shape = def.shape;
|
|
2341
|
+
const subEntityPaths = def.subEntityPaths;
|
|
2342
|
+
if (subEntityPaths !== void 0) {
|
|
2343
|
+
if (typeof subEntityPaths === "string") {
|
|
2344
|
+
const propDef = shape[subEntityPaths];
|
|
2345
|
+
if (propDef && typeof propDef !== "number") {
|
|
2346
|
+
this.findAndUpdateEntities(obj[subEntityPaths], propDef);
|
|
2347
|
+
}
|
|
2348
|
+
} else {
|
|
2349
|
+
for (const path of subEntityPaths) {
|
|
2350
|
+
const propDef = shape[path];
|
|
2351
|
+
if (propDef && typeof propDef !== "number") {
|
|
2352
|
+
this.findAndUpdateEntities(obj[path], propDef);
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
revertOptimisticUpdates() {
|
|
2359
|
+
for (const entityKey of this._pendingOptimisticKeys) {
|
|
2360
|
+
this.queryClient.revertOptimisticUpdate(entityKey);
|
|
2361
|
+
}
|
|
2362
|
+
this._pendingOptimisticKeys.clear();
|
|
2363
|
+
}
|
|
2364
|
+
clearOptimisticUpdates() {
|
|
2365
|
+
for (const entityKey of this._pendingOptimisticKeys) {
|
|
2366
|
+
this.queryClient.clearOptimisticUpdates(entityKey);
|
|
2367
|
+
}
|
|
2368
|
+
this._pendingOptimisticKeys.clear();
|
|
2369
|
+
}
|
|
2370
|
+
// ======================================================
|
|
2371
|
+
// Response parsing
|
|
2372
|
+
// ======================================================
|
|
2373
|
+
parseAndUpdateEntities(response) {
|
|
2374
|
+
const responseShape = this.def.responseShape;
|
|
2375
|
+
if (!(responseShape instanceof ValidatorDef)) {
|
|
2376
|
+
return response;
|
|
2377
|
+
}
|
|
2378
|
+
const entityRefs = /* @__PURE__ */ new Set();
|
|
2379
|
+
const parsed = parseEntities(response, responseShape, this.queryClient, entityRefs);
|
|
2380
|
+
return parsed;
|
|
2381
|
+
}
|
|
2382
|
+
// ======================================================
|
|
2383
|
+
// Retry logic
|
|
2384
|
+
// ======================================================
|
|
2385
|
+
async executeWithRetry(request) {
|
|
2386
|
+
const { retries, retryDelay } = this.getRetryConfig();
|
|
2387
|
+
let lastError;
|
|
2388
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
2389
|
+
try {
|
|
2390
|
+
return await this.def.mutateFn(this.queryClient.getContext(), request);
|
|
2391
|
+
} catch (error) {
|
|
2392
|
+
lastError = error;
|
|
2393
|
+
if (attempt >= retries) {
|
|
2394
|
+
throw error;
|
|
2395
|
+
}
|
|
2396
|
+
const delay = retryDelay(attempt);
|
|
2397
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
throw lastError;
|
|
2401
|
+
}
|
|
2402
|
+
getRetryConfig() {
|
|
2403
|
+
const retryOption = this.def.cache?.retry;
|
|
2404
|
+
let retries;
|
|
2405
|
+
let retryDelay;
|
|
2406
|
+
if (retryOption === false) {
|
|
2407
|
+
retries = 0;
|
|
2408
|
+
} else if (retryOption === void 0) {
|
|
2409
|
+
retries = 0;
|
|
2410
|
+
} else if (typeof retryOption === "number") {
|
|
2411
|
+
retries = retryOption;
|
|
2412
|
+
} else {
|
|
2413
|
+
retries = retryOption.retries;
|
|
2414
|
+
}
|
|
2415
|
+
if (typeof retryOption === "object" && retryOption.retryDelay) {
|
|
2416
|
+
retryDelay = retryOption.retryDelay;
|
|
2417
|
+
} else {
|
|
2418
|
+
retryDelay = (attempt) => 1e3 * Math.pow(2, attempt);
|
|
2419
|
+
}
|
|
2420
|
+
return { retries, retryDelay };
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
const BASE_TICK_INTERVAL = 1e3;
|
|
2424
|
+
class RefetchManager {
|
|
2425
|
+
constructor(multiplier = 1) {
|
|
2426
|
+
this.multiplier = multiplier;
|
|
2427
|
+
const tickInterval = BASE_TICK_INTERVAL * this.multiplier;
|
|
2428
|
+
this.intervalId = setTimeout(() => this.tick(), tickInterval);
|
|
2429
|
+
}
|
|
2430
|
+
intervalId;
|
|
2431
|
+
clock = 0;
|
|
2432
|
+
// Increments by 1000ms on each tick
|
|
2433
|
+
// Buckets: Map of actual interval -> Set of query instances
|
|
2434
|
+
buckets = /* @__PURE__ */ new Map();
|
|
2435
|
+
addQuery(instance) {
|
|
2436
|
+
if (instance.def.type === QueryType.Stream) {
|
|
2437
|
+
return;
|
|
2438
|
+
}
|
|
2439
|
+
const interval = instance.def.cache?.refetchInterval;
|
|
2440
|
+
if (!interval) {
|
|
2441
|
+
return;
|
|
2442
|
+
}
|
|
2443
|
+
const actualInterval = interval * this.multiplier;
|
|
2444
|
+
let bucket = this.buckets.get(actualInterval);
|
|
2445
|
+
if (!bucket) {
|
|
2446
|
+
bucket = /* @__PURE__ */ new Set();
|
|
2447
|
+
this.buckets.set(actualInterval, bucket);
|
|
2448
|
+
}
|
|
2449
|
+
bucket.add(instance);
|
|
2450
|
+
}
|
|
2451
|
+
removeQuery(query2) {
|
|
2452
|
+
if (query2.def.type === QueryType.Stream) {
|
|
2453
|
+
return;
|
|
2454
|
+
}
|
|
2455
|
+
const interval = query2.def.cache?.refetchInterval;
|
|
2456
|
+
if (!interval) {
|
|
2457
|
+
return;
|
|
2458
|
+
}
|
|
2459
|
+
const actualInterval = interval * this.multiplier;
|
|
2460
|
+
const bucket = this.buckets.get(actualInterval);
|
|
2461
|
+
if (bucket) {
|
|
2462
|
+
bucket.delete(query2);
|
|
2463
|
+
if (bucket.size === 0) {
|
|
2464
|
+
this.buckets.delete(actualInterval);
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
tick() {
|
|
2469
|
+
this.clock += BASE_TICK_INTERVAL * this.multiplier;
|
|
2470
|
+
for (const [interval, bucket] of this.buckets.entries()) {
|
|
2471
|
+
if (this.clock % interval === 0) {
|
|
2472
|
+
for (const query2 of bucket) {
|
|
2473
|
+
if (query2 && !query2.isFetching) {
|
|
2474
|
+
query2.refetch();
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
const tickInterval = BASE_TICK_INTERVAL * this.multiplier;
|
|
2480
|
+
this.intervalId = setTimeout(() => this.tick(), tickInterval);
|
|
2481
|
+
}
|
|
2482
|
+
destroy() {
|
|
2483
|
+
clearTimeout(this.intervalId);
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
class NoOpRefetchManager {
|
|
2487
|
+
addQuery(_instance) {
|
|
2488
|
+
}
|
|
2489
|
+
removeQuery(_query) {
|
|
2490
|
+
}
|
|
2491
|
+
destroy() {
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
const EVICTION_INTERVAL = 60 * 1e3;
|
|
2495
|
+
class MemoryEvictionManager {
|
|
2496
|
+
// Queries to evict on tick after next
|
|
2497
|
+
constructor(queryClient, multiplier = 1) {
|
|
2498
|
+
this.queryClient = queryClient;
|
|
2499
|
+
this.multiplier = multiplier;
|
|
2500
|
+
this.intervalId = setInterval(this.tick, EVICTION_INTERVAL * this.multiplier);
|
|
2501
|
+
}
|
|
2502
|
+
intervalId;
|
|
2503
|
+
currentFlush = /* @__PURE__ */ new Set();
|
|
2504
|
+
// Queries to evict on next tick
|
|
2505
|
+
nextFlush = /* @__PURE__ */ new Set();
|
|
2506
|
+
scheduleEviction(queryKey) {
|
|
2507
|
+
this.nextFlush.add(queryKey);
|
|
2508
|
+
}
|
|
2509
|
+
cancelEviction(queryKey) {
|
|
2510
|
+
this.currentFlush.delete(queryKey);
|
|
2511
|
+
this.nextFlush.delete(queryKey);
|
|
2512
|
+
}
|
|
2513
|
+
tick = () => {
|
|
2514
|
+
if (!this.queryClient) return;
|
|
2515
|
+
for (const queryKey of this.currentFlush) {
|
|
2516
|
+
this.queryClient.queryInstances.delete(queryKey);
|
|
2517
|
+
}
|
|
2518
|
+
this.currentFlush = this.nextFlush;
|
|
2519
|
+
this.nextFlush = /* @__PURE__ */ new Set();
|
|
2520
|
+
};
|
|
2521
|
+
destroy() {
|
|
2522
|
+
clearInterval(this.intervalId);
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
class NoOpMemoryEvictionManager {
|
|
2526
|
+
scheduleEviction(_queryKey) {
|
|
2527
|
+
}
|
|
2528
|
+
cancelEviction(_queryKey) {
|
|
2529
|
+
}
|
|
2530
|
+
destroy() {
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
function resolveBaseUrl(baseUrl) {
|
|
2534
|
+
if (baseUrl === void 0) return void 0;
|
|
2535
|
+
if (typeof baseUrl === "string") return baseUrl;
|
|
2536
|
+
if (typeof baseUrl === "function") return baseUrl();
|
|
2537
|
+
return baseUrl.value;
|
|
2538
|
+
}
|
|
2539
|
+
var QueryType = /* @__PURE__ */ ((QueryType2) => {
|
|
2540
|
+
QueryType2["Query"] = "query";
|
|
2541
|
+
QueryType2["InfiniteQuery"] = "infiniteQuery";
|
|
2542
|
+
QueryType2["Stream"] = "stream";
|
|
2543
|
+
return QueryType2;
|
|
2544
|
+
})(QueryType || {});
|
|
2545
|
+
function isSignal(value) {
|
|
2546
|
+
return typeof value === "object" && value !== null;
|
|
2547
|
+
}
|
|
2548
|
+
function extractParamsForKey(params) {
|
|
2549
|
+
if (params === void 0) {
|
|
2550
|
+
return void 0;
|
|
2551
|
+
}
|
|
2552
|
+
const extracted = {};
|
|
2553
|
+
for (const [key, value] of Object.entries(params)) {
|
|
2554
|
+
if (isSignal(value)) {
|
|
2555
|
+
extracted[key] = value.value;
|
|
2556
|
+
} else {
|
|
2557
|
+
extracted[key] = value;
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
return extracted;
|
|
2561
|
+
}
|
|
2562
|
+
const queryKeyFor = (queryDef, params) => {
|
|
2563
|
+
return utils.hashValue([queryDef.id, queryDef.shapeKey, params]);
|
|
2564
|
+
};
|
|
2565
|
+
class QueryClient {
|
|
2566
|
+
constructor(store, context2 = { fetch, log: console }, networkManager, memoryEvictionManager, refetchManager) {
|
|
2567
|
+
this.store = store;
|
|
2568
|
+
this.context = context2;
|
|
2569
|
+
this.memoryEvictionManager = memoryEvictionManager ?? new MemoryEvictionManager(this, this.context.evictionMultiplier);
|
|
2570
|
+
this.refetchManager = refetchManager ?? new RefetchManager(this.context.refetchMultiplier);
|
|
2571
|
+
this.networkManager = networkManager ?? new NetworkManager();
|
|
2572
|
+
this.isServer = typeof window === "undefined";
|
|
2573
|
+
this.entityMap = new EntityStore(this);
|
|
2574
|
+
}
|
|
2575
|
+
entityMap;
|
|
2576
|
+
queryInstances = /* @__PURE__ */ new Map();
|
|
2577
|
+
mutationInstances = /* @__PURE__ */ new Map();
|
|
2578
|
+
memoryEvictionManager;
|
|
2579
|
+
refetchManager;
|
|
2580
|
+
networkManager;
|
|
2581
|
+
isServer;
|
|
2582
|
+
getContext() {
|
|
2583
|
+
return this.context;
|
|
2584
|
+
}
|
|
2585
|
+
saveQueryData(queryDef, queryKey, data, updatedAt, entityRefs, extra) {
|
|
2586
|
+
const clonedRefs = entityRefs !== void 0 ? new Set(entityRefs) : void 0;
|
|
2587
|
+
this.store.saveQuery(queryDef, queryKey, data, updatedAt, clonedRefs, extra);
|
|
2588
|
+
}
|
|
2589
|
+
activateQuery(queryInstance) {
|
|
2590
|
+
const { def, queryKey, storageKey } = queryInstance;
|
|
2591
|
+
this.store.activateQuery(def, storageKey);
|
|
2592
|
+
if (def.type !== "stream" && def.cache?.refetchInterval) {
|
|
2593
|
+
this.refetchManager.addQuery(queryInstance);
|
|
2594
|
+
}
|
|
2595
|
+
this.memoryEvictionManager.cancelEviction(queryKey);
|
|
2596
|
+
}
|
|
2597
|
+
loadCachedQuery(queryDef, queryKey) {
|
|
2598
|
+
return this.store.loadQuery(queryDef, queryKey, this.entityMap);
|
|
2599
|
+
}
|
|
2600
|
+
deleteCachedQuery(queryKey) {
|
|
2601
|
+
this.store.deleteQuery(queryKey);
|
|
2602
|
+
}
|
|
2603
|
+
/**
|
|
2604
|
+
* Loads a query from the document store and returns a QueryResult
|
|
2605
|
+
* that triggers fetches and prepopulates with cached data
|
|
2606
|
+
*/
|
|
2607
|
+
getQuery(queryDef, params) {
|
|
2608
|
+
const queryKey = queryKeyFor(queryDef, params);
|
|
2609
|
+
let queryInstance = this.queryInstances.get(queryKey);
|
|
2610
|
+
if (queryInstance === void 0) {
|
|
2611
|
+
queryInstance = new QueryResultImpl(queryDef, this, queryKey, params);
|
|
2612
|
+
this.queryInstances.set(queryKey, queryInstance);
|
|
2613
|
+
}
|
|
2614
|
+
return queryInstance;
|
|
2615
|
+
}
|
|
2616
|
+
/**
|
|
2617
|
+
* Gets or creates a MutationResult for the given mutation definition.
|
|
2618
|
+
* Mutations are cached by their definition ID.
|
|
2619
|
+
*/
|
|
2620
|
+
getMutation(mutationDef) {
|
|
2621
|
+
const mutationId = mutationDef.id;
|
|
2622
|
+
let mutationInstance = this.mutationInstances.get(mutationId);
|
|
2623
|
+
if (mutationInstance === void 0) {
|
|
2624
|
+
mutationInstance = new MutationResultImpl(mutationDef, this);
|
|
2625
|
+
this.mutationInstances.set(mutationId, mutationInstance);
|
|
2626
|
+
}
|
|
2627
|
+
return mutationInstance;
|
|
2628
|
+
}
|
|
2629
|
+
// ======================================================
|
|
2630
|
+
// Optimistic Update Management
|
|
2631
|
+
// ======================================================
|
|
2632
|
+
/**
|
|
2633
|
+
* Register pending optimistic updates for an entity.
|
|
2634
|
+
* Called by MutationResult when applying optimistic updates.
|
|
2635
|
+
*/
|
|
2636
|
+
registerOptimisticUpdate(entityKey, fields) {
|
|
2637
|
+
this.entityMap.registerOptimisticUpdate(entityKey, fields);
|
|
2638
|
+
}
|
|
2639
|
+
/**
|
|
2640
|
+
* Clear pending optimistic updates for an entity without reverting.
|
|
2641
|
+
* Called by MutationResult when mutation succeeds.
|
|
2642
|
+
*/
|
|
2643
|
+
clearOptimisticUpdates(entityKey) {
|
|
2644
|
+
this.entityMap.clearOptimisticUpdates(entityKey);
|
|
2645
|
+
}
|
|
2646
|
+
/**
|
|
2647
|
+
* Revert pending optimistic updates for an entity, restoring its snapshot.
|
|
2648
|
+
* Called by MutationResult when mutation fails.
|
|
2649
|
+
*/
|
|
2650
|
+
revertOptimisticUpdate(entityKey) {
|
|
2651
|
+
this.entityMap.revertOptimisticUpdate(entityKey);
|
|
2652
|
+
}
|
|
2653
|
+
hydrateEntity(key, shape) {
|
|
2654
|
+
return this.entityMap.hydratePreloadedEntity(key, shape);
|
|
2655
|
+
}
|
|
2656
|
+
saveEntity(key, obj, shape, entityRefs) {
|
|
2657
|
+
const record = this.entityMap.setEntity(key, obj, shape, entityRefs);
|
|
2658
|
+
this.store.saveEntity(key, obj, entityRefs);
|
|
2659
|
+
return record;
|
|
2660
|
+
}
|
|
2661
|
+
getNestedEntityRefIds(key, refIds) {
|
|
2662
|
+
return this.entityMap.getNestedEntityRefIds(key, refIds);
|
|
2663
|
+
}
|
|
2664
|
+
destroy() {
|
|
2665
|
+
this.refetchManager.destroy();
|
|
2666
|
+
this.memoryEvictionManager.destroy();
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
const QueryClientContext = signalium.context(void 0);
|
|
2670
|
+
function addOptimisticInsert(query2, insert) {
|
|
2671
|
+
query2.addOptimisticInsert(insert);
|
|
2672
|
+
}
|
|
2673
|
+
function removeOptimisticInsert(query2, insert) {
|
|
2674
|
+
query2.removeOptimisticInsert(insert);
|
|
2675
|
+
}
|
|
2676
|
+
function createPathInterpolator(pathTemplate) {
|
|
2677
|
+
const segments = [];
|
|
2678
|
+
const paramKeys = [];
|
|
2679
|
+
const paramKeysSet = /* @__PURE__ */ new Set();
|
|
2680
|
+
let lastIndex = 0;
|
|
2681
|
+
const paramRegex = /\[([^\]]+)\]/g;
|
|
2682
|
+
let match;
|
|
2683
|
+
while ((match = paramRegex.exec(pathTemplate)) !== null) {
|
|
2684
|
+
segments.push(pathTemplate.slice(lastIndex, match.index));
|
|
2685
|
+
paramKeys.push(match[1]);
|
|
2686
|
+
paramKeysSet.add(match[1]);
|
|
2687
|
+
lastIndex = paramRegex.lastIndex;
|
|
2688
|
+
}
|
|
2689
|
+
segments.push(pathTemplate.slice(lastIndex));
|
|
2690
|
+
return (params) => {
|
|
2691
|
+
let result = segments[0];
|
|
2692
|
+
for (let i = 0; i < paramKeys.length; i++) {
|
|
2693
|
+
result += encodeURIComponent(String(params[paramKeys[i]])) + segments[i + 1];
|
|
2694
|
+
}
|
|
2695
|
+
let searchParams = null;
|
|
2696
|
+
for (const key in params) {
|
|
2697
|
+
if (!paramKeysSet.has(key) && params[key] !== void 0) {
|
|
2698
|
+
if (searchParams === null) {
|
|
2699
|
+
searchParams = new URLSearchParams();
|
|
2700
|
+
}
|
|
2701
|
+
searchParams.append(key, String(params[key]));
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
if (searchParams !== null) {
|
|
2705
|
+
result += "?" + searchParams.toString();
|
|
2706
|
+
}
|
|
2707
|
+
return result;
|
|
2708
|
+
};
|
|
2709
|
+
}
|
|
2710
|
+
const QUERY_DEFINITION_MAP = /* @__PURE__ */ new Map();
|
|
2711
|
+
function buildQueryFn(queryDefinitionBuilder) {
|
|
2712
|
+
let queryDefinition;
|
|
2713
|
+
const getQueryDefinition = () => {
|
|
2714
|
+
if (queryDefinition === void 0) {
|
|
2715
|
+
const {
|
|
2716
|
+
path,
|
|
2717
|
+
method = "GET",
|
|
2718
|
+
response,
|
|
2719
|
+
requestOptions,
|
|
2720
|
+
cache,
|
|
2721
|
+
pagination,
|
|
2722
|
+
stream,
|
|
2723
|
+
optimisticInserts,
|
|
2724
|
+
debounce
|
|
2725
|
+
} = queryDefinitionBuilder();
|
|
2726
|
+
const id = `${method}:${path}`;
|
|
2727
|
+
let shape;
|
|
2728
|
+
let shapeKey;
|
|
2729
|
+
if (typeof response === "object") {
|
|
2730
|
+
if (response instanceof ValidatorDef) {
|
|
2731
|
+
shape = response;
|
|
2732
|
+
shapeKey = response.shapeKey;
|
|
2733
|
+
} else if (response instanceof Set) {
|
|
2734
|
+
shape = response;
|
|
2735
|
+
shapeKey = utils.hashValue(response);
|
|
2736
|
+
} else {
|
|
2737
|
+
shape = t.object(response);
|
|
2738
|
+
shapeKey = shape.shapeKey;
|
|
2739
|
+
}
|
|
2740
|
+
} else {
|
|
2741
|
+
shape = response;
|
|
2742
|
+
shapeKey = utils.hashValue(shape);
|
|
2743
|
+
}
|
|
2744
|
+
const interpolatePath = createPathInterpolator(path);
|
|
2745
|
+
const fetchFn = async (context, params) => {
|
|
2746
|
+
const interpolatedPath = interpolatePath(params);
|
|
2747
|
+
const baseUrl = resolveBaseUrl(requestOptions?.baseUrl) ?? resolveBaseUrl(context.baseUrl);
|
|
2748
|
+
const fullUrl = baseUrl ? `${baseUrl}${interpolatedPath}` : interpolatedPath;
|
|
2749
|
+
const { baseUrl: _baseUrl, ...fetchOptions } = requestOptions ?? {};
|
|
2750
|
+
const response2 = await context.fetch(fullUrl, {
|
|
2751
|
+
method,
|
|
2752
|
+
...fetchOptions
|
|
2753
|
+
});
|
|
2754
|
+
return response2.json();
|
|
2755
|
+
};
|
|
2756
|
+
let streamConfig = void 0;
|
|
2757
|
+
if (stream) {
|
|
2758
|
+
let streamShape;
|
|
2759
|
+
let streamShapeKey;
|
|
2760
|
+
const eventDef = stream.type;
|
|
2761
|
+
if (typeof eventDef === "object") {
|
|
2762
|
+
if (eventDef instanceof ValidatorDef) {
|
|
2763
|
+
streamShape = eventDef;
|
|
2764
|
+
streamShapeKey = eventDef.shapeKey;
|
|
2765
|
+
} else if (eventDef instanceof Set) {
|
|
2766
|
+
streamShape = eventDef;
|
|
2767
|
+
streamShapeKey = utils.hashValue(eventDef);
|
|
2768
|
+
} else {
|
|
2769
|
+
streamShape = t.object(eventDef);
|
|
2770
|
+
streamShapeKey = streamShape.shapeKey;
|
|
2771
|
+
}
|
|
2772
|
+
} else {
|
|
2773
|
+
streamShape = eventDef;
|
|
2774
|
+
streamShapeKey = utils.hashValue(streamShape);
|
|
2775
|
+
}
|
|
2776
|
+
streamConfig = {
|
|
2777
|
+
shape: streamShape,
|
|
2778
|
+
shapeKey: streamShapeKey,
|
|
2779
|
+
subscribeFn: (context, params, onUpdate) => {
|
|
2780
|
+
return stream.subscribe(context, params, onUpdate);
|
|
2781
|
+
}
|
|
2782
|
+
};
|
|
2783
|
+
}
|
|
2784
|
+
let optimisticInsertsConfig = void 0;
|
|
2785
|
+
if (optimisticInserts) {
|
|
2786
|
+
let insertShape;
|
|
2787
|
+
let insertShapeKey;
|
|
2788
|
+
const insertDef = optimisticInserts.type;
|
|
2789
|
+
if (typeof insertDef === "object") {
|
|
2790
|
+
if (insertDef instanceof ValidatorDef) {
|
|
2791
|
+
insertShape = insertDef;
|
|
2792
|
+
insertShapeKey = insertDef.shapeKey;
|
|
2793
|
+
} else if (insertDef instanceof Set) {
|
|
2794
|
+
insertShape = insertDef;
|
|
2795
|
+
insertShapeKey = utils.hashValue(insertDef);
|
|
2796
|
+
} else {
|
|
2797
|
+
insertShape = t.object(insertDef);
|
|
2798
|
+
insertShapeKey = insertShape.shapeKey;
|
|
2799
|
+
}
|
|
2800
|
+
} else {
|
|
2801
|
+
insertShape = insertDef;
|
|
2802
|
+
insertShapeKey = utils.hashValue(insertShape);
|
|
2803
|
+
}
|
|
2804
|
+
optimisticInsertsConfig = {
|
|
2805
|
+
shape: insertShape,
|
|
2806
|
+
shapeKey: insertShapeKey
|
|
2807
|
+
};
|
|
2808
|
+
}
|
|
2809
|
+
queryDefinition = {
|
|
2810
|
+
type: pagination ? QueryType.InfiniteQuery : QueryType.Query,
|
|
2811
|
+
id,
|
|
2812
|
+
shape,
|
|
2813
|
+
shapeKey,
|
|
2814
|
+
fetchFn,
|
|
2815
|
+
pagination,
|
|
2816
|
+
cache,
|
|
2817
|
+
stream: streamConfig,
|
|
2818
|
+
optimisticInserts: optimisticInsertsConfig,
|
|
2819
|
+
debounce
|
|
2820
|
+
};
|
|
2821
|
+
}
|
|
2822
|
+
return queryDefinition;
|
|
2823
|
+
};
|
|
2824
|
+
const queryFn = signalium.reactive(
|
|
2825
|
+
(params) => {
|
|
2826
|
+
const queryClient = signalium.getContext(QueryClientContext);
|
|
2827
|
+
if (queryClient === void 0) {
|
|
2828
|
+
throw new Error("QueryClient not found");
|
|
2829
|
+
}
|
|
2830
|
+
return queryClient.getQuery(getQueryDefinition(), params);
|
|
2831
|
+
}
|
|
2832
|
+
// TODO: Getting a lot of type errors due to infinite recursion here.
|
|
2833
|
+
// For now, we return as any to coerce to the external type signature,
|
|
2834
|
+
// and internally we manage the difference.
|
|
2835
|
+
);
|
|
2836
|
+
QUERY_DEFINITION_MAP.set(queryFn, getQueryDefinition);
|
|
2837
|
+
return queryFn;
|
|
2838
|
+
}
|
|
2839
|
+
function query(queryDefinitionBuilder) {
|
|
2840
|
+
return buildQueryFn(queryDefinitionBuilder);
|
|
2841
|
+
}
|
|
2842
|
+
function infiniteQuery(queryDefinitionBuilder) {
|
|
2843
|
+
return buildQueryFn(queryDefinitionBuilder);
|
|
2844
|
+
}
|
|
2845
|
+
function streamQuery(queryDefinitionBuilder) {
|
|
2846
|
+
let streamDefinition;
|
|
2847
|
+
const getStreamDefinition = () => {
|
|
2848
|
+
if (streamDefinition === void 0) {
|
|
2849
|
+
const { id, response, subscribe, cache } = queryDefinitionBuilder();
|
|
2850
|
+
if (!(response instanceof ValidatorDef) || (response.mask & Mask.ENTITY) === 0) {
|
|
2851
|
+
throw new Error("Stream query response must be an EntityDef");
|
|
2852
|
+
}
|
|
2853
|
+
streamDefinition = {
|
|
2854
|
+
type: QueryType.Stream,
|
|
2855
|
+
id,
|
|
2856
|
+
shape: response,
|
|
2857
|
+
shapeKey: response.shapeKey,
|
|
2858
|
+
subscribeFn: (context, params, onUpdate) => {
|
|
2859
|
+
return subscribe(params, onUpdate);
|
|
2860
|
+
},
|
|
2861
|
+
cache
|
|
2862
|
+
};
|
|
2863
|
+
}
|
|
2864
|
+
return streamDefinition;
|
|
2865
|
+
};
|
|
2866
|
+
const streamFn = signalium.reactive((params) => {
|
|
2867
|
+
const queryClient = signalium.getContext(QueryClientContext);
|
|
2868
|
+
if (queryClient === void 0) {
|
|
2869
|
+
throw new Error("QueryClient not found");
|
|
2870
|
+
}
|
|
2871
|
+
return queryClient.getQuery(getStreamDefinition(), params);
|
|
2872
|
+
});
|
|
2873
|
+
QUERY_DEFINITION_MAP.set(streamFn, getStreamDefinition);
|
|
2874
|
+
return streamFn;
|
|
2875
|
+
}
|
|
2876
|
+
const MUTATION_DEFINITION_MAP = /* @__PURE__ */ new Map();
|
|
2877
|
+
function processTypeDef(typeDef) {
|
|
2878
|
+
let shape;
|
|
2879
|
+
let shapeKey;
|
|
2880
|
+
if (typeof typeDef === "object") {
|
|
2881
|
+
if (typeDef instanceof ValidatorDef) {
|
|
2882
|
+
shape = typeDef;
|
|
2883
|
+
shapeKey = typeDef.shapeKey;
|
|
2884
|
+
} else if (typeDef instanceof Set) {
|
|
2885
|
+
shape = typeDef;
|
|
2886
|
+
shapeKey = utils.hashValue(typeDef);
|
|
2887
|
+
} else {
|
|
2888
|
+
shape = t.object(typeDef);
|
|
2889
|
+
shapeKey = shape.shapeKey;
|
|
2890
|
+
}
|
|
2891
|
+
} else {
|
|
2892
|
+
shape = typeDef;
|
|
2893
|
+
shapeKey = utils.hashValue(shape);
|
|
2894
|
+
}
|
|
2895
|
+
return { shape, shapeKey };
|
|
2896
|
+
}
|
|
2897
|
+
function buildMutationFn(mutationDefinitionBuilder) {
|
|
2898
|
+
let mutationDefinition;
|
|
2899
|
+
const getMutationDefinition = () => {
|
|
2900
|
+
if (mutationDefinition === void 0) {
|
|
2901
|
+
const {
|
|
2902
|
+
path,
|
|
2903
|
+
method = "POST",
|
|
2904
|
+
request,
|
|
2905
|
+
response,
|
|
2906
|
+
optimisticUpdates = false,
|
|
2907
|
+
cache
|
|
2908
|
+
} = mutationDefinitionBuilder();
|
|
2909
|
+
const id = `mutation:${method}:${path}`;
|
|
2910
|
+
const { shape: requestShape, shapeKey: requestShapeKey } = processTypeDef(request);
|
|
2911
|
+
const { shape: responseShape, shapeKey: responseShapeKey } = processTypeDef(response);
|
|
2912
|
+
const interpolatePath = createPathInterpolator(path);
|
|
2913
|
+
const pathParamNames = /* @__PURE__ */ new Set();
|
|
2914
|
+
const paramRegex = /\[([^\]]+)\]/g;
|
|
2915
|
+
let match;
|
|
2916
|
+
while ((match = paramRegex.exec(path)) !== null) {
|
|
2917
|
+
pathParamNames.add(match[1]);
|
|
2918
|
+
}
|
|
2919
|
+
const mutateFn = async (context, requestData) => {
|
|
2920
|
+
const pathParams = {};
|
|
2921
|
+
for (const paramName of pathParamNames) {
|
|
2922
|
+
pathParams[paramName] = requestData[paramName];
|
|
2923
|
+
}
|
|
2924
|
+
const url = interpolatePath(pathParams);
|
|
2925
|
+
const fetchResponse = await context.fetch(url, {
|
|
2926
|
+
method,
|
|
2927
|
+
headers: {
|
|
2928
|
+
"Content-Type": "application/json"
|
|
2929
|
+
},
|
|
2930
|
+
body: JSON.stringify(requestData)
|
|
2931
|
+
});
|
|
2932
|
+
return fetchResponse.json();
|
|
2933
|
+
};
|
|
2934
|
+
mutationDefinition = {
|
|
2935
|
+
id,
|
|
2936
|
+
requestShape,
|
|
2937
|
+
requestShapeKey,
|
|
2938
|
+
responseShape,
|
|
2939
|
+
responseShapeKey,
|
|
2940
|
+
mutateFn,
|
|
2941
|
+
optimisticUpdates,
|
|
2942
|
+
cache
|
|
2943
|
+
};
|
|
2944
|
+
}
|
|
2945
|
+
return mutationDefinition;
|
|
2946
|
+
};
|
|
2947
|
+
const mutationFn = () => {
|
|
2948
|
+
const queryClient = signalium.getContext(QueryClientContext);
|
|
2949
|
+
if (queryClient === void 0) {
|
|
2950
|
+
throw new Error("QueryClient not found");
|
|
2951
|
+
}
|
|
2952
|
+
return queryClient.getMutation(getMutationDefinition());
|
|
2953
|
+
};
|
|
2954
|
+
MUTATION_DEFINITION_MAP.set(mutationFn, getMutationDefinition);
|
|
2955
|
+
return mutationFn;
|
|
2956
|
+
}
|
|
2957
|
+
function mutation(mutationDefinitionBuilder) {
|
|
2958
|
+
return buildMutationFn(mutationDefinitionBuilder);
|
|
2959
|
+
}
|
|
2960
|
+
exports.ARRAY_KEY = ARRAY_KEY;
|
|
2961
|
+
exports.Mask = Mask;
|
|
2962
|
+
exports.MemoryEvictionManager = MemoryEvictionManager;
|
|
2963
|
+
exports.NetworkManager = NetworkManager;
|
|
2964
|
+
exports.NetworkManagerContext = NetworkManagerContext;
|
|
2965
|
+
exports.NetworkMode = NetworkMode;
|
|
2966
|
+
exports.NoOpMemoryEvictionManager = NoOpMemoryEvictionManager;
|
|
2967
|
+
exports.NoOpNetworkManager = NoOpNetworkManager;
|
|
2968
|
+
exports.NoOpRefetchManager = NoOpRefetchManager;
|
|
2969
|
+
exports.QueryClient = QueryClient;
|
|
2970
|
+
exports.QueryClientContext = QueryClientContext;
|
|
2971
|
+
exports.RECORD_KEY = RECORD_KEY;
|
|
2972
|
+
exports.RefetchInterval = RefetchInterval;
|
|
2973
|
+
exports.RefetchManager = RefetchManager;
|
|
2974
|
+
exports.addOptimisticInsert = addOptimisticInsert;
|
|
2975
|
+
exports.defaultNetworkManager = defaultNetworkManager;
|
|
2976
|
+
exports.draft = draft;
|
|
2977
|
+
exports.entity = entity;
|
|
2978
|
+
exports.infiniteQuery = infiniteQuery;
|
|
2979
|
+
exports.mutation = mutation;
|
|
2980
|
+
exports.query = query;
|
|
2981
|
+
exports.registerFormat = registerFormat;
|
|
2982
|
+
exports.removeOptimisticInsert = removeOptimisticInsert;
|
|
2983
|
+
exports.streamQuery = streamQuery;
|
|
2984
|
+
exports.t = t;
|
|
2985
|
+
//# sourceMappingURL=index.js.map
|