@rvoh/dream 2.27.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 (322) hide show
  1. package/dist/cjs/src/Dream.js +32 -0
  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/Query.js +64 -9
  29. package/dist/cjs/src/dream/QueryDriver/Base.js +75 -2
  30. package/dist/cjs/src/dream/QueryDriver/Kysely.js +11 -4
  31. package/dist/cjs/src/dream/QueryDriver/Postgres.js +74 -0
  32. package/dist/cjs/src/dream/internal/destroyDream.js +60 -6
  33. package/dist/cjs/src/dream/internal/saveDream.js +109 -7
  34. package/dist/cjs/src/dream/internal/similarity/SimilarityBuilder.js +19 -4
  35. package/dist/cjs/src/dream/internal/softDeleteDream.js +13 -1
  36. package/dist/cjs/src/dream/internal/undestroyDream.js +56 -8
  37. package/dist/cjs/src/dream-app/index.js +21 -0
  38. package/dist/cjs/src/errors/SortableBatchRequiresTooManyScopeLocks.js +57 -0
  39. package/dist/cjs/src/errors/SortableRequiresAdvisoryTransactionLocks.js +26 -0
  40. package/dist/cjs/src/errors/SortableScopeDidNotStabilize.js +39 -0
  41. package/dist/cjs/src/errors/SortableScopeLockWaitTimedOut.js +50 -0
  42. package/dist/cjs/src/package-exports/errors.js +3 -0
  43. package/dist/esm/src/Dream.js +32 -0
  44. package/dist/esm/src/db/errors.js +4 -0
  45. package/dist/esm/src/decorators/field/sortable/Sortable.js +43 -118
  46. package/dist/esm/src/decorators/field/sortable/helpers/acquireSortableScopeLocks.js +38 -0
  47. package/dist/esm/src/decorators/field/sortable/helpers/acquireStabilizedSortableBatchLocks.js +183 -0
  48. package/dist/esm/src/decorators/field/sortable/helpers/acquireStabilizedSortableScopeLocks.js +40 -0
  49. package/dist/esm/src/decorators/field/sortable/helpers/canonicalScopeValue.js +194 -0
  50. package/dist/esm/src/decorators/field/sortable/helpers/clearCachedSortableValues.js +18 -0
  51. package/dist/esm/src/decorators/field/sortable/helpers/decrementScopedRecordsGreaterThanPosition.js +9 -9
  52. package/dist/esm/src/decorators/field/sortable/helpers/filterQueryToInstanceScope.js +20 -0
  53. package/dist/esm/src/decorators/field/sortable/helpers/filterQueryToScopeValues.js +32 -0
  54. package/dist/esm/src/decorators/field/sortable/helpers/heldSortableScopeLockKeys.js +34 -0
  55. package/dist/esm/src/decorators/field/sortable/helpers/performSortableDestroyWork.js +21 -0
  56. package/dist/esm/src/decorators/field/sortable/helpers/performSortablePositionWork.js +36 -0
  57. package/dist/esm/src/decorators/field/sortable/helpers/prepareSortableFieldsForDestroy.js +56 -0
  58. package/dist/esm/src/decorators/field/sortable/helpers/prepareSortableFieldsForSave.js +84 -0
  59. package/dist/esm/src/decorators/field/sortable/helpers/resortAllRecords.js +186 -29
  60. package/dist/esm/src/decorators/field/sortable/helpers/setPosition.js +90 -46
  61. package/dist/esm/src/decorators/field/sortable/helpers/sortableRowCache.js +73 -0
  62. package/dist/esm/src/decorators/field/sortable/helpers/sortableScopeColumns.js +42 -0
  63. package/dist/esm/src/decorators/field/sortable/helpers/sortableScopeLockKey.js +99 -0
  64. package/dist/esm/src/decorators/field/sortable/helpers/sortableScopeLockKeys.js +72 -0
  65. package/dist/esm/src/decorators/field/sortable/helpers/sortableSnapshot.js +101 -0
  66. package/dist/esm/src/decorators/field/sortable/helpers/stabilizeSortableScopeLocks.js +86 -0
  67. package/dist/esm/src/decorators/field/sortable/helpers/validPosition.js +21 -4
  68. package/dist/esm/src/decorators/field/sortable/hooks/afterSortableDestroy.js +24 -7
  69. package/dist/esm/src/decorators/field/sortable/hooks/afterSortableUpdate.js +10 -0
  70. package/dist/esm/src/dream/Query.js +64 -9
  71. package/dist/esm/src/dream/QueryDriver/Base.js +75 -2
  72. package/dist/esm/src/dream/QueryDriver/Kysely.js +11 -4
  73. package/dist/esm/src/dream/QueryDriver/Postgres.js +74 -0
  74. package/dist/esm/src/dream/internal/destroyDream.js +60 -6
  75. package/dist/esm/src/dream/internal/saveDream.js +109 -7
  76. package/dist/esm/src/dream/internal/similarity/SimilarityBuilder.js +19 -4
  77. package/dist/esm/src/dream/internal/softDeleteDream.js +13 -1
  78. package/dist/esm/src/dream/internal/undestroyDream.js +56 -8
  79. package/dist/esm/src/dream-app/index.js +21 -0
  80. package/dist/esm/src/errors/SortableBatchRequiresTooManyScopeLocks.js +57 -0
  81. package/dist/esm/src/errors/SortableRequiresAdvisoryTransactionLocks.js +26 -0
  82. package/dist/esm/src/errors/SortableScopeDidNotStabilize.js +39 -0
  83. package/dist/esm/src/errors/SortableScopeLockWaitTimedOut.js +50 -0
  84. package/dist/esm/src/package-exports/errors.js +3 -0
  85. package/dist/types/src/Dream.d.ts +32 -0
  86. package/dist/types/src/db/errors.d.ts +2 -0
  87. package/dist/types/src/decorators/field/sortable/Sortable.d.ts +33 -0
  88. package/dist/types/src/decorators/field/sortable/helpers/acquireSortableScopeLocks.d.ts +25 -0
  89. package/dist/types/src/decorators/field/sortable/helpers/acquireStabilizedSortableBatchLocks.d.ts +91 -0
  90. package/dist/types/src/decorators/field/sortable/helpers/acquireStabilizedSortableScopeLocks.d.ts +31 -0
  91. package/dist/types/src/decorators/field/sortable/helpers/canonicalScopeValue.d.ts +85 -0
  92. package/dist/types/src/decorators/field/sortable/helpers/clearCachedSortableValues.d.ts +13 -0
  93. package/dist/types/src/decorators/field/sortable/helpers/decrementScopedRecordsGreaterThanPosition.d.ts +8 -1
  94. package/dist/types/src/decorators/field/sortable/helpers/filterQueryToInstanceScope.d.ts +24 -0
  95. package/dist/types/src/decorators/field/sortable/helpers/filterQueryToScopeValues.d.ts +22 -0
  96. package/dist/types/src/decorators/field/sortable/helpers/heldSortableScopeLockKeys.d.ts +27 -0
  97. package/dist/types/src/decorators/field/sortable/helpers/performSortableDestroyWork.d.ts +14 -0
  98. package/dist/types/src/decorators/field/sortable/helpers/performSortablePositionWork.d.ts +15 -0
  99. package/dist/types/src/decorators/field/sortable/helpers/prepareSortableFieldsForDestroy.d.ts +41 -0
  100. package/dist/types/src/decorators/field/sortable/helpers/prepareSortableFieldsForSave.d.ts +35 -0
  101. package/dist/types/src/decorators/field/sortable/helpers/resortAllRecords.d.ts +32 -0
  102. package/dist/types/src/decorators/field/sortable/helpers/setPosition.d.ts +27 -11
  103. package/dist/types/src/decorators/field/sortable/helpers/sortableRowCache.d.ts +48 -0
  104. package/dist/types/src/decorators/field/sortable/helpers/sortableScopeColumns.d.ts +30 -0
  105. package/dist/types/src/decorators/field/sortable/helpers/sortableScopeLockKey.d.ts +44 -0
  106. package/dist/types/src/decorators/field/sortable/helpers/sortableScopeLockKeys.d.ts +51 -0
  107. package/dist/types/src/decorators/field/sortable/helpers/sortableSnapshot.d.ts +92 -0
  108. package/dist/types/src/decorators/field/sortable/helpers/stabilizeSortableScopeLocks.d.ts +59 -0
  109. package/dist/types/src/decorators/field/sortable/helpers/validPosition.d.ts +14 -2
  110. package/dist/types/src/decorators/field/sortable/hooks/afterSortableCreate.d.ts +1 -1
  111. package/dist/types/src/decorators/field/sortable/hooks/afterSortableDestroy.d.ts +3 -1
  112. package/dist/types/src/decorators/field/sortable/hooks/afterSortableUpdate.d.ts +1 -1
  113. package/dist/types/src/dream/Query.d.ts +41 -7
  114. package/dist/types/src/dream/QueryDriver/Base.d.ts +61 -1
  115. package/dist/types/src/dream/QueryDriver/Kysely.d.ts +6 -2
  116. package/dist/types/src/dream/QueryDriver/Postgres.d.ts +40 -0
  117. package/dist/types/src/dream/internal/softDeleteDream.d.ts +7 -1
  118. package/dist/types/src/dream-app/index.d.ts +18 -2
  119. package/dist/types/src/errors/SortableBatchRequiresTooManyScopeLocks.d.ts +37 -0
  120. package/dist/types/src/errors/SortableRequiresAdvisoryTransactionLocks.d.ts +13 -0
  121. package/dist/types/src/errors/SortableScopeDidNotStabilize.d.ts +23 -0
  122. package/dist/types/src/errors/SortableScopeLockWaitTimedOut.d.ts +34 -0
  123. package/dist/types/src/package-exports/errors.d.ts +3 -0
  124. package/docs/assets/hierarchy.js +1 -1
  125. package/docs/assets/navigation.js +1 -1
  126. package/docs/assets/search.js +1 -1
  127. package/docs/classes/db.DreamMigrationHelpers.html +11 -11
  128. package/docs/classes/db.KyselyQueryDriver.html +88 -41
  129. package/docs/classes/db.PostgresQueryDriver.html +82 -42
  130. package/docs/classes/db.QueryDriverBase.html +86 -39
  131. package/docs/classes/errors.CheckConstraintViolation.html +3 -3
  132. package/docs/classes/errors.ColumnOverflow.html +3 -3
  133. package/docs/classes/errors.CreateOrFindByFailedToCreateAndFind.html +3 -3
  134. package/docs/classes/errors.DataIncompatibleWithDatabaseField.html +3 -3
  135. package/docs/classes/errors.DataTypeColumnTypeMismatch.html +3 -3
  136. package/docs/classes/errors.DecryptionError.html +2 -2
  137. package/docs/classes/errors.DecryptionParseError.html +2 -2
  138. package/docs/classes/errors.DecryptionRotationError.html +3 -3
  139. package/docs/classes/errors.GlobalNameNotSet.html +3 -3
  140. package/docs/classes/errors.InvalidCalendarDate.html +2 -2
  141. package/docs/classes/errors.InvalidClockTime.html +2 -2
  142. package/docs/classes/errors.InvalidClockTimeTz.html +2 -2
  143. package/docs/classes/errors.InvalidDateTime.html +2 -2
  144. package/docs/classes/errors.MissingSerializersDefinition.html +3 -3
  145. package/docs/classes/errors.NonLoadedAssociation.html +3 -3
  146. package/docs/classes/errors.NotNullViolation.html +3 -3
  147. package/docs/classes/errors.RecordNotFound.html +3 -3
  148. package/docs/classes/errors.SortableBatchRequiresTooManyScopeLocks.html +61 -0
  149. package/docs/classes/errors.SortableScopeDidNotStabilize.html +48 -0
  150. package/docs/classes/errors.SortableScopeLockWaitTimedOut.html +58 -0
  151. package/docs/classes/errors.ValidationError.html +3 -3
  152. package/docs/classes/index.CalendarDate.html +33 -33
  153. package/docs/classes/index.ClockTime.html +32 -32
  154. package/docs/classes/index.ClockTimeTz.html +35 -35
  155. package/docs/classes/index.DateTime.html +86 -86
  156. package/docs/classes/index.Decorators.html +19 -19
  157. package/docs/classes/index.Dream.html +151 -124
  158. package/docs/classes/index.DreamApp.html +11 -10
  159. package/docs/classes/index.DreamTransaction.html +2 -2
  160. package/docs/classes/index.Env.html +2 -2
  161. package/docs/classes/index.Query.html +94 -84
  162. package/docs/classes/system.CliFileWriter.html +4 -4
  163. package/docs/classes/system.DreamBin.html +2 -2
  164. package/docs/classes/system.DreamCLI.html +7 -7
  165. package/docs/classes/system.DreamImporter.html +2 -2
  166. package/docs/classes/system.DreamLogos.html +2 -2
  167. package/docs/classes/system.DreamSerializerBuilder.html +11 -11
  168. package/docs/classes/system.ObjectSerializerBuilder.html +8 -8
  169. package/docs/classes/system.PathHelpers.html +3 -3
  170. package/docs/classes/utils.Encrypt.html +3 -3
  171. package/docs/classes/utils.Range.html +2 -2
  172. package/docs/functions/db.closeAllDbConnections.html +1 -1
  173. package/docs/functions/db.dreamDbConnections.html +1 -1
  174. package/docs/functions/db.untypedDb.html +1 -1
  175. package/docs/functions/db.validateColumn.html +1 -1
  176. package/docs/functions/db.validateTable.html +1 -1
  177. package/docs/functions/errors.pgErrorType.html +1 -1
  178. package/docs/functions/index.DreamSerializer.html +1 -1
  179. package/docs/functions/index.ObjectSerializer.html +1 -1
  180. package/docs/functions/index.ReplicaSafe.html +1 -1
  181. package/docs/functions/index.STI.html +1 -1
  182. package/docs/functions/index.SoftDelete.html +1 -1
  183. package/docs/functions/utils.camelize.html +1 -1
  184. package/docs/functions/utils.capitalize.html +1 -1
  185. package/docs/functions/utils.cloneDeepSafe.html +1 -1
  186. package/docs/functions/utils.compact.html +1 -1
  187. package/docs/functions/utils.groupBy.html +1 -1
  188. package/docs/functions/utils.hyphenize.html +1 -1
  189. package/docs/functions/utils.intersection.html +1 -1
  190. package/docs/functions/utils.isEmpty.html +1 -1
  191. package/docs/functions/utils.normalizeUnicode.html +1 -1
  192. package/docs/functions/utils.pascalize.html +1 -1
  193. package/docs/functions/utils.percent.html +1 -1
  194. package/docs/functions/utils.range.html +1 -1
  195. package/docs/functions/utils.round.html +1 -1
  196. package/docs/functions/utils.sanitizeString.html +1 -1
  197. package/docs/functions/utils.snakeify.html +1 -1
  198. package/docs/functions/utils.sort.html +2 -2
  199. package/docs/functions/utils.sortBy.html +1 -1
  200. package/docs/functions/utils.sortObjectByKey.html +1 -1
  201. package/docs/functions/utils.sortObjectByValue.html +1 -1
  202. package/docs/functions/utils.uncapitalize.html +1 -1
  203. package/docs/functions/utils.uniq.html +1 -1
  204. package/docs/hierarchy.html +1 -1
  205. package/docs/interfaces/openapi.OpenapiDescription.html +2 -2
  206. package/docs/interfaces/openapi.OpenapiSchemaProperties.html +1 -1
  207. package/docs/interfaces/openapi.OpenapiSchemaPropertiesShorthand.html +1 -1
  208. package/docs/interfaces/openapi.OpenapiTypeFieldObject.html +1 -1
  209. package/docs/interfaces/types.BelongsToStatement.html +2 -2
  210. package/docs/interfaces/types.DecoratorContext.html +2 -2
  211. package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
  212. package/docs/interfaces/types.DreamAppOpts.html +2 -2
  213. package/docs/interfaces/types.DreamDbConfig.html +5 -5
  214. package/docs/interfaces/types.DurationObject.html +2 -2
  215. package/docs/interfaces/types.EncryptOptions.html +2 -2
  216. package/docs/interfaces/types.InternalAnyTypedSerializerRendersMany.html +2 -2
  217. package/docs/interfaces/types.InternalAnyTypedSerializerRendersOne.html +2 -2
  218. package/docs/interfaces/types.SerializerRendererOpts.html +2 -2
  219. package/docs/modules/errors.html +1 -1
  220. package/docs/types/openapi.CommonOpenapiSchemaObjectFields.html +1 -1
  221. package/docs/types/openapi.OpenapiAllTypes.html +1 -1
  222. package/docs/types/openapi.OpenapiFormats.html +1 -1
  223. package/docs/types/openapi.OpenapiNumberFormats.html +1 -1
  224. package/docs/types/openapi.OpenapiPrimitiveBaseTypes.html +1 -1
  225. package/docs/types/openapi.OpenapiPrimitiveTypes.html +1 -1
  226. package/docs/types/openapi.OpenapiSchemaArray.html +1 -1
  227. package/docs/types/openapi.OpenapiSchemaArrayShorthand.html +1 -1
  228. package/docs/types/openapi.OpenapiSchemaBase.html +1 -1
  229. package/docs/types/openapi.OpenapiSchemaBody.html +1 -1
  230. package/docs/types/openapi.OpenapiSchemaBodyShorthand.html +1 -1
  231. package/docs/types/openapi.OpenapiSchemaCommonFields.html +1 -1
  232. package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +2 -2
  233. package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +2 -2
  234. package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +2 -2
  235. package/docs/types/openapi.OpenapiSchemaExpressionRef.html +2 -2
  236. package/docs/types/openapi.OpenapiSchemaExpressionRefSchemaShorthand.html +2 -2
  237. package/docs/types/openapi.OpenapiSchemaInteger.html +1 -1
  238. package/docs/types/openapi.OpenapiSchemaNull.html +2 -2
  239. package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
  240. package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
  241. package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +1 -1
  242. package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +1 -1
  243. package/docs/types/openapi.OpenapiSchemaObjectAnyOf.html +1 -1
  244. package/docs/types/openapi.OpenapiSchemaObjectAnyOfShorthand.html +1 -1
  245. package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
  246. package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.html +1 -1
  247. package/docs/types/openapi.OpenapiSchemaObjectOneOf.html +1 -1
  248. package/docs/types/openapi.OpenapiSchemaObjectOneOfShorthand.html +1 -1
  249. package/docs/types/openapi.OpenapiSchemaObjectShorthand.html +1 -1
  250. package/docs/types/openapi.OpenapiSchemaPrimitiveGeneric.html +1 -1
  251. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAllOf.html +2 -2
  252. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +2 -2
  253. package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +2 -2
  254. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializableRef.html +2 -2
  255. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializerRef.html +2 -2
  256. package/docs/types/openapi.OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
  257. package/docs/types/openapi.OpenapiSchemaString.html +1 -1
  258. package/docs/types/openapi.OpenapiShorthandAllTypes.html +1 -1
  259. package/docs/types/openapi.OpenapiShorthandPrimitiveBaseTypes.html +1 -1
  260. package/docs/types/openapi.OpenapiShorthandPrimitiveTypes.html +1 -1
  261. package/docs/types/openapi.OpenapiTypeField.html +1 -1
  262. package/docs/types/system.DreamAppAllowedPackageManagersEnum.html +1 -1
  263. package/docs/types/types.CalendarDateDurationUnit.html +1 -1
  264. package/docs/types/types.CalendarDateObject.html +1 -1
  265. package/docs/types/types.Camelized.html +1 -1
  266. package/docs/types/types.ClockTimeObject.html +1 -1
  267. package/docs/types/types.DbConnectionType.html +1 -1
  268. package/docs/types/types.DbTypes.html +1 -1
  269. package/docs/types/types.DreamAssociationMetadata.html +1 -1
  270. package/docs/types/types.DreamAttributes.html +1 -1
  271. package/docs/types/types.DreamClassAssociationAndStatement.html +1 -1
  272. package/docs/types/types.DreamClassColumn.html +1 -1
  273. package/docs/types/types.DreamColumn.html +1 -1
  274. package/docs/types/types.DreamColumnNames.html +1 -1
  275. package/docs/types/types.DreamLogLevel.html +1 -1
  276. package/docs/types/types.DreamLogger.html +2 -2
  277. package/docs/types/types.DreamModelSerializerType.html +1 -1
  278. package/docs/types/types.DreamOrViewModelClassSerializerKey.html +1 -1
  279. package/docs/types/types.DreamOrViewModelSerializerKey.html +1 -1
  280. package/docs/types/types.DreamParamSafeAttributes.html +1 -1
  281. package/docs/types/types.DreamParamSafeColumnNames.html +1 -1
  282. package/docs/types/types.DreamSerializable.html +1 -1
  283. package/docs/types/types.DreamSerializableArray.html +1 -1
  284. package/docs/types/types.DreamSerializerKey.html +1 -1
  285. package/docs/types/types.DreamSerializers.html +1 -1
  286. package/docs/types/types.DreamVirtualColumns.html +1 -1
  287. package/docs/types/types.DurationUnit.html +1 -1
  288. package/docs/types/types.EncryptAlgorithm.html +1 -1
  289. package/docs/types/types.HasManyStatement.html +1 -1
  290. package/docs/types/types.HasOneStatement.html +1 -1
  291. package/docs/types/types.Hyphenized.html +1 -1
  292. package/docs/types/types.Pascalized.html +1 -1
  293. package/docs/types/types.PrimaryKeyType.html +1 -1
  294. package/docs/types/types.RoundingPrecision.html +1 -1
  295. package/docs/types/types.SerializerCasing.html +1 -1
  296. package/docs/types/types.SimpleObjectSerializerType.html +1 -1
  297. package/docs/types/types.Snakeified.html +1 -1
  298. package/docs/types/types.StrictInterface.html +1 -1
  299. package/docs/types/types.UpdateableAssociationProperties.html +1 -1
  300. package/docs/types/types.UpdateableProperties.html +1 -1
  301. package/docs/types/types.ValidationType.html +1 -1
  302. package/docs/types/types.ViewModel.html +2 -2
  303. package/docs/types/types.ViewModelClass.html +1 -1
  304. package/docs/types/types.WeekdayName.html +1 -1
  305. package/docs/types/types.WhereStatementForDream.html +1 -1
  306. package/docs/types/types.WhereStatementForDreamClass.html +1 -1
  307. package/docs/variables/index.DreamConst.html +1 -1
  308. package/docs/variables/index.ops.html +1 -1
  309. package/docs/variables/openapi.openapiPrimitiveTypes.html +1 -1
  310. package/docs/variables/openapi.openapiShorthandPrimitiveTypes.html +1 -1
  311. package/docs/variables/system.DreamAppAllowedPackageManagersEnumValues.html +1 -1
  312. package/docs/variables/system.primaryKeyTypes.html +1 -1
  313. package/package.json +1 -1
  314. package/dist/cjs/src/decorators/field/sortable/helpers/applySortableScopeToQuery.js +0 -13
  315. package/dist/cjs/src/decorators/field/sortable/helpers/sortableQueryExcludingDream.js +0 -7
  316. package/dist/cjs/src/decorators/field/sortable/hooks/beforeSortableSave.js +0 -41
  317. package/dist/esm/src/decorators/field/sortable/helpers/applySortableScopeToQuery.js +0 -13
  318. package/dist/esm/src/decorators/field/sortable/helpers/sortableQueryExcludingDream.js +0 -7
  319. package/dist/esm/src/decorators/field/sortable/hooks/beforeSortableSave.js +0 -41
  320. package/dist/types/src/decorators/field/sortable/helpers/applySortableScopeToQuery.d.ts +0 -12
  321. package/dist/types/src/decorators/field/sortable/helpers/sortableQueryExcludingDream.d.ts +0 -12
  322. package/dist/types/src/decorators/field/sortable/hooks/beforeSortableSave.d.ts +0 -12
