@seedprotocol/sdk 0.7.9-next.1177.ga3e08ed → 0.7.9-next.1306.g4ff6582

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 (586) hide show
  1. package/dist/BaseFileManager-BAlySv8w.js +650 -0
  2. package/dist/BaseFileManager-BAlySv8w.js.map +1 -0
  3. package/dist/{Item-0oDoTPhi.js → Item-Bf4kWKpH.js} +5647 -6573
  4. package/dist/Item-Bf4kWKpH.js.map +1 -0
  5. package/dist/{ModelProperty-i5flwzQG.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-DnGKel4V.js → SchemaValidationService-CnmKyzR-.js} +6 -9
  10. package/dist/{SchemaValidationService-DnGKel4V.js.map → SchemaValidationService-CnmKyzR-.js.map} +1 -1
  11. package/dist/{configurePlatform-CrMafpMW.js → configurePlatform-CiOJ1R3q.js} +3 -12
  12. package/dist/configurePlatform-CiOJ1R3q.js.map +1 -0
  13. package/dist/db/drizzle/0025_add_metadata_retract.sql +1 -0
  14. package/dist/db/drizzle/0026_add_content_hash_to_schemas.sql +1 -0
  15. package/dist/db/drizzle/0027_link_metadata_property_ids.sql +74 -0
  16. package/dist/db/drizzle/0028_add_definition_to_properties.sql +17 -0
  17. package/dist/db/drizzle/0029_dedupe_chain_uids_add_unique_indexes.sql +120 -0
  18. package/dist/db/drizzle/meta/0025_snapshot.json +1652 -0
  19. package/dist/db/drizzle/meta/0026_snapshot.json +1659 -0
  20. package/dist/db/drizzle/meta/0027_snapshot.json +1659 -0
  21. package/dist/db/drizzle/meta/0028_snapshot.json +1666 -0
  22. package/dist/db/drizzle/meta/0029_snapshot.json +1691 -0
  23. package/dist/db/drizzle/meta/_journal.json +36 -1
  24. package/dist/{detachRowsBeforeDelete-DXFYSHRR.js → detachRowsBeforeDelete-sEaUwlOU.js} +2 -13
  25. package/dist/{detachRowsBeforeDelete-DXFYSHRR.js.map → detachRowsBeforeDelete-sEaUwlOU.js.map} +1 -1
  26. package/dist/dist/{index-Dx_GTdyQ.d.ts → BaseFileManager-BAlySv8w.d.ts} +1424 -2696
  27. package/dist/dist/BaseFileManager-BAlySv8w.d.ts.map +1 -0
  28. package/dist/dist/{configurePlatform-CrMafpMW.d.ts → configurePlatform-CiOJ1R3q.d.ts} +1 -1
  29. package/dist/dist/configurePlatform-CiOJ1R3q.d.ts.map +1 -0
  30. package/dist/dist/{index-PbnKw2es.d.ts → index-D0V-mjCV.d.ts} +56 -1
  31. package/dist/dist/{index-PbnKw2es.d.ts.map → index-D0V-mjCV.d.ts.map} +1 -1
  32. package/dist/dist/{index.node-CzMwMr_d.d.ts → index.node-CYPemH-t.d.ts} +225 -5
  33. package/dist/dist/{index.node-CzMwMr_d.d.ts.map → index.node-CYPemH-t.d.ts.map} +1 -1
  34. package/dist/dist/platform.node.d.ts +2 -2
  35. package/dist/eas-wB_fQ8Tc.js +32 -0
  36. package/dist/eas-wB_fQ8Tc.js.map +1 -0
  37. package/dist/errors-DLSoqSHG.js +6550 -0
  38. package/dist/errors-DLSoqSHG.js.map +1 -0
  39. package/dist/getPublishPayload-BnIwpwz1.js +760 -0
  40. package/dist/getPublishPayload-BnIwpwz1.js.map +1 -0
  41. package/dist/getPublishPendingDiff-D6_-A5ns.js +125 -0
  42. package/dist/getPublishPendingDiff-D6_-A5ns.js.map +1 -0
  43. package/dist/getPublishUploads-sS6JfPgz.js +783 -0
  44. package/dist/getPublishUploads-sS6JfPgz.js.map +1 -0
  45. package/dist/{index-DJmWkVIK.js → index-BVBmz_cc.js} +11 -14
  46. package/dist/index-BVBmz_cc.js.map +1 -0
  47. package/dist/index-CYGWPoOx.js +27 -0
  48. package/dist/index-CYGWPoOx.js.map +1 -0
  49. package/dist/{index-PbnKw2es.js → index-D0V-mjCV.js} +2 -2
  50. package/dist/index-D0V-mjCV.js.map +1 -0
  51. package/dist/index-DmTtj1nk.js +817 -0
  52. package/dist/index-DmTtj1nk.js.map +1 -0
  53. package/dist/{index.browser-i9uPJice.js → index.browser-CUmBVYen.js} +436 -379
  54. package/dist/index.browser-CUmBVYen.js.map +1 -0
  55. package/dist/{index.node-CzMwMr_d.js → index.node-CYPemH-t.js} +10 -10
  56. package/dist/index.node-CYPemH-t.js.map +1 -0
  57. package/dist/main.js +23 -1105
  58. package/dist/main.js.map +1 -1
  59. package/dist/modelPropertyRecordSchemas-C7UgA5TH.js +48 -0
  60. package/dist/modelPropertyRecordSchemas-C7UgA5TH.js.map +1 -0
  61. package/dist/node.js +14 -18
  62. package/dist/node.js.map +1 -1
  63. package/dist/platform.browser.js +8 -13
  64. package/dist/platform.browser.js.map +1 -1
  65. package/dist/platform.node.js +6 -16
  66. package/dist/platform.node.js.map +1 -1
  67. package/dist/property-BxUYznH0.js +87 -0
  68. package/dist/property-BxUYznH0.js.map +1 -0
  69. package/dist/publishGraph-EcB40Uii.js +311 -0
  70. package/dist/publishGraph-EcB40Uii.js.map +1 -0
  71. package/dist/readHtmlBodyForStorageSeed-D6KPtWoR.js +110 -0
  72. package/dist/readHtmlBodyForStorageSeed-D6KPtWoR.js.map +1 -0
  73. package/dist/seedSchema/MetadataSchema.ts +3 -0
  74. package/dist/seedSchema/ModelSchema.ts +4 -0
  75. package/dist/seedSchema/ModelSchemaSchema.ts +5 -2
  76. package/dist/seedSchema/PendingWriteSchema.ts +4 -1
  77. package/dist/seedSchema/SchemaSchema.ts +3 -0
  78. package/dist/seedSchema/SeedSchema.ts +8 -2
  79. package/dist/seedSchema/VersionSchema.ts +7 -2
  80. package/dist/seedSchema/attestedUid.ts +10 -0
  81. package/dist/src/Item/Item.d.ts +32 -30
  82. package/dist/src/Item/Item.d.ts.map +1 -1
  83. package/dist/src/Item/loadItem.d.ts +12 -0
  84. package/dist/src/Item/loadItem.d.ts.map +1 -0
  85. package/dist/src/Item/service/itemMachineSingle.d.ts +2 -13
  86. package/dist/src/Item/service/itemMachineSingle.d.ts.map +1 -1
  87. package/dist/src/ItemProperty/ItemProperty.d.ts +98 -41
  88. package/dist/src/ItemProperty/ItemProperty.d.ts.map +1 -1
  89. package/dist/src/ItemProperty/loadProperty.d.ts +11 -0
  90. package/dist/src/ItemProperty/loadProperty.d.ts.map +1 -0
  91. package/dist/src/ItemProperty/propertyWrite.d.ts +54 -0
  92. package/dist/src/ItemProperty/propertyWrite.d.ts.map +1 -0
  93. package/dist/src/ItemProperty/{service/actors/saveValueToDb/rowContext.d.ts → rowContext.d.ts} +1 -1
  94. package/dist/src/ItemProperty/rowContext.d.ts.map +1 -0
  95. package/dist/src/ItemProperty/service/actors/hydrateFromDb.d.ts +6 -0
  96. package/dist/src/ItemProperty/service/actors/hydrateFromDb.d.ts.map +1 -1
  97. package/dist/src/ItemProperty/service/actors/modelPropertyRecordSchemas.d.ts +5 -15
  98. package/dist/src/ItemProperty/service/actors/modelPropertyRecordSchemas.d.ts.map +1 -1
  99. package/dist/src/ItemProperty/service/propertyMachine.d.ts +3 -46
  100. package/dist/src/ItemProperty/service/propertyMachine.d.ts.map +1 -1
  101. package/dist/src/Model/Model.d.ts +26 -56
  102. package/dist/src/Model/Model.d.ts.map +1 -1
  103. package/dist/src/Model/createModelProperties.d.ts +10 -0
  104. package/dist/src/Model/createModelProperties.d.ts.map +1 -0
  105. package/dist/src/Model/loadModel.d.ts +13 -0
  106. package/dist/src/Model/loadModel.d.ts.map +1 -0
  107. package/dist/src/Model/service/actors/validateModel.d.ts.map +1 -1
  108. package/dist/src/Model/service/modelMachine.d.ts +5 -114
  109. package/dist/src/Model/service/modelMachine.d.ts.map +1 -1
  110. package/dist/src/ModelProperty/ModelProperty.d.ts +44 -37
  111. package/dist/src/ModelProperty/ModelProperty.d.ts.map +1 -1
  112. package/dist/src/ModelProperty/service/actors/compareAndMarkDraft.d.ts.map +1 -1
  113. package/dist/src/ModelProperty/service/actors/validateProperty.d.ts.map +1 -1
  114. package/dist/src/ModelProperty/service/modelPropertyMachine.d.ts +6 -77
  115. package/dist/src/ModelProperty/service/modelPropertyMachine.d.ts.map +1 -1
  116. package/dist/src/Schema/Schema.d.ts +36 -37
  117. package/dist/src/Schema/Schema.d.ts.map +1 -1
  118. package/dist/src/Schema/loadOrCreateSchema.d.ts +21 -0
  119. package/dist/src/Schema/loadOrCreateSchema.d.ts.map +1 -0
  120. package/dist/src/Schema/service/schemaMachine.d.ts +7 -125
  121. package/dist/src/Schema/service/schemaMachine.d.ts.map +1 -1
  122. package/dist/src/browser/db/Db.d.ts.map +1 -1
  123. package/dist/src/browser/db/changeFeed.d.ts +2 -18
  124. package/dist/src/browser/db/changeFeed.d.ts.map +1 -1
  125. package/dist/src/browser/db/drizzleFiles.d.ts +7 -2
  126. package/dist/src/browser/db/drizzleFiles.d.ts.map +1 -1
  127. package/dist/src/browser/workers/filesDownload.d.ts.map +1 -1
  128. package/dist/src/browser/workers/imageResize.d.ts.map +1 -1
  129. package/dist/src/client/ClientManager.d.ts +90 -57
  130. package/dist/src/client/ClientManager.d.ts.map +1 -1
  131. package/dist/src/client/actors/addModelsToDb.d.ts +9 -7
  132. package/dist/src/client/actors/addModelsToDb.d.ts.map +1 -1
  133. package/dist/src/client/actors/dbInit.d.ts +3 -3
  134. package/dist/src/client/actors/dbInit.d.ts.map +1 -1
  135. package/dist/src/client/actors/fileSystemInit.d.ts +3 -3
  136. package/dist/src/client/actors/fileSystemInit.d.ts.map +1 -1
  137. package/dist/src/client/actors/platformClassesInit.d.ts +10 -5
  138. package/dist/src/client/actors/platformClassesInit.d.ts.map +1 -1
  139. package/dist/src/client/actors/processSchemaFiles.d.ts +18 -3
  140. package/dist/src/client/actors/processSchemaFiles.d.ts.map +1 -1
  141. package/dist/src/client/actors/saveConfig.d.ts +6 -3
  142. package/dist/src/client/actors/saveConfig.d.ts.map +1 -1
  143. package/dist/src/client/clientManagerMachine.d.ts +22 -20
  144. package/dist/src/client/clientManagerMachine.d.ts.map +1 -1
  145. package/dist/src/client/constants.d.ts +1 -9
  146. package/dist/src/client/constants.d.ts.map +1 -1
  147. package/dist/src/client/readiness.d.ts +26 -0
  148. package/dist/src/client/readiness.d.ts.map +1 -0
  149. package/dist/src/db/read/currentPropertyRow.d.ts +61 -0
  150. package/dist/src/db/read/currentPropertyRow.d.ts.map +1 -0
  151. package/dist/src/db/read/findSeedRow.d.ts +20 -0
  152. package/dist/src/db/read/findSeedRow.d.ts.map +1 -0
  153. package/dist/src/db/read/findSeedRowsByUid.d.ts +4 -1
  154. package/dist/src/db/read/findSeedRowsByUid.d.ts.map +1 -1
  155. package/dist/src/db/read/getAttesterForSeed.d.ts +1 -1
  156. package/dist/src/db/read/getAttesterForSeed.d.ts.map +1 -1
  157. package/dist/src/db/read/getItemData.d.ts.map +1 -1
  158. package/dist/src/db/read/getItemProperties.d.ts +5 -0
  159. package/dist/src/db/read/getItemProperties.d.ts.map +1 -1
  160. package/dist/src/db/read/getMetadataAttestationUidsForSeedUid.d.ts.map +1 -1
  161. package/dist/src/db/read/getModelSchemas.d.ts +0 -7
  162. package/dist/src/db/read/getModelSchemas.d.ts.map +1 -1
  163. package/dist/src/db/read/getModelsData.d.ts.map +1 -1
  164. package/dist/src/db/read/getPropertyClear.d.ts +49 -0
  165. package/dist/src/db/read/getPropertyClear.d.ts.map +1 -0
  166. package/dist/src/db/read/getPublishPayload.d.ts +35 -40
  167. package/dist/src/db/read/getPublishPayload.d.ts.map +1 -1
  168. package/dist/src/db/read/getPublishPendingDiff.d.ts +7 -2
  169. package/dist/src/db/read/getPublishPendingDiff.d.ts.map +1 -1
  170. package/dist/src/db/read/getPublishUploads.d.ts +59 -28
  171. package/dist/src/db/read/getPublishUploads.d.ts.map +1 -1
  172. package/dist/src/db/read/getSchemaUidForModel.d.ts.map +1 -1
  173. package/dist/src/db/read/getSeedPublishState.d.ts.map +1 -1
  174. package/dist/src/db/read/getSeedPublisher.d.ts.map +1 -1
  175. package/dist/src/db/read/getVersionsForSeedUid.d.ts.map +1 -1
  176. package/dist/src/db/read/isSeedRevoked.d.ts.map +1 -1
  177. package/dist/src/db/read/localModelSchemaLookups.d.ts +8 -0
  178. package/dist/src/db/read/localModelSchemaLookups.d.ts.map +1 -0
  179. package/dist/src/db/read/metadataProperty.d.ts +104 -0
  180. package/dist/src/db/read/metadataProperty.d.ts.map +1 -0
  181. package/dist/src/db/read/publishGraph.d.ts +148 -0
  182. package/dist/src/db/read/publishGraph.d.ts.map +1 -0
  183. package/dist/src/db/read/resolveModelRecord.d.ts +26 -2
  184. package/dist/src/db/read/resolveModelRecord.d.ts.map +1 -1
  185. package/dist/src/db/read/resolveRelatedSeedRef.d.ts +1 -7
  186. package/dist/src/db/read/resolveRelatedSeedRef.d.ts.map +1 -1
  187. package/dist/src/db/read/subqueries/metadataLatest.d.ts +3 -1
  188. package/dist/src/db/read/subqueries/metadataLatest.d.ts.map +1 -1
  189. package/dist/src/db/read/summarizePublishWork.d.ts +18 -5
  190. package/dist/src/db/read/summarizePublishWork.d.ts.map +1 -1
  191. package/dist/src/db/write/applyPropertyAttestationUidsFromPublish.d.ts +20 -21
  192. package/dist/src/db/write/applyPropertyAttestationUidsFromPublish.d.ts.map +1 -1
  193. package/dist/src/db/write/createMetadata.d.ts.map +1 -1
  194. package/dist/src/db/write/createNewItem.d.ts.map +1 -1
  195. package/dist/src/db/write/createSeeds.d.ts +5 -1
  196. package/dist/src/db/write/createSeeds.d.ts.map +1 -1
  197. package/dist/src/db/write/definitions.d.ts +127 -0
  198. package/dist/src/db/write/definitions.d.ts.map +1 -0
  199. package/dist/src/db/write/markPropertyAttestationsRetracted.d.ts +11 -0
  200. package/dist/src/db/write/markPropertyAttestationsRetracted.d.ts.map +1 -0
  201. package/dist/src/db/write/mergeChainRows.d.ts +22 -0
  202. package/dist/src/db/write/mergeChainRows.d.ts.map +1 -0
  203. package/dist/src/db/write/saveDefinition.d.ts +29 -0
  204. package/dist/src/db/write/saveDefinition.d.ts.map +1 -0
  205. package/dist/src/db/write/updateItemPropertyValue.d.ts +2 -0
  206. package/dist/src/db/write/updateItemPropertyValue.d.ts.map +1 -1
  207. package/dist/src/db/write/updateSeedUid.d.ts +4 -2
  208. package/dist/src/db/write/updateSeedUid.d.ts.map +1 -1
  209. package/dist/src/db/write/updateVersionUid.d.ts +12 -4
  210. package/dist/src/db/write/updateVersionUid.d.ts.map +1 -1
  211. package/dist/src/db/write/writeModelUids.d.ts +10 -0
  212. package/dist/src/db/write/writeModelUids.d.ts.map +1 -0
  213. package/dist/src/eas.d.ts +8 -6
  214. package/dist/src/eas.d.ts.map +1 -1
  215. package/dist/src/events/files/download.d.ts.map +1 -1
  216. package/dist/src/events/item/syncDbWithEas.d.ts.map +1 -1
  217. package/dist/src/helpers/arweaveContentTags.d.ts +1 -1
  218. package/dist/src/helpers/arweaveContentTags.d.ts.map +1 -1
  219. package/dist/src/helpers/compareMetadataRowsLatestFirst.d.ts +27 -12
  220. package/dist/src/helpers/compareMetadataRowsLatestFirst.d.ts.map +1 -1
  221. package/dist/src/helpers/constants.d.ts +5 -2
  222. package/dist/src/helpers/constants.d.ts.map +1 -1
  223. package/dist/src/helpers/dataUrl.d.ts +10 -0
  224. package/dist/src/helpers/dataUrl.d.ts.map +1 -0
  225. package/dist/src/helpers/db.d.ts +54 -105
  226. package/dist/src/helpers/db.d.ts.map +1 -1
  227. package/dist/src/helpers/ensureImageLocal.d.ts.map +1 -1
  228. package/dist/src/helpers/entity/contextUpdate.d.ts +20 -0
  229. package/dist/src/helpers/entity/contextUpdate.d.ts.map +1 -0
  230. package/dist/src/helpers/entity/draftSaveErrors.d.ts +5 -0
  231. package/dist/src/helpers/entity/draftSaveErrors.d.ts.map +1 -1
  232. package/dist/src/helpers/entity/entityCache.d.ts +65 -3
  233. package/dist/src/helpers/entity/entityCache.d.ts.map +1 -1
  234. package/dist/src/helpers/entity/entityCommon.d.ts +0 -22
  235. package/dist/src/helpers/entity/entityCommon.d.ts.map +1 -1
  236. package/dist/src/helpers/entity/entityLifecycle.d.ts +75 -0
  237. package/dist/src/helpers/entity/entityLifecycle.d.ts.map +1 -0
  238. package/dist/src/helpers/entity/entityLiveQuery.d.ts +1 -23
  239. package/dist/src/helpers/entity/entityLiveQuery.d.ts.map +1 -1
  240. package/dist/src/helpers/htmlEmbeddedDataUriPublish.d.ts +4 -9
  241. package/dist/src/helpers/htmlEmbeddedDataUriPublish.d.ts.map +1 -1
  242. package/dist/src/helpers/index.d.ts +2 -24
  243. package/dist/src/helpers/index.d.ts.map +1 -1
  244. package/dist/src/helpers/localImageBlobUrl.d.ts +7 -0
  245. package/dist/src/helpers/localImageBlobUrl.d.ts.map +1 -0
  246. package/dist/src/helpers/mediaRef.d.ts +3 -70
  247. package/dist/src/helpers/mediaRef.d.ts.map +1 -1
  248. package/dist/src/helpers/metadataPropertyNames.d.ts +34 -21
  249. package/dist/src/helpers/metadataPropertyNames.d.ts.map +1 -1
  250. package/dist/src/helpers/mimeTypes.d.ts +2 -1
  251. package/dist/src/helpers/mimeTypes.d.ts.map +1 -1
  252. package/dist/src/helpers/model.d.ts.map +1 -1
  253. package/dist/src/helpers/ownership.d.ts.map +1 -1
  254. package/dist/src/helpers/property.d.ts +21 -26
  255. package/dist/src/helpers/property.d.ts.map +1 -1
  256. package/dist/src/helpers/propertyDefinition.d.ts +59 -0
  257. package/dist/src/helpers/propertyDefinition.d.ts.map +1 -0
  258. package/dist/src/helpers/readHtmlBodyForStorageSeed.d.ts.map +1 -1
  259. package/dist/src/helpers/relationSeedRef.d.ts +0 -7
  260. package/dist/src/helpers/relationSeedRef.d.ts.map +1 -1
  261. package/dist/src/helpers/schema.d.ts +24 -38
  262. package/dist/src/helpers/schema.d.ts.map +1 -1
  263. package/dist/src/helpers/schemaApply.d.ts +135 -0
  264. package/dist/src/helpers/schemaApply.d.ts.map +1 -0
  265. package/dist/src/helpers/tabEvents.d.ts.map +1 -1
  266. package/dist/src/helpers/updateSchema.d.ts +2 -18
  267. package/dist/src/helpers/updateSchema.d.ts.map +1 -1
  268. package/dist/src/imports/index.d.ts +1 -1
  269. package/dist/src/imports/index.d.ts.map +1 -1
  270. package/dist/src/imports/json.d.ts +8 -78
  271. package/dist/src/imports/json.d.ts.map +1 -1
  272. package/dist/src/index.d.ts +16 -14
  273. package/dist/src/index.d.ts.map +1 -1
  274. package/dist/src/interfaces/IItem.d.ts +5 -0
  275. package/dist/src/interfaces/IItem.d.ts.map +1 -1
  276. package/dist/src/interfaces/IItemProperty.d.ts +13 -7
  277. package/dist/src/interfaces/IItemProperty.d.ts.map +1 -1
  278. package/dist/src/node/db/Db.d.ts.map +1 -1
  279. package/dist/src/node/index.d.ts +3 -2
  280. package/dist/src/node/index.d.ts.map +1 -1
  281. package/dist/src/outbox/conflicts.d.ts +2 -0
  282. package/dist/src/outbox/conflicts.d.ts.map +1 -1
  283. package/dist/src/outbox/itemAppliers.d.ts +70 -14
  284. package/dist/src/outbox/itemAppliers.d.ts.map +1 -1
  285. package/dist/src/outbox/outbox.d.ts +14 -3
  286. package/dist/src/outbox/outbox.d.ts.map +1 -1
  287. package/dist/src/outbox/pendingValues.d.ts +3 -1
  288. package/dist/src/outbox/pendingValues.d.ts.map +1 -1
  289. package/dist/src/outbox/storageValueRow.d.ts +7 -0
  290. package/dist/src/outbox/storageValueRow.d.ts.map +1 -0
  291. package/dist/src/platform/configurePlatform.d.ts.map +1 -1
  292. package/dist/src/platform/index.browser.d.ts.map +1 -1
  293. package/dist/src/platform/index.node.d.ts.map +1 -1
  294. package/dist/src/seedSchema/MetadataSchema.d.ts +17 -0
  295. package/dist/src/seedSchema/MetadataSchema.d.ts.map +1 -1
  296. package/dist/src/seedSchema/ModelSchema.d.ts +19 -0
  297. package/dist/src/seedSchema/ModelSchema.d.ts.map +1 -1
  298. package/dist/src/seedSchema/ModelSchemaSchema.d.ts.map +1 -1
  299. package/dist/src/seedSchema/PendingWriteSchema.d.ts.map +1 -1
  300. package/dist/src/seedSchema/SchemaSchema.d.ts +19 -0
  301. package/dist/src/seedSchema/SchemaSchema.d.ts.map +1 -1
  302. package/dist/src/seedSchema/SeedSchema.d.ts.map +1 -1
  303. package/dist/src/seedSchema/VersionSchema.d.ts.map +1 -1
  304. package/dist/src/seedSchema/attestedUid.d.ts +9 -0
  305. package/dist/src/seedSchema/attestedUid.d.ts.map +1 -0
  306. package/dist/src/storageValue/content.d.ts +45 -0
  307. package/dist/src/storageValue/content.d.ts.map +1 -0
  308. package/dist/src/storageValue/findFile.d.ts +7 -0
  309. package/dist/src/storageValue/findFile.d.ts.map +1 -0
  310. package/dist/src/storageValue/resolve.d.ts +71 -0
  311. package/dist/src/storageValue/resolve.d.ts.map +1 -0
  312. package/dist/src/storageValue/save.d.ts +24 -0
  313. package/dist/src/storageValue/save.d.ts.map +1 -0
  314. package/dist/src/storageValue/stage.d.ts +22 -0
  315. package/dist/src/storageValue/stage.d.ts.map +1 -0
  316. package/dist/src/types/db.d.ts +0 -14
  317. package/dist/src/types/db.d.ts.map +1 -1
  318. package/dist/src/types/index.d.ts +3 -3
  319. package/dist/src/types/index.d.ts.map +1 -1
  320. package/dist/src/types/machines.d.ts +1 -1
  321. package/dist/src/types/machines.d.ts.map +1 -1
  322. package/dist/src/types/property.d.ts +9 -16
  323. package/dist/src/types/property.d.ts.map +1 -1
  324. package/dist/src/types/publish.d.ts +1 -1
  325. package/dist/src/types/publish.d.ts.map +1 -1
  326. package/dist/{syncDbWithEas-DUu_wjUG.js → syncDbWithEas-CA7PvBgu.js} +142 -168
  327. package/dist/syncDbWithEas-CA7PvBgu.js.map +1 -0
  328. package/dist/tabEvents-CaVmngqr.js +3804 -0
  329. package/dist/tabEvents-CaVmngqr.js.map +1 -0
  330. package/dist/{tolerantWebAccess-DecKDGen.js → tolerantWebAccess-BJqViq08.js} +10 -15
  331. package/dist/{tolerantWebAccess-DecKDGen.js.map → tolerantWebAccess-BJqViq08.js.map} +1 -1
  332. package/dist/{resolveModelForSyncedSeed-4gO9Hfn2.js → updateSeedRevokedAt-BL9Bg-G7.js} +30 -4
  333. package/dist/updateSeedRevokedAt-BL9Bg-G7.js.map +1 -0
  334. package/package.json +6 -6
  335. package/dist/ClientManager-GZfXErDX.js +0 -5279
  336. package/dist/ClientManager-GZfXErDX.js.map +0 -1
  337. package/dist/Item-0oDoTPhi.js.map +0 -1
  338. package/dist/ModelProperty-i5flwzQG.js.map +0 -1
  339. package/dist/Schema-PFZwG9Nl.js +0 -4370
  340. package/dist/Schema-PFZwG9Nl.js.map +0 -1
  341. package/dist/backfillMetadataPropertyIds-BbxgavEQ.js +0 -72
  342. package/dist/backfillMetadataPropertyIds-BbxgavEQ.js.map +0 -1
  343. package/dist/changeFeed-DKdVlUmt.js +0 -726
  344. package/dist/changeFeed-DKdVlUmt.js.map +0 -1
  345. package/dist/configurePlatform-CrMafpMW.js.map +0 -1
  346. package/dist/dist/ClientManager-GZfXErDX.d.ts +0 -952
  347. package/dist/dist/ClientManager-GZfXErDX.d.ts.map +0 -1
  348. package/dist/dist/Item-0oDoTPhi.d.ts +0 -1076
  349. package/dist/dist/Item-0oDoTPhi.d.ts.map +0 -1
  350. package/dist/dist/ModelProperty-i5flwzQG.d.ts +0 -315
  351. package/dist/dist/ModelProperty-i5flwzQG.d.ts.map +0 -1
  352. package/dist/dist/Schema-PFZwG9Nl.d.ts +0 -432
  353. package/dist/dist/Schema-PFZwG9Nl.d.ts.map +0 -1
  354. package/dist/dist/SchemaValidationService-DnGKel4V.d.ts +0 -118
  355. package/dist/dist/SchemaValidationService-DnGKel4V.d.ts.map +0 -1
  356. package/dist/dist/backfillMetadataPropertyIds-BbxgavEQ.d.ts +0 -7
  357. package/dist/dist/backfillMetadataPropertyIds-BbxgavEQ.d.ts.map +0 -1
  358. package/dist/dist/changeFeed-DKdVlUmt.d.ts +0 -160
  359. package/dist/dist/changeFeed-DKdVlUmt.d.ts.map +0 -1
  360. package/dist/dist/configurePlatform-CrMafpMW.d.ts.map +0 -1
  361. package/dist/dist/detachRowsBeforeDelete-DXFYSHRR.d.ts +0 -13
  362. package/dist/dist/detachRowsBeforeDelete-DXFYSHRR.d.ts.map +0 -1
  363. package/dist/dist/eas-dyepi5jz.d.ts +0 -12
  364. package/dist/dist/eas-dyepi5jz.d.ts.map +0 -1
  365. package/dist/dist/errors-C1qUEZ7A.d.ts +0 -25
  366. package/dist/dist/errors-C1qUEZ7A.d.ts.map +0 -1
  367. package/dist/dist/errors-D1zHAqjr.d.ts +0 -515
  368. package/dist/dist/errors-D1zHAqjr.d.ts.map +0 -1
  369. package/dist/dist/eventBus-7vxTCc3a.d.ts +0 -18
  370. package/dist/dist/eventBus-7vxTCc3a.d.ts.map +0 -1
  371. package/dist/dist/findSeedRowsByUid-CT3sOXXw.d.ts +0 -13
  372. package/dist/dist/findSeedRowsByUid-CT3sOXXw.d.ts.map +0 -1
  373. package/dist/dist/getItem-Bhjecbia.d.ts +0 -6
  374. package/dist/dist/getItem-Bhjecbia.d.ts.map +0 -1
  375. package/dist/dist/getPublishPayload-DH4rMqXG.d.ts +0 -47
  376. package/dist/dist/getPublishPayload-DH4rMqXG.d.ts.map +0 -1
  377. package/dist/dist/getPublishUploads-BzUIDA2G.d.ts +0 -89
  378. package/dist/dist/getPublishUploads-BzUIDA2G.d.ts.map +0 -1
  379. package/dist/dist/index-CkYb3wcU.d.ts +0 -14
  380. package/dist/dist/index-CkYb3wcU.d.ts.map +0 -1
  381. package/dist/dist/index-DJmWkVIK.d.ts +0 -6
  382. package/dist/dist/index-DJmWkVIK.d.ts.map +0 -1
  383. package/dist/dist/index-Dx_GTdyQ.d.ts.map +0 -1
  384. package/dist/dist/index.browser-i9uPJice.d.ts +0 -9908
  385. package/dist/dist/index.browser-i9uPJice.d.ts.map +0 -1
  386. package/dist/dist/mimeTypes-C9r76DCH.d.ts +0 -30
  387. package/dist/dist/mimeTypes-C9r76DCH.d.ts.map +0 -1
  388. package/dist/dist/model-oHA3YhOd.d.ts +0 -6
  389. package/dist/dist/model-oHA3YhOd.d.ts.map +0 -1
  390. package/dist/dist/modelPropertyRecordSchemas-C5lW9aA-.d.ts +0 -7
  391. package/dist/dist/modelPropertyRecordSchemas-C5lW9aA-.d.ts.map +0 -1
  392. package/dist/dist/platform.browser.d.ts +0 -3
  393. package/dist/dist/platform.browser.d.ts.map +0 -1
  394. package/dist/dist/property-BwYtg5Q_.d.ts +0 -37
  395. package/dist/dist/property-BwYtg5Q_.d.ts.map +0 -1
  396. package/dist/dist/propertyValidationRules-CyHdpcPP.d.ts +0 -13
  397. package/dist/dist/propertyValidationRules-CyHdpcPP.d.ts.map +0 -1
  398. package/dist/dist/publishDraftGraph-C5F6b7GK.d.ts +0 -93
  399. package/dist/dist/publishDraftGraph-C5F6b7GK.d.ts.map +0 -1
  400. package/dist/dist/resolveModelForSyncedSeed-4gO9Hfn2.d.ts +0 -21
  401. package/dist/dist/resolveModelForSyncedSeed-4gO9Hfn2.d.ts.map +0 -1
  402. package/dist/dist/syncDbWithEas-DUu_wjUG.d.ts +0 -13
  403. package/dist/dist/syncDbWithEas-DUu_wjUG.d.ts.map +0 -1
  404. package/dist/dist/tolerantWebAccess-DecKDGen.d.ts +0 -113
  405. package/dist/dist/tolerantWebAccess-DecKDGen.d.ts.map +0 -1
  406. package/dist/dist/updateSeedRevokedAt-CP606vKs.d.ts +0 -11
  407. package/dist/dist/updateSeedRevokedAt-CP606vKs.d.ts.map +0 -1
  408. package/dist/eas-dyepi5jz.js +0 -67
  409. package/dist/eas-dyepi5jz.js.map +0 -1
  410. package/dist/errors-D1zHAqjr.js +0 -4006
  411. package/dist/errors-D1zHAqjr.js.map +0 -1
  412. package/dist/eventBus-7vxTCc3a.js +0 -64
  413. package/dist/eventBus-7vxTCc3a.js.map +0 -1
  414. package/dist/findSeedRowsByUid-CT3sOXXw.js +0 -47
  415. package/dist/findSeedRowsByUid-CT3sOXXw.js.map +0 -1
  416. package/dist/getItem-Bhjecbia.js +0 -45
  417. package/dist/getItem-Bhjecbia.js.map +0 -1
  418. package/dist/getPublishPayload-DH4rMqXG.js +0 -1634
  419. package/dist/getPublishPayload-DH4rMqXG.js.map +0 -1
  420. package/dist/getPublishUploads-BzUIDA2G.js +0 -1080
  421. package/dist/getPublishUploads-BzUIDA2G.js.map +0 -1
  422. package/dist/index-CkYb3wcU.js +0 -29
  423. package/dist/index-CkYb3wcU.js.map +0 -1
  424. package/dist/index-DJmWkVIK.js.map +0 -1
  425. package/dist/index-Dx_GTdyQ.js +0 -4745
  426. package/dist/index-Dx_GTdyQ.js.map +0 -1
  427. package/dist/index-PbnKw2es.js.map +0 -1
  428. package/dist/index.browser-i9uPJice.js.map +0 -1
  429. package/dist/index.node-CzMwMr_d.js.map +0 -1
  430. package/dist/mimeTypes-C9r76DCH.js +0 -209
  431. package/dist/mimeTypes-C9r76DCH.js.map +0 -1
  432. package/dist/model-oHA3YhOd.js +0 -29
  433. package/dist/model-oHA3YhOd.js.map +0 -1
  434. package/dist/modelPropertyRecordSchemas-C5lW9aA-.js +0 -94
  435. package/dist/modelPropertyRecordSchemas-C5lW9aA-.js.map +0 -1
  436. package/dist/property-BwYtg5Q_.js +0 -289
  437. package/dist/property-BwYtg5Q_.js.map +0 -1
  438. package/dist/propertyValidationRules-CyHdpcPP.js +0 -73
  439. package/dist/propertyValidationRules-CyHdpcPP.js.map +0 -1
  440. package/dist/publishDraftGraph-C5F6b7GK.js +0 -505
  441. package/dist/publishDraftGraph-C5F6b7GK.js.map +0 -1
  442. package/dist/resolveModelForSyncedSeed-4gO9Hfn2.js.map +0 -1
  443. package/dist/src/Item/service/actors/loadOrCreateItem.d.ts +0 -5
  444. package/dist/src/Item/service/actors/loadOrCreateItem.d.ts.map +0 -1
  445. package/dist/src/Item/service/actors/waitForDb.d.ts +0 -4
  446. package/dist/src/Item/service/actors/waitForDb.d.ts.map +0 -1
  447. package/dist/src/ItemProperty/service/actors/loadOrCreateProperty.d.ts +0 -5
  448. package/dist/src/ItemProperty/service/actors/loadOrCreateProperty.d.ts.map +0 -1
  449. package/dist/src/ItemProperty/service/actors/saveValueToDb/analyzeInput.d.ts +0 -5
  450. package/dist/src/ItemProperty/service/actors/saveValueToDb/analyzeInput.d.ts.map +0 -1
  451. package/dist/src/ItemProperty/service/actors/saveValueToDb/index.d.ts +0 -6
  452. package/dist/src/ItemProperty/service/actors/saveValueToDb/index.d.ts.map +0 -1
  453. package/dist/src/ItemProperty/service/actors/saveValueToDb/rowContext.d.ts.map +0 -1
  454. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveFile.d.ts +0 -24
  455. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveFile.d.ts.map +0 -1
  456. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveHtml.d.ts +0 -5
  457. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveHtml.d.ts.map +0 -1
  458. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveImage.d.ts +0 -5
  459. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveImage.d.ts.map +0 -1
  460. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveItemStorage.d.ts +0 -10
  461. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveItemStorage.d.ts.map +0 -1
  462. package/dist/src/ItemProperty/service/actors/saveValueToDb/storageValue.d.ts +0 -22
  463. package/dist/src/ItemProperty/service/actors/saveValueToDb/storageValue.d.ts.map +0 -1
  464. package/dist/src/ItemProperty/service/actors/waitForDb.d.ts +0 -4
  465. package/dist/src/ItemProperty/service/actors/waitForDb.d.ts.map +0 -1
  466. package/dist/src/Model/service/actors/createModelProperties.d.ts +0 -9
  467. package/dist/src/Model/service/actors/createModelProperties.d.ts.map +0 -1
  468. package/dist/src/Model/service/actors/loadOrCreateModel.d.ts +0 -5
  469. package/dist/src/Model/service/actors/loadOrCreateModel.d.ts.map +0 -1
  470. package/dist/src/Schema/service/actors/checkExistingSchema.d.ts +0 -5
  471. package/dist/src/Schema/service/actors/checkExistingSchema.d.ts.map +0 -1
  472. package/dist/src/Schema/service/actors/createModelInstances.d.ts +0 -11
  473. package/dist/src/Schema/service/actors/createModelInstances.d.ts.map +0 -1
  474. package/dist/src/Schema/service/actors/createPropertyInstances.d.ts +0 -12
  475. package/dist/src/Schema/service/actors/createPropertyInstances.d.ts.map +0 -1
  476. package/dist/src/Schema/service/actors/validateSchema.d.ts +0 -5
  477. package/dist/src/Schema/service/actors/validateSchema.d.ts.map +0 -1
  478. package/dist/src/Schema/service/actors/verifyModelInstancesInCache.d.ts +0 -6
  479. package/dist/src/Schema/service/actors/verifyModelInstancesInCache.d.ts.map +0 -1
  480. package/dist/src/Schema/service/actors/verifyModelsInDb.d.ts +0 -7
  481. package/dist/src/Schema/service/actors/verifyModelsInDb.d.ts.map +0 -1
  482. package/dist/src/Schema/service/actors/verifyPropertiesInDb.d.ts +0 -8
  483. package/dist/src/Schema/service/actors/verifyPropertiesInDb.d.ts.map +0 -1
  484. package/dist/src/Schema/service/actors/verifyPropertyInstancesInCache.d.ts +0 -6
  485. package/dist/src/Schema/service/actors/verifyPropertyInstancesInCache.d.ts.map +0 -1
  486. package/dist/src/Schema/service/actors/verifySchemaInDb.d.ts +0 -7
  487. package/dist/src/Schema/service/actors/verifySchemaInDb.d.ts.map +0 -1
  488. package/dist/src/Schema/service/actors/writeModelsToDb.d.ts +0 -9
  489. package/dist/src/Schema/service/actors/writeModelsToDb.d.ts.map +0 -1
  490. package/dist/src/Schema/service/actors/writePropertiesToDb.d.ts +0 -10
  491. package/dist/src/Schema/service/actors/writePropertiesToDb.d.ts.map +0 -1
  492. package/dist/src/Schema/service/actors/writeSchemaToDb.d.ts +0 -11
  493. package/dist/src/Schema/service/actors/writeSchemaToDb.d.ts.map +0 -1
  494. package/dist/src/browser/helpers/ArweaveClient.d.ts +0 -14
  495. package/dist/src/browser/helpers/ArweaveClient.d.ts.map +0 -1
  496. package/dist/src/browser/helpers/EasClient.d.ts +0 -10
  497. package/dist/src/browser/helpers/EasClient.d.ts.map +0 -1
  498. package/dist/src/browser/helpers/QueryClient.d.ts +0 -9
  499. package/dist/src/browser/helpers/QueryClient.d.ts.map +0 -1
  500. package/dist/src/client/actors/addModelsToStore.d.ts +0 -4
  501. package/dist/src/client/actors/addModelsToStore.d.ts.map +0 -1
  502. package/dist/src/db/backfillMetadataPropertyIds.d.ts +0 -7
  503. package/dist/src/db/backfillMetadataPropertyIds.d.ts.map +0 -1
  504. package/dist/src/db/read/getItem.d.ts +0 -3
  505. package/dist/src/db/read/getItem.d.ts.map +0 -1
  506. package/dist/src/db/read/getPropertyData.d.ts +0 -4
  507. package/dist/src/db/read/getPropertyData.d.ts.map +0 -1
  508. package/dist/src/db/read/getRelatedItemsForPublish.d.ts +0 -10
  509. package/dist/src/db/read/getRelatedItemsForPublish.d.ts.map +0 -1
  510. package/dist/src/db/read/getSeedData.d.ts +0 -12
  511. package/dist/src/db/read/getSeedData.d.ts.map +0 -1
  512. package/dist/src/db/read/itemLoads.d.ts +0 -20
  513. package/dist/src/db/read/itemLoads.d.ts.map +0 -1
  514. package/dist/src/db/read/publishDraftGraph.d.ts +0 -64
  515. package/dist/src/db/read/publishDraftGraph.d.ts.map +0 -1
  516. package/dist/src/db/write/linkModelToSchema.d.ts +0 -12
  517. package/dist/src/db/write/linkModelToSchema.d.ts.map +0 -1
  518. package/dist/src/helpers/ArweaveClient/BaseArweaveClient.d.ts +0 -2
  519. package/dist/src/helpers/ArweaveClient/BaseArweaveClient.d.ts.map +0 -1
  520. package/dist/src/helpers/ArweaveClient/arweaveL1UploadApi.d.ts +0 -2
  521. package/dist/src/helpers/ArweaveClient/arweaveL1UploadApi.d.ts.map +0 -1
  522. package/dist/src/helpers/ArweaveClient/selectReadGateway.d.ts +0 -2
  523. package/dist/src/helpers/ArweaveClient/selectReadGateway.d.ts.map +0 -1
  524. package/dist/src/helpers/ArweaveClient/uploadApiVerification.d.ts +0 -2
  525. package/dist/src/helpers/ArweaveClient/uploadApiVerification.d.ts.map +0 -1
  526. package/dist/src/helpers/EasClient/BaseEasClient.d.ts +0 -2
  527. package/dist/src/helpers/EasClient/BaseEasClient.d.ts.map +0 -1
  528. package/dist/src/helpers/QueryClient/BaseQueryClient.d.ts +0 -2
  529. package/dist/src/helpers/QueryClient/BaseQueryClient.d.ts.map +0 -1
  530. package/dist/src/helpers/easUid.d.ts +0 -2
  531. package/dist/src/helpers/easUid.d.ts.map +0 -1
  532. package/dist/src/helpers/entity/entityDestroy.d.ts +0 -49
  533. package/dist/src/helpers/entity/entityDestroy.d.ts.map +0 -1
  534. package/dist/src/helpers/entity/entityFind.d.ts +0 -52
  535. package/dist/src/helpers/entity/entityFind.d.ts.map +0 -1
  536. package/dist/src/helpers/entity/entityState.d.ts +0 -28
  537. package/dist/src/helpers/entity/entityState.d.ts.map +0 -1
  538. package/dist/src/helpers/entity/entityUnload.d.ts +0 -56
  539. package/dist/src/helpers/entity/entityUnload.d.ts.map +0 -1
  540. package/dist/src/helpers/entity/evictionEpoch.d.ts +0 -43
  541. package/dist/src/helpers/entity/evictionEpoch.d.ts.map +0 -1
  542. package/dist/src/helpers/entity/index.d.ts +0 -12
  543. package/dist/src/helpers/entity/index.d.ts.map +0 -1
  544. package/dist/src/helpers/gateway/gatewayState.d.ts +0 -2
  545. package/dist/src/helpers/gateway/gatewayState.d.ts.map +0 -1
  546. package/dist/src/helpers/gateway/resolveSeedGatewayEndpoints.d.ts +0 -2
  547. package/dist/src/helpers/gateway/resolveSeedGatewayEndpoints.d.ts.map +0 -1
  548. package/dist/src/helpers/gateway/seedGatewayConfig.d.ts +0 -2
  549. package/dist/src/helpers/gateway/seedGatewayConfig.d.ts.map +0 -1
  550. package/dist/src/helpers/getSchemaForItemProperty.d.ts +0 -15
  551. package/dist/src/helpers/getSchemaForItemProperty.d.ts.map +0 -1
  552. package/dist/src/helpers/getSegmentedItemProperties.d.ts +0 -10
  553. package/dist/src/helpers/getSegmentedItemProperties.d.ts.map +0 -1
  554. package/dist/src/helpers/isSqliteUniqueConstraintError.d.ts +0 -7
  555. package/dist/src/helpers/isSqliteUniqueConstraintError.d.ts.map +0 -1
  556. package/dist/src/helpers/propertyValidationRules.d.ts +0 -20
  557. package/dist/src/helpers/propertyValidationRules.d.ts.map +0 -1
  558. package/dist/src/helpers/propertyWriteTickets.d.ts +0 -11
  559. package/dist/src/helpers/propertyWriteTickets.d.ts.map +0 -1
  560. package/dist/src/helpers/reconcilePropertyDraftRows.d.ts +0 -10
  561. package/dist/src/helpers/reconcilePropertyDraftRows.d.ts.map +0 -1
  562. package/dist/src/helpers/waitForEntityIdle.d.ts +0 -21
  563. package/dist/src/helpers/waitForEntityIdle.d.ts.map +0 -1
  564. package/dist/src/interfaces/IQueryClient.d.ts +0 -15
  565. package/dist/src/interfaces/IQueryClient.d.ts.map +0 -1
  566. package/dist/src/node/helpers/ArweaveClient.d.ts +0 -5
  567. package/dist/src/node/helpers/ArweaveClient.d.ts.map +0 -1
  568. package/dist/src/node/helpers/EasClient.d.ts +0 -5
  569. package/dist/src/node/helpers/EasClient.d.ts.map +0 -1
  570. package/dist/src/node/helpers/QueryClient.d.ts +0 -5
  571. package/dist/src/node/helpers/QueryClient.d.ts.map +0 -1
  572. package/dist/src/services/write/actors/validateEntity.d.ts +0 -6
  573. package/dist/src/services/write/actors/validateEntity.d.ts.map +0 -1
  574. package/dist/src/services/write/actors/writeToDatabase.d.ts +0 -14
  575. package/dist/src/services/write/actors/writeToDatabase.d.ts.map +0 -1
  576. package/dist/src/services/write/writeProcessMachine.d.ts +0 -56
  577. package/dist/src/services/write/writeProcessMachine.d.ts.map +0 -1
  578. package/dist/src/stores/eas.d.ts +0 -2
  579. package/dist/src/stores/eas.d.ts.map +0 -1
  580. package/dist/src/types/arweave.d.ts +0 -57
  581. package/dist/src/types/arweave.d.ts.map +0 -1
  582. package/dist/src/types/gateway.d.ts +0 -2
  583. package/dist/src/types/gateway.d.ts.map +0 -1
  584. package/dist/syncDbWithEas-DUu_wjUG.js.map +0 -1
  585. package/dist/updateSeedRevokedAt-CP606vKs.js +0 -31
  586. package/dist/updateSeedRevokedAt-CP606vKs.js.map +0 -1
