@seedprotocol/sdk 0.2.47 → 0.2.49
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/{ArweaveClient-CoFomTCN.js → ArweaveClient-Dcqcpsq4.js} +2 -2
- package/dist/{ArweaveClient-DQrXvrNN.js.map → ArweaveClient-Dcqcpsq4.js.map} +1 -1
- package/dist/{ArweaveClient-DQrXvrNN.js → ArweaveClient-Dh7LRIqD.js} +2 -2
- package/dist/{ArweaveClient-CoFomTCN.js.map → ArweaveClient-Dh7LRIqD.js.map} +1 -1
- package/dist/{Db-Dgbci7BP.js → Db-BjoRtCFg.js} +3 -4
- package/dist/{Db-Dgbci7BP.js.map → Db-BjoRtCFg.js.map} +1 -1
- package/dist/{Db-Dv7s24Zi.js → Db-CMRjCZoY.js} +7 -7
- package/dist/Db-CMRjCZoY.js.map +1 -0
- package/dist/{EasClient-CBu9ENAi.js → EasClient-A1xC7Gm-.js} +2 -2
- package/dist/{EasClient-sCbTHcO8.js.map → EasClient-A1xC7Gm-.js.map} +1 -1
- package/dist/{EasClient-sCbTHcO8.js → EasClient-m2mXad59.js} +2 -2
- package/dist/{EasClient-CBu9ENAi.js.map → EasClient-m2mXad59.js.map} +1 -1
- package/dist/{FileManager-COOp89Mj.js → FileManager-Dv2zn1RW.js} +12 -2
- package/dist/FileManager-Dv2zn1RW.js.map +1 -0
- package/dist/{Item-DOEEhWwi.js → Item-7vg6XRhw.js} +3 -4
- package/dist/{Item-DOEEhWwi.js.map → Item-7vg6XRhw.js.map} +1 -1
- package/dist/{ItemProperty-CABHeoKm.js → ItemProperty-Bcht9WTV.js} +3 -4
- package/dist/{ItemProperty-CABHeoKm.js.map → ItemProperty-Bcht9WTV.js.map} +1 -1
- package/dist/{QueryClient-BBaE-LKI.js → QueryClient-C-hFMF2j.js} +2 -2
- package/dist/{QueryClient-BBaE-LKI.js.map → QueryClient-C-hFMF2j.js.map} +1 -1
- package/dist/{QueryClient-C-ZzUnWq.js → QueryClient-C02sPZ-K.js} +2 -2
- package/dist/{QueryClient-C-ZzUnWq.js.map → QueryClient-C02sPZ-K.js.map} +1 -1
- package/dist/bin.js +21 -21
- package/dist/bin.js.map +1 -1
- package/dist/{constants-Dgv-tSO3.js → constants-C03RQQht.js} +12 -5
- package/dist/constants-C03RQQht.js.map +1 -0
- package/dist/{index-OxV3e-OY.js → index-D3Scq_ka.js} +1095 -137
- package/dist/index-D3Scq_ka.js.map +1 -0
- package/dist/{index-CAikKppg.js → index-Dp3GcggF.js} +3 -4
- package/dist/index-Dp3GcggF.js.map +1 -0
- package/dist/main.js +2 -3
- package/dist/main.js.map +1 -1
- package/dist/{seed.schema.config-Ba_E7t1M.js → seed.schema.config-CS6BvsTl.js} +3 -4
- package/dist/seed.schema.config-CS6BvsTl.js.map +1 -0
- package/dist/src/BaseFileManager.ts +14 -2
- package/dist/src/BaseItem.ts +15 -3
- package/dist/src/BaseItemProperty.ts +1 -1
- package/dist/src/FileManager.ts +14 -1
- package/dist/src/IItem.ts +4 -1
- package/dist/src/ImageResizer.ts +3 -6
- package/dist/src/actors.ts +1 -1
- package/dist/src/client.ts +25 -0
- package/dist/src/configureFs.ts +1 -1
- package/dist/src/createNewItem.ts +5 -1
- package/dist/src/download.ts +6 -16
- package/dist/src/drizzle.ts +1 -1
- package/dist/src/files.ts +1 -1
- package/dist/src/getPublishUploads.ts +1 -1
- package/dist/src/helpers.ts +201 -84
- package/dist/src/hydrateFromDb.ts +1 -1
- package/dist/src/migrate.ts +3 -3
- package/dist/src/resolveRelatedValue.ts +1 -1
- package/dist/src/resolveRemoteStorage.ts +1 -1
- package/dist/src/saveImageSrc.ts +1 -1
- package/dist/src/saveItemStorage.ts +1 -1
- package/dist/src/ts-to-proto.ts +1 -1
- package/dist/src/validate.ts +1 -1
- package/dist/src/waitForDb.ts +9 -10
- package/dist/src/waitForFiles.ts +1 -1
- package/dist/types/src/Item/BaseItem.d.ts +4 -1
- package/dist/types/src/Item/BaseItem.d.ts.map +1 -1
- package/dist/types/src/Item/service/actors/saveDataToDb.d.ts +3 -0
- package/dist/types/src/Item/service/actors/saveDataToDb.d.ts.map +1 -0
- package/dist/types/src/browser/db/Db.d.ts +2 -2
- package/dist/types/src/browser/db/Db.d.ts.map +1 -1
- package/dist/types/src/browser/helpers/FileManager.d.ts +10 -0
- package/dist/types/src/browser/helpers/FileManager.d.ts.map +1 -1
- package/dist/types/src/browser/helpers/index.d.ts +1 -2
- package/dist/types/src/browser/helpers/index.d.ts.map +1 -1
- package/dist/types/src/browser/react/item.d.ts.map +1 -1
- package/dist/types/src/browser/workers/ImageResizer.d.ts.map +1 -1
- package/dist/types/src/client.d.ts +1 -0
- package/dist/types/src/client.d.ts.map +1 -1
- package/dist/types/src/db/write/createNewItem.d.ts.map +1 -1
- package/dist/types/src/events/files/download.d.ts.map +1 -1
- package/dist/types/src/helpers/FileManager/BaseFileManager.d.ts +5 -2
- package/dist/types/src/helpers/FileManager/BaseFileManager.d.ts.map +1 -1
- package/dist/types/src/interfaces/IItem.d.ts +3 -0
- package/dist/types/src/interfaces/IItem.d.ts.map +1 -1
- package/dist/types/src/node/helpers/FileManager.d.ts +2 -0
- package/dist/types/src/node/helpers/FileManager.d.ts.map +1 -1
- package/dist/types/src/services/internal/helpers.d.ts +0 -2
- package/dist/types/src/services/internal/helpers.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/Db-Dv7s24Zi.js.map +0 -1
- package/dist/FileManager-COOp89Mj.js.map +0 -1
- package/dist/FileManager-Np-xCJ1X.js +0 -794
- package/dist/FileManager-Np-xCJ1X.js.map +0 -1
- package/dist/constants-Dgv-tSO3.js.map +0 -1
- package/dist/index-CAikKppg.js.map +0 -1
- package/dist/index-OxV3e-OY.js.map +0 -1
- package/dist/seed.schema.config-Ba_E7t1M.js.map +0 -1
|
@@ -2,7 +2,7 @@ import { immerable, produce, enableMapSet } from 'immer';
|
|
|
2
2
|
import 'reflect-metadata';
|
|
3
3
|
import { Type } from '@sinclair/typebox';
|
|
4
4
|
import { fromCallback, setup, assign, waitFor, createActor, raise } from 'xstate';
|
|
5
|
-
import { b as BaseQueryClient, a as BaseEasClient, G as GLOBAL_INITIALIZING_INTERNAL_SERVICE_READY, f as GLOBAL_INITIALIZING_SEND_CONFIG, g as GLOBAL_INITIALIZING_CREATE_ALL_ITEMS_SERVICES, h as GLOBAL_ADDING_MODELS_TO_DB_SUCCESS, D as DB_CHECK_STATUS_UPDATE_PATHS, i as DB_CHECK_STATUS_EXISTS, d as BROWSER_FS_TOP_DIR, j as DB_CREATING_SUCCESS, k as DB_VALIDATING_SUCCESS, l as DB_VALIDATING_WAIT, m as DB_MIGRATING_SUCCESS, n as DB_WAITING_FOR_FILES_RECEIVED, M as MachineIds, o as DB_MIGRATING_WAIT, p as DbState, I as INTERNAL_VALIDATING_INPUT_SUCCESS, q as ARWEAVE_HOST, B as BaseArweaveClient,
|
|
5
|
+
import { b as BaseQueryClient, a as BaseEasClient, G as GLOBAL_INITIALIZING_INTERNAL_SERVICE_READY, f as GLOBAL_INITIALIZING_SEND_CONFIG, g as GLOBAL_INITIALIZING_CREATE_ALL_ITEMS_SERVICES, h as GLOBAL_ADDING_MODELS_TO_DB_SUCCESS, D as DB_CHECK_STATUS_UPDATE_PATHS, i as DB_CHECK_STATUS_EXISTS, d as BROWSER_FS_TOP_DIR, j as DB_CREATING_SUCCESS, k as DB_VALIDATING_SUCCESS, l as DB_VALIDATING_WAIT, c as BaseFileManager, m as DB_MIGRATING_SUCCESS, n as DB_WAITING_FOR_FILES_RECEIVED, M as MachineIds, o as DB_MIGRATING_WAIT, p as DbState, I as INTERNAL_VALIDATING_INPUT_SUCCESS, q as ARWEAVE_HOST, B as BaseArweaveClient, r as INTERNAL_CONFIGURING_FS_SUCCESS, s as INTERNAL_SAVING_CONFIG_SUCCESS, t as INTERNAL_LOADING_APP_DB_SUCCESS, u as DB_ON_SNAPSHOT, v as DB_NAME_APP, w as InternalState, P as PublishMachineStates, x as GlobalState, y as INTERNAL_DATA_TYPES, z as ImageSize, C as internalPropertyNames, V as VERSION_SCHEMA_UID_OPTIMISM_SEPOLIA, F as defaultAttestationData } from './constants-C03RQQht.js';
|
|
6
6
|
import debug from 'debug';
|
|
7
7
|
import pluralize from 'pluralize';
|
|
8
8
|
import { sqliteTable, text, int, unique } from 'drizzle-orm/sqlite-core';
|
|
@@ -10,10 +10,9 @@ import { relations, or, and, isNotNull, not, eq, inArray, sql, like, getTableCol
|
|
|
10
10
|
import EventEmitter from 'eventemitter3';
|
|
11
11
|
import { customAlphabet } from 'nanoid';
|
|
12
12
|
import * as nanoIdDictionary from 'nanoid-dictionary';
|
|
13
|
-
import
|
|
13
|
+
import fs from '@zenfs/core';
|
|
14
14
|
import { throttle, camelCase, startCase, orderBy } from 'lodash-es';
|
|
15
15
|
import { createBrowserInspector } from '@statelyai/inspect';
|
|
16
|
-
import * as fsNode from 'node:fs';
|
|
17
16
|
import path from 'path';
|
|
18
17
|
import Arweave from 'arweave';
|
|
19
18
|
import { BehaviorSubject } from 'rxjs';
|
|
@@ -124,6 +123,11 @@ class BaseDb {
|
|
|
124
123
|
}
|
|
125
124
|
}
|
|
126
125
|
|
|
126
|
+
var BaseDb$1 = /*#__PURE__*/Object.freeze({
|
|
127
|
+
__proto__: null,
|
|
128
|
+
BaseDb: BaseDb
|
|
129
|
+
});
|
|
130
|
+
|
|
127
131
|
debug('app:item:service:actors:waitForDb');
|
|
128
132
|
const waitForDb$1 = fromCallback(({ sendBack }) => {
|
|
129
133
|
const _waitForDb = new Promise((resolve) => {
|
|
@@ -1345,7 +1349,7 @@ const addModelsToDb = fromCallback(({ sendBack, input: { context } }) => {
|
|
|
1345
1349
|
if (!models$1) {
|
|
1346
1350
|
return;
|
|
1347
1351
|
}
|
|
1348
|
-
const { models: SeedModels } = await import('./seed.schema.config-
|
|
1352
|
+
const { models: SeedModels } = await import('./seed.schema.config-CS6BvsTl.js');
|
|
1349
1353
|
const allModels = {
|
|
1350
1354
|
...SeedModels,
|
|
1351
1355
|
...models$1,
|
|
@@ -1522,57 +1526,962 @@ const validate = fromCallback(({ sendBack, input: { context } }) => {
|
|
|
1522
1526
|
});
|
|
1523
1527
|
});
|
|
1524
1528
|
|
|
1525
|
-
const
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1529
|
+
const saveAppState = async (key, value) => {
|
|
1530
|
+
const appDb = BaseDb.getAppDb();
|
|
1531
|
+
await appDb
|
|
1532
|
+
.insert(appState)
|
|
1533
|
+
.values({
|
|
1534
|
+
key,
|
|
1535
|
+
value,
|
|
1536
|
+
})
|
|
1537
|
+
.onConflictDoUpdate({
|
|
1538
|
+
target: appState.key,
|
|
1539
|
+
set: {
|
|
1540
|
+
value,
|
|
1541
|
+
},
|
|
1542
|
+
});
|
|
1543
|
+
};
|
|
1544
|
+
|
|
1545
|
+
var filesDownload = `(
|
|
1546
|
+
${function () {
|
|
1547
|
+
const identifyString = (str) => {
|
|
1548
|
+
try {
|
|
1549
|
+
JSON.parse(str);
|
|
1550
|
+
return 'json';
|
|
1551
|
+
}
|
|
1552
|
+
catch (e) {
|
|
1553
|
+
// Not JSON
|
|
1554
|
+
}
|
|
1555
|
+
if (!str) {
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
if (str.trim().startsWith('<') && str.trim().endsWith('>')) {
|
|
1559
|
+
return 'html';
|
|
1560
|
+
}
|
|
1561
|
+
// Simple markdown checks (very naive)
|
|
1562
|
+
if (/^#{1,6}\s|^-{3,}|\*{3,}|^-{1,2}\s|\*\s/.test(str)) {
|
|
1563
|
+
return 'markdown';
|
|
1564
|
+
}
|
|
1565
|
+
if (/^data:image\/[a-zA-Z]+;base64,[A-Za-z0-9+/]+={0,2}$/.test(str)) {
|
|
1566
|
+
return 'base64';
|
|
1567
|
+
}
|
|
1568
|
+
// Default to plain text if unsure
|
|
1569
|
+
return 'text';
|
|
1570
|
+
};
|
|
1571
|
+
const getMimeType = (base64) => {
|
|
1572
|
+
if (!base64) {
|
|
1573
|
+
return null;
|
|
1574
|
+
}
|
|
1575
|
+
const result = base64.match(/^data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,/);
|
|
1576
|
+
if (result && result.length > 1) {
|
|
1577
|
+
return result[1];
|
|
1578
|
+
}
|
|
1579
|
+
else {
|
|
1580
|
+
return null; // MIME type could not be determined
|
|
1581
|
+
}
|
|
1582
|
+
};
|
|
1583
|
+
const getDataTypeFromString = (data) => {
|
|
1584
|
+
const nonImageBase64Regex = /^(?!data:image\/(?:jpeg|png|gif|bmp|webp);base64,)[A-Za-z0-9+/=]+$/;
|
|
1585
|
+
if (nonImageBase64Regex.test(data)) {
|
|
1586
|
+
return 'base64';
|
|
1587
|
+
}
|
|
1588
|
+
// Regular expression for base64 (simple version, checking for base64 format)
|
|
1589
|
+
const imageBase64Regex = /^data:image\/[a-zA-Z]+;base64,[A-Za-z0-9+/]+={0,2}$/;
|
|
1590
|
+
if (imageBase64Regex.test(data)) {
|
|
1591
|
+
return 'imageBase64';
|
|
1592
|
+
}
|
|
1593
|
+
// Regular expression for URL (simple version, checking for common URL format)
|
|
1594
|
+
const urlRegex = /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/;
|
|
1595
|
+
if (urlRegex.test(data)) {
|
|
1596
|
+
return 'url';
|
|
1597
|
+
}
|
|
1598
|
+
return null;
|
|
1599
|
+
};
|
|
1600
|
+
const isBinary = (arrayBuffer) => {
|
|
1601
|
+
const view = new Uint8Array(arrayBuffer);
|
|
1602
|
+
let nonTextCount = 0;
|
|
1603
|
+
const threshold = 0.2; // Adjust as needed (e.g., 20% non-text implies binary)
|
|
1604
|
+
for (let i = 0; i < view.length; i++) {
|
|
1605
|
+
const byte = view[i];
|
|
1606
|
+
// ASCII printable characters (32-126) and common whitespace (9, 10, 13)
|
|
1607
|
+
if ((byte >= 32 && byte <= 126) || // Printable ASCII
|
|
1608
|
+
byte === 9 || byte === 10 || byte === 13 // Tab, LF, CR
|
|
1609
|
+
) {
|
|
1610
|
+
continue;
|
|
1541
1611
|
}
|
|
1542
|
-
|
|
1543
|
-
if (
|
|
1544
|
-
|
|
1545
|
-
resolve(true);
|
|
1612
|
+
nonTextCount++;
|
|
1613
|
+
if (nonTextCount / view.length > threshold) {
|
|
1614
|
+
return true; // More than threshold are non-text bytes
|
|
1546
1615
|
}
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1616
|
+
}
|
|
1617
|
+
return false; // Fewer than threshold are non-text bytes
|
|
1618
|
+
};
|
|
1619
|
+
const saveBufferToOPFS = async (filePath, buffer) => {
|
|
1620
|
+
// Access the OPFS root directory
|
|
1621
|
+
const rootHandle = await navigator.storage.getDirectory();
|
|
1622
|
+
// Split the filePath into directory segments and file name
|
|
1623
|
+
const segments = filePath.split('/').filter(Boolean);
|
|
1624
|
+
const fileName = segments.pop(); // Extract the file name
|
|
1625
|
+
if (!fileName) {
|
|
1626
|
+
throw new Error('Invalid file path: No file name provided.');
|
|
1627
|
+
}
|
|
1628
|
+
// Traverse or create directories as needed
|
|
1629
|
+
let currentDirHandle = rootHandle;
|
|
1630
|
+
for (const segment of segments) {
|
|
1631
|
+
currentDirHandle = await currentDirHandle.getDirectoryHandle(segment, { create: true });
|
|
1632
|
+
}
|
|
1633
|
+
// Create or open the file in OPFS
|
|
1634
|
+
const fileHandleAsync = await currentDirHandle.getFileHandle(fileName, { create: true });
|
|
1635
|
+
const fileHandle = await fileHandleAsync.createSyncAccessHandle();
|
|
1636
|
+
// Write the buffer to the file
|
|
1637
|
+
fileHandle.write(buffer);
|
|
1638
|
+
fileHandle.flush();
|
|
1639
|
+
fileHandle.close();
|
|
1640
|
+
};
|
|
1641
|
+
const downloadFiles = async ({ transactionIds, arweaveHost, }) => {
|
|
1642
|
+
let arrayBuffer;
|
|
1643
|
+
for (const transactionId of transactionIds) {
|
|
1644
|
+
try {
|
|
1645
|
+
const response = await fetch(`https://${arweaveHost}/raw/${transactionId}`);
|
|
1646
|
+
arrayBuffer = await response.arrayBuffer();
|
|
1550
1647
|
}
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1648
|
+
catch (error) {
|
|
1649
|
+
console.log(`[filesDownload] transaction ${transactionId} data not found`, error);
|
|
1650
|
+
globalThis.postMessage({
|
|
1651
|
+
message: 'excludeTransaction',
|
|
1652
|
+
transactionId,
|
|
1653
|
+
});
|
|
1654
|
+
continue;
|
|
1655
|
+
}
|
|
1656
|
+
let dataString;
|
|
1657
|
+
const isBinaryData = isBinary(arrayBuffer);
|
|
1658
|
+
if (!isBinaryData) {
|
|
1659
|
+
const decoder = new TextDecoder('utf-8');
|
|
1660
|
+
const text = decoder.decode(arrayBuffer);
|
|
1661
|
+
dataString = text;
|
|
1662
|
+
}
|
|
1663
|
+
if (!dataString && !arrayBuffer) {
|
|
1664
|
+
console.log(`[filesDownload] transaction ${transactionId} data not found`);
|
|
1665
|
+
}
|
|
1666
|
+
if (dataString && dataString.startsWith('===FILE_SEPARATOR===')) {
|
|
1667
|
+
const dataStringParts = dataString
|
|
1668
|
+
.split('===FILE_SEPARATOR===')
|
|
1669
|
+
.slice(1);
|
|
1670
|
+
if (dataStringParts.length % 2 !== 0) {
|
|
1671
|
+
throw new Error('Input array must have an even number of elements.');
|
|
1672
|
+
}
|
|
1673
|
+
for (let i = 0; i < dataStringParts.length; i += 2) {
|
|
1674
|
+
const contentType = dataStringParts[i];
|
|
1675
|
+
const content = dataStringParts[i + 1];
|
|
1676
|
+
const encoder = new TextEncoder();
|
|
1677
|
+
if (contentType === 'html') {
|
|
1678
|
+
const fileName = `${transactionId}.html`;
|
|
1679
|
+
const buffer = encoder.encode(content);
|
|
1680
|
+
saveBufferToOPFS(`/files/html/${fileName}`, buffer);
|
|
1681
|
+
}
|
|
1682
|
+
if (contentType === 'json') {
|
|
1683
|
+
const fileName = `${transactionId}.json`;
|
|
1684
|
+
const buffer = encoder.encode(content);
|
|
1685
|
+
saveBufferToOPFS(`/files/json/${fileName}`, buffer);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
continue;
|
|
1689
|
+
}
|
|
1690
|
+
if (!dataString && arrayBuffer) {
|
|
1691
|
+
saveBufferToOPFS(`/files/images/${transactionId}`, new Uint8Array(arrayBuffer));
|
|
1692
|
+
continue;
|
|
1693
|
+
}
|
|
1694
|
+
if (!dataString) {
|
|
1695
|
+
continue;
|
|
1696
|
+
}
|
|
1697
|
+
let contentType = identifyString(dataString);
|
|
1698
|
+
if (contentType !== 'json' &&
|
|
1699
|
+
contentType !== 'base64' &&
|
|
1700
|
+
contentType !== 'html') {
|
|
1701
|
+
const possibleImageType = getDataTypeFromString(dataString);
|
|
1702
|
+
if (!possibleImageType) {
|
|
1703
|
+
console.log(`[filesDownload] transaction ${transactionId} data not in expected format: ${possibleImageType}`);
|
|
1704
|
+
continue;
|
|
1705
|
+
}
|
|
1706
|
+
contentType = possibleImageType;
|
|
1707
|
+
}
|
|
1708
|
+
if (contentType === 'url') {
|
|
1709
|
+
const url = dataString;
|
|
1710
|
+
let buffer;
|
|
1711
|
+
try {
|
|
1712
|
+
const response = await fetch(url);
|
|
1713
|
+
buffer = await response.arrayBuffer();
|
|
1714
|
+
}
|
|
1715
|
+
catch (error) {
|
|
1716
|
+
console.log(`[filesDownload] transaction ${transactionId} value was url: ${dataString} but failed to fetch`, error);
|
|
1717
|
+
globalThis.postMessage({
|
|
1718
|
+
message: 'excludeTransaction',
|
|
1719
|
+
transactionId,
|
|
1720
|
+
});
|
|
1721
|
+
continue;
|
|
1722
|
+
}
|
|
1723
|
+
const bufferUint8Array = new Uint8Array(buffer);
|
|
1724
|
+
// Extract the file extension from the URL
|
|
1725
|
+
const extensionMatch = url.match(/\.(jpg|jpeg|png|gif|bmp|webp|svg)$/i);
|
|
1726
|
+
if (!extensionMatch) {
|
|
1727
|
+
throw new Error('Unable to determine the file extension from the URL.');
|
|
1728
|
+
}
|
|
1729
|
+
extensionMatch[0]; // e.g., ".jpg"
|
|
1730
|
+
// Set the file name (you can customize this)
|
|
1731
|
+
// const fileNameFromUrl = `${transactionId}${fileExtension}`
|
|
1732
|
+
await saveBufferToOPFS(`/files/images/${transactionId}`, bufferUint8Array);
|
|
1733
|
+
continue;
|
|
1734
|
+
}
|
|
1735
|
+
const mimeType = getMimeType(dataString);
|
|
1736
|
+
let fileExtension = mimeType;
|
|
1737
|
+
if (fileExtension && fileExtension?.startsWith('image')) {
|
|
1738
|
+
fileExtension = fileExtension.replace('image/', '');
|
|
1739
|
+
}
|
|
1740
|
+
let fileName = transactionId;
|
|
1741
|
+
if (contentType === 'base64') {
|
|
1742
|
+
if (fileExtension) {
|
|
1743
|
+
fileName += `.${fileExtension}`;
|
|
1744
|
+
}
|
|
1745
|
+
// Remove the Base64 header if it exists (e.g., "data:image/png;base64,")
|
|
1746
|
+
const base64Data = dataString.split(',').pop() || '';
|
|
1747
|
+
// Decode the Base64 string to binary
|
|
1748
|
+
const binaryString = atob(base64Data);
|
|
1749
|
+
const length = binaryString.length;
|
|
1750
|
+
const binaryData = new Uint8Array(length);
|
|
1751
|
+
for (let i = 0; i < length; i++) {
|
|
1752
|
+
binaryData[i] = binaryString.charCodeAt(i);
|
|
1753
|
+
}
|
|
1754
|
+
await saveBufferToOPFS(`/files/images/${fileName}`, binaryData);
|
|
1755
|
+
}
|
|
1756
|
+
if (contentType === 'html') {
|
|
1757
|
+
fileName += '.html';
|
|
1758
|
+
const encoder = new TextEncoder();
|
|
1759
|
+
const buffer = encoder.encode(dataString);
|
|
1760
|
+
await saveBufferToOPFS(`/files/html/${fileName}`, buffer);
|
|
1761
|
+
}
|
|
1762
|
+
if (contentType === 'json') {
|
|
1763
|
+
fileName += '.json';
|
|
1764
|
+
const encoder = new TextEncoder();
|
|
1765
|
+
const buffer = encoder.encode(dataString);
|
|
1766
|
+
await saveBufferToOPFS(`/files/json/${fileName}`, buffer);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
};
|
|
1770
|
+
onmessage = async (e) => {
|
|
1771
|
+
console.log({
|
|
1772
|
+
message: 'filesDownload onmessage',
|
|
1773
|
+
data: e.data,
|
|
1774
|
+
});
|
|
1775
|
+
await downloadFiles(e.data);
|
|
1776
|
+
globalThis.postMessage({
|
|
1777
|
+
message: 'filesDownload onmessage done',
|
|
1778
|
+
done: true,
|
|
1779
|
+
});
|
|
1780
|
+
};
|
|
1781
|
+
}.toString()}
|
|
1782
|
+
)()`;
|
|
1783
|
+
|
|
1784
|
+
class FileDownloader {
|
|
1785
|
+
constructor() {
|
|
1786
|
+
this.workersArchive = [];
|
|
1787
|
+
this.downloadAll = async ({ transactionIds, arweaveHost, excludedTransactions }) => {
|
|
1788
|
+
if (this.workersArchive.length > 0) {
|
|
1789
|
+
for (let i = 0; i < this.workersArchive.length; i++) {
|
|
1790
|
+
this.workersArchive[i].terminate();
|
|
1791
|
+
delete this.workersArchive[i];
|
|
1792
|
+
}
|
|
1793
|
+
this.workersArchive = [];
|
|
1794
|
+
}
|
|
1795
|
+
const worker = new Worker(this.workerBlobUrl);
|
|
1796
|
+
this.workersArchive.push(worker);
|
|
1797
|
+
const localExcludedTransactions = new Set(excludedTransactions);
|
|
1798
|
+
return new Promise((resolve, reject) => {
|
|
1799
|
+
worker.onmessage = (e) => {
|
|
1800
|
+
console.log('filesDownload main thread onmessage', e.data);
|
|
1801
|
+
if (e.data.message === 'excludeTransaction') {
|
|
1802
|
+
localExcludedTransactions.add(e.data.transactionId);
|
|
1803
|
+
}
|
|
1804
|
+
if (e.data.done) {
|
|
1805
|
+
saveAppState('excludedTransactions', JSON.stringify(Array.from(localExcludedTransactions)))
|
|
1806
|
+
.then(() => {
|
|
1807
|
+
resolve(e.data);
|
|
1808
|
+
})
|
|
1809
|
+
.catch((error) => {
|
|
1810
|
+
reject(error);
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1813
|
+
if (e.data.error) {
|
|
1814
|
+
reject(e.data.error);
|
|
1815
|
+
}
|
|
1816
|
+
};
|
|
1817
|
+
worker.postMessage({
|
|
1818
|
+
transactionIds,
|
|
1819
|
+
arweaveHost,
|
|
1820
|
+
});
|
|
1821
|
+
});
|
|
1822
|
+
};
|
|
1823
|
+
this.cores = Math.min(navigator.hardwareConcurrency || 4, 4);
|
|
1824
|
+
this.workerBlobUrl = globalThis.URL.createObjectURL(new Blob([filesDownload], { type: 'application/javascript' }));
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
var imageResize = `(
|
|
1829
|
+
${function () {
|
|
1830
|
+
async function listFilesInDirectory(directoryHandle) {
|
|
1831
|
+
const entries = [];
|
|
1832
|
+
for await (const [name, handle] of directoryHandle.entries()) {
|
|
1833
|
+
entries.push({
|
|
1834
|
+
name,
|
|
1835
|
+
kind: handle.kind,
|
|
1836
|
+
});
|
|
1837
|
+
}
|
|
1838
|
+
return entries;
|
|
1839
|
+
}
|
|
1840
|
+
const getFileHandle = async (path, rootHandle = null) => {
|
|
1841
|
+
// Split the path into segments
|
|
1842
|
+
const segments = path.split('/').filter(Boolean);
|
|
1843
|
+
// Start from the root directory if not provided
|
|
1844
|
+
if (!rootHandle) {
|
|
1845
|
+
rootHandle = await navigator.storage.getDirectory();
|
|
1846
|
+
}
|
|
1847
|
+
let currentHandle = rootHandle;
|
|
1848
|
+
// Traverse the path segments
|
|
1849
|
+
for (let i = 0; i < segments.length; i++) {
|
|
1850
|
+
const segment = segments[i];
|
|
1851
|
+
const isLastSegment = i === segments.length - 1;
|
|
1852
|
+
try {
|
|
1853
|
+
for await (const [name, handle] of currentHandle.entries()) {
|
|
1854
|
+
if (name !== segment) {
|
|
1855
|
+
continue;
|
|
1856
|
+
}
|
|
1857
|
+
if (isLastSegment) {
|
|
1858
|
+
if (handle.kind === 'file') {
|
|
1859
|
+
return handle; // Return the file handle if found
|
|
1860
|
+
}
|
|
1861
|
+
else {
|
|
1862
|
+
throw new Error(`Path '${path}' refers to a directory, not a file.`);
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
else if (handle.kind === 'directory') {
|
|
1866
|
+
currentHandle = handle; // Traverse into the directory
|
|
1867
|
+
}
|
|
1868
|
+
else {
|
|
1869
|
+
throw new Error(`Invalid path segment '${segment}'`);
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
catch (err) {
|
|
1874
|
+
if (err instanceof Error && err.name === 'NotFoundError') {
|
|
1875
|
+
throw new Error(`Path '${path}' does not exist.`);
|
|
1876
|
+
}
|
|
1877
|
+
else {
|
|
1878
|
+
throw err;
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
throw new Error(`Path '${path}' could not be resolved.`);
|
|
1883
|
+
};
|
|
1884
|
+
async function getFileFromOPFS(path) {
|
|
1885
|
+
const fileHandleAsync = await getFileHandle(path);
|
|
1886
|
+
const file = await fileHandleAsync.getFile();
|
|
1887
|
+
return file;
|
|
1888
|
+
}
|
|
1889
|
+
const DEFAULT_CONFIG = {
|
|
1890
|
+
argorithm: 'null',
|
|
1891
|
+
processByHalf: true,
|
|
1892
|
+
quality: 0.5,
|
|
1893
|
+
maxWidth: 800,
|
|
1894
|
+
maxHeight: 600,
|
|
1895
|
+
debug: false,
|
|
1896
|
+
mimeType: 'image/jpeg',
|
|
1897
|
+
};
|
|
1898
|
+
function isIos() {
|
|
1899
|
+
if (typeof navigator === 'undefined')
|
|
1900
|
+
return false;
|
|
1901
|
+
if (!navigator.userAgent)
|
|
1902
|
+
return false;
|
|
1903
|
+
return /iPad|iPhone|iPod/.test(navigator.userAgent);
|
|
1904
|
+
}
|
|
1905
|
+
const getTargetHeight = (srcHeight, scale, config) => {
|
|
1906
|
+
return Math.min(Math.floor(srcHeight * scale), config.maxHeight);
|
|
1907
|
+
};
|
|
1908
|
+
const findMaxWidth = (config, canvas) => {
|
|
1909
|
+
//Let's find the max available width for scaled image
|
|
1910
|
+
const ratio = canvas.width / canvas.height;
|
|
1911
|
+
let mWidth = Math.min(canvas.width, config.maxWidth, ratio * config.maxHeight);
|
|
1912
|
+
if (config.maxSize &&
|
|
1913
|
+
config.maxSize > 0 &&
|
|
1914
|
+
config.maxSize < (canvas.width * canvas.height) / 1000)
|
|
1915
|
+
mWidth = Math.min(mWidth, Math.floor((config.maxSize * 1000) / canvas.height));
|
|
1916
|
+
if (!!config.scaleRatio)
|
|
1917
|
+
mWidth = Math.min(mWidth, Math.floor(config.scaleRatio * canvas.width));
|
|
1918
|
+
const rHeight = getTargetHeight(canvas.height, mWidth / canvas.width, config);
|
|
1919
|
+
// console.log(
|
|
1920
|
+
// 'browser-image-resizer: original image size = ' +
|
|
1921
|
+
// canvas.width +
|
|
1922
|
+
// ' px (width) X ' +
|
|
1923
|
+
// canvas.height +
|
|
1924
|
+
// ' px (height)'
|
|
1925
|
+
// );
|
|
1926
|
+
// console.log(
|
|
1927
|
+
// 'browser-image-resizer: scaled image size = ' +
|
|
1928
|
+
// mWidth +
|
|
1929
|
+
// ' px (width) X ' +
|
|
1930
|
+
// rHeight +
|
|
1931
|
+
// ' px (height)'
|
|
1932
|
+
// );
|
|
1933
|
+
if (mWidth <= 0) {
|
|
1934
|
+
mWidth = 1;
|
|
1935
|
+
console.warn("browser-image-resizer: image size is too small");
|
|
1936
|
+
}
|
|
1937
|
+
if (isIos() && mWidth * rHeight > 167777216) {
|
|
1938
|
+
console.error("browser-image-resizer: image size is too large for iOS WebKit.", mWidth, rHeight);
|
|
1939
|
+
throw new Error("browser-image-resizer: image size is too large for iOS WebKit.");
|
|
1940
|
+
}
|
|
1941
|
+
return mWidth;
|
|
1942
|
+
};
|
|
1943
|
+
/**
|
|
1944
|
+
* Hermite resize, multicore version - fast image resize/resample using Hermite filter.
|
|
1945
|
+
*/
|
|
1946
|
+
const resample = (srcCanvas, destCanvas, config) => {
|
|
1947
|
+
return new Promise((resolve, reject) => {
|
|
1948
|
+
const ratio_h = srcCanvas.height / destCanvas.height;
|
|
1949
|
+
const cores = Math.min(navigator.hardwareConcurrency || 4, 4);
|
|
1950
|
+
//prepare source and target data for workers
|
|
1951
|
+
const ctx = srcCanvas.getContext('2d');
|
|
1952
|
+
if (!ctx)
|
|
1953
|
+
return reject('Canvas is empty (resample)');
|
|
1954
|
+
const data_part = [];
|
|
1955
|
+
const block_height = Math.ceil(srcCanvas.height / cores / 2) * 2;
|
|
1956
|
+
let end_y = -1;
|
|
1957
|
+
for (let c = 0; c < cores; c++) {
|
|
1958
|
+
//source
|
|
1959
|
+
const offset_y = end_y + 1;
|
|
1960
|
+
if (offset_y >= srcCanvas.height) {
|
|
1961
|
+
//size too small, nothing left for this core
|
|
1962
|
+
continue;
|
|
1963
|
+
}
|
|
1964
|
+
end_y = Math.min(offset_y + block_height - 1, srcCanvas.height - 1);
|
|
1965
|
+
const current_block_height = Math.min(block_height, srcCanvas.height - offset_y);
|
|
1966
|
+
console.log('browser-image-resizer: source split: ', '#' + c, offset_y, end_y, 'height: ' + current_block_height);
|
|
1967
|
+
data_part.push({
|
|
1968
|
+
source: ctx.getImageData(0, offset_y, srcCanvas.width, block_height),
|
|
1969
|
+
startY: Math.ceil(offset_y / ratio_h),
|
|
1970
|
+
height: current_block_height
|
|
1971
|
+
});
|
|
1972
|
+
}
|
|
1973
|
+
//start
|
|
1974
|
+
const destCtx = destCanvas.getContext('2d');
|
|
1975
|
+
if (!destCtx)
|
|
1976
|
+
return reject('Canvas is empty (resample dest)');
|
|
1977
|
+
for (let c = 0; c < data_part.length; c++) {
|
|
1978
|
+
//draw
|
|
1979
|
+
const height_part = Math.ceil(data_part[c].height / ratio_h);
|
|
1980
|
+
const target = destCtx.createImageData(destCanvas.width, height_part);
|
|
1981
|
+
// target.data.set(event.data.target);
|
|
1982
|
+
destCtx.putImageData(target, 0, data_part[c].startY);
|
|
1983
|
+
}
|
|
1984
|
+
});
|
|
1985
|
+
};
|
|
1986
|
+
/**
|
|
1987
|
+
* Hermite resize - fast image resize/resample using Hermite filter. 1 cpu version!
|
|
1988
|
+
*/
|
|
1989
|
+
const resampleSingle = (srcCanvasData, destCanvasData) => {
|
|
1990
|
+
const ratio_w = srcCanvasData.width / destCanvasData.width;
|
|
1991
|
+
const ratio_h = srcCanvasData.height / destCanvasData.height;
|
|
1992
|
+
const ratio_w_half = Math.ceil(ratio_w / 2);
|
|
1993
|
+
const ratio_h_half = Math.ceil(ratio_h / 2);
|
|
1994
|
+
const data = srcCanvasData.data;
|
|
1995
|
+
const data2 = destCanvasData.data;
|
|
1996
|
+
for (let j = 0; j < destCanvasData.height; j++) {
|
|
1997
|
+
for (let i = 0; i < destCanvasData.width; i++) {
|
|
1998
|
+
const x2 = (i + j * destCanvasData.width) * 4;
|
|
1999
|
+
let weight = 0;
|
|
2000
|
+
let weights = 0;
|
|
2001
|
+
let weights_alpha = 0;
|
|
2002
|
+
let gx_r = 0;
|
|
2003
|
+
let gx_g = 0;
|
|
2004
|
+
let gx_b = 0;
|
|
2005
|
+
let gx_a = 0;
|
|
2006
|
+
const center_y = j * ratio_h;
|
|
2007
|
+
const xx_start = Math.floor(i * ratio_w);
|
|
2008
|
+
const xx_stop = Math.min(Math.ceil((i + 1) * ratio_w), srcCanvasData.width);
|
|
2009
|
+
const yy_start = Math.floor(j * ratio_h);
|
|
2010
|
+
const yy_stop = Math.min(Math.ceil((j + 1) * ratio_h), srcCanvasData.height);
|
|
2011
|
+
for (let yy = yy_start; yy < yy_stop; yy++) {
|
|
2012
|
+
let dy = Math.abs(center_y - yy) / ratio_h_half;
|
|
2013
|
+
let center_x = i * ratio_w;
|
|
2014
|
+
let w0 = dy * dy; //pre-calc part of w
|
|
2015
|
+
for (let xx = xx_start; xx < xx_stop; xx++) {
|
|
2016
|
+
let dx = Math.abs(center_x - xx) / ratio_w_half;
|
|
2017
|
+
let w = Math.sqrt(w0 + dx * dx);
|
|
2018
|
+
if (w >= 1) {
|
|
2019
|
+
//pixel too far
|
|
2020
|
+
continue;
|
|
2021
|
+
}
|
|
2022
|
+
//hermite filter
|
|
2023
|
+
weight = 2 * w * w * w - 3 * w * w + 1;
|
|
2024
|
+
let pos_x = 4 * (xx + yy * srcCanvasData.width);
|
|
2025
|
+
//alpha
|
|
2026
|
+
gx_a += weight * data[pos_x + 3];
|
|
2027
|
+
weights_alpha += weight;
|
|
2028
|
+
//colors
|
|
2029
|
+
if (data[pos_x + 3] < 255)
|
|
2030
|
+
weight = weight * data[pos_x + 3] / 250;
|
|
2031
|
+
gx_r += weight * data[pos_x];
|
|
2032
|
+
gx_g += weight * data[pos_x + 1];
|
|
2033
|
+
gx_b += weight * data[pos_x + 2];
|
|
2034
|
+
weights += weight;
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
data2[x2] = gx_r / weights;
|
|
2038
|
+
data2[x2 + 1] = gx_g / weights;
|
|
2039
|
+
data2[x2 + 2] = gx_b / weights;
|
|
2040
|
+
data2[x2 + 3] = gx_a / weights_alpha;
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
};
|
|
2044
|
+
/**
|
|
2045
|
+
* Hermite resize. Detect cpu count and use best option for user.
|
|
2046
|
+
*/
|
|
2047
|
+
const resampleAuto = (srcCanvas, destCanvas, config) => {
|
|
2048
|
+
if (!!globalThis.Worker && navigator.hardwareConcurrency > 1 && config?.argorithm !== 'hermite_single') {
|
|
2049
|
+
//workers supported and we have at least 2 cpu cores - using multithreading
|
|
2050
|
+
return resample(srcCanvas, destCanvas);
|
|
2051
|
+
}
|
|
2052
|
+
else {
|
|
2053
|
+
//1 cpu version
|
|
2054
|
+
const { srcImgData, destImgData } = getImageData(srcCanvas, destCanvas);
|
|
2055
|
+
resampleSingle(srcImgData, destImgData);
|
|
2056
|
+
destCanvas.getContext('2d').putImageData(destImgData, 0, 0);
|
|
2057
|
+
return;
|
|
2058
|
+
}
|
|
2059
|
+
};
|
|
2060
|
+
async function scaleCanvasWithAlgorithm(canvas, config) {
|
|
2061
|
+
const scale = config.outputWidth / canvas.width;
|
|
2062
|
+
const scaled = new OffscreenCanvas(Math.floor(config.outputWidth), getTargetHeight(canvas.height, scale, config));
|
|
2063
|
+
switch (config.algorithm) {
|
|
2064
|
+
case 'hermite': {
|
|
2065
|
+
await resampleAuto(canvas, scaled, config);
|
|
2066
|
+
break;
|
|
2067
|
+
}
|
|
2068
|
+
case 'hermite_single': {
|
|
2069
|
+
const { srcImgData, destImgData } = getImageData(canvas, scaled);
|
|
2070
|
+
resampleSingle(srcImgData, destImgData);
|
|
2071
|
+
scaled?.getContext('2d')?.putImageData(destImgData, 0, 0);
|
|
2072
|
+
break;
|
|
2073
|
+
}
|
|
2074
|
+
case 'bilinear': {
|
|
2075
|
+
// const { srcImgData, destImgData } = getImageData(canvas, scaled);
|
|
2076
|
+
// bilinear(srcImgData, destImgData, scale);
|
|
2077
|
+
// scaled?.getContext('2d')?.putImageData(destImgData, 0, 0);
|
|
2078
|
+
break;
|
|
2079
|
+
}
|
|
2080
|
+
default: {
|
|
2081
|
+
scaled.getContext('2d')?.drawImage(canvas, 0, 0, scaled.width, scaled.height);
|
|
2082
|
+
break;
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
return scaled;
|
|
2086
|
+
}
|
|
2087
|
+
const getHalfScaleCanvas = (src) => {
|
|
2088
|
+
const half = new OffscreenCanvas(src.width / 2, src.height / 2);
|
|
2089
|
+
half
|
|
2090
|
+
?.getContext('2d')
|
|
2091
|
+
?.drawImage(src, 0, 0, half.width, half.height);
|
|
2092
|
+
return half;
|
|
2093
|
+
};
|
|
2094
|
+
const getImageData = (canvas, scaled) => {
|
|
2095
|
+
const srcImgData = canvas
|
|
2096
|
+
?.getContext('2d')
|
|
2097
|
+
?.getImageData(0, 0, canvas.width, canvas.height);
|
|
2098
|
+
const destImgData = scaled
|
|
2099
|
+
?.getContext('2d')
|
|
2100
|
+
?.createImageData(scaled.width, scaled.height);
|
|
2101
|
+
if (!srcImgData || !destImgData)
|
|
2102
|
+
throw Error('Canvas is empty (scaleCanvasWithAlgorithm). You should run this script after the document is ready.');
|
|
2103
|
+
return { srcImgData, destImgData };
|
|
2104
|
+
};
|
|
2105
|
+
async function saveBlobToOPFS(filePath, blob) {
|
|
2106
|
+
// Access the OPFS root directory
|
|
2107
|
+
const rootHandle = await navigator.storage.getDirectory();
|
|
2108
|
+
// Split the filePath into directory segments and file name
|
|
2109
|
+
const segments = filePath.split('/').filter(Boolean);
|
|
2110
|
+
const fileName = segments.pop(); // Extract the file name
|
|
2111
|
+
if (!fileName) {
|
|
2112
|
+
throw new Error('Invalid file path: No file name provided.');
|
|
2113
|
+
}
|
|
2114
|
+
// Traverse or create directories as needed
|
|
2115
|
+
let currentDirHandle = rootHandle;
|
|
2116
|
+
for (const segment of segments) {
|
|
2117
|
+
currentDirHandle = await currentDirHandle.getDirectoryHandle(segment, { create: true });
|
|
2118
|
+
}
|
|
2119
|
+
// Create or open the file in OPFS
|
|
2120
|
+
const fileHandle = await currentDirHandle.getFileHandle(fileName, { create: true });
|
|
2121
|
+
// Write the Blob to the file
|
|
2122
|
+
const writableStream = await fileHandle.createWritable();
|
|
2123
|
+
await writableStream.write(blob);
|
|
2124
|
+
await writableStream.close();
|
|
2125
|
+
}
|
|
2126
|
+
const imageResize = async (filePath, width, height) => {
|
|
2127
|
+
console.log({ filePath, width, height });
|
|
2128
|
+
const config = {
|
|
2129
|
+
...DEFAULT_CONFIG,
|
|
2130
|
+
algorithm: 'hermite_single',
|
|
2131
|
+
mimeType: 'image/webp',
|
|
2132
|
+
maxWidth: width,
|
|
2133
|
+
maxHeight: height,
|
|
2134
|
+
};
|
|
2135
|
+
const rootHandle = await navigator.storage.getDirectory();
|
|
2136
|
+
// List files in the root directory
|
|
2137
|
+
const files = await listFilesInDirectory(rootHandle);
|
|
2138
|
+
console.log({
|
|
2139
|
+
message: 'listFilesInDirectory',
|
|
2140
|
+
files
|
|
2141
|
+
});
|
|
2142
|
+
const file = await getFileFromOPFS(filePath);
|
|
2143
|
+
const imageBitmap = await createImageBitmap(file);
|
|
2144
|
+
let converting;
|
|
2145
|
+
if (isIos() && imageBitmap.width * imageBitmap.height > 16777216) {
|
|
2146
|
+
const scale = Math.sqrt(16777216 / (imageBitmap.width * imageBitmap.height));
|
|
2147
|
+
console.log(`browser-image-resizer: scale: Image is too large in iOS WebKit`);
|
|
2148
|
+
converting = new OffscreenCanvas(Math.floor(imageBitmap.width * scale), Math.floor(imageBitmap.height * scale));
|
|
2149
|
+
converting.getContext('2d')?.drawImage(imageBitmap, 0, 0, converting.width, converting.height);
|
|
2150
|
+
}
|
|
2151
|
+
else {
|
|
2152
|
+
converting = new OffscreenCanvas(imageBitmap.width, imageBitmap.height);
|
|
2153
|
+
converting.getContext('2d')?.drawImage(imageBitmap, 0, 0);
|
|
2154
|
+
}
|
|
2155
|
+
if (!converting?.getContext('2d')) {
|
|
2156
|
+
console.log('browser-image-resizer: Canvas Context is empty.');
|
|
2157
|
+
}
|
|
2158
|
+
const maxWidth = findMaxWidth(config, converting);
|
|
2159
|
+
if (!maxWidth) {
|
|
2160
|
+
throw Error(`browser-image-resizer: maxWidth is ${maxWidth}!!`);
|
|
2161
|
+
}
|
|
2162
|
+
while (config.processByHalf && converting.width >= 2 * maxWidth) {
|
|
2163
|
+
if (config.debug)
|
|
2164
|
+
console.log(`browser-image-resizer: scale: Scaling canvas by half from ${converting.width}`);
|
|
2165
|
+
converting = getHalfScaleCanvas(converting);
|
|
2166
|
+
}
|
|
2167
|
+
if (converting.width > maxWidth) {
|
|
2168
|
+
if (config.debug)
|
|
2169
|
+
console.log(`browser-image-resizer: scale: Scaling canvas by ${config.argorithm} from ${converting.width} to ${maxWidth}`);
|
|
2170
|
+
converting = await scaleCanvasWithAlgorithm(converting, Object.assign(config, { outputWidth: maxWidth }));
|
|
2171
|
+
}
|
|
2172
|
+
if (config.mimeType === null) {
|
|
2173
|
+
return converting;
|
|
2174
|
+
}
|
|
2175
|
+
const resizedBlob = await converting.convertToBlob({ type: config.mimeType, quality: config.quality });
|
|
2176
|
+
const pathSegments = filePath.split('/');
|
|
2177
|
+
const fileName = pathSegments.pop();
|
|
2178
|
+
if (!fileName) {
|
|
2179
|
+
throw Error('Invalid file path: No file name provided.');
|
|
2180
|
+
}
|
|
2181
|
+
const newSegments = [
|
|
2182
|
+
...pathSegments,
|
|
2183
|
+
width,
|
|
2184
|
+
];
|
|
2185
|
+
const fileNameParts = fileName.split('.');
|
|
2186
|
+
const newFileName = `${fileNameParts[0]}.webp`;
|
|
2187
|
+
const newDirPath = newSegments.join('/');
|
|
2188
|
+
const newFilePath = `${newDirPath}/${newFileName}`;
|
|
2189
|
+
// Save resized image to OPFS with new name
|
|
2190
|
+
await saveBlobToOPFS(newFilePath, resizedBlob);
|
|
2191
|
+
globalThis.postMessage({
|
|
2192
|
+
done: true,
|
|
2193
|
+
filePath: newFilePath,
|
|
2194
|
+
});
|
|
2195
|
+
};
|
|
2196
|
+
onmessage = async (e) => {
|
|
2197
|
+
console.log('[imageResize] onmessage', e.data);
|
|
2198
|
+
const { filePath, width, height } = e.data;
|
|
2199
|
+
await imageResize(filePath, width, height);
|
|
2200
|
+
console.log(`[imageResize] Done`, filePath);
|
|
2201
|
+
};
|
|
2202
|
+
}.toString()}
|
|
2203
|
+
)()`;
|
|
2204
|
+
|
|
2205
|
+
class ImageResizer {
|
|
2206
|
+
constructor() {
|
|
2207
|
+
this.workersArchive = new Map();
|
|
2208
|
+
this.cores = Math.min(navigator.hardwareConcurrency || 4, 4);
|
|
2209
|
+
this.workerBlobUrl = globalThis.URL.createObjectURL(new Blob([imageResize], { type: 'application/javascript' }));
|
|
2210
|
+
}
|
|
2211
|
+
async resize({ filePath, width, height }) {
|
|
2212
|
+
if (this.workersArchive.has(filePath)) {
|
|
2213
|
+
const savedWorker = this.workersArchive.get(filePath);
|
|
2214
|
+
savedWorker?.terminate();
|
|
2215
|
+
console.log('[ImageResizer.resize] Terminated worker for filePath due to incoming request', filePath);
|
|
2216
|
+
this.workersArchive.delete(filePath);
|
|
2217
|
+
}
|
|
2218
|
+
const worker = new Worker(this.workerBlobUrl);
|
|
2219
|
+
this.workersArchive.set(filePath, worker);
|
|
2220
|
+
return new Promise((resolve, reject) => {
|
|
2221
|
+
worker.onmessage = (e) => {
|
|
2222
|
+
console.log('[ImageResizer.resize] main thread onmessage', e.data);
|
|
2223
|
+
if (e.data.done) {
|
|
2224
|
+
const savedWorker = this.workersArchive.get(filePath);
|
|
2225
|
+
savedWorker?.terminate();
|
|
2226
|
+
console.log('[ImageResizer.resize] Terminated worker for filePath due to done', filePath);
|
|
2227
|
+
this.workersArchive.delete(filePath);
|
|
2228
|
+
resolve(e.data);
|
|
2229
|
+
}
|
|
2230
|
+
if (e.data.error) {
|
|
2231
|
+
reject(e.data.error);
|
|
2232
|
+
}
|
|
2233
|
+
};
|
|
2234
|
+
worker.postMessage({
|
|
2235
|
+
filePath,
|
|
2236
|
+
width,
|
|
2237
|
+
height,
|
|
2238
|
+
});
|
|
2239
|
+
});
|
|
2240
|
+
}
|
|
2241
|
+
async resizeAll({ width, height }) {
|
|
2242
|
+
const imageDir = '/files/images';
|
|
2243
|
+
let imageFilesStats = await fs.promises.readdir(imageDir, {
|
|
2244
|
+
withFileTypes: true
|
|
2245
|
+
});
|
|
2246
|
+
imageFilesStats = imageFilesStats.filter(file => file.isFile());
|
|
2247
|
+
const imageFiles = imageFilesStats.map(file => file.path);
|
|
2248
|
+
const widthDir = `${imageDir}/${width}`;
|
|
2249
|
+
await FileManager.createDirIfNotExists(widthDir);
|
|
2250
|
+
for (const imageFile of imageFiles) {
|
|
2251
|
+
const resizedImageExists = await fs.promises.exists(`${widthDir}/${imageFile}`);
|
|
2252
|
+
if (!resizedImageExists) {
|
|
2253
|
+
await this.resize({ filePath: `${imageDir}/${imageFile}`, width, height });
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
const logger$k = debug('app:browser:helpers:FileManager');
|
|
2260
|
+
class FileManager extends BaseFileManager {
|
|
2261
|
+
static async readFileAsBuffer(filePath) {
|
|
2262
|
+
return new Promise((resolve, reject) => {
|
|
2263
|
+
reject(new Error('Not implemented'));
|
|
2264
|
+
});
|
|
2265
|
+
}
|
|
2266
|
+
static async getContentUrlFromPath(path) {
|
|
2267
|
+
const fs = await import('@zenfs/core');
|
|
2268
|
+
const fileExists = await fs.promises.exists(path);
|
|
2269
|
+
if (fileExists) {
|
|
2270
|
+
const fileContents = await fs.promises.readFile(path);
|
|
2271
|
+
const fileHandler = new File([fileContents], path);
|
|
2272
|
+
return URL.createObjectURL(fileHandler);
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
static async initializeFileSystem() {
|
|
2276
|
+
const { WebAccess } = await import('@zenfs/dom');
|
|
2277
|
+
const { configureSingle } = await import('@zenfs/core');
|
|
2278
|
+
const handle = await navigator.storage.getDirectory();
|
|
2279
|
+
// await configure({
|
|
2280
|
+
// mounts: {
|
|
2281
|
+
// '/': {
|
|
2282
|
+
// backend: WebAccess,
|
|
2283
|
+
// handle,
|
|
2284
|
+
// },
|
|
1572
2285
|
// },
|
|
1573
|
-
//
|
|
1574
|
-
|
|
1575
|
-
}
|
|
2286
|
+
// disableUpdateOnRead: true,
|
|
2287
|
+
// onlySyncOnClose: true,
|
|
2288
|
+
// })
|
|
2289
|
+
await configureSingle({
|
|
2290
|
+
backend: WebAccess,
|
|
2291
|
+
handle,
|
|
2292
|
+
});
|
|
2293
|
+
}
|
|
2294
|
+
static async downloadAllFiles({ transactionIds, arweaveHost, excludedTransactions, }) {
|
|
2295
|
+
const fileDownloader = new FileDownloader();
|
|
2296
|
+
await fileDownloader.downloadAll({ transactionIds, arweaveHost, excludedTransactions });
|
|
2297
|
+
}
|
|
2298
|
+
static async resizeImage({ filePath, width, height }) {
|
|
2299
|
+
const imageResizer = new ImageResizer();
|
|
2300
|
+
await imageResizer.resize({ filePath, width, height });
|
|
2301
|
+
}
|
|
2302
|
+
static async resizeAllImages({ width, height }) {
|
|
2303
|
+
const imageResizer = new ImageResizer();
|
|
2304
|
+
await imageResizer.resizeAll({ width, height });
|
|
2305
|
+
}
|
|
2306
|
+
static async pathExists(filePath) {
|
|
2307
|
+
try {
|
|
2308
|
+
// Access the root directory of OPFS
|
|
2309
|
+
const root = await navigator.storage.getDirectory();
|
|
2310
|
+
// Split the path into segments
|
|
2311
|
+
const parts = filePath.split('/').filter(Boolean);
|
|
2312
|
+
let currentDir = root;
|
|
2313
|
+
// Traverse each part of the path
|
|
2314
|
+
for (let i = 0; i < parts.length; i++) {
|
|
2315
|
+
const part = parts[i];
|
|
2316
|
+
try {
|
|
2317
|
+
const handle = await currentDir.getDirectoryHandle(part, { create: false });
|
|
2318
|
+
currentDir = handle; // Move into the directory
|
|
2319
|
+
}
|
|
2320
|
+
catch (error) {
|
|
2321
|
+
try {
|
|
2322
|
+
// If it's not a directory, check if it's a file
|
|
2323
|
+
await currentDir.getFileHandle(part, { create: false });
|
|
2324
|
+
// If we successfully got a file handle and it's the last part, return true
|
|
2325
|
+
return i === parts.length - 1;
|
|
2326
|
+
}
|
|
2327
|
+
catch {
|
|
2328
|
+
// Neither a directory nor a file exists
|
|
2329
|
+
return false;
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
return true; // Directory exists
|
|
2334
|
+
}
|
|
2335
|
+
catch (error) {
|
|
2336
|
+
return false; // Any error means the path does not exist
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
static async createDirIfNotExists(filePath) {
|
|
2340
|
+
if (!(await FileManager.pathExists(filePath))) {
|
|
2341
|
+
try {
|
|
2342
|
+
const fs = await import('@zenfs/core');
|
|
2343
|
+
await fs.promises.mkdir(filePath);
|
|
2344
|
+
}
|
|
2345
|
+
catch (error) {
|
|
2346
|
+
// This is a no-op. We tried to create a directory that already exists.
|
|
2347
|
+
console.log('Attempted to create a directory that already exists');
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
/**
|
|
2352
|
+
* Waits for a file to exist at the specified path.
|
|
2353
|
+
* @param {string} filePath - The path of the file to check.
|
|
2354
|
+
* @param {number} interval - The interval in milliseconds between checks (default: 500ms).
|
|
2355
|
+
* @param {number} timeout - The timeout in milliseconds to wait for the file to exist (default: 10s).
|
|
2356
|
+
* @returns {Promise<boolean>} - Resolves to true if the file exists within the timeout period, otherwise false.
|
|
2357
|
+
*/
|
|
2358
|
+
static async waitForFile(filePath, interval = 1000, timeout = 60000) {
|
|
2359
|
+
const fs = await import('@zenfs/core');
|
|
2360
|
+
const fsNode = await import('node:fs');
|
|
2361
|
+
return new Promise((resolve, reject) => {
|
|
2362
|
+
const startTime = Date.now();
|
|
2363
|
+
let isBusy = false;
|
|
2364
|
+
const cancelableInterval = new CancelableInterval(async (stop) => {
|
|
2365
|
+
logger$k('waitForFile', filePath);
|
|
2366
|
+
if (isBusy) {
|
|
2367
|
+
return;
|
|
2368
|
+
}
|
|
2369
|
+
isBusy = true;
|
|
2370
|
+
// TODO: Needs to read from OPFS
|
|
2371
|
+
if (fs.existsSync(filePath) && fsNode.existsSync(filePath)) {
|
|
2372
|
+
stop();
|
|
2373
|
+
resolve(true);
|
|
2374
|
+
}
|
|
2375
|
+
const pathExists = await this.pathExists(filePath);
|
|
2376
|
+
if (pathExists) {
|
|
2377
|
+
stop();
|
|
2378
|
+
resolve(true);
|
|
2379
|
+
}
|
|
2380
|
+
if (Date.now() - startTime >= timeout) {
|
|
2381
|
+
stop();
|
|
2382
|
+
reject(new Error('Timeout exceeded while waiting for file'));
|
|
2383
|
+
}
|
|
2384
|
+
isBusy = false;
|
|
2385
|
+
}, interval);
|
|
2386
|
+
cancelableInterval.start();
|
|
2387
|
+
// const _interval = setInterval(async () => {
|
|
2388
|
+
// logger('waitForFile', filePath)
|
|
2389
|
+
// if (isBusy) {
|
|
2390
|
+
// return
|
|
2391
|
+
// }
|
|
2392
|
+
// isBusy = true
|
|
2393
|
+
// // TODO: Needs to read from OPFS
|
|
2394
|
+
// if (fs.existsSync(filePath) && fsNode.existsSync(filePath)) {
|
|
2395
|
+
// clearInterval(_interval)
|
|
2396
|
+
// resolve(true)
|
|
2397
|
+
// }
|
|
2398
|
+
// const pathExists = await this.pathExists(filePath)
|
|
2399
|
+
// if (pathExists) {
|
|
2400
|
+
// clearInterval(_interval)
|
|
2401
|
+
// resolve(true)
|
|
2402
|
+
// }
|
|
2403
|
+
// if (Date.now() - startTime >= timeout) {
|
|
2404
|
+
// clearInterval(_interval)
|
|
2405
|
+
// reject(new Error('Timeout exceeded while waiting for file'))
|
|
2406
|
+
// }
|
|
2407
|
+
// isBusy = false
|
|
2408
|
+
// }, interval)
|
|
2409
|
+
// retry(
|
|
2410
|
+
// {
|
|
2411
|
+
// times: Math.ceil(timeout / interval),
|
|
2412
|
+
// interval: interval,
|
|
2413
|
+
// },
|
|
2414
|
+
// (callback: Function) => {
|
|
2415
|
+
// if (fs.existsSync(filePath) && fsNode.existsSync(filePath)) {
|
|
2416
|
+
// return callback(null, true) // File exists, finish with success
|
|
2417
|
+
// }
|
|
2418
|
+
// if (Date.now() - startTime >= timeout) {
|
|
2419
|
+
// return callback(new Error('Timeout exceeded while waiting for file'))
|
|
2420
|
+
// }
|
|
2421
|
+
// callback(new Error('File does not exist yet')) // Retry with this error
|
|
2422
|
+
// },
|
|
2423
|
+
// (err: Error, result: boolean) => {
|
|
2424
|
+
// if (err) {
|
|
2425
|
+
// return resolve(false) // Resolve as false if timeout or error occurs
|
|
2426
|
+
// }
|
|
2427
|
+
// resolve(result) // Resolve as true if file exists
|
|
2428
|
+
// },
|
|
2429
|
+
// )
|
|
2430
|
+
});
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
BaseFileManager.setPlatformClass(FileManager);
|
|
2434
|
+
class CancelableInterval {
|
|
2435
|
+
constructor(task, interval) {
|
|
2436
|
+
this.task = task;
|
|
2437
|
+
this.interval = interval;
|
|
2438
|
+
this.intervalId = null;
|
|
2439
|
+
this.currentTaskAbortController = null;
|
|
2440
|
+
}
|
|
2441
|
+
start() {
|
|
2442
|
+
this.intervalId = window.setInterval(async () => {
|
|
2443
|
+
if (this.currentTaskAbortController) {
|
|
2444
|
+
// Cancel the previous running task
|
|
2445
|
+
this.currentTaskAbortController.abort();
|
|
2446
|
+
}
|
|
2447
|
+
// Create a new abort controller for the current task
|
|
2448
|
+
this.currentTaskAbortController = new AbortController();
|
|
2449
|
+
const signal = this.currentTaskAbortController.signal;
|
|
2450
|
+
try {
|
|
2451
|
+
await this.taskWithCancellation(signal);
|
|
2452
|
+
}
|
|
2453
|
+
catch (error) {
|
|
2454
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
2455
|
+
console.log('Previous task was canceled.');
|
|
2456
|
+
}
|
|
2457
|
+
else {
|
|
2458
|
+
console.error('Task error:', error);
|
|
2459
|
+
}
|
|
2460
|
+
this.stop();
|
|
2461
|
+
}
|
|
2462
|
+
}, this.interval);
|
|
2463
|
+
}
|
|
2464
|
+
async taskWithCancellation(signal) {
|
|
2465
|
+
await this.task(() => this.stop());
|
|
2466
|
+
if (signal.aborted) {
|
|
2467
|
+
throw new DOMException('Task was aborted', 'AbortError');
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
stop() {
|
|
2471
|
+
if (this.intervalId !== null) {
|
|
2472
|
+
clearInterval(this.intervalId);
|
|
2473
|
+
this.intervalId = null;
|
|
2474
|
+
}
|
|
2475
|
+
if (this.currentTaskAbortController) {
|
|
2476
|
+
this.currentTaskAbortController.abort();
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
|
|
2481
|
+
var FileManager$1 = /*#__PURE__*/Object.freeze({
|
|
2482
|
+
__proto__: null,
|
|
2483
|
+
FileManager: FileManager
|
|
2484
|
+
});
|
|
1576
2485
|
|
|
1577
2486
|
const logger$j = debug('app:services:db:actors:migrate');
|
|
1578
2487
|
const migrate = fromCallback(({ sendBack, input: { context } }) => {
|
|
@@ -1583,7 +2492,7 @@ const migrate = fromCallback(({ sendBack, input: { context } }) => {
|
|
|
1583
2492
|
const journalPath = `/${pathToDbDir}/meta/_journal.json`;
|
|
1584
2493
|
journalExists = await fs.promises.exists(journalPath);
|
|
1585
2494
|
if (!journalExists) {
|
|
1586
|
-
await waitForFile(journalPath, 500, 60000);
|
|
2495
|
+
await FileManager.waitForFile(journalPath, 500, 60000);
|
|
1587
2496
|
}
|
|
1588
2497
|
};
|
|
1589
2498
|
const _migrate = async () => {
|
|
@@ -1831,26 +2740,22 @@ const createDirectories = async (dirPath) => {
|
|
|
1831
2740
|
return;
|
|
1832
2741
|
}
|
|
1833
2742
|
const parentDir = path.dirname(dirPath);
|
|
1834
|
-
let parentDirExists = await
|
|
2743
|
+
let parentDirExists = await BaseFileManager.pathExists(parentDir);
|
|
1835
2744
|
if (!parentDirExists) {
|
|
1836
2745
|
await createDirectories(parentDir);
|
|
1837
2746
|
}
|
|
1838
|
-
parentDirExists = await
|
|
2747
|
+
parentDirExists = await BaseFileManager.pathExists(parentDir);
|
|
1839
2748
|
if (parentDirExists) {
|
|
1840
|
-
|
|
1841
|
-
await fs.promises.mkdir(dirPath);
|
|
1842
|
-
}
|
|
1843
|
-
catch (error) {
|
|
1844
|
-
logger$h(`[Error] Failed to create directories for ${dirPath}:`, error);
|
|
1845
|
-
}
|
|
1846
|
-
}
|
|
1847
|
-
if (!parentDirExists) {
|
|
1848
|
-
console.log(fs);
|
|
2749
|
+
await BaseFileManager.createDirIfNotExists(dirPath);
|
|
1849
2750
|
}
|
|
1850
2751
|
};
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
2752
|
+
class FileDownloadManager {
|
|
2753
|
+
constructor(fileUrls, maxRetries) {
|
|
2754
|
+
this.isDownloading = false;
|
|
2755
|
+
this.filesToDownload = new Map(fileUrls.map(url => [url, 0]));
|
|
2756
|
+
this.maxRetries = maxRetries;
|
|
2757
|
+
}
|
|
2758
|
+
async downloadFile(url, localFilePath) {
|
|
1854
2759
|
const response = await fetch(url);
|
|
1855
2760
|
const fileData = await response.text().catch((error) => {
|
|
1856
2761
|
console.error(`Failed to parse text from ${url}:`, error);
|
|
@@ -1860,10 +2765,6 @@ const downloadFile = async (url, localFilePath) => {
|
|
|
1860
2765
|
return;
|
|
1861
2766
|
}
|
|
1862
2767
|
const localDirPath = path.dirname(localFilePath);
|
|
1863
|
-
if (busy) {
|
|
1864
|
-
return;
|
|
1865
|
-
}
|
|
1866
|
-
busy = true;
|
|
1867
2768
|
await createDirectories(localDirPath);
|
|
1868
2769
|
const filename = path.basename(localFilePath);
|
|
1869
2770
|
const regex = /(\d+)[\w_]+\.(sql|json)$/;
|
|
@@ -1899,34 +2800,77 @@ const downloadFile = async (url, localFilePath) => {
|
|
|
1899
2800
|
logger$h(`[downloadFile] Wrote file sync to ${localFilePath}`);
|
|
1900
2801
|
}
|
|
1901
2802
|
}
|
|
1902
|
-
|
|
1903
|
-
|
|
2803
|
+
async start() {
|
|
2804
|
+
if (this.isDownloading) {
|
|
2805
|
+
console.warn("Download process is already running.");
|
|
2806
|
+
return;
|
|
2807
|
+
}
|
|
2808
|
+
this.isDownloading = true;
|
|
2809
|
+
for (const [fileUrl, attempts] of this.filesToDownload.entries()) {
|
|
2810
|
+
let success = false;
|
|
2811
|
+
while (attempts < this.maxRetries) {
|
|
2812
|
+
try {
|
|
2813
|
+
console.log(`Starting download: ${fileUrl}`);
|
|
2814
|
+
await this.downloadFile(fileUrl, fileUrl);
|
|
2815
|
+
console.log(`Download successful: ${fileUrl}`);
|
|
2816
|
+
this.filesToDownload.delete(fileUrl);
|
|
2817
|
+
success = true;
|
|
2818
|
+
break; // Move to next file
|
|
2819
|
+
}
|
|
2820
|
+
catch (error) {
|
|
2821
|
+
console.error(`Error downloading ${fileUrl}:`, error);
|
|
2822
|
+
this.filesToDownload.set(fileUrl, attempts + 1);
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
if (!success) {
|
|
2826
|
+
console.error(`Failed to download after ${this.maxRetries} attempts: ${fileUrl}`);
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
this.isDownloading = false;
|
|
2830
|
+
console.log("All downloads completed.");
|
|
1904
2831
|
}
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
return response.json();
|
|
1910
|
-
};
|
|
1911
|
-
const fetchFilesRecursively = async (url, localPath, fileList) => {
|
|
1912
|
-
for (const file of fileList) {
|
|
1913
|
-
try {
|
|
1914
|
-
const fileUrl = `${url}/${file}`;
|
|
1915
|
-
const fileLocalPath = path.join(localPath, file);
|
|
1916
|
-
// logger(`[fetchFilesRecursively] fileUrl: ${fileUrl}`)
|
|
1917
|
-
// logger(`[fetchFilesRecursively] fileLocalPath: ${fileLocalPath}`)
|
|
1918
|
-
await downloadFile(fileUrl, fileLocalPath);
|
|
2832
|
+
addFile(fileUrl) {
|
|
2833
|
+
if (!this.filesToDownload.has(fileUrl)) {
|
|
2834
|
+
this.filesToDownload.set(fileUrl, 0);
|
|
2835
|
+
console.log(`Added file to download queue: ${fileUrl}`);
|
|
1919
2836
|
}
|
|
1920
|
-
|
|
1921
|
-
console.
|
|
2837
|
+
else {
|
|
2838
|
+
console.warn(`File already in queue: ${fileUrl}`);
|
|
1922
2839
|
}
|
|
1923
2840
|
}
|
|
2841
|
+
getPendingFiles() {
|
|
2842
|
+
return Array.from(this.filesToDownload.keys());
|
|
2843
|
+
}
|
|
2844
|
+
clear() {
|
|
2845
|
+
this.filesToDownload.clear();
|
|
2846
|
+
console.log("Cleared all files from the download queue.");
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
const fetchDirectory = async (url) => {
|
|
2850
|
+
const response = await fetch(url);
|
|
2851
|
+
return response.json();
|
|
1924
2852
|
};
|
|
2853
|
+
// export const fetchFilesRecursively = async (
|
|
2854
|
+
// url: string,
|
|
2855
|
+
// localPath: string,
|
|
2856
|
+
// fileList: string[],
|
|
2857
|
+
// ) => {
|
|
2858
|
+
// for (const file of fileList) {
|
|
2859
|
+
// try {
|
|
2860
|
+
// const fileUrl = `${url}/${file}`
|
|
2861
|
+
// const fileLocalPath = path.join(localPath, file)
|
|
2862
|
+
// // logger(`[fetchFilesRecursively] fileUrl: ${fileUrl}`)
|
|
2863
|
+
// // logger(`[fetchFilesRecursively] fileLocalPath: ${fileLocalPath}`)
|
|
2864
|
+
// await downloadFile(fileUrl, fileLocalPath)
|
|
2865
|
+
// } catch (error) {
|
|
2866
|
+
// console.error(`Failed to fetch files from ${url}:`, error)
|
|
2867
|
+
// }
|
|
2868
|
+
// }
|
|
2869
|
+
// }
|
|
1925
2870
|
const confirmFilesExist = async (filePaths) => {
|
|
1926
2871
|
let everythingDownloaded = false;
|
|
1927
2872
|
for (const filePath of filePaths) {
|
|
1928
|
-
|
|
1929
|
-
everythingDownloaded = await fs.promises.exists(fullPath);
|
|
2873
|
+
everythingDownloaded = await fs.promises.exists(filePath);
|
|
1930
2874
|
}
|
|
1931
2875
|
if (!everythingDownloaded) {
|
|
1932
2876
|
setTimeout(async () => {
|
|
@@ -1938,7 +2882,10 @@ const filesToExclude = ['.DS_Store'];
|
|
|
1938
2882
|
const syncDbFiles = async ({ filePaths, files }) => {
|
|
1939
2883
|
let fileList = await fetchDirectory(filePaths);
|
|
1940
2884
|
fileList = fileList.filter((file) => !filesToExclude.includes(file));
|
|
1941
|
-
|
|
2885
|
+
fileList = fileList.map((file) => `${files}/${file}`);
|
|
2886
|
+
const downloadManager = new FileDownloadManager(fileList, 5);
|
|
2887
|
+
await downloadManager.start();
|
|
2888
|
+
// await fetchFilesRecursively(files, BROWSER_FS_TOP_DIR, fileList)
|
|
1942
2889
|
await confirmFilesExist(fileList);
|
|
1943
2890
|
logger$h('[syncDbFiles] Files synced!');
|
|
1944
2891
|
};
|
|
@@ -2031,28 +2978,12 @@ const GET_TRANSACTION_TAGS = graphql(/* GraphQL */ `
|
|
|
2031
2978
|
}
|
|
2032
2979
|
`);
|
|
2033
2980
|
|
|
2034
|
-
const saveAppState = async (key, value) => {
|
|
2035
|
-
const appDb = BaseDb.getAppDb();
|
|
2036
|
-
await appDb
|
|
2037
|
-
.insert(appState)
|
|
2038
|
-
.values({
|
|
2039
|
-
key,
|
|
2040
|
-
value,
|
|
2041
|
-
})
|
|
2042
|
-
.onConflictDoUpdate({
|
|
2043
|
-
target: appState.key,
|
|
2044
|
-
set: {
|
|
2045
|
-
value,
|
|
2046
|
-
},
|
|
2047
|
-
});
|
|
2048
|
-
};
|
|
2049
|
-
|
|
2050
2981
|
const initArweaveClient = async () => {
|
|
2051
2982
|
if (isBrowser()) {
|
|
2052
|
-
(await import('./ArweaveClient-
|
|
2983
|
+
(await import('./ArweaveClient-Dcqcpsq4.js')).ArweaveClient;
|
|
2053
2984
|
}
|
|
2054
2985
|
if (!isBrowser()) {
|
|
2055
|
-
(await import('./ArweaveClient-
|
|
2986
|
+
(await import('./ArweaveClient-Dh7LRIqD.js')).ArweaveClient;
|
|
2056
2987
|
}
|
|
2057
2988
|
};
|
|
2058
2989
|
let domain = 'arweave.net';
|
|
@@ -2138,18 +3069,10 @@ const downloadAllFilesBinaryRequestHandler = async () => {
|
|
|
2138
3069
|
},
|
|
2139
3070
|
}),
|
|
2140
3071
|
});
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
await fs.promises.mkdir('/files/html', { recursive: true });
|
|
2146
|
-
}
|
|
2147
|
-
if (!(await fs.promises.exists('/files/json'))) {
|
|
2148
|
-
await fs.promises.mkdir('/files/json', { recursive: true });
|
|
2149
|
-
}
|
|
2150
|
-
if (!(await fs.promises.exists('/files/images'))) {
|
|
2151
|
-
await fs.promises.mkdir('/files/images', { recursive: true });
|
|
2152
|
-
}
|
|
3072
|
+
await BaseFileManager.createDirIfNotExists('/files');
|
|
3073
|
+
await BaseFileManager.createDirIfNotExists('/files/html');
|
|
3074
|
+
await BaseFileManager.createDirIfNotExists('/files/json');
|
|
3075
|
+
await BaseFileManager.createDirIfNotExists('/files/images');
|
|
2153
3076
|
const appDb = BaseDb.getAppDb();
|
|
2154
3077
|
if (!appDb) {
|
|
2155
3078
|
console.warn('[fetchAll/actors] [fetchAllBinaryData] seedDb not available');
|
|
@@ -4932,7 +5855,7 @@ const hydrateFromDb = fromCallback(({ sendBack, input: { context } }) => {
|
|
|
4932
5855
|
if (propertyRecordSchema &&
|
|
4933
5856
|
propertyRecordSchema.storageType &&
|
|
4934
5857
|
propertyRecordSchema.storageType === 'ItemStorage') {
|
|
4935
|
-
const { Item } = await import('./index-
|
|
5858
|
+
const { Item } = await import('./index-Dp3GcggF.js');
|
|
4936
5859
|
const item = await Item.find({
|
|
4937
5860
|
seedLocalId,
|
|
4938
5861
|
modelName,
|
|
@@ -6519,7 +7442,7 @@ const createNewItem = async ({ modelName, ...propertyData }) => {
|
|
|
6519
7442
|
}
|
|
6520
7443
|
const seedType = modelName.toLowerCase();
|
|
6521
7444
|
const newSeedId = await createSeed({ type: seedType });
|
|
6522
|
-
const newVersionId = await createVersion({ seedLocalId: newSeedId });
|
|
7445
|
+
const newVersionId = await createVersion({ seedLocalId: newSeedId, seedType: toSnakeCase$1(modelName) });
|
|
6523
7446
|
const propertySchemas = getModel(modelName)?.schema;
|
|
6524
7447
|
for (const [propertyName, propertyValue] of Object.entries(propertyData)) {
|
|
6525
7448
|
let propertyRecordSchema;
|
|
@@ -6534,6 +7457,7 @@ const createNewItem = async ({ modelName, ...propertyData }) => {
|
|
|
6534
7457
|
modelName,
|
|
6535
7458
|
}, propertyRecordSchema);
|
|
6536
7459
|
}
|
|
7460
|
+
eventEmitter.emit('item.requestAll', { modelName });
|
|
6537
7461
|
return {
|
|
6538
7462
|
modelName,
|
|
6539
7463
|
seedLocalId: newSeedId,
|
|
@@ -6715,7 +7639,7 @@ class BaseItem {
|
|
|
6715
7639
|
if (!props.modelName) {
|
|
6716
7640
|
throw new Error('Model name is required to create an item');
|
|
6717
7641
|
}
|
|
6718
|
-
const { seedLocalId, versionLocalId,
|
|
7642
|
+
const { seedLocalId, versionLocalId, } = await createNewItem({
|
|
6719
7643
|
modelName: props.modelName,
|
|
6720
7644
|
});
|
|
6721
7645
|
props.seedLocalId = seedLocalId;
|
|
@@ -6804,6 +7728,9 @@ class BaseItem {
|
|
|
6804
7728
|
get latestVersionUid() {
|
|
6805
7729
|
return this.serviceContext.latestVersionUid;
|
|
6806
7730
|
}
|
|
7731
|
+
get latestVersionLocalId() {
|
|
7732
|
+
return this.serviceContext.latestVersionLocalId;
|
|
7733
|
+
}
|
|
6807
7734
|
get modelName() {
|
|
6808
7735
|
return this.serviceContext.modelName;
|
|
6809
7736
|
}
|
|
@@ -6813,6 +7740,12 @@ class BaseItem {
|
|
|
6813
7740
|
get attestationCreatedAt() {
|
|
6814
7741
|
return this.serviceContext.attestationCreatedAt;
|
|
6815
7742
|
}
|
|
7743
|
+
get versionsCount() {
|
|
7744
|
+
return this.serviceContext.versionsCount;
|
|
7745
|
+
}
|
|
7746
|
+
get lastVersionPublishedAt() {
|
|
7747
|
+
return this.serviceContext.lastVersionPublishedAt;
|
|
7748
|
+
}
|
|
6816
7749
|
unload() {
|
|
6817
7750
|
this._subscription?.unsubscribe();
|
|
6818
7751
|
this._service.stop();
|
|
@@ -6865,7 +7798,7 @@ const initItem = async () => {
|
|
|
6865
7798
|
Item$2 = (await Promise.resolve().then(function () { return Item$1; })).Item;
|
|
6866
7799
|
}
|
|
6867
7800
|
if (!isBrowser()) {
|
|
6868
|
-
Item$2 = (await import('./Item-
|
|
7801
|
+
Item$2 = (await import('./Item-7vg6XRhw.js')).Item;
|
|
6869
7802
|
}
|
|
6870
7803
|
};
|
|
6871
7804
|
|
|
@@ -6875,17 +7808,17 @@ const initItemProperty = async () => {
|
|
|
6875
7808
|
ItemProperty$2 = (await Promise.resolve().then(function () { return ItemProperty$1; })).ItemProperty;
|
|
6876
7809
|
}
|
|
6877
7810
|
if (!isBrowser()) {
|
|
6878
|
-
ItemProperty$2 = (await import('./ItemProperty-
|
|
7811
|
+
ItemProperty$2 = (await import('./ItemProperty-Bcht9WTV.js')).ItemProperty;
|
|
6879
7812
|
}
|
|
6880
7813
|
};
|
|
6881
7814
|
|
|
6882
7815
|
let Db;
|
|
6883
7816
|
const initDb = async () => {
|
|
6884
7817
|
if (isBrowser()) {
|
|
6885
|
-
Db = (await import('./Db-
|
|
7818
|
+
Db = (await import('./Db-CMRjCZoY.js')).Db;
|
|
6886
7819
|
}
|
|
6887
7820
|
if (!isBrowser()) {
|
|
6888
|
-
Db = (await import('./Db-
|
|
7821
|
+
Db = (await import('./Db-BjoRtCFg.js')).Db;
|
|
6889
7822
|
}
|
|
6890
7823
|
// TODO: Add config for React Native
|
|
6891
7824
|
};
|
|
@@ -7337,6 +8270,7 @@ const useItems = ({ modelName, deleted = false }) => {
|
|
|
7337
8270
|
}
|
|
7338
8271
|
listenerRef.current();
|
|
7339
8272
|
});
|
|
8273
|
+
readFromDb();
|
|
7340
8274
|
return () => {
|
|
7341
8275
|
eventEmitter.removeListener('item.requestAll', readFromDb);
|
|
7342
8276
|
};
|
|
@@ -7669,28 +8603,28 @@ const setupServiceHandlers = () => {
|
|
|
7669
8603
|
|
|
7670
8604
|
const initEasClient = async () => {
|
|
7671
8605
|
if (isBrowser()) {
|
|
7672
|
-
(await import('./EasClient-
|
|
8606
|
+
(await import('./EasClient-A1xC7Gm-.js')).EasClient;
|
|
7673
8607
|
}
|
|
7674
8608
|
if (!isBrowser()) {
|
|
7675
|
-
(await import('./EasClient-
|
|
8609
|
+
(await import('./EasClient-m2mXad59.js')).EasClient;
|
|
7676
8610
|
}
|
|
7677
8611
|
};
|
|
7678
8612
|
|
|
7679
8613
|
const initQueryClient = async () => {
|
|
7680
8614
|
if (isBrowser()) {
|
|
7681
|
-
(await import('./QueryClient-
|
|
8615
|
+
(await import('./QueryClient-C-hFMF2j.js')).QueryClient;
|
|
7682
8616
|
}
|
|
7683
8617
|
if (!isBrowser()) {
|
|
7684
|
-
(await import('./QueryClient-
|
|
8618
|
+
(await import('./QueryClient-C02sPZ-K.js')).QueryClient;
|
|
7685
8619
|
}
|
|
7686
8620
|
};
|
|
7687
8621
|
|
|
7688
8622
|
const initFileManager = async () => {
|
|
7689
8623
|
if (isBrowser()) {
|
|
7690
|
-
(await
|
|
8624
|
+
(await Promise.resolve().then(function () { return FileManager$1; })).FileManager;
|
|
7691
8625
|
}
|
|
7692
8626
|
if (!isBrowser()) {
|
|
7693
|
-
(await import('./FileManager-
|
|
8627
|
+
(await import('./FileManager-Dv2zn1RW.js')).FileManager;
|
|
7694
8628
|
}
|
|
7695
8629
|
};
|
|
7696
8630
|
|
|
@@ -7736,11 +8670,35 @@ const client = {
|
|
|
7736
8670
|
arweaveDomain,
|
|
7737
8671
|
filesDir,
|
|
7738
8672
|
});
|
|
7739
|
-
const { models: internalModels } = await import('./seed.schema.config-
|
|
8673
|
+
const { models: internalModels } = await import('./seed.schema.config-CS6BvsTl.js');
|
|
7740
8674
|
for (const [key, value] of Object.entries(internalModels)) {
|
|
7741
8675
|
setModel(key, value);
|
|
7742
8676
|
}
|
|
7743
8677
|
},
|
|
8678
|
+
setAddresses: async (addresses) => {
|
|
8679
|
+
const { BaseDb } = await Promise.resolve().then(function () { return BaseDb$1; });
|
|
8680
|
+
if (!BaseDb) {
|
|
8681
|
+
throw new Error('BaseDb not found');
|
|
8682
|
+
}
|
|
8683
|
+
if (!BaseDb.PlatformClass) {
|
|
8684
|
+
await initDb();
|
|
8685
|
+
}
|
|
8686
|
+
const appDb = BaseDb.getAppDb();
|
|
8687
|
+
if (!appDb) {
|
|
8688
|
+
throw new Error('App DB not found');
|
|
8689
|
+
}
|
|
8690
|
+
appDb.insert(appState)
|
|
8691
|
+
.values({
|
|
8692
|
+
key: 'addresses',
|
|
8693
|
+
value: JSON.stringify(addresses),
|
|
8694
|
+
})
|
|
8695
|
+
.onConflictDoUpdate({
|
|
8696
|
+
target: appState.key,
|
|
8697
|
+
set: {
|
|
8698
|
+
value: JSON.stringify(addresses),
|
|
8699
|
+
},
|
|
8700
|
+
});
|
|
8701
|
+
},
|
|
7744
8702
|
subscribe: (callback) => {
|
|
7745
8703
|
const subscription = globalService.subscribe(callback);
|
|
7746
8704
|
eventEmitter.addListener('internal.globalService', callback);
|
|
@@ -7783,5 +8741,5 @@ const client = {
|
|
|
7783
8741
|
|
|
7784
8742
|
enableMapSet();
|
|
7785
8743
|
|
|
7786
|
-
export { BaseItem as B, Db as D, Item as I, Json as J, List as L, Model as M, Property as P, Relation as R, Text as T, BaseItemProperty as a, BaseDb as b, ImageSrc as c, Item$2 as d, ItemProperty$2 as e, useItem as f, useItemProperties as g, useCreateItem as h, useItemProperty as i, useDeleteItem as j, useGlobalServiceStatus as k, usePublishItem as l, usePersistedSnapshots as m, useServices as n, useService as o, getCorrectId as p, getGlobalService as q, eventEmitter as r,
|
|
7787
|
-
//# sourceMappingURL=index-
|
|
8744
|
+
export { BaseItem as B, Db as D, FileManager as F, Item as I, Json as J, List as L, Model as M, Property as P, Relation as R, Text as T, BaseItemProperty as a, BaseDb as b, ImageSrc as c, Item$2 as d, ItemProperty$2 as e, useItem as f, useItemProperties as g, useCreateItem as h, useItemProperty as i, useDeleteItem as j, useGlobalServiceStatus as k, usePublishItem as l, usePersistedSnapshots as m, useServices as n, useService as o, getCorrectId as p, getGlobalService as q, eventEmitter as r, client as s, useItems as u, withSeed as w };
|
|
8745
|
+
//# sourceMappingURL=index-D3Scq_ka.js.map
|