holosphere 2.0.0-alpha7 → 2.0.0-alpha8
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/dist/cjs/holosphere.cjs +1 -1
- package/dist/esm/holosphere.js +1 -1
- package/dist/{index-d6f4RJBM.js → index-4XHHKe6S.js} +356 -58
- package/dist/index-4XHHKe6S.js.map +1 -0
- package/dist/{index-jmTHEbR2.js → index-BjP1TXGz.js} +2 -2
- package/dist/{index-jmTHEbR2.js.map → index-BjP1TXGz.js.map} +1 -1
- package/dist/{index-C-IlLYlk.cjs → index-CKffQDmQ.cjs} +2 -2
- package/dist/{index-C-IlLYlk.cjs.map → index-CKffQDmQ.cjs.map} +1 -1
- package/dist/index-Dz5kOZMI.cjs +5 -0
- package/dist/index-Dz5kOZMI.cjs.map +1 -0
- package/dist/{indexeddb-storage-a8GipaDr.cjs → indexeddb-storage-DD7EFBVc.cjs} +2 -2
- package/dist/{indexeddb-storage-a8GipaDr.cjs.map → indexeddb-storage-DD7EFBVc.cjs.map} +1 -1
- package/dist/{indexeddb-storage-D8kOl0oK.js → indexeddb-storage-lExjjFlV.js} +2 -2
- package/dist/{indexeddb-storage-D8kOl0oK.js.map → indexeddb-storage-lExjjFlV.js.map} +1 -1
- package/dist/{memory-storage-DBQK622V.js → memory-storage-C68adso2.js} +2 -2
- package/dist/{memory-storage-DBQK622V.js.map → memory-storage-C68adso2.js.map} +1 -1
- package/dist/{memory-storage-gfRovk2O.cjs → memory-storage-DD_6yyXT.cjs} +2 -2
- package/dist/{memory-storage-gfRovk2O.cjs.map → memory-storage-DD_6yyXT.cjs.map} +1 -1
- package/dist/{secp256k1-BCAPF45D.cjs → secp256k1-DYELiqgx.cjs} +2 -2
- package/dist/{secp256k1-BCAPF45D.cjs.map → secp256k1-DYELiqgx.cjs.map} +1 -1
- package/dist/{secp256k1-DYm_CMqW.js → secp256k1-OM8siPyy.js} +2 -2
- package/dist/{secp256k1-DYm_CMqW.js.map → secp256k1-OM8siPyy.js.map} +1 -1
- package/examples/holosphere-widget.js +1242 -0
- package/examples/widget-demo.html +274 -0
- package/examples/widget.html +703 -0
- package/package.json +3 -1
- package/src/cdn-entry.js +22 -0
- package/src/contracts/queries.js +16 -1
- package/src/core/holosphere.js +2 -2
- package/src/crypto/nostr-utils.js +36 -2
- package/src/federation/handshake.js +16 -4
- package/src/index.js +16 -2
- package/src/storage/backends/gundb-backend.js +293 -9
- package/src/storage/gun-wrapper.js +64 -16
- package/src/storage/nostr-async.js +40 -25
- package/src/storage/unified-storage.js +31 -1
- package/vite.config.cdn.js +60 -0
- package/dist/index-Bvwyvd0T.cjs +0 -5
- package/dist/index-Bvwyvd0T.cjs.map +0 -1
- package/dist/index-d6f4RJBM.js.map +0 -1
|
@@ -54,15 +54,32 @@ function encodePathComponent(component) {
|
|
|
54
54
|
return encodeURIComponent(component).replace(/%2F/g, '/');
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Navigate to a Gun path using chained .get() calls
|
|
59
|
+
* Gun treats 'a/b/c' as a literal key, not a path.
|
|
60
|
+
* This function splits the path and chains .get() calls properly.
|
|
61
|
+
* @private
|
|
62
|
+
* @param {Object} gun - Gun instance
|
|
63
|
+
* @param {string} path - Path string like "appname/holon/lens/key"
|
|
64
|
+
* @returns {Object} Gun chain reference at the path
|
|
65
|
+
*/
|
|
66
|
+
function getGunPath(gun, path) {
|
|
67
|
+
const parts = path.split('/').filter(p => p.length > 0);
|
|
68
|
+
let ref = gun;
|
|
69
|
+
for (const part of parts) {
|
|
70
|
+
ref = ref.get(part);
|
|
71
|
+
}
|
|
72
|
+
return ref;
|
|
73
|
+
}
|
|
74
|
+
|
|
57
75
|
/**
|
|
58
76
|
* Serialize data for GunDB storage
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* The deserialization handles both this format and raw JSON strings for reading old data
|
|
77
|
+
* Stores data as raw JSON string for compatibility with holosphere original
|
|
78
|
+
* This matches the format used in holosphere v1 for better interoperability
|
|
62
79
|
* @private
|
|
63
80
|
*/
|
|
64
81
|
function serializeForGun(data) {
|
|
65
|
-
return
|
|
82
|
+
return JSON.stringify(data);
|
|
66
83
|
}
|
|
67
84
|
|
|
68
85
|
/**
|
|
@@ -152,11 +169,21 @@ function deserializeFromGun(data) {
|
|
|
152
169
|
export async function write(gun, path, data) {
|
|
153
170
|
try {
|
|
154
171
|
const serialized = serializeForGun(data);
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
172
|
+
const parts = path.split('/').filter(p => p.length > 0);
|
|
173
|
+
console.log('[gun-wrapper] write:', { path, parts, dataId: data?.id });
|
|
174
|
+
const ref = getGunPath(gun, path);
|
|
175
|
+
console.log('[gun-wrapper] write ref soul:', ref?._.get);
|
|
176
|
+
const ack = await gunPut(ref, serialized, 5000); // Increased timeout from 2s to 5s
|
|
177
|
+
console.log('[gun-wrapper] write ack:', { ok: ack.ok, timeout: ack.timeout });
|
|
178
|
+
if (ack.timeout) {
|
|
179
|
+
console.warn('[gun-wrapper] write timed out (data may not be persisted):', path);
|
|
180
|
+
}
|
|
181
|
+
console.log('[gun-wrapper] write complete:', path);
|
|
182
|
+
|
|
183
|
+
// Return ack info so caller can handle timeouts
|
|
184
|
+
return { ok: true, timeout: ack.timeout || false };
|
|
159
185
|
} catch (error) {
|
|
186
|
+
console.error('[gun-wrapper] write error:', error);
|
|
160
187
|
throw error;
|
|
161
188
|
}
|
|
162
189
|
}
|
|
@@ -168,7 +195,12 @@ export async function write(gun, path, data) {
|
|
|
168
195
|
* @returns {Promise<Object|null>} Data or null if not found
|
|
169
196
|
*/
|
|
170
197
|
export async function read(gun, path) {
|
|
171
|
-
const
|
|
198
|
+
const parts = path.split('/').filter(p => p.length > 0);
|
|
199
|
+
console.log('[gun-wrapper] read:', { path, parts });
|
|
200
|
+
const ref = getGunPath(gun, path);
|
|
201
|
+
console.log('[gun-wrapper] read ref soul:', ref?._.get);
|
|
202
|
+
const rawData = await gunPromise(ref, 2000);
|
|
203
|
+
console.log('[gun-wrapper] read rawData:', rawData ? (typeof rawData === 'string' ? rawData.substring(0, 100) : 'object') : 'null');
|
|
172
204
|
|
|
173
205
|
if (!rawData) {
|
|
174
206
|
return null;
|
|
@@ -193,18 +225,23 @@ export async function read(gun, path) {
|
|
|
193
225
|
* @returns {Promise<Object[]>} Array of data objects
|
|
194
226
|
*/
|
|
195
227
|
export async function readAll(gun, path, timeout = 5000) {
|
|
228
|
+
const parts = path.split('/').filter(p => p.length > 0);
|
|
229
|
+
console.log('[gun-wrapper] readAll:', { path, parts });
|
|
230
|
+
|
|
196
231
|
return new Promise((resolve) => {
|
|
197
232
|
const output = new Map();
|
|
198
233
|
let settled = false;
|
|
199
234
|
let expectedCount = 0;
|
|
200
235
|
let receivedCount = 0;
|
|
201
236
|
|
|
202
|
-
const ref = gun
|
|
237
|
+
const ref = getGunPath(gun, path);
|
|
238
|
+
console.log('[gun-wrapper] readAll ref soul:', ref?._.get);
|
|
203
239
|
|
|
204
240
|
const tryResolve = () => {
|
|
205
241
|
if (settled) return;
|
|
206
242
|
if (expectedCount > 0 && receivedCount >= expectedCount) {
|
|
207
243
|
settled = true;
|
|
244
|
+
console.log('[gun-wrapper] readAll resolved with', output.size, 'items');
|
|
208
245
|
resolve(Array.from(output.values()));
|
|
209
246
|
}
|
|
210
247
|
};
|
|
@@ -216,18 +253,24 @@ export async function readAll(gun, path, timeout = 5000) {
|
|
|
216
253
|
// Step 1: Get the parent data to count expected items
|
|
217
254
|
ref.once((parentData) => {
|
|
218
255
|
if (settled) return;
|
|
256
|
+
console.log('[gun-wrapper] readAll parentData:', parentData);
|
|
257
|
+
console.log('[gun-wrapper] readAll parentData keys:', parentData ? Object.keys(parentData).filter(k => k !== '_') : 'null');
|
|
258
|
+
console.log('[gun-wrapper] readAll parentData type:', typeof parentData);
|
|
219
259
|
|
|
220
260
|
if (!parentData) {
|
|
221
261
|
settled = true;
|
|
262
|
+
console.log('[gun-wrapper] readAll: no parent data, returning empty');
|
|
222
263
|
resolve([]);
|
|
223
264
|
return;
|
|
224
265
|
}
|
|
225
266
|
|
|
226
267
|
// Get all keys except Gun metadata
|
|
227
268
|
const keys = Object.keys(parentData).filter(k => k !== '_');
|
|
269
|
+
console.log('[gun-wrapper] readAll keys:', keys);
|
|
228
270
|
|
|
229
271
|
if (keys.length === 0) {
|
|
230
272
|
settled = true;
|
|
273
|
+
console.log('[gun-wrapper] readAll: no keys, returning empty');
|
|
231
274
|
resolve([]);
|
|
232
275
|
return;
|
|
233
276
|
}
|
|
@@ -298,7 +341,7 @@ export async function readAll(gun, path, timeout = 5000) {
|
|
|
298
341
|
* @returns {Promise<boolean>} Success indicator
|
|
299
342
|
*/
|
|
300
343
|
export async function update(gun, path, updates) {
|
|
301
|
-
const rawData = await gunPromise(gun
|
|
344
|
+
const rawData = await gunPromise(getGunPath(gun, path));
|
|
302
345
|
|
|
303
346
|
if (!rawData) {
|
|
304
347
|
return false; // Not found
|
|
@@ -315,7 +358,9 @@ export async function update(gun, path, updates) {
|
|
|
315
358
|
|
|
316
359
|
try {
|
|
317
360
|
const serialized = serializeForGun(merged);
|
|
318
|
-
await gunPut(gun
|
|
361
|
+
await gunPut(getGunPath(gun, path), serialized, 2000);
|
|
362
|
+
// Add delay for propagation
|
|
363
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
319
364
|
return true;
|
|
320
365
|
} catch (error) {
|
|
321
366
|
throw error;
|
|
@@ -331,7 +376,7 @@ export async function update(gun, path, updates) {
|
|
|
331
376
|
export async function deleteData(gun, path) {
|
|
332
377
|
try {
|
|
333
378
|
// First read existing data to get the id
|
|
334
|
-
const rawData = await gunPromise(gun
|
|
379
|
+
const rawData = await gunPromise(getGunPath(gun, path));
|
|
335
380
|
if (!rawData) {
|
|
336
381
|
return true; // Already deleted/doesn't exist
|
|
337
382
|
}
|
|
@@ -345,7 +390,9 @@ export async function deleteData(gun, path) {
|
|
|
345
390
|
_deletedAt: Date.now()
|
|
346
391
|
};
|
|
347
392
|
|
|
348
|
-
await gunPut(gun
|
|
393
|
+
await gunPut(getGunPath(gun, path), serializeForGun(tombstone), 2000);
|
|
394
|
+
// Add delay for propagation
|
|
395
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
349
396
|
return true;
|
|
350
397
|
} catch (error) {
|
|
351
398
|
throw error;
|
|
@@ -389,7 +436,7 @@ export function subscribe(gun, path, callback, options = {}) {
|
|
|
389
436
|
|
|
390
437
|
if (isPrefix) {
|
|
391
438
|
// Subscribe to all items under this prefix
|
|
392
|
-
const ref = gun
|
|
439
|
+
const ref = getGunPath(gun, path);
|
|
393
440
|
|
|
394
441
|
ref.map().on((data, key) => {
|
|
395
442
|
if (data && !key.startsWith('_')) {
|
|
@@ -411,7 +458,7 @@ export function subscribe(gun, path, callback, options = {}) {
|
|
|
411
458
|
};
|
|
412
459
|
} else {
|
|
413
460
|
// Subscribe to single item
|
|
414
|
-
const listener = gun
|
|
461
|
+
const listener = getGunPath(gun, path).on((data, key) => {
|
|
415
462
|
if (data) {
|
|
416
463
|
const deserialized = deserializeFromGun(data);
|
|
417
464
|
if (deserialized && !deserialized._deleted) {
|
|
@@ -450,6 +497,7 @@ export async function writeGlobal(gun, appname, tableName, data) {
|
|
|
450
497
|
throw new Error('writeGlobal: data must have an id field');
|
|
451
498
|
}
|
|
452
499
|
const path = buildGlobalPath(appname, tableName, data.id);
|
|
500
|
+
// Use write function which includes the propagation delay
|
|
453
501
|
return write(gun, path, data);
|
|
454
502
|
}
|
|
455
503
|
|
|
@@ -65,28 +65,33 @@ export async function nostrGet(client, path, kind = 30000, options = {}) {
|
|
|
65
65
|
if (!options.skipPersistent && client.persistentGet) {
|
|
66
66
|
const persistedEvent = await client.persistentGet(path);
|
|
67
67
|
if (persistedEvent && persistedEvent.content) {
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
// Verify author is in requested authors list (persistent storage may have cached events from other authors)
|
|
69
|
+
if (!authors.includes(persistedEvent.pubkey)) {
|
|
70
|
+
// Author mismatch - fall through to relay query
|
|
71
|
+
} else {
|
|
72
|
+
try {
|
|
73
|
+
const data = JSON.parse(persistedEvent.content);
|
|
70
74
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
// Skip deleted items
|
|
76
|
+
if (data._deleted) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
75
79
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
// Optionally include author information
|
|
81
|
+
if (options.includeAuthor) {
|
|
82
|
+
data._author = persistedEvent.pubkey;
|
|
83
|
+
}
|
|
80
84
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
// Trigger background refresh from relays (fire-and-forget)
|
|
86
|
+
if (client.refreshPathInBackground) {
|
|
87
|
+
client.refreshPathInBackground(path, kind, { authors, timeout });
|
|
88
|
+
}
|
|
85
89
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
return data;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
// Fall through to relay query if parsing fails
|
|
93
|
+
console.warn('[nostrGet] Failed to parse persisted event:', error);
|
|
94
|
+
}
|
|
90
95
|
}
|
|
91
96
|
}
|
|
92
97
|
}
|
|
@@ -136,12 +141,15 @@ async function _executeNostrGet(client, path, kind, authors, timeout, options) {
|
|
|
136
141
|
|
|
137
142
|
const events = await client.query(filter, { timeout });
|
|
138
143
|
|
|
139
|
-
|
|
144
|
+
// Filter by author (relays may not respect authors filter)
|
|
145
|
+
const authoredEvents = events.filter(event => authors.includes(event.pubkey));
|
|
146
|
+
|
|
147
|
+
if (authoredEvents.length === 0) {
|
|
140
148
|
return null;
|
|
141
149
|
}
|
|
142
150
|
|
|
143
|
-
// Get most recent event (across
|
|
144
|
-
const event =
|
|
151
|
+
// Get most recent event (across allowed authors)
|
|
152
|
+
const event = authoredEvents.sort((a, b) => b.created_at - a.created_at)[0];
|
|
145
153
|
|
|
146
154
|
try {
|
|
147
155
|
const data = JSON.parse(event.content);
|
|
@@ -187,6 +195,9 @@ export async function nostrGetAll(client, pathPrefix, kind = 30000, options = {}
|
|
|
187
195
|
for (const event of persistedEvents) {
|
|
188
196
|
if (!event || !event.tags) continue;
|
|
189
197
|
|
|
198
|
+
// Verify author is in requested authors list (persistent storage may have cached events from other authors)
|
|
199
|
+
if (!authors.includes(event.pubkey)) continue;
|
|
200
|
+
|
|
190
201
|
const dTag = event.tags.find(t => t[0] === 'd');
|
|
191
202
|
if (!dTag || !dTag[1] || !dTag[1].startsWith(pathPrefix)) continue;
|
|
192
203
|
|
|
@@ -267,10 +278,12 @@ async function _executeNostrGetAll(client, pathPrefix, kind, authors, timeout, l
|
|
|
267
278
|
|
|
268
279
|
const events = await client.query(filter, { timeout });
|
|
269
280
|
|
|
270
|
-
// Filter by path prefix
|
|
281
|
+
// Filter by path prefix AND verify author (relays may not respect authors filter)
|
|
271
282
|
const matching = events.filter(event => {
|
|
272
283
|
const dTag = event.tags.find(t => t[0] === 'd');
|
|
273
|
-
|
|
284
|
+
const pathMatches = dTag && dTag[1] && dTag[1].startsWith(pathPrefix);
|
|
285
|
+
const authorAllowed = authors.includes(event.pubkey);
|
|
286
|
+
return pathMatches && authorAllowed;
|
|
274
287
|
});
|
|
275
288
|
|
|
276
289
|
// Parse content and group by d-tag (keep latest only, across all authors)
|
|
@@ -331,10 +344,12 @@ export async function nostrGetAllHybrid(client, pathPrefix, kind = 30000, option
|
|
|
331
344
|
|
|
332
345
|
const events = await queryMethod.call(client, filter, { timeout });
|
|
333
346
|
|
|
334
|
-
// Filter by path prefix
|
|
347
|
+
// Filter by path prefix AND verify author (relays may not respect authors filter)
|
|
335
348
|
const matching = events.filter(event => {
|
|
336
349
|
const dTag = event.tags.find(t => t[0] === 'd');
|
|
337
|
-
|
|
350
|
+
const pathMatches = dTag && dTag[1] && dTag[1].startsWith(pathPrefix);
|
|
351
|
+
const authorAllowed = authors.includes(event.pubkey);
|
|
352
|
+
return pathMatches && authorAllowed;
|
|
338
353
|
});
|
|
339
354
|
|
|
340
355
|
// Parse content and group by d-tag (keep latest only)
|
|
@@ -27,7 +27,11 @@ export function buildPath(appName, holonId, lensName, key = null) {
|
|
|
27
27
|
* @returns {Promise<boolean>} Success indicator
|
|
28
28
|
*/
|
|
29
29
|
export async function write(client, path, data) {
|
|
30
|
-
// Check if this is a GunDB client
|
|
30
|
+
// Check if this is a GunDB client with backend methods (preferred - has write cache)
|
|
31
|
+
if (client.write && client.gun) {
|
|
32
|
+
return client.write(path, data);
|
|
33
|
+
}
|
|
34
|
+
// Fallback to direct gunWrapper (no write cache)
|
|
31
35
|
if (client.gun) {
|
|
32
36
|
return gunWrapper.write(client.gun, path, data);
|
|
33
37
|
}
|
|
@@ -43,6 +47,11 @@ export async function write(client, path, data) {
|
|
|
43
47
|
* @returns {Promise<Object|null>} Data or null
|
|
44
48
|
*/
|
|
45
49
|
export async function read(client, path, options = {}) {
|
|
50
|
+
// Check if this is a GunDB client with backend methods (preferred - has write cache)
|
|
51
|
+
if (client.read && client.gun) {
|
|
52
|
+
return client.read(path, options);
|
|
53
|
+
}
|
|
54
|
+
// Fallback to direct gunWrapper
|
|
46
55
|
if (client.gun) {
|
|
47
56
|
return gunWrapper.read(client.gun, path);
|
|
48
57
|
}
|
|
@@ -57,6 +66,11 @@ export async function read(client, path, options = {}) {
|
|
|
57
66
|
* @returns {Promise<Object[]>} Array of data objects
|
|
58
67
|
*/
|
|
59
68
|
export async function readAll(client, path, options = {}) {
|
|
69
|
+
// Check if this is a GunDB client with backend methods (preferred - has write cache)
|
|
70
|
+
if (client.readAll && client.gun) {
|
|
71
|
+
return client.readAll(path, options);
|
|
72
|
+
}
|
|
73
|
+
// Fallback to direct gunWrapper
|
|
60
74
|
if (client.gun) {
|
|
61
75
|
return gunWrapper.readAll(client.gun, path);
|
|
62
76
|
}
|
|
@@ -71,6 +85,11 @@ export async function readAll(client, path, options = {}) {
|
|
|
71
85
|
* @returns {Promise<boolean>} Success indicator
|
|
72
86
|
*/
|
|
73
87
|
export async function update(client, path, updates) {
|
|
88
|
+
// Check if this is a GunDB client with backend methods (preferred - has write cache)
|
|
89
|
+
if (client.update && client.gun) {
|
|
90
|
+
return client.update(path, updates);
|
|
91
|
+
}
|
|
92
|
+
// Fallback to direct gunWrapper
|
|
74
93
|
if (client.gun) {
|
|
75
94
|
return gunWrapper.update(client.gun, path, updates);
|
|
76
95
|
}
|
|
@@ -84,6 +103,11 @@ export async function update(client, path, updates) {
|
|
|
84
103
|
* @returns {Promise<boolean>} Success indicator
|
|
85
104
|
*/
|
|
86
105
|
export async function deleteData(client, path) {
|
|
106
|
+
// Check if this is a GunDB client with backend methods (preferred - has write cache)
|
|
107
|
+
if (client.delete && client.gun) {
|
|
108
|
+
return client.delete(path);
|
|
109
|
+
}
|
|
110
|
+
// Fallback to direct gunWrapper
|
|
87
111
|
if (client.gun) {
|
|
88
112
|
return gunWrapper.deleteData(client.gun, path);
|
|
89
113
|
}
|
|
@@ -97,6 +121,7 @@ export async function deleteData(client, path) {
|
|
|
97
121
|
* @returns {Promise<Object>} Deletion results
|
|
98
122
|
*/
|
|
99
123
|
export async function deleteAll(client, path) {
|
|
124
|
+
// Fallback to direct gunWrapper (deleteAll not typically in client interface)
|
|
100
125
|
if (client.gun) {
|
|
101
126
|
return gunWrapper.deleteAll(client.gun, path);
|
|
102
127
|
}
|
|
@@ -112,6 +137,11 @@ export async function deleteAll(client, path) {
|
|
|
112
137
|
* @returns {Object} Subscription with unsubscribe method
|
|
113
138
|
*/
|
|
114
139
|
export function subscribe(client, path, callback, options = {}) {
|
|
140
|
+
// Check if this is a GunDB client with backend methods
|
|
141
|
+
if (client.subscribe && client.gun) {
|
|
142
|
+
return client.subscribe(path, callback, options);
|
|
143
|
+
}
|
|
144
|
+
// Fallback to direct gunWrapper
|
|
115
145
|
if (client.gun) {
|
|
116
146
|
return gunWrapper.subscribe(client.gun, path, callback, options);
|
|
117
147
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* CDN Build Configuration
|
|
6
|
+
* Creates a self-contained bundle for browser usage via CDN
|
|
7
|
+
* All dependencies are bundled (no externals)
|
|
8
|
+
*/
|
|
9
|
+
export default defineConfig({
|
|
10
|
+
build: {
|
|
11
|
+
target: 'es2020',
|
|
12
|
+
outDir: 'dist/cdn',
|
|
13
|
+
lib: {
|
|
14
|
+
entry: resolve(__dirname, 'src/cdn-entry.js'),
|
|
15
|
+
name: 'HoloSphere',
|
|
16
|
+
formats: ['iife'],
|
|
17
|
+
fileName: () => 'holosphere.min.js',
|
|
18
|
+
},
|
|
19
|
+
rollupOptions: {
|
|
20
|
+
// Bundle all dependencies for CDN
|
|
21
|
+
external: [],
|
|
22
|
+
output: {
|
|
23
|
+
// Use default export so HoloSphere is directly accessible
|
|
24
|
+
exports: 'default',
|
|
25
|
+
// Extend window with all exports
|
|
26
|
+
extend: true,
|
|
27
|
+
// Provide globals for any remaining externals
|
|
28
|
+
globals: {},
|
|
29
|
+
// Inline dynamic imports
|
|
30
|
+
inlineDynamicImports: true,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
sourcemap: true,
|
|
34
|
+
minify: 'terser',
|
|
35
|
+
terserOptions: {
|
|
36
|
+
compress: {
|
|
37
|
+
drop_console: false,
|
|
38
|
+
passes: 2,
|
|
39
|
+
},
|
|
40
|
+
mangle: {
|
|
41
|
+
safari10: true,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
// Increase chunk size warning limit for CDN bundle
|
|
45
|
+
chunkSizeWarningLimit: 2000,
|
|
46
|
+
},
|
|
47
|
+
resolve: {
|
|
48
|
+
alias: [
|
|
49
|
+
{
|
|
50
|
+
find: './filesystem-storage.js',
|
|
51
|
+
replacement: resolve(__dirname, 'src/storage/filesystem-storage-browser.js'),
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
define: {
|
|
56
|
+
'process.versions.node': JSON.stringify(undefined),
|
|
57
|
+
'process.env.NODE_ENV': JSON.stringify('production'),
|
|
58
|
+
'global': 'globalThis',
|
|
59
|
+
},
|
|
60
|
+
});
|