@seedprotocol/sdk 0.7.9-next.1189.g5e63267 → 0.7.9

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.
Files changed (581) hide show
  1. package/dist/BaseFileManager-BAlySv8w.js +650 -0
  2. package/dist/BaseFileManager-BAlySv8w.js.map +1 -0
  3. package/dist/{Item-CG5IhPtL.js → Item-Bf4kWKpH.js} +5576 -6649
  4. package/dist/Item-Bf4kWKpH.js.map +1 -0
  5. package/dist/{ModelProperty-DDD47KVm.js → ModelProperty-BlaEgb_U.js} +345 -709
  6. package/dist/ModelProperty-BlaEgb_U.js.map +1 -0
  7. package/dist/Schema-J_1f6p0c.js +1456 -0
  8. package/dist/Schema-J_1f6p0c.js.map +1 -0
  9. package/dist/{SchemaValidationService-CZzKNL04.js → SchemaValidationService-CnmKyzR-.js} +6 -9
  10. package/dist/{SchemaValidationService-CZzKNL04.js.map → SchemaValidationService-CnmKyzR-.js.map} +1 -1
  11. package/dist/{configurePlatform-Dukm2QwO.js → configurePlatform-CiOJ1R3q.js} +3 -12
  12. package/dist/configurePlatform-CiOJ1R3q.js.map +1 -0
  13. package/dist/db/drizzle/0026_add_content_hash_to_schemas.sql +1 -0
  14. package/dist/db/drizzle/0027_link_metadata_property_ids.sql +74 -0
  15. package/dist/db/drizzle/0028_add_definition_to_properties.sql +17 -0
  16. package/dist/db/drizzle/0029_dedupe_chain_uids_add_unique_indexes.sql +120 -0
  17. package/dist/db/drizzle/meta/0026_snapshot.json +1659 -0
  18. package/dist/db/drizzle/meta/0027_snapshot.json +1659 -0
  19. package/dist/db/drizzle/meta/0028_snapshot.json +1666 -0
  20. package/dist/db/drizzle/meta/0029_snapshot.json +1691 -0
  21. package/dist/db/drizzle/meta/_journal.json +29 -1
  22. package/dist/{detachRowsBeforeDelete-Di1enVUF.js → detachRowsBeforeDelete-sEaUwlOU.js} +2 -13
  23. package/dist/{detachRowsBeforeDelete-Di1enVUF.js.map → detachRowsBeforeDelete-sEaUwlOU.js.map} +1 -1
  24. package/dist/dist/{index-rOTW-2QZ.d.ts → BaseFileManager-BAlySv8w.d.ts} +1407 -2700
  25. package/dist/dist/BaseFileManager-BAlySv8w.d.ts.map +1 -0
  26. package/dist/dist/{configurePlatform-Dukm2QwO.d.ts → configurePlatform-CiOJ1R3q.d.ts} +1 -1
  27. package/dist/dist/configurePlatform-CiOJ1R3q.d.ts.map +1 -0
  28. package/dist/dist/{index-DxHpPNfn.d.ts → index-D0V-mjCV.d.ts} +39 -1
  29. package/dist/dist/{index-DxHpPNfn.d.ts.map → index-D0V-mjCV.d.ts.map} +1 -1
  30. package/dist/dist/{index.node-WsEJCOwC.d.ts → index.node-CYPemH-t.d.ts} +157 -5
  31. package/dist/dist/{index.node-WsEJCOwC.d.ts.map → index.node-CYPemH-t.d.ts.map} +1 -1
  32. package/dist/dist/platform.node.d.ts +2 -2
  33. package/dist/eas-wB_fQ8Tc.js +32 -0
  34. package/dist/eas-wB_fQ8Tc.js.map +1 -0
  35. package/dist/errors-DLSoqSHG.js +6550 -0
  36. package/dist/errors-DLSoqSHG.js.map +1 -0
  37. package/dist/getPublishPayload-BnIwpwz1.js +760 -0
  38. package/dist/getPublishPayload-BnIwpwz1.js.map +1 -0
  39. package/dist/getPublishPendingDiff-D6_-A5ns.js +125 -0
  40. package/dist/getPublishPendingDiff-D6_-A5ns.js.map +1 -0
  41. package/dist/getPublishUploads-sS6JfPgz.js +783 -0
  42. package/dist/getPublishUploads-sS6JfPgz.js.map +1 -0
  43. package/dist/{index-DQoyhp2c.js → index-BVBmz_cc.js} +11 -14
  44. package/dist/index-BVBmz_cc.js.map +1 -0
  45. package/dist/index-CYGWPoOx.js +27 -0
  46. package/dist/index-CYGWPoOx.js.map +1 -0
  47. package/dist/{index-DxHpPNfn.js → index-D0V-mjCV.js} +2 -2
  48. package/dist/index-D0V-mjCV.js.map +1 -0
  49. package/dist/index-DmTtj1nk.js +817 -0
  50. package/dist/index-DmTtj1nk.js.map +1 -0
  51. package/dist/{index.browser-QPWBkLJA.js → index.browser-CUmBVYen.js} +397 -356
  52. package/dist/index.browser-CUmBVYen.js.map +1 -0
  53. package/dist/{index.node-WsEJCOwC.js → index.node-CYPemH-t.js} +10 -10
  54. package/dist/index.node-CYPemH-t.js.map +1 -0
  55. package/dist/main.js +23 -1075
  56. package/dist/main.js.map +1 -1
  57. package/dist/modelPropertyRecordSchemas-C7UgA5TH.js +48 -0
  58. package/dist/modelPropertyRecordSchemas-C7UgA5TH.js.map +1 -0
  59. package/dist/node.js +14 -18
  60. package/dist/node.js.map +1 -1
  61. package/dist/platform.browser.js +8 -13
  62. package/dist/platform.browser.js.map +1 -1
  63. package/dist/platform.node.js +6 -16
  64. package/dist/platform.node.js.map +1 -1
  65. package/dist/property-BxUYznH0.js +87 -0
  66. package/dist/property-BxUYznH0.js.map +1 -0
  67. package/dist/publishGraph-EcB40Uii.js +311 -0
  68. package/dist/publishGraph-EcB40Uii.js.map +1 -0
  69. package/dist/readHtmlBodyForStorageSeed-D6KPtWoR.js +110 -0
  70. package/dist/readHtmlBodyForStorageSeed-D6KPtWoR.js.map +1 -0
  71. package/dist/seedSchema/ModelSchema.ts +4 -0
  72. package/dist/seedSchema/ModelSchemaSchema.ts +5 -2
  73. package/dist/seedSchema/PendingWriteSchema.ts +4 -1
  74. package/dist/seedSchema/SchemaSchema.ts +3 -0
  75. package/dist/seedSchema/SeedSchema.ts +8 -2
  76. package/dist/seedSchema/VersionSchema.ts +7 -2
  77. package/dist/seedSchema/attestedUid.ts +10 -0
  78. package/dist/src/Item/Item.d.ts +32 -30
  79. package/dist/src/Item/Item.d.ts.map +1 -1
  80. package/dist/src/Item/loadItem.d.ts +12 -0
  81. package/dist/src/Item/loadItem.d.ts.map +1 -0
  82. package/dist/src/Item/service/itemMachineSingle.d.ts +2 -13
  83. package/dist/src/Item/service/itemMachineSingle.d.ts.map +1 -1
  84. package/dist/src/ItemProperty/ItemProperty.d.ts +77 -46
  85. package/dist/src/ItemProperty/ItemProperty.d.ts.map +1 -1
  86. package/dist/src/ItemProperty/loadProperty.d.ts +11 -0
  87. package/dist/src/ItemProperty/loadProperty.d.ts.map +1 -0
  88. package/dist/src/ItemProperty/propertyWrite.d.ts +54 -0
  89. package/dist/src/ItemProperty/propertyWrite.d.ts.map +1 -0
  90. package/dist/src/ItemProperty/{service/actors/saveValueToDb/rowContext.d.ts → rowContext.d.ts} +1 -1
  91. package/dist/src/ItemProperty/rowContext.d.ts.map +1 -0
  92. package/dist/src/ItemProperty/service/actors/hydrateFromDb.d.ts +6 -0
  93. package/dist/src/ItemProperty/service/actors/hydrateFromDb.d.ts.map +1 -1
  94. package/dist/src/ItemProperty/service/actors/modelPropertyRecordSchemas.d.ts +5 -15
  95. package/dist/src/ItemProperty/service/actors/modelPropertyRecordSchemas.d.ts.map +1 -1
  96. package/dist/src/ItemProperty/service/propertyMachine.d.ts +4 -50
  97. package/dist/src/ItemProperty/service/propertyMachine.d.ts.map +1 -1
  98. package/dist/src/Model/Model.d.ts +26 -56
  99. package/dist/src/Model/Model.d.ts.map +1 -1
  100. package/dist/src/Model/createModelProperties.d.ts +10 -0
  101. package/dist/src/Model/createModelProperties.d.ts.map +1 -0
  102. package/dist/src/Model/loadModel.d.ts +13 -0
  103. package/dist/src/Model/loadModel.d.ts.map +1 -0
  104. package/dist/src/Model/service/actors/validateModel.d.ts.map +1 -1
  105. package/dist/src/Model/service/modelMachine.d.ts +5 -114
  106. package/dist/src/Model/service/modelMachine.d.ts.map +1 -1
  107. package/dist/src/ModelProperty/ModelProperty.d.ts +44 -37
  108. package/dist/src/ModelProperty/ModelProperty.d.ts.map +1 -1
  109. package/dist/src/ModelProperty/service/actors/compareAndMarkDraft.d.ts.map +1 -1
  110. package/dist/src/ModelProperty/service/actors/validateProperty.d.ts.map +1 -1
  111. package/dist/src/ModelProperty/service/modelPropertyMachine.d.ts +6 -77
  112. package/dist/src/ModelProperty/service/modelPropertyMachine.d.ts.map +1 -1
  113. package/dist/src/Schema/Schema.d.ts +36 -37
  114. package/dist/src/Schema/Schema.d.ts.map +1 -1
  115. package/dist/src/Schema/loadOrCreateSchema.d.ts +21 -0
  116. package/dist/src/Schema/loadOrCreateSchema.d.ts.map +1 -0
  117. package/dist/src/Schema/service/schemaMachine.d.ts +7 -125
  118. package/dist/src/Schema/service/schemaMachine.d.ts.map +1 -1
  119. package/dist/src/browser/db/Db.d.ts.map +1 -1
  120. package/dist/src/browser/db/changeFeed.d.ts +2 -18
  121. package/dist/src/browser/db/changeFeed.d.ts.map +1 -1
  122. package/dist/src/browser/db/drizzleFiles.d.ts +6 -2
  123. package/dist/src/browser/db/drizzleFiles.d.ts.map +1 -1
  124. package/dist/src/browser/workers/filesDownload.d.ts.map +1 -1
  125. package/dist/src/browser/workers/imageResize.d.ts.map +1 -1
  126. package/dist/src/client/ClientManager.d.ts +90 -57
  127. package/dist/src/client/ClientManager.d.ts.map +1 -1
  128. package/dist/src/client/actors/addModelsToDb.d.ts +9 -7
  129. package/dist/src/client/actors/addModelsToDb.d.ts.map +1 -1
  130. package/dist/src/client/actors/dbInit.d.ts +3 -3
  131. package/dist/src/client/actors/dbInit.d.ts.map +1 -1
  132. package/dist/src/client/actors/fileSystemInit.d.ts +3 -3
  133. package/dist/src/client/actors/fileSystemInit.d.ts.map +1 -1
  134. package/dist/src/client/actors/platformClassesInit.d.ts +10 -5
  135. package/dist/src/client/actors/platformClassesInit.d.ts.map +1 -1
  136. package/dist/src/client/actors/processSchemaFiles.d.ts +18 -3
  137. package/dist/src/client/actors/processSchemaFiles.d.ts.map +1 -1
  138. package/dist/src/client/actors/saveConfig.d.ts +6 -3
  139. package/dist/src/client/actors/saveConfig.d.ts.map +1 -1
  140. package/dist/src/client/clientManagerMachine.d.ts +22 -20
  141. package/dist/src/client/clientManagerMachine.d.ts.map +1 -1
  142. package/dist/src/client/constants.d.ts +1 -9
  143. package/dist/src/client/constants.d.ts.map +1 -1
  144. package/dist/src/client/readiness.d.ts +26 -0
  145. package/dist/src/client/readiness.d.ts.map +1 -0
  146. package/dist/src/db/read/currentPropertyRow.d.ts +61 -0
  147. package/dist/src/db/read/currentPropertyRow.d.ts.map +1 -0
  148. package/dist/src/db/read/findSeedRow.d.ts +20 -0
  149. package/dist/src/db/read/findSeedRow.d.ts.map +1 -0
  150. package/dist/src/db/read/findSeedRowsByUid.d.ts +4 -1
  151. package/dist/src/db/read/findSeedRowsByUid.d.ts.map +1 -1
  152. package/dist/src/db/read/getAttesterForSeed.d.ts +1 -1
  153. package/dist/src/db/read/getAttesterForSeed.d.ts.map +1 -1
  154. package/dist/src/db/read/getItemData.d.ts.map +1 -1
  155. package/dist/src/db/read/getItemProperties.d.ts +5 -0
  156. package/dist/src/db/read/getItemProperties.d.ts.map +1 -1
  157. package/dist/src/db/read/getMetadataAttestationUidsForSeedUid.d.ts.map +1 -1
  158. package/dist/src/db/read/getModelSchemas.d.ts +0 -7
  159. package/dist/src/db/read/getModelSchemas.d.ts.map +1 -1
  160. package/dist/src/db/read/getModelsData.d.ts.map +1 -1
  161. package/dist/src/db/read/getPropertyClear.d.ts +5 -1
  162. package/dist/src/db/read/getPropertyClear.d.ts.map +1 -1
  163. package/dist/src/db/read/getPublishPayload.d.ts +35 -40
  164. package/dist/src/db/read/getPublishPayload.d.ts.map +1 -1
  165. package/dist/src/db/read/getPublishPendingDiff.d.ts +2 -2
  166. package/dist/src/db/read/getPublishPendingDiff.d.ts.map +1 -1
  167. package/dist/src/db/read/getPublishUploads.d.ts +59 -28
  168. package/dist/src/db/read/getPublishUploads.d.ts.map +1 -1
  169. package/dist/src/db/read/getSchemaUidForModel.d.ts.map +1 -1
  170. package/dist/src/db/read/getSeedPublishState.d.ts.map +1 -1
  171. package/dist/src/db/read/getSeedPublisher.d.ts.map +1 -1
  172. package/dist/src/db/read/getVersionsForSeedUid.d.ts.map +1 -1
  173. package/dist/src/db/read/isSeedRevoked.d.ts.map +1 -1
  174. package/dist/src/db/read/localModelSchemaLookups.d.ts +8 -0
  175. package/dist/src/db/read/localModelSchemaLookups.d.ts.map +1 -0
  176. package/dist/src/db/read/metadataProperty.d.ts +104 -0
  177. package/dist/src/db/read/metadataProperty.d.ts.map +1 -0
  178. package/dist/src/db/read/publishGraph.d.ts +148 -0
  179. package/dist/src/db/read/publishGraph.d.ts.map +1 -0
  180. package/dist/src/db/read/resolveModelRecord.d.ts +26 -2
  181. package/dist/src/db/read/resolveModelRecord.d.ts.map +1 -1
  182. package/dist/src/db/read/resolveRelatedSeedRef.d.ts +1 -7
  183. package/dist/src/db/read/resolveRelatedSeedRef.d.ts.map +1 -1
  184. package/dist/src/db/read/subqueries/metadataLatest.d.ts +3 -1
  185. package/dist/src/db/read/subqueries/metadataLatest.d.ts.map +1 -1
  186. package/dist/src/db/read/summarizePublishWork.d.ts +13 -5
  187. package/dist/src/db/read/summarizePublishWork.d.ts.map +1 -1
  188. package/dist/src/db/write/applyPropertyAttestationUidsFromPublish.d.ts +20 -21
  189. package/dist/src/db/write/applyPropertyAttestationUidsFromPublish.d.ts.map +1 -1
  190. package/dist/src/db/write/createMetadata.d.ts.map +1 -1
  191. package/dist/src/db/write/createNewItem.d.ts.map +1 -1
  192. package/dist/src/db/write/createSeeds.d.ts +5 -1
  193. package/dist/src/db/write/createSeeds.d.ts.map +1 -1
  194. package/dist/src/db/write/definitions.d.ts +127 -0
  195. package/dist/src/db/write/definitions.d.ts.map +1 -0
  196. package/dist/src/db/write/mergeChainRows.d.ts +22 -0
  197. package/dist/src/db/write/mergeChainRows.d.ts.map +1 -0
  198. package/dist/src/db/write/saveDefinition.d.ts +29 -0
  199. package/dist/src/db/write/saveDefinition.d.ts.map +1 -0
  200. package/dist/src/db/write/updateSeedUid.d.ts +4 -2
  201. package/dist/src/db/write/updateSeedUid.d.ts.map +1 -1
  202. package/dist/src/db/write/updateVersionUid.d.ts +12 -4
  203. package/dist/src/db/write/updateVersionUid.d.ts.map +1 -1
  204. package/dist/src/db/write/writeModelUids.d.ts +10 -0
  205. package/dist/src/db/write/writeModelUids.d.ts.map +1 -0
  206. package/dist/src/eas.d.ts +8 -6
  207. package/dist/src/eas.d.ts.map +1 -1
  208. package/dist/src/events/files/download.d.ts.map +1 -1
  209. package/dist/src/events/item/syncDbWithEas.d.ts.map +1 -1
  210. package/dist/src/helpers/arweaveContentTags.d.ts +1 -1
  211. package/dist/src/helpers/arweaveContentTags.d.ts.map +1 -1
  212. package/dist/src/helpers/compareMetadataRowsLatestFirst.d.ts +18 -3
  213. package/dist/src/helpers/compareMetadataRowsLatestFirst.d.ts.map +1 -1
  214. package/dist/src/helpers/constants.d.ts +5 -2
  215. package/dist/src/helpers/constants.d.ts.map +1 -1
  216. package/dist/src/helpers/dataUrl.d.ts +10 -0
  217. package/dist/src/helpers/dataUrl.d.ts.map +1 -0
  218. package/dist/src/helpers/db.d.ts +54 -105
  219. package/dist/src/helpers/db.d.ts.map +1 -1
  220. package/dist/src/helpers/ensureImageLocal.d.ts.map +1 -1
  221. package/dist/src/helpers/entity/contextUpdate.d.ts +20 -0
  222. package/dist/src/helpers/entity/contextUpdate.d.ts.map +1 -0
  223. package/dist/src/helpers/entity/draftSaveErrors.d.ts +5 -0
  224. package/dist/src/helpers/entity/draftSaveErrors.d.ts.map +1 -1
  225. package/dist/src/helpers/entity/entityCache.d.ts +65 -3
  226. package/dist/src/helpers/entity/entityCache.d.ts.map +1 -1
  227. package/dist/src/helpers/entity/entityCommon.d.ts +0 -22
  228. package/dist/src/helpers/entity/entityCommon.d.ts.map +1 -1
  229. package/dist/src/helpers/entity/entityLifecycle.d.ts +75 -0
  230. package/dist/src/helpers/entity/entityLifecycle.d.ts.map +1 -0
  231. package/dist/src/helpers/entity/entityLiveQuery.d.ts +1 -23
  232. package/dist/src/helpers/entity/entityLiveQuery.d.ts.map +1 -1
  233. package/dist/src/helpers/htmlEmbeddedDataUriPublish.d.ts +4 -9
  234. package/dist/src/helpers/htmlEmbeddedDataUriPublish.d.ts.map +1 -1
  235. package/dist/src/helpers/index.d.ts +2 -24
  236. package/dist/src/helpers/index.d.ts.map +1 -1
  237. package/dist/src/helpers/localImageBlobUrl.d.ts +7 -0
  238. package/dist/src/helpers/localImageBlobUrl.d.ts.map +1 -0
  239. package/dist/src/helpers/mediaRef.d.ts +3 -70
  240. package/dist/src/helpers/mediaRef.d.ts.map +1 -1
  241. package/dist/src/helpers/metadataPropertyNames.d.ts +34 -26
  242. package/dist/src/helpers/metadataPropertyNames.d.ts.map +1 -1
  243. package/dist/src/helpers/mimeTypes.d.ts +2 -1
  244. package/dist/src/helpers/mimeTypes.d.ts.map +1 -1
  245. package/dist/src/helpers/model.d.ts.map +1 -1
  246. package/dist/src/helpers/ownership.d.ts.map +1 -1
  247. package/dist/src/helpers/property.d.ts +21 -26
  248. package/dist/src/helpers/property.d.ts.map +1 -1
  249. package/dist/src/helpers/propertyDefinition.d.ts +59 -0
  250. package/dist/src/helpers/propertyDefinition.d.ts.map +1 -0
  251. package/dist/src/helpers/readHtmlBodyForStorageSeed.d.ts.map +1 -1
  252. package/dist/src/helpers/relationSeedRef.d.ts +0 -7
  253. package/dist/src/helpers/relationSeedRef.d.ts.map +1 -1
  254. package/dist/src/helpers/schema.d.ts +24 -38
  255. package/dist/src/helpers/schema.d.ts.map +1 -1
  256. package/dist/src/helpers/schemaApply.d.ts +135 -0
  257. package/dist/src/helpers/schemaApply.d.ts.map +1 -0
  258. package/dist/src/helpers/tabEvents.d.ts.map +1 -1
  259. package/dist/src/helpers/updateSchema.d.ts +2 -18
  260. package/dist/src/helpers/updateSchema.d.ts.map +1 -1
  261. package/dist/src/imports/index.d.ts +1 -1
  262. package/dist/src/imports/index.d.ts.map +1 -1
  263. package/dist/src/imports/json.d.ts +8 -78
  264. package/dist/src/imports/json.d.ts.map +1 -1
  265. package/dist/src/index.d.ts +13 -13
  266. package/dist/src/index.d.ts.map +1 -1
  267. package/dist/src/interfaces/IItem.d.ts +5 -0
  268. package/dist/src/interfaces/IItem.d.ts.map +1 -1
  269. package/dist/src/interfaces/IItemProperty.d.ts +9 -7
  270. package/dist/src/interfaces/IItemProperty.d.ts.map +1 -1
  271. package/dist/src/node/db/Db.d.ts.map +1 -1
  272. package/dist/src/node/index.d.ts +2 -1
  273. package/dist/src/node/index.d.ts.map +1 -1
  274. package/dist/src/outbox/conflicts.d.ts +2 -0
  275. package/dist/src/outbox/conflicts.d.ts.map +1 -1
  276. package/dist/src/outbox/itemAppliers.d.ts +63 -14
  277. package/dist/src/outbox/itemAppliers.d.ts.map +1 -1
  278. package/dist/src/outbox/outbox.d.ts +14 -3
  279. package/dist/src/outbox/outbox.d.ts.map +1 -1
  280. package/dist/src/outbox/pendingValues.d.ts +3 -1
  281. package/dist/src/outbox/pendingValues.d.ts.map +1 -1
  282. package/dist/src/outbox/storageValueRow.d.ts +7 -0
  283. package/dist/src/outbox/storageValueRow.d.ts.map +1 -0
  284. package/dist/src/platform/configurePlatform.d.ts.map +1 -1
  285. package/dist/src/platform/index.browser.d.ts.map +1 -1
  286. package/dist/src/platform/index.node.d.ts.map +1 -1
  287. package/dist/src/seedSchema/ModelSchema.d.ts +19 -0
  288. package/dist/src/seedSchema/ModelSchema.d.ts.map +1 -1
  289. package/dist/src/seedSchema/ModelSchemaSchema.d.ts.map +1 -1
  290. package/dist/src/seedSchema/PendingWriteSchema.d.ts.map +1 -1
  291. package/dist/src/seedSchema/SchemaSchema.d.ts +19 -0
  292. package/dist/src/seedSchema/SchemaSchema.d.ts.map +1 -1
  293. package/dist/src/seedSchema/SeedSchema.d.ts.map +1 -1
  294. package/dist/src/seedSchema/VersionSchema.d.ts.map +1 -1
  295. package/dist/src/seedSchema/attestedUid.d.ts +9 -0
  296. package/dist/src/seedSchema/attestedUid.d.ts.map +1 -0
  297. package/dist/src/storageValue/content.d.ts +45 -0
  298. package/dist/src/storageValue/content.d.ts.map +1 -0
  299. package/dist/src/storageValue/findFile.d.ts +7 -0
  300. package/dist/src/storageValue/findFile.d.ts.map +1 -0
  301. package/dist/src/storageValue/resolve.d.ts +71 -0
  302. package/dist/src/storageValue/resolve.d.ts.map +1 -0
  303. package/dist/src/storageValue/save.d.ts +24 -0
  304. package/dist/src/storageValue/save.d.ts.map +1 -0
  305. package/dist/src/storageValue/stage.d.ts +22 -0
  306. package/dist/src/storageValue/stage.d.ts.map +1 -0
  307. package/dist/src/types/db.d.ts +0 -14
  308. package/dist/src/types/db.d.ts.map +1 -1
  309. package/dist/src/types/index.d.ts +3 -3
  310. package/dist/src/types/index.d.ts.map +1 -1
  311. package/dist/src/types/machines.d.ts +1 -1
  312. package/dist/src/types/machines.d.ts.map +1 -1
  313. package/dist/src/types/property.d.ts +9 -24
  314. package/dist/src/types/property.d.ts.map +1 -1
  315. package/dist/src/types/publish.d.ts +1 -1
  316. package/dist/src/types/publish.d.ts.map +1 -1
  317. package/dist/{syncDbWithEas-BMHoou6X.js → syncDbWithEas-CA7PvBgu.js} +112 -162
  318. package/dist/syncDbWithEas-CA7PvBgu.js.map +1 -0
  319. package/dist/tabEvents-CaVmngqr.js +3804 -0
  320. package/dist/tabEvents-CaVmngqr.js.map +1 -0
  321. package/dist/{tolerantWebAccess-BHndShch.js → tolerantWebAccess-BJqViq08.js} +10 -15
  322. package/dist/{tolerantWebAccess-BHndShch.js.map → tolerantWebAccess-BJqViq08.js.map} +1 -1
  323. package/dist/{resolveModelForSyncedSeed-fIURvYRV.js → updateSeedRevokedAt-BL9Bg-G7.js} +30 -4
  324. package/dist/updateSeedRevokedAt-BL9Bg-G7.js.map +1 -0
  325. package/package.json +6 -6
  326. package/dist/ClientManager-DqL0q_eu.js +0 -5292
  327. package/dist/ClientManager-DqL0q_eu.js.map +0 -1
  328. package/dist/Item-CG5IhPtL.js.map +0 -1
  329. package/dist/ModelProperty-DDD47KVm.js.map +0 -1
  330. package/dist/Schema-BS8oR7QV.js +0 -4370
  331. package/dist/Schema-BS8oR7QV.js.map +0 -1
  332. package/dist/backfillMetadataPropertyIds-C7P7FL6F.js +0 -72
  333. package/dist/backfillMetadataPropertyIds-C7P7FL6F.js.map +0 -1
  334. package/dist/changeFeed-M8sGbOrW.js +0 -726
  335. package/dist/changeFeed-M8sGbOrW.js.map +0 -1
  336. package/dist/configurePlatform-Dukm2QwO.js.map +0 -1
  337. package/dist/dist/ClientManager-DqL0q_eu.d.ts +0 -952
  338. package/dist/dist/ClientManager-DqL0q_eu.d.ts.map +0 -1
  339. package/dist/dist/Item-CG5IhPtL.d.ts +0 -1115
  340. package/dist/dist/Item-CG5IhPtL.d.ts.map +0 -1
  341. package/dist/dist/ModelProperty-DDD47KVm.d.ts +0 -315
  342. package/dist/dist/ModelProperty-DDD47KVm.d.ts.map +0 -1
  343. package/dist/dist/Schema-BS8oR7QV.d.ts +0 -432
  344. package/dist/dist/Schema-BS8oR7QV.d.ts.map +0 -1
  345. package/dist/dist/SchemaValidationService-CZzKNL04.d.ts +0 -118
  346. package/dist/dist/SchemaValidationService-CZzKNL04.d.ts.map +0 -1
  347. package/dist/dist/backfillMetadataPropertyIds-C7P7FL6F.d.ts +0 -7
  348. package/dist/dist/backfillMetadataPropertyIds-C7P7FL6F.d.ts.map +0 -1
  349. package/dist/dist/changeFeed-M8sGbOrW.d.ts +0 -160
  350. package/dist/dist/changeFeed-M8sGbOrW.d.ts.map +0 -1
  351. package/dist/dist/configurePlatform-Dukm2QwO.d.ts.map +0 -1
  352. package/dist/dist/detachRowsBeforeDelete-Di1enVUF.d.ts +0 -13
  353. package/dist/dist/detachRowsBeforeDelete-Di1enVUF.d.ts.map +0 -1
  354. package/dist/dist/eas-B5VJolSj.d.ts +0 -12
  355. package/dist/dist/eas-B5VJolSj.d.ts.map +0 -1
  356. package/dist/dist/errors-C1qUEZ7A.d.ts +0 -25
  357. package/dist/dist/errors-C1qUEZ7A.d.ts.map +0 -1
  358. package/dist/dist/errors-CS3nDpUU.d.ts +0 -515
  359. package/dist/dist/errors-CS3nDpUU.d.ts.map +0 -1
  360. package/dist/dist/eventBus-7vxTCc3a.d.ts +0 -18
  361. package/dist/dist/eventBus-7vxTCc3a.d.ts.map +0 -1
  362. package/dist/dist/findSeedRowsByUid-CuQmK7An.d.ts +0 -13
  363. package/dist/dist/findSeedRowsByUid-CuQmK7An.d.ts.map +0 -1
  364. package/dist/dist/getItem-Uha3_H0t.d.ts +0 -6
  365. package/dist/dist/getItem-Uha3_H0t.d.ts.map +0 -1
  366. package/dist/dist/getPublishPayload-pbin4lk8.d.ts +0 -65
  367. package/dist/dist/getPublishPayload-pbin4lk8.d.ts.map +0 -1
  368. package/dist/dist/getPublishPendingDiff-ByIaPvCn.d.ts +0 -18
  369. package/dist/dist/getPublishPendingDiff-ByIaPvCn.d.ts.map +0 -1
  370. package/dist/dist/getPublishUploads-BBqiowVc.d.ts +0 -89
  371. package/dist/dist/getPublishUploads-BBqiowVc.d.ts.map +0 -1
  372. package/dist/dist/index-DQoyhp2c.d.ts +0 -6
  373. package/dist/dist/index-DQoyhp2c.d.ts.map +0 -1
  374. package/dist/dist/index-njjjo74v.d.ts +0 -14
  375. package/dist/dist/index-njjjo74v.d.ts.map +0 -1
  376. package/dist/dist/index-rOTW-2QZ.d.ts.map +0 -1
  377. package/dist/dist/index.browser-QPWBkLJA.d.ts +0 -9959
  378. package/dist/dist/index.browser-QPWBkLJA.d.ts.map +0 -1
  379. package/dist/dist/mimeTypes-C9r76DCH.d.ts +0 -30
  380. package/dist/dist/mimeTypes-C9r76DCH.d.ts.map +0 -1
  381. package/dist/dist/model-oHA3YhOd.d.ts +0 -6
  382. package/dist/dist/model-oHA3YhOd.d.ts.map +0 -1
  383. package/dist/dist/modelPropertyRecordSchemas-onFOG2x6.d.ts +0 -7
  384. package/dist/dist/modelPropertyRecordSchemas-onFOG2x6.d.ts.map +0 -1
  385. package/dist/dist/platform.browser.d.ts +0 -3
  386. package/dist/dist/platform.browser.d.ts.map +0 -1
  387. package/dist/dist/property-C85DCD7K.d.ts +0 -37
  388. package/dist/dist/property-C85DCD7K.d.ts.map +0 -1
  389. package/dist/dist/propertyValidationRules-CjUPOFeu.d.ts +0 -13
  390. package/dist/dist/propertyValidationRules-CjUPOFeu.d.ts.map +0 -1
  391. package/dist/dist/publishDraftGraph-DBWAN72w.d.ts +0 -93
  392. package/dist/dist/publishDraftGraph-DBWAN72w.d.ts.map +0 -1
  393. package/dist/dist/resolveModelForSyncedSeed-fIURvYRV.d.ts +0 -21
  394. package/dist/dist/resolveModelForSyncedSeed-fIURvYRV.d.ts.map +0 -1
  395. package/dist/dist/syncDbWithEas-BMHoou6X.d.ts +0 -13
  396. package/dist/dist/syncDbWithEas-BMHoou6X.d.ts.map +0 -1
  397. package/dist/dist/tolerantWebAccess-BHndShch.d.ts +0 -113
  398. package/dist/dist/tolerantWebAccess-BHndShch.d.ts.map +0 -1
  399. package/dist/dist/updateSeedRevokedAt-DpfG6ztE.d.ts +0 -11
  400. package/dist/dist/updateSeedRevokedAt-DpfG6ztE.d.ts.map +0 -1
  401. package/dist/eas-B5VJolSj.js +0 -67
  402. package/dist/eas-B5VJolSj.js.map +0 -1
  403. package/dist/errors-CS3nDpUU.js +0 -4006
  404. package/dist/errors-CS3nDpUU.js.map +0 -1
  405. package/dist/eventBus-7vxTCc3a.js +0 -64
  406. package/dist/eventBus-7vxTCc3a.js.map +0 -1
  407. package/dist/findSeedRowsByUid-CuQmK7An.js +0 -47
  408. package/dist/findSeedRowsByUid-CuQmK7An.js.map +0 -1
  409. package/dist/getItem-Uha3_H0t.js +0 -45
  410. package/dist/getItem-Uha3_H0t.js.map +0 -1
  411. package/dist/getPublishPayload-pbin4lk8.js +0 -1736
  412. package/dist/getPublishPayload-pbin4lk8.js.map +0 -1
  413. package/dist/getPublishPendingDiff-ByIaPvCn.js +0 -118
  414. package/dist/getPublishPendingDiff-ByIaPvCn.js.map +0 -1
  415. package/dist/getPublishUploads-BBqiowVc.js +0 -1080
  416. package/dist/getPublishUploads-BBqiowVc.js.map +0 -1
  417. package/dist/index-DQoyhp2c.js.map +0 -1
  418. package/dist/index-DxHpPNfn.js.map +0 -1
  419. package/dist/index-njjjo74v.js +0 -29
  420. package/dist/index-njjjo74v.js.map +0 -1
  421. package/dist/index-rOTW-2QZ.js +0 -4760
  422. package/dist/index-rOTW-2QZ.js.map +0 -1
  423. package/dist/index.browser-QPWBkLJA.js.map +0 -1
  424. package/dist/index.node-WsEJCOwC.js.map +0 -1
  425. package/dist/mimeTypes-C9r76DCH.js +0 -209
  426. package/dist/mimeTypes-C9r76DCH.js.map +0 -1
  427. package/dist/model-oHA3YhOd.js +0 -29
  428. package/dist/model-oHA3YhOd.js.map +0 -1
  429. package/dist/modelPropertyRecordSchemas-onFOG2x6.js +0 -94
  430. package/dist/modelPropertyRecordSchemas-onFOG2x6.js.map +0 -1
  431. package/dist/property-C85DCD7K.js +0 -289
  432. package/dist/property-C85DCD7K.js.map +0 -1
  433. package/dist/propertyValidationRules-CjUPOFeu.js +0 -73
  434. package/dist/propertyValidationRules-CjUPOFeu.js.map +0 -1
  435. package/dist/publishDraftGraph-DBWAN72w.js +0 -505
  436. package/dist/publishDraftGraph-DBWAN72w.js.map +0 -1
  437. package/dist/resolveModelForSyncedSeed-fIURvYRV.js.map +0 -1
  438. package/dist/src/Item/service/actors/loadOrCreateItem.d.ts +0 -5
  439. package/dist/src/Item/service/actors/loadOrCreateItem.d.ts.map +0 -1
  440. package/dist/src/Item/service/actors/waitForDb.d.ts +0 -4
  441. package/dist/src/Item/service/actors/waitForDb.d.ts.map +0 -1
  442. package/dist/src/ItemProperty/service/actors/loadOrCreateProperty.d.ts +0 -5
  443. package/dist/src/ItemProperty/service/actors/loadOrCreateProperty.d.ts.map +0 -1
  444. package/dist/src/ItemProperty/service/actors/saveValueToDb/analyzeInput.d.ts +0 -5
  445. package/dist/src/ItemProperty/service/actors/saveValueToDb/analyzeInput.d.ts.map +0 -1
  446. package/dist/src/ItemProperty/service/actors/saveValueToDb/index.d.ts +0 -6
  447. package/dist/src/ItemProperty/service/actors/saveValueToDb/index.d.ts.map +0 -1
  448. package/dist/src/ItemProperty/service/actors/saveValueToDb/rowContext.d.ts.map +0 -1
  449. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveFile.d.ts +0 -24
  450. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveFile.d.ts.map +0 -1
  451. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveHtml.d.ts +0 -5
  452. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveHtml.d.ts.map +0 -1
  453. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveImage.d.ts +0 -5
  454. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveImage.d.ts.map +0 -1
  455. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveItemStorage.d.ts +0 -10
  456. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveItemStorage.d.ts.map +0 -1
  457. package/dist/src/ItemProperty/service/actors/saveValueToDb/storageValue.d.ts +0 -22
  458. package/dist/src/ItemProperty/service/actors/saveValueToDb/storageValue.d.ts.map +0 -1
  459. package/dist/src/ItemProperty/service/actors/waitForDb.d.ts +0 -4
  460. package/dist/src/ItemProperty/service/actors/waitForDb.d.ts.map +0 -1
  461. package/dist/src/Model/service/actors/createModelProperties.d.ts +0 -9
  462. package/dist/src/Model/service/actors/createModelProperties.d.ts.map +0 -1
  463. package/dist/src/Model/service/actors/loadOrCreateModel.d.ts +0 -5
  464. package/dist/src/Model/service/actors/loadOrCreateModel.d.ts.map +0 -1
  465. package/dist/src/Schema/service/actors/checkExistingSchema.d.ts +0 -5
  466. package/dist/src/Schema/service/actors/checkExistingSchema.d.ts.map +0 -1
  467. package/dist/src/Schema/service/actors/createModelInstances.d.ts +0 -11
  468. package/dist/src/Schema/service/actors/createModelInstances.d.ts.map +0 -1
  469. package/dist/src/Schema/service/actors/createPropertyInstances.d.ts +0 -12
  470. package/dist/src/Schema/service/actors/createPropertyInstances.d.ts.map +0 -1
  471. package/dist/src/Schema/service/actors/validateSchema.d.ts +0 -5
  472. package/dist/src/Schema/service/actors/validateSchema.d.ts.map +0 -1
  473. package/dist/src/Schema/service/actors/verifyModelInstancesInCache.d.ts +0 -6
  474. package/dist/src/Schema/service/actors/verifyModelInstancesInCache.d.ts.map +0 -1
  475. package/dist/src/Schema/service/actors/verifyModelsInDb.d.ts +0 -7
  476. package/dist/src/Schema/service/actors/verifyModelsInDb.d.ts.map +0 -1
  477. package/dist/src/Schema/service/actors/verifyPropertiesInDb.d.ts +0 -8
  478. package/dist/src/Schema/service/actors/verifyPropertiesInDb.d.ts.map +0 -1
  479. package/dist/src/Schema/service/actors/verifyPropertyInstancesInCache.d.ts +0 -6
  480. package/dist/src/Schema/service/actors/verifyPropertyInstancesInCache.d.ts.map +0 -1
  481. package/dist/src/Schema/service/actors/verifySchemaInDb.d.ts +0 -7
  482. package/dist/src/Schema/service/actors/verifySchemaInDb.d.ts.map +0 -1
  483. package/dist/src/Schema/service/actors/writeModelsToDb.d.ts +0 -9
  484. package/dist/src/Schema/service/actors/writeModelsToDb.d.ts.map +0 -1
  485. package/dist/src/Schema/service/actors/writePropertiesToDb.d.ts +0 -10
  486. package/dist/src/Schema/service/actors/writePropertiesToDb.d.ts.map +0 -1
  487. package/dist/src/Schema/service/actors/writeSchemaToDb.d.ts +0 -11
  488. package/dist/src/Schema/service/actors/writeSchemaToDb.d.ts.map +0 -1
  489. package/dist/src/browser/helpers/ArweaveClient.d.ts +0 -14
  490. package/dist/src/browser/helpers/ArweaveClient.d.ts.map +0 -1
  491. package/dist/src/browser/helpers/EasClient.d.ts +0 -10
  492. package/dist/src/browser/helpers/EasClient.d.ts.map +0 -1
  493. package/dist/src/browser/helpers/QueryClient.d.ts +0 -9
  494. package/dist/src/browser/helpers/QueryClient.d.ts.map +0 -1
  495. package/dist/src/client/actors/addModelsToStore.d.ts +0 -4
  496. package/dist/src/client/actors/addModelsToStore.d.ts.map +0 -1
  497. package/dist/src/db/backfillMetadataPropertyIds.d.ts +0 -7
  498. package/dist/src/db/backfillMetadataPropertyIds.d.ts.map +0 -1
  499. package/dist/src/db/read/getItem.d.ts +0 -3
  500. package/dist/src/db/read/getItem.d.ts.map +0 -1
  501. package/dist/src/db/read/getPropertyData.d.ts +0 -4
  502. package/dist/src/db/read/getPropertyData.d.ts.map +0 -1
  503. package/dist/src/db/read/getRelatedItemsForPublish.d.ts +0 -10
  504. package/dist/src/db/read/getRelatedItemsForPublish.d.ts.map +0 -1
  505. package/dist/src/db/read/getSeedData.d.ts +0 -12
  506. package/dist/src/db/read/getSeedData.d.ts.map +0 -1
  507. package/dist/src/db/read/itemLoads.d.ts +0 -20
  508. package/dist/src/db/read/itemLoads.d.ts.map +0 -1
  509. package/dist/src/db/read/publishDraftGraph.d.ts +0 -64
  510. package/dist/src/db/read/publishDraftGraph.d.ts.map +0 -1
  511. package/dist/src/db/write/linkModelToSchema.d.ts +0 -12
  512. package/dist/src/db/write/linkModelToSchema.d.ts.map +0 -1
  513. package/dist/src/helpers/ArweaveClient/BaseArweaveClient.d.ts +0 -2
  514. package/dist/src/helpers/ArweaveClient/BaseArweaveClient.d.ts.map +0 -1
  515. package/dist/src/helpers/ArweaveClient/arweaveL1UploadApi.d.ts +0 -2
  516. package/dist/src/helpers/ArweaveClient/arweaveL1UploadApi.d.ts.map +0 -1
  517. package/dist/src/helpers/ArweaveClient/selectReadGateway.d.ts +0 -2
  518. package/dist/src/helpers/ArweaveClient/selectReadGateway.d.ts.map +0 -1
  519. package/dist/src/helpers/ArweaveClient/uploadApiVerification.d.ts +0 -2
  520. package/dist/src/helpers/ArweaveClient/uploadApiVerification.d.ts.map +0 -1
  521. package/dist/src/helpers/EasClient/BaseEasClient.d.ts +0 -2
  522. package/dist/src/helpers/EasClient/BaseEasClient.d.ts.map +0 -1
  523. package/dist/src/helpers/QueryClient/BaseQueryClient.d.ts +0 -2
  524. package/dist/src/helpers/QueryClient/BaseQueryClient.d.ts.map +0 -1
  525. package/dist/src/helpers/easUid.d.ts +0 -2
  526. package/dist/src/helpers/easUid.d.ts.map +0 -1
  527. package/dist/src/helpers/entity/entityDestroy.d.ts +0 -49
  528. package/dist/src/helpers/entity/entityDestroy.d.ts.map +0 -1
  529. package/dist/src/helpers/entity/entityFind.d.ts +0 -52
  530. package/dist/src/helpers/entity/entityFind.d.ts.map +0 -1
  531. package/dist/src/helpers/entity/entityState.d.ts +0 -28
  532. package/dist/src/helpers/entity/entityState.d.ts.map +0 -1
  533. package/dist/src/helpers/entity/entityUnload.d.ts +0 -56
  534. package/dist/src/helpers/entity/entityUnload.d.ts.map +0 -1
  535. package/dist/src/helpers/entity/evictionEpoch.d.ts +0 -43
  536. package/dist/src/helpers/entity/evictionEpoch.d.ts.map +0 -1
  537. package/dist/src/helpers/entity/index.d.ts +0 -12
  538. package/dist/src/helpers/entity/index.d.ts.map +0 -1
  539. package/dist/src/helpers/gateway/gatewayState.d.ts +0 -2
  540. package/dist/src/helpers/gateway/gatewayState.d.ts.map +0 -1
  541. package/dist/src/helpers/gateway/resolveSeedGatewayEndpoints.d.ts +0 -2
  542. package/dist/src/helpers/gateway/resolveSeedGatewayEndpoints.d.ts.map +0 -1
  543. package/dist/src/helpers/gateway/seedGatewayConfig.d.ts +0 -2
  544. package/dist/src/helpers/gateway/seedGatewayConfig.d.ts.map +0 -1
  545. package/dist/src/helpers/getSchemaForItemProperty.d.ts +0 -15
  546. package/dist/src/helpers/getSchemaForItemProperty.d.ts.map +0 -1
  547. package/dist/src/helpers/getSegmentedItemProperties.d.ts +0 -10
  548. package/dist/src/helpers/getSegmentedItemProperties.d.ts.map +0 -1
  549. package/dist/src/helpers/isSqliteUniqueConstraintError.d.ts +0 -7
  550. package/dist/src/helpers/isSqliteUniqueConstraintError.d.ts.map +0 -1
  551. package/dist/src/helpers/propertyValidationRules.d.ts +0 -20
  552. package/dist/src/helpers/propertyValidationRules.d.ts.map +0 -1
  553. package/dist/src/helpers/propertyWriteTickets.d.ts +0 -11
  554. package/dist/src/helpers/propertyWriteTickets.d.ts.map +0 -1
  555. package/dist/src/helpers/reconcilePropertyDraftRows.d.ts +0 -10
  556. package/dist/src/helpers/reconcilePropertyDraftRows.d.ts.map +0 -1
  557. package/dist/src/helpers/waitForEntityIdle.d.ts +0 -21
  558. package/dist/src/helpers/waitForEntityIdle.d.ts.map +0 -1
  559. package/dist/src/interfaces/IQueryClient.d.ts +0 -15
  560. package/dist/src/interfaces/IQueryClient.d.ts.map +0 -1
  561. package/dist/src/node/helpers/ArweaveClient.d.ts +0 -5
  562. package/dist/src/node/helpers/ArweaveClient.d.ts.map +0 -1
  563. package/dist/src/node/helpers/EasClient.d.ts +0 -5
  564. package/dist/src/node/helpers/EasClient.d.ts.map +0 -1
  565. package/dist/src/node/helpers/QueryClient.d.ts +0 -5
  566. package/dist/src/node/helpers/QueryClient.d.ts.map +0 -1
  567. package/dist/src/services/write/actors/validateEntity.d.ts +0 -6
  568. package/dist/src/services/write/actors/validateEntity.d.ts.map +0 -1
  569. package/dist/src/services/write/actors/writeToDatabase.d.ts +0 -14
  570. package/dist/src/services/write/actors/writeToDatabase.d.ts.map +0 -1
  571. package/dist/src/services/write/writeProcessMachine.d.ts +0 -56
  572. package/dist/src/services/write/writeProcessMachine.d.ts.map +0 -1
  573. package/dist/src/stores/eas.d.ts +0 -2
  574. package/dist/src/stores/eas.d.ts.map +0 -1
  575. package/dist/src/types/arweave.d.ts +0 -57
  576. package/dist/src/types/arweave.d.ts.map +0 -1
  577. package/dist/src/types/gateway.d.ts +0 -2
  578. package/dist/src/types/gateway.d.ts.map +0 -1
  579. package/dist/syncDbWithEas-BMHoou6X.js.map +0 -1
  580. package/dist/updateSeedRevokedAt-DpfG6ztE.js +0 -31
  581. package/dist/updateSeedRevokedAt-DpfG6ztE.js.map +0 -1
