@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.
- package/dist/cjs/src/Dream.js +32 -17
- package/dist/cjs/src/db/errors.js +4 -0
- package/dist/cjs/src/decorators/field/sortable/Sortable.js +43 -118
- package/dist/cjs/src/decorators/field/sortable/helpers/acquireSortableScopeLocks.js +38 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/acquireStabilizedSortableBatchLocks.js +183 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/acquireStabilizedSortableScopeLocks.js +40 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/canonicalScopeValue.js +194 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/clearCachedSortableValues.js +18 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/decrementScopedRecordsGreaterThanPosition.js +9 -9
- package/dist/cjs/src/decorators/field/sortable/helpers/filterQueryToInstanceScope.js +20 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/filterQueryToScopeValues.js +32 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/heldSortableScopeLockKeys.js +34 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/performSortableDestroyWork.js +21 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/performSortablePositionWork.js +36 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/prepareSortableFieldsForDestroy.js +56 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/prepareSortableFieldsForSave.js +84 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/resortAllRecords.js +186 -29
- package/dist/cjs/src/decorators/field/sortable/helpers/setPosition.js +90 -46
- package/dist/cjs/src/decorators/field/sortable/helpers/sortableRowCache.js +73 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/sortableScopeColumns.js +42 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/sortableScopeLockKey.js +99 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/sortableScopeLockKeys.js +72 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/sortableSnapshot.js +101 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/stabilizeSortableScopeLocks.js +86 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/validPosition.js +21 -4
- package/dist/cjs/src/decorators/field/sortable/hooks/afterSortableDestroy.js +24 -7
- package/dist/cjs/src/decorators/field/sortable/hooks/afterSortableUpdate.js +10 -0
- package/dist/cjs/src/dream/DreamClassTransactionBuilder.js +0 -18
- package/dist/cjs/src/dream/Query.js +64 -32
- package/dist/cjs/src/dream/QueryDriver/Base.js +75 -2
- package/dist/cjs/src/dream/QueryDriver/Kysely.js +19 -12
- package/dist/cjs/src/dream/QueryDriver/Postgres.js +74 -0
- package/dist/cjs/src/dream/internal/destroyDream.js +60 -6
- package/dist/cjs/src/dream/internal/saveDream.js +109 -7
- package/dist/cjs/src/dream/internal/similarity/SimilarityBuilder.js +19 -4
- package/dist/cjs/src/dream/internal/softDeleteDream.js +13 -1
- package/dist/cjs/src/dream/internal/undestroyDream.js +56 -8
- package/dist/cjs/src/dream-app/index.js +21 -0
- package/dist/cjs/src/errors/SortableBatchRequiresTooManyScopeLocks.js +57 -0
- package/dist/cjs/src/errors/SortableRequiresAdvisoryTransactionLocks.js +26 -0
- package/dist/cjs/src/errors/SortableScopeDidNotStabilize.js +39 -0
- package/dist/cjs/src/errors/SortableScopeLockWaitTimedOut.js +50 -0
- package/dist/cjs/src/package-exports/errors.js +3 -0
- package/dist/esm/src/Dream.js +32 -17
- package/dist/esm/src/db/errors.js +4 -0
- package/dist/esm/src/decorators/field/sortable/Sortable.js +43 -118
- package/dist/esm/src/decorators/field/sortable/helpers/acquireSortableScopeLocks.js +38 -0
- package/dist/esm/src/decorators/field/sortable/helpers/acquireStabilizedSortableBatchLocks.js +183 -0
- package/dist/esm/src/decorators/field/sortable/helpers/acquireStabilizedSortableScopeLocks.js +40 -0
- package/dist/esm/src/decorators/field/sortable/helpers/canonicalScopeValue.js +194 -0
- package/dist/esm/src/decorators/field/sortable/helpers/clearCachedSortableValues.js +18 -0
- package/dist/esm/src/decorators/field/sortable/helpers/decrementScopedRecordsGreaterThanPosition.js +9 -9
- package/dist/esm/src/decorators/field/sortable/helpers/filterQueryToInstanceScope.js +20 -0
- package/dist/esm/src/decorators/field/sortable/helpers/filterQueryToScopeValues.js +32 -0
- package/dist/esm/src/decorators/field/sortable/helpers/heldSortableScopeLockKeys.js +34 -0
- package/dist/esm/src/decorators/field/sortable/helpers/performSortableDestroyWork.js +21 -0
- package/dist/esm/src/decorators/field/sortable/helpers/performSortablePositionWork.js +36 -0
- package/dist/esm/src/decorators/field/sortable/helpers/prepareSortableFieldsForDestroy.js +56 -0
- package/dist/esm/src/decorators/field/sortable/helpers/prepareSortableFieldsForSave.js +84 -0
- package/dist/esm/src/decorators/field/sortable/helpers/resortAllRecords.js +186 -29
- package/dist/esm/src/decorators/field/sortable/helpers/setPosition.js +90 -46
- package/dist/esm/src/decorators/field/sortable/helpers/sortableRowCache.js +73 -0
- package/dist/esm/src/decorators/field/sortable/helpers/sortableScopeColumns.js +42 -0
- package/dist/esm/src/decorators/field/sortable/helpers/sortableScopeLockKey.js +99 -0
- package/dist/esm/src/decorators/field/sortable/helpers/sortableScopeLockKeys.js +72 -0
- package/dist/esm/src/decorators/field/sortable/helpers/sortableSnapshot.js +101 -0
- package/dist/esm/src/decorators/field/sortable/helpers/stabilizeSortableScopeLocks.js +86 -0
- package/dist/esm/src/decorators/field/sortable/helpers/validPosition.js +21 -4
- package/dist/esm/src/decorators/field/sortable/hooks/afterSortableDestroy.js +24 -7
- package/dist/esm/src/decorators/field/sortable/hooks/afterSortableUpdate.js +10 -0
- package/dist/esm/src/dream/DreamClassTransactionBuilder.js +0 -18
- package/dist/esm/src/dream/Query.js +64 -32
- package/dist/esm/src/dream/QueryDriver/Base.js +75 -2
- package/dist/esm/src/dream/QueryDriver/Kysely.js +19 -12
- package/dist/esm/src/dream/QueryDriver/Postgres.js +74 -0
- package/dist/esm/src/dream/internal/destroyDream.js +60 -6
- package/dist/esm/src/dream/internal/saveDream.js +109 -7
- package/dist/esm/src/dream/internal/similarity/SimilarityBuilder.js +19 -4
- package/dist/esm/src/dream/internal/softDeleteDream.js +13 -1
- package/dist/esm/src/dream/internal/undestroyDream.js +56 -8
- package/dist/esm/src/dream-app/index.js +21 -0
- package/dist/esm/src/errors/SortableBatchRequiresTooManyScopeLocks.js +57 -0
- package/dist/esm/src/errors/SortableRequiresAdvisoryTransactionLocks.js +26 -0
- package/dist/esm/src/errors/SortableScopeDidNotStabilize.js +39 -0
- package/dist/esm/src/errors/SortableScopeLockWaitTimedOut.js +50 -0
- package/dist/esm/src/package-exports/errors.js +3 -0
- package/dist/types/src/Dream.d.ts +35 -2
- package/dist/types/src/db/errors.d.ts +2 -0
- package/dist/types/src/decorators/field/sortable/Sortable.d.ts +33 -0
- package/dist/types/src/decorators/field/sortable/helpers/acquireSortableScopeLocks.d.ts +25 -0
- package/dist/types/src/decorators/field/sortable/helpers/acquireStabilizedSortableBatchLocks.d.ts +91 -0
- package/dist/types/src/decorators/field/sortable/helpers/acquireStabilizedSortableScopeLocks.d.ts +31 -0
- package/dist/types/src/decorators/field/sortable/helpers/canonicalScopeValue.d.ts +85 -0
- package/dist/types/src/decorators/field/sortable/helpers/clearCachedSortableValues.d.ts +13 -0
- package/dist/types/src/decorators/field/sortable/helpers/decrementScopedRecordsGreaterThanPosition.d.ts +8 -1
- package/dist/types/src/decorators/field/sortable/helpers/filterQueryToInstanceScope.d.ts +24 -0
- package/dist/types/src/decorators/field/sortable/helpers/filterQueryToScopeValues.d.ts +22 -0
- package/dist/types/src/decorators/field/sortable/helpers/heldSortableScopeLockKeys.d.ts +27 -0
- package/dist/types/src/decorators/field/sortable/helpers/performSortableDestroyWork.d.ts +14 -0
- package/dist/types/src/decorators/field/sortable/helpers/performSortablePositionWork.d.ts +15 -0
- package/dist/types/src/decorators/field/sortable/helpers/prepareSortableFieldsForDestroy.d.ts +41 -0
- package/dist/types/src/decorators/field/sortable/helpers/prepareSortableFieldsForSave.d.ts +35 -0
- package/dist/types/src/decorators/field/sortable/helpers/resortAllRecords.d.ts +32 -0
- package/dist/types/src/decorators/field/sortable/helpers/setPosition.d.ts +27 -11
- package/dist/types/src/decorators/field/sortable/helpers/sortableRowCache.d.ts +48 -0
- package/dist/types/src/decorators/field/sortable/helpers/sortableScopeColumns.d.ts +30 -0
- package/dist/types/src/decorators/field/sortable/helpers/sortableScopeLockKey.d.ts +44 -0
- package/dist/types/src/decorators/field/sortable/helpers/sortableScopeLockKeys.d.ts +51 -0
- package/dist/types/src/decorators/field/sortable/helpers/sortableSnapshot.d.ts +92 -0
- package/dist/types/src/decorators/field/sortable/helpers/stabilizeSortableScopeLocks.d.ts +59 -0
- package/dist/types/src/decorators/field/sortable/helpers/validPosition.d.ts +14 -2
- package/dist/types/src/decorators/field/sortable/hooks/afterSortableCreate.d.ts +1 -1
- package/dist/types/src/decorators/field/sortable/hooks/afterSortableDestroy.d.ts +3 -1
- package/dist/types/src/decorators/field/sortable/hooks/afterSortableUpdate.d.ts +1 -1
- package/dist/types/src/dream/DreamClassTransactionBuilder.d.ts +3 -2
- package/dist/types/src/dream/Query.d.ts +44 -9
- package/dist/types/src/dream/QueryDriver/Base.d.ts +61 -1
- package/dist/types/src/dream/QueryDriver/Kysely.d.ts +6 -2
- package/dist/types/src/dream/QueryDriver/Postgres.d.ts +40 -0
- package/dist/types/src/dream/internal/softDeleteDream.d.ts +7 -1
- package/dist/types/src/dream-app/index.d.ts +18 -2
- package/dist/types/src/errors/SortableBatchRequiresTooManyScopeLocks.d.ts +37 -0
- package/dist/types/src/errors/SortableRequiresAdvisoryTransactionLocks.d.ts +13 -0
- package/dist/types/src/errors/SortableScopeDidNotStabilize.d.ts +23 -0
- package/dist/types/src/errors/SortableScopeLockWaitTimedOut.d.ts +34 -0
- package/dist/types/src/package-exports/errors.d.ts +3 -0
- package/dist/types/src/types/associations/shared.d.ts +1 -1
- package/dist/types/src/types/dream.d.ts +3 -0
- package/dist/types/src/types/query.d.ts +1 -1
- package/dist/types/src/types/types/associations/shared.ts +8 -6
- package/dist/types/src/types/types/dream.ts +6 -0
- package/dist/types/src/types/types/query.ts +1 -1
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/db.DreamMigrationHelpers.html +11 -11
- package/docs/classes/db.KyselyQueryDriver.html +88 -41
- package/docs/classes/db.PostgresQueryDriver.html +82 -42
- package/docs/classes/db.QueryDriverBase.html +86 -39
- package/docs/classes/errors.CheckConstraintViolation.html +3 -3
- package/docs/classes/errors.ColumnOverflow.html +3 -3
- package/docs/classes/errors.CreateOrFindByFailedToCreateAndFind.html +3 -3
- package/docs/classes/errors.DataIncompatibleWithDatabaseField.html +3 -3
- package/docs/classes/errors.DataTypeColumnTypeMismatch.html +3 -3
- package/docs/classes/errors.DecryptionError.html +2 -2
- package/docs/classes/errors.DecryptionParseError.html +2 -2
- package/docs/classes/errors.DecryptionRotationError.html +3 -3
- package/docs/classes/errors.GlobalNameNotSet.html +3 -3
- package/docs/classes/errors.InvalidCalendarDate.html +2 -2
- package/docs/classes/errors.InvalidClockTime.html +2 -2
- package/docs/classes/errors.InvalidClockTimeTz.html +2 -2
- package/docs/classes/errors.InvalidDateTime.html +2 -2
- package/docs/classes/errors.MissingSerializersDefinition.html +3 -3
- package/docs/classes/errors.NonLoadedAssociation.html +3 -3
- package/docs/classes/errors.NotNullViolation.html +3 -3
- package/docs/classes/errors.RecordNotFound.html +3 -3
- package/docs/classes/errors.SortableBatchRequiresTooManyScopeLocks.html +61 -0
- package/docs/classes/errors.SortableScopeDidNotStabilize.html +48 -0
- package/docs/classes/errors.SortableScopeLockWaitTimedOut.html +58 -0
- package/docs/classes/errors.ValidationError.html +3 -3
- package/docs/classes/index.CalendarDate.html +33 -33
- package/docs/classes/index.ClockTime.html +32 -32
- package/docs/classes/index.ClockTimeTz.html +35 -35
- package/docs/classes/index.DateTime.html +86 -86
- package/docs/classes/index.Decorators.html +19 -19
- package/docs/classes/index.Dream.html +159 -124
- package/docs/classes/index.DreamApp.html +11 -10
- package/docs/classes/index.DreamTransaction.html +2 -2
- package/docs/classes/index.Env.html +2 -2
- package/docs/classes/index.Query.html +122 -99
- package/docs/classes/system.CliFileWriter.html +4 -4
- package/docs/classes/system.DreamBin.html +2 -2
- package/docs/classes/system.DreamCLI.html +7 -7
- package/docs/classes/system.DreamImporter.html +2 -2
- package/docs/classes/system.DreamLogos.html +2 -2
- package/docs/classes/system.DreamSerializerBuilder.html +11 -11
- package/docs/classes/system.ObjectSerializerBuilder.html +8 -8
- package/docs/classes/system.PathHelpers.html +3 -3
- package/docs/classes/utils.Encrypt.html +3 -3
- package/docs/classes/utils.Range.html +2 -2
- package/docs/functions/db.closeAllDbConnections.html +1 -1
- package/docs/functions/db.dreamDbConnections.html +1 -1
- package/docs/functions/db.untypedDb.html +1 -1
- package/docs/functions/db.validateColumn.html +1 -1
- package/docs/functions/db.validateTable.html +1 -1
- package/docs/functions/errors.pgErrorType.html +1 -1
- package/docs/functions/index.DreamSerializer.html +1 -1
- package/docs/functions/index.ObjectSerializer.html +1 -1
- package/docs/functions/index.ReplicaSafe.html +1 -1
- package/docs/functions/index.STI.html +1 -1
- package/docs/functions/index.SoftDelete.html +1 -1
- package/docs/functions/utils.camelize.html +1 -1
- package/docs/functions/utils.capitalize.html +1 -1
- package/docs/functions/utils.cloneDeepSafe.html +1 -1
- package/docs/functions/utils.compact.html +1 -1
- package/docs/functions/utils.groupBy.html +1 -1
- package/docs/functions/utils.hyphenize.html +1 -1
- package/docs/functions/utils.intersection.html +1 -1
- package/docs/functions/utils.isEmpty.html +1 -1
- package/docs/functions/utils.normalizeUnicode.html +1 -1
- package/docs/functions/utils.pascalize.html +1 -1
- package/docs/functions/utils.percent.html +1 -1
- package/docs/functions/utils.range.html +1 -1
- package/docs/functions/utils.round.html +1 -1
- package/docs/functions/utils.sanitizeString.html +1 -1
- package/docs/functions/utils.snakeify.html +1 -1
- package/docs/functions/utils.sort.html +2 -2
- package/docs/functions/utils.sortBy.html +1 -1
- package/docs/functions/utils.sortObjectByKey.html +1 -1
- package/docs/functions/utils.sortObjectByValue.html +1 -1
- package/docs/functions/utils.uncapitalize.html +1 -1
- package/docs/functions/utils.uniq.html +1 -1
- package/docs/hierarchy.html +1 -1
- package/docs/interfaces/openapi.OpenapiDescription.html +2 -2
- package/docs/interfaces/openapi.OpenapiSchemaProperties.html +1 -1
- package/docs/interfaces/openapi.OpenapiSchemaPropertiesShorthand.html +1 -1
- package/docs/interfaces/openapi.OpenapiTypeFieldObject.html +1 -1
- package/docs/interfaces/types.BelongsToStatement.html +2 -2
- package/docs/interfaces/types.DecoratorContext.html +2 -2
- package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
- package/docs/interfaces/types.DreamAppOpts.html +2 -2
- package/docs/interfaces/types.DreamDbConfig.html +5 -5
- package/docs/interfaces/types.DurationObject.html +2 -2
- package/docs/interfaces/types.EncryptOptions.html +2 -2
- package/docs/interfaces/types.InternalAnyTypedSerializerRendersMany.html +2 -2
- package/docs/interfaces/types.InternalAnyTypedSerializerRendersOne.html +2 -2
- package/docs/interfaces/types.SerializerRendererOpts.html +2 -2
- package/docs/modules/errors.html +1 -1
- package/docs/types/openapi.CommonOpenapiSchemaObjectFields.html +1 -1
- package/docs/types/openapi.OpenapiAllTypes.html +1 -1
- package/docs/types/openapi.OpenapiFormats.html +1 -1
- package/docs/types/openapi.OpenapiNumberFormats.html +1 -1
- package/docs/types/openapi.OpenapiPrimitiveBaseTypes.html +1 -1
- package/docs/types/openapi.OpenapiPrimitiveTypes.html +1 -1
- package/docs/types/openapi.OpenapiSchemaArray.html +1 -1
- package/docs/types/openapi.OpenapiSchemaArrayShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBase.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBody.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBodyShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaCommonFields.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionRef.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionRefSchemaShorthand.html +2 -2
- package/docs/types/openapi.OpenapiSchemaInteger.html +1 -1
- package/docs/types/openapi.OpenapiSchemaNull.html +2 -2
- package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAnyOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAnyOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectOneOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectOneOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaPrimitiveGeneric.html +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionAllOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializableRef.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializerRef.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
- package/docs/types/openapi.OpenapiSchemaString.html +1 -1
- package/docs/types/openapi.OpenapiShorthandAllTypes.html +1 -1
- package/docs/types/openapi.OpenapiShorthandPrimitiveBaseTypes.html +1 -1
- package/docs/types/openapi.OpenapiShorthandPrimitiveTypes.html +1 -1
- package/docs/types/openapi.OpenapiTypeField.html +1 -1
- package/docs/types/system.DreamAppAllowedPackageManagersEnum.html +1 -1
- package/docs/types/types.CalendarDateDurationUnit.html +1 -1
- package/docs/types/types.CalendarDateObject.html +1 -1
- package/docs/types/types.Camelized.html +1 -1
- package/docs/types/types.ClockTimeObject.html +1 -1
- package/docs/types/types.DbConnectionType.html +1 -1
- package/docs/types/types.DbTypes.html +1 -1
- package/docs/types/types.DreamAssociationMetadata.html +1 -1
- package/docs/types/types.DreamAttributes.html +1 -1
- package/docs/types/types.DreamClassAssociationAndStatement.html +1 -1
- package/docs/types/types.DreamClassColumn.html +1 -1
- package/docs/types/types.DreamColumn.html +1 -1
- package/docs/types/types.DreamColumnNames.html +1 -1
- package/docs/types/types.DreamLogLevel.html +1 -1
- package/docs/types/types.DreamLogger.html +2 -2
- package/docs/types/types.DreamModelSerializerType.html +1 -1
- package/docs/types/types.DreamOrViewModelClassSerializerKey.html +1 -1
- package/docs/types/types.DreamOrViewModelSerializerKey.html +1 -1
- package/docs/types/types.DreamParamSafeAttributes.html +1 -1
- package/docs/types/types.DreamParamSafeColumnNames.html +1 -1
- package/docs/types/types.DreamSerializable.html +1 -1
- package/docs/types/types.DreamSerializableArray.html +1 -1
- package/docs/types/types.DreamSerializerKey.html +1 -1
- package/docs/types/types.DreamSerializers.html +1 -1
- package/docs/types/types.DreamVirtualColumns.html +1 -1
- package/docs/types/types.DurationUnit.html +1 -1
- package/docs/types/types.EncryptAlgorithm.html +1 -1
- package/docs/types/types.HasManyStatement.html +1 -1
- package/docs/types/types.HasOneStatement.html +1 -1
- package/docs/types/types.Hyphenized.html +1 -1
- package/docs/types/types.Pascalized.html +1 -1
- package/docs/types/types.PrimaryKeyType.html +1 -1
- package/docs/types/types.RoundingPrecision.html +1 -1
- package/docs/types/types.SerializerCasing.html +1 -1
- package/docs/types/types.SimpleObjectSerializerType.html +1 -1
- package/docs/types/types.Snakeified.html +1 -1
- package/docs/types/types.StrictInterface.html +1 -1
- package/docs/types/types.UpdateableAssociationProperties.html +1 -1
- package/docs/types/types.UpdateableProperties.html +1 -1
- package/docs/types/types.ValidationType.html +1 -1
- package/docs/types/types.ViewModel.html +2 -2
- package/docs/types/types.ViewModelClass.html +1 -1
- package/docs/types/types.WeekdayName.html +1 -1
- package/docs/types/types.WhereStatementForDream.html +1 -1
- package/docs/types/types.WhereStatementForDreamClass.html +1 -1
- package/docs/variables/index.DreamConst.html +1 -1
- package/docs/variables/index.ops.html +1 -1
- package/docs/variables/openapi.openapiPrimitiveTypes.html +1 -1
- package/docs/variables/openapi.openapiShorthandPrimitiveTypes.html +1 -1
- package/docs/variables/system.DreamAppAllowedPackageManagersEnumValues.html +1 -1
- package/docs/variables/system.primaryKeyTypes.html +1 -1
- package/package.json +1 -1
- package/dist/cjs/src/decorators/field/sortable/helpers/applySortableScopeToQuery.js +0 -13
- package/dist/cjs/src/decorators/field/sortable/helpers/sortableQueryExcludingDream.js +0 -7
- package/dist/cjs/src/decorators/field/sortable/hooks/beforeSortableSave.js +0 -41
- package/dist/esm/src/decorators/field/sortable/helpers/applySortableScopeToQuery.js +0 -13
- package/dist/esm/src/decorators/field/sortable/helpers/sortableQueryExcludingDream.js +0 -7
- package/dist/esm/src/decorators/field/sortable/hooks/beforeSortableSave.js +0 -41
- package/dist/types/src/decorators/field/sortable/helpers/applySortableScopeToQuery.d.ts +0 -12
- package/dist/types/src/decorators/field/sortable/helpers/sortableQueryExcludingDream.d.ts +0 -12
- package/dist/types/src/decorators/field/sortable/hooks/beforeSortableSave.d.ts +0 -12
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import Dream from '../../../../Dream.js';
|
|
2
|
+
/**
|
|
3
|
+
* @internal
|
|
4
|
+
*
|
|
5
|
+
* The canonical text form of one sort-scope value — the thing an advisory lock
|
|
6
|
+
* key and a resort group identity are actually built from.
|
|
7
|
+
*
|
|
8
|
+
* Two writers of the same physical sort scope have to derive the same key, and
|
|
9
|
+
* they do not always hold the value in the same JavaScript shape. A save that
|
|
10
|
+
* has not been written yet holds the *pending* value the caller supplied
|
|
11
|
+
* (`'1.00'`, an uppercase UUID, an object whose keys are in source order),
|
|
12
|
+
* while a snapshot read, a create's post-INSERT read and `resort`'s table read
|
|
13
|
+
* hold the value Postgres *hydrated* (`1`, a lowercase UUID, an object whose
|
|
14
|
+
* keys are in jsonb's own order). Postgres considers those pairs one value;
|
|
15
|
+
* `typeof`-only normalization considers them two, and two keys do not exclude
|
|
16
|
+
* each other.
|
|
17
|
+
*
|
|
18
|
+
* So canonicalization is keyed on the column's runtime `dbType`, and the
|
|
19
|
+
* canonical form of each type is **the form the hydrated value already takes**.
|
|
20
|
+
* That anchoring is what makes this safe to ship without a migration or a
|
|
21
|
+
* coordinated deploy: no key derived from a database-hydrated value moves, and
|
|
22
|
+
* the pending-side keys that do move are exactly the ones that disagree with
|
|
23
|
+
* every hydrated-path writer of the same scope today.
|
|
24
|
+
*
|
|
25
|
+
* The classes handled, and what the hydrated value looks like:
|
|
26
|
+
*
|
|
27
|
+
* - `uuid` — Postgres renders UUIDs lowercase, so the canonical form is
|
|
28
|
+
* lowercase.
|
|
29
|
+
* - `numeric` / `real` / `double precision` — hydrated through
|
|
30
|
+
* `parsePostgresDecimal`, i.e. `parseFloat`, so `'1.00'` and `1` both
|
|
31
|
+
* canonicalize to `'1'`.
|
|
32
|
+
* - `jsonb` — hydrated by `JSON.parse` of jsonb's own output, whose object keys
|
|
33
|
+
* are ordered by key length and then bytewise. Objects are re-emitted in that
|
|
34
|
+
* order, recursively, so property insertion order stops mattering.
|
|
35
|
+
* - `boolean` — hydrated as a JavaScript boolean; Postgres's own input literals
|
|
36
|
+
* (`'t'`, `'yes'`, `'0'`, …) canonicalize to `'true'`/`'false'`.
|
|
37
|
+
* - `date` and the four timestamp/time types — hydrated as `CalendarDate`,
|
|
38
|
+
* `DateTime`, `ClockTime` or `ClockTimeTz`, whose JSON form is the quoted ISO
|
|
39
|
+
* string. Strings are quoted the same way so a pending `'2026-02-07'` and a
|
|
40
|
+
* hydrated `CalendarDate` agree.
|
|
41
|
+
*
|
|
42
|
+
* Everything else — `bigint` and `character varying`, which is to say the
|
|
43
|
+
* foreign keys and text columns nearly every sort scope is made of — is left on
|
|
44
|
+
* the untyped normalization below, unchanged.
|
|
45
|
+
*
|
|
46
|
+
* Two residuals are known and accepted rather than fixed:
|
|
47
|
+
*
|
|
48
|
+
* - **`citext`**: Postgres stores the case a row was written with while
|
|
49
|
+
* comparing case-insensitively, so two rows of one physical scope hydrate to
|
|
50
|
+
* values that differ in case and split the key on their own. Lowercasing
|
|
51
|
+
* would fix the split but would *move* hydrated keys, which is the one thing
|
|
52
|
+
* this design refuses to do.
|
|
53
|
+
* - **Temporal values that agree on the instant but not on the text**: a zone
|
|
54
|
+
* offset or a fractional-second precision that differs from the hydrated
|
|
55
|
+
* rendering still yields a different key. Only the string-vs-object quoting
|
|
56
|
+
* mismatch is canonicalized.
|
|
57
|
+
*
|
|
58
|
+
* Both are over-serialization or a missed exclusion between two *pending*-side
|
|
59
|
+
* writers, never a corrupt position under a key two writers do share.
|
|
60
|
+
*
|
|
61
|
+
* @param dream - the record whose scope column is being canonicalized
|
|
62
|
+
* @param column - the physical database column
|
|
63
|
+
* @param value - the pending or hydrated value held for that column
|
|
64
|
+
* @returns the canonical text form, or `null` for an absent value
|
|
65
|
+
*/
|
|
66
|
+
export default function canonicalScopeValue(dream: Dream, column: string, value: unknown): string | null;
|
|
67
|
+
/**
|
|
68
|
+
* @internal
|
|
69
|
+
*
|
|
70
|
+
* The type-blind fallback, and the shape every unrecognized `dbType` keeps:
|
|
71
|
+
* the same foreign key can arrive as a string or a number depending on the path
|
|
72
|
+
* that loaded it, so values are rendered as text and null is kept distinct from
|
|
73
|
+
* every string, including the empty one.
|
|
74
|
+
*/
|
|
75
|
+
export declare function untypedScopeValue(value: unknown): string | null;
|
|
76
|
+
/**
|
|
77
|
+
* @internal
|
|
78
|
+
*
|
|
79
|
+
* Postgres's own boolean input literals, lower-cased and trimmed, which a
|
|
80
|
+
* pending value may hold while the hydrated value is a JavaScript boolean.
|
|
81
|
+
* Exported so the spec drives its cases from the sets themselves rather than
|
|
82
|
+
* from a hand-copied list that can silently drift out of step with them.
|
|
83
|
+
*/
|
|
84
|
+
export declare const BOOLEAN_TRUE_LITERALS: ReadonlySet<string>;
|
|
85
|
+
export declare const BOOLEAN_FALSE_LITERALS: ReadonlySet<string>;
|
|
@@ -1,2 +1,15 @@
|
|
|
1
1
|
import Dream from '../../../../Dream.js';
|
|
2
|
+
import { SortableFieldConfig } from '../Sortable.js';
|
|
2
3
|
export default function clearCachedSortableValues(dream: Dream, positionField: string): void;
|
|
4
|
+
/**
|
|
5
|
+
* @internal
|
|
6
|
+
*
|
|
7
|
+
* Discards both halves of what a save's sortable phase stashes on the
|
|
8
|
+
* instance: the cached values the position work reads, and the snapshot of the
|
|
9
|
+
* row it would have computed its shift against. The position work clears them
|
|
10
|
+
* itself as it consumes them, so this is for the paths where it does not run —
|
|
11
|
+
* the row was already gone when the snapshot read found it, or the save's
|
|
12
|
+
* transaction rolled back — leaving a later save of the same instance to read
|
|
13
|
+
* the row for itself rather than inherit this save's reading of it.
|
|
14
|
+
*/
|
|
15
|
+
export declare function clearSortableSaveState(dream: Dream, configs: SortableFieldConfig[]): void;
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import Dream from '../../../../Dream.js';
|
|
2
2
|
import Query from '../../../../dream/Query.js';
|
|
3
|
-
|
|
3
|
+
import { SortableSnapshot } from './sortableSnapshot.js';
|
|
4
|
+
export default function decrementPositionForScopedRecordsGreaterThanPosition(position: number, { dream, positionField, query, scope, snapshot, }: {
|
|
4
5
|
dream: Dream;
|
|
5
6
|
positionField: string;
|
|
6
7
|
query: Query<Dream>;
|
|
7
8
|
scope: string | string[] | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* The destroyed row's real scope values, read before the delete. The scope
|
|
11
|
+
* being compacted is the one the row physically occupied, which is not
|
|
12
|
+
* necessarily the one the instance was loaded from.
|
|
13
|
+
*/
|
|
14
|
+
snapshot?: SortableSnapshot | undefined;
|
|
8
15
|
}): Promise<void>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import Dream from '../../../../Dream.js';
|
|
2
|
+
import Query from '../../../../dream/Query.js';
|
|
3
|
+
/**
|
|
4
|
+
* @internal
|
|
5
|
+
*
|
|
6
|
+
* Narrows a Dream `Query` to one sort scope, **reading the scope values off the
|
|
7
|
+
* in-memory instance**. That is only the scope the row physically occupies when
|
|
8
|
+
* the instance is known to agree with the row — inside a transaction that has
|
|
9
|
+
* just written those values, or on a record read under the scope lock.
|
|
10
|
+
*
|
|
11
|
+
* Where the row may have been moved by another writer, the caller wants
|
|
12
|
+
* `filterQueryToScopeValues` instead, which takes each column's value from the
|
|
13
|
+
* caller (the snapshot read under the lock) rather than from the instance.
|
|
14
|
+
*/
|
|
15
|
+
export default function filterQueryToInstanceScope(query: Query<Dream>, dream: Dream, scope?: string | string[]): Query<Dream, Readonly<{
|
|
16
|
+
joinedAssociations: Readonly<[]>;
|
|
17
|
+
rootTableName: string;
|
|
18
|
+
rootTableAlias: string;
|
|
19
|
+
allowPreload: true;
|
|
20
|
+
allowLeftJoinPreload: true;
|
|
21
|
+
allowLimit: true;
|
|
22
|
+
allowOffset: true;
|
|
23
|
+
allowPaginate: true;
|
|
24
|
+
}>>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { SelectQueryBuilder, UpdateQueryBuilder } from 'kysely';
|
|
2
|
+
import Dream from '../../../../Dream.js';
|
|
3
|
+
/**
|
|
4
|
+
* @internal
|
|
5
|
+
*
|
|
6
|
+
* Narrows a kysely query to one sort scope, taking each scope column's value
|
|
7
|
+
* **from the caller** rather than from the in-memory instance — the snapshot
|
|
8
|
+
* read under the scope lock on the paths that compact a scope a record is
|
|
9
|
+
* leaving, which is not necessarily the scope the instance was loaded from.
|
|
10
|
+
*
|
|
11
|
+
* This is also the one place the null branch lives: a null scope value has to
|
|
12
|
+
* be matched with `is null`, since `column = NULL` is never true and the filter
|
|
13
|
+
* would silently match nothing at all in a null-valued sort scope.
|
|
14
|
+
*
|
|
15
|
+
* The instance-reading sibling is `filterQueryToInstanceScope`.
|
|
16
|
+
*
|
|
17
|
+
* @param dream - the record whose class resolves the scope members to columns
|
|
18
|
+
* @param kyselyQuery - the query to narrow
|
|
19
|
+
* @param whereValueCB - the value to match each scope column against
|
|
20
|
+
* @param scope - the sortable field's scope
|
|
21
|
+
*/
|
|
22
|
+
export default function filterQueryToScopeValues<QB extends UpdateQueryBuilder<any, any, any, any> | SelectQueryBuilder<any, any, any>>(dream: Dream, kyselyQuery: QB, whereValueCB: (column: string) => any, scope?: string | string[]): QB;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import DreamTransaction from '../../../../dream/DreamTransaction.js';
|
|
2
|
+
/**
|
|
3
|
+
* @internal
|
|
4
|
+
*
|
|
5
|
+
* The sortable scope-lock keys this transaction already holds.
|
|
6
|
+
*
|
|
7
|
+
* Advisory transaction locks are released only when the transaction ends, so a
|
|
8
|
+
* key taken once is held for the rest of it: every later acquisition of that
|
|
9
|
+
* key is a statement whose entire effect is to observe that the transaction
|
|
10
|
+
* already has what it is asking for. Tracking them turns those statements into
|
|
11
|
+
* nothing at all — the per-field acquisition inside the position write, and
|
|
12
|
+
* every per-record acquisition inside a locked batch whose preflight already
|
|
13
|
+
* took the batch's whole key set.
|
|
14
|
+
*
|
|
15
|
+
* Being the transaction's whole ledger is also what lets the locked-batch
|
|
16
|
+
* preflight count its bound cumulatively rather than per batch: how many scope
|
|
17
|
+
* locks one more batch would leave the transaction holding is this set's size
|
|
18
|
+
* plus whatever of the batch's keys is not already in it.
|
|
19
|
+
*
|
|
20
|
+
* Keyed by the transaction object rather than by the connection, which is what
|
|
21
|
+
* makes it correct rather than merely fast: a nested transaction gets its own
|
|
22
|
+
* `DreamTransaction` and therefore its own set, so keys taken inside a
|
|
23
|
+
* subtransaction that rolls back — releasing its locks with it — are never
|
|
24
|
+
* remembered as held by the transaction that outlives it. The entry is
|
|
25
|
+
* discarded with the transaction object.
|
|
26
|
+
*/
|
|
27
|
+
export default function heldSortableScopeLockKeys(txn: DreamTransaction<any>): Set<bigint>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import Dream from '../../../../Dream.js';
|
|
2
|
+
import DreamTransaction from '../../../../dream/DreamTransaction.js';
|
|
3
|
+
/**
|
|
4
|
+
* @internal
|
|
5
|
+
*
|
|
6
|
+
* The compaction of a sortable destroy: closes the vacancy each sortable
|
|
7
|
+
* field's sort scope is left with, computed from the snapshot the preparation
|
|
8
|
+
* phase (`prepareSortableFieldsForDestroy`) read before the delete. Called
|
|
9
|
+
* directly from `destroyDream` after the delete and before any user
|
|
10
|
+
* `afterDestroy` hook — never registered as a hook among them — so every
|
|
11
|
+
* after-destroy hook observes the compacted scope regardless of where it was
|
|
12
|
+
* declared, exactly as every after-save hook observes the computed position.
|
|
13
|
+
*/
|
|
14
|
+
export default function performSortableDestroyWork(dream: Dream, txn: DreamTransaction<any>): Promise<void>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import Dream from '../../../../Dream.js';
|
|
2
|
+
import DreamTransaction from '../../../../dream/DreamTransaction.js';
|
|
3
|
+
import { SortableFieldConfig } from '../Sortable.js';
|
|
4
|
+
/**
|
|
5
|
+
* @internal
|
|
6
|
+
*
|
|
7
|
+
* The position work of a sortable save: computes each sortable field's real
|
|
8
|
+
* position under the scope lock and writes it. Called directly from
|
|
9
|
+
* `saveDream` after the driver write — never registered as an after-hook — so
|
|
10
|
+
* no user hook code can interleave with it while the scope lock is held, and
|
|
11
|
+
* on a self-opened save it is the last work before COMMIT releases the lock.
|
|
12
|
+
*/
|
|
13
|
+
export default function performSortablePositionWork(dream: Dream, txn: DreamTransaction<any>, sortableFieldConfigs: SortableFieldConfig[], { alreadyPersisted }: {
|
|
14
|
+
alreadyPersisted: boolean;
|
|
15
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import Dream from '../../../../Dream.js';
|
|
2
|
+
import DreamTransaction from '../../../../dream/DreamTransaction.js';
|
|
3
|
+
/**
|
|
4
|
+
* @internal
|
|
5
|
+
*
|
|
6
|
+
* The sortable preparation phase of a destroy. Called from `destroyDream` after
|
|
7
|
+
* every user `beforeDestroy` hook has run and before the delete — a phase, not
|
|
8
|
+
* a hook among them — so the operation's whole advisory key set, across every
|
|
9
|
+
* sortable field, is taken in one sorted stabilized acquisition, with one
|
|
10
|
+
* snapshot SELECT covering all fields. Per-field acquisition would take
|
|
11
|
+
* multi-field keys in declaration order, which deadlocks against the paths
|
|
12
|
+
* that acquire sorted.
|
|
13
|
+
*
|
|
14
|
+
* Pre-delete is the only seat available for the snapshot read: on a hard
|
|
15
|
+
* destroy the row is gone afterwards, and a soft destroy nulls the position
|
|
16
|
+
* columns in the same UPDATE as `deletedAt`. The lock is taken around the read
|
|
17
|
+
* so the snapshot cannot be invalidated between the read and the compaction;
|
|
18
|
+
* it is transaction-scoped, and `destroyDream` opens a transaction that spans
|
|
19
|
+
* the delete and the after-destroy hooks, so one acquisition covers both.
|
|
20
|
+
*
|
|
21
|
+
* The per-field compactions (`performSortableDestroyWork`) consume the cached
|
|
22
|
+
* snapshots.
|
|
23
|
+
*
|
|
24
|
+
* @returns false when the snapshot read found no row — this destroy has nothing
|
|
25
|
+
* to vacate, and compacting from the instance's remembered position would
|
|
26
|
+
* shift a scope some other writer has already closed. The delete's own
|
|
27
|
+
* affected-row count is the stronger signal (a writer outside the sortable
|
|
28
|
+
* path can remove the row after this read), so the caller checks both.
|
|
29
|
+
*/
|
|
30
|
+
export default function prepareSortableFieldsForDestroy(dream: Dream, txn: DreamTransaction<any>): Promise<boolean>;
|
|
31
|
+
/**
|
|
32
|
+
* @internal
|
|
33
|
+
*
|
|
34
|
+
* Discards what the preparation phase stashed on the instance. The per-field
|
|
35
|
+
* compaction clears its own snapshot as it consumes it, so this
|
|
36
|
+
* exists for the path where that compaction never runs: a destroy that removed
|
|
37
|
+
* nothing leaves the snapshot behind, and a later save or destroy of the same
|
|
38
|
+
* instance would compute its shift from a reading of a row this operation
|
|
39
|
+
* already found gone.
|
|
40
|
+
*/
|
|
41
|
+
export declare function clearSortableFieldsForDestroy(dream: Dream): void;
|
|
@@ -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
|
|
6
|
-
import {
|
|
7
|
-
|
|
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:
|
|
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[];
|