@seedprotocol/sdk 0.7.3 → 0.7.4-next.784.g879c6a3

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 (127) hide show
  1. package/dist/{BasePathResolver-Cm4apKRb.js → BasePathResolver-DxdS0wJ0.js} +43 -8
  2. package/dist/BasePathResolver-DxdS0wJ0.js.map +1 -0
  3. package/dist/{ClientManager-i3nJASkg.js → ClientManager-De-vKHGe.js} +37 -38
  4. package/dist/ClientManager-De-vKHGe.js.map +1 -0
  5. package/dist/{Item-DBFchXpC.js → Item-cq9VcsAY.js} +365 -191
  6. package/dist/Item-cq9VcsAY.js.map +1 -0
  7. package/dist/{ModelProperty-BS49g01Y.js → ModelProperty-DuuoFXaC.js} +45 -37
  8. package/dist/ModelProperty-DuuoFXaC.js.map +1 -0
  9. package/dist/{Schema-CpMNEnUO.js → Schema-Dc5EA8Yb.js} +246 -68
  10. package/dist/Schema-Dc5EA8Yb.js.map +1 -0
  11. package/dist/{SchemaValidationService-DieV4KL-.js → SchemaValidationService-DViJ056F.js} +49 -36
  12. package/dist/SchemaValidationService-DViJ056F.js.map +1 -0
  13. package/dist/{backfillMetadataPropertyIds-DSoOoTnX.js → backfillMetadataPropertyIds-DacVthS5.js} +3 -3
  14. package/dist/{backfillMetadataPropertyIds-DSoOoTnX.js.map → backfillMetadataPropertyIds-DacVthS5.js.map} +1 -1
  15. package/dist/{configurePlatform-D0lQGAMQ.js → configurePlatform-DlZ23w7e.js} +2 -2
  16. package/dist/{configurePlatform-D0lQGAMQ.js.map → configurePlatform-DlZ23w7e.js.map} +1 -1
  17. package/dist/{detachRowsBeforeDelete-S0q-DJPT.js → detachRowsBeforeDelete-YRSZY6dO.js} +2 -2
  18. package/dist/{detachRowsBeforeDelete-S0q-DJPT.js.map → detachRowsBeforeDelete-YRSZY6dO.js.map} +1 -1
  19. package/dist/{eas-6pjU-OuX.js → eas-D_6ptL_F.js} +4 -4
  20. package/dist/{eas-6pjU-OuX.js.map → eas-D_6ptL_F.js.map} +1 -1
  21. package/dist/{index-8O3Uow-Y.js → errors-Cl8rbtKR.js} +192 -48
  22. package/dist/errors-Cl8rbtKR.js.map +1 -0
  23. package/dist/{getItem-CxBNZ-WL.js → getItem-BcmJcUZB.js} +5 -5
  24. package/dist/{getItem-CxBNZ-WL.js.map → getItem-BcmJcUZB.js.map} +1 -1
  25. package/dist/{getPublishPayload-CTCvRUoU.js → getPublishPayload-C278kK9P.js} +15 -15
  26. package/dist/{getPublishPayload-CTCvRUoU.js.map → getPublishPayload-C278kK9P.js.map} +1 -1
  27. package/dist/{getPublishUploads-SFHCbf_y.js → getPublishUploads-DJ1UPh7i.js} +9 -9
  28. package/dist/{getPublishUploads-SFHCbf_y.js.map → getPublishUploads-DJ1UPh7i.js.map} +1 -1
  29. package/dist/{index-Cvt5UBz4.js → index-CqnU-5i0.js} +9 -9
  30. package/dist/index-CqnU-5i0.js.map +1 -0
  31. package/dist/{index-AVFTVp1m.js → index-DAl9U4rn.js} +2 -2
  32. package/dist/index-DAl9U4rn.js.map +1 -0
  33. package/dist/{index.browser-DBhxRoe8.js → index.browser-J6QZ9smP.js} +10 -10
  34. package/dist/{index.browser-DBhxRoe8.js.map → index.browser-J6QZ9smP.js.map} +1 -1
  35. package/dist/{index.node-CJhlcNHM.js → index.node-C9VB9NlX.js} +9 -9
  36. package/dist/index.node-C9VB9NlX.js.map +1 -0
  37. package/dist/main.js +24 -24
  38. package/dist/{model-DGMe-TTm.js → model-oHA3YhOd.js} +5 -1
  39. package/dist/model-oHA3YhOd.js.map +1 -0
  40. package/dist/node.js +11 -11
  41. package/dist/node.js.map +1 -1
  42. package/dist/platform.browser.js +5 -5
  43. package/dist/platform.node.js +4 -4
  44. package/dist/{property-DwPD283A.js → property-Brgr5RdT.js} +11 -11
  45. package/dist/{property-DwPD283A.js.map → property-Brgr5RdT.js.map} +1 -1
  46. package/dist/propertyValidationRules-CDRitiiK.js +51 -0
  47. package/dist/propertyValidationRules-CDRitiiK.js.map +1 -0
  48. package/dist/{publishDraftGraph-BOn3_Occ.js → publishDraftGraph-C37ibxb5.js} +6 -6
  49. package/dist/{publishDraftGraph-BOn3_Occ.js.map → publishDraftGraph-C37ibxb5.js.map} +1 -1
  50. package/dist/{resetAmbiguousLegacyItemData-BkYmBeJc.js → resetAmbiguousLegacyItemData-CcLFRREE.js} +2 -2
  51. package/dist/{resetAmbiguousLegacyItemData-BkYmBeJc.js.map → resetAmbiguousLegacyItemData-CcLFRREE.js.map} +1 -1
  52. package/dist/{resolveModelForSyncedSeed-BL24nLa7.js → resolveModelForSyncedSeed-BTAeCOJE.js} +2 -2
  53. package/dist/{resolveModelForSyncedSeed-BL24nLa7.js.map → resolveModelForSyncedSeed-BTAeCOJE.js.map} +1 -1
  54. package/dist/src/Item/Item.d.ts +6 -2
  55. package/dist/src/Item/Item.d.ts.map +1 -1
  56. package/dist/src/Item/errors.d.ts +23 -0
  57. package/dist/src/Item/errors.d.ts.map +1 -0
  58. package/dist/src/ItemProperty/ItemProperty.d.ts.map +1 -1
  59. package/dist/src/ItemProperty/service/actors/hydrateFromDb.d.ts.map +1 -1
  60. package/dist/src/ItemProperty/service/actors/loadOrCreateProperty.d.ts.map +1 -1
  61. package/dist/src/ItemProperty/service/actors/saveValueToDb/analyzeInput.d.ts.map +1 -1
  62. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveFile.d.ts +19 -0
  63. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveFile.d.ts.map +1 -1
  64. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveImage.d.ts.map +1 -1
  65. package/dist/src/ItemProperty/service/actors/saveValueToDb/saveItemStorage.d.ts.map +1 -1
  66. package/dist/src/ItemProperty/service/propertyMachine.d.ts.map +1 -1
  67. package/dist/src/Model/Model.d.ts +2 -0
  68. package/dist/src/Model/Model.d.ts.map +1 -1
  69. package/dist/src/Model/service/actors/createModelProperties.d.ts.map +1 -1
  70. package/dist/src/ModelProperty/ModelProperty.d.ts +2 -0
  71. package/dist/src/ModelProperty/ModelProperty.d.ts.map +1 -1
  72. package/dist/src/Schema/Schema.d.ts +11 -2
  73. package/dist/src/Schema/Schema.d.ts.map +1 -1
  74. package/dist/src/Schema/service/actors/checkExistingSchema.d.ts.map +1 -1
  75. package/dist/src/Schema/service/actors/loadOrCreateSchema.d.ts.map +1 -1
  76. package/dist/src/Schema/service/schemaMachine.d.ts.map +1 -1
  77. package/dist/src/Schema/service/validation/SchemaValidationService.d.ts +4 -1
  78. package/dist/src/Schema/service/validation/SchemaValidationService.d.ts.map +1 -1
  79. package/dist/src/db/Db/BaseDb.d.ts +5 -2
  80. package/dist/src/db/Db/BaseDb.d.ts.map +1 -1
  81. package/dist/src/db/write/updateItemPropertyValue.d.ts.map +1 -1
  82. package/dist/src/events/item/syncDbWithEas.d.ts.map +1 -1
  83. package/dist/src/helpers/db.d.ts.map +1 -1
  84. package/dist/src/helpers/entity/draftSaveErrors.d.ts +4 -0
  85. package/dist/src/helpers/entity/draftSaveErrors.d.ts.map +1 -0
  86. package/dist/src/helpers/entity/entityUnload.d.ts +2 -1
  87. package/dist/src/helpers/entity/entityUnload.d.ts.map +1 -1
  88. package/dist/src/helpers/entity/evictionEpoch.d.ts +27 -2
  89. package/dist/src/helpers/entity/evictionEpoch.d.ts.map +1 -1
  90. package/dist/src/helpers/isSqliteUniqueConstraintError.d.ts.map +1 -1
  91. package/dist/src/helpers/model.d.ts.map +1 -1
  92. package/dist/src/helpers/propertyValidationRules.d.ts +12 -0
  93. package/dist/src/helpers/propertyValidationRules.d.ts.map +1 -0
  94. package/dist/src/helpers/propertyWriteQueue.d.ts +15 -0
  95. package/dist/src/helpers/propertyWriteQueue.d.ts.map +1 -0
  96. package/dist/src/helpers/schema.d.ts.map +1 -1
  97. package/dist/src/helpers/storageFileName.d.ts +7 -0
  98. package/dist/src/helpers/storageFileName.d.ts.map +1 -0
  99. package/dist/src/index.d.ts +1 -0
  100. package/dist/src/index.d.ts.map +1 -1
  101. package/dist/src/node/db/Db.d.ts +1 -1
  102. package/dist/src/node/db/Db.d.ts.map +1 -1
  103. package/dist/src/node/index.d.ts +1 -0
  104. package/dist/src/node/index.d.ts.map +1 -1
  105. package/dist/src/types/index.d.ts +12 -0
  106. package/dist/src/types/index.d.ts.map +1 -1
  107. package/dist/{syncDbWithEas-Bv2Fe38J.js → syncDbWithEas-B-Ho0IOB.js} +50 -27
  108. package/dist/syncDbWithEas-B-Ho0IOB.js.map +1 -0
  109. package/dist/{tabEvents-9s2WCYw-.js → tabEvents-BTzckWrN.js} +4 -4
  110. package/dist/{tabEvents-9s2WCYw-.js.map → tabEvents-BTzckWrN.js.map} +1 -1
  111. package/dist/{tolerantWebAccess-iUu80pAP.js → tolerantWebAccess-DOm2Xv-f.js} +6 -6
  112. package/dist/{tolerantWebAccess-iUu80pAP.js.map → tolerantWebAccess-DOm2Xv-f.js.map} +1 -1
  113. package/dist/{updateSeedRevokedAt-zBfi931j.js → updateSeedRevokedAt-IHXnJI-i.js} +3 -3
  114. package/dist/{updateSeedRevokedAt-zBfi931j.js.map → updateSeedRevokedAt-IHXnJI-i.js.map} +1 -1
  115. package/package.json +5 -5
  116. package/dist/BasePathResolver-Cm4apKRb.js.map +0 -1
  117. package/dist/ClientManager-i3nJASkg.js.map +0 -1
  118. package/dist/Item-DBFchXpC.js.map +0 -1
  119. package/dist/ModelProperty-BS49g01Y.js.map +0 -1
  120. package/dist/Schema-CpMNEnUO.js.map +0 -1
  121. package/dist/SchemaValidationService-DieV4KL-.js.map +0 -1
  122. package/dist/index-8O3Uow-Y.js.map +0 -1
  123. package/dist/index-AVFTVp1m.js.map +0 -1
  124. package/dist/index-Cvt5UBz4.js.map +0 -1
  125. package/dist/index.node-CJhlcNHM.js.map +0 -1
  126. package/dist/model-DGMe-TTm.js.map +0 -1
  127. package/dist/syncDbWithEas-Bv2Fe38J.js.map +0 -1
