Files
accounted/lib/bookkeeping/bas-account-numbers.ts
T
1a41119682 perf(bundle): drop the BAS chart and the Node crypto polyfill from the shared client baseline (#1942)
* perf(bundle): drop the BAS chart and the Node crypto polyfill from the shared client baseline

Two chunks rode along in the first-load JS of almost every dashboard route:
the full BAS 2026 chart (315 KB uncompressed, in 81 route manifests) and
the browser polyfill for Node's crypto/vm/Buffer (327 KB, in 26 routes
incl. login and register). Neither was needed on first paint; both got
there through static imports of helpers that happen to live next to code
that needs the data or the builtin.

Node polyfill (4 pure splits, behaviour unchanged, re-exported from the
original modules for server callers):
- lib/auth/bankid-flags.ts: isBankIdEnabled (login, register, security
  settings imported it from bankid.ts, which imports crypto).
- lib/import/bank-file/formats.ts: the format registry + detection (the
  import history imported getFormat from parser.ts, which hashes).
- lib/salary/personnummer-format.ts: parsing/validation/formatting (the
  employee forms reached the encrypting personnummer.ts via tax-column).
- lib/auth/api-key-scopes.ts: scope catalogue, groups, tool map, helpers
  (the API key panel imported STAGING_SCOPES from the key generator).

BAS chart:
- lib/bookkeeping/bas-lazy.ts + use-bas-reference.ts: the chart becomes a
  dynamic import, fetched once per session after first paint; components
  that show BAS names/descriptions call useBasReference() and re-render
  when it lands. Until then (and on the server) only the hardcoded
  account-descriptions answer, so SSR and hydration agree.
- lib/bookkeeping/bas-labels.ts: class/group labels out of bas-reference.ts
  (account-descriptions needed a label and paid for the whole chart).
- lib/bookkeeping/bas-account-numbers.ts (generated, ~11 KB) +
  scripts/generate-bas-account-numbers.ts (--check) + parity test:
  isStandardBASAccountNumber for AddAccountDialog/ChartOfAccountsManager.
- lib/bookkeeping/account-classifier-{heuristic,client}.ts: the BAS-aligned
  heuristic shared by the server classifier and a client variant that uses
  the lazy chart.
- lib/bookkeeping/invoice-accounts.ts: INVOICE_FX_RATE_MISSING,
  InvoiceFxRateMissingError, getRevenueAccount, getOutputVatAccount out of
  invoice-entries.ts, whose engine import pulled account-backfill and the
  chart into SendInvoiceDialog/PaymentBookingDialog.
- CorrectOpeningBalanceDialog re-seeds names when the chart lands;
  OpeningBalanceRowEditor builds its Fuse indexes lazily; the
  ChartOfAccountsManager BAS-katalog tab awaits the chunk.

Tooling:
- scripts/perf/client-import-closure.mjs: static import closure of every
  'use client' module with the shortest chain to a target (file or bare
  specifier); found every path above without a build.
- scripts/checks/client-node-builtin.mjs wired into check:guards: a client
  module reaching a Node builtin is a hard failure (0 today).

Left as is: invoices/[id], its credit page and SendInvoiceDialog still
reach the chart through lib/invoices/issue-credit-note -> invoice-entries
-> engine -> account-backfill; splitting the engine is out of scope here.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(perf): unambiguous import-edge regex in the closure walker (CodeQL js/redos)

