@seedprotocol/sdk 0.7.9 → 0.7.10-next.1353.g74aeeed

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 (136) hide show
  1. package/README.md +1 -1
  2. package/dist/{BaseFileManager-BAlySv8w.js → BaseFileManager-DLou1TZS.js} +5 -1
  3. package/dist/BaseFileManager-DLou1TZS.js.map +1 -0
  4. package/dist/{Item-Bf4kWKpH.js → Item-oPnmIehk.js} +571 -346
  5. package/dist/Item-oPnmIehk.js.map +1 -0
  6. package/dist/{ModelProperty-BlaEgb_U.js → ModelProperty-D2vJozmW.js} +95 -62
  7. package/dist/ModelProperty-D2vJozmW.js.map +1 -0
  8. package/dist/{Schema-J_1f6p0c.js → Schema-BMXRF4j9.js} +88 -52
  9. package/dist/Schema-BMXRF4j9.js.map +1 -0
  10. package/dist/{SchemaValidationService-CnmKyzR-.js → SchemaValidationService-CieGrj5c.js} +5 -5
  11. package/dist/{SchemaValidationService-CnmKyzR-.js.map → SchemaValidationService-CieGrj5c.js.map} +1 -1
  12. package/dist/{errors-C1qUEZ7A.js → browserSupport-C64rX-2I.js} +38 -2
  13. package/dist/browserSupport-C64rX-2I.js.map +1 -0
  14. package/dist/{configurePlatform-CiOJ1R3q.js → configurePlatform-WkmvqeTO.js} +2 -2
  15. package/dist/{configurePlatform-CiOJ1R3q.js.map → configurePlatform-WkmvqeTO.js.map} +1 -1
  16. package/dist/{detachRowsBeforeDelete-sEaUwlOU.js → detachRowsBeforeDelete-BZr_0iZU.js} +2 -2
  17. package/dist/{detachRowsBeforeDelete-sEaUwlOU.js.map → detachRowsBeforeDelete-BZr_0iZU.js.map} +1 -1
  18. package/dist/dist/{BaseFileManager-BAlySv8w.d.ts → BaseFileManager-DLou1TZS.d.ts} +3 -1
  19. package/dist/dist/BaseFileManager-DLou1TZS.d.ts.map +1 -0
  20. package/dist/dist/{configurePlatform-CiOJ1R3q.d.ts → configurePlatform-WkmvqeTO.d.ts} +1 -1
  21. package/dist/dist/configurePlatform-WkmvqeTO.d.ts.map +1 -0
  22. package/dist/dist/{index-D0V-mjCV.d.ts → index-BjWk0x4-.d.ts} +1 -1
  23. package/dist/dist/{index-D0V-mjCV.d.ts.map → index-BjWk0x4-.d.ts.map} +1 -1
  24. package/dist/dist/{index.node-CYPemH-t.d.ts → index.node-BfiBriEq.d.ts} +7 -1
  25. package/dist/dist/{index.node-CYPemH-t.d.ts.map → index.node-BfiBriEq.d.ts.map} +1 -1
  26. package/dist/dist/platform.node.d.ts +2 -2
  27. package/dist/{eas-wB_fQ8Tc.js → eas-FZPPqxqe.js} +5 -5
  28. package/dist/{eas-wB_fQ8Tc.js.map → eas-FZPPqxqe.js.map} +1 -1
  29. package/dist/{errors-DLSoqSHG.js → errors-CnT0fSMK.js} +185 -97
  30. package/dist/errors-CnT0fSMK.js.map +1 -0
  31. package/dist/{getPublishPayload-BnIwpwz1.js → getPublishPayload-DKuEaq96.js} +8 -8
  32. package/dist/{getPublishPayload-BnIwpwz1.js.map → getPublishPayload-DKuEaq96.js.map} +1 -1
  33. package/dist/{getPublishPendingDiff-D6_-A5ns.js → getPublishPendingDiff-CH5xLMjU.js} +7 -7
  34. package/dist/{getPublishPendingDiff-D6_-A5ns.js.map → getPublishPendingDiff-CH5xLMjU.js.map} +1 -1
  35. package/dist/{getPublishUploads-sS6JfPgz.js → getPublishUploads-DivYpgNw.js} +7 -7
  36. package/dist/{getPublishUploads-sS6JfPgz.js.map → getPublishUploads-DivYpgNw.js.map} +1 -1
  37. package/dist/{index-D0V-mjCV.js → index-BjWk0x4-.js} +2 -2
  38. package/dist/index-BjWk0x4-.js.map +1 -0
  39. package/dist/{index-CYGWPoOx.js → index-CZMWKXLM.js} +7 -7
  40. package/dist/index-CZMWKXLM.js.map +1 -0
  41. package/dist/{index-BVBmz_cc.js → index-DXRhFg1N.js} +7 -7
  42. package/dist/index-DXRhFg1N.js.map +1 -0
  43. package/dist/{index-DmTtj1nk.js → index-Gh42_47r.js} +402 -20
  44. package/dist/index-Gh42_47r.js.map +1 -0
  45. package/dist/{index.browser-CUmBVYen.js → index.browser-DQstVvRV.js} +46 -10
  46. package/dist/index.browser-DQstVvRV.js.map +1 -0
  47. package/dist/{index.node-CYPemH-t.js → index.node-BfiBriEq.js} +31 -4
  48. package/dist/index.node-BfiBriEq.js.map +1 -0
  49. package/dist/main.js +17 -16
  50. package/dist/main.js.map +1 -1
  51. package/dist/{modelPropertyRecordSchemas-C7UgA5TH.js → modelPropertyRecordSchemas-DEe-lYhn.js} +5 -5
  52. package/dist/{modelPropertyRecordSchemas-C7UgA5TH.js.map → modelPropertyRecordSchemas-DEe-lYhn.js.map} +1 -1
  53. package/dist/node.js +8 -8
  54. package/dist/platform.browser.js +6 -6
  55. package/dist/platform.node.js +4 -4
  56. package/dist/{property-BxUYznH0.js → property-iNW1N3vm.js} +8 -8
  57. package/dist/{property-BxUYznH0.js.map → property-iNW1N3vm.js.map} +1 -1
  58. package/dist/{publishGraph-EcB40Uii.js → publishGraph-CRn_N3NT.js} +5 -5
  59. package/dist/{publishGraph-EcB40Uii.js.map → publishGraph-CRn_N3NT.js.map} +1 -1
  60. package/dist/{readHtmlBodyForStorageSeed-D6KPtWoR.js → readHtmlBodyForStorageSeed-B7HgPmq6.js} +10 -10
  61. package/dist/{readHtmlBodyForStorageSeed-D6KPtWoR.js.map → readHtmlBodyForStorageSeed-B7HgPmq6.js.map} +1 -1
  62. package/dist/src/Item/Item.d.ts +26 -5
  63. package/dist/src/Item/Item.d.ts.map +1 -1
  64. package/dist/src/ItemProperty/ItemProperty.d.ts +20 -5
  65. package/dist/src/ItemProperty/ItemProperty.d.ts.map +1 -1
  66. package/dist/src/ItemProperty/loadProperty.d.ts.map +1 -1
  67. package/dist/src/ItemProperty/propertyWrite.d.ts.map +1 -1
  68. package/dist/src/ItemProperty/service/actors/hydrateFromDb.d.ts.map +1 -1
  69. package/dist/src/Model/Model.d.ts +45 -34
  70. package/dist/src/Model/Model.d.ts.map +1 -1
  71. package/dist/src/ModelProperty/ModelProperty.d.ts +31 -13
  72. package/dist/src/ModelProperty/ModelProperty.d.ts.map +1 -1
  73. package/dist/src/Schema/Schema.d.ts +36 -27
  74. package/dist/src/Schema/Schema.d.ts.map +1 -1
  75. package/dist/src/archive/exportArchive.d.ts +57 -0
  76. package/dist/src/archive/exportArchive.d.ts.map +1 -0
  77. package/dist/src/browser/db/Db.d.ts +5 -0
  78. package/dist/src/browser/db/Db.d.ts.map +1 -1
  79. package/dist/src/browser/helpers/FileManager.d.ts.map +1 -1
  80. package/dist/src/browser/helpers/tolerantWebAccess.d.ts +12 -1
  81. package/dist/src/browser/helpers/tolerantWebAccess.d.ts.map +1 -1
  82. package/dist/src/client/ClientManager.d.ts +14 -4
  83. package/dist/src/client/ClientManager.d.ts.map +1 -1
  84. package/dist/src/db/Db/BaseDb.d.ts +2 -0
  85. package/dist/src/db/Db/BaseDb.d.ts.map +1 -1
  86. package/dist/src/helpers/FileManager/errors.d.ts +11 -0
  87. package/dist/src/helpers/FileManager/errors.d.ts.map +1 -1
  88. package/dist/src/helpers/browserSupport.d.ts +23 -0
  89. package/dist/src/helpers/browserSupport.d.ts.map +1 -0
  90. package/dist/src/helpers/cachedPropertyRefresh.d.ts +6 -6
  91. package/dist/src/helpers/cachedPropertyRefresh.d.ts.map +1 -1
  92. package/dist/src/helpers/entity/entityCache.d.ts +45 -3
  93. package/dist/src/helpers/entity/entityCache.d.ts.map +1 -1
  94. package/dist/src/helpers/entity/entityLifecycle.d.ts +21 -3
  95. package/dist/src/helpers/entity/entityLifecycle.d.ts.map +1 -1
  96. package/dist/src/helpers/propertyDefinition.d.ts +2 -0
  97. package/dist/src/helpers/propertyDefinition.d.ts.map +1 -1
  98. package/dist/src/helpers/schemaApply.d.ts.map +1 -1
  99. package/dist/src/helpers/tabCoordinator.d.ts +9 -2
  100. package/dist/src/helpers/tabCoordinator.d.ts.map +1 -1
  101. package/dist/src/index.d.ts +4 -1
  102. package/dist/src/index.d.ts.map +1 -1
  103. package/dist/src/interfaces/IDb.d.ts +6 -0
  104. package/dist/src/interfaces/IDb.d.ts.map +1 -1
  105. package/dist/src/node/db/Db.d.ts +6 -0
  106. package/dist/src/node/db/Db.d.ts.map +1 -1
  107. package/dist/src/outbox/conflicts.d.ts.map +1 -1
  108. package/dist/src/outbox/outbox.d.ts +6 -0
  109. package/dist/src/outbox/outbox.d.ts.map +1 -1
  110. package/dist/src/types/index.d.ts +16 -0
  111. package/dist/src/types/index.d.ts.map +1 -1
  112. package/dist/{syncDbWithEas-CA7PvBgu.js → syncDbWithEas-FuyqoEKk.js} +14 -14
  113. package/dist/{syncDbWithEas-CA7PvBgu.js.map → syncDbWithEas-FuyqoEKk.js.map} +1 -1
  114. package/dist/{tabEvents-CaVmngqr.js → tabEvents-DT5ez6io.js} +38 -12
  115. package/dist/tabEvents-DT5ez6io.js.map +1 -0
  116. package/dist/{tolerantWebAccess-BJqViq08.js → tolerantWebAccess-zEseJ2zT.js} +53 -13
  117. package/dist/tolerantWebAccess-zEseJ2zT.js.map +1 -0
  118. package/dist/{updateSeedRevokedAt-BL9Bg-G7.js → updateSeedRevokedAt-B9JRwr5T.js} +3 -3
  119. package/dist/{updateSeedRevokedAt-BL9Bg-G7.js.map → updateSeedRevokedAt-B9JRwr5T.js.map} +1 -1
  120. package/package.json +10 -12
  121. package/dist/BaseFileManager-BAlySv8w.js.map +0 -1
  122. package/dist/Item-Bf4kWKpH.js.map +0 -1
  123. package/dist/ModelProperty-BlaEgb_U.js.map +0 -1
  124. package/dist/Schema-J_1f6p0c.js.map +0 -1
  125. package/dist/dist/BaseFileManager-BAlySv8w.d.ts.map +0 -1
  126. package/dist/dist/configurePlatform-CiOJ1R3q.d.ts.map +0 -1
  127. package/dist/errors-C1qUEZ7A.js.map +0 -1
  128. package/dist/errors-DLSoqSHG.js.map +0 -1
  129. package/dist/index-BVBmz_cc.js.map +0 -1
  130. package/dist/index-CYGWPoOx.js.map +0 -1
  131. package/dist/index-D0V-mjCV.js.map +0 -1
  132. package/dist/index-DmTtj1nk.js.map +0 -1
  133. package/dist/index.browser-CUmBVYen.js.map +0 -1
  134. package/dist/index.node-CYPemH-t.js.map +0 -1
  135. package/dist/tabEvents-CaVmngqr.js.map +0 -1
  136. package/dist/tolerantWebAccess-BJqViq08.js.map +0 -1
