@rvoh/dream 2.35.0 → 2.36.1
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 +260 -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 +35 -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 +77 -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 +260 -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 +35 -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 +77 -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 +208 -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 +29 -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/dist/esm/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,49 @@ 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
|
+
* writes (freshly encrypted, or the ciphertext as loaded or last saved,
|
|
3023
|
+
* which it keeps when that already holds the assigned plaintext). Every
|
|
3024
|
+
* caller-supplied value goes through setAttribute.
|
|
3025
|
+
*/
|
|
3026
|
+
setAttributeUnchecked(column, val) {
|
|
2913
3027
|
this.currentAttributes[column] = val;
|
|
2914
3028
|
}
|
|
2915
3029
|
/**
|
|
@@ -2977,43 +3091,55 @@ export default class Dream {
|
|
|
2977
3091
|
* changed since last persisting, along with their current
|
|
2978
3092
|
* and previously persisted values.
|
|
2979
3093
|
*
|
|
3094
|
+
* For a column with an unsaved change, `was` is its value
|
|
3095
|
+
* as of the most recent save. For every other column, `was`
|
|
3096
|
+
* is its value from before that save, so the changes the
|
|
3097
|
+
* save made keep appearing, beside any later unsaved change,
|
|
3098
|
+
* until the next save.
|
|
3099
|
+
*
|
|
3100
|
+
* On a create, that earlier value is `undefined` for every
|
|
3101
|
+
* column of an instance from `new` or `create`, assigned or
|
|
3102
|
+
* not, and the copied value for an instance from
|
|
3103
|
+
* {@link Dream.dup | dup}. So after a create from `new` or
|
|
3104
|
+
* `create`, every column the insert returned counts as
|
|
3105
|
+
* changed: the primary key, timestamps, database defaults,
|
|
3106
|
+
* and `null` for each nullable column nobody assigned. After
|
|
3107
|
+
* a create from `dup`, the columns `dup` cleared (the primary
|
|
3108
|
+
* key, timestamps and sortable positions) count, and a copied
|
|
3109
|
+
* column counts only if the insert returned a different value.
|
|
3110
|
+
*
|
|
2980
3111
|
* ```ts
|
|
2981
3112
|
* const pet = Pet.new({ species: 'dog' })
|
|
2982
3113
|
* pet.changes()
|
|
2983
3114
|
* // {
|
|
2984
|
-
* // species: {
|
|
2985
|
-
* // was: undefined,
|
|
2986
|
-
* // now: 'dog',
|
|
2987
|
-
* // }
|
|
3115
|
+
* // species: { was: undefined, now: 'dog' },
|
|
2988
3116
|
* // }
|
|
2989
3117
|
*
|
|
2990
3118
|
* await pet.save()
|
|
2991
3119
|
* pet.changes()
|
|
2992
3120
|
* // {
|
|
2993
|
-
* //
|
|
2994
|
-
* //
|
|
2995
|
-
* //
|
|
2996
|
-
* //
|
|
3121
|
+
* // id: { was: undefined, now: 1 },
|
|
3122
|
+
* // species: { was: undefined, now: 'dog' },
|
|
3123
|
+
* // name: { was: undefined, now: null },
|
|
3124
|
+
* // ...every other column the insert returned
|
|
2997
3125
|
* // }
|
|
2998
3126
|
*
|
|
2999
3127
|
* pet.species = 'cat'
|
|
3000
3128
|
* pet.species = 'frog'
|
|
3001
3129
|
* pet.changes()
|
|
3002
3130
|
* // {
|
|
3003
|
-
* //
|
|
3004
|
-
* //
|
|
3005
|
-
* //
|
|
3006
|
-
* //
|
|
3131
|
+
* // id: { was: undefined, now: 1 },
|
|
3132
|
+
* // species: { was: 'dog', now: 'frog' },
|
|
3133
|
+
* // name: { was: undefined, now: null },
|
|
3134
|
+
* // ...every other change the create made
|
|
3007
3135
|
* // }
|
|
3008
3136
|
*
|
|
3009
3137
|
* await pet.save()
|
|
3010
3138
|
* pet.changes()
|
|
3011
3139
|
* // {
|
|
3012
|
-
* // species: {
|
|
3013
|
-
* // was: 'dog',
|
|
3014
|
-
* // now: 'frog',
|
|
3015
|
-
* // }
|
|
3140
|
+
* // species: { was: 'dog', now: 'frog' },
|
|
3016
3141
|
* // }
|
|
3142
|
+
* // (and `updatedAt`, on a model with that column)
|
|
3017
3143
|
* ```
|
|
3018
3144
|
*
|
|
3019
3145
|
* @returns An object containing changed attributes
|
|
@@ -3029,8 +3155,22 @@ export default class Dream {
|
|
|
3029
3155
|
return obj;
|
|
3030
3156
|
}
|
|
3031
3157
|
/**
|
|
3032
|
-
* Returns the value
|
|
3033
|
-
*
|
|
3158
|
+
* Returns the value the column held before the most
|
|
3159
|
+
* recent save. For a column with an unsaved change, it
|
|
3160
|
+
* returns the column's value as of that save instead.
|
|
3161
|
+
*
|
|
3162
|
+
* On a create, the value before the save is `undefined`
|
|
3163
|
+
* for every column of an instance from `new` or `create`,
|
|
3164
|
+
* assigned or not, and the copied value for an instance
|
|
3165
|
+
* from {@link Dream.dup | dup} (`undefined` for the
|
|
3166
|
+
* columns `dup` cleared). So after a create from `new` or
|
|
3167
|
+
* `create`, this returns `undefined` for every column
|
|
3168
|
+
* without an unsaved change, not the value the insert
|
|
3169
|
+
* wrote. After an update, it returns the column's value
|
|
3170
|
+
* from before that update, which for a column the update
|
|
3171
|
+
* did not change is its current value. On a record loaded
|
|
3172
|
+
* from the database and not saved since, it returns the
|
|
3173
|
+
* loaded value.
|
|
3034
3174
|
*
|
|
3035
3175
|
* ```ts
|
|
3036
3176
|
* const pet = Pet.new({ species: 'cat' })
|
|
@@ -3065,16 +3205,46 @@ export default class Dream {
|
|
|
3065
3205
|
/**
|
|
3066
3206
|
* Returns true if the columnName provided has
|
|
3067
3207
|
* changes that were persisted during the most
|
|
3068
|
-
* recent save.
|
|
3208
|
+
* recent save. An unsaved change does not count,
|
|
3209
|
+
* and an unsaved edit made after that save does
|
|
3210
|
+
* not undo a change the save made. On a record
|
|
3211
|
+
* that has not been persisted, this returns false.
|
|
3212
|
+
*
|
|
3213
|
+
* A create compares the value the insert returned
|
|
3214
|
+
* with `undefined` for every column of an instance
|
|
3215
|
+
* from `new` or `create`, assigned or not, and with
|
|
3216
|
+
* the copied value for an instance from
|
|
3217
|
+
* {@link Dream.dup | dup}. So after a create from
|
|
3218
|
+
* `new` or `create`, this returns true for every
|
|
3219
|
+
* column the insert returned: the primary key,
|
|
3220
|
+
* timestamps, database defaults, and a nullable
|
|
3221
|
+
* column nobody assigned, which comes back `null`.
|
|
3222
|
+
* To react only to the columns a create assigned,
|
|
3223
|
+
* use the `ifChanged` option of an after hook.
|
|
3224
|
+
*
|
|
3225
|
+
* ```ts
|
|
3226
|
+
* const pet = await Pet.create({ species: 'cat' })
|
|
3227
|
+
* pet.savedChangeToAttribute('species')
|
|
3228
|
+
* // true
|
|
3229
|
+
* pet.savedChangeToAttribute('name')
|
|
3230
|
+
* // true (unassigned, so the insert returned null)
|
|
3231
|
+
*
|
|
3232
|
+
* await pet.update({ name: 'Snoopy' })
|
|
3233
|
+
* pet.savedChangeToAttribute('species')
|
|
3234
|
+
* // false
|
|
3235
|
+
*
|
|
3236
|
+
* pet.species = 'dog'
|
|
3237
|
+
* pet.savedChangeToAttribute('species')
|
|
3238
|
+
* // false (not saved yet)
|
|
3239
|
+
* ```
|
|
3069
3240
|
*
|
|
3070
3241
|
* @param columnName - the column name to check
|
|
3071
3242
|
* @returns A boolean
|
|
3072
3243
|
*/
|
|
3073
3244
|
savedChangeToAttribute(columnName) {
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
return this.isPersisted && notEqual(now, was);
|
|
3245
|
+
if (!this.isPersisted)
|
|
3246
|
+
return false;
|
|
3247
|
+
return notEqual(this.attributesFromBeforeLastSave[columnName], this.frozenAttributes[columnName]);
|
|
3078
3248
|
}
|
|
3079
3249
|
/**
|
|
3080
3250
|
* Returns true if the columnName provided has
|
|
@@ -3306,7 +3476,7 @@ export default class Dream {
|
|
|
3306
3476
|
* deleting their corresponding records within the database.
|
|
3307
3477
|
*
|
|
3308
3478
|
* ```ts
|
|
3309
|
-
* await user.destroyAssociation('posts', { body: 'hello world' })
|
|
3479
|
+
* await user.destroyAssociation('posts', { and: { body: 'hello world' } })
|
|
3310
3480
|
* ```
|
|
3311
3481
|
*
|
|
3312
3482
|
* @param associationName - The name of the association to destroy
|
|
@@ -3321,6 +3491,7 @@ export default class Dream {
|
|
|
3321
3491
|
* @returns The number of records deleted
|
|
3322
3492
|
*/
|
|
3323
3493
|
async destroyAssociation(associationName, options) {
|
|
3494
|
+
assertRecognizedAssociationConditionKeys(options, DESTROY_ASSOCIATION_OPTION_KEYS, associationName, 'destroyAssociation');
|
|
3324
3495
|
if (this.isNewRecord)
|
|
3325
3496
|
throw new CannotDestroyAssociationOnUnpersistedDream(this, associationName);
|
|
3326
3497
|
return await destroyAssociation(this, null, associationName, {
|
|
@@ -3342,7 +3513,7 @@ export default class Dream {
|
|
|
3342
3513
|
* causing those records to be deleted from the database.
|
|
3343
3514
|
*
|
|
3344
3515
|
* ```ts
|
|
3345
|
-
* await user.reallyDestroyAssociation('posts', { body: 'hello world' })
|
|
3516
|
+
* await user.reallyDestroyAssociation('posts', { and: { body: 'hello world' } })
|
|
3346
3517
|
* ```
|
|
3347
3518
|
*
|
|
3348
3519
|
* @param associationName - The name of the association to destroy
|
|
@@ -3357,6 +3528,7 @@ export default class Dream {
|
|
|
3357
3528
|
* @returns The number of records deleted
|
|
3358
3529
|
*/
|
|
3359
3530
|
async reallyDestroyAssociation(associationName, options) {
|
|
3531
|
+
assertRecognizedAssociationConditionKeys(options, DESTROY_ASSOCIATION_OPTION_KEYS, associationName, 'reallyDestroyAssociation');
|
|
3360
3532
|
if (this.isNewRecord)
|
|
3361
3533
|
throw new CannotDestroyAssociationOnUnpersistedDream(this, associationName);
|
|
3362
3534
|
return await destroyAssociation(this, null, associationName, {
|
|
@@ -3375,7 +3547,7 @@ export default class Dream {
|
|
|
3375
3547
|
* will also be undeleted.
|
|
3376
3548
|
*
|
|
3377
3549
|
* ```ts
|
|
3378
|
-
* await user.undestroyAssociation('posts', { body: 'hello world' })
|
|
3550
|
+
* await user.undestroyAssociation('posts', { and: { body: 'hello world' } })
|
|
3379
3551
|
* ```
|
|
3380
3552
|
*
|
|
3381
3553
|
* @param associationName - The name of the association to undestroy
|
|
@@ -3390,6 +3562,7 @@ export default class Dream {
|
|
|
3390
3562
|
* @returns The number of records undestroyed
|
|
3391
3563
|
*/
|
|
3392
3564
|
async undestroyAssociation(associationName, options) {
|
|
3565
|
+
assertRecognizedAssociationConditionKeys(options, DESTROY_ASSOCIATION_OPTION_KEYS, associationName, 'undestroyAssociation');
|
|
3393
3566
|
return await undestroyAssociation(this, null, associationName, {
|
|
3394
3567
|
...undestroyOptions(options),
|
|
3395
3568
|
joinAndStatements: {
|
|
@@ -3422,8 +3595,8 @@ export default class Dream {
|
|
|
3422
3595
|
}
|
|
3423
3596
|
/**
|
|
3424
3597
|
* Updates all records matching the association with
|
|
3425
|
-
* the provided attributes. If
|
|
3426
|
-
*
|
|
3598
|
+
* the provided attributes. If an and, andNot or andAny
|
|
3599
|
+
* statement is passed, it will be applied to the query
|
|
3427
3600
|
* before updating.
|
|
3428
3601
|
*
|
|
3429
3602
|
* ```ts
|
|
@@ -3435,14 +3608,17 @@ export default class Dream {
|
|
|
3435
3608
|
*
|
|
3436
3609
|
* @param associationName - The name of the association to update
|
|
3437
3610
|
* @param attributes - The attributes to update on the association
|
|
3438
|
-
* @param
|
|
3439
|
-
* @param
|
|
3440
|
-
* @param
|
|
3441
|
-
* @param
|
|
3442
|
-
* @param
|
|
3611
|
+
* @param updateAssociationOptions - Options for updating the association
|
|
3612
|
+
* @param updateAssociationOptions.and - Optional and statement to apply to query before updating
|
|
3613
|
+
* @param updateAssociationOptions.andNot - Optional andNot statement to apply to query before updating
|
|
3614
|
+
* @param updateAssociationOptions.andAny - Optional andAny statement to apply to query before updating
|
|
3615
|
+
* @param updateAssociationOptions.skipHooks - If true, skips applying model hooks during the update operation. Defaults to false
|
|
3616
|
+
* @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
|
|
3617
|
+
* @param updateAssociationOptions.defaultScopesToBypass - An array of default scope names to bypass when updating the association. Defaults to an empty array
|
|
3443
3618
|
* @returns The number of updated records
|
|
3444
3619
|
*/
|
|
3445
3620
|
async updateAssociation(associationName, attributes, updateAssociationOptions) {
|
|
3621
|
+
assertRecognizedAssociationConditionKeys(updateAssociationOptions, UPDATE_ASSOCIATION_OPTION_KEYS, associationName, 'updateAssociation');
|
|
3446
3622
|
if (this.isNewRecord)
|
|
3447
3623
|
throw new CannotUpdateAssociationOnUnpersistedDream(this, associationName);
|
|
3448
3624
|
return associationUpdateQuery(this, null, associationName, {
|
|
@@ -3508,19 +3684,19 @@ export default class Dream {
|
|
|
3508
3684
|
* if the association is not already loaded.
|
|
3509
3685
|
*
|
|
3510
3686
|
* ```ts
|
|
3511
|
-
* const
|
|
3512
|
-
* .load('posts', { body: ops.ilike('%hello world%') }, 'comments', 'replies')
|
|
3687
|
+
* const loadedUser = await user
|
|
3688
|
+
* .load('posts', { and: { body: ops.ilike('%hello world%') } }, 'comments', 'replies')
|
|
3513
3689
|
* .load('images')
|
|
3514
3690
|
* .execute()
|
|
3515
3691
|
*
|
|
3516
|
-
*
|
|
3692
|
+
* loadedUser.posts[0].comments[0].replies[0]
|
|
3517
3693
|
* // Reply{}
|
|
3518
3694
|
*
|
|
3519
|
-
*
|
|
3695
|
+
* loadedUser.images[0]
|
|
3520
3696
|
* // Image{}
|
|
3521
3697
|
* ```
|
|
3522
3698
|
*
|
|
3523
|
-
* @param args - A
|
|
3699
|
+
* @param args - A chain of association names and and/andNot/andAny clauses
|
|
3524
3700
|
* @returns A chainable LoadBuilder instance. Call `.execute()` to get the cloned model with associations loaded.
|
|
3525
3701
|
*/
|
|
3526
3702
|
load(...args) {
|
|
@@ -3602,19 +3778,19 @@ export default class Dream {
|
|
|
3602
3778
|
* 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
3779
|
*
|
|
3604
3780
|
* ```ts
|
|
3605
|
-
* const
|
|
3606
|
-
* .leftJoinLoad('posts', { body: ops.ilike('%hello world%') }, 'comments', 'replies')
|
|
3781
|
+
* const loadedUser = await user
|
|
3782
|
+
* .leftJoinLoad('posts', { and: { body: ops.ilike('%hello world%') } }, 'comments', 'replies')
|
|
3607
3783
|
* .leftJoinLoad('images')
|
|
3608
3784
|
* .execute()
|
|
3609
3785
|
*
|
|
3610
|
-
*
|
|
3786
|
+
* loadedUser.posts[0].comments[0].replies[0]
|
|
3611
3787
|
* // Reply{}
|
|
3612
3788
|
*
|
|
3613
|
-
*
|
|
3789
|
+
* loadedUser.images[0]
|
|
3614
3790
|
* // Image{}
|
|
3615
3791
|
* ```
|
|
3616
3792
|
*
|
|
3617
|
-
* @param args - A
|
|
3793
|
+
* @param args - A chain of association names and and/andNot/andAny clauses
|
|
3618
3794
|
* @returns A chainable LeftJoinLoadBuilder instance. Call `.execute()` to get the cloned model with associations loaded.
|
|
3619
3795
|
*/
|
|
3620
3796
|
leftJoinLoad(...args) {
|
|
@@ -3681,6 +3857,13 @@ export default class Dream {
|
|
|
3681
3857
|
* Takes the attributes passed in and sets their values internally,
|
|
3682
3858
|
* bypassing any custom setters defined for these attributes.
|
|
3683
3859
|
*
|
|
3860
|
+
* An encrypted property (e.g. `secret`) is still encrypted. Its
|
|
3861
|
+
* `@Encrypted` backing column (e.g. `encryptedSecret`) accepts only
|
|
3862
|
+
* ciphertext that decrypts with this app's column encryption keys, or
|
|
3863
|
+
* `null`; anything else throws (see {@link Dream.setAttribute | setAttribute}).
|
|
3864
|
+
* Attributes are set in order, so those before the rejected one have
|
|
3865
|
+
* already been set when it throws.
|
|
3866
|
+
*
|
|
3684
3867
|
* NOTE:
|
|
3685
3868
|
* To leverage custom-defined setters, use `#assignAttributes` instead.
|
|
3686
3869
|
*
|
|
@@ -3711,6 +3894,10 @@ export default class Dream {
|
|
|
3711
3894
|
* Upon updating an instance, the update timestamp (if defined)
|
|
3712
3895
|
* will be updated on the model.
|
|
3713
3896
|
*
|
|
3897
|
+
* A column whose value is `undefined` is skipped, not written: a new
|
|
3898
|
+
* record gets the column's database default, and a persisted record
|
|
3899
|
+
* keeps the value stored for it. Set a column to `null` to clear it.
|
|
3900
|
+
*
|
|
3714
3901
|
* ```ts
|
|
3715
3902
|
* const user = User.new({ email: 'how@yadoin' })
|
|
3716
3903
|
* user.name = 'fred'
|
|
@@ -3758,6 +3945,12 @@ export default class Dream {
|
|
|
3758
3945
|
* See {@link Dream.save | save} for details on
|
|
3759
3946
|
* the side effects of saving.
|
|
3760
3947
|
*
|
|
3948
|
+
* A column whose value is still `undefined` after setters run is
|
|
3949
|
+
* skipped, not written (see {@link Dream.save | save}), with two
|
|
3950
|
+
* `@Encrypted` exceptions: `undefined` for an encrypted property (e.g.
|
|
3951
|
+
* `secret`) is encrypted to `null`, so it clears the column, and
|
|
3952
|
+
* `undefined` for its backing column (e.g. `encryptedSecret`) throws.
|
|
3953
|
+
*
|
|
3761
3954
|
* NOTE:
|
|
3762
3955
|
* To bypass custom-defined setters, use {@link Dream.updateAttributes | updateAttributes} instead.
|
|
3763
3956
|
*
|
|
@@ -3785,6 +3978,18 @@ export default class Dream {
|
|
|
3785
3978
|
* See {@link Dream.save | save} for details on
|
|
3786
3979
|
* the side effects of saving.
|
|
3787
3980
|
*
|
|
3981
|
+
* An encrypted property (e.g. `secret`) is still encrypted. Its
|
|
3982
|
+
* `@Encrypted` backing column (e.g. `encryptedSecret`) accepts only
|
|
3983
|
+
* ciphertext that decrypts with this app's column encryption keys, or
|
|
3984
|
+
* `null`; anything else throws before anything is saved (see
|
|
3985
|
+
* {@link Dream.setAttribute | setAttribute}).
|
|
3986
|
+
*
|
|
3987
|
+
* A column whose value is still `undefined` after setters run is
|
|
3988
|
+
* skipped, not written (see {@link Dream.save | save}), with two
|
|
3989
|
+
* `@Encrypted` exceptions: `undefined` for an encrypted property (e.g.
|
|
3990
|
+
* `secret`) is encrypted to `null`, so it clears the column, and
|
|
3991
|
+
* `undefined` for its backing column (e.g. `encryptedSecret`) throws.
|
|
3992
|
+
*
|
|
3788
3993
|
* NOTE:
|
|
3789
3994
|
* To update the values without bypassing any custom-defined
|
|
3790
3995
|
* 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
|
+
}
|