@blamejs/core 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/CHANGELOG.md +230 -0
  2. package/LICENSE +201 -0
  3. package/LTS-CALENDAR.md +29 -0
  4. package/MIGRATING.md +7 -0
  5. package/NOTICE +59 -0
  6. package/README.md +100 -0
  7. package/bin/blamejs.js +13 -0
  8. package/index.js +253 -0
  9. package/lib/api-key.js +705 -0
  10. package/lib/api-snapshot.js +335 -0
  11. package/lib/app-shutdown.js +381 -0
  12. package/lib/app.js +364 -0
  13. package/lib/atomic-file.js +525 -0
  14. package/lib/audit-chain.js +168 -0
  15. package/lib/audit-sign.js +319 -0
  16. package/lib/audit-tools.js +682 -0
  17. package/lib/audit.js +753 -0
  18. package/lib/auth/jwt.js +280 -0
  19. package/lib/auth/oauth.js +691 -0
  20. package/lib/auth/passkey.js +185 -0
  21. package/lib/auth/password.js +139 -0
  22. package/lib/auth/totp.js +17 -0
  23. package/lib/auth-header.js +81 -0
  24. package/lib/backup/bundle.js +219 -0
  25. package/lib/backup/crypto.js +174 -0
  26. package/lib/backup/index.js +490 -0
  27. package/lib/backup/manifest.js +275 -0
  28. package/lib/bundler.js +295 -0
  29. package/lib/cache.js +819 -0
  30. package/lib/chain-writer.js +234 -0
  31. package/lib/cli-helpers.js +201 -0
  32. package/lib/cli.js +1377 -0
  33. package/lib/cluster-provider-db.js +245 -0
  34. package/lib/cluster-storage.js +166 -0
  35. package/lib/cluster.js +691 -0
  36. package/lib/consent.js +222 -0
  37. package/lib/constants.js +186 -0
  38. package/lib/cookies.js +293 -0
  39. package/lib/credential-hash.js +303 -0
  40. package/lib/crypto-field.js +159 -0
  41. package/lib/crypto.js +250 -0
  42. package/lib/db-query.js +297 -0
  43. package/lib/db-schema.js +250 -0
  44. package/lib/db.js +1054 -0
  45. package/lib/deprecate.js +226 -0
  46. package/lib/dev.js +324 -0
  47. package/lib/error-page.js +424 -0
  48. package/lib/events.js +135 -0
  49. package/lib/external-db.js +422 -0
  50. package/lib/forms.js +378 -0
  51. package/lib/framework-error.js +189 -0
  52. package/lib/framework-schema.js +604 -0
  53. package/lib/handlers.js +350 -0
  54. package/lib/html-balance.js +227 -0
  55. package/lib/http-client.js +615 -0
  56. package/lib/i18n.js +780 -0
  57. package/lib/jobs.js +181 -0
  58. package/lib/lazy-require.js +48 -0
  59. package/lib/log-stream-local.js +137 -0
  60. package/lib/log-stream-webhook.js +170 -0
  61. package/lib/log-stream.js +211 -0
  62. package/lib/log.js +355 -0
  63. package/lib/mail-bounce.js +507 -0
  64. package/lib/mail.js +701 -0
  65. package/lib/metrics.js +647 -0
  66. package/lib/middleware/api-encrypt.js +553 -0
  67. package/lib/middleware/attach-user.js +156 -0
  68. package/lib/middleware/body-parser.js +883 -0
  69. package/lib/middleware/bot-guard.js +148 -0
  70. package/lib/middleware/compression.js +436 -0
  71. package/lib/middleware/cors.js +236 -0
  72. package/lib/middleware/csp-nonce.js +332 -0
  73. package/lib/middleware/csrf-protect.js +275 -0
  74. package/lib/middleware/error-handler.js +46 -0
  75. package/lib/middleware/health.js +358 -0
  76. package/lib/middleware/index.js +52 -0
  77. package/lib/middleware/rate-limit.js +319 -0
  78. package/lib/middleware/request-id.js +53 -0
  79. package/lib/middleware/require-auth.js +95 -0
  80. package/lib/middleware/security-headers.js +91 -0
  81. package/lib/migrations.js +353 -0
  82. package/lib/mtls-ca.js +333 -0
  83. package/lib/mtls-engine-default.js +285 -0
  84. package/lib/nonce-store.js +177 -0
  85. package/lib/notify.js +643 -0
  86. package/lib/ntp-check.js +178 -0
  87. package/lib/object-store/azure-blob.js +467 -0
  88. package/lib/object-store/gcs.js +469 -0
  89. package/lib/object-store/http-put.js +153 -0
  90. package/lib/object-store/index.js +140 -0
  91. package/lib/object-store/local.js +163 -0
  92. package/lib/object-store/retry.js +15 -0
  93. package/lib/object-store/sigv4.js +535 -0
  94. package/lib/observability.js +114 -0
  95. package/lib/pagination.js +371 -0
  96. package/lib/parsers/index.js +64 -0
  97. package/lib/parsers/safe-csv.js +224 -0
  98. package/lib/parsers/safe-env.js +614 -0
  99. package/lib/parsers/safe-toml.js +745 -0
  100. package/lib/parsers/safe-xml.js +379 -0
  101. package/lib/parsers/safe-yaml.js +977 -0
  102. package/lib/permissions.js +430 -0
  103. package/lib/pqc-agent.js +85 -0
  104. package/lib/pqc-gate.js +266 -0
  105. package/lib/protocol-dispatcher.js +144 -0
  106. package/lib/queue-local.js +327 -0
  107. package/lib/queue.js +430 -0
  108. package/lib/redact.js +192 -0
  109. package/lib/render.js +193 -0
  110. package/lib/request-helpers.js +178 -0
  111. package/lib/restore-bundle.js +239 -0
  112. package/lib/restore-rollback.js +254 -0
  113. package/lib/restore.js +301 -0
  114. package/lib/retry.js +329 -0
  115. package/lib/router.js +437 -0
  116. package/lib/safe-async.js +520 -0
  117. package/lib/safe-buffer.js +162 -0
  118. package/lib/safe-json.js +532 -0
  119. package/lib/safe-schema.js +1176 -0
  120. package/lib/safe-sql.js +157 -0
  121. package/lib/safe-url.js +109 -0
  122. package/lib/scheduler.js +680 -0
  123. package/lib/seeders.js +622 -0
  124. package/lib/session.js +304 -0
  125. package/lib/slug.js +243 -0
  126. package/lib/static.js +268 -0
  127. package/lib/storage.js +470 -0
  128. package/lib/subject.js +281 -0
  129. package/lib/template.js +781 -0
  130. package/lib/testing.js +621 -0
  131. package/lib/totp.js +285 -0
  132. package/lib/tracing.js +484 -0
  133. package/lib/validate-opts.js +56 -0
  134. package/lib/vault/index.js +299 -0
  135. package/lib/vault/passphrase-ops.js +311 -0
  136. package/lib/vault/passphrase-source.js +198 -0
  137. package/lib/vault/rotate.js +761 -0
  138. package/lib/vault/wrap.js +289 -0
  139. package/lib/vendor/MANIFEST.json +84 -0
  140. package/lib/vendor/argon2/argon2.cjs +466 -0
  141. package/lib/vendor/argon2/argon2.d.cts +62 -0
  142. package/lib/vendor/argon2/package.json +1 -0
  143. package/lib/vendor/argon2/prebuilds/darwin-arm64/argon2.armv8.glibc.node +0 -0
  144. package/lib/vendor/argon2/prebuilds/darwin-x64/argon2.glibc.node +0 -0
  145. package/lib/vendor/argon2/prebuilds/freebsd-arm64/argon2.armv8.glibc.node +0 -0
  146. package/lib/vendor/argon2/prebuilds/freebsd-x64/argon2.glibc.node +0 -0
  147. package/lib/vendor/argon2/prebuilds/linux-arm/argon2.armv7.glibc.node +0 -0
  148. package/lib/vendor/argon2/prebuilds/linux-arm/argon2.armv7.musl.node +0 -0
  149. package/lib/vendor/argon2/prebuilds/linux-arm64/argon2.armv8.glibc.node +0 -0
  150. package/lib/vendor/argon2/prebuilds/linux-arm64/argon2.armv8.musl.node +0 -0
  151. package/lib/vendor/argon2/prebuilds/linux-x64/argon2.glibc.node +0 -0
  152. package/lib/vendor/argon2/prebuilds/linux-x64/argon2.musl.node +0 -0
  153. package/lib/vendor/argon2/prebuilds/win32-x64/argon2.glibc.node +0 -0
  154. package/lib/vendor/noble-ciphers.cjs +9 -0
  155. package/lib/vendor/pki.cjs +181 -0
  156. package/lib/vendor/simplewebauthn-server.cjs +328 -0
  157. package/lib/webhook.js +632 -0
  158. package/lib/websocket-channels.js +413 -0
  159. package/lib/websocket.js +833 -0
  160. package/package.json +39 -0
