mbkbucket 4.0.0 → 4.0.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.
@@ -1,14 +1,14 @@
1
- /**
2
- * MBKBucket - Storage Core
3
- * Copyright (c) 2026 Muhammad Bin Khalid, MBKTech.org and contributors
4
- * Licensed under the MIT License.
5
- */
6
-
7
- export { StorageProvider } from "./storage-provider.js";
8
- export { S3StorageProvider } from "./s3-provider.js";
9
- export { GoogleDriveStorageProvider } from "./gdrive-provider.js";
10
- export { StorageFile, StorageFolder, StorageListResult } from "./models.js";
11
- export { StorageError, StorageNotFoundError, StorageAccessDeniedError, StorageConflictError, StorageValidationError, StorageConfigError, StorageQuotaExceededError, StorageUnsupportedOperationError } from "./errors.js";
12
- export { PathStrategy } from "./path-strategy.js";
13
- export { getGoogleAuthUrl, exchangeCodeForTokens, refreshGoogleAccessToken, getServiceAccountAccessToken, GOOGLE_DRIVE_SCOPES } from "./gdrive-auth.js";
14
- export { StorageManager, storageManager } from "./storage-manager.js";
1
+ /**
2
+ * MBKBucket - Storage Core
3
+ * Copyright (c) 2026 Muhammad Bin Khalid, MBKTech.org and contributors
4
+ * Licensed under the MIT License.
5
+ */
6
+
7
+ export { StorageProvider } from "./storage-provider.js";
8
+ export { S3StorageProvider } from "./s3-provider.js";
9
+ export { GoogleDriveStorageProvider } from "./gdrive-provider.js";
10
+ export { StorageFile, StorageFolder, StorageListResult } from "./models.js";
11
+ export { StorageError, StorageNotFoundError, StorageAccessDeniedError, StorageConflictError, StorageValidationError, StorageConfigError, StorageQuotaExceededError, StorageUnsupportedOperationError } from "./errors.js";
12
+ export { PathStrategy } from "./path-strategy.js";
13
+ export { getGoogleAuthUrl, exchangeCodeForTokens, refreshGoogleAccessToken, getServiceAccountAccessToken, GOOGLE_DRIVE_SCOPES } from "./gdrive-auth.js";
14
+ export { StorageManager, storageManager } from "./storage-manager.js";
@@ -1,80 +1,80 @@
1
- /**
2
- * MBKBucket - Normalized Storage Data Models
3
- * Copyright (c) 2026 Muhammad Bin Khalid, MBKTech.org and contributors
4
- * Licensed under the MIT License.
5
- */
6
-
7
- import { nowIso } from "#helpers";
8
-
9
- /**
10
- * Standardized file item representation across all providers.
11
- */
12
- export class StorageFile {
13
- /**
14
- * @param {object} props
15
- * @param {string} props.key - Normalized virtual key / path
16
- * @param {number} [props.size=0] - File size in bytes
17
- * @param {Date|string} [props.lastModified] - Last modified timestamp
18
- * @param {string} [props.etag] - Entity tag / checksum
19
- * @param {string} [props.contentType] - MIME content type
20
- * @param {string} [props.storageClass] - Storage tier / provider type
21
- * @param {string} [props.id] - Native provider ID (e.g. Google Drive file ID)
22
- * @param {string} [props.webViewLink] - Web preview link
23
- * @param {string} [props.webContentLink] - Direct download link
24
- * @param {object} [props.metadata={}] - Custom user metadata
25
- */
26
- constructor(props) {
27
- this.Key = props.key || props.Key || '';
28
- this.Size = Number(props.size ?? props.Size ?? 0);
29
- this.LastModified = props.lastModified instanceof Date
30
- ? props.lastModified
31
- : new Date(props.lastModified || props.LastModified || Date.now());
32
- this.ETag = props.etag || props.ETag || '';
33
- this.ContentType = props.contentType || props.ContentType || 'application/octet-stream';
34
- this.StorageClass = props.storageClass || props.StorageClass || 'STANDARD';
35
- if (props.id) this.id = props.id;
36
- if (props.mimeType) this.mimeType = props.mimeType;
37
- if (props.webViewLink) this.webViewLink = props.webViewLink;
38
- if (props.webContentLink) this.webContentLink = props.webContentLink;
39
- if (props.metadata) this.metadata = props.metadata;
40
- }
41
- }
42
-
43
- /**
44
- * Standardized folder representation.
45
- */
46
- export class StorageFolder {
47
- /**
48
- * @param {string} prefix - Folder prefix with trailing slash
49
- * @param {string} [id] - Native provider folder ID
50
- */
51
- constructor(prefix, id) {
52
- this.Prefix = String(prefix || '').endsWith('/') ? String(prefix) : `${prefix}/`;
53
- if (id) this.id = id;
54
- }
55
- }
56
-
57
- /**
58
- * Standardized paginated listing result.
59
- */
60
- export class StorageListResult {
61
- /**
62
- * @param {object} props
63
- * @param {StorageFile[]} [props.contents=[]]
64
- * @param {StorageFolder[]} [props.commonPrefixes=[]]
65
- * @param {string|null} [props.nextToken=null]
66
- * @param {boolean} [props.isTruncated=false]
67
- * @param {number} [props.totalFiles]
68
- */
69
- constructor(props = {}) {
70
- this.Contents = props.contents || props.Contents || [];
71
- this.CommonPrefixes = props.commonPrefixes || props.CommonPrefixes || [];
72
- this.NextContinuationToken = props.nextToken ?? props.NextContinuationToken ?? null;
73
- this.nextToken = this.NextContinuationToken;
74
- this.IsTruncated = Boolean(props.isTruncated ?? props.IsTruncated ?? Boolean(this.NextContinuationToken));
75
- this.hasMore = this.IsTruncated;
76
- this.KeyCount = this.Contents.length;
77
- this.totalFiles = props.totalFiles ?? this.Contents.length;
78
- this.requestedAt = nowIso();
79
- }
80
- }
1
+ /**
2
+ * MBKBucket - Normalized Storage Data Models
3
+ * Copyright (c) 2026 Muhammad Bin Khalid, MBKTech.org and contributors
4
+ * Licensed under the MIT License.
5
+ */
6
+
7
+ import { nowIso } from "#helpers";
8
+
9
+ /**
10
+ * Standardized file item representation across all providers.
11
+ */
12
+ export class StorageFile {
13
+ /**
14
+ * @param {object} props
15
+ * @param {string} props.key - Normalized virtual key / path
16
+ * @param {number} [props.size=0] - File size in bytes
17
+ * @param {Date|string} [props.lastModified] - Last modified timestamp
18
+ * @param {string} [props.etag] - Entity tag / checksum
19
+ * @param {string} [props.contentType] - MIME content type
20
+ * @param {string} [props.storageClass] - Storage tier / provider type
21
+ * @param {string} [props.id] - Native provider ID (e.g. Google Drive file ID)
22
+ * @param {string} [props.webViewLink] - Web preview link
23
+ * @param {string} [props.webContentLink] - Direct download link
24
+ * @param {object} [props.metadata={}] - Custom user metadata
25
+ */
26
+ constructor(props) {
27
+ this.Key = props.key || props.Key || '';
28
+ this.Size = Number(props.size ?? props.Size ?? 0);
29
+ this.LastModified = props.lastModified instanceof Date
30
+ ? props.lastModified
31
+ : new Date(props.lastModified || props.LastModified || Date.now());
32
+ this.ETag = props.etag || props.ETag || '';
33
+ this.ContentType = props.contentType || props.ContentType || 'application/octet-stream';
34
+ this.StorageClass = props.storageClass || props.StorageClass || 'STANDARD';
35
+ if (props.id) this.id = props.id;
36
+ if (props.mimeType) this.mimeType = props.mimeType;
37
+ if (props.webViewLink) this.webViewLink = props.webViewLink;
38
+ if (props.webContentLink) this.webContentLink = props.webContentLink;
39
+ if (props.metadata) this.metadata = props.metadata;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Standardized folder representation.
45
+ */
46
+ export class StorageFolder {
47
+ /**
48
+ * @param {string} prefix - Folder prefix with trailing slash
49
+ * @param {string} [id] - Native provider folder ID
50
+ */
51
+ constructor(prefix, id) {
52
+ this.Prefix = String(prefix || '').endsWith('/') ? String(prefix) : `${prefix}/`;
53
+ if (id) this.id = id;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Standardized paginated listing result.
59
+ */
60
+ export class StorageListResult {
61
+ /**
62
+ * @param {object} props
63
+ * @param {StorageFile[]} [props.contents=[]]
64
+ * @param {StorageFolder[]} [props.commonPrefixes=[]]
65
+ * @param {string|null} [props.nextToken=null]
66
+ * @param {boolean} [props.isTruncated=false]
67
+ * @param {number} [props.totalFiles]
68
+ */
69
+ constructor(props = {}) {
70
+ this.Contents = props.contents || props.Contents || [];
71
+ this.CommonPrefixes = props.commonPrefixes || props.CommonPrefixes || [];
72
+ this.NextContinuationToken = props.nextToken ?? props.NextContinuationToken ?? null;
73
+ this.nextToken = this.NextContinuationToken;
74
+ this.IsTruncated = Boolean(props.isTruncated ?? props.IsTruncated ?? Boolean(this.NextContinuationToken));
75
+ this.hasMore = this.IsTruncated;
76
+ this.KeyCount = this.Contents.length;
77
+ this.totalFiles = props.totalFiles ?? this.Contents.length;
78
+ this.requestedAt = nowIso();
79
+ }
80
+ }
@@ -1,65 +1,65 @@
1
- /**
2
- * MBKBucket - Path & Key Strategy
3
- * Copyright (c) 2026 Muhammad Bin Khalid, MBKTech.org and contributors
4
- * Licensed under the MIT License.
5
- */
6
-
7
- import { mbkautheVar } from "mbkauthe";
8
- import { StorageValidationError } from "./errors.js";
9
- import { trimLeadingSlashes, trimSlashes } from "#helpers";
10
-
11
- const escapeRegExp = value => String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
12
-
13
- export class PathStrategy {
14
- static getAppName() {
15
- const app = mbkautheVar?.APP_NAME;
16
- return typeof app === 'string' && app.trim() ? app.trim() : '';
17
- }
18
-
19
- static isRootModeApp(appName) {
20
- return ['portal', 'mbkbucket'].includes(String(appName || '').toLowerCase());
21
- }
22
-
23
- static validateSafety(str, isKey = false) {
24
- if (isKey && !str) throw new StorageValidationError('Key is required');
25
- if (str && (str.includes('../') || str.includes('..\\') || /\.\.[\\/]/.test(str))) {
26
- throw new StorageValidationError(`Path traversal detected: invalid ${isKey ? 'key' : 'prefix'}`);
27
- }
28
- if (str && /[\x00-\x1f\x7f]/.test(str)) {
29
- throw new StorageValidationError(`Invalid characters in ${isKey ? 'key' : 'prefix'}`);
30
- }
31
- }
32
-
33
- static applyKeyPrefix(key = '') {
34
- if (Array.isArray(key)) key = key.join('/');
35
- this.validateSafety(key, true);
36
-
37
- const app = this.getAppName();
38
- const cleaned = trimLeadingSlashes(key);
39
- if (this.isRootModeApp(app)) return cleaned;
40
- if (!app) throw new StorageValidationError('APP_NAME is not configured; set mbkautheVar.APP_NAME or MBKAUTHE_APP_NAME');
41
-
42
- const appPrefixRegex = new RegExp(`^(?:${escapeRegExp(app)}\/)+`);
43
- return cleaned.startsWith(`${app}/`) ? cleaned.replace(appPrefixRegex, `${app}/`) : `${app}/${cleaned}`;
44
- }
45
-
46
- static applyPrefix(prefix = '') {
47
- if (Array.isArray(prefix)) prefix = prefix.join('/');
48
- this.validateSafety(prefix, false);
49
-
50
- const app = this.getAppName();
51
- const p = trimLeadingSlashes(prefix);
52
- if (this.isRootModeApp(app)) return p;
53
- if (!app) throw new StorageValidationError('APP_NAME is not configured; set mbkautheVar.APP_NAME or MBKAUTHE_APP_NAME');
54
- if (!p || p === app || p === `${app}/`) return p || app;
55
-
56
- return p.startsWith(`${app}/`)
57
- ? p.replace(new RegExp(`^(?:${escapeRegExp(app)}\/)+`), `${app}/`)
58
- : `${app}/${p}`;
59
- }
60
-
61
- static normalizeFolderPrefix(prefix = '') {
62
- const p = this.applyPrefix(trimSlashes(prefix));
63
- return p ? `${p}/` : '';
64
- }
65
- }
1
+ /**
2
+ * MBKBucket - Path & Key Strategy
3
+ * Copyright (c) 2026 Muhammad Bin Khalid, MBKTech.org and contributors
4
+ * Licensed under the MIT License.
5
+ */
6
+
7
+ import { mbkautheVar } from "mbkauthe";
8
+ import { StorageValidationError } from "./errors.js";
9
+ import { trimLeadingSlashes, trimSlashes } from "#helpers";
10
+
11
+ const escapeRegExp = value => String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
12
+
13
+ export class PathStrategy {
14
+ static getAppName() {
15
+ const app = mbkautheVar?.APP_NAME;
16
+ return typeof app === 'string' && app.trim() ? app.trim() : '';
17
+ }
18
+
19
+ static isRootModeApp(appName) {
20
+ return ['portal', 'mbkbucket'].includes(String(appName || '').toLowerCase());
21
+ }
22
+
23
+ static validateSafety(str, isKey = false) {
24
+ if (isKey && !str) throw new StorageValidationError('Key is required');
25
+ if (str && (str.includes('../') || str.includes('..\\') || /\.\.[\\/]/.test(str))) {
26
+ throw new StorageValidationError(`Path traversal detected: invalid ${isKey ? 'key' : 'prefix'}`);
27
+ }
28
+ if (str && /[\x00-\x1f\x7f]/.test(str)) {
29
+ throw new StorageValidationError(`Invalid characters in ${isKey ? 'key' : 'prefix'}`);
30
+ }
31
+ }
32
+
33
+ static applyKeyPrefix(key = '') {
34
+ if (Array.isArray(key)) key = key.join('/');
35
+ this.validateSafety(key, true);
36
+
37
+ const app = this.getAppName();
38
+ const cleaned = trimLeadingSlashes(key);
39
+ if (this.isRootModeApp(app)) return cleaned;
40
+ if (!app) throw new StorageValidationError('APP_NAME is not configured; set mbkautheVar.APP_NAME or MBKAUTHE_APP_NAME');
41
+
42
+ const appPrefixRegex = new RegExp(`^(?:${escapeRegExp(app)}\/)+`);
43
+ return cleaned.startsWith(`${app}/`) ? cleaned.replace(appPrefixRegex, `${app}/`) : `${app}/${cleaned}`;
44
+ }
45
+
46
+ static applyPrefix(prefix = '') {
47
+ if (Array.isArray(prefix)) prefix = prefix.join('/');
48
+ this.validateSafety(prefix, false);
49
+
50
+ const app = this.getAppName();
51
+ const p = trimLeadingSlashes(prefix);
52
+ if (this.isRootModeApp(app)) return p;
53
+ if (!app) throw new StorageValidationError('APP_NAME is not configured; set mbkautheVar.APP_NAME or MBKAUTHE_APP_NAME');
54
+ if (!p || p === app || p === `${app}/`) return p || app;
55
+
56
+ return p.startsWith(`${app}/`)
57
+ ? p.replace(new RegExp(`^(?:${escapeRegExp(app)}\/)+`), `${app}/`)
58
+ : `${app}/${p}`;
59
+ }
60
+
61
+ static normalizeFolderPrefix(prefix = '') {
62
+ const p = this.applyPrefix(trimSlashes(prefix));
63
+ return p ? `${p}/` : '';
64
+ }
65
+ }