@rvoh/dream 2.30.1 → 2.31.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 (216) hide show
  1. package/dist/cjs/src/decorators/Decorators.js +81 -6
  2. package/dist/cjs/src/decorators/field/sortable/Sortable.js +4 -31
  3. package/dist/cjs/src/dream/Query.js +37 -13
  4. package/dist/cjs/src/dream/QueryDriver/Kysely.js +4 -1
  5. package/dist/cjs/src/dream/internal/saveDream.js +9 -0
  6. package/dist/cjs/src/errors/CannotSaveMissingDream.js +24 -0
  7. package/dist/cjs/src/errors/SortableBatchRequiresTooManyScopeLocks.js +7 -0
  8. package/dist/cjs/src/errors/associations/MissingRequiredBelongsToAssociation.js +8 -3
  9. package/dist/cjs/src/package-exports/errors.js +10 -1
  10. package/dist/esm/src/decorators/Decorators.js +81 -6
  11. package/dist/esm/src/decorators/field/sortable/Sortable.js +4 -31
  12. package/dist/esm/src/dream/Query.js +37 -13
  13. package/dist/esm/src/dream/QueryDriver/Kysely.js +4 -1
  14. package/dist/esm/src/dream/internal/saveDream.js +9 -0
  15. package/dist/esm/src/errors/CannotSaveMissingDream.js +24 -0
  16. package/dist/esm/src/errors/SortableBatchRequiresTooManyScopeLocks.js +7 -0
  17. package/dist/esm/src/errors/associations/MissingRequiredBelongsToAssociation.js +8 -3
  18. package/dist/esm/src/package-exports/errors.js +10 -1
  19. package/dist/types/src/decorators/Decorators.d.ts +81 -6
  20. package/dist/types/src/decorators/field/sortable/Sortable.d.ts +4 -31
  21. package/dist/types/src/dream/Query.d.ts +37 -13
  22. package/dist/types/src/errors/CannotSaveMissingDream.d.ts +23 -0
  23. package/dist/types/src/errors/SortableBatchRequiresTooManyScopeLocks.d.ts +7 -0
  24. package/dist/types/src/errors/associations/MissingRequiredBelongsToAssociation.d.ts +1 -0
  25. package/dist/types/src/package-exports/errors.d.ts +1 -1
  26. package/docs/assets/hierarchy.js +1 -1
  27. package/docs/assets/navigation.js +1 -1
  28. package/docs/assets/search.js +1 -1
  29. package/docs/classes/db.DreamMigrationHelpers.html +11 -11
  30. package/docs/classes/db.KyselyQueryDriver.html +43 -43
  31. package/docs/classes/db.PostgresQueryDriver.html +44 -44
  32. package/docs/classes/db.QueryDriverBase.html +42 -42
  33. package/docs/classes/errors.CannotSaveMissingDream.html +47 -0
  34. package/docs/classes/errors.CheckConstraintViolation.html +3 -3
  35. package/docs/classes/errors.ColumnOverflow.html +3 -3
  36. package/docs/classes/errors.CreateOrFindByFailedToCreateAndFind.html +3 -3
  37. package/docs/classes/errors.DataIncompatibleWithDatabaseField.html +3 -3
  38. package/docs/classes/errors.DataTypeColumnTypeMismatch.html +3 -3
  39. package/docs/classes/errors.DecryptionError.html +2 -2
  40. package/docs/classes/errors.DecryptionParseError.html +2 -2
  41. package/docs/classes/errors.DecryptionRotationError.html +3 -3
  42. package/docs/classes/errors.GlobalNameNotSet.html +3 -3
  43. package/docs/classes/errors.InvalidCalendarDate.html +2 -2
  44. package/docs/classes/errors.InvalidClockTime.html +2 -2
  45. package/docs/classes/errors.InvalidClockTimeTz.html +2 -2
  46. package/docs/classes/errors.InvalidDateTime.html +2 -2
  47. package/docs/classes/errors.MissingSerializersDefinition.html +3 -3
  48. package/docs/classes/errors.NonLoadedAssociation.html +3 -3
  49. package/docs/classes/errors.NotNullViolation.html +3 -3
  50. package/docs/classes/errors.RecordNotFound.html +3 -3
  51. package/docs/classes/errors.SortableScopeDidNotStabilize.html +3 -3
  52. package/docs/classes/errors.SortableScopeLockWaitTimedOut.html +3 -3
  53. package/docs/classes/errors.ValidationError.html +3 -3
  54. package/docs/classes/index.CalendarDate.html +33 -33
  55. package/docs/classes/index.ClockTime.html +32 -32
  56. package/docs/classes/index.ClockTimeTz.html +35 -35
  57. package/docs/classes/index.DateTime.html +86 -86
  58. package/docs/classes/index.Decorators.html +63 -22
  59. package/docs/classes/index.Dream.html +124 -124
  60. package/docs/classes/index.DreamApp.html +10 -10
  61. package/docs/classes/index.DreamTransaction.html +2 -2
  62. package/docs/classes/index.Env.html +2 -2
  63. package/docs/classes/index.Query.html +88 -67
  64. package/docs/classes/system.CliFileWriter.html +4 -4
  65. package/docs/classes/system.DreamBin.html +2 -2
  66. package/docs/classes/system.DreamCLI.html +7 -7
  67. package/docs/classes/system.DreamImporter.html +2 -2
  68. package/docs/classes/system.DreamLogos.html +2 -2
  69. package/docs/classes/system.DreamSerializerBuilder.html +11 -11
  70. package/docs/classes/system.ObjectSerializerBuilder.html +8 -8
  71. package/docs/classes/system.PathHelpers.html +3 -3
  72. package/docs/classes/utils.Encrypt.html +3 -3
  73. package/docs/classes/utils.Range.html +2 -2
  74. package/docs/functions/db.closeAllDbConnections.html +1 -1
  75. package/docs/functions/db.dreamDbConnections.html +1 -1
  76. package/docs/functions/db.untypedDb.html +1 -1
  77. package/docs/functions/db.validateColumn.html +1 -1
  78. package/docs/functions/db.validateTable.html +1 -1
  79. package/docs/functions/errors.pgErrorType.html +1 -1
  80. package/docs/functions/index.DreamSerializer.html +1 -1
  81. package/docs/functions/index.ObjectSerializer.html +1 -1
  82. package/docs/functions/index.ReplicaSafe.html +1 -1
  83. package/docs/functions/index.STI.html +1 -1
  84. package/docs/functions/index.SoftDelete.html +1 -1
  85. package/docs/functions/utils.camelize.html +1 -1
  86. package/docs/functions/utils.capitalize.html +1 -1
  87. package/docs/functions/utils.cloneDeepSafe.html +1 -1
  88. package/docs/functions/utils.compact.html +1 -1
  89. package/docs/functions/utils.groupBy.html +1 -1
  90. package/docs/functions/utils.hyphenize.html +1 -1
  91. package/docs/functions/utils.intersection.html +1 -1
  92. package/docs/functions/utils.isEmpty.html +1 -1
  93. package/docs/functions/utils.normalizeUnicode.html +1 -1
  94. package/docs/functions/utils.pascalize.html +1 -1
  95. package/docs/functions/utils.percent.html +1 -1
  96. package/docs/functions/utils.range.html +1 -1
  97. package/docs/functions/utils.round.html +1 -1
  98. package/docs/functions/utils.sanitizeString.html +1 -1
  99. package/docs/functions/utils.snakeify.html +1 -1
  100. package/docs/functions/utils.sort.html +1 -1
  101. package/docs/functions/utils.sortBy.html +1 -1
  102. package/docs/functions/utils.sortObjectByKey.html +1 -1
  103. package/docs/functions/utils.sortObjectByValue.html +1 -1
  104. package/docs/functions/utils.uncapitalize.html +1 -1
  105. package/docs/functions/utils.uniq.html +1 -1
  106. package/docs/hierarchy.html +1 -1
  107. package/docs/interfaces/openapi.OpenapiDescription.html +2 -2
  108. package/docs/interfaces/openapi.OpenapiSchemaProperties.html +1 -1
  109. package/docs/interfaces/openapi.OpenapiSchemaPropertiesShorthand.html +1 -1
  110. package/docs/interfaces/openapi.OpenapiTypeFieldObject.html +1 -1
  111. package/docs/interfaces/types.BelongsToStatement.html +2 -2
  112. package/docs/interfaces/types.DecoratorContext.html +2 -2
  113. package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
  114. package/docs/interfaces/types.DreamAppOpts.html +2 -2
  115. package/docs/interfaces/types.DreamDbConfig.html +5 -5
  116. package/docs/interfaces/types.DurationObject.html +2 -2
  117. package/docs/interfaces/types.EncryptOptions.html +2 -2
  118. package/docs/interfaces/types.InternalAnyTypedSerializerRendersMany.html +2 -2
  119. package/docs/interfaces/types.InternalAnyTypedSerializerRendersOne.html +2 -2
  120. package/docs/interfaces/types.SerializerRendererOpts.html +2 -2
  121. package/docs/modules/errors.html +1 -1
  122. package/docs/types/openapi.CommonOpenapiSchemaObjectFields.html +1 -1
  123. package/docs/types/openapi.OpenapiAllTypes.html +1 -1
  124. package/docs/types/openapi.OpenapiFormats.html +1 -1
  125. package/docs/types/openapi.OpenapiNumberFormats.html +1 -1
  126. package/docs/types/openapi.OpenapiPrimitiveBaseTypes.html +1 -1
  127. package/docs/types/openapi.OpenapiPrimitiveTypes.html +1 -1
  128. package/docs/types/openapi.OpenapiSchemaArray.html +1 -1
  129. package/docs/types/openapi.OpenapiSchemaArrayShorthand.html +1 -1
  130. package/docs/types/openapi.OpenapiSchemaBase.html +1 -1
  131. package/docs/types/openapi.OpenapiSchemaBody.html +1 -1
  132. package/docs/types/openapi.OpenapiSchemaBodyShorthand.html +1 -1
  133. package/docs/types/openapi.OpenapiSchemaCommonFields.html +1 -1
  134. package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +2 -2
  135. package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +2 -2
  136. package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +2 -2
  137. package/docs/types/openapi.OpenapiSchemaExpressionRef.html +2 -2
  138. package/docs/types/openapi.OpenapiSchemaExpressionRefSchemaShorthand.html +2 -2
  139. package/docs/types/openapi.OpenapiSchemaInteger.html +1 -1
  140. package/docs/types/openapi.OpenapiSchemaNull.html +2 -2
  141. package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
  142. package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
  143. package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +1 -1
  144. package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +1 -1
  145. package/docs/types/openapi.OpenapiSchemaObjectAnyOf.html +1 -1
  146. package/docs/types/openapi.OpenapiSchemaObjectAnyOfShorthand.html +1 -1
  147. package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
  148. package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.html +1 -1
  149. package/docs/types/openapi.OpenapiSchemaObjectOneOf.html +1 -1
  150. package/docs/types/openapi.OpenapiSchemaObjectOneOfShorthand.html +1 -1
  151. package/docs/types/openapi.OpenapiSchemaObjectShorthand.html +1 -1
  152. package/docs/types/openapi.OpenapiSchemaPrimitiveGeneric.html +1 -1
  153. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAllOf.html +2 -2
  154. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +2 -2
  155. package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +2 -2
  156. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializableRef.html +2 -2
  157. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializerRef.html +2 -2
  158. package/docs/types/openapi.OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
  159. package/docs/types/openapi.OpenapiSchemaString.html +1 -1
  160. package/docs/types/openapi.OpenapiShorthandAllTypes.html +1 -1
  161. package/docs/types/openapi.OpenapiShorthandPrimitiveBaseTypes.html +1 -1
  162. package/docs/types/openapi.OpenapiShorthandPrimitiveTypes.html +1 -1
  163. package/docs/types/openapi.OpenapiTypeField.html +1 -1
  164. package/docs/types/system.DreamAppAllowedPackageManagersEnum.html +1 -1
  165. package/docs/types/types.CalendarDateDurationUnit.html +1 -1
  166. package/docs/types/types.CalendarDateObject.html +1 -1
  167. package/docs/types/types.Camelized.html +1 -1
  168. package/docs/types/types.ClockTimeObject.html +1 -1
  169. package/docs/types/types.DbConnectionType.html +1 -1
  170. package/docs/types/types.DbTypes.html +1 -1
  171. package/docs/types/types.DreamAssociationMetadata.html +1 -1
  172. package/docs/types/types.DreamAttributes.html +1 -1
  173. package/docs/types/types.DreamClassAssociationAndStatement.html +1 -1
  174. package/docs/types/types.DreamClassColumn.html +1 -1
  175. package/docs/types/types.DreamColumn.html +1 -1
  176. package/docs/types/types.DreamColumnNames.html +1 -1
  177. package/docs/types/types.DreamLogLevel.html +1 -1
  178. package/docs/types/types.DreamLogger.html +2 -2
  179. package/docs/types/types.DreamModelSerializerType.html +1 -1
  180. package/docs/types/types.DreamOrViewModelClassSerializerKey.html +1 -1
  181. package/docs/types/types.DreamOrViewModelSerializerKey.html +1 -1
  182. package/docs/types/types.DreamParamSafeAttributes.html +1 -1
  183. package/docs/types/types.DreamParamSafeColumnNames.html +1 -1
  184. package/docs/types/types.DreamSerializable.html +1 -1
  185. package/docs/types/types.DreamSerializableArray.html +1 -1
  186. package/docs/types/types.DreamSerializerKey.html +1 -1
  187. package/docs/types/types.DreamSerializers.html +1 -1
  188. package/docs/types/types.DreamVirtualColumns.html +1 -1
  189. package/docs/types/types.DurationUnit.html +1 -1
  190. package/docs/types/types.EncryptAlgorithm.html +1 -1
  191. package/docs/types/types.HasManyStatement.html +1 -1
  192. package/docs/types/types.HasOneStatement.html +1 -1
  193. package/docs/types/types.Hyphenized.html +1 -1
  194. package/docs/types/types.Pascalized.html +1 -1
  195. package/docs/types/types.PrimaryKeyType.html +1 -1
  196. package/docs/types/types.RoundingPrecision.html +1 -1
  197. package/docs/types/types.SerializerCasing.html +1 -1
  198. package/docs/types/types.SimpleObjectSerializerType.html +1 -1
  199. package/docs/types/types.Snakeified.html +1 -1
  200. package/docs/types/types.StrictInterface.html +1 -1
  201. package/docs/types/types.UpdateableAssociationProperties.html +1 -1
  202. package/docs/types/types.UpdateableProperties.html +1 -1
  203. package/docs/types/types.ValidationType.html +1 -1
  204. package/docs/types/types.ViewModel.html +2 -2
  205. package/docs/types/types.ViewModelClass.html +1 -1
  206. package/docs/types/types.WeekdayName.html +1 -1
  207. package/docs/types/types.WhereStatementForDream.html +1 -1
  208. package/docs/types/types.WhereStatementForDreamClass.html +1 -1
  209. package/docs/variables/index.DreamConst.html +1 -1
  210. package/docs/variables/index.ops.html +1 -1
  211. package/docs/variables/openapi.openapiPrimitiveTypes.html +1 -1
  212. package/docs/variables/openapi.openapiShorthandPrimitiveTypes.html +1 -1
  213. package/docs/variables/system.DreamAppAllowedPackageManagersEnumValues.html +1 -1
  214. package/docs/variables/system.primaryKeyTypes.html +1 -1
  215. package/package.json +1 -1
  216. package/docs/classes/errors.SortableBatchRequiresTooManyScopeLocks.html +0 -61
