@flowselections/pick-schermen-kwekers 1.0.22 → 1.0.23

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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pickRobustness.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pickRobustness.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/pickRobustness.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,68 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ vi.mock("@flowselections/core", () => ({ supabase: {} }));
3
+ import { verifyRowsWith, createRetryingDb } from "../pickApi";
4
+ import { withRetry } from "../pickRetry";
5
+ import { rowsToVerify, rowFingerprint } from "../pickVerify";
6
+ import { friendlyPickError } from "../pickUiText";
7
+ const rows = (n) => Array.from({ length: n }, (_, i) => ({ id: `id-${i}`, updated_at: "t1", payload: {} }));
8
+ const noSleep = { sleep: async () => { } };
9
+ describe("gebundelde controle", () => {
10
+ for (const n of [33, 100]) {
11
+ it(`${n} rijen: 1 aanroep i.p.v. ${n * 2}`, async () => {
12
+ const rpc = vi.fn(async (_fn, args) => ({
13
+ data: args["_rows"].map((r) => ({ id: r.id, plan: null, status: null })),
14
+ error: null,
15
+ }));
16
+ const out = await verifyRowsWith({ rpc }, rows(n).map((r) => ({ id: r.id, ref: r.id })));
17
+ expect(out).toHaveLength(n);
18
+ expect(rpc).toHaveBeenCalledTimes(1);
19
+ console.log(`[test] ${n} rijen → oud ${n * 2} aanroepen, nieuw ${rpc.mock.calls.length}`);
20
+ });
21
+ }
22
+ it("ongewijzigde, printklare rijen: 0 aanroepen in de volgende ronde", () => {
23
+ const items = rows(33);
24
+ const memo = new Map();
25
+ const now = 1000000;
26
+ items.forEach((it) => memo.set(it.id, { fingerprint: rowFingerprint(it), ready: true, checkedAt: now }));
27
+ expect(rowsToVerify(items, memo, now + 15000)).toHaveLength(0);
28
+ // gewijzigde rij gaat wel mee
29
+ const changed = [...items];
30
+ changed[0] = { ...changed[0], updated_at: "t2" };
31
+ expect(rowsToVerify(changed, memo, now + 15000)).toHaveLength(1);
32
+ });
33
+ });
34
+ describe("herhaalpoging", () => {
35
+ it("Failed to fetch bij pick_claim_next_document: herhaalt en slaagt", async () => {
36
+ const rpc = vi
37
+ .fn()
38
+ .mockResolvedValueOnce({ data: null, error: { message: "TypeError: Failed to fetch" } })
39
+ .mockResolvedValueOnce({ data: { id: "ok" }, error: null });
40
+ const res = await withRetry(() => rpc("pick_claim_next_document"), noSleep);
41
+ expect(res.error).toBeNull();
42
+ expect(rpc).toHaveBeenCalledTimes(2);
43
+ });
44
+ it("de gedeelde client herhaalt ook", async () => {
45
+ const base = {
46
+ rpc: vi.fn().mockRejectedValueOnce(new TypeError("Failed to fetch")).mockResolvedValue({ data: 1, error: null }),
47
+ };
48
+ vi.useFakeTimers();
49
+ const p = createRetryingDb(base).rpc("pick_claim_next_document");
50
+ await vi.runAllTimersAsync();
51
+ expect((await p).data).toBe(1);
52
+ expect(base.rpc).toHaveBeenCalledTimes(2);
53
+ vi.useRealTimers();
54
+ });
55
+ it("blijvende netwerkfout: 4 pogingen, dan nette melding", async () => {
56
+ const rpc = vi.fn().mockResolvedValue({ data: null, error: { message: "Failed to fetch" } });
57
+ const res = await withRetry(() => rpc(), noSleep);
58
+ expect(rpc).toHaveBeenCalledTimes(4);
59
+ const f = friendlyPickError(new Error(res.error.message), "nl");
60
+ expect(f.message).toMatch(/Geen verbinding/);
61
+ expect(f.message).not.toMatch(/Failed to fetch/);
62
+ });
63
+ it("databasefout wordt niet herhaald", async () => {
64
+ const rpc = vi.fn().mockResolvedValue({ data: null, error: { message: "Station is niet actief" } });
65
+ await withRetry(() => rpc(), noSleep);
66
+ expect(rpc).toHaveBeenCalledTimes(1);
67
+ });
68
+ });
@@ -1,6 +1,6 @@
1
1
  import { supabase } from "@flowselections/core";
2
2
  import type { PickCartPlan, PickCountry, PickDashboardRow, PickDocumentType, PickLock, PickActivePrinter, PickPrintJob, PickPrintPlek, PickStationPrintPlek, PickStationPrintPlekRow, PickPrinter, PickQueueItem, PickScreen, PickSortRule, PickStation, PickStationType, PickStatus, PickTask } from "./pickTypes";