@@ -0,0 +1,35 @@
1
+ import Dream from '../../../../Dream.js';
2
+ import { SortableFieldConfig } from '../Sortable.js';
3
+ export interface SortableCache {
4
+ changingScope: boolean;
5
+ position: number | undefined;
6
+ previousPosition: number | undefined;
7
+ wasNewRecord: boolean;
8
+ }
9
+ /**
10
+ * @internal
11
+ *
12
+ * The sortable preparation phase of a save. Called from `saveDream` after every
13
+ * consumer before-hook has run — a phase, not a hook among them — so the
14
+ * decision is made from the final state of the instance: a position or scope
15
+ * change introduced by a `beforeSave` hook registered after Sortable's gets the
16
+ * same handling as one made before `save` was called, and a cache is never
17
+ * carried over from a state an intervening hook has made stale.
18
+ *
19
+ * For each sortable field this caches the values the position work needs and
20
+ * applies the position sentinel, then returns the fields whose position work
21
+ * this save will perform — the fields that make the save need a transaction of
22
+ * its own, so that the write and the position write that follows it commit
23
+ * together under one scope lock.
24
+ *
25
+ * - every create on a model with sortable fields performs position work;
26
+ * - an update performs it only when the save changes the position or one of the
27
+ * sortable scope columns.
28
+ *
29
+ * A save that skips hooks performs no position work at all, so it needs no
30
+ * transaction.
31
+ */
32
+ export default function prepareSortableFieldsForSave(dream: Dream, { alreadyPersisted, skipHooks }: {
33
+ alreadyPersisted: boolean;
34
+ skipHooks: boolean;
35
+ }): SortableFieldConfig[];
@@ -1,2 +1,34 @@
1
1
  import Dream from '../../../../Dream.js';