@@ -1,4370 +0,0 @@
1
- import { fromCallback, setup, assign, createActor } from 'xstate';
2
- import { L as BaseDb, aq as SEED_PROTOCOL_SCHEMA_NAME, J as schemas, Q as BaseFileManager, aL as loadModelsFromDbForSchema, aM as mergeDbModelsIntoSchemaModels, aF as isInternalSchema, p as models, z as modelSchemas, aN as addSchemaToDb, ad as generateId, i as isDataType, M as ModelPropertyDataTypes, aO as addModelsToDb, E as properties, aP as clearNameMatchLevels } from './index-Dx_GTdyQ.js';
3
- import { SchemaValidationService } from './SchemaValidationService-DnGKel4V.js';
4
- import { A as listCompleteSchemaFiles, N as getLatestSchemaVersion, O as createRowsWrittenSignal, P as createModelsFromJson, l as loadSchemaFromFile, Q as jsonPropertiesToSchemaProperties, B as loadAllSchemasFromDb, v as getClient } from './ClientManager-GZfXErDX.js';
5
- import { eq, desc, and } from 'drizzle-orm';
6
- import debug from 'debug';
7
- import { c as currentEvictionEpoch, M as Model, g as getDraftSaveError, C as ConflictError, r as recordSchemaDestroy, f as finishSchemaDestroy, e as setupEntityLiveQuery, t as trackDraftSave } from './errors-D1zHAqjr.js';
8
- import 'pluralize';
9
- import '@seedprotocol/eas';
10
- import { aj as ActorKeyedState, N as DEFAULT_READY_TIMEOUT_MS, B as waitForEntityIdle, P as createReactiveProxy, R as findEntity, W as ClientManagerState, ak as clearDestroySubscriptions, S as forceRemoveFromCaches, al as unloadEntity, T as runDestroyLifecycle } from './Item-0oDoTPhi.js';
11
-
12
- const validationService = new SchemaValidationService();
13
- // Validation timeout in milliseconds (10 seconds)
14
- const VALIDATION_TIMEOUT_MS = 10000;
15
- const validateSchema = fromCallback(({ sendBack, input: { context } }) => {
16
- let hasResponded = false;
17
- // Timeout handler to ensure we always respond
18
- const timeoutId = setTimeout(() => {
19
- if (!hasResponded) {
20
- hasResponded = true;
21
- sendBack({
22
- type: 'validationError',
23
- errors: [{
24
- field: 'schema',
25
- message: `Schema validation timed out after ${VALIDATION_TIMEOUT_MS}ms`,
26
- code: 'validation_timeout',
27
- severity: 'error',
28
- }],
29
- });
30
- }
31
- }, VALIDATION_TIMEOUT_MS);
32
- // Run validation and ensure event is always sent back
33
- // Use Promise.resolve().then() to ensure async event sending
34
- // This prevents issues where synchronous validation might not trigger XState properly
35
- Promise.resolve().then(() => {
36
- try {
37
- const result = validationService.validateSchema(context);
38
- if (!hasResponded) {
39
- hasResponded = true;
40
- clearTimeout(timeoutId);
41
- if (result.isValid) {
42
- sendBack({ type: 'validationSuccess', errors: [] });
43
- }
44
- else {
45
- sendBack({ type: 'validationError', errors: result.errors });
46
- }
47
- }
48
- }
49
- catch (error) {
50
- if (!hasResponded) {
51
- hasResponded = true;
52
- clearTimeout(timeoutId);
53
- // Ensure error is always sent back
54
- sendBack({
55
- type: 'validationError',
56
- errors: [{
57
- field: 'schema',
58
- message: error instanceof Error ? error.message : 'Unknown validation error',
59
- code: 'validation_exception',
60
- severity: 'error',
61
- }],
62
- });
63
- }
64
- }
65
- });
66
- return () => {
67
- // Cleanup function - clear timeout if actor is stopped
68
- clearTimeout(timeoutId);
69
- };
70
- });
71
-
72
- const logger$c = debug('seedSdk:schema:actors:checkExistingSchema');
73
- /**
74
- * Query model IDs (schemaFileId) from database for a given schema
75
- */
76
- const getModelIdsForSchema = async (schemaId) => {
77
- const db = BaseDb.getAppDb();
78
- if (!db) {
79
- logger$c('Database not available, cannot query model IDs');
80
- return [];
81
- }
82
- try {
83
- const modelRecords = await db
84
- .select({
85
- modelFileId: models.schemaFileId,
86
- })
87
- .from(modelSchemas)
88
- .innerJoin(models, eq(modelSchemas.modelId, models.id))
89
- .where(eq(modelSchemas.schemaId, schemaId));
90
- const modelIds = modelRecords
91
- .map((row) => row.modelFileId)
92
- .filter((id) => id !== null && id !== undefined);
93
- logger$c(`Found ${modelIds.length} model IDs for schema (id: ${schemaId}): ${modelIds.join(', ')}`);
94
- return modelIds;
95
- }
96
- catch (error) {
97
- logger$c(`Error querying model IDs for schema ${schemaId}:`, error);
98
- return [];
99
- }
100
- };
101
- /**
102
- * Get the full file path for a schema
103
- */
104
- const getSchemaFilePath$1 = (name, version, schemaFileId) => {
105
- const path = BaseFileManager.getPathModule();
106
- const sanitizeSchemaName = (name) => {
107
- return name
108
- .replace(/[^a-zA-Z0-9\s_-]/g, '_')
109
- .replace(/\s+/g, '_')
110
- .replace(/^_+|_+$/g, '')
111
- .replace(/_+/g, '_');
112
- };
113
- const sanitizedName = sanitizeSchemaName(name);
114
- const filename = `${schemaFileId}_${sanitizedName}_v${version}.json`;
115
- const workingDir = BaseFileManager.getWorkingDir();
116
- return path.join(workingDir, filename);
117
- };
118
- /**
119
- * Create Model instances for all model IDs to ensure they're cached
120
- */
121
- const createModelInstances$1 = async (modelIds, evictionEpoch) => {
122
- if (modelIds.length === 0) {
123
- return;
124
- }
125
- try {
126
- const _mod_0 = await import('./errors-D1zHAqjr.js');
127
- const _ns_0 = _mod_0.i;
128
- const mod = _ns_0;
129
- const Model = mod?.Model ?? mod?.default;
130
- if (!Model) {
131
- logger$c('Model not available from dynamic import');
132
- return;
133
- }
134
- const createPromises = modelIds.map(async (modelFileId) => {
135
- try {
136
- // Not if the schema was evicted (Schema.destroy, test cleanup) since this check started
137
- const schemaHold = Model.takeSchemaHold(modelFileId);
138
- const model = await Model.createById(modelFileId, { evictionEpoch, ...(!schemaHold && { hold: false }) });
139
- if (!model && schemaHold)
140
- Model.dropSchemaHold(modelFileId);
141
- if (model) {
142
- logger$c(`Created/cached Model instance for modelFileId "${modelFileId}"`);
143
- }
144
- }
145
- catch (error) {
146
- logger$c(`Error creating Model instance for modelFileId "${modelFileId}": ${error}`);
147
- }
148
- });
149
- await Promise.all(createPromises);
150
- logger$c(`Finished creating/caching ${modelIds.length} Model instances`);
151
- }
152
- catch (error) {
153
- logger$c(`Error in createModelInstances: ${error}`);
154
- }
155
- };
156
- const checkExistingSchema = fromCallback(({ sendBack, input: { context } }) => {
157
- const evictionEpoch = currentEvictionEpoch();
158
- const _check = async () => {
159
- const { schemaName } = context;
160
- if (!schemaName) {
161
- sendBack({
162
- type: 'schemaNotFound',
163
- });
164
- return;
165
- }
166
- const db = BaseDb.getAppDb();
167
- if (!db) {
168
- sendBack({
169
- type: 'schemaNotFound',
170
- });
171
- return;
172
- }
173
- // Check if this is an internal SDK schema (Seed Protocol) — use static import so consumer bundles resolve correctly
174
- const isInternal = isInternalSchema(schemaName);
175
- if (isInternal && schemaName === SEED_PROTOCOL_SCHEMA_NAME) {
176
- // For Seed Protocol, check if it exists in database
177
- try {
178
- const _mod_1 = await import('./ClientManager-GZfXErDX.js');
179
- const _ns_1 = _mod_1.T;
180
- const internalSchema = _ns_1;
181
- const schemaFile = internalSchema.default;
182
- if (db && schemaFile.id) {
183
- const existing = await db
184
- .select()
185
- .from(schemas)
186
- .where(eq(schemas.schemaFileId, schemaFile.id))
187
- .limit(1);
188
- if (existing.length > 0) {
189
- const schemaRecord = existing[0];
190
- let modelIds = [];
191
- if (schemaRecord.id) {
192
- modelIds = await getModelIdsForSchema(schemaRecord.id);
193
- await createModelInstances$1(modelIds, evictionEpoch);
194
- }
195
- sendBack({
196
- type: 'schemaFound',
197
- schema: schemaFile,
198
- schemaRecord,
199
- modelIds,
200
- });
201
- return;
202
- }
203
- }
204
- }
205
- catch (error) {
206
- logger$c(`Error checking internal Seed Protocol schema: ${error}`);
207
- }
208
- }
209
- // STEP 1: Query database first for existing schema (prefer drafts)
210
- let dbSchemas = await db
211
- .select()
212
- .from(schemas)
213
- .where(eq(schemas.name, schemaName))
214
- .orderBy(desc(schemas.isDraft), desc(schemas.version))
215
- .limit(1);
216
- // If not found by name, try querying by schemaFileId
217
- if (dbSchemas.length === 0) {
218
- logger$c(`No schema found by name "${schemaName}", trying to find by schemaFileId`);
219
- const schemasByFileId = await db
220
- .select()
221
- .from(schemas)
222
- .where(eq(schemas.schemaFileId, schemaName))
223
- .orderBy(desc(schemas.isDraft), desc(schemas.version))
224
- .limit(1);
225
- if (schemasByFileId.length > 0) {
226
- logger$c(`Found schema by schemaFileId "${schemaName}" (name in DB: ${schemasByFileId[0].name})`);
227
- dbSchemas = schemasByFileId;
228
- }
229
- }
230
- // Try to find by schemaFileId from file
231
- const shouldTrySchemaFileId = dbSchemas.length === 0 || (dbSchemas.length > 0 && dbSchemas[0].isDraft === false);
232
- if (shouldTrySchemaFileId) {
233
- const completeSchemas = await listCompleteSchemaFiles();
234
- const matchingFileSchemas = completeSchemas.filter((s) => s.name === schemaName);
235
- if (matchingFileSchemas.length > 0) {
236
- const latestFile = matchingFileSchemas.reduce((prev, current) => current.version > prev.version ? current : prev);
237
- try {
238
- const content = await BaseFileManager.readFileAsString(latestFile.filePath);
239
- const fileSchema = JSON.parse(content);
240
- if (fileSchema.id) {
241
- const draftsByFileId = await db
242
- .select()
243
- .from(schemas)
244
- .where(and(eq(schemas.schemaFileId, fileSchema.id), eq(schemas.isDraft, true)))
245
- .orderBy(desc(schemas.version))
246
- .limit(1);
247
- if (draftsByFileId.length > 0) {
248
- logger$c(`Found DRAFT by schemaFileId ${fileSchema.id}`);
249
- dbSchemas = draftsByFileId;
250
- }
251
- else {
252
- const dbSchemasByFileId = await db
253
- .select()
254
- .from(schemas)
255
- .where(eq(schemas.schemaFileId, fileSchema.id))
256
- .orderBy(desc(schemas.isDraft), desc(schemas.version))
257
- .limit(1);
258
- if (dbSchemasByFileId.length > 0) {
259
- const foundSchema = dbSchemasByFileId[0];
260
- if (foundSchema.isDraft === true || dbSchemas.length === 0) {
261
- dbSchemas = dbSchemasByFileId;
262
- }
263
- }
264
- }
265
- }
266
- }
267
- catch (error) {
268
- logger$c(`Error reading file to get schemaFileId: ${error}`);
269
- }
270
- }
271
- // Check all drafts for normalized name match
272
- if (dbSchemas.length === 0) {
273
- const allDrafts = await db
274
- .select()
275
- .from(schemas)
276
- .where(eq(schemas.isDraft, true))
277
- .orderBy(desc(schemas.version));
278
- const normalizeName = (name) => name.toLowerCase().replace(/[^a-z0-9]/g, '');
279
- const normalizedRequested = normalizeName(schemaName);
280
- for (const draft of allDrafts) {
281
- if (draft.schemaData) {
282
- try {
283
- const draftSchema = JSON.parse(draft.schemaData);
284
- const draftMetadataName = draftSchema.metadata?.name || '';
285
- const draftDbName = draft.name || '';
286
- const normalizedDraftMetadata = normalizeName(draftMetadataName);
287
- const normalizedDraftDb = normalizeName(draftDbName);
288
- if (normalizedDraftMetadata === normalizedRequested || normalizedDraftDb === normalizedRequested) {
289
- dbSchemas = [draft];
290
- break;
291
- }
292
- }
293
- catch {
294
- continue;
295
- }
296
- }
297
- }
298
- }
299
- }
300
- // If schema found in database, return it
301
- if (dbSchemas.length > 0) {
302
- const dbSchema = dbSchemas[0];
303
- logger$c(`Found schema record: name="${dbSchema.name}", isDraft=${dbSchema.isDraft}`);
304
- // Load schema from schemaData or file
305
- let schemaFile = null;
306
- if (dbSchema.isDraft === true && dbSchema.schemaData) {
307
- try {
308
- schemaFile = JSON.parse(dbSchema.schemaData);
309
- // Ensure schema.id matches schemaFileId from database
310
- if (dbSchema.schemaFileId && schemaFile.id !== dbSchema.schemaFileId) {
311
- schemaFile.id = dbSchema.schemaFileId;
312
- }
313
- else if (!schemaFile.id && dbSchema.schemaFileId) {
314
- schemaFile.id = dbSchema.schemaFileId;
315
- }
316
- // Merge models from database
317
- if (dbSchema.id) {
318
- const dbModels = await loadModelsFromDbForSchema(dbSchema.id);
319
- if (Object.keys(dbModels).length > 0) {
320
- schemaFile.models = mergeDbModelsIntoSchemaModels(schemaFile.models, dbModels);
321
- }
322
- }
323
- }
324
- catch (error) {
325
- logger$c(`Error parsing schemaData: ${error}`);
326
- }
327
- }
328
- else if (dbSchema.schemaFileId) {
329
- // Try to load from file
330
- try {
331
- const completeSchemas = await listCompleteSchemaFiles();
332
- // Match version too: the row is per schema name, so after a new version is imported
333
- // under a new id it can carry the newer version with the older version's schemaFileId.
334
- const idBasedMatch = completeSchemas.find((s) => s.schemaFileId === dbSchema.schemaFileId && s.version === dbSchema.version);
335
- if (idBasedMatch) {
336
- const content = await BaseFileManager.readFileAsString(idBasedMatch.filePath);
337
- schemaFile = JSON.parse(content);
338
- }
339
- else {
340
- const filePath = getSchemaFilePath$1(dbSchema.name, dbSchema.version, dbSchema.schemaFileId);
341
- try {
342
- const content = await BaseFileManager.readFileAsString(filePath);
343
- schemaFile = JSON.parse(content);
344
- }
345
- catch {
346
- // File doesn't exist
347
- }
348
- }
349
- // Ensure schema.id matches schemaFileId from database (database is source of truth)
350
- if (schemaFile && dbSchema.schemaFileId) {
351
- if (schemaFile.id !== dbSchema.schemaFileId) {
352
- logger$c(`Fixing schema ID mismatch: file has id="${schemaFile.id}", DB has schemaFileId="${dbSchema.schemaFileId}". Using DB value.`);
353
- schemaFile.id = dbSchema.schemaFileId;
354
- }
355
- else if (!schemaFile.id && dbSchema.schemaFileId) {
356
- logger$c(`Schema missing id, using schemaFileId from database: "${dbSchema.schemaFileId}"`);
357
- schemaFile.id = dbSchema.schemaFileId;
358
- }
359
- }
360
- if (schemaFile && dbSchema.id) {
361
- const dbModels = await loadModelsFromDbForSchema(dbSchema.id);
362
- if (Object.keys(dbModels).length > 0) {
363
- schemaFile.models = mergeDbModelsIntoSchemaModels(schemaFile.models, dbModels);
364
- }
365
- }
366
- }
367
- catch (error) {
368
- logger$c(`Error loading from file: ${error}`);
369
- }
370
- }
371
- if (schemaFile) {
372
- // Query model IDs and create instances
373
- let modelIds = [];
374
- if (dbSchema.id) {
375
- modelIds = await getModelIdsForSchema(dbSchema.id);
376
- await createModelInstances$1(modelIds, evictionEpoch);
377
- }
378
- const loadedAt = Date.now();
379
- const dbVersion = dbSchema.version || schemaFile.version;
380
- const dbUpdatedAt = dbSchema.updatedAt || loadedAt;
381
- sendBack({
382
- type: 'schemaFound',
383
- schema: schemaFile,
384
- schemaRecord: dbSchema,
385
- modelIds,
386
- loadedAt,
387
- dbVersion,
388
- dbUpdatedAt,
389
- });
390
- return;
391
- }
392
- }
393
- // Check for existing schema files (backward compatibility)
394
- const completeSchemas = await listCompleteSchemaFiles();
395
- const matchingSchemas = completeSchemas.filter((s) => s.name === schemaName);
396
- if (matchingSchemas.length > 0) {
397
- // Check for matching draft in database
398
- const allDrafts = await db
399
- .select()
400
- .from(schemas)
401
- .where(eq(schemas.isDraft, true))
402
- .orderBy(desc(schemas.version));
403
- const normalizeName = (name) => name.toLowerCase().replace(/[^a-z0-9]/g, '');
404
- const normalizedRequested = normalizeName(schemaName);
405
- let matchingDraft = null;
406
- for (const draft of allDrafts) {
407
- if (draft.schemaData) {
408
- try {
409
- const draftSchema = JSON.parse(draft.schemaData);
410
- const draftMetadataName = draftSchema.metadata?.name || '';
411
- const draftDbName = draft.name || '';
412
- const normalizedDraftMetadata = normalizeName(draftMetadataName);
413
- const normalizedDraftDb = normalizeName(draftDbName);
414
- if (normalizedDraftMetadata === normalizedRequested || normalizedDraftDb === normalizedRequested) {
415
- matchingDraft = draft;
416
- break;
417
- }
418
- }
419
- catch {
420
- continue;
421
- }
422
- }
423
- }
424
- if (matchingDraft && matchingDraft.schemaData) {
425
- try {
426
- const schemaFile = JSON.parse(matchingDraft.schemaData);
427
- let modelIds = [];
428
- if (matchingDraft.id) {
429
- modelIds = await getModelIdsForSchema(matchingDraft.id);
430
- await createModelInstances$1(modelIds, evictionEpoch);
431
- }
432
- const loadedAt = Date.now();
433
- const dbVersion = matchingDraft.version || schemaFile.version;
434
- const dbUpdatedAt = matchingDraft.updatedAt || loadedAt;
435
- sendBack({
436
- type: 'schemaFound',
437
- schema: schemaFile,
438
- schemaRecord: matchingDraft,
439
- modelIds,
440
- loadedAt,
441
- dbVersion,
442
- dbUpdatedAt,
443
- });
444
- return;
445
- }
446
- catch (error) {
447
- logger$c(`Error parsing draft schemaData: ${error}`);
448
- }
449
- }
450
- // Load from file
451
- const latest = matchingSchemas.reduce((prev, current) => current.version > prev.version ? current : prev);
452
- const content = await BaseFileManager.readFileAsString(latest.filePath);
453
- const schemaFile = JSON.parse(content);
454
- // Query model IDs if schema exists in DB
455
- let modelIds = [];
456
- try {
457
- if (db && schemaFile.id) {
458
- const schemaRecords = await db
459
- .select()
460
- .from(schemas)
461
- .where(eq(schemas.schemaFileId, schemaFile.id))
462
- .limit(1);
463
- if (schemaRecords.length > 0 && schemaRecords[0].id) {
464
- modelIds = await getModelIdsForSchema(schemaRecords[0].id);
465
- await createModelInstances$1(modelIds, evictionEpoch);
466
- }
467
- }
468
- }
469
- catch (error) {
470
- logger$c(`Error querying model IDs for schema from file: ${error}`);
471
- }
472
- const loadedAt = Date.now();
473
- const dbVersion = schemaFile.version;
474
- const dbUpdatedAt = new Date(schemaFile.metadata.updatedAt).getTime() || loadedAt;
475
- sendBack({
476
- type: 'schemaFound',
477
- schema: schemaFile,
478
- schemaRecord: null,
479
- modelIds,
480
- loadedAt,
481
- dbVersion,
482
- dbUpdatedAt,
483
- });
484
- return;
485
- }
486
- // No schema found
487
- sendBack({
488
- type: 'schemaNotFound',
489
- });
490
- };
491
- _check().catch((error) => {
492
- logger$c('Error in checkExistingSchema:', error);
493
- sendBack({
494
- type: 'schemaNotFound',
495
- });
496
- });
497
- return () => {
498
- // Cleanup function (optional)
499
- };
500
- });
501
-
502
- const logger$b = debug('seedSdk:schema:actors:writeSchemaToDb');
503
- const writeSchemaToDb = fromCallback(({ sendBack, input }) => {
504
- const _write = async () => {
505
- const { schemaName, schemaFile, existingDbSchema } = input;
506
- try {
507
- // Check if this is an internal SDK schema (Seed Protocol) — use static import so consumer bundles resolve correctly
508
- const isInternal = isInternalSchema(schemaName);
509
- let finalSchema;
510
- let schemaRecord;
511
- if (isInternal && schemaName === SEED_PROTOCOL_SCHEMA_NAME) {
512
- // For Seed Protocol, load from internal file
513
- logger$b(`Loading internal Seed Protocol schema from SDK`);
514
- const _mod_2 = await import('./ClientManager-GZfXErDX.js');
515
- const _ns_2 = _mod_2.T;
516
- const internalSchema = _ns_2;
517
- finalSchema = internalSchema.default;
518
- const db = BaseDb.getAppDb();
519
- if (db && finalSchema.id) {
520
- // Check if it exists in database
521
- const existing = await db
522
- .select()
523
- .from(schemas)
524
- .where(eq(schemas.schemaFileId, finalSchema.id))
525
- .limit(1);
526
- if (existing.length === 0) {
527
- // Add to database if not present
528
- const schemaData = JSON.stringify(finalSchema, null, 2);
529
- schemaRecord = await addSchemaToDb({
530
- name: schemaName,
531
- version: finalSchema.version,
532
- createdAt: new Date(finalSchema.metadata.createdAt).getTime(),
533
- updatedAt: new Date(finalSchema.metadata.updatedAt).getTime(),
534
- }, finalSchema.id, schemaData, false, // isDraft = false (it's a published internal schema)
535
- false);
536
- logger$b(`Added Seed Protocol schema to database`);
537
- }
538
- else {
539
- schemaRecord = existing[0];
540
- logger$b(`Seed Protocol schema already exists in database`);
541
- }
542
- }
543
- else {
544
- throw new Error('Database not available for Seed Protocol schema');
545
- }
546
- }
547
- else if (schemaFile) {
548
- // Use provided schema file
549
- finalSchema = schemaFile;
550
- // Ensure schema has an ID
551
- if (!finalSchema.id) {
552
- finalSchema.id = generateId();
553
- logger$b(`Generated schema ID for schema: ${finalSchema.id}`);
554
- }
555
- const schemaData = JSON.stringify(finalSchema, null, 2);
556
- schemaRecord = await addSchemaToDb({
557
- name: schemaName,
558
- version: finalSchema.version,
559
- createdAt: new Date(finalSchema.metadata.createdAt).getTime(),
560
- updatedAt: new Date(finalSchema.metadata.updatedAt).getTime(),
561
- }, finalSchema.id, schemaData, true, // isDraft = true for new schemas
562
- true);
563
- logger$b(`Created schema ${schemaName} v${finalSchema.version} in database`);
564
- }
565
- else {
566
- // Create new schema
567
- const latestVersion = existingDbSchema?.version
568
- ? existingDbSchema.version + 1
569
- : await getLatestSchemaVersion(schemaName);
570
- const newVersion = latestVersion + 1;
571
- const now = new Date().toISOString();
572
- finalSchema = {
573
- $schema: 'https://seedprotocol.org/schemas/data-model/v1',
574
- version: newVersion,
575
- id: generateId(),
576
- metadata: {
577
- name: schemaName,
578
- createdAt: now,
579
- updatedAt: now,
580
- },
581
- models: {},
582
- enums: {},
583
- migrations: [
584
- {
585
- version: newVersion,
586
- timestamp: now,
587
- description: 'Initial schema',
588
- changes: [],
589
- },
590
- ],
591
- };
592
- // Save to database as draft
593
- const schemaData = JSON.stringify(finalSchema, null, 2);
594
- schemaRecord = await addSchemaToDb({
595
- name: schemaName,
596
- version: newVersion,
597
- createdAt: new Date(now).getTime(),
598
- updatedAt: new Date(now).getTime(),
599
- }, finalSchema.id, schemaData, true, // isDraft = true
600
- true);
601
- logger$b(`Created new draft schema ${schemaName} v${newVersion} in database`);
602
- }
603
- sendBack({
604
- type: 'schemaWritten',
605
- schemaRecord,
606
- schema: finalSchema,
607
- });
608
- }
609
- catch (error) {
610
- logger$b(`Error writing schema to database: ${error}`);
611
- sendBack({
612
- type: 'writeError',
613
- error: error instanceof Error ? error : new Error(String(error)),
614
- });
615
- }
616
- };
617
- _write().catch((error) => {
618
- logger$b('Error in writeSchemaToDb:', error);
619
- sendBack({
620
- type: 'writeError',
621
- error: error instanceof Error ? error : new Error(String(error)),
622
- });
623
- });
624
- return () => {
625
- // Cleanup function (optional)
626
- };
627
- });
628
-
629
- const logger$a = debug('seedSdk:schema:actors:verifySchemaInDb');
630
- /**
631
- * Verify that a schema record exists in the database with retry logic
632
- * Uses exponential backoff: 100ms, 200ms, 400ms, 800ms, 1600ms
633
- * Total timeout: ~3.1 seconds
634
- */
635
- async function verifyWithRetry$4(verifyFn, maxRetries = 5, initialDelay = 100) {
636
- let lastError = null;
637
- let delay = initialDelay;
638
- for (let attempt = 0; attempt < maxRetries; attempt++) {
639
- try {
640
- return await verifyFn();
641
- }
642
- catch (error) {
643
- lastError = error instanceof Error ? error : new Error(String(error));
644
- if (attempt < maxRetries - 1) {
645
- logger$a(`Verification attempt ${attempt + 1} failed, retrying in ${delay}ms: ${lastError.message}`);
646
- await new Promise(resolve => setTimeout(resolve, delay));
647
- delay *= 2; // Exponential backoff
648
- }
649
- }
650
- }
651
- throw lastError || new Error('Verification failed after retries');
652
- }
653
- const verifySchemaInDb = fromCallback(({ sendBack, input }) => {
654
- const _verify = async () => {
655
- const { schemaFileId, expectedSchemaId } = input;
656
- try {
657
- const result = await verifyWithRetry$4(async () => {
658
- const db = BaseDb.getAppDb();
659
- if (!db) {
660
- throw new Error('Database not available');
661
- }
662
- // Query for schema by schemaFileId
663
- const schemaRecords = await db
664
- .select()
665
- .from(schemas)
666
- .where(eq(schemas.schemaFileId, schemaFileId))
667
- .limit(1);
668
- if (schemaRecords.length === 0) {
669
- throw new Error(`Schema with schemaFileId "${schemaFileId}" not found in database`);
670
- }
671
- const schemaRecord = schemaRecords[0];
672
- // If expectedSchemaId is provided, verify it matches
673
- if (expectedSchemaId !== undefined && schemaRecord.id !== expectedSchemaId) {
674
- throw new Error(`Schema ID mismatch: expected ${expectedSchemaId}, found ${schemaRecord.id}`);
675
- }
676
- logger$a(`Schema verified: schemaFileId="${schemaFileId}", schemaId=${schemaRecord.id}`);
677
- return schemaRecord.id;
678
- });
679
- sendBack({
680
- type: 'schemaVerified',
681
- schemaId: result,
682
- });
683
- }
684
- catch (error) {
685
- logger$a(`Schema verification failed after retries: ${error}`);
686
- sendBack({
687
- type: 'verificationFailed',
688
- stage: 'verifySchema',
689
- error: error instanceof Error ? error : new Error(String(error)),
690
- });
691
- }
692
- };
693
- _verify().catch((error) => {
694
- logger$a('Error in verifySchemaInDb:', error);
695
- sendBack({
696
- type: 'verificationFailed',
697
- stage: 'verifySchema',
698
- error: error instanceof Error ? error : new Error(String(error)),
699
- });
700
- });
701
- return () => {
702
- // Cleanup function (optional)
703
- };
704
- });
705
-
706
- const logger$9 = debug('seedSdk:schema:actors:writeModelsToDb');
707
- const writeModelsToDb = fromCallback(({ sendBack, input }) => {
708
- const _write = async () => {
709
- const { schema, schemaRecord, schemaName } = input;
710
- try {
711
- // Check if models already exist in database
712
- const db = BaseDb.getAppDb();
713
- if (!db) {
714
- throw new Error('Database not available');
715
- }
716
- // Check if models are linked to the schema
717
- const modelLinks = await db
718
- .select({
719
- modelId: modelSchemas.modelId,
720
- modelName: models.name,
721
- })
722
- .from(modelSchemas)
723
- .innerJoin(models, eq(modelSchemas.modelId, models.id))
724
- .where(eq(modelSchemas.schemaId, schemaRecord.id));
725
- // Check if we have all expected models
726
- const expectedModelNames = Object.keys(schema.models || {});
727
- const linkedModelNames = modelLinks
728
- .map((link) => link.modelName)
729
- .filter((n) => n !== null);
730
- const missingModels = expectedModelNames.filter(name => !linkedModelNames.includes(name));
731
- // If all models exist, skip writing
732
- if (missingModels.length === 0 && modelLinks.length > 0) {
733
- logger$9(`All models already exist in database for schema ${schemaName}`);
734
- // Extract model IDs from database
735
- const modelFileIds = [];
736
- for (const link of modelLinks) {
737
- if (link.modelId) {
738
- const modelRecord = await db
739
- .select({ schemaFileId: models.schemaFileId })
740
- .from(models)
741
- .where(eq(models.id, link.modelId))
742
- .limit(1);
743
- if (modelRecord.length > 0 && modelRecord[0].schemaFileId) {
744
- modelFileIds.push(modelRecord[0].schemaFileId);
745
- }
746
- }
747
- }
748
- sendBack({
749
- type: 'modelsWritten',
750
- modelIds: modelFileIds,
751
- });
752
- return;
753
- }
754
- // Check if this is Seed Protocol schema (has different format) — use static import so consumer bundles resolve correctly
755
- const isInternal = isInternalSchema(schemaName);
756
- let importData;
757
- if (isInternal && schemaName === SEED_PROTOCOL_SCHEMA_NAME) {
758
- // Convert Seed Protocol schema format to JSON import format
759
- importData = {
760
- name: schemaName,
761
- models: Object.fromEntries(Object.entries(schema.models || {}).map(([modelName, model]) => [
762
- modelName,
763
- {
764
- ...model,
765
- id: undefined,
766
- properties: Object.fromEntries(Object.entries(model.properties || {}).map(([propName, prop]) => {
767
- const schemaProp = prop;
768
- const jsonProp = {
769
- type: schemaProp.dataType || schemaProp.type,
770
- };
771
- // Copy other properties
772
- Object.keys(schemaProp).forEach(key => {
773
- if (key !== 'id' && key !== 'dataType') {
774
- jsonProp[key] = schemaProp[key];
775
- }
776
- });
777
- // Handle Relation type
778
- if (schemaProp.ref || schemaProp.refModelName) {
779
- jsonProp.model = schemaProp.refModelName || schemaProp.ref;
780
- }
781
- // Handle List type
782
- if (isDataType(schemaProp.dataType, ModelPropertyDataTypes.List) && schemaProp.refValueType) {
783
- jsonProp.refValueType = schemaProp.refValueType;
784
- if (schemaProp.ref || schemaProp.refModelName) {
785
- jsonProp.ref = schemaProp.refModelName || schemaProp.ref;
786
- }
787
- }
788
- // Handle storage configuration
789
- if (schemaProp.storageType || schemaProp.localStorageDir || schemaProp.filenameSuffix) {
790
- jsonProp.storage = {
791
- type: schemaProp.storageType === 'ItemStorage' ? 'ItemStorage' : 'PropertyStorage',
792
- path: schemaProp.localStorageDir,
793
- extension: schemaProp.filenameSuffix,
794
- };
795
- }
796
- return [propName, jsonProp];
797
- })),
798
- },
799
- ])),
800
- };
801
- }
802
- else {
803
- // Standard schema format
804
- importData = {
805
- name: schemaName,
806
- models: Object.fromEntries(Object.entries(schema.models || {}).map(([modelName, model]) => [
807
- modelName,
808
- {
809
- ...model,
810
- id: undefined,
811
- properties: model.properties || {},
812
- },
813
- ])),
814
- };
815
- }
816
- // Generate schemaFileIds from JSON file and generate missing ones BEFORE creating models
817
- const modelFileIds = new Map();
818
- const propertyFileIds = new Map();
819
- for (const [modelName, model] of Object.entries(schema.models || {})) {
820
- // Generate model ID if missing
821
- if (!model.id) {
822
- model.id = generateId();
823
- logger$9(`Generated model ID for ${modelName}: ${model.id}`);
824
- }
825
- modelFileIds.set(modelName, model.id);
826
- const propIds = new Map();
827
- for (const [propName, prop] of Object.entries(model.properties || {})) {
828
- // Generate property ID if missing
829
- if (!prop.id) {
830
- prop.id = generateId();
831
- logger$9(`Generated property ID for ${modelName}.${propName}: ${prop.id}`);
832
- }
833
- propIds.set(propName, prop.id);
834
- }
835
- if (propIds.size > 0) {
836
- propertyFileIds.set(modelName, propIds);
837
- }
838
- }
839
- // Convert JSON models to Model classes. addModelsToDb below writes their rows; the models adopt them
840
- // once it has run.
841
- const rowsWritten = createRowsWrittenSignal();
842
- const modelDefinitions = await createModelsFromJson(importData, modelFileIds, propertyFileIds, rowsWritten.promise);
843
- // Add models to database
844
- if (Object.keys(modelDefinitions).length > 0) {
845
- await addModelsToDb(modelDefinitions, schemaRecord, undefined, {
846
- schemaFileId: schema.id,
847
- modelFileIds,
848
- propertyFileIds,
849
- }).finally(rowsWritten.settle);
850
- logger$9(`Added ${Object.keys(modelDefinitions).length} models and their properties to database`);
851
- }
852
- // Extract model IDs that were written
853
- const writtenModelIds = Array.from(modelFileIds.values());
854
- sendBack({
855
- type: 'modelsWritten',
856
- modelIds: writtenModelIds,
857
- });
858
- }
859
- catch (error) {
860
- logger$9(`Error writing models to database: ${error}`);
861
- sendBack({
862
- type: 'writeError',
863
- error: error instanceof Error ? error : new Error(String(error)),
864
- });
865
- }
866
- };
867
- _write().catch((error) => {
868
- logger$9('Error in writeModelsToDb:', error);
869
- sendBack({
870
- type: 'writeError',
871
- error: error instanceof Error ? error : new Error(String(error)),
872
- });
873
- });
874
- return () => {
875
- // Cleanup function (optional)
876
- };
877
- });
878
-
879
- const logger$8 = debug('seedSdk:schema:actors:verifyModelsInDb');
880
- /**
881
- * Verify that model records exist in the database for a schema with retry logic
882
- */
883
- async function verifyWithRetry$3(verifyFn, maxRetries = 5, initialDelay = 100) {
884
- let lastError = null;
885
- let delay = initialDelay;
886
- for (let attempt = 0; attempt < maxRetries; attempt++) {
887
- try {
888
- return await verifyFn();
889
- }
890
- catch (error) {
891
- lastError = error instanceof Error ? error : new Error(String(error));
892
- if (attempt < maxRetries - 1) {
893
- logger$8(`Verification attempt ${attempt + 1} failed, retrying in ${delay}ms: ${lastError.message}`);
894
- await new Promise(resolve => setTimeout(resolve, delay));
895
- delay *= 2; // Exponential backoff
896
- }
897
- }
898
- }
899
- throw lastError || new Error('Verification failed after retries');
900
- }
901
- const verifyModelsInDb = fromCallback(({ sendBack, input }) => {
902
- const _verify = async () => {
903
- const { schemaId, expectedModelIds } = input;
904
- try {
905
- const result = await verifyWithRetry$3(async () => {
906
- const db = BaseDb.getAppDb();
907
- if (!db) {
908
- throw new Error('Database not available');
909
- }
910
- // Query for models linked to this schema
911
- const modelRecords = await db
912
- .select({
913
- modelFileId: models.schemaFileId,
914
- modelId: models.id,
915
- })
916
- .from(modelSchemas)
917
- .innerJoin(models, eq(modelSchemas.modelId, models.id))
918
- .where(eq(modelSchemas.schemaId, schemaId));
919
- // If expectedModelIds is provided and empty, it means the schema should have no models
920
- // This is valid for empty schemas
921
- if (modelRecords.length === 0) {
922
- if (expectedModelIds !== undefined && expectedModelIds.length === 0) {
923
- // Empty schema is expected and valid
924
- logger$8(`Schema ${schemaId} has no models (as expected)`);
925
- return [];
926
- }
927
- throw new Error(`No models found for schema (id: ${schemaId})`);
928
- }
929
- const modelIds = modelRecords
930
- .map((row) => row.modelFileId)
931
- .filter((id) => id !== null && id !== undefined);
932
- // If expectedModelIds provided, verify all are present
933
- if (expectedModelIds && expectedModelIds.length > 0) {
934
- const missingIds = expectedModelIds.filter(id => !modelIds.includes(id));
935
- if (missingIds.length > 0) {
936
- throw new Error(`Missing expected model IDs: ${missingIds.join(', ')}. Found: ${modelIds.join(', ')}`);
937
- }
938
- }
939
- logger$8(`Models verified: schemaId=${schemaId}, found ${modelIds.length} models: ${modelIds.join(', ')}`);
940
- return modelIds;
941
- });
942
- sendBack({
943
- type: 'modelsVerified',
944
- modelIds: result,
945
- });
946
- }
947
- catch (error) {
948
- logger$8(`Model verification failed after retries: ${error}`);
949
- sendBack({
950
- type: 'verificationFailed',
951
- stage: 'verifyModels',
952
- error: error instanceof Error ? error : new Error(String(error)),
953
- });
954
- }
955
- };
956
- _verify().catch((error) => {
957
- logger$8('Error in verifyModelsInDb:', error);
958
- sendBack({
959
- type: 'verificationFailed',
960
- stage: 'verifyModels',
961
- error: error instanceof Error ? error : new Error(String(error)),
962
- });
963
- });
964
- return () => {
965
- // Cleanup function (optional)
966
- };
967
- });
968
-
969
- const logger$7 = debug('seedSdk:schema:actors:createModelInstances');
970
- /**
971
- * Create Model instances for all model IDs to ensure they're cached
972
- * This ensures that Model.getById() in Schema.getContext() will find the instances
973
- */
974
- const createModelInstances = fromCallback(({ sendBack, input }) => {
975
- const evictionEpoch = currentEvictionEpoch();
976
- const _create = async () => {
977
- const { modelIds } = input;
978
- if (modelIds.length === 0) {
979
- logger$7('No model IDs provided, skipping instance creation');
980
- sendBack({
981
- type: 'instancesCreated',
982
- count: 0,
983
- });
984
- return;
985
- }
986
- try {
987
- const _mod_3 = await import('./errors-D1zHAqjr.js');
988
- const _ns_3 = _mod_3.i;
989
- const modelMod = _ns_3;
990
- const { Model } = modelMod;
991
- // Create instances for all model IDs in parallel
992
- // Model.createById() will check cache first, then query DB and create if needed
993
- const createPromises = modelIds.map(async (modelFileId) => {
994
- try {
995
- // Not if the schema was evicted (Schema.destroy, test cleanup) since this started
996
- const schemaHold = Model.takeSchemaHold(modelFileId);
997
- const model = await Model.createById(modelFileId, { evictionEpoch, ...(!schemaHold && { hold: false }) });
998
- if (!model && schemaHold)
999
- Model.dropSchemaHold(modelFileId);
1000
- if (model) {
1001
- logger$7(`Created/cached Model instance for modelFileId "${modelFileId}"`);
1002
- return true;
1003
- }
1004
- else {
1005
- logger$7(`Model.createById returned undefined for modelFileId "${modelFileId}" (may not exist in DB yet)`);
1006
- return false;
1007
- }
1008
- }
1009
- catch (error) {
1010
- logger$7(`Error creating Model instance for modelFileId "${modelFileId}": ${error}`);
1011
- // Don't throw - continue with other models
1012
- return false;
1013
- }
1014
- });
1015
- const results = await Promise.all(createPromises);
1016
- const successCount = results.filter(Boolean).length;
1017
- logger$7(`Finished creating/caching ${successCount}/${modelIds.length} Model instances`);
1018
- sendBack({
1019
- type: 'instancesCreated',
1020
- count: successCount,
1021
- });
1022
- }
1023
- catch (error) {
1024
- logger$7(`Error in createModelInstances: ${error}`);
1025
- sendBack({
1026
- type: 'writeError',
1027
- error: error instanceof Error ? error : new Error(String(error)),
1028
- });
1029
- }
1030
- };
1031
- _create().catch((error) => {
1032
- logger$7('Error in createModelInstances:', error);
1033
- sendBack({
1034
- type: 'writeError',
1035
- error: error instanceof Error ? error : new Error(String(error)),
1036
- });
1037
- });
1038
- return () => {
1039
- // Cleanup function (optional)
1040
- };
1041
- });
1042
-
1043
- const logger$6 = debug('seedSdk:schema:actors:verifyModelInstancesInCache');
1044
- /**
1045
- * Verify that Model instances exist in the static cache with retry logic
1046
- */
1047
- async function verifyWithRetry$2(verifyFn, maxRetries = 5, initialDelay = 100) {
1048
- let lastError = null;
1049
- let delay = initialDelay;
1050
- for (let attempt = 0; attempt < maxRetries; attempt++) {
1051
- try {
1052
- return await verifyFn();
1053
- }
1054
- catch (error) {
1055
- lastError = error instanceof Error ? error : new Error(String(error));
1056
- if (attempt < maxRetries - 1) {
1057
- logger$6(`Verification attempt ${attempt + 1} failed, retrying in ${delay}ms: ${lastError.message}`);
1058
- await new Promise(resolve => setTimeout(resolve, delay));
1059
- delay *= 2; // Exponential backoff
1060
- }
1061
- }
1062
- }
1063
- throw lastError || new Error('Verification failed after retries');
1064
- }
1065
- const verifyModelInstancesInCache = fromCallback(({ sendBack, input }) => {
1066
- const _verify = async () => {
1067
- const { modelIds } = input;
1068
- if (modelIds.length === 0) {
1069
- // No models means no instances to verify - this is valid
1070
- logger$6('No model IDs provided, skipping instance verification');
1071
- sendBack({
1072
- type: 'instancesVerified',
1073
- count: 0,
1074
- });
1075
- return;
1076
- }
1077
- try {
1078
- const result = await verifyWithRetry$2(async () => {
1079
- const _mod_4 = await import('./errors-D1zHAqjr.js');
1080
- const _ns_4 = _mod_4.i;
1081
- const modelMod = _ns_4;
1082
- const { Model } = modelMod;
1083
- // Check each model ID in the cache
1084
- const verifiedInstances = [];
1085
- const missingIds = [];
1086
- for (const modelFileId of modelIds) {
1087
- // Try to get instance from cache
1088
- const instance = Model.peek(modelFileId);
1089
- if (instance) {
1090
- verifiedInstances.push(modelFileId);
1091
- }
1092
- else {
1093
- missingIds.push(modelFileId);
1094
- }
1095
- }
1096
- if (missingIds.length > 0) {
1097
- throw new Error(`Model instances not found in cache: ${missingIds.join(', ')}. Found: ${verifiedInstances.length}/${modelIds.length}`);
1098
- }
1099
- logger$6(`Model instances verified: ${verifiedInstances.length} instances in cache`);
1100
- return verifiedInstances.length;
1101
- });
1102
- sendBack({
1103
- type: 'instancesVerified',
1104
- count: result,
1105
- });
1106
- }
1107
- catch (error) {
1108
- logger$6(`Model instance verification failed after retries: ${error}`);
1109
- sendBack({
1110
- type: 'verificationFailed',
1111
- stage: 'verifyModelInstances',
1112
- error: error instanceof Error ? error : new Error(String(error)),
1113
- });
1114
- }
1115
- };
1116
- _verify().catch((error) => {
1117
- logger$6('Error in verifyModelInstancesInCache:', error);
1118
- sendBack({
1119
- type: 'verificationFailed',
1120
- stage: 'verifyModelInstances',
1121
- error: error instanceof Error ? error : new Error(String(error)),
1122
- });
1123
- });
1124
- return () => {
1125
- // Cleanup function (optional)
1126
- };
1127
- });
1128
-
1129
- const logger$5 = debug('seedSdk:schema:actors:writePropertiesToDb');
1130
- /**
1131
- * Properties are typically written to DB as part of writeModelsToDb
1132
- * This actor verifies that properties exist and extracts their IDs
1133
- */
1134
- const writePropertiesToDb = fromCallback(({ sendBack, input }) => {
1135
- const _write = async () => {
1136
- const { modelIds } = input;
1137
- try {
1138
- const _mod_5 = await import('./index-Dx_GTdyQ.js');
1139
- const _ns_5 = _mod_5.ba;
1140
- const modelSchemaMod = _ns_5;
1141
- const { models: modelsTable, properties: propertiesTable } = modelSchemaMod;
1142
- const drizzleMod = await import('drizzle-orm');
1143
- const { eq } = drizzleMod;
1144
- const db = BaseDb.getAppDb();
1145
- if (!db) {
1146
- throw new Error('Database not available');
1147
- }
1148
- // Get property IDs for all models
1149
- const propertyIds = [];
1150
- for (const modelFileId of modelIds) {
1151
- // Get model DB ID from modelFileId
1152
- const modelRecords = await db
1153
- .select({ id: modelsTable.id })
1154
- .from(modelsTable)
1155
- .where(eq(modelsTable.schemaFileId, modelFileId))
1156
- .limit(1);
1157
- if (modelRecords.length > 0 && modelRecords[0].id) {
1158
- // Get properties for this model
1159
- const propertyRecords = await db
1160
- .select({ schemaFileId: propertiesTable.schemaFileId })
1161
- .from(propertiesTable)
1162
- .where(eq(propertiesTable.modelId, modelRecords[0].id));
1163
- for (const prop of propertyRecords) {
1164
- if (prop.schemaFileId) {
1165
- propertyIds.push(prop.schemaFileId);
1166
- }
1167
- }
1168
- }
1169
- }
1170
- logger$5(`Found ${propertyIds.length} properties for ${modelIds.length} models`);
1171
- sendBack({
1172
- type: 'propertiesWritten',
1173
- propertyIds,
1174
- });
1175
- }
1176
- catch (error) {
1177
- logger$5(`Error writing/verifying properties to database: ${error}`);
1178
- sendBack({
1179
- type: 'writeError',
1180
- error: error instanceof Error ? error : new Error(String(error)),
1181
- });
1182
- }
1183
- };
1184
- _write().catch((error) => {
1185
- logger$5('Error in writePropertiesToDb:', error);
1186
- sendBack({
1187
- type: 'writeError',
1188
- error: error instanceof Error ? error : new Error(String(error)),
1189
- });
1190
- });
1191
- return () => {
1192
- // Cleanup function (optional)
1193
- };
1194
- });
1195
-
1196
- const logger$4 = debug('seedSdk:schema:actors:verifyPropertiesInDb');
1197
- /**
1198
- * Verify that property records exist in the database for models with retry logic
1199
- */
1200
- async function verifyWithRetry$1(verifyFn, maxRetries = 5, initialDelay = 100) {
1201
- let lastError = null;
1202
- let delay = initialDelay;
1203
- for (let attempt = 0; attempt < maxRetries; attempt++) {
1204
- try {
1205
- return await verifyFn();
1206
- }
1207
- catch (error) {
1208
- lastError = error instanceof Error ? error : new Error(String(error));
1209
- if (attempt < maxRetries - 1) {
1210
- logger$4(`Verification attempt ${attempt + 1} failed, retrying in ${delay}ms: ${lastError.message}`);
1211
- await new Promise(resolve => setTimeout(resolve, delay));
1212
- delay *= 2; // Exponential backoff
1213
- }
1214
- }
1215
- }
1216
- throw lastError || new Error('Verification failed after retries');
1217
- }
1218
- const verifyPropertiesInDb = fromCallback(({ sendBack, input }) => {
1219
- const _verify = async () => {
1220
- const { modelIds, modelFileIds, expectedPropertyIds } = input;
1221
- // Convert model file IDs to DB IDs if needed
1222
- let dbModelIds = [];
1223
- if (modelIds && modelIds.length > 0) {
1224
- dbModelIds = modelIds;
1225
- }
1226
- else if (modelFileIds && modelFileIds.length > 0) {
1227
- // Convert file IDs to DB IDs
1228
- const db = BaseDb.getAppDb();
1229
- if (!db) {
1230
- throw new Error('Database not available');
1231
- }
1232
- for (const modelFileId of modelFileIds) {
1233
- const modelRecords = await db
1234
- .select({ id: models.id })
1235
- .from(models)
1236
- .where(eq(models.schemaFileId, modelFileId))
1237
- .limit(1);
1238
- if (modelRecords.length > 0 && modelRecords[0].id) {
1239
- dbModelIds.push(modelRecords[0].id);
1240
- }
1241
- }
1242
- }
1243
- if (dbModelIds.length === 0) {
1244
- // No models means no properties to verify - this is valid
1245
- logger$4('No models provided, skipping property verification');
1246
- sendBack({
1247
- type: 'propertiesVerified',
1248
- propertyIds: [],
1249
- });
1250
- return;
1251
- }
1252
- try {
1253
- const result = await verifyWithRetry$1(async () => {
1254
- const db = BaseDb.getAppDb();
1255
- if (!db) {
1256
- throw new Error('Database not available');
1257
- }
1258
- // Query for properties for all models
1259
- const allProperties = await Promise.all(dbModelIds.map(async (modelId) => {
1260
- const props = await db
1261
- .select({
1262
- propertyFileId: properties.schemaFileId,
1263
- propertyId: properties.id,
1264
- })
1265
- .from(properties)
1266
- .where(eq(properties.modelId, modelId));
1267
- return props;
1268
- }));
1269
- const propertyIds = allProperties
1270
- .flat()
1271
- .map((row) => row.propertyFileId)
1272
- .filter((id) => id !== null && id !== undefined);
1273
- // If expectedPropertyIds provided, verify all are present
1274
- if (expectedPropertyIds && expectedPropertyIds.length > 0) {
1275
- const missingIds = expectedPropertyIds.filter(id => !propertyIds.includes(id));
1276
- if (missingIds.length > 0) {
1277
- throw new Error(`Missing expected property IDs: ${missingIds.join(', ')}. Found: ${propertyIds.join(', ')}`);
1278
- }
1279
- }
1280
- logger$4(`Properties verified: found ${propertyIds.length} properties for ${dbModelIds.length} models`);
1281
- return propertyIds;
1282
- });
1283
- sendBack({
1284
- type: 'propertiesVerified',
1285
- propertyIds: result,
1286
- });
1287
- }
1288
- catch (error) {
1289
- logger$4(`Property verification failed after retries: ${error}`);
1290
- sendBack({
1291
- type: 'verificationFailed',
1292
- stage: 'verifyProperties',
1293
- error: error instanceof Error ? error : new Error(String(error)),
1294
- });
1295
- }
1296
- };
1297
- _verify().catch((error) => {
1298
- logger$4('Error in verifyPropertiesInDb:', error);
1299
- sendBack({
1300
- type: 'verificationFailed',
1301
- stage: 'verifyProperties',
1302
- error: error instanceof Error ? error : new Error(String(error)),
1303
- });
1304
- });
1305
- return () => {
1306
- // Cleanup function (optional)
1307
- };
1308
- });
1309
-
1310
- const logger$3 = debug('seedSdk:schema:actors:createPropertyInstances');
1311
- /**
1312
- * Create ModelProperty instances for all property IDs to ensure they're cached
1313
- * Properties are typically created when Model instances are created, but this
1314
- * ensures they're all available in the cache
1315
- */
1316
- const createPropertyInstances = fromCallback(({ sendBack, input }) => {
1317
- const _create = async () => {
1318
- const { propertyIds, modelIds } = input;
1319
- if (propertyIds.length === 0) {
1320
- logger$3('No property IDs provided, skipping instance creation');
1321
- sendBack({
1322
- type: 'instancesCreated',
1323
- count: 0,
1324
- });
1325
- return;
1326
- }
1327
- try {
1328
- const modProp = await import('./ModelProperty-i5flwzQG.js');
1329
- const ModelProperty = modProp?.ModelProperty ?? modProp?.default;
1330
- const _mod_6 = await import('./errors-D1zHAqjr.js');
1331
- const _ns_6 = _mod_6.i;
1332
- const modModel = _ns_6;
1333
- const Model = modModel?.Model ?? modModel?.default;
1334
- if (!ModelProperty || !Model) {
1335
- logger$3('ModelProperty or Model not available from dynamic import');
1336
- sendBack({ type: 'instancesCreated', count: 0 });
1337
- return;
1338
- }
1339
- // Properties are typically loaded when Model instances are loaded
1340
- // But we can verify they exist by checking Model instances
1341
- let successCount = 0;
1342
- // For each model, check if properties are loaded
1343
- for (const modelFileId of modelIds) {
1344
- const model = Model.peek(modelFileId);
1345
- if (model && model.properties) {
1346
- // Count properties that match our property IDs
1347
- for (const prop of model.properties) {
1348
- const context = prop._getSnapshotContext();
1349
- const propFileId = context._propertyFileId || context.id;
1350
- if (propFileId && propertyIds.includes(String(propFileId))) {
1351
- successCount++;
1352
- }
1353
- }
1354
- }
1355
- }
1356
- // Also try to get properties directly by ID
1357
- for (const propertyFileId of propertyIds) {
1358
- try {
1359
- const property = ModelProperty.getById(propertyFileId);
1360
- if (property) {
1361
- // Check if we already counted this
1362
- // Access _service via type assertion since it's protected
1363
- const context = property._service.getSnapshot().context;
1364
- const propFileId = context._propertyFileId || context.id;
1365
- if (!propertyIds.slice(0, propertyIds.indexOf(propertyFileId)).includes(String(propFileId))) {
1366
- successCount++;
1367
- }
1368
- logger$3(`Found/cached ModelProperty instance for propertyFileId "${propertyFileId}"`);
1369
- }
1370
- }
1371
- catch (error) {
1372
- logger$3(`Error getting ModelProperty instance for propertyFileId "${propertyFileId}": ${error}`);
1373
- }
1374
- }
1375
- logger$3(`Finished verifying/caching ${successCount}/${propertyIds.length} Property instances`);
1376
- sendBack({
1377
- type: 'instancesCreated',
1378
- count: successCount,
1379
- });
1380
- }
1381
- catch (error) {
1382
- logger$3(`Error in createPropertyInstances: ${error}`);
1383
- sendBack({
1384
- type: 'writeError',
1385
- error: error instanceof Error ? error : new Error(String(error)),
1386
- });
1387
- }
1388
- };
1389
- _create().catch((error) => {
1390
- logger$3('Error in createPropertyInstances:', error);
1391
- sendBack({
1392
- type: 'writeError',
1393
- error: error instanceof Error ? error : new Error(String(error)),
1394
- });
1395
- });
1396
- return () => {
1397
- // Cleanup function (optional)
1398
- };
1399
- });
1400
-
1401
- const logger$2 = debug('seedSdk:schema:actors:verifyPropertyInstancesInCache');
1402
- /**
1403
- * Verify that ModelProperty instances exist in the static cache with retry logic
1404
- */
1405
- async function verifyWithRetry(verifyFn, maxRetries = 5, initialDelay = 100) {
1406
- let lastError = null;
1407
- let delay = initialDelay;
1408
- for (let attempt = 0; attempt < maxRetries; attempt++) {
1409
- try {
1410
- return await verifyFn();
1411
- }
1412
- catch (error) {
1413
- lastError = error instanceof Error ? error : new Error(String(error));
1414
- if (attempt < maxRetries - 1) {
1415
- logger$2(`Verification attempt ${attempt + 1} failed, retrying in ${delay}ms: ${lastError.message}`);
1416
- await new Promise(resolve => setTimeout(resolve, delay));
1417
- delay *= 2; // Exponential backoff
1418
- }
1419
- }
1420
- }
1421
- throw lastError || new Error('Verification failed after retries');
1422
- }
1423
- const verifyPropertyInstancesInCache = fromCallback(({ sendBack, input }) => {
1424
- const _verify = async () => {
1425
- const { propertyIds } = input;
1426
- if (propertyIds.length === 0) {
1427
- // No properties means no instances to verify - this is valid
1428
- logger$2('No property IDs provided, skipping instance verification');
1429
- sendBack({
1430
- type: 'instancesVerified',
1431
- count: 0,
1432
- });
1433
- return;
1434
- }
1435
- try {
1436
- const result = await verifyWithRetry(async () => {
1437
- const mod = await import('./ModelProperty-i5flwzQG.js');
1438
- const ModelProperty = mod?.ModelProperty ?? mod?.default;
1439
- if (!ModelProperty) {
1440
- throw new Error('ModelProperty not available from dynamic import');
1441
- }
1442
- // Check each property ID in the cache
1443
- const verifiedInstances = [];
1444
- const missingIds = [];
1445
- for (const propertyFileId of propertyIds) {
1446
- // Try to get instance from cache
1447
- const instance = ModelProperty.getById(propertyFileId);
1448
- if (instance) {
1449
- verifiedInstances.push(propertyFileId);
1450
- }
1451
- else {
1452
- missingIds.push(propertyFileId);
1453
- }
1454
- }
1455
- if (missingIds.length > 0) {
1456
- throw new Error(`Property instances not found in cache: ${missingIds.join(', ')}. Found: ${verifiedInstances.length}/${propertyIds.length}`);
1457
- }
1458
- logger$2(`Property instances verified: ${verifiedInstances.length} instances in cache`);
1459
- return verifiedInstances.length;
1460
- });
1461
- sendBack({
1462
- type: 'instancesVerified',
1463
- count: result,
1464
- });
1465
- }
1466
- catch (error) {
1467
- logger$2(`Property instance verification failed after retries: ${error}`);
1468
- sendBack({
1469
- type: 'verificationFailed',
1470
- stage: 'verifyPropertyInstances',
1471
- error: error instanceof Error ? error : new Error(String(error)),
1472
- });
1473
- }
1474
- };
1475
- _verify().catch((error) => {
1476
- logger$2('Error in verifyPropertyInstancesInCache:', error);
1477
- sendBack({
1478
- type: 'verificationFailed',
1479
- stage: 'verifyPropertyInstances',
1480
- error: error instanceof Error ? error : new Error(String(error)),
1481
- });
1482
- });
1483
- return () => {
1484
- // Cleanup function (optional)
1485
- };
1486
- });
1487
-
1488
- const schemaMachine = setup({
1489
- types: {
1490
- context: {},
1491
- input: {},
1492
- events: {},
1493
- },
1494
- actors: {
1495
- validateSchema,
1496
- // Staged loading actors
1497
- checkExistingSchema,
1498
- writeSchemaToDb,
1499
- verifySchemaInDb,
1500
- writeModelsToDb,
1501
- verifyModelsInDb,
1502
- createModelInstances,
1503
- verifyModelInstancesInCache,
1504
- writePropertiesToDb,
1505
- verifyPropertiesInDb,
1506
- createPropertyInstances,
1507
- verifyPropertyInstancesInCache,
1508
- },
1509
- guards: {
1510
- isSchemaValid: ({ context }) => {
1511
- return !context._validationErrors || context._validationErrors.length === 0;
1512
- },
1513
- },
1514
- }).createMachine({
1515
- id: 'schema',
1516
- initial: 'loading',
1517
- context: ({ input }) => ({
1518
- schemaName: input.schemaName,
1519
- _isDraft: false,
1520
- _editedProperties: new Set(),
1521
- _validationErrors: undefined,
1522
- _liveQueryModelIds: [],
1523
- }),
1524
- on: {
1525
- updateContext: [{
1526
- actions: assign(({ context, event }) => {
1527
- const newContext = Object.assign({}, context);
1528
- // Check if this is only updating internal fields
1529
- Object.keys(event).every((key) => {
1530
- return key === 'type' || key.startsWith('_');
1531
- });
1532
- for (let i = 0; i < Object.keys(event).length; i++) {
1533
- const key = Object.keys(event)[i];
1534
- if (key === 'type') {
1535
- continue;
1536
- }
1537
- if (key in newContext || key.startsWith('_')) {
1538
- newContext[key] = event[key];
1539
- }
1540
- }
1541
- // Clear validation errors on context update (will be re-validated if needed)
1542
- newContext._validationErrors = undefined;
1543
- return newContext;
1544
- }),
1545
- // Only trigger validation if we're updating non-internal fields
1546
- guard: ({ event }) => {
1547
- return !Object.keys(event).every((key) => {
1548
- return key === 'type' || key.startsWith('_');
1549
- });
1550
- },
1551
- target: '.validating',
1552
- }, {
1553
- // Internal-only updates (e.g. the _dbUpdatedAt refresh after a draft save) are applied
1554
- // without re-validating. Dropping them made the next save's conflict check compare
1555
- // against a stale _dbUpdatedAt.
1556
- actions: assign(({ context, event }) => {
1557
- const newContext = Object.assign({}, context);
1558
- for (const key of Object.keys(event)) {
1559
- if (key.startsWith('_')) {
1560
- newContext[key] = event[key];
1561
- }
1562
- }
1563
- return newContext;
1564
- }),
1565
- }],
1566
- validateSchema: {
1567
- target: '.validating',
1568
- },
1569
- validationSuccess: {
1570
- actions: assign(({ context }) => ({
1571
- ...context,
1572
- _validationErrors: [],
1573
- })),
1574
- },
1575
- validationError: {
1576
- actions: assign(({ context, event }) => ({
1577
- ...context,
1578
- _validationErrors: event.errors,
1579
- })),
1580
- },
1581
- markAsDraft: {
1582
- actions: assign(({ context, event }) => ({
1583
- ...context,
1584
- _isDraft: true,
1585
- _isEdited: true,
1586
- _editedProperties: new Set([
1587
- ...(context._editedProperties || []),
1588
- event.propertyKey,
1589
- ]),
1590
- })),
1591
- },
1592
- clearDraft: {
1593
- actions: assign(({ context, event }) => {
1594
- const newContext = { ...context };
1595
- newContext._isDraft = false;
1596
- newContext._isEdited = false;
1597
- newContext._editedProperties = new Set();
1598
- // Update conflict detection metadata if provided
1599
- if (event._dbUpdatedAt !== undefined) {
1600
- newContext._dbUpdatedAt = event._dbUpdatedAt;
1601
- }
1602
- if (event._dbVersion !== undefined) {
1603
- newContext._dbVersion = event._dbVersion;
1604
- }
1605
- // saveNewVersion passes the schema it just wrote: the instance now is that version.
1606
- const saved = event.savedSchema;
1607
- if (saved) {
1608
- newContext.version = saved.version;
1609
- newContext.metadata = saved.metadata;
1610
- newContext.migrations = saved.migrations;
1611
- }
1612
- return newContext;
1613
- }),
1614
- },
1615
- destroyStarted: {
1616
- actions: assign({ _destroyError: null }),
1617
- },
1618
- destroyError: {
1619
- actions: assign(({ event }) => ({
1620
- _destroyError: event.error instanceof Error
1621
- ? { message: event.error.message, name: event.error.name }
1622
- : { message: String(event.error) },
1623
- })),
1624
- },
1625
- },
1626
- states: {
1627
- loading: {
1628
- initial: 'checkingExisting',
1629
- states: {
1630
- // Stage 0: Check for existing schema
1631
- checkingExisting: {
1632
- invoke: {
1633
- src: 'checkExistingSchema',
1634
- input: ({ context }) => ({ context }),
1635
- },
1636
- on: {
1637
- schemaFound: {
1638
- target: '#schema.idle',
1639
- actions: assign(({ event }) => {
1640
- return {
1641
- schemaName: event.schema.metadata.name,
1642
- $schema: event.schema.$schema,
1643
- version: event.schema.version,
1644
- metadata: event.schema.metadata,
1645
- models: event.schema.models,
1646
- enums: event.schema.enums,
1647
- migrations: event.schema.migrations,
1648
- nameMatch: event.schema.nameMatch,
1649
- _isDraft: false,
1650
- _isEdited: event.schemaRecord?.isEdited ?? false,
1651
- _editedProperties: new Set(),
1652
- _validationErrors: undefined,
1653
- id: event.schema.id, // schemaFileId (string)
1654
- _dbId: event.schemaRecord?.id, // Database integer ID
1655
- _loadedAt: event.loadedAt,
1656
- _dbVersion: event.dbVersion,
1657
- _dbUpdatedAt: event.dbUpdatedAt,
1658
- _liveQueryModelIds: event.modelIds || [],
1659
- _schemaRecord: event.schemaRecord,
1660
- };
1661
- }),
1662
- },
1663
- schemaNotFound: {
1664
- target: 'writingSchema',
1665
- },
1666
- },
1667
- },
1668
- // Stage 1: Write and verify schema
1669
- writingSchema: {
1670
- entry: assign({
1671
- _loadingStage: 'writingSchema',
1672
- }),
1673
- invoke: {
1674
- src: 'writeSchemaToDb',
1675
- input: ({ context }) => ({
1676
- schemaName: context.schemaName,
1677
- }),
1678
- },
1679
- on: {
1680
- schemaWritten: {
1681
- target: 'verifyingSchema',
1682
- actions: assign({
1683
- _schemaRecord: ({ event }) => event.schemaRecord,
1684
- id: ({ event }) => event.schema.id, // schemaFileId (string)
1685
- _dbId: ({ event }) => event.schemaRecord?.id, // Database integer ID
1686
- // A schema created in code has these only in the written record; without them
1687
- // validation (and so saveNewVersion) failed with "Schema metadata is required".
1688
- $schema: ({ context, event }) => context.$schema ?? event.schema.$schema,
1689
- version: ({ context, event }) => context.version ?? event.schema.version,
1690
- metadata: ({ context, event }) => context.metadata ?? event.schema.metadata,
1691
- enums: ({ context, event }) => context.enums ?? event.schema.enums,
1692
- migrations: ({ context, event }) => context.migrations ?? event.schema.migrations,
1693
- nameMatch: ({ context, event }) => context.nameMatch ?? event.schema.nameMatch,
1694
- }),
1695
- },
1696
- writeError: {
1697
- target: '#schema.error',
1698
- actions: assign({
1699
- _loadingError: ({ event }) => ({
1700
- stage: 'writingSchema',
1701
- error: event.error,
1702
- }),
1703
- }),
1704
- },
1705
- },
1706
- },
1707
- verifyingSchema: {
1708
- entry: assign({
1709
- _loadingStage: 'verifyingSchema',
1710
- }),
1711
- invoke: {
1712
- src: 'verifySchemaInDb',
1713
- input: ({ context }) => ({
1714
- schemaFileId: context.id,
1715
- expectedSchemaId: context._dbId,
1716
- }),
1717
- },
1718
- on: {
1719
- schemaVerified: {
1720
- target: 'writingModels',
1721
- actions: assign({
1722
- _dbId: ({ event }) => event.schemaId, // Store database integer ID
1723
- }),
1724
- },
1725
- verificationFailed: {
1726
- target: '#schema.error',
1727
- actions: assign({
1728
- _loadingError: ({ event }) => ({
1729
- stage: event.stage,
1730
- error: event.error,
1731
- }),
1732
- }),
1733
- },
1734
- },
1735
- },
1736
- // Stage 2: Write and verify models
1737
- writingModels: {
1738
- entry: assign({
1739
- _loadingStage: 'writingModels',
1740
- }),
1741
- invoke: {
1742
- src: 'writeModelsToDb',
1743
- input: ({ context }) => ({
1744
- schema: {
1745
- id: context.id,
1746
- models: context.models || {},
1747
- version: context.version || 1,
1748
- metadata: context.metadata || { name: context.schemaName, createdAt: '', updatedAt: '' },
1749
- },
1750
- schemaRecord: context._schemaRecord,
1751
- schemaName: context.schemaName,
1752
- }),
1753
- },
1754
- on: {
1755
- modelsWritten: {
1756
- target: 'verifyingModels',
1757
- actions: assign({
1758
- _modelIds: ({ event }) => event.modelIds,
1759
- }),
1760
- },
1761
- writeError: {
1762
- target: '#schema.error',
1763
- actions: assign({
1764
- _loadingError: ({ event }) => ({
1765
- stage: 'writingModels',
1766
- error: event.error,
1767
- }),
1768
- }),
1769
- },
1770
- },
1771
- },
1772
- verifyingModels: {
1773
- entry: assign({
1774
- _loadingStage: 'verifyingModels',
1775
- }),
1776
- invoke: {
1777
- src: 'verifyModelsInDb',
1778
- input: ({ context }) => ({
1779
- schemaId: context._dbId,
1780
- expectedModelIds: context._modelIds,
1781
- }),
1782
- },
1783
- on: {
1784
- modelsVerified: {
1785
- target: 'creatingModelInstances',
1786
- actions: assign({
1787
- _modelIds: ({ event }) => event.modelIds,
1788
- }),
1789
- },
1790
- verificationFailed: {
1791
- target: '#schema.error',
1792
- actions: assign({
1793
- _loadingError: ({ event }) => ({
1794
- stage: event.stage,
1795
- error: event.error,
1796
- }),
1797
- }),
1798
- },
1799
- },
1800
- },
1801
- // Stage 3: Create and verify model instances
1802
- creatingModelInstances: {
1803
- entry: assign({
1804
- _loadingStage: 'creatingModelInstances',
1805
- }),
1806
- invoke: {
1807
- src: 'createModelInstances',
1808
- input: ({ context }) => ({
1809
- modelIds: context._modelIds || [],
1810
- schemaName: context.schemaName,
1811
- }),
1812
- },
1813
- on: {
1814
- instancesCreated: {
1815
- target: 'verifyingModelInstances',
1816
- },
1817
- writeError: {
1818
- target: '#schema.error',
1819
- actions: assign({
1820
- _loadingError: ({ event }) => ({
1821
- stage: 'creatingModelInstances',
1822
- error: event.error,
1823
- }),
1824
- }),
1825
- },
1826
- },
1827
- },
1828
- verifyingModelInstances: {
1829
- entry: assign({
1830
- _loadingStage: 'verifyingModelInstances',
1831
- }),
1832
- invoke: {
1833
- src: 'verifyModelInstancesInCache',
1834
- input: ({ context }) => ({
1835
- modelIds: context._modelIds || [],
1836
- }),
1837
- },
1838
- on: {
1839
- instancesVerified: {
1840
- target: 'writingProperties',
1841
- },
1842
- verificationFailed: {
1843
- target: '#schema.error',
1844
- actions: assign({
1845
- _loadingError: ({ event }) => ({
1846
- stage: event.stage,
1847
- error: event.error,
1848
- }),
1849
- }),
1850
- },
1851
- },
1852
- },
1853
- // Stage 4: Write and verify properties
1854
- writingProperties: {
1855
- entry: assign({
1856
- _loadingStage: 'writingProperties',
1857
- }),
1858
- invoke: {
1859
- src: 'writePropertiesToDb',
1860
- input: ({ context }) => ({
1861
- modelIds: context._modelIds || [],
1862
- }),
1863
- },
1864
- on: {
1865
- propertiesWritten: {
1866
- target: 'verifyingProperties',
1867
- actions: assign({
1868
- _propertyIds: ({ event }) => event.propertyIds,
1869
- }),
1870
- },
1871
- writeError: {
1872
- target: '#schema.error',
1873
- actions: assign({
1874
- _loadingError: ({ event }) => ({
1875
- stage: 'writingProperties',
1876
- error: event.error,
1877
- }),
1878
- }),
1879
- },
1880
- },
1881
- },
1882
- verifyingProperties: {
1883
- entry: assign({
1884
- _loadingStage: 'verifyingProperties',
1885
- }),
1886
- invoke: {
1887
- src: 'verifyPropertiesInDb',
1888
- input: ({ context }) => ({
1889
- modelFileIds: context._modelIds,
1890
- expectedPropertyIds: context._propertyIds,
1891
- }),
1892
- },
1893
- on: {
1894
- propertiesVerified: {
1895
- target: 'creatingPropertyInstances',
1896
- actions: assign({
1897
- _propertyIds: ({ event }) => event.propertyIds,
1898
- }),
1899
- },
1900
- verificationFailed: {
1901
- target: '#schema.error',
1902
- actions: assign({
1903
- _loadingError: ({ event }) => ({
1904
- stage: event.stage,
1905
- error: event.error,
1906
- }),
1907
- }),
1908
- },
1909
- },
1910
- },
1911
- // Stage 5: Create and verify property instances
1912
- creatingPropertyInstances: {
1913
- entry: assign({
1914
- _loadingStage: 'creatingPropertyInstances',
1915
- }),
1916
- invoke: {
1917
- src: 'createPropertyInstances',
1918
- input: ({ context }) => ({
1919
- propertyIds: context._propertyIds || [],
1920
- modelIds: context._modelIds || [],
1921
- }),
1922
- },
1923
- on: {
1924
- instancesCreated: {
1925
- target: 'verifyingPropertyInstances',
1926
- },
1927
- writeError: {
1928
- target: '#schema.error',
1929
- actions: assign({
1930
- _loadingError: ({ event }) => ({
1931
- stage: 'creatingPropertyInstances',
1932
- error: event.error,
1933
- }),
1934
- }),
1935
- },
1936
- },
1937
- },
1938
- verifyingPropertyInstances: {
1939
- entry: assign({
1940
- _loadingStage: 'verifyingPropertyInstances',
1941
- }),
1942
- invoke: {
1943
- src: 'verifyPropertyInstancesInCache',
1944
- input: ({ context }) => ({
1945
- propertyIds: context._propertyIds || [],
1946
- }),
1947
- },
1948
- on: {
1949
- instancesVerified: {
1950
- target: '#schema.idle',
1951
- actions: assign({
1952
- _liveQueryModelIds: ({ context }) => context._modelIds || [],
1953
- _loadingStage: undefined,
1954
- }),
1955
- },
1956
- verificationFailed: {
1957
- target: '#schema.error',
1958
- actions: assign({
1959
- _loadingError: ({ event }) => ({
1960
- stage: event.stage,
1961
- error: event.error,
1962
- }),
1963
- }),
1964
- },
1965
- },
1966
- },
1967
- },
1968
- },
1969
- idle: {
1970
- on: {
1971
- validateSchema: {
1972
- target: 'validating',
1973
- },
1974
- reloadFromDb: {
1975
- target: 'loading',
1976
- },
1977
- },
1978
- // No `always: hasValidationErrors -> validating` here: validation is re-run when the context changes
1979
- // (updateContext) or on request (validateSchema). Re-entering while errors exist would loop
1980
- // forever on a schema that is still invalid.
1981
- },
1982
- validating: {
1983
- on: {
1984
- // These state-level handlers take precedence over the root ones, so they must store the result
1985
- // themselves.
1986
- validationSuccess: {
1987
- target: 'idle',
1988
- actions: assign(({ context }) => ({
1989
- ...context,
1990
- _validationErrors: [],
1991
- })),
1992
- },
1993
- validationError: {
1994
- target: 'idle',
1995
- actions: assign(({ context, event }) => ({
1996
- ...context,
1997
- _validationErrors: event.errors,
1998
- })),
1999
- },
2000
- },
2001
- invoke: {
2002
- src: 'validateSchema',
2003
- input: ({ context }) => ({ context }),
2004
- },
2005
- },
2006
- error: {},
2007
- },
2008
- });
2009
-
2010
- const logger$1 = debug('seedSdk:helpers:updateSchema');
2011
- // Schema reloads renameModelProperty deferred and that haven't finished yet
2012
- const deferredSchemaReloads = new Set();
2013
- /**
2014
- * Get the file path for a schema file
2015
- */
2016
- /**
2017
- * Sanitize a schema name to be filesystem-safe
2018
- * Replaces all special characters (except alphanumeric, hyphens, underscores) with underscores
2019
- * Converts spaces to underscores
2020
- * Removes leading/trailing underscores
2021
- *
2022
- * @param name - Schema name to sanitize
2023
- * @returns Sanitized name safe for use in filenames
2024
- */
2025
- const sanitizeSchemaName = (name) => {
2026
- return name
2027
- .replace(/[^a-zA-Z0-9\s_-]/g, '_') // Replace special chars (except spaces, hyphens, underscores) with underscore
2028
- .replace(/\s+/g, '_') // Convert spaces to underscores
2029
- .replace(/^_+|_+$/g, '') // Remove leading/trailing underscores
2030
- .replace(/_+/g, '_'); // Collapse multiple underscores to single
2031
- };
2032
- /**
2033
- * Get the full file path for a schema
2034
- * Format: {schemaFileId}_{schemaName}_v{version}.json
2035
- *
2036
- * The ID-first format ensures all files for a schema group together when sorted alphabetically.
2037
- *
2038
- * @param name - Schema name
2039
- * @param version - Schema version
2040
- * @param schemaFileId - Schema file ID (required)
2041
- */
2042
- const getSchemaFilePath = (name, version, schemaFileId) => {
2043
- const path = BaseFileManager.getPathModule();
2044
- const workingDir = BaseFileManager.getWorkingDir();
2045
- const sanitizedName = sanitizeSchemaName(name);
2046
- const filename = schemaFileId
2047
- ? `${schemaFileId}_${sanitizedName}_v${version}.json`
2048
- : `${sanitizedName}_v${version}.json`;
2049
- return path.join(workingDir, filename);
2050
- };
2051
- /**
2052
- * Get schemaFileId from database for a schema
2053
- * @param schemaName - Schema name
2054
- * @returns Schema file ID
2055
- * @throws Error if schema not found or missing schemaFileId
2056
- */
2057
- async function getSchemaFileId(schemaName) {
2058
- const db = BaseDb.getAppDb();
2059
- if (!db)
2060
- throw new Error('Database not available');
2061
- const dbSchema = await db
2062
- .select()
2063
- .from(schemas)
2064
- .where(eq(schemas.name, schemaName))
2065
- .orderBy(desc(schemas.version))
2066
- .limit(1);
2067
- if (dbSchema.length === 0 || !dbSchema[0].schemaFileId) {
2068
- throw new Error(`Schema ${schemaName} not found in database or missing schemaFileId`);
2069
- }
2070
- return dbSchema[0].schemaFileId;
2071
- }
2072
- /**
2073
- * Write the full schema to a new version file (e.g. when new models were added).
2074
- * Used when _editedProperties contains 'schema:models' and there are no property-level updates.
2075
- * @param schemaName - Schema name
2076
- * @param schema - Full schema object (e.g. from _buildModelsFromInstances)
2077
- * @returns The file path of the new schema version
2078
- */
2079
- async function writeFullSchemaNewVersion(schemaName, schema) {
2080
- const latestVersion = await getLatestSchemaVersion(schemaName);
2081
- const newVersion = latestVersion + 1;
2082
- const schemaWithNewVersion = {
2083
- ...schema,
2084
- version: newVersion,
2085
- metadata: {
2086
- ...schema.metadata,
2087
- updatedAt: new Date().toISOString(),
2088
- },
2089
- migrations: [
2090
- ...(schema.migrations || []),
2091
- {
2092
- version: newVersion,
2093
- timestamp: new Date().toISOString(),
2094
- description: 'New schema version (e.g. new models added)',
2095
- changes: [{ type: 'full_schema_write' }],
2096
- },
2097
- ],
2098
- };
2099
- const newFilePath = getSchemaFilePath(schemaName, newVersion, schema.id ?? (await getSchemaFileId(schemaName)));
2100
- const newContent = JSON.stringify(schemaWithNewVersion, null, 2);
2101
- await BaseFileManager.saveFile(newFilePath, newContent);
2102
- await BaseFileManager.waitForFileWithContent(newFilePath);
2103
- logger$1(`Created new schema version ${newVersion} for ${schemaName} at ${newFilePath}`);
2104
- return newFilePath;
2105
- }
2106
- /**
2107
- * Get model name from modelId
2108
- * @param modelId - The model ID to look up
2109
- * @returns The model name, or undefined if not found
2110
- */
2111
- async function getModelNameFromId(modelId) {
2112
- if (!modelId) {
2113
- return undefined;
2114
- }
2115
- const db = BaseDb.getAppDb();
2116
- if (!db) {
2117
- throw new Error('Database not found');
2118
- }
2119
- const modelRecords = await db
2120
- .select()
2121
- .from(models)
2122
- .where(eq(models.id, modelId))
2123
- .limit(1);
2124
- if (modelRecords.length === 0) {
2125
- return undefined;
2126
- }
2127
- return modelRecords[0].name;
2128
- }
2129
- /**
2130
- * Convert a TProperty/ModelPropertyMachineContext to SchemaPropertyUpdate format
2131
- * This function converts the internal property representation to the schema file format
2132
- * @param property - The TProperty instance to convert
2133
- * @param modelName - The name of the model this property belongs to
2134
- * @param propertyName - The name of the property
2135
- * @returns A SchemaPropertyUpdate object ready to be passed to updateModelProperties
2136
- */
2137
- async function convertPropertyToSchemaUpdate(property, modelName, propertyName) {
2138
- const updates = {};
2139
- // Convert dataType to type
2140
- if (property.dataType) {
2141
- updates.type = property.dataType;
2142
- }
2143
- // Handle Relation type
2144
- if (isDataType(property.dataType, ModelPropertyDataTypes.Relation)) {
2145
- if (property.ref) {
2146
- updates.model = property.ref;
2147
- }
2148
- else if (property.refModelId) {
2149
- // If ref is not set but refModelId is, get the model name from the database
2150
- const refModelName = await getModelNameFromId(property.refModelId);
2151
- if (refModelName) {
2152
- updates.model = refModelName;
2153
- }
2154
- }
2155
- }
2156
- // Handle List type
2157
- if (isDataType(property.dataType, ModelPropertyDataTypes.List)) {
2158
- if (property.refValueType) {
2159
- updates.refValueType = property.refValueType;
2160
- if (property.ref) {
2161
- updates.ref = property.ref;
2162
- }
2163
- else if (property.refModelId) {
2164
- // If ref is not set but refModelId is, get the model name from the database
2165
- const refModelName = await getModelNameFromId(property.refModelId);
2166
- if (refModelName) {
2167
- updates.ref = refModelName;
2168
- }
2169
- }
2170
- }
2171
- }
2172
- // Handle required (validation constraint)
2173
- if (property.required !== undefined) {
2174
- updates.required = property.required;
2175
- }
2176
- // Handle storage configuration (for Text properties with storage)
2177
- if (property.storageType || property.localStorageDir || property.filenameSuffix) {
2178
- const storageType = property.storageType || 'ItemStorage'; // Default to ItemStorage if not specified
2179
- updates.storage = {
2180
- type: storageType,
2181
- };
2182
- if (property.localStorageDir) {
2183
- updates.storage.path = property.localStorageDir;
2184
- }
2185
- if (property.filenameSuffix) {
2186
- updates.storage.extension = property.filenameSuffix;
2187
- }
2188
- }
2189
- return {
2190
- modelName,
2191
- propertyName,
2192
- updates,
2193
- };
2194
- }
2195
- /**
2196
- * Update model properties in a schema and create a new version
2197
- * @param schemaName - The name of the schema to update
2198
- * @param propertyUpdates - Array of property updates to apply
2199
- * @param modelUpdates - Optional array of model renames
2200
- * @returns The file path of the new schema version
2201
- * @throws Error if schema not found or updates are invalid
2202
- */
2203
- async function updateModelProperties(schemaName, propertyUpdates, modelUpdates) {
2204
- // Get the latest version of the schema
2205
- const latestVersion = await getLatestSchemaVersion(schemaName);
2206
- if (latestVersion === 0) {
2207
- throw new Error(`Schema ${schemaName} not found`);
2208
- }
2209
- // Get schemaFileId from database
2210
- const schemaFileId = await getSchemaFileId(schemaName);
2211
- // Load the latest schema file
2212
- const latestFilePath = getSchemaFilePath(schemaName, latestVersion, schemaFileId);
2213
- const content = await BaseFileManager.readFileAsString(latestFilePath);
2214
- const schemaFile = JSON.parse(content);
2215
- if (!schemaFile.$schema) {
2216
- throw new Error(`Schema file ${latestFilePath} is not a complete schema file`);
2217
- }
2218
- // Create a copy of the schema for the new version
2219
- // Preserve schema ID and all model/property IDs
2220
- const newVersion = latestVersion + 1;
2221
- const updatedSchema = {
2222
- ...schemaFile,
2223
- version: newVersion,
2224
- // Preserve schema ID from previous version
2225
- id: schemaFile.id,
2226
- metadata: {
2227
- ...schemaFile.metadata,
2228
- updatedAt: new Date().toISOString(),
2229
- },
2230
- // Deep copy models to preserve IDs
2231
- models: Object.fromEntries(Object.entries(schemaFile.models).map(([modelName, model]) => [
2232
- modelName,
2233
- {
2234
- ...model,
2235
- // Preserve model ID
2236
- id: model.id,
2237
- // Deep copy properties to preserve IDs
2238
- properties: Object.fromEntries(Object.entries(model.properties).map(([propName, prop]) => [
2239
- propName,
2240
- {
2241
- ...prop,
2242
- // Preserve property ID
2243
- id: prop.id,
2244
- },
2245
- ])),
2246
- },
2247
- ])),
2248
- migrations: [
2249
- ...schemaFile.migrations,
2250
- {
2251
- version: newVersion,
2252
- timestamp: new Date().toISOString(),
2253
- description: `Updated model properties: ${propertyUpdates.map(u => `${u.modelName}.${u.propertyName}`).join(', ')}`,
2254
- changes: propertyUpdates.map(update => ({
2255
- type: 'property_update',
2256
- modelName: update.modelName,
2257
- propertyName: update.propertyName,
2258
- updates: update.updates,
2259
- })),
2260
- },
2261
- ],
2262
- };
2263
- // Apply model renames first (if any)
2264
- if (modelUpdates && modelUpdates.length > 0) {
2265
- for (const modelUpdate of modelUpdates) {
2266
- if (updatedSchema.models[modelUpdate.oldName]) {
2267
- // Rename the model in the models object
2268
- updatedSchema.models[modelUpdate.newName] = updatedSchema.models[modelUpdate.oldName];
2269
- delete updatedSchema.models[modelUpdate.oldName];
2270
- // Update any property references to this model
2271
- for (const modelName in updatedSchema.models) {
2272
- const model = updatedSchema.models[modelName];
2273
- for (const propertyName in model.properties) {
2274
- const property = model.properties[propertyName];
2275
- if (property.model === modelUpdate.oldName) {
2276
- property.model = modelUpdate.newName;
2277
- }
2278
- if (property.ref === modelUpdate.oldName && (isDataType(property.type, ModelPropertyDataTypes.List) || isDataType(property.dataType, ModelPropertyDataTypes.List))) {
2279
- property.ref = modelUpdate.newName;
2280
- }
2281
- // Legacy: items.model
2282
- if (property.items?.model === modelUpdate.oldName) {
2283
- property.items.model = modelUpdate.newName;
2284
- }
2285
- }
2286
- }
2287
- // Add to migration changes
2288
- updatedSchema.migrations[updatedSchema.migrations.length - 1].changes.push({
2289
- type: 'model_rename',
2290
- oldName: modelUpdate.oldName,
2291
- newName: modelUpdate.newName,
2292
- });
2293
- }
2294
- }
2295
- }
2296
- // Apply property updates
2297
- for (const update of propertyUpdates) {
2298
- const model = updatedSchema.models[update.modelName];
2299
- if (!model) {
2300
- throw new Error(`Model ${update.modelName} not found in schema ${schemaName}`);
2301
- }
2302
- if (!model.properties[update.propertyName]) {
2303
- throw new Error(`Property ${update.propertyName} not found in model ${update.modelName} of schema ${schemaName}`);
2304
- }
2305
- // Update the property with new values
2306
- const property = model.properties[update.propertyName];
2307
- Object.assign(property, update.updates);
2308
- }
2309
- // Write the new schema version to file using ID-based naming (preferred)
2310
- const newFilePath = getSchemaFilePath(schemaName, newVersion, updatedSchema.id ?? undefined);
2311
- const newContent = JSON.stringify(updatedSchema, null, 2);
2312
- await BaseFileManager.saveFile(newFilePath, newContent);
2313
- // Wait for the file to be available with content (important for browser/OPFS where writes may not be immediately readable)
2314
- await BaseFileManager.waitForFileWithContent(newFilePath);
2315
- logger$1(`Created new schema version ${newVersion} for ${schemaName} at ${newFilePath}`);
2316
- // Load the new schema file to process models and add them to the database
2317
- // Extract model renames from migrations to pass to database update
2318
- const modelRenames = new Map();
2319
- const latestMigration = updatedSchema.migrations[updatedSchema.migrations.length - 1];
2320
- for (const change of latestMigration.changes) {
2321
- if (change.type === 'model_rename') {
2322
- modelRenames.set(change.oldName, change.newName);
2323
- }
2324
- }
2325
- // Load schema with model renames handled
2326
- await loadSchemaWithRenames(newFilePath, modelRenames);
2327
- return newFilePath;
2328
- }
2329
- /**
2330
- * Load a schema file and handle model renames in the database
2331
- * This is a helper function that processes model renames before loading
2332
- */
2333
- async function loadSchemaWithRenames(schemaFilePath, modelRenames) {
2334
- const content = await BaseFileManager.readFileAsString(schemaFilePath);
2335
- const schemaFile = JSON.parse(content);
2336
- if (!schemaFile.$schema) {
2337
- throw new Error(`File ${schemaFilePath} is not a complete schema file (missing $schema field).`);
2338
- }
2339
- const schemaName = schemaFile.metadata?.name;
2340
- const version = schemaFile.version;
2341
- if (!schemaName) {
2342
- throw new Error('Schema name is required in metadata.name');
2343
- }
2344
- // Convert to JsonImportSchema format for processing
2345
- // Remove id fields for JsonImportSchema format (they're not part of the import format)
2346
- const importData = {
2347
- name: schemaName,
2348
- models: Object.fromEntries(Object.entries(schemaFile.models).map(([modelName, model]) => [
2349
- modelName,
2350
- {
2351
- description: model.description,
2352
- properties: Object.fromEntries(Object.entries(model.properties).map(([propName, prop]) => [
2353
- propName,
2354
- {
2355
- type: prop.type || 'string', // Ensure type is present
2356
- ...prop,
2357
- // Remove id field for import format (not part of JsonImportSchema)
2358
- },
2359
- ])),
2360
- indexes: model.indexes,
2361
- },
2362
- ])),
2363
- };
2364
- // Generate schema ID if missing
2365
- if (!schemaFile.id) {
2366
- schemaFile.id = generateId();
2367
- logger$1('Generated schema ID for schema:', schemaFile.id);
2368
- }
2369
- // Extract schemaFileIds from JSON file and generate missing ones BEFORE creating models
2370
- // This ensures Model instances are created with correct IDs
2371
- const modelFileIds = new Map();
2372
- const propertyFileIds = new Map();
2373
- for (const [modelName, model] of Object.entries(schemaFile.models)) {
2374
- // Generate model ID if missing
2375
- if (!model.id) {
2376
- model.id = generateId();
2377
- logger$1(`Generated model ID for ${modelName}:`, model.id);
2378
- }
2379
- modelFileIds.set(modelName, model.id);
2380
- const propIds = new Map();
2381
- for (const [propName, prop] of Object.entries(model.properties)) {
2382
- // Generate property ID if missing
2383
- if (!prop.id) {
2384
- prop.id = generateId();
2385
- logger$1(`Generated property ID for ${modelName}.${propName}:`, prop.id);
2386
- }
2387
- propIds.set(propName, prop.id);
2388
- }
2389
- if (propIds.size > 0) {
2390
- propertyFileIds.set(modelName, propIds);
2391
- }
2392
- }
2393
- // Convert schema file metadata to schema input for database
2394
- const schemaInput = {
2395
- name: schemaName,
2396
- version,
2397
- schemaFileId: schemaFile.id || null,
2398
- schemaData: null,
2399
- isDraft: false,
2400
- isEdited: false,
2401
- createdAt: new Date(schemaFile.metadata.createdAt).getTime(),
2402
- updatedAt: new Date(schemaFile.metadata.updatedAt).getTime(),
2403
- };
2404
- // Use dynamic import to break circular dependency
2405
- const _mod_7 = await import('./index-Dx_GTdyQ.js');
2406
- const _ns_7 = _mod_7.bf;
2407
- const dbMod = _ns_7;
2408
- const { addSchemaToDb, addModelsToDb } = dbMod;
2409
- // Add schema to database with schemaFileId
2410
- const schemaRecord = await addSchemaToDb(schemaInput, schemaFile.id);
2411
- // A cached Model keeps the properties it was created with (Model.create ignores the ones passed),
2412
- // so addModelsToDb must take changed properties from this file, as in loadSchemaFromFile
2413
- const propertyDefinitions = new Map(Object.entries(importData.models).map(([modelName, modelDef]) => [
2414
- modelName,
2415
- jsonPropertiesToSchemaProperties(modelName, modelDef, propertyFileIds.get(modelName)),
2416
- ]));
2417
- // Convert JSON models to Model classes, passing modelFileIds and propertyFileIds so Model instances use correct IDs.
2418
- // addModelsToDb below writes their rows; the models adopt them once it has run.
2419
- const rowsWritten = createRowsWrittenSignal();
2420
- const modelDefinitions = await createModelsFromJson(importData, modelFileIds, propertyFileIds, rowsWritten.promise);
2421
- // Add models to database with model renames handled and schemaFileIds
2422
- await addModelsToDb(modelDefinitions, schemaRecord, modelRenames, {
2423
- schemaFileId: schemaFile.id,
2424
- modelFileIds,
2425
- propertyFileIds,
2426
- propertyDefinitions,
2427
- }).finally(rowsWritten.settle);
2428
- // Models are now Model instances, no registration needed
2429
- // They should be created via Model.create() and are accessible via Model static methods
2430
- for (const [modelName] of Object.entries(modelDefinitions)) {
2431
- logger$1('loadSchemaWithRenames - model available:', modelName);
2432
- }
2433
- return schemaFilePath;
2434
- }
2435
- /**
2436
- * Rename a property in a model
2437
- * This is a convenience function that updates the property name
2438
- * Note: This creates a new property and you may want to handle the old property separately
2439
- * @param schemaName - The name of the schema
2440
- * @param modelName - The name of the model
2441
- * @param oldPropertyName - The current property name
2442
- * @param newPropertyName - The new property name
2443
- * @returns The file path of the new schema version
2444
- */
2445
- async function renameModelProperty(schemaName, modelName, oldPropertyName, newPropertyName) {
2446
- // Get the latest version
2447
- const latestVersion = await getLatestSchemaVersion(schemaName);
2448
- if (latestVersion === 0) {
2449
- throw new Error(`Schema ${schemaName} not found`);
2450
- }
2451
- // Get schemaFileId from database
2452
- const schemaFileId = await getSchemaFileId(schemaName);
2453
- // Load the latest schema file
2454
- const latestFilePath = getSchemaFilePath(schemaName, latestVersion, schemaFileId);
2455
- const content = await BaseFileManager.readFileAsString(latestFilePath);
2456
- const schemaFile = JSON.parse(content);
2457
- if (!schemaFile.$schema) {
2458
- throw new Error(`Schema file ${latestFilePath} is not a complete schema file`);
2459
- }
2460
- const model = schemaFile.models[modelName];
2461
- if (!model) {
2462
- throw new Error(`Model ${modelName} not found in schema ${schemaName}`);
2463
- }
2464
- if (!model.properties[oldPropertyName]) {
2465
- throw new Error(`Property ${oldPropertyName} not found in model ${modelName} of schema ${schemaName}`);
2466
- }
2467
- if (model.properties[newPropertyName]) {
2468
- throw new Error(`Property ${newPropertyName} already exists in model ${modelName} of schema ${schemaName}`);
2469
- }
2470
- // Create new version with renamed property
2471
- // Preserve all IDs - when renaming, we keep the same property ID
2472
- const newVersion = latestVersion + 1;
2473
- const oldProperty = model.properties[oldPropertyName];
2474
- const updatedSchema = {
2475
- ...schemaFile,
2476
- version: newVersion,
2477
- id: schemaFile.id, // Preserve schema ID
2478
- metadata: {
2479
- ...schemaFile.metadata,
2480
- updatedAt: new Date().toISOString(),
2481
- },
2482
- models: {
2483
- ...schemaFile.models,
2484
- [modelName]: {
2485
- ...model,
2486
- id: model.id, // Preserve model ID
2487
- properties: {
2488
- ...Object.fromEntries(Object.entries(model.properties)
2489
- .filter(([name]) => name !== oldPropertyName)
2490
- .map(([name, prop]) => [name, { ...prop, id: prop.id }])),
2491
- [newPropertyName]: {
2492
- ...oldProperty,
2493
- id: oldProperty.id, // Preserve property ID when renaming
2494
- },
2495
- },
2496
- },
2497
- },
2498
- migrations: [
2499
- ...schemaFile.migrations,
2500
- {
2501
- version: newVersion,
2502
- timestamp: new Date().toISOString(),
2503
- description: `Renamed property ${modelName}.${oldPropertyName} to ${newPropertyName}`,
2504
- changes: [
2505
- {
2506
- type: 'property_rename',
2507
- modelName,
2508
- oldPropertyName,
2509
- newPropertyName,
2510
- },
2511
- ],
2512
- },
2513
- ],
2514
- };
2515
- // Remove the old property name
2516
- delete updatedSchema.models[modelName].properties[oldPropertyName];
2517
- // Write the new schema version using ID-based naming (preferred)
2518
- const newFilePath = getSchemaFilePath(schemaName, newVersion, updatedSchema.id ?? undefined);
2519
- const newContent = JSON.stringify(updatedSchema, null, 2);
2520
- // Ensure the directory exists before saving
2521
- const path = BaseFileManager.getPathModule();
2522
- const dir = path.dirname(newFilePath);
2523
- await BaseFileManager.createDirIfNotExists(dir);
2524
- await BaseFileManager.saveFile(newFilePath, newContent);
2525
- // Wait for the file to be available with content (important for browser/OPFS where writes may not be immediately readable)
2526
- await BaseFileManager.waitForFileWithContent(newFilePath);
2527
- logger$1(`Renamed property ${oldPropertyName} to ${newPropertyName} in schema ${schemaName} v${newVersion}`);
2528
- // Migrate metadata rows so ItemProperty values follow the new name (prevents duplicate ItemProperties)
2529
- const _mod_8 = await import('./index-Dx_GTdyQ.js');
2530
- const _ns_8 = _mod_8.bf;
2531
- const { migrateMetadataForPropertyRename } = _ns_8;
2532
- const migratedCount = await migrateMetadataForPropertyRename(modelName, oldPropertyName, newPropertyName, oldProperty.id, { schemaName });
2533
- if (migratedCount > 0) {
2534
- logger$1(`Migrated ${migratedCount} metadata rows for property rename`);
2535
- }
2536
- // Defer loadSchemaFromFile to avoid sync cascade: it triggers addSchemaToDb/addModelsToDb,
2537
- // which post to BroadcastChannel and cause React invalidates. Running that synchronously
2538
- // can interrupt the save flow and cause freezes (e.g. updateContext/refetch loops).
2539
- const reload = new Promise((resolve) => setTimeout(resolve, 100))
2540
- .then(() => loadSchemaFromFile(newFilePath))
2541
- .then(() => { }, (err) => {
2542
- logger$1('Deferred loadSchemaFromFile after rename failed:', err);
2543
- })
2544
- .finally(() => deferredSchemaReloads.delete(reload));
2545
- deferredSchemaReloads.add(reload);
2546
- return newFilePath;
2547
- }
2548
-
2549
- var updateSchema = /*#__PURE__*/Object.freeze({
2550
- __proto__: null,
2551
- convertPropertyToSchemaUpdate: convertPropertyToSchemaUpdate,
2552
- getModelNameFromId: getModelNameFromId,
2553
- renameModelProperty: renameModelProperty,
2554
- updateModelProperties: updateModelProperties,
2555
- writeFullSchemaNewVersion: writeFullSchemaNewVersion
2556
- });
2557
-
2558
- const logger = debug('seedSdk:schema:saveNewVersion');
2559
- const saveDraftLogger = debug('seedSdk:schema:saveDraftToDb');
2560
- // Non-serializable resources per Schema instance (subscriptions, timers, etc.)
2561
- const schemaInstanceState = new ActorKeyedState();
2562
- // Cache client initialization state globally to avoid repeated checks
2563
- let cachedClientInitialized = null;
2564
- let clientCheckTime = 0;
2565
- const CLIENT_CHECK_CACHE_MS = 50; // Cache for 50ms to avoid excessive checks (reduced from 100ms for faster updates)
2566
- // Define tracked properties for the Proxy
2567
- // These properties will be read from/written to the actor context
2568
- const TRACKED_PROPERTIES = [
2569
- 'id', // schemaFileId (string) - public ID
2570
- '$schema',
2571
- 'version',
2572
- 'metadata',
2573
- 'enums',
2574
- 'migrations',
2575
- 'name', // metadata.name (flattened)
2576
- 'createdAt', // metadata.createdAt (flattened)
2577
- 'updatedAt', // metadata.updatedAt (flattened)
2578
- ];
2579
- class Schema {
2580
- constructor(schemaName) {
2581
- const serviceInput = {
2582
- schemaName,
2583
- };
2584
- this._service = createActor(schemaMachine, {
2585
- input: serviceInput,
2586
- });
2587
- this._service.start();
2588
- // Initialize instance state in WeakMap (only non-serializable resources)
2589
- schemaInstanceState.set(this, {
2590
- liveQuerySubscription: null,
2591
- });
2592
- // Set up liveQuery subscription for cross-instance model updates
2593
- // This will be initialized once we have the schemaId
2594
- this._setupLiveQuerySubscription();
2595
- // Subscribe to schema context changes to update cache keys when schemaFileId becomes available
2596
- this._service.subscribe((snapshot) => {
2597
- // Update cache to use id (schemaFileId) as key once we have it (only once)
2598
- if (snapshot.value === 'idle' && snapshot.context.metadata?.name && snapshot.context.id) {
2599
- // Use a static Set to track which schemas have had their cache key updated
2600
- const cacheKeyUpdatedSet = Schema._cacheKeyUpdatedSet || new Set();
2601
- if (!Schema._cacheKeyUpdatedSet) {
2602
- Schema._cacheKeyUpdatedSet = cacheKeyUpdatedSet;
2603
- }
2604
- if (!cacheKeyUpdatedSet.has(snapshot.context.id)) {
2605
- Schema._updateCacheKey(snapshot.context.schemaName, snapshot.context.id);
2606
- cacheKeyUpdatedSet.add(snapshot.context.id);
2607
- }
2608
- }
2609
- });
2610
- // Note: Property getters/setters are now handled by the Proxy in create()
2611
- }
2612
- static create(schemaName, options) {
2613
- if (!schemaName) {
2614
- throw new Error('Schema name is required');
2615
- }
2616
- const waitForReady = options?.waitForReady !== false;
2617
- const readyTimeout = options?.readyTimeout ?? DEFAULT_READY_TIMEOUT_MS;
2618
- const holds = options?.hold === false ? 0 : 1;
2619
- // First, check if we have an instance cached by name
2620
- const cached = this.instanceCacheByName.get(schemaName);
2621
- if (cached) {
2622
- cached.refCount += holds;
2623
- const { instance } = cached;
2624
- if (!waitForReady)
2625
- return instance;
2626
- return waitForEntityIdle(instance, { timeout: readyTimeout }).then(() => instance);
2627
- }
2628
- // Deduplicate concurrent creates for same schema (prevents many parallel checkExistingSchema loads).
2629
- // The name cache is set before a pending load is recorded, so a hold was already taken above.
2630
- if (waitForReady) {
2631
- const pending = this.pendingSchemaByName.get(schemaName);
2632
- if (pending) {
2633
- return pending;
2634
- }
2635
- }
2636
- // Create new instance
2637
- const newInstance = new this(schemaName);
2638
- // Wrap instance in Proxy for reactive property access
2639
- const proxiedInstance = createReactiveProxy({
2640
- instance: newInstance,
2641
- service: newInstance._service,
2642
- trackedProperties: TRACKED_PROPERTIES,
2643
- getContext: (instance) => {
2644
- // Handle special cases like metadata.name, models array conversion
2645
- const context = instance._getSnapshotContext();
2646
- // Get the schema name, ensuring it's never the ID
2647
- // Prefer metadata.name, then schemaName (but only if it's not the ID)
2648
- const schemaFileId = context.id; // id is now the schemaFileId (string)
2649
- let name = context.metadata?.name;
2650
- // If metadata.name is not available, use schemaName but only if it's not the ID
2651
- // This prevents returning the ID when the schema is still loading
2652
- if (!name) {
2653
- name = (context.schemaName && context.schemaName !== schemaFileId)
2654
- ? context.schemaName
2655
- : undefined;
2656
- }
2657
- // Final fallback - if we still don't have a name, use schemaName (even if it might be the ID)
2658
- // This handles edge cases during loading
2659
- if (!name) {
2660
- name = context.schemaName;
2661
- }
2662
- // CRITICAL: Always create a new metadata object to ensure React detects changes
2663
- // Even if the metadata content is the same, we need a new reference so React re-renders
2664
- const metadata = context.metadata ? {
2665
- ...context.metadata,
2666
- } : undefined;
2667
- return {
2668
- ...context,
2669
- // Always return a new metadata object reference so React detects changes
2670
- metadata,
2671
- // Flatten metadata properties to top level for convenience
2672
- // Fall back to schemaName if metadata.name is not available (handles loading states)
2673
- name,
2674
- createdAt: context.metadata?.createdAt,
2675
- updatedAt: context.metadata?.updatedAt,
2676
- };
2677
- },
2678
- sendUpdate: (instance, prop, value) => {
2679
- // Handle special property updates
2680
- if (prop === 'name') {
2681
- // Update both metadata.name and schemaName
2682
- const context = instance._getSnapshotContext();
2683
- const currentMetadata = context.metadata || {
2684
- name: schemaName,
2685
- createdAt: new Date().toISOString(),
2686
- updatedAt: new Date().toISOString(),
2687
- };
2688
- const oldName = currentMetadata.name || context.schemaName;
2689
- const newName = value;
2690
- // Only update if the name actually changed
2691
- if (oldName !== newName) {
2692
- logger(`Updating schema name from "${oldName}" to "${newName}"`);
2693
- // Check if service is still running before sending events
2694
- let snapshot = instance._service.getSnapshot();
2695
- const wasServiceStopped = snapshot.status === 'stopped';
2696
- if (wasServiceStopped) {
2697
- // CRITICAL: Update the schemaName in the service context BEFORE restarting
2698
- // This ensures that when loadOrCreateSchema runs, it looks for the schema with the NEW name
2699
- // We need to do this by sending updateContext to a running service, but if it's stopped,
2700
- // we need to restart it first, update, then it will reload with the new name
2701
- logger(`Service is stopped, will update schemaName to "${newName}" then restart`);
2702
- // Start the service first
2703
- instance._service.start();
2704
- // Immediately update the schemaName so loadOrCreateSchema uses the new name
2705
- // This must happen before loadOrCreateSchema runs
2706
- instance._service.send({
2707
- type: 'updateContext',
2708
- schemaName: newName,
2709
- });
2710
- logger(`Updated schemaName to "${newName}" before loadOrCreateSchema runs`);
2711
- // Re-check snapshot after restart - service might be loading or already idle
2712
- snapshot = instance._service.getSnapshot();
2713
- }
2714
- // Build the update event with the new name
2715
- const updateEvent = {
2716
- type: 'updateContext',
2717
- schemaName: newName,
2718
- metadata: {
2719
- ...(currentMetadata || {
2720
- name: newName,
2721
- createdAt: new Date().toISOString(),
2722
- updatedAt: new Date().toISOString(),
2723
- }),
2724
- name: newName, // Always use the new name
2725
- updatedAt: new Date().toISOString(), // Always update timestamp
2726
- },
2727
- };
2728
- // Check current state after potential restart
2729
- const currentState = snapshot.value;
2730
- // Check if state is a loading state object (XState v5 nested states)
2731
- const isServiceLoading = typeof currentState === 'object' && 'loading' in currentState;
2732
- // If service is loading (or was just restarted and might be loading), wait for it to finish
2733
- // Otherwise loadOrCreateSchemaSuccess might overwrite our update with old data
2734
- if (isServiceLoading || wasServiceStopped) {
2735
- logger(`Service is ${isServiceLoading ? 'loading' : 'was stopped'}, will send updateContext after loading completes (current state: ${currentState})`);
2736
- // Helper function to send the update
2737
- const sendUpdate = () => {
2738
- logger(`Sending updateContext with newName="${newName}"`);
2739
- instance._service.send(updateEvent);
2740
- // Verify the update
2741
- setTimeout(() => {
2742
- const verifySnapshot = instance._service.getSnapshot();
2743
- logger(`After updateContext: context schemaName="${verifySnapshot.context.schemaName}", metadata.name="${verifySnapshot.context.metadata?.name}"`);
2744
- }, 0);
2745
- };
2746
- // Check if already idle (might have loaded synchronously)
2747
- if (currentState === 'idle') {
2748
- // Service is already idle, but loadOrCreateSchemaSuccess might have just fired
2749
- // Use a small delay to ensure loadOrCreateSchemaSuccess has been processed
2750
- logger(`Service is already idle after restart, will send updateContext after brief delay to ensure loadOrCreateSchemaSuccess processed`);
2751
- setTimeout(() => {
2752
- sendUpdate();
2753
- }, 10);
2754
- }
2755
- else {
2756
- // Subscribe to wait for loading to complete, then send the update
2757
- // Use a small delay after idle to ensure loadOrCreateSchemaSuccess has been processed
2758
- const loadingSubscription = instance._service.subscribe((snapshot) => {
2759
- if (snapshot.value === 'idle') {
2760
- loadingSubscription.unsubscribe();
2761
- logger(`Service finished loading, will send updateContext with newName="${newName}" after brief delay`);
2762
- setTimeout(() => {
2763
- sendUpdate();
2764
- }, 10);
2765
- }
2766
- else if (snapshot.value === 'error') {
2767
- loadingSubscription.unsubscribe();
2768
- logger(`Service failed to load, cannot update context`);
2769
- }
2770
- });
2771
- }
2772
- }
2773
- else {
2774
- // Service is already idle, send update immediately
2775
- logger(`Service is already idle, sending updateContext immediately: newName="${newName}", current context schemaName="${snapshot.context.schemaName}"`);
2776
- instance._service.send(updateEvent);
2777
- // Verify the update
2778
- setTimeout(() => {
2779
- const verifySnapshot = instance._service.getSnapshot();
2780
- logger(`After updateContext: context schemaName="${verifySnapshot.context.schemaName}", metadata.name="${verifySnapshot.context.metadata?.name}"`);
2781
- }, 0);
2782
- }
2783
- // Mark schema as draft when name changes
2784
- instance._service.send({
2785
- type: 'markAsDraft',
2786
- propertyKey: 'schema:name', // Special key for schema name changes
2787
- });
2788
- // Save draft to database immediately so changes persist
2789
- saveDraftLogger(`Name change detected, calling _saveDraftToDb (oldName: "${oldName}", newName: "${newName}")`);
2790
- trackDraftSave(instance._service, instance._saveDraftToDb(oldName, newName), `Schema "${newName}"`);
2791
- }
2792
- else {
2793
- logger(`Schema name unchanged: "${oldName}"`);
2794
- }
2795
- }
2796
- else if (prop === 'models') {
2797
- // Models are read-only, computed from Model instances. Array assignment was removed because
2798
- // the draft save could run before the new Model instances existed, losing them.
2799
- throw new Error('Cannot set schema.models directly. Models are computed from Model instances; ' +
2800
- 'use Model.create(modelName, schema, { properties }) to add one.');
2801
- }
2802
- else if (prop === 'createdAt' || prop === 'updatedAt') {
2803
- // Update metadata object
2804
- const metadataContext = newInstance._getSnapshotContext();
2805
- const currentMetadata = metadataContext.metadata || {
2806
- name: schemaName,
2807
- createdAt: new Date().toISOString(),
2808
- updatedAt: new Date().toISOString(),
2809
- };
2810
- newInstance._service.send({
2811
- type: 'updateContext',
2812
- metadata: {
2813
- ...currentMetadata,
2814
- [prop]: value,
2815
- updatedAt: new Date().toISOString(),
2816
- },
2817
- });
2818
- }
2819
- else {
2820
- // Standard property update
2821
- newInstance._service.send({
2822
- type: 'updateContext',
2823
- [prop]: value,
2824
- });
2825
- // Mark schema as draft for other property changes
2826
- newInstance._service.send({
2827
- type: 'markAsDraft',
2828
- propertyKey: prop,
2829
- });
2830
- // Save draft to database for tracked properties
2831
- if (TRACKED_PROPERTIES.includes(prop)) {
2832
- const trackedPropContext = newInstance._getSnapshotContext();
2833
- const schemaName = trackedPropContext.metadata?.name || trackedPropContext.schemaName;
2834
- trackDraftSave(newInstance._service, newInstance._saveDraftToDb(schemaName, schemaName), `Schema "${schemaName}"`);
2835
- }
2836
- }
2837
- },
2838
- });
2839
- // Cache by name initially (will be moved to id-based cache once schemaFileId is available)
2840
- this.instanceCacheByName.set(schemaName, {
2841
- instance: proxiedInstance,
2842
- refCount: holds,
2843
- });
2844
- const schema = proxiedInstance;
2845
- if (!waitForReady)
2846
- return schema;
2847
- const readyPromise = waitForEntityIdle(schema, { timeout: readyTimeout }).then(() => schema);
2848
- this.pendingSchemaByName.set(schemaName, readyPromise);
2849
- // Clear on both outcomes without a .finally() chain: that derived promise would reject unhandled
2850
- // whenever readyPromise does, even when the caller handles the rejection.
2851
- const clearPending = () => {
2852
- this.pendingSchemaByName.delete(schemaName);
2853
- };
2854
- readyPromise.then(clearPending, clearPending);
2855
- return readyPromise;
2856
- }
2857
- /**
2858
- * Update the cache to use schemaFileId as the key instead of schemaName
2859
- * This should be called once the schema is loaded and we have the schemaFileId
2860
- * We keep the instance in BOTH caches for efficient lookups by either name or ID
2861
- */
2862
- static _updateCacheKey(schemaName, schemaFileId) {
2863
- // If we already have an entry with this id, skip (already processed)
2864
- if (this.instanceCacheById.has(schemaFileId)) {
2865
- return;
2866
- }
2867
- // If we have an instance cached by name, also add it to id-based cache
2868
- // Keep it in both caches for efficient lookups, as one entry: one hold count for both keys
2869
- if (this.instanceCacheByName.has(schemaName)) {
2870
- const cacheEntry = this.instanceCacheByName.get(schemaName);
2871
- // Add to ID cache (don't remove from name cache)
2872
- this.instanceCacheById.set(schemaFileId, cacheEntry);
2873
- logger(`Added schema cache entry to id "${schemaFileId}" (kept in name cache: "${schemaName}")`);
2874
- }
2875
- }
2876
- /**
2877
- * Get schema instance by schemaFileId (preferred method)
2878
- * Returns null if not found in cache
2879
- */
2880
- static getById(schemaFileId) {
2881
- const entry = this.instanceCacheById.get(schemaFileId);
2882
- if (!entry)
2883
- return null;
2884
- entry.refCount += 1;
2885
- return entry.instance;
2886
- }
2887
- /**
2888
- * The cached schema with this file id or name, without taking a hold. For SDK code that reads
2889
- * the schema and doesn't hand it to a caller; callers that keep it use `create` or `find`.
2890
- */
2891
- static peek(schemaFileIdOrName) {
2892
- return (this.instanceCacheById.get(schemaFileIdOrName)?.instance ??
2893
- this.instanceCacheByName.get(schemaFileIdOrName)?.instance);
2894
- }
2895
- /**
2896
- * Clear all cached Schema instances.
2897
- * This is primarily useful for test cleanup.
2898
- * All cached instances will be unloaded and removed from both caches.
2899
- */
2900
- static clearCache() {
2901
- // Collect all unique instances from both caches
2902
- const instances = new Set();
2903
- // Collect from ID-based cache
2904
- for (const { instance } of this.instanceCacheById.values()) {
2905
- instances.add(instance);
2906
- }
2907
- // Collect from name-based cache
2908
- for (const { instance } of this.instanceCacheByName.values()) {
2909
- instances.add(instance);
2910
- }
2911
- // Unload all instances (this will properly clean up services and state)
2912
- for (const instance of instances) {
2913
- try {
2914
- instance.unload();
2915
- }
2916
- catch {
2917
- // Ignore errors during cleanup (instance might already be unloaded)
2918
- }
2919
- }
2920
- // Clear both caches and pending map
2921
- this.instanceCacheById.clear();
2922
- this.instanceCacheByName.clear();
2923
- this.pendingSchemaByName.clear();
2924
- }
2925
- /**
2926
- * Create or get schema instance by schemaFileId
2927
- * Queries the database to find the schema name if not cached
2928
- * @param schemaFileId - The schema file ID
2929
- * @returns Schema instance, with a hold taken (unless `hold: false`)
2930
- */
2931
- static async createById(schemaFileId,
2932
- /** @internal `hold: false` as for `create` */
2933
- options) {
2934
- if (!schemaFileId) {
2935
- throw new Error('Schema file ID is required');
2936
- }
2937
- // First, check if we have an instance cached by ID
2938
- const cachedInstance = options?.hold === false ? this.peek(schemaFileId) : this.getById(schemaFileId);
2939
- if (cachedInstance) {
2940
- return cachedInstance;
2941
- }
2942
- // Query database to get schema name from ID
2943
- const db = BaseDb.getAppDb();
2944
- if (!db) {
2945
- throw new Error('Database not available');
2946
- }
2947
- const dbSchemas = await db
2948
- .select()
2949
- .from(schemas)
2950
- .where(eq(schemas.schemaFileId, schemaFileId))
2951
- .orderBy(desc(schemas.version))
2952
- .limit(1);
2953
- if (dbSchemas.length === 0) {
2954
- throw new Error(`Schema with ID "${schemaFileId}" not found in database`);
2955
- }
2956
- const dbSchema = dbSchemas[0];
2957
- const schemaName = dbSchema.name;
2958
- if (!schemaName) {
2959
- throw new Error(`Schema with ID "${schemaFileId}" has no name in database`);
2960
- }
2961
- // Create schema using the name (this will load from database/file)
2962
- return this.create(schemaName, options?.hold === false ? { hold: false } : undefined);
2963
- }
2964
- /**
2965
- * Find schema instance by schemaFileId
2966
- * Waits for the schema to be fully loaded (idle state) by default
2967
- * @param options - Find options including schemaFileId and wait configuration
2968
- * @returns Schema instance if found, undefined otherwise
2969
- */
2970
- static async find({ schemaFileId, waitForReady = true, readyTimeout = DEFAULT_READY_TIMEOUT_MS, }) {
2971
- if (!schemaFileId) {
2972
- return undefined;
2973
- }
2974
- try {
2975
- return await findEntity({
2976
- getById: (id) => Schema.getById(id) ?? undefined,
2977
- createById: (id) => Schema.createById(id),
2978
- }, { id: schemaFileId }, {
2979
- waitForReady,
2980
- readyTimeout,
2981
- });
2982
- }
2983
- catch {
2984
- return undefined;
2985
- }
2986
- }
2987
- /**
2988
- * Get instantiated Schema objects for all schemas (from database and files)
2989
- * By default, returns only the latest version of each schema and excludes the internal Seed Protocol schema.
2990
- * Uses loadAllSchemasFromDb() as the single source of truth, which intelligently merges database and file data.
2991
- * Takes no holds on the instances it returns: a caller that keeps one uses `Schema.create` or `Schema.find`.
2992
- *
2993
- * @param options - Configuration options
2994
- * @param options.includeAllVersions - If true, returns all versions of each schema. Default: false
2995
- * @param options.includeInternal - If true, includes the internal Seed Protocol schema. Default: false
2996
- * @returns Array of Schema instances
2997
- */
2998
- static async all(options = {}) {
2999
- const { includeAllVersions = false, includeInternal = false, waitForReady = false, readyTimeout = DEFAULT_READY_TIMEOUT_MS, } = options;
3000
- try {
3001
- // Use loadAllSchemasFromDb as single source of truth
3002
- // This intelligently merges database and file data, including drafts
3003
- const allSchemasData = await loadAllSchemasFromDb();
3004
- // Filter internal schemas unless explicitly included
3005
- let filteredSchemas = includeInternal
3006
- ? allSchemasData
3007
- : allSchemasData.filter(schemaData => {
3008
- const schema = schemaData.schema;
3009
- // Check if this is an internal schema by name or ID
3010
- return !isInternalSchema(schema.metadata?.name || '', schema.id);
3011
- });
3012
- // Filter to latest versions if needed
3013
- if (!includeAllVersions) {
3014
- // Group by schema name and keep only the latest version of each
3015
- const schemaMap = new Map();
3016
- for (const schemaData of filteredSchemas) {
3017
- const schemaName = schemaData.schema.metadata?.name || '';
3018
- if (!schemaName)
3019
- continue;
3020
- const existing = schemaMap.get(schemaName);
3021
- if (!existing || schemaData.schema.version > existing.schema.version) {
3022
- schemaMap.set(schemaName, schemaData);
3023
- }
3024
- else if (schemaData.schema.version === existing.schema.version) {
3025
- // If versions are equal, use updatedAt as tiebreaker
3026
- const currentUpdatedAt = new Date(schemaData.schema.metadata?.updatedAt || 0).getTime();
3027
- const existingUpdatedAt = new Date(existing.schema.metadata?.updatedAt || 0).getTime();
3028
- if (currentUpdatedAt > existingUpdatedAt) {
3029
- schemaMap.set(schemaName, schemaData);
3030
- }
3031
- }
3032
- }
3033
- filteredSchemas = Array.from(schemaMap.values());
3034
- }
3035
- // Create Schema instances using the schemaFileId from filtered data to ensure correct version
3036
- const schemaInstances = [];
3037
- const processedSchemaNames = new Set();
3038
- for (const schemaData of filteredSchemas) {
3039
- const schemaName = schemaData.schema.metadata?.name;
3040
- const schemaFileId = schemaData.schema.id;
3041
- const expectedVersion = schemaData.schema.version;
3042
- if (!schemaName)
3043
- continue;
3044
- // Skip if we've already processed this schema name (to avoid duplicates)
3045
- if (processedSchemaNames.has(schemaName)) {
3046
- continue;
3047
- }
3048
- processedSchemaNames.add(schemaName);
3049
- // Try to use schemaFileId to ensure we load the correct version
3050
- // First check if we have a cached instance by schemaFileId. Schema.all takes no holds: it
3051
- // lists schemas (useSchemas caches the list and never unloads it).
3052
- if (schemaFileId) {
3053
- const cachedById = this.peek(schemaFileId);
3054
- if (cachedById) {
3055
- // Verify the cached instance has the correct version
3056
- const cachedContext = cachedById.getService().getSnapshot().context;
3057
- if (cachedContext.version === expectedVersion) {
3058
- schemaInstances.push(cachedById);
3059
- continue;
3060
- }
3061
- }
3062
- }
3063
- // If no cached instance by ID, check by name but verify version
3064
- const cachedByName = this.instanceCacheByName.get(schemaName);
3065
- if (cachedByName) {
3066
- const cachedContext = cachedByName.instance.getService().getSnapshot().context;
3067
- // Only use cached instance if it has the correct version
3068
- if (cachedContext.version === expectedVersion && (!schemaFileId || cachedContext.id === schemaFileId)) {
3069
- schemaInstances.push(cachedByName.instance);
3070
- continue;
3071
- }
3072
- }
3073
- // Create new instance - it will load from database
3074
- // The loadOrCreateSchema actor should query by name and get the latest version
3075
- // But to ensure we get the correct version, we'll use createById if we have the schemaFileId
3076
- if (schemaFileId) {
3077
- try {
3078
- const instance = await this.createById(schemaFileId, { hold: false });
3079
- schemaInstances.push(instance);
3080
- }
3081
- catch {
3082
- // Fallback to creating by name if createById fails
3083
- schemaInstances.push(this.create(schemaName, { waitForReady: false, hold: false }));
3084
- }
3085
- }
3086
- else {
3087
- schemaInstances.push(this.create(schemaName, { waitForReady: false, hold: false }));
3088
- }
3089
- }
3090
- if (waitForReady && schemaInstances.length > 0) {
3091
- await Promise.all(schemaInstances.map((s) => waitForEntityIdle(s, {
3092
- timeout: readyTimeout,
3093
- })));
3094
- }
3095
- return schemaInstances;
3096
- }
3097
- catch (error) {
3098
- // Fallback to file-based approach if database is unavailable
3099
- // This maintains backward compatibility for environments without database
3100
- logger(`Error loading schemas from database, falling back to files: ${error instanceof Error ? error.message : String(error)}`);
3101
- // Use existing file-based implementation as fallback
3102
- const allSchemaFiles = await listCompleteSchemaFiles();
3103
- // Filter out internal schemas unless explicitly included
3104
- const filteredSchemas = includeInternal
3105
- ? allSchemaFiles
3106
- : allSchemaFiles.filter(schema => {
3107
- // Check if this is an internal schema by name or ID
3108
- return !isInternalSchema(schema.name, schema.schemaFileId);
3109
- });
3110
- // If includeAllVersions is false, filter to only latest version of each schema
3111
- const schemasToUse = includeAllVersions
3112
- ? filteredSchemas
3113
- : (() => {
3114
- // Group by schema name and keep only the latest version of each
3115
- const schemaMap = new Map();
3116
- for (const schema of filteredSchemas) {
3117
- const existing = schemaMap.get(schema.name);
3118
- if (!existing || schema.version > existing.version) {
3119
- schemaMap.set(schema.name, schema);
3120
- }
3121
- }
3122
- return Array.from(schemaMap.values());
3123
- })();
3124
- // Get unique schema names (one instance per schema name)
3125
- const uniqueSchemaNames = new Set();
3126
- for (const schema of schemasToUse) {
3127
- uniqueSchemaNames.add(schema.name);
3128
- }
3129
- // Create Schema instances for each unique schema name
3130
- const schemaInstances = [];
3131
- for (const schemaName of uniqueSchemaNames) {
3132
- schemaInstances.push(this.create(schemaName, { waitForReady: false, hold: false }));
3133
- }
3134
- if (waitForReady && schemaInstances.length > 0) {
3135
- await Promise.all(schemaInstances.map((s) => waitForEntityIdle(s, {
3136
- timeout: readyTimeout,
3137
- })));
3138
- }
3139
- return schemaInstances;
3140
- }
3141
- }
3142
- getService() {
3143
- return this._service;
3144
- }
3145
- _getSnapshot() {
3146
- return this._service.getSnapshot();
3147
- }
3148
- _getSnapshotContext() {
3149
- return this._getSnapshot().context;
3150
- }
3151
- get schemaName() {
3152
- const context = this._getSnapshotContext();
3153
- const schemaFileId = context.id; // id is now the schemaFileId (string)
3154
- // Prefer metadata.name if available (most reliable)
3155
- if (context.metadata?.name) {
3156
- return context.metadata.name;
3157
- }
3158
- // If metadata.name is not available, use schemaName but only if it's not the ID
3159
- // This prevents returning the ID when the schema is still loading or if an ID was mistakenly passed
3160
- if (context.schemaName && context.schemaName !== schemaFileId) {
3161
- return context.schemaName;
3162
- }
3163
- // Final fallback - return schemaName even if it might be the ID
3164
- // This handles edge cases during loading, but should be rare
3165
- return context.schemaName || '';
3166
- }
3167
- get schemaFileId() {
3168
- return this._getSnapshotContext().id; // id is now the schemaFileId (string)
3169
- }
3170
- get id() {
3171
- return this._getSnapshotContext().id; // id is now the schemaFileId (string)
3172
- }
3173
- /**
3174
- * Returns Model instances for this schema
3175
- * This is a computed property that reads from the service context
3176
- * Note: This is NOT reactive - use useModels() hook for reactivity
3177
- */
3178
- get models() {
3179
- const context = this._getSnapshotContext();
3180
- const schemaName = context.schemaName;
3181
- // Get model IDs from service context (reactive state)
3182
- const liveQueryIds = context._liveQueryModelIds || [];
3183
- // Get pending model IDs (not yet in DB)
3184
- // Note: schemaId lookup is async, so we skip pending IDs here
3185
- // They will be included when schemaId is available asynchronously
3186
- const pendingIds = [];
3187
- // Combine and deduplicate
3188
- const allModelIds = [...new Set([...liveQueryIds, ...pendingIds])];
3189
- // Get Model instances from static cache (from schema load)
3190
- const seen = new Set();
3191
- const modelInstances = [];
3192
- for (const modelFileId of allModelIds) {
3193
- const model = Model.peek(modelFileId);
3194
- if (model && modelFileId && !seen.has(modelFileId)) {
3195
- modelInstances.push(model);
3196
- seen.add(modelFileId);
3197
- }
3198
- }
3199
- // Include models created at runtime via Model.create() that belong to this schema
3200
- // (they may not be in _liveQueryModelIds until schema context is updated)
3201
- if (schemaName) {
3202
- const cachedForSchema = Model.getCachedInstancesForSchema(schemaName);
3203
- for (const model of cachedForSchema) {
3204
- const id = model.id;
3205
- if (id && !seen.has(id)) {
3206
- modelInstances.push(model);
3207
- seen.add(id);
3208
- }
3209
- }
3210
- }
3211
- // Return a new array reference (snapshot at time of access)
3212
- return [...modelInstances];
3213
- }
3214
- get status() {
3215
- return this._getSnapshot().value;
3216
- }
3217
- get isEdited() {
3218
- const context = this._getSnapshotContext();
3219
- // First check in-memory state
3220
- if (context._isEdited !== undefined) {
3221
- return context._isEdited;
3222
- }
3223
- // Fall back to reading from database if we have _dbId
3224
- if (context._dbId) {
3225
- // Load from database asynchronously (fire-and-forget for now)
3226
- // For synchronous getter, we'll need to cache it or make it async
3227
- // For now, return false if not in context
3228
- return false;
3229
- }
3230
- return false;
3231
- }
3232
- get validationErrors() {
3233
- return this._getSnapshotContext()._validationErrors || [];
3234
- }
3235
- /**
3236
- * The error from the last draft save that an assignment started (e.g. `schema.name = ...`),
3237
- * such as a ConflictError; undefined once a later one succeeds.
3238
- */
3239
- get lastSaveError() {
3240
- return getDraftSaveError(this._service);
3241
- }
3242
- get isValid() {
3243
- const errors = this.validationErrors;
3244
- return errors.length === 0;
3245
- }
3246
- /**
3247
- * Validate the schema
3248
- * @returns Validation result
3249
- */
3250
- async validate() {
3251
- return new Promise((resolve) => {
3252
- let resolved = false;
3253
- const subscription = this._service.subscribe((snapshot) => {
3254
- if (snapshot.value === 'idle' || snapshot.value === 'error') {
3255
- if (!resolved) {
3256
- resolved = true;
3257
- subscription.unsubscribe();
3258
- const errors = snapshot.context._validationErrors || [];
3259
- resolve({
3260
- isValid: errors.length === 0,
3261
- errors,
3262
- });
3263
- }
3264
- }
3265
- });
3266
- this._service.send({ type: 'validateSchema' });
3267
- // Timeout fallback to ensure we always resolve (15 seconds - longer than actor timeout)
3268
- setTimeout(() => {
3269
- if (!resolved) {
3270
- resolved = true;
3271
- subscription.unsubscribe();
3272
- const errors = this._getSnapshotContext()._validationErrors || [];
3273
- resolve({
3274
- isValid: errors.length === 0,
3275
- errors,
3276
- });
3277
- }
3278
- }, 15000);
3279
- });
3280
- }
3281
- /**
3282
- * Build the file-format `models` object from this schema's Model instances: every model
3283
- * (those created at runtime too), each property as its file definition ({ id, type, model,
3284
- * ref, refValueType, required, storage, validation, ... }).
3285
- */
3286
- async _buildModelsFromInstances() {
3287
- const context = this._getSnapshotContext();
3288
- const { convertPropertyToSchemaUpdate } = await Promise.resolve().then(function () { return updateSchema; });
3289
- const models = {};
3290
- for (const modelInstance of this.models) {
3291
- try {
3292
- const modelName = modelInstance.modelName;
3293
- if (!modelName)
3294
- continue;
3295
- const properties$1 = {};
3296
- let propertyContexts = (modelInstance.properties ?? []).map((property) => property.getService().getSnapshot().context);
3297
- // model.properties can still be empty for a model created at runtime (the browser fills
3298
- // it from a live query): read the rows, preferring each row's cached instance.
3299
- const modelDbId = modelInstance.getService().getSnapshot().context._dbId;
3300
- const db = BaseDb.getAppDb();
3301
- if (propertyContexts.length === 0 && modelDbId && db) {
3302
- const { ModelProperty } = await import('./ModelProperty-i5flwzQG.js');
3303
- const rows = await db.select().from(properties).where(eq(properties.modelId, modelDbId));
3304
- propertyContexts = rows.map((row) => {
3305
- const cached = row.schemaFileId ? ModelProperty.getById(row.schemaFileId) : undefined;
3306
- return cached
3307
- ? cached.getService().getSnapshot().context
3308
- : {
3309
- ...row,
3310
- id: row.schemaFileId ?? undefined,
3311
- refModelId: row.refModelId ?? undefined,
3312
- refValueType: row.refValueType ?? undefined,
3313
- storageType: row.storageType ?? undefined,
3314
- localStorageDir: row.localStorageDir ?? undefined,
3315
- filenameSuffix: row.filenameSuffix ?? undefined,
3316
- };
3317
- });
3318
- }
3319
- const contextModel = context.models?.[modelName];
3320
- const fromContext = contextModel?.properties;
3321
- // nameMatch lives only in the schema file (ADR 0009): keep each property's, found by id
3322
- // first so a renamed property keeps it.
3323
- const contextNameMatch = (ctx) => {
3324
- const defs = Object.values(fromContext ?? {});
3325
- const def = (ctx.id ? defs.find((d) => d?.id === ctx.id) : undefined) ?? fromContext?.[ctx.name];
3326
- return def?.nameMatch;
3327
- };
3328
- for (const ctx of propertyContexts) {
3329
- if (!ctx.name)
3330
- continue;
3331
- const { updates } = await convertPropertyToSchemaUpdate(ctx, modelName, ctx.name);
3332
- const nameMatch = contextNameMatch(ctx);
3333
- properties$1[ctx.name] = {
3334
- ...(ctx.id ? { id: ctx.id } : {}),
3335
- ...updates,
3336
- ...(ctx.validation ? { validation: ctx.validation } : {}),
3337
- ...(ctx.htmlEmbeddedDataUriPolicy ? { htmlEmbeddedDataUriPolicy: ctx.htmlEmbeddedDataUriPolicy } : {}),
3338
- ...(nameMatch !== undefined ? { nameMatch } : {}),
3339
- };
3340
- }
3341
- // No property instances loaded for this model: keep the definitions the schema has.
3342
- models[modelName] = {
3343
- ...(modelInstance.id ? { id: modelInstance.id } : {}),
3344
- ...(contextModel?.nameMatch !== undefined ? { nameMatch: contextModel.nameMatch } : {}),
3345
- properties: Object.keys(properties$1).length > 0 || !fromContext ? properties$1 : fromContext,
3346
- };
3347
- }
3348
- catch (error) {
3349
- logger(`Error building model from instance: ${error instanceof Error ? error.message : String(error)}`);
3350
- }
3351
- }
3352
- logger(`Built ${Object.keys(models).length} models from Model instances`);
3353
- return models;
3354
- }
3355
- /**
3356
- * Saves all edited properties to a new schema version.
3357
- * This writes the changes to a new JSON file and clears the draft flags.
3358
- * Validates the schema before saving.
3359
- * Transitions schema from draft (DB-only) to published (file + DB).
3360
- * @returns The file path of the new schema version
3361
- */
3362
- async saveNewVersion() {
3363
- // Check for conflicts before saving
3364
- const conflictCheck = await this._checkForConflicts();
3365
- if (conflictCheck.hasConflict) {
3366
- const errorMessage = conflictCheck.message || 'Database was updated externally. Please reload the schema and try again.';
3367
- logger(`CONFLICT DETECTED: ${errorMessage}`);
3368
- throw new ConflictError(errorMessage, conflictCheck);
3369
- }
3370
- const context = this._getSnapshotContext();
3371
- if (!context._isDraft || !context._editedProperties || context._editedProperties.size === 0) {
3372
- logger('No changes to save');
3373
- return '';
3374
- }
3375
- // Validate schema before saving
3376
- const validationResult = await this.validate();
3377
- if (!validationResult.isValid) {
3378
- logger(`Schema validation failed with ${validationResult.errors.length} errors`);
3379
- throw new Error(`Cannot save schema: validation failed. Errors: ${validationResult.errors.map(e => e.message).join(', ')}`);
3380
- }
3381
- logger(`Saving new version for schema ${this.schemaName} with ${context._editedProperties.size} edited properties`);
3382
- // STEP 1: Ensure draft exists in database and update it with current state
3383
- const db = BaseDb.getAppDb();
3384
- if (!db) {
3385
- throw new Error('Database not found');
3386
- }
3387
- // Build current schema state from context
3388
- // CRITICAL: Read models from Model instances, not from Schema context
3389
- // Model instances are the source of truth for model data
3390
- const modelsFromInstances = await this._buildModelsFromInstances();
3391
- const currentSchema = {
3392
- id: context.id,
3393
- $schema: context.$schema || 'https://seedprotocol.org/schemas/data-model/v1',
3394
- version: context.version || 1,
3395
- metadata: context.metadata || {
3396
- name: this.schemaName,
3397
- createdAt: new Date().toISOString(),
3398
- updatedAt: new Date().toISOString(),
3399
- },
3400
- models: modelsFromInstances, // Read from Model instances, not context.models
3401
- enums: context.enums || {},
3402
- migrations: context.migrations || [],
3403
- ...(context.nameMatch !== undefined && { nameMatch: context.nameMatch }),
3404
- };
3405
- // Update draft in database with current state
3406
- await addSchemaToDb({
3407
- name: this.schemaName,
3408
- version: currentSchema.version,
3409
- createdAt: new Date(currentSchema.metadata.createdAt).getTime(),
3410
- updatedAt: new Date(currentSchema.metadata.updatedAt).getTime(),
3411
- }, currentSchema.id, JSON.stringify(currentSchema, null, 2), true);
3412
- const dbSchema = await db
3413
- .select()
3414
- .from(schemas)
3415
- .where(eq(schemas.name, this.schemaName))
3416
- .limit(1);
3417
- // Check we have something to save: schema:models (new models) or modelName:propertyName (property edits)
3418
- const hasSchemaModels = context._editedProperties.has('schema:models');
3419
- const hasPropertyEdits = [...context._editedProperties].some((key) => {
3420
- const [modelName, propertyName] = key.split(':');
3421
- return modelName && propertyName && key !== 'schema:name';
3422
- });
3423
- if (!hasSchemaModels && !hasPropertyEdits) {
3424
- logger('No valid property updates to save');
3425
- return '';
3426
- }
3427
- // Use writeFullSchemaNewVersion for all edits - handles renames, new models, property updates.
3428
- // Model instances have the correct (current) structure including renamed properties.
3429
- // STEP 2: Save to new schema version (writes file)
3430
- const newFilePath = await writeFullSchemaNewVersion(this.schemaName, currentSchema);
3431
- // STEP 3: After file is written, update database to mark as published (isDraft = false)
3432
- // Load the file to get the final schema with IDs
3433
- const fileContent = await BaseFileManager.readFileAsString(newFilePath);
3434
- const publishedSchema = JSON.parse(fileContent);
3435
- // Update database record: set isDraft = false and update schemaFileId
3436
- if (dbSchema.length > 0) {
3437
- clearNameMatchLevels(); // the stored schema file changes (ADR 0009)
3438
- await db
3439
- .update(schemas)
3440
- .set({
3441
- isDraft: false,
3442
- isEdited: false, // Clear isEdited flag after saving to file
3443
- schemaFileId: publishedSchema.id,
3444
- schemaData: JSON.stringify(publishedSchema, null, 2),
3445
- version: publishedSchema.version,
3446
- updatedAt: new Date(publishedSchema.metadata.updatedAt).getTime(),
3447
- })
3448
- .where(eq(schemas.id, dbSchema[0].id));
3449
- }
3450
- else {
3451
- // Create new record if it doesn't exist (shouldn't happen, but safety)
3452
- await addSchemaToDb({
3453
- name: this.schemaName,
3454
- version: publishedSchema.version,
3455
- createdAt: new Date(publishedSchema.metadata.createdAt).getTime(),
3456
- updatedAt: new Date(publishedSchema.metadata.updatedAt).getTime(),
3457
- }, publishedSchema.id, JSON.stringify(publishedSchema, null, 2), false, // isDraft = false (published)
3458
- false);
3459
- }
3460
- // Clear draft flags on Schema and update conflict detection metadata
3461
- this._service.send({
3462
- type: 'clearDraft',
3463
- _dbUpdatedAt: new Date(publishedSchema.metadata.updatedAt).getTime(),
3464
- _dbVersion: publishedSchema.version,
3465
- savedSchema: publishedSchema,
3466
- });
3467
- // Clear edited flags on all ModelProperty instances and in database
3468
- const modelPropertyMod = await import('./ModelProperty-i5flwzQG.js');
3469
- const { ModelProperty } = modelPropertyMod;
3470
- for (const propertyKey of context._editedProperties) {
3471
- const [modelName, propertyName] = propertyKey.split(':');
3472
- const ModelPropertyClass = ModelProperty;
3473
- // Model names are only unique per schema: pick this schema's instance, which may be keyed by id.
3474
- const thisSchemaName = this.schemaName;
3475
- const cachedInstance = [...ModelPropertyClass.instanceCache.values()].find(({ instance }) => {
3476
- const ctx = instance.getService().getSnapshot().context;
3477
- return (ctx.modelName === modelName &&
3478
- ctx.name === propertyName &&
3479
- (!ctx._schemaName || ctx._schemaName === thisSchemaName));
3480
- });
3481
- if (cachedInstance) {
3482
- const modelProperty = cachedInstance.instance;
3483
- // Update original values to current values and clear edited flag
3484
- modelProperty.getService().send({
3485
- type: 'clearEdited',
3486
- });
3487
- // Clear isEdited flag in database
3488
- try {
3489
- if (db && modelName && propertyName) {
3490
- // Find this schema's model by name
3491
- const _mod_9 = await import('./index-Dx_GTdyQ.js');
3492
- const _ns_9 = _mod_9.be;
3493
- const { resolveModelRecord } = _ns_9;
3494
- const modelRecord = await resolveModelRecord(modelName, { schemaName: thisSchemaName }, db);
3495
- const modelRecords = modelRecord ? [modelRecord] : [];
3496
- if (modelRecords.length > 0) {
3497
- // Find property by name and modelId
3498
- const propertyRecords = await db
3499
- .select({ id: properties.id })
3500
- .from(properties)
3501
- .where(and(eq(properties.name, propertyName), eq(properties.modelId, modelRecords[0].id)))
3502
- .limit(1);
3503
- if (propertyRecords.length > 0) {
3504
- // Clear isEdited flag in database
3505
- await db
3506
- .update(properties)
3507
- .set({ isEdited: false })
3508
- .where(eq(properties.id, propertyRecords[0].id));
3509
- }
3510
- }
3511
- }
3512
- }
3513
- catch (error) {
3514
- logger(`Error clearing isEdited flag in database for property ${propertyKey}: ${error}`);
3515
- }
3516
- }
3517
- }
3518
- // Clear isEdited flags for all models in this schema
3519
- try {
3520
- if (db && context._dbId) {
3521
- // Get all models for this schema
3522
- const modelRecords = await db
3523
- .select({ id: models.id })
3524
- .from(modelSchemas)
3525
- .innerJoin(models, eq(modelSchemas.modelId, models.id))
3526
- .where(eq(modelSchemas.schemaId, context._dbId));
3527
- // Clear isEdited flag for all models
3528
- for (const modelRecord of modelRecords) {
3529
- if (modelRecord.id) {
3530
- await db
3531
- .update(models)
3532
- .set({ isEdited: false })
3533
- .where(eq(models.id, modelRecord.id));
3534
- }
3535
- }
3536
- }
3537
- }
3538
- catch (error) {
3539
- logger(`Error clearing isEdited flags for models: ${error}`);
3540
- }
3541
- logger(`Successfully saved new version for schema ${this.schemaName} and published to file`);
3542
- return newFilePath;
3543
- }
3544
- /**
3545
- * Check for conflicts between actor context and database
3546
- * @returns ConflictResult indicating if a conflict exists
3547
- */
3548
- async _checkForConflicts() {
3549
- const context = this._getSnapshotContext();
3550
- // If we don't have load metadata, can't check for conflicts
3551
- if (!context._dbUpdatedAt || !context.id) {
3552
- return { hasConflict: false };
3553
- }
3554
- try {
3555
- const db = BaseDb.getAppDb();
3556
- if (!db) {
3557
- return { hasConflict: false }; // Can't check without DB
3558
- }
3559
- // Get current DB record
3560
- const dbSchemas = await db
3561
- .select()
3562
- .from(schemas)
3563
- .where(eq(schemas.schemaFileId, context.id)) // id is now the schemaFileId (string)
3564
- .orderBy(desc(schemas.version))
3565
- .limit(1);
3566
- if (dbSchemas.length === 0) {
3567
- return { hasConflict: false }; // No DB record, no conflict
3568
- }
3569
- const dbRecord = dbSchemas[0];
3570
- // Check if DB was updated after we loaded
3571
- const dbUpdatedAt = dbRecord.updatedAt || 0;
3572
- const localUpdatedAt = context._dbUpdatedAt;
3573
- if (dbUpdatedAt > localUpdatedAt) {
3574
- return {
3575
- hasConflict: true,
3576
- localVersion: context.version,
3577
- dbVersion: dbRecord.version,
3578
- localUpdatedAt: context.metadata?.updatedAt,
3579
- dbUpdatedAt: new Date(dbUpdatedAt).toISOString(),
3580
- message: `Database was updated externally. Local version: ${context.version}, DB version: ${dbRecord.version}`,
3581
- };
3582
- }
3583
- return { hasConflict: false };
3584
- }
3585
- catch (error) {
3586
- logger(`Error checking for conflicts: ${error instanceof Error ? error.message : String(error)}`);
3587
- return { hasConflict: false }; // On error, assume no conflict to allow save
3588
- }
3589
- }
3590
- /**
3591
- * Reload schema from database
3592
- * This refreshes the actor context with the latest data from the database
3593
- */
3594
- async reload() {
3595
- logger(`Reloading schema ${this.schemaName} from database`);
3596
- // Send reload event to machine
3597
- this._service.send({ type: 'reloadFromDb' });
3598
- // Wait for reload to complete
3599
- return new Promise((resolve, reject) => {
3600
- // Cleared once the reload settles: a pending timer kept Node scripts alive for 10 s.
3601
- const timer = setTimeout(() => {
3602
- subscription.unsubscribe();
3603
- reject(new Error('Timeout waiting for schema reload'));
3604
- }, 10000);
3605
- const subscription = this._service.subscribe((snapshot) => {
3606
- if (snapshot.value === 'idle') {
3607
- clearTimeout(timer);
3608
- subscription.unsubscribe();
3609
- resolve();
3610
- }
3611
- else if (snapshot.value === 'error') {
3612
- clearTimeout(timer);
3613
- subscription.unsubscribe();
3614
- reject(new Error('Failed to reload schema from database'));
3615
- }
3616
- });
3617
- });
3618
- }
3619
- /**
3620
- * Save the current schema state to the database as a draft
3621
- * This persists changes immediately without creating a new file version
3622
- * @param oldName - Optional old name to look up existing record before name change
3623
- * @param newName - Optional new name to use (if not provided, uses this.schemaName)
3624
- */
3625
- async _saveDraftToDb(oldName, newName) {
3626
- saveDraftLogger(`_saveDraftToDb called for schema (oldName: "${oldName}", newName: "${newName}")`);
3627
- // Don't save during initialization - schemas are being loaded from files, not created as drafts
3628
- // Check this FIRST before doing any expensive work like _getSnapshotContext()
3629
- if (typeof window !== 'undefined') {
3630
- const now = Date.now();
3631
- // Use cached check to avoid expensive operations
3632
- // Always check if cache is stale or if we previously got false (to allow recovery)
3633
- const cacheIsStale = cachedClientInitialized === null || (now - clientCheckTime) > CLIENT_CHECK_CACHE_MS;
3634
- const shouldRecheck = cacheIsStale || cachedClientInitialized === false;
3635
- saveDraftLogger(`Client check: cacheIsStale=${cacheIsStale}, shouldRecheck=${shouldRecheck}, cachedValue=${cachedClientInitialized}, timeSinceCheck=${now - clientCheckTime}ms`);
3636
- if (shouldRecheck) {
3637
- try {
3638
- const client = getClient();
3639
- const clientSnapshot = client.getService().getSnapshot();
3640
- // Check if state is IDLE (primary check) - isInitialized is set in entry action so should be true
3641
- // But we check it as a secondary safeguard
3642
- const isIdle = clientSnapshot.value === ClientManagerState.IDLE;
3643
- const isInitialized = clientSnapshot.context.isInitialized;
3644
- // If state is IDLE, trust it even if isInitialized isn't set yet (entry action should set it)
3645
- // This aligns with useIsClientReady which only checks the state value
3646
- cachedClientInitialized = isIdle && (isInitialized !== false);
3647
- clientCheckTime = now;
3648
- saveDraftLogger(`Client state checked: state=${clientSnapshot.value}, isIdle=${isIdle}, isInitialized=${isInitialized}, result=${cachedClientInitialized}`);
3649
- }
3650
- catch (error) {
3651
- // If we can't check client state, assume not initialized to be safe
3652
- // But only cache for a short time to allow recovery
3653
- cachedClientInitialized = false;
3654
- clientCheckTime = now;
3655
- saveDraftLogger(`Error checking client state: ${error instanceof Error ? error.message : String(error)}, setting cachedClientInitialized=false`);
3656
- }
3657
- }
3658
- else {
3659
- saveDraftLogger(`Using cached client state: cachedClientInitialized=${cachedClientInitialized}`);
3660
- }
3661
- if (!cachedClientInitialized) {
3662
- saveDraftLogger(`Client not initialized, skipping save (oldName: "${oldName}", newName: "${newName}")`);
3663
- // Skip silently during initialization to avoid log spam and reduce overhead
3664
- return;
3665
- }
3666
- }
3667
- saveDraftLogger(`Client is initialized, proceeding with save (oldName: "${oldName}", newName: "${newName}")`);
3668
- // Check for conflicts before saving
3669
- const conflictCheck = await this._checkForConflicts();
3670
- if (conflictCheck.hasConflict) {
3671
- const errorMessage = conflictCheck.message || 'Database was updated externally. Please reload the schema and try again.';
3672
- saveDraftLogger(`CONFLICT DETECTED: ${errorMessage}`);
3673
- throw new ConflictError(errorMessage, conflictCheck);
3674
- }
3675
- const context = this._getSnapshotContext();
3676
- const schemaName = newName || context.schemaName || oldName || '';
3677
- const schemaFileId = context.id || ''; // id is now the schemaFileId (string)
3678
- // Use schemaFileId as the key if available, otherwise use schemaName
3679
- const saveKey = schemaFileId || schemaName;
3680
- // Prevent concurrent saves for the same schema
3681
- if (Schema.savingSchemas.has(saveKey)) {
3682
- saveDraftLogger(`Schema ${schemaName} (key: ${saveKey}) is already being saved, skipping concurrent save`);
3683
- return;
3684
- }
3685
- Schema.savingSchemas.add(saveKey);
3686
- saveDraftLogger(`Starting save for schema ${schemaName} (key: ${saveKey}, schemaFileId: ${schemaFileId})`);
3687
- try {
3688
- // Get context - if service is stopped, use the last known context or build from metadata
3689
- let context;
3690
- try {
3691
- context = this._getSnapshotContext();
3692
- }
3693
- catch {
3694
- // Service might be stopped, build context from what we know
3695
- logger(`Service stopped, building context from available data`);
3696
- context = {
3697
- schemaName: newName || oldName || '',
3698
- metadata: {
3699
- name: newName || oldName || '',
3700
- createdAt: new Date().toISOString(),
3701
- updatedAt: new Date().toISOString(),
3702
- },
3703
- version: 1,
3704
- _isDraft: true,
3705
- _editedProperties: new Set(),
3706
- };
3707
- }
3708
- const db = BaseDb.getAppDb();
3709
- if (!db) {
3710
- throw new Error('Database not found');
3711
- }
3712
- // Use provided newName or fall back to context schemaName (avoid getter which might be stale)
3713
- const finalNewName = newName || context.schemaName || this.schemaName;
3714
- // CRITICAL: If name changed, we MUST look up by old name to find the existing record
3715
- // Don't use finalNewName if oldName is provided - that would look up by the new name and not find the old record
3716
- const lookupName = oldName && oldName !== finalNewName ? oldName : finalNewName;
3717
- // Try to get existing schema ID from database to preserve it
3718
- // PRIMARY: Look up by schemaFileId (most reliable, independent of name changes)
3719
- // FALLBACK: Look up by old name if name changed, otherwise by current name
3720
- let existingSchemaId;
3721
- let existingSchemaRecord;
3722
- // PRIMARY: Look up by schemaFileId if we have it (this is the most reliable way)
3723
- if (context.id) {
3724
- logger(`Looking up schema by schemaFileId: ${context.id}`);
3725
- const schemasById = await db
3726
- .select()
3727
- .from(schemas)
3728
- .where(eq(schemas.schemaFileId, context.id)) // id is now the schemaFileId (string)
3729
- .limit(1);
3730
- if (schemasById.length > 0) {
3731
- const foundRecord = schemasById[0];
3732
- existingSchemaRecord = foundRecord;
3733
- existingSchemaId = foundRecord.schemaFileId || context.id;
3734
- logger(`Found schema by schemaFileId: ${context.id} (id: ${foundRecord.id}, name: ${foundRecord.name})`);
3735
- }
3736
- else {
3737
- logger(`No schema found by schemaFileId: ${context.id}`);
3738
- }
3739
- }
3740
- else {
3741
- logger(`No id (schemaFileId) in context, will look up by name`);
3742
- }
3743
- // FALLBACK: If not found by schemaFileId, try by name (for newly created schemas that don't have schemaFileId yet)
3744
- if (!existingSchemaRecord) {
3745
- logger(`Looking up schema by name "${lookupName}" (oldName: ${oldName}, finalNewName: ${finalNewName})`);
3746
- const existingSchemas = await db
3747
- .select()
3748
- .from(schemas)
3749
- .where(eq(schemas.name, lookupName))
3750
- .limit(10); // Get multiple to find drafts
3751
- logger(`Found ${existingSchemas.length} records with name "${lookupName}"`);
3752
- // If name changed, prefer draft records; otherwise prefer any record
3753
- if (existingSchemas.length > 0) {
3754
- if (oldName && oldName !== finalNewName) {
3755
- // When name changes, prefer draft records
3756
- const draftRecord = existingSchemas.find((s) => s.isDraft === true);
3757
- const selectedRecord = draftRecord || existingSchemas[0];
3758
- existingSchemaRecord = selectedRecord;
3759
- logger(`Selected ${draftRecord ? 'draft' : 'first'} record (id: ${selectedRecord.id}, isDraft: ${selectedRecord.isDraft})`);
3760
- }
3761
- else {
3762
- const selectedRecord = existingSchemas[0];
3763
- existingSchemaRecord = selectedRecord;
3764
- logger(`Selected first record (id: ${selectedRecord.id}, isDraft: ${selectedRecord.isDraft})`);
3765
- }
3766
- if (existingSchemaRecord?.schemaFileId) {
3767
- existingSchemaId = existingSchemaRecord.schemaFileId;
3768
- }
3769
- }
3770
- else {
3771
- logger(`No records found with name "${lookupName}"`);
3772
- }
3773
- }
3774
- if (existingSchemaRecord) {
3775
- logger(`Using existing record (id: ${existingSchemaRecord.id}, name: ${existingSchemaRecord.name}, isDraft: ${existingSchemaRecord.isDraft}, schemaFileId: ${existingSchemaRecord.schemaFileId || 'none'})`);
3776
- }
3777
- else {
3778
- logger(`No existing record found`);
3779
- }
3780
- // Build current schema state from context
3781
- // Use existing schemaFileId if we found one, otherwise use the one from context, or generate new
3782
- const schemaFileId = existingSchemaId || context.id || generateId(); // id is now the schemaFileId (string)
3783
- // Build metadata - if name changed, use newName, otherwise use context metadata
3784
- // Always ensure metadata.name matches finalNewName to prevent inconsistencies
3785
- const currentMetadata = context.metadata ? {
3786
- ...context.metadata,
3787
- name: finalNewName, // Always use the final name (from newName parameter or context)
3788
- updatedAt: new Date().toISOString(), // Always update timestamp when saving
3789
- } : {
3790
- name: finalNewName,
3791
- createdAt: new Date().toISOString(),
3792
- updatedAt: new Date().toISOString(),
3793
- };
3794
- // CRITICAL: Ensure schema.id always matches schemaFileId (database is source of truth)
3795
- // Use existingSchemaRecord.schemaFileId if available, otherwise use the calculated schemaFileId
3796
- const finalSchemaFileId = existingSchemaRecord?.schemaFileId || schemaFileId;
3797
- // CRITICAL: Read models from Model instances, not from Schema context
3798
- // Model instances are the source of truth for model data
3799
- const modelsFromInstances = await this._buildModelsFromInstances();
3800
- const currentSchema = {
3801
- $schema: context.$schema || 'https://seedprotocol.org/schemas/data-model/v1',
3802
- version: context.version || 1,
3803
- id: finalSchemaFileId, // Always use schemaFileId from database as source of truth
3804
- metadata: currentMetadata,
3805
- models: modelsFromInstances, // Read from Model instances, not context.models
3806
- enums: context.enums || {},
3807
- migrations: context.migrations || [],
3808
- ...(context.nameMatch !== undefined && { nameMatch: context.nameMatch }),
3809
- };
3810
- // Log if there was a mismatch that we're fixing
3811
- if (existingSchemaRecord?.schemaFileId && context.id && context.id !== finalSchemaFileId) {
3812
- saveDraftLogger(`Fixed schema ID mismatch: context.id="${context.id}" does not match DB schemaFileId="${finalSchemaFileId}". Using DB value.`);
3813
- }
3814
- saveDraftLogger(`Building schema with metadata.name="${currentMetadata.name}", finalNewName="${finalNewName}"`);
3815
- // If name changed, we MUST update the existing record (don't create a new one)
3816
- if (oldName && oldName !== finalNewName) {
3817
- if (existingSchemaRecord && existingSchemaRecord.id) {
3818
- logger(`Updating existing schema record (id: ${existingSchemaRecord.id}) from "${oldName}" to "${finalNewName}"`);
3819
- // Update the existing record with the new name
3820
- const schemaDataString = JSON.stringify(currentSchema, null, 2);
3821
- saveDraftLogger(`Saving schemaData with metadata.name="${currentSchema.metadata.name}", schemaData length=${schemaDataString.length}`);
3822
- // CRITICAL: Ensure schemaFileId column matches schema.id (schema.id is now guaranteed to match finalSchemaFileId)
3823
- // Always update schemaFileId to match the id in the schema JSON to maintain consistency
3824
- clearNameMatchLevels(); // the stored schema file changes (ADR 0009)
3825
- const updateData = {
3826
- name: finalNewName,
3827
- schemaData: schemaDataString,
3828
- schemaFileId: finalSchemaFileId, // Always set to match schema.id
3829
- version: currentSchema.version,
3830
- updatedAt: new Date(currentSchema.metadata.updatedAt).getTime(),
3831
- isDraft: true, // Ensure it's marked as a draft when saving via _saveDraftToDb
3832
- };
3833
- // Log if we're fixing a mismatch
3834
- if (existingSchemaRecord.schemaFileId && existingSchemaRecord.schemaFileId !== finalSchemaFileId) {
3835
- saveDraftLogger(`Fixing schemaFileId mismatch: DB had "${existingSchemaRecord.schemaFileId}", schema.id is "${finalSchemaFileId}". Updating DB to match schema.id.`);
3836
- }
3837
- else if (!existingSchemaRecord.schemaFileId && finalSchemaFileId) {
3838
- saveDraftLogger(`Setting schemaFileId to ${finalSchemaFileId} (was null)`);
3839
- }
3840
- await db
3841
- .update(schemas)
3842
- .set(updateData)
3843
- .where(eq(schemas.id, existingSchemaRecord.id));
3844
- // Verify what was saved by reading it back
3845
- const verifyRecord = await db
3846
- .select()
3847
- .from(schemas)
3848
- .where(eq(schemas.id, existingSchemaRecord.id))
3849
- .limit(1);
3850
- if (verifyRecord.length > 0 && verifyRecord[0].schemaData) {
3851
- try {
3852
- const savedSchema = JSON.parse(verifyRecord[0].schemaData);
3853
- saveDraftLogger(`Verified saved schemaData: metadata.name="${savedSchema.metadata?.name}", name column="${verifyRecord[0].name}", isDraft=${verifyRecord[0].isDraft}, schemaFileId="${verifyRecord[0].schemaFileId}"`);
3854
- if (verifyRecord[0].isDraft !== true) {
3855
- saveDraftLogger(`ERROR: isDraft is not true after save! Expected true, got ${verifyRecord[0].isDraft}. This will cause the schema to load from file instead of database!`);
3856
- // Try to fix it immediately
3857
- await db
3858
- .update(schemas)
3859
- .set({ isDraft: true })
3860
- .where(eq(schemas.id, existingSchemaRecord.id));
3861
- saveDraftLogger(`Attempted to fix isDraft by setting it to true again`);
3862
- // Verify the fix
3863
- const fixedRecord = await db
3864
- .select()
3865
- .from(schemas)
3866
- .where(eq(schemas.id, existingSchemaRecord.id))
3867
- .limit(1);
3868
- if (fixedRecord.length > 0) {
3869
- saveDraftLogger(`After fix attempt: isDraft=${fixedRecord[0].isDraft}`);
3870
- }
3871
- }
3872
- }
3873
- catch (error) {
3874
- saveDraftLogger(`Error parsing saved schemaData for verification: ${error}`);
3875
- }
3876
- }
3877
- else {
3878
- saveDraftLogger(`WARNING: Could not verify saved record - record not found or has no schemaData`);
3879
- }
3880
- // Update the context's id (schemaFileId) and conflict detection metadata
3881
- try {
3882
- const snapshot = this._service.getSnapshot();
3883
- if (snapshot.status !== 'stopped') {
3884
- // Use finalSchemaFileId which matches the schema.id
3885
- // Also update conflict detection metadata after successful save
3886
- this._service.send({
3887
- type: 'updateContext',
3888
- id: finalSchemaFileId, // id is now the schemaFileId (string)
3889
- _dbUpdatedAt: new Date(currentSchema.metadata.updatedAt).getTime(),
3890
- _dbVersion: currentSchema.version,
3891
- });
3892
- }
3893
- }
3894
- catch (error) {
3895
- // Service might be stopped, ignore
3896
- logger(`Could not update id (schemaFileId) in context: ${error instanceof Error ? error.message : String(error)}`);
3897
- }
3898
- logger(`Successfully updated schema name from "${oldName}" to "${finalNewName}" in database`);
3899
- Schema.savingSchemas.delete(saveKey);
3900
- return;
3901
- }
3902
- else {
3903
- // Name changed but we didn't find the existing record - try harder to find it
3904
- logger(`WARNING: Name changed from "${oldName}" to "${finalNewName}" but existing record not found. Trying additional lookups...`);
3905
- // Try to find by looking for ANY draft with the old name (even if it doesn't match exactly)
3906
- const allDrafts = await db
3907
- .select()
3908
- .from(schemas)
3909
- .where(eq(schemas.name, oldName))
3910
- .limit(1);
3911
- if (allDrafts.length > 0 && allDrafts[0].id) {
3912
- const foundRecord = allDrafts[0];
3913
- logger(`Found existing record by old name "${oldName}" (id: ${foundRecord.id}), updating to new name "${finalNewName}"`);
3914
- // Update the existing record with the new name
3915
- // CRITICAL: Ensure schemaFileId matches schema.id
3916
- clearNameMatchLevels(); // the stored schema file changes (ADR 0009)
3917
- await db
3918
- .update(schemas)
3919
- .set({
3920
- name: finalNewName,
3921
- schemaData: JSON.stringify(currentSchema, null, 2),
3922
- schemaFileId: finalSchemaFileId, // Always set to match schema.id
3923
- version: currentSchema.version,
3924
- updatedAt: new Date(currentSchema.metadata.updatedAt).getTime(),
3925
- isDraft: true, // Ensure it's marked as a draft when saving via _saveDraftToDb
3926
- })
3927
- .where(eq(schemas.id, foundRecord.id));
3928
- // Update context with id (schemaFileId) and conflict detection metadata
3929
- try {
3930
- const snapshot = this._service.getSnapshot();
3931
- if (snapshot.status !== 'stopped' && finalSchemaFileId) {
3932
- this._service.send({
3933
- type: 'updateContext',
3934
- id: finalSchemaFileId, // id is now the schemaFileId (string)
3935
- _dbUpdatedAt: new Date(currentSchema.metadata.updatedAt).getTime(),
3936
- _dbVersion: currentSchema.version,
3937
- });
3938
- }
3939
- }
3940
- catch {
3941
- // Service might be stopped, ignore
3942
- }
3943
- logger(`Successfully updated schema name from "${oldName}" to "${finalNewName}" in database (found by old name)`);
3944
- Schema.savingSchemas.delete(saveKey);
3945
- return;
3946
- }
3947
- // If we still can't find it, this is an error - don't create a duplicate
3948
- logger(`ERROR: Could not find existing schema record with name "${oldName}". Cannot update name to "${finalNewName}" without creating a duplicate.`);
3949
- Schema.savingSchemas.delete(saveKey);
3950
- throw new Error(`Cannot update schema name: existing schema with name "${oldName}" not found in database`);
3951
- }
3952
- }
3953
- // If name changed, we should have already updated the record above and returned
3954
- // Only call addSchemaToDb if name didn't change (normal save scenario)
3955
- if (oldName && oldName !== finalNewName) {
3956
- // This should never happen - we should have updated above and returned
3957
- // But if we get here, it means the update path didn't work, so we need to handle it
3958
- logger(`ERROR: Name changed from "${oldName}" to "${finalNewName}" but update path didn't complete. This should not occur.`);
3959
- Schema.savingSchemas.delete(saveKey);
3960
- throw new Error(`Failed to update schema name: existing record not found or update failed`);
3961
- }
3962
- // Otherwise, use addSchemaToDb which will handle create/update logic (for normal saves, not name changes)
3963
- await addSchemaToDb({
3964
- name: finalNewName,
3965
- version: currentSchema.version,
3966
- createdAt: new Date(currentSchema.metadata.createdAt).getTime(),
3967
- updatedAt: new Date(currentSchema.metadata.updatedAt).getTime(),
3968
- }, currentSchema.id, // schemaFileId
3969
- JSON.stringify(currentSchema, null, 2), // schemaData
3970
- true);
3971
- // Update conflict detection metadata after successful save
3972
- try {
3973
- const snapshot = this._service.getSnapshot();
3974
- if (snapshot.status !== 'stopped') {
3975
- this._service.send({
3976
- type: 'updateContext',
3977
- _dbUpdatedAt: new Date(currentSchema.metadata.updatedAt).getTime(),
3978
- _dbVersion: currentSchema.version,
3979
- });
3980
- }
3981
- }
3982
- catch {
3983
- // Service might be stopped, ignore
3984
- }
3985
- saveDraftLogger(`Successfully saved draft schema "${finalNewName}" to database (key: ${saveKey})`);
3986
- logger(`Saved draft schema ${finalNewName} to database`);
3987
- }
3988
- catch (error) {
3989
- saveDraftLogger(`ERROR: Failed to save draft to database for schema "${schemaName}" (key: ${saveKey}): ${error instanceof Error ? error.message : String(error)}`);
3990
- logger(`Failed to save draft to database: ${error instanceof Error ? error.message : String(error)}`);
3991
- throw error;
3992
- }
3993
- finally {
3994
- // Always remove from saving set, even if there was an error
3995
- Schema.savingSchemas.delete(saveKey);
3996
- saveDraftLogger(`Removed schema from saving set (key: ${saveKey})`);
3997
- }
3998
- }
3999
- /**
4000
- * Force-evict the cached Schema instance(s) named `schemaName`, ignoring refCounts: stop their live
4001
- * query and service. Schema.destroy does the same for itself. For code that deletes a schema's rows
4002
- * without destroying it (test cleanup): a live Schema keeps re-creating its models from its live
4003
- * query while their rows are being deleted.
4004
- */
4005
- static evict(schemaName) {
4006
- const instances = new Set();
4007
- for (const { instance } of Schema.instanceCacheById.values())
4008
- instances.add(instance);
4009
- for (const { instance } of Schema.instanceCacheByName.values())
4010
- instances.add(instance);
4011
- for (const instance of instances) {
4012
- let context;
4013
- try {
4014
- context = instance._getSnapshotContext();
4015
- }
4016
- catch {
4017
- continue;
4018
- }
4019
- if (context.schemaName !== schemaName)
4020
- continue;
4021
- clearDestroySubscriptions(instance, {
4022
- instanceState: schemaInstanceState,
4023
- onUnload: () => schemaInstanceState.delete(instance),
4024
- });
4025
- forceRemoveFromCaches(instance, {
4026
- getCacheKeys: () => [context.id, context.schemaName].filter((k) => !!k),
4027
- caches: [
4028
- Schema.instanceCacheById,
4029
- Schema.instanceCacheByName,
4030
- ],
4031
- });
4032
- try {
4033
- instance._service.stop();
4034
- }
4035
- catch {
4036
- // Service might already be stopped
4037
- }
4038
- }
4039
- }
4040
- unload() {
4041
- try {
4042
- const context = this._getSnapshotContext();
4043
- const cacheKeys = [];
4044
- if (context.id) {
4045
- cacheKeys.push(context.id);
4046
- }
4047
- if (context.schemaName) {
4048
- cacheKeys.push(context.schemaName);
4049
- }
4050
- unloadEntity(this, {
4051
- getCacheKeys: () => cacheKeys,
4052
- caches: [Schema.instanceCacheById, Schema.instanceCacheByName],
4053
- instanceState: schemaInstanceState,
4054
- getService: (instance) => instance._service,
4055
- onUnload: () => {
4056
- // Clean up WeakMap entry
4057
- schemaInstanceState.delete(this);
4058
- if (context.schemaName)
4059
- Model.releaseSchemaHolds(context.schemaName);
4060
- },
4061
- });
4062
- }
4063
- catch (error) {
4064
- // Service might be stopped, can't get context - that's okay
4065
- logger(`Could not get context during unload: ${error instanceof Error ? error.message : String(error)}`);
4066
- // Still try to clean up what we can
4067
- const instanceState = schemaInstanceState.get(this);
4068
- if (instanceState?.liveQuerySubscription) {
4069
- instanceState.liveQuerySubscription.unsubscribe();
4070
- instanceState.liveQuerySubscription = null;
4071
- }
4072
- schemaInstanceState.delete(this);
4073
- try {
4074
- this._service.stop();
4075
- }
4076
- catch {
4077
- // Service might already be stopped
4078
- }
4079
- }
4080
- }
4081
- /**
4082
- * Destroy the schema instance completely: remove from caches, delete from database (cascade),
4083
- * and stop the service. Uses shared destroy helpers.
4084
- */
4085
- async destroy() {
4086
- const context = this._getSnapshotContext();
4087
- const schemaFileId = context.id;
4088
- const schemaName = context.schemaName;
4089
- clearDestroySubscriptions(this, {
4090
- instanceState: schemaInstanceState,
4091
- onUnload: () => schemaInstanceState.delete(this),
4092
- });
4093
- const cacheKeys = [];
4094
- if (schemaFileId)
4095
- cacheKeys.push(schemaFileId);
4096
- if (schemaName)
4097
- cacheKeys.push(schemaName);
4098
- forceRemoveFromCaches(this, {
4099
- getCacheKeys: () => cacheKeys,
4100
- caches: [
4101
- Schema.instanceCacheById,
4102
- Schema.instanceCacheByName,
4103
- ],
4104
- });
4105
- // Until its rows are gone, nothing may re-create this schema's models. Its own actor stays live
4106
- // till then, and its live query reacting to destroyStarted could read a model row the drained
4107
- // write had just landed and re-create (and re-write) that model after the delete below.
4108
- if (schemaName)
4109
- recordSchemaDestroy(schemaName);
4110
- try {
4111
- await this._destroyRows(schemaFileId, schemaName, context.metadata?.name);
4112
- }
4113
- finally {
4114
- if (schemaName)
4115
- finishSchemaDestroy(schemaName);
4116
- }
4117
- }
4118
- async _destroyRows(schemaFileId, schemaName, metadataName) {
4119
- // The rows for this schema's models and properties are deleted below; evict their cached
4120
- // instances too, or a re-import would get them back with dangling _dbIds.
4121
- if (schemaName) {
4122
- const evictedModelNames = Model.evictForSchema(schemaName);
4123
- const { ModelProperty } = await import('./ModelProperty-i5flwzQG.js');
4124
- ModelProperty.evictForModels(evictedModelNames, schemaName);
4125
- // Evicting stops new writes but not ones already running; let those land before deleting their rows
4126
- const _mod_10 = await import('./errors-D1zHAqjr.js');
4127
- const _ns_10 = _mod_10.h;
4128
- const { waitForInFlightWrites } = _ns_10;
4129
- await waitForInFlightWrites();
4130
- }
4131
- await runDestroyLifecycle(this, {
4132
- getService: (instance) => instance._service,
4133
- doDestroy: async () => {
4134
- const db = BaseDb.getAppDb();
4135
- if (!db || !schemaFileId)
4136
- return;
4137
- const schemaRecords = await db
4138
- .select({ id: schemas.id })
4139
- .from(schemas)
4140
- .where(eq(schemas.schemaFileId, schemaFileId));
4141
- if (schemaRecords.length === 0)
4142
- return;
4143
- const schemaIds = schemaRecords
4144
- .map((r) => r.id)
4145
- .filter((id) => id != null);
4146
- if (schemaIds.length === 0)
4147
- return;
4148
- const drizzleMod = await import('drizzle-orm');
4149
- const { inArray } = drizzleMod;
4150
- const joinRows = await db
4151
- .select({ modelId: modelSchemas.modelId })
4152
- .from(modelSchemas)
4153
- .where(inArray(modelSchemas.schemaId, schemaIds));
4154
- const modelIds = [
4155
- ...new Set(joinRows
4156
- .map((r) => r.modelId)
4157
- .filter((id) => id != null)),
4158
- ];
4159
- const { detachModelRows } = await import('./detachRowsBeforeDelete-DXFYSHRR.js');
4160
- await detachModelRows(db, modelIds);
4161
- await db.delete(modelSchemas).where(inArray(modelSchemas.schemaId, schemaIds));
4162
- for (const modelId of modelIds) {
4163
- await db.delete(properties).where(eq(properties.modelId, modelId));
4164
- }
4165
- for (const modelId of modelIds) {
4166
- await db.delete(models).where(eq(models.id, modelId));
4167
- }
4168
- await db.delete(schemas).where(eq(schemas.schemaFileId, schemaFileId));
4169
- // Delete the SDK's own copies of this schema's files (all versions) in the working dir.
4170
- // loadAllSchemasFromDb imports any schema file without a DB row, so leaving them brought
4171
- // the destroyed schema back on the next Schema.all().
4172
- // Match on name as well as id: versions imported from complete schema files can each
4173
- // have their own id, and any version left behind would bring the schema back.
4174
- const _mod_11 = await import('./ClientManager-GZfXErDX.js');
4175
- const _ns_11 = _mod_11.S;
4176
- const { listCompleteSchemaFiles } = _ns_11;
4177
- const files = await listCompleteSchemaFiles().catch(() => []);
4178
- const fs = await BaseFileManager.getFs();
4179
- const names = new Set([schemaName, metadataName].filter(Boolean));
4180
- for (const file of files) {
4181
- if (file.schemaFileId === schemaFileId || names.has(file.name)) {
4182
- await fs.promises.unlink(file.filePath).catch(() => { });
4183
- }
4184
- }
4185
- },
4186
- });
4187
- }
4188
- /**
4189
- * Set up liveQuery subscription to watch for model changes in the database
4190
- * This enables cross-instance synchronization (e.g., changes in other tabs/windows)
4191
- */
4192
- _setupLiveQuerySubscription() {
4193
- // Only set up in browser environment where liveQuery is available
4194
- if (typeof window === 'undefined') {
4195
- return;
4196
- }
4197
- setupEntityLiveQuery(this, {
4198
- getEntityId: async (schema) => {
4199
- const db = BaseDb.getAppDb();
4200
- if (!db) {
4201
- return undefined;
4202
- }
4203
- const context = schema._getSnapshotContext();
4204
- const schemaName = context.metadata?.name;
4205
- if (!schemaName) {
4206
- return undefined;
4207
- }
4208
- const schemaRecords = await db
4209
- .select()
4210
- .from(schemas)
4211
- .where(eq(schemas.name, schemaName))
4212
- .limit(1);
4213
- if (schemaRecords.length === 0 || !schemaRecords[0].id) {
4214
- return undefined;
4215
- }
4216
- return schemaRecords[0].id;
4217
- },
4218
- buildQuery: async (schemaId) => {
4219
- const db = BaseDb.getAppDb();
4220
- if (!db) {
4221
- throw new Error('Database not available');
4222
- }
4223
- return BaseDb.liveQuery(db
4224
- .select({
4225
- modelId: modelSchemas.modelId,
4226
- modelName: models.name,
4227
- modelFileId: models.schemaFileId,
4228
- })
4229
- .from(modelSchemas)
4230
- .innerJoin(models, eq(modelSchemas.modelId, models.id))
4231
- .where(eq(modelSchemas.schemaId, typeof schemaId === 'string' ? parseInt(schemaId, 10) : schemaId)));
4232
- },
4233
- extractEntityIds: (rows) => rows.map(row => row.modelFileId).filter(Boolean),
4234
- updateContext: (schema, ids) => {
4235
- // Helper function to send updateContext event with model IDs
4236
- const sendUpdateContext = () => {
4237
- // Double-check instance state still exists
4238
- const verifyInstanceState = schemaInstanceState.get(schema);
4239
- if (!verifyInstanceState) {
4240
- logger(`[Schema._setupLiveQuerySubscription] Instance state cleaned up before sending updateContext`);
4241
- return;
4242
- }
4243
- try {
4244
- const snapshot = schema._service.getSnapshot();
4245
- if (snapshot.status === 'stopped') {
4246
- logger(`[Schema._setupLiveQuerySubscription] Service stopped before sending, skipping`);
4247
- return;
4248
- }
4249
- // Send updateContext with liveQueryModelIds in service context for reactive updates
4250
- schema._service.send({
4251
- type: 'updateContext',
4252
- _liveQueryModelIds: ids, // Store in service context for reactivity
4253
- _modelsUpdated: Date.now(), // Internal field for tracking
4254
- });
4255
- logger(`[Schema._setupLiveQuerySubscription] Sent updateContext event with ${ids.length} model IDs`);
4256
- }
4257
- catch (error) {
4258
- logger(`[Schema._setupLiveQuerySubscription] Error sending updateContext: ${error}`);
4259
- }
4260
- };
4261
- // Check if service is stopped before sending events
4262
- const snapshot = schema._service.getSnapshot();
4263
- const isServiceStopped = snapshot.status === 'stopped';
4264
- if (isServiceStopped) {
4265
- logger(`[Schema._setupLiveQuerySubscription] Service is stopped, restarting before sending updateContext`);
4266
- // Restart the service first
4267
- schema._service.start();
4268
- // Wait for service to be ready (idle state) before sending event
4269
- setTimeout(() => {
4270
- const delayedInstanceState = schemaInstanceState.get(schema);
4271
- if (!delayedInstanceState) {
4272
- logger(`[Schema._setupLiveQuerySubscription] Instance state cleaned up during restart delay`);
4273
- return;
4274
- }
4275
- const newSnapshot = schema._service.getSnapshot();
4276
- if (newSnapshot.status !== 'stopped') {
4277
- sendUpdateContext();
4278
- }
4279
- else {
4280
- logger(`[Schema._setupLiveQuerySubscription] Service still stopped after restart attempt`);
4281
- }
4282
- }, 10);
4283
- }
4284
- else {
4285
- // Service is running, send immediately
4286
- sendUpdateContext();
4287
- }
4288
- },
4289
- createChildInstances: async (ids) => {
4290
- // One at a time: an eviction of this schema part way through stops the rest
4291
- const evictionEpoch = currentEvictionEpoch();
4292
- for (const id of ids) {
4293
- await Model.createById(id, { evictionEpoch, hold: false });
4294
- }
4295
- },
4296
- queryInitialData: async (schemaId) => {
4297
- const db = BaseDb.getAppDb();
4298
- if (!db) {
4299
- return [];
4300
- }
4301
- // Retry logic for initial query
4302
- const queryInitialModels = async (retries = 3) => {
4303
- try {
4304
- const initialModels = await db
4305
- .select({
4306
- modelId: modelSchemas.modelId,
4307
- modelName: models.name,
4308
- modelFileId: models.schemaFileId,
4309
- })
4310
- .from(modelSchemas)
4311
- .innerJoin(models, eq(modelSchemas.modelId, models.id))
4312
- .where(eq(modelSchemas.schemaId, typeof schemaId === 'string' ? parseInt(schemaId, 10) : schemaId));
4313
- logger(`[Schema._setupLiveQuerySubscription] Initial query found ${initialModels.length} models`);
4314
- if (initialModels.length > 0) {
4315
- return initialModels.map((row) => ({
4316
- modelId: row.modelId,
4317
- modelName: row.modelName,
4318
- modelFileId: row.modelFileId || '',
4319
- }));
4320
- }
4321
- else if (retries > 0) {
4322
- logger(`[Schema._setupLiveQuerySubscription] No models found, retrying... (${retries} retries left)`);
4323
- await new Promise(resolve => setTimeout(resolve, 100));
4324
- return queryInitialModels(retries - 1);
4325
- }
4326
- else {
4327
- logger(`[Schema._setupLiveQuerySubscription] No models found in initial query after retries`);
4328
- return [];
4329
- }
4330
- }
4331
- catch (error) {
4332
- if (retries > 0) {
4333
- logger(`[Schema._setupLiveQuerySubscription] Error querying initial models, retrying... (${retries} retries left): ${error}`);
4334
- await new Promise(resolve => setTimeout(resolve, 100));
4335
- return queryInitialModels(retries - 1);
4336
- }
4337
- else {
4338
- logger(`[Schema._setupLiveQuerySubscription] Error querying initial models after retries: ${error}`);
4339
- return [];
4340
- }
4341
- }
4342
- };
4343
- return queryInitialModels();
4344
- },
4345
- instanceState: schemaInstanceState,
4346
- loggerName: 'seedSdk:schema:liveQuery',
4347
- isReady: (schema) => {
4348
- const snapshot = schema._service.getSnapshot();
4349
- return snapshot.value === 'idle' && !!snapshot.context.metadata?.name;
4350
- },
4351
- });
4352
- }
4353
- }
4354
- // Cache by schemaFileId (primary key - doesn't change when name changes)
4355
- Schema.instanceCacheById = new Map();
4356
- // Temporary cache by schemaName (used until schemaFileId is available)
4357
- Schema.instanceCacheByName = new Map();
4358
- // Deduplicate concurrent Schema.create(schemaName) calls - prevents many parallel loads for same schema
4359
- Schema.pendingSchemaByName = new Map();
4360
- // Track which schemas are currently being saved to prevent concurrent saves
4361
- Schema.savingSchemas = new Set();
4362
-
4363
- var Schema$1 = /*#__PURE__*/Object.freeze({
4364
- __proto__: null,
4365
- Schema: Schema,
4366
- schemaInstanceState: schemaInstanceState
4367
- });
4368
-
4369
- export { Schema as S, Schema$1 as a, renameModelProperty as r, schemaMachine as s, updateModelProperties as u };
4370
- //# sourceMappingURL=Schema-PFZwG9Nl.js.map