One quantifier per span: a greedy [^'"]* up to the specifier quote, which it
cannot cross, so a run of whitespace has a single parse. Same edges as
before (multi-line named imports, re-exports, side-effect imports; type-only
imports still skipped).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-26 15:07:49 +02:00

125 lines
11 KiB
TypeScript

// GENERATED by scripts/generate-bas-account-numbers.ts from lib/bookkeeping/bas-data.
// Do not edit by hand: run `npx tsx scripts/generate-bas-account-numbers.ts`.
//
// The sorted list of standard BAS account numbers (~9 KB) so client code can
// check membership without importing the full chart (315 KB uncompressed).
// lib/bookkeeping/__tests__/bas-account-numbers.test.ts pins parity.
export const BAS_ACCOUNT_NUMBERS: readonly string[] = [
'1010', '1011', '1012', '1018', '1019', '1020', '1028', '1029', '1030', '1038', '1039', '1040',
'1048', '1049', '1050', '1058', '1059', '1060', '1068', '1069', '1070', '1078', '1079', '1080',
'1081', '1088', '1090', '1092', '1098', '1099', '1110', '1111', '1112', '1118', '1119', '1120',
'1129', '1130', '1140', '1150', '1158', '1159', '1180', '1181', '1188', '1210', '1211', '1212',
'1214', '1216', '1217', '1218', '1219', '1220', '1221', '1222', '1224', '1226', '1227', '1228',
'1229', '1230', '1240', '1241', '1242', '1249', '1250', '1251', '1259', '1260', '1261', '1269',
'1280', '1281', '1288', '1290', '1291', '1292', '1298', '1299', '1310', '1311', '1312', '1313',
'1314', '1316', '1317', '1318', '1320', '1321', '1322', '1323', '1328', '1330', '1331', '1332',
'1333', '1334', '1336', '1337', '1340', '1341', '1342', '1343', '1344', '1346', '1347', '1350',
'1351', '1352', '1353', '1354', '1356', '1357', '1358', '1360', '1369', '1370', '1380', '1381',
'1382', '1383', '1384', '1385', '1387', '1388', '1389', '1410', '1419', '1420', '1429', '1440',
'1449', '1450', '1459', '1460', '1465', '1466', '1467', '1469', '1470', '1471', '1478', '1479',
'1480', '1481', '1489', '1490', '1491', '1492', '1493', '1510', '1511', '1512', '1513', '1516',
'1518', '1519', '1520', '1525', '1529', '1530', '1531', '1532', '1536', '1539', '1550', '1560',
'1561', '1562', '1563', '1568', '1569', '1570', '1571', '1572', '1573', '1610', '1611', '1612',
'1613', '1614', '1619', '1620', '1630', '1640', '1650', '1660', '1661', '1662', '1663', '1670',
'1671', '1672', '1673', '1680', '1681', '1682', '1683', '1684', '1685', '1686', '1687', '1688',
'1689', '1690', '1710', '1720', '1730', '1740', '1750', '1760', '1770', '1780', '1790', '1810',
'1820', '1830', '1860', '1880', '1886', '1889', '1890', '1910', '1911', '1912', '1913', '1920',
'1930', '1940', '1950', '1960', '1970', '1972', '1973', '1974', '1979', '1980', '1990', '2010',
'2011', '2013', '2017', '2018', '2019', '2020', '2021', '2023', '2027', '2028', '2029', '2030',
'2031', '2033', '2037', '2038', '2039', '2040', '2041', '2043', '2047', '2048', '2049', '2050',
'2060', '2061', '2064', '2065', '2066', '2067', '2068', '2069', '2070', '2071', '2072', '2080',
'2081', '2082', '2083', '2084', '2085', '2086', '2087', '2088', '2089', '2090', '2091', '2092',
'2093', '2094', '2095', '2096', '2097', '2098', '2099', '2110', '2120', '2121', '2122', '2123',
'2124', '2125', '2126', '2127', '2129', '2130', '2131', '2132', '2133', '2134', '2135', '2136',
'2137', '2139', '2150', '2151', '2152', '2153', '2160', '2161', '2162', '2164', '2190', '2196',
'2199', '2210', '2220', '2230', '2240', '2250', '2252', '2253', '2290', '2310', '2320', '2321',
'2322', '2323', '2324', '2330', '2340', '2350', '2351', '2355', '2359', '2360', '2361', '2362',
'2363', '2370', '2371', '2372', '2373', '2390', '2391', '2392', '2393', '2394', '2395', '2396',
'2397', '2399', '2410', '2411', '2412', '2417', '2419', '2420', '2421', '2429', '2430', '2431',
'2438', '2439', '2440', '2441', '2443', '2445', '2448', '2450', '2460', '2461', '2462', '2463',
'2470', '2471', '2472', '2473', '2480', '2490', '2491', '2492', '2499', '2510', '2512', '2513',
'2514', '2515', '2517', '2518', '2610', '2611', '2612', '2613', '2614', '2615', '2616', '2618',
'2620', '2621', '2622', '2623', '2624', '2625', '2626', '2628', '2630', '2631', '2632', '2633',
'2634', '2635', '2636', '2638', '2640', '2641', '2642', '2645', '2646', '2647', '2648', '2649',
'2650', '2660', '2670', '2710', '2730', '2731', '2732', '2740', '2750', '2760', '2761', '2762',
'2790', '2791', '2792', '2793', '2794', '2795', '2799', '2810', '2811', '2812', '2820', '2821',
'2822', '2823', '2829', '2830', '2840', '2841', '2849', '2850', '2852', '2860', '2861', '2862',
'2863', '2870', '2871', '2872', '2873', '2880', '2890', '2891', '2892', '2893', '2895', '2897',
'2898', '2899', '2910', '2911', '2912', '2919', '2920', '2930', '2931', '2940', '2941', '2942',
'2943', '2944', '2950', '2951', '2959', '2960', '2970', '2971', '2972', '2979', '2980', '2990',
'2991', '2992', '2993', '2995', '2998', '2999', '3000', '3001', '3002', '3003', '3004', '3100',
'3105', '3106', '3108', '3200', '3211', '3212', '3231', '3300', '3305', '3308', '3400', '3401',
'3402', '3403', '3404', '3500', '3510', '3511', '3518', '3520', '3521', '3522', '3530', '3540',
'3541', '3542', '3550', '3560', '3561', '3562', '3563', '3570', '3590', '3600', '3610', '3611',
'3612', '3613', '3619', '3620', '3630', '3670', '3671', '3672', '3679', '3680', '3690', '3700',
'3710', '3730', '3731', '3732', '3740', '3750', '3751', '3752', '3790', '3800', '3840', '3850',
'3870', '3900', '3910', '3911', '3912', '3913', '3914', '3920', '3921', '3922', '3925', '3940',
'3950', '3960', '3970', '3971', '3972', '3973', '3980', '3981', '3985', '3987', '3988', '3989',
'3990', '3991', '3992', '3993', '3994', '3995', '3996', '3997', '3998', '3999', '4000', '4010',
'4060', '4065', '4066', '4067', '4070', '4075', '4076', '4077', '4078', '4080', '4085', '4086',
'4087', '4090', '4091', '4092', '4099', '4200', '4210', '4211', '4212', '4300', '4310', '4400',
'4410', '4415', '4416', '4417', '4420', '4425', '4426', '4427', '4500', '4510', '4515', '4516',
'4517', '4518', '4530', '4531', '4532', '4533', '4535', '4536', '4537', '4538', '4540', '4545',
'4546', '4547', '4598', '4600', '4610', '4670', '4700', '4730', '4731', '4732', '4739', '4800',
'4810', '4820', '4830', '4840', '4890', '4900', '4910', '4920', '4940', '4944', '4945', '4947',
'4950', '4960', '4970', '4974', '4975', '4977', '4980', '4981', '4987', '4988', '5000', '5010',
'5011', '5012', '5013', '5019', '5020', '5030', '5040', '5050', '5060', '5061', '5062', '5064',
'5065', '5069', '5070', '5090', '5100', '5110', '5120', '5130', '5131', '5132', '5139', '5140',
'5160', '5161', '5162', '5164', '5165', '5169', '5170', '5190', '5191', '5192', '5193', '5198',
'5200', '5210', '5220', '5250', '5290', '5300', '5310', '5320', '5330', '5340', '5350', '5360',
'5370', '5380', '5390', '5400', '5410', '5411', '5412', '5420', '5430', '5440', '5460', '5480',
'5500', '5510', '5520', '5530', '5550', '5580', '5590', '5600', '5610', '5611', '5612', '5613',
'5615', '5616', '5619', '5620', '5621', '5622', '5623', '5625', '5626', '5629', '5630', '5631',
'5632', '5633', '5635', '5639', '5640', '5641', '5642', '5643', '5645', '5646', '5649', '5650',
'5651', '5652', '5653', '5655', '5656', '5659', '5670', '5671', '5672', '5673', '5675', '5679',
'5680', '5681', '5682', '5683', '5685', '5689', '5690', '5691', '5692', '5693', '5695', '5696',
'5699', '5700', '5710', '5711', '5712', '5720', '5721', '5722', '5729', '5730', '5790', '5800',
'5810', '5820', '5830', '5831', '5832', '5890', '5900', '5910', '5920', '5930', '5940', '5950',
'5960', '5970', '5980', '5981', '5982', '5990', '6000', '6010', '6020', '6030', '6040', '6050',
'6055', '6059', '6060', '6061', '6062', '6063', '6064', '6069', '6070', '6071', '6072', '6080',
'6090', '6100', '6110', '6150', '6200', '6210', '6211', '6212', '6219', '6230', '6250', '6290',
'6300', '6310', '6320', '6330', '6340', '6341', '6342', '6350', '6351', '6352', '6360', '6361',
'6362', '6370', '6380', '6390', '6391', '6392', '6400', '6420', '6421', '6422', '6423', '6424',
'6430', '6440', '6450', '6490', '6500', '6510', '6520', '6530', '6540', '6550', '6551', '6552',
'6553', '6554', '6555', '6556', '6559', '6560', '6570', '6580', '6590', '6700', '6710', '6800',
'6810', '6820', '6830', '6840', '6850', '6860', '6870', '6880', '6890', '6900', '6910', '6920',
'6930', '6940', '6950', '6970', '6980', '6981', '6982', '6990', '6991', '6992', '6993', '6996',
'6997', '6998', '6999', '7000', '7010', '7011', '7012', '7013', '7017', '7018', '7019', '7030',
'7031', '7032', '7037', '7038', '7039', '7080', '7081', '7082', '7083', '7089', '7090', '7200',
'7210', '7211', '7212', '7213', '7217', '7218', '7219', '7220', '7221', '7222', '7227', '7228',
'7229', '7230', '7231', '7232', '7237', '7238', '7239', '7240', '7280', '7281', '7282', '7283',
'7284', '7285', '7286', '7288', '7289', '7290', '7291', '7292', '7300', '7310', '7311', '7312',
'7313', '7314', '7315', '7316', '7317', '7318', '7319', '7320', '7321', '7322', '7323', '7324',
'7330', '7331', '7332', '7333', '7350', '7370', '7380', '7381', '7382', '7383', '7384', '7385',
'7386', '7387', '7388', '7389', '7390', '7391', '7392', '7400', '7410', '7411', '7412', '7420',
'7430', '7440', '7441', '7448', '7460', '7461', '7462', '7463', '7470', '7490', '7500', '7510',
'7511', '7512', '7515', '7516', '7518', '7519', '7530', '7531', '7532', '7533', '7550', '7551',
'7552', '7553', '7554', '7570', '7571', '7572', '7580', '7581', '7582', '7583', '7589', '7590',
'7600', '7610', '7620', '7621', '7622', '7623', '7630', '7631', '7632', '7650', '7670', '7671',
'7678', '7690', '7691', '7692', '7693', '7699', '7710', '7720', '7730', '7731', '7732', '7733',
'7740', '7760', '7770', '7780', '7781', '7782', '7783', '7790', '7810', '7811', '7812', '7813',
'7814', '7815', '7816', '7817', '7819', '7820', '7821', '7824', '7829', '7830', '7831', '7832',
'7836', '7839', '7840', '7940', '7960', '7970', '7971', '7972', '7973', '7990', '8010', '8012',
'8016', '8020', '8030', '8070', '8072', '8076', '8077', '8080', '8082', '8086', '8087', '8110',
'8111', '8112', '8113', '8116', '8117', '8118', '8120', '8121', '8122', '8123', '8130', '8131',
'8132', '8133', '8170', '8171', '8172', '8173', '8174', '8176', '8177', '8180', '8181', '8182',
'8183', '8184', '8186', '8187', '8210', '8212', '8216', '8220', '8221', '8222', '8223', '8230',
'8231', '8236', '8240', '8250', '8251', '8252', '8254', '8255', '8260', '8261', '8262', '8263',
'8270', '8271', '8272', '8273', '8280', '8281', '8282', '8283', '8290', '8291', '8295', '8310',
'8311', '8312', '8313', '8314', '8317', '8319', '8320', '8321', '8325', '8330', '8331', '8336',
'8340', '8350', '8360', '8361', '8362', '8363', '8370', '8380', '8390', '8400', '8410', '8411',
'8412', '8413', '8415', '8417', '8418', '8419', '8420', '8421', '8422', '8423', '8424', '8429',
'8430', '8431', '8436', '8440', '8450', '8451', '8455', '8460', '8461', '8462', '8463', '8480',
'8490', '8491', '8810', '8811', '8819', '8820', '8830', '8840', '8850', '8851', '8852', '8853',
'8860', '8861', '8862', '8864', '8865', '8866', '8869', '8890', '8892', '8896', '8899', '8910',
'8920', '8930', '8940', '8980', '8990', '8999',
]
const BAS_ACCOUNT_NUMBER_SET: ReadonlySet<string> = new Set(BAS_ACCOUNT_NUMBERS)
/** Whether the number exists in the BAS chart (same answer as isStandardBASAccount). */
export function isStandardBASAccountNumber(accountNumber: string): boolean {
return BAS_ACCOUNT_NUMBER_SET.has(accountNumber)
}