@@ -7,7 +7,7 @@
7
7
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
8
8
  // @ts-ignore
9
9
  import pg from 'pg';
10
- import { PostgresDialect, sql, } from 'kysely';
10
+ import { NoResultError, PostgresDialect, sql, } from 'kysely';
11
11
  import pluralize from 'pluralize-esm';
12
12
  import { CliFileWriter } from '../../cli/CliFileWriter.js';
13
13
  import DreamCLI from '../../cli/index.js';
@@ -30,6 +30,7 @@ import MissingThroughAssociationSource from '../../errors/associations/MissingTh
30
30
  import CannotNegateSimilarityClause from '../../errors/CannotNegateSimilarityClause.js';
31
31
  import CannotNamespaceAssociationFilterToAnotherTable from '../../errors/CannotNamespaceAssociationFilterToAnotherTable.js';
32
32
  import CannotPassUndefinedAsAValueToAWhereClause from '../../errors/CannotPassUndefinedAsAValueToAWhereClause.js';
33
+ import CannotSaveMissingDream from '../../errors/CannotSaveMissingDream.js';
33
34
  import ArrayEqualityRequiresArrayColumn from '../../errors/ops/ArrayEqualityRequiresArrayColumn.js';
34
35
  import RowLockIncompatibleWithDistinct from '../../errors/RowLockIncompatibleWithDistinct.js';
