@seedprotocol/sdk 0.7.10 → 0.7.11-next.1396.g883ef47

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 (174) hide show
  1. package/README.md +1 -1
  2. package/dist/{BaseFileManager-DLou1TZS.js → BaseFileManager-D-pLXQAp.js} +2 -14
  3. package/dist/BaseFileManager-D-pLXQAp.js.map +1 -0
  4. package/dist/{Item-oPnmIehk.js → Item-BU9wIT7l.js} +2053 -412
  5. package/dist/Item-BU9wIT7l.js.map +1 -0
  6. package/dist/{ModelProperty-D2vJozmW.js → ModelProperty-Bv7KzP97.js} +40 -38
  7. package/dist/ModelProperty-Bv7KzP97.js.map +1 -0
  8. package/dist/{Schema-BMXRF4j9.js → Schema-C_4wv0RP.js} +22 -22
  9. package/dist/{Schema-BMXRF4j9.js.map → Schema-C_4wv0RP.js.map} +1 -1
  10. package/dist/{SchemaValidationService-CieGrj5c.js → SchemaValidationService-Dc-lpFQX.js} +9 -7
  11. package/dist/{SchemaValidationService-CieGrj5c.js.map → SchemaValidationService-Dc-lpFQX.js.map} +1 -1
  12. package/dist/arweaveContentTags-iW_HcWJA.js +1456 -0
  13. package/dist/arweaveContentTags-iW_HcWJA.js.map +1 -0
  14. package/dist/{configurePlatform-WkmvqeTO.js → configurePlatform-BH1YOZpg.js} +2 -2
  15. package/dist/{configurePlatform-WkmvqeTO.js.map → configurePlatform-BH1YOZpg.js.map} +1 -1
  16. package/dist/db/drizzle/0030_drop_property_uids.sql +1 -0
  17. package/dist/db/drizzle/meta/0030_snapshot.json +1638 -0
  18. package/dist/db/drizzle/meta/_journal.json +8 -1
  19. package/dist/{detachRowsBeforeDelete-BZr_0iZU.js → detachRowsBeforeDelete-Cie55SoG.js} +4 -6
  20. package/dist/detachRowsBeforeDelete-Cie55SoG.js.map +1 -0
  21. package/dist/dist/{BaseFileManager-DLou1TZS.d.ts → BaseFileManager-D-pLXQAp.d.ts} +432 -495
  22. package/dist/dist/BaseFileManager-D-pLXQAp.d.ts.map +1 -0
  23. package/dist/dist/{configurePlatform-WkmvqeTO.d.ts → configurePlatform-BH1YOZpg.d.ts} +1 -1
  24. package/dist/dist/configurePlatform-BH1YOZpg.d.ts.map +1 -0
  25. package/dist/dist/{index-BjWk0x4-.d.ts → index-2PaIhvOU.d.ts} +1 -64
  26. package/dist/dist/{index-BjWk0x4-.d.ts.map → index-2PaIhvOU.d.ts.map} +1 -1
  27. package/dist/dist/{index.node-BfiBriEq.d.ts → index.node-C3rI1upv.d.ts} +25 -281
  28. package/dist/dist/{index.node-BfiBriEq.d.ts.map → index.node-C3rI1upv.d.ts.map} +1 -1
  29. package/dist/dist/minimumUpgradeVersion-Dg2MguFf.d.ts +20 -0
  30. package/dist/dist/minimumUpgradeVersion-Dg2MguFf.d.ts.map +1 -0
  31. package/dist/dist/platform.node.d.ts +2 -2
  32. package/dist/download-DHWUPLlK.js +393 -0
  33. package/dist/download-DHWUPLlK.js.map +1 -0
  34. package/dist/{eas-FZPPqxqe.js → eas-MXEdKKVc.js} +12 -10
  35. package/dist/{eas-FZPPqxqe.js.map → eas-MXEdKKVc.js.map} +1 -1
  36. package/dist/ensureImageLocal-B6suz6FC.js +175 -0
  37. package/dist/ensureImageLocal-B6suz6FC.js.map +1 -0
  38. package/dist/{getPublishPayload-DKuEaq96.js → getPublishPayload-Dd3HCYeI.js} +8 -49
  39. package/dist/getPublishPayload-Dd3HCYeI.js.map +1 -0
  40. package/dist/{getPublishPendingDiff-CH5xLMjU.js → getPublishPendingDiff-kcTP52Kj.js} +13 -12
  41. package/dist/{getPublishPendingDiff-CH5xLMjU.js.map → getPublishPendingDiff-kcTP52Kj.js.map} +1 -1
  42. package/dist/{index-BjWk0x4-.js → index-2PaIhvOU.js} +2 -4
  43. package/dist/index-2PaIhvOU.js.map +1 -0
  44. package/dist/index-BXTyvnKb.js +28 -0
  45. package/dist/index-BXTyvnKb.js.map +1 -0
  46. package/dist/{index-DXRhFg1N.js → index-CmNqUKQN.js} +10 -8
  47. package/dist/index-CmNqUKQN.js.map +1 -0
  48. package/dist/{index-Gh42_47r.js → index-Xs_EDi-_.js} +18 -401
  49. package/dist/index-Xs_EDi-_.js.map +1 -0
  50. package/dist/{index.browser-DQstVvRV.js → index.browser-BkcOQGhw.js} +59 -102
  51. package/dist/index.browser-BkcOQGhw.js.map +1 -0
  52. package/dist/{index.node-BfiBriEq.js → index.node-C3rI1upv.js} +14 -36
  53. package/dist/index.node-C3rI1upv.js.map +1 -0
  54. package/dist/main.js +19 -19
  55. package/dist/{minimumUpgradeVersion-BDhHvHtr.js → minimumUpgradeVersion-Dg2MguFf.js} +38 -9
  56. package/dist/minimumUpgradeVersion-Dg2MguFf.js.map +1 -0
  57. package/dist/{modelPropertyRecordSchemas-DEe-lYhn.js → modelPropertyRecordSchemas-CEac3FB8.js} +10 -10
  58. package/dist/{modelPropertyRecordSchemas-DEe-lYhn.js.map → modelPropertyRecordSchemas-CEac3FB8.js.map} +1 -1
  59. package/dist/node.js +14 -12
  60. package/dist/node.js.map +1 -1
  61. package/dist/platform.browser.js +10 -12
  62. package/dist/platform.browser.js.map +1 -1
  63. package/dist/platform.node.js +5 -5
  64. package/dist/{property-iNW1N3vm.js → property-4jSqHieb.js} +9 -9
  65. package/dist/{property-iNW1N3vm.js.map → property-4jSqHieb.js.map} +1 -1
  66. package/dist/{errors-CnT0fSMK.js → publishErrors-DZXDX3B6.js} +1830 -245
  67. package/dist/publishErrors-DZXDX3B6.js.map +1 -0
  68. package/dist/{readHtmlBodyForStorageSeed-B7HgPmq6.js → readHtmlBodyForStorageSeed-Cva1DPzE.js} +10 -12
  69. package/dist/{readHtmlBodyForStorageSeed-B7HgPmq6.js.map → readHtmlBodyForStorageSeed-Cva1DPzE.js.map} +1 -1
  70. package/dist/seedSchema/index.ts +0 -1
  71. package/dist/src/Item/Item.d.ts.map +1 -1
  72. package/dist/src/ItemProperty/ItemProperty.d.ts +1 -0
  73. package/dist/src/ItemProperty/ItemProperty.d.ts.map +1 -1
  74. package/dist/src/ItemProperty/service/actors/initialize.d.ts.map +1 -1
  75. package/dist/src/Model/index.d.ts +0 -2
  76. package/dist/src/Model/index.d.ts.map +1 -1
  77. package/dist/src/browser/db/Db.d.ts.map +1 -1
  78. package/dist/src/browser/db/drizzleFiles.d.ts +3 -2
  79. package/dist/src/browser/db/drizzleFiles.d.ts.map +1 -1
  80. package/dist/src/browser/helpers/FileManager.d.ts +1 -3
  81. package/dist/src/browser/helpers/FileManager.d.ts.map +1 -1
  82. package/dist/src/browser/workers/FileDownloader.d.ts +1 -0
  83. package/dist/src/browser/workers/FileDownloader.d.ts.map +1 -1
  84. package/dist/src/browser/workers/ImageResizer.d.ts +1 -0
  85. package/dist/src/browser/workers/ImageResizer.d.ts.map +1 -1
  86. package/dist/src/client/actors/addModelsToDb.d.ts.map +1 -1
  87. package/dist/src/client/actors/saveAppState.d.ts.map +1 -1
  88. package/dist/src/client/actors/saveConfig.d.ts.map +1 -1
  89. package/dist/src/db/minimumUpgradeVersion.d.ts +20 -5
  90. package/dist/src/db/minimumUpgradeVersion.d.ts.map +1 -1
  91. package/dist/src/db/read/metadataProperty.d.ts +1 -1
  92. package/dist/src/db/read/metadataProperty.d.ts.map +1 -1
  93. package/dist/src/db/read/resolveModelRecord.d.ts +0 -20
  94. package/dist/src/db/read/resolveModelRecord.d.ts.map +1 -1
  95. package/dist/src/db/write/createMetadata.d.ts +1 -8
  96. package/dist/src/db/write/createMetadata.d.ts.map +1 -1
  97. package/dist/src/db/write/removeLocalCopiesForAddresses.d.ts +0 -5
  98. package/dist/src/db/write/removeLocalCopiesForAddresses.d.ts.map +1 -1
  99. package/dist/src/db/write/updateItemPropertyValue.d.ts.map +1 -1
  100. package/dist/src/events/files/download.d.ts.map +1 -1
  101. package/dist/src/events/item/syncDbWithEas.d.ts.map +1 -1
  102. package/dist/src/helpers/easPropertyCanonical.d.ts +1 -1
  103. package/dist/src/helpers/easPropertyCanonical.d.ts.map +1 -1
  104. package/dist/src/helpers/entity/detachRowsBeforeDelete.d.ts +2 -3
  105. package/dist/src/helpers/entity/detachRowsBeforeDelete.d.ts.map +1 -1
  106. package/dist/src/helpers/entity/entityLifecycle.d.ts +0 -2
  107. package/dist/src/helpers/entity/entityLifecycle.d.ts.map +1 -1
  108. package/dist/src/helpers/environment.d.ts +0 -13
  109. package/dist/src/helpers/environment.d.ts.map +1 -1
  110. package/dist/src/helpers/index.d.ts +0 -4
  111. package/dist/src/helpers/index.d.ts.map +1 -1
  112. package/dist/src/helpers/itemStorageFile.d.ts +0 -11
  113. package/dist/src/helpers/itemStorageFile.d.ts.map +1 -1
  114. package/dist/src/helpers/propertySchemaUid.d.ts +7 -5
  115. package/dist/src/helpers/propertySchemaUid.d.ts.map +1 -1
  116. package/dist/src/helpers/storageDir.d.ts +0 -2
  117. package/dist/src/helpers/storageDir.d.ts.map +1 -1
  118. package/dist/src/index.d.ts +4 -1
  119. package/dist/src/index.d.ts.map +1 -1
  120. package/dist/src/node/db/Db.d.ts +0 -2
  121. package/dist/src/node/db/Db.d.ts.map +1 -1
  122. package/dist/src/node/helpers/FileManager.d.ts +0 -2
  123. package/dist/src/node/helpers/FileManager.d.ts.map +1 -1
  124. package/dist/src/node/index.d.ts +11 -3
  125. package/dist/src/node/index.d.ts.map +1 -1
  126. package/dist/src/outbox/index.d.ts +5 -5
  127. package/dist/src/outbox/index.d.ts.map +1 -1
  128. package/dist/src/seedSchema/index.d.ts +0 -1
  129. package/dist/src/seedSchema/index.d.ts.map +1 -1
  130. package/dist/{syncDbWithEas-FuyqoEKk.js → syncDbWithEas-V_9gIyHp.js} +25 -23
  131. package/dist/syncDbWithEas-V_9gIyHp.js.map +1 -0
  132. package/dist/{tolerantWebAccess-zEseJ2zT.js → tolerantWebAccess-D6hFxREe.js} +11 -13
  133. package/dist/{tolerantWebAccess-zEseJ2zT.js.map → tolerantWebAccess-D6hFxREe.js.map} +1 -1
  134. package/dist/{updateSeedRevokedAt-B9JRwr5T.js → updateSeedRevokedAt-CsAoUEEt.js} +4 -3
  135. package/dist/{updateSeedRevokedAt-B9JRwr5T.js.map → updateSeedRevokedAt-CsAoUEEt.js.map} +1 -1
  136. package/package.json +7 -12
  137. package/dist/BaseFileManager-DLou1TZS.js.map +0 -1
  138. package/dist/Item-oPnmIehk.js.map +0 -1
  139. package/dist/ModelProperty-D2vJozmW.js.map +0 -1
  140. package/dist/detachRowsBeforeDelete-BZr_0iZU.js.map +0 -1
  141. package/dist/dist/BaseFileManager-DLou1TZS.d.ts.map +0 -1
  142. package/dist/dist/configurePlatform-WkmvqeTO.d.ts.map +0 -1
  143. package/dist/dist/minimumUpgradeVersion-BDhHvHtr.d.ts +0 -9
  144. package/dist/dist/minimumUpgradeVersion-BDhHvHtr.d.ts.map +0 -1
  145. package/dist/errors-CnT0fSMK.js.map +0 -1
  146. package/dist/getPublishPayload-DKuEaq96.js.map +0 -1
  147. package/dist/getPublishUploads-DivYpgNw.js +0 -783
  148. package/dist/getPublishUploads-DivYpgNw.js.map +0 -1
  149. package/dist/index-BjWk0x4-.js.map +0 -1
  150. package/dist/index-CZMWKXLM.js +0 -27
  151. package/dist/index-CZMWKXLM.js.map +0 -1
  152. package/dist/index-DXRhFg1N.js.map +0 -1
  153. package/dist/index-Gh42_47r.js.map +0 -1
  154. package/dist/index.browser-DQstVvRV.js.map +0 -1
  155. package/dist/index.node-BfiBriEq.js.map +0 -1
  156. package/dist/minimumUpgradeVersion-BDhHvHtr.js.map +0 -1
  157. package/dist/publishGraph-CRn_N3NT.js +0 -311
  158. package/dist/publishGraph-CRn_N3NT.js.map +0 -1
  159. package/dist/seedSchema/PropertyUidSchema.ts +0 -16
  160. package/dist/src/db/read/getItemProperty.d.ts +0 -6
  161. package/dist/src/db/read/getItemProperty.d.ts.map +0 -1
  162. package/dist/src/db/read/isSeedRevoked.d.ts +0 -7
  163. package/dist/src/db/read/isSeedRevoked.d.ts.map +0 -1
  164. package/dist/src/db/write/saveMetadata.d.ts +0 -3
  165. package/dist/src/db/write/saveMetadata.d.ts.map +0 -1
  166. package/dist/src/db/write/updateMetadata.d.ts +0 -13
  167. package/dist/src/db/write/updateMetadata.d.ts.map +0 -1
  168. package/dist/src/seedSchema/PropertyUidSchema.d.ts +0 -64
  169. package/dist/src/seedSchema/PropertyUidSchema.d.ts.map +0 -1
  170. package/dist/src/types/fileManager.d.ts +0 -13
  171. package/dist/src/types/fileManager.d.ts.map +0 -1
  172. package/dist/syncDbWithEas-FuyqoEKk.js.map +0 -1
  173. package/dist/tabEvents-DT5ez6io.js +0 -3830
  174. package/dist/tabEvents-DT5ez6io.js.map +0 -1
@@ -1,12 +1,13 @@
1
- import { b0 as PROPERTY_NAMES_EXEMPT_FROM_ID_SUFFIX_STRIP, n as normalizeDataType, v as INTERNAL_DATA_TYPES, af as MachineIds, O as resolveItemModelFileId, l as resolveModelRecordByNameOrType, A as AmbiguousModelError, m as generateId, b1 as TAB_ID, aN as onTabMessage, ax as whenLeaderTab, ap as isLeaderTab, aM as postTabMessage, j as findSeedRow, b2 as getModelSchemaName, K as selectInBatches, aY as getOwnedAddressesFromDb, Z as ZERO_BYTES32, ar as normalizeAddressList, J as normalizeHexAddress, am as getAddressesForItemsFilter, a0 as INTERNAL_PROPERTY_NAMES, a7 as convertTxIdToImage, i as isDataType, R as getExtensionForMimeType, b3 as normalizeMimeType, aA as sniffMimeType, Y as getItemStorageSubdir, ad as ImageSize, b4 as findFileInDir, b5 as getStorageSubdirForDataType, g as normalizePropertyRecordSchema, h as eventEmitter, b6 as timingMarks, a4 as getPropertyDefinition, aU as tryWithTabLock, b7 as withTabLock, M as ModelPropertyDataTypes, aC as createSessionObjectUrl, U as INTERNAL_STORAGE_MODEL_FILE_IDS, b8 as revokeSessionObjectUrl, b9 as getPropertyDefinitionsByFileId, aw as whenDbReady, ba as timed, av as watchEntityRows, a5 as propertyDefinitionFromRow, bb as isSessionObjectUrl, a6 as addCachedPropertyRefresher, x as normalizePublisher, _ as getItemStorageLocalFileName, bc as timingNow, bd as timingSince, be as timingLogger, bf as timedDb } from './tabEvents-DT5ez6io.js';
2
- import { fromCallback, setup, assign, and as and$1, stateIn, createActor, waitFor, isMachineSnapshot } from 'xstate';
1
+ import { Z as ZERO_BYTES32, ad as PROPERTY_NAMES_EXEMPT_FROM_ID_SUFFIX_STRIP, n as normalizeDataType, m as INTERNAL_DATA_TYPES, M as ModelPropertyDataTypes, g as normalizePropertyRecordSchema, k as generateId, ae as TAB_ID, a3 as onTabMessage, O as whenLeaderTab, H as isLeaderTab, a2 as postTabMessage, r as INTERNAL_PROPERTY_NAMES, u as convertTxIdToImage, i as isDataType, aa as getExtensionForMimeType, af as normalizeMimeType, U as sniffMimeType, I as ImageSize, h as eventEmitter, ag as timingMarks, a6 as tryWithTabLock, ah as withTabLock, X as createSessionObjectUrl, ab as INTERNAL_STORAGE_MODEL_FILE_IDS, ai as revokeSessionObjectUrl, aj as timed, N as watchEntityRows, ak as isSessionObjectUrl, al as timingNow, am as timingSince, an as timingLogger, ao as timedDb } from './arweaveContentTags-iW_HcWJA.js';
2
+ import { waitFor, fromCallback, setup, assign, and as and$1, stateIn, createActor, isMachineSnapshot } from 'xstate';
3
3
  import debug from 'debug';
4
4
  import pluralize from 'pluralize';
5
- import { easName, isValidEasAttestationUid, isPlaceholderUid, getEasSchemaUidForSchemaDefinition, getSchemaUidForModelFromCache, setSchemaUidForModel, cachedSchemaLookup, getEasModelSchemasForNames, getEasSchemaUidForExactDefinition } from '@seedprotocol/eas';
6
- import { sql, inArray, or, eq, and, isNull, getTableColumns, asc, lt, isNotNull, max, count, gt, desc, ne } from 'drizzle-orm';
7
- import { m as metadata, l as properties, B as BaseDb, d as models, p as pendingWrites, x as isSqliteBusyError, v as versions, s as seeds, w as BaseFileManager, b as modelUids, r as publishProcesses, e as arweaveL1FinalizeJobs, h as htmlEmbeddedImageCoPublish } from './BaseFileManager-DLou1TZS.js';
5
+ import { DEFAULT_NAME_MATCH_LEVEL, isNameMatchLevel, compareNameMatchTiers, localNamesInBestMatchTier, easName, isValidEasAttestationUid, isPlaceholderUid, getEasSchemaUidForExactDefinition, getSchemaUidForModelFromCache, setSchemaUidForModel, cachedSchemaLookup, getEasModelSchemasForNames } from '@seedprotocol/eas';
6
+ import { eq, getTableColumns, and, inArray, isNotNull, desc, sql, or, isNull, asc, lt, max, count, gt, ne } from 'drizzle-orm';
7
+ import { B as BaseDb, q as schemas, d as models, j as modelSchemas, v as versions, s as seeds, l as properties, p as pendingWrites, a as appState, m as metadata, t as isSqliteBusyError, r as BaseFileManager, b as modelUids, o as publishProcesses, e as arweaveL1FinalizeJobs, h as htmlEmbeddedImageCoPublish } from './BaseFileManager-D-pLXQAp.js';
8
8
  import { BehaviorSubject } from 'rxjs';
9
9
  import { orderBy } from 'lodash-es';
10
+ import { alias } from 'drizzle-orm/sqlite-core';
10
11
  import { normalizeRelationPropertyValue, resolveSeedIdsFromRefString, BaseArweaveClient } from '@seedprotocol/arweave';
11
12
 
