@lichens-innovation/ts-common 1.17.0 → 1.18.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/dist/csv.cjs.map +1 -1
- package/dist/csv.js.map +1 -1
- package/dist/dimensions.utils-BSm9gmWz.d.cts +47 -0
- package/dist/dimensions.utils-BSm9gmWz.d.ts +47 -0
- package/dist/excel.cjs.map +1 -1
- package/dist/excel.js.map +1 -1
- package/dist/index.cjs +234 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -43
- package/dist/index.d.ts +38 -43
- package/dist/index.js +217 -1
- package/dist/index.js.map +1 -1
- package/dist/mime.cjs +11 -0
- package/dist/mime.cjs.map +1 -1
- package/dist/mime.d.cts +7 -1
- package/dist/mime.d.ts +7 -1
- package/dist/mime.js +9 -1
- package/dist/mime.js.map +1 -1
- package/dist/web.cjs +271 -0
- package/dist/web.cjs.map +1 -1
- package/dist/web.d.cts +65 -2
- package/dist/web.d.ts +65 -2
- package/dist/web.js +249 -1
- package/dist/web.js.map +1 -1
- package/package.json +1 -1
- package/dist/dimensions.utils-BwIBA5op.d.cts +0 -6
- package/dist/dimensions.utils-BwIBA5op.d.ts +0 -6
package/dist/csv.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mime/index.ts","../src/csv/index.ts"],"names":["mime","Papa"],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"sources":["../src/mime/index.ts","../src/csv/index.ts"],"names":["mime","Papa"],"mappings":";;;;;;;;;;;AAwDO,IAAM,WAAA,GAAc,CAAC,SAAA,KAA8B;AACxD,EAAA,MAAM,QAAA,GAAWA,qBAAA,CAAK,OAAA,CAAQ,SAAS,CAAA;AACvC,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,kDAAA,EAAqD,SAAS,CAAA,CAAA,CAAG,CAAA;AAAA,EACnF;AAEA,EAAA,OAAO,QAAA;AACT,CAAA;;;AC1DO,IAAM,eAAA,GAAkB,CAAC,GAAA,KAA2D;AACzF,EAAA,MAAM,YAAoC,EAAC;AAE3C,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA,EAAG;AAC9C,IAAA,SAAA,CAAU,GAAG,CAAA,GAAI,MAAA,CAAO,KAAA,IAAS,EAAE,CAAA;AAAA,EACrC;AAEA,EAAA,OAAO,SAAA;AACT;AAEO,IAAM,eAAA,GAAkB,CAAC,IAAA,KAA4C;AAC1E,EAAA,MAAM,IAAA,GAAiC,IAAA,CAAK,GAAA,CAAI,eAAe,CAAA;AAC/D,EAAA,MAAM,GAAA,GAAMC,sBAAK,OAAA,CAAQ,IAAA,EAAM,EAAE,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW,GAAA,EAAK,CAAA;AAE/D,EAAA,OAAO,IAAI,IAAA,CAAK,CAAC,GAAG,CAAA,EAAG,EAAE,IAAA,EAAM,WAAA,CAAY,KAAK,CAAA,EAAG,CAAA;AACrD","file":"csv.cjs","sourcesContent":["import mime from \"mime\";\n\nimport { DATA_URI_PATTERN, isValidDataUri } from \"~/utils/uri.utils\";\nimport { VALID_IMAGE_TYPES } from './mime.utils';\n\nexport { VALID_IMAGE_TYPES } from './mime.utils';\n\nexport interface ParseDataUriResult {\n mimeType: string;\n base64: string;\n ext: string;\n}\n\nexport const parseDataUri = (input: string): ParseDataUriResult | null => {\n if (!isValidDataUri(input)) return null;\n\n const match = DATA_URI_PATTERN.exec(input);\n if (!match) return null;\n\n const [, mimeType, base64] = match;\n if (!mimeType || !base64) return null;\n\n return { mimeType, base64, ext: mimeToExt(mimeType) };\n};\n\nexport const isImageMimeType = (mimeType: string): boolean => {\n return VALID_IMAGE_TYPES.includes(mimeType);\n};\n\nexport const isValidImageFile = (file: { type: string }): boolean => {\n return isImageMimeType(file.type);\n};\n\nexport const getExtensionFromDataUri = (dataUri: string): string => {\n const parsed = parseDataUri(dataUri);\n if (!parsed) return 'bin';\n\n return parsed.ext;\n};\n\nexport const getImagePreviewSrc = (input: string): string | null => {\n const parsed = parseDataUri(input);\n if (!parsed) return null;\n if (!isImageMimeType(parsed.mimeType)) return null;\n\n return input;\n};\n\nexport const mimeToExt = (mimeType: string): string => {\n const ext = mime.getExtension(mimeType);\n if (ext) return ext;\n\n const subtype = mimeType.split(\"/\")[1] ?? mimeType;\n return subtype.split(\"+\")[0] ?? subtype;\n};\n\nexport const getMimeType = (extension: string): string => {\n const mimeType = mime.getType(extension);\n if (!mimeType) {\n throw new Error(`[getMimeType] Mime type not found for extension: \"${extension}\"`);\n }\n\n return mimeType;\n};\n\n","import Papa from \"papaparse\";\nimport { getMimeType } from \"../mime\";\n\nexport type CellValue = string | number | null | undefined;\n\nexport const convertToCsvRow = (row: Record<string, CellValue>): Record<string, string> => {\n const stringRow: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(row)) {\n stringRow[key] = String(value ?? \"\");\n }\n\n return stringRow;\n};\n\nexport const generateCsvBlob = (data: Record<string, CellValue>[]): Blob => {\n const rows: Record<string, string>[] = data.map(convertToCsvRow);\n const csv = Papa.unparse(rows, { header: true, delimiter: \",\" });\n\n return new Blob([csv], { type: getMimeType(\"csv\") });\n};\n"]}
|
package/dist/csv.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mime/index.ts","../src/csv/index.ts"],"names":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"sources":["../src/mime/index.ts","../src/csv/index.ts"],"names":[],"mappings":";;;;AAwDO,IAAM,WAAA,GAAc,CAAC,SAAA,KAA8B;AACxD,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,OAAA,CAAQ,SAAS,CAAA;AACvC,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,kDAAA,EAAqD,SAAS,CAAA,CAAA,CAAG,CAAA;AAAA,EACnF;AAEA,EAAA,OAAO,QAAA;AACT,CAAA;;;AC1DO,IAAM,eAAA,GAAkB,CAAC,GAAA,KAA2D;AACzF,EAAA,MAAM,YAAoC,EAAC;AAE3C,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA,EAAG;AAC9C,IAAA,SAAA,CAAU,GAAG,CAAA,GAAI,MAAA,CAAO,KAAA,IAAS,EAAE,CAAA;AAAA,EACrC;AAEA,EAAA,OAAO,SAAA;AACT;AAEO,IAAM,eAAA,GAAkB,CAAC,IAAA,KAA4C;AAC1E,EAAA,MAAM,IAAA,GAAiC,IAAA,CAAK,GAAA,CAAI,eAAe,CAAA;AAC/D,EAAA,MAAM,GAAA,GAAM,KAAK,OAAA,CAAQ,IAAA,EAAM,EAAE,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW,GAAA,EAAK,CAAA;AAE/D,EAAA,OAAO,IAAI,IAAA,CAAK,CAAC,GAAG,CAAA,EAAG,EAAE,IAAA,EAAM,WAAA,CAAY,KAAK,CAAA,EAAG,CAAA;AACrD","file":"csv.js","sourcesContent":["import mime from \"mime\";\n\nimport { DATA_URI_PATTERN, isValidDataUri } from \"~/utils/uri.utils\";\nimport { VALID_IMAGE_TYPES } from './mime.utils';\n\nexport { VALID_IMAGE_TYPES } from './mime.utils';\n\nexport interface ParseDataUriResult {\n mimeType: string;\n base64: string;\n ext: string;\n}\n\nexport const parseDataUri = (input: string): ParseDataUriResult | null => {\n if (!isValidDataUri(input)) return null;\n\n const match = DATA_URI_PATTERN.exec(input);\n if (!match) return null;\n\n const [, mimeType, base64] = match;\n if (!mimeType || !base64) return null;\n\n return { mimeType, base64, ext: mimeToExt(mimeType) };\n};\n\nexport const isImageMimeType = (mimeType: string): boolean => {\n return VALID_IMAGE_TYPES.includes(mimeType);\n};\n\nexport const isValidImageFile = (file: { type: string }): boolean => {\n return isImageMimeType(file.type);\n};\n\nexport const getExtensionFromDataUri = (dataUri: string): string => {\n const parsed = parseDataUri(dataUri);\n if (!parsed) return 'bin';\n\n return parsed.ext;\n};\n\nexport const getImagePreviewSrc = (input: string): string | null => {\n const parsed = parseDataUri(input);\n if (!parsed) return null;\n if (!isImageMimeType(parsed.mimeType)) return null;\n\n return input;\n};\n\nexport const mimeToExt = (mimeType: string): string => {\n const ext = mime.getExtension(mimeType);\n if (ext) return ext;\n\n const subtype = mimeType.split(\"/\")[1] ?? mimeType;\n return subtype.split(\"+\")[0] ?? subtype;\n};\n\nexport const getMimeType = (extension: string): string => {\n const mimeType = mime.getType(extension);\n if (!mimeType) {\n throw new Error(`[getMimeType] Mime type not found for extension: \"${extension}\"`);\n }\n\n return mimeType;\n};\n\n","import Papa from \"papaparse\";\nimport { getMimeType } from \"../mime\";\n\nexport type CellValue = string | number | null | undefined;\n\nexport const convertToCsvRow = (row: Record<string, CellValue>): Record<string, string> => {\n const stringRow: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(row)) {\n stringRow[key] = String(value ?? \"\");\n }\n\n return stringRow;\n};\n\nexport const generateCsvBlob = (data: Record<string, CellValue>[]): Blob => {\n const rows: Record<string, string>[] = data.map(convertToCsvRow);\n const csv = Papa.unparse(rows, { header: true, delimiter: \",\" });\n\n return new Blob([csv], { type: getMimeType(\"csv\") });\n};\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
interface RgbColor {
|
|
2
|
+
r: number;
|
|
3
|
+
g: number;
|
|
4
|
+
b: number;
|
|
5
|
+
}
|
|
6
|
+
interface RgbaColor extends RgbColor {
|
|
7
|
+
a: number;
|
|
8
|
+
}
|
|
9
|
+
declare const rgbToHex: (r: number, g: number, b: number) => string;
|
|
10
|
+
declare const hexToRgb: (hex: string) => RgbColor;
|
|
11
|
+
declare const rgbaToHex: (color: RgbaColor) => string;
|
|
12
|
+
declare const getOpacityHexValue: (opacity: number) => string;
|
|
13
|
+
declare const rgbaToHexWithAlpha: (color: RgbaColor) => string;
|
|
14
|
+
declare const rgbToString: (color: RgbColor) => string;
|
|
15
|
+
declare const rgbaToString: (color: RgbaColor) => string;
|
|
16
|
+
declare const getLuminance: (r: number, g: number, b: number) => number;
|
|
17
|
+
declare const getContrastTextColor: (hexColor: string) => string;
|
|
18
|
+
/**
|
|
19
|
+
* Normalized RGB color with values between 0 and 1.
|
|
20
|
+
* Useful for graphics libraries like Three.js.
|
|
21
|
+
*/
|
|
22
|
+
interface NormalizedRgb {
|
|
23
|
+
r: number;
|
|
24
|
+
g: number;
|
|
25
|
+
b: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Convert a hex color string to normalized RGB values (0-1 range).
|
|
29
|
+
* @param hex - Hex color string (with or without # prefix)
|
|
30
|
+
* @returns Normalized RGB object with values between 0 and 1
|
|
31
|
+
*/
|
|
32
|
+
declare const hexToNormalizedRgb: (hex: string) => NormalizedRgb;
|
|
33
|
+
/**
|
|
34
|
+
* Determine if a color is light or dark based on relative luminance.
|
|
35
|
+
* Useful for choosing contrasting text colors.
|
|
36
|
+
* @param hex - Hex color string
|
|
37
|
+
* @returns True if the color is considered light (luminance > 0.5)
|
|
38
|
+
*/
|
|
39
|
+
declare const isLightColor: (hex: string) => boolean;
|
|
40
|
+
declare const getColorForPercentage: (percent: number) => string;
|
|
41
|
+
|
|
42
|
+
interface Dimensions {
|
|
43
|
+
width: number;
|
|
44
|
+
height: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { type Dimensions as D, type NormalizedRgb as N, type RgbColor as R, type RgbaColor as a, rgbaToHex as b, rgbaToHexWithAlpha as c, rgbToString as d, rgbaToString as e, getLuminance as f, getOpacityHexValue as g, hexToRgb as h, getContrastTextColor as i, hexToNormalizedRgb as j, isLightColor as k, getColorForPercentage as l, rgbToHex as r };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
interface RgbColor {
|
|
2
|
+
r: number;
|
|
3
|
+
g: number;
|
|
4
|
+
b: number;
|
|
5
|
+
}
|
|
6
|
+
interface RgbaColor extends RgbColor {
|
|
7
|
+
a: number;
|
|
8
|
+
}
|
|
9
|
+
declare const rgbToHex: (r: number, g: number, b: number) => string;
|
|
10
|
+
declare const hexToRgb: (hex: string) => RgbColor;
|
|
11
|
+
declare const rgbaToHex: (color: RgbaColor) => string;
|
|
12
|
+
declare const getOpacityHexValue: (opacity: number) => string;
|
|
13
|
+
declare const rgbaToHexWithAlpha: (color: RgbaColor) => string;
|
|
14
|
+
declare const rgbToString: (color: RgbColor) => string;
|
|
15
|
+
declare const rgbaToString: (color: RgbaColor) => string;
|
|
16
|
+
declare const getLuminance: (r: number, g: number, b: number) => number;
|
|
17
|
+
declare const getContrastTextColor: (hexColor: string) => string;
|
|
18
|
+
/**
|
|
19
|
+
* Normalized RGB color with values between 0 and 1.
|
|
20
|
+
* Useful for graphics libraries like Three.js.
|
|
21
|
+
*/
|
|
22
|
+
interface NormalizedRgb {
|
|
23
|
+
r: number;
|
|
24
|
+
g: number;
|
|
25
|
+
b: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Convert a hex color string to normalized RGB values (0-1 range).
|
|
29
|
+
* @param hex - Hex color string (with or without # prefix)
|
|
30
|
+
* @returns Normalized RGB object with values between 0 and 1
|
|
31
|
+
*/
|
|
32
|
+
declare const hexToNormalizedRgb: (hex: string) => NormalizedRgb;
|
|
33
|
+
/**
|
|
34
|
+
* Determine if a color is light or dark based on relative luminance.
|
|
35
|
+
* Useful for choosing contrasting text colors.
|
|
36
|
+
* @param hex - Hex color string
|
|
37
|
+
* @returns True if the color is considered light (luminance > 0.5)
|
|
38
|
+
*/
|
|
39
|
+
declare const isLightColor: (hex: string) => boolean;
|
|
40
|
+
declare const getColorForPercentage: (percent: number) => string;
|
|
41
|
+
|
|
42
|
+
interface Dimensions {
|
|
43
|
+
width: number;
|
|
44
|
+
height: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { type Dimensions as D, type NormalizedRgb as N, type RgbColor as R, type RgbaColor as a, rgbaToHex as b, rgbaToHexWithAlpha as c, rgbToString as d, rgbaToString as e, getLuminance as f, getOpacityHexValue as g, hexToRgb as h, getContrastTextColor as i, hexToNormalizedRgb as j, isLightColor as k, getColorForPercentage as l, rgbToHex as r };
|
package/dist/excel.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/excel/excel-generator.ts","../src/excel/excel-generator.types.ts","../src/utils/types.utils.ts","../src/mime/index.ts","../src/excel/exceljs.utils.ts"],"names":["mime","ExcelJS"],"mappings":"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","file":"excel.cjs","sourcesContent":["import ExcelJS from \"exceljs\";\nimport type { CellStyle, CellValue } from \"./excel-generator.types\";\n\ninterface AddCellArgs {\n row: number;\n col: number;\n value: CellValue;\n style?: CellStyle;\n}\n\ninterface AddRowArgs {\n values: CellValue[];\n styles?: CellStyle[];\n}\n\nexport class WorksheetBuilder {\n private worksheet: ExcelJS.Worksheet;\n private currentRow: number;\n private defaultRowStyles: CellStyle[];\n\n constructor(worksheet: ExcelJS.Worksheet) {\n this.worksheet = worksheet;\n this.currentRow = 1; // ExcelJS uses 1-based indexing\n this.defaultRowStyles = [];\n }\n\n /** Returns the current row index (1-based). */\n get rowCount(): number {\n return this.currentRow;\n }\n\n addCell({ row, col, value, style }: AddCellArgs): void {\n const cell = this.worksheet.getCell(row + 1, col + 1); // Convert to 1-based\n cell.value = value ?? \"\";\n\n if (style) {\n this.applyStyle(cell, style);\n }\n }\n\n addRow({ values, styles }: AddRowArgs): void {\n const stylesToUse = styles ?? this.defaultRowStyles;\n const row = this.worksheet.addRow(values);\n\n // Apply styles to each cell in the row\n for (let col = 0; col < values.length; col++) {\n const style = stylesToUse[col];\n if (style) {\n const cell = row.getCell(col + 1); // ExcelJS uses 1-based indexing\n this.applyStyle(cell, style);\n }\n }\n\n this.currentRow++;\n }\n\n setDefaultRowStyles(styles: CellStyle[]): void {\n this.defaultRowStyles = styles;\n }\n\n addEmptyRow(): void {\n this.worksheet.addRow([]);\n this.currentRow++;\n }\n\n setColumnWidths(columnWidths: number[]): void {\n this.worksheet.columns = columnWidths.map((width) => ({ width }));\n }\n\n getWorksheet(): ExcelJS.Worksheet {\n return this.worksheet;\n }\n\n private applyStyle(cell: ExcelJS.Cell, style: CellStyle): void {\n if (style.font) {\n cell.font = { ...cell.font, ...style.font };\n }\n\n if (style.fill) {\n cell.fill = style.fill as ExcelJS.Fill;\n }\n\n if (style.alignment) {\n cell.alignment = { ...cell.alignment, ...style.alignment };\n }\n\n if (style.border) {\n cell.border = { ...cell.border, ...style.border };\n }\n\n if (style.numFmt) {\n cell.numFmt = style.numFmt;\n }\n }\n}\n","import type { Font, Fill, Alignment } from \"exceljs\";\n\nexport type CellValue = string | number | null | undefined;\n\nexport type VerticalAlignment = \"top\" | \"middle\" | \"bottom\";\nexport type HorizontalAlignment = \"left\" | \"center\" | \"right\";\n\nexport type ColumnValueType = \"number\" | \"text\" | \"auto\";\n\nexport interface ColumnMetadata {\n name: string;\n width: number;\n valueType?: ColumnValueType;\n}\n\ninterface BorderSide {\n style?: \"thin\" | \"medium\" | \"thick\" | \"dotted\" | \"dashed\" | \"double\";\n color?: { argb: string };\n}\n\n/**\n * Type for ExcelJS cell borders.\n */\nexport interface CellBorder {\n top?: BorderSide;\n bottom?: BorderSide;\n left?: BorderSide;\n right?: BorderSide;\n}\n\n/**\n * Type for ExcelJS cell styles.\n * This is a simplified version of ExcelJS.Style that focuses on commonly used properties.\n */\nexport interface CellStyle {\n font?: Partial<Font>;\n fill?: Partial<Fill>;\n alignment?: Partial<Alignment>;\n border?: CellBorder;\n numFmt?: string;\n}\n\n/**\n * Helper type to convert RGB color string to ARGB format for ExcelJS.\n * ExcelJS uses ARGB format: \"FFFFFFFF\" where FF is alpha, then RGB.\n */\nexport type ArgbColor = { argb: string };\n\n/**\n * Helper function to convert RGB color string to ARGB format.\n * @param rgb - RGB color string without # (e.g., \"FFFFFF\")\n * @returns ARGB color object (e.g., { argb: \"FFFFFFFF\" })\n */\nexport const rgbToArgb = (rgb: string): ArgbColor => {\n return { argb: `FF${rgb}` };\n};\n","export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import mime from \"mime\";\n\nimport { DATA_URI_PATTERN, isValidDataUri } from \"~/utils/uri.utils\";\nimport { VALID_IMAGE_TYPES } from \"./mime.utils\";\n\nexport interface ParseDataUriResult {\n mimeType: string;\n base64: string;\n ext: string;\n}\n\nexport const parseDataUri = (input: string): ParseDataUriResult | null => {\n if (!isValidDataUri(input)) return null;\n\n const match = DATA_URI_PATTERN.exec(input);\n if (!match) return null;\n\n const [, mimeType, base64] = match;\n if (!mimeType || !base64) return null;\n\n return { mimeType, base64, ext: mimeToExt(mimeType) };\n};\n\nexport const isImageMimeType = (mimeType: string): boolean => {\n return VALID_IMAGE_TYPES.includes(mimeType);\n};\n\nexport const getImagePreviewSrc = (input: string): string | null => {\n const parsed = parseDataUri(input);\n if (!parsed) return null;\n if (!isImageMimeType(parsed.mimeType)) return null;\n\n return input;\n};\n\nexport const mimeToExt = (mimeType: string): string => {\n const ext = mime.getExtension(mimeType);\n if (ext) return ext;\n\n const subtype = mimeType.split(\"/\")[1] ?? mimeType;\n return subtype.split(\"+\")[0] ?? subtype;\n};\n\nexport const getMimeType = (extension: string): string => {\n const mimeType = mime.getType(extension);\n if (!mimeType) {\n throw new Error(`[getMimeType] Mime type not found for extension: \"${extension}\"`);\n }\n\n return mimeType;\n};\n\n","import ExcelJS from \"exceljs\";\nimport { getMimeType } from \"../mime\";\nimport { isNullish } from \"../utils\";\nimport type { CellValue, ColumnMetadata } from \"./excel-generator.types\";\n\n/**\n * Sanitizes a worksheet title to comply with Excel's limitations.\n *\n * Excel enforces strict rules for worksheet names:\n * - Maximum length of 31 characters (legacy limitation from older Excel versions)\n * - Certain characters are reserved for special purposes and cannot be used:\n * - `:` (colon) - used in cell range references (e.g., A1:B2)\n * - `/` (forward slash) - used in file paths\n * - `\\` (backslash) - used in file paths and escape sequences\n * - `?` (question mark) - used as wildcard in formulas\n * - `*` (asterisk) - used as wildcard in formulas\n * - `[` and `]` (square brackets) - used in external references (e.g., [Book1.xlsx]Sheet1!A1)\n *\n * This function removes invalid characters and truncates the title to 31 characters\n * to ensure compatibility with Excel's file format specifications.\n *\n * @param title - The original worksheet title to sanitize\n * @returns A sanitized title that complies with Excel's naming rules\n */\nexport const sanitizeWorksheetTitle = (title: string): string => {\n const MAX_LENGTH = 31;\n const INVALID_CHARS = /[:/\\\\?*[\\]]/g;\n\n return title.trim().replace(INVALID_CHARS, \"\").slice(0, MAX_LENGTH);\n};\n\ninterface ToSafeCellValueArgs {\n value: CellValue;\n valueType: ColumnMetadata[\"valueType\"];\n}\n\nconst toSafeCellValue = ({ value, valueType }: ToSafeCellValueArgs): CellValue => {\n if (!isNullish(value) && valueType === \"number\") {\n const numValue = typeof value === \"number\" ? value : Number(value);\n return isNaN(numValue) ? value : numValue;\n }\n\n return value ?? \"\";\n};\n\ninterface ToSafeCellValuesArgs {\n headers: string[];\n rowData: Record<string, CellValue>;\n columnsMetadata?: Record<string, ColumnMetadata>;\n}\n\nconst toSafeCellValues = ({ headers, rowData, columnsMetadata }: ToSafeCellValuesArgs): CellValue[] => {\n return headers.map((header) => {\n const value = rowData[header];\n const metadata = columnsMetadata?.[header];\n const valueType = metadata?.valueType ?? \"auto\";\n return toSafeCellValue({ value, valueType });\n });\n};\n\ninterface ApplyTextFormattingArgs {\n row: ExcelJS.Row;\n headers: string[];\n columnsMetadata?: Record<string, ColumnMetadata>;\n}\n\nconst applyTextFormatting = ({ row, headers, columnsMetadata }: ApplyTextFormattingArgs): void => {\n for (let colIndex = 0; colIndex < headers.length; colIndex++) {\n const header = headers[colIndex];\n const metadata = columnsMetadata?.[header];\n const valueType = metadata?.valueType ?? \"auto\";\n\n if (valueType === \"text\") {\n const cell = row.getCell(colIndex + 1);\n cell.numFmt = \"@\";\n }\n }\n};\n\nconst applyHeaderStyles = (headerRow: ExcelJS.Row): void => {\n headerRow.font = { bold: true };\n headerRow.fill = {\n type: \"pattern\",\n pattern: \"solid\",\n fgColor: { argb: \"FFE0E0E0\" },\n };\n headerRow.alignment = { wrapText: true, vertical: \"top\" };\n};\n\ninterface SetupHeaderFrozenRowArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n}\n\nconst setupHeaderFrozenRow = ({ worksheet, headers }: SetupHeaderFrozenRowArgs): void => {\n const headerRow = worksheet.addRow(headers);\n applyHeaderStyles(headerRow);\n worksheet.views = [{ state: \"frozen\", ySplit: 1 }];\n};\n\ninterface ApplyDataRowStylesArgs {\n worksheet: ExcelJS.Worksheet;\n startRowIndex: number;\n endRowIndex: number;\n}\n\nconst applyDataRowStyles = ({ worksheet, startRowIndex, endRowIndex }: ApplyDataRowStylesArgs): void => {\n for (let rowIndex = startRowIndex; rowIndex <= endRowIndex; rowIndex++) {\n const row = worksheet.getRow(rowIndex);\n row.alignment = { wrapText: false, vertical: \"top\" };\n }\n};\n\ninterface SetColumnWidthsArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n columnsMetadata: Record<string, ColumnMetadata>;\n}\n\nconst setColumnWidths = ({ worksheet, headers, columnsMetadata }: SetColumnWidthsArgs): void => {\n worksheet.columns = headers.map((header) => {\n const metadata = columnsMetadata[header];\n return metadata ? { width: metadata.width } : {};\n });\n};\n\ninterface AddDataRowsArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n columnsMetadata: Record<string, ColumnMetadata>;\n data: Record<string, CellValue>[];\n}\n\nconst addDataRows = ({ worksheet, headers, columnsMetadata, data }: AddDataRowsArgs): void => {\n for (const rowData of data) {\n const safeCellValues = toSafeCellValues({ headers, rowData, columnsMetadata });\n const row = worksheet.addRow(safeCellValues);\n applyTextFormatting({ row, headers, columnsMetadata });\n }\n\n applyDataRowStyles({ worksheet, startRowIndex: 2, endRowIndex: data.length + 1 });\n};\n\ninterface GenerateExcelBlobArgs {\n worksheetTitle: string;\n data: Record<string, CellValue>[];\n /**\n * Metadata for columns, keyed by column name (translated header name).\n * If not provided, columns will use default width and auto-detect value types.\n */\n columnsMetadata: Record<string, ColumnMetadata>;\n}\n\nexport const generateExcelBlob = async (args: GenerateExcelBlobArgs): Promise<Blob> => {\n const { data, columnsMetadata, worksheetTitle } = args;\n\n if (data.length === 0) {\n throw new Error(\"No data to export\");\n }\n\n const headers = Object.keys(data[0]);\n const workbook = new ExcelJS.Workbook();\n const worksheet = workbook.addWorksheet(sanitizeWorksheetTitle(worksheetTitle));\n\n setupHeaderFrozenRow({ worksheet, headers });\n addDataRows({ worksheet, headers, columnsMetadata, data });\n setColumnWidths({ worksheet, headers, columnsMetadata });\n\n const buffer = await workbook.xlsx.writeBuffer();\n return new Blob([buffer], { type: getMimeType(\"xlsx\") });\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/excel/excel-generator.ts","../src/excel/excel-generator.types.ts","../src/utils/types.utils.ts","../src/mime/index.ts","../src/excel/exceljs.utils.ts"],"names":["mime","ExcelJS"],"mappings":"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","file":"excel.cjs","sourcesContent":["import ExcelJS from \"exceljs\";\nimport type { CellStyle, CellValue } from \"./excel-generator.types\";\n\ninterface AddCellArgs {\n row: number;\n col: number;\n value: CellValue;\n style?: CellStyle;\n}\n\ninterface AddRowArgs {\n values: CellValue[];\n styles?: CellStyle[];\n}\n\nexport class WorksheetBuilder {\n private worksheet: ExcelJS.Worksheet;\n private currentRow: number;\n private defaultRowStyles: CellStyle[];\n\n constructor(worksheet: ExcelJS.Worksheet) {\n this.worksheet = worksheet;\n this.currentRow = 1; // ExcelJS uses 1-based indexing\n this.defaultRowStyles = [];\n }\n\n /** Returns the current row index (1-based). */\n get rowCount(): number {\n return this.currentRow;\n }\n\n addCell({ row, col, value, style }: AddCellArgs): void {\n const cell = this.worksheet.getCell(row + 1, col + 1); // Convert to 1-based\n cell.value = value ?? \"\";\n\n if (style) {\n this.applyStyle(cell, style);\n }\n }\n\n addRow({ values, styles }: AddRowArgs): void {\n const stylesToUse = styles ?? this.defaultRowStyles;\n const row = this.worksheet.addRow(values);\n\n // Apply styles to each cell in the row\n for (let col = 0; col < values.length; col++) {\n const style = stylesToUse[col];\n if (style) {\n const cell = row.getCell(col + 1); // ExcelJS uses 1-based indexing\n this.applyStyle(cell, style);\n }\n }\n\n this.currentRow++;\n }\n\n setDefaultRowStyles(styles: CellStyle[]): void {\n this.defaultRowStyles = styles;\n }\n\n addEmptyRow(): void {\n this.worksheet.addRow([]);\n this.currentRow++;\n }\n\n setColumnWidths(columnWidths: number[]): void {\n this.worksheet.columns = columnWidths.map((width) => ({ width }));\n }\n\n getWorksheet(): ExcelJS.Worksheet {\n return this.worksheet;\n }\n\n private applyStyle(cell: ExcelJS.Cell, style: CellStyle): void {\n if (style.font) {\n cell.font = { ...cell.font, ...style.font };\n }\n\n if (style.fill) {\n cell.fill = style.fill as ExcelJS.Fill;\n }\n\n if (style.alignment) {\n cell.alignment = { ...cell.alignment, ...style.alignment };\n }\n\n if (style.border) {\n cell.border = { ...cell.border, ...style.border };\n }\n\n if (style.numFmt) {\n cell.numFmt = style.numFmt;\n }\n }\n}\n","import type { Font, Fill, Alignment } from \"exceljs\";\n\nexport type CellValue = string | number | null | undefined;\n\nexport type VerticalAlignment = \"top\" | \"middle\" | \"bottom\";\nexport type HorizontalAlignment = \"left\" | \"center\" | \"right\";\n\nexport type ColumnValueType = \"number\" | \"text\" | \"auto\";\n\nexport interface ColumnMetadata {\n name: string;\n width: number;\n valueType?: ColumnValueType;\n}\n\ninterface BorderSide {\n style?: \"thin\" | \"medium\" | \"thick\" | \"dotted\" | \"dashed\" | \"double\";\n color?: { argb: string };\n}\n\n/**\n * Type for ExcelJS cell borders.\n */\nexport interface CellBorder {\n top?: BorderSide;\n bottom?: BorderSide;\n left?: BorderSide;\n right?: BorderSide;\n}\n\n/**\n * Type for ExcelJS cell styles.\n * This is a simplified version of ExcelJS.Style that focuses on commonly used properties.\n */\nexport interface CellStyle {\n font?: Partial<Font>;\n fill?: Partial<Fill>;\n alignment?: Partial<Alignment>;\n border?: CellBorder;\n numFmt?: string;\n}\n\n/**\n * Helper type to convert RGB color string to ARGB format for ExcelJS.\n * ExcelJS uses ARGB format: \"FFFFFFFF\" where FF is alpha, then RGB.\n */\nexport type ArgbColor = { argb: string };\n\n/**\n * Helper function to convert RGB color string to ARGB format.\n * @param rgb - RGB color string without # (e.g., \"FFFFFF\")\n * @returns ARGB color object (e.g., { argb: \"FFFFFFFF\" })\n */\nexport const rgbToArgb = (rgb: string): ArgbColor => {\n return { argb: `FF${rgb}` };\n};\n","export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import mime from \"mime\";\n\nimport { DATA_URI_PATTERN, isValidDataUri } from \"~/utils/uri.utils\";\nimport { VALID_IMAGE_TYPES } from './mime.utils';\n\nexport { VALID_IMAGE_TYPES } from './mime.utils';\n\nexport interface ParseDataUriResult {\n mimeType: string;\n base64: string;\n ext: string;\n}\n\nexport const parseDataUri = (input: string): ParseDataUriResult | null => {\n if (!isValidDataUri(input)) return null;\n\n const match = DATA_URI_PATTERN.exec(input);\n if (!match) return null;\n\n const [, mimeType, base64] = match;\n if (!mimeType || !base64) return null;\n\n return { mimeType, base64, ext: mimeToExt(mimeType) };\n};\n\nexport const isImageMimeType = (mimeType: string): boolean => {\n return VALID_IMAGE_TYPES.includes(mimeType);\n};\n\nexport const isValidImageFile = (file: { type: string }): boolean => {\n return isImageMimeType(file.type);\n};\n\nexport const getExtensionFromDataUri = (dataUri: string): string => {\n const parsed = parseDataUri(dataUri);\n if (!parsed) return 'bin';\n\n return parsed.ext;\n};\n\nexport const getImagePreviewSrc = (input: string): string | null => {\n const parsed = parseDataUri(input);\n if (!parsed) return null;\n if (!isImageMimeType(parsed.mimeType)) return null;\n\n return input;\n};\n\nexport const mimeToExt = (mimeType: string): string => {\n const ext = mime.getExtension(mimeType);\n if (ext) return ext;\n\n const subtype = mimeType.split(\"/\")[1] ?? mimeType;\n return subtype.split(\"+\")[0] ?? subtype;\n};\n\nexport const getMimeType = (extension: string): string => {\n const mimeType = mime.getType(extension);\n if (!mimeType) {\n throw new Error(`[getMimeType] Mime type not found for extension: \"${extension}\"`);\n }\n\n return mimeType;\n};\n\n","import ExcelJS from \"exceljs\";\nimport { getMimeType } from \"../mime\";\nimport { isNullish } from \"../utils\";\nimport type { CellValue, ColumnMetadata } from \"./excel-generator.types\";\n\n/**\n * Sanitizes a worksheet title to comply with Excel's limitations.\n *\n * Excel enforces strict rules for worksheet names:\n * - Maximum length of 31 characters (legacy limitation from older Excel versions)\n * - Certain characters are reserved for special purposes and cannot be used:\n * - `:` (colon) - used in cell range references (e.g., A1:B2)\n * - `/` (forward slash) - used in file paths\n * - `\\` (backslash) - used in file paths and escape sequences\n * - `?` (question mark) - used as wildcard in formulas\n * - `*` (asterisk) - used as wildcard in formulas\n * - `[` and `]` (square brackets) - used in external references (e.g., [Book1.xlsx]Sheet1!A1)\n *\n * This function removes invalid characters and truncates the title to 31 characters\n * to ensure compatibility with Excel's file format specifications.\n *\n * @param title - The original worksheet title to sanitize\n * @returns A sanitized title that complies with Excel's naming rules\n */\nexport const sanitizeWorksheetTitle = (title: string): string => {\n const MAX_LENGTH = 31;\n const INVALID_CHARS = /[:/\\\\?*[\\]]/g;\n\n return title.trim().replace(INVALID_CHARS, \"\").slice(0, MAX_LENGTH);\n};\n\ninterface ToSafeCellValueArgs {\n value: CellValue;\n valueType: ColumnMetadata[\"valueType\"];\n}\n\nconst toSafeCellValue = ({ value, valueType }: ToSafeCellValueArgs): CellValue => {\n if (!isNullish(value) && valueType === \"number\") {\n const numValue = typeof value === \"number\" ? value : Number(value);\n return isNaN(numValue) ? value : numValue;\n }\n\n return value ?? \"\";\n};\n\ninterface ToSafeCellValuesArgs {\n headers: string[];\n rowData: Record<string, CellValue>;\n columnsMetadata?: Record<string, ColumnMetadata>;\n}\n\nconst toSafeCellValues = ({ headers, rowData, columnsMetadata }: ToSafeCellValuesArgs): CellValue[] => {\n return headers.map((header) => {\n const value = rowData[header];\n const metadata = columnsMetadata?.[header];\n const valueType = metadata?.valueType ?? \"auto\";\n return toSafeCellValue({ value, valueType });\n });\n};\n\ninterface ApplyTextFormattingArgs {\n row: ExcelJS.Row;\n headers: string[];\n columnsMetadata?: Record<string, ColumnMetadata>;\n}\n\nconst applyTextFormatting = ({ row, headers, columnsMetadata }: ApplyTextFormattingArgs): void => {\n for (let colIndex = 0; colIndex < headers.length; colIndex++) {\n const header = headers[colIndex];\n const metadata = columnsMetadata?.[header];\n const valueType = metadata?.valueType ?? \"auto\";\n\n if (valueType === \"text\") {\n const cell = row.getCell(colIndex + 1);\n cell.numFmt = \"@\";\n }\n }\n};\n\nconst applyHeaderStyles = (headerRow: ExcelJS.Row): void => {\n headerRow.font = { bold: true };\n headerRow.fill = {\n type: \"pattern\",\n pattern: \"solid\",\n fgColor: { argb: \"FFE0E0E0\" },\n };\n headerRow.alignment = { wrapText: true, vertical: \"top\" };\n};\n\ninterface SetupHeaderFrozenRowArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n}\n\nconst setupHeaderFrozenRow = ({ worksheet, headers }: SetupHeaderFrozenRowArgs): void => {\n const headerRow = worksheet.addRow(headers);\n applyHeaderStyles(headerRow);\n worksheet.views = [{ state: \"frozen\", ySplit: 1 }];\n};\n\ninterface ApplyDataRowStylesArgs {\n worksheet: ExcelJS.Worksheet;\n startRowIndex: number;\n endRowIndex: number;\n}\n\nconst applyDataRowStyles = ({ worksheet, startRowIndex, endRowIndex }: ApplyDataRowStylesArgs): void => {\n for (let rowIndex = startRowIndex; rowIndex <= endRowIndex; rowIndex++) {\n const row = worksheet.getRow(rowIndex);\n row.alignment = { wrapText: false, vertical: \"top\" };\n }\n};\n\ninterface SetColumnWidthsArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n columnsMetadata: Record<string, ColumnMetadata>;\n}\n\nconst setColumnWidths = ({ worksheet, headers, columnsMetadata }: SetColumnWidthsArgs): void => {\n worksheet.columns = headers.map((header) => {\n const metadata = columnsMetadata[header];\n return metadata ? { width: metadata.width } : {};\n });\n};\n\ninterface AddDataRowsArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n columnsMetadata: Record<string, ColumnMetadata>;\n data: Record<string, CellValue>[];\n}\n\nconst addDataRows = ({ worksheet, headers, columnsMetadata, data }: AddDataRowsArgs): void => {\n for (const rowData of data) {\n const safeCellValues = toSafeCellValues({ headers, rowData, columnsMetadata });\n const row = worksheet.addRow(safeCellValues);\n applyTextFormatting({ row, headers, columnsMetadata });\n }\n\n applyDataRowStyles({ worksheet, startRowIndex: 2, endRowIndex: data.length + 1 });\n};\n\ninterface GenerateExcelBlobArgs {\n worksheetTitle: string;\n data: Record<string, CellValue>[];\n /**\n * Metadata for columns, keyed by column name (translated header name).\n * If not provided, columns will use default width and auto-detect value types.\n */\n columnsMetadata: Record<string, ColumnMetadata>;\n}\n\nexport const generateExcelBlob = async (args: GenerateExcelBlobArgs): Promise<Blob> => {\n const { data, columnsMetadata, worksheetTitle } = args;\n\n if (data.length === 0) {\n throw new Error(\"No data to export\");\n }\n\n const headers = Object.keys(data[0]);\n const workbook = new ExcelJS.Workbook();\n const worksheet = workbook.addWorksheet(sanitizeWorksheetTitle(worksheetTitle));\n\n setupHeaderFrozenRow({ worksheet, headers });\n addDataRows({ worksheet, headers, columnsMetadata, data });\n setColumnWidths({ worksheet, headers, columnsMetadata });\n\n const buffer = await workbook.xlsx.writeBuffer();\n return new Blob([buffer], { type: getMimeType(\"xlsx\") });\n};\n"]}
|
package/dist/excel.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/excel/excel-generator.ts","../src/excel/excel-generator.types.ts","../src/utils/types.utils.ts","../src/mime/index.ts","../src/excel/exceljs.utils.ts"],"names":[],"mappings":"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","file":"excel.js","sourcesContent":["import ExcelJS from \"exceljs\";\nimport type { CellStyle, CellValue } from \"./excel-generator.types\";\n\ninterface AddCellArgs {\n row: number;\n col: number;\n value: CellValue;\n style?: CellStyle;\n}\n\ninterface AddRowArgs {\n values: CellValue[];\n styles?: CellStyle[];\n}\n\nexport class WorksheetBuilder {\n private worksheet: ExcelJS.Worksheet;\n private currentRow: number;\n private defaultRowStyles: CellStyle[];\n\n constructor(worksheet: ExcelJS.Worksheet) {\n this.worksheet = worksheet;\n this.currentRow = 1; // ExcelJS uses 1-based indexing\n this.defaultRowStyles = [];\n }\n\n /** Returns the current row index (1-based). */\n get rowCount(): number {\n return this.currentRow;\n }\n\n addCell({ row, col, value, style }: AddCellArgs): void {\n const cell = this.worksheet.getCell(row + 1, col + 1); // Convert to 1-based\n cell.value = value ?? \"\";\n\n if (style) {\n this.applyStyle(cell, style);\n }\n }\n\n addRow({ values, styles }: AddRowArgs): void {\n const stylesToUse = styles ?? this.defaultRowStyles;\n const row = this.worksheet.addRow(values);\n\n // Apply styles to each cell in the row\n for (let col = 0; col < values.length; col++) {\n const style = stylesToUse[col];\n if (style) {\n const cell = row.getCell(col + 1); // ExcelJS uses 1-based indexing\n this.applyStyle(cell, style);\n }\n }\n\n this.currentRow++;\n }\n\n setDefaultRowStyles(styles: CellStyle[]): void {\n this.defaultRowStyles = styles;\n }\n\n addEmptyRow(): void {\n this.worksheet.addRow([]);\n this.currentRow++;\n }\n\n setColumnWidths(columnWidths: number[]): void {\n this.worksheet.columns = columnWidths.map((width) => ({ width }));\n }\n\n getWorksheet(): ExcelJS.Worksheet {\n return this.worksheet;\n }\n\n private applyStyle(cell: ExcelJS.Cell, style: CellStyle): void {\n if (style.font) {\n cell.font = { ...cell.font, ...style.font };\n }\n\n if (style.fill) {\n cell.fill = style.fill as ExcelJS.Fill;\n }\n\n if (style.alignment) {\n cell.alignment = { ...cell.alignment, ...style.alignment };\n }\n\n if (style.border) {\n cell.border = { ...cell.border, ...style.border };\n }\n\n if (style.numFmt) {\n cell.numFmt = style.numFmt;\n }\n }\n}\n","import type { Font, Fill, Alignment } from \"exceljs\";\n\nexport type CellValue = string | number | null | undefined;\n\nexport type VerticalAlignment = \"top\" | \"middle\" | \"bottom\";\nexport type HorizontalAlignment = \"left\" | \"center\" | \"right\";\n\nexport type ColumnValueType = \"number\" | \"text\" | \"auto\";\n\nexport interface ColumnMetadata {\n name: string;\n width: number;\n valueType?: ColumnValueType;\n}\n\ninterface BorderSide {\n style?: \"thin\" | \"medium\" | \"thick\" | \"dotted\" | \"dashed\" | \"double\";\n color?: { argb: string };\n}\n\n/**\n * Type for ExcelJS cell borders.\n */\nexport interface CellBorder {\n top?: BorderSide;\n bottom?: BorderSide;\n left?: BorderSide;\n right?: BorderSide;\n}\n\n/**\n * Type for ExcelJS cell styles.\n * This is a simplified version of ExcelJS.Style that focuses on commonly used properties.\n */\nexport interface CellStyle {\n font?: Partial<Font>;\n fill?: Partial<Fill>;\n alignment?: Partial<Alignment>;\n border?: CellBorder;\n numFmt?: string;\n}\n\n/**\n * Helper type to convert RGB color string to ARGB format for ExcelJS.\n * ExcelJS uses ARGB format: \"FFFFFFFF\" where FF is alpha, then RGB.\n */\nexport type ArgbColor = { argb: string };\n\n/**\n * Helper function to convert RGB color string to ARGB format.\n * @param rgb - RGB color string without # (e.g., \"FFFFFF\")\n * @returns ARGB color object (e.g., { argb: \"FFFFFFFF\" })\n */\nexport const rgbToArgb = (rgb: string): ArgbColor => {\n return { argb: `FF${rgb}` };\n};\n","export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import mime from \"mime\";\n\nimport { DATA_URI_PATTERN, isValidDataUri } from \"~/utils/uri.utils\";\nimport { VALID_IMAGE_TYPES } from \"./mime.utils\";\n\nexport interface ParseDataUriResult {\n mimeType: string;\n base64: string;\n ext: string;\n}\n\nexport const parseDataUri = (input: string): ParseDataUriResult | null => {\n if (!isValidDataUri(input)) return null;\n\n const match = DATA_URI_PATTERN.exec(input);\n if (!match) return null;\n\n const [, mimeType, base64] = match;\n if (!mimeType || !base64) return null;\n\n return { mimeType, base64, ext: mimeToExt(mimeType) };\n};\n\nexport const isImageMimeType = (mimeType: string): boolean => {\n return VALID_IMAGE_TYPES.includes(mimeType);\n};\n\nexport const getImagePreviewSrc = (input: string): string | null => {\n const parsed = parseDataUri(input);\n if (!parsed) return null;\n if (!isImageMimeType(parsed.mimeType)) return null;\n\n return input;\n};\n\nexport const mimeToExt = (mimeType: string): string => {\n const ext = mime.getExtension(mimeType);\n if (ext) return ext;\n\n const subtype = mimeType.split(\"/\")[1] ?? mimeType;\n return subtype.split(\"+\")[0] ?? subtype;\n};\n\nexport const getMimeType = (extension: string): string => {\n const mimeType = mime.getType(extension);\n if (!mimeType) {\n throw new Error(`[getMimeType] Mime type not found for extension: \"${extension}\"`);\n }\n\n return mimeType;\n};\n\n","import ExcelJS from \"exceljs\";\nimport { getMimeType } from \"../mime\";\nimport { isNullish } from \"../utils\";\nimport type { CellValue, ColumnMetadata } from \"./excel-generator.types\";\n\n/**\n * Sanitizes a worksheet title to comply with Excel's limitations.\n *\n * Excel enforces strict rules for worksheet names:\n * - Maximum length of 31 characters (legacy limitation from older Excel versions)\n * - Certain characters are reserved for special purposes and cannot be used:\n * - `:` (colon) - used in cell range references (e.g., A1:B2)\n * - `/` (forward slash) - used in file paths\n * - `\\` (backslash) - used in file paths and escape sequences\n * - `?` (question mark) - used as wildcard in formulas\n * - `*` (asterisk) - used as wildcard in formulas\n * - `[` and `]` (square brackets) - used in external references (e.g., [Book1.xlsx]Sheet1!A1)\n *\n * This function removes invalid characters and truncates the title to 31 characters\n * to ensure compatibility with Excel's file format specifications.\n *\n * @param title - The original worksheet title to sanitize\n * @returns A sanitized title that complies with Excel's naming rules\n */\nexport const sanitizeWorksheetTitle = (title: string): string => {\n const MAX_LENGTH = 31;\n const INVALID_CHARS = /[:/\\\\?*[\\]]/g;\n\n return title.trim().replace(INVALID_CHARS, \"\").slice(0, MAX_LENGTH);\n};\n\ninterface ToSafeCellValueArgs {\n value: CellValue;\n valueType: ColumnMetadata[\"valueType\"];\n}\n\nconst toSafeCellValue = ({ value, valueType }: ToSafeCellValueArgs): CellValue => {\n if (!isNullish(value) && valueType === \"number\") {\n const numValue = typeof value === \"number\" ? value : Number(value);\n return isNaN(numValue) ? value : numValue;\n }\n\n return value ?? \"\";\n};\n\ninterface ToSafeCellValuesArgs {\n headers: string[];\n rowData: Record<string, CellValue>;\n columnsMetadata?: Record<string, ColumnMetadata>;\n}\n\nconst toSafeCellValues = ({ headers, rowData, columnsMetadata }: ToSafeCellValuesArgs): CellValue[] => {\n return headers.map((header) => {\n const value = rowData[header];\n const metadata = columnsMetadata?.[header];\n const valueType = metadata?.valueType ?? \"auto\";\n return toSafeCellValue({ value, valueType });\n });\n};\n\ninterface ApplyTextFormattingArgs {\n row: ExcelJS.Row;\n headers: string[];\n columnsMetadata?: Record<string, ColumnMetadata>;\n}\n\nconst applyTextFormatting = ({ row, headers, columnsMetadata }: ApplyTextFormattingArgs): void => {\n for (let colIndex = 0; colIndex < headers.length; colIndex++) {\n const header = headers[colIndex];\n const metadata = columnsMetadata?.[header];\n const valueType = metadata?.valueType ?? \"auto\";\n\n if (valueType === \"text\") {\n const cell = row.getCell(colIndex + 1);\n cell.numFmt = \"@\";\n }\n }\n};\n\nconst applyHeaderStyles = (headerRow: ExcelJS.Row): void => {\n headerRow.font = { bold: true };\n headerRow.fill = {\n type: \"pattern\",\n pattern: \"solid\",\n fgColor: { argb: \"FFE0E0E0\" },\n };\n headerRow.alignment = { wrapText: true, vertical: \"top\" };\n};\n\ninterface SetupHeaderFrozenRowArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n}\n\nconst setupHeaderFrozenRow = ({ worksheet, headers }: SetupHeaderFrozenRowArgs): void => {\n const headerRow = worksheet.addRow(headers);\n applyHeaderStyles(headerRow);\n worksheet.views = [{ state: \"frozen\", ySplit: 1 }];\n};\n\ninterface ApplyDataRowStylesArgs {\n worksheet: ExcelJS.Worksheet;\n startRowIndex: number;\n endRowIndex: number;\n}\n\nconst applyDataRowStyles = ({ worksheet, startRowIndex, endRowIndex }: ApplyDataRowStylesArgs): void => {\n for (let rowIndex = startRowIndex; rowIndex <= endRowIndex; rowIndex++) {\n const row = worksheet.getRow(rowIndex);\n row.alignment = { wrapText: false, vertical: \"top\" };\n }\n};\n\ninterface SetColumnWidthsArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n columnsMetadata: Record<string, ColumnMetadata>;\n}\n\nconst setColumnWidths = ({ worksheet, headers, columnsMetadata }: SetColumnWidthsArgs): void => {\n worksheet.columns = headers.map((header) => {\n const metadata = columnsMetadata[header];\n return metadata ? { width: metadata.width } : {};\n });\n};\n\ninterface AddDataRowsArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n columnsMetadata: Record<string, ColumnMetadata>;\n data: Record<string, CellValue>[];\n}\n\nconst addDataRows = ({ worksheet, headers, columnsMetadata, data }: AddDataRowsArgs): void => {\n for (const rowData of data) {\n const safeCellValues = toSafeCellValues({ headers, rowData, columnsMetadata });\n const row = worksheet.addRow(safeCellValues);\n applyTextFormatting({ row, headers, columnsMetadata });\n }\n\n applyDataRowStyles({ worksheet, startRowIndex: 2, endRowIndex: data.length + 1 });\n};\n\ninterface GenerateExcelBlobArgs {\n worksheetTitle: string;\n data: Record<string, CellValue>[];\n /**\n * Metadata for columns, keyed by column name (translated header name).\n * If not provided, columns will use default width and auto-detect value types.\n */\n columnsMetadata: Record<string, ColumnMetadata>;\n}\n\nexport const generateExcelBlob = async (args: GenerateExcelBlobArgs): Promise<Blob> => {\n const { data, columnsMetadata, worksheetTitle } = args;\n\n if (data.length === 0) {\n throw new Error(\"No data to export\");\n }\n\n const headers = Object.keys(data[0]);\n const workbook = new ExcelJS.Workbook();\n const worksheet = workbook.addWorksheet(sanitizeWorksheetTitle(worksheetTitle));\n\n setupHeaderFrozenRow({ worksheet, headers });\n addDataRows({ worksheet, headers, columnsMetadata, data });\n setColumnWidths({ worksheet, headers, columnsMetadata });\n\n const buffer = await workbook.xlsx.writeBuffer();\n return new Blob([buffer], { type: getMimeType(\"xlsx\") });\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/excel/excel-generator.ts","../src/excel/excel-generator.types.ts","../src/utils/types.utils.ts","../src/mime/index.ts","../src/excel/exceljs.utils.ts"],"names":[],"mappings":"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","file":"excel.js","sourcesContent":["import ExcelJS from \"exceljs\";\nimport type { CellStyle, CellValue } from \"./excel-generator.types\";\n\ninterface AddCellArgs {\n row: number;\n col: number;\n value: CellValue;\n style?: CellStyle;\n}\n\ninterface AddRowArgs {\n values: CellValue[];\n styles?: CellStyle[];\n}\n\nexport class WorksheetBuilder {\n private worksheet: ExcelJS.Worksheet;\n private currentRow: number;\n private defaultRowStyles: CellStyle[];\n\n constructor(worksheet: ExcelJS.Worksheet) {\n this.worksheet = worksheet;\n this.currentRow = 1; // ExcelJS uses 1-based indexing\n this.defaultRowStyles = [];\n }\n\n /** Returns the current row index (1-based). */\n get rowCount(): number {\n return this.currentRow;\n }\n\n addCell({ row, col, value, style }: AddCellArgs): void {\n const cell = this.worksheet.getCell(row + 1, col + 1); // Convert to 1-based\n cell.value = value ?? \"\";\n\n if (style) {\n this.applyStyle(cell, style);\n }\n }\n\n addRow({ values, styles }: AddRowArgs): void {\n const stylesToUse = styles ?? this.defaultRowStyles;\n const row = this.worksheet.addRow(values);\n\n // Apply styles to each cell in the row\n for (let col = 0; col < values.length; col++) {\n const style = stylesToUse[col];\n if (style) {\n const cell = row.getCell(col + 1); // ExcelJS uses 1-based indexing\n this.applyStyle(cell, style);\n }\n }\n\n this.currentRow++;\n }\n\n setDefaultRowStyles(styles: CellStyle[]): void {\n this.defaultRowStyles = styles;\n }\n\n addEmptyRow(): void {\n this.worksheet.addRow([]);\n this.currentRow++;\n }\n\n setColumnWidths(columnWidths: number[]): void {\n this.worksheet.columns = columnWidths.map((width) => ({ width }));\n }\n\n getWorksheet(): ExcelJS.Worksheet {\n return this.worksheet;\n }\n\n private applyStyle(cell: ExcelJS.Cell, style: CellStyle): void {\n if (style.font) {\n cell.font = { ...cell.font, ...style.font };\n }\n\n if (style.fill) {\n cell.fill = style.fill as ExcelJS.Fill;\n }\n\n if (style.alignment) {\n cell.alignment = { ...cell.alignment, ...style.alignment };\n }\n\n if (style.border) {\n cell.border = { ...cell.border, ...style.border };\n }\n\n if (style.numFmt) {\n cell.numFmt = style.numFmt;\n }\n }\n}\n","import type { Font, Fill, Alignment } from \"exceljs\";\n\nexport type CellValue = string | number | null | undefined;\n\nexport type VerticalAlignment = \"top\" | \"middle\" | \"bottom\";\nexport type HorizontalAlignment = \"left\" | \"center\" | \"right\";\n\nexport type ColumnValueType = \"number\" | \"text\" | \"auto\";\n\nexport interface ColumnMetadata {\n name: string;\n width: number;\n valueType?: ColumnValueType;\n}\n\ninterface BorderSide {\n style?: \"thin\" | \"medium\" | \"thick\" | \"dotted\" | \"dashed\" | \"double\";\n color?: { argb: string };\n}\n\n/**\n * Type for ExcelJS cell borders.\n */\nexport interface CellBorder {\n top?: BorderSide;\n bottom?: BorderSide;\n left?: BorderSide;\n right?: BorderSide;\n}\n\n/**\n * Type for ExcelJS cell styles.\n * This is a simplified version of ExcelJS.Style that focuses on commonly used properties.\n */\nexport interface CellStyle {\n font?: Partial<Font>;\n fill?: Partial<Fill>;\n alignment?: Partial<Alignment>;\n border?: CellBorder;\n numFmt?: string;\n}\n\n/**\n * Helper type to convert RGB color string to ARGB format for ExcelJS.\n * ExcelJS uses ARGB format: \"FFFFFFFF\" where FF is alpha, then RGB.\n */\nexport type ArgbColor = { argb: string };\n\n/**\n * Helper function to convert RGB color string to ARGB format.\n * @param rgb - RGB color string without # (e.g., \"FFFFFF\")\n * @returns ARGB color object (e.g., { argb: \"FFFFFFFF\" })\n */\nexport const rgbToArgb = (rgb: string): ArgbColor => {\n return { argb: `FF${rgb}` };\n};\n","export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import mime from \"mime\";\n\nimport { DATA_URI_PATTERN, isValidDataUri } from \"~/utils/uri.utils\";\nimport { VALID_IMAGE_TYPES } from './mime.utils';\n\nexport { VALID_IMAGE_TYPES } from './mime.utils';\n\nexport interface ParseDataUriResult {\n mimeType: string;\n base64: string;\n ext: string;\n}\n\nexport const parseDataUri = (input: string): ParseDataUriResult | null => {\n if (!isValidDataUri(input)) return null;\n\n const match = DATA_URI_PATTERN.exec(input);\n if (!match) return null;\n\n const [, mimeType, base64] = match;\n if (!mimeType || !base64) return null;\n\n return { mimeType, base64, ext: mimeToExt(mimeType) };\n};\n\nexport const isImageMimeType = (mimeType: string): boolean => {\n return VALID_IMAGE_TYPES.includes(mimeType);\n};\n\nexport const isValidImageFile = (file: { type: string }): boolean => {\n return isImageMimeType(file.type);\n};\n\nexport const getExtensionFromDataUri = (dataUri: string): string => {\n const parsed = parseDataUri(dataUri);\n if (!parsed) return 'bin';\n\n return parsed.ext;\n};\n\nexport const getImagePreviewSrc = (input: string): string | null => {\n const parsed = parseDataUri(input);\n if (!parsed) return null;\n if (!isImageMimeType(parsed.mimeType)) return null;\n\n return input;\n};\n\nexport const mimeToExt = (mimeType: string): string => {\n const ext = mime.getExtension(mimeType);\n if (ext) return ext;\n\n const subtype = mimeType.split(\"/\")[1] ?? mimeType;\n return subtype.split(\"+\")[0] ?? subtype;\n};\n\nexport const getMimeType = (extension: string): string => {\n const mimeType = mime.getType(extension);\n if (!mimeType) {\n throw new Error(`[getMimeType] Mime type not found for extension: \"${extension}\"`);\n }\n\n return mimeType;\n};\n\n","import ExcelJS from \"exceljs\";\nimport { getMimeType } from \"../mime\";\nimport { isNullish } from \"../utils\";\nimport type { CellValue, ColumnMetadata } from \"./excel-generator.types\";\n\n/**\n * Sanitizes a worksheet title to comply with Excel's limitations.\n *\n * Excel enforces strict rules for worksheet names:\n * - Maximum length of 31 characters (legacy limitation from older Excel versions)\n * - Certain characters are reserved for special purposes and cannot be used:\n * - `:` (colon) - used in cell range references (e.g., A1:B2)\n * - `/` (forward slash) - used in file paths\n * - `\\` (backslash) - used in file paths and escape sequences\n * - `?` (question mark) - used as wildcard in formulas\n * - `*` (asterisk) - used as wildcard in formulas\n * - `[` and `]` (square brackets) - used in external references (e.g., [Book1.xlsx]Sheet1!A1)\n *\n * This function removes invalid characters and truncates the title to 31 characters\n * to ensure compatibility with Excel's file format specifications.\n *\n * @param title - The original worksheet title to sanitize\n * @returns A sanitized title that complies with Excel's naming rules\n */\nexport const sanitizeWorksheetTitle = (title: string): string => {\n const MAX_LENGTH = 31;\n const INVALID_CHARS = /[:/\\\\?*[\\]]/g;\n\n return title.trim().replace(INVALID_CHARS, \"\").slice(0, MAX_LENGTH);\n};\n\ninterface ToSafeCellValueArgs {\n value: CellValue;\n valueType: ColumnMetadata[\"valueType\"];\n}\n\nconst toSafeCellValue = ({ value, valueType }: ToSafeCellValueArgs): CellValue => {\n if (!isNullish(value) && valueType === \"number\") {\n const numValue = typeof value === \"number\" ? value : Number(value);\n return isNaN(numValue) ? value : numValue;\n }\n\n return value ?? \"\";\n};\n\ninterface ToSafeCellValuesArgs {\n headers: string[];\n rowData: Record<string, CellValue>;\n columnsMetadata?: Record<string, ColumnMetadata>;\n}\n\nconst toSafeCellValues = ({ headers, rowData, columnsMetadata }: ToSafeCellValuesArgs): CellValue[] => {\n return headers.map((header) => {\n const value = rowData[header];\n const metadata = columnsMetadata?.[header];\n const valueType = metadata?.valueType ?? \"auto\";\n return toSafeCellValue({ value, valueType });\n });\n};\n\ninterface ApplyTextFormattingArgs {\n row: ExcelJS.Row;\n headers: string[];\n columnsMetadata?: Record<string, ColumnMetadata>;\n}\n\nconst applyTextFormatting = ({ row, headers, columnsMetadata }: ApplyTextFormattingArgs): void => {\n for (let colIndex = 0; colIndex < headers.length; colIndex++) {\n const header = headers[colIndex];\n const metadata = columnsMetadata?.[header];\n const valueType = metadata?.valueType ?? \"auto\";\n\n if (valueType === \"text\") {\n const cell = row.getCell(colIndex + 1);\n cell.numFmt = \"@\";\n }\n }\n};\n\nconst applyHeaderStyles = (headerRow: ExcelJS.Row): void => {\n headerRow.font = { bold: true };\n headerRow.fill = {\n type: \"pattern\",\n pattern: \"solid\",\n fgColor: { argb: \"FFE0E0E0\" },\n };\n headerRow.alignment = { wrapText: true, vertical: \"top\" };\n};\n\ninterface SetupHeaderFrozenRowArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n}\n\nconst setupHeaderFrozenRow = ({ worksheet, headers }: SetupHeaderFrozenRowArgs): void => {\n const headerRow = worksheet.addRow(headers);\n applyHeaderStyles(headerRow);\n worksheet.views = [{ state: \"frozen\", ySplit: 1 }];\n};\n\ninterface ApplyDataRowStylesArgs {\n worksheet: ExcelJS.Worksheet;\n startRowIndex: number;\n endRowIndex: number;\n}\n\nconst applyDataRowStyles = ({ worksheet, startRowIndex, endRowIndex }: ApplyDataRowStylesArgs): void => {\n for (let rowIndex = startRowIndex; rowIndex <= endRowIndex; rowIndex++) {\n const row = worksheet.getRow(rowIndex);\n row.alignment = { wrapText: false, vertical: \"top\" };\n }\n};\n\ninterface SetColumnWidthsArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n columnsMetadata: Record<string, ColumnMetadata>;\n}\n\nconst setColumnWidths = ({ worksheet, headers, columnsMetadata }: SetColumnWidthsArgs): void => {\n worksheet.columns = headers.map((header) => {\n const metadata = columnsMetadata[header];\n return metadata ? { width: metadata.width } : {};\n });\n};\n\ninterface AddDataRowsArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n columnsMetadata: Record<string, ColumnMetadata>;\n data: Record<string, CellValue>[];\n}\n\nconst addDataRows = ({ worksheet, headers, columnsMetadata, data }: AddDataRowsArgs): void => {\n for (const rowData of data) {\n const safeCellValues = toSafeCellValues({ headers, rowData, columnsMetadata });\n const row = worksheet.addRow(safeCellValues);\n applyTextFormatting({ row, headers, columnsMetadata });\n }\n\n applyDataRowStyles({ worksheet, startRowIndex: 2, endRowIndex: data.length + 1 });\n};\n\ninterface GenerateExcelBlobArgs {\n worksheetTitle: string;\n data: Record<string, CellValue>[];\n /**\n * Metadata for columns, keyed by column name (translated header name).\n * If not provided, columns will use default width and auto-detect value types.\n */\n columnsMetadata: Record<string, ColumnMetadata>;\n}\n\nexport const generateExcelBlob = async (args: GenerateExcelBlobArgs): Promise<Blob> => {\n const { data, columnsMetadata, worksheetTitle } = args;\n\n if (data.length === 0) {\n throw new Error(\"No data to export\");\n }\n\n const headers = Object.keys(data[0]);\n const workbook = new ExcelJS.Workbook();\n const worksheet = workbook.addWorksheet(sanitizeWorksheetTitle(worksheetTitle));\n\n setupHeaderFrozenRow({ worksheet, headers });\n addDataRows({ worksheet, headers, columnsMetadata, data });\n setColumnWidths({ worksheet, headers, columnsMetadata });\n\n const buffer = await workbook.xlsx.writeBuffer();\n return new Blob([buffer], { type: getMimeType(\"xlsx\") });\n};\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -2,6 +2,116 @@
|
|
|
2
2
|
|
|
3
3
|
var dateFns = require('date-fns');
|
|
4
4
|
|
|
5
|
+
// src/utils/base64.utils.ts
|
|
6
|
+
var BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
7
|
+
var encodeUtf8ToBytes = (text) => {
|
|
8
|
+
const codePoints = [];
|
|
9
|
+
for (let i = 0; i < text.length; i++) {
|
|
10
|
+
const codeUnit = text.charCodeAt(i);
|
|
11
|
+
if (codeUnit >= 55296 && codeUnit <= 56319 && i + 1 < text.length) {
|
|
12
|
+
const next = text.charCodeAt(i + 1);
|
|
13
|
+
if (next >= 56320 && next <= 57343) {
|
|
14
|
+
codePoints.push(65536 + (codeUnit - 55296 << 10) + (next - 56320));
|
|
15
|
+
i++;
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
codePoints.push(codeUnit);
|
|
20
|
+
}
|
|
21
|
+
const bytes = [];
|
|
22
|
+
for (const codePoint of codePoints) {
|
|
23
|
+
if (codePoint < 128) {
|
|
24
|
+
bytes.push(codePoint);
|
|
25
|
+
} else if (codePoint < 2048) {
|
|
26
|
+
bytes.push(192 | codePoint >> 6, 128 | codePoint & 63);
|
|
27
|
+
} else if (codePoint < 65536) {
|
|
28
|
+
bytes.push(224 | codePoint >> 12, 128 | codePoint >> 6 & 63, 128 | codePoint & 63);
|
|
29
|
+
} else {
|
|
30
|
+
bytes.push(
|
|
31
|
+
240 | codePoint >> 18,
|
|
32
|
+
128 | codePoint >> 12 & 63,
|
|
33
|
+
128 | codePoint >> 6 & 63,
|
|
34
|
+
128 | codePoint & 63
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return bytes;
|
|
39
|
+
};
|
|
40
|
+
var decodeUtf8FromBytes = (bytes) => {
|
|
41
|
+
let result = "";
|
|
42
|
+
let i = 0;
|
|
43
|
+
while (i < bytes.length) {
|
|
44
|
+
const b0 = bytes[i];
|
|
45
|
+
let codePoint;
|
|
46
|
+
if (b0 < 128) {
|
|
47
|
+
codePoint = b0;
|
|
48
|
+
i += 1;
|
|
49
|
+
} else if ((b0 & 224) === 192) {
|
|
50
|
+
codePoint = (b0 & 31) << 6 | bytes[i + 1] & 63;
|
|
51
|
+
i += 2;
|
|
52
|
+
} else if ((b0 & 240) === 224) {
|
|
53
|
+
codePoint = (b0 & 15) << 12 | (bytes[i + 1] & 63) << 6 | bytes[i + 2] & 63;
|
|
54
|
+
i += 3;
|
|
55
|
+
} else {
|
|
56
|
+
codePoint = (b0 & 7) << 18 | (bytes[i + 1] & 63) << 12 | (bytes[i + 2] & 63) << 6 | bytes[i + 3] & 63;
|
|
57
|
+
i += 4;
|
|
58
|
+
}
|
|
59
|
+
if (codePoint <= 65535) {
|
|
60
|
+
result += String.fromCharCode(codePoint);
|
|
61
|
+
} else {
|
|
62
|
+
const adjusted = codePoint - 65536;
|
|
63
|
+
result += String.fromCharCode(55296 + (adjusted >> 10), 56320 + (adjusted & 1023));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
};
|
|
68
|
+
var encodeBytesToBase64 = (bytes) => {
|
|
69
|
+
let output = "";
|
|
70
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
71
|
+
const b0 = bytes[i];
|
|
72
|
+
const b1 = i + 1 < bytes.length ? bytes[i + 1] : 0;
|
|
73
|
+
const b2 = i + 2 < bytes.length ? bytes[i + 2] : 0;
|
|
74
|
+
const triplet = b0 << 16 | b1 << 8 | b2;
|
|
75
|
+
output += BASE64_CHARS[triplet >> 18 & 63];
|
|
76
|
+
output += BASE64_CHARS[triplet >> 12 & 63];
|
|
77
|
+
output += i + 1 < bytes.length ? BASE64_CHARS[triplet >> 6 & 63] : "=";
|
|
78
|
+
output += i + 2 < bytes.length ? BASE64_CHARS[triplet & 63] : "=";
|
|
79
|
+
}
|
|
80
|
+
return output;
|
|
81
|
+
};
|
|
82
|
+
var decodeBase64ToBytes = (base64) => {
|
|
83
|
+
const sanitized = base64.replace(/[^A-Za-z0-9+/=]/g, "");
|
|
84
|
+
const bytes = [];
|
|
85
|
+
for (let i = 0; i < sanitized.length; i += 4) {
|
|
86
|
+
const c0 = BASE64_CHARS.indexOf(sanitized[i]);
|
|
87
|
+
const c1 = BASE64_CHARS.indexOf(sanitized[i + 1]);
|
|
88
|
+
const c2 = sanitized[i + 2] === "=" ? 0 : BASE64_CHARS.indexOf(sanitized[i + 2]);
|
|
89
|
+
const c3 = sanitized[i + 3] === "=" ? 0 : BASE64_CHARS.indexOf(sanitized[i + 3]);
|
|
90
|
+
if (c0 < 0 || c1 < 0 || sanitized[i + 2] !== "=" && c2 < 0 || sanitized[i + 3] !== "=" && c3 < 0) {
|
|
91
|
+
throw new Error("Invalid base64 character");
|
|
92
|
+
}
|
|
93
|
+
const triplet = c0 << 18 | c1 << 12 | c2 << 6 | c3;
|
|
94
|
+
bytes.push(triplet >> 16 & 255);
|
|
95
|
+
if (sanitized[i + 2] !== "=") bytes.push(triplet >> 8 & 255);
|
|
96
|
+
if (sanitized[i + 3] !== "=") bytes.push(triplet & 255);
|
|
97
|
+
}
|
|
98
|
+
return bytes;
|
|
99
|
+
};
|
|
100
|
+
var encodeBase64 = (text) => {
|
|
101
|
+
try {
|
|
102
|
+
return encodeBytesToBase64(encodeUtf8ToBytes(text));
|
|
103
|
+
} catch {
|
|
104
|
+
return "";
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
var decodeBase64 = (base64) => {
|
|
108
|
+
try {
|
|
109
|
+
return decodeUtf8FromBytes(decodeBase64ToBytes(base64));
|
|
110
|
+
} catch {
|
|
111
|
+
return "";
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
5
115
|
// src/utils/types.utils.ts
|
|
6
116
|
var NO_OP = () => {
|
|
7
117
|
};
|
|
@@ -284,6 +394,29 @@ var toFormStringInteger = (n) => {
|
|
|
284
394
|
return String(Math.round(n));
|
|
285
395
|
};
|
|
286
396
|
|
|
397
|
+
// src/utils/html.utils.ts
|
|
398
|
+
var HTML_ESCAPE_MAP = {
|
|
399
|
+
"&": "&",
|
|
400
|
+
"<": "<",
|
|
401
|
+
">": ">",
|
|
402
|
+
'"': """,
|
|
403
|
+
"'": "'"
|
|
404
|
+
};
|
|
405
|
+
var HTML_UNESCAPE_MAP = {
|
|
406
|
+
"&": "&",
|
|
407
|
+
"<": "<",
|
|
408
|
+
">": ">",
|
|
409
|
+
""": '"',
|
|
410
|
+
"'": "'",
|
|
411
|
+
"'": "'"
|
|
412
|
+
};
|
|
413
|
+
var escapeHtml = (text) => {
|
|
414
|
+
return text.replace(/[&<>"']/g, (char) => HTML_ESCAPE_MAP[char] ?? char);
|
|
415
|
+
};
|
|
416
|
+
var unescapeHtml = (text) => {
|
|
417
|
+
return text.replace(/&(?:amp|lt|gt|quot|#39|#x27);/g, (entity) => HTML_UNESCAPE_MAP[entity] ?? entity);
|
|
418
|
+
};
|
|
419
|
+
|
|
287
420
|
// src/utils/http.utils.ts
|
|
288
421
|
var isHttpSuccessStatus = (status) => {
|
|
289
422
|
if (isNullish(status)) {
|
|
@@ -304,6 +437,56 @@ var isHttpServerErrorStatus = (status) => {
|
|
|
304
437
|
return status >= 500;
|
|
305
438
|
};
|
|
306
439
|
|
|
440
|
+
// src/utils/json.utils.ts
|
|
441
|
+
var safeJsonParse = (value, fallback) => {
|
|
442
|
+
try {
|
|
443
|
+
return JSON.parse(value);
|
|
444
|
+
} catch {
|
|
445
|
+
return fallback;
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
var safeJsonStringify = (obj, space = 2) => {
|
|
449
|
+
if (!obj) return "";
|
|
450
|
+
try {
|
|
451
|
+
return JSON.stringify(obj, null, space);
|
|
452
|
+
} catch {
|
|
453
|
+
return "";
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
var sortObjectKeys = (_key, value) => value instanceof Object && !(value instanceof Array) ? Object.keys(value).sort().reduce(
|
|
457
|
+
(sorted, key) => {
|
|
458
|
+
sorted[key] = value[key];
|
|
459
|
+
return sorted;
|
|
460
|
+
},
|
|
461
|
+
{}
|
|
462
|
+
) : value;
|
|
463
|
+
var formatJson = ({ value, space = 2, sortKeys = false }) => {
|
|
464
|
+
if (!value) return "";
|
|
465
|
+
try {
|
|
466
|
+
const obj = JSON.parse(value);
|
|
467
|
+
return JSON.stringify(obj, sortKeys ? sortObjectKeys : void 0, space);
|
|
468
|
+
} catch {
|
|
469
|
+
return value;
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
var prettifyJson = (value) => {
|
|
473
|
+
return formatJson({ value, space: 4, sortKeys: true });
|
|
474
|
+
};
|
|
475
|
+
var minifyJson = (value) => {
|
|
476
|
+
return formatJson({ value, space: 0, sortKeys: true });
|
|
477
|
+
};
|
|
478
|
+
var isValidJson = (value) => {
|
|
479
|
+
try {
|
|
480
|
+
JSON.parse(value);
|
|
481
|
+
return true;
|
|
482
|
+
} catch {
|
|
483
|
+
return false;
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
var isMinifiedJson = (value) => {
|
|
487
|
+
return value === minifyJson(value);
|
|
488
|
+
};
|
|
489
|
+
|
|
307
490
|
// src/utils/number.utils.ts
|
|
308
491
|
var isInputValidNumber = (value) => {
|
|
309
492
|
if (isBlank(value)) return false;
|
|
@@ -337,6 +520,21 @@ var roundUpToNearest10 = (value) => {
|
|
|
337
520
|
if (isNullish(value)) return 0;
|
|
338
521
|
return Math.ceil(value / 10) * 10;
|
|
339
522
|
};
|
|
523
|
+
var formatCount = (count) => {
|
|
524
|
+
if (count >= 1e6) {
|
|
525
|
+
return `${(count / 1e6).toFixed(1)}M`;
|
|
526
|
+
}
|
|
527
|
+
if (count >= 1e3) {
|
|
528
|
+
return `${(count / 1e3).toFixed(1)}K`;
|
|
529
|
+
}
|
|
530
|
+
return count.toString();
|
|
531
|
+
};
|
|
532
|
+
var formatDuration = (ms) => {
|
|
533
|
+
if (ms < 1e3) {
|
|
534
|
+
return `${ms}ms`;
|
|
535
|
+
}
|
|
536
|
+
return `${(ms / 1e3).toFixed(1)}s`;
|
|
537
|
+
};
|
|
340
538
|
var getOrderOfMagnitudeExponent = (n) => {
|
|
341
539
|
if (isNullish(n)) return 0;
|
|
342
540
|
if (!isNumber(n)) return 0;
|
|
@@ -428,6 +626,24 @@ var hasScheme = (uri) => {
|
|
|
428
626
|
var extractBase64FromDataUri = (dataUri) => {
|
|
429
627
|
return dataUri.split(",")[1] ?? dataUri;
|
|
430
628
|
};
|
|
629
|
+
var encodeUrl = (value) => {
|
|
630
|
+
if (!value) return "";
|
|
631
|
+
return encodeURIComponent(value);
|
|
632
|
+
};
|
|
633
|
+
var decodeUrl = (value) => {
|
|
634
|
+
if (!value) return "";
|
|
635
|
+
try {
|
|
636
|
+
return decodeURIComponent(value);
|
|
637
|
+
} catch {
|
|
638
|
+
return value;
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
var formatDataUri = ({ mimeType, base64 }) => {
|
|
642
|
+
return `data:${mimeType};base64,${base64}`;
|
|
643
|
+
};
|
|
644
|
+
var getBase64ApproxSize = (base64) => {
|
|
645
|
+
return Math.round(base64.length * 3 / 4);
|
|
646
|
+
};
|
|
431
647
|
|
|
432
648
|
// src/utils/websocket.utils.ts
|
|
433
649
|
var WEBSOCKET_CONNECT_STATES = [WebSocket.CONNECTING, WebSocket.OPEN];
|
|
@@ -465,14 +681,24 @@ exports.countWords = countWords;
|
|
|
465
681
|
exports.dateAs_HHMMSS = dateAs_HHMMSS;
|
|
466
682
|
exports.dateAs_YYYYMMDD = dateAs_YYYYMMDD;
|
|
467
683
|
exports.dateAs_YYYYMMDD_HHMMSS = dateAs_YYYYMMDD_HHMMSS;
|
|
684
|
+
exports.decodeBase64 = decodeBase64;
|
|
685
|
+
exports.decodeUrl = decodeUrl;
|
|
686
|
+
exports.encodeBase64 = encodeBase64;
|
|
687
|
+
exports.encodeUrl = encodeUrl;
|
|
688
|
+
exports.escapeHtml = escapeHtml;
|
|
468
689
|
exports.extractBase64FromDataUri = extractBase64FromDataUri;
|
|
690
|
+
exports.formatCount = formatCount;
|
|
691
|
+
exports.formatDataUri = formatDataUri;
|
|
692
|
+
exports.formatDuration = formatDuration;
|
|
469
693
|
exports.formatIntegerDisplay = formatIntegerDisplay;
|
|
694
|
+
exports.formatJson = formatJson;
|
|
470
695
|
exports.formatUnixTimestamp = formatUnixTimestamp;
|
|
471
696
|
exports.fromHzToRpm = fromHzToRpm;
|
|
472
697
|
exports.fromM3psToGPM = fromM3psToGPM;
|
|
473
698
|
exports.fromMToInches = fromMToInches;
|
|
474
699
|
exports.fromPaToFt = fromPaToFt;
|
|
475
700
|
exports.fromWToHp = fromWToHp;
|
|
701
|
+
exports.getBase64ApproxSize = getBase64ApproxSize;
|
|
476
702
|
exports.getColorForPercentage = getColorForPercentage;
|
|
477
703
|
exports.getContrastTextColor = getContrastTextColor;
|
|
478
704
|
exports.getCurrentUnixTimestamp = getCurrentUnixTimestamp;
|
|
@@ -497,20 +723,24 @@ exports.isInputValidNegativeInteger = isInputValidNegativeInteger;
|
|
|
497
723
|
exports.isInputValidNumber = isInputValidNumber;
|
|
498
724
|
exports.isInputValidPositiveInteger = isInputValidPositiveInteger;
|
|
499
725
|
exports.isLightColor = isLightColor;
|
|
726
|
+
exports.isMinifiedJson = isMinifiedJson;
|
|
500
727
|
exports.isNotBlank = isNotBlank;
|
|
501
728
|
exports.isNullish = isNullish;
|
|
502
729
|
exports.isNumber = isNumber;
|
|
503
730
|
exports.isRuntimeEnvNodeJs = isRuntimeEnvNodeJs;
|
|
504
731
|
exports.isString = isString;
|
|
505
732
|
exports.isValidDataUri = isValidDataUri;
|
|
733
|
+
exports.isValidJson = isValidJson;
|
|
506
734
|
exports.isWsClosable = isWsClosable;
|
|
507
735
|
exports.isWsOpenOrConnecting = isWsOpenOrConnecting;
|
|
736
|
+
exports.minifyJson = minifyJson;
|
|
508
737
|
exports.nowAsDate = nowAsDate;
|
|
509
738
|
exports.nowAsDateTime = nowAsDateTime;
|
|
510
739
|
exports.nowAsDateTimeForFilename = nowAsDateTimeForFilename;
|
|
511
740
|
exports.nowAsTime = nowAsTime;
|
|
512
741
|
exports.parseCommaSeparatedList = parseCommaSeparatedList;
|
|
513
742
|
exports.parseOptionalFormNumber = parseOptionalFormNumber;
|
|
743
|
+
exports.prettifyJson = prettifyJson;
|
|
514
744
|
exports.removeDiacriticalMarks = removeDiacriticalMarks;
|
|
515
745
|
exports.rgbToHex = rgbToHex;
|
|
516
746
|
exports.rgbToString = rgbToString;
|
|
@@ -519,7 +749,10 @@ exports.rgbaToHexWithAlpha = rgbaToHexWithAlpha;
|
|
|
519
749
|
exports.rgbaToString = rgbaToString;
|
|
520
750
|
exports.roundToNiceNumber = roundToNiceNumber;
|
|
521
751
|
exports.roundUpToNearest10 = roundUpToNearest10;
|
|
752
|
+
exports.safeJsonParse = safeJsonParse;
|
|
753
|
+
exports.safeJsonStringify = safeJsonStringify;
|
|
522
754
|
exports.sleep = sleep;
|
|
755
|
+
exports.sortObjectKeys = sortObjectKeys;
|
|
523
756
|
exports.tickFormatter = tickFormatter;
|
|
524
757
|
exports.toError = toError;
|
|
525
758
|
exports.toFixed = toFixed;
|
|
@@ -528,6 +761,7 @@ exports.toFormStringInteger = toFormStringInteger;
|
|
|
528
761
|
exports.toToleranceLabel = toToleranceLabel;
|
|
529
762
|
exports.tooltipValueFormatter = tooltipValueFormatter;
|
|
530
763
|
exports.truncate = truncate;
|
|
764
|
+
exports.unescapeHtml = unescapeHtml;
|
|
531
765
|
exports.yieldToMainThread = yieldToMainThread;
|
|
532
766
|
//# sourceMappingURL=index.cjs.map
|
|
533
767
|
//# sourceMappingURL=index.cjs.map
|