@mearie/core 0.0.0 → 0.0.1-next.1
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/README.md +10 -0
- package/dist/index.cjs +719 -2
- package/dist/index.d.cts +307 -2
- package/dist/index.d.ts +306 -2
- package/dist/index.js +679 -2
- package/package.json +14 -8
- package/src/index.ts +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,723 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
value: mod,
|
|
20
|
+
enumerable: true
|
|
21
|
+
}) : target, mod));
|
|
1
22
|
|
|
23
|
+
//#endregion
|
|
24
|
+
let __logtape_logtape = require("@logtape/logtape");
|
|
25
|
+
__logtape_logtape = __toESM(__logtape_logtape);
|
|
26
|
+
let picocolors = require("picocolors");
|
|
27
|
+
picocolors = __toESM(picocolors);
|
|
28
|
+
|
|
29
|
+
//#region src/errors.ts
|
|
30
|
+
/**
|
|
31
|
+
* Custom error class for Mearie-specific errors.
|
|
32
|
+
*/
|
|
33
|
+
var MearieError = class MearieError extends Error {
|
|
34
|
+
filePath;
|
|
35
|
+
line;
|
|
36
|
+
column;
|
|
37
|
+
constructor(message, filePath, line, column) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = "MearieError";
|
|
40
|
+
this.filePath = filePath;
|
|
41
|
+
this.line = line;
|
|
42
|
+
this.column = column;
|
|
43
|
+
}
|
|
44
|
+
static fromNative(data) {
|
|
45
|
+
if (!data || typeof data !== "object") throw new TypeError("Invalid native error data");
|
|
46
|
+
const error = data;
|
|
47
|
+
const filePath = error.location?.file_path;
|
|
48
|
+
const line = error.location?.line;
|
|
49
|
+
const column = error.location?.column;
|
|
50
|
+
return new MearieError(error.message, filePath, line, column);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Aggregate error for multiple Mearie errors.
|
|
55
|
+
*/
|
|
56
|
+
var MearieAggregateError = class extends Error {
|
|
57
|
+
errors;
|
|
58
|
+
constructor(errors, message) {
|
|
59
|
+
super(message ?? `${errors.length} error${errors.length > 1 ? "s" : ""} occurred`);
|
|
60
|
+
this.name = "MearieAggregateError";
|
|
61
|
+
this.errors = errors;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/utils.ts
|
|
67
|
+
/**
|
|
68
|
+
* Stable JSON serialization with sorted keys.
|
|
69
|
+
* Used for both variables and field arguments.
|
|
70
|
+
* @internal
|
|
71
|
+
* @param value - The value to stringify.
|
|
72
|
+
* @returns The stable JSON string.
|
|
73
|
+
*/
|
|
74
|
+
const stableStringify = (value) => {
|
|
75
|
+
if (value === null) return "null";
|
|
76
|
+
if (value === void 0) return "undefined";
|
|
77
|
+
const type = typeof value;
|
|
78
|
+
if (type === "string") return JSON.stringify(value);
|
|
79
|
+
if (type === "number" || type === "boolean") return String(value);
|
|
80
|
+
if (Array.isArray(value)) return "[" + value.map((v) => stableStringify(v)).join(",") + "]";
|
|
81
|
+
if (type === "object") {
|
|
82
|
+
const obj = value;
|
|
83
|
+
return "{" + Object.keys(obj).toSorted().map((k) => `"${k}":${stableStringify(obj[k])}`).join(",") + "}";
|
|
84
|
+
}
|
|
85
|
+
return JSON.stringify(value) ?? "";
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Hash a string using FNV-1a algorithm.
|
|
89
|
+
* @internal
|
|
90
|
+
* @param str - The string to hash.
|
|
91
|
+
* @returns The hash value.
|
|
92
|
+
*/
|
|
93
|
+
const hashString = (str) => {
|
|
94
|
+
const FNV_OFFSET = 2166136261;
|
|
95
|
+
const FNV_PRIME = 16777619;
|
|
96
|
+
let hash = FNV_OFFSET;
|
|
97
|
+
for (let i = 0; i < str.length; i++) {
|
|
98
|
+
hash ^= str.codePointAt(i) ?? 0;
|
|
99
|
+
hash = Math.imul(hash, FNV_PRIME);
|
|
100
|
+
}
|
|
101
|
+
return hash >>> 0;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Combine two hashes using FNV-1a algorithm.
|
|
105
|
+
* Used for query key generation.
|
|
106
|
+
* @internal
|
|
107
|
+
* @param hash1 - The first hash.
|
|
108
|
+
* @param hash2 - The second hash.
|
|
109
|
+
* @returns The combined hash.
|
|
110
|
+
*/
|
|
111
|
+
const combineHashes = (hash1, hash2) => {
|
|
112
|
+
const FNV_PRIME = 16777619;
|
|
113
|
+
let hash = hash1;
|
|
114
|
+
for (let i = 0; i < 4; i++) {
|
|
115
|
+
hash ^= hash2 >> i * 8 & 255;
|
|
116
|
+
hash = Math.imul(hash, FNV_PRIME);
|
|
117
|
+
}
|
|
118
|
+
return hash >>> 0;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/logger.ts
|
|
123
|
+
(0, __logtape_logtape.configureSync)({
|
|
124
|
+
sinks: { console: (0, __logtape_logtape.getConsoleSink)({ formatter: (0, __logtape_logtape.getAnsiColorFormatter)({
|
|
125
|
+
level: "FULL",
|
|
126
|
+
timestamp: "time",
|
|
127
|
+
category: (category) => `💬 ${category.join("·")}`
|
|
128
|
+
}) }) },
|
|
129
|
+
loggers: [{
|
|
130
|
+
category: "mearie",
|
|
131
|
+
lowestLevel: "info",
|
|
132
|
+
sinks: ["console"]
|
|
133
|
+
}, {
|
|
134
|
+
category: ["logtape", "meta"],
|
|
135
|
+
lowestLevel: "warning",
|
|
136
|
+
sinks: ["console"]
|
|
137
|
+
}]
|
|
138
|
+
});
|
|
139
|
+
const logger = (0, __logtape_logtape.getLogger)(["mearie"]);
|
|
140
|
+
const formatMearieError = (error) => {
|
|
141
|
+
const parts = [
|
|
142
|
+
error.filePath,
|
|
143
|
+
error.line,
|
|
144
|
+
error.column
|
|
145
|
+
].filter((part) => part !== void 0 && part !== null).map(String);
|
|
146
|
+
const location = parts.length > 0 ? parts.join(":") : "";
|
|
147
|
+
if (location) return `${picocolors.default.bold(error.message)} ${picocolors.default.cyan(picocolors.default.underline(location))}`;
|
|
148
|
+
return picocolors.default.bold(error.message);
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* Reports an error using the provided logger.
|
|
152
|
+
* @param logger - The logger to use.
|
|
153
|
+
* @param error - The error to report.
|
|
154
|
+
*/
|
|
155
|
+
const report = (logger$1, error) => {
|
|
156
|
+
if (error instanceof MearieAggregateError) for (const err of error.errors) logger$1.error(formatMearieError(err));
|
|
157
|
+
else if (error instanceof MearieError) logger$1.error(formatMearieError(error));
|
|
158
|
+
else logger$1.error("{error}", { error });
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
//#endregion
|
|
162
|
+
//#region src/link.ts
|
|
163
|
+
/**
|
|
164
|
+
* @param links - The chain of links to execute.
|
|
165
|
+
* @param ctx - The link context.
|
|
166
|
+
* @param finalHandler - The final handler function.
|
|
167
|
+
* @returns The link result.
|
|
168
|
+
*/
|
|
169
|
+
const executeLinks = (links, ctx, finalHandler) => {
|
|
170
|
+
let index = 0;
|
|
171
|
+
const dispatch = async () => {
|
|
172
|
+
if (index >= links.length) return await finalHandler(ctx);
|
|
173
|
+
const link = links[index++];
|
|
174
|
+
if (!link) throw new Error("Link is undefined");
|
|
175
|
+
return link.execute(ctx, dispatch);
|
|
176
|
+
};
|
|
177
|
+
return dispatch();
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
//#endregion
|
|
181
|
+
//#region src/links/dedup.ts
|
|
182
|
+
/**
|
|
183
|
+
* @returns The deduplication link.
|
|
184
|
+
*/
|
|
185
|
+
const createDedupLink = () => {
|
|
186
|
+
const pending = /* @__PURE__ */ new Map();
|
|
187
|
+
return {
|
|
188
|
+
name: "dedup",
|
|
189
|
+
async execute(ctx, next) {
|
|
190
|
+
const { document, variables } = ctx.operation;
|
|
191
|
+
const queryHash = document.hash;
|
|
192
|
+
const key = combineHashes(queryHash, variables ? hashString(stableStringify(variables)) : 0);
|
|
193
|
+
const existing = pending.get(key);
|
|
194
|
+
if (existing) return existing;
|
|
195
|
+
const promise = next();
|
|
196
|
+
pending.set(key, promise);
|
|
197
|
+
try {
|
|
198
|
+
return await promise;
|
|
199
|
+
} finally {
|
|
200
|
+
pending.delete(key);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
};
|
|
205
|
+
const dedupLink = createDedupLink;
|
|
206
|
+
|
|
207
|
+
//#endregion
|
|
208
|
+
//#region src/links/retry.ts
|
|
209
|
+
/**
|
|
210
|
+
* @param options - The retry options.
|
|
211
|
+
* @returns The retry link.
|
|
212
|
+
*/
|
|
213
|
+
const createRetryLink = (options = {}) => {
|
|
214
|
+
const { maxAttempts = 3, backoff = (attempt) => Math.min(1e3 * 2 ** attempt, 3e4), shouldRetry = () => true } = options;
|
|
215
|
+
return {
|
|
216
|
+
name: "retry",
|
|
217
|
+
async execute(ctx, next) {
|
|
218
|
+
let lastError;
|
|
219
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) try {
|
|
220
|
+
return await next();
|
|
221
|
+
} catch (error) {
|
|
222
|
+
lastError = error;
|
|
223
|
+
if (attempt === maxAttempts - 1 || !shouldRetry(error)) throw error;
|
|
224
|
+
const delay = backoff(attempt);
|
|
225
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
226
|
+
}
|
|
227
|
+
throw lastError;
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
const retryLink = createRetryLink;
|
|
232
|
+
|
|
233
|
+
//#endregion
|
|
234
|
+
//#region src/links/fetch.ts
|
|
235
|
+
/**
|
|
236
|
+
* @param options - The HTTP options.
|
|
237
|
+
* @returns The HTTP link.
|
|
238
|
+
*/
|
|
239
|
+
const createHttpLink = (options) => {
|
|
240
|
+
const { url, credentials = "same-origin", headers = {} } = options;
|
|
241
|
+
return {
|
|
242
|
+
name: "http",
|
|
243
|
+
async execute(ctx) {
|
|
244
|
+
const { document, variables, headers: operationHeaders } = ctx.operation;
|
|
245
|
+
const response = await fetch(url, {
|
|
246
|
+
method: "POST",
|
|
247
|
+
credentials,
|
|
248
|
+
headers: {
|
|
249
|
+
"Content-Type": "application/json",
|
|
250
|
+
...headers,
|
|
251
|
+
...operationHeaders
|
|
252
|
+
},
|
|
253
|
+
body: JSON.stringify({
|
|
254
|
+
query: document.body,
|
|
255
|
+
variables
|
|
256
|
+
}),
|
|
257
|
+
signal: ctx.signal
|
|
258
|
+
});
|
|
259
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
260
|
+
return response.json();
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
};
|
|
264
|
+
const httpLink = createHttpLink;
|
|
265
|
+
|
|
266
|
+
//#endregion
|
|
267
|
+
//#region src/links/auth.ts
|
|
268
|
+
/**
|
|
269
|
+
* @param options - The authentication options.
|
|
270
|
+
* @returns The authentication link.
|
|
271
|
+
*/
|
|
272
|
+
const createAuthLink = (options) => {
|
|
273
|
+
const { getToken, refreshToken, header = "Authorization" } = options;
|
|
274
|
+
return {
|
|
275
|
+
name: "auth",
|
|
276
|
+
async execute(ctx, next) {
|
|
277
|
+
const token = await getToken();
|
|
278
|
+
if (token) ctx.operation.headers = {
|
|
279
|
+
...ctx.operation.headers,
|
|
280
|
+
[header]: `Bearer ${token}`
|
|
281
|
+
};
|
|
282
|
+
try {
|
|
283
|
+
return await next();
|
|
284
|
+
} catch (error) {
|
|
285
|
+
if (refreshToken && typeof error === "object" && error !== null && "response" in error && typeof error.response === "object" && error.response !== null && "status" in error.response && error.response.status === 401) {
|
|
286
|
+
const newToken = await refreshToken();
|
|
287
|
+
ctx.operation.headers = {
|
|
288
|
+
...ctx.operation.headers,
|
|
289
|
+
[header]: `Bearer ${newToken}`
|
|
290
|
+
};
|
|
291
|
+
return next();
|
|
292
|
+
}
|
|
293
|
+
throw error;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
};
|
|
298
|
+
const authLink = createAuthLink;
|
|
299
|
+
|
|
300
|
+
//#endregion
|
|
301
|
+
//#region src/links/cache.ts
|
|
302
|
+
/**
|
|
303
|
+
* @param options - The cache options.
|
|
304
|
+
* @returns The cache link.
|
|
305
|
+
*/
|
|
306
|
+
const createCacheLink = (options) => {
|
|
307
|
+
const { cache, fetchPolicy = "cache-first" } = options;
|
|
308
|
+
return {
|
|
309
|
+
name: "cache",
|
|
310
|
+
async execute(ctx, next) {
|
|
311
|
+
const { document, variables, kind } = ctx.operation;
|
|
312
|
+
if (kind === "mutation" || kind === "subscription") {
|
|
313
|
+
const result$1 = await next();
|
|
314
|
+
if (kind === "mutation" && result$1.data) cache.writeQuery(document, variables, result$1.data);
|
|
315
|
+
return result$1;
|
|
316
|
+
}
|
|
317
|
+
if (fetchPolicy === "network-only") {
|
|
318
|
+
const result$1 = await next();
|
|
319
|
+
if (result$1.data) cache.writeQuery(document, variables, result$1.data);
|
|
320
|
+
return result$1;
|
|
321
|
+
}
|
|
322
|
+
const cached = cache.readQuery(document, variables);
|
|
323
|
+
if (fetchPolicy === "cache-only") return { data: cached ?? void 0 };
|
|
324
|
+
if (fetchPolicy === "cache-first" && cached) return { data: cached };
|
|
325
|
+
const result = await next();
|
|
326
|
+
if (result.data) cache.writeQuery(document, variables, result.data);
|
|
327
|
+
return result;
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
};
|
|
331
|
+
const cacheLink = createCacheLink;
|
|
332
|
+
|
|
333
|
+
//#endregion
|
|
334
|
+
//#region src/cache/constants.ts
|
|
335
|
+
/**
|
|
336
|
+
* Special key used to mark normalized entity references in the cache.
|
|
337
|
+
* When an entity is normalized, it's replaced with an object containing this key
|
|
338
|
+
* with the entity key as the value.
|
|
339
|
+
* @internal
|
|
340
|
+
*/
|
|
341
|
+
const EntityLinkKey = "__ref";
|
|
342
|
+
/**
|
|
343
|
+
* Special key for storing root query fields.
|
|
344
|
+
* @internal
|
|
345
|
+
*/
|
|
346
|
+
const RootFieldKey = "__root";
|
|
347
|
+
|
|
348
|
+
//#endregion
|
|
349
|
+
//#region src/cache/utils.ts
|
|
350
|
+
/**
|
|
351
|
+
* Generates a unique cache key for an entity based on its typename and key field values.
|
|
352
|
+
* @internal
|
|
353
|
+
* @param typename - The GraphQL typename of the entity.
|
|
354
|
+
* @param keyValues - Array of key field values used to identify the entity.
|
|
355
|
+
* @returns A unique entity key in the format "typename:value1:value2:...".
|
|
356
|
+
*/
|
|
357
|
+
const makeEntityKey = (typename, keyValues) => {
|
|
358
|
+
return `${typename}:${keyValues.join(":")}`;
|
|
359
|
+
};
|
|
360
|
+
/**
|
|
361
|
+
* Resolves GraphQL arguments by replacing variable references with their actual values.
|
|
362
|
+
* @internal
|
|
363
|
+
* @param args - Argument definitions from the query, containing either literal values or variable references.
|
|
364
|
+
* @param variables - Object containing the actual variable values.
|
|
365
|
+
* @returns Object with all arguments resolved to their actual values.
|
|
366
|
+
*/
|
|
367
|
+
const resolveArguments = (args, variables) => {
|
|
368
|
+
const resolved = {};
|
|
369
|
+
for (const [key, value] of Object.entries(args)) resolved[key] = value.kind === "literal" ? value.value : variables[value.name];
|
|
370
|
+
return resolved;
|
|
371
|
+
};
|
|
372
|
+
/**
|
|
373
|
+
* Generates a cache key for a GraphQL field selection.
|
|
374
|
+
* Always uses the actual field name (not alias) with a hash of the arguments.
|
|
375
|
+
* @internal
|
|
376
|
+
* @param selection - The selection node containing field information.
|
|
377
|
+
* @param variables - Variable values for resolving argument references.
|
|
378
|
+
* @returns Field cache key string in "fieldName@argsHash" format.
|
|
379
|
+
*/
|
|
380
|
+
const makeFieldKey = (selection, variables) => {
|
|
381
|
+
const argsHash = hashString(stableStringify(selection.args ? resolveArguments(selection.args, variables) : {}));
|
|
382
|
+
return `${selection.name}@${argsHash}`;
|
|
383
|
+
};
|
|
384
|
+
/**
|
|
385
|
+
* Retrieves entity metadata from the schema for a given typename.
|
|
386
|
+
* @internal
|
|
387
|
+
* @param typename - The GraphQL typename to look up.
|
|
388
|
+
* @param schemaMetadata - The schema metadata containing entity configurations.
|
|
389
|
+
* @returns Entity metadata if found, undefined otherwise.
|
|
390
|
+
*/
|
|
391
|
+
const getEntityMetadata = (typename, schemaMetadata) => {
|
|
392
|
+
return typename ? schemaMetadata.entities[typename] : void 0;
|
|
393
|
+
};
|
|
394
|
+
/**
|
|
395
|
+
* Creates a unique query key combining document hash and variables.
|
|
396
|
+
* @internal
|
|
397
|
+
* @param hash - Document hash.
|
|
398
|
+
* @param variables - Query variables.
|
|
399
|
+
* @returns Unique query key.
|
|
400
|
+
*/
|
|
401
|
+
const makeQueryKey = (hash, variables) => {
|
|
402
|
+
if (!variables || typeof variables === "object" && Object.keys(variables).length === 0) return hash;
|
|
403
|
+
return combineHashes(hash, hashString(stableStringify(variables)));
|
|
404
|
+
};
|
|
405
|
+
/**
|
|
406
|
+
* Gets a unique key for tracking a field dependency.
|
|
407
|
+
* @internal
|
|
408
|
+
* @param storageKey Storage key (entity or root query key).
|
|
409
|
+
* @param fieldKey Field key.
|
|
410
|
+
* @returns Unique dependency key in the format "storageKey.field".
|
|
411
|
+
*/
|
|
412
|
+
const makeDependencyKey = (storageKey, fieldKey) => {
|
|
413
|
+
return `${storageKey}.${fieldKey}`;
|
|
414
|
+
};
|
|
415
|
+
/**
|
|
416
|
+
* Type guard to check if a value is an entity link.
|
|
417
|
+
* @internal
|
|
418
|
+
* @param value - Value to check.
|
|
419
|
+
* @returns True if the value is an EntityLink.
|
|
420
|
+
*/
|
|
421
|
+
const isEntityLink = (value) => {
|
|
422
|
+
return typeof value === "object" && value !== null && EntityLinkKey in value;
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
//#endregion
|
|
426
|
+
//#region src/cache/normalize.ts
|
|
427
|
+
/**
|
|
428
|
+
* @param data - The data to normalize.
|
|
429
|
+
* @param selections - The selection nodes.
|
|
430
|
+
* @param schemaMetadata - The schema metadata.
|
|
431
|
+
* @param storage - The normalized storage map.
|
|
432
|
+
* @param variables - The variable values.
|
|
433
|
+
* @param accessor - Callback invoked when a field dependency is encountered.
|
|
434
|
+
*/
|
|
435
|
+
const normalize = (data, selections, schemaMetadata, storage, variables, accessor) => {
|
|
436
|
+
const normalizeField = (parentKey, selections$1, value) => {
|
|
437
|
+
if (value === null || value === void 0) return value;
|
|
438
|
+
if (typeof value !== "object" || Array.isArray(value)) return value;
|
|
439
|
+
const data$1 = value;
|
|
440
|
+
const fields = {};
|
|
441
|
+
for (const selection of selections$1) {
|
|
442
|
+
const fieldKey = makeFieldKey(selection, variables);
|
|
443
|
+
const fieldValue = data$1[selection.alias ?? selection.name];
|
|
444
|
+
accessor(parentKey, fieldKey);
|
|
445
|
+
if (fieldValue === void 0) continue;
|
|
446
|
+
if (Array.isArray(fieldValue)) fields[fieldKey] = fieldValue.map((item) => selection.selections ? normalizeField(parentKey, selection.selections, item) : item);
|
|
447
|
+
else if (selection.selections) fields[fieldKey] = normalizeField(parentKey, selection.selections, fieldValue);
|
|
448
|
+
else fields[fieldKey] = fieldValue;
|
|
449
|
+
}
|
|
450
|
+
const typename = data$1.__typename;
|
|
451
|
+
const entityMetadata = typename ? getEntityMetadata(typename, schemaMetadata) : void 0;
|
|
452
|
+
if (entityMetadata) {
|
|
453
|
+
const keyValues = [];
|
|
454
|
+
for (const field of entityMetadata.keyFields) {
|
|
455
|
+
const fieldValue = data$1[field];
|
|
456
|
+
if (fieldValue == null) return fields;
|
|
457
|
+
keyValues.push(fieldValue);
|
|
458
|
+
}
|
|
459
|
+
const entityKey = makeEntityKey(typename, keyValues);
|
|
460
|
+
const normalized = {
|
|
461
|
+
...storage.get(entityKey) ?? {},
|
|
462
|
+
...fields
|
|
463
|
+
};
|
|
464
|
+
storage.set(entityKey, normalized);
|
|
465
|
+
return { [EntityLinkKey]: entityKey };
|
|
466
|
+
}
|
|
467
|
+
return fields;
|
|
468
|
+
};
|
|
469
|
+
if (data === null || typeof data !== "object") return;
|
|
470
|
+
const existingRoot = storage.get(RootFieldKey) ?? {};
|
|
471
|
+
const result = normalizeField(RootFieldKey, selections, data);
|
|
472
|
+
storage.set(RootFieldKey, {
|
|
473
|
+
...existingRoot,
|
|
474
|
+
...result
|
|
475
|
+
});
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
//#endregion
|
|
479
|
+
//#region src/cache/denormalize.ts
|
|
480
|
+
/**
|
|
481
|
+
* @param value - The value to denormalize.
|
|
482
|
+
* @param selection - The selection node.
|
|
483
|
+
* @param storage - The normalized storage map.
|
|
484
|
+
* @param variables - The variable values.
|
|
485
|
+
* @param sourceData - The source data object.
|
|
486
|
+
* @returns The denormalized value.
|
|
487
|
+
*/
|
|
488
|
+
const denormalizeValue = (value, selection, storage, variables, sourceData) => {
|
|
489
|
+
if (value === null || value === void 0) return value;
|
|
490
|
+
if (selection.array && Array.isArray(value)) return value.map((item) => denormalizeValue(item, selection, storage, variables, sourceData));
|
|
491
|
+
if (typeof value !== "object" || Array.isArray(value)) return value;
|
|
492
|
+
const data = value;
|
|
493
|
+
let source = sourceData ?? data;
|
|
494
|
+
if (isEntityLink(data)) {
|
|
495
|
+
const entity = storage.get(data[EntityLinkKey]);
|
|
496
|
+
if (!entity || !selection.selections) return value;
|
|
497
|
+
source = entity;
|
|
498
|
+
sourceData = entity;
|
|
499
|
+
}
|
|
500
|
+
if (!selection.selections) return value;
|
|
501
|
+
const result = {};
|
|
502
|
+
for (const childSelection of selection.selections) {
|
|
503
|
+
const fieldKey = sourceData ? makeFieldKey(childSelection, variables) : childSelection.alias ?? childSelection.name;
|
|
504
|
+
const responseKey = childSelection.alias ?? childSelection.name;
|
|
505
|
+
const fieldValue = source[fieldKey];
|
|
506
|
+
if (fieldValue !== void 0) result[responseKey] = denormalizeValue(fieldValue, childSelection, storage, variables, sourceData);
|
|
507
|
+
}
|
|
508
|
+
return result;
|
|
509
|
+
};
|
|
510
|
+
/**
|
|
511
|
+
* @param selections - The selection nodes.
|
|
512
|
+
* @param storage - The normalized storage map.
|
|
513
|
+
* @param variables - The variable values.
|
|
514
|
+
* @returns The denormalized data.
|
|
515
|
+
*/
|
|
516
|
+
const denormalize = (selections, storage, variables) => {
|
|
517
|
+
const queryRoot = storage.get(RootFieldKey);
|
|
518
|
+
if (!queryRoot) return null;
|
|
519
|
+
const result = {};
|
|
520
|
+
for (const selection of selections) {
|
|
521
|
+
const fieldKey = makeFieldKey(selection, variables);
|
|
522
|
+
const responseKey = selection.alias ?? selection.name;
|
|
523
|
+
const fieldValue = queryRoot[fieldKey];
|
|
524
|
+
if (fieldValue !== void 0) result[responseKey] = denormalizeValue(fieldValue, selection, storage, variables, queryRoot);
|
|
525
|
+
}
|
|
526
|
+
return Object.keys(result).length === 0 ? null : result;
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
//#endregion
|
|
530
|
+
//#region src/cache/cache.ts
|
|
531
|
+
/**
|
|
532
|
+
* A normalized cache that stores and manages GraphQL query results and entities.
|
|
533
|
+
* Supports entity normalization, cache invalidation, and reactive updates through subscriptions.
|
|
534
|
+
*/
|
|
535
|
+
var Cache = class {
|
|
536
|
+
#storage = /* @__PURE__ */ new Map();
|
|
537
|
+
#dependencies = /* @__PURE__ */ new Map();
|
|
538
|
+
#listeners = /* @__PURE__ */ new Map();
|
|
539
|
+
#schemaMetadata;
|
|
540
|
+
constructor(schemaMetadata) {
|
|
541
|
+
this.#schemaMetadata = schemaMetadata;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Writes a query result to the cache, normalizing entities and tracking dependencies.
|
|
545
|
+
* @param document - GraphQL document node.
|
|
546
|
+
* @param variables - Query variables.
|
|
547
|
+
* @param result - Query result data.
|
|
548
|
+
*/
|
|
549
|
+
writeQuery(document, variables, result) {
|
|
550
|
+
const queryKey = makeQueryKey(document.hash, variables);
|
|
551
|
+
normalize(result, document.selections, this.#schemaMetadata, this.#storage, variables, (storageKey, fieldKey) => {
|
|
552
|
+
this.#trackDependency(queryKey, storageKey, fieldKey);
|
|
553
|
+
});
|
|
554
|
+
this.#notifyListeners(queryKey);
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Reads a query result from the cache, denormalizing entities if available.
|
|
558
|
+
* @param document - GraphQL document node.
|
|
559
|
+
* @param variables - Query variables.
|
|
560
|
+
* @returns Denormalized query result or null if not found.
|
|
561
|
+
*/
|
|
562
|
+
readQuery(document, variables) {
|
|
563
|
+
return denormalize(document.selections, this.#storage, variables);
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Subscribes to cache invalidations for a specific query.
|
|
567
|
+
* @param document - GraphQL document node.
|
|
568
|
+
* @param variables - Query variables.
|
|
569
|
+
* @param callback - Callback function to invoke on cache invalidation.
|
|
570
|
+
* @returns Unsubscribe function.
|
|
571
|
+
*/
|
|
572
|
+
subscribe(document, variables, callback) {
|
|
573
|
+
const queryKey = makeQueryKey(document.hash, variables);
|
|
574
|
+
let listeners = this.#listeners.get(queryKey);
|
|
575
|
+
if (!listeners) {
|
|
576
|
+
listeners = /* @__PURE__ */ new Set();
|
|
577
|
+
this.#listeners.set(queryKey, listeners);
|
|
578
|
+
}
|
|
579
|
+
listeners.add(callback);
|
|
580
|
+
return () => {
|
|
581
|
+
listeners.delete(callback);
|
|
582
|
+
if (listeners.size === 0) this.#listeners.delete(queryKey);
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Evicts a query from the cache and notifies listeners.
|
|
587
|
+
* @param document - GraphQL document node.
|
|
588
|
+
* @param variables - Query variables.
|
|
589
|
+
*/
|
|
590
|
+
evictQuery(document, variables) {
|
|
591
|
+
const queryKey = makeQueryKey(document.hash, variables);
|
|
592
|
+
const queryRoot = this.#storage.get(RootFieldKey);
|
|
593
|
+
if (queryRoot) for (const selection of document.selections) {
|
|
594
|
+
const fieldKey = makeFieldKey(selection, variables);
|
|
595
|
+
delete queryRoot[fieldKey];
|
|
596
|
+
}
|
|
597
|
+
this.#notifyListeners(queryKey);
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Clears all cache data.
|
|
601
|
+
*/
|
|
602
|
+
clear() {
|
|
603
|
+
this.#storage.clear();
|
|
604
|
+
this.#dependencies.clear();
|
|
605
|
+
this.#listeners.clear();
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Tracks dependency between a query and a storage field.
|
|
609
|
+
* @param queryKey - Query key to track.
|
|
610
|
+
* @param storageKey - Storage key (entity or root).
|
|
611
|
+
* @param fieldKey - Field key.
|
|
612
|
+
*/
|
|
613
|
+
#trackDependency(queryKey, storageKey, fieldKey) {
|
|
614
|
+
const dependencyKey = makeDependencyKey(storageKey, fieldKey);
|
|
615
|
+
let queryKeys = this.#dependencies.get(dependencyKey);
|
|
616
|
+
if (!queryKeys) {
|
|
617
|
+
queryKeys = /* @__PURE__ */ new Set();
|
|
618
|
+
this.#dependencies.set(dependencyKey, queryKeys);
|
|
619
|
+
}
|
|
620
|
+
queryKeys.add(queryKey);
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Notifies all listeners subscribed to a query.
|
|
624
|
+
* @param queryKey - Query key to notify listeners for.
|
|
625
|
+
*/
|
|
626
|
+
#notifyListeners(queryKey) {
|
|
627
|
+
const listeners = this.#listeners.get(queryKey);
|
|
628
|
+
if (listeners) for (const listener of listeners) listener();
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
//#endregion
|
|
633
|
+
//#region src/client.ts
|
|
634
|
+
/**
|
|
635
|
+
* GraphQL client for executing queries and mutations.
|
|
636
|
+
*/
|
|
637
|
+
var Client = class {
|
|
638
|
+
links;
|
|
639
|
+
cache;
|
|
640
|
+
/**
|
|
641
|
+
* @param config - The client configuration.
|
|
642
|
+
*/
|
|
643
|
+
constructor(config) {
|
|
644
|
+
this.links = config.links.map((link) => typeof link === "function" ? link() : link);
|
|
645
|
+
this.cache = config.cache;
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* @param document - The query document.
|
|
649
|
+
* @param variables - The query variables.
|
|
650
|
+
* @returns The query result.
|
|
651
|
+
*/
|
|
652
|
+
async query(document, variables) {
|
|
653
|
+
return { data: {} };
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* @param document - The mutation document.
|
|
657
|
+
* @param variables - The mutation variables.
|
|
658
|
+
* @returns The mutation result.
|
|
659
|
+
*/
|
|
660
|
+
async mutate(document, variables) {
|
|
661
|
+
return { data: {} };
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* @param document - The mutation document.
|
|
665
|
+
* @param variables - The mutation variables.
|
|
666
|
+
* @returns The mutation result.
|
|
667
|
+
*/
|
|
668
|
+
async mutation(document, variables) {
|
|
669
|
+
return { data: {} };
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* @param document - The subscription document.
|
|
673
|
+
* @param variables - The subscription variables.
|
|
674
|
+
* @returns An observable of subscription results.
|
|
675
|
+
*/
|
|
676
|
+
subscription(document, variables) {
|
|
677
|
+
return { subscribe: () => ({ unsubscribe: () => {} }) };
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* @param fragment - The fragment document.
|
|
681
|
+
* @param fragmentRef - The fragment reference data.
|
|
682
|
+
* @returns The fragment data.
|
|
683
|
+
*/
|
|
684
|
+
readFragment(fragment, fragmentRef) {
|
|
685
|
+
return {};
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* @returns The normalized cache instance.
|
|
689
|
+
*/
|
|
690
|
+
getCache() {
|
|
691
|
+
return this.cache;
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
/**
|
|
695
|
+
* @param config - The client configuration.
|
|
696
|
+
* @returns A new client instance.
|
|
697
|
+
*/
|
|
698
|
+
const createClient = (config) => {
|
|
699
|
+
return new Client(config);
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
//#endregion
|
|
2
703
|
//#region src/index.ts
|
|
3
|
-
|
|
704
|
+
const graphql = () => {};
|
|
4
705
|
|
|
5
706
|
//#endregion
|
|
6
|
-
|
|
707
|
+
exports.Client = Client;
|
|
708
|
+
exports.MearieAggregateError = MearieAggregateError;
|
|
709
|
+
exports.MearieError = MearieError;
|
|
710
|
+
exports.NormalizedCache = Cache;
|
|
711
|
+
exports.authLink = authLink;
|
|
712
|
+
exports.cacheLink = cacheLink;
|
|
713
|
+
exports.combineHashes = combineHashes;
|
|
714
|
+
exports.createClient = createClient;
|
|
715
|
+
exports.dedupLink = dedupLink;
|
|
716
|
+
exports.executeLinks = executeLinks;
|
|
717
|
+
exports.graphql = graphql;
|
|
718
|
+
exports.hashString = hashString;
|
|
719
|
+
exports.httpLink = httpLink;
|
|
720
|
+
exports.logger = logger;
|
|
721
|
+
exports.report = report;
|
|
722
|
+
exports.retryLink = retryLink;
|
|
723
|
+
exports.stableStringify = stableStringify;
|