mbkbucket 1.5.1 → 2.1.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.
@@ -1,7 +1,10 @@
1
1
  import { createRequire } from "module";
2
2
  import dotenv from "dotenv";
3
+ import { parseAndValidateMbkbucketVar, parseAndValidateBucketConnection } from "./validation.js";
4
+ import { createLogger } from "../debug.js";
3
5
 
4
6
  dotenv.config();
7
+ const debugConfig = createLogger('config');
5
8
 
6
9
  // Load package.json from mbkbucket package (not parent project)
7
10
  const require = createRequire(import.meta.url);
@@ -27,19 +30,31 @@ try {
27
30
  }
28
31
  }
29
32
 
33
+ function compareVersions(a, b) {
34
+ const left = String(a || '').split(/[.-]/).map((part) => parseInt(part, 10) || 0);
35
+ const right = String(b || '').split(/[.-]/).map((part) => parseInt(part, 10) || 0);
36
+ const length = Math.max(left.length, right.length);
37
+
38
+ for (let i = 0; i < length; i += 1) {
39
+ const diff = (left[i] || 0) - (right[i] || 0);
40
+ if (diff !== 0) return diff;
41
+ }
42
+ return 0;
43
+ }
44
+
30
45
  // Fetch latest version from GitHub
31
46
  async function getLatestVersion() {
32
47
  try {
33
48
  const response = await fetch('https://raw.githubusercontent.com/MIbnEKhalid/mbkbucket/master/package.json');
34
49
  if (!response.ok) {
35
- console.error(`[mbkbucket] GitHub API responded with status ${response.status}`);
50
+ debugConfig('GitHub API responded with status %s', response.status);
36
51
  return "0.0.0";
37
52
  }
38
53
 
39
54
  const latestPackageJson = await response.json();
40
55
  return latestPackageJson.version;
41
56
  } catch (error) {
42
- console.error('[mbkbucket] Error fetching latest version from GitHub:', error);
57
+ debugConfig('Error fetching latest version from GitHub: %s', error.message);
43
58
  return null;
44
59
  }
45
60
  }