35
36
  import RowLockIncompatibleWithJoinLoad from '../../errors/RowLockIncompatibleWithJoinLoad.js';
@@ -806,6 +807,8 @@ export default class KyselyQueryDriver extends QueryDriverBase {
806
807
  }
807
808
  }
808
809
  catch (error) {
810
+ if (dream.isPersisted && error instanceof NoResultError)
811
+ throw new CannotSaveMissingDream(dream);
809
812
  switch (pgErrorType(error)) {
810
813
  case COLUMN_OVERFLOW:
811
814
  throw new ColumnOverflow(error instanceof Error ? error : new Error('database column overflow'));
@@ -3,6 +3,7 @@ import { clearSortableSaveState } from '../../decorators/field/sortable/helpers/
3
3
  import performSortablePositionWork from '../../decorators/field/sortable/helpers/performSortablePositionWork.js';
4
4
  import prepareSortableFieldsForSave from '../../decorators/field/sortable/helpers/prepareSortableFieldsForSave.js';
5
5
  import { cacheSortableSnapshots } from '../../decorators/field/sortable/helpers/sortableSnapshot.js';
6
+ import CannotSaveMissingDream from '../../errors/CannotSaveMissingDream.js';
6
7
  import ValidationError from '../../errors/ValidationError.js';
7
8
  import sqlAttributes from '../../helpers/sqlAttributes.js';
8
9
  import { DateTime } from '../../utils/datetime/DateTime.js';
@@ -114,6 +115,14 @@ async function writeDream(dream, txn, { alreadyPersisted, sortableFieldConfigs,
114
115
  dream.createdAt = now;
115
116
  if (!dream.dirtyAttributes().updatedAt && dream.columns().has('updatedAt'))
116
117
  dream.updatedAt = now;
118
+ // The scope-lock read above positively found the row gone, so there is
119
+ // nothing to update. Raised here rather than after the write because the
120
+ // write below would not reach the database at all on a position-only save,
121
+ // whose attributes Sortable has already cleared; a save that does carry data
122
+ // raises the equivalent diagnostic from the driver, which sees the update
123
+ // match no row.
124
+ if (alreadyPersisted && !rowFoundBeforeWrite)
125
+ throw new CannotSaveMissingDream(dream);
117
126
  const hasUnsavedData = !!Object.keys(sqlAttributes(dream)).length;
118
127
  // BeforeSave/Update actions may clear all the data that we intended to save, leaving us with
119
128
  // an invalid update command. The Sortable decorator is an example of this.
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Raised when saving a persisted instance whose database row no longer exists —
3
+ * the row was deleted between the moment the instance was loaded and the moment
4
+ * the save reached the database.
5
+ *
6
+ * This is the write-side counterpart of `RecordNotFound`, and applications catch
7
+ * it for the same reasons: a request updating a record another request has since
8
+ * deleted should answer 404 or 409 rather than 500, and a background job holding
9
+ * a stale instance should usually drop the work rather than fail loudly. Nothing
10
+ * is written when it is raised.
11
+ *
12
+ * It does not mean the save was rejected — a save that fails validation raises
13
+ * `ValidationError`, and a constraint violation raises its own error. It means
14
+ * the row that the instance stands for is gone.
15
+ *
16
+ * Dream raises it from the query driver, so a custom driver overriding
17
+ * `saveDream` translates its own adapter's empty-result error the same way, as
18
+ * the test-app MySQL driver does.
19
+ */
20
+ export default class CannotSaveMissingDream extends Error {
21
+ constructor(dream) {
22
+ super(`Cannot save ${dream.sanitizedConstructorName} with primary key ${String(dream.primaryKeyValue())} because its persisted database row no longer exists.`);
23
+ }
24
+ }
@@ -1,4 +1,6 @@
1
1
  /**
2
+ * @internal
3
+ *
2
4
  * Raised when a batch of a locked `Query#destroy` or `Query#update` on a
3
5
  * sortable model would carry its transaction past the number of advisory
4
6
  * sort-scope locks Dream is willing to hold at once.
@@ -27,6 +29,11 @@
27
29
  * — a long multi-batch run inside a transaction you opened — either let Dream
28
30
  * open a transaction per batch (do not pass a transaction of your own) or
29
31
  * commit between runs, which is what releases the locks as it goes.
32
+ *
33
+ * Not exported from `@rvoh/dream/errors`. Exceeding the bound is a sizing
34
+ * mistake in the calling code, not a runtime condition an application can
35
+ * recover from, so there is nothing for a consumer to catch: the fix is a
36
+ * smaller batch or a narrower transaction, never a retry.
30
37
  */
31
38
  export default class SortableBatchRequiresTooManyScopeLocks extends Error {
32
39
  dreamClassName;
@@ -48,10 +48,15 @@ export default class MissingRequiredBelongsToAssociation extends Error {
48
48
  Attempting to access required BelongsTo association \`${this.associationName}\` on an instance of \`${this.dreamClass.sanitizedName}\`,
49
49
  but the loaded association is null.
50
50
 
51
- ${this.foreignKeyMessage}
52
-
53
- If the associated record was deleted, check whether the inverse HasOne/HasMany association should specify dependent: 'destroy'.
51
+ ${this.foreignKeyMessage}${this.populatedForeignKeyDiagnosticMessage}
54
52
  `;
53
+ }
54
+ get populatedForeignKeyDiagnosticMessage() {
55
+ if (this.foreignKeyValue === null || this.foreignKeyValue === undefined)
56
+ return '';
57
+ return `
58
+
59
+ The associated record may have been deleted (including soft-deleted), or it may not match this association’s target—for example, its STI type may differ from the targeted STI child. If the record was deleted unexpectedly, check whether the inverse HasOne/HasMany association should specify \`dependent: 'destroy'\`.`;
55
60
  }
56
61
  get foreignKeyMessage() {
57
62
  if (this.foreignKeyValue === null || this.foreignKeyValue === undefined) {
@@ -1,18 +1,27 @@
1
1
  export { pgErrorType } from '../db/errors.js';
2
2
  export { default as NonLoadedAssociation } from '../errors/associations/NonLoadedAssociation.js';
3
+ export { default as CannotSaveMissingDream } from '../errors/CannotSaveMissingDream.js';
3
4
  export { default as CreateOrFindByFailedToCreateAndFind } from '../errors/CreateOrFindByFailedToCreateAndFind.js';
4
5
  export { default as CheckConstraintViolation } from '../errors/db/CheckConstraintViolation.js';
5
6
  export { default as ColumnOverflow } from '../errors/db/ColumnOverflow.js';
6
7
  export { default as DataIncompatibleWithDatabaseField } from '../errors/db/DataIncompatibleWithDatabaseField.js';
7
8
  export { default as DataTypeColumnTypeMismatch } from '../errors/db/DataTypeColumnTypeMismatch.js';
8
9
  export { default as NotNullViolation } from '../errors/db/NotNullViolation.js';
10
+ // Raised by the framework packages layered on Dream, not only by Dream: psychic
11
+ // (`src/controller/index.ts`) raises it for a controller's global name, and
12
+ // psychic-workers (`src/background/BaseBackgroundedService.ts`,
13
+ // `src/background/BaseScheduledService.ts`) for service global names. Both reach it
14
+ // through this entry point, so unexporting it breaks the build of every application
15
+ // that installs either package.
9
16
  export { default as GlobalNameNotSet } from '../errors/dream-app/GlobalNameNotSet.js';
10
17
  export { default as DecryptionError } from '../errors/encrypt/DecryptionError.js';
11
18
  export { default as DecryptionParseError } from '../errors/encrypt/DecryptionParseError.js';
12
19
  export { default as DecryptionRotationError } from '../errors/encrypt/DecryptionRotationError.js';
13
20
  export { default as RecordNotFound } from '../errors/RecordNotFound.js';
21
+ // Caught through this entry point by psychic
22
+ // (`test-app/src/app/controllers/SerializerTestsController.ts`), which distinguishes it
23
+ // to prove a controller can render a missing-serializers failure as a response.
14
24
  export { default as MissingSerializersDefinition } from '../errors/serializers/MissingSerializersDefinition.js';
15
- export { default as SortableBatchRequiresTooManyScopeLocks } from '../errors/SortableBatchRequiresTooManyScopeLocks.js';
16
25
  export { default as SortableScopeDidNotStabilize } from '../errors/SortableScopeDidNotStabilize.js';
17
26
  export { default as SortableScopeLockWaitTimedOut } from '../errors/SortableScopeLockWaitTimedOut.js';
18
27
  export { default as ValidationError } from '../errors/ValidationError.js';
@@ -63,18 +63,93 @@ export default class Decorators<TD extends typeof Dream, T extends Dream = Insta
63
63
  default?: boolean;
64
64
  }): any;
65
65
  /**
66
- * The Sortable decorator automatically adjusts the value of the columns
67
- * corresponding to the decorated field.
68
- *
69
- * NOTE: the Sortable decorator may not be used in STI child models (it may be used in the STI base class)
66
+ * Marks an integer column as a sortable position: Dream keeps the positions of
67
+ * every record in a sort scope contiguous, starting at 1, as records are
68
+ * created, moved, destroyed and undestroyed.
70
69
  *
71
70
  * ```ts
72
- * class Balloon {
71
+ * class Post extends ApplicationModel {
73
72
  * @deco.Sortable({ scope: 'user' })
74
- * public position: DreamColumn<Balloon, 'position'>
73
+ * public position: number
74
+ * }
75
+ *
76
+ * await post.update({ position: 2 }) // the records at 2 and above shift up
77
+ * ```
78
+ *
79
+ * A position past the end of the scope is clamped to the end, and a position
80
+ * below 1 — or none at all — lands the record at the end.
81
+ *
82
+ * **A save that changes the sort scope ignores a position given alongside it.**
83
+ * The record lands at the end of the scope it moves into, whatever position the
84
+ * same `update` supplied:
85
+ *
86
+ * ```ts
87
+ * await post.update({ user: otherUser, position: 1 })
88
+ * // post is now the last record in otherUser's scope, not the first
89
+ * ```
90
+ *
91
+ * Move it in two saves to place it: `await post.update({ user: otherUser })`,
92
+ * then `await post.update({ position: 1 })`.
93
+ *
94
+ * Sortable requires a query driver that supports advisory transaction locks —
95
+ * the `PostgresQueryDriver` does — since every position write serializes the
96
+ * writers of its sort scope on one. This concurrency guarantee first shipped
97
+ * in Dream 2.28.0 and begins only after every writer is running a lock-aware
98
+ * release. During the first rolling deployment, older processes take no
99
+ * advisory locks and can still race the upgraded processes.
100
+ *
101
+ * All participating Dream writers of one hot scope serialize. Ordinary saves
102
+ * and destroys with `skipHooks`, direct query writes, raw SQL, and older
103
+ * pre-lock Dream processes bypass Sortable maintenance and do not participate
104
+ * in its locking protocol. Undestroy still performs stabilized Sortable
105
+ * maintenance and acquires scope locks with `skipHooks: true`; locked query
106
+ * batches likewise acquire their scope locks during preflight, before any
107
+ * per-record callbacks, even when those callbacks skip hooks. A waiter that
108
+ * enters the protocol holds a pooled connection until the holder finishes or
109
+ * `sortableScopeLockTimeout` expires; sustained contention can therefore
110
+ * produce latency waves, timeouts, and pool starvation. Keep database work
111
+ * inside the lock window short, and avoid cross-region database latency for
112
+ * hot scopes.
113
+ *
114
+ * `SortableScopeDidNotStabilize` and `SortableScopeLockWaitTimedOut` are the
115
+ * expected Dream errors an application may choose to retry. Database deadlocks
116
+ * remain native adapter errors (for example PostgreSQL code `40P01` or MySQL
117
+ * errno `1213`) so their driver fields and stacks remain intact. A database
118
+ * deadlock aborts the whole transaction: retry by starting the transaction
119
+ * again from the beginning, never by continuing it or retrying only the failed
120
+ * statement. Recurrent deadlocks call for shorter transactions or a consistent
121
+ * multi-resource acquisition order; they do not by themselves prove Sortable
122
+ * caused the cycle.
123
+ *
124
+ * ```ts
125
+ * import {
126
+ * SortableScopeDidNotStabilize,
127
+ * SortableScopeLockWaitTimedOut,
128
+ * } from '@rvoh/dream/errors'
129
+ *
130
+ * const runTransaction = async () =>
131
+ * await ApplicationModel.transaction(async txn => {
132
+ * // bind every operation in the attempt to txn
133
+ * })
134
+ *
135
+ * try {
136
+ * await runTransaction()
137
+ * } catch (error) {
138
+ * const adapterDeadlock =
139
+ * (error as { code?: string }).code === '40P01' ||
140
+ * (error as { errno?: number }).errno === 1213
141
+ * const retryable =
142
+ * error instanceof SortableScopeDidNotStabilize ||
143
+ * error instanceof SortableScopeLockWaitTimedOut ||
144
+ * adapterDeadlock
145
+ * if (!retryable) throw error
146
+ *
147
+ * await runTransaction() // one whole-transaction retry
75
148
  * }
76
149
  * ```
77
150
  *
151
+ * NOTE: the Sortable decorator may not be used in STI child models (it may be used in the STI base class)
152
+ *
78
153
  * @param opts - Configuration options for the sortable decorator
79
154
  * @param opts.scope - The column, association, or combination thereof which you would like to restrict the incrementing logic to. Can be a single column name, a single belongs-to association name, or an array of column/association names
80
155
  * @returns A Sortable decorator
@@ -1,35 +1,8 @@
1
1
  /**
2
- * Marks an integer column as a sortable position: Dream keeps the positions of
3
- * every record in a sort scope contiguous, starting at 1, as records are
4
- * created, moved, destroyed and undestroyed.
5
- *
6
- * ```ts
7
- * class Post extends ApplicationModel {
8
- * @deco.Sortable({ scope: 'user' })
9
- * public position: number
10
- * }
11
- *
12
- * await post.update({ position: 2 }) // the records at 2 and above shift up
13
- * ```
14
- *
15
- * A position past the end of the scope is clamped to the end, and a position
16
- * below 1 — or none at all — lands the record at the end.
17
- *
18
- * **A save that changes the sort scope ignores a position given alongside it.**
19
- * The record lands at the end of the scope it moves into, whatever position the
20
- * same `update` supplied:
21
- *
22
- * ```ts
23
- * await post.update({ user: otherUser, position: 1 })
24
- * // post is now the last record in otherUser's scope, not the first
25
- * ```
26
- *
27
- * Move it in two saves to place it: `await post.update({ user: otherUser })`,
28
- * then `await post.update({ position: 1 })`.
29
- *
30
- * Sortable requires a query driver that supports advisory transaction locks —
31
- * the `PostgresQueryDriver` does — since every position write serializes the
32
- * writers of its sort scope on one.
2
+ * The implementation behind `@deco.Sortable`. The decorator's semantics —
3
+ * position clamping, what a scope-changing save does with a position given
4
+ * alongside it, and the advisory-lock concurrency protocol — are documented on
5
+ * `Decorators#Sortable`, which is where a consumer reads them.
33
6
  */
34
7
  export default function Sortable(opts?: SortableOpts): any;
35
8
  interface SortableOpts {
@@ -1356,13 +1356,15 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
1356
1356
  * {@link Query.delete | delete} (a single statement, no hooks or cascade)
1357
1357
  * are the right tools for bulk removal.
1358
1358
  *
1359
+ * To bypass a default scope, chain `removeDefaultScope` onto the Query
1360
+ * before calling this method; it is Query state, not an option, and it
1361
+ * carries through to the cascade.
1362
+ *
1359
1363
  * @param options - Options for destroying the instance
1360
1364
  * @param options.skipHooks - If true, skips applying model hooks during the destroy operation. Defaults to false
1361
1365
  * @param options.cascade - If false, skips destroying associations marked `dependent: 'destroy'`. Defaults to true
1362
1366
  * @param options.lock - If true, each batch is re-selected with an exclusive row lock inside its own transaction before being destroyed, making the destroy a compare-and-set. Defaults to false
1363
1367
  * @param options.batchSize - The number of records to process per batch. Must be a positive integer. Defaults to 10 when `lock` is true, and to 1000 otherwise
1364
- * @param options.bypassAllDefaultScopes - If true, bypasses user-removable default scopes when cascade destroying; an STI child's reserved discriminator remains enforced, including when associations are loaded. Defaults to false
1365
- * @param options.defaultScopesToBypass - An array of default scope names to bypass when cascade destroying. Defaults to an empty array
1366
1368
  * @returns The number of records that were removed
1367
1369
  * @throws InvalidBatchSize if `batchSize` is not a positive integer
1368
1370
  * @throws BatchingIncompatibleWithLimitOrOffset if the query carries a `limit` or `offset`
@@ -1433,11 +1435,12 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
1433
1435
  * transaction ends, so every batch's scope keys are still held when the
1434
1436
  * next one preflights. That accumulation is bounded — the preflight counts
1435
1437
  * the keys the transaction already holds along with the ones the batch
1436
- * would add, and refuses with `SortableBatchRequiresTooManyScopeLocks`
1437
- * rather than letting a long run over a high-cardinality sort scope exhaust
1438
- * `max_locks_per_transaction` for the whole cluster — so a run long enough
1439
- * raises instead of completing. Letting Dream open a transaction per batch
1440
- * releases the keys as it goes and gives each batch the whole budget.
1438
+ * would add, and raises rather than letting a long run over a
1439
+ * high-cardinality sort scope exhaust `max_locks_per_transaction` for the
1440
+ * whole cluster — so a run long enough raises instead of completing. That
1441
+ * refusal is a sizing failure to fix, not a condition to catch: letting
1442
+ * Dream open a transaction per batch releases the keys as it goes and gives
1443
+ * each batch the whole budget, and a smaller `batchSize` takes fewer.
1441
1444
  * - the preflight covers **the claimed rows' own scopes only**. A
1442
1445
  * `dependent: 'destroy'` cascade under `destroy({ lock: true })` destroys
1443
1446
  * child records of other tables, and a sortable child takes its own table's
@@ -1454,8 +1457,18 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
1454
1457
  private lockedBatches;
1455
1458
  /**
1456
1459
  * Destroys all records matching the Query,
1457
- * ignoring the SoftDelete decorator and
1458
- * permanently removing records from the database.
1460
+ * permanently removing each matched record from
1461
+ * the database instead of soft deleting it.
1462
+ *
1463
+ * The SoftDelete decorator is bypassed when
1464
+ * deleting each matched record, not when
1465
+ * matching: the Query selects under its default
1466
+ * scopes, `dream:SoftDelete` among them, so on a
1467
+ * SoftDelete model it matches only records that
1468
+ * have not already been soft deleted. Reaching
1469
+ * soft deleted records takes
1470
+ * `removeDefaultScope('dream:SoftDelete')` on
1471
+ * the Query.
1459
1472
  *
1460
1473
  * Calls model hooks and applies cascade destroy
1461
1474
  * to associations with `dependent: 'destroy'`,
@@ -1470,17 +1483,26 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
1470
1483
  * // 12
1471
1484
  * ```
1472
1485
  *
1486
+ * ```ts
1487
+ * await User.removeDefaultScope('dream:SoftDelete')
1488
+ * .where({ email: ops.ilike('%burpcollaborator%') })
1489
+ * .reallyDestroy()
1490
+ * // 12
1491
+ * ```
1492
+ *
1473
1493
  * A `limit` or `offset` on the Query is incompatible with `reallyDestroy`,
1474
1494
  * exactly as it is with {@link Query.destroy}: a limit- or offset-carrying
1475
1495
  * Query throws at runtime.
1476
1496
  *
1497
+ * To bypass a default scope, chain `removeDefaultScope` onto the Query
1498
+ * before calling this method; it is Query state, not an option, and it
1499
+ * carries through to the cascade.
1500
+ *
1477
1501
  * @param options - Options for destroying the instance
1478
1502
  * @param options.skipHooks - If true, skips applying model hooks during the destroy operation. Defaults to false
1479
1503
  * @param options.cascade - If false, skips destroying associations marked `dependent: 'destroy'`. Defaults to true
1480
1504
  * @param options.lock - If true, each batch is re-selected with an exclusive row lock inside its own transaction before being destroyed, making the destroy a compare-and-set. See {@link Query.destroy} for the full semantics. Defaults to false
1481
1505
  * @param options.batchSize - The number of records to process per batch. Must be a positive integer. Defaults to 10 when `lock` is true, and to 1000 otherwise
1482
- * @param options.bypassAllDefaultScopes - If true, bypasses user-removable default scopes when cascade destroying; an STI child's reserved discriminator remains enforced, including when associations are loaded. Defaults to false
1483
- * @param options.defaultScopesToBypass - An array of default scope names to bypass when cascade destroying. Defaults to an empty array
1484
1506
  * @returns The number of records that were removed
1485
1507
  * @throws InvalidBatchSize if `batchSize` is not a positive integer
1486
1508
  * @throws BatchingIncompatibleWithLimitOrOffset if the query carries a `limit` or `offset`
@@ -1509,11 +1531,13 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
1509
1531
  * re-applied to every window — so a limit- or offset-carrying Query throws
1510
1532
  * at runtime.
1511
1533
  *
1534
+ * To bypass a default scope, chain `removeDefaultScope` onto the Query
1535
+ * before calling this method; it is Query state, not an option, and it
1536
+ * carries through to the cascade. The SoftDelete scope is always bypassed.
1537
+ *
1512
1538
  * @param options - Options for undestroying the instance
1513
1539
  * @param options.skipHooks - If true, skips applying model hooks during the undestroy operation. Defaults to false
1514
1540
  * @param options.cascade - If false, skips undestroying associations marked `dependent: 'destroy'`. Defaults to true
1515
- * @param options.bypassAllDefaultScopes - If true, bypasses user-removable default scopes when cascade undestroying; an STI child's reserved discriminator remains enforced, including when associations are loaded. Defaults to false
1516
- * @param options.defaultScopesToBypass - An array of default scope names to bypass when cascade undestroying (soft delete is always bypassed). Defaults to an empty array
1517
1541
  * @returns The number of records that were removed
1518
1542
  * @throws BatchingIncompatibleWithLimitOrOffset if the query carries a `limit` or `offset`
1519
1543
  */
@@ -0,0 +1,23 @@
1
+ import Dream from '../Dream.js';
2
+ /**
3
+ * Raised when saving a persisted instance whose database row no longer exists —
4
+ * the row was deleted between the moment the instance was loaded and the moment
5
+ * the save reached the database.
6
+ *
7
+ * This is the write-side counterpart of `RecordNotFound`, and applications catch
8
+ * it for the same reasons: a request updating a record another request has since
9
+ * deleted should answer 404 or 409 rather than 500, and a background job holding
10
+ * a stale instance should usually drop the work rather than fail loudly. Nothing
11
+ * is written when it is raised.
12
+ *
13
+ * It does not mean the save was rejected — a save that fails validation raises
14
+ * `ValidationError`, and a constraint violation raises its own error. It means
15
+ * the row that the instance stands for is gone.
16
+ *
17
+ * Dream raises it from the query driver, so a custom driver overriding
18
+ * `saveDream` translates its own adapter's empty-result error the same way, as
19
+ * the test-app MySQL driver does.
20
+ */
21
+ export default class CannotSaveMissingDream extends Error {
22
+ constructor(dream: Dream);
23
+ }
@@ -1,4 +1,6 @@
1
1
  /**
2
+ * @internal
3
+ *
2
4
  * Raised when a batch of a locked `Query#destroy` or `Query#update` on a
3
5
  * sortable model would carry its transaction past the number of advisory
4
6
  * sort-scope locks Dream is willing to hold at once.
@@ -27,6 +29,11 @@
27
29
  * — a long multi-batch run inside a transaction you opened — either let Dream
28
30
  * open a transaction per batch (do not pass a transaction of your own) or
29
31
  * commit between runs, which is what releases the locks as it goes.
32
+ *
33
+ * Not exported from `@rvoh/dream/errors`. Exceeding the bound is a sizing
34
+ * mistake in the calling code, not a runtime condition an application can
35
+ * recover from, so there is nothing for a consumer to catch: the fix is a
36
+ * smaller batch or a narrower transaction, never a retry.
30
37
  */
31
38
  export default class SortableBatchRequiresTooManyScopeLocks extends Error {
32
39
  dreamClassName: string;
@@ -44,6 +44,7 @@ export default class MissingRequiredBelongsToAssociation extends Error {
44
44
  polymorphic: boolean;
45
45
  });
46
46
  get message(): string;
47
+ private get populatedForeignKeyDiagnosticMessage();
47
48
  private get foreignKeyMessage();
48
49
  private formatValue;
49
50
  }
@@ -1,5 +1,6 @@
1
1
  export { pgErrorType } from '../db/errors.js';
2
2
  export { default as NonLoadedAssociation } from '../errors/associations/NonLoadedAssociation.js';
3
+ export { default as CannotSaveMissingDream } from '../errors/CannotSaveMissingDream.js';
3
4
  export { default as CreateOrFindByFailedToCreateAndFind } from '../errors/CreateOrFindByFailedToCreateAndFind.js';
4
5
  export { default as CheckConstraintViolation } from '../errors/db/CheckConstraintViolation.js';
5
6
  export { default as ColumnOverflow } from '../errors/db/ColumnOverflow.js';
@@ -12,7 +13,6 @@ export { default as DecryptionParseError } from '../errors/encrypt/DecryptionPar
12
13
  export { default as DecryptionRotationError } from '../errors/encrypt/DecryptionRotationError.js';
13
14
  export { default as RecordNotFound } from '../errors/RecordNotFound.js';
14
15
  export { default as MissingSerializersDefinition } from '../errors/serializers/MissingSerializersDefinition.js';
15
- export { default as SortableBatchRequiresTooManyScopeLocks } from '../errors/SortableBatchRequiresTooManyScopeLocks.js';
16
16
  export { default as SortableScopeDidNotStabilize } from '../errors/SortableScopeDidNotStabilize.js';
17
17
  export { default as SortableScopeLockWaitTimedOut } from '../errors/SortableScopeLockWaitTimedOut.js';
18
18
  export { default as ValidationError } from '../errors/ValidationError.js';
@@ -1 +1 @@
1
- window.hierarchyData = "eJyVkkFPwjAUx7/LOxekY3SsRyEmxoiaGD0QDmV7uIauJW2HWQjf3VQuFdGNU5vm/fv79fUdwBrjHfAlZaOMUEpXBCxuFBZeGu2AH4BSGhYtagQOLw3adm7lHu2tcAgEtlKXwGkyJdBYBRwKJZxDd1Ouh2fVw8rXCsipAjh4Vw5CfHA6IFBUUpUWNfBlko9XRwJJPo7oD61D1Ua3dvB/1V9jQMeUBYWwRg7PxvkPi66/xYVEh0egTrK477MKi+3MaOetkNq/SaNE+KL/0GitsW74V7SPwzSPHYxqav20R7tR5rMP+UegB4+Nsog3F17c68LUO+HlWuG79FU4WwuHdxJV2UOh846rJmKSURKaQihLGKEsTb8HhCXsTPu13eHp9WH3KF0tfFH19L0c7tO+NI08FsYvGqWuGZXzSBfzePwC4ktvDw=="
1
+ window.hierarchyData = "eJyVk11PwyAUhv/LuWZT2o50XLrFxBinJkYvll2w9sySUViAzjTL/rvB3eD8KLuCkPPyPMDhANYY74AvKcsKQildEbC4UVh5abQDfgBKaRi0aBE4PHdo+7mVe7Q3wiEQ2EpdA6dZSaCzCjhUSjiH7qpej8+qx41vFZBTBXDwrh6F+Oi0QKBqpKotauDLbJqvjgSyaR7R73uHqo92HeD/qL/EgOaUBYUwRg5Pxvl3iy7d4pfEgEegTsoyos4arLYzo523Qmr/Ko0S4Yn+Q6O1xrrxX9EEB3Ydn3xmVNfqxz3ajTIfKeRvgRReVkS8ufDiTlem3Qkv1wrfpG/C2lo4vJWo6gSFwT0u6ohJWZJwKYSyIieUMfrVIKzIz7Rf+h2eTh9mD9K1wldNou/v4ZTrY/FXXRi/6JS6pFXOI0PM4/ET1ulvBQ=="
@@ -1 +1 @@
1
- window.navigationData = "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"
1
+ window.navigationData = "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"