2
+ /**
3
+ * @internal
4
+ *
5
+ * Renumbers every sort scope of a model so that each one runs 1..n with no gaps.
6
+ *
7
+ * Four things make this safe to run against a live table:
8
+ *
9
+ * - **A driver that cannot take the scope lock fails before the table is
10
+ * read**, rather than succeeding for whichever scopes happen to be contiguous
11
+ * and throwing on the first one that is not.
12
+ * - **The STI scope is stripped**, exactly as every sortable hook strips it. An
13
+ * STI hierarchy shares one position space, so renumbering one child's rows in
14
+ * isolation would hand them positions its siblings already hold.
15
+ * - **Groups are identified structurally**, by the scope columns' `[column,
16
+ * value]` tuple rather than by joining the values into one string. Joining
17
+ * merges scopes whose values differ only in where a delimiter falls, and
18
+ * merges a null member with an empty-string one; a merged group is two
19
+ * physical sort scopes renumbered as though they were one, and no single lock
20
+ * key covers it.
21
+ * - **Every discovered scope is renumbered under its own scope lock, from a
22
+ * read taken inside that lock.** The unlocked pass below only discovers
23
+ * *which* sort scopes exist; it decides nothing about their contents, because
24
+ * a concurrent create, destroy or raw update between the two reads is
25
+ * precisely the case this exists to repair. Whether a scope needs renumbering
26
+ * at all is decided under the lock.
27
+ *
28
+ * Each scope's transaction is this function's own: `resort` cannot join a
29
+ * transaction the caller owns, and must not be called from inside one. It would
30
+ * wait on a connection of its own for a scope lock the caller's transaction may
31
+ * already hold — a wait no deadlock detector can see, which ends in
32
+ * `SortableScopeLockWaitTimedOut` rather than in progress.
33
+ */
2
34
  export default function resortAllRecords(dreamClass: typeof Dream, positionField: string, scope?: string | string[]): Promise<void>;
