@rvoh/dream 2.26.0 → 2.28.0

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 (331) hide show
  1. package/dist/cjs/src/Dream.js +32 -17
  2. package/dist/cjs/src/db/errors.js +4 -0
  3. package/dist/cjs/src/decorators/field/sortable/Sortable.js +43 -118
  4. package/dist/cjs/src/decorators/field/sortable/helpers/acquireSortableScopeLocks.js +38 -0
  5. package/dist/cjs/src/decorators/field/sortable/helpers/acquireStabilizedSortableBatchLocks.js +183 -0
  6. package/dist/cjs/src/decorators/field/sortable/helpers/acquireStabilizedSortableScopeLocks.js +40 -0
  7. package/dist/cjs/src/decorators/field/sortable/helpers/canonicalScopeValue.js +194 -0
  8. package/dist/cjs/src/decorators/field/sortable/helpers/clearCachedSortableValues.js +18 -0
  9. package/dist/cjs/src/decorators/field/sortable/helpers/decrementScopedRecordsGreaterThanPosition.js +9 -9
  10. package/dist/cjs/src/decorators/field/sortable/helpers/filterQueryToInstanceScope.js +20 -0
  11. package/dist/cjs/src/decorators/field/sortable/helpers/filterQueryToScopeValues.js +32 -0
  12. package/dist/cjs/src/decorators/field/sortable/helpers/heldSortableScopeLockKeys.js +34 -0
  13. package/dist/cjs/src/decorators/field/sortable/helpers/performSortableDestroyWork.js +21 -0
  14. package/dist/cjs/src/decorators/field/sortable/helpers/performSortablePositionWork.js +36 -0
  15. package/dist/cjs/src/decorators/field/sortable/helpers/prepareSortableFieldsForDestroy.js +56 -0
  16. package/dist/cjs/src/decorators/field/sortable/helpers/prepareSortableFieldsForSave.js +84 -0
  17. package/dist/cjs/src/decorators/field/sortable/helpers/resortAllRecords.js +186 -29
  18. package/dist/cjs/src/decorators/field/sortable/helpers/setPosition.js +90 -46
  19. package/dist/cjs/src/decorators/field/sortable/helpers/sortableRowCache.js +73 -0
  20. package/dist/cjs/src/decorators/field/sortable/helpers/sortableScopeColumns.js +42 -0
  21. package/dist/cjs/src/decorators/field/sortable/helpers/sortableScopeLockKey.js +99 -0
  22. package/dist/cjs/src/decorators/field/sortable/helpers/sortableScopeLockKeys.js +72 -0
  23. package/dist/cjs/src/decorators/field/sortable/helpers/sortableSnapshot.js +101 -0
  24. package/dist/cjs/src/decorators/field/sortable/helpers/stabilizeSortableScopeLocks.js +86 -0
  25. package/dist/cjs/src/decorators/field/sortable/helpers/validPosition.js +21 -4
  26. package/dist/cjs/src/decorators/field/sortable/hooks/afterSortableDestroy.js +24 -7
  27. package/dist/cjs/src/decorators/field/sortable/hooks/afterSortableUpdate.js +10 -0
  28. package/dist/cjs/src/dream/DreamClassTransactionBuilder.js +0 -18
  29. package/dist/cjs/src/dream/Query.js +64 -32
  30. package/dist/cjs/src/dream/QueryDriver/Base.js +75 -2
  31. package/dist/cjs/src/dream/QueryDriver/Kysely.js +19 -12
  32. package/dist/cjs/src/dream/QueryDriver/Postgres.js +74 -0
  33. package/dist/cjs/src/dream/internal/destroyDream.js +60 -6
  34. package/dist/cjs/src/dream/internal/saveDream.js +109 -7
  35. package/dist/cjs/src/dream/internal/similarity/SimilarityBuilder.js +19 -4
  36. package/dist/cjs/src/dream/internal/softDeleteDream.js +13 -1
  37. package/dist/cjs/src/dream/internal/undestroyDream.js +56 -8
  38. package/dist/cjs/src/dream-app/index.js +21 -0
  39. package/dist/cjs/src/errors/SortableBatchRequiresTooManyScopeLocks.js +57 -0
  40. package/dist/cjs/src/errors/SortableRequiresAdvisoryTransactionLocks.js +26 -0
  41. package/dist/cjs/src/errors/SortableScopeDidNotStabilize.js +39 -0
  42. package/dist/cjs/src/errors/SortableScopeLockWaitTimedOut.js +50 -0
  43. package/dist/cjs/src/package-exports/errors.js +3 -0
  44. package/dist/esm/src/Dream.js +32 -17
  45. package/dist/esm/src/db/errors.js +4 -0
  46. package/dist/esm/src/decorators/field/sortable/Sortable.js +43 -118
  47. package/dist/esm/src/decorators/field/sortable/helpers/acquireSortableScopeLocks.js +38 -0
  48. package/dist/esm/src/decorators/field/sortable/helpers/acquireStabilizedSortableBatchLocks.js +183 -0
  49. package/dist/esm/src/decorators/field/sortable/helpers/acquireStabilizedSortableScopeLocks.js +40 -0
  50. package/dist/esm/src/decorators/field/sortable/helpers/canonicalScopeValue.js +194 -0
  51. package/dist/esm/src/decorators/field/sortable/helpers/clearCachedSortableValues.js +18 -0
  52. package/dist/esm/src/decorators/field/sortable/helpers/decrementScopedRecordsGreaterThanPosition.js +9 -9
  53. package/dist/esm/src/decorators/field/sortable/helpers/filterQueryToInstanceScope.js +20 -0
  54. package/dist/esm/src/decorators/field/sortable/helpers/filterQueryToScopeValues.js +32 -0
  55. package/dist/esm/src/decorators/field/sortable/helpers/heldSortableScopeLockKeys.js +34 -0
  56. package/dist/esm/src/decorators/field/sortable/helpers/performSortableDestroyWork.js +21 -0
  57. package/dist/esm/src/decorators/field/sortable/helpers/performSortablePositionWork.js +36 -0
  58. package/dist/esm/src/decorators/field/sortable/helpers/prepareSortableFieldsForDestroy.js +56 -0
  59. package/dist/esm/src/decorators/field/sortable/helpers/prepareSortableFieldsForSave.js +84 -0
  60. package/dist/esm/src/decorators/field/sortable/helpers/resortAllRecords.js +186 -29
  61. package/dist/esm/src/decorators/field/sortable/helpers/setPosition.js +90 -46
  62. package/dist/esm/src/decorators/field/sortable/helpers/sortableRowCache.js +73 -0
  63. package/dist/esm/src/decorators/field/sortable/helpers/sortableScopeColumns.js +42 -0
  64. package/dist/esm/src/decorators/field/sortable/helpers/sortableScopeLockKey.js +99 -0
  65. package/dist/esm/src/decorators/field/sortable/helpers/sortableScopeLockKeys.js +72 -0
  66. package/dist/esm/src/decorators/field/sortable/helpers/sortableSnapshot.js +101 -0
  67. package/dist/esm/src/decorators/field/sortable/helpers/stabilizeSortableScopeLocks.js +86 -0
  68. package/dist/esm/src/decorators/field/sortable/helpers/validPosition.js +21 -4
  69. package/dist/esm/src/decorators/field/sortable/hooks/afterSortableDestroy.js +24 -7
  70. package/dist/esm/src/decorators/field/sortable/hooks/afterSortableUpdate.js +10 -0
  71. package/dist/esm/src/dream/DreamClassTransactionBuilder.js +0 -18
  72. package/dist/esm/src/dream/Query.js +64 -32
  73. package/dist/esm/src/dream/QueryDriver/Base.js +75 -2
  74. package/dist/esm/src/dream/QueryDriver/Kysely.js +19 -12
  75. package/dist/esm/src/dream/QueryDriver/Postgres.js +74 -0
  76. package/dist/esm/src/dream/internal/destroyDream.js +60 -6
  77. package/dist/esm/src/dream/internal/saveDream.js +109 -7
  78. package/dist/esm/src/dream/internal/similarity/SimilarityBuilder.js +19 -4
  79. package/dist/esm/src/dream/internal/softDeleteDream.js +13 -1
  80. package/dist/esm/src/dream/internal/undestroyDream.js +56 -8
  81. package/dist/esm/src/dream-app/index.js +21 -0
  82. package/dist/esm/src/errors/SortableBatchRequiresTooManyScopeLocks.js +57 -0
  83. package/dist/esm/src/errors/SortableRequiresAdvisoryTransactionLocks.js +26 -0
  84. package/dist/esm/src/errors/SortableScopeDidNotStabilize.js +39 -0
  85. package/dist/esm/src/errors/SortableScopeLockWaitTimedOut.js +50 -0
  86. package/dist/esm/src/package-exports/errors.js +3 -0
  87. package/dist/types/src/Dream.d.ts +35 -2
  88. package/dist/types/src/db/errors.d.ts +2 -0
  89. package/dist/types/src/decorators/field/sortable/Sortable.d.ts +33 -0
  90. package/dist/types/src/decorators/field/sortable/helpers/acquireSortableScopeLocks.d.ts +25 -0
  91. package/dist/types/src/decorators/field/sortable/helpers/acquireStabilizedSortableBatchLocks.d.ts +91 -0
  92. package/dist/types/src/decorators/field/sortable/helpers/acquireStabilizedSortableScopeLocks.d.ts +31 -0
  93. package/dist/types/src/decorators/field/sortable/helpers/canonicalScopeValue.d.ts +85 -0
  94. package/dist/types/src/decorators/field/sortable/helpers/clearCachedSortableValues.d.ts +13 -0
  95. package/dist/types/src/decorators/field/sortable/helpers/decrementScopedRecordsGreaterThanPosition.d.ts +8 -1
  96. package/dist/types/src/decorators/field/sortable/helpers/filterQueryToInstanceScope.d.ts +24 -0
  97. package/dist/types/src/decorators/field/sortable/helpers/filterQueryToScopeValues.d.ts +22 -0
  98. package/dist/types/src/decorators/field/sortable/helpers/heldSortableScopeLockKeys.d.ts +27 -0
  99. package/dist/types/src/decorators/field/sortable/helpers/performSortableDestroyWork.d.ts +14 -0
  100. package/dist/types/src/decorators/field/sortable/helpers/performSortablePositionWork.d.ts +15 -0
  101. package/dist/types/src/decorators/field/sortable/helpers/prepareSortableFieldsForDestroy.d.ts +41 -0
  102. package/dist/types/src/decorators/field/sortable/helpers/prepareSortableFieldsForSave.d.ts +35 -0
  103. package/dist/types/src/decorators/field/sortable/helpers/resortAllRecords.d.ts +32 -0
  104. package/dist/types/src/decorators/field/sortable/helpers/setPosition.d.ts +27 -11
  105. package/dist/types/src/decorators/field/sortable/helpers/sortableRowCache.d.ts +48 -0
  106. package/dist/types/src/decorators/field/sortable/helpers/sortableScopeColumns.d.ts +30 -0
  107. package/dist/types/src/decorators/field/sortable/helpers/sortableScopeLockKey.d.ts +44 -0
  108. package/dist/types/src/decorators/field/sortable/helpers/sortableScopeLockKeys.d.ts +51 -0
  109. package/dist/types/src/decorators/field/sortable/helpers/sortableSnapshot.d.ts +92 -0
  110. package/dist/types/src/decorators/field/sortable/helpers/stabilizeSortableScopeLocks.d.ts +59 -0
  111. package/dist/types/src/decorators/field/sortable/helpers/validPosition.d.ts +14 -2
  112. package/dist/types/src/decorators/field/sortable/hooks/afterSortableCreate.d.ts +1 -1
  113. package/dist/types/src/decorators/field/sortable/hooks/afterSortableDestroy.d.ts +3 -1
  114. package/dist/types/src/decorators/field/sortable/hooks/afterSortableUpdate.d.ts +1 -1
  115. package/dist/types/src/dream/DreamClassTransactionBuilder.d.ts +3 -2
  116. package/dist/types/src/dream/Query.d.ts +44 -9
  117. package/dist/types/src/dream/QueryDriver/Base.d.ts +61 -1
  118. package/dist/types/src/dream/QueryDriver/Kysely.d.ts +6 -2
  119. package/dist/types/src/dream/QueryDriver/Postgres.d.ts +40 -0
  120. package/dist/types/src/dream/internal/softDeleteDream.d.ts +7 -1
  121. package/dist/types/src/dream-app/index.d.ts +18 -2
  122. package/dist/types/src/errors/SortableBatchRequiresTooManyScopeLocks.d.ts +37 -0
  123. package/dist/types/src/errors/SortableRequiresAdvisoryTransactionLocks.d.ts +13 -0
  124. package/dist/types/src/errors/SortableScopeDidNotStabilize.d.ts +23 -0
  125. package/dist/types/src/errors/SortableScopeLockWaitTimedOut.d.ts +34 -0
  126. package/dist/types/src/package-exports/errors.d.ts +3 -0
  127. package/dist/types/src/types/associations/shared.d.ts +1 -1
  128. package/dist/types/src/types/dream.d.ts +3 -0
  129. package/dist/types/src/types/query.d.ts +1 -1
  130. package/dist/types/src/types/types/associations/shared.ts +8 -6
  131. package/dist/types/src/types/types/dream.ts +6 -0
  132. package/dist/types/src/types/types/query.ts +1 -1
  133. package/docs/assets/hierarchy.js +1 -1
  134. package/docs/assets/navigation.js +1 -1
  135. package/docs/assets/search.js +1 -1
  136. package/docs/classes/db.DreamMigrationHelpers.html +11 -11
  137. package/docs/classes/db.KyselyQueryDriver.html +88 -41
  138. package/docs/classes/db.PostgresQueryDriver.html +82 -42
  139. package/docs/classes/db.QueryDriverBase.html +86 -39
  140. package/docs/classes/errors.CheckConstraintViolation.html +3 -3
  141. package/docs/classes/errors.ColumnOverflow.html +3 -3
  142. package/docs/classes/errors.CreateOrFindByFailedToCreateAndFind.html +3 -3
  143. package/docs/classes/errors.DataIncompatibleWithDatabaseField.html +3 -3
  144. package/docs/classes/errors.DataTypeColumnTypeMismatch.html +3 -3
  145. package/docs/classes/errors.DecryptionError.html +2 -2
  146. package/docs/classes/errors.DecryptionParseError.html +2 -2
  147. package/docs/classes/errors.DecryptionRotationError.html +3 -3
  148. package/docs/classes/errors.GlobalNameNotSet.html +3 -3
  149. package/docs/classes/errors.InvalidCalendarDate.html +2 -2
  150. package/docs/classes/errors.InvalidClockTime.html +2 -2
  151. package/docs/classes/errors.InvalidClockTimeTz.html +2 -2
  152. package/docs/classes/errors.InvalidDateTime.html +2 -2
  153. package/docs/classes/errors.MissingSerializersDefinition.html +3 -3
  154. package/docs/classes/errors.NonLoadedAssociation.html +3 -3
  155. package/docs/classes/errors.NotNullViolation.html +3 -3
  156. package/docs/classes/errors.RecordNotFound.html +3 -3
  157. package/docs/classes/errors.SortableBatchRequiresTooManyScopeLocks.html +61 -0
  158. package/docs/classes/errors.SortableScopeDidNotStabilize.html +48 -0
  159. package/docs/classes/errors.SortableScopeLockWaitTimedOut.html +58 -0
  160. package/docs/classes/errors.ValidationError.html +3 -3
  161. package/docs/classes/index.CalendarDate.html +33 -33
  162. package/docs/classes/index.ClockTime.html +32 -32
  163. package/docs/classes/index.ClockTimeTz.html +35 -35
  164. package/docs/classes/index.DateTime.html +86 -86
  165. package/docs/classes/index.Decorators.html +19 -19
  166. package/docs/classes/index.Dream.html +159 -124
  167. package/docs/classes/index.DreamApp.html +11 -10
  168. package/docs/classes/index.DreamTransaction.html +2 -2
  169. package/docs/classes/index.Env.html +2 -2
  170. package/docs/classes/index.Query.html +122 -99
  171. package/docs/classes/system.CliFileWriter.html +4 -4
  172. package/docs/classes/system.DreamBin.html +2 -2
  173. package/docs/classes/system.DreamCLI.html +7 -7
  174. package/docs/classes/system.DreamImporter.html +2 -2
  175. package/docs/classes/system.DreamLogos.html +2 -2
  176. package/docs/classes/system.DreamSerializerBuilder.html +11 -11
  177. package/docs/classes/system.ObjectSerializerBuilder.html +8 -8
  178. package/docs/classes/system.PathHelpers.html +3 -3
  179. package/docs/classes/utils.Encrypt.html +3 -3
  180. package/docs/classes/utils.Range.html +2 -2
  181. package/docs/functions/db.closeAllDbConnections.html +1 -1
  182. package/docs/functions/db.dreamDbConnections.html +1 -1
  183. package/docs/functions/db.untypedDb.html +1 -1
  184. package/docs/functions/db.validateColumn.html +1 -1
  185. package/docs/functions/db.validateTable.html +1 -1
  186. package/docs/functions/errors.pgErrorType.html +1 -1
  187. package/docs/functions/index.DreamSerializer.html +1 -1
  188. package/docs/functions/index.ObjectSerializer.html +1 -1
  189. package/docs/functions/index.ReplicaSafe.html +1 -1
  190. package/docs/functions/index.STI.html +1 -1
  191. package/docs/functions/index.SoftDelete.html +1 -1
  192. package/docs/functions/utils.camelize.html +1 -1
  193. package/docs/functions/utils.capitalize.html +1 -1
  194. package/docs/functions/utils.cloneDeepSafe.html +1 -1
  195. package/docs/functions/utils.compact.html +1 -1
  196. package/docs/functions/utils.groupBy.html +1 -1
  197. package/docs/functions/utils.hyphenize.html +1 -1
  198. package/docs/functions/utils.intersection.html +1 -1
  199. package/docs/functions/utils.isEmpty.html +1 -1
  200. package/docs/functions/utils.normalizeUnicode.html +1 -1
  201. package/docs/functions/utils.pascalize.html +1 -1
  202. package/docs/functions/utils.percent.html +1 -1
  203. package/docs/functions/utils.range.html +1 -1
  204. package/docs/functions/utils.round.html +1 -1
  205. package/docs/functions/utils.sanitizeString.html +1 -1
  206. package/docs/functions/utils.snakeify.html +1 -1
  207. package/docs/functions/utils.sort.html +2 -2
  208. package/docs/functions/utils.sortBy.html +1 -1
  209. package/docs/functions/utils.sortObjectByKey.html +1 -1
  210. package/docs/functions/utils.sortObjectByValue.html +1 -1
  211. package/docs/functions/utils.uncapitalize.html +1 -1
  212. package/docs/functions/utils.uniq.html +1 -1
  213. package/docs/hierarchy.html +1 -1
  214. package/docs/interfaces/openapi.OpenapiDescription.html +2 -2
  215. package/docs/interfaces/openapi.OpenapiSchemaProperties.html +1 -1
  216. package/docs/interfaces/openapi.OpenapiSchemaPropertiesShorthand.html +1 -1
  217. package/docs/interfaces/openapi.OpenapiTypeFieldObject.html +1 -1
  218. package/docs/interfaces/types.BelongsToStatement.html +2 -2
  219. package/docs/interfaces/types.DecoratorContext.html +2 -2
  220. package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
  221. package/docs/interfaces/types.DreamAppOpts.html +2 -2
  222. package/docs/interfaces/types.DreamDbConfig.html +5 -5
  223. package/docs/interfaces/types.DurationObject.html +2 -2
  224. package/docs/interfaces/types.EncryptOptions.html +2 -2
  225. package/docs/interfaces/types.InternalAnyTypedSerializerRendersMany.html +2 -2
  226. package/docs/interfaces/types.InternalAnyTypedSerializerRendersOne.html +2 -2
  227. package/docs/interfaces/types.SerializerRendererOpts.html +2 -2
  228. package/docs/modules/errors.html +1 -1
  229. package/docs/types/openapi.CommonOpenapiSchemaObjectFields.html +1 -1
  230. package/docs/types/openapi.OpenapiAllTypes.html +1 -1
  231. package/docs/types/openapi.OpenapiFormats.html +1 -1
  232. package/docs/types/openapi.OpenapiNumberFormats.html +1 -1
  233. package/docs/types/openapi.OpenapiPrimitiveBaseTypes.html +1 -1
  234. package/docs/types/openapi.OpenapiPrimitiveTypes.html +1 -1
  235. package/docs/types/openapi.OpenapiSchemaArray.html +1 -1
  236. package/docs/types/openapi.OpenapiSchemaArrayShorthand.html +1 -1
  237. package/docs/types/openapi.OpenapiSchemaBase.html +1 -1
  238. package/docs/types/openapi.OpenapiSchemaBody.html +1 -1
  239. package/docs/types/openapi.OpenapiSchemaBodyShorthand.html +1 -1
  240. package/docs/types/openapi.OpenapiSchemaCommonFields.html +1 -1
  241. package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +2 -2
  242. package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +2 -2
  243. package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +2 -2
  244. package/docs/types/openapi.OpenapiSchemaExpressionRef.html +2 -2
  245. package/docs/types/openapi.OpenapiSchemaExpressionRefSchemaShorthand.html +2 -2
  246. package/docs/types/openapi.OpenapiSchemaInteger.html +1 -1
  247. package/docs/types/openapi.OpenapiSchemaNull.html +2 -2
  248. package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
  249. package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
  250. package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +1 -1
  251. package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +1 -1
  252. package/docs/types/openapi.OpenapiSchemaObjectAnyOf.html +1 -1
  253. package/docs/types/openapi.OpenapiSchemaObjectAnyOfShorthand.html +1 -1
  254. package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
  255. package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.html +1 -1
  256. package/docs/types/openapi.OpenapiSchemaObjectOneOf.html +1 -1
  257. package/docs/types/openapi.OpenapiSchemaObjectOneOfShorthand.html +1 -1
  258. package/docs/types/openapi.OpenapiSchemaObjectShorthand.html +1 -1
  259. package/docs/types/openapi.OpenapiSchemaPrimitiveGeneric.html +1 -1
  260. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAllOf.html +2 -2
  261. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +2 -2
  262. package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +2 -2
  263. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializableRef.html +2 -2
  264. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializerRef.html +2 -2
  265. package/docs/types/openapi.OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
  266. package/docs/types/openapi.OpenapiSchemaString.html +1 -1
  267. package/docs/types/openapi.OpenapiShorthandAllTypes.html +1 -1
  268. package/docs/types/openapi.OpenapiShorthandPrimitiveBaseTypes.html +1 -1
  269. package/docs/types/openapi.OpenapiShorthandPrimitiveTypes.html +1 -1
  270. package/docs/types/openapi.OpenapiTypeField.html +1 -1
  271. package/docs/types/system.DreamAppAllowedPackageManagersEnum.html +1 -1
  272. package/docs/types/types.CalendarDateDurationUnit.html +1 -1
  273. package/docs/types/types.CalendarDateObject.html +1 -1
  274. package/docs/types/types.Camelized.html +1 -1
  275. package/docs/types/types.ClockTimeObject.html +1 -1
  276. package/docs/types/types.DbConnectionType.html +1 -1
  277. package/docs/types/types.DbTypes.html +1 -1
  278. package/docs/types/types.DreamAssociationMetadata.html +1 -1
  279. package/docs/types/types.DreamAttributes.html +1 -1
  280. package/docs/types/types.DreamClassAssociationAndStatement.html +1 -1
  281. package/docs/types/types.DreamClassColumn.html +1 -1
  282. package/docs/types/types.DreamColumn.html +1 -1
  283. package/docs/types/types.DreamColumnNames.html +1 -1
  284. package/docs/types/types.DreamLogLevel.html +1 -1
  285. package/docs/types/types.DreamLogger.html +2 -2
  286. package/docs/types/types.DreamModelSerializerType.html +1 -1
  287. package/docs/types/types.DreamOrViewModelClassSerializerKey.html +1 -1
  288. package/docs/types/types.DreamOrViewModelSerializerKey.html +1 -1
  289. package/docs/types/types.DreamParamSafeAttributes.html +1 -1
  290. package/docs/types/types.DreamParamSafeColumnNames.html +1 -1
  291. package/docs/types/types.DreamSerializable.html +1 -1
  292. package/docs/types/types.DreamSerializableArray.html +1 -1
  293. package/docs/types/types.DreamSerializerKey.html +1 -1
  294. package/docs/types/types.DreamSerializers.html +1 -1
  295. package/docs/types/types.DreamVirtualColumns.html +1 -1
  296. package/docs/types/types.DurationUnit.html +1 -1
  297. package/docs/types/types.EncryptAlgorithm.html +1 -1
  298. package/docs/types/types.HasManyStatement.html +1 -1
  299. package/docs/types/types.HasOneStatement.html +1 -1
  300. package/docs/types/types.Hyphenized.html +1 -1
  301. package/docs/types/types.Pascalized.html +1 -1
  302. package/docs/types/types.PrimaryKeyType.html +1 -1
  303. package/docs/types/types.RoundingPrecision.html +1 -1
  304. package/docs/types/types.SerializerCasing.html +1 -1
  305. package/docs/types/types.SimpleObjectSerializerType.html +1 -1
  306. package/docs/types/types.Snakeified.html +1 -1
  307. package/docs/types/types.StrictInterface.html +1 -1
  308. package/docs/types/types.UpdateableAssociationProperties.html +1 -1
  309. package/docs/types/types.UpdateableProperties.html +1 -1
  310. package/docs/types/types.ValidationType.html +1 -1
  311. package/docs/types/types.ViewModel.html +2 -2
  312. package/docs/types/types.ViewModelClass.html +1 -1
  313. package/docs/types/types.WeekdayName.html +1 -1
  314. package/docs/types/types.WhereStatementForDream.html +1 -1
  315. package/docs/types/types.WhereStatementForDreamClass.html +1 -1
  316. package/docs/variables/index.DreamConst.html +1 -1
  317. package/docs/variables/index.ops.html +1 -1
  318. package/docs/variables/openapi.openapiPrimitiveTypes.html +1 -1
  319. package/docs/variables/openapi.openapiShorthandPrimitiveTypes.html +1 -1
  320. package/docs/variables/system.DreamAppAllowedPackageManagersEnumValues.html +1 -1
  321. package/docs/variables/system.primaryKeyTypes.html +1 -1
  322. package/package.json +1 -1
  323. package/dist/cjs/src/decorators/field/sortable/helpers/applySortableScopeToQuery.js +0 -13
  324. package/dist/cjs/src/decorators/field/sortable/helpers/sortableQueryExcludingDream.js +0 -7
  325. package/dist/cjs/src/decorators/field/sortable/hooks/beforeSortableSave.js +0 -41
  326. package/dist/esm/src/decorators/field/sortable/helpers/applySortableScopeToQuery.js +0 -13
  327. package/dist/esm/src/decorators/field/sortable/helpers/sortableQueryExcludingDream.js +0 -7
  328. package/dist/esm/src/decorators/field/sortable/hooks/beforeSortableSave.js +0 -41
  329. package/dist/types/src/decorators/field/sortable/helpers/applySortableScopeToQuery.d.ts +0 -12
  330. package/dist/types/src/decorators/field/sortable/helpers/sortableQueryExcludingDream.d.ts +0 -12
  331. package/dist/types/src/decorators/field/sortable/hooks/beforeSortableSave.d.ts +0 -12
