@seedprotocol/sdk 0.7.3-next.613.g79a8dfd → 0.7.3-next.654.g52217f6
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/{BasePathResolver-BdxCSIzn.js → BasePathResolver-CfSfMW5_.js} +7 -10
- package/dist/BasePathResolver-CfSfMW5_.js.map +1 -0
- package/dist/{ClientManager-DDA9956Y.js → ClientManager-C-QXqUK9.js} +438 -153
- package/dist/ClientManager-C-QXqUK9.js.map +1 -0
- package/dist/{Item-D4FigtYS.js → Item-C7sUUu2B.js} +291 -105
- package/dist/Item-C7sUUu2B.js.map +1 -0
- package/dist/{ModelProperty-BDt6AU2h.js → ModelProperty-DJxeOOrc.js} +28 -27
- package/dist/ModelProperty-DJxeOOrc.js.map +1 -0
- package/dist/{Schema-B8xqLWkL.js → Schema-fXtvd-yL.js} +30 -30
- package/dist/{Schema-B8xqLWkL.js.map → Schema-fXtvd-yL.js.map} +1 -1
- package/dist/{SchemaValidationService-BPxkfDLU.js → SchemaValidationService-CvRS2HSO.js} +10 -9
- package/dist/{SchemaValidationService-BPxkfDLU.js.map → SchemaValidationService-CvRS2HSO.js.map} +1 -1
- package/dist/{backfillMetadataPropertyIds-DuTPcv8t.js → backfillMetadataPropertyIds-SXW-8C64.js} +4 -4
- package/dist/{backfillMetadataPropertyIds-DuTPcv8t.js.map → backfillMetadataPropertyIds-SXW-8C64.js.map} +1 -1
- package/dist/cjs/{ModelProperty-B-cZm1yU.js → ModelProperty-DXZ39Wyv.js} +24 -23
- package/dist/cjs/{ModelProperty-B-cZm1yU.js.map → ModelProperty-DXZ39Wyv.js.map} +1 -1
- package/dist/cjs/{Schema-STP972Xw.js → Schema-DdP109QD.js} +26 -25
- package/dist/cjs/{Schema-STP972Xw.js.map → Schema-DdP109QD.js.map} +1 -1
- package/dist/cjs/{SchemaValidationService-CKNW1bKg.js → SchemaValidationService-CSXxx7sQ.js} +5 -4
- package/dist/cjs/{SchemaValidationService-CKNW1bKg.js.map → SchemaValidationService-CSXxx7sQ.js.map} +1 -1
- package/dist/cjs/{backfillMetadataPropertyIds-CP3y1skt.js → backfillMetadataPropertyIds-CPYoaMl4.js} +6 -5
- package/dist/cjs/{backfillMetadataPropertyIds-CP3y1skt.js.map → backfillMetadataPropertyIds-CPYoaMl4.js.map} +1 -1
- package/dist/cjs/{eas-CAnUC4wd.js → eas-B2YDQssV.js} +6 -5
- package/dist/cjs/{eas-CAnUC4wd.js.map → eas-B2YDQssV.js.map} +1 -1
- package/dist/cjs/{getItem-CWgb3e9G.js → getItem-DgnaQzbE.js} +5 -4
- package/dist/cjs/{getItem-CWgb3e9G.js.map → getItem-DgnaQzbE.js.map} +1 -1
- package/dist/cjs/{getPublishPayload-BsZWiJ03.js → getPublishPayload-CqlqRdot.js} +12 -11
- package/dist/cjs/{getPublishPayload-BsZWiJ03.js.map → getPublishPayload-CqlqRdot.js.map} +1 -1
- package/dist/cjs/getPublishUploads-DsXY9S4U.js +872 -0
- package/dist/cjs/getPublishUploads-DsXY9S4U.js.map +1 -0
- package/dist/cjs/{index-nA5PxBSs.js → index-BHaWOwNF.js} +6 -5
- package/dist/cjs/index-BHaWOwNF.js.map +1 -0
- package/dist/cjs/{index-usKaANAq.js → index-C4XlgCJ4.js} +1156 -526
- package/dist/cjs/index-C4XlgCJ4.js.map +1 -0
- package/dist/cjs/{property-xMwPhQ5N.js → property-fa0iju0J.js} +8 -7
- package/dist/cjs/{property-xMwPhQ5N.js.map → property-fa0iju0J.js.map} +1 -1
- package/dist/cjs/{publishDraftGraph-BRWlR1KO.js → publishDraftGraph-3ASwAlD7.js} +4 -4
- package/dist/cjs/{publishDraftGraph-BRWlR1KO.js.map → publishDraftGraph-3ASwAlD7.js.map} +1 -1
- package/dist/cjs/{resetAmbiguousLegacyItemData-8nfkj9rz.js → resetAmbiguousLegacyItemData-Bu_MBBYt.js} +5 -4
- package/dist/cjs/{resetAmbiguousLegacyItemData-8nfkj9rz.js.map → resetAmbiguousLegacyItemData-Bu_MBBYt.js.map} +1 -1
- package/dist/cjs/{resolveModelForSyncedSeed-kYRuDWYw.js → resolveModelForSyncedSeed-BANIiuFU.js} +2 -2
- package/dist/cjs/{resolveModelForSyncedSeed-kYRuDWYw.js.map → resolveModelForSyncedSeed-BANIiuFU.js.map} +1 -1
- package/dist/cjs/{syncDbWithEas-DqRhV8o2.js → syncDbWithEas-CrmxxmUW.js} +10 -9
- package/dist/cjs/{syncDbWithEas-DqRhV8o2.js.map → syncDbWithEas-CrmxxmUW.js.map} +1 -1
- package/dist/{configurePlatform-YcOiDQBZ.js → configurePlatform-BRw2j0ZN.js} +7 -4
- package/dist/{configurePlatform-YcOiDQBZ.js.map → configurePlatform-BRw2j0ZN.js.map} +1 -1
- package/dist/{db-CQt-gcAl.js → db-D9K_f5cF.js} +14 -14
- package/dist/{db-CQt-gcAl.js.map → db-D9K_f5cF.js.map} +1 -1
- package/dist/{eas-CpOD74Ke.js → eas-DHxfswCt.js} +8 -5
- package/dist/eas-DHxfswCt.js.map +1 -0
- package/dist/{getItem-B_OoFkd7.js → getItem-d6N_aM6S.js} +8 -5
- package/dist/{getItem-B_OoFkd7.js.map → getItem-d6N_aM6S.js.map} +1 -1
- package/dist/{getPublishPayload-Xl3sRkR-.js → getPublishPayload-BHrxVtzY.js} +16 -16
- package/dist/{getPublishPayload-Xl3sRkR-.js.map → getPublishPayload-BHrxVtzY.js.map} +1 -1
- package/dist/getPublishUploads-BEqRiKIS.js +657 -0
- package/dist/getPublishUploads-BEqRiKIS.js.map +1 -0
- package/dist/{index-BGt4_oPO.js → index-CiMEoGu3.js} +12 -11
- package/dist/index-CiMEoGu3.js.map +1 -0
- package/dist/{index-CSOQFAOn.js → index-DglvthOk.js} +2 -2
- package/dist/index-DglvthOk.js.map +1 -0
- package/dist/{index-Xl1_4Lmb.js → index-s15q14rm.js} +29 -29
- package/dist/{index-Xl1_4Lmb.js.map → index-s15q14rm.js.map} +1 -1
- package/dist/{index.browser-CjTe0vt1.js → index.browser-Cm06tP4X.js} +553 -165
- package/dist/index.browser-Cm06tP4X.js.map +1 -0
- package/dist/{index.node-Cfa7npS6.js → index.node-C30dmFgn.js} +5 -5
- package/dist/{index.node-Cfa7npS6.js.map → index.node-C30dmFgn.js.map} +1 -1
- package/dist/main.cjs +4 -3
- package/dist/main.cjs.map +1 -1
- package/dist/main.js +32 -44
- package/dist/main.js.map +1 -1
- package/dist/mimeTypes-DHAHC4eZ.js +204 -0
- package/dist/mimeTypes-DHAHC4eZ.js.map +1 -0
- package/dist/node.js +13 -12
- package/dist/node.js.map +1 -1
- package/dist/platform.browser.js +9 -7
- package/dist/platform.browser.js.map +1 -1
- package/dist/platform.node.js +3 -3
- package/dist/{property-BrXkY1Rg.js → property-DhmmYAvQ.js} +12 -9
- package/dist/property-DhmmYAvQ.js.map +1 -0
- package/dist/{publishDraftGraph-X0Wr9txE.js → publishDraftGraph-yYP74WRt.js} +8 -8
- package/dist/{publishDraftGraph-X0Wr9txE.js.map → publishDraftGraph-yYP74WRt.js.map} +1 -1
- package/dist/{resetAmbiguousLegacyItemData-Ci6PM_vC.js → resetAmbiguousLegacyItemData-Bk0QyDW8.js} +2 -2
- package/dist/{resetAmbiguousLegacyItemData-Ci6PM_vC.js.map → resetAmbiguousLegacyItemData-Bk0QyDW8.js.map} +1 -1
- package/dist/{resolveModelForSyncedSeed-DFVdnYq8.js → resolveModelForSyncedSeed-8cAzvUFd.js} +2 -2
- package/dist/{resolveModelForSyncedSeed-DFVdnYq8.js.map → resolveModelForSyncedSeed-8cAzvUFd.js.map} +1 -1
- package/dist/src/Item/Item.d.ts +1 -1
- package/dist/src/Item/Item.d.ts.map +1 -1
- package/dist/src/ItemProperty/service/actors/saveValueToDb/saveItemStorage.d.ts.map +1 -1
- package/dist/src/browser/helpers/FileManager.d.ts +4 -1
- package/dist/src/browser/helpers/FileManager.d.ts.map +1 -1
- package/dist/src/browser/helpers/tolerantWebAccess.d.ts +79 -4
- package/dist/src/browser/helpers/tolerantWebAccess.d.ts.map +1 -1
- package/dist/src/browser/workers/FileDownloader.d.ts +19 -4
- package/dist/src/browser/workers/FileDownloader.d.ts.map +1 -1
- package/dist/src/browser/workers/filesDownload.d.ts.map +1 -1
- package/dist/src/client/clientManagerMachine.d.ts.map +1 -1
- package/dist/src/db/migrateLegacyItemStorageRows.d.ts +57 -0
- package/dist/src/db/migrateLegacyItemStorageRows.d.ts.map +1 -0
- package/dist/src/db/read/getCompositeChildPaths.d.ts +13 -0
- package/dist/src/db/read/getCompositeChildPaths.d.ts.map +1 -0
- package/dist/src/db/read/getItems.d.ts.map +1 -1
- package/dist/src/db/read/getPublishUploads.d.ts +32 -3
- package/dist/src/db/read/getPublishUploads.d.ts.map +1 -1
- package/dist/src/db/read/summarizePublishWork.d.ts.map +1 -1
- package/dist/src/db/write/convertVerbatimItemStorageRow.d.ts +44 -0
- package/dist/src/db/write/convertVerbatimItemStorageRow.d.ts.map +1 -0
- package/dist/src/db/write/createNewItem.d.ts.map +1 -1
- package/dist/src/events/files/download.d.ts.map +1 -1
- package/dist/src/helpers/FileManager/BaseFileManager.d.ts +3 -3
- package/dist/src/helpers/FileManager/BaseFileManager.d.ts.map +1 -1
- package/dist/src/helpers/FileManager/IFileManager.d.ts +25 -1
- package/dist/src/helpers/FileManager/IFileManager.d.ts.map +1 -1
- package/dist/src/helpers/arweaveContentTags.d.ts +33 -0
- package/dist/src/helpers/arweaveContentTags.d.ts.map +1 -0
- package/dist/src/helpers/compositeUpload.d.ts +28 -0
- package/dist/src/helpers/compositeUpload.d.ts.map +1 -0
- package/dist/src/helpers/itemStorageFile.d.ts +57 -0
- package/dist/src/helpers/itemStorageFile.d.ts.map +1 -0
- package/dist/src/helpers/mapWithConcurrency.d.ts +7 -0
- package/dist/src/helpers/mapWithConcurrency.d.ts.map +1 -0
- package/dist/src/helpers/mimeTypes.d.ts +43 -0
- package/dist/src/helpers/mimeTypes.d.ts.map +1 -0
- package/dist/src/helpers/ownership.d.ts +19 -3
- package/dist/src/helpers/ownership.d.ts.map +1 -1
- package/dist/src/helpers/storageDir.d.ts +16 -0
- package/dist/src/helpers/storageDir.d.ts.map +1 -0
- package/dist/src/helpers/tabCoordinator.d.ts +5 -1
- package/dist/src/helpers/tabCoordinator.d.ts.map +1 -1
- package/dist/src/helpers/tabEvents.d.ts +23 -2
- package/dist/src/helpers/tabEvents.d.ts.map +1 -1
- package/dist/src/index.d.ts +6 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/types/fileManager.d.ts +3 -14
- package/dist/src/types/fileManager.d.ts.map +1 -1
- package/dist/src/types/publish.d.ts +14 -0
- package/dist/src/types/publish.d.ts.map +1 -1
- package/dist/{syncDbWithEas-BU-jZ5bJ.js → syncDbWithEas--HUA9WNb.js} +15 -14
- package/dist/syncDbWithEas--HUA9WNb.js.map +1 -0
- package/dist/{tabEvents-BZJMY5uK.js → tabEvents-DguZpCCv.js} +294 -58
- package/dist/tabEvents-DguZpCCv.js.map +1 -0
- package/dist/tolerantWebAccess-BZYWt09Y.js +551 -0
- package/dist/tolerantWebAccess-BZYWt09Y.js.map +1 -0
- package/dist/{updateSeedRevokedAt-iLpzae1i.js → updateSeedRevokedAt-D_EG-q3b.js} +3 -3
- package/dist/{updateSeedRevokedAt-iLpzae1i.js.map → updateSeedRevokedAt-D_EG-q3b.js.map} +1 -1
- package/package.json +5 -5
- package/dist/BasePathResolver-BdxCSIzn.js.map +0 -1
- package/dist/ClientManager-DDA9956Y.js.map +0 -1
- package/dist/Item-D4FigtYS.js.map +0 -1
- package/dist/ModelProperty-BDt6AU2h.js.map +0 -1
- package/dist/cjs/getPublishUploads-BQr8aMVP.js +0 -495
- package/dist/cjs/getPublishUploads-BQr8aMVP.js.map +0 -1
- package/dist/cjs/index-nA5PxBSs.js.map +0 -1
- package/dist/cjs/index-usKaANAq.js.map +0 -1
- package/dist/eas-CpOD74Ke.js.map +0 -1
- package/dist/getPublishUploads-DHP01KgU.js +0 -484
- package/dist/getPublishUploads-DHP01KgU.js.map +0 -1
- package/dist/index-BGt4_oPO.js.map +0 -1
- package/dist/index-CSOQFAOn.js.map +0 -1
- package/dist/index.browser-CjTe0vt1.js.map +0 -1
- package/dist/property-BrXkY1Rg.js.map +0 -1
- package/dist/syncDbWithEas-BU-jZ5bJ.js.map +0 -1
- package/dist/tabEvents-BZJMY5uK.js.map +0 -1
- package/dist/tolerantWebAccess-BZ2RA_mb.js +0 -160
- package/dist/tolerantWebAccess-BZ2RA_mb.js.map +0 -1
|
@@ -12,12 +12,13 @@ var pluralize = require('pluralize');
|
|
|
12
12
|
var typebox = require('@sinclair/typebox');
|
|
13
13
|
var casing = require('drizzle-orm/casing');
|
|
14
14
|
var lodashEs = require('lodash-es');
|
|
15
|
+
var sha256 = require('@noble/hashes/sha256');
|
|
16
|
+
var sha3 = require('@noble/hashes/sha3');
|
|
17
|
+
var utils = require('@noble/hashes/utils');
|
|
15
18
|
var nanoid = require('nanoid');
|
|
16
19
|
var nanoidDictionary = require('nanoid-dictionary');
|
|
17
20
|
var query = require('@seedprotocol/query');
|
|
18
21
|
var rxjs = require('rxjs');
|
|
19
|
-
var sha3 = require('@noble/hashes/sha3');
|
|
20
|
-
var utils = require('@noble/hashes/utils');
|
|
21
22
|
var fsAsync = require('fs/promises');
|
|
22
23
|
var fs = require('fs');
|
|
23
24
|
var path = require('path');
|
|
@@ -210,20 +211,17 @@ class BaseFileManager {
|
|
|
210
211
|
static getContentUrlFromPath(path) {
|
|
211
212
|
return BaseFileManager.requireImpl().getContentUrlFromPath(path);
|
|
212
213
|
}
|
|
213
|
-
static downloadAllFiles(
|
|
214
|
-
return BaseFileManager.requireImpl().downloadAllFiles(
|
|
215
|
-
transactionIds,
|
|
216
|
-
arweaveHost,
|
|
217
|
-
arweaveBaseUrls,
|
|
218
|
-
excludedTransactions,
|
|
219
|
-
});
|
|
214
|
+
static downloadAllFiles(params) {
|
|
215
|
+
return BaseFileManager.requireImpl().downloadAllFiles(params);
|
|
220
216
|
}
|
|
221
|
-
static downloadFileByTransactionId({ transactionId, arweaveHost, arweaveBaseUrls, excludedTransactions, }) {
|
|
217
|
+
static downloadFileByTransactionId({ transactionId, arweaveHost, arweaveBaseUrls, excludedTransactions, expectedContentHash, compositeChildPaths, }) {
|
|
222
218
|
return BaseFileManager.requireImpl().downloadAllFiles({
|
|
223
219
|
transactionIds: [transactionId],
|
|
224
220
|
arweaveHost,
|
|
225
221
|
arweaveBaseUrls,
|
|
226
222
|
excludedTransactions,
|
|
223
|
+
expectedContentHashes: expectedContentHash ? { [transactionId]: expectedContentHash } : undefined,
|
|
224
|
+
compositeChildPaths: compositeChildPaths ? { [transactionId]: compositeChildPaths } : undefined,
|
|
227
225
|
});
|
|
228
226
|
}
|
|
229
227
|
static resizeImage({ filePath, width, height }) {
|
|
@@ -1508,7 +1506,7 @@ function linkModelToSchema(db, modelId, schemaId) {
|
|
|
1508
1506
|
return current;
|
|
1509
1507
|
}
|
|
1510
1508
|
|
|
1511
|
-
const logger$
|
|
1509
|
+
const logger$B = debug('seedSdk:helpers:db');
|
|
1512
1510
|
/**
|
|
1513
1511
|
* The `properties` storage columns for a property definition, in either the internal shape
|
|
1514
1512
|
* (`storageType`, `localStorageDir`, `filenameSuffix`) or the schema-file shape
|
|
@@ -1614,7 +1612,7 @@ async function getItemStoragePropertiesForModel(modelNameOrType, scope = {}) {
|
|
|
1614
1612
|
async function migrateMetadataForPropertyRename(modelName, oldPropertyName, newPropertyName, propertySchemaFileId, scope = {}) {
|
|
1615
1613
|
const db = BaseDb.getAppDb();
|
|
1616
1614
|
if (!db) {
|
|
1617
|
-
logger$
|
|
1615
|
+
logger$B('[migrateMetadataForPropertyRename] Database not available, skipping');
|
|
1618
1616
|
return 0;
|
|
1619
1617
|
}
|
|
1620
1618
|
// Find the property row by schemaFileId (preferred) or by name
|
|
@@ -1626,7 +1624,7 @@ async function migrateMetadataForPropertyRename(modelName, oldPropertyName, newP
|
|
|
1626
1624
|
propertyId = await getPropertyIdForModelAndName(modelName, oldPropertyName, scope);
|
|
1627
1625
|
}
|
|
1628
1626
|
if (propertyId == null) {
|
|
1629
|
-
logger$
|
|
1627
|
+
logger$B(`[migrateMetadataForPropertyRename] Property not found: ${modelName}.${oldPropertyName}, skipping`);
|
|
1630
1628
|
return 0;
|
|
1631
1629
|
}
|
|
1632
1630
|
// Primary path: update metadata by propertyId
|
|
@@ -1655,11 +1653,11 @@ async function migrateMetadataForPropertyRename(modelName, oldPropertyName, newP
|
|
|
1655
1653
|
.where(drizzleOrm.and(drizzleOrm.eq(metadata$1.propertyName, oldPropertyName), drizzleOrm.eq(metadata$1.modelType, modelType), drizzleOrm.isNull(metadata$1.propertyId)));
|
|
1656
1654
|
}
|
|
1657
1655
|
if (updatedByFallback > 0) {
|
|
1658
|
-
logger$
|
|
1656
|
+
logger$B(`[migrateMetadataForPropertyRename] Fallback updated ${updatedByFallback} rows (propertyId was null)`);
|
|
1659
1657
|
}
|
|
1660
1658
|
const total = updatedByPropertyId + updatedByFallback;
|
|
1661
1659
|
if (total > 0) {
|
|
1662
|
-
logger$
|
|
1660
|
+
logger$B(`[migrateMetadataForPropertyRename] Migrated ${total} metadata rows for ${modelName}.${oldPropertyName} -> ${newPropertyName}`);
|
|
1663
1661
|
}
|
|
1664
1662
|
return total;
|
|
1665
1663
|
}
|
|
@@ -1808,7 +1806,7 @@ const findOrCreateModelRecord = async (db, modelName, modelFileId, schemaId) =>
|
|
|
1808
1806
|
.set({ schemaFileId: modelFileId, isEdited: false })
|
|
1809
1807
|
.where(drizzleOrm.eq(models$1.id, chosen.id));
|
|
1810
1808
|
chosen = { ...chosen, schemaFileId: modelFileId, isEdited: false };
|
|
1811
|
-
logger$
|
|
1809
|
+
logger$B(`Adopted existing model "${modelName}" (id: ${chosen.id}) with schemaFileId "${modelFileId}"`);
|
|
1812
1810
|
}
|
|
1813
1811
|
if (chosen) {
|
|
1814
1812
|
return chosen;
|
|
@@ -2079,7 +2077,7 @@ const addSchemaToDb = async (schema, schemaFileId, schemaData, isDraft, isEdited
|
|
|
2079
2077
|
.limit(1);
|
|
2080
2078
|
if (draftSchemas.length > 0) {
|
|
2081
2079
|
// Found a draft - we'll update it to non-draft below
|
|
2082
|
-
logger$
|
|
2080
|
+
logger$B(`Found draft schema "${schema.name}" with schemaFileId "${draftSchemas[0].schemaFileId}", will update to non-draft with schemaFileId "${schemaFileId}"`);
|
|
2083
2081
|
existingSchemas = draftSchemas;
|
|
2084
2082
|
}
|
|
2085
2083
|
}
|
|
@@ -2099,11 +2097,11 @@ const addSchemaToDb = async (schema, schemaFileId, schemaData, isDraft, isEdited
|
|
|
2099
2097
|
!existingSchemas[0].schemaFileId ||
|
|
2100
2098
|
(isDraft === false && existingSchemas[0].isDraft === true);
|
|
2101
2099
|
if (shouldUpdate) {
|
|
2102
|
-
logger$
|
|
2100
|
+
logger$B(`Updating schemaFileId from "${existingSchemas[0].schemaFileId}" to "${schemaFileId}" for schema "${schema.name}"`);
|
|
2103
2101
|
updates.schemaFileId = schemaFileId;
|
|
2104
2102
|
}
|
|
2105
2103
|
else {
|
|
2106
|
-
logger$
|
|
2104
|
+
logger$B(`Preserving existing schemaFileId "${existingSchemas[0].schemaFileId}" for non-draft schema "${schema.name}" (new schemaFileId "${schemaFileId}" would overwrite it)`);
|
|
2107
2105
|
}
|
|
2108
2106
|
}
|
|
2109
2107
|
// Always update name if it's different (handles name changes)
|
|
@@ -2130,16 +2128,16 @@ const addSchemaToDb = async (schema, schemaFileId, schemaData, isDraft, isEdited
|
|
|
2130
2128
|
updates.updatedAt = schema.updatedAt;
|
|
2131
2129
|
}
|
|
2132
2130
|
if (Object.keys(updates).length > 0) {
|
|
2133
|
-
logger$
|
|
2131
|
+
logger$B(`Applying updates to schema "${schema.name}" (id: ${existingSchemas[0].id}):`, Object.keys(updates));
|
|
2134
2132
|
await db
|
|
2135
2133
|
.update(schemas)
|
|
2136
2134
|
.set(updates)
|
|
2137
2135
|
.where(drizzleOrm.eq(schemas.id, existingSchemas[0].id));
|
|
2138
2136
|
const updated = { ...existingSchemas[0], ...updates };
|
|
2139
|
-
logger$
|
|
2137
|
+
logger$B(`Schema "${schema.name}" updated. New schemaFileId: "${updated.schemaFileId}", isDraft: ${updated.isDraft}`);
|
|
2140
2138
|
return updated;
|
|
2141
2139
|
}
|
|
2142
|
-
logger$
|
|
2140
|
+
logger$B(`No updates needed for schema "${schema.name}" (id: ${existingSchemas[0].id})`);
|
|
2143
2141
|
return existingSchemas[0];
|
|
2144
2142
|
}
|
|
2145
2143
|
const newSchema = await db.insert(schemas).values({
|
|
@@ -2322,7 +2320,7 @@ modelId) {
|
|
|
2322
2320
|
// Compare database value with schema file value
|
|
2323
2321
|
// If they differ, the property has been edited
|
|
2324
2322
|
if (dbProperty.dataType !== schemaFileValue.dataType) {
|
|
2325
|
-
logger$
|
|
2323
|
+
logger$B(`Property ${modelName}:${propertyName} has been edited (dataType differs: DB=${dbProperty.dataType}, Schema=${schemaFileValue.dataType})`);
|
|
2326
2324
|
return true;
|
|
2327
2325
|
}
|
|
2328
2326
|
// Check refModelId if it's a relation
|
|
@@ -2331,19 +2329,19 @@ modelId) {
|
|
|
2331
2329
|
if (refModelRecord) {
|
|
2332
2330
|
const expectedRefModelId = refModelRecord.id;
|
|
2333
2331
|
if (dbProperty.refModelId !== expectedRefModelId) {
|
|
2334
|
-
logger$
|
|
2332
|
+
logger$B(`Property ${modelName}:${propertyName} has been edited (refModelId differs)`);
|
|
2335
2333
|
return true;
|
|
2336
2334
|
}
|
|
2337
2335
|
}
|
|
2338
2336
|
}
|
|
2339
2337
|
else if (dbProperty.refModelId !== null) {
|
|
2340
2338
|
// Schema file has no ref, but DB has one
|
|
2341
|
-
logger$
|
|
2339
|
+
logger$B(`Property ${modelName}:${propertyName} has been edited (refModelId differs)`);
|
|
2342
2340
|
return true;
|
|
2343
2341
|
}
|
|
2344
2342
|
// Check refValueType
|
|
2345
2343
|
if (dbProperty.refValueType !== (schemaFileValue.refValueType || null)) {
|
|
2346
|
-
logger$
|
|
2344
|
+
logger$B(`Property ${modelName}:${propertyName} has been edited (refValueType differs)`);
|
|
2347
2345
|
return true;
|
|
2348
2346
|
}
|
|
2349
2347
|
// Check required (for Relation/Image properties)
|
|
@@ -2351,7 +2349,7 @@ modelId) {
|
|
|
2351
2349
|
const schemaRequired = !!schemaFileValue.required;
|
|
2352
2350
|
const dbRequired = dbProperty.required === true;
|
|
2353
2351
|
if (dbRequired !== schemaRequired) {
|
|
2354
|
-
logger$
|
|
2352
|
+
logger$B(`Property ${modelName}:${propertyName} has been edited (required differs: DB=${dbRequired}, Schema=${schemaRequired})`);
|
|
2355
2353
|
return true;
|
|
2356
2354
|
}
|
|
2357
2355
|
}
|
|
@@ -2367,7 +2365,7 @@ modelId) {
|
|
|
2367
2365
|
if (dbStorage.storageType !== fileStorage.storageType ||
|
|
2368
2366
|
dbStorage.localStorageDir !== fileStorage.localStorageDir ||
|
|
2369
2367
|
dbStorage.filenameSuffix !== fileStorage.filenameSuffix) {
|
|
2370
|
-
logger$
|
|
2368
|
+
logger$B(`Property ${modelName}:${propertyName} has been edited (storage settings differ)`);
|
|
2371
2369
|
return true;
|
|
2372
2370
|
}
|
|
2373
2371
|
}
|
|
@@ -2379,7 +2377,7 @@ modelId) {
|
|
|
2379
2377
|
}
|
|
2380
2378
|
catch (error) {
|
|
2381
2379
|
// If we can't check, assume it's not edited to be safe
|
|
2382
|
-
logger$
|
|
2380
|
+
logger$B('Error checking if property is edited:', error);
|
|
2383
2381
|
return false;
|
|
2384
2382
|
}
|
|
2385
2383
|
}
|
|
@@ -2404,11 +2402,11 @@ const addModelsToDb$1 = async (models, schema, modelRenames, schemaFileData) =>
|
|
|
2404
2402
|
// SchemaType includes id, so check if it's set
|
|
2405
2403
|
if (schema.id !== null && schema.id !== undefined) {
|
|
2406
2404
|
schemaRecord = schema;
|
|
2407
|
-
logger$
|
|
2405
|
+
logger$B(`Using existing schema record with id: ${schemaRecord.id}`);
|
|
2408
2406
|
}
|
|
2409
2407
|
else {
|
|
2410
2408
|
schemaRecord = await addSchemaToDb(schema, schemaFileData?.schemaFileId);
|
|
2411
|
-
logger$
|
|
2409
|
+
logger$B(`Created/found schema record with id: ${schemaRecord.id}`);
|
|
2412
2410
|
}
|
|
2413
2411
|
}
|
|
2414
2412
|
// Handle model renames first if provided
|
|
@@ -2434,7 +2432,7 @@ const addModelsToDb$1 = async (models, schema, modelRenames, schemaFileData) =>
|
|
|
2434
2432
|
.where(drizzleOrm.eq(models$1.schemaFileId, modelFileId))
|
|
2435
2433
|
.limit(1);
|
|
2436
2434
|
if (existingWithFileId.length > 0 && existingWithFileId[0].id !== modelRecord.id) {
|
|
2437
|
-
logger$
|
|
2435
|
+
logger$B(`WARNING: Model "${modelName}" (id: ${modelRecord.id}) conflicts with existing model "${existingWithFileId[0].name}" (id: ${existingWithFileId[0].id}) both trying to use schemaFileId "${modelFileId}"`);
|
|
2438
2436
|
modelRecord = existingWithFileId[0];
|
|
2439
2437
|
}
|
|
2440
2438
|
else if (!modelRecord.schemaFileId) {
|
|
@@ -2492,7 +2490,7 @@ const addModelsToDb$1 = async (models, schema, modelRenames, schemaFileData) =>
|
|
|
2492
2490
|
if (!propertyFileId) {
|
|
2493
2491
|
const { generateId } = await Promise.resolve().then(function () { return generateId$1; });
|
|
2494
2492
|
propertyFileId = generateId();
|
|
2495
|
-
logger$
|
|
2493
|
+
logger$B(`Generated propertyFileId "${propertyFileId}" for property "${modelName}:${propertyName}" (not found in propertyFileIds map)`);
|
|
2496
2494
|
}
|
|
2497
2495
|
// First, try to find by schemaFileId (preferred for change tracking)
|
|
2498
2496
|
let existingProperty;
|
|
@@ -2559,7 +2557,7 @@ const addModelsToDb$1 = async (models, schema, modelRenames, schemaFileData) =>
|
|
|
2559
2557
|
if (potentialMatches.length > 0) {
|
|
2560
2558
|
const matchedProperty = potentialMatches[0];
|
|
2561
2559
|
existingProperty = matchedProperty;
|
|
2562
|
-
logger$
|
|
2560
|
+
logger$B(`Found renamed property: ${modelName}:${matchedProperty.name} -> ${propertyName} (matched by characteristics)`);
|
|
2563
2561
|
}
|
|
2564
2562
|
}
|
|
2565
2563
|
if (existingProperty) {
|
|
@@ -2580,7 +2578,7 @@ const addModelsToDb$1 = async (models, schema, modelRenames, schemaFileData) =>
|
|
|
2580
2578
|
}, modelRecord.id);
|
|
2581
2579
|
if (isPropertyEdited) {
|
|
2582
2580
|
// Property has been locally edited - skip update to preserve local changes
|
|
2583
|
-
logger$
|
|
2581
|
+
logger$B(`Skipping update for property ${modelName}:${existingProperty.name} -> ${propertyName} - it has been locally edited and will be preserved`);
|
|
2584
2582
|
continue;
|
|
2585
2583
|
}
|
|
2586
2584
|
// Property exists (possibly with different name) - update it with new values from schema file
|
|
@@ -2631,18 +2629,18 @@ const addModelsToDb$1 = async (models, schema, modelRenames, schemaFileData) =>
|
|
|
2631
2629
|
if (existingByFileId.length > 0) {
|
|
2632
2630
|
const found = existingByFileId[0];
|
|
2633
2631
|
matchedDbPropertyIds.add(found.id);
|
|
2634
|
-
logger$
|
|
2632
|
+
logger$B(`Property ${modelName}:${propertyName} already exists by schemaFileId "${propertyFileId}" (id: ${found.id}), skipping insert`);
|
|
2635
2633
|
continue;
|
|
2636
2634
|
}
|
|
2637
2635
|
}
|
|
2638
|
-
logger$
|
|
2636
|
+
logger$B(`Creating new property ${modelName}:${propertyName} with schemaFileId: ${propertyData.schemaFileId}`);
|
|
2639
2637
|
try {
|
|
2640
2638
|
await db.insert(properties).values(propertyDataWithIsEdited);
|
|
2641
2639
|
}
|
|
2642
2640
|
catch (insertError) {
|
|
2643
2641
|
// Treat UNIQUE constraint as success - property already exists (e.g. from concurrent addModelsToDb call)
|
|
2644
2642
|
if (isSqliteUniqueConstraintError(insertError)) {
|
|
2645
|
-
logger$
|
|
2643
|
+
logger$B(`Property ${modelName}:${propertyName} already exists (UNIQUE constraint), treating as success`);
|
|
2646
2644
|
}
|
|
2647
2645
|
else {
|
|
2648
2646
|
throw insertError;
|
|
@@ -2653,14 +2651,14 @@ const addModelsToDb$1 = async (models, schema, modelRenames, schemaFileData) =>
|
|
|
2653
2651
|
}
|
|
2654
2652
|
catch (error) {
|
|
2655
2653
|
const errorMessage = error?.message || String(error);
|
|
2656
|
-
logger$
|
|
2654
|
+
logger$B(`Error processing model "${modelName}": ${errorMessage}`);
|
|
2657
2655
|
modelProcessingErrors.push({ modelName, error: errorMessage });
|
|
2658
2656
|
// Continue processing other models even if one fails
|
|
2659
2657
|
}
|
|
2660
2658
|
}
|
|
2661
2659
|
if (modelProcessingErrors.length > 0) {
|
|
2662
2660
|
console.warn(`[addModelsToDb] Errors occurred while processing ${modelProcessingErrors.length} model(s):`, modelProcessingErrors.map(e => `${e.modelName}: ${e.error}`).join('; '));
|
|
2663
|
-
logger$
|
|
2661
|
+
logger$B(`Errors processing models: ${modelProcessingErrors.map(e => e.modelName).join(', ')}`);
|
|
2664
2662
|
}
|
|
2665
2663
|
// If schema was provided, create modelSchema join records to connect models to the schema
|
|
2666
2664
|
if (schemaRecord && schemaRecord.id) {
|
|
@@ -2668,7 +2666,7 @@ const addModelsToDb$1 = async (models, schema, modelRenames, schemaFileData) =>
|
|
|
2668
2666
|
.filter(([, r]) => r.id)
|
|
2669
2667
|
.map(([name, r]) => ({ modelName: name, modelId: r.id }));
|
|
2670
2668
|
if (modelIds.length === 0) {
|
|
2671
|
-
logger$
|
|
2669
|
+
logger$B('No model IDs to link, skipping schema linking');
|
|
2672
2670
|
}
|
|
2673
2671
|
else {
|
|
2674
2672
|
// Single batch query: get all existing joins for this schema
|
|
@@ -2682,13 +2680,13 @@ const addModelsToDb$1 = async (models, schema, modelRenames, schemaFileData) =>
|
|
|
2682
2680
|
for (const { modelName, modelId } of toInsert) {
|
|
2683
2681
|
if (!(await linkModelToSchema(db, modelId, schemaRecord.id)))
|
|
2684
2682
|
continue;
|
|
2685
|
-
logger$
|
|
2683
|
+
logger$B(`Created join table entry for model ${modelName} (id: ${modelId}) to schema (id: ${schemaRecord.id})`);
|
|
2686
2684
|
}
|
|
2687
2685
|
}
|
|
2688
2686
|
}
|
|
2689
2687
|
}
|
|
2690
2688
|
else if (schemaRecord && !schemaRecord.id) {
|
|
2691
|
-
logger$
|
|
2689
|
+
logger$B(`Warning: schemaRecord provided but has no id, cannot create join table entries`);
|
|
2692
2690
|
}
|
|
2693
2691
|
};
|
|
2694
2692
|
/**
|
|
@@ -2701,7 +2699,7 @@ const addModelsToDb$1 = async (models, schema, modelRenames, schemaFileData) =>
|
|
|
2701
2699
|
const loadModelsFromDbForSchema = async (schemaId) => {
|
|
2702
2700
|
const db = BaseDb.getAppDb();
|
|
2703
2701
|
if (!db) {
|
|
2704
|
-
logger$
|
|
2702
|
+
logger$B('Database not found, cannot load models from DB');
|
|
2705
2703
|
return {};
|
|
2706
2704
|
}
|
|
2707
2705
|
try {
|
|
@@ -2770,11 +2768,11 @@ const loadModelsFromDbForSchema = async (schemaId) => {
|
|
|
2770
2768
|
// or reconstructed from schemaData if available
|
|
2771
2769
|
};
|
|
2772
2770
|
}
|
|
2773
|
-
logger$
|
|
2771
|
+
logger$B(`Loaded ${Object.keys(models).length} models from database for schema ${schemaId}`);
|
|
2774
2772
|
return models;
|
|
2775
2773
|
}
|
|
2776
2774
|
catch (error) {
|
|
2777
|
-
logger$
|
|
2775
|
+
logger$B(`Error loading models from database for schema ${schemaId}:`, error);
|
|
2778
2776
|
return {};
|
|
2779
2777
|
}
|
|
2780
2778
|
};
|
|
@@ -2891,34 +2889,34 @@ const savePropertyToDb = async (property) => {
|
|
|
2891
2889
|
// Find existing property - try multiple strategies to handle name changes
|
|
2892
2890
|
// 1. First try by schemaFileId (most reliable - doesn't change when name changes)
|
|
2893
2891
|
let existingProperties = [];
|
|
2894
|
-
logger$
|
|
2892
|
+
logger$B(`[savePropertyToDb] Looking for property ${property.modelName}:${property.name} (schemaFileId: ${schemaFileId}, originalName: ${property._originalValues?.name})`);
|
|
2895
2893
|
if (schemaFileId) {
|
|
2896
2894
|
existingProperties = await db
|
|
2897
2895
|
.select()
|
|
2898
2896
|
.from(properties)
|
|
2899
2897
|
.where(drizzleOrm.and(drizzleOrm.eq(properties.schemaFileId, schemaFileId), drizzleOrm.eq(properties.modelId, modelRecord.id)))
|
|
2900
2898
|
.limit(1);
|
|
2901
|
-
logger$
|
|
2899
|
+
logger$B(`[savePropertyToDb] Found ${existingProperties.length} properties by schemaFileId`);
|
|
2902
2900
|
}
|
|
2903
2901
|
// 2. If not found by schemaFileId, try by original name (if name was changed)
|
|
2904
2902
|
if (existingProperties.length === 0 && property._originalValues?.name && property._originalValues.name !== property.name) {
|
|
2905
|
-
logger$
|
|
2903
|
+
logger$B(`[savePropertyToDb] Trying to find by original name: ${property._originalValues.name}`);
|
|
2906
2904
|
existingProperties = await db
|
|
2907
2905
|
.select()
|
|
2908
2906
|
.from(properties)
|
|
2909
2907
|
.where(drizzleOrm.and(drizzleOrm.eq(properties.name, property._originalValues.name), drizzleOrm.eq(properties.modelId, modelRecord.id)))
|
|
2910
2908
|
.limit(1);
|
|
2911
|
-
logger$
|
|
2909
|
+
logger$B(`[savePropertyToDb] Found ${existingProperties.length} properties by original name`);
|
|
2912
2910
|
}
|
|
2913
2911
|
// 3. Fallback: try by current name
|
|
2914
2912
|
if (existingProperties.length === 0) {
|
|
2915
|
-
logger$
|
|
2913
|
+
logger$B(`[savePropertyToDb] Trying to find by current name: ${property.name}`);
|
|
2916
2914
|
existingProperties = await db
|
|
2917
2915
|
.select()
|
|
2918
2916
|
.from(properties)
|
|
2919
2917
|
.where(drizzleOrm.and(drizzleOrm.eq(properties.name, property.name), drizzleOrm.eq(properties.modelId, modelRecord.id)))
|
|
2920
2918
|
.limit(1);
|
|
2921
|
-
logger$
|
|
2919
|
+
logger$B(`[savePropertyToDb] Found ${existingProperties.length} properties by current name`);
|
|
2922
2920
|
}
|
|
2923
2921
|
// Prepare property data
|
|
2924
2922
|
const propertyData = {
|
|
@@ -2967,7 +2965,7 @@ const savePropertyToDb = async (property) => {
|
|
|
2967
2965
|
isEdited: true, // Mark as edited when saving changes
|
|
2968
2966
|
})
|
|
2969
2967
|
.where(drizzleOrm.eq(properties.id, existingProperty.id));
|
|
2970
|
-
logger$
|
|
2968
|
+
logger$B(`Updated property ${property.modelName}:${property._originalValues?.name || 'unknown'} -> ${property.name} in database`);
|
|
2971
2969
|
}
|
|
2972
2970
|
else {
|
|
2973
2971
|
// Property doesn't exist, create it
|
|
@@ -2977,12 +2975,12 @@ const savePropertyToDb = async (property) => {
|
|
|
2977
2975
|
...propertyData,
|
|
2978
2976
|
isEdited: true, // Runtime-created properties are edited
|
|
2979
2977
|
});
|
|
2980
|
-
logger$
|
|
2978
|
+
logger$B(`Created property ${property.modelName}:${property.name} in database`);
|
|
2981
2979
|
}
|
|
2982
2980
|
catch (insertError) {
|
|
2983
2981
|
// Treat UNIQUE constraint as success - property already exists (e.g. from concurrent addModelsToDb or race)
|
|
2984
2982
|
if (isSqliteUniqueConstraintError(insertError)) {
|
|
2985
|
-
logger$
|
|
2983
|
+
logger$B(`Property ${property.modelName}:${property.name} already exists (UNIQUE constraint), treating as success`);
|
|
2986
2984
|
}
|
|
2987
2985
|
else {
|
|
2988
2986
|
throw insertError;
|
|
@@ -3099,7 +3097,7 @@ async function writeModelToDb(modelFileId, data) {
|
|
|
3099
3097
|
if (doubleCheck.length > 0) {
|
|
3100
3098
|
// Another process created it, use existing
|
|
3101
3099
|
modelId = doubleCheck[0].id;
|
|
3102
|
-
logger$
|
|
3100
|
+
logger$B(`Model with schemaFileId "${modelFileId}" was created by another process, using existing record (id: ${modelId})`);
|
|
3103
3101
|
}
|
|
3104
3102
|
else {
|
|
3105
3103
|
try {
|
|
@@ -3113,7 +3111,7 @@ async function writeModelToDb(modelFileId, data) {
|
|
|
3113
3111
|
catch (error) {
|
|
3114
3112
|
// Handle unique constraint violation
|
|
3115
3113
|
if (isSqliteUniqueConstraintError(error)) {
|
|
3116
|
-
logger$
|
|
3114
|
+
logger$B(`Unique constraint violation for schemaFileId "${modelFileId}", attempting to find existing model`);
|
|
3117
3115
|
const existing = await db
|
|
3118
3116
|
.select()
|
|
3119
3117
|
.from(models$1)
|
|
@@ -3121,7 +3119,7 @@ async function writeModelToDb(modelFileId, data) {
|
|
|
3121
3119
|
.limit(1);
|
|
3122
3120
|
if (existing.length > 0) {
|
|
3123
3121
|
modelId = existing[0].id;
|
|
3124
|
-
logger$
|
|
3122
|
+
logger$B(`Found existing model with schemaFileId "${modelFileId}" (id: ${modelId}) after constraint violation`);
|
|
3125
3123
|
}
|
|
3126
3124
|
else {
|
|
3127
3125
|
throw error;
|
|
@@ -3191,7 +3189,7 @@ async function writeModelToDb(modelFileId, data) {
|
|
|
3191
3189
|
if (schemaCheck.length === 0) {
|
|
3192
3190
|
throw new Error(`Schema with id ${data.schemaId} does not exist in database. Cannot create join record.`);
|
|
3193
3191
|
}
|
|
3194
|
-
logger$
|
|
3192
|
+
logger$B(`Creating join record: modelId=${modelId}, schemaId=${data.schemaId} (both verified to exist)`);
|
|
3195
3193
|
await linkModelToSchema(db, modelId, data.schemaId);
|
|
3196
3194
|
// Notify React useModels so it can invalidate; live query over join often doesn't re-run when model_schemas is inserted.
|
|
3197
3195
|
// Yield so the insert is visible to the refetch that will run when the broadcast is received.
|
|
@@ -3206,13 +3204,13 @@ async function writeModelToDb(modelFileId, data) {
|
|
|
3206
3204
|
}
|
|
3207
3205
|
catch (_) { }
|
|
3208
3206
|
}
|
|
3209
|
-
logger$
|
|
3207
|
+
logger$B(`Successfully created join record for model ${data.modelName} (id: ${modelId}) to schema (id: ${data.schemaId})`);
|
|
3210
3208
|
}
|
|
3211
3209
|
catch (error) {
|
|
3212
3210
|
if (error?.code === 'SQLITE_CONSTRAINT_FOREIGNKEY') {
|
|
3213
|
-
logger$
|
|
3214
|
-
logger$
|
|
3215
|
-
logger$
|
|
3211
|
+
logger$B(`FOREIGN KEY constraint failed when creating join record for model "${data.modelName}"`);
|
|
3212
|
+
logger$B(`modelId: ${modelId}, schemaId: ${data.schemaId}`);
|
|
3213
|
+
logger$B(`Error details:`, error);
|
|
3216
3214
|
// Re-throw with more context
|
|
3217
3215
|
throw new Error(`FOREIGN KEY constraint failed when creating join record for model "${data.modelName}" (modelId: ${modelId}, schemaId: ${data.schemaId}). ${error.message}`);
|
|
3218
3216
|
}
|
|
@@ -3232,7 +3230,7 @@ async function writeModelToDb(modelFileId, data) {
|
|
|
3232
3230
|
if (existingProps.length > 0 && existingProps[0].schemaFileId) {
|
|
3233
3231
|
// Use existing property's schemaFileId
|
|
3234
3232
|
propertyFileId = existingProps[0].schemaFileId;
|
|
3235
|
-
logger$
|
|
3233
|
+
logger$B(`Using existing propertyFileId "${propertyFileId}" for property "${data.modelName}:${propName}"`);
|
|
3236
3234
|
}
|
|
3237
3235
|
else if (propData.id) {
|
|
3238
3236
|
// Use provided ID
|
|
@@ -3243,7 +3241,7 @@ async function writeModelToDb(modelFileId, data) {
|
|
|
3243
3241
|
// IDs should be generated in the import process before calling writeModelToDb
|
|
3244
3242
|
const { generateId } = await Promise.resolve().then(function () { return generateId$1; });
|
|
3245
3243
|
propertyFileId = generateId();
|
|
3246
|
-
logger$
|
|
3244
|
+
logger$B(`Generated propertyFileId "${propertyFileId}" for property "${data.modelName}:${propName}"`);
|
|
3247
3245
|
}
|
|
3248
3246
|
await writePropertyToDb(propertyFileId, {
|
|
3249
3247
|
modelId,
|
|
@@ -3252,7 +3250,7 @@ async function writeModelToDb(modelFileId, data) {
|
|
|
3252
3250
|
});
|
|
3253
3251
|
}
|
|
3254
3252
|
}
|
|
3255
|
-
logger$
|
|
3253
|
+
logger$B(`Wrote model ${data.modelName} (${modelFileId}) to database`);
|
|
3256
3254
|
}
|
|
3257
3255
|
/**
|
|
3258
3256
|
* Write property to database
|
|
@@ -3327,7 +3325,7 @@ async function writePropertyToDb(propertyFileId, data, isEdited = false) {
|
|
|
3327
3325
|
.update(properties)
|
|
3328
3326
|
.set(propertyData)
|
|
3329
3327
|
.where(drizzleOrm.eq(properties.id, existing.id));
|
|
3330
|
-
logger$
|
|
3328
|
+
logger$B(`Updated property ${data.name} (${propertyFileId}) in database`);
|
|
3331
3329
|
}
|
|
3332
3330
|
else {
|
|
3333
3331
|
// Property doesn't exist, create it
|
|
@@ -3343,17 +3341,17 @@ async function writePropertyToDb(propertyFileId, data, isEdited = false) {
|
|
|
3343
3341
|
.update(properties)
|
|
3344
3342
|
.set(propertyData)
|
|
3345
3343
|
.where(drizzleOrm.eq(properties.id, doubleCheck[0].id));
|
|
3346
|
-
logger$
|
|
3344
|
+
logger$B(`Property with schemaFileId "${propertyFileId}" was created by another process, updated existing record`);
|
|
3347
3345
|
}
|
|
3348
3346
|
else {
|
|
3349
3347
|
try {
|
|
3350
3348
|
await db.insert(properties).values({ required: false, ...propertyData });
|
|
3351
|
-
logger$
|
|
3349
|
+
logger$B(`Created property ${data.name} (${propertyFileId}) in database`);
|
|
3352
3350
|
}
|
|
3353
3351
|
catch (error) {
|
|
3354
3352
|
// Handle unique constraint violation
|
|
3355
3353
|
if (isSqliteUniqueConstraintError(error)) {
|
|
3356
|
-
logger$
|
|
3354
|
+
logger$B(`Unique constraint violation for property schemaFileId "${propertyFileId}", attempting to find existing property`);
|
|
3357
3355
|
const existing = await db
|
|
3358
3356
|
.select()
|
|
3359
3357
|
.from(properties)
|
|
@@ -3364,7 +3362,7 @@ async function writePropertyToDb(propertyFileId, data, isEdited = false) {
|
|
|
3364
3362
|
.update(properties)
|
|
3365
3363
|
.set(propertyData)
|
|
3366
3364
|
.where(drizzleOrm.eq(properties.id, existing[0].id));
|
|
3367
|
-
logger$
|
|
3365
|
+
logger$B(`Found existing property with schemaFileId "${propertyFileId}" (id: ${existing[0].id}) after constraint violation, updated it`);
|
|
3368
3366
|
}
|
|
3369
3367
|
else {
|
|
3370
3368
|
throw new Error(`Failed to create or find property "${data.name}" with schemaFileId "${propertyFileId}": ${error.message}`);
|
|
@@ -3637,7 +3635,114 @@ const supportsOpfsFileDownloads = () => {
|
|
|
3637
3635
|
return isBrowser() || isElectronRenderer();
|
|
3638
3636
|
};
|
|
3639
3637
|
|
|
3640
|
-
|
|
3638
|
+
/**
|
|
3639
|
+
* Tag every Seed upload carries with the hex digest of its data. Producers: the SDK's
|
|
3640
|
+
* `getPublishUploadData` / `getPublishUploads` (and through them both the L1 transaction path and
|
|
3641
|
+
* the bundler / DataItem path in `@seedprotocol/publish`). Consumer: the files download
|
|
3642
|
+
* (`events/files/download.ts` and the download worker), which verifies downloaded bytes against it
|
|
3643
|
+
* and copies a verified value into `metadata.content_hash`.
|
|
3644
|
+
*/
|
|
3645
|
+
const CONTENT_SHA256_TAG = 'Content-SHA-256';
|
|
3646
|
+
const CONTENT_TYPE_TAG = 'Content-Type';
|
|
3647
|
+
/**
|
|
3648
|
+
* Lowercase hex SHA-256 of `data`: the value of the {@link CONTENT_SHA256_TAG} tag.
|
|
3649
|
+
*
|
|
3650
|
+
* Uses @noble/hashes rather than WebCrypto so it also works outside secure contexts (WebCrypto's
|
|
3651
|
+
* `crypto.subtle` is missing on plain-http origins); the digest is the same.
|
|
3652
|
+
*
|
|
3653
|
+
* In SDK 0.7.2 and earlier the L1 transaction path (`getPublishUploads`) wrote SHA3-256 under this tag name
|
|
3654
|
+
* while the bundler path wrote SHA-256. Uploads from that path still carry SHA3-256 digests; use
|
|
3655
|
+
* {@link contentSha256TagMatches} to check a tag value against bytes.
|
|
3656
|
+
*/
|
|
3657
|
+
const getContentSha256 = (data) => utils.bytesToHex(sha256.sha256(data));
|
|
3658
|
+
/**
|
|
3659
|
+
* Tags for one upload, in the order every upload uses: content hash, content type, then any
|
|
3660
|
+
* configured extra tags (e.g. App-Name from `arweaveUploadTags`).
|
|
3661
|
+
*/
|
|
3662
|
+
const buildArweaveContentTags = (contentHash, contentType, extra) => {
|
|
3663
|
+
const tags = [];
|
|
3664
|
+
if (contentHash)
|
|
3665
|
+
tags.push({ name: CONTENT_SHA256_TAG, value: contentHash });
|
|
3666
|
+
if (contentType)
|
|
3667
|
+
tags.push({ name: CONTENT_TYPE_TAG, value: contentType });
|
|
3668
|
+
if (extra?.length)
|
|
3669
|
+
tags.push(...extra);
|
|
3670
|
+
return tags;
|
|
3671
|
+
};
|
|
3672
|
+
|
|
3673
|
+
/** Subdirectory of the files root that holds a storage seed's file, by its data type. */
|
|
3674
|
+
const getStorageSubdirForDataType = (dataType) => {
|
|
3675
|
+
switch (normalizeDataType(dataType ?? undefined)) {
|
|
3676
|
+
case 'Image':
|
|
3677
|
+
return 'images';
|
|
3678
|
+
case 'Html':
|
|
3679
|
+
return 'html';
|
|
3680
|
+
case 'Json':
|
|
3681
|
+
return 'json';
|
|
3682
|
+
case 'File':
|
|
3683
|
+
default:
|
|
3684
|
+
// Unknown types are generic files; `images/` is only for what the SDK knows is an image.
|
|
3685
|
+
return 'files';
|
|
3686
|
+
}
|
|
3687
|
+
};
|
|
3688
|
+
/** Absolute directory for a storage seed's file (`images/`, `files/`, `html/` or `json/` under the files root). */
|
|
3689
|
+
const getStorageDirForDataType = (dataType) => BaseFileManager.getFilesPath(getStorageSubdirForDataType(dataType));
|
|
3690
|
+
/**
|
|
3691
|
+
* Subdirectory (relative to the files root, no leading slash) for an ItemStorage property's file:
|
|
3692
|
+
* its model's `storage.path` (`localStorageDir`), else `files`.
|
|
3693
|
+
*/
|
|
3694
|
+
const getItemStorageSubdir = (localStorageDir) => (localStorageDir ?? '').replace(/^\/+/, '').replace(/\/+$/, '') || 'files';
|
|
3695
|
+
/**
|
|
3696
|
+
* File name a local save gives an ItemStorage property: `<seedUid or seedLocalId><extension>`
|
|
3697
|
+
* (see saveItemStorage). Downloaded content is named after its transaction instead
|
|
3698
|
+
* (`<txId><extension>`, see syncDbWithEas), which is what the metadata row's refResolvedValue says.
|
|
3699
|
+
*/
|
|
3700
|
+
const getItemStorageLocalFileName = (seedId, filenameSuffix) => `${seedId}${filenameSuffix ?? ''}`;
|
|
3701
|
+
|
|
3702
|
+
/** Rows per query: well under SQLite's bound-parameter limit. */
|
|
3703
|
+
const CHUNK_SIZE = 400;
|
|
3704
|
+
/**
|
|
3705
|
+
* Where the download worker saves each child of a composite upload, by transaction id then child
|
|
3706
|
+
* (property) name: a path relative to the files root.
|
|
3707
|
+
*
|
|
3708
|
+
* The paths come from the ItemStorage rows sync derives from a `storage_transaction_id`
|
|
3709
|
+
* attestation (see `createMetadataRecordsForStorageTransactionId`): property value = the
|
|
3710
|
+
* transaction id, `refResolvedValue` = `<txId><extension>`, `localStorageDir` = the model's storage
|
|
3711
|
+
* directory. Those rows are what hydration reads, so a child saved here is found as that property's
|
|
3712
|
+
* file. Transactions with no such rows are left out; the worker then falls back to the legacy
|
|
3713
|
+
* html / json layout.
|
|
3714
|
+
*/
|
|
3715
|
+
const getCompositeChildPaths = async (transactionIds) => {
|
|
3716
|
+
const out = {};
|
|
3717
|
+
const appDb = BaseDb.getAppDb();
|
|
3718
|
+
if (!appDb || transactionIds.length === 0)
|
|
3719
|
+
return out;
|
|
3720
|
+
for (let i = 0; i < transactionIds.length; i += CHUNK_SIZE) {
|
|
3721
|
+
const chunk = transactionIds.slice(i, i + CHUNK_SIZE);
|
|
3722
|
+
const rows = await appDb
|
|
3723
|
+
.select({
|
|
3724
|
+
transactionId: metadata$1.propertyValue,
|
|
3725
|
+
propertyName: metadata$1.propertyName,
|
|
3726
|
+
refResolvedValue: metadata$1.refResolvedValue,
|
|
3727
|
+
localStorageDir: metadata$1.localStorageDir,
|
|
3728
|
+
})
|
|
3729
|
+
.from(metadata$1)
|
|
3730
|
+
.where(drizzleOrm.and(drizzleOrm.isNotNull(metadata$1.derivedFromUid), drizzleOrm.isNotNull(metadata$1.refResolvedValue), drizzleOrm.inArray(metadata$1.propertyValue, chunk)));
|
|
3731
|
+
for (const row of rows) {
|
|
3732
|
+
const { transactionId, propertyName, refResolvedValue } = row;
|
|
3733
|
+
if (!transactionId || !propertyName || !refResolvedValue)
|
|
3734
|
+
continue;
|
|
3735
|
+
// A file name, never a path: anything else is not where the SDK would look.
|
|
3736
|
+
if (refResolvedValue.includes('/') || refResolvedValue.includes('\\'))
|
|
3737
|
+
continue;
|
|
3738
|
+
const byName = (out[transactionId] ?? (out[transactionId] = {}));
|
|
3739
|
+
byName[propertyName] ?? (byName[propertyName] = `${getItemStorageSubdir(row.localStorageDir)}/${refResolvedValue}`);
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
return out;
|
|
3743
|
+
};
|
|
3744
|
+
|
|
3745
|
+
const logger$A = debug('seedSdk:files:download');
|
|
3641
3746
|
const AUTO_BULK_DOWNLOAD_THROTTLE_MS = 10000;
|
|
3642
3747
|
/** Avoid refetch-on-every-call: transient empty indexer responses were overwriting good cache (see trailing throttle ~10s after success). */
|
|
3643
3748
|
const GET_FILES_METADATA_STALE_MS = 120000;
|
|
@@ -3647,7 +3752,7 @@ const lazyDownloadInFlightByTransactionId = new Map();
|
|
|
3647
3752
|
const syncDbFiles = async (endpoints) => {
|
|
3648
3753
|
// TODO: Implement syncDbFiles functionality if needed
|
|
3649
3754
|
// This was previously in @/services/internal/helpers
|
|
3650
|
-
logger$
|
|
3755
|
+
logger$A('[download] syncDbFiles called but not yet implemented');
|
|
3651
3756
|
return Promise.resolve();
|
|
3652
3757
|
};
|
|
3653
3758
|
const getAppDbReady = async () => {
|
|
@@ -3787,76 +3892,117 @@ const parseTransactionIdsFromMetadata = (filesMetadata, excludedTransactions) =>
|
|
|
3787
3892
|
}
|
|
3788
3893
|
return transactionIds;
|
|
3789
3894
|
};
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3895
|
+
/** Content-SHA-256 tag values for the transactions that have one, from one batched tag lookup (cached). */
|
|
3896
|
+
const getExpectedContentHashes = async (transactionIds) => {
|
|
3897
|
+
const queryClient = eas.BaseQueryClient.getQueryClient();
|
|
3898
|
+
const tagsById = new Map();
|
|
3899
|
+
const toFetch = [];
|
|
3900
|
+
for (const transactionId of transactionIds) {
|
|
3901
|
+
// Tags never change once a transaction is indexed, so a cached lookup stays good.
|
|
3902
|
+
const cached = queryClient.getQueryData?.(['getTransactionTags', transactionId]);
|
|
3903
|
+
if (cached)
|
|
3904
|
+
tagsById.set(transactionId, cached);
|
|
3905
|
+
else
|
|
3906
|
+
toFetch.push(transactionId);
|
|
3907
|
+
}
|
|
3908
|
+
if (toFetch.length > 0) {
|
|
3909
|
+
try {
|
|
3910
|
+
const fetched = await arweave.getTransactionTagsByIds(toFetch, {
|
|
3911
|
+
client: arweave.BaseArweaveClient.getArweaveClient(),
|
|
3912
|
+
onError: (error) => logger$A('[download] transaction tags lookup failed', error),
|
|
3913
|
+
});
|
|
3914
|
+
for (const [transactionId, tags] of fetched) {
|
|
3915
|
+
tagsById.set(transactionId, tags);
|
|
3916
|
+
// Seed the per-transaction cache entry (the client interface has no setQueryData).
|
|
3917
|
+
void queryClient
|
|
3918
|
+
.fetchQuery({ queryKey: ['getTransactionTags', transactionId], queryFn: async () => tags })
|
|
3919
|
+
.catch(() => { });
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
3922
|
+
catch (error) {
|
|
3923
|
+
logger$A('[download] transaction tags lookup failed', error);
|
|
3924
|
+
}
|
|
3793
3925
|
}
|
|
3794
|
-
const
|
|
3795
|
-
|
|
3796
|
-
|
|
3926
|
+
const out = {};
|
|
3927
|
+
for (const [transactionId, tags] of tagsById) {
|
|
3928
|
+
const value = tags.find((tag) => tag.name === CONTENT_SHA256_TAG)?.value?.trim();
|
|
3929
|
+
if (value)
|
|
3930
|
+
out[transactionId] = value;
|
|
3797
3931
|
}
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3932
|
+
return out;
|
|
3933
|
+
};
|
|
3934
|
+
/** Store the (now verified) Content-SHA-256 of each downloaded transaction on its metadata row. */
|
|
3935
|
+
const recordVerifiedContentHashes = async (transactionIds, expectedContentHashes) => {
|
|
3802
3936
|
for (const transactionId of transactionIds) {
|
|
3803
|
-
|
|
3937
|
+
const contentHash = expectedContentHashes[transactionId];
|
|
3938
|
+
if (!contentHash)
|
|
3804
3939
|
continue;
|
|
3805
|
-
}
|
|
3806
3940
|
try {
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
try {
|
|
3811
|
-
tags = await queryClient.fetchQuery({
|
|
3812
|
-
queryKey: ['getTransactionTags', transactionId],
|
|
3813
|
-
queryFn: async () => arweave.BaseArweaveClient.getTransactionTags(transactionId),
|
|
3814
|
-
});
|
|
3815
|
-
}
|
|
3816
|
-
catch {
|
|
3817
|
-
tags = [];
|
|
3941
|
+
const metadataRecord = await getMetadata({ storageTransactionId: transactionId });
|
|
3942
|
+
if (metadataRecord) {
|
|
3943
|
+
await saveMetadata(metadataRecord, { contentHash });
|
|
3818
3944
|
}
|
|
3819
|
-
if (tags && tags.length > 0) {
|
|
3820
|
-
for (const { name, value } of tags) {
|
|
3821
|
-
if (name !== 'Content-SHA-256')
|
|
3822
|
-
continue;
|
|
3823
|
-
const metadataRecord = await getMetadata({
|
|
3824
|
-
storageTransactionId: transactionId,
|
|
3825
|
-
});
|
|
3826
|
-
if (metadataRecord) {
|
|
3827
|
-
await saveMetadata(metadataRecord, {
|
|
3828
|
-
contentHash: value,
|
|
3829
|
-
});
|
|
3830
|
-
}
|
|
3831
|
-
}
|
|
3832
|
-
}
|
|
3833
|
-
transactionIdsToDownload.push(transactionId);
|
|
3834
3945
|
}
|
|
3835
3946
|
catch (error) {
|
|
3836
|
-
logger$
|
|
3947
|
+
logger$A(error);
|
|
3837
3948
|
}
|
|
3838
3949
|
}
|
|
3950
|
+
};
|
|
3951
|
+
const downloadTransactionIds = async (rawTransactionIds, options) => {
|
|
3952
|
+
if (!supportsOpfsFileDownloads()) {
|
|
3953
|
+
return false;
|
|
3954
|
+
}
|
|
3955
|
+
const transactionIds = Array.from(new Set(rawTransactionIds.filter((tx) => !!tx && tx.trim().length > 0)));
|
|
3956
|
+
if (transactionIds.length === 0) {
|
|
3957
|
+
return false;
|
|
3958
|
+
}
|
|
3959
|
+
const excludedTransactions = await getExcludedTransactions();
|
|
3960
|
+
await arweave.ensureReadGatewaySelected();
|
|
3961
|
+
// Browser: do not call getTransactionStatus() — many gateways (e.g. g8way.io) redirect or omit
|
|
3962
|
+
// CORS headers, so fetch() fails and we would wrongly exclude txs. The OPFS worker fetch decides success.
|
|
3963
|
+
const transactionIdsToDownload = transactionIds.filter((id) => !excludedTransactions.has(id));
|
|
3839
3964
|
if (transactionIdsToDownload.length === 0) {
|
|
3840
3965
|
return false;
|
|
3841
3966
|
}
|
|
3967
|
+
// One batched lookup for every upload's Content-SHA-256 tag; downloads are verified against it.
|
|
3968
|
+
// Missing tags (old uploads, or a just-published one not indexed yet) just mean no verification.
|
|
3969
|
+
const expectedContentHashes = await getExpectedContentHashes(transactionIdsToDownload);
|
|
3842
3970
|
// Preferred gateways first: a just-published item is only on the gateway it was uploaded through.
|
|
3843
3971
|
const arweaveBaseUrls = arweave.getArweaveReadBaseUrls();
|
|
3972
|
+
// Where each ItemStorage child of a composite goes (the worker cannot read the DB or the schema).
|
|
3973
|
+
const compositeChildPaths = await getCompositeChildPaths(transactionIdsToDownload).catch((error) => {
|
|
3974
|
+
logger$A('[download] composite child paths lookup failed', error);
|
|
3975
|
+
return {};
|
|
3976
|
+
});
|
|
3977
|
+
let result;
|
|
3844
3978
|
if (transactionIdsToDownload.length === 1) {
|
|
3845
|
-
|
|
3846
|
-
|
|
3979
|
+
const transactionId = transactionIdsToDownload[0];
|
|
3980
|
+
result = await BaseFileManager.downloadFileByTransactionId({
|
|
3981
|
+
transactionId,
|
|
3847
3982
|
arweaveHost: arweave.BaseArweaveClient.getBaseUrl(),
|
|
3848
3983
|
arweaveBaseUrls,
|
|
3849
3984
|
excludedTransactions,
|
|
3985
|
+
expectedContentHash: expectedContentHashes[transactionId],
|
|
3986
|
+
compositeChildPaths: compositeChildPaths[transactionId],
|
|
3850
3987
|
});
|
|
3851
3988
|
}
|
|
3852
3989
|
else {
|
|
3853
|
-
await BaseFileManager.downloadAllFiles({
|
|
3990
|
+
result = await BaseFileManager.downloadAllFiles({
|
|
3854
3991
|
transactionIds: transactionIdsToDownload,
|
|
3855
3992
|
arweaveHost: arweave.BaseArweaveClient.getBaseUrl(),
|
|
3856
3993
|
arweaveBaseUrls,
|
|
3857
3994
|
excludedTransactions,
|
|
3995
|
+
expectedContentHashes,
|
|
3996
|
+
compositeChildPaths,
|
|
3858
3997
|
});
|
|
3859
3998
|
}
|
|
3999
|
+
if (result) {
|
|
4000
|
+
for (const transactionId of result.hashMismatchTransactionIds) {
|
|
4001
|
+
// Not saved by the worker; left for a later run (a gateway may serve the right bytes then).
|
|
4002
|
+
logger$A(`[download] ${transactionId}: downloaded bytes do not match Content-SHA-256; skipped`);
|
|
4003
|
+
}
|
|
4004
|
+
await recordVerifiedContentHashes(result.verifiedTransactionIds, expectedContentHashes);
|
|
4005
|
+
}
|
|
3860
4006
|
if (options?.resizeAllImages !== false) {
|
|
3861
4007
|
await BaseFileManager.resizeAllImages({
|
|
3862
4008
|
width: 480,
|
|
@@ -3941,6 +4087,7 @@ const downloadAllFilesBinaryRequestHandler = async (addressesHintOrPayload) => {
|
|
|
3941
4087
|
await BaseFileManager.createDirIfNotExists(BaseFileManager.getFilesPath('html'));
|
|
3942
4088
|
await BaseFileManager.createDirIfNotExists(BaseFileManager.getFilesPath('json'));
|
|
3943
4089
|
await BaseFileManager.createDirIfNotExists(BaseFileManager.getFilesPath('images'));
|
|
4090
|
+
await BaseFileManager.createDirIfNotExists(BaseFileManager.getFilesPath('files'));
|
|
3944
4091
|
const excludedTransactions = await getExcludedTransactions();
|
|
3945
4092
|
const fromEas = parseTransactionIdsFromMetadata(filesMetadata ?? [], excludedTransactions);
|
|
3946
4093
|
const fromLocal = await collectTransactionIdsFromLocalMetadata();
|
|
@@ -3970,7 +4117,7 @@ const downloadTransactionIdWithDedupe = async (transactionId) => {
|
|
|
3970
4117
|
};
|
|
3971
4118
|
const scheduleBulkFilesDownloadFromEasSyncThrottled = lodashEs.throttle((addressesHint) => {
|
|
3972
4119
|
void downloadAllFilesBinaryRequestHandler(addressesHint).catch((error) => {
|
|
3973
|
-
logger$
|
|
4120
|
+
logger$A('[scheduleBulkFilesDownloadFromEasSync] failed', error);
|
|
3974
4121
|
});
|
|
3975
4122
|
}, AUTO_BULK_DOWNLOAD_THROTTLE_MS, { leading: true, trailing: false });
|
|
3976
4123
|
const scheduleBulkFilesDownloadFromEasSync = (addressesHint) => {
|
|
@@ -4122,7 +4269,7 @@ const INTERNAL_STORAGE_MODEL_FILE_IDS = {
|
|
|
4122
4269
|
html: 'SEEDPROTOCOL_HTML',
|
|
4123
4270
|
};
|
|
4124
4271
|
|
|
4125
|
-
const logger$
|
|
4272
|
+
const logger$z = debug('seedSdk:helpers:ensureImageLocal');
|
|
4126
4273
|
/** Arweave transaction ids are 43 chars in the URL-safe base64 alphabet. */
|
|
4127
4274
|
const ARWEAVE_TX_ID_RE = /^[a-z0-9_-]{43}$/i;
|
|
4128
4275
|
const IMAGE_SIZES = [
|
|
@@ -4291,7 +4438,7 @@ const runEnsureImageLocal = async (params) => {
|
|
|
4291
4438
|
};
|
|
4292
4439
|
}
|
|
4293
4440
|
catch (error) {
|
|
4294
|
-
logger$
|
|
4441
|
+
logger$z('ensureImageLocal failed', error);
|
|
4295
4442
|
return { status: 'failed' };
|
|
4296
4443
|
}
|
|
4297
4444
|
};
|
|
@@ -4348,7 +4495,7 @@ var generateId$1 = /*#__PURE__*/Object.freeze({
|
|
|
4348
4495
|
generateId: generateId
|
|
4349
4496
|
});
|
|
4350
4497
|
|
|
4351
|
-
const logger$
|
|
4498
|
+
const logger$y = debug('seedSdk:shared:helpers');
|
|
4352
4499
|
const toSnakeCase = (str) => {
|
|
4353
4500
|
return str.replace(/([a-z])([A-Z])/g, '$1_$2').toLowerCase();
|
|
4354
4501
|
};
|
|
@@ -4405,7 +4552,7 @@ const convertTxIdToImage = async (txId) => {
|
|
|
4405
4552
|
const imageFilePath = BaseFileManager.getFilesPath('images', txId);
|
|
4406
4553
|
const fileExists = await BaseFileManager.pathExists(imageFilePath);
|
|
4407
4554
|
if (!fileExists) {
|
|
4408
|
-
logger$
|
|
4555
|
+
logger$y(`[ItemView] [updateImage] ${imageFilePath} does not exist`);
|
|
4409
4556
|
return;
|
|
4410
4557
|
}
|
|
4411
4558
|
const buffer = await BaseFileManager.readFileAsBuffer(imageFilePath);
|
|
@@ -4450,7 +4597,7 @@ function isActorStopped(actor) {
|
|
|
4450
4597
|
return actor.getSnapshot().status === 'stopped';
|
|
4451
4598
|
}
|
|
4452
4599
|
|
|
4453
|
-
const logger$
|
|
4600
|
+
const logger$x = debug('seedSdk:model:actors:loadOrCreateModel');
|
|
4454
4601
|
/**
|
|
4455
4602
|
* Create ModelProperty instances for all property IDs to ensure they're cached
|
|
4456
4603
|
* This ensures that ModelProperty.getById() in Model.properties getter will find the instances
|
|
@@ -4461,10 +4608,10 @@ const createPropertyInstances = async (propertyFileIds) => {
|
|
|
4461
4608
|
return;
|
|
4462
4609
|
}
|
|
4463
4610
|
try {
|
|
4464
|
-
const mod = await Promise.resolve().then(function () { return require('./ModelProperty-
|
|
4611
|
+
const mod = await Promise.resolve().then(function () { return require('./ModelProperty-DXZ39Wyv.js'); });
|
|
4465
4612
|
const ModelProperty = mod?.ModelProperty ?? mod?.default;
|
|
4466
4613
|
if (!ModelProperty) {
|
|
4467
|
-
logger$
|
|
4614
|
+
logger$x('createPropertyInstances: ModelProperty not available from dynamic import');
|
|
4468
4615
|
return;
|
|
4469
4616
|
}
|
|
4470
4617
|
// Create instances for all property IDs in parallel
|
|
@@ -4473,22 +4620,22 @@ const createPropertyInstances = async (propertyFileIds) => {
|
|
|
4473
4620
|
try {
|
|
4474
4621
|
const property = await ModelProperty.createById(propertyFileId);
|
|
4475
4622
|
if (property) {
|
|
4476
|
-
logger$
|
|
4623
|
+
logger$x(`Created/cached ModelProperty instance for propertyFileId "${propertyFileId}"`);
|
|
4477
4624
|
}
|
|
4478
4625
|
else {
|
|
4479
|
-
logger$
|
|
4626
|
+
logger$x(`ModelProperty.createById returned undefined for propertyFileId "${propertyFileId}" (may not exist in DB yet)`);
|
|
4480
4627
|
}
|
|
4481
4628
|
}
|
|
4482
4629
|
catch (error) {
|
|
4483
|
-
logger$
|
|
4630
|
+
logger$x(`Error creating ModelProperty instance for propertyFileId "${propertyFileId}": ${error}`);
|
|
4484
4631
|
// Don't throw - continue with other properties
|
|
4485
4632
|
}
|
|
4486
4633
|
});
|
|
4487
4634
|
await Promise.all(createPromises);
|
|
4488
|
-
logger$
|
|
4635
|
+
logger$x(`Finished creating/caching ${propertyFileIds.length} ModelProperty instances`);
|
|
4489
4636
|
}
|
|
4490
4637
|
catch (error) {
|
|
4491
|
-
logger$
|
|
4638
|
+
logger$x(`Error in createPropertyInstances: ${error}`);
|
|
4492
4639
|
// Don't throw - this is best-effort to pre-populate cache
|
|
4493
4640
|
}
|
|
4494
4641
|
};
|
|
@@ -4518,7 +4665,7 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4518
4665
|
if (dbModels.length > 0) {
|
|
4519
4666
|
modelRecord = dbModels[0];
|
|
4520
4667
|
foundBySchemaFileId = true;
|
|
4521
|
-
logger$
|
|
4668
|
+
logger$x(`Found model "${modelName}" in database by schemaFileId "${schemaFileId}"`);
|
|
4522
4669
|
}
|
|
4523
4670
|
}
|
|
4524
4671
|
// If not found by ID, try by name
|
|
@@ -4572,16 +4719,16 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4572
4719
|
// Access instanceCacheById via type assertion since it's protected
|
|
4573
4720
|
const cacheById = Model.instanceCacheById;
|
|
4574
4721
|
if (cacheById.has(dbSchemaFileId)) {
|
|
4575
|
-
logger$
|
|
4722
|
+
logger$x(`Model "${modelName}" found in database by name with schemaFileId "${dbSchemaFileId}", and a cached instance already exists. Updating current instance to use the same schemaFileId.`);
|
|
4576
4723
|
// Update the current instance's schemaFileId to match the database
|
|
4577
4724
|
// This ensures both instances point to the same cached instance
|
|
4578
4725
|
modelRecord = foundModel;
|
|
4579
4726
|
schemaFileId = dbSchemaFileId;
|
|
4580
|
-
logger$
|
|
4727
|
+
logger$x(`Using existing cached instance with schemaFileId "${schemaFileId}" for model "${modelName}"`);
|
|
4581
4728
|
}
|
|
4582
4729
|
}
|
|
4583
4730
|
catch (error) {
|
|
4584
|
-
logger$
|
|
4731
|
+
logger$x(`Error checking Model cache for schemaFileId "${dbSchemaFileId}": ${error}`);
|
|
4585
4732
|
// Fall through to normal processing
|
|
4586
4733
|
}
|
|
4587
4734
|
}
|
|
@@ -4589,31 +4736,31 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4589
4736
|
// this is a new model - don't use the existing model (will be renamed if duplicate)
|
|
4590
4737
|
// UNLESS we found a cached instance above (in which case modelRecord is already set)
|
|
4591
4738
|
if (!modelRecord && schemaFileId && dbSchemaFileId && schemaFileId !== dbSchemaFileId) {
|
|
4592
|
-
logger$
|
|
4739
|
+
logger$x(`Model "${modelName}" found by name but has different schemaFileId (provided: "${schemaFileId}", db: "${dbSchemaFileId}"). Creating new model (will check for duplicates).`);
|
|
4593
4740
|
// Don't set modelRecord - we'll create a new model with potentially renamed name
|
|
4594
4741
|
}
|
|
4595
4742
|
else if (!modelRecord && schemaFileId && !dbSchemaFileId) {
|
|
4596
4743
|
// We have a schemaFileId but the found model doesn't have one - this is a new model
|
|
4597
4744
|
// Don't reuse the existing model, create a new one (will be renamed if duplicate)
|
|
4598
|
-
logger$
|
|
4745
|
+
logger$x(`Model "${modelName}" found by name but found model has no schemaFileId. Creating new model with schemaFileId "${schemaFileId}" (will check for duplicates).`);
|
|
4599
4746
|
// Don't set modelRecord - we'll create a new model
|
|
4600
4747
|
}
|
|
4601
4748
|
else if (!modelRecord) {
|
|
4602
4749
|
// Only reuse existing model if:
|
|
4603
4750
|
// 1. No schemaFileId was provided (loading existing model), OR
|
|
4604
4751
|
// 2. schemaFileId matches (same model)
|
|
4605
|
-
logger$
|
|
4752
|
+
logger$x(`Model "${modelName}" found by name, reusing existing model (schemaFileId: provided="${schemaFileId}", db="${dbSchemaFileId}")`);
|
|
4606
4753
|
modelRecord = foundModel;
|
|
4607
4754
|
// Only use database schemaFileId if no id was explicitly provided
|
|
4608
4755
|
// If an id was provided, we should use it (it might be creating a new model with a specific ID)
|
|
4609
4756
|
if (!id && dbSchemaFileId) {
|
|
4610
4757
|
schemaFileId = dbSchemaFileId;
|
|
4611
|
-
logger$
|
|
4758
|
+
logger$x(`Using database schemaFileId "${schemaFileId}" for model "${modelName}" (no ID was provided)`);
|
|
4612
4759
|
}
|
|
4613
4760
|
else if (id) {
|
|
4614
|
-
logger$
|
|
4761
|
+
logger$x(`Preserving provided id (schemaFileId) "${schemaFileId}" for model "${modelName}" (ignoring database schemaFileId "${dbSchemaFileId}")`);
|
|
4615
4762
|
}
|
|
4616
|
-
logger$
|
|
4763
|
+
logger$x(`Found model "${modelName}" in database by name, schemaFileId: "${schemaFileId}"`);
|
|
4617
4764
|
}
|
|
4618
4765
|
}
|
|
4619
4766
|
}
|
|
@@ -4638,7 +4785,7 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4638
4785
|
// Generate schemaFileId if not set
|
|
4639
4786
|
if (!schemaFileId) {
|
|
4640
4787
|
schemaFileId = generateId();
|
|
4641
|
-
logger$
|
|
4788
|
+
logger$x(`Generated id (schemaFileId) "${schemaFileId}" for model "${modelName}"`);
|
|
4642
4789
|
}
|
|
4643
4790
|
// Include _dbId from database record so properties can be created if needed
|
|
4644
4791
|
// Even if we found an existing model, we may still need to create properties
|
|
@@ -4667,14 +4814,14 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4667
4814
|
}
|
|
4668
4815
|
}
|
|
4669
4816
|
catch (error) {
|
|
4670
|
-
logger$
|
|
4817
|
+
logger$x(`Error loading model from database: ${error}`);
|
|
4671
4818
|
// Fall through to create new model
|
|
4672
4819
|
}
|
|
4673
4820
|
}
|
|
4674
4821
|
// Step 2: Fallback to Schema context (only if database doesn't have the model)
|
|
4675
4822
|
// This handles the case where model exists in schema file but not yet in database
|
|
4676
4823
|
try {
|
|
4677
|
-
const schemaMod = await Promise.resolve().then(function () { return require('./Schema-
|
|
4824
|
+
const schemaMod = await Promise.resolve().then(function () { return require('./Schema-DdP109QD.js'); });
|
|
4678
4825
|
const { Schema } = schemaMod;
|
|
4679
4826
|
// Stopped meanwhile (unloaded, or evicted with its schema): don't load the schema again
|
|
4680
4827
|
if (isActorStopped(self))
|
|
@@ -4705,10 +4852,10 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4705
4852
|
const byName = schemaContext.models[modelName];
|
|
4706
4853
|
const schemaModelId = byName?.id ?? null;
|
|
4707
4854
|
if (schemaFileId === schemaModelId) {
|
|
4708
|
-
logger$
|
|
4855
|
+
logger$x(`Found model "${modelName}" in Schema context (database fallback)`);
|
|
4709
4856
|
if (!schemaFileId) {
|
|
4710
4857
|
schemaFileId = generateId();
|
|
4711
|
-
logger$
|
|
4858
|
+
logger$x(`Generated id (schemaFileId) "${schemaFileId}" for model "${modelName}"`);
|
|
4712
4859
|
}
|
|
4713
4860
|
sendBack({
|
|
4714
4861
|
type: 'loadOrCreateModelSuccess',
|
|
@@ -4724,7 +4871,7 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4724
4871
|
// Schema context may not have model by name yet (e.g. still loading). If our id appears in any schema model, we're the schema model.
|
|
4725
4872
|
for (const def of Object.values(schemaContext.models)) {
|
|
4726
4873
|
if (def?.id === schemaFileId) {
|
|
4727
|
-
logger$
|
|
4874
|
+
logger$x(`Found model in Schema context by id "${schemaFileId}" (database fallback)`);
|
|
4728
4875
|
sendBack({
|
|
4729
4876
|
type: 'loadOrCreateModelSuccess',
|
|
4730
4877
|
model: { id: schemaFileId },
|
|
@@ -4736,7 +4883,7 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4736
4883
|
}
|
|
4737
4884
|
}
|
|
4738
4885
|
catch (error) {
|
|
4739
|
-
logger$
|
|
4886
|
+
logger$x(`Error loading model from Schema context: ${error}`);
|
|
4740
4887
|
// Fall through to create new model
|
|
4741
4888
|
}
|
|
4742
4889
|
// Step 3: Check for duplicate names in database before creating new model
|
|
@@ -4756,7 +4903,7 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4756
4903
|
// 3. This is an internal schema (internal schemas should preserve original names from schema files), OR
|
|
4757
4904
|
// 4. This instance was created with modelFileId from schema (_idFromSchema) - schema models keep their name
|
|
4758
4905
|
const shouldSkipDuplicateCheck = modelRecord !== null || foundBySchemaFileId || isInternal || !!_idFromSchema;
|
|
4759
|
-
logger$
|
|
4906
|
+
logger$x(`Duplicate check: modelRecord=${modelRecord !== null}, foundBySchemaFileId=${foundBySchemaFileId}, isInternal=${isInternal}, _idFromSchema=${!!_idFromSchema}, schemaFileId=${schemaFileId}, shouldSkip=${shouldSkipDuplicateCheck}`);
|
|
4760
4907
|
if (!shouldSkipDuplicateCheck) {
|
|
4761
4908
|
try {
|
|
4762
4909
|
const lowerModelName = modelName.toLowerCase();
|
|
@@ -4768,7 +4915,7 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4768
4915
|
// Check name-based cache for this schema
|
|
4769
4916
|
// Skip the current model (context.id): Model.create() adds the new instance to the cache
|
|
4770
4917
|
// before this callback runs, so we must not count it as a duplicate.
|
|
4771
|
-
logger$
|
|
4918
|
+
logger$x(`Checking Model cache for duplicates in schema "${schemaName}"`);
|
|
4772
4919
|
const currentModelFileId = schemaFileId ?? null;
|
|
4773
4920
|
const cacheByName = Model.instanceCacheByName;
|
|
4774
4921
|
for (const [nameKey, cachedModelFileId] of cacheByName.entries()) {
|
|
@@ -4779,7 +4926,7 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4779
4926
|
const lowerCachedName = cachedModelName.toLowerCase();
|
|
4780
4927
|
if (lowerCachedName === lowerModelName) {
|
|
4781
4928
|
existingNumbers.add(0); // Base name exists
|
|
4782
|
-
logger$
|
|
4929
|
+
logger$x(`Found duplicate in Model cache: "${cachedModelName}" matches "${modelName}"`);
|
|
4783
4930
|
}
|
|
4784
4931
|
else if (lowerCachedName.startsWith(lowerModelName + ' ')) {
|
|
4785
4932
|
// Check if it's the base name followed by a space and a number
|
|
@@ -4787,7 +4934,7 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4787
4934
|
const number = parseInt(suffix, 10);
|
|
4788
4935
|
if (!isNaN(number) && suffix === number.toString()) {
|
|
4789
4936
|
existingNumbers.add(number);
|
|
4790
|
-
logger$
|
|
4937
|
+
logger$x(`Found numbered variant in Model cache: "${cachedModelName}" (number: ${number})`);
|
|
4791
4938
|
}
|
|
4792
4939
|
}
|
|
4793
4940
|
}
|
|
@@ -4803,7 +4950,7 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4803
4950
|
const lowerCachedName = cachedModelName.toLowerCase();
|
|
4804
4951
|
if (lowerCachedName === lowerModelName) {
|
|
4805
4952
|
existingNumbers.add(0); // Base name exists
|
|
4806
|
-
logger$
|
|
4953
|
+
logger$x(`Found duplicate in Model legacy cache: "${cachedModelName}" matches "${modelName}"`);
|
|
4807
4954
|
}
|
|
4808
4955
|
else if (lowerCachedName.startsWith(lowerModelName + ' ')) {
|
|
4809
4956
|
// Check if it's the base name followed by a space and a number
|
|
@@ -4811,14 +4958,14 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4811
4958
|
const number = parseInt(suffix, 10);
|
|
4812
4959
|
if (!isNaN(number) && suffix === number.toString()) {
|
|
4813
4960
|
existingNumbers.add(number);
|
|
4814
|
-
logger$
|
|
4961
|
+
logger$x(`Found numbered variant in Model legacy cache: "${cachedModelName}" (number: ${number})`);
|
|
4815
4962
|
}
|
|
4816
4963
|
}
|
|
4817
4964
|
}
|
|
4818
4965
|
}
|
|
4819
4966
|
}
|
|
4820
4967
|
catch (error) {
|
|
4821
|
-
logger$
|
|
4968
|
+
logger$x(`Error checking Model cache for duplicates: ${error}`);
|
|
4822
4969
|
// Continue with database check
|
|
4823
4970
|
}
|
|
4824
4971
|
// Also check database for models (in case they're persisted but not in Schema context)
|
|
@@ -4828,7 +4975,7 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4828
4975
|
const schemaSchemaMod = await Promise.resolve().then(function () { return SchemaSchema; });
|
|
4829
4976
|
const { schemas: schemasTable } = schemaSchemaMod;
|
|
4830
4977
|
// Query all models for this schema to check for duplicates (case-insensitive)
|
|
4831
|
-
logger$
|
|
4978
|
+
logger$x(`Checking database for duplicate model names in schema "${schemaName}"`);
|
|
4832
4979
|
const allModelsForSchema = await db
|
|
4833
4980
|
.select({
|
|
4834
4981
|
name: models$1.name,
|
|
@@ -4837,16 +4984,16 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4837
4984
|
.innerJoin(modelSchemas, drizzleOrm.eq(models$1.id, modelSchemas.modelId))
|
|
4838
4985
|
.innerJoin(schemasTable, drizzleOrm.eq(modelSchemas.schemaId, schemasTable.id))
|
|
4839
4986
|
.where(drizzleOrm.eq(schemasTable.name, schemaName));
|
|
4840
|
-
logger$
|
|
4987
|
+
logger$x(`Found ${allModelsForSchema.length} models in database for schema "${schemaName}": ${allModelsForSchema.map((m) => m.name).join(', ')}`);
|
|
4841
4988
|
if (allModelsForSchema.length === 0 && modelName.includes('Shared')) {
|
|
4842
|
-
logger$
|
|
4989
|
+
logger$x(`[DEBUG] No models found in database for schema "${schemaName}" when checking for duplicate "${modelName}"`);
|
|
4843
4990
|
}
|
|
4844
4991
|
// Check for exact match (case-insensitive) or matches with number suffix
|
|
4845
4992
|
for (const dbModel of allModelsForSchema) {
|
|
4846
4993
|
const lowerDbName = dbModel.name.toLowerCase();
|
|
4847
4994
|
if (lowerDbName === lowerModelName) {
|
|
4848
4995
|
existingNumbers.add(0); // Base name exists
|
|
4849
|
-
logger$
|
|
4996
|
+
logger$x(`Found duplicate in database: "${dbModel.name}" matches "${modelName}"`);
|
|
4850
4997
|
}
|
|
4851
4998
|
else if (lowerDbName.startsWith(lowerModelName + ' ')) {
|
|
4852
4999
|
// Check if it's the base name followed by a space and a number
|
|
@@ -4854,48 +5001,48 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4854
5001
|
const number = parseInt(suffix, 10);
|
|
4855
5002
|
if (!isNaN(number) && suffix === number.toString()) {
|
|
4856
5003
|
existingNumbers.add(number);
|
|
4857
|
-
logger$
|
|
5004
|
+
logger$x(`Found numbered variant in database: "${dbModel.name}" (number: ${number})`);
|
|
4858
5005
|
}
|
|
4859
5006
|
}
|
|
4860
5007
|
}
|
|
4861
5008
|
}
|
|
4862
5009
|
// If duplicates found, generate unique name
|
|
4863
|
-
logger$
|
|
5010
|
+
logger$x(`Duplicate check results: existingNumbers=${Array.from(existingNumbers).join(', ')}, hasBaseName=${existingNumbers.has(0)}`);
|
|
4864
5011
|
if (existingNumbers.has(0)) {
|
|
4865
5012
|
let nextNumber = 1;
|
|
4866
5013
|
while (existingNumbers.has(nextNumber)) {
|
|
4867
5014
|
nextNumber++;
|
|
4868
5015
|
}
|
|
4869
5016
|
finalModelName = `${modelName} ${nextNumber}`;
|
|
4870
|
-
logger$
|
|
5017
|
+
logger$x(`Found duplicate model name "${modelName}" in schema "${schemaName}", using unique name "${finalModelName}"`);
|
|
4871
5018
|
}
|
|
4872
5019
|
else {
|
|
4873
|
-
logger$
|
|
5020
|
+
logger$x(`No duplicate found for model name "${modelName}" in schema "${schemaName}"`);
|
|
4874
5021
|
}
|
|
4875
5022
|
}
|
|
4876
5023
|
catch (error) {
|
|
4877
|
-
logger$
|
|
5024
|
+
logger$x(`Error checking for duplicate model names: ${error}`);
|
|
4878
5025
|
// Continue with original name if check fails
|
|
4879
5026
|
}
|
|
4880
5027
|
}
|
|
4881
5028
|
else {
|
|
4882
5029
|
if (foundBySchemaFileId) {
|
|
4883
|
-
logger$
|
|
5030
|
+
logger$x(`Preserving original model name "${modelName}" for model found by schemaFileId (schemaFileId: ${schemaFileId})`);
|
|
4884
5031
|
}
|
|
4885
5032
|
else if (modelRecord) {
|
|
4886
|
-
logger$
|
|
5033
|
+
logger$x(`Using existing model name "${modelName}" from database (modelRecord found)`);
|
|
4887
5034
|
}
|
|
4888
5035
|
else if (isInternal) {
|
|
4889
|
-
logger$
|
|
5036
|
+
logger$x(`Preserving original model name "${modelName}" for internal schema "${schemaName}" (skipping duplicate check)`);
|
|
4890
5037
|
}
|
|
4891
5038
|
}
|
|
4892
5039
|
}
|
|
4893
5040
|
// Step 4: Create new empty model (not found in database or Schema context)
|
|
4894
|
-
logger$
|
|
5041
|
+
logger$x(`Creating new model "${finalModelName}" in schema "${schemaName}"`);
|
|
4895
5042
|
// Generate schemaFileId for new model
|
|
4896
5043
|
if (!schemaFileId) {
|
|
4897
5044
|
schemaFileId = generateId();
|
|
4898
|
-
logger$
|
|
5045
|
+
logger$x(`Generated id (schemaFileId) "${schemaFileId}" for new model "${finalModelName}"`);
|
|
4899
5046
|
}
|
|
4900
5047
|
// Runtime creates use a placeholder cache key in Model.create; move it to the final name. Skip when schema model (_idFromSchema).
|
|
4901
5048
|
if (!_idFromSchema) {
|
|
@@ -4907,7 +5054,7 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4907
5054
|
// This avoids the issue where updateContext with modelName triggers validation and state transition
|
|
4908
5055
|
// We use _pendingModelName (internal field) which won't trigger validation
|
|
4909
5056
|
if (finalModelName !== modelName) {
|
|
4910
|
-
logger$
|
|
5057
|
+
logger$x(`Model name changed from "${modelName}" to "${finalModelName}", storing in _pendingModelName for loadOrCreateModelSuccess`);
|
|
4911
5058
|
// Store the final name in a temporary internal field that will be picked up by loadOrCreateModelSuccess
|
|
4912
5059
|
sendBack({
|
|
4913
5060
|
type: 'updateContext',
|
|
@@ -4915,11 +5062,11 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4915
5062
|
});
|
|
4916
5063
|
}
|
|
4917
5064
|
else {
|
|
4918
|
-
logger$
|
|
5065
|
+
logger$x(`Model name unchanged: "${modelName}"`);
|
|
4919
5066
|
}
|
|
4920
5067
|
// Mark schema as draft when a new model is created so saveNewVersion() can persist it
|
|
4921
5068
|
try {
|
|
4922
|
-
const schemaMod = await Promise.resolve().then(function () { return require('./Schema-
|
|
5069
|
+
const schemaMod = await Promise.resolve().then(function () { return require('./Schema-DdP109QD.js'); });
|
|
4923
5070
|
const { Schema } = schemaMod;
|
|
4924
5071
|
if (!isActorStopped(self)) {
|
|
4925
5072
|
const schema = Schema.create(schemaName, { waitForReady: false });
|
|
@@ -4927,7 +5074,7 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4927
5074
|
}
|
|
4928
5075
|
}
|
|
4929
5076
|
catch (err) {
|
|
4930
|
-
logger$
|
|
5077
|
+
logger$x(`Failed to mark schema as draft after creating model: ${err}`);
|
|
4931
5078
|
}
|
|
4932
5079
|
sendBack({
|
|
4933
5080
|
type: 'loadOrCreateModelSuccess',
|
|
@@ -4937,12 +5084,12 @@ const loadOrCreateModel = xstate.fromCallback(({ sendBack, input: { context }, s
|
|
|
4937
5084
|
});
|
|
4938
5085
|
};
|
|
4939
5086
|
_loadOrCreateModel().catch((error) => {
|
|
4940
|
-
logger$
|
|
5087
|
+
logger$x('Error loading or creating model:', error);
|
|
4941
5088
|
sendBack({ type: 'loadOrCreateModelError', error });
|
|
4942
5089
|
});
|
|
4943
5090
|
});
|
|
4944
5091
|
|
|
4945
|
-
const logger$
|
|
5092
|
+
const logger$w = debug('seedSdk:model:actors:validateModel');
|
|
4946
5093
|
// Validation timeout in milliseconds (10 seconds)
|
|
4947
5094
|
const VALIDATION_TIMEOUT_MS = 10000;
|
|
4948
5095
|
const validateModel = xstate.fromCallback(({ sendBack, input: { context }, self }) => {
|
|
@@ -4965,10 +5112,10 @@ const validateModel = xstate.fromCallback(({ sendBack, input: { context }, self
|
|
|
4965
5112
|
const _validateModel = async () => {
|
|
4966
5113
|
try {
|
|
4967
5114
|
// Use dynamic imports to break circular dependencies
|
|
4968
|
-
const validationServiceMod = await Promise.resolve().then(function () { return require('./SchemaValidationService-
|
|
5115
|
+
const validationServiceMod = await Promise.resolve().then(function () { return require('./SchemaValidationService-CSXxx7sQ.js'); });
|
|
4969
5116
|
const { SchemaValidationService } = validationServiceMod;
|
|
4970
5117
|
const validationService = new SchemaValidationService();
|
|
4971
|
-
const schemaMod = await Promise.resolve().then(function () { return require('./Schema-
|
|
5118
|
+
const schemaMod = await Promise.resolve().then(function () { return require('./Schema-DdP109QD.js'); });
|
|
4972
5119
|
const { Schema } = schemaMod;
|
|
4973
5120
|
// Validate model structure
|
|
4974
5121
|
const structureResult = validationService.validateModelStructure(context);
|
|
@@ -5012,12 +5159,12 @@ const validateModel = xstate.fromCallback(({ sendBack, input: { context }, self
|
|
|
5012
5159
|
}
|
|
5013
5160
|
}
|
|
5014
5161
|
else {
|
|
5015
|
-
logger$
|
|
5162
|
+
logger$w(`Schema is in ${schemaStatus} state, skipping schema validation`);
|
|
5016
5163
|
// Continue with structure validation only
|
|
5017
5164
|
}
|
|
5018
5165
|
}
|
|
5019
5166
|
catch (error) {
|
|
5020
|
-
logger$
|
|
5167
|
+
logger$w('Error validating model against schema:', error);
|
|
5021
5168
|
// Continue with structure validation only
|
|
5022
5169
|
}
|
|
5023
5170
|
}
|
|
@@ -5032,7 +5179,7 @@ const validateModel = xstate.fromCallback(({ sendBack, input: { context }, self
|
|
|
5032
5179
|
if (!hasResponded) {
|
|
5033
5180
|
hasResponded = true;
|
|
5034
5181
|
clearTimeout(timeoutId);
|
|
5035
|
-
logger$
|
|
5182
|
+
logger$w('Error in validateModel:', error);
|
|
5036
5183
|
sendBack({
|
|
5037
5184
|
type: 'validationError',
|
|
5038
5185
|
errors: [{
|
|
@@ -5049,7 +5196,7 @@ const validateModel = xstate.fromCallback(({ sendBack, input: { context }, self
|
|
|
5049
5196
|
if (!hasResponded) {
|
|
5050
5197
|
hasResponded = true;
|
|
5051
5198
|
clearTimeout(timeoutId);
|
|
5052
|
-
logger$
|
|
5199
|
+
logger$w('Error in validateModel:', error);
|
|
5053
5200
|
sendBack({
|
|
5054
5201
|
type: 'validationError',
|
|
5055
5202
|
errors: [{
|
|
@@ -5067,7 +5214,7 @@ const validateModel = xstate.fromCallback(({ sendBack, input: { context }, self
|
|
|
5067
5214
|
};
|
|
5068
5215
|
});
|
|
5069
5216
|
|
|
5070
|
-
const logger$
|
|
5217
|
+
const logger$v = debug('seedSdk:model:actors:createModelProperties');
|
|
5071
5218
|
const createModelProperties = xstate.fromCallback(({ sendBack, input, self }) => {
|
|
5072
5219
|
// The model was stopped (unloaded, or evicted with its schema by Schema.destroy / test cleanup) while
|
|
5073
5220
|
// this ran: stopping it stops this actor too, but can't cancel the awaits below. A stopped model
|
|
@@ -5080,15 +5227,15 @@ const createModelProperties = xstate.fromCallback(({ sendBack, input, self }) =>
|
|
|
5080
5227
|
throw new Error('Model ID, file ID, and name are required to create properties');
|
|
5081
5228
|
}
|
|
5082
5229
|
if (!propertyDefinitions || Object.keys(propertyDefinitions).length === 0) {
|
|
5083
|
-
logger$
|
|
5230
|
+
logger$v('No property definitions provided, skipping property creation');
|
|
5084
5231
|
sendBack({ type: 'createModelPropertiesSuccess' });
|
|
5085
5232
|
return;
|
|
5086
5233
|
}
|
|
5087
|
-
logger$
|
|
5088
|
-
const mod = await Promise.resolve().then(function () { return require('./ModelProperty-
|
|
5234
|
+
logger$v(`Creating ${Object.keys(propertyDefinitions).length} properties for model "${modelName}" (id: ${_dbId})`);
|
|
5235
|
+
const mod = await Promise.resolve().then(function () { return require('./ModelProperty-DXZ39Wyv.js'); });
|
|
5089
5236
|
const ModelProperty = mod?.ModelProperty ?? mod?.default;
|
|
5090
5237
|
if (!ModelProperty) {
|
|
5091
|
-
logger$
|
|
5238
|
+
logger$v('ModelProperty not available from dynamic import');
|
|
5092
5239
|
sendBack({ type: 'createModelPropertiesError', error: new Error('ModelProperty not available') });
|
|
5093
5240
|
return;
|
|
5094
5241
|
}
|
|
@@ -5112,7 +5259,7 @@ const createModelProperties = xstate.fromCallback(({ sendBack, input, self }) =>
|
|
|
5112
5259
|
// IDs should be generated in the import process before creating properties
|
|
5113
5260
|
const { generateId } = await Promise.resolve().then(function () { return generateId$1; });
|
|
5114
5261
|
const propertyFileId = propData.id || propertyRecord?.schemaFileId || generateId();
|
|
5115
|
-
logger$
|
|
5262
|
+
logger$v(`Creating property "${propName}" with fileId "${propertyFileId}"`);
|
|
5116
5263
|
// Query database to get refModelId if the property has ref/refModelName
|
|
5117
5264
|
// The property should already be in DB from addModelsToDb
|
|
5118
5265
|
let refModelId = propData.refModelId;
|
|
@@ -5122,7 +5269,7 @@ const createModelProperties = xstate.fromCallback(({ sendBack, input, self }) =>
|
|
|
5122
5269
|
// First try to get refModelId from the property record in the database
|
|
5123
5270
|
if (propertyRecord?.refModelId) {
|
|
5124
5271
|
refModelId = propertyRecord.refModelId;
|
|
5125
|
-
logger$
|
|
5272
|
+
logger$v(`Found refModelId ${refModelId} from database for property "${propName}"`);
|
|
5126
5273
|
}
|
|
5127
5274
|
else if (refModelName) {
|
|
5128
5275
|
// Fallback: resolve the ref by name within this model's schema
|
|
@@ -5130,17 +5277,17 @@ const createModelProperties = xstate.fromCallback(({ sendBack, input, self }) =>
|
|
|
5130
5277
|
const refModelRecord = await resolveModelRecord(refModelName, { schemaName }, db);
|
|
5131
5278
|
if (refModelRecord?.id) {
|
|
5132
5279
|
refModelId = refModelRecord.id;
|
|
5133
|
-
logger$
|
|
5280
|
+
logger$v(`Resolved refModelId ${refModelId} from model name "${refModelName}" for property "${propName}"`);
|
|
5134
5281
|
}
|
|
5135
5282
|
}
|
|
5136
5283
|
}
|
|
5137
5284
|
catch (error) {
|
|
5138
|
-
logger$
|
|
5285
|
+
logger$v(`Error fetching refModelId for property "${propName}":`, error);
|
|
5139
5286
|
// Continue without refModelId - it will be resolved later in _initializeOriginalValues
|
|
5140
5287
|
}
|
|
5141
5288
|
}
|
|
5142
5289
|
if (stopped()) {
|
|
5143
|
-
logger$
|
|
5290
|
+
logger$v(`Model "${modelName}" was stopped while creating its properties; not creating the rest`);
|
|
5144
5291
|
return;
|
|
5145
5292
|
}
|
|
5146
5293
|
// Create ModelProperty instance
|
|
@@ -5166,11 +5313,11 @@ const createModelProperties = xstate.fromCallback(({ sendBack, input, self }) =>
|
|
|
5166
5313
|
_dbId: propertyRecord?.schemaFileId === propertyFileId ? propertyRecord.id : undefined, // Already persisted: skip the write
|
|
5167
5314
|
}, { schemaName }); // Use 'as any' because _propertyFileId is not in TProperty type
|
|
5168
5315
|
}
|
|
5169
|
-
logger$
|
|
5316
|
+
logger$v(`Successfully created all properties for model "${modelName}"`);
|
|
5170
5317
|
sendBack({ type: 'createModelPropertiesSuccess' });
|
|
5171
5318
|
};
|
|
5172
5319
|
_createProperties().catch((error) => {
|
|
5173
|
-
logger$
|
|
5320
|
+
logger$v(`Error creating model properties: ${error}`);
|
|
5174
5321
|
sendBack({
|
|
5175
5322
|
type: 'createModelPropertiesError',
|
|
5176
5323
|
error: error instanceof Error ? error : new Error(String(error))
|
|
@@ -5212,7 +5359,7 @@ function anyEvictionSince(since) {
|
|
|
5212
5359
|
return epoch !== since;
|
|
5213
5360
|
}
|
|
5214
5361
|
|
|
5215
|
-
const logger$
|
|
5362
|
+
const logger$u = debug('seedSdk:write:validateEntity');
|
|
5216
5363
|
// XState v5 type inference bug: fromPromise types are inverted
|
|
5217
5364
|
// We use type assertion to work around this
|
|
5218
5365
|
const validateEntity = xstate.fromPromise(async ({ input }) => {
|
|
@@ -5225,7 +5372,7 @@ const validateEntity = xstate.fromPromise(async ({ input }) => {
|
|
|
5225
5372
|
const evictionEpoch = currentEvictionEpoch();
|
|
5226
5373
|
/** The schema to validate against; throws (validation then skips the schema check) once it was evicted. */
|
|
5227
5374
|
const schemaForValidation = async (schemaName) => {
|
|
5228
|
-
const { Schema } = await Promise.resolve().then(function () { return require('./Schema-
|
|
5375
|
+
const { Schema } = await Promise.resolve().then(function () { return require('./Schema-DdP109QD.js'); });
|
|
5229
5376
|
if (schemaEvictedSince(schemaName, evictionEpoch)) {
|
|
5230
5377
|
throw new Error(`Schema "${schemaName}" was evicted during validation`);
|
|
5231
5378
|
}
|
|
@@ -5234,20 +5381,20 @@ const validateEntity = xstate.fromPromise(async ({ input }) => {
|
|
|
5234
5381
|
const _validate = async () => {
|
|
5235
5382
|
try {
|
|
5236
5383
|
const msg = `[validateEntity] Starting validation for ${entityInput.entityType}`;
|
|
5237
|
-
logger$
|
|
5238
|
-
logger$
|
|
5384
|
+
logger$u(msg);
|
|
5385
|
+
logger$u(`[validateEntity] Entity data:`, entityInput.entityData);
|
|
5239
5386
|
let result = { isValid: true, errors: [] };
|
|
5240
5387
|
if (entityInput.entityType === 'model') {
|
|
5241
5388
|
const structureMsg = `[validateEntity] Validating model structure`;
|
|
5242
|
-
logger$
|
|
5389
|
+
logger$u(structureMsg);
|
|
5243
5390
|
// Use existing Model validation
|
|
5244
|
-
const validationServiceMod = await Promise.resolve().then(function () { return require('./SchemaValidationService-
|
|
5391
|
+
const validationServiceMod = await Promise.resolve().then(function () { return require('./SchemaValidationService-CSXxx7sQ.js'); });
|
|
5245
5392
|
const { SchemaValidationService } = validationServiceMod;
|
|
5246
5393
|
const validationService = new SchemaValidationService();
|
|
5247
5394
|
// Validate model structure
|
|
5248
5395
|
const structureResult = validationService.validateModelStructure(entityInput.entityData);
|
|
5249
5396
|
const structureResultMsg = `[validateEntity] Structure validation result: isValid=${structureResult.isValid}, errors=${structureResult.errors?.length || 0}`;
|
|
5250
|
-
logger$
|
|
5397
|
+
logger$u(structureResultMsg);
|
|
5251
5398
|
if (!structureResult.isValid) {
|
|
5252
5399
|
result = {
|
|
5253
5400
|
isValid: false,
|
|
@@ -5258,16 +5405,16 @@ const validateEntity = xstate.fromPromise(async ({ input }) => {
|
|
|
5258
5405
|
// If schema name provided, validate against schema
|
|
5259
5406
|
if (entityInput.entityData.schemaName) {
|
|
5260
5407
|
try {
|
|
5261
|
-
logger$
|
|
5408
|
+
logger$u(`[validateEntity] Validating model against schema "${entityInput.entityData.schemaName}"`);
|
|
5262
5409
|
const schema = await schemaForValidation(entityInput.entityData.schemaName);
|
|
5263
5410
|
const schemaSnapshot = schema.getService().getSnapshot();
|
|
5264
5411
|
const schemaStatus = schemaSnapshot.value;
|
|
5265
|
-
logger$
|
|
5412
|
+
logger$u(`[validateEntity] Schema status: ${schemaStatus}`);
|
|
5266
5413
|
if (schemaStatus === 'idle') {
|
|
5267
5414
|
const schemaContext = schemaSnapshot.context;
|
|
5268
|
-
logger$
|
|
5415
|
+
logger$u(`[validateEntity] Running validateModelAgainstSchema`);
|
|
5269
5416
|
const schemaResult = validationService.validateModelAgainstSchema(schemaContext, entityInput.entityData.modelName, entityInput.entityData);
|
|
5270
|
-
logger$
|
|
5417
|
+
logger$u(`[validateEntity] Schema validation result:`, schemaResult);
|
|
5271
5418
|
if (!schemaResult.isValid) {
|
|
5272
5419
|
result = {
|
|
5273
5420
|
isValid: false,
|
|
@@ -5276,22 +5423,22 @@ const validateEntity = xstate.fromPromise(async ({ input }) => {
|
|
|
5276
5423
|
}
|
|
5277
5424
|
}
|
|
5278
5425
|
else {
|
|
5279
|
-
logger$
|
|
5426
|
+
logger$u(`[validateEntity] Schema not in idle state (${schemaStatus}), skipping schema validation`);
|
|
5280
5427
|
}
|
|
5281
5428
|
}
|
|
5282
5429
|
catch (error) {
|
|
5283
|
-
logger$
|
|
5430
|
+
logger$u('Error validating model against schema:', error);
|
|
5284
5431
|
// Continue with structure validation only
|
|
5285
5432
|
}
|
|
5286
5433
|
}
|
|
5287
5434
|
else {
|
|
5288
|
-
logger$
|
|
5435
|
+
logger$u(`[validateEntity] No schemaName provided, skipping schema validation`);
|
|
5289
5436
|
}
|
|
5290
5437
|
}
|
|
5291
5438
|
}
|
|
5292
5439
|
else if (entityInput.entityType === 'modelProperty') {
|
|
5293
5440
|
// Use existing ModelProperty validation
|
|
5294
|
-
const validationServiceMod = await Promise.resolve().then(function () { return require('./SchemaValidationService-
|
|
5441
|
+
const validationServiceMod = await Promise.resolve().then(function () { return require('./SchemaValidationService-CSXxx7sQ.js'); });
|
|
5295
5442
|
const { SchemaValidationService } = validationServiceMod;
|
|
5296
5443
|
const validationService = new SchemaValidationService();
|
|
5297
5444
|
// Validate property structure
|
|
@@ -5323,7 +5470,7 @@ const validateEntity = xstate.fromPromise(async ({ input }) => {
|
|
|
5323
5470
|
}
|
|
5324
5471
|
}
|
|
5325
5472
|
catch (error) {
|
|
5326
|
-
logger$
|
|
5473
|
+
logger$u('Error validating property against schema:', error);
|
|
5327
5474
|
// Continue with structure validation only
|
|
5328
5475
|
}
|
|
5329
5476
|
}
|
|
@@ -5331,7 +5478,7 @@ const validateEntity = xstate.fromPromise(async ({ input }) => {
|
|
|
5331
5478
|
}
|
|
5332
5479
|
else if (entityInput.entityType === 'schema') {
|
|
5333
5480
|
// Schema validation - use existing validation
|
|
5334
|
-
const validationServiceMod = await Promise.resolve().then(function () { return require('./SchemaValidationService-
|
|
5481
|
+
const validationServiceMod = await Promise.resolve().then(function () { return require('./SchemaValidationService-CSXxx7sQ.js'); });
|
|
5335
5482
|
const { SchemaValidationService } = validationServiceMod;
|
|
5336
5483
|
const validationService = new SchemaValidationService();
|
|
5337
5484
|
const schemaResult = validationService.validateSchema(entityInput.entityData);
|
|
@@ -5344,14 +5491,14 @@ const validateEntity = xstate.fromPromise(async ({ input }) => {
|
|
|
5344
5491
|
}
|
|
5345
5492
|
// Return the result - fromPromise will automatically put this in event.output
|
|
5346
5493
|
const completeMsg = `[validateEntity] Validation complete: isValid=${result.isValid}, errors=${result.errors?.length || 0}`;
|
|
5347
|
-
logger$
|
|
5348
|
-
logger$
|
|
5494
|
+
logger$u(completeMsg);
|
|
5495
|
+
logger$u(`[validateEntity] Validation complete:`, result);
|
|
5349
5496
|
return result;
|
|
5350
5497
|
}
|
|
5351
5498
|
catch (error) {
|
|
5352
5499
|
const errorMsg = `[validateEntity] Error in validateEntity: ${error instanceof Error ? error.message : String(error)}`;
|
|
5353
|
-
logger$
|
|
5354
|
-
logger$
|
|
5500
|
+
logger$u(errorMsg);
|
|
5501
|
+
logger$u('Error in validateEntity:', error);
|
|
5355
5502
|
// Throw error - fromPromise will automatically create error.platform event
|
|
5356
5503
|
throw error;
|
|
5357
5504
|
}
|
|
@@ -5360,7 +5507,7 @@ const validateEntity = xstate.fromPromise(async ({ input }) => {
|
|
|
5360
5507
|
// @ts-ignore - XState v5 type inference bug: fromPromise incorrectly expects output type to match input type
|
|
5361
5508
|
});
|
|
5362
5509
|
|
|
5363
|
-
const logger$
|
|
5510
|
+
const logger$t = debug('seedSdk:write:writeToDatabase');
|
|
5364
5511
|
// Writes that have started. Stopping a write process's actor can't cancel one, so code that deletes rows
|
|
5365
5512
|
// under live instances (Schema.destroy, test cleanup) waits for these after evicting the instances.
|
|
5366
5513
|
const inFlightWrites = new Set();
|
|
@@ -5387,8 +5534,8 @@ const writeToDatabase = xstate.fromCallback(({ sendBack, input }) => {
|
|
|
5387
5534
|
let output = input.entityData;
|
|
5388
5535
|
if (input.entityType === 'model') {
|
|
5389
5536
|
const writeMsg = `Writing model to database: ${input.entityData.modelName} (schemaId: ${input.entityData.schemaId})`;
|
|
5390
|
-
logger$
|
|
5391
|
-
logger$
|
|
5537
|
+
logger$t(writeMsg);
|
|
5538
|
+
logger$t(`Writing model to database:`, {
|
|
5392
5539
|
modelFileId: input.entityId,
|
|
5393
5540
|
modelName: input.entityData.modelName,
|
|
5394
5541
|
schemaId: input.entityData.schemaId,
|
|
@@ -5413,14 +5560,14 @@ const writeToDatabase = xstate.fromCallback(({ sendBack, input }) => {
|
|
|
5413
5560
|
output = input.entityData;
|
|
5414
5561
|
}
|
|
5415
5562
|
const successMsg = `Successfully wrote model "${input.entityData.modelName}" to database`;
|
|
5416
|
-
logger$
|
|
5563
|
+
logger$t(successMsg);
|
|
5417
5564
|
}
|
|
5418
5565
|
else if (input.entityType === 'modelProperty') {
|
|
5419
5566
|
// Use current ModelProperty context when available so a user rename before the
|
|
5420
5567
|
// initial write completes is not overwritten by the stale requestWrite payload.
|
|
5421
5568
|
let dataToWrite = input.entityData;
|
|
5422
5569
|
try {
|
|
5423
|
-
const mod = await Promise.resolve().then(function () { return require('./ModelProperty-
|
|
5570
|
+
const mod = await Promise.resolve().then(function () { return require('./ModelProperty-DXZ39Wyv.js'); });
|
|
5424
5571
|
const ModelProperty = mod?.ModelProperty ?? mod?.default;
|
|
5425
5572
|
if (ModelProperty && typeof ModelProperty.getById === 'function') {
|
|
5426
5573
|
const instance = ModelProperty.getById(input.entityId);
|
|
@@ -5463,7 +5610,7 @@ const writeToDatabase = xstate.fromCallback(({ sendBack, input }) => {
|
|
|
5463
5610
|
});
|
|
5464
5611
|
}
|
|
5465
5612
|
catch (error) {
|
|
5466
|
-
logger$
|
|
5613
|
+
logger$t('Error in writeToDatabase:', error);
|
|
5467
5614
|
sendBack({
|
|
5468
5615
|
type: 'writeError',
|
|
5469
5616
|
error: error instanceof Error ? error : new Error(String(error)),
|
|
@@ -5471,7 +5618,7 @@ const writeToDatabase = xstate.fromCallback(({ sendBack, input }) => {
|
|
|
5471
5618
|
}
|
|
5472
5619
|
};
|
|
5473
5620
|
const write = _write().catch((error) => {
|
|
5474
|
-
logger$
|
|
5621
|
+
logger$t('Error in writeToDatabase promise:', error);
|
|
5475
5622
|
sendBack({
|
|
5476
5623
|
type: 'writeError',
|
|
5477
5624
|
error: error instanceof Error ? error : new Error(String(error)),
|
|
@@ -5490,7 +5637,7 @@ var writeToDatabase$1 = /*#__PURE__*/Object.freeze({
|
|
|
5490
5637
|
writeToDatabase: writeToDatabase
|
|
5491
5638
|
});
|
|
5492
5639
|
|
|
5493
|
-
const logger$
|
|
5640
|
+
const logger$s = debug('seedSdk:write:writeProcessMachine');
|
|
5494
5641
|
const writeProcessMachine = xstate.setup({
|
|
5495
5642
|
types: {
|
|
5496
5643
|
context: {},
|
|
@@ -5528,8 +5675,8 @@ const writeProcessMachine = xstate.setup({
|
|
|
5528
5675
|
writeStatus: 'validating',
|
|
5529
5676
|
}),
|
|
5530
5677
|
({ event, context }) => {
|
|
5531
|
-
logger$
|
|
5532
|
-
logger$
|
|
5678
|
+
logger$s(`[startWrite] Received startWrite for ${context.entityType} "${context.entityId}"`);
|
|
5679
|
+
logger$s(`[startWrite] Data:`, event.data);
|
|
5533
5680
|
},
|
|
5534
5681
|
],
|
|
5535
5682
|
},
|
|
@@ -5538,14 +5685,14 @@ const writeProcessMachine = xstate.setup({
|
|
|
5538
5685
|
validating: {
|
|
5539
5686
|
entry: ({ context }) => {
|
|
5540
5687
|
const msg = `[validating] Entering validating state for ${context.entityType} "${context.entityId}"`;
|
|
5541
|
-
logger$
|
|
5542
|
-
logger$
|
|
5688
|
+
logger$s(msg);
|
|
5689
|
+
logger$s(`[validating] Entity data:`, context.pendingWrite?.data || context.entityData);
|
|
5543
5690
|
},
|
|
5544
5691
|
invoke: {
|
|
5545
5692
|
src: 'validateEntity',
|
|
5546
5693
|
input: ({ context }) => {
|
|
5547
5694
|
const entityData = context.pendingWrite?.data || context.entityData;
|
|
5548
|
-
logger$
|
|
5695
|
+
logger$s(`[validating] Invoking validateEntity with:`, { entityType: context.entityType, entityData });
|
|
5549
5696
|
return {
|
|
5550
5697
|
entityType: context.entityType,
|
|
5551
5698
|
entityData,
|
|
@@ -5573,8 +5720,8 @@ const writeProcessMachine = xstate.setup({
|
|
|
5573
5720
|
return false;
|
|
5574
5721
|
}
|
|
5575
5722
|
const resultMsg = `[validating] Validation result: isValid=${result.isValid}, errors=${result.errors?.length || 0}`;
|
|
5576
|
-
logger$
|
|
5577
|
-
logger$
|
|
5723
|
+
logger$s(resultMsg);
|
|
5724
|
+
logger$s(`[validating] Validation result:`, result);
|
|
5578
5725
|
return result.isValid;
|
|
5579
5726
|
},
|
|
5580
5727
|
actions: [
|
|
@@ -5584,7 +5731,7 @@ const writeProcessMachine = xstate.setup({
|
|
|
5584
5731
|
}),
|
|
5585
5732
|
({ context }) => {
|
|
5586
5733
|
const msg = `[validating] Validation passed, transitioning to writing for ${context.entityType} "${context.entityId}"`;
|
|
5587
|
-
logger$
|
|
5734
|
+
logger$s(msg);
|
|
5588
5735
|
},
|
|
5589
5736
|
],
|
|
5590
5737
|
},
|
|
@@ -5604,7 +5751,7 @@ const writeProcessMachine = xstate.setup({
|
|
|
5604
5751
|
}),
|
|
5605
5752
|
({ context, event }) => {
|
|
5606
5753
|
const result = event.output;
|
|
5607
|
-
logger$
|
|
5754
|
+
logger$s(`[validating] Validation failed for ${context.entityType} "${context.entityId}":`, result.errors);
|
|
5608
5755
|
},
|
|
5609
5756
|
],
|
|
5610
5757
|
},
|
|
@@ -5618,7 +5765,7 @@ const writeProcessMachine = xstate.setup({
|
|
|
5618
5765
|
}),
|
|
5619
5766
|
({ context, event }) => {
|
|
5620
5767
|
const errorMsg = `[validating] Validation error for ${context.entityType} "${context.entityId}": ${event.error}`;
|
|
5621
|
-
logger$
|
|
5768
|
+
logger$s(errorMsg);
|
|
5622
5769
|
console.error(errorMsg, event.error); // Always log to console
|
|
5623
5770
|
},
|
|
5624
5771
|
],
|
|
@@ -5633,8 +5780,8 @@ const writeProcessMachine = xstate.setup({
|
|
|
5633
5780
|
src: 'writeToDatabase',
|
|
5634
5781
|
input: ({ context }) => {
|
|
5635
5782
|
const entityData = context.pendingWrite?.data || context.entityData;
|
|
5636
|
-
logger$
|
|
5637
|
-
logger$
|
|
5783
|
+
logger$s(`[writing] Calling writeToDatabase for ${context.entityType} "${context.entityId}"`);
|
|
5784
|
+
logger$s(`[writing] Entity data:`, entityData);
|
|
5638
5785
|
return {
|
|
5639
5786
|
entityType: context.entityType,
|
|
5640
5787
|
entityId: context.entityId,
|
|
@@ -5655,7 +5802,7 @@ const writeProcessMachine = xstate.setup({
|
|
|
5655
5802
|
},
|
|
5656
5803
|
}),
|
|
5657
5804
|
({ context }) => {
|
|
5658
|
-
logger$
|
|
5805
|
+
logger$s(`[writing] Write successful for ${context.entityType} "${context.entityId}"`);
|
|
5659
5806
|
},
|
|
5660
5807
|
],
|
|
5661
5808
|
},
|
|
@@ -5669,7 +5816,7 @@ const writeProcessMachine = xstate.setup({
|
|
|
5669
5816
|
}),
|
|
5670
5817
|
({ context, event }) => {
|
|
5671
5818
|
const errorMsg = `[writing] Write error for ${context.entityType} "${context.entityId}": ${event.error}`;
|
|
5672
|
-
logger$
|
|
5819
|
+
logger$s(errorMsg);
|
|
5673
5820
|
// Nothing else reports a failed persist to the caller, so always log to console
|
|
5674
5821
|
console.error(errorMsg, event.error);
|
|
5675
5822
|
},
|
|
@@ -5712,7 +5859,7 @@ const writeProcessMachine = xstate.setup({
|
|
|
5712
5859
|
},
|
|
5713
5860
|
});
|
|
5714
5861
|
|
|
5715
|
-
const logger$
|
|
5862
|
+
const logger$r = debug('seedSdk:model:modelMachine');
|
|
5716
5863
|
const modelMachine = xstate.setup({
|
|
5717
5864
|
types: {
|
|
5718
5865
|
context: {},
|
|
@@ -5814,25 +5961,25 @@ const modelMachine = xstate.setup({
|
|
|
5814
5961
|
// Create ModelProperty instances for any new property IDs
|
|
5815
5962
|
if (Array.isArray(newPropertyIds) && newPropertyIds.length > 0) {
|
|
5816
5963
|
// Import and create instances asynchronously (fire-and-forget)
|
|
5817
|
-
Promise.resolve().then(function () { return require('./ModelProperty-
|
|
5964
|
+
Promise.resolve().then(function () { return require('./ModelProperty-DXZ39Wyv.js'); }).then(({ ModelProperty }) => {
|
|
5818
5965
|
if (self.getSnapshot().status === 'stopped')
|
|
5819
5966
|
return;
|
|
5820
5967
|
const createPromises = newPropertyIds.map(async (propertyFileId) => {
|
|
5821
5968
|
try {
|
|
5822
5969
|
const property = await ModelProperty.createById(propertyFileId, { evictionEpoch });
|
|
5823
5970
|
if (property) {
|
|
5824
|
-
logger$
|
|
5971
|
+
logger$r(`[modelMachine] Created/cached ModelProperty instance for propertyFileId "${propertyFileId}" after _liveQueryPropertyIds update`);
|
|
5825
5972
|
}
|
|
5826
5973
|
}
|
|
5827
5974
|
catch (error) {
|
|
5828
|
-
logger$
|
|
5975
|
+
logger$r(`[modelMachine] Error creating ModelProperty instance for propertyFileId "${propertyFileId}": ${error}`);
|
|
5829
5976
|
}
|
|
5830
5977
|
});
|
|
5831
5978
|
Promise.all(createPromises).catch((error) => {
|
|
5832
|
-
logger$
|
|
5979
|
+
logger$r(`[modelMachine] Error creating property instances: ${error}`);
|
|
5833
5980
|
});
|
|
5834
5981
|
}).catch((error) => {
|
|
5835
|
-
logger$
|
|
5982
|
+
logger$r(`[modelMachine] Error importing ModelProperty: ${error}`);
|
|
5836
5983
|
});
|
|
5837
5984
|
}
|
|
5838
5985
|
}, 0);
|
|
@@ -5910,11 +6057,11 @@ const modelMachine = xstate.setup({
|
|
|
5910
6057
|
target: 'idle',
|
|
5911
6058
|
actions: xstate.assign(({ context, event }) => {
|
|
5912
6059
|
const hasPendingProps = !!(context._pendingPropertyDefinitions && Object.keys(context._pendingPropertyDefinitions).length > 0);
|
|
5913
|
-
logger$
|
|
6060
|
+
logger$r(`[loadOrCreateModelSuccess] Preserving _pendingPropertyDefinitions: ${hasPendingProps} (${Object.keys(context._pendingPropertyDefinitions || {}).length} properties)`);
|
|
5914
6061
|
// Check if there's a pending model name update (set before this event to avoid validation trigger)
|
|
5915
6062
|
const finalModelName = context._pendingModelName || context.modelName;
|
|
5916
6063
|
if (context._pendingModelName) {
|
|
5917
|
-
logger$
|
|
6064
|
+
logger$r(`[loadOrCreateModelSuccess] Applying pending model name: "${context.modelName}" -> "${finalModelName}"`);
|
|
5918
6065
|
}
|
|
5919
6066
|
return {
|
|
5920
6067
|
...context,
|
|
@@ -5955,7 +6102,7 @@ const modelMachine = xstate.setup({
|
|
|
5955
6102
|
entry: xstate.assign({
|
|
5956
6103
|
writeProcess: ({ context, spawn }) => {
|
|
5957
6104
|
if (!context.writeProcess && context.id) {
|
|
5958
|
-
logger$
|
|
6105
|
+
logger$r(`[idle entry] Spawning writeProcess for model "${context.modelName}" (${context.id})`);
|
|
5959
6106
|
return spawn(writeProcessMachine, {
|
|
5960
6107
|
input: {
|
|
5961
6108
|
entityType: 'model',
|
|
@@ -5976,7 +6123,7 @@ const modelMachine = xstate.setup({
|
|
|
5976
6123
|
actions: ({ self }) => {
|
|
5977
6124
|
// This event will be handled by the Model instance's subscription
|
|
5978
6125
|
// The Model will call _refreshPropertiesFromDb when it receives this event
|
|
5979
|
-
logger$
|
|
6126
|
+
logger$r(`[idle] refreshProperties event received, will be handled by Model instance`);
|
|
5980
6127
|
},
|
|
5981
6128
|
},
|
|
5982
6129
|
validateModel: {
|
|
@@ -5988,14 +6135,14 @@ const modelMachine = xstate.setup({
|
|
|
5988
6135
|
requestWrite: {
|
|
5989
6136
|
actions: ({ context, event }) => {
|
|
5990
6137
|
if (context.writeProcess) {
|
|
5991
|
-
logger$
|
|
6138
|
+
logger$r(`[requestWrite] Sending startWrite to writeProcess with data:`, event.data);
|
|
5992
6139
|
context.writeProcess.send({
|
|
5993
6140
|
type: 'startWrite',
|
|
5994
6141
|
data: event.data,
|
|
5995
6142
|
});
|
|
5996
6143
|
}
|
|
5997
6144
|
else {
|
|
5998
|
-
logger$
|
|
6145
|
+
logger$r(`[requestWrite] ERROR: writeProcess not available for model "${context.modelName}" (${context.id})`);
|
|
5999
6146
|
}
|
|
6000
6147
|
},
|
|
6001
6148
|
},
|
|
@@ -6004,18 +6151,18 @@ const modelMachine = xstate.setup({
|
|
|
6004
6151
|
guard: ({ context }) => {
|
|
6005
6152
|
// If we have pending property definitions, transition to creatingProperties
|
|
6006
6153
|
const hasPending = !!(context._pendingPropertyDefinitions && Object.keys(context._pendingPropertyDefinitions).length > 0);
|
|
6007
|
-
logger$
|
|
6154
|
+
logger$r(`[writeSuccess guard] hasPending: ${hasPending}, _pendingPropertyDefinitions keys: ${context._pendingPropertyDefinitions ? Object.keys(context._pendingPropertyDefinitions).length : 0}`);
|
|
6008
6155
|
return hasPending;
|
|
6009
6156
|
},
|
|
6010
6157
|
target: 'creatingProperties',
|
|
6011
6158
|
actions: xstate.assign(({ context, event }) => {
|
|
6012
|
-
logger$
|
|
6159
|
+
logger$r(`[writeSuccess] Transitioning to creatingProperties for model "${context.modelName}"`);
|
|
6013
6160
|
// Update _dbId from write output if available (writeToDatabase returns _dbId, not id)
|
|
6014
6161
|
const newContext = { ...context };
|
|
6015
6162
|
const dbId = event.output?._dbId ?? event.output?.id;
|
|
6016
6163
|
if (dbId != null) {
|
|
6017
6164
|
newContext._dbId = dbId;
|
|
6018
|
-
logger$
|
|
6165
|
+
logger$r(`[writeSuccess] Updated _dbId to ${dbId}`);
|
|
6019
6166
|
}
|
|
6020
6167
|
return newContext;
|
|
6021
6168
|
}),
|
|
@@ -6059,12 +6206,12 @@ const modelMachine = xstate.setup({
|
|
|
6059
6206
|
},
|
|
6060
6207
|
creatingProperties: {
|
|
6061
6208
|
entry: ({ context }) => {
|
|
6062
|
-
logger$
|
|
6209
|
+
logger$r(`[creatingProperties entry] Starting property creation for model "${context.modelName}" with ${Object.keys(context._pendingPropertyDefinitions || {}).length} properties`);
|
|
6063
6210
|
},
|
|
6064
6211
|
invoke: {
|
|
6065
6212
|
src: 'createModelProperties',
|
|
6066
6213
|
input: ({ context }) => {
|
|
6067
|
-
logger$
|
|
6214
|
+
logger$r(`[creatingProperties invoke] Invoking createModelProperties with ${Object.keys(context._pendingPropertyDefinitions || {}).length} property definitions`);
|
|
6068
6215
|
return {
|
|
6069
6216
|
context,
|
|
6070
6217
|
propertyDefinitions: context._pendingPropertyDefinitions || {},
|
|
@@ -6096,7 +6243,7 @@ const modelMachine = xstate.setup({
|
|
|
6096
6243
|
createModelPropertiesError: {
|
|
6097
6244
|
target: 'error',
|
|
6098
6245
|
actions: ({ event }) => {
|
|
6099
|
-
logger$
|
|
6246
|
+
logger$r('Error creating model properties:', event.error);
|
|
6100
6247
|
},
|
|
6101
6248
|
},
|
|
6102
6249
|
},
|
|
@@ -6452,7 +6599,7 @@ var metadataLatest = /*#__PURE__*/Object.freeze({
|
|
|
6452
6599
|
getMetadataLatest: getMetadataLatest
|
|
6453
6600
|
});
|
|
6454
6601
|
|
|
6455
|
-
const logger$
|
|
6602
|
+
const logger$q = debug('seedSdk:item:actors:loadOrCreateItem');
|
|
6456
6603
|
/**
|
|
6457
6604
|
* Maps metadata.property_name to the ItemProperty Map key (same rules as the instance loop below).
|
|
6458
6605
|
*/
|
|
@@ -6544,16 +6691,16 @@ const createItemPropertyInstances = async (metadataRows, seedLocalId, seedUid, m
|
|
|
6544
6691
|
const schemaNameToTry = schemaName ?? model$1?.schemaName;
|
|
6545
6692
|
if (schemaNameToTry) {
|
|
6546
6693
|
try {
|
|
6547
|
-
const { Schema } = await Promise.resolve().then(function () { return require('./Schema-
|
|
6694
|
+
const { Schema } = await Promise.resolve().then(function () { return require('./Schema-DdP109QD.js'); });
|
|
6548
6695
|
const schemaInstance = Schema.create(schemaNameToTry, { waitForReady: false });
|
|
6549
6696
|
const schemaContext = schemaInstance.getService().getSnapshot().context;
|
|
6550
6697
|
if (schemaContext.models?.[modelName]?.properties) {
|
|
6551
6698
|
propertySchemas = schemaContext.models[modelName].properties;
|
|
6552
|
-
logger$
|
|
6699
|
+
logger$q(`Fallback: got ${Object.keys(propertySchemas).length} property schemas from Schema context for ${modelName}`);
|
|
6553
6700
|
}
|
|
6554
6701
|
}
|
|
6555
6702
|
catch (err) {
|
|
6556
|
-
logger$
|
|
6703
|
+
logger$q(`Fallback Schema context failed for ${modelName}: ${err}`);
|
|
6557
6704
|
}
|
|
6558
6705
|
}
|
|
6559
6706
|
if (Object.keys(propertySchemas).length === 0) {
|
|
@@ -6566,13 +6713,13 @@ const createItemPropertyInstances = async (metadataRows, seedLocalId, seedUid, m
|
|
|
6566
6713
|
const models = schemaFile.models;
|
|
6567
6714
|
if (models?.[modelName]?.properties) {
|
|
6568
6715
|
propertySchemas = models[modelName].properties;
|
|
6569
|
-
logger$
|
|
6716
|
+
logger$q(`Fallback: got ${Object.keys(propertySchemas).length} property schemas from loadAllSchemasFromDb for ${modelName}`);
|
|
6570
6717
|
break;
|
|
6571
6718
|
}
|
|
6572
6719
|
}
|
|
6573
6720
|
}
|
|
6574
6721
|
catch (err) {
|
|
6575
|
-
logger$
|
|
6722
|
+
logger$q(`Fallback loadAllSchemasFromDb failed for ${modelName}: ${err}`);
|
|
6576
6723
|
}
|
|
6577
6724
|
}
|
|
6578
6725
|
if (Object.keys(propertySchemas).length === 0) {
|
|
@@ -6587,17 +6734,17 @@ const createItemPropertyInstances = async (metadataRows, seedLocalId, seedUid, m
|
|
|
6587
6734
|
continue;
|
|
6588
6735
|
if (schemaFile.models?.[modelName]?.properties) {
|
|
6589
6736
|
propertySchemas = schemaFile.models[modelName].properties;
|
|
6590
|
-
logger$
|
|
6737
|
+
logger$q(`Fallback: got ${Object.keys(propertySchemas).length} property schemas from schema file for ${modelName}`);
|
|
6591
6738
|
break;
|
|
6592
6739
|
}
|
|
6593
6740
|
}
|
|
6594
6741
|
}
|
|
6595
6742
|
catch (err) {
|
|
6596
|
-
logger$
|
|
6743
|
+
logger$q(`Fallback schema file scan failed for ${modelName}: ${err}`);
|
|
6597
6744
|
}
|
|
6598
6745
|
}
|
|
6599
6746
|
if (Object.keys(propertySchemas).length === 0) {
|
|
6600
|
-
const schemaMod = await Promise.resolve().then(function () { return require('./index-
|
|
6747
|
+
const schemaMod = await Promise.resolve().then(function () { return require('./index-BHaWOwNF.js'); });
|
|
6601
6748
|
const { ModelPropertyDataTypes } = schemaMod;
|
|
6602
6749
|
const KNOWN_MODEL_FALLBACKS = {
|
|
6603
6750
|
Post: {
|
|
@@ -6609,12 +6756,12 @@ const createItemPropertyInstances = async (metadataRows, seedLocalId, seedUid, m
|
|
|
6609
6756
|
};
|
|
6610
6757
|
if (KNOWN_MODEL_FALLBACKS[modelName]) {
|
|
6611
6758
|
propertySchemas = KNOWN_MODEL_FALLBACKS[modelName];
|
|
6612
|
-
logger$
|
|
6759
|
+
logger$q(`Fallback: using known model schema for ${modelName} (${Object.keys(propertySchemas).length} properties)`);
|
|
6613
6760
|
}
|
|
6614
6761
|
}
|
|
6615
6762
|
}
|
|
6616
6763
|
// Fallbacks above may return schema-file shape ({ type, model, items }); normalize to dataType/ref/refValueType.
|
|
6617
|
-
const { normalizePropertyRecordSchema } = await Promise.resolve().then(function () { return require('./property-
|
|
6764
|
+
const { normalizePropertyRecordSchema } = await Promise.resolve().then(function () { return require('./property-fa0iju0J.js'); });
|
|
6618
6765
|
propertySchemas = Object.fromEntries(Object.entries(propertySchemas).map(([name, def]) => [name, normalizePropertyRecordSchema(def)]));
|
|
6619
6766
|
// Collapse rows that map to the same instance key (e.g. authorIdentityIds + authors -> authors).
|
|
6620
6767
|
// ItemProperty.create cache does not sync propertyValue on hit; processing order would otherwise
|
|
@@ -6627,7 +6774,7 @@ const createItemPropertyInstances = async (metadataRows, seedLocalId, seedUid, m
|
|
|
6627
6774
|
try {
|
|
6628
6775
|
let propertyName = metaRow.propertyName;
|
|
6629
6776
|
if (!propertyName) {
|
|
6630
|
-
logger$
|
|
6777
|
+
logger$q(`Metadata row missing propertyName, skipping`);
|
|
6631
6778
|
continue;
|
|
6632
6779
|
}
|
|
6633
6780
|
const listSchemaKey = resolveStorageNameToSchemaName(propertySchemas, propertyName);
|
|
@@ -6647,7 +6794,7 @@ const createItemPropertyInstances = async (metadataRows, seedLocalId, seedUid, m
|
|
|
6647
6794
|
// Infer propertyRecordSchema from metadata when schema is missing (enables persistence)
|
|
6648
6795
|
let propSchema = propertySchemas[propertyName];
|
|
6649
6796
|
if (!propSchema && isRefTypeFromMeta) {
|
|
6650
|
-
const schemaMod = await Promise.resolve().then(function () { return require('./index-
|
|
6797
|
+
const schemaMod = await Promise.resolve().then(function () { return require('./index-BHaWOwNF.js'); });
|
|
6651
6798
|
const { ModelPropertyDataTypes } = schemaMod;
|
|
6652
6799
|
propSchema = {
|
|
6653
6800
|
dataType: refSeedType === 'html' ? ModelPropertyDataTypes.Html
|
|
@@ -6677,14 +6824,14 @@ const createItemPropertyInstances = async (metadataRows, seedLocalId, seedUid, m
|
|
|
6677
6824
|
const property = ItemProperty.create(createProps, { waitForReady: false });
|
|
6678
6825
|
if (property) {
|
|
6679
6826
|
propertyInstances.set(propertyName, property);
|
|
6680
|
-
logger$
|
|
6827
|
+
logger$q(`Created/cached ItemProperty instance for propertyName "${propertyName}" with propertyRecordSchema: ${!!createProps.propertyRecordSchema}`);
|
|
6681
6828
|
}
|
|
6682
6829
|
else {
|
|
6683
|
-
logger$
|
|
6830
|
+
logger$q(`ItemProperty.create returned undefined for propertyName "${propertyName}"`);
|
|
6684
6831
|
}
|
|
6685
6832
|
}
|
|
6686
6833
|
catch (error) {
|
|
6687
|
-
logger$
|
|
6834
|
+
logger$q(`Error creating ItemProperty instance for propertyName "${metaRow.propertyName}": ${error}`);
|
|
6688
6835
|
}
|
|
6689
6836
|
}
|
|
6690
6837
|
// Create placeholder ItemProperty instances for model schema properties without metadata
|
|
@@ -6707,17 +6854,17 @@ const createItemPropertyInstances = async (metadataRows, seedLocalId, seedUid, m
|
|
|
6707
6854
|
const property = ItemProperty.create(createProps, { waitForReady: false });
|
|
6708
6855
|
if (property) {
|
|
6709
6856
|
propertyInstances.set(propertyName, property);
|
|
6710
|
-
logger$
|
|
6857
|
+
logger$q(`Created placeholder ItemProperty for model property "${propertyName}" (no metadata)`);
|
|
6711
6858
|
}
|
|
6712
6859
|
}
|
|
6713
6860
|
catch (error) {
|
|
6714
|
-
logger$
|
|
6861
|
+
logger$q(`Error creating placeholder ItemProperty for "${propertyName}": ${error}`);
|
|
6715
6862
|
}
|
|
6716
6863
|
}
|
|
6717
|
-
logger$
|
|
6864
|
+
logger$q(`Finished creating/caching ${propertyInstances.size} ItemProperty instances`);
|
|
6718
6865
|
}
|
|
6719
6866
|
catch (error) {
|
|
6720
|
-
logger$
|
|
6867
|
+
logger$q(`Error in createItemPropertyInstances: ${error}`);
|
|
6721
6868
|
// Don't throw - this is best-effort to pre-populate cache
|
|
6722
6869
|
}
|
|
6723
6870
|
return propertyInstances;
|
|
@@ -6725,7 +6872,7 @@ const createItemPropertyInstances = async (metadataRows, seedLocalId, seedUid, m
|
|
|
6725
6872
|
const loadOrCreateItem = xstate.fromCallback(({ sendBack, input: { context } }) => {
|
|
6726
6873
|
const _loadOrCreateItem = async () => {
|
|
6727
6874
|
const { seedLocalId, seedUid, modelName } = context;
|
|
6728
|
-
logger$
|
|
6875
|
+
logger$q(`loadOrCreateItem called for modelName: ${modelName}, seedLocalId: ${seedLocalId}, seedUid: ${seedUid}`);
|
|
6729
6876
|
if (!seedLocalId && !seedUid) {
|
|
6730
6877
|
throw new Error('seedLocalId or seedUid is required');
|
|
6731
6878
|
}
|
|
@@ -6755,7 +6902,7 @@ const loadOrCreateItem = xstate.fromCallback(({ sendBack, input: { context } })
|
|
|
6755
6902
|
.limit(1);
|
|
6756
6903
|
if (seedRecords.length === 0) {
|
|
6757
6904
|
// Seed not found - this is a new item, will be created elsewhere
|
|
6758
|
-
logger$
|
|
6905
|
+
logger$q(`Seed not found in database for seedLocalId: ${seedLocalId}, seedUid: ${seedUid}`);
|
|
6759
6906
|
sendBack({
|
|
6760
6907
|
type: 'loadOrCreateItemSuccess',
|
|
6761
6908
|
item: {
|
|
@@ -6798,7 +6945,7 @@ const loadOrCreateItem = xstate.fromCallback(({ sendBack, input: { context } })
|
|
|
6798
6945
|
.where(drizzleOrm.eq(seeds.localId, resolvedSeedLocalId))
|
|
6799
6946
|
.limit(1);
|
|
6800
6947
|
if (versionRecords.length === 0) {
|
|
6801
|
-
logger$
|
|
6948
|
+
logger$q(`No version data found for seedLocalId: ${resolvedSeedLocalId}`);
|
|
6802
6949
|
sendBack({
|
|
6803
6950
|
type: 'loadOrCreateItemSuccess',
|
|
6804
6951
|
item: {
|
|
@@ -6825,7 +6972,7 @@ const loadOrCreateItem = xstate.fromCallback(({ sendBack, input: { context } })
|
|
|
6825
6972
|
// A seed whose versions are all revoked (fully unpublished) has no latest version but still has
|
|
6826
6973
|
// its last property values; load those like any other item. Only a seed without versions stops here.
|
|
6827
6974
|
if (!latestVersionLocalId && !versionRecord.versionsCount) {
|
|
6828
|
-
logger$
|
|
6975
|
+
logger$q(`No latest version found for seedLocalId: ${resolvedSeedLocalId}`);
|
|
6829
6976
|
sendBack({
|
|
6830
6977
|
type: 'loadOrCreateItemSuccess',
|
|
6831
6978
|
item: {
|
|
@@ -6857,7 +7004,7 @@ const loadOrCreateItem = xstate.fromCallback(({ sendBack, input: { context } })
|
|
|
6857
7004
|
.select()
|
|
6858
7005
|
.from(metadataLatest)
|
|
6859
7006
|
.where(drizzleOrm.eq(metadataLatest.rowNum, 1));
|
|
6860
|
-
logger$
|
|
7007
|
+
logger$q(`Found ${metadataRecords.length} latest-per-property metadata rows for seed ${resolvedSeedLocalId} (latestVersionLocalId=${latestVersionLocalId} used for placeholders only)`);
|
|
6861
7008
|
// Step 4: Create ItemProperty instances from metadata records + placeholders for model schema properties
|
|
6862
7009
|
// Always call when we have a valid version so placeholders are created for properties without metadata
|
|
6863
7010
|
const propertyInstances = await createItemPropertyInstances(metadataRecords, resolvedSeedLocalId, resolvedSeedUid, modelName, latestVersionLocalId, latestVersionUid, modelScope);
|
|
@@ -6890,7 +7037,7 @@ const loadOrCreateItem = xstate.fromCallback(({ sendBack, input: { context } })
|
|
|
6890
7037
|
});
|
|
6891
7038
|
};
|
|
6892
7039
|
_loadOrCreateItem().catch((error) => {
|
|
6893
|
-
logger$
|
|
7040
|
+
logger$q(`Error in loadOrCreateItem: ${error}`);
|
|
6894
7041
|
sendBack({
|
|
6895
7042
|
type: 'loadOrCreateItemError',
|
|
6896
7043
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -6898,7 +7045,7 @@ const loadOrCreateItem = xstate.fromCallback(({ sendBack, input: { context } })
|
|
|
6898
7045
|
});
|
|
6899
7046
|
});
|
|
6900
7047
|
|
|
6901
|
-
const logger$
|
|
7048
|
+
const logger$p = debug('seedSdk:Item:runPublish');
|
|
6902
7049
|
const runPublish = xstate.fromCallback(({ sendBack, input: { context } }) => {
|
|
6903
7050
|
const { seedLocalId } = context;
|
|
6904
7051
|
const _runPublish = async () => {
|
|
@@ -6914,9 +7061,9 @@ const runPublish = xstate.fromCallback(({ sendBack, input: { context } }) => {
|
|
|
6914
7061
|
sendBack({ type: 'publishError', error: new Error(`Item not found for seedLocalId: ${seedLocalId}`) });
|
|
6915
7062
|
return;
|
|
6916
7063
|
}
|
|
6917
|
-
const getPublishUploadsMod = await Promise.resolve().then(function () { return require('./getPublishUploads-
|
|
7064
|
+
const getPublishUploadsMod = await Promise.resolve().then(function () { return require('./getPublishUploads-DsXY9S4U.js'); });
|
|
6918
7065
|
const { getPublishUploads } = getPublishUploadsMod;
|
|
6919
|
-
const getPublishPayloadMod = await Promise.resolve().then(function () { return require('./getPublishPayload-
|
|
7066
|
+
const getPublishPayloadMod = await Promise.resolve().then(function () { return require('./getPublishPayload-CqlqRdot.js'); });
|
|
6920
7067
|
const { getPublishPayload } = getPublishPayloadMod;
|
|
6921
7068
|
const uploads = await getPublishUploads(item);
|
|
6922
7069
|
let uploadedTransactions = [];
|
|
@@ -6932,12 +7079,12 @@ const runPublish = xstate.fromCallback(({ sendBack, input: { context } }) => {
|
|
|
6932
7079
|
}
|
|
6933
7080
|
}
|
|
6934
7081
|
await getPublishPayload(item, uploadedTransactions);
|
|
6935
|
-
logger$
|
|
7082
|
+
logger$p('runPublish: payload prepared (upload/EAS submit stubbed)');
|
|
6936
7083
|
sendBack({ type: 'publishSuccess' });
|
|
6937
7084
|
}
|
|
6938
7085
|
catch (err) {
|
|
6939
7086
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
6940
|
-
logger$
|
|
7087
|
+
logger$p('runPublish error:', error);
|
|
6941
7088
|
sendBack({ type: 'publishError', error });
|
|
6942
7089
|
}
|
|
6943
7090
|
};
|
|
@@ -7212,7 +7359,7 @@ async function getLatestPublishedVersionRow(seedLocalId) {
|
|
|
7212
7359
|
return null;
|
|
7213
7360
|
}
|
|
7214
7361
|
|
|
7215
|
-
const logger$
|
|
7362
|
+
const logger$o = debug('seedSdk:db:read:getItemData');
|
|
7216
7363
|
/**
|
|
7217
7364
|
* Assemble flat ItemData for Item / liveQuery / draft-head authoring.
|
|
7218
7365
|
*
|
|
@@ -7228,7 +7375,7 @@ const getItemData = async ({ modelName, seedLocalId, seedUid, }) => {
|
|
|
7228
7375
|
if (seedUid && !seedLocalId) {
|
|
7229
7376
|
const seedData = await getSeedData({ seedUid });
|
|
7230
7377
|
if (!seedData) {
|
|
7231
|
-
logger$
|
|
7378
|
+
logger$o('[db/queries] [getItem] no seedData seedUid', seedUid);
|
|
7232
7379
|
return;
|
|
7233
7380
|
}
|
|
7234
7381
|
seedLocalId = seedData.localId || undefined;
|
|
@@ -7255,9 +7402,9 @@ const getItemData = async ({ modelName, seedLocalId, seedUid, }) => {
|
|
|
7255
7402
|
.from(seeds)
|
|
7256
7403
|
.where(drizzleOrm.and(...whereClauses))
|
|
7257
7404
|
.limit(1);
|
|
7258
|
-
logger$
|
|
7405
|
+
logger$o('[getItemData] Seed query result', { rowsCount: seedRows?.length || 0, firstRow: seedRows?.[0] });
|
|
7259
7406
|
if (!seedRows || seedRows.length === 0) {
|
|
7260
|
-
logger$
|
|
7407
|
+
logger$o('[db/queries] [getItemDataFromDb] no seedRows found', { modelName, seedLocalId, seedUid });
|
|
7261
7408
|
return;
|
|
7262
7409
|
}
|
|
7263
7410
|
const seedRow = seedRows[0];
|
|
@@ -7283,7 +7430,7 @@ const getItemData = async ({ modelName, seedLocalId, seedUid, }) => {
|
|
|
7283
7430
|
}
|
|
7284
7431
|
}
|
|
7285
7432
|
catch (error) {
|
|
7286
|
-
logger$
|
|
7433
|
+
logger$o('[getItemData] Error deriving schemaName:', error);
|
|
7287
7434
|
}
|
|
7288
7435
|
}
|
|
7289
7436
|
// Now get version data if it exists - query versions table directly
|
|
@@ -7539,8 +7686,9 @@ function isSeedRowOwned(row, ownedAddresses) {
|
|
|
7539
7686
|
return publisherIsInOwnedSet(publisher, ownedAddresses);
|
|
7540
7687
|
}
|
|
7541
7688
|
/**
|
|
7542
|
-
* SQL for
|
|
7543
|
-
* Compares `lower(publisher)` so checksummed rows match persisted lowercase
|
|
7689
|
+
* SQL for an address list on `publisher`. Empty list → no rows (`1=0`).
|
|
7690
|
+
* Compares `lower(publisher)` so checksummed rows match persisted lowercase addresses.
|
|
7691
|
+
* Used for `addressFilter: 'watched'`; `'owned'` uses {@link ownedSeedsSql}.
|
|
7544
7692
|
*/
|
|
7545
7693
|
function publisherInAddressListSql(column, addresses) {
|
|
7546
7694
|
const normalized = normalizeAddressList(addresses);
|
|
@@ -7549,6 +7697,30 @@ function publisherInAddressListSql(column, addresses) {
|
|
|
7549
7697
|
}
|
|
7550
7698
|
return drizzleOrm.inArray(drizzleOrm.sql `lower(${column})`, normalized);
|
|
7551
7699
|
}
|
|
7700
|
+
/** SQL form of {@link isPlaceholderUid}: null, `''`, `'NULL'`, or zero bytes32 (trimmed, any case). */
|
|
7701
|
+
function placeholderUidSql(column) {
|
|
7702
|
+
return drizzleOrm.or(drizzleOrm.isNull(column), drizzleOrm.eq(column, 'NULL'), drizzleOrm.sql `trim(${column}) = ''`, drizzleOrm.sql `lower(trim(${column})) = ${ZERO_BYTES32.toLowerCase()}`);
|
|
7703
|
+
}
|
|
7704
|
+
const isBlankSql = (column) => drizzleOrm.or(drizzleOrm.isNull(column), drizzleOrm.eq(column, ''));
|
|
7705
|
+
/** SQL form of {@link isLocalUnsealedDraft} on `seeds`. */
|
|
7706
|
+
function localUnsealedDraftSql() {
|
|
7707
|
+
return drizzleOrm.and(isBlankSql(seeds.publisher), placeholderUidSql(seeds.uid), isBlankSql(seeds.attestationRaw));
|
|
7708
|
+
}
|
|
7709
|
+
/**
|
|
7710
|
+
* SQL for `addressFilter: 'owned'`: a row passes exactly when {@link isSeedRowOwned} is true.
|
|
7711
|
+
* - Empty owned → local unsealed drafts (pre-connect authoring), not every row.
|
|
7712
|
+
* - Owned set → `lower(trim(publisher))` in the set, or, when publisher is blank, the
|
|
7713
|
+
* `attestationRaw.attester` ({@link resolvePublisherFromSeedRow}). `json_valid` guards
|
|
7714
|
+
* `json_extract`, which throws on malformed JSON.
|
|
7715
|
+
*/
|
|
7716
|
+
function ownedSeedsSql(ownedAddresses) {
|
|
7717
|
+
const normalized = normalizeAddressList(ownedAddresses);
|
|
7718
|
+
if (normalized.length === 0) {
|
|
7719
|
+
return localUnsealedDraftSql();
|
|
7720
|
+
}
|
|
7721
|
+
const attester = drizzleOrm.sql `CASE WHEN json_valid(${seeds.attestationRaw}) THEN lower(trim(json_extract(${seeds.attestationRaw}, '$.attester'))) END`;
|
|
7722
|
+
return drizzleOrm.or(drizzleOrm.inArray(drizzleOrm.sql `lower(trim(${seeds.publisher}))`, normalized), drizzleOrm.and(isBlankSql(seeds.publisher), drizzleOrm.inArray(attester, normalized)));
|
|
7723
|
+
}
|
|
7552
7724
|
/**
|
|
7553
7725
|
* Checks if the current user owns the item (publisher is in persisted owned addresses).
|
|
7554
7726
|
*
|
|
@@ -7585,6 +7757,9 @@ var ownership = /*#__PURE__*/Object.freeze({
|
|
|
7585
7757
|
isItemOwned: isItemOwned,
|
|
7586
7758
|
isLocalUnsealedDraft: isLocalUnsealedDraft,
|
|
7587
7759
|
isSeedRowOwned: isSeedRowOwned,
|
|
7760
|
+
localUnsealedDraftSql: localUnsealedDraftSql,
|
|
7761
|
+
ownedSeedsSql: ownedSeedsSql,
|
|
7762
|
+
placeholderUidSql: placeholderUidSql,
|
|
7588
7763
|
publisherInAddressListSql: publisherInAddressListSql,
|
|
7589
7764
|
publisherIsInOwnedSet: publisherIsInOwnedSet,
|
|
7590
7765
|
resolvePublisherFromSeedRow: resolvePublisherFromSeedRow
|
|
@@ -7604,7 +7779,7 @@ const getItemsData = async ({ modelName, modelFileId, schemaName, deleted, inclu
|
|
|
7604
7779
|
const appDb = BaseDb.getAppDb();
|
|
7605
7780
|
const conditions = [];
|
|
7606
7781
|
if (!includeEas) {
|
|
7607
|
-
conditions.push(
|
|
7782
|
+
conditions.push(placeholderUidSql(seeds.uid));
|
|
7608
7783
|
}
|
|
7609
7784
|
if (modelName) {
|
|
7610
7785
|
conditions.push(drizzleOrm.eq(seeds.type, casing.toSnakeCase(modelName)));
|
|
@@ -7620,7 +7795,7 @@ const getItemsData = async ({ modelName, modelFileId, schemaName, deleted, inclu
|
|
|
7620
7795
|
}
|
|
7621
7796
|
if (addressFilter === 'owned') {
|
|
7622
7797
|
const ownedAddresses = await getAddressesForItemsFilter('owned');
|
|
7623
|
-
conditions.push(
|
|
7798
|
+
conditions.push(ownedSeedsSql(ownedAddresses));
|
|
7624
7799
|
}
|
|
7625
7800
|
else if (addressFilter === 'watched') {
|
|
7626
7801
|
const watchedAddresses = await getAddressesForItemsFilter('watched');
|
|
@@ -7697,12 +7872,12 @@ async function getLatestVersionRow(seedLocalId) {
|
|
|
7697
7872
|
return rows[0] ?? null;
|
|
7698
7873
|
}
|
|
7699
7874
|
|
|
7700
|
-
const logger$
|
|
7875
|
+
const logger$n = debug('seedSdk:ItemProperty:service:actors:waitForDb');
|
|
7701
7876
|
const waitForDb = xstate.fromCallback(({ sendBack }) => {
|
|
7702
7877
|
// Check immediately first
|
|
7703
7878
|
const appDb = BaseDb.getAppDb();
|
|
7704
7879
|
if (appDb) {
|
|
7705
|
-
logger$
|
|
7880
|
+
logger$n('Database is ready (immediate check)');
|
|
7706
7881
|
sendBack({ type: 'waitForDbSuccess' });
|
|
7707
7882
|
return;
|
|
7708
7883
|
}
|
|
@@ -7714,24 +7889,24 @@ const waitForDb = xstate.fromCallback(({ sendBack }) => {
|
|
|
7714
7889
|
const appDb = BaseDb.getAppDb();
|
|
7715
7890
|
if (appDb) {
|
|
7716
7891
|
clearInterval(interval);
|
|
7717
|
-
logger$
|
|
7892
|
+
logger$n('Database is ready (after polling)');
|
|
7718
7893
|
resolve();
|
|
7719
7894
|
}
|
|
7720
7895
|
else if (Date.now() - startTime > timeout) {
|
|
7721
7896
|
clearInterval(interval);
|
|
7722
7897
|
const error = new Error('Database not available after timeout');
|
|
7723
|
-
logger$
|
|
7898
|
+
logger$n(`Database wait timeout: ${error.message}`);
|
|
7724
7899
|
reject(error);
|
|
7725
7900
|
}
|
|
7726
7901
|
}, 100);
|
|
7727
7902
|
});
|
|
7728
7903
|
_waitForDb
|
|
7729
7904
|
.then(() => {
|
|
7730
|
-
logger$
|
|
7905
|
+
logger$n('Sending waitForDbSuccess event');
|
|
7731
7906
|
sendBack({ type: 'waitForDbSuccess' });
|
|
7732
7907
|
})
|
|
7733
7908
|
.catch((error) => {
|
|
7734
|
-
logger$
|
|
7909
|
+
logger$n(`Error waiting for database: ${error}`);
|
|
7735
7910
|
sendBack({ type: 'waitForDbError', error: error.message });
|
|
7736
7911
|
});
|
|
7737
7912
|
});
|
|
@@ -7883,7 +8058,7 @@ const getRelationValueData = async (propertyValue) => {
|
|
|
7883
8058
|
|
|
7884
8059
|
// Dynamic import to break circular dependency: schema/index -> ... -> resolveRelatedValue -> schema/index
|
|
7885
8060
|
// import { ModelPropertyDataTypes } from '@/schema'
|
|
7886
|
-
const logger$
|
|
8061
|
+
const logger$m = debug('seedSdk:property:actors:resolveRelatedValue');
|
|
7887
8062
|
const storageTransactionIdToContentUrl = new Map();
|
|
7888
8063
|
const refResolvedValueToContentUrl = new Map();
|
|
7889
8064
|
const seedUidToContentUrl = new Map();
|
|
@@ -7891,14 +8066,14 @@ const resolveRelatedValue = xstate.fromCallback(({ sendBack, input: { context }
|
|
|
7891
8066
|
const { isRelation, propertyValue, propertyName, seedUid, propertyRecordSchema, populatedFromDb, schemaUid, } = context;
|
|
7892
8067
|
const _resolveRelatedValue = async () => {
|
|
7893
8068
|
// Use dynamic import to break circular dependency
|
|
7894
|
-
const schemaMod = await Promise.resolve().then(function () { return require('./index-
|
|
8069
|
+
const schemaMod = await Promise.resolve().then(function () { return require('./index-BHaWOwNF.js'); });
|
|
7895
8070
|
const { ModelPropertyDataTypes } = schemaMod;
|
|
7896
8071
|
if (!propertyValue || !isRelation || populatedFromDb) {
|
|
7897
8072
|
return;
|
|
7898
8073
|
}
|
|
7899
8074
|
if (Array.isArray(propertyValue)) {
|
|
7900
8075
|
// TODO: Handle array of seedUids
|
|
7901
|
-
logger$
|
|
8076
|
+
logger$m(`${propertyName} value is an array of seedUids`);
|
|
7902
8077
|
return;
|
|
7903
8078
|
}
|
|
7904
8079
|
if (typeof propertyValue === 'object') {
|
|
@@ -7910,7 +8085,7 @@ const resolveRelatedValue = xstate.fromCallback(({ sendBack, input: { context }
|
|
|
7910
8085
|
parsedValue = JSON.parse(relationKey);
|
|
7911
8086
|
}
|
|
7912
8087
|
catch (error) {
|
|
7913
|
-
logger$
|
|
8088
|
+
logger$m(`${propertyName} value is not a JSON string.`);
|
|
7914
8089
|
}
|
|
7915
8090
|
if (!parsedValue && seedUidToContentUrl.has(relationKey)) {
|
|
7916
8091
|
const contentUrl = seedUidToContentUrl.get(relationKey);
|
|
@@ -7925,7 +8100,7 @@ const resolveRelatedValue = xstate.fromCallback(({ sendBack, input: { context }
|
|
|
7925
8100
|
return true;
|
|
7926
8101
|
}
|
|
7927
8102
|
if (Array.isArray(parsedValue)) {
|
|
7928
|
-
logger$
|
|
8103
|
+
logger$m(`${propertyName} value is a stringified array of seedUids`);
|
|
7929
8104
|
return;
|
|
7930
8105
|
}
|
|
7931
8106
|
const storageTransactionId = await getStorageTransactionIdForSeedUid(relationKey);
|
|
@@ -8374,7 +8549,7 @@ async function readHtmlBodyForStorageSeedPropertyValue(propertyValue) {
|
|
|
8374
8549
|
|
|
8375
8550
|
// Dynamic import to break circular dependency: schema/index -> ... -> hydrateFromDb -> schema/index
|
|
8376
8551
|
// import { ModelPropertyDataTypes } from '@/schema'
|
|
8377
|
-
const logger$
|
|
8552
|
+
const logger$l = debug('seedSdk:property:actors:hydrateFromDb');
|
|
8378
8553
|
const hydrateFromDb = xstate.fromCallback(({ sendBack, input: { context } }) => {
|
|
8379
8554
|
const { seedUid, seedLocalId, propertyName: propertyNameRaw, propertyRecordSchema, modelName, } = context;
|
|
8380
8555
|
if (!propertyNameRaw) {
|
|
@@ -8385,7 +8560,7 @@ const hydrateFromDb = xstate.fromCallback(({ sendBack, input: { context } }) =>
|
|
|
8385
8560
|
// to use dynamically imported ModelPropertyDataTypes
|
|
8386
8561
|
const _hydrateFromDb = async () => {
|
|
8387
8562
|
// Use dynamic import to break circular dependency
|
|
8388
|
-
const schemaMod = await Promise.resolve().then(function () { return require('./index-
|
|
8563
|
+
const schemaMod = await Promise.resolve().then(function () { return require('./index-BHaWOwNF.js'); });
|
|
8389
8564
|
const { ModelPropertyDataTypes } = schemaMod;
|
|
8390
8565
|
const appDb = BaseDb.getAppDb();
|
|
8391
8566
|
const whereClauses = [];
|
|
@@ -8491,7 +8666,7 @@ const hydrateFromDb = xstate.fromCallback(({ sendBack, input: { context } }) =>
|
|
|
8491
8666
|
}
|
|
8492
8667
|
}
|
|
8493
8668
|
catch (e) {
|
|
8494
|
-
logger$
|
|
8669
|
+
logger$l('[hydrateFromDb] Image file resolution error', e);
|
|
8495
8670
|
}
|
|
8496
8671
|
}
|
|
8497
8672
|
}
|
|
@@ -8522,7 +8697,7 @@ const hydrateFromDb = xstate.fromCallback(({ sendBack, input: { context } }) =>
|
|
|
8522
8697
|
}
|
|
8523
8698
|
}
|
|
8524
8699
|
catch (e) {
|
|
8525
|
-
logger$
|
|
8700
|
+
logger$l('[hydrateFromDb] File resolution error', e);
|
|
8526
8701
|
}
|
|
8527
8702
|
}
|
|
8528
8703
|
if (propertyRecordSchema &&
|
|
@@ -8625,11 +8800,11 @@ const hydrateFromDb = xstate.fromCallback(({ sendBack, input: { context } }) =>
|
|
|
8625
8800
|
});
|
|
8626
8801
|
});
|
|
8627
8802
|
|
|
8628
|
-
const logger$
|
|
8803
|
+
const logger$k = debug('seedSdk:itemProperty:actors:loadOrCreateProperty');
|
|
8629
8804
|
const loadOrCreateProperty = xstate.fromCallback(({ sendBack, input: { context } }) => {
|
|
8630
8805
|
const _loadOrCreateProperty = async () => {
|
|
8631
8806
|
const { seedLocalId, seedUid, propertyName, versionLocalId } = context;
|
|
8632
|
-
logger$
|
|
8807
|
+
logger$k(`loadOrCreateProperty called for propertyName: ${propertyName}, seedLocalId: ${seedLocalId}, seedUid: ${seedUid}`);
|
|
8633
8808
|
if (!seedLocalId && !seedUid) {
|
|
8634
8809
|
throw new Error('seedLocalId or seedUid is required');
|
|
8635
8810
|
}
|
|
@@ -8681,15 +8856,15 @@ const loadOrCreateProperty = xstate.fromCallback(({ sendBack, input: { context }
|
|
|
8681
8856
|
const schemas = modelPropertiesToObject(model$1.properties);
|
|
8682
8857
|
propertyRecordSchema = schemas[propertyName];
|
|
8683
8858
|
if (propertyRecordSchema) {
|
|
8684
|
-
logger$
|
|
8859
|
+
logger$k(`Metadata not found: loaded propertyRecordSchema from Model for propertyName "${propertyName}"`);
|
|
8685
8860
|
}
|
|
8686
8861
|
}
|
|
8687
8862
|
}
|
|
8688
8863
|
catch (error) {
|
|
8689
|
-
logger$
|
|
8864
|
+
logger$k(`Metadata not found: Model fallback failed for propertyName "${propertyName}": ${error}`);
|
|
8690
8865
|
}
|
|
8691
8866
|
}
|
|
8692
|
-
logger$
|
|
8867
|
+
logger$k(`Metadata not found in database for propertyName: ${propertyName}, seedLocalId: ${seedLocalId}`);
|
|
8693
8868
|
sendBack({
|
|
8694
8869
|
type: 'loadOrCreatePropertySuccess',
|
|
8695
8870
|
property: {
|
|
@@ -8762,7 +8937,7 @@ const loadOrCreateProperty = xstate.fromCallback(({ sendBack, input: { context }
|
|
|
8762
8937
|
};
|
|
8763
8938
|
// Merge with schema from file/DB to get validation rules (enum, pattern, etc.) - properties table doesn't store these
|
|
8764
8939
|
try {
|
|
8765
|
-
const { getPropertySchema } = await Promise.resolve().then(function () { return require('./property-
|
|
8940
|
+
const { getPropertySchema } = await Promise.resolve().then(function () { return require('./property-fa0iju0J.js'); });
|
|
8766
8941
|
let schemaFromFile = await getPropertySchema(normalizedModelName, propertyName, {
|
|
8767
8942
|
modelFileId: modelRecord?.schemaFileId ?? modelFileId,
|
|
8768
8943
|
});
|
|
@@ -8802,7 +8977,7 @@ const loadOrCreateProperty = xstate.fromCallback(({ sendBack, input: { context }
|
|
|
8802
8977
|
}
|
|
8803
8978
|
}
|
|
8804
8979
|
catch (error) {
|
|
8805
|
-
logger$
|
|
8980
|
+
logger$k(`Error loading propertyRecordSchema from database: ${error}`);
|
|
8806
8981
|
// Continue without propertyRecordSchema
|
|
8807
8982
|
}
|
|
8808
8983
|
}
|
|
@@ -8820,12 +8995,12 @@ const loadOrCreateProperty = xstate.fromCallback(({ sendBack, input: { context }
|
|
|
8820
8995
|
const schemas = modelPropertiesToObject(model$1.properties);
|
|
8821
8996
|
propertyRecordSchema = schemas[propertyName];
|
|
8822
8997
|
if (propertyRecordSchema) {
|
|
8823
|
-
logger$
|
|
8998
|
+
logger$k(`Fallback: loaded propertyRecordSchema from Model for propertyName "${propertyName}"`);
|
|
8824
8999
|
}
|
|
8825
9000
|
}
|
|
8826
9001
|
}
|
|
8827
9002
|
catch (error) {
|
|
8828
|
-
logger$
|
|
9003
|
+
logger$k(`Fallback Model lookup failed for propertyName "${propertyName}": ${error}`);
|
|
8829
9004
|
}
|
|
8830
9005
|
}
|
|
8831
9006
|
// For Html: read file content for renderValue (propertyValue is seed ID; blob URLs in DB are invalid after reload)
|
|
@@ -8866,7 +9041,7 @@ const loadOrCreateProperty = xstate.fromCallback(({ sendBack, input: { context }
|
|
|
8866
9041
|
}
|
|
8867
9042
|
}
|
|
8868
9043
|
catch (e) {
|
|
8869
|
-
logger$
|
|
9044
|
+
logger$k(`Failed to read Html file for ${propertyName}: ${e}`);
|
|
8870
9045
|
}
|
|
8871
9046
|
}
|
|
8872
9047
|
// Covers missing ref row, wrong ref, or DB dataType casing "html" (handled by isHtml above).
|
|
@@ -8924,7 +9099,7 @@ const loadOrCreateProperty = xstate.fromCallback(({ sendBack, input: { context }
|
|
|
8924
9099
|
});
|
|
8925
9100
|
}
|
|
8926
9101
|
catch (e) {
|
|
8927
|
-
logger$
|
|
9102
|
+
logger$k(`loadOrCreateProperty: updateMetadata refResolvedValue failed for ${propertyName}:`, e);
|
|
8928
9103
|
}
|
|
8929
9104
|
}
|
|
8930
9105
|
}
|
|
@@ -8946,7 +9121,7 @@ const loadOrCreateProperty = xstate.fromCallback(({ sendBack, input: { context }
|
|
|
8946
9121
|
}
|
|
8947
9122
|
}
|
|
8948
9123
|
catch (e) {
|
|
8949
|
-
logger$
|
|
9124
|
+
logger$k(`Failed to resolve Image file for ${propertyName}: ${e}`);
|
|
8950
9125
|
}
|
|
8951
9126
|
}
|
|
8952
9127
|
const isFile = refSeedType === 'file' ||
|
|
@@ -8964,7 +9139,7 @@ const loadOrCreateProperty = xstate.fromCallback(({ sendBack, input: { context }
|
|
|
8964
9139
|
}
|
|
8965
9140
|
}
|
|
8966
9141
|
catch (e) {
|
|
8967
|
-
logger$
|
|
9142
|
+
logger$k(`Failed to resolve File for ${propertyName}: ${e}`);
|
|
8968
9143
|
}
|
|
8969
9144
|
}
|
|
8970
9145
|
let propertyValueOut = metadataRecord.propertyValue || undefined;
|
|
@@ -8997,7 +9172,7 @@ const loadOrCreateProperty = xstate.fromCallback(({ sendBack, input: { context }
|
|
|
8997
9172
|
});
|
|
8998
9173
|
};
|
|
8999
9174
|
_loadOrCreateProperty().catch((error) => {
|
|
9000
|
-
logger$
|
|
9175
|
+
logger$k(`Error in loadOrCreateProperty: ${error}`);
|
|
9001
9176
|
sendBack({
|
|
9002
9177
|
type: 'loadOrCreatePropertyError',
|
|
9003
9178
|
error: error instanceof Error ? error.message : String(error),
|
|
@@ -9027,10 +9202,10 @@ const getVersionData = async ({ localId, seedLocalId, uid, }) => {
|
|
|
9027
9202
|
return queryRows[0];
|
|
9028
9203
|
};
|
|
9029
9204
|
|
|
9030
|
-
const logger$
|
|
9205
|
+
const logger$j = debug('seedSdk:write:updateItemPropertyValue');
|
|
9031
9206
|
const updateItemPropertyValue = async ({ localId: localIdParam, propertyName, newValue, seedUid, seedLocalId, modelName, refSeedType, refResolvedValue, refResolvedDisplayValue, versionLocalId, versionUid, schemaUid, localStorageDir, dataType, refValueType, }) => {
|
|
9032
9207
|
if (!localIdParam && !seedLocalId) {
|
|
9033
|
-
logger$
|
|
9208
|
+
logger$j(`[db/write] [updateItemPropertyValue] no propertyLocalId or seedLocalId for property: ${propertyName}`);
|
|
9034
9209
|
return;
|
|
9035
9210
|
}
|
|
9036
9211
|
// Every write below binds parameters: store the value as given (no SQL escaping).
|
|
@@ -9082,7 +9257,7 @@ const updateItemPropertyValue = async ({ localId: localIdParam, propertyName, ne
|
|
|
9082
9257
|
modelType === modelName?.toLowerCase() &&
|
|
9083
9258
|
refSeedTypeFromDb === refSeedType &&
|
|
9084
9259
|
refResolvedValueFromDb === refResolvedValue) {
|
|
9085
|
-
logger$
|
|
9260
|
+
logger$j(`[db/write] [updateItemPropertyValue] value is the same as most recent record for property: ${propertyNameFromDb}`);
|
|
9086
9261
|
return;
|
|
9087
9262
|
}
|
|
9088
9263
|
// This means we already have a local-only record so we should just update that one
|
|
@@ -9183,9 +9358,9 @@ const analyzeInput = xstate.fromCallback(({ sendBack, input: { context, event }
|
|
|
9183
9358
|
}
|
|
9184
9359
|
const _analyzeInput = async () => {
|
|
9185
9360
|
// Use dynamic import to break circular dependency
|
|
9186
|
-
const schemaMod = await Promise.resolve().then(function () { return require('./index-
|
|
9361
|
+
const schemaMod = await Promise.resolve().then(function () { return require('./index-BHaWOwNF.js'); });
|
|
9187
9362
|
const { ModelPropertyDataTypes } = schemaMod;
|
|
9188
|
-
const { SchemaValidationService } = await Promise.resolve().then(function () { return require('./SchemaValidationService-
|
|
9363
|
+
const { SchemaValidationService } = await Promise.resolve().then(function () { return require('./SchemaValidationService-CSXxx7sQ.js'); });
|
|
9189
9364
|
let propertyName = propertyNameRaw;
|
|
9190
9365
|
if (!propertyName) {
|
|
9191
9366
|
throw new Error('propertyName is required');
|
|
@@ -9446,7 +9621,11 @@ function waitForTurn(previous, name, timeoutMs) {
|
|
|
9446
9621
|
return Promise.race([previous, timedOut]).finally(() => clearTimeout(timer));
|
|
9447
9622
|
}
|
|
9448
9623
|
|
|
9449
|
-
const logger$
|
|
9624
|
+
const logger$i = debug('seedSdk:helpers:tabCoordinator');
|
|
9625
|
+
/** Identifies this tab (this page's SDK instance) in the messages it sends. */
|
|
9626
|
+
const TAB_ID = typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
|
|
9627
|
+
? crypto.randomUUID()
|
|
9628
|
+
: `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
9450
9629
|
let state;
|
|
9451
9630
|
let leader = false;
|
|
9452
9631
|
let leaderWaiters = [];
|
|
@@ -9465,6 +9644,10 @@ function startTabCoordination({ filesDir, mode = 'coordinate' }) {
|
|
|
9465
9644
|
}
|
|
9466
9645
|
const channel = new BroadcastChannel(`seed:tabs:${dbKey}`);
|
|
9467
9646
|
channel.onmessage = (event) => {
|
|
9647
|
+
// A BroadcastChannel never hears itself, but another channel object in this page would (a
|
|
9648
|
+
// second channel opened by a test, or the same module loaded twice): skip our own messages.
|
|
9649
|
+
if (event.data?.from === TAB_ID)
|
|
9650
|
+
return;
|
|
9468
9651
|
for (const handler of messageHandlers)
|
|
9469
9652
|
handler(event.data);
|
|
9470
9653
|
};
|
|
@@ -9485,7 +9668,7 @@ function becomeLeader() {
|
|
|
9485
9668
|
if (leader)
|
|
9486
9669
|
return;
|
|
9487
9670
|
leader = true;
|
|
9488
|
-
logger$
|
|
9671
|
+
logger$i(`this tab leads ${state?.dbKey}`);
|
|
9489
9672
|
const waiters = leaderWaiters;
|
|
9490
9673
|
leaderWaiters = [];
|
|
9491
9674
|
for (const resolve of waiters)
|
|
@@ -9502,7 +9685,7 @@ function whenLeaderTab() {
|
|
|
9502
9685
|
}
|
|
9503
9686
|
/** Sends to the other tabs of this database; a no-op when not coordinating. */
|
|
9504
9687
|
function postTabMessage(message) {
|
|
9505
|
-
state?.channel?.postMessage(message);
|
|
9688
|
+
state?.channel?.postMessage({ ...message, from: TAB_ID });
|
|
9506
9689
|
}
|
|
9507
9690
|
function onTabMessage(handler) {
|
|
9508
9691
|
messageHandlers.add(handler);
|
|
@@ -9513,7 +9696,7 @@ function withDbTabLock(scope, fn, options) {
|
|
|
9513
9696
|
return state ? withTabLock(`seed:${scope}:${state.dbKey}`, fn, options) : fn();
|
|
9514
9697
|
}
|
|
9515
9698
|
|
|
9516
|
-
const logger$
|
|
9699
|
+
const logger$h = debug('seedSdk:helpers:tabEvents');
|
|
9517
9700
|
/**
|
|
9518
9701
|
* Keeps other tabs current (docs/MULTI_TAB.md). The event bus is per tab, so events that mean "the
|
|
9519
9702
|
* shared database or OPFS changed" are also posted to the other tabs, which refresh their caches
|
|
@@ -9533,12 +9716,11 @@ function emitAcrossTabs(name, payload) {
|
|
|
9533
9716
|
postTabMessage({ type: 'event', name, payload });
|
|
9534
9717
|
}
|
|
9535
9718
|
/**
|
|
9536
|
-
* A file saved through this tab's file system: emits `file-saved` here
|
|
9537
|
-
*
|
|
9719
|
+
* A file saved through this tab's file system: emits `file-saved` here. Other tabs hear about it
|
|
9720
|
+
* from the file system itself (see queueFilesChangedNotice), once the write has reached OPFS.
|
|
9538
9721
|
*/
|
|
9539
9722
|
function notifyFileSaved(filePath) {
|
|
9540
9723
|
eventEmitter.emit('file-saved', filePath);
|
|
9541
|
-
postTabMessage({ type: 'files-changed', paths: [filePath] });
|
|
9542
9724
|
}
|
|
9543
9725
|
/** Refreshes this tab's in-memory state for an event another tab emitted, before re-emitting it. */
|
|
9544
9726
|
async function catchUpOn(name, payload) {
|
|
@@ -9566,7 +9748,7 @@ function listenForOtherTabsEvents() {
|
|
|
9566
9748
|
onTabMessage((message) => {
|
|
9567
9749
|
if (message.type === 'event' && RELAYED_EVENTS.has(message.name)) {
|
|
9568
9750
|
void catchUpOn(message.name, message.payload)
|
|
9569
|
-
.catch((error) => logger$
|
|
9751
|
+
.catch((error) => logger$h(`catching up on ${message.name} failed`, error))
|
|
9570
9752
|
.then(() => {
|
|
9571
9753
|
if (message.payload === undefined) {
|
|
9572
9754
|
eventEmitter.emit(message.name);
|
|
@@ -9577,8 +9759,9 @@ function listenForOtherTabsEvents() {
|
|
|
9577
9759
|
});
|
|
9578
9760
|
}
|
|
9579
9761
|
else if (message.type === 'files-changed') {
|
|
9762
|
+
// Only refreshes caches: never writes, so never sends files-changed back.
|
|
9580
9763
|
void BaseFileManager.invalidateCachedPaths(message.paths)
|
|
9581
|
-
.catch((error) => logger$
|
|
9764
|
+
.catch((error) => logger$h('refreshing changed files failed', error))
|
|
9582
9765
|
.then(() => eventEmitter.emit(FILES_CHANGED_EVENT, message.paths));
|
|
9583
9766
|
}
|
|
9584
9767
|
});
|
|
@@ -9717,7 +9900,7 @@ const createMetadata = async (metadataValues, propertyRecordSchema, options) =>
|
|
|
9717
9900
|
propertyRecordSchema?.validation &&
|
|
9718
9901
|
metadataValues.propertyValue != null &&
|
|
9719
9902
|
metadataValues.propertyValue !== '') {
|
|
9720
|
-
const { SchemaValidationService } = await Promise.resolve().then(function () { return require('./SchemaValidationService-
|
|
9903
|
+
const { SchemaValidationService } = await Promise.resolve().then(function () { return require('./SchemaValidationService-CSXxx7sQ.js'); });
|
|
9721
9904
|
const validationService = new SchemaValidationService();
|
|
9722
9905
|
const validationResult = validationService.validatePropertyValue(metadataValues.propertyValue, normalizeDataType(propertyRecordSchema.dataType), propertyRecordSchema.validation, propertyRecordSchema.refValueType
|
|
9723
9906
|
? normalizeDataType(String(propertyRecordSchema.refValueType))
|
|
@@ -10299,7 +10482,7 @@ const saveRelation = xstate.fromCallback(({ sendBack, input: { context, event }
|
|
|
10299
10482
|
}
|
|
10300
10483
|
const _saveRelation = async () => {
|
|
10301
10484
|
// Use dynamic import to break circular dependency
|
|
10302
|
-
const schemaMod = await Promise.resolve().then(function () { return require('./index-
|
|
10485
|
+
const schemaMod = await Promise.resolve().then(function () { return require('./index-BHaWOwNF.js'); });
|
|
10303
10486
|
const { ModelPropertyDataTypes } = schemaMod;
|
|
10304
10487
|
if (!propertyNameRaw) {
|
|
10305
10488
|
throw new Error('propertyName is required');
|
|
@@ -10390,6 +10573,86 @@ const getItemPropertyData = async (props) => {
|
|
|
10390
10573
|
return queryRows[0];
|
|
10391
10574
|
};
|
|
10392
10575
|
|
|
10576
|
+
/** Absolute path of the file {@link writeItemStorageFile} writes for `target`. */
|
|
10577
|
+
const getItemStorageFilePath = (target) => BaseFileManager.getFilesPath(getItemStorageSubdir(target.localStorageDir), target.fileName || getItemStorageLocalFileName(target.seedId, target.filenameSuffix));
|
|
10578
|
+
/**
|
|
10579
|
+
* Write an ItemStorage property's content to its local file, where saveItemStorage, reads, publish
|
|
10580
|
+
* (resolveItemStorageChildFile) and download all look for it. Returns what the metadata row records:
|
|
10581
|
+
* `refResolvedValue` (the file name) and `localStorageDir` (with a leading slash).
|
|
10582
|
+
*
|
|
10583
|
+
* Callers that also update the file's metadata row hold {@link withItemStorageFileLock} around
|
|
10584
|
+
* both, so another writer of the same file can't land in between.
|
|
10585
|
+
*/
|
|
10586
|
+
const writeItemStorageFile = async (target, content) => {
|
|
10587
|
+
const dir = getItemStorageSubdir(target.localStorageDir);
|
|
10588
|
+
const fileName = target.fileName || getItemStorageLocalFileName(target.seedId, target.filenameSuffix);
|
|
10589
|
+
await BaseFileManager.createDirIfNotExists(BaseFileManager.getFilesPath(dir));
|
|
10590
|
+
const filePath = BaseFileManager.getFilesPath(dir, fileName);
|
|
10591
|
+
await BaseFileManager.saveFile(filePath, content);
|
|
10592
|
+
return { fileName, filePath, localStorageDir: target.localStorageDir || `/${dir}` };
|
|
10593
|
+
};
|
|
10594
|
+
/**
|
|
10595
|
+
* Lock name for one ItemStorage file: `seed:item-storage:<absolute file path>`. The path starts
|
|
10596
|
+
* with the files root, which also names the database (`<filesDir>/db/seed.db`), so the name is
|
|
10597
|
+
* per database as well as per file.
|
|
10598
|
+
*/
|
|
10599
|
+
const itemStorageFileLockName = (filePath) => `seed:item-storage:${filePath}`;
|
|
10600
|
+
/**
|
|
10601
|
+
* Runs `fn` holding the lock of one ItemStorage file, across tabs (Web Locks) and within this JS
|
|
10602
|
+
* realm (an in-process queue where Web Locks are missing, e.g. Node < 24.5; see `withTabLock`).
|
|
10603
|
+
*
|
|
10604
|
+
* Writers of a local draft's file (saveItemStorage, convertVerbatimItemStorageRow, createNewItem)
|
|
10605
|
+
* hold it across "read the row → write the file → update the row", so the file and the row end
|
|
10606
|
+
* with the same writer's content. See docs/MULTI_TAB.md.
|
|
10607
|
+
*
|
|
10608
|
+
* Lock ordering: this lock is always the innermost. It may be taken while holding a broader lock
|
|
10609
|
+
* (`seed:item-storage-migration:<db>`, `seed:publish:<seedLocalId>`), never the other way round,
|
|
10610
|
+
* and no other lock (nor a second file lock) is requested while it is held. Keep the section to
|
|
10611
|
+
* local DB and file I/O: no network. Not re-entrant.
|
|
10612
|
+
*/
|
|
10613
|
+
function withItemStorageFileLock(filePath, fn) {
|
|
10614
|
+
return withTabLock(itemStorageFileLockName(filePath), fn);
|
|
10615
|
+
}
|
|
10616
|
+
/** Lock attempts before giving up when the row's target file keeps changing under us. */
|
|
10617
|
+
const ROW_LOCK_ATTEMPTS = 3;
|
|
10618
|
+
const readRow = async (localId) => {
|
|
10619
|
+
const rows = await BaseDb.getAppDb()
|
|
10620
|
+
.select()
|
|
10621
|
+
.from(metadata$1)
|
|
10622
|
+
.where(drizzleOrm.eq(metadata$1.localId, localId))
|
|
10623
|
+
.limit(1);
|
|
10624
|
+
return rows[0];
|
|
10625
|
+
};
|
|
10626
|
+
/**
|
|
10627
|
+
* Runs `fn` with a fresh read of metadata row `localId`, holding the lock of the file that row's
|
|
10628
|
+
* content goes to. `fileOf` names that file (absolute path) for a row, or returns undefined when
|
|
10629
|
+
* there is nothing to write (then `fn` isn't called and the result is undefined).
|
|
10630
|
+
*
|
|
10631
|
+
* The file depends on the row, so the row is read once to pick the lock and again once it is held;
|
|
10632
|
+
* if the row now points at another file (another writer changed it meanwhile), the lock is
|
|
10633
|
+
* released and taken again for that file.
|
|
10634
|
+
*/
|
|
10635
|
+
async function withLockedItemStorageRow(localId, fileOf, fn) {
|
|
10636
|
+
for (let attempt = 0; attempt < ROW_LOCK_ATTEMPTS; attempt++) {
|
|
10637
|
+
const row = await readRow(localId);
|
|
10638
|
+
const filePath = row && fileOf(row);
|
|
10639
|
+
if (!filePath)
|
|
10640
|
+
return undefined;
|
|
10641
|
+
const outcome = await withItemStorageFileLock(filePath, async () => {
|
|
10642
|
+
const fresh = await readRow(localId);
|
|
10643
|
+
const freshPath = fresh && fileOf(fresh);
|
|
10644
|
+
if (!freshPath)
|
|
10645
|
+
return { done: true, value: undefined };
|
|
10646
|
+
if (freshPath !== filePath)
|
|
10647
|
+
return { done: false };
|
|
10648
|
+
return { done: true, value: await fn(fresh, filePath) };
|
|
10649
|
+
});
|
|
10650
|
+
if (outcome.done)
|
|
10651
|
+
return outcome.value;
|
|
10652
|
+
}
|
|
10653
|
+
throw new Error(`ItemStorage file of metadata row ${localId} kept changing; not written`);
|
|
10654
|
+
}
|
|
10655
|
+
|
|
10393
10656
|
/** A local edit: no attestation uid of its own and not derived from one. */
|
|
10394
10657
|
const isLocalDraft = (row) => !row.uid && row.derivedFromUid == null;
|
|
10395
10658
|
const saveItemStorage = xstate.fromCallback(({ sendBack, input: { context, event } }) => {
|
|
@@ -10443,22 +10706,25 @@ const saveItemStorage = xstate.fromCallback(({ sendBack, input: { context, event
|
|
|
10443
10706
|
const latest = [...queryRows].sort(compareMetadataRowsLatestFirst)[0];
|
|
10444
10707
|
propertyData = latest && isLocalDraft(latest) ? latest : undefined;
|
|
10445
10708
|
if (!propertyData && newValue) {
|
|
10446
|
-
const
|
|
10447
|
-
|
|
10448
|
-
await BaseFileManager.createDirIfNotExists(BaseFileManager.getFilesPath(dir));
|
|
10449
|
-
const writeToPath = BaseFileManager.getFilesPath(dir, filename);
|
|
10450
|
-
await BaseFileManager.saveFile(writeToPath, newValue);
|
|
10451
|
-
propertyData = await createMetadata({
|
|
10452
|
-
propertyName,
|
|
10453
|
-
propertyValue: filename,
|
|
10454
|
-
modelType: (modelName ?? knownRow?.modelType ?? '').toLowerCase() || undefined,
|
|
10455
|
-
seedLocalId: draftSeedLocalId,
|
|
10456
|
-
seedUid: draftSeedUid,
|
|
10457
|
-
versionLocalId,
|
|
10458
|
-
versionUid,
|
|
10709
|
+
const target = {
|
|
10710
|
+
seedId: draftSeedUid || draftSeedLocalId,
|
|
10459
10711
|
localStorageDir: propertyRecordSchema.localStorageDir,
|
|
10460
|
-
|
|
10461
|
-
}
|
|
10712
|
+
filenameSuffix: propertyRecordSchema.filenameSuffix,
|
|
10713
|
+
};
|
|
10714
|
+
propertyData = await withItemStorageFileLock(getItemStorageFilePath(target), async () => {
|
|
10715
|
+
const { fileName: filename } = await writeItemStorageFile(target, newValue);
|
|
10716
|
+
return createMetadata({
|
|
10717
|
+
propertyName,
|
|
10718
|
+
propertyValue: filename,
|
|
10719
|
+
modelType: (modelName ?? knownRow?.modelType ?? '').toLowerCase() || undefined,
|
|
10720
|
+
seedLocalId: draftSeedLocalId,
|
|
10721
|
+
seedUid: draftSeedUid,
|
|
10722
|
+
versionLocalId,
|
|
10723
|
+
versionUid,
|
|
10724
|
+
localStorageDir: propertyRecordSchema.localStorageDir,
|
|
10725
|
+
refValueType: 'file',
|
|
10726
|
+
}, propertyRecordSchema);
|
|
10727
|
+
});
|
|
10462
10728
|
}
|
|
10463
10729
|
}
|
|
10464
10730
|
else if (propertyData && !isLocalDraft(propertyData)) {
|
|
@@ -10467,31 +10733,64 @@ const saveItemStorage = xstate.fromCallback(({ sendBack, input: { context, event
|
|
|
10467
10733
|
if (!propertyData) {
|
|
10468
10734
|
throw new Error(`No metadata row to save ${propertyName} to`);
|
|
10469
10735
|
}
|
|
10470
|
-
|
|
10471
|
-
const
|
|
10472
|
-
|
|
10473
|
-
|
|
10474
|
-
|
|
10475
|
-
|
|
10476
|
-
|
|
10477
|
-
|
|
10478
|
-
|
|
10479
|
-
|
|
10480
|
-
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
10484
|
-
|
|
10485
|
-
|
|
10486
|
-
|
|
10487
|
-
|
|
10488
|
-
|
|
10489
|
-
|
|
10490
|
-
|
|
10491
|
-
|
|
10736
|
+
// No storage.path: the `files` directory, as resolveItemStorageChildFile and download assume.
|
|
10737
|
+
const localStorageDirOf = (row) => propertyRecordSchema.localStorageDir || row.localStorageDir || '/files';
|
|
10738
|
+
const fileNameOf = (row) => {
|
|
10739
|
+
if (row.refResolvedValue)
|
|
10740
|
+
return row.refResolvedValue;
|
|
10741
|
+
const seedId = row.seedUid || row.seedLocalId;
|
|
10742
|
+
return seedId ? getItemStorageLocalFileName(seedId, propertyRecordSchema.filenameSuffix) : undefined;
|
|
10743
|
+
};
|
|
10744
|
+
if (!fileNameOf(propertyData)) {
|
|
10745
|
+
throw new Error(`Missing a seed id to name the ${propertyName} file after`);
|
|
10746
|
+
}
|
|
10747
|
+
// Read the row, write the file and update the row under the file's lock, so a conversion of a
|
|
10748
|
+
// legacy row (migration, publish) or another save of this file, in this tab or another, can't
|
|
10749
|
+
// land in between and leave the file with older content than the row says.
|
|
10750
|
+
const saved = await withLockedItemStorageRow(propertyData.localId, (row) => {
|
|
10751
|
+
const fileName = fileNameOf(row);
|
|
10752
|
+
return fileName && BaseFileManager.getFilesPath(getItemStorageSubdir(localStorageDirOf(row)), fileName);
|
|
10753
|
+
}, async (row, filePath) => {
|
|
10754
|
+
const localStorageDir = localStorageDirOf(row);
|
|
10755
|
+
const fileName = fileNameOf(row);
|
|
10756
|
+
try {
|
|
10757
|
+
await writeItemStorageFile({ seedId: row.seedUid || row.seedLocalId || '', localStorageDir, fileName }, newValue);
|
|
10758
|
+
}
|
|
10759
|
+
catch (error) {
|
|
10760
|
+
const fs = await BaseFileManager.getFs();
|
|
10761
|
+
fs.writeFileSync(filePath, newValue);
|
|
10762
|
+
}
|
|
10763
|
+
// A legacy row (made before Item.create saved ItemStorage values) holds the old content itself
|
|
10764
|
+
// in propertyValue. The file is the content now: drop it, or a fresh load shows the old text
|
|
10765
|
+
// until the file is read. A row whose propertyValue is the file name keeps it.
|
|
10766
|
+
const clearsVerbatimValue = !row.refResolvedValue &&
|
|
10767
|
+
row.propertyValue != null &&
|
|
10768
|
+
row.propertyValue !== '' &&
|
|
10769
|
+
row.propertyValue !== fileName;
|
|
10770
|
+
// The row says where the file is (read back by hydration, the browser's metadata live query
|
|
10771
|
+
// and publish); without localStorageDir the live query would clear it from the context.
|
|
10772
|
+
await appDb
|
|
10773
|
+
.update(metadata$1)
|
|
10774
|
+
.set({
|
|
10775
|
+
refResolvedValue: fileName,
|
|
10776
|
+
localStorageDir,
|
|
10777
|
+
// Same shape as a row the migration or publish converted (convertVerbatimItemStorageRow).
|
|
10778
|
+
...(clearsVerbatimValue && { propertyValue: null, refValueType: 'file' }),
|
|
10779
|
+
})
|
|
10780
|
+
.where(drizzleOrm.eq(metadata$1.localId, row.localId));
|
|
10781
|
+
return { fileName, localStorageDir };
|
|
10782
|
+
});
|
|
10783
|
+
// The row went away (or lost its seed ids) before the lock was granted.
|
|
10784
|
+
if (!saved)
|
|
10785
|
+
throw new Error(`No metadata row to save ${propertyName} to`);
|
|
10786
|
+
const { fileName, localStorageDir } = saved;
|
|
10492
10787
|
sendBack({
|
|
10493
10788
|
type: 'updateContext',
|
|
10494
10789
|
renderValue: newValue,
|
|
10790
|
+
// Where the file is, right away: localStoragePath (and so publish uploads) must not wait for
|
|
10791
|
+
// the browser's metadata live query, which Node does not have at all.
|
|
10792
|
+
refResolvedValue: fileName,
|
|
10793
|
+
localStorageDir,
|
|
10495
10794
|
// Point the property at the draft row when the edit moved off a published row.
|
|
10496
10795
|
...(propertyData.localId !== localId && { localId: propertyData.localId }),
|
|
10497
10796
|
});
|
|
@@ -11150,7 +11449,7 @@ hints = {}) => {
|
|
|
11150
11449
|
};
|
|
11151
11450
|
/** Convert modelType (snake_case from DB) to Model name (PascalCase). startCase adds spaces ("Test Post"); Model names are "TestPost". */
|
|
11152
11451
|
const modelTypeToModelName = (modelType) => modelType ? lodashEs.upperFirst(lodashEs.camelCase(modelType)) : '';
|
|
11153
|
-
const logger$
|
|
11452
|
+
const logger$g = debug('seedSdk:property:class');
|
|
11154
11453
|
// Define tracked properties for the Proxy
|
|
11155
11454
|
// These properties will be read from/written to the actor context
|
|
11156
11455
|
const TRACKED_PROPERTIES$2 = [
|
|
@@ -11242,7 +11541,7 @@ class ItemProperty {
|
|
|
11242
11541
|
serviceInput.propertyValue = JSON.parse(propertyValue);
|
|
11243
11542
|
}
|
|
11244
11543
|
catch (e) {
|
|
11245
|
-
logger$
|
|
11544
|
+
logger$g('List property value is not JSON', e);
|
|
11246
11545
|
}
|
|
11247
11546
|
}
|
|
11248
11547
|
}
|
|
@@ -11265,7 +11564,7 @@ class ItemProperty {
|
|
|
11265
11564
|
return;
|
|
11266
11565
|
}
|
|
11267
11566
|
// Use dynamic import to break circular dependency
|
|
11268
|
-
const schemaMod = await Promise.resolve().then(function () { return require('./index-
|
|
11567
|
+
const schemaMod = await Promise.resolve().then(function () { return require('./index-BHaWOwNF.js'); });
|
|
11269
11568
|
const { ModelPropertyDataTypes } = schemaMod;
|
|
11270
11569
|
const { context } = snapshot;
|
|
11271
11570
|
const { propertyRecordSchema } = context;
|
|
@@ -11359,7 +11658,7 @@ class ItemProperty {
|
|
|
11359
11658
|
}
|
|
11360
11659
|
}
|
|
11361
11660
|
catch (e) {
|
|
11362
|
-
logger$
|
|
11661
|
+
logger$g(`[ItemProperty] [${context.seedLocalId}] [${context.propertyName}] [storageType] error`, e);
|
|
11363
11662
|
renderValue = isHtml ? undefined : 'No file found';
|
|
11364
11663
|
}
|
|
11365
11664
|
}
|
|
@@ -12425,13 +12724,38 @@ const createNewItem = async ({ modelName, modelFileId, ...propertyData }) => {
|
|
|
12425
12724
|
for (const propertyName of allPropertyNames) {
|
|
12426
12725
|
const propertyValue = propertyData[propertyName];
|
|
12427
12726
|
const propertyRecordSchema = propertySchemas?.[propertyName];
|
|
12428
|
-
|
|
12727
|
+
// ItemStorage content lives in the property's file (`<seedLocalId><extension>`), as a save puts
|
|
12728
|
+
// it; the row records where. Stored verbatim in the row, publish would never upload it.
|
|
12729
|
+
const isItemStorageValue = propertyRecordSchema?.storageType === 'ItemStorage' &&
|
|
12730
|
+
propertyValue != null &&
|
|
12731
|
+
propertyValue !== '';
|
|
12732
|
+
const insertRow = (storageFields) => createMetadata({
|
|
12429
12733
|
seedLocalId: newSeedId,
|
|
12430
12734
|
versionLocalId: newVersionId,
|
|
12431
12735
|
propertyName,
|
|
12432
|
-
propertyValue,
|
|
12736
|
+
propertyValue: storageFields ? undefined : propertyValue,
|
|
12737
|
+
...storageFields,
|
|
12433
12738
|
modelName,
|
|
12434
12739
|
}, propertyRecordSchema, { skipValidation: true, modelFileId });
|
|
12740
|
+
if (!isItemStorageValue) {
|
|
12741
|
+
await insertRow();
|
|
12742
|
+
continue;
|
|
12743
|
+
}
|
|
12744
|
+
// File and row under the file's lock, like every writer of a local draft's file (see
|
|
12745
|
+
// withItemStorageFileLock). The seed is new, so nothing else should be writing it yet.
|
|
12746
|
+
const target = {
|
|
12747
|
+
seedId: newSeedId,
|
|
12748
|
+
localStorageDir: propertyRecordSchema.localStorageDir,
|
|
12749
|
+
filenameSuffix: propertyRecordSchema.filenameSuffix,
|
|
12750
|
+
};
|
|
12751
|
+
await withItemStorageFileLock(getItemStorageFilePath(target), async () => {
|
|
12752
|
+
const written = await writeItemStorageFile(target, propertyValue);
|
|
12753
|
+
await insertRow({
|
|
12754
|
+
refResolvedValue: written.fileName,
|
|
12755
|
+
localStorageDir: written.localStorageDir,
|
|
12756
|
+
refValueType: 'file',
|
|
12757
|
+
});
|
|
12758
|
+
});
|
|
12435
12759
|
}
|
|
12436
12760
|
return {
|
|
12437
12761
|
modelName,
|
|
@@ -12593,10 +12917,16 @@ const getModel = () => {
|
|
|
12593
12917
|
};
|
|
12594
12918
|
// Define tracked properties for the Proxy
|
|
12595
12919
|
// These properties will be read from/written to the actor context
|
|
12596
|
-
/**
|
|
12597
|
-
|
|
12920
|
+
/**
|
|
12921
|
+
* Initial values that need ItemProperty.save() rather than a verbatim metadata row: raw File/Image/Html
|
|
12922
|
+
* content (not already a storage seed reference) becomes a storage seed, and an ItemStorage property's
|
|
12923
|
+
* content goes into its file (`<seedLocalId><extension>`), where publish and reads look for it.
|
|
12924
|
+
*/
|
|
12925
|
+
function needsStorageSavePipeline(dataType, value, storageType) {
|
|
12598
12926
|
if (value == null || value === '')
|
|
12599
12927
|
return false;
|
|
12928
|
+
if (storageType === 'ItemStorage')
|
|
12929
|
+
return true;
|
|
12600
12930
|
const isStorageType = isDataType(dataType, exports.ModelPropertyDataTypes.File) ||
|
|
12601
12931
|
isDataType(dataType, exports.ModelPropertyDataTypes.Image) ||
|
|
12602
12932
|
isDataType(dataType, exports.ModelPropertyDataTypes.Html);
|
|
@@ -12709,12 +13039,12 @@ class Item {
|
|
|
12709
13039
|
};
|
|
12710
13040
|
this.getPublishUploads = async (options) => {
|
|
12711
13041
|
// Use dynamic import to break circular dependency
|
|
12712
|
-
const getPublishUploadsMod = await Promise.resolve().then(function () { return require('./getPublishUploads-
|
|
13042
|
+
const getPublishUploadsMod = await Promise.resolve().then(function () { return require('./getPublishUploads-DsXY9S4U.js'); });
|
|
12713
13043
|
const { getPublishUploads } = getPublishUploadsMod;
|
|
12714
13044
|
return await getPublishUploads(this, [], undefined, options);
|
|
12715
13045
|
};
|
|
12716
13046
|
this.getPublishPayload = async (uploadedTransactions, options) => {
|
|
12717
|
-
const getPublishPayloadMod = await Promise.resolve().then(function () { return require('./getPublishPayload-
|
|
13047
|
+
const getPublishPayloadMod = await Promise.resolve().then(function () { return require('./getPublishPayload-CqlqRdot.js'); });
|
|
12718
13048
|
const { getPublishPayload } = getPublishPayloadMod;
|
|
12719
13049
|
return await getPublishPayload(this, uploadedTransactions, options);
|
|
12720
13050
|
};
|
|
@@ -13244,6 +13574,7 @@ class Item {
|
|
|
13244
13574
|
// Get property names directly from database to make Item independent from Model
|
|
13245
13575
|
let propertyNames = [];
|
|
13246
13576
|
const dataTypeByPropertyName = new Map();
|
|
13577
|
+
const storageTypeByPropertyName = new Map();
|
|
13247
13578
|
const db = BaseDb.getAppDb();
|
|
13248
13579
|
if (db && props.modelName) {
|
|
13249
13580
|
// Model names are only unique per schema: resolve the exact models row from the most specific scope.
|
|
@@ -13273,13 +13604,19 @@ class Item {
|
|
|
13273
13604
|
}
|
|
13274
13605
|
if (modelRecords.length > 0 && modelRecords[0].id) {
|
|
13275
13606
|
const propertyRecords = await db
|
|
13276
|
-
.select({
|
|
13607
|
+
.select({
|
|
13608
|
+
name: properties.name,
|
|
13609
|
+
dataType: properties.dataType,
|
|
13610
|
+
storageType: properties.storageType,
|
|
13611
|
+
})
|
|
13277
13612
|
.from(properties)
|
|
13278
13613
|
.where(drizzleOrm.eq(properties.modelId, modelRecords[0].id));
|
|
13279
13614
|
propertyNames = propertyRecords.map((r) => r.name).filter((name) => Boolean(name));
|
|
13280
13615
|
for (const r of propertyRecords) {
|
|
13281
13616
|
if (r.name && r.dataType)
|
|
13282
13617
|
dataTypeByPropertyName.set(r.name, r.dataType);
|
|
13618
|
+
if (r.name && r.storageType)
|
|
13619
|
+
storageTypeByPropertyName.set(r.name, r.storageType);
|
|
13283
13620
|
}
|
|
13284
13621
|
}
|
|
13285
13622
|
}
|
|
@@ -13288,7 +13625,8 @@ class Item {
|
|
|
13288
13625
|
modelFileId: props.modelFileId,
|
|
13289
13626
|
};
|
|
13290
13627
|
// File/Image/Html values that are raw content (html string, data URI, URL, File) must go through
|
|
13291
|
-
// the property's save pipeline to become storage seeds
|
|
13628
|
+
// the property's save pipeline to become storage seeds, and ItemStorage values to be written to
|
|
13629
|
+
// their file with the property context pointing at it (as `prop.value = v; await prop.save()`).
|
|
13292
13630
|
const pipelineValues = [];
|
|
13293
13631
|
// Only include properties that are in the model schema
|
|
13294
13632
|
// Exclude modelInstance, modelName, and schemaName as they're metadata, not item properties
|
|
@@ -13298,7 +13636,7 @@ class Item {
|
|
|
13298
13636
|
continue;
|
|
13299
13637
|
}
|
|
13300
13638
|
if (propertyNames.length === 0 || propertyNames.includes(key)) {
|
|
13301
|
-
if (needsStorageSavePipeline(dataTypeByPropertyName.get(key), value)) {
|
|
13639
|
+
if (needsStorageSavePipeline(dataTypeByPropertyName.get(key), value, storageTypeByPropertyName.get(key))) {
|
|
13302
13640
|
pipelineValues.push([key, value]);
|
|
13303
13641
|
continue;
|
|
13304
13642
|
}
|
|
@@ -13430,7 +13768,7 @@ class Item {
|
|
|
13430
13768
|
return proxiedInstance;
|
|
13431
13769
|
}
|
|
13432
13770
|
/**
|
|
13433
|
-
* Run each File/Image/Html initial value through its ItemProperty save pipeline (sequentially, to
|
|
13771
|
+
* Run each File/Image/Html/ItemStorage initial value through its ItemProperty save pipeline (sequentially, to
|
|
13434
13772
|
* avoid SQLite write contention). Rejects on the first failure; the item itself has been created.
|
|
13435
13773
|
*/
|
|
13436
13774
|
static async savePipelineValues(item, pipelineValues, timeout) {
|
|
@@ -14520,7 +14858,7 @@ const runEasSync = xstate.fromPromise(async ({ input, }) => {
|
|
|
14520
14858
|
requestPayload: input.requestPayload,
|
|
14521
14859
|
persistedSnapshot: input.runningSnapshot,
|
|
14522
14860
|
});
|
|
14523
|
-
const { runSyncFromEas } = await Promise.resolve().then(function () { return require('./syncDbWithEas-
|
|
14861
|
+
const { runSyncFromEas } = await Promise.resolve().then(function () { return require('./syncDbWithEas-CrmxxmUW.js'); });
|
|
14524
14862
|
try {
|
|
14525
14863
|
// Sync checks for rows and then inserts the rest (no unique index on uids), so syncs in
|
|
14526
14864
|
// different tabs take turns. A sync can run for minutes, so wait without a timeout.
|
|
@@ -14696,7 +15034,7 @@ function newCorrelationId() {
|
|
|
14696
15034
|
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
14697
15035
|
}
|
|
14698
15036
|
/** Requests deferred in this tab until it leads, by source; each runs once on takeover. */
|
|
14699
|
-
const deferredUntilLeader = new Set();
|
|
15037
|
+
const deferredUntilLeader$1 = new Set();
|
|
14700
15038
|
/**
|
|
14701
15039
|
* Automatic syncs run in the leader tab only (docs/MULTI_TAB.md). A tab that skips one runs it if
|
|
14702
15040
|
* it later becomes leader, so closing the leader doesn't lose the catch-up sync.
|
|
@@ -14707,11 +15045,11 @@ function runInLeaderTab(source, request) {
|
|
|
14707
15045
|
return;
|
|
14708
15046
|
}
|
|
14709
15047
|
log(`${source} sync skipped: another tab leads`);
|
|
14710
|
-
if (deferredUntilLeader.has(source))
|
|
15048
|
+
if (deferredUntilLeader$1.has(source))
|
|
14711
15049
|
return;
|
|
14712
|
-
deferredUntilLeader.add(source);
|
|
15050
|
+
deferredUntilLeader$1.add(source);
|
|
14713
15051
|
void whenLeaderTab().then(() => {
|
|
14714
|
-
deferredUntilLeader.delete(source);
|
|
15052
|
+
deferredUntilLeader$1.delete(source);
|
|
14715
15053
|
request();
|
|
14716
15054
|
});
|
|
14717
15055
|
}
|
|
@@ -14785,10 +15123,10 @@ var easSyncManager = /*#__PURE__*/Object.freeze({
|
|
|
14785
15123
|
startEasSyncActor: startEasSyncActor
|
|
14786
15124
|
});
|
|
14787
15125
|
|
|
14788
|
-
const logger$
|
|
15126
|
+
const logger$f = debug('seedSdk:services:events');
|
|
14789
15127
|
const handleServiceSaveState = (event) => {
|
|
14790
15128
|
const { state, serviceId } = event;
|
|
14791
|
-
logger$
|
|
15129
|
+
logger$f(`[browser] [service.saveState.request] serviceId: ${serviceId}`);
|
|
14792
15130
|
// TODO: This is called from Node as well. Do we need it? If so, need to generalize this to all platforms.
|
|
14793
15131
|
// localStorage.setItem(`seed_sdk_service_${serviceId}`, JSON.stringify(state))
|
|
14794
15132
|
};
|
|
@@ -14897,9 +15235,9 @@ function normalizeSeedConfigEndpoints(config) {
|
|
|
14897
15235
|
return { ...config, endpoints: next };
|
|
14898
15236
|
}
|
|
14899
15237
|
|
|
14900
|
-
const logger$
|
|
15238
|
+
const logger$e = debug('seedSdk:ClientManager:initialize');
|
|
14901
15239
|
const platformClassesInit = xstate.fromCallback(({ sendBack, input: { context, event } }) => {
|
|
14902
|
-
logger$
|
|
15240
|
+
logger$e('initialize from ClientManager');
|
|
14903
15241
|
const { isInitialized } = context;
|
|
14904
15242
|
if (!event || !('options' in event)) {
|
|
14905
15243
|
throw new Error('Initialize event must include options');
|
|
@@ -15071,7 +15409,7 @@ const platformClassesInit = xstate.fromCallback(({ sendBack, input: { context, e
|
|
|
15071
15409
|
};
|
|
15072
15410
|
});
|
|
15073
15411
|
|
|
15074
|
-
const logger$
|
|
15412
|
+
const logger$d = debug('seedSdk:client:writeToDb');
|
|
15075
15413
|
const saveAppState = xstate.fromCallback(({ sendBack, input: { key, value } }) => {
|
|
15076
15414
|
const _saveAppState = async () => {
|
|
15077
15415
|
try {
|
|
@@ -15079,7 +15417,7 @@ const saveAppState = xstate.fromCallback(({ sendBack, input: { key, value } }) =
|
|
|
15079
15417
|
if (!appDb) {
|
|
15080
15418
|
// In test environments, continue anyway
|
|
15081
15419
|
if (process.env.NODE_ENV === 'test' || process.env.IS_SEED_DEV) {
|
|
15082
|
-
logger$
|
|
15420
|
+
logger$d('[client/actors] [saveAppState] App DB not found, but continuing in test environment');
|
|
15083
15421
|
return;
|
|
15084
15422
|
}
|
|
15085
15423
|
throw new Error('App DB not found');
|
|
@@ -15095,13 +15433,13 @@ const saveAppState = xstate.fromCallback(({ sendBack, input: { key, value } }) =
|
|
|
15095
15433
|
value: JSON.stringify(value),
|
|
15096
15434
|
},
|
|
15097
15435
|
});
|
|
15098
|
-
logger$
|
|
15436
|
+
logger$d('result', result);
|
|
15099
15437
|
}
|
|
15100
15438
|
catch (error) {
|
|
15101
|
-
logger$
|
|
15439
|
+
logger$d('[client/actors] [saveAppState] Error saving app state:', error);
|
|
15102
15440
|
// In test environments, continue anyway
|
|
15103
15441
|
if (process.env.NODE_ENV === 'test' || process.env.IS_SEED_DEV) {
|
|
15104
|
-
logger$
|
|
15442
|
+
logger$d('[client/actors] [saveAppState] Continuing despite error in test environment');
|
|
15105
15443
|
return;
|
|
15106
15444
|
}
|
|
15107
15445
|
throw error;
|
|
@@ -15116,10 +15454,10 @@ const saveAppState = xstate.fromCallback(({ sendBack, input: { key, value } }) =
|
|
|
15116
15454
|
});
|
|
15117
15455
|
})
|
|
15118
15456
|
.catch((error) => {
|
|
15119
|
-
logger$
|
|
15457
|
+
logger$d('[client/actors] [saveAppState] Error in promise chain:', error);
|
|
15120
15458
|
// In test environments, still send success to allow state machine to progress
|
|
15121
15459
|
if (process.env.NODE_ENV === 'test' || process.env.IS_SEED_DEV) {
|
|
15122
|
-
logger$
|
|
15460
|
+
logger$d('[client/actors] [saveAppState] Sending success despite error in test environment');
|
|
15123
15461
|
sendBack({
|
|
15124
15462
|
type: 'saveAppStateSuccess',
|
|
15125
15463
|
key,
|
|
@@ -15189,7 +15527,7 @@ const fileSystemInit = xstate.fromCallback(({ sendBack, input: { context }, }) =
|
|
|
15189
15527
|
};
|
|
15190
15528
|
});
|
|
15191
15529
|
|
|
15192
|
-
const logger$
|
|
15530
|
+
const logger$c = debug('seedSdk:client:actors:dbInit');
|
|
15193
15531
|
const dbInit = xstate.fromCallback(({ sendBack, input: { context } }) => {
|
|
15194
15532
|
const _dbInit = async () => {
|
|
15195
15533
|
const { filesDir } = context;
|
|
@@ -15203,14 +15541,14 @@ const dbInit = xstate.fromCallback(({ sendBack, input: { context } }) => {
|
|
|
15203
15541
|
if (!appDb) {
|
|
15204
15542
|
throw new Error('Database not available after preparation');
|
|
15205
15543
|
}
|
|
15206
|
-
logger$
|
|
15544
|
+
logger$c('[client/actors] [dbInit] Database prepared and ready');
|
|
15207
15545
|
};
|
|
15208
15546
|
_dbInit()
|
|
15209
15547
|
.then(() => {
|
|
15210
15548
|
sendBack({ type: ClientManagerEvents.DB_READY });
|
|
15211
15549
|
})
|
|
15212
15550
|
.catch((error) => {
|
|
15213
|
-
logger$
|
|
15551
|
+
logger$c('Error in dbInit:', error);
|
|
15214
15552
|
sendBack({
|
|
15215
15553
|
type: 'error',
|
|
15216
15554
|
error: error instanceof Error ? error : new Error(String(error))
|
|
@@ -15218,7 +15556,7 @@ const dbInit = xstate.fromCallback(({ sendBack, input: { context } }) => {
|
|
|
15218
15556
|
});
|
|
15219
15557
|
});
|
|
15220
15558
|
|
|
15221
|
-
const logger$
|
|
15559
|
+
const logger$b = debug('seedSdk:client:actors:addModelsToStore');
|
|
15222
15560
|
const addModelsToStore = xstate.fromCallback(({ sendBack, input: { context } }) => {
|
|
15223
15561
|
const _addModelsToStore = async () => {
|
|
15224
15562
|
let { models } = context;
|
|
@@ -15232,7 +15570,7 @@ const addModelsToStore = xstate.fromCallback(({ sendBack, input: { context } })
|
|
|
15232
15570
|
// Models are now Model instances, no registration needed
|
|
15233
15571
|
// They should be created via Model.create() and are accessible via Model static methods
|
|
15234
15572
|
for (const [key] of Object.entries(allModels)) {
|
|
15235
|
-
logger$
|
|
15573
|
+
logger$b('model available:', key);
|
|
15236
15574
|
}
|
|
15237
15575
|
};
|
|
15238
15576
|
_addModelsToStore()
|
|
@@ -15240,7 +15578,7 @@ const addModelsToStore = xstate.fromCallback(({ sendBack, input: { context } })
|
|
|
15240
15578
|
sendBack({ type: ClientManagerEvents.ADD_MODELS_TO_STORE_SUCCESS });
|
|
15241
15579
|
})
|
|
15242
15580
|
.catch((error) => {
|
|
15243
|
-
logger$
|
|
15581
|
+
logger$b('[client/actors] [addModelsToStore] Error:', error);
|
|
15244
15582
|
sendBack({
|
|
15245
15583
|
type: 'error',
|
|
15246
15584
|
error: error instanceof Error ? error : new Error(String(error)),
|
|
@@ -15248,7 +15586,7 @@ const addModelsToStore = xstate.fromCallback(({ sendBack, input: { context } })
|
|
|
15248
15586
|
});
|
|
15249
15587
|
});
|
|
15250
15588
|
|
|
15251
|
-
const logger$
|
|
15589
|
+
const logger$a = debug('seedSdk:client:actors:addModelsToDb');
|
|
15252
15590
|
/**
|
|
15253
15591
|
* Returns every `models` row named in `modelNames`, inserting a null-schemaFileId stub for each name
|
|
15254
15592
|
* with no row yet (the schema import adopts stubs via findOrCreateModelRecord). Check-then-insert:
|
|
@@ -15271,7 +15609,7 @@ async function ensureModelStubs(appDb, modelNames, filesDir) {
|
|
|
15271
15609
|
.returning({ id: models$1.id, name: models$1.name, schemaFileId: models$1.schemaFileId }));
|
|
15272
15610
|
rows.push(...newlyInserted);
|
|
15273
15611
|
for (const name of modelsToInsert) {
|
|
15274
|
-
logger$
|
|
15612
|
+
logger$a('[client/actors] [addModelsToDb] inserted model:', name);
|
|
15275
15613
|
}
|
|
15276
15614
|
}
|
|
15277
15615
|
return rows;
|
|
@@ -15308,7 +15646,7 @@ const addModelsToDb = xstate.fromCallback(({ sendBack, input: { context } }) =>
|
|
|
15308
15646
|
for (const modelName of modelNames) {
|
|
15309
15647
|
const foundModel = rowFor(modelName);
|
|
15310
15648
|
if (!foundModel) {
|
|
15311
|
-
logger$
|
|
15649
|
+
logger$a('[client/actors] [addModelsToDb] Warning: Could not find or create model:', modelName);
|
|
15312
15650
|
hasModelsInDb = false;
|
|
15313
15651
|
continue;
|
|
15314
15652
|
}
|
|
@@ -15320,7 +15658,7 @@ const addModelsToDb = xstate.fromCallback(({ sendBack, input: { context } }) =>
|
|
|
15320
15658
|
}
|
|
15321
15659
|
// If we have no models to process, still send success (empty models is valid)
|
|
15322
15660
|
if (schemaDefsByModelName.size === 0) {
|
|
15323
|
-
logger$
|
|
15661
|
+
logger$a('[client/actors] [addModelsToDb] No models to process, but continuing');
|
|
15324
15662
|
sendBack({ type: ClientManagerEvents.ADD_MODELS_TO_DB_SUCCESS });
|
|
15325
15663
|
return;
|
|
15326
15664
|
}
|
|
@@ -15363,15 +15701,15 @@ const addModelsToDb = xstate.fromCallback(({ sendBack, input: { context } }) =>
|
|
|
15363
15701
|
requestEasSyncFromModelsInit();
|
|
15364
15702
|
}
|
|
15365
15703
|
else {
|
|
15366
|
-
logger$
|
|
15704
|
+
logger$a('[client/actors] [addModelsToDb] No schemas found from EAS, but continuing');
|
|
15367
15705
|
}
|
|
15368
15706
|
}
|
|
15369
15707
|
catch (error) {
|
|
15370
15708
|
if (process.env.NODE_ENV === 'test' || process.env.IS_SEED_DEV) {
|
|
15371
|
-
logger$
|
|
15709
|
+
logger$a('[client/actors] [addModelsToDb] Warning: Could not fetch schemas from EAS (background):', error.message);
|
|
15372
15710
|
}
|
|
15373
15711
|
else {
|
|
15374
|
-
logger$
|
|
15712
|
+
logger$a('[client/actors] [addModelsToDb] Error fetching schemas (background):', error.message);
|
|
15375
15713
|
}
|
|
15376
15714
|
}
|
|
15377
15715
|
};
|
|
@@ -15383,10 +15721,10 @@ const addModelsToDb = xstate.fromCallback(({ sendBack, input: { context } }) =>
|
|
|
15383
15721
|
sendBack({ type: ClientManagerEvents.ADD_MODELS_TO_DB_SUCCESS });
|
|
15384
15722
|
})
|
|
15385
15723
|
.catch((error) => {
|
|
15386
|
-
logger$
|
|
15724
|
+
logger$a('[client/actors] [addModelsToDb] Error:', error);
|
|
15387
15725
|
// In test environments, still send success to allow initialization to complete
|
|
15388
15726
|
if (process.env.NODE_ENV === 'test' || process.env.IS_SEED_DEV) {
|
|
15389
|
-
logger$
|
|
15727
|
+
logger$a('[client/actors] [addModelsToDb] Continuing despite error in test environment');
|
|
15390
15728
|
sendBack({ type: ClientManagerEvents.ADD_MODELS_TO_DB_SUCCESS });
|
|
15391
15729
|
}
|
|
15392
15730
|
else {
|
|
@@ -15399,7 +15737,7 @@ const addModelsToDb = xstate.fromCallback(({ sendBack, input: { context } }) =>
|
|
|
15399
15737
|
return () => { };
|
|
15400
15738
|
});
|
|
15401
15739
|
|
|
15402
|
-
const logger$
|
|
15740
|
+
const logger$9 = debug('seedSdk:helpers:localDbChain');
|
|
15403
15741
|
/** appState key recording which EAS chain the local database holds attestations from. */
|
|
15404
15742
|
const EAS_CHAIN_ID_APP_STATE_KEY = 'easChainId';
|
|
15405
15743
|
/**
|
|
@@ -15469,7 +15807,7 @@ async function loadLocalDbChain() {
|
|
|
15469
15807
|
const localChainId = await resolveLocalDbChainId(appDb);
|
|
15470
15808
|
if (localChainId !== undefined) {
|
|
15471
15809
|
eas.configureEasReadChain('localDb', { chainId: localChainId });
|
|
15472
|
-
logger$
|
|
15810
|
+
logger$9('local DB holds chain %d', localChainId);
|
|
15473
15811
|
return;
|
|
15474
15812
|
}
|
|
15475
15813
|
if (eas.isEasReadChainConfigured()) {
|
|
@@ -15495,7 +15833,7 @@ async function assertLocalDbChain() {
|
|
|
15495
15833
|
eas.configureEasReadChain('localDb', { chainId: localChainId ?? chainId });
|
|
15496
15834
|
}
|
|
15497
15835
|
|
|
15498
|
-
const logger$
|
|
15836
|
+
const logger$8 = debug('seedSdk:client:actors:saveConfig');
|
|
15499
15837
|
/**
|
|
15500
15838
|
* Saves the addresses passed to init. Hosts pass `addresses: []` before a wallet connects. In the
|
|
15501
15839
|
* first (leader) tab that clears stored addresses, as it always has, so drafts made before the
|
|
@@ -15515,13 +15853,13 @@ async function persistInitAddresses(appDb, { addresses, ownedAddresses, watchedA
|
|
|
15515
15853
|
.onConflictDoUpdate({ target: appState.key, set: { value } });
|
|
15516
15854
|
}
|
|
15517
15855
|
const saveConfig = xstate.fromCallback(({ sendBack, input: { context } }) => {
|
|
15518
|
-
logger$
|
|
15856
|
+
logger$8('saveConfig starting');
|
|
15519
15857
|
const { endpoints, addresses, ownedAddresses, watchedAddresses, arweaveDomain, uploadApiBaseUrl, gatewayTransport, gatewayHyperKey, gatewaySidecarHost, gatewaySidecarPort, gatewayProxyBaseUrl, } = context;
|
|
15520
15858
|
// Validate endpoints - required for proper initialization
|
|
15521
15859
|
// If endpoints are missing or invalid, initialization should fail
|
|
15522
15860
|
if (!endpoints || !endpoints.filePaths || !endpoints.files) {
|
|
15523
15861
|
const error = new Error('saveConfig called with invalid endpoints: endpoints must include both filePaths and files');
|
|
15524
|
-
logger$
|
|
15862
|
+
logger$8('[internal/actors] [saveConfig] Invalid endpoints:', { endpoints });
|
|
15525
15863
|
throw error;
|
|
15526
15864
|
}
|
|
15527
15865
|
const _saveConfig = async () => {
|
|
@@ -15530,7 +15868,7 @@ const saveConfig = xstate.fromCallback(({ sendBack, input: { context } }) => {
|
|
|
15530
15868
|
if (!appDb) {
|
|
15531
15869
|
// In test environments, continue anyway
|
|
15532
15870
|
if (process.env.NODE_ENV === 'test' || process.env.IS_SEED_DEV) {
|
|
15533
|
-
logger$
|
|
15871
|
+
logger$8('[internal/actors] [saveConfig] App DB not found, but continuing in test environment');
|
|
15534
15872
|
return;
|
|
15535
15873
|
}
|
|
15536
15874
|
throw new Error('App DB not found');
|
|
@@ -15585,10 +15923,10 @@ const saveConfig = xstate.fromCallback(({ sendBack, input: { context } }) => {
|
|
|
15585
15923
|
await loadLocalDbChain();
|
|
15586
15924
|
}
|
|
15587
15925
|
catch (error) {
|
|
15588
|
-
logger$
|
|
15926
|
+
logger$8('[internal/actors] [saveConfig] Error saving config:', error);
|
|
15589
15927
|
// In test environments, continue anyway
|
|
15590
15928
|
if (process.env.NODE_ENV === 'test' || process.env.IS_SEED_DEV) {
|
|
15591
|
-
logger$
|
|
15929
|
+
logger$8('[internal/actors] [saveConfig] Continuing despite error in test environment');
|
|
15592
15930
|
return;
|
|
15593
15931
|
}
|
|
15594
15932
|
throw error;
|
|
@@ -15597,14 +15935,14 @@ const saveConfig = xstate.fromCallback(({ sendBack, input: { context } }) => {
|
|
|
15597
15935
|
// Init writes run one tab at a time (docs/MULTI_TAB.md).
|
|
15598
15936
|
withSeedDbLock('init', context.filesDir, _saveConfig)
|
|
15599
15937
|
.then(() => {
|
|
15600
|
-
logger$
|
|
15938
|
+
logger$8('[internal/actors] [saveConfig] saveConfig success');
|
|
15601
15939
|
return sendBack({ type: ClientManagerEvents.SAVE_CONFIG_SUCCESS });
|
|
15602
15940
|
})
|
|
15603
15941
|
.catch((error) => {
|
|
15604
|
-
logger$
|
|
15942
|
+
logger$8('[internal/actors] [saveConfig] Error in saveConfig promise chain:', error);
|
|
15605
15943
|
// In test environments, still send success to allow state machine to progress
|
|
15606
15944
|
if (process.env.NODE_ENV === 'test' || process.env.IS_SEED_DEV) {
|
|
15607
|
-
logger$
|
|
15945
|
+
logger$8('[internal/actors] [saveConfig] Sending success despite error in test environment');
|
|
15608
15946
|
sendBack({ type: ClientManagerEvents.SAVE_CONFIG_SUCCESS });
|
|
15609
15947
|
}
|
|
15610
15948
|
else {
|
|
@@ -15617,7 +15955,7 @@ const saveConfig = xstate.fromCallback(({ sendBack, input: { context } }) => {
|
|
|
15617
15955
|
return () => { };
|
|
15618
15956
|
});
|
|
15619
15957
|
|
|
15620
|
-
const logger$
|
|
15958
|
+
const logger$7 = debug('seedSdk:helpers:schema');
|
|
15621
15959
|
/**
|
|
15622
15960
|
* Sanitize a schema name to be filesystem-safe
|
|
15623
15961
|
* Replaces all special characters (except alphanumeric, hyphens, underscores) with underscores
|
|
@@ -15813,7 +16151,7 @@ async function listCompleteSchemaFiles() {
|
|
|
15813
16151
|
const dirExists = await BaseFileManager.pathExists(workingDir);
|
|
15814
16152
|
if (!dirExists) {
|
|
15815
16153
|
// Directory doesn't exist yet - return empty array (not an error condition)
|
|
15816
|
-
logger$
|
|
16154
|
+
logger$7(`Working directory does not exist yet: ${workingDir}, returning empty schema list`);
|
|
15817
16155
|
return [];
|
|
15818
16156
|
}
|
|
15819
16157
|
const files = await fs.promises.readdir(workingDir);
|
|
@@ -15949,7 +16287,7 @@ async function loadAllSchemasFromDb() {
|
|
|
15949
16287
|
}
|
|
15950
16288
|
catch (error) {
|
|
15951
16289
|
// If we can't list schema files (e.g., directory doesn't exist yet), continue without file-based loading
|
|
15952
|
-
logger$
|
|
16290
|
+
logger$7(`Error listing complete schema files (continuing with DB data): ${error instanceof Error ? error.message : String(error)}`);
|
|
15953
16291
|
completeSchemaFiles = [];
|
|
15954
16292
|
}
|
|
15955
16293
|
}
|
|
@@ -16002,7 +16340,7 @@ async function loadAllSchemasFromDb() {
|
|
|
16002
16340
|
continue;
|
|
16003
16341
|
}
|
|
16004
16342
|
catch (error) {
|
|
16005
|
-
logger$
|
|
16343
|
+
logger$7(`Error parsing schemaData for ${schemaName}:`, error);
|
|
16006
16344
|
// Fall through to file-based loading
|
|
16007
16345
|
}
|
|
16008
16346
|
}
|
|
@@ -16044,7 +16382,7 @@ async function loadAllSchemasFromDb() {
|
|
|
16044
16382
|
continue;
|
|
16045
16383
|
}
|
|
16046
16384
|
catch (error) {
|
|
16047
|
-
logger$
|
|
16385
|
+
logger$7(`Error loading schema file for ${schemaName}:`, error);
|
|
16048
16386
|
// Fall through to try loading from schemaData if available
|
|
16049
16387
|
}
|
|
16050
16388
|
}
|
|
@@ -16088,7 +16426,7 @@ async function loadAllSchemasFromDb() {
|
|
|
16088
16426
|
continue;
|
|
16089
16427
|
}
|
|
16090
16428
|
catch (error) {
|
|
16091
|
-
logger$
|
|
16429
|
+
logger$7(`Error parsing schemaData for ${schemaName}:`, error);
|
|
16092
16430
|
}
|
|
16093
16431
|
}
|
|
16094
16432
|
}
|
|
@@ -16099,11 +16437,11 @@ async function loadAllSchemasFromDb() {
|
|
|
16099
16437
|
// Only process if not already in result
|
|
16100
16438
|
if (!processedSchemaNames.has(schemaFileInfo.name)) {
|
|
16101
16439
|
if (isInternalSchema(schemaFileInfo.name, schemaFileInfo.schemaFileId)) {
|
|
16102
|
-
logger$
|
|
16440
|
+
logger$7(`STEP 3: skipping internal schema file: ${schemaFileInfo.filePath}`);
|
|
16103
16441
|
continue;
|
|
16104
16442
|
}
|
|
16105
16443
|
try {
|
|
16106
|
-
logger$
|
|
16444
|
+
logger$7(`STEP 3: processing schema file: ${schemaFileInfo.name} from ${schemaFileInfo.filePath}`);
|
|
16107
16445
|
const content = await BaseFileManager.readFileAsString(schemaFileInfo.filePath);
|
|
16108
16446
|
const schemaFile = JSON.parse(content);
|
|
16109
16447
|
// Import this schema to database (mark as non-draft since it's in a file)
|
|
@@ -16123,7 +16461,7 @@ async function loadAllSchemasFromDb() {
|
|
|
16123
16461
|
processedSchemaNames.add(schemaFileInfo.name);
|
|
16124
16462
|
}
|
|
16125
16463
|
catch (error) {
|
|
16126
|
-
logger$
|
|
16464
|
+
logger$7(`Error loading schema file ${schemaFileInfo.filePath}:`, error);
|
|
16127
16465
|
}
|
|
16128
16466
|
}
|
|
16129
16467
|
}
|
|
@@ -16140,7 +16478,7 @@ async function migrateFileSchemasToDb() {
|
|
|
16140
16478
|
if (!db) {
|
|
16141
16479
|
throw new Error('Database not found');
|
|
16142
16480
|
}
|
|
16143
|
-
logger$
|
|
16481
|
+
logger$7('Starting migration of file-based schemas to database');
|
|
16144
16482
|
// Get all complete schema files
|
|
16145
16483
|
const completeSchemas = await listCompleteSchemaFiles();
|
|
16146
16484
|
let migratedCount = 0;
|
|
@@ -16150,7 +16488,7 @@ async function migrateFileSchemasToDb() {
|
|
|
16150
16488
|
const content = await BaseFileManager.readFileAsString(schemaFileInfo.filePath);
|
|
16151
16489
|
const schemaFile = JSON.parse(content);
|
|
16152
16490
|
if (!schemaFile.$schema || !schemaFile.metadata?.name) {
|
|
16153
|
-
logger$
|
|
16491
|
+
logger$7(`Skipping invalid schema file: ${schemaFileInfo.filePath}`);
|
|
16154
16492
|
continue;
|
|
16155
16493
|
}
|
|
16156
16494
|
const schemaName = schemaFile.metadata.name;
|
|
@@ -16172,17 +16510,17 @@ async function migrateFileSchemasToDb() {
|
|
|
16172
16510
|
JSON.stringify(schemaFile, null, 2), // schemaData
|
|
16173
16511
|
false);
|
|
16174
16512
|
migratedCount++;
|
|
16175
|
-
logger$
|
|
16513
|
+
logger$7(`Migrated schema: ${schemaName} v${schemaFile.version}`);
|
|
16176
16514
|
}
|
|
16177
16515
|
else {
|
|
16178
|
-
logger$
|
|
16516
|
+
logger$7(`Schema ${schemaName} already exists in database, skipping`);
|
|
16179
16517
|
}
|
|
16180
16518
|
}
|
|
16181
16519
|
catch (error) {
|
|
16182
|
-
logger$
|
|
16520
|
+
logger$7(`Error migrating schema file ${schemaFileInfo.filePath}:`, error);
|
|
16183
16521
|
}
|
|
16184
16522
|
}
|
|
16185
|
-
logger$
|
|
16523
|
+
logger$7(`Migration complete: ${migratedCount} schemas migrated`);
|
|
16186
16524
|
return migratedCount;
|
|
16187
16525
|
}
|
|
16188
16526
|
/**
|
|
@@ -16237,7 +16575,7 @@ const getDeterministicIdsWithPrefix = (prefix) => {
|
|
|
16237
16575
|
return (suffix) => utils.bytesToHex(prefixHash.clone().update(utils.utf8ToBytes(suffix)).digest()).slice(0, 10);
|
|
16238
16576
|
};
|
|
16239
16577
|
|
|
16240
|
-
const logger$
|
|
16578
|
+
const logger$6 = debug('seedSdk:imports:json');
|
|
16241
16579
|
/**
|
|
16242
16580
|
* Get refValueType from a property object with case-insensitive key lookup.
|
|
16243
16581
|
* Handles refValueType, refvaluetype, ref_value_type, elementType, and legacy items.type / items.Type.
|
|
@@ -16326,11 +16664,11 @@ const verifyPropertiesPersisted = async (db, modelId, modelName, maxRetries = 10
|
|
|
16326
16664
|
.where(eq(propertiesTable.modelId, modelId))
|
|
16327
16665
|
.limit(1);
|
|
16328
16666
|
if (props.length > 0) {
|
|
16329
|
-
logger$
|
|
16667
|
+
logger$6(`Verified properties exist for model "${modelName}" (modelId: ${modelId}) after ${attempt + 1} attempt(s)`);
|
|
16330
16668
|
return;
|
|
16331
16669
|
}
|
|
16332
16670
|
if (attempt < maxRetries - 1) {
|
|
16333
|
-
logger$
|
|
16671
|
+
logger$6(`Properties not yet visible for model "${modelName}" (modelId: ${modelId}), retrying in ${retryDelay}ms (attempt ${attempt + 1}/${maxRetries})`);
|
|
16334
16672
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
|
16335
16673
|
}
|
|
16336
16674
|
}
|
|
@@ -16636,7 +16974,7 @@ async function importJsonSchema(importFilePathOrContents, version = 1) {
|
|
|
16636
16974
|
if (parsed.$schema && parsed.metadata) {
|
|
16637
16975
|
// It's already a complete schema format - preserve it (id may be missing, will be generated)
|
|
16638
16976
|
originalSchemaFile = parsed;
|
|
16639
|
-
logger$
|
|
16977
|
+
logger$6(`Input is complete schema format (${originalSchemaFile.id ? `preserving ID: ${originalSchemaFile.id}` : 'id will be generated'})`);
|
|
16640
16978
|
}
|
|
16641
16979
|
}
|
|
16642
16980
|
catch {
|
|
@@ -16673,16 +17011,16 @@ async function importJsonSchema(importFilePathOrContents, version = 1) {
|
|
|
16673
17011
|
.limit(1);
|
|
16674
17012
|
if (existingByName.length > 0 && existingByName[0].schemaData) {
|
|
16675
17013
|
const existing = existingByName[0];
|
|
16676
|
-
logger$
|
|
17014
|
+
logger$6(`Idempotency: found existing schema by name+version: ${schemaFile.metadata.name} v${schemaFile.version ?? version}`);
|
|
16677
17015
|
const existingSchema = JSON.parse(existing.schemaData);
|
|
16678
17016
|
const contentMatches = compareSchemaContent(schemaFile, existingSchema);
|
|
16679
|
-
logger$
|
|
17017
|
+
logger$6(`Idempotency: compareSchemaContent=${contentMatches} for ${schemaFile.metadata.name}`);
|
|
16680
17018
|
if (contentMatches) {
|
|
16681
17019
|
if (existing.schemaFileId) {
|
|
16682
17020
|
const workingDir = BaseFileManager.getWorkingDir();
|
|
16683
17021
|
const existingFilePath = getSchemaFilePath(workingDir, schemaFile.metadata.name, schemaFile.version ?? version, existing.schemaFileId);
|
|
16684
17022
|
if (await BaseFileManager.pathExists(existingFilePath)) {
|
|
16685
|
-
logger$
|
|
17023
|
+
logger$6(`Schema ${schemaFile.metadata.name} v${schemaFile.version} already exists with same content, loading existing file`);
|
|
16686
17024
|
return await loadSchemaFromFile(existingFilePath);
|
|
16687
17025
|
}
|
|
16688
17026
|
await BaseFileManager.createDirIfNotExists(workingDir);
|
|
@@ -16690,7 +17028,7 @@ async function importJsonSchema(importFilePathOrContents, version = 1) {
|
|
|
16690
17028
|
await BaseFileManager.waitForFileWithContent(existingFilePath, 100, 5000);
|
|
16691
17029
|
return await loadSchemaFromFile(existingFilePath);
|
|
16692
17030
|
}
|
|
16693
|
-
logger$
|
|
17031
|
+
logger$6(`Schema ${schemaFile.metadata.name} v${schemaFile.version} already exists with same content (no schemaFileId), using DB`);
|
|
16694
17032
|
return '';
|
|
16695
17033
|
}
|
|
16696
17034
|
}
|
|
@@ -16706,7 +17044,7 @@ async function importJsonSchema(importFilePathOrContents, version = 1) {
|
|
|
16706
17044
|
// processSchemaFiles runs multiple times (React StrictMode, app restarts).
|
|
16707
17045
|
if (!schemaFile.id) {
|
|
16708
17046
|
schemaFile.id = await getDeterministicSchemaFileId(schemaFile);
|
|
16709
|
-
logger$
|
|
17047
|
+
logger$6(`Generated deterministic schema ID for ${schemaFile.metadata.name}:`, schemaFile.id);
|
|
16710
17048
|
}
|
|
16711
17049
|
// Before generating IDs, check if a file already exists with this schemaFileId
|
|
16712
17050
|
// This handles the case where the schema was already loaded via loadSchemaFromFile
|
|
@@ -16716,7 +17054,7 @@ async function importJsonSchema(importFilePathOrContents, version = 1) {
|
|
|
16716
17054
|
const existingFileExists = await BaseFileManager.pathExists(existingFilePath);
|
|
16717
17055
|
if (existingFileExists) {
|
|
16718
17056
|
// File already exists with this schemaFileId - just load it instead of creating a new one
|
|
16719
|
-
logger$
|
|
17057
|
+
logger$6(`Schema file already exists with schemaFileId ${schemaFile.id}, loading it instead of creating new file`);
|
|
16720
17058
|
return await loadSchemaFromFile(existingFilePath);
|
|
16721
17059
|
}
|
|
16722
17060
|
// Generate missing model and property IDs.
|
|
@@ -16727,12 +17065,12 @@ async function importJsonSchema(importFilePathOrContents, version = 1) {
|
|
|
16727
17065
|
for (const [modelName, model] of Object.entries(schemaFile.models || {})) {
|
|
16728
17066
|
if (!model.id) {
|
|
16729
17067
|
model.id = getDeterministicId(modelName);
|
|
16730
|
-
logger$
|
|
17068
|
+
logger$6(`Generated deterministic model ID for ${modelName}:`, model.id);
|
|
16731
17069
|
}
|
|
16732
17070
|
for (const [propName, prop] of Object.entries(model.properties || {})) {
|
|
16733
17071
|
if (!prop.id) {
|
|
16734
17072
|
prop.id = getDeterministicId(modelName + propName);
|
|
16735
|
-
logger$
|
|
17073
|
+
logger$6(`Generated deterministic property ID for ${modelName}.${propName}:`, prop.id);
|
|
16736
17074
|
}
|
|
16737
17075
|
}
|
|
16738
17076
|
}
|
|
@@ -16766,14 +17104,14 @@ async function importJsonSchema(importFilePathOrContents, version = 1) {
|
|
|
16766
17104
|
const workingDir = BaseFileManager.getWorkingDir();
|
|
16767
17105
|
const existingFilePath = getSchemaFilePath(workingDir, importData.name, version, existing.schemaFileId);
|
|
16768
17106
|
if (await BaseFileManager.pathExists(existingFilePath)) {
|
|
16769
|
-
logger$
|
|
17107
|
+
logger$6(`Schema ${importData.name} v${version} already exists with same content, loading existing file`);
|
|
16770
17108
|
return await loadSchemaFromFile(existingFilePath);
|
|
16771
17109
|
}
|
|
16772
17110
|
// File missing but schemaData exists - restore file from schemaData
|
|
16773
17111
|
await BaseFileManager.createDirIfNotExists(workingDir);
|
|
16774
17112
|
await BaseFileManager.saveFile(existingFilePath, existing.schemaData);
|
|
16775
17113
|
await BaseFileManager.waitForFileWithContent(existingFilePath, 100, 5000);
|
|
16776
|
-
logger$
|
|
17114
|
+
logger$6(`Restored schema file from schemaData for ${importData.name} v${version}`);
|
|
16777
17115
|
return await loadSchemaFromFile(existingFilePath);
|
|
16778
17116
|
}
|
|
16779
17117
|
// No schemaFileId - restore file from schemaData using id from parsed schema
|
|
@@ -16784,10 +17122,10 @@ async function importJsonSchema(importFilePathOrContents, version = 1) {
|
|
|
16784
17122
|
await BaseFileManager.createDirIfNotExists(workingDir);
|
|
16785
17123
|
await BaseFileManager.saveFile(existingFilePath, existing.schemaData);
|
|
16786
17124
|
await BaseFileManager.waitForFileWithContent(existingFilePath, 100, 5000);
|
|
16787
|
-
logger$
|
|
17125
|
+
logger$6(`Restored schema file from schemaData for ${importData.name} v${version}`);
|
|
16788
17126
|
return await loadSchemaFromFile(existingFilePath);
|
|
16789
17127
|
}
|
|
16790
|
-
logger$
|
|
17128
|
+
logger$6(`Schema ${importData.name} v${version} already exists with same content (no id), using DB`);
|
|
16791
17129
|
return '';
|
|
16792
17130
|
}
|
|
16793
17131
|
// Content differs - create new version
|
|
@@ -16798,7 +17136,7 @@ async function importJsonSchema(importFilePathOrContents, version = 1) {
|
|
|
16798
17136
|
.orderBy(desc(schemasTable.version))
|
|
16799
17137
|
.limit(1);
|
|
16800
17138
|
const newVersion = latestVersionRows.length > 0 ? latestVersionRows[0].version + 1 : version;
|
|
16801
|
-
logger$
|
|
17139
|
+
logger$6(`Schema ${importData.name} content changed, creating new version v${newVersion}`);
|
|
16802
17140
|
schemaFile = transformImportToSchemaFile(importData, newVersion);
|
|
16803
17141
|
version = newVersion;
|
|
16804
17142
|
}
|
|
@@ -16822,7 +17160,7 @@ async function importJsonSchema(importFilePathOrContents, version = 1) {
|
|
|
16822
17160
|
// Check if this is an internal SDK schema (should not create files in app directory)
|
|
16823
17161
|
const isInternal = isInternalSchema(schemaFile.metadata.name, schemaFile.id);
|
|
16824
17162
|
if (isInternal) {
|
|
16825
|
-
logger$
|
|
17163
|
+
logger$6(`Skipping file creation for internal schema: ${schemaFile.metadata.name}`);
|
|
16826
17164
|
// For internal schemas, just load to database and store, don't create file
|
|
16827
17165
|
// Extract schema name and version
|
|
16828
17166
|
const schemaName = schemaFile.metadata?.name;
|
|
@@ -16879,7 +17217,7 @@ async function importJsonSchema(importFilePathOrContents, version = 1) {
|
|
|
16879
17217
|
// Use fixed internal schema ID if missing (never generate for internal)
|
|
16880
17218
|
if (!schemaFile.id) {
|
|
16881
17219
|
schemaFile.id = INTERNAL_SCHEMA_IDS[0];
|
|
16882
|
-
logger$
|
|
17220
|
+
logger$6('Using fixed internal schema ID:', schemaFile.id);
|
|
16883
17221
|
}
|
|
16884
17222
|
// Extract schemaFileIds from JSON file and generate missing ones BEFORE creating models
|
|
16885
17223
|
// This ensures Model instances are created with correct IDs
|
|
@@ -16889,7 +17227,7 @@ async function importJsonSchema(importFilePathOrContents, version = 1) {
|
|
|
16889
17227
|
// Generate model ID if missing
|
|
16890
17228
|
if (!model.id) {
|
|
16891
17229
|
model.id = generateId();
|
|
16892
|
-
logger$
|
|
17230
|
+
logger$6(`Generated model ID for ${modelName}:`, model.id);
|
|
16893
17231
|
// Update the schemaFile object so it's included in schemaData
|
|
16894
17232
|
schemaFile.models[modelName].id = model.id;
|
|
16895
17233
|
}
|
|
@@ -16899,7 +17237,7 @@ async function importJsonSchema(importFilePathOrContents, version = 1) {
|
|
|
16899
17237
|
// Generate property ID if missing
|
|
16900
17238
|
if (!prop.id) {
|
|
16901
17239
|
prop.id = generateId();
|
|
16902
|
-
logger$
|
|
17240
|
+
logger$6(`Generated property ID for ${modelName}.${propName}:`, prop.id);
|
|
16903
17241
|
// Update the schemaFile object so it's included in schemaData
|
|
16904
17242
|
schemaFile.models[modelName].properties[propName].id = prop.id;
|
|
16905
17243
|
}
|
|
@@ -16911,7 +17249,7 @@ async function importJsonSchema(importFilePathOrContents, version = 1) {
|
|
|
16911
17249
|
}
|
|
16912
17250
|
// Convert JSON models to Model classes, passing modelFileIds and propertyFileIds so Model instances use correct IDs
|
|
16913
17251
|
const modelDefinitions = await createModelsFromJson(importDataForInternal, modelFileIds, propertyFileIds);
|
|
16914
|
-
logger$
|
|
17252
|
+
logger$6('loadSchemaFromFile (internal) - modelDefinitions length:', Object.keys(modelDefinitions).length);
|
|
16915
17253
|
// Convert schema file metadata to schema input for database
|
|
16916
17254
|
const schemaInput = {
|
|
16917
17255
|
name: schemaName,
|
|
@@ -17031,12 +17369,12 @@ async function importJsonSchema(importFilePathOrContents, version = 1) {
|
|
|
17031
17369
|
}
|
|
17032
17370
|
catch (dbError) {
|
|
17033
17371
|
// Database not available - log warning but continue
|
|
17034
|
-
logger$
|
|
17372
|
+
logger$6('Database not available, skipping database operations:', dbError instanceof Error ? dbError.message : String(dbError));
|
|
17035
17373
|
}
|
|
17036
17374
|
// Models are now Model instances, no registration needed
|
|
17037
17375
|
// They should be created via Model.create() and are accessible via Model static methods
|
|
17038
17376
|
for (const [modelName] of Object.entries(modelDefinitions)) {
|
|
17039
|
-
logger$
|
|
17377
|
+
logger$6('loadSchemaFromFile (internal) - model available:', modelName);
|
|
17040
17378
|
}
|
|
17041
17379
|
// Schema is now saved to database
|
|
17042
17380
|
// useSchemas hook will pick it up via database queries
|
|
@@ -17047,7 +17385,7 @@ async function importJsonSchema(importFilePathOrContents, version = 1) {
|
|
|
17047
17385
|
// Ensure schema ID is defined (should always be set by this point, but check for safety)
|
|
17048
17386
|
if (!schemaFile.id) {
|
|
17049
17387
|
schemaFile.id = generateId();
|
|
17050
|
-
logger$
|
|
17388
|
+
logger$6('Generated schema ID before file path creation:', schemaFile.id);
|
|
17051
17389
|
}
|
|
17052
17390
|
// Get the target file path using ID-based naming (preferred)
|
|
17053
17391
|
const filePath = getSchemaFilePath(workingDir, schemaFile.metadata.name, version, schemaFile.id);
|
|
@@ -17066,7 +17404,7 @@ async function importJsonSchema(importFilePathOrContents, version = 1) {
|
|
|
17066
17404
|
const data = JSON.parse(content);
|
|
17067
17405
|
if (data.$schema) {
|
|
17068
17406
|
// File is already a complete schema, just load it
|
|
17069
|
-
logger$
|
|
17407
|
+
logger$6(`Schema file ${filePath} already exists and is complete, loading it`);
|
|
17070
17408
|
return await loadSchemaFromFile(filePath);
|
|
17071
17409
|
}
|
|
17072
17410
|
}
|
|
@@ -17084,7 +17422,7 @@ async function importJsonSchema(importFilePathOrContents, version = 1) {
|
|
|
17084
17422
|
// Compare schemas structurally
|
|
17085
17423
|
if (compareSchemasStructurally(schemaFile, existingSchema)) {
|
|
17086
17424
|
// Schemas match - just load the existing file
|
|
17087
|
-
logger$
|
|
17425
|
+
logger$6(`Schema ${schemaFile.metadata.name} v${version} already exists with matching content, loading it`);
|
|
17088
17426
|
return await loadSchemaFromFile(filePath);
|
|
17089
17427
|
}
|
|
17090
17428
|
else {
|
|
@@ -17259,7 +17597,7 @@ const loadSchemaFromFile = async (schemaFilePath) => {
|
|
|
17259
17597
|
// Generate schema ID if missing (before creating schemaInput)
|
|
17260
17598
|
if (!schemaFile.id) {
|
|
17261
17599
|
schemaFile.id = generateId();
|
|
17262
|
-
logger$
|
|
17600
|
+
logger$6('Generated schema ID for schema file:', schemaFile.id);
|
|
17263
17601
|
}
|
|
17264
17602
|
// Extract schemaFileIds from JSON file and generate missing ones BEFORE creating models
|
|
17265
17603
|
// This ensures Model instances are created with correct IDs
|
|
@@ -17269,7 +17607,7 @@ const loadSchemaFromFile = async (schemaFilePath) => {
|
|
|
17269
17607
|
// Generate model ID if missing
|
|
17270
17608
|
if (!model.id) {
|
|
17271
17609
|
model.id = generateId();
|
|
17272
|
-
logger$
|
|
17610
|
+
logger$6(`Generated model ID for ${modelName}:`, model.id);
|
|
17273
17611
|
// Update the schemaFile object so it's included in schemaData
|
|
17274
17612
|
schemaFile.models[modelName].id = model.id;
|
|
17275
17613
|
}
|
|
@@ -17279,7 +17617,7 @@ const loadSchemaFromFile = async (schemaFilePath) => {
|
|
|
17279
17617
|
// Generate property ID if missing
|
|
17280
17618
|
if (!prop.id) {
|
|
17281
17619
|
prop.id = generateId();
|
|
17282
|
-
logger$
|
|
17620
|
+
logger$6(`Generated property ID for ${modelName}.${propName}:`, prop.id);
|
|
17283
17621
|
// Update the schemaFile object so it's included in schemaData
|
|
17284
17622
|
schemaFile.models[modelName].properties[propName].id = prop.id;
|
|
17285
17623
|
}
|
|
@@ -17291,7 +17629,7 @@ const loadSchemaFromFile = async (schemaFilePath) => {
|
|
|
17291
17629
|
}
|
|
17292
17630
|
// Convert JSON models to Model classes, passing modelFileIds and propertyFileIds so Model instances use correct IDs
|
|
17293
17631
|
const modelDefinitions = await createModelsFromJson(importData, modelFileIds, propertyFileIds);
|
|
17294
|
-
logger$
|
|
17632
|
+
logger$6('loadSchemaFromFile - modelDefinitions length:', Object.keys(modelDefinitions).length);
|
|
17295
17633
|
// Try to add schema and models to database if database is available
|
|
17296
17634
|
try {
|
|
17297
17635
|
const db = BaseDb.getAppDb();
|
|
@@ -17346,17 +17684,17 @@ const loadSchemaFromFile = async (schemaFilePath) => {
|
|
|
17346
17684
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
17347
17685
|
}
|
|
17348
17686
|
}
|
|
17349
|
-
logger$
|
|
17687
|
+
logger$6(`Verifying properties: found ${modelLinks.length} model links for schema ${schemaName} (id: ${schemaRecord.id})`);
|
|
17350
17688
|
// If we didn't verify via direct lookup, verify via join table
|
|
17351
17689
|
if (!seedModelId || (seedModel.length === 0 || !seedModel[0].id)) {
|
|
17352
17690
|
const seedModelLink = modelLinks.find((link) => link.modelName === 'Seed');
|
|
17353
17691
|
if (seedModelLink && seedModelLink.modelId) {
|
|
17354
|
-
logger$
|
|
17692
|
+
logger$6(`Verifying properties for Seed model via join table (modelId: ${seedModelLink.modelId})`);
|
|
17355
17693
|
await verifyPropertiesPersisted(db, seedModelLink.modelId, 'Seed', 10, 100);
|
|
17356
17694
|
}
|
|
17357
17695
|
else if (modelLinks.length > 0 && modelLinks[0].modelId) {
|
|
17358
17696
|
// Fallback to first model if Seed not found
|
|
17359
|
-
logger$
|
|
17697
|
+
logger$6(`Verifying properties for ${modelLinks[0].modelName} model via join table (modelId: ${modelLinks[0].modelId})`);
|
|
17360
17698
|
await verifyPropertiesPersisted(db, modelLinks[0].modelId, modelLinks[0].modelName || 'unknown', 10, 100);
|
|
17361
17699
|
}
|
|
17362
17700
|
}
|
|
@@ -17387,7 +17725,7 @@ const loadSchemaFromFile = async (schemaFilePath) => {
|
|
|
17387
17725
|
const currentId = schemaFile.models[modelName].properties[propRecord.name].id;
|
|
17388
17726
|
if (currentId !== propRecord.schemaFileId) {
|
|
17389
17727
|
schemaFile.models[modelName].properties[propRecord.name].id = propRecord.schemaFileId;
|
|
17390
|
-
logger$
|
|
17728
|
+
logger$6(`Updated schemaFile property ID for ${modelName}.${propRecord.name} from ${currentId} to ${propRecord.schemaFileId}`);
|
|
17391
17729
|
schemaFileUpdated = true;
|
|
17392
17730
|
}
|
|
17393
17731
|
}
|
|
@@ -17405,21 +17743,21 @@ const loadSchemaFromFile = async (schemaFilePath) => {
|
|
|
17405
17743
|
.set({ schemaData: updatedSchemaData })
|
|
17406
17744
|
.where(eq(schemasTable.id, schemaRecord.id));
|
|
17407
17745
|
if (schemaFileUpdated) {
|
|
17408
|
-
logger$
|
|
17746
|
+
logger$6(`Updated schemaData in database with actual property IDs for schema "${schemaName}"`);
|
|
17409
17747
|
}
|
|
17410
17748
|
else {
|
|
17411
|
-
logger$
|
|
17749
|
+
logger$6(`Verified schemaData in database matches schemaFile for schema "${schemaName}"`);
|
|
17412
17750
|
}
|
|
17413
17751
|
}
|
|
17414
17752
|
}
|
|
17415
17753
|
catch (dbError) {
|
|
17416
17754
|
// Database not available - log warning but continue
|
|
17417
|
-
logger$
|
|
17755
|
+
logger$6('Database not available, skipping database operations:', dbError instanceof Error ? dbError.message : String(dbError));
|
|
17418
17756
|
}
|
|
17419
17757
|
// Models are now Model instances, no registration needed
|
|
17420
17758
|
// They should be created via Model.create() and are accessible via Model static methods
|
|
17421
17759
|
for (const [modelName] of Object.entries(modelDefinitions)) {
|
|
17422
|
-
logger$
|
|
17760
|
+
logger$6('loadSchemaFromFile - model available:', modelName);
|
|
17423
17761
|
}
|
|
17424
17762
|
return schemaFilePath;
|
|
17425
17763
|
}
|
|
@@ -17504,10 +17842,10 @@ propertyFileIds) => {
|
|
|
17504
17842
|
// Pass id (schemaFileId) if provided to ensure Model instance uses correct ID
|
|
17505
17843
|
// Note: indexes and description are ignored - they remain in JSON for schema file format compliance but are not used by Model instances
|
|
17506
17844
|
if (modelFileId) {
|
|
17507
|
-
logger$
|
|
17845
|
+
logger$6(`Creating schema model "${modelName}" with modelFileId: ${modelFileId}`);
|
|
17508
17846
|
}
|
|
17509
17847
|
else {
|
|
17510
|
-
logger$
|
|
17848
|
+
logger$6(`Warning: Creating schema model "${modelName}" without modelFileId - it will be renamed if duplicates exist`);
|
|
17511
17849
|
}
|
|
17512
17850
|
const modelInstance = Model.create(modelName, schemaName, {
|
|
17513
17851
|
modelFileId: modelFileId, // Pass modelFileId (preferred) as id (schemaFileId) from JSON file
|
|
@@ -17533,7 +17871,7 @@ const createModelsFromJson = async (importData, modelFileIds, propertyFileIds) =
|
|
|
17533
17871
|
// Use modelFileId from map (no fallback to modelDef.id as JsonImportSchema doesn't have id property)
|
|
17534
17872
|
const finalModelFileId = modelFileId;
|
|
17535
17873
|
if (!finalModelFileId) {
|
|
17536
|
-
logger$
|
|
17874
|
+
logger$6(`Warning: No modelFileId found for model "${modelName}" - model will be created without ID`);
|
|
17537
17875
|
}
|
|
17538
17876
|
// Get propertyFileIds for this model if available
|
|
17539
17877
|
const modelPropertyFileIds = propertyFileIds?.get(modelName);
|
|
@@ -17723,7 +18061,7 @@ var SEEDPROTOCOL_Seed_Protocol_v1 = /*#__PURE__*/Object.freeze({
|
|
|
17723
18061
|
version: version
|
|
17724
18062
|
});
|
|
17725
18063
|
|
|
17726
|
-
const logger$
|
|
18064
|
+
const logger$5 = debug('seedSdk:client:actors:processSchemaFiles');
|
|
17727
18065
|
let processSchemaFilesChain = Promise.resolve();
|
|
17728
18066
|
function resolveSchemaFilePath(schemaFile) {
|
|
17729
18067
|
const path = BaseFileManager.getPathModule();
|
|
@@ -17751,7 +18089,7 @@ async function resolveSchemaNameFromSource(source) {
|
|
|
17751
18089
|
}
|
|
17752
18090
|
}
|
|
17753
18091
|
catch (error) {
|
|
17754
|
-
logger$
|
|
18092
|
+
logger$5('Could not resolve schema name from path:', source, error);
|
|
17755
18093
|
}
|
|
17756
18094
|
}
|
|
17757
18095
|
return null;
|
|
@@ -17760,7 +18098,7 @@ async function resolveSchemaNameFromSource(source) {
|
|
|
17760
18098
|
// Schema processing can be slow with syncSchemaFromSource, loadAllSchemasFromDb, and OPFS file I/O
|
|
17761
18099
|
const PROCESS_SCHEMA_FILES_TIMEOUT_MS = 120000;
|
|
17762
18100
|
const processSchemaFiles = xstate.fromCallback(({ sendBack, input: { context } }) => {
|
|
17763
|
-
logger$
|
|
18101
|
+
logger$5('processSchemaFiles started');
|
|
17764
18102
|
let hasResponded = false;
|
|
17765
18103
|
let timeoutId = null;
|
|
17766
18104
|
const reportError = (error) => {
|
|
@@ -17770,7 +18108,7 @@ const processSchemaFiles = xstate.fromCallback(({ sendBack, input: { context } }
|
|
|
17770
18108
|
clearTimeout(timeoutId);
|
|
17771
18109
|
timeoutId = null;
|
|
17772
18110
|
}
|
|
17773
|
-
logger$
|
|
18111
|
+
logger$5('processSchemaFiles error:', error);
|
|
17774
18112
|
sendBack({
|
|
17775
18113
|
type: 'error',
|
|
17776
18114
|
error: error instanceof Error ? error : new Error(String(error))
|
|
@@ -17784,7 +18122,7 @@ const processSchemaFiles = xstate.fromCallback(({ sendBack, input: { context } }
|
|
|
17784
18122
|
clearTimeout(timeoutId);
|
|
17785
18123
|
timeoutId = null;
|
|
17786
18124
|
}
|
|
17787
|
-
logger$
|
|
18125
|
+
logger$5('processSchemaFiles completed');
|
|
17788
18126
|
sendBack({ type: ClientManagerEvents.PROCESS_SCHEMA_FILES_SUCCESS });
|
|
17789
18127
|
}
|
|
17790
18128
|
};
|
|
@@ -17796,7 +18134,7 @@ const processSchemaFiles = xstate.fromCallback(({ sendBack, input: { context } }
|
|
|
17796
18134
|
// Schemas written (or confirmed present) this run — skip redundant loadSchemaFromFile later
|
|
17797
18135
|
const schemasWrittenThisRun = new Set();
|
|
17798
18136
|
// First, load the internal seed-protocol schema
|
|
17799
|
-
logger$
|
|
18137
|
+
logger$5('Loading internal seed-protocol schema');
|
|
17800
18138
|
const internalSchemaData = internalSchema;
|
|
17801
18139
|
const db = BaseDb.getAppDb();
|
|
17802
18140
|
const internalSchemaExists = db
|
|
@@ -17805,21 +18143,21 @@ const processSchemaFiles = xstate.fromCallback(({ sendBack, input: { context } }
|
|
|
17805
18143
|
// Always mark internal schema as written so the DB/file loop does not re-apply it
|
|
17806
18144
|
schemasWrittenThisRun.add(SEED_PROTOCOL_SCHEMA_NAME);
|
|
17807
18145
|
if (internalSchemaExists) {
|
|
17808
|
-
logger$
|
|
18146
|
+
logger$5('Internal seed-protocol schema already in DB, skipping import');
|
|
17809
18147
|
}
|
|
17810
18148
|
else {
|
|
17811
18149
|
try {
|
|
17812
18150
|
// Import the internal schema to database (will create models and add to store)
|
|
17813
18151
|
await importJsonSchema({ contents: JSON.stringify(internalSchemaData) }, internalSchemaData.version);
|
|
17814
|
-
logger$
|
|
18152
|
+
logger$5(`Loaded internal seed-protocol schema v${internalSchemaData.version}`);
|
|
17815
18153
|
}
|
|
17816
18154
|
catch (error) {
|
|
17817
18155
|
// If schema already exists, that's fine - it means it was already loaded
|
|
17818
18156
|
if (error?.message?.includes('already exists')) {
|
|
17819
|
-
logger$
|
|
18157
|
+
logger$5('Internal seed-protocol schema already loaded');
|
|
17820
18158
|
}
|
|
17821
18159
|
else {
|
|
17822
|
-
logger$
|
|
18160
|
+
logger$5('Error loading internal seed-protocol schema:', error);
|
|
17823
18161
|
// Don't fail if internal schema can't be loaded - it might already be in DB
|
|
17824
18162
|
}
|
|
17825
18163
|
}
|
|
@@ -17832,7 +18170,7 @@ const processSchemaFiles = xstate.fromCallback(({ sendBack, input: { context } }
|
|
|
17832
18170
|
: '';
|
|
17833
18171
|
const schemaId = typeof context.schema === 'object' && context.schema?.id ? String(context.schema.id) : undefined;
|
|
17834
18172
|
if (isInternalSchema(schemaName, schemaId)) {
|
|
17835
|
-
logger$
|
|
18173
|
+
logger$5('Schema is internal (Seed Protocol), already loaded - skipping syncSchemaFromSource');
|
|
17836
18174
|
}
|
|
17837
18175
|
else {
|
|
17838
18176
|
try {
|
|
@@ -17841,10 +18179,10 @@ const processSchemaFiles = xstate.fromCallback(({ sendBack, input: { context } }
|
|
|
17841
18179
|
if (syncedName) {
|
|
17842
18180
|
schemasWrittenThisRun.add(syncedName);
|
|
17843
18181
|
}
|
|
17844
|
-
logger$
|
|
18182
|
+
logger$5(`Synced canonical schema from ${typeof context.schema === 'string' ? context.schema : 'object'}`);
|
|
17845
18183
|
}
|
|
17846
18184
|
catch (error) {
|
|
17847
|
-
logger$
|
|
18185
|
+
logger$5(`Error syncing canonical schema:`, error);
|
|
17848
18186
|
// Don't fail init - schema might already be in DB
|
|
17849
18187
|
}
|
|
17850
18188
|
}
|
|
@@ -17859,23 +18197,23 @@ const processSchemaFiles = xstate.fromCallback(({ sendBack, input: { context } }
|
|
|
17859
18197
|
if (parsed?.metadata?.name) {
|
|
17860
18198
|
schemasWrittenThisRun.add(String(parsed.metadata.name));
|
|
17861
18199
|
}
|
|
17862
|
-
logger$
|
|
18200
|
+
logger$5(`Loaded schema from ${resolvedPath}`);
|
|
17863
18201
|
}
|
|
17864
18202
|
catch (error) {
|
|
17865
|
-
logger$
|
|
18203
|
+
logger$5(`Error loading schema file ${context.schemaFile}:`, error);
|
|
17866
18204
|
// Don't fail init - schema might already be in DB
|
|
17867
18205
|
}
|
|
17868
18206
|
}
|
|
17869
18207
|
// Process minimal import files (files without $schema) - skip when canonical schema is set
|
|
17870
18208
|
if (!context.schema) {
|
|
17871
|
-
logger$
|
|
18209
|
+
logger$5('Listing schema files');
|
|
17872
18210
|
let schemaFiles = [];
|
|
17873
18211
|
try {
|
|
17874
18212
|
schemaFiles = await listSchemaFiles();
|
|
17875
|
-
logger$
|
|
18213
|
+
logger$5(`Found ${schemaFiles.length} schema files to process`);
|
|
17876
18214
|
}
|
|
17877
18215
|
catch (error) {
|
|
17878
|
-
logger$
|
|
18216
|
+
logger$5('Error listing schema files (continuing anyway):', error);
|
|
17879
18217
|
schemaFiles = [];
|
|
17880
18218
|
}
|
|
17881
18219
|
for (const schemaFile of schemaFiles) {
|
|
@@ -17886,19 +18224,19 @@ const processSchemaFiles = xstate.fromCallback(({ sendBack, input: { context } }
|
|
|
17886
18224
|
}
|
|
17887
18225
|
}
|
|
17888
18226
|
catch (error) {
|
|
17889
|
-
logger$
|
|
18227
|
+
logger$5(`Error importing schema file ${schemaFile.filePath}:`, error);
|
|
17890
18228
|
}
|
|
17891
18229
|
}
|
|
17892
18230
|
}
|
|
17893
18231
|
// Then, load all schemas using the unified database-first approach
|
|
17894
|
-
logger$
|
|
18232
|
+
logger$5('Loading schemas from database and files');
|
|
17895
18233
|
let allSchemasData = [];
|
|
17896
18234
|
try {
|
|
17897
18235
|
allSchemasData = await loadAllSchemasFromDb();
|
|
17898
|
-
logger$
|
|
18236
|
+
logger$5(`Loaded ${allSchemasData.length} schemas (${allSchemasData.filter(s => s.isDraft).length} drafts, ${allSchemasData.filter(s => !s.isDraft).length} published)`);
|
|
17899
18237
|
}
|
|
17900
18238
|
catch (error) {
|
|
17901
|
-
logger$
|
|
18239
|
+
logger$5('Error loading schemas from database (continuing anyway):', error);
|
|
17902
18240
|
// Continue with empty array - this is not critical for initialization
|
|
17903
18241
|
// Schemas can be loaded on-demand later
|
|
17904
18242
|
allSchemasData = [];
|
|
@@ -17919,13 +18257,13 @@ const processSchemaFiles = xstate.fromCallback(({ sendBack, input: { context } }
|
|
|
17919
18257
|
}
|
|
17920
18258
|
}
|
|
17921
18259
|
catch (error) {
|
|
17922
|
-
logger$
|
|
18260
|
+
logger$5('Error counting linked models (re-applying all schema files):', error);
|
|
17923
18261
|
}
|
|
17924
18262
|
}
|
|
17925
18263
|
for (const schemaData of allSchemasData) {
|
|
17926
18264
|
const schema = schemaData.schema;
|
|
17927
18265
|
const schemaName = schema.metadata?.name || 'unknown';
|
|
17928
|
-
logger$
|
|
18266
|
+
logger$5(`Processing schema: ${schemaName} v${schema.version} (${schemaData.isDraft ? 'draft' : 'published'}, source: ${schemaData.source})`);
|
|
17929
18267
|
// For published schemas (not drafts), load models from file if available
|
|
17930
18268
|
if (!schemaData.isDraft && schema.id) {
|
|
17931
18269
|
// loadAllSchemasFromDb already picked the latest file for this schema (and read it)
|
|
@@ -17955,7 +18293,7 @@ const processSchemaFiles = xstate.fromCallback(({ sendBack, input: { context } }
|
|
|
17955
18293
|
}
|
|
17956
18294
|
}
|
|
17957
18295
|
catch (error) {
|
|
17958
|
-
logger$
|
|
18296
|
+
logger$5(`Error loading schema file for ${schemaName}:`, error);
|
|
17959
18297
|
}
|
|
17960
18298
|
}
|
|
17961
18299
|
else {
|
|
@@ -18004,11 +18342,11 @@ const processSchemaFiles = xstate.fromCallback(({ sendBack, input: { context } }
|
|
|
18004
18342
|
};
|
|
18005
18343
|
const modelDefinitions = await createModelsFromJson(importData, modelFileIds, propertyFileIds);
|
|
18006
18344
|
Object.assign(allModels, modelDefinitions);
|
|
18007
|
-
logger$
|
|
18345
|
+
logger$5(`Loaded ${Object.keys(modelDefinitions).length} models from schema data for ${schemaName}`);
|
|
18008
18346
|
}
|
|
18009
18347
|
}
|
|
18010
18348
|
catch (error) {
|
|
18011
|
-
logger$
|
|
18349
|
+
logger$5(`Error creating models from schema data for ${schemaName}:`, error);
|
|
18012
18350
|
}
|
|
18013
18351
|
}
|
|
18014
18352
|
}
|
|
@@ -18058,11 +18396,11 @@ const processSchemaFiles = xstate.fromCallback(({ sendBack, input: { context } }
|
|
|
18058
18396
|
};
|
|
18059
18397
|
const modelDefinitions = await createModelsFromJson(importData, modelFileIds, propertyFileIds);
|
|
18060
18398
|
Object.assign(allModels, modelDefinitions);
|
|
18061
|
-
logger$
|
|
18399
|
+
logger$5(`Loaded ${Object.keys(modelDefinitions).length} models from draft schema ${schemaName}`);
|
|
18062
18400
|
}
|
|
18063
18401
|
}
|
|
18064
18402
|
catch (error) {
|
|
18065
|
-
logger$
|
|
18403
|
+
logger$5(`Error creating models from draft schema ${schemaName}:`, error);
|
|
18066
18404
|
}
|
|
18067
18405
|
}
|
|
18068
18406
|
// Schemas are now loaded on-demand via database queries (useSchemas hook uses loadAllSchemasFromDb)
|
|
@@ -18080,7 +18418,7 @@ const processSchemaFiles = xstate.fromCallback(({ sendBack, input: { context } }
|
|
|
18080
18418
|
// Its schema and model inserts check for rows first, so tabs take turns (docs/MULTI_TAB.md).
|
|
18081
18419
|
.then(() => withSeedDbLock('init', context.filesDir, _processSchemaFiles))
|
|
18082
18420
|
.catch((err) => {
|
|
18083
|
-
logger$
|
|
18421
|
+
logger$5('processSchemaFiles chain error:', err);
|
|
18084
18422
|
throw err;
|
|
18085
18423
|
});
|
|
18086
18424
|
processSchemaFilesChain.then(reportSuccess).catch((error) => {
|
|
@@ -18095,6 +18433,285 @@ const processSchemaFiles = xstate.fromCallback(({ sendBack, input: { context } }
|
|
|
18095
18433
|
};
|
|
18096
18434
|
});
|
|
18097
18435
|
|
|
18436
|
+
/**
|
|
18437
|
+
* A legacy ItemStorage draft row: a local edit (no attestation uid, not derived from one) that holds
|
|
18438
|
+
* the content itself in property_value, with no file reference. Item.create / createNewItem stored
|
|
18439
|
+
* ItemStorage values like this before they went through the save pipeline.
|
|
18440
|
+
*/
|
|
18441
|
+
const isVerbatimItemStorageDraftRow = (row) => !row.uid &&
|
|
18442
|
+
row.derivedFromUid == null &&
|
|
18443
|
+
!row.refResolvedValue &&
|
|
18444
|
+
typeof row.propertyValue === 'string' &&
|
|
18445
|
+
row.propertyValue !== '';
|
|
18446
|
+
/**
|
|
18447
|
+
* Convert one legacy ItemStorage draft row (see {@link isVerbatimItemStorageDraftRow}) into the
|
|
18448
|
+
* shape saveItemStorage gives a row: the content goes to the file a save would write
|
|
18449
|
+
* (`<seedUid or seedLocalId><extension>` in the property's storage directory), and the row records
|
|
18450
|
+
* `refResolvedValue`, `localStorageDir` and `refValueType: 'file'` with property_value cleared.
|
|
18451
|
+
* Used by publish (resolveItemStorageChildFile) and the one-time migration (migrateLegacyItemStorageRows).
|
|
18452
|
+
*
|
|
18453
|
+
* Reading the row, writing the file and updating the row happen under the file's lock
|
|
18454
|
+
* ({@link withItemStorageFileLock}), which saveItemStorage holds too: a save in this or another tab
|
|
18455
|
+
* either finishes before (the row, read again once the lock is held, is no longer legacy and is
|
|
18456
|
+
* left alone) or starts after (and overwrites the file and row with its content). Without the lock
|
|
18457
|
+
* a save landing between the read and the file write would have its file overwritten with the old
|
|
18458
|
+
* content. The row update also stays conditional on the row being unchanged, for writers that
|
|
18459
|
+
* don't take the lock (a tab still running an older SDK).
|
|
18460
|
+
*
|
|
18461
|
+
* Returns undefined when there was nothing to convert. Throws when the file can't be written; the
|
|
18462
|
+
* row is then unchanged.
|
|
18463
|
+
*/
|
|
18464
|
+
const convertVerbatimItemStorageRow = async (localId, storage = {}) => {
|
|
18465
|
+
const appDb = BaseDb.getAppDb();
|
|
18466
|
+
if (!appDb || !localId)
|
|
18467
|
+
return undefined;
|
|
18468
|
+
const targetOf = (row) => {
|
|
18469
|
+
if (!isVerbatimItemStorageDraftRow(row))
|
|
18470
|
+
return undefined;
|
|
18471
|
+
const seedId = storage.seedId || row.seedUid || row.seedLocalId;
|
|
18472
|
+
if (!seedId)
|
|
18473
|
+
return undefined;
|
|
18474
|
+
return {
|
|
18475
|
+
seedId,
|
|
18476
|
+
localStorageDir: storage.localStorageDir || row.localStorageDir,
|
|
18477
|
+
filenameSuffix: storage.filenameSuffix,
|
|
18478
|
+
};
|
|
18479
|
+
};
|
|
18480
|
+
return withLockedItemStorageRow(localId, (row) => {
|
|
18481
|
+
const target = targetOf(row);
|
|
18482
|
+
return target && getItemStorageFilePath(target);
|
|
18483
|
+
}, async (row) => {
|
|
18484
|
+
const content = row.propertyValue;
|
|
18485
|
+
const { fileName, filePath, localStorageDir } = await writeItemStorageFile(targetOf(row), content);
|
|
18486
|
+
await appDb
|
|
18487
|
+
.update(metadata$1)
|
|
18488
|
+
.set({ refResolvedValue: fileName, localStorageDir, refValueType: 'file', propertyValue: null })
|
|
18489
|
+
.where(drizzleOrm.and(drizzleOrm.eq(metadata$1.localId, localId), drizzleOrm.isNull(metadata$1.refResolvedValue), drizzleOrm.eq(metadata$1.propertyValue, content)));
|
|
18490
|
+
// Changed between the read and the update by a writer outside the lock: that change stands.
|
|
18491
|
+
const after = await appDb
|
|
18492
|
+
.select({ refResolvedValue: metadata$1.refResolvedValue, propertyValue: metadata$1.propertyValue })
|
|
18493
|
+
.from(metadata$1)
|
|
18494
|
+
.where(drizzleOrm.eq(metadata$1.localId, localId))
|
|
18495
|
+
.limit(1);
|
|
18496
|
+
if (after[0]?.refResolvedValue !== fileName || after[0]?.propertyValue != null)
|
|
18497
|
+
return undefined;
|
|
18498
|
+
return { fileName, filePath, localStorageDir, content };
|
|
18499
|
+
});
|
|
18500
|
+
};
|
|
18501
|
+
|
|
18502
|
+
const logger$4 = debug('seedSdk:db:migrateLegacyItemStorageRows');
|
|
18503
|
+
/** appState key recording that legacy ItemStorage rows have been written to their files. */
|
|
18504
|
+
const LEGACY_ITEM_STORAGE_MIGRATION_KEY = 'legacyItemStorageFiles:v1';
|
|
18505
|
+
/** Runs in a row that left rows unconverted because a file write failed, before giving up. */
|
|
18506
|
+
const LEGACY_ITEM_STORAGE_MIGRATION_MAX_ATTEMPTS = 3;
|
|
18507
|
+
const readMarker = async () => {
|
|
18508
|
+
const db = BaseDb.getAppDb();
|
|
18509
|
+
const rows = await db
|
|
18510
|
+
.select({ value: appState.value })
|
|
18511
|
+
.from(appState)
|
|
18512
|
+
.where(drizzleOrm.eq(appState.key, LEGACY_ITEM_STORAGE_MIGRATION_KEY))
|
|
18513
|
+
.limit(1);
|
|
18514
|
+
if (!rows.length)
|
|
18515
|
+
return undefined;
|
|
18516
|
+
try {
|
|
18517
|
+
return JSON.parse(rows[0].value ?? '');
|
|
18518
|
+
}
|
|
18519
|
+
catch {
|
|
18520
|
+
return null;
|
|
18521
|
+
}
|
|
18522
|
+
};
|
|
18523
|
+
const writeMarker = async (marker) => {
|
|
18524
|
+
const db = BaseDb.getAppDb();
|
|
18525
|
+
const now = Date.now();
|
|
18526
|
+
const value = JSON.stringify(marker);
|
|
18527
|
+
await db
|
|
18528
|
+
.insert(appState)
|
|
18529
|
+
.values({ key: LEGACY_ITEM_STORAGE_MIGRATION_KEY, value, createdAt: now, updatedAt: now })
|
|
18530
|
+
.onConflictDoUpdate({ target: appState.key, set: { value, updatedAt: now } });
|
|
18531
|
+
};
|
|
18532
|
+
/**
|
|
18533
|
+
* The storage settings of a metadata row's property: by its property_id, else by name in the
|
|
18534
|
+
* seed's own model (model names are only unique per schema). Undefined when it can't be told.
|
|
18535
|
+
*/
|
|
18536
|
+
const createStorageResolver = () => {
|
|
18537
|
+
const db = BaseDb.getAppDb();
|
|
18538
|
+
const byPropertyId = new Map();
|
|
18539
|
+
const byModelAndName = new Map();
|
|
18540
|
+
return async (row) => {
|
|
18541
|
+
if (row.propertyId != null) {
|
|
18542
|
+
if (!byPropertyId.has(row.propertyId)) {
|
|
18543
|
+
const found = await db
|
|
18544
|
+
.select({
|
|
18545
|
+
storageType: properties.storageType,
|
|
18546
|
+
localStorageDir: properties.localStorageDir,
|
|
18547
|
+
filenameSuffix: properties.filenameSuffix,
|
|
18548
|
+
})
|
|
18549
|
+
.from(properties)
|
|
18550
|
+
.where(drizzleOrm.eq(properties.id, row.propertyId))
|
|
18551
|
+
.limit(1);
|
|
18552
|
+
byPropertyId.set(row.propertyId, found[0]);
|
|
18553
|
+
}
|
|
18554
|
+
const fromId = byPropertyId.get(row.propertyId);
|
|
18555
|
+
if (fromId)
|
|
18556
|
+
return fromId;
|
|
18557
|
+
}
|
|
18558
|
+
const modelFileId = await resolveItemModelFileId({ seedLocalId: row.seedLocalId, seedUid: row.seedUid });
|
|
18559
|
+
if (!modelFileId || !row.propertyName)
|
|
18560
|
+
return undefined;
|
|
18561
|
+
const key = `${modelFileId}\u0000${row.propertyName}`;
|
|
18562
|
+
if (!byModelAndName.has(key)) {
|
|
18563
|
+
const found = await db
|
|
18564
|
+
.select({
|
|
18565
|
+
storageType: properties.storageType,
|
|
18566
|
+
localStorageDir: properties.localStorageDir,
|
|
18567
|
+
filenameSuffix: properties.filenameSuffix,
|
|
18568
|
+
})
|
|
18569
|
+
.from(properties)
|
|
18570
|
+
.innerJoin(models$1, drizzleOrm.eq(properties.modelId, models$1.id))
|
|
18571
|
+
.where(drizzleOrm.and(drizzleOrm.eq(models$1.schemaFileId, modelFileId), drizzleOrm.eq(properties.name, row.propertyName)))
|
|
18572
|
+
.limit(1);
|
|
18573
|
+
byModelAndName.set(key, found[0]);
|
|
18574
|
+
}
|
|
18575
|
+
return byModelAndName.get(key);
|
|
18576
|
+
};
|
|
18577
|
+
};
|
|
18578
|
+
/** True if `row` is the row readers show for its property: the newest of its seed's rows by that name. */
|
|
18579
|
+
const isNewestRowOfProperty = async (row) => {
|
|
18580
|
+
if (!row.seedLocalId || !row.propertyName)
|
|
18581
|
+
return false;
|
|
18582
|
+
const db = BaseDb.getAppDb();
|
|
18583
|
+
const siblings = await db
|
|
18584
|
+
.select()
|
|
18585
|
+
.from(metadata$1)
|
|
18586
|
+
.where(drizzleOrm.and(drizzleOrm.eq(metadata$1.seedLocalId, row.seedLocalId), drizzleOrm.eq(metadata$1.propertyName, row.propertyName)));
|
|
18587
|
+
return [...siblings].sort(compareMetadataRowsLatestFirst)[0]?.localId === row.localId;
|
|
18588
|
+
};
|
|
18589
|
+
/**
|
|
18590
|
+
* One-time data migration: writes the content of legacy ItemStorage draft rows to their files.
|
|
18591
|
+
*
|
|
18592
|
+
* Before Item.create / createNewItem saved ItemStorage values through the save pipeline, a value
|
|
18593
|
+
* given at creation was stored verbatim in metadata.property_value with no file. Publish converts
|
|
18594
|
+
* such a row lazily (resolveItemStorageChildFile); this converts them all up front, with the same
|
|
18595
|
+
* code ({@link convertVerbatimItemStorageRow}), so every ItemStorage draft has the shape a save
|
|
18596
|
+
* gives it: the content in `<seedUid or seedLocalId><extension>`, the row holding
|
|
18597
|
+
* refResolvedValue / localStorageDir / refValueType 'file' and no property_value.
|
|
18598
|
+
*
|
|
18599
|
+
* Only local drafts (no uid, not derived from an attestation) of properties whose properties row
|
|
18600
|
+
* says `storage_type = 'ItemStorage'` are touched, and of those only the newest row of each
|
|
18601
|
+
* seed's property: that is the row reads show and saves write to, and older rows sharing the one
|
|
18602
|
+
* file name would overwrite it with older content. Published and synced rows are never changed.
|
|
18603
|
+
*
|
|
18604
|
+
* A row that fails is logged and left as it was (publish still converts it). The run is recorded
|
|
18605
|
+
* in appState under {@link LEGACY_ITEM_STORAGE_MIGRATION_KEY}; a run with failed rows is retried
|
|
18606
|
+
* on the next start, up to {@link LEGACY_ITEM_STORAGE_MIGRATION_MAX_ATTEMPTS} runs. Safe to run
|
|
18607
|
+
* again regardless: a converted row no longer matches.
|
|
18608
|
+
*
|
|
18609
|
+
* @param options.force run even when the marker says it is done
|
|
18610
|
+
*/
|
|
18611
|
+
async function migrateLegacyItemStorageRows(options = {}) {
|
|
18612
|
+
const result = {
|
|
18613
|
+
alreadyDone: false,
|
|
18614
|
+
candidates: 0,
|
|
18615
|
+
converted: 0,
|
|
18616
|
+
failed: 0,
|
|
18617
|
+
skipped: 0,
|
|
18618
|
+
};
|
|
18619
|
+
const db = BaseDb.getAppDb();
|
|
18620
|
+
if (!db)
|
|
18621
|
+
return { ...result, alreadyDone: true };
|
|
18622
|
+
const marker = await readMarker();
|
|
18623
|
+
const previousAttempts = marker?.status === 'retry' ? marker.attempts : 0;
|
|
18624
|
+
if (!options.force && marker && marker.status === 'done') {
|
|
18625
|
+
return { ...result, alreadyDone: true };
|
|
18626
|
+
}
|
|
18627
|
+
const itemStorageNames = (await db
|
|
18628
|
+
.selectDistinct({ name: properties.name })
|
|
18629
|
+
.from(properties)
|
|
18630
|
+
.where(drizzleOrm.eq(properties.storageType, 'ItemStorage'))).map((r) => r.name);
|
|
18631
|
+
const candidates = itemStorageNames.length
|
|
18632
|
+
? await db
|
|
18633
|
+
.select()
|
|
18634
|
+
.from(metadata$1)
|
|
18635
|
+
.where(drizzleOrm.and(drizzleOrm.inArray(metadata$1.propertyName, itemStorageNames), drizzleOrm.or(drizzleOrm.isNull(metadata$1.uid), drizzleOrm.eq(metadata$1.uid, '')), drizzleOrm.isNull(metadata$1.derivedFromUid), drizzleOrm.isNull(metadata$1.refResolvedValue), drizzleOrm.isNotNull(metadata$1.propertyValue), drizzleOrm.ne(metadata$1.propertyValue, '')))
|
|
18636
|
+
: [];
|
|
18637
|
+
const resolveStorage = createStorageResolver();
|
|
18638
|
+
for (const row of candidates) {
|
|
18639
|
+
if (!row.localId || !isVerbatimItemStorageDraftRow(row))
|
|
18640
|
+
continue;
|
|
18641
|
+
try {
|
|
18642
|
+
const storage = await resolveStorage(row);
|
|
18643
|
+
if (storage?.storageType !== 'ItemStorage')
|
|
18644
|
+
continue;
|
|
18645
|
+
result.candidates++;
|
|
18646
|
+
if (!(await isNewestRowOfProperty(row))) {
|
|
18647
|
+
result.skipped++;
|
|
18648
|
+
continue;
|
|
18649
|
+
}
|
|
18650
|
+
const converted = await convertVerbatimItemStorageRow(row.localId, {
|
|
18651
|
+
localStorageDir: storage.localStorageDir,
|
|
18652
|
+
filenameSuffix: storage.filenameSuffix,
|
|
18653
|
+
});
|
|
18654
|
+
if (converted) {
|
|
18655
|
+
result.converted++;
|
|
18656
|
+
logger$4(`wrote ${row.propertyName} of ${row.seedLocalId} to ${converted.filePath}`);
|
|
18657
|
+
}
|
|
18658
|
+
else {
|
|
18659
|
+
result.skipped++;
|
|
18660
|
+
}
|
|
18661
|
+
}
|
|
18662
|
+
catch (error) {
|
|
18663
|
+
result.failed++;
|
|
18664
|
+
logger$4(`converting ${row.propertyName} of ${row.seedLocalId} (${row.localId}) failed; left as is`, error);
|
|
18665
|
+
}
|
|
18666
|
+
}
|
|
18667
|
+
const attempts = previousAttempts + 1;
|
|
18668
|
+
await writeMarker({
|
|
18669
|
+
status: result.failed > 0 && attempts < LEGACY_ITEM_STORAGE_MIGRATION_MAX_ATTEMPTS ? 'retry' : 'done',
|
|
18670
|
+
attempts,
|
|
18671
|
+
converted: result.converted,
|
|
18672
|
+
failed: result.failed,
|
|
18673
|
+
at: Date.now(),
|
|
18674
|
+
});
|
|
18675
|
+
if (result.candidates > 0) {
|
|
18676
|
+
logger$4(`legacy ItemStorage rows: ${result.converted} converted, ${result.failed} failed, ` +
|
|
18677
|
+
`${result.skipped} skipped of ${result.candidates}`);
|
|
18678
|
+
}
|
|
18679
|
+
return result;
|
|
18680
|
+
}
|
|
18681
|
+
let running;
|
|
18682
|
+
let deferredUntilLeader = false;
|
|
18683
|
+
const runInBackground = () => {
|
|
18684
|
+
running ?? (running = withDbTabLock('item-storage-migration', () => migrateLegacyItemStorageRows())
|
|
18685
|
+
.then(() => undefined)
|
|
18686
|
+
.catch((error) => {
|
|
18687
|
+
// Never fails init: rows stay as they were and publish converts them lazily.
|
|
18688
|
+
logger$4('legacy ItemStorage migration failed', error);
|
|
18689
|
+
})
|
|
18690
|
+
.finally(() => {
|
|
18691
|
+
running = undefined;
|
|
18692
|
+
}));
|
|
18693
|
+
return running;
|
|
18694
|
+
};
|
|
18695
|
+
/**
|
|
18696
|
+
* Starts {@link migrateLegacyItemStorageRows} in the background once client init has finished
|
|
18697
|
+
* (the properties table then holds the current storage settings). It runs in the leader tab only
|
|
18698
|
+
* (docs/MULTI_TAB.md), under a `seed:item-storage-migration:<db>` lock in case several tabs lead
|
|
18699
|
+
* (`multiTab: 'off'`); a tab that isn't leader runs it if it takes over. Never throws.
|
|
18700
|
+
*/
|
|
18701
|
+
function startLegacyItemStorageMigration() {
|
|
18702
|
+
if (isLeaderTab()) {
|
|
18703
|
+
void runInBackground();
|
|
18704
|
+
return;
|
|
18705
|
+
}
|
|
18706
|
+
if (deferredUntilLeader)
|
|
18707
|
+
return;
|
|
18708
|
+
deferredUntilLeader = true;
|
|
18709
|
+
void whenLeaderTab().then(() => {
|
|
18710
|
+
deferredUntilLeader = false;
|
|
18711
|
+
void runInBackground();
|
|
18712
|
+
});
|
|
18713
|
+
}
|
|
18714
|
+
|
|
18098
18715
|
const { UNINITIALIZED, PLATFORM_CLASSES_INIT, FILE_SYSTEM_INIT, DB_INIT, SAVE_CONFIG, PROCESS_SCHEMA_FILES, ADD_MODELS_TO_STORE, ADD_MODELS_TO_DB, IDLE, INIT_FAILED, } = exports.ClientManagerState;
|
|
18099
18716
|
const { UPDATE_CONTEXT, PLATFORM_CLASSES_READY, FILE_SYSTEM_READY, DB_READY, SAVE_CONFIG_SUCCESS, SAVE_APP_STATE_SUCCESS, SAVE_APP_STATE_ERROR, SET_ADDRESSES, ADD_MODELS_TO_STORE_SUCCESS, ADD_MODELS_TO_DB_SUCCESS, PROCESS_SCHEMA_FILES_SUCCESS, } = ClientManagerEvents;
|
|
18100
18717
|
const logSyncAfterAddressSave = debug("seedSdk:client:clientManagerMachine:syncAfterAddressSave");
|
|
@@ -18315,9 +18932,15 @@ const clientManagerMachine = xstate.setup({
|
|
|
18315
18932
|
},
|
|
18316
18933
|
},
|
|
18317
18934
|
[IDLE]: {
|
|
18318
|
-
entry:
|
|
18319
|
-
|
|
18320
|
-
|
|
18935
|
+
entry: [
|
|
18936
|
+
xstate.assign({
|
|
18937
|
+
isInitialized: true,
|
|
18938
|
+
}),
|
|
18939
|
+
// One-time data migration that writes files, so it can't be a SQL migration in prepareDb.
|
|
18940
|
+
// In the background after init: the app is usable at once, the properties table holds the
|
|
18941
|
+
// current storage settings by now, and a failure never fails init.
|
|
18942
|
+
() => startLegacyItemStorageMigration(),
|
|
18943
|
+
],
|
|
18321
18944
|
on: {
|
|
18322
18945
|
[SAVE_APP_STATE_SUCCESS]: {
|
|
18323
18946
|
actions: [
|
|
@@ -19391,7 +20014,7 @@ const saveDraftLogger = debug('seedSdk:model:saveDraftToDb');
|
|
|
19391
20014
|
// Lazy import cache for ModelProperty to avoid circular dependency
|
|
19392
20015
|
// Eagerly start loading to minimize delay on first access
|
|
19393
20016
|
let ModelPropertyClass = null;
|
|
19394
|
-
const modelPropertyImportPromise = Promise.resolve().then(function () { return require('./ModelProperty-
|
|
20017
|
+
const modelPropertyImportPromise = Promise.resolve().then(function () { return require('./ModelProperty-DXZ39Wyv.js'); })
|
|
19395
20018
|
.then(module => {
|
|
19396
20019
|
ModelPropertyClass = module.ModelProperty;
|
|
19397
20020
|
return ModelPropertyClass;
|
|
@@ -19407,7 +20030,7 @@ function getModelProperty() {
|
|
|
19407
20030
|
return ModelPropertyClass;
|
|
19408
20031
|
}
|
|
19409
20032
|
// Lazy import for Schema to avoid circular dependency
|
|
19410
|
-
const schemaImportPromise = Promise.resolve().then(function () { return require('./Schema-
|
|
20033
|
+
const schemaImportPromise = Promise.resolve().then(function () { return require('./Schema-DdP109QD.js'); })
|
|
19411
20034
|
.then(module => module.Schema)
|
|
19412
20035
|
.catch(() => null);
|
|
19413
20036
|
// WeakMap to store mutable state per Model instance
|
|
@@ -21472,7 +22095,7 @@ function resolveSdkDrizzleMigrationsDir(sdkRootDir) {
|
|
|
21472
22095
|
})();
|
|
21473
22096
|
// Also resolve relative to this module so temp-cwd tests / odd roots still find
|
|
21474
22097
|
// packages/sdk/src/db/drizzle (or dist/db/drizzle after publish).
|
|
21475
|
-
const moduleDir = path.dirname(node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cjs/index-
|
|
22098
|
+
const moduleDir = path.dirname(node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cjs/index-C4XlgCJ4.js', document.baseURI).href))));
|
|
21476
22099
|
const candidates = [
|
|
21477
22100
|
path.join(root, 'db', 'drizzle'), // production: sdkRoot is dist/
|
|
21478
22101
|
path.join(root, 'dist', 'db', 'drizzle'), // sdk package root with built dist
|
|
@@ -21542,10 +22165,10 @@ class NodeDb {
|
|
|
21542
22165
|
logger$1('[Db.prepareDb] running migrations from %s', outDir);
|
|
21543
22166
|
await migrator.migrate(db, { migrationsFolder: outDir });
|
|
21544
22167
|
this.db = db;
|
|
21545
|
-
const { backfillMetadataPropertyIds } = await Promise.resolve().then(function () { return require('./backfillMetadataPropertyIds-
|
|
22168
|
+
const { backfillMetadataPropertyIds } = await Promise.resolve().then(function () { return require('./backfillMetadataPropertyIds-CPYoaMl4.js'); });
|
|
21546
22169
|
await backfillMetadataPropertyIds();
|
|
21547
22170
|
// One-time: clear item data whose model can't be determined (seeds.model_file_id, migration 0015)
|
|
21548
|
-
const { resetAmbiguousLegacyItemData } = await Promise.resolve().then(function () { return require('./resetAmbiguousLegacyItemData-
|
|
22171
|
+
const { resetAmbiguousLegacyItemData } = await Promise.resolve().then(function () { return require('./resetAmbiguousLegacyItemData-Bu_MBBYt.js'); });
|
|
21549
22172
|
await resetAmbiguousLegacyItemData();
|
|
21550
22173
|
return this.db;
|
|
21551
22174
|
}
|
|
@@ -22076,10 +22699,13 @@ exports.anyEvictionSince = anyEvictionSince;
|
|
|
22076
22699
|
exports.appState = appState;
|
|
22077
22700
|
exports.arweaveL1FinalizeJobs = arweaveL1FinalizeJobs;
|
|
22078
22701
|
exports.assertLocalDbChain = assertLocalDbChain;
|
|
22702
|
+
exports.buildArweaveContentTags = buildArweaveContentTags;
|
|
22079
22703
|
exports.chunkValues = chunkValues;
|
|
22080
22704
|
exports.clearDestroySubscriptions = clearDestroySubscriptions;
|
|
22081
22705
|
exports.commandExists = commandExists;
|
|
22706
|
+
exports.compareMetadataRowsLatestFirst = compareMetadataRowsLatestFirst;
|
|
22082
22707
|
exports.config = config;
|
|
22708
|
+
exports.convertVerbatimItemStorageRow = convertVerbatimItemStorageRow;
|
|
22083
22709
|
exports.createModelsFromJson = createModelsFromJson;
|
|
22084
22710
|
exports.createReactiveProxy = createReactiveProxy;
|
|
22085
22711
|
exports.currentEvictionEpoch = currentEvictionEpoch;
|
|
@@ -22095,13 +22721,15 @@ exports.forceRemoveFromCaches = forceRemoveFromCaches;
|
|
|
22095
22721
|
exports.generateId = generateId;
|
|
22096
22722
|
exports.getAllAddressesFromDb = getAllAddressesFromDb;
|
|
22097
22723
|
exports.getClient = getClient;
|
|
22098
|
-
exports.
|
|
22724
|
+
exports.getContentSha256 = getContentSha256;
|
|
22099
22725
|
exports.getCorrectId = getCorrectId;
|
|
22100
22726
|
exports.getEasSchemaUidForModel = getEasSchemaUidForModel;
|
|
22101
22727
|
exports.getGetAdditionalSyncAddresses = getGetAdditionalSyncAddresses;
|
|
22102
22728
|
exports.getItemData = getItemData;
|
|
22103
22729
|
exports.getItemModelScope = getItemModelScope;
|
|
22730
|
+
exports.getItemStorageLocalFileName = getItemStorageLocalFileName;
|
|
22104
22731
|
exports.getItemStoragePropertiesForModel = getItemStoragePropertiesForModel;
|
|
22732
|
+
exports.getItemStorageSubdir = getItemStorageSubdir;
|
|
22105
22733
|
exports.getLatestPublishedVersionRow = getLatestPublishedVersionRow;
|
|
22106
22734
|
exports.getLatestSchemaVersion = getLatestSchemaVersion;
|
|
22107
22735
|
exports.getModelId = getModelId;
|
|
@@ -22109,6 +22737,7 @@ exports.getModelIdByFileId = getModelIdByFileId;
|
|
|
22109
22737
|
exports.getPropertyData = getPropertyData$1;
|
|
22110
22738
|
exports.getPropertyIdForModelAndName = getPropertyIdForModelAndName;
|
|
22111
22739
|
exports.getRefValueType = getRefValueType;
|
|
22740
|
+
exports.getStorageDirForDataType = getStorageDirForDataType;
|
|
22112
22741
|
exports.htmlEmbeddedImageCoPublish = htmlEmbeddedImageCoPublish;
|
|
22113
22742
|
exports.importJsonSchema = importJsonSchema;
|
|
22114
22743
|
exports.isActorStopped = isActorStopped;
|
|
@@ -22116,6 +22745,7 @@ exports.isDataType = isDataType;
|
|
|
22116
22745
|
exports.isInternalSchema = isInternalSchema;
|
|
22117
22746
|
exports.isPublishedSeedRef = isPublishedSeedRef;
|
|
22118
22747
|
exports.isStorageSeedRef = isStorageSeedRef;
|
|
22748
|
+
exports.isVerbatimItemStorageDraftRow = isVerbatimItemStorageDraftRow;
|
|
22119
22749
|
exports.isVersionRevoked = isVersionRevoked;
|
|
22120
22750
|
exports.listCompleteSchemaFiles = listCompleteSchemaFiles;
|
|
22121
22751
|
exports.listRelationEasPropertyName = listRelationEasPropertyName;
|
|
@@ -22168,4 +22798,4 @@ exports.withSeed = withSeed;
|
|
|
22168
22798
|
exports.writeInBatches = writeInBatches;
|
|
22169
22799
|
exports.writeProcessMachine = writeProcessMachine;
|
|
22170
22800
|
exports.writeToDatabase = writeToDatabase$1;
|
|
22171
|
-
//# sourceMappingURL=index-
|
|
22801
|
+
//# sourceMappingURL=index-C4XlgCJ4.js.map
|