12
13
  /**
@@ -66,6 +67,663 @@ function differsFromOriginals(originals, next, event, ignore = []) {
66
67
  });
67
68
  }
68
69
 
70
+ var MachineIds;
71
+ (function (MachineIds) {
72
+ MachineIds["CLIENT_MANAGER"] = "@seedSdk/clientManager";
73
+ MachineIds["EAS_SYNC_MANAGER"] = "@seedSdk/easSyncManager";
74
+ MachineIds["GLOBAL"] = "@seedSdk/global";
75
+ MachineIds["INTERNAL"] = "@seedSdk/internal";
76
+ MachineIds["EAS"] = "@seedSdk/eas";
77
+ MachineIds["ITEM"] = "@seedSdk/item";
78
+ MachineIds["ALL_ITEMS"] = "@seedSdk/allItems";
79
+ MachineIds["MODEL"] = "@seedSdk/model";
80
+ MachineIds["FILE_SYSTEM"] = "@seedSdk/fileSystem";
81
+ })(MachineIds || (MachineIds = {}));
82
+ var ClientManagerState;
83
+ (function (ClientManagerState) {
84
+ ClientManagerState["UNINITIALIZED"] = "uninitialized";
85
+ ClientManagerState["PLATFORM_CLASSES_INIT"] = "platformClassesInit";
86
+ ClientManagerState["FILE_SYSTEM_INIT"] = "fileSystemInit";
87
+ ClientManagerState["DB_INIT"] = "dbInit";
88
+ ClientManagerState["SAVE_CONFIG"] = "saveConfig";
89
+ ClientManagerState["ADD_MODELS_TO_DB"] = "addModelsToDb";
90
+ ClientManagerState["PROCESS_SCHEMA_FILES"] = "processSchemaFiles";
91
+ ClientManagerState["IDLE"] = "idle";
92
+ ClientManagerState["INIT_FAILED"] = "initFailed";
93
+ })(ClientManagerState || (ClientManagerState = {}));
94
+ var ClientManagerEvents;
95
+ (function (ClientManagerEvents) {
96
+ ClientManagerEvents["UPDATE_CONTEXT"] = "updateContext";
97
+ ClientManagerEvents["SAVE_APP_STATE_SUCCESS"] = "saveAppStateSuccess";
98
+ ClientManagerEvents["SAVE_APP_STATE_ERROR"] = "saveAppStateError";
99
+ ClientManagerEvents["SET_ADDRESSES"] = "setAddresses";
100
+ })(ClientManagerEvents || (ClientManagerEvents = {}));
101
+
102
+ // Set by ClientManager when it loads. Importing it here would pull in most of the SDK, and
103
+ // readiness is read from inside that graph (entity loads, downloads).
104
+ let getClientActor;
105
+ /** @internal ClientManager registers the getter for its current actor (init after stop replaces it). */
106
+ function registerClientActor(getter) {
107
+ getClientActor = getter;
108
+ }
109
+ // Client states in which the app database is open: dbInit finishes before saveConfig starts.
110
+ const DB_OPEN_STATES = new Set([
111
+ ClientManagerState.SAVE_CONFIG,
112
+ ClientManagerState.PROCESS_SCHEMA_FILES,
113
+ ClientManagerState.ADD_MODELS_TO_DB,
114
+ ClientManagerState.IDLE,
115
+ ]);
116
+ /** Whether a clientManager snapshot is a running client that finished init. */
117
+ function isClientReadySnapshot(snapshot) {
118
+ return snapshot.status === 'active' && snapshot.value === ClientManagerState.IDLE;
119
+ }
120
+ /**
121
+ * Whether `client.init` finished and the app database is open. False before init, while it runs,
122
+ * and after `client.stop()`.
123
+ */
124
+ function isClientReady() {
125
+ const actor = getClientActor?.();
126
+ return !!actor && isClientReadySnapshot(actor.getSnapshot()) && BaseDb.isAppDbReady();
127
+ }
128
+ /**
129
+ * The app database, once client init has opened it. Waits up to `timeoutMs` for init to get that
130
+ * far; returns undefined when it doesn't (no init, a failed or stopped client).
131
+ */
132
+ async function whenDbReady(timeoutMs = 30000) {
133
+ if (BaseDb.isAppDbReady())
134
+ return BaseDb.getAppDb();
135
+ const actor = getClientActor?.();
136
+ if (!actor)
137
+ return undefined;
138
+ try {
139
+ await waitFor(actor, (snapshot) => DB_OPEN_STATES.has(snapshot.value), {
140
+ timeout: timeoutMs,
141
+ });
142
+ }
143
+ catch {
144
+ return undefined;
145
+ }
146
+ return BaseDb.isAppDbReady() ? BaseDb.getAppDb() : undefined;
147
+ }
148
+
149
+ const refreshers = new Set();
150
+ function addCachedPropertyRefresher(fn) {
151
+ refreshers.add(fn);
152
+ }
153
+ /** Give cached instances of these properties their rows' current definitions. */
154
+ async function refreshCachedProperties(propertyFileIds) {
155
+ if (propertyFileIds.length === 0)
156
+ return;
157
+ for (const refresher of refreshers)
158
+ await refresher(propertyFileIds);
159
+ }
160
+
161
+ const level = (value) => (isNameMatchLevel(value) ? value : undefined);
162
+ const tighter = (a, b) => a === undefined || compareNameMatchTiers(b, a) < 0 ? b : a;
163
+ /** The levels in one schema file for one of its models, or undefined when the file lacks the model. */
164
+ const modelLevelsFromFile = (file, modelName) => {
165
+ const modelDef = file.models?.[modelName];
166
+ if (!modelDef)
167
+ return undefined;
168
+ const model = level(modelDef.nameMatch) ?? level(file.nameMatch) ?? DEFAULT_NAME_MATCH_LEVEL;
169
+ const properties = new Map();
170
+ for (const [propertyName, def] of Object.entries(modelDef.properties ?? {})) {
171
+ properties.set(propertyName, level(def?.nameMatch) ?? model);
172
+ }
173
+ return { model, properties };
174
+ };
175
+ /**
176
+ * Builds NameMatchLevels from the schema files that own each model row. Exported for tests;
177
+ * use {@link nameMatchLevels}.
178
+ */
179
+ const nameMatchLevelsFromRows = (rows) => {
180
+ const byFileId = new Map();
181
+ const byName = new Map();
182
+ const parsed = new Map();
183
+ for (const row of rows) {
184
+ if (!row.schemaData)
185
+ continue;
186
+ if (!parsed.has(row.schemaData)) {
187
+ try {
188
+ parsed.set(row.schemaData, JSON.parse(row.schemaData));
189
+ }
190
+ catch {
191
+ parsed.set(row.schemaData, undefined);
192
+ }
193
+ }
194
+ const file = parsed.get(row.schemaData);
195
+ const levels = file && modelLevelsFromFile(file, row.modelName);
196
+ if (!levels)
197
+ continue;
198
+ if (row.modelFileId)
199
+ byFileId.set(row.modelFileId, levels);
200
+ const seen = byName.get(row.modelName);
201
+ if (!seen) {
202
+ byName.set(row.modelName, { model: levels.model, properties: new Map(levels.properties) });
203
+ continue;
204
+ }
205
+ seen.model = tighter(seen.model, levels.model);
206
+ for (const [name, value] of levels.properties)
207
+ seen.properties.set(name, tighter(seen.properties.get(name), value));
208
+ }
209
+ return {
210
+ model: (modelName) => byName.get(modelName)?.model ?? DEFAULT_NAME_MATCH_LEVEL,
211
+ property: (modelName, propertyName, modelFileId) => {
212
+ const levels = (modelFileId ? byFileId.get(modelFileId) : undefined) ?? byName.get(modelName);
213
+ return levels?.properties.get(propertyName) ?? levels?.model ?? DEFAULT_NAME_MATCH_LEVEL;
214
+ },
215
+ };
216
+ };
217
+ const DEFAULT_LEVELS = {
218
+ model: () => DEFAULT_NAME_MATCH_LEVEL,
219
+ property: () => DEFAULT_NAME_MATCH_LEVEL,
220
+ };
221
+ let current;
222
+ let loading;
223
+ /** Reads the levels from the schema files stored with each model's schema row (schemas.schema_data). */
224
+ const loadNameMatchLevels = async (db = BaseDb.getAppDb()) => {
225
+ if (!db)
226
+ return DEFAULT_LEVELS;
227
+ const load = (async () => {
228
+ const rows = (await db
229
+ .select({ modelName: models.name, modelFileId: models.schemaFileId, schemaData: schemas.schemaData })
230
+ .from(models)
231
+ .innerJoin(modelSchemas, eq(models.id, modelSchemas.modelId))
232
+ .innerJoin(schemas, eq(modelSchemas.schemaId, schemas.id)));
233
+ return nameMatchLevelsFromRows(rows);
234
+ })();
235
+ loading = load;
236
+ try {
237
+ const levels = await load;
238
+ if (loading === load) {
239
+ current = levels;
240
+ previous = undefined;
241
+ }
242
+ return levels;
243
+ }
244
+ finally {
245
+ if (loading === load)
246
+ loading = undefined;
247
+ }
248
+ };
249
+ /** The levels, loaded once and reused until a schema write clears them. */
250
+ const nameMatchLevels = async (db = BaseDb.getAppDb()) => current ?? loading ?? loadNameMatchLevels(db);
251
+ /**
252
+ * The last loaded levels, for synchronous callers (Model.findByModelType). Init loads them before
253
+ * it reports ready. After a schema write clears them, this returns the levels from before the
254
+ * write until the reload that clear starts finishes.
255
+ */
256
+ const currentNameMatchLevels = () => current ?? previous ?? DEFAULT_LEVELS;
257
+ let previous;
258
+ let reloadScheduled = false;
259
+ /**
260
+ * Called when a schema row or its models are written. The next {@link nameMatchLevels} read
261
+ * reloads, and a reload starts on the next tick (one per burst of writes) for synchronous callers.
262
+ */
263
+ const clearNameMatchLevels = () => {
264
+ if (current)
265
+ previous = current;
266
+ current = undefined;
267
+ loading = undefined;
268
+ if (reloadScheduled)
269
+ return;
270
+ reloadScheduled = true;
271
+ setTimeout(() => {
272
+ reloadScheduled = false;
273
+ if (current || loading || !BaseDb.isAppDbReady())
274
+ return;
275
+ loadNameMatchLevels().catch(() => {
276
+ // The next nameMatchLevels() read retries.
277
+ });
278
+ }, 0);
279
+ };
280
+ /**
281
+ * `nameMatch` values in a schema file that aren't a level. They are ignored (the next level up
282
+ * applies); schema loads warn about them.
283
+ */
284
+ const invalidNameMatchValues = (file) => {
285
+ const problems = [];
286
+ const check = (where, value) => {
287
+ if (value !== undefined && !isNameMatchLevel(value)) {
288
+ problems.push(`${where} nameMatch ${JSON.stringify(value)} is not 'exact', 'normalized' or 'fuzzy'`);
289
+ }
290
+ };
291
+ check('schema', file.nameMatch);
292
+ for (const [modelName, modelDef] of Object.entries(file.models ?? {})) {
293
+ check(modelName, modelDef?.nameMatch);
294
+ for (const [propertyName, def] of Object.entries(modelDef?.properties ?? {})) {
295
+ check(`${modelName}.${propertyName}`, def?.nameMatch);
296
+ }
297
+ }
298
+ return problems;
299
+ };
300
+ /** Warns about {@link invalidNameMatchValues}. */
301
+ const warnOnInvalidNameMatch = (file) => {
302
+ const problems = invalidNameMatchValues(file);
303
+ for (const problem of problems) {
304
+ console.warn(`[seed] schema ${file.metadata?.name ?? ''}: ${problem}; it is ignored.`);
305
+ }
306
+ return problems;
307
+ };
308
+
309
+ /**
310
+ * SQLite builds before 3.32 (and builds that keep the old default) allow at most 999 bound
311
+ * parameters per statement (SQLITE_MAX_VARIABLE_NUMBER). sqlite-wasm and libsql allow 32766, but a
312
+ * statement that binds one parameter per synced item still has to stay under 999 to work everywhere.
313
+ */
314
+ const SQLITE_DEFAULT_MAX_VARIABLE_NUMBER = 999;
315
+ /**
316
+ * Values per `IN (...)` list. Leaves room under {@link SQLITE_DEFAULT_MAX_VARIABLE_NUMBER} for the
317
+ * statement's other parameters (a second list, `SET` values, other conditions).
318
+ */
319
+ const IN_LIST_BATCH = 400;
320
+ /**
321
+ * Rows per multi-row INSERT into `table` so the statement binds at most
322
+ * {@link SQLITE_DEFAULT_MAX_VARIABLE_NUMBER} parameters even with every column set.
323
+ */
324
+ const rowsPerInsert = (table) => Math.max(1, Math.floor(SQLITE_DEFAULT_MAX_VARIABLE_NUMBER / Object.keys(getTableColumns(table)).length));
325
+ /** Splits `values` into chunks of at most `size` (one empty list gives no chunks). */
326
+ const chunkValues = (values, size = IN_LIST_BATCH) => {
327
+ if (!Number.isInteger(size) || size < 1) {
328
+ throw new Error(`chunkValues: size must be a positive integer, got ${size}`);
329
+ }
330
+ const chunks = [];
331
+ for (let i = 0; i < values.length; i += size) {
332
+ chunks.push(values.slice(i, i + size));
333
+ }
334
+ return chunks;
335
+ };
336
+ /**
337
+ * Runs `query` once per chunk of `values` (see {@link chunkValues}), one after another, and
338
+ * concatenates the rows. Use it for `inArray(column, values)` reads whose list can be large.
339
+ */
340
+ const selectInBatches = async (values, query, size = IN_LIST_BATCH) => {
341
+ const rows = [];
342
+ for (const chunk of chunkValues(values, size)) {
343
+ rows.push(...(await query(chunk)));
344
+ }
345
+ return rows;
346
+ };
347
+ /** Runs `write` once per chunk of `values`, one after another (deletes / updates with an `IN` list). */
348
+ const writeInBatches = async (values, write, size = IN_LIST_BATCH) => {
349
+ for (const chunk of chunkValues(values, size)) {
350
+ await write(chunk);
351
+ }
352
+ };
353
+
354
+ /**
355
+ * The local seed row for each seed attestation uid. A uid recorded on a seed's versions
356
+ * (`versions.seed_uid`) belongs to that seed: its current uid, or an old one from before it was
357
+ * unpublished and published again (the seed row then holds the new uid, the old version rows keep
358
+ * the old one). A uid no version records resolves by `seeds.uid`. Uids with no local seed are left
359
+ * out of the map.
360
+ *
361
+ * Going through versions first also resolves a republished seed's old uid to the item when an
362
+ * older sync left a second, version-less seed row with that uid (migration 0024 removes those).
363
+ */
364
+ const findSeedRowsByUid = async (uids,
365
+ /** The database or transaction to read; the app database by default. */
366
+ db = BaseDb.getAppDb()) => {
367
+ const result = new Map();
368
+ const appDb = db;
369
+ const wanted = [...new Set(uids.filter(Boolean))];
370
+ if (!appDb || wanted.length === 0)
371
+ return result;
372
+ const owners = await selectInBatches(wanted, (chunk) => appDb
373
+ .selectDistinct({ seedUid: versions.seedUid, seedLocalId: versions.seedLocalId })
374
+ .from(versions)
375
+ .where(and(inArray(versions.seedUid, chunk), isNotNull(versions.seedLocalId))));
376
+ const ownerLocalIds = [...new Set(owners.map((o) => o.seedLocalId))];
377
+ const ownerRows = await selectInBatches(ownerLocalIds, (chunk) => appDb.select().from(seeds).where(inArray(seeds.localId, chunk)));
378
+ const ownerByLocalId = new Map(ownerRows.map((row) => [row.localId, row]));
379
+ for (const { seedUid, seedLocalId } of owners) {
380
+ const row = ownerByLocalId.get(seedLocalId);
381
+ if (!seedUid || !row)
382
+ continue;
383
+ // Versions of two seeds recording one uid shouldn't happen; the seed that holds it wins.
384
+ const current = result.get(seedUid);
385
+ if (!current || (current.uid !== seedUid && row.uid === seedUid))
386
+ result.set(seedUid, row);
387
+ }
388
+ const rest = wanted.filter((uid) => !result.has(uid));
389
+ const byUid = await selectInBatches(rest, (chunk) => appDb.select().from(seeds).where(inArray(seeds.uid, chunk)));
390
+ for (const row of byUid) {
391
+ if (row.uid && !result.has(row.uid))
392
+ result.set(row.uid, row);
393
+ }
394
+ return result;
395
+ };
396
+
397
+ /**
398
+ * The local seed row for an item known by local id, seed uid or both. The local id wins: it names
399
+ * one row, while a uid can be an old one the seed held before it was unpublished and published
400
+ * again. A uid (when the local id is absent or matches nothing) resolves as
401
+ * {@link findSeedRowsByUid} resolves it: the seed whose versions record it (`versions.seed_uid`),
402
+ * else the row with that `seeds.uid`. The zero uid matches nothing.
403
+ */
404
+ const findSeedRow = async ({ localId, uid },
405
+ /** The database or transaction to read; the app database by default. */
406
+ db = BaseDb.getAppDb()) => {
407
+ if (!db)
408
+ return undefined;
409
+ if (localId) {
410
+ const [row] = await db.select().from(seeds).where(eq(seeds.localId, localId)).limit(1);
411
+ if (row?.localId)
412
+ return row;
413
+ }
414
+ if (!uid || uid === ZERO_BYTES32)
415
+ return undefined;
416
+ const row = (await findSeedRowsByUid([uid], db)).get(uid);
417
+ return row?.localId ? row : undefined;
418
+ };
419
+
420
+ var findSeedRow$1 = /*#__PURE__*/Object.freeze({
421
+ __proto__: null,
422
+ findSeedRow: findSeedRow
423
+ });
424
+
425
+ /**
426
+ * Thrown when a model name exists in more than one schema and nothing says which one is meant.
427
+ * Model names are only unique within a schema; pass `schemaName` (or `modelFileId`) to choose.
428
+ */
429
+ class AmbiguousModelError extends Error {
430
+ constructor(modelName, schemaNames) {
431
+ const names = [...new Set(schemaNames)].sort();
432
+ super(`Model "${modelName}" exists in schemas ${names.map((n) => `"${n}"`).join(', ')}; ` +
433
+ `pass schemaName (or modelFileId) to choose one.`);
434
+ this.name = 'AmbiguousModelError';
435
+ this.modelName = modelName;
436
+ this.schemaNames = names;
437
+ }
438
+ }
439
+
440
+ const columns = {
441
+ id: models.id,
442
+ name: models.name,
443
+ schemaFileId: models.schemaFileId,
444
+ };
445
+ /**
446
+ * Resolve a models row from a model name plus whatever scope is known, most specific first:
447
+ * modelFileId, modelId, propertyFileId, then schemaId/schemaName + name, then the name alone (not
448
+ * with `strict` and a schema scope). The name alone is only accepted when it is unambiguous;
449
+ * otherwise this throws AmbiguousModelError.
450
+ */
451
+ const resolveModelRecord = async (modelName, scope = {}, db = BaseDb.getAppDb()) => {
452
+ if (!db)
453
+ return undefined;
454
+ if (scope.modelFileId) {
455
+ const rows = await db
456
+ .select(columns)
457
+ .from(models)
458
+ .where(eq(models.schemaFileId, scope.modelFileId))
459
+ .limit(1);
460
+ if (rows.length > 0)
461
+ return rows[0];
462
+ }
463
+ if (scope.modelId) {
464
+ const rows = await db.select(columns).from(models).where(eq(models.id, scope.modelId)).limit(1);
465
+ if (rows.length > 0)
466
+ return rows[0];
467
+ }
468
+ if (scope.propertyFileId) {
469
+ const rows = await db
470
+ .select(columns)
471
+ .from(properties)
472
+ .innerJoin(models, eq(properties.modelId, models.id))
473
+ .where(eq(properties.schemaFileId, scope.propertyFileId))
474
+ .limit(1);
475
+ if (rows.length > 0)
476
+ return rows[0];
477
+ }
478
+ if (!modelName)
479
+ return undefined;
480
+ if (scope.schemaId || scope.schemaName) {
481
+ const rows = await db
482
+ .select(columns)
483
+ .from(models)
484
+ .innerJoin(modelSchemas, eq(models.id, modelSchemas.modelId))
485
+ .innerJoin(schemas, eq(modelSchemas.schemaId, schemas.id))
486
+ .where(and(eq(models.name, modelName), scope.schemaId ? eq(schemas.id, scope.schemaId) : eq(schemas.name, scope.schemaName)))
487
+ .limit(1);
488
+ if (rows.length > 0)
489
+ return rows[0];
490
+ if (scope.strict)
491
+ return undefined;
492
+ }
493
+ return resolveModelRecordByNameOnly(modelName, db);
494
+ };
495
+ /**
496
+ * Name of the schema a models row is linked to (the newest version's when several are), or
497
+ * undefined for an unlinked stub.
498
+ */
499
+ const getModelSchemaName = async (modelId, db = BaseDb.getAppDb()) => {
500
+ if (!db)
501
+ return undefined;
502
+ const rows = await db
503
+ .select({ schemaName: schemas.name })
504
+ .from(modelSchemas)
505
+ .innerJoin(schemas, eq(modelSchemas.schemaId, schemas.id))
506
+ .where(eq(modelSchemas.modelId, modelId))
507
+ .orderBy(desc(schemas.version))
508
+ .limit(1);
509
+ return rows[0]?.schemaName ?? undefined;
510
+ };
511
+ /** The model a property row (by its schemaFileId) belongs to, and the property's data type. */
512
+ const resolvePropertyModelRecord = async (propertyFileId, db = BaseDb.getAppDb()) => {
513
+ if (!db || !propertyFileId)
514
+ return undefined;
515
+ const rows = await db
516
+ .select({ ...columns, dataType: properties.dataType })
517
+ .from(properties)
518
+ .innerJoin(models, eq(properties.modelId, models.id))
519
+ .where(eq(properties.schemaFileId, propertyFileId))
520
+ .limit(1);
521
+ const row = rows[0];
522
+ return row ? { id: row.id, name: row.name, schemaFileId: row.schemaFileId, dataType: row.dataType ?? '' } : undefined;
523
+ };
524
+ /**
525
+ * Name-only resolution. Rows linked to a schema win over unlinked stubs (null-schemaFileId rows
526
+ * from ref resolution); if more than one linked row has the name it is ambiguous and this throws.
527
+ * `label` names the lookup in the error (defaults to the model name).
528
+ */
529
+ const resolveModelRecordByNameOnly = async (modelName, db, label = Array.isArray(modelName) ? modelName.join(', ') : modelName) => {
530
+ const rows = (await db
531
+ .select({ ...columns, schemaName: schemas.name })
532
+ .from(models)
533
+ .leftJoin(modelSchemas, eq(models.id, modelSchemas.modelId))
534
+ .leftJoin(schemas, eq(modelSchemas.schemaId, schemas.id))
535
+ .where(Array.isArray(modelName) ? inArray(models.name, modelName) : eq(models.name, modelName))
536
+ .orderBy(desc(models.id)));
537
+ if (rows.length === 0)
538
+ return undefined;
539
+ const linked = rows.filter((r) => r.schemaName);
540
+ const linkedIds = new Set(linked.map((r) => r.id));
541
+ if (linkedIds.size > 1) {
542
+ throw new AmbiguousModelError(label, linked.map((r) => r.schemaName));
543
+ }
544
+ const chosen = linked[0] ?? rows[0];
545
+ return { id: chosen.id, name: chosen.name, schemaFileId: chosen.schemaFileId };
546
+ };
547
+ /**
548
+ * The model names a model name or EAS model type can refer to: the name itself, else the names
549
+ * that match it in the tightest tier (ADR 0008, as Model.findByModelType matches): `new_model` →
550
+ * "New model", `sync_storage_post` → "SyncStoragePost", `htmlpage` → "HTMLPage". Each name at its
551
+ * nameMatch level (ADR 0009).
552
+ */
553
+ const modelNamesForNameOrType = async (nameOrType, db) => {
554
+ const rows = (await db.selectDistinct({ name: models.name }).from(models));
555
+ const names = rows.map((r) => r.name);
556
+ if (names.includes(nameOrType))
557
+ return [nameOrType];
558
+ const levels = await nameMatchLevels(db);
559
+ return [...localNamesInBestMatchTier(nameOrType, names, { level: (name) => levels.model(name) })];
560
+ };
561
+ /**
562
+ * resolveModelRecord for a model name **or** an EAS model type (snake_case model name, what sync
563
+ * stores in seeds.type): "New model" resolves from `new_model`. Scope and ambiguity behave as in
564
+ * resolveModelRecord: modelFileId/modelId first, then the schema, then the name(s) alone, which throws
565
+ * AmbiguousModelError when models in several schemas match.
566
+ */
567
+ const resolveModelRecordByNameOrType = async (nameOrType, scope = {}, db = BaseDb.getAppDb()) => {
568
+ if (!db)
569
+ return undefined;
570
+ if (scope.modelFileId || scope.modelId || scope.propertyFileId) {
571
+ const byId = await resolveModelRecord(undefined, scope, db);
572
+ if (byId)
573
+ return byId;
574
+ }
575
+ if (!nameOrType)
576
+ return undefined;
577
+ const names = await modelNamesForNameOrType(nameOrType, db);
578
+ if (names.length === 0)
579
+ return undefined;
580
+ if (names.length === 1) {
581
+ return resolveModelRecord(names[0], { ...scope, modelFileId: undefined, modelId: undefined, propertyFileId: undefined }, db);
582
+ }
583
+ // Several model names map to this type (e.g. "New model" and "NewModel" in different schemas).
584
+ if (scope.schemaId || scope.schemaName) {
585
+ const rows = (await db
586
+ .select(columns)
587
+ .from(models)
588
+ .innerJoin(modelSchemas, eq(models.id, modelSchemas.modelId))
589
+ .innerJoin(schemas, eq(modelSchemas.schemaId, schemas.id))
590
+ .where(and(inArray(models.name, names), scope.schemaId ? eq(schemas.id, scope.schemaId) : eq(schemas.name, scope.schemaName))));
591
+ if (new Set(rows.map((r) => r.id)).size === 1)
592
+ return rows[0];
593
+ if (scope.strict && rows.length === 0)
594
+ return undefined;
595
+ }
596
+ return resolveModelRecordByNameOnly(names, db, nameOrType);
597
+ };
598
+ /**
599
+ * The model name that a model name or EAS model type (`seeds.type`, `metadata.model_type`) refers
600
+ * to, resolved as {@link resolveModelRecordByNameOrType} resolves it: `blog_post` → "Blog Post"
601
+ * through the model's easName at its nameMatch level, never by re-casing the type. Undefined when
602
+ * no model matches; throws AmbiguousModelError when models in several schemas do.
603
+ */
604
+ const modelNameForType = async (nameOrType, scope = {}, db = BaseDb.getAppDb()) => (await resolveModelRecordByNameOrType(nameOrType, scope, db))?.name;
605
+ /**
606
+ * Work out which model (schemaFileId) an item belongs to from whatever is at hand: an explicit
607
+ * modelFileId, the seed's model_file_id, or a metadata row's property_id → properties.model_id.
608
+ */
609
+ const resolveItemModelFileId = async (hints, db = BaseDb.getAppDb()) => {
610
+ if (hints.modelFileId)
611
+ return hints.modelFileId;
612
+ if (!db)
613
+ return undefined;
614
+ if (hints.seedLocalId || hints.seedUid) {
615
+ const seed = await findSeedRow({ localId: hints.seedLocalId, uid: hints.seedUid }, db);
616
+ if (seed?.modelFileId)
617
+ return seed.modelFileId;
618
+ }
619
+ if (hints.propertyId) {
620
+ const rows = await db
621
+ .select({ modelFileId: models.schemaFileId })
622
+ .from(properties)
623
+ .innerJoin(models, eq(properties.modelId, models.id))
624
+ .where(eq(properties.id, hints.propertyId))
625
+ .limit(1);
626
+ if (rows[0]?.modelFileId)
627
+ return rows[0].modelFileId;
628
+ }
629
+ return undefined;
630
+ };
631
+ /**
632
+ * Resolve a ref (e.g. a Relation's target model) by name from the schema(s) its owning model belongs
633
+ * to, so "Tag" means the owner schema's Tag rather than any Tag. Falls back to resolveModelRecord.
634
+ */
635
+ const resolveRefModelRecord = async (refModelName, owner, db = BaseDb.getAppDb()) => {
636
+ if (!db || !refModelName)
637
+ return undefined;
638
+ const ownerRow = owner.modelFileId || owner.modelId ? await resolveModelRecord(undefined, owner, db) : undefined;
639
+ const schemaFilter = ownerRow
640
+ ? inArray(modelSchemas.schemaId,
641
+ // any schema the owner is linked to
642
+ db.select({ schemaId: modelSchemas.schemaId }).from(modelSchemas).where(eq(modelSchemas.modelId, ownerRow.id)))
643
+ : owner.schemaId
644
+ ? eq(modelSchemas.schemaId, owner.schemaId)
645
+ : owner.schemaName
646
+ ? eq(schemas.name, owner.schemaName)
647
+ : undefined;
648
+ if (schemaFilter) {
649
+ const rows = await db
650
+ .select(columns)
651
+ .from(models)
652
+ .innerJoin(modelSchemas, eq(models.id, modelSchemas.modelId))
653
+ .innerJoin(schemas, eq(modelSchemas.schemaId, schemas.id))
654
+ .where(and(eq(models.name, refModelName), schemaFilter))
655
+ .limit(1);
656
+ if (rows.length > 0)
657
+ return rows[0];
658
+ }
659
+ return resolveModelRecord(refModelName, {}, db);
660
+ };
661
+
662
+ var resolveModelRecord$1 = /*#__PURE__*/Object.freeze({
663
+ __proto__: null,
664
+ getModelSchemaName: getModelSchemaName,
665
+ modelNameForType: modelNameForType,
666
+ resolveItemModelFileId: resolveItemModelFileId,
667
+ resolveModelRecord: resolveModelRecord,
668
+ resolveModelRecordByNameOrType: resolveModelRecordByNameOrType,
669
+ resolvePropertyModelRecord: resolvePropertyModelRecord,
670
+ resolveRefModelRecord: resolveRefModelRecord
671
+ });
672
+
673
+ /**
674
+ * Lowercase + trim an address for persist and compare.
675
+ * Does not checksum or validate — test and placeholder addresses must keep working.
676
+ */
677
+ function normalizeHexAddress(addr) {
678
+ return addr.trim().toLowerCase();
679
+ }
680
+ /**
681
+ * Lowercase, drop empties, and dedupe (first-seen order).
682
+ */
683
+ function normalizeAddressList(addrs) {
684
+ if (!addrs?.length)
685
+ return [];
686
+ const seen = new Set();
687
+ const out = [];
688
+ for (const addr of addrs) {
689
+ if (addr == null || addr === '')
690
+ continue;
691
+ const normalized = normalizeHexAddress(String(addr));
692
+ if (!normalized || seen.has(normalized))
693
+ continue;
694
+ seen.add(normalized);
695
+ out.push(normalized);
696
+ }
697
+ return out;
698
+ }
699
+ /**
700
+ * Lowercase a publisher stamp. Empty / null → undefined (omit on write).
701
+ */
702
+ function normalizePublisher(publisher) {
703
+ if (publisher == null || publisher === '')
704
+ return undefined;
705
+ const normalized = normalizeHexAddress(publisher);
706
+ return normalized || undefined;
707
+ }
708
+ /**
709
+ * Normalizes AddressConfiguration to { owned, watched }.
710
+ * - string[] -> { owned: addresses, watched: [] }
711
+ * - { owned, watched? } -> { owned, watched: watched ?? [] }
712
+ * Addresses are lowercased and deduped.
713
+ */
714
+ function normalizeAddressConfig(addresses) {
715
+ if (!addresses) {
716
+ return { owned: [], watched: [] };
717
+ }
718
+ if (Array.isArray(addresses)) {
719
+ return { owned: normalizeAddressList(addresses), watched: [] };
720
+ }
721
+ return {
722
+ owned: normalizeAddressList(addresses.owned),
723
+ watched: normalizeAddressList(addresses.watched),
724
+ };
725
+ }
726
+
69
727
  /** Ref model name when propDef is a List of Relation, else undefined. */
