layercache 1.2.8 → 1.3.0
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 +11 -4
- package/benchmarks/direct.ts +221 -0
- package/benchmarks/edge-utils.ts +28 -0
- package/benchmarks/edge.ts +491 -0
- package/benchmarks/http.ts +99 -0
- package/benchmarks/memory-pressure.ts +144 -0
- package/benchmarks/multi-process-fanout.ts +231 -0
- package/benchmarks/multi-process-worker.ts +151 -0
- package/benchmarks/paths.ts +25 -0
- package/benchmarks/queue-amplification-utils.ts +48 -0
- package/benchmarks/queue-amplification.ts +230 -0
- package/benchmarks/redis-latency-proxy.ts +100 -0
- package/benchmarks/redis.ts +107 -0
- package/benchmarks/scenario-utils.ts +38 -0
- package/benchmarks/server.ts +157 -0
- package/benchmarks/slow-redis-latency.ts +309 -0
- package/benchmarks/slow-redis-utils.ts +29 -0
- package/benchmarks/slow-redis.ts +47 -0
- package/benchmarks/stats.ts +46 -0
- package/benchmarks/workload.ts +77 -0
- package/dist/cli.cjs +14 -1
- package/dist/cli.js +14 -1
- package/dist/{edge-DBs8Ko5W.d.cts → edge-BXWTKlI1.d.cts} +1 -0
- package/dist/{edge-DBs8Ko5W.d.ts → edge-BXWTKlI1.d.ts} +1 -0
- package/dist/edge.d.cts +1 -1
- package/dist/edge.d.ts +1 -1
- package/dist/index.cjs +304 -112
- package/dist/index.d.cts +17 -4
- package/dist/index.d.ts +17 -4
- package/dist/index.js +301 -109
- package/package.json +12 -2
- package/packages/nestjs/dist/index.cjs +146 -68
- package/packages/nestjs/dist/index.d.cts +1 -0
- package/packages/nestjs/dist/index.d.ts +1 -0
- package/packages/nestjs/dist/index.js +146 -68
|
@@ -688,7 +688,7 @@ function normalizeForSerialization(value) {
|
|
|
688
688
|
}
|
|
689
689
|
function serializeKeyPart(value) {
|
|
690
690
|
if (typeof value === "string") {
|
|
691
|
-
return `s:${value}`;
|
|
691
|
+
return `s:${value.replace(/%/g, "%25").replace(/:/g, "%3A")}`;
|
|
692
692
|
}
|
|
693
693
|
if (typeof value === "number") {
|
|
694
694
|
return `n:${value}`;
|
|
@@ -1077,6 +1077,7 @@ var CacheStackLayerWriter = class {
|
|
|
1077
1077
|
}
|
|
1078
1078
|
const results = await Promise.allSettled(operations.map((operation) => operation()));
|
|
1079
1079
|
const failures = results.filter((result) => result.status === "rejected");
|
|
1080
|
+
const degraded = results.filter((result) => result.status === "fulfilled");
|
|
1080
1081
|
if (failures.length === 0) {
|
|
1081
1082
|
return;
|
|
1082
1083
|
}
|
|
@@ -1255,6 +1256,7 @@ function planFreshReadPolicies({
|
|
|
1255
1256
|
}
|
|
1256
1257
|
|
|
1257
1258
|
// ../../src/internal/CacheStackSnapshotManager.ts
|
|
1259
|
+
import { randomBytes } from "crypto";
|
|
1258
1260
|
import { constants, promises as fs } from "fs";
|
|
1259
1261
|
import path from "path";
|
|
1260
1262
|
|
|
@@ -1354,6 +1356,42 @@ async function readUtf8HandleWithLimit(handle, byteLimit) {
|
|
|
1354
1356
|
return Buffer.concat(chunks).toString("utf8");
|
|
1355
1357
|
}
|
|
1356
1358
|
|
|
1359
|
+
// ../../src/internal/StructuredDataSanitizer.ts
|
|
1360
|
+
var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
1361
|
+
function sanitizeStructuredData(value, options) {
|
|
1362
|
+
return sanitizeValue(value, 0, { count: 0 }, options);
|
|
1363
|
+
}
|
|
1364
|
+
function sanitizeValue(value, depth, state, options) {
|
|
1365
|
+
state.count += 1;
|
|
1366
|
+
if (state.count > options.maxNodes) {
|
|
1367
|
+
throw new Error(`${options.label} exceeds max node count of ${options.maxNodes}.`);
|
|
1368
|
+
}
|
|
1369
|
+
if (depth > options.maxDepth) {
|
|
1370
|
+
throw new Error(`${options.label} exceeds max depth of ${options.maxDepth}.`);
|
|
1371
|
+
}
|
|
1372
|
+
if (Array.isArray(value)) {
|
|
1373
|
+
const sanitized2 = [];
|
|
1374
|
+
for (const entry of value) {
|
|
1375
|
+
sanitized2.push(sanitizeValue(entry, depth + 1, state, options));
|
|
1376
|
+
}
|
|
1377
|
+
return sanitized2;
|
|
1378
|
+
}
|
|
1379
|
+
if (!isPlainObject(value)) {
|
|
1380
|
+
return value;
|
|
1381
|
+
}
|
|
1382
|
+
const sanitized = options.createObject?.() ?? {};
|
|
1383
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
1384
|
+
if (DANGEROUS_KEYS.has(key)) {
|
|
1385
|
+
continue;
|
|
1386
|
+
}
|
|
1387
|
+
sanitized[key] = sanitizeValue(entry, depth + 1, state, options);
|
|
1388
|
+
}
|
|
1389
|
+
return sanitized;
|
|
1390
|
+
}
|
|
1391
|
+
function isPlainObject(value) {
|
|
1392
|
+
return Object.prototype.toString.call(value) === "[object Object]";
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1357
1395
|
// ../../src/internal/CacheStackSnapshotManager.ts
|
|
1358
1396
|
var DEFAULT_SNAPSHOT_IMPORT_BATCH_SIZE = 50;
|
|
1359
1397
|
var CacheStackSnapshotManager = class {
|
|
@@ -1378,7 +1416,16 @@ var CacheStackSnapshotManager = class {
|
|
|
1378
1416
|
const batch = normalizedEntries.slice(index, index + DEFAULT_SNAPSHOT_IMPORT_BATCH_SIZE);
|
|
1379
1417
|
await Promise.all(
|
|
1380
1418
|
batch.map(async (entry) => {
|
|
1381
|
-
await Promise.all(
|
|
1419
|
+
await Promise.all(
|
|
1420
|
+
this.options.layers.map(async (layer) => {
|
|
1421
|
+
if (this.options.shouldSkipLayer(layer)) return;
|
|
1422
|
+
try {
|
|
1423
|
+
await layer.set(entry.key, entry.value, entry.ttl);
|
|
1424
|
+
} catch (error) {
|
|
1425
|
+
await this.options.handleLayerFailure(layer, "write", error);
|
|
1426
|
+
}
|
|
1427
|
+
})
|
|
1428
|
+
);
|
|
1382
1429
|
await this.options.tagIndex.touch(entry.key);
|
|
1383
1430
|
})
|
|
1384
1431
|
);
|
|
@@ -1388,7 +1435,7 @@ var CacheStackSnapshotManager = class {
|
|
|
1388
1435
|
const targetPath = await validateSnapshotFilePath(filePath, "write", snapshotBaseDir);
|
|
1389
1436
|
const tempPath = path.join(
|
|
1390
1437
|
path.dirname(targetPath),
|
|
1391
|
-
`.layercache-${process.pid}-${Date.now()}-${
|
|
1438
|
+
`.layercache-${process.pid}-${Date.now()}-${randomBytes(8).toString("hex")}.tmp`
|
|
1392
1439
|
);
|
|
1393
1440
|
let handle;
|
|
1394
1441
|
try {
|
|
@@ -1488,7 +1535,13 @@ var CacheStackSnapshotManager = class {
|
|
|
1488
1535
|
});
|
|
1489
1536
|
}
|
|
1490
1537
|
sanitizeSnapshotValue(value) {
|
|
1491
|
-
|
|
1538
|
+
const roundTripped = this.options.snapshotSerializer.deserialize(this.options.snapshotSerializer.serialize(value));
|
|
1539
|
+
return sanitizeStructuredData(roundTripped, {
|
|
1540
|
+
label: "Snapshot value",
|
|
1541
|
+
maxDepth: 64,
|
|
1542
|
+
maxNodes: 1e4,
|
|
1543
|
+
createObject: () => /* @__PURE__ */ Object.create(null)
|
|
1544
|
+
});
|
|
1492
1545
|
}
|
|
1493
1546
|
};
|
|
1494
1547
|
|
|
@@ -1868,7 +1921,13 @@ var FetchRateLimiter = class {
|
|
|
1868
1921
|
this.pendingBuckets.add(next.bucketKey);
|
|
1869
1922
|
}
|
|
1870
1923
|
this.cleanupBucket(next.bucketKey, bucket, next.options.intervalMs);
|
|
1871
|
-
this.
|
|
1924
|
+
if (!this.drainTimer) {
|
|
1925
|
+
this.drainTimer = setTimeout(() => {
|
|
1926
|
+
this.drainTimer = void 0;
|
|
1927
|
+
this.drain();
|
|
1928
|
+
}, 0);
|
|
1929
|
+
this.drainTimer.unref?.();
|
|
1930
|
+
}
|
|
1872
1931
|
});
|
|
1873
1932
|
}
|
|
1874
1933
|
}
|
|
@@ -1910,6 +1969,9 @@ var FetchRateLimiter = class {
|
|
|
1910
1969
|
}
|
|
1911
1970
|
if (this.buckets.size >= MAX_BUCKETS) {
|
|
1912
1971
|
this.evictIdleBuckets();
|
|
1972
|
+
if (this.buckets.size >= MAX_BUCKETS) {
|
|
1973
|
+
throw new Error(`FetchRateLimiter bucket limit (${MAX_BUCKETS}) exceeded.`);
|
|
1974
|
+
}
|
|
1913
1975
|
}
|
|
1914
1976
|
const bucket = { active: 0, startedAt: [] };
|
|
1915
1977
|
this.buckets.set(bucketKey, bucket);
|
|
@@ -2388,38 +2450,6 @@ var TagIndex = class {
|
|
|
2388
2450
|
}
|
|
2389
2451
|
};
|
|
2390
2452
|
|
|
2391
|
-
// ../../src/internal/StructuredDataSanitizer.ts
|
|
2392
|
-
var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
2393
|
-
function sanitizeStructuredData(value, options) {
|
|
2394
|
-
return sanitizeValue(value, 0, { count: 0 }, options);
|
|
2395
|
-
}
|
|
2396
|
-
function sanitizeValue(value, depth, state, options) {
|
|
2397
|
-
state.count += 1;
|
|
2398
|
-
if (state.count > options.maxNodes) {
|
|
2399
|
-
throw new Error(`${options.label} exceeds max node count of ${options.maxNodes}.`);
|
|
2400
|
-
}
|
|
2401
|
-
if (depth > options.maxDepth) {
|
|
2402
|
-
throw new Error(`${options.label} exceeds max depth of ${options.maxDepth}.`);
|
|
2403
|
-
}
|
|
2404
|
-
if (Array.isArray(value)) {
|
|
2405
|
-
return value.map((entry) => sanitizeValue(entry, depth + 1, state, options));
|
|
2406
|
-
}
|
|
2407
|
-
if (!isPlainObject(value)) {
|
|
2408
|
-
return value;
|
|
2409
|
-
}
|
|
2410
|
-
const sanitized = options.createObject?.() ?? {};
|
|
2411
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
2412
|
-
if (DANGEROUS_KEYS.has(key)) {
|
|
2413
|
-
continue;
|
|
2414
|
-
}
|
|
2415
|
-
sanitized[key] = sanitizeValue(entry, depth + 1, state, options);
|
|
2416
|
-
}
|
|
2417
|
-
return sanitized;
|
|
2418
|
-
}
|
|
2419
|
-
function isPlainObject(value) {
|
|
2420
|
-
return Object.prototype.toString.call(value) === "[object Object]";
|
|
2421
|
-
}
|
|
2422
|
-
|
|
2423
2453
|
// ../../src/serialization/JsonSerializer.ts
|
|
2424
2454
|
var JsonSerializer = class {
|
|
2425
2455
|
serialize(value) {
|
|
@@ -2437,27 +2467,34 @@ var JsonSerializer = class {
|
|
|
2437
2467
|
|
|
2438
2468
|
// ../../src/stampede/StampedeGuard.ts
|
|
2439
2469
|
var StampedeGuard = class {
|
|
2440
|
-
|
|
2470
|
+
inFlight = /* @__PURE__ */ new Map();
|
|
2441
2471
|
async execute(key, task) {
|
|
2442
|
-
const
|
|
2472
|
+
const existing = this.inFlight.get(key);
|
|
2473
|
+
if (existing) {
|
|
2474
|
+
existing.references += 1;
|
|
2475
|
+
try {
|
|
2476
|
+
return await existing.promise;
|
|
2477
|
+
} finally {
|
|
2478
|
+
this.releaseEntry(key, existing);
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
const entry = {
|
|
2482
|
+
promise: Promise.resolve().then(task),
|
|
2483
|
+
references: 1
|
|
2484
|
+
};
|
|
2485
|
+
this.inFlight.set(key, entry);
|
|
2443
2486
|
try {
|
|
2444
|
-
return await entry.
|
|
2487
|
+
return await entry.promise;
|
|
2445
2488
|
} finally {
|
|
2446
|
-
|
|
2447
|
-
const current = this.mutexes.get(key);
|
|
2448
|
-
if (current === entry && entry.references === 0 && !entry.mutex.isLocked()) {
|
|
2449
|
-
this.mutexes.delete(key);
|
|
2450
|
-
}
|
|
2489
|
+
this.releaseEntry(key, entry);
|
|
2451
2490
|
}
|
|
2452
2491
|
}
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
this.
|
|
2492
|
+
releaseEntry(key, entry) {
|
|
2493
|
+
entry.references -= 1;
|
|
2494
|
+
const current = this.inFlight.get(key);
|
|
2495
|
+
if (current === entry && entry.references === 0) {
|
|
2496
|
+
this.inFlight.delete(key);
|
|
2458
2497
|
}
|
|
2459
|
-
entry.references += 1;
|
|
2460
|
-
return entry;
|
|
2461
2498
|
}
|
|
2462
2499
|
};
|
|
2463
2500
|
|
|
@@ -2583,6 +2620,8 @@ var CacheStack = class extends EventEmitter {
|
|
|
2583
2620
|
tagIndex: this.tagIndex,
|
|
2584
2621
|
snapshotSerializer: this.snapshotSerializer,
|
|
2585
2622
|
readLayerEntry: this.readLayerEntry.bind(this),
|
|
2623
|
+
shouldSkipLayer: (layer) => this.shouldSkipLayer(layer),
|
|
2624
|
+
handleLayerFailure: async (layer, operation, error) => this.handleLayerFailure(layer, operation, error),
|
|
2586
2625
|
qualifyKey: this.qualifyKey.bind(this),
|
|
2587
2626
|
stripQualifiedKey: this.stripQualifiedKey.bind(this),
|
|
2588
2627
|
validateCacheKey,
|
|
@@ -2607,6 +2646,7 @@ var CacheStack = class extends EventEmitter {
|
|
|
2607
2646
|
layerWriter;
|
|
2608
2647
|
snapshots;
|
|
2609
2648
|
backgroundRefreshes = /* @__PURE__ */ new Map();
|
|
2649
|
+
backgroundRefreshAbort = /* @__PURE__ */ new Map();
|
|
2610
2650
|
layerDegradedUntil = /* @__PURE__ */ new Map();
|
|
2611
2651
|
maintenance = new CacheStackMaintenance();
|
|
2612
2652
|
ttlResolver;
|
|
@@ -2669,7 +2709,7 @@ var CacheStack = class extends EventEmitter {
|
|
|
2669
2709
|
if (!fetcher) {
|
|
2670
2710
|
return null;
|
|
2671
2711
|
}
|
|
2672
|
-
return this.fetchWithGuards(normalizedKey, fetcher, options);
|
|
2712
|
+
return this.fetchWithGuards(normalizedKey, fetcher, options, void 0, void 0, true);
|
|
2673
2713
|
}
|
|
2674
2714
|
/**
|
|
2675
2715
|
* Alias for `get(key, fetcher, options)` — explicit get-or-set pattern.
|
|
@@ -2851,7 +2891,7 @@ var CacheStack = class extends EventEmitter {
|
|
|
2851
2891
|
}
|
|
2852
2892
|
for (let layerIndex = 0; layerIndex < this.layers.length; layerIndex += 1) {
|
|
2853
2893
|
const layer = this.layers[layerIndex];
|
|
2854
|
-
if (!layer) continue;
|
|
2894
|
+
if (!layer || this.shouldSkipLayer(layer)) continue;
|
|
2855
2895
|
const keys = [...pending];
|
|
2856
2896
|
if (keys.length === 0) {
|
|
2857
2897
|
break;
|
|
@@ -2868,6 +2908,9 @@ var CacheStack = class extends EventEmitter {
|
|
|
2868
2908
|
await layer.delete(key);
|
|
2869
2909
|
continue;
|
|
2870
2910
|
}
|
|
2911
|
+
if (resolved.state === "stale-while-revalidate" || resolved.state === "stale-if-error") {
|
|
2912
|
+
this.metricsCollector.increment("staleHits", indexesByKey.get(key)?.length ?? 1);
|
|
2913
|
+
}
|
|
2871
2914
|
await this.tagIndex.touch(key);
|
|
2872
2915
|
await this.backfill(key, stored, layerIndex - 1);
|
|
2873
2916
|
resultsByKey.set(key, resolved.value);
|
|
@@ -3123,7 +3166,25 @@ var CacheStack = class extends EventEmitter {
|
|
|
3123
3166
|
await this.unsubscribeInvalidation?.();
|
|
3124
3167
|
await this.flushWriteBehindQueue();
|
|
3125
3168
|
await this.maintenance.waitForGenerationCleanup();
|
|
3126
|
-
|
|
3169
|
+
for (const key of this.backgroundRefreshAbort.keys()) {
|
|
3170
|
+
this.backgroundRefreshAbort.set(key, true);
|
|
3171
|
+
}
|
|
3172
|
+
await Promise.allSettled(
|
|
3173
|
+
[...this.backgroundRefreshes.values()].map((promise) => {
|
|
3174
|
+
let timer;
|
|
3175
|
+
return Promise.race([
|
|
3176
|
+
promise,
|
|
3177
|
+
new Promise((resolve) => {
|
|
3178
|
+
timer = setTimeout(resolve, 5e3);
|
|
3179
|
+
timer.unref?.();
|
|
3180
|
+
})
|
|
3181
|
+
]).finally(() => {
|
|
3182
|
+
if (timer) clearTimeout(timer);
|
|
3183
|
+
});
|
|
3184
|
+
})
|
|
3185
|
+
);
|
|
3186
|
+
this.backgroundRefreshes.clear();
|
|
3187
|
+
this.backgroundRefreshAbort.clear();
|
|
3127
3188
|
this.maintenance.disposeWriteBehindTimer();
|
|
3128
3189
|
this.fetchRateLimiter.dispose();
|
|
3129
3190
|
await Promise.allSettled(this.layers.map((layer) => layer.dispose?.() ?? Promise.resolve()));
|
|
@@ -3139,12 +3200,15 @@ var CacheStack = class extends EventEmitter {
|
|
|
3139
3200
|
await this.handleInvalidationMessage(message);
|
|
3140
3201
|
});
|
|
3141
3202
|
}
|
|
3142
|
-
async fetchWithGuards(key, fetcher, options, expectedClearEpoch, expectedKeyEpoch) {
|
|
3203
|
+
async fetchWithGuards(key, fetcher, options, expectedClearEpoch, expectedKeyEpoch, initialMissConfirmed = false) {
|
|
3143
3204
|
const fetchTask = async () => {
|
|
3144
|
-
const
|
|
3145
|
-
if (
|
|
3146
|
-
this.
|
|
3147
|
-
|
|
3205
|
+
const shouldRecheckFreshLayers = !(initialMissConfirmed && this.options.singleFlightCoordinator);
|
|
3206
|
+
if (shouldRecheckFreshLayers) {
|
|
3207
|
+
const secondHit = await this.readFromLayers(key, options, "fresh-only");
|
|
3208
|
+
if (secondHit.found) {
|
|
3209
|
+
this.metricsCollector.increment("hits");
|
|
3210
|
+
return secondHit.value;
|
|
3211
|
+
}
|
|
3148
3212
|
}
|
|
3149
3213
|
return this.fetchAndPopulate(key, fetcher, options, expectedClearEpoch, expectedKeyEpoch);
|
|
3150
3214
|
};
|
|
@@ -3152,12 +3216,22 @@ var CacheStack = class extends EventEmitter {
|
|
|
3152
3216
|
if (!this.options.singleFlightCoordinator) {
|
|
3153
3217
|
return fetchTask();
|
|
3154
3218
|
}
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3219
|
+
try {
|
|
3220
|
+
return await this.options.singleFlightCoordinator.execute(
|
|
3221
|
+
key,
|
|
3222
|
+
this.resolveSingleFlightOptions(),
|
|
3223
|
+
fetchTask,
|
|
3224
|
+
() => this.waitForFreshValue(key, fetcher, options, expectedClearEpoch, expectedKeyEpoch)
|
|
3225
|
+
);
|
|
3226
|
+
} catch (error) {
|
|
3227
|
+
if (!this.isGracefulDegradationEnabled()) {
|
|
3228
|
+
throw error;
|
|
3229
|
+
}
|
|
3230
|
+
this.metricsCollector.increment("degradedOperations");
|
|
3231
|
+
this.logger.warn?.("single-flight-coordinator-degraded", { key, error: this.formatError(error) });
|
|
3232
|
+
this.emitError("single-flight", { key, degraded: true, error: this.formatError(error) });
|
|
3233
|
+
return fetchTask();
|
|
3234
|
+
}
|
|
3161
3235
|
};
|
|
3162
3236
|
if (this.options.stampedePrevention === false) {
|
|
3163
3237
|
return singleFlightTask();
|
|
@@ -3390,15 +3464,19 @@ var CacheStack = class extends EventEmitter {
|
|
|
3390
3464
|
}
|
|
3391
3465
|
const clearEpoch = this.maintenance.currentClearEpoch();
|
|
3392
3466
|
const keyEpoch = this.maintenance.currentKeyEpoch(key);
|
|
3467
|
+
this.backgroundRefreshAbort.set(key, false);
|
|
3393
3468
|
const refresh = (async () => {
|
|
3394
3469
|
this.metricsCollector.increment("refreshes");
|
|
3395
3470
|
try {
|
|
3471
|
+
if (this.backgroundRefreshAbort.get(key)) return;
|
|
3396
3472
|
await this.runBackgroundRefresh(key, fetcher, options, clearEpoch, keyEpoch);
|
|
3397
3473
|
} catch (error) {
|
|
3474
|
+
if (this.backgroundRefreshAbort.get(key)) return;
|
|
3398
3475
|
this.metricsCollector.increment("refreshErrors");
|
|
3399
3476
|
this.logger.debug?.("refresh-error", { key, error: this.formatError(error) });
|
|
3400
3477
|
} finally {
|
|
3401
3478
|
this.backgroundRefreshes.delete(key);
|
|
3479
|
+
this.backgroundRefreshAbort.delete(key);
|
|
3402
3480
|
}
|
|
3403
3481
|
})();
|
|
3404
3482
|
this.backgroundRefreshes.set(key, refresh);
|
|
@@ -3501,7 +3579,7 @@ var CacheStack = class extends EventEmitter {
|
|
|
3501
3579
|
timer.unref?.();
|
|
3502
3580
|
})
|
|
3503
3581
|
]);
|
|
3504
|
-
if (result && typeof result === "object" && "kind" in result) {
|
|
3582
|
+
if (result !== null && result !== void 0 && typeof result === "object" && "kind" in result) {
|
|
3505
3583
|
if (result.kind === "error") {
|
|
3506
3584
|
throw result.error;
|
|
3507
3585
|
}
|
|
@@ -3519,7 +3597,7 @@ var CacheStack = class extends EventEmitter {
|
|
|
3519
3597
|
}
|
|
3520
3598
|
async observeOperation(name, attributes, execute) {
|
|
3521
3599
|
const id = this.nextOperationId;
|
|
3522
|
-
this.nextOperationId
|
|
3600
|
+
this.nextOperationId = (this.nextOperationId + 1) % Number.MAX_SAFE_INTEGER;
|
|
3523
3601
|
this.emit("operation-start", { id, name, attributes });
|
|
3524
3602
|
try {
|
|
3525
3603
|
const result = await execute();
|