@@ -1,4 +1,6 @@
1
- import { applySortableScopesToQuery } from '../../decorators/field/sortable/helpers/setPosition.js';
1
+ import acquireStabilizedSortableScopeLocks from '../../decorators/field/sortable/helpers/acquireStabilizedSortableScopeLocks.js';
2
+ import filterQueryToScopeValues from '../../decorators/field/sortable/helpers/filterQueryToScopeValues.js';
3
+ import { snapshotScopeValue } from '../../decorators/field/sortable/helpers/sortableSnapshot.js';
2
4
  import runHooksFor from './runHooksFor.js';
3
5
  /**
4
6
  * @internal
@@ -31,11 +33,23 @@ async function undestroyDreamWithTransaction(dream, txn, options) {
31
33
  if (!skipHooks) {
32
34
  await runHooksFor('beforeUpdate', dream, true, null, txn);
33
35
  }
34
- await doUndestroyDream(dream, txn);
36
+ // The cascade runs ahead of the restore, matching `destroyDream`: this
37
+ // record's own scope lock is taken inside `doUndestroyDream`, so a cascade
38
+ // seated after it would hold that lock across every descendant's undestroy,
39
+ // and a parent and its children would take their advisory keys in the
40
+ // opposite order to the one the destroy side takes them in. The nested select
41
+ // the cascade builds bypasses the soft-delete scope on this record's own
42
+ // class (`undestroyOptions`), so it reaches the children while this record is
43
+ // still deleted.
35
44
  if (cascade) {
36
45
  await undestroyAssociatedRecords(dream, txn, options);
37
46
  }
38
- if (!skipHooks) {
47
+ const restoredRowCount = await doUndestroyDream(dream, txn);
48
+ // A restore of a row that is not deleted — a second `undestroy()`, a job
49
+ // retry, or a record that was never destroyed, which `Query#undestroy` reaches
50
+ // routinely since it lifts the soft-delete scope and walks every match —
51
+ // matches no row, so there is nothing for the after-update hooks to react to.
52
+ if (!skipHooks && restoredRowCount > 0) {
39
53
  await runHooksFor('afterUpdate', dream, true, null, txn);
40
54
  await runHooksFor('afterUpdateCommit', dream, true, null, txn);
41
55
  }
@@ -45,22 +59,56 @@ async function undestroyDreamWithTransaction(dream, txn, options) {
45
59
  /**
46
60
  * @internal
47
61
  *
48
- * Destroys the dream iff it was not blocked from
49
- * deleting by one of the beforeDestroy model hooks
62
+ * Clears the record's `deletedAt` and, for a sortable model, gives it a
63
+ * position at the end of the sort scope its row physically occupies.
64
+ *
65
+ * Restoring is conditional on the row still being deleted, and the count of
66
+ * rows the statement matched is what the caller reads that off. A row that is
67
+ * already back would otherwise be inside the `max(position)` its new position
68
+ * is computed from, and would move one past itself on every repeat — a
69
+ * permanent gap in the scope.
70
+ *
71
+ * @returns the number of rows restored: 1, or 0 when the row was not deleted
50
72
  */
