@pureq/pureq 1.1.3 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/src/client/createClient.d.ts.map +1 -1
- package/dist/src/client/createClient.js +7 -3
- package/dist/src/client/createClient.js.map +1 -1
- package/dist/src/middleware/dedupe.d.ts.map +1 -1
- package/dist/src/middleware/dedupe.js +11 -2
- package/dist/src/middleware/dedupe.js.map +1 -1
- package/dist/src/middleware/httpCache.d.ts.map +1 -1
- package/dist/src/middleware/httpCache.js +4 -0
- package/dist/src/middleware/httpCache.js.map +1 -1
- package/dist/src/middleware/retry.d.ts.map +1 -1
- package/dist/src/middleware/retry.js +15 -6
- package/dist/src/middleware/retry.js.map +1 -1
- package/dist/src/middleware/validation.d.ts +5 -0
- package/dist/src/middleware/validation.d.ts.map +1 -1
- package/dist/src/middleware/validation.js +1 -1
- package/dist/src/middleware/validation.js.map +1 -1
- package/dist/src/utils/crypto.d.ts.map +1 -1
- package/dist/src/utils/crypto.js +2 -1
- package/dist/src/utils/crypto.js.map +1 -1
- package/dist/tests/auth-refresh.test.d.ts +2 -0
- package/dist/tests/auth-refresh.test.d.ts.map +1 -0
- package/dist/tests/auth-refresh.test.js +75 -0
- package/dist/tests/auth-refresh.test.js.map +1 -0
- package/dist/tests/chaos-heavy.test.d.ts +2 -0
- package/dist/tests/chaos-heavy.test.d.ts.map +1 -0
- package/dist/tests/chaos-heavy.test.js +164 -0
- package/dist/tests/chaos-heavy.test.js.map +1 -0
- package/dist/tests/crypto-utils.test.d.ts +2 -0
- package/dist/tests/crypto-utils.test.d.ts.map +1 -0
- package/dist/tests/crypto-utils.test.js +39 -0
- package/dist/tests/crypto-utils.test.js.map +1 -0
- package/dist/tests/fallback-validation-stale-policy.test.d.ts +2 -0
- package/dist/tests/fallback-validation-stale-policy.test.d.ts.map +1 -0
- package/dist/tests/fallback-validation-stale-policy.test.js +138 -0
- package/dist/tests/fallback-validation-stale-policy.test.js.map +1 -0
- package/dist/tests/invariant-fuzz.test.d.ts +2 -0
- package/dist/tests/invariant-fuzz.test.d.ts.map +1 -0
- package/dist/tests/invariant-fuzz.test.js +149 -0
- package/dist/tests/invariant-fuzz.test.js.map +1 -0
- package/dist/tests/resilience-shock.test.d.ts +2 -0
- package/dist/tests/resilience-shock.test.d.ts.map +1 -0
- package/dist/tests/resilience-shock.test.js +114 -0
- package/dist/tests/resilience-shock.test.js.map +1 -0
- package/dist/tests/storage-adapters.test.d.ts +2 -0
- package/dist/tests/storage-adapters.test.d.ts.map +1 -0
- package/dist/tests/storage-adapters.test.js +109 -0
- package/dist/tests/storage-adapters.test.js.map +1 -0
- package/dist/tests/traffic-load.test.d.ts +2 -0
- package/dist/tests/traffic-load.test.d.ts.map +1 -0
- package/dist/tests/traffic-load.test.js +91 -0
- package/dist/tests/traffic-load.test.js.map +1 -0
- package/package.json +6 -3
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { createOfflineQueue } from "../src/middleware/offlineQueue";
|
|
3
|
+
import { concurrencyLimit } from "../src/middleware/concurrencyLimit";
|
|
4
|
+
import { execute } from "../src/executor/execute";
|
|
5
|
+
import { HttpResponse } from "../src/response/response";
|
|
6
|
+
function sleep(ms) {
|
|
7
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
8
|
+
}
|
|
9
|
+
describe("resilience/shock: abort and queue storms", () => {
|
|
10
|
+
it("remains healthy after queued abort storm", async () => {
|
|
11
|
+
const startedAt = Date.now();
|
|
12
|
+
const middleware = concurrencyLimit({ maxConcurrent: 1, maxQueue: 400 });
|
|
13
|
+
const first = middleware({ method: "GET", url: "/gate" }, async () => {
|
|
14
|
+
await sleep(25);
|
|
15
|
+
return new HttpResponse(new Response("first", { status: 200 }));
|
|
16
|
+
});
|
|
17
|
+
const controllers = Array.from({ length: 80 }, () => new AbortController());
|
|
18
|
+
const abortedTasks = controllers.map((controller, i) => middleware({ method: "GET", url: `/queued/${i}`, signal: controller.signal }, async () => {
|
|
19
|
+
return new HttpResponse(new Response("never", { status: 200 }));
|
|
20
|
+
}).then(() => ({ ok: true }), (error) => ({ ok: false, error })));
|
|
21
|
+
for (const c of controllers) {
|
|
22
|
+
c.abort(new DOMException("Aborted", "AbortError"));
|
|
23
|
+
}
|
|
24
|
+
await first;
|
|
25
|
+
const settled = await Promise.all(abortedTasks);
|
|
26
|
+
const rejectedCount = settled.filter((x) => x.ok === false).length;
|
|
27
|
+
console.info("[pureq][shock-metrics]", {
|
|
28
|
+
scenario: "abort-storm",
|
|
29
|
+
aborted: controllers.length,
|
|
30
|
+
rejectedCount,
|
|
31
|
+
durationMs: Date.now() - startedAt,
|
|
32
|
+
});
|
|
33
|
+
expect(rejectedCount).toBe(80);
|
|
34
|
+
const healthy = await middleware({ method: "GET", url: "/healthy" }, async () => {
|
|
35
|
+
return new HttpResponse(new Response("ok", { status: 200 }));
|
|
36
|
+
});
|
|
37
|
+
expect(healthy.status).toBe(200);
|
|
38
|
+
});
|
|
39
|
+
it("flushes large offline queue with partial replay failures", async () => {
|
|
40
|
+
const startedAt = Date.now();
|
|
41
|
+
const queue = createOfflineQueue({
|
|
42
|
+
isOffline: () => true,
|
|
43
|
+
maxQueueSize: 500,
|
|
44
|
+
});
|
|
45
|
+
const total = 140;
|
|
46
|
+
for (let i = 0; i < total; i++) {
|
|
47
|
+
await queue.middleware({
|
|
48
|
+
method: "POST",
|
|
49
|
+
url: `/events/${i}`,
|
|
50
|
+
priority: i % 7,
|
|
51
|
+
body: { i },
|
|
52
|
+
}, async () => new HttpResponse(new Response("live", { status: 200 })));
|
|
53
|
+
}
|
|
54
|
+
const flushed = await queue.flush(async (req) => {
|
|
55
|
+
const id = Number(req.url.split("/").pop());
|
|
56
|
+
await sleep(1);
|
|
57
|
+
if (id % 10 === 0) {
|
|
58
|
+
throw new Error("replay failed");
|
|
59
|
+
}
|
|
60
|
+
return new HttpResponse(new Response("replayed", { status: 201 }));
|
|
61
|
+
}, { concurrency: 16 });
|
|
62
|
+
const snapshot = await queue.snapshot();
|
|
63
|
+
console.info("[pureq][shock-metrics]", {
|
|
64
|
+
scenario: "offline-flush-burst",
|
|
65
|
+
queued: total,
|
|
66
|
+
flushed: flushed.length,
|
|
67
|
+
remaining: snapshot.size,
|
|
68
|
+
replayFailureRate: Number(((snapshot.size / total) * 100).toFixed(2)),
|
|
69
|
+
durationMs: Date.now() - startedAt,
|
|
70
|
+
});
|
|
71
|
+
expect(flushed.length).toBe(126);
|
|
72
|
+
expect(snapshot.size).toBe(14);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
describe("aggressive input robustness", () => {
|
|
76
|
+
it("fails fast on unresolved path placeholders instead of issuing request", async () => {
|
|
77
|
+
const startedAt = Date.now();
|
|
78
|
+
await expect(execute({
|
|
79
|
+
method: "GET",
|
|
80
|
+
url: "https://example.com/users/:id/orders/:orderId",
|
|
81
|
+
params: { id: "u1" },
|
|
82
|
+
}, {
|
|
83
|
+
adapter: async () => new Response("unexpected", { status: 200 }),
|
|
84
|
+
})).rejects.toThrow("pureq: unresolved path parameters");
|
|
85
|
+
console.info("[pureq][aggressive-metrics]", {
|
|
86
|
+
scenario: "unresolved-placeholder-fast-fail",
|
|
87
|
+
durationMs: Date.now() - startedAt,
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
it("handles very large query arrays without crashing", async () => {
|
|
91
|
+
const startedAt = Date.now();
|
|
92
|
+
const values = Array.from({ length: 1000 }, (_, i) => i);
|
|
93
|
+
let capturedUrl = "";
|
|
94
|
+
await execute({
|
|
95
|
+
method: "GET",
|
|
96
|
+
url: "/search",
|
|
97
|
+
query: { tag: values },
|
|
98
|
+
}, {
|
|
99
|
+
adapter: async (url) => {
|
|
100
|
+
capturedUrl = url;
|
|
101
|
+
return new Response("ok", { status: 200 });
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
console.info("[pureq][aggressive-metrics]", {
|
|
105
|
+
scenario: "large-query-array",
|
|
106
|
+
queryItems: values.length,
|
|
107
|
+
finalUrlLength: capturedUrl.length,
|
|
108
|
+
durationMs: Date.now() - startedAt,
|
|
109
|
+
});
|
|
110
|
+
expect(capturedUrl.includes("search?")).toBe(true);
|
|
111
|
+
expect(capturedUrl.includes("tag=999")).toBe(true);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
//# sourceMappingURL=resilience-shock.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resilience-shock.test.js","sourceRoot":"","sources":["../../tests/resilience-shock.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-adapters.test.d.ts","sourceRoot":"","sources":["../../tests/storage-adapters.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { mkdtemp, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { webcrypto } from "node:crypto";
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
import { EncryptedQueueStorageAdapter } from "../src/adapters/storage/encryptedStorage";
|
|
7
|
+
import { IndexedDBQueueStorageAdapter } from "../src/adapters/storage/indexedDBAdapter";
|
|
8
|
+
import { FileSystemQueueStorageAdapter } from "../src/node/fsAdapter";
|
|
9
|
+
function createMemoryStorage(initial = []) {
|
|
10
|
+
let items = [...initial];
|
|
11
|
+
return {
|
|
12
|
+
async push(item) {
|
|
13
|
+
items = [...items.filter((x) => x.id !== item.id), item];
|
|
14
|
+
},
|
|
15
|
+
async getAll() {
|
|
16
|
+
return items;
|
|
17
|
+
},
|
|
18
|
+
async remove(id) {
|
|
19
|
+
items = items.filter((x) => x.id !== id);
|
|
20
|
+
},
|
|
21
|
+
async clear() {
|
|
22
|
+
items = [];
|
|
23
|
+
},
|
|
24
|
+
async size() {
|
|
25
|
+
return items.length;
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
vi.stubGlobal("crypto", webcrypto);
|
|
31
|
+
});
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
vi.restoreAllMocks();
|
|
34
|
+
vi.unstubAllGlobals();
|
|
35
|
+
});
|
|
36
|
+
describe("EncryptedQueueStorageAdapter", () => {
|
|
37
|
+
it("encrypts on push and decrypts on getAll", async () => {
|
|
38
|
+
const key = await globalThis.crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, true, [
|
|
39
|
+
"encrypt",
|
|
40
|
+
"decrypt",
|
|
41
|
+
]);
|
|
42
|
+
const inner = createMemoryStorage();
|
|
43
|
+
const adapter = new EncryptedQueueStorageAdapter(inner, key);
|
|
44
|
+
await adapter.push({
|
|
45
|
+
id: 1,
|
|
46
|
+
queuedAt: 100,
|
|
47
|
+
req: { method: "POST", url: "/items", body: { id: "a" } },
|
|
48
|
+
});
|
|
49
|
+
const raw = await inner.getAll();
|
|
50
|
+
expect(typeof raw[0].req).toBe("string");
|
|
51
|
+
const all = await adapter.getAll();
|
|
52
|
+
expect(all).toHaveLength(1);
|
|
53
|
+
expect(all[0]?.req).toMatchObject({ method: "POST", url: "/items" });
|
|
54
|
+
});
|
|
55
|
+
it("throws aggregate error when one or more entries fail decryption", async () => {
|
|
56
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
57
|
+
const key = await globalThis.crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, true, [
|
|
58
|
+
"encrypt",
|
|
59
|
+
"decrypt",
|
|
60
|
+
]);
|
|
61
|
+
const inner = createMemoryStorage([
|
|
62
|
+
{
|
|
63
|
+
id: 1,
|
|
64
|
+
queuedAt: 1,
|
|
65
|
+
req: "invalid:ciphertext",
|
|
66
|
+
},
|
|
67
|
+
]);
|
|
68
|
+
const adapter = new EncryptedQueueStorageAdapter(inner, key);
|
|
69
|
+
await expect(adapter.getAll()).rejects.toMatchObject({
|
|
70
|
+
code: "PUREQ_DECRYPTION_FAILURE",
|
|
71
|
+
});
|
|
72
|
+
expect(warnSpy).toHaveBeenCalledTimes(1);
|
|
73
|
+
warnSpy.mockRestore();
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe("IndexedDBQueueStorageAdapter", () => {
|
|
77
|
+
it("rejects when IndexedDB is unavailable", async () => {
|
|
78
|
+
const original = globalThis.indexedDB;
|
|
79
|
+
globalThis.indexedDB = undefined;
|
|
80
|
+
const adapter = new IndexedDBQueueStorageAdapter("pureq-test-db");
|
|
81
|
+
await expect(adapter.size()).rejects.toThrow("pureq: IndexedDB is not available in this environment");
|
|
82
|
+
globalThis.indexedDB = original;
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
describe("FileSystemQueueStorageAdapter", () => {
|
|
86
|
+
it("stores, loads, removes, and clears queued requests", async () => {
|
|
87
|
+
const dir = await mkdtemp(join(tmpdir(), "pureq-fs-"));
|
|
88
|
+
const adapter = new FileSystemQueueStorageAdapter(dir);
|
|
89
|
+
await adapter.push({ id: 10, queuedAt: 1, req: { method: "POST", url: "/a" } });
|
|
90
|
+
await adapter.push({ id: 11, queuedAt: 2, req: { method: "DELETE", url: "/b" } });
|
|
91
|
+
expect(await adapter.size()).toBe(2);
|
|
92
|
+
const all = await adapter.getAll();
|
|
93
|
+
expect(all.map((x) => x.id).sort()).toEqual([10, 11]);
|
|
94
|
+
await adapter.remove(10);
|
|
95
|
+
expect(await adapter.size()).toBe(1);
|
|
96
|
+
await adapter.clear();
|
|
97
|
+
expect(await adapter.size()).toBe(0);
|
|
98
|
+
});
|
|
99
|
+
it("skips malformed json files", async () => {
|
|
100
|
+
const dir = await mkdtemp(join(tmpdir(), "pureq-fs-"));
|
|
101
|
+
const adapter = new FileSystemQueueStorageAdapter(dir);
|
|
102
|
+
await writeFile(join(dir, "broken.json"), "{not-json", "utf8");
|
|
103
|
+
await adapter.push({ id: 22, queuedAt: 2, req: { method: "POST", url: "/ok" } });
|
|
104
|
+
const all = await adapter.getAll();
|
|
105
|
+
expect(all).toHaveLength(1);
|
|
106
|
+
expect(all[0]?.id).toBe(22);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
//# sourceMappingURL=storage-adapters.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-adapters.test.js","sourceRoot":"","sources":["../../tests/storage-adapters.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traffic-load.test.d.ts","sourceRoot":"","sources":["../../tests/traffic-load.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { concurrencyLimit } from "../src/middleware/concurrencyLimit";
|
|
3
|
+
import { dedupe } from "../src/middleware/dedupe";
|
|
4
|
+
import { createCircuitBreaker, PureqCircuitOpenError } from "../src/middleware/circuitBreaker";
|
|
5
|
+
import { HttpResponse } from "../src/response/response";
|
|
6
|
+
function sleep(ms) {
|
|
7
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
8
|
+
}
|
|
9
|
+
describe("traffic/load: high concurrency", () => {
|
|
10
|
+
it("enforces maxConcurrent under burst traffic", async () => {
|
|
11
|
+
const startedAt = Date.now();
|
|
12
|
+
const maxConcurrent = 8;
|
|
13
|
+
const middleware = concurrencyLimit({ maxConcurrent });
|
|
14
|
+
let inFlight = 0;
|
|
15
|
+
let maxSeen = 0;
|
|
16
|
+
const total = 240;
|
|
17
|
+
const tasks = Array.from({ length: total }, (_, i) => middleware({ method: "GET", url: `/burst/${i}` }, async () => {
|
|
18
|
+
inFlight += 1;
|
|
19
|
+
maxSeen = Math.max(maxSeen, inFlight);
|
|
20
|
+
await sleep(3);
|
|
21
|
+
inFlight -= 1;
|
|
22
|
+
return new HttpResponse(new Response("ok", { status: 200 }));
|
|
23
|
+
}));
|
|
24
|
+
const responses = await Promise.all(tasks);
|
|
25
|
+
const durationMs = Date.now() - startedAt;
|
|
26
|
+
console.info("[pureq][traffic-metrics]", {
|
|
27
|
+
scenario: "concurrency-burst",
|
|
28
|
+
total,
|
|
29
|
+
maxConcurrent,
|
|
30
|
+
maxSeen,
|
|
31
|
+
success: responses.length,
|
|
32
|
+
durationMs,
|
|
33
|
+
throughputRps: Number((responses.length / Math.max(durationMs / 1000, 0.001)).toFixed(2)),
|
|
34
|
+
});
|
|
35
|
+
expect(responses).toHaveLength(total);
|
|
36
|
+
expect(responses.every((res) => res.status === 200)).toBe(true);
|
|
37
|
+
expect(maxSeen).toBeLessThanOrEqual(maxConcurrent);
|
|
38
|
+
});
|
|
39
|
+
it("collapses same-signature request storms via dedupe", async () => {
|
|
40
|
+
const startedAt = Date.now();
|
|
41
|
+
const middleware = dedupe();
|
|
42
|
+
let upstreamCalls = 0;
|
|
43
|
+
const req = { method: "GET", url: "https://example.com/hot" };
|
|
44
|
+
const total = 120;
|
|
45
|
+
const calls = Array.from({ length: total }, () => middleware(req, async () => {
|
|
46
|
+
upstreamCalls += 1;
|
|
47
|
+
await sleep(5);
|
|
48
|
+
return new HttpResponse(new Response("shared", { status: 200 }));
|
|
49
|
+
}));
|
|
50
|
+
const responses = await Promise.all(calls);
|
|
51
|
+
const texts = await Promise.all(responses.map((r) => r.text()));
|
|
52
|
+
const durationMs = Date.now() - startedAt;
|
|
53
|
+
console.info("[pureq][traffic-metrics]", {
|
|
54
|
+
scenario: "dedupe-storm",
|
|
55
|
+
total,
|
|
56
|
+
upstreamCalls,
|
|
57
|
+
dedupeRate: Number((((total - upstreamCalls) / total) * 100).toFixed(2)),
|
|
58
|
+
durationMs,
|
|
59
|
+
});
|
|
60
|
+
expect(upstreamCalls).toBe(1);
|
|
61
|
+
expect(texts.every((t) => t === "shared")).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
describe("traffic/load: failure burst handling", () => {
|
|
65
|
+
it("opens circuit quickly after repeated failures and short-circuits subsequent traffic", async () => {
|
|
66
|
+
const startedAt = Date.now();
|
|
67
|
+
const controller = createCircuitBreaker({
|
|
68
|
+
failureThreshold: 3,
|
|
69
|
+
cooldownMs: 60000,
|
|
70
|
+
keyBuilder: () => "api:orders",
|
|
71
|
+
});
|
|
72
|
+
const req = { method: "GET", url: "/orders" };
|
|
73
|
+
for (let i = 0; i < 3; i++) {
|
|
74
|
+
await expect(controller.middleware(req, async () => {
|
|
75
|
+
throw new Error("upstream down");
|
|
76
|
+
})).rejects.toThrow("upstream down");
|
|
77
|
+
}
|
|
78
|
+
await expect(controller.middleware(req, async () => {
|
|
79
|
+
return new HttpResponse(new Response("should-not-run", { status: 200 }));
|
|
80
|
+
})).rejects.toBeInstanceOf(PureqCircuitOpenError);
|
|
81
|
+
const snapshot = await controller.snapshot();
|
|
82
|
+
console.info("[pureq][traffic-metrics]", {
|
|
83
|
+
scenario: "circuit-open-burst",
|
|
84
|
+
failuresToOpen: 3,
|
|
85
|
+
openCircuits: snapshot.summary.open,
|
|
86
|
+
durationMs: Date.now() - startedAt,
|
|
87
|
+
});
|
|
88
|
+
expect(snapshot.summary.open).toBe(1);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
//# sourceMappingURL=traffic-load.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traffic-load.test.js","sourceRoot":"","sources":["../../tests/traffic-load.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pureq/pureq",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"description": "Functional, immutable, and type-safe HTTP client layer with middleware composition.",
|
|
5
5
|
"main": "./dist/src/index.js",
|
|
6
6
|
"types": "./dist/src/index.d.ts",
|
|
@@ -30,11 +30,14 @@
|
|
|
30
30
|
"test:edge": "esbuild scripts/edge-smoke-entry.ts --bundle --format=iife --platform=browser --outfile=.tmp/edge-smoke.bundle.js && node ./scripts/run-edge-smoke.mjs",
|
|
31
31
|
"test": "vitest run",
|
|
32
32
|
"test:watch": "vitest",
|
|
33
|
-
"test:unit": "vitest run tests --exclude tests/*.integration.test.ts --exclude tests/*.contract.test.ts --exclude tests/*.stress.test.ts",
|
|
33
|
+
"test:unit": "vitest run tests --exclude tests/*.integration.test.ts --exclude tests/*.contract.test.ts --exclude tests/*.stress.test.ts --exclude tests/traffic-load.test.ts --exclude tests/resilience-shock.test.ts --exclude tests/chaos-heavy.test.ts --exclude tests/invariant-fuzz.test.ts",
|
|
34
34
|
"test:integration": "vitest run tests/client.integration.test.ts",
|
|
35
35
|
"test:contract": "vitest run tests/public-api.contract.test.ts",
|
|
36
36
|
"test:stress": "vitest run tests/retry.stress.test.ts",
|
|
37
|
-
"test:
|
|
37
|
+
"test:heavy": "vitest run tests/traffic-load.test.ts tests/resilience-shock.test.ts tests/retry.stress.test.ts tests/chaos-heavy.test.ts tests/invariant-fuzz.test.ts",
|
|
38
|
+
"test:nightly": "npm run test:heavy",
|
|
39
|
+
"test:ci": "npm run test:unit && npm run test:integration && npm run test:contract && npm run test:stress && npm run typecheck",
|
|
40
|
+
"test:ci:full": "npm run test:ci && npm run test:heavy"
|
|
38
41
|
},
|
|
39
42
|
"keywords": [
|
|
40
43
|
"http",
|