@2112-lab/central-plant 0.2.12 → 0.3.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.
@@ -0,0 +1,497 @@
1
+ import { createClass as _createClass, classCallCheck as _classCallCheck, asyncToGenerator as _asyncToGenerator, regenerator as _regenerator, toConsumableArray as _toConsumableArray, objectSpread2 as _objectSpread2 } from '../../../_virtual/_rollupPluginBabelHelpers.js';
2
+ import { getCachedJsonData, cacheJsonData, removeCachedJsonData } from './S3CacheService.js';
3
+
4
+ var CACHE_KEY = 's3-component-metadata';
5
+ var CACHE_VERSION = 1;
6
+
7
+ /**
8
+ * Simple mutex implementation for preventing concurrent read-modify-write operations
9
+ */
10
+ var SimpleMutex = /*#__PURE__*/function () {
11
+ function SimpleMutex() {
12
+ _classCallCheck(this, SimpleMutex);
13
+ this._locked = false;
14
+ this._queue = [];
15
+ }
16
+ return _createClass(SimpleMutex, [{
17
+ key: "acquire",
18
+ value: function () {
19
+ var _acquire = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
20
+ var _this = this;
21
+ return _regenerator().w(function (_context) {
22
+ while (1) switch (_context.n) {
23
+ case 0:
24
+ return _context.a(2, new Promise(function (resolve) {
25
+ if (!_this._locked) {
26
+ _this._locked = true;
27
+ resolve();
28
+ } else {
29
+ _this._queue.push(resolve);
30
+ }
31
+ }));
32
+ }
33
+ }, _callee);
34
+ }));
35
+ function acquire() {
36
+ return _acquire.apply(this, arguments);
37
+ }
38
+ return acquire;
39
+ }()
40
+ }, {
41
+ key: "release",
42
+ value: function release() {
43
+ if (this._queue.length > 0) {
44
+ var next = this._queue.shift();
45
+ next();
46
+ } else {
47
+ this._locked = false;
48
+ }
49
+ }
50
+ }]);
51
+ }();
52
+ var S3MetadataCacheService = /*#__PURE__*/function () {
53
+ function S3MetadataCacheService() {
54
+ _classCallCheck(this, S3MetadataCacheService);
55
+ this._mutex = new SimpleMutex();
56
+ this._memoryCache = null;
57
+ this._memoryCacheTime = 0;
58
+ this._memoryCacheTTL = 5000; // 5 seconds in-memory cache
59
+ }
60
+
61
+ /**
62
+ * Create empty metadata structure
63
+ */
64
+ return _createClass(S3MetadataCacheService, [{
65
+ key: "_createEmptyMetadata",
66
+ value: function _createEmptyMetadata() {
67
+ return {
68
+ version: CACHE_VERSION,
69
+ components: [],
70
+ timestamp: Date.now()
71
+ };
72
+ }
73
+
74
+ /**
75
+ * Validate and migrate metadata if needed
76
+ */
77
+ }, {
78
+ key: "_validateMetadata",
79
+ value: function _validateMetadata(metadata) {
80
+ if (!metadata) {
81
+ return this._createEmptyMetadata();
82
+ }
83
+
84
+ // Ensure components array exists
85
+ if (!Array.isArray(metadata.components)) {
86
+ metadata.components = [];
87
+ }
88
+
89
+ // Add version if missing (for legacy cache)
90
+ if (metadata.version === undefined) {
91
+ metadata.version = CACHE_VERSION;
92
+ }
93
+ return metadata;
94
+ }
95
+
96
+ /**
97
+ * Check if memory cache is still valid
98
+ */
99
+ }, {
100
+ key: "_isMemoryCacheValid",
101
+ value: function _isMemoryCacheValid() {
102
+ return this._memoryCache !== null && Date.now() - this._memoryCacheTime < this._memoryCacheTTL;
103
+ }
104
+
105
+ /**
106
+ * Invalidate the memory cache
107
+ */
108
+ }, {
109
+ key: "_invalidateMemoryCache",
110
+ value: function _invalidateMemoryCache() {
111
+ this._memoryCache = null;
112
+ this._memoryCacheTime = 0;
113
+ }
114
+
115
+ /**
116
+ * Get all cached components
117
+ * @returns {Promise<Array>} Array of cached component metadata
118
+ */
119
+ }, {
120
+ key: "getAll",
121
+ value: (function () {
122
+ var _getAll = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
123
+ var metadata, validated, _t;
124
+ return _regenerator().w(function (_context2) {
125
+ while (1) switch (_context2.n) {
126
+ case 0:
127
+ if (!this._isMemoryCacheValid()) {
128
+ _context2.n = 1;
129
+ break;
130
+ }
131
+ return _context2.a(2, _toConsumableArray(this._memoryCache.components));
132
+ case 1:
133
+ _context2.p = 1;
134
+ _context2.n = 2;
135
+ return getCachedJsonData(CACHE_KEY);
136
+ case 2:
137
+ metadata = _context2.v;
138
+ validated = this._validateMetadata(metadata); // Update memory cache
139
+ this._memoryCache = validated;
140
+ this._memoryCacheTime = Date.now();
141
+ return _context2.a(2, _toConsumableArray(validated.components));
142
+ case 3:
143
+ _context2.p = 3;
144
+ _t = _context2.v;
145
+ console.warn('⚠️ S3MetadataCacheService: Failed to get components:', _t);
146
+ return _context2.a(2, []);
147
+ }
148
+ }, _callee2, this, [[1, 3]]);
149
+ }));
150
+ function getAll() {
151
+ return _getAll.apply(this, arguments);
152
+ }
153
+ return getAll;
154
+ }()
155
+ /**
156
+ * Get the full metadata object (for advanced use cases)
157
+ * @returns {Promise<Object>} Full metadata object with version, components, timestamp
158
+ */
159
+ )
160
+ }, {
161
+ key: "getMetadata",
162
+ value: (function () {
163
+ var _getMetadata = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() {
164
+ var metadata, validated, _t2;
165
+ return _regenerator().w(function (_context3) {
166
+ while (1) switch (_context3.n) {
167
+ case 0:
168
+ if (!this._isMemoryCacheValid()) {
169
+ _context3.n = 1;
170
+ break;
171
+ }
172
+ return _context3.a(2, _objectSpread2({}, this._memoryCache));
173
+ case 1:
174
+ _context3.p = 1;
175
+ _context3.n = 2;
176
+ return getCachedJsonData(CACHE_KEY);
177
+ case 2:
178
+ metadata = _context3.v;
179
+ validated = this._validateMetadata(metadata);
180
+ this._memoryCache = validated;
181
+ this._memoryCacheTime = Date.now();
182
+ return _context3.a(2, _objectSpread2({}, validated));
183
+ case 3:
184
+ _context3.p = 3;
185
+ _t2 = _context3.v;
186
+ console.warn('⚠️ S3MetadataCacheService: Failed to get metadata:', _t2);
187
+ return _context3.a(2, this._createEmptyMetadata());
188
+ }
189
+ }, _callee3, this, [[1, 3]]);
190
+ }));
191
+ function getMetadata() {
192
+ return _getMetadata.apply(this, arguments);
193
+ }
194
+ return getMetadata;
195
+ }()
196
+ /**
197
+ * Add or update a component in the cache
198
+ * Uses mutex to prevent race conditions
199
+ * @param {Object} component - Component metadata to add/update
200
+ * @returns {Promise<boolean>} Success status
201
+ */
202
+ )
203
+ }, {
204
+ key: "addComponent",
205
+ value: (function () {
206
+ var _addComponent = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(component) {
207
+ var metadata, validated, existingIndex, _t3;
208
+ return _regenerator().w(function (_context4) {
209
+ while (1) switch (_context4.n) {
210
+ case 0:
211
+ if (!(!component || !component.id && !component.uuid)) {
212
+ _context4.n = 1;
213
+ break;
214
+ }
215
+ console.warn('⚠️ S3MetadataCacheService: Cannot add component without id or uuid');
216
+ return _context4.a(2, false);
217
+ case 1:
218
+ _context4.n = 2;
219
+ return this._mutex.acquire();
220
+ case 2:
221
+ _context4.p = 2;
222
+ _context4.n = 3;
223
+ return getCachedJsonData(CACHE_KEY);
224
+ case 3:
225
+ metadata = _context4.v;
226
+ validated = this._validateMetadata(metadata); // Remove existing entry if it exists (by id or uuid)
227
+ existingIndex = validated.components.findIndex(function (c) {
228
+ return c.id === component.id || c.uuid === component.uuid || component.id && c.uuid === component.id || component.uuid && c.id === component.uuid;
229
+ });
230
+ if (existingIndex !== -1) {
231
+ validated.components.splice(existingIndex, 1);
232
+ }
233
+
234
+ // Add new component
235
+ validated.components.push(component);
236
+ validated.timestamp = Date.now();
237
+
238
+ // Write back to cache
239
+ _context4.n = 4;
240
+ return cacheJsonData(CACHE_KEY, validated);
241
+ case 4:
242
+ // Update memory cache
243
+ this._memoryCache = validated;
244
+ this._memoryCacheTime = Date.now();
245
+ console.log("\uD83D\uDCBE S3MetadataCacheService: Added/updated component ".concat(component.name || component.id));
246
+ return _context4.a(2, true);
247
+ case 5:
248
+ _context4.p = 5;
249
+ _t3 = _context4.v;
250
+ console.error('❌ S3MetadataCacheService: Failed to add component:', _t3);
251
+ return _context4.a(2, false);
252
+ case 6:
253
+ _context4.p = 6;
254
+ this._mutex.release();
255
+ return _context4.f(6);
256
+ case 7:
257
+ return _context4.a(2);
258
+ }
259
+ }, _callee4, this, [[2, 5, 6, 7]]);
260
+ }));
261
+ function addComponent(_x) {
262
+ return _addComponent.apply(this, arguments);
263
+ }
264
+ return addComponent;
265
+ }()
266
+ /**
267
+ * Remove a component from the cache
268
+ * Uses mutex to prevent race conditions
269
+ * @param {string} identifier - Component id or uuid to remove
270
+ * @returns {Promise<boolean>} Success status
271
+ */
272
+ )
273
+ }, {
274
+ key: "removeComponent",
275
+ value: (function () {
276
+ var _removeComponent = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(identifier) {
277
+ var metadata, validated, originalLength, removed, _t4;
278
+ return _regenerator().w(function (_context5) {
279
+ while (1) switch (_context5.n) {
280
+ case 0:
281
+ if (identifier) {
282
+ _context5.n = 1;
283
+ break;
284
+ }
285
+ console.warn('⚠️ S3MetadataCacheService: Cannot remove component without identifier');
286
+ return _context5.a(2, false);
287
+ case 1:
288
+ _context5.n = 2;
289
+ return this._mutex.acquire();
290
+ case 2:
291
+ _context5.p = 2;
292
+ _context5.n = 3;
293
+ return getCachedJsonData(CACHE_KEY);
294
+ case 3:
295
+ metadata = _context5.v;
296
+ if (!(!metadata || !metadata.components)) {
297
+ _context5.n = 4;
298
+ break;
299
+ }
300
+ console.log('ℹ️ S3MetadataCacheService: No metadata to remove from');
301
+ return _context5.a(2, true);
302
+ case 4:
303
+ validated = this._validateMetadata(metadata);
304
+ originalLength = validated.components.length; // Filter out the component by both id and uuid
305
+ validated.components = validated.components.filter(function (c) {
306
+ return c.id !== identifier && c.uuid !== identifier;
307
+ });
308
+ removed = originalLength !== validated.components.length;
309
+ if (!removed) {
310
+ _context5.n = 6;
311
+ break;
312
+ }
313
+ validated.timestamp = Date.now();
314
+ _context5.n = 5;
315
+ return cacheJsonData(CACHE_KEY, validated);
316
+ case 5:
317
+ // Update memory cache
318
+ this._memoryCache = validated;
319
+ this._memoryCacheTime = Date.now();
320
+ console.log("\uD83D\uDDD1\uFE0F S3MetadataCacheService: Removed component ".concat(identifier));
321
+ case 6:
322
+ return _context5.a(2, true);
323
+ case 7:
324
+ _context5.p = 7;
325
+ _t4 = _context5.v;
326
+ console.error('❌ S3MetadataCacheService: Failed to remove component:', _t4);
327
+ return _context5.a(2, false);
328
+ case 8:
329
+ _context5.p = 8;
330
+ this._mutex.release();
331
+ return _context5.f(8);
332
+ case 9:
333
+ return _context5.a(2);
334
+ }
335
+ }, _callee5, this, [[2, 7, 8, 9]]);
336
+ }));
337
+ function removeComponent(_x2) {
338
+ return _removeComponent.apply(this, arguments);
339
+ }
340
+ return removeComponent;
341
+ }()
342
+ /**
343
+ * Clear all cached metadata
344
+ * @returns {Promise<boolean>} Success status
345
+ */
346
+ )
347
+ }, {
348
+ key: "clear",
349
+ value: (function () {
350
+ var _clear = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6() {
351
+ var _t5;
352
+ return _regenerator().w(function (_context6) {
353
+ while (1) switch (_context6.n) {
354
+ case 0:
355
+ _context6.n = 1;
356
+ return this._mutex.acquire();
357
+ case 1:
358
+ _context6.p = 1;
359
+ _context6.n = 2;
360
+ return removeCachedJsonData(CACHE_KEY);
361
+ case 2:
362
+ this._invalidateMemoryCache();
363
+ console.log('🗑️ S3MetadataCacheService: Cleared all metadata');
364
+ return _context6.a(2, true);
365
+ case 3:
366
+ _context6.p = 3;
367
+ _t5 = _context6.v;
368
+ console.error('❌ S3MetadataCacheService: Failed to clear metadata:', _t5);
369
+ return _context6.a(2, false);
370
+ case 4:
371
+ _context6.p = 4;
372
+ this._mutex.release();
373
+ return _context6.f(4);
374
+ case 5:
375
+ return _context6.a(2);
376
+ }
377
+ }, _callee6, this, [[1, 3, 4, 5]]);
378
+ }));
379
+ function clear() {
380
+ return _clear.apply(this, arguments);
381
+ }
382
+ return clear;
383
+ }()
384
+ /**
385
+ * Check if a component exists in the cache
386
+ * @param {string} identifier - Component id or uuid
387
+ * @returns {Promise<boolean>} True if exists
388
+ */
389
+ )
390
+ }, {
391
+ key: "hasComponent",
392
+ value: (function () {
393
+ var _hasComponent = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7(identifier) {
394
+ var components;
395
+ return _regenerator().w(function (_context7) {
396
+ while (1) switch (_context7.n) {
397
+ case 0:
398
+ _context7.n = 1;
399
+ return this.getAll();
400
+ case 1:
401
+ components = _context7.v;
402
+ return _context7.a(2, components.some(function (c) {
403
+ return c.id === identifier || c.uuid === identifier;
404
+ }));
405
+ }
406
+ }, _callee7, this);
407
+ }));
408
+ function hasComponent(_x3) {
409
+ return _hasComponent.apply(this, arguments);
410
+ }
411
+ return hasComponent;
412
+ }()
413
+ /**
414
+ * Force refresh from persistent cache (bypass memory cache)
415
+ * @returns {Promise<Array>} Array of cached components
416
+ */
417
+ )
418
+ }, {
419
+ key: "refresh",
420
+ value: (function () {
421
+ var _refresh = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8() {
422
+ return _regenerator().w(function (_context8) {
423
+ while (1) switch (_context8.n) {
424
+ case 0:
425
+ this._invalidateMemoryCache();
426
+ return _context8.a(2, this.getAll());
427
+ }
428
+ }, _callee8, this);
429
+ }));
430
+ function refresh() {
431
+ return _refresh.apply(this, arguments);
432
+ }
433
+ return refresh;
434
+ }()
435
+ /**
436
+ * Replace all cached components with a new array
437
+ * Used for bulk operations like initial fetch
438
+ * @param {Array} components - Array of components to cache
439
+ * @returns {Promise<boolean>} Success status
440
+ */
441
+ )
442
+ }, {
443
+ key: "setAll",
444
+ value: (function () {
445
+ var _setAll = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9(components) {
446
+ var metadata, _t6;
447
+ return _regenerator().w(function (_context9) {
448
+ while (1) switch (_context9.n) {
449
+ case 0:
450
+ if (Array.isArray(components)) {
451
+ _context9.n = 1;
452
+ break;
453
+ }
454
+ console.warn('⚠️ S3MetadataCacheService: setAll requires an array');
455
+ return _context9.a(2, false);
456
+ case 1:
457
+ _context9.n = 2;
458
+ return this._mutex.acquire();
459
+ case 2:
460
+ _context9.p = 2;
461
+ metadata = {
462
+ version: CACHE_VERSION,
463
+ components: components,
464
+ timestamp: Date.now()
465
+ };
466
+ _context9.n = 3;
467
+ return cacheJsonData(CACHE_KEY, metadata);
468
+ case 3:
469
+ // Update memory cache
470
+ this._memoryCache = metadata;
471
+ this._memoryCacheTime = Date.now();
472
+ console.log("\uD83D\uDCBE S3MetadataCacheService: Set ".concat(components.length, " components"));
473
+ return _context9.a(2, true);
474
+ case 4:
475
+ _context9.p = 4;
476
+ _t6 = _context9.v;
477
+ console.error('❌ S3MetadataCacheService: Failed to set components:', _t6);
478
+ return _context9.a(2, false);
479
+ case 5:
480
+ _context9.p = 5;
481
+ this._mutex.release();
482
+ return _context9.f(5);
483
+ case 6:
484
+ return _context9.a(2);
485
+ }
486
+ }, _callee9, this, [[2, 4, 5, 6]]);
487
+ }));
488
+ function setAll(_x4) {
489
+ return _setAll.apply(this, arguments);
490
+ }
491
+ return setAll;
492
+ }())
493
+ }]);
494
+ }(); // Export singleton instance
495
+ var s3MetadataCache = new S3MetadataCacheService();
496
+
497
+ export { S3MetadataCacheService, s3MetadataCache };
@@ -0,0 +1,120 @@
1
+ import { asyncToGenerator as _asyncToGenerator, regenerator as _regenerator } from '../../../_virtual/_rollupPluginBabelHelpers.js';
2
+ import { cacheManager } from './CacheManager.js';
3
+
4
+ /**
5
+ * Retrieve the injected Auth instance from cacheManager config.
6
+ * Falls back gracefully if not configured (skip credential refresh).
7
+ */
8
+ function getAuth() {
9
+ return cacheManager.config.auth || null;
10
+ }
11
+
12
+ /**
13
+ * Measure S3 operation time and log results
14
+ * @param {string} operation - Operation name (e.g., "Upload GLB", "Download JSON")
15
+ * @param {Function} asyncFn - Async function to measure
16
+ * @param {number} fileSize - Optional file size in bytes for speed calculation
17
+ * @returns {Promise} - Result from asyncFn
18
+ */
19
+ function measureS3Transfer(_x, _x2) {
20
+ return _measureS3Transfer.apply(this, arguments);
21
+ }
22
+ function _measureS3Transfer() {
23
+ _measureS3Transfer = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(operation, asyncFn) {
24
+ var fileSize,
25
+ startTime,
26
+ executeTransfer,
27
+ Auth,
28
+ endTime,
29
+ duration,
30
+ _args2 = arguments,
31
+ _t,
32
+ _t2;
33
+ return _regenerator().w(function (_context2) {
34
+ while (1) switch (_context2.n) {
35
+ case 0:
36
+ fileSize = _args2.length > 2 && _args2[2] !== undefined ? _args2[2] : null;
37
+ startTime = performance.now();
38
+ executeTransfer = /*#__PURE__*/function () {
39
+ var _ref = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
40
+ var result, endTime, duration, logMessage, sizeMB, speedMBps;
41
+ return _regenerator().w(function (_context) {
42
+ while (1) switch (_context.n) {
43
+ case 0:
44
+ _context.n = 1;
45
+ return asyncFn();
46
+ case 1:
47
+ result = _context.v;
48
+ endTime = performance.now();
49
+ duration = ((endTime - startTime) / 1000).toFixed(3); // Convert to seconds
50
+ logMessage = "\u23F1\uFE0F S3 ".concat(operation, ": ").concat(duration, "s");
51
+ if (fileSize) {
52
+ sizeMB = (fileSize / (1024 * 1024)).toFixed(2);
53
+ speedMBps = (sizeMB / duration).toFixed(2);
54
+ logMessage += " (".concat(sizeMB, " MB @ ").concat(speedMBps, " MB/s)");
55
+ }
56
+ console.log(logMessage);
57
+ return _context.a(2, result);
58
+ }
59
+ }, _callee);
60
+ }));
61
+ return function executeTransfer() {
62
+ return _ref.apply(this, arguments);
63
+ };
64
+ }();
65
+ _context2.p = 1;
66
+ _context2.n = 2;
67
+ return executeTransfer();
68
+ case 2:
69
+ return _context2.a(2, _context2.v);
70
+ case 3:
71
+ _context2.p = 3;
72
+ _t = _context2.v;
73
+ if (!(_t.code === 'ExpiredToken' || _t.message && _t.message.includes('ExpiredToken'))) {
74
+ _context2.n = 8;
75
+ break;
76
+ }
77
+ console.warn("\u26A0\uFE0F Token expired during ".concat(operation, ". Attempting session refresh and retry..."));
78
+ Auth = getAuth();
79
+ if (Auth) {
80
+ _context2.n = 4;
81
+ break;
82
+ }
83
+ console.error("\u274C Cannot refresh credentials: Auth not injected via cacheManager.configure({ auth })");
84
+ throw _t;
85
+ case 4:
86
+ _context2.p = 4;
87
+ _context2.n = 5;
88
+ return Auth.currentCredentials({
89
+ bypassCache: true
90
+ });
91
+ case 5:
92
+ console.log("\u2705 Session refreshed. Retrying ".concat(operation, "..."));
93
+
94
+ // Reset timer for the retry
95
+ startTime = performance.now();
96
+ _context2.n = 6;
97
+ return executeTransfer();
98
+ case 6:
99
+ return _context2.a(2, _context2.v);
100
+ case 7:
101
+ _context2.p = 7;
102
+ _t2 = _context2.v;
103
+ console.error("\u274C Retry failed for ".concat(operation, ":"), _t2);
104
+ // If retry fails, throw the Retry Error (likely unrelated or persistent auth issue)
105
+ throw _t2;
106
+ case 8:
107
+ // Standard error handling for non-expired tokens (or if logic above didn't catch it)
108
+ endTime = performance.now();
109
+ duration = ((endTime - startTime) / 1000).toFixed(3);
110
+ console.error("\u274C S3 ".concat(operation, " failed after ").concat(duration, "s:"), _t);
111
+ throw _t;
112
+ case 9:
113
+ return _context2.a(2);
114
+ }
115
+ }, _callee2, null, [[4, 7], [1, 3]]);
116
+ }));
117
+ return _measureS3Transfer.apply(this, arguments);
118
+ }
119
+
120
+ export { measureS3Transfer };