@@ -1,10 +1,10 @@
1
- import { a_ 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, a$ as TAB_ID, aM as onTabMessage, ax as whenLeaderTab, ap as isLeaderTab, aL as postTabMessage, j as findSeedRow, b0 as getModelSchemaName, K as selectInBatches, aX 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, b1 as normalizeMimeType, aA as sniffMimeType, Y as getItemStorageSubdir, ad as ImageSize, b2 as findFileInDir, b3 as getStorageSubdirForDataType, g as normalizePropertyRecordSchema, h as eventEmitter, b4 as timingMarks, a4 as getPropertyDefinition, aT as tryWithTabLock, b5 as withTabLock, M as ModelPropertyDataTypes, aC as createSessionObjectUrl, U as INTERNAL_STORAGE_MODEL_FILE_IDS, b6 as revokeSessionObjectUrl, aw as whenDbReady, b7 as timed, av as watchEntityRows, a5 as propertyDefinitionFromRow, b8 as isSessionObjectUrl, x as normalizePublisher, _ as getItemStorageLocalFileName, b9 as timingNow, ba as timingSince, bb as timingLogger, bc as timedDb } from './tabEvents-CaVmngqr.js';
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
2
  import { fromCallback, setup, assign, and as and$1, stateIn, createActor, waitFor, isMachineSnapshot } from 'xstate';
3
3
  import debug from 'debug';
4
4
  import pluralize from 'pluralize';
5
5
  import { easName, isValidEasAttestationUid, isPlaceholderUid, getEasSchemaUidForSchemaDefinition, getSchemaUidForModelFromCache, setSchemaUidForModel, cachedSchemaLookup, getEasModelSchemasForNames, getEasSchemaUidForExactDefinition } from '@seedprotocol/eas';
6
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-BAlySv8w.js';
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';
8
8
  import { BehaviorSubject } from 'rxjs';
9
9
  import { orderBy } from 'lodash-es';
10
10
  import { normalizeRelationPropertyValue, resolveSeedIdsFromRefString, BaseArweaveClient } from '@seedprotocol/arweave';