@@ -1,20 +1,36 @@
1
- import { SelectQueryBuilder, UpdateQueryBuilder } from 'kysely';
2
1
  import Dream from '../../../../Dream.js';
3
2
  import DreamTransaction from '../../../../dream/DreamTransaction.js';
4
3
  import Query from '../../../../dream/Query.js';
5
- import PostgresQueryDriver from '../../../../dream/QueryDriver/Postgres.js';
6
- import { SortableCache } from '../hooks/beforeSortableSave.js';
7
- interface SortableCacheWithOptionalTransaction extends SortableCache {
4
+ import { SortableCache } from './prepareSortableFieldsForSave.js';
5
+ import { SortableSnapshot } from './sortableSnapshot.js';
6
+ /**
7
+ * @internal
8
+ *
9
+ * Everything one sortable field's position write needs. The transaction is
10
+ * required: every path into `setPosition` runs from
11
+ * `performSortablePositionWork`, which is called by `saveDream` inside the
12
+ * transaction that holds the scope lock, and a position write that opened a
13
+ * transaction of its own would block on a key its own caller holds.
14
+ */
15
+ export interface SortablePositionWrite extends SortableCache {
8
16
  dream: Dream;
9
17
  positionField: string;
10
18
  query: Query<Dream>;
11
19
  scope: string | string[] | undefined;
12
- txn?: DreamTransaction<any> | undefined;
13
- }
14
- export interface SortableCacheWithRequiredTransaction extends SortableCacheWithOptionalTransaction {
15
20
  txn: DreamTransaction<any>;
21
+ /**
22
+ * The record's physical position and sort scope values, read from the row
23
+ * under the scope lock before the driver write. Present on the update path;
24
+ * absent on create, which has no prior row.
25
+ */
26
+ snapshot?: SortableSnapshot | undefined;
27
+ /**
28
+ * The position the record holds in the scope its position is being computed
29
+ * over, where the driver write has replaced it on the row with the position
30
+ * sentinel. Set only for a save that lands in the scope the row already
31
+ * occupies; on every other path the scope's own rows carry their real
32
+ * positions, or the record is joining the scope rather than already in it.
33
+ */
34
+ positionUnderSentinel?: number | undefined;
16
35
  }
17
- export default function setPosition(obj: SortableCacheWithOptionalTransaction): Promise<void>;
18
- export declare function applySortableScopesToQuery<QB extends UpdateQueryBuilder<any, any, any, any> | SelectQueryBuilder<any, any, any>>(dream: Dream, kyselyQuery: QB, whereValueCB: (column: string) => any, scope?: string | string[]): QB;
19
- export declare function getPostgresQueryDriver(connectionName: string): typeof PostgresQueryDriver;
20
- export {};
36
+ export default function setPosition(obj: SortablePositionWrite): Promise<void>;
@@ -0,0 +1,48 @@
1
+ import DreamTransaction from '../../../../dream/DreamTransaction.js';
2
+ /**
3
+ * @internal
4
+ *
5
+ * Stashes rows a locked batch's preflight read under the batch's scope locks,
6
+ * so that the per-record position work that follows does not read them a second
7
+ * time. The preflight reads every candidate's position and sort scope columns
8
+ * to derive the key set; each record's own snapshot read then asks the same
9
+ * question of the same row, in the same transaction, under the same locks.
10
+ *
11
+ * Called once per preflight, with the rows of the pass that converged: an
12
+ * earlier pass read before some of the locks were held, and a preflight that
13
+ * raised holds no lock the rest of the transaction can rely on. The rows are
14
+ * consumed one at a time and the whole entry is dropped by the first position
15
+ * write of the batch, since a compaction moves rows this holds stale positions
16
+ * for — so what it saves is the snapshot reads of the records walked before
17
+ * that first write.
18
+ *
19
+ * @param txn - the batch's transaction
20
+ * @param table - the table the rows came from
21
+ * @param primaryKey - the column the rows are keyed by
22
+ * @param rows - the rows read, carrying at least the position and scope columns
23
+ */
24
+ export declare function cacheSortableRowsReadUnderLock(txn: DreamTransaction<any>, table: string, primaryKey: string, rows: Record<string, unknown>[]): void;
25
+ /**
26
+ * @internal
27
+ *
28
+ * The cached row for one record, removed from the cache as it is handed over.
29
+ *
30
+ * Consuming is destructive because the consumer is a position-mutating
31
+ * operation: it is about to compact or renumber the scope the row sits in, and
32
+ * a second read of the same cached row would see the position the row had
33
+ * before that. {@link invalidateSortableRowCache} covers the same hazard for
34
+ * the *other* records a compaction moves.
35
+ */
36
+ export declare function takeCachedSortableRow(txn: DreamTransaction<any>, table: string, primaryKeyValue: unknown): Record<string, unknown> | undefined;
37
+ /**
38
+ * @internal
39
+ *
40
+ * Discards everything cached for this transaction. Called for two reasons: a
41
+ * position write just moved rows the cache cannot identify — a compaction
42
+ * shifts every row in one sort scope above a position, and the cache holds no
43
+ * index by scope — and the batch the rows were read for has finished, after
44
+ * which they describe nothing the rest of a caller-owned transaction should
45
+ * trust. The records still to be walked read their own rows again, which is
46
+ * what they did before the cache existed.
47
+ */
48
+ export declare function invalidateSortableRowCache(txn: DreamTransaction<any>): void;
@@ -0,0 +1,30 @@
1
+ import Dream from '../../../../Dream.js';
2
+ /**
3
+ * @internal
4
+ *
5
+ * The scope members of a sortable field paired with the database columns they
6
+ * resolve to. A member names either the column itself or the BelongsTo
7
+ * association whose foreign key backs it, and a caller matching incoming
8
+ * attributes needs both spellings; a member that resolves to no column is
9
+ * dropped.
10
+ *
11
+ * Every sortable path resolves its scope columns through this one function, so
12
+ * that a lock key, a snapshot read, a group identity and a scope filter can
13
+ * never disagree about which columns a sort scope is made of.
14
+ */
15
+ export declare function sortableScopeColumnPairs(dream: Dream, scope: string | string[] | undefined): [string, string][];
16
+ /**
17
+ * @internal
18
+ *
19
+ * The database columns a sortable field's scope resolves to.
20
+ */
21
+ export default function sortableScopeColumns(dream: Dream, scope: string | string[] | undefined): string[];
22
+ /**
23
+ * @internal
24
+ *
25
+ * The sort scope as `[column, value]` pairs — the shape a lock key is derived
26
+ * from and a group is identified by. The value for each column comes from the
27
+ * caller, which decides whether it is reading the in-memory instance or a
28
+ * snapshot of the row.
29
+ */
30
+ export declare function sortableScopeValues(dream: Dream, scope: string | string[] | undefined, valueForColumn: (column: string) => unknown): [string, unknown][];
@@ -0,0 +1,44 @@
1
+ import Dream from '../../../../Dream.js';
2
+ /**
3
+ * @internal
4
+ *
5
+ * Derives the advisory lock key that makes one sort scope's position
6
+ * computation exclusive. Every position-mutating path derives it the same way,
7
+ * since two paths that disagree about the key do not exclude each other.
8
+ *
9
+ * The key covers:
10
+ *
11
+ * - the **physical database name**, because Postgres advisory locks are scoped
12
+ * to the cluster rather than to a database. Without it, two unrelated
13
+ * databases in one cluster — including this repo's own parallel test
14
+ * databases — would contend on the same key, and that contention shows up
15
+ * only as writers of unrelated scopes queueing behind each other.
16
+ * - the table and the position field, since a model can declare several
17
+ * sortable fields, each with its own position space.
18
+ * - the scope column values, which are what a "sort scope" actually is.
19
+ *
20
+ * Values are encoded structurally rather than joined into a string, so a null
21
+ * member is distinct from an empty string and no combination of delimiters in
22
+ * one member can be mistaken for a different tuple. They are also canonicalized
23
+ * against the column's database type (`canonicalScopeValue`), so a writer
24
+ * holding a pending JavaScript value and a writer holding the value Postgres
25
+ * hydrated derive one key rather than two.
26
+ *
27
+ * **Scope values are assumed non-adversarial.** The hash is FNV-1a, which is
28
+ * unkeyed: anyone who can choose a scope column's value — where that column
29
+ * holds user-supplied text rather than a foreign key — can compute a value
30
+ * whose key equals another scope's, and share that scope's lock with it. A
31
+ * collision costs contention and never correctness, since a shared lock
32
+ * over-serializes rather than under-locking, and the bounded wait
33
+ * (`sortableScopeLockTimeout`) caps what a deliberate one can achieve at
34
+ * stalls rather than at hangs. A keyed digest would make deliberate collisions
35
+ * expensive, and is not used: changing the derivation would give old and new
36
+ * processes different keys for one scope during a rolling deploy, which is the
37
+ * race this lock exists to eliminate.
38
+ *
39
+ * @param dream - the record whose scope is being locked
40
+ * @param positionField - the sortable position column
41
+ * @param scopeValues - `[column, value]` pairs for the scope being locked
42
+ * @returns a 64-bit signed key
43
+ */
44
+ export default function sortableScopeLockKey(dream: Dream, positionField: string, scopeValues: [string, unknown][]): bigint;
@@ -0,0 +1,51 @@
1
+ import Dream from '../../../../Dream.js';
2
+ import { SortableFieldConfig } from '../Sortable.js';
3
+ import { SortableSnapshots } from './sortableSnapshot.js';
4
+ /**
5
+ * @internal
6
+ *
7
+ * The single key for the scope the record currently occupies — the scope its
8
+ * position is about to be computed within. Used by the position write itself,
9
+ * where the record already carries its final scope values.
10
+ */
11
+ export declare function sortableScopeLockKeyForCurrentScope(dream: Dream, positionField: string, scope: string | string[] | undefined): bigint;
12
+ /**
13
+ * @internal
14
+ *
15
+ * Every key a create's position work needs — one per sortable field, derived
16
+ * from the scope values the row now carries — sorted and deduplicated, so that
17
+ * a multi-sortable-field create acquires in the same global order as every
18
+ * other position-mutating operation. Declaration order cannot serve: the keys
19
+ * hash the runtime scope values, so which field's key sorts first varies from
20
+ * record to record.
21
+ */
22
+ export declare function sortableCurrentScopeLockKeys(dream: Dream, configs: SortableFieldConfig[]): bigint[];
23
+ /**
24
+ * @internal
25
+ *
26
+ * Every key a per-record position-mutating operation needs, sorted and
27
+ * deduplicated. A save that moves a record between sort scopes compacts the
28
+ * scope it leaves and makes room in the scope it enters, so it needs both keys;
29
+ * a save within one scope needs one, and so do a destroy and an undestroy, which
30
+ * write no scope values at all.
31
+ *
32
+ * The *new*-scope key comes from the values being written. The *old*-scope key
33
+ * comes from the database snapshot of the record's physical scope columns when
34
+ * one is available, and only falls back to the instance's pending changes when
35
+ * it is not — which is the case exactly once per save, on the first
36
+ * acquisition, because the snapshot cannot be read until something is locked.
37
+ * The caller closes that window by re-reading under the lock and calling this
38
+ * again with the snapshot; see `acquireStabilizedSortableScopeLocks`.
39
+ *
40
+ * A pending change is only an *old* scope when it has a previous persisted
41
+ * value. A record created earlier in this process reports every column as
42
+ * changed with `was === undefined` (`Dream#changes` compares against the
43
+ * pre-save state), which is not a scope the row was ever in: reading it as one
44
+ * would take a second, bogus lock — and, since every such record produces the
45
+ * same all-null scope tuple, it would be the *same* bogus key for every record
46
+ * in the table, serializing writers that touch unrelated scopes.
47
+ *
48
+ * Either way this must be called *before* the driver write overwrites the
49
+ * record's scope columns.
50
+ */
51
+ export declare function sortableSaveLockKeys(dream: Dream, configs: SortableFieldConfig[], snapshots?: SortableSnapshots): bigint[];
@@ -0,0 +1,92 @@
1
+ import Dream from '../../../../Dream.js';
2
+ import DreamTransaction from '../../../../dream/DreamTransaction.js';
3
+ import { SortableFieldConfig } from '../Sortable.js';
4
+ /**
5
+ * @internal
6
+ *
7
+ * What a sortable position-mutating path knows about the row as it physically
8
+ * exists, rather than as the in-memory instance remembers it.
9
+ *
10
+ * `position` is the row's real position; `scopeValues` are the row's real sort
11
+ * scope column values, keyed by column. An instance loaded before another
12
+ * writer moved the row carries neither.
13
+ */
14
+ export interface SortableSnapshot {
15
+ position: number | null;
16
+ scopeValues: Map<string, unknown>;
17
+ }
18
+ /**
19
+ * @internal
20
+ *
21
+ * Snapshots keyed by position field — a model may declare several sortable
22
+ * fields, each with its own scope and its own position space.
23
+ */
24
+ export type SortableSnapshots = Map<string, SortableSnapshot>;
25
+ /**
26
+ * @internal
27
+ *
28
+ * The outcome of a snapshot read: the snapshots themselves, plus whether the
29
+ * row was physically there to read.
30
+ *
31
+ * The two are deliberately separate. An empty `snapshots` map means only "no
32
+ * snapshot is available", which the model having no sortable fields produces
33
+ * just as readily as a row another writer has already deleted — and a caller
34
+ * that reads absence out of the empty map compacts a scope this operation never
35
+ * vacated.
36
+ */
37
+ export interface SortableSnapshotRead {
38
+ /**
39
+ * False only when a read positively found no row. A read that never had to
40
+ * run — a model with no sortable fields — reports true, since it observed
41
+ * nothing.
42
+ */
43
+ rowExists: boolean;
44
+ snapshots: SortableSnapshots;
45
+ }
46
+ /**
47
+ * @internal
48
+ *
49
+ * Reads the record's real position and sort scope column values straight from
50
+ * the row, inside the given transaction.
51
+ *
52
+ * The read deliberately bypasses Dream's default scopes and goes to the table
53
+ * by primary key: soft-deleted rows have to be readable here (undestroy reads
54
+ * one), and an STI child's row is the same physical row whatever scope the
55
+ * class carries.
56
+ *
57
+ * When the row is not there the snapshots are empty and `rowExists` is false;
58
+ * a caller that goes on to compute a shift falls back to the instance, which is
59
+ * the pre-existing behavior.
60
+ *
61
+ * @param dream - the record to snapshot
62
+ * @param txn - the transaction the read runs in; the read must be protected by
63
+ * whatever scope lock the caller holds in that transaction
64
+ * @param configs - the sortable fields whose position and scope columns to read
65
+ */
66
+ export declare function readSortableSnapshots(dream: Dream, txn: DreamTransaction<any>, configs: SortableFieldConfig[]): Promise<SortableSnapshotRead>;
67
+ /**
68
+ * @internal
69
+ *
70
+ * Stashes snapshots on the instance so the phase that computes the shift can
71
+ * reach the read the earlier phase made. The read and the shift are separate
72
+ * calls on every path — `prepareSortableFieldsForDestroy` to
73
+ * `performSortableDestroyWork`, the `saveDream` lock acquisition to
74
+ * `performSortablePositionWork` — and the instance is what crosses between
75
+ * them.
76
+ */
77
+ export declare function cacheSortableSnapshots(dream: Dream, snapshots: SortableSnapshots): void;
78
+ /**
79
+ * @internal
80
+ */
81
+ export declare function sortableSnapshotFor(dream: Dream, positionField: string): SortableSnapshot | undefined;
82
+ /**
83
+ * @internal
84
+ */
85
+ export declare function clearSortableSnapshot(dream: Dream, positionField: string): void;
86
+ /**
87
+ * @internal
88
+ *
89
+ * The value to filter a sort scope column by: the row's real value when a
90
+ * snapshot is available, and the caller's own fallback when it is not.
91
+ */
92
+ export declare function snapshotScopeValue(snapshot: SortableSnapshot | undefined, column: string, fallback: () => unknown): unknown;
@@ -0,0 +1,59 @@
1
+ import Dream from '../../../../Dream.js';
2
+ import DreamTransaction from '../../../../dream/DreamTransaction.js';
3
+ /**
4
+ * How many acquisition passes a position-mutating operation will make while
5
+ * following a record that has been moved to another sort scope, before giving
6
+ * up. One pass is the ordinary case (lock what the caller says, read, agree); a
7
+ * second covers a competitor that moved the row between the two; a third exists
8
+ * so that a single unlucky retry is not fatal. Beyond that the row is being
9
+ * moved faster than it can be read, and raising is better than compacting a
10
+ * scope it no longer occupies.
11
+ *
12
+ * The bound counts acquisitions, and every one of them counts: the convergence
13
+ * check runs after the last acquisition and the read taken under it, so an
14
+ * operation that settles on its final pass settles rather than raising.
15
+ */
16
+ export declare const MAX_SCOPE_STABILIZATION_ROUNDS = 3;
17
+ /**
18
+ * @internal
19
+ *
20
+ * The stabilization protocol every sortable lock acquisition follows, in one
21
+ * place: acquire the keys the current knowledge implies, read again under those
22
+ * locks, and take whatever additional key the read reveals — until a pass
23
+ * reveals nothing new, or the bound above is exhausted.
24
+ *
25
+ * The bootstrapping problem it solves: a sort scope's lock key is derived from
26
+ * the record's *physical* scope columns, and those cannot be read safely until
27
+ * that scope is locked. So the first pass locks what the caller can know without
28
+ * reading — an instance's own values, or an unprotected candidate read — and
29
+ * every later pass locks what the read taken under those locks reveals.
30
+ *
31
+ * Nothing here is ever unwound: the locks are transaction-scoped advisory locks,
32
+ * which are additive and are released only when the transaction ends. A pass
33
+ * that discovers a new key simply also takes it, holding at worst one lock too
34
+ * many for the rest of the transaction — which costs contention and never
35
+ * correctness. That is what lets this run inside a caller-owned transaction
36
+ * unchanged; no path here restarts or rolls back a transaction it did not open.
37
+ *
38
+ * Keys the transaction already holds — taken by an earlier phase of the same
39
+ * operation, or by the preflight of the locked batch this record is being
40
+ * walked in — are not taken again; they are held to the end of the transaction
41
+ * regardless. Convergence is judged on that same set, so a pass whose read
42
+ * reveals only keys already held is a pass that agrees.
43
+ *
44
+ * Keys are taken in sorted order within a pass, so that two operations touching
45
+ * the same set of scopes cannot deadlock against each other. Across passes the
46
+ * order is discovery-driven and therefore unsortable in principle, which is the
47
+ * named fallback: two operations moving records in opposite directions between
48
+ * the same two scopes can raise Postgres's `40P01` deadlock error, a visible
49
+ * retryable failure.
50
+ *
51
+ * @param dream - a record of the model being locked; supplies the connection
52
+ * and, when this fails, the class name in the error
53
+ * @param txn - the transaction the locks are scoped to
54
+ * @param keysForRound - reads whatever this pass can see — the caller's own
55
+ * snapshot or candidate read — and returns every lock key it implies. Called
56
+ * once per pass with the number of acquisitions made so far, so a caller whose
57
+ * first pass needs no read can skip it on round 0
58
+ */
59
+ export default function stabilizeSortableScopeLocks(dream: Dream, txn: DreamTransaction<any>, keysForRound: (round: number) => Promise<Iterable<bigint>>): Promise<void>;
@@ -1,2 +1,14 @@
1
- import { SortableCacheWithRequiredTransaction } from './setPosition.js';
2
- export default function validPosition({ changingScope, dream, position, positionField, query, scope, wasNewRecord, previousPosition, txn, }: SortableCacheWithRequiredTransaction): Promise<number>;
1
+ import { SortablePositionWrite } from './setPosition.js';
2
+ /**
3
+ * @internal
4
+ *
5
+ * The position the record will be written at: the caller's, clamped to the
6
+ * scope the record now occupies.
7
+ *
8
+ * The scope is read off the instance rather than off the snapshot because the
9
+ * scope in question is the one being *entered*, which is the instance's — the
10
+ * snapshot holds the scope the row was in before the driver write. On every
11
+ * path into here the driver write has already landed the instance's scope
12
+ * values on the row.
13
+ */
14
+ export default function validPosition({ changingScope, dream, position, positionField, positionUnderSentinel, query, scope, wasNewRecord, previousPosition, txn, }: SortablePositionWrite): Promise<number>;
@@ -5,6 +5,6 @@ export default function afterSortableCreate({ positionField, dream, query, txn,
5
5
  positionField: string;
6
6
  dream: Dream;
7
7
  query: Query<Dream>;
8
- txn?: DreamTransaction<any> | undefined;
8
+ txn: DreamTransaction<any>;
9
9
  scope: string | string[] | undefined;
10
10
  }): Promise<void>;
@@ -1,8 +1,10 @@
1
1
  import Dream from '../../../../Dream.js';
2
+ import DreamTransaction from '../../../../dream/DreamTransaction.js';
2
3
  import Query from '../../../../dream/Query.js';
3
- export default function afterSortableDestroy({ positionField, dream, query, scope, }: {
4
+ export default function afterSortableDestroy({ positionField, dream, query, scope, txn, }: {
4
5
  positionField: string;
5
6
  dream: Dream;
6
7
  query: Query<Dream>;
7
8
  scope: string | string[] | undefined;
9
+ txn: DreamTransaction<any>;
8
10
  }): Promise<void>;
@@ -5,6 +5,6 @@ export default function afterUpdateSortable({ positionField, dream, query, txn,
5
5
  positionField: string;
6
6
  dream: Dream;
7
7
  query: Query<Dream>;
8
- txn?: DreamTransaction<any> | undefined;
8
+ txn: DreamTransaction<any>;
9
9
  scope: string | string[] | undefined;
10
10
  }): Promise<void>;
@@ -1328,9 +1328,11 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
1328
1328
  * read is a plain `FOR UPDATE`: if another transaction already holds a lock
1329
1329
  * on one of the batch's rows, this call blocks until that transaction ends,
1330
1330
  * holding its own transaction and its pooled connection open the whole
1331
- * time. Dream sets no `lock_timeout` or `statement_timeout`, so a
1332
- * long-running writer on the other side stalls the run indefinitely and, at
1333
- * scale, ties up connections. If that is a risk in your application, set a
1331
+ * time. Dream sets no `lock_timeout` or `statement_timeout` on a row-lock
1332
+ * wait — the one it does set, around a sortable model's scope-lock
1333
+ * acquisition, is restored before the rows are claimed — so a long-running
1334
+ * writer on the other side stalls the run indefinitely and, at scale, ties
1335
+ * up connections. If that is a risk in your application, set a
1334
1336
  * `lock_timeout` on the connection so a stuck batch fails instead of
1335
1337
  * hanging.
1336
1338
  * - **A `limit` or `offset` on the Query is incompatible with `destroy` on
@@ -1408,6 +1410,36 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
1408
1410
  * When the Query already carries a transaction, that transaction is used for
1409
1411
  * every batch rather than opening one per batch, so the caller's transaction
1410
1412
  * boundary wins and the locks are held until the caller commits.
1413
+ *
1414
+ * On a sortable model, the advisory scope keys this batch needs are acquired
1415
+ * before the claim (see `acquireStabilizedSortableBatchLocks`), which removes
1416
+ * the row-lock/advisory-lock inversion for the rows the batch itself claims.
1417
+ * Two things stay outside that guarantee and are fallbacks rather than
1418
+ * guarantees, with Postgres's `40P01` as the residual failure in both:
1419
+ *
1420
+ * - the ordering is **per batch**. A multi-batch run inside a caller-owned
1421
+ * transaction keeps batch N-1's row locks while batch N preflights. The
1422
+ * same shape accumulates *advisory* locks: they are released only when the
1423
+ * transaction ends, so every batch's scope keys are still held when the
1424
+ * next one preflights. That accumulation is bounded — the preflight counts
1425
+ * the keys the transaction already holds along with the ones the batch
1426
+ * would add, and refuses with `SortableBatchRequiresTooManyScopeLocks`
1427
+ * rather than letting a long run over a high-cardinality sort scope exhaust
1428
+ * `max_locks_per_transaction` for the whole cluster — so a run long enough
1429
+ * raises instead of completing. Letting Dream open a transaction per batch
1430
+ * releases the keys as it goes and gives each batch the whole budget.
1431
+ * - the preflight covers **the claimed rows' own scopes only**. A
1432
+ * `dependent: 'destroy'` cascade under `destroy({ lock: true })` destroys
1433
+ * child records of other tables, and a sortable child takes its own table's
1434
+ * advisory key inside `perBatch` — after this transaction is already
1435
+ * holding the root rows' row locks. Walking
1436
+ * `dependentDestroyAssociationNames()` to fold those keys into the sorted
1437
+ * pass is deliberately not done: the cascade's shape is only knowable per
1438
+ * record, and the deadlock it leaves is detected and retryable. The keys
1439
+ * the preflight *did* take are held across the whole batch, cascade
1440
+ * included — a per-record destroy holds its scope lock across one delete
1441
+ * and one compaction, while here the hold spans batch size × cascade depth.
1442
+ * A smaller `batchSize` shortens it (the default is 10).
1411
1443
  */
1412
1444
  private lockedBatches;
1413
1445
  /**
@@ -1651,10 +1683,12 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
1651
1683
  * read is a plain `FOR UPDATE`: if another transaction already holds a
1652
1684
  * lock on one of the batch's rows, this call blocks until that transaction
1653
1685
  * ends, holding its own transaction and its pooled connection open the
1654
- * whole time. Dream sets no `lock_timeout` or `statement_timeout`, so a
1655
- * long-running writer on the other side stalls the run indefinitely and,
1656
- * at scale, ties up connections. If that is a risk in your application,
1657
- * set a `lock_timeout` on the connection so a stuck batch fails instead of
1686
+ * whole time. Dream sets no `lock_timeout` or `statement_timeout` on a
1687
+ * row-lock wait — the one it does set, around a sortable model's scope-lock
1688
+ * acquisition, is restored before the rows are claimed — so a long-running
1689
+ * writer on the other side stalls the run indefinitely and, at scale, ties
1690
+ * up connections. If that is a risk in your application, set a
1691
+ * `lock_timeout` on the connection so a stuck batch fails instead of
1658
1692
  * hanging.
1659
1693
  * - **`skipHooks` composes with `lock`, but stays on the per-instance
1660
1694
  * path.** With an attributes object and no `lock`, `skipHooks: true`
@@ -145,6 +145,62 @@ export default class QueryDriverBase<DreamInstance extends Dream> {
145
145
  * worker's lifetime.
146
146
  */
147
147
  static openTestDatabaseLockSession(connectionName: string): Promise<TestDatabaseLockSession>;
148
+ /**
149
+ * @internal
150
+ *
151
+ * Whether this driver implements the transaction-scoped advisory lock seam
152
+ * ({@link acquireAdvisoryTransactionLocks}). Adapters that can hold a
153
+ * key-addressed lock for the life of a transaction override this to `true`.
154
+ * It defaults to `false` so that a driver without the primitive fails loud
155
+ * for features that depend on it (today, the `Sortable` decorator) rather
156
+ * than silently racing on position values.
157
+ */
158
+ static supportsAdvisoryTransactionLocks: boolean;
159
+ /**
160
+ * @internal
161
+ *
162
+ * Acquire exclusive, transaction-scoped locks on arbitrary keys, waiting
163
+ * until any other transaction holding one of them finishes. The locks are
164
+ * released when the enclosing transaction commits or rolls back — never by an
165
+ * explicit unlock — so callers only ever have to acquire (Postgres:
166
+ * `pg_advisory_xact_lock(bigint)`).
167
+ *
168
+ * Unlike {@link applyRowLock}, this does not depend on rows existing: it is
169
+ * the only way to serialize writers that are about to insert into the same
170
+ * logical group. The `Sortable` decorator uses it to make each sort scope's
171
+ * position computation exclusive, keyed on the physical database, table,
172
+ * position field and scope values.
173
+ *
174
+ * The seam takes the whole key set rather than one key at a time so that a
175
+ * driver able to express it can take them in a single round trip — the
176
+ * batched, row-locking query APIs can need one key per candidate record per
177
+ * sortable field. **Keys must be acquired in the order given**: callers sort
178
+ * them, and that shared order is what keeps two operations over the same
179
+ * scopes from deadlocking. A driver that has no bulk form implements this by
180
+ * looping over the keys in order.
181
+ *
182
+ * **The wait must be bounded.** A caller that finds a key held waits for the
183
+ * holder's transaction, and an implementation that waits forever turns an
184
+ * ordinary mistake — a sortable operation issued without the enclosing
185
+ * transaction, so that it waits on a key that transaction itself holds — into
186
+ * a permanently wedged connection that no deadlock detector can see, because
187
+ * only one side is waiting. So an implementation applies the
188
+ * `sortableScopeLockTimeout` DreamApp option (milliseconds) to the
189
+ * acquisition and raises `SortableScopeLockWaitTimedOut` when it elapses. A
190
+ * configured `0` means no bound, and is applied just as literally: the
191
+ * acquisition waits without bound even where the connection carries a
192
+ * wait bound of its own. The bound belongs to
193
+ * the acquisition alone: whatever lock-wait behavior the connection was
194
+ * configured with has to be in force again by the time this returns, so that
195
+ * the row locks {@link applyRowLock} takes are unaffected.
196
+ *
197
+ * Only ever called when {@link supportsAdvisoryTransactionLocks} is `true`;
198
+ * the base default throws.
199
+ *
200
+ * @param txn - the transaction the locks are scoped to
201
+ * @param keys - the 64-bit signed lock keys, in acquisition order
202
+ */
203
+ static acquireAdvisoryTransactionLocks(txn: DreamTransaction<Dream>, keys: bigint[]): Promise<void>;
148
204
  static getColumnData(connectionName: string, tableName: string, allTableAssociationData: {
149
205
  [key: string]: SchemaBuilderAssociationData;
150
206
  }): Promise<{
@@ -391,8 +447,12 @@ export default class QueryDriverBase<DreamInstance extends Dream> {
391
447
  * @param dream - the dream instance you wish to destroy
392
448
  * @param txn - a transaction to encapsulate, consistently provided by underlying dream mechanisms
393
449
  * @param reallyDestroy - whether or not to reallyDestroy. If false, soft delete will be attempted when relevant
450
+ * @returns the number of rows this call removed (or soft deleted). Zero means
451
+ * the row was already gone, or that a `preventDeletion` veto stopped the
452
+ * write — callers distinguish the two by the veto flag, and use the count to
453
+ * decide whether there is anything for the after-destroy hooks to act on.
394
454
  */
395
- static destroyDream(dream: Dream, txn: DreamTransaction<Dream>, reallyDestroy: boolean): Promise<void>;
455
+ static destroyDream(dream: Dream, txn: DreamTransaction<Dream>, reallyDestroy: boolean): Promise<number>;
396
456
  static get syncDialect(): string;
397
457
  /**
398
458
  * Returns the sql that would be executed by this Query