@@ -0,0 +1,379 @@
1
+ "use strict";
2
+ /**
3
+ * Security-focused XML parser. Same security defaults blamejs's json
4
+ * parser applies, adapted to XML's threat model.
5
+ *
6
+ * Native XML.parse doesn't exist in Node, and most npm libs (xml2js,
7
+ * fast-xml-parser, sax, libxml) leave at least one of the following to
8
+ * the caller — usually all of them:
9
+ *
10
+ * - XXE (XML External Entity): <!ENTITY foo SYSTEM "file:///etc/passwd">
11
+ * - Billion-laughs entity expansion (DoS)
12
+ * - DTDs (DOCTYPE) opening the door to either of the above
13
+ * - Processing instructions referencing external resources
14
+ * - Unbounded recursion / element count / attribute count
15
+ * - CDATA sections of arbitrary length
16
+ *
17
+ * This parser closes all of them by default. DOCTYPE, external entities,
18
+ * and processing instructions other than '<?xml ?>' are REJECTED — apps
19
+ * that need them are using the wrong parser.
20
+ *
21
+ * Output: a plain JS object. Element with attributes + children:
22
+ * <root id="x"><child>text</child></root>
23
+ * becomes:
24
+ * { root: { '@attrs': { id: 'x' }, child: 'text' } }
25
+ *
26
+ * Multiple children with the same tag become arrays. Mixed content
27
+ * (text + elements interleaved) is preserved as { '#text': '...', ... }.
28
+ *
29
+ * Public API:
30
+ * xml.parse(input, opts?) → JS object | throws SafeXmlError
31
+ * xml.SafeXmlError → error class
32
+ *
33
+ * Defaults:
34
+ * maxBytes: 1 MiB
35
+ * maxDepth: 100
36
+ * maxElements: 10000
37
+ * maxAttributes: 100 per element
38
+ * allowDoctype: false
39
+ * allowProcessing: false (only <?xml version="..."?> is permitted)
40
+ */
41
+
42
+ var C = require("../constants");
43
+ var safeBuffer = require("../safe-buffer");
44
+ var { FrameworkError } = require("../framework-error");
45
+
46
+ class SafeXmlError extends FrameworkError {
47
+ constructor(message, code, position) {
48
+ super(message);
49
+ this.name = "SafeXmlError";
50
+ this.code = code || "xml/invalid";
51
+ this.position = position || null;
52
+ this.isSafeXmlError = true;
53
+ }
54
+ }
55
+
56
+ var DEFAULTS = {
57
+ maxBytes: C.BYTES.mib(1),
58
+ maxDepth: 100,
59
+ maxElements: 10000,
60
+ maxAttributes: 100,
61
+ allowDoctype: false,
62
+ allowProcessing: false, // <?...?> other than the xml decl
63
+ };
64
+
65
+ var ABSOLUTE_MAX_BYTES = C.BYTES.mib(64);
66
+ var ABSOLUTE_MAX_DEPTH = 1000;
67
+ var ABSOLUTE_MAX_ELEMENTS = 1000000;
68
+
69
+ // XML built-in entities (the ONLY entities allowed)
70
+ var BUILT_IN_ENTITIES = { lt: "<", gt: ">", amp: "&", quot: "\"", apos: "'" };
71
+
72
+ function _capInt(value, defaultValue, ceiling) {
73
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return defaultValue;
74
+ return Math.min(Math.floor(value), ceiling);
75
+ }
76
+
77
+ function parse(input, opts) {
78
+ opts = opts || {};
79
+
80
+ var maxBytes = _capInt(opts.maxBytes, DEFAULTS.maxBytes, ABSOLUTE_MAX_BYTES);
81
+ var maxDepth = _capInt(opts.maxDepth, DEFAULTS.maxDepth, ABSOLUTE_MAX_DEPTH);
82
+ var maxElements = _capInt(opts.maxElements, DEFAULTS.maxElements, ABSOLUTE_MAX_ELEMENTS);
83
+ var maxAttrs = _capInt(opts.maxAttributes, DEFAULTS.maxAttributes, 1000);
84
+ var allowDoctype = !!opts.allowDoctype;
85
+ var allowProcessing = !!opts.allowProcessing;
86
+
87
+ input = safeBuffer.normalizeText(input, {
88
+ maxBytes: maxBytes,
89
+ errorClass: SafeXmlError,
90
+ typeCode: "xml/wrong-input-type",
91
+ sizeCode: "xml/too-large",
92
+ });
93
+
94
+ var pos = 0;
95
+ var len = input.length;
96
+ var elementCount = 0;
97
+
98
+ function _err(msg, code) {
99
+ return new SafeXmlError(msg + " at position " + pos, code || "xml/invalid", pos);
100
+ }
101
+
102
+ // Skip whitespace
103
+ function skipWs() {
104
+ while (pos < len) {
105
+ var c = input.charCodeAt(pos);
106
+ if (c === 0x20 || c === 0x09 || c === 0x0A || c === 0x0D) pos += 1;
107
+ else break;
108
+ }
109
+ }
110
+
111
+ function expectChar(ch) {
112
+ if (input.charAt(pos) !== ch) {
113
+ throw _err("expected '" + ch + "'");
114
+ }
115
+ pos += 1;
116
+ }
117
+
118
+ // Decode &amp; / &lt; / etc. — ONLY the 5 built-ins + numeric character refs.
119
+ // Custom entities are forbidden by design (XXE protection).
120
+ function decodeEntities(s) {
121
+ var out = "";
122
+ var i = 0;
123
+ while (i < s.length) {
124
+ var ch = s.charAt(i);
125
+ if (ch !== "&") { out += ch; i += 1; continue; }
126
+ var end = s.indexOf(";", i);
127
+ if (end < 0) throw _err("unterminated entity reference", "xml/bad-entity");
128
+ var name = s.substring(i + 1, end);
129
+ if (name.charAt(0) === "#") {
130
+ // Numeric character reference
131
+ var code;
132
+ if (name.charAt(1) === "x" || name.charAt(1) === "X") {
133
+ code = parseInt(name.substring(2), 16);
134
+ } else {
135
+ code = parseInt(name.substring(1), 10);
136
+ }
137
+ if (!Number.isFinite(code) || code < 0 || code > 0x10FFFF) {
138
+ throw _err("invalid numeric character reference", "xml/bad-entity");
139
+ }
140
+ // XML 1.0 forbids most C0 control chars
141
+ if ((code < 0x20 && code !== 0x09 && code !== 0x0A && code !== 0x0D) || (code >= 0xD800 && code <= 0xDFFF)) {
142
+ throw _err("character reference points to forbidden codepoint", "xml/bad-entity");
143
+ }
144
+ out += String.fromCodePoint(code);
145
+ i = end + 1;
146
+ } else if (BUILT_IN_ENTITIES[name] !== undefined) {
147
+ out += BUILT_IN_ENTITIES[name];
148
+ i = end + 1;
149
+ } else {
150
+ throw _err("unknown entity '" + name + "' (custom entities forbidden — XXE protection)", "xml/external-entity");
151
+ }
152
+ }
153
+ return out;
154
+ }
155
+
156
+ // Parse a name (element / attribute name)
157
+ function parseName() {
158
+ var start = pos;
159
+ while (pos < len) {
160
+ var c = input.charCodeAt(pos);
161
+ // XML name characters — simplified subset (ASCII letters, digits, '-', '_', '.', ':')
162
+ if ((c >= 0x41 && c <= 0x5A) || (c >= 0x61 && c <= 0x7A) ||
163
+ (c >= 0x30 && c <= 0x39) ||
164
+ c === 0x2D || c === 0x5F || c === 0x2E || c === 0x3A ||
165
+ (c >= 0x80 && c <= 0xFFFF)) {
166
+ pos += 1;
167
+ } else break;
168
+ }
169
+ if (pos === start) throw _err("expected name", "xml/bad-name");
170
+ return input.substring(start, pos);
171
+ }
172
+
173
+ // Parse an attribute value (single- or double-quoted)
174
+ function parseAttrValue() {
175
+ var quote = input.charAt(pos);
176
+ if (quote !== "\"" && quote !== "'") throw _err("expected quoted attribute value", "xml/bad-attr");
177
+ pos += 1;
178
+ var start = pos;
179
+ while (pos < len && input.charAt(pos) !== quote) {
180
+ if (input.charAt(pos) === "<") throw _err("'<' not allowed in attribute value", "xml/bad-attr");
181
+ pos += 1;
182
+ }
183
+ if (pos >= len) throw _err("unterminated attribute value", "xml/bad-attr");
184
+ var raw = input.substring(start, pos);
185
+ pos += 1;
186
+ return decodeEntities(raw);
187
+ }
188
+
189
+ // Top-level: optional XML declaration, optional DOCTYPE (rejected), root element
190
+ function parseDocument() {
191
+ skipWs();
192
+ // XML declaration <?xml version="1.0" ... ?>
193
+ if (input.startsWith("<?xml", pos)) {
194
+ var declEnd = input.indexOf("?>", pos);
195
+ if (declEnd < 0) throw _err("unterminated XML declaration", "xml/bad-decl");
196
+ pos = declEnd + 2;
197
+ }
198
+ skipWs();
199
+ // DOCTYPE — rejected by default (XXE / billion-laughs vector)
200
+ if (input.startsWith("<!DOCTYPE", pos)) {
201
+ if (!allowDoctype) {
202
+ throw _err("DOCTYPE declarations are forbidden (XXE protection)", "xml/doctype");
203
+ }
204
+ // If allowed (operator opt-in), skip past the DOCTYPE without parsing internal subset
205
+ var doctypeEnd = pos;
206
+ var depth = 0;
207
+ while (doctypeEnd < len) {
208
+ var c = input.charAt(doctypeEnd);
209
+ if (c === "[") depth += 1;
210
+ else if (c === "]") depth -= 1;
211
+ else if (c === ">" && depth === 0) break;
212
+ doctypeEnd += 1;
213
+ }
214
+ pos = doctypeEnd + 1;
215
+ }
216
+ skipWs();
217
+
218
+ // Comments / processing instructions before root
219
+ while (input.startsWith("<!--", pos) || input.startsWith("<?", pos)) {
220
+ if (input.startsWith("<!--", pos)) {
221
+ var commentEnd = input.indexOf("-->", pos);
222
+ if (commentEnd < 0) throw _err("unterminated comment", "xml/bad-comment");
223
+ pos = commentEnd + 3;
224
+ } else {
225
+ // Processing instruction
226
+ if (!allowProcessing) {
227
+ throw _err("processing instructions are forbidden (allowProcessing: true to permit)", "xml/processing");
228
+ }
229
+ var piEnd = input.indexOf("?>", pos);
230
+ if (piEnd < 0) throw _err("unterminated processing instruction", "xml/bad-pi");
231
+ pos = piEnd + 2;
232
+ }
233
+ skipWs();
234
+ }
235
+
236
+ if (input.charAt(pos) !== "<") throw _err("expected root element", "xml/no-root");
237
+ var root = parseElement(0);
238
+ skipWs();
239
+ if (pos !== len) {
240
+ // Trailing comments / whitespace OK; otherwise reject extra content
241
+ while (pos < len && input.startsWith("<!--", pos)) {
242
+ var ce = input.indexOf("-->", pos);
243
+ if (ce < 0) throw _err("unterminated trailing comment", "xml/bad-comment");
244
+ pos = ce + 3;
245
+ skipWs();
246
+ }
247
+ if (pos !== len) throw _err("unexpected content after root element", "xml/extra-content");
248
+ }
249
+ return root;
250
+ }
251
+
252
+ function parseElement(depth) {
253
+ if (depth > maxDepth) throw _err("nesting exceeds maxDepth", "xml/too-deep");
254
+ elementCount += 1;
255
+ if (elementCount > maxElements) throw _err("element count exceeds maxElements", "xml/too-many-elements");
256
+
257
+ expectChar("<");
258
+ var name = parseName();
259
+ var attrs = {};
260
+ var attrCount = 0;
261
+
262
+ while (pos < len) {
263
+ // Look for attribute, '/>', or '>'
264
+ var c = input.charAt(pos);
265
+ if (c === "/") { pos += 1; expectChar(">"); return _wrap(name, attrs, []); }
266
+ if (c === ">") { pos += 1; break; }
267
+ if (c === " " || c === "\t" || c === "\n" || c === "\r") { pos += 1; continue; }
268
+
269
+ // Attribute
270
+ attrCount += 1;
271
+ if (attrCount > maxAttrs) throw _err("attribute count exceeds maxAttributes", "xml/too-many-attrs");
272
+ var attrName = parseName();
273
+ skipWs();
274
+ expectChar("=");
275
+ skipWs();
276
+ var attrValue = parseAttrValue();
277
+ if (attrs[attrName] !== undefined) {
278
+ throw _err("duplicate attribute '" + attrName + "'", "xml/duplicate-attr");
279
+ }
280
+ attrs[attrName] = attrValue;
281
+ }
282
+
283
+ // Children + text
284
+ var children = [];
285
+ while (pos < len) {
286
+ if (input.startsWith("</", pos)) {
287
+ pos += 2;
288
+ var endName = parseName();
289
+ if (endName !== name) throw _err("mismatched end tag </" + endName + "> for <" + name + ">", "xml/mismatched-tag");
290
+ skipWs();
291
+ expectChar(">");
292
+ return _wrap(name, attrs, children);
293
+ }
294
+ if (input.startsWith("<!--", pos)) {
295
+ var commentEnd = input.indexOf("-->", pos);
296
+ if (commentEnd < 0) throw _err("unterminated comment", "xml/bad-comment");
297
+ pos = commentEnd + 3;
298
+ continue;
299
+ }
300
+ if (input.startsWith("<![CDATA[", pos)) {
301
+ var cdataEnd = input.indexOf("]]>", pos + 9);
302
+ if (cdataEnd < 0) throw _err("unterminated CDATA", "xml/bad-cdata");
303
+ children.push({ kind: "text", value: input.substring(pos + 9, cdataEnd) });
304
+ pos = cdataEnd + 3;
305
+ continue;
306
+ }
307
+ if (input.startsWith("<?", pos)) {
308
+ if (!allowProcessing) throw _err("processing instructions forbidden", "xml/processing");
309
+ var piEnd = input.indexOf("?>", pos);
310
+ if (piEnd < 0) throw _err("unterminated PI", "xml/bad-pi");
311
+ pos = piEnd + 2;
312
+ continue;
313
+ }
314
+ if (input.charAt(pos) === "<") {
315
+ children.push({ kind: "element", value: parseElement(depth + 1) });
316
+ continue;
317
+ }
318
+ // Text
319
+ var textStart = pos;
320
+ while (pos < len && input.charAt(pos) !== "<") pos += 1;
321
+ var rawText = input.substring(textStart, pos);
322
+ if (rawText.length > 0) {
323
+ children.push({ kind: "text", value: decodeEntities(rawText) });
324
+ }
325
+ }
326
+ throw _err("unexpected end of input inside <" + name + ">", "xml/truncated");
327
+ }
328
+
329
+ // Wrap parsed element into the JS-object shape.
330
+ function _wrap(name, attrs, children) {
331
+ var elementChildren = children.filter(function (c) { return c.kind === "element"; });
332
+ var textParts = children.filter(function (c) { return c.kind === "text"; }).map(function (c) { return c.value; });
333
+ var hasAttrs = Object.keys(attrs).length > 0;
334
+
335
+ // Self-closing or empty element with no attrs → empty string
336
+ if (children.length === 0 && !hasAttrs) {
337
+ return _make(name, "");
338
+ }
339
+ if (elementChildren.length === 0 && !hasAttrs) {
340
+ // Pure-text element → string
341
+ return _make(name, textParts.join("").trim() === "" ? textParts.join("") : textParts.join(""));
342
+ }
343
+ // Mixed / attributed element → object
344
+ var obj = {};
345
+ if (hasAttrs) obj["@attrs"] = attrs;
346
+ var grouped = {};
347
+ for (var i = 0; i < elementChildren.length; i++) {
348
+ var childWrap = elementChildren[i].value;
349
+ var childName = Object.keys(childWrap)[0];
350
+ var childVal = childWrap[childName];
351
+ if (grouped[childName] === undefined) {
352
+ grouped[childName] = childVal;
353
+ } else if (Array.isArray(grouped[childName])) {
354
+ grouped[childName].push(childVal);
355
+ } else {
356
+ grouped[childName] = [grouped[childName], childVal];
357
+ }
358
+ }
359
+ Object.assign(obj, grouped);
360
+ var combinedText = textParts.join("").replace(/\s+/g, " ").trim();
361
+ if (combinedText.length > 0) obj["#text"] = combinedText;
362
+ return _make(name, obj);
363
+ }
364
+
365
+ function _make(name, value) {
366
+ var out = {};
367
+ out[name] = value;
368
+ return out;
369
+ }
370
+
371
+ return parseDocument();
372
+ }
373
+
374
+ module.exports = {
375
+ parse: parse,
376
+ SafeXmlError: SafeXmlError,
377
+ DEFAULTS: DEFAULTS,
378
+ BUILT_IN_ENTITIES: BUILT_IN_ENTITIES,
379
+ };