@manta-eu/lib 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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/dist/ad-platform-links.d.ts +7 -0
  3. package/dist/ad-platform-links.d.ts.map +1 -0
  4. package/dist/ad-platform-links.js +13 -0
  5. package/dist/array.d.ts +3 -0
  6. package/dist/array.d.ts.map +1 -0
  7. package/dist/array.js +7 -0
  8. package/dist/asset-upload-grouping.d.ts +29 -0
  9. package/dist/asset-upload-grouping.d.ts.map +1 -0
  10. package/dist/asset-upload-grouping.js +33 -0
  11. package/dist/color.d.ts +17 -0
  12. package/dist/color.d.ts.map +1 -0
  13. package/dist/color.js +61 -0
  14. package/dist/countries.d.ts +3 -0
  15. package/dist/countries.d.ts.map +1 -0
  16. package/dist/countries.js +18 -0
  17. package/dist/date.d.ts +9 -0
  18. package/dist/date.d.ts.map +1 -0
  19. package/dist/date.js +27 -0
  20. package/dist/enum.d.ts +6 -0
  21. package/dist/enum.d.ts.map +1 -0
  22. package/dist/enum.js +9 -0
  23. package/dist/file.d.ts +4 -0
  24. package/dist/file.d.ts.map +1 -0
  25. package/dist/file.js +14 -0
  26. package/dist/humanize.d.ts +22 -0
  27. package/dist/humanize.d.ts.map +1 -0
  28. package/dist/humanize.js +70 -0
  29. package/dist/identifier-words.d.ts +3 -0
  30. package/dist/identifier-words.d.ts.map +1 -0
  31. package/dist/identifier-words.js +15 -0
  32. package/dist/number.d.ts +2 -0
  33. package/dist/number.d.ts.map +1 -0
  34. package/dist/number.js +9 -0
  35. package/dist/object-url.d.ts +3 -0
  36. package/dist/object-url.d.ts.map +1 -0
  37. package/dist/object-url.js +10 -0
  38. package/dist/present.d.ts +5 -0
  39. package/dist/present.d.ts.map +1 -0
  40. package/dist/present.js +23 -0
  41. package/dist/set.d.ts +3 -0
  42. package/dist/set.d.ts.map +1 -0
  43. package/dist/set.js +11 -0
  44. package/dist/sleep.d.ts +2 -0
  45. package/dist/sleep.d.ts.map +1 -0
  46. package/dist/sleep.js +3 -0
  47. package/dist/string.d.ts +8 -0
  48. package/dist/string.d.ts.map +1 -0
  49. package/dist/string.js +13 -0
  50. package/dist/types.d.ts +2 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +1 -0
  53. package/dist/url.d.ts +9 -0
  54. package/dist/url.d.ts.map +1 -0
  55. package/dist/url.js +40 -0
  56. package/package.json +71 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Manta AI AS
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,7 @@
1
+ export type MetaAdsManagerCampaignLinkOptions = {
2
+ adAccountExternalId: string;
3
+ campaignExternalId: string[];
4
+ marketerPartnerBusinessId: boolean;
5
+ };
6
+ export declare function generateMetaAdsManagerCampaignLink(args: MetaAdsManagerCampaignLinkOptions): string;
7
+ //# sourceMappingURL=ad-platform-links.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ad-platform-links.d.ts","sourceRoot":"","sources":["../src/ad-platform-links.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iCAAiC,GAAG;IAC9C,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,yBAAyB,EAAE,OAAO,CAAC;CACpC,CAAC;AAEF,wBAAgB,kCAAkC,CAAC,IAAI,EAAE,iCAAiC,UAYzF"}
@@ -0,0 +1,13 @@
1
+ export function generateMetaAdsManagerCampaignLink(args) {
2
+ const { adAccountExternalId, campaignExternalId, marketerPartnerBusinessId } = args;
3
+ const recordSeparator = String.fromCharCode(0x1e);
4
+ const campaignIds = campaignExternalId.map((id) => `"${id}"`).join(",");
5
+ const filterSet = `SEARCH_BY_CAMPAIGN_GROUP_IDS-STRING_SET${recordSeparator}ANY${recordSeparator}[${campaignIds}]`;
6
+ const url = new URL("https://adsmanager.facebook.com/adsmanager/manage/campaigns");
7
+ url.searchParams.set("act", adAccountExternalId);
8
+ if (marketerPartnerBusinessId) {
9
+ url.searchParams.set("business_id", "845646597259963");
10
+ }
11
+ url.searchParams.set("filter_set", filterSet);
12
+ return url.toString();
13
+ }
@@ -0,0 +1,3 @@
1
+ export declare function arrayWrap<T>(value: T | T[]): T[];
2
+ export declare function replaceAtIndex<T>(arr: T[], index: number, item: T): T[];
3
+ //# sourceMappingURL=array.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"array.d.ts","sourceRoot":"","sources":["../src/array.ts"],"names":[],"mappings":"AAEA,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,CAEhD;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAEvE"}
package/dist/array.js ADDED
@@ -0,0 +1,7 @@
1
+ import { update } from "ramda";
2
+ export function arrayWrap(value) {
3
+ return Array.isArray(value) ? value : [value];
4
+ }
5
+ export function replaceAtIndex(arr, index, item) {
6
+ return update(index, item, arr);
7
+ }
@@ -0,0 +1,29 @@
1
+ export type ParsedAssetFilename<TRatio> = {
2
+ baseName: string;
3
+ ratio: TRatio | undefined;
4
+ };
5
+ export type ParseAssetFilenameWithSuffixMapProps<TRatio> = {
6
+ filename: string;
7
+ suffixMap: Record<string, TRatio>;
8
+ };
9
+ export declare function parseAssetFilenameWithSuffixMap<TRatio>(props: ParseAssetFilenameWithSuffixMapProps<TRatio>): ParsedAssetFilename<TRatio>;
10
+ export type UploadGroupingItem<TAsset, TRatio> = {
11
+ asset: TAsset;
12
+ ratio: TRatio | undefined;
13
+ baseName: string;
14
+ };
15
+ export type UploadedAssetGroup<TAsset, TRatio> = {
16
+ baseName: string;
17
+ assets: Array<{
18
+ asset: TAsset;
19
+ ratio: TRatio;
20
+ }>;
21
+ };
22
+ export type GroupUploadedAssetsByBaseNameProps<TAsset, TRatio> = {
23
+ results: UploadGroupingItem<TAsset, TRatio>[];
24
+ };
25
+ export declare function groupUploadedAssetsByBaseName<TAsset, TRatio>(props: GroupUploadedAssetsByBaseNameProps<TAsset, TRatio>): {
26
+ groups: UploadedAssetGroup<TAsset, TRatio>[];
27
+ unmatched: TAsset[];
28
+ };
29
+ //# sourceMappingURL=asset-upload-grouping.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset-upload-grouping.d.ts","sourceRoot":"","sources":["../src/asset-upload-grouping.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,CAAC,MAAM,IAAI;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,oCAAoC,CAAC,MAAM,IAAI;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC,CAAC;AAEF,wBAAgB,+BAA+B,CAAC,MAAM,EACpD,KAAK,EAAE,oCAAoC,CAAC,MAAM,CAAC,GAClD,mBAAmB,CAAC,MAAM,CAAC,CAc7B;AAED,MAAM,MAAM,kBAAkB,CAAC,MAAM,EAAE,MAAM,IAAI;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,kBAAkB,CAAC,MAAM,EAAE,MAAM,IAAI;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjD,CAAC;AAEF,MAAM,MAAM,kCAAkC,CAAC,MAAM,EAAE,MAAM,IAAI;IAC/D,OAAO,EAAE,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;CAC/C,CAAC;AAEF,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,MAAM,EAC1D,KAAK,EAAE,kCAAkC,CAAC,MAAM,EAAE,MAAM,CAAC,GACxD;IACD,MAAM,EAAE,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC7C,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB,CAqBA"}
@@ -0,0 +1,33 @@
1
+ export function parseAssetFilenameWithSuffixMap(props) {
2
+ const { filename, suffixMap } = props;
3
+ const nameWithoutExt = filename.replace(/\.[^.]+$/, "");
4
+ const lower = nameWithoutExt.toLowerCase();
5
+ const suffixEntries = Object.entries(suffixMap).sort((a, b) => b[0].length - a[0].length);
6
+ for (const [suffix, ratio] of suffixEntries) {
7
+ if (!lower.endsWith(suffix))
8
+ continue;
9
+ const rawBaseName = nameWithoutExt.slice(0, -suffix.length);
10
+ const baseName = rawBaseName.replace(/[_\-\s]+$/, "").trim();
11
+ return { baseName: baseName || nameWithoutExt, ratio };
12
+ }
13
+ return { baseName: nameWithoutExt.trim(), ratio: undefined };
14
+ }
15
+ export function groupUploadedAssetsByBaseName(props) {
16
+ const { results } = props;
17
+ const groupMap = new Map();
18
+ const unmatched = [];
19
+ for (const result of results) {
20
+ if (result.ratio === undefined || result.baseName.trim() === "") {
21
+ unmatched.push(result.asset);
22
+ continue;
23
+ }
24
+ const key = result.baseName.toLowerCase();
25
+ const group = groupMap.get(key) ?? { baseName: result.baseName, assets: [] };
26
+ group.assets.push({ asset: result.asset, ratio: result.ratio });
27
+ groupMap.set(key, group);
28
+ }
29
+ return {
30
+ groups: [...groupMap.values()],
31
+ unmatched,
32
+ };
33
+ }
@@ -0,0 +1,17 @@
1
+ import type { DateArg } from "date-fns";
2
+ export type RGBColor = {
3
+ r: number;
4
+ g: number;
5
+ b: number;
6
+ };
7
+ export type HexColor = `#${string}`;
8
+ export declare const hexToRgb: (hex: HexColor) => RGBColor;
9
+ export declare function rgbToHex({ r, g, b }: RGBColor): HexColor;
10
+ export declare function capBrightness(hexColor: HexColor, maxBrightness?: number): string;
11
+ export declare const generateHexColorBasedOnName: (name: string) => {
12
+ color: string;
13
+ contrast: string;
14
+ };
15
+ export declare const getAverageColor: (ctx: CanvasRenderingContext2D, width: number, height: number) => RGBColor;
16
+ export declare function getAgeColor(date: DateArg<Date>): string;
17
+ //# sourceMappingURL=color.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../src/color.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAGxC,MAAM,MAAM,QAAQ,GAAG;IACrB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,IAAI,MAAM,EAAE,CAAC;AAEpC,eAAO,MAAM,QAAQ,GAAI,KAAK,QAAQ,KAAG,QAmBxC,CAAC;AAEF,wBAAgB,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,QAAQ,GAAG,QAAQ,CAOxD;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,EAAE,aAAa,GAAE,MAAY,GAAG,MAAM,CAQrF;AAED,eAAO,MAAM,2BAA2B,GAAI,MAAM,MAAM;;;CAOvD,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,KAAK,wBAAwB,EAAE,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAG,QAsB9F,CAAC;AAEF,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAOvD"}
package/dist/color.js ADDED
@@ -0,0 +1,61 @@
1
+ import { differenceInHours } from "date-fns";
2
+ export const hexToRgb = (hex) => {
3
+ let sanitizedHex = hex.replace("#", "");
4
+ if (sanitizedHex.length === 3) {
5
+ sanitizedHex = sanitizedHex
6
+ .split("")
7
+ .map((h) => `${h}${h}`)
8
+ .join("");
9
+ }
10
+ const bigint = parseInt(sanitizedHex, 16);
11
+ if (isNaN(bigint))
12
+ throw new Error(`Invalid hex value: ${hex}`);
13
+ return {
14
+ r: (bigint >> 16) & 255,
15
+ g: (bigint >> 8) & 255,
16
+ b: bigint & 255,
17
+ };
18
+ };
19
+ export function rgbToHex({ r, g, b }) {
20
+ const componentToHex = (c) => {
21
+ const hex = c.toString(16);
22
+ return hex.length === 1 ? `0${hex}` : hex;
23
+ };
24
+ return `#${componentToHex(r)}${componentToHex(g)}${componentToHex(b)}`;
25
+ }
26
+ export function capBrightness(hexColor, maxBrightness = 150) {
27
+ let { r, g, b } = hexToRgb(hexColor);
28
+ r = Math.min(r, maxBrightness);
29
+ g = Math.min(g, maxBrightness);
30
+ b = Math.min(b, maxBrightness);
31
+ return rgbToHex({ r, g, b });
32
+ }
33
+ export const generateHexColorBasedOnName = (name) => {
34
+ const hash = name.split("").reduce((acc, char) => acc + char.charCodeAt(0), 0);
35
+ const color = `hsl(${hash % 360}, 50%, 50%)`;
36
+ const contrast = hash % 360 > 180 ? "black" : "white";
37
+ return { color, contrast };
38
+ };
39
+ export const getAverageColor = (ctx, width, height) => {
40
+ const imageData = ctx.getImageData(0, 0, width, height);
41
+ const data = imageData.data;
42
+ const length = data.length;
43
+ let r = 0, g = 0, b = 0, count = 0;
44
+ for (let i = 0; i < length; i += 4) {
45
+ r += data[i];
46
+ g += data[i + 1];
47
+ b += data[i + 2];
48
+ count++;
49
+ }
50
+ return {
51
+ r: Math.floor(r / count),
52
+ g: Math.floor(g / count),
53
+ b: Math.floor(b / count),
54
+ };
55
+ };
56
+ export function getAgeColor(date) {
57
+ const diffInHours = differenceInHours(Date.now(), date);
58
+ const clampedHours = Math.min(Math.max(diffInHours, 0), 36);
59
+ const hue = Math.round(120 - clampedHours * 3.3333);
60
+ return `hsl(${hue}, 80%, 40%)`;
61
+ }
@@ -0,0 +1,3 @@
1
+ export declare function getCountryName(countryCode: string): string;
2
+ export declare function getLanguageName(languageCode: string): string;
3
+ //# sourceMappingURL=countries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"countries.d.ts","sourceRoot":"","sources":["../src/countries.ts"],"names":[],"mappings":"AAGA,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,UAMjD;AAED,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,UAMnD"}
@@ -0,0 +1,18 @@
1
+ const regionDisplayNames = new Intl.DisplayNames(["en"], { type: "region" });
2
+ const languageDisplayNames = new Intl.DisplayNames(["en"], { type: "language" });
3
+ export function getCountryName(countryCode) {
4
+ try {
5
+ return regionDisplayNames.of(countryCode) ?? countryCode;
6
+ }
7
+ catch {
8
+ return countryCode;
9
+ }
10
+ }
11
+ export function getLanguageName(languageCode) {
12
+ try {
13
+ return languageDisplayNames.of(languageCode.toLowerCase()) ?? languageCode;
14
+ }
15
+ catch {
16
+ return languageCode;
17
+ }
18
+ }
package/dist/date.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import type { DateArg, FormatOptions } from "date-fns";
2
+ export declare function setTime<DateType extends Date, ResultDate extends Date = DateType>(date: DateArg<DateType>, time: string): ResultDate;
3
+ export declare function setTime<DateType extends Date, ResultDate extends Date = DateType>(date: DateArg<DateType>, hours: number, minutes: number): ResultDate;
4
+ export declare function formatDate(date: DateArg<Date> & {}): string;
5
+ export declare function formatISODate(date: DateArg<Date>): string;
6
+ export type FormatTimeOptions = Intl.DateTimeFormatOptions;
7
+ export declare function formatTime(date: Date, options?: FormatTimeOptions): string;
8
+ export declare function formatDateTime(date: DateArg<Date>, formatStr?: string, options?: FormatOptions): string;
9
+ //# sourceMappingURL=date.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../src/date.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAKvD,wBAAgB,OAAO,CAAC,QAAQ,SAAS,IAAI,EAAE,UAAU,SAAS,IAAI,GAAG,QAAQ,EAC/E,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,EACvB,IAAI,EAAE,MAAM,GACX,UAAU,CAAC;AAEd,wBAAgB,OAAO,CAAC,QAAQ,SAAS,IAAI,EAAE,UAAU,SAAS,IAAI,GAAG,QAAQ,EAC/E,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,EACvB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GACd,UAAU,CAAC;AAoBd,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAE3D;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAEzD;AAED,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,qBAAqB,CAAC;AAE3D,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,GAAE,iBAAsB,GAAG,MAAM,CAE9E;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,SAAqB,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAEnH"}
package/dist/date.js ADDED
@@ -0,0 +1,27 @@
1
+ import { format, formatISO, set } from "date-fns";
2
+ import { take } from "ramda";
3
+ import { parseInteger } from "./number.js";
4
+ export function setTime(date, timeOrHour, optMinutes) {
5
+ let hours;
6
+ let minutes;
7
+ if (typeof timeOrHour === "string") {
8
+ [hours, minutes] = take(2, timeOrHour.split(":", 3)).map(parseInteger);
9
+ }
10
+ else {
11
+ hours = timeOrHour;
12
+ minutes = optMinutes;
13
+ }
14
+ return set(date, { hours, minutes, seconds: 0, milliseconds: 0 });
15
+ }
16
+ export function formatDate(date) {
17
+ return format(date, "yyyy-MM-dd");
18
+ }
19
+ export function formatISODate(date) {
20
+ return formatISO(date, { representation: "date" });
21
+ }
22
+ export function formatTime(date, options = {}) {
23
+ return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", ...options });
24
+ }
25
+ export function formatDateTime(date, formatStr = "yyyy-MM-dd HH:mm", options) {
26
+ return format(date, formatStr, options);
27
+ }
package/dist/enum.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export type Option<T extends string> = {
2
+ id: T;
3
+ label: string;
4
+ };
5
+ export declare function enumToOptions<T extends string>(enums: T[], labelFunction?: (value: T) => string): Option<T>[];
6
+ //# sourceMappingURL=enum.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enum.d.ts","sourceRoot":"","sources":["../src/enum.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,MAAM,CAAC,CAAC,SAAS,MAAM,IAAI;IACrC,EAAE,EAAE,CAAC,CAAC;IACN,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAC5C,KAAK,EAAE,CAAC,EAAE,EACV,aAAa,GAAE,CAAC,KAAK,EAAE,CAAC,KAAK,MAAiB,GAC7C,MAAM,CAAC,CAAC,CAAC,EAAE,CAOb"}
package/dist/enum.js ADDED
@@ -0,0 +1,9 @@
1
+ import { identity } from "ramda";
2
+ export function enumToOptions(enums, labelFunction = identity) {
3
+ return enums
4
+ .map((value) => ({
5
+ id: value,
6
+ label: labelFunction(value),
7
+ }))
8
+ .sort((a, b) => a.label.localeCompare(b.label));
9
+ }
package/dist/file.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export declare function getExtension(filename: string): string | undefined;
2
+ export declare function getBasename(filename: string): string;
3
+ export declare function fileIdentityKey(file: File): string;
4
+ //# sourceMappingURL=file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../src/file.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,sBAQ5C;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,UAI3C;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,UAEzC"}
package/dist/file.js ADDED
@@ -0,0 +1,14 @@
1
+ export function getExtension(filename) {
2
+ const lastDotIndex = filename.lastIndexOf(".");
3
+ if (lastDotIndex <= 0 || lastDotIndex === filename.length - 1) {
4
+ return undefined;
5
+ }
6
+ return filename.slice(lastDotIndex + 1).toLowerCase();
7
+ }
8
+ export function getBasename(filename) {
9
+ const lastDotIndex = filename.lastIndexOf(".");
10
+ return lastDotIndex === -1 ? filename : filename.slice(0, lastDotIndex);
11
+ }
12
+ export function fileIdentityKey(file) {
13
+ return `${file.name}-${file.size}-${file.lastModified}`;
14
+ }
@@ -0,0 +1,22 @@
1
+ import type { DateArg } from "date-fns";
2
+ export declare function humanBytes(input: number): string;
3
+ export declare function humanCount(count: number, word: string): string;
4
+ export declare function humanNumber(num: bigint | number): string;
5
+ export declare function humanizeEnum(value: string | undefined): string | undefined;
6
+ export declare function humanDimensions({ width, height }: {
7
+ width: number;
8
+ height: number;
9
+ }): string;
10
+ export declare function humanPlural(word: string, count: number): string;
11
+ export type HumanListOptions = {
12
+ truncateAt?: number;
13
+ conjunction?: string;
14
+ };
15
+ export declare function humanList(list: string[], options?: HumanListOptions): string;
16
+ export type HumanDurationArgs = {
17
+ start: DateArg<Date>;
18
+ end: DateArg<Date>;
19
+ largest?: number;
20
+ };
21
+ export declare function humanDuration(args: HumanDurationArgs): string;
22
+ //# sourceMappingURL=humanize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"humanize.d.ts","sourceRoot":"","sources":["../src/humanize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA0B,MAAM,UAAU,CAAC;AAOhE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMhD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CASxD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAS1E;AAED,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAE5F;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAc5E;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACrB,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAYF,wBAAgB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAW7D"}
@@ -0,0 +1,70 @@
1
+ import { formatDuration, intervalToDuration } from "date-fns";
2
+ import { filesize } from "filesize";
3
+ import humanizeList from "humanize-list";
4
+ import pluralize from "pluralize";
5
+ import { isPresent } from "./present.js";
6
+ export function humanBytes(input) {
7
+ return filesize(input, {
8
+ base: 2,
9
+ separator: ".",
10
+ standard: "jedec",
11
+ });
12
+ }
13
+ export function humanCount(count, word) {
14
+ return pluralize(word, count, true);
15
+ }
16
+ export function humanNumber(num) {
17
+ const n = Number(num);
18
+ if (n >= 1000000) {
19
+ return `${(n / 1000000).toFixed(1)}M`;
20
+ }
21
+ if (n >= 1000) {
22
+ return `${(n / 1000).toFixed(1)}K`;
23
+ }
24
+ return n.toString();
25
+ }
26
+ export function humanizeEnum(value) {
27
+ if (!isPresent(value))
28
+ return undefined;
29
+ return value
30
+ .toLowerCase()
31
+ .split(/[_-]/)
32
+ .filter(isPresent)
33
+ .map((word) => `${word.charAt(0).toUpperCase()}${word.slice(1)}`)
34
+ .join(" ");
35
+ }
36
+ export function humanDimensions({ width, height }) {
37
+ return `${width}x${height}`;
38
+ }
39
+ export function humanPlural(word, count) {
40
+ return pluralize(word, count, false);
41
+ }
42
+ export function humanList(list, options) {
43
+ const { truncateAt, conjunction = "and" } = options || {};
44
+ if (!truncateAt || list.length <= truncateAt) {
45
+ return humanizeList(list, {
46
+ oxfordComma: list.length > 2,
47
+ conjunction,
48
+ });
49
+ }
50
+ const remaining = list.length - truncateAt;
51
+ const truncated = [...list.slice(0, truncateAt), `+${remaining} more`];
52
+ return humanizeList(truncated, { oxfordComma: true, conjunction });
53
+ }
54
+ const periods = ["years", "months", "weeks", "days", "hours", "minutes", "seconds"];
55
+ function getDurationFormat(duration, largest) {
56
+ const formats = periods.reduce((acc, period) => {
57
+ return acc.length < largest && duration[period] ? [...acc, period] : acc;
58
+ }, []);
59
+ return formats.length === 0 ? ["seconds"] : formats;
60
+ }
61
+ export function humanDuration(args) {
62
+ const { start, end, largest } = args;
63
+ const duration = intervalToDuration({
64
+ start,
65
+ end,
66
+ });
67
+ return formatDuration(duration, {
68
+ format: largest ? getDurationFormat(duration, largest) : undefined,
69
+ });
70
+ }
@@ -0,0 +1,3 @@
1
+ export declare const identifierWordRules: ReadonlyArray<readonly [pattern: string, replacement: string]>;
2
+ export declare function splitIdentifierWords(raw: string): string;
3
+ //# sourceMappingURL=identifier-words.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifier-words.d.ts","sourceRoot":"","sources":["../src/identifier-words.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,mBAAmB,EAAE,aAAa,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,CAQ9F,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIxD"}
@@ -0,0 +1,15 @@
1
+ // Splits a programmer identifier into words: `actionPlan_v2` becomes `action Plan v2`.
2
+ export const identifierWordRules = [
3
+ ["_", " "],
4
+ ["([a-z])([A-Z])", "$1 $2"],
5
+ ["([A-Z])([A-Z][a-z])", "$1 $2"],
6
+ ["([a-z])([0-9])", "$1 $2"],
7
+ ["([0-9])([A-Z][a-z])", "$1 $2"],
8
+ ["([0-9]+)x", " $1x"],
9
+ [" *", " "],
10
+ ];
11
+ export function splitIdentifierWords(raw) {
12
+ return identifierWordRules
13
+ .reduce((text, [pattern, replacement]) => text.replace(new RegExp(pattern, "g"), replacement), raw)
14
+ .trim();
15
+ }
@@ -0,0 +1,2 @@
1
+ export declare function parseInteger(input?: string | null): number | undefined;
2
+ //# sourceMappingURL=number.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"number.d.ts","sourceRoot":"","sources":["../src/number.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAStE"}
package/dist/number.js ADDED
@@ -0,0 +1,9 @@
1
+ export function parseInteger(input) {
2
+ if (input && input.trim().length > 0) {
3
+ const number = parseInt(input, 10);
4
+ if (!isNaN(number)) {
5
+ return number;
6
+ }
7
+ }
8
+ return;
9
+ }
@@ -0,0 +1,3 @@
1
+ export declare const createObjectURL: (file: File | Blob | undefined) => string;
2
+ export declare const revokeObjectURL: (url: string) => void;
3
+ //# sourceMappingURL=object-url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-url.d.ts","sourceRoot":"","sources":["../src/object-url.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,GAAI,MAAM,IAAI,GAAG,IAAI,GAAG,SAAS,KAAG,MAG/D,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,KAAG,IAI7C,CAAC"}
@@ -0,0 +1,10 @@
1
+ export const createObjectURL = (file) => {
2
+ if (!file)
3
+ return "";
4
+ return URL.createObjectURL(file);
5
+ };
6
+ export const revokeObjectURL = (url) => {
7
+ if (url && url.startsWith("blob:")) {
8
+ URL.revokeObjectURL(url);
9
+ }
10
+ };
@@ -0,0 +1,5 @@
1
+ export declare function isPresent(value: string | null | undefined): value is string;
2
+ export declare function isPresent<T>(value: T[] | null | undefined): value is [T, ...T[]];
3
+ export declare function isPresent(value: Record<string, unknown> | null | undefined): value is Record<string, unknown>;
4
+ export declare function isPresent<T>(value: T | null | undefined): value is T;
5
+ //# sourceMappingURL=present.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"present.d.ts","sourceRoot":"","sources":["../src/present.ts"],"names":[],"mappings":"AAEA,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,IAAI,MAAM,CAAC;AAC7E,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,IAAI,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;AAClF,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC/G,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,IAAI,CAAC,CAAC"}
@@ -0,0 +1,23 @@
1
+ // After https://apidock.com/rails/Object/blank%3F
2
+ export function isPresent(value) {
3
+ if (value === null || value === undefined || value === false) {
4
+ return false;
5
+ }
6
+ if (typeof value === "string") {
7
+ return value.trim().length > 0;
8
+ }
9
+ if (Array.isArray(value)) {
10
+ return value.length > 0;
11
+ }
12
+ if (typeof value === "object") {
13
+ if (Object.keys(value).length > 0)
14
+ return true;
15
+ if (value instanceof Map || value instanceof Set)
16
+ return value.size > 0;
17
+ if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer)
18
+ return value.byteLength > 0;
19
+ const prototype = Object.getPrototypeOf(value);
20
+ return prototype !== Object.prototype && prototype !== null;
21
+ }
22
+ return true;
23
+ }
package/dist/set.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export declare function addAll<T>(set: Set<T>, items: Array<T>): void;
2
+ export declare function addAll<T>(set: Set<T>, other: Set<T>): void;
3
+ //# sourceMappingURL=set.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"set.d.ts","sourceRoot":"","sources":["../src/set.ts"],"names":[],"mappings":"AAAA,wBAAgB,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;AAC9D,wBAAgB,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC"}
package/dist/set.js ADDED
@@ -0,0 +1,11 @@
1
+ export function addAll(set, items) {
2
+ if (Array.isArray(items)) {
3
+ items.forEach((item) => set.add(item));
4
+ return;
5
+ }
6
+ if (items instanceof Set) {
7
+ items.forEach((item) => set.add(item));
8
+ return;
9
+ }
10
+ throw new Error("Unsupported type");
11
+ }
@@ -0,0 +1,2 @@
1
+ export declare function sleep(ms: number): Promise<void>;
2
+ //# sourceMappingURL=sleep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sleep.d.ts","sourceRoot":"","sources":["../src/sleep.ts"],"names":[],"mappings":"AAAA,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C"}
package/dist/sleep.js ADDED
@@ -0,0 +1,3 @@
1
+ export function sleep(ms) {
2
+ return new Promise((resolve) => setTimeout(resolve, ms));
3
+ }
@@ -0,0 +1,8 @@
1
+ export declare function trimLines(input: string): string;
2
+ export type GetInitialsArgs = {
3
+ name: string;
4
+ separator?: " ";
5
+ maxCount?: number;
6
+ };
7
+ export declare function getInitials(args: GetInitialsArgs): string;
8
+ //# sourceMappingURL=string.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"string.d.ts","sourceRoot":"","sources":["../src/string.ts"],"names":[],"mappings":"AAEA,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,UAEtC;AAED,MAAM,MAAM,eAAe,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,GAAG,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnF,wBAAgB,WAAW,CAAC,IAAI,EAAE,eAAe,UAShD"}
package/dist/string.js ADDED
@@ -0,0 +1,13 @@
1
+ import { trim } from "ramda";
2
+ export function trimLines(input) {
3
+ return input.split("\n").map(trim).join("\n");
4
+ }
5
+ export function getInitials(args) {
6
+ const { name, separator = " ", maxCount = 2 } = args;
7
+ return name
8
+ .split(separator)
9
+ .map((part) => part.match(/[a-zA-Z]/)?.[0]?.toUpperCase())
10
+ .filter(Boolean)
11
+ .slice(0, maxCount)
12
+ .join("");
13
+ }
@@ -0,0 +1,2 @@
1
+ export type Awaitable<T> = T | Promise<T>;
2
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/url.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ export type URLLike = URL | string;
2
+ export declare function toURL(url: URLLike): URL;
3
+ export declare function ensureProtocol(input: string): string;
4
+ export declare function getHostname(url: URLLike): string;
5
+ export declare function isHttpURL(url: URLLike): boolean;
6
+ export type ParamsInput = URLSearchParams | Map<string, string> | Record<string, string>;
7
+ export declare function mergeAll(to: URLSearchParams, params: ParamsInput): URLSearchParams;
8
+ export declare function mergeURLParams(url: URL, params: ParamsInput): URL;
9
+ //# sourceMappingURL=url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,GAAG,GAAG,GAAG,MAAM,CAAC;AAEnC,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,CAEvC;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAEhD;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAQ/C;AAED,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEzF,wBAAgB,QAAQ,CAAC,EAAE,EAAE,eAAe,EAAE,MAAM,EAAE,WAAW,GAAG,eAAe,CAelF;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,GAAG,GAAG,CAGjE"}
package/dist/url.js ADDED
@@ -0,0 +1,40 @@
1
+ export function toURL(url) {
2
+ return typeof url === "string" ? new URL(url) : url;
3
+ }
4
+ export function ensureProtocol(input) {
5
+ return /^https?:\/\//i.test(input) ? input : `https://${input}`;
6
+ }
7
+ export function getHostname(url) {
8
+ return toURL(url).hostname;
9
+ }
10
+ export function isHttpURL(url) {
11
+ try {
12
+ const parsed = toURL(url);
13
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
14
+ }
15
+ catch {
16
+ return false;
17
+ }
18
+ }
19
+ export function mergeAll(to, params) {
20
+ if (params instanceof URLSearchParams) {
21
+ for (const [key, value] of params) {
22
+ to.set(key, value);
23
+ }
24
+ }
25
+ else if (params instanceof Map) {
26
+ for (const [key, value] of params) {
27
+ to.set(key, value);
28
+ }
29
+ }
30
+ else {
31
+ for (const [key, value] of Object.entries(params)) {
32
+ to.set(key, value);
33
+ }
34
+ }
35
+ return to;
36
+ }
37
+ export function mergeURLParams(url, params) {
38
+ mergeAll(url.searchParams, params);
39
+ return url;
40
+ }
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@manta-eu/lib",
3
+ "version": "0.1.0",
4
+ "description": "Pure TypeScript helpers shared across the Manta packages",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "exports": {
8
+ "./*": {
9
+ "types": "./dist/*.d.ts",
10
+ "import": "./dist/*.js"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist"
15
+ ],
16
+ "dependencies": {
17
+ "date-fns": "^4.1.0",
18
+ "filesize": "^11.0.2",
19
+ "humanize-list": "^1.0.1",
20
+ "pluralize": "^8.0.0",
21
+ "ramda": "^0.32.0"
22
+ },
23
+ "devDependencies": {
24
+ "@tanstack/react-query": "^5.100.10",
25
+ "@types/node": "^20.0.0",
26
+ "@types/pluralize": "^0.0.33",
27
+ "@types/ramda": "^0.31.1",
28
+ "@types/react": "^19.2.7",
29
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
30
+ "@typescript-eslint/parser": "^8.0.0",
31
+ "@vitest/coverage-v8": "^3.2.4",
32
+ "eslint": "^9.0.0",
33
+ "prettier": "^3.6.2",
34
+ "react": "^19.2.3",
35
+ "sonner": "^1.7.4",
36
+ "tsc-alias": "^1.9.5",
37
+ "typescript": "^5.0.0",
38
+ "usehooks-ts": "^3.1.1",
39
+ "vitest": "^3.0.0",
40
+ "@manta-eu/eslint-config": "0.1.0"
41
+ },
42
+ "prettier": {
43
+ "printWidth": 120
44
+ },
45
+ "imports": {
46
+ "#*": {
47
+ "manta-source": [
48
+ "./src/*",
49
+ "./src/*.ts",
50
+ "./src/*.tsx",
51
+ "./src/*/index.ts",
52
+ "./src/*/index.tsx"
53
+ ],
54
+ "types": "./dist/*.d.ts",
55
+ "default": "./dist/*.js"
56
+ }
57
+ },
58
+ "publishConfig": {
59
+ "access": "public"
60
+ },
61
+ "scripts": {
62
+ "build": "tsc --project tsconfig.build.json && tsc-alias --project tsconfig.build.json --resolve-full-paths",
63
+ "build:lib": "tsc --project tsconfig.build.json",
64
+ "test": "vitest --run",
65
+ "test:watch": "vitest --watch",
66
+ "check": "vitest --run",
67
+ "types": "tsc --noEmit",
68
+ "format": "prettier --write .",
69
+ "format:check": "prettier --check ."
70
+ }
71
+ }