@rvoh/dream 2.34.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 +270 -69
- 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 +270 -69
- 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 +217 -55
- 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 +281 -155
- 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
|
@@ -238,9 +238,43 @@ export default class DreamApp {
|
|
|
238
238
|
load<RT extends 'models' | 'serializers'>(resourceType: RT, resourcePath: string, importCb: (path: string) => Promise<any>): Promise<void>;
|
|
239
239
|
plugin(cb: (app: DreamApp) => void | Promise<void>): void;
|
|
240
240
|
set<ApplyOpt extends DreamAppSetOption>(applyOption: ApplyOpt, options: ApplyOpt extends 'bypassDeprecationChecks' ? boolean : ApplyOpt extends 'db' ? DreamDbCredentialOptions | string : ApplyOpt extends 'encryption' ? DreamAppEncryptionOptions : ApplyOpt extends 'primaryKeyType' ? LegacyCompatiblePrimaryKeyType : ApplyOpt extends 'importExtension' ? GeneratorImportStyle : ApplyOpt extends 'logger' ? DreamLogger : ApplyOpt extends 'projectRoot' ? string : ApplyOpt extends 'inflections' ? () => void | Promise<void> : ApplyOpt extends 'packageManager' ? DreamAppAllowedPackageManagersEnum : ApplyOpt extends 'paths' ? DreamDirectoryPaths : ApplyOpt extends 'parallelTests' ? number : ApplyOpt extends 'unicodeNormalization' ? UnicodeNormalizationForm : ApplyOpt extends 'paginationPageSize' ? number : ApplyOpt extends 'paginationMaxPageSize' ? number : ApplyOpt extends 'sortableScopeLockTimeout' ? number : never, secondaryOptions?: ApplyOpt extends 'db' ? DreamDbCredentialOptions : never): void;
|
|
241
|
-
|
|
241
|
+
/**
|
|
242
|
+
* Registers a callback for an app-level event. Callbacks for the same
|
|
243
|
+
* event run in the order they were registered.
|
|
244
|
+
*
|
|
245
|
+
* - `'db:log'`: called with a {@link KyselyLogEvent} each time a query on a
|
|
246
|
+
* Dream database connection completes or fails.
|
|
247
|
+
* - `'repl:start'`: called, and awaited, with the console's REPL context
|
|
248
|
+
* when the Dream console starts.
|
|
249
|
+
* - `'encryption:legacy-key-used'`: called with an
|
|
250
|
+
* {@link EncryptionLegacyKeyUsedEvent} when reading an `@deco.Encrypted`
|
|
251
|
+
* property needed the `legacy` column encryption key, not `current`, to
|
|
252
|
+
* open the stored value. Use it during a key rotation to find rows still
|
|
253
|
+
* encrypted with the old key. The property decrypts on every read, so a
|
|
254
|
+
* record whose value is still under the old key fires the event each time
|
|
255
|
+
* the property is read. A row nobody reads never fires it, so a quiet
|
|
256
|
+
* event does not prove that `legacy` is safe to drop;
|
|
257
|
+
* `Model.reencryptEncryptedColumns()` rewrites every row instead. Neither
|
|
258
|
+
* it nor the migration helpers fire the event. The callback runs
|
|
259
|
+
* synchronously inside the property read, and an error it throws
|
|
260
|
+
* propagates from that read. Reading the same property of the same
|
|
261
|
+
* record inside the callback returns its value without firing the event
|
|
262
|
+
* again, so a callback can read the property and assign the value back,
|
|
263
|
+
* which re-encrypts it under `current` for the record's next save. That
|
|
264
|
+
* holds only while the callback runs synchronously: a callback that
|
|
265
|
+
* reads the property after an `await` fires the event again from that
|
|
266
|
+
* read, so an `async` callback should read the property before its
|
|
267
|
+
* first `await`.
|
|
268
|
+
*
|
|
269
|
+
* ```ts
|
|
270
|
+
* dreamApp.on('encryption:legacy-key-used', ({ dream, property }) => {
|
|
271
|
+
* console.warn(`${dream.constructor.name} ${dream.primaryKeyValue()} ${property} still uses the legacy key`)
|
|
272
|
+
* })
|
|
273
|
+
* ```
|
|
274
|
+
*/
|
|
275
|
+
on<T extends DreamHookEventType>(hookEventType: T, cb: T extends 'db:log' ? (event: KyselyLogEvent) => void : T extends 'repl:start' ? (context: Context) => void | Promise<void> : T extends 'encryption:legacy-key-used' ? (event: EncryptionLegacyKeyUsedEvent) => void : never): void;
|
|
242
276
|
}
|
|
243
|
-
export type DreamHookEventType = 'db:log' | 'repl:start';
|
|
277
|
+
export type DreamHookEventType = 'db:log' | 'repl:start' | 'encryption:legacy-key-used';
|
|
244
278
|
export interface DreamAppOpts {
|
|
245
279
|
projectRoot: string;
|
|
246
280
|
primaryKeyType: LegacyCompatiblePrimaryKeyType;
|
|
@@ -373,6 +407,26 @@ interface SegmentedEncryptionOptions {
|
|
|
373
407
|
export interface DreamAppSpecialHooks {
|
|
374
408
|
dbLog: ((event: KyselyLogEvent) => void)[];
|
|
375
409
|
replStart: ((context: Context) => void | Promise<void>)[];
|
|
410
|
+
encryptionLegacyKeyUsed: ((event: EncryptionLegacyKeyUsedEvent) => void)[];
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Passed to `dreamApp.on('encryption:legacy-key-used', ...)` callbacks.
|
|
414
|
+
*/
|
|
415
|
+
export interface EncryptionLegacyKeyUsedEvent {
|
|
416
|
+
/**
|
|
417
|
+
* The record whose `@deco.Encrypted` property was read. Its class and
|
|
418
|
+
* `primaryKeyValue()` identify the row; a record not yet saved has no
|
|
419
|
+
* primary key.
|
|
420
|
+
*/
|
|
421
|
+
dream: Dream;
|
|
422
|
+
/**
|
|
423
|
+
* The `@deco.Encrypted` property that was read, e.g. `'phone'`.
|
|
424
|
+
*/
|
|
425
|
+
property: string;
|
|
426
|
+
/**
|
|
427
|
+
* The column holding the property's ciphertext, e.g. `'encryptedPhone'`.
|
|
428
|
+
*/
|
|
429
|
+
encryptedColumnName: string;
|
|
376
430
|
}
|
|
377
431
|
export interface DreamAppInitOptions {
|
|
378
432
|
bypassDreamIntegrityChecks?: boolean;
|
|
@@ -1,6 +1,25 @@
|
|
|
1
|
+
import { DecryptCallbacks } from './index.js';
|
|
1
2
|
export default class InternalEncrypt {
|
|
2
3
|
static encryptColumn(data: any): string | null;
|
|
3
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Decrypts `data` with the column keys, current then legacy.
|
|
6
|
+
* `onLegacyKeyUsed` is passed to `Encrypt.decrypt`, so it is called only
|
|
7
|
+
* when the legacy key opened the value.
|
|
8
|
+
*/
|
|
9
|
+
static decryptColumn(data: any, callbacks?: DecryptCallbacks): unknown;
|
|
10
|
+
/**
|
|
11
|
+
* Encrypts `data`, a column's ciphertext, again with the current column key
|
|
12
|
+
* (see `Encrypt.reencrypt`), opening it with the current key or, failing
|
|
13
|
+
* that, the legacy one. It does not call `onLegacyKeyUsed`.
|
|
14
|
+
*/
|
|
15
|
+
static reencryptColumn(data: any): string | null;
|
|
16
|
+
/**
|
|
17
|
+
* Whether `data` is ciphertext that the column keys (current, then legacy)
|
|
18
|
+
* open to a value `decryptColumn` returns. A missing column encryption
|
|
19
|
+
* config or key still throws, since that is a configuration error rather
|
|
20
|
+
* than a property of `data`.
|
|
21
|
+
*/
|
|
22
|
+
static isDecryptableColumnCiphertext(data: string): boolean;
|
|
4
23
|
private static doEncryption;
|
|
5
24
|
private static doDecryption;
|
|
6
25
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The column that holds an `@deco.Encrypted` property's ciphertext when the
|
|
3
|
+
* decorator is not given one: the property's name, pascalized and prefixed
|
|
4
|
+
* with `encrypted`. `secretPhone`, or `secret_phone`, gives
|
|
5
|
+
* `encryptedSecretPhone`, which is the `encrypted_secret_phone` column.
|
|
6
|
+
*/
|
|
7
|
+
export default function defaultEncryptedColumnName(propertyName: string): string;
|
|
@@ -19,12 +19,50 @@ export default class Encrypt {
|
|
|
19
19
|
*
|
|
20
20
|
* `MissingEncryptionKey` propagates from either form when a key is missing.
|
|
21
21
|
*
|
|
22
|
+
* **`onLegacyKeyUsed`** (optional fourth argument): during a key rotation,
|
|
23
|
+
* tells you which values still need the legacy key. It is called with no
|
|
24
|
+
* arguments, synchronously, before a three-arg call returns a value that
|
|
25
|
+
* the legacy key opened. It is not called when the current key opens the
|
|
26
|
+
* value, when decryption throws, or in the two-arg form. An error it
|
|
27
|
+
* throws propagates from `decrypt` as is.
|
|
28
|
+
*
|
|
29
|
+
* ```ts
|
|
30
|
+
* const value = Encrypt.decrypt<string>(
|
|
31
|
+
* encrypted,
|
|
32
|
+
* { algorithm: 'aes-256-gcm', key: newKey },
|
|
33
|
+
* { algorithm: 'aes-256-gcm', key: oldKey },
|
|
34
|
+
* { onLegacyKeyUsed: () => console.warn('this value is still encrypted with the old key') }
|
|
35
|
+
* )
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @throws MissingEncryptionKey
|
|
39
|
+
* @throws DecryptionError
|
|
40
|
+
* @throws DecryptionParseError
|
|
41
|
+
* @throws DecryptionRotationError
|
|
42
|
+
*/
|
|
43
|
+
static decrypt<RetType>(encrypted: string, { algorithm, key }: DecryptOptions, legacyOpts?: DecryptOptions, { onLegacyKeyUsed }?: DecryptCallbacks): RetType | null;
|
|
44
|
+
/**
|
|
45
|
+
* Opens `encrypted` as {@link Encrypt.decrypt} does, with `currentOpts` and,
|
|
46
|
+
* when they do not open it, `legacyOpts`, and encrypts the value again with
|
|
47
|
+
* `currentOpts`, so that reading it no longer needs the legacy key. Use it
|
|
48
|
+
* during a key rotation for a value stored outside an `@deco.Encrypted`
|
|
49
|
+
* column, such as a cookie; `Model.reencryptEncryptedColumns()` does this
|
|
50
|
+
* for every value of a model's encrypted properties.
|
|
51
|
+
*
|
|
52
|
+
* ```ts
|
|
53
|
+
* const rewritten = Encrypt.reencrypt(
|
|
54
|
+
* storedValue,
|
|
55
|
+
* { algorithm: 'aes-256-gcm', key: newKey },
|
|
56
|
+
* { algorithm: 'aes-256-gcm', key: oldKey }
|
|
57
|
+
* )
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
22
60
|
* @throws MissingEncryptionKey
|
|
23
61
|
* @throws DecryptionError
|
|
24
62
|
* @throws DecryptionParseError
|
|
25
63
|
* @throws DecryptionRotationError
|
|
26
64
|
*/
|
|
27
|
-
static
|
|
65
|
+
static reencrypt(encrypted: string, currentOpts: EncryptOptions, legacyOpts?: DecryptOptions): string;
|
|
28
66
|
private static attemptDecryptionWithLegacyKeys;
|
|
29
67
|
/**
|
|
30
68
|
* Generates a base64-encoded random key suitable for the given algorithm.
|
|
@@ -42,11 +80,26 @@ export default class Encrypt {
|
|
|
42
80
|
* to decrypt via `legacy` fallback.
|
|
43
81
|
* 4. For cookies, wait at least the cookie `maxAge` so all in-flight
|
|
44
82
|
* cookies have either expired or been re-issued under the new key. For
|
|
45
|
-
* `@Encrypted` columns, re-encrypt every existing row under the new
|
|
46
|
-
*
|
|
47
|
-
* `
|
|
83
|
+
* `@Encrypted` columns, re-encrypt every existing row under the new key
|
|
84
|
+
* by calling `reencryptEncryptedColumns()` on each model that has
|
|
85
|
+
* encrypted properties (`await User.reencryptEncryptedColumns()`), in
|
|
86
|
+
* each environment, from a console or a script rather than a migration.
|
|
87
|
+
* Run it only once every server in that environment runs with the new
|
|
88
|
+
* `current`: a server still on the old `current` keeps writing old-key
|
|
89
|
+
* values that its single pass over the table can miss. Writing old-key
|
|
90
|
+
* ciphertext straight into a backing column (`setAttribute`,
|
|
91
|
+
* `setAttributes` or `updateAttributes` given the backing column) after
|
|
92
|
+
* it has run also puts an old-key value back. For any other value
|
|
93
|
+
* encrypted with these keys, {@link Encrypt.reencrypt} rewrites it.
|
|
48
94
|
* 5. Drop `legacy` from config and deploy again.
|
|
49
95
|
*
|
|
96
|
+
* To see which values still need the old key while `legacy` is
|
|
97
|
+
* configured, register `dreamApp.on('encryption:legacy-key-used', ...)`,
|
|
98
|
+
* which fires each time reading an `@Encrypted` property needed `legacy`
|
|
99
|
+
* to open the value, or pass `onLegacyKeyUsed` to {@link Encrypt.decrypt}
|
|
100
|
+
* for values you decrypt yourself. A value nobody reads never fires
|
|
101
|
+
* either, so silence alone does not show that `legacy` is safe to drop.
|
|
102
|
+
*
|
|
50
103
|
* ## When to rotate
|
|
51
104
|
*
|
|
52
105
|
* - On a scheduled cadence (90–180 days is a reasonable policy default).
|
|
@@ -58,8 +111,15 @@ export default class Encrypt {
|
|
|
58
111
|
* - For cookies: at least the cookie `maxAge`, so in-flight sessions are
|
|
59
112
|
* not forced to re-authenticate.
|
|
60
113
|
* - For `@Encrypted` columns: until every existing row has been
|
|
61
|
-
* re-encrypted under the new key.
|
|
62
|
-
* `
|
|
114
|
+
* re-encrypted under the new key. Once `legacy` is dropped, reading the
|
|
115
|
+
* `@Encrypted` property of a row that is still under the old key throws
|
|
116
|
+
* `DecryptionError`, the same error a tampered or corrupted value
|
|
117
|
+
* throws, so the error class alone cannot show that `legacy` was
|
|
118
|
+
* dropped too early. `DecryptionRotationError` is thrown only while
|
|
119
|
+
* `legacy` is configured and neither key opens the value. Neither class
|
|
120
|
+
* extends the other, so code that catches `DecryptionRotationError`
|
|
121
|
+
* during a rotation must also catch `DecryptionError` to handle such a
|
|
122
|
+
* read once `legacy` is dropped.
|
|
63
123
|
*/
|
|
64
124
|
static generateKey(algorithm: EncryptAlgorithm): string;
|
|
65
125
|
static validateKey(base64EncodedKey: string, algorithm: EncryptAlgorithm): boolean;
|
|
@@ -68,6 +128,15 @@ export interface EncryptOptions extends BaseOptions {
|
|
|
68
128
|
}
|
|
69
129
|
export interface DecryptOptions extends BaseOptions {
|
|
70
130
|
}
|
|
131
|
+
export interface DecryptCallbacks {
|
|
132
|
+
/**
|
|
133
|
+
* Called with no arguments, synchronously, before a three-arg
|
|
134
|
+
* {@link Encrypt.decrypt} returns a value that the legacy key opened.
|
|
135
|
+
* Never called when the current key opens the value, when decryption
|
|
136
|
+
* throws, or in the two-arg form.
|
|
137
|
+
*/
|
|
138
|
+
onLegacyKeyUsed?: () => void;
|
|
139
|
+
}
|
|
71
140
|
interface BaseOptions {
|
|
72
141
|
algorithm: EncryptAlgorithm;
|
|
73
142
|
key: string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export default class UnrecognizedAssociationConditionKeys extends Error {
|
|
2
|
+
private associationName;
|
|
3
|
+
private unrecognizedKeys;
|
|
4
|
+
private acceptedKeys;
|
|
5
|
+
private methodName;
|
|
6
|
+
constructor(associationName: string, unrecognizedKeys: string[], acceptedKeys: readonly string[], methodName: string | null);
|
|
7
|
+
get message(): string;
|
|
8
|
+
}
|
|
@@ -18,12 +18,40 @@ export default class Env<T extends {
|
|
|
18
18
|
} | undefined, ReturnType extends OptionalConfig extends undefined ? number : OptionalConfig extends {
|
|
19
19
|
optional: boolean;
|
|
20
20
|
} ? OptionalConfig['optional'] extends true ? number | undefined : number : number>(env: IntegerEnvs, opts?: OptionalConfig): ReturnType;
|
|
21
|
+
/**
|
|
22
|
+
* Reads a boolean environment variable. Only the exact string `"1"` is
|
|
23
|
+
* true. Every other value is false, including `"true"`, `"TRUE"`, `"yes"`
|
|
24
|
+
* and `""`, and so is an unset variable.
|
|
25
|
+
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* // FEATURE_FLAG=1
|
|
28
|
+
* AppEnv.boolean('FEATURE_FLAG') // true
|
|
29
|
+
*
|
|
30
|
+
* // FEATURE_FLAG=true
|
|
31
|
+
* AppEnv.boolean('FEATURE_FLAG') // false
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @param env - the name of the environment variable
|
|
35
|
+
* @returns `true` when the variable is exactly `"1"`, otherwise `false`
|
|
36
|
+
*/
|
|
21
37
|
boolean(env: BooleanEnvs): boolean;
|
|
22
38
|
setString(env: StringEnvs, val: string | undefined): void;
|
|
23
39
|
unsetString(env: StringEnvs): void;
|
|
24
40
|
setInteger(env: IntegerEnvs, val: number | undefined): void;
|
|
25
41
|
unsetInteger(env: IntegerEnvs): void;
|
|
42
|
+
/**
|
|
43
|
+
* Sets a boolean environment variable to `"1"`, the only value
|
|
44
|
+
* {@link Env.boolean} reads as true.
|
|
45
|
+
*
|
|
46
|
+
* @param env - the name of the environment variable
|
|
47
|
+
*/
|
|
26
48
|
setBoolean(env: BooleanEnvs): void;
|
|
49
|
+
/**
|
|
50
|
+
* Deletes a boolean environment variable. {@link Env.boolean} reads an
|
|
51
|
+
* unset variable as false, since only the exact string `"1"` is true.
|
|
52
|
+
*
|
|
53
|
+
* @param env - the name of the environment variable
|
|
54
|
+
*/
|
|
27
55
|
unsetBoolean(env: BooleanEnvs): void;
|
|
28
56
|
protected required(variable: string): string;
|
|
29
57
|
protected optional(variable: string): string | undefined;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { openapiPrimitiveTypes, openapiShorthandPrimitiveTypes } from '../dream/constants.js';
|
|
2
|
-
export { type CommonOpenapiSchemaObjectFields, type OpenapiAllTypes, type OpenapiDescription, type OpenapiFormats, type OpenapiNumberFormats, type OpenapiPrimitiveBaseTypes, type OpenapiPrimitiveTypes, type OpenapiSchemaArray, type OpenapiSchemaArrayShorthand, type OpenapiSchemaBase, type OpenapiSchemaBody, type OpenapiSchemaBodyShorthand, type OpenapiSchemaCommonFields, type OpenapiSchemaExpressionAllOf, type OpenapiSchemaExpressionAnyOf, type OpenapiSchemaExpressionOneOf, type OpenapiSchemaExpressionRef, type OpenapiSchemaExpressionRefSchemaShorthand, type OpenapiSchemaInteger, type OpenapiSchemaNull, type OpenapiSchemaNumber, type OpenapiSchemaObject, type OpenapiSchemaObjectAllOf, type OpenapiSchemaObjectAllOfShorthand, type
|
|
2
|
+
export { type CommonOpenapiSchemaObjectFields, type OpenapiAllTypes, type OpenapiDescription, type OpenapiFormats, type OpenapiNumberFormats, type OpenapiPrimitiveBaseTypes, type OpenapiPrimitiveTypes, type OpenapiSchemaArray, type OpenapiSchemaArrayShorthand, type OpenapiSchemaBase, type OpenapiSchemaBody, type OpenapiSchemaBodyShorthand, type OpenapiSchemaCombinatorFields, type OpenapiSchemaCommonFields, type OpenapiSchemaExpressionAllOf, type OpenapiSchemaExpressionAnyOf, type OpenapiSchemaExpressionOneOf, type OpenapiSchemaExpressionRef, type OpenapiSchemaExpressionRefSchemaShorthand, type OpenapiSchemaInteger, type OpenapiSchemaNull, type OpenapiSchemaNumber, type OpenapiSchemaObject, type OpenapiSchemaObjectAllOf, type OpenapiSchemaObjectAllOfShorthand, type OpenapiSchemaObjectBase, type OpenapiSchemaObjectBaseShorthand, type OpenapiSchemaObjectShorthand, type OpenapiSchemaPrimitiveGeneric, type OpenapiSchemaProperties, type OpenapiSchemaPropertiesShorthand, type OpenapiSchemaShorthandExpressionAllOf, type OpenapiSchemaShorthandExpressionAnyOf, type OpenapiSchemaShorthandExpressionOneOf, type OpenapiSchemaShorthandExpressionSerializableRef, type OpenapiSchemaShorthandExpressionSerializerRef, type OpenapiSchemaShorthandPrimitiveGeneric, type OpenapiSchemaString, type OpenapiShorthandAllTypes, type OpenapiShorthandPrimitiveBaseTypes, type OpenapiShorthandPrimitiveTypes, type OpenapiTypeField, type OpenapiTypeFieldObject, } from '../types/openapi.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { type DecoratorContext } from '../decorators/DecoratorContextType.js';
|
|
2
|
-
export { type EncryptAlgorithm, type EncryptOptions } from '../encrypt/index.js';
|
|
2
|
+
export { type DecryptCallbacks, type EncryptAlgorithm, type EncryptOptions } from '../encrypt/index.js';
|
|
3
3
|
export { type RoundingPrecision } from '../helpers/round.js';
|
|
4
4
|
export { type Camelized, type Hyphenized, type Pascalized, type Snakeified } from '../helpers/stringCasing.js';
|
|
5
5
|
export { type SerializerRendererOpts } from '../serializer/SerializerRenderer.js';
|
|
@@ -14,5 +14,5 @@ export { type DurationObject, type DurationUnit, type WeekdayName } from '../typ
|
|
|
14
14
|
export { type DreamAssociationMetadata, type DreamAttributes, type DreamClassAssociationAndStatement, type DreamClassColumn, type DreamColumn, type DreamColumnNames, type DreamOrViewModelClassSerializerKey, type DreamOrViewModelSerializerKey, type DreamParamSafeAttributes, type DreamParamSafeColumnNames, type DreamSerializable, type DreamSerializableArray, type DreamSerializerKey, type DreamSerializers, type DreamVirtualColumns, type UpdateableAssociationProperties, type UpdateableProperties, type ViewModel, type ViewModelClass, } from '../types/dream.js';
|
|
15
15
|
export { type DreamModelSerializerType, type InternalAnyTypedSerializerRendersMany, type InternalAnyTypedSerializerRendersOne, type SerializerCasing, type SimpleObjectSerializerType, } from '../types/serializer.js';
|
|
16
16
|
export { type ValidationType } from '../types/validation.js';
|
|
17
|
-
export { type DreamAppInitOptions, type DreamAppOpts, type DreamLogger, type DreamLogLevel, type DreamDbConfig, } from '../dream-app/index.js';
|
|
17
|
+
export { type DreamAppInitOptions, type DreamAppOpts, type DreamLogger, type DreamLogLevel, type DreamDbConfig, type EncryptionLegacyKeyUsedEvent, } from '../dream-app/index.js';
|
|
18
18
|
export { type WhereStatementForDream, type WhereStatementForDreamClass, } from '../types/associations/shared.js';
|
|
@@ -10,5 +10,18 @@ export default class SerializerRenderer {
|
|
|
10
10
|
private renderOpts;
|
|
11
11
|
constructor(serializerBuilder: DreamSerializerBuilder<any, any> | ObjectSerializerBuilder<any, any> | null | undefined, passthroughData?: object, { casing }?: SerializerRendererOpts);
|
|
12
12
|
render(): Record<string, any> | null;
|
|
13
|
+
/**
|
|
14
|
+
* The keys a flattened `rendersOne` adds to the parent's payload when its associated object is
|
|
15
|
+
* null: each key any of `serializers` declares, set to `null`. There are several serializers when
|
|
16
|
+
* the association is a polymorphic BelongsTo, one for each target class, or when the associated
|
|
17
|
+
* class is an STI base, one for each child, and their keys are unioned.
|
|
18
|
+
*
|
|
19
|
+
* Each serializer is built with `undefined` data to read its declarations, as Psychic's OpenAPI
|
|
20
|
+
* generation and `preloadFor` build it, and nothing is rendered: no attribute callback runs and
|
|
21
|
+
* no association of the missing object is read. A flattened `rendersOne` it declares adds its
|
|
22
|
+
* own serializers' keys the same way. A flattened `customAttribute` adds none, since only its
|
|
23
|
+
* callback knows its keys.
|
|
24
|
+
*/
|
|
25
|
+
private nullFlattenedAttributes;
|
|
13
26
|
private setCase;
|
|
14
27
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Dream from '../../Dream.js';
|
|
2
2
|
import { DreamOrViewModelSerializerKey, DreamVirtualColumns, NonJsonDreamColumnNames, ViewModel, ViewModelClass } from '../../types/dream.js';
|
|
3
|
-
import { AutomaticSerializerAttributeOptions, AutomaticSerializerAttributeOptionsForType, InternalAnyTypedSerializerAttribute, InternalAnyTypedSerializerCustomAttribute, InternalAnyTypedSerializerDelegatedAttribute, InternalAnyTypedSerializerRendersMany, InternalAnyTypedSerializerRendersOne, NonAutomaticSerializerAttributeOptionsWithPossibleDecimalRenderOption, SerializerAttributeOptionsForVirtualColumn, SerializerType } from '../../types/serializer.js';
|
|
3
|
+
import { AutomaticSerializerAttributeOptions, AutomaticSerializerAttributeOptionsForType, CustomAttributeOptions, DelegatedAttributeOptionsArgs, InternalAnyTypedSerializerAttribute, InternalAnyTypedSerializerCustomAttribute, InternalAnyTypedSerializerDelegatedAttribute, InternalAnyTypedSerializerRendersMany, InternalAnyTypedSerializerRendersOne, NonAutomaticSerializerAttributeOptionsWithPossibleDecimalRenderOption, SerializerAttributeOptionsForVirtualColumn, SerializerType } from '../../types/serializer.js';
|
|
4
4
|
import { SerializerRendererOpts } from '../SerializerRenderer.js';
|
|
5
5
|
export type DreamPropertiesToExclude = keyof Dream | 'serializers';
|
|
6
6
|
export default class DreamSerializerBuilder<DataTypeForOpenapi extends typeof Dream, MaybeNullDataType extends Dream | null, PassthroughDataType = any, DataType extends Exclude<MaybeNullDataType, null> = Exclude<MaybeNullDataType, null>> {
|
|
@@ -54,7 +54,7 @@ export default class DreamSerializerBuilder<DataTypeForOpenapi extends typeof Dr
|
|
|
54
54
|
*/
|
|
55
55
|
attribute(name: NonJsonDreamColumnNames<DataType> & keyof DataType & 'type', options?: AutomaticSerializerAttributeOptionsForType): DreamSerializerBuilder<DataTypeForOpenapi, MaybeNullDataType, PassthroughDataType, DataType>;
|
|
56
56
|
attribute(name: DreamVirtualColumns<DataType>[number], options?: SerializerAttributeOptionsForVirtualColumn): DreamSerializerBuilder<DataTypeForOpenapi, MaybeNullDataType, PassthroughDataType, DataType>;
|
|
57
|
-
attribute<
|
|
57
|
+
attribute<AttributeName extends Exclude<keyof DataType & string, NonJsonDreamColumnNames<DataType> | DreamPropertiesToExclude>>(name: AttributeName, options: NonAutomaticSerializerAttributeOptionsWithPossibleDecimalRenderOption): DreamSerializerBuilder<DataTypeForOpenapi, MaybeNullDataType, PassthroughDataType, DataType>;
|
|
58
58
|
attribute<AttributeName extends NonJsonDreamColumnNames<DataType> & keyof DataType & string>(name: AttributeName, options?: AutomaticSerializerAttributeOptions): DreamSerializerBuilder<DataTypeForOpenapi, MaybeNullDataType, PassthroughDataType, DataType>;
|
|
59
59
|
/**
|
|
60
60
|
* Includes an attribute from a nested object in the serialized output.
|
|
@@ -65,9 +65,10 @@ export default class DreamSerializerBuilder<DataTypeForOpenapi extends typeof Dr
|
|
|
65
65
|
* 2. `required: false` → omits the key entirely from the rendered output
|
|
66
66
|
* 3. otherwise → renders `null`
|
|
67
67
|
*
|
|
68
|
-
* When the target is a Dream model,
|
|
69
|
-
*
|
|
70
|
-
*
|
|
68
|
+
* When the target is a Dream model, a non-json column's OpenAPI shape is inferred
|
|
69
|
+
* from the column, so its `openapi` option takes only a `description`. json/jsonb
|
|
70
|
+
* columns and non-Dream targets have no shape to infer, so they require a full
|
|
71
|
+
* `openapi` shape.
|
|
71
72
|
*
|
|
72
73
|
* `optional` and `required` are not aliases — they encode different things and can
|
|
73
74
|
* be used together:
|
|
@@ -91,12 +92,14 @@ export default class DreamSerializerBuilder<DataTypeForOpenapi extends typeof Dr
|
|
|
91
92
|
* a discriminator string when the association is actually missing produces a response
|
|
92
93
|
* indistinguishable from "association present with that type," which is misleading)
|
|
93
94
|
* - `openapi` - OpenAPI schema definition; required for non-Dream targets and json/jsonb
|
|
94
|
-
* columns
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
95
|
+
* columns. On a non-json Dream column, whose shape is inferred, it takes only a
|
|
96
|
+
* `description` (and, on the STI `type` column, a `type: 'string'` and `enum`)
|
|
97
|
+
* - `optional` - Set to `true` to mark the value as nullable in the OpenAPI schema:
|
|
98
|
+
* Psychic adds `null` to the rendered schema (e.g., `type: 'string'` becomes
|
|
99
|
+
* `type: ['string', 'null']`, and a `$ref` becomes `anyOf: [{ $ref }, { type: 'null' }]`).
|
|
100
|
+
* OpenAPI-only — the key is still rendered (as `null`). For Dream models, this is
|
|
101
|
+
* auto-inferred from optional BelongsTo associations. Use this when delegating
|
|
102
|
+
* through a HasOne or other nullable association.
|
|
100
103
|
* - `precision` - Round decimal values to the specified number of decimal places (0–9)
|
|
101
104
|
* during rendering; does not affect the OpenAPI shape (not available when delegating
|
|
102
105
|
* to a `'type'` STI discriminator column, which is always a string enum)
|
|
@@ -108,52 +111,45 @@ export default class DreamSerializerBuilder<DataTypeForOpenapi extends typeof Dr
|
|
|
108
111
|
* @example
|
|
109
112
|
* ```typescript
|
|
110
113
|
* // Delegate to a Dream association's column (type inferred)
|
|
111
|
-
* .delegatedAttribute('currentLocalizedText', 'title'
|
|
114
|
+
* .delegatedAttribute('currentLocalizedText', 'title')
|
|
112
115
|
*
|
|
113
116
|
* // With default value for null target or attribute
|
|
114
|
-
* .delegatedAttribute('user', 'displayName', {
|
|
115
|
-
* openapi: { type: 'string' },
|
|
116
|
-
* default: 'Unknown User'
|
|
117
|
-
* })
|
|
117
|
+
* .delegatedAttribute('user', 'displayName', { default: 'Unknown User' })
|
|
118
118
|
*
|
|
119
119
|
* // Rename the output key
|
|
120
|
-
* .delegatedAttribute('profile', 'avatarUrl', {
|
|
121
|
-
*
|
|
122
|
-
*
|
|
120
|
+
* .delegatedAttribute('profile', 'avatarUrl', { as: 'avatar' })
|
|
121
|
+
*
|
|
122
|
+
* // A json/jsonb column has no shape to infer, so it declares one
|
|
123
|
+
* .delegatedAttribute('user', 'preferences', {
|
|
124
|
+
* openapi: { type: 'object', properties: { theme: 'string' } }
|
|
123
125
|
* })
|
|
124
126
|
* ```
|
|
125
127
|
*/
|
|
126
|
-
delegatedAttribute<ProvidedModelType = undefined, ProvidedTargetName extends ProvidedModelType extends undefined ? undefined : Exclude<keyof ProvidedModelType, DreamPropertiesToExclude> = ProvidedModelType extends undefined ? undefined : Exclude<keyof ProvidedModelType, DreamPropertiesToExclude>, ActualDataType extends ProvidedModelType extends undefined ? InstanceType<DataTypeForOpenapi> : ProvidedModelType = ProvidedModelType extends undefined ? InstanceType<DataTypeForOpenapi> : ProvidedModelType, TargetName extends ProvidedTargetName extends undefined ? Exclude<keyof ActualDataType, DreamPropertiesToExclude> : ProvidedTargetName & keyof ActualDataType = ProvidedTargetName extends undefined ? Exclude<keyof ActualDataType, DreamPropertiesToExclude> : ProvidedTargetName & keyof ActualDataType, AssociatedModelType = Exclude<ActualDataType[TargetName], null>, TargetAttributeName extends AssociatedModelType extends object ? Exclude<keyof AssociatedModelType, DreamPropertiesToExclude> & string : never = AssociatedModelType extends object ? Exclude<keyof AssociatedModelType, DreamPropertiesToExclude> & string : never>(targetName: TargetName, name: TargetAttributeName, options
|
|
127
|
-
optional?: boolean;
|
|
128
|
-
required?: false;
|
|
129
|
-
} : TargetAttributeName extends DreamVirtualColumns<AssociatedModelType>[number] ? SerializerAttributeOptionsForVirtualColumn & {
|
|
130
|
-
optional?: boolean;
|
|
131
|
-
} : TargetAttributeName extends NonJsonDreamColumnNames<AssociatedModelType> & keyof AssociatedModelType & string ? (AutomaticSerializerAttributeOptions & {
|
|
132
|
-
optional?: boolean;
|
|
133
|
-
}) | (NonAutomaticSerializerAttributeOptionsWithPossibleDecimalRenderOption & {
|
|
134
|
-
optional?: boolean;
|
|
135
|
-
}) : NonAutomaticSerializerAttributeOptionsWithPossibleDecimalRenderOption & {
|
|
136
|
-
optional?: boolean;
|
|
137
|
-
} : NonAutomaticSerializerAttributeOptionsWithPossibleDecimalRenderOption & {
|
|
138
|
-
optional?: boolean;
|
|
139
|
-
}): this;
|
|
128
|
+
delegatedAttribute<ProvidedModelType = undefined, ProvidedTargetName extends ProvidedModelType extends undefined ? undefined : Exclude<keyof ProvidedModelType, DreamPropertiesToExclude> = ProvidedModelType extends undefined ? undefined : Exclude<keyof ProvidedModelType, DreamPropertiesToExclude>, ActualDataType extends ProvidedModelType extends undefined ? InstanceType<DataTypeForOpenapi> : ProvidedModelType = ProvidedModelType extends undefined ? InstanceType<DataTypeForOpenapi> : ProvidedModelType, TargetName extends ProvidedTargetName extends undefined ? Exclude<keyof ActualDataType, DreamPropertiesToExclude> : ProvidedTargetName & keyof ActualDataType = ProvidedTargetName extends undefined ? Exclude<keyof ActualDataType, DreamPropertiesToExclude> : ProvidedTargetName & keyof ActualDataType, AssociatedModelType = Exclude<ActualDataType[TargetName], null>, TargetAttributeName extends AssociatedModelType extends object ? Exclude<keyof AssociatedModelType, DreamPropertiesToExclude> & string : never = AssociatedModelType extends object ? Exclude<keyof AssociatedModelType, DreamPropertiesToExclude> & string : never>(targetName: TargetName, name: TargetAttributeName, ...options: DelegatedAttributeOptionsArgs<AssociatedModelType, TargetAttributeName>): this;
|
|
140
129
|
/**
|
|
141
130
|
* Includes a computed value in the serialized output.
|
|
142
131
|
*
|
|
143
132
|
* Executes a callback function to generate a custom attribute value.
|
|
144
133
|
* The `openapi` option is always required since the return type cannot be inferred.
|
|
145
134
|
*
|
|
135
|
+
* The callback's return value renders as is, so any fallback or rounding belongs in the
|
|
136
|
+
* callback.
|
|
137
|
+
*
|
|
146
138
|
* @param name - The attribute name for the computed value
|
|
147
139
|
* @param fn - Callback function that returns the computed value
|
|
148
140
|
* @param options - Configuration options:
|
|
149
141
|
* - `openapi` - (required) OpenAPI schema definition for the computed value
|
|
150
|
-
* - `as` - Rename the attribute key in the serialized output and OpenAPI shape
|
|
151
|
-
* - `default` - Value to use when the callback returns undefined
|
|
152
142
|
* - `flatten` - When `true`, spreads the returned object's properties directly into the
|
|
153
|
-
* parent serialized output instead of nesting them under `name
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
143
|
+
* parent serialized output instead of nesting them under `name`. The `openapi` option
|
|
144
|
+
* then describes the whole returned object, not each property, because Psychic adds it
|
|
145
|
+
* to the parent's schema as an `allOf` branch: either an object schema with full
|
|
146
|
+
* property bodies (`type: 'object'`, `properties`, and `required` listing the keys that
|
|
147
|
+
* are always present), or `{ $serializer: SomeSerializer }` when the callback returns
|
|
148
|
+
* what that serializer renders. A serializer ref with `many: true` does not type-check
|
|
149
|
+
* here, since a list has no properties to spread. A `null` or `undefined` return adds no
|
|
150
|
+
* keys. Attributes render in declaration order, so a key the returned object shares with
|
|
151
|
+
* another attribute takes the value of whichever is declared later; under the `allOf`,
|
|
152
|
+
* that value must match both schemas
|
|
157
153
|
* - `required` - Set to `false` to mark the attribute as optional in the OpenAPI schema;
|
|
158
154
|
* when omitted, attributes are required by default
|
|
159
155
|
* @returns The serializer builder for method chaining
|
|
@@ -166,25 +162,21 @@ export default class DreamSerializerBuilder<DataTypeForOpenapi extends typeof Dr
|
|
|
166
162
|
* { openapi: { type: 'string' } }
|
|
167
163
|
* )
|
|
168
164
|
*
|
|
169
|
-
* // Flattened object properties
|
|
165
|
+
* // Flattened object properties: `openapi` describes the whole returned object
|
|
170
166
|
* .customAttribute('coordinates', () => ({ lat: 40.7, lng: -74.0 }), {
|
|
171
167
|
* flatten: true,
|
|
172
168
|
* openapi: {
|
|
173
|
-
*
|
|
174
|
-
*
|
|
169
|
+
* type: 'object',
|
|
170
|
+
* required: ['lat', 'lng'],
|
|
171
|
+
* properties: {
|
|
172
|
+
* lat: { type: 'number' },
|
|
173
|
+
* lng: { type: 'number' }
|
|
174
|
+
* }
|
|
175
175
|
* }
|
|
176
176
|
* })
|
|
177
|
-
*
|
|
178
|
-
* // With decimal precision
|
|
179
|
-
* .customAttribute('averageRating', () => calculateAverage(ratings), {
|
|
180
|
-
* openapi: 'decimal',
|
|
181
|
-
* precision: 2
|
|
182
|
-
* })
|
|
183
177
|
* ```
|
|
184
178
|
*/
|
|
185
|
-
customAttribute(name: string, fn: () => unknown, options:
|
|
186
|
-
flatten?: boolean;
|
|
187
|
-
}): this;
|
|
179
|
+
customAttribute(name: string, fn: () => unknown, options: CustomAttributeOptions): this;
|
|
188
180
|
/**
|
|
189
181
|
* Includes a single associated object in the serialized output.
|
|
190
182
|
*
|
|
@@ -195,9 +187,18 @@ export default class DreamSerializerBuilder<DataTypeForOpenapi extends typeof Dr
|
|
|
195
187
|
* @param options - Configuration options:
|
|
196
188
|
* - `as` - Rename the association key in the serialized output
|
|
197
189
|
* - `flatten` - When `true`, spreads the rendered association's attributes directly into
|
|
198
|
-
* the parent serialized output instead of nesting them under `name`.
|
|
199
|
-
*
|
|
200
|
-
*
|
|
190
|
+
* the parent serialized output instead of nesting them under `name`. Attributes render in
|
|
191
|
+
* declaration order, so when the parent and the association share a key (e.g., `id`),
|
|
192
|
+
* whichever is declared later wins: the association's value replaces an attribute declared
|
|
193
|
+
* before the `rendersOne`, and an attribute declared after it replaces the association's.
|
|
194
|
+
* When the association is `null`, every key its serializer declares renders as `null`
|
|
195
|
+
* (for a polymorphic `BelongsTo`, every key the serializer of any of its target classes
|
|
196
|
+
* declares, and for an association to an STI base, every key the serializer of any of its
|
|
197
|
+
* STI children declares), in the same order (so an `id` declared before the `rendersOne` becomes
|
|
198
|
+
* `null`); nothing in that serializer is rendered, so its custom attribute callbacks do
|
|
199
|
+
* not run, and a flattened `customAttribute` in it adds no keys. Psychic adds the association's OpenAPI
|
|
200
|
+
* schema to the parent's as an `allOf` branch, so a shared key's value must match both
|
|
201
|
+
* schemas, whichever declaration wins
|
|
201
202
|
* - `optional` - When `true`, allows the association to be null/missing without causing
|
|
202
203
|
* an `OpenapiResponseValidationFailure` during Psychic controller unit specs. By default,
|
|
203
204
|
* `rendersOne` expects the association to be present (mirroring the `optional` option on
|
|
@@ -251,7 +252,10 @@ export default class DreamSerializerBuilder<DataTypeForOpenapi extends typeof Dr
|
|
|
251
252
|
as?: string;
|
|
252
253
|
/**
|
|
253
254
|
* If `true`, the rendered association's attributes are merged directly into
|
|
254
|
-
* the parent object instead of being nested under the association key.
|
|
255
|
+
* the parent object instead of being nested under the association key. A key
|
|
256
|
+
* the parent shares with the association takes the value of whichever is
|
|
257
|
+
* declared later, and when the association is `null`, every key its serializer
|
|
258
|
+
* declares renders as `null`. See the `flatten` option of `rendersOne`.
|
|
255
259
|
*/
|
|
256
260
|
flatten?: boolean;
|
|
257
261
|
/**
|