70
728
  function listRelationRef(propDef) {
71
729
  if (!propDef)
@@ -159,123 +817,1205 @@ function metadataStorageName(key, def) {
159
817
  return toMetadataPropertyName(key, def?.dataType, def?.refValueType);
160
818
  }
161
819
  /**
162
- * Every name a row of this property may be stored under: its storage name, its key, and for an
163
- * Image, File, Html or Relation (single or List) `<key>Id`, which older writers and sync used.
820
+ * Every name a row of this property may be stored under: its storage name, its key, and for an
821
+ * Image, File, Html or Relation (single or List) `<key>Id`, which older writers and sync used.
822
+ */
823
+ function metadataStoredNameForms(key, def) {
824
+ const forms = [metadataStorageName(key, def), key];
825
+ if (!ID_SUFFIX_EXCLUDE.has(key) && needsMetadataIdSuffix(def?.dataType, def?.refValueType))
826
+ forms.push(`${key}Id`);
827
+ return [...new Set(forms)];
828
+ }
829
+ /**
830
+ * The schema key a stored metadata name belongs to among `defs` (keyed by schema key): an exact key
831
+ * first, then any of a key's stored forms. Undefined when no property matches. For rows without
832
+ * `property_id`; rows with one take the key from their properties row.
833
+ */
834
+ function propertyKeyFromStoredName(defs, storedName) {
835
+ if (!storedName)
836
+ return undefined;
837
+ if (Object.prototype.hasOwnProperty.call(defs, storedName))
838
+ return storedName;
839
+ for (const [key, def] of Object.entries(defs)) {
840
+ if (metadataStoredNameForms(key, def).includes(storedName))
841
+ return key;
842
+ }
843
+ return undefined;
844
+ }
845
+ /**
846
+ * The names a property's rows may be stored under, for matching rows that have no property_id and
847
+ * outbox ops (which name a property, not its id): the name with and without its `Id` for an Image,
848
+ * File, Html or Relation, or when `dataType` is unknown.
849
+ */
850
+ function getMetadataPropertyNamesForQuery(propertyName, dataType, refValueType) {
851
+ if (ID_SUFFIX_EXCLUDE.has(propertyName))
852
+ return [propertyName];
853
+ if (propertyName.endsWith('Ids'))
854
+ return [propertyName];
855
+ if (propertyName.endsWith('Id')) {
856
+ const base = propertyName.slice(0, -2);
857
+ return [propertyName, base];
858
+ }
859
+ const needsId = needsMetadataIdSuffix(dataType, refValueType) ||
860
+ (dataType === undefined && refValueType === undefined);
861
+ if (needsId) {
862
+ return [propertyName, `${propertyName}Id`];
863
+ }
864
+ return [propertyName];
865
+ }
866
+ /**
867
+ * The name an item exposes a property under, from the name it is stored under: its alias when it
868
+ * has one (List of relation), else `tagIds` → `tags`, `authorId` → `author`. Names in
869
+ * PROPERTY_NAMES_EXEMPT_FROM_ID_SUFFIX_STRIP keep their `Id`.
870
+ */
871
+ function toItemPropertyKey(storedName, alias) {
872
+ if (alias)
873
+ return alias;
874
+ if (storedName.endsWith('Ids'))
875
+ return pluralize(storedName.slice(0, -3));
876
+ if (storedName.endsWith('Id') && !PROPERTY_NAMES_EXEMPT_FROM_ID_SUFFIX_STRIP.has(storedName)) {
877
+ return storedName.slice(0, -2);
878
+ }
879
+ return storedName;
880
+ }
881
+ /**
882
+ * Schema property name from metadata name (strip Id/Ids suffix).
883
+ */
884
+ function toSchemaPropertyName(propertyName) {
885
+ if (propertyName.endsWith('Ids'))
886
+ return propertyName.slice(0, -3);
887
+ if (propertyName.endsWith('Id'))
888
+ return propertyName.slice(0, -2);
889
+ return undefined;
890
+ }
891
+ /**
892
+ * Alternate property name for instance lookup (base <-> Id variant).
893
+ */
894
+ function getAlternatePropertyNameForInstanceLookup(propertyName) {
895
+ if (ID_SUFFIX_EXCLUDE.has(propertyName))
896
+ return undefined;
897
+ if (propertyName.endsWith('Id'))
898
+ return propertyName.slice(0, -2);
899
+ return `${propertyName}Id`;
900
+ }
901
+ /**
902
+ * The EAS property name publish writes for an item property (ADR 0008 §3), already in EAS form:
903
+ *
904
+ * - List of relations: its `<name><Model>Ids` storage name (`author_identity_ids`).
905
+ * - Single Relation, Image, File or Html: its schema key (`author`), never the local `<name>Id`
906
+ * storage name. The key is `propertyDef.name` when known, else `propertyName` without its `Id`.
907
+ * - Anything else: the property name.
908
+ */
909
+ function easPropertyNameForPublish(propertyName, propertyDef) {
910
+ const listRelationName = listRelationEasPropertyName(propertyName, propertyDef ?? undefined);
911
+ if (listRelationName)
912
+ return easName(listRelationName);
913
+ if (propertyDef &&
914
+ normalizeDataType(propertyDef.dataType) !== 'List' &&
915
+ needsMetadataIdSuffix(propertyDef.dataType, propertyDef.refValueType) &&
916
+ !ID_SUFFIX_EXCLUDE.has(propertyName)) {
917
+ const key = propertyDef.name ?? (propertyName.endsWith('Id') ? propertyName.slice(0, -2) : propertyName);
918
+ return easName(key);
919
+ }
920
+ return easName(propertyName);
921
+ }
922
+ /**
923
+ * The EAS schema definition publish attests a property under (`string title`, `bytes32 author`),
924
+ * or undefined when its data type has no EAS type. Look schemas up by this, not by name: a name
925
+ * can have schemas of several types (Optimism Sepolia has `bytes32 storage_transaction_id` beside
926
+ * `string storage_transaction_id`).
927
+ */
928
+ function easSchemaDefinitionForProperty(propertyName, propertyDef) {
929
+ if (!propertyDef?.dataType)
930
+ return undefined;
931
+ const dataType = normalizeDataType(String(propertyDef.dataType));
932
+ const easType = INTERNAL_DATA_TYPES[dataType]?.eas;
933
+ if (!easType)
934
+ return undefined;
935
+ return `${easType} ${easPropertyNameForPublish(propertyName, propertyDef)}`;
936
+ }
937
+
938
+ /** Schema file spellings that normalizePropertyRecordSchema maps to internal fields. */
939
+ const FILE_ALIAS_KEYS = ['type', 'model', 'items', 'storage'];
940
+ /**
941
+ * Fields an instance or a row carries that aren't part of the definition. Keys starting with `_`
942
+ * (`_dbId`, `_propertyFileId`, `_originalValues`, ...) are dropped too.
943
+ */
944
+ const NON_DEFINITION_KEYS = new Set([
945
+ ...FILE_ALIAS_KEYS,
946
+ 'name',
947
+ 'modelId',
948
+ 'modelName',
949
+ 'refModelId',
950
+ 'refModelName',
951
+ 'schemaName',
952
+ 'isEdited',
953
+ 'TObject',
954
+ ]);
955
+ const parseDefinition = (json) => {
956
+ if (!json)
957
+ return {};
958
+ try {
959
+ const parsed = JSON.parse(json);
960
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
961
+ }
962
+ catch {
963
+ return {};
964
+ }
965
+ };
966
+ /** A definition in any accepted shape, reduced to what the column stores. */
967
+ const toStoredDefinition = (def) => {
968
+ const normalized = normalizePropertyRecordSchema(def ?? {});
969
+ const hasRef = normalized.dataType === ModelPropertyDataTypes.Relation || normalized.dataType === ModelPropertyDataTypes.List;
970
+ const out = {};
971
+ for (const [key, value] of Object.entries(normalized)) {
972
+ if (value === undefined || typeof value === 'function')
973
+ continue;
974
+ if (key.startsWith('_') || NON_DEFINITION_KEYS.has(key))
975
+ continue;
976
+ if (key === 'ref' && !hasRef)
977
+ continue;
978
+ out[key] = value;
979
+ }
980
+ return out;
981
+ };
982
+ /**
983
+ * The `definition` column value for a definition in any accepted shape (schema file property,
984
+ * Model.create property, ModelProperty context). With `previous` (the row's current value), fields
985
+ * the new definition doesn't have are kept: a write that only knows the columns (a creation
986
+ * payload, a row-built instance) must not drop the validation a schema file or code stored.
987
+ */
988
+ function propertyDefinitionColumn(def, previous) {
989
+ const stored = toStoredDefinition(def);
990
+ return JSON.stringify(previous ? { ...toStoredDefinition(parseDefinition(previous)), ...stored } : stored);
991
+ }
992
+ /**
993
+ * A row as a {@link PropertyDefinition}. `refModelName` is the name of the row's `ref_model_id`
994
+ * model; without one, a Relation or List keeps the definition's `ref` (a target not stored yet).
995
+ */
996
+ function propertyDefinitionFromRow(row, refModelName) {
997
+ const stored = toStoredDefinition(parseDefinition(row.definition));
998
+ const dataType = normalizeDataType(row.dataType);
999
+ const hasRef = dataType === ModelPropertyDataTypes.Relation || dataType === ModelPropertyDataTypes.List;
1000
+ const ref = refModelName ?? (hasRef ? stored.ref : undefined);
1001
+ const def = {
1002
+ ...stored,
1003
+ id: row.schemaFileId ?? undefined,
1004
+ _dbId: row.id,
1005
+ name: row.name,
1006
+ modelId: row.modelId,
1007
+ dataType,
1008
+ ref,
1009
+ refModelName: ref,
1010
+ refModelId: row.refModelId ?? undefined,
1011
+ refValueType: row.refValueType ? normalizeDataType(row.refValueType) : undefined,
1012
+ storageType: row.storageType ?? undefined,
1013
+ localStorageDir: row.localStorageDir ?? undefined,
1014
+ filenameSuffix: row.filenameSuffix ?? undefined,
1015
+ required: row.required ?? stored.required ?? undefined,
1016
+ };
1017
+ for (const key of Object.keys(def))
1018
+ if (def[key] === undefined)
1019
+ delete def[key];
1020
+ return def;
1021
+ }
1022
+ const refModels = alias(models, 'ref_model');
1023
+ const selectDefinitionRows = (db) => db
1024
+ .select({ property: properties, refModelName: refModels.name })
1025
+ .from(properties)
1026
+ .leftJoin(refModels, eq(refModels.id, properties.refModelId));
1027
+ /**
1028
+ * The complete definition of one property, from its row: by row id (`metadata.property_id`), or by
1029
+ * model row id and property name. Undefined when there's no such row.
1030
+ */
1031
+ async function getPropertyDefinition(target, db = BaseDb.getAppDb()) {
1032
+ if (!db)
1033
+ throw new Error('Database not available');
1034
+ const where = typeof target === 'number'
1035
+ ? eq(properties.id, target)
1036
+ : and(eq(properties.modelId, target.modelId), eq(properties.name, target.name));
1037
+ const [row] = await selectDefinitionRows(db).where(where).limit(1);
1038
+ return row ? propertyDefinitionFromRow(row.property, row.refModelName) : undefined;
1039
+ }
1040
+ /** {@link getPropertyDefinition} for every property of one model, by property name, in one read. */
1041
+ async function getPropertyDefinitions(modelId, db = BaseDb.getAppDb()) {
1042
+ if (!db)
1043
+ throw new Error('Database not available');
1044
+ const rows = await selectDefinitionRows(db).where(eq(properties.modelId, modelId));
1045
+ return new Map(rows.map((row) => [
1046
+ row.property.name,
1047
+ propertyDefinitionFromRow(row.property, row.refModelName),
1048
+ ]));
1049
+ }
1050
+ /** {@link getPropertyDefinition} for each of these property file ids that has a row, by file id. */
1051
+ async function getPropertyDefinitionsByFileId(propertyFileIds, db = BaseDb.getAppDb()) {
1052
+ if (!db)
1053
+ throw new Error('Database not available');
1054
+ if (propertyFileIds.length === 0)
1055
+ return new Map();
1056
+ const rows = await selectDefinitionRows(db).where(inArray(properties.schemaFileId, propertyFileIds));
1057
+ return new Map(rows.map((row) => [
1058
+ row.property.schemaFileId,
1059
+ propertyDefinitionFromRow(row.property, row.refModelName),
1060
+ ]));
1061
+ }
1062
+ /** Fields only a definition holds (no column), which code-defined properties can set. */
1063
+ const DEFINITION_ONLY_FIELDS = ['validation', 'htmlEmbeddedDataUriPolicy', 'description'];
1064
+ /** The {@link DEFINITION_ONLY_FIELDS} a definition sets. */
1065
+ const definitionOnlyFields = (def) => Object.fromEntries(DEFINITION_ONLY_FIELDS.filter((key) => def?.[key] !== undefined).map((key) => [key, def[key]]));
1066
+ /**
1067
+ * Stores code-defined fields ({@link definitionOnlyFields}) in the row's definition, when the row
1068
+ * exists and its definition differs. For properties whose row was written before the code that
1069
+ * defines them ran (a schema file's row, an earlier run), so their rules persist at create time.
1070
+ */
1071
+ async function persistDefinitionFields(where, fields, db = BaseDb.getAppDb()) {
1072
+ if (!db || Object.keys(fields).length === 0)
1073
+ return;
1074
+ const condition = 'id' in where ? eq(properties.id, where.id) : eq(properties.schemaFileId, where.schemaFileId);
1075
+ const [row] = await db.select({ id: properties.id, definition: properties.definition }).from(properties).where(condition).limit(1);
1076
+ if (!row)
1077
+ return;
1078
+ const next = propertyDefinitionColumn(fields, row.definition);
1079
+ if (row.definition && JSON.stringify(toStoredDefinition(parseDefinition(row.definition))) === next)
1080
+ return;
1081
+ await db.update(properties).set({ definition: next }).where(eq(properties.id, row.id));
1082
+ }
1083
+
1084
+ var propertyDefinition = /*#__PURE__*/Object.freeze({
1085
+ __proto__: null,
1086
+ DEFINITION_ONLY_FIELDS: DEFINITION_ONLY_FIELDS,
1087
+ definitionOnlyFields: definitionOnlyFields,
1088
+ getPropertyDefinition: getPropertyDefinition,
1089
+ getPropertyDefinitions: getPropertyDefinitions,
1090
+ getPropertyDefinitionsByFileId: getPropertyDefinitionsByFileId,
1091
+ persistDefinitionFields: persistDefinitionFields,
1092
+ propertyDefinitionColumn: propertyDefinitionColumn,
1093
+ propertyDefinitionFromRow: propertyDefinitionFromRow
1094
+ });
1095
+
1096
+ /**
1097
+ * The writers of definition rows: `schemas`, `models`, `properties` and `model_schemas` (SDK audit
1098
+ * T9). One upsert per table. Every function takes the database to write, normally a transaction
1099
+ * from {@link inDefinitionsTransaction}, and reads only through it: in the browser a query on
1100
+ * `BaseDb.getAppDb()` waits for an open transaction to end.
1101
+ *
1102
+ * The lookups here decide which row a definition is (a schema by file id, then by name; a model by
1103
+ * file id, then an adoptable row of that name; a property by file id, then by name in its model).
1104
+ * The unique keys (`schema_file_id` on all three tables, `properties(name, model_id)`,
1105
+ * `model_schemas(model_id, schema_id)`) are enforced by the database, and inserts resolve a
1106
+ * conflict with ON CONFLICT instead of a second lookup.
1107
+ */
1108
+ const logger$b = debug('seedSdk:db:write:definitions');
1109
+ /**
1110
+ * Runs one user action on definitions (an apply, a save, a rename) in one transaction. `fn` must
1111
+ * write through `tx` only and have no other side effects: it is retried whole when another tab
1112
+ * holds the database.
1113
+ */
1114
+ const inDefinitionsTransaction = (fn) => BaseDb.transaction(fn);
1115
+ // ---------------------------------------------------------------------------------------------
1116
+ // model_schemas
1117
+ /** Links a model to a schema. Returns true when this call inserted the link. */
1118
+ async function linkModelToSchemaRow(db, modelId, schemaId) {
1119
+ const inserted = await db
1120
+ .insert(modelSchemas)
1121
+ .values({ modelId, schemaId })
1122
+ .onConflictDoNothing()
1123
+ .returning({ id: modelSchemas.id });
1124
+ return inserted.length > 0;
1125
+ }
1126
+ /**
1127
+ * Writes a schema row and returns it with `inserted` set when it is new.
1128
+ *
1129
+ * The row is the one with `schemaFileId`, else one of that name: a draft write takes a draft of
1130
+ * that name; a published write takes a published row, else a draft (which it publishes). A
1131
+ * published row keeps its schemaFileId against another id of the same version, and keeps its newer
1132
+ * version against an older version's file. With `rowId` that row is written, found by the caller
1133
+ * (a rename finds it by the old name), and takes `schemaFileId`.
1134
+ */
1135
+ async function upsertSchemaRow(db, input, options = {}) {
1136
+ // The stored schema file may change its nameMatch levels (ADR 0009).
1137
+ clearNameMatchLevels();
1138
+ const { schemaFileId, schemaData, isDraft, isEdited, contentHash } = input;
1139
+ const update = async (existing, updates) => {
1140
+ if (input.name && existing.name !== input.name)
1141
+ updates.name = input.name;
1142
+ if (schemaData !== undefined)
1143
+ updates.schemaData = schemaData;
1144
+ if (contentHash !== undefined && existing.contentHash !== contentHash)
1145
+ updates.contentHash = contentHash;
1146
+ if (input.version !== undefined && existing.version !== input.version)
1147
+ updates.version = input.version;
1148
+ if (isDraft !== undefined && existing.isDraft !== isDraft)
1149
+ updates.isDraft = isDraft;
1150
+ if (isEdited !== undefined && existing.isEdited !== isEdited)
1151
+ updates.isEdited = isEdited;
1152
+ if (input.updatedAt && existing.updatedAt !== input.updatedAt)
1153
+ updates.updatedAt = input.updatedAt;
1154
+ if (Object.keys(updates).length > 0) {
1155
+ await db.update(schemas).set(updates).where(eq(schemas.id, existing.id));
1156
+ }
1157
+ return { ...existing, ...updates, inserted: false };
1158
+ };
1159
+ if (options.rowId !== undefined) {
1160
+ const [row] = await db.select().from(schemas).where(eq(schemas.id, options.rowId)).limit(1);
1161
+ if (!row)
1162
+ throw new Error(`Schema row ${options.rowId} not found`);
1163
+ return update(row, schemaFileId && row.schemaFileId !== schemaFileId ? { schemaFileId } : {});
1164
+ }
1165
+ if (schemaFileId) {
1166
+ const [byFileId] = await db.select().from(schemas).where(eq(schemas.schemaFileId, schemaFileId)).limit(1);
1167
+ if (byFileId)
1168
+ return update(byFileId, {});
1169
+ }
1170
+ if (isDraft === true) {
1171
+ const [byName] = await db.select().from(schemas).where(eq(schemas.name, input.name)).limit(1);
1172
+ if (byName?.isDraft === true) {
1173
+ const updates = { updatedAt: input.updatedAt || Date.now() };
1174
+ if (schemaFileId && byName.schemaFileId !== schemaFileId)
1175
+ updates.schemaFileId = schemaFileId;
1176
+ return update(byName, updates);
1177
+ }
1178
+ }
1179
+ // By name: a published write prefers a published row, then takes a draft of that name.
1180
+ let [existing] = await db
1181
+ .select()
1182
+ .from(schemas)
1183
+ .where(isDraft === false ? and(eq(schemas.name, input.name), eq(schemas.isDraft, false)) : eq(schemas.name, input.name))
1184
+ .limit(1);
1185
+ if (!existing && isDraft === false) {
1186
+ [existing] = await db
1187
+ .select()
1188
+ .from(schemas)
1189
+ .where(and(eq(schemas.name, input.name), eq(schemas.isDraft, true)))
1190
+ .limit(1);
1191
+ }
1192
+ if (existing) {
1193
+ // A complete schema file can bring a version under its own id (imported files may not keep it).
1194
+ const isOtherVersionFile = !!schemaFileId &&
1195
+ !!existing.schemaFileId &&
1196
+ existing.schemaFileId !== schemaFileId &&
1197
+ existing.isDraft !== true &&
1198
+ input.version !== undefined &&
1199
+ input.version !== existing.version;
1200
+ if (isOtherVersionFile && input.version < existing.version) {
1201
+ // An older version's file: rolling the row back to it would leave the newer version's id
1202
+ // pointing at the older version's data.
1203
+ logger$b(`Keeping schema "${input.name}" at v${existing.version}; not replacing it with older v${input.version}`);
1204
+ return { ...existing, inserted: false };
1205
+ }
1206
+ const updates = {};
1207
+ // A published row keeps its id unless this is a newer version's file; a draft or id-less row
1208
+ // takes the new one.
1209
+ if (schemaFileId &&
1210
+ existing.schemaFileId !== schemaFileId &&
1211
+ (existing.isDraft === true || !existing.schemaFileId || isOtherVersionFile)) {
1212
+ updates.schemaFileId = schemaFileId;
1213
+ }
1214
+ return update(existing, updates);
1215
+ }
1216
+ const [row] = await db
1217
+ .insert(schemas)
1218
+ .values({
1219
+ name: input.name,
1220
+ version: input.version,
1221
+ schemaFileId: schemaFileId || null,
1222
+ schemaData: schemaData || null,
1223
+ contentHash: contentHash ?? null,
1224
+ isDraft: isDraft ?? false,
1225
+ isEdited: isEdited ?? false,
1226
+ createdAt: input.createdAt,
1227
+ updatedAt: input.updatedAt,
1228
+ })
1229
+ .returning();
1230
+ return { ...row, inserted: true };
1231
+ }
1232
+ // ---------------------------------------------------------------------------------------------
1233
+ // models
1234
+ /**
1235
+ * Writes the models row with `schemaFileId`, or inserts a name-only row without one. An existing
1236
+ * row takes `name`; `isEdited` is set on insert only.
1237
+ */
1238
+ async function upsertModelRow(db, input) {
1239
+ const values = { name: input.name, schemaFileId: input.schemaFileId || null, isEdited: input.isEdited ?? false };
1240
+ if (!values.schemaFileId) {
1241
+ const [row] = await db.insert(models).values(values).returning();
1242
+ return row;
1243
+ }
1244
+ const [row] = await db
1245
+ .insert(models)
1246
+ .values(values)
1247
+ .onConflictDoUpdate({ target: models.schemaFileId, set: { name: input.name } })
1248
+ .returning();
1249
+ return row;
1250
+ }
1251
+ /** Name-only models rows for the names that have none yet. Returns every row of those names. */
1252
+ async function insertModelStubs(db, modelNames) {
1253
+ const columns = { id: models.id, name: models.name, schemaFileId: models.schemaFileId };
1254
+ const rows = modelNames.length
1255
+ ? await db.select(columns).from(models).where(inArray(models.name, modelNames))
1256
+ : [];
1257
+ const missing = [...new Set(modelNames)].filter((name) => !rows.some((row) => row.name === name));
1258
+ if (missing.length > 0) {
1259
+ rows.push(...(await db.insert(models).values(missing.map((name) => ({ name }))).returning(columns)));
1260
+ }
1261
+ return rows;
1262
+ }
1263
+ const isLinkedTo = async (db, modelId, schemaId) => {
1264
+ const links = await db
1265
+ .select({ schemaId: modelSchemas.schemaId })
1266
+ .from(modelSchemas)
1267
+ .where(eq(modelSchemas.modelId, modelId));
1268
+ return {
1269
+ any: links.length > 0,
1270
+ other: links.some((link) => link.schemaId !== schemaId),
1271
+ };
1272
+ };
1273
+ /**
1274
+ * Finds or creates the models row for `modelName` in a schema, by `modelFileId` (preferred) or
1275
+ * name. Model names are only unique per schema, so a same-name row is reused only when no other
1276
+ * schema claims it: a null-schemaFileId stub (from ref resolution or init), or for a ref without a
1277
+ * file id, this schema's row. A row with a file id that no schema links is a deleted schema's
1278
+ * leftover and is not adopted. An adopted row takes `modelFileId`.
1279
+ */
1280
+ async function findOrCreateModelRow(db, modelName, modelFileId, schemaId) {
1281
+ if (modelFileId) {
1282
+ const [byFileId] = await db.select().from(models).where(eq(models.schemaFileId, modelFileId)).limit(1);
1283
+ if (byFileId) {
1284
+ if (byFileId.name === modelName)
1285
+ return byFileId;
1286
+ await db.update(models).set({ name: modelName }).where(eq(models.id, byFileId.id));
1287
+ return { ...byFileId, name: modelName };
1288
+ }
1289
+ }
1290
+ const byName = await db.select().from(models).where(eq(models.name, modelName));
1291
+ let chosen;
1292
+ if (byName.length > 0) {
1293
+ if (!modelFileId && schemaId && byName.length > 1) {
1294
+ for (const candidate of byName) {
1295
+ const links = await isLinkedTo(db, candidate.id, schemaId);
1296
+ if (links.any && !links.other) {
1297
+ chosen = candidate;
1298
+ break;
1299
+ }
1300
+ }
1301
+ }
1302
+ chosen ?? (chosen = byName.find((row) => !row.schemaFileId));
1303
+ if (!chosen && modelFileId) {
1304
+ // Every same-name row has another file id: adopt only one no other schema claims.
1305
+ for (const candidate of byName) {
1306
+ const links = await isLinkedTo(db, candidate.id, schemaId);
1307
+ if (candidate.schemaFileId && !links.any)
1308
+ continue;
1309
+ if (!links.other) {
1310
+ chosen = candidate;
1311
+ break;
1312
+ }
1313
+ }
1314
+ }
1315
+ else if (!chosen) {
1316
+ chosen = (byName.length === 1 ? byName[0] : undefined) || byName.find((row) => !!row.schemaFileId) || byName[0];
1317
+ }
1318
+ }
1319
+ if (chosen && modelFileId && chosen.schemaFileId !== modelFileId) {
1320
+ // Adopt a stub or an unclaimed same-name row by giving it this file id.
1321
+ await db.update(models).set({ schemaFileId: modelFileId, isEdited: false }).where(eq(models.id, chosen.id));
1322
+ logger$b(`Adopted existing model "${modelName}" (id: ${chosen.id}) with schemaFileId "${modelFileId}"`);
1323
+ return { ...chosen, schemaFileId: modelFileId, isEdited: false };
1324
+ }
1325
+ if (chosen)
1326
+ return chosen;
1327
+ return upsertModelRow(db, { name: modelName, schemaFileId: modelFileId });
1328
+ }
1329
+ /**
1330
+ * Finds or creates the models row a Relation property on `ownerModelId` points to: the ref model
1331
+ * in the owner's schema(s) first, since model names are only unique per schema.
1332
+ */
1333
+ async function findOrCreateRefModelRow(db, refModelName, ownerModelId) {
1334
+ try {
1335
+ const ref = await resolveRefModelRecord(refModelName, { modelId: ownerModelId }, db);
1336
+ if (ref)
1337
+ return ref;
1338
+ }
1339
+ catch (error) {
1340
+ // Not in the owner's schema and defined by several others: pick/create like an import does
1341
+ if (!(error instanceof AmbiguousModelError))
1342
+ throw error;
1343
+ }
1344
+ const [ownerLink] = await db
1345
+ .select({ schemaId: modelSchemas.schemaId })
1346
+ .from(modelSchemas)
1347
+ .where(eq(modelSchemas.modelId, ownerModelId))
1348
+ .limit(1);
1349
+ return findOrCreateModelRow(db, refModelName, undefined, ownerLink?.schemaId ?? undefined);
1350
+ }
1351
+ /**
1352
+ * Renames a model. With `schemaNameOrId` only that schema's model is renamed; without it the name
1353
+ * alone is used, which throws AmbiguousModelError when several schemas define it. When the new
1354
+ * name already exists in the scope that row is returned unchanged. Unscoped, a missing model is
1355
+ * inserted under the new name.
1356
+ */
1357
+ async function renameModelRow(db, oldName, newName, schemaNameOrId) {
1358
+ const scope = schemaNameOrId === undefined
1359
+ ? {}
1360
+ : typeof schemaNameOrId === 'number'
1361
+ ? { schemaId: schemaNameOrId, strict: true }
1362
+ : { schemaName: schemaNameOrId, strict: true };
1363
+ const model = await resolveModelRecord(oldName, scope, db);
1364
+ if (!model) {
1365
+ if (schemaNameOrId !== undefined)
1366
+ throw new Error(`Model "${oldName}" not found in schema "${schemaNameOrId}"`);
1367
+ return upsertModelRow(db, { name: newName });
1368
+ }
1369
+ const existingWithNewName = await resolveModelRecord(newName, scope, db);
1370
+ if (existingWithNewName)
1371
+ return existingWithNewName;
1372
+ await db.update(models).set({ name: newName }).where(eq(models.id, model.id));
1373
+ return { ...model, name: newName };
1374
+ }
1375
+ const propertyRowFields = [
1376
+ 'name',
1377
+ 'modelId',
1378
+ 'dataType',
1379
+ 'schemaFileId',
1380
+ 'refModelId',
1381
+ 'refValueType',
1382
+ 'required',
1383
+ 'isEdited',
1384
+ 'storageType',
1385
+ 'localStorageDir',
1386
+ 'filenameSuffix',
1387
+ 'definition',
1388
+ ];
1389
+ /**
1390
+ * Writes the properties row for a property definition. The row is the one with `schemaFileId`,
1391
+ * else the one named `previousName` or `name` in the model. A row with that schema_file_id on
1392
+ * another model is left alone. The rules for fields the input leaves out are on
1393
+ * {@link PropertyRowInput}; an unchanged row is not written.
1394
+ */
1395
+ async function upsertPropertyRow(db, input, options = {}) {
1396
+ let existing;
1397
+ if (input.schemaFileId) {
1398
+ [existing] = await db.select().from(properties).where(eq(properties.schemaFileId, input.schemaFileId)).limit(1);
1399
+ if (existing && existing.modelId !== input.modelId) {
1400
+ logger$b(`Property ${input.name} (${input.schemaFileId}) belongs to model row ${existing.modelId}, not ${input.modelId}; left as is`);
1401
+ return { row: existing, changed: false };
1402
+ }
1403
+ }
1404
+ for (const name of [input.previousName, input.name]) {
1405
+ if (existing || !name)
1406
+ continue;
1407
+ [existing] = await db
1408
+ .select()
1409
+ .from(properties)
1410
+ .where(and(eq(properties.name, name), eq(properties.modelId, input.modelId)))
1411
+ .limit(1);
1412
+ }
1413
+ if (existing && options.keepEditedRow && existing.isEdited === true) {
1414
+ logger$b(`Keeping locally edited property row ${existing.name} (id ${existing.id})`);
1415
+ return { row: existing, changed: false };
1416
+ }
1417
+ let refModelId = input.refModelId ?? null;
1418
+ if (input.refModelName && input.refModelId == null) {
1419
+ refModelId = (await findOrCreateRefModelRow(db, input.refModelName, input.modelId)).id ?? null;
1420
+ }
1421
+ const values = {
1422
+ name: input.name,
1423
+ modelId: input.modelId,
1424
+ dataType: input.dataType ? normalizeDataType(input.dataType) : (existing?.dataType ?? ''),
1425
+ schemaFileId: (options.keepStoredSchemaFileId && existing?.schemaFileId) || input.schemaFileId || existing?.schemaFileId || null,
1426
+ refModelId,
1427
+ refValueType: input.refValueType ? normalizeDataType(input.refValueType) : null,
1428
+ required: input.required ?? existing?.required ?? false,
1429
+ isEdited: input.isEdited ?? existing?.isEdited ?? false,
1430
+ storageType: input.storage ? (input.storage.storageType ?? null) : (existing?.storageType ?? null),
1431
+ localStorageDir: input.storage ? (input.storage.localStorageDir ?? null) : (existing?.localStorageDir ?? null),
1432
+ filenameSuffix: input.storage ? (input.storage.filenameSuffix ?? null) : (existing?.filenameSuffix ?? null),
1433
+ definition: input.definition
1434
+ ? propertyDefinitionColumn(input.definition, input.replaceDefinition ? null : existing?.definition)
1435
+ : (existing?.definition ?? null),
1436
+ };
1437
+ if (existing) {
1438
+ const unchanged = propertyRowFields.every((field) => (existing[field] ?? null) === (values[field] ?? null));
1439
+ if (unchanged)
1440
+ return { row: existing, changed: false };
1441
+ await db.update(properties).set(values).where(eq(properties.id, existing.id));
1442
+ return { row: { ...existing, ...values }, changed: true };
1443
+ }
1444
+ const { name, modelId, ...rest } = values;
1445
+ const [row] = await db
1446
+ .insert(properties)
1447
+ .values(values)
1448
+ .onConflictDoUpdate(values.schemaFileId
1449
+ ? { target: properties.schemaFileId, set: { name, ...rest } }
1450
+ : { target: [properties.name, properties.modelId], set: rest })
1451
+ .returning();
1452
+ return { row, changed: true };
1453
+ }
1454
+
1455
+ var definitions = /*#__PURE__*/Object.freeze({
1456
+ __proto__: null,
1457
+ findOrCreateModelRow: findOrCreateModelRow,
1458
+ findOrCreateRefModelRow: findOrCreateRefModelRow,
1459
+ inDefinitionsTransaction: inDefinitionsTransaction,
1460
+ insertModelStubs: insertModelStubs,
1461
+ linkModelToSchemaRow: linkModelToSchemaRow,
1462
+ renameModelRow: renameModelRow,
1463
+ upsertModelRow: upsertModelRow,
1464
+ upsertPropertyRow: upsertPropertyRow,
1465
+ upsertSchemaRow: upsertSchemaRow
1466
+ });
1467
+
1468
+ const logger$a = debug('seedSdk:helpers:db');
1469
+ /**
1470
+ * Posts one message and closes the channel. A message already posted is still delivered after its
1471
+ * sender closes, and an open BroadcastChannel keeps a Node process alive (each was a MessagePort
1472
+ * handle that stopped scripts exiting).
1473
+ */
1474
+ const postBroadcast = (channelName, message) => {
1475
+ const channel = new BroadcastChannel(channelName);
1476
+ try {
1477
+ channel.postMessage(message);
1478
+ }
1479
+ finally {
1480
+ channel.close();
1481
+ }
1482
+ };
1483
+ /**
1484
+ * The `properties` storage columns for a property definition, in either the internal shape
1485
+ * (`storageType`, `localStorageDir`, `filenameSuffix`) or the schema-file shape
1486
+ * (`storage: { type, path, extension }`). All null when the definition has no storage config.
1487
+ */
1488
+ function propertyStorageColumns(def) {
1489
+ const normalized = normalizePropertyRecordSchema((def ?? {}));
1490
+ return {
1491
+ storageType: normalized.storageType ?? null,
1492
+ localStorageDir: normalized.localStorageDir ?? null,
1493
+ filenameSuffix: normalized.filenameSuffix ?? null,
1494
+ };
1495
+ }
1496
+ /** True when the definition carries any storage setting (so writing its columns can't wipe them by omission). */
1497
+ const hasStorageSettings = (columns) => columns.storageType != null || columns.localStorageDir != null || columns.filenameSuffix != null;
1498
+ /**
1499
+ * A model's ItemStorage properties as stored in `properties` (storage_type 'ItemStorage'), with
1500
+ * their storage path and extension. Reads the table, not ModelProperty instances, so it doesn't
1501
+ * depend on whether (or from where) the model's property instances have loaded.
1502
+ */
1503
+ async function getItemStoragePropertiesForModel(modelNameOrType, scope = {}) {
1504
+ const db = BaseDb.getAppDb();
1505
+ if (!db || !modelNameOrType)
1506
+ return [];
1507
+ const modelRecord = await resolveModelRecordByNameOrType(modelNameOrType, scope, db);
1508
+ if (!modelRecord)
1509
+ return [];
1510
+ return db
1511
+ .select({
1512
+ id: properties.id,
1513
+ name: properties.name,
1514
+ localStorageDir: properties.localStorageDir,
1515
+ filenameSuffix: properties.filenameSuffix,
1516
+ })
1517
+ .from(properties)
1518
+ .where(and(eq(properties.modelId, modelRecord.id), eq(properties.storageType, 'ItemStorage')));
1519
+ }
1520
+ /**
1521
+ * Moves a renamed property's pending outbox edits (ADR 0019) to its new name, so they apply as the
1522
+ * property's. Its metadata rows need no move: readers match them by property_id (decision D4).
1523
+ * Returns the number of ops renamed.
1524
+ */
1525
+ async function renamePendingWritesForProperty(modelName, oldPropertyName, newPropertyName) {
1526
+ const db = BaseDb.getAppDb();
1527
+ if (!db)
1528
+ return 0;
1529
+ const renamed = await db
1530
+ .update(pendingWrites)
1531
+ .set({ propertyName: newPropertyName })
1532
+ .where(and(eq(pendingWrites.propertyName, oldPropertyName), eq(pendingWrites.status, 'pending'), inArray(pendingWrites.seedLocalId, db.select({ localId: seeds.localId }).from(seeds).where(eq(seeds.type, easName(modelName))))))
1533
+ .returning({ opId: pendingWrites.opId });
1534
+ return renamed.length;
1535
+ }
1536
+ /** Tells React's useSchemas a schemas row was inserted (its live query may not re-run). */
1537
+ const broadcastSchemasInvalidate = () => {
1538
+ if (typeof BroadcastChannel === 'undefined')
1539
+ return;
1540
+ try {
1541
+ postBroadcast('seed-schemas-invalidate', {});
1542
+ }
1543
+ catch (_) { }
1544
+ };
1545
+ /** Tells React's useModels a model was linked to a schema (its live query may not re-run). */
1546
+ const broadcastModelsInvalidate = (schema) => {
1547
+ if (typeof BroadcastChannel === 'undefined')
1548
+ return;
1549
+ try {
1550
+ postBroadcast('seed-models-invalidate', {
1551
+ schemaName: schema.name ?? undefined,
1552
+ schemaFileId: schema.schemaFileId ?? undefined,
1553
+ });
1554
+ }
1555
+ catch (_) { }
1556
+ };
1557
+ /**
1558
+ * Writes a schema row ({@link upsertSchemaRow} has the matching rules) and returns it.
1559
+ * @param schemaFileId - Optional schemaFileId from JSON file for change tracking
1560
+ * @param schemaData - Optional full schema content as JSON string (SchemaFileFormat)
1561
+ * @param isDraft - Optional flag indicating if schema is in draft state (default: false)
1562
+ * @param isEdited - Optional flag indicating if schema has been edited locally (default: false)
1563
+ * @param options.db - Write on this transaction; the caller then tells React after it commits.
1564
+ * Without it the write runs in its own transaction.
1565
+ * @param options.contentHash - The applied file's hash (applySchemaFile); written with schemaData
1566
+ */
1567
+ const addSchemaToDb = async (schema, schemaFileId, schemaData, isDraft, isEdited, options = {}) => {
1568
+ if (!options.db && !BaseDb.getAppDb()) {
1569
+ throw new Error('Database not found');
1570
+ }
1571
+ const input = {
1572
+ name: schema.name,
1573
+ version: schema.version,
1574
+ createdAt: schema.createdAt,
1575
+ updatedAt: schema.updatedAt,
1576
+ schemaFileId,
1577
+ schemaData,
1578
+ isDraft,
1579
+ isEdited,
1580
+ contentHash: options.contentHash,
1581
+ };
1582
+ if (options.db) {
1583
+ const { inserted: _inserted, ...row } = await upsertSchemaRow(options.db, input);
1584
+ return row;
1585
+ }
1586
+ const { inserted, ...row } = await inDefinitionsTransaction((tx) => upsertSchemaRow(tx, input));
1587
+ if (inserted)
1588
+ broadcastSchemasInvalidate();
1589
+ return row;
1590
+ };
1591
+ /**
1592
+ * Renames a model ({@link renameModelRow}), in its own transaction unless `db` is given.
1593
+ * @param schemaNameOrId - Schema name or id to scope the rename (required when multiple schemas have models with the same name)
164
1594
  */
165
- function metadataStoredNameForms(key, def) {
166
- const forms = [metadataStorageName(key, def), key];
167
- if (!ID_SUFFIX_EXCLUDE.has(key) && needsMetadataIdSuffix(def?.dataType, def?.refValueType))
168
- forms.push(`${key}Id`);
169
- return [...new Set(forms)];
170
- }
1595
+ const renameModelInDb = async (oldName, newName, schemaNameOrId, db) => {
1596
+ if (db)
1597
+ return renameModelRow(db, oldName, newName, schemaNameOrId);
1598
+ if (!BaseDb.getAppDb())
1599
+ throw new Error('Database not found');
1600
+ return inDefinitionsTransaction((tx) => renameModelRow(tx, oldName, newName, schemaNameOrId));
1601
+ };
171
1602
  /**
172
- * The schema key a stored metadata name belongs to among `defs` (keyed by schema key): an exact key
173
- * first, then any of a key's stored forms. Undefined when no property matches. For rows without
174
- * `property_id`; rows with one take the key from their properties row.
1603
+ * Writes a schema file's models, their property rows and their model_schemas links.
1604
+ * @param models - Models to write besides those in schemaFileData.propertyDefinitions
1605
+ * @param schema - Optional schema to link models to (a row, or written first when it has no id)
1606
+ * @param modelRenames - Optional map of old model names to new model names
1607
+ * @param schemaFileData - The file's ids and property definitions: { schemaFileId, modelFileIds: Map<modelName, id>, propertyFileIds: Map<modelName, Map<propertyName, id>>, propertyDefinitions }
1608
+ * @param options.db - Write on this transaction; the caller refreshes cached ModelProperty
1609
+ * instances with the returned ids once it has committed. Without it the write runs in its own
1610
+ * transaction and refreshes them itself.
1611
+ * @returns The schemaFileIds of the property rows this call changed
175
1612
  */
176
- function propertyKeyFromStoredName(defs, storedName) {
177
- if (!storedName)
178
- return undefined;
179
- if (Object.prototype.hasOwnProperty.call(defs, storedName))
180
- return storedName;
181
- for (const [key, def] of Object.entries(defs)) {
182
- if (metadataStoredNameForms(key, def).includes(storedName))
183
- return key;
1613
+ const addModelsToDb = async (models, schema, modelRenames, schemaFileData, options = {}) => {
1614
+ if (!options.db) {
1615
+ if (!BaseDb.getAppDb())
1616
+ throw new Error('Database not found');
1617
+ const changed = await inDefinitionsTransaction((tx) => addModelsToDb(models, schema, modelRenames, schemaFileData, { db: tx }));
1618
+ // A cached instance is otherwise never re-read from its row (createById returns it as is)
1619
+ await refreshCachedProperties(changed);
1620
+ return changed;
1621
+ }
1622
+ const db = options.db;
1623
+ // Model rows and their schema links decide which schema file's nameMatch applies (ADR 0009).
1624
+ clearNameMatchLevels();
1625
+ // The schema may already be a row (with id)
1626
+ let schemaRecord;
1627
+ if (schema) {
1628
+ schemaRecord =
1629
+ schema.id !== null && schema.id !== undefined
1630
+ ? schema
1631
+ : await addSchemaToDb(schema, schemaFileData?.schemaFileId, undefined, undefined, undefined, { db });
1632
+ }
1633
+ if (modelRenames && schemaRecord) {
1634
+ const schemaNameOrId = schemaRecord.name ?? schemaRecord.id;
1635
+ for (const [oldName, newName] of modelRenames.entries()) {
1636
+ await renameModelRow(db, oldName, newName, schemaNameOrId);
1637
+ }
1638
+ }
1639
+ const modelRecords = new Map();
1640
+ const modelProcessingErrors = [];
1641
+ // Property rows this call changed, so cached ModelProperty instances can be given the new values
1642
+ const updatedPropertyFileIds = [];
1643
+ const _mod_0 = await import('./arweaveContentTags-iW_HcWJA.js');
1644
+ const _ns_0 = _mod_0.ap;
1645
+ const { generateId } = _ns_0;
1646
+ const modelNames = [...new Set([...Object.keys(models), ...(schemaFileData?.propertyDefinitions?.keys() ?? [])])];
1647
+ for (const modelName of modelNames) {
1648
+ try {
1649
+ const modelFileId = schemaFileData?.modelFileIds?.get(modelName);
1650
+ const modelRecord = modelRecords.get(modelName) ?? (await findOrCreateModelRow(db, modelName, modelFileId, schemaRecord?.id));
1651
+ modelRecords.set(modelName, modelRecord);
1652
+ const definitions = schemaFileData?.propertyDefinitions?.get(modelName) ?? {};
1653
+ for (const [propertyName, propertyValues] of Object.entries(definitions)) {
1654
+ if (!propertyValues) {
1655
+ throw new Error(`Property values not found for ${propertyName}`);
1656
+ }
1657
+ // IDs are assigned before this is called; a generated one doesn't replace a stored id.
1658
+ const propertyFileId = schemaFileData?.propertyFileIds?.get(modelName)?.get(propertyName);
1659
+ let refModelId = null;
1660
+ const refModelName = propertyValues.ref || propertyValues.refModelName;
1661
+ if (refModelName) {
1662
+ let refModel = modelRecords.get(refModelName);
1663
+ if (!refModel) {
1664
+ const refModelFileId = schemaFileData?.modelFileIds?.get(refModelName);
1665
+ refModel = await findOrCreateModelRow(db, refModelName, refModelFileId, schemaRecord?.id);
1666
+ modelRecords.set(refModelName, refModel);
1667
+ }
1668
+ refModelId = refModel.id ?? null;
1669
+ }
1670
+ // ADR 0008: a rename needs the property's id. A name that matches no stored property by id
1671
+ // or name is a new property. The schema file is the source of truth for every column, and
1672
+ // a property edited locally keeps its row.
1673
+ const { row, changed } = await upsertPropertyRow(db, {
1674
+ modelId: modelRecord.id,
1675
+ name: propertyName,
1676
+ dataType: propertyValues.dataType,
1677
+ schemaFileId: propertyFileId ?? generateId(),
1678
+ refModelId,
1679
+ refValueType: propertyValues.refValueType ?? null,
1680
+ required: propertyValues.required ?? false,
1681
+ storage: propertyStorageColumns(propertyValues),
1682
+ isEdited: false,
1683
+ definition: schemaFileData?.propertyFileDefinitions?.get(modelName)?.[propertyName] ?? propertyValues,
1684
+ replaceDefinition: true,
1685
+ }, { keepStoredSchemaFileId: !propertyFileId, keepEditedRow: true });
1686
+ if (changed && row?.schemaFileId)
1687
+ updatedPropertyFileIds.push(row.schemaFileId);
1688
+ }
1689
+ }
1690
+ catch (error) {
1691
+ const errorMessage = error?.message || String(error);
1692
+ logger$a(`Error processing model "${modelName}": ${errorMessage}`);
1693
+ modelProcessingErrors.push({ modelName, error: errorMessage });
1694
+ // Continue processing other models even if one fails
1695
+ }
184
1696
  }
185
- return undefined;
186
- }
1697
+ if (modelProcessingErrors.length > 0) {
1698
+ console.warn(`[addModelsToDb] Errors occurred while processing ${modelProcessingErrors.length} model(s):`, modelProcessingErrors.map(e => `${e.modelName}: ${e.error}`).join('; '));
1699
+ }
1700
+ if (schemaRecord?.id) {
1701
+ for (const [modelName, record] of modelRecords) {
1702
+ if (!record.id)
1703
+ continue;
1704
+ if (await linkModelToSchemaRow(db, record.id, schemaRecord.id)) {
1705
+ logger$a(`Linked model ${modelName} (id: ${record.id}) to schema (id: ${schemaRecord.id})`);
1706
+ }
1707
+ }
1708
+ }
1709
+ return updatedPropertyFileIds;
1710
+ };
187
1711
  /**
188
- * The names a property's rows may be stored under, for matching rows that have no property_id and
189
- * outbox ops (which name a property, not its id): the name with and without its `Id` for an Image,
190
- * File, Html or Relation, or when `dataType` is unknown.
1712
+ * The models linked to a schema row (model_schemas) with their property rows, in schema-file
1713
+ * shape: `{ [modelName]: { id, properties: { [name]: { dataType, schemaFileId, ref, ... } } } }`.
1714
+ * Properties carry their row's definition (validation, description, ...); model-level fields only a
1715
+ * schema file holds aren't here, see {@link mergeDbModelsIntoSchemaModels}.
1716
+ * @param schemaId - The ID of the schema record in the database
191
1717
  */
192
- function getMetadataPropertyNamesForQuery(propertyName, dataType, refValueType) {
193
- if (ID_SUFFIX_EXCLUDE.has(propertyName))
194
- return [propertyName];
195
- if (propertyName.endsWith('Ids'))
196
- return [propertyName];
197
- if (propertyName.endsWith('Id')) {
198
- const base = propertyName.slice(0, -2);
199
- return [propertyName, base];
1718
+ const loadModelsFromDbForSchema = async (schemaId, db = BaseDb.getAppDb()) => {
1719
+ if (!db) {
1720
+ logger$a('Database not found, cannot load models from DB');
1721
+ return {};
200
1722
  }
201
- const needsId = needsMetadataIdSuffix(dataType, refValueType) ||
202
- (dataType === undefined && refValueType === undefined);
203
- if (needsId) {
204
- return [propertyName, `${propertyName}Id`];
1723
+ try {
1724
+ const linked = await db
1725
+ .select({ modelId: modelSchemas.modelId, modelName: models.name, schemaFileId: models.schemaFileId })
1726
+ .from(modelSchemas)
1727
+ .innerJoin(models, eq(modelSchemas.modelId, models.id))
1728
+ .where(eq(modelSchemas.schemaId, schemaId));
1729
+ const modelIds = linked.map((row) => row.modelId).filter((id) => id != null);
1730
+ if (modelIds.length === 0)
1731
+ return {};
1732
+ const refModels = alias(models, 'ref_models');
1733
+ const propertyRows = await db
1734
+ .select({ property: properties, refName: refModels.name })
1735
+ .from(properties)
1736
+ .leftJoin(refModels, eq(properties.refModelId, refModels.id))
1737
+ .where(inArray(properties.modelId, modelIds));
1738
+ const models$1 = {};
1739
+ const nameById = new Map();
1740
+ for (const { modelId, modelName, schemaFileId } of linked) {
1741
+ if (!modelId || !modelName)
1742
+ continue;
1743
+ nameById.set(modelId, modelName);
1744
+ // The id (schemaFileId) lets Model.create find the existing row instead of adding a "Post 1".
1745
+ models$1[modelName] = { properties: {}, ...(schemaFileId ? { id: schemaFileId } : {}) };
1746
+ }
1747
+ for (const { property: prop, refName } of propertyRows) {
1748
+ const modelName = nameById.get(prop.modelId);
1749
+ if (!modelName)
1750
+ continue;
1751
+ // The row's definition without its row keys; `required` stays the file's
1752
+ const { id: _id, _dbId, name: _name, modelId: _modelId, refModelId: _refModelId, refModelName: _refModelName, required: _required, ...definition } = propertyDefinitionFromRow(prop, refName);
1753
+ models$1[modelName].properties[prop.name] = {
1754
+ ...definition,
1755
+ ...(prop.schemaFileId ? { schemaFileId: prop.schemaFileId } : {}),
1756
+ };
1757
+ }
1758
+ logger$a(`Loaded ${Object.keys(models$1).length} models from database for schema ${schemaId}`);
1759
+ return models$1;
205
1760
  }
206
- return [propertyName];
207
- }
1761
+ catch (error) {
1762
+ logger$a(`Error loading models from database for schema ${schemaId}:`, error);
1763
+ return {};
1764
+ }
1765
+ };
208
1766
  /**
209
- * The name an item exposes a property under, from the name it is stored under: its alias when it
210
- * has one (List of relation), else `tagIds` → `tags`, `authorId` → `author`. Names in
211
- * PROPERTY_NAMES_EXEMPT_FROM_ID_SUFFIX_STRIP keep their `Id`.
1767
+ * Merges the models {@link loadModelsFromDbForSchema} read from the database into a schema's
1768
+ * models. The database decides which models and properties exist and their stored fields; the
1769
+ * schema keeps what only it holds (nameMatch, description, validation, ...), found by id first so
1770
+ * a property renamed in the database keeps them. A schema property whose id a database property
1771
+ * has is dropped under its old name.
212
1772
  */
213
- function toItemPropertyKey(storedName, alias) {
214
- if (alias)
215
- return alias;
216
- if (storedName.endsWith('Ids'))
217
- return pluralize(storedName.slice(0, -3));
218
- if (storedName.endsWith('Id') && !PROPERTY_NAMES_EXEMPT_FROM_ID_SUFFIX_STRIP.has(storedName)) {
219
- return storedName.slice(0, -2);
1773
+ const mergeDbModelsIntoSchemaModels = (schemaModels, dbModels) => {
1774
+ const merged = { ...(schemaModels ?? {}) };
1775
+ for (const [modelName, dbModel] of Object.entries(dbModels)) {
1776
+ const schemaModel = schemaModels?.[modelName];
1777
+ if (!schemaModel) {
1778
+ merged[modelName] = dbModel;
1779
+ continue;
1780
+ }
1781
+ const schemaProps = schemaModel.properties ?? {};
1782
+ const dbProps = dbModel.properties ?? {};
1783
+ const schemaNameById = new Map();
1784
+ for (const [name, prop] of Object.entries(schemaProps)) {
1785
+ const id = prop?.id ?? prop?.schemaFileId;
1786
+ if (id)
1787
+ schemaNameById.set(id, name);
1788
+ }
1789
+ const properties = {};
1790
+ const matched = new Set();
1791
+ for (const [name, dbProp] of Object.entries(dbProps)) {
1792
+ const byId = dbProp?.schemaFileId ? schemaNameById.get(dbProp.schemaFileId) : undefined;
1793
+ const byName = schemaProps[name];
1794
+ const byNameId = byName?.id ?? byName?.schemaFileId;
1795
+ // By name only when the ids don't say it's another property.
1796
+ const schemaName = byId ?? (byName && !(byNameId && dbProp?.schemaFileId && byNameId !== dbProp.schemaFileId) ? name : undefined);
1797
+ const schemaProp = schemaName ? schemaProps[schemaName] : undefined;
1798
+ if (schemaName && schemaProp)
1799
+ matched.add(schemaName);
1800
+ // The database's dataType and ref replace the file's type and model.
1801
+ const { type, model, ...fromSchema } = schemaProp ?? {};
1802
+ properties[name] = {
1803
+ ...fromSchema,
1804
+ ...(dbProp?.dataType === undefined && type !== undefined ? { type } : {}),
1805
+ ...(dbProp?.ref === undefined && model !== undefined ? { model } : {}),
1806
+ ...dbProp,
1807
+ };
1808
+ }
1809
+ const unmatched = Object.fromEntries(Object.entries(schemaProps).filter(([name]) => !matched.has(name)));
1810
+ merged[modelName] = { ...schemaModel, ...dbModel, properties: { ...unmatched, ...properties } };
220
1811
  }
221
- return storedName;
222
- }
1812
+ return merged;
1813
+ };
223
1814
  /**
224
- * Schema property name from metadata name (strip Id/Ids suffix).
1815
+ * Saves a ModelProperty's edit to its properties row (is_edited true) without updating the schema
1816
+ * file, in one transaction. A context without `required` or storage settings leaves the row's.
1817
+ * @param property - The ModelPropertyMachineContext with updated values
225
1818
  */
226
- function toSchemaPropertyName(propertyName) {
227
- if (propertyName.endsWith('Ids'))
228
- return propertyName.slice(0, -3);
229
- if (propertyName.endsWith('Id'))
230
- return propertyName.slice(0, -2);
231
- return undefined;
1819
+ const savePropertyToDb = async (property) => {
1820
+ if (!BaseDb.getAppDb()) {
1821
+ throw new Error('Database not found');
1822
+ }
1823
+ const name = property.name;
1824
+ if (!name) {
1825
+ throw new Error('Property name is required');
1826
+ }
1827
+ const schemaFileId = property._propertyFileId || (typeof property.id === 'string' ? property.id : undefined);
1828
+ const storage = propertyStorageColumns(property);
1829
+ await inDefinitionsTransaction(async (tx) => {
1830
+ // modelId, then the model of the property row (by its schemaFileId), then modelName in the
1831
+ // property's schema. context.modelName can also hold the model's schemaFileId.
1832
+ const modelRecord = await resolveModelRecord(property.modelName, {
1833
+ modelId: typeof property.modelId === 'number' ? property.modelId : undefined,
1834
+ propertyFileId: schemaFileId,
1835
+ modelFileId: property.modelName,
1836
+ schemaName: property._schemaName,
1837
+ }, tx);
1838
+ if (!modelRecord) {
1839
+ throw new Error(`Model not found in database (modelId=${property.modelId}, modelName=${property.modelName})`);
1840
+ }
1841
+ await upsertPropertyRow(tx, {
1842
+ modelId: modelRecord.id,
1843
+ name,
1844
+ dataType: property.dataType,
1845
+ schemaFileId,
1846
+ previousName: property._originalValues?.name,
1847
+ refModelName: property.refModelName,
1848
+ refModelId: property.refModelName ? undefined : (property.refModelId ?? null),
1849
+ refValueType: property.refValueType ?? null,
1850
+ required: property.required,
1851
+ storage: hasStorageSettings(storage) ? storage : undefined,
1852
+ isEdited: true,
1853
+ definition: property,
1854
+ });
1855
+ });
1856
+ logger$a(`Saved property ${property.modelName}:${property.name}`);
1857
+ };
1858
+ function parseAddressConfig(value) {
1859
+ if (!value)
1860
+ return null;
1861
+ try {
1862
+ const parsed = JSON.parse(value);
1863
+ if (Array.isArray(parsed)) {
1864
+ return normalizeAddressConfig(parsed);
1865
+ }
1866
+ if (parsed && typeof parsed === 'object' && Array.isArray(parsed.owned)) {
1867
+ return normalizeAddressConfig({
1868
+ owned: parsed.owned,
1869
+ watched: Array.isArray(parsed.watched) ? parsed.watched : [],
1870
+ });
1871
+ }
1872
+ return null;
1873
+ }
1874
+ catch {
1875
+ return null;
1876
+ }
232
1877
  }
1878
+ const getOwnedAddressesFromDb = async () => {
1879
+ const config = await getAddressConfigFromDb();
1880
+ return config?.owned ?? [];
1881
+ };
1882
+ const getWatchedAddressesFromDb = async () => {
1883
+ const config = await getAddressConfigFromDb();
1884
+ return config?.watched ?? [];
1885
+ };
233
1886
  /**
234
- * Alternate property name for instance lookup (base <-> Id variant).
1887
+ * Returns persisted owned or watched addresses for list filters.
1888
+ * Does not include `getAdditionalSyncAddresses` (those are extra EAS indexers).
235
1889
  */
236
- function getAlternatePropertyNameForInstanceLookup(propertyName) {
237
- if (ID_SUFFIX_EXCLUDE.has(propertyName))
238
- return undefined;
239
- if (propertyName.endsWith('Id'))
240
- return propertyName.slice(0, -2);
241
- return `${propertyName}Id`;
242
- }
1890
+ const getAddressesForItemsFilter = async (addressFilter) => {
1891
+ if (addressFilter === 'owned') {
1892
+ return getOwnedAddressesFromDb();
1893
+ }
1894
+ return getWatchedAddressesFromDb();
1895
+ };
243
1896
  /**
244
- * The EAS property name publish writes for an item property (ADR 0008 §3), already in EAS form:
245
- *
246
- * - List of relations: its `<name><Model>Ids` storage name (`author_identity_ids`).
247
- * - Single Relation, Image, File or Html: its schema key (`author`), never the local `<name>Id`
248
- * storage name. The key is `propertyDef.name` when known, else `propertyName` without its `Id`.
249
- * - Anything else: the property name.
1897
+ * Returns owned + watched addresses. Use for EAS sync and file download.
250
1898
  */
251
- function easPropertyNameForPublish(propertyName, propertyDef) {
252
- const listRelationName = listRelationEasPropertyName(propertyName, propertyDef ?? undefined);
253
- if (listRelationName)
254
- return easName(listRelationName);
255
- if (propertyDef &&
256
- normalizeDataType(propertyDef.dataType) !== 'List' &&
257
- needsMetadataIdSuffix(propertyDef.dataType, propertyDef.refValueType) &&
258
- !ID_SUFFIX_EXCLUDE.has(propertyName)) {
259
- const key = propertyDef.name ?? (propertyName.endsWith('Id') ? propertyName.slice(0, -2) : propertyName);
260
- return easName(key);
1899
+ const getAllAddressesFromDb = async () => {
1900
+ const config = await getAddressConfigFromDb();
1901
+ if (!config)
1902
+ return [];
1903
+ return [...config.owned, ...config.watched];
1904
+ };
1905
+ async function getAddressConfigFromDb() {
1906
+ // Before init opens the database (or with no init) there are no stored addresses to read
1907
+ const appDb = await whenDbReady();
1908
+ if (!appDb)
1909
+ return null;
1910
+ const appStatesRecords = await appDb
1911
+ .select()
1912
+ .from(appState)
1913
+ .where(eq(appState.key, 'addresses'))
1914
+ .limit(1);
1915
+ if (!appStatesRecords || appStatesRecords.length === 0) {
1916
+ return null;
261
1917
  }
262
- return easName(propertyName);
1918
+ const addressArrayString = appStatesRecords[0].value;
1919
+ return parseAddressConfig(addressArrayString);
263
1920
  }
264
1921
  /**
265
- * The EAS schema definition publish attests a property under (`string title`, `bytes32 author`),
266
- * or undefined when its data type has no EAS type. Look schemas up by this, not by name: a name
267
- * can have schemas of several types (Optimism Sepolia has `bytes32 storage_transaction_id` beside
268
- * `string storage_transaction_id`).
1922
+ * Writes a runtime-created model: its models row (is_edited true when new), its model_schemas link
1923
+ * and the properties given, in one transaction. Returns the model's row id.
1924
+ * @param modelFileId - The model file ID (schema_file_id)
1925
+ * @param data - Model data including modelName, schemaId, and optional properties
269
1926
  */
270
- function easSchemaDefinitionForProperty(propertyName, propertyDef) {
271
- if (!propertyDef?.dataType)
272
- return undefined;
273
- const dataType = normalizeDataType(String(propertyDef.dataType));
274
- const easType = INTERNAL_DATA_TYPES[dataType]?.eas;
275
- if (!easType)
276
- return undefined;
277
- return `${easType} ${easPropertyNameForPublish(propertyName, propertyDef)}`;
1927
+ async function writeModelToDb(modelFileId, data) {
1928
+ if (!BaseDb.getAppDb())
1929
+ throw new Error('Database not available');
1930
+ if (!data.schemaId || !Number.isInteger(data.schemaId) || data.schemaId <= 0) {
1931
+ throw new Error(`Invalid schemaId: ${data.schemaId}. Schema record must exist before creating join entry.`);
1932
+ }
1933
+ const _mod_1 = await import('./arweaveContentTags-iW_HcWJA.js');
1934
+ const _ns_1 = _mod_1.ap;
1935
+ const { generateId } = _ns_1;
1936
+ const { linked, schema, modelId } = await inDefinitionsTransaction(async (tx) => {
1937
+ const [schema] = await tx
1938
+ .select({ id: schemas.id, name: schemas.name, schemaFileId: schemas.schemaFileId })
1939
+ .from(schemas)
1940
+ .where(eq(schemas.id, data.schemaId))
1941
+ .limit(1);
1942
+ if (!schema) {
1943
+ throw new Error(`Schema with id ${data.schemaId} does not exist in database. Cannot create join record.`);
1944
+ }
1945
+ const model = await upsertModelRow(tx, { name: data.modelName, schemaFileId: modelFileId, isEdited: true });
1946
+ const linked = await linkModelToSchemaRow(tx, model.id, data.schemaId);
1947
+ for (const [propName, propData] of Object.entries(data.properties ?? {})) {
1948
+ const refModelName = propData.refModelName || propData.ref;
1949
+ const storage = propertyStorageColumns(propData);
1950
+ // A stored row keeps its id; the given or a generated one is for a new row.
1951
+ await upsertPropertyRow(tx, {
1952
+ modelId: model.id,
1953
+ name: propName,
1954
+ dataType: propData.dataType,
1955
+ schemaFileId: propData.id ?? generateId(),
1956
+ refModelName,
1957
+ refModelId: refModelName ? undefined : (propData.refModelId ?? null),
1958
+ refValueType: propData.refValueType ?? null,
1959
+ required: propData.required,
1960
+ storage: hasStorageSettings(storage) ? storage : undefined,
1961
+ definition: propData,
1962
+ }, { keepStoredSchemaFileId: true });
1963
+ }
1964
+ return { linked, schema, modelId: model.id };
1965
+ });
1966
+ if (linked)
1967
+ broadcastModelsInvalidate(schema);
1968
+ logger$a(`Wrote model ${data.modelName} (${modelFileId}) to database`);
1969
+ return modelId;
278
1970
  }
1971
+ /**
1972
+ * Writes a property's row by its file id ({@link upsertPropertyRow}), in its own transaction.
1973
+ * `required` and storage settings the data leaves out keep the row's.
1974
+ * @param propertyFileId - The property file ID (schema_file_id)
1975
+ * @param data - Property data including modelId, name, dataType, and other property fields
1976
+ * @param isEdited - Optional flag indicating if property has been edited locally. Left unset, an update
1977
+ * keeps the row's flag (a load or creation write must not clear an unsaved edit) and an insert stores false.
1978
+ */
1979
+ async function writePropertyToDb(propertyFileId, data, isEdited) {
1980
+ if (!BaseDb.getAppDb())
1981
+ throw new Error('Database not available');
1982
+ const refModelName = data.refModelName || data.ref;
1983
+ const storage = propertyStorageColumns(data);
1984
+ await inDefinitionsTransaction((tx) => upsertPropertyRow(tx, {
1985
+ modelId: data.modelId,
1986
+ name: data.name,
1987
+ dataType: data.dataType,
1988
+ schemaFileId: propertyFileId,
1989
+ refModelName,
1990
+ refModelId: refModelName ? undefined : (data.refModelId ?? null),
1991
+ refValueType: data.refValueType ?? null,
1992
+ required: data.required,
1993
+ storage: hasStorageSettings(storage) ? storage : undefined,
1994
+ isEdited,
1995
+ definition: data,
1996
+ }));
1997
+ logger$a(`Wrote property ${data.name} (${propertyFileId})`);
1998
+ }
1999
+
2000
+ var db = /*#__PURE__*/Object.freeze({
2001
+ __proto__: null,
2002
+ addModelsToDb: addModelsToDb,
2003
+ addSchemaToDb: addSchemaToDb,
2004
+ broadcastSchemasInvalidate: broadcastSchemasInvalidate,
2005
+ getAddressesForItemsFilter: getAddressesForItemsFilter,
2006
+ getAllAddressesFromDb: getAllAddressesFromDb,
2007
+ getItemStoragePropertiesForModel: getItemStoragePropertiesForModel,
2008
+ getOwnedAddressesFromDb: getOwnedAddressesFromDb,
2009
+ getWatchedAddressesFromDb: getWatchedAddressesFromDb,
2010
+ loadModelsFromDbForSchema: loadModelsFromDbForSchema,
2011
+ mergeDbModelsIntoSchemaModels: mergeDbModelsIntoSchemaModels,
2012
+ propertyStorageColumns: propertyStorageColumns,
2013
+ renameModelInDb: renameModelInDb,
2014
+ renamePendingWritesForProperty: renamePendingWritesForProperty,
2015
+ savePropertyToDb: savePropertyToDb,
2016
+ writeModelToDb: writeModelToDb,
2017
+ writePropertyToDb: writePropertyToDb
2018
+ });
279
2019
 
280
2020
  /**
281
2021
  * Proxy [[GetOwnProperty]] that stays compatible with the target.
@@ -896,7 +2636,6 @@ var metadataProperty = /*#__PURE__*/Object.freeze({
896
2636
  METADATA_PROPERTY_GROUP_KEY_SQL: METADATA_PROPERTY_GROUP_KEY_SQL,
897
2637
  findMetadataPropertyByName: findMetadataPropertyByName,
898
2638
  matchMetadataProperty: matchMetadataProperty,
899
- metadataPropertyGroupKeys: metadataPropertyGroupKeys,
900
2639
  metadataPropertyKeyColumn: metadataPropertyKeyColumn,
901
2640
  metadataPropertyNameForms: metadataPropertyNameForms,
902
2641
  metadataRowsOfProperty: metadataRowsOfProperty,
@@ -2235,9 +3974,7 @@ const getItemsData = async ({ modelName, modelFileId, schemaName, deleted, inclu
2235
3974
  conditions.push(eq(seeds.type, easName(modelName)));
2236
3975
  }
2237
3976
  if (!modelFileId && schemaName && modelName) {
2238
- const _mod_0 = await import('./tabEvents-DT5ez6io.js');
2239
- const _ns_0 = _mod_0.bh;
2240
- const { resolveModelRecord } = _ns_0;
3977
+ const { resolveModelRecord } = await Promise.resolve().then(function () { return resolveModelRecord$1; });
2241
3978
  modelFileId = (await resolveModelRecord(modelName, { schemaName }))?.schemaFileId ?? undefined;
2242
3979
  if (!modelFileId)
2243
3980
  return []; // that schema has no such model
@@ -2324,6 +4061,33 @@ async function getLatestVersionRow(seedLocalId) {
2324
4061
  return rows[0] ?? null;
2325
4062
  }
2326
4063
 
4064
+ const logger$6 = debug('seedSdk:helpers:propertySchemaUid');
4065
+ /**
4066
+ * UID of the EAS schema a property publishes under, or undefined when EAS has none, the property's
4067
+ * type has no EAS type, or the lookup fails. Reads `getEasSchemaUidForExactDefinition`, which
4068
+ * answers with a UID publish registered in this session before the indexer has it, and caches
4069
+ * found UIDs and misses, so creating an item and then saving its properties asks EAS once per
4070
+ * definition. May call EAS, so run it before a transaction, not inside one. Callers store the UID
4071
+ * on the metadata row; publish resolves schemas itself, so a missing one costs nothing there.
4072
+ */
4073
+ async function lookupPropertySchemaUid(propertyName, propertyDef) {
4074
+ const definition = easSchemaDefinitionForProperty(propertyName, propertyDef);
4075
+ if (!definition)
4076
+ return undefined;
4077
+ try {
4078
+ return await getEasSchemaUidForExactDefinition(definition);
4079
+ }
4080
+ catch (error) {
4081
+ logger$6(`EAS schema lookup for "${definition}" failed (offline?): ${error}`);
4082
+ return undefined;
4083
+ }
4084
+ }
4085
+
4086
+ var propertySchemaUid = /*#__PURE__*/Object.freeze({
4087
+ __proto__: null,
4088
+ lookupPropertySchemaUid: lookupPropertySchemaUid
4089
+ });
4090
+
2327
4091
  const initialize = fromCallback(({ sendBack, input: { context } }) => {
2328
4092
  const { isRelation, propertyName, storageTransactionId, propertyRecordSchema, } = context;
2329
4093
  let { schemaUid } = context;
@@ -2334,21 +4098,10 @@ const initialize = fromCallback(({ sendBack, input: { context } }) => {
2334
4098
  (!propertyRecordSchema ||
2335
4099
  !propertyRecordSchema.storageType ||
2336
4100
  propertyRecordSchema.storageType !== 'ItemStorage')) {
2337
- try {
2338
- // A name alone matches schemas of any type (`bytes32 storage_transaction_id` beside
2339
- // `string storage_transaction_id`), so look up the exact definition when the type is known.
2340
- const schemaText = easSchemaDefinitionForProperty(propertyName, propertyRecordSchema) ?? propertyName;
2341
- schemaUid = await getEasSchemaUidForSchemaDefinition({ schemaText });
2342
- if (schemaUid) {
2343
- sendBack({ type: 'updateContext', schemaUid });
2344
- }
2345
- }
2346
- catch (error) {
2347
- // If schema fetch fails, continue without schemaUid - it's not required for local metadata
2348
- // Log error in development but don't throw
2349
- if (process.env.NODE_ENV === 'development') {
2350
- console.warn(`Failed to fetch schemaUid for property ${propertyName}:`, error);
2351
- }
4101
+ // Undefined when the lookup fails: local metadata doesn't need it.
4102
+ schemaUid = await lookupPropertySchemaUid(propertyName, propertyRecordSchema);
4103
+ if (schemaUid) {
4104
+ sendBack({ type: 'updateContext', schemaUid });
2352
4105
  }
2353
4106
  }
2354
4107
  if (isRelation) {
@@ -2483,7 +4236,7 @@ const getRelationValueData = async (propertyValue) => {
2483
4236
 
2484
4237
  // Dynamic import to break circular dependency: schema/index -> ... -> resolveRelatedValue -> schema/index
2485
4238
  // import { ModelPropertyDataTypes } from '@/schema'
2486
- const logger$6 = debug('seedSdk:property:actors:resolveRelatedValue');
4239
+ const logger$5 = debug('seedSdk:property:actors:resolveRelatedValue');
2487
4240
  const storageTransactionIdToContentUrl = new Map();
2488
4241
  const refResolvedValueToContentUrl = new Map();
2489
4242
  const seedUidToContentUrl = new Map();
@@ -2491,14 +4244,14 @@ const resolveRelatedValue = fromCallback(({ sendBack, input: { context } }) => {
2491
4244
  const { isRelation, propertyValue, propertyName, propertyRecordSchema, populatedFromDb, } = context;
2492
4245
  const _resolveRelatedValue = async () => {
2493
4246
  // Use dynamic import to break circular dependency
2494
- const schemaMod = await import('./index-DXRhFg1N.js');
4247
+ const schemaMod = await import('./index-CmNqUKQN.js');
2495
4248
  const { ModelPropertyDataTypes } = schemaMod;
2496
4249
  if (!propertyValue || !isRelation || populatedFromDb) {
2497
4250
  return;
2498
4251
  }
2499
4252
  if (Array.isArray(propertyValue)) {
2500
4253
  // TODO: Handle array of seedUids
2501
- logger$6(`${propertyName} value is an array of seedUids`);
4254
+ logger$5(`${propertyName} value is an array of seedUids`);
2502
4255
  return;
2503
4256
  }
2504
4257
  if (typeof propertyValue === 'object') {
@@ -2510,7 +4263,7 @@ const resolveRelatedValue = fromCallback(({ sendBack, input: { context } }) => {
2510
4263
  parsedValue = JSON.parse(relationKey);
2511
4264
  }
2512
4265
  catch {
2513
- logger$6(`${propertyName} value is not a JSON string.`);
4266
+ logger$5(`${propertyName} value is not a JSON string.`);
2514
4267
  }
2515
4268
  if (!parsedValue && seedUidToContentUrl.has(relationKey)) {
2516
4269
  const contentUrl = seedUidToContentUrl.get(relationKey);
@@ -2525,7 +4278,7 @@ const resolveRelatedValue = fromCallback(({ sendBack, input: { context } }) => {
2525
4278
  return true;
2526
4279
  }
2527
4280
  if (Array.isArray(parsedValue)) {
2528
- logger$6(`${propertyName} value is a stringified array of seedUids`);
4281
+ logger$5(`${propertyName} value is a stringified array of seedUids`);
2529
4282
  return;
2530
4283
  }
2531
4284
  const storageTransactionId = await getStorageTransactionIdForSeedUid(relationKey);
@@ -2823,6 +4576,33 @@ function parseListPropertyValueFromStorage(raw) {
2823
4576
  return trimmed.split(',').map((s) => s.trim()).filter(Boolean);
2824
4577
  }
2825
4578
 
4579
+ /** Subdirectory of the files root that holds a storage seed's file, by its data type. */
4580
+ const getStorageSubdirForDataType = (dataType) => {
4581
+ switch (normalizeDataType(dataType ?? undefined)) {
4582
+ case 'Image':
4583
+ return 'images';
4584
+ case 'Html':
4585
+ return 'html';
4586
+ case 'Json':
4587
+ return 'json';
4588
+ case 'File':
4589
+ default:
4590
+ // Unknown types are generic files; `images/` is only for what the SDK knows is an image.
4591
+ return 'files';
4592
+ }
4593
+ };
4594
+ /**
4595
+ * Subdirectory (relative to the files root, no leading slash) for an ItemStorage property's file:
4596
+ * its model's `storage.path` (`localStorageDir`), else `files`.
4597
+ */
4598
+ const getItemStorageSubdir = (localStorageDir) => (localStorageDir ?? '').replace(/^\/+/, '').replace(/\/+$/, '') || 'files';
4599
+ /**
4600
+ * File name a local save gives an ItemStorage property: `<seedUid or seedLocalId><extension>`
4601
+ * (see saveItemStorage). Downloaded content is named after its transaction instead
4602
+ * (`<txId><extension>`, see syncDbWithEas), which is what the metadata row's refResolvedValue says.
4603
+ */
4604
+ const getItemStorageLocalFileName = (seedId, filenameSuffix) => `${seedId}${filenameSuffix ?? ''}`;
4605
+
2826
4606
  /**
2827
4607
  * Name of a new Image or File seed's file in the shared `images/` or `files/` folder. Uploads keep
2828
4608
  * their own name (and so their extension, which publish reads for the content type), prefixed with
@@ -2946,6 +4726,52 @@ function storageFileName(kind, seedLocalId, content) {
2946
4726
  return extension ? `${seedLocalId}.${extension}` : seedLocalId;
2947
4727
  }
2948
4728
 
4729
+ const isFile = async (path) => {
4730
+ try {
4731
+ const fs = await BaseFileManager.getFs();
4732
+ return (await fs.promises.stat(path)).isFile();
4733
+ }
4734
+ catch {
4735
+ return false;
4736
+ }
4737
+ };
4738
+ /**
4739
+ * A file in `subdir` (relative to the files root) for a stored reference: the file of that exact
4740
+ * name, else the first whose name contains one of `needles` (a downloaded file is named after its
4741
+ * transaction id, sometimes with an extension). Folders (an image's size variants) don't count.
4742
+ */
4743
+ async function findFileInDir(subdir, needles) {
4744
+ const wanted = needles.filter((n) => !!n);
4745
+ if (wanted.length === 0)
4746
+ return undefined;
4747
+ for (const needle of wanted) {
4748
+ const exact = BaseFileManager.getFilesPath(subdir, needle);
4749
+ if ((await BaseFileManager.pathExists(exact)) && (await isFile(exact)))
4750
+ return exact;
4751
+ }
4752
+ const dirPath = BaseFileManager.getFilesPath(subdir);
4753
+ if (!(await BaseFileManager.pathExists(dirPath)))
4754
+ return undefined;
4755
+ const fs = await BaseFileManager.getFs();
4756
+ const path = BaseFileManager.getPathModule();
4757
+ const names = await fs.promises.readdir(dirPath);
4758
+ for (const needle of wanted) {
4759
+ for (const name of names) {
4760
+ if (!path.basename(name).includes(needle))
4761
+ continue;
4762
+ const candidate = BaseFileManager.getFilesPath(subdir, name);
4763
+ if (await isFile(candidate))
4764
+ return candidate;
4765
+ }
4766
+ }
4767
+ return undefined;
4768
+ }
4769
+
4770
+ var findFile = /*#__PURE__*/Object.freeze({
4771
+ __proto__: null,
4772
+ findFileInDir: findFileInDir
4773
+ });
4774
+
2949
4775
  /** Which kind of file holds a property's value, from its definition, else the row's `refSeedType`. */
2950
4776
  function storedValueKind(def, row) {
2951
4777
  if (def?.storageType === 'ItemStorage')
@@ -3005,7 +4831,7 @@ async function resolveStorageRef(kind, row) {
3005
4831
  }
3006
4832
  if (kind === 'html') {
3007
4833
  if (!out.refResolvedValue && out.propertyValue) {
3008
- const { resolveHtmlStorageSeedLocalId } = await import('./readHtmlBodyForStorageSeed-B7HgPmq6.js');
4834
+ const { resolveHtmlStorageSeedLocalId } = await import('./readHtmlBodyForStorageSeed-Cva1DPzE.js');
3009
4835
  const localId = await resolveHtmlStorageSeedLocalId(out.propertyValue);
3010
4836
  if (localId)
3011
4837
  out.refResolvedValue = `${localId}.html`;
@@ -3054,7 +4880,7 @@ async function resolveStorageDisplay(kind, row, options = {}) {
3054
4880
  }
3055
4881
  }
3056
4882
  if (!renderValue && options.fetch && ref.propertyValue) {
3057
- const { readHtmlBodyForStorageSeedPropertyValue } = await import('./readHtmlBodyForStorageSeed-B7HgPmq6.js');
4883
+ const { readHtmlBodyForStorageSeedPropertyValue } = await import('./readHtmlBodyForStorageSeed-Cva1DPzE.js');
3058
4884
  renderValue = await readHtmlBodyForStorageSeedPropertyValue(ref.propertyValue);
3059
4885
  }
3060
4886
  return { ref, filePath, renderValue };
@@ -3066,16 +4892,12 @@ async function resolveStorageDisplay(kind, row, options = {}) {
3066
4892
  : BaseFileManager.pathExists(BaseFileManager.getFilesPath(dir, name)).then((exists) => exists ? BaseFileManager.getFilesPath(dir, name) : undefined);
3067
4893
  // ensureImageLocal also makes the smallest size variant when the original is already there
3068
4894
  if (kind === 'image' && options.fetch) {
3069
- const _mod_1 = await import('./tabEvents-DT5ez6io.js');
3070
- const _ns_1 = _mod_1.bo;
3071
- const { ensureImageLocal } = _ns_1;
4895
+ const { ensureImageLocal } = await import('./ensureImageLocal-B6suz6FC.js');
3072
4896
  await ensureImageLocal({ transactionId: name, fileName: name, widths: [ImageSize.EXTRA_SMALL] });
3073
4897
  }
3074
4898
  let filePath = await find();
3075
4899
  if (!filePath && kind === 'file' && options.fetch) {
3076
- const _mod_2 = await import('./tabEvents-DT5ez6io.js');
3077
- const _ns_2 = _mod_2.bm;
3078
- const { downloadTransactionIdWithDedupe } = _ns_2;
4900
+ const { downloadTransactionIdWithDedupe } = await import('./download-DHWUPLlK.js');
3079
4901
  await downloadTransactionIdWithDedupe(name);
3080
4902
  filePath = await find();
3081
4903
  }
@@ -3094,7 +4916,7 @@ var resolve = /*#__PURE__*/Object.freeze({
3094
4916
  storedValueKind: storedValueKind
3095
4917
  });
3096
4918
 
3097
- const logger$5 = debug('seedSdk:property:actors:hydrateFromDb');
4919
+ const logger$4 = debug('seedSdk:property:actors:hydrateFromDb');
3098
4920
  /**
3099
4921
  * Re-reads a loaded property (the `rehydrateFromDb` event): its current row with the pending
3100
4922
  * outbox value over it (currentPropertyRow), its recorded conflict, and an Image, File or Html
@@ -3147,7 +4969,7 @@ const hydrateFromDb = fromCallback(({ sendBack, input: { context } }) => {
3147
4969
  }
3148
4970
  }
3149
4971
  catch (e) {
3150
- logger$5(`[hydrateFromDb] ${kind} file resolution error`, e);
4972
+ logger$4(`[hydrateFromDb] ${kind} file resolution error`, e);
3151
4973
  }
3152
4974
  }
3153
4975
  const propertyValue = propertyRecordSchema && normalizedDataType === 'List' && typeof row.propertyValue === 'string'
@@ -4070,14 +5892,14 @@ const evictionLog = {
4070
5892
  },
4071
5893
  };
4072
5894
 
4073
- const logger$4 = debug('seedSdk:itemProperty:loadProperty');
5895
+ const logger$3 = debug('seedSdk:itemProperty:loadProperty');
4074
5896
  /**
4075
5897
  * Load a property's value and definition: its current row (with a pending outbox value over it,
4076
5898
  * ADR 0019), else only its definition, for a property with no row yet.
4077
5899
  */
4078
5900
  async function loadProperty(context) {
4079
5901
  const { seedLocalId, seedUid, propertyName, versionLocalId } = context;
4080
- logger$4(`loadOrCreateProperty called for propertyName: ${propertyName}, seedLocalId: ${seedLocalId}, seedUid: ${seedUid}`);
5902
+ logger$3(`loadOrCreateProperty called for propertyName: ${propertyName}, seedLocalId: ${seedLocalId}, seedUid: ${seedUid}`);
4081
5903
  if (!seedLocalId && !seedUid) {
4082
5904
  throw new Error('seedLocalId or seedUid is required');
4083
5905
  }
@@ -4106,9 +5928,7 @@ async function loadProperty(context) {
4106
5928
  const modelNameForNew = context.modelName;
4107
5929
  if (modelNameForNew) {
4108
5930
  try {
4109
- const _mod_3 = await import('./tabEvents-DT5ez6io.js');
4110
- const _ns_3 = _mod_3.bh;
4111
- const { resolveItemModelFileId, modelNameForType } = _ns_3;
5931
+ const { resolveItemModelFileId, modelNameForType } = await Promise.resolve().then(function () { return resolveModelRecord$1; });
4112
5932
  const itemModelFileId = await resolveItemModelFileId({
4113
5933
  modelFileId: context.modelFileId,
4114
5934
  seedLocalId,
@@ -4123,10 +5943,10 @@ async function loadProperty(context) {
4123
5943
  });
4124
5944
  }
4125
5945
  catch (error) {
4126
- logger$4(`Metadata not found: no definition for propertyName "${propertyName}": ${error}`);
5946
+ logger$3(`Metadata not found: no definition for propertyName "${propertyName}": ${error}`);
4127
5947
  }
4128
5948
  }
4129
- logger$4(`Metadata not found in database for propertyName: ${propertyName}, seedLocalId: ${seedLocalId}`);
5949
+ logger$3(`Metadata not found in database for propertyName: ${propertyName}, seedLocalId: ${seedLocalId}`);
4130
5950
  return {
4131
5951
  propertyName,
4132
5952
  propertyValue: context.propertyValue,
@@ -4146,9 +5966,7 @@ async function loadProperty(context) {
4146
5966
  // Load propertyRecordSchema from database to make ItemProperty independent from Model
4147
5967
  let propertyRecordSchema = undefined;
4148
5968
  // Model names are only unique per schema: pin lookups to the item's own model row.
4149
- const _mod_4 = await import('./tabEvents-DT5ez6io.js');
4150
- const _ns_4 = _mod_4.bh;
4151
- const { resolveItemModelFileId, modelNameForType } = _ns_4;
5969
+ const { resolveItemModelFileId, modelNameForType } = await Promise.resolve().then(function () { return resolveModelRecord$1; });
4152
5970
  const modelFileId = await resolveItemModelFileId({
4153
5971
  modelFileId: context.modelFileId,
4154
5972
  seedLocalId: seedLocalId ?? metadataRecord.seedLocalId,
@@ -4172,7 +5990,7 @@ async function loadProperty(context) {
4172
5990
  timing.mark('model schema');
4173
5991
  }
4174
5992
  catch (error) {
4175
- logger$4(`Error loading the definition of "${propertyName}": ${error}`);
5993
+ logger$3(`Error loading the definition of "${propertyName}": ${error}`);
4176
5994
  // Continue without propertyRecordSchema
4177
5995
  }
4178
5996
  // A file's display: a session URL for an Image or File, the text of an Html body, fetched when
@@ -4204,7 +6022,7 @@ async function loadProperty(context) {
4204
6022
  }
4205
6023
  }
4206
6024
  catch (e) {
4207
- logger$4(`Failed to resolve the ${kind} file for ${propertyName}: ${e}`);
6025
+ logger$3(`Failed to resolve the ${kind} file for ${propertyName}: ${e}`);
4208
6026
  }
4209
6027
  }
4210
6028
  let propertyValueOut = metadataRecord.propertyValue || undefined;
@@ -4251,13 +6069,13 @@ async function loadPropertyDefinition(db, { propertyId, modelName, modelFileId,
4251
6069
  }
4252
6070
  if (!modelName)
4253
6071
  return undefined;
4254
- const { loadModelPropertyRecordSchemas } = await import('./modelPropertyRecordSchemas-DEe-lYhn.js');
6072
+ const { loadModelPropertyRecordSchemas } = await import('./modelPropertyRecordSchemas-CEac3FB8.js');
4255
6073
  const definition = (await loadModelPropertyRecordSchemas(modelName, modelFileId, db)).get(propertyName);
4256
6074
  if (definition)
4257
6075
  return definition;
4258
- const _mod_5 = await import('./errors-CnT0fSMK.js');
4259
- const _ns_5 = _mod_5.a4;
4260
- const { Model } = _ns_5;
6076
+ const _mod_2 = await import('./publishErrors-DZXDX3B6.js');
6077
+ const _ns_2 = _mod_2.am;
6078
+ const { Model } = _ns_2;
4261
6079
  const { modelPropertiesToObject } = await Promise.resolve().then(function () { return model; });
4262
6080
  // Try the name first ("post" -> "Post"); then findByModelType for names with spaces ("new_model" -> "New model")
4263
6081
  let model$1 = Model.resolve(modelName, { modelFileId });
@@ -4266,7 +6084,7 @@ async function loadPropertyDefinition(db, { propertyId, modelName, modelFileId,
4266
6084
  }
4267
6085
  const unwritten = model$1?.properties?.length ? modelPropertiesToObject(model$1.properties)[propertyName] : undefined;
4268
6086
  if (unwritten)
4269
- logger$4(`No row yet: in-memory definition of "${propertyName}" from Model ${modelName}`);
6087
+ logger$3(`No row yet: in-memory definition of "${propertyName}" from Model ${modelName}`);
4270
6088
  return unwritten;
4271
6089
  }
4272
6090
 
@@ -4365,7 +6183,7 @@ function whenValidationLoaded() {
4365
6183
  // neutralizes the minified chunk-export read (`.then(n => n._)`) in this form
4366
6184
  validationLoading ?? (validationLoading = (async () => {
4367
6185
  try {
4368
- const { SchemaValidationService } = await import('./SchemaValidationService-CieGrj5c.js');
6186
+ const { SchemaValidationService } = await import('./SchemaValidationService-Dc-lpFQX.js');
4369
6187
  validationService = new SchemaValidationService();
4370
6188
  }
4371
6189
  finally {
@@ -4600,17 +6418,13 @@ hints = {}) => {
4600
6418
  }
4601
6419
  try {
4602
6420
  if (hints.propertyId != null) {
4603
- const _mod_6 = await import('./tabEvents-DT5ez6io.js');
4604
- const _ns_6 = _mod_6.bi;
4605
- const { getPropertyDefinition } = _ns_6;
6421
+ const { getPropertyDefinition } = await Promise.resolve().then(function () { return propertyDefinition; });
4606
6422
  const definition = await getPropertyDefinition(hints.propertyId);
4607
6423
  if (definition)
4608
6424
  return definition;
4609
6425
  }
4610
- const _mod_7 = await import('./tabEvents-DT5ez6io.js');
4611
- const _ns_7 = _mod_7.bh;
4612
- const { resolveItemModelFileId } = _ns_7;
4613
- const { getModelPropertyDefinitions, resolvePropertyKey } = await import('./property-iNW1N3vm.js');
6426
+ const { resolveItemModelFileId } = await Promise.resolve().then(function () { return resolveModelRecord$1; });
6427
+ const { getModelPropertyDefinitions, resolvePropertyKey } = await import('./property-4jSqHieb.js');
4614
6428
  const modelFileId = (await resolveItemModelFileId(hints)) ?? undefined;
4615
6429
  const name = modelName || ((await modelTypeToModelName(modelType)) ?? '');
4616
6430
  // An ambiguous bare name reads nothing here; the in-memory model below may still know it
@@ -4618,9 +6432,9 @@ hints = {}) => {
4618
6432
  const key = resolvePropertyKey(definitions, propertyName);
4619
6433
  if (key)
4620
6434
  return definitions[key];
4621
- const _mod_8 = await import('./errors-CnT0fSMK.js');
4622
- const _ns_8 = _mod_8.a4;
4623
- const { Model } = _ns_8;
6435
+ const _mod_3 = await import('./publishErrors-DZXDX3B6.js');
6436
+ const _ns_3 = _mod_3.am;
6437
+ const { Model } = _ns_3;
4624
6438
  const { modelPropertiesToObject } = await Promise.resolve().then(function () { return model; });
4625
6439
  let model$1 = modelName ? Model.peekByName(modelName) : undefined;
4626
6440
  if (!model$1?.properties?.length && modelType)
@@ -4639,12 +6453,10 @@ hints = {}) => {
4639
6453
  const modelTypeToModelName = async (modelType) => {
4640
6454
  if (!modelType)
4641
6455
  return undefined;
4642
- const _mod_9 = await import('./tabEvents-DT5ez6io.js');
4643
- const _ns_9 = _mod_9.bh;
4644
- const { modelNameForType } = _ns_9;
6456
+ const { modelNameForType } = await Promise.resolve().then(function () { return resolveModelRecord$1; });
4645
6457
  return modelNameForType(modelType).catch(() => undefined);
4646
6458
  };
4647
- const logger$3 = debug('seedSdk:property:class');
6459
+ const logger$2 = debug('seedSdk:property:class');
4648
6460
  // Define tracked properties for the Proxy
4649
6461
  // These properties will be read from/written to the actor context
4650
6462
  const TRACKED_PROPERTIES$1 = [
@@ -4776,7 +6588,7 @@ class ItemProperty {
4776
6588
  serviceInput.propertyValue = JSON.parse(propertyValue);
4777
6589
  }
4778
6590
  catch (e) {
4779
- logger$3('List property value is not JSON', e);
6591
+ logger$2('List property value is not JSON', e);
4780
6592
  }
4781
6593
  }
4782
6594
  }
@@ -4803,7 +6615,7 @@ class ItemProperty {
4803
6615
  return;
4804
6616
  }
4805
6617
  // Use dynamic import to break circular dependency
4806
- const schemaMod = await import('./index-DXRhFg1N.js');
6618
+ const schemaMod = await import('./index-CmNqUKQN.js');
4807
6619
  const { ModelPropertyDataTypes } = schemaMod;
4808
6620
  const { context } = snapshot;
4809
6621
  const { propertyRecordSchema } = context;
@@ -4909,7 +6721,7 @@ class ItemProperty {
4909
6721
  }
4910
6722
  }
4911
6723
  catch (e) {
4912
- logger$3(`[ItemProperty] [${context.seedLocalId}] [${context.propertyName}] [storageType] error`, e);
6724
+ logger$2(`[ItemProperty] [${context.seedLocalId}] [${context.propertyName}] [storageType] error`, e);
4913
6725
  renderValue = isHtml ? undefined : 'No file found';
4914
6726
  }
4915
6727
  }
@@ -4959,7 +6771,7 @@ class ItemProperty {
4959
6771
  });
4960
6772
  this._service.start();
4961
6773
  // Assignments check values synchronously (planPropertyWrite)
4962
- whenValidationLoaded().catch((error) => logger$3('[ItemProperty] loading validation', error));
6774
+ whenValidationLoaded().catch((error) => logger$2('[ItemProperty] loading validation', error));
4963
6775
  onEntityTeardown(this, () => {
4964
6776
  this._subscription?.unsubscribe();
4965
6777
  revokeHeldObjectUrls(this._service);
@@ -5016,9 +6828,9 @@ class ItemProperty {
5016
6828
  // The change feed reports writes by stored name: watch every name this property's rows have
5017
6829
  const propertyNames = [...property.names];
5018
6830
  if (propertyId != null) {
5019
- const _mod_10 = await import('./index-BjWk0x4-.js');
5020
- const _ns_10 = _mod_10.s;
5021
- const { metadata } = _ns_10;
6831
+ const _mod_4 = await import('./index-2PaIhvOU.js');
6832
+ const _ns_4 = _mod_4.s;
6833
+ const { metadata } = _ns_4;
5022
6834
  const { eq: eqOp } = await import('drizzle-orm');
5023
6835
  const stored = await db
5024
6836
  .selectDistinct({ propertyName: metadata.propertyName })
@@ -5242,9 +7054,7 @@ class ItemProperty {
5242
7054
  const ctx = this._service.getSnapshot().context;
5243
7055
  let modelFileId;
5244
7056
  try {
5245
- const _mod_11 = await import('./tabEvents-DT5ez6io.js');
5246
- const _ns_11 = _mod_11.bh;
5247
- const { resolveItemModelFileId } = _ns_11;
7057
+ const { resolveItemModelFileId } = await Promise.resolve().then(function () { return resolveModelRecord$1; });
5248
7058
  modelFileId = await resolveItemModelFileId({
5249
7059
  modelFileId: ctx.modelFileId,
5250
7060
  seedLocalId: ctx.seedLocalId,
@@ -5561,9 +7371,7 @@ class ItemProperty {
5561
7371
  const propertiesData = await getItemProperties({ seedLocalId, seedUid });
5562
7372
  const instances = [];
5563
7373
  const seenNormalized = new Set();
5564
- const _mod_12 = await import('./tabEvents-DT5ez6io.js');
5565
- const _ns_12 = _mod_12.bh;
5566
- const { resolveItemModelFileId } = _ns_12;
7374
+ const { resolveItemModelFileId } = await Promise.resolve().then(function () { return resolveModelRecord$1; });
5567
7375
  const itemModelFileId = await resolveItemModelFileId({ seedLocalId, seedUid });
5568
7376
  // One lookup per model type, not per property
5569
7377
  const modelNameByType = new Map();
@@ -5583,7 +7391,7 @@ class ItemProperty {
5583
7391
  // (`textId` → `text`, `authorIdentityIds` → `authors`)
5584
7392
  const definitionsKey = JSON.stringify([modelName, modelFileId ?? null]);
5585
7393
  if (modelName && !definitionsByModel.has(definitionsKey)) {
5586
- const { getModelPropertyDefinitions } = await import('./property-iNW1N3vm.js');
7394
+ const { getModelPropertyDefinitions } = await import('./property-4jSqHieb.js');
5587
7395
  definitionsByModel.set(definitionsKey,
5588
7396
  // No model row: the stored name decides
5589
7397
  getModelPropertyDefinitions(modelName, { modelFileId }).then(Object.fromEntries, () => ({})));
@@ -5757,7 +7565,7 @@ class ItemProperty {
5757
7565
  const { seedLocalId, propertyName } = this._getSnapshotContext();
5758
7566
  if (!seedLocalId || !propertyName)
5759
7567
  return;
5760
- const { appendOutboxOpAndWait, markPropertyConflictsResolved, startItemOutbox, throwItemWriteError } = await import('./index-CZMWKXLM.js');
7568
+ const { appendOutboxOpAndWait, markPropertyConflictsResolved, startItemOutbox, throwItemWriteError } = await import('./index-BXTyvnKb.js');
5761
7569
  if (choice === 'remote') {
5762
7570
  startItemOutbox();
5763
7571
  await appendOutboxOpAndWait({
@@ -5821,8 +7629,10 @@ class ItemProperty {
5821
7629
  * Versions stop showing the value too. Revocation is permanent.
5822
7630
  * @throws ClearNotSupportedError for a Boolean or Date property without `retract`: their EAS types
5823
7631
  * have no empty value.
7632
+ * @throws EntityUnloadedError when this instance was unloaded; nothing is written.
5824
7633
  */
5825
7634
  async clear(options = {}) {
7635
+ this._assertLoaded('clear');
5826
7636
  const retract = options.retract === true;
5827
7637
  const context = this._getSnapshotContext();
5828
7638
  const dataType = normalizeDataType(this._dataType ?? context.propertyRecordSchema?.dataType);
@@ -5905,9 +7715,7 @@ class ItemProperty {
5905
7715
  context = this._getSnapshotContext();
5906
7716
  if (!context.seedLocalId) {
5907
7717
  // An item known only by its seed uid: its local seed, if it has one
5908
- const _mod_13 = await import('./tabEvents-DT5ez6io.js');
5909
- const _ns_13 = _mod_13.bg;
5910
- const { findSeedRow } = _ns_13;
7718
+ const { findSeedRow } = await Promise.resolve().then(function () { return findSeedRow$1; });
5911
7719
  const seedLocalId = context.seedUid ? (await findSeedRow({ uid: context.seedUid }))?.localId : undefined;
5912
7720
  if (!seedLocalId)
5913
7721
  throw new Error(`No local seed for the item to save ${context.propertyName} to`);
@@ -5945,7 +7753,7 @@ class ItemProperty {
5945
7753
  /** Waits for an appended op to settle, then shows its row, or the current value if it failed. */
5946
7754
  async _followWrite(write, appended, plan) {
5947
7755
  try {
5948
- const { getOutboxOps, outboxOpResult, startItemOutbox, waitForOutboxOp } = await import('./index-CZMWKXLM.js');
7756
+ const { getOutboxOps, outboxOpResult, startItemOutbox, waitForOutboxOp } = await import('./index-BXTyvnKb.js');
5949
7757
  startItemOutbox();
5950
7758
  const opId = await appended;
5951
7759
  await waitForOutboxOp(opId);
@@ -6004,7 +7812,7 @@ class ItemProperty {
6004
7812
  this._service.send({ type: 'updateContext', ...outcome.applied, isSaving: false });
6005
7813
  return;
6006
7814
  }
6007
- const { throwItemWriteError } = await import('./index-CZMWKXLM.js');
7815
+ const { throwItemWriteError } = await import('./index-BXTyvnKb.js');
6008
7816
  let error = outcome.error;
6009
7817
  try {
6010
7818
  throwItemWriteError(outcome.error);
@@ -6012,7 +7820,7 @@ class ItemProperty {
6012
7820
  catch (mapped) {
6013
7821
  error = mapped;
6014
7822
  }
6015
- await this._showCurrentValue().catch((readError) => logger$3(`[ItemProperty] reading ${this.propertyName} after a failed write`, readError));
7823
+ await this._showCurrentValue().catch((readError) => logger$2(`[ItemProperty] reading ${this.propertyName} after a failed write`, readError));
6016
7824
  if (this._latestWrite !== write || isActorStopped(this._service))
6017
7825
  return;
6018
7826
  this._service.send({
@@ -6139,7 +7947,7 @@ class ItemProperty {
6139
7947
  if (db && seedLocalId) {
6140
7948
  // Through the outbox (ADR 0019): after the property's pending edits, which would
6141
7949
  // otherwise write its rows back
6142
- const { appendOutboxOpAndWait, startItemOutbox, throwItemWriteError } = await import('./index-CZMWKXLM.js');
7950
+ const { appendOutboxOpAndWait, startItemOutbox, throwItemWriteError } = await import('./index-BXTyvnKb.js');
6143
7951
  startItemOutbox();
6144
7952
  await appendOutboxOpAndWait({
6145
7953
  seedLocalId,
@@ -6149,9 +7957,9 @@ class ItemProperty {
6149
7957
  }).catch(throwItemWriteError);
6150
7958
  }
6151
7959
  else if (db) {
6152
- const _mod_14 = await import('./index-BjWk0x4-.js');
6153
- const _ns_14 = _mod_14.s;
6154
- const seedSchemaMod = _ns_14;
7960
+ const _mod_5 = await import('./index-2PaIhvOU.js');
7961
+ const _ns_5 = _mod_5.s;
7962
+ const seedSchemaMod = _ns_5;
6155
7963
  const { metadata } = seedSchemaMod;
6156
7964
  const drizzleMod = await import('drizzle-orm');
6157
7965
  const { and, eq } = drizzleMod;
@@ -6187,7 +7995,7 @@ var ItemProperty$1 = /*#__PURE__*/Object.freeze({
6187
7995
  ItemPropertySaveValidationError: ItemPropertySaveValidationError
6188
7996
  });
6189
7997
 
6190
- const logger$2 = debug('seedSdk:db:read:getSchemaUidForModel');
7998
+ const logger$1 = debug('seedSdk:db:read:getSchemaUidForModel');
6191
7999
  /**
6192
8000
  * EAS schema UID of a model's seeds: the in-memory cache, then `model_uids` (written from EAS by
6193
8001
  * startup and sync), then EAS itself. The DB comes before EAS so creating items and saving
@@ -6224,7 +8032,7 @@ const getEasSchemaUidForModel = async (modelName) => {
6224
8032
  });
6225
8033
  }
6226
8034
  catch (error) {
6227
- logger$2(`EAS schema lookup for model ${modelName} failed (offline?): ${error}`);
8035
+ logger$1(`EAS schema lookup for model ${modelName} failed (offline?): ${error}`);
6228
8036
  return undefined;
6229
8037
  }
6230
8038
  if (easSchemaUid) {
@@ -6270,165 +8078,6 @@ const createSeed = async ({ type, seedUid, modelFileId }, options = {}) => {
6270
8078
  return newSeedLocalId;
6271
8079
  };
6272
8080
 
6273
- /**
6274
- * The publisher a seed's version and property rows take: the seed's own (its `publisher`, else the
6275
- * attester of its stored attestation). Undefined for a draft that belongs to no wallet yet; those
6276
- * rows are then claimed together with their seed (claimUnpublishedDrafts).
6277
- */
6278
- async function getSeedPublisher(seedLocalId,
6279
- /** The database or transaction to read; the app database by default. */
6280
- db = BaseDb.getAppDb()) {
6281
- if (!seedLocalId)
6282
- return undefined;
6283
- const row = await findSeedRow({ localId: seedLocalId }, db);
6284
- if (!row)
6285
- return undefined;
6286
- return normalizePublisher(resolvePublisherFromSeedRow(row));
6287
- }
6288
-
6289
- const logger$1 = debug('seedSdk:helpers:propertySchemaUid');
6290
- /**
6291
- * UID of the EAS schema a property publishes under, or undefined when EAS has none, the property's
6292
- * type has no EAS type, or the lookup fails. Found UIDs and misses are cached
6293
- * (`getEasSchemaUidForExactDefinition`), so creating an item and then saving its properties asks
6294
- * EAS once per definition. Callers store the UID on the metadata row; publish resolves schemas
6295
- * itself, so a missing one costs nothing there.
6296
- */
6297
- async function lookupPropertySchemaUid(propertyName, propertyDef) {
6298
- const definition = easSchemaDefinitionForProperty(propertyName, propertyDef);
6299
- if (!definition)
6300
- return undefined;
6301
- try {
6302
- return await getEasSchemaUidForExactDefinition(definition);
6303
- }
6304
- catch (error) {
6305
- logger$1(`EAS schema lookup for "${definition}" failed (offline?): ${error}`);
6306
- return undefined;
6307
- }
6308
- }
6309
-
6310
- var propertySchemaUid = /*#__PURE__*/Object.freeze({
6311
- __proto__: null,
6312
- lookupPropertySchemaUid: lookupPropertySchemaUid
6313
- });
6314
-
6315
- /** Error thrown when metadata validation fails (e.g. enum violation). */
6316
- class MetadataValidationError extends Error {
6317
- constructor(message, validationErrors) {
6318
- super(message);
6319
- this.validationErrors = validationErrors;
6320
- this.name = 'MetadataValidationError';
6321
- }
6322
- }
6323
- /**
6324
- * The EAS schema uid a metadata row of this property takes, from the property's data type and EAS
6325
- * name. Undefined for a data type with no EAS type or when the lookup fails. May call EAS, so run
6326
- * it before a transaction, not inside one.
6327
- */
6328
- /** The property's EAS schema UID from the shared lookup cache (lookupPropertySchemaUid). */
6329
- const resolveMetadataSchemaUid = async (propertyName, propertyRecordSchema) => lookupPropertySchemaUid(propertyName, propertyRecordSchema);
6330
- const createMetadata = async (metadataValues, propertyRecordSchema, options) => {
6331
- const appDb = options?.db ?? BaseDb.getAppDb();
6332
- metadataValues.localId = generateId();
6333
- // A property row belongs to its seed's wallet (none yet for a draft made with no wallet connected).
6334
- const publisher = await getSeedPublisher(metadataValues.seedLocalId, appDb);
6335
- if (publisher) {
6336
- metadataValues.publisher = publisher;
6337
- }
6338
- if (!metadataValues.modelType && metadataValues.modelName) {
6339
- metadataValues.modelType = easName(metadataValues.modelName);
6340
- }
6341
- // property_id is the match key (decision D4); the row is written under the property's storage name.
6342
- const property = await resolveMetadataProperty({
6343
- propertyId: metadataValues.propertyId,
6344
- definitionId: propertyRecordSchema?.id,
6345
- propertyName: metadataValues.propertyName,
6346
- modelName: metadataValues.modelName ?? metadataValues.modelType,
6347
- modelFileId: options?.modelFileId,
6348
- seedLocalId: metadataValues.seedLocalId,
6349
- seedUid: metadataValues.seedUid,
6350
- }, appDb, { onAmbiguousModel: 'none' });
6351
- if (property) {
6352
- metadataValues.propertyId = property.id;
6353
- metadataValues.propertyName = property.storageName;
6354
- }
6355
- else if (metadataValues.propertyName && propertyRecordSchema) {
6356
- metadataValues.propertyName = metadataStorageName(metadataValues.propertyName, propertyRecordSchema);
6357
- }
6358
- const isItemStorage = propertyRecordSchema && propertyRecordSchema.storageType === 'ItemStorage';
6359
- // if (
6360
- // propertyRecordSchema &&
6361
- // propertyRecordSchema.localStorageDir &&
6362
- // isItemStorage
6363
- // ) {
6364
- // const filename = `${metadataValues.seedUid || metadataValues.seedLocalId}${propertyRecordSchema.filenameSuffix}`
6365
- // const filePath = path.join(propertyRecordSchema.localStorageDir, filename)
6366
- // await fs.promises.writeFile(filePath, metadataValues.propertyValue)
6367
- // metadataValues.propertyValue = filename
6368
- // metadataValues.refValueType = 'file'
6369
- // }
6370
- // Convert propertyValue to string if it's not already (metadata table expects text)
6371
- if (metadataValues.propertyValue !== undefined && metadataValues.propertyValue !== null) {
6372
- if (Array.isArray(metadataValues.propertyValue)) {
6373
- // List values: JSON array is the storage format (String() would give legacy comma-separated ids)
6374
- metadataValues.propertyValue = JSON.stringify(metadataValues.propertyValue);
6375
- }
6376
- else if (typeof metadataValues.propertyValue !== 'string') {
6377
- metadataValues.propertyValue = String(metadataValues.propertyValue);
6378
- }
6379
- }
6380
- // Validate against property validation rules (enum, pattern, minLength, maxLength) unless skipped
6381
- if (!options?.skipValidation &&
6382
- propertyRecordSchema?.validation &&
6383
- metadataValues.propertyValue != null &&
6384
- metadataValues.propertyValue !== '') {
6385
- const { SchemaValidationService } = await import('./SchemaValidationService-CieGrj5c.js');
6386
- const validationService = new SchemaValidationService();
6387
- const validationResult = validationService.validatePropertyValue(metadataValues.propertyValue, normalizeDataType(propertyRecordSchema.dataType), propertyRecordSchema.validation, propertyRecordSchema.refValueType
6388
- ? normalizeDataType(String(propertyRecordSchema.refValueType))
6389
- : undefined);
6390
- if (!validationResult.isValid && validationResult.errors.length > 0) {
6391
- const validationErrors = validationResult.errors.map((e) => ({
6392
- field: e.field,
6393
- message: e.message,
6394
- code: e.code,
6395
- }));
6396
- const message = validationErrors.map((e) => e.message).join('; ');
6397
- throw new MetadataValidationError(message, validationErrors);
6398
- }
6399
- }
6400
- // With a given db (a transaction), the caller resolves schemaUid first: the lookup may call EAS.
6401
- // Without one it isn't required for metadata insertion, so a failed lookup leaves it unset.
6402
- if (!options?.db && !isItemStorage && !metadataValues.schemaUid && metadataValues.propertyName) {
6403
- const schemaUid = await resolveMetadataSchemaUid(metadataValues.propertyName, propertyRecordSchema);
6404
- if (schemaUid)
6405
- metadataValues.schemaUid = schemaUid;
6406
- }
6407
- let inserted;
6408
- try {
6409
- inserted = await appDb
6410
- .insert(metadata)
6411
- .values({
6412
- ...metadataValues,
6413
- // Stamped here: a value carried in metadataValues would otherwise win over the column default.
6414
- createdAt: Date.now(),
6415
- updatedAt: Date.now(),
6416
- })
6417
- .returning();
6418
- }
6419
- catch (error) {
6420
- // Drizzle's message is the full SQL; the driver error that explains it is only on `cause`.
6421
- const cause = error?.cause;
6422
- const reason = cause instanceof Error ? cause.message : cause != null ? String(cause) : undefined;
6423
- throw new Error(`Failed to insert metadata for property ${metadataValues.propertyName}` +
6424
- (reason ? `: ${reason}` : `: ${error instanceof Error ? error.message : String(error)}`), { cause: error });
6425
- }
6426
- if (!inserted || inserted.length === 0) {
6427
- throw new Error(`Failed to insert metadata record for property ${metadataValues.propertyName}`);
6428
- }
6429
- return inserted[0];
6430
- };
6431
-
6432
8081
  /**
6433
8082
  * Convert Model.properties array to object format (for backward compatibility)
6434
8083
  * This replaces the old model.schema getter
@@ -6476,9 +8125,9 @@ const createNewItem = async ({ modelName, modelFileId, ...propertyData }) => {
6476
8125
  }
6477
8126
  const seedType = easName(modelName);
6478
8127
  // Dynamic import to break circular dependency
6479
- const _mod_15 = await import('./errors-CnT0fSMK.js');
6480
- const _ns_15 = _mod_15.a4;
6481
- const modelMod = _ns_15;
8128
+ const _mod_6 = await import('./publishErrors-DZXDX3B6.js');
8129
+ const _ns_6 = _mod_6.am;
8130
+ const modelMod = _ns_6;
6482
8131
  const { Model } = modelMod;
6483
8132
  const label = `createNewItem ${modelName}`;
6484
8133
  const model = await timed(`${label} model`, () => Model.resolveAsync(modelName, { modelFileId }));
@@ -6504,7 +8153,7 @@ const createNewItem = async ({ modelName, modelFileId, ...propertyData }) => {
6504
8153
  const propertyRecordSchema = propertySchemas?.[propertyName];
6505
8154
  if (!propertyRecordSchema || propertyRecordSchema.storageType === 'ItemStorage')
6506
8155
  return;
6507
- schemaUids.set(propertyName, await resolveMetadataSchemaUid(listRelationEasPropertyName(propertyName, propertyRecordSchema) ?? propertyName, propertyRecordSchema));
8156
+ schemaUids.set(propertyName, await lookupPropertySchemaUid(listRelationEasPropertyName(propertyName, propertyRecordSchema) ?? propertyName, propertyRecordSchema));
6508
8157
  })));
6509
8158
  const rows = [];
6510
8159
  const stagedPaths = [];
@@ -6534,7 +8183,7 @@ const createNewItem = async ({ modelName, modelFileId, ...propertyData }) => {
6534
8183
  }
6535
8184
  rows.push({ propertyName, propertyValue, propertyRecordSchema, schemaUid: schemaUids.get(propertyName) });
6536
8185
  }
6537
- const { appendOutboxOpAndWait, startItemOutbox } = await import('./index-CZMWKXLM.js');
8186
+ const { appendOutboxOpAndWait, startItemOutbox } = await import('./index-BXTyvnKb.js');
6538
8187
  startItemOutbox();
6539
8188
  const fields = await timed(`${label} seed fields`, () => resolveNewSeedFields(seedType));
6540
8189
  const applied = await timed(`${label} outbox apply`, () => appendOutboxOpAndWait({
@@ -6553,9 +8202,9 @@ const createNewItem = async ({ modelName, modelFileId, ...propertyData }) => {
6553
8202
  }
6554
8203
  catch (error) {
6555
8204
  // Staged values of a create that never got appended (once appended, the op's discard does it)
6556
- const _mod_16 = await import('./errors-CnT0fSMK.js');
6557
- const _ns_16 = _mod_16.a7;
6558
- const { deleteFiles } = _ns_16;
8205
+ const _mod_7 = await import('./publishErrors-DZXDX3B6.js');
8206
+ const _ns_7 = _mod_7.ap;
8207
+ const { deleteFiles } = _ns_7;
6559
8208
  await deleteFiles(stagedPaths);
6560
8209
  throw error;
6561
8210
  }
@@ -6629,16 +8278,16 @@ const createItemPropertyInstances = async (metadataRows, seedLocalId, seedUid, m
6629
8278
  };
6630
8279
  // The model's property definitions from their rows; a model created in this session whose
6631
8280
  // rows aren't written yet gives its in-memory ones
6632
- const { getModelPropertyDefinitions } = await import('./property-iNW1N3vm.js');
8281
+ const { getModelPropertyDefinitions } = await import('./property-4jSqHieb.js');
6633
8282
  let propertySchemas = Object.fromEntries(await getModelPropertyDefinitions(modelName, modelScope).catch((err) => {
6634
8283
  logger(`No property definitions for ${modelName}: ${err}`);
6635
8284
  return [];
6636
8285
  }));
6637
8286
  if (Object.keys(propertySchemas).length === 0) {
6638
8287
  const { modelPropertiesToObject } = await Promise.resolve().then(function () { return model; });
6639
- const _mod_17 = await import('./errors-CnT0fSMK.js');
6640
- const _ns_17 = _mod_17.a4;
6641
- const { Model } = _ns_17;
8288
+ const _mod_8 = await import('./publishErrors-DZXDX3B6.js');
8289
+ const _ns_8 = _mod_8.am;
8290
+ const { Model } = _ns_8;
6642
8291
  const model$1 = Model.resolve(modelName, modelScope);
6643
8292
  if (model$1?.properties?.length)
6644
8293
  propertySchemas = modelPropertiesToObject(model$1.properties);
@@ -6664,7 +8313,7 @@ const createItemPropertyInstances = async (metadataRows, seedLocalId, seedUid, m
6664
8313
  // Infer propertyRecordSchema from metadata when schema is missing (enables persistence)
6665
8314
  let propSchema = propertySchemas[propertyName];
6666
8315
  if (!propSchema && isRefTypeFromMeta) {
6667
- const schemaMod = await import('./index-DXRhFg1N.js');
8316
+ const schemaMod = await import('./index-CmNqUKQN.js');
6668
8317
  const { ModelPropertyDataTypes } = schemaMod;
6669
8318
  propSchema = {
6670
8319
  dataType: refSeedType === 'html' ? ModelPropertyDataTypes.Html
@@ -7047,9 +8696,9 @@ const getModel = () => {
7047
8696
  // the fragile `import(...).then(n => n.X)` chunk-namespace pattern.
7048
8697
  if (!modelImportPromise) {
7049
8698
  modelImportPromise = (async () => {
7050
- const _mod_18 = await import('./errors-CnT0fSMK.js');
7051
- const _ns_18 = _mod_18.a4;
7052
- const mod = _ns_18;
8699
+ const _mod_9 = await import('./publishErrors-DZXDX3B6.js');
8700
+ const _ns_9 = _mod_9.am;
8701
+ const mod = _ns_9;
7053
8702
  ModelClass = mod.Model;
7054
8703
  return mod;
7055
8704
  })();
@@ -7248,16 +8897,16 @@ class Item {
7248
8897
  };
7249
8898
  this.getPublishUploads = async (options) => {
7250
8899
  // Use dynamic import to break circular dependency
7251
- const _mod_19 = await import('./getPublishUploads-DivYpgNw.js');
7252
- const _ns_19 = _mod_19.q;
7253
- const getPublishUploadsMod = _ns_19;
8900
+ const _mod_10 = await import('./publishErrors-DZXDX3B6.js');
8901
+ const _ns_10 = _mod_10.aq;
8902
+ const getPublishUploadsMod = _ns_10;
7254
8903
  const { getPublishUploads } = getPublishUploadsMod;
7255
8904
  return await getPublishUploads(this, options);
7256
8905
  };
7257
8906
  this.getPublishPayload = async (uploadedTransactions, options) => {
7258
- const _mod_20 = await import('./getPublishPayload-DKuEaq96.js');
7259
- const _ns_20 = _mod_20.c;
7260
- const getPublishPayloadMod = _ns_20;
8907
+ const _mod_11 = await import('./getPublishPayload-Dd3HCYeI.js');
8908
+ const _ns_11 = _mod_11.b;
8909
+ const getPublishPayloadMod = _ns_11;
7261
8910
  const { getPublishPayload } = getPublishPayloadMod;
7262
8911
  return await getPublishPayload(this, uploadedTransactions, options);
7263
8912
  };
@@ -7585,9 +9234,7 @@ class Item {
7585
9234
  if (!props.modelName && props.type) {
7586
9235
  // The model the seed's type names (its easName, at the model's nameMatch level): `blog_post`
7587
9236
  // finds "Blog Post". No match leaves the type, which then fails as an unknown model.
7588
- const _mod_21 = await import('./tabEvents-DT5ez6io.js');
7589
- const _ns_21 = _mod_21.bh;
7590
- const { resolveModelRecordByNameOrType } = _ns_21;
9237
+ const { resolveModelRecordByNameOrType } = await Promise.resolve().then(function () { return resolveModelRecord$1; });
7591
9238
  const model = await resolveModelRecordByNameOrType(props.type, {
7592
9239
  modelFileId: props.modelFileId,
7593
9240
  schemaName: props.schemaName,
@@ -7649,18 +9296,16 @@ class Item {
7649
9296
  const db = BaseDb.getAppDb();
7650
9297
  if (db && props.modelName) {
7651
9298
  // Model names are only unique per schema: resolve the exact models row from the most specific scope.
7652
- const _mod_22 = await import('./tabEvents-DT5ez6io.js');
7653
- const _ns_22 = _mod_22.bh;
7654
- const { resolveModelRecord } = _ns_22;
9299
+ const { resolveModelRecord } = await Promise.resolve().then(function () { return resolveModelRecord$1; });
7655
9300
  const modelRecord = await resolveModelRecord(props.modelName, {
7656
9301
  modelFileId: props.modelFileId ?? modelInstance?.id,
7657
9302
  schemaName: schemaName ?? modelInstance?.schemaName,
7658
9303
  });
7659
9304
  const modelRecords = modelRecord ? [modelRecord] : [];
7660
9305
  if (!modelRecord) {
7661
- const _mod_23 = await import('./errors-CnT0fSMK.js');
7662
- const _ns_23 = _mod_23.a8;
7663
- const { UnknownModelError } = _ns_23;
9306
+ const _mod_12 = await import('./publishErrors-DZXDX3B6.js');
9307
+ const _ns_12 = _mod_12.ar;
9308
+ const { UnknownModelError } = _ns_12;
7664
9309
  throw new UnknownModelError(props.modelName, schemaName ?? modelInstance?.schemaName);
7665
9310
  }
7666
9311
  if (modelRecord?.schemaFileId) {
@@ -7669,9 +9314,7 @@ class Item {
7669
9314
  // Scope the constructor's Model lookup to this model's schema (as getItemData does on reload),
7670
9315
  // so a same-named model cached from another schema can't supply the property set.
7671
9316
  if (modelRecord && !props.schemaName) {
7672
- const _mod_24 = await import('./tabEvents-DT5ez6io.js');
7673
- const _ns_24 = _mod_24.bh;
7674
- const { getModelSchemaName } = _ns_24;
9317
+ const { getModelSchemaName } = await Promise.resolve().then(function () { return resolveModelRecord$1; });
7675
9318
  const modelSchemaName = await getModelSchemaName(modelRecord.id, db);
7676
9319
  if (modelSchemaName) {
7677
9320
  props.schemaName = modelSchemaName;
@@ -7774,7 +9417,7 @@ class Item {
7774
9417
  /* best-effort cache cleanup */
7775
9418
  }
7776
9419
  try {
7777
- const { appendOutboxOpAndWait, deleteFiles, startItemOutbox } = await import('./index-CZMWKXLM.js');
9420
+ const { appendOutboxOpAndWait, deleteFiles, startItemOutbox } = await import('./index-BXTyvnKb.js');
7778
9421
  startItemOutbox();
7779
9422
  const applied = await appendOutboxOpAndWait({
7780
9423
  seedLocalId,
@@ -7797,9 +9440,9 @@ class Item {
7797
9440
  for (const [propertyName, value] of pipelineValues) {
7798
9441
  const property = await this.waitForPropertyInstance(item, propertyName, timeout);
7799
9442
  if (!property) {
7800
- const _mod_25 = await import('./errors-CnT0fSMK.js');
7801
- const _ns_25 = _mod_25.a8;
7802
- const { ItemCreateError } = _ns_25;
9443
+ const _mod_13 = await import('./publishErrors-DZXDX3B6.js');
9444
+ const _ns_13 = _mod_13.ar;
9445
+ const { ItemCreateError } = _ns_13;
7803
9446
  throw new ItemCreateError(`Item.create: property ${item.modelName}.${propertyName} did not load for item ${item.seedLocalId}; value was not saved`, { code: 'property_not_loaded', modelName: item.modelName, property: propertyName, seedLocalId: item.seedLocalId });
7804
9447
  }
7805
9448
  // setValue, not the setter: the setter skips the save when the value is already in context.
@@ -7809,16 +9452,16 @@ class Item {
7809
9452
  }
7810
9453
  catch (error) {
7811
9454
  if (error instanceof ItemPropertySaveValidationError) {
7812
- const _mod_26 = await import('./errors-CnT0fSMK.js');
7813
- const _ns_26 = _mod_26.a8;
7814
- const { ItemCreateValidationError } = _ns_26;
9455
+ const _mod_14 = await import('./publishErrors-DZXDX3B6.js');
9456
+ const _ns_14 = _mod_14.ar;
9457
+ const { ItemCreateValidationError } = _ns_14;
7815
9458
  const created = new ItemCreateValidationError(item.modelName, error.validationErrors.map((e) => ({ property: propertyName, code: e.code, message: e.message })));
7816
9459
  created.cause = error;
7817
9460
  throw created;
7818
9461
  }
7819
- const _mod_27 = await import('./errors-CnT0fSMK.js');
7820
- const _ns_27 = _mod_27.a8;
7821
- const { ItemCreateError } = _ns_27;
9462
+ const _mod_15 = await import('./publishErrors-DZXDX3B6.js');
9463
+ const _ns_15 = _mod_15.ar;
9464
+ const { ItemCreateError } = _ns_15;
7822
9465
  throw new ItemCreateError(`Item.create: failed to save ${item.modelName}.${propertyName} for item ${item.seedLocalId}: ` +
7823
9466
  (error instanceof Error ? error.message : String(error)), { code: 'save_failed', modelName: item.modelName, property: propertyName, seedLocalId: item.seedLocalId, cause: error });
7824
9467
  }
@@ -7994,9 +9637,9 @@ class Item {
7994
9637
  if (!appDb || Item.cache.size === 0) {
7995
9638
  return;
7996
9639
  }
7997
- const _mod_28 = await import('./index-BjWk0x4-.js');
7998
- const _ns_28 = _mod_28.s;
7999
- const { metadata } = _ns_28;
9640
+ const _mod_16 = await import('./index-2PaIhvOU.js');
9641
+ const _ns_16 = _mod_16.s;
9642
+ const { metadata } = _ns_16;
8000
9643
  const { eq, and } = await import('drizzle-orm');
8001
9644
  await Promise.all(Item.cache.instances().map(async (instance) => {
8002
9645
  try {
@@ -8106,11 +9749,11 @@ class Item {
8106
9749
  }
8107
9750
  /** Throws ItemCreateValidationError for unknown keys and values that break their property's rules. */
8108
9751
  static async assertValidCreateValues(props, propertyNames, dataTypeByPropertyName, skipKeys) {
8109
- const _mod_29 = await import('./errors-CnT0fSMK.js');
8110
- const _ns_29 = _mod_29.a8;
8111
- const { ItemCreateValidationError } = _ns_29;
8112
- const { getModelPropertyDefinitions, resolvePropertyKey } = await import('./property-iNW1N3vm.js');
8113
- const { SchemaValidationService } = await import('./SchemaValidationService-CieGrj5c.js');
9752
+ const _mod_17 = await import('./publishErrors-DZXDX3B6.js');
9753
+ const _ns_17 = _mod_17.ar;
9754
+ const { ItemCreateValidationError } = _ns_17;
9755
+ const { getModelPropertyDefinitions, resolvePropertyKey } = await import('./property-4jSqHieb.js');
9756
+ const { SchemaValidationService } = await import('./SchemaValidationService-Dc-lpFQX.js');
8114
9757
  const validationService = new SchemaValidationService();
8115
9758
  const modelName = props.modelName;
8116
9759
  const known = new Set(propertyNames);
@@ -8464,9 +10107,9 @@ class Item {
8464
10107
  itemActor.send(event);
8465
10108
  };
8466
10109
  try {
8467
- const _mod_30 = await import('./index-BjWk0x4-.js');
8468
- const _ns_30 = _mod_30.s;
8469
- const seedSchemaMod = _ns_30;
10110
+ const _mod_18 = await import('./index-2PaIhvOU.js');
10111
+ const _ns_18 = _mod_18.s;
10112
+ const seedSchemaMod = _ns_18;
8470
10113
  const { seeds, metadata } = seedSchemaMod;
8471
10114
  const drizzleMod = await import('drizzle-orm');
8472
10115
  const { eq, and } = drizzleMod;
@@ -8478,9 +10121,7 @@ class Item {
8478
10121
  return;
8479
10122
  }
8480
10123
  // Query initial seed and version data
8481
- const _mod_31 = await import('./tabEvents-DT5ez6io.js');
8482
- const _ns_31 = _mod_31.bg;
8483
- const { findSeedRow } = _ns_31;
10124
+ const { findSeedRow } = await Promise.resolve().then(function () { return findSeedRow$1; });
8484
10125
  const seed = await findSeedRow({ localId: seedLocalId, uid: seedUid }, db);
8485
10126
  if (!seed) {
8486
10127
  logger(`[Item._setupLiveQuerySubscription] Seed not found for seedLocalId: ${seedLocalId}`);
@@ -8779,7 +10420,7 @@ class Item {
8779
10420
  return;
8780
10421
  }
8781
10422
  // Through the outbox (ADR 0019): after the item's pending edits, not before them
8782
- const { appendOutboxOpAndWait, startItemOutbox, throwItemWriteError } = await import('./index-CZMWKXLM.js');
10423
+ const { appendOutboxOpAndWait, startItemOutbox, throwItemWriteError } = await import('./index-BXTyvnKb.js');
8783
10424
  startItemOutbox();
8784
10425
  await appendOutboxOpAndWait({
8785
10426
  seedLocalId: context.seedLocalId,
@@ -8806,5 +10447,5 @@ var Item$1 = /*#__PURE__*/Object.freeze({
8806
10447
  Item: Item
8807
10448
  });
8808
10449
 
8809
- export { runEntityLoad as $, getCorrectId as A, isItemOwned as B, ClearNotSupportedError as C, isPublishedSeedRef as D, propertyKeyFromStoredName as E, currentPropertyRows as F, metadataPropertyGroupKeys as G, compareMetadataRowsLatestFirst as H, Item as I, readMetadataPropertiesById as J, isPublishedMetadataRow as K, getLatestPublishedVersionRow as L, isVersionRevoked as M, readModelMetadataProperties as N, OUTBOX_DEFER_MS as O, metadataPropertyNameForms as P, currentPropertyRow as Q, getLatestVersionRow as R, parseListPropertyValueFromStorage as S, applyContextUpdate as T, isInternalUpdate as U, evictionLog as V, holdChild as W, isEntityTornDown as X, sameEntity as Y, DEFAULT_READY_TIMEOUT_MS as Z, createReactiveProxy as _, appendOutboxOpAndWait as a, createSeed as a$, waitForEntityReady as a0, evictEntities as a1, EntityUnloadedError as a2, entityStatus as a3, releaseEntity as a4, destroyEntity as a5, EntityCache as a6, getGetAdditionalSyncAddresses as a7, modelPropertiesToObject as a8, listRelationEasPropertyName as a9, getPublishExecutor as aA, getRevokeExecutor as aB, getUploadExecutor as aC, getVersionData as aD, isLocalUnsealedDraft as aE, isSeedRowOwned as aF, localUnsealedDraftSql as aG, ownedPublisherSql as aH, ownedSeedsSql as aI, pickLatestMetadataRowPerProperty as aJ, placeholderUidSql as aK, propertyMachine as aL, publisherInAddressListSql as aM, publisherIsInOwnedSet as aN, setAdditionalSyncAddresses as aO, setGetPublisherForNewSeeds as aP, setPublishExecutor as aQ, setRevokeExecutor as aR, setUploadExecutor as aS, getSeedPublisher as aT, deleteStagedFile as aU, storageFileName as aV, STORAGE_SUBDIR as aW, resolveNewSeedFields as aX, lookupPropertySchemaUid as aY, stageStorageContent as aZ, requireStagedFile as a_, easPropertyNameForPublish as aa, metadataStorageName as ab, resolveMetadataPropertyId as ac, storageSeedTransactionId as ad, storageUploadPath as ae, matchMetadataProperty as af, metadataRowsOfProperty as ag, METADATA_LATEST_FIRST_ORDER_SQL as ah, getEasSchemaUidForModel as ai, isStorageSeedRef as aj, isActorStopped as ak, differsFromOriginals as al, onEntityTeardown as am, getStorageTransactionIdForSeedUid as an, latestVersionOrder as ao, otherVersionRowsWithUid as ap, mergeVersionRowInto as aq, findMetadataPropertyByName as ar, storedValueKind as as, resolveStorageDisplay as at, resolvePublisherFromSeedRow as au, PUBLISH_NOT_CONFIGURED_MESSAGE as av, createNewItem as aw, getArweaveUrlForTransaction as ax, getGetPublisherForNewSeeds as ay, getMetadataLatest as az, waitForPendingOutboxOps as b, createMetadata as b0, storageValueRowOf as b1, findSetConflict as b2, recordPropertyEdit as b3, setEntityUnloadGraceMs as b4, stopOutbox as b5, evictAllEntities as b6, currentPropertyRow$1 as b7, resolve as b8, conflicts as b9, propertySchemaUid as ba, Item$1 as bb, OUTBOX_MAX_ATTEMPTS as c, OUTBOX_PRUNE_AFTER_MS as d, OUTBOX_PRUNE_FAILED_AFTER_MS as e, OUTBOX_RETRY_DELAYS_MS as f, getOutboxOps as g, OutboxOpDeferredError as h, OutboxOpFailedError as i, OutboxOpRejectedError as j, appendOutboxOp as k, drainOutbox as l, markPropertyConflictsResolved as m, getItemConflicts as n, outboxOpResult as o, getPendingSetValue as p, getPropertyConflict as q, outboxOpValue as r, pruneOutbox as s, registerOutboxApplier as t, startOutbox as u, ItemProperty as v, waitForOutboxOp as w, ItemPropertySaveValidationError as x, OwnershipError as y, assertItemOwned as z };
8810
- //# sourceMappingURL=Item-oPnmIehk.js.map
10450
+ export { entityStatus as $, AmbiguousModelError as A, metadataPropertyGroupKeys as B, ClearNotSupportedError as C, compareMetadataRowsLatestFirst as D, EntityUnloadedError as E, readMetadataPropertiesById as F, isPublishedMetadataRow as G, getLatestPublishedVersionRow as H, Item as I, writeInBatches as J, getItemStorageSubdir as K, getAllAddressesFromDb as L, whenDbReady as M, applyContextUpdate as N, OUTBOX_PRUNE_AFTER_MS as O, isInternalUpdate as P, evictionLog as Q, addSchemaToDb as R, StagedValueMissingError as S, holdChild as T, isEntityTornDown as U, sameEntity as V, DEFAULT_READY_TIMEOUT_MS as W, createReactiveProxy as X, runEntityLoad as Y, waitForEntityReady as Z, evictEntities as _, appendOutboxOpAndWait as a, normalizeAddressConfig as a$, inDefinitionsTransaction as a0, releaseEntity as a1, destroyEntity as a2, isClientReady as a3, upsertSchemaRow as a4, broadcastSchemasInvalidate as a5, EntityCache as a6, normalizePublisher as a7, chunkValues as a8, rowsPerInsert as a9, addCachedPropertyRefresher as aA, getStorageTransactionIdForSeedUid as aB, latestVersionOrder as aC, otherVersionRowsWithUid as aD, mergeVersionRowInto as aE, isVersionRevoked as aF, findMetadataPropertyByName as aG, readModelMetadataProperties as aH, parseListPropertyValueFromStorage as aI, findSeedRow as aJ, resolvePublisherFromSeedRow as aK, ClientManagerState as aL, MachineIds as aM, PUBLISH_NOT_CONFIGURED_MESSAGE as aN, createNewItem as aO, getAddressesForItemsFilter as aP, getArweaveUrlForTransaction as aQ, getGetPublisherForNewSeeds as aR, getMetadataLatest as aS, getPublishExecutor as aT, getRevokeExecutor as aU, getUploadExecutor as aV, getVersionData as aW, isClientReadySnapshot as aX, isLocalUnsealedDraft as aY, isSeedRowOwned as aZ, localUnsealedDraftSql as a_, loadNameMatchLevels as aa, getGetAdditionalSyncAddresses as ab, findSeedRowsByUid as ac, normalizeHexAddress as ad, selectInBatches as ae, nameMatchLevels as af, modelPropertiesToObject as ag, listRelationEasPropertyName as ah, easPropertyNameForPublish as ai, metadataStorageName as aj, resolveMetadataPropertyId as ak, getItemStoragePropertiesForModel as al, resolveItemModelFileId as am, matchMetadataProperty as an, metadataRowsOfProperty as ao, METADATA_LATEST_FIRST_ORDER_SQL as ap, getEasSchemaUidForModel as aq, isStorageSeedRef as ar, isActorStopped as as, differsFromOriginals as at, resolveModelRecord as au, definitionOnlyFields as av, persistDefinitionFields as aw, onEntityTeardown as ax, getPropertyDefinition as ay, propertyDefinitionFromRow as az, waitForPendingOutboxOps as b, normalizeAddressList as b0, ownedPublisherSql as b1, ownedSeedsSql as b2, pickLatestMetadataRowPerProperty as b3, placeholderUidSql as b4, propertyMachine as b5, publisherInAddressListSql as b6, publisherIsInOwnedSet as b7, setAdditionalSyncAddresses as b8, setGetPublisherForNewSeeds as b9, getWatchedAddressesFromDb as bA, getOwnedAddressesFromDb as bB, setEntityUnloadGraceMs as bC, stopOutbox as bD, evictAllEntities as bE, registerClientActor as bF, getPropertyDefinitions as bG, metadataPropertyNameForms as bH, currentPropertyRow as bI, resolveModelRecordByNameOrType as bJ, storageSeedTransactionId as bK, storageUploadPath as bL, storedValueKind as bM, resolveStorageDisplay as bN, findSeedRow$1 as bO, resolveModelRecord$1 as bP, propertyDefinition as bQ, definitions as bR, db as bS, currentPropertyRow$1 as bT, propertySchemaUid as bU, findFile as bV, resolve as bW, conflicts as bX, Item$1 as bY, setPublishExecutor as ba, setRevokeExecutor as bb, setUploadExecutor as bc, writeModelToDb as bd, writePropertyToDb as be, currentNameMatchLevels as bf, renameModelInDb as bg, insertModelStubs as bh, warnOnInvalidNameMatch as bi, refreshCachedProperties as bj, resolveMetadataProperty as bk, lookupPropertySchemaUid as bl, getItemStorageLocalFileName as bm, deleteStagedFile as bn, storageFileName as bo, STORAGE_SUBDIR as bp, resolveNewSeedFields as bq, stageStorageContent as br, requireStagedFile as bs, findSetConflict as bt, recordPropertyEdit as bu, startOutbox as bv, createSeed as bw, storageValueRowOf as bx, getLatestVersionRow as by, ClientManagerEvents as bz, OUTBOX_PRUNE_FAILED_AFTER_MS as c, OutboxOpDeferredError as d, OutboxOpFailedError as e, OutboxOpRejectedError as f, getOutboxOps as g, appendOutboxOp as h, drainOutbox as i, getPendingSetValue as j, outboxOpValue as k, ItemProperty as l, markPropertyConflictsResolved as m, ItemPropertySaveValidationError as n, outboxOpResult as o, pruneOutbox as p, OwnershipError as q, registerOutboxApplier as r, assertItemOwned as s, getCorrectId as t, isItemOwned as u, isPublishedSeedRef as v, waitForOutboxOp as w, propertyKeyFromStoredName as x, findFileInDir as y, currentPropertyRows as z };
10451
+ //# sourceMappingURL=Item-BU9wIT7l.js.map