@stll/money 0.3.1 → 0.4.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/README.md CHANGED
@@ -6,7 +6,7 @@ The package keeps money in integer minor units, rejects fractional cents, and
6
6
  separates totals by currency so incompatible amounts cannot be combined.
7
7
 
8
8
  ```ts
9
- import { MoneyTotals, cents } from "@stll/money";
9
+ import { cents, MoneyTotals } from "@stll/money";
10
10
 
11
11
  const totals = new MoneyTotals();
12
12
  totals.add("EUR", cents(1250));
package/dist/index.d.ts CHANGED
@@ -1,11 +1,4 @@
1
- //#region src/cents.d.ts
2
- /**
3
- * The minor-unit brand and its constructors.
4
- *
5
- * Separate from `index.ts` because `format.ts` mints amounts too: the
6
- * conversion helpers there return a `CentsAmount`, and a module the package
7
- * entry re-exports cannot import back from that entry without a cycle.
8
- */
1
+ import { Result } from "better-result";
9
2
  declare const __cents: unique symbol;
10
3
  type CentsAmount = number & {
11
4
  readonly [__cents]: "CentsAmount";
@@ -13,7 +6,7 @@ type CentsAmount = number & {
13
6
  /**
14
7
  * Construct a CentsAmount from a value already known to be in minor
15
8
  * units. Use at boundaries where the input is validated as an integer
16
- * minor-unit value (e.g. after Elysia `tMinorUnitAmount(...)` or after
9
+ * minor-unit value (e.g. after `minorUnitAmountSchema(...)` or after
17
10
  * scaling a typed major-unit amount by the currency's exponent).
18
11
  *
19
12
  * SAFE integer, not merely integer: `Number.isInteger` is true for 2^53 and
@@ -30,8 +23,6 @@ export declare const cents: (value: number) => CentsAmount;
30
23
  * comment naming why the value is already a valid minor-unit integer.
31
24
  */
32
25
  export declare const unsafeCents: (value: number) => CentsAmount;
33
- //#endregion
34
- //#region src/format.d.ts
35
26
  /**
36
27
  * How many minor units make a major one, for this currency: 100 for CZK, 1 for
37
28
  * JPY, 1000 for KWD. It is a property of the currency and not of the reader, so
@@ -41,6 +32,55 @@ export declare const unsafeCents: (value: number) => CentsAmount;
41
32
  * `?? 2` on its result could help, so the fallback has to wrap the call.
42
33
  */
43
34
  export declare const currencyMinorUnitDigits: (currency: string) => number;
35
+ type LocaleAmountParseError = {
36
+ reason: "empty" | "syntax" | "ambiguous" | "precision" | "unsafe-integer" | "locale";
37
+ message: string;
38
+ };
39
+ type ParseLocaleMinorUnitsParams = {
40
+ amount: string;
41
+ currency: string;
42
+ locale: string;
43
+ };
44
+ type ParseLocaleDecimalParams = {
45
+ amount: string;
46
+ locale: string;
47
+ maximumFractionDigits?: number;
48
+ };
49
+ declare const localeInputTextBrand: unique symbol;
50
+ /** Text whose separators and digits belong to a person's formatting locale. */
51
+ type LocaleInputText = string & {
52
+ readonly [localeInputTextBrand]: "LocaleInputText";
53
+ };
54
+ /** The control a person typed into: an input or textarea element. */
55
+ type LocaleInputSource = {
56
+ readonly value: string;
57
+ readonly tagName: string;
58
+ };
59
+ /**
60
+ * The text of the control a person typed into. It takes the element, not its
61
+ * string, so canonical stored text cannot be passed off as locale input; stored
62
+ * values reach a field only through the redisplay formatters below.
63
+ */
64
+ export declare const localeInputText: (source: LocaleInputSource) => LocaleInputText;
65
+ /** The text of a cleared locale input. */
66
+ export declare const EMPTY_LOCALE_INPUT_TEXT: LocaleInputText;
67
+ /** Normalize a human-entered locale decimal without converting through a float. */
68
+ export declare const parseLocaleDecimal: ({ amount, locale, maximumFractionDigits }: ParseLocaleDecimalParams) => Result<string, LocaleAmountParseError>;
69
+ /** Parse human-entered major units according to their formatting locale. */
70
+ export declare const parseLocaleMinorUnits: ({ amount, currency, locale }: ParseLocaleMinorUnitsParams) => Result<CentsAmount, LocaleAmountParseError>;
71
+ type FormatLocaleMinorUnitsInputParams = {
72
+ amountCents: number;
73
+ currency: string;
74
+ locale: string;
75
+ };
76
+ type FormatLocaleDecimalInputParams = {
77
+ amount: string;
78
+ locale: string;
79
+ };
80
+ /** Render canonical decimal text using locale digits and its decimal mark. */
81
+ export declare const formatLocaleDecimalInput: ({ amount, locale }: FormatLocaleDecimalInputParams) => LocaleInputText;
82
+ /** Render stored minor units as locale input text that parses back unchanged. */
83
+ export declare const formatLocaleMinorUnitsInput: ({ amountCents, currency, locale }: FormatLocaleMinorUnitsInputParams) => LocaleInputText;
44
84
  type ToMinorUnitsParams = {
45
85
  /**
46
86
  * The amount in major units: the decimal text a form holds, or a number.
@@ -84,6 +124,14 @@ type ToMajorUnitsParams = {
84
124
  };
85
125
  /** The inverse: a stored amount as the major-unit number a person reads. */
86
126
  export declare const toMajorUnits: ({ amountCents, currency }: ToMajorUnitsParams) => number;
127
+ /**
128
+ * A stored amount as exact major-unit decimal text, with no grouping and a
129
+ * dot separator: 123456 EUR is "1234.56", 1500 JPY is "1500". The digits are
130
+ * moved, not divided, so a value near the safe-integer edge keeps every minor
131
+ * unit that `toMajorUnits` would round away. For machine readers (a prompt,
132
+ * an export), not for display; display goes through `formatMoneyCents`.
133
+ */
134
+ export declare const toMajorUnitsText: ({ amountCents, currency }: ToMajorUnitsParams) => string;
87
135
  type FormatMoneyCentsParams = {
88
136
  amountCents: number;
89
137
  currency: string;
@@ -103,8 +151,6 @@ type FormatMoneyCentsParams = {
103
151
  * RangeError would take the whole board down with it.
104
152
  */
105
153
  export declare const formatMoneyCents: ({ amountCents, currency, locale, fractionDigits }: FormatMoneyCentsParams) => string;
106
- //#endregion
107
- //#region src/index.d.ts
108
154
  export type ProrateHourlyCentsInput = {
109
155
  billedMinutes: number;
110
156
  hourlyRateCents: CentsAmount;
@@ -156,7 +202,7 @@ type IsSingletonCurrency<C extends string> = string extends C ? never : [C] exte
156
202
  * Both operands are further constrained by `IsSingletonCurrency` to reject
157
203
  * any non-singleton currency type: the WIDE `CurrencyCents<string>`, and
158
204
  * also a finite union such as `CurrencyCents<"USD" | "EUR">` (e.g. from a
159
- * validator's `t.UnionEnum`). A currency read back from a DB row or
205
+ * validator's `v.picklist`). A currency read back from a DB row or
160
206
  * narrowed only to a finite set of allowed codes types as `string` or a
161
207
  * union, not a single literal, so without this, `addCents(currencyCents(a,
162
208
  * x), currencyCents(b, y))` would still typecheck even when `a`/`b` are
@@ -204,5 +250,4 @@ export declare class MoneyTotals {
204
250
  */
205
251
  entries(): MoneyTotalsEntry[];
206
252
  }
207
- //#endregion
208
- export type { CentsAmount, FormatMoneyCentsParams, ToMajorUnitsParams, ToMinorUnitsParams };
253
+ export type { CentsAmount, FormatLocaleDecimalInputParams, FormatLocaleMinorUnitsInputParams, FormatMoneyCentsParams, LocaleAmountParseError, LocaleInputSource, LocaleInputText, ParseLocaleDecimalParams, ParseLocaleMinorUnitsParams, ToMajorUnitsParams, ToMinorUnitsParams };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Result, panic } from "better-result";
2
- //#region src/cents.ts
2
+ // src/cents.ts
3
3
  /**
4
4
  * The minor-unit brand and its constructors.
5
5
  *
@@ -12,7 +12,7 @@ const isMinorUnitAmount = (value) => Number.isSafeInteger(value);
12
12
  /**
13
13
  * Construct a CentsAmount from a value already known to be in minor
14
14
  * units. Use at boundaries where the input is validated as an integer
15
- * minor-unit value (e.g. after Elysia `tMinorUnitAmount(...)` or after
15
+ * minor-unit value (e.g. after `minorUnitAmountSchema(...)` or after
16
16
  * scaling a typed major-unit amount by the currency's exponent).
17
17
  *
18
18
  * SAFE integer, not merely integer: `Number.isInteger` is true for 2^53 and
@@ -32,8 +32,7 @@ const cents = (value) => {
32
32
  * comment naming why the value is already a valid minor-unit integer.
33
33
  */
34
34
  const unsafeCents = (value) => value;
35
- //#endregion
36
- //#region src/format.ts
35
+ // src/format.ts
37
36
  /**
38
37
  * Moving between a stored minor-unit amount and the major-unit number a
39
38
  * person types, and rendering either as text.
@@ -72,6 +71,176 @@ const currencyMinorUnitDigits = (currency) => {
72
71
  };
73
72
  /** A decimal amount in major units: an optional sign, digits, an optional point. */
74
73
  const DECIMAL_AMOUNT = /^(?<sign>[+-]?)(?<whole>\d*)(?:\.(?<fraction>\d*))?$/u;
74
+ const localeSeparators = (locale) => Result.try(() => {
75
+ const formatter = new Intl.NumberFormat(locale, { useGrouping: false });
76
+ const groupedParts = new Intl.NumberFormat(locale).formatToParts(1234567890123);
77
+ const digits = /* @__PURE__ */ new Map();
78
+ for (let digit = 0; digit <= 9; digit += 1) digits.set(formatter.format(digit), String(digit));
79
+ const integerGroups = groupedParts.filter((part) => part.type === "integer").map((part) => part.value.length);
80
+ return {
81
+ decimal: new Intl.NumberFormat(locale).formatToParts(12345.6).find((part) => part.type === "decimal")?.value ?? ".",
82
+ group: groupedParts.find((part) => part.type === "group")?.value,
83
+ primaryGroupSize: integerGroups.at(-1) ?? 3,
84
+ secondaryGroupSize: integerGroups.at(-2) ?? integerGroups.at(-1) ?? 3,
85
+ digits
86
+ };
87
+ });
88
+ const normalizeGroupedWhole = ({ whole, grouping, primaryGroupSize, secondaryGroupSize }) => {
89
+ const groups = whole.split(grouping);
90
+ const leadingGroup = groups.at(0) ?? "";
91
+ const trailingGroup = groups.at(-1) ?? "";
92
+ const middleGroups = groups.slice(1, -1);
93
+ if (!new RegExp(`^[0-9]{1,${secondaryGroupSize}}$`, "u").test(leadingGroup) || !new RegExp(`^[0-9]{${primaryGroupSize}}$`, "u").test(trailingGroup) || middleGroups.some((part) => !new RegExp(`^[0-9]{${secondaryGroupSize}}$`, "u").test(part))) return null;
94
+ return groups.join("");
95
+ };
96
+ const ASCII_DECIMAL_MARKS = [".", ","];
97
+ const normalizeInputSeparators = ({ unsigned, decimal, group, primaryGroupSize }) => {
98
+ const alternateDecimal = ASCII_DECIMAL_MARKS.filter((mark) => mark !== decimal).find((mark) => unsigned.includes(mark)) ?? (decimal === "," ? "." : ",");
99
+ const alternateParts = unsigned.split(alternateDecimal);
100
+ const hasLocaleDecimal = unsigned.includes(decimal);
101
+ const hasSingleAlternate = alternateParts.length === 2;
102
+ const alternateFractionLength = alternateParts.at(1)?.length;
103
+ if (!hasLocaleDecimal && hasSingleAlternate && alternateFractionLength === primaryGroupSize) return { status: "ambiguous" };
104
+ if (!hasLocaleDecimal && hasSingleAlternate && alternateFractionLength !== primaryGroupSize) return {
105
+ status: "normalized",
106
+ unsigned: unsigned.replace(alternateDecimal, () => decimal),
107
+ grouping: group
108
+ };
109
+ let grouping = group;
110
+ if (decimal === "," && unsigned.split(decimal).at(0)?.includes(".")) grouping = ".";
111
+ return {
112
+ status: "normalized",
113
+ unsigned,
114
+ grouping
115
+ };
116
+ };
117
+ /**
118
+ * The fraction kept at the allowed places, undefined when none is left, or
119
+ * null when a digit past them is not zero. Trailing zeros carry no value
120
+ * (100.00 yen is 100 yen); any other extra digit would need rounding.
121
+ */
122
+ const exactFraction = (fraction, maximumFractionDigits) => {
123
+ if (fraction === void 0 || maximumFractionDigits === void 0 || fraction.length <= maximumFractionDigits) return fraction;
124
+ if (!/^0*$/u.test(fraction.slice(maximumFractionDigits))) return null;
125
+ const kept = fraction.slice(0, maximumFractionDigits);
126
+ return kept === "" ? void 0 : kept;
127
+ };
128
+ function brandLocaleInputText(value) {
129
+ return value;
130
+ }
131
+ /**
132
+ * The text of the control a person typed into. It takes the element, not its
133
+ * string, so canonical stored text cannot be passed off as locale input; stored
134
+ * values reach a field only through the redisplay formatters below.
135
+ */
136
+ const localeInputText = (source) => brandLocaleInputText(source.value);
137
+ /** The text of a cleared locale input. */
138
+ const EMPTY_LOCALE_INPUT_TEXT = brandLocaleInputText("");
139
+ /** Normalize a human-entered locale decimal without converting through a float. */
140
+ const parseLocaleDecimal = ({ amount, locale, maximumFractionDigits }) => {
141
+ const text = amount.trim();
142
+ if (text.length === 0) return Result.err({
143
+ reason: "empty",
144
+ message: "Amount is empty"
145
+ });
146
+ const separators = localeSeparators(locale);
147
+ if (separators.isErr()) return Result.err({
148
+ reason: "locale",
149
+ message: "Invalid locale"
150
+ });
151
+ const { decimal, group, primaryGroupSize, secondaryGroupSize, digits } = separators.value;
152
+ const whitespaceGroup = group !== void 0 && /^\s$/u.test(group);
153
+ let localizedDigits = "";
154
+ for (const character of text) localizedDigits += digits.get(character) ?? character;
155
+ const normalizedWhitespace = whitespaceGroup ? localizedDigits.replace(/[\u0020\u00a0\u202f]/gu, () => group) : localizedDigits;
156
+ const sign = /^[+-]/u.exec(normalizedWhitespace)?.at(0) ?? "";
157
+ const unsignedInput = sign ? normalizedWhitespace.slice(1) : normalizedWhitespace;
158
+ const normalizedSeparators = normalizeInputSeparators({
159
+ unsigned: unsignedInput,
160
+ decimal,
161
+ group,
162
+ primaryGroupSize
163
+ });
164
+ if (normalizedSeparators.status === "ambiguous") return Result.err({
165
+ reason: "ambiguous",
166
+ message: "Amount has an ambiguous separator"
167
+ });
168
+ const { unsigned, grouping } = normalizedSeparators;
169
+ const decimalParts = unsigned.split(decimal);
170
+ if (decimalParts.length > 2 || decimalParts.length === 1 && decimalParts.at(0)?.length === 0 || decimalParts.length === 2 && decimalParts.at(1)?.length === 0) return Result.err({
171
+ reason: "syntax",
172
+ message: "Invalid amount format"
173
+ });
174
+ const whole = decimalParts.at(0) || "0";
175
+ const fraction = decimalParts.at(1);
176
+ let wholeDigits = whole;
177
+ if (grouping !== void 0 && whole.includes(grouping)) {
178
+ const normalizedWhole = normalizeGroupedWhole({
179
+ whole,
180
+ grouping,
181
+ primaryGroupSize,
182
+ secondaryGroupSize
183
+ });
184
+ if (normalizedWhole === null) return Result.err({
185
+ reason: "syntax",
186
+ message: "Invalid amount grouping"
187
+ });
188
+ wholeDigits = normalizedWhole;
189
+ }
190
+ if (!/^[0-9]+$/u.test(wholeDigits) || fraction !== void 0 && !/^[0-9]+$/u.test(fraction)) return Result.err({
191
+ reason: "syntax",
192
+ message: "Invalid amount format"
193
+ });
194
+ const exact = exactFraction(fraction, maximumFractionDigits);
195
+ if (exact === null) return Result.err({
196
+ reason: "precision",
197
+ message: "Amount has too many fraction digits"
198
+ });
199
+ return Result.ok(`${sign}${wholeDigits}${exact === void 0 ? "" : `.${exact}`}`);
200
+ };
201
+ /** Parse human-entered major units according to their formatting locale. */
202
+ const parseLocaleMinorUnits = ({ amount, currency, locale }) => {
203
+ const normalized = parseLocaleDecimal({
204
+ amount,
205
+ locale,
206
+ maximumFractionDigits: currencyMinorUnitDigits(currency)
207
+ });
208
+ if (normalized.isErr()) return normalized;
209
+ const parsed = tryToMinorUnits({
210
+ amount: normalized.value,
211
+ currency
212
+ });
213
+ return parsed === null ? Result.err({
214
+ reason: "unsafe-integer",
215
+ message: "Amount exceeds the supported range"
216
+ }) : Result.ok(parsed);
217
+ };
218
+ const localizeAsciiDigits = (value, digits) => {
219
+ const localizedByAscii = new Map([...digits].map(([localized, ascii]) => [ascii, localized]));
220
+ return value.split("").map((character) => localizedByAscii.get(character) ?? character).join("");
221
+ };
222
+ /** Render canonical decimal text using locale digits and its decimal mark. */
223
+ const formatLocaleDecimalInput = ({ amount, locale }) => {
224
+ const groups = DECIMAL_AMOUNT.exec(amount)?.groups;
225
+ const whole = groups?.["whole"] ?? "";
226
+ const fraction = groups?.["fraction"];
227
+ if (groups === void 0 || `${whole}${fraction ?? ""}`.length === 0) return panic(`formatLocaleDecimalInput(${JSON.stringify(amount)}): not a canonical decimal`);
228
+ const separators = localeSeparators(locale).unwrap("formatLocaleDecimalInput: invalid locale");
229
+ const canonical = `${groups["sign"] ?? ""}${whole || "0"}${fraction === void 0 ? "" : `${separators.decimal}${fraction}`}`;
230
+ return brandLocaleInputText(localizeAsciiDigits(canonical, separators.digits));
231
+ };
232
+ /** Render stored minor units as locale input text that parses back unchanged. */
233
+ const formatLocaleMinorUnitsInput = ({ amountCents, currency, locale }) => {
234
+ const fractionDigits = currencyMinorUnitDigits(currency);
235
+ const sign = amountCents < 0 ? "-" : "";
236
+ const digits = String(Math.abs(amountCents)).padStart(fractionDigits + 1, "0");
237
+ const split = digits.length - fractionDigits;
238
+ const canonical = fractionDigits === 0 ? `${sign}${digits}` : `${sign}${digits.slice(0, split)}.${digits.slice(split)}`;
239
+ return formatLocaleDecimalInput({
240
+ amount: canonical,
241
+ locale
242
+ });
243
+ };
75
244
  /** Scientific notation, which `String(1e21)` and `String(1e-7)` both produce. */
76
245
  const EXPONENT_AMOUNT = /^(?<sign>[+-]?)(?<whole>\d+)(?:\.(?<fraction>\d+))?[eE](?<exponent>[+-]?\d+)$/u;
77
246
  /**
@@ -163,6 +332,21 @@ const tryToMinorUnits = ({ amount, currency }) => {
163
332
  /** The inverse: a stored amount as the major-unit number a person reads. */
164
333
  const toMajorUnits = ({ amountCents, currency }) => amountCents / 10 ** currencyMinorUnitDigits(currency);
165
334
  /**
335
+ * A stored amount as exact major-unit decimal text, with no grouping and a
336
+ * dot separator: 123456 EUR is "1234.56", 1500 JPY is "1500". The digits are
337
+ * moved, not divided, so a value near the safe-integer edge keeps every minor
338
+ * unit that `toMajorUnits` would round away. For machine readers (a prompt,
339
+ * an export), not for display; display goes through `formatMoneyCents`.
340
+ */
341
+ const toMajorUnitsText = ({ amountCents, currency }) => {
342
+ const digits = currencyMinorUnitDigits(currency);
343
+ const magnitude = BigInt(Math.abs(amountCents)).toString();
344
+ const sign = amountCents < 0 ? "-" : "";
345
+ if (digits === 0) return `${sign}${magnitude}`;
346
+ const padded = magnitude.padStart(digits + 1, "0");
347
+ return `${sign}${padded.slice(0, -digits)}.${padded.slice(-digits)}`;
348
+ };
349
+ /**
166
350
  * Money is stored in minor units, and how many of them make a major one is a
167
351
  * property of the currency. Ask the currency rather than assuming a hundred.
168
352
  *
@@ -200,8 +384,7 @@ const formatMoneyCents = ({ amountCents, currency, locale, fractionDigits }) =>
200
384
  });
201
385
  return formatted.isErr() ? `${negative ? "-" : ""}${whole}${digits === 0 ? "" : `.${fraction}`} ${currency}` : formatted.value;
202
386
  };
203
- //#endregion
204
- //#region src/index.ts
387
+ // src/index.ts
205
388
  /**
206
389
  * Branded monetary amounts and minor-unit billing arithmetic.
207
390
  *
@@ -212,8 +395,9 @@ const formatMoneyCents = ({ amountCents, currency, locale, fractionDigits }) =>
212
395
  *
213
396
  * The brand lives in this package, so it threads end to end:
214
397
  * the same `CentsAmount` flows from a Drizzle column declared with
215
- * `.$type<CentsAmount>()`, across the API boundary (Eden infers the
216
- * brand from the handler's return type), into browser previews and back.
398
+ * `.$type<CentsAmount>()`, across the API boundary (the API route types
399
+ * carry the brand from the handler's return type), into browser previews
400
+ * and back.
217
401
  * A plain `number` is not assignable to `CentsAmount`; mint one with
218
402
  * `cents()` after validating minor-unit input, or `unsafeCents()` at a
219
403
  * documented boundary.
@@ -263,7 +447,7 @@ const currencyCents = (currency, amount) => {
263
447
  * Both operands are further constrained by `IsSingletonCurrency` to reject
264
448
  * any non-singleton currency type: the WIDE `CurrencyCents<string>`, and
265
449
  * also a finite union such as `CurrencyCents<"USD" | "EUR">` (e.g. from a
266
- * validator's `t.UnionEnum`). A currency read back from a DB row or
450
+ * validator's `v.picklist`). A currency read back from a DB row or
267
451
  * narrowed only to a finite set of allowed codes types as `string` or a
268
452
  * union, not a single literal, so without this, `addCents(currencyCents(a,
269
453
  * x), currencyCents(b, y))` would still typecheck even when `a`/`b` are
@@ -296,5 +480,4 @@ var MoneyTotals = class {
296
480
  }));
297
481
  }
298
482
  };
299
- //#endregion
300
- export { MoneyTotals, addCents, applyMarkupCents, cents, currencyCents, currencyMinorUnitDigits, formatMoneyCents, prorateHourlyCents, timeEntryAmount, toMajorUnits, toMinorUnits, tryToMinorUnits, unsafeCents };
483
+ export { EMPTY_LOCALE_INPUT_TEXT, MoneyTotals, addCents, applyMarkupCents, cents, currencyCents, currencyMinorUnitDigits, formatLocaleDecimalInput, formatLocaleMinorUnitsInput, formatMoneyCents, localeInputText, parseLocaleDecimal, parseLocaleMinorUnits, prorateHourlyCents, timeEntryAmount, toMajorUnits, toMajorUnitsText, toMinorUnits, tryToMinorUnits, unsafeCents };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/money",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Branded minor-unit monetary amounts and currency-safe billing arithmetic.",
5
5
  "keywords": [
6
6
  "billing",
@@ -39,10 +39,9 @@
39
39
  "build": "tsdown",
40
40
  "pack:dry-run": "bun pm pack --dry-run",
41
41
  "test": "bun test src",
42
- "typecheck": "bun ../../packages/scripts/src/tsc-native.ts --noEmit",
42
+ "typecheck": "bun check --no-pretty --all --project=tsconfig.json",
43
43
  "lint": "cd ../.. && bun --bun oxlint -c oxlint.config.ts --report-unused-disable-directives-severity=error --type-aware --type-check packages/money",
44
44
  "lint:fix": "cd ../.. && bun --bun oxlint -c oxlint.config.ts --type-aware --fix packages/money",
45
- "format": "bun ../../scripts/run-oxfmt.ts .",
46
45
  "prepack": "bun run build"
47
46
  },
48
47
  "dependencies": {
@@ -50,8 +49,8 @@
50
49
  },
51
50
  "devDependencies": {
52
51
  "@stll/typescript-config": "0.0.0",
53
- "bun-types": "1.4.2",
54
- "tsdown": "0.23.0"
52
+ "bun-types": "1.4.4",
53
+ "tsdown": "0.23.4"
55
54
  },
56
55
  "main": "./dist/index.js",
57
56
  "types": "./dist/index.d.ts"