3
- declare const db: {
3
+ type PickDbClient = {
4
4
  from: (t: string) => any;
5
5
  rpc: (fn: string, args?: Record<string, unknown>) => Promise<{
6
6
  data: any;
@@ -10,7 +10,25 @@ declare const db: {
10
10
  removeChannel: (c: any) => void;
11
11
  auth: typeof supabase.auth;
12
12
  };
13
+ /** Zelfde client, maar RPC-aanroepen vangen tijdelijke netwerkhaperingen op. */
14
+ export declare function createRetryingDb(base: PickDbClient): PickDbClient;
15
+ declare const db: PickDbClient;
13
16
  export { db as pickDb };
17
+ export interface VerifiedRow {
18
+ id: string;
19
+ plan: PickCartPlan | null;
20
+ status: {
21
+ ready: boolean;
22
+ released: boolean;
23
+ reason: string | null;
24
+ } | null;
25
+ error: string | null;
26
+ }
27
+ /** Eén aanroep per blok van max. 200 regels i.p.v. twee per regel. */
28
+ export declare function verifyRowsWith(client: Pick<PickDbClient, "rpc">, rows: {
29
+ id: string;
30
+ ref: string | null;
31
+ }[]): Promise<VerifiedRow[]>;
14
32
  export interface PickBatchPrintJob {
15
33
  cartId: string;
16
34
  cartLabel: string | null;
@@ -159,6 +177,11 @@ export declare const pickApi: {
159
177
  * Karplan van de hele pick-eenheid: bij een bufferorder alle karren van alle
160
178
  * orders in de buffer samen. Nummers en totalen komen uit de Engine.
161
179
  */
180
+ /** Gebundelde gereedheidscontrole voor meerdere regels. */
181
+ verifyRows(rows: {
182
+ id: string;
183
+ ref: string | null;
184
+ }[]): Promise<VerifiedRow[]>;
162
185
  queueCartPlan(queueItemId: string): Promise<PickCartPlan | null>;
163
186
  /** Zet één kar in de printopdracht en rondt alleen deze kar af. */
164
187
  completePrintCart(itemId: string, sessionId: string, cartId: string): Promise<PickQueueItem>;
@@ -1 +1 @@
1
- {"version":3,"file":"pickApi.d.ts","sourceRoot":"","sources":["../../src/lib/pickApi.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pickApi.d.ts","sourceRoot":"","sources":["../../src/lib/pickApi.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,51 @@
1
1
  import { supabase } from "@flowselections/core";
2
2
  import { parsePickCartPlan } from "./pickTypes";
3
3
  import { safeArray } from "./pickTypes";
4
- // De core-client is getypeerd op het core-schema; deze module gebruikt eigen tabellen.
5
- const db = supabase;
4
+ import { withRetry } from "./pickRetry";
5
+ const raw = supabase;
6
+ /** Zelfde client, maar RPC-aanroepen vangen tijdelijke netwerkhaperingen op. */
7
+ export function createRetryingDb(base) {
8
+ return {
9
+ from: (t) => base.from(t),
10
+ rpc: (fn, args) => withRetry(() => base.rpc(fn, args)),
11
+ channel: (name) => base.channel(name),
12
+ removeChannel: (c) => base.removeChannel(c),
13
+ get auth() {
14
+ return base.auth;
15
+ },
16
+ };
17
+ }
18
+ const db = createRetryingDb(raw);
6
19
  export { db as pickDb };
20
+ function parsePlanStatus(data) {
21
+ if (!data || typeof data !== "object")
22
+ return null;
23
+ const row = data;
24
+ return {
25
+ ready: row["ready"] === true,
26
+ released: row["released"] === true,
27
+ reason: typeof row["reason"] === "string" ? row["reason"] : null,
28
+ };
29
+ }
30
+ /** Eén aanroep per blok van max. 200 regels i.p.v. twee per regel. */
31
+ export async function verifyRowsWith(client, rows) {
32
+ const out = [];
33
+ for (let i = 0; i < rows.length; i += 200) {
34
+ const chunk = rows.slice(i, i + 200);
35
+ const { data, error } = await client.rpc("pick_verify_rows", { _rows: chunk });
36
+ if (error)
37
+ throw new Error(error.message);
38
+ for (const r of safeArray(data)) {
39
+ out.push({
40
+ id: String(r["id"]),
41
+ plan: parsePickCartPlan(r["plan"]),
42
+ status: parsePlanStatus(r["status"]),
43
+ error: typeof r["error"] === "string" ? r["error"] : null,
44
+ });
45
+ }
46
+ }
47
+ return out;
48
+ }
7
49
  function unwrap(res) {
8
50
  if (res.error)
9
51
  throw new Error(res.error.message);
@@ -361,6 +403,10 @@ export const pickApi = {
361
403
  * Karplan van de hele pick-eenheid: bij een bufferorder alle karren van alle
362
404
  * orders in de buffer samen. Nummers en totalen komen uit de Engine.
363
405
  */
406
+ /** Gebundelde gereedheidscontrole voor meerdere regels. */
407
+ verifyRows(rows) {
408
+ return verifyRowsWith(db, rows);
409
+ },
364
410
  async queueCartPlan(queueItemId) {
365
411
  const { data, error } = await db.rpc("pick_queue_cart_plan", { _queue_item_id: queueItemId });
366
412
  if (error)
@@ -0,0 +1,13 @@
1
+ export declare const RETRY_DELAYS_MS: readonly [300, 900, 2000];
2
+ export declare function isNetworkError(error: unknown): boolean;
3
+ type RpcResult = {
4
+ data: unknown;
5
+ error: unknown;
6
+ };
7
+ /** Voert een Supabase-aanroep uit; herhaalt alleen bij netwerkfouten. */
8
+ export declare function withRetry<T extends RpcResult>(call: () => PromiseLike<T>, opts?: {
9
+ delays?: readonly number[];
10
+ sleep?: (ms: number) => Promise<void>;
11
+ }): Promise<T>;
12
+ export {};
13
+ //# sourceMappingURL=pickRetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pickRetry.d.ts","sourceRoot":"","sources":["../../src/lib/pickRetry.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,2BAA4B,CAAC;AAmBzD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAOtD;AAED,KAAK,SAAS,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAEnD,yEAAyE;AACzE,wBAAsB,SAAS,CAAC,CAAC,SAAS,SAAS,EACjD,IAAI,EAAE,MAAM,WAAW,CAAC,CAAC,CAAC,EAC1B,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAAO,GAC/E,OAAO,CAAC,CAAC,CAAC,CAmBZ"}
@@ -0,0 +1,52 @@
1
+ // Herhaalpogingen voor tijdelijke netwerkhaperingen. Databasefouten
2
+ // (bijv. "niet actief", "andere sessie") worden nooit herhaald.
3
+ export const RETRY_DELAYS_MS = [300, 900, 2000];
4
+ const NETWORK_PATTERNS = [
5
+ "failed to fetch",
6
+ "networkerror",
7
+ "network request failed",
8
+ "load failed",
9
+ "fetch failed",
10
+ "timeout",
11
+ "timed out",
12
+ "econnreset",
13
+ "502",
14
+ "503",
15
+ "504",
16
+ "bad gateway",
17
+ "service unavailable",
18
+ "gateway timeout",
19
+ ];
20
+ export function isNetworkError(error) {
21
+ if (!error)
22
+ return false;
23
+ const e = error;
24
+ const status = Number(e.status);
25
+ if (status === 502 || status === 503 || status === 504)
26
+ return true;
27
+ const msg = (typeof e.message === "string" ? e.message : String(error)).toLowerCase();
28
+ return NETWORK_PATTERNS.some((p) => msg.includes(p));
29
+ }
30
+ /** Voert een Supabase-aanroep uit; herhaalt alleen bij netwerkfouten. */
31
+ export async function withRetry(call, opts = {}) {
32
+ const delays = opts.delays ?? RETRY_DELAYS_MS;
33
+ const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
34
+ for (let attempt = 0;; attempt += 1) {
35
+ let result = null;
36
+ let thrown = null;
37
+ try {
38
+ result = await call();
39
+ }
40
+ catch (e) {
41
+ thrown = e;
42
+ }
43
+ const err = thrown ?? result?.error;
44
+ if (!err || !isNetworkError(err) || attempt >= delays.length) {
45
+ if (thrown)
46
+ throw thrown;
47
+ return result;
48
+ }
49
+ const base = delays[attempt];
50
+ await sleep(base + Math.round(Math.random() * base * 0.25));
51
+ }
52
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"pickUiText.d.ts","sourceRoot":"","sources":["../../src/lib/pickUiText.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AA4DD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAExD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,MAAM,GACX;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,CAkC7D"}
1
+ {"version":3,"file":"pickUiText.d.ts","sourceRoot":"","sources":["../../src/lib/pickUiText.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AA4DD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAExD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,MAAM,GACX;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,CAsC7D"}
@@ -1,6 +1,4 @@
1
- // Extra schermteksten voor de pickomgeving (filters, scanner, meldingen).
2
- // Bewust los van pickI18n: die dictionaries zijn volledig per taal gevuld,
3
- // hier volstaat Nederlands met Engels als terugval.
1
+ import { isNetworkError } from "./pickRetry";
4
2
  const nl = {
5
3
  onlyPending: "Alleen nog printen",
6
4
  onlyUrgent: "Alleen spoed",
@@ -65,6 +63,10 @@ export function friendlyPickError(error, lang) {
65
63
  const texts = getPickUiTexts(lang);
66
64
  const raw = error instanceof Error ? error.message : typeof error === "string" ? error : "";
67
65
  const low = raw.toLowerCase();
66
+ // Netwerkhapering die na alle herhaalpogingen blijft: nooit rauw tonen.
67
+ if (isNetworkError(error) || isNetworkError(raw)) {
68
+ return { message: texts.connectionLost, detail: raw || undefined, expiredClaim: false };
69
+ }
68
70
  // Een botsing op de idempotentiesleutel van de printdienst is géén race:
69
71
  // er is dan geen ander scherm bezig met dit document.
70
72
  if (low.includes("idempotentie_sleutel") || low.includes("idempotentiesleutel")) {
@@ -0,0 +1,15 @@
1
+ import type { PickQueueItem } from "./pickTypes";
2
+ export interface VerifyMemo {
3
+ fingerprint: string;
4
+ ready: boolean;
5
+ checkedAt: number;
6
+ }
7
+ export declare const VERIFY_MAX_AGE_MS = 60000;
8
+ /** Alles wat aangeeft dat een regel in de wachtrij veranderd is. */
9
+ export declare function rowFingerprint(item: PickQueueItem): string;
10
+ /**
11
+ * Kiest welke regels deze ronde opnieuw bij de bron gecontroleerd moeten worden:
12
+ * gewijzigd, nog niet printklaar, of langer dan maxAge niet gecontroleerd.
13
+ */
14
+ export declare function rowsToVerify(items: PickQueueItem[], memo: Map<string, VerifyMemo>, now: number, maxAge?: number): PickQueueItem[];
15
+ //# sourceMappingURL=pickVerify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pickVerify.d.ts","sourceRoot":"","sources":["../../src/lib/pickVerify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,iBAAiB,QAAS,CAAC;AAExC,oEAAoE;AACpE,wBAAgB,cAAc,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAgB1D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,aAAa,EAAE,EACtB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EAC7B,GAAG,EAAE,MAAM,EACX,MAAM,SAAoB,GACzB,aAAa,EAAE,CAQjB"}
@@ -0,0 +1,32 @@
1
+ export const VERIFY_MAX_AGE_MS = 60000;
2
+ /** Alles wat aangeeft dat een regel in de wachtrij veranderd is. */
3
+ export function rowFingerprint(item) {
4
+ const r = item;
5
+ const p = (r["payload"] && typeof r["payload"] === "object" ? r["payload"] : {});
6
+ return JSON.stringify([
7
+ r["status_id"] ?? r["status"] ?? null,
8
+ r["printed_at"] ?? null,
9
+ r["updated_at"] ?? null,
10
+ p["cartPlanReady"] ?? null,
11
+ p["cartPlanReleased"] ?? null,
12
+ p["cartPlanReason"] ?? null,
13
+ p["totalCarts"] ?? p["cartTotal"] ?? null,
14
+ p["cartRowFor"] ?? null,
15
+ ]);
16
+ }
17
+ /**
18
+ * Kiest welke regels deze ronde opnieuw bij de bron gecontroleerd moeten worden:
19
+ * gewijzigd, nog niet printklaar, of langer dan maxAge niet gecontroleerd.
20
+ */
21
+ export function rowsToVerify(items, memo, now, maxAge = VERIFY_MAX_AGE_MS) {
22
+ return items.filter((item) => {
23
+ const m = memo.get(item.id);
24
+ if (!m)
25
+ return true;
26
+ if (m.fingerprint !== rowFingerprint(item))
27
+ return true;
28
+ if (!m.ready)
29
+ return true;
30
+ return now - m.checkedAt >= maxAge;
31
+ });
32
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowselections/pick-schermen-kwekers",
3
- "version": "1.0.22",
3
+ "version": "1.0.23",
4
4
  "flowselections": {
5
5
  "moduleId": "pick-schermen",
6
6
  "pages": [
@@ -64,7 +64,7 @@
64
64
  "devDependencies": {
65
65
  "@cloudflare/vite-plugin": "^1.25.5",
66
66
  "@eslint/js": "^9.32.0",
67
- "@flowselections/core": "1.0.21",
67
+ "@flowselections/core": "1.0.28",
68
68
  "@lovable.dev/vite-tanstack-config": "2.23.1",
69
69
  "@rsbuild/core": "^1.0.0",
70
70
  "@tailwindcss/vite": "^4.2.1",
@@ -91,6 +91,7 @@
91
91
  "typescript": "^5.8.3",
92
92
  "typescript-eslint": "^8.56.1",
93
93
  "vite": "^7.3.1",
94
- "vite-tsconfig-paths": "^6.0.2"
94
+ "vite-tsconfig-paths": "^6.0.2",
95
+ "vitest": "5.0.3"
95
96
  }
96
97
  }