@krizaka/outbox 0.0.0-stage → 0.1.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/LICENSE +201 -0
- package/README.md +38 -2
- package/dist/chunk-7WIJNAGK.js +24 -0
- package/dist/chunk-7WIJNAGK.js.map +1 -0
- package/dist/chunk-AD76AB44.js +44 -0
- package/dist/chunk-AD76AB44.js.map +1 -0
- package/dist/index.d.ts +164 -0
- package/dist/index.js +310 -0
- package/dist/index.js.map +1 -0
- package/dist/port-CXch5iMa.d.ts +124 -0
- package/dist/settings.d.ts +22 -0
- package/dist/settings.js +33 -0
- package/dist/settings.js.map +1 -0
- package/dist/testkit.d.ts +6 -0
- package/dist/testkit.js +139 -0
- package/dist/testkit.js.map +1 -0
- package/package.json +81 -3
package/dist/testkit.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FAIL_STATE
|
|
3
|
+
} from "./chunk-7WIJNAGK.js";
|
|
4
|
+
|
|
5
|
+
// src/testkit.ts
|
|
6
|
+
import { randomUUID } from "crypto";
|
|
7
|
+
import { expect, it } from "vitest";
|
|
8
|
+
var T0 = /* @__PURE__ */ new Date("2026-10-26T12:00:00.000Z");
|
|
9
|
+
var at = (seconds) => new Date(T0.valueOf() + seconds * 1e3);
|
|
10
|
+
var RULES = { maxAttempts: 3, backoffSeconds: 30, backoffMaxSeconds: 100 };
|
|
11
|
+
var claimFor = (store, owner, seconds, over = {}) => store.claim({ owner, limit: over.limit ?? 10, leaseSeconds: over.lease ?? 60, maxAttempts: RULES.maxAttempts, now: at(seconds), ...over.topic === void 0 ? {} : { topic: over.topic } });
|
|
12
|
+
var put = (store, over = {}) => {
|
|
13
|
+
const id = over.id ?? randomUUID();
|
|
14
|
+
return store.append({ id, topic: over.topic ?? "mail", type: "evt.mail.requested.v1", payload: over.payload ?? { to: "a@b.c" }, createdAt: T0, availableAt: over.availableAt ?? T0 }).then(() => id);
|
|
15
|
+
};
|
|
16
|
+
function outboxStoreContract(name, make) {
|
|
17
|
+
it(`${name}: appends a message once, whatever the number of appends of its id`, async () => {
|
|
18
|
+
const store = await make();
|
|
19
|
+
const id = randomUUID();
|
|
20
|
+
expect(await store.append({ id, topic: "mail", type: "evt.mail.requested.v1", payload: { n: 1 }, createdAt: T0, availableAt: T0 })).toBe(true);
|
|
21
|
+
expect(await store.append({ id, topic: "mail", type: "evt.mail.requested.v1", payload: { n: 2 }, createdAt: T0, availableAt: T0 })).toBe(false);
|
|
22
|
+
const [only, ...rest] = await claimFor(store, "w1", 0);
|
|
23
|
+
expect(rest).toEqual([]);
|
|
24
|
+
expect(only).toMatchObject({ id, topic: "mail", type: "evt.mail.requested.v1", payload: { n: 1 }, attempts: 1 });
|
|
25
|
+
expect(only.occurredAt.toISOString()).toBe(T0.toISOString());
|
|
26
|
+
});
|
|
27
|
+
it(`${name}: holds a claimed message for its lease and gives it to nobody else`, async () => {
|
|
28
|
+
const store = await make();
|
|
29
|
+
await put(store);
|
|
30
|
+
expect(await claimFor(store, "w1", 0)).toHaveLength(1);
|
|
31
|
+
expect(await claimFor(store, "w2", 30)).toEqual([]);
|
|
32
|
+
expect(await claimFor(store, "w2", 59)).toEqual([]);
|
|
33
|
+
});
|
|
34
|
+
it(`${name}: delivers again after a crash: the lease expires without complete, and the message is claimed again`, async () => {
|
|
35
|
+
const store = await make();
|
|
36
|
+
const id = await put(store);
|
|
37
|
+
expect((await claimFor(store, "w1", 0))[0]).toMatchObject({ id, attempts: 1 });
|
|
38
|
+
expect((await claimFor(store, "w2", 60))[0]).toMatchObject({ id, attempts: 2 });
|
|
39
|
+
expect(await store.complete(id, "w1", at(61))).toBe(false);
|
|
40
|
+
expect(await store.complete(id, "w2", at(61))).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
it(`${name}: completes once, for the holder of the lease only`, async () => {
|
|
43
|
+
const store = await make();
|
|
44
|
+
const id = await put(store);
|
|
45
|
+
await claimFor(store, "w1", 0);
|
|
46
|
+
expect(await store.complete(id, "intruder", at(1))).toBe(false);
|
|
47
|
+
expect(await store.complete(id, "w1", at(1))).toBe(true);
|
|
48
|
+
expect(await store.complete(id, "w1", at(2))).toBe(false);
|
|
49
|
+
expect(await claimFor(store, "w1", 1e3)).toEqual([]);
|
|
50
|
+
});
|
|
51
|
+
it(`${name}: backs off after a failure, doubling and capped, then goes dead after the last attempt`, async () => {
|
|
52
|
+
const store = await make();
|
|
53
|
+
const id = await put(store);
|
|
54
|
+
await claimFor(store, "w", 0);
|
|
55
|
+
expect(await store.fail(id, "w", "boom", { now: at(1), ...RULES })).toEqual({ state: "retry", availableAt: at(31) });
|
|
56
|
+
expect(await claimFor(store, "w", 30)).toEqual([]);
|
|
57
|
+
expect((await claimFor(store, "w", 31))[0]).toMatchObject({ id, attempts: 2 });
|
|
58
|
+
expect(await store.fail(id, "w", "boom", { now: at(40), ...RULES })).toEqual({ state: "retry", availableAt: at(100) });
|
|
59
|
+
expect((await claimFor(store, "w", 100))[0]).toMatchObject({ id, attempts: 3 });
|
|
60
|
+
expect(await store.fail(id, "w", "boom", { now: at(110), ...RULES })).toEqual({ state: "dead" });
|
|
61
|
+
expect(await claimFor(store, "w", 1e5)).toEqual([]);
|
|
62
|
+
expect(await store.stats(at(200))).toMatchObject({ dead: 1, pending: 0, leased: 0 });
|
|
63
|
+
});
|
|
64
|
+
it(`${name}: caps the back-off`, async () => {
|
|
65
|
+
const store = await make();
|
|
66
|
+
const id = await put(store);
|
|
67
|
+
const rules = { maxAttempts: 10, backoffSeconds: 30, backoffMaxSeconds: 100 };
|
|
68
|
+
let clock = 0;
|
|
69
|
+
const waits = [];
|
|
70
|
+
for (let attempt = 1; attempt <= 4; attempt += 1) {
|
|
71
|
+
const [m] = await store.claim({ owner: "w", limit: 1, leaseSeconds: 60, maxAttempts: 10, now: at(clock) });
|
|
72
|
+
expect(m?.id).toBe(id);
|
|
73
|
+
const outcome = await store.fail(id, "w", "boom", { now: at(clock), ...rules });
|
|
74
|
+
if (outcome.state !== FAIL_STATE.retry) throw new Error("expected a retry");
|
|
75
|
+
waits.push((outcome.availableAt.valueOf() - at(clock).valueOf()) / 1e3);
|
|
76
|
+
clock = (outcome.availableAt.valueOf() - T0.valueOf()) / 1e3;
|
|
77
|
+
}
|
|
78
|
+
expect(waits).toEqual([30, 60, 100, 100]);
|
|
79
|
+
});
|
|
80
|
+
it(`${name}: answers \xAB lost \xBB to a failure from a worker that no longer holds the lease`, async () => {
|
|
81
|
+
const store = await make();
|
|
82
|
+
const id = await put(store);
|
|
83
|
+
await claimFor(store, "w1", 0);
|
|
84
|
+
await claimFor(store, "w2", 60);
|
|
85
|
+
expect(await store.fail(id, "w1", "late", { now: at(61), ...RULES })).toEqual({ state: "lost" });
|
|
86
|
+
});
|
|
87
|
+
it(`${name}: marks dead a message whose last allowed attempt's lease expired with no answer`, async () => {
|
|
88
|
+
const store = await make();
|
|
89
|
+
const id = await put(store);
|
|
90
|
+
await claimFor(store, "w", 0);
|
|
91
|
+
await claimFor(store, "w", 60);
|
|
92
|
+
await claimFor(store, "w", 120);
|
|
93
|
+
expect(await claimFor(store, "w", 180)).toEqual([]);
|
|
94
|
+
expect(await store.stats(at(181))).toMatchObject({ dead: 1, pending: 0 });
|
|
95
|
+
expect(await store.retryDead(id, at(190))).toBe(true);
|
|
96
|
+
expect((await claimFor(store, "w", 190))[0]).toMatchObject({ id, attempts: 1 });
|
|
97
|
+
});
|
|
98
|
+
it(`${name}: waits for the scheduled instant, filters by topic and honours the limit`, async () => {
|
|
99
|
+
const store = await make();
|
|
100
|
+
await put(store, { topic: "later", availableAt: at(500) });
|
|
101
|
+
await put(store, { topic: "mail" });
|
|
102
|
+
await put(store, { topic: "mail" });
|
|
103
|
+
await put(store, { topic: "other" });
|
|
104
|
+
expect(await claimFor(store, "w", 0, { topic: "later" })).toEqual([]);
|
|
105
|
+
expect(await claimFor(store, "w", 0, { topic: "mail", limit: 1 })).toHaveLength(1);
|
|
106
|
+
expect(await claimFor(store, "w", 0, { topic: "mail" })).toHaveLength(1);
|
|
107
|
+
expect(await claimFor(store, "w", 500, { topic: "later" })).toHaveLength(1);
|
|
108
|
+
expect(await claimFor(store, "w", 0, { topic: "other" })).toHaveLength(1);
|
|
109
|
+
});
|
|
110
|
+
it(`${name}: purges what is delivered or dead and older than the cut-off, and keeps the rest`, async () => {
|
|
111
|
+
const store = await make();
|
|
112
|
+
await put(store);
|
|
113
|
+
await put(store);
|
|
114
|
+
const [first] = await claimFor(store, "w", 0, { limit: 1 });
|
|
115
|
+
await store.complete(first.id, "w", at(10));
|
|
116
|
+
expect(await store.purgeBefore(at(5))).toBe(0);
|
|
117
|
+
expect(await store.purgeBefore(at(20))).toBe(1);
|
|
118
|
+
expect(await store.stats(at(30))).toMatchObject({ delivered: 0, pending: 1 });
|
|
119
|
+
});
|
|
120
|
+
it(`${name}: never gives one message to two workers claiming at once`, async () => {
|
|
121
|
+
const store = await make();
|
|
122
|
+
for (let i = 0; i < 40; i += 1) await put(store);
|
|
123
|
+
const claimers = Array.from({ length: 5 }, (_, i) => async () => {
|
|
124
|
+
const mine = [];
|
|
125
|
+
for (; ; ) {
|
|
126
|
+
const batch = await claimFor(store, `w${i}`, 0, { limit: 4, lease: 600 });
|
|
127
|
+
if (batch.length === 0) return mine;
|
|
128
|
+
mine.push(...batch.map((m) => m.id));
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
const taken = (await Promise.all(claimers.map((run) => run()))).flat();
|
|
132
|
+
expect(taken).toHaveLength(40);
|
|
133
|
+
expect(new Set(taken).size).toBe(40);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
export {
|
|
137
|
+
outboxStoreContract
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=testkit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/testkit.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\n\nimport { expect, it } from \"vitest\";\n\nimport { FAIL_STATE, type OutboxStore } from \"./port\";\n\nconst T0 = new Date(\"2026-10-26T12:00:00.000Z\");\nconst at = (seconds: number) => new Date(T0.valueOf() + seconds * 1000);\nconst RULES = { maxAttempts: 3, backoffSeconds: 30, backoffMaxSeconds: 100 };\nconst claimFor = (store: OutboxStore, owner: string, seconds: number, over: { topic?: string; limit?: number; lease?: number } = {}) =>\n store.claim({ owner, limit: over.limit ?? 10, leaseSeconds: over.lease ?? 60, maxAttempts: RULES.maxAttempts, now: at(seconds), ...(over.topic === undefined ? {} : { topic: over.topic }) });\nconst put = (store: OutboxStore, over: { id?: string; topic?: string; availableAt?: Date; payload?: unknown } = {}) => {\n const id = over.id ?? randomUUID();\n return store.append({ id, topic: over.topic ?? \"mail\", type: \"evt.mail.requested.v1\", payload: over.payload ?? { to: \"a@b.c\" }, createdAt: T0, availableAt: over.availableAt ?? T0 }).then(() => id);\n};\n\n/** What every `OutboxStore` adapter must do: the same behaviour behind the same port. `make` gives an empty store. */\nexport function outboxStoreContract(name: string, make: () => OutboxStore | Promise<OutboxStore>) {\n it(`${name}: appends a message once, whatever the number of appends of its id`, async () => {\n const store = await make();\n const id = randomUUID();\n expect(await store.append({ id, topic: \"mail\", type: \"evt.mail.requested.v1\", payload: { n: 1 }, createdAt: T0, availableAt: T0 })).toBe(true);\n expect(await store.append({ id, topic: \"mail\", type: \"evt.mail.requested.v1\", payload: { n: 2 }, createdAt: T0, availableAt: T0 })).toBe(false);\n const [only, ...rest] = await claimFor(store, \"w1\", 0);\n expect(rest).toEqual([]);\n expect(only).toMatchObject({ id, topic: \"mail\", type: \"evt.mail.requested.v1\", payload: { n: 1 }, attempts: 1 });\n expect(only!.occurredAt.toISOString()).toBe(T0.toISOString());\n });\n\n it(`${name}: holds a claimed message for its lease and gives it to nobody else`, async () => {\n const store = await make();\n await put(store);\n expect(await claimFor(store, \"w1\", 0)).toHaveLength(1);\n expect(await claimFor(store, \"w2\", 30)).toEqual([]);\n expect(await claimFor(store, \"w2\", 59)).toEqual([]);\n });\n\n it(`${name}: delivers again after a crash: the lease expires without complete, and the message is claimed again`, async () => {\n const store = await make();\n const id = await put(store);\n expect((await claimFor(store, \"w1\", 0))[0]).toMatchObject({ id, attempts: 1 });\n // w1 delivered, then died before `complete`\n expect((await claimFor(store, \"w2\", 60))[0]).toMatchObject({ id, attempts: 2 });\n expect(await store.complete(id, \"w1\", at(61))).toBe(false); // the dead worker's late answer is refused\n expect(await store.complete(id, \"w2\", at(61))).toBe(true);\n });\n\n it(`${name}: completes once, for the holder of the lease only`, async () => {\n const store = await make();\n const id = await put(store);\n await claimFor(store, \"w1\", 0);\n expect(await store.complete(id, \"intruder\", at(1))).toBe(false);\n expect(await store.complete(id, \"w1\", at(1))).toBe(true);\n expect(await store.complete(id, \"w1\", at(2))).toBe(false);\n expect(await claimFor(store, \"w1\", 1000)).toEqual([]);\n });\n\n it(`${name}: backs off after a failure, doubling and capped, then goes dead after the last attempt`, async () => {\n const store = await make();\n const id = await put(store);\n await claimFor(store, \"w\", 0);\n expect(await store.fail(id, \"w\", \"boom\", { now: at(1), ...RULES })).toEqual({ state: \"retry\", availableAt: at(31) }); // 30 s\n expect(await claimFor(store, \"w\", 30)).toEqual([]);\n expect((await claimFor(store, \"w\", 31))[0]).toMatchObject({ id, attempts: 2 });\n expect(await store.fail(id, \"w\", \"boom\", { now: at(40), ...RULES })).toEqual({ state: \"retry\", availableAt: at(100) }); // 60 s\n expect((await claimFor(store, \"w\", 100))[0]).toMatchObject({ id, attempts: 3 });\n expect(await store.fail(id, \"w\", \"boom\", { now: at(110), ...RULES })).toEqual({ state: \"dead\" });\n expect(await claimFor(store, \"w\", 100_000)).toEqual([]);\n expect(await store.stats(at(200))).toMatchObject({ dead: 1, pending: 0, leased: 0 });\n });\n\n it(`${name}: caps the back-off`, async () => {\n const store = await make();\n const id = await put(store);\n const rules = { maxAttempts: 10, backoffSeconds: 30, backoffMaxSeconds: 100 };\n let clock = 0;\n const waits: number[] = [];\n for (let attempt = 1; attempt <= 4; attempt += 1) {\n const [m] = await store.claim({ owner: \"w\", limit: 1, leaseSeconds: 60, maxAttempts: 10, now: at(clock) });\n expect(m?.id).toBe(id);\n const outcome = await store.fail(id, \"w\", \"boom\", { now: at(clock), ...rules });\n if (outcome.state !== FAIL_STATE.retry) throw new Error(\"expected a retry\");\n waits.push((outcome.availableAt.valueOf() - at(clock).valueOf()) / 1000);\n clock = (outcome.availableAt.valueOf() - T0.valueOf()) / 1000;\n }\n expect(waits).toEqual([30, 60, 100, 100]);\n });\n\n it(`${name}: answers « lost » to a failure from a worker that no longer holds the lease`, async () => {\n const store = await make();\n const id = await put(store);\n await claimFor(store, \"w1\", 0);\n await claimFor(store, \"w2\", 60);\n expect(await store.fail(id, \"w1\", \"late\", { now: at(61), ...RULES })).toEqual({ state: \"lost\" });\n });\n\n it(`${name}: marks dead a message whose last allowed attempt's lease expired with no answer`, async () => {\n const store = await make();\n const id = await put(store);\n await claimFor(store, \"w\", 0);\n await claimFor(store, \"w\", 60);\n await claimFor(store, \"w\", 120); // the 3rd and last attempt; the worker dies\n expect(await claimFor(store, \"w\", 180)).toEqual([]);\n expect(await store.stats(at(181))).toMatchObject({ dead: 1, pending: 0 });\n expect(await store.retryDead(id, at(190))).toBe(true);\n expect((await claimFor(store, \"w\", 190))[0]).toMatchObject({ id, attempts: 1 });\n });\n\n it(`${name}: waits for the scheduled instant, filters by topic and honours the limit`, async () => {\n const store = await make();\n await put(store, { topic: \"later\", availableAt: at(500) });\n await put(store, { topic: \"mail\" });\n await put(store, { topic: \"mail\" });\n await put(store, { topic: \"other\" });\n expect(await claimFor(store, \"w\", 0, { topic: \"later\" })).toEqual([]);\n expect(await claimFor(store, \"w\", 0, { topic: \"mail\", limit: 1 })).toHaveLength(1);\n expect(await claimFor(store, \"w\", 0, { topic: \"mail\" })).toHaveLength(1);\n expect((await claimFor(store, \"w\", 500, { topic: \"later\" }))).toHaveLength(1);\n expect(await claimFor(store, \"w\", 0, { topic: \"other\" })).toHaveLength(1);\n });\n\n it(`${name}: purges what is delivered or dead and older than the cut-off, and keeps the rest`, async () => {\n const store = await make();\n await put(store);\n await put(store); // one is delivered, the other stays pending\n const [first] = await claimFor(store, \"w\", 0, { limit: 1 });\n await store.complete(first!.id, \"w\", at(10));\n expect(await store.purgeBefore(at(5))).toBe(0);\n expect(await store.purgeBefore(at(20))).toBe(1);\n expect(await store.stats(at(30))).toMatchObject({ delivered: 0, pending: 1 });\n });\n\n it(`${name}: never gives one message to two workers claiming at once`, async () => {\n const store = await make();\n for (let i = 0; i < 40; i += 1) await put(store);\n const claimers = Array.from({ length: 5 }, (_, i) => async () => {\n const mine: string[] = [];\n for (;;) {\n const batch = await claimFor(store, `w${i}`, 0, { limit: 4, lease: 600 });\n if (batch.length === 0) return mine;\n mine.push(...batch.map((m) => m.id));\n }\n });\n const taken = (await Promise.all(claimers.map((run) => run()))).flat();\n expect(taken).toHaveLength(40);\n expect(new Set(taken).size).toBe(40);\n });\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,84 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@krizaka/outbox",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The transactional outbox of every Krizaka product: an event written in the caller's transaction, delivered after the commit at least once by leased workers, with doubling retries, dead letters and an idempotent-consumer helper. No broker, no database client, no wall clock.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"krizaka",
|
|
7
|
+
"outbox",
|
|
8
|
+
"events",
|
|
9
|
+
"postgresql",
|
|
10
|
+
"at-least-once"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/krizaka/krizaka-ts/tree/main/packages/outbox",
|
|
13
|
+
"bugs": "https://github.com/krizaka/krizaka-ts/issues",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/krizaka/krizaka-ts.git",
|
|
17
|
+
"directory": "packages/outbox"
|
|
18
|
+
},
|
|
19
|
+
"license": "Apache-2.0",
|
|
20
|
+
"author": "Krizaka (https://www.krizaka.com)",
|
|
21
|
+
"type": "module",
|
|
22
|
+
"main": "./dist/index.js",
|
|
23
|
+
"module": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./settings": {
|
|
31
|
+
"types": "./dist/settings.d.ts",
|
|
32
|
+
"import": "./dist/settings.js"
|
|
33
|
+
},
|
|
34
|
+
"./testkit": {
|
|
35
|
+
"types": "./dist/testkit.d.ts",
|
|
36
|
+
"import": "./dist/testkit.js"
|
|
37
|
+
},
|
|
38
|
+
"./package.json": "./package.json"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"README.md",
|
|
43
|
+
"LICENSE"
|
|
44
|
+
],
|
|
45
|
+
"sideEffects": false,
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=22"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^22.10.0",
|
|
51
|
+
"@types/pg": "^8.23.1",
|
|
52
|
+
"eslint": "^9.39.0",
|
|
53
|
+
"pg": "^8.23.1",
|
|
54
|
+
"tsup": "^8.5.0",
|
|
55
|
+
"typescript": "^5.9.3",
|
|
56
|
+
"vitest": "^4.1.11",
|
|
57
|
+
"zod": "^4.6.7",
|
|
58
|
+
"@krizaka/config": "^0.8.1"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@krizaka/db": "^0.2.0",
|
|
62
|
+
"@krizaka/time": "^0.1.0",
|
|
63
|
+
"@krizaka/settings": "^0.4.0"
|
|
64
|
+
},
|
|
65
|
+
"peerDependencies": {
|
|
66
|
+
"vitest": ">=4",
|
|
67
|
+
"zod": ">=3.23"
|
|
68
|
+
},
|
|
69
|
+
"peerDependenciesMeta": {
|
|
70
|
+
"vitest": {
|
|
71
|
+
"optional": true
|
|
72
|
+
},
|
|
73
|
+
"zod": {
|
|
74
|
+
"optional": true
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"scripts": {
|
|
78
|
+
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsup",
|
|
79
|
+
"lint": "eslint . --max-warnings 0 && tsc --noEmit",
|
|
80
|
+
"typecheck": "tsc --noEmit",
|
|
81
|
+
"test": "vitest run",
|
|
82
|
+
"check": "pnpm run lint && pnpm run test && pnpm run build"
|
|
83
|
+
}
|
|
6
84
|
}
|