@@ -48,90 +63,30 @@ async function getLatestVersion() {
48
63
  async function checkVersion() {
49
64
  try {
50
65
  const latestVersion = await getLatestVersion();
51
- if (latestVersion && latestVersion !== packageJson.version) {
52
- console.warn(`[mbkbucket] Current version (${packageJson.version}) is outdated. Latest version: ${latestVersion}. Consider updating mbkbucket.`);
66
+ if (latestVersion && compareVersions(latestVersion, packageJson.version) > 0) {
67
+ debugConfig('Current version (%s) is outdated. Latest version: %s.', packageJson.version, latestVersion);
68
+ } else if (latestVersion && compareVersions(latestVersion, packageJson.version) < 0) {
69
+ debugConfig('Current version (%s) is newer than published version: %s.', packageJson.version, latestVersion);
53
70
  } else if (latestVersion) {
54
- console.info(`[mbkbucket] Running latest version (${packageJson.version}).`);
71
+ debugConfig('Running latest version (%s).', packageJson.version);
55
72
  }
56
73
  } catch (error) {
57
- console.warn(`[mbkbucket] Failed to check for updates: ${error.message}`);
74
+ debugConfig('Failed to check for updates: %s', error.message);
58
75
  }
59
76
  }
60
77
 
61
78
  // Comprehensive validation function
62
79
  function validateConfiguration() {
63
- const errors = [];
64
-
65
- // Parse and validate mbkbucketVar
66
- let mbkbucketVar;
67
80
  try {
81
+ const parsed = parseAndValidateMbkbucketVar(process.env.mbkbucketVar);
68
82
  if (!process.env.mbkbucketVar) {
69
- // If mbkbucketVar is not defined, use empty object (defaults will be applied later)
70
- mbkbucketVar = {};
71
- console.log(`[mbkbucket] mbkbucketVar not defined, using defaults`);
72
- } else {
73
- mbkbucketVar = JSON.parse(process.env.mbkbucketVar);
83
+ debugConfig('mbkbucketVar not defined, using defaults');
74
84
  }
85
+ debugConfig('Configuration loaded');
86
+ return parsed;
75
87
  } catch (error) {
76
- errors.push("Invalid JSON in process.env.mbkbucketVar");
77
- throw new Error(`[mbkbucket] Configuration Error:\n - ${errors.join('\n - ')}`);
78
- }
79
-
80
- if (!mbkbucketVar || typeof mbkbucketVar !== 'object') {
81
- errors.push("mbkbucketVar must be a valid object");
82
- throw new Error(`[mbkbucket] Configuration Error:\n - ${errors.join('\n - ')}`);
88
+ throw new Error(`[mbkbucket] Configuration Validation Failed:\n - ${error.message}`);
83
89
  }
84
-
85
- // Apply defaults for optional keys (booleans now)
86
- const defaults = {
87
- p_view_inline: true,
88
- publiView_enabled: false
89
- };
90
-
91
- const usedDefaults = [];
92
- Object.entries(defaults).forEach(([key, defaultVal]) => {
93
- const current = mbkbucketVar[key];
94
- if (current === undefined || (typeof current === 'string' && current.trim() === '')) {
95
- mbkbucketVar[key] = defaultVal;
96
- usedDefaults.push(key);
97
- }
98
- });
99
-
100
- // Helper to normalize various truthy/falsy representations
101
- function normalizeBool(val) {
102
- if (typeof val === 'boolean') return val;
103
- if (typeof val === 'string') {
104
- const norm = val.trim().toLowerCase();
105
- if (['true', 't', '1', 'yes', 'y', 'on'].includes(norm)) return true;
106
- if (['false', 'f', '0', 'no', 'n', 'off'].includes(norm)) return false;
107
- return val; // return original so later validation will flag it
108
- }
109
- return val;
110
- }
111
-
112
- // Normalize boolean-like values early so validation works on cleaned values;
113
- // results are actual booleans.
114
- ['p_view_inline', 'publiView_enabled'].forEach(k => {
115
- if (k in mbkbucketVar) {
116
- mbkbucketVar[k] = normalizeBool(mbkbucketVar[k]);
117
- }
118
- });
119
-
120
- // Validate that applied values are proper booleans (after defaults and normalization)
121
- // Note: p_view_inline and publiView_enabled are optional - defaults are applied above
122
- ['p_view_inline', 'publiView_enabled'].forEach(key => {
123
- if (mbkbucketVar[key] !== undefined && typeof mbkbucketVar[key] !== 'boolean') {
124
- errors.push(`mbkbucketVar.${key} must be a boolean if provided`);
125
- }
126
- });
127
-
128
- if (errors.length > 0) {
129
- throw new Error(`[mbkbucket] Configuration Validation Failed:\n - ${errors.join('\n - ')}`);
130
- }
131
-
132
- const summary = usedDefaults.length > 0 ? ` (defaults applied: ${usedDefaults.join(', ')})` : '';
133
- console.log(`[mbkbucket] Configuration loaded${summary}`);
134
- return mbkbucketVar;
135
90
  }
136
91
 
137
92
  // Validate BucketConnection configuration
@@ -144,30 +99,10 @@ function validateBucketConnection() {
144
99
  }
145
100
 
146
101
  try {
147
- const bucketConfig = JSON.parse(process.env.BucketConnection);
148
-
149
- if (!bucketConfig || typeof bucketConfig !== 'object' || Object.keys(bucketConfig).length === 0) {
150
- throw new Error('BucketConnection must be a non-empty object');
151
- }
152
-
153
- // Validate each bucket configuration
154
- const bucketNames = Object.keys(bucketConfig);
155
- for (const bucketName of bucketNames) {
156
- const config = bucketConfig[bucketName];
157
-
158
- if (!config || typeof config !== 'object') {
159
- throw new Error(`Bucket '${bucketName}' configuration must be an object, not a string. Remove quotes around the inner JSON object.`);
160
- }
161
-
162
- const requiredFields = ['BUCKET_NAME', 'ACCESS_KEY_ID', 'SECRET_ACCESS_KEY', 'ENDPOINT'];
163
- const missingFields = requiredFields.filter(field => !config[field]);
164
-
165
- if (missingFields.length > 0) {
166
- throw new Error(`Bucket '${bucketName}' is missing required fields: ${missingFields.join(', ')}`);
167
- }
168
- }
102
+ const bucketConfig = parseAndValidateBucketConnection(process.env.BucketConnection);
103
+ const bucketNames = Object.keys(bucketConfig || {});
169
104
 
170
- console.log(`[mbkbucket] ✓ BucketConnection validated (${bucketNames.length} bucket(s): ${bucketNames.join(', ')})`);
105
+ debugConfig('BucketConnection validated (%s bucket(s): %s)', bucketNames.length, bucketNames.join(', '));
171
106
  return bucketConfig;
172
107
  } catch (error) {
173
108
  console.error(`[mbkbucket] ❌ BucketConnection validation failed: ${error.message}`);
@@ -181,10 +116,11 @@ function validateBucketConnection() {
181
116
 
182
117
  // Parse and validate mbkbucketVar once
183
118
  const mbkbucketVar = validateConfiguration();
184
- console.log(`[mbkbucket] mbkbucketVar configuration:`, mbkbucketVar);
119
+ debugConfig('mbkbucketVar configuration: %O', mbkbucketVar);
185
120
 
186
121
  // Validate BucketConnection
187
122
  validateBucketConnection();
188
123
 
189
124
  export { packageJson, appVersion, getLatestVersion, checkVersion };
190
- export { mbkbucketVar };
125
+ export { mbkbucketVar };
126
+ export { parseAndValidateMbkbucketVar, parseAndValidateBucketConnection };
@@ -0,0 +1,89 @@
1
+ const TRUE_VALUES = new Set(['true', 't', '1', 'yes', 'y', 'on']);
2
+ const FALSE_VALUES = new Set(['false', 'f', '0', 'no', 'n', 'off']);
3
+
4
+ function normalizeBooleanLike(value) {
5
+ if (typeof value === 'boolean') return value;
6
+ if (typeof value === 'string') {
7
+ const normalized = value.trim().toLowerCase();
8
+ if (TRUE_VALUES.has(normalized)) return true;
9
+ if (FALSE_VALUES.has(normalized)) return false;
10
+ }
11
+ return value;
12
+ }
13
+
14
+ export function parseAndValidateMbkbucketVar(rawValue) {
15
+ const errors = [];
16
+ let parsed;
17
+
18
+ try {
19
+ parsed = rawValue ? JSON.parse(rawValue) : {};
20
+ } catch {
21
+ throw new Error('Invalid JSON in process.env.mbkbucketVar');
22
+ }
23
+
24
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
25
+ throw new Error('mbkbucketVar must be a valid object');
26
+ }
27
+
28
+ const defaults = {
29
+ p_view_inline: true,
30
+ publiView_enabled: false
31
+ };
32
+
33
+ for (const [key, defaultVal] of Object.entries(defaults)) {
34
+ const current = parsed[key];
35
+ if (current === undefined || (typeof current === 'string' && current.trim() === '')) {
36
+ parsed[key] = defaultVal;
37
+ }
38
+ }
39
+
40
+ parsed.p_view_inline = normalizeBooleanLike(parsed.p_view_inline);
41
+ parsed.publiView_enabled = normalizeBooleanLike(parsed.publiView_enabled);
42
+
43
+ if (typeof parsed.p_view_inline !== 'boolean') {
44
+ errors.push('mbkbucketVar.p_view_inline must be a boolean if provided');
45
+ }
46
+ if (typeof parsed.publiView_enabled !== 'boolean') {
47
+ errors.push('mbkbucketVar.publiView_enabled must be a boolean if provided');
48
+ }
49
+
50
+ if (errors.length) {
51
+ throw new Error(errors.join(' | '));
52
+ }
53
+
54
+ return parsed;
55
+ }
56
+
57
+ export function parseAndValidateBucketConnection(rawValue) {
58
+ if (!rawValue) {
59
+ return null;
60
+ }
61
+
62
+ let parsed;
63
+ try {
64
+ parsed = JSON.parse(rawValue);
65
+ } catch {
66
+ throw new Error('BucketConnection is not valid JSON');
67
+ }
68
+
69
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed) || Object.keys(parsed).length === 0) {
70
+ throw new Error('BucketConnection must be a non-empty object');
71
+ }
72
+
73
+ const requiredFields = ['BUCKET_NAME', 'ACCESS_KEY_ID', 'SECRET_ACCESS_KEY', 'ENDPOINT'];
74
+
75
+ for (const [bucketName, config] of Object.entries(parsed)) {
76
+ if (!config || typeof config !== 'object' || Array.isArray(config)) {
77
+ throw new Error(`Bucket '${bucketName}' configuration must be an object, not a string. Remove quotes around the inner JSON object.`);
78
+ }
79
+
80
+ const missingFields = requiredFields.filter((field) => !config[field]);
81
+ if (missingFields.length) {
82
+ throw new Error(`Bucket '${bucketName}' is missing required fields: ${missingFields.join(', ')}`);
83
+ }
84
+ }
85
+
86
+ return parsed;
87
+ }
88
+
89
+ export { normalizeBooleanLike };
package/lib/debug.js ADDED
@@ -0,0 +1,12 @@
1
+ import createDebug from "debug";
2
+ import dotenv from "dotenv";
3
+
4
+ dotenv.config();
5
+
6
+ if (process.env.DEBUG) {
7
+ createDebug.enable(process.env.DEBUG);
8
+ }
9
+
10
+ export function createLogger(namespace) {
11
+ return createDebug(`mbkbucket:${namespace}`);
12
+ }