51
73
  async function doUndestroyDream(dream, txn) {
52
74
  const updateStatement = txn.kyselyTransaction.updateTable(dream.table);
53
75
  let query = updateStatement
54
76
  .where(dream['_primaryKey'], '=', dream.primaryKeyValue())
77
+ .where(dream['_deletedAtField'], 'is not', null)
55
78
  .set({ [dream['_deletedAtField']]: null });
56
79
  const dreamClass = dream.constructor;
57
- dreamClass['sortableFields']?.forEach(sortableFieldMetadata => {
80
+ const sortableFields = (dreamClass['sortableFields'] ?? []);
81
+ // Undestroy has no sortable hook of its own — the position is recomputed
82
+ // inline, below, before `afterUpdate` ever runs — so the scope lock is taken
83
+ // here, around that recomputation. Without it the `max(position) + 1`
84
+ // subquery races every other writer of the scope the record is being restored
85
+ // into.
86
+ //
87
+ // The scope this record is being restored into is the one its row physically
88
+ // carries, not the one the instance was loaded with: another writer may have
89
+ // moved it between scopes while it was soft deleted, which is what the
90
+ // stabilized acquisition converges on.
91
+ const { snapshots } = await acquireStabilizedSortableScopeLocks(dream, txn, sortableFields);
92
+ sortableFields.forEach(sortableFieldMetadata => {
58
93
  const positionColumn = sortableFieldMetadata.positionField;
94
+ const snapshot = snapshots.get(positionColumn);
59
95
  query = query.set(eb => ({
60
- [positionColumn]: eb(applySortableScopesToQuery(dream, txn.kyselyTransaction.selectFrom(dream.table), column => dream[column], sortableFieldMetadata.scope).select(eb => eb.fn.max(positionColumn).as(positionColumn + '_max')), '+', 1),
96
+ [positionColumn]: eb(filterQueryToScopeValues(dream, txn.kyselyTransaction.selectFrom(dream.table), column => snapshotScopeValue(snapshot, column, () => dream[column]), sortableFieldMetadata.scope).select(eb =>
97
+ // `max` over a scope with no positioned rows left — an empty
98
+ // scope, or one whose every remaining row is soft deleted and so
99
+ // carries a NULL position — is NULL, and NULL + 1 is NULL, which
100
+ // would restore the record with no position at all. Coalescing
101
+ // before the increment restores it at position 1.
102
+ eb.fn.coalesce(eb.fn.max(positionColumn), eb.lit(0)).as(positionColumn + '_max')), '+', 1),
61
103
  }));
62
104
  });
63
- return await query.execute();
105
+ const [result] = await query.execute();
106
+ // No sortable row cache invalidation here: the restored record took a position
107
+ // at the end of the scope it was restored into and nothing else moved, so no
108
+ // other record's cached preflight row went stale, and this record's own cached
109
+ // row was consumed — destructively — by the snapshot read every sortable field
110
+ // above goes through.
111
+ return Number(result?.numUpdatedRows ?? 0);
64
112
  }
65
113
  /**
66
114
  * @internal
@@ -212,6 +212,24 @@ export default class DreamApp {
212
212
  get paginationMaxPageSize() {
213
213
  return this._paginationMaxPageSize;
214
214
  }
215
+ /**
216
+ * How long, in milliseconds, a position-mutating operation on a
217
+ * `@deco.Sortable` model will wait for another transaction to release the
218
+ * sort scope it needs before giving up with
219
+ * `SortableScopeLockWaitTimedOut`. Applied to the lock acquisition statement
220
+ * alone — the transaction's other lock waits keep whatever `lock_timeout`
221
+ * the application has configured.
222
+ *
223
+ * `0` waits without bound, which is what Postgres's `lock_timeout` means by
224
+ * it, and holds even on a connection configured with a `lock_timeout` of its
225
+ * own: the acquisition disables the bound for the duration of the statement.
226
+ *
227
+ * Set it with `dreamApp.set('sortableScopeLockTimeout', milliseconds)`.
228
+ */
229
+ _sortableScopeLockTimeout = 5000;
230
+ get sortableScopeLockTimeout() {
231
+ return this._sortableScopeLockTimeout;
232
+ }
215
233
  _primaryKeyType = 'bigint';
216
234
  get primaryKeyType() {
217
235
  return this._primaryKeyType;
@@ -492,6 +510,9 @@ export default class DreamApp {
492
510
  case 'paginationMaxPageSize':
493
511
  this._paginationMaxPageSize = options;
494
512
  break;
513
+ case 'sortableScopeLockTimeout':
514
+ this._sortableScopeLockTimeout = options;
515
+ break;
495
516
  default: {
496
517
  // protection so that if a new ApplyOpt is ever added, this will throw a type error at build time
497
518
  const _never = applyOption;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Raised when a batch of a locked `Query#destroy` or `Query#update` on a
3
+ * sortable model would carry its transaction past the number of advisory
4
+ * sort-scope locks Dream is willing to hold at once.
5
+ *
6
+ * The batched, row-locking query APIs take every advisory scope key the batch
7
+ * needs before they claim a single row — one key per sort scope the batch's
8
+ * candidate records occupy, per sortable field the model declares. Advisory
9
+ * locks live in Postgres's cluster-wide shared lock table, whose size is fixed
10
+ * at startup (`max_locks_per_transaction` × `max_connections`), and they are
11
+ * held until the transaction ends. A batch over a table whose sort scope has
12
+ * very high cardinality can therefore consume a meaningful fraction of a
13
+ * resource every other connection in the cluster shares, and exhausting it
14
+ * fails unrelated transactions rather than only this one.
15
+ *
16
+ * So the preflight counts first and refuses, before claiming any row. The count
17
+ * is the whole transaction's: the keys it already holds plus the keys the
18
+ * refused batch would add, since advisory locks are released only when the
19
+ * transaction ends. Inside a transaction Dream opens for the batch that is the
20
+ * batch's own key set and nothing has been locked when this is raised; inside a
21
+ * transaction you own, it also counts every key the run's earlier batches took,
22
+ * and those stay held.
23
+ *
24
+ * The fix is almost always a smaller `batchSize`: the keys one batch needs are
25
+ * bounded by how many distinct sort scopes its records span, so halving the
26
+ * batch size halves the worst case. If the run itself is what accumulates them
27
+ * — a long multi-batch run inside a transaction you opened — either let Dream
28
+ * open a transaction per batch (do not pass a transaction of your own) or
29
+ * commit between runs, which is what releases the locks as it goes.
30
+ */
31
+ export default class SortableBatchRequiresTooManyScopeLocks extends Error {
32
+ dreamClassName;
33
+ keyCount;
34
+ maxKeyCount;
35
+ constructor(dreamClassName, keyCount, maxKeyCount) {
36
+ super();
37
+ this.dreamClassName = dreamClassName;
38
+ this.keyCount = keyCount;
39
+ this.maxKeyCount = maxKeyCount;
40
+ }
41
+ get message() {
42
+ return `\
43
+ A locked batch over ${this.dreamClassName} would leave its transaction holding ${this.keyCount} sort scope locks, and the limit is ${this.maxKeyCount}.
44
+
45
+ Sortable takes one advisory lock per sort scope a batch touches, per sortable field, before the
46
+ batch claims any row, and holds them until the transaction ends. Advisory locks come out of a
47
+ lock table the whole Postgres cluster shares, so a batch that would carry its transaction past
48
+ this limit is refused rather than risking every other connection's transactions.
49
+
50
+ Use a smaller batchSize. The keys one batch needs are bounded by the number of distinct sort
51
+ scopes its records span, so a smaller batch takes proportionally fewer. If this operation runs
52
+ inside a transaction you opened, note that the locks of earlier batches are still held while
53
+ later batches preflight, and are counted here; letting Dream open a transaction per batch
54
+ releases them as it goes.
55
+ `;
56
+ }
57
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Raised when a model declaring sortable fields is used on a query driver that
3
+ * cannot take transaction-scoped advisory locks.
4
+ *
5
+ * Sortable computes position values under a lock on the sort scope, without
6
+ * which concurrent writers collide on the same position. A driver that cannot
7
+ * provide that lock fails loudly here rather than racing silently.
8
+ */
9
+ export default class SortableRequiresAdvisoryTransactionLocks extends Error {
10
+ queryDriverClassName;
11
+ constructor(queryDriverClassName) {
12
+ super();
13
+ this.queryDriverClassName = queryDriverClassName;
14
+ }
15
+ get message() {
16
+ return `\
17
+ ${this.queryDriverClassName} cannot be used with the Sortable decorator.
18
+
19
+ Sortable computes position values under a transaction-scoped advisory lock on the sort
20
+ scope, without which concurrent writers collide on the same position. Implement
21
+ \`acquireAdvisoryTransactionLocks\` on ${this.queryDriverClassName} and set
22
+ \`supportsAdvisoryTransactionLocks = true\`, or use the PostgresQueryDriver for models
23
+ that declare sortable fields.
24
+ `;
25
+ }
26
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Raised when a position-mutating operation cannot settle on the sort scope its
3
+ * record actually occupies.
4
+ *
5
+ * A save, destroy, undestroy or locked batch operation locks the sort scopes it
6
+ * believes it is touching, then reads the rows to find out which scopes they
7
+ * physically occupy. When another writer has moved a row in between, the
8
+ * operation takes the additional scope's lock and reads again. A row that keeps
9
+ * moving exhausts that bound, and the operation raises rather than compacting a
10
+ * scope the row has already left.
11
+ *
12
+ * This is a retryable condition: retrying the operation is the intended
13
+ * response. One caveat for saves the caller did not wrap in a transaction: the
14
+ * model's before-hooks run before the transaction this error aborts, so any
15
+ * effects those hooks had outside the record's own row — writes to other
16
+ * tables, enqueued work — are not rolled back, and run again on retry.
17
+ */
18
+ export default class SortableScopeDidNotStabilize extends Error {
19
+ dreamClassName;
20
+ rounds;
21
+ constructor(dreamClassName, rounds) {
22
+ super();
23
+ this.dreamClassName = dreamClassName;
24
+ this.rounds = rounds;
25
+ }
26
+ get message() {
27
+ return `\
28
+ The sort scope of a ${this.dreamClassName} record kept changing while its position was being computed.
29
+
30
+ Sortable locks the sort scopes an operation touches and then reads the record's actual scope
31
+ under those locks, taking any additional lock the read reveals. After ${this.rounds} rounds the
32
+ record had still been moved to another scope by a competing writer, so the operation was
33
+ abandoned rather than compacting a scope the record no longer occupies. Retrying the operation
34
+ is the intended response; note that on a save the caller did not wrap in a transaction, the
35
+ model's before-hooks ran before the transaction this error aborted, so their effects outside
36
+ the record's own row are not rolled back and run again on retry.
37
+ `;
38
+ }
39
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Raised when a position-mutating operation waited too long for another
3
+ * transaction to release a sort scope.
4
+ *
5
+ * Every position-mutating path — a save, a destroy, an undestroy, a locked
6
+ * batch, `Dream.resort` — computes its positions under an exclusive advisory
7
+ * lock on the sort scope, held until the transaction that took it ends. A
8
+ * writer that finds the scope already locked waits, and that wait is bounded:
9
+ * Dream applies a `lock_timeout` (5 seconds by default, set through the
10
+ * `sortableScopeLockTimeout` DreamApp option) to the acquisition statement
11
+ * alone, and restores whatever `lock_timeout` was in effect afterward, so the
12
+ * transaction's other lock waits — row locks taken by `lock: true`, foreign key
13
+ * checks — are left exactly as the application configured them.
14
+ *
15
+ * This is a retryable condition: the operation touched nothing, and the holder
16
+ * it was waiting for is a transaction that will end. What has to be retried
17
+ * depends on who owns the transaction. When Dream opened it — a plain
18
+ * `save()`, `destroy()`, `undestroy()` or `resort()` — only that call was rolled
19
+ * back, and retrying the call is the whole response. Inside a transaction the
20
+ * caller owns, the failed statement aborts **the entire transaction**: nothing
21
+ * further can run on it, and the caller has to retry the transaction as a whole
22
+ * rather than the sortable operation inside it.
23
+ *
24
+ * Hitting this bound routinely is a signal rather than a transient: it means one
25
+ * sort scope is being written by more concurrent writers than it can serialize,
26
+ * or that something slow is running inside a transaction that holds the scope —
27
+ * on a destroy or an undestroy, that includes your own `afterDestroy` and
28
+ * `afterUpdate` hooks, which run inside the lock window.
29
+ */
30
+ export default class SortableScopeLockWaitTimedOut extends Error {
31
+ timeoutMs;
32
+ constructor(timeoutMs) {
33
+ super();
34
+ this.timeoutMs = timeoutMs;
35
+ }
36
+ get message() {
37
+ return `\
38
+ Timed out after ${this.timeoutMs}ms waiting for another transaction to release a sort scope.
39
+
40
+ Sortable computes positions under an exclusive advisory lock on the sort scope, held until the
41
+ transaction holding it ends. This operation waited the configured bound and gave up rather than
42
+ waiting indefinitely; it changed nothing, and retrying it is the intended response — but if the
43
+ transaction this ran in was one you opened yourself, the whole transaction is aborted and has to
44
+ be retried as a whole.
45
+
46
+ Change the bound with \`dreamApp.set('sortableScopeLockTimeout', milliseconds)\` in your Dream
47
+ configuration, or set it to 0 to wait without bound.
48
+ `;
49
+ }
50
+ }
@@ -12,6 +12,9 @@ export { default as DecryptionParseError } from '../errors/encrypt/DecryptionPar
12
12
  export { default as DecryptionRotationError } from '../errors/encrypt/DecryptionRotationError.js';
13
13
  export { default as RecordNotFound } from '../errors/RecordNotFound.js';
14
14
  export { default as MissingSerializersDefinition } from '../errors/serializers/MissingSerializersDefinition.js';
15
+ export { default as SortableBatchRequiresTooManyScopeLocks } from '../errors/SortableBatchRequiresTooManyScopeLocks.js';
16
+ export { default as SortableScopeDidNotStabilize } from '../errors/SortableScopeDidNotStabilize.js';
17
+ export { default as SortableScopeLockWaitTimedOut } from '../errors/SortableScopeLockWaitTimedOut.js';
15
18
  export { default as ValidationError } from '../errors/ValidationError.js';
16
19
  export { InvalidCalendarDate } from '../utils/datetime/CalendarDate.js';
17
20
  export { InvalidClockTime } from '../utils/datetime/ClockTime.js';
@@ -8,7 +8,7 @@ import LoadBuilder from './dream/LoadBuilder.js';
8
8
  import Query from './dream/Query.js';
9
9
  import { OnStatementForSpecificColumns, PassthroughOnClause, WhereStatement } from './types/associations/shared.js';
10
10
  import { AssociationTableNames, DbConnectionType } from './types/db.js';
11
- import { AllDefaultScopeNames, AssociationNameToDream, AttributeKeys, CreateOrFindByExtraOpts, DefaultOrNamedScopeName, DreamAssociationNames, DreamAssociationNamesWithoutRequiredOnClauses, DreamAssociationNamesWithPassthroughOnClauses, DreamAssociationNamesWithRequiredOnClauses, DreamAttributes, DreamBelongsToAssociationNames, DreamColumnNames, DreamHasManyAssociationNames, DreamHasOneAssociationNames, DreamModelAssociationNames, DreamPrimaryKeyType, DreamSerializerKey, FindablePropertiesForClass, GlobalModelNames, GlobalSerializerName, JoinAndStatements, OrderDir, PassthroughColumnNames, PassthroughOnClauseKeys, PluckEachArgs, PrimaryKeyForFind, RequiredOnClauseKeys, TableColumnNames, UpdateableAssociationProperties, UpdateableProperties, UpdateablePropertiesForClass, UpdateOrCreateByExtraOpts } from './types/dream.js';
11
+ import { AllDefaultScopeNames, AssociationNameToDream, AttributeKeys, CreateOrFindByExtraOpts, DefaultOrNamedScopeName, DreamAssociationNames, DreamAssociationNamesWithoutRequiredOnClauses, DreamAssociationNamesWithPassthroughOnClauses, DreamAssociationNamesWithRequiredOnClauses, DreamAttributes, DreamBelongsToAssociationNames, DreamColumnNames, DreamHasManyAssociationNames, DreamHasOneAssociationNames, DreamModelAssociationNames, DreamPrimaryKeyType, DreamSerializerKey, FindablePropertiesForClass, GlobalModelNames, GlobalSerializerName, JoinAndStatements, OrderDir, PassthroughColumnNames, PassthroughOnClauseKeys, PluckEachArgs, PrimaryKeyForFind, RequiredOnClauseKeys, TableColumnNames, UpdateableAssociationProperties, UpdateableProperties, UpdateablePropertiesForClass, UpdateOrCreateByExtraOpts, ValidatedPluckEachColumnNames } from './types/dream.js';
12
12
  import { BaseModelColumnTypes, CursorPaginatedDreamQueryOptions, CursorPaginatedDreamQueryResult, DefaultQueryTypeOptions, FindEachOpts, LoadForModifierFn, PaginatedDreamQueryOptions, PaginatedDreamQueryResult, QueryWithJoinedAssociationsType, QueryWithJoinedAssociationsTypeAndNoPreload } from './types/query.js';
13
13
  import { DreamModelSerializerType, SimpleObjectSerializerType } from './types/serializer.js';
14
14
  import { ValidationType } from './types/validation.js';
@@ -1445,7 +1445,8 @@ export default class Dream {
1445
1445
  * @param args - a list of fields to pluck, followed by a callback function to call for each set of found fields
1446
1446
  * @returns void
1447
1447
  */
1448
- static pluckEach<T extends typeof Dream, I extends InstanceType<T>, ColumnNames extends TableColumnNames<I['DB'], I['table']>[], CbArgTypes extends BaseModelColumnTypes<ColumnNames, I>>(this: T, ...args: PluckEachArgs<ColumnNames, CbArgTypes>): Promise<void>;
1448
+ static pluckEach<T extends typeof Dream, I extends InstanceType<T>, const ColumnNames extends unknown[]>(this: T, ...args: PluckEachArgs<ValidatedPluckEachColumnNames<ColumnNames, TableColumnNames<I['DB'], I['table']>>, BaseModelColumnTypes<ValidatedPluckEachColumnNames<ColumnNames, TableColumnNames<I['DB'], I['table']>>, I>>): Promise<void>;
1449
+ static pluckEach<T extends typeof Dream, I extends InstanceType<T>, const ColumnNames extends TableColumnNames<I['DB'], I['table']>[]>(this: T, ...args: PluckEachArgs<ColumnNames, BaseModelColumnTypes<ColumnNames, I>>): Promise<void>;
1449
1450
  /**
1450
1451
  * Used in conjunction with the Sortable decorator, `resort`
1451
1452
  * takes a list of sortable fields, and for each one, finds and
@@ -1459,6 +1460,25 @@ export default class Dream {
1459
1460
  * with no gaps, accounting for the scopes specified in the
1460
1461
  * corresponding Sortable decorator.
1461
1462
  *
1463
+ * Each sort scope is renumbered in its own transaction, under the same
1464
+ * advisory lock every other position-mutating path takes on that scope, from
1465
+ * a read of the scope taken inside that lock — so a concurrent create or
1466
+ * destroy cannot make the renumbering land on positions it never read.
1467
+ *
1468
+ * **Do not call `resort` from inside a transaction you own.** It opens those
1469
+ * transactions itself, on connections of its own, so it can neither join
1470
+ * yours nor roll back with it: its writes commit on their own. Worse, if your
1471
+ * transaction already holds the scope lock for a scope being renumbered — any
1472
+ * position-mutating write to that scope earlier in the transaction takes it —
1473
+ * `resort` waits on that lock from the outside, where no deadlock detector
1474
+ * can see the cycle, until the wait bound expires and it throws
1475
+ * `SortableScopeLockWaitTimedOut`.
1476
+ *
1477
+ * On an STI hierarchy the position space is shared by every class in the
1478
+ * hierarchy, exactly as it is for creates, updates and destroys. Calling
1479
+ * `resort` on a child class therefore renumbers the hierarchy's rows, not
1480
+ * only the receiver's.
1481
+ *
1462
1482
  * ```ts
1463
1483
  * class Post extends ApplicationModel {
1464
1484
  * @deco.Sortable({ scope: ['user']})
@@ -2973,6 +2993,19 @@ export default class Dream {
2973
2993
  * This is usually used for a soft-delete
2974
2994
  * pattern
2975
2995
  *
2996
+ * The veto is fail-closed, and the record is left
2997
+ * entirely untouched: on a soft-delete model the soft
2998
+ * delete is skipped as well, no `afterDestroy` or
2999
+ * `afterDestroyCommit` hooks run, since the record is
3000
+ * still present, and the destroy returns before the
3001
+ * `dependent: 'destroy'` cascade, so this record's
3002
+ * dependent associations survive too.
3003
+ *
3004
+ * A veto only stops the subtree rooted at the record
3005
+ * that called it. When a record is reached by another
3006
+ * record's cascade, vetoing spares it and its own
3007
+ * dependents; the destroy that reached it carries on.
3008
+ *
2976
3009
  * ```ts
2977
3010
  * class User extends ApplicationModel {
2978
3011
  * @deco.BeforeDestroy()
@@ -3,6 +3,7 @@ export declare const FOREIGN_KEY_VIOLATION = "FOREIGN_KEY_VIOLATION";
3
3
  export declare const INTEGRITY_CONSTRAINT_VIOLATION = "INTEGRITY_CONSTRAINT_VIOLATION";
4
4
  export declare const INVALID_INPUT_SYNTAX = "INVALID_INPUT_SYNTAX";
5
5
  export declare const COLUMN_OVERFLOW = "COLUMN_OVERFLOW";
6
+ export declare const LOCK_NOT_AVAILABLE = "LOCK_NOT_AVAILABLE";
6
7
  export declare const NOT_NULL_VIOLATION = "NOT_NULL_VIOLATION";
7
8
  export declare const RESTRICT_VIOLATION = "RESTRICT_VIOLATION";
8
9
  export declare const UNIQUE_VIOLATION = "UNIQUE_VIOLATION";
@@ -17,6 +18,7 @@ export declare const PG_ERRORS: {
17
18
  readonly '23000': "INTEGRITY_CONSTRAINT_VIOLATION";
18
19
  readonly '23001': "RESTRICT_VIOLATION";
19
20
  readonly '23503': "FOREIGN_KEY_VIOLATION";
21
+ readonly '55P03': "LOCK_NOT_AVAILABLE";
20
22
  };
21
23
  type PgErrorType = (typeof PG_ERRORS)[keyof typeof PG_ERRORS];
22
24
  export declare function pgErrorType(error: any): PgErrorType | null;
@@ -1,3 +1,36 @@
1
+ /**
2
+ * Marks an integer column as a sortable position: Dream keeps the positions of
3
+ * every record in a sort scope contiguous, starting at 1, as records are
4
+ * created, moved, destroyed and undestroyed.
5
+ *
6
+ * ```ts
7
+ * class Post extends ApplicationModel {
8
+ * @deco.Sortable({ scope: 'user' })
9
+ * public position: number
10
+ * }
11
+ *
12
+ * await post.update({ position: 2 }) // the records at 2 and above shift up
13
+ * ```
14
+ *
15
+ * A position past the end of the scope is clamped to the end, and a position
16
+ * below 1 — or none at all — lands the record at the end.
17
+ *
18
+ * **A save that changes the sort scope ignores a position given alongside it.**
19
+ * The record lands at the end of the scope it moves into, whatever position the
20
+ * same `update` supplied:
21
+ *
22
+ * ```ts
23
+ * await post.update({ user: otherUser, position: 1 })
24
+ * // post is now the last record in otherUser's scope, not the first
25
+ * ```
26
+ *
27
+ * Move it in two saves to place it: `await post.update({ user: otherUser })`,
28
+ * then `await post.update({ position: 1 })`.
29
+ *
30
+ * Sortable requires a query driver that supports advisory transaction locks —
31
+ * the `PostgresQueryDriver` does — since every position write serializes the
32
+ * writers of its sort scope on one.
33
+ */
1
34
  export default function Sortable(opts?: SortableOpts): any;
2
35
  interface SortableOpts {
3
36
  /**
@@ -0,0 +1,25 @@
1
+ import Dream from '../../../../Dream.js';
2
+ import DreamTransaction from '../../../../dream/DreamTransaction.js';
3
+ /**
4
+ * @internal
5
+ *
6
+ * Serializes every writer of a set of sort scopes by taking those scopes'
7
+ * transaction-scoped advisory locks. They are released when the enclosing
8
+ * transaction ends, so there is nothing to release here.
9
+ *
10
+ * Keys this transaction already holds are dropped rather than re-acquired: the
11
+ * lock is held to the end of the transaction either way, so the statement would
12
+ * do nothing but cost a round trip inside the lock window. Whatever is left is
13
+ * taken in the order given — callers that need more than one sort them first,
14
+ * so that two operations touching the same pair of scopes cannot deadlock
15
+ * against each other — and in one statement, on drivers that can express it.
16
+ *
17
+ * Waiting for a key another transaction holds is bounded by the driver, which
18
+ * raises `SortableScopeLockWaitTimedOut` rather than waiting forever; see
19
+ * `QueryDriverBase.acquireAdvisoryTransactionLocks`.
20
+ *
21
+ * @param dream - the record whose connection provides the driver
22
+ * @param txn - the transaction the locks are scoped to
23
+ * @param keys - the scope keys to acquire, in acquisition order
24
+ */
25
+ export default function acquireSortableScopeLocks(dream: Dream, txn: DreamTransaction<any>, keys: bigint[]): Promise<void>;
@@ -0,0 +1,91 @@
1
+ import Dream from '../../../../Dream.js';
2
+ import DreamTransaction from '../../../../dream/DreamTransaction.js';
3
+ /**
4
+ * The most advisory sort-scope locks a batch preflight will let one transaction
5
+ * hold.
6
+ *
7
+ * Advisory locks are entries in a cluster-wide shared lock table sized at
8
+ * startup (`max_locks_per_transaction` × `max_connections`, 6400 entries under
9
+ * the stock settings), and a transaction holds every key it takes until it
10
+ * ends. `batchSize` is the caller's to choose and nothing bounds the number of
11
+ * distinct sort scopes a batch's records span, so without a limit a wide enough
12
+ * batch over a high-cardinality scope can starve every other connection in the
13
+ * cluster of a resource they all draw on — a failure that lands on unrelated
14
+ * transactions rather than on the one that caused it.
15
+ *
16
+ * The count is **cumulative over the transaction**: each preflight round counts
17
+ * the keys the transaction already holds together with the keys that round
18
+ * would add. So the bound holds across the rounds of one stabilization loop
19
+ * (each of which can discover a different set of scopes), and across the
20
+ * batches of a multi-batch run inside a caller-owned transaction, where
21
+ * advisory locks are released only when the caller commits.
22
+ *
23
+ * The headroom this leaves: the locked-batch defaults are ten records per batch
24
+ * (`Query.BATCH_SIZES.LOCKED_DESTROY` / `LOCKED_UPDATE`), so reaching the bound
25
+ * takes a caller who raises `batchSize` by two orders of magnitude and whose
26
+ * records each occupy a sort scope of their own — or a long run of such batches
27
+ * inside one transaction. Two shapes consume the budget faster than one key per
28
+ * record: a model declaring more than one sortable field needs one key per
29
+ * field, and the attribute form of `update` needs up to two per record per
30
+ * field, since the record's current scope and its destination scope are
31
+ * different keys.
32
+ *
33
+ * Only the batch preflight enforces this. A per-record save, destroy or
34
+ * undestroy takes its keys without counting them, so what they take is counted
35
+ * by the next preflight in the same transaction but cannot itself raise.
36
+ */
37
+ export declare const MAX_SORTABLE_BATCH_SCOPE_LOCKS = 1000;
38
+ /**
39
+ * @internal
40
+ *
41
+ * The operation-level scope-lock preflight for the batched, row-locking query
42
+ * APIs — `Query#destroy({ lock: true })` and the attribute form of
43
+ * `Query#update(attributes, { lock: true })`.
44
+ *
45
+ * Why it exists: those operations take *row* locks (`takeAll({ lock: true })`)
46
+ * and then run per-record sortable work that takes *advisory* scope locks. Two
47
+ * transactions running in that order can each hold what the other is waiting
48
+ * for. Acquiring every advisory key the batch needs — across every sortable
49
+ * field and every candidate record — in one sorted pass, before a single row is
50
+ * claimed, removes the inversion: the advisory locks are always taken first, and
51
+ * always in the same order.
52
+ *
53
+ * The key set cannot simply be read once: `Query.lockedBatches` plucks only
54
+ * primary keys, so the candidates' scope columns have to be read separately, and
55
+ * that first read is not protected by any lock. So the read is stabilized the
56
+ * same way the per-record paths stabilize theirs, through the same
57
+ * `stabilizeSortableScopeLocks` protocol — acquire on what the unlocked read
58
+ * implies, re-read under those locks, take any additional key the re-read
59
+ * reveals — with the same bound, and the same `SortableScopeDidNotStabilize`
60
+ * when a batch of rows keeps moving between scopes faster than it can be read.
61
+ *
62
+ * A model with no sortable fields never reaches the driver seam, so this is
63
+ * inert — and free — for every other model.
64
+ *
65
+ * How many keys the transaction may hold is bounded — see
66
+ * {@link MAX_SORTABLE_BATCH_SCOPE_LOCKS} — and a batch that would carry it past
67
+ * the bound is refused before it claims any row. The first round refuses before
68
+ * anything at all is locked; a later round refuses after that loop's earlier
69
+ * rounds took their keys, which stay held until the transaction ends like every
70
+ * advisory lock this branch takes.
71
+ *
72
+ * What the preflight can and cannot see: `incomingScopeValue` recognizes an
73
+ * attribute that names a scope column directly or the BelongsTo association
74
+ * backing it. A destination scope produced any other way — by a custom setter,
75
+ * a virtual attribute, or a `beforeSave` hook that moves the record between
76
+ * scopes during the per-record save — is invisible here, and those shapes are
77
+ * preflight fallbacks like the callback form of `update` and a multi-batch run
78
+ * inside a caller-owned transaction: the missing key is taken as the
79
+ * per-record save reaches it, after this batch's row locks, and the residual
80
+ * failure is Postgres's `40P01` deadlock error — a visible, retryable failure
81
+ * rather than a silent wrong ordering.
82
+ *
83
+ * @param dreamInstance - a bare instance of the Query's model, used for the
84
+ * table, the connection, and the scope column resolution
85
+ * @param txn - the batch's transaction; the locks live until it ends
86
+ * @param primaryKeyValues - the candidate records this batch will try to claim
87
+ * @param incomingAttributes - the attributes the operation will write, when they
88
+ * are knowable before the claim (the attribute form of `update`); a scope
89
+ * column among them means the batch also needs the destination scope's key
90
+ */
91
+ export default function acquireStabilizedSortableBatchLocks(dreamInstance: Dream, txn: DreamTransaction<any>, primaryKeyValues: unknown[], incomingAttributes?: Record<string, unknown>): Promise<void>;
@@ -0,0 +1,31 @@
1
+ import Dream from '../../../../Dream.js';
2
+ import DreamTransaction from '../../../../dream/DreamTransaction.js';
3
+ import { SortableFieldConfig } from '../Sortable.js';
4
+ import { SortableSnapshotRead } from './sortableSnapshot.js';
5
+ /**
6
+ * @internal
7
+ *
8
+ * Takes the scope locks a per-record position-mutating operation needs, and
9
+ * returns the database snapshot read under them. Every per-record path goes
10
+ * through this: a position- or scope-changing save (from `saveDream`'s sortable
11
+ * phase), a destroy (from the sortable destroy phase), and an undestroy (from
12
+ * `doUndestroyDream`). A destroy and an undestroy have no incoming scope
13
+ * values, so their instance-derived key set is a single key per sortable field —
14
+ * the scope the instance believes the row occupies — and the snapshot is what
15
+ * corrects it.
16
+ *
17
+ * The first pass locks the key set the instance implies, since the snapshot
18
+ * cannot be read until something is locked; every later pass locks what the
19
+ * snapshot read under those locks reveals. `stabilizeSortableScopeLocks` owns
20
+ * that protocol, its bound, and its failure — including the guarantee that a
21
+ * second pass always runs, so the snapshot is read under the locks even when
22
+ * the transaction already held every key the instance implies (a record being
23
+ * walked inside a locked batch, whose preflight took the whole batch's keys).
24
+ *
25
+ * @param dream - the record whose scope is being locked
26
+ * @param txn - the transaction the operation's write runs in
27
+ * @param configs - the sortable fields this operation performs position work for
28
+ * @returns the snapshot read under the locks, alongside whether the row was
29
+ * physically there to read — an empty snapshot map on its own cannot say
30
+ */
31
+ export default function acquireStabilizedSortableScopeLocks(dream: Dream, txn: DreamTransaction<any>, configs: SortableFieldConfig[]): Promise<SortableSnapshotRead>;