s3mini 0.2.0 → 0.3.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/src/utils.ts CHANGED
@@ -17,6 +17,10 @@ export const hash = (content: string | Buffer): string => {
17
17
  return _createHash('sha256').update(content).digest('hex') as string;
18
18
  };
19
19
 
20
+ export const md5base64 = (data: string | Buffer): string => {
21
+ return _createHash('md5').update(data).digest('base64') as string;
22
+ };
23
+
20
24
  /**
21
25
  * Compute HMAC-SHA-256 of arbitrary data and return a hex string.
22
26
  * @param {string|Buffer} key – secret key
@@ -53,6 +57,20 @@ const entityMap = {
53
57
  '&': '&',
54
58
  } as const;
55
59
 
60
+ /**
61
+ * Escape special characters for XML
62
+ * @param value String to escape
63
+ * @returns XML-escaped string
64
+ */
65
+ export const escapeXml = (value: string): string => {
66
+ return value
67
+ .replace(/&/g, '&')
68
+ .replace(/</g, '&lt;')
69
+ .replace(/>/g, '&gt;')
70
+ .replace(/"/g, '&quot;')
71
+ .replace(/'/g, '&apos;');
72
+ };
73
+
56
74
  const unescapeXml = (value: string): string =>
57
75
  value.replace(/&(quot|apos|lt|gt|amp);/g, m => entityMap[m as keyof typeof entityMap] ?? m);
58
76
 
@@ -62,31 +80,35 @@ const unescapeXml = (value: string): string =>
62
80
  * @param input raw XML string
63
81
  * @returns string for leaf nodes, otherwise a map of children
64
82
  */
83
+
65
84
  export const parseXml = (input: string): XmlValue => {
66
- const RE_TAG = /<(\w)([-\w]+)(?:\/|[^>]*>((?:(?!<\1)[\s\S])*)<\/\1\2)>/gm;
85
+ const xmlContent = input.replace(/<\?xml[^?]*\?>\s*/, '');
86
+ const RE_TAG = /<([A-Za-z_][\w\-.]*)[^>]*>([\s\S]*?)<\/\1>/gm;
67
87
  const result: XmlMap = {}; // strong type, no `any`
68
88
  let match: RegExpExecArray | null;
69
89
 
70
- while ((match = RE_TAG.exec(input)) !== null) {
71
- const [, prefix = '', key, inner] = match;
72
- const fullKey = `${prefix.toLowerCase()}${key}`;
73
- const node: XmlValue = inner ? parseXml(inner) : '';
74
-
75
- const current = result[fullKey];
90
+ while ((match = RE_TAG.exec(xmlContent)) !== null) {
91
+ const tagName = match[1];
92
+ const innerContent = match[2];
93
+ const node: XmlValue = innerContent ? parseXml(innerContent) : unescapeXml(innerContent?.trim() || '');
94
+ if (!tagName) {
95
+ continue;
96
+ }
97
+ const current = result[tagName];
76
98
  if (current === undefined) {
77
- // first occurrence
78
- result[fullKey] = node;
99
+ // First occurrence
100
+ result[tagName] = node;
79
101
  } else if (Array.isArray(current)) {
80
- // already an array
102
+ // Already an array
81
103
  current.push(node);
82
104
  } else {
83
- // promote to array on the second occurrence
84
- result[fullKey] = [current, node];
105
+ // Promote to array on the second occurrence
106
+ result[tagName] = [current, node];
85
107
  }
86
108
  }
87
109
 
88
110
  // No child tags? — return the text, after entity decode
89
- return Object.keys(result).length > 0 ? result : unescapeXml(input);
111
+ return Object.keys(result).length > 0 ? result : unescapeXml(xmlContent.trim());
90
112
  };
91
113
 
92
114
  /**