@rvoh/dream 2.28.2 → 2.28.3

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 (218) hide show
  1. package/dist/cjs/src/Dream.js +11 -9
  2. package/dist/cjs/src/dream/DreamClassTransactionBuilder.js +4 -2
  3. package/dist/cjs/src/dream/DreamInstanceTransactionBuilder.js +7 -7
  4. package/dist/cjs/src/dream/LoadBuilder.js +3 -2
  5. package/dist/cjs/src/dream/Query.js +23 -10
  6. package/dist/cjs/src/dream/QueryDriver/Base.js +12 -0
  7. package/dist/cjs/src/dream/QueryDriver/Kysely.js +11 -2
  8. package/dist/cjs/src/dream/internal/shouldBypassDefaultScope.js +3 -4
  9. package/dist/cjs/src/dream/internal/sqlResultToDreamInstance.js +4 -1
  10. package/dist/cjs/src/errors/NoUpdateOnAssociationQuery.js +1 -1
  11. package/dist/cjs/src/errors/sti/STIChildTypeMismatch.js +28 -0
  12. package/dist/esm/src/Dream.js +11 -9
  13. package/dist/esm/src/dream/DreamClassTransactionBuilder.js +4 -2
  14. package/dist/esm/src/dream/DreamInstanceTransactionBuilder.js +7 -7
  15. package/dist/esm/src/dream/LoadBuilder.js +3 -2
  16. package/dist/esm/src/dream/Query.js +23 -10
  17. package/dist/esm/src/dream/QueryDriver/Base.js +12 -0
  18. package/dist/esm/src/dream/QueryDriver/Kysely.js +11 -2
  19. package/dist/esm/src/dream/internal/shouldBypassDefaultScope.js +3 -4
  20. package/dist/esm/src/dream/internal/sqlResultToDreamInstance.js +4 -1
  21. package/dist/esm/src/errors/NoUpdateOnAssociationQuery.js +1 -1
  22. package/dist/esm/src/errors/sti/STIChildTypeMismatch.js +28 -0
  23. package/dist/types/src/Dream.d.ts +7 -5
  24. package/dist/types/src/dream/DreamClassTransactionBuilder.d.ts +4 -2
  25. package/dist/types/src/dream/DreamInstanceTransactionBuilder.d.ts +3 -3
  26. package/dist/types/src/dream/LoadBuilder.d.ts +3 -2
  27. package/dist/types/src/dream/Query.d.ts +16 -8
  28. package/dist/types/src/dream/QueryDriver/Base.d.ts +6 -0
  29. package/dist/types/src/dream/internal/destroyOptions.d.ts +3 -2
  30. package/dist/types/src/errors/sti/STIChildTypeMismatch.d.ts +11 -0
  31. package/docs/assets/hierarchy.js +1 -1
  32. package/docs/assets/search.js +1 -1
  33. package/docs/classes/db.DreamMigrationHelpers.html +11 -11
  34. package/docs/classes/db.KyselyQueryDriver.html +44 -42
  35. package/docs/classes/db.PostgresQueryDriver.html +45 -43
  36. package/docs/classes/db.QueryDriverBase.html +43 -41
  37. package/docs/classes/errors.CheckConstraintViolation.html +3 -3
  38. package/docs/classes/errors.ColumnOverflow.html +3 -3
  39. package/docs/classes/errors.CreateOrFindByFailedToCreateAndFind.html +3 -3
  40. package/docs/classes/errors.DataIncompatibleWithDatabaseField.html +3 -3
  41. package/docs/classes/errors.DataTypeColumnTypeMismatch.html +3 -3
  42. package/docs/classes/errors.DecryptionError.html +2 -2
  43. package/docs/classes/errors.DecryptionParseError.html +2 -2
  44. package/docs/classes/errors.DecryptionRotationError.html +3 -3
  45. package/docs/classes/errors.GlobalNameNotSet.html +3 -3
  46. package/docs/classes/errors.InvalidCalendarDate.html +2 -2
  47. package/docs/classes/errors.InvalidClockTime.html +2 -2
  48. package/docs/classes/errors.InvalidClockTimeTz.html +2 -2
  49. package/docs/classes/errors.InvalidDateTime.html +2 -2
  50. package/docs/classes/errors.MissingSerializersDefinition.html +3 -3
  51. package/docs/classes/errors.NonLoadedAssociation.html +3 -3
  52. package/docs/classes/errors.NotNullViolation.html +3 -3
  53. package/docs/classes/errors.RecordNotFound.html +3 -3
  54. package/docs/classes/errors.SortableBatchRequiresTooManyScopeLocks.html +3 -3
  55. package/docs/classes/errors.SortableScopeDidNotStabilize.html +3 -3
  56. package/docs/classes/errors.SortableScopeLockWaitTimedOut.html +3 -3
  57. package/docs/classes/errors.ValidationError.html +3 -3
  58. package/docs/classes/index.CalendarDate.html +33 -33
  59. package/docs/classes/index.ClockTime.html +32 -32
  60. package/docs/classes/index.ClockTimeTz.html +35 -35
  61. package/docs/classes/index.DateTime.html +86 -86
  62. package/docs/classes/index.Decorators.html +19 -19
  63. package/docs/classes/index.Dream.html +130 -128
  64. package/docs/classes/index.DreamApp.html +10 -10
  65. package/docs/classes/index.DreamTransaction.html +2 -2
  66. package/docs/classes/index.Env.html +2 -2
  67. package/docs/classes/index.Query.html +69 -67
  68. package/docs/classes/system.CliFileWriter.html +4 -4
  69. package/docs/classes/system.DreamBin.html +2 -2
  70. package/docs/classes/system.DreamCLI.html +7 -7
  71. package/docs/classes/system.DreamImporter.html +2 -2
  72. package/docs/classes/system.DreamLogos.html +2 -2
  73. package/docs/classes/system.DreamSerializerBuilder.html +11 -11
  74. package/docs/classes/system.ObjectSerializerBuilder.html +8 -8
  75. package/docs/classes/system.PathHelpers.html +3 -3
  76. package/docs/classes/utils.Encrypt.html +3 -3
  77. package/docs/classes/utils.Range.html +2 -2
  78. package/docs/functions/db.closeAllDbConnections.html +1 -1
  79. package/docs/functions/db.dreamDbConnections.html +1 -1
  80. package/docs/functions/db.untypedDb.html +1 -1
  81. package/docs/functions/db.validateColumn.html +1 -1
  82. package/docs/functions/db.validateTable.html +1 -1
  83. package/docs/functions/errors.pgErrorType.html +1 -1
  84. package/docs/functions/index.DreamSerializer.html +1 -1
  85. package/docs/functions/index.ObjectSerializer.html +1 -1
  86. package/docs/functions/index.ReplicaSafe.html +1 -1
  87. package/docs/functions/index.STI.html +1 -1
  88. package/docs/functions/index.SoftDelete.html +1 -1
  89. package/docs/functions/utils.camelize.html +1 -1
  90. package/docs/functions/utils.capitalize.html +1 -1
  91. package/docs/functions/utils.cloneDeepSafe.html +1 -1
  92. package/docs/functions/utils.compact.html +1 -1
  93. package/docs/functions/utils.groupBy.html +1 -1
  94. package/docs/functions/utils.hyphenize.html +1 -1
  95. package/docs/functions/utils.intersection.html +1 -1
  96. package/docs/functions/utils.isEmpty.html +1 -1
  97. package/docs/functions/utils.normalizeUnicode.html +1 -1
  98. package/docs/functions/utils.pascalize.html +1 -1
  99. package/docs/functions/utils.percent.html +1 -1
  100. package/docs/functions/utils.range.html +1 -1
  101. package/docs/functions/utils.round.html +1 -1
  102. package/docs/functions/utils.sanitizeString.html +1 -1
  103. package/docs/functions/utils.snakeify.html +1 -1
  104. package/docs/functions/utils.sort.html +1 -1
  105. package/docs/functions/utils.sortBy.html +1 -1
  106. package/docs/functions/utils.sortObjectByKey.html +1 -1
  107. package/docs/functions/utils.sortObjectByValue.html +1 -1
  108. package/docs/functions/utils.uncapitalize.html +1 -1
  109. package/docs/functions/utils.uniq.html +1 -1
  110. package/docs/hierarchy.html +1 -1
  111. package/docs/interfaces/openapi.OpenapiDescription.html +2 -2
  112. package/docs/interfaces/openapi.OpenapiSchemaProperties.html +1 -1
  113. package/docs/interfaces/openapi.OpenapiSchemaPropertiesShorthand.html +1 -1
  114. package/docs/interfaces/openapi.OpenapiTypeFieldObject.html +1 -1
  115. package/docs/interfaces/types.BelongsToStatement.html +2 -2
  116. package/docs/interfaces/types.DecoratorContext.html +2 -2
  117. package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
  118. package/docs/interfaces/types.DreamAppOpts.html +2 -2
  119. package/docs/interfaces/types.DreamDbConfig.html +5 -5
  120. package/docs/interfaces/types.DurationObject.html +2 -2
  121. package/docs/interfaces/types.EncryptOptions.html +2 -2
  122. package/docs/interfaces/types.InternalAnyTypedSerializerRendersMany.html +2 -2
  123. package/docs/interfaces/types.InternalAnyTypedSerializerRendersOne.html +2 -2
  124. package/docs/interfaces/types.SerializerRendererOpts.html +2 -2
  125. package/docs/types/openapi.CommonOpenapiSchemaObjectFields.html +1 -1
  126. package/docs/types/openapi.OpenapiAllTypes.html +1 -1
  127. package/docs/types/openapi.OpenapiFormats.html +1 -1
  128. package/docs/types/openapi.OpenapiNumberFormats.html +1 -1
  129. package/docs/types/openapi.OpenapiPrimitiveBaseTypes.html +1 -1
  130. package/docs/types/openapi.OpenapiPrimitiveTypes.html +1 -1
  131. package/docs/types/openapi.OpenapiSchemaArray.html +1 -1
  132. package/docs/types/openapi.OpenapiSchemaArrayShorthand.html +1 -1
  133. package/docs/types/openapi.OpenapiSchemaBase.html +1 -1
  134. package/docs/types/openapi.OpenapiSchemaBody.html +1 -1
  135. package/docs/types/openapi.OpenapiSchemaBodyShorthand.html +1 -1
  136. package/docs/types/openapi.OpenapiSchemaCommonFields.html +1 -1
  137. package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +2 -2
  138. package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +2 -2
  139. package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +2 -2
  140. package/docs/types/openapi.OpenapiSchemaExpressionRef.html +2 -2
  141. package/docs/types/openapi.OpenapiSchemaExpressionRefSchemaShorthand.html +2 -2
  142. package/docs/types/openapi.OpenapiSchemaInteger.html +1 -1
  143. package/docs/types/openapi.OpenapiSchemaNull.html +2 -2
  144. package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
  145. package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
  146. package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +1 -1
  147. package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +1 -1
  148. package/docs/types/openapi.OpenapiSchemaObjectAnyOf.html +1 -1
  149. package/docs/types/openapi.OpenapiSchemaObjectAnyOfShorthand.html +1 -1
  150. package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
  151. package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.html +1 -1
  152. package/docs/types/openapi.OpenapiSchemaObjectOneOf.html +1 -1
  153. package/docs/types/openapi.OpenapiSchemaObjectOneOfShorthand.html +1 -1
  154. package/docs/types/openapi.OpenapiSchemaObjectShorthand.html +1 -1
  155. package/docs/types/openapi.OpenapiSchemaPrimitiveGeneric.html +1 -1
  156. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAllOf.html +2 -2
  157. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +2 -2
  158. package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +2 -2
  159. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializableRef.html +2 -2
  160. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializerRef.html +2 -2
  161. package/docs/types/openapi.OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
  162. package/docs/types/openapi.OpenapiSchemaString.html +1 -1
  163. package/docs/types/openapi.OpenapiShorthandAllTypes.html +1 -1
  164. package/docs/types/openapi.OpenapiShorthandPrimitiveBaseTypes.html +1 -1
  165. package/docs/types/openapi.OpenapiShorthandPrimitiveTypes.html +1 -1
  166. package/docs/types/openapi.OpenapiTypeField.html +1 -1
  167. package/docs/types/system.DreamAppAllowedPackageManagersEnum.html +1 -1
  168. package/docs/types/types.CalendarDateDurationUnit.html +1 -1
  169. package/docs/types/types.CalendarDateObject.html +1 -1
  170. package/docs/types/types.Camelized.html +1 -1
  171. package/docs/types/types.ClockTimeObject.html +1 -1
  172. package/docs/types/types.DbConnectionType.html +1 -1
  173. package/docs/types/types.DbTypes.html +1 -1
  174. package/docs/types/types.DreamAssociationMetadata.html +1 -1
  175. package/docs/types/types.DreamAttributes.html +1 -1
  176. package/docs/types/types.DreamClassAssociationAndStatement.html +1 -1
  177. package/docs/types/types.DreamClassColumn.html +1 -1
  178. package/docs/types/types.DreamColumn.html +1 -1
  179. package/docs/types/types.DreamColumnNames.html +1 -1
  180. package/docs/types/types.DreamLogLevel.html +1 -1
  181. package/docs/types/types.DreamLogger.html +2 -2
  182. package/docs/types/types.DreamModelSerializerType.html +1 -1
  183. package/docs/types/types.DreamOrViewModelClassSerializerKey.html +1 -1
  184. package/docs/types/types.DreamOrViewModelSerializerKey.html +1 -1
  185. package/docs/types/types.DreamParamSafeAttributes.html +1 -1
  186. package/docs/types/types.DreamParamSafeColumnNames.html +1 -1
  187. package/docs/types/types.DreamSerializable.html +1 -1
  188. package/docs/types/types.DreamSerializableArray.html +1 -1
  189. package/docs/types/types.DreamSerializerKey.html +1 -1
  190. package/docs/types/types.DreamSerializers.html +1 -1
  191. package/docs/types/types.DreamVirtualColumns.html +1 -1
  192. package/docs/types/types.DurationUnit.html +1 -1
  193. package/docs/types/types.EncryptAlgorithm.html +1 -1
  194. package/docs/types/types.HasManyStatement.html +1 -1
  195. package/docs/types/types.HasOneStatement.html +1 -1
  196. package/docs/types/types.Hyphenized.html +1 -1
  197. package/docs/types/types.Pascalized.html +1 -1
  198. package/docs/types/types.PrimaryKeyType.html +1 -1
  199. package/docs/types/types.RoundingPrecision.html +1 -1
  200. package/docs/types/types.SerializerCasing.html +1 -1
  201. package/docs/types/types.SimpleObjectSerializerType.html +1 -1
  202. package/docs/types/types.Snakeified.html +1 -1
  203. package/docs/types/types.StrictInterface.html +1 -1
  204. package/docs/types/types.UpdateableAssociationProperties.html +1 -1
  205. package/docs/types/types.UpdateableProperties.html +1 -1
  206. package/docs/types/types.ValidationType.html +1 -1
  207. package/docs/types/types.ViewModel.html +2 -2
  208. package/docs/types/types.ViewModelClass.html +1 -1
  209. package/docs/types/types.WeekdayName.html +1 -1
  210. package/docs/types/types.WhereStatementForDream.html +1 -1
  211. package/docs/types/types.WhereStatementForDreamClass.html +1 -1
  212. package/docs/variables/index.DreamConst.html +1 -1
  213. package/docs/variables/index.ops.html +1 -1
  214. package/docs/variables/openapi.openapiPrimitiveTypes.html +1 -1
  215. package/docs/variables/openapi.openapiShorthandPrimitiveTypes.html +1 -1
  216. package/docs/variables/system.DreamAppAllowedPackageManagersEnumValues.html +1 -1
  217. package/docs/variables/system.primaryKeyTypes.html +1 -1
  218. package/package.json +1 -1
