@blamejs/core 0.7.4 → 0.7.18
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/CHANGELOG.md +423 -395
- package/README.md +150 -149
- package/bin/blamejs.js +0 -0
- package/index.js +308 -284
- package/lib/api-key.js +660 -663
- package/lib/api-snapshot.js +338 -338
- package/lib/app-shutdown.js +385 -385
- package/lib/app.js +365 -365
- package/lib/archive.js +250 -250
- package/lib/atomic-file.js +544 -544
- package/lib/audit-chain.js +177 -177
- package/lib/audit-sign.js +344 -344
- package/lib/audit-tools.js +677 -677
- package/lib/audit.js +766 -766
- package/lib/auth/jwt.js +311 -311
- package/lib/auth/lockout.js +436 -436
- package/lib/auth/oauth.js +721 -721
- package/lib/auth/passkey.js +181 -181
- package/lib/auth/password.js +594 -594
- package/lib/backup/bundle.js +217 -217
- package/lib/backup/crypto.js +176 -176
- package/lib/backup/index.js +515 -515
- package/lib/backup/manifest.js +282 -282
- package/lib/break-glass.js +1338 -1338
- package/lib/bundler.js +441 -441
- package/lib/cache-redis.js +256 -256
- package/lib/cache.js +1206 -1206
- package/lib/canonical-json.js +115 -115
- package/lib/chain-writer.js +234 -234
- package/lib/cli-helpers.js +206 -206
- package/lib/cli.js +2334 -2334
- package/lib/cluster-provider-db.js +317 -317
- package/lib/cluster-storage.js +226 -226
- package/lib/cluster.js +703 -703
- package/lib/codepoint-class.js +196 -0
- package/lib/config-drift.js +301 -301
- package/lib/consent.js +222 -222
- package/lib/constants.js +191 -191
- package/lib/cookies.js +315 -315
- package/lib/credential-hash.js +322 -322
- package/lib/crypto.js +266 -266
- package/lib/csv.js +275 -286
- package/lib/db-declare-row-policy.js +267 -267
- package/lib/db-declare-view.js +420 -421
- package/lib/db-query.js +406 -406
- package/lib/db-schema.js +319 -319
- package/lib/db.js +1288 -1288
- package/lib/deprecate.js +222 -222
- package/lib/dev.js +335 -335
- package/lib/dual-control.js +473 -473
- package/lib/error-page.js +420 -420
- package/lib/external-db-migrate.js +441 -441
- package/lib/external-db.js +1061 -1061
- package/lib/file-type.js +273 -273
- package/lib/file-upload.js +213 -10
- package/lib/forms.js +422 -422
- package/lib/framework-error.js +293 -215
- package/lib/framework-schema.js +717 -717
- package/lib/gate-contract.js +971 -0
- package/lib/guard-all.js +405 -0
- package/lib/guard-archive.js +739 -0
- package/lib/guard-csv.js +816 -0
- package/lib/guard-email.js +744 -0
- package/lib/guard-filename.js +724 -0
- package/lib/guard-html.js +976 -0
- package/lib/guard-json.js +729 -0
- package/lib/guard-markdown.js +586 -0
- package/lib/guard-svg.js +976 -0
- package/lib/guard-xml.js +405 -0
- package/lib/guard-yaml.js +529 -0
- package/lib/handlers.js +350 -350
- package/lib/http-client-cookie-jar.js +508 -508
- package/lib/http-client.js +1195 -1195
- package/lib/i18n.js +878 -878
- package/lib/jobs.js +185 -185
- package/lib/log-stream-cloudwatch.js +369 -369
- package/lib/log-stream-local.js +146 -146
- package/lib/log-stream-otlp-grpc.js +410 -410
- package/lib/log-stream-otlp.js +286 -286
- package/lib/log-stream-syslog.js +302 -302
- package/lib/log-stream-webhook.js +199 -199
- package/lib/log-stream.js +330 -330
- package/lib/log.js +500 -500
- package/lib/mail-bounce.js +528 -528
- package/lib/mail-dkim.js +369 -362
- package/lib/mail.js +981 -962
- package/lib/metrics.js +683 -683
- package/lib/middleware/api-encrypt.js +936 -936
- package/lib/middleware/attach-user.js +157 -157
- package/lib/middleware/body-parser.js +1170 -1091
- package/lib/middleware/bot-guard.js +178 -178
- package/lib/middleware/compression.js +452 -452
- package/lib/middleware/cors.js +314 -314
- package/lib/middleware/csp-nonce.js +348 -348
- package/lib/middleware/csrf-protect.js +316 -316
- package/lib/middleware/db-role-for.js +264 -264
- package/lib/middleware/health.js +392 -392
- package/lib/middleware/index.js +79 -79
- package/lib/middleware/rate-limit.js +358 -358
- package/lib/middleware/request-id.js +61 -61
- package/lib/middleware/request-log.js +168 -168
- package/lib/middleware/require-auth.js +104 -104
- package/lib/middleware/security-headers.js +116 -116
- package/lib/middleware/sse.js +166 -166
- package/lib/migrations.js +383 -383
- package/lib/mtls-ca.js +518 -518
- package/lib/mtls-engine-default.js +481 -481
- package/lib/network-dns.js +632 -632
- package/lib/network-heartbeat.js +290 -290
- package/lib/network-nts.js +574 -574
- package/lib/network-proxy.js +265 -265
- package/lib/network-tls.js +328 -328
- package/lib/network.js +233 -233
- package/lib/notify.js +612 -612
- package/lib/ntp-check.js +229 -229
- package/lib/numeric-bounds.js +111 -91
- package/lib/object-store/azure-blob-bucket-ops.js +349 -349
- package/lib/object-store/azure-blob.js +488 -488
- package/lib/object-store/gcs-bucket-ops.js +351 -351
- package/lib/object-store/gcs.js +519 -519
- package/lib/object-store/http-put.js +153 -153
- package/lib/object-store/index.js +197 -197
- package/lib/object-store/sigv4-bucket-ops.js +1092 -1092
- package/lib/object-store/sigv4.js +903 -903
- package/lib/observability.js +151 -151
- package/lib/otel-export.js +269 -269
- package/lib/pagination.js +464 -464
- package/lib/parsers/index.js +80 -80
- package/lib/parsers/safe-env.js +642 -642
- package/lib/parsers/safe-ini.js +292 -292
- package/lib/parsers/safe-toml.js +784 -784
- package/lib/parsers/safe-xml.js +390 -390
- package/lib/parsers/safe-yaml.js +1015 -1015
- package/lib/permissions.js +708 -708
- package/lib/pqc-agent.js +87 -87
- package/lib/pqc-gate.js +279 -279
- package/lib/protobuf-encoder.js +190 -190
- package/lib/protocol-dispatcher.js +161 -161
- package/lib/pubsub-redis.js +167 -167
- package/lib/pubsub.js +429 -429
- package/lib/queue-local.js +476 -476
- package/lib/queue-redis.js +745 -745
- package/lib/queue-sqs.js +319 -319
- package/lib/queue.js +695 -695
- package/lib/redis-client.js +519 -519
- package/lib/request-helpers.js +340 -340
- package/lib/restore-bundle.js +237 -237
- package/lib/restore-rollback.js +259 -259
- package/lib/restore.js +409 -409
- package/lib/retry.js +376 -376
- package/lib/router.js +748 -748
- package/lib/safe-async.js +735 -735
- package/lib/safe-buffer.js +237 -237
- package/lib/safe-json.js +541 -541
- package/lib/safe-schema.js +1266 -1266
- package/lib/safe-url.js +159 -159
- package/lib/scheduler.js +706 -706
- package/lib/security-assert.js +373 -373
- package/lib/seeders.js +618 -618
- package/lib/session.js +478 -478
- package/lib/slug.js +269 -269
- package/lib/ssrf-guard.js +401 -401
- package/lib/static.js +184 -4
- package/lib/storage.js +471 -471
- package/lib/subject.js +281 -281
- package/lib/template.js +791 -791
- package/lib/testing.js +798 -798
- package/lib/time.js +310 -310
- package/lib/totp.js +302 -302
- package/lib/tracing.js +494 -494
- package/lib/uuid.js +132 -132
- package/lib/validate-opts.js +340 -319
- package/lib/vault/index.js +308 -308
- package/lib/vault/rotate.js +784 -784
- package/lib/vault/wrap.js +296 -296
- package/lib/vendor/noble-ciphers.cjs +9 -9
- package/lib/webhook.js +595 -595
- package/lib/websocket.js +1048 -1048
- package/package.json +77 -77
- package/sbom.cyclonedx.json +7 -7
package/lib/csv.js
CHANGED
|
@@ -1,286 +1,275 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* csv — RFC 4180 parser + serializer with operator-friendly defaults.
|
|
4
|
-
*
|
|
5
|
-
* var rows = b.csv.parse(text);
|
|
6
|
-
* // header row by default — returns array of objects keyed by header
|
|
7
|
-
* // [ { name: "alice", age: "30" }, ... ]
|
|
8
|
-
*
|
|
9
|
-
* var arrays = b.csv.parse(text, { header: false });
|
|
10
|
-
* // returns array of arrays
|
|
11
|
-
*
|
|
12
|
-
* var out = b.csv.stringify(rows);
|
|
13
|
-
* // RFC 4180 +
|
|
14
|
-
*
|
|
15
|
-
* Defaults:
|
|
16
|
-
* parse:
|
|
17
|
-
* header: true first row is column names
|
|
18
|
-
* delimiter: "," single byte; "\t" / ";" supported
|
|
19
|
-
* quote: '"'
|
|
20
|
-
* trim: false cell whitespace preserved
|
|
21
|
-
* maxBytes: 16 MiB
|
|
22
|
-
* maxRows: 1,000,000
|
|
23
|
-
* maxFieldBytes: 1 MiB
|
|
24
|
-
* onBadRow: "throw" "skip" tolerates short/long rows
|
|
25
|
-
*
|
|
26
|
-
* stringify:
|
|
27
|
-
* header:
|
|
28
|
-
* delimiter:
|
|
29
|
-
* quote:
|
|
30
|
-
* eol:
|
|
31
|
-
* alwaysQuote:
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
var
|
|
47
|
-
|
|
48
|
-
var
|
|
49
|
-
|
|
50
|
-
var
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
columns:
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
function _validateDelim(name, value) {
|
|
73
|
-
if (typeof value !== "string" || value.length !== 1) {
|
|
74
|
-
throw new CsvError("csv/bad-delimiter",
|
|
75
|
-
name + " must be a single character, got " + JSON.stringify(value));
|
|
76
|
-
}
|
|
77
|
-
if (value === "\r" || value === "\n") {
|
|
78
|
-
throw new CsvError("csv/bad-delimiter",
|
|
79
|
-
name + " cannot be CR or LF");
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// ---- parse ----
|
|
84
|
-
|
|
85
|
-
function parse(input, opts) {
|
|
86
|
-
opts = Object.assign({}, DEFAULTS_PARSE, opts || {});
|
|
87
|
-
// maxBytes / maxRows / maxFieldBytes via shared lib/numeric-bounds —
|
|
88
|
-
// Infinity / NaN bypass the corresponding caps and let a hostile
|
|
89
|
-
// multi-megabyte CSV (or a single hostile row / single hostile field)
|
|
90
|
-
// through unbounded.
|
|
91
|
-
numericBounds.
|
|
92
|
-
"
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
var
|
|
124
|
-
var
|
|
125
|
-
var
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (ch ===
|
|
165
|
-
|
|
166
|
-
pos += 1;
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
} else if (ch === "
|
|
172
|
-
|
|
173
|
-
pos += 1;
|
|
174
|
-
} else
|
|
175
|
-
|
|
176
|
-
pos += 1;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if (
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if (
|
|
232
|
-
asObjects =
|
|
233
|
-
header = Array.isArray(opts.columns) ? opts.columns :
|
|
234
|
-
} else
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
return out.join(opts.eol);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
module.exports = {
|
|
281
|
-
parse: parse,
|
|
282
|
-
stringify: stringify,
|
|
283
|
-
CsvError: CsvError,
|
|
284
|
-
DEFAULTS_PARSE: DEFAULTS_PARSE,
|
|
285
|
-
DEFAULTS_STRINGIFY: DEFAULTS_STRINGIFY,
|
|
286
|
-
};
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* csv — RFC 4180 parser + serializer with operator-friendly defaults.
|
|
4
|
+
*
|
|
5
|
+
* var rows = b.csv.parse(text);
|
|
6
|
+
* // header row by default — returns array of objects keyed by header
|
|
7
|
+
* // [ { name: "alice", age: "30" }, ... ]
|
|
8
|
+
*
|
|
9
|
+
* var arrays = b.csv.parse(text, { header: false });
|
|
10
|
+
* // returns array of arrays
|
|
11
|
+
*
|
|
12
|
+
* var out = b.csv.stringify(rows);
|
|
13
|
+
* // RFC 4180 quoting + anti-DoS bounds. NO threat-catalog handling.
|
|
14
|
+
*
|
|
15
|
+
* Defaults:
|
|
16
|
+
* parse:
|
|
17
|
+
* header: true first row is column names
|
|
18
|
+
* delimiter: "," single byte; "\t" / ";" supported
|
|
19
|
+
* quote: '"'
|
|
20
|
+
* trim: false cell whitespace preserved
|
|
21
|
+
* maxBytes: 16 MiB
|
|
22
|
+
* maxRows: 1,000,000
|
|
23
|
+
* maxFieldBytes: 1 MiB
|
|
24
|
+
* onBadRow: "throw" "skip" tolerates short/long rows
|
|
25
|
+
*
|
|
26
|
+
* stringify:
|
|
27
|
+
* header: true (or array of explicit columns)
|
|
28
|
+
* delimiter: ","
|
|
29
|
+
* quote: '"'
|
|
30
|
+
* eol: "\r\n" RFC 4180; "\n" via opt
|
|
31
|
+
* alwaysQuote: false only quote when needed
|
|
32
|
+
*
|
|
33
|
+
* SCOPE: this module is for trusted-source-only emission. It performs RFC
|
|
34
|
+
* 4180 quote/delimiter escaping but does NOT defend against the broader
|
|
35
|
+
* CSV-injection threat catalog (Excel/Sheets formula triggers, Unicode
|
|
36
|
+
* bidi overrides, dangerous-function denylist, homoglyphs, control-byte
|
|
37
|
+
* injection, BOM mid-stream, dialect ambiguity, CSV-bombs). Any path
|
|
38
|
+
* that emits or accepts user-supplied cells MUST route through
|
|
39
|
+
* `b.guardCsv` — its `serialize` / `validate` / `sanitize` / `gate`
|
|
40
|
+
* surface handles every documented threat with a single profile choice
|
|
41
|
+
* (strict / balanced / permissive / email-attachment) or compliance
|
|
42
|
+
* posture (hipaa / pci-dss / gdpr / soc2-cc7).
|
|
43
|
+
*
|
|
44
|
+
* Throws CsvError (FrameworkError, permanent) on shape violations.
|
|
45
|
+
*/
|
|
46
|
+
var C = require("./constants");
|
|
47
|
+
var numericBounds = require("./numeric-bounds");
|
|
48
|
+
var { defineClass } = require("./framework-error");
|
|
49
|
+
|
|
50
|
+
var CsvError = defineClass("CsvError", { alwaysPermanent: true });
|
|
51
|
+
|
|
52
|
+
var DEFAULTS_PARSE = {
|
|
53
|
+
header: true,
|
|
54
|
+
delimiter: ",",
|
|
55
|
+
quote: "\"",
|
|
56
|
+
trim: false,
|
|
57
|
+
maxBytes: C.BYTES.mib(16),
|
|
58
|
+
maxRows: 1_000_000,
|
|
59
|
+
maxFieldBytes: C.BYTES.mib(1),
|
|
60
|
+
onBadRow: "throw",
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
var DEFAULTS_STRINGIFY = {
|
|
64
|
+
header: true,
|
|
65
|
+
delimiter: ",",
|
|
66
|
+
quote: "\"",
|
|
67
|
+
eol: "\r\n",
|
|
68
|
+
alwaysQuote: false,
|
|
69
|
+
columns: null,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
function _validateDelim(name, value) {
|
|
73
|
+
if (typeof value !== "string" || value.length !== 1) {
|
|
74
|
+
throw new CsvError("csv/bad-delimiter",
|
|
75
|
+
name + " must be a single character, got " + JSON.stringify(value));
|
|
76
|
+
}
|
|
77
|
+
if (value === "\r" || value === "\n") {
|
|
78
|
+
throw new CsvError("csv/bad-delimiter",
|
|
79
|
+
name + " cannot be CR or LF");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ---- parse ----
|
|
84
|
+
|
|
85
|
+
function parse(input, opts) {
|
|
86
|
+
opts = Object.assign({}, DEFAULTS_PARSE, opts || {});
|
|
87
|
+
// maxBytes / maxRows / maxFieldBytes via shared lib/numeric-bounds —
|
|
88
|
+
// Infinity / NaN bypass the corresponding caps and let a hostile
|
|
89
|
+
// multi-megabyte CSV (or a single hostile row / single hostile field)
|
|
90
|
+
// through unbounded.
|
|
91
|
+
numericBounds.requireAllPositiveFiniteIntIfPresent(opts,
|
|
92
|
+
["maxBytes", "maxRows", "maxFieldBytes"],
|
|
93
|
+
"csv.parse", CsvError, "csv/bad-opt");
|
|
94
|
+
|
|
95
|
+
var s;
|
|
96
|
+
if (typeof input === "string") s = input;
|
|
97
|
+
else if (Buffer.isBuffer(input)) s = input.toString("utf8");
|
|
98
|
+
else if (input instanceof Uint8Array) s = Buffer.from(input).toString("utf8");
|
|
99
|
+
else {
|
|
100
|
+
throw new CsvError("csv/bad-input",
|
|
101
|
+
"parse: input must be string, Buffer, or Uint8Array, got " + typeof input);
|
|
102
|
+
}
|
|
103
|
+
if (s.charCodeAt(0) === 0xFEFF) s = s.slice(1);
|
|
104
|
+
if (Buffer.byteLength(s, "utf8") > opts.maxBytes) {
|
|
105
|
+
throw new CsvError("csv/too-large",
|
|
106
|
+
"parse: input exceeds maxBytes (" + opts.maxBytes + ")");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
_validateDelim("delimiter", opts.delimiter);
|
|
110
|
+
_validateDelim("quote", opts.quote);
|
|
111
|
+
if (opts.delimiter === opts.quote) {
|
|
112
|
+
throw new CsvError("csv/bad-delimiter",
|
|
113
|
+
"delimiter and quote must differ");
|
|
114
|
+
}
|
|
115
|
+
if (opts.onBadRow !== "throw" && opts.onBadRow !== "skip") {
|
|
116
|
+
throw new CsvError("csv/bad-opt",
|
|
117
|
+
"onBadRow must be 'throw' or 'skip'");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
var len = s.length;
|
|
121
|
+
var pos = 0;
|
|
122
|
+
var rows = [];
|
|
123
|
+
var row = [];
|
|
124
|
+
var field = "";
|
|
125
|
+
var inQuote = false;
|
|
126
|
+
|
|
127
|
+
function pushField() {
|
|
128
|
+
if (Buffer.byteLength(field, "utf8") > opts.maxFieldBytes) {
|
|
129
|
+
throw new CsvError("csv/field-too-large",
|
|
130
|
+
"field exceeds maxFieldBytes at row " + (rows.length + 1));
|
|
131
|
+
}
|
|
132
|
+
row.push(opts.trim ? field.trim() : field);
|
|
133
|
+
field = "";
|
|
134
|
+
}
|
|
135
|
+
function pushRow() {
|
|
136
|
+
pushField();
|
|
137
|
+
rows.push(row);
|
|
138
|
+
if (rows.length > opts.maxRows) {
|
|
139
|
+
throw new CsvError("csv/too-many-rows",
|
|
140
|
+
"row count exceeds maxRows (" + opts.maxRows + ")");
|
|
141
|
+
}
|
|
142
|
+
row = [];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
while (pos < len) {
|
|
146
|
+
var ch = s.charAt(pos);
|
|
147
|
+
if (inQuote) {
|
|
148
|
+
if (ch === opts.quote) {
|
|
149
|
+
if (pos + 1 < len && s.charAt(pos + 1) === opts.quote) {
|
|
150
|
+
field += opts.quote;
|
|
151
|
+
pos += 2;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
inQuote = false;
|
|
155
|
+
pos += 1;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
field += ch;
|
|
159
|
+
pos += 1;
|
|
160
|
+
} else {
|
|
161
|
+
if (ch === opts.delimiter) {
|
|
162
|
+
pushField();
|
|
163
|
+
pos += 1;
|
|
164
|
+
} else if (ch === "\r") {
|
|
165
|
+
pushRow();
|
|
166
|
+
pos += 1;
|
|
167
|
+
if (pos < len && s.charAt(pos) === "\n") pos += 1;
|
|
168
|
+
} else if (ch === "\n") {
|
|
169
|
+
pushRow();
|
|
170
|
+
pos += 1;
|
|
171
|
+
} else if (ch === opts.quote && field === "") {
|
|
172
|
+
inQuote = true;
|
|
173
|
+
pos += 1;
|
|
174
|
+
} else {
|
|
175
|
+
field += ch;
|
|
176
|
+
pos += 1;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (inQuote) {
|
|
181
|
+
throw new CsvError("csv/unterminated-quote",
|
|
182
|
+
"unterminated quoted field");
|
|
183
|
+
}
|
|
184
|
+
if (field.length > 0 || row.length > 0) pushRow();
|
|
185
|
+
|
|
186
|
+
if (!opts.header) return rows;
|
|
187
|
+
if (rows.length === 0) return [];
|
|
188
|
+
|
|
189
|
+
var header = rows[0];
|
|
190
|
+
var out = [];
|
|
191
|
+
for (var r = 1; r < rows.length; r++) {
|
|
192
|
+
var rec = rows[r];
|
|
193
|
+
if (rec.length !== header.length) {
|
|
194
|
+
if (opts.onBadRow === "skip") continue;
|
|
195
|
+
throw new CsvError("csv/row-length-mismatch",
|
|
196
|
+
"row " + r + " has " + rec.length + " fields, header has " + header.length);
|
|
197
|
+
}
|
|
198
|
+
var obj = {};
|
|
199
|
+
for (var i = 0; i < header.length; i++) obj[header[i]] = rec[i];
|
|
200
|
+
out.push(obj);
|
|
201
|
+
}
|
|
202
|
+
return out;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ---- stringify ----
|
|
206
|
+
|
|
207
|
+
function stringify(rows, opts) {
|
|
208
|
+
opts = Object.assign({}, DEFAULTS_STRINGIFY, opts || {});
|
|
209
|
+
if (!Array.isArray(rows)) {
|
|
210
|
+
throw new CsvError("csv/bad-input",
|
|
211
|
+
"stringify: rows must be an array, got " + typeof rows);
|
|
212
|
+
}
|
|
213
|
+
_validateDelim("delimiter", opts.delimiter);
|
|
214
|
+
_validateDelim("quote", opts.quote);
|
|
215
|
+
if (opts.delimiter === opts.quote) {
|
|
216
|
+
throw new CsvError("csv/bad-delimiter",
|
|
217
|
+
"delimiter and quote must differ");
|
|
218
|
+
}
|
|
219
|
+
if (opts.eol !== "\r\n" && opts.eol !== "\n") {
|
|
220
|
+
throw new CsvError("csv/bad-opt",
|
|
221
|
+
"eol must be '\\r\\n' or '\\n'");
|
|
222
|
+
}
|
|
223
|
+
if (rows.length === 0) return "";
|
|
224
|
+
|
|
225
|
+
var first = rows[0];
|
|
226
|
+
var asObjects;
|
|
227
|
+
var header;
|
|
228
|
+
if (Array.isArray(first)) {
|
|
229
|
+
asObjects = false;
|
|
230
|
+
header = Array.isArray(opts.columns) ? opts.columns : null;
|
|
231
|
+
} else if (first !== null && typeof first === "object") {
|
|
232
|
+
asObjects = true;
|
|
233
|
+
header = Array.isArray(opts.columns) ? opts.columns : Object.keys(first);
|
|
234
|
+
} else {
|
|
235
|
+
throw new CsvError("csv/bad-input",
|
|
236
|
+
"stringify: rows must be arrays or plain objects");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function escapeCell(value) {
|
|
240
|
+
var str = value == null ? "" : String(value);
|
|
241
|
+
var needsQuote = opts.alwaysQuote ||
|
|
242
|
+
str.indexOf(opts.delimiter) !== -1 ||
|
|
243
|
+
str.indexOf(opts.quote) !== -1 ||
|
|
244
|
+
str.indexOf("\n") !== -1 ||
|
|
245
|
+
str.indexOf("\r") !== -1;
|
|
246
|
+
if (needsQuote) {
|
|
247
|
+
str = opts.quote + str.split(opts.quote).join(opts.quote + opts.quote) + opts.quote;
|
|
248
|
+
}
|
|
249
|
+
return str;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
var out = [];
|
|
253
|
+
if (opts.header && header) {
|
|
254
|
+
out.push(header.map(escapeCell).join(opts.delimiter));
|
|
255
|
+
}
|
|
256
|
+
for (var i = 0; i < rows.length; i++) {
|
|
257
|
+
var rec = rows[i];
|
|
258
|
+
var cells;
|
|
259
|
+
if (asObjects) {
|
|
260
|
+
cells = header.map(function (k) { return escapeCell(rec[k]); });
|
|
261
|
+
} else {
|
|
262
|
+
cells = rec.map(escapeCell);
|
|
263
|
+
}
|
|
264
|
+
out.push(cells.join(opts.delimiter));
|
|
265
|
+
}
|
|
266
|
+
return out.join(opts.eol);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
module.exports = {
|
|
270
|
+
parse: parse,
|
|
271
|
+
stringify: stringify,
|
|
272
|
+
CsvError: CsvError,
|
|
273
|
+
DEFAULTS_PARSE: DEFAULTS_PARSE,
|
|
274
|
+
DEFAULTS_STRINGIFY: DEFAULTS_STRINGIFY,
|
|
275
|
+
};
|