india2actual 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/.env.example +23 -0
- package/LICENSE +21 -0
- package/README.md +280 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +295 -0
- package/dist/cli.js.map +1 -0
- package/dist/env-file.d.ts +38 -0
- package/dist/env-file.js +40 -0
- package/dist/env-file.js.map +1 -0
- package/dist/extract/csv.d.ts +13 -0
- package/dist/extract/csv.js +60 -0
- package/dist/extract/csv.js.map +1 -0
- package/dist/extract/html-table.d.ts +3 -0
- package/dist/extract/html-table.js +77 -0
- package/dist/extract/html-table.js.map +1 -0
- package/dist/extract/index.d.ts +21 -0
- package/dist/extract/index.js +44 -0
- package/dist/extract/index.js.map +1 -0
- package/dist/extract/pdf.d.ts +47 -0
- package/dist/extract/pdf.js +439 -0
- package/dist/extract/pdf.js.map +1 -0
- package/dist/extract/sniff.d.ts +12 -0
- package/dist/extract/sniff.js +57 -0
- package/dist/extract/sniff.js.map +1 -0
- package/dist/extract/spreadsheetml.d.ts +3 -0
- package/dist/extract/spreadsheetml.js +93 -0
- package/dist/extract/spreadsheetml.js.map +1 -0
- package/dist/extract/types.d.ts +20 -0
- package/dist/extract/types.js +2 -0
- package/dist/extract/types.js.map +1 -0
- package/dist/extract/xlsx.d.ts +2 -0
- package/dist/extract/xlsx.js +64 -0
- package/dist/extract/xlsx.js.map +1 -0
- package/dist/interpret/header.d.ts +24 -0
- package/dist/interpret/header.js +57 -0
- package/dist/interpret/header.js.map +1 -0
- package/dist/interpret/roundtrip.d.ts +29 -0
- package/dist/interpret/roundtrip.js +89 -0
- package/dist/interpret/roundtrip.js.map +1 -0
- package/dist/interpret/rows.d.ts +54 -0
- package/dist/interpret/rows.js +151 -0
- package/dist/interpret/rows.js.map +1 -0
- package/dist/interpret/synonyms.d.ts +27 -0
- package/dist/interpret/synonyms.js +55 -0
- package/dist/interpret/synonyms.js.map +1 -0
- package/dist/interpret/validate.d.ts +27 -0
- package/dist/interpret/validate.js +93 -0
- package/dist/interpret/validate.js.map +1 -0
- package/dist/interpret/values.d.ts +22 -0
- package/dist/interpret/values.js +131 -0
- package/dist/interpret/values.js.map +1 -0
- package/dist/merchants-file.d.ts +14 -0
- package/dist/merchants-file.js +40 -0
- package/dist/merchants-file.js.map +1 -0
- package/dist/narration/merchants.d.ts +46 -0
- package/dist/narration/merchants.js +161 -0
- package/dist/narration/merchants.js.map +1 -0
- package/dist/narration/parse.d.ts +14 -0
- package/dist/narration/parse.js +659 -0
- package/dist/narration/parse.js.map +1 -0
- package/dist/narration/types.d.ts +25 -0
- package/dist/narration/types.js +2 -0
- package/dist/narration/types.js.map +1 -0
- package/dist/out/csv.d.ts +21 -0
- package/dist/out/csv.js +40 -0
- package/dist/out/csv.js.map +1 -0
- package/dist/out/push.d.ts +74 -0
- package/dist/out/push.js +103 -0
- package/dist/out/push.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The transaction families Indian banks encode into the narration/particulars
|
|
3
|
+
* column. `other` covers interest postings, charges, cheques and anything else
|
|
4
|
+
* we do not recognise — it is not an error.
|
|
5
|
+
*/
|
|
6
|
+
export type NarrationKind = 'upi' | 'neft' | 'imps' | 'rtgs' | 'pos' | 'ach' | 'atm' | 'other';
|
|
7
|
+
export type ParsedNarration = {
|
|
8
|
+
kind: NarrationKind;
|
|
9
|
+
/**
|
|
10
|
+
* Display payee. Guaranteed non-empty: when nothing can be extracted this
|
|
11
|
+
* falls back to the raw narration, so the worst case matches what Actual
|
|
12
|
+
* would have shown anyway.
|
|
13
|
+
*/
|
|
14
|
+
merchant: string;
|
|
15
|
+
/** Virtual Payment Address, e.g. `swiggy@ybl`, when present. */
|
|
16
|
+
vpa?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Bank-issued UPI reference (UTR/RRN). Only set when we are confident it is
|
|
19
|
+
* genuinely per-transaction unique, because a non-unique value here would
|
|
20
|
+
* make Actual collapse distinct transactions. See `extractRef`.
|
|
21
|
+
*/
|
|
22
|
+
ref?: string;
|
|
23
|
+
/** The narration exactly as the bank wrote it. */
|
|
24
|
+
raw: string;
|
|
25
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/narration/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { StatementTransaction } from '../interpret/rows.js';
|
|
2
|
+
/**
|
|
3
|
+
* Columns chosen to match what Actual's import dialog can actually map:
|
|
4
|
+
* date, payee, notes and amount.
|
|
5
|
+
*
|
|
6
|
+
* Note the deliberate compromise on notes. Actual's CSV field mapping has no
|
|
7
|
+
* `imported_payee` slot (only date, amount, payee, notes, category, in/out and
|
|
8
|
+
* inflow/outflow), so the raw narration goes into Notes to keep it visible.
|
|
9
|
+
* The API path does not have this limitation and sets `imported_payee`
|
|
10
|
+
* properly — this is the one place the CSV route is lossier.
|
|
11
|
+
*
|
|
12
|
+
* `Reference` is emitted for human inspection; leave it unmapped on import.
|
|
13
|
+
*
|
|
14
|
+
* This header is also the signature the round-trip path matches on, so that
|
|
15
|
+
* the tool can read its own output back and push a CSV you have reviewed.
|
|
16
|
+
* Renaming or reordering a column here means updating
|
|
17
|
+
* `interpret/roundtrip.ts`; a test asserts the two agree.
|
|
18
|
+
*/
|
|
19
|
+
export declare const COLUMNS: readonly ["Date", "Payee", "Notes", "Amount", "Reference"];
|
|
20
|
+
export declare function toCsv(transactions: StatementTransaction[]): string;
|
|
21
|
+
export declare function writeCsv(path: string, transactions: StatementTransaction[]): Promise<void>;
|
package/dist/out/csv.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { writeFile } from 'node:fs/promises';
|
|
2
|
+
import { stringify } from 'csv-stringify/sync';
|
|
3
|
+
/**
|
|
4
|
+
* Columns chosen to match what Actual's import dialog can actually map:
|
|
5
|
+
* date, payee, notes and amount.
|
|
6
|
+
*
|
|
7
|
+
* Note the deliberate compromise on notes. Actual's CSV field mapping has no
|
|
8
|
+
* `imported_payee` slot (only date, amount, payee, notes, category, in/out and
|
|
9
|
+
* inflow/outflow), so the raw narration goes into Notes to keep it visible.
|
|
10
|
+
* The API path does not have this limitation and sets `imported_payee`
|
|
11
|
+
* properly — this is the one place the CSV route is lossier.
|
|
12
|
+
*
|
|
13
|
+
* `Reference` is emitted for human inspection; leave it unmapped on import.
|
|
14
|
+
*
|
|
15
|
+
* This header is also the signature the round-trip path matches on, so that
|
|
16
|
+
* the tool can read its own output back and push a CSV you have reviewed.
|
|
17
|
+
* Renaming or reordering a column here means updating
|
|
18
|
+
* `interpret/roundtrip.ts`; a test asserts the two agree.
|
|
19
|
+
*/
|
|
20
|
+
export const COLUMNS = [
|
|
21
|
+
'Date',
|
|
22
|
+
'Payee',
|
|
23
|
+
'Notes',
|
|
24
|
+
'Amount',
|
|
25
|
+
'Reference',
|
|
26
|
+
];
|
|
27
|
+
export function toCsv(transactions) {
|
|
28
|
+
const rows = transactions.map(transaction => [
|
|
29
|
+
transaction.date,
|
|
30
|
+
transaction.payee,
|
|
31
|
+
transaction.raw,
|
|
32
|
+
transaction.amount.toFixed(2),
|
|
33
|
+
transaction.ref ?? '',
|
|
34
|
+
]);
|
|
35
|
+
return stringify(rows, { header: true, columns: [...COLUMNS] });
|
|
36
|
+
}
|
|
37
|
+
export async function writeCsv(path, transactions) {
|
|
38
|
+
await writeFile(path, toCsv(transactions), 'utf8');
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=csv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv.js","sourceRoot":"","sources":["../../src/out/csv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAI/C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,MAAM;IACN,OAAO;IACP,OAAO;IACP,QAAQ;IACR,WAAW;CACH,CAAC;AAEX,MAAM,UAAU,KAAK,CAAC,YAAoC;IACxD,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;QAC3C,WAAW,CAAC,IAAI;QAChB,WAAW,CAAC,KAAK;QACjB,WAAW,CAAC,GAAG;QACf,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7B,WAAW,CAAC,GAAG,IAAI,EAAE;KACtB,CAAC,CAAC;IAEH,OAAO,SAAS,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,IAAY,EACZ,YAAoC;IAEpC,MAAM,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { StatementTransaction } from '../interpret/rows.js';
|
|
2
|
+
/**
|
|
3
|
+
* The subset of Actual's `ImportTransactionEntity` we populate. Declared
|
|
4
|
+
* locally so this module type-checks without `@actual-app/api` installed —
|
|
5
|
+
* it is an optional peer dependency, since the CSV path is the default and
|
|
6
|
+
* the API package pulls in a native SQLite build.
|
|
7
|
+
*/
|
|
8
|
+
export type ActualImportTransaction = {
|
|
9
|
+
date: string;
|
|
10
|
+
/** Integer paise. */
|
|
11
|
+
amount: number;
|
|
12
|
+
payee_name: string;
|
|
13
|
+
imported_payee: string;
|
|
14
|
+
notes: string;
|
|
15
|
+
imported_id?: string;
|
|
16
|
+
cleared: boolean;
|
|
17
|
+
};
|
|
18
|
+
export type PushConfig = {
|
|
19
|
+
serverURL: string;
|
|
20
|
+
password: string;
|
|
21
|
+
syncId: string;
|
|
22
|
+
dataDir: string;
|
|
23
|
+
/** End-to-end encryption password, for encrypted budget files. */
|
|
24
|
+
encryptionPassword?: string;
|
|
25
|
+
/** Account name (case-insensitive) or id. */
|
|
26
|
+
account: string;
|
|
27
|
+
dryRun: boolean;
|
|
28
|
+
};
|
|
29
|
+
export type PushResult = {
|
|
30
|
+
accountName: string;
|
|
31
|
+
accountId: string;
|
|
32
|
+
added: number;
|
|
33
|
+
updated: number;
|
|
34
|
+
errors: string[];
|
|
35
|
+
dryRun: boolean;
|
|
36
|
+
};
|
|
37
|
+
/** Rupees to integer paise, matching Actual's own `amountToInteger`. */
|
|
38
|
+
export declare function toPaise(amount: number): number;
|
|
39
|
+
/**
|
|
40
|
+
* Map parsed statement rows onto Actual's import shape.
|
|
41
|
+
*
|
|
42
|
+
* Two deliberate choices:
|
|
43
|
+
*
|
|
44
|
+
* - `payee_name` gets the cleaned merchant and `imported_payee` the original
|
|
45
|
+
* narration, which is exactly what those fields are for: the payee collapses
|
|
46
|
+
* correctly while Actual's payee matching still sees the raw text. This is
|
|
47
|
+
* the one thing the CSV output cannot do, since Actual's CSV field mapping
|
|
48
|
+
* has no `imported_payee` slot.
|
|
49
|
+
* - `notes` also gets the narration, duplicating `imported_payee`. That
|
|
50
|
+
* duplication looked redundant and was originally left out, which was wrong:
|
|
51
|
+
* `imported_payee` is not a column you can read at a glance, whereas Notes
|
|
52
|
+
* is one you can see, search and filter. Without it the narration is
|
|
53
|
+
* effectively invisible after a push.
|
|
54
|
+
* - `imported_id` is only set when a reference survived the uniqueness checks.
|
|
55
|
+
* Omitting it is the safe default: Actual falls back to matching on date and
|
|
56
|
+
* amount within a week, whereas a non-unique id makes it treat distinct
|
|
57
|
+
* transactions as the same one and drop them.
|
|
58
|
+
*/
|
|
59
|
+
export declare function toImportEntities(transactions: StatementTransaction[]): ActualImportTransaction[];
|
|
60
|
+
type ActualAccount = {
|
|
61
|
+
id: string;
|
|
62
|
+
name: string;
|
|
63
|
+
closed?: boolean;
|
|
64
|
+
};
|
|
65
|
+
export declare function resolveAccount(accounts: ActualAccount[], wanted: string): ActualAccount;
|
|
66
|
+
/**
|
|
67
|
+
* Send transactions to Actual.
|
|
68
|
+
*
|
|
69
|
+
* `@actual-app/api` is imported dynamically so the converter works without it
|
|
70
|
+
* installed, and so a missing install produces an actionable message rather
|
|
71
|
+
* than a module-resolution error at startup.
|
|
72
|
+
*/
|
|
73
|
+
export declare function pushTransactions(transactions: StatementTransaction[], config: PushConfig): Promise<PushResult>;
|
|
74
|
+
export {};
|
package/dist/out/push.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/** Rupees to integer paise, matching Actual's own `amountToInteger`. */
|
|
2
|
+
export function toPaise(amount) {
|
|
3
|
+
return Math.round(amount * 100);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Map parsed statement rows onto Actual's import shape.
|
|
7
|
+
*
|
|
8
|
+
* Two deliberate choices:
|
|
9
|
+
*
|
|
10
|
+
* - `payee_name` gets the cleaned merchant and `imported_payee` the original
|
|
11
|
+
* narration, which is exactly what those fields are for: the payee collapses
|
|
12
|
+
* correctly while Actual's payee matching still sees the raw text. This is
|
|
13
|
+
* the one thing the CSV output cannot do, since Actual's CSV field mapping
|
|
14
|
+
* has no `imported_payee` slot.
|
|
15
|
+
* - `notes` also gets the narration, duplicating `imported_payee`. That
|
|
16
|
+
* duplication looked redundant and was originally left out, which was wrong:
|
|
17
|
+
* `imported_payee` is not a column you can read at a glance, whereas Notes
|
|
18
|
+
* is one you can see, search and filter. Without it the narration is
|
|
19
|
+
* effectively invisible after a push.
|
|
20
|
+
* - `imported_id` is only set when a reference survived the uniqueness checks.
|
|
21
|
+
* Omitting it is the safe default: Actual falls back to matching on date and
|
|
22
|
+
* amount within a week, whereas a non-unique id makes it treat distinct
|
|
23
|
+
* transactions as the same one and drop them.
|
|
24
|
+
*/
|
|
25
|
+
export function toImportEntities(transactions) {
|
|
26
|
+
return transactions.map(transaction => ({
|
|
27
|
+
date: transaction.date,
|
|
28
|
+
amount: toPaise(transaction.amount),
|
|
29
|
+
payee_name: transaction.payee,
|
|
30
|
+
imported_payee: transaction.raw,
|
|
31
|
+
notes: transaction.raw,
|
|
32
|
+
...(transaction.ref ? { imported_id: transaction.ref } : {}),
|
|
33
|
+
// Statement rows have already settled at the bank.
|
|
34
|
+
cleared: true,
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
export function resolveAccount(accounts, wanted) {
|
|
38
|
+
const needle = wanted.trim().toLowerCase();
|
|
39
|
+
const match = accounts.find(account => account.id === wanted) ??
|
|
40
|
+
accounts.find(account => account.name.trim().toLowerCase() === needle);
|
|
41
|
+
if (!match) {
|
|
42
|
+
const available = accounts
|
|
43
|
+
.filter(account => !account.closed)
|
|
44
|
+
.map(account => ` ${account.name}`)
|
|
45
|
+
.join('\n');
|
|
46
|
+
throw new Error(`No account matching "${wanted}". Open accounts:\n${available || ' (none)'}`);
|
|
47
|
+
}
|
|
48
|
+
return match;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Send transactions to Actual.
|
|
52
|
+
*
|
|
53
|
+
* `@actual-app/api` is imported dynamically so the converter works without it
|
|
54
|
+
* installed, and so a missing install produces an actionable message rather
|
|
55
|
+
* than a module-resolution error at startup.
|
|
56
|
+
*/
|
|
57
|
+
export async function pushTransactions(transactions, config) {
|
|
58
|
+
let api;
|
|
59
|
+
try {
|
|
60
|
+
// Non-literal specifier on purpose: it keeps TypeScript from trying to
|
|
61
|
+
// resolve an optional dependency that may not be installed.
|
|
62
|
+
const specifier = '@actual-app/api';
|
|
63
|
+
api = (await import(specifier));
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
throw new Error('The --push option needs the Actual API package:\n' +
|
|
67
|
+
' npm install @actual-app/api');
|
|
68
|
+
}
|
|
69
|
+
await api.init({
|
|
70
|
+
dataDir: config.dataDir,
|
|
71
|
+
serverURL: config.serverURL,
|
|
72
|
+
password: config.password,
|
|
73
|
+
});
|
|
74
|
+
try {
|
|
75
|
+
await api.downloadBudget(config.syncId, config.encryptionPassword
|
|
76
|
+
? { password: config.encryptionPassword }
|
|
77
|
+
: undefined);
|
|
78
|
+
const accounts = await api.getAccounts();
|
|
79
|
+
const account = resolveAccount(accounts, config.account);
|
|
80
|
+
const result = await api.importTransactions(account.id, toImportEntities(transactions), {
|
|
81
|
+
dryRun: config.dryRun,
|
|
82
|
+
defaultCleared: true,
|
|
83
|
+
// Actual title-cases imported payees by default, which lowercases
|
|
84
|
+
// first: `DMart` would become `Dmart` and `HDFC ... SIP` would become
|
|
85
|
+
// `Hdfc ... Sip`. The names here are already deliberately cased.
|
|
86
|
+
payeeNameNormalization: 'original',
|
|
87
|
+
});
|
|
88
|
+
return {
|
|
89
|
+
accountName: account.name,
|
|
90
|
+
accountId: account.id,
|
|
91
|
+
added: result.added?.length ?? 0,
|
|
92
|
+
updated: result.updated?.length ?? 0,
|
|
93
|
+
errors: (result.errors ?? []).map(error => error.message),
|
|
94
|
+
dryRun: config.dryRun,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
// Always shut down: this flushes the sync and closes the budget, and
|
|
99
|
+
// leaving it open corrupts the local cache for the next run.
|
|
100
|
+
await api.shutdown();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=push.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/out/push.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "india2actual",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Convert Indian bank statements (CSV, XLS, PDF) into Actual Budget transactions with real merchant names",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "emilgeo",
|
|
7
|
+
"homepage": "https://github.com/emilgeo/india2actual#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/emilgeo/india2actual.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/emilgeo/india2actual/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"actual",
|
|
17
|
+
"actual-budget",
|
|
18
|
+
"budgeting",
|
|
19
|
+
"personal-finance",
|
|
20
|
+
"india",
|
|
21
|
+
"indian-banks",
|
|
22
|
+
"upi",
|
|
23
|
+
"bank-statement",
|
|
24
|
+
"csv",
|
|
25
|
+
"pdf",
|
|
26
|
+
"importer",
|
|
27
|
+
"converter"
|
|
28
|
+
],
|
|
29
|
+
"bin": {
|
|
30
|
+
"india2actual": "dist/cli.js"
|
|
31
|
+
},
|
|
32
|
+
"type": "module",
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
".env.example"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc",
|
|
39
|
+
"dev": "tsx src/cli.ts",
|
|
40
|
+
"test": "vitest --run",
|
|
41
|
+
"test:watch": "vitest",
|
|
42
|
+
"typecheck": "tsc --noEmit",
|
|
43
|
+
"prepublishOnly": "npm run typecheck && npm test && npm run build"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"csv-parse": "^7.0.2",
|
|
47
|
+
"csv-stringify": "^6.5.0",
|
|
48
|
+
"exceljs": "^4.4.0",
|
|
49
|
+
"fast-xml-parser": "^5.11.1",
|
|
50
|
+
"node-html-parser": "^9.0.4",
|
|
51
|
+
"pdfjs-dist": "^6.3.289"
|
|
52
|
+
},
|
|
53
|
+
"optionalDependencies": {
|
|
54
|
+
"@actual-app/api": "^26.9.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/node": "^22.10.0",
|
|
58
|
+
"tsx": "^4.19.0",
|
|
59
|
+
"typescript": "^5.7.0",
|
|
60
|
+
"vitest": "^5.0.1"
|
|
61
|
+
},
|
|
62
|
+
"engines": {
|
|
63
|
+
"node": ">=22"
|
|
64
|
+
}
|
|
65
|
+
}
|