s3mini 0.2.0 → 0.4.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/consts.ts CHANGED
@@ -15,9 +15,9 @@ export const DEFAULT_REQUEST_SIZE_IN_BYTES = 8 * 1024 * 1024;
15
15
  export const HEADER_AMZ_CONTENT_SHA256 = 'x-amz-content-sha256';
16
16
  export const HEADER_AMZ_DATE = 'x-amz-date';
17
17
  export const HEADER_HOST = 'host';
18
- export const HEADER_AUTHORIZATION = 'Authorization';
19
- export const HEADER_CONTENT_TYPE = 'Content-Type';
20
- export const HEADER_CONTENT_LENGTH = 'Content-Length';
18
+ export const HEADER_AUTHORIZATION = 'authorization';
19
+ export const HEADER_CONTENT_TYPE = 'content-type';
20
+ export const HEADER_CONTENT_LENGTH = 'content-length';
21
21
  export const HEADER_ETAG = 'etag';
22
22
  export const HEADER_LAST_MODIFIED = 'last-modified';
23
23
 
package/src/index.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- import { s3mini } from './S3.js';
3
+ import { S3mini, s3mini } from './S3.js';
4
4
  import { sanitizeETag, runInBatches } from './utils.js';
5
5
 
6
6
  // Export the S3 class as default export and named export
7
- export { s3mini, sanitizeETag, runInBatches };
8
- export default s3mini;
7
+ export { S3mini, s3mini, sanitizeETag, runInBatches };
8
+ export default S3mini;
9
9
 
10
10
  // Re-export types
11
11
  export type {
package/src/types.ts CHANGED
@@ -8,6 +8,12 @@ export interface S3Config {
8
8
  logger?: Logger;
9
9
  }
10
10
 
11
+ export interface SSECHeaders {
12
+ 'x-amz-server-side-encryption-customer-algorithm': string;
13
+ 'x-amz-server-side-encryption-customer-key': string;
14
+ 'x-amz-server-side-encryption-customer-key-md5': string;
15
+ }
16
+
11
17
  export interface Crypto {
12
18
  createHmac: (
13
19
  algorithm: string,
@@ -33,6 +39,14 @@ export interface UploadPart {
33
39
  etag: string;
34
40
  }
35
41
 
42
+ export interface ListObject {
43
+ Key: string;
44
+ Size: number;
45
+ LastModified: Date;
46
+ ETag: string;
47
+ StorageClass: string;
48
+ }
49
+
36
50
  export interface CompleteMultipartUploadResult {
37
51
  location: string;
38
52
  bucket: string;
package/src/utils.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  'use strict';
2
-
3
2
  import type { Crypto, XmlValue, XmlMap, ListBucketResponse, ErrorWithCode } from './types.js';
4
3
  declare const crypto: Crypto;
5
4
 
@@ -17,6 +16,10 @@ export const hash = (content: string | Buffer): string => {
17
16
  return _createHash('sha256').update(content).digest('hex') as string;
18
17
  };
19
18
 
19
+ export const md5base64 = (data: string | Buffer): string => {
20
+ return _createHash('md5').update(data).digest('base64') as string;
21
+ };
22
+
20
23
  /**
21
24
  * Compute HMAC-SHA-256 of arbitrary data and return a hex string.
22
25
  * @param {string|Buffer} key – secret key
@@ -53,6 +56,20 @@ const entityMap = {
53
56
  '&': '&',
54
57
  } as const;
55
58
 
59
+ /**
60
+ * Escape special characters for XML
61
+ * @param value String to escape
62
+ * @returns XML-escaped string
63
+ */
64
+ export const escapeXml = (value: string): string => {
65
+ return value
66
+ .replace(/&/g, '&')
67
+ .replace(/</g, '&lt;')
68
+ .replace(/>/g, '&gt;')
69
+ .replace(/"/g, '&quot;')
70
+ .replace(/'/g, '&apos;');
71
+ };
72
+
56
73
  const unescapeXml = (value: string): string =>
57
74
  value.replace(/&(quot|apos|lt|gt|amp);/g, m => entityMap[m as keyof typeof entityMap] ?? m);
58
75
 
@@ -62,31 +79,35 @@ const unescapeXml = (value: string): string =>
62
79
  * @param input raw XML string
63
80
  * @returns string for leaf nodes, otherwise a map of children
64
81
  */
82
+
65
83
  export const parseXml = (input: string): XmlValue => {
66
- const RE_TAG = /<(\w)([-\w]+)(?:\/|[^>]*>((?:(?!<\1)[\s\S])*)<\/\1\2)>/gm;
84
+ const xmlContent = input.replace(/<\?xml[^?]*\?>\s*/, '');
85
+ const RE_TAG = /<([A-Za-z_][\w\-.]*)[^>]*>([\s\S]*?)<\/\1>/gm;
67
86
  const result: XmlMap = {}; // strong type, no `any`
68
87
  let match: RegExpExecArray | null;
69
88
 
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];
89
+ while ((match = RE_TAG.exec(xmlContent)) !== null) {
90
+ const tagName = match[1];
91
+ const innerContent = match[2];
92
+ const node: XmlValue = innerContent ? parseXml(innerContent) : unescapeXml(innerContent?.trim() || '');
93
+ if (!tagName) {
94
+ continue;
95
+ }
96
+ const current = result[tagName];
76
97
  if (current === undefined) {
77
- // first occurrence
78
- result[fullKey] = node;
98
+ // First occurrence
99
+ result[tagName] = node;
79
100
  } else if (Array.isArray(current)) {
80
- // already an array
101
+ // Already an array
81
102
  current.push(node);
82
103
  } else {
83
- // promote to array on the second occurrence
84
- result[fullKey] = [current, node];
104
+ // Promote to array on the second occurrence
105
+ result[tagName] = [current, node];
85
106
  }
86
107
  }
87
108
 
88
109
  // No child tags? — return the text, after entity decode
89
- return Object.keys(result).length > 0 ? result : unescapeXml(input);
110
+ return Object.keys(result).length > 0 ? result : unescapeXml(xmlContent.trim());
90
111
  };
91
112
 
92
113
  /**