@@ -1,10 +1,10 @@
1
1
  import { fromCallback, fromPromise, setup, assign, createActor } from 'xstate';
2
- import { E as BaseDb, s as models, u as properties, $ as generateId, aF as isInternalSchema, aG as writeModelToDb, aH as writePropertyToDb, aI as addSchemaToDb, q as modelSchemas, z as schemas, F as AmbiguousModelError, aJ as renameModelInDb, a as TProperty } from './BasePathResolver-Cm4apKRb.js';
2
+ import { E as BaseDb, s as models, u as properties, $ as generateId, aF as isInternalSchema, aG as writeModelToDb, aH as writePropertyToDb, aI as addSchemaToDb, q as modelSchemas, z as schemas, F as AmbiguousModelError, aJ as renameModelInDb, a as TProperty } from './BasePathResolver-DxdS0wJ0.js';
3
3
  import { eq, and, isNull, or } from 'drizzle-orm';
4
4
  import debug from 'debug';
5
- import { n as waitForEntityIdle, D as DEFAULT_READY_TIMEOUT_MS, $ as getInvariantSafePropertyDescriptor, q as findEntity, C as ClientManagerState, I as Item, a0 as clearDestroySubscriptions, t as forceRemoveFromCaches, a1 as unloadEntity, u as runDestroyLifecycle } from './Item-DBFchXpC.js';
6
- import { k as getClient } from './ClientManager-i3nJASkg.js';
7
- import { modelPropertiesToObject } from './model-DGMe-TTm.js';
5
+ import { n as waitForEntityIdle, D as DEFAULT_READY_TIMEOUT_MS, $ as getInvariantSafePropertyDescriptor, q as findEntity, C as ClientManagerState, I as Item, a0 as clearDestroySubscriptions, t as forceRemoveFromCaches, a1 as unloadEntity, u as runDestroyLifecycle } from './Item-cq9VcsAY.js';
6
+ import { k as getClient } from './ClientManager-De-vKHGe.js';
7
+ import { modelPropertiesToObject } from './model-oHA3YhOd.js';
8
8
  import { toSnakeCase } from 'drizzle-orm/casing';
