@rvoh/dream 2.35.0 → 2.36.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/README.md +2 -2
- package/dist/cjs/src/Dream.js +258 -55
- package/dist/cjs/src/bin/index.js +13 -0
- package/dist/cjs/src/cli/index.js +28 -17
- package/dist/cjs/src/db/migration-helpers/DreamMigrationHelpers.js +23 -21
- package/dist/cjs/src/decorators/Decorators.js +26 -4
- package/dist/cjs/src/decorators/field/Encrypted.js +30 -6
- package/dist/cjs/src/dream/DreamClassTransactionBuilder.js +6 -6
- package/dist/cjs/src/dream/DreamInstanceTransactionBuilder.js +63 -27
- package/dist/cjs/src/dream/LeftJoinLoadBuilder.js +3 -3
- package/dist/cjs/src/dream/LoadBuilder.js +1 -1
- package/dist/cjs/src/dream/Query.js +542 -167
- package/dist/cjs/src/dream/QueryDriver/Base.js +72 -0
- package/dist/cjs/src/dream/QueryDriver/Kysely.js +216 -50
- package/dist/cjs/src/dream/internal/associations/assertRecognizedAssociationConditionKeys.js +48 -0
- package/dist/cjs/src/dream/internal/associations/associationQuery.js +4 -0
- package/dist/cjs/src/dream/internal/reload.js +6 -1
- package/dist/cjs/src/dream/internal/saveDream.js +8 -2
- package/dist/cjs/src/dream/internal/sqlResultToDreamInstance.js +2 -0
- package/dist/cjs/src/dream/internal/undestroyDream.js +1 -1
- package/dist/cjs/src/dream-app/index.js +38 -0
- package/dist/cjs/src/encrypt/InternalEncrypt.js +49 -4
- package/dist/cjs/src/encrypt/defaultEncryptedColumnName.js +10 -0
- package/dist/cjs/src/encrypt/index.js +76 -9
- package/dist/cjs/src/errors/DoNotSetEncryptedFieldsDirectly.js +4 -0
- package/dist/cjs/src/errors/associations/UnrecognizedAssociationConditionKeys.js +25 -0
- package/dist/cjs/src/helpers/Env.js +28 -0
- package/dist/cjs/src/helpers/cli/generateDreamContent.js +2 -1
- package/dist/cjs/src/helpers/cli/generateFactoryContent.js +1 -1
- package/dist/cjs/src/helpers/cli/generateMigrationContent.js +9 -9
- package/dist/cjs/src/helpers/cli/generateSerializerContent.js +21 -8
- package/dist/cjs/src/serializer/SerializerRenderer.js +65 -13
- package/dist/cjs/src/serializer/builders/DreamSerializerBuilder.js +54 -38
- package/dist/cjs/src/serializer/builders/ObjectSerializerBuilder.js +34 -14
- package/dist/cjs/src/serializer/helpers/serializersForAssociatedClass.js +43 -0
- package/dist/esm/src/Dream.js +258 -55
- package/dist/esm/src/bin/index.js +13 -0
- package/dist/esm/src/cli/index.js +28 -17
- package/dist/esm/src/db/migration-helpers/DreamMigrationHelpers.js +23 -21
- package/dist/esm/src/decorators/Decorators.js +26 -4
- package/dist/esm/src/decorators/field/Encrypted.js +30 -6
- package/dist/esm/src/dream/DreamClassTransactionBuilder.js +6 -6
- package/dist/esm/src/dream/DreamInstanceTransactionBuilder.js +63 -27
- package/dist/esm/src/dream/LeftJoinLoadBuilder.js +3 -3
- package/dist/esm/src/dream/LoadBuilder.js +1 -1
- package/dist/esm/src/dream/Query.js +542 -167
- package/dist/esm/src/dream/QueryDriver/Base.js +72 -0
- package/dist/esm/src/dream/QueryDriver/Kysely.js +216 -50
- package/dist/esm/src/dream/internal/associations/assertRecognizedAssociationConditionKeys.js +48 -0
- package/dist/esm/src/dream/internal/associations/associationQuery.js +4 -0
- package/dist/esm/src/dream/internal/reload.js +6 -1
- package/dist/esm/src/dream/internal/saveDream.js +8 -2
- package/dist/esm/src/dream/internal/sqlResultToDreamInstance.js +2 -0
- package/dist/esm/src/dream/internal/undestroyDream.js +1 -1
- package/dist/esm/src/dream-app/index.js +38 -0
- package/dist/esm/src/encrypt/InternalEncrypt.js +49 -4
- package/dist/esm/src/encrypt/defaultEncryptedColumnName.js +10 -0
- package/dist/esm/src/encrypt/index.js +76 -9
- package/dist/esm/src/errors/DoNotSetEncryptedFieldsDirectly.js +4 -0
- package/dist/esm/src/errors/associations/UnrecognizedAssociationConditionKeys.js +25 -0
- package/dist/esm/src/helpers/Env.js +28 -0
- package/dist/esm/src/helpers/cli/generateDreamContent.js +2 -1
- package/dist/esm/src/helpers/cli/generateFactoryContent.js +1 -1
- package/dist/esm/src/helpers/cli/generateMigrationContent.js +9 -9
- package/dist/esm/src/helpers/cli/generateSerializerContent.js +21 -8
- package/dist/esm/src/serializer/SerializerRenderer.js +65 -13
- package/dist/esm/src/serializer/builders/DreamSerializerBuilder.js +54 -38
- package/dist/esm/src/serializer/builders/ObjectSerializerBuilder.js +34 -14
- package/dist/esm/src/serializer/helpers/serializersForAssociatedClass.js +43 -0
- package/dist/types/src/Dream.d.ts +206 -42
- package/dist/types/src/bin/index.d.ts +7 -1
- package/dist/types/src/cli/index.d.ts +13 -0
- package/dist/types/src/db/migration-helpers/DreamMigrationHelpers.d.ts +2 -2
- package/dist/types/src/decorators/Decorators.d.ts +26 -3
- package/dist/types/src/decorators/field/Encrypted.d.ts +6 -1
- package/dist/types/src/dream/DreamClassTransactionBuilder.d.ts +9 -9
- package/dist/types/src/dream/DreamInstanceTransactionBuilder.d.ts +52 -25
- package/dist/types/src/dream/LeftJoinLoadBuilder.d.ts +4 -4
- package/dist/types/src/dream/LoadBuilder.d.ts +1 -1
- package/dist/types/src/dream/Query.d.ts +272 -33
- package/dist/types/src/dream/QueryDriver/Base.d.ts +63 -1
- package/dist/types/src/dream/QueryDriver/Kysely.d.ts +85 -1
- package/dist/types/src/dream/internal/associations/assertRecognizedAssociationConditionKeys.d.ts +29 -0
- package/dist/types/src/dream-app/index.d.ts +56 -2
- package/dist/types/src/encrypt/InternalEncrypt.d.ts +20 -1
- package/dist/types/src/encrypt/defaultEncryptedColumnName.d.ts +7 -0
- package/dist/types/src/encrypt/index.d.ts +75 -6
- package/dist/types/src/errors/associations/UnrecognizedAssociationConditionKeys.d.ts +8 -0
- package/dist/types/src/helpers/Env.d.ts +28 -0
- package/dist/types/src/package-exports/openapi.d.ts +1 -1
- package/dist/types/src/package-exports/types.d.ts +2 -2
- package/dist/types/src/serializer/SerializerRenderer.d.ts +13 -0
- package/dist/types/src/serializer/builders/DreamSerializerBuilder.d.ts +59 -55
- package/dist/types/src/serializer/builders/ObjectSerializerBuilder.d.ts +40 -19
- package/dist/types/src/serializer/helpers/serializersForAssociatedClass.d.ts +17 -0
- package/dist/types/src/types/lifecycle.d.ts +6 -0
- package/dist/types/src/types/openapi.d.ts +52 -29
- package/dist/types/src/types/serializer.d.ts +43 -2
- package/dist/types/src/types/types/lifecycle.ts +6 -0
- package/dist/types/src/types/types/openapi.ts +51 -37
- package/dist/types/src/types/types/serializer.ts +62 -2
- package/dist/types/src/types/types/variadic.ts +99 -43
- package/dist/types/src/types/variadic.d.ts +29 -3
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/highlight.css +4 -11
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/db.DreamMigrationHelpers.html +13 -13
- package/docs/classes/db.KyselyQueryDriver.html +85 -46
- package/docs/classes/db.PostgresQueryDriver.html +86 -47
- package/docs/classes/db.QueryDriverBase.html +84 -45
- package/docs/classes/errors.CannotSaveMissingDream.html +2 -2
- 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.SortableScopeDidNotStabilize.html +3 -3
- package/docs/classes/errors.SortableScopeLockWaitTimedOut.html +3 -3
- 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 +37 -23
- package/docs/classes/index.Dream.html +270 -142
- package/docs/classes/index.DreamApp.html +40 -10
- package/docs/classes/index.DreamTransaction.html +3 -3
- package/docs/classes/index.Env.html +16 -2
- package/docs/classes/index.Query.html +331 -129
- package/docs/classes/system.CliFileWriter.html +4 -4
- package/docs/classes/system.DreamBin.html +6 -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 +52 -34
- package/docs/classes/system.ObjectSerializerBuilder.html +41 -22
- package/docs/classes/system.PathHelpers.html +4 -4
- package/docs/classes/utils.Encrypt.html +54 -10
- 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 +1 -1
- 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/index.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.DecryptCallbacks.html +6 -0
- package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
- package/docs/interfaces/types.DreamAppOpts.html +2 -2
- package/docs/interfaces/types.DreamDbConfig.html +6 -6
- package/docs/interfaces/types.DurationObject.html +2 -2
- package/docs/interfaces/types.EncryptOptions.html +2 -2
- package/docs/interfaces/types.EncryptionLegacyKeyUsedEvent.html +10 -0
- 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/openapi.html +1 -1
- package/docs/modules/types.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.OpenapiSchemaCombinatorFields.html +4 -0
- package/docs/types/openapi.OpenapiSchemaCommonFields.html +5 -1
- package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +1 -2
- package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +1 -2
- package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +1 -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 +4 -2
- package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +5 -1
- package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +2 -1
- package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.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 +1 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +1 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +1 -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/serializer/helpers/serializerForAssociatedClass.js +0 -37
- package/dist/esm/src/serializer/helpers/serializerForAssociatedClass.js +0 -37
- package/dist/types/src/serializer/helpers/serializerForAssociatedClass.d.ts +0 -9
- package/docs/types/openapi.OpenapiSchemaObjectAnyOf.html +0 -1
- package/docs/types/openapi.OpenapiSchemaObjectAnyOfShorthand.html +0 -1
- package/docs/types/openapi.OpenapiSchemaObjectOneOf.html +0 -1
- package/docs/types/openapi.OpenapiSchemaObjectOneOfShorthand.html +0 -1
package/README.md
CHANGED
|
@@ -261,11 +261,11 @@ const userIds = await User.where({ name: ops.similarity('steeven hawkins') }).pl
|
|
|
261
261
|
const min = await User.where({ name: ops.similarity('steeven hawkins') }).min('id')
|
|
262
262
|
const max = await User.where({ name: ops.similarity('steeven hawkins') }).max('id')
|
|
263
263
|
const count = await User.where({ name: ops.similarity('steeven hawkins') }).count()
|
|
264
|
-
const petIds = await User.innerJoin('pets', { name: ops.similarity('fido') }).pluck('pets.id')
|
|
264
|
+
const petIds = await User.innerJoin('pets', { and: { name: ops.similarity('fido') } }).pluck('pets.id')
|
|
265
265
|
const numUpdatedRecords = await User.where({ name: ops.similarity('steeven hawkins') }).update({
|
|
266
266
|
name: 'Stephen Hawking',
|
|
267
267
|
})
|
|
268
|
-
const users = await User.innerJoin('pets', { name: ops.similarity('fido') }).all()
|
|
268
|
+
const users = await User.innerJoin('pets', { and: { name: ops.similarity('fido') } }).all()
|
|
269
269
|
|
|
270
270
|
const user = await User.first()
|
|
271
271
|
const pets = await user
|
package/dist/cjs/src/Dream.js
CHANGED
|
@@ -8,6 +8,7 @@ import DreamApp from './dream-app/index.js';
|
|
|
8
8
|
import { RECURSIVE_SERIALIZATION_MAX_REPEATS } from './dream/constants.js';
|
|
9
9
|
import DreamClassTransactionBuilder from './dream/DreamClassTransactionBuilder.js';
|
|
10
10
|
import DreamInstanceTransactionBuilder from './dream/DreamInstanceTransactionBuilder.js';
|
|
11
|
+
import assertRecognizedAssociationConditionKeys, { DESTROY_ASSOCIATION_OPTION_KEYS, UPDATE_ASSOCIATION_OPTION_KEYS, } from './dream/internal/associations/assertRecognizedAssociationConditionKeys.js';
|
|
11
12
|
import associationQuery from './dream/internal/associations/associationQuery.js';
|
|
12
13
|
import associationUpdateQuery from './dream/internal/associations/associationUpdateQuery.js';
|
|
13
14
|
import createAssociation from './dream/internal/associations/createAssociation.js';
|
|
@@ -30,6 +31,7 @@ import updateOrCreateBy from './dream/internal/updateOrCreateBy.js';
|
|
|
30
31
|
import LeftJoinLoadBuilder from './dream/LeftJoinLoadBuilder.js';
|
|
31
32
|
import LoadBuilder from './dream/LoadBuilder.js';
|
|
32
33
|
import Query from './dream/Query.js';
|
|
34
|
+
import InternalEncrypt from './encrypt/InternalEncrypt.js';
|
|
33
35
|
import CannotAssociationQueryOnUnpersistedDream from './errors/associations/CannotAssociationQueryOnUnpersistedDream.js';
|
|
34
36
|
import CannotCreateAssociationOnUnpersistedDream from './errors/associations/CannotCreateAssociationOnUnpersistedDream.js';
|
|
35
37
|
import CannotDestroyAssociationOnUnpersistedDream from './errors/associations/CannotDestroyAssociationOnUnpersistedDream.js';
|
|
@@ -40,6 +42,7 @@ import CannotCallUndestroyOnANonSoftDeleteModel from './errors/CannotCallUndestr
|
|
|
40
42
|
import ConstructorOnlyForInternalUse from './errors/ConstructorOnlyForInternalUse.js';
|
|
41
43
|
import CreateOrFindByFailedToCreateAndFind from './errors/CreateOrFindByFailedToCreateAndFind.js';
|
|
42
44
|
import CreateOrUpdateByFailedToCreateAndUpdate from './errors/CreateOrUpdateByFailedToCreateAndUpdate.js';
|
|
45
|
+
import DoNotSetEncryptedFieldsDirectly from './errors/DoNotSetEncryptedFieldsDirectly.js';
|
|
43
46
|
import GlobalNameNotSet from './errors/dream-app/GlobalNameNotSet.js';
|
|
44
47
|
import DreamMissingRequiredOverride from './errors/DreamMissingRequiredOverride.js';
|
|
45
48
|
import NonExistentScopeProvidedToResort from './errors/NonExistentScopeProvidedToResort.js';
|
|
@@ -1429,6 +1432,67 @@ export default class Dream {
|
|
|
1429
1432
|
static async findEach(cb, opts) {
|
|
1430
1433
|
await this.query().findEach(cb, opts);
|
|
1431
1434
|
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Encrypts every value of the model's `@deco.Encrypted` properties again
|
|
1437
|
+
* with the current column encryption key, so that reading them no longer
|
|
1438
|
+
* needs the `legacy` key. It is a step of a key rotation (see
|
|
1439
|
+
* `Encrypt.generateKey`): run it in each environment, once that
|
|
1440
|
+
* environment has deployed its rotated keys, from a console or a script,
|
|
1441
|
+
* not from a migration, whose result would then depend on the keys a given
|
|
1442
|
+
* environment has configured when the migration runs.
|
|
1443
|
+
*
|
|
1444
|
+
* ```ts
|
|
1445
|
+
* await User.reencryptEncryptedColumns()
|
|
1446
|
+
* ```
|
|
1447
|
+
*
|
|
1448
|
+
* It walks every row of the model's table, including rows its default
|
|
1449
|
+
* scopes (such as soft delete) hide and an STI model's siblings, in batches in primary key order, and opens
|
|
1450
|
+
* each non-null value with the current key or, failing that, the legacy
|
|
1451
|
+
* one (see `Encrypt.reencrypt`). Each row is written on its own, with
|
|
1452
|
+
* no model hooks and without changing `updatedAt`, and only while the
|
|
1453
|
+
* column still holds the value read, so a row an app saves in between
|
|
1454
|
+
* keeps the newer value. Reading values here does not fire
|
|
1455
|
+
* `dreamApp.on('encryption:legacy-key-used', ...)`.
|
|
1456
|
+
*
|
|
1457
|
+
* **Run it only once every server encrypts with the new key.** It walks
|
|
1458
|
+
* the table once, so a row a server still configured with the old
|
|
1459
|
+
* `current` writes behind it stays encrypted with the old key, and so does
|
|
1460
|
+
* old-key ciphertext later written straight into a backing column
|
|
1461
|
+
* (`setAttribute`, `setAttributes` and `updateAttributes` accept, for a
|
|
1462
|
+
* backing column, ciphertext that either key opens). Once no writer uses
|
|
1463
|
+
* the old key and this has run for every model with encrypted properties,
|
|
1464
|
+
* `legacy` is safe to drop.
|
|
1465
|
+
*
|
|
1466
|
+
* **A value neither key opens stops it**, throwing as reading the property
|
|
1467
|
+
* would (`DecryptionRotationError` while a `legacy` key is configured,
|
|
1468
|
+
* `DecryptionError` without one). The rows rewritten before it stay
|
|
1469
|
+
* rewritten; running it again once the value is fixed rewrites them again
|
|
1470
|
+
* harmlessly.
|
|
1471
|
+
*
|
|
1472
|
+
* @param opts - Optional parameters
|
|
1473
|
+
* @param opts.batchSize - how many rows to read per batch. Defaults to 1000
|
|
1474
|
+
*/
|
|
1475
|
+
static async reencryptEncryptedColumns(opts) {
|
|
1476
|
+
const encryptedColumnNames = this.encryptedAttributes.map(attribute => attribute.encryptedColumnName);
|
|
1477
|
+
if (!encryptedColumnNames.length)
|
|
1478
|
+
return;
|
|
1479
|
+
const tableClass = this.stiBaseClassOrOwnClass;
|
|
1480
|
+
await tableClass.removeAllDefaultScopes().findEach(async (record) => {
|
|
1481
|
+
for (const columnName of encryptedColumnNames) {
|
|
1482
|
+
const ciphertext = record.getAttribute(columnName);
|
|
1483
|
+
if (ciphertext === null || ciphertext === undefined)
|
|
1484
|
+
continue;
|
|
1485
|
+
// written through Kysely, since Query#update refuses any value for a
|
|
1486
|
+
// backing column, ciphertext included
|
|
1487
|
+
await tableClass
|
|
1488
|
+
.removeAllDefaultScopes()
|
|
1489
|
+
.where({ [tableClass.primaryKey]: record.primaryKeyValue(), [columnName]: ciphertext })
|
|
1490
|
+
.toKysely('update')
|
|
1491
|
+
.set({ [columnName]: InternalEncrypt.reencryptColumn(ciphertext) })
|
|
1492
|
+
.execute();
|
|
1493
|
+
}
|
|
1494
|
+
}, opts);
|
|
1495
|
+
}
|
|
1432
1496
|
/**
|
|
1433
1497
|
* Returns a new instance of Query scoped to the given
|
|
1434
1498
|
* model class. Especially useful with {@link Query#toKysely}.
|
|
@@ -1560,12 +1624,12 @@ export default class Dream {
|
|
|
1560
1624
|
* planned drop safe.
|
|
1561
1625
|
*
|
|
1562
1626
|
* ```ts
|
|
1563
|
-
* const user = await User.leftJoinPreload('posts', 'comments', {
|
|
1627
|
+
* const user = await User.leftJoinPreload('posts', 'comments', { and: { visibility: 'public' } }, 'replies').first()
|
|
1564
1628
|
* console.log(user.posts[0].comments[0].replies)
|
|
1565
1629
|
* // [Reply{id: 1}, Reply{id: 2}]
|
|
1566
1630
|
* ```
|
|
1567
1631
|
*
|
|
1568
|
-
* @param args - A chain of association names and
|
|
1632
|
+
* @param args - A chain of association names and and/andNot/andAny clauses
|
|
1569
1633
|
* @returns A query for this model with the include statement applied
|
|
1570
1634
|
*/
|
|
1571
1635
|
static leftJoinPreload(...args) {
|
|
@@ -1582,12 +1646,12 @@ export default class Dream {
|
|
|
1582
1646
|
* Preload is useful for avoiding the N+1 query problem
|
|
1583
1647
|
*
|
|
1584
1648
|
* ```ts
|
|
1585
|
-
* const user = await User.preload('posts', 'comments', {
|
|
1649
|
+
* const user = await User.preload('posts', 'comments', { and: { visibility: 'public' } }, 'replies').first()
|
|
1586
1650
|
* console.log(user.posts[0].comments[0].replies)
|
|
1587
1651
|
* // [Reply{id: 1}, Reply{id: 2}]
|
|
1588
1652
|
* ```
|
|
1589
1653
|
*
|
|
1590
|
-
* @param args - A chain of association names and
|
|
1654
|
+
* @param args - A chain of association names and and/andNot/andAny clauses
|
|
1591
1655
|
* @returns A query for this model with the preload statement applied
|
|
1592
1656
|
*/
|
|
1593
1657
|
static preload(...args) {
|
|
@@ -1647,7 +1711,7 @@ export default class Dream {
|
|
|
1647
1711
|
* await User.innerJoin('posts').first()
|
|
1648
1712
|
* ```
|
|
1649
1713
|
*
|
|
1650
|
-
* @param args - A chain of association names and
|
|
1714
|
+
* @param args - A chain of association names and and/andNot/andAny clauses
|
|
1651
1715
|
* @returns A Query for this model with the inner join clause applied
|
|
1652
1716
|
*/
|
|
1653
1717
|
static innerJoin(...args) {
|
|
@@ -1661,7 +1725,7 @@ export default class Dream {
|
|
|
1661
1725
|
* await user.innerJoin('posts').first()
|
|
1662
1726
|
* ```
|
|
1663
1727
|
*
|
|
1664
|
-
* @param args - A chain of association names and
|
|
1728
|
+
* @param args - A chain of association names and and/andNot/andAny clauses
|
|
1665
1729
|
* @returns A Query for this model with the inner join clause applied
|
|
1666
1730
|
*/
|
|
1667
1731
|
innerJoin(...args) {
|
|
@@ -1675,7 +1739,7 @@ export default class Dream {
|
|
|
1675
1739
|
* await User.leftJoin('posts').first()
|
|
1676
1740
|
* ```
|
|
1677
1741
|
*
|
|
1678
|
-
* @param args - A chain of association names and
|
|
1742
|
+
* @param args - A chain of association names and and/andNot/andAny clauses
|
|
1679
1743
|
* @returns A Query for this model with the left join clause applied
|
|
1680
1744
|
*/
|
|
1681
1745
|
static leftJoin(...args) {
|
|
@@ -1689,7 +1753,7 @@ export default class Dream {
|
|
|
1689
1753
|
* await user.leftJoin('posts').first()
|
|
1690
1754
|
* ```
|
|
1691
1755
|
*
|
|
1692
|
-
* @param args - A chain of association names and
|
|
1756
|
+
* @param args - A chain of association names and and/andNot/andAny clauses
|
|
1693
1757
|
* @returns A Query for this model with the left join clause applied
|
|
1694
1758
|
*/
|
|
1695
1759
|
leftJoin(...args) {
|
|
@@ -2589,6 +2653,15 @@ export default class Dream {
|
|
|
2589
2653
|
* const user = User.new({ email: 'how@yadoin' })
|
|
2590
2654
|
* ```
|
|
2591
2655
|
*
|
|
2656
|
+
* @param opts - the attributes to set on the new instance
|
|
2657
|
+
* @param additionalOpts - optional parameters
|
|
2658
|
+
* @param additionalOpts.bypassUserDefinedSetters - if true, sets the attributes
|
|
2659
|
+
* as {@link Dream.setAttributes | setAttributes} does, bypassing any
|
|
2660
|
+
* custom-defined setters. An encrypted property (e.g. `secret`) is still
|
|
2661
|
+
* encrypted, and an `@Encrypted` backing column (e.g. `encryptedSecret`)
|
|
2662
|
+
* accepts only ciphertext that decrypts with this app's column encryption
|
|
2663
|
+
* keys, or `null`; anything else throws (see
|
|
2664
|
+
* {@link Dream.setAttribute | setAttribute}). Defaults to false
|
|
2592
2665
|
* @returns A new (unpersisted) instance of the provided dream class
|
|
2593
2666
|
*/
|
|
2594
2667
|
static new(opts, additionalOpts = {}) {
|
|
@@ -2658,7 +2731,7 @@ export default class Dream {
|
|
|
2658
2731
|
*
|
|
2659
2732
|
* Used for determining which attributes to update
|
|
2660
2733
|
*/
|
|
2661
|
-
static extractAttributesFromUpdateableProperties(attributes, dreamInstance, { bypassUserDefinedSetters = false } = {}) {
|
|
2734
|
+
static extractAttributesFromUpdateableProperties(attributes, dreamInstance, { bypassUserDefinedSetters = false, fromDatabase = false } = {}) {
|
|
2662
2735
|
const returnValues = {};
|
|
2663
2736
|
const setAttributeOnDreamInstance = (attr, value) => {
|
|
2664
2737
|
if (!dreamInstance)
|
|
@@ -2672,8 +2745,13 @@ export default class Dream {
|
|
|
2672
2745
|
}
|
|
2673
2746
|
else if (bypassUserDefinedSetters && !isJsonColumn(this, attr)) {
|
|
2674
2747
|
// bypass user defined setters, and this field is not json, so set
|
|
2675
|
-
// the attribute directly and return the attribute
|
|
2676
|
-
|
|
2748
|
+
// the attribute directly and return the attribute. A row read from
|
|
2749
|
+
// the database already holds the ciphertext Dream stored, so it skips
|
|
2750
|
+
// setAttribute's check of @Encrypted backing columns
|
|
2751
|
+
if (fromDatabase)
|
|
2752
|
+
dreamInstance['setAttributeUnchecked'](attr, value);
|
|
2753
|
+
else
|
|
2754
|
+
dreamInstance.setAttribute(attr, value);
|
|
2677
2755
|
return dreamInstance.getAttribute(attr);
|
|
2678
2756
|
}
|
|
2679
2757
|
else {
|
|
@@ -2903,13 +2981,47 @@ export default class Dream {
|
|
|
2903
2981
|
* bypassing any custom-defined setters. If you would like to set attributes
|
|
2904
2982
|
* without bypassing custom-defined setters, use #assignAttribute instead
|
|
2905
2983
|
*
|
|
2984
|
+
* An `@Encrypted` backing column (e.g. `encryptedSecret`) accepts only
|
|
2985
|
+
* a string of ciphertext that decrypts with this app's column encryption
|
|
2986
|
+
* keys (current or legacy), or `null`, which clears it. Anything else,
|
|
2987
|
+
* including plaintext, `undefined`, a non-string value, and ciphertext
|
|
2988
|
+
* made with a key this app does not hold, throws and leaves the column
|
|
2989
|
+
* unchanged. The check covers the backing columns of this model's class,
|
|
2990
|
+
* including those it inherits from an STI parent; a backing column
|
|
2991
|
+
* declared only by another class in the same STI hierarchy is not
|
|
2992
|
+
* checked.
|
|
2993
|
+
*
|
|
2994
|
+
* Given the name of an encrypted property (e.g. `secret`), setAttribute
|
|
2995
|
+
* neither encrypts the value nor stores it: it keeps the value,
|
|
2996
|
+
* unencrypted, under that name in this instance's attributes, which a
|
|
2997
|
+
* save does not write, while the backing column and the property keep
|
|
2998
|
+
* the value they had. To store a new value, assign the property
|
|
2999
|
+
* (`user.secret = 'new secret'`) or pass it to
|
|
3000
|
+
* {@link Dream.setAttributes | setAttributes}, each of which encrypts it.
|
|
3001
|
+
*
|
|
2906
3002
|
* ```ts
|
|
2907
3003
|
* const user = new User()
|
|
2908
3004
|
* user.setAttribute('email', 'sally@gmail.com')
|
|
2909
3005
|
* ```
|
|
2910
3006
|
*/
|
|
2911
3007
|
setAttribute(column, val) {
|
|
2912
|
-
;
|
|
3008
|
+
const dreamClass = this.constructor;
|
|
3009
|
+
const encryptedAttribute = dreamClass.encryptedAttributes.find(({ encryptedColumnName }) => encryptedColumnName === column);
|
|
3010
|
+
if (encryptedAttribute &&
|
|
3011
|
+
val !== null &&
|
|
3012
|
+
!(typeof val === 'string' && InternalEncrypt.isDecryptableColumnCiphertext(val)))
|
|
3013
|
+
throw new DoNotSetEncryptedFieldsDirectly(dreamClass, column, encryptedAttribute.property);
|
|
3014
|
+
this.setAttributeUnchecked(column, val);
|
|
3015
|
+
}
|
|
3016
|
+
/**
|
|
3017
|
+
* @internal
|
|
3018
|
+
*
|
|
3019
|
+
* Writes the value into the current attributes as given, with none of
|
|
3020
|
+
* setAttribute's checks. Only for values Dream already trusts: a row read
|
|
3021
|
+
* from the database, and the ciphertext the @Encrypted property setter
|
|
3022
|
+
* has just produced. Every caller-supplied value goes through setAttribute.
|
|
3023
|
+
*/
|
|
3024
|
+
setAttributeUnchecked(column, val) {
|
|
2913
3025
|
this.currentAttributes[column] = val;
|
|
2914
3026
|
}
|
|
2915
3027
|
/**
|
|
@@ -2977,43 +3089,55 @@ export default class Dream {
|
|
|
2977
3089
|
* changed since last persisting, along with their current
|
|
2978
3090
|
* and previously persisted values.
|
|
2979
3091
|
*
|
|
3092
|
+
* For a column with an unsaved change, `was` is its value
|
|
3093
|
+
* as of the most recent save. For every other column, `was`
|
|
3094
|
+
* is its value from before that save, so the changes the
|
|
3095
|
+
* save made keep appearing, beside any later unsaved change,
|
|
3096
|
+
* until the next save.
|
|
3097
|
+
*
|
|
3098
|
+
* On a create, that earlier value is `undefined` for every
|
|
3099
|
+
* column of an instance from `new` or `create`, assigned or
|
|
3100
|
+
* not, and the copied value for an instance from
|
|
3101
|
+
* {@link Dream.dup | dup}. So after a create from `new` or
|
|
3102
|
+
* `create`, every column the insert returned counts as
|
|
3103
|
+
* changed: the primary key, timestamps, database defaults,
|
|
3104
|
+
* and `null` for each nullable column nobody assigned. After
|
|
3105
|
+
* a create from `dup`, the columns `dup` cleared (the primary
|
|
3106
|
+
* key, timestamps and sortable positions) count, and a copied
|
|
3107
|
+
* column counts only if the insert returned a different value.
|
|
3108
|
+
*
|
|
2980
3109
|
* ```ts
|
|
2981
3110
|
* const pet = Pet.new({ species: 'dog' })
|
|
2982
3111
|
* pet.changes()
|
|
2983
3112
|
* // {
|
|
2984
|
-
* // species: {
|
|
2985
|
-
* // was: undefined,
|
|
2986
|
-
* // now: 'dog',
|
|
2987
|
-
* // }
|
|
3113
|
+
* // species: { was: undefined, now: 'dog' },
|
|
2988
3114
|
* // }
|
|
2989
3115
|
*
|
|
2990
3116
|
* await pet.save()
|
|
2991
3117
|
* pet.changes()
|
|
2992
3118
|
* // {
|
|
2993
|
-
* //
|
|
2994
|
-
* //
|
|
2995
|
-
* //
|
|
2996
|
-
* //
|
|
3119
|
+
* // id: { was: undefined, now: 1 },
|
|
3120
|
+
* // species: { was: undefined, now: 'dog' },
|
|
3121
|
+
* // name: { was: undefined, now: null },
|
|
3122
|
+
* // ...every other column the insert returned
|
|
2997
3123
|
* // }
|
|
2998
3124
|
*
|
|
2999
3125
|
* pet.species = 'cat'
|
|
3000
3126
|
* pet.species = 'frog'
|
|
3001
3127
|
* pet.changes()
|
|
3002
3128
|
* // {
|
|
3003
|
-
* //
|
|
3004
|
-
* //
|
|
3005
|
-
* //
|
|
3006
|
-
* //
|
|
3129
|
+
* // id: { was: undefined, now: 1 },
|
|
3130
|
+
* // species: { was: 'dog', now: 'frog' },
|
|
3131
|
+
* // name: { was: undefined, now: null },
|
|
3132
|
+
* // ...every other change the create made
|
|
3007
3133
|
* // }
|
|
3008
3134
|
*
|
|
3009
3135
|
* await pet.save()
|
|
3010
3136
|
* pet.changes()
|
|
3011
3137
|
* // {
|
|
3012
|
-
* // species: {
|
|
3013
|
-
* // was: 'dog',
|
|
3014
|
-
* // now: 'frog',
|
|
3015
|
-
* // }
|
|
3138
|
+
* // species: { was: 'dog', now: 'frog' },
|
|
3016
3139
|
* // }
|
|
3140
|
+
* // (and `updatedAt`, on a model with that column)
|
|
3017
3141
|
* ```
|
|
3018
3142
|
*
|
|
3019
3143
|
* @returns An object containing changed attributes
|
|
@@ -3029,8 +3153,22 @@ export default class Dream {
|
|
|
3029
3153
|
return obj;
|
|
3030
3154
|
}
|
|
3031
3155
|
/**
|
|
3032
|
-
* Returns the value
|
|
3033
|
-
*
|
|
3156
|
+
* Returns the value the column held before the most
|
|
3157
|
+
* recent save. For a column with an unsaved change, it
|
|
3158
|
+
* returns the column's value as of that save instead.
|
|
3159
|
+
*
|
|
3160
|
+
* On a create, the value before the save is `undefined`
|
|
3161
|
+
* for every column of an instance from `new` or `create`,
|
|
3162
|
+
* assigned or not, and the copied value for an instance
|
|
3163
|
+
* from {@link Dream.dup | dup} (`undefined` for the
|
|
3164
|
+
* columns `dup` cleared). So after a create from `new` or
|
|
3165
|
+
* `create`, this returns `undefined` for every column
|
|
3166
|
+
* without an unsaved change, not the value the insert
|
|
3167
|
+
* wrote. After an update, it returns the column's value
|
|
3168
|
+
* from before that update, which for a column the update
|
|
3169
|
+
* did not change is its current value. On a record loaded
|
|
3170
|
+
* from the database and not saved since, it returns the
|
|
3171
|
+
* loaded value.
|
|
3034
3172
|
*
|
|
3035
3173
|
* ```ts
|
|
3036
3174
|
* const pet = Pet.new({ species: 'cat' })
|
|
@@ -3065,16 +3203,46 @@ export default class Dream {
|
|
|
3065
3203
|
/**
|
|
3066
3204
|
* Returns true if the columnName provided has
|
|
3067
3205
|
* changes that were persisted during the most
|
|
3068
|
-
* recent save.
|
|
3206
|
+
* recent save. An unsaved change does not count,
|
|
3207
|
+
* and an unsaved edit made after that save does
|
|
3208
|
+
* not undo a change the save made. On a record
|
|
3209
|
+
* that has not been persisted, this returns false.
|
|
3210
|
+
*
|
|
3211
|
+
* A create compares the value the insert returned
|
|
3212
|
+
* with `undefined` for every column of an instance
|
|
3213
|
+
* from `new` or `create`, assigned or not, and with
|
|
3214
|
+
* the copied value for an instance from
|
|
3215
|
+
* {@link Dream.dup | dup}. So after a create from
|
|
3216
|
+
* `new` or `create`, this returns true for every
|
|
3217
|
+
* column the insert returned: the primary key,
|
|
3218
|
+
* timestamps, database defaults, and a nullable
|
|
3219
|
+
* column nobody assigned, which comes back `null`.
|
|
3220
|
+
* To react only to the columns a create assigned,
|
|
3221
|
+
* use the `ifChanged` option of an after hook.
|
|
3222
|
+
*
|
|
3223
|
+
* ```ts
|
|
3224
|
+
* const pet = await Pet.create({ species: 'cat' })
|
|
3225
|
+
* pet.savedChangeToAttribute('species')
|
|
3226
|
+
* // true
|
|
3227
|
+
* pet.savedChangeToAttribute('name')
|
|
3228
|
+
* // true (unassigned, so the insert returned null)
|
|
3229
|
+
*
|
|
3230
|
+
* await pet.update({ name: 'Snoopy' })
|
|
3231
|
+
* pet.savedChangeToAttribute('species')
|
|
3232
|
+
* // false
|
|
3233
|
+
*
|
|
3234
|
+
* pet.species = 'dog'
|
|
3235
|
+
* pet.savedChangeToAttribute('species')
|
|
3236
|
+
* // false (not saved yet)
|
|
3237
|
+
* ```
|
|
3069
3238
|
*
|
|
3070
3239
|
* @param columnName - the column name to check
|
|
3071
3240
|
* @returns A boolean
|
|
3072
3241
|
*/
|
|
3073
3242
|
savedChangeToAttribute(columnName) {
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
return this.isPersisted && notEqual(now, was);
|
|
3243
|
+
if (!this.isPersisted)
|
|
3244
|
+
return false;
|
|
3245
|
+
return notEqual(this.attributesFromBeforeLastSave[columnName], this.frozenAttributes[columnName]);
|
|
3078
3246
|
}
|
|
3079
3247
|
/**
|
|
3080
3248
|
* Returns true if the columnName provided has
|
|
@@ -3306,7 +3474,7 @@ export default class Dream {
|
|
|
3306
3474
|
* deleting their corresponding records within the database.
|
|
3307
3475
|
*
|
|
3308
3476
|
* ```ts
|
|
3309
|
-
* await user.destroyAssociation('posts', { body: 'hello world' })
|
|
3477
|
+
* await user.destroyAssociation('posts', { and: { body: 'hello world' } })
|
|
3310
3478
|
* ```
|
|
3311
3479
|
*
|
|
3312
3480
|
* @param associationName - The name of the association to destroy
|
|
@@ -3321,6 +3489,7 @@ export default class Dream {
|
|
|
3321
3489
|
* @returns The number of records deleted
|
|
3322
3490
|
*/
|
|
3323
3491
|
async destroyAssociation(associationName, options) {
|
|
3492
|
+
assertRecognizedAssociationConditionKeys(options, DESTROY_ASSOCIATION_OPTION_KEYS, associationName, 'destroyAssociation');
|
|
3324
3493
|
if (this.isNewRecord)
|
|
3325
3494
|
throw new CannotDestroyAssociationOnUnpersistedDream(this, associationName);
|
|
3326
3495
|
return await destroyAssociation(this, null, associationName, {
|
|
@@ -3342,7 +3511,7 @@ export default class Dream {
|
|
|
3342
3511
|
* causing those records to be deleted from the database.
|
|
3343
3512
|
*
|
|
3344
3513
|
* ```ts
|
|
3345
|
-
* await user.reallyDestroyAssociation('posts', { body: 'hello world' })
|
|
3514
|
+
* await user.reallyDestroyAssociation('posts', { and: { body: 'hello world' } })
|
|
3346
3515
|
* ```
|
|
3347
3516
|
*
|
|
3348
3517
|
* @param associationName - The name of the association to destroy
|
|
@@ -3357,6 +3526,7 @@ export default class Dream {
|
|
|
3357
3526
|
* @returns The number of records deleted
|
|
3358
3527
|
*/
|
|
3359
3528
|
async reallyDestroyAssociation(associationName, options) {
|
|
3529
|
+
assertRecognizedAssociationConditionKeys(options, DESTROY_ASSOCIATION_OPTION_KEYS, associationName, 'reallyDestroyAssociation');
|
|
3360
3530
|
if (this.isNewRecord)
|
|
3361
3531
|
throw new CannotDestroyAssociationOnUnpersistedDream(this, associationName);
|
|
3362
3532
|
return await destroyAssociation(this, null, associationName, {
|
|
@@ -3375,7 +3545,7 @@ export default class Dream {
|
|
|
3375
3545
|
* will also be undeleted.
|
|
3376
3546
|
*
|
|
3377
3547
|
* ```ts
|
|
3378
|
-
* await user.undestroyAssociation('posts', { body: 'hello world' })
|
|
3548
|
+
* await user.undestroyAssociation('posts', { and: { body: 'hello world' } })
|
|
3379
3549
|
* ```
|
|
3380
3550
|
*
|
|
3381
3551
|
* @param associationName - The name of the association to undestroy
|
|
@@ -3390,6 +3560,7 @@ export default class Dream {
|
|
|
3390
3560
|
* @returns The number of records undestroyed
|
|
3391
3561
|
*/
|
|
3392
3562
|
async undestroyAssociation(associationName, options) {
|
|
3563
|
+
assertRecognizedAssociationConditionKeys(options, DESTROY_ASSOCIATION_OPTION_KEYS, associationName, 'undestroyAssociation');
|
|
3393
3564
|
return await undestroyAssociation(this, null, associationName, {
|
|
3394
3565
|
...undestroyOptions(options),
|
|
3395
3566
|
joinAndStatements: {
|
|
@@ -3422,8 +3593,8 @@ export default class Dream {
|
|
|
3422
3593
|
}
|
|
3423
3594
|
/**
|
|
3424
3595
|
* Updates all records matching the association with
|
|
3425
|
-
* the provided attributes. If
|
|
3426
|
-
*
|
|
3596
|
+
* the provided attributes. If an and, andNot or andAny
|
|
3597
|
+
* statement is passed, it will be applied to the query
|
|
3427
3598
|
* before updating.
|
|
3428
3599
|
*
|
|
3429
3600
|
* ```ts
|
|
@@ -3435,14 +3606,17 @@ export default class Dream {
|
|
|
3435
3606
|
*
|
|
3436
3607
|
* @param associationName - The name of the association to update
|
|
3437
3608
|
* @param attributes - The attributes to update on the association
|
|
3438
|
-
* @param
|
|
3439
|
-
* @param
|
|
3440
|
-
* @param
|
|
3441
|
-
* @param
|
|
3442
|
-
* @param
|
|
3609
|
+
* @param updateAssociationOptions - Options for updating the association
|
|
3610
|
+
* @param updateAssociationOptions.and - Optional and statement to apply to query before updating
|
|
3611
|
+
* @param updateAssociationOptions.andNot - Optional andNot statement to apply to query before updating
|
|
3612
|
+
* @param updateAssociationOptions.andAny - Optional andAny statement to apply to query before updating
|
|
3613
|
+
* @param updateAssociationOptions.skipHooks - If true, skips applying model hooks during the update operation. Defaults to false
|
|
3614
|
+
* @param updateAssociationOptions.bypassAllDefaultScopes - If true, bypasses user-removable default scopes when updating the association; an STI child's reserved discriminator remains enforced, including when associations are loaded. Defaults to false
|
|
3615
|
+
* @param updateAssociationOptions.defaultScopesToBypass - An array of default scope names to bypass when updating the association. Defaults to an empty array
|
|
3443
3616
|
* @returns The number of updated records
|
|
3444
3617
|
*/
|
|
3445
3618
|
async updateAssociation(associationName, attributes, updateAssociationOptions) {
|
|
3619
|
+
assertRecognizedAssociationConditionKeys(updateAssociationOptions, UPDATE_ASSOCIATION_OPTION_KEYS, associationName, 'updateAssociation');
|
|
3446
3620
|
if (this.isNewRecord)
|
|
3447
3621
|
throw new CannotUpdateAssociationOnUnpersistedDream(this, associationName);
|
|
3448
3622
|
return associationUpdateQuery(this, null, associationName, {
|
|
@@ -3508,19 +3682,19 @@ export default class Dream {
|
|
|
3508
3682
|
* if the association is not already loaded.
|
|
3509
3683
|
*
|
|
3510
3684
|
* ```ts
|
|
3511
|
-
* const
|
|
3512
|
-
* .load('posts', { body: ops.ilike('%hello world%') }, 'comments', 'replies')
|
|
3685
|
+
* const loadedUser = await user
|
|
3686
|
+
* .load('posts', { and: { body: ops.ilike('%hello world%') } }, 'comments', 'replies')
|
|
3513
3687
|
* .load('images')
|
|
3514
3688
|
* .execute()
|
|
3515
3689
|
*
|
|
3516
|
-
*
|
|
3690
|
+
* loadedUser.posts[0].comments[0].replies[0]
|
|
3517
3691
|
* // Reply{}
|
|
3518
3692
|
*
|
|
3519
|
-
*
|
|
3693
|
+
* loadedUser.images[0]
|
|
3520
3694
|
* // Image{}
|
|
3521
3695
|
* ```
|
|
3522
3696
|
*
|
|
3523
|
-
* @param args - A
|
|
3697
|
+
* @param args - A chain of association names and and/andNot/andAny clauses
|
|
3524
3698
|
* @returns A chainable LoadBuilder instance. Call `.execute()` to get the cloned model with associations loaded.
|
|
3525
3699
|
*/
|
|
3526
3700
|
load(...args) {
|
|
@@ -3602,19 +3776,19 @@ export default class Dream {
|
|
|
3602
3776
|
* 5. associations loading associations loading associations could result in exponential amounts of data; in those cases, `.load(...).findEach(...)` avoids instantiating massive amounts of data at once
|
|
3603
3777
|
*
|
|
3604
3778
|
* ```ts
|
|
3605
|
-
* const
|
|
3606
|
-
* .leftJoinLoad('posts', { body: ops.ilike('%hello world%') }, 'comments', 'replies')
|
|
3779
|
+
* const loadedUser = await user
|
|
3780
|
+
* .leftJoinLoad('posts', { and: { body: ops.ilike('%hello world%') } }, 'comments', 'replies')
|
|
3607
3781
|
* .leftJoinLoad('images')
|
|
3608
3782
|
* .execute()
|
|
3609
3783
|
*
|
|
3610
|
-
*
|
|
3784
|
+
* loadedUser.posts[0].comments[0].replies[0]
|
|
3611
3785
|
* // Reply{}
|
|
3612
3786
|
*
|
|
3613
|
-
*
|
|
3787
|
+
* loadedUser.images[0]
|
|
3614
3788
|
* // Image{}
|
|
3615
3789
|
* ```
|
|
3616
3790
|
*
|
|
3617
|
-
* @param args - A
|
|
3791
|
+
* @param args - A chain of association names and and/andNot/andAny clauses
|
|
3618
3792
|
* @returns A chainable LeftJoinLoadBuilder instance. Call `.execute()` to get the cloned model with associations loaded.
|
|
3619
3793
|
*/
|
|
3620
3794
|
leftJoinLoad(...args) {
|
|
@@ -3681,6 +3855,13 @@ export default class Dream {
|
|
|
3681
3855
|
* Takes the attributes passed in and sets their values internally,
|
|
3682
3856
|
* bypassing any custom setters defined for these attributes.
|
|
3683
3857
|
*
|
|
3858
|
+
* An encrypted property (e.g. `secret`) is still encrypted. Its
|
|
3859
|
+
* `@Encrypted` backing column (e.g. `encryptedSecret`) accepts only
|
|
3860
|
+
* ciphertext that decrypts with this app's column encryption keys, or
|
|
3861
|
+
* `null`; anything else throws (see {@link Dream.setAttribute | setAttribute}).
|
|
3862
|
+
* Attributes are set in order, so those before the rejected one have
|
|
3863
|
+
* already been set when it throws.
|
|
3864
|
+
*
|
|
3684
3865
|
* NOTE:
|
|
3685
3866
|
* To leverage custom-defined setters, use `#assignAttributes` instead.
|
|
3686
3867
|
*
|
|
@@ -3711,6 +3892,10 @@ export default class Dream {
|
|
|
3711
3892
|
* Upon updating an instance, the update timestamp (if defined)
|
|
3712
3893
|
* will be updated on the model.
|
|
3713
3894
|
*
|
|
3895
|
+
* A column whose value is `undefined` is skipped, not written: a new
|
|
3896
|
+
* record gets the column's database default, and a persisted record
|
|
3897
|
+
* keeps the value stored for it. Set a column to `null` to clear it.
|
|
3898
|
+
*
|
|
3714
3899
|
* ```ts
|
|
3715
3900
|
* const user = User.new({ email: 'how@yadoin' })
|
|
3716
3901
|
* user.name = 'fred'
|
|
@@ -3758,6 +3943,12 @@ export default class Dream {
|
|
|
3758
3943
|
* See {@link Dream.save | save} for details on
|
|
3759
3944
|
* the side effects of saving.
|
|
3760
3945
|
*
|
|
3946
|
+
* A column whose value is still `undefined` after setters run is
|
|
3947
|
+
* skipped, not written (see {@link Dream.save | save}), with two
|
|
3948
|
+
* `@Encrypted` exceptions: `undefined` for an encrypted property (e.g.
|
|
3949
|
+
* `secret`) is encrypted to `null`, so it clears the column, and
|
|
3950
|
+
* `undefined` for its backing column (e.g. `encryptedSecret`) throws.
|
|
3951
|
+
*
|
|
3761
3952
|
* NOTE:
|
|
3762
3953
|
* To bypass custom-defined setters, use {@link Dream.updateAttributes | updateAttributes} instead.
|
|
3763
3954
|
*
|
|
@@ -3785,6 +3976,18 @@ export default class Dream {
|
|
|
3785
3976
|
* See {@link Dream.save | save} for details on
|
|
3786
3977
|
* the side effects of saving.
|
|
3787
3978
|
*
|
|
3979
|
+
* An encrypted property (e.g. `secret`) is still encrypted. Its
|
|
3980
|
+
* `@Encrypted` backing column (e.g. `encryptedSecret`) accepts only
|
|
3981
|
+
* ciphertext that decrypts with this app's column encryption keys, or
|
|
3982
|
+
* `null`; anything else throws before anything is saved (see
|
|
3983
|
+
* {@link Dream.setAttribute | setAttribute}).
|
|
3984
|
+
*
|
|
3985
|
+
* A column whose value is still `undefined` after setters run is
|
|
3986
|
+
* skipped, not written (see {@link Dream.save | save}), with two
|
|
3987
|
+
* `@Encrypted` exceptions: `undefined` for an encrypted property (e.g.
|
|
3988
|
+
* `secret`) is encrypted to `null`, so it clears the column, and
|
|
3989
|
+
* `undefined` for its backing column (e.g. `encryptedSecret`) throws.
|
|
3990
|
+
*
|
|
3788
3991
|
* NOTE:
|
|
3789
3992
|
* To update the values without bypassing any custom-defined
|
|
3790
3993
|
* setters, use {@link Dream.update | update} instead.
|
|
@@ -4,6 +4,7 @@ import Query from '../dream/Query.js';
|
|
|
4
4
|
import DBClassDeprecation from '../helpers/cli/DBClassDeprecation.js';
|
|
5
5
|
import generateDream from '../helpers/cli/generateDream.js';
|
|
6
6
|
import EnvInternal from '../helpers/EnvInternal.js';
|
|
7
|
+
import standardizeFullyQualifiedModelName from '../helpers/standardizeFullyQualifiedModelName.js';
|
|
7
8
|
export default class DreamBin {
|
|
8
9
|
static async sync(onSync, options) {
|
|
9
10
|
if (!EnvInternal.isTest) {
|
|
@@ -58,6 +59,7 @@ export default class DreamBin {
|
|
|
58
59
|
});
|
|
59
60
|
}
|
|
60
61
|
static async generateStiChild(fullyQualifiedModelName, fullyQualifiedParentName, columnsWithTypes, options) {
|
|
62
|
+
const connectionName = options.connectionName ?? stiParentConnectionName(fullyQualifiedModelName, fullyQualifiedParentName);
|
|
61
63
|
await generateDream({
|
|
62
64
|
fullyQualifiedModelName,
|
|
63
65
|
columnsWithTypes,
|
|
@@ -65,6 +67,7 @@ export default class DreamBin {
|
|
|
65
67
|
includeAdminSerializers: options.adminSerializers ?? false,
|
|
66
68
|
includeInternalSerializers: options.internalSerializers ?? false,
|
|
67
69
|
...options,
|
|
70
|
+
connectionName,
|
|
68
71
|
stiBaseSerializer: false,
|
|
69
72
|
// `@SoftDelete()` is incompatible with STI children — never auto-apply.
|
|
70
73
|
softDelete: false,
|
|
@@ -95,3 +98,13 @@ export default class DreamBin {
|
|
|
95
98
|
DreamCLI.logger.logEndProgress();
|
|
96
99
|
}
|
|
97
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* An STI child shares its parent's table, so its migration belongs to the
|
|
103
|
+
* parent model's connection.
|
|
104
|
+
*/
|
|
105
|
+
function stiParentConnectionName(fullyQualifiedModelName, fullyQualifiedParentName) {
|
|
106
|
+
const parentModel = DreamApp.getOrFail().models[standardizeFullyQualifiedModelName(fullyQualifiedParentName)];
|
|
107
|
+
if (!parentModel)
|
|
108
|
+
throw new Error(`Cannot generate the STI child ${fullyQualifiedModelName}: its parent, ${fullyQualifiedParentName}, is not one of the app models. The parent must already exist, and its name must match its path under the models directory (e.g. Health/Coach for Health/Coach.ts).`);
|
|
109
|
+
return parentModel.prototype.connectionName;
|
|
110
|
+
}
|