df-script 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -34
- package/dist/api.d.ts +16 -8
- package/dist/api.js +11 -3
- package/dist/columnExpressions/ColumnExpr.d.ts +26 -364
- package/dist/columnExpressions/ColumnExpr.js +199 -3
- package/dist/columnExpressions/ExprBase.d.ts +8 -11
- package/dist/columnExpressions/ExprBase.js +20 -61
- package/dist/columnExpressions/constants.d.ts +2 -0
- package/dist/columnExpressions/constants.js +3 -1
- package/dist/columnExpressions/functions/coalesce.js +8 -4
- package/dist/columnExpressions/functions/element.d.ts +5 -0
- package/dist/columnExpressions/functions/element.js +11 -0
- package/dist/columnExpressions/functions/implode.d.ts +3 -0
- package/dist/columnExpressions/functions/implode.js +7 -0
- package/dist/columnExpressions/functions/lit.d.ts +3 -1
- package/dist/columnExpressions/functions/lit.js +8 -13
- package/dist/columnExpressions/functions/repeat.d.ts +47 -0
- package/dist/columnExpressions/functions/repeat.js +131 -0
- package/dist/columnExpressions/functions/seq_range.d.ts +31 -0
- package/dist/columnExpressions/functions/seq_range.js +91 -0
- package/dist/columnExpressions/functions/when.js +8 -4
- package/dist/columnExpressions/index.d.ts +5 -2
- package/dist/columnExpressions/index.js +5 -48
- package/dist/columnExpressions/mixins/AggregationExpr.d.ts +25 -50
- package/dist/columnExpressions/mixins/AggregationExpr.js +69 -165
- package/dist/columnExpressions/mixins/ArithmeticExpr.d.ts +41 -66
- package/dist/columnExpressions/mixins/ArithmeticExpr.js +126 -138
- package/dist/columnExpressions/mixins/BooleanExpr.d.ts +40 -0
- package/dist/columnExpressions/mixins/BooleanExpr.js +67 -0
- package/dist/columnExpressions/mixins/ComparisonExpr.d.ts +31 -60
- package/dist/columnExpressions/mixins/ComparisonExpr.js +231 -370
- package/dist/columnExpressions/mixins/ListExpr.d.ts +15 -39
- package/dist/columnExpressions/mixins/ListExpr.js +124 -148
- package/dist/columnExpressions/mixins/LogicalExpr.d.ts +7 -33
- package/dist/columnExpressions/mixins/LogicalExpr.js +48 -49
- package/dist/columnExpressions/mixins/ManipulationExpr.d.ts +6 -31
- package/dist/columnExpressions/mixins/ManipulationExpr.js +61 -25
- package/dist/columnExpressions/mixins/NumericExpr.d.ts +92 -0
- package/dist/columnExpressions/mixins/NumericExpr.js +259 -0
- package/dist/columnExpressions/mixins/StringExpr.d.ts +5 -30
- package/dist/columnExpressions/mixins/StringExpr.js +37 -49
- package/dist/columnExpressions/mixins/StructExpr.d.ts +16 -0
- package/dist/columnExpressions/mixins/StructExpr.js +139 -0
- package/dist/columnExpressions/mixins/TemporalExpr.d.ts +4 -30
- package/dist/columnExpressions/mixins/TemporalExpr.js +28 -29
- package/dist/columnExpressions/mixins/WindowExpr.d.ts +27 -49
- package/dist/columnExpressions/mixins/WindowExpr.js +144 -274
- package/dist/columnExpressions/utils.d.ts +4 -0
- package/dist/columnExpressions/utils.js +45 -0
- package/dist/dataframe/constants.d.ts +3 -0
- package/dist/dataframe/constants.js +4 -1
- package/dist/dataframe/dataframe.d.ts +27 -10
- package/dist/dataframe/dataframe.js +461 -220
- package/dist/dataframe/grouped/grouped.d.ts +2 -3
- package/dist/dataframe/grouped/grouped.js +17 -16
- package/dist/dataframe/types.d.ts +65 -1
- package/dist/dataframe/utils.d.ts +11 -3
- package/dist/dataframe/utils.js +101 -9
- package/dist/datatypes/DataType.d.ts +2 -1
- package/dist/datatypes/DataType.js +29 -0
- package/dist/datatypes/index.d.ts +2 -2
- package/dist/datatypes/types.d.ts +37 -36
- package/dist/datatypes/types.js +15 -11
- package/dist/exceptions/index.d.ts +2 -0
- package/dist/exceptions/index.js +4 -1
- package/dist/exceptions/utils.d.ts +1 -0
- package/dist/exceptions/utils.js +19 -0
- package/dist/functions/concat.d.ts +1 -2
- package/dist/functions/concat.js +24 -12
- package/dist/functions/index.d.ts +3 -1
- package/dist/functions/index.js +7 -15
- package/dist/functions/read_csv.d.ts +8 -0
- package/dist/functions/read_csv.js +53 -0
- package/dist/functions/read_json.d.ts +10 -0
- package/dist/functions/read_json.js +21 -0
- package/dist/functions/transpose.d.ts +7 -0
- package/dist/functions/transpose.js +84 -0
- package/dist/index.js +6 -1
- package/dist/types.d.ts +31 -5
- package/dist/utils/csv.d.ts +77 -0
- package/dist/utils/csv.js +313 -0
- package/dist/utils/date.js +5 -6
- package/dist/utils/guards.d.ts +0 -2
- package/dist/utils/guards.js +0 -31
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/json.d.ts +120 -2
- package/dist/utils/json.js +204 -20
- package/dist/utils/list.d.ts +173 -1
- package/dist/utils/list.js +255 -22
- package/dist/utils/number.d.ts +61 -28
- package/dist/utils/number.js +248 -70
- package/dist/utils/string.d.ts +5 -0
- package/dist/utils/string.js +89 -0
- package/package.json +5 -1
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatCsvValue = formatCsvValue;
|
|
4
|
+
exports.stringifyCSV = stringifyCSV;
|
|
5
|
+
exports.parseCSV = parseCSV;
|
|
6
|
+
exports.inferAndCoerceCSVColumn = inferAndCoerceCSVColumn;
|
|
7
|
+
const date_1 = require("./date");
|
|
8
|
+
const json_1 = require("./json");
|
|
9
|
+
const number_1 = require("./number");
|
|
10
|
+
const constants_1 = require("../dataframe/constants");
|
|
11
|
+
const datatypes_1 = require("../datatypes");
|
|
12
|
+
// Removed stringifyCsvObject
|
|
13
|
+
function formatCsvValueInternal(options = {}) {
|
|
14
|
+
const nullValue = options.nullValue !== undefined ? options.nullValue : "";
|
|
15
|
+
const formatNum = (0, number_1.formatNumber)(options.numericFormatOptions);
|
|
16
|
+
const replacerOptions = options.replacerOptions || {};
|
|
17
|
+
const formatDate = (v) => {
|
|
18
|
+
if (options.datetimeFormat)
|
|
19
|
+
return (0, date_1.strftime)(v, options.datetimeFormat);
|
|
20
|
+
if (options.dateFormat)
|
|
21
|
+
return (0, date_1.strftime)(v, options.dateFormat);
|
|
22
|
+
if (options.timeFormat)
|
|
23
|
+
return (0, date_1.strftime)(v, options.timeFormat);
|
|
24
|
+
return v.toISOString();
|
|
25
|
+
};
|
|
26
|
+
const replacer = (0, json_1.createSafeJsonReplacer)({
|
|
27
|
+
formatDate,
|
|
28
|
+
...replacerOptions
|
|
29
|
+
});
|
|
30
|
+
const hasCustomBigInt = typeof replacerOptions.onBigInt === "function";
|
|
31
|
+
return (val) => {
|
|
32
|
+
if (val === null || val === undefined || typeof val === "symbol" || typeof val === "function") {
|
|
33
|
+
return { str: nullValue, isNumeric: false };
|
|
34
|
+
}
|
|
35
|
+
if (typeof val === "bigint" && !hasCustomBigInt && !replacerOptions.voidBigIntReplacement) {
|
|
36
|
+
return { str: formatNum(val), isNumeric: true };
|
|
37
|
+
}
|
|
38
|
+
const processed = replacer.call(null, "", val);
|
|
39
|
+
if (processed === null || processed === undefined || typeof processed === "symbol" || typeof processed === "function") {
|
|
40
|
+
return { str: nullValue, isNumeric: false };
|
|
41
|
+
}
|
|
42
|
+
if (typeof processed === "string")
|
|
43
|
+
return { str: processed, isNumeric: false };
|
|
44
|
+
if (typeof processed === "number" || typeof processed === "bigint") {
|
|
45
|
+
return { str: formatNum(processed), isNumeric: true };
|
|
46
|
+
}
|
|
47
|
+
if (typeof processed === "boolean")
|
|
48
|
+
return { str: processed ? "true" : "false", isNumeric: false };
|
|
49
|
+
if (processed instanceof Number)
|
|
50
|
+
return { str: formatNum(processed.valueOf()), isNumeric: true };
|
|
51
|
+
if (processed instanceof String)
|
|
52
|
+
return { str: processed.valueOf(), isNumeric: false };
|
|
53
|
+
if (processed instanceof Boolean)
|
|
54
|
+
return { str: processed.valueOf() ? "true" : "false", isNumeric: false };
|
|
55
|
+
if (typeof processed === "object") {
|
|
56
|
+
// Instantiate a fresh replacer specifically for nested stringification
|
|
57
|
+
// to avoid state carry-over (such as visited circular reference sets)
|
|
58
|
+
return {
|
|
59
|
+
str: JSON.stringify(processed, (0, json_1.createSafeJsonReplacer)({
|
|
60
|
+
formatDate,
|
|
61
|
+
...replacerOptions
|
|
62
|
+
})),
|
|
63
|
+
isNumeric: false
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return { str: String(processed), isNumeric: false };
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function formatCsvValue(options = {}) {
|
|
70
|
+
const formatted = formatCsvValueInternal(options);
|
|
71
|
+
return (val) => formatted(val).str;
|
|
72
|
+
}
|
|
73
|
+
function stringifyCSV(columns, height, options = {}) {
|
|
74
|
+
const { separator = ",", quoteChar = '"', includeHeader = true, lineTerminator = constants_1.NEWLINE, quoteStyle = "necessary", includeBom = false, onRow, ...formatOptions } = options;
|
|
75
|
+
const keys = Object.keys(columns);
|
|
76
|
+
const numKeys = keys.length;
|
|
77
|
+
const lines = [];
|
|
78
|
+
let isFirstRow = true;
|
|
79
|
+
const formatValue = formatCsvValueInternal(formatOptions);
|
|
80
|
+
const escapeAndQuote = (val, isHeader = false) => {
|
|
81
|
+
let strVal;
|
|
82
|
+
let isNumeric = false;
|
|
83
|
+
if (isHeader) {
|
|
84
|
+
strVal = String(val);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const formatted = formatValue(val);
|
|
88
|
+
strVal = formatted.str;
|
|
89
|
+
isNumeric = formatted.isNumeric;
|
|
90
|
+
}
|
|
91
|
+
if (quoteStyle === "never") {
|
|
92
|
+
return strVal;
|
|
93
|
+
}
|
|
94
|
+
if (quoteStyle === "always") {
|
|
95
|
+
const escaped = strVal.split(quoteChar).join(quoteChar + quoteChar);
|
|
96
|
+
return quoteChar + escaped + quoteChar;
|
|
97
|
+
}
|
|
98
|
+
if (quoteStyle === "non_numeric") {
|
|
99
|
+
if (!isNumeric && val != null) {
|
|
100
|
+
const escaped = strVal.split(quoteChar).join(quoteChar + quoteChar);
|
|
101
|
+
return quoteChar + escaped + quoteChar;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Default: "necessary"
|
|
105
|
+
const needsQuoting = strVal.includes(separator) ||
|
|
106
|
+
strVal.includes(quoteChar) ||
|
|
107
|
+
strVal.includes(constants_1.NEWLINE) ||
|
|
108
|
+
strVal.includes(constants_1.CARRIAGE_RETURN);
|
|
109
|
+
if (needsQuoting) {
|
|
110
|
+
const escaped = strVal.split(quoteChar).join(quoteChar + quoteChar);
|
|
111
|
+
return quoteChar + escaped + quoteChar;
|
|
112
|
+
}
|
|
113
|
+
return strVal;
|
|
114
|
+
};
|
|
115
|
+
const outputLine = (line) => {
|
|
116
|
+
if (isFirstRow) {
|
|
117
|
+
const initial = includeBom ? constants_1.UTF8_BOM + line : line;
|
|
118
|
+
if (onRow) {
|
|
119
|
+
onRow(initial);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
lines.push(initial);
|
|
123
|
+
}
|
|
124
|
+
isFirstRow = false;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
if (onRow) {
|
|
128
|
+
onRow(lineTerminator + line);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
lines.push(line);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
// Headers
|
|
136
|
+
if (includeHeader) {
|
|
137
|
+
const headerRow = new Array(numKeys);
|
|
138
|
+
for (let i = 0; i < numKeys; i++) {
|
|
139
|
+
headerRow[i] = escapeAndQuote(keys[i], true);
|
|
140
|
+
}
|
|
141
|
+
outputLine(headerRow.join(separator));
|
|
142
|
+
}
|
|
143
|
+
// Rows
|
|
144
|
+
for (let r = 0; r < height; r++) {
|
|
145
|
+
const row = new Array(numKeys);
|
|
146
|
+
for (let i = 0; i < numKeys; i++) {
|
|
147
|
+
row[i] = escapeAndQuote(columns[keys[i]][r], false);
|
|
148
|
+
}
|
|
149
|
+
outputLine(row.join(separator));
|
|
150
|
+
}
|
|
151
|
+
return onRow ? (includeBom ? constants_1.UTF8_BOM : "") : lines.join(lineTerminator);
|
|
152
|
+
}
|
|
153
|
+
function parseCSV(content, options = {}) {
|
|
154
|
+
const separator = options.separator || ",";
|
|
155
|
+
const quoteChar = options.quoteChar || '"';
|
|
156
|
+
let csvContent = content;
|
|
157
|
+
if (csvContent.startsWith(constants_1.UTF8_BOM)) {
|
|
158
|
+
csvContent = csvContent.substring(constants_1.UTF8_BOM.length);
|
|
159
|
+
}
|
|
160
|
+
const rows = [];
|
|
161
|
+
let currentRow = [];
|
|
162
|
+
let currentCell = "";
|
|
163
|
+
let inQuotes = false;
|
|
164
|
+
let hasAnyData = false;
|
|
165
|
+
let lastCharWasSeparator = false;
|
|
166
|
+
const len = csvContent.length;
|
|
167
|
+
for (let i = 0; i < len; i++) {
|
|
168
|
+
const char = csvContent[i];
|
|
169
|
+
if (inQuotes) {
|
|
170
|
+
hasAnyData = true;
|
|
171
|
+
lastCharWasSeparator = false;
|
|
172
|
+
if (char === quoteChar) {
|
|
173
|
+
if (i + 1 < len && csvContent[i + 1] === quoteChar) {
|
|
174
|
+
currentCell += quoteChar;
|
|
175
|
+
i++; // Skip escaped quote
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
inQuotes = false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
currentCell += char;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
if (char === quoteChar) {
|
|
187
|
+
hasAnyData = true;
|
|
188
|
+
lastCharWasSeparator = false;
|
|
189
|
+
inQuotes = true;
|
|
190
|
+
}
|
|
191
|
+
else if (char === separator) {
|
|
192
|
+
currentRow.push(currentCell);
|
|
193
|
+
currentCell = "";
|
|
194
|
+
lastCharWasSeparator = true;
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
let isLineBreak = false;
|
|
198
|
+
if (char === constants_1.CARRIAGE_RETURN) {
|
|
199
|
+
isLineBreak = true;
|
|
200
|
+
if (i + 1 < len && csvContent[i + 1] === constants_1.NEWLINE) {
|
|
201
|
+
i++;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else if (char === constants_1.NEWLINE) {
|
|
205
|
+
isLineBreak = true;
|
|
206
|
+
}
|
|
207
|
+
if (isLineBreak) {
|
|
208
|
+
if (hasAnyData || currentRow.length > 0 || currentCell !== "" || lastCharWasSeparator) {
|
|
209
|
+
currentRow.push(currentCell);
|
|
210
|
+
rows.push(currentRow);
|
|
211
|
+
currentRow = [];
|
|
212
|
+
currentCell = "";
|
|
213
|
+
hasAnyData = false;
|
|
214
|
+
lastCharWasSeparator = false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
hasAnyData = true;
|
|
219
|
+
lastCharWasSeparator = false;
|
|
220
|
+
currentCell += char;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (hasAnyData || currentRow.length > 0 || currentCell !== "" || lastCharWasSeparator) {
|
|
226
|
+
currentRow.push(currentCell);
|
|
227
|
+
rows.push(currentRow);
|
|
228
|
+
}
|
|
229
|
+
return rows;
|
|
230
|
+
}
|
|
231
|
+
function inferAndCoerceCSVColumn(values, options = {}) {
|
|
232
|
+
const nullValues = new Set(options.nullValues ?? ["", "NA", "null", "NaN"]);
|
|
233
|
+
const len = values.length;
|
|
234
|
+
let isAllBoolean = true;
|
|
235
|
+
let isAllNumber = true;
|
|
236
|
+
let isAllBigInt = true;
|
|
237
|
+
let isAllDate = true;
|
|
238
|
+
let hasValidData = false;
|
|
239
|
+
for (let i = 0; i < len; i++) {
|
|
240
|
+
const val = values[i].trim();
|
|
241
|
+
if (nullValues.has(val))
|
|
242
|
+
continue;
|
|
243
|
+
hasValidData = true;
|
|
244
|
+
if (isAllBoolean) {
|
|
245
|
+
const lower = val.toLowerCase();
|
|
246
|
+
if (lower !== "true" && lower !== "false" && lower !== "1" && lower !== "0") {
|
|
247
|
+
isAllBoolean = false;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (isAllBigInt) {
|
|
251
|
+
if ((0, number_1.toValidBigInt)(val, { truncate: false }) === null) {
|
|
252
|
+
isAllBigInt = false;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (isAllNumber) {
|
|
256
|
+
if ((0, number_1.toValidNumber)(val, { allowNonFiniteNumbers: true }) === null) {
|
|
257
|
+
isAllNumber = false;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (isAllDate) {
|
|
261
|
+
if ((0, date_1.toValidDate)(val) === null) {
|
|
262
|
+
isAllDate = false;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Fast exit if it's strictly a string column
|
|
266
|
+
if (!isAllBoolean && !isAllNumber && !isAllBigInt && !isAllDate) {
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const out = new Array(len);
|
|
271
|
+
if (!hasValidData || (!isAllBoolean && !isAllNumber && !isAllBigInt && !isAllDate)) {
|
|
272
|
+
for (let i = 0; i < len; i++) {
|
|
273
|
+
const val = values[i];
|
|
274
|
+
out[i] = nullValues.has(val.trim()) ? null : val;
|
|
275
|
+
}
|
|
276
|
+
return { type: datatypes_1.Utf8, values: out };
|
|
277
|
+
}
|
|
278
|
+
if (isAllBoolean) {
|
|
279
|
+
for (let i = 0; i < len; i++) {
|
|
280
|
+
const val = values[i].trim();
|
|
281
|
+
if (nullValues.has(val)) {
|
|
282
|
+
out[i] = null;
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
const lower = val.toLowerCase();
|
|
286
|
+
out[i] = (lower === "true" || lower === "1");
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return { type: datatypes_1.Boolean, values: out };
|
|
290
|
+
}
|
|
291
|
+
if (isAllBigInt) {
|
|
292
|
+
for (let i = 0; i < len; i++) {
|
|
293
|
+
const val = values[i].trim();
|
|
294
|
+
out[i] = nullValues.has(val) ? null : (0, number_1.toValidBigInt)(val, { truncate: false });
|
|
295
|
+
}
|
|
296
|
+
return { type: datatypes_1.Int64, values: out };
|
|
297
|
+
}
|
|
298
|
+
if (isAllNumber) {
|
|
299
|
+
for (let i = 0; i < len; i++) {
|
|
300
|
+
const val = values[i].trim();
|
|
301
|
+
out[i] = nullValues.has(val) ? null : (0, number_1.toValidNumber)(val, { allowNonFiniteNumbers: true });
|
|
302
|
+
}
|
|
303
|
+
return { type: datatypes_1.Float64, values: out };
|
|
304
|
+
}
|
|
305
|
+
if (isAllDate) {
|
|
306
|
+
for (let i = 0; i < len; i++) {
|
|
307
|
+
const val = values[i].trim();
|
|
308
|
+
out[i] = nullValues.has(val) ? null : (0, date_1.toValidDate)(val);
|
|
309
|
+
}
|
|
310
|
+
return { type: datatypes_1.Datetime, values: out };
|
|
311
|
+
}
|
|
312
|
+
return { type: datatypes_1.Utf8, values: out };
|
|
313
|
+
}
|
package/dist/utils/date.js
CHANGED
|
@@ -18,6 +18,7 @@ exports.strftime = strftime;
|
|
|
18
18
|
exports.strptime = strptime;
|
|
19
19
|
exports.toValidDate = toValidDate;
|
|
20
20
|
const number_1 = require("./number");
|
|
21
|
+
const string_1 = require("./string");
|
|
21
22
|
/**
|
|
22
23
|
* Matches string values beginning with standard hour-and-minute formatting.
|
|
23
24
|
* Examples:
|
|
@@ -129,10 +130,9 @@ function isValidDate(v) {
|
|
|
129
130
|
return isValidDateObj(new Date(normalizeEpochToMs(Number(v))));
|
|
130
131
|
}
|
|
131
132
|
if (typeof v === "string") {
|
|
132
|
-
|
|
133
|
-
if (s === "")
|
|
133
|
+
if ((0, string_1.isBlankString)(v))
|
|
134
134
|
return false;
|
|
135
|
-
return isValidDateObj(new Date(
|
|
135
|
+
return isValidDateObj(new Date(v));
|
|
136
136
|
}
|
|
137
137
|
return false;
|
|
138
138
|
}
|
|
@@ -339,10 +339,9 @@ function toValidDate(input) {
|
|
|
339
339
|
return isValidDateObj(d) ? d : null;
|
|
340
340
|
}
|
|
341
341
|
if (typeof input === "string") {
|
|
342
|
-
|
|
343
|
-
if (s === "")
|
|
342
|
+
if ((0, string_1.isBlankString)(input))
|
|
344
343
|
return null;
|
|
345
|
-
const d = new Date(
|
|
344
|
+
const d = new Date(input);
|
|
346
345
|
return isValidDateObj(d) ? d : null;
|
|
347
346
|
}
|
|
348
347
|
return null;
|
package/dist/utils/guards.d.ts
CHANGED
|
@@ -10,5 +10,3 @@ export declare function isScalar(v: unknown): v is string | number | boolean | b
|
|
|
10
10
|
export declare function isValidBinary(v: unknown): v is Uint8Array | string | ArrayLike<any>;
|
|
11
11
|
export declare function toValidBinary(v: unknown): Uint8Array | null;
|
|
12
12
|
export declare function tryParseBoolean(v: unknown): boolean | undefined;
|
|
13
|
-
export declare function isJsonString(input: unknown, allowPrimitives?: boolean): input is string;
|
|
14
|
-
export declare function safeJsonParse(input: unknown): unknown;
|
package/dist/utils/guards.js
CHANGED
|
@@ -12,8 +12,6 @@ exports.isScalar = isScalar;
|
|
|
12
12
|
exports.isValidBinary = isValidBinary;
|
|
13
13
|
exports.toValidBinary = toValidBinary;
|
|
14
14
|
exports.tryParseBoolean = tryParseBoolean;
|
|
15
|
-
exports.isJsonString = isJsonString;
|
|
16
|
-
exports.safeJsonParse = safeJsonParse;
|
|
17
15
|
const date_1 = require("./date");
|
|
18
16
|
const number_1 = require("./number");
|
|
19
17
|
function isTypedArray(v) {
|
|
@@ -112,32 +110,3 @@ function tryParseBoolean(v) {
|
|
|
112
110
|
return undefined;
|
|
113
111
|
return boolMap[v.trim().toLowerCase()];
|
|
114
112
|
}
|
|
115
|
-
function isJsonString(input, allowPrimitives = false) {
|
|
116
|
-
if (typeof input !== "string")
|
|
117
|
-
return false;
|
|
118
|
-
const s = input.trim();
|
|
119
|
-
if (s === "")
|
|
120
|
-
return false;
|
|
121
|
-
if (!allowPrimitives) {
|
|
122
|
-
const isWrapped = (s.startsWith("{") && s.endsWith("}")) || (s.startsWith("[") && s.endsWith("]"));
|
|
123
|
-
if (!isWrapped)
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
try {
|
|
127
|
-
JSON.parse(s);
|
|
128
|
-
return true;
|
|
129
|
-
}
|
|
130
|
-
catch {
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
function safeJsonParse(input) {
|
|
135
|
-
if (typeof input !== "string")
|
|
136
|
-
return input;
|
|
137
|
-
try {
|
|
138
|
-
return JSON.parse(input);
|
|
139
|
-
}
|
|
140
|
-
catch {
|
|
141
|
-
return input;
|
|
142
|
-
}
|
|
143
|
-
}
|
package/dist/utils/index.d.ts
CHANGED
package/dist/utils/index.js
CHANGED
|
@@ -19,3 +19,5 @@ __exportStar(require("./number"), exports);
|
|
|
19
19
|
__exportStar(require("./date"), exports);
|
|
20
20
|
__exportStar(require("./list"), exports);
|
|
21
21
|
__exportStar(require("./string"), exports);
|
|
22
|
+
__exportStar(require("./json"), exports);
|
|
23
|
+
__exportStar(require("./csv"), exports);
|
package/dist/utils/json.d.ts
CHANGED
|
@@ -1,2 +1,120 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
1
|
+
import type { JSONFormat } from "../types";
|
|
2
|
+
export interface NDJSONParseOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Skip malformed or invalid lines instead of failing the entire parse.
|
|
5
|
+
* @default false
|
|
6
|
+
*/
|
|
7
|
+
skipInvalidLines?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Maximum number of non-empty lines to validate or parse. Lines beyond this limit are ignored.
|
|
10
|
+
*/
|
|
11
|
+
maxLines?: number;
|
|
12
|
+
/**
|
|
13
|
+
* Number of non-empty lines to skip at the beginning of the content.
|
|
14
|
+
* @default 0
|
|
15
|
+
*/
|
|
16
|
+
skipLines?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface JSONParseOptions {
|
|
19
|
+
/**
|
|
20
|
+
* The format of the JSON content, either "json" (standard JSON) or "ndjson" (Newline Delimited JSON).
|
|
21
|
+
* @default "json"
|
|
22
|
+
*/
|
|
23
|
+
format?: JSONFormat;
|
|
24
|
+
/**
|
|
25
|
+
* Whether to allow JSON primitives (like numbers, booleans, strings, or null) instead of requiring wrapped arrays/objects.
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
allowPrimitives?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Whether to trim leading and trailing whitespace from the outermost input before validating/parsing.
|
|
31
|
+
* @default false
|
|
32
|
+
*/
|
|
33
|
+
trimBeforeParse?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* A reviver function passed directly to JSON.parse for custom value transformation.
|
|
36
|
+
*/
|
|
37
|
+
reviver?: Parameters<typeof JSON.parse>[1];
|
|
38
|
+
/**
|
|
39
|
+
* Options specific to NDJSON parsing. Only applicable when format is "ndjson".
|
|
40
|
+
*/
|
|
41
|
+
ndjson?: NDJSONParseOptions;
|
|
42
|
+
}
|
|
43
|
+
export type SafeJsonParseOptions<T = unknown, F = T> = JSONParseOptions & {
|
|
44
|
+
/**
|
|
45
|
+
* A guard function to validate the shape of the parsed result. If the guard returns false,
|
|
46
|
+
* `onError` is called and the fallback (or original input) is returned.
|
|
47
|
+
*/
|
|
48
|
+
guard?: ((value: unknown) => value is T) | ((value: unknown) => boolean);
|
|
49
|
+
/**
|
|
50
|
+
* Called when parsing succeeds but the guard check fails.
|
|
51
|
+
*/
|
|
52
|
+
onError?: (err: unknown) => void;
|
|
53
|
+
/**
|
|
54
|
+
* A fallback value to return if parsing fails or the guard check fails.
|
|
55
|
+
* If not specified, the original input is returned.
|
|
56
|
+
*/
|
|
57
|
+
fallback?: F;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Validates whether the given value is a valid JSON or NDJSON string.
|
|
61
|
+
* Leverages single-pass parsing under the hood to ensure zero duplicate loops.
|
|
62
|
+
*
|
|
63
|
+
* @param input - The value to check.
|
|
64
|
+
* @param options - Configuration options for validation.
|
|
65
|
+
* @returns `true` if the input is a valid JSON or NDJSON string; `false` otherwise.
|
|
66
|
+
*/
|
|
67
|
+
export declare function isJsonString(input: unknown, options?: JSONParseOptions): input is string;
|
|
68
|
+
/**
|
|
69
|
+
* Safely parses a string containing JSON or NDJSON content in a single pass, returning the parsed value if successful
|
|
70
|
+
* and passing the guard validation. If parsing or validation fails, returns the fallback value (if provided)
|
|
71
|
+
* or the original input.
|
|
72
|
+
*
|
|
73
|
+
* @param input - The value to parse.
|
|
74
|
+
* @param options - Configuration options for parsing and validation.
|
|
75
|
+
* @returns The parsed value, the fallback, or the original input.
|
|
76
|
+
*/
|
|
77
|
+
export declare function safeJsonParse<T = unknown, I = unknown, F = T>(input: I, { format, allowPrimitives, trimBeforeParse, reviver, ndjson: { skipInvalidLines, maxLines, skipLines }, guard, onError, fallback }?: SafeJsonParseOptions<T, F>): T | I | F;
|
|
78
|
+
export interface SafeJsonReplacerOptions {
|
|
79
|
+
/** Custom formatter function for Date objects. Ignored if onDate is specified. */
|
|
80
|
+
formatDate?: (v: Date) => string;
|
|
81
|
+
/** Convert BigInts to numeric strings ("123") or numbers if safe. Defaults to "string". */
|
|
82
|
+
bigintStrategy?: "string" | "number";
|
|
83
|
+
/** Custom serialization override for BigInt values. */
|
|
84
|
+
onBigInt?: (v: bigint) => any;
|
|
85
|
+
/** Custom serialization override for TypedArray values. */
|
|
86
|
+
onTypedArray?: (v: any) => any;
|
|
87
|
+
/** Custom serialization override for Set objects. */
|
|
88
|
+
onSet?: (v: Set<any>) => any;
|
|
89
|
+
/** Custom serialization override for Map objects. */
|
|
90
|
+
onMap?: (v: Map<any, any>) => any;
|
|
91
|
+
/** Custom serialization override for RegExp objects. */
|
|
92
|
+
onRegExp?: (v: RegExp) => any;
|
|
93
|
+
/** Custom serialization override for Date objects. Takes precedence over formatDate. */
|
|
94
|
+
onDate?: (v: Date) => any;
|
|
95
|
+
/** Custom serialization override for Error objects. Prevents empty {} strings. */
|
|
96
|
+
onError?: (v: Error) => any;
|
|
97
|
+
/** Custom serialization override for URLSearchParams objects. */
|
|
98
|
+
onURLSearchParams?: (v: URLSearchParams) => any;
|
|
99
|
+
/** Catch-all serialization override for custom types. Runs after native type checks. */
|
|
100
|
+
onCustom?: (this: any, k: string, v: any) => any;
|
|
101
|
+
/** If true, handles circular references by replacing them with a string/override instead of throwing. */
|
|
102
|
+
handleCircular?: boolean;
|
|
103
|
+
/** Custom fallback string or value when a circular reference is found. Defaults to "[Circular]" */
|
|
104
|
+
onCircular?: (this: any, k: string, v: any) => any;
|
|
105
|
+
/** If true, disables/voids the default safe serialization for BigInt values. */
|
|
106
|
+
voidBigIntReplacement?: boolean;
|
|
107
|
+
/** If true, disables/voids the default safe serialization for TypedArray values. */
|
|
108
|
+
voidTypedArrayReplacement?: boolean;
|
|
109
|
+
/** If true, disables/voids the default safe serialization for Set objects. */
|
|
110
|
+
voidSetReplacement?: boolean;
|
|
111
|
+
/** If true, disables/voids the default safe serialization for Map objects. */
|
|
112
|
+
voidMapReplacement?: boolean;
|
|
113
|
+
/** If true, disables/voids the default safe serialization for RegExp objects. */
|
|
114
|
+
voidRegExpReplacement?: boolean;
|
|
115
|
+
/** If true, disables/voids the default safe serialization for Date objects. */
|
|
116
|
+
voidDateReplacement?: boolean;
|
|
117
|
+
/** A custom replacer function or array whitelist that runs first for pre-processing keys/values. */
|
|
118
|
+
replacer?: ((this: any, k: string, v: any) => any) | (string | number)[] | null;
|
|
119
|
+
}
|
|
120
|
+
export declare function createSafeJsonReplacer(options?: SafeJsonReplacerOptions): (this: any, k: string, v: any) => any;
|