9
9
  import { Type } from '@sinclair/typebox';
10
10
 
@@ -35,7 +35,7 @@ const createPropertyInstances = async (propertyFileIds) => {
35
35
  return;
36
36
  }
37
37
  try {
38
- const mod = await import('./ModelProperty-BS49g01Y.js');
38
+ const mod = await import('./ModelProperty-DuuoFXaC.js');
39
39
  const ModelProperty = mod?.ModelProperty ?? mod?.default;
40
40
  if (!ModelProperty) {
41
41
  logger$7('createPropertyInstances: ModelProperty not available from dynamic import');
@@ -101,11 +101,11 @@ const loadOrCreateModel = fromCallback(({ sendBack, input: { context }, self })
101
101
  if (!modelRecord) {
102
102
  // Model names are only unique per schema: prefer this schema's row, and otherwise only
103
103
  // consider same-name rows that no schema claims yet (never another schema's model).
104
- const _mod_0 = await import('./BasePathResolver-Cm4apKRb.js');
105
- const _ns_0 = _mod_0.b0;
104
+ const _mod_0 = await import('./BasePathResolver-DxdS0wJ0.js');
105
+ const _ns_0 = _mod_0.b1;
106
106
  const { modelSchemas } = _ns_0;
107
- const _mod_1 = await import('./BasePathResolver-Cm4apKRb.js');
108
- const _ns_1 = _mod_1.a$;
107
+ const _mod_1 = await import('./BasePathResolver-DxdS0wJ0.js');
108
+ const _ns_1 = _mod_1.b0;
109
109
  const { schemas: schemasTable } = _ns_1;
110
110
  let dbModels = await db
111
111
  .select({
@@ -252,7 +252,7 @@ const loadOrCreateModel = fromCallback(({ sendBack, input: { context }, self })
252
252
  // Step 2: Fallback to Schema context (only if database doesn't have the model)
253
253
  // This handles the case where model exists in schema file but not yet in database
254
254
  try {
255
- const _mod_2 = await import('./Schema-CpMNEnUO.js');
255
+ const _mod_2 = await import('./Schema-Dc5EA8Yb.js');
256
256
  const _ns_2 = _mod_2.b;
257
257
  const schemaMod = _ns_2;
258
258
  const { Schema } = schemaMod;
@@ -403,12 +403,12 @@ const loadOrCreateModel = fromCallback(({ sendBack, input: { context }, self })
403
403
  }
404
404
  // Also check database for models (in case they're persisted but not in Schema context)
405
405
  if (db) {
406
- const _mod_3 = await import('./BasePathResolver-Cm4apKRb.js');
407
- const _ns_3 = _mod_3.b0;
406
+ const _mod_3 = await import('./BasePathResolver-DxdS0wJ0.js');
407
+ const _ns_3 = _mod_3.b1;
408
408
  const modelSchemaSchemaMod = _ns_3;
409
409
  const { modelSchemas } = modelSchemaSchemaMod;
410
- const _mod_4 = await import('./BasePathResolver-Cm4apKRb.js');
411
- const _ns_4 = _mod_4.a$;
410
+ const _mod_4 = await import('./BasePathResolver-DxdS0wJ0.js');
411
+ const _ns_4 = _mod_4.b0;
412
412
  const schemaSchemaMod = _ns_4;
413
413
  const { schemas: schemasTable } = schemaSchemaMod;
414
414
  // Query all models for this schema to check for duplicates (case-insensitive)
@@ -503,7 +503,7 @@ const loadOrCreateModel = fromCallback(({ sendBack, input: { context }, self })
503
503
  }
504
504
  // Mark schema as draft when a new model is created so saveNewVersion() can persist it
505
505
  try {
506
- const _mod_5 = await import('./Schema-CpMNEnUO.js');
506
+ const _mod_5 = await import('./Schema-Dc5EA8Yb.js');
507
507
  const _ns_5 = _mod_5.b;
508
508
  const schemaMod = _ns_5;
509
509
  const { Schema } = schemaMod;
@@ -551,10 +551,10 @@ const validateModel = fromCallback(({ sendBack, input: { context }, self }) => {
551
551
  const _validateModel = async () => {
552
552
  try {
553
553
  // Use dynamic imports to break circular dependencies
554
- const validationServiceMod = await import('./SchemaValidationService-DieV4KL-.js');
554
+ const validationServiceMod = await import('./SchemaValidationService-DViJ056F.js');
555
555
  const { SchemaValidationService } = validationServiceMod;
556
556
  const validationService = new SchemaValidationService();
557
- const _mod_6 = await import('./Schema-CpMNEnUO.js');
557
+ const _mod_6 = await import('./Schema-Dc5EA8Yb.js');
558
558
  const _ns_6 = _mod_6.b;
559
559
  const schemaMod = _ns_6;
560
560
  const { Schema } = schemaMod;
@@ -673,7 +673,7 @@ const createModelProperties = fromCallback(({ sendBack, input, self }) => {
673
673
  return;
674
674
  }
675
675
  logger$5(`Creating ${Object.keys(propertyDefinitions).length} properties for model "${modelName}" (id: ${_dbId})`);
676
- const mod = await import('./ModelProperty-BS49g01Y.js');
676
+ const mod = await import('./ModelProperty-DuuoFXaC.js');
677
677
  const ModelProperty = mod?.ModelProperty ?? mod?.default;
678
678
  if (!ModelProperty) {
679
679
  logger$5('ModelProperty not available from dynamic import');
@@ -698,8 +698,8 @@ const createModelProperties = fromCallback(({ sendBack, input, self }) => {
698
698
  : undefined;
699
699
  // Otherwise use provided ID or generate a random one
700
700
  // IDs should be generated in the import process before creating properties
701
- const _mod_7 = await import('./BasePathResolver-Cm4apKRb.js');
702
- const _ns_7 = _mod_7.b4;
701
+ const _mod_7 = await import('./BasePathResolver-DxdS0wJ0.js');
702
+ const _ns_7 = _mod_7.b5;
703
703
  const { generateId } = _ns_7;
704
704
  const propertyFileId = propData.id || propertyRecord?.schemaFileId || generateId();
705
705
  logger$5(`Creating property "${propName}" with fileId "${propertyFileId}"`);
@@ -716,8 +716,8 @@ const createModelProperties = fromCallback(({ sendBack, input, self }) => {
716
716
  }
717
717
  else if (refModelName) {
718
718
  // Fallback: resolve the ref by name within this model's schema
719
- const _mod_8 = await import('./BasePathResolver-Cm4apKRb.js');
720
- const _ns_8 = _mod_8.b1;
719
+ const _mod_8 = await import('./BasePathResolver-DxdS0wJ0.js');
720
+ const _ns_8 = _mod_8.b2;
721
721
  const { resolveModelRecord } = _ns_8;
722
722
  const refModelRecord = await resolveModelRecord(refModelName, { schemaName }, db);
723
723
  if (refModelRecord?.id) {
@@ -754,6 +754,10 @@ const createModelProperties = fromCallback(({ sendBack, input, self }) => {
754
754
  localStorageDir: propData.localStorageDir ?? propertyRecord?.localStorageDir ?? undefined,
755
755
  filenameSuffix: propData.filenameSuffix ?? propertyRecord?.filenameSuffix ?? undefined,
756
756
  required: propData.required,
757
+ // Rules defined in code live only on the instance (the properties table has no column
758
+ // for them); getPropertySchema reads them from here when no schema file has them.
759
+ validation: propData.validation,
760
+ htmlEmbeddedDataUriPolicy: propData.htmlEmbeddedDataUriPolicy,
757
761
  _propertyFileId: propertyFileId, // Store schemaFileId for getById() lookups
758
762
  _dbId: propertyRecord?.schemaFileId === propertyFileId ? propertyRecord.id : undefined, // Already persisted: skip the write
759
763
  }, { schemaName }); // Use 'as any' because _propertyFileId is not in TProperty type
@@ -785,6 +789,12 @@ const createModelProperties = fromCallback(({ sendBack, input, self }) => {
785
789
  */
786
790
  let epoch = 0;
787
791
  const lastEvictionBySchema = new Map();
792
+ // Schemas whose destroy() is still running (by name)
793
+ const destroyingSchemas = new Map();
794
+ // Single models destroyed (Model.destroy), by modelFileId
795
+ const lastDestroyByModelId = new Map();
796
+ // Models whose destroy() is still deleting their rows
797
+ const destroyingModelIds = new Map();
788
798
  function currentEvictionEpoch() {
789
799
  return epoch;
790
800
  }
@@ -793,15 +803,73 @@ function recordSchemaEviction(schemaName) {
793
803
  epoch += 1;
794
804
  lastEvictionBySchema.set(schemaName, epoch);
795
805
  }
796
- /** True when the schema's instances were evicted after `since` (an epoch from currentEvictionEpoch). */
806
+ /**
807
+ * Called by Schema.destroy before it evicts the schema's models; `finishSchemaDestroy` once its rows
808
+ * are gone. The schema's actor stays live until then, and work it starts meanwhile (its live query
809
+ * reading a model row the drained write just landed) captures an epoch after the eviction, so the
810
+ * epoch alone can't tell it to stay away.
811
+ */
812
+ function recordSchemaDestroy(schemaName) {
813
+ recordSchemaEviction(schemaName);
814
+ destroyingSchemas.set(schemaName, (destroyingSchemas.get(schemaName) ?? 0) + 1);
815
+ }
816
+ function finishSchemaDestroy(schemaName) {
817
+ const remaining = (destroyingSchemas.get(schemaName) ?? 1) - 1;
818
+ if (remaining > 0)
819
+ destroyingSchemas.set(schemaName, remaining);
820
+ else
821
+ destroyingSchemas.delete(schemaName);
822
+ // Work that read a row during the destroy and checks after it must still see the eviction
823
+ recordSchemaEviction(schemaName);
824
+ }
825
+ /**
826
+ * True when the schema is being destroyed, or its instances were evicted after `since` (an epoch
827
+ * from currentEvictionEpoch).
828
+ */
797
829
  function schemaEvictedSince(schemaName, since) {
798
- if (epoch === since || !schemaName)
830
+ if (!schemaName)
831
+ return false;
832
+ if (destroyingSchemas.has(schemaName))
833
+ return true;
834
+ if (epoch === since)
799
835
  return false;
800
836
  return (lastEvictionBySchema.get(schemaName) ?? 0) > since;
801
837
  }
802
- /** True when any schema was evicted after `since` (cheap check before looking up which schemas apply). */
838
+ /**
839
+ * Called by Model.destroy before it clears the caches; `finishModelDestroy` once its rows are gone.
840
+ * Until then a lookup by id still finds the row, so it must not re-cache the model either.
841
+ */
842
+ function recordModelDestroy(modelFileId) {
843
+ epoch += 1;
844
+ lastDestroyByModelId.set(modelFileId, epoch);
845
+ destroyingModelIds.set(modelFileId, (destroyingModelIds.get(modelFileId) ?? 0) + 1);
846
+ }
847
+ function finishModelDestroy(modelFileId) {
848
+ const remaining = (destroyingModelIds.get(modelFileId) ?? 1) - 1;
849
+ if (remaining > 0)
850
+ destroyingModelIds.set(modelFileId, remaining);
851
+ else
852
+ destroyingModelIds.delete(modelFileId);
853
+ }
854
+ /**
855
+ * True when the model is being destroyed, or was destroyed after `since` (an epoch from
856
+ * currentEvictionEpoch): a lookup that read its row must not create or cache an instance.
857
+ */
858
+ function modelDestroyedSince(modelFileId, since) {
859
+ if (!modelFileId)
860
+ return false;
861
+ if (destroyingModelIds.has(modelFileId))
862
+ return true;
863
+ if (epoch === since)
864
+ return false;
865
+ return (lastDestroyByModelId.get(modelFileId) ?? 0) > since;
866
+ }
867
+ /**
868
+ * True when any schema was evicted after `since` or is being destroyed (cheap check before looking up
869
+ * which schemas apply).
870
+ */
803
871
  function anyEvictionSince(since) {
804
- return epoch !== since;
872
+ return epoch !== since || destroyingSchemas.size > 0;
805
873
  }
806
874
 
807
875
  const logger$4 = debug('seedSdk:write:validateEntity');
@@ -817,7 +885,7 @@ const validateEntity = fromPromise(async ({ input }) => {
817
885
  const evictionEpoch = currentEvictionEpoch();
818
886
  /** The schema to validate against; throws (validation then skips the schema check) once it was evicted. */
819
887
  const schemaForValidation = async (schemaName) => {
820
- const _mod_9 = await import('./Schema-CpMNEnUO.js');
888
+ const _mod_9 = await import('./Schema-Dc5EA8Yb.js');
821
889
  const _ns_9 = _mod_9.b;
822
890
  const { Schema } = _ns_9;
823
891
  if (schemaEvictedSince(schemaName, evictionEpoch)) {
@@ -835,7 +903,7 @@ const validateEntity = fromPromise(async ({ input }) => {
835
903
  const structureMsg = `[validateEntity] Validating model structure`;
836
904
  logger$4(structureMsg);
837
905
  // Use existing Model validation
838
- const validationServiceMod = await import('./SchemaValidationService-DieV4KL-.js');
906
+ const validationServiceMod = await import('./SchemaValidationService-DViJ056F.js');
839
907
  const { SchemaValidationService } = validationServiceMod;
840
908
  const validationService = new SchemaValidationService();
841
909
  // Validate model structure
@@ -885,7 +953,7 @@ const validateEntity = fromPromise(async ({ input }) => {
885
953
  }
886
954
  else if (entityInput.entityType === 'modelProperty') {
887
955
  // Use existing ModelProperty validation
888
- const validationServiceMod = await import('./SchemaValidationService-DieV4KL-.js');
956
+ const validationServiceMod = await import('./SchemaValidationService-DViJ056F.js');
889
957
  const { SchemaValidationService } = validationServiceMod;
890
958
  const validationService = new SchemaValidationService();
891
959
  // Validate property structure
@@ -925,7 +993,7 @@ const validateEntity = fromPromise(async ({ input }) => {
925
993
  }
926
994
  else if (entityInput.entityType === 'schema') {
927
995
  // Schema validation - use existing validation
928
- const validationServiceMod = await import('./SchemaValidationService-DieV4KL-.js');
996
+ const validationServiceMod = await import('./SchemaValidationService-DViJ056F.js');
929
997
  const { SchemaValidationService } = validationServiceMod;
930
998
  const validationService = new SchemaValidationService();
931
999
  const schemaResult = validationService.validateSchema(entityInput.entityData);
@@ -1014,7 +1082,7 @@ const writeToDatabase = fromCallback(({ sendBack, input }) => {
1014
1082
  // initial write completes is not overwritten by the stale requestWrite payload.
1015
1083
  let dataToWrite = input.entityData;
1016
1084
  try {
1017
- const mod = await import('./ModelProperty-BS49g01Y.js');
1085
+ const mod = await import('./ModelProperty-DuuoFXaC.js');
1018
1086
  const ModelProperty = mod?.ModelProperty ?? mod?.default;
1019
1087
  if (ModelProperty && typeof ModelProperty.getById === 'function') {
1020
1088
  const instance = ModelProperty.getById(input.entityId);
@@ -1408,7 +1476,7 @@ const modelMachine = setup({
1408
1476
  // Create ModelProperty instances for any new property IDs
1409
1477
  if (Array.isArray(newPropertyIds) && newPropertyIds.length > 0) {
1410
1478
  // Import and create instances asynchronously (fire-and-forget)
1411
- import('./ModelProperty-BS49g01Y.js').then(({ ModelProperty }) => {
1479
+ import('./ModelProperty-DuuoFXaC.js').then(({ ModelProperty }) => {
1412
1480
  if (self.getSnapshot().status === 'stopped')
1413
1481
  return;
1414
1482
  const createPromises = newPropertyIds.map(async (propertyFileId) => {
@@ -1908,12 +1976,33 @@ const getModelsData = async (schemaIdentifier) => {
1908
1976
  return rows;
1909
1977
  };
1910
1978
 
1979
+ /**
1980
+ * Errors from draft saves that assignment starts in the background (schema/model renames and
1981
+ * tracked-property edits). Nothing awaits those saves, so the error is kept for `lastSaveError`
1982
+ * instead of only being logged. Keyed by the instance's service, which the instance and its
1983
+ * proxy share.
1984
+ */
1985
+ const draftSaveErrors = new WeakMap();
1986
+ function getDraftSaveError(service) {
1987
+ return draftSaveErrors.get(service);
1988
+ }
1989
+ /** Run a background draft save, recording its failure (or clearing an earlier one on success). */
1990
+ function trackDraftSave(service, save, label) {
1991
+ save.then(() => {
1992
+ draftSaveErrors.delete(service);
1993
+ }, (error) => {
1994
+ const recorded = error instanceof Error ? error : new Error(String(error));
1995
+ draftSaveErrors.set(service, recorded);
1996
+ console.warn(`[seed] ${label}: draft save failed (see lastSaveError):`, recorded.message);
1997
+ });
1998
+ }
1999
+
1911
2000
  const logger = debug('seedSdk:model:Model');
1912
2001
  const saveDraftLogger = debug('seedSdk:model:saveDraftToDb');
1913
2002
  // Lazy import cache for ModelProperty to avoid circular dependency
1914
2003
  // Eagerly start loading to minimize delay on first access
1915
2004
  let ModelPropertyClass = null;
1916
- const modelPropertyImportPromise = import('./ModelProperty-BS49g01Y.js')
2005
+ const modelPropertyImportPromise = import('./ModelProperty-DuuoFXaC.js')
1917
2006
  .then(module => {
1918
2007
  ModelPropertyClass = module.ModelProperty;
1919
2008
  return ModelPropertyClass;
@@ -2356,9 +2445,7 @@ class Model {
2356
2445
  propertyKey: 'modelName',
2357
2446
  });
2358
2447
  // Save draft to database immediately so changes persist
2359
- target._saveDraftToDb(oldName, newName).catch((error) => {
2360
- logger(`Failed to save draft to database: ${error instanceof Error ? error.message : String(error)}`);
2361
- });
2448
+ trackDraftSave(target._service, target._saveDraftToDb(oldName, newName), `Model "${newName}"`);
2362
2449
  // NOTE: Schema is not notified of model name changes during edits.
2363
2450
  // Schema will read from Model instances when persisting/saving.
2364
2451
  }
@@ -2868,9 +2955,11 @@ class Model {
2868
2955
  .limit(1);
2869
2956
  const record = rows[0];
2870
2957
  if (record?.modelName && record.schemaName && record.modelFileId) {
2871
- // Its schema was evicted while we read the row: don't bring the model back.
2958
+ // Its schema was evicted, or it was destroyed, while we read the row: don't bring it back.
2872
2959
  if (schemaEvictedSince(record.schemaName, evictionEpoch))
2873
2960
  return undefined;
2961
+ if (modelDestroyedSince(record.modelFileId, evictionEpoch))
2962
+ return undefined;
2874
2963
  return this.create(record.modelName, record.schemaName, { id: record.modelFileId });
2875
2964
  }
2876
2965
  }
@@ -2904,8 +2993,8 @@ class Model {
2904
2993
  const evictionEpoch = options?.evictionEpoch ?? currentEvictionEpoch();
2905
2994
  try {
2906
2995
  // Query model by name (throws AmbiguousModelError when several schemas define it)
2907
- const _mod_10 = await import('./BasePathResolver-Cm4apKRb.js');
2908
- const _ns_10 = _mod_10.b1;
2996
+ const _mod_10 = await import('./BasePathResolver-DxdS0wJ0.js');
2997
+ const _ns_10 = _mod_10.b2;
2909
2998
  const { resolveModelRecord } = _ns_10;
2910
2999
  const resolved = await resolveModelRecord(modelName, {}, db);
2911
3000
  if (!resolved?.schemaFileId) {
@@ -2929,10 +3018,13 @@ class Model {
2929
3018
  if (!record.modelName || !record.schemaName || !record.modelFileId) {
2930
3019
  return undefined;
2931
3020
  }
2932
- // Its schema was evicted while we read the row: don't bring the model back.
3021
+ // Its schema was evicted, or it was destroyed, while we read the row: don't bring it back.
2933
3022
  if (schemaEvictedSince(record.schemaName, evictionEpoch)) {
2934
3023
  return undefined;
2935
3024
  }
3025
+ if (modelDestroyedSince(record.modelFileId, evictionEpoch)) {
3026
+ return undefined;
3027
+ }
2936
3028
  // Create model instance (will be cached)
2937
3029
  return this.create(record.modelName, record.schemaName, {
2938
3030
  id: record.modelFileId, // id is now the schemaFileId (string)
@@ -2999,6 +3091,11 @@ class Model {
2999
3091
  logger(`Model.createById: schema "${schemaName}" was evicted during the lookup of "${modelFileId}"`);
3000
3092
  return undefined;
3001
3093
  }
3094
+ // Destroyed while we read the row: destroy() clears the caches before it deletes the rows
3095
+ if (modelDestroyedSince(modelFileId, evictionEpoch)) {
3096
+ logger(`Model.createById: model "${modelFileId}" was destroyed during the lookup`);
3097
+ return undefined;
3098
+ }
3002
3099
  // Step 3: Create model using existing create method (which will handle caching)
3003
3100
  logger(`Model.createById: Creating model "${modelName}" in schema "${schemaName}" from ID "${modelFileId}"`);
3004
3101
  return this.create(modelName, schemaName, {
@@ -3076,10 +3173,13 @@ class Model {
3076
3173
  if (!record.modelName || !record.schemaName || !record.modelFileId) {
3077
3174
  continue;
3078
3175
  }
3079
- // Its schema was evicted while we read the rows: don't bring its models back.
3176
+ // Its schema was evicted, or the model destroyed, while we read the rows: don't bring it back.
3080
3177
  if (schemaEvictedSince(record.schemaName, evictionEpoch)) {
3081
3178
  continue;
3082
3179
  }
3180
+ if (modelDestroyedSince(record.modelFileId, evictionEpoch)) {
3181
+ continue;
3182
+ }
3083
3183
  // Check cache first
3084
3184
  const cached = this.getById(record.modelFileId);
3085
3185
  if (cached) {
@@ -3373,6 +3473,10 @@ class Model {
3373
3473
  get status() {
3374
3474
  return this._getSnapshot().value;
3375
3475
  }
3476
+ /** The error from the last draft save a rename started, or undefined once a later one succeeds. */
3477
+ get lastSaveError() {
3478
+ return getDraftSaveError(this._service);
3479
+ }
3376
3480
  get validationErrors() {
3377
3481
  return this._getSnapshotContext()._validationErrors || [];
3378
3482
  }
@@ -3611,6 +3715,9 @@ class Model {
3611
3715
  const schemaName = context.schemaName;
3612
3716
  const nameKey = schemaName && modelName ? `${schemaName}:${modelName}` : '';
3613
3717
  clearDestroySubscriptions(this, { instanceState: modelInstanceState });
3718
+ // Before the caches are cleared, so a lookup by id already reading the row doesn't re-cache it
3719
+ if (modelFileId)
3720
+ recordModelDestroy(modelFileId);
3614
3721
  const cacheKeys = [];
3615
3722
  if (modelFileId)
3616
3723
  cacheKeys.push(modelFileId);
@@ -3624,7 +3731,7 @@ class Model {
3624
3731
  Model.instanceCache,
3625
3732
  ],
3626
3733
  });
3627
- await runDestroyLifecycle(this, {
3734
+ const destroying = runDestroyLifecycle(this, {
3628
3735
  getService: (instance) => instance._service,
3629
3736
  doDestroy: async () => {
3630
3737
  const db = BaseDb.getAppDb();
@@ -3638,7 +3745,7 @@ class Model {
3638
3745
  .limit(1);
3639
3746
  if (modelRecords.length > 0) {
3640
3747
  const modelId = modelRecords[0].id;
3641
- const { detachModelRows } = await import('./detachRowsBeforeDelete-S0q-DJPT.js');
3748
+ const { detachModelRows } = await import('./detachRowsBeforeDelete-YRSZY6dO.js');
3642
3749
  await detachModelRows(db, [typeof modelId === 'string' ? parseInt(modelId, 10) : modelId]);
3643
3750
  await db.delete(modelSchemas).where(eq(modelSchemas.modelId, modelId));
3644
3751
  await db
@@ -3649,7 +3756,7 @@ class Model {
3649
3756
  }
3650
3757
  if (schemaName && modelName) {
3651
3758
  // Imported here, not at module load: Model and Schema import each other.
3652
- const _mod_11 = await import('./Schema-CpMNEnUO.js');
3759
+ const _mod_11 = await import('./Schema-Dc5EA8Yb.js');
3653
3760
  const _ns_11 = _mod_11.b;
3654
3761
  const { Schema } = _ns_11;
3655
3762
  const schema = Schema.create(schemaName, { waitForReady: false });
@@ -3664,6 +3771,13 @@ class Model {
3664
3771
  }
3665
3772
  },
3666
3773
  });
3774
+ try {
3775
+ await destroying;
3776
+ }
3777
+ finally {
3778
+ if (modelFileId)
3779
+ finishModelDestroy(modelFileId);
3780
+ }
3667
3781
  }
3668
3782
  /**
3669
3783
  * Set up liveQuery subscription to watch for property changes in the database
@@ -3686,8 +3800,8 @@ class Model {
3686
3800
  return undefined;
3687
3801
  }
3688
3802
  // Model names are only unique per schema: find this model's row by its id (schemaFileId).
3689
- const _mod_12 = await import('./BasePathResolver-Cm4apKRb.js');
3690
- const _ns_12 = _mod_12.b1;
3803
+ const _mod_12 = await import('./BasePathResolver-DxdS0wJ0.js');
3804
+ const _ns_12 = _mod_12.b2;
3691
3805
  const { resolveModelRecord } = _ns_12;
3692
3806
  const modelRecord = await resolveModelRecord(modelName, { modelFileId: context.id, schemaName: context.schemaName }, db);
3693
3807
  if (!modelRecord?.id) {
@@ -3891,5 +4005,35 @@ var Model$1 = /*#__PURE__*/Object.freeze({
3891
4005
  const TModelValues = Type.Record(Type.String(), Type.Any());
3892
4006
  const TModelSchema = Type.Record(Type.String(), TProperty);
3893
4007
 
3894
- export { ConflictError as C, Model as M, TModelSchema as T, TModelValues as a, anyEvictionSince as b, currentEvictionEpoch as c, setupEntityLiveQuery as d, writeToDatabase$1 as e, Model$1 as f, isActorStopped as i, schemaEvictedSince as s, writeProcessMachine as w };
3895
- //# sourceMappingURL=index-8O3Uow-Y.js.map
4008
+ /**
4009
+ * Thrown by Item.create when values don't fit the model: unknown keys, wrong types, or values
4010
+ * that break the property's validation rules. Nothing is written.
4011
+ */
4012
+ class ItemCreateValidationError extends Error {
4013
+ constructor(modelName, problems) {
4014
+ super(`Cannot create ${modelName}: ` + problems.map((p) => `${p.property}: ${p.message}`).join('; '));
4015
+ this.name = 'ItemCreateValidationError';
4016
+ this.modelName = modelName;
4017
+ this.problems = problems;
4018
+ Object.setPrototypeOf(this, ItemCreateValidationError.prototype);
4019
+ }
4020
+ }
4021
+ /** Thrown by Item.create when no model with that name exists (in the given schema, if any). */
4022
+ class UnknownModelError extends Error {
4023
+ constructor(modelName, schemaName) {
4024
+ super(`Model "${modelName}" not found${schemaName ? ` in schema "${schemaName}"` : ''}.`);
4025
+ this.name = 'UnknownModelError';
4026
+ this.modelName = modelName;
4027
+ this.schemaName = schemaName;
4028
+ Object.setPrototypeOf(this, UnknownModelError.prototype);
4029
+ }
4030
+ }
4031
+
4032
+ var errors = /*#__PURE__*/Object.freeze({
4033
+ __proto__: null,
4034
+ ItemCreateValidationError: ItemCreateValidationError,
4035
+ UnknownModelError: UnknownModelError
4036
+ });
4037
+
4038
+ export { ConflictError as C, ItemCreateValidationError as I, Model as M, TModelSchema as T, UnknownModelError as U, TModelValues as a, anyEvictionSince as b, currentEvictionEpoch as c, setupEntityLiveQuery as d, writeToDatabase$1 as e, finishSchemaDestroy as f, getDraftSaveError as g, Model$1 as h, isActorStopped as i, errors as j, recordSchemaDestroy as r, schemaEvictedSince as s, trackDraftSave as t, writeProcessMachine as w };
4039
+ //# sourceMappingURL=errors-Cl8rbtKR.js.map