@@ -1,4760 +0,0 @@
1
- import debug from 'debug';
2
- import { isEasAttestationExplorerUrl, BaseArweaveClient, ensureReadGatewaySelected, getArweaveReadBaseUrls, getTransactionTagsByIds } from '@seedprotocol/arweave';
3
- import { VERSION_SCHEMA_UID, easName, DEFAULT_NAME_MATCH_LEVEL, compareNameMatchTiers, isNameMatchLevel, localNamesInBestMatchTier, BaseQueryClient, BaseEasClient, GET_FILES_METADATA } from '@seedprotocol/eas';
4
- import { waitFor } from 'xstate';
5
- import pluralize from 'pluralize';
6
- import { Type } from '@sinclair/typebox';
7
- import { sqliteTable, integer, text, unique, int, index, blob, uniqueIndex } from 'drizzle-orm/sqlite-core';
8
- import { relations, and, eq, inArray, desc, isNull, isNotNull, or } from 'drizzle-orm';
9
- import { customAlphabet } from 'nanoid';
10
- import { alphanumeric } from 'nanoid-dictionary';
11
- import { sha256 } from '@noble/hashes/sha256';
12
- import { sha3_256 } from '@noble/hashes/sha3';
13
- import { bytesToHex } from '@noble/hashes/utils';
14
- import { throttle } from 'lodash-es';
15
-
16
- const logger$5 = debug('seedSdk:browser:db:sqliteBusyRetry');
17
- /** Waits between attempts; about 1 s in total. */
18
- const SQLITE_BUSY_RETRY_DELAYS_MS = [25, 75, 150, 300, 450];
19
- /**
20
- * Another connection (usually another tab) holds the database lock. SQLite reports this as
21
- * SQLITE_BUSY ("database is locked") or SQLITE_LOCKED ("database table is locked").
22
- */
23
- function isSqliteBusyError(error) {
24
- // Drizzle wraps a driver's error ("Failed query: ...") and keeps it as `cause`
25
- for (let current = error, depth = 0; current != null && depth < 5; depth++) {
26
- const message = current instanceof Error ? current.message : String(current);
27
- if (/SQLITE_BUSY|SQLITE_LOCKED|database (table )?is locked/i.test(message))
28
- return true;
29
- current = current.cause;
30
- }
31
- return false;
32
- }
33
- /**
34
- * Retries a statement another tab's lock rejected. Safe for the SDK's writes because each is a
35
- * single autocommit statement: one that failed with SQLITE_BUSY didn't apply. Don't use this for
36
- * statements inside a transaction, where a retry would replay part of it.
37
- */
38
- function withSqliteBusyRetry(driver, delaysMs = SQLITE_BUSY_RETRY_DELAYS_MS) {
39
- return async (...args) => {
40
- for (let attempt = 0;; attempt++) {
41
- try {
42
- return await driver(...args);
43
- }
44
- catch (error) {
45
- if (!isSqliteBusyError(error) || attempt >= delaysMs.length)
46
- throw error;
47
- logger$5(`database busy (attempt ${attempt + 1}); retrying`);
48
- await new Promise((resolve) => setTimeout(resolve, delaysMs[attempt]));
49
- }
50
- }
51
- };
52
- }
53
-
54
- class BaseDb {
55
- static configure(impl) {
56
- if (!impl) {
57
- throw new Error('Cannot configure Db with undefined or null. Ensure the platform-specific Db is properly created.');
58
- }
59
- BaseDb._impl = impl;
60
- }
61
- static requireImpl() {
62
- if (!BaseDb._impl) {
63
- throw new Error('Db not configured. Call BaseDb.configure() during platform init.');
64
- }
65
- return BaseDb._impl;
66
- }
67
- static getAppDb() {
68
- return BaseDb.requireImpl().getAppDb();
69
- }
70
- static prepareDb(filesDir, config) {
71
- return BaseDb.requireImpl().prepareDb(filesDir, config);
72
- }
73
- static isAppDbReady() {
74
- if (!BaseDb._impl) {
75
- return false;
76
- }
77
- return BaseDb._impl.isAppDbReady();
78
- }
79
- static connectToDb(pathToDir) {
80
- return BaseDb.requireImpl().connectToDb(pathToDir);
81
- }
82
- static async migrate(pathToDbDir, dbName, dbId) {
83
- return BaseDb.requireImpl().migrate(pathToDbDir, dbName, dbId);
84
- }
85
- /**
86
- * Execute a reactive query that emits new results whenever the underlying data changes.
87
- *
88
- * Supports two usage patterns:
89
- * 1. SQL tag function (browser only): liveQuery((sql) => sql`SELECT * FROM models`)
90
- * 2. Drizzle query builder: liveQuery(db.select().from(models))
91
- *
92
- * On Node only the query builder works (a tag function throws), and it is re-run every second;
93
- * the observable emits when the result changes.
94
- */
95
- static liveQuery(query) {
96
- return BaseDb.requireImpl().liveQuery(query);
97
- }
98
- /**
99
- * Runs `fn` in one transaction: every query it runs on `tx` (a Drizzle database, used like
100
- * `getAppDb()`) commits together, or none does when `fn` throws. Queries on `getAppDb()` inside
101
- * `fn` are not part of the transaction, and in the browser they wait for it to end, so don't
102
- * call `getAppDb()` or `BaseDb.transaction` from `fn`.
103
- *
104
- * When another connection (another tab) holds the database lock, the whole transaction is
105
- * retried, so `fn` must have no side effects outside `tx`.
106
- */
107
- static async transaction(fn) {
108
- const impl = BaseDb.requireImpl();
109
- for (let attempt = 0;; attempt++) {
110
- try {
111
- return await impl.transaction(fn);
112
- }
113
- catch (error) {
114
- if (!isSqliteBusyError(error) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length)
115
- throw error;
116
- await new Promise((resolve) => setTimeout(resolve, SQLITE_BUSY_RETRY_DELAYS_MS[attempt]));
117
- }
118
- }
119
- }
120
- }
121
- BaseDb._impl = null;
122
-
123
- var BaseDb$1 = /*#__PURE__*/Object.freeze({
124
- __proto__: null,
125
- BaseDb: BaseDb
126
- });
127
-
128
- const models = sqliteTable('models', {
129
- id: integer('id').primaryKey({ autoIncrement: true }),
130
- name: text('name').notNull(),
131
- schemaFileId: text('schema_file_id'), // ID from JSON file for change tracking - must be unique
132
- isEdited: integer('is_edited', { mode: 'boolean' }), // true if model has been edited locally, false if matches schema file
133
- }, (table) => {
134
- return {
135
- uniqueSchemaFileId: unique('unique_schema_file_id').on(table.schemaFileId),
136
- };
137
- });
138
- const modelsRelations = relations(models, ({ many }) => ({
139
- properties: many(properties),
140
- }));
141
- const properties = sqliteTable('properties', {
142
- id: integer('id').primaryKey({ autoIncrement: true }),
143
- name: text('name').notNull(),
144
- dataType: text('data_type').notNull(),
145
- modelId: integer('model_id')
146
- .notNull()
147
- .references(() => models.id),
148
- refModelId: integer('ref_model_id').references(() => models.id),
149
- refValueType: text('ref_value_type'),
150
- schemaFileId: text('schema_file_id'), // ID from JSON file for change tracking
151
- isEdited: integer('is_edited', { mode: 'boolean' }), // true if property has been edited locally, false if matches schema file
152
- required: integer('required', { mode: 'boolean' }), // true if relation/image property is required (publish fails when related item not found)
153
- // Storage settings from the schema file's `storage: { type, path, extension }`, in the internal
154
- // shape ModelProperty uses. Null when the property has no storage config.
155
- storageType: text('storage_type'), // 'ItemStorage' | 'PropertyStorage'
156
- localStorageDir: text('local_storage_dir'), // storage.path, e.g. '/html'
157
- filenameSuffix: text('filename_suffix'), // storage.extension, e.g. '.html'
158
- }, (table) => {
159
- return {
160
- uniqueNameModelId: unique('unique_name_model_id').on(table.name, table.modelId),
161
- uniqueSchemaFileId: unique('unique_property_schema_file_id').on(table.schemaFileId),
162
- };
163
- });
164
- const propertiesRelations = relations(properties, ({ one }) => ({
165
- model: one(models),
166
- refModel: one(models),
167
- }));
168
-
169
- var ModelSchema = /*#__PURE__*/Object.freeze({
170
- __proto__: null,
171
- models: models,
172
- modelsRelations: modelsRelations,
173
- properties: properties,
174
- propertiesRelations: propertiesRelations
175
- });
176
-
177
- class BaseFileManager {
178
- static configure(impl) {
179
- if (!impl) {
180
- throw new Error('Cannot configure FileManager with undefined or null. Ensure the platform-specific FileManager is properly created.');
181
- }
182
- BaseFileManager._impl = impl;
183
- }
184
- static requireImpl() {
185
- if (!BaseFileManager._impl) {
186
- throw new Error('FileManager not configured. Call BaseFileManager.configure() during platform init.');
187
- }
188
- return BaseFileManager._impl;
189
- }
190
- /**
191
- * Clears FS init flags so a later call can retry. Used after a failed init
192
- * and by tests. Does not unmount or delete any files.
193
- */
194
- static resetInitializationState() {
195
- this.fileSystemInitialized = false;
196
- this.initializing = false;
197
- this.initializePromise = null;
198
- this.workingDir = undefined;
199
- }
200
- static initializeFileSystem(workingDir) {
201
- if (this.fileSystemInitialized) {
202
- return Promise.resolve();
203
- }
204
- if (this.initializePromise) {
205
- return this.initializePromise;
206
- }
207
- this.initializing = true;
208
- const pending = this.runInitializeFileSystem(workingDir).finally(() => {
209
- this.initializing = false;
210
- if (this.initializePromise === pending) {
211
- this.initializePromise = null;
212
- }
213
- });
214
- this.initializePromise = pending;
215
- return pending;
216
- }
217
- static async runInitializeFileSystem(workingDir) {
218
- await BaseFileManager.requireImpl().initializeFileSystem(workingDir);
219
- this.fileSystemInitialized = true;
220
- this.workingDir = workingDir;
221
- }
222
- static getWorkingDir() {
223
- if (!this.workingDir) {
224
- throw new Error('Working directory is not set');
225
- }
226
- return this.workingDir;
227
- }
228
- /**
229
- * Build a path under the configured files root (e.g. /app-files).
230
- * Use this instead of hardcoding /files/ for images, html, json, etc.
231
- * @param subpaths - path segments to join (e.g. 'images', fileName)
232
- * @returns full path like /app-files/images/egg.jpg
233
- */
234
- static getFilesPath(...subpaths) {
235
- const root = this.getWorkingDir().replace(/\/$/, '');
236
- const parts = [root, ...subpaths].filter(Boolean);
237
- return parts.join('/').replace(/\/+/g, '/');
238
- }
239
- static getContentUrlFromPath(path) {
240
- return BaseFileManager.requireImpl().getContentUrlFromPath(path);
241
- }
242
- static downloadAllFiles(params) {
243
- return BaseFileManager.requireImpl().downloadAllFiles(params);
244
- }
245
- static downloadFileByTransactionId({ transactionId, arweaveHost, arweaveBaseUrls, excludedTransactions, expectedContentHash, compositeChildPaths, skipExisting, }) {
246
- return BaseFileManager.requireImpl().downloadAllFiles({
247
- transactionIds: [transactionId],
248
- arweaveHost,
249
- arweaveBaseUrls,
250
- excludedTransactions,
251
- expectedContentHashes: expectedContentHash ? { [transactionId]: expectedContentHash } : undefined,
252
- compositeChildPaths: compositeChildPaths ? { [transactionId]: compositeChildPaths } : undefined,
253
- skipExisting,
254
- });
255
- }
256
- static resizeImage({ filePath, width, height }) {
257
- return BaseFileManager.requireImpl().resizeImage({ filePath, width, height });
258
- }
259
- static resizeAllImages({ width, height }) {
260
- return BaseFileManager.requireImpl().resizeAllImages({ width, height });
261
- }
262
- static pathExists(filePath) {
263
- return BaseFileManager.requireImpl().pathExists(filePath);
264
- }
265
- static invalidateCachedPaths(filePaths) {
266
- return BaseFileManager.requireImpl().invalidateCachedPaths(filePaths);
267
- }
268
- /** File size in bytes, or null if the path does not exist / cannot be stated. */
269
- static getFileSize(filePath) {
270
- return BaseFileManager.requireImpl().getFileSize(filePath);
271
- }
272
- /**
273
- * Returns a list of filenames in the given directory (e.g. 'images', 'files').
274
- */
275
- static listFiles(dir) {
276
- return BaseFileManager.requireImpl().listFiles(dir);
277
- }
278
- /**
279
- * Returns a list of image filenames in the images folder (originals only, excludes size subdirs).
280
- * Use this to get all stored images without traversing 480/760/1024/1440/1920 subdirectories.
281
- */
282
- static listImageFiles() {
283
- return this.listFiles('images');
284
- }
285
- static createDirIfNotExists(filePath) {
286
- return BaseFileManager.requireImpl().createDirIfNotExists(filePath);
287
- }
288
- static async waitForFile(filePath) {
289
- return BaseFileManager.requireImpl().waitForFile(filePath);
290
- }
291
- static async waitForFileWithContent(filePath, interval, timeout) {
292
- return BaseFileManager.requireImpl().waitForFileWithContent(filePath, interval, timeout);
293
- }
294
- static async saveFile(filePath, content) {
295
- return BaseFileManager.requireImpl().saveFile(filePath, content);
296
- }
297
- static saveFileSync(filePath, content) {
298
- return BaseFileManager.requireImpl().saveFileSync(filePath, content);
299
- }
300
- static async readFile(filePath) {
301
- return BaseFileManager.requireImpl().readFile(filePath);
302
- }
303
- static readFileSync(filePath) {
304
- return BaseFileManager.requireImpl().readFileSync(filePath);
305
- }
306
- static async readFileAsBuffer(filePath) {
307
- return BaseFileManager.requireImpl().readFileAsBuffer(filePath);
308
- }
309
- static async readFileAsString(filePath) {
310
- return BaseFileManager.requireImpl().readFileAsString(filePath);
311
- }
312
- static async getFs() {
313
- return BaseFileManager.requireImpl().getFs();
314
- }
315
- static getFsSync() {
316
- return BaseFileManager.requireImpl().getFsSync();
317
- }
318
- static getPathModule() {
319
- return BaseFileManager.requireImpl().getPathModule();
320
- }
321
- static getParentDirPath(filePath) {
322
- return BaseFileManager.requireImpl().getParentDirPath(filePath);
323
- }
324
- static getFilenameFromPath(filePath) {
325
- return BaseFileManager.requireImpl().getFilenameFromPath(filePath);
326
- }
327
- }
328
- BaseFileManager.fileSystemInitialized = false;
329
- BaseFileManager.initializing = false;
330
- BaseFileManager._impl = null;
331
- BaseFileManager.initializePromise = null;
332
-
333
- var BaseFileManager$1 = /*#__PURE__*/Object.freeze({
334
- __proto__: null,
335
- BaseFileManager: BaseFileManager
336
- });
337
-
338
- /** Default maximum length for Text properties when not explicitly set. Html holds arbitrary-length content and does not use this limit. */
339
- const DEFAULT_TEXT_MAX_LENGTH = 255;
340
- /**
341
- * TypeBox schema for ValidationRules
342
- */
343
- const TValidationRules = Type.Object({
344
- pattern: Type.Optional(Type.String()),
345
- minLength: Type.Optional(Type.Integer({ minimum: 0 })),
346
- maxLength: Type.Optional(Type.Integer({ minimum: 1 })),
347
- enum: Type.Optional(Type.Array(Type.Any())),
348
- custom: Type.Optional(Type.String()),
349
- }, { additionalProperties: true });
350
-
351
- /**
352
- * Enum for all property data types
353
- */
354
- var ModelPropertyDataTypes;
355
- (function (ModelPropertyDataTypes) {
356
- ModelPropertyDataTypes["Text"] = "Text";
357
- ModelPropertyDataTypes["Number"] = "Number";
358
- ModelPropertyDataTypes["List"] = "List";
359
- ModelPropertyDataTypes["Relation"] = "Relation";
360
- ModelPropertyDataTypes["Image"] = "Image";
361
- ModelPropertyDataTypes["Json"] = "Json";
362
- ModelPropertyDataTypes["File"] = "File";
363
- ModelPropertyDataTypes["Boolean"] = "Boolean";
364
- ModelPropertyDataTypes["Date"] = "Date";
365
- ModelPropertyDataTypes["Html"] = "Html";
366
- })(ModelPropertyDataTypes || (ModelPropertyDataTypes = {}));
367
- /** Known data types in lowercase for normalization */
368
- const DATA_TYPE_LOWER_MAP = {
369
- text: 'Text',
370
- string: 'Text', // JSON Schema convention
371
- number: 'Number',
372
- list: 'List',
373
- relation: 'Relation',
374
- image: 'Image',
375
- json: 'Json',
376
- file: 'File',
377
- boolean: 'Boolean',
378
- date: 'Date',
379
- html: 'Html',
380
- blob: 'Blob',
381
- };
382
- /**
383
- * Normalize dataType to canonical PascalCase form.
384
- * Accepts lowercase (e.g. 'text') or mixed case and returns 'Text'.
385
- * Use when ingesting dataType from JSON schema files.
386
- */
387
- function normalizeDataType(value) {
388
- if (!value || typeof value !== 'string')
389
- return value ?? '';
390
- const lower = value.toLowerCase().trim();
391
- return DATA_TYPE_LOWER_MAP[lower] ?? value.charAt(0).toUpperCase() + value.slice(1).toLowerCase();
392
- }
393
- /**
394
- * Case-insensitive dataType check. Use instead of `dataType === 'List'` so schemas
395
- * (or existing DB rows) with lowercase types like "list" still match.
396
- */
397
- function isDataType(value, expected) {
398
- if (!value)
399
- return false;
400
- return normalizeDataType(value) === expected;
401
- }
402
- /**
403
- * Normalize a property definition to the internal shape ItemProperty reads (`dataType`,
404
- * `refValueType`, `ref`, `storageType`, ...). Accepts schema-file shape (`type`, `model`,
405
- * `items`, `storage`) and any casing. Mirrors the conversion in imports/json createModelFromJson
406
- * for the fallback paths that read schema-file properties directly.
407
- */
408
- function normalizePropertyRecordSchema(def) {
409
- if (!def || typeof def !== 'object')
410
- return def;
411
- const src = def;
412
- const out = { ...src };
413
- const rawType = src.dataType ?? src.type;
414
- if (typeof rawType === 'string' && rawType)
415
- out.dataType = normalizeDataType(rawType);
416
- const ref = src.ref ?? src.refModelName ?? src.items?.model ?? src.model;
417
- if (ref && !out.ref)
418
- out.ref = ref;
419
- if (ref && !out.refModelName)
420
- out.refModelName = ref;
421
- const rawRefValueType = src.refValueType ?? src.items?.type;
422
- if (typeof rawRefValueType === 'string' && rawRefValueType) {
423
- out.refValueType = normalizeDataType(rawRefValueType);
424
- }
425
- else if (out.dataType === ModelPropertyDataTypes.List && ref) {
426
- out.refValueType = ModelPropertyDataTypes.Relation;
427
- }
428
- if (src.storage && typeof src.storage === 'object' && !out.storageType) {
429
- out.storageType = src.storage.type === 'ItemStorage' ? 'ItemStorage' : 'PropertyStorage';
430
- if (src.storage.path !== undefined && out.localStorageDir === undefined)
431
- out.localStorageDir = src.storage.path;
432
- if (src.storage.extension !== undefined && out.filenameSuffix === undefined)
433
- out.filenameSuffix = src.storage.extension;
434
- }
435
- return out;
436
- }
437
- // String literals, not enum members: PropertyDataType must accept plain strings
438
- // such as `{ dataType: 'Text' }`. ModelPropertyDataTypes members still fit.
439
- const TPropertyDataType = Type.Union([
440
- Type.Literal('Text'),
441
- Type.Literal('Number'),
442
- Type.Literal('List'),
443
- Type.Literal('Relation'),
444
- Type.Literal('Image'),
445
- Type.Literal('Json'),
446
- Type.Literal('File'),
447
- Type.Literal('Boolean'),
448
- Type.Literal('Date'),
449
- Type.Literal('Html'),
450
- ]);
451
- const TStorageType = Type.Union([
452
- Type.Literal('ItemStorage'), // Looks for a storageTransactionId property on the item
453
- Type.Literal('PropertyStorage'), // Looks for a storageTransactionId value on the property
454
- ]);
455
- /** How publish handles `data:image/*` URIs inside Html storage. Only applies when `dataType === 'Html'`. */
456
- const THtmlEmbeddedDataUriPolicy = Type.Union([
457
- Type.Literal('materialize'),
458
- Type.Literal('preserve'),
459
- ]);
460
- const TProperty = Type.Object({
461
- id: Type.Optional(Type.String()), // schemaFileId (string) - public ID
462
- _dbId: Type.Optional(Type.Number()), // Database integer ID - internal only
463
- name: Type.Optional(Type.String()),
464
- dataType: TPropertyDataType,
465
- ref: Type.Optional(Type.String()),
466
- // modelId: string (modelFileId) in code; number only for DB foreign keys when resolved
467
- modelId: Type.Optional(Type.Union([Type.Number(), Type.String()])),
468
- modelName: Type.Optional(Type.String()),
469
- refModelId: Type.Optional(Type.Number()),
470
- refModelName: Type.Optional(Type.String()),
471
- refValueType: Type.Optional(TPropertyDataType),
472
- storageType: Type.Optional(TStorageType),
473
- localStorageDir: Type.Optional(Type.String()),
474
- filenameSuffix: Type.Optional(Type.String()),
475
- validation: Type.Optional(TValidationRules),
476
- required: Type.Optional(Type.Boolean()),
477
- /** When set on an Html property: `materialize` uploads embedded images as Image seeds and rewrites src; `preserve` leaves data URIs. */
478
- htmlEmbeddedDataUriPolicy: Type.Optional(THtmlEmbeddedDataUriPolicy),
479
- });
480
- const TPropertyConstructor = Type.Function([
481
- Type.Optional(Type.Union([Type.String(), TStorageType, Type.Undefined()])),
482
- Type.Optional(Type.Union([Type.String(), TPropertyDataType])),
483
- Type.Optional(Type.Union([Type.String(), Type.Boolean()])),
484
- ], TProperty);
485
- const TPropertyDefs = Type.Record(TPropertyDataType, TPropertyConstructor);
486
- function htmlPropertyFactory(opts) {
487
- return {
488
- dataType: ModelPropertyDataTypes.Html,
489
- ...(opts?.htmlEmbeddedDataUriPolicy
490
- ? { htmlEmbeddedDataUriPolicy: opts.htmlEmbeddedDataUriPolicy }
491
- : {}),
492
- };
493
- }
494
- const Property = {
495
- Text: (storageType, localStorageDir, filenameSuffix) => ({
496
- dataType: ModelPropertyDataTypes.Text,
497
- storageType,
498
- localStorageDir,
499
- filenameSuffix,
500
- TObject: Type.String(),
501
- }),
502
- Json: () => ({ dataType: ModelPropertyDataTypes.Json }),
503
- File: () => ({ dataType: ModelPropertyDataTypes.File }),
504
- Number: () => ({ dataType: ModelPropertyDataTypes.Number }),
505
- /**
506
- * Create a List property. refValueType is required; ref is required only for list of relations.
507
- * @param refValueType - Element type: 'Text', 'Number', 'Relation', etc.
508
- * @param ref - Model name when refValueType is 'Relation' (optional for primitives)
509
- * @example List('Text') - list of strings
510
- * @example List('Relation', 'Tag') - list of relations to Tag model
511
- */
512
- List: (refValueType, ref) => ({
513
- dataType: ModelPropertyDataTypes.List,
514
- ref,
515
- refValueType,
516
- }),
517
- Relation: (ref, refValueType, required) => ({
518
- dataType: ModelPropertyDataTypes.Relation,
519
- ref,
520
- refValueType,
521
- required,
522
- }),
523
- Image: () => ({ dataType: ModelPropertyDataTypes.Image }),
524
- Boolean: () => ({ dataType: ModelPropertyDataTypes.Boolean }),
525
- Date: () => ({ dataType: ModelPropertyDataTypes.Date }),
526
- Html: htmlPropertyFactory,
527
- /** `Html` opts are not representable in `TPropertyConstructor`; keep runtime shape via double assert. */
528
- };
529
-
530
- // Define zero constants ourselves since they're not exported from eas-sdk in newer versions
531
- const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
532
- const ZERO_BYTES = '0x';
533
- const ZERO_BYTES32 = '0x0000000000000000000000000000000000000000000000000000000000000000';
534
- const INTERNAL_DATA_TYPES = {
535
- Text: {
536
- eas: 'string',
537
- },
538
- // ADR 0010: decimal strings, so negatives, fractions and values above 255 publish.
539
- Number: {
540
- eas: 'string',
541
- },
542
- Image: {
543
- eas: 'bytes32',
544
- },
545
- Relation: {
546
- eas: 'bytes32',
547
- },
548
- List: {
549
- eas: 'bytes32[]',
550
- },
551
- File: {
552
- eas: 'bytes32',
553
- },
554
- Json: {
555
- eas: 'bytes32',
556
- },
557
- Blob: {
558
- eas: 'bytes32',
559
- },
560
- Boolean: {
561
- eas: 'bool',
562
- },
563
- Date: {
564
- eas: 'uint256',
565
- },
566
- Html: {
567
- eas: 'bytes32',
568
- },
569
- };
570
- /** Full property names ending in `Id` that must not be stripped to a “base” name for `allProperties` keys. */
571
- const PROPERTY_NAMES_EXEMPT_FROM_ID_SUFFIX_STRIP = new Set(['storageTransactionId']);
572
- const INTERNAL_PROPERTY_NAMES = [
573
- 'localId',
574
- 'uid',
575
- 'seedLocalId',
576
- 'seedUid',
577
- 'schemaUid',
578
- 'attestationCreatedAt',
579
- 'attestationRaw',
580
- 'createdAt',
581
- 'updatedAt',
582
- 'lastVersionPublishedAt',
583
- 'latestVersionLocalId',
584
- 'latestVersionUid',
585
- 'lastLocalUpdateAt',
586
- 'modelName',
587
- 'refSeedType',
588
- 'refValueType',
589
- 'refResolvedValue',
590
- 'refResolvedDisplayValue',
591
- 'type',
592
- // Image
593
- 'src',
594
- 'alt',
595
- //
596
- 'versionLocalId',
597
- 'versionsCount',
598
- 'versionUid',
599
- '_markedForDeletion',
600
- // SDK-internal: wallet address of attester; never publish as property attestation
601
- 'publisher',
602
- // SDK-internal: Unix timestamp when attestations were revoked; never a schema field
603
- 'revokedAt',
604
- ];
605
- /** @deprecated The UID is chain-independent. Use {@link VERSION_SCHEMA_UID}. */
606
- const VERSION_SCHEMA_UID_OPTIMISM_SEPOLIA = VERSION_SCHEMA_UID;
607
- const defaultAttestationData = {
608
- recipient: ZERO_ADDRESS,
609
- revocable: true,
610
- value: BigInt(0),
611
- refUID: ZERO_BYTES32,
612
- expirationTime: BigInt(0),
613
- data: ZERO_BYTES,
614
- };
615
- var ImageSize;
616
- (function (ImageSize) {
617
- ImageSize[ImageSize["EXTRA_SMALL"] = 480] = "EXTRA_SMALL";
618
- ImageSize[ImageSize["SMALL"] = 760] = "SMALL";
619
- ImageSize[ImageSize["MEDIUM"] = 1024] = "MEDIUM";
620
- ImageSize[ImageSize["LARGE"] = 1440] = "LARGE";
621
- ImageSize[ImageSize["EXTRA_LARGE"] = 1920] = "EXTRA_LARGE";
622
- })(ImageSize || (ImageSize = {}));
623
- const CLIENT_NOT_INITIALIZED = 'ClientManager is not initialized. Please call init() first.';
624
- var SeedModels;
625
- (function (SeedModels) {
626
- SeedModels["Seed"] = "Seed";
627
- SeedModels["Metadata"] = "Metadata";
628
- SeedModels["Version"] = "Version";
629
- })(SeedModels || (SeedModels = {}));
630
- // Internal SDK schema that should not be created in app's files directory
631
- const SEED_PROTOCOL_SCHEMA_NAME = 'Seed Protocol';
632
- const INTERNAL_SCHEMA_IDS = ['SEEDPROTOCOL'];
633
- /** Fired after `syncDbWithEas` persists EAS seeds/versions/metadata to SQLite (see `Item.rehydrateCachedItemsFromDbAfterEasSync`). */
634
- const EAS_SEED_DATA_SYNCED_TO_DB_EVENT = 'easSeedDataSyncedToDb';
635
- /**
636
- * Check if a schema is an internal SDK schema that should not be created in app files
637
- */
638
- function isInternalSchema(schemaName, schemaId) {
639
- return schemaName === SEED_PROTOCOL_SCHEMA_NAME ||
640
- (schemaId !== undefined && INTERNAL_SCHEMA_IDS.includes(schemaId));
641
- }
642
- /**
643
- * Model ids (schemaFileId) of the internal Seed Protocol storage models. Storage seeds are always
644
- * these models, even if a user schema also defines a model named Image/File/Html.
645
- */
646
- const INTERNAL_STORAGE_MODEL_FILE_IDS = {
647
- image: 'SEEDPROTOCOL_IMAGE',
648
- file: 'SEEDPROTOCOL_FILE',
649
- html: 'SEEDPROTOCOL_HTML',
650
- };
651
-
652
- /** Ref model name when propDef is a List of Relation, else undefined. */
653
- function listRelationRef(propDef) {
654
- if (!propDef)
655
- return undefined;
656
- if (normalizeDataType(propDef.dataType) !== 'List')
657
- return undefined;
658
- return propDef.ref || propDef.refModelName || undefined;
659
- }
660
- /**
661
- * Storage/EAS name for a List-of-relation property (`authorIdentityIds`), given either its
662
- * schema key (`authors`) or its storage name. A name already in storage form is returned as-is,
663
- * so this is safe on ItemProperties built before their schema was known.
664
- * Returns undefined when propDef is not a List of Relation.
665
- */
666
- function listRelationEasPropertyName(propertyName, propDef) {
667
- const ref = listRelationRef(propDef);
668
- if (!ref || !propertyName)
669
- return undefined;
670
- if (propertyName.endsWith(`${ref}Ids`))
671
- return propertyName;
672
- return `${pluralize(propertyName, 1)}${ref}Ids`;
673
- }
674
- /**
675
- * For a schema property key like `authors` (List of Relation to Identity), returns the
676
- * internal storage/EAS name: `authorIdentityIds` (singular + ref + Ids).
677
- */
678
- function listRelationStoragePropertyName(propertySchemas, schemaPropertyKey) {
679
- return listRelationEasPropertyName(schemaPropertyKey, propertySchemas[schemaPropertyKey]);
680
- }
681
- /**
682
- * Reverse of listRelationStoragePropertyName: maps DB/EAS name `authorIdentityIds` to schema key `authors`.
683
- */
684
- function resolveStorageNameToSchemaName(propertySchemas, storagePropertyName) {
685
- for (const [schemaKey, propDef] of Object.entries(propertySchemas)) {
686
- if (normalizeDataType(propDef?.dataType) !== 'List')
687
- continue;
688
- const ref = propDef.ref || propDef.refModelName;
689
- if (!ref)
690
- continue;
691
- const internalName = `${pluralize(schemaKey, 1)}${ref}Ids`;
692
- if (internalName === storagePropertyName) {
693
- return schemaKey;
694
- }
695
- }
696
- return undefined;
697
- }
698
- /** Internal property names that never use Id-suffix metadata convention */
699
- const ID_SUFFIX_EXCLUDE = new Set(['storageTransactionId', 'transactionId']);
700
- /** Data types that store metadata with Id suffix in DB */
701
- const METADATA_ID_SUFFIX_TYPES = ['Image', 'File', 'Html', 'Relation'];
702
- /**
703
- * Whether this type uses Id suffix in metadata table.
704
- */
705
- function needsMetadataIdSuffix(dataType, refValueType) {
706
- const dt = normalizeDataType(dataType);
707
- const rvt = normalizeDataType(refValueType);
708
- return (METADATA_ID_SUFFIX_TYPES.includes(dt) ||
709
- METADATA_ID_SUFFIX_TYPES.includes(rvt));
710
- }
711
- /**
712
- * Canonical metadata property name for WRITES.
713
- * Use when creating/updating metadata.
714
- */
715
- function toMetadataPropertyName(propertyName, dataType, refValueType) {
716
- if (ID_SUFFIX_EXCLUDE.has(propertyName))
717
- return propertyName;
718
- if (propertyName.endsWith('Ids'))
719
- return propertyName;
720
- if (propertyName.endsWith('Id'))
721
- return propertyName;
722
- if (needsMetadataIdSuffix(dataType, refValueType)) {
723
- return `${propertyName}Id`;
724
- }
725
- return propertyName;
726
- }
727
- /**
728
- * For READS: property names to include in OR query (both variants).
729
- * When dataType is undefined, includes all variants for safety (backwards compat with getPropertyData).
730
- */
731
- function getMetadataPropertyNamesForQuery(propertyName, dataType, refValueType) {
732
- if (ID_SUFFIX_EXCLUDE.has(propertyName))
733
- return [propertyName];
734
- if (propertyName.endsWith('Ids'))
735
- return [propertyName];
736
- if (propertyName.endsWith('Id')) {
737
- const base = propertyName.slice(0, -2);
738
- return [propertyName, base];
739
- }
740
- const needsId = needsMetadataIdSuffix(dataType, refValueType) ||
741
- (dataType === undefined && refValueType === undefined);
742
- if (needsId) {
743
- return [propertyName, `${propertyName}Id`, `${propertyName}Ids`];
744
- }
745
- return [propertyName];
746
- }
747
- /**
748
- * The name an item exposes a property under, from the name it is stored under: its alias when it
749
- * has one (List of relation), else `tagIds` → `tags`, `authorId` → `author`. Names in
750
- * PROPERTY_NAMES_EXEMPT_FROM_ID_SUFFIX_STRIP keep their `Id`.
751
- */
752
- function toItemPropertyKey(storedName, alias) {
753
- if (alias)
754
- return alias;
755
- if (storedName.endsWith('Ids'))
756
- return pluralize(storedName.slice(0, -3));
757
- if (storedName.endsWith('Id') && !PROPERTY_NAMES_EXEMPT_FROM_ID_SUFFIX_STRIP.has(storedName)) {
758
- return storedName.slice(0, -2);
759
- }
760
- return storedName;
761
- }
762
- /**
763
- * Schema property name from metadata name (strip Id/Ids suffix).
764
- */
765
- function toSchemaPropertyName(propertyName) {
766
- if (propertyName.endsWith('Ids'))
767
- return propertyName.slice(0, -3);
768
- if (propertyName.endsWith('Id'))
769
- return propertyName.slice(0, -2);
770
- return undefined;
771
- }
772
- /**
773
- * Alternate property name for instance lookup (base <-> Id variant).
774
- */
775
- function getAlternatePropertyNameForInstanceLookup(propertyName) {
776
- if (ID_SUFFIX_EXCLUDE.has(propertyName))
777
- return undefined;
778
- if (propertyName.endsWith('Id'))
779
- return propertyName.slice(0, -2);
780
- return `${propertyName}Id`;
781
- }
782
- /**
783
- * Resolve the best metadata record when multiple variants exist (base + Id).
784
- * For Image/File/Html: prefer the record with refResolvedValue (Id variant has filename).
785
- *
786
- * Records come newest first. Only each name's newest record counts, so an older record of the
787
- * same name (the value before a clear, or an earlier Version's) never wins. A cleared Id record (no
788
- * value, no file) wins over the base variant (ADR 0021).
789
- */
790
- function resolveMetadataRecord(records, preferredPropertyName, dataType, refValueType) {
791
- if (records.length === 0)
792
- throw new Error('No records to resolve');
793
- records = records.filter((record, index) => records.findIndex((other) => other.propertyName === record.propertyName) === index);
794
- if (records.length === 1)
795
- return records[0];
796
- const needsId = needsMetadataIdSuffix(dataType, refValueType) ||
797
- (dataType === undefined && refValueType === undefined);
798
- if (needsId) {
799
- const cleared = records.find(
800
- // A stored row holds null, not undefined, when it has no value
801
- (r) => r.propertyName?.endsWith('Id') && r.propertyValue === null && !r.refResolvedValue);
802
- if (cleared)
803
- return cleared;
804
- }
805
- // For Image/File/Html: prefer the record with refResolvedValue (Id variant has filename)
806
- if (needsId) {
807
- const withRef = records.find((r) => r.refResolvedValue);
808
- if (withRef)
809
- return withRef;
810
- }
811
- // Also prefer by refSeedType when we need file ref
812
- if (needsId) {
813
- const byRefSeed = records.find((r) => r.refSeedType === 'image' ||
814
- r.refSeedType === 'file' ||
815
- r.refSeedType === 'html');
816
- if (byRefSeed)
817
- return byRefSeed;
818
- }
819
- // Fallback: prefer exact propertyName match, else first
820
- return (records.find((r) => r.propertyName === preferredPropertyName) ?? records[0]);
821
- }
822
- /**
823
- * The EAS property name publish writes for an item property (ADR 0008 §3), already in EAS form:
824
- *
825
- * - List of relations: its `<name><Model>Ids` storage name (`author_identity_ids`).
826
- * - Single Relation, Image, File or Html: its schema key (`author`), never the local `<name>Id`
827
- * storage name. The key is `propertyDef.name` when known, else `propertyName` without its `Id`.
828
- * - Anything else: the property name.
829
- */
830
- function easPropertyNameForPublish(propertyName, propertyDef) {
831
- const listRelationName = listRelationEasPropertyName(propertyName, propertyDef ?? undefined);
832
- if (listRelationName)
833
- return easName(listRelationName);
834
- if (propertyDef &&
835
- normalizeDataType(propertyDef.dataType) !== 'List' &&
836
- needsMetadataIdSuffix(propertyDef.dataType, propertyDef.refValueType) &&
837
- !ID_SUFFIX_EXCLUDE.has(propertyName)) {
838
- const key = propertyDef.name ?? (propertyName.endsWith('Id') ? propertyName.slice(0, -2) : propertyName);
839
- return easName(key);
840
- }
841
- return easName(propertyName);
842
- }
843
- /**
844
- * The EAS schema definition publish attests a property under (`string title`, `bytes32 author`),
845
- * or undefined when its data type has no EAS type. Look schemas up by this, not by name: a name
846
- * can have schemas of several types (Optimism Sepolia has `bytes32 storage_transaction_id` beside
847
- * `string storage_transaction_id`).
848
- */
849
- function easSchemaDefinitionForProperty(propertyName, propertyDef) {
850
- if (!propertyDef?.dataType)
851
- return undefined;
852
- const dataType = normalizeDataType(String(propertyDef.dataType));
853
- const easType = INTERNAL_DATA_TYPES[dataType]?.eas;
854
- if (!easType)
855
- return undefined;
856
- return `${easType} ${easPropertyNameForPublish(propertyName, propertyDef)}`;
857
- }
858
-
859
- /** Same rules as getCorrectId in helpers/index (avoid barrel circular import). */
860
- function parseLocalIdOrUid(localIdOrUid) {
861
- const id = { localId: undefined, uid: undefined };
862
- if (!localIdOrUid)
863
- return id;
864
- if (localIdOrUid.length === 10) {
865
- id.localId = localIdOrUid;
866
- }
867
- if (localIdOrUid.startsWith('0x') && localIdOrUid.length === 66) {
868
- id.uid = localIdOrUid;
869
- }
870
- return id;
871
- }
872
- /** Relation/image refs may be string ids or `{ seedLocalId?, seedUid? }`. */
873
- function normalizeRelationPropertyValue(value) {
874
- if (value == null)
875
- return undefined;
876
- if (typeof value === 'string') {
877
- const t = value.trim();
878
- return t !== '' ? t : undefined;
879
- }
880
- if (typeof value === 'object' && value !== null) {
881
- const o = value;
882
- if (typeof o.seedLocalId === 'string' && o.seedLocalId.trim() !== '') {
883
- return o.seedLocalId.trim();
884
- }
885
- if (typeof o.seedUid === 'string' && o.seedUid.trim() !== '') {
886
- return o.seedUid.trim();
887
- }
888
- }
889
- return undefined;
890
- }
891
- /** getCorrectId only accepts 10-char local ids and 0x66 uids; local seed refs can be up to 21 chars. */
892
- const LOCAL_SEED_REF = /^[a-zA-Z0-9_-]{10,21}$/;
893
- /** Resolve seed ids from a single string (context propertyValue or DB row). */
894
- function resolveSeedIdsFromRefString(s) {
895
- const fromCorrect = parseLocalIdOrUid(s);
896
- if (fromCorrect.localId || fromCorrect.uid) {
897
- return { seedLocalId: fromCorrect.localId, seedUid: fromCorrect.uid };
898
- }
899
- if (LOCAL_SEED_REF.test(s) && !s.startsWith('0x')) {
900
- return { seedLocalId: s };
901
- }
902
- return {};
903
- }
904
- /**
905
- * True when a relation ref is an attested seed uid (0x…) rather than a local id. A uid only exists once
906
- * the seed is on EAS, so publish can attest the uid as-is when no local copy exists (e.g. a server
907
- * publishing on someone's behalf that only holds the uids it was given). Callers must not walk into it.
908
- */
909
- function isPublishedSeedRef(value) {
910
- const ref = normalizeRelationPropertyValue(value);
911
- if (!ref)
912
- return false;
913
- const { seedLocalId, seedUid } = resolveSeedIdsFromRefString(ref);
914
- return !!seedUid && !seedLocalId;
915
- }
916
- /**
917
- * Storage seed local id (nanoid) or 0x uid. Stricter than resolveSeedIdsFromRefString, which accepts
918
- * any 10-char string (so a 10-char html snippet like "<p>raw</p>" would pass as a local id).
919
- */
920
- const STORAGE_SEED_REF = /^(?:[A-Za-z0-9_-]{10,21}|0x[0-9a-fA-F]{64})$/;
921
- /** True when a File/Image/Html value is already a storage seed reference rather than raw content. */
922
- function isStorageSeedRef(value) {
923
- return typeof value === 'string' && STORAGE_SEED_REF.test(value.trim());
924
- }
925
-
926
- const seeds = sqliteTable('seeds', {
927
- localId: text('local_id').unique(),
928
- uid: text('uid'),
929
- schemaUid: text('schema_uid'),
930
- type: text('type'),
931
- // schemaFileId of the models row this item belongs to (= Model.id). Disambiguates items whose
932
- // model name exists in more than one schema; null for legacy rows that couldn't be backfilled.
933
- modelFileId: text('model_file_id'),
934
- publisher: text('publisher'),
935
- attestationRaw: text('attestation_raw'),
936
- attestationCreatedAt: int('attestation_created_at'),
937
- // Set on insert when the writer leaves it out. No $onUpdate: readers rank rows by
938
- // COALESCE(attestation_created_at, created_at), and some writers move it on purpose.
939
- createdAt: int('created_at').$defaultFn(() => Date.now()),
940
- // Set on every insert and update unless the writer passes a value.
941
- updatedAt: int('updated_at')
942
- .$defaultFn(() => Date.now())
943
- .$onUpdate(() => Date.now()),
944
- _markedForDeletion: int('_marked_for_deletion'),
945
- revokedAt: int('revoked_at'),
946
- }, (table) => [index('seeds_uid_idx').on(table.uid), index('seeds_type_idx').on(table.type)]);
947
-
948
- const versions = sqliteTable('versions', {
949
- localId: text('local_id').unique(),
950
- uid: text('uid'),
951
- seedLocalId: text('seed_local_id'),
952
- seedUid: text('seed_uid'),
953
- seedType: text('seed_type'),
954
- note: text('note'),
955
- // Set on insert when the writer leaves it out. No $onUpdate: readers rank rows by
956
- // COALESCE(attestation_created_at, created_at), and some writers move it on purpose.
957
- createdAt: int('created_at').$defaultFn(() => Date.now()),
958
- // Set on every insert and update unless the writer passes a value.
959
- updatedAt: int('updated_at')
960
- .$defaultFn(() => Date.now())
961
- .$onUpdate(() => Date.now()),
962
- attestationCreatedAt: int('attestation_created_at'),
963
- attestationRaw: text('attestation_raw'),
964
- publisher: text('publisher'),
965
- /**
966
- * When the version attestation was revoked, in Unix seconds (EAS `revocationTime`, or the local
967
- * unpublish time until sync confirms it). Null while live or unpublished.
968
- */
969
- revokedAt: int('revoked_at'),
970
- }, (table) => [index('versions_seed_local_id_idx').on(table.seedLocalId)]);
971
-
972
- const metadata = sqliteTable('metadata', {
973
- localId: text('local_id').unique(),
974
- uid: text('uid'),
975
- propertyId: integer('property_id').references(() => properties.id),
976
- propertyName: text('property_name'),
977
- propertyValue: text('property_value'),
978
- schemaUid: text('schema_uid'),
979
- modelType: text('model_type'),
980
- seedLocalId: text('seed_local_id'),
981
- seedUid: text('seed_uid'),
982
- versionLocalId: text('version_local_id'),
983
- versionUid: text('version_uid'),
984
- easDataType: text('eas_data_type'),
985
- refValueType: text('ref_value_type'),
986
- refModelUid: text('ref_schema_uid'),
987
- refSeedType: text('ref_seed_type'),
988
- refResolvedValue: text('ref_resolved_value'),
989
- refResolvedDisplayValue: text('ref_resolved_display_value'),
990
- localStorageDir: text('local_storage_dir'),
991
- attestationRaw: text('attestation_raw'),
992
- attestationCreatedAt: int('attestation_created_at'),
993
- contentHash: text('content_hash'),
994
- // Set on insert when the writer leaves it out. No $onUpdate: readers rank rows by
995
- // COALESCE(attestation_created_at, created_at), and some writers move it on purpose.
996
- createdAt: int('created_at').$defaultFn(() => Date.now()),
997
- // Set on every insert and update unless the writer passes a value.
998
- updatedAt: int('updated_at')
999
- .$defaultFn(() => Date.now())
1000
- .$onUpdate(() => Date.now()),
1001
- publisher: text('publisher'),
1002
- // Unix seconds when this row's attestation was revoked on EAS; null while it's live (or for a
1003
- // local, unpublished row). EAS sync keeps only the canonical attestation per (version, property
1004
- // schema): the newest live one, or the newest revoked one when all are revoked. So a non-null
1005
- // value marks a property kept only for its last value, not one that is present on the version.
1006
- revokedAt: int('revoked_at'),
1007
- // For a row EAS sync derived for an ItemStorage property from a `storage_transaction_id`
1008
- // attestation (uid null, ref_value_type 'file', value = the transaction id): that attestation's
1009
- // uid. Null for every other row. Marks the row as published content, not a local draft.
1010
- derivedFromUid: text('derived_from_uid'),
1011
- // On a cleared local row (null value): publish revokes the property's attestations instead of
1012
- // attesting the empty value (ADR 0021). Null on every other row.
1013
- retract: int('retract', { mode: 'boolean' }),
1014
- }, (table) => [
1015
- index('metadata_seed_local_id_property_name_idx').on(table.seedLocalId, table.propertyName),
1016
- index('metadata_seed_uid_idx').on(table.seedUid),
1017
- ]);
1018
-
1019
- const appState = sqliteTable('appState', {
1020
- key: text('key').unique(),
1021
- value: text('value'),
1022
- createdAt: int('created_at'),
1023
- updatedAt: int('updated_at'),
1024
- });
1025
-
1026
- const modelUids = sqliteTable('model_uids', {
1027
- id: int('id').primaryKey({ autoIncrement: true }),
1028
- uid: text('uid').notNull(),
1029
- modelId: int('model_id')
1030
- .notNull()
1031
- .unique()
1032
- .references(() => models.id),
1033
- });
1034
- const modelRelations = relations(modelUids, ({ one }) => ({
1035
- model: one(models),
1036
- }));
1037
-
1038
- const propertyUids = sqliteTable('property_uids', {
1039
- id: int('id').primaryKey({ autoIncrement: true }),
1040
- uid: text('uid').notNull(),
1041
- propertyId: int('property_id')
1042
- .notNull()
1043
- .unique()
1044
- .references(() => properties.id),
1045
- });
1046
- const propertyUidRelations = relations(propertyUids, ({ one }) => ({
1047
- property: one(properties),
1048
- }));
1049
-
1050
- const config = sqliteTable('config', {
1051
- id: integer('id').primaryKey({ autoIncrement: true }),
1052
- key: text('key').notNull(),
1053
- text: text('text'),
1054
- json: text('json'),
1055
- blob: blob('blob'),
1056
- });
1057
-
1058
- const schemas = sqliteTable('schemas', {
1059
- id: integer('id').primaryKey({ autoIncrement: true }),
1060
- name: text('name').notNull(),
1061
- version: integer('version').notNull(),
1062
- schemaFileId: text('schema_file_id'), // ID from JSON file for change tracking - must be unique
1063
- schemaData: text('schema_data'), // Full JSON schema content (SchemaFileFormat as JSON string)
1064
- isDraft: integer('is_draft', { mode: 'boolean' }), // true if schema is in draft/editing state, false if published to file
1065
- isEdited: integer('is_edited', { mode: 'boolean' }), // true if schema has been edited locally, false if matches schema file
1066
- createdAt: int('created_at'),
1067
- updatedAt: int('updated_at'),
1068
- }, (table) => {
1069
- return {
1070
- uniqueSchemaFileId: unique('unique_schema_schema_file_id').on(table.schemaFileId),
1071
- };
1072
- });
1073
-
1074
- var SchemaSchema = /*#__PURE__*/Object.freeze({
1075
- __proto__: null,
1076
- schemas: schemas
1077
- });
1078
-
1079
- const modelSchemas = sqliteTable('model_schemas', {
1080
- id: integer('id').primaryKey({ autoIncrement: true }),
1081
- modelId: integer('model_id').references(() => models.id),
1082
- schemaId: integer('schema_id').references(() => schemas.id),
1083
- });
1084
-
1085
- var ModelSchemaSchema = /*#__PURE__*/Object.freeze({
1086
- __proto__: null,
1087
- modelSchemas: modelSchemas
1088
- });
1089
-
1090
- const publishProcesses = sqliteTable('publish_processes', {
1091
- id: int('id').primaryKey({ autoIncrement: true }),
1092
- seedLocalId: text('seed_local_id').notNull(),
1093
- modelName: text('model_name').notNull(),
1094
- schemaId: text('schema_id'),
1095
- status: text('status').notNull(), // 'in_progress' | 'completed' | 'failed' | 'interrupted'
1096
- startedAt: int('started_at').notNull(),
1097
- completedAt: int('completed_at'),
1098
- errorMessage: text('error_message'),
1099
- errorStep: text('error_step'),
1100
- errorDetails: text('error_details'),
1101
- errorCode: text('error_code'), // e.g. ManagedAccountPublishError.code
1102
- persistedSnapshot: text('persisted_snapshot').notNull(),
1103
- seedId: text('seed_id'),
1104
- existingSeedUid: text('existing_seed_uid'),
1105
- createdAt: int('created_at'),
1106
- updatedAt: int('updated_at'),
1107
- });
1108
-
1109
- const uploadProcesses = sqliteTable('upload_processes', {
1110
- id: int('id').primaryKey({ autoIncrement: true }),
1111
- reimbursementConfirmed: int('reimbursement_confirmed').notNull(), // 0 or 1 for boolean
1112
- reimbursementTransactionId: text('reimbursement_transaction_id'),
1113
- transactionKeys: text('transaction_keys'),
1114
- persistedSnapshot: text('persisted_snapshot').notNull(), // JSON string
1115
- createdAt: int('created_at'),
1116
- updatedAt: int('updated_at'),
1117
- });
1118
-
1119
- /** Background job: resolve L1 tx id and wait for L1 confirmation after bundler publish. */
1120
- const arweaveL1FinalizeJobs = sqliteTable('arweave_l1_finalize_jobs', {
1121
- id: int('id').primaryKey({ autoIncrement: true }),
1122
- seedLocalId: text('seed_local_id').notNull(),
1123
- dataItemId: text('data_item_id').notNull(),
1124
- /** L1 bundle / anchor transaction id from gateway GraphQL `bundledIn.id`. */
1125
- l1TransactionId: text('l1_transaction_id'),
1126
- bundleId: text('bundle_id'),
1127
- versionLocalId: text('version_local_id'),
1128
- itemPropertyName: text('item_property_name'),
1129
- /** `pending_l1` | `confirmed` | `failed` */
1130
- phase: text('phase').notNull(),
1131
- statusJson: text('status_json'),
1132
- errorMessage: text('error_message'),
1133
- createdAt: int('created_at').notNull(),
1134
- updatedAt: int('updated_at').notNull(),
1135
- }, (table) => ({
1136
- dataItemIdUnique: uniqueIndex('arweave_l1_finalize_jobs_data_item_id_unique').on(table.dataItemId),
1137
- }));
1138
-
1139
- const easSyncProcesses = sqliteTable('eas_sync_processes', {
1140
- id: int('id').primaryKey({ autoIncrement: true }),
1141
- status: text('status').notNull(), // 'in_progress' | 'completed' | 'failed'
1142
- startedAt: int('started_at').notNull(),
1143
- completedAt: int('completed_at'),
1144
- requestPayload: text('request_payload').notNull(),
1145
- errorMessage: text('error_message'),
1146
- errorDetails: text('error_details'),
1147
- persistedSnapshot: text('persisted_snapshot').notNull(),
1148
- createdAt: int('created_at'),
1149
- updatedAt: int('updated_at'),
1150
- });
1151
-
1152
- /**
1153
- * Links Image seeds materialized from Html `data:image/*` URIs to the parent Item seed for co-publish.
1154
- * Rows are removed after a successful publish (see clearHtmlEmbeddedImageCoPublishRows).
1155
- */
1156
- const htmlEmbeddedImageCoPublish = sqliteTable('html_embedded_image_co_publish', {
1157
- id: int('id').primaryKey({ autoIncrement: true }),
1158
- parentSeedLocalId: text('parent_seed_local_id').notNull(),
1159
- htmlSeedLocalId: text('html_seed_local_id').notNull(),
1160
- imageSeedLocalId: text('image_seed_local_id').notNull(),
1161
- /** Stable dedup key (e.g. sha256 of data URI or normalized URI string). */
1162
- stableKey: text('stable_key').notNull(),
1163
- createdAt: int('created_at').notNull(),
1164
- }, (t) => [
1165
- uniqueIndex('html_embed_co_pub_parent_html_stable').on(t.parentSeedLocalId, t.htmlSeedLocalId, t.stableKey),
1166
- ]);
1167
-
1168
- /**
1169
- * The item write outbox (ADR 0019): one row per edit to an item, appended when the value is
1170
- * assigned and applied to `seeds`, `versions` and `metadata` by the leader tab's drainer.
1171
- */
1172
- const pendingWrites = sqliteTable('pending_writes', {
1173
- id: int('id').primaryKey({ autoIncrement: true }),
1174
- /** Unique per op; the drainer and `save()` refer to an op by it. */
1175
- opId: text('op_id').notNull(),
1176
- /** Unused: nothing appends grouped ops. Kept until a migration drops it. */
1177
- groupId: text('group_id'),
1178
- seedLocalId: text('seed_local_id').notNull(),
1179
- /** Null for ops on the seed itself (`create`, `delete`). */
1180
- propertyName: text('property_name'),
1181
- /** `create` | `set` | `delete` | `storage` */
1182
- kind: text('kind').notNull(),
1183
- /** JSON-encoded value. */
1184
- value: text('value'),
1185
- /** Unused: a storage op's staged path is in its JSON value. Kept until a migration drops it. */
1186
- valueRef: text('value_ref'),
1187
- /** The stored metadata row the edit was made against, to detect a synced row written since. */
1188
- baseRowId: text('base_row_id'),
1189
- /** Unused: the base uid is read through base_row_id. Kept until a migration drops it. */
1190
- baseUid: text('base_uid'),
1191
- createdAt: int('created_at').notNull(),
1192
- /** The tab that appended the op. */
1193
- tabId: text('tab_id'),
1194
- /** `pending` | `applied` | `superseded` | `failed`. An op is claimed and applied in one transaction. */
1195
- status: text('status').notNull(),
1196
- error: text('error'),
1197
- /** The name of the error that failed the op (`OwnershipError`, say), so a waiter can tell why. */
1198
- errorName: text('error_name'),
1199
- attempts: int('attempts').notNull().default(0),
1200
- appliedAt: int('applied_at'),
1201
- /** JSON the applier recorded when the op applied (for a `set`: the metadata row written). */
1202
- result: text('result'),
1203
- /** JSON: the synced value this op was applied over, when there was one (ADR 0019). */
1204
- conflict: text('conflict'),
1205
- conflictResolvedAt: int('conflict_resolved_at'),
1206
- }, (table) => ({
1207
- opIdUnique: uniqueIndex('pending_writes_op_id_unique').on(table.opId),
1208
- /** The drainer's order: per property, oldest first, among one status. */
1209
- drainOrder: index('pending_writes_drain_order').on(table.status, table.seedLocalId, table.propertyName, table.createdAt),
1210
- }));
1211
-
1212
- /**
1213
- * The entity change log (ADR 0020): one row per row written to `seeds`, `versions` or `metadata`,
1214
- * inserted by triggers (migration 0023), so it commits or rolls back with the write. The browser's
1215
- * change feed reads it to tell loaded Items and ItemProperties which seeds changed. Nothing else
1216
- * writes it; the leader tab prunes rows older than 10 minutes.
1217
- */
1218
- const entityChanges = sqliteTable('entity_changes', {
1219
- seq: int('seq').primaryKey({ autoIncrement: true }),
1220
- /** `seeds` | `versions` | `metadata` */
1221
- tbl: text('tbl').notNull(),
1222
- seedLocalId: text('seed_local_id'),
1223
- seedUid: text('seed_uid'),
1224
- /** Set for `metadata` rows only. */
1225
- propertyName: text('property_name'),
1226
- /** Unix ms when the trigger ran. */
1227
- at: int('at').notNull(),
1228
- }, (table) => ({
1229
- at: index('entity_changes_at').on(table.at),
1230
- }));
1231
-
1232
- const seedUidToStorageTransactionId = new Map();
1233
- const getStorageTransactionIdForSeedUid = async (seedUid) => {
1234
- if (seedUidToStorageTransactionId.has(seedUid)) {
1235
- return seedUidToStorageTransactionId.get(seedUid);
1236
- }
1237
- const appDb = BaseDb.getAppDb();
1238
- const results = (await appDb
1239
- .select({
1240
- storageTransactionId: metadata.propertyValue,
1241
- })
1242
- .from(metadata)
1243
- .where(and(eq(metadata.seedUid, seedUid), eq(metadata.propertyName, 'storageTransactionId'))));
1244
- if (!results || results.length === 0) {
1245
- return;
1246
- }
1247
- seedUidToStorageTransactionId.set(seedUid, results[0].storageTransactionId);
1248
- return results[0].storageTransactionId;
1249
- };
1250
-
1251
- const logger$4 = debug('seedSdk:helpers:mediaRef');
1252
- /** Typical Arweave transaction id: 43 URL-safe base64 characters. */
1253
- const ARWEAVE_TX_ID = /^[a-zA-Z0-9_-]{43}$/;
1254
- function trimOrEmpty(raw) {
1255
- if (raw == null)
1256
- return '';
1257
- return typeof raw === 'string' ? raw.trim() : String(raw).trim();
1258
- }
1259
- function coalesceStringField(item, key) {
1260
- const v = item[key];
1261
- if (v == null)
1262
- return '';
1263
- if (typeof v === 'string')
1264
- return v.trim();
1265
- if (typeof v === 'number' || typeof v === 'boolean')
1266
- return String(v);
1267
- return '';
1268
- }
1269
- /**
1270
- * Try JSON relation payloads and plain relation id strings.
1271
- */
1272
- function unwrapRelationString(raw) {
1273
- const t = raw.trim();
1274
- if (!t)
1275
- return t;
1276
- try {
1277
- const parsed = JSON.parse(t);
1278
- const fromObj = normalizeRelationPropertyValue(parsed);
1279
- if (fromObj)
1280
- return fromObj;
1281
- }
1282
- catch {
1283
- // not JSON
1284
- }
1285
- return t;
1286
- }
1287
- /**
1288
- * Pure, sync classification of a media-related string from feeds or XML.
1289
- */
1290
- function classifyMediaRef(raw, options) {
1291
- const s = trimOrEmpty(raw);
1292
- if (!s) {
1293
- return { kind: 'empty' };
1294
- }
1295
- const treatAs = options?.treatAs;
1296
- if (treatAs === 'url') {
1297
- return { kind: 'url', href: s };
1298
- }
1299
- if (treatAs === 'arweaveTx') {
1300
- return { kind: 'arweaveTxId', txId: s };
1301
- }
1302
- if (treatAs === 'seedUid') {
1303
- if (s.startsWith('0x') && s.length === 66) {
1304
- return { kind: 'seedUid', uid: s };
1305
- }
1306
- return { kind: 'unknown', raw: s };
1307
- }
1308
- if (s.startsWith('http://') || s.startsWith('https://') || s.startsWith('blob:') || s.startsWith('data:')) {
1309
- if (isEasAttestationExplorerUrl(s)) {
1310
- return { kind: 'nonMediaUrl', href: s };
1311
- }
1312
- return { kind: 'url', href: s };
1313
- }
1314
- const unwrapped = unwrapRelationString(s);
1315
- const ids = resolveSeedIdsFromRefString(unwrapped);
1316
- if (ids.seedUid) {
1317
- return { kind: 'seedUid', uid: ids.seedUid };
1318
- }
1319
- if (ids.seedLocalId) {
1320
- return { kind: 'seedLocalId', localId: ids.seedLocalId };
1321
- }
1322
- if (unwrapped.startsWith('0x') && unwrapped.length === 66) {
1323
- return { kind: 'seedUid', uid: unwrapped };
1324
- }
1325
- if (ARWEAVE_TX_ID.test(unwrapped) && !unwrapped.startsWith('0x')) {
1326
- return { kind: 'arweaveTxId', txId: unwrapped };
1327
- }
1328
- return { kind: 'unknown', raw: s };
1329
- }
1330
- /**
1331
- * Same as convertTxIdToImage but local to this module to avoid circular imports with helpers/index.
1332
- */
1333
- async function tryLocalBlobUrlFromTxId(txId) {
1334
- const imageFilePath = BaseFileManager.getFilesPath('images', txId);
1335
- const fileExists = await BaseFileManager.pathExists(imageFilePath);
1336
- if (!fileExists) {
1337
- logger$4(`[tryLocalBlobUrlFromTxId] ${imageFilePath} does not exist`);
1338
- return undefined;
1339
- }
1340
- const buffer = await BaseFileManager.readFileAsBuffer(imageFilePath);
1341
- let arrayBuffer;
1342
- if (buffer instanceof Blob) {
1343
- arrayBuffer = await buffer.arrayBuffer();
1344
- }
1345
- else if (buffer instanceof ArrayBuffer) {
1346
- arrayBuffer = buffer;
1347
- }
1348
- else {
1349
- const sliced = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
1350
- if (sliced instanceof SharedArrayBuffer) {
1351
- const uint8Array = new Uint8Array(sliced);
1352
- arrayBuffer = uint8Array.buffer.slice(0);
1353
- }
1354
- else {
1355
- arrayBuffer = sliced;
1356
- }
1357
- }
1358
- const uint = new Uint8Array(arrayBuffer);
1359
- const imageBlob = new Blob([uint]);
1360
- return URL.createObjectURL(imageBlob);
1361
- }
1362
- async function resolveTxId(txId) {
1363
- const local = await tryLocalBlobUrlFromTxId(txId);
1364
- if (local) {
1365
- return { status: 'ready', href: local, source: 'localBlob' };
1366
- }
1367
- return {
1368
- status: 'ready',
1369
- href: BaseArweaveClient.getRawUrl(txId),
1370
- source: 'gateway',
1371
- };
1372
- }
1373
- /**
1374
- * Resolve a classified media reference to a display URL (https/blob/data), using local files when present.
1375
- */
1376
- async function resolveMediaRef(raw, options) {
1377
- const classification = classifyMediaRef(raw, options);
1378
- switch (classification.kind) {
1379
- case 'empty':
1380
- return { status: 'empty' };
1381
- case 'url':
1382
- return {
1383
- status: 'ready',
1384
- href: classification.href,
1385
- source: 'direct',
1386
- };
1387
- case 'nonMediaUrl':
1388
- return {
1389
- status: 'unresolved',
1390
- reason: 'non_media_url',
1391
- classification,
1392
- };
1393
- case 'seedLocalId':
1394
- return {
1395
- status: 'unresolved',
1396
- reason: 'seed_local_id_not_portable',
1397
- classification,
1398
- };
1399
- case 'unknown':
1400
- return {
1401
- status: 'unresolved',
1402
- reason: 'unknown_ref',
1403
- classification,
1404
- };
1405
- case 'arweaveTxId':
1406
- return resolveTxId(classification.txId);
1407
- case 'seedUid': {
1408
- const appDb = BaseDb.getAppDb();
1409
- if (!appDb) {
1410
- return {
1411
- status: 'unresolved',
1412
- reason: 'seed_uid_requires_app_db',
1413
- classification,
1414
- };
1415
- }
1416
- const storageTransactionId = await getStorageTransactionIdForSeedUid(classification.uid);
1417
- if (!storageTransactionId) {
1418
- return {
1419
- status: 'unresolved',
1420
- reason: 'seed_uid_no_storage_transaction',
1421
- classification,
1422
- };
1423
- }
1424
- return resolveTxId(storageTransactionId);
1425
- }
1426
- default:
1427
- return {
1428
- status: 'unresolved',
1429
- reason: 'unknown_ref',
1430
- classification,
1431
- };
1432
- }
1433
- }
1434
- /**
1435
- * Read a string field from a parsed item, trying camelCase and snake_case keys.
1436
- */
1437
- function getFeedItemStringField(item, key) {
1438
- const direct = coalesceStringField(item, key);
1439
- if (direct)
1440
- return direct;
1441
- const snake = key.replace(/[A-Z]/g, (l) => `_${l.toLowerCase()}`);
1442
- if (snake !== key) {
1443
- const alt = coalesceStringField(item, snake);
1444
- if (alt)
1445
- return alt;
1446
- }
1447
- const parts = key.split('_');
1448
- if (parts.length > 1) {
1449
- const camel = parts[0] + parts.slice(1).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join('');
1450
- const alt2 = coalesceStringField(item, camel);
1451
- if (alt2)
1452
- return alt2;
1453
- }
1454
- return '';
1455
- }
1456
- /**
1457
- * Apply a field manifest to a plain feed item: classify media/file fields; pass through html/text.
1458
- */
1459
- function normalizeFeedItemFields(item, manifest) {
1460
- const out = {};
1461
- for (const [fieldKey, descriptor] of Object.entries(manifest)) {
1462
- const raw = getFeedItemStringField(item, fieldKey);
1463
- if (!raw) {
1464
- out[fieldKey] = undefined;
1465
- continue;
1466
- }
1467
- if (descriptor.role === 'html') {
1468
- out[fieldKey] = { role: 'html', raw };
1469
- continue;
1470
- }
1471
- if (descriptor.role === 'text') {
1472
- out[fieldKey] = { role: 'text', raw };
1473
- continue;
1474
- }
1475
- const classification = classifyMediaRef(raw, { treatAs: descriptor.treatAs });
1476
- out[fieldKey] = {
1477
- role: descriptor.role,
1478
- raw,
1479
- classification,
1480
- };
1481
- }
1482
- return out;
1483
- }
1484
-
1485
- let refresher;
1486
- function setCachedPropertyRefresher(fn) {
1487
- refresher = fn;
1488
- }
1489
- /** Give cached ModelProperty instances of these properties their rows' current values. */
1490
- async function refreshCachedProperties(propertyFileIds) {
1491
- if (!refresher || propertyFileIds.length === 0)
1492
- return;
1493
- await refresher(propertyFileIds);
1494
- }
1495
-
1496
- /**
1497
- * Detect SQLite UNIQUE constraint failures across better-sqlite3 and drizzle/wasm wrappers.
1498
- * Drizzle often surfaces `Failed query: insert into...` without "UNIQUE constraint" on message,
1499
- * with the real SQLITE_CONSTRAINT_UNIQUE on cause / nested error.
1500
- */
1501
- function isSqliteUniqueConstraintError(error) {
1502
- const seen = new Set();
1503
- const queue = [error];
1504
- while (queue.length > 0) {
1505
- const current = queue.shift();
1506
- if (current == null || seen.has(current))
1507
- continue;
1508
- seen.add(current);
1509
- if (typeof current === 'string') {
1510
- if (matchesUniqueConstraintText(current))
1511
- return true;
1512
- continue;
1513
- }
1514
- if (typeof current !== 'object')
1515
- continue;
1516
- const err = current;
1517
- const code = err.code;
1518
- if (code === 'SQLITE_CONSTRAINT_UNIQUE' ||
1519
- code === 2067 ||
1520
- code === '2067') {
1521
- return true;
1522
- }
1523
- for (const key of ['message', 'msg', 'stack']) {
1524
- const raw = err[key];
1525
- const value = key === 'stack' && typeof raw === 'string' ? stackWithoutFrames(raw) : raw;
1526
- if (typeof value === 'string' && matchesUniqueConstraintText(value)) {
1527
- return true;
1528
- }
1529
- }
1530
- // Drizzle / wasm often nest the real SQLite error
1531
- for (const key of ['cause', 'reason', 'error', 'originalError', 'err']) {
1532
- if (err[key] != null)
1533
- queue.push(err[key]);
1534
- }
1535
- }
1536
- return false;
1537
- }
1538
- function matchesUniqueConstraintText(text) {
1539
- const lower = text.toLowerCase();
1540
- return (lower.includes('unique constraint') ||
1541
- lower.includes('sqlite_constraint_unique') ||
1542
- (lower.includes('constraint failed') && lower.includes('unique')) ||
1543
- /\brc\s*=\s*2067\b/i.test(text) ||
1544
- (text.includes('2067') && lower.includes('constraint')));
1545
- }
1546
- /**
1547
- * A stack's message lines without its frames. Frames hold file names and line:column numbers, so
1548
- * a frame in a file named like this one, with a 2067 in its position or URL timestamp, would match.
1549
- */
1550
- function stackWithoutFrames(stack) {
1551
- return stack
1552
- .split('\n')
1553
- .filter((line) => !/^\s*at\s/.test(line) && !/@\S*:\d+:\d+\s*$/.test(line))
1554
- .join('\n');
1555
- }
1556
-
1557
- const level = (value) => (isNameMatchLevel(value) ? value : undefined);
1558
- const tighter = (a, b) => a === undefined || compareNameMatchTiers(b, a) < 0 ? b : a;
1559
- /** The levels in one schema file for one of its models, or undefined when the file lacks the model. */
1560
- const modelLevelsFromFile = (file, modelName) => {
1561
- const modelDef = file.models?.[modelName];
1562
- if (!modelDef)
1563
- return undefined;
1564
- const model = level(modelDef.nameMatch) ?? level(file.nameMatch) ?? DEFAULT_NAME_MATCH_LEVEL;
1565
- const properties = new Map();
1566
- for (const [propertyName, def] of Object.entries(modelDef.properties ?? {})) {
1567
- properties.set(propertyName, level(def?.nameMatch) ?? model);
1568
- }
1569
- return { model, properties };
1570
- };
1571
- /**
1572
- * Builds NameMatchLevels from the schema files that own each model row. Exported for tests;
1573
- * use {@link nameMatchLevels}.
1574
- */
1575
- const nameMatchLevelsFromRows = (rows) => {
1576
- const byFileId = new Map();
1577
- const byName = new Map();
1578
- const parsed = new Map();
1579
- for (const row of rows) {
1580
- if (!row.schemaData)
1581
- continue;
1582
- if (!parsed.has(row.schemaData)) {
1583
- try {
1584
- parsed.set(row.schemaData, JSON.parse(row.schemaData));
1585
- }
1586
- catch {
1587
- parsed.set(row.schemaData, undefined);
1588
- }
1589
- }
1590
- const file = parsed.get(row.schemaData);
1591
- const levels = file && modelLevelsFromFile(file, row.modelName);
1592
- if (!levels)
1593
- continue;
1594
- if (row.modelFileId)
1595
- byFileId.set(row.modelFileId, levels);
1596
- const seen = byName.get(row.modelName);
1597
- if (!seen) {
1598
- byName.set(row.modelName, { model: levels.model, properties: new Map(levels.properties) });
1599
- continue;
1600
- }
1601
- seen.model = tighter(seen.model, levels.model);
1602
- for (const [name, value] of levels.properties)
1603
- seen.properties.set(name, tighter(seen.properties.get(name), value));
1604
- }
1605
- return {
1606
- model: (modelName) => byName.get(modelName)?.model ?? DEFAULT_NAME_MATCH_LEVEL,
1607
- property: (modelName, propertyName, modelFileId) => {
1608
- const levels = (modelFileId ? byFileId.get(modelFileId) : undefined) ?? byName.get(modelName);
1609
- return levels?.properties.get(propertyName) ?? levels?.model ?? DEFAULT_NAME_MATCH_LEVEL;
1610
- },
1611
- };
1612
- };
1613
- const DEFAULT_LEVELS = {
1614
- model: () => DEFAULT_NAME_MATCH_LEVEL,
1615
- property: () => DEFAULT_NAME_MATCH_LEVEL,
1616
- };
1617
- let current;
1618
- let loading;
1619
- /** Reads the levels from the schema files stored with each model's schema row (schemas.schema_data). */
1620
- const loadNameMatchLevels = async (db = BaseDb.getAppDb()) => {
1621
- if (!db)
1622
- return DEFAULT_LEVELS;
1623
- const load = (async () => {
1624
- const rows = (await db
1625
- .select({ modelName: models.name, modelFileId: models.schemaFileId, schemaData: schemas.schemaData })
1626
- .from(models)
1627
- .innerJoin(modelSchemas, eq(models.id, modelSchemas.modelId))
1628
- .innerJoin(schemas, eq(modelSchemas.schemaId, schemas.id)));
1629
- return nameMatchLevelsFromRows(rows);
1630
- })();
1631
- loading = load;
1632
- try {
1633
- const levels = await load;
1634
- if (loading === load) {
1635
- current = levels;
1636
- previous = undefined;
1637
- }
1638
- return levels;
1639
- }
1640
- finally {
1641
- if (loading === load)
1642
- loading = undefined;
1643
- }
1644
- };
1645
- /** The levels, loaded once and reused until a schema write clears them. */
1646
- const nameMatchLevels = async (db = BaseDb.getAppDb()) => current ?? loading ?? loadNameMatchLevels(db);
1647
- /**
1648
- * The last loaded levels, for synchronous callers (Model.findByModelType). Init loads them before
1649
- * it reports ready. After a schema write clears them, this returns the levels from before the
1650
- * write until the reload that clear starts finishes.
1651
- */
1652
- const currentNameMatchLevels = () => current ?? previous ?? DEFAULT_LEVELS;
1653
- let previous;
1654
- let reloadScheduled = false;
1655
- /**
1656
- * Called when a schema row or its models are written. The next {@link nameMatchLevels} read
1657
- * reloads, and a reload starts on the next tick (one per burst of writes) for synchronous callers.
1658
- */
1659
- const clearNameMatchLevels = () => {
1660
- if (current)
1661
- previous = current;
1662
- current = undefined;
1663
- loading = undefined;
1664
- if (reloadScheduled)
1665
- return;
1666
- reloadScheduled = true;
1667
- setTimeout(() => {
1668
- reloadScheduled = false;
1669
- if (current || loading || !BaseDb.isAppDbReady())
1670
- return;
1671
- loadNameMatchLevels().catch(() => {
1672
- // The next nameMatchLevels() read retries.
1673
- });
1674
- }, 0);
1675
- };
1676
- /**
1677
- * `nameMatch` values in a schema file that aren't a level. They are ignored (the next level up
1678
- * applies); schema loads warn about them.
1679
- */
1680
- const invalidNameMatchValues = (file) => {
1681
- const problems = [];
1682
- const check = (where, value) => {
1683
- if (value !== undefined && !isNameMatchLevel(value)) {
1684
- problems.push(`${where} nameMatch ${JSON.stringify(value)} is not 'exact', 'normalized' or 'fuzzy'`);
1685
- }
1686
- };
1687
- check('schema', file.nameMatch);
1688
- for (const [modelName, modelDef] of Object.entries(file.models ?? {})) {
1689
- check(modelName, modelDef?.nameMatch);
1690
- for (const [propertyName, def] of Object.entries(modelDef?.properties ?? {})) {
1691
- check(`${modelName}.${propertyName}`, def?.nameMatch);
1692
- }
1693
- }
1694
- return problems;
1695
- };
1696
- /** Warns about {@link invalidNameMatchValues}. */
1697
- const warnOnInvalidNameMatch = (file) => {
1698
- const problems = invalidNameMatchValues(file);
1699
- for (const problem of problems) {
1700
- console.warn(`[seed] schema ${file.metadata?.name ?? ''}: ${problem}; it is ignored.`);
1701
- }
1702
- return problems;
1703
- };
1704
-
1705
- /**
1706
- * Thrown when a model name exists in more than one schema and nothing says which one is meant.
1707
- * Model names are only unique within a schema; pass `schemaName` (or `modelFileId`) to choose.
1708
- */
1709
- class AmbiguousModelError extends Error {
1710
- constructor(modelName, schemaNames) {
1711
- const names = [...new Set(schemaNames)].sort();
1712
- super(`Model "${modelName}" exists in schemas ${names.map((n) => `"${n}"`).join(', ')}; ` +
1713
- `pass schemaName (or modelFileId) to choose one.`);
1714
- this.name = 'AmbiguousModelError';
1715
- this.modelName = modelName;
1716
- this.schemaNames = names;
1717
- }
1718
- }
1719
-
1720
- const columns = {
1721
- id: models.id,
1722
- name: models.name,
1723
- schemaFileId: models.schemaFileId,
1724
- };
1725
- /**
1726
- * Resolve a models row from a model name plus whatever scope is known, most specific first:
1727
- * modelFileId, modelId, then schemaId/schemaName + name, then the name alone. The name alone is
1728
- * only accepted when it is unambiguous; otherwise this throws AmbiguousModelError.
1729
- */
1730
- const resolveModelRecord = async (modelName, scope = {}, db = BaseDb.getAppDb()) => {
1731
- if (!db)
1732
- return undefined;
1733
- if (scope.modelFileId) {
1734
- const rows = await db
1735
- .select(columns)
1736
- .from(models)
1737
- .where(eq(models.schemaFileId, scope.modelFileId))
1738
- .limit(1);
1739
- if (rows.length > 0)
1740
- return rows[0];
1741
- }
1742
- if (scope.modelId) {
1743
- const rows = await db.select(columns).from(models).where(eq(models.id, scope.modelId)).limit(1);
1744
- if (rows.length > 0)
1745
- return rows[0];
1746
- }
1747
- if (!modelName)
1748
- return undefined;
1749
- if (scope.schemaId || scope.schemaName) {
1750
- const rows = await db
1751
- .select(columns)
1752
- .from(models)
1753
- .innerJoin(modelSchemas, eq(models.id, modelSchemas.modelId))
1754
- .innerJoin(schemas, eq(modelSchemas.schemaId, schemas.id))
1755
- .where(and(eq(models.name, modelName), scope.schemaId ? eq(schemas.id, scope.schemaId) : eq(schemas.name, scope.schemaName)))
1756
- .limit(1);
1757
- if (rows.length > 0)
1758
- return rows[0];
1759
- }
1760
- return resolveModelRecordByNameOnly(modelName, db);
1761
- };
1762
- /**
1763
- * Name-only resolution. Rows linked to a schema win over unlinked stubs (null-schemaFileId rows
1764
- * from ref resolution); if more than one linked row has the name it is ambiguous and this throws.
1765
- * `label` names the lookup in the error (defaults to the model name).
1766
- */
1767
- const resolveModelRecordByNameOnly = async (modelName, db, label = Array.isArray(modelName) ? modelName.join(', ') : modelName) => {
1768
- const rows = (await db
1769
- .select({ ...columns, schemaName: schemas.name })
1770
- .from(models)
1771
- .leftJoin(modelSchemas, eq(models.id, modelSchemas.modelId))
1772
- .leftJoin(schemas, eq(modelSchemas.schemaId, schemas.id))
1773
- .where(Array.isArray(modelName) ? inArray(models.name, modelName) : eq(models.name, modelName))
1774
- .orderBy(desc(models.id)));
1775
- if (rows.length === 0)
1776
- return undefined;
1777
- const linked = rows.filter((r) => r.schemaName);
1778
- const linkedIds = new Set(linked.map((r) => r.id));
1779
- if (linkedIds.size > 1) {
1780
- throw new AmbiguousModelError(label, linked.map((r) => r.schemaName));
1781
- }
1782
- const chosen = linked[0] ?? rows[0];
1783
- return { id: chosen.id, name: chosen.name, schemaFileId: chosen.schemaFileId };
1784
- };
1785
- /**
1786
- * The model names a model name or EAS model type can refer to: the name itself, else the names
1787
- * that match it in the tightest tier (ADR 0008, as Model.findByModelType matches): `new_model` →
1788
- * "New model", `sync_storage_post` → "SyncStoragePost", `htmlpage` → "HTMLPage". Each name at its
1789
- * nameMatch level (ADR 0009).
1790
- */
1791
- const modelNamesForNameOrType = async (nameOrType, db) => {
1792
- const rows = (await db.selectDistinct({ name: models.name }).from(models));
1793
- const names = rows.map((r) => r.name);
1794
- if (names.includes(nameOrType))
1795
- return [nameOrType];
1796
- const levels = await nameMatchLevels(db);
1797
- return [...localNamesInBestMatchTier(nameOrType, names, { level: (name) => levels.model(name) })];
1798
- };
1799
- /**
1800
- * resolveModelRecord for a model name **or** an EAS model type (snake_case model name, what sync
1801
- * stores in seeds.type): "New model" resolves from `new_model`. Scope and ambiguity behave as in
1802
- * resolveModelRecord: modelFileId/modelId first, then the schema, then the name(s) alone, which throws
1803
- * AmbiguousModelError when models in several schemas match.
1804
- */
1805
- const resolveModelRecordByNameOrType = async (nameOrType, scope = {}, db = BaseDb.getAppDb()) => {
1806
- if (!db)
1807
- return undefined;
1808
- if (scope.modelFileId || scope.modelId) {
1809
- const byId = await resolveModelRecord(undefined, scope, db);
1810
- if (byId)
1811
- return byId;
1812
- }
1813
- if (!nameOrType)
1814
- return undefined;
1815
- const names = await modelNamesForNameOrType(nameOrType, db);
1816
- if (names.length === 0)
1817
- return undefined;
1818
- if (names.length === 1)
1819
- return resolveModelRecord(names[0], { ...scope, modelFileId: undefined, modelId: undefined }, db);
1820
- // Several model names map to this type (e.g. "New model" and "NewModel" in different schemas).
1821
- if (scope.schemaId || scope.schemaName) {
1822
- const rows = (await db
1823
- .select(columns)
1824
- .from(models)
1825
- .innerJoin(modelSchemas, eq(models.id, modelSchemas.modelId))
1826
- .innerJoin(schemas, eq(modelSchemas.schemaId, schemas.id))
1827
- .where(and(inArray(models.name, names), scope.schemaId ? eq(schemas.id, scope.schemaId) : eq(schemas.name, scope.schemaName))));
1828
- if (new Set(rows.map((r) => r.id)).size === 1)
1829
- return rows[0];
1830
- }
1831
- return resolveModelRecordByNameOnly(names, db, nameOrType);
1832
- };
1833
- /**
1834
- * Work out which model (schemaFileId) an item belongs to from whatever is at hand: an explicit
1835
- * modelFileId, the seed's model_file_id, or a metadata row's property_id → properties.model_id.
1836
- */
1837
- const resolveItemModelFileId = async (hints, db = BaseDb.getAppDb()) => {
1838
- if (hints.modelFileId)
1839
- return hints.modelFileId;
1840
- if (!db)
1841
- return undefined;
1842
- if (hints.seedLocalId || hints.seedUid) {
1843
- const rows = await db
1844
- .select({ modelFileId: seeds.modelFileId })
1845
- .from(seeds)
1846
- .where(hints.seedLocalId ? eq(seeds.localId, hints.seedLocalId) : eq(seeds.uid, hints.seedUid))
1847
- .limit(1);
1848
- if (rows[0]?.modelFileId)
1849
- return rows[0].modelFileId;
1850
- }
1851
- if (hints.propertyId) {
1852
- const rows = await db
1853
- .select({ modelFileId: models.schemaFileId })
1854
- .from(properties)
1855
- .innerJoin(models, eq(properties.modelId, models.id))
1856
- .where(eq(properties.id, hints.propertyId))
1857
- .limit(1);
1858
- if (rows[0]?.modelFileId)
1859
- return rows[0].modelFileId;
1860
- }
1861
- return undefined;
1862
- };
1863
- /** ModelScope for an Item (or ItemProperty) instance: its recorded modelFileId (context or seed row) and schemaName. */
1864
- const getItemModelScope = async (item) => {
1865
- let context;
1866
- try {
1867
- context = item.getService?.().getSnapshot().context;
1868
- }
1869
- catch {
1870
- context = undefined;
1871
- }
1872
- const modelFileId = await resolveItemModelFileId({
1873
- modelFileId: context?.modelFileId,
1874
- seedLocalId: item.seedLocalId ?? context?.seedLocalId,
1875
- seedUid: item.seedUid ?? context?.seedUid,
1876
- propertyId: context?.propertyId,
1877
- });
1878
- return { modelFileId, schemaName: context?.schemaName };
1879
- };
1880
- /**
1881
- * Resolve a ref (e.g. a Relation's target model) by name from the schema(s) its owning model belongs
1882
- * to, so "Tag" means the owner schema's Tag rather than any Tag. Falls back to resolveModelRecord.
1883
- */
1884
- const resolveRefModelRecord = async (refModelName, owner, db = BaseDb.getAppDb()) => {
1885
- if (!db || !refModelName)
1886
- return undefined;
1887
- const ownerRow = owner.modelFileId || owner.modelId ? await resolveModelRecord(undefined, owner, db) : undefined;
1888
- const schemaFilter = ownerRow
1889
- ? inArray(modelSchemas.schemaId,
1890
- // any schema the owner is linked to
1891
- db.select({ schemaId: modelSchemas.schemaId }).from(modelSchemas).where(eq(modelSchemas.modelId, ownerRow.id)))
1892
- : owner.schemaId
1893
- ? eq(modelSchemas.schemaId, owner.schemaId)
1894
- : owner.schemaName
1895
- ? eq(schemas.name, owner.schemaName)
1896
- : undefined;
1897
- if (schemaFilter) {
1898
- const rows = await db
1899
- .select(columns)
1900
- .from(models)
1901
- .innerJoin(modelSchemas, eq(models.id, modelSchemas.modelId))
1902
- .innerJoin(schemas, eq(modelSchemas.schemaId, schemas.id))
1903
- .where(and(eq(models.name, refModelName), schemaFilter))
1904
- .limit(1);
1905
- if (rows.length > 0)
1906
- return rows[0];
1907
- }
1908
- return resolveModelRecord(refModelName, {}, db);
1909
- };
1910
-
1911
- var resolveModelRecord$1 = /*#__PURE__*/Object.freeze({
1912
- __proto__: null,
1913
- getItemModelScope: getItemModelScope,
1914
- resolveItemModelFileId: resolveItemModelFileId,
1915
- resolveModelRecord: resolveModelRecord,
1916
- resolveModelRecordByNameOrType: resolveModelRecordByNameOrType,
1917
- resolveRefModelRecord: resolveRefModelRecord
1918
- });
1919
-
1920
- /**
1921
- * Lowercase + trim an address for persist and compare.
1922
- * Does not checksum or validate — test and placeholder addresses must keep working.
1923
- */
1924
- function normalizeHexAddress(addr) {
1925
- return addr.trim().toLowerCase();
1926
- }
1927
- /**
1928
- * Lowercase, drop empties, and dedupe (first-seen order).
1929
- */
1930
- function normalizeAddressList(addrs) {
1931
- if (!addrs?.length)
1932
- return [];
1933
- const seen = new Set();
1934
- const out = [];
1935
- for (const addr of addrs) {
1936
- if (addr == null || addr === '')
1937
- continue;
1938
- const normalized = normalizeHexAddress(String(addr));
1939
- if (!normalized || seen.has(normalized))
1940
- continue;
1941
- seen.add(normalized);
1942
- out.push(normalized);
1943
- }
1944
- return out;
1945
- }
1946
- /**
1947
- * Lowercase a publisher stamp. Empty / null → undefined (omit on write).
1948
- */
1949
- function normalizePublisher(publisher) {
1950
- if (publisher == null || publisher === '')
1951
- return undefined;
1952
- const normalized = normalizeHexAddress(publisher);
1953
- return normalized || undefined;
1954
- }
1955
- /**
1956
- * Normalizes AddressConfiguration to { owned, watched }.
1957
- * - string[] -> { owned: addresses, watched: [] }
1958
- * - { owned, watched? } -> { owned, watched: watched ?? [] }
1959
- * Addresses are lowercased and deduped.
1960
- */
1961
- function normalizeAddressConfig(addresses) {
1962
- if (!addresses) {
1963
- return { owned: [], watched: [] };
1964
- }
1965
- if (Array.isArray(addresses)) {
1966
- return { owned: normalizeAddressList(addresses), watched: [] };
1967
- }
1968
- return {
1969
- owned: normalizeAddressList(addresses.owned),
1970
- watched: normalizeAddressList(addresses.watched),
1971
- };
1972
- }
1973
-
1974
- const pendingLinks = new Map();
1975
- /**
1976
- * Insert the `model_schemas` row linking a model to a schema, unless it already exists.
1977
- *
1978
- * `model_schemas` has no unique constraint, and several writers link the same model during one
1979
- * import (importJsonSchema's addModelsToDb and the write service's writeModelToDb). Each used to
1980
- * check-then-insert on its own, so concurrent writers inserted the link twice. The check and insert
1981
- * run serialized per (model, schema) here.
1982
- *
1983
- * @returns true when this call inserted the link
1984
- */
1985
- function linkModelToSchema(db, modelId, schemaId) {
1986
- const key = `${modelId}:${schemaId}`;
1987
- const previous = pendingLinks.get(key) ?? Promise.resolve(false);
1988
- const current = previous
1989
- .catch(() => false)
1990
- .then(async () => {
1991
- const existing = await db
1992
- .select({ id: modelSchemas.id })
1993
- .from(modelSchemas)
1994
- .where(and(eq(modelSchemas.modelId, modelId), eq(modelSchemas.schemaId, schemaId)))
1995
- .limit(1);
1996
- if (existing.length > 0)
1997
- return false;
1998
- await db.insert(modelSchemas).values({ modelId, schemaId });
1999
- return true;
2000
- });
2001
- pendingLinks.set(key, current);
2002
- const cleanup = () => {
2003
- if (pendingLinks.get(key) === current)
2004
- pendingLinks.delete(key);
2005
- };
2006
- current.then(cleanup, cleanup);
2007
- return current;
2008
- }
2009
-
2010
- const logger$3 = debug('seedSdk:helpers:db');
2011
- /**
2012
- * Posts one message and closes the channel. A message already posted is still delivered after its
2013
- * sender closes, and an open BroadcastChannel keeps a Node process alive (each was a MessagePort
2014
- * handle that stopped scripts exiting).
2015
- */
2016
- const postBroadcast = (channelName, message) => {
2017
- const channel = new BroadcastChannel(channelName);
2018
- try {
2019
- channel.postMessage(message);
2020
- }
2021
- finally {
2022
- channel.close();
2023
- }
2024
- };
2025
- /**
2026
- * The `properties` storage columns for a property definition, in either the internal shape
2027
- * (`storageType`, `localStorageDir`, `filenameSuffix`) or the schema-file shape
2028
- * (`storage: { type, path, extension }`). All null when the definition has no storage config.
2029
- */
2030
- function propertyStorageColumns(def) {
2031
- const normalized = normalizePropertyRecordSchema((def ?? {}));
2032
- return {
2033
- storageType: normalized.storageType ?? null,
2034
- localStorageDir: normalized.localStorageDir ?? null,
2035
- filenameSuffix: normalized.filenameSuffix ?? null,
2036
- };
2037
- }
2038
- /** True when the definition carries any storage setting (so writing its columns can't wipe them by omission). */
2039
- const hasStorageSettings = (columns) => columns.storageType != null || columns.localStorageDir != null || columns.filenameSuffix != null;
2040
- /**
2041
- * Resolve property_id (integer properties.id) from a property's schemaFileId (the string `id` in
2042
- * schema files and propertyRecordSchema). Scoped to the model since schemaFileIds are per schema file.
2043
- * Reads `db` (a transaction's `tx`, say), the app database by default.
2044
- * @returns properties.id or null if not found
2045
- */
2046
- async function getPropertyIdForSchemaFileId(modelNameOrType, schemaFileId, scope = {}, db = BaseDb.getAppDb()) {
2047
- if (!db || !modelNameOrType || !schemaFileId)
2048
- return null;
2049
- const modelRecord = await resolveModelRecordByNameOrType(modelNameOrType, scope, db);
2050
- if (!modelRecord)
2051
- return null;
2052
- const rows = await db
2053
- .select({ id: properties.id })
2054
- .from(properties)
2055
- .where(and(eq(properties.modelId, modelRecord.id), eq(properties.schemaFileId, schemaFileId)))
2056
- .limit(1);
2057
- return rows[0]?.id ?? null;
2058
- }
2059
- /**
2060
- * Resolve property_id from model name/type and property name.
2061
- * Handles property name variants (e.g. htmlId -> html, avatarImageIds -> avatarImages).
2062
- * @param modelNameOrType - Model name (PascalCase) or model type (snake_case)
2063
- * @param propertyName - Property name as stored in metadata (may have Id/Ids suffix)
2064
- * @param scope - Which model is meant when several schemas define one with this name
2065
- * @param db - The database or transaction to read; the app database by default
2066
- * @returns properties.id or null if not found
2067
- */
2068
- async function getPropertyIdForModelAndName(modelNameOrType, propertyName, scope = {}, db = BaseDb.getAppDb()) {
2069
- if (!db)
2070
- return null;
2071
- if (!modelNameOrType || !propertyName)
2072
- return null;
2073
- const modelRecord = await resolveModelRecordByNameOrType(modelNameOrType, scope, db);
2074
- if (!modelRecord)
2075
- return null;
2076
- const propertyNamesToTry = [propertyName];
2077
- if (propertyName.endsWith('Ids')) {
2078
- propertyNamesToTry.push(propertyName.slice(0, -3));
2079
- }
2080
- if (propertyName.endsWith('Id')) {
2081
- propertyNamesToTry.push(propertyName.slice(0, -2));
2082
- }
2083
- for (const pName of propertyNamesToTry) {
2084
- const rows = await db
2085
- .select({ id: properties.id })
2086
- .from(properties)
2087
- .where(and(eq(properties.modelId, modelRecord.id), eq(properties.name, pName)))
2088
- .limit(1);
2089
- if (rows.length > 0 && rows[0].id != null) {
2090
- return rows[0].id;
2091
- }
2092
- }
2093
- return null;
2094
- }
2095
- /**
2096
- * A model's ItemStorage properties as stored in `properties` (storage_type 'ItemStorage'), with
2097
- * their storage path and extension. Reads the table, not ModelProperty instances, so it doesn't
2098
- * depend on whether (or from where) the model's property instances have loaded.
2099
- */
2100
- async function getItemStoragePropertiesForModel(modelNameOrType, scope = {}) {
2101
- const db = BaseDb.getAppDb();
2102
- if (!db || !modelNameOrType)
2103
- return [];
2104
- const modelRecord = await resolveModelRecordByNameOrType(modelNameOrType, scope, db);
2105
- if (!modelRecord)
2106
- return [];
2107
- return db
2108
- .select({
2109
- id: properties.id,
2110
- name: properties.name,
2111
- localStorageDir: properties.localStorageDir,
2112
- filenameSuffix: properties.filenameSuffix,
2113
- })
2114
- .from(properties)
2115
- .where(and(eq(properties.modelId, modelRecord.id), eq(properties.storageType, 'ItemStorage')));
2116
- }
2117
- /**
2118
- * Migrate metadata rows when a property is renamed.
2119
- * Updates metadata.propertyName from old to new for all rows that reference the renamed property.
2120
- * Called from renameModelProperty to prevent duplicate ItemProperties.
2121
- * @param modelName - Model name (PascalCase)
2122
- * @param oldPropertyName - Current property name before rename
2123
- * @param newPropertyName - New property name after rename
2124
- * @param propertySchemaFileId - Optional schema file ID for the property (most reliable lookup)
2125
- * @returns Count of metadata rows updated
2126
- */
2127
- async function migrateMetadataForPropertyRename(modelName, oldPropertyName, newPropertyName, propertySchemaFileId, scope = {}) {
2128
- const db = BaseDb.getAppDb();
2129
- if (!db) {
2130
- logger$3('[migrateMetadataForPropertyRename] Database not available, skipping');
2131
- return 0;
2132
- }
2133
- // Find the property row by schemaFileId (preferred) or by name
2134
- let propertyId = null;
2135
- if (propertySchemaFileId) {
2136
- propertyId = await getPropertyIdForSchemaFileId(modelName, propertySchemaFileId, scope);
2137
- }
2138
- if (propertyId == null) {
2139
- propertyId = await getPropertyIdForModelAndName(modelName, oldPropertyName, scope);
2140
- }
2141
- if (propertyId == null) {
2142
- logger$3(`[migrateMetadataForPropertyRename] Property not found: ${modelName}.${oldPropertyName}, skipping`);
2143
- return 0;
2144
- }
2145
- // Primary path: update metadata by propertyId
2146
- const toUpdateByPropertyId = await db
2147
- .select({ localId: metadata.localId })
2148
- .from(metadata)
2149
- .where(eq(metadata.propertyId, propertyId));
2150
- const updatedByPropertyId = toUpdateByPropertyId.length;
2151
- if (updatedByPropertyId > 0) {
2152
- await db
2153
- .update(metadata)
2154
- .set({ propertyName: newPropertyName })
2155
- .where(eq(metadata.propertyId, propertyId));
2156
- }
2157
- // Fallback: update metadata with null propertyId (legacy rows) by propertyName + modelType
2158
- const modelType = easName(modelName);
2159
- const toUpdateByFallback = await db
2160
- .select({ localId: metadata.localId })
2161
- .from(metadata)
2162
- .where(and(eq(metadata.propertyName, oldPropertyName), eq(metadata.modelType, modelType), isNull(metadata.propertyId)));
2163
- const updatedByFallback = toUpdateByFallback.length;
2164
- if (updatedByFallback > 0) {
2165
- await db
2166
- .update(metadata)
2167
- .set({ propertyName: newPropertyName })
2168
- .where(and(eq(metadata.propertyName, oldPropertyName), eq(metadata.modelType, modelType), isNull(metadata.propertyId)));
2169
- }
2170
- if (updatedByFallback > 0) {
2171
- logger$3(`[migrateMetadataForPropertyRename] Fallback updated ${updatedByFallback} rows (propertyId was null)`);
2172
- }
2173
- // Edits of the property still in the outbox (ADR 0019) apply under its new name
2174
- await db
2175
- .update(pendingWrites)
2176
- .set({ propertyName: newPropertyName })
2177
- .where(and(eq(pendingWrites.propertyName, oldPropertyName), eq(pendingWrites.status, 'pending'), inArray(pendingWrites.seedLocalId, db.select({ localId: seeds.localId }).from(seeds).where(eq(seeds.type, modelType)))));
2178
- const total = updatedByPropertyId + updatedByFallback;
2179
- if (total > 0) {
2180
- logger$3(`[migrateMetadataForPropertyRename] Migrated ${total} metadata rows for ${modelName}.${oldPropertyName} -> ${newPropertyName}`);
2181
- }
2182
- return total;
2183
- }
2184
- /**
2185
- * Moves a renamed property's metadata rows to its new name: rows linked by propertyId, and legacy
2186
- * rows without one by name and model type. Each stored form of the name moves to the same form of
2187
- * the new one: the key (`author` → `writer`), a single relation's `<key>Id` (`authorId` →
2188
- * `writerId`), a list relation's storage name (`authorIdentityIds` → `writerIdentityIds`).
2189
- * Returns the number of rows moved.
2190
- */
2191
- async function moveMetadataToRenamedProperty(db, { propertyId, modelType, oldName, newName, propertyDef, }) {
2192
- const forms = (name) => [
2193
- name,
2194
- toMetadataPropertyName(name, propertyDef?.dataType, propertyDef?.refValueType),
2195
- listRelationEasPropertyName(name, propertyDef) ?? name,
2196
- ];
2197
- const oldForms = forms(oldName);
2198
- const newForms = forms(newName);
2199
- let moved = 0;
2200
- const seen = new Set();
2201
- for (let i = 0; i < oldForms.length; i++) {
2202
- const from = oldForms[i];
2203
- const to = newForms[i];
2204
- if (from === to || seen.has(from))
2205
- continue;
2206
- seen.add(from);
2207
- const byId = await db
2208
- .update(metadata)
2209
- .set({ propertyName: to })
2210
- .where(and(eq(metadata.propertyId, propertyId), eq(metadata.propertyName, from)))
2211
- .returning({ localId: metadata.localId });
2212
- const legacy = await db
2213
- .update(metadata)
2214
- .set({ propertyName: to })
2215
- .where(and(isNull(metadata.propertyId), eq(metadata.propertyName, from), eq(metadata.modelType, modelType)))
2216
- .returning({ localId: metadata.localId });
2217
- moved += byId.length + legacy.length;
2218
- }
2219
- if (moved > 0)
2220
- logger$3(`[moveMetadataToRenamedProperty] moved ${moved} metadata rows ${oldName} -> ${newName}`);
2221
- return moved;
2222
- }
2223
- /**
2224
- * True when the models row is linked (model_schemas) to any schema other than schemaId.
2225
- */
2226
- const isModelLinkedToOtherSchema = async (db, modelId, schemaId) => {
2227
- const links = await db
2228
- .select({ schemaId: modelSchemas.schemaId })
2229
- .from(modelSchemas)
2230
- .where(eq(modelSchemas.modelId, modelId));
2231
- return links.some((l) => l.schemaId !== schemaId);
2232
- };
2233
- const isModelLinkedToAnySchema = async (db, modelId) => {
2234
- const links = await db
2235
- .select({ id: modelSchemas.id })
2236
- .from(modelSchemas)
2237
- .where(eq(modelSchemas.modelId, modelId))
2238
- .limit(1);
2239
- return links.length > 0;
2240
- };
2241
- /**
2242
- * Find or create a models row by schemaFileId (preferred) or name.
2243
- * Reuses an existing same-name row (especially schemaFileId=null stubs created via
2244
- * ref resolution) instead of inserting a second row — models.name is not unique,
2245
- * and duplicate names break name lookups (e.g. Resource.tags → Tag).
2246
- *
2247
- * When modelFileId is given, a same-name row that already carries a *different*
2248
- * non-null schemaFileId is only adopted if no other schema is linked to it via
2249
- * model_schemas. Otherwise it belongs to another schema (e.g. two schemas that each
2250
- * define "Post"), and re-id'ing it would strand that schema's cached Model instance,
2251
- * so a new row is inserted instead.
2252
- */
2253
- const findOrCreateModelRecord = async (db, modelName, modelFileId, schemaId) => {
2254
- if (modelFileId) {
2255
- const byFileId = await db
2256
- .select()
2257
- .from(models)
2258
- .where(eq(models.schemaFileId, modelFileId))
2259
- .limit(1);
2260
- if (byFileId.length > 0) {
2261
- const record = byFileId[0];
2262
- if (record.name !== modelName) {
2263
- await db
2264
- .update(models)
2265
- .set({ name: modelName })
2266
- .where(eq(models.id, record.id));
2267
- return { ...record, name: modelName };
2268
- }
2269
- return record;
2270
- }
2271
- }
2272
- const byName = (await db
2273
- .select()
2274
- .from(models)
2275
- .where(eq(models.name, modelName)));
2276
- if (byName.length > 0) {
2277
- // Prefer exact file-id match, then (for refs without a file id) this schema's row, then an
2278
- // adoptable null schemaFileId stub, then any single row.
2279
- let chosen = modelFileId
2280
- ? byName.find((r) => r.schemaFileId === modelFileId)
2281
- : undefined;
2282
- if (!chosen && !modelFileId && schemaId && byName.length > 1) {
2283
- for (const candidate of byName) {
2284
- if (!(await isModelLinkedToOtherSchema(db, candidate.id, schemaId))) {
2285
- const links = await db
2286
- .select({ schemaId: modelSchemas.schemaId })
2287
- .from(modelSchemas)
2288
- .where(eq(modelSchemas.modelId, candidate.id));
2289
- if (links.length > 0) {
2290
- chosen = candidate;
2291
- break;
2292
- }
2293
- }
2294
- }
2295
- }
2296
- chosen ?? (chosen = byName.find((r) => !r.schemaFileId));
2297
- if (!chosen && modelFileId) {
2298
- // Every same-name row has a different file id. Only adopt one that no other
2299
- // schema claims; otherwise fall through and insert a row for this file id.
2300
- // A row with a file id that no schema links is a deleted schema's leftover (its old
2301
- // properties are still attached), not an unclaimed row: don't adopt it either.
2302
- for (const candidate of byName) {
2303
- if (candidate.schemaFileId && !(await isModelLinkedToAnySchema(db, candidate.id)))
2304
- continue;
2305
- if (!(await isModelLinkedToOtherSchema(db, candidate.id, schemaId))) {
2306
- chosen = candidate;
2307
- break;
2308
- }
2309
- }
2310
- }
2311
- else if (!chosen) {
2312
- chosen =
2313
- (byName.length === 1 ? byName[0] : undefined) ||
2314
- byName.find((r) => !!r.schemaFileId) ||
2315
- byName[0];
2316
- }
2317
- if (chosen && modelFileId && chosen.schemaFileId !== modelFileId) {
2318
- const conflict = await db
2319
- .select()
2320
- .from(models)
2321
- .where(eq(models.schemaFileId, modelFileId))
2322
- .limit(1);
2323
- if (conflict.length > 0) {
2324
- return conflict[0];
2325
- }
2326
- // Adopt stub (null schemaFileId) or an unclaimed same-name row by attaching this file id.
2327
- await db
2328
- .update(models)
2329
- .set({ schemaFileId: modelFileId, isEdited: false })
2330
- .where(eq(models.id, chosen.id));
2331
- chosen = { ...chosen, schemaFileId: modelFileId, isEdited: false };
2332
- logger$3(`Adopted existing model "${modelName}" (id: ${chosen.id}) with schemaFileId "${modelFileId}"`);
2333
- }
2334
- if (chosen) {
2335
- return chosen;
2336
- }
2337
- }
2338
- try {
2339
- // A Model instance's own write can insert this file id between the lookups above and here
2340
- // (schema import writes each model twice at once). DO NOTHING instead of a failed statement,
2341
- // which sqlite-wasm logs as a warning even though the error is caught.
2342
- const inserted = await db
2343
- .insert(models)
2344
- .values({
2345
- name: modelName,
2346
- schemaFileId: modelFileId || null,
2347
- isEdited: false,
2348
- })
2349
- .onConflictDoNothing()
2350
- .returning();
2351
- if (inserted.length > 0) {
2352
- return inserted[0];
2353
- }
2354
- if (modelFileId) {
2355
- const existing = await db
2356
- .select()
2357
- .from(models)
2358
- .where(eq(models.schemaFileId, modelFileId))
2359
- .limit(1);
2360
- if (existing.length > 0) {
2361
- return existing[0];
2362
- }
2363
- }
2364
- throw new Error(`Model "${modelName}" was not inserted and no row has schemaFileId "${modelFileId}"`);
2365
- }
2366
- catch (error) {
2367
- if (isSqliteUniqueConstraintError(error) && modelFileId) {
2368
- const existing = await db
2369
- .select()
2370
- .from(models)
2371
- .where(eq(models.schemaFileId, modelFileId))
2372
- .limit(1);
2373
- if (existing.length > 0) {
2374
- return existing[0];
2375
- }
2376
- }
2377
- // Race: another writer inserted by name — reuse rather than throw on duplicates.
2378
- const raced = (await db
2379
- .select()
2380
- .from(models)
2381
- .where(eq(models.name, modelName)));
2382
- if (raced.length > 0) {
2383
- return ((modelFileId
2384
- ? raced.find((r) => r.schemaFileId === modelFileId)
2385
- : undefined) ||
2386
- raced.find((r) => !r.schemaFileId) ||
2387
- raced[0]);
2388
- }
2389
- throw error;
2390
- }
2391
- };
2392
- /**
2393
- * Find or create the models row a Relation property on ownerModelId points to. Model names are only
2394
- * unique per schema, so the ref is looked up in the owner's schema(s) first; a global name lookup
2395
- * by name threw "Multiple records found" once two schemas defined the ref model.
2396
- */
2397
- const findOrCreateRefModelRecord = async (db, refModelName, ownerModelId) => {
2398
- try {
2399
- const ref = await resolveRefModelRecord(refModelName, { modelId: ownerModelId }, db);
2400
- if (ref)
2401
- return ref;
2402
- }
2403
- catch (error) {
2404
- // Not in the owner's schema and defined by several others: pick/create like an import does
2405
- if (!(error instanceof AmbiguousModelError))
2406
- throw error;
2407
- }
2408
- const [ownerLink] = await db
2409
- .select({ schemaId: modelSchemas.schemaId })
2410
- .from(modelSchemas)
2411
- .where(eq(modelSchemas.modelId, ownerModelId))
2412
- .limit(1);
2413
- return findOrCreateModelRecord(db, refModelName, undefined, ownerLink?.schemaId ?? undefined);
2414
- };
2415
- /**
2416
- * Searches the database for an existing schema by schemaFileId (preferred) or name and creates it if it doesn't exist
2417
- * @param schema - The schema to add to the database
2418
- * @param schemaFileId - Optional schemaFileId from JSON file for change tracking
2419
- * @param schemaData - Optional full schema content as JSON string (SchemaFileFormat)
2420
- * @param isDraft - Optional flag indicating if schema is in draft state (default: false)
2421
- * @param isEdited - Optional flag indicating if schema has been edited locally (default: false)
2422
- * @returns The schema record (either existing or newly created)
2423
- */
2424
- const addSchemaToDb = async (schema, schemaFileId, schemaData, isDraft, isEdited) => {
2425
- const db = BaseDb.getAppDb();
2426
- if (!db) {
2427
- throw new Error('Database not found');
2428
- }
2429
- // The stored schema file may change its nameMatch levels (ADR 0009).
2430
- clearNameMatchLevels();
2431
- // First, try to find existing schema by schemaFileId (preferred for change tracking)
2432
- if (schemaFileId) {
2433
- const existingByFileId = await db
2434
- .select()
2435
- .from(schemas)
2436
- .where(eq(schemas.schemaFileId, schemaFileId))
2437
- .limit(1);
2438
- if (existingByFileId.length > 0) {
2439
- // Update fields if provided
2440
- const updates = {};
2441
- if (schemaFileId && !existingByFileId[0].schemaFileId) {
2442
- updates.schemaFileId = schemaFileId;
2443
- }
2444
- // Always update name if it's different (handles name changes)
2445
- if (schema.name && existingByFileId[0].name !== schema.name) {
2446
- updates.name = schema.name;
2447
- }
2448
- if (schemaData !== undefined) {
2449
- updates.schemaData = schemaData;
2450
- }
2451
- if (schema.version !== undefined && existingByFileId[0].version !== schema.version) {
2452
- updates.version = schema.version;
2453
- }
2454
- if (isDraft !== undefined && existingByFileId[0].isDraft !== isDraft) {
2455
- updates.isDraft = isDraft;
2456
- }
2457
- if (isEdited !== undefined && existingByFileId[0].isEdited !== isEdited) {
2458
- updates.isEdited = isEdited;
2459
- }
2460
- if (schema.updatedAt && existingByFileId[0].updatedAt !== schema.updatedAt) {
2461
- updates.updatedAt = schema.updatedAt;
2462
- }
2463
- if (Object.keys(updates).length > 0) {
2464
- await db
2465
- .update(schemas)
2466
- .set(updates)
2467
- .where(eq(schemas.id, existingByFileId[0].id));
2468
- return { ...existingByFileId[0], ...updates };
2469
- }
2470
- return existingByFileId[0];
2471
- }
2472
- }
2473
- // For drafts, check if there's an existing draft by name (not by schemaFileId)
2474
- if (isDraft === true) {
2475
- const existingDrafts = await db
2476
- .select()
2477
- .from(schemas)
2478
- .where(eq(schemas.name, schema.name))
2479
- .limit(1);
2480
- if (existingDrafts.length > 0 && existingDrafts[0].isDraft === true) {
2481
- // Update existing draft
2482
- const updates = {};
2483
- // Update schemaFileId if provided and different from existing
2484
- if (schemaFileId && existingDrafts[0].schemaFileId !== schemaFileId) {
2485
- updates.schemaFileId = schemaFileId;
2486
- }
2487
- if (schemaData !== undefined) {
2488
- updates.schemaData = schemaData;
2489
- }
2490
- if (schema.version !== undefined) {
2491
- updates.version = schema.version;
2492
- }
2493
- updates.updatedAt = schema.updatedAt || Date.now();
2494
- if (Object.keys(updates).length > 0) {
2495
- await db
2496
- .update(schemas)
2497
- .set(updates)
2498
- .where(eq(schemas.id, existingDrafts[0].id));
2499
- return { ...existingDrafts[0], ...updates };
2500
- }
2501
- return existingDrafts[0];
2502
- }
2503
- }
2504
- // Fallback: Search for existing schema by name
2505
- // BUT: If we have a schemaFileId, also check if any record with that schemaFileId exists
2506
- // (in case the name changed but we're looking up by the new name)
2507
- if (schemaFileId) {
2508
- // Double-check by schemaFileId - maybe the name changed and we're looking up by new name
2509
- const doubleCheckByFileId = await db
2510
- .select()
2511
- .from(schemas)
2512
- .where(eq(schemas.schemaFileId, schemaFileId))
2513
- .limit(1);
2514
- if (doubleCheckByFileId.length > 0) {
2515
- // Found by schemaFileId - update it (name might have changed)
2516
- const updates = {};
2517
- // Always update name if it's different (handles name changes)
2518
- if (schema.name && doubleCheckByFileId[0].name !== schema.name) {
2519
- updates.name = schema.name;
2520
- }
2521
- if (schemaData !== undefined) {
2522
- updates.schemaData = schemaData;
2523
- }
2524
- if (schema.version !== undefined && doubleCheckByFileId[0].version !== schema.version) {
2525
- updates.version = schema.version;
2526
- }
2527
- if (isDraft !== undefined && doubleCheckByFileId[0].isDraft !== isDraft) {
2528
- updates.isDraft = isDraft;
2529
- }
2530
- if (isEdited !== undefined && doubleCheckByFileId[0].isEdited !== isEdited) {
2531
- updates.isEdited = isEdited;
2532
- }
2533
- if (schema.updatedAt && doubleCheckByFileId[0].updatedAt !== schema.updatedAt) {
2534
- updates.updatedAt = schema.updatedAt;
2535
- }
2536
- if (Object.keys(updates).length > 0) {
2537
- await db
2538
- .update(schemas)
2539
- .set(updates)
2540
- .where(eq(schemas.id, doubleCheckByFileId[0].id));
2541
- return { ...doubleCheckByFileId[0], ...updates };
2542
- }
2543
- return doubleCheckByFileId[0];
2544
- }
2545
- }
2546
- // Final fallback: Search by name (only if no schemaFileId or schemaFileId lookup failed)
2547
- // If isDraft is explicitly false, first try to find non-draft, but also check for drafts
2548
- // to handle the case where a draft exists and we're loading a non-draft version
2549
- let existingSchemas = await db
2550
- .select()
2551
- .from(schemas)
2552
- .where(isDraft === false
2553
- ? and(eq(schemas.name, schema.name), eq(schemas.isDraft, false))
2554
- : eq(schemas.name, schema.name))
2555
- .limit(1);
2556
- // If isDraft is false and we didn't find a non-draft, check for a draft with the same name
2557
- // This handles the case where a draft was created first and we're now loading the non-draft version
2558
- if (isDraft === false && existingSchemas.length === 0) {
2559
- const draftSchemas = await db
2560
- .select()
2561
- .from(schemas)
2562
- .where(and(eq(schemas.name, schema.name), eq(schemas.isDraft, true)))
2563
- .limit(1);
2564
- if (draftSchemas.length > 0) {
2565
- // Found a draft - we'll update it to non-draft below
2566
- logger$3(`Found draft schema "${schema.name}" with schemaFileId "${draftSchemas[0].schemaFileId}", will update to non-draft with schemaFileId "${schemaFileId}"`);
2567
- existingSchemas = draftSchemas;
2568
- }
2569
- }
2570
- if (existingSchemas.length > 0) {
2571
- // The row is per schema name and holds its latest version. A complete schema file can bring
2572
- // a version under its own id (SDK version bumps keep the id; imported files may not).
2573
- const isOtherVersionFile = !!schemaFileId &&
2574
- !!existingSchemas[0].schemaFileId &&
2575
- existingSchemas[0].schemaFileId !== schemaFileId &&
2576
- existingSchemas[0].isDraft !== true &&
2577
- schema.version !== undefined &&
2578
- schema.version !== existingSchemas[0].version;
2579
- if (isOtherVersionFile && schema.version < existingSchemas[0].version) {
2580
- // An older version's file: rolling the row back to it would leave the newer version's id
2581
- // pointing at the older version's data.
2582
- logger$3(`Keeping schema "${schema.name}" at v${existingSchemas[0].version}; not replacing it with older v${schema.version}`);
2583
- return existingSchemas[0];
2584
- }
2585
- // Update fields if provided
2586
- const updates = {};
2587
- // Update schemaFileId if provided and different from existing (or if existing doesn't have one or is null)
2588
- // This is important for preserving IDs from schema files when drafts exist
2589
- // BUT: Don't overwrite a non-draft schema's schemaFileId with a new generated one
2590
- // This prevents loadOrCreateSchema from overwriting correct IDs from fixture files
2591
- if (schemaFileId && existingSchemas[0].schemaFileId !== schemaFileId) {
2592
- // Only update if:
2593
- // 1. Existing schema is a draft (can be updated)
2594
- // 2. Existing schema has no schemaFileId (should be set)
2595
- // 3. We're explicitly setting isDraft to false (updating from draft to non-draft)
2596
- // 4. It's a newer version's file: the row takes that version's data, so it takes its id too
2597
- const shouldUpdate = existingSchemas[0].isDraft === true ||
2598
- !existingSchemas[0].schemaFileId ||
2599
- (isDraft === false && existingSchemas[0].isDraft === true) ||
2600
- isOtherVersionFile;
2601
- if (shouldUpdate) {
2602
- logger$3(`Updating schemaFileId from "${existingSchemas[0].schemaFileId}" to "${schemaFileId}" for schema "${schema.name}"`);
2603
- updates.schemaFileId = schemaFileId;
2604
- }
2605
- else {
2606
- logger$3(`Preserving existing schemaFileId "${existingSchemas[0].schemaFileId}" for non-draft schema "${schema.name}" (new schemaFileId "${schemaFileId}" would overwrite it)`);
2607
- }
2608
- }
2609
- // Always update name if it's different (handles name changes)
2610
- if (schema.name && existingSchemas[0].name !== schema.name) {
2611
- updates.name = schema.name;
2612
- }
2613
- if (schemaData !== undefined) {
2614
- updates.schemaData = schemaData;
2615
- }
2616
- if (schema.version !== undefined && existingSchemas[0].version !== schema.version) {
2617
- updates.version = schema.version;
2618
- }
2619
- // If isDraft is explicitly false and we found a draft, update it to non-draft
2620
- if (isDraft === false && existingSchemas[0].isDraft === true) {
2621
- updates.isDraft = false;
2622
- }
2623
- else if (isDraft !== undefined && existingSchemas[0].isDraft !== isDraft) {
2624
- updates.isDraft = isDraft;
2625
- }
2626
- if (isEdited !== undefined && existingSchemas[0].isEdited !== isEdited) {
2627
- updates.isEdited = isEdited;
2628
- }
2629
- if (schema.updatedAt && existingSchemas[0].updatedAt !== schema.updatedAt) {
2630
- updates.updatedAt = schema.updatedAt;
2631
- }
2632
- if (Object.keys(updates).length > 0) {
2633
- logger$3(`Applying updates to schema "${schema.name}" (id: ${existingSchemas[0].id}):`, Object.keys(updates));
2634
- await db
2635
- .update(schemas)
2636
- .set(updates)
2637
- .where(eq(schemas.id, existingSchemas[0].id));
2638
- const updated = { ...existingSchemas[0], ...updates };
2639
- logger$3(`Schema "${schema.name}" updated. New schemaFileId: "${updated.schemaFileId}", isDraft: ${updated.isDraft}`);
2640
- return updated;
2641
- }
2642
- logger$3(`No updates needed for schema "${schema.name}" (id: ${existingSchemas[0].id})`);
2643
- return existingSchemas[0];
2644
- }
2645
- const newSchema = await db.insert(schemas).values({
2646
- name: schema.name,
2647
- version: schema.version,
2648
- schemaFileId: schemaFileId || null,
2649
- schemaData: schemaData || null,
2650
- isDraft: isDraft ?? false,
2651
- isEdited: isEdited ?? false,
2652
- createdAt: schema.createdAt,
2653
- updatedAt: schema.updatedAt,
2654
- }).returning();
2655
- // Notify React useSchemas so it can invalidate; live query often doesn't re-run when schemas table is inserted.
2656
- if (typeof BroadcastChannel !== 'undefined') {
2657
- try {
2658
- await new Promise((r) => setTimeout(r, 10));
2659
- postBroadcast('seed-schemas-invalidate', {});
2660
- }
2661
- catch (_) { }
2662
- }
2663
- return newSchema[0];
2664
- };
2665
- /**
2666
- * Rename a model in the database
2667
- * Updates the model name and all properties that reference it
2668
- * @param oldName - The current model name
2669
- * @param newName - The new model name
2670
- * @param schemaNameOrId - Schema name or schema ID to scope the rename (required when multiple schemas have models with the same name)
2671
- * @returns The updated model record
2672
- */
2673
- const renameModelInDb = async (oldName, newName, schemaNameOrId) => {
2674
- const db = BaseDb.getAppDb();
2675
- if (!db) {
2676
- throw new Error('Database not found');
2677
- }
2678
- const { schemas: schemasTable } = await Promise.resolve().then(function () { return SchemaSchema; });
2679
- // Find the model by old name, optionally scoped by schema
2680
- let existingModels;
2681
- if (schemaNameOrId !== undefined) {
2682
- if (typeof schemaNameOrId === 'number') {
2683
- const rows = await db
2684
- .select({
2685
- id: models.id,
2686
- name: models.name,
2687
- schemaFileId: models.schemaFileId,
2688
- isEdited: models.isEdited,
2689
- })
2690
- .from(models)
2691
- .innerJoin(modelSchemas, eq(models.id, modelSchemas.modelId))
2692
- .where(and(eq(models.name, oldName), eq(modelSchemas.schemaId, schemaNameOrId)))
2693
- .limit(1);
2694
- existingModels = rows;
2695
- }
2696
- else {
2697
- const rows = await db
2698
- .select({
2699
- id: models.id,
2700
- name: models.name,
2701
- schemaFileId: models.schemaFileId,
2702
- isEdited: models.isEdited,
2703
- })
2704
- .from(models)
2705
- .innerJoin(modelSchemas, eq(models.id, modelSchemas.modelId))
2706
- .innerJoin(schemasTable, eq(modelSchemas.schemaId, schemasTable.id))
2707
- .where(and(eq(models.name, oldName), eq(schemasTable.name, schemaNameOrId)))
2708
- .limit(1);
2709
- existingModels = rows;
2710
- }
2711
- }
2712
- else {
2713
- existingModels = await db
2714
- .select({
2715
- id: models.id,
2716
- name: models.name,
2717
- schemaFileId: models.schemaFileId,
2718
- isEdited: models.isEdited,
2719
- })
2720
- .from(models)
2721
- .where(eq(models.name, oldName));
2722
- // Model names are only unique per schema: an unscoped rename must not pick one arbitrarily.
2723
- if (existingModels.length > 1) {
2724
- throw new Error(`Model "${oldName}" exists in more than one schema; pass the schema name or id to rename it`);
2725
- }
2726
- }
2727
- if (existingModels.length === 0) {
2728
- if (schemaNameOrId !== undefined) {
2729
- throw new Error(`Model "${oldName}" not found in schema "${schemaNameOrId}"`);
2730
- }
2731
- // Backward compat: model doesn't exist, create it with new name
2732
- const newModel = await db.insert(models).values({ name: newName }).returning();
2733
- return newModel[0];
2734
- }
2735
- const modelToRename = existingModels[0];
2736
- // Check if a model with the new name already exists in the same schema
2737
- let existingWithNewName;
2738
- if (schemaNameOrId !== undefined) {
2739
- if (typeof schemaNameOrId === 'number') {
2740
- existingWithNewName = await db
2741
- .select({
2742
- id: models.id,
2743
- name: models.name,
2744
- schemaFileId: models.schemaFileId,
2745
- isEdited: models.isEdited,
2746
- })
2747
- .from(models)
2748
- .innerJoin(modelSchemas, eq(models.id, modelSchemas.modelId))
2749
- .where(and(eq(models.name, newName), eq(modelSchemas.schemaId, schemaNameOrId)))
2750
- .limit(1);
2751
- }
2752
- else {
2753
- existingWithNewName = await db
2754
- .select({
2755
- id: models.id,
2756
- name: models.name,
2757
- schemaFileId: models.schemaFileId,
2758
- isEdited: models.isEdited,
2759
- })
2760
- .from(models)
2761
- .innerJoin(modelSchemas, eq(models.id, modelSchemas.modelId))
2762
- .innerJoin(schemasTable, eq(modelSchemas.schemaId, schemasTable.id))
2763
- .where(and(eq(models.name, newName), eq(schemasTable.name, schemaNameOrId)))
2764
- .limit(1);
2765
- }
2766
- }
2767
- else {
2768
- existingWithNewName = await db
2769
- .select({
2770
- id: models.id,
2771
- name: models.name,
2772
- schemaFileId: models.schemaFileId,
2773
- isEdited: models.isEdited,
2774
- })
2775
- .from(models)
2776
- .where(eq(models.name, newName))
2777
- .limit(1);
2778
- }
2779
- if (existingWithNewName.length > 0) {
2780
- // New name already exists in this schema, return it
2781
- return existingWithNewName[0];
2782
- }
2783
- // Update the model name
2784
- await db
2785
- .update(models)
2786
- .set({ name: newName })
2787
- .where(eq(models.id, modelToRename.id));
2788
- return { ...modelToRename, name: newName };
2789
- };
2790
- /**
2791
- * Adds models and their properties to the database.
2792
- * Optionally connects models to a schema via join records.
2793
- * @param models - The model definitions to add
2794
- * @param schema - Optional schema to connect models to
2795
- * @param modelRenames - Optional map of old model names to new model names for handling renames
2796
- * @param schemaFileData - Optional object containing schemaFileId mappings from JSON file: { schemaFileId, modelFileIds: Map<modelName, id>, propertyFileIds: Map<modelName, Map<propertyName, id>> }
2797
- */
2798
- const addModelsToDb = async (models$1, schema, modelRenames, schemaFileData) => {
2799
- const db = BaseDb.getAppDb();
2800
- if (!db) {
2801
- throw new Error('Database not found');
2802
- }
2803
- // Model rows and their schema links decide which schema file's nameMatch applies (ADR 0009).
2804
- clearNameMatchLevels();
2805
- // If schema is provided, add it to the database
2806
- // Note: schema might already be a database record (with id) if passed from importJsonSchema
2807
- let schemaRecord;
2808
- if (schema) {
2809
- // Check if schema already has an id (it's already a database record)
2810
- // SchemaType includes id, so check if it's set
2811
- if (schema.id !== null && schema.id !== undefined) {
2812
- schemaRecord = schema;
2813
- logger$3(`Using existing schema record with id: ${schemaRecord.id}`);
2814
- }
2815
- else {
2816
- schemaRecord = await addSchemaToDb(schema, schemaFileData?.schemaFileId);
2817
- logger$3(`Created/found schema record with id: ${schemaRecord.id}`);
2818
- }
2819
- }
2820
- // Handle model renames first if provided
2821
- if (modelRenames && schemaRecord) {
2822
- const schemaNameOrId = schemaRecord.name ?? schemaRecord.id;
2823
- for (const [oldName, newName] of modelRenames.entries()) {
2824
- await renameModelInDb(oldName, newName, schemaNameOrId);
2825
- }
2826
- }
2827
- // Search for all models by schemaFileId (preferred) or name and create them if they don't exist
2828
- const modelRecords = new Map();
2829
- const modelProcessingErrors = [];
2830
- // Property rows this call changed, so cached ModelProperty instances can be given the new values
2831
- const updatedPropertyFileIds = [];
2832
- for (const [modelName, modelClass] of Object.entries(models$1)) {
2833
- try {
2834
- const modelFileId = schemaFileData?.modelFileIds?.get(modelName);
2835
- let modelRecord = await findOrCreateModelRecord(db, modelName, modelFileId, schemaRecord?.id);
2836
- // Keep schemaFileId aligned when findOrCreate returned a row that still needs adoption
2837
- // (rare: findOrCreate handles stubs and unclaimed same-name rows itself).
2838
- if (modelFileId && modelRecord.schemaFileId !== modelFileId) {
2839
- const existingWithFileId = await db
2840
- .select()
2841
- .from(models)
2842
- .where(eq(models.schemaFileId, modelFileId))
2843
- .limit(1);
2844
- if (existingWithFileId.length > 0 && existingWithFileId[0].id !== modelRecord.id) {
2845
- logger$3(`WARNING: Model "${modelName}" (id: ${modelRecord.id}) conflicts with existing model "${existingWithFileId[0].name}" (id: ${existingWithFileId[0].id}) both trying to use schemaFileId "${modelFileId}"`);
2846
- modelRecord = existingWithFileId[0];
2847
- }
2848
- else if (!modelRecord.schemaFileId) {
2849
- await db
2850
- .update(models)
2851
- .set({ schemaFileId: modelFileId })
2852
- .where(eq(models.id, modelRecord.id));
2853
- modelRecord = { ...modelRecord, schemaFileId: modelFileId };
2854
- }
2855
- }
2856
- modelRecords.set(modelName, modelRecord);
2857
- // Get all existing properties for this model upfront to handle renamed properties
2858
- const allDbProperties = await db
2859
- .select()
2860
- .from(properties)
2861
- .where(eq(properties.modelId, modelRecord.id));
2862
- // Track which DB properties have been matched to schema properties
2863
- const matchedDbPropertyIds = new Set();
2864
- // Get properties from Model context (where they're stored as an object when Model is created)
2865
- // modelClass.properties returns ModelProperty instances from DB, but we need the context properties
2866
- // which are stored as an object when Model.create() is called with properties
2867
- const modelContext = modelClass._getSnapshotContext?.() || {};
2868
- const contextProperties = modelContext.properties || {};
2869
- // When models are created from JSON, properties are stored as _pendingPropertyDefinitions
2870
- const pendingProperties = modelContext._pendingPropertyDefinitions || {};
2871
- // If context properties are empty, try modelClass.properties (for backward compatibility)
2872
- // but convert ModelProperty instances to object format
2873
- // Property definitions read from the schema file win: a cached Model keeps the properties it
2874
- // was created with, so after a rename its context still has the old name.
2875
- const filePropertyDefinitions = schemaFileData?.propertyDefinitions?.get(modelName);
2876
- let schema = {};
2877
- if (filePropertyDefinitions) {
2878
- schema = filePropertyDefinitions;
2879
- }
2880
- else if (Object.keys(contextProperties).length > 0) {
2881
- schema = contextProperties;
2882
- }
2883
- else if (Object.keys(pendingProperties).length > 0) {
2884
- // Use pending property definitions (from JSON import)
2885
- schema = pendingProperties;
2886
- }
2887
- else {
2888
- const modelHelpersMod = await import('./model-oHA3YhOd.js');
2889
- const { modelPropertiesToObject } = modelHelpersMod;
2890
- const modelProperties = modelClass.properties || [];
2891
- schema = modelPropertiesToObject(modelProperties);
2892
- }
2893
- // Search for all properties and create them if they don't exist
2894
- // Properties are unique by name + modelId, so we search using both
2895
- const schemaEntries = Object.entries(schema);
2896
- for (let index = 0; index < schemaEntries.length; index++) {
2897
- const [propertyName, propertyValues] = schemaEntries[index];
2898
- if (!propertyValues) {
2899
- throw new Error(`Property values not found for ${propertyName}`);
2900
- }
2901
- // Get property schemaFileId if available
2902
- let propertyFileId = schemaFileData?.propertyFileIds?.get(modelName)?.get(propertyName);
2903
- const propertyFileIdGenerated = !propertyFileId;
2904
- // If no propertyFileId from map, generate a random ID
2905
- // IDs should be generated in the import process before calling addModelsToDb
2906
- if (!propertyFileId) {
2907
- const { generateId } = await Promise.resolve().then(function () { return generateId$1; });
2908
- propertyFileId = generateId();
2909
- logger$3(`Generated propertyFileId "${propertyFileId}" for property "${modelName}:${propertyName}" (not found in propertyFileIds map)`);
2910
- }
2911
- // First, try to find by schemaFileId (preferred for change tracking)
2912
- let existingProperty;
2913
- if (propertyFileId) {
2914
- existingProperty = allDbProperties.find((p) => p.schemaFileId === propertyFileId && !matchedDbPropertyIds.has(p.id));
2915
- }
2916
- // Fallback: check if property exists by name + modelId
2917
- if (!existingProperty) {
2918
- existingProperty = allDbProperties.find((p) => p.name === propertyName && !matchedDbPropertyIds.has(p.id));
2919
- }
2920
- // Prepare property values
2921
- const propertyData = {
2922
- name: propertyName,
2923
- modelId: modelRecord.id,
2924
- dataType: normalizeDataType(propertyValues.dataType),
2925
- schemaFileId: propertyFileId || null,
2926
- required: propertyValues.required ?? false,
2927
- // The schema file is the source of truth here: no storage config means none.
2928
- ...propertyStorageColumns(propertyValues),
2929
- };
2930
- // Handle ref property - create ref model if needed
2931
- // Check both ref and refModelName (refModelName is set by createModelFromJson)
2932
- const refModelName = propertyValues.ref || propertyValues.refModelName;
2933
- if (refModelName) {
2934
- // When ref model was already processed in this batch, use it (avoids db round-trip)
2935
- const cachedRef = modelRecords.get(refModelName);
2936
- const refModelFileId = schemaFileData?.modelFileIds?.get(refModelName);
2937
- const refModel = cachedRef
2938
- ? cachedRef
2939
- : await findOrCreateModelRecord(db, refModelName, refModelFileId, schemaRecord?.id);
2940
- if (!cachedRef) {
2941
- modelRecords.set(refModelName, refModel);
2942
- }
2943
- propertyData.refModelId = refModel.id ?? null;
2944
- }
2945
- else {
2946
- // If it's not a Relation type, ensure refModelId is null
2947
- propertyData.refModelId = null;
2948
- }
2949
- if (propertyValues.refValueType) {
2950
- propertyData.refValueType = normalizeDataType(propertyValues.refValueType);
2951
- }
2952
- else {
2953
- // If refValueType is not set, ensure it's null
2954
- propertyData.refValueType = null;
2955
- }
2956
- // ADR 0008: a rename needs the property's id. A name that matches no stored property by id
2957
- // or name is a new property, even if one of the same type was removed in the same edit.
2958
- if (existingProperty) {
2959
- // Mark this DB property as matched
2960
- matchedDbPropertyIds.add(existingProperty.id);
2961
- // A property edited locally keeps its row.
2962
- const isPropertyEdited = existingProperty.isEdited === true;
2963
- if (isPropertyEdited) {
2964
- // Property has been locally edited - skip update to preserve local changes
2965
- logger$3(`Skipping update for property ${modelName}:${existingProperty.name} -> ${propertyName} - it has been locally edited and will be preserved`);
2966
- continue;
2967
- }
2968
- // Property exists (possibly with different name) - update it with new values from schema file
2969
- // This handles both regular updates and renames
2970
- // Ensure schemaFileId is updated if we have it
2971
- // Preserve existing isEdited value if it was true (local edits take precedence)
2972
- const updateData = { ...propertyData };
2973
- // A generated id must not replace the one the row already has from its schema file
2974
- if (propertyFileIdGenerated && existingProperty.schemaFileId) {
2975
- updateData.schemaFileId = existingProperty.schemaFileId;
2976
- }
2977
- if (existingProperty.isEdited === true) {
2978
- // Preserve isEdited = true if property was locally edited
2979
- delete updateData.isEdited;
2980
- }
2981
- else {
2982
- updateData.isEdited = false;
2983
- }
2984
- // Skip db.update when data unchanged (avoids slow OPFS writes in browser)
2985
- const unchanged = existingProperty.name === updateData.name &&
2986
- existingProperty.dataType === updateData.dataType &&
2987
- existingProperty.schemaFileId === (updateData.schemaFileId ?? null) &&
2988
- existingProperty.required === (updateData.required ?? false) &&
2989
- existingProperty.refModelId === (updateData.refModelId ?? null) &&
2990
- existingProperty.refValueType === (updateData.refValueType ?? null) &&
2991
- (existingProperty.storageType ?? null) === (updateData.storageType ?? null) &&
2992
- (existingProperty.localStorageDir ?? null) === (updateData.localStorageDir ?? null) &&
2993
- (existingProperty.filenameSuffix ?? null) === (updateData.filenameSuffix ?? null) &&
2994
- existingProperty.isEdited === (updateData.isEdited ?? false);
2995
- if (!unchanged) {
2996
- await db
2997
- .update(properties)
2998
- .set(updateData)
2999
- .where(eq(properties.id, existingProperty.id));
3000
- const updatedFileId = updateData.schemaFileId ?? existingProperty.schemaFileId;
3001
- if (updatedFileId)
3002
- updatedPropertyFileIds.push(updatedFileId);
3003
- }
3004
- if (existingProperty.name !== propertyName) {
3005
- // Renamed (matched by id): its values move to the new name.
3006
- await moveMetadataToRenamedProperty(db, {
3007
- propertyId: existingProperty.id,
3008
- modelType: easName(modelName),
3009
- oldName: existingProperty.name,
3010
- newName: propertyName,
3011
- propertyDef: propertyValues,
3012
- });
3013
- }
3014
- }
3015
- else {
3016
- // Property doesn't exist, create it with schemaFileId
3017
- // Set isEdited = false when loading from schema file
3018
- const propertyDataWithIsEdited = {
3019
- ...propertyData,
3020
- isEdited: false,
3021
- };
3022
- // Global schemaFileId lookup (unique across all models) before insert — avoids
3023
- // UNIQUE races when the property already exists on another model row or from a prior pass.
3024
- if (propertyFileId) {
3025
- const existingByFileId = await db
3026
- .select()
3027
- .from(properties)
3028
- .where(eq(properties.schemaFileId, propertyFileId))
3029
- .limit(1);
3030
- if (existingByFileId.length > 0) {
3031
- const found = existingByFileId[0];
3032
- matchedDbPropertyIds.add(found.id);
3033
- logger$3(`Property ${modelName}:${propertyName} already exists by schemaFileId "${propertyFileId}" (id: ${found.id}), skipping insert`);
3034
- continue;
3035
- }
3036
- }
3037
- logger$3(`Creating new property ${modelName}:${propertyName} with schemaFileId: ${propertyData.schemaFileId}`);
3038
- // A conflict means the row already exists: the schema import's Model instances write their
3039
- // properties at the same time as this. DO NOTHING keeps sqlite-wasm from logging a failed insert.
3040
- const inserted = await db
3041
- .insert(properties)
3042
- .values(propertyDataWithIsEdited)
3043
- .onConflictDoNothing()
3044
- .returning({ id: properties.id });
3045
- if (inserted.length === 0) {
3046
- logger$3(`Property ${modelName}:${propertyName} already exists (UNIQUE constraint), treating as success`);
3047
- }
3048
- }
3049
- }
3050
- }
3051
- catch (error) {
3052
- const errorMessage = error?.message || String(error);
3053
- logger$3(`Error processing model "${modelName}": ${errorMessage}`);
3054
- modelProcessingErrors.push({ modelName, error: errorMessage });
3055
- // Continue processing other models even if one fails
3056
- }
3057
- }
3058
- if (modelProcessingErrors.length > 0) {
3059
- console.warn(`[addModelsToDb] Errors occurred while processing ${modelProcessingErrors.length} model(s):`, modelProcessingErrors.map(e => `${e.modelName}: ${e.error}`).join('; '));
3060
- logger$3(`Errors processing models: ${modelProcessingErrors.map(e => e.modelName).join(', ')}`);
3061
- }
3062
- // If schema was provided, create modelSchema join records to connect models to the schema
3063
- if (schemaRecord && schemaRecord.id) {
3064
- const modelIds = [...modelRecords.entries()]
3065
- .filter(([, r]) => r.id)
3066
- .map(([name, r]) => ({ modelName: name, modelId: r.id }));
3067
- if (modelIds.length === 0) {
3068
- logger$3('No model IDs to link, skipping schema linking');
3069
- }
3070
- else {
3071
- // Single batch query: get all existing joins for this schema
3072
- const existingJoins = await db
3073
- .select({ modelId: modelSchemas.modelId })
3074
- .from(modelSchemas)
3075
- .where(eq(modelSchemas.schemaId, schemaRecord.id));
3076
- const existingSet = new Set(existingJoins.map((r) => r.modelId));
3077
- const toInsert = modelIds.filter(({ modelId }) => !existingSet.has(modelId));
3078
- if (toInsert.length > 0) {
3079
- for (const { modelName, modelId } of toInsert) {
3080
- if (!(await linkModelToSchema(db, modelId, schemaRecord.id)))
3081
- continue;
3082
- logger$3(`Created join table entry for model ${modelName} (id: ${modelId}) to schema (id: ${schemaRecord.id})`);
3083
- }
3084
- }
3085
- }
3086
- }
3087
- else if (schemaRecord && !schemaRecord.id) {
3088
- logger$3(`Warning: schemaRecord provided but has no id, cannot create join table entries`);
3089
- }
3090
- // A cached instance is otherwise never re-read from its row (createById returns it as is)
3091
- await refreshCachedProperties(updatedPropertyFileIds);
3092
- };
3093
- /**
3094
- * Loads models from the database for a given schema by querying the model_schemas join table.
3095
- * This ensures that models added to the database (via model_schemas) are included even if
3096
- * they're not in the schemaData JSON.
3097
- * @param schemaId - The ID of the schema record in the database
3098
- * @returns A map of model names to model data (compatible with SchemaFileFormat.models)
3099
- */
3100
- const loadModelsFromDbForSchema = async (schemaId) => {
3101
- const db = BaseDb.getAppDb();
3102
- if (!db) {
3103
- logger$3('Database not found, cannot load models from DB');
3104
- return {};
3105
- }
3106
- try {
3107
- // Query model_schemas join table to find all models linked to this schema
3108
- const modelSchemaRecords = await db
3109
- .select({
3110
- modelId: modelSchemas.modelId,
3111
- modelName: models.name,
3112
- schemaFileId: models.schemaFileId,
3113
- })
3114
- .from(modelSchemas)
3115
- .innerJoin(models, eq(modelSchemas.modelId, models.id))
3116
- .where(eq(modelSchemas.schemaId, schemaId));
3117
- const models$1 = {};
3118
- // For each model, load its properties
3119
- for (const { modelId, modelName, schemaFileId } of modelSchemaRecords) {
3120
- if (!modelId || !modelName)
3121
- continue;
3122
- // Get all properties for this model
3123
- const propertyRecords = await db
3124
- .select()
3125
- .from(properties)
3126
- .where(eq(properties.modelId, modelId));
3127
- // Reconstruct properties object
3128
- const modelProperties = {};
3129
- for (const prop of propertyRecords) {
3130
- // Build a basic property structure from database fields
3131
- // Note: This is a simplified reconstruction - full property schemas
3132
- // (like Relation details, List configs, etc.) should come from schemaData
3133
- const propertyData = {
3134
- dataType: normalizeDataType(prop.dataType ?? undefined),
3135
- };
3136
- if (prop.schemaFileId) {
3137
- propertyData.schemaFileId = prop.schemaFileId;
3138
- }
3139
- // Add ref information if it's a relation
3140
- if (prop.refModelId) {
3141
- // Get the referenced model name
3142
- const refModelRecords = await db
3143
- .select({ name: models.name })
3144
- .from(models)
3145
- .where(eq(models.id, prop.refModelId))
3146
- .limit(1);
3147
- if (refModelRecords.length > 0) {
3148
- propertyData.ref = refModelRecords[0].name;
3149
- }
3150
- }
3151
- if (prop.refValueType) {
3152
- propertyData.refValueType = normalizeDataType(prop.refValueType ?? undefined);
3153
- }
3154
- if (prop.storageType)
3155
- propertyData.storageType = prop.storageType;
3156
- if (prop.localStorageDir)
3157
- propertyData.localStorageDir = prop.localStorageDir;
3158
- if (prop.filenameSuffix)
3159
- propertyData.filenameSuffix = prop.filenameSuffix;
3160
- modelProperties[prop.name] = propertyData;
3161
- }
3162
- // Create model structure. Include id (schemaFileId) so processSchemaFiles can populate
3163
- // modelFileIds and Model.create can skip duplicate-name logic, preventing model
3164
- // duplication on reload (Post 1 1, Post 2 1, etc.).
3165
- models$1[modelName] = {
3166
- properties: modelProperties,
3167
- ...(schemaFileId ? { id: schemaFileId } : {}),
3168
- // Note: description would need to be stored separately
3169
- // or reconstructed from schemaData if available
3170
- };
3171
- }
3172
- logger$3(`Loaded ${Object.keys(models$1).length} models from database for schema ${schemaId}`);
3173
- return models$1;
3174
- }
3175
- catch (error) {
3176
- logger$3(`Error loading models from database for schema ${schemaId}:`, error);
3177
- return {};
3178
- }
3179
- };
3180
- /**
3181
- * Merges the models {@link loadModelsFromDbForSchema} read from the database into a schema's
3182
- * models. The database decides which models and properties exist and their stored fields; the
3183
- * schema keeps what only it holds (nameMatch, description, validation, ...), found by id first so
3184
- * a property renamed in the database keeps them. A schema property whose id a database property
3185
- * has is dropped under its old name.
3186
- */
3187
- const mergeDbModelsIntoSchemaModels = (schemaModels, dbModels) => {
3188
- const merged = { ...(schemaModels ?? {}) };
3189
- for (const [modelName, dbModel] of Object.entries(dbModels)) {
3190
- const schemaModel = schemaModels?.[modelName];
3191
- if (!schemaModel) {
3192
- merged[modelName] = dbModel;
3193
- continue;
3194
- }
3195
- const schemaProps = schemaModel.properties ?? {};
3196
- const dbProps = dbModel.properties ?? {};
3197
- const schemaNameById = new Map();
3198
- for (const [name, prop] of Object.entries(schemaProps)) {
3199
- const id = prop?.id ?? prop?.schemaFileId;
3200
- if (id)
3201
- schemaNameById.set(id, name);
3202
- }
3203
- const properties = {};
3204
- const matched = new Set();
3205
- for (const [name, dbProp] of Object.entries(dbProps)) {
3206
- const byId = dbProp?.schemaFileId ? schemaNameById.get(dbProp.schemaFileId) : undefined;
3207
- const byName = schemaProps[name];
3208
- const byNameId = byName?.id ?? byName?.schemaFileId;
3209
- // By name only when the ids don't say it's another property.
3210
- const schemaName = byId ?? (byName && !(byNameId && dbProp?.schemaFileId && byNameId !== dbProp.schemaFileId) ? name : undefined);
3211
- const schemaProp = schemaName ? schemaProps[schemaName] : undefined;
3212
- if (schemaName && schemaProp)
3213
- matched.add(schemaName);
3214
- // The database's dataType and ref replace the file's type and model.
3215
- const { type, model, ...fromSchema } = schemaProp ?? {};
3216
- properties[name] = {
3217
- ...fromSchema,
3218
- ...(dbProp?.dataType === undefined && type !== undefined ? { type } : {}),
3219
- ...(dbProp?.ref === undefined && model !== undefined ? { model } : {}),
3220
- ...dbProp,
3221
- };
3222
- }
3223
- const unmatched = Object.fromEntries(Object.entries(schemaProps).filter(([name]) => !matched.has(name)));
3224
- merged[modelName] = { ...schemaModel, ...dbModel, properties: { ...unmatched, ...properties } };
3225
- }
3226
- return merged;
3227
- };
3228
- /**
3229
- * Returns model name by database model ID.
3230
- */
3231
- async function getModelNameByModelId(modelId) {
3232
- const db = BaseDb.getAppDb();
3233
- if (!db)
3234
- return undefined;
3235
- try {
3236
- const rows = await db
3237
- .select({ name: models.name })
3238
- .from(models)
3239
- .where(eq(models.id, modelId))
3240
- .limit(1);
3241
- return rows.length > 0 ? rows[0].name : undefined;
3242
- }
3243
- catch {
3244
- return undefined;
3245
- }
3246
- }
3247
- /**
3248
- * Resolves modelName and dataType for a property by its schemaFileId (e.g. context.id).
3249
- * Used when machine context lacks these (e.g. just-created property renamed before full context is set).
3250
- */
3251
- async function getPropertyModelNameAndDataType(schemaFileId) {
3252
- const db = BaseDb.getAppDb();
3253
- if (!db || !schemaFileId)
3254
- return undefined;
3255
- try {
3256
- const rows = await db
3257
- .select({
3258
- dataType: properties.dataType,
3259
- modelId: properties.modelId,
3260
- })
3261
- .from(properties)
3262
- .where(eq(properties.schemaFileId, schemaFileId))
3263
- .limit(1);
3264
- if (rows.length === 0)
3265
- return undefined;
3266
- const modelRows = await db
3267
- .select({ name: models.name })
3268
- .from(models)
3269
- .where(eq(models.id, rows[0].modelId))
3270
- .limit(1);
3271
- if (modelRows.length === 0)
3272
- return undefined;
3273
- return {
3274
- modelName: modelRows[0].name,
3275
- dataType: rows[0].dataType ?? '',
3276
- };
3277
- }
3278
- catch {
3279
- return undefined;
3280
- }
3281
- }
3282
- /**
3283
- * Saves a property's changes to the database without updating the JSON schema file.
3284
- * This is used when properties are edited but the schema hasn't been saved as a new version yet.
3285
- * @param property - The ModelPropertyMachineContext with updated values
3286
- */
3287
- const savePropertyToDb = async (property) => {
3288
- const db = BaseDb.getAppDb();
3289
- if (!db) {
3290
- throw new Error('Database not found');
3291
- }
3292
- if (!property.name) {
3293
- throw new Error('Property name is required');
3294
- }
3295
- // Find the model: prefer modelId (reliable), then modelName as name, then modelName as schemaFileId
3296
- // (context.modelName can be a model schemaFileId when schema uses that as identifier)
3297
- let modelRecord;
3298
- if (property.modelId != null && typeof property.modelId === 'number') {
3299
- const byId = await db
3300
- .select()
3301
- .from(models)
3302
- .where(eq(models.id, property.modelId))
3303
- .limit(1);
3304
- modelRecord = byId[0];
3305
- }
3306
- if (!modelRecord && property.modelName) {
3307
- // Model names are only unique per schema: scope by the property's schema when known
3308
- modelRecord = await resolveModelRecord(property.modelName, { schemaName: property._schemaName }, db);
3309
- }
3310
- if (!modelRecord && property.modelName) {
3311
- const bySchemaFileId = await db
3312
- .select()
3313
- .from(models)
3314
- .where(eq(models.schemaFileId, property.modelName))
3315
- .limit(1);
3316
- modelRecord = bySchemaFileId[0];
3317
- }
3318
- // Fallback: resolve model from existing property row (by schemaFileId) when modelName is wrong
3319
- const schemaFileId = property._propertyFileId || (typeof property.id === 'string' ? property.id : undefined);
3320
- if (!modelRecord && schemaFileId) {
3321
- const propRows = await db
3322
- .select({ modelId: properties.modelId })
3323
- .from(properties)
3324
- .where(eq(properties.schemaFileId, schemaFileId))
3325
- .limit(1);
3326
- if (propRows.length > 0 && propRows[0].modelId) {
3327
- const byPropModelId = await db
3328
- .select()
3329
- .from(models)
3330
- .where(eq(models.id, propRows[0].modelId))
3331
- .limit(1);
3332
- modelRecord = byPropModelId[0];
3333
- }
3334
- }
3335
- if (!modelRecord) {
3336
- throw new Error(`Model not found in database (modelId=${property.modelId}, modelName=${property.modelName})`);
3337
- }
3338
- // Find existing property - try multiple strategies to handle name changes
3339
- // 1. First try by schemaFileId (most reliable - doesn't change when name changes)
3340
- let existingProperties = [];
3341
- logger$3(`[savePropertyToDb] Looking for property ${property.modelName}:${property.name} (schemaFileId: ${schemaFileId}, originalName: ${property._originalValues?.name})`);
3342
- if (schemaFileId) {
3343
- existingProperties = await db
3344
- .select()
3345
- .from(properties)
3346
- .where(and(eq(properties.schemaFileId, schemaFileId), eq(properties.modelId, modelRecord.id)))
3347
- .limit(1);
3348
- logger$3(`[savePropertyToDb] Found ${existingProperties.length} properties by schemaFileId`);
3349
- }
3350
- // 2. If not found by schemaFileId, try by original name (if name was changed)
3351
- if (existingProperties.length === 0 && property._originalValues?.name && property._originalValues.name !== property.name) {
3352
- logger$3(`[savePropertyToDb] Trying to find by original name: ${property._originalValues.name}`);
3353
- existingProperties = await db
3354
- .select()
3355
- .from(properties)
3356
- .where(and(eq(properties.name, property._originalValues.name), eq(properties.modelId, modelRecord.id)))
3357
- .limit(1);
3358
- logger$3(`[savePropertyToDb] Found ${existingProperties.length} properties by original name`);
3359
- }
3360
- // 3. Fallback: try by current name
3361
- if (existingProperties.length === 0) {
3362
- logger$3(`[savePropertyToDb] Trying to find by current name: ${property.name}`);
3363
- existingProperties = await db
3364
- .select()
3365
- .from(properties)
3366
- .where(and(eq(properties.name, property.name), eq(properties.modelId, modelRecord.id)))
3367
- .limit(1);
3368
- logger$3(`[savePropertyToDb] Found ${existingProperties.length} properties by current name`);
3369
- }
3370
- // Prepare property data
3371
- const propertyData = {
3372
- name: property.name,
3373
- modelId: modelRecord.id,
3374
- dataType: normalizeDataType(property.dataType),
3375
- };
3376
- // Preserve schemaFileId if we have it
3377
- if (schemaFileId) {
3378
- propertyData.schemaFileId = schemaFileId;
3379
- }
3380
- // Handle ref property - create ref model if needed
3381
- if (property.refModelName) {
3382
- const refModel = await findOrCreateRefModelRecord(db, property.refModelName, modelRecord.id);
3383
- propertyData.refModelId = refModel.id;
3384
- }
3385
- else if (property.refModelId) {
3386
- propertyData.refModelId = property.refModelId;
3387
- }
3388
- else {
3389
- // If it's not a Relation type, ensure refModelId is null
3390
- propertyData.refModelId = null;
3391
- }
3392
- if (property.refValueType) {
3393
- propertyData.refValueType = normalizeDataType(property.refValueType);
3394
- }
3395
- else {
3396
- // If refValueType is not set, ensure it's null
3397
- propertyData.refValueType = null;
3398
- }
3399
- propertyData.required = property.required ?? false;
3400
- // Only when the context has storage settings: a context built without them (e.g. loaded before
3401
- // they were stored) must not clear the row's.
3402
- const storage = propertyStorageColumns(property);
3403
- if (hasStorageSettings(storage)) {
3404
- Object.assign(propertyData, storage);
3405
- }
3406
- if (existingProperties.length > 0) {
3407
- // Property exists, update it with new values (including new name)
3408
- // Set isEdited = true when property is edited
3409
- const existingProperty = existingProperties[0];
3410
- await db
3411
- .update(properties)
3412
- .set({
3413
- ...propertyData,
3414
- isEdited: true, // Mark as edited when saving changes
3415
- })
3416
- .where(eq(properties.id, existingProperty.id));
3417
- logger$3(`Updated property ${property.modelName}:${property._originalValues?.name || 'unknown'} -> ${property.name} in database`);
3418
- }
3419
- else {
3420
- // Property doesn't exist, create it
3421
- // Set isEdited = true for runtime-created properties
3422
- // A conflict means the row already exists (concurrent addModelsToDb or another write): treat as success
3423
- const inserted = await db
3424
- .insert(properties)
3425
- .values({
3426
- ...propertyData,
3427
- isEdited: true, // Runtime-created properties are edited
3428
- })
3429
- .onConflictDoNothing()
3430
- .returning({ id: properties.id });
3431
- if (inserted.length > 0) {
3432
- logger$3(`Created property ${property.modelName}:${property.name} in database`);
3433
- }
3434
- else {
3435
- logger$3(`Property ${property.modelName}:${property.name} already exists (UNIQUE constraint), treating as success`);
3436
- }
3437
- }
3438
- };
3439
- function parseAddressConfig(value) {
3440
- if (!value)
3441
- return null;
3442
- try {
3443
- const parsed = JSON.parse(value);
3444
- if (Array.isArray(parsed)) {
3445
- return normalizeAddressConfig(parsed);
3446
- }
3447
- if (parsed && typeof parsed === 'object' && Array.isArray(parsed.owned)) {
3448
- return normalizeAddressConfig({
3449
- owned: parsed.owned,
3450
- watched: Array.isArray(parsed.watched) ? parsed.watched : [],
3451
- });
3452
- }
3453
- return null;
3454
- }
3455
- catch {
3456
- return null;
3457
- }
3458
- }
3459
- const getOwnedAddressesFromDb = async () => {
3460
- const config = await getAddressConfigFromDb();
3461
- return config?.owned ?? [];
3462
- };
3463
- const getWatchedAddressesFromDb = async () => {
3464
- const config = await getAddressConfigFromDb();
3465
- return config?.watched ?? [];
3466
- };
3467
- /**
3468
- * Returns persisted owned or watched addresses for list filters.
3469
- * Does not include `getAdditionalSyncAddresses` (those are extra EAS indexers).
3470
- */
3471
- const getAddressesForItemsFilter = async (addressFilter) => {
3472
- if (addressFilter === 'owned') {
3473
- return getOwnedAddressesFromDb();
3474
- }
3475
- return getWatchedAddressesFromDb();
3476
- };
3477
- /**
3478
- * Returns owned + watched addresses. Use for EAS sync and file download.
3479
- */
3480
- const getAllAddressesFromDb = async () => {
3481
- const config = await getAddressConfigFromDb();
3482
- if (!config)
3483
- return [];
3484
- return [...config.owned, ...config.watched];
3485
- };
3486
- async function getAddressConfigFromDb() {
3487
- const appDb = BaseDb.getAppDb();
3488
- if (!appDb) {
3489
- return new Promise((resolve) => {
3490
- setTimeout(async () => {
3491
- resolve(await getAddressConfigFromDb());
3492
- }, 500);
3493
- });
3494
- }
3495
- const appStatesRecords = await appDb
3496
- .select()
3497
- .from(appState)
3498
- .where(eq(appState.key, 'addresses'))
3499
- .limit(1);
3500
- if (!appStatesRecords || appStatesRecords.length === 0) {
3501
- return null;
3502
- }
3503
- const addressArrayString = appStatesRecords[0].value;
3504
- return parseAddressConfig(addressArrayString);
3505
- }
3506
- /**
3507
- * Write model to database and create model_schemas join entry
3508
- * @param modelFileId - The model file ID (schema_file_id)
3509
- * @param data - Model data including modelName, schemaId, and optional properties
3510
- */
3511
- async function writeModelToDb(modelFileId, data) {
3512
- const db = BaseDb.getAppDb();
3513
- if (!db)
3514
- throw new Error('Database not available');
3515
- // Find or create model record
3516
- const modelRecords = await db
3517
- .select()
3518
- .from(models)
3519
- .where(eq(models.schemaFileId, modelFileId))
3520
- .limit(1);
3521
- let modelId;
3522
- if (modelRecords.length === 0) {
3523
- // Create new model record
3524
- // Check again right before insert to avoid race conditions
3525
- const doubleCheck = await db
3526
- .select()
3527
- .from(models)
3528
- .where(eq(models.schemaFileId, modelFileId))
3529
- .limit(1);
3530
- if (doubleCheck.length > 0) {
3531
- // Another process created it, use existing
3532
- modelId = doubleCheck[0].id;
3533
- logger$3(`Model with schemaFileId "${modelFileId}" was created by another process, using existing record (id: ${modelId})`);
3534
- }
3535
- else {
3536
- // DO NOTHING on conflict: addModelsToDb writes the same row during schema import, and a failed
3537
- // insert is logged by sqlite-wasm even when caught.
3538
- const newModel = await db
3539
- .insert(models)
3540
- .values({
3541
- name: data.modelName,
3542
- schemaFileId: modelFileId,
3543
- isEdited: true, // Runtime-created models are edited
3544
- })
3545
- .onConflictDoNothing()
3546
- .returning();
3547
- if (newModel.length > 0) {
3548
- modelId = newModel[0].id;
3549
- }
3550
- else {
3551
- const existing = await db
3552
- .select()
3553
- .from(models)
3554
- .where(eq(models.schemaFileId, modelFileId))
3555
- .limit(1);
3556
- if (existing.length === 0) {
3557
- throw new Error(`Failed to create or find model "${data.modelName}" with schemaFileId "${modelFileId}"`);
3558
- }
3559
- modelId = existing[0].id;
3560
- logger$3(`Found existing model with schemaFileId "${modelFileId}" (id: ${modelId}) after insert conflict`);
3561
- }
3562
- }
3563
- }
3564
- else {
3565
- // Update existing model record
3566
- modelId = modelRecords[0].id;
3567
- const updates = {};
3568
- if (data.modelName !== modelRecords[0].name) {
3569
- updates.name = data.modelName;
3570
- }
3571
- if (Object.keys(updates).length > 0) {
3572
- await db
3573
- .update(models)
3574
- .set(updates)
3575
- .where(eq(models.id, modelId));
3576
- }
3577
- }
3578
- // Validate that modelId and schemaId are valid before creating join entry
3579
- if (!modelId || !Number.isInteger(modelId) || modelId <= 0) {
3580
- throw new Error(`Invalid modelId: ${modelId}. Model record must be created successfully before creating join entry.`);
3581
- }
3582
- if (!data.schemaId || !Number.isInteger(data.schemaId) || data.schemaId <= 0) {
3583
- throw new Error(`Invalid schemaId: ${data.schemaId}. Schema record must exist before creating join entry.`);
3584
- }
3585
- // Create model_schemas join entry
3586
- // CRITICAL: This must happen AFTER the model record is fully saved and committed
3587
- // The modelId comes from .returning() which ensures the record is committed
3588
- const existingJoin = await db
3589
- .select()
3590
- .from(modelSchemas)
3591
- .where(and(eq(modelSchemas.modelId, modelId), eq(modelSchemas.schemaId, data.schemaId)))
3592
- .limit(1);
3593
- if (existingJoin.length === 0) {
3594
- // Only provide modelId and schemaId - id is auto-increment and should not be included
3595
- // Don't use type cast - let Drizzle infer the correct type without id
3596
- // Verify both modelId and schemaId exist before inserting join record
3597
- try {
3598
- // Verify modelId exists
3599
- const modelCheck = await db
3600
- .select({ id: models.id })
3601
- .from(models)
3602
- .where(eq(models.id, modelId))
3603
- .limit(1);
3604
- if (modelCheck.length === 0) {
3605
- throw new Error(`Model with id ${modelId} does not exist in database. Cannot create join record.`);
3606
- }
3607
- // Verify schemaId exists and get name/fileId for invalidation broadcast
3608
- const schemaSchemaMod = await Promise.resolve().then(function () { return SchemaSchema; });
3609
- const { schemas: schemasTable } = schemaSchemaMod;
3610
- const schemaCheck = await db
3611
- .select({
3612
- id: schemasTable.id,
3613
- name: schemasTable.name,
3614
- schemaFileId: schemasTable.schemaFileId,
3615
- })
3616
- .from(schemasTable)
3617
- .where(eq(schemasTable.id, data.schemaId))
3618
- .limit(1);
3619
- if (schemaCheck.length === 0) {
3620
- throw new Error(`Schema with id ${data.schemaId} does not exist in database. Cannot create join record.`);
3621
- }
3622
- logger$3(`Creating join record: modelId=${modelId}, schemaId=${data.schemaId} (both verified to exist)`);
3623
- await linkModelToSchema(db, modelId, data.schemaId);
3624
- // Notify React useModels so it can invalidate; live query over join often doesn't re-run when model_schemas is inserted.
3625
- // Yield so the insert is visible to the refetch that will run when the broadcast is received.
3626
- if (typeof BroadcastChannel !== 'undefined') {
3627
- await new Promise((r) => setTimeout(r, 10));
3628
- const row = schemaCheck[0];
3629
- try {
3630
- postBroadcast('seed-models-invalidate', {
3631
- schemaName: row.name ?? undefined,
3632
- schemaFileId: row.schemaFileId ?? undefined,
3633
- });
3634
- }
3635
- catch (_) { }
3636
- }
3637
- logger$3(`Successfully created join record for model ${data.modelName} (id: ${modelId}) to schema (id: ${data.schemaId})`);
3638
- }
3639
- catch (error) {
3640
- if (error?.code === 'SQLITE_CONSTRAINT_FOREIGNKEY') {
3641
- logger$3(`FOREIGN KEY constraint failed when creating join record for model "${data.modelName}"`);
3642
- logger$3(`modelId: ${modelId}, schemaId: ${data.schemaId}`);
3643
- logger$3(`Error details:`, error);
3644
- // Re-throw with more context
3645
- throw new Error(`FOREIGN KEY constraint failed when creating join record for model "${data.modelName}" (modelId: ${modelId}, schemaId: ${data.schemaId}). ${error.message}`);
3646
- }
3647
- throw error;
3648
- }
3649
- }
3650
- // Write properties if provided
3651
- if (data.properties) {
3652
- for (const [propName, propData] of Object.entries(data.properties)) {
3653
- // Check if property already exists in database
3654
- const existingProps = await db
3655
- .select()
3656
- .from(properties)
3657
- .where(and(eq(properties.name, propName), eq(properties.modelId, modelId)))
3658
- .limit(1);
3659
- let propertyFileId;
3660
- if (existingProps.length > 0 && existingProps[0].schemaFileId) {
3661
- // Use existing property's schemaFileId
3662
- propertyFileId = existingProps[0].schemaFileId;
3663
- logger$3(`Using existing propertyFileId "${propertyFileId}" for property "${data.modelName}:${propName}"`);
3664
- }
3665
- else if (propData.id) {
3666
- // Use provided ID
3667
- propertyFileId = propData.id;
3668
- }
3669
- else {
3670
- // Generate random propertyFileId
3671
- // IDs should be generated in the import process before calling writeModelToDb
3672
- const { generateId } = await Promise.resolve().then(function () { return generateId$1; });
3673
- propertyFileId = generateId();
3674
- logger$3(`Generated propertyFileId "${propertyFileId}" for property "${data.modelName}:${propName}"`);
3675
- }
3676
- await writePropertyToDb(propertyFileId, {
3677
- modelId,
3678
- name: propName,
3679
- ...propData,
3680
- });
3681
- }
3682
- }
3683
- logger$3(`Wrote model ${data.modelName} (${modelFileId}) to database`);
3684
- }
3685
- /**
3686
- * Write property to database
3687
- * @param propertyFileId - The property file ID (schema_file_id)
3688
- * @param data - Property data including modelId, name, dataType, and other property fields
3689
- * @param isEdited - Optional flag indicating if property has been edited locally. Left unset, an update
3690
- * keeps the row's flag (a load or creation write must not clear an unsaved edit) and an insert stores false.
3691
- */
3692
- async function writePropertyToDb(propertyFileId, data, isEdited) {
3693
- const db = BaseDb.getAppDb();
3694
- if (!db)
3695
- throw new Error('Database not available');
3696
- // Find existing property by schemaFileId first (preferred for uniqueness)
3697
- let existingProperties = await db
3698
- .select()
3699
- .from(properties)
3700
- .where(eq(properties.schemaFileId, propertyFileId))
3701
- .limit(1);
3702
- // Fallback: find by modelId and name if not found by schemaFileId
3703
- if (existingProperties.length === 0) {
3704
- existingProperties = await db
3705
- .select()
3706
- .from(properties)
3707
- .where(and(eq(properties.name, data.name), eq(properties.modelId, data.modelId)))
3708
- .limit(1);
3709
- }
3710
- // Prepare property data
3711
- const propertyData = {
3712
- name: data.name,
3713
- modelId: data.modelId,
3714
- dataType: normalizeDataType(data.dataType),
3715
- schemaFileId: propertyFileId,
3716
- ...(isEdited !== undefined && { isEdited }),
3717
- };
3718
- // Handle ref property - create ref model if needed
3719
- // Check refModelName first, then ref (for backwards compatibility with schema files)
3720
- const refModelName = data.refModelName || data.ref;
3721
- if (refModelName) {
3722
- const refModel = await findOrCreateRefModelRecord(db, refModelName, data.modelId);
3723
- propertyData.refModelId = refModel.id;
3724
- }
3725
- else if (data.refModelId) {
3726
- propertyData.refModelId = data.refModelId;
3727
- }
3728
- else {
3729
- // If it's not a Relation type, ensure refModelId is null
3730
- propertyData.refModelId = null;
3731
- }
3732
- if (data.refValueType) {
3733
- propertyData.refValueType = normalizeDataType(data.refValueType);
3734
- }
3735
- else {
3736
- // If refValueType is not set, ensure it's null
3737
- propertyData.refValueType = null;
3738
- }
3739
- // Only overwrite `required` when the caller knows it. The creation write's payload can lack it, and
3740
- // defaulting to false here cleared the flag the schema import set (required relations became optional).
3741
- if (data.required !== undefined) {
3742
- propertyData.required = data.required;
3743
- }
3744
- // Storage settings, like `required`, only when the caller has them: the creation write's payload
3745
- // can lack them, and writing nulls would clear what the schema import stored.
3746
- const storage = propertyStorageColumns(data);
3747
- if (hasStorageSettings(storage)) {
3748
- Object.assign(propertyData, storage);
3749
- }
3750
- if (existingProperties.length > 0) {
3751
- // Property exists, update it with new values.
3752
- // Use existing row id for WHERE (not name+modelId) so renames work: the row
3753
- // may have the old name but we're updating to the new one.
3754
- const existing = existingProperties[0];
3755
- await db
3756
- .update(properties)
3757
- .set(propertyData)
3758
- .where(eq(properties.id, existing.id));
3759
- logger$3(`Updated property ${data.name} (${propertyFileId}) in database`);
3760
- }
3761
- else {
3762
- // Property doesn't exist, create it
3763
- // Double-check before insert to avoid race conditions
3764
- const doubleCheck = await db
3765
- .select()
3766
- .from(properties)
3767
- .where(eq(properties.schemaFileId, propertyFileId))
3768
- .limit(1);
3769
- if (doubleCheck.length > 0) {
3770
- // Another process created it, update it instead
3771
- await db
3772
- .update(properties)
3773
- .set(propertyData)
3774
- .where(eq(properties.id, doubleCheck[0].id));
3775
- logger$3(`Property with schemaFileId "${propertyFileId}" was created by another process, updated existing record`);
3776
- }
3777
- else {
3778
- // DO NOTHING on conflict: addModelsToDb writes the same row during schema import, and a failed
3779
- // insert is logged by sqlite-wasm even when caught.
3780
- const inserted = await db
3781
- .insert(properties)
3782
- .values({ required: false, isEdited: false, ...propertyData })
3783
- .onConflictDoNothing()
3784
- .returning({ id: properties.id });
3785
- if (inserted.length > 0) {
3786
- logger$3(`Created property ${data.name} (${propertyFileId}) in database`);
3787
- }
3788
- else {
3789
- const existing = await db
3790
- .select()
3791
- .from(properties)
3792
- .where(eq(properties.schemaFileId, propertyFileId))
3793
- .limit(1);
3794
- if (existing.length === 0) {
3795
- throw new Error(`Failed to create or find property "${data.name}" with schemaFileId "${propertyFileId}"`);
3796
- }
3797
- await db
3798
- .update(properties)
3799
- .set(propertyData)
3800
- .where(eq(properties.id, existing[0].id));
3801
- logger$3(`Found existing property with schemaFileId "${propertyFileId}" (id: ${existing[0].id}) after insert conflict, updated it`);
3802
- }
3803
- }
3804
- }
3805
- }
3806
- /**
3807
- * Get model database ID from model name or modelFileId
3808
- * @param modelNameOrFileId - Model name (string) or modelFileId (string)
3809
- * @param schemaNameOrId - Optional schema name or ID to narrow search
3810
- * @returns Model database ID
3811
- * @throws Error if model not found
3812
- */
3813
- async function getModelId(modelNameOrFileId, schemaNameOrId) {
3814
- const db = BaseDb.getAppDb();
3815
- if (!db) {
3816
- throw new Error('Database not available');
3817
- }
3818
- const modelSchemaMod = await Promise.resolve().then(function () { return ModelSchema; });
3819
- const { models: modelsTable } = modelSchemaMod;
3820
- const drizzleMod = await import('drizzle-orm');
3821
- const { eq, and } = drizzleMod;
3822
- // Try to find by modelFileId first (more reliable)
3823
- let records = await db
3824
- .select()
3825
- .from(modelsTable)
3826
- .where(eq(modelsTable.schemaFileId, modelNameOrFileId))
3827
- .limit(1);
3828
- // If not found by modelFileId, try by name
3829
- if (records.length === 0) {
3830
- // If schema is provided, narrow the search
3831
- if (schemaNameOrId !== undefined) {
3832
- const modelSchemaSchemaMod = await Promise.resolve().then(function () { return ModelSchemaSchema; });
3833
- const { modelSchemas } = modelSchemaSchemaMod;
3834
- const schemaSchemaMod = await Promise.resolve().then(function () { return SchemaSchema; });
3835
- const { schemas: schemasTable } = schemaSchemaMod;
3836
- if (typeof schemaNameOrId === 'number') {
3837
- // schemaNameOrId is schemaId
3838
- records = await db
3839
- .select({ id: modelsTable.id })
3840
- .from(modelsTable)
3841
- .innerJoin(modelSchemas, eq(modelsTable.id, modelSchemas.modelId))
3842
- .where(and(eq(modelsTable.name, modelNameOrFileId), eq(modelSchemas.schemaId, schemaNameOrId)))
3843
- .limit(1);
3844
- }
3845
- else {
3846
- // schemaNameOrId is schemaName
3847
- records = await db
3848
- .select({ id: modelsTable.id })
3849
- .from(modelsTable)
3850
- .innerJoin(modelSchemas, eq(modelsTable.id, modelSchemas.modelId))
3851
- .innerJoin(schemasTable, eq(modelSchemas.schemaId, schemasTable.id))
3852
- .where(and(eq(modelsTable.name, modelNameOrFileId), eq(schemasTable.name, schemaNameOrId)))
3853
- .limit(1);
3854
- }
3855
- }
3856
- else {
3857
- // No schema filter: name-only (newest row, warns when the name is ambiguous)
3858
- const byName = await resolveModelRecord(modelNameOrFileId, {}, db);
3859
- records = byName ? [byName] : [];
3860
- }
3861
- }
3862
- if (records.length === 0) {
3863
- const schemaInfo = schemaNameOrId ? ` in schema "${schemaNameOrId}"` : '';
3864
- throw new Error(`Model "${modelNameOrFileId}"${schemaInfo} not found in database`);
3865
- }
3866
- return records[0].id;
3867
- }
3868
- /**
3869
- * Get model database ID from modelFileId
3870
- * @param modelFileId - The model file ID (schema_file_id)
3871
- * @returns Model database ID
3872
- * @throws Error if model not found
3873
- */
3874
- async function getModelIdByFileId(modelFileId) {
3875
- const db = BaseDb.getAppDb();
3876
- if (!db) {
3877
- throw new Error('Database not available');
3878
- }
3879
- const modelSchemaMod = await Promise.resolve().then(function () { return ModelSchema; });
3880
- const { models: modelsTable } = modelSchemaMod;
3881
- const drizzleMod = await import('drizzle-orm');
3882
- const { eq } = drizzleMod;
3883
- const records = await db
3884
- .select()
3885
- .from(modelsTable)
3886
- .where(eq(modelsTable.schemaFileId, modelFileId))
3887
- .limit(1);
3888
- if (records.length === 0) {
3889
- throw new Error(`Model with file ID "${modelFileId}" not found in database`);
3890
- }
3891
- return records[0].id;
3892
- }
3893
-
3894
- var db = /*#__PURE__*/Object.freeze({
3895
- __proto__: null,
3896
- addModelsToDb: addModelsToDb,
3897
- addSchemaToDb: addSchemaToDb,
3898
- getAddressesForItemsFilter: getAddressesForItemsFilter,
3899
- getAllAddressesFromDb: getAllAddressesFromDb,
3900
- getItemStoragePropertiesForModel: getItemStoragePropertiesForModel,
3901
- getModelId: getModelId,
3902
- getModelIdByFileId: getModelIdByFileId,
3903
- getModelNameByModelId: getModelNameByModelId,
3904
- getOwnedAddressesFromDb: getOwnedAddressesFromDb,
3905
- getPropertyIdForModelAndName: getPropertyIdForModelAndName,
3906
- getPropertyIdForSchemaFileId: getPropertyIdForSchemaFileId,
3907
- getPropertyModelNameAndDataType: getPropertyModelNameAndDataType,
3908
- getWatchedAddressesFromDb: getWatchedAddressesFromDb,
3909
- loadModelsFromDbForSchema: loadModelsFromDbForSchema,
3910
- mergeDbModelsIntoSchemaModels: mergeDbModelsIntoSchemaModels,
3911
- migrateMetadataForPropertyRename: migrateMetadataForPropertyRename,
3912
- moveMetadataToRenamedProperty: moveMetadataToRenamedProperty,
3913
- propertyStorageColumns: propertyStorageColumns,
3914
- renameModelInDb: renameModelInDb,
3915
- savePropertyToDb: savePropertyToDb,
3916
- writeModelToDb: writeModelToDb,
3917
- writePropertyToDb: writePropertyToDb
3918
- });
3919
-
3920
- const isNode = () => {
3921
- return (typeof process !== 'undefined' &&
3922
- process.versions != null &&
3923
- process.versions.node != null);
3924
- };
3925
- const isBrowser = () => {
3926
- return !isElectron() && typeof document !== 'undefined' && typeof window !== 'undefined';
3927
- };
3928
- const isElectron = () => {
3929
- return typeof process !== 'undefined' && process.versions != null && process.versions.electron != null;
3930
- };
3931
- /**
3932
- * Detects if code is running in an Electron renderer process.
3933
- *
3934
- * Uses multiple detection methods for robustness:
3935
- * 1. Checks process.type === 'renderer' (most reliable)
3936
- * 2. Falls back to checking if Electron is present and window/document exist
3937
- *
3938
- * @returns true if running in Electron renderer process, false otherwise
3939
- */
3940
- const isElectronRenderer = () => {
3941
- // Method 1: Check process.type (most reliable - official Electron API)
3942
- if (typeof process !== 'undefined' &&
3943
- process.type === 'renderer') {
3944
- return true;
3945
- }
3946
- // Method 2: Fallback - Electron + browser-like environment
3947
- // Renderer processes have window/document, main process doesn't (in modern Electron)
3948
- if (isElectron() &&
3949
- typeof window !== 'undefined' &&
3950
- typeof document !== 'undefined') {
3951
- return true;
3952
- }
3953
- return false;
3954
- };
3955
- /**
3956
- * True where OPFS-backed Arweave file downloads should run: normal browsers and
3957
- * Electron renderer. {@link isBrowser} is false for every Electron build (including renderer).
3958
- */
3959
- const supportsOpfsFileDownloads = () => {
3960
- return isBrowser() || isElectronRenderer();
3961
- };
3962
- /**
3963
- * NODE_ENV is 'test' or IS_SEED_DEV is set. Bundlers compile `process.env.NODE_ENV` in, but
3964
- * not IS_SEED_DEV, so that is only read where a `process` global exists: browsers have none.
3965
- */
3966
- const isTestOrSeedDev = () => process.env.NODE_ENV === 'test' || (typeof process !== 'undefined' && !!process.env?.IS_SEED_DEV);
3967
-
3968
- /**
3969
- * Tag every Seed upload carries with the hex digest of its data. Producers: the SDK's
3970
- * `getPublishUploadData` / `getPublishUploads` (and through them both the L1 transaction path and
3971
- * the bundler / DataItem path in `@seedprotocol/publish`). Consumer: the files download
3972
- * (`events/files/download.ts` and the download worker), which verifies downloaded bytes against it.
3973
- */
3974
- const CONTENT_SHA256_TAG = 'Content-SHA-256';
3975
- const CONTENT_TYPE_TAG = 'Content-Type';
3976
- /**
3977
- * Lowercase hex SHA-256 of `data`: the value of the {@link CONTENT_SHA256_TAG} tag.
3978
- *
3979
- * Uses @noble/hashes rather than WebCrypto so it also works outside secure contexts (WebCrypto's
3980
- * `crypto.subtle` is missing on plain-http origins); the digest is the same.
3981
- *
3982
- * In SDK 0.7.2 and earlier the L1 transaction path (`getPublishUploads`) wrote SHA3-256 under this tag name
3983
- * while the bundler path wrote SHA-256. Uploads from that path still carry SHA3-256 digests; use
3984
- * {@link contentSha256TagMatches} to check a tag value against bytes.
3985
- */
3986
- const getContentSha256 = (data) => bytesToHex(sha256(data));
3987
- /**
3988
- * True when `tagValue` (a {@link CONTENT_SHA256_TAG} value read from Arweave) is the digest of
3989
- * `data`. Accepts SHA-256 and, for uploads made by SDK 0.7.2 and earlier through the L1 transaction path,
3990
- * SHA3-256.
3991
- */
3992
- const contentSha256TagMatches = (data, tagValue) => {
3993
- const expected = tagValue.trim().toLowerCase();
3994
- if (!expected)
3995
- return false;
3996
- return getContentSha256(data) === expected || bytesToHex(sha3_256(data)) === expected;
3997
- };
3998
- /**
3999
- * Tags for one upload, in the order every upload uses: content hash, content type, then any
4000
- * configured extra tags (e.g. App-Name from `arweaveUploadTags`).
4001
- */
4002
- const buildArweaveContentTags = (contentHash, contentType, extra) => {
4003
- const tags = [];
4004
- if (contentHash)
4005
- tags.push({ name: CONTENT_SHA256_TAG, value: contentHash });
4006
- if (contentType)
4007
- tags.push({ name: CONTENT_TYPE_TAG, value: contentType });
4008
- if (extra?.length)
4009
- tags.push(...extra);
4010
- return tags;
4011
- };
4012
-
4013
- /** Subdirectory of the files root that holds a storage seed's file, by its data type. */
4014
- const getStorageSubdirForDataType = (dataType) => {
4015
- switch (normalizeDataType(dataType ?? undefined)) {
4016
- case 'Image':
4017
- return 'images';
4018
- case 'Html':
4019
- return 'html';
4020
- case 'Json':
4021
- return 'json';
4022
- case 'File':
4023
- default:
4024
- // Unknown types are generic files; `images/` is only for what the SDK knows is an image.
4025
- return 'files';
4026
- }
4027
- };
4028
- /** Absolute directory for a storage seed's file (`images/`, `files/`, `html/` or `json/` under the files root). */
4029
- const getStorageDirForDataType = (dataType) => BaseFileManager.getFilesPath(getStorageSubdirForDataType(dataType));
4030
- /**
4031
- * Subdirectory (relative to the files root, no leading slash) for an ItemStorage property's file:
4032
- * its model's `storage.path` (`localStorageDir`), else `files`.
4033
- */
4034
- const getItemStorageSubdir = (localStorageDir) => (localStorageDir ?? '').replace(/^\/+/, '').replace(/\/+$/, '') || 'files';
4035
- /**
4036
- * File name a local save gives an ItemStorage property: `<seedUid or seedLocalId><extension>`
4037
- * (see saveItemStorage). Downloaded content is named after its transaction instead
4038
- * (`<txId><extension>`, see syncDbWithEas), which is what the metadata row's refResolvedValue says.
4039
- */
4040
- const getItemStorageLocalFileName = (seedId, filenameSuffix) => `${seedId}${filenameSuffix ?? ''}`;
4041
-
4042
- /** Rows per query: well under SQLite's bound-parameter limit. */
4043
- const CHUNK_SIZE = 400;
4044
- /**
4045
- * Where the download worker saves each child of a composite upload, by transaction id then child
4046
- * (property) name: a path relative to the files root.
4047
- *
4048
- * The paths come from the ItemStorage rows sync derives from a `storage_transaction_id`
4049
- * attestation (see `createMetadataRecordsForStorageTransactionId`): property value = the
4050
- * transaction id, `refResolvedValue` = `<txId><extension>`, `localStorageDir` = the model's storage
4051
- * directory. Those rows are what hydration reads, so a child saved here is found as that property's
4052
- * file. Transactions with no such rows are left out; the worker then falls back to the legacy
4053
- * html / json layout.
4054
- */
4055
- const getCompositeChildPaths = async (transactionIds) => {
4056
- const out = {};
4057
- const appDb = BaseDb.getAppDb();
4058
- if (!appDb || transactionIds.length === 0)
4059
- return out;
4060
- for (let i = 0; i < transactionIds.length; i += CHUNK_SIZE) {
4061
- const chunk = transactionIds.slice(i, i + CHUNK_SIZE);
4062
- const rows = await appDb
4063
- .select({
4064
- transactionId: metadata.propertyValue,
4065
- propertyName: metadata.propertyName,
4066
- refResolvedValue: metadata.refResolvedValue,
4067
- localStorageDir: metadata.localStorageDir,
4068
- })
4069
- .from(metadata)
4070
- .where(and(isNotNull(metadata.derivedFromUid), isNotNull(metadata.refResolvedValue), inArray(metadata.propertyValue, chunk)));
4071
- for (const row of rows) {
4072
- const { transactionId, propertyName, refResolvedValue } = row;
4073
- if (!transactionId || !propertyName || !refResolvedValue)
4074
- continue;
4075
- // A file name, never a path: anything else is not where the SDK would look.
4076
- if (refResolvedValue.includes('/') || refResolvedValue.includes('\\'))
4077
- continue;
4078
- const byName = (out[transactionId] ?? (out[transactionId] = {}));
4079
- byName[propertyName] ?? (byName[propertyName] = `${getItemStorageSubdir(row.localStorageDir)}/${refResolvedValue}`);
4080
- }
4081
- }
4082
- return out;
4083
- };
4084
-
4085
- const logger$2 = debug('seedSdk:files:download');
4086
- const AUTO_BULK_DOWNLOAD_THROTTLE_MS = 10000;
4087
- /** Avoid refetch-on-every-call: transient empty indexer responses were overwriting good cache (see trailing throttle ~10s after success). */
4088
- const GET_FILES_METADATA_STALE_MS = 120000;
4089
- let bulkDownloadInFlight = null;
4090
- const lazyDownloadInFlightByTransactionId = new Map();
4091
- const getAppDbReady = async () => {
4092
- if (!BaseDb.isAppDbReady()) {
4093
- // Wait for ClientManager to be ready (DB_INIT state or later)
4094
- const _mod_0 = await import('./ClientManager-DqL0q_eu.js');
4095
- const _ns_0 = _mod_0.W;
4096
- const clientManagerMod = _ns_0;
4097
- const { getClient } = clientManagerMod;
4098
- const clientManager = getClient();
4099
- const clientService = clientManager.getService();
4100
- await waitFor(clientService, (snapshot) => {
4101
- const state = snapshot.value;
4102
- return state === 'dbInit' ||
4103
- state === 'saveConfig' ||
4104
- state === 'processSchemaFiles' ||
4105
- state === 'addModelsToStore' ||
4106
- state === 'addModelsToDb' ||
4107
- state === 'idle';
4108
- }, { timeout: 30000 });
4109
- }
4110
- return BaseDb.getAppDb();
4111
- };
4112
- const getExcludedTransactions = async () => {
4113
- const appDb = await getAppDbReady();
4114
- if (!appDb) {
4115
- return new Set();
4116
- }
4117
- const excludedTransactionsQuery = await appDb
4118
- .select()
4119
- .from(appState)
4120
- .where(eq(appState.key, 'excludedTransactions'));
4121
- const excludedTransactions = new Set();
4122
- if (excludedTransactionsQuery && excludedTransactionsQuery.length === 1) {
4123
- const valueString = excludedTransactionsQuery[0].value;
4124
- if (valueString) {
4125
- const excludedTransactionsArray = JSON.parse(valueString);
4126
- for (const txId of excludedTransactionsArray) {
4127
- excludedTransactions.add(txId);
4128
- }
4129
- }
4130
- }
4131
- return excludedTransactions;
4132
- };
4133
- /** Arweave transaction ids are 43 chars in the URL-safe base64 alphabet. */
4134
- const ARWEAVE_TX_ID_RE$1 = /^[a-z0-9_-]{43}$/i;
4135
- const normalizeArweaveTxId = (raw) => {
4136
- if (raw == null || raw === '')
4137
- return undefined;
4138
- const t = String(raw).trim();
4139
- if (ARWEAVE_TX_ID_RE$1.test(t))
4140
- return t;
4141
- const m = t.match(/^([a-z0-9_-]{43})(?:\.[^/]+)?$/i);
4142
- if (m)
4143
- return m[1];
4144
- // Values may be full gateway URLs or paths; scan for an embedded tx id. It must be 43 characters
4145
- // on its own, not the start of a longer run: the first 43 of a bytes32 value (`0x0000…0020`,
4146
- // an EAS UID) are in the alphabet too.
4147
- const embedded = t.match(/(?<![a-z0-9_-])[a-z0-9_-]{43}(?![a-z0-9_-])/gi);
4148
- return embedded?.[0];
4149
- };
4150
- const mergeAddressLists = (a, b) => {
4151
- const seen = new Set();
4152
- const out = [];
4153
- for (const addr of [...(a ?? []), ...(b ?? [])]) {
4154
- if (!addr || !addr.trim())
4155
- continue;
4156
- const k = addr.toLowerCase();
4157
- if (seen.has(k))
4158
- continue;
4159
- seen.add(k);
4160
- out.push(addr);
4161
- }
4162
- return out;
4163
- };
4164
- const collectTransactionIdsFromLocalMetadata = async () => {
4165
- const appDb = await getAppDbReady();
4166
- if (!appDb) {
4167
- return [];
4168
- }
4169
- const nameOr = or(eq(metadata.propertyName, 'storageTransactionId'), eq(metadata.propertyName, 'storage_transaction_id'), eq(metadata.propertyName, 'transactionId'));
4170
- const propRows = await appDb
4171
- .select({ propertyValue: metadata.propertyValue })
4172
- .from(metadata)
4173
- .where(and(nameOr, isNotNull(metadata.propertyValue)));
4174
- const refRows = await appDb
4175
- .select({ refResolvedValue: metadata.refResolvedValue })
4176
- .from(metadata)
4177
- .where(isNotNull(metadata.refResolvedValue));
4178
- const ids = new Set();
4179
- for (const row of propRows) {
4180
- const id = normalizeArweaveTxId(row.propertyValue ?? undefined);
4181
- if (id)
4182
- ids.add(id);
4183
- }
4184
- for (const row of refRows) {
4185
- const id = normalizeArweaveTxId(row.refResolvedValue ?? undefined);
4186
- if (id)
4187
- ids.add(id);
4188
- }
4189
- return Array.from(ids);
4190
- };
4191
- const parseTransactionIdsFromMetadata = (filesMetadata, excludedTransactions) => {
4192
- const transactionIds = [];
4193
- for (const fileMetadata of filesMetadata) {
4194
- // Validate and parse decodedDataJson
4195
- if (!fileMetadata.decodedDataJson || fileMetadata.decodedDataJson.trim() === '') {
4196
- console.warn('[events/files] [download] empty decodedDataJson for fileMetadata: ', fileMetadata.id);
4197
- continue;
4198
- }
4199
- let json;
4200
- try {
4201
- json = JSON.parse(fileMetadata.decodedDataJson);
4202
- }
4203
- catch (error) {
4204
- console.warn('[events/files] [download] failed to parse decodedDataJson for fileMetadata: ', fileMetadata.id, error);
4205
- continue;
4206
- }
4207
- if (!Array.isArray(json) || json.length === 0 || !json[0]?.value?.value) {
4208
- console.warn('[events/files] [download] invalid decodedDataJson structure for fileMetadata: ', fileMetadata.id);
4209
- continue;
4210
- }
4211
- const rawTx = json[0].value.value;
4212
- if (typeof rawTx !== 'string' || rawTx.trim().length === 0) {
4213
- continue;
4214
- }
4215
- const transactionId = normalizeArweaveTxId(rawTx.trim());
4216
- if (!transactionId) {
4217
- continue;
4218
- }
4219
- if (excludedTransactions.has(transactionId) || excludedTransactions.has(rawTx.trim())) {
4220
- continue;
4221
- }
4222
- transactionIds.push(transactionId);
4223
- }
4224
- return transactionIds;
4225
- };
4226
- /** Content-SHA-256 tag values for the transactions that have one, from one batched tag lookup (cached). */
4227
- const getExpectedContentHashes = async (transactionIds) => {
4228
- const queryClient = BaseQueryClient.getQueryClient();
4229
- const tagsById = new Map();
4230
- const toFetch = [];
4231
- for (const transactionId of transactionIds) {
4232
- // Tags never change once a transaction is indexed, so a cached lookup stays good.
4233
- const cached = queryClient.getQueryData?.(['getTransactionTags', transactionId]);
4234
- if (cached)
4235
- tagsById.set(transactionId, cached);
4236
- else
4237
- toFetch.push(transactionId);
4238
- }
4239
- if (toFetch.length > 0) {
4240
- try {
4241
- const fetched = await getTransactionTagsByIds(toFetch, {
4242
- client: BaseArweaveClient.getArweaveClient(),
4243
- onError: (error) => logger$2('[download] transaction tags lookup failed', error),
4244
- });
4245
- for (const [transactionId, tags] of fetched) {
4246
- tagsById.set(transactionId, tags);
4247
- // Seed the per-transaction cache entry (the client interface has no setQueryData).
4248
- void queryClient
4249
- .fetchQuery({ queryKey: ['getTransactionTags', transactionId], queryFn: async () => tags })
4250
- .catch(() => { });
4251
- }
4252
- }
4253
- catch (error) {
4254
- logger$2('[download] transaction tags lookup failed', error);
4255
- }
4256
- }
4257
- const out = {};
4258
- for (const [transactionId, tags] of tagsById) {
4259
- const value = tags.find((tag) => tag.name === CONTENT_SHA256_TAG)?.value?.trim();
4260
- if (value)
4261
- out[transactionId] = value;
4262
- }
4263
- return out;
4264
- };
4265
- const downloadTransactionIds = async (rawTransactionIds, options) => {
4266
- if (!supportsOpfsFileDownloads()) {
4267
- return false;
4268
- }
4269
- const transactionIds = Array.from(new Set(rawTransactionIds.filter((tx) => !!tx && tx.trim().length > 0)));
4270
- if (transactionIds.length === 0) {
4271
- return false;
4272
- }
4273
- const excludedTransactions = await getExcludedTransactions();
4274
- await ensureReadGatewaySelected();
4275
- // Browser: do not call getTransactionStatus() — many gateways (e.g. g8way.io) redirect or omit
4276
- // CORS headers, so fetch() fails and we would wrongly exclude txs. The OPFS worker fetch decides success.
4277
- const transactionIdsToDownload = transactionIds.filter((id) => !excludedTransactions.has(id));
4278
- if (transactionIdsToDownload.length === 0) {
4279
- return false;
4280
- }
4281
- // One batched lookup for every upload's Content-SHA-256 tag; downloads are verified against it.
4282
- // Missing tags (old uploads, or a just-published one not indexed yet) just mean no verification.
4283
- const expectedContentHashes = await getExpectedContentHashes(transactionIdsToDownload);
4284
- // Preferred gateways first: a just-published item is only on the gateway it was uploaded through.
4285
- const arweaveBaseUrls = getArweaveReadBaseUrls();
4286
- // Where each ItemStorage child of a composite goes (the worker cannot read the DB or the schema).
4287
- const compositeChildPaths = await getCompositeChildPaths(transactionIdsToDownload).catch((error) => {
4288
- logger$2('[download] composite child paths lookup failed', error);
4289
- return {};
4290
- });
4291
- let result;
4292
- if (transactionIdsToDownload.length === 1) {
4293
- const transactionId = transactionIdsToDownload[0];
4294
- result = await BaseFileManager.downloadFileByTransactionId({
4295
- transactionId,
4296
- arweaveHost: BaseArweaveClient.getBaseUrl(),
4297
- arweaveBaseUrls,
4298
- excludedTransactions,
4299
- expectedContentHash: expectedContentHashes[transactionId],
4300
- compositeChildPaths: compositeChildPaths[transactionId],
4301
- skipExisting: options?.skipExisting,
4302
- });
4303
- }
4304
- else {
4305
- result = await BaseFileManager.downloadAllFiles({
4306
- transactionIds: transactionIdsToDownload,
4307
- arweaveHost: BaseArweaveClient.getBaseUrl(),
4308
- arweaveBaseUrls,
4309
- excludedTransactions,
4310
- expectedContentHashes,
4311
- compositeChildPaths,
4312
- skipExisting: options?.skipExisting,
4313
- });
4314
- }
4315
- if (result) {
4316
- for (const transactionId of result.hashMismatchTransactionIds) {
4317
- // Not saved by the worker; left for a later run (a gateway may serve the right bytes then).
4318
- logger$2(`[download] ${transactionId}: downloaded bytes do not match Content-SHA-256; skipped`);
4319
- }
4320
- }
4321
- return true;
4322
- };
4323
- const runBulkDownloadWithDedupe = async (transactionIds) => {
4324
- if (bulkDownloadInFlight) {
4325
- return bulkDownloadInFlight;
4326
- }
4327
- bulkDownloadInFlight = (async () => {
4328
- try {
4329
- await downloadTransactionIds(transactionIds, { skipExisting: true });
4330
- }
4331
- finally {
4332
- bulkDownloadInFlight = null;
4333
- }
4334
- })();
4335
- return bulkDownloadInFlight;
4336
- };
4337
- const downloadAllFilesBinaryRequestHandler = async (addressesHint) => {
4338
- if (!supportsOpfsFileDownloads()) {
4339
- return;
4340
- }
4341
- let fromDb = [];
4342
- if (BaseDb.isAppDbReady()) {
4343
- fromDb = await getAllAddressesFromDb();
4344
- }
4345
- if (!BaseDb.isAppDbReady()) {
4346
- await getAppDbReady();
4347
- fromDb = await getAllAddressesFromDb();
4348
- }
4349
- const addresses = mergeAddressLists(addressesHint, fromDb);
4350
- if (!addresses || addresses.length === 0) {
4351
- return;
4352
- }
4353
- const queryClient = BaseQueryClient.getQueryClient();
4354
- const easClient = BaseEasClient.getEasClient();
4355
- const filesMetadataQueryKey = ['getFilesMetadata', ...addresses];
4356
- const fetchFilesMetadata = () => easClient.request(GET_FILES_METADATA, {
4357
- where: {
4358
- attester: {
4359
- in: addresses,
4360
- },
4361
- schema: {
4362
- is: {
4363
- id: {
4364
- equals: '0x55fdefb36fcbbaebeb7d6b41dc3a1a9666e4e42154267c889de064faa7ede517',
4365
- },
4366
- },
4367
- },
4368
- },
4369
- });
4370
- let { filesMetadata } = await queryClient.fetchQuery({
4371
- queryKey: filesMetadataQueryKey,
4372
- queryFn: fetchFilesMetadata,
4373
- staleTime: GET_FILES_METADATA_STALE_MS,
4374
- });
4375
- if ((!Array.isArray(filesMetadata) || filesMetadata.length === 0) && addresses.length > 0) {
4376
- await queryClient.removeQueries({ queryKey: filesMetadataQueryKey });
4377
- ({ filesMetadata } = await queryClient.fetchQuery({
4378
- queryKey: filesMetadataQueryKey,
4379
- queryFn: fetchFilesMetadata,
4380
- staleTime: GET_FILES_METADATA_STALE_MS,
4381
- }));
4382
- }
4383
- const filesRoot = BaseFileManager.getWorkingDir();
4384
- await BaseFileManager.createDirIfNotExists(filesRoot);
4385
- await BaseFileManager.createDirIfNotExists(BaseFileManager.getFilesPath('html'));
4386
- await BaseFileManager.createDirIfNotExists(BaseFileManager.getFilesPath('json'));
4387
- await BaseFileManager.createDirIfNotExists(BaseFileManager.getFilesPath('images'));
4388
- await BaseFileManager.createDirIfNotExists(BaseFileManager.getFilesPath('files'));
4389
- const excludedTransactions = await getExcludedTransactions();
4390
- const fromEas = parseTransactionIdsFromMetadata(filesMetadata ?? [], excludedTransactions);
4391
- const fromLocal = await collectTransactionIdsFromLocalMetadata();
4392
- const transactionIds = Array.from(new Set([...fromEas, ...fromLocal]));
4393
- await runBulkDownloadWithDedupe(transactionIds);
4394
- };
4395
- const downloadTransactionIdWithDedupe = async (transactionId) => {
4396
- if (!supportsOpfsFileDownloads() || !transactionId || transactionId.trim().length === 0) {
4397
- return false;
4398
- }
4399
- const normalizedTxId = normalizeArweaveTxId(transactionId);
4400
- if (!normalizedTxId) {
4401
- logger$2(`[download] not an Arweave transaction id: ${transactionId}`);
4402
- return false;
4403
- }
4404
- const existing = lazyDownloadInFlightByTransactionId.get(normalizedTxId);
4405
- if (existing) {
4406
- return existing;
4407
- }
4408
- const inFlight = (async () => {
4409
- try {
4410
- // No skipExisting: the caller found no local file, so a file on disk it could not see is
4411
- // written again and announced.
4412
- const ok = await downloadTransactionIds([normalizedTxId]);
4413
- return ok;
4414
- }
4415
- finally {
4416
- lazyDownloadInFlightByTransactionId.delete(normalizedTxId);
4417
- }
4418
- })();
4419
- lazyDownloadInFlightByTransactionId.set(normalizedTxId, inFlight);
4420
- return inFlight;
4421
- };
4422
- const scheduleBulkFilesDownloadFromEasSyncThrottled = throttle((addressesHint) => {
4423
- void downloadAllFilesBinaryRequestHandler(addressesHint).catch((error) => {
4424
- logger$2('[scheduleBulkFilesDownloadFromEasSync] failed', error);
4425
- });
4426
- }, AUTO_BULK_DOWNLOAD_THROTTLE_MS, { leading: true, trailing: false });
4427
- const scheduleBulkFilesDownloadFromEasSync = (addressesHint) => {
4428
- if (!supportsOpfsFileDownloads()) {
4429
- return;
4430
- }
4431
- scheduleBulkFilesDownloadFromEasSyncThrottled(addressesHint);
4432
- };
4433
-
4434
- var download = /*#__PURE__*/Object.freeze({
4435
- __proto__: null,
4436
- downloadAllFilesBinaryRequestHandler: downloadAllFilesBinaryRequestHandler,
4437
- downloadTransactionIdWithDedupe: downloadTransactionIdWithDedupe,
4438
- scheduleBulkFilesDownloadFromEasSync: scheduleBulkFilesDownloadFromEasSync
4439
- });
4440
-
4441
- const logger$1 = debug('seedSdk:helpers:ensureImageLocal');
4442
- /** Arweave transaction ids are 43 chars in the URL-safe base64 alphabet. */
4443
- const ARWEAVE_TX_ID_RE = /^[a-z0-9_-]{43}$/i;
4444
- const IMAGE_SIZES = [
4445
- ImageSize.EXTRA_SMALL,
4446
- ImageSize.SMALL,
4447
- ImageSize.MEDIUM,
4448
- ImageSize.LARGE,
4449
- ImageSize.EXTRA_LARGE,
4450
- ];
4451
- const inFlightByKey = new Map();
4452
- const normalizeArweaveTxIdForEnsure = (raw) => {
4453
- if (raw == null || raw === '')
4454
- return undefined;
4455
- const t = String(raw).trim();
4456
- if (ARWEAVE_TX_ID_RE.test(t))
4457
- return t;
4458
- const m = t.match(/^([a-z0-9_-]{43})(?:\.[^/]+)?$/i);
4459
- if (m)
4460
- return m[1];
4461
- const embedded = t.match(/[a-z0-9_-]{43}/gi);
4462
- if (embedded) {
4463
- for (const candidate of embedded) {
4464
- if (ARWEAVE_TX_ID_RE.test(candidate))
4465
- return candidate;
4466
- }
4467
- }
4468
- return undefined;
4469
- };
4470
- const closestImageSize = (width) => {
4471
- let best = ImageSize.EXTRA_SMALL;
4472
- let bestDist = Math.abs(best - width);
4473
- for (const size of IMAGE_SIZES) {
4474
- const dist = Math.abs(size - width);
4475
- if (dist < bestDist) {
4476
- best = size;
4477
- bestDist = dist;
4478
- }
4479
- }
4480
- return best;
4481
- };
4482
- const mapRequestedWidthsToImageSizes = (widths) => {
4483
- const requested = widths && widths.length > 0 ? widths : [ImageSize.EXTRA_SMALL];
4484
- const unique = new Set();
4485
- for (const w of requested) {
4486
- if (typeof w !== 'number' || !Number.isFinite(w) || w <= 0)
4487
- continue;
4488
- unique.add(closestImageSize(w));
4489
- }
4490
- if (unique.size === 0) {
4491
- unique.add(ImageSize.EXTRA_SMALL);
4492
- }
4493
- return Array.from(unique).sort((a, b) => a - b);
4494
- };
4495
- const getBasename = (filePath) => {
4496
- const path = BaseFileManager.getPathModule();
4497
- return path.basename(filePath);
4498
- };
4499
- const getBasenameWithoutExtension = (fileName) => {
4500
- const match = fileName.match(/^(.*[\/\\])?([^\/\\]+?)(\.[^.\/\\]*)?$/);
4501
- return match?.[2] ?? fileName;
4502
- };
4503
- const resolveMatchingImagePath = async (needle) => {
4504
- const dirPath = BaseFileManager.getFilesPath('images');
4505
- const dirExists = await BaseFileManager.pathExists(dirPath);
4506
- if (!dirExists)
4507
- return undefined;
4508
- const fs = await BaseFileManager.getFs();
4509
- const path = BaseFileManager.getPathModule();
4510
- const files = await fs.promises.readdir(dirPath);
4511
- // Prefer exact basename match (files only); skip size dirs.
4512
- const exact = files.find((file) => path.basename(file) === needle);
4513
- if (exact) {
4514
- const candidate = BaseFileManager.getFilesPath('images', exact);
4515
- try {
4516
- const zenfs = await BaseFileManager.getFs();
4517
- const stat = await zenfs.promises.stat(candidate);
4518
- if (stat.isFile())
4519
- return candidate;
4520
- }
4521
- catch {
4522
- // fall through
4523
- }
4524
- }
4525
- const matchingFiles = files.filter((file) => {
4526
- const base = path.basename(file);
4527
- return base.includes(needle);
4528
- });
4529
- for (const file of matchingFiles) {
4530
- const candidate = BaseFileManager.getFilesPath('images', file);
4531
- try {
4532
- const zenfs = await BaseFileManager.getFs();
4533
- const stat = await zenfs.promises.stat(candidate);
4534
- if (stat.isFile())
4535
- return candidate;
4536
- }
4537
- catch {
4538
- continue;
4539
- }
4540
- }
4541
- return undefined;
4542
- };
4543
- const sizedVariantPath = (originalFilePath, size) => {
4544
- const path = BaseFileManager.getPathModule();
4545
- const dir = path.dirname(originalFilePath);
4546
- const base = getBasenameWithoutExtension(getBasename(originalFilePath));
4547
- return path.join(dir, String(size), `${base}.webp`);
4548
- };
4549
- const ensureKey = (txId, fileName, sizes) => {
4550
- const idPart = txId ?? fileName ?? '';
4551
- return `${idPart}|${sizes.join(',')}`;
4552
- };
4553
- const runEnsureImageLocal = async (params) => {
4554
- if (!supportsOpfsFileDownloads()) {
4555
- return { status: 'unsupported' };
4556
- }
4557
- const fileName = params.fileName?.trim() || undefined;
4558
- const txId = normalizeArweaveTxIdForEnsure(params.transactionId) ??
4559
- normalizeArweaveTxIdForEnsure(fileName);
4560
- if (!txId && !fileName) {
4561
- return { status: 'missing-id' };
4562
- }
4563
- const targetSizes = mapRequestedWidthsToImageSizes(params.widths);
4564
- const needles = [fileName, txId].filter((n) => typeof n === 'string' && n.length > 0);
4565
- try {
4566
- let filePath;
4567
- for (const needle of needles) {
4568
- filePath = await resolveMatchingImagePath(needle);
4569
- if (filePath)
4570
- break;
4571
- }
4572
- if (!filePath && txId) {
4573
- await downloadTransactionIdWithDedupe(txId);
4574
- for (const needle of needles) {
4575
- filePath = await resolveMatchingImagePath(needle);
4576
- if (filePath)
4577
- break;
4578
- }
4579
- // Downloads store as bare tx id
4580
- if (!filePath) {
4581
- const direct = BaseFileManager.getFilesPath('images', txId);
4582
- if (await BaseFileManager.pathExists(direct)) {
4583
- filePath = direct;
4584
- }
4585
- }
4586
- }
4587
- if (!filePath) {
4588
- return { status: 'failed' };
4589
- }
4590
- const createdWidths = [];
4591
- for (const size of targetSizes) {
4592
- const variantPath = sizedVariantPath(filePath, size);
4593
- const exists = await BaseFileManager.pathExists(variantPath);
4594
- if (exists)
4595
- continue;
4596
- await BaseFileManager.resizeImage({
4597
- filePath,
4598
- width: size,
4599
- height: size,
4600
- });
4601
- createdWidths.push(size);
4602
- }
4603
- return {
4604
- status: 'ready',
4605
- filePath,
4606
- createdWidths,
4607
- };
4608
- }
4609
- catch (error) {
4610
- logger$1('ensureImageLocal failed', error);
4611
- return { status: 'failed' };
4612
- }
4613
- };
4614
- /**
4615
- * Ensure an image original exists in OPFS (downloading from Arweave if needed)
4616
- * and create only the requested ImageSize variant(s) for that one file.
4617
- */
4618
- const ensureImageLocal = async (params) => {
4619
- if (!supportsOpfsFileDownloads()) {
4620
- return { status: 'unsupported' };
4621
- }
4622
- const fileName = params.fileName?.trim() || undefined;
4623
- const txId = normalizeArweaveTxIdForEnsure(params.transactionId) ??
4624
- normalizeArweaveTxIdForEnsure(fileName);
4625
- if (!txId && !fileName) {
4626
- return { status: 'missing-id' };
4627
- }
4628
- const targetSizes = mapRequestedWidthsToImageSizes(params.widths);
4629
- const key = ensureKey(txId, fileName, targetSizes);
4630
- const existing = inFlightByKey.get(key);
4631
- if (existing) {
4632
- return existing;
4633
- }
4634
- const inFlight = (async () => {
4635
- try {
4636
- return await runEnsureImageLocal({
4637
- transactionId: txId,
4638
- fileName,
4639
- widths: targetSizes,
4640
- });
4641
- }
4642
- finally {
4643
- inFlightByKey.delete(key);
4644
- }
4645
- })();
4646
- inFlightByKey.set(key, inFlight);
4647
- return inFlight;
4648
- };
4649
-
4650
- const randomId = customAlphabet(alphanumeric, 10);
4651
- /**
4652
- * A 10-character local id. Never starts with "0x" (about 1 in 3,844 random ids would), so code that
4653
- * tells local ids from EAS uids by that prefix can't mistake one for the other.
4654
- */
4655
- const generateId = () => {
4656
- let id = randomId();
4657
- while (id.startsWith('0x'))
4658
- id = randomId();
4659
- return id;
4660
- };
4661
-
4662
- var generateId$1 = /*#__PURE__*/Object.freeze({
4663
- __proto__: null,
4664
- generateId: generateId
4665
- });
4666
-
4667
- const logger = debug('seedSdk:shared:helpers');
4668
- const getMimeType = (base64) => {
4669
- if (!base64) {
4670
- return null;
4671
- }
4672
- const result = base64.match(/^data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+).*,/);
4673
- if (result && result.length > 1) {
4674
- return result[1];
4675
- }
4676
- else {
4677
- return null; // MIME type could not be determined
4678
- }
4679
- };
4680
- const getCorrectId = (localIdOrUid) => {
4681
- const id = {
4682
- localId: undefined,
4683
- uid: undefined,
4684
- };
4685
- if (!localIdOrUid) {
4686
- return id;
4687
- }
4688
- if (localIdOrUid.length === 10) {
4689
- id.localId = localIdOrUid;
4690
- }
4691
- if (localIdOrUid.startsWith('0x') && localIdOrUid.length === 66) {
4692
- id.uid = localIdOrUid;
4693
- }
4694
- return id;
4695
- };
4696
- const getDataTypeFromString = (data) => {
4697
- const nonImageBase64Regex = /^(?!data:image\/(?:jpeg|png|gif|bmp|webp);base64,)[A-Za-z0-9+/=]+$/;
4698
- if (nonImageBase64Regex.test(data)) {
4699
- return 'base64';
4700
- }
4701
- // Regular expression for base64 (simple version, checking for base64 format)
4702
- const imageBase64Regex = /^data:image\/[a-zA-Z]+;base64,[A-Za-z0-9+/]+={0,2}$/;
4703
- if (imageBase64Regex.test(data)) {
4704
- return 'imageBase64';
4705
- }
4706
- // Blob URLs are fetchable but session-scoped; recognize them as url type
4707
- if (data.startsWith('blob:')) {
4708
- return 'url';
4709
- }
4710
- // Regular expression for URL (simple version, checking for common URL format)
4711
- const urlRegex = /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/;
4712
- if (urlRegex.test(data)) {
4713
- return 'url';
4714
- }
4715
- return null;
4716
- };
4717
- const convertTxIdToImage = async (txId) => {
4718
- const imageFilePath = BaseFileManager.getFilesPath('images', txId);
4719
- const fileExists = await BaseFileManager.pathExists(imageFilePath);
4720
- if (!fileExists) {
4721
- logger(`[ItemView] [updateImage] ${imageFilePath} does not exist`);
4722
- return;
4723
- }
4724
- const buffer = await BaseFileManager.readFileAsBuffer(imageFilePath);
4725
- // Convert Blob to ArrayBuffer if needed, or use Buffer directly
4726
- let arrayBuffer;
4727
- if (buffer instanceof Blob) {
4728
- arrayBuffer = await buffer.arrayBuffer();
4729
- }
4730
- else if (buffer instanceof ArrayBuffer) {
4731
- arrayBuffer = buffer;
4732
- }
4733
- else {
4734
- // Node.js Buffer
4735
- const sliced = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
4736
- // Handle SharedArrayBuffer case by creating a new ArrayBuffer
4737
- if (sliced instanceof SharedArrayBuffer) {
4738
- const uint8Array = new Uint8Array(sliced);
4739
- arrayBuffer = uint8Array.buffer.slice(0);
4740
- }
4741
- else {
4742
- arrayBuffer = sliced;
4743
- }
4744
- }
4745
- const uint = new Uint8Array(arrayBuffer);
4746
- const imageBlob = new Blob([uint]);
4747
- return URL.createObjectURL(imageBlob);
4748
- };
4749
- /**
4750
- * Constructs an Arweave URL for a storage transaction ID
4751
- * @param storageTransactionId - The Arweave transaction ID
4752
- * @returns The full URL to access the transaction data on Arweave (e.g., https://arweave.net/raw/{transactionId})
4753
- * @deprecated Use BaseArweaveClient.getRawUrl() instead for better consistency and testability
4754
- */
4755
- const getArweaveUrlForTransaction = (storageTransactionId) => {
4756
- return BaseArweaveClient.getRawUrl(storageTransactionId);
4757
- };
4758
-
4759
- export { INTERNAL_DATA_TYPES as $, AmbiguousModelError as A, modelsRelations as B, pendingWrites as C, DEFAULT_TEXT_MAX_LENGTH as D, properties as E, propertiesRelations as F, propertyUidRelations as G, propertyUids as H, publishProcesses as I, schemas as J, uploadProcesses as K, BaseDb as L, ModelPropertyDataTypes as M, resolveStorageNameToSchemaName as N, nameMatchLevels as O, Property as P, BaseFileManager as Q, resolveModelRecord as R, getPropertyIdForModelAndName as S, THtmlEmbeddedDataUriPolicy as T, resolveItemModelFileId as U, INTERNAL_STORAGE_MODEL_FILE_IDS as V, getStorageDirForDataType as W, getContentSha256 as X, buildArweaveContentTags as Y, getItemStorageSubdir as Z, getItemStorageLocalFileName as _, TProperty as a, convertTxIdToImage as a$, normalizePublisher as a0, getAllAddressesFromDb as a1, loadNameMatchLevels as a2, scheduleBulkFilesDownloadFromEasSync as a3, EAS_SEED_DATA_SYNCED_TO_DB_EVENT as a4, generateId as a5, normalizeHexAddress as a6, listRelationEasPropertyName as a7, easPropertyNameForPublish as a8, toMetadataPropertyName as a9, getFeedItemStringField as aA, normalizeAddressConfig as aB, normalizeAddressList as aC, normalizeArweaveTxIdForEnsure as aD, normalizeFeedItemFields as aE, resolveMediaRef as aF, isInternalSchema as aG, writeModelToDb as aH, writePropertyToDb as aI, renameModelInDb as aJ, currentNameMatchLevels as aK, withSqliteBusyRetry as aL, loadModelsFromDbForSchema as aM, mergeDbModelsIntoSchemaModels as aN, addSchemaToDb as aO, addModelsToDb as aP, clearNameMatchLevels as aQ, isTestOrSeedDev as aR, warnOnInvalidNameMatch as aS, INTERNAL_SCHEMA_IDS as aT, linkModelToSchema as aU, getWatchedAddressesFromDb as aV, getOwnedAddressesFromDb as aW, CLIENT_NOT_INITIALIZED as aX, toSchemaPropertyName as aY, easSchemaDefinitionForProperty as aZ, getStorageTransactionIdForSeedUid as a_, getItemStoragePropertiesForModel as aa, getMetadataPropertyNamesForQuery as ab, isNode as ac, ZERO_BYTES32 as ad, getItemModelScope as ae, INTERNAL_PROPERTY_NAMES as af, isStorageSeedRef as ag, defaultAttestationData as ah, getModelIdByFileId as ai, getModelId as aj, setCachedPropertyRefresher as ak, isSqliteBusyError as al, SQLITE_BUSY_RETRY_DELAYS_MS as am, isBrowser as an, CONTENT_SHA256_TAG as ao, CONTENT_TYPE_TAG as ap, ImageSize as aq, SEED_PROTOCOL_SCHEMA_NAME as ar, SeedModels as as, VERSION_SCHEMA_UID_OPTIMISM_SEPOLIA as at, classifyMediaRef as au, closestImageSize as av, contentSha256TagMatches as aw, ensureImageLocal as ax, getAddressesForItemsFilter as ay, getArweaveUrlForTransaction as az, TPropertyConstructor as b, getPropertyIdForSchemaFileId as b0, downloadTransactionIdWithDedupe as b1, resolveMetadataRecord as b2, getDataTypeFromString as b3, getMimeType as b4, listRelationStoragePropertyName as b5, PROPERTY_NAMES_EXEMPT_FROM_ID_SUFFIX_STRIP as b6, toItemPropertyKey as b7, getAlternatePropertyNameForInstanceLookup as b8, BaseDb$1 as b9, ModelSchema as ba, BaseFileManager$1 as bb, SchemaSchema as bc, ModelSchemaSchema as bd, resolveModelRecord$1 as be, db as bf, download as bg, generateId$1 as bh, TPropertyDataType as c, TPropertyDefs as d, TStorageType as e, TValidationRules as f, normalizePropertyRecordSchema as g, appState as h, isDataType as i, config as j, getCorrectId as k, isPublishedSeedRef as l, metadata as m, normalizeDataType as n, modelUids as o, models as p, normalizeRelationPropertyValue as q, resolveSeedIdsFromRefString as r, seeds as s, arweaveL1FinalizeJobs as t, easSyncProcesses as u, versions as v, entityChanges as w, htmlEmbeddedImageCoPublish as x, modelRelations as y, modelSchemas as z };
4760
- //# sourceMappingURL=index-rOTW-2QZ.js.map