@@ -177,13 +177,13 @@ export default class Query {
177
177
  /**
178
178
  * @internal
179
179
  *
180
- * Whether or not to bypass all default scopes for this Query
180
+ * Whether or not to bypass all user-removable default scopes for this Query
181
181
  */
182
182
  bypassAllDefaultScopes = false;
183
183
  /**
184
184
  * @internal
185
185
  *
186
- * Whether or not to bypass all default scopes for this Query, but not associations
186
+ * Whether or not to bypass all user-removable default scopes for this Query, but not associations
187
187
  */
188
188
  bypassAllDefaultScopesExceptOnAssociations = false;
189
189
  /**
@@ -778,10 +778,12 @@ export default class Query {
778
778
  });
779
779
  }
780
780
  /**
781
- * Prevents default scopes from applying when
782
- * the Query is executed
781
+ * Prevents user-removable default scopes from applying when the Query is
782
+ * executed, including when associations are loaded. An STI child's reserved
783
+ * type discriminator remains enforced; query the STI base model to span its
784
+ * registered children.
783
785
  *
784
- * @returns A new Query which will prevent default scopes from applying
786
+ * @returns A new Query which bypasses user-removable default scopes
785
787
  */
786
788
  removeAllDefaultScopes() {
787
789
  return this.clone({
@@ -1044,8 +1046,19 @@ export default class Query {
1044
1046
  * @returns A Kysely query. Depending on the type passed, it will return either a SelectQueryBuilder, DeleteQueryBuilder, or an UpdateQueryBuilder
1045
1047
  */
1046
1048
  toKysely(type) {
1049
+ if (type !== 'select')
1050
+ this.assertMutationPreservesAssociationQuery();
1047
1051
  return this.dbDriverInstance().toKysely(type);
1048
1052
  }
1053
+ /**
1054
+ * @internal
1055
+ *
1056
+ * Prevents mutation paths that discard an association query's ownership constraint.
1057
+ */
1058
+ assertMutationPreservesAssociationQuery() {
1059
+ if (this.baseSelectQuery)
1060
+ throw new NoUpdateOnAssociationQuery();
1061
+ }
1049
1062
  /**
1050
1063
  * Applies transaction to the Query instance
1051
1064
  *
@@ -1904,7 +1917,7 @@ export default class Query {
1904
1917
  * @param options.cascade - If false, skips destroying associations marked `dependent: 'destroy'`. Defaults to true
1905
1918
  * @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
1906
1919
  * @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
1907
- * @param options.bypassAllDefaultScopes - If true, bypasses all default scopes when cascade destroying. Defaults to false
1920
+ * @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
1908
1921
  * @param options.defaultScopesToBypass - An array of default scope names to bypass when cascade destroying. Defaults to an empty array
1909
1922
  * @returns The number of records that were removed
1910
1923
  * @throws InvalidBatchSize if `batchSize` is not a positive integer
@@ -2126,7 +2139,7 @@ export default class Query {
2126
2139
  * @param options.cascade - If false, skips destroying associations marked `dependent: 'destroy'`. Defaults to true
2127
2140
  * @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
2128
2141
  * @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
2129
- * @param options.bypassAllDefaultScopes - If true, bypasses all default scopes when cascade destroying. Defaults to false
2142
+ * @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
2130
2143
  * @param options.defaultScopesToBypass - An array of default scope names to bypass when cascade destroying. Defaults to an empty array
2131
2144
  * @returns The number of records that were removed
2132
2145
  * @throws InvalidBatchSize if `batchSize` is not a positive integer
@@ -2156,7 +2169,7 @@ export default class Query {
2156
2169
  * @param options - Options for undestroying the instance
2157
2170
  * @param options.skipHooks - If true, skips applying model hooks during the undestroy operation. Defaults to false
2158
2171
  * @param options.cascade - If false, skips undestroying associations marked `dependent: 'destroy'`. Defaults to true
2159
- * @param options.bypassAllDefaultScopes - If true, bypasses all default scopes when cascade undestroying. Defaults to false
2172
+ * @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
2160
2173
  * @param options.defaultScopesToBypass - An array of default scope names to bypass when cascade undestroying (soft delete is always bypassed). Defaults to an empty array
2161
2174
  * @returns The number of records that were removed
2162
2175
  * @throws BatchingIncompatibleWithLimitOrOffset if the query carries a `limit` or `offset`
@@ -2196,11 +2209,11 @@ export default class Query {
2196
2209
  * @returns The number of records that were removed
2197
2210
  */
2198
2211
  async delete() {
2212
+ this.assertMutationPreservesAssociationQuery();
2199
2213
  return await this.dbDriverInstance().delete();
2200
2214
  }
2201
2215
  async update(attributesOrCb, { skipHooks, lock, batchSize } = {}) {
2202
- if (this.baseSelectQuery)
2203
- throw new NoUpdateOnAssociationQuery();
2216
+ this.assertMutationPreservesAssociationQuery();
2204
2217
  if (Object.keys(this.innerJoinStatements).length)
2205
2218
  throw new NoUpdateAllOnJoins();
2206
2219
  if (Object.keys(this.leftJoinStatements).length)
@@ -18,6 +18,14 @@ export default class QueryDriverBase {
18
18
  this.connectionOverride = query['originalOpts'].connection;
19
19
  this.innerJoinDreamClasses = Object.freeze(query['originalOpts'].innerJoinDreamClasses || []);
20
20
  }
21
+ /**
22
+ * @internal
23
+ *
24
+ * Prevents mutation paths that discard an association query's ownership constraint.
25
+ */
26
+ assertMutationPreservesAssociationQuery() {
27
+ this.query['assertMutationPreservesAssociationQuery']();
28
+ }
21
29
  // eslint-disable-next-line @typescript-eslint/require-await
22
30
  static async ensureAllMigrationsHaveBeenRun(
23
31
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -110,6 +118,8 @@ export default class QueryDriverBase {
110
118
  * @returns A Kysely query. Depending on the type passed, it will return either a SelectQueryBuilder, DeleteQueryBuilder, or an UpdateQueryBuilder
111
119
  */
112
120
  toKysely(type) {
121
+ if (type !== 'select')
122
+ this.assertMutationPreservesAssociationQuery();
113
123
  throw new Error('implement toKysely in child class (if it makes sense)');
114
124
  }
115
125
  /**
@@ -543,6 +553,7 @@ export default class QueryDriverBase {
543
553
  */
544
554
  // eslint-disable-next-line @typescript-eslint/require-await
545
555
  async delete() {
556
+ this.assertMutationPreservesAssociationQuery();
546
557
  throw new Error('implement delete in child class');
547
558
  }
548
559
  /**
@@ -551,6 +562,7 @@ export default class QueryDriverBase {
551
562
  */
552
563
  // eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-unused-vars
553
564
  async update(attributes) {
565
+ this.assertMutationPreservesAssociationQuery();
554
566
  throw new Error('implement update in child class');
555
567
  }
556
568
  /**
@@ -301,6 +301,8 @@ export default class KyselyQueryDriver extends QueryDriverBase {
301
301
  * @returns A Kysely query. Depending on the type passed, it will return either a SelectQueryBuilder, DeleteQueryBuilder, or an UpdateQueryBuilder
302
302
  */
303
303
  toKysely(type) {
304
+ if (type !== 'select')
305
+ this.assertMutationPreservesAssociationQuery();
304
306
  switch (type) {
305
307
  case 'select':
306
308
  return this.buildSelect();
@@ -740,6 +742,7 @@ export default class KyselyQueryDriver extends QueryDriverBase {
740
742
  * @returns the number of deleted rows
741
743
  */
742
744
  async delete() {
745
+ this.assertMutationPreservesAssociationQuery();
743
746
  const deletionResult = await executeDatabaseQuery(this.buildDelete(), 'executeTakeFirst');
744
747
  return Number(deletionResult?.numDeletedRows || 0);
745
748
  }
@@ -748,6 +751,7 @@ export default class KyselyQueryDriver extends QueryDriverBase {
748
751
  * @returns the number of updated rows
749
752
  */
750
753
  async update(attributes) {
754
+ this.assertMutationPreservesAssociationQuery();
751
755
  const kyselyQuery = this.buildUpdate(attributes);
752
756
  const res = await executeDatabaseQuery(kyselyQuery, 'execute');
753
757
  const resultData = Array.from(res.entries())?.[0]?.[1];
@@ -1014,7 +1018,11 @@ export default class KyselyQueryDriver extends QueryDriverBase {
1014
1018
  return { kyselyQuery, clone: this.query };
1015
1019
  }
1016
1020
  columnsWithRequiredLoadColumns(columns) {
1017
- return uniq(compact([this.dreamClass.primaryKey, this.dreamClass['isSTIBase'] ? 'type' : null, ...columns]));
1021
+ return uniq(compact([
1022
+ this.dreamClass.primaryKey,
1023
+ this.dreamClass['isSTIBase'] || this.dreamClass['isSTIChild'] ? 'type' : null,
1024
+ ...columns,
1025
+ ]));
1018
1026
  }
1019
1027
  /**
1020
1028
  * @internal
@@ -1784,12 +1792,13 @@ export default class KyselyQueryDriver extends QueryDriverBase {
1784
1792
  }, {});
1785
1793
  }
1786
1794
  conditionallyApplyDefaultScopes() {
1787
- if (this.query['bypassAllDefaultScopes'] || this.query['bypassAllDefaultScopesExceptOnAssociations'])
1795
+ if (this.query['bypassAllDefaultScopesExceptOnAssociations'])
1788
1796
  return this.query;
1789
1797
  const thisScopes = this.dreamClass['scopes'].default;
1790
1798
  let query = this.query;
1791
1799
  for (const scope of thisScopes) {
1792
1800
  if (!shouldBypassDefaultScope(scope.method, {
1801
+ bypassAllDefaultScopes: this.query['bypassAllDefaultScopes'],
1793
1802
  defaultScopesToBypass: [
1794
1803
  ...this.query['defaultScopesToBypass'],
1795
1804
  ...this.query['defaultScopesToBypassExceptOnAssociations'],
@@ -1,9 +1,8 @@
1
+ import { STI_SCOPE_NAME } from '../../decorators/class/STI.js';
1
2
  export default function shouldBypassDefaultScope(scopeName, { bypassAllDefaultScopes = false, defaultScopesToBypass, }) {
2
- if (bypassAllDefaultScopes)
3
- return true;
4
- if (!defaultScopesToBypass.length)
5
- return false;
6
3
  if (defaultScopesToBypass.includes(scopeName))
7
4
  return true;
5
+ if (bypassAllDefaultScopes)
6
+ return scopeName !== STI_SCOPE_NAME;
8
7
  return false;
9
8
  }
@@ -1,6 +1,9 @@
1
1
  import STIChildMissing from '../../errors/sti/STIChildMissing.js';
2
+ import STIChildTypeMismatch from '../../errors/sti/STIChildTypeMismatch.js';
2
3
  import filterRowToKnownColumns from './filterRowToKnownColumns.js';
3
4
  export default function sqlResultToDreamInstance(dreamClass, sqlResult) {
5
+ if (dreamClass['isSTIChild'] && sqlResult.type !== dreamClass['sti'].value)
6
+ throw new STIChildTypeMismatch(dreamClass, sqlResult.type, sqlResult[dreamClass.primaryKey]);
4
7
  // base-model reads select `*`, so under schema/image skew (or with a
5
8
  // column declared in ignoredColumns but not yet dropped) the row can
6
9
  // contain columns the compiled schema doesn't know about. Passing such
@@ -32,7 +35,7 @@ export default function sqlResultToDreamInstance(dreamClass, sqlResult) {
32
35
  export function findExtendingDreamClass(dreamClass, type) {
33
36
  if (!dreamClass['extendedBy'])
34
37
  return undefined;
35
- const extendingDreamClass = dreamClass['extendedBy'].find(extendingDreamClass => extendingDreamClass.sanitizedName === type);
38
+ const extendingDreamClass = dreamClass['extendedBy'].find(extendingDreamClass => extendingDreamClass['sti'].value === type);
36
39
  if (extendingDreamClass)
37
40
  return extendingDreamClass;
38
41
  return dreamClass['extendedBy']
@@ -1,7 +1,7 @@
1
1
  export default class NoUpdateOnAssociationQuery extends Error {
2
2
  get message() {
3
3
  return `
4
- udpate may not be called on an associationQuery. Use updateAssociation instead.
4
+ Mutations may not be called on an associationQuery because its ownership constraint cannot be preserved. Use an association-specific mutation method or query the associated model with explicit conditions.
5
5
  `;
6
6
  }
7
7
  }
@@ -0,0 +1,28 @@
1
+ export default class STIChildTypeMismatch extends Error {
2
+ expectedDreamClass;
3
+ expectedType;
4
+ actualType;
5
+ baseDreamClass;
6
+ table;
7
+ primaryKeyValue;
8
+ constructor(expectedDreamClass, actualType, primaryKeyValue) {
9
+ super();
10
+ this.expectedDreamClass = expectedDreamClass;
11
+ this.expectedType = expectedDreamClass['sti'].value;
12
+ this.actualType = actualType;
13
+ this.baseDreamClass = expectedDreamClass['sti'].baseClass;
14
+ this.table = this.baseDreamClass.table;
15
+ this.primaryKeyValue = primaryKeyValue;
16
+ }
17
+ get message() {
18
+ return `
19
+ Cannot hydrate STI child from a row with a different discriminator.
20
+ Expected Dream class: ${this.expectedDreamClass.sanitizedName}
21
+ Expected type: ${this.expectedType}
22
+ Type specified in DB record: ${String(this.actualType)}
23
+ STI base Dream class: ${this.baseDreamClass.sanitizedName}
24
+ Table: ${this.table}
25
+ Primary key value: ${String(this.primaryKeyValue)}
26
+ `;
27
+ }
28
+ }
@@ -594,9 +594,11 @@ export default class Dream {
594
594
  */
595
595
  static get associationNames(): string[];
596
596
  /**
597
- * Returns a query for this model which disregards default scopes
597
+ * Returns a query which bypasses user-removable default scopes, including on
598
+ * association loads. An STI child's reserved type discriminator remains
599
+ * enforced; query the STI base model to span its registered children.
598
600
  *
599
- * @returns A query for this model which disregards default scopes
601
+ * @returns A query which bypasses user-removable default scopes
600
602
  */
601
603
  static removeAllDefaultScopes<T extends typeof Dream>(this: T): Query<InstanceType<T>>;
602
604
  /**
@@ -2470,7 +2472,7 @@ export default class Dream {
2470
2472
  * @param options - Options for destroying the instance
2471
2473
  * @param options.skipHooks - If true, skips applying model hooks during the destroy operation. Defaults to false
2472
2474
  * @param options.cascade - If false, skips destroying associations marked `dependent: 'destroy'`. Defaults to true
2473
- * @param options.bypassAllDefaultScopes - If true, bypasses all default scopes when cascade destroying. Defaults to false
2475
+ * @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
2474
2476
  * @param options.defaultScopesToBypass - An array of default scope names to bypass when cascade destroying. Defaults to an empty array
2475
2477
  * @returns The instance that was destroyed
2476
2478
  */
@@ -2493,7 +2495,7 @@ export default class Dream {
2493
2495
  * @param options - Options for destroying the instance
2494
2496
  * @param options.skipHooks - If true, skips applying model hooks during the destroy operation. Defaults to false
2495
2497
  * @param options.cascade - If false, skips destroying associations marked `dependent: 'destroy'`. Defaults to true
2496
- * @param options.bypassAllDefaultScopes - If true, bypasses all default scopes when cascade destroying. Defaults to false
2498
+ * @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
2497
2499
  * @param options.defaultScopesToBypass - An array of default scope names to bypass when cascade destroying. Defaults to an empty array
2498
2500
  * @returns The instance that was destroyed
2499
2501
  */
@@ -2513,7 +2515,7 @@ export default class Dream {
2513
2515
  * @param options - Options for undestroying the instance
2514
2516
  * @param options.skipHooks - If true, skips applying model hooks during the undestroy operation. Defaults to false
2515
2517
  * @param options.cascade - If false, skips undestroying associations marked `dependent: 'destroy'`. Defaults to true
2516
- * @param options.bypassAllDefaultScopes - If true, bypasses all default scopes when cascade undestroying. Defaults to false
2518
+ * @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
2517
2519
  * @param options.defaultScopesToBypass - An array of default scope names to bypass when cascade undestroying (soft delete is always bypassed). Defaults to an empty array
2518
2520
  * @returns The undestroyed record
2519
2521
  */
@@ -599,9 +599,11 @@ export default class DreamClassTransactionBuilder<DreamClass extends typeof Drea
599
599
  */
600
600
  queryInstance<I extends DreamClassTransactionBuilder<DreamClass, DreamInstance>>(this: I): Query<DreamInstance>;
601
601
  /**
602
- * Returns a query for this model which disregards default scopes
602
+ * Returns a query which bypasses user-removable default scopes, including on
603
+ * association loads. An STI child's reserved type discriminator remains
604
+ * enforced; query the STI base model to span its registered children.
603
605
  *
604
- * @returns A query for this model which disregards default scopes
606
+ * @returns A query which bypasses user-removable default scopes
605
607
  */
606
608
  removeAllDefaultScopes<I extends DreamClassTransactionBuilder<DreamClass, DreamInstance>>(this: I): Query<DreamInstance>;
607
609
  /**
@@ -174,7 +174,7 @@ export default class DreamInstanceTransactionBuilder<DreamInstance extends Dream
174
174
  * @param options - Options for destroying the instance
175
175
  * @param options.skipHooks - If true, skips applying model hooks during the destroy operation. Defaults to false
176
176
  * @param options.cascade - If false, skips destroying associations marked `dependent: 'destroy'`. Defaults to true
177
- * @param options.bypassAllDefaultScopes - If true, bypasses all default scopes when cascade destroying. Defaults to false
177
+ * @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
178
178
  * @param options.defaultScopesToBypass - An array of default scope names to bypass when cascade destroying. Defaults to an empty array
179
179
  * @returns The instance that was destroyed
180
180
  */
@@ -194,7 +194,7 @@ export default class DreamInstanceTransactionBuilder<DreamInstance extends Dream
194
194
  * @param options - Options for destroying the instance
195
195
  * @param options.skipHooks - If true, skips applying model hooks during the destroy operation. Defaults to false
196
196
  * @param options.cascade - If false, skips destroying associations marked `dependent: 'destroy'`. Defaults to true
197
- * @param options.bypassAllDefaultScopes - If true, bypasses all default scopes when cascade destroying. Defaults to false
197
+ * @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
198
198
  * @param options.defaultScopesToBypass - An array of default scope names to bypass when cascade destroying. Defaults to an empty array
199
199
  * @returns The instance that was destroyed
200
200
  */
@@ -214,7 +214,7 @@ export default class DreamInstanceTransactionBuilder<DreamInstance extends Dream
214
214
  * @param options - Options for undestroying the instance
215
215
  * @param options.skipHooks - If true, skips applying model hooks during the undestroy operation. Defaults to false
216
216
  * @param options.cascade - If false, skips undestroying associations marked `dependent: 'destroy'`. Defaults to true
217
- * @param options.bypassAllDefaultScopes - If true, bypasses all default scopes when cascade undestroying. Defaults to false
217
+ * @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
218
218
  * @param options.defaultScopesToBypass - An array of default scope names to bypass when cascade undestroying (soft delete is always bypassed). Defaults to an empty array
219
219
  * @returns The undestroyed instance
220
220
  */
@@ -41,8 +41,9 @@ export default class LoadBuilder<DreamInstance extends Dream> {
41
41
  */
42
42
  removeDefaultScope<I extends LoadBuilder<DreamInstance>>(this: I, scopeName: AllDefaultScopeNames<DreamInstance>): I;
43
43
  /**
44
- * Prevents all default scopes from applying when
45
- * loading associations
44
+ * Prevents user-removable default scopes from applying when loading
45
+ * associations. An associated STI child's reserved type discriminator remains
46
+ * enforced; loading through its STI base still spans registered children.
46
47
  *
47
48
  * ```ts
48
49
  * const user = await User.firstOrFail()
@@ -143,13 +143,13 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
143
143
  /**
144
144
  * @internal
145
145
  *
146
- * Whether or not to bypass all default scopes for this Query
146
+ * Whether or not to bypass all user-removable default scopes for this Query
147
147
  */
148
148
  private readonly bypassAllDefaultScopes;
149
149
  /**
150
150
  * @internal
151
151
  *
152
- * Whether or not to bypass all default scopes for this Query, but not associations
152
+ * Whether or not to bypass all user-removable default scopes for this Query, but not associations
153
153
  */
154
154
  private readonly bypassAllDefaultScopesExceptOnAssociations;
155
155
  /**
@@ -511,10 +511,12 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
511
511
  */
512
512
  private setAssociationQueryBase;
513
513
  /**
514
- * Prevents default scopes from applying when
515
- * the Query is executed
514
+ * Prevents user-removable default scopes from applying when the Query is
515
+ * executed, including when associations are loaded. An STI child's reserved
516
+ * type discriminator remains enforced; query the STI base model to span its
517
+ * registered children.
516
518
  *
517
- * @returns A new Query which will prevent default scopes from applying
519
+ * @returns A new Query which bypasses user-removable default scopes
518
520
  */
519
521
  removeAllDefaultScopes(): Query<DreamInstance, QueryTypeOpts>;
520
522
  /**
@@ -727,6 +729,12 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
727
729
  * @returns A Kysely query. Depending on the type passed, it will return either a SelectQueryBuilder, DeleteQueryBuilder, or an UpdateQueryBuilder
728
730
  */
729
731
  toKysely<QueryType extends 'select' | 'delete' | 'update', DbType = QueryToKyselyDBType<typeof this>, TableNames = QueryToKyselyTableNamesType<typeof this>, ToKyselyReturnType = QueryType extends 'select' ? SelectQueryBuilder<DbType, TableNames & keyof DbType, unknown> : QueryType extends 'delete' ? DeleteQueryBuilder<DbType, TableNames & keyof DbType, unknown> : QueryType extends 'update' ? UpdateQueryBuilder<DbType, TableNames & keyof DbType, TableNames & keyof DbType, unknown> : never>(type: QueryType): ToKyselyReturnType;
732
+ /**
733
+ * @internal
734
+ *
735
+ * Prevents mutation paths that discard an association query's ownership constraint.
736
+ */
737
+ private assertMutationPreservesAssociationQuery;
730
738
  /**
731
739
  * Applies transaction to the Query instance
732
740
  *
@@ -1351,7 +1359,7 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
1351
1359
  * @param options.cascade - If false, skips destroying associations marked `dependent: 'destroy'`. Defaults to true
1352
1360
  * @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
1353
1361
  * @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
1354
- * @param options.bypassAllDefaultScopes - If true, bypasses all default scopes when cascade destroying. Defaults to false
1362
+ * @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
1355
1363
  * @param options.defaultScopesToBypass - An array of default scope names to bypass when cascade destroying. Defaults to an empty array
1356
1364
  * @returns The number of records that were removed
1357
1365
  * @throws InvalidBatchSize if `batchSize` is not a positive integer
@@ -1469,7 +1477,7 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
1469
1477
  * @param options.cascade - If false, skips destroying associations marked `dependent: 'destroy'`. Defaults to true
1470
1478
  * @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
1471
1479
  * @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
1472
- * @param options.bypassAllDefaultScopes - If true, bypasses all default scopes when cascade destroying. Defaults to false
1480
+ * @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
1473
1481
  * @param options.defaultScopesToBypass - An array of default scope names to bypass when cascade destroying. Defaults to an empty array
1474
1482
  * @returns The number of records that were removed
1475
1483
  * @throws InvalidBatchSize if `batchSize` is not a positive integer
@@ -1502,7 +1510,7 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
1502
1510
  * @param options - Options for undestroying the instance
1503
1511
  * @param options.skipHooks - If true, skips applying model hooks during the undestroy operation. Defaults to false
1504
1512
  * @param options.cascade - If false, skips undestroying associations marked `dependent: 'destroy'`. Defaults to true
1505
- * @param options.bypassAllDefaultScopes - If true, bypasses all default scopes when cascade undestroying. Defaults to false
1513
+ * @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
1506
1514
  * @param options.defaultScopesToBypass - An array of default scope names to bypass when cascade undestroying (soft delete is always bypassed). Defaults to an empty array
1507
1515
  * @returns The number of records that were removed
1508
1516
  * @throws BatchingIncompatibleWithLimitOrOffset if the query carries a `limit` or `offset`
@@ -20,6 +20,12 @@ export default class QueryDriverBase<DreamInstance extends Dream> {
20
20
  */
21
21
  protected readonly innerJoinDreamClasses: readonly (typeof Dream)[];
22
22
  constructor(query: Query<DreamInstance, any>);
23
+ /**
24
+ * @internal
25
+ *
26
+ * Prevents mutation paths that discard an association query's ownership constraint.
27
+ */
28
+ protected assertMutationPreservesAssociationQuery(): void;
23
29
  static ensureAllMigrationsHaveBeenRun(connectionName: string): Promise<void>;
24
30
  /**
25
31
  * migrate the database. Must respond to the NODE_ENV value.
@@ -5,8 +5,9 @@ import { AllDefaultScopeNames } from '../../types/dream.js';
5
5
  */
6
6
  export interface DestroyOptions<DreamInstance extends Dream> {
7
7
  /**
8
- * If true, bypasses all default scopes when destroying the instance.
9
- * Defaults to false.
8
+ * If true, bypasses user-removable default scopes during the destroy operation.
9
+ * An STI child's reserved discriminator remains enforced, including when
10
+ * associations are loaded. Defaults to false.
10
11
  */
11
12
  bypassAllDefaultScopes?: boolean | undefined;
12
13
  /**
@@ -0,0 +1,11 @@
1
+ import Dream from '../../Dream.js';
2
+ export default class STIChildTypeMismatch extends Error {
3
+ readonly expectedDreamClass: typeof Dream;
4
+ readonly expectedType: string;
5
+ readonly actualType: unknown;
6
+ readonly baseDreamClass: typeof Dream;
7
+ readonly table: string;
8
+ readonly primaryKeyValue: unknown;
9
+ constructor(expectedDreamClass: typeof Dream, actualType: unknown, primaryKeyValue: unknown);
10
+ get message(): string;
11
+ }
@@ -1 +1 @@
1
- window.hierarchyData = "eJyVksFOwzAMht/F52w0rRp1PbIJCSEGSAgO0w5Z69FoaTIl6VA17d1R2CWMQdOTI8u//y+2j2C0dhbKFWUJJZTSNQGDW4mVE1pZKI9AKfVB8RahhJcOTb8w4oDmllsEAjuhaihpWhDojIQSKsmtRXtTb6YX1dPGtRLIuQJKcLaeePnknCBQNULWBhWUq3RG1ycC6Sx0f+gtyj7oOuD/q34MAc1o6hF8DBietXUfBm08xRXFAId3zVkeuM4brHZzrawzXCj3JrTkfkX/WaMx2tjpX9IYhiILGbTsWvV0QLOV+jPG+Ycgwo8l4bYX3PF7Vel2z53YSHwXrvG5Dbd4J1DWEQiDPUZdRM5y4odCKEsTQllWfB8IS5ML7Nd+j+ff+9ejsC13VRPJe10cM76sCDiW2i07KcecyqVkyPN0+gKH6W7r"
1
+ window.hierarchyData = "eJyVkkFPwjAUx7/LOxekY3SsRyEmxoiaGD0QDmV7uIauJW2HWQjf3VQuFdGNU5vm/fv79fUdwBrjHfAlZaOMUEpXBCxuFBZeGu2AH4BSGhYtagQOLw3adm7lHu2tcAgEtlKXwGkyJdBYBRwKJZxDd1Ouh2fVw8rXCsipAjh4Vw5CfHA6IFBUUpUWNfBlko9XRwJJPo7oD61D1Ua3dvB/1V9jQMeUBYWwRg7PxvkPi66/xYVEh0egTrK477MKi+3MaOetkNq/SaNE+KL/0GitsW74V7SPwzSPHYxqav20R7tR5rMP+UegB4+Nsog3F17c68LUO+HlWuG79FU4WwuHdxJV2UOh846rJmKSURKaQihLGKEsTb8HhCXsTPu13eHp9WH3KF0tfFH19L0c7tO+NI08FsYvGqWuGZXzSBfzePwC4ktvDw=="