@@ -496,7 +496,7 @@ const runPublish = fromCallback(({ sendBack, input: { context, item: itemFromEve
496
496
  // item.publish() sends its own instance; look it up only if startPublish came without one.
497
497
  const loaded = itemFromEvent
498
498
  ? undefined
499
- : (await (await Promise.resolve().then(function () { return Item$1; })).Item.find({ seedLocalId }));
499
+ : (await (await Promise.resolve().then(function () { return Item$1; })).Item.find({ seedLocalId, hold: true }));
500
500
  const item = itemFromEvent ?? loaded;
501
501
  if (!item) {
502
502
  sendBack({ type: 'publishError', error: new Error(`Item not found: ${seedLocalId}`) });
@@ -1078,6 +1078,9 @@ function outboxOpResult(op) {
1078
1078
  // --- Draining ---------------------------------------------------------------------------------
1079
1079
  let started = false;
1080
1080
  let paused = false;
1081
+ /** Set by stopOutbox (client.stop) until the next startOutbox. */
1082
+ let stopped = false;
1083
+ let stopTabMessages;
1081
1084
  let draining;
1082
1085
  let drainAgain = false;
1083
1086
  let retryTimer;
@@ -1091,7 +1094,8 @@ function startOutbox() {
1091
1094
  if (started)
1092
1095
  return;
1093
1096
  started = true;
1094
- onTabMessage((message) => {
1097
+ stopped = false;
1098
+ stopTabMessages = onTabMessage((message) => {
1095
1099
  if (message.type === 'outbox-nudge' && isLeaderTab())
1096
1100
  scheduleDrain();
1097
1101
  if (message.type === 'outbox-settled')
@@ -1101,7 +1105,7 @@ function startOutbox() {
1101
1105
  }
1102
1106
  /** Drains soon in this tab (if it leads); calls made while a drain runs make it run once more. */
1103
1107
  function scheduleDrain() {
1104
- if (!isLeaderTab())
1108
+ if (stopped || !isLeaderTab())
1105
1109
  return;
1106
1110
  if (draining) {
1107
1111
  drainAgain = true;
@@ -1112,7 +1116,7 @@ function scheduleDrain() {
1112
1116
  do {
1113
1117
  drainAgain = false;
1114
1118
  await drainOutbox();
1115
- } while (drainAgain && !paused);
1119
+ } while (drainAgain && !paused && !stopped);
1116
1120
  }
1117
1121
  catch (error) {
1118
1122
  logger$8('drain failed', error);
@@ -1455,6 +1459,24 @@ async function getOutboxOps(opIds) {
1455
1459
  return [];
1456
1460
  return BaseDb.getAppDb().select().from(pendingWrites).where(inArray(pendingWrites.opId, opIds));
1457
1461
  }
1462
+ /**
1463
+ * Stops draining in this tab (client.stop): no new drain starts, retries are dropped, and this
1464
+ * resolves once a drain that was running has finished. Ops stay pending in the database; the next
1465
+ * startOutbox (any write after init) resumes them, as after a reload.
1466
+ */
1467
+ async function stopOutbox() {
1468
+ started = false;
1469
+ stopped = true;
1470
+ stopTabMessages?.();
1471
+ stopTabMessages = undefined;
1472
+ if (retryTimer !== undefined) {
1473
+ clearTimeout(retryTimer);
1474
+ retryTimer = undefined;
1475
+ }
1476
+ notBefore.clear();
1477
+ while (draining)
1478
+ await draining;
1479
+ }
1458
1480
 
1459
1481
  /** A storage op's row fields, from either op shape. */
1460
1482
  function storageValueRowOf(value) {
@@ -2213,8 +2235,8 @@ const getItemsData = async ({ modelName, modelFileId, schemaName, deleted, inclu
2213
2235
  conditions.push(eq(seeds.type, easName(modelName)));
2214
2236
  }
2215
2237
  if (!modelFileId && schemaName && modelName) {
2216
- const _mod_0 = await import('./tabEvents-CaVmngqr.js');
2217
- const _ns_0 = _mod_0.be;
2238
+ const _mod_0 = await import('./tabEvents-DT5ez6io.js');
2239
+ const _ns_0 = _mod_0.bh;
2218
2240
  const { resolveModelRecord } = _ns_0;
2219
2241
  modelFileId = (await resolveModelRecord(modelName, { schemaName }))?.schemaFileId ?? undefined;
2220
2242
  if (!modelFileId)
@@ -2469,7 +2491,7 @@ const resolveRelatedValue = fromCallback(({ sendBack, input: { context } }) => {
2469
2491
  const { isRelation, propertyValue, propertyName, propertyRecordSchema, populatedFromDb, } = context;
2470
2492
  const _resolveRelatedValue = async () => {
2471
2493
  // Use dynamic import to break circular dependency
2472
- const schemaMod = await import('./index-BVBmz_cc.js');
2494
+ const schemaMod = await import('./index-DXRhFg1N.js');
2473
2495
  const { ModelPropertyDataTypes } = schemaMod;
2474
2496
  if (!propertyValue || !isRelation || populatedFromDb) {
2475
2497
  return;
@@ -2983,7 +3005,7 @@ async function resolveStorageRef(kind, row) {
2983
3005
  }
2984
3006
  if (kind === 'html') {
2985
3007
  if (!out.refResolvedValue && out.propertyValue) {
2986
- const { resolveHtmlStorageSeedLocalId } = await import('./readHtmlBodyForStorageSeed-D6KPtWoR.js');
3008
+ const { resolveHtmlStorageSeedLocalId } = await import('./readHtmlBodyForStorageSeed-B7HgPmq6.js');
2987
3009
  const localId = await resolveHtmlStorageSeedLocalId(out.propertyValue);
2988
3010
  if (localId)
2989
3011
  out.refResolvedValue = `${localId}.html`;
@@ -3032,7 +3054,7 @@ async function resolveStorageDisplay(kind, row, options = {}) {
3032
3054
  }
3033
3055
  }
3034
3056
  if (!renderValue && options.fetch && ref.propertyValue) {
3035
- const { readHtmlBodyForStorageSeedPropertyValue } = await import('./readHtmlBodyForStorageSeed-D6KPtWoR.js');
3057
+ const { readHtmlBodyForStorageSeedPropertyValue } = await import('./readHtmlBodyForStorageSeed-B7HgPmq6.js');
3036
3058
  renderValue = await readHtmlBodyForStorageSeedPropertyValue(ref.propertyValue);
3037
3059
  }
3038
3060
  return { ref, filePath, renderValue };
@@ -3044,15 +3066,15 @@ async function resolveStorageDisplay(kind, row, options = {}) {
3044
3066
  : BaseFileManager.pathExists(BaseFileManager.getFilesPath(dir, name)).then((exists) => exists ? BaseFileManager.getFilesPath(dir, name) : undefined);
3045
3067
  // ensureImageLocal also makes the smallest size variant when the original is already there
3046
3068
  if (kind === 'image' && options.fetch) {
3047
- const _mod_1 = await import('./tabEvents-CaVmngqr.js');
3048
- const _ns_1 = _mod_1.bl;
3069
+ const _mod_1 = await import('./tabEvents-DT5ez6io.js');
3070
+ const _ns_1 = _mod_1.bo;
3049
3071
  const { ensureImageLocal } = _ns_1;
3050
3072
  await ensureImageLocal({ transactionId: name, fileName: name, widths: [ImageSize.EXTRA_SMALL] });
3051
3073
  }
3052
3074
  let filePath = await find();
3053
3075
  if (!filePath && kind === 'file' && options.fetch) {
3054
- const _mod_2 = await import('./tabEvents-CaVmngqr.js');
3055
- const _ns_2 = _mod_2.bj;
3076
+ const _mod_2 = await import('./tabEvents-DT5ez6io.js');
3077
+ const _ns_2 = _mod_2.bm;
3056
3078
  const { downloadTransactionIdWithDedupe } = _ns_2;
3057
3079
  await downloadTransactionIdWithDedupe(name);
3058
3080
  filePath = await find();
@@ -3086,7 +3108,7 @@ const hydrateFromDb = fromCallback(({ sendBack, input: { context } }) => {
3086
3108
  }
3087
3109
  const _hydrateFromDb = async () => {
3088
3110
  const normalizedDataType = normalizeDataType(propertyRecordSchema?.dataType);
3089
- const row = await currentPropertyRow({ seedLocalId, seedUid }, {
3111
+ const read = () => currentPropertyRow({ seedLocalId, seedUid }, {
3090
3112
  propertyId: context.propertyId,
3091
3113
  propertyName,
3092
3114
  modelName,
@@ -3094,10 +3116,15 @@ const hydrateFromDb = fromCallback(({ sendBack, input: { context } }) => {
3094
3116
  dataType: normalizedDataType,
3095
3117
  refValueType: normalizeDataType(propertyRecordSchema?.refValueType),
3096
3118
  });
3119
+ let row = await read();
3097
3120
  if (!row)
3098
3121
  return;
3099
3122
  const { getPropertyConflict } = await Promise.resolve().then(function () { return conflicts; });
3100
3123
  const conflict = await getPropertyConflict(row.seedLocalId ?? seedLocalId, propertyName);
3124
+ // Sync records a conflict and moves its draft to newest in one transaction: a row read before
3125
+ // the conflict may be the synced one
3126
+ if (conflict)
3127
+ row = (await read()) ?? row;
3101
3128
  // Only with a definition: without one, a row's refSeedType alone doesn't decide the display here
3102
3129
  const kind = propertyRecordSchema ? storedValueKind(propertyRecordSchema) : undefined;
3103
3130
  let { refResolvedValue, localStorageDir } = row;
@@ -3480,12 +3507,12 @@ async function keepLocalDraftsOverSyncedRows(synced) {
3480
3507
  const now = Date.now();
3481
3508
  const remoteAt = remote.attestationCreatedAt ?? 0;
3482
3509
  const createdAt = Math.max(draft.createdAt ?? 0, remoteAt + 1);
3483
- // Written after the conflict: the change feed has no trigger on pending_writes, so this write
3510
+ // Written with the conflict: the change feed has no trigger on pending_writes, so this write
3484
3511
  // is what makes a loaded property re-read and pick the conflict up (ADR 0020).
3485
- const keepDraftNewest = async (recordedConflict) => {
3512
+ const keepDraftNewest = async (on, recordedConflict) => {
3486
3513
  if (createdAt === draft.createdAt && !recordedConflict)
3487
3514
  return;
3488
- await db.update(metadata).set({ createdAt }).where(eq(metadata.localId, draft.localId));
3515
+ await on.update(metadata).set({ createdAt }).where(eq(metadata.localId, draft.localId));
3489
3516
  };
3490
3517
  // The attested value the draft replaced: the newest attested row that sync didn't just write
3491
3518
  const base = candidates
@@ -3495,7 +3522,7 @@ async function keepLocalDraftsOverSyncedRows(synced) {
3495
3522
  const remoteValue = remote.propertyValue ?? null;
3496
3523
  const localValue = draft.propertyValue ?? null;
3497
3524
  if (remoteValue === localValue || remoteValue === baseValue) {
3498
- await keepDraftNewest(false);
3525
+ await keepDraftNewest(db, false);
3499
3526
  continue;
3500
3527
  }
3501
3528
  const opId = generateId();
@@ -3508,19 +3535,23 @@ async function keepLocalDraftsOverSyncedRows(synced) {
3508
3535
  remoteUid: remote.uid,
3509
3536
  opId,
3510
3537
  };
3511
- await db.insert(pendingWrites).values({
3512
- opId,
3513
- seedLocalId: draft.seedLocalId,
3514
- propertyName: draft.propertyName,
3515
- kind: 'set',
3516
- value: JSON.stringify({ newValue: localValue }),
3517
- baseRowId: draft.localId,
3518
- createdAt: now,
3519
- status: 'applied',
3520
- appliedAt: now,
3521
- conflict: JSON.stringify(conflict),
3538
+ // One transaction: a reader that sees the conflict also sees the draft as the newest row, or it
3539
+ // could show the synced value beside the conflict
3540
+ await BaseDb.transaction(async (tx) => {
3541
+ await tx.insert(pendingWrites).values({
3542
+ opId,
3543
+ seedLocalId: draft.seedLocalId,
3544
+ propertyName: draft.propertyName,
3545
+ kind: 'set',
3546
+ value: JSON.stringify({ newValue: localValue }),
3547
+ baseRowId: draft.localId,
3548
+ createdAt: now,
3549
+ status: 'applied',
3550
+ appliedAt: now,
3551
+ conflict: JSON.stringify(conflict),
3552
+ });
3553
+ await keepDraftNewest(tx, true);
3522
3554
  });
3523
- await keepDraftNewest(true);
3524
3555
  eventEmitter.emit('itemProperty.conflict', conflict);
3525
3556
  }
3526
3557
  }
@@ -3535,194 +3566,6 @@ var conflicts = /*#__PURE__*/Object.freeze({
3535
3566
  recordPropertyEdit: recordPropertyEdit
3536
3567
  });
3537
3568
 
3538
- /**
3539
- * The cache behind an entity class (ADR 0014). `peek` takes no hold; `hold`, `add({ hold: true })`
3540
- * and `holdInstance` take one each, and `release` gives one back. Entries are found by key or by
3541
- * actor, so a raw instance and its proxy find the same entry.
3542
- */
3543
- class EntityCache {
3544
- constructor() {
3545
- this.byKey = new Map();
3546
- this.byActor = new WeakMap();
3547
- }
3548
- /** Cache `instance` under `keys` (empty and duplicate keys are skipped). Returns its entry. */
3549
- add(instance, keys, options) {
3550
- const existing = this.entryOf(instance);
3551
- if (existing) {
3552
- if (options.hold)
3553
- existing.refCount += 1;
3554
- for (const key of keys)
3555
- if (key)
3556
- this.addKey(instance, key);
3557
- return existing;
3558
- }
3559
- const entry = {
3560
- instance,
3561
- refCount: options.hold ? 1 : 0,
3562
- keys: new Set(),
3563
- };
3564
- this.byActor.set(instance.getService(), entry);
3565
- for (const key of keys)
3566
- if (key)
3567
- this.addKey(instance, key);
3568
- return entry;
3569
- }
3570
- /** The cached instance under `key`, without taking a hold. */
3571
- peek(key) {
3572
- return key ? this.byKey.get(key)?.instance : undefined;
3573
- }
3574
- /** The cached instance under `key`, taking a hold the caller releases with `release`. */
3575
- hold(key) {
3576
- const entry = key ? this.byKey.get(key) : undefined;
3577
- if (!entry)
3578
- return undefined;
3579
- entry.refCount += 1;
3580
- return entry.instance;
3581
- }
3582
- /** Take a hold on a cached instance. False when it isn't cached (unloaded, evicted). */
3583
- holdInstance(instance) {
3584
- const entry = this.entryOf(instance);
3585
- if (!entry)
3586
- return false;
3587
- entry.refCount += 1;
3588
- return true;
3589
- }
3590
- /**
3591
- * Give back one hold. True when the instance should be torn down: its last hold went, or it
3592
- * isn't cached at all (evicted while the caller held it).
3593
- */
3594
- release(instance) {
3595
- const entry = this.entryOf(instance);
3596
- if (!entry)
3597
- return true;
3598
- entry.refCount -= 1;
3599
- return entry.refCount <= 0;
3600
- }
3601
- entryOf(instance) {
3602
- return this.byActor.get(instance.getService());
3603
- }
3604
- has(instance) {
3605
- return this.entryOf(instance) !== undefined;
3606
- }
3607
- /**
3608
- * Store the instance under one more key. A key held by another instance moves to this one:
3609
- * the newest instance for a name or uid wins.
3610
- */
3611
- addKey(instance, key) {
3612
- const entry = this.entryOf(instance);
3613
- if (!entry || !key)
3614
- return;
3615
- const previous = this.byKey.get(key);
3616
- if (previous === entry)
3617
- return;
3618
- previous?.keys.delete(key);
3619
- this.byKey.set(key, entry);
3620
- entry.keys.add(key);
3621
- }
3622
- removeKey(instance, key) {
3623
- const entry = this.entryOf(instance);
3624
- if (!entry || !entry.keys.has(key))
3625
- return;
3626
- entry.keys.delete(key);
3627
- if (this.byKey.get(key) === entry)
3628
- this.byKey.delete(key);
3629
- }
3630
- /** Replace every key of the instance (a rename, an id that changed). */
3631
- setKeys(instance, keys) {
3632
- const entry = this.entryOf(instance);
3633
- if (!entry)
3634
- return;
3635
- const next = new Set(keys.filter((key) => !!key));
3636
- for (const key of [...entry.keys])
3637
- if (!next.has(key))
3638
- this.removeKey(instance, key);
3639
- for (const key of next)
3640
- this.addKey(instance, key);
3641
- }
3642
- keysOf(instance) {
3643
- return [...(this.entryOf(instance)?.keys ?? [])];
3644
- }
3645
- /** Drop the instance under all its keys. Teardown (entityLifecycle) also releases its children. */
3646
- remove(instance) {
3647
- const entry = this.entryOf(instance);
3648
- if (!entry)
3649
- return undefined;
3650
- for (const key of entry.keys) {
3651
- if (this.byKey.get(key) === entry)
3652
- this.byKey.delete(key);
3653
- }
3654
- entry.keys.clear();
3655
- this.byActor.delete(instance.getService());
3656
- return entry;
3657
- }
3658
- /** Every cached instance once, optionally filtered. */
3659
- instances(filter) {
3660
- const seen = new Set();
3661
- const result = [];
3662
- for (const entry of this.byKey.values()) {
3663
- if (seen.has(entry))
3664
- continue;
3665
- seen.add(entry);
3666
- if (!filter || filter(entry.instance))
3667
- result.push(entry.instance);
3668
- }
3669
- return result;
3670
- }
3671
- get size() {
3672
- return this.instances().length;
3673
- }
3674
- }
3675
- /**
3676
- * Evictions and destroys, by scope (`schema:<name>`, `model:<id>`). Work that reads a row and then
3677
- * creates an instance from it (a lookup by id) can't be cancelled by stopping actors: capture
3678
- * `evictionLog.now()` before the read, and skip creating when `evictedSince(scopes, then)`. A scope
3679
- * also counts as evicted while its destroy runs, since its rows still exist until the delete commits.
3680
- */
3681
- const evictionLog = {
3682
- generation: 0,
3683
- last: new Map(),
3684
- running: new Map(),
3685
- now() {
3686
- return this.generation;
3687
- },
3688
- /** Record an eviction of these scopes. Call the returned function once a destroy's rows are gone. */
3689
- evict(scopes) {
3690
- this.generation += 1;
3691
- for (const scope of scopes) {
3692
- this.last.set(scope, this.generation);
3693
- this.running.set(scope, (this.running.get(scope) ?? 0) + 1);
3694
- }
3695
- let finished = false;
3696
- return () => {
3697
- if (finished)
3698
- return;
3699
- finished = true;
3700
- // Work that read a row during the destroy and checks after it must still see the eviction
3701
- this.generation += 1;
3702
- for (const scope of scopes) {
3703
- this.last.set(scope, this.generation);
3704
- const remaining = (this.running.get(scope) ?? 1) - 1;
3705
- if (remaining > 0)
3706
- this.running.set(scope, remaining);
3707
- else
3708
- this.running.delete(scope);
3709
- }
3710
- };
3711
- },
3712
- /** True when a scope is being destroyed, or was evicted after `since`. */
3713
- evictedSince(scopes, since) {
3714
- for (const scope of scopes) {
3715
- if (!scope)
3716
- continue;
3717
- if (this.running.has(scope))
3718
- return true;
3719
- if (this.generation !== since && (this.last.get(scope) ?? 0) > since)
3720
- return true;
3721
- }
3722
- return false;
3723
- },
3724
- };
3725
-
3726
3569
  /**
3727
3570
  * Default timeout for create/find/all with waitForReady. Loads that touch OPFS and the DB can take
3728
3571
  * several seconds on slow devices and CI runners; a load that is really stuck still errors.
@@ -3813,6 +3656,17 @@ async function waitForEntityReady(instance, options = {}) {
3813
3656
  throw cause instanceof Error ? cause : new EntityLoadError('Entity failed to load', { cause });
3814
3657
  }
3815
3658
  }
3659
+ /**
3660
+ * A write or publish on an instance that was unloaded (no hold kept it past the grace period,
3661
+ * or it was destroyed, evicted or dropped by another tab). Look it up again, and hold it while
3662
+ * using it.
3663
+ */
3664
+ class EntityUnloadedError extends Error {
3665
+ constructor(message) {
3666
+ super(message);
3667
+ this.name = 'EntityUnloadedError';
3668
+ }
3669
+ }
3816
3670
  /** Run `dispose` when the entity is torn down (unsubscribe a watch, revoke an object URL). */
3817
3671
  function onEntityTeardown(instance, dispose) {
3818
3672
  const lifecycle = lifecycleOf(instance);
@@ -3844,9 +3698,10 @@ function teardownEntity(instance, cache) {
3844
3698
  lifecycle.error = new EntityLoadError('Entity was unloaded before it finished loading');
3845
3699
  lifecycle.settle();
3846
3700
  }
3701
+ // At once: the parent already waited out its grace period, or was destroyed or evicted
3847
3702
  for (const release of lifecycle.children.values()) {
3848
3703
  try {
3849
- release();
3704
+ release({ immediate: true });
3850
3705
  }
3851
3706
  catch {
3852
3707
  // A child that fails to release must not keep the rest held
@@ -3871,9 +3726,12 @@ function teardownEntity(instance, cache) {
3871
3726
  // Already stopped
3872
3727
  }
3873
3728
  }
3874
- /** Give back one hold; tear the entity down when it was the last. */
3875
- function releaseEntity(instance, cache) {
3876
- if (cache.release(instance))
3729
+ /**
3730
+ * Give back one hold. With the last one the entity is torn down after the grace period (at once
3731
+ * with `immediate`, or when the grace period is 0); a hold taken meanwhile keeps it.
3732
+ */
3733
+ function releaseEntity(instance, cache, options) {
3734
+ if (cache.release(instance, options))
3877
3735
  teardownEntity(instance, cache);
3878
3736
  }
3879
3737
  /**
@@ -3886,6 +3744,14 @@ function evictEntities(cache, filter) {
3886
3744
  teardownEntity(instance, cache);
3887
3745
  return evicted;
3888
3746
  }
3747
+ /**
3748
+ * Stop and drop every cached entity of every class, whoever holds it (client.stop()). Reaches the
3749
+ * caches through EntityCache.all, so callers needn't import the entity classes.
3750
+ */
3751
+ function evictAllEntities() {
3752
+ for (const cache of EntityCache.all)
3753
+ evictEntities(cache, () => true);
3754
+ }
3889
3755
  /**
3890
3756
  * Delete an entity's rows. By default it leaves the cache first, so no lookup returns it meanwhile;
3891
3757
  * with `leaveCache: 'after'` it stays cached until the rows are gone, so a lookup that read a row
@@ -3923,7 +3789,7 @@ function holdChild(parent, child, childCache) {
3923
3789
  return true;
3924
3790
  if (!childCache.holdInstance(child))
3925
3791
  return false;
3926
- lifecycle.children.set(key, () => releaseEntity(child, childCache));
3792
+ lifecycle.children.set(key, (options) => releaseEntity(child, childCache, options));
3927
3793
  return true;
3928
3794
  }
3929
3795
  function releaseChildHold(parent, child) {
@@ -3940,6 +3806,270 @@ function heldChildActors(parent) {
3940
3806
  return [...lifecycleOf(parent).children.keys()];
3941
3807
  }
3942
3808
 
3809
+ /** Default for `SeedConfig.entityCache.unloadGraceMs`. */
3810
+ const DEFAULT_UNLOAD_GRACE_MS = 60000;
3811
+ let defaultUnloadGraceMs = DEFAULT_UNLOAD_GRACE_MS;
3812
+ let configuredUnloadGraceMs;
3813
+ /** How long an instance no one holds stays cached before it is torn down. 0: no grace period. */
3814
+ function entityUnloadGraceMs() {
3815
+ return configuredUnloadGraceMs ?? defaultUnloadGraceMs;
3816
+ }
3817
+ /** `client.init` sets the configured value; `undefined` (client stop, or none given) uses the default. */
3818
+ function setEntityUnloadGraceMs(ms) {
3819
+ configuredUnloadGraceMs = ms === undefined ? undefined : Math.max(0, ms);
3820
+ }
3821
+ /**
3822
+ * The cache behind an entity class (ADR 0014). `peek` takes no hold; `hold`, `add({ hold: true })`
3823
+ * and `holdInstance` take one each, and `release` gives one back. Entries are found by key or by
3824
+ * actor, so a raw instance and its proxy find the same entry.
3825
+ *
3826
+ * An entry with no holds (its last one released, or added or looked up without one) is torn down
3827
+ * after the grace period (`entityUnloadGraceMs`) unless a hold is taken first, which keeps the same
3828
+ * instance. With a grace period of 0, the last release tears it down at once and an entry that
3829
+ * never had a hold stays cached until it is evicted. Removing an entry (teardown, destroy,
3830
+ * eviction) cancels its timer.
3831
+ *
3832
+ * A cache made with `unloadUnheld: false` (the definition classes: Schema, Model, ModelProperty)
3833
+ * never tears an entry down for having no holds: it stays cached until it is destroyed, evicted or
3834
+ * the client stops. Holds are still counted there, and releasing the last one does nothing, except
3835
+ * an `immediate` release: its owner was destroyed or evicted (a Schema's models, a Model's
3836
+ * properties), and a child no one else holds goes with it.
3837
+ */
3838
+ class EntityCache {
3839
+ constructor(options = {}) {
3840
+ this.byKey = new Map();
3841
+ this.byActor = new WeakMap();
3842
+ this.unloadUnheld = options.unloadUnheld !== false;
3843
+ EntityCache.all.add(this);
3844
+ }
3845
+ /** Cache `instance` under `keys` (empty and duplicate keys are skipped). Returns its entry. */
3846
+ add(instance, keys, options) {
3847
+ const existing = this.entryOf(instance);
3848
+ if (existing) {
3849
+ if (options.hold)
3850
+ this.addHold(existing);
3851
+ for (const key of keys)
3852
+ if (key)
3853
+ this.addKey(instance, key);
3854
+ return existing;
3855
+ }
3856
+ const entry = {
3857
+ instance,
3858
+ refCount: options.hold ? 1 : 0,
3859
+ keys: new Set(),
3860
+ };
3861
+ this.byActor.set(instance.getService(), entry);
3862
+ for (const key of keys)
3863
+ if (key)
3864
+ this.addKey(instance, key);
3865
+ if (!options.hold)
3866
+ this.keepUnheld(instance);
3867
+ return entry;
3868
+ }
3869
+ /** The cached instance under `key`, without taking a hold. */
3870
+ peek(key) {
3871
+ return key ? this.byKey.get(key)?.instance : undefined;
3872
+ }
3873
+ /** The cached instance under `key`, taking a hold the caller releases with `release`. */
3874
+ hold(key) {
3875
+ const entry = key ? this.byKey.get(key) : undefined;
3876
+ if (!entry)
3877
+ return undefined;
3878
+ this.addHold(entry);
3879
+ return entry.instance;
3880
+ }
3881
+ /** Take a hold on a cached instance. False when it isn't cached (unloaded, evicted). */
3882
+ holdInstance(instance) {
3883
+ const entry = this.entryOf(instance);
3884
+ if (!entry)
3885
+ return false;
3886
+ this.addHold(entry);
3887
+ return true;
3888
+ }
3889
+ /**
3890
+ * Give back one hold. True when the instance should be torn down now: its last hold went and
3891
+ * there is no grace period (or `immediate`), or it isn't cached at all (evicted while the caller
3892
+ * held it). After the last hold with a grace period, the teardown is scheduled instead. An
3893
+ * instance with no holds (a caller that never held it) is left as it is, and so is an instance of
3894
+ * a cache made with `unloadUnheld: false` unless the release is `immediate`.
3895
+ */
3896
+ release(instance, options = {}) {
3897
+ const entry = this.entryOf(instance);
3898
+ if (!entry)
3899
+ return true;
3900
+ if (entry.refCount <= 0)
3901
+ return false;
3902
+ entry.refCount -= 1;
3903
+ if (entry.refCount > 0)
3904
+ return false;
3905
+ if (options.immediate)
3906
+ return true;
3907
+ if (!this.unloadUnheld)
3908
+ return false;
3909
+ if (entityUnloadGraceMs() === 0)
3910
+ return true;
3911
+ this.scheduleUnload(entry);
3912
+ return false;
3913
+ }
3914
+ /**
3915
+ * An instance a caller looked up without a hold: when no one holds it, restart its grace period,
3916
+ * so it stays cached that long after its last use. Does nothing without a grace period.
3917
+ */
3918
+ keepUnheld(instance) {
3919
+ const entry = this.entryOf(instance);
3920
+ if (entry && entry.refCount <= 0 && this.unloadUnheld && entityUnloadGraceMs() > 0)
3921
+ this.scheduleUnload(entry);
3922
+ }
3923
+ addHold(entry) {
3924
+ entry.refCount += 1;
3925
+ this.cancelUnload(entry);
3926
+ }
3927
+ scheduleUnload(entry) {
3928
+ this.cancelUnload(entry);
3929
+ const teardown = () => {
3930
+ this.cancelUnload(entry);
3931
+ if (this.byActor.get(entry.instance.getService()) === entry && entry.refCount <= 0) {
3932
+ teardownEntity(entry.instance, this);
3933
+ }
3934
+ };
3935
+ const timer = setTimeout(teardown, entityUnloadGraceMs());
3936
+ timer.unref?.();
3937
+ entry.unloadTimer = timer;
3938
+ }
3939
+ cancelUnload(entry) {
3940
+ if (entry.unloadTimer === undefined)
3941
+ return;
3942
+ clearTimeout(entry.unloadTimer);
3943
+ entry.unloadTimer = undefined;
3944
+ }
3945
+ entryOf(instance) {
3946
+ return this.byActor.get(instance.getService());
3947
+ }
3948
+ has(instance) {
3949
+ return this.entryOf(instance) !== undefined;
3950
+ }
3951
+ /**
3952
+ * Store the instance under one more key. A key held by another instance moves to this one:
3953
+ * the newest instance for a name or uid wins.
3954
+ */
3955
+ addKey(instance, key) {
3956
+ const entry = this.entryOf(instance);
3957
+ if (!entry || !key)
3958
+ return;
3959
+ const previous = this.byKey.get(key);
3960
+ if (previous === entry)
3961
+ return;
3962
+ previous?.keys.delete(key);
3963
+ this.byKey.set(key, entry);
3964
+ entry.keys.add(key);
3965
+ }
3966
+ removeKey(instance, key) {
3967
+ const entry = this.entryOf(instance);
3968
+ if (!entry || !entry.keys.has(key))
3969
+ return;
3970
+ entry.keys.delete(key);
3971
+ if (this.byKey.get(key) === entry)
3972
+ this.byKey.delete(key);
3973
+ }
3974
+ /** Replace every key of the instance (a rename, an id that changed). */
3975
+ setKeys(instance, keys) {
3976
+ const entry = this.entryOf(instance);
3977
+ if (!entry)
3978
+ return;
3979
+ const next = new Set(keys.filter((key) => !!key));
3980
+ for (const key of [...entry.keys])
3981
+ if (!next.has(key))
3982
+ this.removeKey(instance, key);
3983
+ for (const key of next)
3984
+ this.addKey(instance, key);
3985
+ }
3986
+ keysOf(instance) {
3987
+ return [...(this.entryOf(instance)?.keys ?? [])];
3988
+ }
3989
+ /** Drop the instance under all its keys. Teardown (entityLifecycle) also releases its children. */
3990
+ remove(instance) {
3991
+ const entry = this.entryOf(instance);
3992
+ if (!entry)
3993
+ return undefined;
3994
+ this.cancelUnload(entry);
3995
+ for (const key of entry.keys) {
3996
+ if (this.byKey.get(key) === entry)
3997
+ this.byKey.delete(key);
3998
+ }
3999
+ entry.keys.clear();
4000
+ this.byActor.delete(instance.getService());
4001
+ return entry;
4002
+ }
4003
+ /** Every cached instance once, optionally filtered. */
4004
+ instances(filter) {
4005
+ const seen = new Set();
4006
+ const result = [];
4007
+ for (const entry of this.byKey.values()) {
4008
+ if (seen.has(entry))
4009
+ continue;
4010
+ seen.add(entry);
4011
+ if (!filter || filter(entry.instance))
4012
+ result.push(entry.instance);
4013
+ }
4014
+ return result;
4015
+ }
4016
+ get size() {
4017
+ return this.instances().length;
4018
+ }
4019
+ }
4020
+ /** Every cache made, one per entity class; client.stop() evicts them all (evictAllEntities). */
4021
+ EntityCache.all = new Set();
4022
+ /**
4023
+ * Evictions and destroys, by scope (`schema:<name>`, `model:<id>`). Work that reads a row and then
4024
+ * creates an instance from it (a lookup by id) can't be cancelled by stopping actors: capture
4025
+ * `evictionLog.now()` before the read, and skip creating when `evictedSince(scopes, then)`. A scope
4026
+ * also counts as evicted while its destroy runs, since its rows still exist until the delete commits.
4027
+ */
4028
+ const evictionLog = {
4029
+ generation: 0,
4030
+ last: new Map(),
4031
+ running: new Map(),
4032
+ now() {
4033
+ return this.generation;
4034
+ },
4035
+ /** Record an eviction of these scopes. Call the returned function once a destroy's rows are gone. */
4036
+ evict(scopes) {
4037
+ this.generation += 1;
4038
+ for (const scope of scopes) {
4039
+ this.last.set(scope, this.generation);
4040
+ this.running.set(scope, (this.running.get(scope) ?? 0) + 1);
4041
+ }
4042
+ let finished = false;
4043
+ return () => {
4044
+ if (finished)
4045
+ return;
4046
+ finished = true;
4047
+ // Work that read a row during the destroy and checks after it must still see the eviction
4048
+ this.generation += 1;
4049
+ for (const scope of scopes) {
4050
+ this.last.set(scope, this.generation);
4051
+ const remaining = (this.running.get(scope) ?? 1) - 1;
4052
+ if (remaining > 0)
4053
+ this.running.set(scope, remaining);
4054
+ else
4055
+ this.running.delete(scope);
4056
+ }
4057
+ };
4058
+ },
4059
+ /** True when a scope is being destroyed, or was evicted after `since`. */
4060
+ evictedSince(scopes, since) {
4061
+ for (const scope of scopes) {
4062
+ if (!scope)
4063
+ continue;
4064
+ if (this.running.has(scope))
4065
+ return true;
4066
+ if (this.generation !== since && (this.last.get(scope) ?? 0) > since)
4067
+ return true;
4068
+ }
4069
+ return false;
4070
+ },
4071
+ };
4072
+
3943
4073
  const logger$4 = debug('seedSdk:itemProperty:loadProperty');
3944
4074
  /**
3945
4075
  * Load a property's value and definition: its current row (with a pending outbox value over it,
@@ -3970,15 +4100,14 @@ async function loadProperty(context) {
3970
4100
  });
3971
4101
  timing.mark('metadata');
3972
4102
  if (!metadataRecord) {
3973
- // Metadata not found - this is a new property, will be created elsewhere.
3974
- // Still resolve propertyRecordSchema from Model so getSegmentedItemProperties and
3975
- // getPublishPayload can route and attest the property (e.g. title, description).
4103
+ // No row yet: its first assignment writes one. The definition still comes from the model,
4104
+ // since the setter checks and routes values by it (planPropertyWrite).
3976
4105
  let propertyRecordSchema = undefined;
3977
4106
  const modelNameForNew = context.modelName;
3978
4107
  if (modelNameForNew) {
3979
4108
  try {
3980
- const _mod_3 = await import('./tabEvents-CaVmngqr.js');
3981
- const _ns_3 = _mod_3.be;
4109
+ const _mod_3 = await import('./tabEvents-DT5ez6io.js');
4110
+ const _ns_3 = _mod_3.bh;
3982
4111
  const { resolveItemModelFileId, modelNameForType } = _ns_3;
3983
4112
  const itemModelFileId = await resolveItemModelFileId({
3984
4113
  modelFileId: context.modelFileId,
@@ -4017,8 +4146,8 @@ async function loadProperty(context) {
4017
4146
  // Load propertyRecordSchema from database to make ItemProperty independent from Model
4018
4147
  let propertyRecordSchema = undefined;
4019
4148
  // Model names are only unique per schema: pin lookups to the item's own model row.
4020
- const _mod_4 = await import('./tabEvents-CaVmngqr.js');
4021
- const _ns_4 = _mod_4.be;
4149
+ const _mod_4 = await import('./tabEvents-DT5ez6io.js');
4150
+ const _ns_4 = _mod_4.bh;
4022
4151
  const { resolveItemModelFileId, modelNameForType } = _ns_4;
4023
4152
  const modelFileId = await resolveItemModelFileId({
4024
4153
  modelFileId: context.modelFileId,
@@ -4122,11 +4251,11 @@ async function loadPropertyDefinition(db, { propertyId, modelName, modelFileId,
4122
4251
  }
4123
4252
  if (!modelName)
4124
4253
  return undefined;
4125
- const { loadModelPropertyRecordSchemas } = await import('./modelPropertyRecordSchemas-C7UgA5TH.js');
4254
+ const { loadModelPropertyRecordSchemas } = await import('./modelPropertyRecordSchemas-DEe-lYhn.js');
4126
4255
  const definition = (await loadModelPropertyRecordSchemas(modelName, modelFileId, db)).get(propertyName);
4127
4256
  if (definition)
4128
4257
  return definition;
4129
- const _mod_5 = await import('./errors-DLSoqSHG.js');
4258
+ const _mod_5 = await import('./errors-CnT0fSMK.js');
4130
4259
  const _ns_5 = _mod_5.a4;
4131
4260
  const { Model } = _ns_5;
4132
4261
  const { modelPropertiesToObject } = await Promise.resolve().then(function () { return model; });
@@ -4232,13 +4361,17 @@ let validationLoading;
4232
4361
  function whenValidationLoaded() {
4233
4362
  if (validationService)
4234
4363
  return Promise.resolve();
4235
- validationLoading ?? (validationLoading = import('./SchemaValidationService-CnmKyzR-.js')
4236
- .then(({ SchemaValidationService }) => {
4237
- validationService = new SchemaValidationService();
4238
- })
4239
- .finally(() => {
4240
- validationLoading = undefined;
4241
- }));
4364
+ // async/await, not import().then: rollup's dynamic-import rewrite (rollup.config.mjs) only
4365
+ // neutralizes the minified chunk-export read (`.then(n => n._)`) in this form
4366
+ validationLoading ?? (validationLoading = (async () => {
4367
+ try {
4368
+ const { SchemaValidationService } = await import('./SchemaValidationService-CieGrj5c.js');
4369
+ validationService = new SchemaValidationService();
4370
+ }
4371
+ finally {
4372
+ validationLoading = undefined;
4373
+ }
4374
+ })());
4242
4375
  return validationLoading;
4243
4376
  }
4244
4377
  const isValidationLoaded = () => validationService !== undefined;
@@ -4467,17 +4600,17 @@ hints = {}) => {
4467
4600
  }
4468
4601
  try {
4469
4602
  if (hints.propertyId != null) {
4470
- const _mod_6 = await import('./tabEvents-CaVmngqr.js');
4471
- const _ns_6 = _mod_6.bf;
4603
+ const _mod_6 = await import('./tabEvents-DT5ez6io.js');
4604
+ const _ns_6 = _mod_6.bi;
4472
4605
  const { getPropertyDefinition } = _ns_6;
4473
4606
  const definition = await getPropertyDefinition(hints.propertyId);
4474
4607
  if (definition)
4475
4608
  return definition;
4476
4609
  }
4477
- const _mod_7 = await import('./tabEvents-CaVmngqr.js');
4478
- const _ns_7 = _mod_7.be;
4610
+ const _mod_7 = await import('./tabEvents-DT5ez6io.js');
4611
+ const _ns_7 = _mod_7.bh;
4479
4612
  const { resolveItemModelFileId } = _ns_7;
4480
- const { getModelPropertyDefinitions, resolvePropertyKey } = await import('./property-BxUYznH0.js');
4613
+ const { getModelPropertyDefinitions, resolvePropertyKey } = await import('./property-iNW1N3vm.js');
4481
4614
  const modelFileId = (await resolveItemModelFileId(hints)) ?? undefined;
4482
4615
  const name = modelName || ((await modelTypeToModelName(modelType)) ?? '');
4483
4616
  // An ambiguous bare name reads nothing here; the in-memory model below may still know it
@@ -4485,7 +4618,7 @@ hints = {}) => {
4485
4618
  const key = resolvePropertyKey(definitions, propertyName);
4486
4619
  if (key)
4487
4620
  return definitions[key];
4488
- const _mod_8 = await import('./errors-DLSoqSHG.js');
4621
+ const _mod_8 = await import('./errors-CnT0fSMK.js');
4489
4622
  const _ns_8 = _mod_8.a4;
4490
4623
  const { Model } = _ns_8;
4491
4624
  const { modelPropertiesToObject } = await Promise.resolve().then(function () { return model; });
@@ -4506,8 +4639,8 @@ hints = {}) => {
4506
4639
  const modelTypeToModelName = async (modelType) => {
4507
4640
  if (!modelType)
4508
4641
  return undefined;
4509
- const _mod_9 = await import('./tabEvents-CaVmngqr.js');
4510
- const _ns_9 = _mod_9.be;
4642
+ const _mod_9 = await import('./tabEvents-DT5ez6io.js');
4643
+ const _ns_9 = _mod_9.bh;
4511
4644
  const { modelNameForType } = _ns_9;
4512
4645
  return modelNameForType(modelType).catch(() => undefined);
4513
4646
  };
@@ -4670,7 +4803,7 @@ class ItemProperty {
4670
4803
  return;
4671
4804
  }
4672
4805
  // Use dynamic import to break circular dependency
4673
- const schemaMod = await import('./index-BVBmz_cc.js');
4806
+ const schemaMod = await import('./index-DXRhFg1N.js');
4674
4807
  const { ModelPropertyDataTypes } = schemaMod;
4675
4808
  const { context } = snapshot;
4676
4809
  const { propertyRecordSchema } = context;
@@ -4883,7 +5016,7 @@ class ItemProperty {
4883
5016
  // The change feed reports writes by stored name: watch every name this property's rows have
4884
5017
  const propertyNames = [...property.names];
4885
5018
  if (propertyId != null) {
4886
- const _mod_10 = await import('./index-D0V-mjCV.js');
5019
+ const _mod_10 = await import('./index-BjWk0x4-.js');
4887
5020
  const _ns_10 = _mod_10.s;
4888
5021
  const { metadata } = _ns_10;
4889
5022
  const { eq: eqOp } = await import('drizzle-orm');
@@ -4923,12 +5056,17 @@ class ItemProperty {
4923
5056
  // A pending edit settles the conflict when it applies, so an overlaid row shows none.
4924
5057
  // `assignSeq` marks a read begun before an assignment in this tab: it may predate that op,
4925
5058
  // which the context already shows, so the feed drops it.
5059
+ // Sync records a conflict and moves its draft to newest in one transaction, so a row read
5060
+ // before the conflict may be the synced one: read the row again once a conflict shows.
4926
5061
  const readRow = async () => {
4927
5062
  const assignSeq = this._assignSeq;
4928
- const row = await currentPropertyRow({ seedLocalId, seedUid }, { match: property, propertyName }, { overlay: true, db });
5063
+ const read = () => currentPropertyRow({ seedLocalId, seedUid }, { match: property, propertyName }, { overlay: true, db });
5064
+ let row = await read();
4929
5065
  if (!row)
4930
5066
  return row;
4931
5067
  const conflict = row.overlaid ? null : await getPropertyConflict(row.seedLocalId ?? seedLocalId, propertyName);
5068
+ if (conflict)
5069
+ row = (await read()) ?? row;
4932
5070
  return { ...row, conflict, assignSeq };
4933
5071
  };
4934
5072
  // In the browser the feed watch below reads the row at once, so only Node (no change feed)
@@ -5057,6 +5195,33 @@ class ItemProperty {
5057
5195
  });
5058
5196
  }
5059
5197
  }
5198
+ /** Takes a property definition read from its row, as the constructor takes the initial one. */
5199
+ _applyDefinition(propertyRecordSchema) {
5200
+ this._service.send({ type: 'updateContext', propertyRecordSchema });
5201
+ this._dataType = normalizeDataType(propertyRecordSchema.dataType);
5202
+ const isList = isDataType(propertyRecordSchema.dataType, ModelPropertyDataTypes.List);
5203
+ this._isList = isList;
5204
+ this._isRelation = isList
5205
+ ? !!propertyRecordSchema.ref
5206
+ : isDataType(propertyRecordSchema.dataType, ModelPropertyDataTypes.Relation);
5207
+ }
5208
+ /**
5209
+ * Gives cached instances of these properties their rows' current definitions, after a schema
5210
+ * change wrote the rows (refreshCachedProperties). An instance keeps the definition it loaded
5211
+ * with otherwise: Node has no live query, and the browser's arrives some time after the write.
5212
+ */
5213
+ static async refreshDefinitions(propertyFileIds) {
5214
+ const instances = this.cache.instances().filter((instance) => !isActorStopped(instance._service));
5215
+ if (instances.length === 0)
5216
+ return;
5217
+ const definitions = await getPropertyDefinitionsByFileId(propertyFileIds);
5218
+ for (const instance of instances) {
5219
+ const fileId = instance._getSnapshotContext().propertyRecordSchema?.id;
5220
+ const definition = fileId ? definitions.get(fileId) : undefined;
5221
+ if (definition)
5222
+ instance._applyDefinition(definition);
5223
+ }
5224
+ }
5060
5225
  /**
5061
5226
  * Set up liveQuery subscription to watch for property schema changes in the properties table.
5062
5227
  * When ModelProperty dataType (or other schema fields) changes in the database, ItemProperty
@@ -5077,8 +5242,8 @@ class ItemProperty {
5077
5242
  const ctx = this._service.getSnapshot().context;
5078
5243
  let modelFileId;
5079
5244
  try {
5080
- const _mod_11 = await import('./tabEvents-CaVmngqr.js');
5081
- const _ns_11 = _mod_11.be;
5245
+ const _mod_11 = await import('./tabEvents-DT5ez6io.js');
5246
+ const _ns_11 = _mod_11.bh;
5082
5247
  const { resolveItemModelFileId } = _ns_11;
5083
5248
  modelFileId = await resolveItemModelFileId({
5084
5249
  modelFileId: ctx.modelFileId,
@@ -5131,29 +5296,7 @@ class ItemProperty {
5131
5296
  const required = row.required;
5132
5297
  const propertyRecordSchema = propertyDefinitionFromRow({ ...row, required: required == null ? null : !!required }, refModelName);
5133
5298
  logger(`[ItemProperty._setupPropertySchemaLiveQuery] Schema updated for ${propertyName}: dataType=${row.dataType}`);
5134
- this._service.send({
5135
- type: 'updateContext',
5136
- propertyRecordSchema,
5137
- });
5138
- // Update instance fields to match new schema (mirror constructor logic)
5139
- this._dataType = normalizeDataType(propertyRecordSchema.dataType);
5140
- if (isDataType(propertyRecordSchema.dataType, ModelPropertyDataTypes.Relation)) {
5141
- ;
5142
- this._isRelation = true;
5143
- }
5144
- else {
5145
- ;
5146
- this._isRelation = false;
5147
- }
5148
- if (isDataType(propertyRecordSchema.dataType, ModelPropertyDataTypes.List)) {
5149
- ;
5150
- this._isList = true;
5151
- this._isRelation = !!(propertyRecordSchema.ref);
5152
- }
5153
- else {
5154
- ;
5155
- this._isList = false;
5156
- }
5299
+ this._applyDefinition(propertyRecordSchema);
5157
5300
  },
5158
5301
  error: (error) => {
5159
5302
  logger(`[ItemProperty._setupPropertySchemaLiveQuery] LiveQuery error: ${error}`);
@@ -5205,6 +5348,8 @@ class ItemProperty {
5205
5348
  if (cached) {
5206
5349
  if (holds)
5207
5350
  this.cache.holdInstance(cached);
5351
+ else
5352
+ this.cache.keepUnheld(cached);
5208
5353
  for (const key of [keyByLocal, keyByUid])
5209
5354
  this.cache.addKey(cached, key);
5210
5355
  this.syncFromRow(cached, props);
@@ -5337,8 +5482,11 @@ class ItemProperty {
5337
5482
  }
5338
5483
  return undefined;
5339
5484
  }
5340
- /** Takes a hold on the instance it returns: release it with `unload()`. */
5341
- static async find({ propertyName, seedLocalId, seedUid, modelName: modelNameOption, waitForReady = true, readyTimeout = DEFAULT_READY_TIMEOUT_MS, hold, }) {
5485
+ /**
5486
+ * The property's instance, loaded from its row when it isn't cached. Takes no hold unless
5487
+ * `hold: true` (release it with `unload()`).
5488
+ */
5489
+ static async find({ propertyName, seedLocalId, seedUid, modelName: modelNameOption, waitForReady = true, readyTimeout = DEFAULT_READY_TIMEOUT_MS, hold = false, }) {
5342
5490
  if ((!seedLocalId && !seedUid) || !propertyName) {
5343
5491
  return undefined;
5344
5492
  }
@@ -5355,8 +5503,10 @@ class ItemProperty {
5355
5503
  return undefined;
5356
5504
  for (const key of [keyByLocal, keyByUid])
5357
5505
  this.cache.addKey(cached, key);
5358
- if (hold !== false)
5506
+ if (hold)
5359
5507
  this.cache.holdInstance(cached);
5508
+ else
5509
+ this.cache.keepUnheld(cached);
5360
5510
  return cached;
5361
5511
  };
5362
5512
  foundProperty = fromCache();
@@ -5400,10 +5550,10 @@ class ItemProperty {
5400
5550
  /**
5401
5551
  * Get all ItemProperty instances for an item.
5402
5552
  * Loads property data via getItemProperties, creates instances via create, optionally waits for idle.
5403
- * Takes a hold on each instance, as `create` does.
5553
+ * Takes no holds unless `hold: true`, as `find`.
5404
5554
  */
5405
5555
  static async all(params, options) {
5406
- const { waitForReady = false, readyTimeout = DEFAULT_READY_TIMEOUT_MS, hold } = options ?? {};
5556
+ const { waitForReady = false, readyTimeout = DEFAULT_READY_TIMEOUT_MS, hold = false } = options ?? {};
5407
5557
  const { seedLocalId, seedUid } = params;
5408
5558
  if (!seedLocalId && !seedUid) {
5409
5559
  return [];
@@ -5411,8 +5561,8 @@ class ItemProperty {
5411
5561
  const propertiesData = await getItemProperties({ seedLocalId, seedUid });
5412
5562
  const instances = [];
5413
5563
  const seenNormalized = new Set();
5414
- const _mod_12 = await import('./tabEvents-CaVmngqr.js');
5415
- const _ns_12 = _mod_12.be;
5564
+ const _mod_12 = await import('./tabEvents-DT5ez6io.js');
5565
+ const _ns_12 = _mod_12.bh;
5416
5566
  const { resolveItemModelFileId } = _ns_12;
5417
5567
  const itemModelFileId = await resolveItemModelFileId({ seedLocalId, seedUid });
5418
5568
  // One lookup per model type, not per property
@@ -5433,7 +5583,7 @@ class ItemProperty {
5433
5583
  // (`textId` → `text`, `authorIdentityIds` → `authors`)
5434
5584
  const definitionsKey = JSON.stringify([modelName, modelFileId ?? null]);
5435
5585
  if (modelName && !definitionsByModel.has(definitionsKey)) {
5436
- const { getModelPropertyDefinitions } = await import('./property-BxUYznH0.js');
5586
+ const { getModelPropertyDefinitions } = await import('./property-iNW1N3vm.js');
5437
5587
  definitionsByModel.set(definitionsKey,
5438
5588
  // No model row: the stored name decides
5439
5589
  getModelPropertyDefinitions(modelName, { modelFileId }).then(Object.fromEntries, () => ({})));
@@ -5607,7 +5757,7 @@ class ItemProperty {
5607
5757
  const { seedLocalId, propertyName } = this._getSnapshotContext();
5608
5758
  if (!seedLocalId || !propertyName)
5609
5759
  return;
5610
- const { appendOutboxOpAndWait, markPropertyConflictsResolved, startItemOutbox, throwItemWriteError } = await import('./index-CYGWPoOx.js');
5760
+ const { appendOutboxOpAndWait, markPropertyConflictsResolved, startItemOutbox, throwItemWriteError } = await import('./index-CZMWKXLM.js');
5611
5761
  if (choice === 'remote') {
5612
5762
  startItemOutbox();
5613
5763
  await appendOutboxOpAndWait({
@@ -5654,6 +5804,7 @@ class ItemProperty {
5654
5804
  * any tab, else its stored row.
5655
5805
  */
5656
5806
  setValue(value) {
5807
+ this._assertLoaded('set');
5657
5808
  // An empty Image, File or Html value has nothing to store: it clears the property
5658
5809
  if ((value == null || (typeof value === 'string' && value.trim() === '')) && this._holdsStorageSeed(this._getSnapshotContext())) {
5659
5810
  this._assign(null, { retract: false });
@@ -5754,8 +5905,8 @@ class ItemProperty {
5754
5905
  context = this._getSnapshotContext();
5755
5906
  if (!context.seedLocalId) {
5756
5907
  // An item known only by its seed uid: its local seed, if it has one
5757
- const _mod_13 = await import('./tabEvents-CaVmngqr.js');
5758
- const _ns_13 = _mod_13.bd;
5908
+ const _mod_13 = await import('./tabEvents-DT5ez6io.js');
5909
+ const _ns_13 = _mod_13.bg;
5759
5910
  const { findSeedRow } = _ns_13;
5760
5911
  const seedLocalId = context.seedUid ? (await findSeedRow({ uid: context.seedUid }))?.localId : undefined;
5761
5912
  if (!seedLocalId)
@@ -5794,7 +5945,7 @@ class ItemProperty {
5794
5945
  /** Waits for an appended op to settle, then shows its row, or the current value if it failed. */
5795
5946
  async _followWrite(write, appended, plan) {
5796
5947
  try {
5797
- const { getOutboxOps, outboxOpResult, startItemOutbox, waitForOutboxOp } = await import('./index-CYGWPoOx.js');
5948
+ const { getOutboxOps, outboxOpResult, startItemOutbox, waitForOutboxOp } = await import('./index-CZMWKXLM.js');
5798
5949
  startItemOutbox();
5799
5950
  const opId = await appended;
5800
5951
  await waitForOutboxOp(opId);
@@ -5853,7 +6004,7 @@ class ItemProperty {
5853
6004
  this._service.send({ type: 'updateContext', ...outcome.applied, isSaving: false });
5854
6005
  return;
5855
6006
  }
5856
- const { throwItemWriteError } = await import('./index-CYGWPoOx.js');
6007
+ const { throwItemWriteError } = await import('./index-CZMWKXLM.js');
5857
6008
  let error = outcome.error;
5858
6009
  try {
5859
6010
  throwItemWriteError(outcome.error);
@@ -5919,6 +6070,7 @@ class ItemProperty {
5919
6070
  * ItemPropertySaveValidationError when the value broke a rule and was not written.
5920
6071
  */
5921
6072
  async save() {
6073
+ this._assertLoaded('save');
5922
6074
  const ctx = this._getSnapshotContext();
5923
6075
  const label = `save ${ctx?.modelName ?? '?'}.${ctx?.propertyName ?? '?'}`;
5924
6076
  const { assertItemOwned } = await Promise.resolve().then(function () { return ownership; });
@@ -5949,6 +6101,14 @@ class ItemProperty {
5949
6101
  * Every cache key holding this instance: its item's local id and seed uid, and the name it had
5950
6102
  * before a rename (List of relation). They share one entry.
5951
6103
  */
6104
+ /** Throws EntityUnloadedError, naming the property and its item, when this instance was unloaded. */
6105
+ _assertLoaded(action) {
6106
+ if (!isEntityTornDown(this))
6107
+ return;
6108
+ const { seedLocalId, seedUid, modelName, propertyName } = this._getSnapshotContext();
6109
+ throw new EntityUnloadedError(`Cannot ${action} ${modelName ?? '?'}.${propertyName ?? '?'} of item ${seedLocalId ?? seedUid ?? '(no id)'}: ` +
6110
+ 'this ItemProperty instance was unloaded. Look the item or property up again, and hold it while you use it.');
6111
+ }
5952
6112
  /** Releases this caller's hold; the last one stops the property and drops it from the cache. */
5953
6113
  unload() {
5954
6114
  releaseEntity(this, ItemProperty.cache);
@@ -5979,7 +6139,7 @@ class ItemProperty {
5979
6139
  if (db && seedLocalId) {
5980
6140
  // Through the outbox (ADR 0019): after the property's pending edits, which would
5981
6141
  // otherwise write its rows back
5982
- const { appendOutboxOpAndWait, startItemOutbox, throwItemWriteError } = await import('./index-CYGWPoOx.js');
6142
+ const { appendOutboxOpAndWait, startItemOutbox, throwItemWriteError } = await import('./index-CZMWKXLM.js');
5983
6143
  startItemOutbox();
5984
6144
  await appendOutboxOpAndWait({
5985
6145
  seedLocalId,
@@ -5989,7 +6149,7 @@ class ItemProperty {
5989
6149
  }).catch(throwItemWriteError);
5990
6150
  }
5991
6151
  else if (db) {
5992
- const _mod_14 = await import('./index-D0V-mjCV.js');
6152
+ const _mod_14 = await import('./index-BjWk0x4-.js');
5993
6153
  const _ns_14 = _mod_14.s;
5994
6154
  const seedSchemaMod = _ns_14;
5995
6155
  const { metadata } = seedSchemaMod;
@@ -6018,6 +6178,7 @@ class ItemProperty {
6018
6178
  }
6019
6179
  /** Cached as `Item_<id>_<propertyName>` under its item's local id and seed uid. */
6020
6180
  ItemProperty.cache = new EntityCache();
6181
+ addCachedPropertyRefresher((propertyFileIds) => ItemProperty.refreshDefinitions(propertyFileIds));
6021
6182
 
6022
6183
  var ItemProperty$1 = /*#__PURE__*/Object.freeze({
6023
6184
  __proto__: null,
@@ -6221,7 +6382,7 @@ const createMetadata = async (metadataValues, propertyRecordSchema, options) =>
6221
6382
  propertyRecordSchema?.validation &&
6222
6383
  metadataValues.propertyValue != null &&
6223
6384
  metadataValues.propertyValue !== '') {
6224
- const { SchemaValidationService } = await import('./SchemaValidationService-CnmKyzR-.js');
6385
+ const { SchemaValidationService } = await import('./SchemaValidationService-CieGrj5c.js');
6225
6386
  const validationService = new SchemaValidationService();
6226
6387
  const validationResult = validationService.validatePropertyValue(metadataValues.propertyValue, normalizeDataType(propertyRecordSchema.dataType), propertyRecordSchema.validation, propertyRecordSchema.refValueType
6227
6388
  ? normalizeDataType(String(propertyRecordSchema.refValueType))
@@ -6315,7 +6476,7 @@ const createNewItem = async ({ modelName, modelFileId, ...propertyData }) => {
6315
6476
  }
6316
6477
  const seedType = easName(modelName);
6317
6478
  // Dynamic import to break circular dependency
6318
- const _mod_15 = await import('./errors-DLSoqSHG.js');
6479
+ const _mod_15 = await import('./errors-CnT0fSMK.js');
6319
6480
  const _ns_15 = _mod_15.a4;
6320
6481
  const modelMod = _ns_15;
6321
6482
  const { Model } = modelMod;
@@ -6373,7 +6534,7 @@ const createNewItem = async ({ modelName, modelFileId, ...propertyData }) => {
6373
6534
  }
6374
6535
  rows.push({ propertyName, propertyValue, propertyRecordSchema, schemaUid: schemaUids.get(propertyName) });
6375
6536
  }
6376
- const { appendOutboxOpAndWait, startItemOutbox } = await import('./index-CYGWPoOx.js');
6537
+ const { appendOutboxOpAndWait, startItemOutbox } = await import('./index-CZMWKXLM.js');
6377
6538
  startItemOutbox();
6378
6539
  const fields = await timed(`${label} seed fields`, () => resolveNewSeedFields(seedType));
6379
6540
  const applied = await timed(`${label} outbox apply`, () => appendOutboxOpAndWait({
@@ -6392,7 +6553,7 @@ const createNewItem = async ({ modelName, modelFileId, ...propertyData }) => {
6392
6553
  }
6393
6554
  catch (error) {
6394
6555
  // Staged values of a create that never got appended (once appended, the op's discard does it)
6395
- const _mod_16 = await import('./errors-DLSoqSHG.js');
6556
+ const _mod_16 = await import('./errors-CnT0fSMK.js');
6396
6557
  const _ns_16 = _mod_16.a7;
6397
6558
  const { deleteFiles } = _ns_16;
6398
6559
  await deleteFiles(stagedPaths);
@@ -6468,14 +6629,14 @@ const createItemPropertyInstances = async (metadataRows, seedLocalId, seedUid, m
6468
6629
  };
6469
6630
  // The model's property definitions from their rows; a model created in this session whose
6470
6631
  // rows aren't written yet gives its in-memory ones
6471
- const { getModelPropertyDefinitions } = await import('./property-BxUYznH0.js');
6632
+ const { getModelPropertyDefinitions } = await import('./property-iNW1N3vm.js');
6472
6633
  let propertySchemas = Object.fromEntries(await getModelPropertyDefinitions(modelName, modelScope).catch((err) => {
6473
6634
  logger(`No property definitions for ${modelName}: ${err}`);
6474
6635
  return [];
6475
6636
  }));
6476
6637
  if (Object.keys(propertySchemas).length === 0) {
6477
6638
  const { modelPropertiesToObject } = await Promise.resolve().then(function () { return model; });
6478
- const _mod_17 = await import('./errors-DLSoqSHG.js');
6639
+ const _mod_17 = await import('./errors-CnT0fSMK.js');
6479
6640
  const _ns_17 = _mod_17.a4;
6480
6641
  const { Model } = _ns_17;
6481
6642
  const model$1 = Model.resolve(modelName, modelScope);
@@ -6503,7 +6664,7 @@ const createItemPropertyInstances = async (metadataRows, seedLocalId, seedUid, m
6503
6664
  // Infer propertyRecordSchema from metadata when schema is missing (enables persistence)
6504
6665
  let propSchema = propertySchemas[propertyName];
6505
6666
  if (!propSchema && isRefTypeFromMeta) {
6506
- const schemaMod = await import('./index-BVBmz_cc.js');
6667
+ const schemaMod = await import('./index-DXRhFg1N.js');
6507
6668
  const { ModelPropertyDataTypes } = schemaMod;
6508
6669
  propSchema = {
6509
6670
  dataType: refSeedType === 'html' ? ModelPropertyDataTypes.Html
@@ -6886,7 +7047,7 @@ const getModel = () => {
6886
7047
  // the fragile `import(...).then(n => n.X)` chunk-namespace pattern.
6887
7048
  if (!modelImportPromise) {
6888
7049
  modelImportPromise = (async () => {
6889
- const _mod_18 = await import('./errors-DLSoqSHG.js');
7050
+ const _mod_18 = await import('./errors-CnT0fSMK.js');
6890
7051
  const _ns_18 = _mod_18.a4;
6891
7052
  const mod = _ns_18;
6892
7053
  ModelClass = mod.Model;
@@ -7002,6 +7163,7 @@ class Item {
7002
7163
  });
7003
7164
  };
7004
7165
  this.unpublish = async () => {
7166
+ this._assertLoaded('unpublish');
7005
7167
  const { assertItemOwned } = await Promise.resolve().then(function () { return ownership; });
7006
7168
  const { getRevokeExecutor } = await Promise.resolve().then(function () { return publishConfig; });
7007
7169
  await assertItemOwned(this);
@@ -7045,6 +7207,7 @@ class Item {
7045
7207
  * if it failed. Throws OwnershipError if none of the client's addresses own the item.
7046
7208
  */
7047
7209
  this.publish = async (options) => {
7210
+ this._assertLoaded('publish');
7048
7211
  const { assertItemOwned } = await Promise.resolve().then(function () { return ownership; });
7049
7212
  const { getPublishExecutor, PUBLISH_NOT_CONFIGURED_MESSAGE } = await Promise.resolve().then(function () { return publishConfig; });
7050
7213
  await assertItemOwned(this);
@@ -7085,14 +7248,14 @@ class Item {
7085
7248
  };
7086
7249
  this.getPublishUploads = async (options) => {
7087
7250
  // Use dynamic import to break circular dependency
7088
- const _mod_19 = await import('./getPublishUploads-sS6JfPgz.js');
7089
- const _ns_19 = _mod_19.n;
7251
+ const _mod_19 = await import('./getPublishUploads-DivYpgNw.js');
7252
+ const _ns_19 = _mod_19.q;
7090
7253
  const getPublishUploadsMod = _ns_19;
7091
7254
  const { getPublishUploads } = getPublishUploadsMod;
7092
7255
  return await getPublishUploads(this, options);
7093
7256
  };
7094
7257
  this.getPublishPayload = async (uploadedTransactions, options) => {
7095
- const _mod_20 = await import('./getPublishPayload-BnIwpwz1.js');
7258
+ const _mod_20 = await import('./getPublishPayload-DKuEaq96.js');
7096
7259
  const _ns_20 = _mod_20.c;
7097
7260
  const getPublishPayloadMod = _ns_20;
7098
7261
  const { getPublishPayload } = getPublishPayloadMod;
@@ -7241,13 +7404,13 @@ class Item {
7241
7404
  * Cache a new item under its ids and keep its keys in step with them (a seed uid arrives on
7242
7405
  * publish, moves on republish), then start its load.
7243
7406
  */
7244
- static cacheAndLoad(instance) {
7407
+ static cacheAndLoad(instance, hold = true) {
7245
7408
  const keys = () => {
7246
7409
  const { seedLocalId, seedUid } = instance._getSnapshotContext();
7247
7410
  return [seedLocalId, seedUid];
7248
7411
  };
7249
7412
  let current = keys().join('\u0000');
7250
- this.cache.add(instance, keys(), { hold: true });
7413
+ this.cache.add(instance, keys(), { hold });
7251
7414
  const idWatch = instance._service.subscribe(() => {
7252
7415
  const next = keys();
7253
7416
  if (next.join('\u0000') === current || isEntityTornDown(instance))
@@ -7286,6 +7449,7 @@ class Item {
7286
7449
  sendUpdate: (target, prop, value) => {
7287
7450
  if (prop === 'publisher')
7288
7451
  throw new Error('Cannot set item.publisher: publisher is read-only.');
7452
+ target._assertLoaded(`set ${prop} on`);
7289
7453
  Item.sendTrackedPropertyUpdate(target, prop, value);
7290
7454
  if (prop === 'seedUid' && typeof value === 'string' && value.length > 0) {
7291
7455
  target._persistSeedUid(value);
@@ -7298,6 +7462,7 @@ class Item {
7298
7462
  return {
7299
7463
  get: () => property.value,
7300
7464
  set: (value) => {
7465
+ target._assertLoaded(`set ${prop} on`);
7301
7466
  property.value = value;
7302
7467
  },
7303
7468
  };
@@ -7313,6 +7478,15 @@ class Item {
7313
7478
  reactiveProxies.set(instance._service, proxy);
7314
7479
  return proxy;
7315
7480
  }
7481
+ /** Throws EntityUnloadedError, naming the item, when this instance was unloaded. */
7482
+ _assertLoaded(action) {
7483
+ if (!isEntityTornDown(this))
7484
+ return;
7485
+ const { seedLocalId, seedUid, modelName } = this._getSnapshotContext();
7486
+ throw new EntityUnloadedError(`Cannot ${action} ${modelName ?? 'item'} ${seedLocalId ?? seedUid ?? '(no id)'}: this Item instance was ` +
7487
+ 'unloaded. Look the item up again (Item.find), and hold it while you use it ' +
7488
+ '(Item.find({ hold: true }) or Item.retain, then unload()).');
7489
+ }
7316
7490
  /**
7317
7491
  * The ItemProperty a model property name reads and writes on the item: by its name, or by its
7318
7492
  * storage name (`html` finds `htmlId` and the other way round). Internal names don't resolve.
@@ -7407,11 +7581,12 @@ class Item {
7407
7581
  static async create(props, options) {
7408
7582
  const waitForReady = options?.waitForReady !== false;
7409
7583
  const readyTimeout = options?.readyTimeout ?? DEFAULT_READY_TIMEOUT_MS;
7584
+ const hold = options?.hold !== false;
7410
7585
  if (!props.modelName && props.type) {
7411
7586
  // The model the seed's type names (its easName, at the model's nameMatch level): `blog_post`
7412
7587
  // finds "Blog Post". No match leaves the type, which then fails as an unknown model.
7413
- const _mod_21 = await import('./tabEvents-CaVmngqr.js');
7414
- const _ns_21 = _mod_21.be;
7588
+ const _mod_21 = await import('./tabEvents-DT5ez6io.js');
7589
+ const _ns_21 = _mod_21.bh;
7415
7590
  const { resolveModelRecordByNameOrType } = _ns_21;
7416
7591
  const model = await resolveModelRecordByNameOrType(props.type, {
7417
7592
  modelFileId: props.modelFileId,
@@ -7423,7 +7598,10 @@ class Item {
7423
7598
  // Local id first, so list rows (local + uid) resolve the same instance as early draft loads
7424
7599
  const cached = this.cache.peek(props.seedLocalId) ?? this.cache.peek(props.seedUid);
7425
7600
  if (cached) {
7426
- this.cache.holdInstance(cached);
7601
+ if (hold)
7602
+ this.cache.holdInstance(cached);
7603
+ else
7604
+ this.cache.keepUnheld(cached);
7427
7605
  const propertyInstances = cached.getService().getSnapshot().context.propertyInstances;
7428
7606
  for (const [propertyName, propertyValue] of Object.entries(props)) {
7429
7607
  propertyInstances?.get(propertyName)?.getService().send({ type: 'updateContext', propertyValue });
@@ -7434,7 +7612,8 @@ class Item {
7434
7612
  await waitForEntityReady(cached, { timeout: readyTimeout });
7435
7613
  }
7436
7614
  catch (err) {
7437
- cached.unload();
7615
+ if (hold)
7616
+ cached.unload();
7438
7617
  throw err;
7439
7618
  }
7440
7619
  return cached;
@@ -7443,14 +7622,15 @@ class Item {
7443
7622
  // Exclude latestVersionUid from props as it has incompatible types (string vs VersionsType)
7444
7623
  const { latestVersionUid: _latestVersionUid, ...propsWithoutVersionUid } = props;
7445
7624
  const proxiedInstance = Item.wrapInProxy(new Item({ ...propsWithoutVersionUid }));
7446
- this.cacheAndLoad(proxiedInstance);
7625
+ this.cacheAndLoad(proxiedInstance, hold);
7447
7626
  if (!waitForReady)
7448
7627
  return proxiedInstance;
7449
7628
  try {
7450
7629
  await waitForEntityReady(proxiedInstance, { timeout: readyTimeout });
7451
7630
  }
7452
7631
  catch (err) {
7453
- proxiedInstance.unload();
7632
+ if (hold)
7633
+ proxiedInstance.unload();
7454
7634
  throw err;
7455
7635
  }
7456
7636
  return proxiedInstance;
@@ -7469,8 +7649,8 @@ class Item {
7469
7649
  const db = BaseDb.getAppDb();
7470
7650
  if (db && props.modelName) {
7471
7651
  // Model names are only unique per schema: resolve the exact models row from the most specific scope.
7472
- const _mod_22 = await import('./tabEvents-CaVmngqr.js');
7473
- const _ns_22 = _mod_22.be;
7652
+ const _mod_22 = await import('./tabEvents-DT5ez6io.js');
7653
+ const _ns_22 = _mod_22.bh;
7474
7654
  const { resolveModelRecord } = _ns_22;
7475
7655
  const modelRecord = await resolveModelRecord(props.modelName, {
7476
7656
  modelFileId: props.modelFileId ?? modelInstance?.id,
@@ -7478,7 +7658,7 @@ class Item {
7478
7658
  });
7479
7659
  const modelRecords = modelRecord ? [modelRecord] : [];
7480
7660
  if (!modelRecord) {
7481
- const _mod_23 = await import('./errors-DLSoqSHG.js');
7661
+ const _mod_23 = await import('./errors-CnT0fSMK.js');
7482
7662
  const _ns_23 = _mod_23.a8;
7483
7663
  const { UnknownModelError } = _ns_23;
7484
7664
  throw new UnknownModelError(props.modelName, schemaName ?? modelInstance?.schemaName);
@@ -7489,8 +7669,8 @@ class Item {
7489
7669
  // Scope the constructor's Model lookup to this model's schema (as getItemData does on reload),
7490
7670
  // so a same-named model cached from another schema can't supply the property set.
7491
7671
  if (modelRecord && !props.schemaName) {
7492
- const _mod_24 = await import('./tabEvents-CaVmngqr.js');
7493
- const _ns_24 = _mod_24.be;
7672
+ const _mod_24 = await import('./tabEvents-DT5ez6io.js');
7673
+ const _ns_24 = _mod_24.bh;
7494
7674
  const { getModelSchemaName } = _ns_24;
7495
7675
  const modelSchemaName = await getModelSchemaName(modelRecord.id, db);
7496
7676
  if (modelSchemaName) {
@@ -7560,10 +7740,16 @@ class Item {
7560
7740
  const propsWithModel = { ...propsWithoutVersionUid };
7561
7741
  const newInstance = new Item(propsWithModel);
7562
7742
  const proxiedInstance = Item.wrapInProxy(newInstance);
7743
+ // Held while create saves its values; `hold: false` gives the hold back as it returns
7563
7744
  this.cacheAndLoad(proxiedInstance);
7745
+ const created = () => {
7746
+ if (!hold)
7747
+ this.cache.release(proxiedInstance);
7748
+ return proxiedInstance;
7749
+ };
7564
7750
  // Storage values must be saved before returning, so wait for readiness even when not requested.
7565
7751
  if (!waitForReady && pipelineValues.length === 0)
7566
- return proxiedInstance;
7752
+ return created();
7567
7753
  try {
7568
7754
  await waitForEntityReady(proxiedInstance, { timeout: readyTimeout });
7569
7755
  await this.savePipelineValues(proxiedInstance, pipelineValues, readyTimeout);
@@ -7573,7 +7759,7 @@ class Item {
7573
7759
  await this.discardCreatedItem(proxiedInstance, seedLocalId);
7574
7760
  throw err;
7575
7761
  }
7576
- return proxiedInstance;
7762
+ return created();
7577
7763
  }
7578
7764
  /**
7579
7765
  * Stops a just-created item and deletes what create wrote: a `discard` outbox op (ADR 0019),
@@ -7588,7 +7774,7 @@ class Item {
7588
7774
  /* best-effort cache cleanup */
7589
7775
  }
7590
7776
  try {
7591
- const { appendOutboxOpAndWait, deleteFiles, startItemOutbox } = await import('./index-CYGWPoOx.js');
7777
+ const { appendOutboxOpAndWait, deleteFiles, startItemOutbox } = await import('./index-CZMWKXLM.js');
7592
7778
  startItemOutbox();
7593
7779
  const applied = await appendOutboxOpAndWait({
7594
7780
  seedLocalId,
@@ -7611,7 +7797,7 @@ class Item {
7611
7797
  for (const [propertyName, value] of pipelineValues) {
7612
7798
  const property = await this.waitForPropertyInstance(item, propertyName, timeout);
7613
7799
  if (!property) {
7614
- const _mod_25 = await import('./errors-DLSoqSHG.js');
7800
+ const _mod_25 = await import('./errors-CnT0fSMK.js');
7615
7801
  const _ns_25 = _mod_25.a8;
7616
7802
  const { ItemCreateError } = _ns_25;
7617
7803
  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 });
@@ -7623,14 +7809,14 @@ class Item {
7623
7809
  }
7624
7810
  catch (error) {
7625
7811
  if (error instanceof ItemPropertySaveValidationError) {
7626
- const _mod_26 = await import('./errors-DLSoqSHG.js');
7812
+ const _mod_26 = await import('./errors-CnT0fSMK.js');
7627
7813
  const _ns_26 = _mod_26.a8;
7628
7814
  const { ItemCreateValidationError } = _ns_26;
7629
7815
  const created = new ItemCreateValidationError(item.modelName, error.validationErrors.map((e) => ({ property: propertyName, code: e.code, message: e.message })));
7630
7816
  created.cause = error;
7631
7817
  throw created;
7632
7818
  }
7633
- const _mod_27 = await import('./errors-DLSoqSHG.js');
7819
+ const _mod_27 = await import('./errors-CnT0fSMK.js');
7634
7820
  const _ns_27 = _mod_27.a8;
7635
7821
  const { ItemCreateError } = _ns_27;
7636
7822
  throw new ItemCreateError(`Item.create: failed to save ${item.modelName}.${propertyName} for item ${item.seedLocalId}: ` +
@@ -7715,7 +7901,15 @@ class Item {
7715
7901
  modelName,
7716
7902
  }));
7717
7903
  }
7718
- static async find({ modelName, seedLocalId, seedUid, waitForReady = true, readyTimeout = DEFAULT_READY_TIMEOUT_MS, }) {
7904
+ /**
7905
+ * The Item for a seedLocalId or seedUid, loaded from its rows when it isn't cached. Takes no
7906
+ * cache hold: an item no one holds stays cached for the grace period
7907
+ * (`SeedConfig.entityCache.unloadGraceMs`) after this lookup, then unloads. A caller that keeps
7908
+ * the item longer passes `hold: true` and releases it with `unload()`, or holds it with
7909
+ * `Item.retain` (React hooks do). Returns undefined, keeping no hold, when there is no such item
7910
+ * or it isn't ready in `readyTimeout`.
7911
+ */
7912
+ static async find({ modelName, seedLocalId, seedUid, waitForReady = true, readyTimeout = DEFAULT_READY_TIMEOUT_MS, hold = false, }) {
7719
7913
  if (!seedLocalId && !seedUid) {
7720
7914
  return undefined;
7721
7915
  }
@@ -7725,11 +7919,22 @@ class Item {
7725
7919
  return undefined;
7726
7920
  }
7727
7921
  let cached = false;
7922
+ let held;
7728
7923
  try {
7729
7924
  return await timedDb(`find ${modelName ?? '?'} ${id}`, async () => {
7730
- const held = Item.getById(id) ?? undefined;
7731
- cached = held !== undefined;
7732
- const item = held ?? (await Item.createById(id, modelName, seedLocalId ? 'localId' : undefined));
7925
+ const fromCache = hold ? (Item.getById(id) ?? undefined) : Item.peek(id);
7926
+ if (fromCache && !hold)
7927
+ Item.cache.keepUnheld(fromCache);
7928
+ cached = fromCache !== undefined;
7929
+ let item = fromCache;
7930
+ if (!item) {
7931
+ item = await Item.createById(id, modelName, seedLocalId ? 'localId' : undefined);
7932
+ // createById holds what it returns; without one the item waits out the grace period
7933
+ if (item && !hold)
7934
+ Item.cache.release(item);
7935
+ }
7936
+ if (hold)
7937
+ held = item;
7733
7938
  if (item && waitForReady)
7734
7939
  await waitForEntityReady(item, { timeout: readyTimeout });
7735
7940
  return item;
@@ -7737,26 +7942,46 @@ class Item {
7737
7942
  }
7738
7943
  catch (error) {
7739
7944
  timingLogger(`find ${modelName ?? '?'} ${id} returned undefined: ${error}`);
7945
+ held?.unload();
7740
7946
  return undefined;
7741
7947
  }
7742
7948
  }
7949
+ /**
7950
+ * Takes one more hold on a cached, running instance, released with `unload()`. False (and no
7951
+ * hold) when the instance isn't cached or was stopped.
7952
+ */
7953
+ static retain(instance) {
7954
+ const item = instance;
7955
+ return item.getService().getSnapshot().status === 'active' && this.cache.holdInstance(item);
7956
+ }
7957
+ /**
7958
+ * The items of a model (every model when `modelName` is omitted), newest first. Takes no cache
7959
+ * holds, as `find`: an item no one holds unloads after the grace period. With `hold: true` each
7960
+ * item comes with a hold the caller releases with `unload()`; when the call then throws (aborted,
7961
+ * an item not ready in `readyTimeout`), it keeps none of the holds it took.
7962
+ */
7743
7963
  static async all(modelName, deleted, options) {
7744
- const { waitForReady = false, readyTimeout = DEFAULT_READY_TIMEOUT_MS, includeEas = false, addressFilter, modelFileId, schemaName, signal, } = options ?? {};
7964
+ const { hold = false, waitForReady = false, readyTimeout = DEFAULT_READY_TIMEOUT_MS, includeEas = false, addressFilter, modelFileId, schemaName, signal, } = options ?? {};
7745
7965
  signal?.throwIfAborted();
7746
7966
  const itemsData = await getItemsData({ modelName, modelFileId, schemaName, deleted, includeEas, addressFilter });
7747
7967
  const itemInstances = [];
7748
- // Built without waiting; with waitForReady the items are then awaited together, below.
7749
- for (const itemData of itemsData) {
7968
+ try {
7969
+ // Built without waiting; with waitForReady the items are then awaited together, below.
7970
+ for (const itemData of itemsData) {
7971
+ signal?.throwIfAborted();
7972
+ itemInstances.push(await Item.create({ ...itemData, modelName }, { waitForReady: false, hold }));
7973
+ }
7974
+ if (waitForReady && itemInstances.length > 0) {
7975
+ await Promise.all(itemInstances.map((item) => waitForEntityReady(item, { timeout: readyTimeout })));
7976
+ }
7750
7977
  signal?.throwIfAborted();
7751
- itemInstances.push(await Item.create({
7752
- ...itemData,
7753
- modelName,
7754
- }, { waitForReady: false }));
7755
7978
  }
7756
- if (waitForReady && itemInstances.length > 0) {
7757
- await Promise.all(itemInstances.map((item) => waitForEntityReady(item, { timeout: readyTimeout })));
7979
+ catch (error) {
7980
+ if (hold)
7981
+ for (const item of itemInstances)
7982
+ item.unload();
7983
+ throw error;
7758
7984
  }
7759
- signal?.throwIfAborted();
7760
7985
  return orderBy(itemInstances, ['createdAt'], ['desc']);
7761
7986
  }
7762
7987
  /**
@@ -7769,7 +7994,7 @@ class Item {
7769
7994
  if (!appDb || Item.cache.size === 0) {
7770
7995
  return;
7771
7996
  }
7772
- const _mod_28 = await import('./index-D0V-mjCV.js');
7997
+ const _mod_28 = await import('./index-BjWk0x4-.js');
7773
7998
  const _ns_28 = _mod_28.s;
7774
7999
  const { metadata } = _ns_28;
7775
8000
  const { eq, and } = await import('drizzle-orm');
@@ -7881,11 +8106,11 @@ class Item {
7881
8106
  }
7882
8107
  /** Throws ItemCreateValidationError for unknown keys and values that break their property's rules. */
7883
8108
  static async assertValidCreateValues(props, propertyNames, dataTypeByPropertyName, skipKeys) {
7884
- const _mod_29 = await import('./errors-DLSoqSHG.js');
8109
+ const _mod_29 = await import('./errors-CnT0fSMK.js');
7885
8110
  const _ns_29 = _mod_29.a8;
7886
8111
  const { ItemCreateValidationError } = _ns_29;
7887
- const { getModelPropertyDefinitions, resolvePropertyKey } = await import('./property-BxUYznH0.js');
7888
- const { SchemaValidationService } = await import('./SchemaValidationService-CnmKyzR-.js');
8112
+ const { getModelPropertyDefinitions, resolvePropertyKey } = await import('./property-iNW1N3vm.js');
8113
+ const { SchemaValidationService } = await import('./SchemaValidationService-CieGrj5c.js');
7889
8114
  const validationService = new SchemaValidationService();
7890
8115
  const modelName = props.modelName;
7891
8116
  const known = new Set(propertyNames);
@@ -8239,7 +8464,7 @@ class Item {
8239
8464
  itemActor.send(event);
8240
8465
  };
8241
8466
  try {
8242
- const _mod_30 = await import('./index-D0V-mjCV.js');
8467
+ const _mod_30 = await import('./index-BjWk0x4-.js');
8243
8468
  const _ns_30 = _mod_30.s;
8244
8469
  const seedSchemaMod = _ns_30;
8245
8470
  const { seeds, metadata } = seedSchemaMod;
@@ -8253,8 +8478,8 @@ class Item {
8253
8478
  return;
8254
8479
  }
8255
8480
  // Query initial seed and version data
8256
- const _mod_31 = await import('./tabEvents-CaVmngqr.js');
8257
- const _ns_31 = _mod_31.bd;
8481
+ const _mod_31 = await import('./tabEvents-DT5ez6io.js');
8482
+ const _ns_31 = _mod_31.bg;
8258
8483
  const { findSeedRow } = _ns_31;
8259
8484
  const seed = await findSeedRow({ localId: seedLocalId, uid: seedUid }, db);
8260
8485
  if (!seed) {
@@ -8554,7 +8779,7 @@ class Item {
8554
8779
  return;
8555
8780
  }
8556
8781
  // Through the outbox (ADR 0019): after the item's pending edits, not before them
8557
- const { appendOutboxOpAndWait, startItemOutbox, throwItemWriteError } = await import('./index-CYGWPoOx.js');
8782
+ const { appendOutboxOpAndWait, startItemOutbox, throwItemWriteError } = await import('./index-CZMWKXLM.js');
8558
8783
  startItemOutbox();
8559
8784
  await appendOutboxOpAndWait({
8560
8785
  seedLocalId: context.seedLocalId,
@@ -8581,5 +8806,5 @@ var Item$1 = /*#__PURE__*/Object.freeze({
8581
8806
  Item: Item
8582
8807
  });
8583
8808
 
8584
- export { createReactiveProxy 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, waitForEntityReady as _, appendOutboxOpAndWait as a, findSetConflict as a$, runEntityLoad as a0, evictEntities as a1, entityStatus as a2, releaseEntity as a3, destroyEntity as a4, EntityCache as a5, getGetAdditionalSyncAddresses as a6, modelPropertiesToObject as a7, listRelationEasPropertyName as a8, easPropertyNameForPublish as a9, getVersionData as aA, isLocalUnsealedDraft as aB, isSeedRowOwned as aC, localUnsealedDraftSql as aD, ownedPublisherSql as aE, ownedSeedsSql as aF, pickLatestMetadataRowPerProperty as aG, placeholderUidSql as aH, propertyMachine as aI, publisherInAddressListSql as aJ, publisherIsInOwnedSet as aK, setAdditionalSyncAddresses as aL, setGetPublisherForNewSeeds as aM, setPublishExecutor as aN, setRevokeExecutor as aO, setUploadExecutor as aP, getSeedPublisher as aQ, deleteStagedFile as aR, storageFileName as aS, STORAGE_SUBDIR as aT, resolveNewSeedFields as aU, lookupPropertySchemaUid as aV, stageStorageContent as aW, requireStagedFile as aX, createSeed as aY, createMetadata as aZ, storageValueRowOf as a_, metadataStorageName as aa, resolveMetadataPropertyId as ab, storageSeedTransactionId as ac, storageUploadPath as ad, matchMetadataProperty as ae, metadataRowsOfProperty as af, METADATA_LATEST_FIRST_ORDER_SQL as ag, getEasSchemaUidForModel as ah, isStorageSeedRef as ai, isActorStopped as aj, differsFromOriginals as ak, onEntityTeardown as al, getStorageTransactionIdForSeedUid as am, latestVersionOrder as an, otherVersionRowsWithUid as ao, mergeVersionRowInto as ap, findMetadataPropertyByName as aq, resolvePublisherFromSeedRow as ar, PUBLISH_NOT_CONFIGURED_MESSAGE as as, createNewItem as at, getArweaveUrlForTransaction as au, getGetPublisherForNewSeeds as av, getMetadataLatest as aw, getPublishExecutor as ax, getRevokeExecutor as ay, getUploadExecutor as az, waitForPendingOutboxOps as b, recordPropertyEdit as b0, currentPropertyRow$1 as b1, resolve as b2, conflicts as b3, propertySchemaUid as b4, Item$1 as b5, 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 };
8585
- //# sourceMappingURL=Item-Bf4kWKpH.js.map
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